diff --git a/.buildkite/pipelines/experimental/0_webui.yml b/.buildkite/pipelines/experimental/0_webui.yml index 54dbbc59d4256..eee9743d39f6a 100644 --- a/.buildkite/pipelines/experimental/0_webui.yml +++ b/.buildkite/pipelines/experimental/0_webui.yml @@ -4,7 +4,6 @@ agents: queue: "julia" sandbox.jl: "true" - steps: - label: ":unlock: Unlock secrets, launch pipelines" plugins: @@ -16,9 +15,8 @@ steps: # pipelines. unsigned_pipelines: - .buildkite/pipelines/experimental/launch_unsigned_builders.yml - # Our signed pipelines must have a `signature` or `signature_file` parameter that # verifies the treehash of the pipeline itself and the inputs listed in `inputs` # signed_pipelines: # - pipeline: .buildkite/pipelines/experimental/misc/foo_bar_baz.yml - # signature: "my_signature" + # signature_file: .buildkite/pipelines/experimental/misc/foo_bar_baz.yml.signature diff --git a/.buildkite/pipelines/experimental/misc/sanitizers.yml b/.buildkite/pipelines/experimental/misc/sanitizers.yml index 67c0b547d4b20..79af4905416fc 100644 --- a/.buildkite/pipelines/experimental/misc/sanitizers.yml +++ b/.buildkite/pipelines/experimental/misc/sanitizers.yml @@ -3,10 +3,9 @@ agents: # Only run on `sandbox.jl` machines (not `docker`-isolated ones) since we need nestable sandboxing sandbox.jl: "true" os: "linux" - steps: - label: "asan" - key: asan + key: "asan" plugins: - JuliaCI/julia#v1: version: 1.6 @@ -17,15 +16,10 @@ steps: gid: 1000 workspaces: - "/cache/repos:/cache/repos" - # `contrib/check-asan.jl` needs a `julia` binary: + # `contrib/check-asan.jl` needs a `julia` binary inside the inner sandbox: - JuliaCI/julia#v1: version: 1.6 + timeout_in_minutes: 120 commands: | echo "--- Build julia-debug with ASAN" - contrib/asan/build.sh ./tmp/test-asan -j$${JULIA_NUM_CORES} debug - echo "--- Test that ASAN is enabled" - contrib/asan/check.jl ./tmp/test-asan/asan/usr/bin/julia-debug - timeout_in_minutes: 120 - # notify: # TODO: uncomment this line - # - github_commit_status: # TODO: uncomment this line - # context: "asan" # TODO: uncomment this line + contrib/asan/build.sh ./tmp/test-asan -j$${JULIA_CPU_THREADS:?} debug diff --git a/.buildkite/pipelines/main/0_webui.yml b/.buildkite/pipelines/main/0_webui.yml index 8e7b9c58ea423..af68158f9a51f 100644 --- a/.buildkite/pipelines/main/0_webui.yml +++ b/.buildkite/pipelines/main/0_webui.yml @@ -4,7 +4,6 @@ agents: queue: "julia" sandbox.jl: "true" - steps: - label: ":unlock: Unlock secrets, launch pipelines" plugins: diff --git a/.buildkite/pipelines/main/launch_unsigned_builders.yml b/.buildkite/pipelines/main/launch_unsigned_builders.yml index 6e9f0f0d8fa23..f978b0c48ed77 100644 --- a/.buildkite/pipelines/main/launch_unsigned_builders.yml +++ b/.buildkite/pipelines/main/launch_unsigned_builders.yml @@ -15,15 +15,16 @@ steps: - label: ":buildkite: Launch unsigned builders" commands: | - # First, we launch the `whitespace` builder, because we want that builder to finish as quickly as possible. + # Launch the `whitespace` builder first, because we want that builder to finish as quickly as possible. buildkite-agent pipeline upload .buildkite/pipelines/main/misc/whitespace.yml - # Next, we launch the miscellaneous builders in alphabetical order. + # Launch the miscellaneous builders in alphabetical order. buildkite-agent pipeline upload .buildkite/pipelines/main/misc/doctest.yml buildkite-agent pipeline upload .buildkite/pipelines/main/misc/embedding.yml buildkite-agent pipeline upload .buildkite/pipelines/main/misc/llvmpasses.yml - # Finally, we launch the platform builders (`package_*`) and (`tester_*`) in alphabetical order. - buildkite-agent pipeline upload .buildkite/pipelines/main/platforms/linux64.yml + # Launch all of the platform builders. + bash .buildkite/pipelines/main/platforms/platforms.sh package_linux + bash .buildkite/pipelines/main/platforms/platforms.sh tester_linux agents: queue: julia diff --git a/.buildkite/pipelines/main/misc/doctest.yml b/.buildkite/pipelines/main/misc/doctest.yml index 0a5dc29bcb1c7..e1af8d7cd839f 100644 --- a/.buildkite/pipelines/main/misc/doctest.yml +++ b/.buildkite/pipelines/main/misc/doctest.yml @@ -3,7 +3,6 @@ agents: # Only run on `sandbox.jl` machines (not `docker`-isolated ones) since we need nestable sandboxing sandbox.jl: "true" os: "linux" - steps: - label: "doctest" key: doctest @@ -31,6 +30,3 @@ steps: echo "--- Run Julia doctests" JULIA_NUM_THREADS=1 make -C doc doctest=true timeout_in_minutes: 45 - notify: - - github_commit_status: - context: "doctest" diff --git a/.buildkite/pipelines/main/misc/embedding.yml b/.buildkite/pipelines/main/misc/embedding.yml index 087ca0f68eb3d..dc5e80e85d065 100644 --- a/.buildkite/pipelines/main/misc/embedding.yml +++ b/.buildkite/pipelines/main/misc/embedding.yml @@ -3,7 +3,6 @@ agents: # Only run on `sandbox.jl` machines (not `docker`-isolated ones) since we need nestable sandboxing sandbox.jl: "true" os: "linux" - steps: - label: "embedding" key: "embedding" @@ -20,15 +19,11 @@ steps: - "/cache/repos:/cache/repos" commands: | prefix="/tmp/prefix" - echo "+++ Build julia, deploy to $${prefix}" - make -j$${JULIA_NUM_CORES} JULIA_PRECOMPILE=0 prefix=$${prefix} install + echo "+++ Build julia, deploy to $${prefix:?}" + make -j$${JULIA_CPU_THREADS:?} JULIA_PRECOMPILE=0 prefix=$${prefix:?} install embedding_output="/tmp/embedding-test" - echo "+++ Run embedding tests, deploy to $${embedding_output}" - mkdir -p "$${embedding_output}" - make -j$${JULIA_NUM_CORES} -C test/embedding JULIA="$${prefix}/bin/julia" BIN="$${embedding_output}" - + echo "+++ Run embedding tests, deploy to $${embedding_output:?}" + mkdir -p "$${embedding_output:?}" + make -j$${JULIA_CPU_THREADS:?} -C test/embedding JULIA="$${prefix:?}/bin/julia" BIN="$${embedding_output:?}" timeout_in_minutes: 60 - notify: - - github_commit_status: - context: "embedding" diff --git a/.buildkite/pipelines/main/misc/llvmpasses.yml b/.buildkite/pipelines/main/misc/llvmpasses.yml index a012ace41acff..921f39c5fb360 100644 --- a/.buildkite/pipelines/main/misc/llvmpasses.yml +++ b/.buildkite/pipelines/main/misc/llvmpasses.yml @@ -3,7 +3,6 @@ agents: # Only run on `sandbox.jl` machines (not `docker`-isolated ones) since we need nestable sandboxing sandbox.jl: "true" os: "linux" - steps: - label: "analyzegc" key: "analyzegc" @@ -18,15 +17,11 @@ steps: - "/cache/repos:/cache/repos" commands: | echo "--- Install in-tree LLVM dependencies" - make -j$${JULIA_NUM_CORES} -C deps install-llvm install-clang install-llvm-tools install-libuv install-utf8proc install-unwind + make -j$${JULIA_CPU_THREADS:?} -C deps install-llvm install-clang install-llvm-tools install-libuv install-utf8proc install-unwind echo "+++ run clangsa/analyzegc" - make -j$${JULIA_NUM_CORES} -C test/clangsa - make -j$${JULIA_NUM_CORES} -C src analyzegc + make -j$${JULIA_CPU_THREADS:?} -C test/clangsa + make -j$${JULIA_CPU_THREADS:?} -C src analyzegc timeout_in_minutes: 60 - notify: - - github_commit_status: - context: "analyzegc" - - label: "llvmpasses" key: "llvmpasses" plugins: @@ -41,12 +36,9 @@ steps: - "/cache/repos:/cache/repos" commands: | echo "--- make release" - make -j$${JULIA_NUM_CORES} release JULIA_PRECOMPILE=0 + make -j$${JULIA_CPU_THREADS:?} release JULIA_PRECOMPILE=0 echo "--- make src/install-analysis-deps" - make -j$${JULIA_NUM_CORES} -C src install-analysis-deps + make -j$${JULIA_CPU_THREADS:?} -C src install-analysis-deps echo "+++ make test/llvmpasses" - make -j$${JULIA_NUM_CORES} -C test/llvmpasses + make -j$${JULIA_CPU_THREADS:?} -C test/llvmpasses timeout_in_minutes: 60 - notify: - - github_commit_status: - context: "llvmpasses" diff --git a/.buildkite/pipelines/main/misc/whitespace.yml b/.buildkite/pipelines/main/misc/whitespace.yml index 3f9bf13421d8e..89231b80cc48d 100644 --- a/.buildkite/pipelines/main/misc/whitespace.yml +++ b/.buildkite/pipelines/main/misc/whitespace.yml @@ -3,7 +3,6 @@ agents: # Only run on `sandbox.jl` machines (not `docker`-isolated ones) since we need nestable sandboxing sandbox.jl: "true" os: "linux" - steps: - label: "whitespace" key: "whitespace" @@ -15,9 +14,9 @@ steps: rootfs_treehash: "8c33c341a864852629b8aac01a6eb6a79b73570e" workspaces: - "/cache/repos:/cache/repos" - commands: | - make -j$${JULIA_NUM_CORES} check-whitespace timeout_in_minutes: 10 notify: - github_commit_status: context: "whitespace" + commands: | + make -j$${JULIA_CPU_THREADS:?} check-whitespace diff --git a/.buildkite/pipelines/main/platforms/linux64.yml b/.buildkite/pipelines/main/platforms/linux64.yml deleted file mode 100644 index ad5d32a20b3a0..0000000000000 --- a/.buildkite/pipelines/main/platforms/linux64.yml +++ /dev/null @@ -1,95 +0,0 @@ -agents: - queue: "julia" - # Only run on `sandbox.jl` machines (not `docker`-isolated ones) since we need nestable sandboxing - sandbox.jl: "true" - os: "linux" - -steps: - - label: "package_linux64" - key: package_linux64 - 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/v3.1/package_linux.x86_64.tar.gz - rootfs_treehash: "8c33c341a864852629b8aac01a6eb6a79b73570e" - uid: 1000 - gid: 1000 - workspaces: - # Include `/cache/repos` so that our `git` version introspection works. - - "/cache/repos:/cache/repos" - commands: | - echo "--- Print the short and long commit hashes" - SHORT_COMMIT_LENGTH=10 - SHORT_COMMIT=`echo $$BUILDKITE_COMMIT | cut -c1-$$SHORT_COMMIT_LENGTH` - JULIA_DIRECTORY_NAME="julia-$$SHORT_COMMIT" - JULIA_BINARYDIST_FILENAME=`make print-JULIA_BINARYDIST_FILENAME | cut -c27-` - ARTIFACT_FILE_EXTENSION="tar.gz" - ARTIFACT_FILENAME="$$JULIA_BINARYDIST_FILENAME.$$ARTIFACT_FILE_EXTENSION" - echo "The full commit is $$BUILDKITE_COMMIT" - echo "The Julia directory name will be $$JULIA_DIRECTORY_NAME" - echo "The artifact filename will be $$ARTIFACT_FILENAME" - - echo "--- Build Julia from source" - make -j 6 - make release - make install - - echo "--- Make sure that the working directory is clean" - if [ -z "$(git status --short)" ]; then echo "INFO: The working directory is clean."; else echo "ERROR: The working directory is dirty."; echo "Output of git status:"; git status; exit 1; fi - - echo "--- Print Julia version info" - ./julia -e 'using InteractiveUtils; InteractiveUtils.versioninfo()' - - echo "--- Compress build artifacts" - ls -ld $$JULIA_DIRECTORY_NAME/ - rm -rf $$ARTIFACT_FILENAME - tar czf $$ARTIFACT_FILENAME $$JULIA_DIRECTORY_NAME/ - ls -l $$ARTIFACT_FILENAME - - echo "--- Upload build artifacts" - buildkite-agent artifact upload $$ARTIFACT_FILENAME - timeout_in_minutes: 60 - notify: - - github_commit_status: - context: "package_linux64" - - # TODO: uncomment the following lines in order to enable the `tester_linux64` builder - # - label: "tester_linux64" - # key: tester_linux64 - # depends_on: package_linux64 - # plugins: - # - JuliaCI/julia#v1: - # version: 1.6 - # - staticfloat/sandbox#v1: - # # TODO: use a separate `tester_linux` image, instead of using the `package_linux` image. - # rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v3.1/package_linux.x86_64.tar.gz - # rootfs_treehash: "8c33c341a864852629b8aac01a6eb6a79b73570e" - # uid: 1000 - # gid: 1000 - # workspaces: - # # Include `/cache/repos` so that our `git` version introspection works. - # - "/cache/repos:/cache/repos" - # env: - # JULIA_SHELL: "/bin/bash" - # commands: | - # echo "--- Download build artifacts" - # rm -rf julia-linux64.tar.gz - # buildkite-agent artifact download julia-linux64.tar.gz . - # - # echo "--- Extract build artifacts" - # rm -rf julia-artifact/ - # tar xzf julia-linux64.tar.gz julia-artifact/ - # - # echo "--- Print Julia version info" - # julia-artifact/bin/julia -e 'using InteractiveUtils; InteractiveUtils.versioninfo()' - # - # echo "--- Run the Julia test suite" - # unset JULIA_DEPOT_PATH - # julia-artifact/bin/julia .buildkite/utilities/rr/rr_capture.jl julia-artifact/bin/julia -e 'Base.runtests(["all"]; ncores = Sys.CPU_THREADS)' - # timeout_in_minutes: 120 - # notify: - # - github_commit_status: - # context: "tester_linux64" diff --git a/.buildkite/pipelines/main/platforms/package_linux.arches b/.buildkite/pipelines/main/platforms/package_linux.arches new file mode 100644 index 0000000000000..d83bd798242ac --- /dev/null +++ b/.buildkite/pipelines/main/platforms/package_linux.arches @@ -0,0 +1,6 @@ +# ARCH ARCH_LABEL ROOTFS_ARCH TIMEOUT ROOTFS_TAG ROOTFS_TREE +# aarch64 aarch64 aarch64 60 v3.2 0566841e29f0f9880541c26a6595fd5ce0beb5ff +# armv7l armv7l armv7l 60 v3.2 fb359370b052a47ce5c84cc6b4a7a03ed7053b25 +32 32 i686 60 v3.2 209c4db679a515befd7fb50ecc6bfbecf7ec3d32 +# ppc64le ppc64le powerpc64le 60 v3.2 c03a0158b19d48ac84b426834fce0d3584cdd0c7 +64 64 x86_64 60 v3.2 474bf61a926b2d7fcf202284d59d4b11a04601d7 diff --git a/.buildkite/pipelines/main/platforms/package_linux.yml b/.buildkite/pipelines/main/platforms/package_linux.yml new file mode 100644 index 0000000000000..a054390bb2c0c --- /dev/null +++ b/.buildkite/pipelines/main/platforms/package_linux.yml @@ -0,0 +1,49 @@ +agents: + queue: "julia" + # Only run on `sandbox.jl` machines (not `docker`-isolated ones) since we need nestable sandboxing + sandbox.jl: "true" + os: "linux" +steps: + - label: "package_linux${ARCH_LABEL?}" + key: package_linux${ARCH_LABEL?} + 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/${ROOTFS_TAG?}/package_linux.${ROOTFS_ARCH?}.tar.gz + rootfs_treehash: "${ROOTFS_TREE?}" + uid: 1000 + gid: 1000 + workspaces: + # Include `/cache/repos` so that our `git` version introspection works. + - "/cache/repos:/cache/repos" + timeout_in_minutes: ${TIMEOUT?} + commands: | + echo "--- Print the full and short commit hashes" + SHORT_COMMIT_LENGTH=10 + SHORT_COMMIT=`echo $${BUILDKITE_COMMIT:?} | cut -c1-$${SHORT_COMMIT_LENGTH:?}` + JULIA_BINARYDIST_FILENAME=`make print-JULIA_BINARYDIST_FILENAME | cut -c27-` + ARTIFACT_FILE_EXTENSION="tar.gz" + ARTIFACT_FILENAME="$${JULIA_BINARYDIST_FILENAME:?}.$${ARTIFACT_FILE_EXTENSION:?}" + echo "The full commit is: $${BUILDKITE_COMMIT:?}" + echo "The short commit is: $${SHORT_COMMIT:?}" + echo "The artifact filename will be: $${ARTIFACT_FILENAME:?}" + + echo "--- Build Julia from source" + rm -rf $${ARTIFACT_FILENAME:?} + make -j 8 + + echo "--- Make sure that the working directory is clean" + if [ -z "$(git status --short)" ]; then echo "INFO: The working directory is clean."; else echo "ERROR: The working directory is dirty."; echo "Output of git status:"; git status; exit 1; fi + + echo "--- Print Julia version info" + ./julia -e 'using InteractiveUtils; InteractiveUtils.versioninfo()' + + echo "--- Create build artifacts" + make -j 8 binary-dist + ls -l $${ARTIFACT_FILENAME:?} + + echo "--- Upload build artifacts" + buildkite-agent artifact upload $${ARTIFACT_FILENAME:?} diff --git a/.buildkite/pipelines/main/platforms/platforms.sh b/.buildkite/pipelines/main/platforms/platforms.sh new file mode 100755 index 0000000000000..d58daeb2a9929 --- /dev/null +++ b/.buildkite/pipelines/main/platforms/platforms.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" + +PLATFORM="$1" + +cat "$SCRIPT_DIR/$PLATFORM.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 `#` + if [[ $line == \#* ]]; then + continue + fi + + export ARCH=`echo $line | cut -d ' ' -f 1` + export ARCH_LABEL=`echo $line | cut -d ' ' -f 2` + export ROOTFS_ARCH=`echo $line | cut -d ' ' -f 3` + export TIMEOUT=`echo $line | cut -d ' ' -f 4` + export ROOTFS_TAG=`echo $line | cut -d ' ' -f 5` + export ROOTFS_TREE=`echo $line | cut -d ' ' -f 6` + echo "Launching: $PLATFORM $ARCH $ARCH_LABEL $ROOTFS_ARCH $TIMEOUT" + buildkite-agent pipeline upload "$SCRIPT_DIR/$PLATFORM.yml" +done diff --git a/.buildkite/pipelines/main/platforms/tester_linux.arches b/.buildkite/pipelines/main/platforms/tester_linux.arches new file mode 100644 index 0000000000000..7d8e8ba607533 --- /dev/null +++ b/.buildkite/pipelines/main/platforms/tester_linux.arches @@ -0,0 +1,8 @@ +# ARCH ARCH_LABEL ROOTFS_ARCH TIMEOUT ROOTFS_TAG ROOTFS_TREE +# aarch64 aarch64 aarch64 60 v3.2 0566841e29f0f9880541c26a6595fd5ce0beb5ff +# armv7l armv7l armv7l 60 v3.2 fb359370b052a47ce5c84cc6b4a7a03ed7053b25 +32 32 i686 60 v3.2 209c4db679a515befd7fb50ecc6bfbecf7ec3d32 +# ppc64le ppc64le powerpc64le 60 v3.2 c03a0158b19d48ac84b426834fce0d3584cdd0c7 +64 64_rr x86_64 180 v3.2 474bf61a926b2d7fcf202284d59d4b11a04601d7 +64 64_st x86_64 60 v3.2 474bf61a926b2d7fcf202284d59d4b11a04601d7 +64 64_mt x86_64 60 v3.2 474bf61a926b2d7fcf202284d59d4b11a04601d7 diff --git a/.buildkite/pipelines/main/platforms/tester_linux.yml b/.buildkite/pipelines/main/platforms/tester_linux.yml new file mode 100644 index 0000000000000..3a71599ad6f0a --- /dev/null +++ b/.buildkite/pipelines/main/platforms/tester_linux.yml @@ -0,0 +1,72 @@ +agents: + queue: "julia" + # Only run on `sandbox.jl` machines (not `docker`-isolated ones) since we need nestable sandboxing + sandbox.jl: "true" + os: "linux" +steps: + - label: "tester_linux${ARCH_LABEL?}" + key: tester_linux${ARCH_LABEL?} + depends_on: package_linux${ARCH?} + 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/${ROOTFS_TAG?}/package_linux.${ROOTFS_ARCH?}.tar.gz + rootfs_treehash: "${ROOTFS_TREE?}" + uid: 1000 + gid: 1000 + workspaces: + # Include `/cache/repos` so that our `git` version introspection works. + - "/cache/repos:/cache/repos" + env: + JULIA_SHELL: "/bin/bash" + timeout_in_minutes: ${TIMEOUT?} + commands: | + echo "--- Print the full and short commit hashes" + SHORT_COMMIT_LENGTH=10 + SHORT_COMMIT=`echo $${BUILDKITE_COMMIT:?} | cut -c1-$${SHORT_COMMIT_LENGTH:?}` + JULIA_DIR="julia-$${SHORT_COMMIT:?}" + JULIA_BINARY="$${JULIA_DIR:?}/bin/julia" + ARTIFACT_FILE_EXTENSION="tar.gz" + ARTIFACT_FILENAME="julia-$${SHORT_COMMIT:?}-linux${ARCH?}.$${ARTIFACT_FILE_EXTENSION:?}" + echo "The full commit is: $${BUILDKITE_COMMIT:?}" + echo "The short commit is: $${SHORT_COMMIT:?}" + echo "The artifact filename will be: $${ARTIFACT_FILENAME:?}" + echo "The Julia directory name will be: $${JULIA_DIR:?}" + echo "The Julia binary will be: $${JULIA_BINARY:?}" + + echo "--- Download build artifacts" + rm -rf $${ARTIFACT_FILENAME:?} + buildkite-agent artifact download $${ARTIFACT_FILENAME:?} . + + echo "--- Extract build artifacts" + rm -rf $${JULIA_DIR:?}/ + tar xzf $${ARTIFACT_FILENAME:?} $${JULIA_DIR:?}/ + + echo "--- Print Julia version info" + $${JULIA_BINARY:?} -e 'using InteractiveUtils; InteractiveUtils.versioninfo()' + echo "JULIA_CPU_THREADS is: $${JULIA_CPU_THREADS:?}" + $${JULIA_BINARY:?} -e '@info "" Sys.CPU_THREADS' + + echo "--- Run the Julia test suite" + unset JULIA_DEPOT_PATH + export JULIA_UNDER_RR="$${JULIA_BINARY:?} .buildkite/utilities/rr/rr_capture.jl $${JULIA_BINARY:?}" + + if [[ "$${BUILDKITE_STEP_KEY:?}" == "tester_linux64_rr" ]]; then + # For the `rr` job, we disable multi-threading. + export JULIA_NUM_THREADS=1 + $${JULIA_UNDER_RR:?} -e 'Base.runtests(["all"]; ncores = parse(Int, ENV["JULIA_RRCAPTURE_NUM_CORES"]))' + elif [[ "$${BUILDKITE_STEP_KEY:?}" == "tester_linux64_st" ]]; then + # "_st" = single-threaded + export JULIA_NUM_THREADS=1 + $${JULIA_BINARY:?} -e 'Base.runtests(["all"]; ncores = Sys.CPU_THREADS)' + elif [[ "$${BUILDKITE_STEP_KEY:?}" == "tester_linux64_mt" ]]; then + # "_mt" = multi-threaded + export JULIA_NUM_THREADS=16 + # TODO: don't skip any tests + $${JULIA_BINARY:?} -e 'Base.runtests(["all", "--skip", "Distributed"]; ncores = Sys.CPU_THREADS)' + else + $${JULIA_BINARY:?} -e 'Base.runtests(["all"]; ncores = Sys.CPU_THREADS)' + fi diff --git a/.buildkite/pipelines/scheduled/0_webui.yml b/.buildkite/pipelines/scheduled/0_webui.yml index 3aa1b575316d8..61324d13d0ca1 100644 --- a/.buildkite/pipelines/scheduled/0_webui.yml +++ b/.buildkite/pipelines/scheduled/0_webui.yml @@ -4,7 +4,6 @@ agents: queue: "julia" sandbox.jl: "true" - steps: - label: ":unlock: Unlock secrets, launch pipelines" plugins: diff --git a/.buildkite/pipelines/scheduled/coverage/coverage_linux64.yml b/.buildkite/pipelines/scheduled/coverage/coverage_linux64.yml index b1e2eaff61497..f5d93264f7aac 100644 --- a/.buildkite/pipelines/scheduled/coverage/coverage_linux64.yml +++ b/.buildkite/pipelines/scheduled/coverage/coverage_linux64.yml @@ -3,7 +3,6 @@ agents: # Only run on `sandbox.jl` machines (not `docker`-isolated ones) since we need nestable sandboxing sandbox.jl: "true" os: "linux" - steps: - label: ":unlock: :coverage: Run coverage test" plugins: diff --git a/.buildkite/utilities/rr/rr_capture.jl b/.buildkite/utilities/rr/rr_capture.jl index 07d57f31ff29c..5d60377a72bb6 100644 --- a/.buildkite/utilities/rr/rr_capture.jl +++ b/.buildkite/utilities/rr/rr_capture.jl @@ -1,44 +1,94 @@ -using Dates -using Pkg -using Tar - -if Base.VERSION < v"1.6" - throw(ErrorException("The `rr_capture.jl` script requires Julia 1.6 or greater")) -end - -if length(ARGS) < 1 - throw(ErrorException("Usage: rr_capture.jl [command...]")) +import Dates +import Pkg +import Tar + +function get_bool_from_env(name::AbstractString, default_value::Bool) + value = get(ENV, name, "$(default_value)") |> strip |> lowercase + result = parse(Bool, value)::Bool + return result end -const TIMEOUT = 2 * 60 * 60 # timeout in seconds +const is_buildkite = get_bool_from_env("BUILDKITE", false) +const always_save_rr_trace = get_bool_from_env("JULIA_ALWAYS_SAVE_RR_TRACE", false) -# We only use `rr` on the `tester_linux64` builder -const use_rr_if_builder_is = "tester_linux64" - -const run_id = get(ENV, "BUILDKITE_JOB_ID", "unknown") -const shortcommit = get(ENV, "BUILDKITE_COMMIT", "unknown") -const builder = get(ENV, "BUILDKITE_STEP_KEY", use_rr_if_builder_is) -const use_rr = builder == use_rr_if_builder_is +function get_from_env(name::AbstractString) + if is_buildkite + value = ENV[name] + else + value = get(ENV, name, "") + end + result = convert(String, strip(value))::String + return result +end -@info "" run_id shortcommit builder use_rr -@info "" ARGS +function my_exit(process::Base.Process) + wait(process) + + @info( + "", + process.exitcode, + process.termsignal, + ) + + # Pass the exit code back up + if process.termsignal != 0 + ccall(:raise, Cvoid, (Cint,), process.termsignal) + + # If for some reason the signal did not cause an exit, we'll exit manually. + # We need to make sure that we exit with a non-zero exit code. + if process.exitcode != 0 + exit(process.exitcode) + else + exit(1) + end + end + exit(process.exitcode) +end -# if !use_rr # TODO: uncomment this line -if true # TODO: delete this line - @info "We will not run the tests under rr" - p = run(`$ARGS`) - exit(p.exitcode) +if Base.VERSION < v"1.6" + throw(ErrorException("The `$(basename(@__FILE__))` script requires Julia 1.6 or greater")) end -@info "We will run the tests under rr" +if length(ARGS) < 1 + throw(ErrorException("Usage: julia $(basename(@__FILE__)) [command...]")) +end -const num_cores = min(Sys.CPU_THREADS, 8, parse(Int, get(ENV, "JULIA_TEST_NUM_CORES", "8")) + 1) -@info "" num_cores +@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 num_cores = min( + 8, + Sys.CPU_THREADS, + julia_test_num_cores_int + 1, +) + +ENV["JULIA_RRCAPTURE_NUM_CORES"] = "$(num_cores)" + +@info( + "", + build_number, + job_name, + commit_full, + commit_short, + timeout_minutes, + num_cores, +) + +const dumps_dir = joinpath(pwd(), "dumps") +const temp_parent_dir = joinpath(pwd(), "temp_for_rr") + +mkpath(dumps_dir) +mkpath(temp_parent_dir) proc = nothing -new_env = copy(ENV) -mktempdir() do dir +mktempdir(temp_parent_dir) do dir Pkg.activate(dir) Pkg.add("rr_jll") Pkg.add("Zstd_jll") @@ -68,18 +118,22 @@ mktempdir() do dir # Start asynchronous timer that will kill `rr` @async begin - sleep(TIMEOUT) + sleep(timeout_minutes * 60) # If we've exceeded the timeout and `rr` is still running, kill it. if isopen(proc) - println(stderr, "\n\nProcess timed out. Signalling `rr` for force-cleanup!") + println(stderr, "\n\nProcess timed out (with a timeout of $(timeout_minutes) minutes). Signalling `rr` for force-cleanup!") kill(proc, Base.SIGTERM) - # Give `rr` a chance to cleanup - sleep(60) + # Give `rr` a chance to cleanup and upload. + # Note: this time period includes the time to upload the `rr` trace files + # as Buildkite artifacts, so make sure it is long enough to allow the + # uploads to finish. + cleanup_minutes = 30 + sleep(cleanup_minutes * 60) if isopen(proc) - println(stderr, "\n\n`rr` failed to cleanup within one minute, killing and exiting immediately!") + println(stderr, "\n\n`rr` failed to cleanup and upload within $(cleanup_minutes) minutes, killing and exiting immediately!") kill(proc, Base.SIGKILL) exit(1) end @@ -87,11 +141,10 @@ mktempdir() do dir end # Wait for `rr` to finish, either through naturally finishing its run, or `SIGTERM`. - # If we have to `SIGKILL` wait(proc) + process_failed = !success(proc) - # On a non-zero exit code, upload the `rr` trace - if !success(proc) + if process_failed || always_save_rr_trace || is_buildkite println(stderr, "`rr` returned $(proc.exitcode), packing and uploading traces...") if !isdir(joinpath(dir, "rr_traces")) @@ -113,22 +166,34 @@ mktempdir() do dir run(ignorestatus(`$(rr_path) pack --pack-dir=$pack_dir $(trace_dirs)`)) # Tar it up - mkpath("dumps") - datestr = Dates.format(now(), dateformat"yyyy-mm-dd_HH_MM_SS") - dst_path = "dumps/rr-run_$(run_id)-gitsha_$(shortcommit)-$(datestr).tar.zst" + mkpath(dumps_dir) + date_str = Dates.format(Dates.now(), Dates.dateformat"yyyy_mm_dd_HH_MM_SS") + dst_file_name = string( + "rr", + "--build_$(build_number)", + "--$(job_name)", + "--commit_$(commit_short)", + "--$(date_str)", + ".tar.zst", + ) + dst_full_path = joinpath(dumps_dir, dst_file_name) zstd_jll.zstdmt() do zstdp - tarproc = open(`$zstdp -o $dst_path`, "w") + tarproc = open(`$(zstdp) -o $(dst_full_path)`, "w") Tar.create(dir, tarproc) close(tarproc.in) end + + @info "The `rr` trace file has been saved to: $(dst_full_path)" + if is_buildkite + @info "Since this is a Buildkite run, we will upload the `rr` trace file." + cd(dumps_dir) do + run(`buildkite-agent artifact upload $(dst_file_name)`) + end + end end + end end -# Pass the exit code back up to Buildkite -if proc.termsignal != 0 - ccall(:raise, Cvoid, (Cint,), proc.termsignal) - exit(1) # Just in case the signal did not cause an exit -else - exit(proc.exitcode) -end +@info "Finished running the command under rr" +my_exit(proc) diff --git a/.github/workflows/retry.yml b/.github/workflows/retry.yml index 2c7f33e886cb2..90160c8ee15e7 100644 --- a/.github/workflows/retry.yml +++ b/.github/workflows/retry.yml @@ -51,7 +51,7 @@ jobs: steps: # For security reasons, we do not checkout any code in this workflow. - - uses: JuliaLang/retry-buildkite@24e8341f74e0d6760717235eac936db639d7e9eb + - uses: JuliaLang/retry-buildkite@057f6f2d37aa29a57b7679fd2af0df1d9f9188b4 with: buildkite_api_token: ${{ secrets.BUILDKITE_API_TOKEN_RETRY }} buildkite_organization_slug: 'julialang' diff --git a/.github/workflows/statuses.yml b/.github/workflows/statuses.yml index ccfda06b1a542..97e7b246d7e65 100644 --- a/.github/workflows/statuses.yml +++ b/.github/workflows/statuses.yml @@ -20,6 +20,7 @@ on: # write permissions, even if the PR is from a fork. # Therefore, for security reasons, we do not checkout any code in this workflow. pull_request_target: + types: [opened, synchronize] branches: - 'master' - 'release-*' diff --git a/Make.inc b/Make.inc index 33bfac180a0fa..8207ce587703f 100644 --- a/Make.inc +++ b/Make.inc @@ -1312,7 +1312,7 @@ ifeq ($(OS), WINNT) ifneq ($(USEMSVC), 1) HAVE_SSP := 1 OSLIBS += -Wl,--export-all-symbols -Wl,--version-script=$(JULIAHOME)/src/julia.expmap \ - $(NO_WHOLE_ARCHIVE) -lpsapi -lkernel32 -lws2_32 -liphlpapi -lwinmm -ldbghelp -luserenv -lsecur32 + $(NO_WHOLE_ARCHIVE) -lpsapi -lkernel32 -lws2_32 -liphlpapi -lwinmm -ldbghelp -luserenv -lsecur32 -latomic JLDFLAGS := -Wl,--stack,8388608 ifeq ($(ARCH),i686) JLDFLAGS += -Wl,--large-address-aware diff --git a/Makefile b/Makefile index 62afa8e685529..d2cc6f93c9b81 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ all: debug release # sort is used to remove potential duplicates DIRS := $(sort $(build_bindir) $(build_depsbindir) $(build_libdir) $(build_private_libdir) $(build_libexecdir) $(build_includedir) $(build_includedir)/julia $(build_sysconfdir)/julia $(build_datarootdir)/julia $(build_datarootdir)/julia/stdlib $(build_man1dir)) ifneq ($(BUILDROOT),$(JULIAHOME)) -BUILDDIRS := $(BUILDROOT) $(addprefix $(BUILDROOT)/,base src src/flisp src/support src/clangsa cli doc deps stdlib test test/embedding test/llvmpasses) +BUILDDIRS := $(BUILDROOT) $(addprefix $(BUILDROOT)/,base src src/flisp src/support src/clangsa cli doc deps stdlib test test/clangsa test/embedding test/llvmpasses) BUILDDIRMAKE := $(addsuffix /Makefile,$(BUILDDIRS)) $(BUILDROOT)/sysimage.mk DIRS := $(DIRS) $(BUILDDIRS) $(BUILDDIRMAKE): | $(BUILDDIRS) diff --git a/NEWS.md b/NEWS.md index b1442f0192350..e49a28fe0fd1b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,7 +5,7 @@ Julia v1.8 Release Notes New language features --------------------- -* `Module(:name, false, false)` can be used to create a `module` that does not import `Core`. ([#40110]) +* `Module(:name, false, false)` can be used to create a `module` that contains no names (it does not import `Base` or `Core` and does not contain a reference to itself). ([#40110, #42154]) * `@inline` and `@noinline` annotations can be used within a function body to give an extra hint about the inlining cost to the compiler. ([#41312]) * `@inline` and `@noinline` annotations can now be applied to a function callsite or block @@ -16,6 +16,9 @@ New language features Language changes ---------------- +* Newly created Task objects (`@spawn`, `@async`, etc.) now adopt the world-age for methods from their parent + Task upon creation, instead of using the global latest world at start. This is done to enable inference to + eventually optimize these calls. Places that wish for the old behavior may use `Base.invokelatest`. ([#41449]) Compiler/Runtime improvements ----------------------------- @@ -78,7 +81,7 @@ Standard library changes #### Random #### REPL - +* `RadioMenu` now supports optional `keybindings` to directly select options ([#41576]). * ` ?(x, y` followed by TAB displays all methods that can be called with arguments `x, y, ...`. (The space at the beginning prevents entering help-mode.) `MyModule.?(x, y` limits the search to `MyModule`. TAB requires that at least one diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 5ded231fa2be2..0816dc898ee80 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3067,29 +3067,9 @@ end ## keepat! ## -""" - keepat!(a::AbstractVector, inds) - -Remove the items at all the indices which are not given by `inds`, -and return the modified `a`. -Items which are kept are shifted to fill the resulting gaps. - -`inds` must be an iterator of sorted and unique integer indices. -See also [`deleteat!`](@ref). +# NOTE: since these use `@inbounds`, they are actually only intended for Vector and BitVector -!!! compat "Julia 1.7" - This function is available as of Julia 1.7. - -# Examples -```jldoctest -julia> keepat!([6, 5, 4, 3, 2, 1], 1:2:5) -3-element Vector{Int64}: - 6 - 4 - 2 -``` -""" -function keepat!(a::AbstractVector, inds) +function _keepat!(a::AbstractVector, inds) local prev i = firstindex(a) for k in inds @@ -3106,3 +3086,18 @@ function keepat!(a::AbstractVector, inds) deleteat!(a, i:lastindex(a)) return a end + +function _keepat!(a::AbstractVector, m::AbstractVector{Bool}) + length(m) == length(a) || throw(BoundsError(a, m)) + j = firstindex(a) + for i in eachindex(a, m) + @inbounds begin + if m[i] + i == j || (a[j] = a[i]) + j = nextind(a, j) + end + end + end + deleteat!(a, j:lastindex(a)) + return a +end diff --git a/base/abstractset.jl b/base/abstractset.jl index e3596597b438f..c4ec114c1a645 100644 --- a/base/abstractset.jl +++ b/base/abstractset.jl @@ -339,6 +339,8 @@ false ⊊, ⊋ ⊊(a::AbstractSet, b::AbstractSet) = length(a) < length(b) && a ⊆ b +⊊(a::AbstractSet, b) = a ⊊ Set(b) +⊊(a, b::AbstractSet) = Set(a) ⊊ b ⊊(a, b) = Set(a) ⊊ Set(b) ⊋(a, b) = b ⊊ a diff --git a/base/array.jl b/base/array.jl index bd9d3b8733541..4d36316a9c22c 100644 --- a/base/array.jl +++ b/base/array.jl @@ -643,23 +643,38 @@ julia> collect(Float64, 1:2:5) """ collect(::Type{T}, itr) where {T} = _collect(T, itr, IteratorSize(itr)) -_collect(::Type{T}, itr, isz::HasLength) where {T} = copyto!(Vector{T}(undef, Int(length(itr)::Integer)), itr) -_collect(::Type{T}, itr, isz::HasShape) where {T} = copyto!(similar(Array{T}, axes(itr)), itr) +_collect(::Type{T}, itr, isz::Union{HasLength,HasShape}) where {T} = + copyto!(_array_for(T, isz, _similar_shape(itr, isz)), itr) function _collect(::Type{T}, itr, isz::SizeUnknown) where T a = Vector{T}() for x in itr - push!(a,x) + push!(a, x) end return a end # make a collection similar to `c` and appropriate for collecting `itr` -_similar_for(c::AbstractArray, ::Type{T}, itr, ::SizeUnknown) where {T} = similar(c, T, 0) -_similar_for(c::AbstractArray, ::Type{T}, itr, ::HasLength) where {T} = - similar(c, T, Int(length(itr)::Integer)) -_similar_for(c::AbstractArray, ::Type{T}, itr, ::HasShape) where {T} = - similar(c, T, axes(itr)) -_similar_for(c, ::Type{T}, itr, isz) where {T} = similar(c, T) +_similar_for(c, ::Type{T}, itr, isz, shp) where {T} = similar(c, T) + +_similar_shape(itr, ::SizeUnknown) = nothing +_similar_shape(itr, ::HasLength) = length(itr)::Integer +_similar_shape(itr, ::HasShape) = axes(itr) + +_similar_for(c::AbstractArray, ::Type{T}, itr, ::SizeUnknown, ::Nothing) where {T} = + similar(c, T, 0) +_similar_for(c::AbstractArray, ::Type{T}, itr, ::HasLength, len::Integer) where {T} = + similar(c, T, len) +_similar_for(c::AbstractArray, ::Type{T}, itr, ::HasShape, axs) where {T} = + similar(c, T, axs) + +# make a collection appropriate for collecting `itr::Generator` +_array_for(::Type{T}, ::SizeUnknown, ::Nothing) where {T} = Vector{T}(undef, 0) +_array_for(::Type{T}, ::HasLength, len::Integer) where {T} = Vector{T}(undef, Int(len)) +_array_for(::Type{T}, ::HasShape{N}, axs) where {T,N} = similar(Array{T,N}, axs) + +# used by syntax lowering for simple typed comprehensions +_array_for(::Type{T}, itr, isz) where {T} = _array_for(T, isz, _similar_shape(itr, isz)) + """ collect(collection) @@ -698,10 +713,10 @@ collect(A::AbstractArray) = _collect_indices(axes(A), A) collect_similar(cont, itr) = _collect(cont, itr, IteratorEltype(itr), IteratorSize(itr)) _collect(cont, itr, ::HasEltype, isz::Union{HasLength,HasShape}) = - copyto!(_similar_for(cont, eltype(itr), itr, isz), itr) + copyto!(_similar_for(cont, eltype(itr), itr, isz, _similar_shape(itr, isz)), itr) function _collect(cont, itr, ::HasEltype, isz::SizeUnknown) - a = _similar_for(cont, eltype(itr), itr, isz) + a = _similar_for(cont, eltype(itr), itr, isz, nothing) for x in itr push!(a,x) end @@ -759,24 +774,19 @@ else end end -_array_for(::Type{T}, itr, isz::HasLength) where {T} = _array_for(T, itr, isz, length(itr)) -_array_for(::Type{T}, itr, isz::HasShape{N}) where {T,N} = _array_for(T, itr, isz, axes(itr)) -_array_for(::Type{T}, itr, ::HasLength, len) where {T} = Vector{T}(undef, len) -_array_for(::Type{T}, itr, ::HasShape{N}, axs) where {T,N} = similar(Array{T,N}, axs) - function collect(itr::Generator) isz = IteratorSize(itr.iter) et = @default_eltype(itr) if isa(isz, SizeUnknown) return grow_to!(Vector{et}(), itr) else - shape = isz isa HasLength ? length(itr) : axes(itr) + shp = _similar_shape(itr, isz) y = iterate(itr) if y === nothing - return _array_for(et, itr.iter, isz) + return _array_for(et, isz, shp) end v1, st = y - dest = _array_for(typeof(v1), itr.iter, isz, shape) + dest = _array_for(typeof(v1), isz, shp) # The typeassert gives inference a helping hand on the element type and dimensionality # (work-around for #28382) et′ = et <: Type ? Type : et @@ -786,15 +796,22 @@ function collect(itr::Generator) end _collect(c, itr, ::EltypeUnknown, isz::SizeUnknown) = - grow_to!(_similar_for(c, @default_eltype(itr), itr, isz), itr) + grow_to!(_similar_for(c, @default_eltype(itr), itr, isz, nothing), itr) function _collect(c, itr, ::EltypeUnknown, isz::Union{HasLength,HasShape}) + et = @default_eltype(itr) + shp = _similar_shape(itr, isz) y = iterate(itr) if y === nothing - return _similar_for(c, @default_eltype(itr), itr, isz) + return _similar_for(c, et, itr, isz, shp) end v1, st = y - collect_to_with_first!(_similar_for(c, typeof(v1), itr, isz), v1, itr, st) + dest = _similar_for(c, typeof(v1), itr, isz, shp) + # The typeassert gives inference a helping hand on the element type and dimensionality + # (work-around for #28382) + et′ = et <: Type ? Type : et + RT = dest isa AbstractArray ? AbstractArray{<:et′, ndims(dest)} : Any + collect_to_with_first!(dest, v1, itr, st)::RT end function collect_to_with_first!(dest::AbstractArray, v1, itr, st) @@ -1462,12 +1479,22 @@ julia> deleteat!([6, 5, 4, 3, 2, 1], 2) 1 ``` """ -deleteat!(a::Vector, i::Integer) = (_deleteat!(a, i, 1); a) +function deleteat!(a::Vector, i::Integer) + i isa Bool && depwarn("passing Bool as an index is deprecated", :deleteat!) + _deleteat!(a, i, 1) + return a +end function deleteat!(a::Vector, r::AbstractUnitRange{<:Integer}) - n = length(a) - isempty(r) || _deleteat!(a, first(r), length(r)) - return a + if eltype(r) === Bool + return invoke(deleteat!, Tuple{Vector, AbstractVector{Bool}}, a, r) + else + n = length(a) + f = first(r) + f isa Bool && depwarn("passing Bool as an index is deprecated", :deleteat!) + isempty(r) || _deleteat!(a, f, length(r)) + return a + end end """ @@ -2575,6 +2602,54 @@ function filter!(f, a::AbstractVector) return a end +""" + keepat!(a::Vector, inds) + +Remove the items at all the indices which are not given by `inds`, +and return the modified `a`. +Items which are kept are shifted to fill the resulting gaps. + +`inds` must be an iterator of sorted and unique integer indices. +See also [`deleteat!`](@ref). + +!!! compat "Julia 1.7" + This function is available as of Julia 1.7. + +# Examples +```jldoctest +julia> keepat!([6, 5, 4, 3, 2, 1], 1:2:5) +3-element Vector{Int64}: + 6 + 4 + 2 +``` +""" +keepat!(a::Vector, inds) = _keepat!(a, inds) + +""" + keepat!(a::Vector, m::AbstractVector{Bool}) + +The in-place version of logical indexing `a = a[m]`. That is, `keepat!(a, m)` on +vectors of equal length `a` and `m` will remove all elements from `a` for which +`m` at the corresponding index is `false`. + +# Examples +```jldoctest +julia> a = [:a, :b, :c]; + +julia> keepat!(a, [true, false, true]) +2-element Vector{Symbol}: + :a + :c + +julia> a +2-element Vector{Symbol}: + :a + :c +``` +""" +keepat!(a::Vector, m::AbstractVector{Bool}) = _keepat!(a, m) + # set-like operators for vectors # These are moderately efficient, preserve order, and remove dupes. diff --git a/base/bitarray.jl b/base/bitarray.jl index 6fe94df378516..33e2715572018 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -947,6 +947,7 @@ function _deleteat!(B::BitVector, i::Int) end function deleteat!(B::BitVector, i::Integer) + i isa Bool && depwarn("passing Bool as an index is deprecated", :deleteat!) i = Int(i) n = length(B) 1 <= i <= n || throw(BoundsError(B, i)) @@ -987,25 +988,27 @@ function deleteat!(B::BitVector, inds) (p, s) = y checkbounds(B, p) + p isa Bool && throw(ArgumentError("invalid index $p of type Bool")) q = p+1 new_l -= 1 y = iterate(inds, s) while y !== nothing (i, s) = y if !(q <= i <= n) + i isa Bool && throw(ArgumentError("invalid index $i of type Bool")) i < q && throw(ArgumentError("indices must be unique and sorted")) throw(BoundsError(B, i)) end new_l -= 1 if i > q - copy_chunks!(Bc, p, Bc, Int(q), Int(i-q)) + copy_chunks!(Bc, Int(p), Bc, Int(q), Int(i-q)) p += i-q end q = i+1 y = iterate(inds, s) end - q <= n && copy_chunks!(Bc, p, Bc, Int(q), Int(n-q+1)) + q <= n && copy_chunks!(Bc, Int(p), Bc, Int(q), Int(n-q+1)) delta_k = num_bit_chunks(new_l) - length(Bc) delta_k < 0 && _deleteend!(Bc, -delta_k) @@ -1019,7 +1022,55 @@ function deleteat!(B::BitVector, inds) return B end +function deleteat!(B::BitVector, inds::AbstractVector{Bool}) + length(inds) == length(B) || throw(BoundsError(B, inds)) + + n = new_l = length(B) + y = findfirst(inds) + y === nothing && return B + + Bc = B.chunks + + p = y + s = y + 1 + checkbounds(B, p) + q = p + 1 + new_l -= 1 + y = findnext(inds, s) + while y !== nothing + i = y + s = y + 1 + new_l -= 1 + if i > q + copy_chunks!(Bc, Int(p), Bc, Int(q), Int(i-q)) + p += i - q + end + q = i + 1 + y = findnext(inds, s) + end + + q <= n && copy_chunks!(Bc, Int(p), Bc, Int(q), Int(n - q + 1)) + + delta_k = num_bit_chunks(new_l) - length(Bc) + delta_k < 0 && _deleteend!(Bc, -delta_k) + + B.len = new_l + + if new_l > 0 + Bc[end] &= _msk_end(new_l) + end + + return B +end + +keepat!(B::BitVector, inds) = _keepat!(B, inds) +keepat!(B::BitVector, inds::AbstractVector{Bool}) = _keepat!(B, inds) + function splice!(B::BitVector, i::Integer) + # TODO: after deprecation remove the four lines below + # as v = B[i] is enough to do both bounds checking + # and Bool check then just pass Int(i) to _deleteat! + i isa Bool && depwarn("passing Bool as an index is deprecated", :splice!) i = Int(i) n = length(B) 1 <= i <= n || throw(BoundsError(B, i)) @@ -1032,8 +1083,10 @@ end const _default_bit_splice = BitVector() function splice!(B::BitVector, r::Union{AbstractUnitRange{Int}, Integer}, ins::AbstractArray = _default_bit_splice) + r isa Bool && depwarn("passing Bool as an index is deprecated", :splice!) _splice_int!(B, isa(r, AbstractUnitRange{Int}) ? r : Int(r), ins) end + function _splice_int!(B::BitVector, r, ins) n = length(B) i_f, i_l = first(r), last(r) diff --git a/base/boot.jl b/base/boot.jl index ee1370bf5dff1..4679acd85d26d 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -428,7 +428,7 @@ eval(Core, :(InterConditional(slot::Int, @nospecialize(vtype), @nospecialize(els eval(Core, :(MethodMatch(@nospecialize(spec_types), sparams::SimpleVector, method::Method, fully_covers::Bool) = $(Expr(:new, :MethodMatch, :spec_types, :sparams, :method, :fully_covers)))) -Module(name::Symbol=:anonymous, std_imports::Bool=true, using_core::Bool=true) = ccall(:jl_f_new_module, Ref{Module}, (Any, Bool, Bool), name, std_imports, using_core) +Module(name::Symbol=:anonymous, std_imports::Bool=true, default_names::Bool=true) = ccall(:jl_f_new_module, Ref{Module}, (Any, Bool, Bool), name, std_imports, default_names) function _Task(@nospecialize(f), reserved_stack::Int, completion_future) return ccall(:jl_new_task, Ref{Task}, (Any, Any, Int), f, completion_future, reserved_stack) diff --git a/base/broadcast.jl b/base/broadcast.jl index 90479189ffee4..af9a809e7c59b 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -697,7 +697,7 @@ julia> Broadcast.broadcastable("hello") # Strings break convention of matching i Base.RefValue{String}("hello") ``` """ -broadcastable(x::Union{Symbol,AbstractString,Function,UndefInitializer,Nothing,RoundingMode,Missing,Val,Ptr,AbstractPattern,Pair}) = Ref(x) +broadcastable(x::Union{Symbol,AbstractString,Function,UndefInitializer,Nothing,RoundingMode,Missing,Val,Ptr,AbstractPattern,Pair,IO}) = Ref(x) broadcastable(::Type{T}) where {T} = Ref{Type{T}}(T) broadcastable(x::Union{AbstractArray,Number,AbstractChar,Ref,Tuple,Broadcasted}) = x # Default to collecting iterables — which will error for non-iterables diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 742783e2baa78..a10e1878ad52f 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1235,7 +1235,7 @@ function abstract_invoke(interp::AbstractInterpreter, argtypes::Vector{Any}, sv: # t, a = ti.parameters[i], argtypes′[i] # argtypes′[i] = t ⊑ a ? t : a # end - const_result = abstract_call_method_with_const_args(interp, result, argtype_to_function(ft′), argtypes′, match, sv, false) + const_result = abstract_call_method_with_const_args(interp, result, singleton_type(ft′), argtypes′, match, sv, false) if const_result !== nothing const_rt, const_result = const_result if const_rt !== rt && const_rt ⊑ rt @@ -1379,7 +1379,7 @@ function abstract_call(interp::AbstractInterpreter, fargs::Union{Nothing,Vector{ sv::InferenceState, max_methods::Int = InferenceParams(interp).MAX_METHODS) #print("call ", e.args[1], argtypes, "\n\n") ft = argtypes[1] - f = argtype_to_function(ft) + f = singleton_type(ft) if isa(ft, PartialOpaque) return abstract_call_opaque_closure(interp, ft, argtypes[2:end], sv) elseif (uft = unwrap_unionall(ft); isa(uft, DataType) && uft.name === typename(Core.OpaqueClosure)) @@ -1396,18 +1396,6 @@ function abstract_call(interp::AbstractInterpreter, fargs::Union{Nothing,Vector{ return abstract_call_known(interp, f, fargs, argtypes, sv, max_methods) end -function argtype_to_function(@nospecialize(ft)) - if isa(ft, Const) - return ft.val - elseif isconstType(ft) - return ft.parameters[1] - elseif isa(ft, DataType) && isdefined(ft, :instance) - return ft.instance - else - return nothing - end -end - function sp_type_rewrap(@nospecialize(T), linfo::MethodInstance, isreturn::Bool) isref = false if T === Bottom diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index fe0c058c533cb..02ee5cdc9ecc1 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -85,7 +85,7 @@ mutable struct OptimizationState if nssavalues isa Int src.ssavaluetypes = Any[ Any for i = 1:nssavalues ] else - nssavalues = length(src.ssavaluetypes) + nssavalues = length(src.ssavaluetypes::Vector{Any}) end nslots = length(src.slotflags) slottypes = src.slottypes @@ -142,6 +142,7 @@ const SLOT_USEDUNDEF = 32 # slot has uses that might raise UndefVarError # NOTE make sure to sync the flag definitions below with julia.h and `jl_code_info_set_ir` in method.c +const IR_FLAG_NULL = 0x00 # This statement is marked as @inbounds by user. # Ff replaced by inlining, any contained boundschecks may be removed. const IR_FLAG_INBOUNDS = 0x01 << 0 @@ -349,15 +350,18 @@ function convert_to_ircode(ci::CodeInfo, code::Vector{Any}, coverage::Bool, sv:: labelmap = coverage ? fill(0, length(code)) : changemap prevloc = zero(eltype(ci.codelocs)) stmtinfo = sv.stmt_info + codelocs = ci.codelocs ssavaluetypes = ci.ssavaluetypes::Vector{Any} + ssaflags = ci.ssaflags while idx <= length(code) - codeloc = ci.codelocs[idx] + codeloc = codelocs[idx] if coverage && codeloc != prevloc && codeloc != 0 # insert a side-effect instruction before the current instruction in the same basic block insert!(code, idx, Expr(:code_coverage_effect)) - insert!(ci.codelocs, idx, codeloc) + insert!(codelocs, idx, codeloc) insert!(ssavaluetypes, idx, Nothing) insert!(stmtinfo, idx, nothing) + insert!(ssaflags, idx, IR_FLAG_NULL) changemap[oldidx] += 1 if oldidx < length(labelmap) labelmap[oldidx + 1] += 1 @@ -369,9 +373,10 @@ function convert_to_ircode(ci::CodeInfo, code::Vector{Any}, coverage::Bool, sv:: if !(idx < length(code) && isa(code[idx + 1], ReturnNode) && !isdefined((code[idx + 1]::ReturnNode), :val)) # insert unreachable in the same basic block after the current instruction (splitting it) insert!(code, idx + 1, ReturnNode()) - insert!(ci.codelocs, idx + 1, ci.codelocs[idx]) + insert!(codelocs, idx + 1, codelocs[idx]) insert!(ssavaluetypes, idx + 1, Union{}) insert!(stmtinfo, idx + 1, nothing) + insert!(ssaflags, idx + 1, ssaflags[idx]) if oldidx < length(changemap) changemap[oldidx + 1] += 1 coverage && (labelmap[oldidx + 1] += 1) @@ -391,7 +396,7 @@ function convert_to_ircode(ci::CodeInfo, code::Vector{Any}, coverage::Bool, sv:: strip_trailing_junk!(ci, code, stmtinfo) cfg = compute_basic_blocks(code) types = Any[] - stmts = InstructionStream(code, types, stmtinfo, ci.codelocs, ci.ssaflags) + stmts = InstructionStream(code, types, stmtinfo, codelocs, ssaflags) ir = IRCode(stmts, cfg, collect(LineInfoNode, ci.linetable::Union{Vector{LineInfoNode},Vector{Any}}), sv.slottypes, meta, sv.sptypes) return ir end diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 33e370e4db7e4..015f3319008d9 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -109,7 +109,7 @@ function inline_into_block!(state::CFGInliningState, block::Int) new_range = state.first_bb+1:block l = length(state.new_cfg_blocks) state.bb_rename[new_range] = (l+1:l+length(new_range)) - append!(state.new_cfg_blocks, map(copy, state.cfg.blocks[new_range])) + append!(state.new_cfg_blocks, (copy(block) for block in state.cfg.blocks[new_range])) push!(state.merged_orig_blocks, last(new_range)) end state.first_bb = block @@ -304,29 +304,47 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}}) # Ok, do the inlining here spec = item.spec::ResolvedInliningSpec + 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]) - for entry in spec.ir.linetable - push!(linetable, LineInfoNode(entry.module, entry.method, entry.file, entry.line, - (entry.inlined_at > 0 ? entry.inlined_at + linetable_offset : inlined_at))) + topline::Int32 = linetable_offset + Int32(1) + coverage = coverage_enabled(def.module) + push!(linetable, LineInfoNode(def.module, def.name, def.file, Int(def.line), inlined_at)) + oldlinetable = spec.ir.linetable + for oldline in 1:length(oldlinetable) + entry = oldlinetable[oldline] + newentry = LineInfoNode(entry.module, entry.method, entry.file, entry.line, + (entry.inlined_at > 0 ? entry.inlined_at + linetable_offset + (oldline == 1) : inlined_at)) + if oldline == 1 + # check for a duplicate on the first iteration (likely true) + if newentry === linetable[topline] + continue + else + linetable_offset += 1 + end + end + push!(linetable, newentry) + end + if coverage && spec.ir.stmts[1][:line] + linetable_offset != topline + insert_node_here!(compact, NewInstruction(Expr(:code_coverage_effect), Nothing, topline)) end - (; def, sparam_vals) = item.mi nargs_def = def.nargs::Int32 isva = nargs_def > 0 && def.isva sig = def.sig if isva - vararg = mk_tuplecall!(compact, argexprs[nargs_def:end], compact.result[idx][:line]) + vararg = mk_tuplecall!(compact, argexprs[nargs_def:end], topline) argexprs = Any[argexprs[1:(nargs_def - 1)]..., vararg] end - is_opaque = isa(def, Method) && def.is_for_opaque_closure + is_opaque = def.is_for_opaque_closure if is_opaque # 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)), - spec.ir.argtypes[1], compact.result[idx][:line])) + spec.ir.argtypes[1], topline)) end flag = compact.result[idx][:flag] boundscheck_idx = boundscheck @@ -511,9 +529,7 @@ function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, end # We're now in the join block. - compact.ssa_rename[compact.idx-1] = insert_node_here!(compact, - NewInstruction(pn, typ, line)) - nothing + return insert_node_here!(compact, NewInstruction(pn, typ, line)) end function batch_inline!(todo::Vector{Pair{Int, Any}}, ir::IRCode, linetable::Vector{LineInfoNode}, propagate_inbounds::Bool) @@ -574,7 +590,7 @@ function batch_inline!(todo::Vector{Pair{Int, Any}}, ir::IRCode, linetable::Vect if isa(item, InliningTodo) compact.ssa_rename[old_idx] = ir_inline_item!(compact, idx, argexprs, linetable, item, boundscheck, state.todo_bbs) elseif isa(item, UnionSplit) - ir_inline_unionsplit!(compact, idx, argexprs, linetable, item, boundscheck, state.todo_bbs) + compact.ssa_rename[old_idx] = ir_inline_unionsplit!(compact, idx, argexprs, linetable, item, boundscheck, state.todo_bbs) end compact[idx] = nothing refinish && finish_current_bb!(compact, 0) @@ -699,15 +715,6 @@ function rewrite_invoke_exprargs!(argexprs::Vector{Any}) return argexprs end -function singleton_type(@nospecialize(ft)) - if isa(ft, Const) - return ft.val - elseif ft isa DataType && isdefined(ft, :instance) - return ft.instance - end - return nothing -end - function compileable_specialization(et::Union{EdgeTracker, Nothing}, match::MethodMatch) mi = specialize_method(match; compilesig=true) mi !== nothing && et !== nothing && push!(et, mi::MethodInstance) diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index a2eaf5c69cbdd..d3d62c9b2dfaa 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -172,7 +172,7 @@ end NewInstruction(@nospecialize(stmt), @nospecialize(type)) = NewInstruction(stmt, type, nothing) NewInstruction(@nospecialize(stmt), @nospecialize(type), line::Union{Nothing, Int32}) = - NewInstruction(stmt, type, nothing, line, 0x00, false) + NewInstruction(stmt, type, nothing, line, IR_FLAG_NULL, false) effect_free(inst::NewInstruction) = NewInstruction(inst.stmt, inst.type, inst.info, inst.line, inst.flag | IR_FLAG_EFFECT_FREE, true) @@ -193,7 +193,7 @@ function InstructionStream(len::Int) info = Array{Any}(undef, len) fill!(info, nothing) lines = fill(Int32(0), len) - flags = fill(0x00, len) + flags = fill(IR_FLAG_NULL, len) return InstructionStream(insts, types, info, lines, flags) end InstructionStream() = InstructionStream(0) @@ -221,7 +221,7 @@ function resize!(stmts::InstructionStream, len) resize!(stmts.flag, len) for i in (old_length + 1):len stmts.line[i] = 0 - stmts.flag[i] = 0x00 + stmts.flag[i] = IR_FLAG_NULL stmts.info[i] = nothing end return stmts diff --git a/base/compiler/ssair/legacy.jl b/base/compiler/ssair/legacy.jl index a832daa8ed418..88f529d2814de 100644 --- a/base/compiler/ssair/legacy.jl +++ b/base/compiler/ssair/legacy.jl @@ -29,9 +29,10 @@ function inflate_ir(ci::CodeInfo, sptypes::Vector{Any}, argtypes::Vector{Any}) code[i] = stmt end end - ssavaluetypes = ci.ssavaluetypes nstmts = length(code) - ssavaluetypes = ci.ssavaluetypes isa Vector{Any} ? copy(ci.ssavaluetypes) : Any[ Any for i = 1:(ci.ssavaluetypes::Int) ] + ssavaluetypes = let ssavaluetypes = ci.ssavaluetypes + ssavaluetypes isa Vector{Any} ? copy(ssavaluetypes) : Any[ Any for i = 1:(ssavaluetypes::Int) ] + end stmts = InstructionStream(code, ssavaluetypes, Any[nothing for i = 1:nstmts], copy(ci.codelocs), copy(ci.ssaflags)) ir = IRCode(stmts, cfg, collect(LineInfoNode, ci.linetable), argtypes, Any[], sptypes) return ir @@ -48,7 +49,7 @@ function replace_code_newstyle!(ci::CodeInfo, ir::IRCode, nargs::Int) push!(ci.code, metanode) push!(ci.codelocs, 1) push!(ci.ssavaluetypes::Vector{Any}, Any) - push!(ci.ssaflags, 0x00) + push!(ci.ssaflags, IR_FLAG_NULL) end # Translate BB Edges to statement edges # (and undo normalization for now) diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 2d097b3d46b6f..07901f8c2f0a2 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -19,37 +19,28 @@ struct SSADefUse end SSADefUse() = SSADefUse(Int[], Int[], Int[]) -try_compute_fieldidx_expr(typ::DataType, expr::Expr) = try_compute_fieldidx_args(typ, expr.args) -function try_compute_fieldidx_args(typ::DataType, args::Vector{Any}) - field = args[3] - isa(field, QuoteNode) && (field = field.value) +function try_compute_field_stmt(compact::IncrementalCompact, stmt::Expr) + field = stmt.args[3] + # fields are usually literals, handle them manually + if isa(field, QuoteNode) + field = field.value + elseif isa(field, Int) + # try to resolve other constants, e.g. global reference + else + field = compact_exprtype(compact, field) + if isa(field, Const) + field = field.val + else + return nothing + end + end isa(field, Union{Int, Symbol}) || return nothing - return try_compute_fieldidx(typ, field) + return field end -function lift_defuse(cfg::CFG, ssa::SSADefUse) - # We remove from `uses` any block where all uses are dominated - # by a def. This prevents insertion of dead phi nodes at the top - # of such a block if that block happens to be in a loop - ordered = Tuple{Int, Int, Bool}[(x, block_for_inst(cfg, x), true) for x in ssa.uses] - for x in ssa.defs - push!(ordered, (x, block_for_inst(cfg, x), false)) - end - ordered = sort(ordered, by=x->x[1]) - bb_defs = Int[] - bb_uses = Int[] - last_bb = last_def_bb = 0 - for (_, bb, is_use) in ordered - if bb != last_bb && is_use - push!(bb_uses, bb) - end - last_bb = bb - if last_def_bb != bb && !is_use - push!(bb_defs, bb) - last_def_bb = bb - end - end - SSADefUse(bb_uses, bb_defs, Int[]) +function try_compute_fieldidx_stmt(compact::IncrementalCompact, stmt::Expr, typ::DataType) + field = try_compute_field_stmt(compact, stmt) + return try_compute_fieldidx(typ, field) end function find_curblock(domtree::DomTree, allblocks::Vector{Int}, curblock::Int) @@ -661,10 +652,8 @@ function getfield_elim_pass!(ir::IRCode) else continue end - ## Normalize the field argument to getfield/setfield - field = stmt.args[3] - isa(field, QuoteNode) && (field = field.value) - isa(field, Union{Int, Symbol}) || continue + field = try_compute_field_stmt(compact, stmt) + field === nothing && continue struct_typ = unwrap_unionall(widenconst(compact_exprtype(compact, stmt.args[2]))) if isa(struct_typ, Union) && struct_typ <: Tuple @@ -804,13 +793,13 @@ function getfield_elim_pass!(ir::IRCode) # it would have been deleted. That's fine, just ignore # the use in that case. stmt === nothing && continue - field = try_compute_fieldidx_expr(typ, stmt) + field = try_compute_fieldidx_stmt(compact, stmt::Expr, typ) field === nothing && (ok = false; break) push!(fielddefuse[field].uses, use) end ok || continue for use in defuse.defs - field = try_compute_fieldidx_expr(typ, ir[SSAValue(use)]) + field = try_compute_fieldidx_stmt(compact, ir[SSAValue(use)]::Expr, typ) field === nothing && (ok = false; break) push!(fielddefuse[field].defs, use) end @@ -1209,12 +1198,12 @@ function cfg_simplify!(ir::IRCode) # Compute (renamed) successors and predecessors given (renamed) block function compute_succs(i) orig_bb = follow_merged_succ(result_bbs[i]) - return map(i -> bb_rename_succ[i], bbs[orig_bb].succs) + return Int[bb_rename_succ[i] for i in bbs[orig_bb].succs] end function compute_preds(i) orig_bb = result_bbs[i] preds = bbs[orig_bb].preds - return map(pred -> bb_rename_pred[pred], preds) + return Int[bb_rename_pred[pred] for pred in preds] end BasicBlock[ diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index 4174267ec5a5e..ffb04751b368f 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -79,14 +79,15 @@ show_unquoted(io::IO, val::Argument, indent::Int, prec::Int) = show_unquoted(io, show_unquoted(io::IO, stmt::PhiNode, indent::Int, ::Int) = show_unquoted_phinode(io, stmt, indent, "%") function show_unquoted_phinode(io::IO, stmt::PhiNode, indent::Int, prefix::String) - args = map(1:length(stmt.edges)) do i + args = String[let e = stmt.edges[i] v = !isassigned(stmt.values, i) ? "#undef" : sprint() do io′ show_unquoted(io′, stmt.values[i], indent) end - return "$prefix$e => $v" - end + "$prefix$e => $v" + end for i in 1:length(stmt.edges) + ] print(io, "φ ", '(') join(io, args, ", ") print(io, ')') @@ -381,7 +382,7 @@ function DILineInfoPrinter(linetable::Vector, showtypes::Bool=false) # if so, drop all existing calls to it from the top of the context # AND check if instead the context was previously printed that way # but now has removed the recursive frames - let method = method_name(context[nctx]) + let method = method_name(context[nctx]) # last matching frame if (nctx < nframes && method_name(DI[nframes - nctx]) === method) || (nctx < length(context) && method_name(context[nctx + 1]) === method) update_line_only = true @@ -390,8 +391,15 @@ function DILineInfoPrinter(linetable::Vector, showtypes::Bool=false) end end end - elseif length(context) > 0 - update_line_only = true + end + # look at the first non-matching element to see if we are only changing the line number + if !update_line_only && nctx < length(context) && nctx < nframes + let CtxLine = context[nctx + 1], + FrameLine = DI[nframes - nctx] + if method_name(CtxLine) === method_name(FrameLine) + update_line_only = true + end + end end elseif nctx < length(context) && nctx < nframes # look at the first non-matching element to see if we are only changing the line number diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index 2e3d1da1c168f..846ebde4c0bb1 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -33,16 +33,6 @@ function scan_entry!(result::Vector{SlotInfo}, idx::Int, @nospecialize(stmt)) end -function lift_defuse(cfg::CFG, defuse) - map(defuse) do slot - SlotInfo( - Int[block_for_inst(cfg, x) for x in slot.defs], - Int[block_for_inst(cfg, x) for x in slot.uses], - slot.any_newvar - ) - end -end - function scan_slot_def_use(nargs::Int, ci::CodeInfo, code::Vector{Any}) nslots = length(ci.slotflags) result = SlotInfo[SlotInfo() for i = 1:nslots] @@ -187,13 +177,14 @@ function strip_trailing_junk!(ci::CodeInfo, code::Vector{Any}, info::Vector{Any} # Remove `nothing`s at the end, we don't handle them well # (we expect the last instruction to be a terminator) ssavaluetypes = ci.ssavaluetypes::Vector{Any} + (; codelocs, ssaflags) = ci for i = length(code):-1:1 if code[i] !== nothing resize!(code, i) resize!(ssavaluetypes, i) - resize!(ci.codelocs, i) + resize!(codelocs, i) resize!(info, i) - resize!(ci.ssaflags, i) + resize!(ssaflags, i) break end end @@ -203,9 +194,9 @@ function strip_trailing_junk!(ci::CodeInfo, code::Vector{Any}, info::Vector{Any} if !isa(term, GotoIfNot) && !isa(term, GotoNode) && !isa(term, ReturnNode) push!(code, ReturnNode()) push!(ssavaluetypes, Union{}) - push!(ci.codelocs, 0) + push!(codelocs, 0) push!(info, nothing) - push!(ci.ssaflags, 0x00) + push!(ssaflags, IR_FLAG_NULL) end nothing end @@ -524,7 +515,7 @@ function domsort_ssa!(ir::IRCode, domtree::DomTree) return new_ir end -function compute_live_ins(cfg::CFG, defuse) +function compute_live_ins(cfg::CFG, defuse #=::Union{SlotInfo,SSADefUse}=#) # We remove from `uses` any block where all uses are dominated # by a def. This prevents insertion of dead phi nodes at the top # of such a block if that block happens to be in a loop @@ -586,8 +577,8 @@ function recompute_type(node::Union{PhiNode, PhiCNode}, ci::CodeInfo, ir::IRCode return new_typ end -function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, defuse, - slottypes::Vector{Any}) +function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, + defuses::Vector{SlotInfo}, slottypes::Vector{Any}) code = ir.stmts.inst cfg = ir.cfg left = Int[] @@ -616,7 +607,7 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, defuse, for (_, exc) in catch_entry_blocks phicnodes[exc] = Vector{Tuple{SlotNumber, NewSSAValue, PhiCNode}}() end - @timeit "idf" for (idx, slot) in Iterators.enumerate(defuse) + @timeit "idf" for (idx, slot) in Iterators.enumerate(defuses) # No uses => no need for phi nodes isempty(slot.uses) && continue # TODO: Restore this optimization @@ -671,9 +662,9 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, defuse, end # Perform SSA renaming initial_incoming_vals = Any[ - if 0 in defuse[x].defs + if 0 in defuses[x].defs Argument(x) - elseif !defuse[x].any_newvar + elseif !defuses[x].any_newvar undef_token else SSAValue(-2) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index ba83516ef2d8f..67294503fed7a 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -883,6 +883,7 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) return Bottom # can't index fields with Bool end if !isa(name, Const) + name = widenconst(name) if !(Int <: name || Symbol <: name) return Bottom end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index f301b8a1756d9..a197731b62f32 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -475,6 +475,7 @@ function finish(me::InferenceState, interp::AbstractInterpreter) end me.result.valid_worlds = me.valid_worlds me.result.result = me.bestguess + validate_code_in_debug_mode(me.linfo, me.src, "inferred") nothing end @@ -666,6 +667,7 @@ function type_annotate!(sv::InferenceState, run_optimizer::Bool) deleteat!(ssavaluetypes, i) deleteat!(src.codelocs, i) deleteat!(sv.stmt_info, i) + deleteat!(src.ssaflags, i) nexpr -= 1 changemap[oldidx] = -1 continue @@ -866,7 +868,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) tree.code = Any[ ReturnNode(quoted(rettype_const)) ] nargs = Int(method.nargs) tree.slotnames = ccall(:jl_uncompress_argnames, Vector{Symbol}, (Any,), method.slot_syms) - tree.slotflags = fill(0x00, nargs) + tree.slotflags = fill(IR_FLAG_NULL, nargs) tree.ssavaluetypes = 1 tree.codelocs = Int32[1] tree.linetable = [LineInfoNode(method.module, method.name, method.file, Int(method.line), 0)] diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 85c81f7f12f54..1da32ebd7b386 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -222,6 +222,10 @@ function method_for_inference_heuristics(method::Method, @nospecialize(sig), spa return nothing end +######### +# types # +######### + argextype(@nospecialize(x), state) = argextype(x, state.src, state.sptypes, state.slottypes) const empty_slottypes = Any[] @@ -259,6 +263,17 @@ function argextype(@nospecialize(x), src, sptypes::Vector{Any}, slottypes::Vecto end end +function singleton_type(@nospecialize(ft)) + if isa(ft, Const) + return ft.val + elseif isconstType(ft) + return ft.parameters[1] + elseif ft isa DataType && isdefined(ft, :instance) + return ft.instance + end + return nothing +end + ################### # SSAValues/Slots # ################### diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl index b7e63900a3fdf..bcde5d894159c 100644 --- a/base/compiler/validation.jl +++ b/base/compiler/validation.jl @@ -48,6 +48,7 @@ const EMPTY_SLOTNAMES = "slotnames field is empty" const SLOTFLAGS_MISMATCH = "length(slotnames) < length(slotflags)" const SSAVALUETYPES_MISMATCH = "not all SSAValues in AST have a type in ssavaluetypes" const SSAVALUETYPES_MISMATCH_UNINFERRED = "uninferred CodeInfo ssavaluetypes field does not equal the number of present SSAValues" +const SSAFLAGS_MISMATCH = "not all SSAValues have a corresponding `ssaflags`" const NON_TOP_LEVEL_METHOD = "encountered `Expr` head `:method` in non-top-level code (i.e. `nargs` > 0)" const NON_TOP_LEVEL_GLOBAL = "encountered `Expr` head `:global` in non-top-level code (i.e. `nargs` > 0)" const SIGNATURE_NARGS_MISMATCH = "method signature does not match number of method arguments" @@ -183,13 +184,16 @@ function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_ nssavals = length(c.code) !is_top_level && nslotnames == 0 && push!(errors, InvalidCodeError(EMPTY_SLOTNAMES)) nslotnames < nslotflags && push!(errors, InvalidCodeError(SLOTFLAGS_MISMATCH, (nslotnames, nslotflags))) - if c.inferred - nssavaluetypes = length(c.ssavaluetypes::Vector{Any}) + ssavaluetypes = c.ssavaluetypes + if isa(ssavaluetypes, Vector{Any}) + nssavaluetypes = length(ssavaluetypes) nssavaluetypes < nssavals && push!(errors, InvalidCodeError(SSAVALUETYPES_MISMATCH, (nssavals, nssavaluetypes))) else - ssavaluetypes = c.ssavaluetypes::Int - ssavaluetypes != nssavals && push!(errors, InvalidCodeError(SSAVALUETYPES_MISMATCH_UNINFERRED, (nssavals, ssavaluetypes))) + nssavaluetypes = ssavaluetypes::Int + nssavaluetypes ≠ nssavals && push!(errors, InvalidCodeError(SSAVALUETYPES_MISMATCH_UNINFERRED, (nssavals, nssavaluetypes))) end + nssaflags = length(c.ssaflags) + nssavals ≠ nssaflags && push!(errors, InvalidCodeError(SSAFLAGS_MISMATCH, (nssavals, nssaflags))) return errors end diff --git a/base/condition.jl b/base/condition.jl index be0f618865a48..71db551498170 100644 --- a/base/condition.jl +++ b/base/condition.jl @@ -91,7 +91,8 @@ end Block the current task until some event occurs, depending on the type of the argument: * [`Channel`](@ref): Wait for a value to be appended to the channel. -* [`Condition`](@ref): Wait for [`notify`](@ref) on a condition. +* [`Condition`](@ref): Wait for [`notify`](@ref) on a condition and return the `val` + parameter passed to `notify`. * `Process`: Wait for a process or process chain to exit. The `exitcode` field of a process can be used to determine success or failure. * [`Task`](@ref): Wait for a `Task` to finish. If the task fails with an exception, a diff --git a/base/dict.jl b/base/dict.jl index 6918677c4f0bb..1978323e88503 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -826,6 +826,6 @@ length(t::ImmutableDict) = count(Returns(true), t) isempty(t::ImmutableDict) = !isdefined(t, :parent) empty(::ImmutableDict, ::Type{K}, ::Type{V}) where {K, V} = ImmutableDict{K,V}() -_similar_for(c::Dict, ::Type{Pair{K,V}}, itr, isz) where {K, V} = empty(c, K, V) -_similar_for(c::AbstractDict, ::Type{T}, itr, isz) where {T} = +_similar_for(c::AbstractDict, ::Type{Pair{K,V}}, itr, isz, len) where {K, V} = empty(c, K, V) +_similar_for(c::AbstractDict, ::Type{T}, itr, isz, len) where {T} = throw(ArgumentError("for AbstractDicts, similar requires an element type of Pair;\n if calling map, consider a comprehension instead")) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index e7ae0123117b5..fdfd1f1eeb965 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -130,7 +130,7 @@ kw"__init__" baremodule `baremodule` declares a module that does not contain `using Base` or local definitions of -[`eval`](@ref Base.eval) and [`include`](@ref Base.include). It does still import `Core`. In other words, +[`eval`](@ref Base.MainInclude.eval) and [`include`](@ref Base.include). It does still import `Core`. In other words, ```julia module Mod @@ -182,8 +182,8 @@ kw"primitive type" A macro maps a sequence of argument expressions to a returned expression, and the resulting expression is substituted directly into the program at the point where the macro is invoked. -Macros are a way to run generated code without calling [`eval`](@ref Base.eval), since the generated -code instead simply becomes part of the surrounding program. +Macros are a way to run generated code without calling [`eval`](@ref Base.MainInclude.eval), +since the generated code instead simply becomes part of the surrounding program. Macro arguments may include expressions, literal values, and symbols. Macros can be defined for variable number of arguments (varargs), but do not accept keyword arguments. Every macro also implicitly gets passed the arguments `__source__`, which contains the line number @@ -1188,10 +1188,10 @@ fields of the type to be set after construction. See the manual section on kw"mutable struct" """ - new + new, or new{A,B,...} -Special function available to inner constructors which created a new object -of the type. +Special function available to inner constructors which creates a new object +of the type. The form new{A,B,...} explicitly specifies values of parameters for parametric types. See the manual section on [Inner Constructor Methods](@ref man-inner-constructor-methods) for more information. """ @@ -1533,8 +1533,9 @@ DomainError """ Task(func) -Create a `Task` (i.e. coroutine) to execute the given function `func` (which must be -callable with no arguments). The task exits when this function returns. +Create a `Task` (i.e. coroutine) to execute the given function `func` (which +must be callable with no arguments). The task exits when this function returns. +The task will run in the "world age" from the parent at construction when [`schedule`](@ref)d. # Examples ```jldoctest @@ -2812,6 +2813,13 @@ StridedVecOrMat Module A `Module` is a separate global variable workspace. See [`module`](@ref) and the [manual section about modules](@ref modules) for details. + + Module(name::Symbol=:anonymous, std_imports=true, default_names=true) + +Return a module with the specified name. A `baremodule` corresponds to `Module(:ModuleName, false)` + +An empty module containing no names at all can be created with `Module(:ModuleName, false, false)`. +This module will not import `Base` or `Core` and does not contain a reference to itself. """ Module diff --git a/base/file.jl b/base/file.jl index 85450ff2d3645..66e49174a4656 100644 --- a/base/file.jl +++ b/base/file.jl @@ -193,22 +193,19 @@ end """ mkpath(path::AbstractString; mode::Unsigned = 0o777) -Create all directories in the given `path`, with permissions `mode`. `mode` defaults to -`0o777`, modified by the current file creation mask. Unlike [`mkdir`](@ref), `mkpath` -does not error if `path` (or parts of it) already exists. -Return `path`. +Create all intermediate directories in the `path` as required. Directories are created with +the permissions `mode` which defaults to `0o777` and is modified by the current file +creation mask. Unlike [`mkdir`](@ref), `mkpath` does not error if `path` (or parts of it) +already exists. Return `path`. + +If `path` includes a filename you will probably want to use `mkpath(dirname(path))` to +avoid creating a directory using the filename. # Examples ```julia-repl -julia> mkdir("testingdir") -"testingdir" - -julia> cd("testingdir") +julia> cd(mktempdir()) -julia> pwd() -"/home/JuliaUser/testingdir" - -julia> mkpath("my/test/dir") +julia> mkpath("my/test/dir") # creates three directories "my/test/dir" julia> readdir() @@ -224,6 +221,13 @@ julia> readdir() julia> readdir("test") 1-element Array{String,1}: "dir" + +julia> mkpath("intermediate_dir/actually_a_directory.txt") # creates two directories +"intermediate_dir/actually_a_directory.txt" + +julia> isdir("intermediate_dir/actually_a_directory.txt") +true + ``` """ function mkpath(path::AbstractString; mode::Integer = 0o777) @@ -321,7 +325,7 @@ function checkfor_mv_cp_cptree(src::AbstractString, dst::AbstractString, txt::Ab "`src` refers to: $(abs_src)\n ", "`dst` refers to: $(abs_dst)\n"))) end - rm(dst; recursive=true) + rm(dst; recursive=true, force=true) else throw(ArgumentError(string("'$dst' exists. `force=true` ", "is required to remove '$dst' before $(txt)."))) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 169bbe313a620..ec57f7f80d809 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -13,25 +13,25 @@ The arguments may be integer and rational numbers. # Examples ```jldoctest -julia> gcd(6,9) +julia> gcd(6, 9) 3 -julia> gcd(6,-9) +julia> gcd(6, -9) 3 -julia> gcd(6,0) +julia> gcd(6, 0) 6 -julia> gcd(0,0) +julia> gcd(0, 0) 0 -julia> gcd(1//3,2//3) +julia> gcd(1//3, 2//3) 1//3 -julia> gcd(1//3,-2//3) +julia> gcd(1//3, -2//3) 1//3 -julia> gcd(1//3,2) +julia> gcd(1//3, 2) 1//3 julia> gcd(0, 0, 10, 15) @@ -90,33 +90,33 @@ The arguments may be integer and rational numbers. # Examples ```jldoctest -julia> lcm(2,3) +julia> lcm(2, 3) 6 -julia> lcm(-2,3) +julia> lcm(-2, 3) 6 -julia> lcm(0,3) +julia> lcm(0, 3) 0 -julia> lcm(0,0) +julia> lcm(0, 0) 0 -julia> lcm(1//3,2//3) +julia> lcm(1//3, 2//3) 2//3 -julia> lcm(1//3,-2//3) +julia> lcm(1//3, -2//3) 2//3 -julia> lcm(1//3,2) +julia> lcm(1//3, 2) 2//1 -julia> lcm(1,3,5,7) +julia> lcm(1, 3, 5, 7) 105 ``` """ function lcm(a::T, b::T) where T<:Integer - # explicit a==0 test is to handle case of lcm(0,0) correctly + # explicit a==0 test is to handle case of lcm(0, 0) correctly # explicit b==0 test is to handle case of lcm(typemin(T),0) correctly if a == 0 || b == 0 return zero(a) @@ -214,13 +214,13 @@ and ``div(y,m) = 0``. This will throw an error if ``m = 0``, or if # Examples ```jldoctest -julia> invmod(2,5) +julia> invmod(2, 5) 3 -julia> invmod(2,3) +julia> invmod(2, 3) 2 -julia> invmod(5,6) +julia> invmod(5, 6) 5 ``` """ @@ -882,6 +882,7 @@ end Return true if and only if the extrema `typemax(T)` and `typemin(T)` are defined. """ hastypemax(::Base.BitIntegerType) = true +hastypemax(::Type{Bool}) = true hastypemax(::Type{T}) where {T} = applicable(typemax, T) && applicable(typemin, T) """ @@ -893,14 +894,14 @@ the array length. If the array length is excessive, the excess portion is filled # Examples ```jldoctest -julia> digits!([2,2,2,2], 10, base = 2) +julia> digits!([2, 2, 2, 2], 10, base = 2) 4-element Vector{Int64}: 0 1 0 1 -julia> digits!([2,2,2,2,2,2], 10, base = 2) +julia> digits!([2, 2, 2, 2, 2, 2], 10, base = 2) 6-element Vector{Int64}: 0 1 diff --git a/base/loading.jl b/base/loading.jl index a87a6ed7423c5..b8b998f7a80b6 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1356,8 +1356,8 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, concrete_d for (pkg, build_id) in concrete_deps push!(deps_strs, "$(pkg_str(pkg)) => $(repr(build_id))") end - deps = repr(eltype(concrete_deps)) * "[" * join(deps_strs, ",") * "]" - + deps_eltype = sprint(show, eltype(concrete_deps); context = :module=>nothing) + deps = deps_eltype * "[" * join(deps_strs, ",") * "]" trace = isassigned(PRECOMPILE_TRACE_COMPILE) ? `--trace-compile=$(PRECOMPILE_TRACE_COMPILE[])` : `` io = open(pipeline(`$(julia_cmd()::Cmd) -O0 --output-ji $output --output-incremental=yes diff --git a/base/math.jl b/base/math.jl index 3857b1b1e8c10..d6cbaf6068a3f 100644 --- a/base/math.jl +++ b/base/math.jl @@ -49,6 +49,9 @@ are promoted to a common type. See also [`clamp!`](@ref), [`min`](@ref), [`max`](@ref). +!!! compat "Julia 1.3" + `missing` as the first argument requires at least Julia 1.3. + # Examples ```jldoctest julia> clamp.([pi, 1.0, big(10)], 2.0, 9.0) @@ -98,6 +101,9 @@ clamp(x, ::Type{T}) where {T<:Integer} = clamp(x, typemin(T), typemax(T)) % T Restrict values in `array` to the specified range, in-place. See also [`clamp`](@ref). +!!! compat "Julia 1.3" + `missing` entries in `array` require at least Julia 1.3. + # Examples ```jldoctest julia> row = collect(-4:4)'; diff --git a/base/multidimensional.jl b/base/multidimensional.jl index a63bd48b2e094..8bbfc6d2f2be7 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -1127,6 +1127,25 @@ end Copy the block of `src` in the range of `Rsrc` to the block of `dest` in the range of `Rdest`. The sizes of the two regions must match. + +# Examples +```jldoctest +julia> A = zeros(5, 5); + +julia> B = [1 2; 3 4]; + +julia> Ainds = CartesianIndices((2:3, 2:3)); + +julia> Binds = CartesianIndices(B); + +julia> copyto!(A, Ainds, B, Binds) +5×5 Matrix{Float64}: + 0.0 0.0 0.0 0.0 0.0 + 0.0 1.0 2.0 0.0 0.0 + 0.0 3.0 4.0 0.0 0.0 + 0.0 0.0 0.0 0.0 0.0 + 0.0 0.0 0.0 0.0 0.0 +``` """ copyto!(::AbstractArray, ::CartesianIndices, ::AbstractArray, ::CartesianIndices) @@ -1147,6 +1166,7 @@ See also [`circshift`](@ref). dest === src && throw(ArgumentError("dest and src must be separate arrays")) inds = axes(src) axes(dest) == inds || throw(ArgumentError("indices of src and dest must match (got $inds and $(axes(dest)))")) + isempty(src) && return dest _circshift!(dest, (), src, (), inds, fill_to_length(shiftamt, 0, Val(N))) end diff --git a/base/opaque_closure.jl b/base/opaque_closure.jl index d14e72db57213..3720a1ef2043c 100644 --- a/base/opaque_closure.jl +++ b/base/opaque_closure.jl @@ -1,3 +1,5 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + """ @opaque (args...) -> body diff --git a/base/set.jl b/base/set.jl index 6511d1dd7e108..dd1400d11dba1 100644 --- a/base/set.jl +++ b/base/set.jl @@ -44,7 +44,7 @@ empty(s::AbstractSet{T}, ::Type{U}=T) where {T,U} = Set{U}() # by default, a Set is returned emptymutable(s::AbstractSet{T}, ::Type{U}=T) where {T,U} = Set{U}() -_similar_for(c::AbstractSet, ::Type{T}, itr, isz) where {T} = empty(c, T) +_similar_for(c::AbstractSet, ::Type{T}, itr, isz, len) where {T} = empty(c, T) function show(io::IO, s::Set) if isempty(s) diff --git a/base/show.jl b/base/show.jl index f3110d5981429..c7d236979b956 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1315,7 +1315,8 @@ is_id_char(c::AbstractChar) = ccall(:jl_id_char, Cint, (UInt32,), c) != 0 isidentifier(s) -> Bool Return whether the symbol or string `s` contains characters that are parsed as -a valid identifier in Julia code. +a valid ordinary identifier (not a binary/unary operator) in Julia code; +see also [`Base.isoperator`](@ref). Internally Julia allows any sequence of characters in a `Symbol` (except `\\0`s), and macros automatically use variable names containing `#` in order to avoid diff --git a/base/timing.jl b/base/timing.jl index e229fbeb328e9..157fe288b9708 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -164,6 +164,16 @@ function timev_print(elapsedtime, diff::GC_Diff, compile_time) padded_nonzero_print(diff.full_sweep, "full collections") end +# Like a try-finally block, except without introducing the try scope +# NOTE: This is deprecated and should not be used from user logic. A proper solution to +# this problem will be introduced in https://github.com/JuliaLang/julia/pull/39217 +macro __tryfinally(ex, fin) + Expr(:tryfinally, + :($(esc(ex))), + :($(esc(fin))) + ) +end + """ @time @@ -207,9 +217,10 @@ macro time(ex) local stats = gc_num() local elapsedtime = time_ns() local compile_elapsedtime = cumulative_compile_time_ns_before() - local val = $(esc(ex)) - compile_elapsedtime = cumulative_compile_time_ns_after() - compile_elapsedtime - elapsedtime = time_ns() - elapsedtime + local val = @__tryfinally($(esc(ex)), + (elapsedtime = time_ns() - elapsedtime; + compile_elapsedtime = cumulative_compile_time_ns_after() - compile_elapsedtime) + ) local diff = GC_Diff(gc_num(), stats) time_print(elapsedtime, diff.allocd, diff.total_time, gc_alloc_count(diff), compile_elapsedtime, true) val @@ -253,9 +264,10 @@ macro timev(ex) local stats = gc_num() local elapsedtime = time_ns() local compile_elapsedtime = cumulative_compile_time_ns_before() - local val = $(esc(ex)) - compile_elapsedtime = cumulative_compile_time_ns_after() - compile_elapsedtime - elapsedtime = time_ns() - elapsedtime + local val = @__tryfinally($(esc(ex)), + (elapsedtime = time_ns() - elapsedtime; + compile_elapsedtime = cumulative_compile_time_ns_after() - compile_elapsedtime) + ) local diff = GC_Diff(gc_num(), stats) timev_print(elapsedtime, diff, compile_elapsedtime) val diff --git a/base/tuple.jl b/base/tuple.jl index 77fa6ba0ea1a3..597a5629efa90 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -356,10 +356,10 @@ end ## filter ## -filter(f, xs::Tuple) = afoldl((ys, x) -> f(x) ? (ys..., x) : ys, (), xs...) +filter_rec(f, xs::Tuple) = afoldl((ys, x) -> f(x) ? (ys..., x) : ys, (), xs...) # use Array for long tuples -filter(f, t::Any32) = Tuple(filter(f, collect(t))) +filter(f, t::Tuple) = length(t) < 32 ? filter_rec(f, t) : Tuple(filter(f, collect(t))) ## comparison ## diff --git a/base/util.jl b/base/util.jl index 7e11521e61591..e0147acceb9d0 100644 --- a/base/util.jl +++ b/base/util.jl @@ -566,6 +566,7 @@ function runtests(tests = ["all"]; ncores::Int = ceil(Int, Sys.CPU_THREADS::Int seed !== nothing && push!(tests, "--seed=0x$(string(seed % UInt128, base=16))") # cast to UInt128 to avoid a minus sign ENV2 = copy(ENV) ENV2["JULIA_CPU_THREADS"] = "$ncores" + ENV2["JULIA_DEPOT_PATH"] = mktempdir(; cleanup = true) try run(setenv(`$(julia_cmd()) $(joinpath(Sys.BINDIR::String, Base.DATAROOTDIR, "julia", "test", "runtests.jl")) $tests`, ENV2)) diff --git a/cli/jl_exports.h b/cli/jl_exports.h index 35d2767726865..1221c97c1d676 100644 --- a/cli/jl_exports.h +++ b/cli/jl_exports.h @@ -1,4 +1,5 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license + // Bring in the curated lists of exported data and function symbols, then // perform C preprocessor magic upon them to generate lists of declarations and // functions to re-export our function symbols from libjulia-internal to libjulia. @@ -19,17 +20,26 @@ JL_EXPORTED_DATA_SYMBOLS(XX) #define XX(name) JL_DLLEXPORT void name(void); typedef void (anonfunc)(void); JL_EXPORTED_FUNCS(XX) +#ifdef _OS_WINDOWS_ +JL_EXPORTED_FUNCS_WIN(XX) +#endif #undef XX -// Define holder locations for function addresses as `const void * $(name)_addr = & $(name);` -#define XX(name) JL_HIDDEN anonfunc * name##_addr = (anonfunc*)&name; +// Define holder locations for function addresses as `const void * $(name)_addr = NULL; +#define XX(name) JL_HIDDEN anonfunc * name##_addr = NULL; JL_EXPORTED_FUNCS(XX) +#ifdef _OS_WINDOWS_ +JL_EXPORTED_FUNCS_WIN(XX) +#endif #undef XX // Generate lists of function names and addresses -#define XX(name) #name, +#define XX(name) "i" #name, static const char *const jl_exported_func_names[] = { JL_EXPORTED_FUNCS(XX) +#ifdef _OS_WINDOWS_ + JL_EXPORTED_FUNCS_WIN(XX) +#endif NULL }; #undef XX @@ -37,6 +47,9 @@ static const char *const jl_exported_func_names[] = { #define XX(name) &name##_addr, static anonfunc **const jl_exported_func_addrs[] = { JL_EXPORTED_FUNCS(XX) +#ifdef _OS_WINDOWS_ + JL_EXPORTED_FUNCS_WIN(XX) +#endif NULL }; #undef XX diff --git a/cli/list_strip_symbols.h b/cli/list_strip_symbols.h index e1a96261fe05a..0ea0f7ac9f6ac 100644 --- a/cli/list_strip_symbols.h +++ b/cli/list_strip_symbols.h @@ -4,4 +4,7 @@ #include "trampolines/common.h" #define XX(x) --strip-symbol=CNAME(x) JL_EXPORTED_FUNCS(XX) +#ifdef _OS_WINDOWS_ +JL_EXPORTED_FUNCS_WIN(XX) +#endif #undef XX diff --git a/cli/loader.h b/cli/loader.h index 6df1557ec2c26..4465b98d4f9cf 100644 --- a/cli/loader.h +++ b/cli/loader.h @@ -53,7 +53,7 @@ # endif #define JL_HIDDEN #else -# if defined(LIBRARY_EXPORTS) && defined(_OS_LINUX) +# if defined(LIBRARY_EXPORTS) && defined(_OS_LINUX_) # define JL_DLLEXPORT __attribute__ ((visibility("protected"))) # else # define JL_DLLEXPORT __attribute__ ((visibility("default"))) diff --git a/cli/loader_exe.c b/cli/loader_exe.c index e5bb9d1a5fbe7..d1269de1ec5fd 100644 --- a/cli/loader_exe.c +++ b/cli/loader_exe.c @@ -1,4 +1,5 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license + // This defines a bare-bones loader that opens `libjulia` and immediately invokes its `load_repl()` function. #include "loader.h" diff --git a/cli/loader_lib.c b/cli/loader_lib.c index d921055f08221..215107cc0bb9c 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -1,4 +1,5 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license + // This file defines an RPATH-style relative path loader for all platforms #include "loader.h" @@ -176,8 +177,8 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { // Once we have libjulia-internal loaded, re-export its symbols: for (unsigned int symbol_idx=0; jl_exported_func_names[symbol_idx] != NULL; ++symbol_idx) { void *addr = lookup_symbol(libjulia_internal, jl_exported_func_names[symbol_idx]); - if (addr == NULL || addr == *jl_exported_func_addrs[symbol_idx]) { - jl_loader_print_stderr3("ERROR: Unable to load ", jl_exported_func_names[symbol_idx], " from libjulia-internal"); + if (addr == NULL) { + jl_loader_print_stderr3("ERROR: Unable to load ", jl_exported_func_names[symbol_idx], " from libjulia-internal\n"); exit(1); } (*jl_exported_func_addrs[symbol_idx]) = addr; diff --git a/cli/loader_win_utils.c b/cli/loader_win_utils.c index 46b07cb1796c7..621834a030c52 100644 --- a/cli/loader_win_utils.c +++ b/cli/loader_win_utils.c @@ -1,3 +1,5 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + // Workarounds for compiling via mingw without using libgcc_s typedef struct { HANDLE fd; diff --git a/cli/trampolines/common.h b/cli/trampolines/common.h index 06d7b9e236971..00d703c341515 100644 --- a/cli/trampolines/common.h +++ b/cli/trampolines/common.h @@ -1,3 +1,7 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#include "../../src/support/platform.h" + // Preprocessor annoyances #define CONCAT_(x,y) x##y #define CONCAT(x,y) CONCAT_(x, y) diff --git a/cli/trampolines/trampolines_aarch64.S b/cli/trampolines/trampolines_aarch64.S index bffeab76c1763..0e5aaf397c8fa 100644 --- a/cli/trampolines/trampolines_aarch64.S +++ b/cli/trampolines/trampolines_aarch64.S @@ -1,3 +1,5 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + #include "common.h" #include "../../src/jl_exported_funcs.inc" @@ -12,4 +14,7 @@ CNAME(name)##: SEP \ .cfi_endproc SEP \ JL_EXPORTED_FUNCS(XX) +#ifdef _OS_WINDOWS_ +JL_EXPORTED_FUNCS_WIN(XX) +#endif #undef XX diff --git a/cli/trampolines/trampolines_arm.S b/cli/trampolines/trampolines_arm.S index f99b7820360b2..11ae17a1a75d1 100644 --- a/cli/trampolines/trampolines_arm.S +++ b/cli/trampolines/trampolines_arm.S @@ -1,3 +1,5 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + #include "common.h" #include "../../src/jl_exported_funcs.inc" @@ -15,4 +17,7 @@ CONCAT(.L,CNAMEADDR(name))##: ; \ .cfi_endproc; \ JL_EXPORTED_FUNCS(XX) +#ifdef _OS_WINDOWS_ +JL_EXPORTED_FUNCS_WIN(XX) +#endif #undef XX diff --git a/cli/trampolines/trampolines_i686.S b/cli/trampolines/trampolines_i686.S index f27949afa47b8..e0bb0ff513922 100644 --- a/cli/trampolines/trampolines_i686.S +++ b/cli/trampolines/trampolines_i686.S @@ -1,3 +1,5 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + #include "common.h" #include "../../src/jl_exported_funcs.inc" @@ -13,4 +15,7 @@ CNAME(name)##:; \ EXPORT(name); \ JL_EXPORTED_FUNCS(XX) +#ifdef _OS_WINDOWS_ +JL_EXPORTED_FUNCS_WIN(XX) +#endif #undef XX diff --git a/cli/trampolines/trampolines_powerpc64le.S b/cli/trampolines/trampolines_powerpc64le.S index cd64f656362d0..ccf40d857b787 100644 --- a/cli/trampolines/trampolines_powerpc64le.S +++ b/cli/trampolines/trampolines_powerpc64le.S @@ -1,3 +1,5 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + #include "common.h" #include "../../src/jl_exported_funcs.inc" diff --git a/cli/trampolines/trampolines_x86_64.S b/cli/trampolines/trampolines_x86_64.S index e06434cf540e5..c00b7c764f699 100644 --- a/cli/trampolines/trampolines_x86_64.S +++ b/cli/trampolines/trampolines_x86_64.S @@ -1,3 +1,5 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + #include "common.h" #include "../../src/jl_exported_funcs.inc" @@ -17,4 +19,7 @@ SEH_END(); \ EXPORT(name); \ JL_EXPORTED_FUNCS(XX) +#ifdef _OS_WINDOWS_ +JL_EXPORTED_FUNCS_WIN(XX) +#endif #undef XX diff --git a/contrib/add_license_to_files.jl b/contrib/add_license_to_files.jl index c5aa0f49d99d3..5f8fa65c143eb 100644 --- a/contrib/add_license_to_files.jl +++ b/contrib/add_license_to_files.jl @@ -15,6 +15,7 @@ const print_result = true # prints files which where not processed. const rootdirs = [ "../base", + "../cli", "../contrib", "../src", "../stdlib", @@ -31,6 +32,7 @@ const excludedirs = [ const skipfiles = [ "../contrib/add_license_to_files.jl", + "../contrib/asan/check.jl", # files to check - already copyright # see: https://github.com/JuliaLang/julia/pull/11073#issuecomment-98099389 "../base/special/trig.jl", @@ -49,6 +51,7 @@ const skipfiles = [ "../src/support/END.h", "../src/support/ENTRY.amd64.h", "../src/support/ENTRY.i387.h", + "../src/support/_setjmp.win32.S", "../src/support/MurmurHash3.c", "../src/support/MurmurHash3.h", "../src/support/asprintf.c", @@ -66,6 +69,7 @@ const ext_prefix = Dict([ (".h", "// "), (".c", "// "), (".cpp", "// "), + (".S", "// "), ]) const new_license = "This file is a part of Julia. License is MIT: https://julialang.org/license" @@ -104,6 +108,7 @@ function getfilespaths!(filepaths::Vector, rootdir::AbstractString) abs_rootdir = abspath(rootdir) for name in readdir(abs_rootdir) path = joinpath(abs_rootdir, name) + islink(path) && continue if isdir(path) getfilespaths!(filepaths, path) else @@ -118,6 +123,7 @@ function add_license_line!(unprocessed::Vector, src::AbstractString, new_license for name in readdir(src) path = normpath(joinpath(src, name)) + islink(path) && continue if isdir(path) if path in abs_excludedirs getfilespaths!(unprocessed, path) diff --git a/contrib/asan/build.sh b/contrib/asan/build.sh index 5ef75a78fa2b1..83853b913b077 100755 --- a/contrib/asan/build.sh +++ b/contrib/asan/build.sh @@ -1,5 +1,6 @@ #!/bin/bash # This file is a part of Julia. License is MIT: https://julialang.org/license + # # Usage: # contrib/asan/build.sh [...] diff --git a/deps/blastrampoline.mk b/deps/blastrampoline.mk index 1ba1d1cdada8d..9795d6bb4b5bd 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 := git://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/Pkg-252e895056b17490bfeabd81f52743bad947e997.tar.gz/md5 b/deps/checksums/Pkg-252e895056b17490bfeabd81f52743bad947e997.tar.gz/md5 deleted file mode 100644 index 21bbb58e5a91e..0000000000000 --- a/deps/checksums/Pkg-252e895056b17490bfeabd81f52743bad947e997.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -743007182ff00d0907bcc6045767e72a diff --git a/deps/checksums/Pkg-252e895056b17490bfeabd81f52743bad947e997.tar.gz/sha512 b/deps/checksums/Pkg-252e895056b17490bfeabd81f52743bad947e997.tar.gz/sha512 deleted file mode 100644 index 24cf91de419ab..0000000000000 --- a/deps/checksums/Pkg-252e895056b17490bfeabd81f52743bad947e997.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -66b19b7ac0899b8b8c9817c926329562fbe07d015224a33b9ee23118e9dd07783b5760dc406f028d613b8ae69101ebc54ea6c52cceecffbd9e3a6f7f9c0cf085 diff --git a/deps/checksums/Pkg-b79518775a82c9eb3353b5dc2e9d855fe155b120.tar.gz/md5 b/deps/checksums/Pkg-b79518775a82c9eb3353b5dc2e9d855fe155b120.tar.gz/md5 new file mode 100644 index 0000000000000..24cd72d481e99 --- /dev/null +++ b/deps/checksums/Pkg-b79518775a82c9eb3353b5dc2e9d855fe155b120.tar.gz/md5 @@ -0,0 +1 @@ +080ac9de622b050b678e789ced7dc4a3 diff --git a/deps/checksums/Pkg-b79518775a82c9eb3353b5dc2e9d855fe155b120.tar.gz/sha512 b/deps/checksums/Pkg-b79518775a82c9eb3353b5dc2e9d855fe155b120.tar.gz/sha512 new file mode 100644 index 0000000000000..a6f2bf2b2e1de --- /dev/null +++ b/deps/checksums/Pkg-b79518775a82c9eb3353b5dc2e9d855fe155b120.tar.gz/sha512 @@ -0,0 +1 @@ +3570cfc4768f9f35d80cde7ce985760da1ca0c9f72fdc56c3f935ae7cc771d023530643a846e637675cd297edb44c1aa53381472719461fdf3724d5b220210f7 diff --git a/deps/checksums/SHA-c5dd533520393b9dea34ad25287f222dc28fe07a.tar.gz/md5 b/deps/checksums/SHA-c5dd533520393b9dea34ad25287f222dc28fe07a.tar.gz/md5 new file mode 100644 index 0000000000000..8cd7d78228287 --- /dev/null +++ b/deps/checksums/SHA-c5dd533520393b9dea34ad25287f222dc28fe07a.tar.gz/md5 @@ -0,0 +1 @@ +b81065c52f24a5577ade96418d09d19c diff --git a/deps/checksums/SHA-c5dd533520393b9dea34ad25287f222dc28fe07a.tar.gz/sha512 b/deps/checksums/SHA-c5dd533520393b9dea34ad25287f222dc28fe07a.tar.gz/sha512 new file mode 100644 index 0000000000000..6f3f44b089912 --- /dev/null +++ b/deps/checksums/SHA-c5dd533520393b9dea34ad25287f222dc28fe07a.tar.gz/sha512 @@ -0,0 +1 @@ +9c9cf0f6fd2826aed930715c6f1d5b9e75b4e4d09d0d552ced1f1a4c18c8f61de3a920bcad66fb9d03a833402078d315e2a028454d60dfc5d5f4cbb4e3433d97 diff --git a/deps/checksums/Statistics-54f9b0d999813aa9fab039f632df222ffd2a96a8.tar.gz/md5 b/deps/checksums/Statistics-54f9b0d999813aa9fab039f632df222ffd2a96a8.tar.gz/md5 deleted file mode 100644 index 62f1954037241..0000000000000 --- a/deps/checksums/Statistics-54f9b0d999813aa9fab039f632df222ffd2a96a8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -4c09536f4f769b23e88fee769f5a09bd diff --git a/deps/checksums/Statistics-54f9b0d999813aa9fab039f632df222ffd2a96a8.tar.gz/sha512 b/deps/checksums/Statistics-54f9b0d999813aa9fab039f632df222ffd2a96a8.tar.gz/sha512 deleted file mode 100644 index a79b037b94de2..0000000000000 --- a/deps/checksums/Statistics-54f9b0d999813aa9fab039f632df222ffd2a96a8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -e409fa943a9683a129b80c78ef74572df316ed414dfc8c208f1500d0f07d4d41870d44654446e2c20d1b9ed11e62c4fc6107b6e5789939edbd049fc2aaf22f63 diff --git a/deps/checksums/Statistics-74897fed33700dba92578aa0fefef5b99ba16086.tar.gz/md5 b/deps/checksums/Statistics-74897fed33700dba92578aa0fefef5b99ba16086.tar.gz/md5 new file mode 100644 index 0000000000000..5a2ea2906c116 --- /dev/null +++ b/deps/checksums/Statistics-74897fed33700dba92578aa0fefef5b99ba16086.tar.gz/md5 @@ -0,0 +1 @@ +c6defdea70beb0f10b22c3f2770f93be diff --git a/deps/checksums/Statistics-74897fed33700dba92578aa0fefef5b99ba16086.tar.gz/sha512 b/deps/checksums/Statistics-74897fed33700dba92578aa0fefef5b99ba16086.tar.gz/sha512 new file mode 100644 index 0000000000000..537df2a1e0602 --- /dev/null +++ b/deps/checksums/Statistics-74897fed33700dba92578aa0fefef5b99ba16086.tar.gz/sha512 @@ -0,0 +1 @@ +1cf4cfd24f647c8a6f0937bf125bdd66fadc4951fa356bdce2e63091fc4f4ec4e080559526dbb5133ff8cb58f31ec5aa544fd8b5422355d07e3d112b9dc6e698 diff --git a/deps/checksums/blastrampoline b/deps/checksums/blastrampoline index cdb05346bfe93..5b3a5d01c051c 100644 --- a/deps/checksums/blastrampoline +++ b/deps/checksums/blastrampoline @@ -1,5 +1,5 @@ -blastrampoline-23de7a09bf354fe6f655c457bab5bf47fdd2486d.tar.gz/md5/0c8016a6e30bc2237184b816b613d11c -blastrampoline-23de7a09bf354fe6f655c457bab5bf47fdd2486d.tar.gz/sha512/7b7dbb101cf05ac833a8e5b09f1eec8eb99f0caafbe80075751a5f7e0bfe03a3b19d11d3507dadd13f503cfa9fc1a6cd53f3461af7d5afb39ca385a0ee26120b +blastrampoline-23de7a09bf354fe6f655c457bab5bf47fdd2486d.tar.gz/md5/b81efa951fd909591339189f5909ff6b +blastrampoline-23de7a09bf354fe6f655c457bab5bf47fdd2486d.tar.gz/sha512/1c2558bab0aeaa76e7094d8a6a9798c95f2cf4efe2960640b70f1fd752f3dfb73813d9de93b539426376571febaab22ac22c2f903ccdf3296c7b067af92fecdc libblastrampoline.v3.1.0+0.aarch64-apple-darwin.tar.gz/md5/9a72574c810323ebe7b496266a5b3d90 libblastrampoline.v3.1.0+0.aarch64-apple-darwin.tar.gz/sha512/559c91374882a137ce8b9f008e9d815dfebc175f65ac07bf784d590e31a07f60302de3d498e5dcc3f409f59fc2c7fbd1fb32623d25ed65bcc31c35a74c11f260 libblastrampoline.v3.1.0+0.aarch64-linux-gnu.tar.gz/md5/95802755d3b6205c88163ba313662fac diff --git a/deps/checksums/gmp b/deps/checksums/gmp index da510e3dc2388..6b95ca883ddf8 100644 --- a/deps/checksums/gmp +++ b/deps/checksums/gmp @@ -1,5 +1,3 @@ -gmp-6.2.1.tar.bz2/md5/28971fc21cf028042d4897f02fd355ea -gmp-6.2.1.tar.bz2/sha512/8904334a3bcc5c896ececabc75cda9dec642e401fb5397c4992c4fabea5e962c9ce8bd44e8e4233c34e55c8010cc28db0545f5f750cbdbb5f00af538dc763be9 GMP.v6.2.1+0.aarch64-apple-darwin.tar.gz/md5/e805c580078e4d6bcaeb6781cb6d56fa GMP.v6.2.1+0.aarch64-apple-darwin.tar.gz/sha512/62435e80f5fa0b67e2788c8bfc3681426add7a9b2853131bbebe890d1a2d9b54cebaea0860f6ddd0e93e1ae302baba39851d5f58a65acf0b2a9ea1226bb4eea4 GMP.v6.2.1+0.aarch64-linux-gnu-cxx03.tar.gz/md5/5384d6ba6fd408bc71c2781b643cd59a @@ -58,3 +56,63 @@ GMP.v6.2.1+0.x86_64-w64-mingw32-cxx03.tar.gz/md5/1499a265b438cf5169286c1830eb573 GMP.v6.2.1+0.x86_64-w64-mingw32-cxx03.tar.gz/sha512/d2e6fe76abe0a0cb1a7445ea93cd5bd0bf9f729aec8df9c76d06a1f6f5e67cce442be69b66950eb33aa22cfda2e5a308f2bade64018a27bebfcb4b7a97e1d047 GMP.v6.2.1+0.x86_64-w64-mingw32-cxx11.tar.gz/md5/fdb4187f617511d8eb19f67f8499a8d0 GMP.v6.2.1+0.x86_64-w64-mingw32-cxx11.tar.gz/sha512/bb6d8ead1c20cffebc2271461d3787cfad794fee2b32e23583af6521c0667ed9107805268a996d23d6edcab9fe653e542a210cab07252f7713af0c23feb76fb3 +GMP.v6.2.1+1.aarch64-apple-darwin.tar.gz/md5/03cb14ac16daabb4a77fe1c78e8e48a9 +GMP.v6.2.1+1.aarch64-apple-darwin.tar.gz/sha512/5b8f974a07f579272981f5ebe44191385a4ce95f58d434a3565ffa827a6d65824cbe4173736b7328630bbccfe6af4242195aec24de3f0aa687e2e32a18a97a5c +GMP.v6.2.1+1.aarch64-linux-gnu-cxx03.tar.gz/md5/0ce7d419a49f2f90033618bdda2588e7 +GMP.v6.2.1+1.aarch64-linux-gnu-cxx03.tar.gz/sha512/16363dedaae116fa0d493182aeadb2ffa7f990f1813e4b47cae3cd61ca71f23b65267ea4e2c698d52bd78d61e12feaa73179d7b86ab6d6df01eeb7b6a9b27958 +GMP.v6.2.1+1.aarch64-linux-gnu-cxx11.tar.gz/md5/011f1cdc39b9e529b4b6ea80f4c33108 +GMP.v6.2.1+1.aarch64-linux-gnu-cxx11.tar.gz/sha512/1ed2139580c5c78578f350ee83dbf9cd0120d9d36e1951438d757f5734cda7931600b3f83bfe0d0d806926636d6aea8048c6b64aa42a22e59310282c2428f417 +GMP.v6.2.1+1.aarch64-linux-musl-cxx03.tar.gz/md5/34f17083a1f142c284b707cc82407b00 +GMP.v6.2.1+1.aarch64-linux-musl-cxx03.tar.gz/sha512/dd32912c31a8422734c2e5d5a37001ac18f0e9de151982583d9dc185e5cc3e45076d737729345cca8e8eaf42993d4102353261a2de245e26a8a9cd86960a2fbf +GMP.v6.2.1+1.aarch64-linux-musl-cxx11.tar.gz/md5/9ba1b822f20f88a1e4c6e81dc8c4fdc1 +GMP.v6.2.1+1.aarch64-linux-musl-cxx11.tar.gz/sha512/d8a4ecd5c35022b9c912c3b4fabe3a4c31258d6a1bd38e4fea13a3da53206a29bfd90f4d602f6e3ee3ee271d84289d1ecdf45534adfabf7e657daef5b5cb0b21 +GMP.v6.2.1+1.armv6l-linux-gnueabihf-cxx03.tar.gz/md5/23e28efa2579d636cb4c80036da5d4ea +GMP.v6.2.1+1.armv6l-linux-gnueabihf-cxx03.tar.gz/sha512/02c8023958fa616c1f944898e686510d449b743d053cfd42f526e9c4fe3ff3dd9de7309694b8537b4bb6dc978085339eb787983ec4ba32dc041448c912a8b982 +GMP.v6.2.1+1.armv6l-linux-gnueabihf-cxx11.tar.gz/md5/bf2a2c4f81f6d04746cc528438f62639 +GMP.v6.2.1+1.armv6l-linux-gnueabihf-cxx11.tar.gz/sha512/1c152abeed24761c775e78a64835f8e61b28b16cbc29a6fde88fa4fdbf2a5782cd62697bd03a552d873995bda58b7bdc081c11ecd5e4badde2dea426e5218116 +GMP.v6.2.1+1.armv6l-linux-musleabihf-cxx03.tar.gz/md5/25cbceed2cf1bb12601fe285c342d6b0 +GMP.v6.2.1+1.armv6l-linux-musleabihf-cxx03.tar.gz/sha512/37d8b21bf59c0c555f2b59d6dca4f486bf1725ae18a7fea9a2f31533c54ebb818b5ddb88ec8aa2b618e0ecad78973659abd1a9f095f64ef65067ab8ed08d7801 +GMP.v6.2.1+1.armv6l-linux-musleabihf-cxx11.tar.gz/md5/8ec72c769625a218c6951abed32b3684 +GMP.v6.2.1+1.armv6l-linux-musleabihf-cxx11.tar.gz/sha512/4cb9ccb97859b0918002b649e1b5e74e1fc89a2daeec6f32d5a06ce0d84217f54d1ee788f472cebeefc73ef52284a3d5607efbed47058b438d2dcbcf9f384ed0 +GMP.v6.2.1+1.armv7l-linux-gnueabihf-cxx03.tar.gz/md5/6f799d6516cc46af28eacf8409634825 +GMP.v6.2.1+1.armv7l-linux-gnueabihf-cxx03.tar.gz/sha512/541c1e03726584ddb672a83becdc9a99c68f5da9a7415750d582753b47774910bf25cee7fe21f5b5c2a80ff8ce87fc327abd45bf54d6cfe821cb202c81b67e43 +GMP.v6.2.1+1.armv7l-linux-gnueabihf-cxx11.tar.gz/md5/17dba9ebcc1bf4637095a98a876375a8 +GMP.v6.2.1+1.armv7l-linux-gnueabihf-cxx11.tar.gz/sha512/648220e632618d23e8611e10fa4bb2e581ed4432e3fff77d0d7349a7585bffa65ae57bf1ce64c550bf6d2acc016f499c0bbbfed8088281445b9d4ecbbf9a64bc +GMP.v6.2.1+1.armv7l-linux-musleabihf-cxx03.tar.gz/md5/79c77b81cc16fd22ad4cef75af7aa220 +GMP.v6.2.1+1.armv7l-linux-musleabihf-cxx03.tar.gz/sha512/0059ba54806ef0ca6621ddcd309a18922c4c7d9d9e214bc6870b6338a9449a472934cc27569402741d41a18dd53a896aae2f68b788f853fd4ea3db63035c8153 +GMP.v6.2.1+1.armv7l-linux-musleabihf-cxx11.tar.gz/md5/87b79bfc5c780e214863d0f0c1944da9 +GMP.v6.2.1+1.armv7l-linux-musleabihf-cxx11.tar.gz/sha512/88dcabcf96d8f2dcc7968333a94adcb8e8a91615b67ca23edf75c3368a89ef60a8deff8e8532d0cd4d5dd5356343b753b0ae0bf88ce7e190639468bf8170939a +GMP.v6.2.1+1.i686-linux-gnu-cxx03.tar.gz/md5/61d39e42ab6fd5844e938605e357b336 +GMP.v6.2.1+1.i686-linux-gnu-cxx03.tar.gz/sha512/8e0d382adf6b45cbf613092cee524551a04096b0bc6fb8893701edae9c1928bda67b5522cae3ef954a882ff73b735190881ade37495d9d1a6db88ed6fbcdc6b1 +GMP.v6.2.1+1.i686-linux-gnu-cxx11.tar.gz/md5/b66b49054426adf3e1d3454a80010d97 +GMP.v6.2.1+1.i686-linux-gnu-cxx11.tar.gz/sha512/b28f22bbfbf796c4e959b1fa3433d46b4cf0dbd402c0497a6d4893c8030aa12fd038da4846d8bce02199f1da9b0158d78f2b4ff2636799ba139602775725ff6d +GMP.v6.2.1+1.i686-linux-musl-cxx03.tar.gz/md5/69ea3b3348813777a1682e41a117d7c3 +GMP.v6.2.1+1.i686-linux-musl-cxx03.tar.gz/sha512/048dd08b5891864e69504baf6328ef5423e0f8e31c5c6cfac552eb51b3ef943af83b7ac654c33e1a0cf061c5832e08eebb9c03dbda6532fbc24e160e99c2aae6 +GMP.v6.2.1+1.i686-linux-musl-cxx11.tar.gz/md5/e7c82091d29a3e5958442c9ec631ad78 +GMP.v6.2.1+1.i686-linux-musl-cxx11.tar.gz/sha512/8574f2e42e181a7bd1cf8aa8056a14d13efe555ee74b14e14aef1bdce7f26ce2afe41b4f85ee20de6823045d5ff38e4dbcebcc7042fff4288af1b7d296202d43 +GMP.v6.2.1+1.i686-w64-mingw32-cxx03.tar.gz/md5/dcef59aa056dcd56e6e36ad49174389f +GMP.v6.2.1+1.i686-w64-mingw32-cxx03.tar.gz/sha512/3cf3096c325ae2baea8b3c3aed4a26d649dc2bb3cf0d979809d9962521422ada3fdcdddbcfc52b27d43b473a1d3ed4a40368cdeb16cac4d32718c604dbc9f388 +GMP.v6.2.1+1.i686-w64-mingw32-cxx11.tar.gz/md5/b772a602b016e73dfc9a93908f51622b +GMP.v6.2.1+1.i686-w64-mingw32-cxx11.tar.gz/sha512/00e06591e2cc44100dca1a8897c72933bf4bd8c3c732daea99a9efa4d0a67f6a8820bf3e5d27583dfddc50d4cda656fa7462a2c453035d03657948f0051dc2fe +GMP.v6.2.1+1.powerpc64le-linux-gnu-cxx03.tar.gz/md5/b31c423855c4c5633b41301e3b424312 +GMP.v6.2.1+1.powerpc64le-linux-gnu-cxx03.tar.gz/sha512/2565176e2bbcb9deab25a91736e8b6de01e7dca619ed1fcc98cebcaaa144eb03f89f4f6d5989aa8454b0d1c7266d1ace690e6deef67c0cf5c3fc1c2ab4d41b43 +GMP.v6.2.1+1.powerpc64le-linux-gnu-cxx11.tar.gz/md5/1ed2494342b5713308f6ffed5fe3863d +GMP.v6.2.1+1.powerpc64le-linux-gnu-cxx11.tar.gz/sha512/c600802c81c77247a24a50ec0695f742177c8c9f090b4c345f9b0cd065b35183f49592a764cdb7b1b6d5ee3722e7dd26672d85db963d1e490731545a36d1e581 +GMP.v6.2.1+1.x86_64-apple-darwin.tar.gz/md5/51e00a2b55e9f81eb62abe23bb5f6fd9 +GMP.v6.2.1+1.x86_64-apple-darwin.tar.gz/sha512/91731427afd8df54b54d87b93006190a8b959438dc591eb5fa44724056911b8bd5588b2b1e70e9da3d8d6e9ce5aaa6fea66b0706f636cb56b3c860e8f3c0550a +GMP.v6.2.1+1.x86_64-linux-gnu-cxx03.tar.gz/md5/3f3a6f15e4e8499470bbe69a9ea885c1 +GMP.v6.2.1+1.x86_64-linux-gnu-cxx03.tar.gz/sha512/2659344ab097cd9542a5946c127a43af6fad05aa1445d69a4978d1a6d9a9f0e0502a5a60c6ca88acccb86d038dd10f2a72a7c2d4dd7ad5383c7d687e9720cc88 +GMP.v6.2.1+1.x86_64-linux-gnu-cxx11.tar.gz/md5/15ee858d8e1f07f18df8a893634d859e +GMP.v6.2.1+1.x86_64-linux-gnu-cxx11.tar.gz/sha512/9d8ffa570eb22a5a908679e06af4dd0ce8c06cf97ff9fd766baeca352a99bcc54b4b71b9c52829ba80043a688f2ed6a33b0302072518f2b16416235d5295ea00 +GMP.v6.2.1+1.x86_64-linux-musl-cxx03.tar.gz/md5/79078a236575994696e7328e34326243 +GMP.v6.2.1+1.x86_64-linux-musl-cxx03.tar.gz/sha512/d4b77a4056a2b0dcb6f789381fff720ab7481cc7edb4672756cb2057ed6475abeb6ea414e6cec3e2450ef7302b647d7d2fc2d9f7de52feddd7767548392e84bb +GMP.v6.2.1+1.x86_64-linux-musl-cxx11.tar.gz/md5/94f822c7521f83652d87fd5f1ad8bb19 +GMP.v6.2.1+1.x86_64-linux-musl-cxx11.tar.gz/sha512/fa4f70f81524d47b65d5cf3ff5abe38a691f09e3297c62f0db2512483702b9af33bc4a3c15f6f1465d6dce4eeb19f665f29872e6dd7caea0806f4c7fd32c2c5a +GMP.v6.2.1+1.x86_64-unknown-freebsd.tar.gz/md5/cdb93a733763e8a4fc29652fda8c8b13 +GMP.v6.2.1+1.x86_64-unknown-freebsd.tar.gz/sha512/ec529f57eb167bfcb367310b375a3cded007cbc386cab9b09faa9fe8f37a443302c674814ada6c82125ad0ce4aebecb75bb61633a21e7a3a00fc928fbe05cb4f +GMP.v6.2.1+1.x86_64-w64-mingw32-cxx03.tar.gz/md5/8b5be9da6a0a293e14ab1d589a622b98 +GMP.v6.2.1+1.x86_64-w64-mingw32-cxx03.tar.gz/sha512/73287b8390cac2ce8afc4565c5218ac739ed8a23c56754f4667570039f022b777284aee25d7857a94ff46fd502ac0fabe46f509a5f870b1aa074f6ed1278dcf1 +GMP.v6.2.1+1.x86_64-w64-mingw32-cxx11.tar.gz/md5/11bcbfc3b65b19d73c3abf92ec46cb6a +GMP.v6.2.1+1.x86_64-w64-mingw32-cxx11.tar.gz/sha512/1dd9a6fe5c4991483a2d46420cd892271d37d9d23c409ed782b7736ab1942cd6c42360efbc308b5684bd5f991c7a96e8d375f3e855dc537bb3089e3402eed110 +gmp-6.2.1.tar.bz2/md5/28971fc21cf028042d4897f02fd355ea +gmp-6.2.1.tar.bz2/sha512/8904334a3bcc5c896ececabc75cda9dec642e401fb5397c4992c4fabea5e962c9ce8bd44e8e4233c34e55c8010cc28db0545f5f750cbdbb5f00af538dc763be9 diff --git a/deps/gmp.mk b/deps/gmp.mk index 9093817b86829..1d7d85556a2af 100644 --- a/deps/gmp.mk +++ b/deps/gmp.mk @@ -22,11 +22,28 @@ $(SRCCACHE)/gmp-$(GMP_VER)/source-extracted: $(SRCCACHE)/gmp-$(GMP_VER).tar.bz2 checksum-gmp: $(SRCCACHE)/gmp-$(GMP_VER).tar.bz2 $(JLCHECKSUM) $< -$(SRCCACHE)/gmp-$(GMP_VER)/build-patched: $(SRCCACHE)/gmp-$(GMP_VER)/source-extracted - cd $(dir $@) && patch -p1 < $(SRCDIR)/patches/gmp-exception.patch - cd $(dir $@) && patch -p1 < $(SRCDIR)/patches/gmp_alloc_overflow_func.patch +# Apply fix to avoid using Apple ARM reserved register X18 +# Necessary for version 6.2.1, remove after next gmp release +$(SRCCACHE)/gmp-$(GMP_VER)/gmp-HG-changeset.patch-applied: $(SRCCACHE)/gmp-$(GMP_VER)/source-extracted + cd $(dir $@) && \ + patch -p1 < $(SRCDIR)/patches/gmp-HG-changeset.patch + echo 1 > $@ + +$(SRCCACHE)/gmp-$(GMP_VER)/gmp-exception.patch-applied: $(SRCCACHE)/gmp-$(GMP_VER)/gmp-HG-changeset.patch-applied + cd $(dir $@) && \ + patch -p1 < $(SRCDIR)/patches/gmp-exception.patch echo 1 > $@ +$(SRCCACHE)/gmp-$(GMP_VER)/gmp_alloc_overflow_func.patch-applied: $(SRCCACHE)/gmp-$(GMP_VER)/gmp-exception.patch-applied + cd $(dir $@) && \ + patch -p1 < $(SRCDIR)/patches/gmp_alloc_overflow_func.patch + echo 1 > $@ + +$(SRCCACHE)/gmp-$(GMP_VER)/build-patched: + $(SRCCACHE)/gmp-$(GMP_VER)/gmp-HG-changeset.patch-applied + $(SRCCACHE)/gmp-$(GMP_VER)/gmp-exception.patch-applied + $(SRCCACHE)/gmp-$(GMP_VER)/gmp_alloc_overflow_func.patch-applied + $(BUILDDIR)/gmp-$(GMP_VER)/build-configured: $(SRCCACHE)/gmp-$(GMP_VER)/source-extracted $(SRCCACHE)/gmp-$(GMP_VER)/build-patched mkdir -p $(dir $@) cd $(dir $@) && \ diff --git a/deps/llvm.mk b/deps/llvm.mk index f9a120f0a19c8..b3fa98e67cdbb 100644 --- a/deps/llvm.mk +++ b/deps/llvm.mk @@ -515,7 +515,7 @@ $(eval $(call LLVM_PROJ_PATCH,llvm-11-D97571-AArch64-loh)) # remove for LLVM 13 $(eval $(call LLVM_PROJ_PATCH,llvm-11-aarch64-addrspace)) # remove for LLVM 13 $(eval $(call LLVM_PROJ_PATCH,llvm-12-fde-symbols-aarch64)) # remove for LLVM 13 $(eval $(call LLVM_PROJ_PATCH,llvm-12-force-eh_frame-aarch64)) # remove for LLVM 13 -$(eval $(call LLVM_PROJ_PATCH,llvm-12-D109203-stackprobe-x86)) # remove for LLVM 14 +$(eval $(call LLVM_PROJ_PATCH,llvm-12-D109203-stackprobe-x86)) # remove for LLVM 13 endif # LLVM_VER 12.0 ifeq ($(LLVM_VER_SHORT),13.0) @@ -530,7 +530,6 @@ $(eval $(call LLVM_PATCH,llvm-11-D93154-globalisel-as)) $(eval $(call LLVM_PATCH,llvm-11-D94813-mergeicmps)) # remove for LLVM 14 $(eval $(call LLVM_PATCH,llvm-13-AArch64-FastIsel-bug)) $(eval $(call LLVM_PATCH,llvm-13-D97435-AArch64-movaddrreg)) -$(eval $(call LLVM_PROJ_PATCH,llvm-13-D109203-stackprobe-x86)) # remove for LLVM 14 (nominated for backport) endif # LLVM_VER 13.0 # Add a JL prefix to the version map. DO NOT REMOVE diff --git a/deps/patches/gmp-HG-changeset.patch b/deps/patches/gmp-HG-changeset.patch new file mode 100644 index 0000000000000..7437fb6f2f748 --- /dev/null +++ b/deps/patches/gmp-HG-changeset.patch @@ -0,0 +1,520 @@ + +# HG changeset patch +# User Torbjorn Granlund +# Date 1606685500 -3600 +# Node ID 5f32dbc41afc1f8cd77af1614f0caeb24deb7d7b +# Parent 94c84d919f83ba963ed1809f8e80c7bef32db55c +Avoid the x18 register since it is reserved on Darwin. + +diff -r 94c84d919f83 -r 5f32dbc41afc mpn/arm64/aors_n.asm +--- a/mpn/arm64/aors_n.asm Sat Nov 28 23:38:32 2020 +0100 ++++ b/mpn/arm64/aors_n.asm Sun Nov 29 22:31:40 2020 +0100 +@@ -68,7 +68,7 @@ + EPILOGUE() + PROLOGUE(func_n) + CLRCY +-L(ent): lsr x18, n, #2 ++L(ent): lsr x17, n, #2 + tbz n, #0, L(bx0) + + L(bx1): ldr x7, [up] +@@ -77,7 +77,7 @@ + str x13, [rp],#8 + tbnz n, #1, L(b11) + +-L(b01): cbz x18, L(ret) ++L(b01): cbz x17, L(ret) + ldp x4, x5, [up,#8] + ldp x8, x9, [vp,#8] + sub up, up, #8 +@@ -88,7 +88,7 @@ + ldp x10, x11, [vp,#8] + add up, up, #8 + add vp, vp, #8 +- cbz x18, L(end) ++ cbz x17, L(end) + b L(top) + + L(bx0): tbnz n, #1, L(b10) +@@ -101,7 +101,7 @@ + + L(b10): ldp x6, x7, [up] + ldp x10, x11, [vp] +- cbz x18, L(end) ++ cbz x17, L(end) + + ALIGN(16) + L(top): ldp x4, x5, [up,#16] +@@ -114,8 +114,8 @@ + ADDSUBC x12, x4, x8 + ADDSUBC x13, x5, x9 + stp x12, x13, [rp],#16 +- sub x18, x18, #1 +- cbnz x18, L(top) ++ sub x17, x17, #1 ++ cbnz x17, L(top) + + L(end): ADDSUBC x12, x6, x10 + ADDSUBC x13, x7, x11 +diff -r 94c84d919f83 -r 5f32dbc41afc mpn/arm64/aorsmul_1.asm +--- a/mpn/arm64/aorsmul_1.asm Sat Nov 28 23:38:32 2020 +0100 ++++ b/mpn/arm64/aorsmul_1.asm Sun Nov 29 22:31:40 2020 +0100 +@@ -32,10 +32,15 @@ + + include(`../config.m4') + +-C cycles/limb +-C Cortex-A53 9.3-9.8 +-C Cortex-A57 7.0 +-C X-Gene 5.0 ++C addmul_1 submul_1 ++C cycles/limb cycles/limb ++C Cortex-A53 9.3-9.8 9.3-9.8 ++C Cortex-A55 9.0-9.5 9.3-9.8 ++C Cortex-A57 7 7 ++C Cortex-A72 ++C Cortex-A73 6 6 ++C X-Gene 5 5 ++C Apple M1 1.75 1.75 + + C NOTES + C * It is possible to keep the carry chain alive between the addition blocks +diff -r 94c84d919f83 -r 5f32dbc41afc mpn/arm64/aorsorrlshC_n.asm +--- a/mpn/arm64/aorsorrlshC_n.asm Sat Nov 28 23:38:32 2020 +0100 ++++ b/mpn/arm64/aorsorrlshC_n.asm Sun Nov 29 22:31:40 2020 +0100 +@@ -65,14 +65,14 @@ + + ASM_START() + PROLOGUE(func_n) +- lsr x18, n, #2 ++ lsr x6, n, #2 + tbz n, #0, L(bx0) + + L(bx1): ldr x5, [up] + tbnz n, #1, L(b11) + + L(b01): ldr x11, [vp] +- cbz x18, L(1) ++ cbz x6, L(1) + ldp x8, x9, [vp,#8] + lsl x13, x11, #LSH + ADDSUB( x15, x13, x5) +@@ -94,7 +94,7 @@ + ADDSUB( x17, x13, x5) + str x17, [rp],#8 + sub up, up, #8 +- cbz x18, L(end) ++ cbz x6, L(end) + b L(top) + + L(bx0): tbnz n, #1, L(b10) +@@ -107,7 +107,7 @@ + L(b10): CLRRCY( x9) + ldp x10, x11, [vp] + sub up, up, #16 +- cbz x18, L(end) ++ cbz x6, L(end) + + ALIGN(16) + L(top): ldp x4, x5, [up,#16] +@@ -124,8 +124,8 @@ + ADDSUBC(x16, x12, x4) + ADDSUBC(x17, x13, x5) + stp x16, x17, [rp],#16 +- sub x18, x18, #1 +- cbnz x18, L(top) ++ sub x6, x6, #1 ++ cbnz x6, L(top) + + L(end): ldp x4, x5, [up,#16] + extr x12, x10, x9, #RSH +diff -r 94c84d919f83 -r 5f32dbc41afc mpn/arm64/cnd_aors_n.asm +--- a/mpn/arm64/cnd_aors_n.asm Sat Nov 28 23:38:32 2020 +0100 ++++ b/mpn/arm64/cnd_aors_n.asm Sun Nov 29 22:31:40 2020 +0100 +@@ -65,7 +65,7 @@ + + CLRCY + +- lsr x18, n, #2 ++ lsr x17, n, #2 + tbz n, #0, L(bx0) + + L(bx1): ldr x13, [vp] +@@ -75,7 +75,7 @@ + str x9, [rp] + tbnz n, #1, L(b11) + +-L(b01): cbz x18, L(rt) ++L(b01): cbz x17, L(rt) + ldp x12, x13, [vp,#8] + ldp x10, x11, [up,#8] + sub up, up, #8 +@@ -86,7 +86,7 @@ + L(b11): ldp x12, x13, [vp,#8]! + ldp x10, x11, [up,#8]! + sub rp, rp, #8 +- cbz x18, L(end) ++ cbz x17, L(end) + b L(top) + + L(bx0): ldp x12, x13, [vp] +@@ -99,7 +99,7 @@ + b L(mid) + + L(b10): sub rp, rp, #16 +- cbz x18, L(end) ++ cbz x17, L(end) + + ALIGN(16) + L(top): bic x6, x12, cnd +@@ -116,8 +116,8 @@ + ADDSUBC x9, x11, x7 + ldp x10, x11, [up,#32]! + stp x8, x9, [rp,#32]! +- sub x18, x18, #1 +- cbnz x18, L(top) ++ sub x17, x17, #1 ++ cbnz x17, L(top) + + L(end): bic x6, x12, cnd + bic x7, x13, cnd +diff -r 94c84d919f83 -r 5f32dbc41afc mpn/arm64/logops_n.asm +--- a/mpn/arm64/logops_n.asm Sat Nov 28 23:38:32 2020 +0100 ++++ b/mpn/arm64/logops_n.asm Sun Nov 29 22:31:40 2020 +0100 +@@ -78,7 +78,7 @@ + + ASM_START() + PROLOGUE(func) +- lsr x18, n, #2 ++ lsr x17, n, #2 + tbz n, #0, L(bx0) + + L(bx1): ldr x7, [up] +@@ -88,7 +88,7 @@ + str x15, [rp],#8 + tbnz n, #1, L(b11) + +-L(b01): cbz x18, L(ret) ++L(b01): cbz x17, L(ret) + ldp x4, x5, [up,#8] + ldp x8, x9, [vp,#8] + sub up, up, #8 +@@ -99,7 +99,7 @@ + ldp x10, x11, [vp,#8] + add up, up, #8 + add vp, vp, #8 +- cbz x18, L(end) ++ cbz x17, L(end) + b L(top) + + L(bx0): tbnz n, #1, L(b10) +@@ -110,7 +110,7 @@ + + L(b10): ldp x6, x7, [up] + ldp x10, x11, [vp] +- cbz x18, L(end) ++ cbz x17, L(end) + + ALIGN(16) + L(top): ldp x4, x5, [up,#16] +@@ -127,8 +127,8 @@ + POSTOP( x12) + POSTOP( x13) + stp x12, x13, [rp],#16 +- sub x18, x18, #1 +- cbnz x18, L(top) ++ sub x17, x17, #1 ++ cbnz x17, L(top) + + L(end): LOGOP( x12, x6, x10) + LOGOP( x13, x7, x11) +diff -r 94c84d919f83 -r 5f32dbc41afc mpn/arm64/lshift.asm +--- a/mpn/arm64/lshift.asm Sat Nov 28 23:38:32 2020 +0100 ++++ b/mpn/arm64/lshift.asm Sun Nov 29 22:31:40 2020 +0100 +@@ -61,7 +61,7 @@ + add rp, rp_arg, n, lsl #3 + add up, up, n, lsl #3 + sub tnc, xzr, cnt +- lsr x18, n, #2 ++ lsr x17, n, #2 + tbz n, #0, L(bx0) + + L(bx1): ldr x4, [up,#-8] +@@ -69,7 +69,7 @@ + + L(b01): NSHIFT x0, x4, tnc + PSHIFT x2, x4, cnt +- cbnz x18, L(gt1) ++ cbnz x17, L(gt1) + str x2, [rp,#-8] + ret + L(gt1): ldp x4, x5, [up,#-24] +@@ -89,7 +89,7 @@ + PSHIFT x13, x5, cnt + NSHIFT x10, x4, tnc + PSHIFT x2, x4, cnt +- cbnz x18, L(gt2) ++ cbnz x17, L(gt2) + orr x10, x10, x13 + stp x2, x10, [rp,#-16] + ret +@@ -123,11 +123,11 @@ + orr x11, x12, x2 + stp x10, x11, [rp,#-32]! + PSHIFT x2, x4, cnt +-L(lo0): sub x18, x18, #1 ++L(lo0): sub x17, x17, #1 + L(lo3): NSHIFT x10, x6, tnc + PSHIFT x13, x7, cnt + NSHIFT x12, x7, tnc +- cbnz x18, L(top) ++ cbnz x17, L(top) + + L(end): orr x10, x10, x13 + orr x11, x12, x2 +diff -r 94c84d919f83 -r 5f32dbc41afc mpn/arm64/lshiftc.asm +--- a/mpn/arm64/lshiftc.asm Sat Nov 28 23:38:32 2020 +0100 ++++ b/mpn/arm64/lshiftc.asm Sun Nov 29 22:31:40 2020 +0100 +@@ -61,7 +61,7 @@ + add rp, rp_arg, n, lsl #3 + add up, up, n, lsl #3 + sub tnc, xzr, cnt +- lsr x18, n, #2 ++ lsr x17, n, #2 + tbz n, #0, L(bx0) + + L(bx1): ldr x4, [up,#-8] +@@ -69,7 +69,7 @@ + + L(b01): NSHIFT x0, x4, tnc + PSHIFT x2, x4, cnt +- cbnz x18, L(gt1) ++ cbnz x17, L(gt1) + mvn x2, x2 + str x2, [rp,#-8] + ret +@@ -90,7 +90,7 @@ + PSHIFT x13, x5, cnt + NSHIFT x10, x4, tnc + PSHIFT x2, x4, cnt +- cbnz x18, L(gt2) ++ cbnz x17, L(gt2) + eon x10, x10, x13 + mvn x2, x2 + stp x2, x10, [rp,#-16] +@@ -125,11 +125,11 @@ + eon x11, x12, x2 + stp x10, x11, [rp,#-32]! + PSHIFT x2, x4, cnt +-L(lo0): sub x18, x18, #1 ++L(lo0): sub x17, x17, #1 + L(lo3): NSHIFT x10, x6, tnc + PSHIFT x13, x7, cnt + NSHIFT x12, x7, tnc +- cbnz x18, L(top) ++ cbnz x17, L(top) + + L(end): eon x10, x10, x13 + eon x11, x12, x2 +diff -r 94c84d919f83 -r 5f32dbc41afc mpn/arm64/mul_1.asm +--- a/mpn/arm64/mul_1.asm Sat Nov 28 23:38:32 2020 +0100 ++++ b/mpn/arm64/mul_1.asm Sun Nov 29 22:31:40 2020 +0100 +@@ -56,7 +56,7 @@ + + PROLOGUE(mpn_mul_1) + adds x4, xzr, xzr C clear register and cy flag +-L(com): lsr x18, n, #2 ++L(com): lsr x17, n, #2 + tbnz n, #0, L(bx1) + + L(bx0): mov x11, x4 +@@ -65,7 +65,7 @@ + L(b10): ldp x4, x5, [up] + mul x8, x4, v0 + umulh x10, x4, v0 +- cbz x18, L(2) ++ cbz x17, L(2) + ldp x6, x7, [up,#16]! + mul x9, x5, v0 + b L(mid)-8 +@@ -80,7 +80,7 @@ + str x9, [rp],#8 + tbnz n, #1, L(b10) + +-L(b01): cbz x18, L(1) ++L(b01): cbz x17, L(1) + + L(b00): ldp x6, x7, [up] + mul x8, x6, v0 +@@ -90,8 +90,8 @@ + adcs x12, x8, x11 + umulh x11, x7, v0 + add rp, rp, #16 +- sub x18, x18, #1 +- cbz x18, L(end) ++ sub x17, x17, #1 ++ cbz x17, L(end) + + ALIGN(16) + L(top): mul x8, x4, v0 +@@ -110,8 +110,8 @@ + stp x12, x13, [rp],#32 + adcs x12, x8, x11 + umulh x11, x7, v0 +- sub x18, x18, #1 +- cbnz x18, L(top) ++ sub x17, x17, #1 ++ cbnz x17, L(top) + + L(end): mul x8, x4, v0 + adcs x13, x9, x10 +diff -r 94c84d919f83 -r 5f32dbc41afc mpn/arm64/rsh1aors_n.asm +--- a/mpn/arm64/rsh1aors_n.asm Sat Nov 28 23:38:32 2020 +0100 ++++ b/mpn/arm64/rsh1aors_n.asm Sun Nov 29 22:31:40 2020 +0100 +@@ -59,7 +59,7 @@ + + ASM_START() + PROLOGUE(func_n) +- lsr x18, n, #2 ++ lsr x6, n, #2 + + tbz n, #0, L(bx0) + +@@ -69,7 +69,7 @@ + + L(b01): ADDSUB x13, x5, x9 + and x10, x13, #1 +- cbz x18, L(1) ++ cbz x6, L(1) + ldp x4, x5, [up],#48 + ldp x8, x9, [vp],#48 + ADDSUBC x14, x4, x8 +@@ -80,8 +80,8 @@ + ADDSUBC x12, x4, x8 + ADDSUBC x13, x5, x9 + str x17, [rp], #24 +- sub x18, x18, #1 +- cbz x18, L(end) ++ sub x6, x6, #1 ++ cbz x6, L(end) + b L(top) + + L(1): cset x14, COND +@@ -97,7 +97,7 @@ + ldp x8, x9, [vp],#32 + ADDSUBC x12, x4, x8 + ADDSUBC x13, x5, x9 +- cbz x18, L(3) ++ cbz x6, L(3) + ldp x4, x5, [up,#-16] + ldp x8, x9, [vp,#-16] + extr x17, x12, x15, #1 +@@ -117,7 +117,7 @@ + ADDSUB x12, x4, x8 + ADDSUBC x13, x5, x9 + and x10, x12, #1 +- cbz x18, L(2) ++ cbz x6, L(2) + ldp x4, x5, [up,#-16] + ldp x8, x9, [vp,#-16] + ADDSUBC x14, x4, x8 +@@ -134,8 +134,8 @@ + ADDSUBC x12, x4, x8 + ADDSUBC x13, x5, x9 + add rp, rp, #16 +- sub x18, x18, #1 +- cbz x18, L(end) ++ sub x6, x6, #1 ++ cbz x6, L(end) + + ALIGN(16) + L(top): ldp x4, x5, [up,#-16] +@@ -152,8 +152,8 @@ + ADDSUBC x12, x4, x8 + ADDSUBC x13, x5, x9 + stp x16, x17, [rp],#32 +- sub x18, x18, #1 +- cbnz x18, L(top) ++ sub x6, x6, #1 ++ cbnz x6, L(top) + + L(end): extr x16, x15, x14, #1 + extr x17, x12, x15, #1 +diff -r 94c84d919f83 -r 5f32dbc41afc mpn/arm64/rshift.asm +--- a/mpn/arm64/rshift.asm Sat Nov 28 23:38:32 2020 +0100 ++++ b/mpn/arm64/rshift.asm Sun Nov 29 22:31:40 2020 +0100 +@@ -60,7 +60,7 @@ + PROLOGUE(mpn_rshift) + mov rp, rp_arg + sub tnc, xzr, cnt +- lsr x18, n, #2 ++ lsr x17, n, #2 + tbz n, #0, L(bx0) + + L(bx1): ldr x5, [up] +@@ -68,7 +68,7 @@ + + L(b01): NSHIFT x0, x5, tnc + PSHIFT x2, x5, cnt +- cbnz x18, L(gt1) ++ cbnz x17, L(gt1) + str x2, [rp] + ret + L(gt1): ldp x4, x5, [up,#8] +@@ -89,7 +89,7 @@ + PSHIFT x13, x4, cnt + NSHIFT x10, x5, tnc + PSHIFT x2, x5, cnt +- cbnz x18, L(gt2) ++ cbnz x17, L(gt2) + orr x10, x10, x13 + stp x10, x2, [rp] + ret +@@ -121,11 +121,11 @@ + orr x11, x12, x2 + stp x11, x10, [rp,#32]! + PSHIFT x2, x5, cnt +-L(lo0): sub x18, x18, #1 ++L(lo0): sub x17, x17, #1 + L(lo3): NSHIFT x10, x7, tnc + NSHIFT x12, x6, tnc + PSHIFT x13, x6, cnt +- cbnz x18, L(top) ++ cbnz x17, L(top) + + L(end): orr x10, x10, x13 + orr x11, x12, x2 +diff -r 94c84d919f83 -r 5f32dbc41afc mpn/arm64/sqr_diag_addlsh1.asm +--- a/mpn/arm64/sqr_diag_addlsh1.asm Sat Nov 28 23:38:32 2020 +0100 ++++ b/mpn/arm64/sqr_diag_addlsh1.asm Sun Nov 29 22:31:40 2020 +0100 +@@ -47,7 +47,7 @@ + ASM_START() + PROLOGUE(mpn_sqr_diag_addlsh1) + ldr x15, [up],#8 +- lsr x18, n, #1 ++ lsr x14, n, #1 + tbz n, #0, L(bx0) + + L(bx1): adds x7, xzr, xzr +@@ -62,8 +62,8 @@ + ldr x17, [up],#16 + ldp x6, x7, [tp],#32 + umulh x11, x15, x15 +- sub x18, x18, #1 +- cbz x18, L(end) ++ sub x14, x14, #1 ++ cbz x14, L(end) + + ALIGN(16) + L(top): extr x9, x6, x5, #63 +@@ -84,8 +84,8 @@ + extr x8, x5, x4, #63 + stp x12, x13, [rp],#16 + adcs x12, x8, x10 +- sub x18, x18, #1 +- cbnz x18, L(top) ++ sub x14, x14, #1 ++ cbnz x14, L(top) + + L(end): extr x9, x6, x5, #63 + mul x10, x17, x17 diff --git a/deps/patches/llvm-13-D109203-stackprobe-x86.patch b/deps/patches/llvm-13-D109203-stackprobe-x86.patch deleted file mode 100644 index 5fd2e136c57ba..0000000000000 --- a/deps/patches/llvm-13-D109203-stackprobe-x86.patch +++ /dev/null @@ -1,157 +0,0 @@ -From d914f133f8d6770cfd230e1b55aef67c16d19473 Mon Sep 17 00:00:00 2001 -From: Elliot Saba -Date: Tue, 7 Sep 2021 10:10:49 -0400 -Subject: [PATCH] [X86] Don't clobber EBX in stackprobes - -On X86, the stackprobe emission code chooses the `R11D` register, which -is illegal on i686. This ends up wrapping around to `EBX`, which does -not get properly callee-saved within the stack probing prologue, -clobbering the register for the callers. - -We fix this by explicitly using `EAX` as the stack probe register. - -Reviewed By: pengfei - -Differential Revision: https://reviews.llvm.org/D109203 ---- - llvm/lib/Target/X86/X86FrameLowering.cpp | 8 +- - llvm/test/CodeGen/X86/stack-clash-large.ll | 97 +++++++++++++--------- - 2 files changed, 64 insertions(+), 41 deletions(-) - -diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp -index 4cde7971e597..86cb86b19d62 100644 ---- a/llvm/lib/Target/X86/X86FrameLowering.cpp -+++ b/llvm/lib/Target/X86/X86FrameLowering.cpp -@@ -671,7 +671,9 @@ void X86FrameLowering::emitStackProbeInlineGenericLoop( - MF.insert(MBBIter, testMBB); - MF.insert(MBBIter, tailMBB); - -- Register FinalStackProbed = Uses64BitFramePtr ? X86::R11 : X86::R11D; -+ Register FinalStackProbed = Uses64BitFramePtr ? X86::R11 -+ : Is64Bit ? X86::R11D -+ : X86::EAX; - BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::COPY), FinalStackProbed) - .addReg(StackPtr) - .setMIFlag(MachineInstr::FrameSetup); -@@ -1092,7 +1094,9 @@ void X86FrameLowering::BuildStackAlignAND(MachineBasicBlock &MBB, - MF.insert(MBBIter, bodyMBB); - MF.insert(MBBIter, footMBB); - const unsigned MovMIOpc = Is64Bit ? X86::MOV64mi32 : X86::MOV32mi; -- Register FinalStackProbed = Uses64BitFramePtr ? X86::R11 : X86::R11D; -+ Register FinalStackProbed = Uses64BitFramePtr ? X86::R11 -+ : Is64Bit ? X86::R11D -+ : X86::EAX; - - // Setup entry block - { -diff --git a/llvm/test/CodeGen/X86/stack-clash-large.ll b/llvm/test/CodeGen/X86/stack-clash-large.ll -index 9129e4ed40fd..00c7843b54f5 100644 ---- a/llvm/test/CodeGen/X86/stack-clash-large.ll -+++ b/llvm/test/CodeGen/X86/stack-clash-large.ll -@@ -1,45 +1,64 @@ --; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --no_x86_scrub_sp --; RUN: llc -mtriple=x86_64-linux-android < %s | FileCheck -check-prefix=CHECK-X86-64 %s --; RUN: llc -mtriple=i686-linux-android < %s | FileCheck -check-prefix=CHECK-X86-32 %s -+; RUN: llc -mtriple=x86_64-linux-android < %s | FileCheck -check-prefix=CHECK-X64 %s -+; RUN: llc -mtriple=i686-linux-android < %s | FileCheck -check-prefix=CHECK-X86 %s -+; RUN: llc -mtriple=x86_64-linux-gnux32 < %s | FileCheck -check-prefix=CHECK-X32 %s - - define i32 @foo() local_unnamed_addr #0 { --; CHECK-X86-64-LABEL: foo: --; CHECK-X86-64: # %bb.0: --; CHECK-X86-64-NEXT: movq %rsp, %r11 --; CHECK-X86-64-NEXT: subq $69632, %r11 # imm = 0x11000 --; CHECK-X86-64-NEXT: .LBB0_1: # =>This Inner Loop Header: Depth=1 --; CHECK-X86-64-NEXT: subq $4096, %rsp # imm = 0x1000 --; CHECK-X86-64-NEXT: movq $0, (%rsp) --; CHECK-X86-64-NEXT: cmpq %r11, %rsp --; CHECK-X86-64-NEXT: jne .LBB0_1 --; CHECK-X86-64-NEXT: # %bb.2: --; CHECK-X86-64-NEXT: subq $2248, %rsp # imm = 0x8C8 --; CHECK-X86-64-NEXT: .cfi_def_cfa_offset 71888 --; CHECK-X86-64-NEXT: movl $1, 264(%rsp) --; CHECK-X86-64-NEXT: movl $1, 28664(%rsp) --; CHECK-X86-64-NEXT: movl -128(%rsp), %eax --; CHECK-X86-64-NEXT: addq $71880, %rsp # imm = 0x118C8 --; CHECK-X86-64-NEXT: .cfi_def_cfa_offset 8 --; CHECK-X86-64-NEXT: retq -+; CHECK-X64-LABEL: foo: -+; CHECK-X64: # %bb.0: -+; CHECK-X64-NEXT: movq %rsp, %r11 -+; CHECK-X64-NEXT: subq $69632, %r11 # imm = 0x11000 -+; CHECK-X64-NEXT: .LBB0_1: # =>This Inner Loop Header: Depth=1 -+; CHECK-X64-NEXT: subq $4096, %rsp # imm = 0x1000 -+; CHECK-X64-NEXT: movq $0, (%rsp) -+; CHECK-X64-NEXT: cmpq %r11, %rsp -+; CHECK-X64-NEXT: jne .LBB0_1 -+; CHECK-X64-NEXT: # %bb.2: -+; CHECK-X64-NEXT: subq $2248, %rsp # imm = 0x8C8 -+; CHECK-X64-NEXT: .cfi_def_cfa_offset 71888 -+; CHECK-X64-NEXT: movl $1, 264(%rsp) -+; CHECK-X64-NEXT: movl $1, 28664(%rsp) -+; CHECK-X64-NEXT: movl -128(%rsp), %eax -+; CHECK-X64-NEXT: addq $71880, %rsp # imm = 0x118C8 -+; CHECK-X64-NEXT: .cfi_def_cfa_offset 8 -+; CHECK-X64-NEXT: retq - ; --; CHECK-X86-32-LABEL: foo: --; CHECK-X86-32: # %bb.0: --; CHECK-X86-32-NEXT: movl %esp, %r11d --; CHECK-X86-32-NEXT: subl $69632, %r11d # imm = 0x11000 --; CHECK-X86-32-NEXT: .LBB0_1: # =>This Inner Loop Header: Depth=1 --; CHECK-X86-32-NEXT: subl $4096, %esp # imm = 0x1000 --; CHECK-X86-32-NEXT: movl $0, (%esp) --; CHECK-X86-32-NEXT: cmpl %r11d, %esp --; CHECK-X86-32-NEXT: jne .LBB0_1 --; CHECK-X86-32-NEXT: # %bb.2: --; CHECK-X86-32-NEXT: subl $2380, %esp # imm = 0x94C --; CHECK-X86-32-NEXT: .cfi_def_cfa_offset 72016 --; CHECK-X86-32-NEXT: movl $1, 392(%esp) --; CHECK-X86-32-NEXT: movl $1, 28792(%esp) --; CHECK-X86-32-NEXT: movl (%esp), %eax --; CHECK-X86-32-NEXT: addl $72012, %esp # imm = 0x1194C --; CHECK-X86-32-NEXT: .cfi_def_cfa_offset 4 --; CHECK-X86-32-NEXT: retl -+; CHECK-X86-LABEL: foo: -+; CHECK-X86: # %bb.0: -+; CHECK-X86-NEXT: movl %esp, %eax -+; CHECK-X86-NEXT: subl $69632, %eax # imm = 0x11000 -+; CHECK-X86-NEXT: .LBB0_1: # =>This Inner Loop Header: Depth=1 -+; CHECK-X86-NEXT: subl $4096, %esp # imm = 0x1000 -+; CHECK-X86-NEXT: movl $0, (%esp) -+; CHECK-X86-NEXT: cmpl %eax, %esp -+; CHECK-X86-NEXT: jne .LBB0_1 -+; CHECK-X86-NEXT: # %bb.2: -+; CHECK-X86-NEXT: subl $2380, %esp # imm = 0x94C -+; CHECK-X86-NEXT: .cfi_def_cfa_offset 72016 -+; CHECK-X86-NEXT: movl $1, 392(%esp) -+; CHECK-X86-NEXT: movl $1, 28792(%esp) -+; CHECK-X86-NEXT: movl (%esp), %eax -+; CHECK-X86-NEXT: addl $72012, %esp # imm = 0x1194C -+; CHECK-X86-NEXT: .cfi_def_cfa_offset 4 -+; CHECK-X86-NEXT: retl -+; -+; CHECK-X32-LABEL: foo: -+; CHECK-X32: # %bb.0: -+; CHECK-X32-NEXT: movl %esp, %r11d -+; CHECK-X32-NEXT: subl $69632, %r11d # imm = 0x11000 -+; CHECK-X32-NEXT: .LBB0_1: # =>This Inner Loop Header: Depth=1 -+; CHECK-X32-NEXT: subl $4096, %esp # imm = 0x1000 -+; CHECK-X32-NEXT: movq $0, (%esp) -+; CHECK-X32-NEXT: cmpl %r11d, %esp -+; CHECK-X32-NEXT: jne .LBB0_1 -+; CHECK-X32-NEXT: # %bb.2: -+; CHECK-X32-NEXT: subl $2248, %esp # imm = 0x8C8 -+; CHECK-X32-NEXT: .cfi_def_cfa_offset 71888 -+; CHECK-X32-NEXT: movl $1, 264(%esp) -+; CHECK-X32-NEXT: movl $1, 28664(%esp) -+; CHECK-X32-NEXT: movl -128(%esp), %eax -+; CHECK-X32-NEXT: addl $71880, %esp # imm = 0x118C8 -+; CHECK-X32-NEXT: .cfi_def_cfa_offset 8 -+; CHECK-X32-NEXT: retq - %a = alloca i32, i64 18000, align 16 - %b0 = getelementptr inbounds i32, i32* %a, i64 98 - %b1 = getelementptr inbounds i32, i32* %a, i64 7198 --- -2.33.0 - diff --git a/doc/src/devdocs/init.md b/doc/src/devdocs/init.md index 24d0874c196a4..3c43bc5dd8e53 100644 --- a/doc/src/devdocs/init.md +++ b/doc/src/devdocs/init.md @@ -14,7 +14,7 @@ and [Legacy `ios.c` library](@ref)). Next [`jl_parse_opts()`](https://github.com/JuliaLang/julia/blob/master/src/jloptions.c) is called to process command line options. Note that `jl_parse_opts()` only deals with options that affect code generation -or early initialization. Other options are handled later by [`process_options()` in `base/client.jl`](https://github.com/JuliaLang/julia/blob/master/base/client.jl). +or early initialization. Other options are handled later by [`exec_options()` in `base/client.jl`](https://github.com/JuliaLang/julia/blob/master/base/client.jl). `jl_parse_opts()` stores command line options in the [global `jl_options` struct](https://github.com/JuliaLang/julia/blob/master/src/julia.h). @@ -156,21 +156,18 @@ executes it. ## `Base._start` -[`Base._start`](https://github.com/JuliaLang/julia/blob/master/base/client.jl) calls [`Base.process_options`](https://github.com/JuliaLang/julia/blob/master/base/client.jl) +[`Base._start`](https://github.com/JuliaLang/julia/blob/master/base/client.jl) calls [`Base.exec_options`](https://github.com/JuliaLang/julia/blob/master/base/client.jl) which calls [`jl_parse_input_line("println("Hello World!")")`](https://github.com/JuliaLang/julia/blob/master/src/ast.c) -to create an expression object and [`Base.eval()`](@ref eval) to execute it. +to create an expression object and [`Core.eval(Main, ex)`](@ref Core.eval) to execute the parsed expression `ex` in the module context of `Main`. -## `Base.eval` +## `Core.eval` -[`Base.eval()`](@ref eval) was [mapped to `jl_f_top_eval`](https://github.com/JuliaLang/julia/blob/master/src/builtins.c) -by `jl_init_primitives()`. - -[`jl_f_top_eval()`](https://github.com/JuliaLang/julia/blob/master/src/builtins.c) calls [`jl_toplevel_eval_in(jl_main_module, ex)`](https://github.com/JuliaLang/julia/blob/master/src/builtins.c), -where `ex` is the parsed expression `println("Hello World!")`. - -[`jl_toplevel_eval_in()`](https://github.com/JuliaLang/julia/blob/master/src/builtins.c) calls -[`jl_toplevel_eval_flex()`](https://github.com/JuliaLang/julia/blob/master/src/toplevel.c) which -calls [`eval()` in `interpreter.c`](https://github.com/JuliaLang/julia/blob/master/src/interpreter.c). +[`Core.eval(Main, ex)`](@ref Core.eval) calls [`jl_toplevel_eval_in(m, ex)`](https://github.com/JuliaLang/julia/blob/master/src/toplevel.c), +which calls [`jl_toplevel_eval_flex`](https://github.com/JuliaLang/julia/blob/master/src/toplevel.c). +`jl_toplevel_eval_flex` implements a simple heuristic to decide whether to compile a given code thunk or run it by interpreter. +When given `println("Hello World!")`, it would usually decide to run the code by interpreter, in which case it calls +[`jl_interpret_toplevel_thunk`](https://github.com/JuliaLang/julia/blob/master/src/interpreter.c), which then calls +[`eval_body`](https://github.com/JuliaLang/julia/blob/master/src/interpreter.c). The stack dump below shows how the interpreter works its way through various methods of [`Base.println()`](@ref) and [`Base.print()`](@ref) before arriving at [`write(s::IO, a::Array{T}) where T`](https://github.com/JuliaLang/julia/blob/master/base/stream.jl) @@ -209,12 +206,11 @@ Hello World! | `jl_apply_generic()` | `gf.c` | `Base.println(String,)` | | `jl_apply()` | `julia.h` |   | | `do_call()` | `interpreter.c` |   | -| `eval()` | `interpreter.c` |   | -| `jl_interpret_toplevel_expr()` | `interpreter.c` |   | -| `jl_toplevel_eval_flex()` | `toplevel.c` |   | -| `jl_toplevel_eval()` | `toplevel.c` |   | -| `jl_toplevel_eval_in()` | `builtins.c` |   | -| `jl_f_top_eval()` | `builtins.c` |   | +| `eval_body()` | `interpreter.c` |   | +| `jl_interpret_toplevel_thunk` | `interpreter.c` |   | +| `jl_toplevel_eval_flex` | `toplevel.c` |   | +| `jl_toplevel_eval_in` | `toplevel.c` |   | +| `Core.eval` | `boot.jl` |   | Since our example has just one function call, which has done its job of printing "Hello World!", the stack now rapidly unwinds back to `main()`. diff --git a/doc/src/devdocs/ssair.md b/doc/src/devdocs/ssair.md index d0ad27b833301..46d33c177f469 100644 --- a/doc/src/devdocs/ssair.md +++ b/doc/src/devdocs/ssair.md @@ -3,12 +3,12 @@ ## Background Beginning in Julia 0.7, parts of the compiler use a new [SSA-form](https://en.wikipedia.org/wiki/Static_single_assignment_form) -intermediate representation. Historically, the compiler used to directly generate LLVM IR, from a lowered form of the Julia +intermediate representation. Historically, the compiler would directly generate LLVM IR from a lowered form of the Julia AST. This form had most syntactic abstractions removed, but still looked a lot like an abstract syntax tree. Over time, in order to facilitate optimizations, SSA values were introduced to this IR and the IR was -linearized (i.e. a form where function arguments may only be SSA values or constants). However, non-SSA values +linearized (i.e. turned into a form where function arguments could only be SSA values or constants). However, non-SSA values (slots) remained in the IR due to the lack of Phi nodes in the IR (necessary for back-edges and re-merging of -conditional control flow), negating much of the usefulness of the SSA form representation to perform +conditional control flow). This negated much of the usefulness of SSA form representation when performing middle end optimizations. Some heroic effort was put into making these optimizations work without a complete SSA form representation, but the lack of such a representation ultimately proved prohibitive. @@ -74,7 +74,7 @@ that is generally done for most optimizations that care about these conditions a Exception handling complicates the SSA story moderately, because exception handling introduces additional control flow edges into the IR across which values must be tracked. -One approach to do so, which is followed by LLVM is to make calls which may throw exceptions +One approach to do so, which is followed by LLVM, is to make calls which may throw exceptions into basic block terminators and add an explicit control flow edge to the catch handler: ``` @@ -87,16 +87,16 @@ catch: # Exceptions go here ``` -However, this is problematic in a language like julia where at the start of the optimization +However, this is problematic in a language like Julia, where at the start of the optimization pipeline, we do not know which calls throw. We would have to conservatively assume that every -call (which in julia is every statement) throws. This would have several negative effects. +call (which in Julia is every statement) throws. This would have several negative effects. On the one hand, it would essentially reduce the scope of every basic block to a single call, defeating the purpose of having operations be performed at the basic block level. On the other hand, every catch basic block would have `n*m` phi node arguments (`n`, the number of statements -in the critical region, `m` the number of live values through the catch block). To work around -this, we use a combination of `Upsilon` and `PhiC` (the C standing for `catch`, -written `φᶜ` in the IR pretty printer, because -unicode subscript c is not available) nodes. There are several ways to think of these nodes, but +in the critical region, `m` the number of live values through the catch block). + +To work around this, we use a combination of `Upsilon` and `PhiC` nodes (the C standing for `catch`, +written `φᶜ` in the IR pretty printer, because unicode subscript c is not available). There are several ways to think of these nodes, but perhaps the easiest is to think of each `PhiC` as a load from a unique store-many, read-once slot, with `Upsilon` being the corresponding store operation. The `PhiC` has an operand list of all the upsilon nodes that store to its implicit slot. The `Upsilon` nodes however, do not record which `PhiC` diff --git a/doc/src/devdocs/subarrays.md b/doc/src/devdocs/subarrays.md index 02e75fa00ec48..cec7a64a65245 100644 --- a/doc/src/devdocs/subarrays.md +++ b/doc/src/devdocs/subarrays.md @@ -19,14 +19,14 @@ julia> A = rand(2,3,4); julia> S1 = view(A, :, 1, 2:3) 2×2 view(::Array{Float64, 3}, :, 1, 2:3) with eltype Float64: - 0.342284 0.831961 - 0.237287 0.435938 + 0.839622 0.711389 + 0.967143 0.103929 julia> S2 = view(A, 1, :, 2:3) 3×2 view(::Array{Float64, 3}, 1, :, 2:3) with eltype Float64: - 0.342284 0.831961 - 0.988944 0.927795 - 0.178426 0.404876 + 0.839622 0.711389 + 0.789764 0.806704 + 0.566704 0.962715 ``` ```@meta DocTestSetup = nothing diff --git a/doc/src/manual/getting-started.md b/doc/src/manual/getting-started.md index f7bdfd2a64afc..75f630091b6c1 100644 --- a/doc/src/manual/getting-started.md +++ b/doc/src/manual/getting-started.md @@ -102,7 +102,7 @@ command-line-options). ## Resources -A curated list of useful learning resources to help new users get started can be found on the [learning](https://julialang.org/learning/) page of the main Julia web site. +A curated list of useful learning resources to help new users get started can be found on the [learning](https://julialang.org/learning/) page of the main Julia website. You can use the REPL as a learning resource by switching into the help mode. Switch to help mode by pressing `?` at an empty `julia> ` prompt, before typing diff --git a/doc/src/manual/modules.md b/doc/src/manual/modules.md index 7d3304810a428..1135991743bb2 100644 --- a/doc/src/manual/modules.md +++ b/doc/src/manual/modules.md @@ -303,7 +303,7 @@ include(p) = Base.include(Mod, p) end ``` -If even `Core` is not wanted, a module that imports nothing at all can be defined with `Module(:YourNameHere, false, false)` and code can be evaluated into it with [`@eval`](@ref) or [`Core.eval`](@ref). +If even `Core` is not wanted, a module that imports nothing and defines no names at all can be defined with `Module(:YourNameHere, false, false)` and code can be evaluated into it with [`@eval`](@ref) or [`Core.eval`](@ref). ### Standard modules diff --git a/doc/src/manual/noteworthy-differences.md b/doc/src/manual/noteworthy-differences.md index 69e236db6c7d1..9825c34cc62b9 100644 --- a/doc/src/manual/noteworthy-differences.md +++ b/doc/src/manual/noteworthy-differences.md @@ -210,10 +210,13 @@ For users coming to Julia from R, these are some noteworthy differences: * In Julia, indexing of arrays, strings, etc. is 1-based not 0-based. * Julia's slice indexing includes the last element, unlike in Python. `a[2:3]` in Julia is `a[1:3]` in Python. - * Julia does not support negative indices. In particular, the last element of a list or array is - indexed with `end` in Julia, not `-1` as in Python. + * Unlike Python, Julia allows [AbstractArrays with arbitrary indexes](https://julialang.org/blog/2017/04/offset-arrays/). + Python's special interpretation of negative indexing, `a[-1]` and `a[-2]`, should be written + `a[end]` and `a[end-1]` in Julia. * Julia requires `end` for indexing until the last element. `x[1:]` in Python is equivalent to `x[2:end]` in Julia. * Julia's range indexing has the format of `x[start:step:stop]`, whereas Python's format is `x[start:(stop+1):step]`. Hence, `x[0:10:2]` in Python is equivalent to `x[1:2:10]` in Julia. Similarly, `x[::-1]` in Python, which refers to the reversed array, is equivalent to `x[end:-1:1]` in Julia. + * In Julia, ranges can be constructed independently as `start:step:stop`, the same syntax it uses + in array-indexing. The `range` function is also supported. * In Julia, indexing a matrix with arrays like `X[[1,2], [1,3]]` refers to a sub-matrix that contains the intersections of the first and second rows with the first and third columns. In Python, `X[[1,2], [1,3]]` refers to a vector that contains the values of cell `[1,1]` and `[2,3]` in the matrix. `X[[1,2], [1,3]]` in Julia is equivalent with `X[np.ix_([0,1],[0,2])]` in Python. `X[[0,1], [0,2]]` in Python is equivalent with `X[[CartesianIndex(1,1), CartesianIndex(2,3)]]` in Julia. * Julia has no line continuation syntax: if, at the end of a line, the input so far is a complete expression, it is considered done; otherwise the input continues. One way to force an expression @@ -249,10 +252,12 @@ For users coming to Julia from R, these are some noteworthy differences: * The logical Julia program structure (Packages and Modules) is independent of the file structure (`include` for additional files), whereas the Python code structure is defined by directories (Packages) and files (Modules). * The ternary operator `x > 0 ? 1 : -1` in Julia corresponds to a conditional expression in Python `1 if x > 0 else -1`. * In Julia the `@` symbol refers to a macro, whereas in Python it refers to a decorator. - * Exception handling in Julia is done using `try` — `catch` — `finally`, instead of `try` — `except` — `finally`. In contrast to Python, it is not recommended to use exception handling as part of the normal workflow in Julia due to performance reasons. + * Exception handling in Julia is done using `try` — `catch` — `finally`, instead of `try` — `except` — `finally`. In contrast to Python, it is not recommended to use exception handling as part of the normal workflow in Julia (compared with Python, Julia is faster at ordinary control flow but slower at exception-catching). * In Julia loops are fast, there is no need to write "vectorized" code for performance reasons. * Be careful with non-constant global variables in Julia, especially in tight loops. Since you can write close-to-metal code in Julia (unlike Python), the effect of globals can be drastic (see [Performance Tips](@ref man-performance-tips)). - * In Python, the majority of values can be used in logical contexts (e.g. `if "a":` means the following block is executed, and `if "":` means it is not). In Julia, you need explicit conversion to `Bool` (e.g. `if "a"` throws an exception). If you want to test for a non-empty string in Julia, you would explicitly write `if !isempty("")`. + * In Julia, rounding and truncation are explicit. Python's `int(3.7)` should be `floor(Int, 3.7)` or `Int(floor(3.7))` and is distinguished from `round(Int, 3.7)`. `floor(x)` and `round(x)` on their own return an integer value of the same type as `x` rather than always returning `Int`. + * In Julia, parsing is explicit. Python's `float("3.7")` would be `parse(Float64, "3.7")` in Julia. + * In Python, the majority of values can be used in logical contexts (e.g. `if "a":` means the following block is executed, and `if "":` means it is not). In Julia, you need explicit conversion to `Bool` (e.g. `if "a"` throws an exception). If you want to test for a non-empty string in Julia, you would explicitly write `if !isempty("")`. Perhaps surprisingly, in Python `if "False"` and `bool("False")` both evaluate to `True` (because `"False"` is a non-empty string); in Julia, `parse(Bool, "false")` returns `false`. * In Julia, a new local scope is introduced by most code blocks, including loops and `try` — `catch` — `finally`. Note that comprehensions (list, generator, etc.) introduce a new local scope both in Python and Julia, whereas `if` blocks do not introduce a new local scope in both languages. ## Noteworthy differences from C/C++ diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index 542166bb248cf..13990769259da 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -77,12 +77,12 @@ julia> function sum_global() end; julia> @time sum_global() - 0.010414 seconds (9.07 k allocations: 373.448 KiB, 98.40% compilation time) -493.6199223951192 + 0.011539 seconds (9.08 k allocations: 373.386 KiB, 98.69% compilation time) +523.0007221951678 julia> @time sum_global() - 0.000108 seconds (3.49 k allocations: 70.156 KiB) -493.6199223951192 + 0.000091 seconds (3.49 k allocations: 70.156 KiB) +523.0007221951678 ``` On the first call (`@time sum_global()`) the function gets compiled. (If you've not yet used [`@time`](@ref) @@ -113,12 +113,12 @@ julia> function sum_arg(x) end; julia> @time sum_arg(x) - 0.007971 seconds (3.96 k allocations: 200.171 KiB, 99.83% compilation time) -493.6199223951192 + 0.007551 seconds (3.98 k allocations: 200.548 KiB, 99.77% compilation time) +523.0007221951678 julia> @time sum_arg(x) - 0.000003 seconds (1 allocation: 16 bytes) -493.6199223951192 + 0.000006 seconds (1 allocation: 16 bytes) +523.0007221951678 ``` The 1 allocation seen is from running the `@time` macro itself in global scope. If we instead run @@ -128,8 +128,8 @@ the timing in a function, we can see that indeed no allocations are performed: julia> time_sum(x) = @time sum_arg(x); julia> time_sum(x) - 0.000001 seconds -493.6199223951192 + 0.000002 seconds +523.0007221951678 ``` In some situations, your function may need to allocate memory as part of its operation, and this diff --git a/src/.gitignore b/src/.gitignore index 3b845e647b02c..388e971d4f12d 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -10,6 +10,7 @@ /julia_flisp.boot /julia_flisp.boot.inc /flisp.boot.inc +/jl_internal_funcs.inc /libjulia-debug.a /libjulia-debug.so diff --git a/src/Makefile b/src/Makefile index ed0cbaf9c8c9a..7833b6d205bd4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -17,7 +17,7 @@ JLDFLAGS += $(LDFLAGS) FLAGS := \ -D_GNU_SOURCE -I$(BUILDDIR) -I$(SRCDIR) \ -I$(SRCDIR)/flisp -I$(SRCDIR)/support \ - -I$(LIBUV_INC) -I$(build_includedir) -DLIBRARY_EXPORTS \ + -I$(LIBUV_INC) -I$(build_includedir) \ -I$(JULIAHOME)/deps/valgrind ifneq ($(USEMSVC), 1) FLAGS += -Wall -Wno-strict-aliasing -fno-omit-frame-pointer -fvisibility=hidden -fno-common \ @@ -95,12 +95,13 @@ endif # 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) $(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 locks.h atomics.h julia_gcext.h) ifeq ($(USE_SYSTEM_LIBUV),0) UV_HEADERS += uv.h UV_HEADERS += uv/*.h endif +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) +HEADERS := $(PUBLIC_HEADERS) $(addprefix $(SRCDIR)/,julia_internal.h options.h timing.h) $(addprefix $(BUILDDIR)/,$(DTRACE_HEADERS) jl_internal_funcs.inc) +PUBLIC_HEADERS += $(addprefix $(SRCDIR)/,julia_gcext.h) PUBLIC_HEADER_TARGETS := $(addprefix $(build_includedir)/julia/,$(notdir $(PUBLIC_HEADERS)) $(UV_HEADERS)) LLVM_LDFLAGS := $(shell $(LLVM_CONFIG_HOST) --ldflags) @@ -135,8 +136,8 @@ RELEASE_LIBS := $(COMMON_LIBPATHS) $(WHOLE_ARCHIVE) $(BUILDDIR)/flisp/libflisp.a OBJS := $(SRCS:%=$(BUILDDIR)/%.o) DOBJS := $(SRCS:%=$(BUILDDIR)/%.dbg.obj) -DEBUGFLAGS += $(FLAGS) -SHIPFLAGS += $(FLAGS) +DEBUGFLAGS += $(FLAGS) -DLIBRARY_EXPORTS +SHIPFLAGS += $(FLAGS) -DLIBRARY_EXPORTS # if not absolute, then relative to the directory of the julia executable SHIPFLAGS += "-DJL_SYSTEM_IMAGE_PATH=\"$(build_private_libdir_rel)/sys.$(SHLIB_EXT)\"" @@ -178,6 +179,13 @@ $(BUILDDIR)/%.h.gen : $(SRCDIR)/%.d sed 's/JULIA_/JL_PROBE_/' $@ > $@.tmp mv $@.tmp $@ +$(BUILDDIR)/jl_internal_funcs.inc: $(SRCDIR)/jl_exported_funcs.inc + # Generate `.inc` file that contains a list of `#define` macros to rename functions defined in `libjulia-internal` + # to have a `ijl_` prefix instead of `jl_`, to denote that they are coming from `libjulia-internal`. This avoids + # potential confusion with debugging tools, when inspecting a process that has both `libjulia` and `libjulia-internal` + # loaded at the same time. + grep 'XX(.\+)' $< | sed -E 's/.*XX\((.+)\).*/#define \1 i\1/g' >$@ + # source file rules $(BUILDDIR)/%.o: $(SRCDIR)/%.c $(HEADERS) | $(BUILDDIR) @$(call PRINT_CC, $(CC) $(JCPPFLAGS) $(JCFLAGS) $(SHIPFLAGS) $(DISABLE_ASSERTIONS) -c $< -o $@) @@ -213,7 +221,7 @@ else JULIA_SPLITDEBUG := 0 endif $(build_shlibdir)/libccalltest.$(SHLIB_EXT): $(SRCDIR)/ccalltest.c - @$(call PRINT_CC, $(CC) $(JCFLAGS) $(JCPPFLAGS) $(DEBUGFLAGS) -O3 $< $(fPIC) -shared -o $@.tmp $(JLDFLAGS)) + @$(call PRINT_CC, $(CC) $(JCFLAGS) $(JCPPFLAGS) $(FLAGS) -O3 $< $(fPIC) -shared -o $@.tmp $(LDFLAGS)) $(INSTALL_NAME_CMD)libccalltest.$(SHLIB_EXT) $@.tmp ifeq ($(JULIA_SPLITDEBUG),1) @# Create split debug info file for libccalltest stacktraces test @@ -230,7 +238,7 @@ endif $(INSTALL_NAME_CMD)libccalltest.$(SHLIB_EXT) $@ $(build_shlibdir)/libllvmcalltest.$(SHLIB_EXT): $(SRCDIR)/llvmcalltest.cpp $(LLVM_CONFIG_ABSOLUTE) - @$(call PRINT_CC, $(CXX) $(LLVM_CXXFLAGS) $(JCXXFLAGS) $(JCPPFLAGS) $(DEBUGFLAGS) -O3 $< $(fPIC) -shared -o $@ $(JLDFLAGS) -L$(build_shlibdir) -L$(build_libdir) $(NO_WHOLE_ARCHIVE) $(LLVMLINK)) -lpthread + @$(call PRINT_CC, $(CXX) $(LLVM_CXXFLAGS) $(FLAGS) $(CPPFLAGS) $(CXXFLAGS) -O3 $< $(fPIC) -shared -o $@ $(LDFLAGS) $(COMMON_LIBPATHS) $(NO_WHOLE_ARCHIVE) $(LLVMLINK)) -lpthread julia_flisp.boot.inc.phony: $(BUILDDIR)/julia_flisp.boot.inc @@ -265,6 +273,7 @@ $(BUILDDIR)/llvm-alloc-opt.o $(BUILDDIR)/llvm-alloc-opt.dbg.obj: $(SRCDIR)/codeg $(BUILDDIR)/llvm-final-gc-lowering.o $(BUILDDIR)/llvm-final-gc-lowering.dbg.obj: $(SRCDIR)/llvm-pass-helpers.h $(BUILDDIR)/llvm-gc-invariant-verifier.o $(BUILDDIR)/llvm-gc-invariant-verifier.dbg.obj: $(SRCDIR)/codegen_shared.h $(BUILDDIR)/llvm-late-gc-lowering.o $(BUILDDIR)/llvm-late-gc-lowering.dbg.obj: $(SRCDIR)/llvm-pass-helpers.h +$(BUILDDIR)/llvm-lower-handlers.o $(BUILDDIR)/llvm-lower-handlers.dbg.obj: $(SRCDIR)/codegen_shared.h $(BUILDDIR)/llvm-multiversioning.o $(BUILDDIR)/llvm-multiversioning.dbg.obj: $(SRCDIR)/codegen_shared.h $(BUILDDIR)/llvm-pass-helpers.o $(BUILDDIR)/llvm-pass-helpers.dbg.obj: $(SRCDIR)/llvm-pass-helpers.h $(SRCDIR)/codegen_shared.h $(BUILDDIR)/llvm-ptls.o $(BUILDDIR)/llvm-ptls.dbg.obj: $(SRCDIR)/codegen_shared.h @@ -355,7 +364,7 @@ clean-support: cleanall: clean clean-flisp clean-support clean-analyzegc -$(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT): $(SRCDIR)/clangsa/GCChecker.cpp $(LLVM_CONFIG_ABSOLUTE) +$(build_shlibdir)/lib%Plugin.$(SHLIB_EXT): $(SRCDIR)/clangsa/%.cpp $(LLVM_CONFIG_ABSOLUTE) @$(call PRINT_CC, $(CXX) -g $(fPIC) -shared -o $@ -DCLANG_PLUGIN -I$(build_includedir) -L$(build_libdir) \ $(LLVM_CXXFLAGS) $(CLANG_LDFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) $(CXXLDFLAGS) $<) @@ -367,24 +376,54 @@ $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT): $(SRCDIR)/clangsa/GCChecker.c install-analysis-deps: $(MAKE) -C $(JULIAHOME)/deps install-llvm install-clang install-llvm-tools install-libuv install-utf8proc install-unwind -analyzegc-deps-check: $(BUILDDIR)/julia_version.h $(BUILDDIR)/julia_flisp.boot.inc +analyzegc-deps-check: $(BUILDDIR)/julia_version.h $(BUILDDIR)/julia_flisp.boot.inc $(BUILDDIR)/jl_internal_funcs.inc ifeq ($(USE_BINARYBUILDER_LLVM),0) ifneq ($(BUILD_LLVM_CLANG),1) $(error Clang must be available to use the clang analyzer. Either build it (BUILD_LLVM_CLANG=1) or use BinaryBuilder) endif endif -clangsa: $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) - -clang-sa-%: $(SRCDIR)/%.c $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) | analyzegc-deps-check - @$(call PRINT_ANALYZE, $(build_bindir)/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_bindir)/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++ $<) - -# Add C files as a target of `analyzegc` -analyzegc: $(addprefix clang-sa-,$(RUNTIME_SRCS)) +clangsa: $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) $(build_shlibdir)/libImplicitAtomicsPlugin.$(SHLIB_EXT) + +clang-sagc-%: $(SRCDIR)/%.c $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) .FORCE | analyzegc-deps-check + @$(call PRINT_ANALYZE, $(build_bindir)/clang -D__clang_gcanalyzer__ --analyze -Xanalyzer -analyzer-werror -Xanalyzer -analyzer-output=text --analyzer-no-default-checks \ + -Xclang -load -Xclang $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) -Xclang -analyzer-checker=core$(COMMA)julia.GCChecker \ + $(CLANGSA_FLAGS) $(JCPPFLAGS) $(JCFLAGS) $(DEBUGFLAGS) -fcolor-diagnostics -Werror -x c $<) +clang-sagc-%: $(SRCDIR)/%.cpp $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) .FORCE | analyzegc-deps-check + @$(call PRINT_ANALYZE, $(build_bindir)/clang -D__clang_gcanalyzer__ --analyze -Xanalyzer -analyzer-werror -Xanalyzer -analyzer-output=text --analyzer-no-default-checks \ + -Xclang -load -Xclang $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) -Xclang -analyzer-checker=core$(COMMA)julia.GCChecker \ + $(CLANGSA_FLAGS) $(CLANGSA_CXXFLAGS) $(LLVM_CXXFLAGS) $(JCPPFLAGS) $(JCXXFLAGS) $(DEBUGFLAGS) -fcolor-diagnostics -Werror -x c++ $<) + + # optarg is a required_argument for these +SA_EXCEPTIONS-jloptions.c := -Xanalyzer -analyzer-disable-checker=core.NonNullParamChecker,unix.cstring.NullArg + # clang doesn't understand that e->vars has the same value in save_env (NULL) and restore_env (assumed non-NULL) +SA_EXCEPTIONS-subtype.c := -Xanalyzer -analyzer-disable-checker=core.uninitialized.Assign,core.UndefinedBinaryOperatorResult + # these need to be annotated (and possibly fixed) +SKIP_IMPLICIT_ATOMICS := dump.c gf.c jitlayers.cpp module.c precompile.c rtutils.c staticdata.c toplevel.c codegen.cpp + +clang-sa-%: $(SRCDIR)/%.c $(build_shlibdir)/libImplicitAtomicsPlugin.$(SHLIB_EXT) .FORCE | analyzegc-deps-check + @$(call PRINT_ANALYZE, $(build_bindir)/clang --analyze -Xanalyzer -analyzer-werror -Xanalyzer -analyzer-output=text \ + $(if $(findstring $(notdir $<),$(SKIP_IMPLICIT_ATOMICS)),,-Xclang -load -Xclang $(build_shlibdir)/libImplicitAtomicsPlugin.$(SHLIB_EXT) -Xclang -analyzer-checker=julia.ImplicitAtomics) \ + -Xanalyzer -analyzer-disable-checker=deadcode.DeadStores \ + --analyzer-no-default-checks \ + $(SA_EXCEPTIONS-$(notdir $<)) \ + $(CLANGSA_FLAGS) $(JCPPFLAGS) $(JCFLAGS) $(DEBUGFLAGS) -fcolor-diagnostics -Werror -x c $<) +clang-sa-%: $(SRCDIR)/%.cpp $(build_shlibdir)/libImplicitAtomicsPlugin.$(SHLIB_EXT) .FORCE | analyzegc-deps-check + @$(call PRINT_ANALYZE, $(build_bindir)/clang --analyze -Xanalyzer -analyzer-werror -Xanalyzer -analyzer-output=text \ + $(if $(findstring $(notdir $<),$(SKIP_IMPLICIT_ATOMICS)),,-Xclang -load -Xclang $(build_shlibdir)/libImplicitAtomicsPlugin.$(SHLIB_EXT) -Xclang -analyzer-checker=julia.ImplicitAtomics) \ + -Xanalyzer -analyzer-disable-checker=deadcode.DeadStores \ + --analyzer-no-default-checks \ + $(SA_EXCEPTIONS-$(notdir $<)) \ + $(CLANGSA_FLAGS) $(CLANGSA_CXXFLAGS) $(LLVM_CXXFLAGS) $(JCPPFLAGS) $(JCXXFLAGS) $(DEBUGFLAGS) -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) + rm -f $(build_shlibdir)/libImplicitAtomicsPlugin.$(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 43376d8ba222b..a7061a0a4176c 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 { @@ -588,7 +588,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; @@ -617,7 +617,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)); @@ -656,7 +656,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); } } @@ -666,7 +666,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); @@ -1243,9 +1243,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); @@ -1259,9 +1261,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 05a0a403ccc58..7f720b80cf90f 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 0fa5d6c193513..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 _COMPILER_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 32afff52e0b5f..bdc6fb94ae9b8 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 } @@ -1761,7 +1761,9 @@ static void add_builtin(const char *name, jl_value_t *v) jl_fptr_args_t jl_get_builtin_fptr(jl_value_t *b) { assert(jl_isa(b, (jl_value_t*)jl_builtin_type)); - return ((jl_typemap_entry_t*)jl_gf_mtable(b)->cache)->func.linfo->cache->specptr.fptr1; + jl_typemap_entry_t *entry = (jl_typemap_entry_t*)jl_atomic_load_relaxed(&jl_gf_mtable(b)->cache); + jl_code_instance_t *ci = jl_atomic_load_relaxed(&entry->func.linfo->cache); + return jl_atomic_load_relaxed(&ci->specptr.fptr1); } static jl_value_t *add_builtin_func(const char *name, jl_fptr_args_t fptr) diff --git a/src/ccall.cpp b/src/ccall.cpp index d561e1b8d6dd6..d8cc164f8ae18 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1264,13 +1264,14 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) return args[6 + i]; }; - auto _is_libjulia_func = [&] (uintptr_t ptr, const char *name) { + auto _is_libjulia_func = [&] (uintptr_t ptr, StringRef name) { if ((uintptr_t)fptr == ptr) return true; if (f_lib) { #ifdef _OS_WINDOWS_ if ((f_lib == JL_EXE_LIBNAME) || // preventing invalid pointer access (f_lib == JL_LIBJULIA_INTERNAL_DL_LIBNAME) || + (!strcmp(f_lib, JL_LIBJULIA_DL_LIBNAME)) || (!strcmp(f_lib, jl_crtdll_basename))) { // libjulia-like } @@ -1280,9 +1281,9 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) return false; #endif } - return f_name && !strcmp(f_name, name); + return f_name && f_name == name; }; -#define is_libjulia_func(name) _is_libjulia_func((uintptr_t)&(name), #name) +#define is_libjulia_func(name) _is_libjulia_func((uintptr_t)&(name), StringRef(#name)) // emit arguments jl_cgval_t *argv = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nccallargs); diff --git a/src/ccalltest.c b/src/ccalltest.c index 23137adf7b9a8..b83a9a2c9615c 100644 --- a/src/ccalltest.c +++ b/src/ccalltest.c @@ -9,6 +9,18 @@ #include "../src/support/platform.h" #include "../src/support/dtypes.h" +// Borrow definition from `support/dtypes.h` +#ifdef _OS_WINDOWS_ +# define DLLEXPORT __declspec(dllexport) +#else +# if defined(_OS_LINUX_) +# define DLLEXPORT __attribute__ ((visibility("protected"))) +# else +# define DLLEXPORT __attribute__ ((visibility("default"))) +# endif +#endif + + #ifdef _P64 #define jint int64_t #define PRIjint PRId64 @@ -33,7 +45,7 @@ int __declspec(noinline) #else int __attribute__((noinline)) #endif -JL_DLLEXPORT testUcharX(unsigned char x) { +DLLEXPORT testUcharX(unsigned char x) { return xs[x]; } @@ -47,41 +59,41 @@ typedef struct { jint imag; } complex_t; -JL_DLLEXPORT complex_t ctest(complex_t a) { +DLLEXPORT complex_t ctest(complex_t a) { a.real += 1; a.imag -= 2; return a; } -JL_DLLEXPORT complex double cgtest(complex double a) { +DLLEXPORT complex double cgtest(complex double a) { //Unpack a ComplexPair{Float64} struct if (verbose) fprintf(stderr,"%g + %g i\n", creal(a), cimag(a)); a += 1 - (2.0*I); return a; } -JL_DLLEXPORT complex double *cgptest(complex double *a) { +DLLEXPORT complex double *cgptest(complex double *a) { //Unpack a ComplexPair{Float64} struct if (verbose) fprintf(stderr,"%g + %g i\n", creal(*a), cimag(*a)); *a += 1 - (2.0*I); return a; } -JL_DLLEXPORT complex float cftest(complex float a) { +DLLEXPORT complex float cftest(complex float a) { //Unpack a ComplexPair{Float32} struct if (verbose) fprintf(stderr,"%g + %g i\n", creal(a), cimag(a)); a += 1 - (2.0*I); return a; } -JL_DLLEXPORT complex float *cfptest(complex float *a) { +DLLEXPORT complex float *cfptest(complex float *a) { //Unpack a ComplexPair{Float64} struct if (verbose) fprintf(stderr,"%g + %g i\n", creal(*a), cimag(*a)); *a += 1 - (2.0*I); return a; } -JL_DLLEXPORT complex_t *cptest(complex_t *a) { +DLLEXPORT complex_t *cptest(complex_t *a) { //Unpack a ComplexPair{Int} struct pointer if (verbose) fprintf(stderr,"%" PRIjint " + %" PRIjint " i\n", a->real, a->imag); a->real += 1; @@ -89,7 +101,7 @@ JL_DLLEXPORT complex_t *cptest(complex_t *a) { return a; } -JL_DLLEXPORT complex_t *cptest_static(complex_t *a) { +DLLEXPORT complex_t *cptest_static(complex_t *a) { if (verbose) fprintf(stderr,"%" PRIjint " + %" PRIjint " i\n", a->real, a->imag); complex_t *b = (complex_t*)malloc_s(sizeof(complex_t)); b->real = a->real; @@ -331,7 +343,7 @@ typedef struct { #endif // _COMPILER_INTEL_ -JL_DLLEXPORT struct1 test_1(struct1 a, float b) { +DLLEXPORT struct1 test_1(struct1 a, float b) { //Unpack a "small" struct { float, double } if (verbose) fprintf(stderr,"%g + %g i & %g\n", a.x, a.y, b); a.x += b * 1; @@ -339,7 +351,7 @@ JL_DLLEXPORT struct1 test_1(struct1 a, float b) { return a; } -JL_DLLEXPORT struct1 test_1long_a(jint x1, jint x2, jint x3, struct1 a, float b) { +DLLEXPORT struct1 test_1long_a(jint x1, jint x2, jint x3, struct1 a, float b) { //Unpack a "small" struct { float, double } if (verbose) fprintf(stderr,"(%" PRIjint ", %" PRIjint ", %" PRIjint ") & %g + %g i & %g\n", x1, x2, x3, a.x, a.y, b); a.x += b + x1 + x2 + x3; @@ -347,7 +359,7 @@ JL_DLLEXPORT struct1 test_1long_a(jint x1, jint x2, jint x3, struct1 a, float b) return a; } -JL_DLLEXPORT struct1 test_1long_b(jint x1, double x2, jint x3, struct1 a, float b) { +DLLEXPORT struct1 test_1long_b(jint x1, double x2, jint x3, struct1 a, float b) { //Unpack a "small" struct { float, double } if (verbose) fprintf(stderr,"(%" PRIjint ", %g, %" PRIjint ") & %g + %g i & %g\n", x1, x2, x3, a.x, a.y, b); a.x += b + x1 + x2 + x3; @@ -355,7 +367,7 @@ JL_DLLEXPORT struct1 test_1long_b(jint x1, double x2, jint x3, struct1 a, float return a; } -JL_DLLEXPORT struct1 test_1long_c(jint x1, double x2, jint x3, jint x4, struct1 a, float b) { +DLLEXPORT struct1 test_1long_c(jint x1, double x2, jint x3, jint x4, struct1 a, float b) { //Unpack a "small" struct { float, double } if (verbose) fprintf(stderr,"(%" PRIjint ", %g, %" PRIjint ", %" PRIjint ") & %g + %g i & %g\n", x1, x2, x3, x4, a.x, a.y, b); a.x += b + x1 + x2 + x3 + x4; @@ -363,7 +375,7 @@ JL_DLLEXPORT struct1 test_1long_c(jint x1, double x2, jint x3, jint x4, struct1 return a; } -JL_DLLEXPORT struct2a test_2a(struct2a a, int32_t b) { +DLLEXPORT struct2a test_2a(struct2a a, int32_t b) { //Unpack a ComplexPair{Int32} struct if (verbose) fprintf(stderr,"%" PRId32 " + %" PRId32 " i & %" PRId32 "\n", a.x.x, a.y.y, b); a.x.x += b*1; @@ -371,7 +383,7 @@ JL_DLLEXPORT struct2a test_2a(struct2a a, int32_t b) { return a; } -JL_DLLEXPORT struct2b test_2b(struct2b a, int32_t b) { +DLLEXPORT struct2b test_2b(struct2b a, int32_t b) { //Unpack a ComplexPair{Int32} struct if (verbose) fprintf(stderr,"%" PRId32 " + %" PRId32 " i & %" PRId32 "\n", a.x, a.y, b); a.x += b*1; @@ -379,7 +391,7 @@ JL_DLLEXPORT struct2b test_2b(struct2b a, int32_t b) { return a; } -JL_DLLEXPORT struct3a test_3a(struct3a a, int64_t b) { +DLLEXPORT struct3a test_3a(struct3a a, int64_t b) { //Unpack a ComplexPair{Int64} struct if (verbose) fprintf(stderr,"%" PRId64 " + %" PRId64 " i & %" PRId64 "\n", a.x.x, a.y.y, b); a.x.x += b*1; @@ -387,7 +399,7 @@ JL_DLLEXPORT struct3a test_3a(struct3a a, int64_t b) { return a; } -JL_DLLEXPORT struct3b test_3b(struct3b a, int64_t b) { +DLLEXPORT struct3b test_3b(struct3b a, int64_t b) { //Unpack a ComplexPair{Int64} struct if (verbose) fprintf(stderr,"%" PRId64 " + %" PRId64 " i & %" PRId64 "\n", a.x, a.y, b); a.x += b*1; @@ -395,7 +407,7 @@ JL_DLLEXPORT struct3b test_3b(struct3b a, int64_t b) { return a; } -JL_DLLEXPORT struct4 test_4(struct4 a, int32_t b) { +DLLEXPORT struct4 test_4(struct4 a, int32_t b) { if (verbose) fprintf(stderr,"%" PRId32 ",%" PRId32 ",%" PRId32 " & %" PRId32 "\n", a.x, a.y, a.z, b); a.x += b*1; a.y -= b*2; @@ -403,7 +415,7 @@ JL_DLLEXPORT struct4 test_4(struct4 a, int32_t b) { return a; } -JL_DLLEXPORT struct5 test_5(struct5 a, int32_t b) { +DLLEXPORT struct5 test_5(struct5 a, int32_t b) { if (verbose) fprintf(stderr,"%" PRId32 ",%" PRId32 ",%" PRId32 ",%" PRId32 " & %" PRId32 "\n", a.x, a.y, a.z, a.a, b); a.x += b*1; a.y -= b*2; @@ -413,7 +425,7 @@ JL_DLLEXPORT struct5 test_5(struct5 a, int32_t b) { return a; } -JL_DLLEXPORT struct6 test_6(struct6 a, int64_t b) { +DLLEXPORT struct6 test_6(struct6 a, int64_t b) { if (verbose) fprintf(stderr,"%" PRId64 ",%" PRId64 ",%" PRId64 " & %" PRId64 "\n", a.x, a.y, a.z, b); a.x += b*1; a.y -= b*2; @@ -421,28 +433,28 @@ JL_DLLEXPORT struct6 test_6(struct6 a, int64_t b) { return a; } -JL_DLLEXPORT struct7 test_7(struct7 a, int8_t b) { +DLLEXPORT struct7 test_7(struct7 a, int8_t b) { if (verbose) fprintf(stderr,"%" PRId64 ",%" PRId8 " & %" PRId8 "\n", a.x, a.y, b); a.x += b*1; a.y -= b*2; return a; } -JL_DLLEXPORT struct8 test_8(struct8 a, int8_t b) { +DLLEXPORT struct8 test_8(struct8 a, int8_t b) { if (verbose) fprintf(stderr,"%" PRId32 ",%" PRId8 " & %" PRId8 "\n", a.x, a.y, b); a.x += b*1; a.y -= b*2; return a; } -JL_DLLEXPORT struct9 test_9(struct9 a, int16_t b) { +DLLEXPORT struct9 test_9(struct9 a, int16_t b) { if (verbose) fprintf(stderr,"%" PRId32 ",%" PRId16 " & %" PRId16 "\n", a.x, a.y, b); a.x += b*1; a.y -= b*2; return a; } -JL_DLLEXPORT struct10 test_10(struct10 a, int8_t b) { +DLLEXPORT struct10 test_10(struct10 a, int8_t b) { if (verbose) fprintf(stderr,"%" PRId8 ",%" PRId8 ",%" PRId8 ",%" PRId8 " & %" PRId8 "\n", a.x, a.y, a.z, a.a, b); a.x += b*1; a.y -= b*2; @@ -452,14 +464,14 @@ JL_DLLEXPORT struct10 test_10(struct10 a, int8_t b) { return a; } -JL_DLLEXPORT struct11 test_11(struct11 a, float b) { +DLLEXPORT struct11 test_11(struct11 a, float b) { //Unpack a nested ComplexPair{Float32} struct if (verbose) fprintf(stderr,"%g + %g i & %g\n", creal(a.x), cimag(a.x), b); a.x += b*1 - (b*2.0*I); return a; } -JL_DLLEXPORT struct12 test_12(struct12 a, float b) { +DLLEXPORT struct12 test_12(struct12 a, float b) { //Unpack two nested ComplexPair{Float32} structs if (verbose) fprintf(stderr,"%g + %g i & %g + %g i & %g\n", creal(a.x), cimag(a.x), creal(a.y), cimag(a.y), b); @@ -468,14 +480,14 @@ JL_DLLEXPORT struct12 test_12(struct12 a, float b) { return a; } -JL_DLLEXPORT struct13 test_13(struct13 a, double b) { +DLLEXPORT struct13 test_13(struct13 a, double b) { //Unpack a nested ComplexPair{Float64} struct if (verbose) fprintf(stderr,"%g + %g i & %g\n", creal(a.x), cimag(a.x), b); a.x += b*1 - (b*2.0*I); return a; } -JL_DLLEXPORT struct14 test_14(struct14 a, float b) { +DLLEXPORT struct14 test_14(struct14 a, float b) { //The C equivalent of a ComplexPair{Float32} struct (but without special complex ABI) if (verbose) fprintf(stderr,"%g + %g i & %g\n", a.x, a.y, b); a.x += b*1; @@ -483,7 +495,7 @@ JL_DLLEXPORT struct14 test_14(struct14 a, float b) { return a; } -JL_DLLEXPORT struct15 test_15(struct15 a, double b) { +DLLEXPORT struct15 test_15(struct15 a, double b) { //The C equivalent of a ComplexPair{Float64} struct (but without special complex ABI) if (verbose) fprintf(stderr,"%g + %g i & %g\n", a.x, a.y, b); a.x += b*1; @@ -491,7 +503,7 @@ JL_DLLEXPORT struct15 test_15(struct15 a, double b) { return a; } -JL_DLLEXPORT struct16 test_16(struct16 a, float b) { +DLLEXPORT struct16 test_16(struct16 a, float b) { //Unpack a struct with non-obvious packing requirements if (verbose) fprintf(stderr,"%g %g %g %g %g %g & %g\n", a.x, a.y, a.z, a.a, a.b, a.c, b); a.x += b*1; @@ -503,7 +515,7 @@ JL_DLLEXPORT struct16 test_16(struct16 a, float b) { return a; } -JL_DLLEXPORT struct17 test_17(struct17 a, int8_t b) { +DLLEXPORT struct17 test_17(struct17 a, int8_t b) { //Unpack a struct with non-obvious packing requirements if (verbose) fprintf(stderr,"%d %d & %d\n", (int)a.a, (int)a.b, (int)b); a.a += b*1; @@ -511,7 +523,7 @@ JL_DLLEXPORT struct17 test_17(struct17 a, int8_t b) { return a; } -JL_DLLEXPORT struct18 test_18(struct18 a, int8_t b) { +DLLEXPORT struct18 test_18(struct18 a, int8_t b) { //Unpack a struct with non-obvious packing requirements if (verbose) fprintf(stderr,"%d %d %d & %d\n", (int)a.a, (int)a.b, (int)a.c, (int)b); @@ -526,7 +538,7 @@ JL_DLLEXPORT struct18 test_18(struct18 a, int8_t b) { // However, it happens to have the same calling convention with `[2 x i64]` // when used as first argument or return value. #define int128_t struct3b -JL_DLLEXPORT int128_t test_128(int128_t a, int64_t b) { +DLLEXPORT int128_t test_128(int128_t a, int64_t b) { //Unpack a Int128 if (verbose) fprintf(stderr,"0x%016" PRIx64 "%016" PRIx64 " & %" PRId64 "\n", a.y, a.x, b); a.x += b*1; @@ -535,7 +547,7 @@ JL_DLLEXPORT int128_t test_128(int128_t a, int64_t b) { return a; } -JL_DLLEXPORT struct_big test_big(struct_big a) { +DLLEXPORT struct_big test_big(struct_big a) { //Unpack a "big" struct { int, int, char } if (verbose) fprintf(stderr,"%" PRIjint " %" PRIjint " %c\n", a.x, a.y, a.z); a.x += 1; @@ -544,7 +556,7 @@ JL_DLLEXPORT struct_big test_big(struct_big a) { return a; } -JL_DLLEXPORT struct_big test_big_long(jint x1, jint x2, jint x3, struct_big a) { +DLLEXPORT struct_big test_big_long(jint x1, jint x2, jint x3, struct_big a) { //Unpack a "big" struct { int, int, char } if (verbose) fprintf(stderr,"(%" PRIjint ", %" PRIjint ", %" PRIjint ") %" PRIjint " %" PRIjint " %c\n", x1, x2, x3, a.x, a.y, a.z); a.x += 1 + x1 + x2 + x3; @@ -554,7 +566,7 @@ JL_DLLEXPORT struct_big test_big_long(jint x1, jint x2, jint x3, struct_big a) { } #define test_huge(suffix, reg) \ -JL_DLLEXPORT struct_huge##suffix test_huge##suffix(char a, struct_huge##suffix b, char c) { \ +DLLEXPORT struct_huge##suffix test_huge##suffix(char a, struct_huge##suffix b, char c) { \ if (verbose) fprintf(stderr,"%c-%c\n", a, c); \ b.reg *= 39; \ return b; \ @@ -577,7 +589,7 @@ test_huge(5b, r1); // Enough arguments for architectures that uses registers for integer or // floating point arguments to spill. -JL_DLLEXPORT int test_long_args_intp(int *a1, int *a2, int *a3, int *a4, +DLLEXPORT int test_long_args_intp(int *a1, int *a2, int *a3, int *a4, int *a5, int *a6, int *a7, int *a8, int *a9, int *a10, int *a11, int *a12, int *a13, int *a14) @@ -586,7 +598,7 @@ JL_DLLEXPORT int test_long_args_intp(int *a1, int *a2, int *a3, int *a4, *a11 + *a12 + *a13 + *a14); } -JL_DLLEXPORT int test_long_args_int(int a1, int a2, int a3, int a4, +DLLEXPORT int test_long_args_int(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12, int a13, int a14) @@ -595,7 +607,7 @@ JL_DLLEXPORT int test_long_args_int(int a1, int a2, int a3, int a4, a11 + a12 + a13 + a14); } -JL_DLLEXPORT float test_long_args_float(float a1, float a2, float a3, +DLLEXPORT float test_long_args_float(float a1, float a2, float a3, float a4, float a5, float a6, float a7, float a8, float a9, float a10, float a11, float a12, @@ -605,7 +617,7 @@ JL_DLLEXPORT float test_long_args_float(float a1, float a2, float a3, a11 + a12 + a13 + a14); } -JL_DLLEXPORT double test_long_args_double(double a1, double a2, double a3, +DLLEXPORT double test_long_args_double(double a1, double a2, double a3, double a4, double a5, double a6, double a7, double a8, double a9, double a10, double a11, double a12, @@ -620,59 +632,59 @@ typedef struct { int *b; } struct_spill_pint; -JL_DLLEXPORT int test_spill_int1(int *v1, struct_spill_pint s) +DLLEXPORT int test_spill_int1(int *v1, struct_spill_pint s) { return *v1 + *s.a + *s.b; } -JL_DLLEXPORT int test_spill_int2(int *v1, int *v2, struct_spill_pint s) +DLLEXPORT int test_spill_int2(int *v1, int *v2, struct_spill_pint s) { return *v1 + *v2 + *s.a + *s.b; } -JL_DLLEXPORT int test_spill_int3(int *v1, int *v2, int *v3, struct_spill_pint s) +DLLEXPORT int test_spill_int3(int *v1, int *v2, int *v3, struct_spill_pint s) { return *v1 + *v2 + *v3 + *s.a + *s.b; } -JL_DLLEXPORT int test_spill_int4(int *v1, int *v2, int *v3, int *v4, +DLLEXPORT int test_spill_int4(int *v1, int *v2, int *v3, int *v4, struct_spill_pint s) { return *v1 + *v2 + *v3 + *v4 + *s.a + *s.b; } -JL_DLLEXPORT int test_spill_int5(int *v1, int *v2, int *v3, int *v4, int *v5, +DLLEXPORT int test_spill_int5(int *v1, int *v2, int *v3, int *v4, int *v5, struct_spill_pint s) { return *v1 + *v2 + *v3 + *v4 + *v5 + *s.a + *s.b; } -JL_DLLEXPORT int test_spill_int6(int *v1, int *v2, int *v3, int *v4, int *v5, +DLLEXPORT int test_spill_int6(int *v1, int *v2, int *v3, int *v4, int *v5, int *v6, struct_spill_pint s) { return *v1 + *v2 + *v3 + *v4 + *v5 + *v6 + *s.a + *s.b; } -JL_DLLEXPORT int test_spill_int7(int *v1, int *v2, int *v3, int *v4, int *v5, +DLLEXPORT int test_spill_int7(int *v1, int *v2, int *v3, int *v4, int *v5, int *v6, int *v7, struct_spill_pint s) { return *v1 + *v2 + *v3 + *v4 + *v5 + *v6 + *v7 + *s.a + *s.b; } -JL_DLLEXPORT int test_spill_int8(int *v1, int *v2, int *v3, int *v4, int *v5, +DLLEXPORT int test_spill_int8(int *v1, int *v2, int *v3, int *v4, int *v5, int *v6, int *v7, int *v8, struct_spill_pint s) { return *v1 + *v2 + *v3 + *v4 + *v5 + *v6 + *v7 + *v8 + *s.a + *s.b; } -JL_DLLEXPORT int test_spill_int9(int *v1, int *v2, int *v3, int *v4, int *v5, +DLLEXPORT int test_spill_int9(int *v1, int *v2, int *v3, int *v4, int *v5, int *v6, int *v7, int *v8, int *v9, struct_spill_pint s) { return *v1 + *v2 + *v3 + *v4 + *v5 + *v6 + *v7 + *v8 + *v9 + *s.a + *s.b; } -JL_DLLEXPORT int test_spill_int10(int *v1, int *v2, int *v3, int *v4, int *v5, +DLLEXPORT int test_spill_int10(int *v1, int *v2, int *v3, int *v4, int *v5, int *v6, int *v7, int *v8, int *v9, int *v10, struct_spill_pint s) { @@ -685,79 +697,79 @@ typedef struct { float b; } struct_spill_float; -JL_DLLEXPORT float test_spill_float1(float v1, struct_spill_float s) +DLLEXPORT float test_spill_float1(float v1, struct_spill_float s) { return v1 + s.a + s.b; } -JL_DLLEXPORT float test_spill_float2(float v1, float v2, struct_spill_float s) +DLLEXPORT float test_spill_float2(float v1, float v2, struct_spill_float s) { return v1 + v2 + s.a + s.b; } -JL_DLLEXPORT float test_spill_float3(float v1, float v2, float v3, +DLLEXPORT float test_spill_float3(float v1, float v2, float v3, struct_spill_float s) { return v1 + v2 + v3 + s.a + s.b; } -JL_DLLEXPORT float test_spill_float4(float v1, float v2, float v3, float v4, +DLLEXPORT float test_spill_float4(float v1, float v2, float v3, float v4, struct_spill_float s) { return v1 + v2 + v3 + v4 + s.a + s.b; } -JL_DLLEXPORT float test_spill_float5(float v1, float v2, float v3, float v4, +DLLEXPORT float test_spill_float5(float v1, float v2, float v3, float v4, float v5, struct_spill_float s) { return v1 + v2 + v3 + v4 + v5 + s.a + s.b; } -JL_DLLEXPORT float test_spill_float6(float v1, float v2, float v3, float v4, +DLLEXPORT float test_spill_float6(float v1, float v2, float v3, float v4, float v5, float v6, struct_spill_float s) { return v1 + v2 + v3 + v4 + v5 + v6 + s.a + s.b; } -JL_DLLEXPORT float test_spill_float7(float v1, float v2, float v3, float v4, +DLLEXPORT float test_spill_float7(float v1, float v2, float v3, float v4, float v5, float v6, float v7, struct_spill_float s) { return v1 + v2 + v3 + v4 + v5 + v6 + v7 + s.a + s.b; } -JL_DLLEXPORT float test_spill_float8(float v1, float v2, float v3, float v4, +DLLEXPORT float test_spill_float8(float v1, float v2, float v3, float v4, float v5, float v6, float v7, float v8, struct_spill_float s) { return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + s.a + s.b; } -JL_DLLEXPORT float test_spill_float9(float v1, float v2, float v3, float v4, +DLLEXPORT float test_spill_float9(float v1, float v2, float v3, float v4, float v5, float v6, float v7, float v8, float v9, struct_spill_float s) { return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + s.a + s.b; } -JL_DLLEXPORT float test_spill_float10(float v1, float v2, float v3, float v4, +DLLEXPORT float test_spill_float10(float v1, float v2, float v3, float v4, float v5, float v6, float v7, float v8, float v9, float v10, struct_spill_float s) { return (v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + s.a + s.b); } -JL_DLLEXPORT int get_c_int(void) +DLLEXPORT int get_c_int(void) { return c_int; } -JL_DLLEXPORT void set_c_int(int i) +DLLEXPORT void set_c_int(int i) { c_int = i; } -JL_DLLEXPORT void finalizer_cptr(void* v) +DLLEXPORT void finalizer_cptr(void* v) { set_c_int(-1); } @@ -766,7 +778,7 @@ JL_DLLEXPORT void finalizer_cptr(void* v) ////////////////////////////////// // Turn off verbose for automated tests, leave on for debugging -JL_DLLEXPORT void set_verbose(int level) { +DLLEXPORT void set_verbose(int level) { verbose = level; } @@ -774,7 +786,7 @@ JL_DLLEXPORT void set_verbose(int level) { ////////////////////////////////// // Other tests -JL_DLLEXPORT void *test_echo_p(void *p) { +DLLEXPORT void *test_echo_p(void *p) { return p; } @@ -782,7 +794,7 @@ JL_DLLEXPORT void *test_echo_p(void *p) { #include -JL_DLLEXPORT __m128i test_m128i(__m128i a, __m128i b, __m128i c, __m128i d) +DLLEXPORT __m128i test_m128i(__m128i a, __m128i b, __m128i c, __m128i d) { // 64-bit x86 has only level 2 SSE, which does not have a <4 x int32> multiplication, // so we use floating-point instead, and assume caller knows about the hack. @@ -791,7 +803,7 @@ JL_DLLEXPORT __m128i test_m128i(__m128i a, __m128i b, __m128i c, __m128i d) _mm_cvtepi32_ps(_mm_sub_epi32(c,d))))); } -JL_DLLEXPORT __m128 test_m128(__m128 a, __m128 b, __m128 c, __m128 d) +DLLEXPORT __m128 test_m128(__m128 a, __m128 b, __m128 c, __m128 d) { return _mm_add_ps(a, _mm_mul_ps(b, _mm_sub_ps(c, d))); } @@ -800,7 +812,7 @@ JL_DLLEXPORT __m128 test_m128(__m128 a, __m128 b, __m128 c, __m128 d) #ifdef _CPU_AARCH64_ -JL_DLLEXPORT __int128 test_aa64_i128_1(int64_t v1, __int128 v2) +DLLEXPORT __int128 test_aa64_i128_1(int64_t v1, __int128 v2) { return v1 * 2 - v2; } @@ -810,7 +822,7 @@ typedef struct { __int128 v2; } struct_aa64_1; -JL_DLLEXPORT struct_aa64_1 test_aa64_i128_2(int64_t v1, __int128 v2, +DLLEXPORT struct_aa64_1 test_aa64_i128_2(int64_t v1, __int128 v2, struct_aa64_1 v3) { struct_aa64_1 x = {(int32_t)v1 / 2 + 1 - v3.v1, v2 * 2 - 1 - v3.v2}; @@ -822,12 +834,12 @@ typedef struct { double v2; } struct_aa64_2; -JL_DLLEXPORT __fp16 test_aa64_fp16_1(int v1, float v2, double v3, __fp16 v4) +DLLEXPORT __fp16 test_aa64_fp16_1(int v1, float v2, double v3, __fp16 v4) { return (__fp16)(v1 + v2 * 2 + v3 * 3 + v4 * 4); } -JL_DLLEXPORT struct_aa64_2 test_aa64_fp16_2(int v1, float v2, +DLLEXPORT struct_aa64_2 test_aa64_fp16_2(int v1, float v2, double v3, __fp16 v4) { struct_aa64_2 x = {v4 / 2 + 1, v1 * 2 + v2 * 4 - v3}; @@ -836,7 +848,7 @@ JL_DLLEXPORT struct_aa64_2 test_aa64_fp16_2(int v1, float v2, #include -JL_DLLEXPORT int64x2_t test_aa64_vec_1(int32x2_t v1, float _v2, int32x2_t v3) +DLLEXPORT int64x2_t test_aa64_vec_1(int32x2_t v1, float _v2, int32x2_t v3) { int v2 = (int)_v2; return vmovl_s32(v1 * v2 + v3); @@ -854,7 +866,7 @@ typedef struct { int16x8_t v1; } struct_aa64_4; -JL_DLLEXPORT struct_aa64_3 test_aa64_vec_2(struct_aa64_3 v1, struct_aa64_4 v2) +DLLEXPORT struct_aa64_3 test_aa64_vec_2(struct_aa64_3 v1, struct_aa64_4 v2) { // The cast below is to workaround GCC issue. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96990 @@ -945,21 +957,21 @@ test_huge(3_ppc64_hva, vf1[0]); test_huge(4_ppc64_hva, v1[0]); test_huge(5_ppc64_hva, v1[0]); -JL_DLLEXPORT int64_t test_ppc64_vec1long( +DLLEXPORT int64_t test_ppc64_vec1long( int64_t d1, int64_t d2, int64_t d3, int64_t d4, int64_t d5, int64_t d6, int64_t d7, int64_t d8, int64_t d9, struct_huge1_ppc64 vs) { return d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + vs.m + vs.v[0] + vs.v[1] + vs.v[2] + vs.v[3]; } -JL_DLLEXPORT int64_t test_ppc64_vec1long_vec( +DLLEXPORT int64_t test_ppc64_vec1long_vec( int64_t d1, int64_t d2, int64_t d3, int64_t d4, int64_t d5, int64_t d6, int64_t d7, int64_t d8, int64_t d9, float32x4_t vs) { return d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + vs[0] + vs[1] + vs[2] + vs[3]; } -JL_DLLEXPORT float32x4_t test_ppc64_vec2(int64_t d1, float32x4_t a, float32x4_t b, float32x4_t c, float32x4_t d, +DLLEXPORT float32x4_t test_ppc64_vec2(int64_t d1, float32x4_t a, float32x4_t b, float32x4_t c, float32x4_t d, float32x4_t e, float32x4_t f, float32x4_t g, float32x4_t h, float32x4_t i, float32x4_t j, float32x4_t k, float32x4_t l, float32x4_t m, float32x4_t n) { @@ -973,13 +985,13 @@ JL_DLLEXPORT float32x4_t test_ppc64_vec2(int64_t d1, float32x4_t a, float32x4_t #endif -JL_DLLEXPORT int threadcall_args(int a, int b) { +DLLEXPORT int threadcall_args(int a, int b) { return a + b; } -JL_DLLEXPORT void c_exit_finalizer(void* v) { +DLLEXPORT void c_exit_finalizer(void* v) { printf("c_exit_finalizer: %d, %u", *(int*)v, (unsigned)((uintptr_t)v & (uintptr_t)1)); } // global variable for cglobal testing -JL_DLLEXPORT const int global_var = 1; +DLLEXPORT const int global_var = 1; 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/cgutils.cpp b/src/cgutils.cpp index 928e1a07b2f1e..7afd276edc31d 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1407,7 +1407,11 @@ static Value *emit_bounds_check(jl_codectx_t &ctx, const jl_cgval_t &ainfo, jl_v return im1; } -static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_value_t *jt, Value* dest = NULL, MDNode *tbaa_dest = nullptr, bool isVolatile = false); +static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_value_t *jt, Value* dest, MDNode *tbaa_dest, bool isVolatile = false); +static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_value_t *jt) +{ + return emit_unbox(ctx, to, x, jt, nullptr, nullptr, false); +} static void emit_write_barrier(jl_codectx_t&, Value*, ArrayRef); static void emit_write_barrier(jl_codectx_t&, Value*, Value*); static void emit_write_multibarrier(jl_codectx_t&, Value*, Value*, jl_value_t*); @@ -1560,7 +1564,7 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, ret = mark_julia_type(ctx, callval, true, jl_any_type); } if (!jl_subtype(ret.typ, jltype)) { - emit_typecheck(ctx, ret, jltype, fname + "typed_store"); + emit_typecheck(ctx, ret, jltype, fname); ret = update_julia_type(ctx, ret, jltype); } return ret; @@ -3075,7 +3079,7 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo) if (jt == (jl_value_t*)jl_nothing_type) return track_pjlvalue(ctx, literal_pointer_val(ctx, jl_nothing)); if (vinfo.isboxed) { - assert(vinfo.V == vinfo.Vboxed); + assert(vinfo.V == vinfo.Vboxed && vinfo.V != nullptr); assert(vinfo.V->getType() == T_prjlvalue); return vinfo.V; } @@ -3589,7 +3593,7 @@ static Value *emit_defer_signal(jl_codectx_t &ctx) return ctx.builder.CreateInBoundsGEP(T_sigatomic, ptls, ArrayRef(offset), "jl_defer_signal"); } -#ifndef NDEBUG +#ifndef JL_NDEBUG static int compare_cgparams(const jl_cgparams_t *a, const jl_cgparams_t *b) { return diff --git a/src/clangsa/GCChecker.cpp b/src/clangsa/GCChecker.cpp index 675afc3453fba..403d393921730 100644 --- a/src/clangsa/GCChecker.cpp +++ b/src/clangsa/GCChecker.cpp @@ -744,8 +744,7 @@ bool GCChecker::isGCTrackedType(QualType QT) { Name.endswith_lower("jl_method_match_t") || Name.endswith_lower("jl_vararg_t") || Name.endswith_lower("jl_opaque_closure_t") || - // Probably not technically true for these, but let's allow - // it + // Probably not technically true for these, but let's allow it Name.endswith_lower("typemap_intersection_env") || Name.endswith_lower("interpreter_state") || Name.endswith_lower("jl_typeenv_t") || @@ -775,7 +774,7 @@ bool GCChecker::isGCTracked(const Expr *E) { bool GCChecker::isGloballyRootedType(QualType QT) const { return isJuliaType( - [](StringRef Name) { return Name.endswith_lower("jl_sym_t"); }, QT); + [](StringRef Name) { return Name.endswith("jl_sym_t"); }, QT); } bool GCChecker::isSafepoint(const CallEvent &Call) const { @@ -813,8 +812,8 @@ bool GCChecker::isSafepoint(const CallEvent &Call) const { if (FD->getBuiltinID() != 0 || FD->isTrivial()) isCalleeSafepoint = false; else if (FD->getDeclName().isIdentifier() && - (FD->getName().startswith_lower("uv_") || - FD->getName().startswith_lower("unw_") || + (FD->getName().startswith("uv_") || + FD->getName().startswith("unw_") || FD->getName().startswith("_U")) && FD->getName() != "uv_run") isCalleeSafepoint = false; @@ -952,13 +951,13 @@ bool GCChecker::processAllocationOfResult(const CallEvent &Call, // global roots. StringRef FDName = FD->getDeclName().isIdentifier() ? FD->getName() : ""; - if (FDName.startswith_lower("jl_box_")) { + if (FDName.startswith("jl_box_") || FDName.startswith("ijl_box_")) { SVal Arg = Call.getArgSVal(0); if (auto CI = Arg.getAs()) { const llvm::APSInt &Value = CI->getValue(); bool GloballyRooted = false; const int64_t NBOX_C = 1024; - if (FDName.startswith_lower("jl_box_u")) { + if (FDName.startswith("jl_box_u") || FDName.startswith("ijl_box_u")) { if (Value < NBOX_C) { GloballyRooted = true; } @@ -1068,10 +1067,10 @@ void GCChecker::checkDerivingExpr(const Expr *Result, const Expr *Parent, // TODO: We may want to refine this. This is to track pointers through the // array list in jl_module_t. bool ParentIsModule = isJuliaType( - [](StringRef Name) { return Name.endswith_lower("jl_module_t"); }, + [](StringRef Name) { return Name.endswith("jl_module_t"); }, Parent->getType()); bool ResultIsArrayList = isJuliaType( - [](StringRef Name) { return Name.endswith_lower("arraylist_t"); }, + [](StringRef Name) { return Name.endswith("arraylist_t"); }, Result->getType()); if (!(ParentIsModule && ResultIsArrayList) && isGCTracked(Parent)) { ResultTracked = false; @@ -1428,7 +1427,7 @@ bool GCChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { C.addTransition( State->set(Sym, ValueState::getRooted(nullptr, -1))); return true; - } else if (name == "jl_gc_enable") { + } else if (name == "jl_gc_enable" || name == "ijl_gc_enable") { ProgramStateRef State = C.getState(); // Check for a literal argument SVal Arg = C.getSVal(CE->getArg(0)); diff --git a/src/clangsa/ImplicitAtomics.cpp b/src/clangsa/ImplicitAtomics.cpp new file mode 100644 index 0000000000000..f3123171a7ecd --- /dev/null +++ b/src/clangsa/ImplicitAtomics.cpp @@ -0,0 +1,215 @@ +//===-- ImplicitAtomicsChecker.cpp - Null dereference checker -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines NullDerefChecker, a builtin check in ExprEngine that performs +// checks for null pointers at loads and stores. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ExprObjC.h" +#include "clang/AST/ExprOpenMP.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" +#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" + + +using namespace clang; +using namespace ento; + +namespace { +class ImplicitAtomicsChecker + : public Checker< check::PreStmt, + check::PreStmt, + check::PreStmt, + check::PreCall> { + //check::Bind + //check::Location + BugType ImplicitAtomicsBugType{this, "Implicit Atomic seq_cst synchronization", "Atomics"}; + + void reportBug(const Stmt *S, CheckerContext &C) const; + void reportBug(const Stmt *S, CheckerContext &C, StringRef desc) const; + void reportBug(const CallEvent &S, CheckerContext &C, StringRef desc="") const; + +public: + //void checkLocation(SVal location, bool isLoad, const Stmt* S, + // CheckerContext &C) const; + //void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; + void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; + void checkPreStmt(const UnaryOperator *UOp, CheckerContext &C) const; + void checkPreStmt(const BinaryOperator *BOp, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; +}; +} // end anonymous namespace + +// Checks if RD has name in Names and is in std namespace +static bool hasStdClassWithName(const CXXRecordDecl *RD, + ArrayRef Names) { + // or could check ASTContext::getQualifiedTemplateName()->isDerivedFrom() ? + if (!RD || !RD->getDeclContext()->isStdNamespace()) + return false; + if (RD->getDeclName().isIdentifier()) { + StringRef Name = RD->getName(); + return llvm::any_of(Names, [&Name](StringRef GivenName) -> bool { + return Name == GivenName; + }); + } + return false; +} + +constexpr llvm::StringLiteral STD_PTR_NAMES[] = {"atomic", "atomic_ref"}; + +static bool isStdAtomic(const CXXRecordDecl *RD) { + return hasStdClassWithName(RD, STD_PTR_NAMES); +} + +static bool isStdAtomicCall(const Expr *E) { + return E && isStdAtomic(E->IgnoreImplicit()->getType()->getAsCXXRecordDecl()); +} + +static bool isStdAtomic(const Expr *E) { + return E->getType()->isAtomicType(); +} + +void ImplicitAtomicsChecker::reportBug(const CallEvent &S, CheckerContext &C, StringRef desc) const { + reportBug(S.getOriginExpr(), C, desc); +} + +// try to find the "best" node to attach this to, so we generate fewer duplicate reports +void ImplicitAtomicsChecker::reportBug(const Stmt *S, CheckerContext &C) const { + while (1) { + const auto *expr = dyn_cast(S); + if (!expr) + break; + expr = expr->IgnoreParenCasts(); + if (const auto *UO = dyn_cast(expr)) + S = UO->getSubExpr(); + else if (const auto *BO = dyn_cast(expr)) + S = isStdAtomic(BO->getLHS()) ? BO->getLHS() : + isStdAtomic(BO->getRHS()) ? BO->getRHS() : + BO->getLHS(); + else + break; + } + reportBug(S, C, ""); +} + +void ImplicitAtomicsChecker::reportBug(const Stmt *S, CheckerContext &C, StringRef desc) const { + SmallString<100> buf; + llvm::raw_svector_ostream os(buf); + os << ImplicitAtomicsBugType.getDescription() << desc; + PathDiagnosticLocation N = PathDiagnosticLocation::createBegin( + S, C.getSourceManager(), C.getLocationContext()); + auto report = std::make_unique(ImplicitAtomicsBugType, buf.str(), N); + C.emitReport(std::move(report)); +} + +void ImplicitAtomicsChecker::checkPreStmt(const CastExpr *CE, CheckerContext &C) const { + //if (isStdAtomic(CE) != isStdAtomic(CE->getSubExpr())) { // AtomicToNonAtomic or NonAtomicToAtomic CastExpr + if (CE->getCastKind() == CK_AtomicToNonAtomic) { + reportBug(CE, C); + } +} + +void ImplicitAtomicsChecker::checkPreStmt(const UnaryOperator *UOp, + CheckerContext &C) const { + if (UOp->getOpcode() == UO_AddrOf) + return; + const Expr *Sub = UOp->getSubExpr(); + if (isStdAtomic(UOp) || isStdAtomic(Sub)) + reportBug(UOp, C); +} + +void ImplicitAtomicsChecker::checkPreStmt(const BinaryOperator *BOp, + CheckerContext &C) const { + const Expr *Lhs = BOp->getLHS(); + const Expr *Rhs = BOp->getRHS(); + if (isStdAtomic(Lhs) || isStdAtomic(Rhs) || isStdAtomic(BOp)) + reportBug(BOp, C); +} + +void ImplicitAtomicsChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + const auto *MC = dyn_cast(&Call); + if (!MC || !isStdAtomicCall(MC->getCXXThisExpr())) + return; + if (const auto *OC = dyn_cast(&Call)) { + OverloadedOperatorKind OOK = OC->getOverloadedOperator(); + if (CXXOperatorCallExpr::isAssignmentOp(OOK) || OOK == OO_PlusPlus || OOK == OO_MinusMinus) { + reportBug(Call, C, " (std::atomic)"); + } + } + else if (const auto *Convert = dyn_cast(MC->getDecl())) { + reportBug(Call, C, " (std::atomic)"); + } +} + + +//// These seem probably unnecessary: +// +//static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false) { +// const Expr *E = nullptr; +// +// // Walk through lvalue casts to get the original expression +// // that syntactically caused the load. +// if (const Expr *expr = dyn_cast(S)) +// E = expr->IgnoreParenLValueCasts(); +// +// if (IsBind) { +// const VarDecl *VD; +// const Expr *Init; +// std::tie(VD, Init) = parseAssignment(S); +// if (VD && Init) +// E = Init; +// } +// return E; +//} +// +//// load or bare symbol +//void ImplicitAtomicsChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, +// CheckerContext &C) const { +// const Expr *expr = getDereferenceExpr(S); +// assert(expr); +// if (isStdAtomic(expr)) +// reportBug(S, C); +//} +// +//// auto &r = *l, or store +//void ImplicitAtomicsChecker::checkBind(SVal L, SVal V, const Stmt *S, +// CheckerContext &C) const { +// const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true); +// assert(expr); +// if (isStdAtomic(expr)) +// reportBug(S, C, " (bind)"); +//} + +namespace clang { +namespace ento { +void registerImplicitAtomicsChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} +bool shouldRegisterImplicitAtomicsChecker(const CheckerManager &mgr) { + return true; +} +} // namespace ento +} // namespace clang + +#ifdef CLANG_PLUGIN +extern "C" const char clang_analyzerAPIVersionString[] = + CLANG_ANALYZER_API_VERSION_STRING; +extern "C" void clang_registerCheckers(CheckerRegistry ®istry) { + registry.addChecker( + "julia.ImplicitAtomics", "Flags implicit atomic operations", "" + ); +} +#endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 9000513151fea..a675ebd71cfc5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -391,43 +391,43 @@ static AttributeList get_attrs_zext(LLVMContext &C) // global vars static const auto jlRTLD_DEFAULT_var = new JuliaVariable{ - "jl_RTLD_DEFAULT_handle", + XSTR(jl_RTLD_DEFAULT_handle), true, [](LLVMContext &C) { return T_pint8; }, }; #ifdef _OS_WINDOWS_ static const auto jlexe_var = new JuliaVariable{ - "jl_exe_handle", + XSTR(jl_exe_handle), true, [](LLVMContext &C) { return T_pint8; }, }; static const auto jldll_var = new JuliaVariable{ - "jl_libjulia_internal_handle", + XSTR(jl_libjulia_internal_handle), true, [](LLVMContext &C) { return T_pint8; }, }; #endif //_OS_WINDOWS_ static const auto jlstack_chk_guard_var = new JuliaVariable{ - "__stack_chk_guard", + XSTR(__stack_chk_guard), true, get_pjlvalue, }; static const auto jlgetworld_global = new JuliaVariable{ - "jl_world_counter", + XSTR(jl_world_counter), false, [](LLVMContext &C) { return (Type*)T_size; }, }; static const auto jlboxed_int8_cache = new JuliaVariable{ - "jl_boxed_int8_cache", + XSTR(jl_boxed_int8_cache), true, [](LLVMContext &C) { return (Type*)ArrayType::get(T_pjlvalue, 256); }, }; static const auto jlboxed_uint8_cache = new JuliaVariable{ - "jl_boxed_uint8_cache", + XSTR(jl_boxed_uint8_cache), true, [](LLVMContext &C) { return (Type*)ArrayType::get(T_pjlvalue, 256); }, }; @@ -444,96 +444,96 @@ static const auto jlpgcstack_func = new JuliaFunction{ // Symbols are not gc-tracked, but we'll treat them as callee rooted anyway, // because they may come from a gc-rooted location static const auto jlnew_func = new JuliaFunction{ - "jl_new_structv", + XSTR(jl_new_structv), get_func_sig, get_func_attrs, }; static const auto jlsplatnew_func = new JuliaFunction{ - "jl_new_structt", + XSTR(jl_new_structt), [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, {T_prjlvalue, T_prjlvalue}, false); }, get_func_attrs, }; static const auto jlthrow_func = new JuliaFunction{ - "jl_throw", + XSTR(jl_throw), [](LLVMContext &C) { return FunctionType::get(T_void, {PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)}, false); }, get_attrs_noreturn, }; static const auto jlerror_func = new JuliaFunction{ - "jl_error", + XSTR(jl_error), [](LLVMContext &C) { return FunctionType::get(T_void, {T_pint8}, false); }, get_attrs_noreturn, }; static const auto jlatomicerror_func = new JuliaFunction{ - "jl_atomic_error", + XSTR(jl_atomic_error), [](LLVMContext &C) { return FunctionType::get(T_void, {T_pint8}, false); }, get_attrs_noreturn, }; static const auto jltypeerror_func = new JuliaFunction{ - "jl_type_error", + XSTR(jl_type_error), [](LLVMContext &C) { return FunctionType::get(T_void, {T_pint8, T_prjlvalue, PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)}, false); }, get_attrs_noreturn, }; static const auto jlundefvarerror_func = new JuliaFunction{ - "jl_undefined_var_error", + XSTR(jl_undefined_var_error), [](LLVMContext &C) { return FunctionType::get(T_void, {PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)}, false); }, get_attrs_noreturn, }; static const auto jlboundserrorv_func = new JuliaFunction{ - "jl_bounds_error_ints", + XSTR(jl_bounds_error_ints), [](LLVMContext &C) { return FunctionType::get(T_void, {PointerType::get(T_jlvalue, AddressSpace::CalleeRooted), T_psize, T_size}, false); }, get_attrs_noreturn, }; static const auto jlboundserror_func = new JuliaFunction{ - "jl_bounds_error_int", + XSTR(jl_bounds_error_int), [](LLVMContext &C) { return FunctionType::get(T_void, {PointerType::get(T_jlvalue, AddressSpace::CalleeRooted), T_size}, false); }, get_attrs_noreturn, }; static const auto jlvboundserror_func = new JuliaFunction{ - "jl_bounds_error_tuple_int", + XSTR(jl_bounds_error_tuple_int), [](LLVMContext &C) { return FunctionType::get(T_void, {T_pprjlvalue, T_size, T_size}, false); }, get_attrs_noreturn, }; static const auto jluboundserror_func = new JuliaFunction{ - "jl_bounds_error_unboxed_int", + XSTR(jl_bounds_error_unboxed_int), [](LLVMContext &C) { return FunctionType::get(T_void, {PointerType::get(T_int8, AddressSpace::Derived), T_pjlvalue, T_size}, false); }, get_attrs_noreturn, }; static const auto jlcheckassign_func = new JuliaFunction{ - "jl_checked_assignment", + XSTR(jl_checked_assignment), [](LLVMContext &C) { return FunctionType::get(T_void, {T_pjlvalue, PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)}, false); }, nullptr, }; static const auto jldeclareconst_func = new JuliaFunction{ - "jl_declare_constant", + XSTR(jl_declare_constant), [](LLVMContext &C) { return FunctionType::get(T_void, {T_pjlvalue}, false); }, nullptr, }; static const auto jlgetbindingorerror_func = new JuliaFunction{ - "jl_get_binding_or_error", + XSTR(jl_get_binding_or_error), [](LLVMContext &C) { return FunctionType::get(T_pjlvalue, {T_pjlvalue, T_pjlvalue}, false); }, nullptr, }; static const auto jlboundp_func = new JuliaFunction{ - "jl_boundp", + XSTR(jl_boundp), [](LLVMContext &C) { return FunctionType::get(T_int32, {T_pjlvalue, T_pjlvalue}, false); }, nullptr, }; static const auto jltopeval_func = new JuliaFunction{ - "jl_toplevel_eval", + XSTR(jl_toplevel_eval), [](LLVMContext &C) { return FunctionType::get(T_pjlvalue, {T_pjlvalue, T_pjlvalue}, false); }, [](LLVMContext &C) { return AttributeList::get(C, @@ -542,7 +542,7 @@ static const auto jltopeval_func = new JuliaFunction{ None); }, }; static const auto jlcopyast_func = new JuliaFunction{ - "jl_copy_ast", + XSTR(jl_copy_ast), [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, {T_prjlvalue}, false); }, [](LLVMContext &C) { return AttributeList::get(C, @@ -551,7 +551,7 @@ static const auto jlcopyast_func = new JuliaFunction{ None); }, }; //static const auto jlnsvec_func = new JuliaFunction{ -// "jl_svec", +// XSTR(jl_svec), // [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, // {T_size}, true); }, // [](LLVMContext &C) { return AttributeList::get(C, @@ -560,12 +560,12 @@ static const auto jlcopyast_func = new JuliaFunction{ // None); }, //}; static const auto jlapplygeneric_func = new JuliaFunction{ - "jl_apply_generic", + XSTR(jl_apply_generic), get_func_sig, get_func_attrs, }; static const auto jlinvoke_func = new JuliaFunction{ - "jl_invoke", + XSTR(jl_invoke), [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, {T_prjlvalue, T_pprjlvalue, T_uint32, T_prjlvalue}, false); }, [](LLVMContext &C) { return AttributeList::get(C, @@ -575,19 +575,19 @@ static const auto jlinvoke_func = new JuliaFunction{ Attributes(C, {Attribute::ReadOnly, Attribute::NoCapture})}); }, }; static const auto jlmethod_func = new JuliaFunction{ - "jl_method_def", + XSTR(jl_method_def), [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, {T_prjlvalue, T_prjlvalue, T_prjlvalue, T_pjlvalue}, false); }, nullptr, }; static const auto jlgenericfunction_func = new JuliaFunction{ - "jl_generic_function_def", + XSTR(jl_generic_function_def), [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, {T_pjlvalue, T_pjlvalue, T_pprjlvalue, T_pjlvalue, T_pjlvalue}, false); }, nullptr, }; static const auto jllockvalue_func = new JuliaFunction{ - "jl_lock_value", + XSTR(jl_lock_value), [](LLVMContext &C) { return FunctionType::get(T_void, {PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)}, false); }, [](LLVMContext &C) { return AttributeList::get(C, @@ -596,7 +596,7 @@ static const auto jllockvalue_func = new JuliaFunction{ {Attributes(C, {Attribute::NoCapture})}); }, }; static const auto jlunlockvalue_func = new JuliaFunction{ - "jl_unlock_value", + XSTR(jl_unlock_value), [](LLVMContext &C) { return FunctionType::get(T_void, {PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)}, false); }, [](LLVMContext &C) { return AttributeList::get(C, @@ -605,35 +605,35 @@ static const auto jlunlockvalue_func = new JuliaFunction{ {Attributes(C, {Attribute::NoCapture})}); }, }; static const auto jlenter_func = new JuliaFunction{ - "jl_enter_handler", + XSTR(jl_enter_handler), [](LLVMContext &C) { return FunctionType::get(T_void, {T_pint8}, false); }, nullptr, }; static const auto jl_current_exception_func = new JuliaFunction{ - "jl_current_exception", + XSTR(jl_current_exception), [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, false); }, nullptr, }; static const auto jlleave_func = new JuliaFunction{ - "jl_pop_handler", + XSTR(jl_pop_handler), [](LLVMContext &C) { return FunctionType::get(T_void, {T_int32}, false); }, nullptr, }; static const auto jl_restore_excstack_func = new JuliaFunction{ - "jl_restore_excstack", + XSTR(jl_restore_excstack), [](LLVMContext &C) { return FunctionType::get(T_void, {T_size}, false); }, nullptr, }; static const auto jl_excstack_state_func = new JuliaFunction{ - "jl_excstack_state", + XSTR(jl_excstack_state), [](LLVMContext &C) { return FunctionType::get(T_size, false); }, nullptr, }; static const auto jlegalx_func = new JuliaFunction{ - "jl_egal__unboxed", + XSTR(jl_egal__unboxed), [](LLVMContext &C) { Type *T = PointerType::get(T_jlvalue, AddressSpace::Derived); return FunctionType::get(T_int32, {T, T, T_prjlvalue}, false); }, @@ -652,7 +652,7 @@ static const auto jl_alloc_obj_func = new JuliaFunction{ None); }, }; static const auto jl_newbits_func = new JuliaFunction{ - "jl_new_bits", + XSTR(jl_new_bits), [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, {T_prjlvalue, T_pint8}, false); }, [](LLVMContext &C) { return AttributeList::get(C, @@ -687,23 +687,23 @@ static const auto jl_write_barrier_func = new JuliaFunction{ [](LLVMContext &C) { return AttributeList::get(C, Attributes(C, {Attribute::NoUnwind, Attribute::NoRecurse, Attribute::InaccessibleMemOnly}), AttributeSet(), - None); }, + {Attributes(C, {Attribute::ReadOnly})}); }, }; static const auto jlisa_func = new JuliaFunction{ - "jl_isa", + XSTR(jl_isa), [](LLVMContext &C) { return FunctionType::get(T_int32, {T_prjlvalue, T_prjlvalue}, false); }, nullptr, }; static const auto jlsubtype_func = new JuliaFunction{ - "jl_subtype", + XSTR(jl_subtype), [](LLVMContext &C) { return FunctionType::get(T_int32, {T_prjlvalue, T_prjlvalue}, false); }, nullptr, }; static const auto jlapplytype_func = new JuliaFunction{ - "jl_instantiate_type_in_env", + XSTR(jl_instantiate_type_in_env), [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, {T_pjlvalue, T_pjlvalue, T_pprjlvalue}, false); }, [](LLVMContext &C) { @@ -715,7 +715,7 @@ static const auto jlapplytype_func = new JuliaFunction{ }, }; static const auto jl_object_id__func = new JuliaFunction{ - "jl_object_id_", + XSTR(jl_object_id_), [](LLVMContext &C) { return FunctionType::get(T_size, {T_prjlvalue, PointerType::get(T_int8, AddressSpace::Derived)}, false); }, nullptr, @@ -734,7 +734,7 @@ static const auto setjmp_func = new JuliaFunction{ None); }, }; static const auto memcmp_func = new JuliaFunction{ - "memcmp", + XSTR(memcmp), [](LLVMContext &C) { return FunctionType::get(T_int32, {T_pint8, T_pint8, T_size}, false); }, [](LLVMContext &C) { return AttributeList::get(C, @@ -744,25 +744,25 @@ static const auto memcmp_func = new JuliaFunction{ // TODO: inferLibFuncAttributes(*memcmp_func, TLI); }; static const auto jldlsym_func = new JuliaFunction{ - "jl_load_and_lookup", + XSTR(jl_load_and_lookup), [](LLVMContext &C) { return FunctionType::get(T_pvoidfunc, {T_pint8, T_pint8, PointerType::get(T_pint8, 0)}, false); }, nullptr, }; static const auto jllazydlsym_func = new JuliaFunction{ - "jl_lazy_load_and_lookup", + XSTR(jl_lazy_load_and_lookup), [](LLVMContext &C) { return FunctionType::get(T_pvoidfunc, {T_prjlvalue, T_pint8}, false); }, nullptr, }; static const auto jltypeassert_func = new JuliaFunction{ - "jl_typeassert", + XSTR(jl_typeassert), [](LLVMContext &C) { return FunctionType::get(T_void, {T_prjlvalue, T_prjlvalue}, false); }, nullptr, }; static const auto jlgetnthfieldchecked_func = new JuliaFunction{ - "jl_get_nth_field_checked", + XSTR(jl_get_nth_field_checked), [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, {T_prjlvalue, T_size}, false); }, [](LLVMContext &C) { return AttributeList::get(C, @@ -771,7 +771,7 @@ static const auto jlgetnthfieldchecked_func = new JuliaFunction{ None); }, }; static const auto jlgetcfunctiontrampoline_func = new JuliaFunction{ - "jl_get_cfunction_trampoline", + XSTR(jl_get_cfunction_trampoline), [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, { T_prjlvalue, // f (object) @@ -788,18 +788,18 @@ static const auto jlgetcfunctiontrampoline_func = new JuliaFunction{ None); }, }; static const auto diff_gc_total_bytes_func = new JuliaFunction{ - "jl_gc_diff_total_bytes", + XSTR(jl_gc_diff_total_bytes), [](LLVMContext &C) { return FunctionType::get(T_int64, false); }, nullptr, }; static const auto sync_gc_total_bytes_func = new JuliaFunction{ - "jl_gc_sync_total_bytes", + XSTR(jl_gc_sync_total_bytes), [](LLVMContext &C) { return FunctionType::get(T_int64, {T_int64}, false); }, nullptr, }; static const auto jlarray_data_owner_func = new JuliaFunction{ - "jl_array_data_owner", + XSTR(jl_array_data_owner), [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, {T_prjlvalue}, false); }, [](LLVMContext &C) { return AttributeList::get(C, @@ -809,7 +809,7 @@ static const auto jlarray_data_owner_func = new JuliaFunction{ }; #define BOX_FUNC(ct,rt,at,attrs) \ static const auto box_##ct##_func = new JuliaFunction{ \ - "jl_box_"#ct, \ + XSTR(jl_box_##ct), \ [](LLVMContext &C) { return FunctionType::get(rt, \ {at}, false); }, \ attrs, \ @@ -861,42 +861,42 @@ static const auto pointer_from_objref_func = new JuliaFunction{ None); }, }; -static const auto jltuple_func = new JuliaFunction{"jl_f_tuple", get_func_sig, get_func_attrs}; +static const auto jltuple_func = new JuliaFunction{XSTR(jl_f_tuple), get_func_sig, get_func_attrs}; static const std::map builtin_func_map = { - { &jl_f_is, new JuliaFunction{"jl_f_is", get_func_sig, get_func_attrs} }, - { &jl_f_typeof, new JuliaFunction{"jl_f_typeof", get_func_sig, get_func_attrs} }, - { &jl_f_sizeof, new JuliaFunction{"jl_f_sizeof", get_func_sig, get_func_attrs} }, - { &jl_f_issubtype, new JuliaFunction{"jl_f_issubtype", get_func_sig, get_func_attrs} }, - { &jl_f_isa, new JuliaFunction{"jl_f_isa", get_func_sig, get_func_attrs} }, - { &jl_f_typeassert, new JuliaFunction{"jl_f_typeassert", get_func_sig, get_func_attrs} }, - { &jl_f_ifelse, new JuliaFunction{"jl_f_ifelse", get_func_sig, get_func_attrs} }, - { &jl_f__apply_iterate, new JuliaFunction{"jl_f__apply_iterate", get_func_sig, get_func_attrs} }, - { &jl_f__apply_pure, new JuliaFunction{"jl_f__apply_pure", get_func_sig, get_func_attrs} }, - { &jl_f__call_latest, new JuliaFunction{"jl_f__call_latest", get_func_sig, get_func_attrs} }, - { &jl_f__call_in_world, new JuliaFunction{"jl_f__call_in_world", get_func_sig, get_func_attrs} }, - { &jl_f_throw, new JuliaFunction{"jl_f_throw", get_func_sig, get_func_attrs} }, + { &jl_f_is, new JuliaFunction{XSTR(jl_f_is), get_func_sig, get_func_attrs} }, + { &jl_f_typeof, new JuliaFunction{XSTR(jl_f_typeof), get_func_sig, get_func_attrs} }, + { &jl_f_sizeof, new JuliaFunction{XSTR(jl_f_sizeof), get_func_sig, get_func_attrs} }, + { &jl_f_issubtype, new JuliaFunction{XSTR(jl_f_issubtype), get_func_sig, get_func_attrs} }, + { &jl_f_isa, new JuliaFunction{XSTR(jl_f_isa), get_func_sig, get_func_attrs} }, + { &jl_f_typeassert, new JuliaFunction{XSTR(jl_f_typeassert), get_func_sig, get_func_attrs} }, + { &jl_f_ifelse, new JuliaFunction{XSTR(jl_f_ifelse), get_func_sig, get_func_attrs} }, + { &jl_f__apply_iterate, new JuliaFunction{XSTR(jl_f__apply_iterate), get_func_sig, get_func_attrs} }, + { &jl_f__apply_pure, new JuliaFunction{XSTR(jl_f__apply_pure), get_func_sig, get_func_attrs} }, + { &jl_f__call_latest, new JuliaFunction{XSTR(jl_f__call_latest), get_func_sig, get_func_attrs} }, + { &jl_f__call_in_world, new JuliaFunction{XSTR(jl_f__call_in_world), get_func_sig, get_func_attrs} }, + { &jl_f_throw, new JuliaFunction{XSTR(jl_f_throw), get_func_sig, get_func_attrs} }, { &jl_f_tuple, jltuple_func }, - { &jl_f_svec, new JuliaFunction{"jl_f_svec", get_func_sig, get_func_attrs} }, - { &jl_f_applicable, new JuliaFunction{"jl_f_applicable", get_func_sig, get_func_attrs} }, - { &jl_f_invoke, new JuliaFunction{"jl_f_invoke", get_func_sig, get_func_attrs} }, - { &jl_f_invoke_kwsorter, new JuliaFunction{"jl_f_invoke_kwsorter", get_func_sig, get_func_attrs} }, - { &jl_f_isdefined, new JuliaFunction{"jl_f_isdefined", get_func_sig, get_func_attrs} }, - { &jl_f_getfield, new JuliaFunction{"jl_f_getfield", get_func_sig, get_func_attrs} }, - { &jl_f_setfield, new JuliaFunction{"jl_f_setfield", get_func_sig, get_func_attrs} }, - { &jl_f_swapfield, new JuliaFunction{"jl_f_swapfield", get_func_sig, get_func_attrs} }, - { &jl_f_modifyfield, new JuliaFunction{"jl_f_modifyfield", get_func_sig, get_func_attrs} }, - { &jl_f_fieldtype, new JuliaFunction{"jl_f_fieldtype", get_func_sig, get_func_attrs} }, - { &jl_f_nfields, new JuliaFunction{"jl_f_nfields", get_func_sig, get_func_attrs} }, - { &jl_f__expr, new JuliaFunction{"jl_f__expr", get_func_sig, get_func_attrs} }, - { &jl_f__typevar, new JuliaFunction{"jl_f__typevar", get_func_sig, get_func_attrs} }, - { &jl_f_arrayref, new JuliaFunction{"jl_f_arrayref", get_func_sig, get_func_attrs} }, - { &jl_f_const_arrayref, new JuliaFunction{"jl_f_const_arrayref", get_func_sig, get_func_attrs} }, - { &jl_f_arrayset, new JuliaFunction{"jl_f_arrayset", get_func_sig, get_func_attrs} }, - { &jl_f_arraysize, new JuliaFunction{"jl_f_arraysize", get_func_sig, get_func_attrs} }, - { &jl_f_apply_type, new JuliaFunction{"jl_f_apply_type", get_func_sig, get_func_attrs} }, + { &jl_f_svec, new JuliaFunction{XSTR(jl_f_svec), get_func_sig, get_func_attrs} }, + { &jl_f_applicable, new JuliaFunction{XSTR(jl_f_applicable), get_func_sig, get_func_attrs} }, + { &jl_f_invoke, new JuliaFunction{XSTR(jl_f_invoke), get_func_sig, get_func_attrs} }, + { &jl_f_invoke_kwsorter, new JuliaFunction{XSTR(jl_f_invoke_kwsorter), get_func_sig, get_func_attrs} }, + { &jl_f_isdefined, new JuliaFunction{XSTR(jl_f_isdefined), get_func_sig, get_func_attrs} }, + { &jl_f_getfield, new JuliaFunction{XSTR(jl_f_getfield), get_func_sig, get_func_attrs} }, + { &jl_f_setfield, new JuliaFunction{XSTR(jl_f_setfield), get_func_sig, get_func_attrs} }, + { &jl_f_swapfield, new JuliaFunction{XSTR(jl_f_swapfield), get_func_sig, get_func_attrs} }, + { &jl_f_modifyfield, new JuliaFunction{XSTR(jl_f_modifyfield), get_func_sig, get_func_attrs} }, + { &jl_f_fieldtype, new JuliaFunction{XSTR(jl_f_fieldtype), get_func_sig, get_func_attrs} }, + { &jl_f_nfields, new JuliaFunction{XSTR(jl_f_nfields), get_func_sig, get_func_attrs} }, + { &jl_f__expr, new JuliaFunction{XSTR(jl_f__expr), get_func_sig, get_func_attrs} }, + { &jl_f__typevar, new JuliaFunction{XSTR(jl_f__typevar), get_func_sig, get_func_attrs} }, + { &jl_f_arrayref, new JuliaFunction{XSTR(jl_f_arrayref), get_func_sig, get_func_attrs} }, + { &jl_f_const_arrayref, new JuliaFunction{XSTR(jl_f_const_arrayref), get_func_sig, get_func_attrs} }, + { &jl_f_arrayset, new JuliaFunction{XSTR(jl_f_arrayset), get_func_sig, get_func_attrs} }, + { &jl_f_arraysize, new JuliaFunction{XSTR(jl_f_arraysize), get_func_sig, get_func_attrs} }, + { &jl_f_apply_type, new JuliaFunction{XSTR(jl_f_apply_type), get_func_sig, get_func_attrs} }, }; -static const auto jl_new_opaque_closure_jlcall_func = new JuliaFunction{"jl_new_opaque_closure_jlcall", get_func_sig, get_func_attrs}; +static const auto jl_new_opaque_closure_jlcall_func = new JuliaFunction{XSTR(jl_new_opaque_closure_jlcall), get_func_sig, get_func_attrs}; static int globalUnique = 0; @@ -2077,6 +2077,7 @@ static void cg_bdw(jl_codectx_t &ctx, jl_binding_t *b) static jl_value_t *static_apply_type(jl_codectx_t &ctx, const jl_cgval_t *args, size_t nargs) { + assert(nargs > 1); jl_value_t **v = (jl_value_t**)alloca(sizeof(jl_value_t*) * nargs); for (size_t i = 0; i < nargs; i++) { if (!args[i].constant) @@ -3674,12 +3675,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; @@ -3688,9 +3691,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; } } @@ -4578,14 +4583,17 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) jl_expr_t *ex = (jl_expr_t*)expr; jl_value_t **args = (jl_value_t**)jl_array_data(ex->args); + size_t nargs = jl_array_len(ex->args); jl_sym_t *head = ex->head; // this is object-disoriented. // however, this is a good way to do it because it should *not* be easy // to add new node types. if (head == isdefined_sym) { + assert(nargs == 1); return emit_isdefined(ctx, args[0]); } else if (head == throw_undef_if_not_sym) { + assert(nargs == 2); jl_sym_t *var = (jl_sym_t*)args[0]; Value *cond = ctx.builder.CreateTrunc(emit_unbox(ctx, T_int8, emit_expr(ctx, args[1]), (jl_value_t*)jl_bool_type), T_int1); if (var == getfield_undefref_sym) { @@ -4629,20 +4637,23 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) return emit_ccall(ctx, args, jl_array_dim0(ex->args)); } else if (head == cfunction_sym) { + assert(nargs == 5); jl_cgval_t fexpr_rt = emit_expr(ctx, args[1]); return emit_cfunction(ctx, args[0], fexpr_rt, args[2], (jl_svec_t*)args[3]); } else if (head == assign_sym) { + assert(nargs == 2); emit_assignment(ctx, args[0], args[1], ssaval); return ghostValue(jl_nothing_type); } else if (head == static_parameter_sym) { + assert(nargs == 1); return emit_sparam(ctx, jl_unbox_long(args[0]) - 1); } else if (head == method_sym) { - if (jl_expr_nargs(ex) == 1) { + if (nargs == 1) { jl_value_t *mn = args[0]; - assert(jl_expr_nargs(ex) != 1 || jl_is_symbol(mn) || jl_is_slot(mn)); + assert(jl_is_symbol(mn) || jl_is_slot(mn)); Value *bp = NULL, *name, *bp_owner = V_null; jl_binding_t *bnd = NULL; @@ -4691,6 +4702,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) emit_error(ctx, "method: invalid declaration"); return jl_cgval_t(); } + assert(nargs == 3); Value *a1 = boxed(ctx, emit_expr(ctx, args[1])); Value *a2 = boxed(ctx, emit_expr(ctx, args[2])); Value *mdargs[4] = { @@ -4707,6 +4719,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) return meth; } else if (head == const_sym) { + assert(nargs == 1); jl_sym_t *sym = (jl_sym_t*)args[0]; jl_module_t *mod = ctx.module; if (jl_is_globalref(sym)) { @@ -4721,7 +4734,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) } } else if (head == new_sym) { - size_t nargs = jl_array_len(ex->args); + assert(nargs > 0); jl_cgval_t *argv = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nargs); for (size_t i = 0; i < nargs; ++i) { argv[i] = emit_expr(ctx, args[i]); @@ -4740,6 +4753,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) } else if (head == splatnew_sym) { jl_cgval_t argv[2]; + assert(nargs == 2); argv[0] = emit_expr(ctx, args[0]); argv[1] = emit_expr(ctx, args[1]); Value *typ = boxed(ctx, argv[0]); @@ -4750,7 +4764,6 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) return mark_julia_type(ctx, val, true, (jl_value_t*)jl_any_type); } else if (head == new_opaque_closure_sym) { - size_t nargs = jl_array_len(ex->args); assert(nargs >= 5 && "Not enough arguments in new_opaque_closure"); SmallVector argv(nargs); for (size_t i = 0; i < nargs; ++i) { @@ -4890,11 +4903,13 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) true, jl_any_type); } else if (head == exc_sym) { + assert(nargs == 0); return mark_julia_type(ctx, ctx.builder.CreateCall(prepare_call(jl_current_exception_func)), true, jl_any_type); } else if (head == copyast_sym) { + assert(nargs == 1); jl_cgval_t ast = emit_expr(ctx, args[0]); if (ast.typ != (jl_value_t*)jl_expr_type && ast.typ != (jl_value_t*)jl_any_type) { // elide call to jl_copy_ast when possible @@ -4907,7 +4922,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) else if (head == loopinfo_sym) { // parse Expr(:loopinfo, "julia.simdloop", ("llvm.loop.vectorize.width", 4)) SmallVector MDs; - for (int i = 0, ie = jl_expr_nargs(ex); i < ie; ++i) { + for (int i = 0, ie = nargs; i < ie; ++i) { Metadata *MD = to_md_tree(args[i]); if (MD) MDs.push_back(MD); @@ -4927,7 +4942,6 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) return mark_julia_const(bounds_check_enabled(ctx, jl_true) ? jl_true : jl_false); } else if (head == gc_preserve_begin_sym) { - size_t nargs = jl_array_len(ex->args); jl_cgval_t *argv = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nargs); for (size_t i = 0; i < nargs; ++i) { argv[i] = emit_expr(ctx, args[i]); @@ -5059,8 +5073,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); @@ -5743,7 +5758,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 @@ -7447,7 +7462,7 @@ static std::pair, jl_llvm_functions_t> assert(lty != T_prjlvalue); Value *isvalid = emit_isa(ctx, val, phiType, NULL).first; emit_guarded_test(ctx, isvalid, nullptr, [&] { - (void)emit_unbox(ctx, lty, val, phiType, maybe_decay_tracked(ctx, dest)); + (void)emit_unbox(ctx, lty, val, phiType, maybe_decay_tracked(ctx, dest), tbaa_stack); return nullptr; }); } @@ -7475,7 +7490,7 @@ static std::pair, jl_llvm_functions_t> V = V_rnull; Type *lty = julia_type_to_llvm(ctx, val.typ); if (dest && !type_is_ghost(lty)) // basically, if !ghost union - emit_unbox(ctx, lty, val, val.typ, dest); + emit_unbox(ctx, lty, val, val.typ, dest, tbaa_stack); RTindex = ConstantInt::get(T_int8, tindex); } } @@ -7820,12 +7835,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; } } @@ -8153,7 +8170,7 @@ static void init_jit_functions(void) #endif #endif -#define BOX_F(ct) add_named_global("jl_box_"#ct, &jl_box_##ct); +#define BOX_F(ct) add_named_global(XSTR(jl_box_##ct), &jl_box_##ct); BOX_F(int8); BOX_F(uint8); BOX_F(int16); BOX_F(uint16); BOX_F(int32); BOX_F(uint32); diff --git a/src/codegen_shared.h b/src/codegen_shared.h index f56854d2b4ca5..ca876b9b03102 100644 --- a/src/codegen_shared.h +++ b/src/codegen_shared.h @@ -6,6 +6,9 @@ #include #include +#define STR(csym) #csym +#define XSTR(csym) STR(csym) + enum AddressSpace { Generic = 0, Tracked = 10, diff --git a/src/datatype.c b/src/datatype.c index 8052719c6f55f..c49c74a8dd91c 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; @@ -726,23 +726,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; @@ -768,11 +768,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; @@ -813,34 +813,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(); @@ -856,18 +856,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(); @@ -880,32 +880,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(); @@ -922,29 +922,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 { @@ -971,10 +971,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; } } @@ -984,10 +984,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; } } @@ -997,13 +997,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; } } @@ -1014,31 +1014,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; } } @@ -1393,7 +1393,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); @@ -1430,7 +1430,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) @@ -1471,7 +1471,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 { @@ -1521,9 +1521,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 { @@ -1593,7 +1593,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; } @@ -1672,7 +1672,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); @@ -1758,7 +1758,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/debuginfo.cpp b/src/debuginfo.cpp index ad9ed659cbe0d..ed0f09b96220e 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -299,8 +299,8 @@ class JuliaJITEventListener: public JITEventListener #if defined(_OS_WINDOWS_) uint64_t SectionAddrCheck = 0; - uint64_t SectionLoadCheck = 0; - uint64_t SectionWriteCheck = 0; + uint64_t SectionLoadCheck = 0; (void)SectionLoadCheck; + uint64_t SectionWriteCheck = 0; (void)SectionWriteCheck; uint8_t *UnwindData = NULL; #if defined(_CPU_X86_64_) uint8_t *catchjmp = NULL; @@ -426,6 +426,7 @@ JL_DLLEXPORT void ORCNotifyObjectEmitted(JITEventListener *Listener, ((JuliaJITEventListener*)Listener)->_NotifyObjectEmitted(Object, L, memmgr); } +// TODO: convert the safe names from aotcomile.cpp:makeSafeName back into symbols static std::pair jl_demangle(const char *name) JL_NOTSAFEPOINT { // This function is not allowed to reference any TLS variables since @@ -782,10 +783,30 @@ static void get_function_name_and_base(llvm::object::SectionRef Section, size_t if (needs_name) { if (auto name_or_err = sym_found.getName()) { auto nameref = name_or_err.get(); + const char globalPrefix = // == DataLayout::getGlobalPrefix +#if defined(_OS_WINDOWS_) && !defined(_CPU_X86_64_) + '_'; +#elif defined(_OS_DARWIN_) + '_'; +#else + '\0'; +#endif + if (globalPrefix) { + if (nameref[0] == globalPrefix) + nameref = nameref.drop_front(); +#if defined(_OS_WINDOWS_) && !defined(_CPU_X86_64_) + else if (nameref[0] == '@') // X86_VectorCall + nameref = nameref.drop_front(); +#endif + // else VectorCall, Assembly, Internal, etc. + } +#if defined(_OS_WINDOWS_) && !defined(_CPU_X86_64_) + nameref = nameref.split('@').first; +#endif size_t len = nameref.size(); *name = (char*)realloc_s(*name, len + 1); - (*name)[len] = 0; memcpy(*name, nameref.data(), len); + (*name)[len] = 0; needs_name = false; } } @@ -1477,6 +1498,13 @@ void register_eh_frames(uint8_t *Addr, size_t Size) jl_profile_atomic([&]() { __register_frame(Addr); }); + + // Now first count the number of FDEs + size_t nentries = 0; + processFDEs((char*)Addr, Size, [&](const char*){ nentries++; }); + if (nentries == 0) + return; + // Our unwinder unw_dyn_info_t *di = new unw_dyn_info_t; // In a shared library, this is set to the address of the PLT. @@ -1484,13 +1512,10 @@ void register_eh_frames(uint8_t *Addr, size_t Size) // not seem to be used on our supported architectures. di->gp = 0; // I'm not a great fan of the naming of this constant, but it means the - // right thing, which is a table of FDEs and ips. + // right thing, which is a table of FDEs and IPs. di->format = UNW_INFO_FORMAT_IP_OFFSET; di->u.rti.name_ptr = 0; di->u.rti.segbase = (unw_word_t)Addr; - // Now first count the number of FDEs - size_t nentries = 0; - processFDEs((char*)Addr, Size, [&](const char*){ nentries++; }); uintptr_t start_ip = (uintptr_t)-1; uintptr_t end_ip = 0; diff --git a/src/disasm.cpp b/src/disasm.cpp index 589f618396eaf..f94459c8a6063 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -229,17 +229,21 @@ void DILineInfoPrinter::emit_lineinfo(raw_ostream &Out, std::vector // if so, drop all existing calls to it from the top of the context // AND check if instead the context was previously printed that way // but now has removed the recursive frames - StringRef method = StringRef(context.at(nctx - 1).FunctionName).rtrim(';'); + StringRef method = StringRef(context.at(nctx - 1).FunctionName).rtrim(';'); // last matching frame if ((nctx < nframes && StringRef(DI.at(nframes - nctx - 1).FunctionName).rtrim(';') == method) || (nctx < context.size() && StringRef(context.at(nctx).FunctionName).rtrim(';') == method)) { update_line_only = true; - while (nctx > 0 && StringRef(context.at(nctx - 1).FunctionName).rtrim(';') == method) { + // transform nctx to exclude the combined frames + while (nctx > 0 && StringRef(context.at(nctx - 1).FunctionName).rtrim(';') == method) nctx -= 1; - } } } - else if (context.size() > 0) { - update_line_only = true; + if (!update_line_only && nctx < context.size() && nctx < nframes) { + // look at the first non-matching element to see if we are only changing the line number + const DILineInfo &CtxLine = context.at(nctx); + const DILineInfo &FrameLine = DI.at(nframes - 1 - nctx); + if (StringRef(CtxLine.FunctionName).rtrim(';') == StringRef(FrameLine.FunctionName).rtrim(';')) + update_line_only = true; } } else if (nctx < context.size() && nctx < nframes) { diff --git a/src/dlload.c b/src/dlload.c index 0f7914050e8b4..bb4d0a68e7dd9 100644 --- a/src/dlload.c +++ b/src/dlload.c @@ -202,7 +202,7 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, */ if (!abspath && 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); + jl_array_t *DL_LOAD_PATH = (jl_array_t*)(b ? jl_atomic_load_relaxed(&b->value) : NULL); if (DL_LOAD_PATH != NULL) { size_t j; for (j = 0; j < jl_array_len(DL_LOAD_PATH); j++) { @@ -309,7 +309,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 20d3f5689d353..0290d90ed94db 100644 --- a/src/dump.c +++ b/src/dump.c @@ -350,13 +350,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)); } @@ -660,7 +660,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); @@ -1510,8 +1510,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); @@ -1650,10 +1651,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/flisp/print.c b/src/flisp/print.c index 789a42133c6b7..2b20d0d98b225 100644 --- a/src/flisp/print.c +++ b/src/flisp/print.c @@ -643,10 +643,10 @@ static void cvalue_printdata(fl_context_t *fl_ctx, ios_t *f, void *data, if (init == 0) { #if defined(RTLD_SELF) jl_static_print = (size_t (*)(ios_t*, void*)) - (uintptr_t)dlsym(RTLD_SELF, "jl_static_show"); + (uintptr_t)dlsym(RTLD_SELF, "ijl_static_show"); #elif defined(RTLD_DEFAULT) jl_static_print = (size_t (*)(ios_t*, void*)) - (uintptr_t)dlsym(RTLD_DEFAULT, "jl_static_show"); + (uintptr_t)dlsym(RTLD_DEFAULT, "ijl_static_show"); #elif defined(_OS_WINDOWS_) HMODULE handle; if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | @@ -654,7 +654,7 @@ static void cvalue_printdata(fl_context_t *fl_ctx, ios_t *f, void *data, (LPCWSTR)(&cvalue_printdata), &handle)) { jl_static_print = (size_t (*)(ios_t*, void*)) - (uintptr_t)GetProcAddress(handle, "jl_static_show"); + (uintptr_t)GetProcAddress(handle, "ijl_static_show"); } #endif init = 1; diff --git a/src/gc-stacks.c b/src/gc-stacks.c index fb43affe53b0d..b7adf254026ca 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 8fb0e00e8f17b..e92b96fa289ee 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; @@ -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 @@ -537,7 +537,7 @@ JL_DLLEXPORT void jl_finalize_th(jl_task_t *ct, jl_value_t *o) // still holding a reference to the object for (int i = 0; i < jl_n_threads; i++) { jl_ptls_t ptls2 = jl_all_tls_states[i]; - finalize_object(&ptls2->finalizers, o, &copied_list, ct->tid != i); + finalize_object(&ptls2->finalizers, o, &copied_list, jl_atomic_load_relaxed(&ct->tid) != i); } finalize_object(&finalizer_list_marked, o, &copied_list, 0); if (copied_list.len > 0) { @@ -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) { @@ -1668,7 +1674,7 @@ static void NOINLINE gc_mark_stack_resize(jl_gc_mark_cache_t *gc_cache, jl_gc_ma sp->pc_start = gc_cache->pc_stack = (void**)realloc_s(pc_stack, stack_size * 2 * sizeof(void*)); gc_cache->pc_stack_end = sp->pc_end = sp->pc_start + stack_size * 2; - sp->pc += sp->pc_start - pc_stack; + sp->pc = sp->pc_start + (sp->pc - pc_stack); JL_UNLOCK_NOGC(&gc_cache->stack_lock); } @@ -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)); @@ -2642,9 +2648,10 @@ mark: { void *stkbuf = ta->stkbuf; if (gc_cblist_task_scanner) { export_gc_state(ptls, &sp); + int16_t tid = jl_atomic_load_relaxed(&ta->tid); gc_invoke_callbacks(jl_gc_cb_task_scanner_t, gc_cblist_task_scanner, - (ta, ta->tid != -1 && ta == jl_all_tls_states[ta->tid]->root_task)); + (ta, tid != -1 && ta == jl_all_tls_states[tid]->root_task)); import_gc_state(ptls, &sp); } #ifdef COPY_STACKS @@ -2658,8 +2665,9 @@ mark: { uintptr_t ub = (uintptr_t)-1; #ifdef COPY_STACKS if (stkbuf && ta->copy_stack && ta->ptls == NULL) { - assert(ta->tid >= 0); - jl_ptls_t ptls2 = jl_all_tls_states[ta->tid]; + int16_t tid = jl_atomic_load_relaxed(&ta->tid); + assert(tid >= 0); + jl_ptls_t ptls2 = jl_all_tls_states[tid]; ub = (uintptr_t)ptls2->stackbase; lb = ub - ta->copy_stack; offset = (uintptr_t)stkbuf - lb; @@ -2802,9 +2810,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) @@ -2860,7 +2870,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) { @@ -2971,8 +2981,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++; } @@ -3212,15 +3222,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. @@ -3244,7 +3255,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 @@ -3329,9 +3340,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 @@ -3376,8 +3387,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); } @@ -3389,8 +3402,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); } @@ -3402,8 +3417,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); } } @@ -3415,10 +3432,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); } @@ -3482,8 +3502,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(); @@ -3513,10 +3535,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_ @@ -3586,7 +3611,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; @@ -3731,8 +3756,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 d6c9741f3ebf3..ad6378cf2827c 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) @@ -477,6 +477,7 @@ void jl_foreach_reachable_mtable(void (*visit)(jl_methtable_t *mt, void *env), v } else { foreach_mtable_in_module(jl_main_module, visit, env, &visited); + foreach_mtable_in_module(jl_core_module, visit, env, &visited); } JL_GC_POP(); } @@ -493,14 +494,15 @@ static void reset_mt_caches(jl_methtable_t *mt, void *env) jl_function_t *jl_typeinf_func = NULL; -size_t jl_typeinf_world = 0; +size_t jl_typeinf_world = 1; JL_DLLEXPORT void jl_set_typeinf_func(jl_value_t *f) { + size_t newfunc = jl_typeinf_world == 1 && jl_typeinf_func == NULL; jl_typeinf_func = (jl_function_t*)f; jl_typeinf_world = jl_get_tls_world_age(); ++jl_world_counter; // make type-inference the only thing in this world - if (jl_typeinf_world == 0) { + if (newfunc) { // give type inference a chance to see all of these // TODO: also reinfer if max_world != ~(size_t)0 jl_array_t *unspec = jl_alloc_vec_any(0); @@ -990,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, @@ -1007,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; } @@ -1133,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(); @@ -1177,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; @@ -1525,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)) @@ -1545,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); @@ -1725,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++) { @@ -1781,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); @@ -2272,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() @@ -2575,6 +2577,7 @@ JL_DLLEXPORT jl_function_t *jl_get_kwsorter(jl_value_t *ty) strcpy(&suffixed[0], name); strcpy(&suffixed[l], "##kw"); jl_sym_t *fname = jl_symbol(suffixed); + free(suffixed); mt->kwsorter = jl_new_generic_function_with_supertype(fname, mt->module, jl_function_type); jl_gc_wb(mt, mt->kwsorter); } 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/init.c b/src/init.c index 1c58753506fb7..5fe2d388a40f3 100644 --- a/src/init.c +++ b/src/init.c @@ -628,6 +628,8 @@ static void restore_fp_env(void) } } +static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_task_t *ct); + JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) { jl_init_timing(); @@ -722,9 +724,14 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) jl_init_threading(); jl_ptls_t ptls = jl_init_threadtls(0); - jl_init_root_task(ptls, stack_lo, stack_hi); - jl_task_t *ct = jl_current_task; + // warning: this changes `jl_current_task`, so be careful not to call that from this function + jl_task_t *ct = jl_init_root_task(ptls, stack_lo, stack_hi); + JL_GC_PROMISE_ROOTED(ct); + _finish_julia_init(rel, ptls, ct); +} +static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_task_t *ct) +{ jl_init_threadinginfra(); jl_resolve_sysimg_location(rel); @@ -854,7 +861,7 @@ static void post_boot_hooks(void) for (i = 1; i < jl_core_module->bindings.size; i += 2) { if (table[i] != HT_NOTFOUND) { jl_binding_t *b = (jl_binding_t*)table[i]; - jl_value_t *v = b->value; + jl_value_t *v = jl_atomic_load_relaxed(&b->value); if (v) { if (jl_is_unionall(v)) v = jl_unwrap_unionall(v); diff --git a/src/interpreter.c b/src/interpreter.c index ea93527d88938..50946a666ab0c 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..1f74f0059b4d1 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -15,7 +15,7 @@ FunctionType *get_intr_args4(LLVMContext &C) { return FunctionType::get(T_prjlva FunctionType *get_intr_args5(LLVMContext &C) { return FunctionType::get(T_prjlvalue, {T_prjlvalue, T_prjlvalue, T_prjlvalue, T_prjlvalue, T_prjlvalue}, false); } static JuliaFunction *runtime_func[num_intrinsics] = { -#define ADD_I(name, nargs) new JuliaFunction{"jl_"#name, get_intr_args##nargs, nullptr}, +#define ADD_I(name, nargs) new JuliaFunction{XSTR(jl_##name), get_intr_args##nargs, nullptr}, #define ADD_HIDDEN ADD_I #define ALIAS(alias, base) nullptr, INTRINSICS @@ -304,9 +304,10 @@ static Value *emit_unboxed_coercion(jl_codectx_t &ctx, Type *to, Value *unboxed) unboxed = emit_bitcast(ctx, unboxed, to); } else if (!ty->isIntOrPtrTy() && !ty->isFloatingPointTy()) { +#ifndef JL_NDEBUG const DataLayout &DL = jl_data_layout; - unsigned nb = DL.getTypeSizeInBits(ty); - assert(nb == DL.getTypeSizeInBits(to)); +#endif + 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())); @@ -1075,6 +1076,7 @@ static jl_cgval_t emit_intrinsic(jl_codectx_t &ctx, intrinsic f, jl_value_t **ar switch (f) { case arraylen: { + assert(nargs == 1); const jl_cgval_t &x = argv[0]; jl_value_t *typ = jl_unwrap_unionall(x.typ); if (!jl_is_datatype(typ) || ((jl_datatype_t*)typ)->name != jl_array_typename) @@ -1082,12 +1084,16 @@ static jl_cgval_t emit_intrinsic(jl_codectx_t &ctx, intrinsic f, jl_value_t **ar return mark_julia_type(ctx, emit_arraylen(ctx, x), false, jl_long_type); } case pointerref: + assert(nargs == 3); return emit_pointerref(ctx, argv); case pointerset: + assert(nargs == 4); return emit_pointerset(ctx, argv); case atomic_fence: + assert(nargs == 1); return emit_atomicfence(ctx, argv); case atomic_pointerref: + assert(nargs == 2); return emit_atomic_pointerref(ctx, argv); case atomic_pointerset: case atomic_pointerswap: @@ -1095,27 +1101,38 @@ static jl_cgval_t emit_intrinsic(jl_codectx_t &ctx, intrinsic f, jl_value_t **ar case atomic_pointerreplace: return emit_atomic_pointerop(ctx, f, argv, nargs, nullptr); case bitcast: + assert(nargs == 2); return generic_bitcast(ctx, argv); case trunc_int: + assert(nargs == 2); return generic_cast(ctx, f, Instruction::Trunc, argv, true, true); case sext_int: + assert(nargs == 2); return generic_cast(ctx, f, Instruction::SExt, argv, true, true); case zext_int: + assert(nargs == 2); return generic_cast(ctx, f, Instruction::ZExt, argv, true, true); case uitofp: + assert(nargs == 2); return generic_cast(ctx, f, Instruction::UIToFP, argv, false, true); case sitofp: + assert(nargs == 2); return generic_cast(ctx, f, Instruction::SIToFP, argv, false, true); case fptoui: + assert(nargs == 2); return generic_cast(ctx, f, Instruction::FPToUI, argv, true, false); case fptosi: + assert(nargs == 2); return generic_cast(ctx, f, Instruction::FPToSI, argv, true, false); case fptrunc: + assert(nargs == 2); return generic_cast(ctx, f, Instruction::FPTrunc, argv, false, false); case fpext: + assert(nargs == 2); return generic_cast(ctx, f, Instruction::FPExt, argv, false, false); case not_int: { + assert(nargs == 1); const jl_cgval_t &x = argv[0]; if (!jl_is_primitivetype(x.typ)) return emit_runtime_call(ctx, f, argv, nargs); diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 12497cadf4917..c0283fce21dac 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -176,14 +176,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_disasm - this_code->specptr.fptr = (void*)getAddressForFunction(decls.specFunctionObject); + jl_atomic_store_release(&this_code->specptr.fptr, (void*)getAddressForFunction(decls.specFunctionObject)); } if (this_code== codeinst) fptr = addr; @@ -413,10 +413,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) @@ -426,7 +426,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); @@ -439,12 +439,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(); @@ -831,18 +831,19 @@ uint64_t JuliaOJIT::getFunctionAddress(StringRef Name) static int globalUniqueGeneratedNames; StringRef JuliaOJIT::getFunctionAtAddress(uint64_t Addr, jl_code_instance_t *codeinst) { - auto &fname = ReverseLocalSymbolTable[(void*)(uintptr_t)Addr]; - if (fname.empty()) { + std::string *fname = &ReverseLocalSymbolTable[(void*)(uintptr_t)Addr]; + if (fname->empty()) { 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 { @@ -850,10 +851,10 @@ StringRef JuliaOJIT::getFunctionAtAddress(uint64_t Addr, jl_code_instance_t *cod } const char* unadorned_name = jl_symbol_name(codeinst->def->def.method->name); stream_fname << unadorned_name << "_" << globalUniqueGeneratedNames++; - fname = strdup(stream_fname.str().c_str()); - addGlobalMapping(fname, Addr); + *fname = std::move(stream_fname.str()); // store to ReverseLocalSymbolTable + addGlobalMapping(*fname, Addr); } - return fname; + return *fname; } diff --git a/src/jitlayers.h b/src/jitlayers.h index e3cc9245932ac..1d3a334639d21 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -223,7 +223,7 @@ class JuliaOJIT { ObjLayerT ObjectLayer; CompileLayerT CompileLayer; - DenseMap ReverseLocalSymbolTable; + DenseMap ReverseLocalSymbolTable; }; extern JuliaOJIT *jl_ExecutionEngine; diff --git a/src/jl_exported_data.inc b/src/jl_exported_data.inc index b8d5ae0e35b29..280e9c24d58c0 100644 --- a/src/jl_exported_data.inc +++ b/src/jl_exported_data.inc @@ -1,3 +1,5 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + // Pointers that are exposed through the public libjulia #define JL_EXPORTED_DATA_POINTERS(XX) \ XX(jl_abstractarray_type) \ diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 877c603c7ac3e..ae802abbf802c 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -1,3 +1,5 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + #define JL_EXPORTED_FUNCS(XX) \ XX(jl_active_task_stack) \ XX(jl_add_optimization_passes) \ @@ -22,7 +24,6 @@ XX(jl_argument_method_table) \ XX(jl_array_cconvert_cstring) \ XX(jl_array_copy) \ - XX(jl_array_data_owner) \ XX(jl_array_del_at) \ XX(jl_array_del_beg) \ XX(jl_array_del_end) \ @@ -44,7 +45,6 @@ XX(jl_array_to_string) \ XX(jl_array_typetagdata) \ XX(jl_arrayunset) \ - XX(jl_astaggedvalue) \ XX(jl_atexit_hook) \ XX(jl_atomic_bool_cmpswap_bits) \ XX(jl_atomic_cmpswap_bits) \ @@ -95,7 +95,6 @@ XX(jl_checked_assignment) \ XX(jl_clear_implicit_imports) \ XX(jl_clear_malloc_data) \ - XX(jl_clock_now) \ XX(jl_close_uv) \ XX(jl_code_for_staged) \ XX(jl_compile_hint) \ @@ -104,9 +103,7 @@ XX(jl_compute_fieldtypes) \ XX(jl_copy_ast) \ XX(jl_copy_code_info) \ - XX(jl_cpu_pause) \ XX(jl_cpu_threads) \ - XX(jl_cpu_wake) \ XX(jl_crc32c_sw) \ XX(jl_create_native) \ XX(jl_create_system_image) \ @@ -125,7 +122,6 @@ XX(jl_dump_function_ir) \ XX(jl_dump_host_cpu) \ XX(jl_dump_method_asm) \ - XX(jl_egal) \ XX(jl_egal__bits) \ XX(jl_egal__special) \ XX(jl_eh_restore_state) \ @@ -161,7 +157,6 @@ XX(jl_gc_add_finalizer) \ XX(jl_gc_add_finalizer_th) \ XX(jl_gc_add_ptr_finalizer) \ - XX(jl_gc_alloc) \ XX(jl_gc_alloc_0w) \ XX(jl_gc_alloc_1w) \ XX(jl_gc_alloc_2w) \ @@ -196,9 +191,6 @@ XX(jl_gc_pool_alloc) \ XX(jl_gc_queue_multiroot) \ XX(jl_gc_queue_root) \ - XX(jl_gc_safe_enter) \ - XX(jl_gc_safe_leave) \ - XX(jl_gc_safepoint) \ XX(jl_gc_schedule_foreign_sweepfunc) \ XX(jl_gc_set_cb_notify_external_alloc) \ XX(jl_gc_set_cb_notify_external_free) \ @@ -208,8 +200,6 @@ XX(jl_gc_set_cb_task_scanner) \ XX(jl_gc_sync_total_bytes) \ XX(jl_gc_total_hrtime) \ - XX(jl_gc_unsafe_enter) \ - XX(jl_gc_unsafe_leave) \ XX(jl_gdblookup) \ XX(jl_generating_output) \ XX(jl_generic_function_def) \ @@ -221,7 +211,6 @@ XX(jl_get_binding_for_method_def) \ XX(jl_get_binding_or_error) \ XX(jl_get_binding_wr) \ - XX(jl_get_cfunction_trampoline) \ XX(jl_get_cpu_name) \ XX(jl_get_current_task) \ XX(jl_get_default_sysimg_path) \ @@ -229,7 +218,6 @@ XX(jl_get_fenv_consts) \ XX(jl_get_field) \ XX(jl_get_field_offset) \ - XX(jl_get_fieldtypes) \ XX(jl_get_function_id) \ XX(jl_get_global) \ XX(jl_get_image_file) \ @@ -262,7 +250,6 @@ XX(jl_get_safe_restore) \ XX(jl_get_size) \ XX(jl_get_task_tid) \ - XX(jl_gettimeofday) \ XX(jl_get_tls_world_age) \ XX(jl_get_UNAME) \ XX(jl_get_world_counter) \ @@ -278,8 +265,6 @@ XX(jl_has_typevar) \ XX(jl_has_typevar_from_unionall) \ XX(jl_hrtime) \ - XX(jl_id_char) \ - XX(jl_id_start_char) \ XX(jl_idtable_rehash) \ XX(jl_infer_thunk) \ XX(jl_init) \ @@ -386,7 +371,6 @@ XX(jl_object_id_) \ XX(jl_obvious_subtype) \ XX(jl_operator_precedence) \ - XX(jl_op_suffix_char) \ XX(jl_parse) \ XX(jl_parse_all) \ XX(jl_parse_input_line) \ @@ -468,10 +452,6 @@ XX(jl_stored_inline) \ XX(jl_string_ptr) \ XX(jl_string_to_array) \ - XX(jl_strtod_c) \ - XX(jl_strtof_c) \ - XX(jl_substrtod) \ - XX(jl_substrtof) \ XX(jl_subtype) \ XX(jl_subtype_env) \ XX(jl_subtype_env_size) \ @@ -481,14 +461,12 @@ XX(jl_svec_copy) \ XX(jl_svec_fill) \ XX(jl_svec_isassigned) \ - XX(jl_svec_len) \ XX(jl_svec_ref) \ XX(jl_switch) \ XX(jl_switchto) \ XX(jl_symbol) \ XX(jl_symbol_lookup) \ XX(jl_symbol_n) \ - XX(jl_symbol_name) \ XX(jl_tagged_gensym) \ XX(jl_take_buffer) \ XX(jl_task_get_next) \ @@ -517,7 +495,6 @@ XX(jl_type_morespecific) \ XX(jl_type_morespecific_no_subtype) \ XX(jl_typename_str) \ - XX(jl_typeof) \ XX(jl_typeof_str) \ XX(jl_types_equal) \ XX(jl_type_to_llvm) \ @@ -540,7 +517,6 @@ XX(jl_uncompress_argnames) \ XX(jl_uncompress_ir) \ XX(jl_undefined_var_error) \ - XX(jl_valueof) \ XX(jl_value_ptr) \ XX(jl_ver_is_release) \ XX(jl_ver_major) \ @@ -552,3 +528,5 @@ XX(jl_wakeup_thread) \ XX(jl_yield) \ +#define JL_EXPORTED_FUNCS_WIN(XX) \ + XX(jl_setjmp) diff --git a/src/jl_uv.c b/src/jl_uv.c index 719d3bf9c6010..ddfb074a4e535 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) { @@ -208,7 +208,7 @@ JL_DLLEXPORT int jl_process_events(void) jl_task_t *ct = jl_current_task; uv_loop_t *loop = jl_io_loop; jl_gc_safepoint_(ct->ptls); - if (loop && (_threadedregion || ct->tid == 0)) { + if (loop && (_threadedregion || jl_atomic_load_relaxed(&ct->tid) == 0)) { if (jl_atomic_load(&jl_uv_n_waiters) == 0 && jl_mutex_trylock(&jl_uv_mutex)) { loop->stop_flag = 0; int r = uv_run(loop, UV_RUN_NOWAIT); @@ -414,7 +414,7 @@ JL_DLLEXPORT int jl_fs_write(uv_os_fd_t handle, const char *data, size_t len, { jl_task_t *ct = jl_get_current_task(); // TODO: fix this cheating - if (jl_get_safe_restore() || ct == NULL || ct->tid != 0) + if (jl_get_safe_restore() || ct == NULL || jl_atomic_load_relaxed(&ct->tid) != 0) #ifdef _OS_WINDOWS_ return WriteFile(handle, data, len, NULL, NULL); #else @@ -514,7 +514,7 @@ JL_DLLEXPORT void jl_uv_puts(uv_stream_t *stream, const char *str, size_t n) // TODO: Hack to make CoreIO thread-safer jl_task_t *ct = jl_get_current_task(); - if (ct == NULL || ct->tid != 0) { + if (ct == NULL || jl_atomic_load_relaxed(&ct->tid) != 0) { if (stream == JL_STDOUT) { fd = UV_STDOUT_FD; } diff --git a/src/jlapi.c b/src/jlapi.c index 4851cebfe15d9..e3c5938d30abf 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 1330650ee8892..cf6d9b607132f 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++) { @@ -732,9 +732,9 @@ 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) +static int cache_insert_type_set_(jl_svec_t *a, jl_datatype_t *val, uint_t hv, int atomic) { - jl_datatype_t **tab = (jl_datatype_t**)jl_svec_data(a); + _Atomic(jl_value_t*) *tab = (_Atomic(jl_value_t*)*)jl_svec_data(a); size_t sz = jl_svec_len(a); if (sz <= 1) return 0; @@ -744,9 +744,12 @@ static int cache_insert_type_set_(jl_svec_t *a, jl_datatype_t *val, uint_t hv) orig = index; size_t maxprobe = max_probe(sz); do { - jl_value_t *tab_i = (jl_value_t*)tab[index]; + jl_value_t *tab_i = jl_atomic_load_relaxed(&tab[index]); if (tab_i == NULL || tab_i == jl_nothing) { - jl_atomic_store_release(&tab[index], val); + if (atomic) + jl_atomic_store_release(&tab[index], (jl_value_t*)val); + else + jl_atomic_store_relaxed(&tab[index], (jl_value_t*)val); jl_gc_wb(a, val); return 1; } @@ -761,10 +764,10 @@ static jl_svec_t *cache_rehash_set(jl_svec_t *a, size_t newsz); static void cache_insert_type_set(jl_datatype_t *val, uint_t hv) { - jl_svec_t *a = val->name->cache; + jl_svec_t *a = jl_atomic_load_relaxed(&val->name->cache); while (1) { JL_GC_PROMISE_ROOTED(a); - if (cache_insert_type_set_(a, val, hv)) + if (cache_insert_type_set_(a, val, hv, 1)) return; /* table full */ @@ -787,17 +790,17 @@ static void cache_insert_type_set(jl_datatype_t *val, uint_t hv) static jl_svec_t *cache_rehash_set(jl_svec_t *a, size_t newsz) { - jl_datatype_t **ol = (jl_datatype_t**)jl_svec_data(a); + jl_value_t **ol = jl_svec_data(a); size_t sz = jl_svec_len(a); while (1) { size_t i; jl_svec_t *newa = jl_alloc_svec(newsz); JL_GC_PUSH1(&newa); for (i = 0; i < sz; i += 1) { - jl_datatype_t *val = ol[i]; - if (val != NULL && (jl_value_t*)val != jl_nothing) { - uint_t hv = val->hash; - if (!cache_insert_type_set_(newa, val, hv)) { + jl_value_t *val = ol[i]; + if (val != NULL && val != jl_nothing) { + uint_t hv = ((jl_datatype_t*)val)->hash; + if (!cache_insert_type_set_(newa, (jl_datatype_t*)val, hv, 0)) { break; } } @@ -811,7 +814,7 @@ static jl_svec_t *cache_rehash_set(jl_svec_t *a, size_t newsz) static void cache_insert_type_linear(jl_datatype_t *type, ssize_t insert_at) { - jl_svec_t *cache = type->name->linearcache; + jl_svec_t *cache = jl_atomic_load_relaxed(&type->name->linearcache); assert(jl_is_svec(cache)); size_t n = jl_svec_len(cache); if (n == 0 || jl_svecref(cache, n - 1) != NULL) { @@ -848,7 +851,7 @@ void jl_cache_type_(jl_datatype_t *type) cache_insert_type_set(type, hv); } else { - ssize_t idx = lookup_type_idx_linear(type->name->linearcache, key, n); + ssize_t idx = lookup_type_idx_linear(jl_atomic_load_relaxed(&type->name->linearcache), key, n); assert(idx < 0); cache_insert_type_linear(type, ~idx); } @@ -2237,8 +2240,8 @@ void jl_init_types(void) JL_GC_DISABLED jl_array_uint8_type = jl_apply_type2((jl_value_t*)jl_array_type, (jl_value_t*)jl_uint8_type, jl_box_long(1)); jl_array_int32_type = jl_apply_type2((jl_value_t*)jl_array_type, (jl_value_t*)jl_int32_type, jl_box_long(1)); jl_an_empty_vec_any = (jl_value_t*)jl_alloc_vec_any(0); // used internally - jl_nonfunction_mt->leafcache = (jl_array_t*)jl_an_empty_vec_any; - jl_type_type_mt->leafcache = (jl_array_t*)jl_an_empty_vec_any; + jl_atomic_store_relaxed(&jl_nonfunction_mt->leafcache, (jl_array_t*)jl_an_empty_vec_any); + jl_atomic_store_relaxed(&jl_type_type_mt->leafcache, (jl_array_t*)jl_an_empty_vec_any); jl_expr_type = jl_new_datatype(jl_symbol("Expr"), core, diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 428b0513b7e52..1b40704b36c3a 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1956,21 +1956,28 @@ (else (error (string "invalid " syntax-str " \"" (deparse el) "\"")))))))) -(define (expand-if e) - (let* ((test (cadr e)) - (blk? (and (pair? test) (eq? (car test) 'block))) - (stmts (if blk? (cdr (butlast test)) '())) - (test (if blk? (last test) test))) +(define (expand-condition cnd) + (let* ((blk? (and (pair? cnd) (eq? (car cnd) 'block))) + (stmts (if blk? (cdr (butlast cnd)) '())) + (test (if blk? (last cnd) cnd))) (if (and (pair? test) (memq (car test) '(&& |\|\||))) (let* ((clauses `(,(car test) ,@(map expand-forms (cdr (flatten-ex (car test) test))))) (clauses (if (null? (cdr clauses)) (if (eq? (car clauses) '&&) '(true) '(false)) clauses))) - `(if ,(if blk? - `(block ,@(map expand-forms stmts) ,clauses) - clauses) - ,@(map expand-forms (cddr e)))) - (cons (car e) (map expand-forms (cdr e)))))) + (if blk? + `(block ,@(map expand-forms stmts) ,clauses) + clauses)) + (expand-forms cnd)))) + +(define (expand-if e) + (list* (car e) (expand-condition (cadr e)) (map expand-forms (cddr e)))) + +(define (expand-while e) + `(break-block loop-exit + (_while ,(expand-condition (cadr e)) + (break-block loop-cont + (scope-block ,(blockify (expand-forms (caddr e)))))))) (define (expand-vcat e (vcat '((top vcat))) @@ -2565,13 +2572,7 @@ 'if expand-if 'elseif expand-if - - 'while - (lambda (e) - `(break-block loop-exit - (_while ,(expand-forms (cadr e)) - (break-block loop-cont - (scope-block ,(blockify (expand-forms (caddr e)))))))) + 'while expand-while 'break (lambda (e) @@ -2734,7 +2735,7 @@ (check-no-return expr) (if (has-break-or-continue? expr) (error "break or continue outside loop")) - (let ((result (gensy)) + (let ((result (make-ssavalue)) (idx (gensy)) (oneresult (make-ssavalue)) (prod (make-ssavalue)) @@ -2758,16 +2759,14 @@ (let ((overall-itr (if (length= itrs 1) (car iv) prod))) `(scope-block (block - (local ,result) (local ,idx) + (local ,idx) ,.(map (lambda (v r) `(= ,v ,(caddr r))) iv itrs) ,.(if (length= itrs 1) '() `((= ,prod (call (top product) ,@iv)))) (= ,isz (call (top IteratorSize) ,overall-itr)) (= ,szunk (call (core isa) ,isz (top SizeUnknown))) - (if ,szunk - (= ,result (call (curly (core Array) ,ty 1) (core undef) 0)) - (= ,result (call (top _array_for) ,ty ,overall-itr ,isz))) + (= ,result (call (top _array_for) ,ty ,overall-itr ,isz)) (= ,idx (call (top first) (call (top LinearIndices) ,result))) ,(construct-loops (reverse itrs) (reverse iv)) ,result))))) @@ -4207,6 +4206,29 @@ f(x) = yt(x) (emit `(= ,tmp ,cnd)) tmp) cnd))) + (define (emit-cond cnd break-labels endl) + (let* ((cnd (if (and (pair? cnd) (eq? (car cnd) 'block)) + (begin (if (length> cnd 2) (compile (butlast cnd) break-labels #f #f)) + (last cnd)) + cnd)) + (or? (and (pair? cnd) (eq? (car cnd) '|\|\||))) + (tests (if or? + (let ((short-circuit `(goto _))) + (for-each + (lambda (clause) + (let ((jmp (emit `(gotoifnot ,(compile-cond clause break-labels) ,endl)))) + (emit short-circuit) + (set-car! (cddr jmp) (make&mark-label)))) + (butlast (cdr cnd))) + (let ((last-jmp (emit `(gotoifnot ,(compile-cond (last (cdr cnd)) break-labels) ,endl)))) + (set-car! (cdr short-circuit) (make&mark-label)) + (list last-jmp))) + (map (lambda (clause) + (emit `(gotoifnot ,(compile-cond clause break-labels) ,endl))) + (if (and (pair? cnd) (eq? (car cnd) '&&)) + (cdr cnd) + (list cnd)))))) + tests)) (define (emit-assignment lhs rhs) (if rhs (if (valid-ir-rvalue? lhs rhs) @@ -4347,28 +4369,7 @@ f(x) = yt(x) (compile (cadr e) break-labels value tail) #f)) ((if elseif) - (let* ((cnd (cadr e)) - (cnd (if (and (pair? cnd) (eq? (car cnd) 'block)) - (begin (if (length> cnd 2) (compile (butlast cnd) break-labels #f #f)) - (last cnd)) - cnd)) - (or? (and (pair? cnd) (eq? (car cnd) '|\|\||))) - (tests (if or? - (let ((short-circuit `(goto _))) - (for-each - (lambda (clause) - (let ((jmp (emit `(gotoifnot ,(compile-cond clause break-labels) _)))) - (emit short-circuit) - (set-car! (cddr jmp) (make&mark-label)))) - (butlast (cdr cnd))) - (let ((last-jmp (emit `(gotoifnot ,(compile-cond (last (cdr cnd)) break-labels) _)))) - (set-car! (cdr short-circuit) (make&mark-label)) - (list last-jmp))) - (map (lambda (clause) - (emit `(gotoifnot ,(compile-cond clause break-labels) _))) - (if (and (pair? cnd) (eq? (car cnd) '&&)) - (cdr cnd) - (list cnd))))) + (let* ((tests (emit-cond (cadr e) break-labels '_)) (end-jump `(goto _)) (val (if (and value (not tail)) (new-mutable-var) #f))) (let ((v1 (compile (caddr e) break-labels value tail))) @@ -4390,9 +4391,8 @@ f(x) = yt(x) val)))) ((_while) (let* ((endl (make-label)) - (topl (make&mark-label)) - (test (compile-cond (cadr e) break-labels))) - (emit `(gotoifnot ,test ,endl)) + (topl (make&mark-label))) + (emit-cond (cadr e) break-labels endl) (compile (caddr e) break-labels #f #f) (emit `(goto ,topl)) (mark-label endl)) diff --git a/src/julia.expmap b/src/julia.expmap index 5f03eccbfcad6..ddf09cf6e474b 100644 --- a/src/julia.expmap +++ b/src/julia.expmap @@ -15,6 +15,7 @@ ios_*; iswprint; jl_*; + ijl_*; rec_backtrace; julia_*; libsupport_init; diff --git a/src/julia.h b/src/julia.h index de82ce182a517..626f78063de12 100644 --- a/src/julia.h +++ b/src/julia.h @@ -3,6 +3,12 @@ #ifndef JULIA_H #define JULIA_H +#ifdef LIBRARY_EXPORTS +#include "jl_internal_funcs.inc" +#undef jl_setjmp +#undef jl_longjmp +#endif + //** Configuration options that affect the Julia ABI **// // if this is not defined, only individual dimension sizes are // stored and not total length, to save space. @@ -78,7 +84,7 @@ typedef struct _jl_taggedvalue_t jl_taggedvalue_t; -#include "atomics.h" +#include "julia_atomics.h" #include "julia_threads.h" #include "julia_assert.h" @@ -108,7 +114,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; @@ -127,7 +133,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)) @@ -135,8 +141,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; @@ -229,14 +235,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 { @@ -297,13 +295,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 @@ -311,7 +309,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 @@ -350,7 +348,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 }; @@ -369,7 +367,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; @@ -384,9 +382,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 @@ -420,8 +424,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; @@ -510,10 +514,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 @@ -534,7 +538,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; @@ -546,7 +550,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; @@ -571,23 +575,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 @@ -761,7 +765,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 @@ -892,7 +896,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, @@ -904,7 +908,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, @@ -936,7 +940,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( @@ -948,7 +952,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, @@ -956,7 +960,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); @@ -1412,7 +1416,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); @@ -1820,13 +1824,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 @@ -1871,7 +1875,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); @@ -1881,16 +1885,27 @@ JL_DLLEXPORT void jl_restore_excstack(size_t state) JL_NOTSAFEPOINT; #if defined(_OS_WINDOWS_) #if defined(_COMPILER_GCC_) -int __attribute__ ((__nothrow__,__returns_twice__)) (jl_setjmp)(jmp_buf _Buf); +JL_DLLEXPORT int __attribute__ ((__nothrow__,__returns_twice__)) (jl_setjmp)(jmp_buf _Buf); __declspec(noreturn) __attribute__ ((__nothrow__)) void (jl_longjmp)(jmp_buf _Buf, int _Value); +JL_DLLEXPORT int __attribute__ ((__nothrow__,__returns_twice__)) (ijl_setjmp)(jmp_buf _Buf); +__declspec(noreturn) __attribute__ ((__nothrow__)) void (ijl_longjmp)(jmp_buf _Buf, int _Value); #else -int (jl_setjmp)(jmp_buf _Buf); +JL_DLLEXPORT int (jl_setjmp)(jmp_buf _Buf); void (jl_longjmp)(jmp_buf _Buf, int _Value); +JL_DLLEXPORT int (ijl_setjmp)(jmp_buf _Buf); +void (ijl_longjmp)(jmp_buf _Buf, int _Value); #endif +#ifdef LIBRARY_EXPORTS +#define jl_setjmp_f ijl_setjmp +#define jl_setjmp_name "ijl_setjmp" +#define jl_setjmp(a,b) ijl_setjmp(a) +#define jl_longjmp(a,b) ijl_longjmp(a,b) +#else #define jl_setjmp_f jl_setjmp #define jl_setjmp_name "jl_setjmp" #define jl_setjmp(a,b) jl_setjmp(a) #define jl_longjmp(a,b) jl_longjmp(a,b) +#endif #elif defined(_OS_EMSCRIPTEN_) #define jl_setjmp(a,b) setjmp(a) #define jl_longjmp(a,b) longjmp(a,b) @@ -1910,7 +1925,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..80ac85acfb9f2 --- /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 _COMPILER_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 35de5b927eeb6..7bc0233fa31a2 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; @@ -416,6 +418,16 @@ jl_svec_t *jl_perm_symsvec(size_t n, ...); "Number of passed arguments does not match expected number"); \ n; \ }), __VA_ARGS__) +#ifdef jl_svec +#undef jl_svec +#define jl_svec(n, ...) \ + (ijl_svec)(__extension__({ \ + static_assert( \ + n == sizeof((void *[]){ __VA_ARGS__ })/sizeof(void *), \ + "Number of passed arguments does not match expected number"); \ + n; \ + }), __VA_ARGS__) +#else #define jl_svec(n, ...) \ (jl_svec)(__extension__({ \ static_assert( \ @@ -425,6 +437,7 @@ jl_svec_t *jl_perm_symsvec(size_t n, ...); }), __VA_ARGS__) #endif #endif +#endif jl_value_t *jl_gc_realloc_string(jl_value_t *s, size_t sz); JL_DLLEXPORT void *jl_gc_counted_malloc(size_t sz); @@ -728,7 +741,7 @@ void jl_init_intrinsic_functions(void); void jl_init_intrinsic_properties(void); void jl_init_tasks(void) JL_GC_DISABLED; void jl_init_stack_limits(int ismaster, void **stack_hi, void **stack_lo); -void jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi); +jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi); void jl_init_serializer(void); void jl_gc_init(void); void jl_init_uv(void); @@ -752,7 +765,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. @@ -799,13 +812,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); @@ -831,7 +844,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*); @@ -1114,7 +1127,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, @@ -1262,11 +1275,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, @@ -1448,7 +1461,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 90% rename from src/locks.h rename to src/julia_locks.h index 0605cefbd1218..1a64a2344f5a4 100644 --- a/src/locks.h +++ b/src/julia_locks.h @@ -28,7 +28,7 @@ static inline void jl_mutex_wait(jl_mutex_t *lock, int safepoint) return; } while (1) { - if (owner == 0 && jl_atomic_cmpswap(&lock->owner, &owner, self)) { + if (owner == (jl_thread_t)0 && jl_atomic_cmpswap(&lock->owner, &owner, self)) { lock->count = 1; return; } @@ -42,7 +42,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 @@ -96,7 +96,7 @@ static inline int jl_mutex_trylock_nogc(jl_mutex_t *lock) lock->count++; return 1; } - if (owner == 0 && jl_atomic_cmpswap(&lock->owner, &owner, self)) { + if (owner == (jl_thread_t)0 && jl_atomic_cmpswap(&lock->owner, &owner, self)) { lock->count = 1; return 1; } @@ -114,11 +114,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_thread_self() && "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_thread_t)0); jl_cpu_wake(); } #endif @@ -136,7 +136,7 @@ static inline void jl_mutex_unlock(jl_mutex_t *lock) static inline void jl_mutex_init(jl_mutex_t *lock) JL_NOTSAFEPOINT { - lock->owner = 0; + jl_atomic_store_relaxed(&lock->owner, (jl_thread_t)0); lock->count = 0; } diff --git a/src/julia_threads.h b/src/julia_threads.h index 23fa8d1ea7864..4dee37f863aea 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(jl_thread_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) @@ -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; diff --git a/src/llvm-demote-float16.cpp b/src/llvm-demote-float16.cpp index 43d80e208bf0b..5488472c71a1f 100644 --- a/src/llvm-demote-float16.cpp +++ b/src/llvm-demote-float16.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -27,15 +28,7 @@ using namespace llvm; namespace { -struct DemoteFloat16Pass : public FunctionPass { - static char ID; - DemoteFloat16Pass() : FunctionPass(ID){}; - -private: - bool runOnFunction(Function &F) override; -}; - -bool DemoteFloat16Pass::runOnFunction(Function &F) +static bool demoteFloat16(Function &F) { auto &ctx = F.getContext(); auto T_float16 = Type::getHalfTy(ctx); @@ -132,17 +125,41 @@ bool DemoteFloat16Pass::runOnFunction(Function &F) return false; } -char DemoteFloat16Pass::ID = 0; -static RegisterPass +} // end anonymous namespace + +struct DemoteFloat16 : PassInfoMixin { + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +PreservedAnalyses DemoteFloat16::run(Function &F, FunctionAnalysisManager &AM) +{ + demoteFloat16(F); + return PreservedAnalyses::all(); +} + +namespace { + +struct DemoteFloat16Legacy : public FunctionPass { + static char ID; + DemoteFloat16Legacy() : FunctionPass(ID){}; + +private: + bool runOnFunction(Function &F) override { + return demoteFloat16(F); + } +}; + +char DemoteFloat16Legacy::ID = 0; +static RegisterPass Y("DemoteFloat16", "Demote Float16 operations to Float32 equivalents.", false, false); -} +} // end anonymous namespac Pass *createDemoteFloat16Pass() { - return new DemoteFloat16Pass(); + return new DemoteFloat16Legacy(); } extern "C" JL_DLLEXPORT void LLVMExtraAddDemoteFloat16Pass(LLVMPassManagerRef PM) diff --git a/src/llvm-final-gc-lowering.cpp b/src/llvm-final-gc-lowering.cpp index bc68edda2cad7..6654e9db7746c 100644 --- a/src/llvm-final-gc-lowering.cpp +++ b/src/llvm-final-gc-lowering.cpp @@ -227,7 +227,7 @@ bool FinalLowerGC::doFinalization(Module &M) functionList + sizeof(functionList) / sizeof(void*)); bool changed = false; SmallVector init; - ConstantArray *CA = dyn_cast(used->getInitializer()); + ConstantArray *CA = cast(used->getInitializer()); for (auto &Op : CA->operands()) { Constant *C = cast_or_null(Op); if (InitAsSet.count(C->stripPointerCasts())) { diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index db8b19bacea52..d178778b7054e 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -1551,8 +1551,8 @@ State LateLowerGCFrame::LocalScan(Function &F) { // Known functions emitted in codegen that are not safepoints if (callee == pointer_from_objref_func || callee == gc_preserve_begin_func || callee == gc_preserve_end_func || callee == typeof_func || - callee == pgcstack_getter || callee->getName() == "jl_egal__unboxed" || - callee->getName() == "jl_lock_value" || callee->getName() == "jl_unlock_value" || + callee == pgcstack_getter || callee->getName() == XSTR(jl_egal__unboxed) || + callee->getName() == XSTR(jl_lock_value) || callee->getName() == XSTR(jl_unlock_value) || callee == write_barrier_func || callee->getName() == "memcmp") { continue; } diff --git a/src/llvm-lower-handlers.cpp b/src/llvm-lower-handlers.cpp index ae5a6f3b0c11d..f4a04696348d6 100644 --- a/src/llvm-lower-handlers.cpp +++ b/src/llvm-lower-handlers.cpp @@ -21,6 +21,7 @@ #include "julia.h" #include "julia_assert.h" +#include "codegen_shared.h" #define DEBUG_TYPE "lower_handlers" #undef DEBUG @@ -95,11 +96,11 @@ static void ensure_enter_function(Module &M) auto T_pint8 = PointerType::get(T_int8, 0); auto T_void = Type::getVoidTy(M.getContext()); auto T_int32 = Type::getInt32Ty(M.getContext()); - if (!M.getNamedValue("jl_enter_handler")) { + if (!M.getNamedValue(XSTR(jl_enter_handler))) { std::vector ehargs(0); ehargs.push_back(T_pint8); Function::Create(FunctionType::get(T_void, ehargs, false), - Function::ExternalLinkage, "jl_enter_handler", &M); + Function::ExternalLinkage, XSTR(jl_enter_handler), &M); } if (!M.getNamedValue(jl_setjmp_name)) { std::vector args2(0); @@ -118,8 +119,8 @@ bool LowerExcHandlers::doInitialization(Module &M) { if (!except_enter_func) return false; ensure_enter_function(M); - leave_func = M.getFunction("jl_pop_handler"); - jlenter_func = M.getFunction("jl_enter_handler"); + leave_func = M.getFunction(XSTR(jl_pop_handler)); + jlenter_func = M.getFunction(XSTR(jl_enter_handler)); setjmp_func = M.getFunction(jl_setjmp_name); auto T_pint8 = Type::getInt8PtrTy(M.getContext(), 0); diff --git a/src/llvm-muladd.cpp b/src/llvm-muladd.cpp index e5d63667df476..95221e02f5fc6 100644 --- a/src/llvm-muladd.cpp +++ b/src/llvm-muladd.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -34,15 +35,6 @@ using namespace llvm; * when `%v0` has no other use */ -struct CombineMulAdd : public FunctionPass { - static char ID; - CombineMulAdd() : FunctionPass(ID) - {} - -private: - bool runOnFunction(Function &F) override; -}; - // Return true if this function shouldn't be called again on the other operand // This will always return false on LLVM 5.0+ static bool checkCombine(Module *m, Instruction *addOp, Value *maybeMul, Value *addend, @@ -60,7 +52,7 @@ static bool checkCombine(Module *m, Instruction *addOp, Value *maybeMul, Value * return false; } -bool CombineMulAdd::runOnFunction(Function &F) +static bool combineMulAdd(Function &F) { Module *m = F.getParent(); for (auto &BB: F) { @@ -90,14 +82,36 @@ bool CombineMulAdd::runOnFunction(Function &F) return true; } -char CombineMulAdd::ID = 0; -static RegisterPass X("CombineMulAdd", "Combine mul and add to muladd", +struct CombineMulAdd : PassInfoMixin { + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +PreservedAnalyses CombineMulAdd::run(Function &F, FunctionAnalysisManager &AM) +{ + combineMulAdd(F); + return PreservedAnalyses::all(); +} + + +struct CombineMulAddLegacy : public FunctionPass { + static char ID; + CombineMulAddLegacy() : FunctionPass(ID) + {} + +private: + bool runOnFunction(Function &F) override { + return combineMulAdd(F); + } +}; + +char CombineMulAddLegacy::ID = 0; +static RegisterPass X("CombineMulAdd", "Combine mul and add to muladd", false /* Only looks at CFG */, false /* Analysis Pass */); Pass *createCombineMulAddPass() { - return new CombineMulAdd(); + return new CombineMulAddLegacy(); } extern "C" JL_DLLEXPORT void LLVMExtraAddCombineMulAddPass(LLVMPassManagerRef PM) diff --git a/src/llvm-pass-helpers.cpp b/src/llvm-pass-helpers.cpp index 0eed7aec98f0b..89263033ce565 100644 --- a/src/llvm-pass-helpers.cpp +++ b/src/llvm-pass-helpers.cpp @@ -15,6 +15,7 @@ #include "codegen_shared.h" #include "julia_assert.h" #include "llvm-pass-helpers.h" +#include "jl_internal_funcs.inc" using namespace llvm; @@ -209,9 +210,9 @@ namespace jl_intrinsics { } namespace jl_well_known { - static const char *GC_BIG_ALLOC_NAME = "jl_gc_big_alloc"; - static const char *GC_POOL_ALLOC_NAME = "jl_gc_pool_alloc"; - static const char *GC_QUEUE_ROOT_NAME = "jl_gc_queue_root"; + static const char *GC_BIG_ALLOC_NAME = XSTR(jl_gc_big_alloc); + static const char *GC_POOL_ALLOC_NAME = XSTR(jl_gc_pool_alloc); + static const char *GC_QUEUE_ROOT_NAME = XSTR(jl_gc_queue_root); using jl_intrinsics::addGCAllocAttributes; diff --git a/src/llvm-remove-ni.cpp b/src/llvm-remove-ni.cpp index e9e1dd23149cc..2652719e0a269 100644 --- a/src/llvm-remove-ni.cpp +++ b/src/llvm-remove-ni.cpp @@ -3,6 +3,7 @@ #include "llvm-version.h" #include +#include #include #include @@ -14,31 +15,48 @@ using namespace llvm; namespace { -struct RemoveNIPass : public ModulePass { +static bool removeNI(Module &M) +{ + auto dlstr = M.getDataLayoutStr(); + auto nistart = dlstr.find("-ni:"); + if (nistart == std::string::npos) + return false; + auto len = dlstr.size(); + auto niend = nistart + 1; + for (; niend < len; niend++) { + if (dlstr[niend] == '-') { + break; + } + } + dlstr.erase(nistart, niend - nistart); + M.setDataLayout(dlstr); + return true; +} +} + +struct RemoveNI : PassInfoMixin { + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +PreservedAnalyses RemoveNI::run(Module &M, ModuleAnalysisManager &AM) +{ + removeNI(M); + return PreservedAnalyses::all(); +} + +namespace { +struct RemoveNILegacy : public ModulePass { static char ID; - RemoveNIPass() : ModulePass(ID) {}; + RemoveNILegacy() : ModulePass(ID) {}; bool runOnModule(Module &M) { - auto dlstr = M.getDataLayoutStr(); - auto nistart = dlstr.find("-ni:"); - if (nistart == std::string::npos) - return false; - auto len = dlstr.size(); - auto niend = nistart + 1; - for (; niend < len; niend++) { - if (dlstr[niend] == '-') { - break; - } - } - dlstr.erase(nistart, niend - nistart); - M.setDataLayout(dlstr); - return true; + return removeNI(M); } }; -char RemoveNIPass::ID = 0; -static RegisterPass +char RemoveNILegacy::ID = 0; +static RegisterPass Y("RemoveNI", "Remove non-integral address space.", false, @@ -47,7 +65,7 @@ static RegisterPass Pass *createRemoveNIPass() { - return new RemoveNIPass(); + return new RemoveNILegacy(); } extern "C" JL_DLLEXPORT void LLVMExtraAddRemoveNIPass(LLVMPassManagerRef PM) diff --git a/src/llvm-simdloop.cpp b/src/llvm-simdloop.cpp index afcfa60082ad8..752ced7bfb59f 100644 --- a/src/llvm-simdloop.cpp +++ b/src/llvm-simdloop.cpp @@ -30,35 +30,7 @@ namespace llvm { - -/// This pass should run after reduction variables have been converted to phi nodes, -/// otherwise floating-point reductions might not be recognized as such and -/// prevent SIMDization. -struct LowerSIMDLoop : public ModulePass { - static char ID; - LowerSIMDLoop() : ModulePass(ID) - { - } - - protected: - void getAnalysisUsage(AnalysisUsage &AU) const override - { - ModulePass::getAnalysisUsage(AU); - AU.addRequired(); - AU.addPreserved(); - AU.setPreservesCFG(); - } - - private: - bool runOnModule(Module &M) override; - - bool markLoopInfo(Module &M, Function *marker); - - /// If Phi is part of a reduction cycle of FAdd, FSub, FMul or FDiv, - /// mark the ops as permitting reassociation/commuting. - /// As of LLVM 4.0, FDiv is not handled by the loop vectorizer - void enableUnsafeAlgebraIfReduction(PHINode *Phi, Loop *L) const; -}; +namespace { static unsigned getReduceOpcode(Instruction *J, Instruction *operand) { @@ -80,7 +52,10 @@ static unsigned getReduceOpcode(Instruction *J, Instruction *operand) } } -void LowerSIMDLoop::enableUnsafeAlgebraIfReduction(PHINode *Phi, Loop *L) const +/// If Phi is part of a reduction cycle of FAdd, FSub, FMul or FDiv, +/// mark the ops as permitting reassociation/commuting. +/// As of LLVM 4.0, FDiv is not handled by the loop vectorizer +static void enableUnsafeAlgebraIfReduction(PHINode *Phi, Loop *L) { typedef SmallVector chainVector; chainVector chain; @@ -130,18 +105,7 @@ void LowerSIMDLoop::enableUnsafeAlgebraIfReduction(PHINode *Phi, Loop *L) const } } -bool LowerSIMDLoop::runOnModule(Module &M) -{ - Function *loopinfo_marker = M.getFunction("julia.loopinfo_marker"); - - bool Changed = false; - if (loopinfo_marker) - Changed |= markLoopInfo(M, loopinfo_marker); - - return Changed; -} - -bool LowerSIMDLoop::markLoopInfo(Module &M, Function *marker) +static bool markLoopInfo(Module &M, Function *marker, function_ref GetLI) { bool Changed = false; std::vector ToDelete; @@ -149,7 +113,7 @@ bool LowerSIMDLoop::markLoopInfo(Module &M, Function *marker) Instruction *I = cast(U); ToDelete.push_back(I); - LoopInfo &LI = getAnalysis(*I->getParent()->getParent()).getLoopInfo(); + LoopInfo &LI = GetLI(*I->getParent()->getParent()); Loop *L = LI.getLoopFor(I->getParent()); I->removeFromParent(); if (!L) @@ -243,15 +207,81 @@ bool LowerSIMDLoop::markLoopInfo(Module &M, Function *marker) return Changed; } -char LowerSIMDLoop::ID = 0; +} // end anonymous namespace + + +/// This pass should run after reduction variables have been converted to phi nodes, +/// otherwise floating-point reductions might not be recognized as such and +/// prevent SIMDization. +struct LowerSIMDLoop : PassInfoMixin { + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + + +PreservedAnalyses LowerSIMDLoop::run(Module &M, ModuleAnalysisManager &AM) +{ + Function *loopinfo_marker = M.getFunction("julia.loopinfo_marker"); + + if (!loopinfo_marker) + return PreservedAnalyses::all(); + + FunctionAnalysisManager &FAM = + AM.getResult(M).getManager(); + + auto GetLI = [&FAM](Function &F) -> LoopInfo & { + return FAM.getResult(F); + }; + + markLoopInfo(M, loopinfo_marker, GetLI); + + return PreservedAnalyses::all(); +} + +namespace { +class LowerSIMDLoopLegacy : public ModulePass { + //LowerSIMDLoop Impl; + +public: + static char ID; + + LowerSIMDLoopLegacy() : ModulePass(ID) { + } + + bool runOnModule(Module &M) override { + bool Changed = false; + + Function *loopinfo_marker = M.getFunction("julia.loopinfo_marker"); + + auto GetLI = [this](Function &F) -> LoopInfo & { + return getAnalysis(F).getLoopInfo(); + }; + + if (loopinfo_marker) + Changed |= markLoopInfo(M, loopinfo_marker, GetLI); + + return Changed; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override + { + ModulePass::getAnalysisUsage(AU); + AU.addRequired(); + AU.addPreserved(); + AU.setPreservesCFG(); + } +}; + +} // end anonymous namespace + +char LowerSIMDLoopLegacy::ID = 0; -static RegisterPass X("LowerSIMDLoop", "LowerSIMDLoop Pass", +static RegisterPass X("LowerSIMDLoop", "LowerSIMDLoop Pass", false /* Only looks at CFG */, false /* Analysis Pass */); JL_DLLEXPORT Pass *createLowerSimdLoopPass() { - return new LowerSIMDLoop(); + return new LowerSIMDLoopLegacy(); } extern "C" JL_DLLEXPORT void LLVMExtraAddLowerSimdLoopPass(LLVMPassManagerRef PM) 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/llvmcalltest.cpp b/src/llvmcalltest.cpp index fee0fed72c0d9..1ce8e9fe55bef 100644 --- a/src/llvmcalltest.cpp +++ b/src/llvmcalltest.cpp @@ -13,9 +13,20 @@ using namespace llvm; +// Borrow definition from `support/dtypes.h` +#ifdef _OS_WINDOWS_ +# define DLLEXPORT __declspec(dllexport) +#else +# if defined(_OS_LINUX_) +# define DLLEXPORT __attribute__ ((visibility("protected"))) +# else +# define DLLEXPORT __attribute__ ((visibility("default"))) +# endif +#endif + extern "C" { -JL_DLLEXPORT const char *MakeIdentityFunction(jl_value_t* jl_AnyTy) { +DLLEXPORT const char *MakeIdentityFunction(jl_value_t* jl_AnyTy) { LLVMContext Ctx; PointerType *AnyTy = PointerType::get(StructType::get(Ctx), 0); // FIXME: get AnyTy via jl_type_to_llvm(Ctx, jl_AnyTy) @@ -40,7 +51,7 @@ JL_DLLEXPORT const char *MakeIdentityFunction(jl_value_t* jl_AnyTy) { return strdup(buf.c_str()); } -JL_DLLEXPORT const char *MakeLoadGlobalFunction() { +DLLEXPORT const char *MakeLoadGlobalFunction() { LLVMContext Ctx; auto M = new Module("shadow", Ctx); diff --git a/src/macroexpand.scm b/src/macroexpand.scm index f17f4d3510dc6..34414d24bde75 100644 --- a/src/macroexpand.scm +++ b/src/macroexpand.scm @@ -271,7 +271,9 @@ (define (other x) (resolve-expansion-vars-with-new-env x env m parent-scope inarg)) (case (car e) ((where) `(where ,(recur (cadr e)) ,@(map other (cddr e)))) - ((|::|) `(|::| ,(recur (cadr e)) ,(other (caddr e)))) + ((|::|) (if (length= e 2) + `(|::| ,(other (cadr e))) + `(|::| ,(recur (cadr e)) ,(other (caddr e))))) ((call) `(call ,(other (cadr e)) ,@(map (lambda (x) (resolve-expansion-vars-with-new-env x env m parent-scope #t)) @@ -397,13 +399,18 @@ ((not (length> e 2)) e) ((and (pair? (cadr e)) (eq? (caadr e) '|::|)) - `(kw (|::| - ,(if inarg - (resolve-expansion-vars- (cadr (cadr e)) env m parent-scope inarg) - ;; in keyword arg A=B, don't transform "A" - (unescape (cadr (cadr e)))) - ,(resolve-expansion-vars- (caddr (cadr e)) env m parent-scope inarg)) - ,(resolve-expansion-vars-with-new-env (caddr e) env m parent-scope inarg))) + (let* ((type-decl (cadr e)) ;; [argname]::type + (argname (and (length> type-decl 2) (cadr type-decl))) + (type (if argname (caddr type-decl) (cadr type-decl)))) + `(kw (|::| + ,@(if argname + (list (if inarg + (resolve-expansion-vars- argname env m parent-scope inarg) + ;; in keyword arg A=B, don't transform "A" + (unescape argname))) + '()) + ,(resolve-expansion-vars- type env m parent-scope inarg)) + ,(resolve-expansion-vars-with-new-env (caddr e) env m parent-scope inarg)))) (else `(kw ,(if inarg (resolve-expansion-vars- (cadr e) env m parent-scope inarg) diff --git a/src/method.c b/src/method.c index 32011cab1fd82..5899ccffc8a71 100644 --- a/src/method.c +++ b/src/method.c @@ -205,8 +205,10 @@ static jl_value_t *resolve_globals(jl_value_t *expr, jl_module_t *module, jl_sve if (fe_mod->istopmod && !strcmp(jl_symbol_name(fe_sym), "getproperty") && jl_is_symbol(s)) { if (eager_resolve || jl_binding_resolved_p(me_mod, me_sym)) { jl_binding_t *b = jl_get_binding(me_mod, me_sym); - if (b && b->constp && b->value && jl_is_module(b->value)) { - return jl_module_globalref((jl_module_t*)b->value, (jl_sym_t*)s); + if (b && b->constp) { + jl_value_t *v = jl_atomic_load_relaxed(&b->value); + if (v && jl_is_module(v)) + return jl_module_globalref((jl_module_t*)v, (jl_sym_t*)s); } } } @@ -220,7 +222,7 @@ static jl_value_t *resolve_globals(jl_value_t *expr, jl_module_t *module, jl_sve if (jl_binding_resolved_p(fe_mod, fe_sym)) { // look at some known called functions jl_binding_t *b = jl_get_binding(fe_mod, fe_sym); - if (b && b->constp && b->value == jl_builtin_tuple) { + if (b && b->constp && jl_atomic_load_relaxed(&b->value) == jl_builtin_tuple) { size_t j; for (j = 1; j < nargs; j++) { if (!jl_is_quotenode(jl_exprarg(e, j))) @@ -416,7 +418,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_new_method_instance_uninit(void) li->uninferred = NULL; li->backedges = NULL; li->callbacks = NULL; - li->cache = NULL; + jl_atomic_store_relaxed(&li->cache, NULL); li->inInference = 0; return li; } @@ -724,8 +726,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; @@ -733,7 +735,7 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t *module) m->module = module; m->external_mt = NULL; m->source = NULL; - m->unspecialized = NULL; + jl_atomic_store_relaxed(&m->unspecialized, NULL); m->generator = NULL; m->name = NULL; m->file = empty_sym; @@ -741,7 +743,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; @@ -784,24 +786,25 @@ 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; assert(name && bp); - if (bnd && bnd->value != NULL && !bnd->constp) + if (bnd && jl_atomic_load_relaxed(&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 4120b6cb9225d..765f83498bac4 100644 --- a/src/module.c +++ b/src/module.c @@ -11,7 +11,7 @@ extern "C" { #endif -JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, uint8_t using_core) +JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, uint8_t default_names) { jl_task_t *ct = jl_current_task; const jl_uuid_t uuid_zero = {0, 0}; @@ -36,11 +36,13 @@ JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, uint8_t using_core) htable_new(&m->bindings, 0); arraylist_new(&m->usings, 0); JL_GC_PUSH1(&m); - if (jl_core_module && using_core) { + if (jl_core_module && default_names) { jl_module_using(m, jl_core_module); } // export own name, so "using Foo" makes "Foo" itself visible - jl_set_const(m, name, (jl_value_t*)m); + if (default_names) { + jl_set_const(m, name, (jl_value_t*)m); + } jl_module_export(m, name); JL_GC_POP(); return m; @@ -56,10 +58,10 @@ uint32_t jl_module_next_counter(jl_module_t *m) return jl_atomic_fetch_add(&m->counter, 1); } -JL_DLLEXPORT jl_value_t *jl_f_new_module(jl_sym_t *name, uint8_t std_imports, uint8_t using_core) +JL_DLLEXPORT jl_value_t *jl_f_new_module(jl_sym_t *name, uint8_t std_imports, uint8_t default_names) { // TODO: should we prohibit this during incremental compilation? - jl_module_t *m = jl_new_module_(name, using_core); + jl_module_t *m = jl_new_module_(name, default_names); JL_GC_PUSH1(&m); m->parent = jl_main_module; // TODO: this is a lie jl_gc_wb(m, m->parent); @@ -186,7 +188,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 @@ -262,7 +264,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 { @@ -380,12 +382,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) @@ -640,7 +647,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); @@ -774,7 +782,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 diff --git a/src/opaque_closure.c b/src/opaque_closure.c index 4a23c604b079d..6021f3bd53431 100644 --- a/src/opaque_closure.c +++ b/src/opaque_closure.c @@ -1,3 +1,5 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + #include "julia.h" #include "julia_internal.h" diff --git a/src/partr.c b/src/partr.c index c3de56b80cc92..d4208f660540e 100644 --- a/src/partr.c +++ b/src/partr.c @@ -60,7 +60,7 @@ typedef struct taskheap_tag { jl_mutex_t lock; jl_task_t **tasks; int32_t ntasks; - int16_t prio; + _Atomic(int16_t) prio; } taskheap_t; /* multiqueue parameters */ @@ -86,7 +86,7 @@ static inline void multiq_init(void) jl_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].prio, INT16_MAX); } unbias_cong(heap_p, &cong_unbias); } @@ -113,7 +113,7 @@ static inline void sift_down(taskheap_t *heap, int32_t idx) 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; @@ -142,9 +142,9 @@ static inline int multiq_insert(jl_task_t *task, int16_t priority) heaps[rn].tasks[heaps[rn].ntasks++] = task; sift_up(&heaps[rn], heaps[rn].ntasks-1); - int16_t prio = jl_atomic_load(&heaps[rn].prio); + int16_t prio = jl_atomic_load_relaxed(&heaps[rn].prio); if (task->prio < prio) - jl_atomic_store(&heaps[rn].prio, task->prio); + jl_atomic_store_relaxed(&heaps[rn].prio, task->prio); jl_mutex_unlock_nogc(&heaps[rn].lock); return 0; @@ -163,8 +163,8 @@ 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; @@ -172,7 +172,7 @@ static inline jl_task_t *multiq_deletemin(void) else if (prio1 == prio2 && prio1 == INT16_MAX) continue; if (jl_mutex_trylock_nogc(&heaps[rn1].lock)) { - if (prio1 == heaps[rn1].prio) + if (prio1 == jl_atomic_load_relaxed(&heaps[rn1].prio)) break; jl_mutex_unlock_nogc(&heaps[rn1].lock); } @@ -192,7 +192,7 @@ static inline jl_task_t *multiq_deletemin(void) sift_down(&heaps[rn1], 0); prio1 = heaps[rn1].tasks[0]->prio; } - jl_atomic_store(&heaps[rn1].prio, prio1); + jl_atomic_store_relaxed(&heaps[rn1].prio, prio1); jl_mutex_unlock_nogc(&heaps[rn1].lock); return task; @@ -247,7 +247,9 @@ void jl_threadfun(void *arg) jl_ptls_t ptls = jl_init_threadtls(targ->tid); void *stack_lo, *stack_hi; jl_init_stack_limits(0, &stack_lo, &stack_hi); - jl_init_root_task(ptls, stack_lo, stack_hi); + // warning: this changes `jl_current_task`, so be careful not to call that from this function + jl_task_t *ct = jl_init_root_task(ptls, stack_lo, stack_hi); + JL_GC_PROMISE_ROOTED(ct); jl_install_thread_signal_handler(ptls); // set up sleep mechanism for this thread @@ -262,7 +264,7 @@ void jl_threadfun(void *arg) free(targ); (void)jl_gc_unsafe_enter(ptls); - jl_finish_task(jl_current_task); // noreturn + jl_finish_task(ct); // noreturn } @@ -330,7 +332,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); @@ -394,7 +396,7 @@ 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; } @@ -479,7 +481,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; } @@ -512,7 +514,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/precompile.c b/src/precompile.c index 9f6fa1a79e8a6..0c5f1fa368a9d 100644 --- a/src/precompile.c +++ b/src/precompile.c @@ -344,8 +344,8 @@ static int precompile_enq_specialization_(jl_method_instance_t *mi, void *closur static int precompile_enq_all_specializations__(jl_typemap_entry_t *def, void *closure) { jl_method_t *m = def->func.method; - if (m->name == jl_symbol("__init__") && jl_is_dispatch_tupletype(m->sig)) { - // ensure `__init__()` gets strongly-hinted, specialized, and compiled + if ((m->name == jl_symbol("__init__") || m->ccallable) && jl_is_dispatch_tupletype(m->sig)) { + // ensure `__init__()` and @ccallables get strongly-hinted, specialized, and compiled jl_method_instance_t *mi = jl_specializations_get_linfo(m, m->sig, jl_emptysvec); jl_array_ptr_1d_push((jl_array_t*)closure, (jl_value_t*)mi); } 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..bf88ecff6b320 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. @@ -111,11 +111,11 @@ void jl_safepoint_init(void) int jl_safepoint_start_gc(void) { if (jl_n_threads == 1) { - jl_gc_running = 1; + jl_atomic_store_relaxed(&jl_gc_running, 1); 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 @@ -135,9 +135,9 @@ int jl_safepoint_start_gc(void) void jl_safepoint_end_gc(void) { - assert(jl_gc_running); + assert(jl_atomic_load_relaxed(&jl_gc_running)); if (jl_n_threads == 1) { - jl_gc_running = 0; + jl_atomic_store_relaxed(&jl_gc_running, 0); return; } jl_mutex_lock_nogc(&safepoint_lock); @@ -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 4743d8b37220a..d4641065ec2c6 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -37,8 +37,14 @@ void jl_shuffle_int_array_inplace(volatile uint64_t *carray, size_t size, uint64 JL_DLLEXPORT int jl_profile_is_buffer_full(void) { + // declare buffer full if there isn't enough room to take samples across all threads + #if defined(_OS_WINDOWS_) + uint64_t nthreads = 1; // windows only profiles the main thread + #else + uint64_t nthreads = jl_n_threads; + #endif // the `+ 6` is for the two block terminators `0` plus 4 metadata entries - return bt_size_cur + (JL_BT_MAX_ENTRY_SIZE + 1) + 6 > bt_size_max; + return bt_size_cur + (((JL_BT_MAX_ENTRY_SIZE + 1) + 6) * nthreads) > bt_size_max; } static uint64_t jl_last_sigint_trigger = 0; diff --git a/src/signals-mach.c b/src/signals-mach.c index 1fc1ff98f2e8c..faaa9e0fa8aa1 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -594,7 +594,7 @@ void *mach_profile_listener(void *arg) bt_data_prof[bt_size_cur++].uintptr = ptls->tid + 1; // store task id - bt_data_prof[bt_size_cur++].uintptr = ptls->current_task; + bt_data_prof[bt_size_cur++].uintptr = (uintptr_t)ptls->current_task; // store cpu cycle clock bt_data_prof[bt_size_cur++].uintptr = cycleclock(); diff --git a/src/signals-unix.c b/src/signals-unix.c index 460ef4d80518e..e5ebad6676ece 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -792,7 +792,7 @@ static void *signal_listener(void *arg) bt_data_prof[bt_size_cur++].uintptr = ptls->tid + 1; // store task id - bt_data_prof[bt_size_cur++].uintptr = ptls->current_task; + 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(); diff --git a/src/simplevector.c b/src/simplevector.c index 2b87eb92c41d1..fa21330b23ab4 100644 --- a/src/simplevector.c +++ b/src/simplevector.c @@ -7,7 +7,7 @@ #include "julia_internal.h" #include "julia_assert.h" -JL_DLLEXPORT jl_svec_t *(jl_svec)(size_t n, ...) +JL_DLLEXPORT jl_svec_t *(ijl_svec)(size_t n, ...) { va_list args; if (n == 0) return jl_emptysvec; 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/stackwalk.c b/src/stackwalk.c index 2994e653cb462..9436926903682 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -102,6 +102,13 @@ static int jl_unw_stepn(bt_cursor_t *cursor, jl_bt_element_t *bt_data, size_t *b // But sometimes the external unwinder doesn't check that. have_more_frames = 0; } + if (return_ip == 0) { + // The return address is clearly wrong, and while the unwinder + // might try to continue (by popping another stack frame), that + // likely won't work well, and it'll confuse the stack frame + // separator detection logic (double-NULL). + have_more_frames = 0; + } if (skip > 0) { skip--; from_signal_handler = 0; @@ -141,8 +148,9 @@ static int jl_unw_stepn(bt_cursor_t *cursor, jl_bt_element_t *bt_data, size_t *b if (!from_signal_handler) call_ip -= 1; // normal frame from_signal_handler = 0; - if (call_ip == JL_BT_NON_PTR_ENTRY) { + if (call_ip == JL_BT_NON_PTR_ENTRY || call_ip == 0) { // Never leave special marker in the bt data as it can corrupt the GC. + have_more_frames = 0; call_ip = 0; } jl_bt_element_t *bt_entry = bt_data + n; @@ -336,7 +344,7 @@ JL_DLLEXPORT jl_value_t *jl_get_excstack(jl_task_t* task, int include_bt, int ma { JL_TYPECHK(current_exceptions, task, (jl_value_t*)task); jl_task_t *ct = jl_current_task; - if (task != ct && task->_state == JL_TASK_STATE_RUNNABLE) { + if (task != ct && jl_atomic_load_relaxed(&task->_state) == JL_TASK_STATE_RUNNABLE) { jl_error("Inspecting the exception stack of a task which might " "be running concurrently isn't allowed."); } diff --git a/src/staticdata.c b/src/staticdata.c index f5892d4218e71..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); @@ -1041,14 +1041,14 @@ static void jl_write_values(jl_serializer_state *s) jl_typename_t *tn = (jl_typename_t*)v; jl_typename_t *newtn = (jl_typename_t*)&s->s->buf[reloc_offset]; if (tn->atomicfields != NULL) { - size_t nf = jl_svec_len(tn->names); + size_t nb = (jl_svec_len(tn->names) + 31) / 32 * sizeof(uint32_t); uintptr_t layout = LLT_ALIGN(ios_pos(s->const_data), sizeof(void*)); write_padding(s->const_data, layout - ios_pos(s->const_data)); // realign stream newtn->atomicfields = NULL; // relocation offset layout /= sizeof(void*); arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_typename_t, atomicfields))); // relocation location arraylist_push(&s->relocs_list, (void*)(((uintptr_t)ConstDataRef << RELOC_TAG_OFFSET) + layout)); // relocation target - ios_write(s->const_data, (char*)tn->atomicfields, nf); + ios_write(s->const_data, (char*)tn->atomicfields, nb); } } else if (((jl_datatype_t*)(jl_typeof(v)))->name == jl_idtable_typename) { diff --git a/src/subtype.c b/src/subtype.c index c3512eeb17dac..8079c81e3846a 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/END.h b/src/support/END.h index 090bbc02eeb1c..ec7b75d397b88 100644 --- a/src/support/END.h +++ b/src/support/END.h @@ -47,7 +47,6 @@ CNAME endp #undef CNAME -#undef HIDENAME #undef STR #undef XSTR #undef _START_ENTRY diff --git a/src/support/ENTRY.amd64.h b/src/support/ENTRY.amd64.h index b8049f0711f89..f18caa502ac5c 100644 --- a/src/support/ENTRY.amd64.h +++ b/src/support/ENTRY.amd64.h @@ -41,7 +41,6 @@ #define EXT_(csym) csym #define EXT(csym) EXT_(csym) #endif -#define HIDENAME(asmsym) .asmsym .text _START_ENTRY .globl EXT(CNAME) @@ -51,7 +50,6 @@ EXT(CNAME): #elif defined(_WIN32) #define EXT_(csym) csym #define EXT(csym) EXT_(csym) -#define HIDENAME(asmsym) .asmsym #ifndef _MSC_VER .intel_syntax noprefix diff --git a/src/support/ENTRY.i387.h b/src/support/ENTRY.i387.h index d80038671247a..cb7b93466cc3e 100644 --- a/src/support/ENTRY.i387.h +++ b/src/support/ENTRY.i387.h @@ -41,7 +41,6 @@ #define EXT_(csym) csym #define EXT(csym) EXT_(csym) #endif -#define HIDENAME(asmsym) .asmsym .text _START_ENTRY .globl EXT(CNAME) @@ -51,7 +50,6 @@ EXT(CNAME): #elif defined(_WIN32) #define EXT_(csym) _##csym #define EXT(csym) EXT_(csym) -#define HIDENAME(asmsym) .asmsym #ifndef _MSC_VER .intel_syntax diff --git a/src/support/_setjmp.win32.S b/src/support/_setjmp.win32.S index 441872dd4261a..33ed50ed3deab 100644 --- a/src/support/_setjmp.win32.S +++ b/src/support/_setjmp.win32.S @@ -56,8 +56,10 @@ * and update fs:[0xEOC] to contain the address of the stack */ -#define CNAME jl_setjmp +#define CNAME ijl_setjmp #include "ENTRY.i387.h" +.globl _jl_setjmp +_jl_setjmp: mov eax,DWORD PTR [esp+4] // arg 1 mov edx,DWORD PTR [esp+0] // rta mov DWORD PTR [eax+0],ebp @@ -73,8 +75,10 @@ #include "END.h" -#define CNAME jl_longjmp +#define CNAME ijl_longjmp #include "ENTRY.i387.h" +.globl _jl_longjmp +_jl_longjmp: mov edx,DWORD PTR [esp+4] // arg 1 mov eax,DWORD PTR [esp+8] // arg 2 mov ebp,DWORD PTR [edx+24] // seh registration @@ -87,14 +91,16 @@ mov ebp,DWORD PTR [edx+0] mov DWORD PTR [esp],ecx test eax,eax - jne a + jne 1f inc eax -a: ret // jmp ecx +1: ret // jmp ecx #include "END.h" -#define CNAME jl_swapcontext +#define CNAME ijl_swapcontext #include "ENTRY.i387.h" +.globl _jl_swapcontext +_jl_swapcontext: mov eax,DWORD PTR [esp+4] // save stack registers mov edx,DWORD PTR fs:[8] // stack top (low) @@ -118,8 +124,10 @@ a: ret // jmp ecx #include "END.h" -#define CNAME jl_setcontext +#define CNAME ijl_setcontext #include "ENTRY.i387.h" +.globl _jl_setcontext +_jl_setcontext: mov eax,DWORD PTR [esp+4] // restore stack registers mov edx,DWORD PTR [eax+0] diff --git a/src/support/_setjmp.win64.S b/src/support/_setjmp.win64.S index cb512cfe4ab3e..f5e5c69c7cff3 100644 --- a/src/support/_setjmp.win64.S +++ b/src/support/_setjmp.win64.S @@ -6,8 +6,10 @@ * and update gs:[0x1478] to contain the address of the stack */ -#define CNAME jl_setjmp +#define CNAME ijl_setjmp #include "ENTRY.amd64.h" +.globl jl_setjmp +jl_setjmp: mov rdx,QWORD PTR [rsp] // rta mov rax,QWORD PTR gs:[0] // SEH mov QWORD PTR [rcx+0],rax @@ -37,8 +39,10 @@ #include "END.h" -#define CNAME jl_longjmp +#define CNAME ijl_longjmp #include "ENTRY.amd64.h" +.globl jl_longjmp +jl_longjmp: mov rax,QWORD PTR [rcx+0] mov rbx,QWORD PTR [rcx+8] mov rsp,QWORD PTR [rcx+16] @@ -63,15 +67,17 @@ mov QWORD PTR gs:[0],rax mov eax,edx // move arg2 to return test eax,eax - jne a + jne 1f inc eax -a: mov QWORD PTR [rsp],r8 +1: mov QWORD PTR [rsp],r8 ret #include "END.h" -#define CNAME jl_swapcontext +#define CNAME ijl_swapcontext #include "ENTRY.amd64.h" +.globl jl_swapcontext +jl_swapcontext: // save stack registers mov r8,QWORD PTR gs:[16] // stack top (low) mov rax,QWORD PTR gs:[8] // stack bottom (high) @@ -109,8 +115,10 @@ a: mov QWORD PTR [rsp],r8 #include "END.h" -#define CNAME jl_setcontext +#define CNAME ijl_setcontext #include "ENTRY.amd64.h" +.globl jl_setcontext +jl_setcontext: // restore stack registers mov r8,QWORD PTR [rcx+0] mov rax,QWORD PTR [rcx+8] 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/support/dtypes.h b/src/support/dtypes.h index 46780e8e64d4a..7f2a7b2e6ad4a 100644 --- a/src/support/dtypes.h +++ b/src/support/dtypes.h @@ -76,17 +76,6 @@ #define JL_DLLIMPORT #endif -/* - * Debug builds include `-fstack-protector`, which adds a bit of extra prologue to - * functions, even naked ones. We don't want that, but we also don't want the - * compiler warnings when `no_stack_protector` has no effect. - */ -#ifdef JL_DEBUG_BUILD -#define JL_NAKED __attribute__ ((naked,no_stack_protector)) -#else -#define JL_NAKED __attribute__ ((naked)) -#endif - #ifdef _OS_LINUX_ #include #define LITTLE_ENDIAN __LITTLE_ENDIAN @@ -369,7 +358,7 @@ STATIC_INLINE void *malloc_s(size_t sz) JL_NOTSAFEPOINT { #ifdef _OS_WINDOWS_ DWORD last_error = GetLastError(); #endif - void *p = malloc(sz); + void *p = malloc(sz == 0 ? 1 : sz); if (p == NULL) { perror("(julia) malloc"); abort(); @@ -386,7 +375,7 @@ STATIC_INLINE void *realloc_s(void *p, size_t sz) JL_NOTSAFEPOINT { #ifdef _OS_WINDOWS_ DWORD last_error = GetLastError(); #endif - p = realloc(p, sz); + p = realloc(p, sz == 0 ? 1 : sz); if (p == NULL) { perror("(julia) realloc"); abort(); diff --git a/src/symbol.c b/src/symbol.c index fe8e975f8f525..303f5a0ab57fb 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) @@ -41,16 +41,17 @@ static jl_sym_t *mk_symbol(const char *str, size_t len) JL_NOTSAFEPOINT sym = (jl_sym_t*)jl_valueof(tag); // set to old marked so that we won't look at it in the GC or write barrier. tag->header = ((uintptr_t)jl_symbol_type) | GC_OLD_MARKED; - sym->left = sym->right = NULL; + jl_atomic_store_relaxed(&sym->left, NULL); + jl_atomic_store_relaxed(&sym->right, NULL); sym->hash = hash_symbol(str, len); memcpy(jl_symbol_name(sym), str, len); jl_symbol_name(sym)[len] = 0; 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 +69,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,19 +78,19 @@ 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); // Someone might have updated it, check and look up again - if (*slot != NULL && (node = symtab_lookup(slot, str, len, &slot))) { + if (jl_atomic_load_relaxed(slot) != NULL && (node = symtab_lookup(slot, str, len, &slot))) { JL_UNLOCK_NOGC(&gc_perm_lock); return node; } @@ -119,12 +120,12 @@ JL_DLLEXPORT jl_sym_t *jl_symbol_n(const char *str, size_t len) JL_DLLEXPORT jl_sym_t *jl_get_root_symbol(void) { - return symtab; + return jl_atomic_load_relaxed(&symtab); } -static uint32_t gs_ctr = 0; // TODO: per-thread -uint32_t jl_get_gs_ctr(void) { return gs_ctr; } -void jl_set_gs_ctr(uint32_t ctr) { gs_ctr = ctr; } +static _Atomic(uint32_t) gs_ctr = 0; // TODO: per-module? +uint32_t jl_get_gs_ctr(void) { return jl_atomic_load_relaxed(&gs_ctr); } +void jl_set_gs_ctr(uint32_t ctr) { jl_atomic_store_relaxed(&gs_ctr, ctr); } JL_DLLEXPORT jl_sym_t *jl_gensym(void) { diff --git a/src/task.c b/src/task.c index d133b24fc727b..83b9f048f6462 100644 --- a/src/task.c +++ b/src/task.c @@ -189,13 +189,13 @@ 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) { jl_task_t *ct = jl_current_task; JL_SIGATOMIC_BEGIN(); - if (t->_isexception) + if (jl_atomic_load_relaxed(&t->_isexception)) jl_atomic_store_release(&t->_state, JL_TASK_STATE_FAILED); else jl_atomic_store_release(&t->_state, JL_TASK_STATE_DONE); @@ -240,7 +240,7 @@ JL_DLLEXPORT void *jl_task_stack_buffer(jl_task_t *task, size_t *size, int *ptid jl_ptls_t ptls2 = task->ptls; *ptid = -1; if (ptls2) { - *ptid = task->tid; + *ptid = jl_atomic_load_relaxed(&task->tid); #ifdef COPY_STACKS if (task->copy_stack) { *size = ptls2->stacksize; @@ -345,7 +345,7 @@ static void ctx_switch(jl_task_t *lastt) } #endif - int killed = lastt->_state != JL_TASK_STATE_RUNNABLE; + int killed = jl_atomic_load_relaxed(&lastt->_state) != JL_TASK_STATE_RUNNABLE; if (!t->started && !t->copy_stack) { // may need to allocate the stack if (t->stkbuf == NULL) { @@ -482,7 +482,7 @@ JL_DLLEXPORT void jl_switch(void) jl_error("task switch not allowed from inside gc finalizer"); if (ptls->in_pure_callback) jl_error("task switch not allowed from inside staged nor pure functions"); - if (!jl_set_task_tid(t, ptls->tid)) // manually yielding to a task + if (!jl_set_task_tid(t, jl_atomic_load_relaxed(&ct->tid))) // manually yielding to a task jl_error("cannot switch to task running on another thread"); // Store old values on the stack and reset @@ -505,7 +505,7 @@ JL_DLLEXPORT void jl_switch(void) t = ptls->previous_task; ptls->previous_task = NULL; assert(t != ct); - assert(t->tid == ptls->tid); + assert(jl_atomic_load_relaxed(&t->tid) == ptls->tid); if (!t->sticky && !t->copy_stack) jl_atomic_store_release(&t->tid, -1); #else @@ -560,12 +560,6 @@ static void JL_NORETURN throw_internal(jl_task_t *ct, jl_value_t *exception JL_M assert(!jl_get_safe_restore()); jl_ptls_t ptls = ct->ptls; ptls->io_wait = 0; - // @time needs its compile timer disabled on error, - // and cannot use a try-finally as it would break scope for assignments - // We blindly disable compilation time tracking here, for all running Tasks, even though - // it may cause some incorrect measurements. This is a known bug, and is being tracked - // here: https://github.com/JuliaLang/julia/pull/39138 - jl_atomic_store_relaxed(&jl_measure_compile_time_enabled, 0); JL_GC_PUSH1(&exception); jl_gc_unsafe_enter(ptls); if (exception) { @@ -736,11 +730,11 @@ 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; - t->_isexception = 0; + jl_atomic_store_relaxed(&t->_isexception, 0); // Inherit logger state from parent task t->logstate = ct->logstate; // Fork task-local random state from parent @@ -752,9 +746,9 @@ 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 ? jl_atomic_load_relaxed(&ct->tid) : -1); // copy_stacks are always pinned since they can't be moved t->ptls = NULL; - t->world_age = 0; + t->world_age = ct->world_age; #ifdef COPY_STACKS if (!t->copy_stack) { @@ -849,7 +843,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 @@ -867,7 +861,7 @@ CFI_NORETURN #endif ct->started = 1; - if (ct->_isexception) { + if (jl_atomic_load_relaxed(&ct->_isexception)) { record_backtrace(ptls, 0); jl_push_excstack(&ct->excstack, ct->result, ptls->bt_data, ptls->bt_size); @@ -880,12 +874,11 @@ CFI_NORETURN jl_sigint_safepoint(ptls); } JL_TIMING(ROOT); - ct->world_age = jl_world_counter; res = jl_apply(&ct->start, 1); } JL_CATCH { res = jl_current_exception(); - ct->_isexception = 1; + jl_atomic_store_relaxed(&ct->_isexception, 1); goto skip_pop_exception; } skip_pop_exception:; @@ -949,7 +942,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 @@ -1258,7 +1251,7 @@ static char *jl_alloc_fiber(jl_ucontext_t *t, size_t *ssize, jl_task_t *owner) J #endif // Initialize a root task using the given stack. -void jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) +jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) { assert(ptls->root_task == NULL); // We need `gcstack` in `Task` to allocate Julia objects; *including* the `Task` type. @@ -1298,16 +1291,16 @@ void 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; - ct->_isexception = 0; + jl_atomic_store_relaxed(&ct->_isexception, 0); ct->logstate = jl_nothing; 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 @@ -1334,13 +1327,14 @@ void jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) #endif if (jl_setjmp(ptls->copy_stack_ctx.uc_mcontext, 0)) start_task(); // sanitizer_finish_switch_fiber is part of start_task - return; + return ct; } ssize = JL_STACK_SIZE; char *stkbuf = jl_alloc_fiber(&ptls->base_ctx, &ssize, NULL); ptls->stackbase = stkbuf + ssize; ptls->stacksize = ssize; #endif + return ct; } JL_DLLEXPORT int jl_is_task_started(jl_task_t *t) JL_NOTSAFEPOINT @@ -1350,7 +1344,7 @@ JL_DLLEXPORT int jl_is_task_started(jl_task_t *t) JL_NOTSAFEPOINT JL_DLLEXPORT int16_t jl_get_task_tid(jl_task_t *t) JL_NOTSAFEPOINT { - return t->tid; + return jl_atomic_load_relaxed(&t->tid); } diff --git a/src/threading.c b/src/threading.c index ffe53c07b45ee..5a9d660329412 100644 --- a/src/threading.c +++ b/src/threading.c @@ -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,15 +287,15 @@ 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 // type of the thread id. JL_DLLEXPORT int16_t jl_threadid(void) { - return jl_current_task->tid; + return jl_atomic_load_relaxed(&jl_current_task->tid); } jl_ptls_t jl_init_threadtls(int16_t tid) @@ -314,7 +314,7 @@ jl_ptls_t jl_init_threadtls(int16_t tid) } #endif ptls->tid = tid; - ptls->gc_state = 0; // GC unsafe + jl_atomic_store_relaxed(&ptls->gc_state, 0); // GC unsafe // Conditionally initialize the safepoint address. See comment in // `safepoint.c` if (tid == 0) { @@ -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 } diff --git a/src/typemap.c b/src/typemap.c index 58dd2b8b13069..dfa8ac67f6abc 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; + if (a != jl_atomic_load_relaxed(cache)) { + 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]); @@ -402,7 +402,7 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, if (t == jl_nothing || t == NULL) continue; if (tparam & 2) { - jl_typemap_t *ml = data[i + 1]; + jl_typemap_t *ml = jl_atomic_load_relaxed(&data[i + 1]); JL_GC_PROMISE_ROOTED(ml); if (ty == (jl_value_t*)jl_any_type || // easy case: Any always matches tname_intersection((jl_datatype_t*)ty, (jl_typename_t*)t, height)) { @@ -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,10 +1034,10 @@ 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]; + jl_typemap_t *ml_or_cache = jl_atomic_load_relaxed(&data[i]); if (ml_or_cache == NULL || ml_or_cache == jl_nothing) continue; jl_typemap_entry_t *ml = jl_typemap_assoc_exact(ml_or_cache, arg1, args, n, offs + 1, world); @@ -1082,7 +1082,7 @@ static unsigned jl_typemap_list_count_locked(jl_typemap_entry_t *ml) JL_NOTSAFEP unsigned count = 0; while (ml != (void*)jl_nothing) { count++; - ml = ml->next; + ml = jl_atomic_load_relaxed(&ml->next); } return count; } @@ -1095,12 +1095,12 @@ static jl_typemap_level_t *jl_new_typemap_level(void) jl_typemap_level_t *cache = (jl_typemap_level_t*)jl_gc_alloc(ct->ptls, sizeof(jl_typemap_level_t), jl_typemap_level_type); - cache->arg1 = (jl_array_t*)jl_an_empty_vec_any; - cache->targ = (jl_array_t*)jl_an_empty_vec_any; - cache->name1 = (jl_array_t*)jl_an_empty_vec_any; - cache->tname = (jl_array_t*)jl_an_empty_vec_any; - cache->linear = (jl_typemap_entry_t*)jl_nothing; - cache->any = jl_nothing; + jl_atomic_store_relaxed(&cache->arg1, (jl_array_t*)jl_an_empty_vec_any); + jl_atomic_store_relaxed(&cache->targ, (jl_array_t*)jl_an_empty_vec_any); + jl_atomic_store_relaxed(&cache->name1, (jl_array_t*)jl_an_empty_vec_any); + jl_atomic_store_relaxed(&cache->tname, (jl_array_t*)jl_an_empty_vec_any); + jl_atomic_store_relaxed(&cache->linear, (jl_typemap_entry_t*)jl_nothing); + jl_atomic_store_relaxed(&cache->any, jl_nothing); return cache; } @@ -1111,8 +1111,9 @@ static jl_typemap_level_t *jl_method_convert_list_to_cache( jl_typemap_entry_t *next = NULL; JL_GC_PUSH3(&cache, &next, &ml); while (ml != (void*)jl_nothing) { - next = ml->next; - ml->next = (jl_typemap_entry_t*)jl_nothing; + next = jl_atomic_load_relaxed(&ml->next); + jl_atomic_store_relaxed(&ml->next, (jl_typemap_entry_t*)jl_nothing); + // n.b. this is being done concurrently with lookups! // TODO: is it safe to be doing this concurrently with lookups? jl_typemap_level_insert_(map, cache, ml, offs); ml = next; @@ -1122,56 +1123,58 @@ 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) break; pml = &l->next; parent = (jl_value_t*)l; - l = l->next; + l = jl_atomic_load_relaxed(&l->next); } - newrec->next = l; - jl_gc_wb(newrec, newrec->next); + jl_atomic_store_relaxed(&newrec->next, l); + jl_gc_wb(newrec, l); jl_atomic_store_release(pml, newrec); jl_gc_wb(parent, newrec); } 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 +1279,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 +1288,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/.gitignore b/stdlib/.gitignore index 891eda58c689d..d90aaa993d049 100644 --- a/stdlib/.gitignore +++ b/stdlib/.gitignore @@ -15,5 +15,7 @@ /NetworkOptions /SuiteSparse-* /SuiteSparse +/SHA-* +/SHA /*_jll/StdlibArtifacts.toml /*/Manifest.toml diff --git a/stdlib/Distributed/src/cluster.jl b/stdlib/Distributed/src/cluster.jl index ebe4cac0f3bbe..cccc377e6790f 100644 --- a/stdlib/Distributed/src/cluster.jl +++ b/stdlib/Distributed/src/cluster.jl @@ -95,13 +95,14 @@ end @enum WorkerState W_CREATED W_CONNECTED W_TERMINATING W_TERMINATED mutable struct Worker id::Int - del_msgs::Array{Any,1} + msg_lock::Threads.ReentrantLock # Lock for del_msgs, add_msgs, and gcflag + del_msgs::Array{Any,1} # XXX: Could del_msgs and add_msgs be Channels? add_msgs::Array{Any,1} - gcflag::Bool - state::WorkerState - c_state::Condition # wait for state changes - ct_time::Float64 # creation time - conn_func::Any # used to setup connections lazily + @atomic gcflag::Bool + @atomic state::WorkerState + c_state::Threads.Condition # wait for state changes, lock for state + ct_time::Float64 # creation time + conn_func::Any # used to setup connections lazily r_stream::IO w_stream::IO @@ -133,7 +134,7 @@ mutable struct Worker if haskey(map_pid_wrkr, id) return map_pid_wrkr[id] end - w=new(id, [], [], false, W_CREATED, Condition(), time(), conn_func) + w=new(id, Threads.ReentrantLock(), [], [], false, W_CREATED, Threads.Condition(), time(), conn_func) w.initialized = Event() register_worker(w) w @@ -143,8 +144,10 @@ mutable struct Worker end function set_worker_state(w, state) - w.state = state - notify(w.c_state; all=true) + lock(w.c_state) do + @atomic w.state = state + notify(w.c_state; all=true) + end end function check_worker_state(w::Worker) @@ -169,6 +172,7 @@ function check_worker_state(w::Worker) wait_for_conn(w) end end + return nothing end exec_conn_func(id::Int) = exec_conn_func(worker_from_id(id)::Worker) @@ -190,9 +194,17 @@ function wait_for_conn(w) timeout = worker_timeout() - (time() - w.ct_time) timeout <= 0 && error("peer $(w.id) has not connected to $(myid())") - @async (sleep(timeout); notify(w.c_state; all=true)) - wait(w.c_state) - w.state === W_CREATED && error("peer $(w.id) didn't connect to $(myid()) within $timeout seconds") + T = Threads.@spawn begin + sleep($timeout) + lock(w.c_state) do + notify(w.c_state; all=true) + end + end + errormonitor(T) + lock(w.c_state) do + wait(w.c_state) + w.state === W_CREATED && error("peer $(w.id) didn't connect to $(myid()) within $timeout seconds") + end end nothing end @@ -471,6 +483,10 @@ function addprocs_locked(manager::ClusterManager; kwargs...) # The `launch` method should add an object of type WorkerConfig for every # worker launched. It provides information required on how to connect # to it. + + # FIXME: launched should be a Channel, launch_ntfy should be a Threads.Condition + # but both are part of the public interface. This means we currently can't use + # `Threads.@spawn` in the code below. launched = WorkerConfig[] launch_ntfy = Condition() @@ -483,7 +499,10 @@ function addprocs_locked(manager::ClusterManager; kwargs...) while true if isempty(launched) istaskdone(t_launch) && break - @async (sleep(1); notify(launch_ntfy)) + @async begin + sleep(1) + notify(launch_ntfy) + end wait(launch_ntfy) end @@ -636,7 +655,12 @@ function create_worker(manager, wconfig) # require the value of config.connect_at which is set only upon connection completion for jw in PGRP.workers if (jw.id != 1) && (jw.id < w.id) - (jw.state === W_CREATED) && wait(jw.c_state) + # wait for wl to join + if jw.state === W_CREATED + lock(jw.c_state) do + wait(jw.c_state) + end + end push!(join_list, jw) end end @@ -659,7 +683,12 @@ function create_worker(manager, wconfig) end for wl in wlist - (wl.state === W_CREATED) && wait(wl.c_state) + lock(wl.c_state) do + if wl.state === W_CREATED + # wait for wl to join + wait(wl.c_state) + end + end push!(join_list, wl) end end @@ -676,7 +705,11 @@ function create_worker(manager, wconfig) @async manage(w.manager, w.id, w.config, :register) # wait for rr_ntfy_join with timeout timedout = false - @async (sleep($timeout); timedout = true; put!(rr_ntfy_join, 1)) + @async begin + sleep($timeout) + timedout = true + put!(rr_ntfy_join, 1) + end wait(rr_ntfy_join) if timedout error("worker did not connect within $timeout seconds") diff --git a/stdlib/Distributed/src/macros.jl b/stdlib/Distributed/src/macros.jl index f96338b69e9fb..0a62fdd5439f0 100644 --- a/stdlib/Distributed/src/macros.jl +++ b/stdlib/Distributed/src/macros.jl @@ -1,14 +1,10 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -let nextidx = 0 +let nextidx = Threads.Atomic{Int}(0) global nextproc function nextproc() - p = -1 - if p == -1 - p = workers()[(nextidx % nworkers()) + 1] - nextidx += 1 - end - p + idx = Threads.atomic_add!(nextidx, 1) + return workers()[(idx % nworkers()) + 1] end end diff --git a/stdlib/Distributed/src/managers.jl b/stdlib/Distributed/src/managers.jl index 91a27aa95cb98..bd5a979603514 100644 --- a/stdlib/Distributed/src/managers.jl +++ b/stdlib/Distributed/src/managers.jl @@ -163,7 +163,7 @@ function launch(manager::SSHManager, params::Dict, launched::Array, launch_ntfy: # Wait for all launches to complete. @sync for (i, (machine, cnt)) in enumerate(manager.machines) let machine=machine, cnt=cnt - @async try + @async try launch_on_machine(manager, $machine, $cnt, params, launched, launch_ntfy) catch e print(stderr, "exception launching on machine $(machine) : $(e)\n") diff --git a/stdlib/Distributed/src/messages.jl b/stdlib/Distributed/src/messages.jl index 47f70e044a2c0..fe3e5ab90b028 100644 --- a/stdlib/Distributed/src/messages.jl +++ b/stdlib/Distributed/src/messages.jl @@ -126,23 +126,30 @@ function flush_gc_msgs(w::Worker) if !isdefined(w, :w_stream) return end - w.gcflag = false - new_array = Any[] - msgs = w.add_msgs - w.add_msgs = new_array - if !isempty(msgs) - remote_do(add_clients, w, msgs) - end + add_msgs = nothing + del_msgs = nothing + @lock w.msg_lock begin + if !w.gcflag # No work needed for this worker + return + end + @atomic w.gcflag = false + if !isempty(w.add_msgs) + add_msgs = w.add_msgs + w.add_msgs = Any[] + end - # del_msgs gets populated by finalizers, so be very careful here about ordering of allocations - # XXX: threading requires this to be atomic - new_array = Any[] - msgs = w.del_msgs - w.del_msgs = new_array - if !isempty(msgs) - #print("sending delete of $msgs\n") - remote_do(del_clients, w, msgs) + if !isempty(w.del_msgs) + del_msgs = w.del_msgs + w.del_msgs = Any[] + end + end + if add_msgs !== nothing + remote_do(add_clients, w, add_msgs) + end + if del_msgs !== nothing + remote_do(del_clients, w, del_msgs) end + return end # Boundary inserted between messages on the wire, used for recovering @@ -187,7 +194,7 @@ end function flush_gc_msgs() try for w in (PGRP::ProcessGroup).workers - if isa(w,Worker) && w.gcflag && (w.state == W_CONNECTED) + if isa(w,Worker) && (w.state == W_CONNECTED) && w.gcflag flush_gc_msgs(w) end end diff --git a/stdlib/Distributed/src/process_messages.jl b/stdlib/Distributed/src/process_messages.jl index 732b972858dc9..a093ffff01d34 100644 --- a/stdlib/Distributed/src/process_messages.jl +++ b/stdlib/Distributed/src/process_messages.jl @@ -57,7 +57,7 @@ function showerror(io::IO, re::RemoteException) showerror(io, re.captured) end -function run_work_thunk(thunk, print_error) +function run_work_thunk(thunk::Function, print_error::Bool) local result try result = thunk() @@ -271,11 +271,11 @@ function process_hdr(s, validate_cookie) end function handle_msg(msg::CallMsg{:call}, header, r_stream, w_stream, version) - schedule_call(header.response_oid, ()->msg.f(msg.args...; msg.kwargs...)) + schedule_call(header.response_oid, ()->invokelatest(msg.f, msg.args...; msg.kwargs...)) end function handle_msg(msg::CallMsg{:call_fetch}, header, r_stream, w_stream, version) errormonitor(@async begin - v = run_work_thunk(()->msg.f(msg.args...; msg.kwargs...), false) + v = run_work_thunk(()->invokelatest(msg.f, msg.args...; msg.kwargs...), false) if isa(v, SyncTake) try deliver_result(w_stream, :call_fetch, header.notify_oid, v.v) @@ -291,14 +291,14 @@ end function handle_msg(msg::CallWaitMsg, header, r_stream, w_stream, version) errormonitor(@async begin - rv = schedule_call(header.response_oid, ()->msg.f(msg.args...; msg.kwargs...)) + rv = schedule_call(header.response_oid, ()->invokelatest(msg.f, msg.args...; msg.kwargs...)) deliver_result(w_stream, :call_wait, header.notify_oid, fetch(rv.c)) nothing end) end function handle_msg(msg::RemoteDoMsg, header, r_stream, w_stream, version) - errormonitor(@async run_work_thunk(()->msg.f(msg.args...; msg.kwargs...), true)) + errormonitor(@async run_work_thunk(()->invokelatest(msg.f, msg.args...; msg.kwargs...), true)) end function handle_msg(msg::ResultMsg, header, r_stream, w_stream, version) diff --git a/stdlib/Distributed/src/remotecall.jl b/stdlib/Distributed/src/remotecall.jl index fabcf10686068..75caf7f3065b7 100644 --- a/stdlib/Distributed/src/remotecall.jl +++ b/stdlib/Distributed/src/remotecall.jl @@ -256,14 +256,27 @@ function del_clients(pairs::Vector) end end -const any_gc_flag = Condition() +# The task below is coalescing the `flush_gc_msgs` call +# across multiple producers, see `send_del_client`, +# and `send_add_client`. +# XXX: Is this worth the additional complexity? +# `flush_gc_msgs` has to iterate over all connected workers. +const any_gc_flag = Threads.Condition() function start_gc_msgs_task() - errormonitor(@async while true - wait(any_gc_flag) - flush_gc_msgs() - end) + errormonitor( + Threads.@spawn begin + while true + lock(any_gc_flag) do + # this might miss events + wait(any_gc_flag) + end + flush_gc_msgs() # handles throws internally + end + end + ) end +# Function can be called within a finalizer function send_del_client(rr) if rr.where == myid() del_client(rr) @@ -281,11 +294,27 @@ function send_del_client_no_lock(rr) end end +function publish_del_msg!(w::Worker, msg) + lock(w.msg_lock) do + push!(w.del_msgs, msg) + @atomic w.gcflag = true + end + lock(any_gc_flag) do + notify(any_gc_flag) + end +end + function process_worker(rr) w = worker_from_id(rr.where)::Worker - push!(w.del_msgs, (remoteref_id(rr), myid())) - w.gcflag = true - notify(any_gc_flag) + msg = (remoteref_id(rr), myid()) + + # Needs to aquire a lock on the del_msg queue + T = Threads.@spawn begin + publish_del_msg!($w, $msg) + end + Base.errormonitor(T) + + return end function add_client(id, client) @@ -310,9 +339,13 @@ function send_add_client(rr::AbstractRemoteRef, i) # to the processor that owns the remote ref. it will add_client # itself inside deserialize(). w = worker_from_id(rr.where) - push!(w.add_msgs, (remoteref_id(rr), i)) - w.gcflag = true - notify(any_gc_flag) + lock(w.msg_lock) do + push!(w.add_msgs, (remoteref_id(rr), i)) + @atomic w.gcflag = true + end + lock(any_gc_flag) do + notify(any_gc_flag) + end end end @@ -370,10 +403,7 @@ end # make a thunk to call f on args in a way that simulates what would happen if # the function were sent elsewhere function local_remotecall_thunk(f, args, kwargs) - if isempty(args) && isempty(kwargs) - return f - end - return ()->f(args...; kwargs...) + return ()->invokelatest(f, args...; kwargs...) end function remotecall(f, w::LocalProcess, args...; kwargs...) diff --git a/stdlib/Distributed/test/distributed_exec.jl b/stdlib/Distributed/test/distributed_exec.jl index 749c18f6b61f0..3b99afac8cc15 100644 --- a/stdlib/Distributed/test/distributed_exec.jl +++ b/stdlib/Distributed/test/distributed_exec.jl @@ -1696,4 +1696,5 @@ include("splitrange.jl") # Run topology tests last after removing all workers, since a given # cluster at any time only supports a single topology. rmprocs(workers()) +include("threads.jl") include("topology.jl") diff --git a/stdlib/Distributed/test/threads.jl b/stdlib/Distributed/test/threads.jl new file mode 100644 index 0000000000000..57d99b7ea056c --- /dev/null +++ b/stdlib/Distributed/test/threads.jl @@ -0,0 +1,63 @@ +using Test +using Distributed, Base.Threads +using Base.Iterators: product + +exeflags = ("--startup-file=no", + "--check-bounds=yes", + "--depwarn=error", + "--threads=2") + +function call_on(f, wid, tid) + remotecall(wid) do + t = Task(f) + ccall(:jl_set_task_tid, Cvoid, (Any, Cint), t, tid - 1) + schedule(t) + @assert threadid(t) == tid + t + end +end + +# Run function on process holding the data to only serialize the result of f. +# This becomes useful for things that cannot be serialized (e.g. running tasks) +# or that would be unnecessarily big if serialized. +fetch_from_owner(f, rr) = remotecall_fetch(f ∘ fetch, rr.where, rr) + +isdone(rr) = fetch_from_owner(istaskdone, rr) +isfailed(rr) = fetch_from_owner(istaskfailed, rr) + +@testset "RemoteChannel allows put!/take! from thread other than 1" begin + ws = ts = product(1:2, 1:2) + @testset "from worker $w1 to $w2 via 1" for (w1, w2) in ws + @testset "from thread $w1.$t1 to $w2.$t2" for (t1, t2) in ts + # We want (the default) lazyness, so that we wait for `Worker.c_state`! + procs_added = addprocs(2; exeflags, lazy=true) + @everywhere procs_added using Base.Threads + + p1 = procs_added[w1] + p2 = procs_added[w2] + chan_id = first(procs_added) + chan = RemoteChannel(chan_id) + send = call_on(p1, t1) do + put!(chan, nothing) + end + recv = call_on(p2, t2) do + take!(chan) + end + + # Wait on the spawned tasks on the owner + @sync begin + Threads.@spawn fetch_from_owner(wait, recv) + Threads.@spawn fetch_from_owner(wait, send) + end + + # Check the tasks + @test isdone(send) + @test isdone(recv) + + @test !isfailed(send) + @test !isfailed(recv) + + rmprocs(procs_added) + end + end +end diff --git a/stdlib/GMP_jll/Project.toml b/stdlib/GMP_jll/Project.toml index a4d989a410199..0fc262e562da7 100644 --- a/stdlib/GMP_jll/Project.toml +++ b/stdlib/GMP_jll/Project.toml @@ -1,10 +1,10 @@ name = "GMP_jll" uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" -version = "6.2.1+0" +version = "6.2.1+1" [deps] -Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" +Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" [compat] julia = "1.6" diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 8c48a9a757959..b9737bf36d0c5 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -586,9 +586,6 @@ function __init__() BLAS.lbt_forward(liblapack_path) end BLAS.check() - Threads.resize_nthreads!(Abuf) - Threads.resize_nthreads!(Bbuf) - Threads.resize_nthreads!(Cbuf) catch ex Base.showerror_nostdio(ex, "WARNING: Error during initialization of module LinearAlgebra") end diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 1a75c6a9287f0..e44def613dea3 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -735,7 +735,7 @@ function ldiv!(A::Bidiagonal, b::AbstractVector) end if N == 0 - return x + return b end @inbounds begin diff --git a/stdlib/LinearAlgebra/src/lbt.jl b/stdlib/LinearAlgebra/src/lbt.jl index b1a2dc24b3449..26b3a1210a3f9 100644 --- a/stdlib/LinearAlgebra/src/lbt.jl +++ b/stdlib/LinearAlgebra/src/lbt.jl @@ -1,3 +1,5 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + ## This file contains libblastrampoline-specific APIs # Keep these in sync with `src/libblastrampoline_internal.h` diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 2b315b0cf6080..0cbfeaf9ed3ea 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -726,10 +726,6 @@ function generic_matmatmul(tA, tB, A::AbstractVecOrMat{T}, B::AbstractMatrix{S}) end const tilebufsize = 10800 # Approximately 32k/3 -# per-thread arrays of buffers resized by __init__ if needed -const Abuf = [Vector{UInt8}(undef, tilebufsize)] -const Bbuf = [Vector{UInt8}(undef, tilebufsize)] -const Cbuf = [Vector{UInt8}(undef, tilebufsize)] function generic_matmatmul!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix, _add::MulAddMul=MulAddMul()) @@ -775,9 +771,8 @@ function _generic_matmatmul!(C::AbstractVecOrMat{R}, tA, tB, A::AbstractVecOrMat @inbounds begin if tile_size > 0 sz = (tile_size, tile_size) - # FIXME: This code is completely invalid!!! - Atile = unsafe_wrap(Array, convert(Ptr{T}, pointer(Abuf[Threads.threadid()])), sz) - Btile = unsafe_wrap(Array, convert(Ptr{S}, pointer(Bbuf[Threads.threadid()])), sz) + Atile = Array{T}(undef, sz) + Btile = Array{S}(undef, sz) z1 = zero(A[1, 1]*B[1, 1] + A[1, 1]*B[1, 1]) z = convert(promote_type(typeof(z1), R), z1) @@ -797,8 +792,7 @@ function _generic_matmatmul!(C::AbstractVecOrMat{R}, tA, tB, A::AbstractVecOrMat end end else - # FIXME: This code is completely invalid!!! - Ctile = unsafe_wrap(Array, convert(Ptr{R}, pointer(Cbuf[Threads.threadid()])), sz) + Ctile = Array{R}(undef, sz) for jb = 1:tile_size:nB jlim = min(jb+tile_size-1,nB) jlen = jlim-jb+1 diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index d9efdc1fd3ee6..274ca24fe018d 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -269,6 +269,11 @@ Random.seed!(1) end end end + zdv = Vector{elty}(undef, 0) + zev = Vector{elty}(undef, 0) + zA = Bidiagonal(zdv, zev, :U) + zb = Vector{elty}(undef, 0) + @test ldiv!(zA, zb) === zb end if elty <: BlasReal @@ -549,6 +554,14 @@ end B = Bidiagonal(dv, ev, uplo) @test dot(x, B, y) ≈ dot(B'x, y) ≈ dot(x, Matrix(B), y) end + dv = Vector{elty}(undef, 0) + ev = Vector{elty}(undef, 0) + x = Vector{elty}(undef, 0) + y = Vector{elty}(undef, 0) + for uplo in (:U, :L) + B = Bidiagonal(dv, ev, uplo) + @test dot(x, B, y) ≈ dot(zero(elty), zero(elty), zero(elty)) + end end end diff --git a/stdlib/LinearAlgebra/test/dense.jl b/stdlib/LinearAlgebra/test/dense.jl index d6d882fabc65b..8477721fee199 100644 --- a/stdlib/LinearAlgebra/test/dense.jl +++ b/stdlib/LinearAlgebra/test/dense.jl @@ -18,27 +18,39 @@ n2 = 2*n1 Random.seed!(1234323) @testset "Matrix condition number" begin - ainit = rand(n,n) + ainit = rand(n, n) @testset "for $elty" for elty in (Float32, Float64, ComplexF32, ComplexF64) ainit = convert(Matrix{elty}, ainit) for a in (copy(ainit), view(ainit, 1:n, 1:n)) - @test cond(a,1) ≈ 122.15725126320953 atol=0.5 - @test cond(a,2) ≈ 78.44837047684149 atol=0.5 - @test cond(a,Inf) ≈ 174.10761543202744 atol=0.4 - @test cond(a[:,1:5]) ≈ 6.7492840150789135 atol=0.01 + ainv = inv(a) + @test cond(a, 1) == opnorm(a, 1) *opnorm(ainv, 1) + @test cond(a, Inf) == opnorm(a, Inf)*opnorm(ainv, Inf) + @test cond(a[:, 1:5]) == (\)(extrema(svdvals(a[:, 1:5]))...) @test_throws ArgumentError cond(a,3) end end @testset "Singular matrices" for p in (1, 2, Inf) @test cond(zeros(Int, 2, 2), p) == Inf - @test cond(zeros(2, 2), p) == Inf - @test cond([0 0; 1 1], p) == Inf - @test cond([0. 0.; 1. 1.], p) == Inf + @test cond(zeros(2, 2), p) == Inf + @test cond([0 0; 1 1], p) == Inf + @test cond([0. 0.; 1. 1.], p) == Inf end @testset "Issue #33547, condition number of 2x2 matrix" begin - M = [1.0 -2.0; -2.0 -1.5] + M = [1.0 -2.0 + -2.0 -1.5] @test cond(M, 1) ≈ 2.227272727272727 end + @testset "Condition numbers of a non-random matrix" begin + # To ensure that we detect any regressions in the underlying functions + Mars= [11 24 7 20 3 + 4 12 25 8 16 + 17 5 13 21 9 + 10 18 1 14 22 + 23 6 19 2 15] + @test cond(Mars, 1) ≈ 7.1 + @test cond(Mars, 2) ≈ 6.181867355918493 + @test cond(Mars, Inf) ≈ 7.1 + end end areal = randn(n,n)/2 diff --git a/stdlib/LinearAlgebra/test/eigen.jl b/stdlib/LinearAlgebra/test/eigen.jl index c1bba4de4f0ee..4ee1845ecc385 100644 --- a/stdlib/LinearAlgebra/test/eigen.jl +++ b/stdlib/LinearAlgebra/test/eigen.jl @@ -163,7 +163,7 @@ end end @testset "eigen of an Adjoint" begin - Random.seed!(1) + Random.seed!(4) A = randn(3,3) @test eigvals(A') == eigvals(copy(A')) @test eigen(A') == eigen(copy(A')) diff --git a/stdlib/LinearAlgebra/test/matmul.jl b/stdlib/LinearAlgebra/test/matmul.jl index 1febdfe49fb3b..1017134f2f6d4 100644 --- a/stdlib/LinearAlgebra/test/matmul.jl +++ b/stdlib/LinearAlgebra/test/matmul.jl @@ -152,7 +152,7 @@ end for vf in (copy(vvf), view(vvf, 1:3)), C in (copy(CC), view(CC, 1:3, 1:3)) @test mul!(C, vf, transpose(vf)) == vf*vf' C .= C0 = rand(eltype(C), size(C)) - @test mul!(C, vf, transpose(vf), 2, 3) == 2vf*vf' .+ 3C0 + @test mul!(C, vf, transpose(vf), 2, 3) ≈ 2vf*vf' .+ 3C0 end end diff --git a/stdlib/LinearAlgebra/test/qr.jl b/stdlib/LinearAlgebra/test/qr.jl index d6085565e3c7f..9706801255c76 100644 --- a/stdlib/LinearAlgebra/test/qr.jl +++ b/stdlib/LinearAlgebra/test/qr.jl @@ -415,4 +415,21 @@ end @test A.Q' * B ≈ A.Q end +@testset "convert between eltypes" begin + a = rand(Float64, 10, 5) + qra = qr(a) + qrwy = LinearAlgebra.QRCompactWY{Float32}(qra.factors, qra.T) + @test Array(qrwy) ≈ Array(qr(Float32.(a))) + @test eltype(qrwy.factors) == eltype(qrwy.T) == Float32 + qra = qr(a, ColumnNorm()) + qrp = QRPivoted{Float32}(qra.factors, qra.τ, qra.jpvt) + @test Array(qrp) ≈ Array(qr(Float32.(a), ColumnNorm())) + @test eltype(qrp.factors) == eltype(qrp.τ) == Float32 + a = rand(Float16, 10, 5) + qra = qr(a) + qrnonblas = QR{ComplexF16}(qra.factors, qra.τ) + @test Array(qrnonblas) ≈ Array(qr(ComplexF16.(a))) + @test eltype(qrnonblas.factors) == eltype(qrnonblas.τ) == ComplexF16 +end + end # module TestQR diff --git a/stdlib/Makefile b/stdlib/Makefile index e782d92eab2b9..5b7f40c8f9f7b 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -44,7 +44,7 @@ STDLIBS = Artifacts Base64 CRC32c Dates DelimitedFiles Distributed FileWatching SharedArrays Sockets SparseArrays SuiteSparse Test TOML Unicode UUIDs \ $(JLL_NAMES) -STDLIBS_EXT = Pkg Statistics LibCURL Downloads ArgTools Tar NetworkOptions SuiteSparse +STDLIBS_EXT = Pkg Statistics LibCURL Downloads ArgTools Tar NetworkOptions SuiteSparse SHA 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 @@ -61,6 +61,8 @@ 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 +SHA_GIT_URL := git://github.com/JuliaCrypto/SHA.jl.git +SHA_TAR_URL = https://api.github.com/repos/JuliaCrypto/SHA.jl/tarball/$1 $(foreach module, $(STDLIBS_EXT), $(eval $(call stdlib-external,$(module),$(shell echo $(module) | tr a-z A-Z)))) diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 4ed54226e67e7..35d6854c594fd 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,2 +1,2 @@ PKG_BRANCH = master -PKG_SHA1 = 252e895056b17490bfeabd81f52743bad947e997 +PKG_SHA1 = b79518775a82c9eb3353b5dc2e9d855fe155b120 diff --git a/stdlib/Printf/src/Printf.jl b/stdlib/Printf/src/Printf.jl index 8dacee5a1dc1b..b7487db017d45 100644 --- a/stdlib/Printf/src/Printf.jl +++ b/stdlib/Printf/src/Printf.jl @@ -292,7 +292,8 @@ fmt(buf, pos, arg::AbstractFloat, spec::Spec{T}) where {T <: Ints} = bs = base(T) arg2 = toint(arg) n = i = ndigits(arg2, base=bs, pad=1) - x, neg = arg2 < 0 ? (-arg2, true) : (arg2, false) + neg = arg2 < 0 + x = arg2 isa Base.BitSigned ? unsigned(abs(arg2)) : abs(arg2) arglen = n + (neg || (plus | space)) + (T == Val{'o'} && hash ? 1 : 0) + (T == Val{'x'} && hash ? 2 : 0) + (T == Val{'X'} && hash ? 2 : 0) diff --git a/stdlib/Printf/test/runtests.jl b/stdlib/Printf/test/runtests.jl index 3d7929c42e9e0..e80cbe9626823 100644 --- a/stdlib/Printf/test/runtests.jl +++ b/stdlib/Printf/test/runtests.jl @@ -762,6 +762,17 @@ end @test Printf.@sprintf("%20.0X", UInt(3989525555)) == " EDCB5433" @test Printf.@sprintf("%20.X", UInt(0)) == " 0" + # issue #41971 + @test Printf.@sprintf("%4d", typemin(Int8)) == "-128" + @test Printf.@sprintf("%4d", typemax(Int8)) == " 127" + @test Printf.@sprintf("%6d", typemin(Int16)) == "-32768" + @test Printf.@sprintf("%6d", typemax(Int16)) == " 32767" + @test Printf.@sprintf("%11d", typemin(Int32)) == "-2147483648" + @test Printf.@sprintf("%11d", typemax(Int32)) == " 2147483647" + @test Printf.@sprintf("%20d", typemin(Int64)) == "-9223372036854775808" + @test Printf.@sprintf("%20d", typemax(Int64)) == " 9223372036854775807" + @test Printf.@sprintf("%40d", typemin(Int128)) == "-170141183460469231731687303715884105728" + @test Printf.@sprintf("%40d", typemax(Int128)) == " 170141183460469231731687303715884105727" end @testset "%n" begin diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index 6520fffc6ca4d..2f79d3de97a2d 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -55,7 +55,8 @@ function init(; n::Union{Nothing,Integer} = nothing, delay::Union{Nothing,Real} n_cur = ccall(:jl_profile_maxlen_data, Csize_t, ()) delay_cur = ccall(:jl_profile_delay_nsec, UInt64, ())/10^9 if n === nothing && delay === nothing - return Int(n_cur), delay_cur + nthreads = Sys.iswindows() ? 1 : Threads.nthreads() # windows only profiles the main thread + return round(Int, n_cur / nthreads), delay_cur end nnew = (n === nothing) ? n_cur : n delaynew = (delay === nothing) ? delay_cur : delay @@ -290,9 +291,12 @@ end function is_block_end(data, i) i < nmeta + 1 && return false - # 32-bit linux has been seen to have rogue NULL ips, so we use two to indicate block end, where the 2nd is the - # actual end index - return data[i] == 0 && data[i - 1] == 0 + # 32-bit linux has been seen to have rogue NULL ips, so we use two to + # indicate block end, where the 2nd is the actual end index. + # and we could have (though very unlikely): + # 1::end + # and we want to ignore the triple NULL (which is an ip). + return data[i] == 0 && data[i - 1] == 0 && data[i - 2] != 0 end """ @@ -555,7 +559,7 @@ function parse_flat(::Type{T}, data::Vector{UInt64}, lidict::Union{LineInfoDict, skip = false nsleeping = 0 for i in startframe:-1:1 - startframe - 1 <= i <= startframe - (nmeta + 1) && continue # skip metadata (it's read ahead below) and extra block-end NULL IP + (startframe - 1) >= i >= (startframe - (nmeta + 1)) && continue # skip metadata (its read ahead below) and extra block end NULL IP ip = data[i] if is_block_end(data, i) # read metadata @@ -795,7 +799,7 @@ end # turn a list of backtraces into a tree (implicitly separated by NULL markers) function tree!(root::StackFrameTree{T}, all::Vector{UInt64}, lidict::Union{LineInfoFlatDict, LineInfoDict}, C::Bool, recur::Symbol, - threads::Union{Int,AbstractVector{Int}}, tasks::Union{UInt,AbstractVector{UInt}}) where {T} + threads::Union{Int,AbstractVector{Int},Nothing}=nothing, tasks::Union{UInt,AbstractVector{UInt},Nothing}=nothing) where {T} parent = root tops = Vector{StackFrameTree{T}}() build = Vector{StackFrameTree{T}}() @@ -803,7 +807,7 @@ function tree!(root::StackFrameTree{T}, all::Vector{UInt64}, lidict::Union{LineI skip = false nsleeping = 0 for i in startframe:-1:1 - startframe - 1 <= i <= startframe - (nmeta + 1) && continue # skip metadata (its read ahead below) and extra block end NULL IP + (startframe - 1) >= i >= (startframe - (nmeta + 1)) && continue # skip metadata (its read ahead below) and extra block end NULL IP ip = all[i] if is_block_end(all, i) # read metadata @@ -811,7 +815,8 @@ function tree!(root::StackFrameTree{T}, all::Vector{UInt64}, lidict::Union{LineI # cpu_cycle_clock = all[i - 3] taskid = all[i - 4] threadid = all[i - 5] - if !in(threadid, threads) || !in(taskid, tasks) + if (threads !== nothing && !in(threadid, threads)) || + (tasks !== nothing && !in(taskid, tasks)) skip = true continue end diff --git a/stdlib/Profile/test/runtests.jl b/stdlib/Profile/test/runtests.jl index 777122e571aed..940f1c4478ae3 100644 --- a/stdlib/Profile/test/runtests.jl +++ b/stdlib/Profile/test/runtests.jl @@ -106,15 +106,27 @@ end @testset "setting sample count and delay in init" begin n_, delay_ = Profile.init() + n_original = n_ + nthreads = Sys.iswindows() ? 1 : Threads.nthreads() + sample_size_bytes = sizeof(Ptr) def_n = Sys.iswindows() && Sys.WORD_SIZE == 32 ? 1_000_000 : 10_000_000 - @test n_ == def_n + if Sys.WORD_SIZE == 32 && (def_n * nthreads * sample_size_bytes) > 2^29 + @test n_ * nthreads * sample_size_bytes <= 2^29 + else + @test n_ == def_n + end + def_delay = Sys.iswindows() && Sys.WORD_SIZE == 32 ? 0.01 : 0.001 @test delay_ == def_delay Profile.init(n=1_000_001, delay=0.0005) n_, delay_ = Profile.init() - @test n_ == 1_000_001 + if Sys.WORD_SIZE == 32 && (1_000_001 * nthreads * sample_size_bytes) > 2^29 + @test n_ * nthreads * sample_size_bytes <= 2^29 + else + @test n_ == 1_000_001 + end @test delay_ == 0.0005 - Profile.init(n=def_n, delay=def_delay) + Profile.init(n=n_original, delay=def_delay) end @testset "warning for buffer full" begin diff --git a/stdlib/REPL/docs/src/index.md b/stdlib/REPL/docs/src/index.md index 552bb6246e384..bd57c87ff3a21 100644 --- a/stdlib/REPL/docs/src/index.md +++ b/stdlib/REPL/docs/src/index.md @@ -135,6 +135,8 @@ REPL.stripmd Base.Docs.apropos ``` +Another feature of help mode is the ability to access extended docstrings. You can do this by typing something like `??Print` rather than `?Print` which will display the `# Extended help` section from the source codes documentation. + Help mode can be exited by pressing backspace at the beginning of the line. ### [Shell mode](@id man-shell-mode) diff --git a/stdlib/REPL/src/TerminalMenus/Pager.jl b/stdlib/REPL/src/TerminalMenus/Pager.jl index af49c3aa63440..c823a5dedd1ba 100644 --- a/stdlib/REPL/src/TerminalMenus/Pager.jl +++ b/stdlib/REPL/src/TerminalMenus/Pager.jl @@ -1,3 +1,5 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + mutable struct Pager{C} <: _ConfiguredMenu{C} lines::Vector{String} pagesize::Int diff --git a/stdlib/REPL/src/TerminalMenus/RadioMenu.jl b/stdlib/REPL/src/TerminalMenus/RadioMenu.jl index 2060af2e14623..75c78def2bbeb 100644 --- a/stdlib/REPL/src/TerminalMenus/RadioMenu.jl +++ b/stdlib/REPL/src/TerminalMenus/RadioMenu.jl @@ -31,7 +31,9 @@ end """ - RadioMenu(options::Array{String,1}; pagesize::Int=10, kwargs...) + RadioMenu(options::Array{String,1}; pagesize::Int=10, + keybindings::Vector{Char}=Char[], + kwargs...) Create a RadioMenu object. Use `request(menu::RadioMenu)` to get user input. `request()` returns an `Int` which is the index of the option selected by the @@ -41,8 +43,12 @@ user. - `options::Array{String, 1}`: Options to be displayed - `pagesize::Int=10`: The number of options to be displayed at one time, the menu will scroll if length(options) > pagesize + - `keybindings::Vector{Char}=Char[]`: Shortcuts to pick corresponding entry from `options` Any additional keyword arguments will be passed to [`TerminalMenus.Config`](@ref). + +!!! compat "Julia 1.8" + The `keybindings` argument requires Julia 1.8 or later. """ function RadioMenu(options::Array{String,1}; pagesize::Int=10, warn::Bool=true, keybindings::Vector{Char}=Char[], kwargs...) length(options) < 1 && error("RadioMenu must have at least one option") diff --git a/stdlib/REPL/test/TerminalMenus/radio_menu.jl b/stdlib/REPL/test/TerminalMenus/radio_menu.jl index 28a19fa7d9ac0..696be1324a8e3 100644 --- a/stdlib/REPL/test/TerminalMenus/radio_menu.jl +++ b/stdlib/REPL/test/TerminalMenus/radio_menu.jl @@ -50,3 +50,5 @@ radio_menu = RadioMenu(["single option"], charset=:ascii) @test simulate_input(1, radio_menu, :up, :up, :down, :up, :enter) radio_menu = RadioMenu(string.(1:3), pagesize=1, charset=:ascii) @test simulate_input(3, radio_menu, :down, :down, :down, :down, :enter) +radio_menu = RadioMenu(["apple", "banana", "cherry"]; keybindings=collect('a':'c'), charset=:ascii) +@test simulate_input(2, radio_menu, 'b') diff --git a/stdlib/Random/Project.toml b/stdlib/Random/Project.toml index 6958e618d3ea8..199dcab940c86 100644 --- a/stdlib/Random/Project.toml +++ b/stdlib/Random/Project.toml @@ -3,6 +3,7 @@ uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [deps] Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/stdlib/Random/docs/src/index.md b/stdlib/Random/docs/src/index.md index a78b08ea89295..6750dd35b40f7 100644 --- a/stdlib/Random/docs/src/index.md +++ b/stdlib/Random/docs/src/index.md @@ -151,22 +151,22 @@ Scalar and array methods for `Die` now work as expected: ```jldoctest Die; setup = :(Random.seed!(1)) julia> rand(Die) -Die(7) +Die(5) julia> rand(MersenneTwister(0), Die) Die(11) julia> rand(Die, 3) 3-element Vector{Die}: - Die(13) - Die(8) - Die(20) + Die(9) + Die(15) + Die(14) julia> a = Vector{Die}(undef, 3); rand!(a) 3-element Vector{Die}: - Die(4) - Die(14) - Die(10) + Die(19) + Die(7) + Die(17) ``` #### A simple sampler without pre-computed data @@ -183,9 +183,9 @@ julia> rand(Die(4)) julia> rand(Die(4), 3) 3-element Vector{Any}: - 3 2 - 4 + 3 + 3 ``` Given a collection type `S`, it's currently assumed that if `rand(::S)` is defined, an object of type `eltype(S)` will be produced. In the last example, a `Vector{Any}` is produced; the reason is that `eltype(Die) == Any`. The remedy is to define `Base.eltype(::Type{Die}) = Int`. diff --git a/stdlib/Random/src/RNGs.jl b/stdlib/Random/src/RNGs.jl index c483296fe3af1..141ec14f4ed31 100644 --- a/stdlib/Random/src/RNGs.jl +++ b/stdlib/Random/src/RNGs.jl @@ -384,7 +384,7 @@ end seed!(rng::_GLOBAL_RNG, ::Nothing) = seed!(rng) # to resolve ambiguity -seed!(seed::Union{Nothing,Integer,Vector{UInt32},Vector{UInt64},NTuple{4,UInt64}}=nothing) = +seed!(seed::Union{Nothing,Integer,Vector{UInt32},Vector{UInt64}}=nothing) = seed!(GLOBAL_RNG, seed) rng_native_52(::_GLOBAL_RNG) = rng_native_52(default_rng()) diff --git a/stdlib/Random/src/Random.jl b/stdlib/Random/src/Random.jl index 45aa6442eed7e..05fe872eb6ebc 100644 --- a/stdlib/Random/src/Random.jl +++ b/stdlib/Random/src/Random.jl @@ -13,6 +13,7 @@ include("DSFMT.jl") using .DSFMT using Base.GMP.MPZ using Base.GMP: Limb +import SHA using Base: BitInteger, BitInteger_types, BitUnsigned, require_one_based_indexing diff --git a/stdlib/Random/src/Xoshiro.jl b/stdlib/Random/src/Xoshiro.jl index d39a41276a7e1..af1a5668507db 100644 --- a/stdlib/Random/src/Xoshiro.jl +++ b/stdlib/Random/src/Xoshiro.jl @@ -117,65 +117,21 @@ end # Shared implementation between Xoshiro and TaskLocalRNG -- seeding -function seed!(x::Union{TaskLocalRNG,Xoshiro}) +function seed!(rng::Union{TaskLocalRNG,Xoshiro}) # as we get good randomness from RandomDevice, we can skip hashing - parent = RandomDevice() - # Constants have nothing up their sleeve, see task.c - # 0x02011ce34bce797f == hash(UInt(1))|0x01 - # 0x5a94851fb48a6e05 == hash(UInt(2))|0x01 - # 0x3688cf5d48899fa7 == hash(UInt(3))|0x01 - # 0x867b4bb4c42e5661 == hash(UInt(4))|0x01 - setstate!(x, - 0x02011ce34bce797f * rand(parent, UInt64), - 0x5a94851fb48a6e05 * rand(parent, UInt64), - 0x3688cf5d48899fa7 * rand(parent, UInt64), - 0x867b4bb4c42e5661 * rand(parent, UInt64)) + rd = RandomDevice() + setstate!(rng, rand(rd, UInt64), rand(rd, UInt64), rand(rd, UInt64), rand(rd, UInt64)) end -function seed!(rng::Union{TaskLocalRNG,Xoshiro}, seed::NTuple{4,UInt64}) - # TODO: Consider a less ad-hoc construction - # We can afford burning a handful of cycles here, and we don't want any - # surprises with respect to bad seeds / bad interactions. - - s0 = s = Base.hash_64_64(seed[1]) - s1 = s += Base.hash_64_64(seed[2]) - s2 = s += Base.hash_64_64(seed[3]) - s3 = s += Base.hash_64_64(seed[4]) - +function seed!(rng::Union{TaskLocalRNG,Xoshiro}, seed::Union{Vector{UInt32}, Vector{UInt64}}) + c = SHA.SHA2_256_CTX() + SHA.update!(c, reinterpret(UInt8, seed)) + s0, s1, s2, s3 = reinterpret(UInt64, SHA.digest!(c)) setstate!(rng, s0, s1, s2, s3) - - rand(rng, UInt64) - rand(rng, UInt64) - rand(rng, UInt64) - rand(rng, UInt64) - rng end -function seed!(rng::Union{TaskLocalRNG, Xoshiro}, seed::UInt128) - seed0 = seed % UInt64 - seed1 = (seed>>>64) % UInt64 - seed!(rng, (seed0, seed1, zero(UInt64), zero(UInt64))) -end -seed!(rng::Union{TaskLocalRNG, Xoshiro}, seed::Integer) = seed!(rng, UInt128(seed)) - -function seed!(rng::Union{TaskLocalRNG, Xoshiro}, seed::AbstractVector{UInt64}) - if length(seed) > 4 - throw(ArgumentError("seed should have no more than 256 bits")) - end - seed0 = length(seed)>0 ? seed[1] : UInt64(0) - seed1 = length(seed)>1 ? seed[2] : UInt64(0) - seed2 = length(seed)>2 ? seed[3] : UInt64(0) - seed3 = length(seed)>3 ? seed[4] : UInt64(0) - seed!(rng, (seed0, seed1, seed2, seed3)) -end +seed!(rng::Union{TaskLocalRNG, Xoshiro}, seed::Integer) = seed!(rng, make_seed(seed)) -function seed!(rng::Union{TaskLocalRNG, Xoshiro}, seed::AbstractVector{UInt32}) - if iseven(length(seed)) - seed!(rng, reinterpret(UInt64, seed)) - else - seed!(rng, UInt64[reinterpret(UInt64, @view(seed[begin:end-1])); seed[end] % UInt64]) - end -end @inline function rand(rng::Union{TaskLocalRNG, Xoshiro}, ::SamplerType{UInt128}) first = rand(rng, UInt64) diff --git a/stdlib/Random/src/misc.jl b/stdlib/Random/src/misc.jl index 2dff5a51eff9a..0d6e06c444a09 100644 --- a/stdlib/Random/src/misc.jl +++ b/stdlib/Random/src/misc.jl @@ -53,13 +53,13 @@ number generator, see [Random Numbers](@ref). # Examples ```jldoctest julia> Random.seed!(3); randstring() -"h8BzxSoS" +"Lxz5hUwn" julia> randstring(MersenneTwister(3), 'a':'z', 6) "ocucay" julia> randstring("ACGT") -"CTTACTGC" +"TGCTCCTC" ``` !!! note diff --git a/stdlib/Random/test/runtests.jl b/stdlib/Random/test/runtests.jl index 1995a9efbc471..8ba7c5fc26663 100644 --- a/stdlib/Random/test/runtests.jl +++ b/stdlib/Random/test/runtests.jl @@ -688,7 +688,7 @@ end @testset "$RNG(seed) & Random.seed!(m::$RNG, seed) produce the same stream" for RNG=(MersenneTwister,Xoshiro) seeds = Any[0, 1, 2, 10000, 10001, rand(UInt32, 8), rand(UInt128, 3)...] if RNG == Xoshiro - push!(seeds, rand(UInt64, rand(1:4)), Tuple(rand(UInt64, 4))) + push!(seeds, rand(UInt64, rand(1:4))) end for seed=seeds m = RNG(seed) @@ -699,7 +699,7 @@ end end @testset "Random.seed!(seed) sets Random.GLOBAL_SEED" begin - seeds = Any[0, rand(UInt128), rand(UInt64, 4), Tuple(rand(UInt64, 4))] + seeds = Any[0, rand(UInt128), rand(UInt64, 4)] for seed=seeds Random.seed!(seed) diff --git a/stdlib/SHA.version b/stdlib/SHA.version new file mode 100644 index 0000000000000..a6a472fe270af --- /dev/null +++ b/stdlib/SHA.version @@ -0,0 +1,2 @@ +SHA_BRANCH = master +SHA_SHA1 = c5dd533520393b9dea34ad25287f222dc28fe07a diff --git a/stdlib/SHA/LICENSE.md b/stdlib/SHA/LICENSE.md deleted file mode 100644 index eec075ce6f2ff..0000000000000 --- a/stdlib/SHA/LICENSE.md +++ /dev/null @@ -1,58 +0,0 @@ -The SHA.jl package is licensed under the MIT "Expat" License: - -> Copyright (c) 2014: Elliot Saba. -> -> Permission is hereby granted, free of charge, to any person obtaining -> a copy of this software and associated documentation files (the -> "Software"), to deal in the Software without restriction, including -> without limitation the rights to use, copy, modify, merge, publish, -> distribute, sublicense, and/or sell copies of the Software, and to -> permit persons to whom the Software is furnished to do so, subject to -> the following conditions: -> -> The above copyright notice and this permission notice shall be -> included in all copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -> IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -> CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -> TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -> SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -This package was inspired by the SHA2 [source code from Minix](https://github.com/minix3/minix/blob/b6cbf7203b080219de306404f8022a65b7884f33/common/lib/libc/hash/sha2/sha2.c), itself released under the BSD license: - -> sha2.c -> -> Version 1.0.0beta1 -> -> Written by Aaron D. Gifford -> -> Copyright 2000 Aaron D. Gifford. All rights reserved. -> -> Redistribution and use in source and binary forms, with or without -> modification, are permitted provided that the following conditions -> are met: -> -> 1. Redistributions of source code must retain the above copyright -> notice, this list of conditions and the following disclaimer. -> -> 2. Redistributions in binary form must reproduce the above copyright -> notice, this list of conditions and the following disclaimer in the -> documentation and/or other materials provided with the distribution. -> -> 3. Neither the name of the copyright holder nor the names of contributors -> may be used to endorse or promote products derived from this software -> without specific prior written permission. -> -> THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTOR(S) ``AS IS'' AND -> ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -> IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -> ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTOR(S) BE LIABLE -> FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -> DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -> OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -> HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -> LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -> OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF diff --git a/stdlib/SHA/Project.toml b/stdlib/SHA/Project.toml deleted file mode 100644 index 7fe8ff0d5c192..0000000000000 --- a/stdlib/SHA/Project.toml +++ /dev/null @@ -1,8 +0,0 @@ -name = "SHA" -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" - -[extras] -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[targets] -test = ["Test"] diff --git a/stdlib/SHA/docs/src/index.md b/stdlib/SHA/docs/src/index.md deleted file mode 100644 index 30c88e5cd8757..0000000000000 --- a/stdlib/SHA/docs/src/index.md +++ /dev/null @@ -1,75 +0,0 @@ -# SHA - - -Usage is very straightforward: -```julia -julia> using SHA - -julia> bytes2hex(sha256("test")) -"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08" -``` - -Each exported function (at the time of this writing, SHA-1, SHA-2 224, 256, 384 and 512, and SHA-3 224, 256, 384 and 512 functions are implemented) takes in either an `AbstractVector{UInt8}`, an `AbstractString` or an `IO` object. This makes it trivial to checksum a file: - -```julia -shell> cat /tmp/test.txt -test -julia> using SHA - -julia> open("/tmp/test.txt") do f - sha2_256(f) - end -32-element Array{UInt8,1}: - 0x9f - 0x86 - 0xd0 - 0x81 - 0x88 - 0x4c - 0x7d - 0x65 - ⋮ - 0x5d - 0x6c - 0x15 - 0xb0 - 0xf0 - 0x0a - 0x08 -``` - -Due to the colloquial usage of `sha256` to refer to `sha2_256`, convenience functions are provided, mapping `shaxxx()` function calls to `sha2_xxx()`. For SHA-3, no such colloquialisms exist and the user must use the full `sha3_xxx()` names. - -`shaxxx()` takes `AbstractString` and array-like objects (`NTuple` and `Array`) with elements of type `UInt8`. - -To create a hash from multiple items the `SHAX_XXX_CTX()` types can be used to create a stateful hash object that -is updated with `update!` and finalized with `digest!` - -```julia -julia> ctx = SHA2_256_CTX() -SHA2 256-bit hash state - -julia> update!(ctx, b"some data") -0x0000000000000009 - -julia> update!(ctx, b"some more data") -0x0000000000000017 - -julia> digest!(ctx) -32-element Vector{UInt8}: - 0xbe - 0xcf - 0x23 - 0xda - 0xaf - 0x02 - ⋮ - 0x25 - 0x52 - 0x19 - 0xa0 - 0x8b - 0xc5 -``` - -Note that, at the time of this writing, the SHA3 code is not optimized, and as such is roughly an order of magnitude slower than SHA2. diff --git a/stdlib/SHA/src/SHA.jl b/stdlib/SHA/src/SHA.jl deleted file mode 100644 index ac93fdf13f442..0000000000000 --- a/stdlib/SHA/src/SHA.jl +++ /dev/null @@ -1,137 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -""" - SHA - -The SHA module provides hashing functionality for SHA1, SHA2 and SHA3 algorithms. - -They are implemented as both pure functions for hashing single pieces of data, -or a stateful context which can be updated with the `update!` function and -finalized with `digest!`. - -```julia-repl -julia> sha1(b"some data") -20-element Vector{UInt8}: - 0xba - 0xf3 - ⋮ - 0xe3 - 0x56 - - -julia> ctx = SHA1_CTX() -SHA1 hash state - -julia> update!(ctx, b"some data") -0x0000000000000009 - -julia> digest!(ctx) -20-element Vector{UInt8}: - 0xba - 0xf3 - ⋮ - 0xe3 - 0x56 -""" -module SHA - -# Export convenience functions, context types, update!() and digest!() functions -export sha1, SHA1_CTX, update!, digest! -export sha224, sha256, sha384, sha512 -export sha2_224, sha2_256, sha2_384, sha2_512 -export sha3_224, sha3_256, sha3_384, sha3_512 -export SHA224_CTX, SHA256_CTX, SHA384_CTX, SHA512_CTX -export SHA2_224_CTX, SHA2_256_CTX, SHA2_384_CTX, SHA2_512_CTX -export SHA3_224_CTX, SHA3_256_CTX, SHA3_384_CTX, SHA3_512_CTX -export HMAC_CTX, hmac_sha1 -export hmac_sha224, hmac_sha256, hmac_sha384, hmac_sha512 -export hmac_sha2_224, hmac_sha2_256, hmac_sha2_384, hmac_sha2_512 -export hmac_sha3_224, hmac_sha3_256, hmac_sha3_384, hmac_sha3_512 - -# data to be hashed: -const AbstractBytes = Union{AbstractVector{UInt8},NTuple{N,UInt8} where N} - -include("constants.jl") -include("types.jl") -include("base_functions.jl") -include("sha1.jl") -include("sha2.jl") -include("sha3.jl") -include("common.jl") -include("hmac.jl") - -# Create data types and convenience functions for each hash implemented -for (f, ctx) in [(:sha1, :SHA1_CTX), - (:sha224, :SHA224_CTX), - (:sha256, :SHA256_CTX), - (:sha384, :SHA384_CTX), - (:sha512, :SHA512_CTX), - (:sha2_224, :SHA2_224_CTX), - (:sha2_256, :SHA2_256_CTX), - (:sha2_384, :SHA2_384_CTX), - (:sha2_512, :SHA2_512_CTX), - (:sha3_224, :SHA3_224_CTX), - (:sha3_256, :SHA3_256_CTX), - (:sha3_384, :SHA3_384_CTX), - (:sha3_512, :SHA3_512_CTX),] - g = Symbol(:hmac_, f) - - @eval begin - # Our basic function is to process arrays of bytes - """ - $($f)(data) - - Hash data using the $($f) algorithm and return the resulting digest. - See also [`$($ctx)`](@ref). - """ - function $f(data::AbstractBytes) - ctx = $ctx() - update!(ctx, data) - return digest!(ctx) - - """ - $($g)(key, data) - - Hash data using the $($f) algorithm using the passed key - See also [`HMAC_CTX`](@ref). - """ - end - function $g(key::Vector{UInt8}, data::AbstractBytes) - ctx = HMAC_CTX($ctx(), key) - update!(ctx, data) - return digest!(ctx) - end - - # AbstractStrings are a pretty handy thing to be able to crunch through - $f(str::AbstractString) = $f(String(str)) # always crunch UTF-8 repr - $f(str::String) = $f(codeunits(str)) - $g(key::Vector{UInt8}, str::AbstractString) = $g(key, String(str)) - $g(key::Vector{UInt8}, str::String) = $g(key, codeunits(str)) - - """ - $($f)(io::IO) - - Hash data from io using $($f) algorithm from io. - """ - function $f(io::IO, chunk_size=4*1024) - ctx = $ctx() - buff = Vector{UInt8}(undef, chunk_size) - while !eof(io) - num_read = readbytes!(io, buff) - update!(ctx, buff, num_read) - end - return digest!(ctx) - end - function $g(key::Vector{UInt8}, io::IO, chunk_size=4*1024) - ctx = HMAC_CTX($ctx(), key) - buff = Vector{UInt8}(undef, chunk_size) - while !eof(io) - num_read = readbytes!(io, buff) - update!(ctx, buff, num_read) - end - return digest!(ctx) - end - end -end - -end #module SHA diff --git a/stdlib/SHA/src/base_functions.jl b/stdlib/SHA/src/base_functions.jl deleted file mode 100644 index 0b6216fdbdf18..0000000000000 --- a/stdlib/SHA/src/base_functions.jl +++ /dev/null @@ -1,42 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# THE SIX LOGICAL FUNCTIONS -# -# Bit shifting and rotation (used by the six SHA-XYZ logical functions: -# -# NOTE: The naming of R and S appears backwards here (R is a SHIFT and -# S is a ROTATION) because the SHA2-256/384/512 description document -# (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this -# same "backwards" definition. - -# 32-bit Rotate-right (equivalent to S32 in SHA-256) and rotate-left -rrot(b,x,width) = ((x >> b) | (x << (width - b))) -lrot(b,x,width) = ((x << b) | (x >> (width - b))) - -# Shift-right (used in SHA-256, SHA-384, and SHA-512): -R(b,x) = (x >> b) -# 32-bit Rotate-right (used in SHA-256): -S32(b,x) = rrot(b,x,32) -# 64-bit Rotate-right (used in SHA-384 and SHA-512): -S64(b,x) = rrot(b,x,64) -# 64-bit Rotate-left (used in SHA3) -L64(b,x) = lrot(b,x,64) - -# Two of six logical functions used in SHA-256, SHA-384, and SHA-512: -Ch(x,y,z) = ((x & y) ⊻ (~x & z)) -Maj(x,y,z) = ((x & y) ⊻ (x & z) ⊻ (y & z)) - -# Four of six logical functions used in SHA-256: -Sigma0_256(x) = (S32(2, UInt32(x)) ⊻ S32(13, UInt32(x)) ⊻ S32(22, UInt32(x))) -Sigma1_256(x) = (S32(6, UInt32(x)) ⊻ S32(11, UInt32(x)) ⊻ S32(25, UInt32(x))) -sigma0_256(x) = (S32(7, UInt32(x)) ⊻ S32(18, UInt32(x)) ⊻ R(3 , UInt32(x))) -sigma1_256(x) = (S32(17, UInt32(x)) ⊻ S32(19, UInt32(x)) ⊻ R(10, UInt32(x))) - -# Four of six logical functions used in SHA-384 and SHA-512: -Sigma0_512(x) = (S64(28, UInt64(x)) ⊻ S64(34, UInt64(x)) ⊻ S64(39, UInt64(x))) -Sigma1_512(x) = (S64(14, UInt64(x)) ⊻ S64(18, UInt64(x)) ⊻ S64(41, UInt64(x))) -sigma0_512(x) = (S64( 1, UInt64(x)) ⊻ S64( 8, UInt64(x)) ⊻ R( 7, UInt64(x))) -sigma1_512(x) = (S64(19, UInt64(x)) ⊻ S64(61, UInt64(x)) ⊻ R( 6, UInt64(x))) - -# Let's be able to bswap arrays of these types as well -bswap!(x::Vector{<:Integer}) = map!(bswap, x, x) diff --git a/stdlib/SHA/src/common.jl b/stdlib/SHA/src/common.jl deleted file mode 100644 index 5500a372f5fa2..0000000000000 --- a/stdlib/SHA/src/common.jl +++ /dev/null @@ -1,116 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# Common update and digest functions which work across SHA1 and SHA2 - -# update! takes in variable-length data, buffering it into blocklen()-sized pieces, -# calling transform!() when necessary to update the internal hash state. -""" - update!(context, data[, datalen]) - -Update the SHA context with the bytes in data. See also [`digest!`](@ref) for -finalizing the hash. - -# Examples -```julia-repl -julia> ctx = SHA1_CTX() -SHA1 hash state - -julia> update!(ctx, b"data to to be hashed") -``` -""" -function update!(context::T, data::U, datalen=length(data)) where {T<:SHA_CTX, U<:AbstractBytes} - # We need to do all our arithmetic in the proper bitwidth - UIntXXX = typeof(context.bytecount) - - # Process as many complete blocks as possible - 0 ≤ datalen ≤ length(data) || throw(BoundsError(data, firstindex(data)+datalen-1)) - len = convert(UIntXXX, datalen) - data_idx = convert(UIntXXX, firstindex(data)-1) - usedspace = context.bytecount % blocklen(T) - while len - data_idx + usedspace >= blocklen(T) - # Fill up as much of the buffer as we can with the data given us - copyto!(context.buffer, usedspace + 1, data, data_idx + 1, blocklen(T) - usedspace) - - transform!(context) - context.bytecount += blocklen(T) - usedspace - data_idx += blocklen(T) - usedspace - usedspace = convert(UIntXXX, 0) - end - - # There is less than a complete block left, but we need to save the leftovers into context.buffer: - if len > data_idx - copyto!(context.buffer, usedspace + 1, data, data_idx + 1, len - data_idx) - context.bytecount += len - data_idx - end -end - -# Pad the remainder leaving space for the bitcount -function pad_remainder!(context::T) where T<:SHA_CTX - usedspace = context.bytecount % blocklen(T) - # If we have anything in the buffer still, pad and transform that data - if usedspace > 0 - # Begin padding with a 1 bit: - context.buffer[usedspace+1] = 0x80 - usedspace += 1 - - # If we have room for the bitcount, then pad up to the short blocklen - if usedspace <= short_blocklen(T) - for i = 1:(short_blocklen(T) - usedspace) - context.buffer[usedspace + i] = 0x0 - end - else - # Otherwise, pad out this entire block, transform it, then pad up to short blocklen - for i = 1:(blocklen(T) - usedspace) - context.buffer[usedspace + i] = 0x0 - end - transform!(context) - for i = 1:short_blocklen(T) - context.buffer[i] = 0x0 - end - end - else - # If we don't have anything in the buffer, pad an entire shortbuffer - context.buffer[1] = 0x80 - for i = 2:short_blocklen(T) - context.buffer[i] = 0x0 - end - end -end - - -# Clear out any saved data in the buffer, append total bitlength, and return our precious hash! -# Note: SHA3_CTX has a more specialised method -""" - digest!(context) - -Finalize the SHA context and return the hash as array of bytes (Array{Uint8, 1}). - -# Examples -```julia-repl -julia> ctx = SHA1_CTX() -SHA1 hash state - -julia> update!(ctx, b"data to to be hashed") - -julia> digest!(ctx) -20-element Array{UInt8,1}: - 0x83 - 0xe4 - ⋮ - 0x89 - 0xf5 -``` -""" -function digest!(context::T) where T<:SHA_CTX - pad_remainder!(context) - # Store the length of the input data (in bits) at the end of the padding - bitcount_idx = div(short_blocklen(T), sizeof(context.bytecount)) + 1 - pbuf = Ptr{typeof(context.bytecount)}(pointer(context.buffer)) - unsafe_store!(pbuf, bswap(context.bytecount * 8), bitcount_idx) - - # Final transform: - transform!(context) - - # Return the digest - return reinterpret(UInt8, bswap!(context.state))[1:digestlen(T)] -end diff --git a/stdlib/SHA/src/constants.jl b/stdlib/SHA/src/constants.jl deleted file mode 100644 index 3c5fde92d3863..0000000000000 --- a/stdlib/SHA/src/constants.jl +++ /dev/null @@ -1,131 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# SHA initial hash values and constants - -# Hash constant words K for SHA1 -const K1 = UInt32[ - 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 -] - -# Initial hash value H for SHA1 -const SHA1_initial_hash_value = UInt32[ - 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 -] - - - -# Hash constant words K for SHA-256: -const K256 = UInt32[ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 -] - -# Initial hash value H for SHA-224: -const SHA2_224_initial_hash_value = UInt32[ - 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, - 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 -] - - -const SHA2_256_initial_hash_value = UInt32[ - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, - 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 -] - -# Hash constant words K for SHA-384 and SHA-512: -const K512 = UInt64[ - 0x428a2f98d728ae22, 0x7137449123ef65cd, - 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, - 0x3956c25bf348b538, 0x59f111f1b605d019, - 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, - 0xd807aa98a3030242, 0x12835b0145706fbe, - 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, - 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, - 0x9bdc06a725c71235, 0xc19bf174cf692694, - 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, - 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, - 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, - 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, - 0x983e5152ee66dfab, 0xa831c66d2db43210, - 0xb00327c898fb213f, 0xbf597fc7beef0ee4, - 0xc6e00bf33da88fc2, 0xd5a79147930aa725, - 0x06ca6351e003826f, 0x142929670a0e6e70, - 0x27b70a8546d22ffc, 0x2e1b21385c26c926, - 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, - 0x650a73548baf63de, 0x766a0abb3c77b2a8, - 0x81c2c92e47edaee6, 0x92722c851482353b, - 0xa2bfe8a14cf10364, 0xa81a664bbc423001, - 0xc24b8b70d0f89791, 0xc76c51a30654be30, - 0xd192e819d6ef5218, 0xd69906245565a910, - 0xf40e35855771202a, 0x106aa07032bbd1b8, - 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, - 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, - 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, - 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, - 0x748f82ee5defb2fc, 0x78a5636f43172f60, - 0x84c87814a1f0ab72, 0x8cc702081a6439ec, - 0x90befffa23631e28, 0xa4506cebde82bde9, - 0xbef9a3f7b2c67915, 0xc67178f2e372532b, - 0xca273eceea26619c, 0xd186b8c721c0c207, - 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, - 0x06f067aa72176fba, 0x0a637dc5a2c898a6, - 0x113f9804bef90dae, 0x1b710b35131c471b, - 0x28db77f523047d84, 0x32caab7b40c72493, - 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, - 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, - 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 -] - -# Initial hash value H for SHA-384 -const SHA2_384_initial_hash_value = UInt64[ - 0xcbbb9d5dc1059ed8, 0x629a292a367cd507, - 0x9159015a3070dd17, 0x152fecd8f70e5939, - 0x67332667ffc00b31, 0x8eb44a8768581511, - 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4 -] - -# Initial hash value H for SHA-512 -const SHA2_512_initial_hash_value = UInt64[ - 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, - 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, - 0x510e527fade682d1, 0x9b05688c2b3e6c1f, - 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179 -] - -# Round constants for SHA3 rounds -const SHA3_ROUND_CONSTS = UInt64[ - 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, - 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, - 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, - 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, - 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, - 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, - 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, - 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 -] - -# Rotation constants for SHA3 rounds -const SHA3_ROTC = UInt64[ - 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, - 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 -] - -# Permutation indices for SHA3 rounds (+1'ed so as to work with julia's 1-based indexing) -const SHA3_PILN = Int[ - 11, 8, 12, 18, 19, 4, 6, 17, 9, 22, 25, 5, - 16, 24, 20, 14, 13, 3, 21, 15, 23, 10, 7, 2 -] diff --git a/stdlib/SHA/src/hmac.jl b/stdlib/SHA/src/hmac.jl deleted file mode 100644 index 1ba9b95c6109d..0000000000000 --- a/stdlib/SHA/src/hmac.jl +++ /dev/null @@ -1,35 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -struct HMAC_CTX{CTX<:SHA_CTX} - context::CTX - outer::Vector{UInt8} - - function HMAC_CTX(ctx::CTX, key::Vector{UInt8}, blocksize::Integer=blocklen(CTX)) where CTX - if length(key) > blocksize - _ctx = CTX() - update!(_ctx, key) - key = digest!(_ctx) - end - - pad = blocksize - length(key) - - if pad > 0 - key = [key; fill(0x00, pad)] - end - - update!(ctx, key .⊻ 0x36) - new{CTX}(ctx, key .⊻ 0x5c) - end -end - -function update!(ctx::HMAC_CTX, data, datalen=length(data)) - update!(ctx.context, data, datalen) -end - -function digest!(ctx::HMAC_CTX{CTX}) where CTX - digest = digest!(ctx.context) - _ctx = CTX() - update!(_ctx, ctx.outer) - update!(_ctx, digest) - digest!(_ctx) -end diff --git a/stdlib/SHA/src/sha1.jl b/stdlib/SHA/src/sha1.jl deleted file mode 100644 index 71fd55e20fc1e..0000000000000 --- a/stdlib/SHA/src/sha1.jl +++ /dev/null @@ -1,95 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# Nonlinear functions, in order to encourage inlining, these sadly are not an array of lambdas -function Round0(b,c,d) - return UInt32((b & c) | (~b & d)) -end - -function Round1And3(b,c,d) - return UInt32(b ⊻ c ⊻ d) -end - -function Round2(b,c,d) - return UInt32((b & c) | (b & d) | (c & d)) -end - -function transform!(context::SHA1_CTX) - # Buffer is 16 elements long, we expand to 80 - pbuf = buffer_pointer(context) - for i in 1:16 - context.W[i] = bswap(unsafe_load(pbuf, i)) - end - - # First round of expansions - for i in 17:32 - @inbounds begin - context.W[i] = lrot(1, context.W[i-3] ⊻ context.W[i-8] ⊻ context.W[i-14] ⊻ context.W[i-16], 32) - end - end - - # Second round of expansions (possibly 4-way SIMD-able) - for i in 33:80 - @inbounds begin - context.W[i] = lrot(2, context.W[i-6] ⊻ context.W[i-16] ⊻ context.W[i-28] ⊻ context.W[i-32], 32) - end - end - - # Initialize registers with the previous intermediate values (our state) - a = context.state[1] - b = context.state[2] - c = context.state[3] - d = context.state[4] - e = context.state[5] - - # Run our rounds, manually separated into the four rounds, unfortunately using an array of lambdas - # really kills performance and causes a huge number of allocations, so we make it easy on the compiler - for i = 1:20 - @inbounds begin - temp = UInt32(lrot(5, a, 32) + Round0(b,c,d) + e + context.W[i] + K1[1]) - e = d - d = c - c = lrot(30, b, 32) - b = a - a = temp - end - end - - for i = 21:40 - @inbounds begin - temp = UInt32(lrot(5, a, 32) + Round1And3(b,c,d) + e + context.W[i] + K1[2]) - e = d - d = c - c = lrot(30, b, 32) - b = a - a = temp - end - end - - for i = 41:60 - @inbounds begin - temp = UInt32(lrot(5, a, 32) + Round2(b,c,d) + e + context.W[i] + K1[3]) - e = d - d = c - c = lrot(30, b, 32) - b = a - a = temp - end - end - - for i = 61:80 - @inbounds begin - temp = UInt32(lrot(5, a, 32) + Round1And3(b,c,d) + e + context.W[i] + K1[4]) - e = d - d = c - c = lrot(30, b, 32) - b = a - a = temp - end - end - - context.state[1] += a - context.state[2] += b - context.state[3] += c - context.state[4] += d - context.state[5] += e -end diff --git a/stdlib/SHA/src/sha2.jl b/stdlib/SHA/src/sha2.jl deleted file mode 100644 index 5cc4363786e39..0000000000000 --- a/stdlib/SHA/src/sha2.jl +++ /dev/null @@ -1,136 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -function transform!(context::T) where {T<:Union{SHA2_224_CTX,SHA2_256_CTX}} - pbuf = buffer_pointer(context) - # Initialize registers with the previous intermediate values (our state) - a = context.state[1] - b = context.state[2] - c = context.state[3] - d = context.state[4] - e = context.state[5] - f = context.state[6] - g = context.state[7] - h = context.state[8] - - # Run initial rounds - for j = 1:16 - @inbounds begin - # We bitswap every input byte - v = bswap(unsafe_load(pbuf, j)) - unsafe_store!(pbuf, v, j) - - # Apply the SHA-256 compression function to update a..h - T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + v - T2 = Sigma0_256(a) + Maj(a, b, c) - h = g - g = f - f = e - e = UInt32(d + T1) - d = c - c = b - b = a - a = UInt32(T1 + T2) - end - end - - for j = 17:64 - @inbounds begin - # Implicit message block expansion: - s0 = unsafe_load(pbuf, mod1(j + 1, 16)) - s0 = sigma0_256(s0) - s1 = unsafe_load(pbuf, mod1(j + 14, 16)) - s1 = sigma1_256(s1) - - # Apply the SHA-256 compression function to update a..h - v = unsafe_load(pbuf, mod1(j, 16)) + s1 + unsafe_load(pbuf, mod1(j + 9, 16)) + s0 - unsafe_store!(pbuf, v, mod1(j, 16)) - T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + v - T2 = Sigma0_256(a) + Maj(a, b, c) - h = g - g = f - f = e - e = UInt32(d + T1) - d = c - c = b - b = a - a = UInt32(T1 + T2) - end - end - - # Compute the current intermediate hash value - context.state[1] += a - context.state[2] += b - context.state[3] += c - context.state[4] += d - context.state[5] += e - context.state[6] += f - context.state[7] += g - context.state[8] += h -end - - -function transform!(context::Union{SHA2_384_CTX,SHA2_512_CTX}) - pbuf = buffer_pointer(context) - # Initialize registers with the prev. intermediate value - a = context.state[1] - b = context.state[2] - c = context.state[3] - d = context.state[4] - e = context.state[5] - f = context.state[6] - g = context.state[7] - h = context.state[8] - - for j = 1:16 - @inbounds begin - v = bswap(unsafe_load(pbuf, j)) - unsafe_store!(pbuf, v, j) - - # Apply the SHA-512 compression function to update a..h - T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + v - T2 = Sigma0_512(a) + Maj(a, b, c) - h = g - g = f - f = e - e = d + T1 - d = c - c = b - b = a - a = T1 + T2 - end - end - - for j = 17:80 - @inbounds begin - # Implicit message block expansion: - s0 = unsafe_load(pbuf, mod1(j + 1, 16)) - s0 = sigma0_512(s0) - s1 = unsafe_load(pbuf, mod1(j + 14, 16)) - s1 = sigma1_512(s1) - - # Apply the SHA-512 compression function to update a..h - v = unsafe_load(pbuf, mod1(j, 16)) + s1 + unsafe_load(pbuf, mod1(j + 9, 16)) + s0 - unsafe_store!(pbuf, v, mod1(j, 16)) - T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + v - T2 = Sigma0_512(a) + Maj(a, b, c) - h = g - g = f - f = e - e = d + T1 - d = c - c = b - b = a - a = T1 + T2 - end - end - - # Compute the current intermediate hash value - context.state[1] += a - context.state[2] += b - context.state[3] += c - context.state[4] += d - context.state[5] += e - context.state[6] += f - context.state[7] += g - context.state[8] += h -end diff --git a/stdlib/SHA/src/sha3.jl b/stdlib/SHA/src/sha3.jl deleted file mode 100644 index 6f94495630742..0000000000000 --- a/stdlib/SHA/src/sha3.jl +++ /dev/null @@ -1,83 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -function transform!(context::T) where {T<:SHA3_CTX} - # First, update state with buffer - pbuf = Ptr{eltype(context.state)}(pointer(context.buffer)) - for idx in 1:div(blocklen(T),8) - context.state[idx] = context.state[idx] ⊻ unsafe_load(pbuf, idx) - end - bc = context.bc - state = context.state - - # We always assume 24 rounds - @inbounds for round in 0:23 - # Theta function - for i in 1:5 - bc[i] = state[i] ⊻ state[i + 5] ⊻ state[i + 10] ⊻ state[i + 15] ⊻ state[i + 20] - end - - for i in 0:4 - temp = bc[rem(i + 4, 5) + 1] ⊻ L64(1, bc[rem(i + 1, 5) + 1]) - j = 0 - while j <= 20 - state[Int(i + j + 1)] = state[i + j + 1] ⊻ temp - j += 5 - end - end - - # Rho Pi - temp = state[2] - for i in 1:24 - j = SHA3_PILN[i] - bc[1] = state[j] - state[j] = L64(SHA3_ROTC[i], temp) - temp = bc[1] - end - - # Chi - j = 0 - while j <= 20 - for i in 1:5 - bc[i] = state[i + j] - end - for i in 0:4 - state[j + i + 1] = state[j + i + 1] ⊻ (~bc[rem(i + 1, 5) + 1] & bc[rem(i + 2, 5) + 1]) - end - j += 5 - end - - # Iota - state[1] = state[1] ⊻ SHA3_ROUND_CONSTS[round+1] - end - - return context.state -end - - - -# Finalize data in the buffer, append total bitlength, and return our precious hash! -function digest!(context::T) where {T<:SHA3_CTX} - usedspace = context.bytecount % blocklen(T) - # If we have anything in the buffer still, pad and transform that data - if usedspace < blocklen(T) - 1 - # Begin padding with a 0x06 - context.buffer[usedspace+1] = 0x06 - # Fill with zeros up until the last byte - context.buffer[usedspace+2:end-1] .= 0x00 - # Finish it off with a 0x80 - context.buffer[end] = 0x80 - else - # Otherwise, we have to add on a whole new buffer just for the zeros and 0x80 - context.buffer[end] = 0x06 - transform!(context) - - context.buffer[1:end-1] = 0x0 - context.buffer[end] = 0x80 - end - - # Final transform: - transform!(context) - - # Return the digest - return reinterpret(UInt8, context.state)[1:digestlen(T)] -end diff --git a/stdlib/SHA/src/types.jl b/stdlib/SHA/src/types.jl deleted file mode 100644 index 3534be4fafc96..0000000000000 --- a/stdlib/SHA/src/types.jl +++ /dev/null @@ -1,230 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# Type hierarchy to aid in splitting up of SHA2 algorithms -# as SHA224/256 are similar, and SHA-384/512 are similar -abstract type SHA_CTX end -abstract type SHA2_CTX <: SHA_CTX end -abstract type SHA3_CTX <: SHA_CTX end -import Base: copy - -# We derive SHA1_CTX straight from SHA_CTX since it doesn't have a -# family of types like SHA2 or SHA3 do -mutable struct SHA1_CTX <: SHA_CTX - state::Array{UInt32,1} - bytecount::UInt64 - buffer::Array{UInt8,1} - W::Array{UInt32,1} -end - -# SHA2 224/256/384/512-bit Context Structures -mutable struct SHA2_224_CTX <: SHA2_CTX - state::Array{UInt32,1} - bytecount::UInt64 - buffer::Array{UInt8,1} -end - -mutable struct SHA2_256_CTX <: SHA2_CTX - state::Array{UInt32,1} - bytecount::UInt64 - buffer::Array{UInt8,1} -end - -mutable struct SHA2_384_CTX <: SHA2_CTX - state::Array{UInt64,1} - bytecount::UInt128 - buffer::Array{UInt8,1} -end - -mutable struct SHA2_512_CTX <: SHA2_CTX - state::Array{UInt64,1} - bytecount::UInt128 - buffer::Array{UInt8,1} -end - -function Base.getproperty(ctx::SHA2_CTX, fieldname::Symbol) - if fieldname === :state - return getfield(ctx, :state)::Union{Vector{UInt32},Vector{UInt64}} - elseif fieldname === :bytecount - return getfield(ctx, :bytecount)::Union{UInt64,UInt128} - elseif fieldname === :buffer - return getfield(ctx, :buffer)::Vector{UInt8} - elseif fieldname === :W - return getfield(ctx, :W)::Vector{UInt32} - else - error("SHA2_CTX has no field ", fieldname) - end -end - - -# Typealias common nicknames for SHA2 family of functions -const SHA224_CTX = SHA2_224_CTX -const SHA256_CTX = SHA2_256_CTX -const SHA384_CTX = SHA2_384_CTX -const SHA512_CTX = SHA2_512_CTX - - -# SHA3 224/256/384/512-bit context structures -mutable struct SHA3_224_CTX <: SHA3_CTX - state::Array{UInt64,1} - bytecount::UInt128 - buffer::Array{UInt8,1} - bc::Array{UInt64,1} -end -mutable struct SHA3_256_CTX <: SHA3_CTX - state::Array{UInt64,1} - bytecount::UInt128 - buffer::Array{UInt8,1} - bc::Array{UInt64,1} -end -mutable struct SHA3_384_CTX <: SHA3_CTX - state::Array{UInt64,1} - bytecount::UInt128 - buffer::Array{UInt8,1} - bc::Array{UInt64,1} -end -mutable struct SHA3_512_CTX <: SHA3_CTX - state::Array{UInt64,1} - bytecount::UInt128 - buffer::Array{UInt8,1} - bc::Array{UInt64,1} -end - -function Base.getproperty(ctx::SHA3_CTX, fieldname::Symbol) - if fieldname === :state - return getfield(ctx, :state)::Vector{UInt64} - elseif fieldname === :bytecount - return getfield(ctx, :bytecount)::UInt128 - elseif fieldname === :buffer - return getfield(ctx, :buffer)::Vector{UInt8} - elseif fieldname === :bc - return getfield(ctx, :bc)::Vector{UInt64} - else - error("type ", typeof(ctx), " has no field ", fieldname) - end -end - -# Define constants via functions so as not to bloat context objects. Yay dispatch! - -# Digest lengths for SHA1, SHA2 and SHA3. This is easy to figure out from the typename -digestlen(::Type{SHA1_CTX}) = 20 -digestlen(::Type{SHA2_224_CTX}) = 28 -digestlen(::Type{SHA3_224_CTX}) = 28 -digestlen(::Type{SHA2_256_CTX}) = 32 -digestlen(::Type{SHA3_256_CTX}) = 32 -digestlen(::Type{SHA2_384_CTX}) = 48 -digestlen(::Type{SHA3_384_CTX}) = 48 -digestlen(::Type{SHA2_512_CTX}) = 64 -digestlen(::Type{SHA3_512_CTX}) = 64 - -# SHA1 and SHA2 have differing element types for the internal state objects -state_type(::Type{SHA1_CTX}) = UInt32 -state_type(::Type{SHA2_224_CTX}) = UInt32 -state_type(::Type{SHA2_256_CTX}) = UInt32 -state_type(::Type{SHA2_384_CTX}) = UInt64 -state_type(::Type{SHA2_512_CTX}) = UInt64 -state_type(::Type{SHA3_CTX}) = UInt64 - -# blocklen is the number of bytes of data processed by the transform!() function at once -blocklen(::Type{SHA1_CTX}) = UInt64(64) -blocklen(::Type{SHA2_224_CTX}) = UInt64(64) -blocklen(::Type{SHA2_256_CTX}) = UInt64(64) -blocklen(::Type{SHA2_384_CTX}) = UInt64(128) -blocklen(::Type{SHA2_512_CTX}) = UInt64(128) - -blocklen(::Type{SHA3_224_CTX}) = UInt64(25*8 - 2*digestlen(SHA3_224_CTX)) -blocklen(::Type{SHA3_256_CTX}) = UInt64(25*8 - 2*digestlen(SHA3_256_CTX)) -blocklen(::Type{SHA3_384_CTX}) = UInt64(25*8 - 2*digestlen(SHA3_384_CTX)) -blocklen(::Type{SHA3_512_CTX}) = UInt64(25*8 - 2*digestlen(SHA3_512_CTX)) - - -# short_blocklen is the size of a block minus the width of bytecount -short_blocklen(::Type{T}) where {T<:SHA_CTX} = blocklen(T) - 2*sizeof(state_type(T)) - -# Once the "blocklen" methods are defined, we can define our outer constructors for SHA types: - -""" - SHA2_224_CTX() - -Construct an empty SHA2_224 context. -""" -SHA2_224_CTX() = SHA2_224_CTX(copy(SHA2_224_initial_hash_value), 0, zeros(UInt8, blocklen(SHA2_224_CTX))) -""" - SHA2_256_CTX() - -Construct an empty SHA2_256 context. -""" -SHA2_256_CTX() = SHA2_256_CTX(copy(SHA2_256_initial_hash_value), 0, zeros(UInt8, blocklen(SHA2_256_CTX))) -""" - SHA2_384() - -Construct an empty SHA2_384 context. -""" -SHA2_384_CTX() = SHA2_384_CTX(copy(SHA2_384_initial_hash_value), 0, zeros(UInt8, blocklen(SHA2_384_CTX))) -""" - SHA2_512_CTX() - -Construct an empty SHA2_512 context. -""" -SHA2_512_CTX() = SHA2_512_CTX(copy(SHA2_512_initial_hash_value), 0, zeros(UInt8, blocklen(SHA2_512_CTX))) - -""" - SHA3_224_CTX() - -Construct an empty SHA3_224 context. -""" -SHA3_224_CTX() = SHA3_224_CTX(zeros(UInt64, 25), 0, zeros(UInt8, blocklen(SHA3_224_CTX)), Vector{UInt64}(undef, 5)) -""" - SHA3_256_CTX() - -Construct an empty SHA3_256 context. -""" -SHA3_256_CTX() = SHA3_256_CTX(zeros(UInt64, 25), 0, zeros(UInt8, blocklen(SHA3_256_CTX)), Vector{UInt64}(undef, 5)) -""" - SHA3_384_CTX() - -Construct an empty SHA3_384 context. -""" -SHA3_384_CTX() = SHA3_384_CTX(zeros(UInt64, 25), 0, zeros(UInt8, blocklen(SHA3_384_CTX)), Vector{UInt64}(undef, 5)) -""" - SHA3_512_CTX() - -Construct an empty SHA3_512 context. -""" -SHA3_512_CTX() = SHA3_512_CTX(zeros(UInt64, 25), 0, zeros(UInt8, blocklen(SHA3_512_CTX)), Vector{UInt64}(undef, 5)) - -# Nickname'd outer constructor methods for SHA2 -const SHA224_CTX = SHA2_224_CTX -const SHA256_CTX = SHA2_256_CTX -const SHA384_CTX = SHA2_384_CTX -const SHA512_CTX = SHA2_512_CTX - -# SHA1 is special; he needs extra workspace -""" - SHA1_CTX() - -Construct an empty SHA1 context. -""" -SHA1_CTX() = SHA1_CTX(copy(SHA1_initial_hash_value), 0, zeros(UInt8, blocklen(SHA1_CTX)), Vector{UInt32}(undef, 80)) - - -# Copy functions -copy(ctx::T) where {T<:SHA1_CTX} = T(copy(ctx.state), ctx.bytecount, copy(ctx.buffer), copy(ctx.W)) -copy(ctx::T) where {T<:SHA2_CTX} = T(copy(ctx.state), ctx.bytecount, copy(ctx.buffer)) -copy(ctx::T) where {T<:SHA3_CTX} = T(copy(ctx.state), ctx.bytecount, copy(ctx.buffer), Vector{UInt64}(undef, 5)) - - -# Make printing these types a little friendlier -import Base.show -show(io::IO, ::SHA1_CTX) = print(io, "SHA1 hash state") -show(io::IO, ::SHA2_224_CTX) = print(io, "SHA2 224-bit hash state") -show(io::IO, ::SHA2_256_CTX) = print(io, "SHA2 256-bit hash state") -show(io::IO, ::SHA2_384_CTX) = print(io, "SHA2 384-bit hash state") -show(io::IO, ::SHA2_512_CTX) = print(io, "SHA2 512-bit hash state") -show(io::IO, ::SHA3_224_CTX) = print(io, "SHA3 224-bit hash state") -show(io::IO, ::SHA3_256_CTX) = print(io, "SHA3 256-bit hash state") -show(io::IO, ::SHA3_384_CTX) = print(io, "SHA3 384-bit hash state") -show(io::IO, ::SHA3_512_CTX) = print(io, "SHA3 512-bit hash state") - - -# use our types to define a method to get a pointer to the state buffer -buffer_pointer(ctx::T) where {T<:SHA_CTX} = Ptr{state_type(T)}(pointer(ctx.buffer)) diff --git a/stdlib/SHA/test/perf.jl b/stdlib/SHA/test/perf.jl deleted file mode 100644 index 08dbbe9b1cb96..0000000000000 --- a/stdlib/SHA/test/perf.jl +++ /dev/null @@ -1,47 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -using SHA - -if isempty(ARGS) - error("need file to test sha perf") -elseif !isfile(ARGS[1]) - error("file $(ARGS[1]) does not exist") -end - - -function do_tests(filepath) - # test performance - print("read: ") - @time begin - fh = open(filepath, "r") - bytes = read(fh) - end - GC.gc() - - print("SHA-1: ") - sha1(bytes) - GC.gc() - @time sha1(bytes) - - print("SHA2-256: ") - sha256(bytes) - GC.gc() - @time sha256(bytes) - - print("SHA2-512: ") - sha512(bytes) - GC.gc() - @time sha512(bytes) - - print("SHA3-256: ") - sha3_256(bytes) - GC.gc() - @time sha3_256(bytes) - - print("SHA3-512: ") - sha3_512(bytes) - GC.gc() - @time sha3_512(bytes) -end - -do_tests(ARGS[1]) diff --git a/stdlib/SHA/test/runtests.jl b/stdlib/SHA/test/runtests.jl deleted file mode 100644 index 49ab05e47b739..0000000000000 --- a/stdlib/SHA/test/runtests.jl +++ /dev/null @@ -1,304 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -using SHA -using Test - -const VERBOSE = false - -# Define some data we will run our tests on -lorem = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." -so_many_as_array = repeat([0x61], 1000000) -so_many_as_tuple = ntuple((i) -> 0x61, 1000000) -tempdir = mktempdir() -file = joinpath(tempdir, ".sha") -fIO = open(file, "w") -write(fIO, '\0') -close(fIO) -data = Any["", "test", lorem, file, so_many_as_array, so_many_as_tuple] - -# Descriptions of the data, the SHA functions we'll run on the data, etc... -data_desc = ["the empty string", "the string \"test\"", "lorem ipsum", - "0 file", "one million a's Array", "one million a's Tuple"] -sha_types = Dict(sha1 => SHA.SHA1_CTX, - sha2_224 => SHA.SHA2_224_CTX, sha2_256 => SHA.SHA2_256_CTX, sha2_384 => SHA.SHA2_384_CTX, sha2_512 => SHA.SHA2_512_CTX, - sha3_224 => SHA.SHA3_224_CTX, sha3_256 => SHA.SHA3_256_CTX, sha3_384 => SHA.SHA3_384_CTX, sha3_512 => SHA.SHA3_512_CTX) -sha_funcs = [sha1, - sha2_224, sha2_256, sha2_384, sha2_512, - sha3_224, sha3_256, sha3_384, sha3_512] -ctxs = [SHA1_CTX, - SHA2_224_CTX, SHA2_256_CTX, SHA2_384_CTX, SHA2_512_CTX, - SHA3_224_CTX, SHA3_256_CTX, SHA3_384_CTX, SHA3_512_CTX] -shws = ["SHA1 hash state", - "SHA2 224-bit hash state", "SHA2 256-bit hash state", "SHA2 384-bit hash state", "SHA2 512-bit hash state", - "SHA3 224-bit hash state", "SHA3 256-bit hash state", "SHA3 384-bit hash state", "SHA3 512-bit hash state"] - -answers = Dict( -sha1 => [ -"da39a3ee5e6b4b0d3255bfef95601890afd80709", -"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", -"19afa2a4a37462c7b940a6c4c61363d49c3a35f4", -"5ba93c9db0cff93f52b521d7420e43f6eda2784f", -"34aa973cd4c4daa4f61eeb2bdbad27316534016f", -"34aa973cd4c4daa4f61eeb2bdbad27316534016f" -], -sha2_224 => [ -"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", -"90a3ed9e32b2aaf4c61c410eb925426119e1a9dc53d4286ade99a809", -"6a0644abcf1e2cecbec2814443dab5f24b7ad8ebb66c75667ab67959", -"fff9292b4201617bdc4d3053fce02734166a683d7d858a7f5f59b073", -"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67", -"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67" -], -sha2_256 => [ -"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", -"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", -"2c7c3d5f244f1a40069a32224215e0cf9b42485c99d80f357d76f006359c7a18", -"6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", -"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0", -"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0" -], -sha2_384 => [ -"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", -"768412320f7b0aa5812fce428dc4706b3cae50e02a64caa16a782249bfe8efc4b7ef1ccb126255d196047dfedf17a0a9", -"63980fd0425cd2c3d8a400ee0f2671ef135db03b947ec1af21b6e28f19c16ca272036469541f4d8e336ac6d1da50580f", -"bec021b4f368e3069134e012c2b4307083d3a9bdd206e24e5f0d86e13d6636655933ec2b413465966817a9c208a11717", -"9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985", -"9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985" -], -sha2_512 => [ -"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", -"ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff", -"f41d92bc9fc1157a0d1387e67f3d0893b70f7039d3d46d8115b5079d45ad601159398c79c281681e2da09bf7d9f8c23b41d1a0a3c5b528a7f2735933a4353194", -"b8244d028981d693af7b456af8efa4cad63d282e19ff14942c246e50d9351d22704a802a71c3580b6370de4ceb293c324a8423342557d4e5c38438f0e36910ee", -"e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b", -"e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b" -], -sha3_224 => [ -"6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", -"3797bf0afbbfca4a7bbba7602a2b552746876517a7f9b7ce2db0ae7b", -"ea5395370949ad8c7d2ca3e7c045ef3306fe3a3f4740de452ef87a28", -"bdd5167212d2dc69665f5a8875ab87f23d5ce7849132f56371a19096", -"d69335b93325192e516a912e6d19a15cb51c6ed5c15243e7a7fd653c", -"d69335b93325192e516a912e6d19a15cb51c6ed5c15243e7a7fd653c" -], -sha3_256 => [ -"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", -"36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab80", -"8c8142d2ca964ab307ace567ddd5764f17ebb76eb8ff25543ab54c14fe2ab139", -"5d53469f20fef4f8eab52b88044ede69c77a6a68a60728609fc4a65ff531e7d0", -"5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1", -"5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1", -], -sha3_384 => [ -"0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004", -"e516dabb23b6e30026863543282780a3ae0dccf05551cf0295178d7ff0f1b41eecb9db3ff219007c4e097260d58621bd", -"eb9fbba3eb916a4efe384b3125f5d03ceb9c5c1b94431ac30fa86c54408b92701ca5d2628cd7113aa5541177ec3ccd1d", -"127677f8b66725bbcb7c3eae9698351ca41e0eb6d66c784bd28dcdb3b5fb12d0c8e840342db03ad1ae180b92e3504933", -"eee9e24d78c1855337983451df97c8ad9eedf256c6334f8e948d252d5e0e76847aa0774ddb90a842190d2c558b4b8340", -"eee9e24d78c1855337983451df97c8ad9eedf256c6334f8e948d252d5e0e76847aa0774ddb90a842190d2c558b4b8340", -], -sha3_512 => [ -"a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26", -"9ece086e9bac491fac5c1d1046ca11d737b92a2b2ebd93f005d7b710110c0a678288166e7fbe796883a4f2e9b3ca9f484f521d0ce464345cc1aec96779149c14", -"3a4318353396a12dfd20442cfce1d8ad4d7e732e85cc56b01b4cf9057a41c8827c0a03c70812e76ace68d776759225c213b4f581aac0dba5dd43b785b1a33fe5", -"7127aab211f82a18d06cf7578ff49d5089017944139aa60d8bee057811a15fb55a53887600a3eceba004de51105139f32506fe5b53e1913bfa6b32e716fe97da", -"3c3a876da14034ab60627c077bb98f7e120a2a5370212dffb3385a18d4f38859ed311d0a9d5141ce9cc5c66ee689b266a8aa18ace8282a0e0db596c90b0a7b87", -"3c3a876da14034ab60627c077bb98f7e120a2a5370212dffb3385a18d4f38859ed311d0a9d5141ce9cc5c66ee689b266a8aa18ace8282a0e0db596c90b0a7b87", -] -) - -function describe_hash(T::Type{S}) where {S <: SHA.SHA_CTX} - if T <: SHA.SHA1_CTX return "SHA1" end - if T <: SHA.SHA2_CTX return "SHA2-$(SHA.digestlen(T)*8)" end - if T <: SHA.SHA3_CTX return "SHA3-$(SHA.digestlen(T)*8)" end -end - -VERBOSE && println("Loaded hash types: $(join(sort([describe_hash(t[2]) for t in sha_types]), ", ", " and "))") - -# First, test processing the data in one go -nerrors = 0 -for idx in 1:length(data) - global nerrors - - desc = data_desc[idx] - VERBOSE && print("Testing on $desc$(join(["." for z in 1:(34-length(desc))]))") - nerrors_old = nerrors - for sha_idx in 1:length(sha_funcs) - sha_func = sha_funcs[sha_idx] - - if idx == 4 - open(data[idx]) do f - hash = bytes2hex(sha_func(f)) - end - else - hash = bytes2hex(sha_func(data[idx])) - end - - if hash != answers[sha_func][idx] - print("\n") - @warn( - """ - For $(describe_hash(sha_types[sha_func])) expected: - $(answers[sha_func][idx]) - Calculated: - $(hash) - """) - nerrors += 1 - else - VERBOSE && print(".") - end - end - VERBOSE && println("Done! [$(nerrors - nerrors_old) errors]") -end - -# Do another test on the "so many a's" data where we chunk up the data into -# two chunks, (sized appropriately to AVOID overflow from one update to another) -# in order to test multiple update!() calls -VERBOSE && print("Testing on one million a's (chunked properly)") -nerrors_old = nerrors -for sha_idx in 1:length(sha_funcs) - global nerrors - - ctx = sha_types[sha_funcs[sha_idx]]() - SHA.update!(ctx, so_many_as_array[1:2*SHA.blocklen(typeof(ctx))]) - SHA.update!(ctx, so_many_as_array[2*SHA.blocklen(typeof(ctx))+1:end]) - hash = bytes2hex(SHA.digest!(ctx)) - if hash != answers[sha_funcs[sha_idx]][end] - print("\n") - @warn( - """ - For $(describe_hash(sha_types[sha_funcs[sha_idx]])) expected: - $(answers[sha_funcs[sha_idx]][end-1]) - Calculated: - $(hash) - """) - nerrors += 1 - else - VERBOSE && print(".") - end -end -VERBOSE && println("Done! [$(nerrors - nerrors_old) errors]") - -# Do another test on the "so many a's" data where we chunk up the data into -# three chunks, (sized appropriately to CAUSE overflow from one update to another) -# in order to test multiple update!() calls as well as the overflow codepaths -VERBOSE && print("Testing on one million a's (chunked clumsily)") -nerrors_old = nerrors -for sha_idx in 1:length(sha_funcs) - global nerrors - ctx = sha_types[sha_funcs[sha_idx]]() - - # Get indices awkwardly placed for the blocklength of this hash type - idx0 = round(Int, 0.3*SHA.blocklen(typeof(ctx))) - idx1 = round(Int, 1.7*SHA.blocklen(typeof(ctx))) - idx2 = round(Int, 2.6*SHA.blocklen(typeof(ctx))) - - # Feed data in according to our dastardly blocking scheme - SHA.update!(ctx, so_many_as_array[0 + 1:1*idx0]) - SHA.update!(ctx, so_many_as_array[1*idx0 + 1:2*idx0]) - SHA.update!(ctx, so_many_as_array[2*idx0 + 1:3*idx0]) - SHA.update!(ctx, so_many_as_array[3*idx0 + 1:4*idx0]) - SHA.update!(ctx, so_many_as_array[4*idx0 + 1:idx1]) - SHA.update!(ctx, so_many_as_array[idx1 + 1:idx2]) - SHA.update!(ctx, so_many_as_array[idx2 + 1:end]) - - # Ensure the hash is the appropriate one - hash = bytes2hex(SHA.digest!(ctx)) - if hash != answers[sha_funcs[sha_idx]][end] - print("\n") - @warn( - """ - For $(describe_hash(sha_types[sha_funcs[sha_idx]])) expected: - $(answers[sha_funcs[sha_idx]][end-1]) - Calculated: - $(hash) - """) - nerrors += 1 - else - VERBOSE && print(".") - end -end -VERBOSE && println("Done! [$(nerrors - nerrors_old) errors]") - -# test hmac correctness using the examples on [wiki](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code#Examples) -VERBOSE && print("Testing on the hmac functions") -nerrors_old = nerrors -for (key, msg, fun, hash) in ( - (b"", b"", hmac_sha1, "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d"), - (b"", b"", hmac_sha256, "b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), - (b"key", b"The quick brown fox jumps over the lazy dog", hmac_sha1, "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"), - (b"key", b"The quick brown fox jumps over the lazy dog", hmac_sha256, "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8"), -) - global nerrors - digest1 = bytes2hex(fun(Vector(key), Vector(msg))) - digest2 = bytes2hex(fun(Vector(key), IOBuffer(Vector(msg)))) - if digest1 != hash || digest2 != hash - print("\n") - @warn( - """ - For $fun($(String(key)), $(String(msg))) expected: - $hash - Calculated: - $digest1 - $digest2 - """) - nerrors += 1 - else - VERBOSE && print(".") - end -end -VERBOSE && println("Done! [$(nerrors - nerrors_old) errors]") - -replstr(x) = sprint((io, x) -> show(IOContext(io, :limit => true), MIME("text/plain"), x), x) - -for idx in 1:length(ctxs) - global nerrors - # Part #1: copy - VERBOSE && print("Testing copy function @ $(ctxs[idx]) ...") - try - copy(ctxs[idx]()) - catch - print("\n") - @warn("Some weird copy error happened with $(ctxs[idx])") - nerrors += 1 - end - VERBOSE && println("Done! [$(nerrors - nerrors_old) errors]") - - # Part #2: show - VERBOSE && print("Testing show function @ $(ctxs[idx]) ...") - if replstr(ctxs[idx]()) != shws[idx] - print("\n") - @warn("Some weird show error happened with $(ctxs[idx])") - nerrors += 1 - end - VERBOSE && println("Done! [$(nerrors - nerrors_old) errors]") -end - -# test error if eltype of input is not UInt8 -for f in sha_funcs - global nerrors - local data = UInt32[0x23467, 0x324775] - try - f(data) - catch ex - if ex isa MethodError && - ex.f === f && - ex.args === (data,) - continue - end - rethrow() - end - @warn("Non-UInt8 Arrays should fail") - nerrors += 1 -end - -# Clean up the I/O mess -rm(file) -rm(tempdir) - -if nerrors == 0 - VERBOSE && println("ALL OK") -else - println("Failed with $nerrors failures") -end -@test nerrors == 0 diff --git a/stdlib/SparseArrays/src/sparsematrix.jl b/stdlib/SparseArrays/src/sparsematrix.jl index 5287da8d959a0..ea8a8f0b442e2 100644 --- a/stdlib/SparseArrays/src/sparsematrix.jl +++ b/stdlib/SparseArrays/src/sparsematrix.jl @@ -1642,12 +1642,13 @@ argument specifies a random number generator, see [Random Numbers](@ref). ```jldoctest; setup = :(using Random; Random.seed!(1234)) julia> sprand(Bool, 2, 2, 0.5) 2×2 SparseMatrixCSC{Bool, Int64} with 2 stored entries: - 1 ⋅ - ⋅ 1 + 1 1 + ⋅ ⋅ julia> sprand(Float64, 3, 0.75) -3-element SparseVector{Float64, Int64} with 1 stored entry: - [3] = 0.787459 +3-element SparseVector{Float64, Int64} with 2 stored entries: + [1] = 0.795547 + [2] = 0.49425 ``` """ function sprand(r::AbstractRNG, m::Integer, n::Integer, density::AbstractFloat, rfn::Function, ::Type{T}=eltype(rfn(r, 1))) where T @@ -1688,9 +1689,9 @@ argument specifies a random number generator, see [Random Numbers](@ref). # Examples ```jldoctest; setup = :(using Random; Random.seed!(0)) julia> sprandn(2, 2, 0.75) -2×2 SparseMatrixCSC{Float64, Int64} with 1 stored entry: - ⋅ ⋅ - ⋅ 1.32078 +2×2 SparseMatrixCSC{Float64, Int64} with 3 stored entries: + -1.20577 ⋅ + 0.311817 -0.234641 ``` """ sprandn(r::AbstractRNG, m::Integer, n::Integer, density::AbstractFloat) = diff --git a/stdlib/SparseArrays/test/sparse.jl b/stdlib/SparseArrays/test/sparse.jl index bc8e3f15a0ec7..128bb4735f063 100644 --- a/stdlib/SparseArrays/test/sparse.jl +++ b/stdlib/SparseArrays/test/sparse.jl @@ -1750,7 +1750,7 @@ end A = guardseed(1234321) do triu(sprand(10, 10, 0.2)) end - @test getcolptr(SparseArrays.droptol!(A, 0.01)) == [1, 1, 3, 4, 5, 6, 7, 11, 13, 15, 18] + @test getcolptr(SparseArrays.droptol!(A, 0.01)) == [1, 1, 1, 1, 2, 2, 2, 4, 4, 5, 5] @test isequal(SparseArrays.droptol!(sparse([1], [1], [1]), 1), SparseMatrixCSC(1, 1, Int[1, 1], Int[], Int[])) end diff --git a/stdlib/Statistics.version b/stdlib/Statistics.version index 84cdf8630e8fc..bcd3ac4ac113f 100644 --- a/stdlib/Statistics.version +++ b/stdlib/Statistics.version @@ -1,2 +1,2 @@ STATISTICS_BRANCH = master -STATISTICS_SHA1 = 54f9b0d999813aa9fab039f632df222ffd2a96a8 +STATISTICS_SHA1 = 74897fed33700dba92578aa0fefef5b99ba16086 diff --git a/stdlib/Test/test/nothrow_testset.jl b/stdlib/Test/test/nothrow_testset.jl index 73811b9702346..5b5c775960a9a 100644 --- a/stdlib/Test/test/nothrow_testset.jl +++ b/stdlib/Test/test/nothrow_testset.jl @@ -1,3 +1,5 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + mutable struct NoThrowTestSet <: Test.AbstractTestSet results::Vector NoThrowTestSet(desc) = new([]) diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 32c367a7a50a8..c9547f3d9610e 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -514,6 +514,9 @@ function test_primitives(::Type{T}, shape, ::Type{TestAbstractArray}) where T @test convert(Matrix, Y) == Y @test convert(Matrix, view(Y, 1:2, 1:2)) == Y @test_throws MethodError convert(Matrix, X) + + # convert(::Type{Union{}}, A::AbstractMatrix) + @test_throws MethodError convert(Union{}, X) end mutable struct TestThrowNoGetindex{T} <: AbstractVector{T} end diff --git a/test/arrayops.jl b/test/arrayops.jl index f2905aa0b582a..dd96fedf85a6a 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -567,6 +567,7 @@ end @test findlast(!iszero, a) == 8 @test findlast(a.==0) == 5 @test findlast(a.==5) == nothing + @test findlast(false) == nothing # test non-AbstractArray findlast @test findlast(isequal(3), [1,2,4,1,2,3,4]) == 6 @test findlast(isodd, [2,4,6,3,9,2,0]) == 5 @test findlast(isodd, [2,4,6,2,0]) == nothing @@ -748,6 +749,12 @@ end @test res === dst == [5 6 4; 2 3 1] res = circshift!(dst, src, (3.0, 2.0)) @test res === dst == [5 6 4; 2 3 1] + + # https://github.com/JuliaLang/julia/issues/41402 + src = Float64[] + @test circshift(src, 1) == src + src = zeros(Bool, (4,0)) + @test circshift(src, 1) == src end @testset "circcopy" begin @@ -1451,6 +1458,26 @@ end @test isempty(eoa) end +@testset "logical keepat!" begin + # Vector + a = Vector(1:10) + keepat!(a, [falses(5); trues(5)]) + @test a == 6:10 + @test_throws BoundsError keepat!(a, trues(1)) + @test_throws BoundsError keepat!(a, trues(11)) + + # BitVector + ba = rand(10) .> 0.5 + @test isa(ba, BitArray) + keepat!(ba, ba) + @test all(ba) + + # empty array + ea = [] + keepat!(ea, Bool[]) + @test isempty(ea) +end + @testset "deleteat!" begin for idx in Any[1, 2, 5, 9, 10, 1:0, 2:1, 1:1, 2:2, 1:2, 2:4, 9:8, 10:9, 9:9, 10:10, 8:9, 9:10, 6:9, 7:10] @@ -1550,6 +1577,12 @@ end @test reverse!(Any[]) == Any[] end +@testset "reverseind" begin + @test reverseind([1, 2, 3], 2) == 2 + @test reverseind([1, 2, 3], 0) == 4 + @test reverseind([1, 2, 3], 3) == 1 +end + @testset "reverse dim" begin @test isequal(reverse([2,3,1], dims=1), [1,3,2]) @test_throws ArgumentError reverse([2,3,1], dims=2) diff --git a/test/bitarray.jl b/test/bitarray.jl index b565252664876..291317a1444d3 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -1711,3 +1711,40 @@ end @test typeof([a a ;;; a a]) <: BitArray @test typeof([a a ;;; [a a]]) <: BitArray end + +@testset "deleteat! additional tests" begin + for v in ([1, 2, 3], [true, true, true], trues(3)) + @test_throws BoundsError deleteat!(v, true:true) + end + + for v in ([1], [true], trues(1)) + @test length(deleteat!(v, false:false)) == 1 + @test isempty(deleteat!(v, true:true)) + end + + x = trues(3) + x[3] = false + @test deleteat!(x, [UInt8(2)]) == [true, false] + @test_throws ArgumentError deleteat!(x, Any[true]) + @test_throws ArgumentError deleteat!(x, Any[1, true]) + @test_throws ArgumentError deleteat!(x, Any[2, 1]) + @test_throws BoundsError deleteat!(x, Any[4]) + @test_throws BoundsError deleteat!(x, Any[2, 4]) + + function test_equivalence(n::Int) + x1 = rand(Bool, n) + x2 = BitVector(x1) + inds1 = rand(Bool, n) + inds2 = BitVector(inds1) + return deleteat!(copy(x1), findall(inds1)) == + deleteat!(copy(x1), inds1) == + deleteat!(copy(x2), inds1) == + deleteat!(copy(x1), inds2) == + deleteat!(copy(x2), inds2) + end + + Random.seed!(1234) + for n in 1:20, _ in 1:100 + @test test_equivalence(n) + end +end \ No newline at end of file diff --git a/test/broadcast.jl b/test/broadcast.jl index 329bcc602206b..38950dd10b5b0 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -1052,3 +1052,9 @@ end @test Broadcast.BroadcastFunction(+)(2:3, 2:3) == 4:2:6 @test Broadcast.BroadcastFunction(+)(2:3, 2:3) isa AbstractRange end + +@testset "#42063" begin + buf = IOBuffer() + @test println.(buf, [1,2,3]) == [nothing, nothing, nothing] + @test String(take!(buf)) == "1\n2\n3\n" +end diff --git a/test/clangsa/GCPushPop.cpp b/test/clangsa/GCPushPop.cpp index a992630291bb5..f8dcfdafa5aa9 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 --analyzer-no-default-checks -x c++ %s #include "julia.h" diff --git a/test/clangsa/ImplicitAtomicsTest.c b/test/clangsa/ImplicitAtomicsTest.c new file mode 100644 index 0000000000000..f423d21084bdf --- /dev/null +++ b/test/clangsa/ImplicitAtomicsTest.c @@ -0,0 +1,99 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +// RUN: clang --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libImplicitAtomicsPlugin%shlibext -Xclang -verify -I%julia_home/src -I%julia_home/src/support -I%julia_home/usr/include ${CLANGSA_FLAGS} ${CPPFLAGS} ${CFLAGS} ${CXXFLAGS} -Xclang -analyzer-checker=core,julia.ImplicitAtomics --analyzer-no-default-checks -x c++ -std=c++11 %s -v +// RUN: clang --analyze -Xanalyzer -analyzer-output=text -Xclang -load -Xclang libImplicitAtomicsPlugin%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.ImplicitAtomics --analyzer-no-default-checks -x c -std=c11 %s -v + +#include "julia_atomics.h" + +_Atomic(int) x, *px; +struct Atomic_xy_t { + _Atomic(int) x; + _Atomic(int) *px; + int y; +} y, *py; +_Atomic(int) z[2]; + + +// jwn: add tests for casts, and *py = y; + +void hiddenAtomics(void) { + px = &x; + py = &y; + y.px = &y.x; + ++x; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + --x; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + x++; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + x--; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + x += 2; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + x -= 2; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} +#ifndef __cplusplus // invalid C++ code + x *= 2; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + x = // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + x; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} +#endif + x = 2; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + x + 2; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + + ++*px; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + --*px; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + px++; + px--; + 1 + *px++; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + 1 + *px--; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + (*px)++; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + (*px)--; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + *px += 2; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + *px -= 2; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} +#ifndef __cplusplus // invalid C++ code + *px *= 2; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + *px = // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + x; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + x = // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + *px; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} +#endif + *px = 2; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + *px + 2; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + + *(int*)&x = 3; + *(int*)px = 3; + + y.y = 2; + py->y = 2; +#ifndef __cplusplus // invalid C++ code + *py = // TODO + y; // TODO + y = // TODO + *py; // TODO +#endif + *(_Atomic(int)*)&y.y = 2; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + *(_Atomic(int)*)&py->y = 2; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + + y.x = 1; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + *y.px = 1; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + + py->x = 1; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + *py->px = 1; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + + z[1] = 1; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + *z = 1; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + *z += 1; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + +#ifdef __cplusplus // check initialization / finalization + _Atomic(int) lx{2}; + lx = 3; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + lx += 1; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + + struct large_type { int x[16]; }; + auto *ly = new std::atomic(); + *ly = // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + ly->load(); + struct large_type a = *ly; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + delete ly; + +#if 0 // enable for C++2a + std::atomic_ref lz(*(int*)px); + lz = 3; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} + lz += 1; // expected-warning{{Implicit Atomic seq_cst synchronization}} expected-note{{Implicit Atomic seq_cst synchronization}} +#endif +#endif +} diff --git a/test/clangsa/Makefile b/test/clangsa/Makefile index 850f9ea76985a..3bebd45c9a5a6 100644 --- a/test/clangsa/Makefile +++ b/test/clangsa/Makefile @@ -3,11 +3,11 @@ JULIAHOME := $(abspath $(SRCDIR)/../..) BUILDDIR := . include $(JULIAHOME)/Make.inc -check: $(SRCDIR) +check: . TESTS = $(patsubst $(SRCDIR)/%,%,$(wildcard $(SRCDIR)/*.c) $(wildcard $(SRCDIR)/*.cpp)) -$(SRCDIR) $(TESTS): +. $(TESTS): @$(MAKE) -C $(BUILDDIR)/../../src $(build_includedir)/julia/julia_version.h @$(MAKE) -C $(BUILDDIR)/../../src clangsa PATH=$(build_bindir):$(build_depsbindir):$$PATH \ @@ -17,6 +17,6 @@ $(SRCDIR) $(TESTS): CPPFLAGS_FLAGS="${CPPFLAGS_FLAGS}" \ CFLAGS_FLAGS="${CFLAGS_FLAGS}" \ CXXFLAGS_FLAGS="${CXXFLAGS_FLAGS}" \ - $(build_depsbindir)/lit/lit.py -v $@ + $(build_depsbindir)/lit/lit.py -v $(addprefix $(SRCDIR)/,$@) -.PHONY: $(TESTS) $(SRCDIR) check all +.PHONY: $(TESTS) check all . 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/clangsa/lit.cfg.py b/test/clangsa/lit.cfg.py index 5790eab812e9c..bb48f0b891acf 100644 --- a/test/clangsa/lit.cfg.py +++ b/test/clangsa/lit.cfg.py @@ -14,8 +14,6 @@ platform.system() == 'Windows' else '.so')) config.substitutions.append(("%julia_home", os.path.join(os.path.dirname(__file__), "../.."))) -path = os.path.pathsep.join((os.path.join(os.path.dirname(__file__),"../../usr/tools"), os.path.join(os.path.dirname(__file__),"../../usr/bin"), config.environment['PATH'])) -config.environment['PATH'] = path config.environment['HOME'] = "/tmp" config.environment['CLANGSA_FLAGS'] = os.environ.get('CLANGSA_FLAGS', "") config.environment['CLANGSA_CXXFLAGS'] = os.environ.get('CLANGSA_CXXFLAGS', "") diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index fb206acf03477..8d85b1a1cc5c8 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -119,7 +119,7 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` # handling of @projectname in --project and JULIA_PROJECT let expanded = abspath(Base.load_path_expand("@foo")) @test expanded == readchomp(`$exename --project='@foo' -e 'println(Base.active_project())'`) - @test expanded == readchomp(setenv(`$exename -e 'println(Base.active_project())'`, "JULIA_PROJECT" => "@foo", "HOME" => homedir())) + @test expanded == readchomp(addenv(`$exename -e 'println(Base.active_project())'`, "JULIA_PROJECT" => "@foo", "HOME" => homedir())) end # --quiet, --banner diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 7bfbf695f1304..84750a1d2748b 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -241,6 +241,15 @@ barTuple2() = fooTuple{tuple(:y)}() Dict{Int64,Tuple{UnitRange{Int64},UnitRange{Int64}}}, Core.Compiler.Const(:vals)) == Array{Tuple{UnitRange{Int64},UnitRange{Int64}},1} +# assert robustness of `getfield_tfunc` +struct GetfieldRobustness + field::String +end +@test Base.return_types((GetfieldRobustness,String,)) do obj, s + t = (10, s) # to form `PartialStruct` + getfield(obj, t) +end |> only === Union{} + # issue #12476 function f12476(a) (k, v) = a @@ -2889,6 +2898,21 @@ function symcmp36230(vec) end @test Base.return_types(symcmp36230, (Vector{Any},)) == Any[Bool] +function foo42190(r::Union{Nothing,Int}, n::Int) + while r !== nothing && r < n + return r # `r::Int` + end + return n +end +@test Base.return_types(foo42190, (Union{Nothing, Int}, Int)) == Any[Int] +function bar42190(r::Union{Nothing,Int}, n::Int) + while r === nothing || r < n + return n + end + return r # `r::Int` +end +@test Base.return_types(bar42190, (Union{Nothing, Int}, Int)) == Any[Int] + # Issue #36531, double varargs in abstract_iteration f36531(args...) = tuple((args...)...) @test @inferred(f36531(1,2,3)) == (1,2,3) diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index a276e6ef65c77..6bdb71bf8f292 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -650,3 +650,33 @@ let @test ninlined == length(code) end end + +# https://github.com/JuliaLang/julia/issues/42246 +@test mktempdir() do dir + cd(dir) do + code = quote + issue42246() = @noinline IOBuffer("a") + let + ci, rt = only(code_typed(issue42246)) + if any(ci.code) do stmt + Meta.isexpr(stmt, :invoke) && + stmt.args[1].def.name === nameof(IOBuffer) + end + exit(0) + else + exit(1) + end + end + end |> string + cmd = `$(Base.julia_cmd()) --code-coverage=tmp.info -e $code` + success(pipeline(Cmd(cmd); stdout=stdout, stderr=stderr)) + end +end + +# Issue #42264 - crash on certain union splits +let f(x) = (x...,) + # Test splatting with a Union of non-{Tuple, SimpleVector} types that require creating new `iterate` calls + # in inlining. For this particular case, we're relying on `iterate(::CaretesianIndex)` throwing an error, such + # 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 diff --git a/test/compiler/irpasses.jl b/test/compiler/irpasses.jl index c4e3023184c13..cb464a51ce45c 100644 --- a/test/compiler/irpasses.jl +++ b/test/compiler/irpasses.jl @@ -383,3 +383,45 @@ exc39508 = ErrorException("expected") return err end @test test39508() === exc39508 + +let # `getfield_elim_pass!` should work with constant globals + # immutable pass + src = @eval Module() begin + const REF_FLD = :x + struct ImmutableRef{T} + x::T + end + + code_typed((Int,)) do x + r = ImmutableRef{Int}(x) # should be eliminated + x = getfield(r, REF_FLD) # should be eliminated + return sin(x) + end |> only |> first + end + @test !any(src.code) do @nospecialize(stmt) + Meta.isexpr(stmt, :call) || return false + ft = Core.Compiler.argextype(stmt.args[1], src, Any[], src.slottypes) + return Core.Compiler.widenconst(ft) == typeof(getfield) + end + @test !any(src.code) do @nospecialize(stmt) + return Meta.isexpr(stmt, :new) + end + + # mutable pass + src = @eval Module() begin + const REF_FLD = :x + code_typed() do + r = Ref{Int}(42) # should be eliminated + x = getfield(r, REF_FLD) # should be eliminated + return sin(x) + end |> only |> first + end + @test !any(src.code) do @nospecialize(stmt) + Meta.isexpr(stmt, :call) || return false + ft = Core.Compiler.argextype(stmt.args[1], src, Any[], src.slottypes) + return Core.Compiler.widenconst(ft) == typeof(getfield) + end + @test !any(src.code) do @nospecialize(stmt) + return Meta.isexpr(stmt, :new) + end +end diff --git a/test/compiler/ssair.jl b/test/compiler/ssair.jl index 17a0753eddc64..ffb48a9de38e9 100644 --- a/test/compiler/ssair.jl +++ b/test/compiler/ssair.jl @@ -314,3 +314,23 @@ end # Issue #41975 - SSA conversion drops type check f_if_typecheck() = (if nothing; end; unsafe_load(Ptr{Int}(0))) @test_throws TypeError f_if_typecheck() + +@test let # https://github.com/JuliaLang/julia/issues/42258 + code = quote + function foo() + a = @noinline rand(rand(0:10)) + if isempty(a) + err = BoundsError(a) + throw(err) + return nothing + end + return a + end + code_typed(foo; optimize=true) + + code_typed(Core.Compiler.setindex!, (Core.Compiler.UseRef,Core.Compiler.NewSSAValue); optimize=true) + end |> string + cmd = `$(Base.julia_cmd()) -g 2 -e $code` + stderr = IOBuffer() + success(pipeline(Cmd(cmd); stdout=stdout, stderr=stderr)) && isempty(String(take!(stderr))) +end diff --git a/test/compiler/validation.jl b/test/compiler/validation.jl index 3863d3b11351f..6326cb709e30d 100644 --- a/test/compiler/validation.jl +++ b/test/compiler/validation.jl @@ -105,6 +105,14 @@ end @test errors[1].kind === Core.Compiler.SSAVALUETYPES_MISMATCH_UNINFERRED end +@testset "SSAFLAGS_MISMATCH" begin + c = copy(c0) + empty!(c.ssaflags) + errors = Core.Compiler.validate_code(c) + @test length(errors) == 1 + @test errors[1].kind === Core.Compiler.SSAFLAGS_MISMATCH +end + @testset "SIGNATURE_NARGS_MISMATCH" begin old_sig = mi.def.sig mi.def.sig = Tuple{1,2} diff --git a/test/embedding/embedding-test.jl b/test/embedding/embedding-test.jl index f358ff2a74cdf..797f6dabd9a89 100644 --- a/test/embedding/embedding-test.jl +++ b/test/embedding/embedding-test.jl @@ -20,8 +20,7 @@ end close(out.in) close(err.in) out_task = @async readlines(out) - err = read(err, String) - @test err == "MethodError: no method matching this_function_has_no_methods()\n" + @test readline(err) == "MethodError: no method matching this_function_has_no_methods()" @test success(p) lines = fetch(out_task) @test length(lines) == 10 @@ -29,4 +28,5 @@ end @test lines[8] == "called bar" @test lines[9] == "calling new bar" @test lines[10] == " From worker 2:\tTaking over the world..." + @test readline(err) == "exception caught from C" end diff --git a/test/embedding/embedding.c b/test/embedding/embedding.c index d1816947f3856..659ddde3f72df 100644 --- a/test/embedding/embedding.c +++ b/test/embedding/embedding.c @@ -175,6 +175,13 @@ int main() checked_eval_string("f28825()"); } + JL_TRY { + jl_error("exception thrown"); + } + JL_CATCH { + jl_printf(jl_stderr_stream(), "exception caught from C\n"); + } + int ret = 0; jl_atexit_hook(ret); return ret; diff --git a/test/errorshow.jl b/test/errorshow.jl index 137f15dd6b21a..80ad770de6f1e 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -728,7 +728,7 @@ end # Test that implementation detail of include() is hidden from the user by default let bt = try - include("testhelpers/include_error.jl") + @noinline include("testhelpers/include_error.jl") catch catch_backtrace() end @@ -740,7 +740,7 @@ end # Test backtrace printing module B module C - f(x; y=2.0) = error() + @noinline f(x; y=2.0) = error() end module D import ..C: f @@ -749,7 +749,8 @@ module B end @testset "backtrace" begin - bt = try B.D.g() + bt = try + B.D.g() catch catch_backtrace() end @@ -777,7 +778,8 @@ if Sys.isapple() || (Sys.islinux() && Sys.ARCH === :x86_64) pair_repeater_b() = pair_repeater_a() @testset "repeated stack frames" begin - let bt = try single_repeater() + let bt = try + single_repeater() catch catch_backtrace() end @@ -785,7 +787,8 @@ if Sys.isapple() || (Sys.islinux() && Sys.ARCH === :x86_64) @test occursin(r"repeats \d+ times", bt_str) end - let bt = try pair_repeater_a() + let bt = try + pair_repeater_a() catch catch_backtrace() end diff --git a/test/iterators.jl b/test/iterators.jl index c7d00c4e7e2e8..86c325a85b617 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -293,11 +293,14 @@ let (a, b) = (1:3, [4 6; end # collect stateful iterator -let - itr = (i+1 for i in Base.Stateful([1,2,3])) +let itr + itr = Iterators.Stateful(Iterators.map(identity, 1:5)) + @test collect(itr) == 1:5 + @test collect(itr) == Int[] # Stateful do not preserve shape + itr = (i+1 for i in Base.Stateful([1, 2, 3])) @test collect(itr) == [2, 3, 4] - A = zeros(Int, 0, 0) - itr = (i-1 for i in Base.Stateful(A)) + @test collect(itr) == Int[] # Stateful do not preserve shape + itr = (i-1 for i in Base.Stateful(zeros(Int, 0, 0))) @test collect(itr) == Int[] # Stateful do not preserve shape end diff --git a/test/llvmpasses/Makefile b/test/llvmpasses/Makefile index ef583fd451f07..a0b9cf977ede8 100644 --- a/test/llvmpasses/Makefile +++ b/test/llvmpasses/Makefile @@ -2,13 +2,13 @@ SRCDIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) JULIAHOME := $(abspath $(SRCDIR)/../..) include $(JULIAHOME)/Make.inc -check: $(SRCDIR) +check: . TESTS = $(patsubst $(SRCDIR)/%,%,$(wildcard $(SRCDIR)/*.jl $(SRCDIR)/*.ll)) -$(SRCDIR) $(TESTS): +. $(TESTS): PATH=$(build_bindir):$(build_depsbindir):$$PATH \ LD_LIBRARY_PATH=${build_libdir}:$$LD_LIBRARY_PATH \ - $(build_depsbindir)/lit/lit.py -v $@ + $(build_depsbindir)/lit/lit.py -v $(addprefix $(SRCDIR)/,$@) -.PHONY: $(TESTS) $(SRCDIR) check all +.PHONY: $(TESTS) check all . diff --git a/test/llvmpasses/alloc-opt-gcframe.jl b/test/llvmpasses/alloc-opt-gcframe.jl index 227569a545adb..b08e668e1a201 100644 --- a/test/llvmpasses/alloc-opt-gcframe.jl +++ b/test/llvmpasses/alloc-opt-gcframe.jl @@ -12,7 +12,7 @@ target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" # CHECK-LABEL: @return_obj # CHECK-NOT: @julia.gc_alloc_obj -# CHECK: %v = call noalias nonnull {} addrspace(10)* @jl_gc_pool_alloc +# CHECK: %v = call noalias nonnull {} addrspace(10)* @ijl_gc_pool_alloc # CHECK: store atomic {} addrspace(10)* @tag, {} addrspace(10)* addrspace(10)* {{.*}} unordered, align 8, !tbaa !0 println(""" define {} addrspace(10)* @return_obj() { @@ -52,7 +52,7 @@ define i64 @return_load(i64 %i) { # CHECK: call {}*** @julia.get_pgcstack() # CHECK: call {}*** @julia.ptls_states() # CHECK-NOT: @julia.gc_alloc_obj -# CHECK: @jl_gc_pool_alloc +# CHECK: @ijl_gc_pool_alloc # CHECK: store atomic {} addrspace(10)* @tag, {} addrspace(10)* addrspace(10)* {{.*}} unordered, align 8, !tbaa !0 println(""" define void @ccall_obj(i8* %fptr) { @@ -98,7 +98,7 @@ define void @ccall_ptr(i8* %fptr) { # CHECK: call {}*** @julia.get_pgcstack() # CHECK: call {}*** @julia.ptls_states() # CHECK-NOT: @julia.gc_alloc_obj -# CHECK: @jl_gc_pool_alloc +# CHECK: @ijl_gc_pool_alloc # CHECK: store atomic {} addrspace(10)* @tag, {} addrspace(10)* addrspace(10)* {{.*}} unordered, align 8, !tbaa !0 println(""" define void @ccall_unknown_bundle(i8* %fptr) { @@ -262,8 +262,8 @@ L3: """) # CHECK-LABEL: }{{$}} -# CHECK: declare noalias nonnull {} addrspace(10)* @jl_gc_pool_alloc(i8*, -# CHECK: declare noalias nonnull {} addrspace(10)* @jl_gc_big_alloc(i8*, +# CHECK: declare noalias nonnull {} addrspace(10)* @ijl_gc_pool_alloc(i8*, +# CHECK: declare noalias nonnull {} addrspace(10)* @ijl_gc_big_alloc(i8*, println(""" declare void @external_function() declare {}*** @julia.ptls_states() diff --git a/test/llvmpasses/final-lower-gc.ll b/test/llvmpasses/final-lower-gc.ll index e29ada14a0d00..6caf6dead7038 100644 --- a/test/llvmpasses/final-lower-gc.ll +++ b/test/llvmpasses/final-lower-gc.ll @@ -3,11 +3,9 @@ @tag = external addrspace(10) global {} declare void @boxed_simple({} addrspace(10)*, {} addrspace(10)*) -declare {} addrspace(10)* @jl_box_int64(i64) +declare {} addrspace(10)* @ijl_box_int64(i64) declare {}*** @julia.ptls_states() declare {}*** @julia.get_pgcstack() -declare void @jl_safepoint() -declare {} addrspace(10)* @jl_apply_generic({} addrspace(10)*, {} addrspace(10)**, i32) declare noalias nonnull {} addrspace(10)** @julia.new_gc_frame(i32) declare void @julia.push_gc_frame({} addrspace(10)**, i32) @@ -34,11 +32,11 @@ top: ; CHECK-DAG: [[GCFRAME_SLOT2:%.*]] = bitcast {}*** [[GCFRAME_SLOT]] to {} addrspace(10)*** ; CHECK-NEXT: store {} addrspace(10)** %gcframe, {} addrspace(10)*** [[GCFRAME_SLOT2]], align 8 call void @julia.push_gc_frame({} addrspace(10)** %gcframe, i32 2) - %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a) + %aboxed = call {} addrspace(10)* @ijl_box_int64(i64 signext %a) ; CHECK: %frame_slot_1 = getelementptr inbounds {} addrspace(10)*, {} addrspace(10)** %gcframe, i32 3 %frame_slot_1 = call {} addrspace(10)** @julia.get_gc_frame_slot({} addrspace(10)** %gcframe, i32 1) store {} addrspace(10)* %aboxed, {} addrspace(10)** %frame_slot_1, align 8 - %bboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %b) + %bboxed = call {} addrspace(10)* @ijl_box_int64(i64 signext %b) ; CHECK: %frame_slot_2 = getelementptr inbounds {} addrspace(10)*, {} addrspace(10)** %gcframe, i32 2 %frame_slot_2 = call {} addrspace(10)** @julia.get_gc_frame_slot({} addrspace(10)** %gcframe, i32 0) store {} addrspace(10)* %bboxed, {} addrspace(10)** %frame_slot_2, align 8 @@ -59,7 +57,7 @@ top: %pgcstack = call {}*** @julia.get_pgcstack() %ptls = call {}*** @julia.ptls_states() %ptls_i8 = bitcast {}*** %ptls to i8* -; CHECK: %v = call noalias nonnull {} addrspace(10)* @jl_gc_pool_alloc +; CHECK: %v = call noalias nonnull {} addrspace(10)* @ijl_gc_pool_alloc %v = call {} addrspace(10)* @julia.gc_alloc_bytes(i8* %ptls_i8, i64 8) %0 = bitcast {} addrspace(10)* %v to {} addrspace(10)* addrspace(10)* %1 = getelementptr {} addrspace(10)*, {} addrspace(10)* addrspace(10)* %0, i64 -1 diff --git a/test/llvmpasses/lit.cfg.py b/test/llvmpasses/lit.cfg.py index 2054876ed9a5d..f53854faf2559 100644 --- a/test/llvmpasses/lit.cfg.py +++ b/test/llvmpasses/lit.cfg.py @@ -13,8 +13,6 @@ config.substitutions.append(('%shlibext', '.dylib' if platform.system() == 'Darwin' else '.dll' if platform.system() == 'Windows' else '.so')) -path = os.path.pathsep.join((os.path.join(os.path.dirname(__file__),"../../usr/tools"), os.path.join(os.path.dirname(__file__),"../../usr/bin"), config.environment['PATH'])) -config.environment['PATH'] = path config.environment['HOME'] = "/tmp" if platform.machine() == "x86_64": diff --git a/test/llvmpasses/lower-handlers.ll b/test/llvmpasses/lower-handlers.ll index d9d5ac087b773..4191fa664c6cd 100644 --- a/test/llvmpasses/lower-handlers.ll +++ b/test/llvmpasses/lower-handlers.ll @@ -2,7 +2,7 @@ attributes #1 = { returns_twice } declare i32 @julia.except_enter() #1 -declare void @jl_pop_handler(i32) +declare void @ijl_pop_handler(i32) declare i8**** @julia.ptls_states() declare i8**** @julia.get_pgcstack() @@ -10,7 +10,7 @@ define void @simple() { top: %pgcstack = call i8**** @julia.get_pgcstack() ; CHECK: call void @llvm.lifetime.start -; CHECK: call void @jl_enter_handler +; CHECK: call void @ijl_enter_handler ; CHECK: setjmp %r = call i32 @julia.except_enter() %cmp = icmp eq i32 %r, 0 @@ -20,7 +20,7 @@ try: catch: br label %after after: - call void @jl_pop_handler(i32 1) + call void @ijl_pop_handler(i32 1) ; CHECK: llvm.lifetime.end ret void } diff --git a/test/llvmpasses/refinements.ll b/test/llvmpasses/refinements.ll index b883a53554a0c..40dd020bca260 100644 --- a/test/llvmpasses/refinements.ll +++ b/test/llvmpasses/refinements.ll @@ -5,7 +5,7 @@ declare {}*** @julia.ptls_states() declare {}*** @julia.get_pgcstack() declare void @jl_safepoint() declare void @one_arg_boxed({} addrspace(10)*) -declare {} addrspace(10)* @jl_box_int64(i64) +declare {} addrspace(10)* @ijl_box_int64(i64) define void @argument_refinement({} addrspace(10)* %a) { ; CHECK-LABEL: @argument_refinement @@ -26,7 +26,7 @@ define void @heap_refinement1(i64 %a) { ; CHECK: %gcframe = alloca {} addrspace(10)*, i32 3 %pgcstack = call {}*** @julia.get_pgcstack() %ptls = call {}*** @julia.ptls_states() - %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a) + %aboxed = call {} addrspace(10)* @ijl_box_int64(i64 signext %a) %casted1 = bitcast {} addrspace(10)* %aboxed to {} addrspace(10)* addrspace(10)* %loaded1 = load {} addrspace(10)*, {} addrspace(10)* addrspace(10)* %casted1, !tbaa !1 ; CHECK: store {} addrspace(10)* %aboxed @@ -43,7 +43,7 @@ define void @heap_refinement2(i64 %a) { ; CHECK: %gcframe = alloca {} addrspace(10)*, i32 3 %pgcstack = call {}*** @julia.get_pgcstack() %ptls = call {}*** @julia.ptls_states() - %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a) + %aboxed = call {} addrspace(10)* @ijl_box_int64(i64 signext %a) %casted1 = bitcast {} addrspace(10)* %aboxed to {} addrspace(10)* addrspace(10)* %loaded1 = load {} addrspace(10)*, {} addrspace(10)* addrspace(10)* %casted1, !tbaa !1 ; CHECK: store {} addrspace(10)* %loaded1 @@ -211,7 +211,7 @@ declare void @julia.write_barrier({} addrspace(10)*, {} addrspace(10)*) #1 define {} addrspace(10)* @setfield({} addrspace(10)* %p) { ; CHECK-LABEL: @setfield( ; CHECK-NOT: %gcframe -; CHECK: call void @jl_gc_queue_root +; CHECK: call void @ijl_gc_queue_root %pgcstack = call {}*** @julia.get_pgcstack() %ptls = call {}*** @julia.ptls_states() %c = call {} addrspace(10)* @allocate_some_value() diff --git a/test/syntax.jl b/test/syntax.jl index 09a91a31305f3..14b17d6eaa0b2 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -2966,3 +2966,14 @@ end @generated g25678(x) = return :x @test g25678(7) === 7 + +# issue 42220 +macro m42220() + return quote + function foo(::Type{T}=Float64) where {T} + return Vector{T}(undef, 10) + end + end +end +@test @m42220()() isa Vector{Float64} +@test @m42220()(Bool) isa Vector{Bool} diff --git a/test/testhelpers/coverage_file.info b/test/testhelpers/coverage_file.info index 1dea941fb2441..c83e75dee8060 100644 --- a/test/testhelpers/coverage_file.info +++ b/test/testhelpers/coverage_file.info @@ -10,9 +10,9 @@ DA:11,1 DA:12,1 DA:14,0 DA:17,1 -DA:19,1 +DA:19,2 DA:20,1 DA:22,1 -LH:10 -LF:13 +LH:12 +LF:14 end_of_record diff --git a/test/testhelpers/coverage_file.info.bad b/test/testhelpers/coverage_file.info.bad index d4d81eda26787..311f6379381ee 100644 --- a/test/testhelpers/coverage_file.info.bad +++ b/test/testhelpers/coverage_file.info.bad @@ -11,7 +11,7 @@ DA:12,1 DA:14,0 DA:17,1 DA:18,0 -DA:19,1 +DA:19,2 DA:20,1 DA:22,1 DA:1234,0 diff --git a/test/worlds.jl b/test/worlds.jl index 2b4f575e1905a..c8d671bea63d8 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -107,8 +107,24 @@ end g265() = [f265(x) for x in 1:3.] wc265 = get_world_counter() -f265(::Any) = 1.0 -@test wc265 + 1 == get_world_counter() +wc265_41332a = Task(tls_world_age) +@test tls_world_age() == wc265 +(function () + global wc265_41332b = Task(tls_world_age) + @eval f265(::Any) = 1.0 + global wc265_41332c = Base.invokelatest(Task, tls_world_age) + global wc265_41332d = Task(tls_world_age) + nothing +end)() +@test wc265 + 2 == get_world_counter() == tls_world_age() +schedule(wc265_41332a) +schedule(wc265_41332b) +schedule(wc265_41332c) +schedule(wc265_41332d) +@test wc265 == fetch(wc265_41332a) +@test wc265 + 1 == fetch(wc265_41332b) +@test wc265 + 2 == fetch(wc265_41332c) +@test wc265 + 1 == fetch(wc265_41332d) chnls, tasks = Base.channeled_tasks(2, wfunc) t265 = tasks[1]