diff --git a/.buildkite/pipelines/main/misc/doctest.yml b/.buildkite/pipelines/main/misc/doctest.yml index 1f185284ec641..b83139ddc1f9b 100644 --- a/.buildkite/pipelines/main/misc/doctest.yml +++ b/.buildkite/pipelines/main/misc/doctest.yml @@ -12,8 +12,8 @@ steps: persist_depot_dirs: packages,artifacts,compiled version: '1.6' - staticfloat/sandbox#v1: - rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v3.1/package_linux.x86_64.tar.gz - rootfs_treehash: "8c33c341a864852629b8aac01a6eb6a79b73570e" + rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v4.8/package_linux.x86_64.tar.gz + rootfs_treehash: "2a058481b567f0e91b9aa3ce4ad4f09e6419355a" uid: 1000 gid: 1000 workspaces: diff --git a/.buildkite/pipelines/main/misc/embedding.yml b/.buildkite/pipelines/main/misc/embedding.yml index 45c740f4e8a6e..bdd2a0a6065f5 100644 --- a/.buildkite/pipelines/main/misc/embedding.yml +++ b/.buildkite/pipelines/main/misc/embedding.yml @@ -12,8 +12,8 @@ steps: persist_depot_dirs: packages,artifacts,compiled version: '1.6' - staticfloat/sandbox#v1: - rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v3.1/package_linux.x86_64.tar.gz - rootfs_treehash: "8c33c341a864852629b8aac01a6eb6a79b73570e" + rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v4.8/package_linux.x86_64.tar.gz + rootfs_treehash: "2a058481b567f0e91b9aa3ce4ad4f09e6419355a" uid: 1000 gid: 1000 workspaces: diff --git a/.buildkite/pipelines/main/misc/llvmpasses.yml b/.buildkite/pipelines/main/misc/llvmpasses.yml index 5b444a6db41aa..1f6d89014eb0d 100644 --- a/.buildkite/pipelines/main/misc/llvmpasses.yml +++ b/.buildkite/pipelines/main/misc/llvmpasses.yml @@ -12,8 +12,8 @@ steps: persist_depot_dirs: packages,artifacts,compiled version: '1.6' - staticfloat/sandbox#v1: - rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v3.1/llvm_passes.x86_64.tar.gz - rootfs_treehash: "9dd715500b117a16fcfa419ea0bca0c0ca902cee" + rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v4.8/llvm_passes.x86_64.tar.gz + rootfs_treehash: "c7a289a8cc544b234b1e2d7cbcce3e6815359ecd" workspaces: # Include `/cache/repos` so that our `git` version introspection works. - "/cache/repos:/cache/repos" @@ -32,8 +32,8 @@ steps: persist_depot_dirs: packages,artifacts,compiled version: '1.6' - staticfloat/sandbox#v1: - rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v3.8/package_linux.x86_64.tar.gz - rootfs_treehash: "84a323ae8fcc724f8ea5aca5901bbbf4bda3e519" + rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v4.8/package_linux.x86_64.tar.gz + rootfs_treehash: "2a058481b567f0e91b9aa3ce4ad4f09e6419355a" uid: 1000 gid: 1000 workspaces: diff --git a/.buildkite/pipelines/main/misc/sanitizers.yml b/.buildkite/pipelines/main/misc/sanitizers.yml index f586881917f0a..a0c40dda7e12f 100644 --- a/.buildkite/pipelines/main/misc/sanitizers.yml +++ b/.buildkite/pipelines/main/misc/sanitizers.yml @@ -12,8 +12,8 @@ steps: persist_depot_dirs: packages,artifacts,compiled version: '1.6' - staticfloat/sandbox#v1: - rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v3.1/llvm_passes.x86_64.tar.gz - rootfs_treehash: "9dd715500b117a16fcfa419ea0bca0c0ca902cee" + rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v4.8/llvm_passes.x86_64.tar.gz + rootfs_treehash: "c7a289a8cc544b234b1e2d7cbcce3e6815359ecd" uid: 1000 gid: 1000 workspaces: @@ -25,3 +25,23 @@ steps: commands: | echo "--- Build julia-debug with ASAN" contrib/asan/build.sh ./tmp/test-asan -j$${JULIA_CPU_THREADS:?} debug + - label: "tsan" + key: "tsan" + plugins: + - JuliaCI/julia#v1: + # Drop default "registries" directory, so it is not persisted from execution to execution + persist_depot_dirs: packages,artifacts,compiled + version: '1.6' + - staticfloat/sandbox#v1: + rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v4.8/llvm_passes.x86_64.tar.gz + rootfs_treehash: "c7a289a8cc544b234b1e2d7cbcce3e6815359ecd" + uid: 1000 + gid: 1000 + workspaces: + - "/cache/repos:/cache/repos" + timeout_in_minutes: 120 + if: | # We only run the `tsan` job on Julia 1.8 and later. + (pipeline.slug != "julia-release-1-dot-6") && (pipeline.slug != "julia-release-1-dot-7") + commands: | + echo "--- Build julia-debug runtime with TSAN" + contrib/tsan/build.sh ./tmp/test-tsan -j$${JULIA_CPU_THREADS:?} julia-src-debug diff --git a/.buildkite/pipelines/main/misc/signed_pipeline_test.yml.signature b/.buildkite/pipelines/main/misc/signed_pipeline_test.yml.signature index 299f959c1db10..b0844748c486f 100644 Binary files a/.buildkite/pipelines/main/misc/signed_pipeline_test.yml.signature and b/.buildkite/pipelines/main/misc/signed_pipeline_test.yml.signature differ diff --git a/.buildkite/pipelines/main/misc/whitespace.yml b/.buildkite/pipelines/main/misc/whitespace.yml index 5643c80e98db8..673221e54cfdc 100644 --- a/.buildkite/pipelines/main/misc/whitespace.yml +++ b/.buildkite/pipelines/main/misc/whitespace.yml @@ -12,8 +12,8 @@ steps: persist_depot_dirs: packages,artifacts,compiled version: '1.6' - staticfloat/sandbox#v1: - rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v3.1/package_linux.x86_64.tar.gz - rootfs_treehash: "8c33c341a864852629b8aac01a6eb6a79b73570e" + rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v4.8/package_linux.x86_64.tar.gz + rootfs_treehash: "2a058481b567f0e91b9aa3ce4ad4f09e6419355a" workspaces: - "/cache/repos:/cache/repos" timeout_in_minutes: 10 diff --git a/.buildkite/pipelines/main/platforms/package_linux.arches b/.buildkite/pipelines/main/platforms/package_linux.arches index 89e3c9fd7dc4d..dec82f530a832 100644 --- a/.buildkite/pipelines/main/platforms/package_linux.arches +++ b/.buildkite/pipelines/main/platforms/package_linux.arches @@ -1,7 +1,7 @@ -# PLATFORM LABEL ALLOW_FAIL ARCH ARCH_ROOTFS MAKE_FLAGS TIMEOUT IS_RR IS_ST IS_MT ROOTFS_TAG ROOTFS_HASH -# linux _aarch64 false _aarch64 aarch64 none 60 no no no v3.2 0566841e29f0f9880541c26a6595fd5ce0beb5ff -# linux _armv7l false _armv7l armv7l none 60 no no no v3.2 fb359370b052a47ce5c84cc6b4a7a03ed7053b25 -linux 32 false 32 i686 none 60 no no no v3.2 209c4db679a515befd7fb50ecc6bfbecf7ec3d32 -# linux _ppc64le false _ppc64le powerpc64le none 60 no no no v3.2 c03a0158b19d48ac84b426834fce0d3584cdd0c7 -linux 64 false 64 x86_64 none 60 no no no v3.2 474bf61a926b2d7fcf202284d59d4b11a04601d7 -musl 64 false 64 x86_64 none 60 no no no v3.19 e6a2730e37c386c46915b2650d6aaaa398195152 +# PLATFORM LABEL GROUP ALLOW_FAIL ARCH ARCH_ROOTFS MAKE_FLAGS TIMEOUT_BK TIMEOUT_RR RETRIES IS_RR IS_ST IS_MT ROOTFS_TAG ROOTFS_HASH +linux 32 . . 32 i686 . . . . . . . v4.8 b6dffc772ab4c2cd7fd4f83459308f6f0d89b957 +linux 64 . . 64 x86_64 . . . . . . . v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a +# linux aarch64 . . aarch64 aarch64 . . . . . . . .... ........................................ +# linux armv7l . . armv7l armv7l . . . . . . . .... ........................................ +# linux ppc64le . . ppc64le powerpc64le . . . . . . . .... ........................................ +musl 64 . . 64 x86_64 . . . . . . . v4.8 d13a47c87c38005bd5d97132e51789cafd852f90 diff --git a/.buildkite/pipelines/main/platforms/package_linux.yml b/.buildkite/pipelines/main/platforms/package_linux.yml index 66c0f4784d942..ce778c393b064 100644 --- a/.buildkite/pipelines/main/platforms/package_linux.yml +++ b/.buildkite/pipelines/main/platforms/package_linux.yml @@ -19,7 +19,7 @@ steps: workspaces: # Include `/cache/repos` so that our `git` version introspection works. - "/cache/repos:/cache/repos" - timeout_in_minutes: ${TIMEOUT?} + timeout_in_minutes: ${TIMEOUT_BK?} commands: | echo "--- Print the full and short commit hashes" SHORT_COMMIT_LENGTH=10 diff --git a/.buildkite/pipelines/main/platforms/tester_linux.arches b/.buildkite/pipelines/main/platforms/tester_linux.arches index 9d07ff26d2775..d1304563cb815 100644 --- a/.buildkite/pipelines/main/platforms/tester_linux.arches +++ b/.buildkite/pipelines/main/platforms/tester_linux.arches @@ -1,9 +1,25 @@ -# PLATFORM LABEL ALLOW_FAIL ARCH ARCH_ROOTFS MAKE_FLAGS TIMEOUT IS_RR IS_ST IS_MT ROOTFS_TAG ROOTFS_HASH -# linux _aarch64 false _aarch64 aarch64 none 60 no no no v3.2 0566841e29f0f9880541c26a6595fd5ce0beb5ff -# linux _armv7l false _armv7l armv7l none 60 no no no v3.2 fb359370b052a47ce5c84cc6b4a7a03ed7053b25 -linux 32 false 32 i686 none 60 no no no v3.2 209c4db679a515befd7fb50ecc6bfbecf7ec3d32 -# linux _ppc64le false _ppc64le powerpc64le none 60 no no no v3.2 c03a0158b19d48ac84b426834fce0d3584cdd0c7 -linux 64_rr false 64 x86_64 none 60 yes no no v3.2 474bf61a926b2d7fcf202284d59d4b11a04601d7 -linux 64_st false 64 x86_64 none 60 no yes no v3.2 474bf61a926b2d7fcf202284d59d4b11a04601d7 -linux 64_mt false 64 x86_64 none 60 no no yes v3.2 474bf61a926b2d7fcf202284d59d4b11a04601d7 -musl 64 true 64 x86_64 none 60 no no no v3.19 e6a2730e37c386c46915b2650d6aaaa398195152 +# PLATFORM LABEL GROUP ALLOW_FAIL ARCH ARCH_ROOTFS MAKE_FLAGS TIMEOUT_BK TIMEOUT_RR RETRIES IS_RR IS_ST IS_MT ROOTFS_TAG ROOTFS_HASH +linux 32_g1 g1 . 32 i686 . . . . . . . v4.8 b6dffc772ab4c2cd7fd4f83459308f6f0d89b957 +linux 32_g2 g2 . 32 i686 . . . 3 . . . v4.8 b6dffc772ab4c2cd7fd4f83459308f6f0d89b957 + +linux 64_g1_mt g1 . 64 x86_64 . . . . . . yes v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a +linux 64_g2_mt g2 . 64 x86_64 . . . 3 . . yes v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a + +linux 64_g1_st g1 . 64 x86_64 . . . . . yes . v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a +linux 64_g2_st g2 . 64 x86_64 . . . 3 . yes . v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a + +linux 64_g1_rrst g1 . 64 x86_64 . 300 240 . yes yes . v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a +linux 64_g2_rrst g2 . 64 x86_64 . 180 120 3 yes yes . v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a +linux 64_g3_st g3 . 64 x86_64 . . . 3 . yes . v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a + +# linux aarch64_g1 g1 true aarch64 aarch64 . . . . . . . ---- ---------------------------------------- +# linux aarch64_g2 g2 true aarch64 aarch64 . . . . . . . ---- ---------------------------------------- + +# linux armv7l_g1 g1 true armv7l armv7l . . . . . . . ---- ---------------------------------------- +# linux armv7l_g2 g2 true armv7l armv7l . . . . . . . ---- ---------------------------------------- + +# linux ppc64le_g1 g1 true ppc64le powerpc64le . . . . . . . ---- ---------------------------------------- +# linux ppc64le_g2 g2 true ppc64le powerpc64le . . . . . . . ---- ---------------------------------------- + +musl 64_g1 g1 true 64 x86_64 . . . . . . . v4.8 d13a47c87c38005bd5d97132e51789cafd852f90 +musl 64_g2 g2 true 64 x86_64 . . . . . . . v4.8 d13a47c87c38005bd5d97132e51789cafd852f90 diff --git a/.buildkite/pipelines/main/platforms/tester_linux.yml b/.buildkite/pipelines/main/platforms/tester_linux.yml index 123462458cfa1..18b019e66c8b7 100644 --- a/.buildkite/pipelines/main/platforms/tester_linux.yml +++ b/.buildkite/pipelines/main/platforms/tester_linux.yml @@ -23,7 +23,11 @@ steps: - "/cache/repos:/cache/repos" env: JULIA_SHELL: "/bin/bash" - timeout_in_minutes: ${TIMEOUT?} + timeout_in_minutes: ${TIMEOUT_BK?} + retry: + automatic: + - exit_status: "*" + limit: ${RETRIES?} soft_fail: ${ALLOW_FAIL?} commands: | echo "--- Print the full and short commit hashes" @@ -52,48 +56,65 @@ steps: echo "JULIA_CPU_THREADS is: $${JULIA_CPU_THREADS:?}" $${JULIA_BINARY:?} -e '@info "" Sys.CPU_THREADS' - echo "--- Set some environment variables in preparation for running the Julia test suite" - unset JULIA_DEPOT_PATH + echo "--- Set some environment variables" export OPENBLAS_NUM_THREADS=8 - export TESTS="[\"all\"]" + unset JULIA_DEPOT_PATH + unset JULIA_PKG_SERVER - if [[ "${IS_RR?}" == "yes" ]]; then - export JULIA_BINARY_UNDER_RR="$${JULIA_BINARY:?} .buildkite/utilities/rr/rr_capture.jl $${JULIA_BINARY:?}" + # Make sure that temp files and temp directories are created in a location that is + # backed by real storage. + export TMPDIR="$(pwd)/tmp" + mkdir -p $${TMPDIR:?} - export JULIA_BINARY_FOR_TESTS="$${JULIA_BINARY_UNDER_RR:?}" - export NCORES_FOR_TESTS="parse(Int, ENV[\"JULIA_RRCAPTURE_NUM_CORES\"])" + export NETWORK_RELATED_TESTS="Artifacts Downloads download LazyArtifacts LibGit2/online Pkg" - # For the `rr` job, we disable multi-threading. - export JULIA_NUM_THREADS=1 + if [[ "${GROUP?}" == "all" ]]; then + export TESTS="all LibGit2/online --force-net" + elif [[ "${GROUP?}" == "all_except_pkg" ]]; then + export TESTS="all LibGit2/online --force-net --skip Pkg" + elif [[ "${GROUP?}" == "g1" ]]; then + # Group 1: ALL tests EXCEPT the network-related tests. + export TESTS="all --force-net --skip $${NETWORK_RELATED_TESTS:?}" + elif [[ "${GROUP?}" == "g2" ]]; then + # Group 2: ONLY the network-related tests. + # In Group 2, we use whatever the default setting is with regards to the Pkg server. + export TESTS="$${NETWORK_RELATED_TESTS:?} --force-net" + elif [[ "${GROUP?}" == "g3" ]]; then + # Group 3: only Pkg. + # In Group 3, we explicitly opt-out of the Pkg server. + # The purpose of group 3 is to test the non-Pkg-server codepaths of Pkg. + export TESTS="Pkg --force-net" + export JULIA_PKG_SERVER="" + else + echo "Invalid value for GROUP: ${GROUP?}" + exit 1 + fi + + export JULIA_TEST_RR_TIMEOUT="${TIMEOUT_RR?}" + + if [[ "${IS_RR?}" == "yes" ]]; then + export JULIA_CMD_FOR_TESTS="$${JULIA_BINARY:?} .buildkite/utilities/rr/rr_capture.jl $${JULIA_BINARY:?}" + export NCORES_FOR_TESTS="parse(Int, ENV[\"JULIA_RRCAPTURE_NUM_CORES\"])" else - export JULIA_BINARY_FOR_TESTS="$${JULIA_BINARY:?}" + export JULIA_CMD_FOR_TESTS="$${JULIA_BINARY:?}" export NCORES_FOR_TESTS="Sys.CPU_THREADS" + fi - if [[ "${IS_ST?}" == "yes" ]]; then - # "ST" = single-threaded - export JULIA_NUM_THREADS=1 - elif [[ "${IS_MT?}" == "yes" ]]; then - # "MT" = multi-threaded - export JULIA_NUM_THREADS=16 + if [[ "${IS_ST?}" == "yes" ]]; then + export JULIA_NUM_THREADS=1 + fi - if [[ "$${BUILDKITE_PIPELINE_SLUG:?}" == "julia-release-1-dot-6" ]]; then - # On Julia 1.6, we skip the Distributed test suite if multithreading is enabled. - export TESTS="[\"all\", \"--skip\", \"Distributed\"]" - elif [[ "$${BUILDKITE_PIPELINE_SLUG:?}" == "julia-release-1-dot-7" ]]; then - # On Julia 1.7, we skip the Distributed test suite if multithreading is enabled. - export TESTS="[\"all\", \"--skip\", \"Distributed\"]" - else - # On Julia 1.8 and later, we do not skip any test suites. - export TESTS="[\"all\"]" - fi - fi + if [[ "${IS_MT?}" == "yes" ]]; then + export JULIA_NUM_THREADS=16 fi - echo "JULIA_BINARY_FOR_TESTS is: $${JULIA_BINARY_FOR_TESTS:?}" - echo "JULIA_NUM_THREADS is: $${JULIA_NUM_THREADS}" # this variable might not be set + echo "--- Print the test group, list of test sets, and other useful environment variables" + echo "JULIA_CMD_FOR_TESTS is: $${JULIA_CMD_FOR_TESTS:?}" + echo "JULIA_NUM_THREADS is: $${JULIA_NUM_THREADS}" # Note: this environment variable might not be set echo "NCORES_FOR_TESTS is: $${NCORES_FOR_TESTS:?}" echo "OPENBLAS_NUM_THREADS is: $${OPENBLAS_NUM_THREADS:?}" + echo "GROUP is: ${GROUP?}" echo "TESTS is: $${TESTS:?}" echo "--- Run the Julia test suite" - $${JULIA_BINARY_FOR_TESTS:?} -e "Base.runtests($${TESTS:?}; ncores = $${NCORES_FOR_TESTS:?})" + $${JULIA_CMD_FOR_TESTS:?} -e "Base.runtests(\"$${TESTS:?}\"; ncores = $${NCORES_FOR_TESTS:?})" diff --git a/.buildkite/pipelines/scheduled/coverage/coverage_linux64.yml b/.buildkite/pipelines/scheduled/coverage/coverage_linux64.yml index b16b7e8af82dd..1ff88577e2be4 100644 --- a/.buildkite/pipelines/scheduled/coverage/coverage_linux64.yml +++ b/.buildkite/pipelines/scheduled/coverage/coverage_linux64.yml @@ -19,10 +19,11 @@ steps: persist_depot_dirs: packages,artifacts,compiled version: '1.6' - staticfloat/sandbox#v1: - rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v3.1/package_linux.x86_64.tar.gz - rootfs_treehash: "8c33c341a864852629b8aac01a6eb6a79b73570e" + rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v4.8/package_linux.x86_64.tar.gz + rootfs_treehash: "2a058481b567f0e91b9aa3ce4ad4f09e6419355a" uid: 1000 gid: 1000 + timeout_in_minutes: 360 # 360 minutes = 6 hours commands: | echo "--- Build Julia from source" make --output-sync -j 6 @@ -41,5 +42,3 @@ steps: echo "--- Process and upload coverage information" ./julia .buildkite/pipelines/scheduled/coverage/upload_coverage.jl - timeout_in_minutes: 240 # 240 minutes = 4 hours - diff --git a/.buildkite/pipelines/scheduled/coverage/coverage_linux64.yml.signature b/.buildkite/pipelines/scheduled/coverage/coverage_linux64.yml.signature index 840a19cccfec2..4ecec8e8bb72c 100644 --- a/.buildkite/pipelines/scheduled/coverage/coverage_linux64.yml.signature +++ b/.buildkite/pipelines/scheduled/coverage/coverage_linux64.yml.signature @@ -1 +1 @@ -Salted__@P=jRU(,~p @Q'h7O MJgt<A(v?ɴ<,:jY'oڥσdٛ \ No newline at end of file +Salted__Iy֌>yNc kBvn+HvrލƼr/uYuIiiE(vL!?v \ No newline at end of file diff --git a/.buildkite/pipelines/scheduled/no_bb/no_bb_package_linux.arches b/.buildkite/pipelines/scheduled/no_bb/no_bb_package_linux.arches index ce76950e626f7..dff2aab4591e2 100644 --- a/.buildkite/pipelines/scheduled/no_bb/no_bb_package_linux.arches +++ b/.buildkite/pipelines/scheduled/no_bb/no_bb_package_linux.arches @@ -1,2 +1,2 @@ -# PLATFORM LABEL ALLOW_FAIL ARCH ARCH_ROOTFS MAKE_FLAGS TIMEOUT IS_RR IS_ST IS_MT ROOTFS_TAG ROOTFS_HASH -linux 64_no_bb false 64_no_bb x86_64 USE_BINARYBUILDER=0 240 no no no v4.4 ec3873fc1756ecb4c815e8785e9ca45cf54c68b6 +# PLATFORM LABEL GROUP ALLOW_FAIL ARCH ARCH_ROOTFS MAKE_FLAGS TIMEOUT_BK TIMEOUT_RR RETRIES IS_RR IS_ST IS_MT ROOTFS_TAG ROOTFS_HASH +linux 64src . . 64src x86_64 USE_BINARYBUILDER=0 180 . . . . . v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a diff --git a/.buildkite/pipelines/scheduled/no_bb/no_bb_tester_linux.arches b/.buildkite/pipelines/scheduled/no_bb/no_bb_tester_linux.arches index 9cbe03b0c0d43..0b1fbdf63b796 100644 --- a/.buildkite/pipelines/scheduled/no_bb/no_bb_tester_linux.arches +++ b/.buildkite/pipelines/scheduled/no_bb/no_bb_tester_linux.arches @@ -1,2 +1,10 @@ -# PLATFORM LABEL ALLOW_FAIL ARCH ARCH_ROOTFS MAKE_FLAGS TIMEOUT IS_RR IS_ST IS_MT ROOTFS_TAG ROOTFS_HASH -linux 64_st_no_bb false 64_no_bb x86_64 none 60 no yes no v4.4 ec3873fc1756ecb4c815e8785e9ca45cf54c68b6 +# PLATFORM LABEL GROUP ALLOW_FAIL ARCH ARCH_ROOTFS MAKE_FLAGS TIMEOUT_BK TIMEOUT_RR RETRIES IS_RR IS_ST IS_MT ROOTFS_TAG ROOTFS_HASH +linux 64src_g1_mt g1 . 64src x86_64 . . . . . . yes v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a +linux 64src_g2_mt g2 . 64src x86_64 . . . 3 . . yes v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a + +linux 64src_g1_st g1 . 64src x86_64 . . . . . yes . v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a +linux 64src_g2_st g2 . 64src x86_64 . . . 3 . yes . v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a + +linux 64src_g1_rrst g1 . 64src x86_64 . 300 240 . yes yes . v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a +linux 64src_g2_rrst g2 . 64src x86_64 . 180 120 3 yes yes . v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a +linux 64src_g3_st g3 . 64src x86_64 . . . 3 . yes . v4.8 2a058481b567f0e91b9aa3ce4ad4f09e6419355a diff --git a/.buildkite/utilities/platforms/platforms.sh b/.buildkite/utilities/platforms/platforms.sh index 87daa8a2a9e28..9a47c18e9855b 100755 --- a/.buildkite/utilities/platforms/platforms.sh +++ b/.buildkite/utilities/platforms/platforms.sh @@ -4,12 +4,12 @@ ARCHES="$1" YAML="$2" if [[ ! -f "${ARCHES:?}" ]] ; then - echo "File does not exist: ${ARCHES:?}" + echo "Arches file does not exist: ${ARCHES:?}" exit 1 fi if [[ ! -f "${YAML:?}" ]] ; then - echo "File does not exist: ${YAML:?}" + echo "YAML file does not exist: ${YAML:?}" exit 1 fi @@ -17,27 +17,60 @@ cat "${ARCHES:?}" | tr -s ' ' | while read _line; do # Remove whitespace from the beginning and end of each line line=`echo $_line | tr -s ' '` - # Skip all lines that begin with `#` + # Skip any line that begins with the `#` character if [[ $line == \#* ]]; then continue fi + # Skip any empty line + if [[ $line == "" ]]; then + continue + fi + export PLATFORM=`echo $line | cut -d ' ' -f 1 | tr -s ' '` export LABEL=`echo $line | cut -d ' ' -f 2 | tr -s ' '` - export ALLOW_FAIL=`echo $line | cut -d ' ' -f 3 | tr -s ' '` - export ARCH=`echo $line | cut -d ' ' -f 4 | tr -s ' '` - export ARCH_ROOTFS=`echo $line | cut -d ' ' -f 5 | tr -s ' '` - export MAKE_FLAGS=`echo $line | cut -d ' ' -f 6 | tr -s ' '` - export TIMEOUT=`echo $line | cut -d ' ' -f 7 | tr -s ' '` - export IS_RR=`echo $line | cut -d ' ' -f 8 | tr -s ' '` - export IS_ST=`echo $line | cut -d ' ' -f 9 | tr -s ' '` - export IS_MT=`echo $line | cut -d ' ' -f 10 | tr -s ' '` - export ROOTFS_TAG=`echo $line | cut -d ' ' -f 11 | tr -s ' '` - export ROOTFS_HASH=`echo $line | cut -d ' ' -f 12 | tr -s ' '` - - if [[ "${MAKE_FLAGS:?}" == "none" ]]; then + export GROUP=`echo $line | cut -d ' ' -f 3 | tr -s ' '` + + export ALLOW_FAIL=`echo $line | cut -d ' ' -f 4 | tr -s ' '` + export ARCH=`echo $line | cut -d ' ' -f 5 | tr -s ' '` + export ARCH_ROOTFS=`echo $line | cut -d ' ' -f 6 | tr -s ' '` + + export MAKE_FLAGS=`echo $line | cut -d ' ' -f 7 | tr -s ' '` + export TIMEOUT_BK=`echo $line | cut -d ' ' -f 8 | tr -s ' '` + export TIMEOUT_RR=`echo $line | cut -d ' ' -f 9 | tr -s ' '` + export RETRIES=`echo $line | cut -d ' ' -f 10 | tr -s ' '` + export IS_RR=`echo $line | cut -d ' ' -f 11 | tr -s ' '` + export IS_ST=`echo $line | cut -d ' ' -f 12 | tr -s ' '` + export IS_MT=`echo $line | cut -d ' ' -f 13 | tr -s ' '` + export ROOTFS_TAG=`echo $line | cut -d ' ' -f 14 | tr -s ' '` + export ROOTFS_HASH=`echo $line | cut -d ' ' -f 15 | tr -s ' '` + + if [[ "${IS_ST:?}" == "yes" ]]; then + if [[ "${IS_MT:?}" == "yes" ]]; then + echo "You cannot set both IS_ST and IS_MT to yes" + exit 1 + fi + fi + + if [[ "${ALLOW_FAIL:?}" == "." ]]; then + export ALLOW_FAIL="false" + fi + + if [[ "${MAKE_FLAGS:?}" == "." ]]; then export MAKE_FLAGS="" fi + if [[ "${TIMEOUT_BK:?}" == "." ]]; then + export TIMEOUT_BK="90" # minutes + fi + + if [[ "${TIMEOUT_RR:?}" == "." ]]; then + export TIMEOUT_RR="60" # minutes + fi + + if [[ "${RETRIES:?}" == "." ]]; then + export RETRIES="0" + fi + buildkite-agent pipeline upload "${YAML:?}" done diff --git a/.buildkite/utilities/rr/rr_capture.jl b/.buildkite/utilities/rr/rr_capture.jl index 5d60377a72bb6..37bf3ca124271 100644 --- a/.buildkite/utilities/rr/rr_capture.jl +++ b/.buildkite/utilities/rr/rr_capture.jl @@ -55,13 +55,14 @@ end @info "We will run the command under rr" -const build_number = get_from_env("BUILDKITE_BUILD_NUMBER") -const job_name = get_from_env("BUILDKITE_STEP_KEY") -const commit_full = get_from_env("BUILDKITE_COMMIT") -const commit_short = first(commit_full, 10) -const timeout_minutes = 120 -const JULIA_TEST_NUM_CORES = get(ENV, "JULIA_TEST_NUM_CORES", "8") -const julia_test_num_cores_int = parse(Int, JULIA_TEST_NUM_CORES) +const build_number = get_from_env("BUILDKITE_BUILD_NUMBER") +const job_name = get_from_env("BUILDKITE_STEP_KEY") +const commit_full = get_from_env("BUILDKITE_COMMIT") +const commit_short = first(commit_full, 10) +const JULIA_TEST_RR_TIMEOUT = get(ENV, "JULIA_TEST_RR_TIMEOUT", "120") +const timeout_minutes = parse(Int, JULIA_TEST_RR_TIMEOUT) +const JULIA_TEST_NUM_CORES = get(ENV, "JULIA_TEST_NUM_CORES", "8") +const julia_test_num_cores_int = parse(Int, JULIA_TEST_NUM_CORES) const num_cores = min( 8, Sys.CPU_THREADS, diff --git a/.github/workflows/statuses.yml b/.github/workflows/statuses.yml index 57d7596d8ee83..16c07f0f040cc 100644 --- a/.github/workflows/statuses.yml +++ b/.github/workflows/statuses.yml @@ -48,9 +48,6 @@ jobs: - run: | declare -a CONTEXT_LIST=( "buildbot/tester_freebsd64" - "buildbot/tester_linux32" - "buildbot/tester_linux64" - "buildbot/tester_linuxaarch64" "buildbot/tester_macos64" "buildbot/tester_win32" "buildbot/tester_win64" diff --git a/.gitignore b/.gitignore index 2c5ee63bc3ee9..678f4fc73cec2 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,9 @@ .DS_Store .idea/* .vscode/* + +# Buildkite: cryptic plugin +# Ignore the unencrypted repo_key +repo_key +# Ignore any agent keys (public or private) we have stored +agent_key* diff --git a/Make.inc b/Make.inc index 5651023261152..0f9c2a82e5e47 100644 --- a/Make.inc +++ b/Make.inc @@ -41,6 +41,7 @@ USE_SYSTEM_LIBM:=0 USE_SYSTEM_OPENLIBM:=0 UNTRUSTED_SYSTEM_LIBM:=0 USE_SYSTEM_DSFMT:=0 +USE_SYSTEM_LIBBLASTRAMPOLINE:=0 USE_SYSTEM_BLAS:=0 USE_SYSTEM_LAPACK:=0 USE_SYSTEM_GMP:=0 @@ -54,6 +55,7 @@ USE_SYSTEM_NGHTTP2:=0 USE_SYSTEM_CURL:=0 USE_SYSTEM_LIBGIT2:=0 USE_SYSTEM_PATCHELF:=0 +USE_SYSTEM_LIBWHICH:=0 USE_SYSTEM_ZLIB:=0 USE_SYSTEM_P7ZIP:=0 @@ -1064,6 +1066,12 @@ else PATCHELF := $(build_depsbindir)/patchelf endif +ifeq ($(USE_SYSTEM_LIBWHICH), 1) +LIBWHICH := libwhich +else +LIBWHICH := $(build_depsbindir)/libwhich +endif + # On aarch64 and powerpc64le, we assume the page size is 64K. Our binutils linkers # and such already assume this, but `patchelf` seems to be behind the times. We # explicitly tell it to use this large page size so that when we rewrite rpaths and @@ -1233,12 +1241,12 @@ else ifeq ($(OS), Darwin) RPATH := -Wl,-rpath,'@executable_path/$(build_libdir_rel)' RPATH_ORIGIN := -Wl,-rpath,'@loader_path/' RPATH_ESCAPED_ORIGIN := $(RPATH_ORIGIN) - RPATH_LIB := -Wl,-rpath,'@loader_path/julia/' -Wl,-rpath,'@loader_path/' + RPATH_LIB := -Wl,-rpath,'@loader_path/' else RPATH := -Wl,-rpath,'$$ORIGIN/$(build_libdir_rel)' -Wl,-rpath,'$$ORIGIN/$(build_private_libdir_rel)' -Wl,-rpath-link,$(build_shlibdir) -Wl,-z,origin RPATH_ORIGIN := -Wl,-rpath,'$$ORIGIN' -Wl,-z,origin RPATH_ESCAPED_ORIGIN := -Wl,-rpath,'\$$\$$ORIGIN' -Wl,-z,origin -Wl,-rpath-link,$(build_shlibdir) - RPATH_LIB := -Wl,-rpath,'$$ORIGIN/julia' -Wl,-rpath,'$$ORIGIN' -Wl,-z,origin + RPATH_LIB := -Wl,-rpath,'$$ORIGIN/' -Wl,-z,origin endif # --whole-archive diff --git a/Makefile b/Makefile index 62afa8e685529..24eb91d84300a 100644 --- a/Makefile +++ b/Makefile @@ -165,13 +165,14 @@ JL_TARGETS += julia-debug endif # private libraries, that are installed in $(prefix)/lib/julia -JL_PRIVATE_LIBS-0 := libccalltest libllvmcalltest libjulia-internal libblastrampoline +JL_PRIVATE_LIBS-0 := libccalltest libllvmcalltest libjulia-internal ifeq ($(BUNDLE_DEBUG_LIBS),1) JL_PRIVATE_LIBS-0 += libjulia-internal-debug endif ifeq ($(USE_GPL_LIBS), 1) JL_PRIVATE_LIBS-$(USE_SYSTEM_LIBSUITESPARSE) += libamd libbtf libcamd libccolamd libcholmod libcolamd libklu libldl librbio libspqr libsuitesparseconfig libumfpack endif +JL_PRIVATE_LIBS-$(USE_SYSTEM_LIBBLASTRAMPOLINE) += libblastrampoline JL_PRIVATE_LIBS-$(USE_SYSTEM_PCRE) += libpcre2-8 JL_PRIVATE_LIBS-$(USE_SYSTEM_DSFMT) += libdSFMT JL_PRIVATE_LIBS-$(USE_SYSTEM_GMP) += libgmp libgmpxx @@ -368,6 +369,11 @@ ifeq ($(BUNDLE_DEBUG_LIBS),1) endif endif + # Set rpath for LLVM.so which is `$ORIGIN/../lib` moving from `../lib` to `../lib/julia`. We only need to do this for Linux/FreeBSD +ifneq (,$(findstring $(OS),Linux FreeBSD)) + $(PATCHELF) --set-rpath '$$ORIGIN:$$ORIGIN/$(reverse_private_libdir_rel)' $(DESTDIR)$(private_libdir)/libLLVM.$(SHLIB_EXT) +endif + ifneq ($(LOADER_BUILD_DEP_LIBS),$(LOADER_INSTALL_DEP_LIBS)) # Next, overwrite relative path to libjulia-internal in our loader if $$(LOADER_BUILD_DEP_LIBS) != $$(LOADER_INSTALL_DEP_LIBS) diff --git a/NEWS.md b/NEWS.md index e5922cbd8a4fe..4160ce40e7c29 100644 --- a/NEWS.md +++ b/NEWS.md @@ -41,6 +41,8 @@ Language changes * `Iterators.peel(itr)` now returns `nothing` when `itr` is empty instead of throwing a `BoundsError` ([#39607]). * Multiple successive semicolons in an array expresion were previously ignored (e.g., `[1 ;; 2] == [1 ; 2]`). This syntax is now used to separate dimensions (see **New language features**). +* Unbalanced Unicode bidirectional formatting directives are now disallowed within strings and comments, + to mitigate the ["trojan source"](https://www.trojansource.codes) vulnerability ([#42918]). Compiler/Runtime improvements ----------------------------- diff --git a/VERSION b/VERSION index 57dfe270da0e9..65f18517c9a25 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.7.0-rc1 +1.7.0-rc2 diff --git a/base/Base.jl b/base/Base.jl index 09fdcb1689de3..b3da1458836c0 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -216,8 +216,6 @@ let os = ccall(:jl_get_UNAME, Any, ()) if os === :Darwin || os === :Apple if Base.DARWIN_FRAMEWORK push!(DL_LOAD_PATH, "@loader_path/Frameworks") - else - push!(DL_LOAD_PATH, "@loader_path/julia") end push!(DL_LOAD_PATH, "@loader_path") end diff --git a/base/Makefile b/base/Makefile index 56e1cbebf21bf..c7a2c7a241e83 100644 --- a/base/Makefile +++ b/base/Makefile @@ -164,7 +164,7 @@ endif define symlink_system_library libname_$2 := $$(notdir $(call versioned_libname,$2,$3)) -libpath_$2 := $$(shell $$(call spawn,$$(build_depsbindir)/libwhich) -p $$(libname_$2) 2>/dev/null) +libpath_$2 := $$(shell $$(call spawn,$$(LIBWHICH)) -p $$(libname_$2) 2>/dev/null) symlink_$2: $$(build_private_libdir)/$$(libname_$2) $$(build_private_libdir)/$$(libname_$2): @if [ -e "$$(libpath_$2)" ]; then \ @@ -205,6 +205,7 @@ $(eval $(call symlink_system_library,CSL,libatomic,1,ALLOW_FAILURE)) $(eval $(call symlink_system_library,CSL,libgomp,1,ALLOW_FAILURE)) $(eval $(call symlink_system_library,PCRE,libpcre2-8)) $(eval $(call symlink_system_library,DSFMT,libdSFMT)) +$(eval $(call symlink_system_library,LIBBLASTRAMPOLINE,libblastrampoline)) $(eval $(call symlink_system_library,BLAS,$(LIBBLASNAME))) ifneq ($(LIBLAPACKNAME),$(LIBBLASNAME)) $(eval $(call symlink_system_library,LAPACK,$(LIBLAPACKNAME))) diff --git a/base/asyncevent.jl b/base/asyncevent.jl index c8d2c404b0a09..ca0e5da512147 100644 --- a/base/asyncevent.jl +++ b/base/asyncevent.jl @@ -60,10 +60,13 @@ end Create a timer that wakes up tasks waiting for it (by calling [`wait`](@ref) on the timer object). -Waiting tasks are woken after an initial delay of `delay` seconds, and then repeating with the given -`interval` in seconds. If `interval` is equal to `0`, the timer is only triggered once. When -the timer is closed (by [`close`](@ref)) waiting tasks are woken with an error. Use [`isopen`](@ref) -to check whether a timer is still active. +Waiting tasks are woken after an initial delay of at least `delay` seconds, and then repeating after +at least `interval` seconds again elapse. If `interval` is equal to `0`, the timer is only triggered +once. When the timer is closed (by [`close`](@ref)) waiting tasks are woken with an error. Use +[`isopen`](@ref) to check whether a timer is still active. + +Note: `interval` is subject to accumulating time skew. If you need precise events at a particular +absolute time, create a new timer at each expiration with the difference to the next time computed. """ mutable struct Timer handle::Ptr{Cvoid} @@ -74,8 +77,9 @@ mutable struct Timer function Timer(timeout::Real; interval::Real = 0.0) timeout ≥ 0 || throw(ArgumentError("timer cannot have negative timeout of $timeout seconds")) interval ≥ 0 || throw(ArgumentError("timer cannot have negative repeat interval of $interval seconds")) - timeout = UInt64(round(timeout * 1000)) + 1 - interval = UInt64(ceil(interval * 1000)) + # libuv has a tendency to timeout 1 ms early, so we need +1 on the timeout (in milliseconds), unless it is zero + timeoutms = ceil(UInt64, timeout * 1000) + !iszero(timeout) + intervalms = ceil(UInt64, interval * 1000) loop = eventloop() this = new(Libc.malloc(_sizeof_uv_timer), ThreadSynchronizer(), true, false) @@ -87,7 +91,7 @@ mutable struct Timer ccall(:uv_update_time, Cvoid, (Ptr{Cvoid},), loop) err = ccall(:uv_timer_start, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, UInt64, UInt64), this, @cfunction(uv_timercb, Cvoid, (Ptr{Cvoid},)), - timeout, interval) + timeoutms, intervalms) @assert err == 0 iolock_end() return this @@ -222,18 +226,18 @@ end """ Timer(callback::Function, delay; interval = 0) -Create a timer that wakes up tasks waiting for it (by calling [`wait`](@ref) on the timer object) and -calls the function `callback`. +Create a timer that runs the function `callback` at each timer expiration. -Waiting tasks are woken and the function `callback` is called after an initial delay of `delay` seconds, -and then repeating with the given `interval` in seconds. If `interval` is equal to `0`, the timer -is only triggered once. The function `callback` is called with a single argument, the timer itself. -When the timer is closed (by [`close`](@ref)) waiting tasks are woken with an error. Use [`isopen`](@ref) -to check whether a timer is still active. +Waiting tasks are woken and the function `callback` is called after an initial delay of `delay` +seconds, and then repeating with the given `interval` in seconds. If `interval` is equal to `0`, the +callback is only run once. The function `callback` is called with a single argument, the timer +itself. Stop a timer by calling `close`. The `cb` may still be run one final time, if the timer has +already expired. # Examples -Here the first number is printed after a delay of two seconds, then the following numbers are printed quickly. +Here the first number is printed after a delay of two seconds, then the following numbers are +printed quickly. ```julia-repl julia> begin diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index a057a1879412c..f1f6e78e241f6 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -568,17 +568,27 @@ end function maybe_get_const_prop_profitable(interp::AbstractInterpreter, result::MethodCallResult, @nospecialize(f), argtypes::Vector{Any}, match::MethodMatch, sv::InferenceState) - const_prop_entry_heuristic(interp, result, sv) || return nothing + if !InferenceParams(interp).ipo_constant_propagation + add_remark!(interp, sv, "[constprop] Disabled by parameter") + return nothing + end method = match.method + force = force_const_prop(interp, f, method) + force || const_prop_entry_heuristic(interp, result, sv) || return nothing nargs::Int = method.nargs method.isva && (nargs -= 1) - if length(argtypes) < nargs + length(argtypes) < nargs && return nothing + if !(const_prop_argument_heuristic(interp, argtypes) || const_prop_rettype_heuristic(interp, result.rt)) + add_remark!(interp, sv, "[constprop] Disabled by argument and rettype heuristics") return nothing end - const_prop_argument_heuristic(interp, argtypes) || const_prop_rettype_heuristic(interp, result.rt) || return nothing allconst = is_allconst(argtypes) - force = force_const_prop(interp, f, method) - force || const_prop_function_heuristic(interp, f, argtypes, nargs, allconst) || return nothing + if !force + if !const_prop_function_heuristic(interp, f, argtypes, nargs, allconst) + add_remark!(interp, sv, "[constprop] Disabled by function heuristic") + return nothing + end + end force |= allconst mi = specialize_method(match, !force) if mi === nothing @@ -594,8 +604,13 @@ function maybe_get_const_prop_profitable(interp::AbstractInterpreter, result::Me end function const_prop_entry_heuristic(interp::AbstractInterpreter, result::MethodCallResult, sv::InferenceState) - call_result_unused(sv) && result.edgecycle && return false - return is_improvable(result.rt) && InferenceParams(interp).ipo_constant_propagation + if call_result_unused(sv) && result.edgecycle + add_remark!(interp, sv, "[constprop] Disabled by entry heuristic (edgecycle with unused result)") + return false + end + is_improvable(result.rt) && return true + add_remark!(interp, sv, "[constprop] Disabled by entry heuristic (unimprovable return type)") + return false end # see if propagating constants may be worthwhile @@ -670,14 +685,14 @@ function const_prop_function_heuristic(interp::AbstractInterpreter, @nospecializ # but highly worthwhile to inline promote of a constant length(argtypes) > 2 || return false t1 = widenconst(argtypes[2]) - all_same = true for i in 3:length(argtypes) - if widenconst(argtypes[i]) !== t1 - all_same = false - break + at = argtypes[i] + ty = isvarargtype(at) ? unwraptv(at) : widenconst(at) + if ty !== t1 + return true end end - return !all_same + return false end return true end @@ -756,10 +771,7 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) if isa(tti, DataType) && tti.name === NamedTuple_typename # A NamedTuple iteration is the same as the iteration of its Tuple parameter: # compute a new `tti == unwrap_unionall(tti0)` based on that Tuple type - tti = tti.parameters[2] - while isa(tti, TypeVar) - tti = tti.ub - end + tti = unwraptv(tti.parameters[2]) tti0 = rewrap_unionall(tti, tti0) end if isa(tti, Union) @@ -1141,7 +1153,8 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, fargs::U end end end - return isa(rt, TypeVar) ? rt.ub : rt + @assert !isa(rt, TypeVar) "unhandled TypeVar" + return rt end function abstract_call_unionall(argtypes::Vector{Any}) @@ -1242,9 +1255,12 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), return CallMeta(abstract_call_builtin(interp, f, fargs, argtypes, sv, max_methods), false) elseif f === Core.kwfunc if la == 2 - ft = widenconst(argtypes[2]) - if isa(ft, DataType) && isdefined(ft.name, :mt) && isdefined(ft.name.mt, :kwsorter) - return CallMeta(Const(ft.name.mt.kwsorter), MethodResultPure()) + aty = argtypes[2] + if !isvarargtype(aty) + ft = widenconst(aty) + if isa(ft, DataType) && isdefined(ft.name, :mt) && isdefined(ft.name.mt, :kwsorter) + return CallMeta(Const(ft.name.mt.kwsorter), MethodResultPure()) + end end end return CallMeta(Any, false) @@ -1264,8 +1280,12 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), return CallMeta(typevar_tfunc(n, lb_var, ub_var), false) elseif f === UnionAll return CallMeta(abstract_call_unionall(argtypes), false) - elseif f === Tuple && la == 2 && !isconcretetype(widenconst(argtypes[2])) - return CallMeta(Tuple, false) + elseif f === Tuple && la == 2 + aty = argtypes[2] + ty = isvarargtype(aty) ? unwrapva(aty) : widenconst(aty) + if !isconcretetype(ty) + return CallMeta(Tuple, false) + end elseif is_return_type(f) return return_type_tfunc(interp, argtypes, sv) elseif la == 2 && istopfunction(f, :!) @@ -1405,7 +1425,7 @@ function sp_type_rewrap(@nospecialize(T), linfo::MethodInstance, isreturn::Bool) spsig = linfo.def.sig if isa(spsig, UnionAll) if !isempty(linfo.sparam_vals) - sparam_vals = Any[isa(v, Core.TypeofVararg) ? TypeVar(:N, Union{}, Any) : + sparam_vals = Any[isvarargtype(v) ? TypeVar(:N, Union{}, Any) : v for v in linfo.sparam_vals] T = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), T, spsig, sparam_vals) isref && isreturn && T === Any && return Bottom # catch invalid return Ref{T} where T = Any @@ -1419,10 +1439,7 @@ function sp_type_rewrap(@nospecialize(T), linfo::MethodInstance, isreturn::Bool) end end end - while isa(T, TypeVar) - T = T.ub - end - return T + return unwraptv(T) end function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::VarTable, sv::InferenceState) @@ -1636,7 +1653,7 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), else t = abstract_eval_value_expr(interp, e, vtypes, sv) end - @assert !isa(t, TypeVar) + @assert !isa(t, TypeVar) "unhandled TypeVar" if isa(t, DataType) && isdefined(t, :instance) # replace singleton types with their equivalent Const object t = Const(t.instance) @@ -1721,7 +1738,8 @@ function widenreturn(@nospecialize(rt), @nospecialize(bestguess), nslots::Int, s fields = copy(rt.fields) haveconst = false for i in 1:length(fields) - a = widenreturn(fields[i], bestguess, nslots, slottypes, changes) + a = fields[i] + a = isvarargtype(a) ? a : widenreturn(a, bestguess, nslots, slottypes, changes) if !haveconst && has_const_info(a) # TODO: consider adding && const_prop_profitable(a) here? haveconst = true diff --git a/base/compiler/bootstrap.jl b/base/compiler/bootstrap.jl index f351429aff7eb..7e0dfaf9d2a78 100644 --- a/base/compiler/bootstrap.jl +++ b/base/compiler/bootstrap.jl @@ -25,9 +25,7 @@ let fs = Any[typeinf_ext, typeinf, typeinf_edge, pure_eval_call, run_passes], # remove any TypeVars from the intersection typ = Any[m.spec_types.parameters...] for i = 1:length(typ) - if isa(typ[i], TypeVar) - typ[i] = typ[i].ub - end + typ[i] = unwraptv(typ[i]) end typeinf_type(interp, m.method, Tuple{typ...}, m.sparams) end diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 6a0362b78f759..60596b550546b 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -6,7 +6,8 @@ using Core.Intrinsics, Core.IR import Core: print, println, show, write, unsafe_write, stdout, stderr, _apply_iterate, svec, apply_type, Builtin, IntrinsicFunction, - MethodInstance, CodeInstance, MethodMatch, PartialOpaque + MethodInstance, CodeInstance, MethodMatch, PartialOpaque, + TypeofVararg const getproperty = Core.getfield const setproperty! = Core.setfield! diff --git a/base/compiler/inferenceresult.jl b/base/compiler/inferenceresult.jl index 483e2f38d9ee8..fe484f310da38 100644 --- a/base/compiler/inferenceresult.jl +++ b/base/compiler/inferenceresult.jl @@ -50,7 +50,7 @@ end function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(specTypes), isva::Bool, withfirst::Bool = true) toplevel = method === nothing - linfo_argtypes = Any[unwrap_unionall(specTypes).parameters...] + linfo_argtypes = Any[(unwrap_unionall(specTypes)::DataType).parameters...] nargs::Int = toplevel ? 0 : method.nargs if !withfirst # For opaque closure, the closure environment is processed elsewhere @@ -80,8 +80,8 @@ function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(spe else vargtype_elements = Any[] for p in linfo_argtypes[nargs:linfo_argtypes_length] - p = isvarargtype(p) ? unconstrain_vararg_length(p) : p - push!(vargtype_elements, rewrap(p, specTypes)) + p = unwraptv(isvarargtype(p) ? unconstrain_vararg_length(p) : p) + push!(vargtype_elements, elim_free_typevars(rewrap(p, specTypes))) end for i in 1:length(vargtype_elements) atyp = vargtype_elements[i] @@ -113,16 +113,14 @@ function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(spe atyp = unwrapva(atyp) tail_index -= 1 end - while isa(atyp, TypeVar) - atyp = atyp.ub - end + atyp = unwraptv(atyp) if isa(atyp, DataType) && isdefined(atyp, :instance) # replace singleton types with their equivalent Const object atyp = Const(atyp.instance) elseif isconstType(atyp) atyp = Const(atyp.parameters[1]) else - atyp = rewrap(atyp, specTypes) + atyp = elim_free_typevars(rewrap(atyp, specTypes)) end i == n && (lastatype = atyp) cache_argtypes[i] = atyp @@ -136,6 +134,19 @@ function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(spe cache_argtypes end +# eliminate free `TypeVar`s in order to make the life much easier down the road: +# at runtime only `Type{...}::DataType` can contain invalid type parameters, and other +# malformed types here are user-constructed type arguments given at an inference entry +# so this function will replace only the malformed `Type{...}::DataType` with `Type` +# and simply replace other possibilities with `Any` +function elim_free_typevars(@nospecialize t) + if has_free_typevars(t) + return isType(t) ? Type : Any + else + return t + end +end + function matching_cache_argtypes(linfo::MethodInstance, ::Nothing, va_override::Bool) mthd = isa(linfo.def, Method) ? linfo.def::Method : nothing cache_argtypes = most_general_argtypes(mthd, linfo.specTypes, diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index f13622edb23fe..7d18915afc151 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -297,7 +297,7 @@ function sptypes_from_meth_instance(linfo::MethodInstance) ty = UnionAll(tv, Type{tv}) end end - elseif isa(v, Core.TypeofVararg) + elseif isvarargtype(v) ty = Int else ty = Const(v) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index a2e5a46787734..d454eeb1593b3 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -35,7 +35,6 @@ end struct DelayedInliningSpec match::Union{MethodMatch, InferenceResult} atypes::Vector{Any} - stmttype::Any end struct InliningTodo @@ -44,23 +43,32 @@ struct InliningTodo spec::Union{ResolvedInliningSpec, DelayedInliningSpec} end -InliningTodo(mi::MethodInstance, match::MethodMatch, - atypes::Vector{Any}, @nospecialize(stmttype)) = InliningTodo(mi, DelayedInliningSpec(match, atypes, stmttype)) +InliningTodo(mi::MethodInstance, match::MethodMatch, atypes::Vector{Any}) = + InliningTodo(mi, DelayedInliningSpec(match, atypes)) -InliningTodo(result::InferenceResult, atypes::Vector{Any}, @nospecialize(stmttype)) = - InliningTodo(result.linfo, DelayedInliningSpec(result, atypes, stmttype)) +InliningTodo(result::InferenceResult, atypes::Vector{Any}) = + InliningTodo(result.linfo, DelayedInliningSpec(result, atypes)) struct ConstantCase val::Any ConstantCase(val) = new(val) end +struct InliningCase + sig # ::Type + item # Union{InliningTodo, MethodInstance, ConstantCase} + function InliningCase(@nospecialize(sig), @nospecialize(item)) + @assert isa(item, Union{InliningTodo, MethodInstance, ConstantCase}) "invalid inlining item" + return new(sig, item) + end +end + struct UnionSplit fully_covered::Bool atype # ::Type - cases::Vector{Pair{Any, Any}} + cases::Vector{InliningCase} bbs::Vector{Int} - UnionSplit(fully_covered::Bool, atype, cases::Vector{Pair{Any, Any}}) = + UnionSplit(fully_covered::Bool, atype, cases::Vector{InliningCase}) = new(fully_covered, atype, cases, Int[]) end @@ -137,14 +145,13 @@ function cfg_inline_item!(ir::IRCode, idx::Int, spec::ResolvedInliningSpec, stat need_split = true #!(idx == last_block_idx) end - if !need_split - delete!(state.merged_orig_blocks, last(new_range)) - end + need_split || delete!(state.merged_orig_blocks, last(new_range)) push!(state.todo_bbs, (length(state.new_cfg_blocks) - 1 + (need_split_before ? 1 : 0), post_bb_id)) from_unionsplit || delete!(state.split_targets, length(state.new_cfg_blocks)) - orig_succs = copy(state.new_cfg_blocks[end].succs) + local orig_succs + need_split && (orig_succs = copy(state.new_cfg_blocks[end].succs)) empty!(state.new_cfg_blocks[end].succs) if need_split_before l = length(state.new_cfg_blocks) @@ -204,53 +211,51 @@ function cfg_inline_item!(ir::IRCode, idx::Int, spec::ResolvedInliningSpec, stat end end end + any_edges || push!(state.dead_blocks, post_bb_id) - if !any_edges - push!(state.dead_blocks, post_bb_id) - end + return nothing end -function cfg_inline_unionsplit!(ir::IRCode, idx::Int, item::UnionSplit, state::CFGInliningState) - block = block_for_inst(ir, idx) - inline_into_block!(state, block) +function cfg_inline_unionsplit!(ir::IRCode, idx::Int, + (; fully_covered, #=atype,=# cases, bbs)::UnionSplit, + state::CFGInliningState) + inline_into_block!(state, block_for_inst(ir, idx)) from_bbs = Int[] delete!(state.split_targets, length(state.new_cfg_blocks)) orig_succs = copy(state.new_cfg_blocks[end].succs) empty!(state.new_cfg_blocks[end].succs) - for (i, (_, case)) in enumerate(item.cases) + for i in 1:length(cases) # The condition gets sunk into the previous block # Add a block for the union-split body push!(state.new_cfg_blocks, BasicBlock(StmtRange(idx, idx))) cond_bb = length(state.new_cfg_blocks)-1 push!(state.new_cfg_blocks[end].preds, cond_bb) push!(state.new_cfg_blocks[cond_bb].succs, cond_bb+1) + case = cases[i].item if isa(case, InliningTodo) spec = case.spec::ResolvedInliningSpec if !spec.linear_inline_eligible cfg_inline_item!(ir, idx, spec, state, true) end end - bb = length(state.new_cfg_blocks) - push!(from_bbs, bb) + push!(from_bbs, length(state.new_cfg_blocks)) # TODO: Right now we unconditionally generate a fallback block # in case of subtyping errors - This is probably unnecessary. - if true # i != length(item.cases) || !item.fully_covered + if true # i != length(cases) || !fully_covered # This block will have the next condition or the final else case push!(state.new_cfg_blocks, BasicBlock(StmtRange(idx, idx))) push!(state.new_cfg_blocks[cond_bb].succs, length(state.new_cfg_blocks)) push!(state.new_cfg_blocks[end].preds, cond_bb) - push!(item.bbs, length(state.new_cfg_blocks)) + push!(bbs, length(state.new_cfg_blocks)) end end # The edge from the fallback block. - if !item.fully_covered - push!(from_bbs, length(state.new_cfg_blocks)) - end + fully_covered || push!(from_bbs, length(state.new_cfg_blocks)) # This block will be the block everyone returns to push!(state.new_cfg_blocks, BasicBlock(StmtRange(idx, idx), from_bbs, orig_succs)) join_bb = length(state.new_cfg_blocks) push!(state.split_targets, join_bb) - push!(item.bbs, join_bb) + push!(bbs, join_bb) for bb in from_bbs push!(state.new_cfg_blocks[bb].succs, join_bb) end @@ -258,8 +263,10 @@ end function finish_cfg_inline!(state::CFGInliningState) new_range = (state.first_bb + 1):length(state.cfg.blocks) - l = length(state.new_cfg_blocks) - state.bb_rename[new_range] = (l+1:l+length(new_range)) + state.bb_rename[new_range] = let + l = length(state.new_cfg_blocks) + l+1:l+length(new_range) + end append!(state.new_cfg_blocks, state.cfg.blocks[new_range]) # Rename edges original bbs @@ -307,7 +314,6 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector sparam_vals = item.mi.sparam_vals def = item.mi.def::Method inline_cfg = spec.ir.cfg - stmt = compact.result[idx][:inst] linetable_offset::Int32 = length(linetable) # Append the linetable of the inlined function to our line table inlined_at = Int(compact.result[idx][:line]) @@ -339,8 +345,7 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector vararg = mk_tuplecall!(compact, argexprs[nargs_def:end], topline) argexprs = Any[argexprs[1:(nargs_def - 1)]..., vararg] end - is_opaque = def.is_for_opaque_closure - if is_opaque + if def.is_for_opaque_closure # Replace the first argument by a load of the capture environment argexprs[1] = insert_node_here!(compact, NewInstruction(Expr(:call, GlobalRef(Core, :getfield), argexprs[1], QuoteNode(:captures)), @@ -358,7 +363,6 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector local return_value # Special case inlining that maintains the current basic block if there's only one BB in the target if spec.linear_inline_eligible - terminator = spec.ir[SSAValue(last(inline_cfg.blocks[1].stmts))] #compact[idx] = nothing inline_compact = IncrementalCompact(compact, spec.ir, compact.result_idx) for ((_, idx′), stmt′) in inline_compact @@ -449,16 +453,18 @@ const fatal_type_bound_error = ErrorException("fatal error in type inference (ty function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, argexprs::Vector{Any}, linetable::Vector{LineInfoNode}, - item::UnionSplit, boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}}) + (; fully_covered, atype, cases, bbs)::UnionSplit, + boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}}) stmt, typ, line = compact.result[idx][:inst], compact.result[idx][:type], compact.result[idx][:line] - atype = item.atype - generic_bb = item.bbs[end-1] - join_bb = item.bbs[end] - bb = compact.active_result_bb + join_bb = bbs[end] pn = PhiNode() - has_generic = false - @assert length(item.bbs) > length(item.cases) - for ((metharg, case), next_cond_bb) in zip(item.cases, item.bbs) + local bb = compact.active_result_bb + @assert length(bbs) > length(cases) + for i in 1:length(cases) + ithcase = cases[i] + metharg = ithcase.sig + case = ithcase.item + next_cond_bb = bbs[i] @assert !isa(metharg, UnionAll) cond = true @assert length(atype.parameters) == length(metharg.parameters) @@ -513,7 +519,7 @@ function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, end bb += 1 # We're now in the fall through block, decide what to do - if item.fully_covered + if fully_covered e = Expr(:call, GlobalRef(Core, :throw), fatal_type_bound_error) insert_node_here!(compact, NewInstruction(e, Union{}, line)) insert_node_here!(compact, NewInstruction(ReturnNode(), Union{}, line)) @@ -672,8 +678,9 @@ function rewrite_apply_exprargs!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx:: info = call.info handled = false if isa(info, ConstCallInfo) - if maybe_handle_const_call!(ir, state1.id, new_stmt, info, new_sig, - call.rt, istate, false, todo) + if maybe_handle_const_call!( + ir, state1.id, new_stmt, info, new_sig, + istate, false, todo) handled = true else info = info.call @@ -683,8 +690,9 @@ function rewrite_apply_exprargs!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx:: info = isa(info, MethodMatchInfo) ? MethodMatchInfo[info] : info.matches # See if we can inline this call to `iterate` - analyze_single_call!(ir, todo, state1.id, new_stmt, - new_sig, call.rt, info, istate) + analyze_single_call!( + ir, todo, state1.id, new_stmt, + new_sig, info, istate) end if i != length(thisarginfo.each) valT = getfield_tfunc(call.rt, Const(1)) @@ -704,11 +712,13 @@ function rewrite_apply_exprargs!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx:: return new_argexprs, new_atypes end -function rewrite_invoke_exprargs!(argexprs::Vector{Any}) +function rewrite_invoke_exprargs!(expr::Expr) + argexprs = expr.args argexpr0 = argexprs[2] - argexprs = argexprs[4:end] - pushfirst!(argexprs, argexpr0) - return argexprs + argexprs = argexprs[3:end] + argexprs[1] = argexpr0 + expr.args = argexprs + return expr end function singleton_type(@nospecialize(ft)) @@ -785,28 +795,26 @@ function resolve_todo(todo::InliningTodo, state::InliningState) return InliningTodo(todo.mi, src) end -function resolve_todo(todo::UnionSplit, state::InliningState) - UnionSplit(todo.fully_covered, todo.atype, - Pair{Any,Any}[sig=>resolve_todo(item, state) for (sig, item) in todo.cases]) -end - -function resolve_todo!(todo::Vector{Pair{Int, Any}}, state::InliningState) - for i = 1:length(todo) - idx, item = todo[i] - todo[i] = idx=>resolve_todo(item, state) +function resolve_todo((; fully_covered, atype, cases, #=bbs=#)::UnionSplit, state::InliningState) + ncases = length(cases) + newcases = Vector{InliningCase}(undef, ncases) + for i in 1:ncases + (; sig, item) = cases[i] + newitem = resolve_todo(item, state) + push!(newcases, InliningCase(sig, newitem)) end - todo + return UnionSplit(fully_covered, atype, newcases) end function validate_sparams(sparams::SimpleVector) for i = 1:length(sparams) - (isa(sparams[i], TypeVar) || isa(sparams[i], Core.TypeofVararg)) && return false + (isa(sparams[i], TypeVar) || isvarargtype(sparams[i])) && return false end return true end function analyze_method!(match::MethodMatch, atypes::Vector{Any}, - state::InliningState, @nospecialize(stmttyp)) + state::InliningState) method = match.method methsig = method.sig @@ -836,7 +844,7 @@ function analyze_method!(match::MethodMatch, atypes::Vector{Any}, return compileable_specialization(state.et, match) end - todo = InliningTodo(mi, match, atypes, stmttyp) + todo = InliningTodo(mi, match, atypes) # If we don't have caches here, delay resolving this MethodInstance # until the batch inlining step (or an external post-processing pass) state.mi_cache === nothing && return todo @@ -906,17 +914,13 @@ function handle_single_case!(ir::IRCode, stmt::Expr, idx::Int, @nospecialize(cas if isa(case, ConstantCase) ir[SSAValue(idx)] = case.val elseif isa(case, MethodInstance) - if isinvoke - stmt.args = rewrite_invoke_exprargs!(stmt.args) - end + isinvoke && rewrite_invoke_exprargs!(stmt) stmt.head = :invoke pushfirst!(stmt.args, case) elseif case === nothing # Do, well, nothing else - if isinvoke - stmt.args = rewrite_invoke_exprargs!(stmt.args) - end + isinvoke && rewrite_invoke_exprargs!(stmt) push!(todo, idx=>(case::InliningTodo)) end nothing @@ -933,9 +937,7 @@ function is_valid_type_for_apply_rewrite(@nospecialize(typ), params::Optimizatio typ = widenconst(typ) if isa(typ, DataType) && typ.name === NamedTuple_typename typ = typ.parameters[2] - while isa(typ, TypeVar) - typ = typ.ub - end + typ = unwraptv(typ) end isa(typ, DataType) || return false if typ.name === Tuple.name @@ -1068,7 +1070,6 @@ is_builtin(s::Signature) = function inline_invoke!(ir::IRCode, idx::Int, sig::Signature, (; match, result)::InvokeCallInfo, state::InliningState, todo::Vector{Pair{Int, Any}}) stmt = ir.stmts[idx][:inst] - calltype = ir.stmts[idx][:type] if !match.fully_covers # TODO: We could union split out the signature check and continue on @@ -1081,7 +1082,7 @@ function inline_invoke!(ir::IRCode, idx::Int, sig::Signature, (; match, result): pushfirst!(atypes, atype0) if isa(result, InferenceResult) - (; mi) = item = InliningTodo(result, atypes, calltype) + (; mi) = item = InliningTodo(result, atypes) validate_sparams(mi.sparam_vals) || return nothing if argtypes_to_type(atypes) <: mi.def.sig state.mi_cache !== nothing && (item = resolve_todo(item, state)) @@ -1090,7 +1091,7 @@ function inline_invoke!(ir::IRCode, idx::Int, sig::Signature, (; match, result): end end - result = analyze_method!(match, atypes, state, calltype) + result = analyze_method!(match, atypes, state) handle_single_case!(ir, stmt, idx, result, true, todo) return nothing end @@ -1199,13 +1200,13 @@ function process_simple!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int, sta return sig end -function analyze_single_call!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int, @nospecialize(stmt), - sig::Signature, @nospecialize(calltype), infos::Vector{MethodMatchInfo}, - state::InliningState) - cases = Pair{Any, Any}[] - signature_union = Union{} - only_method = nothing # keep track of whether there is one matching method - too_many = false +# TODO inline non-`isdispatchtuple`, union-split callsites +function analyze_single_call!( + ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int, @nospecialize(stmt), + (; atypes, atype)::Signature, infos::Vector{MethodMatchInfo}, state::InliningState) + cases = InliningCase[] + local signature_union = Bottom + local only_method = nothing # keep track of whether there is one matching method local meth local fully_covered = true for i in 1:length(infos) @@ -1214,8 +1215,7 @@ function analyze_single_call!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int if meth.ambig # Too many applicable methods # Or there is a (partial?) ambiguity - too_many = true - break + return elseif length(meth) == 0 # No applicable methods; try next union split continue @@ -1235,38 +1235,36 @@ function analyze_single_call!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int fully_covered = false continue end - case = analyze_method!(match, sig.atypes, state, calltype) - if case === nothing + item = analyze_method!(match, atypes, state) + if item === nothing fully_covered = false continue - elseif _any(p->p[1] === spec_types, cases) + elseif _any(case->case.sig === spec_types, cases) continue end - push!(cases, Pair{Any,Any}(spec_types, case)) + push!(cases, InliningCase(spec_types, item)) end end - too_many && return - - signature_fully_covered = sig.atype <: signature_union - # If we're fully covered and there's only one applicable method, - # we inline, even if the signature is not a dispatch tuple - if signature_fully_covered && length(cases) == 0 && only_method isa Method - if length(infos) > 1 - (metharg, methsp) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), - sig.atype, only_method.sig)::SimpleVector - match = MethodMatch(metharg, methsp, only_method, true) - else - meth = meth::MethodLookupResult - @assert length(meth) == 1 - match = meth[1] + # if the signature is fully covered and there is only one applicable method, + # we can try to inline it even if the signature is not a dispatch tuple + if atype <: signature_union + if length(cases) == 0 && only_method isa Method + if length(infos) > 1 + (metharg, methsp) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), + atype, only_method.sig)::SimpleVector + match = MethodMatch(metharg, methsp, only_method, true) + else + meth = meth::MethodLookupResult + @assert length(meth) == 1 + match = meth[1] + end + item = analyze_method!(match, atypes, state) + item === nothing && return + push!(cases, InliningCase(match.spec_types, item)) + fully_covered = true end - fully_covered = true - case = analyze_method!(match, sig.atypes, state, calltype) - case === nothing && return - push!(cases, Pair{Any,Any}(match.spec_types, case)) - end - if !signature_fully_covered + else fully_covered = false end @@ -1274,42 +1272,82 @@ function analyze_single_call!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int # be able to do the inlining now (for constant cases), or push it directly # onto the todo list if fully_covered && length(cases) == 1 - handle_single_case!(ir, stmt, idx, cases[1][2], false, todo) - return + handle_single_case!(ir, stmt, idx, cases[1].item, false, todo) + elseif length(cases) > 0 + push!(todo, idx=>UnionSplit(fully_covered, atype, cases)) end - length(cases) == 0 && return - push!(todo, idx=>UnionSplit(fully_covered, sig.atype, cases)) return nothing end -function maybe_handle_const_call!(ir::IRCode, idx::Int, stmt::Expr, - info::ConstCallInfo, sig::Signature, @nospecialize(calltype), - state::InliningState, - isinvoke::Bool, todo::Vector{Pair{Int, Any}}) - # when multiple matches are found, bail out and later inliner will union-split this signature - # TODO effectively use multiple constant analysis results here - length(info.results) == 1 || return false - result = info.results[1] - isa(result, InferenceResult) || return false - - item = InliningTodo(result, sig.atypes, calltype) - validate_sparams(item.mi.sparam_vals) || return true - mthd_sig = item.mi.def.sig - mistypes = item.mi.specTypes - state.mi_cache !== nothing && (item = resolve_todo(item, state)) - if sig.atype <: mthd_sig - handle_single_case!(ir, stmt, idx, item, isinvoke, todo) - return true - else - item === nothing && return true - # Union split out the error case - item = UnionSplit(false, sig.atype, Pair{Any, Any}[mistypes => item]) - if isinvoke - stmt.args = rewrite_invoke_exprargs!(stmt.args) +# try to create `InliningCase`s using constant-prop'ed results +# currently it works only when constant-prop' succeeded for all (union-split) signatures +# TODO use any of constant-prop'ed results, and leave the other unhandled cases to later +# TODO this function contains a lot of duplications with `analyze_single_call!`, factor them out +function maybe_handle_const_call!( + ir::IRCode, idx::Int, stmt::Expr, (; results)::ConstCallInfo, (; atypes, atype)::Signature, + state::InliningState, isinvoke::Bool, todo::Vector{Pair{Int, Any}}) + cases = InliningCase[] # TODO avoid this allocation for single cases ? + local fully_covered = true + local signature_union = Bottom + for result in results + isa(result, InferenceResult) || return false + (; mi) = item = InliningTodo(result, atypes) + spec_types = mi.specTypes + signature_union = Union{signature_union, spec_types} + if !isdispatchtuple(spec_types) + fully_covered = false + continue end - push!(todo, idx=>item) - return true + if !validate_sparams(mi.sparam_vals) + fully_covered = false + continue + end + state.mi_cache !== nothing && (item = resolve_todo(item, state)) + if item === nothing + fully_covered = false + continue + end + push!(cases, InliningCase(spec_types, item)) end + + # if the signature is fully covered and there is only one applicable method, + # we can try to inline it even if the signature is not a dispatch tuple + if atype <: signature_union + if length(cases) == 0 && length(results) == 1 + (; mi) = item = InliningTodo(results[1]::InferenceResult, atypes) + state.mi_cache !== nothing && (item = resolve_todo(item, state)) + validate_sparams(mi.sparam_vals) || return true + item === nothing && return true + push!(cases, InliningCase(mi.specTypes, item)) + fully_covered = true + end + else + fully_covered = false + end + + # If we only have one case and that case is fully covered, we may either + # be able to do the inlining now (for constant cases), or push it directly + # onto the todo list + if fully_covered && length(cases) == 1 + handle_single_case!(ir, stmt, idx, cases[1].item, isinvoke, todo) + elseif length(cases) > 0 + isinvoke && rewrite_invoke_exprargs!(stmt) + push!(todo, idx=>UnionSplit(fully_covered, atype, cases)) + end + return true +end + +function handle_const_opaque_closure_call!( + ir::IRCode, idx::Int, stmt::Expr, (; results)::ConstCallInfo, + (; atypes)::Signature, state::InliningState, todo::Vector{Pair{Int, Any}}) + @assert length(results) == 1 + result = results[1]::InferenceResult + item = InliningTodo(result, atypes) + isdispatchtuple(item.mi.specTypes) || return + validate_sparams(item.mi.sparam_vals) || return + state.mi_cache !== nothing && (item = resolve_todo(item, state)) + handle_single_case!(ir, stmt, idx, item, false, todo) + return nothing end function assemble_inline_todo!(ir::IRCode, state::InliningState) @@ -1321,11 +1359,11 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState) sig === nothing && continue stmt = ir.stmts[idx][:inst] - calltype = ir.stmts[idx][:type] info = ir.stmts[idx][:info] # Check whether this call was @pure and evaluates to a constant if info isa MethodResultPure + calltype = ir.stmts[idx][:type] if calltype isa Const && is_inlineable_constant(calltype.val) ir.stmts[idx][:inst] = quoted(calltype.val) continue @@ -1339,20 +1377,25 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState) continue end - # If inference arrived at this result by using constant propagation, - # it'll have performed a specialized analysis for just this case. Use its - # result. + # if inference arrived here with constant-prop'ed result(s), + # we can perform a specialized analysis for just this case if isa(info, ConstCallInfo) - if maybe_handle_const_call!(ir, idx, stmt, info, sig, calltype, state, sig.f === Core.invoke, todo) + if isa(info.call, OpaqueClosureCallInfo) + handle_const_opaque_closure_call!( + ir, idx, stmt, info, + sig, state, todo) continue else - info = info.call + maybe_handle_const_call!( + ir, idx, stmt, info, sig, + state, sig.f === Core.invoke, todo) && continue end + info = info.call # cascade to the non-constant handling end if isa(info, OpaqueClosureCallInfo) - result = analyze_method!(info.match, sig.atypes, state, calltype) - handle_single_case!(ir, stmt, idx, result, false, todo) + item = analyze_method!(info.match, sig.atypes, state) + handle_single_case!(ir, stmt, idx, item, false, todo) continue end @@ -1373,7 +1416,7 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState) continue end - analyze_single_call!(ir, todo, idx, stmt, sig, calltype, infos, state) + analyze_single_call!(ir, todo, idx, stmt, sig, infos, state) end todo end diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index ca03710bbbd47..9358b7231c861 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -549,11 +549,9 @@ function typeof_tfunc(@nospecialize(t)) return Type{<:t} end elseif isa(t, Union) - a = widenconst(typeof_tfunc(t.a)) - b = widenconst(typeof_tfunc(t.b)) + a = widenconst(_typeof_tfunc(t.a)) + b = widenconst(_typeof_tfunc(t.b)) return Union{a, b} - elseif isa(t, TypeVar) && !(Any === t.ub) - return typeof_tfunc(t.ub) elseif isa(t, UnionAll) u = unwrap_unionall(t) if isa(u, DataType) && !isabstracttype(u) @@ -570,6 +568,13 @@ function typeof_tfunc(@nospecialize(t)) end return DataType # typeof(anything)::DataType end +# helper function of `typeof_tfunc`, which accepts `TypeVar` +function _typeof_tfunc(@nospecialize(t)) + if isa(t, TypeVar) + return t.ub !== Any ? _typeof_tfunc(t.ub) : DataType + end + return typeof_tfunc(t) +end add_tfunc(typeof, 1, 1, typeof_tfunc, 1) function typeassert_tfunc(@nospecialize(v), @nospecialize(t)) @@ -864,10 +869,7 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) elseif Symbol ⊑ name name = Int end - _ts = s.parameters[2] - while isa(_ts, TypeVar) - _ts = _ts.ub - end + _ts = unwraptv(s.parameters[2]) _ts = rewrap_unionall(_ts, s00) if !(_ts <: Tuple) return Any @@ -1266,7 +1268,7 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...) return Any end if !isempty(args) && isvarargtype(args[end]) - return isvarargtype(headtype) ? Core.TypeofVararg : Type + return isvarargtype(headtype) ? TypeofVararg : Type end largs = length(args) if headtype === Union @@ -1327,7 +1329,7 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...) canconst &= !has_free_typevars(aip1) push!(tparams, aip1) elseif isa(ai, Const) && (isa(ai.val, Type) || isa(ai.val, TypeVar) || - valid_tparam(ai.val) || (istuple && isa(ai.val, Core.TypeofVararg))) + valid_tparam(ai.val) || (istuple && isvarargtype(ai.val))) push!(tparams, ai.val) elseif isa(ai, PartialTypeVar) canconst = false @@ -1393,11 +1395,11 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...) catch ex # type instantiation might fail if one of the type parameters # doesn't match, which could happen if a type estimate is too coarse - return isvarargtype(headtype) ? Core.TypeofVararg : Type{<:headtype} + return isvarargtype(headtype) ? TypeofVararg : Type{<:headtype} end !uncertain && canconst && return Const(appl) if isvarargtype(appl) - return Core.TypeofVararg + return TypeofVararg end if istuple return Type{<:appl} @@ -1437,12 +1439,15 @@ function tuple_tfunc(atypes::Vector{Any}) if has_struct_const_info(x) anyinfo = true else - atypes[i] = x = widenconst(x) + if !isvarargtype(x) + x = widenconst(x) + end + atypes[i] = x end if isa(x, Const) params[i] = typeof(x.val) else - x = widenconst(x) + x = isvarargtype(x) ? x : widenconst(x) if isType(x) anyinfo = true xparam = x.parameters[1] @@ -1465,10 +1470,8 @@ end function arrayref_tfunc(@nospecialize(boundscheck), @nospecialize(a), @nospecialize i...) a = widenconst(a) if a <: Array - if isa(a, DataType) && (isa(a.parameters[1], Type) || isa(a.parameters[1], TypeVar)) - # TODO: the TypeVar case should not be needed here - a = a.parameters[1] - return isa(a, TypeVar) ? a.ub : a + if isa(a, DataType) && isa(a.parameters[1], Type) + return a.parameters[1] elseif isa(a, UnionAll) && !has_free_typevars(a) unw = unwrap_unionall(a) if isa(unw, DataType) @@ -1627,7 +1630,7 @@ function builtin_tfunction(interp::AbstractInterpreter, @nospecialize(f), argtyp if length(argtypes) - 1 == tf[2] argtypes = argtypes[1:end-1] else - vatype = argtypes[end]::Core.TypeofVararg + vatype = argtypes[end]::TypeofVararg argtypes = argtypes[1:end-1] while length(argtypes) < tf[1] push!(argtypes, unwrapva(vatype)) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index ef6e5a161864a..2755d82399ca7 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -958,7 +958,6 @@ function typeinf_ext_toplevel(interp::AbstractInterpreter, linfo::MethodInstance return src end - function return_type(@nospecialize(f), @nospecialize(t)) world = ccall(:jl_get_tls_world_age, UInt, ()) return ccall(:jl_call_in_typeinf_world, Any, (Ptr{Ptr{Cvoid}}, Cint), Any[_return_type, f, t, world], 4) @@ -970,14 +969,10 @@ function _return_type(interp::AbstractInterpreter, @nospecialize(f), @nospeciali rt = Union{} if isa(f, Builtin) rt = builtin_tfunction(interp, f, Any[t.parameters...], nothing) - if isa(rt, TypeVar) - rt = rt.ub - else - rt = widenconst(rt) - end + rt = widenconst(rt) else for match in _methods(f, t, -1, get_world_counter(interp))::Vector - match = match::Core.MethodMatch + match = match::MethodMatch ty = typeinf_type(interp, match.method, match.spec_types, match.sparams) ty === nothing && return Any rt = tmerge(rt, ty) diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index 2f026d41efb35..ccc113b512e56 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -293,8 +293,8 @@ widenconst(c::PartialTypeVar) = TypeVar widenconst(t::PartialStruct) = t.typ widenconst(t::PartialOpaque) = t.typ widenconst(t::Type) = t -widenconst(t::TypeVar) = t -widenconst(t::Core.TypeofVararg) = t +widenconst(t::TypeVar) = error("unhandled TypeVar") +widenconst(t::TypeofVararg) = error("unhandled Vararg") widenconst(t::LimitedAccuracy) = error("unhandled LimitedAccuracy") issubstate(a::VarState, b::VarState) = (a.typ ⊑ b.typ && a.undef <= b.undef) diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 23045c65cc6bb..139bb19084cab 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -39,6 +39,8 @@ function is_derived_type(@nospecialize(t), @nospecialize(c), mindepth::Int) if t === c return mindepth <= 1 end + isvarargtype(t) && (t = unwrapva(t)) + isvarargtype(c) && (c = unwrapva(c)) if isa(c, Union) # see if it is one of the elements of the union return is_derived_type(t, c.a, mindepth) || is_derived_type(t, c.b, mindepth) @@ -46,8 +48,6 @@ function is_derived_type(@nospecialize(t), @nospecialize(c), mindepth::Int) # see if it is derived from the body # also handle the var here, since this construct bounds the mindepth to the smallest possible value return is_derived_type(t, c.var.ub, mindepth) || is_derived_type(t, c.body, mindepth) - elseif isa(c, Core.TypeofVararg) - return is_derived_type(t, unwrapva(c), mindepth) elseif isa(c, DataType) if mindepth > 0 mindepth -= 1 @@ -79,6 +79,7 @@ end # The goal of this function is to return a type of greater "size" and less "complexity" than # both `t` or `c` over the lattice defined by `sources`, `depth`, and `allowed_tuplelen`. function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVector, depth::Int, allowed_tuplelen::Int) + @assert isa(t, Type) && isa(c, Type) "unhandled TypeVar / Vararg" if t === c return t # quick egal test elseif t === Union{} @@ -98,43 +99,29 @@ function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVec # first attempt to turn `c` into a type that contributes meaningful information # by peeling off meaningless non-matching wrappers of comparison one at a time # then unwrap `t` - if isa(c, TypeVar) - if isa(t, TypeVar) && t.ub === c.ub && (t.lb === Union{} || t.lb === c.lb) - return t # it's ok to change the name, or widen `lb` to Union{}, so we can handle this immediately here - end - return _limit_type_size(t, c.ub, sources, depth, allowed_tuplelen) - end + # NOTE that `TypeVar` / `Vararg` are handled separately to catch the logic errors if isa(c, UnionAll) - return _limit_type_size(t, c.body, sources, depth, allowed_tuplelen) + return __limit_type_size(t, c.body, sources, depth, allowed_tuplelen)::Type end if isa(t, UnionAll) - tbody = _limit_type_size(t.body, c, sources, depth, allowed_tuplelen) + tbody = __limit_type_size(t.body, c, sources, depth, allowed_tuplelen) tbody === t.body && return t - return UnionAll(t.var, tbody) - elseif isa(t, TypeVar) - # don't have a matching TypeVar in comparison, so we keep just the upper bound - return _limit_type_size(t.ub, c, sources, depth, allowed_tuplelen) + return UnionAll(t.var, tbody)::Type elseif isa(t, Union) if isa(c, Union) - a = _limit_type_size(t.a, c.a, sources, depth, allowed_tuplelen) - b = _limit_type_size(t.b, c.b, sources, depth, allowed_tuplelen) + a = __limit_type_size(t.a, c.a, sources, depth, allowed_tuplelen) + b = __limit_type_size(t.b, c.b, sources, depth, allowed_tuplelen) return Union{a, b} end - elseif isa(t, Core.TypeofVararg) - isa(c, Core.TypeofVararg) || return Vararg - VaT = _limit_type_size(unwrapva(t), unwrapva(c), sources, depth + 1, 0) - if isdefined(t, :N) && (isa(t.N, TypeVar) || (isdefined(c, :N) && t.N === c.N)) - return Vararg{VaT, t.N} - end - return Vararg{VaT} elseif isa(t, DataType) - if isa(c, Core.TypeofVararg) - # Tuple{Vararg{T}} --> Tuple{T} is OK - return _limit_type_size(t, unwrapva(c), sources, depth, 0) - elseif isType(t) # allow taking typeof as Type{...}, but ensure it doesn't start nesting + if isType(t) # see equivalent case in type_more_complex tt = unwrap_unionall(t.parameters[1]) - (!isa(tt, DataType) || isType(tt)) && (depth += 1) - is_derived_type_from_any(tt, sources, depth) && return t + if isa(tt, Union) || isa(tt, TypeVar) || isType(tt) + is_derived_type_from_any(tt, sources, depth + 1) && return t + else + isType(c) && (c = unwrap_unionall(c.parameters[1])) + type_more_complex(tt, c, sources, depth, 0, 0) || return t + end return Type elseif isa(c, DataType) tP = t.parameters @@ -161,7 +148,7 @@ function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVec else cPi = Any end - Q[i] = _limit_type_size(Q[i], cPi, sources, depth + 1, 0) + Q[i] = __limit_type_size(Q[i], cPi, sources, depth + 1, 0) end return Tuple{Q...} end @@ -182,6 +169,38 @@ function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVec return Any end +# helper function of `_limit_type_size`, which has the right to take and return `TypeVar` / `Vararg` +function __limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVector, depth::Int, allowed_tuplelen::Int) + cN = 0 + if isvarargtype(c) # Tuple{Vararg{T}} --> Tuple{T} is OK + isdefined(c, :N) && (cN = c.N) + c = unwrapva(c) + end + if isa(c, TypeVar) + if isa(t, TypeVar) && t.ub === c.ub && (t.lb === Union{} || t.lb === c.lb) + return t # it's ok to change the name, or widen `lb` to Union{}, so we can handle this immediately here + end + return __limit_type_size(t, c.ub, sources, depth, allowed_tuplelen) + elseif isa(t, TypeVar) + # don't have a matching TypeVar in comparison, so we keep just the upper bound + return __limit_type_size(t.ub, c, sources, depth, allowed_tuplelen) + elseif isvarargtype(t) + # Tuple{Vararg{T,N}} --> Tuple{Vararg{S,M}} is OK + # Tuple{T} --> Tuple{Vararg{T}} is OK + # but S must be more limited than T, and must not introduce a new number for M + VaT = __limit_type_size(unwrapva(t), c, sources, depth + 1, 0) + if isdefined(t, :N) + tN = t.N + if isa(tN, TypeVar) || tN === cN + return Vararg{VaT, tN} + end + end + return Vararg{VaT} + else + return _limit_type_size(t, c, sources, depth, allowed_tuplelen) + end +end + function type_more_complex(@nospecialize(t), @nospecialize(c), sources::SimpleVector, depth::Int, tupledepth::Int, allowed_tuplelen::Int) # detect cases where the comparison is trivial if t === c @@ -197,6 +216,8 @@ function type_more_complex(@nospecialize(t), @nospecialize(c), sources::SimpleVe return false # t isn't something new end # peel off wrappers + isvarargtype(t) && (t = unwrapva(t)) + isvarargtype(c) && (c = unwrapva(c)) if isa(c, UnionAll) # allow wrapping type with fewer UnionAlls than comparison if in a covariant context if !isa(t, UnionAll) && tupledepth == 0 @@ -225,18 +246,19 @@ function type_more_complex(@nospecialize(t), @nospecialize(c), sources::SimpleVe return t !== 1 && !(0 <= t < c) # alternatively, could use !(abs(t) <= abs(c) || abs(t) < n) for some n end # base case for data types - if isa(t, Core.TypeofVararg) - if isa(c, Core.TypeofVararg) - return type_more_complex(unwrapva(t), unwrapva(c), sources, depth + 1, tupledepth, 0) - end - elseif isa(t, DataType) + if isa(t, DataType) tP = t.parameters - if isa(c, Core.TypeofVararg) - return type_more_complex(t, unwrapva(c), sources, depth, tupledepth, 0) - elseif isType(t) # allow taking typeof any source type anywhere as Type{...}, as long as it isn't nesting Type{Type{...}} + if isType(t) + # Treat Type{T} and T as equivalent to allow taking typeof any + # source type (DataType) anywhere as Type{...}, as long as it isn't + # nesting as Type{Type{...}} tt = unwrap_unionall(t.parameters[1]) - (!isa(tt, DataType) || isType(tt)) && (depth += 1) - return !is_derived_type_from_any(tt, sources, depth) + if isa(tt, Union) || isa(tt, TypeVar) || isType(tt) + return !is_derived_type_from_any(tt, sources, depth + 1) + else + isType(c) && (c = unwrap_unionall(c.parameters[1])) + return type_more_complex(tt, c, sources, depth, 0, 0) + end elseif isa(c, DataType) && t.name === c.name cP = c.parameters length(cP) < length(tP) && return true @@ -604,10 +626,11 @@ function tmeet(@nospecialize(v), @nospecialize(t)) @assert widev <: Tuple new_fields = Vector{Any}(undef, length(v.fields)) for i = 1:length(new_fields) - if isa(v.fields[i], Core.TypeofVararg) - new_fields[i] = v.fields[i] + vfi = v.fields[i] + if isvarargtype(vfi) + new_fields[i] = vfi else - new_fields[i] = tmeet(v.fields[i], widenconst(getfield_tfunc(t, Const(i)))) + new_fields[i] = tmeet(vfi, widenconst(getfield_tfunc(t, Const(i)))) if new_fields[i] === Bottom return Bottom end diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index ae4913e4aec18..40fbb28ecb147 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -5,7 +5,7 @@ ##################### function rewrap(@nospecialize(t), @nospecialize(u)) - if isa(t, TypeVar) || isa(t, Type) || isa(t, Core.TypeofVararg) + if isa(t, TypeVar) || isa(t, Type) || isvarargtype(t) return rewrap_unionall(t, u) end return t @@ -50,7 +50,7 @@ has_concrete_subtype(d::DataType) = d.flags & 0x20 == 0x20 # certain combinations of `a` and `b` where one/both isa/are `Union`/`UnionAll` type(s)s. isnotbrokensubtype(@nospecialize(a), @nospecialize(b)) = (!iskindtype(b) || !isType(a) || hasuniquerep(a.parameters[1]) || b <: a) -argtypes_to_type(argtypes::Array{Any,1}) = Tuple{anymap(widenconst, argtypes)...} +argtypes_to_type(argtypes::Array{Any,1}) = Tuple{anymap(@nospecialize(a) -> isvarargtype(a) ? a : widenconst(a), argtypes)...} function isknownlength(t::DataType) isvatuple(t) || return true @@ -104,8 +104,8 @@ end function compatible_vatuple(a::DataType, b::DataType) vaa = a.parameters[end] vab = a.parameters[end] - if !(isa(vaa, Core.TypeofVararg) && isa(vab, Core.TypeofVararg)) - return isa(vaa, Core.TypeofVararg) == isa(vab, Core.TypeofVararg) + if !(isvarargtype(vaa) && isvarargtype(vab)) + return isvarargtype(vaa) == isvarargtype(vab) end (isdefined(vaa, :N) == isdefined(vab, :N)) || return false !isdefined(vaa, :N) && return true @@ -147,7 +147,7 @@ function typesubtract(@nospecialize(a), @nospecialize(b), MAX_UNION_SPLITTING::I ta = collect(a.parameters) ap = a.parameters[i] bp = b.parameters[i] - (isa(ap, Core.TypeofVararg) || isa(bp, Core.TypeofVararg)) && return a + (isvarargtype(ap) || isvarargtype(bp)) && return a ta[i] = typesubtract(ap, bp, min(2, MAX_UNION_SPLITTING)) return Tuple{ta...} end @@ -257,7 +257,7 @@ function unioncomplexity(t::DataType) return c end unioncomplexity(u::UnionAll) = max(unioncomplexity(u.body)::Int, unioncomplexity(u.var.ub)::Int) -unioncomplexity(t::Core.TypeofVararg) = isdefined(t, :T) ? unioncomplexity(t.T)::Int : 0 +unioncomplexity(t::TypeofVararg) = isdefined(t, :T) ? unioncomplexity(t.T)::Int : 0 unioncomplexity(@nospecialize(x)) = 0 function improvable_via_constant_propagation(@nospecialize(t)) @@ -286,3 +286,10 @@ function unswitchtupleunion(u::Union) end Tuple{Any[ Union{Any[t.parameters[i] for t in ts]...} for i in 1:n ]...} end + +function unwraptv(@nospecialize t) + while isa(t, TypeVar) + t = t.ub + end + return t +end diff --git a/base/condition.jl b/base/condition.jl index be0f618865a48..1c8b9bbbfc6f2 100644 --- a/base/condition.jl +++ b/base/condition.jl @@ -82,6 +82,17 @@ function _wait2(c::GenericCondition, waiter::Task) ct = current_task() assert_havelock(c) push!(c.waitq, waiter) + # since _wait2 is similar to schedule, we should observe the sticky bit now + if waiter.sticky && Threads.threadid(waiter) == 0 + # Issue #41324 + # t.sticky && tid == 0 is a task that needs to be co-scheduled with + # the parent task. If the parent (current_task) is not sticky we must + # set it to be sticky. + # XXX: Ideally we would be able to unset this + ct.sticky = true + tid = Threads.threadid() + ccall(:jl_set_task_tid, Cvoid, (Any, Cint), waiter, tid-1) + end return end diff --git a/base/iterators.jl b/base/iterators.jl index f9728cbbd0793..64e661bf4be0c 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -786,7 +786,7 @@ end IteratorSize(::Type{<:TakeWhile}) = SizeUnknown() eltype(::Type{TakeWhile{I,P}} where P) where {I} = eltype(I) -IteratorEltype(::Type{TakeWhile{I}} where P) where {I} = IteratorEltype(I) +IteratorEltype(::Type{TakeWhile{I, P}} where P) where {I} = IteratorEltype(I) # dropwhile diff --git a/base/multidimensional.jl b/base/multidimensional.jl index d93d2ddaae250..95f7451484f5b 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -1623,7 +1623,7 @@ _unique_dims(A::AbstractArray, dims::Colon) = invoke(unique, Tuple{Any}, A) else j_d = i_d end) begin - if (@nref $N A j) != (@nref $N A i) + if !isequal((@nref $N A j), (@nref $N A i)) collided[k] = true end end @@ -1653,7 +1653,7 @@ _unique_dims(A::AbstractArray, dims::Colon) = invoke(unique, Tuple{Any}, A) j_d = i_d end end begin - if (@nref $N A j) != (@nref $N A i) + if !isequal((@nref $N A j), (@nref $N A i)) nowcollided[k] = true end end diff --git a/base/show.jl b/base/show.jl index 1f75c5481a86a..c54c7dc479071 100644 --- a/base/show.jl +++ b/base/show.jl @@ -993,10 +993,17 @@ function show_datatype(io::IO, @nospecialize(x::DataType), wheres::Vector=TypeVa # Print homogeneous tuples with more than 3 elements compactly as NTuple{N, T} if istuple if n > 3 && all(@nospecialize(i) -> (parameters[1] === i), parameters) - print(io, "NTuple{", n, ", ", parameters[1], "}") + print(io, "NTuple{", n, ", ") + show(io, parameters[1]) + print(io, "}") else print(io, "Tuple{") - join(io, parameters, ", ") + # join(io, params, ", ") params but `show` it + first = true + for param in parameters + first ? (first = false) : print(io, ", ") + show(io, param) + end print(io, "}") end else @@ -1096,7 +1103,20 @@ function show(io::IO, m::Module) if is_root_module(m) print(io, nameof(m)) else - print(io, join(fullname(m),".")) + print_fullname(io, m) + end +end +# The call to print_fullname above was originally `print(io, join(fullname(m),"."))`, +# which allocates. The method below provides the same behavior without allocating. +# See https://github.com/JuliaLang/julia/pull/42773 for perf information. +function print_fullname(io::IO, m::Module) + mp = parentmodule(m) + if m === Main || m === Base || m === Core || mp === m + print(io, nameof(m)) + else + print_fullname(io, mp) + print(io, '.') + print(io, nameof(m)) end end diff --git a/base/special/exp.jl b/base/special/exp.jl index 230f979ec91f7..b716bd65a00e9 100644 --- a/base/special/exp.jl +++ b/base/special/exp.jl @@ -227,6 +227,8 @@ end end @inline function exp_impl_fast(x::Float64, base) T = Float64 + x >= MAX_EXP(base, T) && return Inf + x <= -SUBNORM_EXP(base, T) && return 0.0 N_float = muladd(x, LogBo256INV(base, T), MAGIC_ROUND_CONST(T)) N = reinterpret(UInt64, N_float) % Int32 N_float -= MAGIC_ROUND_CONST(T) #N_float now equals round(x*LogBo256INV(base, T)) @@ -261,6 +263,8 @@ end @inline function exp_impl_fast(x::Float32, base) T = Float32 + x >= MAX_EXP(base, T) && return Inf32 + x <= -SUBNORM_EXP(base, T) && return 0f0 N_float = round(x*LogBINV(base, T)) N = unsafe_trunc(Int32, N_float) r = muladd(N_float, LogBU(base, T), x) diff --git a/base/task.jl b/base/task.jl index 0d4e5da4ccfd4..b25197e0aadcc 100644 --- a/base/task.jl +++ b/base/task.jl @@ -306,6 +306,18 @@ function _wait2(t::Task, waiter::Task) if !istaskdone(t) push!(t.donenotify.waitq, waiter) unlock(t.donenotify) + # since _wait2 is similar to schedule, we should observe the sticky + # bit, even if we aren't calling `schedule` due to this early-return + if waiter.sticky && Threads.threadid(waiter) == 0 + # Issue #41324 + # t.sticky && tid == 0 is a task that needs to be co-scheduled with + # the parent task. If the parent (current_task) is not sticky we must + # set it to be sticky. + # XXX: Ideally we would be able to unset this + current_task().sticky = true + tid = Threads.threadid() + ccall(:jl_set_task_tid, Cvoid, (Any, Cint), waiter, tid-1) + end return nothing else unlock(t.donenotify) @@ -455,6 +467,7 @@ function errormonitor(t::Task) end nothing end + t2.sticky = false _wait2(t, t2) return t end diff --git a/contrib/new-stdlib.sh b/contrib/new-stdlib.sh index 323dc11271da6..15f82cffb1c46 100755 --- a/contrib/new-stdlib.sh +++ b/contrib/new-stdlib.sh @@ -50,14 +50,14 @@ UNAME=$(echo "$NAME" | tr [a-z] [A-Z]) sed -e "/^STDLIBS_EXT =/,/^\$/s!^\$!\\ STDLIBS_EXT += $NAME\\ -${UNAME}_GIT_URL := git://github.com/$USER/$NAME.jl.git\\ -${UNAME}_TAR_URL = https://api.github.com/repos/$USER/$NAME.jl/tarball/\$1\\ !" "$ROOT/Makefile" >"$ROOT/Makefile.tmp" mv "$ROOT/Makefile.tmp" "$ROOT/Makefile" cat >"$ROOT/$NAME.version" < [...] +# +# Build TSAN-enabled julia. Given a workspace directory , build +# TSAN-enabled julia in /tsan. Required toolss are install under +# /toolchain. Note that the same passed to `contrib/asan/build.sh` +# can be used to share the toolchain used for ASAN. This scripts also takes +# optional arguments which are passed to `make`. The default +# make target is `debug`. + +set -ue + +# `$WORKSPACE` is a directory in which we create `toolchain` and `tsan` +# sub-directories. +WORKSPACE="$1" +shift +if [ "$WORKSPACE" = "" ]; then + echo "Workspace directory must be specified as the first argument" >&2 + exit 2 +fi + +mkdir -pv "$WORKSPACE" +WORKSPACE="$(cd "$WORKSPACE" && pwd)" +if [ "$WORKSPACE" = "" ]; then + echo "Failed to create the workspace directory." >&2 + exit 2 +fi + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +JULIA_HOME="$HERE/../../" + +echo +echo "Installing toolchain..." + +TOOLCHAIN="$WORKSPACE/toolchain" +if [ ! -d "$TOOLCHAIN" ]; then + make -C "$JULIA_HOME" configure O=$TOOLCHAIN + cp "$HERE/../asan/Make.user.tools" "$TOOLCHAIN/Make.user" +fi + +make -C "$TOOLCHAIN/deps" install-clang install-llvm-tools + +# TODO: https://github.com/JuliaPackaging/Yggdrasil/issues/3359 +rm "$TOOLCHAIN/usr/tools/clang++" +ln -s "$TOOLCHAIN/usr/bin/clang" "$TOOLCHAIN/usr/tools/clang++" + +echo +echo "Building Julia..." + +BUILD="$WORKSPACE/tsan" +if [ ! -d "$BUILD" ]; then + make -C "$JULIA_HOME" configure O="$BUILD" + cp "$HERE/Make.user.tsan" "$BUILD/Make.user" +fi + +cd "$BUILD" # so that we can pass `-C src` to `make` +make "$@" diff --git a/deps/Makefile b/deps/Makefile index 27b93f444580f..06897fc316493 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -39,8 +39,9 @@ unexport CONFIG_SITE DEP_LIBS := -# Always use libblastrampoline +ifeq ($(USE_SYSTEM_LIBBLASTRAMPOLINE), 0) DEP_LIBS += blastrampoline +endif ifeq ($(USE_SYSTEM_CSL), 0) DEP_LIBS += csl @@ -158,9 +159,11 @@ DEP_LIBS += lapack endif endif +ifeq ($(USE_SYSTEM_LIBWHICH), 0) ifneq ($(OS), WINNT) DEP_LIBS += libwhich endif +endif # list all targets DEP_LIBS_STAGED_ALL := llvm llvm-tools clang llvmunwind unwind libuv pcre \ diff --git a/deps/Versions.make b/deps/Versions.make index 4bae019d0a11d..28abff6c4cc42 100644 --- a/deps/Versions.make +++ b/deps/Versions.make @@ -89,7 +89,7 @@ OPENLIBM_VER := 0.7.3 OPENLIBM_JLL_NAME := OpenLibm # Patchelf (we don't ship this or even use a JLL, we just always build it) -PATCHELF_VER := 0.9 +PATCHELF_VER := 0.13 # p7zip P7ZIP_VER := 16.2.0 @@ -106,7 +106,7 @@ LIBSUITESPARSE_JLL_NAME := SuiteSparse # unwind UNWIND_VER := 1.3.2 UNWIND_JLL_NAME := LibUnwind -UNWIND_JLL_VER := 1.3.2+4 +UNWIND_JLL_VER := 1.3.2+6 # zlib ZLIB_VER := 1.2.11 diff --git a/deps/blastrampoline.mk b/deps/blastrampoline.mk index 1ba1d1cdada8d..a29b9b19e0eaa 100644 --- a/deps/blastrampoline.mk +++ b/deps/blastrampoline.mk @@ -2,8 +2,8 @@ ifneq ($(USE_BINARYBUILDER_BLASTRAMPOLINE),1) -BLASTRAMPOLINE_GIT_URL := git://github.com/staticfloat/libblastrampoline.git -BLASTRAMPOLINE_TAR_URL = https://api.github.com/repos/staticfloat/libblastrampoline/tarball/$1 +BLASTRAMPOLINE_GIT_URL := https://github.com/JuliaLinearAlgebra/libblastrampoline.git +BLASTRAMPOLINE_TAR_URL = https://api.github.com/repos/JuliaLinearAlgebra/libblastrampoline/tarball/$1 $(eval $(call git-external,blastrampoline,BLASTRAMPOLINE,,,$(BUILDDIR))) $(BUILDDIR)/$(BLASTRAMPOLINE_SRC_DIR)/build-configured: $(BUILDDIR)/$(BLASTRAMPOLINE_SRC_DIR)/source-extracted diff --git a/deps/checksums/Downloads-26d79afcde9cf837a331fce023b31f1d3699700c.tar.gz/md5 b/deps/checksums/Downloads-26d79afcde9cf837a331fce023b31f1d3699700c.tar.gz/md5 new file mode 100644 index 0000000000000..02c7e0f4915d1 --- /dev/null +++ b/deps/checksums/Downloads-26d79afcde9cf837a331fce023b31f1d3699700c.tar.gz/md5 @@ -0,0 +1 @@ +2265854c26d1ff5a061ef9033eb3b9d4 diff --git a/deps/checksums/Downloads-26d79afcde9cf837a331fce023b31f1d3699700c.tar.gz/sha512 b/deps/checksums/Downloads-26d79afcde9cf837a331fce023b31f1d3699700c.tar.gz/sha512 new file mode 100644 index 0000000000000..19d71e70f6b23 --- /dev/null +++ b/deps/checksums/Downloads-26d79afcde9cf837a331fce023b31f1d3699700c.tar.gz/sha512 @@ -0,0 +1 @@ +7817f2ab0ccd3224420e65ae7525c2a801f0dbd7e5986e3a0eeac29e3842ea94933e494b895a78e0495ab1188a7375cd98033f507d270aea38eeeb5532226873 diff --git a/deps/checksums/Downloads-5d00bddf2db81b0ea62990b4d81fe5bf51c38b10.tar.gz/md5 b/deps/checksums/Downloads-5d00bddf2db81b0ea62990b4d81fe5bf51c38b10.tar.gz/md5 deleted file mode 100644 index 84213cc894eae..0000000000000 --- a/deps/checksums/Downloads-5d00bddf2db81b0ea62990b4d81fe5bf51c38b10.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -f0f313b7144397902487c3c10d4b06a9 diff --git a/deps/checksums/Downloads-5d00bddf2db81b0ea62990b4d81fe5bf51c38b10.tar.gz/sha512 b/deps/checksums/Downloads-5d00bddf2db81b0ea62990b4d81fe5bf51c38b10.tar.gz/sha512 deleted file mode 100644 index 2257d39dafc5e..0000000000000 --- a/deps/checksums/Downloads-5d00bddf2db81b0ea62990b4d81fe5bf51c38b10.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -28111355518dd952452dd90cc8488f46b527e9d1e0f4f3271b297c0954928dc5ae8483930af4ef464d2cc7f9c7779bfce367c5870e5f1c93544505e3ac1460d5 diff --git a/deps/checksums/Pkg-347dbfbbf18c4b5f3ac921a48663aed87b357dbf.tar.gz/md5 b/deps/checksums/Pkg-347dbfbbf18c4b5f3ac921a48663aed87b357dbf.tar.gz/md5 new file mode 100644 index 0000000000000..2c84bea469fbe --- /dev/null +++ b/deps/checksums/Pkg-347dbfbbf18c4b5f3ac921a48663aed87b357dbf.tar.gz/md5 @@ -0,0 +1 @@ +839dbce29e13d51f1d20d84ab99ef2dd diff --git a/deps/checksums/Pkg-347dbfbbf18c4b5f3ac921a48663aed87b357dbf.tar.gz/sha512 b/deps/checksums/Pkg-347dbfbbf18c4b5f3ac921a48663aed87b357dbf.tar.gz/sha512 new file mode 100644 index 0000000000000..64829520c310a --- /dev/null +++ b/deps/checksums/Pkg-347dbfbbf18c4b5f3ac921a48663aed87b357dbf.tar.gz/sha512 @@ -0,0 +1 @@ +b27620f0e284e41b599d374297f627a97acc20b01f08c10697c20c9407d273a9882a01a797f09cf45691691580670beec301eedbbe0a59d1cbd853b420f7db2a diff --git a/deps/checksums/Pkg-a230dcf5f3546bd464e575e7752d6259a3221bd5.tar.gz/md5 b/deps/checksums/Pkg-a230dcf5f3546bd464e575e7752d6259a3221bd5.tar.gz/md5 deleted file mode 100644 index 51a29ea2a88ea..0000000000000 --- a/deps/checksums/Pkg-a230dcf5f3546bd464e575e7752d6259a3221bd5.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -7ce92ba7476912068e90f37e2246af4c diff --git a/deps/checksums/Pkg-a230dcf5f3546bd464e575e7752d6259a3221bd5.tar.gz/sha512 b/deps/checksums/Pkg-a230dcf5f3546bd464e575e7752d6259a3221bd5.tar.gz/sha512 deleted file mode 100644 index 9d8ff79b1573c..0000000000000 --- a/deps/checksums/Pkg-a230dcf5f3546bd464e575e7752d6259a3221bd5.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -0f532f97b39f6a3a5f326b635378bd64b2f5e6c1b0f67d1a3a597f848cac07031172d62a567d2ecead1f9a3522431fb1254cea29a96f2da10c3f8b3b3be6c9bc diff --git a/deps/checksums/Statistics-5256d570d0a554780ed80949c79116f47eac6382.tar.gz/md5 b/deps/checksums/Statistics-5256d570d0a554780ed80949c79116f47eac6382.tar.gz/md5 new file mode 100644 index 0000000000000..cad5503d6fd27 --- /dev/null +++ b/deps/checksums/Statistics-5256d570d0a554780ed80949c79116f47eac6382.tar.gz/md5 @@ -0,0 +1 @@ +0b43a9ed3c1f081f9510dffd6697c825 diff --git a/deps/checksums/Statistics-5256d570d0a554780ed80949c79116f47eac6382.tar.gz/sha512 b/deps/checksums/Statistics-5256d570d0a554780ed80949c79116f47eac6382.tar.gz/sha512 new file mode 100644 index 0000000000000..e9aae6344832c --- /dev/null +++ b/deps/checksums/Statistics-5256d570d0a554780ed80949c79116f47eac6382.tar.gz/sha512 @@ -0,0 +1 @@ +15b2ed0b269605b1dab150fccc8e202829278bc4920293b711c611627cd4a61767373895acd1d45a0b37557e8c0cd123673d75944cd08216cffa81dc5186c0fc diff --git a/deps/checksums/libuv b/deps/checksums/libuv index f3ecef6586547..1dddd23bcd992 100644 --- a/deps/checksums/libuv +++ b/deps/checksums/libuv @@ -1,34 +1,34 @@ -LibUV.v2.0.1+3.aarch64-apple-darwin.tar.gz/md5/7c1f08965b45726099a940ab28a79207 -LibUV.v2.0.1+3.aarch64-apple-darwin.tar.gz/sha512/9f9d170fc36e2e30c53e96cdf9ae7cd4eb969905db65119627d4a9e10abb5e41aeb4ab07705d571300d44d4894c9d179c6a9450e3e6415042a6131bae0971c26 -LibUV.v2.0.1+3.aarch64-linux-gnu.tar.gz/md5/8d59850a63ea71c4007d08f7d23ceda2 -LibUV.v2.0.1+3.aarch64-linux-gnu.tar.gz/sha512/f662b36ce6e336e724c6fd03482d91a70e7ae8cfe7a1fff98cbca2cdb99b0cd3475bf32e57547097ca625c1fceb57c989871f391faea9227a6d6d4faf7649175 -LibUV.v2.0.1+3.aarch64-linux-musl.tar.gz/md5/3ad43e49a7a996b50341e7150064f13d -LibUV.v2.0.1+3.aarch64-linux-musl.tar.gz/sha512/69c6675fee6647eb7a2c8c680bd49ba31c1dcda676530af1ad1ca979c0cf8d9c908e0cb246608718d4bbca4d73174751c608aa80e3f11557ed4d05cc1d270021 -LibUV.v2.0.1+3.armv6l-linux-gnueabihf.tar.gz/md5/fec375c1c45fbd1b2eb22fbd5f727e5f -LibUV.v2.0.1+3.armv6l-linux-gnueabihf.tar.gz/sha512/1b279406cdb43bf1c3fd6b1d2d24fe9b5ca75a65212b5720d5406af26a6b7551b18efb0471c884e98e97c50693412344fd733c3ef19ea8fecf1c2c26ae888492 -LibUV.v2.0.1+3.armv6l-linux-musleabihf.tar.gz/md5/07790f330f3394d0a3ea47aa56529be1 -LibUV.v2.0.1+3.armv6l-linux-musleabihf.tar.gz/sha512/4fd4bf7c1b333907fbbbfdca213c91cb6b387e56886b6349271d7a3c1ddb3f9349f0799c60178de507e039289662c310829d81be804c5076ce9ae16eb61c8cb1 -LibUV.v2.0.1+3.armv7l-linux-gnueabihf.tar.gz/md5/2ce38a69564e279b0deb32a6af1acc52 -LibUV.v2.0.1+3.armv7l-linux-gnueabihf.tar.gz/sha512/d520ef8c556db6a42534c4abdd59f4b64ebd77cdb7b2972385d6a90463e0a490ca168bb83128e66b13555128305f85d5c979a739f7c369a11f8217e49505ce0a -LibUV.v2.0.1+3.armv7l-linux-musleabihf.tar.gz/md5/034d903cb13a45278df742df23c46275 -LibUV.v2.0.1+3.armv7l-linux-musleabihf.tar.gz/sha512/f900fd47320628ac3cf0ba0b9d9408ef4e46ed508f5efc00b94b532c5d713fcf4695827f978f605b3d9f0a1db42abcd0b1fec6a6c5bb6a502ed5f0fed55f4a32 -LibUV.v2.0.1+3.i686-linux-gnu.tar.gz/md5/60c51d15d9ae2ac8418b71730dc093d0 -LibUV.v2.0.1+3.i686-linux-gnu.tar.gz/sha512/84841f6fb4c744e335d6ce25e3059d9aeeaa54b823fe714f43fa8b11caf209e539afb9605e96992d075ae16a664da6c3a3c3d4cdb905d42c0ddc8722267c19af -LibUV.v2.0.1+3.i686-linux-musl.tar.gz/md5/d70ffffd8e57dfbcb4265ad202722fca -LibUV.v2.0.1+3.i686-linux-musl.tar.gz/sha512/c76dc0df03f7a683e08313baa0d8471b45c64099e13cf71a6595cb50c9e8ff1e96c31c400f83144ee816e5a7c8f01ad6f48d8b50a6cd161e0d246737780c2a9e -LibUV.v2.0.1+3.i686-w64-mingw32.tar.gz/md5/aa634a6e49c5683dcca287f2cf1ac71e -LibUV.v2.0.1+3.i686-w64-mingw32.tar.gz/sha512/ac1b2b53cbd7e317866259c30de349c453deda2c970587e4f4b40605f966f722fe8250e9dd7677ded0c928c9398240b7e4867805bb1404fb9d0c7dfab8493c79 -LibUV.v2.0.1+3.powerpc64le-linux-gnu.tar.gz/md5/9233d6bb298bd5020b680c25f742de98 -LibUV.v2.0.1+3.powerpc64le-linux-gnu.tar.gz/sha512/172253d1e6ce888865f5fd2182aad6426ff9988313937c7abe80ccdba859289f7ec8997574bb853f9786596d90414c4a926a475c1817c17574e0dd2ea8ad68ad -LibUV.v2.0.1+3.x86_64-apple-darwin.tar.gz/md5/09b83fe0ced427136ea5680cea64b004 -LibUV.v2.0.1+3.x86_64-apple-darwin.tar.gz/sha512/1842df6b14f23cc42c1d29546aa00b4b07390646beb312092bceb00d1de855116368ddcdd4ccd8525741fb6ecd26312c90dc9342d2e1ef4d36fbb896bdd2cbd3 -LibUV.v2.0.1+3.x86_64-linux-gnu.tar.gz/md5/d54b4fbfd998228e80c42c89df2c14a1 -LibUV.v2.0.1+3.x86_64-linux-gnu.tar.gz/sha512/290b2432e72712e73f20aa0ea6f787f9ffbf9ab5e3249c708355f7d57fb3b40ec3913e0c9a7141197caf3a741d6edbb1788cdef7e1a9a8547117dcf40559e567 -LibUV.v2.0.1+3.x86_64-linux-musl.tar.gz/md5/7690b83baecf1b614f30c7c0b661d86d -LibUV.v2.0.1+3.x86_64-linux-musl.tar.gz/sha512/53950b56f87ea0acc13790bf3a82441ca4ccfcd66cf272c81915e999487e4973c8c474caa811584d5fa8873cdd18ac0944f3d6f415c483a26e38645eb1701cdb -LibUV.v2.0.1+3.x86_64-unknown-freebsd.tar.gz/md5/6d2a330a2e87b9b8564708fba963845f -LibUV.v2.0.1+3.x86_64-unknown-freebsd.tar.gz/sha512/f15daf2f5a1bda49234fe546e13f97280c843d6939c26c9f05133302ec81e38bd47a5098d4900e39290f913835cffbdaa0b2c6406d6a86dc2eb8d0159f461e9f -LibUV.v2.0.1+3.x86_64-w64-mingw32.tar.gz/md5/4f934cf8dd1d45b206af6a72c3e679f7 -LibUV.v2.0.1+3.x86_64-w64-mingw32.tar.gz/sha512/36ef56923d7cf5c31aba87fb75774ce68976f7b6b7971a4c86011da275a5472e28033a6d97b965887b7bbe335bedfb6970618b88f20c095228ffa5f783ab8eb1 +LibUV.v2.0.1+4.aarch64-apple-darwin.tar.gz/md5/c44261bfb4a254100af5085624e9805c +LibUV.v2.0.1+4.aarch64-apple-darwin.tar.gz/sha512/f94b74fcd6a39c903f05efdd626cbe6af9016099f37cfbe0da50c0dce962a7998f884a38a586b14a9b5e7a01b96f653e5e204afbcf2c22188834394de3b3e607 +LibUV.v2.0.1+4.aarch64-linux-gnu.tar.gz/md5/5cf0c759aacd96784a81b464240901ae +LibUV.v2.0.1+4.aarch64-linux-gnu.tar.gz/sha512/b8488345516cf424bcf4b4637799cbfcf6019b109dd6104784d09381a85d4f145c02d0e0ad3a3a3679b68b7d5a5ef0a9d63cbed62734272c80e8e3927eb047f5 +LibUV.v2.0.1+4.aarch64-linux-musl.tar.gz/md5/1c3ef838685ec4b32a68ee260cd9dfba +LibUV.v2.0.1+4.aarch64-linux-musl.tar.gz/sha512/f2560edceeb680ad46a3f4146a0d22f28a6727e892520f9599f0d5a105b0d7776dadf688b48e773f7e5b2d4204d3f56bd0f8f23d09c6ac5b4d6cd85c05a20fe5 +LibUV.v2.0.1+4.armv6l-linux-gnueabihf.tar.gz/md5/bc7fa34f167fa6ed945ef2f29807e910 +LibUV.v2.0.1+4.armv6l-linux-gnueabihf.tar.gz/sha512/124646ac504e8f995bccfcac0b8ae5ef524016f1cc2f2e58e058b23624193c52ab7f554ea4ffcb3046422e638cb2422442a8fcfb9e8b828d173f1f97d5ade910 +LibUV.v2.0.1+4.armv6l-linux-musleabihf.tar.gz/md5/c123949e81d4e49c8e1a4a63327c2ccf +LibUV.v2.0.1+4.armv6l-linux-musleabihf.tar.gz/sha512/b04aa8e293abcabf125e63d11efd56215d3605e1709b2635a7325d84b5e4de7174fb69695bde3c1e042309333f7ad80f8782bc8a9576efdbfe8cac62dcbba7bc +LibUV.v2.0.1+4.armv7l-linux-gnueabihf.tar.gz/md5/eb031d1135a79615381f3010b85e4a02 +LibUV.v2.0.1+4.armv7l-linux-gnueabihf.tar.gz/sha512/13383beb19cf6fa6601d02fd7c193f27877ccc63acefd935edd2ff7c13d4b8d8b900b5571da19fe418e007e3ade4c49c1f64a971326abb50aca5dec60c10a4b6 +LibUV.v2.0.1+4.armv7l-linux-musleabihf.tar.gz/md5/09ce6bb24ca286f92675349d583c03db +LibUV.v2.0.1+4.armv7l-linux-musleabihf.tar.gz/sha512/a71f58f61e7bbd479bb66c560804b99b0e4218df0e9b4b325b254cd58d8ab8600eca35a8b9b5e54f57099834ec22e36a1a11fb923b150305c7561242b7e62030 +LibUV.v2.0.1+4.i686-linux-gnu.tar.gz/md5/1efc848d7961a677cdeb0acec37d826b +LibUV.v2.0.1+4.i686-linux-gnu.tar.gz/sha512/8855729060b7e59a5a34ff2aea209d4af84657d7b801e736fc374d49d338e1bc87796e3346eeac7340f3e8e1f8037bf420144f04d81b93d3017fb5a32eece43a +LibUV.v2.0.1+4.i686-linux-musl.tar.gz/md5/71bde27fb51e9c7ccfe1c7eab34afbb4 +LibUV.v2.0.1+4.i686-linux-musl.tar.gz/sha512/588616fd1ff342e8070def2121fa2dd6be349e9ff1d19653d2414f0c713ba02d50a89aa7cdddeb19e6864654690c870164238204767990f09b277ddf788c9935 +LibUV.v2.0.1+4.i686-w64-mingw32.tar.gz/md5/090d2e845fcef61c3ef019fdbf7877df +LibUV.v2.0.1+4.i686-w64-mingw32.tar.gz/sha512/aaea203d285ee490803852d27fc628763358680e05373208385f85ca33d14bc09baf63cf243fd45788ef68b415858e5d919178574322cfc9e4a42774227ba8ab +LibUV.v2.0.1+4.powerpc64le-linux-gnu.tar.gz/md5/b69fd18d09ab59e3b139963b3988321e +LibUV.v2.0.1+4.powerpc64le-linux-gnu.tar.gz/sha512/e257c2c86af1c7a7ab76cd08faabf7e74ef9fa462e0f8f3df346d11a249157b4b47130fad44b47317d358bf74233bb9b854c57a9b76c29c0e05f15f9322b8b53 +LibUV.v2.0.1+4.x86_64-apple-darwin.tar.gz/md5/4242ead21755564805144cf2712e3d55 +LibUV.v2.0.1+4.x86_64-apple-darwin.tar.gz/sha512/f7448587af8186c5eb59f81cca7f48f840578440762b22a7a122e8243509bb9e2c541e337c1f2d1e94599cce1a928ec6b6c14e219e412bed21d82a68416caece +LibUV.v2.0.1+4.x86_64-linux-gnu.tar.gz/md5/46155e2617a76e3910f379c33127a31b +LibUV.v2.0.1+4.x86_64-linux-gnu.tar.gz/sha512/fa94f0dea120ff5381e7803672dd588ef69990d488bc7124c662a55ab52805b874b0913fb5c2b623ccf22ff0d1065229c3a06f44669a758b8186464118902b35 +LibUV.v2.0.1+4.x86_64-linux-musl.tar.gz/md5/921637f115807c2f0b86d6a5c1949789 +LibUV.v2.0.1+4.x86_64-linux-musl.tar.gz/sha512/95ee29a34d919dae348fea2ca81d7549be8210143936987ea68f28271331983e4358aaba884edc5e1fd16eef8e9d35770f6b113d3f1db412a3a829d381b9df42 +LibUV.v2.0.1+4.x86_64-unknown-freebsd.tar.gz/md5/f4e733fa82a5a34da86a8e9d143596c1 +LibUV.v2.0.1+4.x86_64-unknown-freebsd.tar.gz/sha512/f6e390126d2e75c2dd32da40db48905c48134437e52634101d10ade67b7426101324ccf652bb4c4cc29272c5b641d5543e673bac7c6ec1c31f8d7e77f61c09c0 +LibUV.v2.0.1+4.x86_64-w64-mingw32.tar.gz/md5/1c8e63632fb40fa97805efde91de764d +LibUV.v2.0.1+4.x86_64-w64-mingw32.tar.gz/sha512/2c64ac559beccbee8ce62dc0ce0277d8eaca5e46c13585bf10d2a79811bf4f5dcac30d49b2b6a02472c72857a859be021e4e7114f30e560f97c4e3979486dc7c libuv-c6869fba163a1e04af64ede438a8fd0191e75e9e.tar.gz/md5/b60fc7b00bdfafcbbc66317858882058 libuv-c6869fba163a1e04af64ede438a8fd0191e75e9e.tar.gz/sha512/197b386af51eb4456ce65e2951e033731e1194fca8bed08755a78360ebb3431ab4d8d69a75279e7995d2e4197133d613892e5b9b5d6411bffa692df35542420f diff --git a/deps/checksums/patchelf b/deps/checksums/patchelf index d30c53e73a1ac..a7122c400749a 100644 --- a/deps/checksums/patchelf +++ b/deps/checksums/patchelf @@ -1,2 +1,2 @@ -patchelf-0.9.tar.gz/md5/3c265508526760f233620f35d79c79fc -patchelf-0.9.tar.gz/sha512/715db21156e6bd91cfa626f5201b32a6619e51532f5635ef52396da8193738ba66113485b61cc1e218b16737e66f72cc2e4bb3a7a33e73061ac2ef2c6330a299 +patchelf-0.13.tar.bz2/md5/d387eee9325414be0b1a80c8fbd2745f +patchelf-0.13.tar.bz2/sha512/43c3f99fe922e2f34d860389165bcc2b0f3f3317e124eb8443017f71b1f223d96a7c815dc81f51b14958b7dc316f75c4ab367ccc287cd99c82abe890b09a478d diff --git a/deps/checksums/unwind b/deps/checksums/unwind index dc14fbffdd765..7ed14ef8714d9 100644 --- a/deps/checksums/unwind +++ b/deps/checksums/unwind @@ -1,27 +1,27 @@ -LibUnwind.v1.3.2+4.aarch64-linux-gnu.tar.gz/md5/8f5cbf9820033211513f6d33e36194f1 -LibUnwind.v1.3.2+4.aarch64-linux-gnu.tar.gz/sha512/589886c4f141064126aecc1bf63365c610a4c3dd70e386aa8e17ce562505cac873542fa92cea023850e9bf54fcef3cf475d52f035d17d830a81c01d06d0454e4 -LibUnwind.v1.3.2+4.aarch64-linux-musl.tar.gz/md5/836a2d8ea7a11d87a74aee09f82582b5 -LibUnwind.v1.3.2+4.aarch64-linux-musl.tar.gz/sha512/4cd3805ae59854fdceee441967ba4b812246cf1a1e9ed20367f5bbbad9a47f0093731b4f78f881c696e52c101dec83498398c7b798c81c1a441232cd4ee96b58 -LibUnwind.v1.3.2+4.armv6l-linux-gnueabihf.tar.gz/md5/0047d02c4b4888050b363c77106d4ea1 -LibUnwind.v1.3.2+4.armv6l-linux-gnueabihf.tar.gz/sha512/8b02fb5189ca749e421fc17d560601e8624cbcc19a4c5c45e38828323b33db30ced8a92e08ebd429c663e52358c486d3e284e7e04898229cff2839cc01c067d5 -LibUnwind.v1.3.2+4.armv6l-linux-musleabihf.tar.gz/md5/1fe78c6f0ff7120b35c6745b16c6f838 -LibUnwind.v1.3.2+4.armv6l-linux-musleabihf.tar.gz/sha512/9576f913fbc40d00b42573f600c038fea85eb3c9b88a4878cff0e041c4096d9d005b856dbcd0d057dc40a3cdb74deeca6e9c1cc5c213e6e062328f75633ba8e3 -LibUnwind.v1.3.2+4.armv7l-linux-gnueabihf.tar.gz/md5/510db51b0364cf17207eb00e44d58974 -LibUnwind.v1.3.2+4.armv7l-linux-gnueabihf.tar.gz/sha512/76f119654a65b460917f41a321008c5a0593523db53fa12ac9aa82732368ebdee05d6366fdfdcdd300ba0fe4c7239aac25d80fb3b1ad0235f79b235dab68c796 -LibUnwind.v1.3.2+4.armv7l-linux-musleabihf.tar.gz/md5/4bb58bdc423312c74eafe52a781dd712 -LibUnwind.v1.3.2+4.armv7l-linux-musleabihf.tar.gz/sha512/02b69ec40dfcacc447169786bab3aac39c6db6b07874e9657c49a2907654be79efe16863abf09ee1e2a647cd6a651155b65bdbbd6d810a3ceaa332fc0a3ace4b -LibUnwind.v1.3.2+4.i686-linux-gnu.tar.gz/md5/76f549ae171aad91570d7874e73f44f6 -LibUnwind.v1.3.2+4.i686-linux-gnu.tar.gz/sha512/a5a654dd6233099e841d1b9c54b16cb99d736549d063e28d17d5f2014c3090d829a4a8dc4fee042d0f4a9d8a155fb30c5840cb84b9fd71758256fa072137baad -LibUnwind.v1.3.2+4.i686-linux-musl.tar.gz/md5/f8b58f061a03f24111f39f2f8cf72c61 -LibUnwind.v1.3.2+4.i686-linux-musl.tar.gz/sha512/cc6dedc551ee4d5e131cdd7ea7dd4a9cc64efe930d16cddb0c21dca7b13076b6810e00e406acb949404c80b506ca9e09d1e223069d8159e9f73fa8aa022e3f41 -LibUnwind.v1.3.2+4.powerpc64le-linux-gnu.tar.gz/md5/2fd4fda3c82c99ff102b630d078723f5 -LibUnwind.v1.3.2+4.powerpc64le-linux-gnu.tar.gz/sha512/b1c7f16d2877e08cfc9d1aa63c5c9acf30049bd11bdad90c6b1425a09f86762c76f0c1a27817ea1b939244f6e24320854552bc860c95f297a772403eeddc053d -LibUnwind.v1.3.2+4.x86_64-linux-gnu.tar.gz/md5/cd98359922fddcbbcfda56fbc011bea4 -LibUnwind.v1.3.2+4.x86_64-linux-gnu.tar.gz/sha512/7b2d78869be148db23ab8372bb6699abcf26cc58718871f050b8e67054c0c6c909f9a8c59d27c748abeef0ecb5eabc09484043c3b2232469d03c78a42a590e13 -LibUnwind.v1.3.2+4.x86_64-linux-musl.tar.gz/md5/bd8ea5006d6078a1d91743f599f37732 -LibUnwind.v1.3.2+4.x86_64-linux-musl.tar.gz/sha512/1c7feea46d70c60dbecfe6b945a29a086dc120e0d674ea9d488dc7943901711ba0505288694c94a2b0804bab6cd826b32e58912e407ed918724d16b6b6ec1d3d -LibUnwind.v1.3.2+4.x86_64-unknown-freebsd.tar.gz/md5/e72c36f0563a088282147275de90048b -LibUnwind.v1.3.2+4.x86_64-unknown-freebsd.tar.gz/sha512/3aaa7e5c21b3bcc30ff7826af4bc0b926865cac3a5b14dfa7f27f0c5d4344fa2a568a78c0c4ee32a18e668758cdac70c09f31f5ca55cc56c3d6a88654aa906fa +LibUnwind.v1.3.2+6.aarch64-linux-gnu.tar.gz/md5/dcd327c5b3d7b2ba082f2ad7f11939d3 +LibUnwind.v1.3.2+6.aarch64-linux-gnu.tar.gz/sha512/fffedf6df127538dff7cd394b4a780862fded082c7299e5ac36dc682dcd28a35db596c4621e94d9dce8483fb8053c6d0030a25b7a4bbbecc39af3efd3af14ab3 +LibUnwind.v1.3.2+6.aarch64-linux-musl.tar.gz/md5/d1582fc675158d3838f4d36214e51105 +LibUnwind.v1.3.2+6.aarch64-linux-musl.tar.gz/sha512/0636bfc0159b66d6427ae3437be3989930b6d404f94b2ffac92bd9115d87a5509a9bddc723f0c38b7c30ba2098da48c92a74b4648d6361dbebf547ccc4139813 +LibUnwind.v1.3.2+6.armv6l-linux-gnueabihf.tar.gz/md5/3e621439132504c14daae4944fb5eab8 +LibUnwind.v1.3.2+6.armv6l-linux-gnueabihf.tar.gz/sha512/d324018841343873b256df9684b51fdc9108d5b1af73f5275cd2e81d31a4c00917027afac1554b0cdc5bffd5d52a6417612d3f9399bedfb6a49100df594709f4 +LibUnwind.v1.3.2+6.armv6l-linux-musleabihf.tar.gz/md5/4603961011230cf0ef8c24881d7add7a +LibUnwind.v1.3.2+6.armv6l-linux-musleabihf.tar.gz/sha512/64e87f9074cb51442539a7907c979424682dc5a9a4cf4d538fbdc5f3bcba169c9b4d5aeb445c76060c0b0604d3fb5b0b88dbc7d3d919a4153c403d9c39110115 +LibUnwind.v1.3.2+6.armv7l-linux-gnueabihf.tar.gz/md5/2c8c2ca6b238bfdbd1c4d021df7ac7a6 +LibUnwind.v1.3.2+6.armv7l-linux-gnueabihf.tar.gz/sha512/b665d8d6b5de0b4a23f2577fe12208c4921f7ee6c3643f1434732c3c5203d1892d86f84875e3488cfc85efb250ceb0c66d02f2356e0accb3c24f200c936eeb84 +LibUnwind.v1.3.2+6.armv7l-linux-musleabihf.tar.gz/md5/0f786d4baf3d5f4fc94884ad7ae1b74b +LibUnwind.v1.3.2+6.armv7l-linux-musleabihf.tar.gz/sha512/1e4c447983d3aee05705b90962f0bbccd58f255b1d7b582069408d8927d21697fdeefea3aeb7ad84d7d087a70417164194d60c66e57bbb3a587845bbf636c06d +LibUnwind.v1.3.2+6.i686-linux-gnu.tar.gz/md5/d022d2a6a88ccf741c19889055132882 +LibUnwind.v1.3.2+6.i686-linux-gnu.tar.gz/sha512/75615cba1b4e945cd5969fd4debf7edd3913d5ae3819abf8ca724b0f1ece8f03064dcd2fde54d14d052f7e3e15bcb0efa5c7d9b55c4875e7dedee38034038b7f +LibUnwind.v1.3.2+6.i686-linux-musl.tar.gz/md5/5e57a072a0c683005921db5597f43d64 +LibUnwind.v1.3.2+6.i686-linux-musl.tar.gz/sha512/4bbaac240ef37ed48d1273cb68302e1b856622804f7704917a3db2a213118a9e0bd95ca504555a537aeec7f5baa2057cf93ca62e350a64b428a34770e64602a6 +LibUnwind.v1.3.2+6.powerpc64le-linux-gnu.tar.gz/md5/1ee397961af5567c37b5429d7b7a52cd +LibUnwind.v1.3.2+6.powerpc64le-linux-gnu.tar.gz/sha512/902017d1c64d00a67a378d0e7aef64493655a88480d27a5f720cac363bbd0aeab2f03f2b77560fae395a5799ae3da1f4122b6e8cda8d80f158c751215a1848bb +LibUnwind.v1.3.2+6.x86_64-linux-gnu.tar.gz/md5/e45a0c38c35ed4afbdcffe385998e6d3 +LibUnwind.v1.3.2+6.x86_64-linux-gnu.tar.gz/sha512/049ac0d6b74fbc6a96a7abe345b4ec783968a90bd0f3c230558ad9b3a44cbe65cf4553bfc9abdc9128529d746077308570a14f55317ffef5f65836a8413aa938 +LibUnwind.v1.3.2+6.x86_64-linux-musl.tar.gz/md5/77f053b93396484f3e4d37af9a294ad6 +LibUnwind.v1.3.2+6.x86_64-linux-musl.tar.gz/sha512/e87406503348d316940ea28f09b304c9349f3915e1ed193c87b823b7c5c7a1f6046e6b0e5eeba3b4760d5a403def5f87aa42a3f5f4d8c5f540dd4fba3743394b +LibUnwind.v1.3.2+6.x86_64-unknown-freebsd.tar.gz/md5/33c56decf549b45712642ebc73b622bd +LibUnwind.v1.3.2+6.x86_64-unknown-freebsd.tar.gz/sha512/ffb3866b2ccd3ddea168b7ce0b345d475914fa4f87a3743b92e3c07ac9453b4ad929ff01596677e00f08d4d30cf67676052cc3c4f985f722a800b82cba8334c3 LLVMLibUnwind.v11.0.1+1.aarch64-apple-darwin.tar.gz/md5/aceea9c7eca53a8da86c6d0b713a8c99 LLVMLibUnwind.v11.0.1+1.aarch64-apple-darwin.tar.gz/sha512/621b6c23b852332039bcd856ff330cc6109f5f18e646a7863900dd5ae9115a1f8a2f5da3fd50de2589da1af5e2326634259dec505972e2033f0772b7c38c5944 LLVMLibUnwind.v11.0.1+1.aarch64-linux-gnu.tar.gz/md5/53999245ae1b82eb15baa9aaad078365 @@ -54,3 +54,5 @@ LLVMLibUnwind.v11.0.1+1.x86_64-unknown-freebsd.tar.gz/md5/bed33a2f926ae888a97f87 LLVMLibUnwind.v11.0.1+1.x86_64-unknown-freebsd.tar.gz/sha512/a043e01e2e18c4c7b907cc37a7af2231ca3895edaf69ba0df62f5d615e594d2d75193c03d902beb4c39a06cbcb9f394959de527ce7144374b22371f4c47b9b43 LLVMLibUnwind.v11.0.1+1.x86_64-w64-mingw32.tar.gz/md5/c9f98a3a63137d4a5ad3c81f24d8e3cb LLVMLibUnwind.v11.0.1+1.x86_64-w64-mingw32.tar.gz/sha512/4b27d33dc31511e3a7913f360dee4e0ff7f3bf9f6256f4e71473e3b3fafbedf27fbe87d6a96ed4bba57be8c2150f9e42ee2627a48c6d3d87e466c274cd5949cb +libunwind-1.3.2.tar.gz/md5/52a8be39f0d6fd4efb7409973e425fa8 +libunwind-1.3.2.tar.gz/sha512/221864eae6bf0fde281d9551662af1e539ce919fbb7050947e60dbcc09efed4f5d34574dbce11792513e63151e0af72f02801b7bcd37a6a519e6d868abb8b509 diff --git a/deps/libgit2.mk b/deps/libgit2.mk index f9b6aba547f7e..8f6ab58bc00da 100644 --- a/deps/libgit2.mk +++ b/deps/libgit2.mk @@ -1,7 +1,7 @@ ## libgit2 ifneq ($(USE_BINARYBUILDER_LIBGIT2),1) -LIBGIT2_GIT_URL := git://github.com/libgit2/libgit2.git +LIBGIT2_GIT_URL := https://github.com/libgit2/libgit2.git LIBGIT2_TAR_URL = https://api.github.com/repos/libgit2/libgit2/tarball/$1 $(eval $(call git-external,libgit2,LIBGIT2,CMakeLists.txt,,$(SRCCACHE))) diff --git a/deps/libssh2.mk b/deps/libssh2.mk index 53e6659304770..990de300fac71 100644 --- a/deps/libssh2.mk +++ b/deps/libssh2.mk @@ -1,6 +1,6 @@ ## libssh2 ifneq ($(USE_BINARYBUILDER_LIBSSH2), 1) -LIBSSH2_GIT_URL := git://github.com/libssh2/libssh2.git +LIBSSH2_GIT_URL := https://github.com/libssh2/libssh2.git LIBSSH2_TAR_URL = https://api.github.com/repos/libssh2/libssh2/tarball/$1 $(eval $(call git-external,libssh2,LIBSSH2,CMakeLists.txt,,$(SRCCACHE))) diff --git a/deps/libuv.mk b/deps/libuv.mk index ff749fef38651..6021762ad7dc5 100644 --- a/deps/libuv.mk +++ b/deps/libuv.mk @@ -1,6 +1,6 @@ ## LIBUV ## ifneq ($(USE_BINARYBUILDER_LIBUV),1) -LIBUV_GIT_URL:=git://github.com/JuliaLang/libuv.git +LIBUV_GIT_URL:=https://github.com/JuliaLang/libuv.git LIBUV_TAR_URL=https://api.github.com/repos/JuliaLang/libuv/tarball/$1 $(eval $(call git-external,libuv,LIBUV,configure,,$(SRCCACHE))) diff --git a/deps/libwhich.mk b/deps/libwhich.mk index 5f4a50c4bf07e..7531adf12c1b5 100644 --- a/deps/libwhich.mk +++ b/deps/libwhich.mk @@ -1,5 +1,5 @@ ## LIBWHICH ## -LIBWHICH_GIT_URL := git://github.com/vtjnash/libwhich.git +LIBWHICH_GIT_URL := https://github.com/vtjnash/libwhich.git LIBWHICH_TAR_URL = https://api.github.com/repos/vtjnash/libwhich/tarball/$1 $(eval $(call git-external,libwhich,LIBWHICH,,,$(BUILDDIR))) diff --git a/deps/llvm.mk b/deps/llvm.mk index 643df8b2829bc..38e9b4831b045 100644 --- a/deps/llvm.mk +++ b/deps/llvm.mk @@ -3,7 +3,7 @@ include $(SRCDIR)/llvm-ver.make include $(SRCDIR)/llvm-options.mk ifneq ($(USE_BINARYBUILDER_LLVM), 1) -LLVM_GIT_URL:=git://github.com/JuliaLang/llvm-project.git +LLVM_GIT_URL:=https://github.com/JuliaLang/llvm-project.git LLVM_TAR_URL=https://api.github.com/repos/JuliaLang/llvm-project/tarball/$1 $(eval $(call git-external,llvm,LLVM,CMakeLists.txt,,$(SRCCACHE))) diff --git a/deps/openblas.mk b/deps/openblas.mk index 760e651ea5db4..4dca264409db3 100644 --- a/deps/openblas.mk +++ b/deps/openblas.mk @@ -1,7 +1,7 @@ ## OpenBLAS ## ifneq ($(USE_BINARYBUILDER_OPENBLAS), 1) # LAPACK is built into OpenBLAS by default -OPENBLAS_GIT_URL := git://github.com/xianyi/OpenBLAS.git +OPENBLAS_GIT_URL := https://github.com/xianyi/OpenBLAS.git OPENBLAS_TAR_URL = https://api.github.com/repos/xianyi/OpenBLAS/tarball/$1 $(eval $(call git-external,openblas,OPENBLAS,,,$(BUILDDIR))) diff --git a/deps/openlibm.mk b/deps/openlibm.mk index 6e63571b07cc3..544519e12f0d0 100644 --- a/deps/openlibm.mk +++ b/deps/openlibm.mk @@ -1,6 +1,6 @@ ## openlibm ## ifneq ($(USE_BINARYBUILDER_OPENLIBM), 1) -OPENLIBM_GIT_URL := git://github.com/JuliaMath/openlibm.git +OPENLIBM_GIT_URL := https://github.com/JuliaMath/openlibm.git OPENLIBM_TAR_URL = https://api.github.com/repos/JuliaMath/openlibm/tarball/$1 $(eval $(call git-external,openlibm,OPENLIBM,,,$(BUILDDIR))) diff --git a/deps/patchelf.mk b/deps/patchelf.mk index 1dbc07a511813..3df361c8e900d 100644 --- a/deps/patchelf.mk +++ b/deps/patchelf.mk @@ -1,15 +1,16 @@ ## patchelf ## -$(SRCCACHE)/patchelf-$(PATCHELF_VER).tar.gz: | $(SRCCACHE) - $(JLDOWNLOAD) $@ https://nixos.org/releases/patchelf/patchelf-$(PATCHELF_VER)/patchelf-$(PATCHELF_VER).tar.gz +$(SRCCACHE)/patchelf-$(PATCHELF_VER).tar.bz2: | $(SRCCACHE) + $(JLDOWNLOAD) $@ https://github.com/NixOS/patchelf/releases/download/$(PATCHELF_VER)/patchelf-$(PATCHELF_VER).tar.bz2 -$(SRCCACHE)/patchelf-$(PATCHELF_VER)/source-extracted: $(SRCCACHE)/patchelf-$(PATCHELF_VER).tar.gz +$(SRCCACHE)/patchelf-$(PATCHELF_VER)/source-extracted: $(SRCCACHE)/patchelf-$(PATCHELF_VER).tar.bz2 $(JLCHECKSUM) $< - cd $(dir $<) && $(TAR) zxf $< + mkdir $(dir $@) + cd $(dir $@) && $(TAR) jxf $< --strip-components=1 touch -c $(SRCCACHE)/patchelf-$(PATCHELF_VER)/configure # old target echo 1 > $@ -checksum-patchelf: $(SRCCACHE)/patchelf-$(PATCHELF_VER).tar.gz +checksum-patchelf: $(SRCCACHE)/patchelf-$(PATCHELF_VER).tar.bz2 $(JLCHECKSUM) $< $(BUILDDIR)/patchelf-$(PATCHELF_VER)/build-configured: $(SRCCACHE)/patchelf-$(PATCHELF_VER)/source-extracted @@ -39,12 +40,12 @@ clean-patchelf: -$(MAKE) -C $(BUILDDIR)/patchelf-$(PATCHELF_VER) clean distclean-patchelf: - -rm -rf $(SRCCACHE)/patchelf-$(PATCHELF_VER).tar.gz \ + -rm -rf $(SRCCACHE)/patchelf-$(PATCHELF_VER).tar.bz2 \ $(SRCCACHE)/patchelf-$(PATCHELF_VER) \ $(BUILDDIR)/patchelf-$(PATCHELF_VER) -get-patchelf: $(SRCCACHE)/patchelf-$(PATCHELF_VER).tar.gz +get-patchelf: $(SRCCACHE)/patchelf-$(PATCHELF_VER).tar.bz2 extract-patchelf: $(SRCCACHE)/patchelf-$(PATCHELF_VER)/source-extracted configure-patchelf: $(BUILDDIR)/patchelf-$(PATCHELF_VER)/build-configured compile-patchelf: $(BUILDDIR)/patchelf-$(PATCHELF_VER)/build-compiled diff --git a/deps/patches/libunwind-dwarf-table.patch b/deps/patches/libunwind-dwarf-table.patch new file mode 100644 index 0000000000000..5905982f9a349 --- /dev/null +++ b/deps/patches/libunwind-dwarf-table.patch @@ -0,0 +1,36 @@ +From a5b5fd28ed03cb1ab524d24dc534c1fa167bf5a1 Mon Sep 17 00:00:00 2001 +From: Alex Arslan +Date: Fri, 5 Nov 2021 16:58:41 -0700 +Subject: [PATCH] Fix table indexing in `dwarf_search_unwind_table` + +`table_len` is used as an index into `table`, assuming it represents the +number of entries. However, it is defined as the number of entries +multiplied by `sizeof(unw_word_t)`. This is accounted for in other +places that use `table_len`, e.g. in `lookup`, which divides out the +size of `unw_word_t`, but the indexing expression uses `table_len` +directly. So when `table` has say 2 entries, we're actually looking at +index 15 rather than 1 in the comparison. This can cause the conditional +to erroneously evaluate to true, allowing the following line to +segfault. + +This was observed with JIT compiled code from Julia with LLVM on +FreeBSD. + +Co-Authored-By: Jameson Nash +--- + src/dwarf/Gfind_proc_info-lsb.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/dwarf/Gfind_proc_info-lsb.c b/src/dwarf/Gfind_proc_info-lsb.c +index 5e27a501..af4cbce8 100644 +--- a/src/dwarf/Gfind_proc_info-lsb.c ++++ b/src/dwarf/Gfind_proc_info-lsb.c +@@ -866,7 +866,7 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, + if (as == unw_local_addr_space) + { + e = lookup (table, table_len, ip - ip_base); +- if (e && &e[1] < &table[table_len]) ++ if (e && &e[1] < &table[table_len / sizeof (unw_word_t)]) + last_ip = e[1].start_ip_offset + ip_base; + else + last_ip = di->end_ip; diff --git a/deps/unwind.mk b/deps/unwind.mk index 5db0ee2f99b85..ed60e306bff48 100644 --- a/deps/unwind.mk +++ b/deps/unwind.mk @@ -28,7 +28,11 @@ $(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-cfa-rsp.patch-applied: $(SRCCACHE) cd $(SRCCACHE)/libunwind-$(UNWIND_VER) && patch -p1 -f -u < $(SRCDIR)/patches/libunwind-cfa-rsp.patch echo 1 > $@ -$(BUILDDIR)/libunwind-$(UNWIND_VER)/build-configured: $(SRCCACHE)/libunwind-$(UNWIND_VER)/source-extracted $(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-cfa-rsp.patch-applied +$(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-dwarf-table.patch-applied: $(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-cfa-rsp.patch-applied + cd $(SRCCACHE)/libunwind-$(UNWIND_VER) && patch -p1 -f -u -l < $(SRCDIR)/patches/libunwind-dwarf-table.patch + echo 1 > $@ + +$(BUILDDIR)/libunwind-$(UNWIND_VER)/build-configured: $(SRCCACHE)/libunwind-$(UNWIND_VER)/source-extracted $(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-dwarf-table.patch-applied mkdir -p $(dir $@) cd $(dir $@) && \ $(dir $<)/configure $(CONFIGURE_COMMON) CPPFLAGS="$(CPPFLAGS) $(LIBUNWIND_CPPFLAGS)" CFLAGS="$(CFLAGS) $(LIBUNWIND_CFLAGS)" --enable-shared --disable-minidebuginfo --disable-tests diff --git a/deps/utf8proc.mk b/deps/utf8proc.mk index b368e1c12b0da..52775a2dff5e6 100644 --- a/deps/utf8proc.mk +++ b/deps/utf8proc.mk @@ -1,5 +1,5 @@ ## UTF8PROC ## -UTF8PROC_GIT_URL := git://github.com/JuliaLang/utf8proc.git +UTF8PROC_GIT_URL := https://github.com/JuliaLang/utf8proc.git UTF8PROC_TAR_URL = https://api.github.com/repos/JuliaLang/utf8proc/tarball/$1 $(eval $(call git-external,utf8proc,UTF8PROC,,,$(BUILDDIR))) diff --git a/deps/zlib.mk b/deps/zlib.mk index abc626fd2f79f..b31ab425ccfc3 100644 --- a/deps/zlib.mk +++ b/deps/zlib.mk @@ -1,6 +1,6 @@ ## Zlib ## ifneq ($(USE_BINARYBUILDER_ZLIB), 1) -ZLIB_GIT_URL := git://github.com/madler/zlib.git +ZLIB_GIT_URL := https://github.com/madler/zlib.git ZLIB_TAR_URL = https://api.github.com/repos/madler/zlib/tarball/$1 $(eval $(call git-external,zlib,ZLIB,,,$(SRCCACHE))) diff --git a/src/Makefile b/src/Makefile index 412967003ed5a..479d1652f6808 100644 --- a/src/Makefile +++ b/src/Makefile @@ -87,8 +87,8 @@ SRCS += $(RUNTIME_SRCS) # headers are used for dependency tracking, while public headers will be part of the dist UV_HEADERS := -HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,julia.h julia_assert.h julia_threads.h julia_fasttls.h locks.h atomics.h julia_internal.h options.h timing.h) -PUBLIC_HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,julia.h julia_assert.h julia_threads.h julia_fasttls.h locks.h atomics.h julia_gcext.h jloptions.h) +HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,julia.h julia_assert.h julia_threads.h julia_fasttls.h julia_locks.h julia_atomics.h julia_internal.h options.h timing.h) $(addprefix $(BUILDDIR)/, $(DTRACE_HEADERS)) +PUBLIC_HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,julia.h julia_assert.h julia_threads.h julia_fasttls.h julia_locks.h julia_atomics.h julia_gcext.h jloptions.h) ifeq ($(USE_SYSTEM_LIBUV),0) UV_HEADERS += uv.h UV_HEADERS += uv/*.h @@ -358,15 +358,23 @@ endif clangsa: $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) -clang-sa-%: $(SRCDIR)/%.c $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) | analyzegc-deps-check - @$(call PRINT_ANALYZE, $(build_depsbindir)/clang --analyze -Xanalyzer -analyzer-werror -Xanalyzer -analyzer-output=text -Xclang -load -Xclang $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) $(CLANGSA_FLAGS) $(JCPPFLAGS) $(JCFLAGS) $(DEBUGFLAGS) -Xclang -analyzer-checker=core$(COMMA)julia.GCChecker --analyzer-no-default-checks -fcolor-diagnostics -Werror -x c $<) -clang-sa-%: $(SRCDIR)/%.cpp $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) | analyzegc-deps-check - @$(call PRINT_ANALYZE, $(build_depsbindir)/clang --analyze -Xanalyzer -analyzer-werror -Xanalyzer -analyzer-output=text -Xclang -load -Xclang $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) $(CLANGSA_FLAGS) $(CLANGSA_CXXFLAGS) $(LLVM_CXXFLAGS) $(JCPPFLAGS) $(JCXXFLAGS) $(DEBUGFLAGS) -Xclang -analyzer-checker=core$(COMMA)julia.GCChecker --analyzer-no-default-checks -fcolor-diagnostics -Werror -x c++ $<) +clang-sagc-%: $(SRCDIR)/%.c $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) .FORCE | analyzegc-deps-check + @$(call PRINT_ANALYZE, $(build_depsbindir)/clang -D__clang_gcanalyzer__ --analyze -Xanalyzer -analyzer-werror -Xanalyzer -analyzer-output=text -Xclang -load -Xclang $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) $(CLANGSA_FLAGS) $(JCPPFLAGS) $(JCFLAGS) $(DEBUGFLAGS) -Xclang -analyzer-checker=core$(COMMA)julia.GCChecker --analyzer-no-default-checks -fcolor-diagnostics -Werror -x c $<) +clang-sagc-%: $(SRCDIR)/%.cpp $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) .FORCE | analyzegc-deps-check + @$(call PRINT_ANALYZE, $(build_depsbindir)/clang -D__clang_gcanalyzer__ --analyze -Xanalyzer -analyzer-werror -Xanalyzer -analyzer-output=text -Xclang -load -Xclang $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) $(CLANGSA_FLAGS) $(CLANGSA_CXXFLAGS) $(LLVM_CXXFLAGS) $(JCPPFLAGS) $(JCXXFLAGS) $(DEBUGFLAGS) -Xclang -analyzer-checker=core$(COMMA)julia.GCChecker --analyzer-no-default-checks -fcolor-diagnostics -Werror -x c++ $<) -# Add C files as a target of `analyzegc` -analyzegc: $(addprefix clang-sa-,$(RUNTIME_SRCS)) +clang-sa-%: $(SRCDIR)/%.c .FORCE | analyzegc-deps-check + @$(call PRINT_ANALYZE, $(build_depsbindir)/clang --analyze -Xanalyzer -analyzer-werror -Xanalyzer -analyzer-output=text $(CLANGSA_FLAGS) $(JCPPFLAGS) $(JCFLAGS) $(DEBUGFLAGS) --analyzer-no-default-checks -fcolor-diagnostics -Werror -x c $<) +clang-sa-%: $(SRCDIR)/%.cpp .FORCE | analyzegc-deps-check + @$(call PRINT_ANALYZE, $(build_depsbindir)/clang --analyze -Xanalyzer -analyzer-werror -Xanalyzer -analyzer-output=text $(CLANGSA_FLAGS) $(CLANGSA_CXXFLAGS) $(LLVM_CXXFLAGS) $(JCPPFLAGS) $(JCXXFLAGS) $(DEBUGFLAGS) --analyzer-no-default-checks -fcolor-diagnostics -Werror -x c++ $<) + + +# Add C files as a target of `analyzesrc` and `analyzegc` +analyzesrc: $(addprefix clang-sa-,$(SRCS)) +analyzegc: analyzesrc $(addprefix clang-sagc-,$(RUNTIME_SRCS)) clean-analyzegc: rm -f $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) -.PHONY: default all debug release clean cleanall clean-* libccalltest libllvmcalltest julia_flisp.boot.inc.phony analyzegc clang-sa-* +.FORCE: +.PHONY: default all debug release clean cleanall clean-* libccalltest libllvmcalltest julia_flisp.boot.inc.phony analyzegc analyzesrc .FORCE diff --git a/src/array.c b/src/array.c index bc86eb50b95c5..43aeb3f805492 100644 --- a/src/array.c +++ b/src/array.c @@ -26,7 +26,7 @@ static inline void arrayassign_safe(int hasptr, jl_value_t *parent, char *dst, c assert(nb >= jl_datatype_size(jl_typeof(src))); // nb might move some undefined bits, but we should be okay with that if (hasptr) { size_t nptr = nb / sizeof(void*); - memmove_refs((void**)dst, (void**)src, nptr); + memmove_refs((void**)dst, (void* const*)src, nptr); jl_gc_multi_wb(parent, src); } else { @@ -581,7 +581,7 @@ JL_DLLEXPORT jl_value_t *jl_ptrarrayref(jl_array_t *a JL_PROPAGATES_ROOT, size_t { assert(i < jl_array_len(a)); assert(a->flags.ptrarray); - jl_value_t *elt = jl_atomic_load_relaxed(((jl_value_t**)a->data) + i); + jl_value_t *elt = jl_atomic_load_relaxed(((_Atomic(jl_value_t*)*)a->data) + i); if (elt == NULL) jl_throw(jl_undefref_exception); return elt; @@ -610,7 +610,7 @@ JL_DLLEXPORT jl_value_t *jl_arrayref(jl_array_t *a, size_t i) JL_DLLEXPORT int jl_array_isassigned(jl_array_t *a, size_t i) { if (a->flags.ptrarray) { - return jl_atomic_load_relaxed(((jl_value_t**)jl_array_data(a)) + i) != NULL; + return jl_atomic_load_relaxed(((_Atomic(jl_value_t*)*)jl_array_data(a)) + i) != NULL; } else if (a->flags.hasptr) { jl_datatype_t *eltype = (jl_datatype_t*)jl_tparam0(jl_typeof(a)); @@ -649,7 +649,7 @@ JL_DLLEXPORT void jl_arrayset(jl_array_t *a JL_ROOTING_ARGUMENT, jl_value_t *rhs arrayassign_safe(hasptr, jl_array_owner(a), &((char*)a->data)[i * a->elsize], rhs, a->elsize); } else { - jl_atomic_store_relaxed(((jl_value_t**)a->data) + i, rhs); + jl_atomic_store_relaxed(((_Atomic(jl_value_t*)*)a->data) + i, rhs); jl_gc_wb(jl_array_owner(a), rhs); } } @@ -659,7 +659,7 @@ JL_DLLEXPORT void jl_arrayunset(jl_array_t *a, size_t i) if (i >= jl_array_len(a)) jl_bounds_error_int((jl_value_t*)a, i + 1); if (a->flags.ptrarray) - jl_atomic_store_relaxed(((jl_value_t**)a->data) + i, NULL); + jl_atomic_store_relaxed(((_Atomic(jl_value_t*)*)a->data) + i, NULL); else if (a->flags.hasptr) { size_t elsize = a->elsize; jl_assume(elsize >= sizeof(void*) && elsize % sizeof(void*) == 0); @@ -1236,9 +1236,11 @@ static NOINLINE ssize_t jl_array_ptr_copy_forward(jl_value_t *owner, void **src_p, void **dest_p, ssize_t n) JL_NOTSAFEPOINT { + _Atomic(void*) *src_pa = (_Atomic(void*)*)src_p; + _Atomic(void*) *dest_pa = (_Atomic(void*)*)dest_p; for (ssize_t i = 0; i < n; i++) { - void *val = jl_atomic_load_relaxed(src_p + i); - jl_atomic_store_relaxed(dest_p + i, val); + void *val = jl_atomic_load_relaxed(src_pa + i); + jl_atomic_store_relaxed(dest_pa + i, val); // `val` is young or old-unmarked if (val && !(jl_astaggedvalue(val)->bits.gc & GC_MARKED)) { jl_gc_queue_root(owner); @@ -1252,9 +1254,11 @@ static NOINLINE ssize_t jl_array_ptr_copy_backward(jl_value_t *owner, void **src_p, void **dest_p, ssize_t n) JL_NOTSAFEPOINT { + _Atomic(void*) *src_pa = (_Atomic(void*)*)src_p; + _Atomic(void*) *dest_pa = (_Atomic(void*)*)dest_p; for (ssize_t i = 0; i < n; i++) { - void *val = jl_atomic_load_relaxed(src_p + n - i - 1); - jl_atomic_store_relaxed(dest_p + n - i - 1, val); + void *val = jl_atomic_load_relaxed(src_pa + n - i - 1); + jl_atomic_store_relaxed(dest_pa + n - i - 1, val); // `val` is young or old-unmarked if (val && !(jl_astaggedvalue(val)->bits.gc & GC_MARKED)) { jl_gc_queue_root(owner); diff --git a/src/ast.c b/src/ast.c index de2492db08c94..5312bf74756ba 100644 --- a/src/ast.c +++ b/src/ast.c @@ -125,7 +125,7 @@ typedef struct _jl_ast_context_t { static jl_ast_context_t jl_ast_main_ctx; -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ jl_ast_context_t *jl_ast_ctx(fl_context_t *fl) JL_GLOBALLY_ROOTED JL_NOTSAFEPOINT; #else #define jl_ast_ctx(fl_ctx) container_of(fl_ctx, jl_ast_context_t, fl) diff --git a/src/atomics.h b/src/atomics.h deleted file mode 100644 index 4a33368745aa1..0000000000000 --- a/src/atomics.h +++ /dev/null @@ -1,163 +0,0 @@ -// This file is a part of Julia. License is MIT: https://julialang.org/license - -#ifndef JL_ATOMICS_H -#define JL_ATOMICS_H - -// Low-level atomic operations - -#if defined(__i386__) && defined(__GNUC__) && !defined(__SSE2__) -# error Julia can only be built for architectures above Pentium 4. Pass -march=pentium4, or set MARCH=pentium4 and ensure that -march is not passed separately with an older architecture. -#endif -#ifdef _COMPILER_MICROSOFT_ -# include -# include -#endif -#if defined(_CPU_X86_64_) || defined(_CPU_X86_) -# include -#endif -#ifndef _OS_WINDOWS_ -# include -#endif -#include - -enum jl_memory_order { - jl_memory_order_unspecified = -2, - jl_memory_order_invalid = -1, - jl_memory_order_notatomic = 0, - jl_memory_order_unordered, - jl_memory_order_monotonic, - jl_memory_order_consume, - jl_memory_order_acquire, - jl_memory_order_release, - jl_memory_order_acq_rel, - jl_memory_order_seq_cst -}; - -/** - * Thread synchronization primitives: - * - * These roughly follows the c11/c++11 memory model and the act as memory - * barriers at both the compiler level and the hardware level. - * The only exception is the GC safepoint and GC state transitions for which - * we use only a compiler (signal) barrier and use the signal handler to do the - * synchronization in order to lower the mutator overhead as much as possible. - * - * We use the compiler intrinsics to implement a similar API to the c11/c++11 - * one instead of using it directly because, we need interoperability between - * code written in different languages. The current c++ standard (c++14) does - * not allow using c11 atomic functions or types and there's currently no - * guarantee that the two types are compatible (although most of them probably - * are). We also need to access these atomic variables from the LLVM JIT code - * which is very hard unless the layout of the object is fully specified. - */ -#define jl_fence() __atomic_thread_fence(__ATOMIC_SEQ_CST) -#define jl_fence_release() __atomic_thread_fence(__ATOMIC_RELEASE) -#define jl_signal_fence() __atomic_signal_fence(__ATOMIC_SEQ_CST) - - -# define jl_atomic_fetch_add_relaxed(obj, arg) \ - __atomic_fetch_add(obj, arg, __ATOMIC_RELAXED) -# define jl_atomic_fetch_add(obj, arg) \ - __atomic_fetch_add(obj, arg, __ATOMIC_SEQ_CST) -# define jl_atomic_add_fetch(obj, arg) \ - __atomic_add_fetch(obj, arg, __ATOMIC_SEQ_CST) -# define jl_atomic_fetch_and_relaxed(obj, arg) \ - __atomic_fetch_and(obj, arg, __ATOMIC_RELAXED) -# define jl_atomic_fetch_and(obj, arg) \ - __atomic_fetch_and(obj, arg, __ATOMIC_SEQ_CST) -# define jl_atomic_fetch_or_relaxed(obj, arg) \ - __atomic_fetch_or(obj, arg, __ATOMIC_RELAXED) -# define jl_atomic_fetch_or(obj, arg) \ - __atomic_fetch_or(obj, arg, __ATOMIC_SEQ_CST) -# define jl_atomic_cmpswap(obj, expected, desired) \ - __atomic_compare_exchange_n(obj, expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) -# define jl_atomic_cmpswap_relaxed(obj, expected, desired) \ - __atomic_compare_exchange_n(obj, expected, desired, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED) -// TODO: Maybe add jl_atomic_cmpswap_weak for spin lock -# define jl_atomic_exchange(obj, desired) \ - __atomic_exchange_n(obj, desired, __ATOMIC_SEQ_CST) -# define jl_atomic_exchange_relaxed(obj, desired) \ - __atomic_exchange_n(obj, desired, __ATOMIC_RELAXED) -# define jl_atomic_store(obj, val) \ - __atomic_store_n(obj, val, __ATOMIC_SEQ_CST) -# define jl_atomic_store_relaxed(obj, val) \ - __atomic_store_n(obj, val, __ATOMIC_RELAXED) - -# if defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) || \ - !(defined(_CPU_X86_) || defined(_CPU_X86_64_)) -// ICC and Clang doesn't have this bug... -# define jl_atomic_store_release(obj, val) \ - __atomic_store_n(obj, val, __ATOMIC_RELEASE) -# else -// Workaround a GCC bug when using store with release order by using the -// stronger version instead. -// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67458 -// fixed in https://gcc.gnu.org/git/?p=gcc.git&a=commit;h=d8c40eff56f69877b33c697ded756d50fde90c27 -# define jl_atomic_store_release(obj, val) do { \ - jl_signal_fence(); \ - __atomic_store_n(obj, val, __ATOMIC_RELEASE); \ - } while (0) -# endif -# define jl_atomic_load(obj) \ - __atomic_load_n(obj, __ATOMIC_SEQ_CST) -# define jl_atomic_load_acquire(obj) \ - __atomic_load_n(obj, __ATOMIC_ACQUIRE) -#ifdef JL_TSAN_ENABLED -// For the sake of tsan, call these loads consume ordering since they will act -// as such on the processors we support while normally, the compiler would -// upgrade this to acquire ordering, which is strong (and slower) than we want. -# define jl_atomic_load_relaxed(obj) \ - __atomic_load_n(obj, __ATOMIC_CONSUME) -#else -# define jl_atomic_load_relaxed(obj) \ - __atomic_load_n(obj, __ATOMIC_RELAXED) -#endif - -#ifdef __clang_analyzer__ -// for the purposes of the analyzer, we can turn these into non-atomic expressions with similar properties -// (for the sake of the analyzer, we don't care if it is an exact match for behavior) - -#undef jl_atomic_exchange -#undef jl_atomic_exchange_relaxed -#define jl_atomic_exchange(obj, desired) \ - (__extension__({ \ - __typeof__((obj)) p__analyzer__ = (obj); \ - __typeof__(*p__analyzer__) temp__analyzer__ = *p__analyzer__; \ - *p__analyzer__ = (desired); \ - temp__analyzer__; \ - })) -#define jl_atomic_exchange_relaxed jl_atomic_exchange - -#undef jl_atomic_cmpswap -#undef jl_atomic_cmpswap_relaxed -#define jl_atomic_cmpswap(obj, expected, desired) \ - (__extension__({ \ - __typeof__((obj)) p__analyzer__ = (obj); \ - __typeof__(*p__analyzer__) temp__analyzer__ = *p__analyzer__; \ - __typeof__((expected)) x__analyzer__ = (expected); \ - if (temp__analyzer__ == *x__analyzer__) \ - *p__analyzer__ = (desired); \ - else \ - *x__analyzer__ = temp__analyzer__; \ - temp__analyzer__ == *x__analyzer__; \ - })) -#define jl_atomic_cmpswap_relaxed jl_atomic_cmpswap - -#undef jl_atomic_store -#undef jl_atomic_store_release -#undef jl_atomic_store_relaxed -#define jl_atomic_store(obj, val) (*(obj) = (val)) -#define jl_atomic_store_release jl_atomic_store -#define jl_atomic_store_relaxed jl_atomic_store - -#undef jl_atomic_load -#undef jl_atomic_load_acquire -#undef jl_atomic_load_relaxed -#define jl_atomic_load(obj) (*(obj)) -#define jl_atomic_load_acquire jl_atomic_load -#define jl_atomic_load_relaxed jl_atomic_load - -#endif - - -#endif // JL_ATOMICS_H diff --git a/src/builtins.c b/src/builtins.c index b186aadbed5d2..01723bf81733e 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -706,7 +706,7 @@ static jl_value_t *do_apply( jl_value_t **args, uint32_t nargs, jl_value_t *iter } if (arg_heap) { // optimization: keep only the first root, free the others -#ifndef __clang_analyzer__ +#ifndef __clang_gcanalyzer__ ((void**)roots)[-2] = (void*)JL_GC_ENCODE_PUSHARGS(1); #endif } diff --git a/src/cgmemmgr.cpp b/src/cgmemmgr.cpp index 23d8b7437b823..344f044737f9d 100644 --- a/src/cgmemmgr.cpp +++ b/src/cgmemmgr.cpp @@ -205,7 +205,7 @@ static intptr_t get_anon_hdl(void) return -1; } -static size_t map_offset = 0; +static _Atomic(size_t) map_offset{0}; // Multiple of 128MB. // Hopefully no one will set a ulimit for this to be a problem... static constexpr size_t map_size_inc_default = 128 * 1024 * 1024; @@ -239,7 +239,7 @@ static intptr_t init_shared_map() anon_hdl = get_anon_hdl(); if (anon_hdl == -1) return -1; - map_offset = 0; + jl_atomic_store_relaxed(&map_offset, 0); map_size = get_map_size_inc(); int ret = ftruncate(anon_hdl, map_size); if (ret != 0) { diff --git a/src/codegen.cpp b/src/codegen.cpp index a5eda27b95424..616da07c13939 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3655,12 +3655,14 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const else { jl_value_t *ci = ctx.params->lookup(mi, ctx.world, ctx.world); // TODO: need to use the right pair world here jl_code_instance_t *codeinst = (jl_code_instance_t*)ci; - if (ci != jl_nothing && codeinst->invoke != jl_fptr_sparam) { // check if we know we definitely can't handle this specptr - if (codeinst->invoke == jl_fptr_const_return) { + if (ci != jl_nothing) { + auto invoke = jl_atomic_load_relaxed(&codeinst->invoke); + // check if we know how to handle this specptr + if (invoke == jl_fptr_const_return) { result = mark_julia_const(codeinst->rettype_const); handled = true; } - else { + else if (invoke != jl_fptr_sparam) { bool specsig, needsparams; std::tie(specsig, needsparams) = uses_specsig(mi, codeinst->rettype, ctx.params->prefer_specsig); std::string name; @@ -3669,9 +3671,11 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const if (ctx.use_cache) { // optimization: emit the correct name immediately, if we know it // TODO: use `emitted` map here too to try to consolidate names? - if (codeinst->specptr.fptr) { - if (specsig ? codeinst->isspecsig : codeinst->invoke == jl_fptr_args) { - protoname = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)codeinst->specptr.fptr, codeinst); + auto invoke = jl_atomic_load_relaxed(&codeinst->invoke); + auto fptr = jl_atomic_load_relaxed(&codeinst->specptr.fptr); + if (fptr) { + if (specsig ? codeinst->isspecsig : invoke == jl_fptr_args) { + protoname = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)fptr, codeinst); need_to_emit = false; } } @@ -5040,8 +5044,9 @@ static Function *emit_tojlinvoke(jl_code_instance_t *codeinst, Module *M, jl_cod ctx.builder.SetInsertPoint(b0); Function *theFunc; Value *theFarg; - if (params.cache && codeinst->invoke != NULL) { - StringRef theFptrName = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)codeinst->invoke, codeinst); + auto invoke = jl_atomic_load_relaxed(&codeinst->invoke); + if (params.cache && invoke != NULL) { + StringRef theFptrName = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)invoke, codeinst); theFunc = cast( M->getOrInsertFunction(theFptrName, jlinvoke_func->_type(jl_LLVMContext)).getCallee()); theFarg = literal_pointer_val(ctx, (jl_value_t*)codeinst); @@ -5726,7 +5731,7 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con // some sanity checking and check whether there's a vararg size_t nargt = jl_svec_len(argt); bool isVa = (nargt > 0 && jl_is_vararg(jl_svecref(argt, nargt - 1))); - assert(!isVa); + assert(!isVa); (void)isVa; jl_array_t *closure_types = NULL; jl_value_t *sigt = NULL; // dispatch-sig = type signature with Ref{} annotations removed and applied to the env @@ -7803,12 +7808,14 @@ void jl_compile_workqueue( "invalid world for code-instance"); StringRef preal_decl = ""; bool preal_specsig = false; - if (params.cache && codeinst->invoke != NULL) { - if (codeinst->invoke == jl_fptr_args) { - preal_decl = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)codeinst->specptr.fptr, codeinst); + auto invoke = jl_atomic_load_relaxed(&codeinst->invoke); + if (params.cache && invoke != NULL) { + auto fptr = jl_atomic_load_relaxed(&codeinst->specptr.fptr); + if (invoke == jl_fptr_args) { + preal_decl = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)fptr, codeinst); } else if (codeinst->isspecsig) { - preal_decl = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)codeinst->specptr.fptr, codeinst); + preal_decl = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)fptr, codeinst); preal_specsig = true; } } diff --git a/src/datatype.c b/src/datatype.c index cf4c0cd94b5f2..31b6bc93cd014 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -48,9 +48,9 @@ JL_DLLEXPORT jl_methtable_t *jl_new_method_table(jl_sym_t *name, jl_module_t *mo jl_methtable_type); mt->name = jl_demangle_typename(name); mt->module = module; - mt->defs = jl_nothing; - mt->leafcache = (jl_array_t*)jl_an_empty_vec_any; - mt->cache = jl_nothing; + jl_atomic_store_relaxed(&mt->defs, jl_nothing); + jl_atomic_store_relaxed(&mt->leafcache, (jl_array_t*)jl_an_empty_vec_any); + jl_atomic_store_relaxed(&mt->cache, jl_nothing); mt->max_args = 0; mt->kwsorter = NULL; mt->backedges = NULL; @@ -69,8 +69,8 @@ JL_DLLEXPORT jl_typename_t *jl_new_typename_in(jl_sym_t *name, jl_module_t *modu tn->name = name; tn->module = module; tn->wrapper = NULL; - tn->cache = jl_emptysvec; - tn->linearcache = jl_emptysvec; + jl_atomic_store_relaxed(&tn->cache, jl_emptysvec); + jl_atomic_store_relaxed(&tn->linearcache, jl_emptysvec); tn->names = NULL; tn->hash = bitmix(bitmix(module ? module->build_id : 0, name->hash), 0xa1ada1da); tn->abstract = abstract; @@ -727,23 +727,23 @@ JL_DLLEXPORT int jl_is_foreign_type(jl_datatype_t *dt) #if MAX_ATOMIC_SIZE > MAX_POINTERATOMIC_SIZE #error MAX_ATOMIC_SIZE too large #endif +#if MAX_ATOMIC_SIZE >= 16 && !defined(_P64) +#error 12 byte GC pool size alignment unimplemented for 32-bit +#endif #if MAX_POINTERATOMIC_SIZE > 16 #error MAX_POINTERATOMIC_SIZE too large #endif -#if MAX_POINTERATOMIC_SIZE >= 16 -#ifndef _P64 -#error 12 byte GC pool size not implemented for 32-bit -#endif -typedef __uint128_t uint128_t; -typedef uint128_t jl_uatomicmax_t; -#else -typedef uint64_t jl_uatomicmax_t; -#endif - #if BYTE_ORDER != LITTLE_ENDIAN #error using masks for atomics (instead of memcpy like nb == 16) assumes little endian #endif +#if MAX_POINTERATOMIC_SIZE >= 16 +typedef struct _jl_uint128_t { + uint64_t a; + uint64_t b; +} jl_uint128_t; +#endif + static inline uint32_t zext_read32(const jl_value_t *x, size_t nb) JL_NOTSAFEPOINT { uint32_t y = *(uint32_t*)x; @@ -769,11 +769,11 @@ static inline uint64_t zext_read64(const jl_value_t *x, size_t nb) JL_NOTSAFEPOI #endif #if MAX_POINTERATOMIC_SIZE >= 16 -static inline uint128_t zext_read128(const jl_value_t *x, size_t nb) JL_NOTSAFEPOINT +static inline jl_uint128_t zext_read128(const jl_value_t *x, size_t nb) JL_NOTSAFEPOINT { - uint128_t y = 0; + jl_uint128_t y = {0}; if (nb == 16) - y = *(uint128_t*)x; + y = *(jl_uint128_t*)x; else memcpy(&y, x, nb); return y; @@ -814,34 +814,34 @@ JL_DLLEXPORT jl_value_t *jl_atomic_new_bits(jl_value_t *dt, const char *data) size_t nb = jl_datatype_size(bt); // some types have special pools to minimize allocations if (nb == 0) return jl_new_struct_uninit(bt); // returns bt->instance - if (bt == jl_bool_type) return (1 & jl_atomic_load((int8_t*)data)) ? jl_true : jl_false; - if (bt == jl_uint8_type) return jl_box_uint8(jl_atomic_load((uint8_t*)data)); - if (bt == jl_int64_type) return jl_box_int64(jl_atomic_load((int64_t*)data)); - if (bt == jl_int32_type) return jl_box_int32(jl_atomic_load((int32_t*)data)); - if (bt == jl_int8_type) return jl_box_int8(jl_atomic_load((int8_t*)data)); - if (bt == jl_int16_type) return jl_box_int16(jl_atomic_load((int16_t*)data)); - if (bt == jl_uint64_type) return jl_box_uint64(jl_atomic_load((uint64_t*)data)); - if (bt == jl_uint32_type) return jl_box_uint32(jl_atomic_load((uint32_t*)data)); - if (bt == jl_uint16_type) return jl_box_uint16(jl_atomic_load((uint16_t*)data)); - if (bt == jl_char_type) return jl_box_char(jl_atomic_load((uint32_t*)data)); + if (bt == jl_bool_type) return (1 & jl_atomic_load((_Atomic(int8_t)*)data)) ? jl_true : jl_false; + if (bt == jl_uint8_type) return jl_box_uint8(jl_atomic_load((_Atomic(uint8_t)*)data)); + if (bt == jl_int64_type) return jl_box_int64(jl_atomic_load((_Atomic(int64_t)*)data)); + if (bt == jl_int32_type) return jl_box_int32(jl_atomic_load((_Atomic(int32_t)*)data)); + if (bt == jl_int8_type) return jl_box_int8(jl_atomic_load((_Atomic(int8_t)*)data)); + if (bt == jl_int16_type) return jl_box_int16(jl_atomic_load((_Atomic(int16_t)*)data)); + if (bt == jl_uint64_type) return jl_box_uint64(jl_atomic_load((_Atomic(uint64_t)*)data)); + if (bt == jl_uint32_type) return jl_box_uint32(jl_atomic_load((_Atomic(uint32_t)*)data)); + if (bt == jl_uint16_type) return jl_box_uint16(jl_atomic_load((_Atomic(uint16_t)*)data)); + if (bt == jl_char_type) return jl_box_char(jl_atomic_load((_Atomic(uint32_t)*)data)); jl_task_t *ct = jl_current_task; jl_value_t *v = jl_gc_alloc(ct->ptls, nb, bt); // data is aligned to the power of two, // we will write too much of v, but the padding should exist if (nb == 1) - *(uint8_t*) v = jl_atomic_load((uint8_t*)data); + *(uint8_t*) v = jl_atomic_load((_Atomic(uint8_t)*)data); else if (nb <= 2) - *(uint16_t*)v = jl_atomic_load((uint16_t*)data); + *(uint16_t*)v = jl_atomic_load((_Atomic(uint16_t)*)data); else if (nb <= 4) - *(uint32_t*)v = jl_atomic_load((uint32_t*)data); + *(uint32_t*)v = jl_atomic_load((_Atomic(uint32_t)*)data); #if MAX_POINTERATOMIC_SIZE >= 8 else if (nb <= 8) - *(uint64_t*)v = jl_atomic_load((uint64_t*)data); + *(uint64_t*)v = jl_atomic_load((_Atomic(uint64_t)*)data); #endif #if MAX_POINTERATOMIC_SIZE >= 16 else if (nb <= 16) - *(uint128_t*)v = jl_atomic_load((uint128_t*)data); + *(jl_uint128_t*)v = jl_atomic_load((_Atomic(jl_uint128_t)*)data); #endif else abort(); @@ -857,18 +857,18 @@ JL_DLLEXPORT void jl_atomic_store_bits(char *dst, const jl_value_t *src, int nb) if (nb == 0) ; else if (nb == 1) - jl_atomic_store((uint8_t*)dst, *(uint8_t*)src); + jl_atomic_store((_Atomic(uint8_t)*)dst, *(uint8_t*)src); else if (nb == 2) - jl_atomic_store((uint16_t*)dst, *(uint16_t*)src); + jl_atomic_store((_Atomic(uint16_t)*)dst, *(uint16_t*)src); else if (nb <= 4) - jl_atomic_store((uint32_t*)dst, zext_read32(src, nb)); + jl_atomic_store((_Atomic(uint32_t)*)dst, zext_read32(src, nb)); #if MAX_POINTERATOMIC_SIZE >= 8 else if (nb <= 8) - jl_atomic_store((uint64_t*)dst, zext_read64(src, nb)); + jl_atomic_store((_Atomic(uint64_t)*)dst, zext_read64(src, nb)); #endif #if MAX_POINTERATOMIC_SIZE >= 16 else if (nb <= 16) - jl_atomic_store((uint128_t*)dst, zext_read128(src, nb)); + jl_atomic_store((_Atomic(jl_uint128_t)*)dst, zext_read128(src, nb)); #endif else abort(); @@ -881,32 +881,32 @@ JL_DLLEXPORT jl_value_t *jl_atomic_swap_bits(jl_value_t *dt, char *dst, const jl jl_datatype_t *bt = (jl_datatype_t*)dt; // some types have special pools to minimize allocations if (nb == 0) return jl_new_struct_uninit(bt); // returns bt->instance - if (bt == jl_bool_type) return (1 & jl_atomic_exchange((int8_t*)dst, 1 & *(int8_t*)src)) ? jl_true : jl_false; - if (bt == jl_uint8_type) return jl_box_uint8(jl_atomic_exchange((uint8_t*)dst, *(int8_t*)src)); - if (bt == jl_int64_type) return jl_box_int64(jl_atomic_exchange((int64_t*)dst, *(int64_t*)src)); - if (bt == jl_int32_type) return jl_box_int32(jl_atomic_exchange((int32_t*)dst, *(int32_t*)src)); - if (bt == jl_int8_type) return jl_box_int8(jl_atomic_exchange((int8_t*)dst, *(int8_t*)src)); - if (bt == jl_int16_type) return jl_box_int16(jl_atomic_exchange((int16_t*)dst, *(int16_t*)src)); - if (bt == jl_uint64_type) return jl_box_uint64(jl_atomic_exchange((uint64_t*)dst, *(uint64_t*)src)); - if (bt == jl_uint32_type) return jl_box_uint32(jl_atomic_exchange((uint32_t*)dst, *(uint32_t*)src)); - if (bt == jl_uint16_type) return jl_box_uint16(jl_atomic_exchange((uint16_t*)dst, *(uint16_t*)src)); - if (bt == jl_char_type) return jl_box_char(jl_atomic_exchange((uint32_t*)dst, *(uint32_t*)src)); + if (bt == jl_bool_type) return (1 & jl_atomic_exchange((_Atomic(int8_t)*)dst, 1 & *(int8_t*)src)) ? jl_true : jl_false; + if (bt == jl_uint8_type) return jl_box_uint8(jl_atomic_exchange((_Atomic(uint8_t)*)dst, *(int8_t*)src)); + if (bt == jl_int64_type) return jl_box_int64(jl_atomic_exchange((_Atomic(int64_t)*)dst, *(int64_t*)src)); + if (bt == jl_int32_type) return jl_box_int32(jl_atomic_exchange((_Atomic(int32_t)*)dst, *(int32_t*)src)); + if (bt == jl_int8_type) return jl_box_int8(jl_atomic_exchange((_Atomic(int8_t)*)dst, *(int8_t*)src)); + if (bt == jl_int16_type) return jl_box_int16(jl_atomic_exchange((_Atomic(int16_t)*)dst, *(int16_t*)src)); + if (bt == jl_uint64_type) return jl_box_uint64(jl_atomic_exchange((_Atomic(uint64_t)*)dst, *(uint64_t*)src)); + if (bt == jl_uint32_type) return jl_box_uint32(jl_atomic_exchange((_Atomic(uint32_t)*)dst, *(uint32_t*)src)); + if (bt == jl_uint16_type) return jl_box_uint16(jl_atomic_exchange((_Atomic(uint16_t)*)dst, *(uint16_t*)src)); + if (bt == jl_char_type) return jl_box_char(jl_atomic_exchange((_Atomic(uint32_t)*)dst, *(uint32_t*)src)); jl_task_t *ct = jl_current_task; jl_value_t *v = jl_gc_alloc(ct->ptls, jl_datatype_size(bt), bt); if (nb == 1) - *(uint8_t*)v = jl_atomic_exchange((uint8_t*)dst, *(uint8_t*)src); + *(uint8_t*)v = jl_atomic_exchange((_Atomic(uint8_t)*)dst, *(uint8_t*)src); else if (nb == 2) - *(uint16_t*)v = jl_atomic_exchange((uint16_t*)dst, *(uint16_t*)src); + *(uint16_t*)v = jl_atomic_exchange((_Atomic(uint16_t)*)dst, *(uint16_t*)src); else if (nb <= 4) - *(uint32_t*)v = jl_atomic_exchange((uint32_t*)dst, zext_read32(src, nb)); + *(uint32_t*)v = jl_atomic_exchange((_Atomic(uint32_t)*)dst, zext_read32(src, nb)); #if MAX_POINTERATOMIC_SIZE >= 8 else if (nb <= 8) - *(uint64_t*)v = jl_atomic_exchange((uint64_t*)dst, zext_read64(src, nb)); + *(uint64_t*)v = jl_atomic_exchange((_Atomic(uint64_t)*)dst, zext_read64(src, nb)); #endif #if MAX_POINTERATOMIC_SIZE >= 16 else if (nb <= 16) - *(uint128_t*)v = jl_atomic_exchange((uint128_t*)dst, zext_read128(src, nb)); + *(jl_uint128_t*)v = jl_atomic_exchange((_Atomic(jl_uint128_t)*)dst, zext_read128(src, nb)); #endif else abort(); @@ -923,29 +923,29 @@ JL_DLLEXPORT int jl_atomic_bool_cmpswap_bits(char *dst, const jl_value_t *expect } else if (nb == 1) { uint8_t y = *(uint8_t*)expected; - success = jl_atomic_cmpswap((uint8_t*)dst, &y, *(uint8_t*)src); + success = jl_atomic_cmpswap((_Atomic(uint8_t)*)dst, &y, *(uint8_t*)src); } else if (nb == 2) { uint16_t y = *(uint16_t*)expected; - success = jl_atomic_cmpswap((uint16_t*)dst, &y, *(uint16_t*)src); + success = jl_atomic_cmpswap((_Atomic(uint16_t)*)dst, &y, *(uint16_t*)src); } else if (nb <= 4) { uint32_t y = zext_read32(expected, nb); uint32_t z = zext_read32(src, nb); - success = jl_atomic_cmpswap((uint32_t*)dst, &y, z); + success = jl_atomic_cmpswap((_Atomic(uint32_t)*)dst, &y, z); } #if MAX_POINTERATOMIC_SIZE >= 8 else if (nb <= 8) { uint64_t y = zext_read64(expected, nb); uint64_t z = zext_read64(src, nb); - success = jl_atomic_cmpswap((uint64_t*)dst, &y, z); + success = jl_atomic_cmpswap((_Atomic(uint64_t)*)dst, &y, z); } #endif #if MAX_POINTERATOMIC_SIZE >= 16 else if (nb <= 16) { - uint128_t y = zext_read128(expected, nb); - uint128_t z = zext_read128(src, nb); - success = jl_atomic_cmpswap((uint128_t*)dst, &y, z); + jl_uint128_t y = zext_read128(expected, nb); + jl_uint128_t z = zext_read128(src, nb); + success = jl_atomic_cmpswap((_Atomic(jl_uint128_t)*)dst, &y, z); } #endif else { @@ -972,10 +972,10 @@ JL_DLLEXPORT jl_value_t *jl_atomic_cmpswap_bits(jl_datatype_t *dt, jl_datatype_t if (dt == et) { *y8 = *(uint8_t*)expected; uint8_t z8 = *(uint8_t*)src; - success = jl_atomic_cmpswap((uint8_t*)dst, y8, z8); + success = jl_atomic_cmpswap((_Atomic(uint8_t)*)dst, y8, z8); } else { - *y8 = jl_atomic_load((uint8_t*)dst); + *y8 = jl_atomic_load((_Atomic(uint8_t)*)dst); success = 0; } } @@ -985,10 +985,10 @@ JL_DLLEXPORT jl_value_t *jl_atomic_cmpswap_bits(jl_datatype_t *dt, jl_datatype_t if (dt == et) { *y16 = *(uint16_t*)expected; uint16_t z16 = *(uint16_t*)src; - success = jl_atomic_cmpswap((uint16_t*)dst, y16, z16); + success = jl_atomic_cmpswap((_Atomic(uint16_t)*)dst, y16, z16); } else { - *y16 = jl_atomic_load((uint16_t*)dst); + *y16 = jl_atomic_load((_Atomic(uint16_t)*)dst); success = 0; } } @@ -998,13 +998,13 @@ JL_DLLEXPORT jl_value_t *jl_atomic_cmpswap_bits(jl_datatype_t *dt, jl_datatype_t *y32 = zext_read32(expected, nb); uint32_t z32 = zext_read32(src, nb); while (1) { - success = jl_atomic_cmpswap((uint32_t*)dst, y32, z32); + success = jl_atomic_cmpswap((_Atomic(uint32_t)*)dst, y32, z32); if (success || !dt->layout->haspadding || !jl_egal__bits(y, expected, dt)) break; } } else { - *y32 = jl_atomic_load((uint32_t*)dst); + *y32 = jl_atomic_load((_Atomic(uint32_t)*)dst); success = 0; } } @@ -1015,31 +1015,31 @@ JL_DLLEXPORT jl_value_t *jl_atomic_cmpswap_bits(jl_datatype_t *dt, jl_datatype_t *y64 = zext_read64(expected, nb); uint64_t z64 = zext_read64(src, nb); while (1) { - success = jl_atomic_cmpswap((uint64_t*)dst, y64, z64); + success = jl_atomic_cmpswap((_Atomic(uint64_t)*)dst, y64, z64); if (success || !dt->layout->haspadding || !jl_egal__bits(y, expected, dt)) break; } } else { - *y64 = jl_atomic_load((uint64_t*)dst); + *y64 = jl_atomic_load((_Atomic(uint64_t)*)dst); success = 0; } } #endif #if MAX_POINTERATOMIC_SIZE >= 16 else if (nb <= 16) { - uint128_t *y128 = (uint128_t*)y; + jl_uint128_t *y128 = (jl_uint128_t*)y; if (dt == et) { *y128 = zext_read128(expected, nb); - uint128_t z128 = zext_read128(src, nb); + jl_uint128_t z128 = zext_read128(src, nb); while (1) { - success = jl_atomic_cmpswap((uint128_t*)dst, y128, z128); + success = jl_atomic_cmpswap((_Atomic(jl_uint128_t)*)dst, y128, z128); if (success || !dt->layout->haspadding || !jl_egal__bits(y, expected, dt)) break; } } else { - *y128 = jl_atomic_load((uint128_t*)dst); + *y128 = jl_atomic_load((_Atomic(jl_uint128_t)*)dst); success = 0; } } @@ -1394,7 +1394,7 @@ JL_DLLEXPORT jl_value_t *jl_get_nth_field(jl_value_t *v, size_t i) jl_bounds_error_int(v, i + 1); size_t offs = jl_field_offset(st, i); if (jl_field_isptr(st, i)) { - return jl_atomic_load_relaxed((jl_value_t**)((char*)v + offs)); + return jl_atomic_load_relaxed((_Atomic(jl_value_t*)*)((char*)v + offs)); } jl_value_t *ty = jl_field_type_concrete(st, i); int isatomic = jl_field_isatomic(st, i); @@ -1431,7 +1431,7 @@ JL_DLLEXPORT jl_value_t *jl_get_nth_field_noalloc(jl_value_t *v JL_PROPAGATES_RO assert(i < jl_datatype_nfields(st)); size_t offs = jl_field_offset(st,i); assert(jl_field_isptr(st,i)); - return jl_atomic_load_relaxed((jl_value_t**)((char*)v + offs)); + return jl_atomic_load_relaxed((_Atomic(jl_value_t*)*)((char*)v + offs)); } JL_DLLEXPORT jl_value_t *jl_get_nth_field_checked(jl_value_t *v, size_t i) @@ -1472,7 +1472,7 @@ void set_nth_field(jl_datatype_t *st, jl_value_t *v, size_t i, jl_value_t *rhs, return; } if (jl_field_isptr(st, i)) { - jl_atomic_store_relaxed((jl_value_t**)((char*)v + offs), rhs); + jl_atomic_store_relaxed((_Atomic(jl_value_t*)*)((char*)v + offs), rhs); jl_gc_wb(v, rhs); } else { @@ -1522,9 +1522,9 @@ jl_value_t *swap_nth_field(jl_datatype_t *st, jl_value_t *v, size_t i, jl_value_ jl_value_t *r; if (jl_field_isptr(st, i)) { if (isatomic) - r = jl_atomic_exchange((jl_value_t**)((char*)v + offs), rhs); + r = jl_atomic_exchange((_Atomic(jl_value_t*)*)((char*)v + offs), rhs); else - r = jl_atomic_exchange_relaxed((jl_value_t**)((char*)v + offs), rhs); + r = jl_atomic_exchange_relaxed((_Atomic(jl_value_t*)*)((char*)v + offs), rhs); jl_gc_wb(v, rhs); } else { @@ -1594,7 +1594,7 @@ jl_value_t *modify_nth_field(jl_datatype_t *st, jl_value_t *v, size_t i, jl_valu if (!jl_isa(y, ty)) jl_type_error("modifyfield!", ty, y); if (jl_field_isptr(st, i)) { - jl_value_t **p = (jl_value_t**)((char*)v + offs); + _Atomic(jl_value_t*) *p = (_Atomic(jl_value_t*)*)((char*)v + offs); if (isatomic ? jl_atomic_cmpswap(p, &r, y) : jl_atomic_cmpswap_relaxed(p, &r, y)) break; } @@ -1673,7 +1673,7 @@ jl_value_t *replace_nth_field(jl_datatype_t *st, jl_value_t *v, size_t i, jl_val jl_datatype_t *rettyp = jl_apply_cmpswap_type(ty); JL_GC_PROMISE_ROOTED(rettyp); // (JL_ALWAYS_LEAFTYPE) if (jl_field_isptr(st, i)) { - jl_value_t **p = (jl_value_t**)((char*)v + offs); + _Atomic(jl_value_t*) *p = (_Atomic(jl_value_t*)*)((char*)v + offs); int success; while (1) { success = isatomic ? jl_atomic_cmpswap(p, &r, rhs) : jl_atomic_cmpswap_relaxed(p, &r, rhs); @@ -1759,7 +1759,7 @@ JL_DLLEXPORT int jl_field_isdefined(jl_value_t *v, size_t i) JL_NOTSAFEPOINT { jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v); size_t offs = jl_field_offset(st, i); - jl_value_t **fld = (jl_value_t**)((char*)v + offs); + _Atomic(jl_value_t*) *fld = (_Atomic(jl_value_t*)*)((char*)v + offs); if (!jl_field_isptr(st, i)) { jl_datatype_t *ft = (jl_datatype_t*)jl_field_type_concrete(st, i); if (!jl_is_datatype(ft) || ft->layout->first_ptr < 0) diff --git a/src/dlload.c b/src/dlload.c index df03d9d8e900f..920bd3d00d43b 100644 --- a/src/dlload.c +++ b/src/dlload.c @@ -166,6 +166,7 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, uv_stat_t stbuf; void *handle; int abspath; + int is_atpath; // number of extensions to try — if modname already ends with the // standard extension, then we don't try adding additional extensions int n_extensions = endswith_extension(modname) ? 1 : N_EXTENSIONS; @@ -191,16 +192,30 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, } abspath = isabspath(modname); + is_atpath = 0; + + // Detect if our `modname` is something like `@rpath/libfoo.dylib` +#ifdef _OS_DARWIN_ + size_t nameLen = strlen(modname); + const char *const atPaths[] = {"@executable_path/", "@loader_path/", "@rpath/"}; + for (i = 0; i < sizeof(atPaths)/sizeof(char*); ++i) { + size_t atLen = strlen(atPaths[i]); + if (nameLen >= atLen && 0 == strncmp(modname, atPaths[i], atLen)) { + is_atpath = 1; + } + } +#endif /* this branch permutes all base paths in DL_LOAD_PATH with all extensions note: skip when !jl_base_module to avoid UndefVarError(:DL_LOAD_PATH), and also skip for absolute paths + and also skip for `@`-paths on macOS We also do simple string replacement here for elements starting with `@executable_path/`. While these exist as OS concepts on Darwin, we want to use them on other platforms such as Windows, so we emulate them here. */ - if (!abspath && jl_base_module != NULL) { + if (!abspath && !is_atpath && jl_base_module != NULL) { jl_binding_t *b = jl_get_module_binding(jl_base_module, jl_symbol("DL_LOAD_PATH")); jl_array_t *DL_LOAD_PATH = (jl_array_t*)(b ? b->value : NULL); if (DL_LOAD_PATH != NULL) { @@ -309,7 +324,7 @@ JL_DLLEXPORT int jl_dlsym(void *handle, const char *symbol, void ** value, int t char err[256]; win32_formatmessage(GetLastError(), err, sizeof(err)); #endif -#ifndef __clang_analyzer__ +#ifndef __clang_gcanalyzer__ // Hide the error throwing from the analyser since there isn't a way to express // "safepoint only when throwing error" currently. jl_errorf("could not load symbol \"%s\":\n%s", symbol, err); diff --git a/src/dump.c b/src/dump.c index f7a0ced4a6ab6..c88889027720c 100644 --- a/src/dump.c +++ b/src/dump.c @@ -349,13 +349,13 @@ static void jl_serialize_module(jl_serializer_state *s, jl_module_t *m) jl_serialize_value(s, (jl_value_t*)table[i]); jl_binding_t *b = (jl_binding_t*)table[i+1]; jl_serialize_value(s, b->name); - jl_value_t *e = b->value; + jl_value_t *e = jl_atomic_load_relaxed(&b->value); if (!b->constp && e && jl_is_cpointer(e) && jl_unbox_voidpointer(e) != (void*)-1 && jl_unbox_voidpointer(e) != NULL) // reset Ptr fields to C_NULL (but keep MAP_FAILED / INVALID_HANDLE) jl_serialize_cnull(s, jl_typeof(e)); else jl_serialize_value(s, e); - jl_serialize_value(s, b->globalref); + jl_serialize_value(s, jl_atomic_load_relaxed(&b->globalref)); jl_serialize_value(s, b->owner); write_int8(s->s, (b->deprecated<<3) | (b->constp<<2) | (b->exportp<<1) | (b->imported)); } @@ -659,7 +659,7 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li if (!(serialization_mode & METHOD_INTERNAL)) return; jl_serialize_value(s, m->specializations); - jl_serialize_value(s, m->speckeyset); + jl_serialize_value(s, jl_atomic_load_relaxed(&m->speckeyset)); jl_serialize_value(s, (jl_value_t*)m->name); jl_serialize_value(s, (jl_value_t*)m->file); write_int32(s->s, m->line); @@ -1509,8 +1509,9 @@ static jl_value_t *jl_deserialize_value_method(jl_serializer_state *s, jl_value_ } m->specializations = (jl_svec_t*)jl_deserialize_value(s, (jl_value_t**)&m->specializations); jl_gc_wb(m, m->specializations); - m->speckeyset = (jl_array_t*)jl_deserialize_value(s, (jl_value_t**)&m->speckeyset); - jl_gc_wb(m, m->speckeyset); + jl_array_t *speckeyset = (jl_array_t*)jl_deserialize_value(s, (jl_value_t**)&m->speckeyset); + jl_atomic_store_relaxed(&m->speckeyset, speckeyset); + jl_gc_wb(m, speckeyset); m->name = (jl_sym_t*)jl_deserialize_value(s, NULL); jl_gc_wb(m, m->name); m->file = (jl_sym_t*)jl_deserialize_value(s, NULL); @@ -1647,10 +1648,12 @@ static jl_value_t *jl_deserialize_value_module(jl_serializer_state *s) JL_GC_DIS break; jl_binding_t *b = jl_get_binding_wr(m, asname, 1); b->name = (jl_sym_t*)jl_deserialize_value(s, (jl_value_t**)&b->name); - b->value = jl_deserialize_value(s, &b->value); - if (b->value != NULL) jl_gc_wb(m, b->value); - b->globalref = jl_deserialize_value(s, &b->globalref); - if (b->globalref != NULL) jl_gc_wb(m, b->globalref); + jl_value_t *bvalue = jl_deserialize_value(s, (jl_value_t**)&b->value); + *(jl_value_t**)&b->value = bvalue; + if (bvalue != NULL) jl_gc_wb(m, bvalue); + jl_value_t *bglobalref = jl_deserialize_value(s, (jl_value_t**)&b->globalref); + *(jl_value_t**)&b->globalref = bglobalref; + if (bglobalref != NULL) jl_gc_wb(m, bglobalref); b->owner = (jl_module_t*)jl_deserialize_value(s, (jl_value_t**)&b->owner); if (b->owner != NULL) jl_gc_wb(m, b->owner); int8_t flags = read_int8(s->s); diff --git a/src/gc-stacks.c b/src/gc-stacks.c index 3708531e9b405..a621bd96ab3d4 100644 --- a/src/gc-stacks.c +++ b/src/gc-stacks.c @@ -23,7 +23,7 @@ #define MIN_STACK_MAPPINGS_PER_POOL 5 const size_t jl_guard_size = (4096 * 8); -static uint32_t num_stack_mappings = 0; +static _Atomic(uint32_t) num_stack_mappings = 0; #ifdef _OS_WINDOWS_ #define MAP_FAILED NULL diff --git a/src/gc.c b/src/gc.c index 8b9688833c5dd..1ba8f65c09efd 100644 --- a/src/gc.c +++ b/src/gc.c @@ -132,7 +132,7 @@ static jl_mutex_t gc_cache_lock; // Flag that tells us whether we need to support conservative marking // of objects. -static int support_conservative_marking = 0; +static _Atomic(int) support_conservative_marking = 0; /** * Note about GC synchronization: @@ -166,7 +166,7 @@ static int support_conservative_marking = 0; * finalizers in unmanaged (GC safe) mode. */ -jl_gc_num_t gc_num = {0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +jl_gc_num_t gc_num = {0}; static size_t last_long_collect_interval; pagetable_t memory_map; @@ -181,7 +181,7 @@ bigval_t *big_objects_marked = NULL; // `to_finalize` should not have tagged pointers. arraylist_t finalizer_list_marked; arraylist_t to_finalize; -int jl_gc_have_pending_finalizers = 0; +JL_DLLEXPORT _Atomic(int) jl_gc_have_pending_finalizers = 0; NOINLINE uintptr_t gc_get_stack_ptr(void) { @@ -298,7 +298,7 @@ static void finalize_object(arraylist_t *list, jl_value_t *o, // This way, the mutation should not conflict with the owning thread, // which only writes to locations later than `len` // and will not resize the buffer without acquiring the lock. - size_t len = need_sync ? jl_atomic_load_acquire(&list->len) : list->len; + size_t len = need_sync ? jl_atomic_load_acquire((_Atomic(size_t)*)&list->len) : list->len; size_t oldlen = len; void **items = list->items; size_t j = 0; @@ -331,7 +331,7 @@ static void finalize_object(arraylist_t *list, jl_value_t *o, // The `memset` (like any other content mutation) has to be done // **before** the `cmpxchg` which publishes the length. memset(&items[len], 0, (oldlen - len) * sizeof(void*)); - jl_atomic_cmpswap(&list->len, &oldlen, len); + jl_atomic_cmpswap((_Atomic(size_t)*)&list->len, &oldlen, len); } else { list->len = len; @@ -484,7 +484,7 @@ void jl_gc_run_all_finalizers(jl_task_t *ct) static void gc_add_finalizer_(jl_ptls_t ptls, void *v, void *f) JL_NOTSAFEPOINT { - assert(ptls->gc_state == 0); + assert(jl_atomic_load_relaxed(&ptls->gc_state) == 0); arraylist_t *a = &ptls->finalizers; // This acquire load and the release store at the end are used to // synchronize with `finalize_object` on another thread. Apart from the GC, @@ -493,7 +493,7 @@ static void gc_add_finalizer_(jl_ptls_t ptls, void *v, void *f) JL_NOTSAFEPOINT // (only one thread since it needs to acquire the finalizer lock). // Similar to `finalize_object`, all content mutation has to be done // between the acquire and the release of the length. - size_t oldlen = jl_atomic_load_acquire(&a->len); + size_t oldlen = jl_atomic_load_acquire((_Atomic(size_t)*)&a->len); if (__unlikely(oldlen + 2 > a->max)) { JL_LOCK_NOGC(&finalizers_lock); // `a->len` might have been modified. @@ -507,7 +507,7 @@ static void gc_add_finalizer_(jl_ptls_t ptls, void *v, void *f) JL_NOTSAFEPOINT void **items = a->items; items[oldlen] = v; items[oldlen + 1] = f; - jl_atomic_store_release(&a->len, oldlen + 2); + jl_atomic_store_release((_Atomic(size_t)*)&a->len, oldlen + 2); } JL_DLLEXPORT void jl_gc_add_ptr_finalizer(jl_ptls_t ptls, jl_value_t *v, void *f) JL_NOTSAFEPOINT @@ -738,7 +738,7 @@ STATIC_INLINE int gc_setmark_tag(jl_taggedvalue_t *o, uint8_t mark_mode, assert((tag & 0x3) == mark_mode); } *bits = mark_mode; - tag = jl_atomic_exchange_relaxed(&o->header, tag); + tag = jl_atomic_exchange_relaxed((_Atomic(uintptr_t)*)&o->header, tag); verify_val(jl_valueof(o)); return !gc_marked(tag); } @@ -781,7 +781,8 @@ STATIC_INLINE void gc_setmark_pool_(jl_ptls_t ptls, jl_taggedvalue_t *o, jl_assume(page); if (mark_mode == GC_OLD_MARKED) { ptls->gc_cache.perm_scanned_bytes += page->osize; - jl_atomic_fetch_add_relaxed(&page->nold, 1); + static_assert(sizeof(_Atomic(uint16_t)) == sizeof(page->nold), ""); + jl_atomic_fetch_add_relaxed((_Atomic(uint16_t)*)&page->nold, 1); } else { ptls->gc_cache.scanned_bytes += page->osize; @@ -790,7 +791,7 @@ STATIC_INLINE void gc_setmark_pool_(jl_ptls_t ptls, jl_taggedvalue_t *o, char *page_begin = gc_page_data(o) + GC_PAGE_OFFSET; int obj_id = (((char*)o) - page_begin) / page->osize; uint8_t *ages = page->ages + obj_id / 8; - jl_atomic_fetch_and_relaxed(ages, ~(1 << (obj_id % 8))); + jl_atomic_fetch_and_relaxed((_Atomic(uint8_t)*)ages, ~(1 << (obj_id % 8))); } } objprofile_count(jl_typeof(jl_valueof(o)), @@ -877,7 +878,7 @@ void jl_gc_force_mark_old(jl_ptls_t ptls, jl_value_t *v) JL_NOTSAFEPOINT static inline void maybe_collect(jl_ptls_t ptls) { - if (ptls->gc_num.allocd >= 0 || gc_debug_check_other()) { + if (jl_atomic_load_relaxed(&ptls->gc_num.allocd) >= 0 || gc_debug_check_other()) { jl_gc_collect(JL_GC_AUTO); } else { @@ -956,8 +957,10 @@ JL_DLLEXPORT jl_value_t *jl_gc_big_alloc(jl_ptls_t ptls, size_t sz) jl_throw(jl_memory_exception); gc_invoke_callbacks(jl_gc_cb_notify_external_alloc_t, gc_cblist_notify_external_alloc, (v, allocsz)); - ptls->gc_num.allocd += allocsz; - ptls->gc_num.bigalloc++; + jl_atomic_store_relaxed(&ptls->gc_num.allocd, + jl_atomic_load_relaxed(&ptls->gc_num.allocd) + allocsz); + jl_atomic_store_relaxed(&ptls->gc_num.bigalloc, + jl_atomic_load_relaxed(&ptls->gc_num.bigalloc) + 1); #ifdef MEMDEBUG memset(v, 0xee, allocsz); #endif @@ -1050,7 +1053,8 @@ void jl_gc_track_malloced_array(jl_ptls_t ptls, jl_array_t *a) JL_NOTSAFEPOINT void jl_gc_count_allocd(size_t sz) JL_NOTSAFEPOINT { jl_ptls_t ptls = jl_current_task->ptls; - ptls->gc_num.allocd += sz; + jl_atomic_store_relaxed(&ptls->gc_num.allocd, + jl_atomic_load_relaxed(&ptls->gc_num.allocd) + sz); } static void combine_thread_gc_counts(jl_gc_num_t *dest) JL_NOTSAFEPOINT @@ -1074,8 +1078,8 @@ static void reset_thread_gc_counts(void) JL_NOTSAFEPOINT for (int i = 0; i < jl_n_threads; i++) { jl_ptls_t ptls = jl_all_tls_states[i]; if (ptls) { - memset(&ptls->gc_num, 0, sizeof(jl_thread_gc_num_t)); - ptls->gc_num.allocd = -(int64_t)gc_num.interval; + memset(&ptls->gc_num, 0, sizeof(ptls->gc_num)); + jl_atomic_store_relaxed(&ptls->gc_num.allocd, -(int64_t)gc_num.interval); } } } @@ -1198,13 +1202,15 @@ JL_DLLEXPORT jl_value_t *jl_gc_pool_alloc(jl_ptls_t ptls, int pool_offset, // to workaround a llvm bug. // Ref https://llvm.org/bugs/show_bug.cgi?id=27190 jl_gc_pool_t *p = (jl_gc_pool_t*)((char*)ptls + pool_offset); - assert(ptls->gc_state == 0); + assert(jl_atomic_load_relaxed(&ptls->gc_state) == 0); #ifdef MEMDEBUG return jl_gc_big_alloc(ptls, osize); #endif maybe_collect(ptls); - ptls->gc_num.allocd += osize; - ptls->gc_num.poolalloc++; + jl_atomic_store_relaxed(&ptls->gc_num.allocd, + jl_atomic_load_relaxed(&ptls->gc_num.allocd) + osize); + jl_atomic_store_relaxed(&ptls->gc_num.poolalloc, + jl_atomic_load_relaxed(&ptls->gc_num.poolalloc) + 1); // first try to use the freelist jl_taggedvalue_t *v = p->freelist; if (v) { @@ -2398,8 +2404,8 @@ module_binding: { void *vb = jl_astaggedvalue(b); verify_parent1("module", binding->parent, &vb, "binding_buff"); (void)vb; - jl_value_t *value = b->value; - jl_value_t *globalref = b->globalref; + jl_value_t *value = jl_atomic_load_relaxed(&b->value); + jl_value_t *globalref = jl_atomic_load_relaxed(&b->globalref); if (value) { verify_parent2("module", binding->parent, &b->value, "binding(%s)", jl_symbol_name(b->name)); @@ -2800,9 +2806,11 @@ static void mark_roots(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp) } } gc_mark_queue_obj(gc_cache, sp, jl_anytuple_type_type); - for (size_t i = 0; i < N_CALL_CACHE; i++) - if (call_cache[i]) - gc_mark_queue_obj(gc_cache, sp, call_cache[i]); + for (size_t i = 0; i < N_CALL_CACHE; i++) { + jl_typemap_entry_t *v = jl_atomic_load_relaxed(&call_cache[i]); + if (v != NULL) + gc_mark_queue_obj(gc_cache, sp, v); + } if (jl_all_methods != NULL) gc_mark_queue_obj(gc_cache, sp, jl_all_methods); if (_jl_debug_method_invalidation != NULL) @@ -2858,7 +2866,7 @@ static void sweep_finalizer_list(arraylist_t *list) } // collector entry point and control -static volatile uint32_t jl_gc_disable_counter = 1; +static _Atomic(uint32_t) jl_gc_disable_counter = 1; JL_DLLEXPORT int jl_gc_enable(int on) { @@ -2969,8 +2977,8 @@ static void jl_gc_queue_remset(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp jl_binding_t *ptr = (jl_binding_t*)items[i]; // A null pointer can happen here when the binding is cleaned up // as an exception is thrown after it was already queued (#10221) - if (!ptr->value) continue; - if (gc_mark_queue_obj(gc_cache, sp, ptr->value)) { + jl_value_t *v = jl_atomic_load_relaxed(&ptr->value); + if (v != NULL && gc_mark_queue_obj(gc_cache, sp, v)) { items[n_bnd_refyoung] = ptr; n_bnd_refyoung++; } @@ -3204,15 +3212,16 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) { jl_task_t *ct = jl_current_task; jl_ptls_t ptls = ct->ptls; - if (jl_gc_disable_counter) { - size_t localbytes = ptls->gc_num.allocd + gc_num.interval; - ptls->gc_num.allocd = -(int64_t)gc_num.interval; - jl_atomic_add_fetch(&gc_num.deferred_alloc, localbytes); + if (jl_atomic_load_relaxed(&jl_gc_disable_counter)) { + size_t localbytes = jl_atomic_load_relaxed(&ptls->gc_num.allocd) + gc_num.interval; + jl_atomic_store_relaxed(&ptls->gc_num.allocd, -(int64_t)gc_num.interval); + static_assert(sizeof(_Atomic(uint64_t)) == sizeof(gc_num.deferred_alloc), ""); + jl_atomic_fetch_add((_Atomic(uint64_t)*)&gc_num.deferred_alloc, localbytes); return; } gc_debug_print(); - int8_t old_state = ptls->gc_state; + int8_t old_state = jl_atomic_load_relaxed(&ptls->gc_state); jl_atomic_store_release(&ptls->gc_state, JL_GC_STATE_WAITING); // `jl_safepoint_start_gc()` makes sure only one thread can // run the GC. @@ -3234,7 +3243,7 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) gc_invoke_callbacks(jl_gc_cb_pre_gc_t, gc_cblist_pre_gc, (collection)); - if (!jl_gc_disable_counter) { + if (!jl_atomic_load_relaxed(&jl_gc_disable_counter)) { JL_LOCK_NOGC(&finalizers_lock); if (_jl_gc_collect(ptls, collection)) { // recollect @@ -3319,9 +3328,9 @@ void jl_init_thread_heap(jl_ptls_t ptls) gc_cache->pc_stack_end = gc_cache->pc_stack + init_size; gc_cache->data_stack = (jl_gc_mark_data_t *)malloc_s(init_size * sizeof(jl_gc_mark_data_t)); - memset(&ptls->gc_num, 0, sizeof(jl_thread_gc_num_t)); + memset(&ptls->gc_num, 0, sizeof(ptls->gc_num)); assert(gc_num.interval == default_collect_interval); - ptls->gc_num.allocd = -(int64_t)gc_num.interval; + jl_atomic_store_relaxed(&ptls->gc_num.allocd, -(int64_t)gc_num.interval); } // System-wide initializations @@ -3366,8 +3375,10 @@ JL_DLLEXPORT void *jl_gc_counted_malloc(size_t sz) if (pgcstack && ct->world_age) { jl_ptls_t ptls = ct->ptls; maybe_collect(ptls); - ptls->gc_num.allocd += sz; - ptls->gc_num.malloc++; + jl_atomic_store_relaxed(&ptls->gc_num.allocd, + jl_atomic_load_relaxed(&ptls->gc_num.allocd) + sz); + jl_atomic_store_relaxed(&ptls->gc_num.malloc, + jl_atomic_load_relaxed(&ptls->gc_num.malloc) + 1); } return malloc(sz); } @@ -3379,8 +3390,10 @@ JL_DLLEXPORT void *jl_gc_counted_calloc(size_t nm, size_t sz) if (pgcstack && ct->world_age) { jl_ptls_t ptls = ct->ptls; maybe_collect(ptls); - ptls->gc_num.allocd += nm*sz; - ptls->gc_num.malloc++; + jl_atomic_store_relaxed(&ptls->gc_num.allocd, + jl_atomic_load_relaxed(&ptls->gc_num.allocd) + nm*sz); + jl_atomic_store_relaxed(&ptls->gc_num.malloc, + jl_atomic_load_relaxed(&ptls->gc_num.malloc) + 1); } return calloc(nm, sz); } @@ -3392,8 +3405,10 @@ JL_DLLEXPORT void jl_gc_counted_free_with_size(void *p, size_t sz) free(p); if (pgcstack && ct->world_age) { jl_ptls_t ptls = ct->ptls; - ptls->gc_num.freed += sz; - ptls->gc_num.freecall++; + jl_atomic_store_relaxed(&ptls->gc_num.freed, + jl_atomic_load_relaxed(&ptls->gc_num.freed) + sz); + jl_atomic_store_relaxed(&ptls->gc_num.freecall, + jl_atomic_load_relaxed(&ptls->gc_num.freecall) + 1); } } @@ -3405,10 +3420,13 @@ JL_DLLEXPORT void *jl_gc_counted_realloc_with_old_size(void *p, size_t old, size jl_ptls_t ptls = ct->ptls; maybe_collect(ptls); if (sz < old) - ptls->gc_num.freed += (old - sz); + jl_atomic_store_relaxed(&ptls->gc_num.freed, + jl_atomic_load_relaxed(&ptls->gc_num.freed) + (old - sz)); else - ptls->gc_num.allocd += (sz - old); - ptls->gc_num.realloc++; + jl_atomic_store_relaxed(&ptls->gc_num.allocd, + jl_atomic_load_relaxed(&ptls->gc_num.allocd) + (sz - old)); + jl_atomic_store_relaxed(&ptls->gc_num.realloc, + jl_atomic_load_relaxed(&ptls->gc_num.realloc) + 1); } return realloc(p, sz); } @@ -3472,8 +3490,10 @@ JL_DLLEXPORT void *jl_gc_managed_malloc(size_t sz) size_t allocsz = LLT_ALIGN(sz, JL_CACHE_BYTE_ALIGNMENT); if (allocsz < sz) // overflow in adding offs, size was "negative" jl_throw(jl_memory_exception); - ptls->gc_num.allocd += allocsz; - ptls->gc_num.malloc++; + jl_atomic_store_relaxed(&ptls->gc_num.allocd, + jl_atomic_load_relaxed(&ptls->gc_num.allocd) + allocsz); + jl_atomic_store_relaxed(&ptls->gc_num.malloc, + jl_atomic_load_relaxed(&ptls->gc_num.malloc) + 1); int last_errno = errno; #ifdef _OS_WINDOWS_ DWORD last_error = GetLastError(); @@ -3503,10 +3523,13 @@ static void *gc_managed_realloc_(jl_ptls_t ptls, void *d, size_t sz, size_t olds live_bytes += allocsz - oldsz; } else if (allocsz < oldsz) - ptls->gc_num.freed += (oldsz - allocsz); + jl_atomic_store_relaxed(&ptls->gc_num.freed, + jl_atomic_load_relaxed(&ptls->gc_num.freed) + (oldsz - allocsz)); else - ptls->gc_num.allocd += (allocsz - oldsz); - ptls->gc_num.realloc++; + jl_atomic_store_relaxed(&ptls->gc_num.allocd, + jl_atomic_load_relaxed(&ptls->gc_num.allocd) + (allocsz - oldsz)); + jl_atomic_store_relaxed(&ptls->gc_num.realloc, + jl_atomic_load_relaxed(&ptls->gc_num.realloc) + 1); int last_errno = errno; #ifdef _OS_WINDOWS_ @@ -3576,7 +3599,7 @@ jl_value_t *jl_gc_realloc_string(jl_value_t *s, size_t sz) #define GC_PERM_POOL_SIZE (2 * 1024 * 1024) // 20k limit for pool allocation. At most 1% fragmentation #define GC_PERM_POOL_LIMIT (20 * 1024) -jl_mutex_t gc_perm_lock = {0, 0}; +jl_mutex_t gc_perm_lock; static uintptr_t gc_perm_pool = 0; static uintptr_t gc_perm_end = 0; @@ -3721,8 +3744,8 @@ JL_DLLEXPORT int jl_gc_enable_conservative_gc_support(void) } return result; } else { - int result = support_conservative_marking; - support_conservative_marking = 1; + int result = jl_atomic_load(&support_conservative_marking); + jl_atomic_store(&support_conservative_marking, 1); return result; } } diff --git a/src/gc.h b/src/gc.h index 06faa64a8b07f..19fe3401665d1 100644 --- a/src/gc.h +++ b/src/gc.h @@ -371,7 +371,7 @@ typedef struct { int ub; } pagetable_t; -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ unsigned ffs_u32(uint32_t bitvec) JL_NOTSAFEPOINT; #else STATIC_INLINE unsigned ffs_u32(uint32_t bitvec) diff --git a/src/gf.c b/src/gf.c index 3f742af4ca1f6..7e0d6cf59f533 100644 --- a/src/gf.c +++ b/src/gf.c @@ -118,7 +118,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo(jl_method_t *m J } } else { - jl_method_instance_t **data = (jl_method_instance_t**)jl_svec_data(specializations); + _Atomic(jl_method_instance_t*) *data = (_Atomic(jl_method_instance_t*)*)jl_svec_data(specializations); JL_GC_PUSH1(&specializations); // clang-sa doesn't realize this loop uses specializations for (i = cl; i > 0; i--) { jl_method_instance_t *mi = jl_atomic_load_relaxed(&data[i - 1]); @@ -140,7 +140,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo(jl_method_t *m J } else { if (hv) { - jl_method_instance_t **data = (jl_method_instance_t**)jl_svec_data(specializations); + _Atomic(jl_method_instance_t*) *data = (_Atomic(jl_method_instance_t*)*)jl_svec_data(specializations); for (i = 0; i < cl; i++) { jl_method_instance_t *mi = jl_atomic_load_relaxed(&data[i]); if ((jl_value_t*)mi == jl_nothing) @@ -992,7 +992,7 @@ static inline jl_typemap_entry_t *lookup_leafcache(jl_array_t *leafcache JL_PROP } static jl_method_instance_t *cache_method( - jl_methtable_t *mt, jl_typemap_t **cache, jl_value_t *parent JL_PROPAGATES_ROOT, + jl_methtable_t *mt, _Atomic(jl_typemap_t*) *cache, jl_value_t *parent JL_PROPAGATES_ROOT, jl_tupletype_t *tt, // the original tupletype of the signature jl_method_t *definition, size_t world, size_t min_valid, size_t max_valid, @@ -1009,7 +1009,7 @@ static jl_method_instance_t *cache_method( return entry->func.linfo; } struct jl_typemap_assoc search = {(jl_value_t*)tt, world, NULL, 0, ~(size_t)0}; - jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(*cache, &search, offs, /*subtype*/1); + jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(jl_atomic_load_relaxed(cache), &search, offs, /*subtype*/1); if (entry && entry->func.value) return entry->func.linfo; } @@ -1135,7 +1135,7 @@ static jl_method_instance_t *cache_method( // that satisfies our requirements if (cachett != tt) { struct jl_typemap_assoc search = {(jl_value_t*)cachett, world, NULL, 0, ~(size_t)0}; - jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(*cache, &search, offs, /*subtype*/1); + jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(jl_atomic_load_relaxed(cache), &search, offs, /*subtype*/1); if (entry && jl_egal((jl_value_t*)entry->simplesig, simplett ? (jl_value_t*)simplett : jl_nothing) && jl_egal((jl_value_t*)guardsigs, (jl_value_t*)entry->guardsigs)) { JL_GC_POP(); @@ -1179,7 +1179,7 @@ static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt JL_PROPAGATE // caller must hold the mt->writelock assert(tt->isdispatchtuple || tt->hasfreetypevars); if (tt->isdispatchtuple) { - jl_array_t *leafcache = mt->leafcache; + jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); jl_typemap_entry_t *entry = lookup_leafcache(leafcache, (jl_value_t*)tt, world); if (entry) return entry->func.linfo; @@ -1527,7 +1527,7 @@ static int typemap_search(jl_typemap_entry_t *entry, void *closure) static jl_typemap_entry_t *do_typemap_search(jl_methtable_t *mt JL_PROPAGATES_ROOT, jl_method_t *method) JL_NOTSAFEPOINT; -#ifndef __clang_analyzer__ +#ifndef __clang_gcanalyzer__ static jl_typemap_entry_t *do_typemap_search(jl_methtable_t *mt JL_PROPAGATES_ROOT, jl_method_t *method) JL_NOTSAFEPOINT { jl_value_t *closure = (jl_value_t*)(method); if (jl_typemap_visitor(mt->defs, typemap_search, &closure)) @@ -1547,7 +1547,7 @@ static void jl_method_table_invalidate(jl_methtable_t *mt, jl_typemap_entry_t *m mt_cache_env.shadowed = NULL; mt_cache_env.invalidated = 0; jl_typemap_visitor(mt->cache, disable_mt_cache, (void*)&mt_cache_env); - jl_array_t *leafcache = mt->leafcache; + jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); size_t i, l = jl_array_len(leafcache); for (i = 1; i < l; i += 2) { jl_typemap_entry_t *oldentry = (jl_typemap_entry_t*)jl_array_ptr_ref(leafcache, i); @@ -1727,7 +1727,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method if (morespec[j] == (char)morespec_is) continue; jl_svec_t *specializations = jl_atomic_load_acquire(&m->specializations); - jl_method_instance_t **data = (jl_method_instance_t**)jl_svec_data(specializations); + _Atomic(jl_method_instance_t*) *data = (_Atomic(jl_method_instance_t*)*)jl_svec_data(specializations); size_t i, l = jl_svec_len(specializations); enum morespec_options ambig = morespec_unknown; for (i = 0; i < l; i++) { @@ -1783,7 +1783,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method mt_cache_env.invalidated = 0; jl_typemap_visitor(mt->cache, invalidate_mt_cache, (void*)&mt_cache_env); - jl_array_t *leafcache = mt->leafcache; + jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); size_t i, l = jl_array_len(leafcache); for (i = 1; i < l; i += 2) { jl_value_t *entry = jl_array_ptr_ref(leafcache, i); @@ -1826,7 +1826,7 @@ static void JL_NORETURN jl_method_error_bare(jl_function_t *f, jl_value_t *args, jl_static_show((JL_STREAM*)STDERR_FILENO,args); jl_printf((JL_STREAM*)STDERR_FILENO,"\n"); jl_ptls_t ptls = jl_current_task->ptls; ptls->bt_size = rec_backtrace(ptls->bt_data, JL_MAX_BT_SIZE, 0); - jl_critical_error(0, NULL); + jl_critical_error(0, NULL, jl_current_task); abort(); } // not reached @@ -2274,8 +2274,8 @@ STATIC_INLINE int sig_match_fast(jl_value_t *arg1t, jl_value_t **args, jl_value_ return 1; } -jl_typemap_entry_t *call_cache[N_CALL_CACHE] JL_GLOBALLY_ROOTED; -static uint8_t pick_which[N_CALL_CACHE]; +_Atomic(jl_typemap_entry_t*) call_cache[N_CALL_CACHE] JL_GLOBALLY_ROOTED; +static _Atomic(uint8_t) pick_which[N_CALL_CACHE]; #ifdef JL_GF_PROFILE size_t ncalls; void call_cache_stats() diff --git a/src/iddict.c b/src/iddict.c index 0d67a2b4c82c1..73c2999491c28 100644 --- a/src/iddict.c +++ b/src/iddict.c @@ -43,7 +43,7 @@ static inline int jl_table_assign_bp(jl_array_t **pa, jl_value_t *key, jl_value_ *pa = a; } size_t maxprobe = max_probe(sz); - void **tab = (void **)a->data; + _Atomic(jl_value_t*) *tab = (_Atomic(jl_value_t*)*)a->data; hv = keyhash(key); while (1) { @@ -54,7 +54,7 @@ static inline int jl_table_assign_bp(jl_array_t **pa, jl_value_t *key, jl_value_ empty_slot = -1; do { - jl_value_t *k2 = (jl_value_t*)tab[index]; + jl_value_t *k2 = tab[index]; if (k2 == NULL) { if (empty_slot == -1) empty_slot = index; @@ -102,20 +102,20 @@ static inline int jl_table_assign_bp(jl_array_t **pa, jl_value_t *key, jl_value_ *pa = jl_idtable_rehash(*pa, newsz); a = *pa; - tab = (void **)a->data; + tab = (_Atomic(jl_value_t*)*)a->data; sz = hash_size(a); maxprobe = max_probe(sz); } } /* returns bp if key is in hash, otherwise NULL */ -inline jl_value_t **jl_table_peek_bp(jl_array_t *a, jl_value_t *key) JL_NOTSAFEPOINT +inline _Atomic(jl_value_t*) *jl_table_peek_bp(jl_array_t *a, jl_value_t *key) JL_NOTSAFEPOINT { size_t sz = hash_size(a); if (sz == 0) return NULL; size_t maxprobe = max_probe(sz); - void **tab = (void **)a->data; + _Atomic(jl_value_t*) *tab = (_Atomic(jl_value_t*)*)a->data; uint_t hv = keyhash(key); size_t index = h2index(hv, sz); sz *= 2; @@ -123,12 +123,12 @@ inline jl_value_t **jl_table_peek_bp(jl_array_t *a, jl_value_t *key) JL_NOTSAFEP size_t iter = 0; do { - jl_value_t *k2 = (jl_value_t*)jl_atomic_load_relaxed(&tab[index]); // just to ensure the load doesn't get duplicated + jl_value_t *k2 = jl_atomic_load_relaxed(&tab[index]); // just to ensure the load doesn't get duplicated if (k2 == NULL) return NULL; if (jl_egal(key, k2)) { if (jl_atomic_load_relaxed(&tab[index + 1]) != NULL) - return (jl_value_t**)&tab[index + 1]; + return &tab[index + 1]; // `nothing` is our sentinel value for deletion, so need to keep searching if it's also our search key if (key != jl_nothing) return NULL; // concurrent insertion hasn't completed yet @@ -155,21 +155,21 @@ jl_array_t *jl_eqtable_put(jl_array_t *h, jl_value_t *key, jl_value_t *val, int JL_DLLEXPORT jl_value_t *jl_eqtable_get(jl_array_t *h, jl_value_t *key, jl_value_t *deflt) JL_NOTSAFEPOINT { - jl_value_t **bp = jl_table_peek_bp(h, key); + _Atomic(jl_value_t*) *bp = jl_table_peek_bp(h, key); return (bp == NULL) ? deflt : jl_atomic_load_relaxed(bp); } JL_DLLEXPORT jl_value_t *jl_eqtable_pop(jl_array_t *h, jl_value_t *key, jl_value_t *deflt, int *found) { - jl_value_t **bp = jl_table_peek_bp(h, key); + _Atomic(jl_value_t*) *bp = jl_table_peek_bp(h, key); if (found) *found = (bp != NULL); if (bp == NULL) return deflt; - jl_value_t *val = *bp; - *(bp - 1) = jl_nothing; // clear the key - *bp = NULL; + jl_value_t *val = jl_atomic_load_relaxed(bp); + jl_atomic_store_relaxed(bp - 1, jl_nothing); // clear the key + jl_atomic_store_relaxed(bp, NULL); // and the value (briefly corrupting the table) return val; } diff --git a/src/interpreter.c b/src/interpreter.c index 2bfa6482aa9f7..9aab434f1d8a5 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -37,7 +37,7 @@ typedef struct { JL_GCC_IGNORE_STOP #endif -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ extern void JL_GC_ENABLEFRAME(interpreter_state*) JL_NOTSAFEPOINT; @@ -93,7 +93,7 @@ static jl_value_t *eval_methoddef(jl_expr_t *ex, interpreter_state *s) } jl_value_t *bp_owner = (jl_value_t*)modu; jl_binding_t *b = jl_get_binding_for_method_def(modu, fname); - jl_value_t **bp = &b->value; + _Atomic(jl_value_t*) *bp = &b->value; jl_value_t *gf = jl_generic_function_def(b->name, b->owner, bp, bp_owner, b); return gf; } diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 1847fc5c60e37..e566c6b6bd731 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -305,8 +305,7 @@ static Value *emit_unboxed_coercion(jl_codectx_t &ctx, Type *to, Value *unboxed) } else if (!ty->isIntOrPtrTy() && !ty->isFloatingPointTy()) { const DataLayout &DL = jl_data_layout; - unsigned nb = DL.getTypeSizeInBits(ty); - assert(nb == DL.getTypeSizeInBits(to)); + assert(DL.getTypeSizeInBits(ty) == DL.getTypeSizeInBits(to)); AllocaInst *cast = ctx.builder.CreateAlloca(ty); ctx.builder.CreateStore(unboxed, cast); unboxed = ctx.builder.CreateLoad(to, ctx.builder.CreateBitCast(cast, to->getPointerTo())); diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index a87e14f7a76a4..792250490544a 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -175,14 +175,14 @@ static jl_callptr_t _jl_compile_codeinst( // once set, don't change invoke-ptr, as that leads to race conditions // with the (not) simultaneous updates to invoke and specptr if (!decls.specFunctionObject.empty()) { - this_code->specptr.fptr = (void*)getAddressForFunction(decls.specFunctionObject); + jl_atomic_store_release(&this_code->specptr.fptr, (void*)getAddressForFunction(decls.specFunctionObject)); this_code->isspecsig = isspecsig; } jl_atomic_store_release(&this_code->invoke, addr); } else if (this_code->invoke == jl_fptr_const_return && !decls.specFunctionObject.empty()) { - // hack to export this pointer value to jl_dump_method_asm - this_code->specptr.fptr = (void*)getAddressForFunction(decls.specFunctionObject); + // hack to export this pointer value to jl_dump_method_disasm + jl_atomic_store_release(&this_code->specptr.fptr, (void*)getAddressForFunction(decls.specFunctionObject)); } if (this_code== codeinst) fptr = addr; @@ -412,10 +412,10 @@ jl_value_t *jl_dump_method_asm(jl_method_instance_t *mi, size_t world, // printing via disassembly jl_code_instance_t *codeinst = jl_generate_fptr(mi, world); if (codeinst) { - uintptr_t fptr = (uintptr_t)codeinst->invoke; + uintptr_t fptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->invoke); if (getwrapper) return jl_dump_fptr_asm(fptr, raw_mc, asm_variant, debuginfo, binary); - uintptr_t specfptr = (uintptr_t)codeinst->specptr.fptr; + uintptr_t specfptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->specptr.fptr); if (fptr == (uintptr_t)&jl_fptr_const_return && specfptr == 0) { // normally we prevent native code from being generated for these functions, // (using sentinel value `1` instead) @@ -425,7 +425,7 @@ jl_value_t *jl_dump_method_asm(jl_method_instance_t *mi, size_t world, uint8_t measure_compile_time_enabled = jl_atomic_load_relaxed(&jl_measure_compile_time_enabled); if (measure_compile_time_enabled) compiler_start_time = jl_hrtime(); - specfptr = (uintptr_t)codeinst->specptr.fptr; + specfptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->specptr.fptr); if (specfptr == 0) { jl_code_info_t *src = jl_type_infer(mi, world, 0); JL_GC_PUSH1(&src); @@ -438,12 +438,12 @@ jl_value_t *jl_dump_method_asm(jl_method_instance_t *mi, size_t world, if (src && (jl_value_t*)src != jl_nothing) src = jl_uncompress_ir(mi->def.method, codeinst, (jl_array_t*)src); } - fptr = (uintptr_t)codeinst->invoke; - specfptr = (uintptr_t)codeinst->specptr.fptr; + fptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->invoke); + specfptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->specptr.fptr); if (src && jl_is_code_info(src)) { if (fptr == (uintptr_t)&jl_fptr_const_return && specfptr == 0) { fptr = (uintptr_t)_jl_compile_codeinst(codeinst, src, world); - specfptr = (uintptr_t)codeinst->specptr.fptr; + specfptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->specptr.fptr); } } JL_GC_POP(); @@ -830,13 +830,14 @@ StringRef JuliaOJIT::getFunctionAtAddress(uint64_t Addr, jl_code_instance_t *cod std::string string_fname; raw_string_ostream stream_fname(string_fname); // try to pick an appropriate name that describes it - if (Addr == (uintptr_t)codeinst->invoke) { + jl_callptr_t invoke = jl_atomic_load_relaxed(&codeinst->invoke); + if (Addr == (uintptr_t)invoke) { stream_fname << "jsysw_"; } - else if (codeinst->invoke == &jl_fptr_args) { + else if (invoke == &jl_fptr_args) { stream_fname << "jsys1_"; } - else if (codeinst->invoke == &jl_fptr_sparam) { + else if (invoke == &jl_fptr_sparam) { stream_fname << "jsys3_"; } else { diff --git a/src/jl_uv.c b/src/jl_uv.c index 18733926e8c54..5795cd8f7e3ba 100644 --- a/src/jl_uv.c +++ b/src/jl_uv.c @@ -57,7 +57,7 @@ void jl_init_uv(void) JL_MUTEX_INIT(&jl_uv_mutex); // a file-scope initializer can be used instead } -int jl_uv_n_waiters = 0; +_Atomic(int) jl_uv_n_waiters = 0; void JL_UV_LOCK(void) { diff --git a/src/jlapi.c b/src/jlapi.c index 2192d4cee665f..fa480414a83f1 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -433,7 +433,7 @@ JL_DLLEXPORT jl_value_t *(jl_get_fieldtypes)(jl_value_t *v) } -#ifndef __clang_analyzer__ +#ifndef __clang_gcanalyzer__ JL_DLLEXPORT int8_t (jl_gc_unsafe_enter)(void) { jl_task_t *ct = jl_current_task; diff --git a/src/jltypes.c b/src/jltypes.c index 43171ee332e87..16db7bcc0ce11 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -19,7 +19,7 @@ extern "C" { #endif -jl_value_t *cmpswap_names JL_GLOBALLY_ROOTED; +_Atomic(jl_value_t*) cmpswap_names JL_GLOBALLY_ROOTED; // compute empirical max-probe for a given size #define max_probe(size) ((size) <= 1024 ? 16 : (size) >> 6) @@ -625,7 +625,7 @@ static jl_datatype_t *lookup_type_set(jl_svec_t *cache, jl_value_t **key, size_t if (sz == 0) return NULL; size_t maxprobe = max_probe(sz); - jl_datatype_t **tab = (jl_datatype_t**)jl_svec_data(cache); + _Atomic(jl_datatype_t*) *tab = (_Atomic(jl_datatype_t*)*)jl_svec_data(cache); size_t index = h2index(hv, sz); size_t orig = index; size_t iter = 0; @@ -648,7 +648,7 @@ static jl_datatype_t *lookup_type_setvalue(jl_svec_t *cache, jl_value_t *key1, j if (sz == 0) return NULL; size_t maxprobe = max_probe(sz); - jl_datatype_t **tab = (jl_datatype_t**)jl_svec_data(cache); + _Atomic(jl_datatype_t*) *tab = (_Atomic(jl_datatype_t*)*)jl_svec_data(cache); size_t index = h2index(hv, sz); size_t orig = index; size_t iter = 0; @@ -671,7 +671,7 @@ static ssize_t lookup_type_idx_linear(jl_svec_t *cache, jl_value_t **key, size_t { if (n == 0) return -1; - jl_datatype_t **data = (jl_datatype_t**)jl_svec_data(cache); + _Atomic(jl_datatype_t*) *data = (_Atomic(jl_datatype_t*)*)jl_svec_data(cache); size_t cl = jl_svec_len(cache); ssize_t i; for (i = 0; i < cl; i++) { @@ -688,7 +688,7 @@ static ssize_t lookup_type_idx_linearvalue(jl_svec_t *cache, jl_value_t *key1, j { if (n == 0) return -1; - jl_datatype_t **data = (jl_datatype_t**)jl_svec_data(cache); + _Atomic(jl_datatype_t*) *data = (_Atomic(jl_datatype_t*)*)jl_svec_data(cache); size_t cl = jl_svec_len(cache); ssize_t i; for (i = 0; i < cl; i++) { @@ -734,7 +734,7 @@ static jl_value_t *lookup_typevalue(jl_typename_t *tn, jl_value_t *key1, jl_valu static int cache_insert_type_set_(jl_svec_t *a, jl_datatype_t *val, uint_t hv) { - jl_datatype_t **tab = (jl_datatype_t**)jl_svec_data(a); + _Atomic(jl_datatype_t*) *tab = (_Atomic(jl_datatype_t*)*)jl_svec_data(a); size_t sz = jl_svec_len(a); if (sz <= 1) return 0; @@ -2623,13 +2623,8 @@ void jl_init_types(void) JL_GC_DISABLED jl_svecset(jl_methtable_type->types, 4, jl_long_type); jl_svecset(jl_methtable_type->types, 6, jl_module_type); jl_svecset(jl_methtable_type->types, 7, jl_array_any_type); -#ifdef __LP64__ - jl_svecset(jl_methtable_type->types, 8, jl_int64_type); // unsigned long - jl_svecset(jl_methtable_type->types, 9, jl_int64_type); // uint32_t plus alignment -#else - jl_svecset(jl_methtable_type->types, 8, jl_int32_type); // DWORD - jl_svecset(jl_methtable_type->types, 9, jl_int32_type); // uint32_t -#endif + jl_svecset(jl_methtable_type->types, 8, jl_long_type); // jl_task_t* + jl_svecset(jl_methtable_type->types, 9, jl_long_type); // uint32_t plus alignment jl_svecset(jl_methtable_type->types, 10, jl_uint8_type); jl_svecset(jl_methtable_type->types, 11, jl_uint8_type); jl_svecset(jl_method_type->types, 12, jl_method_instance_type); diff --git a/src/julia-parser.scm b/src/julia-parser.scm index a22e714135822..fe4af526a5082 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -221,13 +221,6 @@ (define (newline? c) (eqv? c #\newline)) -(define (skip-to-eol port) - (let ((c (peek-char port))) - (cond ((eof-object? c) c) - ((eqv? c #\newline) c) - (else (read-char port) - (skip-to-eol port))))) - (define (op-or-sufchar? c) (or (op-suffix-char? c) (opchar? c))) (define (read-operator port c0 (postfix? #f)) @@ -495,33 +488,56 @@ (pair? (cadr t)) (eq? (car (cadr t)) 'core) (memq (cadadr t) '(@int128_str @uint128_str @big_str)))) +(define (make-bidi-state) '(0 . 0)) + +(define (update-bidi-state st c) + (case c + ((#\U202A #\U202B #\U202D #\U202E) (cons (+ (car st) 1) (cdr st))) ;; LRE RLE LRO RLO + ((#\U2066 #\U2067 #\U2068) (cons (car st) (+ (cdr st) 1))) ;; LRI RLI FSI + ((#\U202C) (cons (- (car st) 1) (cdr st))) ;; PDF + ((#\U2069) (cons (car st) (- (cdr st) 1))) ;; PDI + ((#\newline) '(0 . 0)) + (else st))) + +(define (bidi-state-terminated? st) (equal? st '(0 . 0))) + +(define (skip-line-comment port) + (let ((c (peek-char port))) + (cond ((eof-object? c) c) + ((eqv? c #\newline) c) + (else (read-char port) + (skip-line-comment port))))) + +(define (skip-multiline-comment port count bds) + (let ((c (read-char port))) + (if (eof-object? c) + (error "incomplete: unterminated multi-line comment #= ... =#") ; NOTE: changing this may affect code in base/client.jl + (if (eqv? c #\=) + (let ((c (peek-char port))) + (if (eqv? c #\#) + (begin + (read-char port) + (if (> count 1) + (skip-multiline-comment port (- count 1) bds) + (if (not (bidi-state-terminated? bds)) + (error "unbalanced bidirectional formatting in comment")))) + (skip-multiline-comment port count (update-bidi-state bds c)))) + (if (eqv? c #\#) + (skip-multiline-comment port + (if (eqv? (peek-char port) #\=) + (begin (read-char port) + (+ count 1)) + count) + bds) + (skip-multiline-comment port count (update-bidi-state bds c))))))) + ;; skip to end of comment, starting at #: either #... or #= .... =#. (define (skip-comment port) - (define (skip-multiline-comment port count) - (let ((c (read-char port))) - (if (eof-object? c) - (error "incomplete: unterminated multi-line comment #= ... =#") ; NOTE: changing this may affect code in base/client.jl - (begin (if (eqv? c #\=) - (let ((c (peek-char port))) - (if (eqv? c #\#) - (begin - (read-char port) - (if (> count 1) - (skip-multiline-comment port (- count 1)))) - (skip-multiline-comment port count))) - (if (eqv? c #\#) - (skip-multiline-comment port - (if (eqv? (peek-char port) #\=) - (begin (read-char port) - (+ count 1)) - count)) - (skip-multiline-comment port count))))))) - (read-char port) ; read # that was already peeked (if (eqv? (peek-char port) #\=) (begin (read-char port) ; read initial = - (skip-multiline-comment port 1)) - (skip-to-eol port))) + (skip-multiline-comment port 1 (make-bidi-state))) + (skip-line-comment port))) (define (skip-ws-and-comments port) (skip-ws port #t) @@ -2336,24 +2352,28 @@ (let loop ((c (read-char p)) (b (open-output-string)) (e ()) - (quotes 0)) + (quotes 0) + (bds (make-bidi-state))) (cond ((eqv? c delim) (if (< quotes n) - (loop (read-char p) b e (+ quotes 1)) - (reverse (cons (io.tostring! b) e)))) + (loop (read-char p) b e (+ quotes 1) bds) + (begin + (if (not (bidi-state-terminated? bds)) + (error "unbalanced bidirectional formatting in string literal")) + (reverse (cons (io.tostring! b) e))))) ((= quotes 1) (if (not raw) (write-char #\\ b)) (write-char delim b) - (loop c b e 0)) + (loop c b e 0 (update-bidi-state bds c))) ((= quotes 2) (if (not raw) (write-char #\\ b)) (write-char delim b) (if (not raw) (write-char #\\ b)) (write-char delim b) - (loop c b e 0)) + (loop c b e 0 (update-bidi-state bds c))) ((eqv? c #\\) (if raw @@ -2366,19 +2386,19 @@ (io.write b (string.rep "\\" (div count 2))) (if (odd? count) (begin (write-char delim b) - (loop (read-char p) b e 0)) - (loop nxch b e 0))) + (loop (read-char p) b e 0 bds)) + (loop nxch b e 0 bds))) (else (io.write b (string.rep "\\" count)) (write-char nxch b) - (loop (read-char p) b e 0)))) + (loop (read-char p) b e 0 (update-bidi-state bds nxch))))) (let ((nxch (not-eof-for delim (read-char p)))) (write-char #\\ b) (if (eqv? nxch #\return) - (loop nxch b e 0) + (loop nxch b e 0 bds) (begin (write-char nxch b) - (loop (read-char p) b e 0)))))) + (loop (read-char p) b e 0 (update-bidi-state bds nxch))))))) ((and (eqv? c #\$) (not raw)) (let* ((ex (parse-interpolate s)) @@ -2388,7 +2408,7 @@ (loop (read-char p) (open-output-string) (list* ex (io.tostring! b) e) - 0))) + 0 bds))) ; convert literal \r and \r\n in strings to \n (issue #11988) ((eqv? c #\return) ; \r @@ -2396,11 +2416,11 @@ (if (eqv? (peek-char p) #\linefeed) ; \r\n (read-char p)) (write-char #\newline b) - (loop (read-char p) b e 0))) + (loop (read-char p) b e 0 bds))) (else (write-char (not-eof-for delim c) b) - (loop (read-char p) b e 0))))) + (loop (read-char p) b e 0 (update-bidi-state bds c)))))) (define (not-eof-1 c) (if (eof-object? c) diff --git a/src/julia.h b/src/julia.h index d05dda984da8f..55f14e86f2b2d 100644 --- a/src/julia.h +++ b/src/julia.h @@ -97,7 +97,7 @@ typedef struct _jl_taggedvalue_t jl_taggedvalue_t; -#include "atomics.h" +#include "julia_atomics.h" #include "julia_threads.h" #include "julia_assert.h" @@ -127,7 +127,7 @@ JL_EXTENSION struct _jl_taggedvalue_t { // jl_value_t value; }; -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ JL_DLLEXPORT jl_taggedvalue_t *_jl_astaggedvalue(jl_value_t *v JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; #define jl_astaggedvalue(v) _jl_astaggedvalue((jl_value_t*)(v)) jl_value_t *_jl_valueof(jl_taggedvalue_t *tv JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; @@ -146,7 +146,7 @@ static inline void jl_set_typeof(void *v, void *t) JL_NOTSAFEPOINT { // Do not call this on a value that is already initialized. jl_taggedvalue_t *tag = jl_astaggedvalue(v); - jl_atomic_store_relaxed(&tag->type, (jl_value_t*)t); + jl_atomic_store_relaxed((_Atomic(jl_value_t*)*)&tag->type, (jl_value_t*)t); } #define jl_typeis(v,t) (jl_typeof(v)==(jl_value_t*)(t)) @@ -154,8 +154,8 @@ static inline void jl_set_typeof(void *v, void *t) JL_NOTSAFEPOINT // The string data is nul-terminated and hangs off the end of the struct. typedef struct _jl_sym_t { JL_DATA_TYPE - struct _jl_sym_t *left; - struct _jl_sym_t *right; + _Atomic(struct _jl_sym_t*) left; + _Atomic(struct _jl_sym_t*) right; uintptr_t hash; // precomputed hash value // JL_ATTRIBUTE_ALIGN_PTRSIZE(char name[]); } jl_sym_t; @@ -248,14 +248,6 @@ typedef jl_value_t *(*jl_fptr_sparam_t)(jl_value_t*, jl_value_t**, uint32_t, jl_ JL_DLLEXPORT extern jl_call_t jl_fptr_interpret_call; -JL_EXTENSION typedef union { - void* fptr; - jl_fptr_args_t fptr1; - // 2 constant - jl_fptr_sparam_t fptr3; - // 4 interpreter -} jl_generic_specptr_t; - typedef struct _jl_method_instance_t jl_method_instance_t; typedef struct _jl_line_info_node_t { @@ -314,13 +306,13 @@ typedef struct _jl_method_t { jl_value_t *sig; // table of all jl_method_instance_t specializations we have - jl_svec_t *specializations; // allocated as [hashable, ..., NULL, linear, ....] - jl_array_t *speckeyset; // index lookup by hash into specializations + _Atomic(jl_svec_t*) specializations; // allocated as [hashable, ..., NULL, linear, ....] + _Atomic(jl_array_t*) speckeyset; // index lookup by hash into specializations jl_value_t *slot_syms; // compacted list of slot names (String) jl_value_t *external_mt; // reference to the method table this method is part of, null if part of the internal table jl_value_t *source; // original code template (jl_code_info_t, but may be compressed), null for builtins - struct _jl_method_instance_t *unspecialized; // unspecialized executable method instance, or null + _Atomic(struct _jl_method_instance_t*) unspecialized; // unspecialized executable method instance, or null jl_value_t *generator; // executable code-generating function if available jl_array_t *roots; // pointers in generated code (shared to reduce memory), or null jl_svec_t *ccallable; // svec(rettype, sig) if a ccallable entry point is requested for this @@ -328,7 +320,7 @@ typedef struct _jl_method_t { // cache of specializations of this method for invoke(), i.e. // cases where this method was called even though it was not necessarily // the most specific for the argument types. - jl_typemap_t *invokes; + _Atomic(jl_typemap_t*) invokes; // A function that compares two specializations of this method, returning // `true` if the first signature is to be considered "smaller" than the @@ -366,7 +358,7 @@ struct _jl_method_instance_t { jl_value_t *uninferred; // cached uncompressed code, for generated functions, top-level thunks, or the interpreter jl_array_t *backedges; // list of method-instances which contain a call into this method-instance jl_array_t *callbacks; // list of callback functions to inform external caches about invalidations - struct _jl_code_instance_t *cache; + _Atomic(struct _jl_code_instance_t*) cache; uint8_t inInference; // flags to tell if inference is running on this object }; @@ -385,7 +377,7 @@ typedef struct jl_opaque_closure_t { typedef struct _jl_code_instance_t { JL_DATA_TYPE jl_method_instance_t *def; // method this is specialized from - struct _jl_code_instance_t *next; // pointer to the next cache entry + _Atomic(struct _jl_code_instance_t*) next; // pointer to the next cache entry // world range for which this object is valid to use size_t min_world; @@ -400,9 +392,15 @@ typedef struct _jl_code_instance_t { // compilation state cache uint8_t isspecsig; // if specptr is a specialized function signature for specTypes->rettype - uint8_t precompile; // if set, this will be added to the output system image - jl_callptr_t invoke; // jlcall entry point - jl_generic_specptr_t specptr; // private data for `jlcall entry point` + _Atomic(uint8_t) precompile; // if set, this will be added to the output system image + _Atomic(jl_callptr_t) invoke; // jlcall entry point + union _jl_generic_specptr_t { + _Atomic(void*) fptr; + _Atomic(jl_fptr_args_t) fptr1; + // 2 constant + _Atomic(jl_fptr_sparam_t) fptr3; + // 4 interpreter + } specptr; // private data for `jlcall entry point } jl_code_instance_t; // all values are callable as Functions @@ -436,8 +434,8 @@ typedef struct { // `wrapper` is either the only instantiation of the type (if no parameters) // or a UnionAll accepting parameters to make an instantiation. jl_value_t *wrapper; - jl_svec_t *cache; // sorted array - jl_svec_t *linearcache; // unsorted array + _Atomic(jl_svec_t*) cache; // sorted array + _Atomic(jl_svec_t*) linearcache; // unsorted array struct _jl_methtable_t *mt; jl_array_t *partial; // incomplete instantiations of this type intptr_t hash; @@ -526,10 +524,10 @@ typedef struct { typedef struct { // not first-class jl_sym_t *name; - jl_value_t *value; - jl_value_t *globalref; // cached GlobalRef for this binding - struct _jl_module_t *owner; // for individual imported bindings - uint8_t constp; + _Atomic(jl_value_t*) value; + _Atomic(jl_value_t*) globalref; // cached GlobalRef for this binding + struct _jl_module_t* owner; // for individual imported bindings -- TODO: make _Atomic + uint8_t constp:1; uint8_t exportp:1; uint8_t imported:1; uint8_t deprecated:2; // 0=not deprecated, 1=renamed, 2=moved to another package @@ -550,7 +548,7 @@ typedef struct _jl_module_t { uint64_t build_id; jl_uuid_t uuid; size_t primary_world; - uint32_t counter; + _Atomic(uint32_t) counter; int32_t nospecialize; // global bit flags: initialization for new methods int8_t optlevel; int8_t compile; @@ -562,7 +560,7 @@ typedef struct _jl_module_t { // one Type-to-Value entry typedef struct _jl_typemap_entry_t { JL_DATA_TYPE - struct _jl_typemap_entry_t *next; // invasive linked list + _Atomic(struct _jl_typemap_entry_t*) next; // invasive linked list jl_tupletype_t *sig; // the type signature for this entry jl_tupletype_t *simplesig; // a simple signature for fast rejection jl_svec_t *guardsigs; @@ -587,23 +585,23 @@ typedef struct _jl_typemap_level_t { // next split may be on Type{T} as LeafTypes then TypeName's parents up to Any // next split may be on LeafType // next split may be on TypeName - jl_array_t *arg1; // contains LeafType - jl_array_t *targ; // contains Type{LeafType} - jl_array_t *name1; // contains non-abstract TypeName, for parents up to (excluding) Any - jl_array_t *tname; // contains a dict of Type{TypeName}, for parents up to Any + _Atomic(jl_array_t*) arg1; // contains LeafType + _Atomic(jl_array_t*) targ; // contains Type{LeafType} + _Atomic(jl_array_t*) name1; // contains non-abstract TypeName, for parents up to (excluding) Any + _Atomic(jl_array_t*) tname; // contains a dict of Type{TypeName}, for parents up to Any // next a linear list of things too complicated at this level for analysis (no more levels) - jl_typemap_entry_t *linear; + _Atomic(jl_typemap_entry_t*) linear; // finally, start a new level if the type at offs is Any - jl_typemap_t *any; + _Atomic(jl_typemap_t*) any; } jl_typemap_level_t; // contains the TypeMap for one Type typedef struct _jl_methtable_t { JL_DATA_TYPE jl_sym_t *name; // sometimes a hack used by serialization to handle kwsorter - jl_typemap_t *defs; - jl_array_t *leafcache; - jl_typemap_t *cache; + _Atomic(jl_typemap_t*) defs; + _Atomic(jl_array_t*) leafcache; + _Atomic(jl_typemap_t*) cache; intptr_t max_args; // max # of non-vararg arguments in a signature jl_value_t *kwsorter; // keyword argument sorter function jl_module_t *module; // used for incremental serialization to locate original binding @@ -777,7 +775,7 @@ struct _jl_gcframe_t { #define JL_GC_ENCODE_PUSHARGS(n) (((size_t)(n))<<2) #define JL_GC_ENCODE_PUSH(n) ((((size_t)(n))<<2)|1) -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ // When running with the analyzer make these real function calls, that are // easier to detect in the analyzer @@ -908,7 +906,7 @@ JL_DLLEXPORT void *jl_gc_managed_realloc(void *d, size_t sz, size_t oldsz, #define jl_svec_set_len_unsafe(t,n) (((jl_svec_t*)(t))->length=(n)) #define jl_svec_data(t) ((jl_value_t**)((char*)(t) + sizeof(jl_svec_t))) -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ STATIC_INLINE jl_value_t *jl_svecref(void *t JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT; STATIC_INLINE jl_value_t *jl_svecset( void *t JL_ROOTING_ARGUMENT JL_PROPAGATES_ROOT, @@ -920,7 +918,7 @@ STATIC_INLINE jl_value_t *jl_svecref(void *t JL_PROPAGATES_ROOT, size_t i) JL_NO assert(i < jl_svec_len(t)); // while svec is supposedly immutable, in practice we sometimes publish it first // and set the values lazily - return jl_atomic_load_relaxed(jl_svec_data(t) + i); + return jl_atomic_load_relaxed((_Atomic(jl_value_t*)*)jl_svec_data(t) + i); } STATIC_INLINE jl_value_t *jl_svecset( void *t JL_ROOTING_ARGUMENT JL_PROPAGATES_ROOT, @@ -952,7 +950,7 @@ JL_DLLEXPORT size_t jl_array_len_(jl_array_t *a); JL_DLLEXPORT char *jl_array_typetagdata(jl_array_t *a) JL_NOTSAFEPOINT; -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ jl_value_t **jl_array_ptr_data(jl_array_t *a JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; STATIC_INLINE jl_value_t *jl_array_ptr_ref(void *a JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT; STATIC_INLINE jl_value_t *jl_array_ptr_set( @@ -964,7 +962,7 @@ STATIC_INLINE jl_value_t *jl_array_ptr_ref(void *a JL_PROPAGATES_ROOT, size_t i) { assert(((jl_array_t*)a)->flags.ptrarray); assert(i < jl_array_len(a)); - return jl_atomic_load_relaxed(((jl_value_t**)(jl_array_data(a))) + i); + return jl_atomic_load_relaxed(((_Atomic(jl_value_t*)*)(jl_array_data(a))) + i); } STATIC_INLINE jl_value_t *jl_array_ptr_set( void *a JL_ROOTING_ARGUMENT, size_t i, @@ -972,7 +970,7 @@ STATIC_INLINE jl_value_t *jl_array_ptr_set( { assert(((jl_array_t*)a)->flags.ptrarray); assert(i < jl_array_len(a)); - jl_atomic_store_relaxed(((jl_value_t**)(jl_array_data(a))) + i, (jl_value_t*)x); + jl_atomic_store_relaxed(((_Atomic(jl_value_t*)*)(jl_array_data(a))) + i, (jl_value_t*)x); if (x) { if (((jl_array_t*)a)->flags.how == 3) { a = jl_array_data_owner(a); @@ -1428,7 +1426,7 @@ JL_DLLEXPORT jl_sym_t *jl_tagged_gensym(const char *str, size_t len); JL_DLLEXPORT jl_sym_t *jl_get_root_symbol(void); JL_DLLEXPORT jl_value_t *jl_generic_function_def(jl_sym_t *name, jl_module_t *module, - jl_value_t **bp, jl_value_t *bp_owner, + _Atomic(jl_value_t*) *bp, jl_value_t *bp_owner, jl_binding_t *bnd); JL_DLLEXPORT jl_method_t *jl_method_def(jl_svec_t *argdata, jl_methtable_t *mt, jl_code_info_t *f, jl_module_t *module); JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo); @@ -1836,13 +1834,13 @@ typedef struct _jl_task_t { uint64_t rngState1; uint64_t rngState2; uint64_t rngState3; - uint8_t _state; + _Atomic(uint8_t) _state; uint8_t sticky; // record whether this Task can be migrated to a new thread - uint8_t _isexception; // set if `result` is an exception to throw or that we exited with + _Atomic(uint8_t) _isexception; // set if `result` is an exception to throw or that we exited with // hidden state: // id of owning thread - does not need to be defined until the task runs - int16_t tid; + _Atomic(int16_t) tid; // multiqueue priority int16_t prio; // saved gc stack top for context switches @@ -1887,7 +1885,7 @@ JL_DLLEXPORT void JL_NORETURN jl_no_exc_handler(jl_value_t *e); JL_DLLEXPORT JL_CONST_FUNC jl_gcframe_t **(jl_get_pgcstack)(void) JL_GLOBALLY_ROOTED JL_NOTSAFEPOINT; #define jl_current_task (container_of(jl_get_pgcstack(), jl_task_t, gcstack)) -#include "locks.h" // requires jl_task_t definition +#include "julia_locks.h" // requires jl_task_t definition JL_DLLEXPORT void jl_enter_handler(jl_handler_t *eh); JL_DLLEXPORT void jl_eh_restore_state(jl_handler_t *eh); @@ -1926,7 +1924,7 @@ void (jl_longjmp)(jmp_buf _Buf, int _Value); #endif -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ // This is hard. Ideally we'd teach the static analyzer about the extra control // flow edges. But for now, just hide this as best we can diff --git a/src/julia_atomics.h b/src/julia_atomics.h new file mode 100644 index 0000000000000..0f3d31176a0c3 --- /dev/null +++ b/src/julia_atomics.h @@ -0,0 +1,311 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#ifndef JL_ATOMICS_H +#define JL_ATOMICS_H + +#if defined(__i386__) && defined(__GNUC__) && !defined(__SSE2__) +# error Julia can only be built for architectures above Pentium 4. Pass -march=pentium4, or set MARCH=pentium4 and ensure that -march is not passed separately with an older architecture. +#endif + +// Low-level atomic operations +#ifdef __cplusplus +#include +using std::memory_order_relaxed; +using std::memory_order_consume; +using std::memory_order_acquire; +using std::memory_order_release; +using std::memory_order_acq_rel; +using std::memory_order_seq_cst; +using std::atomic_thread_fence; +using std::atomic_signal_fence; +using std::atomic_load; +using std::atomic_load_explicit; +using std::atomic_store; +using std::atomic_store_explicit; +using std::atomic_fetch_add; +using std::atomic_fetch_add_explicit; +using std::atomic_fetch_and; +using std::atomic_fetch_and_explicit; +using std::atomic_fetch_or; +using std::atomic_fetch_or_explicit; +using std::atomic_compare_exchange_strong; +using std::atomic_compare_exchange_strong_explicit; +using std::atomic_exchange; +using std::atomic_exchange_explicit; +extern "C" { +#define _Atomic(T) std::atomic +#else +#include +#endif +#include // for sig_atomic_t + +#if defined(_CPU_X86_64_) || defined(_CPU_X86_) +# include +#endif + +enum jl_memory_order { + jl_memory_order_unspecified = -2, + jl_memory_order_invalid = -1, + jl_memory_order_notatomic = 0, + jl_memory_order_unordered, + jl_memory_order_monotonic, + jl_memory_order_consume, + jl_memory_order_acquire, + jl_memory_order_release, + jl_memory_order_acq_rel, + jl_memory_order_seq_cst +}; + +/** + * Thread synchronization primitives: + * + * These roughly follows the c11/c++11 memory model and the act as memory + * barriers at both the compiler level and the hardware level. + * The only exception is the GC safepoint and GC state transitions for which + * we use only a compiler (signal) barrier and use the signal handler to do the + * synchronization in order to lower the mutator overhead as much as possible. + * + * We use the compiler intrinsics to implement a similar API to the c11/c++11 + * one instead of using it directly because, we need interoperability between + * code written in different languages. The current c++ standard (c++14) does + * not allow using c11 atomic functions or types and there's currently no + * guarantee that the two types are compatible (although most of them probably + * are). We also need to access these atomic variables from the LLVM JIT code + * which is very hard unless the layout of the object is fully specified. + */ +#define jl_fence() atomic_thread_fence(memory_order_seq_cst) +#define jl_fence_release() atomic_thread_fence(memory_order_release) +#define jl_signal_fence() atomic_signal_fence(memory_order_seq_cst) + +#ifdef __cplusplus +} +// implicit conversion wasn't correctly specified 2017, so many compilers get +// this wrong thus we include the correct definitions here (with implicit +// conversion), instead of using the macro version +template +T jl_atomic_load(std::atomic *ptr) +{ + return std::atomic_load(ptr); +} +template +T jl_atomic_load_explicit(std::atomic *ptr, std::memory_order order) +{ + return std::atomic_load_explicit(ptr, order); +} +#define jl_atomic_load_relaxed(ptr) jl_atomic_load_explicit(ptr, memory_order_relaxed) +#define jl_atomic_load_acquire(ptr) jl_atomic_load_explicit(ptr, memory_order_acquire) +template +void jl_atomic_store(std::atomic *ptr, S desired) +{ + std::atomic_store(ptr, desired); +} +template +void jl_atomic_store_explicit(std::atomic *ptr, S desired, std::memory_order order) +{ + std::atomic_store_explicit(ptr, desired, order); +} +#define jl_atomic_store_relaxed(ptr, val) jl_atomic_store_explicit(ptr, val, memory_order_relaxed) +#define jl_atomic_store_release(ptr, val) jl_atomic_store_explicit(ptr, val, memory_order_release) +template +T jl_atomic_fetch_add(std::atomic *ptr, S val) +{ + return std::atomic_fetch_add(ptr, val); +} +template +T jl_atomic_fetch_add_explicit(std::atomic *ptr, S val, std::memory_order order) +{ + return std::atomic_fetch_add_explicit(ptr, val, order); +} +#define jl_atomic_fetch_add_relaxed(ptr, val) jl_atomic_fetch_add_explicit(ptr, val, memory_order_relaxed) +template +T jl_atomic_fetch_and(std::atomic *ptr, S val) +{ + return std::atomic_fetch_and(ptr, val); +} +template +T jl_atomic_fetch_and_explicit(std::atomic *ptr, S val, std::memory_order order) +{ + return std::atomic_fetch_and_explicit(ptr, val, order); +} +#define jl_atomic_fetch_and_relaxed(ptr, val) jl_atomic_fetch_and_explicit(ptr, val, memory_order_relaxed) +template +T jl_atomic_fetch_or(std::atomic *ptr, S val) +{ + return std::atomic_fetch_or(ptr, val); +} +template +T jl_atomic_fetch_or_explicit(std::atomic *ptr, S val, std::memory_order order) +{ + return std::atomic_fetch_or_explicit(ptr, val, order); +} +#define jl_atomic_fetch_or_relaxed(ptr, val) jl_atomic_fetch_or_explicit(ptr, val, memory_order_relaxed) +template +bool jl_atomic_cmpswap(std::atomic *ptr, T *expected, S val) +{ + return std::atomic_compare_exchange_strong(ptr, expected, val); +} +template +bool jl_atomic_cmpswap_explicit(std::atomic *ptr, T *expected, S val, std::memory_order order) +{ + return std::atomic_compare_exchange_strong_explicit(ptr, expected, val, order, order); +} +#define jl_atomic_cmpswap_relaxed(ptr, val) jl_atomic_cmpswap_explicit(ptr, val, memory_order_relaxed) +template +T jl_atomic_exchange(std::atomic *ptr, S desired) +{ + return std::atomic_exchange(ptr, desired); +} +template +T jl_atomic_exchange_explicit(std::atomic *ptr, S desired, std::memory_order order) +{ + return std::atomic_exchange_explicit(ptr, desired, order); +} +#define jl_atomic_exchange_relaxed(ptr, val) jl_atomic_exchange_explicit(ptr, val, memory_order_relaxed) +extern "C" { +#else + +# define jl_atomic_fetch_add_relaxed(obj, arg) \ + atomic_fetch_add_explicit(obj, arg, memory_order_relaxed) +# define jl_atomic_fetch_add(obj, arg) \ + atomic_fetch_add(obj, arg) +# define jl_atomic_fetch_and_relaxed(obj, arg) \ + atomic_fetch_and_explicit(obj, arg, memory_order_relaxed) +# define jl_atomic_fetch_and(obj, arg) \ + atomic_fetch_and(obj, arg) +# define jl_atomic_fetch_or_relaxed(obj, arg) \ + atomic_fetch_or_explicit(obj, arg, __ATOMIC_RELAXED) +# define jl_atomic_fetch_or(obj, arg) \ + atomic_fetch_or(obj, arg) +# define jl_atomic_cmpswap(obj, expected, desired) \ + atomic_compare_exchange_strong(obj, expected, desired) +# define jl_atomic_cmpswap_relaxed(obj, expected, desired) \ + atomic_compare_exchange_strong_explicit(obj, expected, desired, memory_order_relaxed, memory_order_relaxed) +// TODO: Maybe add jl_atomic_cmpswap_weak for spin lock +# define jl_atomic_exchange(obj, desired) \ + atomic_exchange(obj, desired) +# define jl_atomic_exchange_relaxed(obj, desired) \ + atomic_exchange_explicit(obj, desired, memory_order_relaxed) +# define jl_atomic_store(obj, val) \ + atomic_store(obj, val) +# define jl_atomic_store_relaxed(obj, val) \ + atomic_store_explicit(obj, val, memory_order_relaxed) + +# if defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) || \ + !(defined(_CPU_X86_) || defined(_CPU_X86_64_)) +// ICC and Clang doesn't have this bug... +# define jl_atomic_store_release(obj, val) \ + atomic_store_explicit(obj, val, memory_order_release) +# else +// Workaround a GCC bug when using store with release order by using the +// stronger version instead. +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67458 +// fixed in https://gcc.gnu.org/git/?p=gcc.git&a=commit;h=d8c40eff56f69877b33c697ded756d50fde90c27 +# define jl_atomic_store_release(obj, val) do { \ + jl_signal_fence(); \ + atomic_store_explicit(obj, val, memory_order_release); \ + } while (0) +# endif +# define jl_atomic_load(obj) \ + atomic_load(obj) +# define jl_atomic_load_acquire(obj) \ + atomic_load_explicit(obj, memory_order_acquire) +#ifdef JL_TSAN_ENABLED +// For the sake of tsan, call these loads consume ordering since they will act +// as such on the processors we support while normally, the compiler would +// upgrade this to acquire ordering, which is strong (and slower) than we want. +# define jl_atomic_load_relaxed(obj) \ + atomic_load_explicit(obj, memory_order_consume) +#else +# define jl_atomic_load_relaxed(obj) \ + atomic_load_explicit(obj, memory_order_relaxed) +#endif +#endif + +#ifdef __clang_gcanalyzer__ +// for the purposes of the GC analyzer, we can turn these into non-atomic +// expressions with similar properties (for the sake of the analyzer, we don't +// care if it is an exact match for behavior) + +#undef _Atomic +#define _Atomic(T) T + +#undef jl_atomic_exchange +#undef jl_atomic_exchange_relaxed +#define jl_atomic_exchange(obj, desired) \ + (__extension__({ \ + __typeof__((obj)) p__analyzer__ = (obj); \ + __typeof__(*p__analyzer__) temp__analyzer__ = *p__analyzer__; \ + *p__analyzer__ = (desired); \ + temp__analyzer__; \ + })) +#define jl_atomic_exchange_relaxed jl_atomic_exchange + +#undef jl_atomic_cmpswap +#undef jl_atomic_cmpswap_relaxed +#define jl_atomic_cmpswap(obj, expected, desired) \ + (__extension__({ \ + __typeof__((obj)) p__analyzer__ = (obj); \ + __typeof__(*p__analyzer__) temp__analyzer__ = *p__analyzer__; \ + __typeof__((expected)) x__analyzer__ = (expected); \ + int eq__analyzer__ = memcmp(&temp__analyzer__, x__analyzer__, sizeof(temp__analyzer__)) == 0; \ + if (eq__analyzer__) \ + *p__analyzer__ = (desired); \ + else \ + *x__analyzer__ = temp__analyzer__; \ + eq__analyzer__; \ + })) +#define jl_atomic_cmpswap_relaxed jl_atomic_cmpswap + +#undef jl_atomic_store +#undef jl_atomic_store_release +#undef jl_atomic_store_relaxed +#define jl_atomic_store(obj, val) (*(obj) = (val)) +#define jl_atomic_store_release jl_atomic_store +#define jl_atomic_store_relaxed jl_atomic_store + +#undef jl_atomic_load +#undef jl_atomic_load_acquire +#undef jl_atomic_load_relaxed +#define jl_atomic_load(obj) (*(obj)) +#define jl_atomic_load_acquire jl_atomic_load +#define jl_atomic_load_relaxed jl_atomic_load + +#undef jl_atomic_fetch_add +#undef jl_atomic_fetch_and +#undef jl_atomic_fetch_or +#undef jl_atomic_fetch_add_relaxed +#undef jl_atomic_fetch_and_relaxed +#undef jl_atomic_fetch_or_relaxed +#define jl_atomic_fetch_add(obj, val) \ + (__extension__({ \ + __typeof__((obj)) p__analyzer__ = (obj); \ + __typeof__(*p__analyzer__) temp__analyzer__ = *p__analyzer__; \ + *(p__analyzer__) = temp__analyzer__ + (val); \ + temp__analyzer__; \ + })) +#define jl_atomic_fetch_and(obj, val) \ + (__extension__({ \ + __typeof__((obj)) p__analyzer__ = (obj); \ + __typeof__(*p__analyzer__) temp__analyzer__ = *p__analyzer__; \ + *(p__analyzer__) = temp__analyzer__ & (val); \ + temp__analyzer__; \ + })) +#define jl_atomic_fetch_or(obj, val) \ + (__extension__({ \ + __typeof__((obj)) p__analyzer__ = (obj); \ + __typeof__(*p__analyzer__) temp__analyzer__ = *p__analyzer__; \ + *(p__analyzer__) = temp__analyzer__ | (val); \ + temp__analyzer__; \ + })) +#define jl_atomic_fetch_add_relaxed jl_atomic_fetch_add +#define jl_atomic_fetch_and_relaxed jl_atomic_fetch_and +#define jl_atomic_fetch_or_relaxed jl_atomic_fetch_or + +#endif + + +#ifdef __cplusplus +} +#endif + +#endif // JL_ATOMICS_H diff --git a/src/julia_internal.h b/src/julia_internal.h index 2605da0cad312..fbded1926dfb6 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -4,7 +4,7 @@ #define JL_INTERNAL_H #include "options.h" -#include "locks.h" +#include "julia_locks.h" #include #if !defined(_WIN32) #include @@ -105,7 +105,7 @@ void __tsan_switch_to_fiber(void *fiber, unsigned flags); static uv_loop_t *const unused_uv_loop_arg = (uv_loop_t *)0xBAD10; extern jl_mutex_t jl_uv_mutex; -extern int jl_uv_n_waiters; +extern _Atomic(int) jl_uv_n_waiters; void JL_UV_LOCK(void); #define JL_UV_UNLOCK() JL_UNLOCK(&jl_uv_mutex) @@ -155,8 +155,8 @@ static inline uint64_t cycleclock(void) #include "timing.h" // Global *atomic* integers controlling *process-wide* measurement of compilation time. -extern uint8_t jl_measure_compile_time_enabled; -extern uint64_t jl_cumulative_compile_time; +extern _Atomic(uint8_t) jl_measure_compile_time_enabled; +extern _Atomic(uint64_t) jl_cumulative_compile_time; #ifdef _COMPILER_MICROSOFT_ # define jl_return_address() ((uintptr_t)_ReturnAddress()) @@ -184,14 +184,16 @@ STATIC_INLINE uint32_t jl_int32hash_fast(uint32_t a) static inline void memmove_refs(void **dstp, void *const *srcp, size_t n) JL_NOTSAFEPOINT { size_t i; + _Atomic(void*) *srcpa = (_Atomic(void*)*)srcp; + _Atomic(void*) *dstpa = (_Atomic(void*)*)dstp; if (dstp < srcp || dstp > srcp + n) { for (i = 0; i < n; i++) { - jl_atomic_store_relaxed(dstp + i, jl_atomic_load_relaxed(srcp + i)); + jl_atomic_store_relaxed(dstpa + i, jl_atomic_load_relaxed(srcpa + i)); } } else { for (i = 0; i < n; i++) { - jl_atomic_store_relaxed(dstp + n - i - 1, jl_atomic_load_relaxed(srcp + n - i - 1)); + jl_atomic_store_relaxed(dstpa + n - i - 1, jl_atomic_load_relaxed(srcpa + n - i - 1)); } } } @@ -217,7 +219,7 @@ extern jl_array_t *_jl_debug_method_invalidation JL_GLOBALLY_ROOTED; extern size_t jl_page_size; extern jl_function_t *jl_typeinf_func; extern size_t jl_typeinf_world; -extern jl_typemap_entry_t *call_cache[N_CALL_CACHE] JL_GLOBALLY_ROOTED; +extern _Atomic(jl_typemap_entry_t*) call_cache[N_CALL_CACHE] JL_GLOBALLY_ROOTED; extern jl_array_t *jl_all_methods JL_GLOBALLY_ROOTED; JL_DLLEXPORT extern int jl_lineno; @@ -719,7 +721,7 @@ STATIC_INLINE int jl_addr_is_safepoint(uintptr_t addr) uintptr_t safepoint_addr = (uintptr_t)jl_safepoint_pages; return addr >= safepoint_addr && addr < safepoint_addr + jl_page_size * 3; } -extern uint32_t jl_gc_running; +extern _Atomic(uint32_t) jl_gc_running; // All the functions are safe to be called from within a signal handler // provided that the thread will not be interrupted by another asynchronous // signal. @@ -766,13 +768,13 @@ typedef jl_gcframe_t ***(*jl_pgcstack_key_t)(void) JL_NOTSAFEPOINT; #endif void jl_pgcstack_getkey(jl_get_pgcstack_func **f, jl_pgcstack_key_t *k); -#if !defined(__clang_analyzer__) +#if !defined(__clang_gcanalyzer__) static inline void jl_set_gc_and_wait(void) { jl_task_t *ct = jl_current_task; // reading own gc state doesn't need atomic ops since no one else // should store to it. - int8_t state = ct->ptls->gc_state; + int8_t state = jl_atomic_load_relaxed(&ct->ptls->gc_state); jl_atomic_store_release(&ct->ptls->gc_state, JL_GC_STATE_WAITING); jl_safepoint_wait_gc(); jl_atomic_store_release(&ct->ptls->gc_state, state); @@ -798,7 +800,7 @@ void jl_get_function_id(void *native_code, jl_code_instance_t *ncode, // the first argument to jl_idtable_rehash is used to return a value // make sure it is rooted if it is used after the function returns JL_DLLEXPORT jl_array_t *jl_idtable_rehash(jl_array_t *a, size_t newsz); -jl_value_t **jl_table_peek_bp(jl_array_t *a, jl_value_t *key) JL_NOTSAFEPOINT; +_Atomic(jl_value_t*) *jl_table_peek_bp(jl_array_t *a, jl_value_t *key) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t*); @@ -979,7 +981,7 @@ size_t rec_backtrace_ctx(jl_bt_element_t *bt_data, size_t maxsize, bt_context_t size_t rec_backtrace_ctx_dwarf(jl_bt_element_t *bt_data, size_t maxsize, bt_context_t *ctx, jl_gcframe_t *pgcstack) JL_NOTSAFEPOINT; #endif JL_DLLEXPORT jl_value_t *jl_get_backtrace(void); -void jl_critical_error(int sig, bt_context_t *context); +void jl_critical_error(int sig, bt_context_t *context, jl_task_t *ct); JL_DLLEXPORT void jl_raise_debugger(void); int jl_getFunctionInfo(jl_frame_t **frames, uintptr_t pointer, int skipC, int noInline) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_gdblookup(void* ip) JL_NOTSAFEPOINT; @@ -1081,7 +1083,7 @@ extern void *jl_winsock_handle; void *jl_get_library_(const char *f_lib, int throw_err); #define jl_get_library(f_lib) jl_get_library_(f_lib, 1) -JL_DLLEXPORT void *jl_load_and_lookup(const char *f_lib, const char *f_name, void **hnd); +JL_DLLEXPORT void *jl_load_and_lookup(const char *f_lib, const char *f_name, _Atomic(void*) *hnd); JL_DLLEXPORT void *jl_lazy_load_and_lookup(jl_value_t *lib_val, const char *f_name); JL_DLLEXPORT jl_value_t *jl_get_cfunction_trampoline( jl_value_t *fobj, jl_datatype_t *result, htable_t *cache, jl_svec_t *fill, @@ -1229,11 +1231,11 @@ void jl_mach_gc_end(void); typedef uint_t (*smallintset_hash)(size_t val, jl_svec_t *data); typedef int (*smallintset_eq)(size_t val, const void *key, jl_svec_t *data, uint_t hv); ssize_t jl_smallintset_lookup(jl_array_t *cache, smallintset_eq eq, const void *key, jl_svec_t *data, uint_t hv); -void jl_smallintset_insert(jl_array_t **pcache, jl_value_t *parent, smallintset_hash hash, size_t val, jl_svec_t *data); +void jl_smallintset_insert(_Atomic(jl_array_t*) *pcache, jl_value_t *parent, smallintset_hash hash, size_t val, jl_svec_t *data); // -- typemap.c -- // -void jl_typemap_insert(jl_typemap_t **cache, jl_value_t *parent, +void jl_typemap_insert(_Atomic(jl_typemap_t*) *cache, jl_value_t *parent, jl_typemap_entry_t *newrec, int8_t offs); jl_typemap_entry_t *jl_typemap_alloc( jl_tupletype_t *type, jl_tupletype_t *simpletype, jl_svec_t *guardsigs, @@ -1415,7 +1417,7 @@ jl_sym_t *_jl_symbol(const char *str, size_t len) JL_NOTSAFEPOINT; #define JL_GCC_IGNORE_STOP #endif // _COMPILER_GCC_ -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ // Not a safepoint (so it dosn't free other values), but an artificial use. // Usually this is unnecessary because the analyzer can see all real uses, // but sometimes real uses are harder for the analyzer to see, or it may diff --git a/src/locks.h b/src/julia_locks.h similarity index 79% rename from src/locks.h rename to src/julia_locks.h index 0605cefbd1218..8da0fc8ac9537 100644 --- a/src/locks.h +++ b/src/julia_locks.h @@ -11,29 +11,27 @@ extern "C" { // Lock acquire and release primitives -// JL_LOCK and jl_mutex_lock are GC safe points while JL_LOCK_NOGC -// and jl_mutex_lock_nogc are not. +// JL_LOCK and jl_mutex_lock are GC safe points, use uv_mutex_t if that is not desired. // Always use JL_LOCK unless no one holding the lock can trigger a GC or GC -// safepoint. JL_LOCK_NOGC should only be needed for GC internal locks. +// safepoint. uv_mutex_t should only be needed for GC internal locks. // The JL_LOCK* and JL_UNLOCK* macros are no-op for non-threading build // while the jl_mutex_* functions are always locking and unlocking the locks. static inline void jl_mutex_wait(jl_mutex_t *lock, int safepoint) { - jl_thread_t self = jl_thread_self(); - jl_thread_t owner = jl_atomic_load_relaxed(&lock->owner); - jl_task_t *ct = jl_current_task; + jl_task_t *self = jl_current_task; + jl_task_t *owner = jl_atomic_load_relaxed(&lock->owner); if (owner == self) { lock->count++; return; } while (1) { - if (owner == 0 && jl_atomic_cmpswap(&lock->owner, &owner, self)) { + if (owner == NULL && jl_atomic_cmpswap(&lock->owner, &owner, self)) { lock->count = 1; return; } if (safepoint) { - jl_gc_safepoint_(ct->ptls); + jl_gc_safepoint_(self->ptls); } jl_cpu_pause(); owner = jl_atomic_load_relaxed(&lock->owner); @@ -42,7 +40,7 @@ static inline void jl_mutex_wait(jl_mutex_t *lock, int safepoint) static inline void jl_mutex_lock_nogc(jl_mutex_t *lock) JL_NOTSAFEPOINT { -#ifndef __clang_analyzer__ +#ifndef __clang_gcanalyzer__ // Hide this body from the analyzer, otherwise it complains that we're calling // a non-safepoint from this function. The 0 arguments guarantees that we do // not reach the safepoint, but the analyzer can't figure that out @@ -90,13 +88,13 @@ static inline void jl_mutex_lock(jl_mutex_t *lock) static inline int jl_mutex_trylock_nogc(jl_mutex_t *lock) { - jl_thread_t self = jl_thread_self(); - jl_thread_t owner = jl_atomic_load_acquire(&lock->owner); + jl_task_t *self = jl_current_task; + jl_task_t *owner = jl_atomic_load_acquire(&lock->owner); if (owner == self) { lock->count++; return 1; } - if (owner == 0 && jl_atomic_cmpswap(&lock->owner, &owner, self)) { + if (owner == NULL && jl_atomic_cmpswap(&lock->owner, &owner, self)) { lock->count = 1; return 1; } @@ -114,11 +112,11 @@ static inline int jl_mutex_trylock(jl_mutex_t *lock) } static inline void jl_mutex_unlock_nogc(jl_mutex_t *lock) JL_NOTSAFEPOINT { -#ifndef __clang_analyzer__ - assert(lock->owner == jl_thread_self() && +#ifndef __clang_gcanalyzer__ + assert(jl_atomic_load_relaxed(&lock->owner) == jl_current_task && "Unlocking a lock in a different thread."); if (--lock->count == 0) { - jl_atomic_store_release(&lock->owner, 0); + jl_atomic_store_release(&lock->owner, (jl_task_t*)NULL); jl_cpu_wake(); } #endif @@ -129,14 +127,14 @@ static inline void jl_mutex_unlock(jl_mutex_t *lock) jl_mutex_unlock_nogc(lock); jl_lock_frame_pop(); JL_SIGATOMIC_END(); - if (jl_gc_have_pending_finalizers) { + if (jl_atomic_load_relaxed(&jl_gc_have_pending_finalizers)) { jl_gc_run_pending_finalizers(jl_current_task); // may GC } } static inline void jl_mutex_init(jl_mutex_t *lock) JL_NOTSAFEPOINT { - lock->owner = 0; + jl_atomic_store_relaxed(&lock->owner, (jl_task_t*)NULL); lock->count = 0; } diff --git a/src/julia_threads.h b/src/julia_threads.h index d0d70f88c79db..57270832123c3 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -4,7 +4,7 @@ #ifndef JL_THREADS_H #define JL_THREADS_H -#include +#include "julia_atomics.h" // threading ------------------------------------------------------------------ #ifdef __cplusplus @@ -97,7 +97,7 @@ typedef pthread_t jl_thread_t; // Recursive spin lock typedef struct { - volatile jl_thread_t owner; + _Atomic(struct _jl_task_t*) owner; uint32_t count; } jl_mutex_t; @@ -108,13 +108,13 @@ typedef struct { } jl_gc_pool_t; typedef struct { - int64_t allocd; - int64_t freed; - uint64_t malloc; - uint64_t realloc; - uint64_t poolalloc; - uint64_t bigalloc; - uint64_t freecall; + _Atomic(int64_t) allocd; + _Atomic(int64_t) freed; + _Atomic(uint64_t) malloc; + _Atomic(uint64_t) realloc; + _Atomic(uint64_t) poolalloc; + _Atomic(uint64_t) bigalloc; + _Atomic(uint64_t) freecall; } jl_thread_gc_num_t; typedef struct { @@ -194,7 +194,7 @@ typedef struct _jl_tls_states_t { int16_t tid; uint64_t rngseed; volatile size_t *safepoint; - int8_t sleep_check_state; // read/write from foreign threads + _Atomic(int8_t) sleep_check_state; // read/write from foreign threads // Whether it is safe to execute GC at the same time. #define JL_GC_STATE_WAITING 1 // gc_state = 1 means the thread is doing GC or is waiting for the GC to @@ -202,7 +202,7 @@ typedef struct _jl_tls_states_t { #define JL_GC_STATE_SAFE 2 // gc_state = 2 means the thread is running unmanaged code that can be // execute at the same time with the GC. - int8_t gc_state; // read from foreign threads + _Atomic(int8_t) gc_state; // read from foreign threads // execution of certain certain impure // statements is prohibited from certain // callbacks (such as generated functions) @@ -217,7 +217,7 @@ typedef struct _jl_tls_states_t { uv_mutex_t sleep_lock; uv_cond_t wake_signal; volatile sig_atomic_t defer_signal; - struct _jl_task_t *current_task; + _Atomic(struct _jl_task_t*) current_task; struct _jl_task_t *next_task; struct _jl_task_t *previous_task; struct _jl_task_t *root_task; @@ -239,7 +239,7 @@ typedef struct _jl_tls_states_t { struct _jl_bt_element_t *bt_data; // JL_MAX_BT_SIZE + 1 elements long size_t bt_size; // Size for backtrace in transit in bt_data // Atomically set by the sender, reset by the handler. - volatile sig_atomic_t signal_request; + volatile _Atomic(sig_atomic_t) signal_request; // TODO: no actual reason for this to be _Atomic // Allow the sigint to be raised asynchronously // this is limited to the few places we do synchronous IO // we can make this more general (similar to defer_signal) if necessary @@ -293,7 +293,7 @@ typedef jl_tls_states_t *jl_ptls_t; JL_DLLEXPORT void (jl_cpu_pause)(void); JL_DLLEXPORT void (jl_cpu_wake)(void); -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ // Note that the sigint safepoint can also trigger GC, albeit less likely void jl_gc_safepoint_(jl_ptls_t tls); void jl_sigint_safepoint(jl_ptls_t tls); @@ -328,9 +328,9 @@ STATIC_INLINE int8_t jl_gc_state_set(jl_ptls_t ptls, int8_t state, STATIC_INLINE int8_t jl_gc_state_save_and_set(jl_ptls_t ptls, int8_t state) { - return jl_gc_state_set(ptls, state, ptls->gc_state); + return jl_gc_state_set(ptls, state, jl_atomic_load_relaxed(&ptls->gc_state)); } -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ int8_t jl_gc_unsafe_enter(jl_ptls_t ptls); // Can be a safepoint int8_t jl_gc_unsafe_leave(jl_ptls_t ptls, int8_t state) JL_NOTSAFEPOINT; int8_t jl_gc_safe_enter(jl_ptls_t ptls) JL_NOTSAFEPOINT; @@ -347,7 +347,7 @@ JL_DLLEXPORT void jl_gc_enable_finalizers(struct _jl_task_t *ct, int on); JL_DLLEXPORT void jl_gc_disable_finalizers_internal(void); JL_DLLEXPORT void jl_gc_enable_finalizers_internal(void); JL_DLLEXPORT void jl_gc_run_pending_finalizers(struct _jl_task_t *ct); -extern JL_DLLEXPORT int jl_gc_have_pending_finalizers; +extern JL_DLLEXPORT _Atomic(int) jl_gc_have_pending_finalizers; JL_DLLEXPORT void jl_wakeup_thread(int16_t tid); diff --git a/src/llvm-version.h b/src/llvm-version.h index f59f7826c334d..fd7b47fc9461e 100644 --- a/src/llvm-version.h +++ b/src/llvm-version.h @@ -21,7 +21,9 @@ #define LLVM_ENABLE_STATS 0 #endif +#ifdef __cplusplus #if defined(__GNUC__) && (__GNUC__ >= 9) // Added in GCC 9, this warning is annoying #pragma GCC diagnostic ignored "-Winit-list-lifetime" #endif +#endif diff --git a/src/method.c b/src/method.c index 22145a4349853..80588897fe6e9 100644 --- a/src/method.c +++ b/src/method.c @@ -658,8 +658,8 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t *module) jl_task_t *ct = jl_current_task; jl_method_t *m = (jl_method_t*)jl_gc_alloc(ct->ptls, sizeof(jl_method_t), jl_method_type); - m->specializations = jl_emptysvec; - m->speckeyset = (jl_array_t*)jl_an_empty_vec_any; + jl_atomic_store_relaxed(&m->specializations, jl_emptysvec); + jl_atomic_store_relaxed(&m->speckeyset, (jl_array_t*)jl_an_empty_vec_any); m->sig = NULL; m->slot_syms = NULL; m->roots = NULL; @@ -675,7 +675,7 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t *module) m->called = 0xff; m->nospecialize = module->nospecialize; m->nkw = 0; - m->invokes = NULL; + jl_atomic_store_relaxed(&m->invokes, NULL); m->recursion_relation = NULL; m->isva = 0; m->nargs = 0; @@ -718,7 +718,8 @@ jl_method_t *jl_make_opaque_closure_method(jl_module_t *module, jl_value_t *name // empty generic function def JL_DLLEXPORT jl_value_t *jl_generic_function_def(jl_sym_t *name, jl_module_t *module, - jl_value_t **bp, jl_value_t *bp_owner, + _Atomic(jl_value_t*) *bp, + jl_value_t *bp_owner, jl_binding_t *bnd) { jl_value_t *gf = NULL; @@ -726,16 +727,16 @@ JL_DLLEXPORT jl_value_t *jl_generic_function_def(jl_sym_t *name, assert(name && bp); if (bnd && bnd->value != NULL && !bnd->constp) jl_errorf("cannot define function %s; it already has a value", jl_symbol_name(bnd->name)); - if (*bp != NULL) { - gf = *bp; + gf = jl_atomic_load_relaxed(bp); + if (gf != NULL) { if (!jl_is_datatype_singleton((jl_datatype_t*)jl_typeof(gf)) && !jl_is_type(gf)) jl_errorf("cannot define function %s; it already has a value", jl_symbol_name(name)); } if (bnd) bnd->constp = 1; - if (*bp == NULL) { + if (gf == NULL) { gf = (jl_value_t*)jl_new_generic_function(name, module); - *bp = gf; + jl_atomic_store(bp, gf); // TODO: fix constp assignment data race if (bp_owner) jl_gc_wb(bp_owner, gf); } return gf; diff --git a/src/module.c b/src/module.c index eee3bc8137ed0..fcd4b29e63a5b 100644 --- a/src/module.c +++ b/src/module.c @@ -181,7 +181,7 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, // Hash tables don't generically root their contents, but they do for bindings. // Express this to the analyzer. // NOTE: Must hold m->lock while calling these. -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ jl_binding_t *_jl_get_module_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var) JL_NOTSAFEPOINT; jl_binding_t **_jl_get_module_binding_bp(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var) JL_NOTSAFEPOINT; #else @@ -257,7 +257,7 @@ static jl_binding_t *jl_get_binding_(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t static inline jl_module_t *module_usings_getidx(jl_module_t *m JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT; -#ifndef __clang_analyzer__ +#ifndef __clang_gcanalyzer__ // The analyzer doesn't like looking through the arraylist, so just model the // access for it using this function static inline jl_module_t *module_usings_getidx(jl_module_t *m JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT { @@ -375,12 +375,17 @@ JL_DLLEXPORT jl_value_t *jl_module_globalref(jl_module_t *m, jl_sym_t *var) JL_UNLOCK(&m->lock); return jl_new_struct(jl_globalref_type, m, var); } - if (b->globalref == NULL) { - b->globalref = jl_new_struct(jl_globalref_type, m, var); - jl_gc_wb(m, b->globalref); + jl_value_t *globalref = jl_atomic_load_relaxed(&b->globalref); + if (globalref == NULL) { + jl_value_t *newref = jl_new_struct(jl_globalref_type, m, var); + if (jl_atomic_cmpswap_relaxed(&b->globalref, &globalref, newref)) { + JL_GC_PROMISE_ROOTED(newref); + globalref = newref; + jl_gc_wb(m, globalref); + } } - JL_UNLOCK(&m->lock); - return b->globalref; + JL_UNLOCK(&m->lock); // may GC + return globalref; } static int eq_bindings(jl_binding_t *a, jl_binding_t *b) @@ -635,7 +640,8 @@ JL_DLLEXPORT void jl_set_const(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var jl_binding_t *bp = jl_get_binding_wr(m, var, 1); if (bp->value == NULL) { uint8_t constp = 0; - if (jl_atomic_cmpswap(&bp->constp, &constp, 1)) { + // if (jl_atomic_cmpswap(&bp->constp, &constp, 1)) { + if (constp = bp->constp, bp->constp = 1, constp == 0) { jl_value_t *old = NULL; if (jl_atomic_cmpswap(&bp->value, &old, val)) { jl_gc_wb_binding(bp, val); @@ -769,7 +775,7 @@ JL_DLLEXPORT void jl_checked_assignment(jl_binding_t *b, jl_value_t *rhs) JL_NOT if (jl_egal(rhs, old)) return; if (jl_typeof(rhs) != jl_typeof(old) || jl_is_type(rhs) || jl_is_module(rhs)) { -#ifndef __clang_analyzer__ +#ifndef __clang_gcanalyzer__ jl_errorf("invalid redefinition of constant %s", jl_symbol_name(b->name)); #endif @@ -820,9 +826,10 @@ JL_DLLEXPORT jl_value_t *jl_module_names(jl_module_t *m, int all, int imported) (imported && b->imported) || (b->owner == m && !b->imported && (all || m == jl_main_module))) && (all || (!b->deprecated && !hidden))) { + jl_sym_t *in_module_name = (jl_sym_t*)table[i-1]; // the name in the module may not be b->name, use the httable key instead jl_array_grow_end(a, 1); //XXX: change to jl_arrayset if array storage allocation for Array{Symbols,1} changes: - jl_array_ptr_set(a, jl_array_dim0(a)-1, (jl_value_t*)b->name); + jl_array_ptr_set(a, jl_array_dim0(a)-1, (jl_value_t*)in_module_name); } } } diff --git a/src/partr.c b/src/partr.c index da43ea53a4e87..a3f2dd43165f7 100644 --- a/src/partr.c +++ b/src/partr.c @@ -57,10 +57,10 @@ extern int jl_gc_mark_queue_obj_explicit(jl_gc_mark_cache_t *gc_cache, /* a task heap */ typedef struct taskheap_tag { - jl_mutex_t lock; + uv_mutex_t lock; jl_task_t **tasks; - int32_t ntasks; - int16_t prio; + _Atomic(int32_t) ntasks; + _Atomic(int16_t) prio; } taskheap_t; /* multiqueue parameters */ @@ -83,10 +83,10 @@ static inline void multiq_init(void) heap_p = heap_c * jl_n_threads; heaps = (taskheap_t *)calloc(heap_p, sizeof(taskheap_t)); for (int32_t i = 0; i < heap_p; ++i) { - jl_mutex_init(&heaps[i].lock); + uv_mutex_init(&heaps[i].lock); heaps[i].tasks = (jl_task_t **)calloc(tasks_per_heap, sizeof(jl_task_t*)); - heaps[i].ntasks = 0; - heaps[i].prio = INT16_MAX; + jl_atomic_store_relaxed(&heaps[i].ntasks, 0); + jl_atomic_store_relaxed(&heaps[i].prio, INT16_MAX); } unbias_cong(heap_p, &cong_unbias); } @@ -108,12 +108,12 @@ static inline void sift_up(taskheap_t *heap, int32_t idx) static inline void sift_down(taskheap_t *heap, int32_t idx) { - if (idx < heap->ntasks) { + if (idx < jl_atomic_load_relaxed(&heap->ntasks)) { for (int32_t child = heap_d*idx + 1; child < tasks_per_heap && child <= heap_d*idx + heap_d; ++child) { if (heap->tasks[child] - && heap->tasks[child]->prio < heap->tasks[idx]->prio) { + && heap->tasks[child]->prio < heap->tasks[idx]->prio) { jl_task_t *t = heap->tasks[idx]; heap->tasks[idx] = heap->tasks[child]; heap->tasks[child] = t; @@ -132,20 +132,22 @@ static inline int multiq_insert(jl_task_t *task, int16_t priority) task->prio = priority; do { rn = cong(heap_p, cong_unbias, &ptls->rngseed); - } while (!jl_mutex_trylock_nogc(&heaps[rn].lock)); + } while (uv_mutex_trylock(&heaps[rn].lock) != 0); - if (heaps[rn].ntasks >= tasks_per_heap) { - jl_mutex_unlock_nogc(&heaps[rn].lock); + if (jl_atomic_load_relaxed(&heaps[rn].ntasks) >= tasks_per_heap) { + uv_mutex_unlock(&heaps[rn].lock); // multiq insertion failed, increase #tasks per heap return -1; } - heaps[rn].tasks[heaps[rn].ntasks++] = task; - sift_up(&heaps[rn], heaps[rn].ntasks-1); - int16_t prio = jl_atomic_load(&heaps[rn].prio); + int32_t ntasks = jl_atomic_load_relaxed(&heaps[rn].ntasks); + jl_atomic_store_relaxed(&heaps[rn].ntasks, ntasks + 1); + heaps[rn].tasks[ntasks] = task; + sift_up(&heaps[rn], ntasks); + int16_t prio = jl_atomic_load_relaxed(&heaps[rn].prio); if (task->prio < prio) - jl_atomic_store(&heaps[rn].prio, task->prio); - jl_mutex_unlock_nogc(&heaps[rn].lock); + jl_atomic_store_relaxed(&heaps[rn].prio, task->prio); + uv_mutex_unlock(&heaps[rn].lock); return 0; } @@ -163,18 +165,18 @@ static inline jl_task_t *multiq_deletemin(void) for (i = 0; i < heap_p; ++i) { rn1 = cong(heap_p, cong_unbias, &ptls->rngseed); rn2 = cong(heap_p, cong_unbias, &ptls->rngseed); - prio1 = jl_atomic_load(&heaps[rn1].prio); - prio2 = jl_atomic_load(&heaps[rn2].prio); + prio1 = jl_atomic_load_relaxed(&heaps[rn1].prio); + prio2 = jl_atomic_load_relaxed(&heaps[rn2].prio); if (prio1 > prio2) { prio1 = prio2; rn1 = rn2; } else if (prio1 == prio2 && prio1 == INT16_MAX) continue; - if (jl_mutex_trylock_nogc(&heaps[rn1].lock)) { - if (prio1 == heaps[rn1].prio) + if (uv_mutex_trylock(&heaps[rn1].lock) == 0) { + if (prio1 == jl_atomic_load_relaxed(&heaps[rn1].prio)) break; - jl_mutex_unlock_nogc(&heaps[rn1].lock); + uv_mutex_unlock(&heaps[rn1].lock); } } if (i == heap_p) @@ -182,18 +184,20 @@ static inline jl_task_t *multiq_deletemin(void) task = heaps[rn1].tasks[0]; if (!jl_set_task_tid(task, ptls->tid)) { - jl_mutex_unlock_nogc(&heaps[rn1].lock); + uv_mutex_unlock(&heaps[rn1].lock); goto retry; } - heaps[rn1].tasks[0] = heaps[rn1].tasks[--heaps[rn1].ntasks]; - heaps[rn1].tasks[heaps[rn1].ntasks] = NULL; + int32_t ntasks = jl_atomic_load_relaxed(&heaps[rn1].ntasks) - 1; + jl_atomic_store_relaxed(&heaps[rn1].ntasks, ntasks); + heaps[rn1].tasks[0] = heaps[rn1].tasks[ntasks]; + heaps[rn1].tasks[ntasks] = NULL; prio1 = INT16_MAX; - if (heaps[rn1].ntasks > 0) { + if (ntasks > 0) { sift_down(&heaps[rn1], 0); prio1 = heaps[rn1].tasks[0]->prio; } - jl_atomic_store(&heaps[rn1].prio, prio1); - jl_mutex_unlock_nogc(&heaps[rn1].lock); + jl_atomic_store_relaxed(&heaps[rn1].prio, prio1); + uv_mutex_unlock(&heaps[rn1].lock); return task; } @@ -203,7 +207,7 @@ void jl_gc_mark_enqueued_tasks(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp { int32_t i, j; for (i = 0; i < heap_p; ++i) - for (j = 0; j < heaps[i].ntasks; ++j) + for (j = 0; j < jl_atomic_load_relaxed(&heaps[i].ntasks); ++j) jl_gc_mark_queue_obj_explicit(gc_cache, sp, (jl_value_t *)heaps[i].tasks[j]); } @@ -212,7 +216,7 @@ static int multiq_check_empty(void) { int32_t i; for (i = 0; i < heap_p; ++i) { - if (heaps[i].ntasks != 0) + if (jl_atomic_load_relaxed(&heaps[i].ntasks) != 0) return 0; } return 1; @@ -224,6 +228,7 @@ static int multiq_check_empty(void) // --- // initialize the threading infrastructure +// (used only by the main thread) void jl_init_threadinginfra(void) { /* initialize the synchronization trees pool and the multiqueue */ @@ -282,22 +287,20 @@ int jl_running_under_rr(int recheck) #ifdef _OS_LINUX_ #define RR_CALL_BASE 1000 #define SYS_rrcall_check_presence (RR_CALL_BASE + 8) - static int checked_running_under_rr = 0; - static int is_running_under_rr = 0; - if (!checked_running_under_rr || recheck) { + static _Atomic(int) is_running_under_rr = 0; + int rr = jl_atomic_load_relaxed(&is_running_under_rr); + if (rr == 0 || recheck) { int ret = syscall(SYS_rrcall_check_presence, 0, 0, 0, 0, 0, 0); - if (ret == -1) { + if (ret == -1) // Should always be ENOSYS, but who knows what people do for // unknown syscalls with their seccomp filters, so just say // that we don't have rr. - is_running_under_rr = 0; - } - else { - is_running_under_rr = 1; - } - checked_running_under_rr = 1; + rr = 2; + else + rr = 1; + jl_atomic_store_relaxed(&is_running_under_rr, rr); } - return is_running_under_rr; + return rr == 1; #else return 0; #endif @@ -332,7 +335,7 @@ static int sleep_check_after_threshold(uint64_t *start_cycles) static void wake_thread(int16_t tid) { jl_ptls_t other = jl_all_tls_states[tid]; - uint8_t state = sleeping; + int8_t state = sleeping; jl_atomic_cmpswap(&other->sleep_check_state, &state, not_sleeping); if (state == sleeping) { uv_mutex_lock(&other->sleep_lock); @@ -352,24 +355,24 @@ static void wake_libuv(void) /* ensure thread tid is awake if necessary */ JL_DLLEXPORT void jl_wakeup_thread(int16_t tid) { - jl_ptls_t ptls = jl_current_task->ptls; - jl_thread_t uvlock = jl_atomic_load(&jl_uv_mutex.owner); - int16_t self = ptls->tid; - jl_thread_t system_self = jl_all_tls_states[self]->system_id; + jl_task_t *ct = jl_current_task; + jl_ptls_t ptls = ct->ptls; + jl_task_t *uvlock = jl_atomic_load(&jl_uv_mutex.owner); + int16_t self = jl_atomic_load_relaxed(&ct->tid); JULIA_DEBUG_SLEEPWAKE( wakeup_enter = cycleclock() ); if (tid == self || tid == -1) { // we're already awake, but make sure we'll exit uv_run if (jl_atomic_load_relaxed(&ptls->sleep_check_state) == sleeping) jl_atomic_store(&ptls->sleep_check_state, not_sleeping); - if (uvlock == system_self) + if (uvlock == ct) uv_stop(jl_global_event_loop()); } else { // something added to the sticky-queue: notify that thread wake_thread(tid); // check if we need to notify uv_run too - jl_thread_t system_tid = jl_all_tls_states[tid]->system_id; - if (uvlock != system_self && jl_atomic_load(&jl_uv_mutex.owner) == system_tid) + jl_task_t *system_tid = jl_atomic_load_relaxed(&jl_all_tls_states[tid]->current_task); + if (uvlock != ct && jl_atomic_load(&jl_uv_mutex.owner) == system_tid) wake_libuv(); } // check if the other threads might be sleeping @@ -382,7 +385,7 @@ JL_DLLEXPORT void jl_wakeup_thread(int16_t tid) wake_thread(tid); } // check if we need to notify uv_run too - if (uvlock != system_self && jl_atomic_load(&jl_uv_mutex.owner) != 0) + if (uvlock != ct && jl_atomic_load(&jl_uv_mutex.owner) != NULL) wake_libuv(); } JULIA_DEBUG_SLEEPWAKE( wakeup_leave = cycleclock() ); @@ -396,14 +399,14 @@ static jl_task_t *get_next_task(jl_value_t *trypoptask, jl_value_t *q) jl_value_t *args[2] = { trypoptask, q }; jl_task_t *task = (jl_task_t*)jl_apply(args, 2); if (jl_typeis(task, jl_task_type)) { - int self = jl_current_task->tid; + int self = jl_atomic_load_relaxed(&jl_current_task->tid); jl_set_task_tid(task, self); return task; } return multiq_deletemin(); } -static int may_sleep(jl_ptls_t ptls) +static int may_sleep(jl_ptls_t ptls) JL_NOTSAFEPOINT { // sleep_check_state is only transitioned from not_sleeping to sleeping // by the thread itself. As a result, if this returns false, it will @@ -411,7 +414,7 @@ static int may_sleep(jl_ptls_t ptls) return jl_atomic_load_relaxed(&ptls->sleep_check_state) == sleeping; } -extern volatile unsigned _threadedregion; +extern _Atomic(unsigned) _threadedregion; JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q) { @@ -432,7 +435,7 @@ JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q) jl_cpu_pause(); jl_ptls_t ptls = ct->ptls; - if (sleep_check_after_threshold(&start_cycles) || (!_threadedregion && ptls->tid == 0)) { + if (sleep_check_after_threshold(&start_cycles) || (!jl_atomic_load_relaxed(&_threadedregion) && ptls->tid == 0)) { jl_atomic_store(&ptls->sleep_check_state, sleeping); // acquire sleep-check lock if (!multiq_check_empty()) { if (jl_atomic_load_relaxed(&ptls->sleep_check_state) != not_sleeping) @@ -441,7 +444,7 @@ JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q) } task = get_next_task(trypoptask, q); // WARNING: this should not yield if (ptls != ct->ptls) - continue; + continue; // oops, get_next_task did yield--start over if (task) { if (jl_atomic_load_relaxed(&ptls->sleep_check_state) != not_sleeping) jl_atomic_store(&ptls->sleep_check_state, not_sleeping); // let other threads know they don't need to wake us @@ -454,7 +457,7 @@ JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q) // outside of threaded regions, all IO is permitted, // but only on thread 1 int uvlock = 0; - if (_threadedregion) { + if (jl_atomic_load_relaxed(&_threadedregion)) { uvlock = jl_mutex_trylock(&jl_uv_mutex); } else if (ptls->tid == 0) { @@ -481,7 +484,7 @@ JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q) JL_UV_UNLOCK(); // optimization: check again first if we may have work to do if (!may_sleep(ptls)) { - assert(ptls->sleep_check_state == not_sleeping); + assert(jl_atomic_load_relaxed(&ptls->sleep_check_state) == not_sleeping); start_cycles = 0; continue; } @@ -496,7 +499,7 @@ JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q) continue; } } - if (!_threadedregion && active && ptls->tid == 0) { + if (!jl_atomic_load_relaxed(&_threadedregion) && active && ptls->tid == 0) { // thread 0 is the only thread permitted to run the event loop // so it needs to stay alive if (jl_atomic_load_relaxed(&ptls->sleep_check_state) != not_sleeping) @@ -514,7 +517,7 @@ JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q) uv_cond_wait(&ptls->wake_signal, &ptls->sleep_lock); // TODO: help with gc work here, if applicable } - assert(ptls->sleep_check_state == not_sleeping); + assert(jl_atomic_load_relaxed(&ptls->sleep_check_state) == not_sleeping); uv_mutex_unlock(&ptls->sleep_lock); JULIA_DEBUG_SLEEPWAKE( ptls->sleep_leave = cycleclock() ); jl_gc_safe_leave(ptls, gc_state); // contains jl_gc_safepoint diff --git a/src/rtutils.c b/src/rtutils.c index 67d17c39c67ec..99af741993c44 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -222,7 +222,7 @@ JL_DLLEXPORT void jl_enter_handler(jl_handler_t *eh) // Must have no safepoint eh->prev = ct->eh; eh->gcstack = ct->gcstack; - eh->gc_state = ct->ptls->gc_state; + eh->gc_state = jl_atomic_load_relaxed(&ct->ptls->gc_state); eh->locks_len = ct->ptls->locks.len; eh->defer_signal = ct->ptls->defer_signal; eh->world_age = ct->world_age; @@ -250,7 +250,7 @@ JL_DLLEXPORT void jl_eh_restore_state(jl_handler_t *eh) // This function should **NOT** have any safepoint before the ones at the // end. sig_atomic_t old_defer_signal = ct->ptls->defer_signal; - int8_t old_gc_state = ct->ptls->gc_state; + int8_t old_gc_state = jl_atomic_load_relaxed(&ct->ptls->gc_state); ct->eh = eh->prev; ct->gcstack = eh->gcstack; small_arraylist_t *locks = &ct->ptls->locks; diff --git a/src/runtime_ccall.cpp b/src/runtime_ccall.cpp index ba265eb67be76..ebbc9668bf6a3 100644 --- a/src/runtime_ccall.cpp +++ b/src/runtime_ccall.cpp @@ -54,7 +54,7 @@ void *jl_get_library_(const char *f_lib, int throw_err) } extern "C" JL_DLLEXPORT -void *jl_load_and_lookup(const char *f_lib, const char *f_name, void **hnd) +void *jl_load_and_lookup(const char *f_lib, const char *f_name, _Atomic(void*) *hnd) { void *handle = jl_atomic_load_acquire(hnd); if (!handle) diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index 741bb5448b847..00c110f7ce1c5 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -83,7 +83,7 @@ JL_DLLEXPORT jl_value_t *jl_atomic_pointerref(jl_value_t *p, jl_value_t *order) jl_value_t *ety = jl_tparam0(jl_typeof(p)); char *pp = (char*)jl_unbox_long(p); if (ety == (jl_value_t*)jl_any_type) { - return jl_atomic_load((jl_value_t**)pp); + return jl_atomic_load((_Atomic(jl_value_t*)*)pp); } else { if (!is_valid_intrinsic_elptr(ety)) @@ -103,7 +103,7 @@ JL_DLLEXPORT jl_value_t *jl_atomic_pointerset(jl_value_t *p, jl_value_t *x, jl_v jl_value_t *ety = jl_tparam0(jl_typeof(p)); char *pp = (char*)jl_unbox_long(p); if (ety == (jl_value_t*)jl_any_type) { - jl_atomic_store((jl_value_t**)pp, x); + jl_atomic_store((_Atomic(jl_value_t*)*)pp, x); } else { if (!is_valid_intrinsic_elptr(ety)) @@ -127,7 +127,7 @@ JL_DLLEXPORT jl_value_t *jl_atomic_pointerswap(jl_value_t *p, jl_value_t *x, jl_ jl_value_t *y; char *pp = (char*)jl_unbox_long(p); if (ety == (jl_value_t*)jl_any_type) { - y = jl_atomic_exchange((jl_value_t**)pp, x); + y = jl_atomic_exchange((_Atomic(jl_value_t*)*)pp, x); } else { if (!is_valid_intrinsic_elptr(ety)) @@ -151,7 +151,7 @@ JL_DLLEXPORT jl_value_t *jl_atomic_pointermodify(jl_value_t *p, jl_value_t *f, j char *pp = (char*)jl_unbox_long(p); jl_value_t *expected; if (ety == (jl_value_t*)jl_any_type) { - expected = jl_atomic_load((jl_value_t**)pp); + expected = jl_atomic_load((_Atomic(jl_value_t*)*)pp); } else { if (!is_valid_intrinsic_elptr(ety)) @@ -169,7 +169,7 @@ JL_DLLEXPORT jl_value_t *jl_atomic_pointermodify(jl_value_t *p, jl_value_t *f, j jl_value_t *y = jl_apply_generic(f, args, 2); args[1] = y; if (ety == (jl_value_t*)jl_any_type) { - if (jl_atomic_cmpswap((jl_value_t**)pp, &expected, y)) + if (jl_atomic_cmpswap((_Atomic(jl_value_t*)*)pp, &expected, y)) break; } else { @@ -215,7 +215,7 @@ JL_DLLEXPORT jl_value_t *jl_atomic_pointerreplace(jl_value_t *p, jl_value_t *exp result = expected; int success; while (1) { - success = jl_atomic_cmpswap((jl_value_t**)pp, &result, x); + success = jl_atomic_cmpswap((_Atomic(jl_value_t*)*)pp, &result, x); if (success || !jl_egal(result, expected)) break; } diff --git a/src/safepoint.c b/src/safepoint.c index d54c7c62bec56..dba2387fcde2e 100644 --- a/src/safepoint.c +++ b/src/safepoint.c @@ -19,7 +19,7 @@ extern "C" { // 1: at least one sigint is pending, only the sigint page is enabled. // 2: at least one sigint is pending, both safepoint pages are enabled. JL_DLLEXPORT sig_atomic_t jl_signal_pending = 0; -uint32_t jl_gc_running = 0; +_Atomic(uint32_t) jl_gc_running = 0; char *jl_safepoint_pages = NULL; // The number of safepoints enabled on the three pages. // The first page, is the SIGINT page, only used by the master thread. @@ -115,7 +115,7 @@ int jl_safepoint_start_gc(void) return 1; } // The thread should have set this already - assert(jl_current_task->ptls->gc_state == JL_GC_STATE_WAITING); + assert(jl_atomic_load_relaxed(&jl_current_task->ptls->gc_state) == JL_GC_STATE_WAITING); jl_mutex_lock_nogc(&safepoint_lock); // In case multiple threads enter the GC at the same time, only allow // one of them to actually run the collection. We can't just let the @@ -157,7 +157,7 @@ void jl_safepoint_end_gc(void) void jl_safepoint_wait_gc(void) { // The thread should have set this is already - assert(jl_current_task->ptls->gc_state != 0); + assert(jl_atomic_load_relaxed(&jl_current_task->ptls->gc_state) != 0); // Use normal volatile load in the loop for speed until GC finishes. // Then use an acquire load to make sure the GC result is visible on this thread. while (jl_atomic_load_relaxed(&jl_gc_running) || jl_atomic_load_acquire(&jl_gc_running)) { diff --git a/src/signal-handling.c b/src/signal-handling.c index 8011c62934d28..58fbdb06aefcd 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -231,19 +231,20 @@ void jl_show_sigill(void *_ctx) } // what to do on a critical error on a thread -void jl_critical_error(int sig, bt_context_t *context) +void jl_critical_error(int sig, bt_context_t *context, jl_task_t *ct) { - jl_task_t *ct = jl_current_task; - jl_bt_element_t *bt_data = ct->ptls->bt_data; - size_t *bt_size = &ct->ptls->bt_size; - size_t i, n = *bt_size; + jl_bt_element_t *bt_data = ct ? ct->ptls->bt_data : NULL; + size_t *bt_size = ct ? &ct->ptls->bt_size : NULL; + size_t i, n = ct ? *bt_size : 0; if (sig) { // kill this task, so that we cannot get back to it accidentally (via an untimely ^C or jlbacktrace in jl_exit) jl_set_safe_restore(NULL); - ct->gcstack = NULL; - ct->eh = NULL; - ct->excstack = NULL; + if (ct) { + ct->gcstack = NULL; + ct->eh = NULL; + ct->excstack = NULL; + } #ifndef _OS_WINDOWS_ sigset_t sset; sigemptyset(&sset); @@ -267,7 +268,7 @@ void jl_critical_error(int sig, bt_context_t *context) jl_safe_printf("\nsignal (%d): %s\n", sig, strsignal(sig)); } jl_safe_printf("in expression starting at %s:%d\n", jl_filename, jl_lineno); - if (context) { + if (context && ct) { // Must avoid extended backtrace frames here unless we're sure bt_data // is properly rooted. *bt_size = n = rec_backtrace_ctx(bt_data, JL_MAX_BT_SIZE, context, NULL); diff --git a/src/signals-mach.c b/src/signals-mach.c index 3f133c3189b10..93da46f7cef1e 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -402,7 +402,7 @@ static void jl_try_deliver_sigint(void) static void JL_NORETURN jl_exit_thread0_cb(int exitstate) { CFI_NORETURN - jl_critical_error(exitstate - 128, NULL); + jl_critical_error(exitstate - 128, NULL, jl_current_task); jl_exit(exitstate); } diff --git a/src/signals-unix.c b/src/signals-unix.c index bb19e2bd65d78..617b6114da7ee 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -227,7 +227,7 @@ static void sigdie_handler(int sig, siginfo_t *info, void *context) uv_tty_reset_mode(); if (sig == SIGILL) jl_show_sigill(context); - jl_critical_error(sig, jl_to_bt_context(context)); + jl_critical_error(sig, jl_to_bt_context(context), jl_get_current_task()); if (sig != SIGSEGV && sig != SIGBUS && sig != SIGILL) { @@ -406,7 +406,7 @@ CFI_NORETURN // (unavoidable due to its async nature). // Try harder to exit each time if we get multiple exit requests. if (thread0_exit_count <= 1) { - jl_critical_error(thread0_exit_state - 128, NULL); + jl_critical_error(thread0_exit_state - 128, NULL, jl_current_task); jl_exit(thread0_exit_state); } else if (thread0_exit_count == 2) { @@ -448,6 +448,8 @@ void usr2_handler(int sig, siginfo_t *info, void *ctx) if (ct == NULL) return; jl_ptls_t ptls = ct->ptls; + if (ptls == NULL) + return; int errno_save = errno; sig_atomic_t request = jl_atomic_exchange(&ptls->signal_request, 0); #if !defined(JL_DISABLE_LIBUNWIND) @@ -743,53 +745,68 @@ static void *signal_listener(void *arg) unw_context_t *signal_context; // sample each thread, round-robin style in reverse order // (so that thread zero gets notified last) - if (critical || profile) + if (critical || profile) { jl_lock_profile(); - for (int i = jl_n_threads; i-- > 0; ) { - // notify thread to stop - jl_thread_suspend_and_get_state(i, &signal_context); - - // do backtrace on thread contexts for critical signals - // this part must be signal-handler safe - if (critical) { - bt_size += rec_backtrace_ctx(bt_data + bt_size, - JL_MAX_BT_SIZE / jl_n_threads - 1, - signal_context, NULL); - bt_data[bt_size++].uintptr = 0; - } - - // do backtrace for profiler - if (profile && running) { - if (jl_profile_is_buffer_full()) { - // Buffer full: Delete the timer - jl_profile_stop_timer(); + for (int i = jl_n_threads; i-- > 0; ) { + // notify thread to stop + jl_thread_suspend_and_get_state(i, &signal_context); + + // do backtrace on thread contexts for critical signals + // this part must be signal-handler safe + if (critical) { + bt_size += rec_backtrace_ctx(bt_data + bt_size, + JL_MAX_BT_SIZE / jl_n_threads - 1, + signal_context, NULL); + bt_data[bt_size++].uintptr = 0; } - else { - // unwinding can fail, so keep track of the current state - // and restore from the SEGV handler if anything happens. - jl_jmp_buf *old_buf = jl_get_safe_restore(); - jl_jmp_buf buf; - - jl_set_safe_restore(&buf); - if (jl_setjmp(buf, 0)) { - jl_safe_printf("WARNING: profiler attempt to access an invalid memory location\n"); - } else { - // Get backtrace data - bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, - bt_size_max - bt_size_cur - 1, signal_context, NULL); + + // do backtrace for profiler + if (profile && running) { + if (jl_profile_is_buffer_full()) { + // Buffer full: Delete the timer + jl_profile_stop_timer(); + } + else { + // unwinding can fail, so keep track of the current state + // and restore from the SEGV handler if anything happens. + jl_jmp_buf *old_buf = jl_get_safe_restore(); + jl_jmp_buf buf; + + jl_set_safe_restore(&buf); + if (jl_setjmp(buf, 0)) { + jl_safe_printf("WARNING: profiler attempt to access an invalid memory location\n"); + } else { + // Get backtrace data + bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, + bt_size_max - bt_size_cur - 1, signal_context, NULL); + } + jl_set_safe_restore(old_buf); + + jl_ptls_t ptls = jl_all_tls_states[i]; + + // store threadid but add 1 as 0 is preserved to indicate end of block + bt_data_prof[bt_size_cur++].uintptr = ptls->tid + 1; + + // store task id + bt_data_prof[bt_size_cur++].jlvalue = (jl_value_t*)ptls->current_task; + + // store cpu cycle clock + bt_data_prof[bt_size_cur++].uintptr = cycleclock(); + + // store whether thread is sleeping but add 1 as 0 is preserved to indicate end of block + bt_data_prof[bt_size_cur++].uintptr = ptls->sleep_check_state + 1; + + // Mark the end of this block with two 0's + bt_data_prof[bt_size_cur++].uintptr = 0; + bt_data_prof[bt_size_cur++].uintptr = 0; } - jl_set_safe_restore(old_buf); - // Mark the end of this block with 0 - bt_data_prof[bt_size_cur++].uintptr = 0; + // notify thread to resume + jl_thread_resume(i, sig); } } - - // notify thread to resume - jl_thread_resume(i, sig); - } - if (critical || profile) jl_unlock_profile(); + } #ifndef HAVE_MACH if (profile && running) { #if defined(HAVE_TIMER) diff --git a/src/signals-win.c b/src/signals-win.c index 5d1a963078df4..2dedc7b2baf14 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -59,7 +59,6 @@ static void jl_try_throw_sigint(void) void __cdecl crt_sig_handler(int sig, int num) { - jl_task_t *ct = jl_current_task; CONTEXT Context; switch (sig) { case SIGFPE: @@ -92,7 +91,7 @@ void __cdecl crt_sig_handler(int sig, int num) RtlCaptureContext(&Context); if (sig == SIGILL) jl_show_sigill(&Context); - jl_critical_error(sig, &Context); + jl_critical_error(sig, &Context, jl_get_current_task()); raise(sig); } } @@ -226,7 +225,8 @@ static BOOL WINAPI sigint_handler(DWORD wsig) //This needs winapi types to guara LONG WINAPI jl_exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo) { - jl_ptls_t ptls = jl_current_task->ptls; + jl_task_t *ct = jl_current_task; + jl_ptls_t ptls = ct->ptls; if (ExceptionInfo->ExceptionRecord->ExceptionFlags == 0) { switch (ExceptionInfo->ExceptionRecord->ExceptionCode) { case EXCEPTION_INT_DIVIDE_BY_ZERO: @@ -313,7 +313,7 @@ LONG WINAPI jl_exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo) jl_safe_printf(" at 0x%Ix -- ", (size_t)ExceptionInfo->ExceptionRecord->ExceptionAddress); jl_print_native_codeloc((uintptr_t)ExceptionInfo->ExceptionRecord->ExceptionAddress); - jl_critical_error(0, ExceptionInfo->ContextRecord); + jl_critical_error(0, ExceptionInfo->ContextRecord, ct); static int recursion = 0; if (recursion++) exit(1); diff --git a/src/smallintset.c b/src/smallintset.c index 7598d8fd85ce4..54fdad616a758 100644 --- a/src/smallintset.c +++ b/src/smallintset.c @@ -130,14 +130,16 @@ static int smallintset_insert_(jl_array_t *a, uint_t hv, size_t val1) return 0; } -static void smallintset_rehash(jl_array_t **cache, jl_value_t *parent, smallintset_hash hash, jl_svec_t *data, size_t newsz, size_t np); +static void smallintset_rehash(_Atomic(jl_array_t*) *pcache, jl_value_t *parent, smallintset_hash hash, jl_svec_t *data, size_t newsz, size_t np); -void jl_smallintset_insert(jl_array_t **cache, jl_value_t *parent, smallintset_hash hash, size_t val, jl_svec_t *data) +void jl_smallintset_insert(_Atomic(jl_array_t*) *pcache, jl_value_t *parent, smallintset_hash hash, size_t val, jl_svec_t *data) { - if (val + 1 > jl_max_int(*cache)) - smallintset_rehash(cache, parent, hash, data, jl_array_len(*cache), val + 1); + jl_array_t *a = jl_atomic_load_relaxed(pcache); + if (val + 1 > jl_max_int(a)) + smallintset_rehash(pcache, parent, hash, data, jl_array_len(a), val + 1); while (1) { - if (smallintset_insert_(*cache, hash(val, data), val + 1)) + a = jl_atomic_load_relaxed(pcache); + if (smallintset_insert_(a, hash(val, data), val + 1)) return; /* table full */ @@ -145,20 +147,21 @@ void jl_smallintset_insert(jl_array_t **cache, jl_value_t *parent, smallintset_h /* it's important to grow the table really fast; otherwise we waste */ /* lots of time rehashing all the keys over and over. */ size_t newsz; - size_t sz = jl_array_len(*cache); + a = jl_atomic_load_relaxed(pcache); + size_t sz = jl_array_len(a); if (sz < HT_N_INLINE) newsz = HT_N_INLINE; else if (sz >= (1 << 19) || (sz <= (1 << 8))) newsz = sz << 1; else newsz = sz << 2; - smallintset_rehash(cache, parent, hash, data, newsz, 0); + smallintset_rehash(pcache, parent, hash, data, newsz, 0); } } -static void smallintset_rehash(jl_array_t **cache, jl_value_t *parent, smallintset_hash hash, jl_svec_t *data, size_t newsz, size_t np) +static void smallintset_rehash(_Atomic(jl_array_t*) *pcache, jl_value_t *parent, smallintset_hash hash, jl_svec_t *data, size_t newsz, size_t np) { - jl_array_t *a = *cache; + jl_array_t *a = jl_atomic_load_relaxed(pcache); size_t sz = jl_array_len(a); size_t i; for (i = 0; i < sz; i += 1) { @@ -179,7 +182,7 @@ static void smallintset_rehash(jl_array_t **cache, jl_value_t *parent, smallints } JL_GC_POP(); if (i == sz) { - *cache = newa; + jl_atomic_store_release(pcache, newa); jl_gc_wb(parent, newa); return; } diff --git a/src/staticdata.c b/src/staticdata.c index 0a473684ff5dc..537abd77aa669 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -403,8 +403,8 @@ static void jl_serialize_module(jl_serializer_state *s, jl_module_t *m) jl_serialize_value(s, (jl_value_t*)table[i]); jl_binding_t *b = (jl_binding_t*)table[i+1]; jl_serialize_value(s, b->name); - jl_serialize_value(s, b->value); - jl_serialize_value(s, b->globalref); + jl_serialize_value(s, jl_atomic_load_relaxed(&b->value)); + jl_serialize_value(s, jl_atomic_load_relaxed(&b->globalref)); jl_serialize_value(s, b->owner); } } @@ -655,8 +655,8 @@ static void jl_write_module(jl_serializer_state *s, uintptr_t item, jl_module_t record_gvar(s, jl_get_llvm_gv(native_functions, (jl_value_t*)b), ((uintptr_t)DataRef << RELOC_TAG_OFFSET) + binding_reloc_offset); write_pointerfield(s, (jl_value_t*)b->name); - write_pointerfield(s, b->value); - write_pointerfield(s, b->globalref); + write_pointerfield(s, jl_atomic_load_relaxed(&b->value)); + write_pointerfield(s, jl_atomic_load_relaxed(&b->globalref)); write_pointerfield(s, (jl_value_t*)b->owner); size_t flag_offset = offsetof(jl_binding_t, owner) + sizeof(b->owner); ios_write(s->s, (char*)b + flag_offset, sizeof(*b) - flag_offset); diff --git a/src/subtype.c b/src/subtype.c index 2e1bfb4b05a66..4c1ae89f6c0ab 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -107,7 +107,7 @@ typedef struct jl_stenv_t { // state manipulation utilities // look up a type variable in an environment -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ static jl_varbinding_t *lookup(jl_stenv_t *e, jl_tvar_t *v) JL_GLOBALLY_ROOTED JL_NOTSAFEPOINT; #else static jl_varbinding_t *lookup(jl_stenv_t *e, jl_tvar_t *v) JL_GLOBALLY_ROOTED JL_NOTSAFEPOINT @@ -171,7 +171,7 @@ static void save_env(jl_stenv_t *e, jl_value_t **root, jl_savedenv_t *se) if (root) *root = (jl_value_t*)jl_alloc_svec(len * 3); se->buf = (int8_t*)(len > 8 ? malloc_s(len * 2) : &se->_space); -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ memset(se->buf, 0, len * 2); #endif int i=0, j=0; v = e->vars; diff --git a/src/support/analyzer_annotations.h b/src/support/analyzer_annotations.h index 1579584a572a9..70b5a273953f1 100644 --- a/src/support/analyzer_annotations.h +++ b/src/support/analyzer_annotations.h @@ -8,7 +8,7 @@ #endif #define JL_NONNULL _Nonnull -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ #define JL_PROPAGATES_ROOT __attribute__((annotate("julia_propagates_root"))) #define JL_NOTSAFEPOINT __attribute__((annotate("julia_not_safepoint"))) diff --git a/src/symbol.c b/src/symbol.c index fe8e975f8f525..fd5024eefeca2 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -15,7 +15,7 @@ extern "C" { #endif -static jl_sym_t *symtab = NULL; +static _Atomic(jl_sym_t*) symtab = NULL; #define MAX_SYM_LEN ((size_t)INTPTR_MAX - sizeof(jl_taggedvalue_t) - sizeof(jl_sym_t) - 1) @@ -48,9 +48,9 @@ static jl_sym_t *mk_symbol(const char *str, size_t len) JL_NOTSAFEPOINT return sym; } -static jl_sym_t *symtab_lookup(jl_sym_t **ptree, const char *str, size_t len, jl_sym_t ***slot) JL_NOTSAFEPOINT +static jl_sym_t *symtab_lookup(_Atomic(jl_sym_t*) *ptree, const char *str, size_t len, _Atomic(jl_sym_t*) **slot) JL_NOTSAFEPOINT { - jl_sym_t *node = jl_atomic_load_acquire(ptree); // consume + jl_sym_t *node = jl_atomic_load_relaxed(ptree); // consume uintptr_t h = hash_symbol(str, len); // Tree nodes sorted by major key of (int(hash)) and minor key of (str). @@ -68,7 +68,7 @@ static jl_sym_t *symtab_lookup(jl_sym_t **ptree, const char *str, size_t len, jl ptree = &node->left; else ptree = &node->right; - node = jl_atomic_load_acquire(ptree); // consume + node = jl_atomic_load_relaxed(ptree); // consume } if (slot != NULL) *slot = ptree; @@ -77,14 +77,14 @@ static jl_sym_t *symtab_lookup(jl_sym_t **ptree, const char *str, size_t len, jl jl_sym_t *_jl_symbol(const char *str, size_t len) JL_NOTSAFEPOINT // (or throw) { -#ifndef __clang_analyzer__ +#ifndef __clang_gcanalyzer__ // Hide the error throwing from the analyser since there isn't a way to express // "safepoint only when throwing error" currently. if (len > MAX_SYM_LEN) jl_exceptionf(jl_argumenterror_type, "Symbol name too long"); #endif assert(!memchr(str, 0, len)); - jl_sym_t **slot; + _Atomic(jl_sym_t*) *slot; jl_sym_t *node = symtab_lookup(&symtab, str, len, &slot); if (node == NULL) { JL_LOCK_NOGC(&gc_perm_lock); @@ -122,7 +122,7 @@ JL_DLLEXPORT jl_sym_t *jl_get_root_symbol(void) return symtab; } -static uint32_t gs_ctr = 0; // TODO: per-thread +static _Atomic(uint32_t) gs_ctr = 0; // TODO: per-module? uint32_t jl_get_gs_ctr(void) { return gs_ctr; } void jl_set_gs_ctr(uint32_t ctr) { gs_ctr = ctr; } diff --git a/src/task.c b/src/task.c index 82b988a588132..ceb114617fc38 100644 --- a/src/task.c +++ b/src/task.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include "julia.h" @@ -67,8 +68,8 @@ static inline void tsan_switch_to_ctx(void *state) { // empirically, jl_finish_task needs about 64k stack space to infer/run // and additionally, gc-stack reserves 64k for the guard pages -#if defined(MINSIGSTKSZ) && MINSIGSTKSZ > 131072 -#define MINSTKSZ MINSIGSTKSZ +#if defined(MINSIGSTKSZ) +#define MINSTKSZ (MINSIGSTKSZ > 131072 ? MINSIGSTKSZ : 131072) #else #define MINSTKSZ 131072 #endif @@ -188,7 +189,7 @@ static void restore_stack2(jl_task_t *t, jl_ptls_t ptls, jl_task_t *lastt) #endif /* Rooted by the base module */ -static jl_function_t *task_done_hook_func JL_GLOBALLY_ROOTED = NULL; +static _Atomic(jl_function_t*) task_done_hook_func JL_GLOBALLY_ROOTED = NULL; void JL_NORETURN jl_finish_task(jl_task_t *t) { @@ -727,7 +728,7 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion t->next = jl_nothing; t->queue = jl_nothing; t->tls = jl_nothing; - t->_state = JL_TASK_STATE_RUNNABLE; + jl_atomic_store_relaxed(&t->_state, JL_TASK_STATE_RUNNABLE); t->start = start; t->result = jl_nothing; t->donenotify = completion_future; @@ -743,7 +744,7 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion t->excstack = NULL; t->started = 0; t->prio = -1; - t->tid = t->copy_stack ? ct->tid : -1; // copy_stacks are always pinned since they can't be moved + jl_atomic_store_relaxed(&t->tid, t->copy_stack ? ct->tid : -1); // copy_stacks are always pinned since they can't be moved t->ptls = NULL; t->world_age = 0; @@ -840,7 +841,7 @@ STATIC_OR_JS void NOINLINE JL_NORETURN start_task(void) CFI_NORETURN // this runs the first time we switch to a task sanitizer_finish_switch_fiber(); -#ifdef __clang_analyzer__ +#ifdef __clang_gcanalyzer__ jl_task_t *ct = jl_get_current_task(); JL_GC_PROMISE_ROOTED(ct); #else @@ -940,7 +941,7 @@ static char *jl_alloc_fiber(jl_ucontext_t *t, size_t *ssize, jl_task_t *owner) char *stkbuf = (char*)jl_malloc_stack(ssize, owner); if (stkbuf == NULL) return NULL; -#ifndef __clang_analyzer__ +#ifndef __clang_gcanalyzer__ ((char**)t)[0] = stkbuf; // stash the stack pointer somewhere for start_fiber ((size_t*)t)[1] = *ssize; // stash the stack size somewhere for start_fiber #endif @@ -1289,7 +1290,7 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) ct->next = jl_nothing; ct->queue = jl_nothing; ct->tls = jl_nothing; - ct->_state = JL_TASK_STATE_RUNNABLE; + jl_atomic_store_relaxed(&ct->_state, JL_TASK_STATE_RUNNABLE); ct->start = NULL; ct->result = jl_nothing; ct->donenotify = jl_nothing; @@ -1298,7 +1299,7 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) ct->eh = NULL; ct->gcstack = NULL; ct->excstack = NULL; - ct->tid = ptls->tid; + jl_atomic_store_relaxed(&ct->tid, ptls->tid); ct->sticky = 1; ct->ptls = ptls; ct->world_age = 1; // OK to run Julia code on this task diff --git a/src/threading.c b/src/threading.c index ffe53c07b45ee..a92bccc785b5f 100644 --- a/src/threading.c +++ b/src/threading.c @@ -176,7 +176,7 @@ JL_DLLEXPORT void jl_set_safe_restore(jl_jmp_buf *sr) JL_CONST_FUNC jl_gcframe_t **jl_get_pgcstack(void) JL_NOTSAFEPOINT { SAVE_ERRNO; - jl_gcframe_t **pgcstack = (jl_ptls_t)TlsGetValue(jl_pgcstack_key); + jl_gcframe_t **pgcstack = (jl_gcframe_t**)TlsGetValue(jl_pgcstack_key); LOAD_ERRNO; return pgcstack; } @@ -226,7 +226,7 @@ static jl_gcframe_t ***jl_pgcstack_addr_fallback(void) JL_NOTSAFEPOINT } void jl_set_pgcstack(jl_gcframe_t **pgcstack) JL_NOTSAFEPOINT { - *jl_pgcstack_key() = pgcstack; + *(jl_gcframe_t**)jl_pgcstack_key() = pgcstack; } # if JL_USE_IFUNC JL_DLLEXPORT __attribute__((weak)) @@ -271,7 +271,7 @@ JL_DLLEXPORT void jl_pgcstack_setkey(jl_get_pgcstack_func *f, jl_pgcstack_key_t JL_DLLEXPORT jl_gcframe_t **jl_get_pgcstack(void) JL_GLOBALLY_ROOTED { -#ifndef __clang_analyzer__ +#ifndef __clang_gcanalyzer__ return jl_get_pgcstack_cb(); #endif } @@ -287,8 +287,8 @@ void jl_pgcstack_getkey(jl_get_pgcstack_func **f, jl_pgcstack_key_t *k) #endif jl_ptls_t *jl_all_tls_states JL_GLOBALLY_ROOTED; -uint8_t jl_measure_compile_time_enabled = 0; -uint64_t jl_cumulative_compile_time = 0; +_Atomic(uint8_t) jl_measure_compile_time_enabled = 0; +_Atomic(uint64_t) jl_cumulative_compile_time = 0; // return calling thread's ID // Also update the suspended_threads list in signals-mach when changing the @@ -467,7 +467,7 @@ void jl_init_threading(void) } if (jl_n_threads <= 0) jl_n_threads = 1; -#ifndef __clang_analyzer__ +#ifndef __clang_gcanalyzer__ jl_all_tls_states = (jl_ptls_t*)calloc(jl_n_threads, sizeof(void*)); #endif } @@ -527,7 +527,7 @@ void jl_start_threads(void) uv_barrier_wait(&thread_init_done); } -unsigned volatile _threadedregion; // HACK: keep track of whether it is safe to do IO +_Atomic(unsigned) _threadedregion; // HACK: keep track of whether it is safe to do IO JL_DLLEXPORT int jl_in_threaded_region(void) { diff --git a/src/typemap.c b/src/typemap.c index 58dd2b8b13069..13e97a5ada160 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -259,28 +259,28 @@ static int is_cache_leaf(jl_value_t *ty, int tparam) return (jl_is_concrete_type(ty) && (tparam || !jl_is_kind(ty))); } -static jl_typemap_t **mtcache_hash_lookup_bp(jl_array_t *cache JL_PROPAGATES_ROOT, jl_value_t *ty) JL_NOTSAFEPOINT +static _Atomic(jl_typemap_t*) *mtcache_hash_lookup_bp(jl_array_t *cache JL_PROPAGATES_ROOT, jl_value_t *ty) JL_NOTSAFEPOINT { if (cache == (jl_array_t*)jl_an_empty_vec_any) return NULL; - jl_typemap_t **pml = jl_table_peek_bp(cache, ty); + _Atomic(jl_typemap_t*) *pml = jl_table_peek_bp(cache, ty); JL_GC_PROMISE_ROOTED(pml); // clang-sa doesn't trust our JL_PROPAGATES_ROOT claim return pml; } -static void mtcache_hash_insert(jl_array_t **cache, jl_value_t *parent, jl_value_t *key, jl_typemap_t *val) +static void mtcache_hash_insert(_Atomic(jl_array_t*) *cache, jl_value_t *parent, jl_value_t *key, jl_typemap_t *val) { int inserted = 0; - jl_array_t *a = *cache; + jl_array_t *a = jl_atomic_load_relaxed(cache); if (a == (jl_array_t*)jl_an_empty_vec_any) { a = jl_alloc_vec_any(16); - *cache = a; + jl_atomic_store_release(cache, a); jl_gc_wb(parent, a); } a = jl_eqtable_put(a, key, val, &inserted); assert(inserted); if (a != *cache) { - *cache = a; + jl_atomic_store_release(cache, a); jl_gc_wb(parent, a); } } @@ -299,7 +299,7 @@ static jl_typemap_t *mtcache_hash_lookup(jl_array_t *cache JL_PROPAGATES_ROOT, j static int jl_typemap_array_visitor(jl_array_t *a, jl_typemap_visitor_fptr fptr, void *closure) { size_t i, l = jl_array_len(a); - jl_typemap_t **data = (jl_typemap_t **)jl_array_data(a); + _Atomic(jl_typemap_t*) *data = (_Atomic(jl_typemap_t*)*)jl_array_data(a); for (i = 1; i < l; i += 2) { jl_value_t *d = jl_atomic_load_relaxed(&data[i]); JL_GC_PROMISE_ROOTED(d); @@ -394,7 +394,7 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, { JL_GC_PUSH1(&a); size_t i, l = jl_array_len(a); - jl_typemap_t **data = (jl_typemap_t **)jl_array_data(a); + _Atomic(jl_typemap_t*) *data = (_Atomic(jl_typemap_t*)*)jl_array_data(a); unsigned height = tparam & 2 ? jl_supertype_height((jl_datatype_t*)ty) : 0; for (i = 0; i < l; i += 2) { jl_value_t *t = jl_atomic_load_relaxed(&data[i]); @@ -845,7 +845,7 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type( if (!ty || !jl_has_empty_intersection((jl_value_t*)jl_type_type, ty)) { // couldn't figure out unique `a0` initial point, so scan all for matches size_t i, l = jl_array_len(tname); - jl_typemap_t **data = (jl_typemap_t **)jl_array_ptr_data(tname); + _Atomic(jl_typemap_t*) *data = (_Atomic(jl_typemap_t*)*)jl_array_ptr_data(tname); JL_GC_PUSH1(&tname); for (i = 1; i < l; i += 2) { jl_typemap_t *ml = jl_atomic_load_relaxed(&data[i]); @@ -884,7 +884,7 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type( else { // doing subtype, but couldn't figure out unique `ty`, so scan all for supertypes size_t i, l = jl_array_len(name1); - jl_typemap_t **data = (jl_typemap_t **)jl_array_ptr_data(name1); + _Atomic(jl_typemap_t*) *data = (_Atomic(jl_typemap_t*)*)jl_array_ptr_data(name1); JL_GC_PUSH1(&name1); for (i = 1; i < l; i += 2) { jl_typemap_t *ml = jl_atomic_load_relaxed(&data[i]); @@ -1034,7 +1034,7 @@ jl_typemap_entry_t *jl_typemap_level_assoc_exact(jl_typemap_level_t *cache, jl_v else { // couldn't figure out unique `name` initial point, so must scan all for matches size_t i, l = jl_array_len(tname); - jl_typemap_t **data = (jl_typemap_t **)jl_array_ptr_data(tname); + _Atomic(jl_typemap_t*) *data = (_Atomic(jl_typemap_t*)*)jl_array_ptr_data(tname); JL_GC_PUSH1(&tname); for (i = 1; i < l; i += 2) { jl_typemap_t *ml_or_cache = data[i]; @@ -1122,10 +1122,10 @@ static jl_typemap_level_t *jl_method_convert_list_to_cache( } static void jl_typemap_list_insert_( - jl_typemap_t *map, jl_typemap_entry_t **pml, jl_value_t *parent, + jl_typemap_t *map, _Atomic(jl_typemap_entry_t*) *pml, jl_value_t *parent, jl_typemap_entry_t *newrec) { - jl_typemap_entry_t *l = *pml; + jl_typemap_entry_t *l = jl_atomic_load_relaxed(pml); while ((jl_value_t*)l != jl_nothing) { if (newrec->isleafsig || !l->isleafsig) if (newrec->issimplesig || !l->issimplesig) @@ -1141,37 +1141,39 @@ static void jl_typemap_list_insert_( } static void jl_typemap_insert_generic( - jl_typemap_t *map, jl_typemap_t **pml, jl_value_t *parent, + jl_typemap_t *map, _Atomic(jl_typemap_t*) *pml, jl_value_t *parent, jl_typemap_entry_t *newrec, int8_t offs) { - if (jl_typeof(*pml) == (jl_value_t*)jl_typemap_level_type) { - jl_typemap_level_insert_(map, (jl_typemap_level_t*)*pml, newrec, offs); + jl_typemap_t *ml = jl_atomic_load_relaxed(pml); + if (jl_typeof(ml) == (jl_value_t*)jl_typemap_level_type) { + jl_typemap_level_insert_(map, (jl_typemap_level_t*)ml, newrec, offs); return; } - unsigned count = jl_typemap_list_count_locked((jl_typemap_entry_t*)*pml); + unsigned count = jl_typemap_list_count_locked((jl_typemap_entry_t*)ml); if (count > MAX_METHLIST_COUNT) { - *pml = (jl_typemap_t*)jl_method_convert_list_to_cache( - map, (jl_typemap_entry_t *)*pml, - offs); - jl_gc_wb(parent, *pml); - jl_typemap_level_insert_(map, (jl_typemap_level_t*)*pml, newrec, offs); + ml = (jl_typemap_t*)jl_method_convert_list_to_cache( + map, (jl_typemap_entry_t*)ml, offs); + jl_atomic_store_release(pml, ml); + jl_gc_wb(parent, ml); + jl_typemap_level_insert_(map, (jl_typemap_level_t*)ml, newrec, offs); return; } - jl_typemap_list_insert_(map, (jl_typemap_entry_t **)pml, + jl_typemap_list_insert_(map, (_Atomic(jl_typemap_entry_t*)*)pml, parent, newrec); } static void jl_typemap_array_insert_( - jl_typemap_t *map, jl_array_t **cache, jl_value_t *key, jl_typemap_entry_t *newrec, + jl_typemap_t *map, _Atomic(jl_array_t*) *pcache, jl_value_t *key, jl_typemap_entry_t *newrec, jl_value_t *parent, int8_t offs) { - jl_typemap_t **pml = mtcache_hash_lookup_bp(*cache, key); + jl_array_t *cache = jl_atomic_load_relaxed(pcache); + _Atomic(jl_typemap_t*) *pml = mtcache_hash_lookup_bp(cache, key); if (pml != NULL) - jl_typemap_insert_generic(map, pml, (jl_value_t*)*cache, newrec, offs+1); + jl_typemap_insert_generic(map, pml, (jl_value_t*)cache, newrec, offs+1); else - mtcache_hash_insert(cache, parent, key, (jl_typemap_t*)newrec); + mtcache_hash_insert(pcache, parent, key, (jl_typemap_t*)newrec); } static void jl_typemap_level_insert_( @@ -1276,7 +1278,7 @@ jl_typemap_entry_t *jl_typemap_alloc( newrec->simplesig = simpletype; newrec->func.value = newvalue; newrec->guardsigs = guardsigs; - newrec->next = (jl_typemap_entry_t*)jl_nothing; + jl_atomic_store_relaxed(&newrec->next, (jl_typemap_entry_t*)jl_nothing); newrec->min_world = min_world; newrec->max_world = max_world; newrec->va = isva; @@ -1285,10 +1287,11 @@ jl_typemap_entry_t *jl_typemap_alloc( return newrec; } -void jl_typemap_insert(jl_typemap_t **cache, jl_value_t *parent, +void jl_typemap_insert(_Atomic(jl_typemap_t *) *pcache, jl_value_t *parent, jl_typemap_entry_t *newrec, int8_t offs) { - jl_typemap_insert_generic(*cache, cache, parent, newrec, offs); + jl_typemap_t *cache = jl_atomic_load_relaxed(pcache); + jl_typemap_insert_generic(cache, pcache, parent, newrec, offs); } #ifdef __cplusplus diff --git a/stdlib/ArgTools.version b/stdlib/ArgTools.version index c9026ce76af7b..0d638596c6fd1 100644 --- a/stdlib/ArgTools.version +++ b/stdlib/ArgTools.version @@ -1,2 +1,4 @@ ARGTOOLS_BRANCH = master ARGTOOLS_SHA1 = fa878696ff2ae4ba7ca9942bf9544556c0d86ce4 +ARGTOOLS_GIT_URL := git://github.com/JuliaIO/ArgTools.jl.git +ARGTOOLS_TAR_URL = https://api.github.com/repos/JuliaIO/ArgTools.jl/tarball/$1 diff --git a/stdlib/Distributed/test/distributed_exec.jl b/stdlib/Distributed/test/distributed_exec.jl index 3335807fe17e2..ea301b5f80651 100644 --- a/stdlib/Distributed/test/distributed_exec.jl +++ b/stdlib/Distributed/test/distributed_exec.jl @@ -132,7 +132,7 @@ end testf(id_me) testf(id_other) -function poll_while(f::Function; timeout_seconds::Integer = 60) +function poll_while(f::Function; timeout_seconds::Integer = 120) start_time = time_ns() while f() sleep(1) @@ -144,6 +144,27 @@ function poll_while(f::Function; timeout_seconds::Integer = 60) return true end +function _getenv_include_thread_unsafe() + environment_variable_name = "JULIA_TEST_INCLUDE_THREAD_UNSAFE" + default_value = "false" + environment_variable_value = strip(get(ENV, environment_variable_name, default_value)) + b = parse(Bool, environment_variable_value)::Bool + return b +end +const _env_include_thread_unsafe = _getenv_include_thread_unsafe() +function include_thread_unsafe() + if Threads.nthreads() > 1 + if _env_include_thread_unsafe + return true + end + msg = "Skipping a thread-unsafe test because `Threads.nthreads() > 1`" + @warn msg Threads.nthreads() + Test.@test_broken false + return false + end + return true +end + # Distributed GC tests for Futures function test_futures_dgc(id) f = remotecall(myid, id) @@ -267,10 +288,14 @@ let wid1 = workers()[1], fstore = RemoteChannel(wid2) put!(fstore, rr) - @test remotecall_fetch(k -> haskey(Distributed.PGRP.refs, k), wid1, rrid) == true + if include_thread_unsafe() + @test remotecall_fetch(k -> haskey(Distributed.PGRP.refs, k), wid1, rrid) == true + end finalize(rr) # finalize locally yield() # flush gc msgs - @test remotecall_fetch(k -> haskey(Distributed.PGRP.refs, k), wid1, rrid) == true + if include_thread_unsafe() + @test remotecall_fetch(k -> haskey(Distributed.PGRP.refs, k), wid1, rrid) == true + end remotecall_fetch(r -> (finalize(take!(r)); yield(); nothing), wid2, fstore) # finalize remotely sleep(0.5) # to ensure that wid2 messages have been executed on wid1 @test remotecall_fetch(k -> haskey(Distributed.PGRP.refs, k), wid1, rrid) == false diff --git a/stdlib/Downloads.version b/stdlib/Downloads.version index 0eddf27872401..056fc38d8254a 100644 --- a/stdlib/Downloads.version +++ b/stdlib/Downloads.version @@ -1,2 +1,4 @@ -DOWNLOADS_BRANCH = master -DOWNLOADS_SHA1 = 5d00bddf2db81b0ea62990b4d81fe5bf51c38b10 +DOWNLOADS_BRANCH = release-1.5 +DOWNLOADS_SHA1 = 26d79afcde9cf837a331fce023b31f1d3699700c +DOWNLOADS_GIT_URL := git://github.com/JuliaLang/Downloads.jl.git +DOWNLOADS_TAR_URL = https://api.github.com/repos/JuliaLang/Downloads.jl/tarball/$1 diff --git a/stdlib/LibCURL.version b/stdlib/LibCURL.version index aa3dc34719df9..55dc3f3f6c965 100644 --- a/stdlib/LibCURL.version +++ b/stdlib/LibCURL.version @@ -1,2 +1,4 @@ LIBCURL_BRANCH = master LIBCURL_SHA1 = 8310487053915d5c995513f569ad85ba65c3544f +LIBCURL_GIT_URL := git://github.com/JuliaWeb/LibCURL.jl.git +LIBCURL_TAR_URL = https://api.github.com/repos/JuliaWeb/LibCURL.jl/tarball/$1 diff --git a/stdlib/LibUV_jll/Project.toml b/stdlib/LibUV_jll/Project.toml index 6215781ec4177..20b72189e91ed 100644 --- a/stdlib/LibUV_jll/Project.toml +++ b/stdlib/LibUV_jll/Project.toml @@ -1,6 +1,6 @@ name = "LibUV_jll" uuid = "183b4373-6708-53ba-ad28-60e28bb38547" -version = "2.0.1+3" +version = "2.0.1+4" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/Makefile b/stdlib/Makefile index 38c8c45ac8975..b17e41b441b0e 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -45,22 +45,6 @@ STDLIBS = Artifacts Base64 CRC32c Dates DelimitedFiles Distributed FileWatching $(JLL_NAMES) STDLIBS_EXT = Pkg Statistics LibCURL Downloads ArgTools Tar NetworkOptions SuiteSparse -PKG_GIT_URL := git://github.com/JuliaLang/Pkg.jl.git -PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 -STATISTICS_GIT_URL := git://github.com/JuliaLang/Statistics.jl.git -STATISTICS_TAR_URL = https://api.github.com/repos/JuliaLang/Statistics.jl/tarball/$1 -LIBCURL_GIT_URL := git://github.com/JuliaWeb/LibCURL.jl.git -LIBCURL_TAR_URL = https://api.github.com/repos/JuliaWeb/LibCURL.jl/tarball/$1 -DOWNLOADS_GIT_URL := git://github.com/JuliaLang/Downloads.jl.git -DOWNLOADS_TAR_URL = https://api.github.com/repos/JuliaLang/Downloads.jl/tarball/$1 -ARGTOOLS_GIT_URL := git://github.com/JuliaIO/ArgTools.jl.git -ARGTOOLS_TAR_URL = https://api.github.com/repos/JuliaIO/ArgTools.jl/tarball/$1 -TAR_GIT_URL := git://github.com/JuliaIO/Tar.jl.git -TAR_TAR_URL = https://api.github.com/repos/JuliaIO/Tar.jl/tarball/$1 -NETWORKOPTIONS_GIT_URL := git://github.com/JuliaLang/NetworkOptions.jl.git -NETWORKOPTIONS_TAR_URL = https://api.github.com/repos/JuliaLang/NetworkOptions.jl/tarball/$1 -SUITESPARSE_GIT_URL := git://github.com/JuliaLang/SuiteSparse.jl.git -SUITESPARSE_TAR_URL = https://api.github.com/repos/JuliaLang/SuiteSparse.jl/tarball/$1 $(foreach module, $(STDLIBS_EXT), $(eval $(call stdlib-external,$(module),$(shell echo $(module) | tr a-z A-Z)))) diff --git a/stdlib/NetworkOptions.version b/stdlib/NetworkOptions.version index b8e8b3d371c38..bdc00e2bf8441 100644 --- a/stdlib/NetworkOptions.version +++ b/stdlib/NetworkOptions.version @@ -1,2 +1,4 @@ NETWORKOPTIONS_BRANCH = master NETWORKOPTIONS_SHA1 = a251de1e1c8ce4edc351d0f05233ba7fe7d2c27a +NETWORKOPTIONS_GIT_URL := git://github.com/JuliaLang/NetworkOptions.jl.git +NETWORKOPTIONS_TAR_URL = https://api.github.com/repos/JuliaLang/NetworkOptions.jl/tarball/$1 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index b88b7bc5112e2..14d458ee5d538 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,2 +1,4 @@ PKG_BRANCH = release-1.7 -PKG_SHA1 = a230dcf5f3546bd464e575e7752d6259a3221bd5 +PKG_SHA1 = 347dbfbbf18c4b5f3ac921a48663aed87b357dbf +PKG_GIT_URL := git://github.com/JuliaLang/Pkg.jl.git +PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 diff --git a/stdlib/Random/src/RNGs.jl b/stdlib/Random/src/RNGs.jl index 141ec14f4ed31..5c36b085cd505 100644 --- a/stdlib/Random/src/RNGs.jl +++ b/stdlib/Random/src/RNGs.jl @@ -23,16 +23,26 @@ else # !windows rand(rd::RandomDevice, sp::SamplerBoolBitInteger) = read(getfile(rd), sp[]) rand(rd::RandomDevice, ::SamplerType{Bool}) = read(getfile(rd), UInt8) % Bool - function getfile(rd::RandomDevice) - devrandom = rd.unlimited ? DEV_URANDOM : DEV_RANDOM - # TODO: there is a data-race, this can leak up to nthreads() copies of the file descriptors, - # so use a "thread-once" utility once available - isassigned(devrandom) || (devrandom[] = open(rd.unlimited ? "/dev/urandom" : "/dev/random")) - devrandom[] + mutable struct FileRef + @atomic file::Union{IOStream, Nothing} end - const DEV_RANDOM = Ref{IOStream}() - const DEV_URANDOM = Ref{IOStream}() + const DEV_RANDOM = FileRef(nothing) + const DEV_URANDOM = FileRef(nothing) + + function getfile(rd::RandomDevice) + ref = rd.unlimited ? DEV_URANDOM : DEV_RANDOM + fd = ref.file + if fd === nothing + fd = open(rd.unlimited ? "/dev/urandom" : "/dev/random") + old, ok = @atomicreplace ref.file nothing => fd + if !ok + close(fd) + fd = old::IOStream + end + end + return fd + end end # os-test @@ -411,6 +421,10 @@ for T in BitInteger_types end function __init__() + @static if !Sys.iswindows() + @atomic DEV_RANDOM.file = nothing + @atomic DEV_URANDOM.file = nothing + end seed!(GLOBAL_RNG) end diff --git a/stdlib/Statistics.version b/stdlib/Statistics.version index 84cdf8630e8fc..b277db56bee4b 100644 --- a/stdlib/Statistics.version +++ b/stdlib/Statistics.version @@ -1,2 +1,4 @@ STATISTICS_BRANCH = master -STATISTICS_SHA1 = 54f9b0d999813aa9fab039f632df222ffd2a96a8 +STATISTICS_SHA1 = 5256d570d0a554780ed80949c79116f47eac6382 +STATISTICS_GIT_URL := git://github.com/JuliaLang/Statistics.jl.git +STATISTICS_TAR_URL = https://api.github.com/repos/JuliaLang/Statistics.jl/tarball/$1 diff --git a/stdlib/SuiteSparse.version b/stdlib/SuiteSparse.version index b1853bbdace76..0f8588009e4ba 100644 --- a/stdlib/SuiteSparse.version +++ b/stdlib/SuiteSparse.version @@ -1,2 +1,4 @@ SUITESPARSE_BRANCH = master SUITESPARSE_SHA1 = b2d965aa8493f5ef25d6c861ce9d06ed02978ccc +SUITESPARSE_GIT_URL := git://github.com/JuliaLang/SuiteSparse.jl.git +SUITESPARSE_TAR_URL = https://api.github.com/repos/JuliaLang/SuiteSparse.jl/tarball/$1 diff --git a/stdlib/Tar.version b/stdlib/Tar.version index b129d22665e12..39cbf1bfe62b6 100644 --- a/stdlib/Tar.version +++ b/stdlib/Tar.version @@ -1,2 +1,4 @@ TAR_BRANCH = master TAR_SHA1 = ffb3dd5e697eb6690fce9cceb67edb82134f8337 +TAR_GIT_URL := git://github.com/JuliaIO/Tar.jl.git +TAR_TAR_URL = https://api.github.com/repos/JuliaIO/Tar.jl/tarball/$1 diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index c916501dc2543..f803d8bf01721 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -932,7 +932,7 @@ record(ts::DefaultTestSet, t::Pass) = (ts.n_passed += 1; t) # but do not terminate. Print a backtrace. function record(ts::DefaultTestSet, t::Union{Fail, Error}) if TESTSET_PRINT_ENABLE[] - printstyled(ts.description, ": ", color=:white) + print(ts.description, ": ") # don't print for interrupted tests if !(t isa Error) || t.test_type !== :test_interrupted print(t) @@ -991,7 +991,7 @@ function print_test_results(ts::DefaultTestSet, depth_pad=0) align = max(get_alignment(ts, 0), length("Test Summary:")) # Print the outer test set header once pad = total == 0 ? "" : " " - printstyled(rpad("Test Summary:", align, " "), " |", pad; bold=true, color=:white) + printstyled(rpad("Test Summary:", align, " "), " |", pad; bold=true) if pass_width > 0 printstyled(lpad("Pass", pass_width, " "), " "; bold=true, color=:green) end diff --git a/test/Makefile b/test/Makefile index 3d16f88a741b8..24e137a5b1492 100644 --- a/test/Makefile +++ b/test/Makefile @@ -7,7 +7,7 @@ STDLIBDIR := $(build_datarootdir)/julia/stdlib/$(VERSDIR) # TODO: this Makefile ignores BUILDDIR, except for computing JULIA_EXECUTABLE TESTGROUPS = unicode strings compiler -TESTS = all stdlib $(TESTGROUPS) \ +TESTS = all default stdlib $(TESTGROUPS) \ $(patsubst $(STDLIBDIR)/%/,%,$(dir $(wildcard $(STDLIBDIR)/*/.))) \ $(filter-out runtests testdefs, \ $(patsubst $(SRCDIR)/%.jl,%,$(wildcard $(SRCDIR)/*.jl))) \ @@ -19,7 +19,7 @@ EMBEDDING_ARGS := "JULIA=$(JULIA_EXECUTABLE)" "BIN=$(SRCDIR)/embedding" "CC=$(CC GCEXT_ARGS := "JULIA=$(JULIA_EXECUTABLE)" "BIN=$(SRCDIR)/gcext" "CC=$(CC)" -default: all +default: $(TESTS): @cd $(SRCDIR) && \ diff --git a/test/arrayops.jl b/test/arrayops.jl index d32ba840dabc1..a37d93ee90473 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -790,6 +790,10 @@ let A, B, C, D # With hash collisions @test map(x -> x.x, unique(map(HashCollision, B), dims=1)) == C + + # With NaNs: + E = [1 NaN 3; 1 NaN 3; 1 NaN 3]; + @test isequal(unique(E, dims=1), [1 NaN 3]) end @testset "large matrices transpose" begin diff --git a/test/choosetests.jl b/test/choosetests.jl index e34a1b4dacf67..af25f711fd1cf 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -5,34 +5,7 @@ using Random, Sockets const STDLIB_DIR = Sys.STDLIB const STDLIBS = filter!(x -> isfile(joinpath(STDLIB_DIR, x, "src", "$(x).jl")), readdir(STDLIB_DIR)) -""" - -`tests, net_on, exit_on_error, seed = choosetests(choices)` selects a set of tests to be -run. `choices` should be a vector of test names; if empty or set to -`["all"]`, all tests are selected. - -This function also supports "test collections": specifically, "linalg" - refers to collections of tests in the correspondingly-named -directories. - -Upon return: - - `tests` is a vector of fully-expanded test names, - - `net_on` is true if networking is available (required for some tests), - - `exit_on_error` is true if an error in one test should cancel - remaining tests to be run (otherwise, all tests are run unconditionally), - - `seed` is a seed which will be used to initialize the global RNG for each - test to be run. - -Three options can be passed to `choosetests` by including a special token -in the `choices` argument: - - "--skip", which makes all tests coming after be skipped, - - "--exit-on-error" which sets the value of `exit_on_error`, - - "--seed=SEED", which sets the value of `seed` to `SEED` - (parsed as an `UInt128`); `seed` is otherwise initialized randomly. - This option can be used to reproduce failed tests. -""" -function choosetests(choices = []) - testnames = [ +const TESTNAMES = [ "subarray", "core", "compiler", "worlds", "atomics", "keywordargs", "numbers", "subtype", "char", "strings", "triplequote", "unicode", "intrinsics", @@ -56,17 +29,48 @@ function choosetests(choices = []) "channels", "iostream", "secretbuffer", "specificity", "reinterpretarray", "syntax", "corelogging", "missing", "asyncmap", "smallarrayshrink", "opaque_closure", "filesystem", "download" - ] +] + +""" + +`(; tests, net_on, exit_on_error, seed) = choosetests(choices)` selects a set of tests to be +run. `choices` should be a vector of test names; if empty or set to +`["all"]`, all tests are selected. +This function also supports "test collections": specifically, "linalg" + refers to collections of tests in the correspondingly-named +directories. + +The function returns a named tuple with the following elements: + - `tests` is a vector of fully-expanded test names, + - `net_on` is true if networking is available (required for some tests), + - `exit_on_error` is true if an error in one test should cancel + remaining tests to be run (otherwise, all tests are run unconditionally), + - `seed` is a seed which will be used to initialize the global RNG for each + test to be run. + +Several options can be passed to `choosetests` by including a special token +in the `choices` argument: + - "--skip", which makes all tests coming after be skipped, + - "--exit-on-error" which sets the value of `exit_on_error`, + - "--seed=SEED", which sets the value of `seed` to `SEED` + (parsed as an `UInt128`); `seed` is otherwise initialized randomly. + This option can be used to reproduce failed tests. + - "--help", which prints a help message and then skips all tests. + - "--help-list", which prints the options computed without running them. +""" +function choosetests(choices = []) tests = [] - skip_tests = [] + skip_tests = Set() exit_on_error = false use_revise = false seed = rand(RandomDevice(), UInt128) + force_net = false + dryrun = false for (i, t) in enumerate(choices) if t == "--skip" - skip_tests = choices[i + 1:end] + union!(skip_tests, choices[i + 1:end]) break elseif t == "--exit-on-error" exit_on_error = true @@ -74,25 +78,60 @@ function choosetests(choices = []) use_revise = true elseif startswith(t, "--seed=") seed = parse(UInt128, t[8:end]) + elseif t == "--force-net" + force_net = true + elseif t == "--help-list" + dryrun = true + elseif t == "--help" + println(""" + USAGE: ./julia runtests.jl [options] [tests] + OPTIONS: + --exit-on-error : stop tests immediately when a test group fails + --help : prints this help message + --help-list : prints the options computed without running them + --revise : load Revise + --seed= : set the initial seed for all testgroups (parsed as a UInt128) + --skip ... : skip test or collection tagged with + TESTS: + Can be special tokens, such as "all", "unicode", "stdlib", the names of stdlib \ + modules, or the names of any file in the TESTNAMES array (defaults to "all"). + + Or prefix a name with `-` (such as `-core`) to skip a particular test. + """) + return [], false, false, false, UInt128(0) + elseif startswith(t, "--") + error("unknown option: $t") + elseif startswith(t, "-") + push!(skip_tests, t[2:end]) else push!(tests, t) end end - if tests == ["all"] || isempty(tests) - tests = testnames + unhandled = copy(skip_tests) + + requested_all = "all" in tests + requested_default = "default" in tests + if isempty(tests) || requested_all || requested_default + append!(tests, TESTNAMES) end + filter!(x -> x != "all", tests) + filter!(x -> x != "default", tests) function filtertests!(tests, name, files=[name]) flt = x -> (x != name && !(x in files)) if name in skip_tests filter!(flt, tests) + pop!(unhandled, name) elseif name in tests filter!(flt, tests) prepend!(tests, files) end end + explicit_pkg = "Pkg" in tests + explicit_libgit2 = "LibGit2/online" in tests + filtertests!(tests, "unicode", ["unicode/utf8"]) filtertests!(tests, "strings", ["strings/basic", "strings/search", "strings/util", "strings/io", "strings/types"]) @@ -108,7 +147,7 @@ function choosetests(choices = []) if startswith(string(Sys.ARCH), "arm") # Remove profile from default tests on ARM since it currently segfaults # Allow explicitly adding it for testing - @warn "Skipping Profile tests" + @warn "Skipping Profile tests because the architecture is ARM" filter!(x -> (x != "Profile"), tests) end @@ -117,7 +156,12 @@ function choosetests(choices = []) net_on = true try ipa = getipaddr() - catch + catch ex + if force_net + msg = "Networking is unavailable, and the `--force-net` option was passed" + @error msg + rethrow() + end @warn "Networking unavailable: Skipping tests [" * join(net_required_for, ", ") * "]" net_on = false end @@ -131,10 +175,9 @@ function choosetests(choices = []) filter!(x -> x != "rounding", tests) end + filter!(!in(tests), unhandled) filter!(!in(skip_tests), tests) - explicit_pkg3 = "Pkg/pkg" in tests - explicit_libgit2 = "LibGit2/online" in tests new_tests = String[] for test in tests if test in STDLIBS @@ -150,11 +193,31 @@ function choosetests(choices = []) end filter!(x -> (x != "stdlib" && !(x in STDLIBS)) , tests) append!(tests, new_tests) - explicit_pkg3 || filter!(x -> x != "Pkg/pkg", tests) - explicit_libgit2 || filter!(x -> x != "LibGit2/online", tests) + + requested_all || explicit_pkg || filter!(x -> x != "Pkg", tests) + requested_all || explicit_libgit2 || filter!(x -> x != "LibGit2/online", tests) # Filter out tests from the test groups in the stdlibs + filter!(!in(tests), unhandled) filter!(!in(skip_tests), tests) - tests, net_on, exit_on_error, use_revise, seed + if !isempty(unhandled) + @warn "Not skipping tests: $(join(unhandled, ", "))" + end + + if dryrun + print("Tests enabled to run:") + foreach(t -> print("\n ", t), tests) + if !isempty(skip_tests) + print("\n\nTests skipped:") + foreach(t -> print("\n ", t), skip_tests) + end + print("\n") + exit_on_error && (print("\nwith option "); printstyled("exit_on_error", bold=true)) + use_revise && (print("\nwith option "); printstyled("use_revise", bold=true); print(" (Revise.jl)")) + print("\n\n") + empty!(tests) + end + + return (; tests, net_on, exit_on_error, use_revise, seed) end diff --git a/test/clangsa/GCPushPop.cpp b/test/clangsa/GCPushPop.cpp index a992630291bb5..f57f9ba7711df 100644 --- a/test/clangsa/GCPushPop.cpp +++ b/test/clangsa/GCPushPop.cpp @@ -1,6 +1,6 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license -// RUN: clang --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libGCCheckerPlugin%shlibext -Xclang -verify -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CPPFLAGS} ${CFLAGS} -Xclang -analyzer-checker=core,julia.GCChecker -x c++ %s +// RUN: clang -D__clang_gcanalyzer__ --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libGCCheckerPlugin%shlibext -Xclang -verify -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CPPFLAGS} ${CFLAGS} -Xclang -analyzer-checker=core,julia.GCChecker -x c++ %s #include "julia.h" diff --git a/test/clangsa/MissingRoots.c b/test/clangsa/MissingRoots.c index 78dcc195d59ce..f0b32c54bc7b8 100644 --- a/test/clangsa/MissingRoots.c +++ b/test/clangsa/MissingRoots.c @@ -1,6 +1,6 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license -// RUN: clang --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libGCCheckerPlugin%shlibext -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CPPFLAGS} ${CFLAGS} -Xclang -analyzer-checker=core,julia.GCChecker --analyzer-no-default-checks -Xclang -verify -x c %s +// RUN: clang -D__clang_gcanalyzer__ --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libGCCheckerPlugin%shlibext -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CPPFLAGS} ${CFLAGS} -Xclang -analyzer-checker=core,julia.GCChecker --analyzer-no-default-checks -Xclang -verify -x c %s #include "julia.h" #include "julia_internal.h" diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index fe7a79b5aadef..836fc5e067403 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -46,8 +46,42 @@ end # obtain Vararg with 2 undefined fields let va = ccall(:jl_type_intersection_with_env, Any, (Any, Any), Tuple{Tuple}, Tuple{Tuple{Vararg{Any, N}}} where N)[2][1] - @test Core.Compiler.limit_type_size(Tuple, va, Union{}, 2, 2) === Any -end + @test Core.Compiler.__limit_type_size(Tuple, va, Core.svec(va, Union{}), 2, 2) === Tuple +end + +# issue #42835 +@test !Core.Compiler.type_more_complex(Int, Any, Core.svec(), 1, 1, 1) +@test !Core.Compiler.type_more_complex(Int, Type{Int}, Core.svec(), 1, 1, 1) +@test !Core.Compiler.type_more_complex(Type{Int}, Any, Core.svec(), 1, 1, 1) +@test Core.Compiler.type_more_complex(Type{Type{Int}}, Type{Int}, Core.svec(Type{Int}), 1, 1, 1) +@test Core.Compiler.type_more_complex(Type{Type{Int}}, Int, Core.svec(Type{Int}), 1, 1, 1) +@test Core.Compiler.type_more_complex(Type{Type{Int}}, Any, Core.svec(), 1, 1, 1) +@test Core.Compiler.type_more_complex(Type{Type{Type{Int}}}, Type{Type{Int}}, Core.svec(Type{Type{Int}}), 1, 1, 1) + +@test Core.Compiler.type_more_complex(ComplexF32, Any, Core.svec(), 1, 1, 1) +@test !Core.Compiler.type_more_complex(ComplexF32, Any, Core.svec(Type{ComplexF32}), 1, 1, 1) +@test Core.Compiler.type_more_complex(ComplexF32, Type{ComplexF32}, Core.svec(), 1, 1, 1) +@test !Core.Compiler.type_more_complex(Type{ComplexF32}, Any, Core.svec(Type{Type{ComplexF32}}), 1, 1, 1) +@test Core.Compiler.type_more_complex(Type{ComplexF32}, Type{Type{ComplexF32}}, Core.svec(), 1, 1, 1) +@test !Core.Compiler.type_more_complex(Type{ComplexF32}, ComplexF32, Core.svec(), 1, 1, 1) +@test Core.Compiler.type_more_complex(Type{ComplexF32}, Any, Core.svec(), 1, 1, 1) +@test Core.Compiler.type_more_complex(Type{Type{ComplexF32}}, Type{ComplexF32}, Core.svec(Type{ComplexF32}), 1, 1, 1) +@test Core.Compiler.type_more_complex(Type{Type{ComplexF32}}, ComplexF32, Core.svec(ComplexF32), 1, 1, 1) +@test Core.Compiler.type_more_complex(Type{Type{Type{ComplexF32}}}, Type{Type{ComplexF32}}, Core.svec(Type{ComplexF32}), 1, 1, 1) + +# n.b. Type{Type{Union{}} === Type{Core.TypeofBottom} +@test !Core.Compiler.type_more_complex(Type{Union{}}, Any, Core.svec(), 1, 1, 1) +@test !Core.Compiler.type_more_complex(Type{Type{Union{}}}, Any, Core.svec(), 1, 1, 1) +@test Core.Compiler.type_more_complex(Type{Type{Type{Union{}}}}, Any, Core.svec(), 1, 1, 1) +@test Core.Compiler.type_more_complex(Type{Type{Type{Union{}}}}, Type{Type{Union{}}}, Core.svec(Type{Type{Union{}}}), 1, 1, 1) +@test Core.Compiler.type_more_complex(Type{Type{Type{Type{Union{}}}}}, Type{Type{Type{Union{}}}}, Core.svec(Type{Type{Type{Union{}}}}), 1, 1, 1) + +@test !Core.Compiler.type_more_complex(Type{1}, Type{2}, Core.svec(), 1, 1, 1) +@test Core.Compiler.type_more_complex(Type{Union{Float32,Float64}}, Union{Float32,Float64}, Core.svec(Union{Float32,Float64}), 1, 1, 1) +@test !Core.Compiler.type_more_complex(Type{Union{Float32,Float64}}, Union{Float32,Float64}, Core.svec(Union{Float32,Float64}), 0, 1, 1) +@test_broken Core.Compiler.type_more_complex(Type{<:Union{Float32,Float64}}, Type{Union{Float32,Float64}}, Core.svec(Union{Float32,Float64}), 1, 1, 1) +@test Core.Compiler.type_more_complex(Type{<:Union{Float32,Float64}}, Any, Core.svec(Union{Float32,Float64}), 1, 1, 1) + let # 40336 t = Type{Type{Int}} @@ -3507,3 +3541,70 @@ Foo42097(f::F, args) where {F} = Foo42097{F}() Foo42097(A) = Foo42097(Base.inferencebarrier(+), Base.inferencebarrier(1)...) foo42097() = Foo42097([1]...) @test foo42097() isa Foo42097{typeof(+)} + +# eliminate unbound `TypeVar`s on `argtypes` construction +let + a0(a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, va...) = nothing + method = only(methods(a0)) + unbound = TypeVar(:Unbound, Integer) + specTypes = Tuple{typeof(a0), + # TypeVar + #=01=# Bound, # => Integer + #=02=# unbound, # => Integer (invalid `TypeVar` widened beforehand) + # DataType + #=03=# Type{Bound}, # => Type{Bound} where Bound<:Integer + #=04=# Type{unbound}, # => Type + #=05=# Vector{Bound}, # => Vector{Bound} where Bound<:Integer + #=06=# Vector{unbound}, # => Any + # UnionAll + #=07=# Type{<:Bound}, # => Type{<:Bound} where Bound<:Integer + #=08=# Type{<:unbound}, # => Any + # Union + #=09=# Union{Nothing,Bound}, # => Union{Nothing,Bound} where Bound<:Integer + #=10=# Union{Nothing,unbound}, # => Any + # Vararg + #=va=# Bound, unbound, # => Tuple{Integer,Integer} (invalid `TypeVar` widened beforehand) + } where Bound<:Integer + argtypes = Core.Compiler.most_general_argtypes(method, specTypes, true) + popfirst!(argtypes) + @test argtypes[1] == Integer + @test argtypes[2] == Integer + @test argtypes[3] == Type{Bound} where Bound<:Integer + @test argtypes[4] == Type + @test argtypes[5] == Vector{Bound} where Bound<:Integer + @test argtypes[6] == Any + @test argtypes[7] == Type{<:Bound} where Bound<:Integer + @test argtypes[8] == Any + @test argtypes[9] == Union{Nothing,Bound} where Bound<:Integer + @test argtypes[10] == Any + @test argtypes[11] == Tuple{Integer,Integer} +end + +# make sure not to call `widenconst` on `TypeofVararg` objects +@testset "unhandled Vararg" begin + struct UnhandledVarargCond + val::Bool + end + function Base.:+(a::UnhandledVarargCond, xs...) + if a.val + return nothing + else + s = 0 + for x in xs + s += x + end + return s + end + end + @test Base.return_types((Vector{Int},)) do xs + +(UnhandledVarargCond(false), xs...) + end |> only === Int + + @test (Base.return_types((Vector{Any},)) do xs + Core.kwfunc(xs...) + end; true) + + @test Base.return_types((Vector{Vector{Int}},)) do xs + Tuple(xs...) + end |> only === Tuple{Vararg{Int}} +end diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 475dc8b571442..f157f78a433e7 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -392,3 +392,147 @@ let f(x) = (x...,) # the the original apply call is not union-split, but the inserted `iterate` call is. @test code_typed(f, Tuple{Union{Int64, CartesianIndex{1}, CartesianIndex{3}}})[1][2] == Tuple{Int64} end + +import Core.Compiler: argextype, singleton_type + +code_typed1(args...; kwargs...) = first(only(code_typed(args...; kwargs...)))::Core.CodeInfo +get_code(args...; kwargs...) = code_typed1(args...; kwargs...).code + +# check if `x` is a dynamic call of a given function +function iscall((src, f)::Tuple{Core.CodeInfo,Function}, @nospecialize(x)) + return iscall(x) do @nospecialize x + singleton_type(argextype(x, src, Any[])) === f + end +end +iscall(pred, @nospecialize(x)) = Meta.isexpr(x, :call) && pred(x.args[1]) + +# check if `x` is a statically-resolved call of a function whose name is `sym` +isinvoke(sym::Symbol, @nospecialize(x)) = isinvoke(mi->mi.def.name===sym, x) +isinvoke(pred, @nospecialize(x)) = Meta.isexpr(x, :invoke) && pred(x.args[1]::Core.MethodInstance) + +# https://github.com/JuliaLang/julia/issues/42754 +# inline union-split constant-prop'ed results +mutable struct X42754 + # NOTE in order to confuse `fieldtype_tfunc`, we need to have at least two fields with different types + a::Union{Nothing, Int} + b::Symbol +end +let code = get_code((X42754, Union{Nothing,Int})) do x, a + # this `setproperty` call would be union-split and constant-prop will happen for + # each signature: inlining would fail if we don't use constant-prop'ed source + # since the approximate inlining cost of `convert(fieldtype(X, sym), a)` would + # end up very high if we don't propagate `sym::Const(:a)` + x.a = a + x + end + @test all(code) do @nospecialize(x) + isinvoke(x, :setproperty!) && return false + if Meta.isexpr(x, :call) + f = x.args[1] + isa(f, GlobalRef) && f.name === :setproperty! && return false + end + return true + end +end + +import Base: @constprop + +# test union-split callsite with successful and unsuccessful constant-prop' results +@constprop :aggressive @inline f42840(xs, a::Int) = xs[a] # should be successful, and inlined +@constprop :none @noinline f42840(xs::AbstractVector, a::Int) = xs[a] # should be unsuccessful, but still statically resolved +let src = code_typed1((Union{Tuple{Int,Int,Int}, Vector{Int}},)) do xs + f42840(xs, 2) + end + @test count(src.code) do @nospecialize x + iscall((src, getfield), x) # `(xs::Tuple{Int,Int,Int})[a::Const(2)]` => `getfield(xs, 2)` + end == 1 + @test count(src.code) do @nospecialize x + isinvoke(:f42840, x) + end == 1 +end +# a bit weird, but should handle this kind of case as well +@constprop :aggressive @noinline g42840(xs, a::Int) = xs[a] # should be successful, but only statically resolved +@constprop :none @inline g42840(xs::AbstractVector, a::Int) = xs[a] # should be unsuccessful, still inlined +let src = code_typed1((Union{Tuple{Int,Int,Int}, Vector{Int}},)) do xs + g42840(xs, 2) + end + @test count(src.code) do @nospecialize x + iscall((src, Base.arrayref), x) # `(xs::Vector{Int})[a::Const(2)]` => `Base.arrayref(true, xs, 2)` + end == 1 + @test count(src.code) do @nospecialize x + isinvoke(:g42840, x) + end == 1 +end + +# test single, non-dispatchtuple callsite inlining + +@constprop :none @inline test_single_nondispatchtuple(@nospecialize(t)) = + isa(t, DataType) && t.name === Type.body.name +let + code = get_code((Any,)) do x + test_single_nondispatchtuple(x) + end + @test all(code) do @nospecialize(x) + isinvoke(x, :test_single_nondispatchtuple) && return false + if Meta.isexpr(x, :call) + f = x.args[1] + isa(f, GlobalRef) && f.name === :test_single_nondispatchtuple && return false + end + return true + end +end + +@constprop :aggressive @inline test_single_nondispatchtuple(c, @nospecialize(t)) = + c && isa(t, DataType) && t.name === Type.body.name +let + code = get_code((Any,)) do x + test_single_nondispatchtuple(true, x) + end + @test all(code) do @nospecialize(x) + isinvoke(x, :test_single_nondispatchtuple) && return false + if Meta.isexpr(x, :call) + f = x.args[1] + isa(f, GlobalRef) && f.name === :test_single_nondispatchtuple && return false + end + return true + end +end + +# force constant-prop' for `setproperty!` +let m = Module() + code = @eval m begin + # if we don't force constant-prop', `T = fieldtype(Foo, ::Symbol)` will be union-split to + # `Union{Type{Any},Type{Int}` and it will make `convert(T, nothing)` too costly + # and it leads to inlining failure + mutable struct Foo + val + _::Int + end + + function setter(xs) + for x in xs + x.val = nothing + end + end + + $get_code(setter, (Vector{Foo},)) + end + + @test !any(x->isinvoke(x, :setproperty!), code) +end + +# validate inlining processing + +@constprop :none @inline validate_unionsplit_inlining(@nospecialize(t)) = throw("invalid inlining processing detected") +@constprop :none @noinline validate_unionsplit_inlining(i::Integer) = (println(IOBuffer(), "prevent inlining"); false) +let + invoke(xs) = validate_unionsplit_inlining(xs[1]) + @test invoke(Any[10]) === false +end + +@constprop :aggressive @inline validate_unionsplit_inlining(c, @nospecialize(t)) = c && throw("invalid inlining processing detected") +@constprop :aggressive @noinline validate_unionsplit_inlining(c, i::Integer) = c && (println(IOBuffer(), "prevent inlining"); false) +let + invoke(xs) = validate_unionsplit_inlining(true, xs[1]) + @test invoke(Any[10]) === false +end diff --git a/test/fastmath.jl b/test/fastmath.jl index edaab1c6eb0cf..e93fb93330b4f 100644 --- a/test/fastmath.jl +++ b/test/fastmath.jl @@ -249,3 +249,13 @@ end @test (@fastmath "a" * "b") == "ab" @test (@fastmath "a" ^ 2) == "aa" end + + +@testset "exp overflow and underflow" begin + for T in (Float32,Float64) + for func in (@fastmath exp2,exp,exp10) + @test func(T(2000)) == T(Inf) + @test func(T(-2000)) == T(0) + end + end +end diff --git a/test/file.jl b/test/file.jl index caaeb0a12e9c3..af2909ba13f64 100644 --- a/test/file.jl +++ b/test/file.jl @@ -91,13 +91,15 @@ using Random end @testset "tempname with parent" begin - t = tempname() - @test dirname(t) == tempdir() - mktempdir() do d - t = tempname(d) - @test dirname(t) == d + withenv("TMPDIR" => nothing) do + t = tempname() + @test dirname(t) == tempdir() + mktempdir() do d + t = tempname(d) + @test dirname(t) == d + end + @test_throws ArgumentError tempname(randstring()) end - @test_throws ArgumentError tempname(randstring()) end child_eval(code::String) = eval(Meta.parse(readchomp(`$(Base.julia_cmd()) -E $code`))) diff --git a/test/iterators.jl b/test/iterators.jl index 86c325a85b617..fb6f213fae80d 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -202,6 +202,7 @@ end @test collect(takewhile(Returns(true),5:10)) == 5:10 @test collect(takewhile(isodd,[1,1,2,3])) == [1,1] @test collect(takewhile(<(2), takewhile(<(3), [1,1,2,3]))) == [1,1] + @test Base.IteratorEltype(typeof(takewhile(<(4),Iterators.map(identity, 1:10)))) isa Base.EltypeUnknown end # dropwhile @@ -214,6 +215,7 @@ end @test isempty(dropwhile(Returns(true), 1:3)) @test collect(dropwhile(isodd,[1,1,2,3])) == [2,3] @test collect(dropwhile(iseven,dropwhile(isodd,[1,1,2,3]))) == [3] + @test Base.IteratorEltype(typeof(dropwhile(<(4),Iterators.map(identity, 1:10)))) isa Base.EltypeUnknown end # cycle diff --git a/test/misc.jl b/test/misc.jl index 94c35c43ffaec..8a267b8be9762 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -1008,12 +1008,25 @@ end # Test that read fault on a prot-none region does not incorrectly give # ReadOnlyMemoryEror, but rather crashes the program const MAP_ANONYMOUS_PRIVATE = Sys.isbsd() ? 0x1002 : 0x22 -let script = :(let ptr = Ptr{Cint}(ccall(:jl_mmap, Ptr{Cvoid}, - (Ptr{Cvoid}, Csize_t, Cint, Cint, Cint, Int), - C_NULL, 16*1024, 0, $MAP_ANONYMOUS_PRIVATE, -1, 0)); try - unsafe_load(ptr) - catch e; println(e) end; end) - @test !success(`$(Base.julia_cmd()) -e $script`) +let script = :( + let ptr = Ptr{Cint}(ccall(:jl_mmap, Ptr{Cvoid}, + (Ptr{Cvoid}, Csize_t, Cint, Cint, Cint, Int), + C_NULL, 16*1024, 0, $MAP_ANONYMOUS_PRIVATE, -1, 0)) + try + unsafe_load(ptr) + catch e + println(e) + end + end + ) + cmd = if Sys.isunix() + # Set the maximum core dump size to 0 to keep this expected crash from + # producing a (and potentially overwriting an existing) core dump file + `sh -c "ulimit -c 0; $(Base.shell_escape(Base.julia_cmd())) -e '$script'"` + else + `$(Base.julia_cmd()) -e '$script'` + end + @test !success(cmd) end # issue #41656 diff --git a/test/runtests.jl b/test/runtests.jl index ea94fca877057..4dea6fb4d535e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -10,7 +10,7 @@ using Base: Experimental include("choosetests.jl") include("testenv.jl") -tests, net_on, exit_on_error, use_revise, seed = choosetests(ARGS) +(; tests, net_on, exit_on_error, use_revise, seed) = choosetests(ARGS) tests = unique(tests) if Sys.islinux() diff --git a/test/show.jl b/test/show.jl index b9f8c45797b35..40dd0f41fdfda 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1347,6 +1347,20 @@ test_repr("(:).a") @test repr(Tuple{Float64, Float64, Float64, Float64}) == "NTuple{4, Float64}" @test repr(Tuple{Float32, Float32, Float32}) == "Tuple{Float32, Float32, Float32}" +@testset "issue #42931" begin + @test repr(NTuple{4, :A}) == "NTuple{4, :A}" + @test repr(NTuple{3, :A}) == "Tuple{:A, :A, :A}" + @test repr(NTuple{2, :A}) == "Tuple{:A, :A}" + @test repr(NTuple{1, :A}) == "Tuple{:A}" + @test repr(NTuple{0, :A}) == "Tuple{}" + + @test repr(Tuple{:A, :A, :A, :B}) == "Tuple{:A, :A, :A, :B}" + @test repr(Tuple{:A, :A, :A, :A}) == "NTuple{4, :A}" + @test repr(Tuple{:A, :A, :A}) == "Tuple{:A, :A, :A}" + @test repr(Tuple{:A}) == "Tuple{:A}" + @test repr(Tuple{}) == "Tuple{}" +end + # Test that REPL/mime display of invalid UTF-8 data doesn't throw an exception: @test isa(repr("text/plain", String(UInt8[0x00:0xff;])), String) diff --git a/test/syntax.jl b/test/syntax.jl index 9c5a41e8fb9cd..afea3b6fcb2e1 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -2493,6 +2493,10 @@ import .Mod: x as x2 @test x2 == 1 @test !@isdefined(x) +module_names = names(@__MODULE__; all=true, imported=true) +@test :x2 ∈ module_names +@test :x ∉ module_names + import .Mod2.y as y2 @test y2 == 2 @@ -2960,3 +2964,21 @@ macro m42220() end @test @m42220()() isa Vector{Float64} @test @m42220()(Bool) isa Vector{Bool} + +@test_throws ParseError Meta.parse(""" +function checkUserAccess(u::User) + if u.accessLevel != "user\u202e \u2066# users are not allowed\u2069\u2066" + return true + end + return false +end +""") + +@test_throws ParseError Meta.parse(""" +function checkUserAccess(u::User) + #=\u202e \u2066if (u.isAdmin)\u2069 \u2066 begin admins only =# + return true + #= end admin only \u202e \u2066end\u2069 \u2066=# + return false +end +""")