From 32bee7ccb9e2a1dd4b14a4b5497f75223825ae8e Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 10 Oct 2024 16:10:33 +0200 Subject: [PATCH 01/76] irrationals: restrict assume effects annotations to known types (#55886) Other changes: * replace `:total` with the less powerful `:foldable` * add an `<:Integer` dispatch constraint on the `rationalize` method, closes #55872 * replace `Rational{<:Integer}` with just `Rational`, they're equal Other issues, related to `BigFloat` precision, are still present in irrationals.jl, to be fixed by followup PRs, including #55853. Fixes #55874 (cherry picked from commit d60837f29e861fad4afe6c29dba788e44b627bf4) --- base/irrationals.jl | 22 ++++++++++++++-------- base/mathconstants.jl | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/base/irrationals.jl b/base/irrationals.jl index eafe388162353..dc583f8d5b849 100644 --- a/base/irrationals.jl +++ b/base/irrationals.jl @@ -51,8 +51,7 @@ AbstractFloat(x::AbstractIrrational) = Float64(x)::Float64 Float16(x::AbstractIrrational) = Float16(Float32(x)::Float32) Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x)) -# XXX this may change `DEFAULT_PRECISION`, thus not effect free -@assume_effects :total function Rational{T}(x::AbstractIrrational) where T<:Integer +function _irrational_to_rational(::Type{T}, x::AbstractIrrational) where T<:Integer o = precision(BigFloat) p = 256 while true @@ -66,13 +65,16 @@ Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x)) p += 32 end end -Rational{BigInt}(x::AbstractIrrational) = throw(ArgumentError("Cannot convert an AbstractIrrational to a Rational{BigInt}: use rationalize(BigInt, x) instead")) +Rational{T}(x::AbstractIrrational) where {T<:Integer} = _irrational_to_rational(T, x) +_throw_argument_error_irrational_to_rational_bigint() = throw(ArgumentError("Cannot convert an AbstractIrrational to a Rational{BigInt}: use rationalize(BigInt, x) instead")) +Rational{BigInt}(::AbstractIrrational) = _throw_argument_error_irrational_to_rational_bigint() -@assume_effects :total function (t::Type{T})(x::AbstractIrrational, r::RoundingMode) where T<:Union{Float32,Float64} +function _irrational_to_float(::Type{T}, x::AbstractIrrational, r::RoundingMode) where T<:Union{Float32,Float64} setprecision(BigFloat, 256) do T(BigFloat(x)::BigFloat, r) end end +(::Type{T})(x::AbstractIrrational, r::RoundingMode) where {T<:Union{Float32,Float64}} = _irrational_to_float(T, x, r) float(::Type{<:AbstractIrrational}) = Float64 @@ -110,14 +112,18 @@ end <=(x::AbstractFloat, y::AbstractIrrational) = x < y # Irrational vs Rational -@assume_effects :total function rationalize(::Type{T}, x::AbstractIrrational; tol::Real=0) where T +function _rationalize_irrational(::Type{T}, x::AbstractIrrational, tol::Real) where {T<:Integer} return rationalize(T, big(x), tol=tol) end -@assume_effects :total function lessrational(rx::Rational{<:Integer}, x::AbstractIrrational) - # an @assume_effects :total version of `<` for determining if the rationalization of - # an irrational number required rounding up or down +function rationalize(::Type{T}, x::AbstractIrrational; tol::Real=0) where {T<:Integer} + return _rationalize_irrational(T, x, tol) +end +function _lessrational(rx::Rational, x::AbstractIrrational) return rx < big(x) end +function lessrational(rx::Rational, x::AbstractIrrational) + return _lessrational(rx, x) +end function <(x::AbstractIrrational, y::Rational{T}) where T T <: Unsigned && x < 0.0 && return true rx = rationalize(T, x) diff --git a/base/mathconstants.jl b/base/mathconstants.jl index 4bb8c409acf00..de6b98cea634d 100644 --- a/base/mathconstants.jl +++ b/base/mathconstants.jl @@ -16,6 +16,26 @@ Base.@irrational γ euler Base.@irrational φ (1+sqrt(big(5)))/2 Base.@irrational catalan catalan +const _KnownIrrational = Union{ + typeof(π), typeof(ℯ), typeof(γ), typeof(φ), typeof(catalan) +} + +function Rational{BigInt}(::_KnownIrrational) + Base._throw_argument_error_irrational_to_rational_bigint() +end +Base.@assume_effects :foldable function Rational{T}(x::_KnownIrrational) where {T<:Integer} + Base._irrational_to_rational(T, x) +end +Base.@assume_effects :foldable function (::Type{T})(x::_KnownIrrational, r::RoundingMode) where {T<:Union{Float32,Float64}} + Base._irrational_to_float(T, x, r) +end +Base.@assume_effects :foldable function rationalize(::Type{T}, x::_KnownIrrational; tol::Real=0) where {T<:Integer} + Base._rationalize_irrational(T, x, tol) +end +Base.@assume_effects :foldable function Base.lessrational(rx::Rational, x::_KnownIrrational) + Base._lessrational(rx, x) +end + # aliases """ π From 4fe24a21b137ba9ddff02a51a9f682db28fe28fa Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 10 Oct 2024 16:56:22 +0200 Subject: [PATCH 02/76] update `hash` doc string: `widen` not required any more (#55867) Implementing `widen` isn't a requirement any more, since #26022. (cherry picked from commit e95860c8595934af535398d160a2b461eeccffc5) --- base/hashing.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/base/hashing.jl b/base/hashing.jl index 7de9f47de3182..d4a6217de6edb 100644 --- a/base/hashing.jl +++ b/base/hashing.jl @@ -11,9 +11,7 @@ optional second argument `h` is another hash code to be mixed with the result. New types should implement the 2-argument form, typically by calling the 2-argument `hash` method recursively in order to mix hashes of the contents with each other (and with `h`). Typically, any type that implements `hash` should also implement its own [`==`](@ref) (hence -[`isequal`](@ref)) to guarantee the property mentioned above. Types supporting subtraction -(operator `-`) should also implement [`widen`](@ref), which is required to hash -values inside heterogeneous arrays. +[`isequal`](@ref)) to guarantee the property mentioned above. The hash value may change when a new Julia process is started. From 7c8fbbcf0b6e7ea045ffa14c61c1eca483f22b23 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 10 Oct 2024 18:36:31 +0200 Subject: [PATCH 03/76] slightly improve inference in precompilation code (#56084) Avoids the ``` 11: signature Tuple{typeof(convert), Type{String}, Any} triggered MethodInstance for Base.Precompilation.ExplicitEnv(::String) (84 children) ``` shown in https://github.com/JuliaLang/julia/issues/56080#issuecomment-2404765120 Co-authored-by: KristofferC (cherry picked from commit dc609a7328ffe42552dea8b79456d29557c08ac3) --- base/precompilation.jl | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index 406b6dee2f7c8..8390cf10bcbaf 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -42,7 +42,7 @@ function ExplicitEnv(envpath::String=Base.active_project()) # Collect all direct dependencies of the project for key in ["deps", "weakdeps", "extras"] - for (name, _uuid) in get(Dict{String, Any}, project_d, key)::Dict{String, Any} + for (name, _uuid::String) in get(Dict{String, Any}, project_d, key)::Dict{String, Any} v = key == "deps" ? project_deps : key == "weakdeps" ? project_weakdeps : key == "extras" ? project_extras : @@ -105,9 +105,8 @@ function ExplicitEnv(envpath::String=Base.active_project()) sizehint!(name_to_uuid, length(manifest_d)) sizehint!(lookup_strategy, length(manifest_d)) - for (name, pkg_infos) in get_deps(manifest_d) - pkg_infos = pkg_infos::Vector{Any} - for pkg_info in pkg_infos + for (name, pkg_infos::Vector{Any}) in get_deps(manifest_d) + for pkg_info::Dict{String, Any} in pkg_infos m_uuid = UUID(pkg_info["uuid"]::String) # If we have multiple packages with the same name we will overwrite things here @@ -139,8 +138,7 @@ function ExplicitEnv(envpath::String=Base.active_project()) # Extensions deps_pkg = get(Dict{String, Any}, pkg_info, "extensions")::Dict{String, Any} - for (ext, triggers) in deps_pkg - triggers = triggers::Union{String, Vector{String}} + for (ext, triggers::Union{String, Vector{String}}) in deps_pkg if triggers isa String triggers = [triggers] end @@ -174,7 +172,7 @@ function ExplicitEnv(envpath::String=Base.active_project()) if proj_name !== nothing && proj_uuid !== nothing deps_expanded[proj_uuid] = filter!(!=(proj_uuid), collect(values(project_deps))) extensions_expanded[proj_uuid] = project_extensions - path = get(project_d, "path", nothing) + path = get(project_d, "path", nothing)::Union{String, Nothing} entry_point = path !== nothing ? path : dirname(envpath) lookup_strategy[proj_uuid] = entry_point end From a36ea4c6134faccb2fef691cf042367e21046208 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 11 Oct 2024 13:51:51 +0200 Subject: [PATCH 04/76] make `Base.ANSIIterator` have a concrete field (#56088) Avoids the invalidation ``` backedges: 1: superseding sizeof(s::AbstractString) @ Base strings/basic.jl:177 with MethodInstance for sizeof(::AbstractString) (75 children) ``` shown in https://github.com/JuliaLang/julia/issues/56080#issuecomment-2404765120. Co-authored-by: KristofferC (cherry picked from commit 9844d854408d2312d70003fdde247c7195bffa4c) --- base/show.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base/show.jl b/base/show.jl index b9cda8d64ec36..ba0a6e5dfbf1a 100644 --- a/base/show.jl +++ b/base/show.jl @@ -72,13 +72,13 @@ ncodeunits(c::ANSIDelimiter) = ncodeunits(c.del) textwidth(::ANSIDelimiter) = 0 # An iterator similar to `pairs(::String)` but whose values are Char or ANSIDelimiter -struct ANSIIterator - captures::RegexMatchIterator +struct ANSIIterator{S} + captures::RegexMatchIterator{S} end ANSIIterator(s::AbstractString) = ANSIIterator(eachmatch(ansi_regex, s)) -IteratorSize(::Type{ANSIIterator}) = SizeUnknown() -eltype(::Type{ANSIIterator}) = Pair{Int, Union{Char,ANSIDelimiter}} +IteratorSize(::Type{<:ANSIIterator}) = SizeUnknown() +eltype(::Type{<:ANSIIterator}) = Pair{Int, Union{Char,ANSIDelimiter}} function iterate(I::ANSIIterator, (i, m_st)=(1, iterate(I.captures))) m_st === nothing && return nothing m, (j, new_m_st) = m_st From 7f6a0809ae4a5314de49f7adac41bd45be46972b Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 15 Oct 2024 02:02:11 +0200 Subject: [PATCH 05/76] Fix `JULIA_CPU_TARGET` being propagated to workers precompiling stdlib pkgimages (#54093) Apparently (thanks ChatGPT) each line in a makefile is executed in a separate shell so adding an `export` line on one line does not propagate to the next line. (cherry picked from commit b86e647159a4d9f1285e4f8c70a18e1b2bf2aa7d) --- pkgimage.mk | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgimage.mk b/pkgimage.mk index 740b9760cab48..0bc035ee03b08 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -25,8 +25,7 @@ print-depot-path: @$(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e '@show Base.DEPOT_PATH') $(BUILDDIR)/stdlib/%.image: $(JULIAHOME)/stdlib/Project.toml $(JULIAHOME)/stdlib/Manifest.toml $(INDEPENDENT_STDLIBS_SRCS) $(JULIA_DEPOT_PATH)/compiled - export JULIA_CPU_TARGET="$(JULIA_CPU_TARGET)" - @$(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.Precompilation.precompilepkgs(;configs=[``=>Base.CacheFlags(), `--check-bounds=yes`=>Base.CacheFlags(;check_bounds=1)])') + @$(call PRINT_JULIA, JULIA_CPU_TARGET="$(JULIA_CPU_TARGET)" $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.Precompilation.precompilepkgs(;configs=[``=>Base.CacheFlags(), `--check-bounds=yes`=>Base.CacheFlags(;check_bounds=1)])') touch $@ $(BUILDDIR)/stdlib/release.image: $(build_private_libdir)/sys.$(SHLIB_EXT) From 709e31cdc442bef119651b294100fef5551855d8 Mon Sep 17 00:00:00 2001 From: abhro <5664668+abhro@users.noreply.github.com> Date: Tue, 15 Oct 2024 03:30:52 -0400 Subject: [PATCH 06/76] Fix markdown list in installation.md (#56165) Documenter.jl requires all trailing list content to follow the same indentation as the header. So, in the current view (https://docs.julialang.org/en/v1/manual/installation/#Command-line-arguments) the list appears broken. (cherry picked from commit 9f92989274640b14d4d0015107e501e7252344b2) --- doc/src/manual/installation.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/src/manual/installation.md b/doc/src/manual/installation.md index 07acfd1c62681..f45aba2c37a28 100644 --- a/doc/src/manual/installation.md +++ b/doc/src/manual/installation.md @@ -44,21 +44,21 @@ curl -fsSL https://install.julialang.org | sh -s -- Here `` should be replaced with one or more of the following arguments: - `--yes` (or `-y`): Run the installer in a non-interactive mode. All -configuration values use their default or a value supplied as a command line -argument. + configuration values use their default or a value supplied as a command line + argument. - `--default-channel=`: Configure the default Juliaup channel. For -example `--default-channel lts` would install the `lts` channel and configure it -as the default. + example `--default-channel lts` would install the `lts` channel and configure it + as the default. - `--add-to-path=`: Configure whether Julia should be added to the `PATH` -environment variable. Valid values are `yes` (default) and `no`. + environment variable. Valid values are `yes` (default) and `no`. - `--background-selfupdate=`: Configure an optional CRON job that -auto-updates Juliaup if `` has a value larger than 0. The actual value -controls how often the CRON job will run to check for a new Juliaup version in -seconds. The default value is 0, i.e. no CRON job will be created. + auto-updates Juliaup if `` has a value larger than 0. The actual value + controls how often the CRON job will run to check for a new Juliaup version in + seconds. The default value is 0, i.e. no CRON job will be created. - `--startup-selfupdate=`: Configure how often Julia will check for new -versions of Juliaup when Julia is started. The default is every 1440 minutes. + versions of Juliaup when Julia is started. The default is every 1440 minutes. - `-p=` (or `--path`): Configure where the Julia and Juliaup binaries are -installed. The default is `~/.juliaup`. + installed. The default is `~/.juliaup`. ## Alternative installation methods From 276e66c57b642e36fe69c61d77b551f0c14f2a3b Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 15 Oct 2024 15:39:05 -0400 Subject: [PATCH 07/76] Make loading work when stdlib deps are missing in the manifest (#56148) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes https://github.com/JuliaLang/julia/issues/56109 Simulating a bad manifest by having `LibGit2_jll` missing as a dep of `LibGit2` in my default env, say because the manifest was generated by a different julia version or different master julia commit. ## This PR, it just works ``` julia> using Revise julia> ``` i.e. ``` % JULIA_DEBUG=loading ./julia --startup-file=no julia> using Revise ... ┌ Debug: Stdlib LibGit2 [76f85450-5226-5b5a-8eaa-529ad045b433] is trying to load `LibGit2_jll` │ which is not listed as a dep in the load path manifests, so resorting to search │ in the stdlib Project.tomls for true deps └ @ Base loading.jl:387 ┌ Debug: LibGit2 [76f85450-5226-5b5a-8eaa-529ad045b433] indeed depends on LibGit2_jll in project /Users/ian/Documents/GitHub/julia/usr/share/julia/stdlib/v1.12/LibGit2/Project.toml └ @ Base loading.jl:395 ... julia> ``` ## Master ``` julia> using Revise Info Given Revise was explicitly requested, output will be shown live ERROR: LoadError: ArgumentError: Package LibGit2 does not have LibGit2_jll in its dependencies: - Note that the following manifests in the load path were resolved with a potentially different DEV version of the current version, which may be the cause of the error. Try to re-resolve them in the current version, or consider deleting them if that fails: /Users/ian/.julia/environments/v1.12/Manifest.toml - You may have a partially installed environment. Try `Pkg.instantiate()` to ensure all packages in the environment are installed. - Or, if you have LibGit2 checked out for development and have added LibGit2_jll as a dependency but haven't updated your primary environment's manifest file, try `Pkg.resolve()`. - Otherwise you may need to report an issue with LibGit2 ... ``` (cherry picked from commit b02d6715d3be345962a312d6080bed7c732a4300) --- base/loading.jl | 37 ++++++++++++++ test/loading.jl | 12 +++++ test/project/deps/BadStdlibDeps/Manifest.toml | 51 +++++++++++++++++++ test/project/deps/BadStdlibDeps/Project.toml | 2 + 4 files changed, 102 insertions(+) create mode 100644 test/project/deps/BadStdlibDeps/Manifest.toml create mode 100644 test/project/deps/BadStdlibDeps/Project.toml diff --git a/base/loading.jl b/base/loading.jl index afbeeccec3bdd..45553153f7c0e 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -309,6 +309,21 @@ function find_package(arg) # ::Union{Nothing,String} return locate_package(pkg, env) end +# is there a better/faster ground truth? +function is_stdlib(pkgid::PkgId) + pkgid.name in readdir(Sys.STDLIB) || return false + stdlib_root = joinpath(Sys.STDLIB, pkgid.name) + project_file = locate_project_file(stdlib_root) + if project_file isa String + d = parsed_toml(project_file) + uuid = get(d, "uuid", nothing) + if uuid !== nothing + return UUID(uuid) == pkgid.uuid + end + end + return false +end + """ Base.identify_package_env(name::String)::Union{Tuple{PkgId, String}, Nothing} Base.identify_package_env(where::Union{Module,PkgId}, name::String)::Union{Tuple{PkgId, Union{String, Nothing}}, Nothing} @@ -337,6 +352,12 @@ function identify_package_env(where::PkgId, name::String) end break # found in implicit environment--return "not found" end + if pkg_env === nothing && is_stdlib(where) + # if not found it could be that manifests are from a different julia version/commit + # where stdlib dependencies have changed, so look up deps based on the stdlib Project.toml + # as a fallback + pkg_env = identify_stdlib_project_dep(where, name) + end end if cache !== nothing cache.identified_where[(where, name)] = pkg_env @@ -363,6 +384,22 @@ function identify_package_env(name::String) return pkg_env end +function identify_stdlib_project_dep(stdlib::PkgId, depname::String) + @debug """ + Stdlib $(repr("text/plain", stdlib)) is trying to load `$depname` + which is not listed as a dep in the load path manifests, so resorting to search + in the stdlib Project.tomls for true deps""" + stdlib_projfile = locate_project_file(joinpath(Sys.STDLIB, stdlib.name)) + stdlib_projfile === nothing && return nothing + found = explicit_project_deps_get(stdlib_projfile, depname) + if found !== nothing + @debug "$(repr("text/plain", stdlib)) indeed depends on $depname in project $stdlib_projfile" + pkgid = PkgId(found, depname) + return pkgid, stdlib_projfile + end + return nothing +end + _nothing_or_first(x) = x === nothing ? nothing : first(x) """ diff --git a/test/loading.jl b/test/loading.jl index c0f1d04df5ff9..c328e0b8d2803 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1325,6 +1325,18 @@ end end end +@testset "Fallback for stdlib deps if manifest deps aren't found" begin + mktempdir() do depot + # This manifest has a LibGit2 entry that is missing LibGit2_jll, which should be + # handled by falling back to the stdlib Project.toml for dependency truth. + badmanifest_test_dir = joinpath(@__DIR__, "project", "deps", "BadStdlibDeps.jl") + @test success(addenv( + `$(Base.julia_cmd()) --project=$badmanifest_test_dir --startup-file=no -e 'using LibGit2'`, + "JULIA_DEPOT_PATH" => depot * Base.Filesystem.pathsep(), + )) + end +end + @testset "code coverage disabled during precompilation" begin mktempdir() do depot cov_test_dir = joinpath(@__DIR__, "project", "deps", "CovTest.jl") diff --git a/test/project/deps/BadStdlibDeps/Manifest.toml b/test/project/deps/BadStdlibDeps/Manifest.toml new file mode 100644 index 0000000000000..32aaa0b83dc0a --- /dev/null +++ b/test/project/deps/BadStdlibDeps/Manifest.toml @@ -0,0 +1,51 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.12.0-DEV" +manifest_format = "2.0" +project_hash = "dc9d33b0ee13d9466bdb75b8d375808a534a79ec" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" +version = "1.11.0" + +# This is intentionally missing LibGit2_jll for testing purposes +[[deps.LibGit2]] +deps = ["NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +version = "1.11.0" + +[[deps.LibGit2_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] +uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" +version = "1.8.0+0" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.11.0+1" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" +version = "1.11.0" + +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.6+1" + +[[deps.NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" + +[[deps.Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" +version = "1.11.0" + +[[deps.SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" + +[[deps.Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" +version = "1.11.0" diff --git a/test/project/deps/BadStdlibDeps/Project.toml b/test/project/deps/BadStdlibDeps/Project.toml new file mode 100644 index 0000000000000..223889185ea15 --- /dev/null +++ b/test/project/deps/BadStdlibDeps/Project.toml @@ -0,0 +1,2 @@ +[deps] +LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433" From b53bbe5ce3bbfc00e6d27e18bf8775beecc60143 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 15 Oct 2024 20:22:33 -0400 Subject: [PATCH 08/76] Fix implicit `convert(String, ...)` in several places (#56174) This removes several `convert(String, ...)` from this code, which really shouldn't be something we invalidate on in the first place (see https://github.com/JuliaLang/julia/issues/56173) but this is still an improvement in code quality so let's take it. (cherry picked from commit a7521ea86952168c2f342b4c2275340ea7726a94) --- base/precompilation.jl | 21 +++++++++++++-------- base/regex.jl | 2 +- stdlib/REPL/src/LineEdit.jl | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index 8390cf10bcbaf..04605d83a2ca0 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -42,12 +42,12 @@ function ExplicitEnv(envpath::String=Base.active_project()) # Collect all direct dependencies of the project for key in ["deps", "weakdeps", "extras"] - for (name, _uuid::String) in get(Dict{String, Any}, project_d, key)::Dict{String, Any} + for (name, _uuid) in get(Dict{String, Any}, project_d, key)::Dict{String, Any} v = key == "deps" ? project_deps : key == "weakdeps" ? project_weakdeps : key == "extras" ? project_extras : error() - uuid = UUID(_uuid) + uuid = UUID(_uuid::String) v[name] = uuid names[UUID(uuid)] = name project_uuid_to_name[name] = UUID(uuid) @@ -61,9 +61,11 @@ function ExplicitEnv(envpath::String=Base.active_project()) project_extensions = Dict{String, Vector{UUID}}() # Collect all extensions of the project - for (name, triggers::Union{String, Vector{String}}) in get(Dict{String, Any}, project_d, "extensions")::Dict{String, Any} + for (name, triggers) in get(Dict{String, Any}, project_d, "extensions")::Dict{String, Any} if triggers isa String triggers = [triggers] + else + triggers = triggers::Vector{String} end uuids = UUID[] for trigger in triggers @@ -105,8 +107,9 @@ function ExplicitEnv(envpath::String=Base.active_project()) sizehint!(name_to_uuid, length(manifest_d)) sizehint!(lookup_strategy, length(manifest_d)) - for (name, pkg_infos::Vector{Any}) in get_deps(manifest_d) - for pkg_info::Dict{String, Any} in pkg_infos + for (name, pkg_infos) in get_deps(manifest_d) + for pkg_info in pkg_infos::Vector{Any} + pkg_info = pkg_info::Dict{String, Any} m_uuid = UUID(pkg_info["uuid"]::String) # If we have multiple packages with the same name we will overwrite things here @@ -127,8 +130,8 @@ function ExplicitEnv(envpath::String=Base.active_project()) # Expanded format: else uuids = UUID[] - for (name_dep, _dep_uuid::String) in deps_pkg - dep_uuid = UUID(_dep_uuid) + for (name_dep, _dep_uuid) in deps_pkg + dep_uuid = UUID(_dep_uuid::String) push!(uuids, dep_uuid) names[dep_uuid] = name_dep end @@ -138,9 +141,11 @@ function ExplicitEnv(envpath::String=Base.active_project()) # Extensions deps_pkg = get(Dict{String, Any}, pkg_info, "extensions")::Dict{String, Any} - for (ext, triggers::Union{String, Vector{String}}) in deps_pkg + for (ext, triggers) in deps_pkg if triggers isa String triggers = [triggers] + else + triggers = triggers::Vector{String} end deps_pkg[ext] = triggers end diff --git a/base/regex.jl b/base/regex.jl index 78eefa1741b0c..9d5c146a6e840 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -28,7 +28,7 @@ mutable struct Regex <: AbstractPattern function Regex(pattern::AbstractString, compile_options::Integer, match_options::Integer) - pattern = String(pattern) + pattern = String(pattern)::String compile_options = UInt32(compile_options) match_options = UInt32(match_options) if (compile_options & ~PCRE.COMPILE_MASK) != 0 diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 76e6ee42fd53b..ff5596183a05c 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -166,7 +166,7 @@ region_active(s::PromptState) = s.region_active region_active(s::ModeState) = :off -input_string(s::PromptState) = String(take!(copy(s.input_buffer))) +input_string(s::PromptState) = String(take!(copy(s.input_buffer)))::String input_string_newlines(s::PromptState) = count(c->(c == '\n'), input_string(s)) function input_string_newlines_aftercursor(s::PromptState) From b27d83e55565f1ba33f75a045da736ec59d7c0da Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 17 Oct 2024 07:27:24 -0400 Subject: [PATCH 09/76] Include default user depot when JULIA_DEPOT_PATH has leading empty entry (#56195) (cherry picked from commit af51bcc563e15023d9dacca099e323b32b8a266f) --- base/initdefs.jl | 11 +++++++---- doc/src/manual/environment-variables.md | 26 ++++++++++++++++--------- test/loading.jl | 2 +- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/base/initdefs.jl b/base/initdefs.jl index 96bdc7957bcca..950c09663a1bf 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -112,20 +112,23 @@ function init_depot_path() # otherwise, populate the depot path with the entries in JULIA_DEPOT_PATH, # expanding empty strings to the bundled depot - populated = false - for path in eachsplit(str, Sys.iswindows() ? ';' : ':') + pushfirst_default = true + for (i, path) in enumerate(eachsplit(str, Sys.iswindows() ? ';' : ':')) if isempty(path) append_bundled_depot_path!(DEPOT_PATH) else path = expanduser(path) path in DEPOT_PATH || push!(DEPOT_PATH, path) - populated = true + if i == 1 + # if a first entry is given, don't add the default depot at the start + pushfirst_default = false + end end end # backwards compatibility: if JULIA_DEPOT_PATH only contains empty entries # (e.g., JULIA_DEPOT_PATH=':'), make sure to use the default depot - if !populated + if pushfirst_default pushfirst!(DEPOT_PATH, joinpath(homedir(), ".julia")) end else diff --git a/doc/src/manual/environment-variables.md b/doc/src/manual/environment-variables.md index e1e450998f016..b26813ac45ead 100644 --- a/doc/src/manual/environment-variables.md +++ b/doc/src/manual/environment-variables.md @@ -130,17 +130,19 @@ environment variable or if it must have a value, set it to the string `:`. ### [`JULIA_DEPOT_PATH`](@id JULIA_DEPOT_PATH) -The [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH) environment variable is used to populate the global Julia -[`DEPOT_PATH`](@ref) variable, which controls where the package manager, as well -as Julia's code loading mechanisms, look for package registries, installed -packages, named environments, repo clones, cached compiled package images, -configuration files, and the default location of the REPL's history file. +The [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH) environment variable is used to populate the +global Julia [`DEPOT_PATH`](@ref) variable, which controls where the package manager, as well +as Julia's code loading mechanisms, look for package registries, installed packages, named +environments, repo clones, cached compiled package images, configuration files, and the default +location of the REPL's history file. Unlike the shell `PATH` variable but similar to [`JULIA_LOAD_PATH`](@ref JULIA_LOAD_PATH), -empty entries in [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH) are expanded to the default -value of `DEPOT_PATH`, excluding the user depot. This allows easy overriding of the user -depot, while still retaining access to resources that are bundled with Julia, like cache -files, artifacts, etc. For example, to switch the user depot to `/foo/bar` just do +empty entries in [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH) have special behavior: +- At the end, it is expanded to the default value of `DEPOT_PATH`, *excluding* the user depot. +- At the start, it is expanded to the default value of `DEPOT_PATH`, *including* the user depot. +This allows easy overriding of the user depot, while still retaining access to resources that +are bundled with Julia, like cache files, artifacts, etc. For example, to switch the user depot +to `/foo/bar` use a trailing `:` ```sh export JULIA_DEPOT_PATH="/foo/bar:" ``` @@ -150,6 +152,12 @@ resources will still be available. If you really only want to use the depot at ` and not load any bundled resources, simply set the environment variable to `/foo/bar` without the trailing colon. +To append a depot at the end of the full default list, including the default user depot, use a +leading `:` +```sh +export JULIA_DEPOT_PATH=":/foo/bar" +``` + There are two exceptions to the above rule. First, if [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH) is set to the empty string, it expands to an empty `DEPOT_PATH` array. In other words, the empty string is interpreted as a zero-element array, not a one-element diff --git a/test/loading.jl b/test/loading.jl index c328e0b8d2803..f06b0fd65ffc7 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -728,7 +728,7 @@ end "" => [], "$s" => [default; bundled], "$tmp$s" => [tmp; bundled], - "$s$tmp" => [bundled; tmp], + "$s$tmp" => [default; bundled; tmp], ) for (env, result) in pairs(cases) script = "DEPOT_PATH == $(repr(result)) || error(\"actual depot \" * join(DEPOT_PATH,':') * \" does not match expected depot \" * join($(repr(result)), ':'))" From 0bd77f576101668f2f47caf0b3c14a79e5892ef8 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 17 Oct 2024 15:50:28 -0400 Subject: [PATCH 10/76] [REPL] fix lock ordering mistake in load_pkg (#56215) Fixes #56206 (cherry picked from commit 1f935afc07edde9f8c2e1a0f05d4772e18a55e97) --- base/loading.jl | 1 + stdlib/REPL/src/Pkg_beforeload.jl | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 45553153f7c0e..6759bbab3f8cb 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2044,6 +2044,7 @@ debug_loading_deadlocks::Bool = true # Enable a slightly more expensive, but mor function start_loading(modkey::PkgId, build_id::UInt128, stalecheck::Bool) # handle recursive and concurrent calls to require assert_havelock(require_lock) + require_lock.reentrancy_cnt == 1 || throw(ConcurrencyViolationError("recursive call to start_loading")) while true loaded = stalecheck ? maybe_root_module(modkey) : nothing loaded isa Module && return loaded diff --git a/stdlib/REPL/src/Pkg_beforeload.jl b/stdlib/REPL/src/Pkg_beforeload.jl index c5f7f107fcadc..407ee5bf01e92 100644 --- a/stdlib/REPL/src/Pkg_beforeload.jl +++ b/stdlib/REPL/src/Pkg_beforeload.jl @@ -1,17 +1,16 @@ ## Pkg stuff needed before Pkg has loaded const Pkg_pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg") -const Pkg_REPLExt_pkgid = Base.PkgId(Base.UUID("ceef7b17-42e7-5b1c-81d4-4cc4a2494ccf"), "REPLExt") function load_pkg() + REPLExt = Base.require_stdlib(Pkg_pkgid, "REPLExt") @lock Base.require_lock begin - REPLExt = Base.require_stdlib(Pkg_pkgid, "REPLExt") # require_stdlib does not guarantee that the `__init__` of the package is done when loading is done async # but we need to wait for the repl mode to be set up - lock = get(Base.package_locks, Pkg_REPLExt_pkgid.uuid, nothing) + lock = get(Base.package_locks, Base.PkgId(REPLExt), nothing) lock !== nothing && wait(lock[2]) - return REPLExt end + return REPLExt end ## Below here copied/tweaked from Pkg Types.jl so that the dummy Pkg prompt From 9dda314b6ccef8bfc634e821051ec3c5b38b8df6 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 15 Oct 2024 03:07:19 -0400 Subject: [PATCH 11/76] Add invalidation barriers for `displaysize` and `implicit_typeinfo` (#56159) These are invalidated by our own stdlibs (Dates and REPL) unfortunately so we need to put this barrier in. This fix is _very_ un-satisfying, because it doesn't do anything to solve this problem for downstream libraries that use e.g. `displaysize`. To fix that, I think we need a way to make sure callers get these invalidation barriers by default... (cherry picked from commit 9223088faefd9680d8217b44d0bf82e478a311c1) --- base/arrayshow.jl | 16 +++++++++++----- base/logging/ConsoleLogger.jl | 2 +- base/precompilation.jl | 4 ++-- base/show.jl | 4 ++-- base/stream.jl | 7 +++++++ 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/base/arrayshow.jl b/base/arrayshow.jl index db639a88876e8..cea698526fb44 100644 --- a/base/arrayshow.jl +++ b/base/arrayshow.jl @@ -545,6 +545,12 @@ typeinfo_eltype(typeinfo::Type{<:AbstractArray{T}}) where {T} = eltype(typeinfo) typeinfo_eltype(typeinfo::Type{<:AbstractDict{K,V}}) where {K,V} = eltype(typeinfo) typeinfo_eltype(typeinfo::Type{<:AbstractSet{T}}) where {T} = eltype(typeinfo) +# This is a fancy way to make de-specialize a call to `typeinfo_implicit(T)` +# which is unfortunately invalidated by Dates +# (https://github.com/JuliaLang/julia/issues/56080) +# +# This makes the call less efficient, but avoids being invalidated by Dates. +_typeinfo_implicit(@nospecialize(T)) = Base.invoke_in_world(Base.tls_world_age(), typeinfo_implicit, T)::Bool # types that can be parsed back accurately from their un-decorated representations function typeinfo_implicit(@nospecialize(T)) @@ -553,9 +559,9 @@ function typeinfo_implicit(@nospecialize(T)) return true end return isconcretetype(T) && - ((T <: Array && typeinfo_implicit(eltype(T))) || - ((T <: Tuple || T <: Pair) && all(typeinfo_implicit, fieldtypes(T))) || - (T <: AbstractDict && typeinfo_implicit(keytype(T)) && typeinfo_implicit(valtype(T)))) + ((T <: Array && _typeinfo_implicit(eltype(T))) || + ((T <: Tuple || T <: Pair) && all(_typeinfo_implicit, fieldtypes(T))) || + (T <: AbstractDict && _typeinfo_implicit(keytype(T)) && _typeinfo_implicit(valtype(T)))) end # X not constrained, can be any iterable (cf. show_vector) @@ -573,7 +579,7 @@ function typeinfo_prefix(io::IO, X) if X isa AbstractDict if eltype_X == eltype_ctx sprint(show_type_name, typeof(X).name; context=io), false - elseif !isempty(X) && typeinfo_implicit(keytype(X)) && typeinfo_implicit(valtype(X)) + elseif !isempty(X) && _typeinfo_implicit(keytype(X)) && _typeinfo_implicit(valtype(X)) sprint(show_type_name, typeof(X).name; context=io), true else sprint(print, typeof(X); context=io), false @@ -582,7 +588,7 @@ function typeinfo_prefix(io::IO, X) # Types hard-coded here are those which are created by default for a given syntax if eltype_X == eltype_ctx "", false - elseif !isempty(X) && typeinfo_implicit(eltype_X) + elseif !isempty(X) && _typeinfo_implicit(eltype_X) "", true elseif print_without_params(eltype_X) sprint(show_type_name, unwrap_unionall(eltype_X).name; context=io), false # Print "Array" rather than "Array{T,N}" diff --git a/base/logging/ConsoleLogger.jl b/base/logging/ConsoleLogger.jl index c4596dd86c3f5..818b2272b773c 100644 --- a/base/logging/ConsoleLogger.jl +++ b/base/logging/ConsoleLogger.jl @@ -130,7 +130,7 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module if !(isopen(stream)::Bool) stream = stderr end - dsize = displaysize(stream)::Tuple{Int,Int} + dsize = Base.displaysize_(stream)::Tuple{Int,Int} nkwargs = length(kwargs)::Int if nkwargs > hasmaxlog valbuf = IOBuffer() diff --git a/base/precompilation.jl b/base/precompilation.jl index 04605d83a2ca0..34720a1078816 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -658,7 +658,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; n_print_rows = 0 while !printloop_should_exit lock(print_lock) do - term_size = Base.displaysize(io)::Tuple{Int,Int} + term_size = Base.displaysize_(io) num_deps_show = term_size[1] - 3 pkg_queue_show = if !interrupted_or_done.set && length(pkg_queue) > num_deps_show last(pkg_queue, num_deps_show) @@ -673,7 +673,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; bar.max = n_total - n_already_precomp # when sizing to the terminal width subtract a little to give some tolerance to resizing the # window between print cycles - termwidth = displaysize(io)[2] - 4 + termwidth = Base.displaysize_(io)[2] - 4 if !final_loop str = sprint(io -> show_progress(io, bar; termwidth, carriagereturn=false); context=io) print(iostr, Base._truncate_at_width_or_chars(true, str, termwidth), "\n") diff --git a/base/show.jl b/base/show.jl index ba0a6e5dfbf1a..724fc70f89678 100644 --- a/base/show.jl +++ b/base/show.jl @@ -427,7 +427,7 @@ get(io::IO, key, default) = default keys(io::IOContext) = keys(io.dict) keys(io::IO) = keys(ImmutableDict{Symbol,Any}()) -displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize]::Tuple{Int,Int} : displaysize(io.io) +displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize]::Tuple{Int,Int} : Base.displaysize_(io.io) show_circular(io::IO, @nospecialize(x)) = false function show_circular(io::IOContext, @nospecialize(x)) @@ -2608,7 +2608,7 @@ end function type_limited_string_from_context(out::IO, str::String) typelimitflag = get(out, :stacktrace_types_limited, nothing) if typelimitflag isa RefValue{Bool} - sz = get(out, :displaysize, displaysize(out))::Tuple{Int, Int} + sz = get(out, :displaysize, Base.displaysize_(out))::Tuple{Int, Int} str_lim = type_depth_limit(str, max(sz[2], 120)) if sizeof(str_lim) < sizeof(str) typelimitflag[] = true diff --git a/base/stream.jl b/base/stream.jl index d46c5fabb1940..a46027f803364 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -569,6 +569,13 @@ displaysize(io::IO) = displaysize() displaysize() = (parse(Int, get(ENV, "LINES", "24")), parse(Int, get(ENV, "COLUMNS", "80")))::Tuple{Int, Int} +# This is a fancy way to make de-specialize a call to `displaysize(io::IO)` +# which is unfortunately invalidated by REPL +# (https://github.com/JuliaLang/julia/issues/56080) +# +# This makes the call less efficient, but avoids being invalidated by REPL. +displaysize_(io::IO) = Base.invoke_in_world(Base.tls_world_age(), displaysize, io)::Tuple{Int,Int} + function displaysize(io::TTY) check_open(io) From 415294a560db01f3d89699d8f00bdcba51257e34 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 15 Oct 2024 09:25:56 +0530 Subject: [PATCH 12/76] Call `MulAddMul` instead of multiplication in _generic_matmatmul! (#56089) Fix https://github.com/JuliaLang/julia/issues/56085 by calling a newly created `MulAddMul` object that only wraps the `alpha` (with `beta` set to `false`). This avoids the explicit multiplication if `alpha` is known to be `isone`. (cherry picked from commit 0af99e641a4329b57e48a314e2cedb592e02cd3b) --- stdlib/LinearAlgebra/src/matmul.jl | 6 ++++-- stdlib/LinearAlgebra/test/matmul.jl | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 9c74addd6b69c..0a008256316d7 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -869,7 +869,7 @@ Base.@constprop :aggressive generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::A _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), _add) @noinline function _generic_matmatmul!(C::AbstractVecOrMat{R}, A::AbstractVecOrMat{T}, B::AbstractVecOrMat{S}, - _add::MulAddMul) where {T,S,R} + _add::MulAddMul{ais1}) where {T,S,R,ais1} AxM = axes(A, 1) AxK = axes(A, 2) # we use two `axes` calls in case of `AbstractVector` BxK = axes(B, 1) @@ -885,11 +885,13 @@ Base.@constprop :aggressive generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::A if BxN != CxN throw(DimensionMismatch(lazy"matrix B has axes ($BxK,$BxN), matrix C has axes ($CxM,$CxN)")) end + _rmul_alpha = MulAddMul{ais1,true,typeof(_add.alpha),Bool}(_add.alpha,false) if isbitstype(R) && sizeof(R) ≤ 16 && !(A isa Adjoint || A isa Transpose) _rmul_or_fill!(C, _add.beta) (iszero(_add.alpha) || isempty(A) || isempty(B)) && return C @inbounds for n in BxN, k in BxK - Balpha = B[k,n]*_add.alpha + # Balpha = B[k,n] * alpha, but we skip the multiplication in case isone(alpha) + Balpha = _rmul_alpha(B[k,n]) @simd for m in AxM C[m,n] = muladd(A[m,k], Balpha, C[m,n]) end diff --git a/stdlib/LinearAlgebra/test/matmul.jl b/stdlib/LinearAlgebra/test/matmul.jl index db61fbe0ab45a..aab535cbe0303 100644 --- a/stdlib/LinearAlgebra/test/matmul.jl +++ b/stdlib/LinearAlgebra/test/matmul.jl @@ -1107,4 +1107,22 @@ end end end +@testset "issue #56085" begin + struct Thing + data::Float64 + end + + Base.zero(::Type{Thing}) = Thing(0.) + Base.zero(::Thing) = Thing(0.) + Base.one(::Type{Thing}) = Thing(1.) + Base.one(::Thing) = Thing(1.) + Base.:+(t::Thing...) = +(getfield.(t, :data)...) + Base.:*(t::Thing...) = *(getfield.(t, :data)...) + + M = Float64[1 2; 3 4] + A = Thing.(M) + + @test A * A ≈ M * M +end + end # module TestMatmul From e20cc3e7f07c7018f4465a51239da56270d1c301 Mon Sep 17 00:00:00 2001 From: TEC Date: Thu, 12 Sep 2024 01:46:02 +0800 Subject: [PATCH 13/76] Change annotation type to a NamedTuple --- base/strings/annotated.jl | 226 +++++++++++++++++++++----------------- base/strings/io.jl | 4 +- doc/src/manual/strings.md | 4 +- test/strings/annotated.jl | 180 +++++++++++++++--------------- 4 files changed, 218 insertions(+), 196 deletions(-) diff --git a/base/strings/annotated.jl b/base/strings/annotated.jl index b54cbf855ba9b..c5c330fe0dfcd 100644 --- a/base/strings/annotated.jl +++ b/base/strings/annotated.jl @@ -1,5 +1,8 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +const Annotation = NamedTuple{(:label, :value), Tuple{Symbol, Any}} +const RegionAnnotation = NamedTuple{(:region, :label, :value), Tuple{UnitRange{Int}, Symbol, Any}} + """ AnnotatedString{S <: AbstractString} <: AbstractString @@ -20,7 +23,8 @@ annotated with labeled values. The above diagram represents a `AnnotatedString` where three ranges have been annotated (labeled `A`, `B`, and `C`). Each annotation holds a label (`Symbol`) -and a value (`Any`), paired together as a `Pair{Symbol, <:Any}`. +and a value (`Any`). These three pieces of information are held as a +`$RegionAnnotation`. Labels do not need to be unique, the same region can hold multiple annotations with the same label. @@ -43,7 +47,7 @@ See also [`AnnotatedChar`](@ref), [`annotatedstring`](@ref), ```julia AnnotatedString(s::S<:AbstractString) -> AnnotatedString{S} -AnnotatedString(s::S<:AbstractString, annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, <:Any}}}) +AnnotatedString(s::S<:AbstractString, annotations::Vector{$RegionAnnotation}) ``` A AnnotatedString can also be created with [`annotatedstring`](@ref), which acts much @@ -59,7 +63,7 @@ julia> AnnotatedString("this is an example annotated string", """ struct AnnotatedString{S <: AbstractString} <: AbstractString string::S - annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}} + annotations::Vector{RegionAnnotation} end """ @@ -68,8 +72,8 @@ end A Char with annotations. More specifically, this is a simple wrapper around any other -[`AbstractChar`](@ref), which holds a list of arbitrary labeled annotations -(`Pair{Symbol, <:Any}`) with the wrapped character. +[`AbstractChar`](@ref), which holds a list of arbitrary labelled annotations +(`$Annotation`) with the wrapped character. See also: [`AnnotatedString`](@ref), [`annotatedstring`](@ref), `annotations`, and `annotate!`. @@ -78,7 +82,7 @@ and `annotate!`. ```julia AnnotatedChar(s::S) -> AnnotatedChar{S} -AnnotatedChar(s::S, annotations::Vector{Pair{Symbol, <:Any}}) +AnnotatedChar(s::S, annotations::Vector{$Annotation}) ``` # Examples @@ -90,41 +94,48 @@ julia> AnnotatedChar('j', :label => 1) """ struct AnnotatedChar{C <: AbstractChar} <: AbstractChar char::C - annotations::Vector{Pair{Symbol, Any}} + annotations::Vector{Annotation} end ## Constructors ## # When called with overly-specialised arguments -AnnotatedString(s::AbstractString, annots::Vector{<:Tuple{UnitRange{Int}, <:Pair{Symbol, <:Any}}}) = - AnnotatedString(s, Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}(annots)) +AnnotatedString(s::AbstractString, annots::Vector) = + AnnotatedString(s, Vector{RegionAnnotation}(annots)) + +AnnotatedString(s::AbstractString, annots) = + AnnotatedString(s, collect(RegionAnnotation, annots)) -AnnotatedChar(c::AbstractChar, annots::Vector{<:Pair{Symbol, <:Any}}) = - AnnotatedChar(c, Vector{Pair{Symbol, Any}}(annots)) +AnnotatedChar(c::AbstractChar, annots::Vector) = + AnnotatedChar(c, Vector{Annotation}(annots)) + +AnnotatedChar(c::AbstractChar, annots) = + AnnotatedChar(c, collect(Annotation, annots)) # Constructors to avoid recursive wrapping -AnnotatedString(s::AnnotatedString, annots::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}) = +AnnotatedString(s::AnnotatedString, annots::Vector{RegionAnnotation}) = AnnotatedString(s.string, vcat(s.annotations, annots)) -AnnotatedChar(c::AnnotatedChar, annots::Vector{Pair{Symbol, Any}}) = - AnnotatedChar(c.char, vcat(c.annotations, annots)) +AnnotatedChar(c::AnnotatedChar, annots::Vector{Annotation}) = + AnnotatedChar(c.char, vcat(c.annotations, Vector{Annotation}(annots))) -String(s::AnnotatedString{String}) = s.string # To avoid pointless overhead +# To avoid pointless overhead +String(s::AnnotatedString{String}) = s.string ## Conversion/promotion ## convert(::Type{AnnotatedString}, s::AnnotatedString) = s convert(::Type{AnnotatedString{S}}, s::S) where {S <: AbstractString} = - AnnotatedString(s, Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}()) + AnnotatedString(s, Vector{RegionAnnotation}()) convert(::Type{AnnotatedString}, s::S) where {S <: AbstractString} = convert(AnnotatedString{S}, s) AnnotatedString(s::S) where {S <: AbstractString} = convert(AnnotatedString{S}, s) convert(::Type{AnnotatedChar}, c::AnnotatedChar) = c convert(::Type{AnnotatedChar{C}}, c::C) where { C <: AbstractChar } = - AnnotatedChar{C}(c, Vector{Pair{Symbol, Any}}()) + AnnotatedChar{C}(c, Vector{Annotation}()) convert(::Type{AnnotatedChar}, c::C) where { C <: AbstractChar } = convert(AnnotatedChar{C}, c) @@ -150,7 +161,7 @@ lastindex(s::AnnotatedString) = lastindex(s.string) function getindex(s::AnnotatedString, i::Integer) @boundscheck checkbounds(s, i) @inbounds if isvalid(s, i) - AnnotatedChar(s.string[i], map(last, annotations(s, i))) + AnnotatedChar(s.string[i], Annotation[(; label, value) for (; label, value) in annotations(s, i)]) else string_index_err(s, i) end @@ -164,7 +175,8 @@ function show(io::IO, s::A) where {A <: AnnotatedString} print(io, '(') show(io, s.string) print(io, ", ") - show(IOContext(io, :typeinfo => typeof(annotations(s))), annotations(s)) + tupanns = Vector{Tuple{UnitRange{Int}, Symbol, Any}}(map(values, s.annotations)) + show(IOContext(io, :typeinfo => typeof(tupanns)), tupanns) print(io, ')') end @@ -233,27 +245,27 @@ function annotatedstring(xs...) size = mapreduce(_str_sizehint, +, xs) buf = IOBuffer(sizehint=size) s = IOContext(buf, :color => true) - annotations = Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}() + annotations = Vector{RegionAnnotation}() for x in xs size = filesize(s.io) if x isa AnnotatedString - for (region, annot) in x.annotations - push!(annotations, (size .+ (region), annot)) + for annot in x.annotations + push!(annotations, setindex(annot, annot.region .+ size, :region)) end print(s, x.string) elseif x isa SubString{<:AnnotatedString} - for (region, annot) in x.string.annotations - start, stop = first(region), last(region) + for annot in x.string.annotations + start, stop = first(annot.region), last(annot.region) if start <= x.offset + x.ncodeunits && stop > x.offset rstart = size + max(0, start - x.offset - 1) + 1 rstop = size + min(stop, x.offset + x.ncodeunits) - x.offset - push!(annotations, (rstart:rstop, annot)) + push!(annotations, setindex(annot, rstart:rstop, :region)) end end print(s, SubString(x.string.string, x.offset, x.ncodeunits, Val(:noshift))) elseif x isa AnnotatedChar for annot in x.annotations - push!(annotations, (1+size:1+size, annot)) + push!(annotations, (region=1+size:1+size, annot...)) end print(s, x.char) else @@ -266,7 +278,7 @@ end annotatedstring(s::AnnotatedString) = s annotatedstring(c::AnnotatedChar) = - AnnotatedString(string(c.char), [(1:ncodeunits(c), annot) for annot in c.annotations]) + AnnotatedString(string(c.char), [(region=1:ncodeunits(c), annot...) for annot in c.annotations]) AnnotatedString(s::SubString{<:AnnotatedString}) = annotatedstring(s) @@ -274,18 +286,19 @@ function repeat(str::AnnotatedString, r::Integer) r == 0 && return one(AnnotatedString) r == 1 && return str unannot = repeat(str.string, r) - annotations = Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}() + annotations = Vector{RegionAnnotation}() len = ncodeunits(str) fullregion = firstindex(str):lastindex(str) - if allequal(first, str.annotations) && first(first(str.annotations)) == fullregion + if isempty(str.annotations) + elseif allequal(a -> a.region, str.annotations) && first(str.annotations).region == fullregion newfullregion = firstindex(unannot):lastindex(unannot) - for (_, annot) in str.annotations - push!(annotations, (newfullregion, annot)) + for annot in str.annotations + push!(annotations, setindex(annot, newfullregion, :region)) end else for offset in 0:len:(r-1)*len - for (region, annot) in str.annotations - push!(annotations, (region .+ offset, annot)) + for annot in str.annotations + push!(annotations, setindex(annot, annot.region .+ offset, :region)) end end end @@ -298,16 +311,18 @@ repeat(str::SubString{<:AnnotatedString}, r::Integer) = function repeat(c::AnnotatedChar, r::Integer) str = repeat(c.char, r) fullregion = firstindex(str):lastindex(str) - AnnotatedString(str, [(fullregion, annot) for annot in c.annotations]) + AnnotatedString(str, [(region=fullregion, annot...) for annot in c.annotations]) end function reverse(s::AnnotatedString) lastind = lastindex(s) - AnnotatedString(reverse(s.string), - [(UnitRange(1 + lastind - last(region), - 1 + lastind - first(region)), - annot) - for (region, annot) in s.annotations]) + AnnotatedString( + reverse(s.string), + [setindex(annot, + UnitRange(1 + lastind - last(annot.region), + 1 + lastind - first(annot.region)), + :region) + for annot in s.annotations]) end # TODO optimise? @@ -317,18 +332,17 @@ reverse(s::SubString{<:AnnotatedString}) = reverse(AnnotatedString(s)) ## End AbstractString interface ## -function _annotate!(annlist::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any})) - label, val = labelval - if val === nothing - deleteat!(annlist, findall(ann -> ann[1] == range && first(ann[2]) === label, annlist)) +function _annotate!(annlist::Vector{RegionAnnotation}, region::UnitRange{Int}, label::Symbol, @nospecialize(value::Any)) + if value === nothing + deleteat!(annlist, findall(ann -> ann.region == region && ann.label === label, annlist)) else - push!(annlist, (range, Pair{Symbol, Any}(label, val))) + push!(annlist, RegionAnnotation((; region, label, value))) end end """ - annotate!(str::AnnotatedString, [range::UnitRange{Int}], label::Symbol => value) - annotate!(str::SubString{AnnotatedString}, [range::UnitRange{Int}], label::Symbol => value) + annotate!(str::AnnotatedString, [range::UnitRange{Int}], label::Symbol, value) + annotate!(str::SubString{AnnotatedString}, [range::UnitRange{Int}], label::Symbol, value) Annotate a `range` of `str` (or the entire string) with a labeled value (`label` => `value`). To remove existing `label` annotations, use a value of `nothing`. @@ -336,30 +350,30 @@ To remove existing `label` annotations, use a value of `nothing`. The order in which annotations are applied to `str` is semantically meaningful, as described in [`AnnotatedString`](@ref). """ -annotate!(s::AnnotatedString, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any})) = - (_annotate!(s.annotations, range, labelval); s) +annotate!(s::AnnotatedString, range::UnitRange{Int}, label::Symbol, @nospecialize(val::Any)) = + (_annotate!(s.annotations, range, label, val); s) -annotate!(ss::AnnotatedString, @nospecialize(labelval::Pair{Symbol, <:Any})) = - annotate!(ss, firstindex(ss):lastindex(ss), labelval) +annotate!(ss::AnnotatedString, label::Symbol, @nospecialize(val::Any)) = + annotate!(ss, firstindex(ss):lastindex(ss), label, val) -annotate!(s::SubString{<:AnnotatedString}, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any})) = - (annotate!(s.string, s.offset .+ (range), labelval); s) +annotate!(s::SubString{<:AnnotatedString}, range::UnitRange{Int}, label::Symbol, @nospecialize(val::Any)) = + (annotate!(s.string, s.offset .+ (range), label, val); s) -annotate!(s::SubString{<:AnnotatedString}, @nospecialize(labelval::Pair{Symbol, <:Any})) = - (annotate!(s.string, s.offset .+ (1:s.ncodeunits), labelval); s) +annotate!(s::SubString{<:AnnotatedString}, label::Symbol, @nospecialize(val::Any)) = + (annotate!(s.string, s.offset .+ (1:s.ncodeunits), label, val); s) """ - annotate!(char::AnnotatedChar, label::Symbol => value) + annotate!(char::AnnotatedChar, label::Symbol, value::Any) Annotate `char` with the pair `label => value`. """ -annotate!(c::AnnotatedChar, @nospecialize(labelval::Pair{Symbol, <:Any})) = - (push!(c.annotations, labelval); c) +annotate!(c::AnnotatedChar, label::Symbol, @nospecialize(val::Any)) = + (push!(c.annotations, Annotation((; label, val))); c) """ annotations(str::Union{AnnotatedString, SubString{AnnotatedString}}, [position::Union{Integer, UnitRange}]) -> - Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}} + Vector{$RegionAnnotation} Get all annotations that apply to `str`. Should `position` be provided, only annotations that overlap with `position` will be returned. @@ -375,15 +389,16 @@ See also: [`annotate!`](@ref). annotations(s::AnnotatedString) = s.annotations function annotations(s::SubString{<:AnnotatedString}) - map(((region, annot),) -> (first(region)-s.offset:last(region)-s.offset, annot), - annotations(s.string, s.offset+1:s.offset+s.ncodeunits)) + RegionAnnotation[ + setindex(ann, first(ann.region)-s.offset:last(ann.region)-s.offset, :region) + for ann in annotations(s.string, s.offset+1:s.offset+s.ncodeunits)] end function annotations(s::AnnotatedString, pos::UnitRange{<:Integer}) # TODO optimise - Tuple{UnitRange{Int64}, Pair{Symbol, Any}}[ - (max(first(pos), first(region)):min(last(pos), last(region)), annot) - for (region, annot) in s.annotations if !isempty(intersect(pos, region))] + RegionAnnotation[ + setindex(ann, max(first(pos), first(ann.region)):min(last(pos), last(ann.region)), :region) + for ann in s.annotations if !isempty(intersect(pos, ann.region))] end annotations(s::AnnotatedString, pos::Integer) = annotations(s, pos:pos) @@ -395,7 +410,7 @@ annotations(s::SubString{<:AnnotatedString}, pos::UnitRange{<:Integer}) = annotations(s.string, first(pos)+s.offset:last(pos)+s.offset) """ - annotations(chr::AnnotatedChar) -> Vector{Pair{Symbol, Any}} + annotations(chr::AnnotatedChar) -> Vector{$Annotation} Get all annotations of `chr`, in the form of a vector of annotation pairs. """ @@ -420,7 +435,7 @@ string type of `str`). """ function annotated_chartransform(f::Function, str::AnnotatedString, state=nothing) outstr = IOBuffer() - annots = Tuple{UnitRange{Int}, Pair{Symbol, Any}}[] + annots = RegionAnnotation[] bytepos = firstindex(str) - 1 offsets = [bytepos => 0] for c in str.string @@ -437,11 +452,10 @@ function annotated_chartransform(f::Function, str::AnnotatedString, state=nothin end end for annot in str.annotations - region, value = annot - start, stop = first(region), last(region) + start, stop = first(annot.region), last(annot.region) start_offset = last(offsets[findlast(<=(start) ∘ first, offsets)::Int]) stop_offset = last(offsets[findlast(<=(stop) ∘ first, offsets)::Int]) - push!(annots, ((start + start_offset):(stop + stop_offset), value)) + push!(annots, setindex(annot, (start + start_offset):(stop + stop_offset), :region)) end AnnotatedString(String(take!(outstr)), annots) end @@ -450,10 +464,10 @@ end struct AnnotatedIOBuffer <: AbstractPipe io::IOBuffer - annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}} + annotations::Vector{RegionAnnotation} end -AnnotatedIOBuffer(io::IOBuffer) = AnnotatedIOBuffer(io, Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}()) +AnnotatedIOBuffer(io::IOBuffer) = AnnotatedIOBuffer(io, Vector{RegionAnnotation}()) AnnotatedIOBuffer() = AnnotatedIOBuffer(IOBuffer()) function show(io::IO, aio::AnnotatedIOBuffer) @@ -475,8 +489,8 @@ copy(io::AnnotatedIOBuffer) = AnnotatedIOBuffer(copy(io.io), copy(io.annotations annotations(io::AnnotatedIOBuffer) = io.annotations -annotate!(io::AnnotatedIOBuffer, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any})) = - (_annotate!(io.annotations, range, labelval); io) +annotate!(io::AnnotatedIOBuffer, range::UnitRange{Int}, label::Symbol, @nospecialize(val::Any)) = + (_annotate!(io.annotations, range, label, val); io) function write(io::AnnotatedIOBuffer, astr::Union{AnnotatedString, SubString{<:AnnotatedString}}) astr = AnnotatedString(astr) @@ -487,7 +501,7 @@ function write(io::AnnotatedIOBuffer, astr::Union{AnnotatedString, SubString{<:A end write(io::AnnotatedIOBuffer, c::AnnotatedChar) = - write(io, AnnotatedString(string(c), map(a -> (1:ncodeunits(c), a), annotations(c)))) + write(io, AnnotatedString(string(c), [(region=1:ncodeunits(c), a...) for a in c.annotations])) write(io::AnnotatedIOBuffer, x::AbstractString) = write(io.io, x) write(io::AnnotatedIOBuffer, s::Union{SubString{String}, String}) = write(io.io, s) write(io::AnnotatedIOBuffer, b::UInt8) = write(io.io, b) @@ -498,8 +512,8 @@ function write(dest::AnnotatedIOBuffer, src::AnnotatedIOBuffer) srcpos = position(src) nb = write(dest.io, src.io) isappending || _clear_annotations_in_region!(dest.annotations, destpos:destpos+nb) - srcannots = [(max(1 + srcpos, first(region)):last(region), annot) - for (region, annot) in src.annotations if first(region) >= srcpos] + srcannots = [setindex(annot, max(1 + srcpos, first(annot.region)):last(annot.region), :region) + for annot in src.annotations if first(annot.region) >= srcpos] _insert_annotations!(dest, srcannots, destpos - srcpos) nb end @@ -523,7 +537,7 @@ function write(io::AbstractPipe, c::AnnotatedChar) end """ - _clear_annotations_in_region!(annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}, span::UnitRange{Int}) + _clear_annotations_in_region!(annotations::Vector{$RegionAnnotation}, span::UnitRange{Int}) Erase the presence of `annotations` within a certain `span`. @@ -531,21 +545,27 @@ This operates by removing all elements of `annotations` that are entirely contained in `span`, truncating ranges that partially overlap, and splitting annotations that subsume `span` to just exist either side of `span`. """ -function _clear_annotations_in_region!(annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}, span::UnitRange{Int}) +function _clear_annotations_in_region!(annotations::Vector{RegionAnnotation}, span::UnitRange{Int}) # Clear out any overlapping pre-existing annotations. - filter!(((region, _),) -> first(region) < first(span) || last(region) > last(span), annotations) - extras = Tuple{Int, Tuple{UnitRange{Int}, Pair{Symbol, Any}}}[] + filter!(ann -> first(ann.region) < first(span) || last(ann.region) > last(span), annotations) + extras = Tuple{Int, RegionAnnotation}[] for i in eachindex(annotations) - region, annot = annotations[i] + annot = annotations[i] + region = annot.region # Test for partial overlap if first(region) <= first(span) <= last(region) || first(region) <= last(span) <= last(region) - annotations[i] = (if first(region) < first(span) - first(region):first(span)-1 - else last(span)+1:last(region) end, annot) + annotations[i] = + setindex(annot, + if first(region) < first(span) + first(region):first(span)-1 + else + last(span)+1:last(region) + end, + :region) # If `span` fits exactly within `region`, then we've only copied over # the beginning overhang, but also need to conserve the end overhang. if first(region) < first(span) && last(span) < last(region) - push!(extras, (i, (last(span)+1:last(region), annot))) + push!(extras, (i, setindex(annot, last(span)+1:last(region), :region))) end end end @@ -557,7 +577,7 @@ function _clear_annotations_in_region!(annotations::Vector{Tuple{UnitRange{Int}, end """ - _insert_annotations!(io::AnnotatedIOBuffer, annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}, offset::Int = position(io)) + _insert_annotations!(io::AnnotatedIOBuffer, annotations::Vector{$RegionAnnotation}, offset::Int = position(io)) Register new `annotations` in `io`, applying an `offset` to their regions. @@ -573,19 +593,19 @@ This is implemented so that one can say write an `AnnotatedString` to an `AnnotatedIOBuffer` one character at a time without needlessly producing a new annotation for each character. """ -function _insert_annotations!(io::AnnotatedIOBuffer, annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}, offset::Int = position(io)) +function _insert_annotations!(io::AnnotatedIOBuffer, annotations::Vector{RegionAnnotation}, offset::Int = position(io)) run = 0 - if !isempty(io.annotations) && last(first(last(io.annotations))) == offset + if !isempty(io.annotations) && last(last(io.annotations).region) == offset for i in reverse(axes(annotations, 1)) annot = annotations[i] - first(first(annot)) == 1 || continue + first(annot.region) == 1 || continue i <= length(io.annotations) || continue - if last(annot) == last(last(io.annotations)) + if annot.label == last(io.annotations).label && annot.value == last(io.annotations).value valid_run = true for runlen in 1:i - new_range, new_annot = annotations[begin+runlen-1] - old_range, old_annot = io.annotations[end-i+runlen] - if last(old_range) != offset || first(new_range) != 1 || old_annot != new_annot + new = annotations[begin+runlen-1] + old = io.annotations[end-i+runlen] + if last(old.region) != offset || first(new.region) != 1 || old.label != new.label || old.value != new.value valid_run = false break end @@ -599,14 +619,14 @@ function _insert_annotations!(io::AnnotatedIOBuffer, annotations::Vector{Tuple{U end for runindex in 0:run-1 old_index = lastindex(io.annotations) - run + 1 + runindex - old_region, annot = io.annotations[old_index] - new_region, _ = annotations[begin+runindex] - io.annotations[old_index] = (first(old_region):last(new_region)+offset, annot) + old = io.annotations[old_index] + new = annotations[begin+runindex] + io.annotations[old_index] = setindex(old, first(old.region):last(new.region)+offset, :region) end for index in run+1:lastindex(annotations) - region, annot = annotations[index] - start, stop = first(region), last(region) - push!(io.annotations, (start+offset:stop+offset, annot)) + annot = annotations[index] + start, stop = first(annot.region), last(annot.region) + push!(io.annotations, setindex(annotations[index], start+offset:stop+offset, :region)) end end @@ -614,8 +634,8 @@ function read(io::AnnotatedIOBuffer, ::Type{AnnotatedString{T}}) where {T <: Abs if (start = position(io)) == 0 AnnotatedString(read(io.io, T), copy(io.annotations)) else - annots = [(UnitRange{Int}(max(1, first(region) - start), last(region)-start), val) - for (region, val) in io.annotations if last(region) > start] + annots = [setindex(annot, UnitRange{Int}(max(1, first(annot.region) - start), last(annot.region)-start), :region) + for annot in io.annotations if last(annot.region) > start] AnnotatedString(read(io.io, T), annots) end end @@ -625,7 +645,7 @@ read(io::AnnotatedIOBuffer, ::Type{AnnotatedString}) = read(io, AnnotatedString{ function read(io::AnnotatedIOBuffer, ::Type{AnnotatedChar{T}}) where {T <: AbstractChar} pos = position(io) char = read(io.io, T) - annots = [annot for (range, annot) in io.annotations if pos+1 in range] + annots = [NamedTuple{(:label, :value)}(annot) for annot in io.annotations if pos+1 in annot.region] AnnotatedChar(char, annots) end read(io::AnnotatedIOBuffer, ::Type{AnnotatedChar{AbstractChar}}) = read(io, AnnotatedChar{Char}) @@ -633,8 +653,8 @@ read(io::AnnotatedIOBuffer, ::Type{AnnotatedChar}) = read(io, AnnotatedChar{Char function truncate(io::AnnotatedIOBuffer, size::Integer) truncate(io.io, size) - filter!(((range, _),) -> first(range) <= size, io.annotations) - map!(((range, val),) -> (first(range):min(size, last(range)), val), + filter!(ann -> first(ann.region) <= size, io.annotations) + map!(ann -> setindex(ann, first(ann.region):min(size, last(ann.region)), :region), io.annotations, io.annotations) io end diff --git a/base/strings/io.jl b/base/strings/io.jl index 4e413c0ebd78c..4ab22637e0eb1 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -800,12 +800,12 @@ function AnnotatedString(chars::AbstractVector{C}) where {C<:AbstractChar} end end end - annots = Tuple{UnitRange{Int}, Pair{Symbol, Any}}[] + annots = RegionAnnotation[] point = 1 for c in chars if c isa AnnotatedChar for annot in c.annotations - push!(annots, (point:point, annot)) + push!(annots, (point:point, annot...)) end end point += ncodeunits(c) diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index 28fa4a7ca6d76..072238905e07d 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -1230,7 +1230,7 @@ to keep the string annotations. ```jldoctest julia> str = Base.AnnotatedString("hello there", - [(1:5, :word => :greeting), (7:11, :label => 1)]) + [(1:5, :word, :greeting), (7:11, :label, 1)]) "hello there" julia> length(str) @@ -1242,7 +1242,7 @@ julia> lpad(str, 14) julia> typeof(lpad(str, 7)) Base.AnnotatedString{String} -julia> str2 = Base.AnnotatedString(" julia", [(2:6, :face => :magenta)]) +julia> str2 = Base.AnnotatedString(" julia", [(2:6, :face, :magenta)]) " julia" julia> Base.annotatedstring(str, str2) diff --git a/test/strings/annotated.jl b/test/strings/annotated.jl index 9e24ae46fd206..8d0e54e38e38e 100644 --- a/test/strings/annotated.jl +++ b/test/strings/annotated.jl @@ -4,7 +4,7 @@ using StyledStrings # see https://github.com/JuliaLang/StyledStrings.jl/issues/6 @testset "AnnotatedString" begin str = Base.AnnotatedString("some string") - @test str == Base.AnnotatedString(str.string, Tuple{UnitRange{Int}, Pair{Symbol, Any}}[]) + @test str == Base.AnnotatedString(str.string, Base.RegionAnnotation[]) @test length(str) == 11 @test ncodeunits(str) == 11 @test eltype(str) == Base.AnnotatedChar{eltype(str.string)} @@ -16,9 +16,9 @@ using StyledStrings # see https://github.com/JuliaLang/StyledStrings.jl/issues/6 @test str * str == Base.AnnotatedString("some stringsome string") @test str[3:4] == SubString("me") @test SubString("me") == str[3:4] - Base.annotate!(str, 1:4, :thing => 0x01) - Base.annotate!(str, 6:11, :other => 0x02) - Base.annotate!(str, 1:11, :all => 0x03) + Base.annotate!(str, 1:4, :thing, 0x01) + Base.annotate!(str, 6:11, :other, 0x02) + Base.annotate!(str, 1:11, :all, 0x03) # :thing :other # ┌┸─┐ ┌──┸─┐ # "some string" @@ -28,21 +28,21 @@ using StyledStrings # see https://github.com/JuliaLang/StyledStrings.jl/issues/6 @test str[3:4] != SubString("me") @test SubString("me") != str[3:4] @test Base.AnnotatedString(str[3:4]) == - Base.AnnotatedString("me", [(1:2, :thing => 0x01), (1:2, :all => 0x03)]) + Base.AnnotatedString("me", [(1:2, :thing, 0x01), (1:2, :all, 0x03)]) @test Base.AnnotatedString(str[3:6]) == - Base.AnnotatedString("me s", [(1:2, :thing => 0x01), (4:4, :other => 0x02), (1:4, :all => 0x03)]) - @test str == Base.AnnotatedString("some string", [(1:4, :thing => 0x01), (6:11, :other => 0x02), (1:11, :all => 0x03)]) + Base.AnnotatedString("me s", [(1:2, :thing, 0x01), (4:4, :other, 0x02), (1:4, :all, 0x03)]) + @test str == Base.AnnotatedString("some string", [(1:4, :thing, 0x01), (6:11, :other, 0x02), (1:11, :all, 0x03)]) @test str != Base.AnnotatedString("some string") - @test str != Base.AnnotatedString("some string", [(1:1, :thing => 0x01), (1:11, :all => 0x03), (6:6, :other => 0x02)]) - @test str != Base.AnnotatedString("some string", [(1:4, :thing => 0x11), (1:11, :all => 0x13), (6:11, :other => 0x12)]) - @test str != Base.AnnotatedString("some thingg", [(1:4, :thing => 0x01), (1:11, :all => 0x03), (6:11, :other => 0x02)]) - @test Base.AnnotatedString([Base.AnnotatedChar('a', [:a => 1]), Base.AnnotatedChar('b', [:b => 2])]) == - Base.AnnotatedString("ab", [(1:1, :a => 1), (2:2, :b => 2)]) + @test str != Base.AnnotatedString("some string", [(1:1, :thing, 0x01), (1:11, :all, 0x03), (6:6, :other, 0x02)]) + @test str != Base.AnnotatedString("some string", [(1:4, :thing, 0x11), (1:11, :all, 0x13), (6:11, :other, 0x12)]) + @test str != Base.AnnotatedString("some thingg", [(1:4, :thing, 0x01), (1:11, :all, 0x03), (6:11, :other, 0x02)]) + @test Base.AnnotatedString([Base.AnnotatedChar('a', [(:a, 1)]), Base.AnnotatedChar('b', [(:b, 2)])]) == + Base.AnnotatedString("ab", [(1:1, :a, 1), (2:2, :b, 2)]) let allstrings = - ['a', Base.AnnotatedChar('a'), Base.AnnotatedChar('a', [:aaa => 0x04]), + ['a', Base.AnnotatedChar('a'), Base.AnnotatedChar('a', [(:aaa, 0x04)]), "a string", Base.AnnotatedString("a string"), - Base.AnnotatedString("a string", [(1:2, :hmm => '%')]), - SubString(Base.AnnotatedString("a string", [(1:2, :hmm => '%')]), 1:1)] + Base.AnnotatedString("a string", [(1:2, :hmm, '%')]), + SubString(Base.AnnotatedString("a string", [(1:2, :hmm, '%')]), 1:1)] for str1 in repeat(allstrings, 2) for str2 in repeat(allstrings, 2) @test String(str1 * str2) == @@ -55,10 +55,10 @@ using StyledStrings # see https://github.com/JuliaLang/StyledStrings.jl/issues/6 end end # @test collect(Base.eachstyle(str)) == - # [("some", [:thing => 0x01, :all => 0x03]), - # (" string", [:all => 0x03, :other => 0x02])] + # [("some", [:thing, 0x01, :all, 0x03]), + # (" string", [:all, 0x03, :other, 0x02])] @test chopprefix(sprint(show, str), "Base.") == - "AnnotatedString{String}(\"some string\", [(1:4, :thing => 0x01), (6:11, :other => 0x02), (1:11, :all => 0x03)])" + "AnnotatedString{String}(\"some string\", [(1:4, :thing, 0x01), (6:11, :other, 0x02), (1:11, :all, 0x03)])" @test eval(Meta.parse(repr(str))) == str @test sprint(show, MIME("text/plain"), str) == "\"some string\"" end @@ -69,16 +69,16 @@ end @test uppercase(chr) == Base.AnnotatedChar('C') @test titlecase(chr) == Base.AnnotatedChar('C') @test lowercase(Base.AnnotatedChar('C')) == chr - str = Base.AnnotatedString("hmm", [(1:1, :attr => "h0h0"), - (1:2, :attr => "h0m1"), - (2:3, :attr => "m1m2")]) - @test str[1] == Base.AnnotatedChar('h', Pair{Symbol, Any}[:attr => "h0h0"]) - @test str[2] == Base.AnnotatedChar('m', Pair{Symbol, Any}[:attr => "h0m1", :attr => "m1m2"]) - @test str[3] == Base.AnnotatedChar('m', Pair{Symbol, Any}[:attr => "m1m2"]) + str = Base.AnnotatedString("hmm", [(1:1, :attr, "h0h0"), + (1:2, :attr, "h0m1"), + (2:3, :attr, "m1m2")]) + @test str[1] == Base.AnnotatedChar('h', [(:attr, "h0h0")]) + @test str[2] == Base.AnnotatedChar('m', [(:attr, "h0m1"), (:attr, "m1m2")]) + @test str[3] == Base.AnnotatedChar('m', [(:attr, "m1m2")]) end @testset "Styling preservation" begin - str = Base.AnnotatedString("some string", [(1:4, :thing => 0x01), (1:11, :all => 0x03), (6:11, :other => 0x02)]) + str = Base.AnnotatedString("some string", [(1:4, :thing, 0x01), (1:11, :all, 0x03), (6:11, :other, 0x02)]) @test match(r".e", str).match == str[3:4] @test match(r"(.e)", str).captures == [str[3:4]] let m0 = match(r"(.)e", str) @@ -88,43 +88,43 @@ end end end @test lpad(str, 12) == - Base.AnnotatedString(" some string", [(2:5, :thing => 0x01), - (2:12, :all => 0x03), - (7:12, :other => 0x02)]) + Base.AnnotatedString(" some string", [(2:5, :thing, 0x01), + (2:12, :all, 0x03), + (7:12, :other, 0x02)]) @test rpad(str, 12) == - Base.AnnotatedString("some string ", [(1:4, :thing => 0x01), - (1:11, :all => 0x03), - (6:11, :other => 0x02)]) - str1 = Base.AnnotatedString("test", [(1:4, :label => 5)]) - str2 = Base.AnnotatedString("case", [(2:3, :label => "oomph")]) + Base.AnnotatedString("some string ", [(1:4, :thing, 0x01), + (1:11, :all, 0x03), + (6:11, :other, 0x02)]) + str1 = Base.AnnotatedString("test", [(1:4, :label, 5)]) + str2 = Base.AnnotatedString("case", [(2:3, :label, "oomph")]) @test join([str1, str1], ' ') == Base.AnnotatedString("test test", - [(1:4, :label => 5), - (6:9, :label => 5)]) - @test join([str1, str1], Base.AnnotatedString(" ", [(1:1, :label => 2)])) == + [(1:4, :label, 5), + (6:9, :label, 5)]) + @test join([str1, str1], Base.AnnotatedString(" ", [(1:1, :label, 2)])) == Base.AnnotatedString("test test", - [(1:4, :label => 5), - (5:5, :label => 2), - (6:9, :label => 5)]) + [(1:4, :label, 5), + (5:5, :label, 2), + (6:9, :label, 5)]) @test join((String(str1), str1), ' ') == - Base.AnnotatedString("test test", [(6:9, :label => 5)]) - @test repeat(str1, 2) == Base.AnnotatedString("testtest", [(1:8, :label => 5)]) - @test repeat(str2, 2) == Base.AnnotatedString("casecase", [(2:3, :label => "oomph"), - (6:7, :label => "oomph")]) - @test repeat(str1[1], 3) == Base.AnnotatedString("ttt", [(1:3, :label => 5)]) - @test reverse(str1) == Base.AnnotatedString("tset", [(1:4, :label => 5)]) - @test reverse(str2) == Base.AnnotatedString("esac", [(2:3, :label => "oomph")]) + Base.AnnotatedString("test test", [(6:9, :label, 5)]) + @test repeat(str1, 2) == Base.AnnotatedString("testtest", [(1:8, :label, 5)]) + @test repeat(str2, 2) == Base.AnnotatedString("casecase", [(2:3, :label, "oomph"), + (6:7, :label, "oomph")]) + @test repeat(str1[1], 3) == Base.AnnotatedString("ttt", [(1:3, :label, 5)]) + @test reverse(str1) == Base.AnnotatedString("tset", [(1:4, :label, 5)]) + @test reverse(str2) == Base.AnnotatedString("esac", [(2:3, :label, "oomph")]) end @testset "Unicode" begin for words in (["ᲃase", "cɦɒnɡeȿ", "can", "CHⱯNGE", "Сodeunıts"], ["Сodeunıts", "ᲃase", "cɦɒnɡeȿ", "can", "CHⱯNGE"]) - ann_words = [Base.AnnotatedString(w, [(1:ncodeunits(w), :i => i)]) + ann_words = [Base.AnnotatedString(w, [(1:ncodeunits(w), :i, i)]) for (i, w) in enumerate(words)] ann_str = join(ann_words, '-') for transform in (lowercase, uppercase, titlecase) t_words = map(transform, words) - ann_t_words = [Base.AnnotatedString(w, [(1:ncodeunits(w), :i => i)]) + ann_t_words = [Base.AnnotatedString(w, [(1:ncodeunits(w), :i, i)]) for (i, w) in enumerate(t_words)] ann_t_str = join(ann_t_words, '-') t_ann_str = transform(ann_str) @@ -133,7 +133,7 @@ end end for transform in (uppercasefirst, lowercasefirst) t_words = vcat(transform(first(words)), words[2:end]) - ann_t_words = [Base.AnnotatedString(w, [(1:ncodeunits(w), :i => i)]) + ann_t_words = [Base.AnnotatedString(w, [(1:ncodeunits(w), :i, i)]) for (i, w) in enumerate(t_words)] ann_t_str = join(ann_t_words, '-') t_ann_str = transform(ann_str) @@ -145,31 +145,32 @@ end @testset "AnnotatedIOBuffer" begin aio = Base.AnnotatedIOBuffer() + vec2ann(v::Vector{<:Tuple}) = collect(Base.RegionAnnotation, v) # Append-only writing - @test write(aio, Base.AnnotatedString("hello", [(1:5, :tag => 1)])) == 5 + @test write(aio, Base.AnnotatedString("hello", [(1:5, :tag, 1)])) == 5 @test write(aio, ' ') == 1 - @test write(aio, Base.AnnotatedString("world", [(1:5, :tag => 2)])) == 5 - @test Base.annotations(aio) == [(1:5, :tag => 1), (7:11, :tag => 2)] + @test write(aio, Base.AnnotatedString("world", [(1:5, :tag, 2)])) == 5 + @test Base.annotations(aio) == vec2ann([(1:5, :tag, 1), (7:11, :tag, 2)]) # Check `annotate!`, including region sorting @test truncate(aio, 0).io.size == 0 @test write(aio, "hello world") == ncodeunits("hello world") - @test Base.annotate!(aio, 1:5, :tag => 1) === aio - @test Base.annotate!(aio, 7:11, :tag => 2) === aio - @test Base.annotations(aio) == [(1:5, :tag => 1), (7:11, :tag => 2)] + @test Base.annotate!(aio, 1:5, :tag, 1) === aio + @test Base.annotate!(aio, 7:11, :tag, 2) === aio + @test Base.annotations(aio) == vec2ann([(1:5, :tag, 1), (7:11, :tag, 2)]) # Reading @test read(seekstart(deepcopy(aio.io)), String) == "hello world" @test read(seekstart(deepcopy(aio)), String) == "hello world" - @test read(seek(aio, 0), Base.AnnotatedString) == Base.AnnotatedString("hello world", [(1:5, :tag => 1), (7:11, :tag => 2)]) - @test read(seek(aio, 1), Base.AnnotatedString) == Base.AnnotatedString("ello world", [(1:4, :tag => 1), (6:10, :tag => 2)]) - @test read(seek(aio, 4), Base.AnnotatedString) == Base.AnnotatedString("o world", [(1:1, :tag => 1), (3:7, :tag => 2)]) - @test read(seek(aio, 5), Base.AnnotatedString) == Base.AnnotatedString(" world", [(2:6, :tag => 2)]) + @test read(seek(aio, 0), Base.AnnotatedString) == Base.AnnotatedString("hello world", [(1:5, :tag, 1), (7:11, :tag, 2)]) + @test read(seek(aio, 1), Base.AnnotatedString) == Base.AnnotatedString("ello world", [(1:4, :tag, 1), (6:10, :tag, 2)]) + @test read(seek(aio, 4), Base.AnnotatedString) == Base.AnnotatedString("o world", [(1:1, :tag, 1), (3:7, :tag, 2)]) + @test read(seek(aio, 5), Base.AnnotatedString) == Base.AnnotatedString(" world", [(2:6, :tag, 2)]) @test read(seekend(aio), Base.AnnotatedString) == Base.AnnotatedString("") - @test read(seekstart(truncate(deepcopy(aio), 5)), Base.AnnotatedString) == Base.AnnotatedString("hello", [(1:5, :tag => 1)]) - @test read(seekstart(truncate(deepcopy(aio), 6)), Base.AnnotatedString) == Base.AnnotatedString("hello ", [(1:5, :tag => 1)]) - @test read(seekstart(truncate(deepcopy(aio), 7)), Base.AnnotatedString) == Base.AnnotatedString("hello w", [(1:5, :tag => 1), (7:7, :tag => 2)]) - @test read(seek(aio, 0), Base.AnnotatedChar) == Base.AnnotatedChar('h', [:tag => 1]) - @test read(seek(aio, 5), Base.AnnotatedChar) == Base.AnnotatedChar(' ', Pair{Symbol, Any}[]) - @test read(seek(aio, 6), Base.AnnotatedChar) == Base.AnnotatedChar('w', [:tag => 2]) + @test read(seekstart(truncate(deepcopy(aio), 5)), Base.AnnotatedString) == Base.AnnotatedString("hello", [(1:5, :tag, 1)]) + @test read(seekstart(truncate(deepcopy(aio), 6)), Base.AnnotatedString) == Base.AnnotatedString("hello ", [(1:5, :tag, 1)]) + @test read(seekstart(truncate(deepcopy(aio), 7)), Base.AnnotatedString) == Base.AnnotatedString("hello w", [(1:5, :tag, 1), (7:7, :tag, 2)]) + @test read(seek(aio, 0), Base.AnnotatedChar) == Base.AnnotatedChar('h', [(:tag, 1)]) + @test read(seek(aio, 5), Base.AnnotatedChar) == Base.AnnotatedChar(' ', []) + @test read(seek(aio, 6), Base.AnnotatedChar) == Base.AnnotatedChar('w', [(:tag, 2)]) # Check method compatibility with IOBuffer @test position(aio) == 7 @test seek(aio, 4) === aio @@ -179,19 +180,19 @@ end # Writing into the middle of the buffer @test write(seek(aio, 6), "alice") == 5 # Replace 'world' with 'alice' @test read(seekstart(aio), String) == "hello alice" - @test Base.annotations(aio) == [(1:5, :tag => 1), (7:11, :tag => 2)] # Should be unchanged - @test write(seek(aio, 0), Base.AnnotatedString("hey-o", [(1:5, :hey => 'o')])) == 5 + @test Base.annotations(aio) == vec2ann([(1:5, :tag, 1), (7:11, :tag, 2)]) # Should be unchanged + @test write(seek(aio, 0), Base.AnnotatedString("hey-o", [(1:5, :hey, 'o')])) == 5 @test read(seekstart(aio), String) == "hey-o alice" - @test Base.annotations(aio) == [(7:11, :tag => 2), (1:5, :hey => 'o')] # First annotation should have been entirely replaced - @test write(seek(aio, 7), Base.AnnotatedString("bbi", [(1:3, :hey => 'a')])) == 3 # a[lic => bbi]e ('alice' => 'abbie') + @test Base.annotations(aio) == vec2ann([(7:11, :tag, 2), (1:5, :hey, 'o')]) # First annotation should have been entirely replaced + @test write(seek(aio, 7), Base.AnnotatedString("bbi", [(1:3, :hey, 'a')])) == 3 # a[lic, bbi]e ('alice', 'abbie') @test read(seekstart(aio), String) == "hey-o abbie" - @test Base.annotations(aio) == [(7:7, :tag => 2), (11:11, :tag => 2), (1:5, :hey => 'o'), (8:10, :hey => 'a')] + @test Base.annotations(aio) == vec2ann([(7:7, :tag, 2), (11:11, :tag, 2), (1:5, :hey, 'o'), (8:10, :hey, 'a')]) @test write(seek(aio, 0), Base.AnnotatedString("ab")) == 2 # Check first annotation's region is adjusted correctly @test read(seekstart(aio), String) == "aby-o abbie" - @test Base.annotations(aio) == [(7:7, :tag => 2), (11:11, :tag => 2), (3:5, :hey => 'o'), (8:10, :hey => 'a')] + @test Base.annotations(aio) == vec2ann([(7:7, :tag, 2), (11:11, :tag, 2), (3:5, :hey, 'o'), (8:10, :hey, 'a')]) @test write(seek(aio, 3), Base.AnnotatedString("ss")) == 2 @test read(seekstart(aio), String) == "abyss abbie" - @test Base.annotations(aio) == [(7:7, :tag => 2), (11:11, :tag => 2), (3:3, :hey => 'o'), (8:10, :hey => 'a')] + @test Base.annotations(aio) == vec2ann([(7:7, :tag, 2), (11:11, :tag, 2), (3:3, :hey, 'o'), (8:10, :hey, 'a')]) # Writing one buffer to another newaio = Base.AnnotatedIOBuffer() @test write(newaio, seekstart(aio)) == 11 @@ -201,36 +202,37 @@ end @test sort(Base.annotations(newaio)) == sort(Base.annotations(aio)) @test write(newaio, seek(aio, 5)) == 6 @test read(seekstart(newaio), String) == "abyss abbie abbie" - @test sort(Base.annotations(newaio)) == sort(vcat(Base.annotations(aio), [(13:13, :tag => 2), (14:16, :hey => 'a'), (17:17, :tag => 2)])) + @test sort(Base.annotations(newaio)) == + sort(vcat(Base.annotations(aio), vec2ann([(13:13, :tag, 2), (14:16, :hey, 'a'), (17:17, :tag, 2)]))) # The `_insert_annotations!` cautious-merging optimisation aio = Base.AnnotatedIOBuffer() - @test write(aio, Base.AnnotatedChar('a', [:a => 1, :b => 2])) == 1 - @test Base.annotations(aio) == [(1:1, :a => 1), (1:1, :b => 2)] - @test write(aio, Base.AnnotatedChar('b', [:a => 1, :b => 2])) == 1 - @test Base.annotations(aio) == [(1:2, :a => 1), (1:2, :b => 2)] + @test write(aio, Base.AnnotatedChar('a', [(:a, 1), (:b, 2)])) == 1 + @test Base.annotations(aio) == vec2ann([(1:1, :a, 1), (1:1, :b, 2)]) + @test write(aio, Base.AnnotatedChar('b', [(:a, 1), (:b, 2)])) == 1 + @test Base.annotations(aio) == vec2ann([(1:2, :a, 1), (1:2, :b, 2)]) let aio2 = copy(aio) # A different start makes merging too risky to do. - @test write(aio2, Base.AnnotatedChar('c', [:a => 0, :b => 2])) == 1 - @test Base.annotations(aio2) == [(1:2, :a => 1), (1:2, :b => 2), (3:3, :a => 0), (3:3, :b => 2)] + @test write(aio2, Base.AnnotatedChar('c', [(:a, 0), (:b, 2)])) == 1 + @test Base.annotations(aio2) == vec2ann([(1:2, :a, 1), (1:2, :b, 2), (3:3, :a, 0), (3:3, :b, 2)]) end let aio2 = copy(aio) # Merging some run of the most recent annotations is fine though. - @test write(aio2, Base.AnnotatedChar('c', [:b => 2])) == 1 - @test Base.annotations(aio2) == [(1:2, :a => 1), (1:3, :b => 2)] + @test write(aio2, Base.AnnotatedChar('c', [(:b, 2)])) == 1 + @test Base.annotations(aio2) == vec2ann([(1:2, :a, 1), (1:3, :b, 2)]) end let aio2 = copy(aio) # ...and any subsequent annotations after a matching run can just be copied over. - @test write(aio2, Base.AnnotatedChar('c', [:b => 2, :c => 3, :d => 4])) == 1 - @test Base.annotations(aio2) == [(1:2, :a => 1), (1:3, :b => 2), (3:3, :c => 3), (3:3, :d => 4)] + @test write(aio2, Base.AnnotatedChar('c', [(:b, 2), (:c, 3), (:d, 4)])) == 1 + @test Base.annotations(aio2) == vec2ann([(1:2, :a, 1), (1:3, :b, 2), (3:3, :c, 3), (3:3, :d, 4)]) end let aio2 = Base.AnnotatedIOBuffer() - @test write(aio2, Base.AnnotatedChar('a', [:b => 1])) == 1 - @test write(aio2, Base.AnnotatedChar('b', [:a => 1, :b => 1])) == 1 + @test write(aio2, Base.AnnotatedChar('a', [(:b, 1)])) == 1 + @test write(aio2, Base.AnnotatedChar('b', [(:a, 1), (:b, 1)])) == 1 @test read(seekstart(aio2), Base.AnnotatedString) == - Base.AnnotatedString("ab", [(1:1, :b => 1), (2:2, :a => 1), (2:2, :b => 1)]) + Base.AnnotatedString("ab", [(1:1, :b, 1), (2:2, :a, 1), (2:2, :b, 1)]) end # Working through an IOContext aio = Base.AnnotatedIOBuffer() wrapio = IOContext(aio) - @test write(wrapio, Base.AnnotatedString("hey", [(1:3, :x => 1)])) == 3 - @test write(wrapio, Base.AnnotatedChar('a', [:y => 2])) == 1 + @test write(wrapio, Base.AnnotatedString("hey", [(1:3, :x, 1)])) == 3 + @test write(wrapio, Base.AnnotatedChar('a', [(:y, 2)])) == 1 @test read(seekstart(aio), Base.AnnotatedString) == - Base.AnnotatedString("heya", [(1:3, :x => 1), (4:4, :y => 2)]) + Base.AnnotatedString("heya", [(1:3, :x, 1), (4:4, :y, 2)]) end From b28fbd053b45a7473546473aa10228196ea97991 Mon Sep 17 00:00:00 2001 From: TEC Date: Fri, 20 Sep 2024 23:17:00 +0800 Subject: [PATCH 14/76] Bump StyledStrings This bump contains the following commits: a5b1174 * Adjust to change of annotations type in Base d9d7472 * Adopt Base's annotated types/functions as API da41b6a * Call load_customisations! automatically, lazily af972e0 * Fix markdown syntax in "Mark API as experimental" 8332e45 * Mark API as Experimental cfcfe8c * Fix syntax of generated CSS c82409c * Remove tag wrapping from HTML show method 03211c9 * Small optimisation to single-interpolation styled d080103 * Improve type stability within styled"" macro 448314b * More meticulously check indices in eachregion 02cd20b * Consistent Face hashes --- .../md5 | 1 + .../sha512 | 1 + stdlib/StyledStrings.version | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/md5 create mode 100644 deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/sha512 diff --git a/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/md5 b/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/md5 new file mode 100644 index 0000000000000..8d78dd7b0a11b --- /dev/null +++ b/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/md5 @@ -0,0 +1 @@ +f053c84279a8920f355f202e605842af diff --git a/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/sha512 b/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/sha512 new file mode 100644 index 0000000000000..5a8ca888c38f8 --- /dev/null +++ b/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/sha512 @@ -0,0 +1 @@ +b6f4c1d6c0dc73a520472746c96adff506e5405154e4b93d419e07b577b01804d2fc87d4a6cac48a136777579bebf8388c2c1e54f849b51e233138d482146b4f diff --git a/stdlib/StyledStrings.version b/stdlib/StyledStrings.version index cdd6154747eb7..5e58a5456148a 100644 --- a/stdlib/StyledStrings.version +++ b/stdlib/StyledStrings.version @@ -1,4 +1,4 @@ STYLEDSTRINGS_BRANCH = main -STYLEDSTRINGS_SHA1 = af972e0a772069b4996cdd206d4e36defebd2453 +STYLEDSTRINGS_SHA1 = 056e843b2d428bb9735b03af0cff97e738ac7e14 STYLEDSTRINGS_GIT_URL := https://github.com/JuliaLang/StyledStrings.jl.git STYLEDSTRINGS_TAR_URL = https://api.github.com/repos/JuliaLang/StyledStrings.jl/tarball/$1 From 48e1a727714dccf55e8119c4dd46878163d1e80b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Fri, 18 Oct 2024 22:42:50 +0100 Subject: [PATCH 15/76] Do not call `rand` during sysimage precompilation (#56227) This change by itself doesn't do anything significant on `master`, but when backported to the v1.11 branch it'll address #56177. However it'd be great if someone could tell _why_ this fixes that issue, because it looks very unrelated. --------- Co-authored-by: Ian Butterworth (cherry picked from commit f36f34298e5154b14fea1ec680b4e6c369b61d2b) --- contrib/generate_precompile.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index e4eeeed577686..6e8ae120e01bb 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -178,10 +178,10 @@ for match = Base._methods(+, (Int, Int), -1, Base.get_world_counter()) # interactive statup uses this write(IOBuffer(), "") - # not critical, but helps hide unrelated compilation from @time when using --trace-compile - foo() = rand(2,2) * rand(2,2) - @time foo() - @time foo() + # Not critical, but helps hide unrelated compilation from @time when using --trace-compile. + f55729() = Base.Experimental.@force_compile + @time @eval f55729() + @time @eval f55729() break # only actually need to do this once end From 581e65d5f52d6328625f86653b654e8b1bfe6eda Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 18 Oct 2024 15:33:29 -0400 Subject: [PATCH 16/76] stream: fix reading LibuvStream into array (#56092) Adds a new internal function `_take!(dst::Array{T,N}, src::Array{T,N})` for doing an efficient `copyto!` equivalent. Previously it was assumed that `compact` did this automatically, which wasn't a great assumption. Fixes #56078 (cherry picked from commit fc40e629b1d2ddc94ad9ecb87bc308b2892044e5) --- base/array.jl | 11 +++++++++++ base/stream.jl | 4 +++- test/read.jl | 22 +++++++++++++++------- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/base/array.jl b/base/array.jl index 762cf2cbc5bc5..7efdd1839b0d8 100644 --- a/base/array.jl +++ b/base/array.jl @@ -351,6 +351,17 @@ copy return $(Expr(:new, :(typeof(a)), :(memoryref(newmem)), :(a.size))) end +# a mutating version of copyto! that results in dst aliasing src afterwards +function _take!(dst::Array{T,N}, src::Array{T,N}) where {T,N} + if getfield(dst, :ref) !== getfield(src, :ref) + setfield!(dst, :ref, getfield(src, :ref)) + end + if getfield(dst, :size) !== getfield(src, :size) + setfield!(dst, :size, getfield(src, :size)) + end + return dst +end + ## Constructors ## similar(a::Array{T,1}) where {T} = Vector{T}(undef, size(a,1)) diff --git a/base/stream.jl b/base/stream.jl index a46027f803364..712c7c7c30001 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -940,6 +940,7 @@ function readbytes!(s::LibuvStream, a::Vector{UInt8}, nb::Int) if bytesavailable(sbuf) >= nb nread = readbytes!(sbuf, a, nb) else + initsize = length(a) newbuf = PipeBuffer(a, maxsize=nb) newbuf.size = newbuf.offset # reset the write pointer to the beginning nread = try @@ -950,7 +951,8 @@ function readbytes!(s::LibuvStream, a::Vector{UInt8}, nb::Int) finally s.buffer = sbuf end - compact(newbuf) + _take!(a, _unsafe_take!(newbuf)) + length(a) >= initsize || resize!(a, initsize) end iolock_end() return nread diff --git a/test/read.jl b/test/read.jl index 34224c146864e..99903d92d270f 100644 --- a/test/read.jl +++ b/test/read.jl @@ -268,13 +268,27 @@ for (name, f) in l n2 = readbytes!(s2, a2) @test n1 == n2 @test length(a1) == length(a2) - @test a1[1:n1] == a2[1:n2] + let l = min(l, n) + @test a1[1:l] == a2[1:l] + end @test n <= length(text) || eof(s1) @test n <= length(text) || eof(s2) cleanup() end + # Test growing output array + let x = UInt8[], + io = io() + n = readbytes!(io, x) + @test n == 0 + @test isempty(x) + n = readbytes!(io, x, typemax(Int)) + @test n == length(x) + @test x == codeunits(text) + cleanup() + end + verbose && println("$name read!...") l = length(text) for n = [1, 2, l-2, l-1, l] @@ -477,12 +491,6 @@ let s = "qwerty" @test read(IOBuffer(s)) == codeunits(s) @test read(IOBuffer(s), 10) == codeunits(s) @test read(IOBuffer(s), 1) == codeunits(s)[1:1] - - # Test growing output array - x = UInt8[] - n = readbytes!(IOBuffer(s), x, 10) - @test x == codeunits(s) - @test n == length(x) end From a10b94fe37b59451d7092e38a52500cfb6f757d4 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Fri, 18 Oct 2024 16:29:42 +0200 Subject: [PATCH 17/76] fix infinite recursion in `promote_type` for `Irrational` (#55870) Fixes #51001 (cherry picked from commit ca3713e7ac8489acd1afe0a47dc8ceeaafb4a292) --- base/irrationals.jl | 11 ++++++++++- test/numbers.jl | 8 ++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/base/irrationals.jl b/base/irrationals.jl index dc583f8d5b849..f887c93f184fe 100644 --- a/base/irrationals.jl +++ b/base/irrationals.jl @@ -45,7 +45,16 @@ promote_rule(::Type{<:AbstractIrrational}, ::Type{Float16}) = Float16 promote_rule(::Type{<:AbstractIrrational}, ::Type{Float32}) = Float32 promote_rule(::Type{<:AbstractIrrational}, ::Type{<:AbstractIrrational}) = Float64 promote_rule(::Type{<:AbstractIrrational}, ::Type{T}) where {T<:Real} = promote_type(Float64, T) -promote_rule(::Type{S}, ::Type{T}) where {S<:AbstractIrrational,T<:Number} = promote_type(promote_type(S, real(T)), T) + +function promote_rule(::Type{S}, ::Type{T}) where {S<:AbstractIrrational,T<:Number} + U = promote_type(S, real(T)) + if S <: U + # prevent infinite recursion + promote_type(Float64, T) + else + promote_type(U, T) + end +end AbstractFloat(x::AbstractIrrational) = Float64(x)::Float64 Float16(x::AbstractIrrational) = Float16(Float32(x)::Float32) diff --git a/test/numbers.jl b/test/numbers.jl index 922fa0da3337e..da4573f38b9a6 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -2897,6 +2897,14 @@ end @test log(π,ComplexF32(2)) isa ComplexF32 end +@testset "irrational promotion shouldn't recurse without bound, issue #51001" begin + for s ∈ (:π, :ℯ) + T = Irrational{s} + @test promote_type(Complex{T}, T) <: Complex + @test promote_type(T, Complex{T}) <: Complex + end +end + @testset "printing non finite floats" begin let float_types = Set() allsubtypes!(Base, AbstractFloat, float_types) From 7d3cbd8738f9612b0ced2ba74e8ae8d61f656b06 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sat, 19 Oct 2024 01:20:59 -0400 Subject: [PATCH 18/76] rename: invalid -> incompatible cache header (#56240) Falling back to the older serial precompilation process is basically a bug (except for if a manifest hasn't been resolved) so https://github.com/JuliaLang/julia/pull/52619 added more info on why it's been hit so we have a chance of fixing issues that are otherwise very difficult to recreate. However "invalid header" which usually just means it was made by a different julia version appears to sound too alarming to users. https://discourse.julialang.org/t/cache-misses-when-using-packages-since-upgrading-to-1-11/121445 So soften it there and in error messages, given it seems a better description. Suggested by @giordano in https://discourse.julialang.org/t/cache-misses-when-using-packages-since-upgrading-to-1-11/121445/4?u=ianshmean (cherry picked from commit aa51abe6400bc29c6093dca5a395fc03806ab511) --- base/loading.jl | 22 +++++++++++----------- stdlib/Logging/docs/src/index.md | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 6759bbab3f8cb..3b3c17f18e825 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1210,7 +1210,7 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No else io = open(path, "r") try - iszero(isvalid_cache_header(io)) && return ArgumentError("Invalid header in cache file $path.") + iszero(isvalid_cache_header(io)) && return ArgumentError("Incompatible header in cache file $path.") _, (includes, _, _), _, _, _, _, _, _ = parse_cache_header(io, path) ignore_native = pkg_tracked(includes) finally @@ -1831,7 +1831,7 @@ function isrelocatable(pkg::PkgId) isnothing(path) && return false io = open(path, "r") try - iszero(isvalid_cache_header(io)) && throw(ArgumentError("Invalid header in cache file $cachefile.")) + iszero(isvalid_cache_header(io)) && throw(ArgumentError("Incompatible header in cache file $cachefile.")) _, (includes, includes_srcfiles, _), _... = _parse_cache_header(io, path) for inc in includes !startswith(inc.filename, "@depot") && return false @@ -1906,7 +1906,7 @@ function _tryrequire_from_serialized(pkg::PkgId, path::String, ocachepath::Union io = open(path, "r") ignore_native = false try - iszero(isvalid_cache_header(io)) && return ArgumentError("Invalid header in cache file $path.") + iszero(isvalid_cache_header(io)) && return ArgumentError("Incompatible header in cache file $path.") _, (includes, _, _), depmodnames, _, _, _, clone_targets, _ = parse_cache_header(io, path) ignore_native = pkg_tracked(includes) @@ -3049,7 +3049,7 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in # append extra crc to the end of the .ji file: open(tmppath, "r+") do f if iszero(isvalid_cache_header(f)) - error("Invalid header for $(repr("text/plain", pkg)) in new cache file $(repr(tmppath)).") + error("Incompatible header for $(repr("text/plain", pkg)) in new cache file $(repr(tmppath)).") end seekend(f) write(f, crc_so) @@ -3358,7 +3358,7 @@ end function parse_cache_header(cachefile::String) io = open(cachefile, "r") try - iszero(isvalid_cache_header(io)) && throw(ArgumentError("Invalid header in cache file $cachefile.")) + iszero(isvalid_cache_header(io)) && throw(ArgumentError("Incompatible header in cache file $cachefile.")) ret = parse_cache_header(io, cachefile) return ret finally @@ -3371,7 +3371,7 @@ function preferences_hash(cachefile::String) io = open(cachefile, "r") try if iszero(isvalid_cache_header(io)) - throw(ArgumentError("Invalid header in cache file $cachefile.")) + throw(ArgumentError("Incompatible header in cache file $cachefile.")) end return preferences_hash(io, cachefile) finally @@ -3387,7 +3387,7 @@ end function cache_dependencies(cachefile::String) io = open(cachefile, "r") try - iszero(isvalid_cache_header(io)) && throw(ArgumentError("Invalid header in cache file $cachefile.")) + iszero(isvalid_cache_header(io)) && throw(ArgumentError("Incompatible header in cache file $cachefile.")) return cache_dependencies(io, cachefile) finally close(io) @@ -3427,7 +3427,7 @@ end function read_dependency_src(cachefile::String, filename::AbstractString) io = open(cachefile, "r") try - iszero(isvalid_cache_header(io)) && throw(ArgumentError("Invalid header in cache file $cachefile.")) + iszero(isvalid_cache_header(io)) && throw(ArgumentError("Incompatible header in cache file $cachefile.")) return read_dependency_src(io, cachefile, filename) finally close(io) @@ -3693,9 +3693,9 @@ end try checksum = isvalid_cache_header(io) if iszero(checksum) - @debug "Rejecting cache file $cachefile due to it containing an invalid cache header" - record_reason(reasons, "invalid header") - return true # invalid cache file + @debug "Rejecting cache file $cachefile due to it containing an incompatible cache header" + record_reason(reasons, "incompatible header") + return true # incompatible cache file end modules, (includes, _, requires), required_modules, srctextpos, prefs, prefs_hash, clone_targets, actual_flags = parse_cache_header(io, cachefile) if isempty(modules) diff --git a/stdlib/Logging/docs/src/index.md b/stdlib/Logging/docs/src/index.md index c2bde11720f4c..150d3e0e72705 100644 --- a/stdlib/Logging/docs/src/index.md +++ b/stdlib/Logging/docs/src/index.md @@ -191,10 +191,10 @@ module. Loading julia with `JULIA_DEBUG=loading` will activate ``` $ JULIA_DEBUG=loading julia -e 'using OhMyREPL' -┌ Debug: Rejecting cache file /home/user/.julia/compiled/v0.7/OhMyREPL.ji due to it containing an invalid cache header +┌ Debug: Rejecting cache file /home/user/.julia/compiled/v0.7/OhMyREPL.ji due to it containing an incompatible cache header └ @ Base loading.jl:1328 [ Info: Recompiling stale cache file /home/user/.julia/compiled/v0.7/OhMyREPL.ji for module OhMyREPL -┌ Debug: Rejecting cache file /home/user/.julia/compiled/v0.7/Tokenize.ji due to it containing an invalid cache header +┌ Debug: Rejecting cache file /home/user/.julia/compiled/v0.7/Tokenize.ji due to it containing an incompatible cache header └ @ Base loading.jl:1328 ... ``` From 6fadf5add7bc92aa5c9a54e67fa5521aa7b82ab7 Mon Sep 17 00:00:00 2001 From: Nick Robinson Date: Sat, 19 Oct 2024 09:27:56 +0100 Subject: [PATCH 19/76] Restore support for checking for UndefVarError variable name in at-test_throws (#56231) Fix https://github.com/JuliaLang/julia/issues/54082 Arguably this was a breaking change (as a consequence of https://github.com/JuliaLang/julia/pull/51979). But regardless, it seems like useful functionality to have a public API for testing that an `UndefVarError` was thrown for the expected variable name (regardless of scope). This is particularly useful if you don't know what the scope is (for example, in my use-case i want to test that a specific `UndefVarError` is thrown from a module with a `gensym`'d name). Pre-v1.11 the syntax for this was ```julia @test_throws UndefVarError(:x) foo() ``` but that stopped working in v1.11 when `UndefVarError` got a second field (in fact in v1.11.1 this is an error when before it would pass) This PR restores that functionality. We might want to backport it to v1.11.x so that v1.11 isn't the only version that doesn't support this. (cherry picked from commit b0c1525f1731186767ae42e7d625bf0909e49af8) --- stdlib/Test/src/Test.jl | 6 +++++- stdlib/Test/test/runtests.jl | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index ddd62ca9a10f8..4004e7289a305 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -809,7 +809,11 @@ function do_test_throws(result::ExecutionResult, orig_expr, extype) if extype isa LoadError && !(exc isa LoadError) && typeof(extype.error) == typeof(exc) extype = extype.error # deprecated end - if isa(exc, typeof(extype)) + # Support `UndefVarError(:x)` meaning `UndefVarError(:x, scope)` for any `scope`. + # Retains the behaviour from pre-v1.11 when `UndefVarError` didn't have `scope`. + if isa(extype, UndefVarError) && !isdefined(extype, :scope) + success = exc isa UndefVarError && exc.var == extype.var + else isa(exc, typeof(extype)) success = true for fld in 1:nfields(extype) if !isequal(getfield(extype, fld), getfield(exc, fld)) diff --git a/stdlib/Test/test/runtests.jl b/stdlib/Test/test/runtests.jl index 460e2eadf42b7..0a833c33aa026 100644 --- a/stdlib/Test/test/runtests.jl +++ b/stdlib/Test/test/runtests.jl @@ -1710,3 +1710,20 @@ end @test occursin(expected, result) end end + +# Issue #54082 +module M54082 end +@testset "@test_throws UndefVarError(:var)" begin + # Single-arg `UndefVarError` should match all `UndefVarError` for the + # same variable name, regardless of scope, to keep pre-v1.11 behaviour. + f54082() = var + @test_throws UndefVarError(:var) f54082() + # But if scope is set, then it has to match. + @test_throws UndefVarError(:var, M54082) M54082.var + let result = @testset NoThrowTestSet begin + # Wrong module scope + @test_throws UndefVarError(:var, Main) M54082.var + end + @test only(result) isa Test.Fail + end +end From aea077b8d1426db458fb94e91563c5f448e6010c Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 18 Mar 2024 17:08:32 -0400 Subject: [PATCH 20/76] fix functional assert statements (#53737) We currently never remove asserts but I still think this is not a good practice. (cherry picked from commit 8e67f99678a70b5d9eafe39430f12e296ceaf607) --- base/loading.jl | 8 ++++---- base/pkgid.jl | 3 ++- base/threadingconstructs.jl | 6 ++++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 3b3c17f18e825..a839798b9f9b9 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -3995,7 +3995,7 @@ function precompile(@nospecialize(argt::Type), m::Method) return precompile(mi) end -@assert precompile(include_package_for_output, (PkgId, String, Vector{String}, Vector{String}, Vector{String}, typeof(_concrete_dependencies), Nothing)) -@assert precompile(include_package_for_output, (PkgId, String, Vector{String}, Vector{String}, Vector{String}, typeof(_concrete_dependencies), String)) -@assert precompile(create_expr_cache, (PkgId, String, String, String, typeof(_concrete_dependencies), Cmd, IO, IO)) -@assert precompile(create_expr_cache, (PkgId, String, String, Nothing, typeof(_concrete_dependencies), Cmd, IO, IO)) +precompile(include_package_for_output, (PkgId, String, Vector{String}, Vector{String}, Vector{String}, typeof(_concrete_dependencies), Nothing)) || @assert false +precompile(include_package_for_output, (PkgId, String, Vector{String}, Vector{String}, Vector{String}, typeof(_concrete_dependencies), String)) || @assert false +precompile(create_expr_cache, (PkgId, String, String, String, typeof(_concrete_dependencies), Cmd, IO, IO)) || @assert false +precompile(create_expr_cache, (PkgId, String, String, Nothing, typeof(_concrete_dependencies), Cmd, IO, IO)) || @assert false diff --git a/base/pkgid.jl b/base/pkgid.jl index a3d3ead184bb8..8c776d79a69cb 100644 --- a/base/pkgid.jl +++ b/base/pkgid.jl @@ -37,7 +37,8 @@ end function binunpack(s::String) io = IOBuffer(s) - @assert read(io, UInt8) === 0x00 + z = read(io, UInt8) + @assert z === 0x00 uuid = read(io, UInt128) name = read(io, String) return PkgId(UUID(uuid), name) diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl index 4a0faff3b6bff..7212cb664f37e 100644 --- a/base/threadingconstructs.jl +++ b/base/threadingconstructs.jl @@ -158,7 +158,8 @@ function threading_run(fun, static) else # TODO: this should be the current pool (except interactive) if there # are ever more than two pools. - @assert ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, _sym_to_tpid(:default)) == 1 + _result = ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, _sym_to_tpid(:default)) + @assert _result == 1 end tasks[i] = t schedule(t) @@ -410,7 +411,8 @@ function _spawn_set_thrpool(t::Task, tp::Symbol) if tpid == -1 || _nthreads_in_pool(tpid) == 0 tpid = _sym_to_tpid(:default) end - @assert ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, tpid) == 1 + _result = ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, tpid) + @assert _result == 1 nothing end From d59df0ba307968ca9052c77b0f62e2d7d440b391 Mon Sep 17 00:00:00 2001 From: alongdate <167442392+alongdate@users.noreply.github.com> Date: Fri, 19 Apr 2024 20:25:49 +0800 Subject: [PATCH 21/76] Fix some typos in comments (#54149) Signed-off-by: alongdate (cherry picked from commit 2f9096218bf7b488dd7a2b6688365ba108a0e71f) --- contrib/generate_precompile.jl | 2 +- src/staticdata_utils.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 6e8ae120e01bb..49bcf3f43ea41 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -175,7 +175,7 @@ for match = Base._methods(+, (Int, Int), -1, Base.get_world_counter()) println(k) end - # interactive statup uses this + # interactive startup uses this write(IOBuffer(), "") # Not critical, but helps hide unrelated compilation from @time when using --trace-compile. diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 66ef05963bbe3..c6bfcae1dca8d 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -631,7 +631,7 @@ JL_DLLEXPORT uint8_t jl_match_cache_flags(uint8_t requested_flags, uint8_t actua actual_flags &= ~1; } - // 2. Check all flags, execept opt level must be exact + // 2. Check all flags, except opt level must be exact uint8_t mask = (1 << OPT_LEVEL)-1; if ((actual_flags & mask) != (requested_flags & mask)) return 0; From e91b8bedbdfb28147fb7ba643dcf6e8413254178 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sun, 20 Oct 2024 13:12:44 -0400 Subject: [PATCH 22/76] REPL: run repl hint generation for modeswitch chars when not switching (#56251) Fixes https://github.com/JuliaLang/julia/issues/56003 (cherry picked from commit 1fd7ada972911c181750d104604487a35ae3bea9) --- stdlib/REPL/src/REPL.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index e49d76e0d56bf..5561b046c77db 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1224,6 +1224,7 @@ function setup_interface( end else edit_insert(s, ';') + LineEdit.check_for_hint(s) && LineEdit.refresh_line(s) end end, '?' => function (s::MIState,o...) @@ -1234,6 +1235,7 @@ function setup_interface( end else edit_insert(s, '?') + LineEdit.check_for_hint(s) && LineEdit.refresh_line(s) end end, ']' => function (s::MIState,o...) @@ -1270,6 +1272,7 @@ function setup_interface( Base.errormonitor(t_replswitch) else edit_insert(s, ']') + LineEdit.check_for_hint(s) && LineEdit.refresh_line(s) end end, From 1f5c07f95b63ae2ae458a4e982b324d1be8a38c3 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Sun, 20 Oct 2024 21:09:44 -0400 Subject: [PATCH 23/76] =?UTF-8?q?=F0=9F=A4=96=20[backports-release-1.11]?= =?UTF-8?q?=20Bump=20the=20Pkg=20stdlib=20from=20aba90d22b=20to=209438b6e9?= =?UTF-8?q?9=20(#56261)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: Pkg URL: https://github.com/JuliaLang/Pkg.jl.git Stdlib branch: release-1.11 Julia branch: backports-release-1.11 Old commit: aba90d22b New commit: 9438b6e99 Julia version: 1.11.1 Pkg version: 1.11.0(Does not match) Bump invoked by: @IanButterworth Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaLang/Pkg.jl/compare/aba90d22b42a993b118276caa8df5146776d3844...9438b6e997aa1d63da16653350b130c45c1c086c ``` $ git log --oneline aba90d22b..9438b6e99 9438b6e99 Merge pull request #4055 from JuliaLang/backports-release-1.11 c83160aac REPLExt: use Base.isaccessibledir rather than isdir in completions (#4053) 7997ec320 REPLExt: run repl hint generation for modeswitch chars when not switching (#4054) ``` Co-authored-by: Dilum Aluthge --- .../Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/md5 | 1 + .../Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/sha512 | 1 + .../Pkg-aba90d22b42a993b118276caa8df5146776d3844.tar.gz/md5 | 1 - .../Pkg-aba90d22b42a993b118276caa8df5146776d3844.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/md5 create mode 100644 deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-aba90d22b42a993b118276caa8df5146776d3844.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-aba90d22b42a993b118276caa8df5146776d3844.tar.gz/sha512 diff --git a/deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/md5 b/deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/md5 new file mode 100644 index 0000000000000..98452bbc8f83b --- /dev/null +++ b/deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/md5 @@ -0,0 +1 @@ +c0906f18115dccaf61b2b7d2252beb2f diff --git a/deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/sha512 b/deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/sha512 new file mode 100644 index 0000000000000..162ddb54c8f4c --- /dev/null +++ b/deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/sha512 @@ -0,0 +1 @@ +d612a5aa88427b466ff2f5633f80578785e831e6ea8a3cd73bd411172b15b10141062d6685dea533b1be6a31504b5c69f1bf6d18e1934d83255738a61511f47e diff --git a/deps/checksums/Pkg-aba90d22b42a993b118276caa8df5146776d3844.tar.gz/md5 b/deps/checksums/Pkg-aba90d22b42a993b118276caa8df5146776d3844.tar.gz/md5 deleted file mode 100644 index f089de090242f..0000000000000 --- a/deps/checksums/Pkg-aba90d22b42a993b118276caa8df5146776d3844.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -d826591b2b1dd656aef3f278045ad8e2 diff --git a/deps/checksums/Pkg-aba90d22b42a993b118276caa8df5146776d3844.tar.gz/sha512 b/deps/checksums/Pkg-aba90d22b42a993b118276caa8df5146776d3844.tar.gz/sha512 deleted file mode 100644 index 6fe05afe2c667..0000000000000 --- a/deps/checksums/Pkg-aba90d22b42a993b118276caa8df5146776d3844.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -191c44b8a3520d727c2340ac4d0e43984cd0be89fa2abe8f7f8e0c161c434037ff3f974977621e375b59bc4cdd2489d9994fa26e9bc5543154820b1d64605a17 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 56fe7ac37a8f2..fa830950ce505 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.11 -PKG_SHA1 = aba90d22b42a993b118276caa8df5146776d3844 +PKG_SHA1 = 9438b6e997aa1d63da16653350b130c45c1c086c PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 168f7e76c211a50070f28cc9cb50eeb44b87253b Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 21 Oct 2024 12:38:11 +0530 Subject: [PATCH 24/76] Specialize adding/subtracting mixed Upper/LowerTriangular (#56149) --- stdlib/LinearAlgebra/src/triangular.jl | 20 +++++++++- stdlib/LinearAlgebra/test/triangular.jl | 52 +++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index b1de141b109c0..e3c96533a9864 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -152,6 +152,7 @@ UnitUpperTriangular const UpperOrUnitUpperTriangular{T,S} = Union{UpperTriangular{T,S}, UnitUpperTriangular{T,S}} const LowerOrUnitLowerTriangular{T,S} = Union{LowerTriangular{T,S}, UnitLowerTriangular{T,S}} const UpperOrLowerTriangular{T,S} = Union{UpperOrUnitUpperTriangular{T,S}, LowerOrUnitLowerTriangular{T,S}} +const UnitUpperOrUnitLowerTriangular{T,S} = Union{UnitUpperTriangular{T,S}, UnitLowerTriangular{T,S}} uppertriangular(M) = UpperTriangular(M) lowertriangular(M) = LowerTriangular(M) @@ -221,6 +222,16 @@ function Matrix{T}(A::UnitUpperTriangular) where T B end +function full(A::Union{UpperTriangular,LowerTriangular}) + return _triangularize(A)(parent(A)) +end +function full(A::UnitUpperOrUnitLowerTriangular) + isupper = A isa UnitUpperTriangular + Ap = _triangularize(A)(parent(A), isupper ? 1 : -1) + Ap[diagind(Ap, IndexStyle(Ap))] = @view A[diagind(A, IndexStyle(A))] + return Ap +end + function full!(A::LowerTriangular) B = A.data tril!(B) @@ -553,6 +564,9 @@ function copyto!(A::T, B::T) where {T<:Union{LowerTriangular,UnitLowerTriangular return A end +_triangularize(::UpperOrUnitUpperTriangular) = triu +_triangularize(::LowerOrUnitLowerTriangular) = tril + @inline _rscale_add!(A::AbstractTriangular, B::AbstractTriangular, C::Number, alpha::Number, beta::Number) = _triscale!(A, B, C, MulAddMul(alpha, beta)) @inline _lscale_add!(A::AbstractTriangular, B::Number, C::AbstractTriangular, alpha::Number, beta::Number) = @@ -812,7 +826,8 @@ function +(A::UnitLowerTriangular, B::UnitLowerTriangular) (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B LowerTriangular(tril(A.data, -1) + tril(B.data, -1) + 2I) end -+(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) + copyto!(similar(parent(B)), B) ++(A::UpperOrLowerTriangular, B::UpperOrLowerTriangular) = full(A) + full(B) ++(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A), size(A)), A) + copyto!(similar(parent(B), size(B)), B) function -(A::UpperTriangular, B::UpperTriangular) (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B @@ -846,7 +861,8 @@ function -(A::UnitLowerTriangular, B::UnitLowerTriangular) (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B LowerTriangular(tril(A.data, -1) - tril(B.data, -1)) end --(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) - copyto!(similar(parent(B)), B) +-(A::UpperOrLowerTriangular, B::UpperOrLowerTriangular) = full(A) - full(B) +-(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A), size(A)), A) - copyto!(similar(parent(B), size(B)), B) # use broadcasting if the parents are strided, where we loop only over the triangular part for op in (:+, :-) diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index f37bf05650452..f3e6e4a744088 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -25,6 +25,12 @@ debug && println("Test basic type functionality") @test_throws DimensionMismatch LowerTriangular(randn(5, 4)) @test LowerTriangular(randn(3, 3)) |> t -> [size(t, i) for i = 1:3] == [size(Matrix(t), i) for i = 1:3] +struct MyTriangular{T, A<:LinearAlgebra.AbstractTriangular{T}} <: LinearAlgebra.AbstractTriangular{T} + data :: A +end +Base.size(A::MyTriangular) = size(A.data) +Base.getindex(A::MyTriangular, i::Int, j::Int) = A.data[i,j] + # The following test block tries to call all methods in base/linalg/triangular.jl in order for a combination of input element types. Keep the ordering when adding code. @testset for elty1 in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}, Int) # Begin loop for first Triangular matrix @@ -1078,4 +1084,50 @@ end end end +@testset "addition/subtraction of mixed triangular" begin + for A in (Hermitian(rand(4, 4)), Diagonal(rand(5))) + for T in (UpperTriangular, LowerTriangular, + UnitUpperTriangular, UnitLowerTriangular) + B = T(A) + M = Matrix(B) + R = B - B' + if A isa Diagonal + @test R isa Diagonal + end + @test R == M - M' + R = B + B' + if A isa Diagonal + @test R isa Diagonal + end + @test R == M + M' + C = MyTriangular(B) + @test C - C' == M - M' + @test C + C' == M + M' + end + end + @testset "unfilled parent" begin + @testset for T in (UpperTriangular, LowerTriangular, + UnitUpperTriangular, UnitLowerTriangular) + F = Matrix{BigFloat}(undef, 2, 2) + B = T(F) + isupper = B isa Union{UpperTriangular, UnitUpperTriangular} + B[1+!isupper, 1+isupper] = 2 + if !(B isa Union{UnitUpperTriangular, UnitLowerTriangular}) + B[1,1] = B[2,2] = 3 + end + M = Matrix(B) + # These are broken, as triu/tril don't work with + # unfilled adjoint matrices + # See https://github.com/JuliaLang/julia/pull/55312 + @test_broken B - B' == M - M' + @test_broken B + B' == M + M' + @test B - copy(B') == M - M' + @test B + copy(B') == M + M' + C = MyTriangular(B) + @test C - C' == M - M' + @test C + C' == M + M' + end + end +end + end # module TestTriangular From 34a8c47b3d139937d008ae0e2a7476780609819e Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 18 Oct 2024 15:37:36 -0400 Subject: [PATCH 25/76] fix precompile process flag propagation (#56214) CacheFlags could get set, but were never propagated to the target process, so the result would be unusable. Additionally, the debug and optimization levels were not synchronized with the sysimg, causing a regression in pkgimage usability after moving out stdlibs. Fixes #56207 Fixes #56054 Fixes #56206 (cherry picked from commit 82b150645e318bcb6bcb450e945b3b689a1eb264) --- base/Base.jl | 2 +- base/loading.jl | 61 ++++++++++++++++++++++++----------- base/precompilation.jl | 72 +++++++++++++++++++++++++++--------------- base/show.jl | 5 ++- base/util.jl | 3 +- base/uuid.jl | 2 ++ pkgimage.mk | 3 +- src/staticdata_utils.c | 15 ++++----- test/loading.jl | 22 ++++++------- 9 files changed, 117 insertions(+), 68 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 0aa191206e3df..881e9f258dd5d 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -508,6 +508,7 @@ include("deepcopy.jl") include("download.jl") include("summarysize.jl") include("errorshow.jl") +include("util.jl") include("initdefs.jl") Filesystem.__postinit__() @@ -524,7 +525,6 @@ include("loading.jl") # misc useful functions & macros include("timing.jl") -include("util.jl") include("client.jl") include("asyncmap.jl") diff --git a/base/loading.jl b/base/loading.jl index a839798b9f9b9..e0d060c56f302 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1645,6 +1645,8 @@ function CacheFlags(cf::CacheFlags=CacheFlags(ccall(:jl_cache_flags, UInt8, ())) opt_level === nothing ? cf.opt_level : opt_level ) end +# reflecting jloptions.c defaults +const DefaultCacheFlags = CacheFlags(use_pkgimages=true, debug_level=isdebugbuild() ? 2 : 1, check_bounds=0, inline=true, opt_level=2) function _cacheflag_to_uint8(cf::CacheFlags)::UInt8 f = UInt8(0) @@ -1656,12 +1658,29 @@ function _cacheflag_to_uint8(cf::CacheFlags)::UInt8 return f end +function translate_cache_flags(cacheflags::CacheFlags, defaultflags::CacheFlags) + opts = String[] + cacheflags.use_pkgimages != defaultflags.use_pkgimages && push!(opts, cacheflags.use_pkgimages ? "--pkgimages=yes" : "--pkgimages=no") + cacheflags.debug_level != defaultflags.debug_level && push!(opts, "-g$(cacheflags.debug_level)") + cacheflags.check_bounds != defaultflags.check_bounds && push!(opts, ("--check-bounds=auto", "--check-bounds=yes", "--check-bounds=no")[cacheflags.check_bounds + 1]) + cacheflags.inline != defaultflags.inline && push!(opts, cacheflags.inline ? "--inline=yes" : "--inline=no") + cacheflags.opt_level != defaultflags.opt_level && push!(opts, "-O$(cacheflags.opt_level)") + return opts +end + function show(io::IO, cf::CacheFlags) - print(io, "use_pkgimages = ", cf.use_pkgimages) - print(io, ", debug_level = ", cf.debug_level) - print(io, ", check_bounds = ", cf.check_bounds) - print(io, ", inline = ", cf.inline) - print(io, ", opt_level = ", cf.opt_level) + print(io, "CacheFlags(") + print(io, "; use_pkgimages=") + print(io, cf.use_pkgimages) + print(io, ", debug_level=") + print(io, cf.debug_level) + print(io, ", check_bounds=") + print(io, cf.check_bounds) + print(io, ", inline=") + print(io, cf.inline) + print(io, ", opt_level=") + print(io, cf.opt_level) + print(io, ")") end struct ImageTarget @@ -2848,7 +2867,8 @@ end const PRECOMPILE_TRACE_COMPILE = Ref{String}() function create_expr_cache(pkg::PkgId, input::String, output::String, output_o::Union{Nothing, String}, - concrete_deps::typeof(_concrete_dependencies), flags::Cmd=``, internal_stderr::IO = stderr, internal_stdout::IO = stdout, isext::Bool=false) + concrete_deps::typeof(_concrete_dependencies), flags::Cmd=``, cacheflags::CacheFlags=CacheFlags(), + internal_stderr::IO = stderr, internal_stdout::IO = stdout, isext::Bool=false) @nospecialize internal_stderr internal_stdout rm(output, force=true) # Remove file if it exists output_o === nothing || rm(output_o, force=true) @@ -2891,24 +2911,29 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: deps = deps_eltype * "[" * join(deps_strs, ",") * "]" precomp_stack = "Base.PkgId[$(join(map(pkg_str, vcat(Base.precompilation_stack, pkg)), ", "))]" + if output_o === nothing + # remove options that make no difference given the other cache options + cacheflags = CacheFlags(cacheflags, opt_level=0) + end + opts = translate_cache_flags(cacheflags, CacheFlags()) # julia_cmd is generated for the running system, and must be fixed if running for precompile instead if output_o !== nothing @debug "Generating object cache file for $(repr("text/plain", pkg))" cpu_target = get(ENV, "JULIA_CPU_TARGET", nothing) - opts = `--output-o $(output_o) --output-ji $(output) --output-incremental=yes` + push!(opts, "--output-o", output_o) else @debug "Generating cache file for $(repr("text/plain", pkg))" cpu_target = nothing - opts = `-O0 --output-ji $(output) --output-incremental=yes` end + push!(opts, "--output-ji", output) + isassigned(PRECOMPILE_TRACE_COMPILE) && push!(opts, "--trace-compile=$(PRECOMPILE_TRACE_COMPILE[])") - trace = isassigned(PRECOMPILE_TRACE_COMPILE) ? `--trace-compile=$(PRECOMPILE_TRACE_COMPILE[]) --trace-compile-timing` : `` io = open(pipeline(addenv(`$(julia_cmd(;cpu_target)::Cmd) - $(flags) - $(opts) - --startup-file=no --history-file=no --warn-overwrite=yes - --color=$(have_color === nothing ? "auto" : have_color ? "yes" : "no") - $trace - -`, + $(flags) + $(opts) + --output-incremental=yes + --startup-file=no --history-file=no --warn-overwrite=yes + $(have_color === nothing ? "--color=auto" : have_color ? "--color=yes" : "--color=no") + -`, "OPENBLAS_NUM_THREADS" => 1, "JULIA_NUM_THREADS" => 1), stderr = internal_stderr, stdout = internal_stdout), @@ -3026,7 +3051,7 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in close(tmpio_o) close(tmpio_so) end - p = create_expr_cache(pkg, path, tmppath, tmppath_o, concrete_deps, flags, internal_stderr, internal_stdout, isext) + p = create_expr_cache(pkg, path, tmppath, tmppath_o, concrete_deps, flags, cacheflags, internal_stderr, internal_stdout, isext) if success(p) if cache_objects @@ -3997,5 +4022,5 @@ end precompile(include_package_for_output, (PkgId, String, Vector{String}, Vector{String}, Vector{String}, typeof(_concrete_dependencies), Nothing)) || @assert false precompile(include_package_for_output, (PkgId, String, Vector{String}, Vector{String}, Vector{String}, typeof(_concrete_dependencies), String)) || @assert false -precompile(create_expr_cache, (PkgId, String, String, String, typeof(_concrete_dependencies), Cmd, IO, IO)) || @assert false -precompile(create_expr_cache, (PkgId, String, String, Nothing, typeof(_concrete_dependencies), Cmd, IO, IO)) || @assert false +precompile(create_expr_cache, (PkgId, String, String, String, typeof(_concrete_dependencies), Cmd, CacheFlags, IO, IO)) || @assert false +precompile(create_expr_cache, (PkgId, String, String, Nothing, typeof(_concrete_dependencies), Cmd, CacheFlags, IO, IO)) || @assert false diff --git a/base/precompilation.jl b/base/precompilation.jl index 34720a1078816..0cd0f6346c308 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -349,7 +349,7 @@ function printpkgstyle(io, header, msg; color=:light_green) end const Config = Pair{Cmd, Base.CacheFlags} -const PkgConfig = Tuple{Base.PkgId,Config} +const PkgConfig = Tuple{PkgId,Config} function precompilepkgs(pkgs::Vector{String}=String[]; internal_call::Bool=false, @@ -360,10 +360,23 @@ function precompilepkgs(pkgs::Vector{String}=String[]; configs::Union{Config,Vector{Config}}=(``=>Base.CacheFlags()), io::IO=stderr, # asking for timing disables fancy mode, as timing is shown in non-fancy mode - fancyprint::Bool = can_fancyprint(io) && !timing - ) + fancyprint::Bool = can_fancyprint(io) && !timing) + # monomorphize this to avoid latency problems + _precompilepkgs(pkgs, internal_call, strict, warn_loaded, timing, _from_loading, + configs isa Vector{Config} ? configs : [configs], + IOContext{IO}(io), fancyprint) +end - configs = configs isa Config ? [configs] : configs +function _precompilepkgs(pkgs::Vector{String}, + internal_call::Bool, + strict::Bool, + warn_loaded::Bool, + timing::Bool, + _from_loading::Bool, + configs::Vector{Config}, + io::IOContext{IO}, + fancyprint::Bool) + requested_pkgs = copy(pkgs) # for understanding user intent time_start = time_ns() @@ -379,17 +392,32 @@ function precompilepkgs(pkgs::Vector{String}=String[]; if _from_loading && !Sys.isinteractive() && Base.get_bool_env("JULIA_TESTS", false) # suppress passive loading printing in julia test suite. `JULIA_TESTS` is set in Base.runtests - io = devnull + io = IOContext{IO}(devnull) end + nconfigs = length(configs) hascolor = get(io, :color, false)::Bool color_string(cstr::String, col::Union{Int64, Symbol}) = _color_string(cstr, col, hascolor) stale_cache = Dict{StaleCacheKey, Bool}() - exts = Dict{Base.PkgId, String}() # ext -> parent + exts = Dict{PkgId, String}() # ext -> parent # make a flat map of each dep and its direct deps - depsmap = Dict{Base.PkgId, Vector{Base.PkgId}}() - pkg_exts_map = Dict{Base.PkgId, Vector{Base.PkgId}}() + depsmap = Dict{PkgId, Vector{PkgId}}() + pkg_exts_map = Dict{PkgId, Vector{PkgId}}() + + function describe_pkg(pkg::PkgId, is_direct_dep::Bool, flags::Cmd, cacheflags::Base.CacheFlags) + name = haskey(exts, pkg) ? string(exts[pkg], " → ", pkg.name) : pkg.name + name = is_direct_dep ? name : color_string(name, :light_black) + if nconfigs > 1 && !isempty(flags) + config_str = join(flags, " ") + name *= color_string(" `$config_str`", :light_black) + end + if nconfigs > 1 + config_str = join(Base.translate_cache_flags(cacheflags, Base.DefaultCacheFlags), " ") + name *= color_string(" $config_str", :light_black) + end + return name + end for (dep, deps) in env.deps pkg = Base.PkgId(dep, env.names[dep]) @@ -554,7 +582,6 @@ function precompilepkgs(pkgs::Vector{String}=String[]; else target = "project" end - nconfigs = length(configs) if nconfigs == 1 if !isempty(only(configs)[1]) target *= " for configuration $(join(only(configs)[1], " "))" @@ -569,7 +596,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; failed_deps = Dict{PkgConfig, String}() precomperr_deps = PkgConfig[] # packages that may succeed after a restart (i.e. loaded packages with no cache file) - print_lock = io isa Base.LibuvStream ? io.lock::ReentrantLock : ReentrantLock() + print_lock = io.io isa Base.LibuvStream ? io.io.lock::ReentrantLock : ReentrantLock() first_started = Base.Event() printloop_should_exit::Bool = !fancyprint # exit print loop immediately if not fancy printing interrupted_or_done = Base.Event() @@ -658,7 +685,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; n_print_rows = 0 while !printloop_should_exit lock(print_lock) do - term_size = Base.displaysize_(io) + term_size = displaysize(io) num_deps_show = term_size[1] - 3 pkg_queue_show = if !interrupted_or_done.set && length(pkg_queue) > num_deps_show last(pkg_queue, num_deps_show) @@ -673,7 +700,7 @@ function precompilepkgs(pkgs::Vector{String}=String[]; bar.max = n_total - n_already_precomp # when sizing to the terminal width subtract a little to give some tolerance to resizing the # window between print cycles - termwidth = Base.displaysize_(io)[2] - 4 + termwidth = displaysize(io)[2] - 4 if !final_loop str = sprint(io -> show_progress(io, bar; termwidth, carriagereturn=false); context=io) print(iostr, Base._truncate_at_width_or_chars(true, str, termwidth), "\n") @@ -681,12 +708,8 @@ function precompilepkgs(pkgs::Vector{String}=String[]; for pkg_config in pkg_queue_show dep, config = pkg_config loaded = warn_loaded && haskey(Base.loaded_modules, dep) - _name = haskey(exts, dep) ? string(exts[dep], " → ", dep.name) : dep.name - name = dep in direct_deps ? _name : string(color_string(_name, :light_black)) - if nconfigs > 1 && !isempty(config[1]) - config_str = "$(join(config[1], " "))" - name *= color_string(" $(config_str)", :light_black) - end + flags, cacheflags = config + name = describe_pkg(dep, dep in direct_deps, flags, cacheflags) line = if pkg_config in precomperr_deps string(color_string(" ? ", Base.warn_color()), name) elseif haskey(failed_deps, pkg_config) @@ -774,14 +797,11 @@ function precompilepkgs(pkgs::Vector{String}=String[]; std_pipe = Base.link_pipe!(Pipe(); reader_supports_async=true, writer_supports_async=true) t_monitor = @async monitor_std(pkg_config, std_pipe; single_requested_pkg) - _name = haskey(exts, pkg) ? string(exts[pkg], " → ", pkg.name) : pkg.name - name = is_direct_dep ? _name : string(color_string(_name, :light_black)) - if nconfigs > 1 && !isempty(flags) - config_str = "$(join(flags, " "))" - name *= color_string(" $(config_str)", :light_black) - end - !fancyprint && lock(print_lock) do - isempty(pkg_queue) && printpkgstyle(io, :Precompiling, target) + name = describe_pkg(pkg, is_direct_dep, flags, cacheflags) + lock(print_lock) do + if !fancyprint && isempty(pkg_queue) + printpkgstyle(io, :Precompiling, something(target, "packages...")) + end end push!(pkg_queue, pkg_config) started[pkg_config] = true diff --git a/base/show.jl b/base/show.jl index 724fc70f89678..3bec8a5c885a2 100644 --- a/base/show.jl +++ b/base/show.jl @@ -324,8 +324,11 @@ end convert(::Type{IOContext}, io::IOContext) = io convert(::Type{IOContext}, io::IO) = IOContext(io, ioproperties(io))::IOContext +convert(::Type{IOContext{IO_t}}, io::IOContext{IO_t}) where {IO_t} = io +convert(::Type{IOContext{IO_t}}, io::IO) where {IO_t} = IOContext{IO_t}(io, ioproperties(io))::IOContext{IO_t} IOContext(io::IO) = convert(IOContext, io) +IOContext{IO_t}(io::IO) where {IO_t} = convert(IOContext{IO_t}, io) function IOContext(io::IO, KV::Pair) d = ioproperties(io) @@ -427,7 +430,7 @@ get(io::IO, key, default) = default keys(io::IOContext) = keys(io.dict) keys(io::IO) = keys(ImmutableDict{Symbol,Any}()) -displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize]::Tuple{Int,Int} : Base.displaysize_(io.io) +displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize]::Tuple{Int,Int} : displaysize(io.io) show_circular(io::IO, @nospecialize(x)) = false function show_circular(io::IOContext, @nospecialize(x)) diff --git a/base/util.jl b/base/util.jl index 5e86f026f8f9a..3a621211162ec 100644 --- a/base/util.jl +++ b/base/util.jl @@ -249,7 +249,7 @@ function julia_cmd(julia=joinpath(Sys.BINDIR, julia_exename()); cpu_target::Unio end function julia_exename() - if !Base.isdebugbuild() + if !isdebugbuild() return @static Sys.iswindows() ? "julia.exe" : "julia" else return @static Sys.iswindows() ? "julia-debug.exe" : "julia-debug" @@ -512,7 +512,6 @@ function _crc32c(io::IO, nb::Integer, crc::UInt32=0x00000000) end _crc32c(io::IO, crc::UInt32=0x00000000) = _crc32c(io, typemax(Int64), crc) _crc32c(io::IOStream, crc::UInt32=0x00000000) = _crc32c(io, filesize(io)-position(io), crc) -_crc32c(uuid::UUID, crc::UInt32=0x00000000) = _crc32c(uuid.value, crc) _crc32c(x::UInt128, crc::UInt32=0x00000000) = ccall(:jl_crc32c, UInt32, (UInt32, Ref{UInt128}, Csize_t), crc, x, 16) _crc32c(x::UInt64, crc::UInt32=0x00000000) = diff --git a/base/uuid.jl b/base/uuid.jl index 9b2da3c6409db..56f3a6aa417e7 100644 --- a/base/uuid.jl +++ b/base/uuid.jl @@ -36,6 +36,8 @@ let Base.hash(uuid::UUID, h::UInt) = hash(uuid_hash_seed, hash(convert(NTuple{2, UInt64}, uuid), h)) end +_crc32c(uuid::UUID, crc::UInt32=0x00000000) = _crc32c(uuid.value, crc) + let @inline function uuid_kernel(s, i, u) _c = UInt32(@inbounds codeunit(s, i)) diff --git a/pkgimage.mk b/pkgimage.mk index 0bc035ee03b08..78b2618be549f 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -25,7 +25,8 @@ print-depot-path: @$(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e '@show Base.DEPOT_PATH') $(BUILDDIR)/stdlib/%.image: $(JULIAHOME)/stdlib/Project.toml $(JULIAHOME)/stdlib/Manifest.toml $(INDEPENDENT_STDLIBS_SRCS) $(JULIA_DEPOT_PATH)/compiled - @$(call PRINT_JULIA, JULIA_CPU_TARGET="$(JULIA_CPU_TARGET)" $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.Precompilation.precompilepkgs(;configs=[``=>Base.CacheFlags(), `--check-bounds=yes`=>Base.CacheFlags(;check_bounds=1)])') + @$(call PRINT_JULIA, JULIA_CPU_TARGET="$(JULIA_CPU_TARGET)" $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e \ + 'Base.Precompilation.precompilepkgs(configs=[``=>Base.CacheFlags(debug_level=2, opt_level=3), ``=>Base.CacheFlags(check_bounds=1, debug_level=2, opt_level=3)])') touch $@ $(BUILDDIR)/stdlib/release.image: $(build_private_libdir)/sys.$(SHLIB_EXT) diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index c6bfcae1dca8d..1669bcc10d0ca 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -600,15 +600,15 @@ static void write_mod_list(ios_t *s, jl_array_t *a) write_int32(s, 0); } -// OPT_LEVEL should always be the upper bits #define OPT_LEVEL 6 +#define DEBUG_LEVEL 1 JL_DLLEXPORT uint8_t jl_cache_flags(void) { // OOICCDDP uint8_t flags = 0; flags |= (jl_options.use_pkgimages & 1); // 0-bit - flags |= (jl_options.debug_level & 3) << 1; // 1-2 bit + flags |= (jl_options.debug_level & 3) << DEBUG_LEVEL; // 1-2 bit flags |= (jl_options.check_bounds & 3) << 3; // 3-4 bit flags |= (jl_options.can_inline & 1) << 5; // 5-bit flags |= (jl_options.opt_level & 3) << OPT_LEVEL; // 6-7 bit @@ -631,14 +631,13 @@ JL_DLLEXPORT uint8_t jl_match_cache_flags(uint8_t requested_flags, uint8_t actua actual_flags &= ~1; } - // 2. Check all flags, except opt level must be exact - uint8_t mask = (1 << OPT_LEVEL)-1; + // 2. Check all flags, except opt level and debug level must be exact + uint8_t mask = (~(3u << OPT_LEVEL) & ~(3u << DEBUG_LEVEL)) & 0x7f; if ((actual_flags & mask) != (requested_flags & mask)) return 0; - // 3. allow for higher optimization flags in cache - actual_flags >>= OPT_LEVEL; - requested_flags >>= OPT_LEVEL; - return actual_flags >= requested_flags; + // 3. allow for higher optimization and debug level flags in cache to minimize required compile option combinations + return ((actual_flags >> OPT_LEVEL) & 3) >= ((requested_flags >> OPT_LEVEL) & 3) && + ((actual_flags >> DEBUG_LEVEL) & 3) >= ((requested_flags >> DEBUG_LEVEL) & 3); } JL_DLLEXPORT uint8_t jl_match_cache_flags_current(uint8_t flags) diff --git a/test/loading.jl b/test/loading.jl index f06b0fd65ffc7..664bd778ca926 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1212,10 +1212,7 @@ end @test cf.check_bounds == 3 @test cf.inline @test cf.opt_level == 3 - - io = PipeBuffer() - show(io, cf) - @test read(io, String) == "use_pkgimages = true, debug_level = 3, check_bounds = 3, inline = true, opt_level = 3" + @test repr(cf) == "CacheFlags(; use_pkgimages=true, debug_level=3, check_bounds=3, inline=true, opt_level=3)" end empty!(Base.DEPOT_PATH) @@ -1403,13 +1400,16 @@ end "JULIA_LOAD_PATH" => dir, "JULIA_DEBUG" => "loading") - out = Pipe() - proc = run(pipeline(cmd, stdout=out, stderr=out)) - close(out.in) - - log = @async String(read(out)) - @test success(proc) - fetch(log) + out = Base.PipeEndpoint() + log = @async read(out, String) + try + proc = run(pipeline(cmd, stdout=out, stderr=out)) + @test success(proc) + catch + @show fetch(log) + rethrow() + end + return fetch(log) end log = load_package("Parent", `--compiled-modules=no --pkgimages=no`) From ba0e9b96a54581da6724535d565e55e9cc74f6ea Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 21 Oct 2024 07:46:43 -0400 Subject: [PATCH 26/76] Actually setup jit targets when compiling packageimages instead of targeting only one (#54471) Co-authored-by: Gabriel Baraldi Co-authored-by: Dilum Aluthge --- src/codegen.cpp | 5 ++- src/llvm-multiversioning.cpp | 1 + src/processor_arm.cpp | 52 +++++++++++++++++++++-- src/processor_fallback.cpp | 23 +++++++++-- src/processor_x86.cpp | 80 ++++++++++++++++++++++++++++++++++-- test/precompile.jl | 13 ++++++ 6 files changed, 162 insertions(+), 12 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 951cc1c86f9ee..a0a4136ca6224 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7260,8 +7260,11 @@ static Function* gen_cfun_wrapper( ctx.builder.ClearInsertionPoint(); if (aliasname) { - GlobalAlias::create(cw->getValueType(), cw->getType()->getAddressSpace(), + auto alias = GlobalAlias::create(cw->getValueType(), cw->getType()->getAddressSpace(), GlobalValue::ExternalLinkage, aliasname, cw, M); + if(ctx.emission_context.TargetTriple.isOSBinFormatCOFF()) { + alias->setDLLStorageClass(GlobalValue::DLLStorageClassTypes::DLLExportStorageClass); + } } if (nest) { diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index d1c3f9bc464eb..c7175859acf83 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -673,6 +673,7 @@ void CloneCtx::rewrite_alias(GlobalAlias *alias, Function *F) trampoline->removeFnAttr("julia.mv.reloc"); trampoline->removeFnAttr("julia.mv.clones"); trampoline->addFnAttr("julia.mv.alias"); + trampoline->setDLLStorageClass(alias->getDLLStorageClass()); alias->eraseFromParent(); uint32_t id; diff --git a/src/processor_arm.cpp b/src/processor_arm.cpp index 0d069b085ac47..955209ea9c90b 100644 --- a/src/processor_arm.cpp +++ b/src/processor_arm.cpp @@ -1890,12 +1890,56 @@ const std::pair &jl_get_llvm_disasm_target(void) return res; } +#ifndef __clang_gcanalyzer__ llvm::SmallVector jl_get_llvm_clone_targets(void) { - if (jit_targets.empty()) - jl_error("JIT targets not initialized"); + + auto &cmdline = get_cmdline_targets(); + check_cmdline(cmdline, true); + llvm::SmallVector, 0> image_targets; + for (auto &arg: cmdline) { + auto data = arg_target_data(arg, image_targets.empty()); + image_targets.push_back(std::move(data)); + } + auto ntargets = image_targets.size(); + if (image_targets.empty()) + jl_error("No targets specified"); llvm::SmallVector res; - for (auto &target: jit_targets) { + // Now decide the clone condition. + for (size_t i = 1; i < ntargets; i++) { + auto &t = image_targets[i]; + if (t.en.flags & JL_TARGET_CLONE_ALL) + continue; + auto &features0 = image_targets[t.base].en.features; + // Always clone when code checks CPU features + t.en.flags |= JL_TARGET_CLONE_CPU; + static constexpr uint32_t clone_fp16[] = {Feature::fp16fml,Feature::fullfp16}; + for (auto fe: clone_fp16) { + if (!test_nbit(features0, fe) && test_nbit(t.en.features, fe)) { + t.en.flags |= JL_TARGET_CLONE_FLOAT16; + break; + } + } + // The most useful one in general... + t.en.flags |= JL_TARGET_CLONE_LOOP; +#ifdef _CPU_ARM_ + static constexpr uint32_t clone_math[] = {Feature::vfp3, Feature::vfp4, Feature::neon}; + for (auto fe: clone_math) { + if (!test_nbit(features0, fe) && test_nbit(t.en.features, fe)) { + t.en.flags |= JL_TARGET_CLONE_MATH; + break; + } + } + static constexpr uint32_t clone_simd[] = {Feature::neon}; + for (auto fe: clone_simd) { + if (!test_nbit(features0, fe) && test_nbit(t.en.features, fe)) { + t.en.flags |= JL_TARGET_CLONE_SIMD; + break; + } + } +#endif + } + for (auto &target: image_targets) { auto features_en = target.en.features; auto features_dis = target.dis.features; for (auto &fename: feature_names) { @@ -1916,6 +1960,8 @@ llvm::SmallVector jl_get_llvm_clone_targets(void) return res; } +#endif + extern "C" int jl_test_cpu_feature(jl_cpu_feature_t feature) { if (feature >= 32 * feature_sz) diff --git a/src/processor_fallback.cpp b/src/processor_fallback.cpp index 1c266e29e9dd7..bb39b15018ad5 100644 --- a/src/processor_fallback.cpp +++ b/src/processor_fallback.cpp @@ -144,13 +144,27 @@ const std::pair &jl_get_llvm_disasm_target(void) jl_get_cpu_features_llvm(), {{}, 0}, {{}, 0}, 0}); return res; } - +#ifndef __clang_gcanalyzer__ llvm::SmallVector jl_get_llvm_clone_targets(void) { - if (jit_targets.empty()) - jl_error("JIT targets not initialized"); + + auto &cmdline = get_cmdline_targets(); + check_cmdline(cmdline, true); + llvm::SmallVector, 0> image_targets; + for (auto &arg: cmdline) { + auto data = arg_target_data(arg, image_targets.empty()); + image_targets.push_back(std::move(data)); + } + auto ntargets = image_targets.size(); + // Now decide the clone condition. + for (size_t i = 1; i < ntargets; i++) { + auto &t = image_targets[i]; + t.en.flags |= JL_TARGET_CLONE_ALL; + } + if (image_targets.empty()) + jl_error("No image targets found"); llvm::SmallVector res; - for (auto &target: jit_targets) { + for (auto &target: image_targets) { jl_target_spec_t ele; std::tie(ele.cpu_name, ele.cpu_features) = get_llvm_target_str(target); ele.data = serialize_target_data(target.name, target.en.features, @@ -161,6 +175,7 @@ llvm::SmallVector jl_get_llvm_clone_targets(void) } return res; } +#endif JL_DLLEXPORT jl_value_t *jl_cpu_has_fma(int bits) { diff --git a/src/processor_x86.cpp b/src/processor_x86.cpp index 24363d2d9d28f..5d832c137337e 100644 --- a/src/processor_x86.cpp +++ b/src/processor_x86.cpp @@ -910,6 +910,8 @@ static uint32_t pkgimg_init_cb(const void *id, jl_value_t **rejection_reason) return match.best_idx; } +//This function serves as a fallback during bootstrapping, at that point we don't have a sysimage with native code +// so we won't call sysimg_init_cb, else this function shouldn't do anything. static void ensure_jit_target(bool imaging) { auto &cmdline = get_cmdline_targets(); @@ -1102,13 +1104,82 @@ const std::pair &jl_get_llvm_disasm_target(void) {feature_masks, 0}, {{}, 0}, 0}); return res; } - +//This function parses the -C command line to figure out which targets to multiversion to. +#ifndef __clang_gcanalyzer__ llvm::SmallVector jl_get_llvm_clone_targets(void) { - if (jit_targets.empty()) - jl_error("JIT targets not initialized"); + auto &cmdline = get_cmdline_targets(); + check_cmdline(cmdline, true); + llvm::SmallVector, 0> image_targets; + for (auto &arg: cmdline) { + auto data = arg_target_data(arg, image_targets.empty()); + image_targets.push_back(std::move(data)); + } + + auto ntargets = image_targets.size(); + // Now decide the clone condition. + for (size_t i = 1; i < ntargets; i++) { + auto &t = image_targets[i]; + if (t.en.flags & JL_TARGET_CLONE_ALL) + continue; + // Always clone when code checks CPU features + t.en.flags |= JL_TARGET_CLONE_CPU; + // The most useful one in general... + t.en.flags |= JL_TARGET_CLONE_LOOP; + auto &features0 = image_targets[t.base].en.features; + // Special case for KNL/KNM since they're so different + if (!(t.dis.flags & JL_TARGET_CLONE_ALL)) { + if ((t.name == "knl" || t.name == "knm") && + image_targets[t.base].name != "knl" && image_targets[t.base].name != "knm") { + t.en.flags |= JL_TARGET_CLONE_ALL; + break; + } + } + static constexpr uint32_t clone_math[] = {Feature::fma, Feature::fma4}; + static constexpr uint32_t clone_simd[] = {Feature::sse3, Feature::ssse3, + Feature::sse41, Feature::sse42, + Feature::avx, Feature::avx2, + Feature::vaes, Feature::vpclmulqdq, + Feature::sse4a, Feature::avx512f, + Feature::avx512dq, Feature::avx512ifma, + Feature::avx512pf, Feature::avx512er, + Feature::avx512cd, Feature::avx512bw, + Feature::avx512vl, Feature::avx512vbmi, + Feature::avx512vpopcntdq, Feature::avxvnni, + Feature::avx512vbmi2, Feature::avx512vnni, + Feature::avx512bitalg, Feature::avx512bf16, + Feature::avx512vp2intersect, Feature::avx512fp16}; + for (auto fe: clone_math) { + if (!test_nbit(features0, fe) && test_nbit(t.en.features, fe)) { + t.en.flags |= JL_TARGET_CLONE_MATH; + break; + } + } + for (auto fe: clone_simd) { + if (!test_nbit(features0, fe) && test_nbit(t.en.features, fe)) { + t.en.flags |= JL_TARGET_CLONE_SIMD; + break; + } + } + static constexpr uint32_t clone_fp16[] = {Feature::avx512fp16}; + for (auto fe: clone_fp16) { + if (!test_nbit(features0, fe) && test_nbit(t.en.features, fe)) { + t.en.flags |= JL_TARGET_CLONE_FLOAT16; + break; + } + } + static constexpr uint32_t clone_bf16[] = {Feature::avx512bf16}; + for (auto fe: clone_bf16) { + if (!test_nbit(features0, fe) && test_nbit(t.en.features, fe)) { + t.en.flags |= JL_TARGET_CLONE_BFLOAT16; + break; + } + } + } + if (image_targets.empty()) + jl_error("No targets specified"); llvm::SmallVector res; - for (auto &target: jit_targets) { + for (auto &target: image_targets) { auto features_en = target.en.features; auto features_dis = target.dis.features; for (auto &fename: feature_names) { @@ -1128,6 +1199,7 @@ llvm::SmallVector jl_get_llvm_clone_targets(void) } return res; } +#endif extern "C" int jl_test_cpu_feature(jl_cpu_feature_t feature) { diff --git a/test/precompile.jl b/test/precompile.jl index 4dfa5f672072f..95634300704b3 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -2124,6 +2124,19 @@ precompile_test_harness("Test flags") do load_path @test !Base.isprecompiled(id, ;flags=current_flags) end +if Base.get_bool_env("CI", false) && (Sys.ARCH === :x86_64 || Sys.ARCH === :aarch64) + @testset "Multiversioning" begin # This test isn't the most robust because it relies on being in CI, + pkg = Base.identify_package("Test") # but we need better target reflection to make a better one. + cachefiles = Base.find_all_in_cache_path(pkg) + pkgpath = Base.locate_package(pkg) + idx = findfirst(cachefiles) do cf + Base.stale_cachefile(pkgpath, cf) !== true + end + targets = Base.parse_image_targets(Base.parse_cache_header(cachefiles[idx])[7]) + @test length(targets) > 1 + end +end + precompile_test_harness("Issue #52063") do load_path fname = joinpath(load_path, "i_do_not_exist.jl") @test try From 7bc6e0c2bd6c1aeef3cb145f7d1c1a84eeb3fa2a Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Fri, 17 May 2024 20:51:31 -0300 Subject: [PATCH 27/76] Make ASAN build not be debug, following clangs recommendation (#54094) (cherry picked from commit 6c17db1ba12e8b6922ff0bf25d39511b31ce4cb4) --- contrib/asan/Make.user.asan | 3 --- sysimage.mk | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/contrib/asan/Make.user.asan b/contrib/asan/Make.user.asan index 96ed13b54e0f9..ba2d549d1f178 100644 --- a/contrib/asan/Make.user.asan +++ b/contrib/asan/Make.user.asan @@ -16,9 +16,6 @@ override SANITIZE_ADDRESS=1 # make the GC use regular malloc/frees, which are hooked by ASAN override WITH_GC_DEBUG_ENV=1 -# default to a debug build for better line number reporting -override JULIA_BUILD_MODE=debug - # Enable Julia assertions and LLVM assertions FORCE_ASSERTIONS=1 LLVM_ASSERTIONS=1 diff --git a/sysimage.mk b/sysimage.mk index 96f26b071a997..d0e106d4ce3da 100644 --- a/sysimage.mk +++ b/sysimage.mk @@ -63,13 +63,13 @@ RELBUILDROOT := $(call rel_path,$(JULIAHOME)/base,$(BUILDROOT)/base)/ # <-- make $(build_private_libdir)/corecompiler.ji: $(COMPILER_SRCS) @$(call PRINT_JULIA, cd $(JULIAHOME)/base && \ $(call spawn,$(JULIA_EXECUTABLE)) -C "$(JULIA_CPU_TARGET)" $(HEAPLIM) --output-ji $(call cygpath_w,$@).tmp \ - --startup-file=no --warn-overwrite=yes -g$(BOOTSTRAP_DEBUG_LEVEL) -O0 compiler/compiler.jl) + --startup-file=no --warn-overwrite=yes -g$(BOOTSTRAP_DEBUG_LEVEL) -O1 compiler/compiler.jl) @mv $@.tmp $@ $(build_private_libdir)/sys.ji: $(build_private_libdir)/corecompiler.ji $(JULIAHOME)/VERSION $(BASE_SRCS) $(STDLIB_SRCS) @$(call PRINT_JULIA, cd $(JULIAHOME)/base && \ if ! JULIA_BINDIR=$(call cygpath_w,$(build_bindir)) WINEPATH="$(call cygpath_w,$(build_bindir));$$WINEPATH" \ - $(call spawn, $(JULIA_EXECUTABLE)) -g1 -O0 -C "$(JULIA_CPU_TARGET)" $(HEAPLIM) --output-ji $(call cygpath_w,$@).tmp $(JULIA_SYSIMG_BUILD_FLAGS) \ + $(call spawn, $(JULIA_EXECUTABLE)) -g1 -O1 -C "$(JULIA_CPU_TARGET)" $(HEAPLIM) --output-ji $(call cygpath_w,$@).tmp $(JULIA_SYSIMG_BUILD_FLAGS) \ --startup-file=no --warn-overwrite=yes --sysimage $(call cygpath_w,$<) sysimg.jl $(RELBUILDROOT); then \ echo '*** This error might be fixed by running `make clean`. If the error persists$(COMMA) try `make cleanall`. ***'; \ false; \ From 8d07746268f2211d8b0bb6645bf8c59299d5ded2 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 21 Oct 2024 12:04:40 -0400 Subject: [PATCH 28/76] add Pkg 1.11 NEWS --- NEWS.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/NEWS.md b/NEWS.md index aec96ccd0b21c..20b17cf6f68cd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -139,6 +139,17 @@ Standard library changes `length(::Stateful)` method. The last type parameter of `Stateful` is gone, too. Issue: ([#47790]), PR: ([#51747]). +#### Package Manager + +* It is now possible to specify "sources" for packages in a `[sources]` section in Project.toml. + This can be used to add non-registered normal or test dependencies. +* Pkg now obeys `[compat]` bounds for `julia` and raises an error if the version of the running Julia binary is incompatible with the bounds in `Project.toml`. + Pkg has always obeyed this compat when working with Registry packages. This change affects mostly local packages +* `pkg> add` and `Pkg.add` will now add compat entries for new direct dependencies if the active environment is a + package (has a `name` and `uuid` entry). +* Dependencies can now be directly added as weak deps or extras via the `pkg> add --weak/extra Foo` or + `Pkg.add("Foo", target=:weakdeps/:extras)` forms. + #### StyledStrings * A new standard library for handling styling in a more comprehensive and structured way ([#49586]). From 716cf46d490422dcf42c14a21c904ad8db7793ab Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Sat, 5 Oct 2024 05:34:27 +0800 Subject: [PATCH 29/76] typeintersect: allocation tuning for large `UnionAll` (#54745) This PR tries to reduce the allocation caused by `save_env` by skipping `alloc_env` and truncating env size when possible. (cherry picked from commit 99cc59c7b59a3facf4be3ac0d72218f79db0593d) --- src/subtype.c | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index b12a8ec88e2b7..59cba8a895653 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2412,24 +2412,47 @@ static jl_value_t *intersect_aside(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, if (obviously_egal(x, y)) return x; + jl_varbinding_t *vars = NULL; + jl_varbinding_t *bbprev = NULL; + jl_varbinding_t *xb = jl_is_typevar(x) ? lookup(e, (jl_tvar_t *)x) : NULL; + jl_varbinding_t *yb = jl_is_typevar(y) ? lookup(e, (jl_tvar_t *)y) : NULL; + int simple_x = !jl_has_free_typevars(!jl_is_typevar(x) ? x : xb ? xb->ub : ((jl_tvar_t *)x)->ub); + int simple_y = !jl_has_free_typevars(!jl_is_typevar(y) ? y : yb ? yb->ub : ((jl_tvar_t *)y)->ub); + if (simple_x && simple_y && !(xb && yb)) { + vars = e->vars; + e->vars = xb ? xb : yb; + if (e->vars != NULL) { + bbprev = e->vars->prev; + e->vars->prev = NULL; + } + } jl_saved_unionstate_t oldRunions; push_unionstate(&oldRunions, &e->Runions); int savedepth = e->invdepth; e->invdepth = depth; jl_value_t *res = intersect_all(x, y, e); e->invdepth = savedepth; pop_unionstate(&e->Runions, &oldRunions); + if (bbprev) e->vars->prev = bbprev; + if (vars) e->vars = vars; return res; } static jl_value_t *intersect_union(jl_value_t *x, jl_uniontype_t *u, jl_stenv_t *e, int8_t R, int param) { - if (param == 2 || (!jl_has_free_typevars(x) && !jl_has_free_typevars((jl_value_t*)u))) { + int no_free = !jl_has_free_typevars(x) && !jl_has_free_typevars((jl_value_t*)u); + if (param == 2 || no_free) { jl_value_t *a=NULL, *b=NULL; JL_GC_PUSH2(&a, &b); + jl_varbinding_t *vars = NULL; + if (no_free) { + vars = e->vars; + e->vars = NULL; + } jl_saved_unionstate_t oldRunions; push_unionstate(&oldRunions, &e->Runions); a = R ? intersect_all(x, u->a, e) : intersect_all(u->a, x, e); b = R ? intersect_all(x, u->b, e) : intersect_all(u->b, x, e); pop_unionstate(&e->Runions, &oldRunions); + if (vars) e->vars = vars; jl_value_t *i = simple_join(a,b); JL_GC_POP(); return i; @@ -4127,9 +4150,13 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) save_env(e, &se, 1); int niter = 0, total_iter = 0; is[0] = intersect(x, y, e, 0); // root - if (is[0] != jl_bottom_type) + if (is[0] == jl_bottom_type) { + restore_env(e, &se, 1); + } + else if (!e->emptiness_only && has_next_union_state(e, 1)) { niter = merge_env(e, &me, &se, niter); - restore_env(e, &se, 1); + restore_env(e, &se, 1); + } while (next_union_state(e, 1)) { if (e->emptiness_only && is[0] != jl_bottom_type) break; @@ -4137,9 +4164,16 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) e->Runions.more = 0; is[1] = intersect(x, y, e, 0); - if (is[1] != jl_bottom_type) + if (is[1] == jl_bottom_type) { + restore_env(e, &se, 1); + } + else if (niter > 0 || (!e->emptiness_only && has_next_union_state(e, 1))) { niter = merge_env(e, &me, &se, niter); - restore_env(e, &se, 1); + restore_env(e, &se, 1); + } + else { + assert(is[0] == jl_bottom_type); + } if (is[0] == jl_bottom_type) is[0] = is[1]; else if (is[1] != jl_bottom_type) { From addff452bef6ae5d7b12ab19188a74041b58b10c Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 24 Oct 2024 07:32:33 +0800 Subject: [PATCH 30/76] typeintersect: more fastpath to skip intersect under circular env (#56304) fix #56040 (cherry picked from commit 53ffe5630cff5d974722ec197c6f53b907ffd457) --- src/subtype.c | 9 +++++++-- test/subtype.jl | 9 +++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 59cba8a895653..70c570ce5b8d7 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2408,8 +2408,10 @@ static jl_value_t *intersect_aside(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, return y; if (y == (jl_value_t*)jl_any_type && !jl_is_typevar(x)) return x; - // band-aid for #46736 - if (obviously_egal(x, y)) + // band-aid for #46736 #56040 + if (obviously_in_union(x, y)) + return y; + if (obviously_in_union(y, x)) return x; jl_varbinding_t *vars = NULL; @@ -2439,6 +2441,9 @@ static jl_value_t *intersect_aside(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, static jl_value_t *intersect_union(jl_value_t *x, jl_uniontype_t *u, jl_stenv_t *e, int8_t R, int param) { + // band-aid for #56040 + if (!jl_is_uniontype(x) && obviously_in_union((jl_value_t *)u, x)) + return x; int no_free = !jl_has_free_typevars(x) && !jl_has_free_typevars((jl_value_t*)u); if (param == 2 || no_free) { jl_value_t *a=NULL, *b=NULL; diff --git a/test/subtype.jl b/test/subtype.jl index 7be869107b432..dfa1487eaa55d 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2721,3 +2721,12 @@ let T1 = NTuple{12, Union{Val{1}, Val{2}, Val{3}, Val{4}, Val{5}, Val{6}}} @test !(T1 <: T2) @test Tuple{Union{Val{1},Val{2}}} <: Tuple{S} where {T, S<:Val{T}} end + +#issue 56040 +let S = Dict{V,V} where {V}, + T = Dict{Ref{Union{Set{A2}, Set{A3}, A3}}, Ref{Union{Set{A3}, Set{A2}, Set{A1}, Set{A4}, A4}}} where {A1, A2<:Set{A1}, A3<:Union{Set{A1}, Set{A2}}, A4<:Union{Set{A2}, Set{A1}, Set{A3}}}, + A = Dict{Ref{Set{Union{}}}, Ref{Set{Union{}}}} + @testintersect(S, T, !Union{}) + @test A <: typeintersect(S, T) + @test A <: typeintersect(T, S) +end From 69ed5fdcdc59f345692fd6b0419a456aa4c0c907 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 15 Oct 2024 17:41:31 -0300 Subject: [PATCH 31/76] Remove llvm-muladd pass and move it's functionality to to llvm-simdloop (#55802) Provides no-op fallbacks for compatibility. Co-authored-by: Tim Besard --- doc/src/devdocs/llvm-passes.md | 12 --- doc/src/devdocs/llvm.md | 1 - src/Makefile | 2 +- src/llvm-muladd.cpp | 117 ------------------------------ src/llvm-simdloop.cpp | 66 +++++++++++++++++ src/passes.h | 11 ++- src/pipeline.cpp | 1 - test/llvmpasses/julia-simdloop.ll | 21 ++++++ test/llvmpasses/muladd.ll | 64 ---------------- test/llvmpasses/parsing.ll | 2 +- 10 files changed, 96 insertions(+), 201 deletions(-) delete mode 100644 src/llvm-muladd.cpp delete mode 100644 test/llvmpasses/muladd.ll diff --git a/doc/src/devdocs/llvm-passes.md b/doc/src/devdocs/llvm-passes.md index 36383acaef512..736faf54c219b 100644 --- a/doc/src/devdocs/llvm-passes.md +++ b/doc/src/devdocs/llvm-passes.md @@ -114,18 +114,6 @@ This pass is used to verify Julia's invariants about LLVM IR. This includes thin These passes are used to perform transformations on LLVM IR that LLVM will not perform itself, e.g. fast math flag propagation, escape analysis, and optimizations on Julia-specific internal functions. They use knowledge about Julia's semantics to perform these optimizations. -### CombineMulAdd - -* Filename: `llvm-muladd.cpp` -* Class Name: `CombineMulAddPass` -* Opt Name: `function(CombineMulAdd)` - -This pass serves to optimize the particular combination of a regular `fmul` with a fast `fadd` into a contract `fmul` with a fast `fadd`. This is later optimized by the backend to a [fused multiply-add](https://en.wikipedia.org/wiki/Multiply%E2%80%93accumulate_operation#Fused_multiply%E2%80%93add) instruction, which can provide significantly faster operations at the cost of more [unpredictable semantics](https://simonbyrne.github.io/notes/fastmath/). - -!!! note - - This optimization only occurs when the `fmul` has a single use, which is the fast `fadd`. - ### AllocOpt * Filename: `llvm-alloc-opt.cpp` diff --git a/doc/src/devdocs/llvm.md b/doc/src/devdocs/llvm.md index 170a812c09994..8884e7c91f2bf 100644 --- a/doc/src/devdocs/llvm.md +++ b/doc/src/devdocs/llvm.md @@ -30,7 +30,6 @@ The code for lowering Julia AST to LLVM IR or interpreting it directly is in dir | `llvm-julia-licm.cpp` | Custom LLVM pass to hoist/sink Julia-specific intrinsics | | `llvm-late-gc-lowering.cpp` | Custom LLVM pass to root GC-tracked values | | `llvm-lower-handlers.cpp` | Custom LLVM pass to lower try-catch blocks | -| `llvm-muladd.cpp` | Custom LLVM pass for fast-match FMA | | `llvm-multiversioning.cpp` | Custom LLVM pass to generate sysimg code on multiple architectures | | `llvm-propagate-addrspaces.cpp` | Custom LLVM pass to canonicalize addrspaces | | `llvm-ptls.cpp` | Custom LLVM pass to lower TLS operations | diff --git a/src/Makefile b/src/Makefile index bf9001e5fba93..46e4fe2fb5532 100644 --- a/src/Makefile +++ b/src/Makefile @@ -52,7 +52,7 @@ RT_LLVMLINK := CG_LLVMLINK := ifeq ($(JULIACODEGEN),LLVM) -CODEGEN_SRCS := codegen jitlayers aotcompile debuginfo disasm llvm-simdloop llvm-muladd \ +CODEGEN_SRCS := codegen jitlayers aotcompile debuginfo disasm llvm-simdloop \ llvm-final-gc-lowering llvm-pass-helpers llvm-late-gc-lowering llvm-ptls \ llvm-lower-handlers llvm-gc-invariant-verifier llvm-propagate-addrspaces \ llvm-multiversioning llvm-alloc-opt llvm-alloc-helpers cgmemmgr llvm-remove-addrspaces \ diff --git a/src/llvm-muladd.cpp b/src/llvm-muladd.cpp deleted file mode 100644 index 12f1c8ad765d9..0000000000000 --- a/src/llvm-muladd.cpp +++ /dev/null @@ -1,117 +0,0 @@ -// This file is a part of Julia. License is MIT: https://julialang.org/license - -#include "llvm-version.h" -#include "passes.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "julia.h" -#include "julia_assert.h" - -#define DEBUG_TYPE "combine-muladd" -#undef DEBUG - -using namespace llvm; -STATISTIC(TotalContracted, "Total number of multiplies marked for FMA"); - -#ifndef __clang_gcanalyzer__ -#define REMARK(remark) ORE.emit(remark) -#else -#define REMARK(remark) (void) 0; -#endif - -/** - * Combine - * ``` - * %v0 = fmul ... %a, %b - * %v = fadd contract ... %v0, %c - * ``` - * to - * `%v = call contract @llvm.fmuladd.<...>(... %a, ... %b, ... %c)` - * when `%v0` has no other use - */ - -// Return true if we changed the mulOp -static bool checkCombine(Value *maybeMul, OptimizationRemarkEmitter &ORE) JL_NOTSAFEPOINT -{ - auto mulOp = dyn_cast(maybeMul); - if (!mulOp || mulOp->getOpcode() != Instruction::FMul) - return false; - if (!mulOp->hasOneUse()) { - LLVM_DEBUG(dbgs() << "mulOp has multiple uses: " << *maybeMul << "\n"); - REMARK([&](){ - return OptimizationRemarkMissed(DEBUG_TYPE, "Multiuse FMul", mulOp) - << "fmul had multiple uses " << ore::NV("fmul", mulOp); - }); - return false; - } - // On 5.0+ we only need to mark the mulOp as contract and the backend will do the work for us. - auto fmf = mulOp->getFastMathFlags(); - if (!fmf.allowContract()) { - LLVM_DEBUG(dbgs() << "Marking mulOp for FMA: " << *maybeMul << "\n"); - REMARK([&](){ - return OptimizationRemark(DEBUG_TYPE, "Marked for FMA", mulOp) - << "marked for fma " << ore::NV("fmul", mulOp); - }); - ++TotalContracted; - fmf.setAllowContract(true); - mulOp->copyFastMathFlags(fmf); - return true; - } - return false; -} - -static bool combineMulAdd(Function &F) JL_NOTSAFEPOINT -{ - OptimizationRemarkEmitter ORE(&F); - bool modified = false; - for (auto &BB: F) { - for (auto it = BB.begin(); it != BB.end();) { - auto &I = *it; - it++; - switch (I.getOpcode()) { - case Instruction::FAdd: { - if (!I.hasAllowContract()) - continue; - modified |= checkCombine(I.getOperand(0), ORE) || checkCombine(I.getOperand(1), ORE); - break; - } - case Instruction::FSub: { - if (!I.hasAllowContract()) - continue; - modified |= checkCombine(I.getOperand(0), ORE) || checkCombine(I.getOperand(1), ORE); - break; - } - default: - break; - } - } - } -#ifdef JL_VERIFY_PASSES - assert(!verifyLLVMIR(F)); -#endif - return modified; -} - -PreservedAnalyses CombineMulAddPass::run(Function &F, FunctionAnalysisManager &AM) JL_NOTSAFEPOINT -{ - if (combineMulAdd(F)) { - return PreservedAnalyses::allInSet(); - } - return PreservedAnalyses::all(); -} diff --git a/src/llvm-simdloop.cpp b/src/llvm-simdloop.cpp index f29802b438e1e..66571f1383a22 100644 --- a/src/llvm-simdloop.cpp +++ b/src/llvm-simdloop.cpp @@ -41,6 +41,7 @@ STATISTIC(ReductionChainLength, "Total sum of instructions folded from reduction STATISTIC(MaxChainLength, "Max length of reduction chain"); STATISTIC(AddChains, "Addition reduction chains"); STATISTIC(MulChains, "Multiply reduction chains"); +STATISTIC(TotalContracted, "Total number of multiplies marked for FMA"); #ifndef __clang_gcanalyzer__ #define REMARK(remark) ORE.emit(remark) @@ -49,6 +50,49 @@ STATISTIC(MulChains, "Multiply reduction chains"); #endif namespace { +/** + * Combine + * ``` + * %v0 = fmul ... %a, %b + * %v = fadd contract ... %v0, %c + * ``` + * to + * %v0 = fmul contract ... %a, %b + * %v = fadd contract ... %v0, %c + * when `%v0` has no other use + */ + +static bool checkCombine(Value *maybeMul, Loop &L, OptimizationRemarkEmitter &ORE) JL_NOTSAFEPOINT +{ + auto mulOp = dyn_cast(maybeMul); + if (!mulOp || mulOp->getOpcode() != Instruction::FMul) + return false; + if (!L.contains(mulOp)) + return false; + if (!mulOp->hasOneUse()) { + LLVM_DEBUG(dbgs() << "mulOp has multiple uses: " << *maybeMul << "\n"); + REMARK([&](){ + return OptimizationRemarkMissed(DEBUG_TYPE, "Multiuse FMul", mulOp) + << "fmul had multiple uses " << ore::NV("fmul", mulOp); + }); + return false; + } + // On 5.0+ we only need to mark the mulOp as contract and the backend will do the work for us. + auto fmf = mulOp->getFastMathFlags(); + if (!fmf.allowContract()) { + LLVM_DEBUG(dbgs() << "Marking mulOp for FMA: " << *maybeMul << "\n"); + REMARK([&](){ + return OptimizationRemark(DEBUG_TYPE, "Marked for FMA", mulOp) + << "marked for fma " << ore::NV("fmul", mulOp); + }); + ++TotalContracted; + fmf.setAllowContract(true); + mulOp->copyFastMathFlags(fmf); + return true; + } + return false; +} + static unsigned getReduceOpcode(Instruction *J, Instruction *operand) JL_NOTSAFEPOINT { switch (J->getOpcode()) { @@ -150,6 +194,28 @@ static void enableUnsafeAlgebraIfReduction(PHINode *Phi, Loop &L, OptimizationRe }); (*K)->setHasAllowReassoc(true); (*K)->setHasAllowContract(true); + switch ((*K)->getOpcode()) { + case Instruction::FAdd: { + if (!(*K)->hasAllowContract()) + continue; + // (*K)->getOperand(0)->print(dbgs()); + // (*K)->getOperand(1)->print(dbgs()); + checkCombine((*K)->getOperand(0), L, ORE); + checkCombine((*K)->getOperand(1), L, ORE); + break; + } + case Instruction::FSub: { + if (!(*K)->hasAllowContract()) + continue; + // (*K)->getOperand(0)->print(dbgs()); + // (*K)->getOperand(1)->print(dbgs()); + checkCombine((*K)->getOperand(0), L, ORE); + checkCombine((*K)->getOperand(1), L, ORE); + break; + } + default: + break; + } if (SE) SE->forgetValue(*K); ++length; diff --git a/src/passes.h b/src/passes.h index 6557a5813063d..1bb04816af641 100644 --- a/src/passes.h +++ b/src/passes.h @@ -15,15 +15,18 @@ struct DemoteFloat16Pass : PassInfoMixin { static bool isRequired() { return true; } }; -struct CombineMulAddPass : PassInfoMixin { - PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) JL_NOTSAFEPOINT; -}; - struct LateLowerGCPass : PassInfoMixin { PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) JL_NOTSAFEPOINT; static bool isRequired() { return true; } }; +struct CombineMulAddPass : PassInfoMixin { + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) JL_NOTSAFEPOINT { + // no-op + return PreservedAnalyses::all(); + } +}; + struct AllocOptPass : PassInfoMixin { PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) JL_NOTSAFEPOINT; }; diff --git a/src/pipeline.cpp b/src/pipeline.cpp index 5c12e3dad0dd7..54b36daad9a0e 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -577,7 +577,6 @@ static void buildCleanupPipeline(ModulePassManager &MPM, PassBuilder *PB, Optimi if (options.cleanup) { if (O.getSpeedupLevel() >= 2) { FunctionPassManager FPM; - JULIA_PASS(FPM.addPass(CombineMulAddPass())); FPM.addPass(DivRemPairsPass()); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); } diff --git a/test/llvmpasses/julia-simdloop.ll b/test/llvmpasses/julia-simdloop.ll index df96e34979a3d..a6e0ac03439fa 100644 --- a/test/llvmpasses/julia-simdloop.ll +++ b/test/llvmpasses/julia-simdloop.ll @@ -61,6 +61,26 @@ loopdone: ret double %nextv } +; CHECK-LABEL: @simd_test_sub4( +define double @simd_test_sub4(double *%a) { +top: + br label %loop +loop: + %i = phi i64 [0, %top], [%nexti, %loop] + %v = phi double [0.000000e+00, %top], [%nextv, %loop] + %aptr = getelementptr double, double *%a, i64 %i + %aval = load double, double *%aptr + %nextv2 = fmul double %aval, %aval + ; CHECK: fmul contract double %aval, %aval + %nextv = fsub double %v, %nextv2 +; CHECK: fsub reassoc contract double %v, %nextv2 + %nexti = add i64 %i, 1 + %done = icmp sgt i64 %nexti, 500 + br i1 %done, label %loopdone, label %loop, !llvm.loop !0 +loopdone: + ret double %nextv +} + ; Tests if we correctly pass through other metadata ; CHECK-LABEL: @disabled( define i32 @disabled(i32* noalias nocapture %a, i32* noalias nocapture readonly %b, i32 %N) { @@ -84,6 +104,7 @@ for.end: ; preds = %for.body ret i32 %1 } + !0 = distinct !{!0, !"julia.simdloop"} !1 = distinct !{!1, !"julia.simdloop", !"julia.ivdep"} !2 = distinct !{!2, !"julia.simdloop", !"julia.ivdep", !3} diff --git a/test/llvmpasses/muladd.ll b/test/llvmpasses/muladd.ll deleted file mode 100644 index 3c1c995ce7376..0000000000000 --- a/test/llvmpasses/muladd.ll +++ /dev/null @@ -1,64 +0,0 @@ -; This file is a part of Julia. License is MIT: https://julialang.org/license - -; RUN: opt -enable-new-pm=1 --opaque-pointers=0 --load-pass-plugin=libjulia-codegen%shlibext -passes='CombineMulAdd' -S %s | FileCheck %s - -; RUN: opt -enable-new-pm=1 --opaque-pointers=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='CombineMulAdd' -S %s | FileCheck %s - - -; CHECK-LABEL: @fast_muladd1 -define double @fast_muladd1(double %a, double %b, double %c) { -top: -; CHECK: {{contract|fmuladd}} - %v1 = fmul double %a, %b - %v2 = fadd fast double %v1, %c -; CHECK: ret double - ret double %v2 -} - -; CHECK-LABEL: @fast_mulsub1 -define double @fast_mulsub1(double %a, double %b, double %c) { -top: -; CHECK: {{contract|fmuladd}} - %v1 = fmul double %a, %b - %v2 = fsub fast double %v1, %c -; CHECK: ret double - ret double %v2 -} - -; CHECK-LABEL: @fast_mulsub_vec1 -define <2 x double> @fast_mulsub_vec1(<2 x double> %a, <2 x double> %b, <2 x double> %c) { -top: -; CHECK: {{contract|fmuladd}} - %v1 = fmul <2 x double> %a, %b - %v2 = fsub fast <2 x double> %c, %v1 -; CHECK: ret <2 x double> - ret <2 x double> %v2 -} - -; COM: Should not mark fmul as contract when multiple uses of fmul exist -; CHECK-LABEL: @slow_muladd1 -define double @slow_muladd1(double %a, double %b, double %c) { -top: -; CHECK: %v1 = fmul double %a, %b - %v1 = fmul double %a, %b -; CHECK: %v2 = fadd fast double %v1, %c - %v2 = fadd fast double %v1, %c -; CHECK: %v3 = fadd fast double %v1, %b - %v3 = fadd fast double %v1, %b -; CHECK: %v4 = fadd fast double %v3, %v2 - %v4 = fadd fast double %v3, %v2 -; CHECK: ret double %v4 - ret double %v4 -} - -; COM: Should not mark fadd->fadd fast as contract -; CHECK-LABEL: @slow_addadd1 -define double @slow_addadd1(double %a, double %b, double %c) { -top: -; CHECK: %v1 = fadd double %a, %b - %v1 = fadd double %a, %b -; CHECK: %v2 = fadd fast double %v1, %c - %v2 = fadd fast double %v1, %c -; CHECK: ret double %v2 - ret double %v2 -} diff --git a/test/llvmpasses/parsing.ll b/test/llvmpasses/parsing.ll index e0a726176b225..b8aec5ee2fa71 100644 --- a/test/llvmpasses/parsing.ll +++ b/test/llvmpasses/parsing.ll @@ -1,6 +1,6 @@ ; COM: NewPM-only test, tests for ability to parse Julia passes -; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='module(CPUFeatures,RemoveNI,JuliaMultiVersioning,RemoveJuliaAddrspaces,LowerPTLSPass,function(DemoteFloat16,CombineMulAdd,LateLowerGCFrame,FinalLowerGC,AllocOpt,PropagateJuliaAddrspaces,LowerExcHandlers,GCInvariantVerifier,loop(LowerSIMDLoop,JuliaLICM),GCInvariantVerifier,GCInvariantVerifier),LowerPTLSPass,LowerPTLSPass,JuliaMultiVersioning,JuliaMultiVersioning)' -S %s -o /dev/null +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='module(CPUFeatures,RemoveNI,JuliaMultiVersioning,RemoveJuliaAddrspaces,LowerPTLSPass,function(DemoteFloat16,LateLowerGCFrame,FinalLowerGC,AllocOpt,PropagateJuliaAddrspaces,LowerExcHandlers,GCInvariantVerifier,loop(LowerSIMDLoop,JuliaLICM),GCInvariantVerifier,GCInvariantVerifier),LowerPTLSPass,LowerPTLSPass,JuliaMultiVersioning,JuliaMultiVersioning)' -S %s -o /dev/null ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes="julia" -S %s -o /dev/null ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes="julia" -S %s -o /dev/null ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes="julia" -S %s -o /dev/null From 71838b93bd5fd358a95e3a965fc95c52a931072d Mon Sep 17 00:00:00 2001 From: KristofferC Date: Thu, 24 Oct 2024 11:52:53 +0200 Subject: [PATCH 32/76] Revert "Remove llvm-muladd pass and move it's functionality to to llvm-simdloop (#55802)" This reverts commit 69ed5fdcdc59f345692fd6b0419a456aa4c0c907. --- doc/src/devdocs/llvm-passes.md | 12 +++ doc/src/devdocs/llvm.md | 1 + src/Makefile | 2 +- src/llvm-muladd.cpp | 117 ++++++++++++++++++++++++++++++ src/llvm-simdloop.cpp | 66 ----------------- src/passes.h | 11 +-- src/pipeline.cpp | 1 + test/llvmpasses/julia-simdloop.ll | 21 ------ test/llvmpasses/muladd.ll | 64 ++++++++++++++++ test/llvmpasses/parsing.ll | 2 +- 10 files changed, 201 insertions(+), 96 deletions(-) create mode 100644 src/llvm-muladd.cpp create mode 100644 test/llvmpasses/muladd.ll diff --git a/doc/src/devdocs/llvm-passes.md b/doc/src/devdocs/llvm-passes.md index 736faf54c219b..36383acaef512 100644 --- a/doc/src/devdocs/llvm-passes.md +++ b/doc/src/devdocs/llvm-passes.md @@ -114,6 +114,18 @@ This pass is used to verify Julia's invariants about LLVM IR. This includes thin These passes are used to perform transformations on LLVM IR that LLVM will not perform itself, e.g. fast math flag propagation, escape analysis, and optimizations on Julia-specific internal functions. They use knowledge about Julia's semantics to perform these optimizations. +### CombineMulAdd + +* Filename: `llvm-muladd.cpp` +* Class Name: `CombineMulAddPass` +* Opt Name: `function(CombineMulAdd)` + +This pass serves to optimize the particular combination of a regular `fmul` with a fast `fadd` into a contract `fmul` with a fast `fadd`. This is later optimized by the backend to a [fused multiply-add](https://en.wikipedia.org/wiki/Multiply%E2%80%93accumulate_operation#Fused_multiply%E2%80%93add) instruction, which can provide significantly faster operations at the cost of more [unpredictable semantics](https://simonbyrne.github.io/notes/fastmath/). + +!!! note + + This optimization only occurs when the `fmul` has a single use, which is the fast `fadd`. + ### AllocOpt * Filename: `llvm-alloc-opt.cpp` diff --git a/doc/src/devdocs/llvm.md b/doc/src/devdocs/llvm.md index 8884e7c91f2bf..170a812c09994 100644 --- a/doc/src/devdocs/llvm.md +++ b/doc/src/devdocs/llvm.md @@ -30,6 +30,7 @@ The code for lowering Julia AST to LLVM IR or interpreting it directly is in dir | `llvm-julia-licm.cpp` | Custom LLVM pass to hoist/sink Julia-specific intrinsics | | `llvm-late-gc-lowering.cpp` | Custom LLVM pass to root GC-tracked values | | `llvm-lower-handlers.cpp` | Custom LLVM pass to lower try-catch blocks | +| `llvm-muladd.cpp` | Custom LLVM pass for fast-match FMA | | `llvm-multiversioning.cpp` | Custom LLVM pass to generate sysimg code on multiple architectures | | `llvm-propagate-addrspaces.cpp` | Custom LLVM pass to canonicalize addrspaces | | `llvm-ptls.cpp` | Custom LLVM pass to lower TLS operations | diff --git a/src/Makefile b/src/Makefile index 46e4fe2fb5532..bf9001e5fba93 100644 --- a/src/Makefile +++ b/src/Makefile @@ -52,7 +52,7 @@ RT_LLVMLINK := CG_LLVMLINK := ifeq ($(JULIACODEGEN),LLVM) -CODEGEN_SRCS := codegen jitlayers aotcompile debuginfo disasm llvm-simdloop \ +CODEGEN_SRCS := codegen jitlayers aotcompile debuginfo disasm llvm-simdloop llvm-muladd \ llvm-final-gc-lowering llvm-pass-helpers llvm-late-gc-lowering llvm-ptls \ llvm-lower-handlers llvm-gc-invariant-verifier llvm-propagate-addrspaces \ llvm-multiversioning llvm-alloc-opt llvm-alloc-helpers cgmemmgr llvm-remove-addrspaces \ diff --git a/src/llvm-muladd.cpp b/src/llvm-muladd.cpp new file mode 100644 index 0000000000000..12f1c8ad765d9 --- /dev/null +++ b/src/llvm-muladd.cpp @@ -0,0 +1,117 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#include "llvm-version.h" +#include "passes.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "julia.h" +#include "julia_assert.h" + +#define DEBUG_TYPE "combine-muladd" +#undef DEBUG + +using namespace llvm; +STATISTIC(TotalContracted, "Total number of multiplies marked for FMA"); + +#ifndef __clang_gcanalyzer__ +#define REMARK(remark) ORE.emit(remark) +#else +#define REMARK(remark) (void) 0; +#endif + +/** + * Combine + * ``` + * %v0 = fmul ... %a, %b + * %v = fadd contract ... %v0, %c + * ``` + * to + * `%v = call contract @llvm.fmuladd.<...>(... %a, ... %b, ... %c)` + * when `%v0` has no other use + */ + +// Return true if we changed the mulOp +static bool checkCombine(Value *maybeMul, OptimizationRemarkEmitter &ORE) JL_NOTSAFEPOINT +{ + auto mulOp = dyn_cast(maybeMul); + if (!mulOp || mulOp->getOpcode() != Instruction::FMul) + return false; + if (!mulOp->hasOneUse()) { + LLVM_DEBUG(dbgs() << "mulOp has multiple uses: " << *maybeMul << "\n"); + REMARK([&](){ + return OptimizationRemarkMissed(DEBUG_TYPE, "Multiuse FMul", mulOp) + << "fmul had multiple uses " << ore::NV("fmul", mulOp); + }); + return false; + } + // On 5.0+ we only need to mark the mulOp as contract and the backend will do the work for us. + auto fmf = mulOp->getFastMathFlags(); + if (!fmf.allowContract()) { + LLVM_DEBUG(dbgs() << "Marking mulOp for FMA: " << *maybeMul << "\n"); + REMARK([&](){ + return OptimizationRemark(DEBUG_TYPE, "Marked for FMA", mulOp) + << "marked for fma " << ore::NV("fmul", mulOp); + }); + ++TotalContracted; + fmf.setAllowContract(true); + mulOp->copyFastMathFlags(fmf); + return true; + } + return false; +} + +static bool combineMulAdd(Function &F) JL_NOTSAFEPOINT +{ + OptimizationRemarkEmitter ORE(&F); + bool modified = false; + for (auto &BB: F) { + for (auto it = BB.begin(); it != BB.end();) { + auto &I = *it; + it++; + switch (I.getOpcode()) { + case Instruction::FAdd: { + if (!I.hasAllowContract()) + continue; + modified |= checkCombine(I.getOperand(0), ORE) || checkCombine(I.getOperand(1), ORE); + break; + } + case Instruction::FSub: { + if (!I.hasAllowContract()) + continue; + modified |= checkCombine(I.getOperand(0), ORE) || checkCombine(I.getOperand(1), ORE); + break; + } + default: + break; + } + } + } +#ifdef JL_VERIFY_PASSES + assert(!verifyLLVMIR(F)); +#endif + return modified; +} + +PreservedAnalyses CombineMulAddPass::run(Function &F, FunctionAnalysisManager &AM) JL_NOTSAFEPOINT +{ + if (combineMulAdd(F)) { + return PreservedAnalyses::allInSet(); + } + return PreservedAnalyses::all(); +} diff --git a/src/llvm-simdloop.cpp b/src/llvm-simdloop.cpp index 66571f1383a22..f29802b438e1e 100644 --- a/src/llvm-simdloop.cpp +++ b/src/llvm-simdloop.cpp @@ -41,7 +41,6 @@ STATISTIC(ReductionChainLength, "Total sum of instructions folded from reduction STATISTIC(MaxChainLength, "Max length of reduction chain"); STATISTIC(AddChains, "Addition reduction chains"); STATISTIC(MulChains, "Multiply reduction chains"); -STATISTIC(TotalContracted, "Total number of multiplies marked for FMA"); #ifndef __clang_gcanalyzer__ #define REMARK(remark) ORE.emit(remark) @@ -50,49 +49,6 @@ STATISTIC(TotalContracted, "Total number of multiplies marked for FMA"); #endif namespace { -/** - * Combine - * ``` - * %v0 = fmul ... %a, %b - * %v = fadd contract ... %v0, %c - * ``` - * to - * %v0 = fmul contract ... %a, %b - * %v = fadd contract ... %v0, %c - * when `%v0` has no other use - */ - -static bool checkCombine(Value *maybeMul, Loop &L, OptimizationRemarkEmitter &ORE) JL_NOTSAFEPOINT -{ - auto mulOp = dyn_cast(maybeMul); - if (!mulOp || mulOp->getOpcode() != Instruction::FMul) - return false; - if (!L.contains(mulOp)) - return false; - if (!mulOp->hasOneUse()) { - LLVM_DEBUG(dbgs() << "mulOp has multiple uses: " << *maybeMul << "\n"); - REMARK([&](){ - return OptimizationRemarkMissed(DEBUG_TYPE, "Multiuse FMul", mulOp) - << "fmul had multiple uses " << ore::NV("fmul", mulOp); - }); - return false; - } - // On 5.0+ we only need to mark the mulOp as contract and the backend will do the work for us. - auto fmf = mulOp->getFastMathFlags(); - if (!fmf.allowContract()) { - LLVM_DEBUG(dbgs() << "Marking mulOp for FMA: " << *maybeMul << "\n"); - REMARK([&](){ - return OptimizationRemark(DEBUG_TYPE, "Marked for FMA", mulOp) - << "marked for fma " << ore::NV("fmul", mulOp); - }); - ++TotalContracted; - fmf.setAllowContract(true); - mulOp->copyFastMathFlags(fmf); - return true; - } - return false; -} - static unsigned getReduceOpcode(Instruction *J, Instruction *operand) JL_NOTSAFEPOINT { switch (J->getOpcode()) { @@ -194,28 +150,6 @@ static void enableUnsafeAlgebraIfReduction(PHINode *Phi, Loop &L, OptimizationRe }); (*K)->setHasAllowReassoc(true); (*K)->setHasAllowContract(true); - switch ((*K)->getOpcode()) { - case Instruction::FAdd: { - if (!(*K)->hasAllowContract()) - continue; - // (*K)->getOperand(0)->print(dbgs()); - // (*K)->getOperand(1)->print(dbgs()); - checkCombine((*K)->getOperand(0), L, ORE); - checkCombine((*K)->getOperand(1), L, ORE); - break; - } - case Instruction::FSub: { - if (!(*K)->hasAllowContract()) - continue; - // (*K)->getOperand(0)->print(dbgs()); - // (*K)->getOperand(1)->print(dbgs()); - checkCombine((*K)->getOperand(0), L, ORE); - checkCombine((*K)->getOperand(1), L, ORE); - break; - } - default: - break; - } if (SE) SE->forgetValue(*K); ++length; diff --git a/src/passes.h b/src/passes.h index 1bb04816af641..6557a5813063d 100644 --- a/src/passes.h +++ b/src/passes.h @@ -15,16 +15,13 @@ struct DemoteFloat16Pass : PassInfoMixin { static bool isRequired() { return true; } }; -struct LateLowerGCPass : PassInfoMixin { +struct CombineMulAddPass : PassInfoMixin { PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) JL_NOTSAFEPOINT; - static bool isRequired() { return true; } }; -struct CombineMulAddPass : PassInfoMixin { - PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) JL_NOTSAFEPOINT { - // no-op - return PreservedAnalyses::all(); - } +struct LateLowerGCPass : PassInfoMixin { + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) JL_NOTSAFEPOINT; + static bool isRequired() { return true; } }; struct AllocOptPass : PassInfoMixin { diff --git a/src/pipeline.cpp b/src/pipeline.cpp index 54b36daad9a0e..5c12e3dad0dd7 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -577,6 +577,7 @@ static void buildCleanupPipeline(ModulePassManager &MPM, PassBuilder *PB, Optimi if (options.cleanup) { if (O.getSpeedupLevel() >= 2) { FunctionPassManager FPM; + JULIA_PASS(FPM.addPass(CombineMulAddPass())); FPM.addPass(DivRemPairsPass()); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); } diff --git a/test/llvmpasses/julia-simdloop.ll b/test/llvmpasses/julia-simdloop.ll index a6e0ac03439fa..df96e34979a3d 100644 --- a/test/llvmpasses/julia-simdloop.ll +++ b/test/llvmpasses/julia-simdloop.ll @@ -61,26 +61,6 @@ loopdone: ret double %nextv } -; CHECK-LABEL: @simd_test_sub4( -define double @simd_test_sub4(double *%a) { -top: - br label %loop -loop: - %i = phi i64 [0, %top], [%nexti, %loop] - %v = phi double [0.000000e+00, %top], [%nextv, %loop] - %aptr = getelementptr double, double *%a, i64 %i - %aval = load double, double *%aptr - %nextv2 = fmul double %aval, %aval - ; CHECK: fmul contract double %aval, %aval - %nextv = fsub double %v, %nextv2 -; CHECK: fsub reassoc contract double %v, %nextv2 - %nexti = add i64 %i, 1 - %done = icmp sgt i64 %nexti, 500 - br i1 %done, label %loopdone, label %loop, !llvm.loop !0 -loopdone: - ret double %nextv -} - ; Tests if we correctly pass through other metadata ; CHECK-LABEL: @disabled( define i32 @disabled(i32* noalias nocapture %a, i32* noalias nocapture readonly %b, i32 %N) { @@ -104,7 +84,6 @@ for.end: ; preds = %for.body ret i32 %1 } - !0 = distinct !{!0, !"julia.simdloop"} !1 = distinct !{!1, !"julia.simdloop", !"julia.ivdep"} !2 = distinct !{!2, !"julia.simdloop", !"julia.ivdep", !3} diff --git a/test/llvmpasses/muladd.ll b/test/llvmpasses/muladd.ll new file mode 100644 index 0000000000000..3c1c995ce7376 --- /dev/null +++ b/test/llvmpasses/muladd.ll @@ -0,0 +1,64 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + +; RUN: opt -enable-new-pm=1 --opaque-pointers=0 --load-pass-plugin=libjulia-codegen%shlibext -passes='CombineMulAdd' -S %s | FileCheck %s + +; RUN: opt -enable-new-pm=1 --opaque-pointers=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='CombineMulAdd' -S %s | FileCheck %s + + +; CHECK-LABEL: @fast_muladd1 +define double @fast_muladd1(double %a, double %b, double %c) { +top: +; CHECK: {{contract|fmuladd}} + %v1 = fmul double %a, %b + %v2 = fadd fast double %v1, %c +; CHECK: ret double + ret double %v2 +} + +; CHECK-LABEL: @fast_mulsub1 +define double @fast_mulsub1(double %a, double %b, double %c) { +top: +; CHECK: {{contract|fmuladd}} + %v1 = fmul double %a, %b + %v2 = fsub fast double %v1, %c +; CHECK: ret double + ret double %v2 +} + +; CHECK-LABEL: @fast_mulsub_vec1 +define <2 x double> @fast_mulsub_vec1(<2 x double> %a, <2 x double> %b, <2 x double> %c) { +top: +; CHECK: {{contract|fmuladd}} + %v1 = fmul <2 x double> %a, %b + %v2 = fsub fast <2 x double> %c, %v1 +; CHECK: ret <2 x double> + ret <2 x double> %v2 +} + +; COM: Should not mark fmul as contract when multiple uses of fmul exist +; CHECK-LABEL: @slow_muladd1 +define double @slow_muladd1(double %a, double %b, double %c) { +top: +; CHECK: %v1 = fmul double %a, %b + %v1 = fmul double %a, %b +; CHECK: %v2 = fadd fast double %v1, %c + %v2 = fadd fast double %v1, %c +; CHECK: %v3 = fadd fast double %v1, %b + %v3 = fadd fast double %v1, %b +; CHECK: %v4 = fadd fast double %v3, %v2 + %v4 = fadd fast double %v3, %v2 +; CHECK: ret double %v4 + ret double %v4 +} + +; COM: Should not mark fadd->fadd fast as contract +; CHECK-LABEL: @slow_addadd1 +define double @slow_addadd1(double %a, double %b, double %c) { +top: +; CHECK: %v1 = fadd double %a, %b + %v1 = fadd double %a, %b +; CHECK: %v2 = fadd fast double %v1, %c + %v2 = fadd fast double %v1, %c +; CHECK: ret double %v2 + ret double %v2 +} diff --git a/test/llvmpasses/parsing.ll b/test/llvmpasses/parsing.ll index b8aec5ee2fa71..e0a726176b225 100644 --- a/test/llvmpasses/parsing.ll +++ b/test/llvmpasses/parsing.ll @@ -1,6 +1,6 @@ ; COM: NewPM-only test, tests for ability to parse Julia passes -; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='module(CPUFeatures,RemoveNI,JuliaMultiVersioning,RemoveJuliaAddrspaces,LowerPTLSPass,function(DemoteFloat16,LateLowerGCFrame,FinalLowerGC,AllocOpt,PropagateJuliaAddrspaces,LowerExcHandlers,GCInvariantVerifier,loop(LowerSIMDLoop,JuliaLICM),GCInvariantVerifier,GCInvariantVerifier),LowerPTLSPass,LowerPTLSPass,JuliaMultiVersioning,JuliaMultiVersioning)' -S %s -o /dev/null +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='module(CPUFeatures,RemoveNI,JuliaMultiVersioning,RemoveJuliaAddrspaces,LowerPTLSPass,function(DemoteFloat16,CombineMulAdd,LateLowerGCFrame,FinalLowerGC,AllocOpt,PropagateJuliaAddrspaces,LowerExcHandlers,GCInvariantVerifier,loop(LowerSIMDLoop,JuliaLICM),GCInvariantVerifier,GCInvariantVerifier),LowerPTLSPass,LowerPTLSPass,JuliaMultiVersioning,JuliaMultiVersioning)' -S %s -o /dev/null ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes="julia" -S %s -o /dev/null ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes="julia" -S %s -o /dev/null ; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes="julia" -S %s -o /dev/null From c00ba05f9a02463a501167c6b0c78f6be68e8381 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 21 Oct 2024 08:21:44 -0400 Subject: [PATCH 33/76] REPL: fix brace detection when ' is used for transpose (#56252) (cherry picked from commit 1c67d0cfdc8ab109120dc3f0720053e509a10131) --- stdlib/REPL/src/REPLCompletions.jl | 9 +++++++-- stdlib/REPL/test/replcompletions.jl | 6 ++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 46d6fc7b62dd3..021253fe99002 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -464,6 +464,7 @@ function find_start_brace(s::AbstractString; c_start='(', c_end=')') i = firstindex(r) braces = in_comment = 0 in_single_quotes = in_double_quotes = in_back_ticks = false + num_single_quotes_in_string = count('\'', s) while i <= ncodeunits(r) c, i = iterate(r, i) if c == '#' && i <= ncodeunits(r) && iterate(r, i)[1] == '=' @@ -486,7 +487,9 @@ function find_start_brace(s::AbstractString; c_start='(', c_end=')') braces += 1 elseif c == c_end braces -= 1 - elseif c == '\'' + elseif c == '\'' && num_single_quotes_in_string % 2 == 0 + # ' can be a transpose too, so check if there are even number of 's in the string + # TODO: This probably needs to be more robust in_single_quotes = true elseif c == '"' in_double_quotes = true @@ -1152,7 +1155,9 @@ function complete_identifiers!(suggestions::Vector{Completion}, @nospecialize(ff if !isinfix # Handle infix call argument completion of the form bar + foo(qux). frange, end_of_identifier = find_start_brace(@view s[1:prevind(s, end)]) - isinfix = Meta.parse(@view(s[frange[1]:end]), raise=false, depwarn=false) == ex.args[end] + if !isempty(frange) # if find_start_brace fails to find the brace just continue + isinfix = Meta.parse(@view(s[frange[1]:end]), raise=false, depwarn=false) == ex.args[end] + end end if isinfix ex = ex.args[end] diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 0a73a944ec8ea..e72b45c897d54 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -327,6 +327,12 @@ end # inexistent completion inside a cmd @test_nocompletion("run(`lol") +# issue 55856: copy(A'). errors in the REPL +let + c, r = test_complete("copy(A').") + @test isempty(c) +end + # test latex symbol completions let s = "\\alpha" c, r = test_bslashcomplete(s) From 7b3408e55e058f69dd2f6164fb5cd47cdefe5885 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 22 Oct 2024 09:49:42 -0400 Subject: [PATCH 34/76] move time_imports and trace_* macros to Base but remain owned by InteractiveUtils (#56276) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This way all packages can be timed including InteractiveUtils and its deps (Base64, JuliaSyntaxHighlighting, Markdown, StyledStrings). With this PR ``` % ./julia --start=no -e "@time Base.@time_imports using REPL" 41.8 ms StyledStrings ┌ 0.1 ms JuliaSyntaxHighlighting.__init__() 14.2 ms JuliaSyntaxHighlighting 1.0 ms Base64 ┌ 0.0 ms Markdown.__init__() 9.6 ms Markdown 2.2 ms InteractiveUtils 0.3 ms Unicode ┌ 0.0 ms REPL.REPLCompletions.__init__() ├ 0.0 ms REPL.__init__() 95.7 ms REPL 0.225907 seconds (290.95 k allocations: 16.761 MiB) ``` Otherwise ``` % ./julia --start=no -e "using InteractiveUtils; @time @time_imports using REPL" 0.5 ms Unicode ┌ 0.0 ms REPL.REPLCompletions.__init__() ├ 0.1 ms REPL.__init__() 107.5 ms REPL 0.127016 seconds (164.18 k allocations: 9.199 MiB) ``` Also the `@trace_compile` and `@trace_dispatch` macros for the same reason. (cherry picked from commit ab22f982427184b0a50ba407e4f1cbedbc862ced) --- base/timing.jl | 13 +++++++++++++ stdlib/InteractiveUtils/src/macros.jl | 15 ++++----------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/base/timing.jl b/base/timing.jl index dcdd052926b67..0e0420195b678 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -598,3 +598,16 @@ macro timed(ex) ) end end + +# Exported, documented, and tested in InteractiveUtils +# here so it's possible to time all imports, including InteractiveUtils and its deps +macro time_imports(ex) + quote + try + Base.Threads.atomic_add!(Base.TIMING_IMPORTS, 1) + $(esc(ex)) + finally + Base.Threads.atomic_sub!(Base.TIMING_IMPORTS, 1) + end + end +end diff --git a/stdlib/InteractiveUtils/src/macros.jl b/stdlib/InteractiveUtils/src/macros.jl index 939098464e148..71f895ba88618 100644 --- a/stdlib/InteractiveUtils/src/macros.jl +++ b/stdlib/InteractiveUtils/src/macros.jl @@ -4,6 +4,10 @@ import Base: typesof, insert!, replace_ref_begin_end!, infer_effects +# defined in Base so it's possible to time all imports, including InteractiveUtils and its deps +# via. `Base.@time_imports` etc. +import Base: @time_imports + separate_kwargs(args...; kwargs...) = (args, values(kwargs)) """ @@ -240,17 +244,6 @@ macro code_lowered(ex0...) end end -macro time_imports(ex) - quote - try - Base.Threads.atomic_add!(Base.TIMING_IMPORTS, 1) - $(esc(ex)) - finally - Base.Threads.atomic_sub!(Base.TIMING_IMPORTS, 1) - end - end -end - """ @functionloc From 70f87db2d605c1d4997e5199836d0e1ec4f38442 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 23 Oct 2024 07:59:06 -0400 Subject: [PATCH 35/76] REPL: fix closing quote on completing files in a ~ path (#56253) (cherry picked from commit 133051f20f5995d4128f9c7973efff62ed25e919) --- stdlib/REPL/src/REPLCompletions.jl | 18 +++++++----------- stdlib/REPL/test/replcompletions.jl | 27 ++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 021253fe99002..ca5a6877c7da6 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -899,17 +899,11 @@ function afterusing(string::String, startpos::Int) return occursin(r"^\b(using|import)\s*((\w+[.])*\w+\s*,\s*)*$", str[fr:end]) end -function close_path_completion(dir, paths, str, pos) - length(paths) == 1 || return false # Only close if there's a single choice... - path = (paths[1]::PathCompletion).path +function close_path_completion(dir, path, str, pos) path = unescape_string(replace(path, "\\\$"=>"\$")) path = joinpath(dir, path) # ...except if it's a directory... - try - isdir(path) - catch e - e isa Base.IOError || rethrow() # `path` cannot be determined to be a file - end && return false + Base.isaccessibledir(path) && return false # ...and except if there's already a " at the cursor. return lastindex(str) <= pos || str[nextind(str, pos)] != '"' end @@ -1308,10 +1302,12 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif if !isnothing(path) paths, dir, success = complete_path(path::String, string_escape=true) - if close_path_completion(dir, paths, path, pos) - p = (paths[1]::PathCompletion).path * "\"" + if length(paths) == 1 + p = (paths[1]::PathCompletion).path hint && was_expanded && (p = contractuser(p)) - paths[1] = PathCompletion(p) + if close_path_completion(dir, p, path, pos) + paths[1] = PathCompletion(p * "\"") + end end if success && !isempty(dir) diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index e72b45c897d54..ef75750676a2d 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -1223,7 +1223,7 @@ let current_dir, forbidden e isa Base.IOError && occursin("ELOOP", e.msg) end c, r = test_complete("\"$(escape_string(path))/selfsym") - @test c == ["selfsymlink"] + @test c == ["selfsymlink\""] end end @@ -1344,6 +1344,31 @@ let (c, r, res) = test_complete("\"~/julia") c, r, res = test_complete("\"foo~bar") @test !res end +if !Sys.iswindows() + # create a dir and file temporarily in the home directory + path = mkpath(joinpath(homedir(), "Zx6Wa0GkC0")) + touch(joinpath(path, "my_file")) + try + let (c, r, res) = test_complete("\"~/Zx6Wa0GkC") + @test res + @test c == String["Zx6Wa0GkC0/"] + end + let (c, r, res) = test_complete("\"~/Zx6Wa0GkC0") + @test res + @test c == String[homedir() * "/Zx6Wa0GkC0"] + end + let (c, r, res) = test_complete("\"~/Zx6Wa0GkC0/my_") + @test res + @test c == String["my_file\""] + end + let (c, r, res) = test_complete("\"~/Zx6Wa0GkC0/my_file") + @test res + @test c == String[homedir() * "/Zx6Wa0GkC0/my_file"] + end + finally + rm(path, recursive=true) + end +end # Test the completion returns nothing when the folder do not exist let (c, r) = test_complete("cd(\"folder_do_not_exist_77/file") From 32d97c472882a85bbfadf8cc4a428b1f4cdb47e6 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 22 Oct 2024 18:14:39 -0400 Subject: [PATCH 36/76] REPL: Don't search for ?( completions when hinting (#56278) (cherry picked from commit 049d92a2ac506316ca2413e103647f72ce847b56) --- stdlib/REPL/src/REPLCompletions.jl | 50 ++++++++++++++++-------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index ca5a6877c7da6..c2c051b1e01a7 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -1172,33 +1172,35 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif partial = string[1:pos] inc_tag = Base.incomplete_tag(Meta.parse(partial, raise=false, depwarn=false)) - # ?(x, y)TAB lists methods you can call with these objects - # ?(x, y TAB lists methods that take these objects as the first two arguments - # MyModule.?(x, y)TAB restricts the search to names in MyModule - rexm = match(r"(\w+\.|)\?\((.*)$", partial) - if rexm !== nothing - # Get the module scope - if isempty(rexm.captures[1]) - callee_module = context_module - else - modname = Symbol(rexm.captures[1][1:end-1]) - if isdefined(context_module, modname) - callee_module = getfield(context_module, modname) - if !isa(callee_module, Module) + if !hint # require a tab press for completion of these + # ?(x, y)TAB lists methods you can call with these objects + # ?(x, y TAB lists methods that take these objects as the first two arguments + # MyModule.?(x, y)TAB restricts the search to names in MyModule + rexm = match(r"(\w+\.|)\?\((.*)$", partial) + if rexm !== nothing + # Get the module scope + if isempty(rexm.captures[1]) + callee_module = context_module + else + modname = Symbol(rexm.captures[1][1:end-1]) + if isdefined(context_module, modname) + callee_module = getfield(context_module, modname) + if !isa(callee_module, Module) + callee_module = context_module + end + else callee_module = context_module end - else - callee_module = context_module end - end - moreargs = !endswith(rexm.captures[2], ')') - callstr = "_(" * rexm.captures[2] - if moreargs - callstr *= ')' - end - ex_org = Meta.parse(callstr, raise=false, depwarn=false) - if isa(ex_org, Expr) - return complete_any_methods(ex_org, callee_module::Module, context_module, moreargs, shift), (0:length(rexm.captures[1])+1) .+ rexm.offset, false + moreargs = !endswith(rexm.captures[2], ')') + callstr = "_(" * rexm.captures[2] + if moreargs + callstr *= ')' + end + ex_org = Meta.parse(callstr, raise=false, depwarn=false) + if isa(ex_org, Expr) + return complete_any_methods(ex_org, callee_module::Module, context_module, moreargs, shift), (0:length(rexm.captures[1])+1) .+ rexm.offset, false + end end end From 6be0afc3f76955aef3227c7dab8edd4c2ac8e065 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 23 Oct 2024 19:14:02 -0400 Subject: [PATCH 37/76] REPL: don't complete str and cmd macros when the input matches the internal name like `r_` to `r"` (#56254) (cherry picked from commit 4236a33bc5a33dd123a8ffaf2ed2b4fe5641bb87) --- stdlib/REPL/src/REPLCompletions.jl | 13 +++++++++++++ stdlib/REPL/test/replcompletions.jl | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index c2c051b1e01a7..20d8bc44b854a 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -140,6 +140,19 @@ function filtered_mod_names(ffunc::Function, mod::Module, name::AbstractString, all || filter!(Base.Fix1(Base.isexported, mod), ssyms) filter!(ffunc, ssyms) macros = filter(x -> startswith(String(x), "@" * name), ssyms) + + # don't complete string and command macros when the input matches the internal name like `r_` to `r"` + if !startswith(name, "@") + filter!(macros) do m + s = String(m) + if endswith(s, "_str") || endswith(s, "_cmd") + occursin(name, first(s, length(s)-4)) + else + true + end + end + end + syms = String[sprint((io,s)->Base.show_sym(io, s; allow_macroname=true), s) for s in ssyms if completes_global(String(s), name)] appendmacro!(syms, macros, "_str", "\"") appendmacro!(syms, macros, "_cmd", "`") diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index ef75750676a2d..91447efb71b9b 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -1529,6 +1529,16 @@ test_dict_completion("test_repl_comp_customdict") @test "testcmd`" in c c, r, res = test_complete("CompletionFoo.tϵsτc") @test "tϵsτcmδ`" in c + + # Issue #56071: don't complete string and command macros when the input matches the internal name like `r_` to `r"` + c, r, res = test_complete("CompletionFoo.teststr_") + @test isempty(c) + c, r, res = test_complete("CompletionFoo.teststr_s") + @test isempty(c) + c, r, res = test_complete("CompletionFoo.testcmd_") + @test isempty(c) + c, r, res = test_complete("CompletionFoo.testcmd_c") + @test isempty(c) end @testset "Keyword-argument completion" begin From 47061cea49523b3a426d7f0f54953e0dcfc7e2ed Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 29 Aug 2024 14:45:01 -0400 Subject: [PATCH 38/76] add pending state back to jl_thread_suspend_and_get_state-machine (#55622) Fixes an issue with #55500, where signals may abruptly abort the process as they observe it is still processing the resume SIGUSR2 message and are not able to wait for that processing to end before setting the new message to exit. (cherry picked from commit da3468c1208b087161af5b69a26a92a91967a367) --- src/signals-unix.c | 65 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/src/signals-unix.c b/src/signals-unix.c index 5732fd1e9c91d..a2b56952f71bd 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -413,6 +413,7 @@ static int signal_caught_cond = -1; int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) { + int err; pthread_mutex_lock(&in_signal_lock); jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; jl_task_t *ct2 = ptls2 ? jl_atomic_load_relaxed(&ptls2->current_task) : NULL; @@ -421,22 +422,51 @@ int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) pthread_mutex_unlock(&in_signal_lock); return 0; } - sig_atomic_t request = 0; - if (!jl_atomic_cmpswap(&ptls2->signal_request, &request, 1)) { + if (jl_atomic_load(&ptls2->signal_request) != 0) { // something is wrong, or there is already a usr2 in flight elsewhere + // try to wait for it to finish or wait for timeout + struct pollfd event = {signal_caught_cond, POLLIN, 0}; + do { + err = poll(&event, 1, timeout * 1000); + } while (err == -1 && errno == EINTR); + if (err == -1 || (event.revents & POLLIN) == 0) { + // not ready after timeout: cancel this request + pthread_mutex_unlock(&in_signal_lock); + return 0; + } + } + // check for any stale signal_caught_cond events + struct pollfd event = {signal_caught_cond, POLLIN, 0}; + do { + err = poll(&event, 1, 0); + } while (err == -1 && errno == EINTR); + if (err == -1) { pthread_mutex_unlock(&in_signal_lock); return 0; } + if ((event.revents & POLLIN) != 0) { + // consume it before continuing + eventfd_t got; + do { + err = read(signal_caught_cond, &got, sizeof(eventfd_t)); + } while (err == -1 && errno == EINTR); + if (err != sizeof(eventfd_t)) abort(); + assert(got == 1); (void) got; + } + sig_atomic_t request = jl_atomic_exchange(&ptls2->signal_request, 1); + assert(request == 0 || request == -1); request = 1; - int err = pthread_kill(ptls2->system_id, SIGUSR2); - // wait for thread to acknowledge or timeout - struct pollfd event = {signal_caught_cond, POLLIN, 0}; + err = pthread_kill(ptls2->system_id, SIGUSR2); if (err == 0) { + // wait for thread to acknowledge or timeout + struct pollfd event = {signal_caught_cond, POLLIN, 0}; do { err = poll(&event, 1, timeout * 1000); } while (err == -1 && errno == EINTR); + if (err != 1 || (event.revents & POLLIN) == 0) + err = -1; } - if ((event.revents & POLLIN) == 0) { + if (err == -1) { // not ready after timeout: try to cancel this request if (jl_atomic_cmpswap(&ptls2->signal_request, &request, 0)) { pthread_mutex_unlock(&in_signal_lock); @@ -452,7 +482,7 @@ int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) // Now the other thread is waiting on exit_signal_cond (verify that here by // checking it is 0, and add an acquire barrier for good measure) request = jl_atomic_load_acquire(&ptls2->signal_request); - assert(request == 0); (void) request; + assert(request == 0 || request == -1); (void) request; jl_atomic_store_release(&ptls2->signal_request, 4); // prepare to resume normally, but later code may change this *ctx = *signal_context; return 1; @@ -511,6 +541,7 @@ static void jl_exit_thread0(int signo, jl_bt_element_t *bt_data, size_t bt_size) } // request: +// -1: processing // 0: nothing [not from here] // 1: get state & wait for request // 2: throw sigint if `!defer_signal && io_wait` or if force throw threshold @@ -526,22 +557,36 @@ void usr2_handler(int sig, siginfo_t *info, void *ctx) if (ptls == NULL) return; int errno_save = errno; - // acknowledge that we saw the signal_request - sig_atomic_t request = jl_atomic_exchange(&ptls->signal_request, 0); + sig_atomic_t request = jl_atomic_load(&ptls->signal_request); + if (request == 0) + return; + if (!jl_atomic_cmpswap(&ptls->signal_request, &request, -1)) + return; if (request == 1) { signal_context = jl_to_bt_context(ctx); + // acknowledge that we saw the signal_request and set signal_context int err; eventfd_t got = 1; err = write(signal_caught_cond, &got, sizeof(eventfd_t)); if (err != sizeof(eventfd_t)) abort(); + sig_atomic_t processing = -1; + jl_atomic_cmpswap(&ptls->signal_request, &processing, 0); + // wait for exit signal do { err = read(exit_signal_cond, &got, sizeof(eventfd_t)); } while (err == -1 && errno == EINTR); if (err != sizeof(eventfd_t)) abort(); assert(got == 1); - request = jl_atomic_exchange(&ptls->signal_request, 0); + request = jl_atomic_exchange(&ptls->signal_request, -1); + signal_context = NULL; assert(request == 2 || request == 3 || request == 4); } + int err; + eventfd_t got = 1; + err = write(signal_caught_cond, &got, sizeof(eventfd_t)); + if (err != sizeof(eventfd_t)) abort(); + sig_atomic_t processing = -1; + jl_atomic_cmpswap(&ptls->signal_request, &processing, 0); if (request == 2) { int force = jl_check_force_sigint(); if (force || (!ptls->defer_signal && ptls->io_wait)) { From 0495045cc11bf9088ace3e218157df6ffc08c2b8 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 7 Sep 2024 18:16:22 -0400 Subject: [PATCH 39/76] [Profile] fix threading issue (#55704) I forgot about the existence of threads, so had hard-coded this to only support one thread. Clearly that is not sufficient though, so use the semaphore here as it is intended to be used. Fixes #55703 --------- Co-authored-by: Ian Butterworth (cherry picked from commit 4f0a333d9d76df76a6383ed2113e66c789d5ecee) --- src/signals-unix.c | 24 ++++++++++-------------- stdlib/Profile/test/runtests.jl | 3 ++- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/signals-unix.c b/src/signals-unix.c index a2b56952f71bd..2aafd335a68b8 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -410,6 +410,7 @@ pthread_mutex_t in_signal_lock; // shared with jl_delete_thread static bt_context_t *signal_context; // protected by in_signal_lock static int exit_signal_cond = -1; static int signal_caught_cond = -1; +static int signals_inflight = 0; int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) { @@ -422,7 +423,7 @@ int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) pthread_mutex_unlock(&in_signal_lock); return 0; } - if (jl_atomic_load(&ptls2->signal_request) != 0) { + while (signals_inflight) { // something is wrong, or there is already a usr2 in flight elsewhere // try to wait for it to finish or wait for timeout struct pollfd event = {signal_caught_cond, POLLIN, 0}; @@ -434,25 +435,16 @@ int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) pthread_mutex_unlock(&in_signal_lock); return 0; } - } - // check for any stale signal_caught_cond events - struct pollfd event = {signal_caught_cond, POLLIN, 0}; - do { - err = poll(&event, 1, 0); - } while (err == -1 && errno == EINTR); - if (err == -1) { - pthread_mutex_unlock(&in_signal_lock); - return 0; - } - if ((event.revents & POLLIN) != 0) { // consume it before continuing eventfd_t got; do { err = read(signal_caught_cond, &got, sizeof(eventfd_t)); } while (err == -1 && errno == EINTR); if (err != sizeof(eventfd_t)) abort(); - assert(got == 1); (void) got; + assert(signals_inflight >= got); + signals_inflight -= got; } + signals_inflight++; sig_atomic_t request = jl_atomic_exchange(&ptls2->signal_request, 1); assert(request == 0 || request == -1); request = 1; @@ -469,6 +461,7 @@ int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) if (err == -1) { // not ready after timeout: try to cancel this request if (jl_atomic_cmpswap(&ptls2->signal_request, &request, 0)) { + signals_inflight--; pthread_mutex_unlock(&in_signal_lock); return 0; } @@ -478,7 +471,9 @@ int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) err = read(signal_caught_cond, &got, sizeof(eventfd_t)); } while (err == -1 && errno == EINTR); if (err != sizeof(eventfd_t)) abort(); - assert(got == 1); (void) got; + assert(signals_inflight >= got); + signals_inflight -= got; + signals_inflight++; // Now the other thread is waiting on exit_signal_cond (verify that here by // checking it is 0, and add an acquire barrier for good measure) request = jl_atomic_load_acquire(&ptls2->signal_request); @@ -505,6 +500,7 @@ static void jl_try_deliver_sigint(void) jl_safepoint_enable_sigint(); jl_wake_libuv(); pthread_mutex_lock(&in_signal_lock); + signals_inflight++; jl_atomic_store_release(&ptls2->signal_request, 2); // This also makes sure `sleep` is aborted. pthread_kill(ptls2->system_id, SIGUSR2); diff --git a/stdlib/Profile/test/runtests.jl b/stdlib/Profile/test/runtests.jl index cbfdde61d7054..958f1fefb6981 100644 --- a/stdlib/Profile/test/runtests.jl +++ b/stdlib/Profile/test/runtests.jl @@ -168,7 +168,8 @@ let cmd = Base.julia_cmd() println("done") print(Profile.len_data()) """ - p = open(`$cmd -e $script`) + # use multiple threads here to ensure that profiling works with threading + p = open(`$cmd -t2 -e $script`) t = Timer(120) do t # should be under 10 seconds, so give it 2 minutes then report failure println("KILLING debuginfo registration test BY PROFILE TEST WATCHDOG\n") From 27f28b1d8b36fbe90a90c671209d3edcb48bcdaa Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Mon, 21 Oct 2024 02:45:47 -0300 Subject: [PATCH 40/76] Make isbitstypes use memmove instead of the runtime function in copyto! (#56237) This might help llvm understand whats going on. Also enzyme really wants this to look like this to trace through it better. --------- Co-authored-by: Jameson Nash (cherry picked from commit fee8090fa86fb0529ae59fe3e3d339c60e2b90a8) --- base/genericmemory.jl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/base/genericmemory.jl b/base/genericmemory.jl index 3e9d230b44459..b37e2414c456d 100644 --- a/base/genericmemory.jl +++ b/base/genericmemory.jl @@ -114,7 +114,17 @@ function unsafe_copyto!(dest::MemoryRef{T}, src::MemoryRef{T}, n) where {T} @_terminates_globally_notaskstate_meta n == 0 && return dest @boundscheck memoryref(dest, n), memoryref(src, n) - ccall(:jl_genericmemory_copyto, Cvoid, (Any, Ptr{Cvoid}, Any, Ptr{Cvoid}, Int), dest.mem, dest.ptr_or_offset, src.mem, src.ptr_or_offset, Int(n)) + if isbitstype(T) + tdest = @_gc_preserve_begin dest + tsrc = @_gc_preserve_begin src + pdest = unsafe_convert(Ptr{Cvoid}, dest) + psrc = unsafe_convert(Ptr{Cvoid}, src) + memmove(pdest, psrc, aligned_sizeof(T) * n) + @_gc_preserve_end tdest + @_gc_preserve_end tsrc + else + ccall(:jl_genericmemory_copyto, Cvoid, (Any, Ptr{Cvoid}, Any, Ptr{Cvoid}, Int), dest.mem, dest.ptr_or_offset, src.mem, src.ptr_or_offset, Int(n)) + end return dest end From aa54c81d715453e16933ab68d9719f131353807f Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 21 Oct 2024 11:28:56 +0530 Subject: [PATCH 41/76] Fix kron indexing for types without a unique zero (#56229) This fixes a bug introduced in https://github.com/JuliaLang/julia/pull/55941. We may also take this opportunity to limit the scope of the `@inbounds` annotations, and also use `axes` to compute the bounds instead of hard-coding them. The real "fix" here is on line 767, where `l in 1:nA` should have been `l in 1:mB`. Using `axes` avoids such errors, and makes the operation safer as well. (cherry picked from commit b01095e0274bf078e162379a9f243197710053ff) --- stdlib/LinearAlgebra/src/diagonal.jl | 50 +++++++++++++-------------- stdlib/LinearAlgebra/test/diagonal.jl | 4 +-- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index eb0aec7726bc2..1f2c7037f8d8a 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -648,16 +648,16 @@ end zerofilled = true end end - @inbounds for i = 1:nA, j = 1:nB + for i in eachindex(valA), j in eachindex(valB) idx = (i-1)*nB+j - C[idx, idx] = valA[i] * valB[j] + @inbounds C[idx, idx] = valA[i] * valB[j] end if !zerofilled - for j in 1:nA, i in 1:mA + for j in axes(A,2), i in axes(A,1) Δrow, Δcol = (i-1)*mB, (j-1)*nB - for k in 1:nB, l in 1:mB + for k in axes(B,2), l in axes(B,1) i == j && k == l && continue - C[Δrow + l, Δcol + k] = A[i,j] * B[l,k] + @inbounds C[Δrow + l, Δcol + k] = A[i,j] * B[l,k] end end end @@ -697,24 +697,24 @@ end end end m = 1 - @inbounds for j = 1:nA - A_jj = A[j,j] - for k = 1:nB - for l = 1:mB - C[m] = A_jj * B[l,k] + for j in axes(A,2) + A_jj = @inbounds A[j,j] + for k in axes(B,2) + for l in axes(B,1) + @inbounds C[m] = A_jj * B[l,k] m += 1 end m += (nA - 1) * mB end if !zerofilled # populate the zero elements - for i in 1:mA + for i in axes(A,1) i == j && continue - A_ij = A[i, j] + A_ij = @inbounds A[i, j] Δrow, Δcol = (i-1)*mB, (j-1)*nB - for k in 1:nB, l in 1:nA - B_lk = B[l, k] - C[Δrow + l, Δcol + k] = A_ij * B_lk + for k in axes(B,2), l in axes(B,1) + B_lk = @inbounds B[l, k] + @inbounds C[Δrow + l, Δcol + k] = A_ij * B_lk end end end @@ -740,23 +740,23 @@ end end end m = 1 - @inbounds for j = 1:nA - for l = 1:mB - Bll = B[l,l] - for i = 1:mA - C[m] = A[i,j] * Bll + for j in axes(A,2) + for l in axes(B,1) + Bll = @inbounds B[l,l] + for i in axes(A,1) + @inbounds C[m] = A[i,j] * Bll m += nB end m += 1 end if !zerofilled - for i in 1:mA - A_ij = A[i, j] + for i in axes(A,1) + A_ij = @inbounds A[i, j] Δrow, Δcol = (i-1)*mB, (j-1)*nB - for k in 1:nB, l in 1:mB + for k in axes(B,2), l in axes(B,1) l == k && continue - B_lk = B[l, k] - C[Δrow + l, Δcol + k] = A_ij * B_lk + B_lk = @inbounds B[l, k] + @inbounds C[Δrow + l, Δcol + k] = A_ij * B_lk end end end diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index cfd8a5277e5f0..fda51cf402a5b 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -343,7 +343,7 @@ Random.seed!(1) D3 = Diagonal(convert(Vector{elty}, rand(n÷2))) DM3= Matrix(D3) @test Matrix(kron(D, D3)) ≈ kron(DM, DM3) - M4 = rand(elty, n÷2, n÷2) + M4 = rand(elty, size(D3,1) + 1, size(D3,2) + 2) # choose a different size from D3 @test kron(D3, M4) ≈ kron(DM3, M4) @test kron(M4, D3) ≈ kron(M4, DM3) X = [ones(1,1) for i in 1:2, j in 1:2] @@ -1324,7 +1324,7 @@ end end @testset "zeros in kron with block matrices" begin - D = Diagonal(1:2) + D = Diagonal(1:4) B = reshape([ones(2,2), ones(3,2), ones(2,3), ones(3,3)], 2, 2) @test kron(D, B) == kron(Array(D), B) @test kron(B, D) == kron(B, Array(D)) From 88ae46cf6cd4582ff3d0e8413d1762b58296c6f5 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 21 Oct 2024 11:57:10 -0400 Subject: [PATCH 42/76] precompile: add error for using require_stdlib during precompile (#56233) This function could accidentally add a dependency on the stdlib in the user's package, which would make it immediately stale. As pointed out to me by topolarity (cherry picked from commit 2188ba4d70a349594484c927bc7a6e71edaa5902) --- base/loading.jl | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index e0d060c56f302..db022a256c518 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1526,7 +1526,6 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any} end end -precompiling_package::Bool = false loading_extension::Bool = false precompiling_extension::Bool = false function run_extension_callbacks(extid::ExtensionId) @@ -2239,11 +2238,6 @@ For more details regarding code loading, see the manual sections on [modules](@r [parallel computing](@ref code-availability). """ function require(into::Module, mod::Symbol) - if into === Base.__toplevel__ && precompiling_package - # this error type needs to match the error type compilecache throws for non-125 errors. - error("`using/import $mod` outside of a Module detected. Importing a package outside of a module \ - is not allowed during package precompilation.") - end if _require_world_age[] != typemax(UInt) Base.invoke_in_world(_require_world_age[], __require, into, mod) else @@ -2252,6 +2246,10 @@ function require(into::Module, mod::Symbol) end function __require(into::Module, mod::Symbol) + if into === Base.__toplevel__ && generating_output(#=incremental=#true) + error("`using/import $mod` outside of a Module detected. Importing a package outside of a module \ + is not allowed during package precompilation.") + end @lock require_lock begin LOADING_CACHE[] = LoadingCache() try @@ -2611,6 +2609,10 @@ end # load a serialized file directly from append_bundled_depot_path for uuidkey without stalechecks function require_stdlib(package_uuidkey::PkgId, ext::Union{Nothing, String}=nothing) + if generating_output(#=incremental=#true) + # Otherwise this would lead to awkward dependency issues by loading a package that isn't in the Project/Manifest + error("This interactive function requires a stdlib to be loaded, and package code should instead use it directly from that stdlib.") + end @lock require_lock begin # the PkgId of the ext, or package if not an ext this_uuidkey = ext isa String ? PkgId(uuid5(package_uuidkey.uuid, ext), ext) : package_uuidkey @@ -2943,7 +2945,6 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: empty!(Base.EXT_DORMITORY) # If we have a custom sysimage with `EXT_DORMITORY` prepopulated Base.track_nested_precomp($precomp_stack) Base.precompiling_extension = $(loading_extension | isext) - Base.precompiling_package = true Base.include_package_for_output($(pkg_str(pkg)), $(repr(abspath(input))), $(repr(depot_path)), $(repr(dl_load_path)), $(repr(load_path)), $deps, $(repr(source_path(nothing)))) """) From 8ec604d5d1aef8a70c9e2c061e432cd6681de431 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 23 Oct 2024 15:26:38 +0530 Subject: [PATCH 43/76] Fix fetching parent in symmetric algebra (#56286) We only need the `parent` of the result if it is a triangular matrix. For other structurally triangular matrices, such as `Diagonal`s, we may use these directly in the `Hermitian` constructor. The operation proceeds as (assuming `H isa Hermitian` with `H.uplo == 'U'`): ```julia function +(H::Hermitian, H::Hermitian) U = uppertriangular(parent(H)) Ures = U + U data = Ures isa UpperTriangular ? parent(Ures) : Ures # this PR Hermitian(data, :U) end ``` This accounts for the fact that `Ures` may not be an `UpperTriangular`, as `uppertriangular` might be specialized by the parent. In such cases we should not extract the parent. Currently, only `Diagonal` specializes `uppertriangular`, so this issue only exists for a `Hermitian` or a `Symmetric` that wraps a `Diagonal`. Fixes https://github.com/JuliaLang/julia/issues/56283 (cherry picked from commit 5e4fb519b5955b2f9ff9a26bd1dc5454561ecef7) --- stdlib/LinearAlgebra/src/symmetric.jl | 4 +++- stdlib/LinearAlgebra/test/symmetric.jl | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/src/symmetric.jl b/stdlib/LinearAlgebra/src/symmetric.jl index d6b2e860d1a06..d7fccdbf188fa 100644 --- a/stdlib/LinearAlgebra/src/symmetric.jl +++ b/stdlib/LinearAlgebra/src/symmetric.jl @@ -294,7 +294,9 @@ function applytri(f, A::HermOrSym, B::HermOrSym) f(uppertriangular(_conjugation(A)(A.data)), uppertriangular(B.data)) end end -parentof_applytri(f, args...) = applytri(parent ∘ f, args...) +_parent_tri(U::UpperOrLowerTriangular) = parent(U) +_parent_tri(U) = U +parentof_applytri(f, args...) = _parent_tri(applytri(f, args...)) isdiag(A::HermOrSym) = applytri(isdiag, A) diff --git a/stdlib/LinearAlgebra/test/symmetric.jl b/stdlib/LinearAlgebra/test/symmetric.jl index acf202fba0937..445dda3496b5d 100644 --- a/stdlib/LinearAlgebra/test/symmetric.jl +++ b/stdlib/LinearAlgebra/test/symmetric.jl @@ -1039,4 +1039,11 @@ end @test symT-s == Array(symT) - Array(s) end +@testset "issue #56283" begin + a = 1.0 + D = Diagonal(randn(10)) + H = Hermitian(D*D') + @test a*H == H +end + end # module TestSymmetric From ced7df7d0c9974e3656da7d5f2f75781142ba34c Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 23 Oct 2024 20:15:39 -0300 Subject: [PATCH 44/76] Fix trampoline warning on x86 as well (#56280) (cherry picked from commit 894296bd2398c297a4d18f0f16c7601a3237ffef) --- cli/trampolines/trampolines_x86_64.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/trampolines/trampolines_x86_64.S b/cli/trampolines/trampolines_x86_64.S index 3b800da56eee1..fcc8e40e1ddc9 100644 --- a/cli/trampolines/trampolines_x86_64.S +++ b/cli/trampolines/trampolines_x86_64.S @@ -6,9 +6,9 @@ #define XX(name) \ DEBUGINFO(name); \ .global CNAME(name); \ +CNAME(name)##:; \ .cfi_startproc; \ SEH_START1(name); \ -CNAME(name)##:; \ SEH_START2(); \ CET_START(); \ mov CNAMEADDR(name)(%rip),%r11; \ From debbc22f88d97c120a80135735b616ba7db71a73 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Sat, 26 Oct 2024 03:01:53 +0200 Subject: [PATCH 45/76] recommend explicit `using Foo: Foo, ...` in package code (was: "using considered harmful") (#42080) I feel we are heading up against a "`using` crisis" where any new feature that is implemented by exporting a new name (either in Base or a package) becomes a breaking change. This is already happening (https://github.com/JuliaGPU/CUDA.jl/pull/1097, https://github.com/JuliaWeb/HTTP.jl/pull/745) and as projects get bigger and more names are exported, the likelihood of this rapidly increases. The flaw in `using Foo` is fundamental in that you cannot lexically see where a name comes from so when two packages export the same name, you are screwed. Any code that relies on `using Foo` and then using an exported name from `Foo` is vulnerable to another dependency exporting the same name. Therefore, I think we should start to strongly discourage the use of `using Foo` and only recommend `using Foo` for ephemeral work (e.g. REPL work). --------- Co-authored-by: Dilum Aluthge Co-authored-by: Mason Protter Co-authored-by: Max Horn Co-authored-by: Matt Bauman Co-authored-by: Alex Arslan Co-authored-by: Ian Butterworth Co-authored-by: Neven Sajko (cherry picked from commit ee09ae70d9f4a04ed8b745f36d3c5d9d578d2887) --- base/docs/basedocs.jl | 8 ++++++++ doc/src/manual/modules.md | 9 ++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 0c07de336248b..be0ce0f216515 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -37,6 +37,14 @@ kw"help", kw"Julia", kw"julia", kw"" available for direct use. Names can also be used via dot syntax (e.g. `Foo.foo` to access the name `foo`), whether they are `export`ed or not. See the [manual section about modules](@ref modules) for details. + +!!! note + When two or more packages/modules export a name and that name does not refer to the + same thing in each of the packages, and the packages are loaded via `using` without + an explicit list of names, it is an error to reference that name without qualification. + It is thus recommended that code intended to be forward-compatible with future versions + of its dependencies and of Julia, e.g., code in released packages, list the names it + uses from each loaded package, e.g., `using Foo: Foo, f` rather than `using Foo`. """ kw"using" diff --git a/doc/src/manual/modules.md b/doc/src/manual/modules.md index 75e79d889dc7e..b7854c10844d3 100644 --- a/doc/src/manual/modules.md +++ b/doc/src/manual/modules.md @@ -112,7 +112,7 @@ and above. To maintain compatibility with Julia 1.10 and below, use the `@compat ### Standalone `using` and `import` -Possibly the most common way of loading a module is `using ModuleName`. This [loads](@ref +For interactive use, the most common way of loading a module is `using ModuleName`. This [loads](@ref code-loading) the code associated with `ModuleName`, and brings 1. the module name @@ -168,6 +168,13 @@ Importantly, the module name `NiceStuff` will *not* be in the namespace. If you julia> using .NiceStuff: nice, DOG, NiceStuff ``` +When two or more packages/modules export a name and that name does not refer to the +same thing in each of the packages, and the packages are loaded via `using` without +an explicit list of names, it is an error to reference that name without qualification. +It is thus recommended that code intended to be forward-compatible with future versions +of its dependencies and of Julia, e.g., code in released packages, list the names it +uses from each loaded package, e.g., `using Foo: Foo, f` rather than `using Foo`. + Julia has two forms for seemingly the same thing because only `import ModuleName: f` allows adding methods to `f` *without a module path*. That is to say, the following example will give an error: From 92405f293fbb43805a93d6ef73de27e5f0b4ca74 Mon Sep 17 00:00:00 2001 From: Aravindh Krishnamoorthy Date: Mon, 28 Oct 2024 12:32:21 +0100 Subject: [PATCH 46/76] Fix `log_quasitriu` for internal scaling `s=0` (#56311) This PR is a potential fix for #54833. ## Description The function https://github.com/JuliaLang/julia/blob/2a06376c18afd7ec875335070743dcebcd85dee7/stdlib/LinearAlgebra/src/triangular.jl#L2220 computes $\boldsymbol{A}^{\dfrac{1}{2^s}} - \boldsymbol{I}$ for a real-valued $2\times 2$ matrix $\boldsymbol{A}$ using Algorithm 5.1 in [R1]. However, the algorithm in [R1] as well as the above function do not handle the case $s=0.$ This fix extends the function to compute $\boldsymbol{A}^{\dfrac{1}{2^s}} - \boldsymbol{I} \Bigg|_{s=0} = \boldsymbol{A} - \boldsymbol{I}.$ ## Checklist - [X] Fix code: `stdlib\LinearAlgebra\src\triangular.jl` in function `_sqrt_pow_diag_block_2x2!(A, A0, s)`. - [X] Add test case: `stdlib\LinearAlgebra\test\triangular.jl`. - [X] Update `NEWS.md`. - [X] Testing and self review. | Tag | Reference | | --- | --- | | [R1] | Al-Mohy, Awad H. and Higham, Nicholas J. "Improved Inverse Scaling and Squaring Algorithms for the Matrix Logarithm", 2011, url: https://eprints.maths.manchester.ac.uk/1687/1/paper11.pdf | --------- Co-authored-by: Daniel Karrasch Co-authored-by: Oscar Smith (cherry picked from commit 2cdfe062952c3a1168da7545a10bfa0ec205b4db) --- stdlib/LinearAlgebra/src/triangular.jl | 5 +++++ stdlib/LinearAlgebra/test/triangular.jl | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index e3c96533a9864..ddf7c9f16ce16 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -2068,6 +2068,11 @@ end # SIAM J. Sci. Comput., 34(4), (2012) C153–C169. doi: 10.1137/110852553 # Algorithm 5.1 Base.@propagate_inbounds function _sqrt_pow_diag_block_2x2!(A, A0, s) + if iszero(s) + A[1,1] -= 1 + A[2,2] -= 1 + return A + end _sqrt_real_2x2!(A, A0) if isone(s) A[1,1] -= 1 diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index f3e6e4a744088..2ab6923212bb5 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -1130,4 +1130,14 @@ end end end +@testset "log_quasitriu with internal scaling s=0 (issue #54833)" begin + M = [0.9949357359852791 -0.015567763143324862 -0.09091193493947397 -0.03994428739762443 0.07338356301650806; + 0.011813655598647289 0.9968988574699793 -0.06204555000202496 0.04694097614450692 0.09028834462782365; + 0.092737943594701 0.059546719185135925 0.9935850721633324 0.025348893985651405 -0.018530261590167685; + 0.0369187299165628 -0.04903571106913449 -0.025962938675946543 0.9977767446862031 0.12901494726320517; + 0.0 0.0 0.0 0.0 1.0] + + @test exp(log(M)) ≈ M +end + end # module TestTriangular From 05182dc3bdb03c6409670387e2e0a94e1da8a2cb Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Mon, 28 Oct 2024 17:57:04 -0400 Subject: [PATCH 47/76] make `_unsetindex` fast for isbits eltype (#56364) fixes https://github.com/JuliaLang/julia/issues/56359#issuecomment-2441537634 ``` using Plots function f(n) a = Vector{Int}(undef, n) s = time_ns() resize!(a, 8) time_ns() - s end x = 8:10:1000000 y = f.(x) plot(x, y) ``` ![image](https://github.com/user-attachments/assets/5a1fb963-7d44-4cac-bedd-6f0733d4cf56) (cherry picked from commit e802eff6dd910a8321b45c6c629dc4300f5aef1e) --- base/genericmemory.jl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/base/genericmemory.jl b/base/genericmemory.jl index b37e2414c456d..4cfbb606de641 100644 --- a/base/genericmemory.jl +++ b/base/genericmemory.jl @@ -80,17 +80,16 @@ function _unsetindex!(A::MemoryRef{T}) where T MemT = typeof(mem) arrayelem = datatype_arrayelem(MemT) elsz = datatype_layoutsize(MemT) - isboxed = 1; isunion = 2 + isbits = 0; isboxed = 1; isunion = 2 + arrayelem == isbits && datatype_pointerfree(T::DataType) && return A t = @_gc_preserve_begin mem p = Ptr{Ptr{Cvoid}}(@inbounds pointer(A)) if arrayelem == isboxed Intrinsics.atomic_pointerset(p, C_NULL, :monotonic) elseif arrayelem != isunion - if !datatype_pointerfree(T::DataType) - for j = 1:Core.sizeof(Ptr{Cvoid}):elsz - # XXX: this violates memory ordering, since it writes more than one C_NULL to each - Intrinsics.atomic_pointerset(p + j - 1, C_NULL, :monotonic) - end + for j = 1:Core.sizeof(Ptr{Cvoid}):elsz + # XXX: this violates memory ordering, since it writes more than one C_NULL to each + Intrinsics.atomic_pointerset(p + j - 1, C_NULL, :monotonic) end end @_gc_preserve_end t From c558d263d109479d64e3b4c51e5b1b85cba5c1f0 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 30 Oct 2024 16:01:07 +0100 Subject: [PATCH 48/76] load extensions with fewer triggers earlier (#49891) Aimed to support the use case in https://github.com/JuliaLang/julia/issues/48734#issuecomment-1554626135. https://github.com/KristofferC/ExtSquared.jl is an example, see specifically https://github.com/KristofferC/ExtSquared.jl/blob/ded7c57d6f799674e3310b8174dfb07591bbe025/ext/BExt.jl#L4. I think this makes sense, happy for a second pair of eyes though. cc @termi-official --------- Co-authored-by: KristofferC Co-authored-by: Cody Tapscott <84105208+topolarity@users.noreply.github.com> --- base/loading.jl | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index db022a256c518..d36059fd36cdc 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1414,6 +1414,7 @@ end mutable struct ExtensionId const id::PkgId const parentid::PkgId # just need the name, for printing + const n_total_triggers::Int ntriggers::Int # how many more packages must be defined until this is loaded end @@ -1509,7 +1510,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any} continue # extension is already primed or loaded, don't add it again end EXT_PRIMED[id] = parent - gid = ExtensionId(id, parent, 1 + length(triggers)) + gid = ExtensionId(id, parent, 1 + length(triggers), 1 + length(triggers)) trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, parent) push!(trigger1, gid) for trigger in triggers @@ -1553,25 +1554,22 @@ function run_extension_callbacks(pkgid::PkgId) # take ownership of extids that depend on this pkgid extids = pop!(EXT_DORMITORY, pkgid, nothing) extids === nothing && return + extids_to_load = Vector{ExtensionId}() for extid in extids - if extid.ntriggers > 0 - # indicate pkgid is loaded - extid.ntriggers -= 1 - end - if extid.ntriggers < 0 - # indicate pkgid is loaded - extid.ntriggers += 1 - succeeded = false - else - succeeded = true - end + @assert extid.ntriggers > 0 + extid.ntriggers -= 1 if extid.ntriggers == 0 - # actually load extid, now that all dependencies are met, - # and record the result - succeeded = succeeded && run_extension_callbacks(extid) - succeeded || push!(EXT_DORMITORY_FAILED, extid) + push!(extids_to_load, extid) end end + # Load extensions with the fewest triggers first + sort!(extids_to_load, by=extid->extid.n_total_triggers) + for extid in extids_to_load + # actually load extid, now that all dependencies are met, + succeeded = run_extension_callbacks(extid) + succeeded || push!(EXT_DORMITORY_FAILED, extid) + end + return end From 23cb1281abb710d996727d8b69ed2115275207b1 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 30 Oct 2024 22:35:37 +0530 Subject: [PATCH 49/76] Fix broken diagonal kron test --- stdlib/LinearAlgebra/test/diagonal.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index fda51cf402a5b..4ecbef9bfd058 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -1329,8 +1329,10 @@ end @test kron(D, B) == kron(Array(D), B) @test kron(B, D) == kron(B, Array(D)) D2 = Diagonal([ones(2,2), ones(3,3)]) - @test kron(D, D2) == Diagonal([diag(D2); 2diag(D2)]) - @test kron(D2, D) == Diagonal([ones(2,2), fill(2.0,2,2), ones(3,3), fill(2.0,3,3)]) + A = kron(D, D2) + @test A == kron!(similar(A), D, D2) == kron!(similar(A, size(A)), D, D2) + A = kron(D2, D) + @test A == kron!(similar(A), D2, D) == kron!(similar(A, size(A)), D2, D) end end # module TestDiagonal From bf5675bce19d9a47b2516663fcd1d4b3cffa5c93 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Thu, 31 Oct 2024 09:25:28 -0400 Subject: [PATCH 50/76] =?UTF-8?q?[backports-release-1.11]=20Allow=20ext=20?= =?UTF-8?q?=E2=86=92=20ext=20dependency=20if=20triggers=20are=20a=20strict?= =?UTF-8?q?=20superset=20(#56368)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an effort at a proper workaround (feature?) to resolve https://github.com/JuliaLang/julia/issues/56204. For example: ```toml [extensions] PlottingExt = "Plots" StatisticsPlottingExt = ["Plots", "Statistics"] ``` Here `StatisticsPlottingExt` is allowed to depend on `PlottingExt` This provides a way to declare `ext → ext` dependencies while still avoiding any extension cycles. The same trick can also be used to make an extension in one package depend on an extension provided in another. We'll also need to land #49891, so that we guarantee these load in the right order. --- NEWS.md | 4 + base/loading.jl | 75 ++++++++++--------- base/precompilation.jl | 31 +++++--- test/loading.jl | 68 +++++++++++++++++ .../Manifest.toml | 32 ++++++++ .../Project.toml | 12 +++ .../ext/ExtAB.jl | 12 +++ .../src/CrossPackageExtToExtDependency.jl | 7 ++ .../Extensions/CyclicExtensions/Manifest.toml | 7 +- .../Extensions/EnvWithDeps/Manifest.toml | 7 +- .../EnvWithHasExtensions/Manifest.toml | 7 +- .../EnvWithHasExtensionsv2/Manifest.toml | 7 +- .../project/Extensions/ExtDep.jl/Project.toml | 1 + .../Extensions/ExtDep.jl/src/ExtDep.jl | 1 + .../ExtToExtDependency/Manifest.toml | 21 ++++++ .../ExtToExtDependency/Project.toml | 14 ++++ .../Extensions/ExtToExtDependency/ext/ExtA.jl | 6 ++ .../ExtToExtDependency/ext/ExtAB.jl | 12 +++ .../src/ExtToExtDependency.jl | 7 ++ .../HasDepWithExtensions.jl/Manifest.toml | 7 +- .../Extensions/SomeOtherPackage/Project.toml | 4 + .../SomeOtherPackage/src/SomeOtherPackage.jl | 5 ++ 22 files changed, 297 insertions(+), 50 deletions(-) create mode 100644 test/project/Extensions/CrossPackageExtToExtDependency/Manifest.toml create mode 100644 test/project/Extensions/CrossPackageExtToExtDependency/Project.toml create mode 100644 test/project/Extensions/CrossPackageExtToExtDependency/ext/ExtAB.jl create mode 100644 test/project/Extensions/CrossPackageExtToExtDependency/src/CrossPackageExtToExtDependency.jl create mode 100644 test/project/Extensions/ExtToExtDependency/Manifest.toml create mode 100644 test/project/Extensions/ExtToExtDependency/Project.toml create mode 100644 test/project/Extensions/ExtToExtDependency/ext/ExtA.jl create mode 100644 test/project/Extensions/ExtToExtDependency/ext/ExtAB.jl create mode 100644 test/project/Extensions/ExtToExtDependency/src/ExtToExtDependency.jl create mode 100644 test/project/Extensions/SomeOtherPackage/Project.toml create mode 100644 test/project/Extensions/SomeOtherPackage/src/SomeOtherPackage.jl diff --git a/NEWS.md b/NEWS.md index 20b17cf6f68cd..a26b5b2d5944f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -37,6 +37,10 @@ Language changes omit the default user depot ([#51448]). * Precompilation cache files are now relocatable and their validity is now verified through a content hash of their source files instead of their `mtime` ([#49866]). +* Extensions may now depend on other extensions, if their triggers include all triggers of any + extension they wish to depend upon (+ at least one other trigger). Ext-to-ext dependencies + that don't meet this requirement are now blocked from using `Base.get_extension` during pre- + compilation, to prevent extension cycles [#55557]. Compiler/Runtime improvements ----------------------------- diff --git a/base/loading.jl b/base/loading.jl index d36059fd36cdc..558eb76d51e96 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -733,8 +733,9 @@ function manifest_uuid_path(env::String, pkg::PkgId)::Union{Nothing,String,Missi proj = implicit_manifest_uuid_path(env, pkg) proj === nothing || return proj # if not found - parentid = get(EXT_PRIMED, pkg, nothing) - if parentid !== nothing + triggers = get(EXT_PRIMED, pkg, nothing) + if triggers !== nothing + parentid = triggers[1] _, parent_project_file = entry_point_and_project_file(env, parentid.name) if parent_project_file !== nothing parentproj = project_file_name_uuid(parent_project_file, parentid.name) @@ -1387,9 +1388,7 @@ function run_module_init(mod::Module, i::Int=1) end function run_package_callbacks(modkey::PkgId) - if !precompiling_extension - run_extension_callbacks(modkey) - end + run_extension_callbacks(modkey) assert_havelock(require_lock) unlock(require_lock) try @@ -1418,7 +1417,7 @@ mutable struct ExtensionId ntriggers::Int # how many more packages must be defined until this is loaded end -const EXT_PRIMED = Dict{PkgId, PkgId}() # Extension -> Parent +const EXT_PRIMED = Dict{PkgId,Vector{PkgId}}() # Extension -> Parent + Triggers (parent is always first) const EXT_DORMITORY = Dict{PkgId,Vector{ExtensionId}}() # Trigger -> Extensions that can be triggered by it const EXT_DORMITORY_FAILED = ExtensionId[] @@ -1509,7 +1508,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any} if haskey(EXT_PRIMED, id) || haskey(Base.loaded_modules, id) continue # extension is already primed or loaded, don't add it again end - EXT_PRIMED[id] = parent + EXT_PRIMED[id] = trigger_ids = PkgId[parent] gid = ExtensionId(id, parent, 1 + length(triggers), 1 + length(triggers)) trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, parent) push!(trigger1, gid) @@ -1517,6 +1516,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any} # TODO: Better error message if this lookup fails? uuid_trigger = UUID(totaldeps[trigger]::String) trigger_id = PkgId(uuid_trigger, trigger) + push!(trigger_ids, trigger_id) if !haskey(explicit_loaded_modules, trigger_id) || haskey(package_locks, trigger_id) trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, trigger_id) push!(trigger1, gid) @@ -1528,6 +1528,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any} end loading_extension::Bool = false +loadable_extensions::Union{Nothing,Vector{PkgId}} = nothing precompiling_extension::Bool = false function run_extension_callbacks(extid::ExtensionId) assert_havelock(require_lock) @@ -1558,7 +1559,7 @@ function run_extension_callbacks(pkgid::PkgId) for extid in extids @assert extid.ntriggers > 0 extid.ntriggers -= 1 - if extid.ntriggers == 0 + if extid.ntriggers == 0 && (loadable_extensions === nothing || extid.id in loadable_extensions) push!(extids_to_load, extid) end end @@ -2542,7 +2543,17 @@ function _require(pkg::PkgId, env=nothing) # double-check the search now that we have lock m = _require_search_from_serialized(pkg, path, UInt128(0), true) m isa Module && return m - return compilecache(pkg, path; reasons) + triggers = get(EXT_PRIMED, pkg, nothing) + loadable_exts = nothing + if triggers !== nothing # extension + loadable_exts = PkgId[] + for (ext′, triggers′) in EXT_PRIMED + if triggers′ ⊊ triggers + push!(loadable_exts, ext′) + end + end + end + return compilecache(pkg, path; reasons, loadable_exts) end loaded isa Module && return loaded if isnothing(loaded) # maybe_cachefile_lock returns nothing if it had to wait for another process @@ -2865,10 +2876,16 @@ function check_package_module_loaded(pkg::PkgId) return nothing end +# protects against PkgId and UUID being imported and losing Base prefix +_pkg_str(_pkg::PkgId) = (_pkg.uuid === nothing) ? "Base.PkgId($(repr(_pkg.name)))" : "Base.PkgId(Base.UUID(\"$(_pkg.uuid)\"), $(repr(_pkg.name)))" +_pkg_str(_pkg::Vector) = sprint(show, eltype(_pkg); context = :module=>nothing) * "[" * join(map(_pkg_str, _pkg), ",") * "]" +_pkg_str(_pkg::Pair{PkgId}) = _pkg_str(_pkg.first) * " => " * repr(_pkg.second) +_pkg_str(_pkg::Nothing) = "nothing" + const PRECOMPILE_TRACE_COMPILE = Ref{String}() function create_expr_cache(pkg::PkgId, input::String, output::String, output_o::Union{Nothing, String}, concrete_deps::typeof(_concrete_dependencies), flags::Cmd=``, cacheflags::CacheFlags=CacheFlags(), - internal_stderr::IO = stderr, internal_stdout::IO = stdout, isext::Bool=false) + internal_stderr::IO = stderr, internal_stdout::IO = stdout, loadable_exts::Union{Vector{PkgId},Nothing}=nothing) @nospecialize internal_stderr internal_stdout rm(output, force=true) # Remove file if it exists output_o === nothing || rm(output_o, force=true) @@ -2876,8 +2893,9 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: dl_load_path = map(abspath, DL_LOAD_PATH) load_path = map(abspath, Base.load_path()) # if pkg is a stdlib, append its parent Project.toml to the load path - parentid = get(EXT_PRIMED, pkg, nothing) - if parentid !== nothing + triggers = get(EXT_PRIMED, pkg, nothing) + if triggers !== nothing + parentid = triggers[1] for env in load_path project_file = env_project_file(env) if project_file === true @@ -2895,22 +2913,6 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: any(path -> path_sep in path, load_path) && error("LOAD_PATH entries cannot contain $(repr(path_sep))") - deps_strs = String[] - # protects against PkgId and UUID being imported and losing Base prefix - function pkg_str(_pkg::PkgId) - if _pkg.uuid === nothing - "Base.PkgId($(repr(_pkg.name)))" - else - "Base.PkgId(Base.UUID(\"$(_pkg.uuid)\"), $(repr(_pkg.name)))" - end - end - for (pkg, build_id) in concrete_deps - push!(deps_strs, "$(pkg_str(pkg)) => $(repr(build_id))") - end - deps_eltype = sprint(show, eltype(concrete_deps); context = :module=>nothing) - deps = deps_eltype * "[" * join(deps_strs, ",") * "]" - precomp_stack = "Base.PkgId[$(join(map(pkg_str, vcat(Base.precompilation_stack, pkg)), ", "))]" - if output_o === nothing # remove options that make no difference given the other cache options cacheflags = CacheFlags(cacheflags, opt_level=0) @@ -2941,10 +2943,11 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: # write data over stdin to avoid the (unlikely) case of exceeding max command line size write(io.in, """ empty!(Base.EXT_DORMITORY) # If we have a custom sysimage with `EXT_DORMITORY` prepopulated - Base.track_nested_precomp($precomp_stack) - Base.precompiling_extension = $(loading_extension | isext) - Base.include_package_for_output($(pkg_str(pkg)), $(repr(abspath(input))), $(repr(depot_path)), $(repr(dl_load_path)), - $(repr(load_path)), $deps, $(repr(source_path(nothing)))) + Base.track_nested_precomp($(_pkg_str(vcat(Base.precompilation_stack, pkg)))) + Base.loadable_extensions = $(_pkg_str(loadable_exts)) + Base.precompiling_extension = $(loading_extension) + Base.include_package_for_output($(_pkg_str(pkg)), $(repr(abspath(input))), $(repr(depot_path)), $(repr(dl_load_path)), + $(repr(load_path)), $(_pkg_str(concrete_deps)), $(repr(source_path(nothing)))) """) close(io.in) return io @@ -2999,18 +3002,18 @@ This can be used to reduce package load times. Cache files are stored in `DEPOT_PATH[1]/compiled`. See [Module initialization and precompilation](@ref) for important notes. """ -function compilecache(pkg::PkgId, internal_stderr::IO = stderr, internal_stdout::IO = stdout; flags::Cmd=``, reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}(), isext::Bool=false) +function compilecache(pkg::PkgId, internal_stderr::IO = stderr, internal_stdout::IO = stdout; flags::Cmd=``, reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}(), loadable_exts::Union{Vector{PkgId},Nothing}=nothing) @nospecialize internal_stderr internal_stdout path = locate_package(pkg) path === nothing && throw(ArgumentError("$(repr("text/plain", pkg)) not found during precompilation")) - return compilecache(pkg, path, internal_stderr, internal_stdout; flags, reasons, isext) + return compilecache(pkg, path, internal_stderr, internal_stdout; flags, reasons, loadable_exts) end const MAX_NUM_PRECOMPILE_FILES = Ref(10) function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, internal_stdout::IO = stdout, keep_loaded_modules::Bool = true; flags::Cmd=``, cacheflags::CacheFlags=CacheFlags(), - reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}(), isext::Bool=false) + reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}(), loadable_exts::Union{Vector{PkgId},Nothing}=nothing) @nospecialize internal_stderr internal_stdout # decide where to put the resulting cache file @@ -3050,7 +3053,7 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in close(tmpio_o) close(tmpio_so) end - p = create_expr_cache(pkg, path, tmppath, tmppath_o, concrete_deps, flags, cacheflags, internal_stderr, internal_stdout, isext) + p = create_expr_cache(pkg, path, tmppath, tmppath_o, concrete_deps, flags, cacheflags, internal_stderr, internal_stdout, loadable_exts) if success(p) if cache_objects diff --git a/base/precompilation.jl b/base/precompilation.jl index 0cd0f6346c308..c6fabf9b71a54 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -419,6 +419,7 @@ function _precompilepkgs(pkgs::Vector{String}, return name end + triggers = Dict{Base.PkgId,Vector{Base.PkgId}}() for (dep, deps) in env.deps pkg = Base.PkgId(dep, env.names[dep]) Base.in_sysimage(pkg) && continue @@ -427,25 +428,22 @@ function _precompilepkgs(pkgs::Vector{String}, # add any extensions pkg_exts = Dict{Base.PkgId, Vector{Base.PkgId}}() for (ext_name, extdep_uuids) in env.extensions[dep] - ext_deps = Base.PkgId[] - push!(ext_deps, pkg) # depends on parent package + ext_uuid = Base.uuid5(pkg.uuid, ext_name) + ext = Base.PkgId(ext_uuid, ext_name) + triggers[ext] = Base.PkgId[pkg] # depends on parent package all_extdeps_available = true for extdep_uuid in extdep_uuids extdep_name = env.names[extdep_uuid] if extdep_uuid in keys(env.deps) - push!(ext_deps, Base.PkgId(extdep_uuid, extdep_name)) + push!(triggers[ext], Base.PkgId(extdep_uuid, extdep_name)) else all_extdeps_available = false break end end all_extdeps_available || continue - ext_uuid = Base.uuid5(pkg.uuid, ext_name) - ext = Base.PkgId(ext_uuid, ext_name) - filter!(!Base.in_sysimage, ext_deps) - depsmap[ext] = ext_deps exts[ext] = pkg.name - pkg_exts[ext] = ext_deps + pkg_exts[ext] = depsmap[ext] = filter(!Base.in_sysimage, triggers[ext]) end if !isempty(pkg_exts) pkg_exts_map[pkg] = collect(keys(pkg_exts)) @@ -461,6 +459,16 @@ function _precompilepkgs(pkgs::Vector{String}, append!(direct_deps, keys(filter(d->last(d) in keys(env.project_deps), exts))) @debug "precompile: deps collected" + + # An extension effectively depends on another extension if it has a strict superset of its triggers + for ext_a in keys(exts) + for ext_b in keys(exts) + if triggers[ext_a] ⊋ triggers[ext_b] + push!(depsmap[ext_a], ext_b) + end + end + end + # this loop must be run after the full depsmap has been populated for (pkg, pkg_exts) in pkg_exts_map # find any packages that depend on the extension(s)'s deps and replace those deps in their deps list with the extension(s), @@ -817,7 +825,12 @@ function _precompilepkgs(pkgs::Vector{String}, t = @elapsed ret = precompile_pkgs_maybe_cachefile_lock(io, print_lock, fancyprint, pkg_config, pkgspidlocked, hascolor) do Base.with_logger(Base.NullLogger()) do # The false here means we ignore loaded modules, so precompile for a fresh session - Base.compilecache(pkg, sourcepath, std_pipe, std_pipe, false; flags, cacheflags, isext = haskey(exts, pkg)) + keep_loaded_modules = false + # for extensions, any extension in our direct dependencies is one we have a right to load + # for packages, we may load any extension (all possible triggers are accounted for above) + loadable_exts = haskey(exts, pkg) ? filter((dep)->haskey(exts, dep), depsmap[pkg]) : nothing + Base.compilecache(pkg, sourcepath, std_pipe, std_pipe, keep_loaded_modules; + flags, cacheflags, loadable_exts) end end if ret isa Base.PrecompilableError diff --git a/test/loading.jl b/test/loading.jl index 664bd778ca926..8f0c4897e24eb 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1158,6 +1158,74 @@ end cmd = addenv(cmd, "JULIA_LOAD_PATH" => proj) @test occursin("Hello Cycles!", String(read(cmd))) + # Extension-to-extension dependencies + + mktempdir() do depot # Parallel pre-compilation + code = """ + Base.disable_parallel_precompile = false + using ExtToExtDependency + Base.get_extension(ExtToExtDependency, :ExtA) isa Module || error("expected extension to load") + Base.get_extension(ExtToExtDependency, :ExtAB) isa Module || error("expected extension to load") + ExtToExtDependency.greet() + """ + proj = joinpath(@__DIR__, "project", "Extensions", "ExtToExtDependency") + cmd = `$(Base.julia_cmd()) --startup-file=no -e $code` + cmd = addenv(cmd, + "JULIA_LOAD_PATH" => proj, + "JULIA_DEPOT_PATH" => depot * Base.Filesystem.pathsep(), + ) + @test occursin("Hello ext-to-ext!", String(read(cmd))) + end + mktempdir() do depot # Serial pre-compilation + code = """ + Base.disable_parallel_precompile = true + using ExtToExtDependency + Base.get_extension(ExtToExtDependency, :ExtA) isa Module || error("expected extension to load") + Base.get_extension(ExtToExtDependency, :ExtAB) isa Module || error("expected extension to load") + ExtToExtDependency.greet() + """ + proj = joinpath(@__DIR__, "project", "Extensions", "ExtToExtDependency") + cmd = `$(Base.julia_cmd()) --startup-file=no -e $code` + cmd = addenv(cmd, + "JULIA_LOAD_PATH" => proj, + "JULIA_DEPOT_PATH" => depot * Base.Filesystem.pathsep(), + ) + @test occursin("Hello ext-to-ext!", String(read(cmd))) + end + + mktempdir() do depot # Parallel pre-compilation + code = """ + Base.disable_parallel_precompile = false + using CrossPackageExtToExtDependency + Base.get_extension(CrossPackageExtToExtDependency.CyclicExtensions, :ExtA) isa Module || error("expected extension to load") + Base.get_extension(CrossPackageExtToExtDependency, :ExtAB) isa Module || error("expected extension to load") + CrossPackageExtToExtDependency.greet() + """ + proj = joinpath(@__DIR__, "project", "Extensions", "CrossPackageExtToExtDependency") + cmd = `$(Base.julia_cmd()) --startup-file=no -e $code` + cmd = addenv(cmd, + "JULIA_LOAD_PATH" => proj, + "JULIA_DEPOT_PATH" => depot * Base.Filesystem.pathsep(), + ) + @test occursin("Hello x-package ext-to-ext!", String(read(cmd))) + end + mktempdir() do depot # Serial pre-compilation + code = """ + Base.disable_parallel_precompile = true + using CrossPackageExtToExtDependency + Base.get_extension(CrossPackageExtToExtDependency.CyclicExtensions, :ExtA) isa Module || error("expected extension to load") + Base.get_extension(CrossPackageExtToExtDependency, :ExtAB) isa Module || error("expected extension to load") + CrossPackageExtToExtDependency.greet() + """ + proj = joinpath(@__DIR__, "project", "Extensions", "CrossPackageExtToExtDependency") + cmd = `$(Base.julia_cmd()) --startup-file=no -e $code` + cmd = addenv(cmd, + "JULIA_LOAD_PATH" => proj, + "JULIA_DEPOT_PATH" => depot * Base.Filesystem.pathsep(), + ) + @test occursin("Hello x-package ext-to-ext!", String(read(cmd))) + end + finally try rm(depot_path, force=true, recursive=true) diff --git a/test/project/Extensions/CrossPackageExtToExtDependency/Manifest.toml b/test/project/Extensions/CrossPackageExtToExtDependency/Manifest.toml new file mode 100644 index 0000000000000..5497fdb7091bb --- /dev/null +++ b/test/project/Extensions/CrossPackageExtToExtDependency/Manifest.toml @@ -0,0 +1,32 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.11.1" +manifest_format = "2.0" +project_hash = "dc35c2cf8c6b82fb5b9624c9713c2df34ca30499" + +[[deps.CyclicExtensions]] +deps = ["ExtDep"] +path = "../CyclicExtensions" +uuid = "17d4f0df-b55c-4714-ac4b-55fa23f7355c" +version = "0.1.0" +weakdeps = ["SomePackage"] + + [deps.CyclicExtensions.extensions] + ExtA = ["SomePackage"] + ExtB = ["SomePackage"] + +[[deps.ExtDep]] +deps = ["SomeOtherPackage", "SomePackage"] +path = "../ExtDep.jl" +uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" +version = "0.1.0" + +[[deps.SomeOtherPackage]] +path = "../SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +version = "0.1.0" + +[[deps.SomePackage]] +path = "../SomePackage" +uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" +version = "0.1.0" diff --git a/test/project/Extensions/CrossPackageExtToExtDependency/Project.toml b/test/project/Extensions/CrossPackageExtToExtDependency/Project.toml new file mode 100644 index 0000000000000..76ffb7bd1c882 --- /dev/null +++ b/test/project/Extensions/CrossPackageExtToExtDependency/Project.toml @@ -0,0 +1,12 @@ +name = "CrossPackageExtToExtDependency" +uuid = "30f07f2e-c47e-40db-93a2-cbc4d1b301cc" +version = "0.1.0" + +[deps] +CyclicExtensions = "17d4f0df-b55c-4714-ac4b-55fa23f7355c" + +[weakdeps] +SomePackage = "678608ae-7bb3-42c7-98b1-82102067a3d8" + +[extensions] +ExtAB = ["CyclicExtensions", "SomePackage"] diff --git a/test/project/Extensions/CrossPackageExtToExtDependency/ext/ExtAB.jl b/test/project/Extensions/CrossPackageExtToExtDependency/ext/ExtAB.jl new file mode 100644 index 0000000000000..1ded9f2df5097 --- /dev/null +++ b/test/project/Extensions/CrossPackageExtToExtDependency/ext/ExtAB.jl @@ -0,0 +1,12 @@ +module ExtAB + +using CrossPackageExtToExtDependency +using SomePackage +using CyclicExtensions + +const ExtA = Base.get_extension(CyclicExtensions, :ExtA) +if !(ExtA isa Module) + error("expected extension to load") +end + +end diff --git a/test/project/Extensions/CrossPackageExtToExtDependency/src/CrossPackageExtToExtDependency.jl b/test/project/Extensions/CrossPackageExtToExtDependency/src/CrossPackageExtToExtDependency.jl new file mode 100644 index 0000000000000..28b229e2d61bf --- /dev/null +++ b/test/project/Extensions/CrossPackageExtToExtDependency/src/CrossPackageExtToExtDependency.jl @@ -0,0 +1,7 @@ +module CrossPackageExtToExtDependency + +using CyclicExtensions + +greet() = print("Hello x-package ext-to-ext!") + +end # module CrossPackageExtToTextDependency diff --git a/test/project/Extensions/CyclicExtensions/Manifest.toml b/test/project/Extensions/CyclicExtensions/Manifest.toml index a506825cf7995..0f280293c07b6 100644 --- a/test/project/Extensions/CyclicExtensions/Manifest.toml +++ b/test/project/Extensions/CyclicExtensions/Manifest.toml @@ -5,7 +5,7 @@ manifest_format = "2.0" project_hash = "ec25ff8df3a5e2212a173c3de2c7d716cc47cd36" [[deps.ExtDep]] -deps = ["SomePackage"] +deps = ["SomePackage", "SomeOtherPackage"] path = "../ExtDep.jl" uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" version = "0.1.0" @@ -15,6 +15,11 @@ path = "../ExtDep2" uuid = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" version = "0.1.0" +[[deps.SomeOtherPackage]] +path = "../SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +version = "0.1.0" + [[deps.SomePackage]] path = "../SomePackage" uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" diff --git a/test/project/Extensions/EnvWithDeps/Manifest.toml b/test/project/Extensions/EnvWithDeps/Manifest.toml index 85ff259f0a4d5..554a317b370eb 100644 --- a/test/project/Extensions/EnvWithDeps/Manifest.toml +++ b/test/project/Extensions/EnvWithDeps/Manifest.toml @@ -5,7 +5,7 @@ manifest_format = "2.0" project_hash = "ec25ff8df3a5e2212a173c3de2c7d716cc47cd36" [[deps.ExtDep]] -deps = ["SomePackage"] +deps = ["SomePackage", "SomeOtherPackage"] path = "../ExtDep.jl" uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" version = "0.1.0" @@ -15,6 +15,11 @@ path = "../ExtDep2" uuid = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" version = "0.1.0" +[[deps.SomeOtherPackage]] +path = "../SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +version = "0.1.0" + [[deps.SomePackage]] path = "../SomePackage" uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" diff --git a/test/project/Extensions/EnvWithHasExtensions/Manifest.toml b/test/project/Extensions/EnvWithHasExtensions/Manifest.toml index 004ef7892c173..ca2be57c61596 100644 --- a/test/project/Extensions/EnvWithHasExtensions/Manifest.toml +++ b/test/project/Extensions/EnvWithHasExtensions/Manifest.toml @@ -5,7 +5,7 @@ manifest_format = "2.0" project_hash = "a4c480cfa7da9610333d5c42623bf746bd286c5f" [[deps.ExtDep]] -deps = ["SomePackage"] +deps = ["SomePackage", "SomeOtherPackage"] path = "../ExtDep.jl" uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" version = "0.1.0" @@ -25,6 +25,11 @@ version = "0.1.0" ExtDep2 = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +[[deps.SomeOtherPackage]] +path = "../SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +version = "0.1.0" + [[deps.SomePackage]] path = "../SomePackage" uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" diff --git a/test/project/Extensions/EnvWithHasExtensionsv2/Manifest.toml b/test/project/Extensions/EnvWithHasExtensionsv2/Manifest.toml index 66781a5701363..9f8c717041b6e 100644 --- a/test/project/Extensions/EnvWithHasExtensionsv2/Manifest.toml +++ b/test/project/Extensions/EnvWithHasExtensionsv2/Manifest.toml @@ -5,7 +5,7 @@ manifest_format = "2.0" project_hash = "caa716752e6dff3d77c3de929ebbb5d2024d04ef" [[deps.ExtDep]] -deps = ["SomePackage"] +deps = ["SomePackage", "SomeOtherPackage"] path = "../ExtDep.jl" uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" version = "0.1.0" @@ -19,6 +19,11 @@ weakdeps = ["ExtDep"] [deps.HasExtensions.extensions] Extension2 = "ExtDep" +[[deps.SomeOtherPackage]] +path = "../SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +version = "0.1.0" + [[deps.SomePackage]] path = "../SomePackage" uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" diff --git a/test/project/Extensions/ExtDep.jl/Project.toml b/test/project/Extensions/ExtDep.jl/Project.toml index d246934b7f958..1ece7bf11f95a 100644 --- a/test/project/Extensions/ExtDep.jl/Project.toml +++ b/test/project/Extensions/ExtDep.jl/Project.toml @@ -4,3 +4,4 @@ version = "0.1.0" [deps] SomePackage = "678608ae-7bb3-42c7-98b1-82102067a3d8" +SomeOtherPackage = "178f68a2-4498-45ee-a775-452b36359b63" diff --git a/test/project/Extensions/ExtDep.jl/src/ExtDep.jl b/test/project/Extensions/ExtDep.jl/src/ExtDep.jl index 1c0022d879f51..2d3c6b7f28827 100644 --- a/test/project/Extensions/ExtDep.jl/src/ExtDep.jl +++ b/test/project/Extensions/ExtDep.jl/src/ExtDep.jl @@ -2,6 +2,7 @@ module ExtDep # loading this package makes the check for loading extensions trigger # which tests #47921 +using SomeOtherPackage using SomePackage struct ExtDepStruct end diff --git a/test/project/Extensions/ExtToExtDependency/Manifest.toml b/test/project/Extensions/ExtToExtDependency/Manifest.toml new file mode 100644 index 0000000000000..41546213cdd41 --- /dev/null +++ b/test/project/Extensions/ExtToExtDependency/Manifest.toml @@ -0,0 +1,21 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.11.1" +manifest_format = "2.0" +project_hash = "90b427e837c654fabb1434527ea698dabad46d29" + +[[deps.ExtDep]] +deps = ["SomeOtherPackage", "SomePackage"] +path = "../ExtDep.jl" +uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" +version = "0.1.0" + +[[deps.SomeOtherPackage]] +path = "../SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +version = "0.1.0" + +[[deps.SomePackage]] +path = "../SomePackage" +uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" +version = "0.1.0" diff --git a/test/project/Extensions/ExtToExtDependency/Project.toml b/test/project/Extensions/ExtToExtDependency/Project.toml new file mode 100644 index 0000000000000..980db74c04dc4 --- /dev/null +++ b/test/project/Extensions/ExtToExtDependency/Project.toml @@ -0,0 +1,14 @@ +name = "ExtToExtDependency" +uuid = "594ddb71-72fb-4cfe-9471-775d48a5b70b" +version = "0.1.0" + +[deps] +ExtDep = "fa069be4-f60b-4d4c-8b95-f8008775090c" + +[weakdeps] +SomeOtherPackage = "178f68a2-4498-45ee-a775-452b36359b63" +SomePackage = "678608ae-7bb3-42c7-98b1-82102067a3d8" + +[extensions] +ExtA = ["SomePackage"] +ExtAB = ["SomePackage", "SomeOtherPackage"] diff --git a/test/project/Extensions/ExtToExtDependency/ext/ExtA.jl b/test/project/Extensions/ExtToExtDependency/ext/ExtA.jl new file mode 100644 index 0000000000000..71ed09795157c --- /dev/null +++ b/test/project/Extensions/ExtToExtDependency/ext/ExtA.jl @@ -0,0 +1,6 @@ +module ExtA + +using ExtToExtDependency +using SomePackage + +end diff --git a/test/project/Extensions/ExtToExtDependency/ext/ExtAB.jl b/test/project/Extensions/ExtToExtDependency/ext/ExtAB.jl new file mode 100644 index 0000000000000..a5b2c43cafd58 --- /dev/null +++ b/test/project/Extensions/ExtToExtDependency/ext/ExtAB.jl @@ -0,0 +1,12 @@ +module ExtAB + +using ExtToExtDependency +using SomePackage +using SomeOtherPackage + +const ExtA = Base.get_extension(ExtToExtDependency, :ExtA) +if !(ExtA isa Module) + error("expected extension to load") +end + +end diff --git a/test/project/Extensions/ExtToExtDependency/src/ExtToExtDependency.jl b/test/project/Extensions/ExtToExtDependency/src/ExtToExtDependency.jl new file mode 100644 index 0000000000000..ec2bf58f18641 --- /dev/null +++ b/test/project/Extensions/ExtToExtDependency/src/ExtToExtDependency.jl @@ -0,0 +1,7 @@ +module ExtToExtDependency + +using ExtDep + +greet() = print("Hello ext-to-ext!") + +end # module ExtToExtDependency diff --git a/test/project/Extensions/HasDepWithExtensions.jl/Manifest.toml b/test/project/Extensions/HasDepWithExtensions.jl/Manifest.toml index 5706aba59d1e0..59bd802c9710a 100644 --- a/test/project/Extensions/HasDepWithExtensions.jl/Manifest.toml +++ b/test/project/Extensions/HasDepWithExtensions.jl/Manifest.toml @@ -5,7 +5,7 @@ manifest_format = "2.0" project_hash = "4e196b07f2ee7adc48ac9d528d42b3cf3737c7a0" [[deps.ExtDep]] -deps = ["SomePackage"] +deps = ["SomePackage", "SomeOtherPackage"] path = "../ExtDep.jl" uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" version = "0.1.0" @@ -37,6 +37,11 @@ version = "0.1.0" ExtDep2 = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +[[deps.SomeOtherPackage]] +path = "../SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +version = "0.1.0" + [[deps.SomePackage]] path = "../SomePackage" uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" diff --git a/test/project/Extensions/SomeOtherPackage/Project.toml b/test/project/Extensions/SomeOtherPackage/Project.toml new file mode 100644 index 0000000000000..6e7eee40c7be2 --- /dev/null +++ b/test/project/Extensions/SomeOtherPackage/Project.toml @@ -0,0 +1,4 @@ +name = "SomeOtherPackage" +uuid = "178f68a2-4498-45ee-a775-452b36359b63" +authors = ["Cody Tapscott "] +version = "0.1.0" diff --git a/test/project/Extensions/SomeOtherPackage/src/SomeOtherPackage.jl b/test/project/Extensions/SomeOtherPackage/src/SomeOtherPackage.jl new file mode 100644 index 0000000000000..ba23eb3914561 --- /dev/null +++ b/test/project/Extensions/SomeOtherPackage/src/SomeOtherPackage.jl @@ -0,0 +1,5 @@ +module SomeOtherPackage + +greet() = print("Hello World!") + +end # module SomeOtherPackage From 3ccd11c11918ec7e2568b6ec05e8d4a5afc8faef Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Fri, 1 Nov 2024 21:23:52 +0100 Subject: [PATCH 51/76] Fix dispatch for `rdiv!` with `LU` (#55764) --- stdlib/LinearAlgebra/src/lu.jl | 2 +- stdlib/LinearAlgebra/src/triangular.jl | 33 ++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/stdlib/LinearAlgebra/src/lu.jl b/stdlib/LinearAlgebra/src/lu.jl index d2e82af5d6409..0837ac08e74ea 100644 --- a/stdlib/LinearAlgebra/src/lu.jl +++ b/stdlib/LinearAlgebra/src/lu.jl @@ -786,7 +786,7 @@ function ldiv!(adjA::AdjointFactorization{<:Any,<:LU{T,Tridiagonal{T,V}}}, B::Ab return B end -rdiv!(B::AbstractMatrix, A::LU) = transpose(ldiv!(transpose(A), transpose(B))) +rdiv!(B::AbstractMatrix, A::LU{T,Tridiagonal{T,V}}) where {T,V} = transpose(ldiv!(transpose(A), transpose(B))) # Conversions AbstractMatrix(F::LU) = (F.L * F.U)[invperm(F.p),:] diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index ddf7c9f16ce16..cc819ffcecc34 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -919,9 +919,20 @@ _trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::AbstractTriangular) = _trimul!(C::AbstractMatrix, A::AbstractTriangular, B::UpperOrLowerTriangular) = generic_mattrimul!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B))) -lmul!(A::AbstractTriangular, B::AbstractVecOrMat) = @inline _trimul!(B, A, B) -rmul!(A::AbstractMatrix, B::AbstractTriangular) = @inline _trimul!(A, A, B) - +function lmul!(A::AbstractTriangular, B::AbstractVecOrMat) + if istriu(A) + _trimul!(B, uppertriangular(A), B) + else + _trimul!(B, lowertriangular(A), B) + end +end +function rmul!(A::AbstractMatrix, B::AbstractTriangular) + if istriu(B) + _trimul!(A, A, uppertriangular(B)) + else + _trimul!(A, A, lowertriangular(B)) + end +end for TC in (:AbstractVector, :AbstractMatrix) @eval @inline function _mul!(C::$TC, A::AbstractTriangular, B::AbstractVector, alpha::Number, beta::Number) @@ -957,8 +968,20 @@ _ldiv!(C::AbstractVecOrMat, A::UpperOrLowerTriangular, B::AbstractVecOrMat) = _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::UpperOrLowerTriangular) = generic_mattridiv!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B))) -ldiv!(A::AbstractTriangular, B::AbstractVecOrMat) = @inline _ldiv!(B, A, B) -rdiv!(A::AbstractMatrix, B::AbstractTriangular) = @inline _rdiv!(A, A, B) +function ldiv!(A::AbstractTriangular, B::AbstractVecOrMat) + if istriu(A) + _ldiv!(B, uppertriangular(A), B) + else + _ldiv!(B, lowertriangular(A), B) + end +end +function rdiv!(A::AbstractMatrix, B::AbstractTriangular) + if istriu(B) + _rdiv!(A, A, uppertriangular(B)) + else + _rdiv!(A, A, lowertriangular(B)) + end +end # preserve triangular structure in in-place multiplication/division for (cty, aty, bty) in ((:UpperTriangular, :UpperTriangular, :UpperTriangular), From 62081b89bde79eb5de466d3eb2d0570db3bf8d47 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Sun, 3 Nov 2024 20:55:18 -0500 Subject: [PATCH 52/76] =?UTF-8?q?=F0=9F=A4=96=20[backports-release-1.11]?= =?UTF-8?q?=20Bump=20the=20Pkg=20stdlib=20from=209438b6e99=20to=20af1088b4?= =?UTF-8?q?e=20(#56429)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: Pkg URL: https://github.com/JuliaLang/Pkg.jl.git Stdlib branch: release-1.11 Julia branch: backports-release-1.11 Old commit: 9438b6e99 New commit: af1088b4e Julia version: 1.11.1 Pkg version: 1.11.0(Does not match) Bump invoked by: @IanButterworth Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaLang/Pkg.jl/compare/9438b6e997aa1d63da16653350b130c45c1c086c...af1088b4e8a556cf9026253420855d4e28bd5b6b ``` $ git log --oneline 9438b6e99..af1088b4e af1088b4e Merge pull request #4066 from JuliaLang/backports-release-1.11 6886c9eb7 update julia version in CI ffcf85877 strip out tree_hash for stdlibs that have have been freed in newer julia versions (#4062) ``` Co-authored-by: Dilum Aluthge --- .../Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/md5 | 1 - .../Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/sha512 | 1 - .../Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/md5 | 1 + .../Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/md5 create mode 100644 deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/sha512 diff --git a/deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/md5 b/deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/md5 deleted file mode 100644 index 98452bbc8f83b..0000000000000 --- a/deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -c0906f18115dccaf61b2b7d2252beb2f diff --git a/deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/sha512 b/deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/sha512 deleted file mode 100644 index 162ddb54c8f4c..0000000000000 --- a/deps/checksums/Pkg-9438b6e997aa1d63da16653350b130c45c1c086c.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -d612a5aa88427b466ff2f5633f80578785e831e6ea8a3cd73bd411172b15b10141062d6685dea533b1be6a31504b5c69f1bf6d18e1934d83255738a61511f47e diff --git a/deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/md5 b/deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/md5 new file mode 100644 index 0000000000000..440e5b099a67b --- /dev/null +++ b/deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/md5 @@ -0,0 +1 @@ +dd9952a965af495f9223dc54ec63d1f0 diff --git a/deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/sha512 b/deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/sha512 new file mode 100644 index 0000000000000..ede88688cc9c1 --- /dev/null +++ b/deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/sha512 @@ -0,0 +1 @@ +9cbd0fc518c4c14f2674015df806fad214b033a3f0f5ee23a244450831d6e499e23a4e7d43b588f0d62ea2cd539ef5af55398a17f4a0ff98bcdb52b257855ecf diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index fa830950ce505..512b1db2fbe69 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.11 -PKG_SHA1 = 9438b6e997aa1d63da16653350b130c45c1c086c +PKG_SHA1 = af1088b4e8a556cf9026253420855d4e28bd5b6b PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 64a046fdd47f6f95f41e68525361454b2d75a8a3 Mon Sep 17 00:00:00 2001 From: Dilum Aluthge Date: Mon, 4 Nov 2024 15:24:25 +0000 Subject: [PATCH 53/76] =?UTF-8?q?=F0=9F=A4=96=20[backports-release-1.11]?= =?UTF-8?q?=20Bump=20the=20Pkg=20stdlib=20from=20af1088b4e=20to=202c625417?= =?UTF-8?q?c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/md5 | 1 + .../Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/sha512 | 1 + .../Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/md5 | 1 - .../Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/md5 create mode 100644 deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/sha512 diff --git a/deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/md5 b/deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/md5 new file mode 100644 index 0000000000000..965eff016641a --- /dev/null +++ b/deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/md5 @@ -0,0 +1 @@ +600a9b3b22a7f2df75a3636ab561a1a9 diff --git a/deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/sha512 b/deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/sha512 new file mode 100644 index 0000000000000..ff0728cde7342 --- /dev/null +++ b/deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/sha512 @@ -0,0 +1 @@ +1b907a6cd06cbd30e8709ffdcbb2ebb07a32a0fc63203f6972d90c2e07842dd4435cb651cd83db4e322bc3725b77172e4c6727f4a0a77a6da29a491d39a3787a diff --git a/deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/md5 b/deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/md5 deleted file mode 100644 index 440e5b099a67b..0000000000000 --- a/deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -dd9952a965af495f9223dc54ec63d1f0 diff --git a/deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/sha512 b/deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/sha512 deleted file mode 100644 index ede88688cc9c1..0000000000000 --- a/deps/checksums/Pkg-af1088b4e8a556cf9026253420855d4e28bd5b6b.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -9cbd0fc518c4c14f2674015df806fad214b033a3f0f5ee23a244450831d6e499e23a4e7d43b588f0d62ea2cd539ef5af55398a17f4a0ff98bcdb52b257855ecf diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 512b1db2fbe69..1e4d2aa0b931f 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.11 -PKG_SHA1 = af1088b4e8a556cf9026253420855d4e28bd5b6b +PKG_SHA1 = 2c625417c67f2d9ca59e46ef3a95022e0ec1eea8 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 8ec200d7834ef208c3d495dfeeaa1879807d0dbf Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 6 Nov 2024 14:03:03 -0300 Subject: [PATCH 54/76] Remove apple-m3 from cpu name to make enzyme happier (#56372) Should fix the warning folks are complaining about on 1.11 with enzyme --- src/processor_arm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/processor_arm.cpp b/src/processor_arm.cpp index 955209ea9c90b..ba2ad225c0e60 100644 --- a/src/processor_arm.cpp +++ b/src/processor_arm.cpp @@ -721,7 +721,7 @@ static NOINLINE std::pair> _get_host_cpu() else if (cpu_name.find("M2") != StringRef ::npos) return std::make_pair((uint32_t)CPU::apple_m2, Feature::apple_m2); else if (cpu_name.find("M3") != StringRef ::npos) - return std::make_pair((uint32_t)CPU::apple_m3, Feature::apple_m3); + return std::make_pair((uint32_t)CPU::apple_m2, Feature::apple_m2); // m3 doesn't exist in LLVM 16 for 1.11 else return std::make_pair((uint32_t)CPU::apple_m1, Feature::apple_m1); } From 52da829f8c23946d761c2f2c739ca17d88de11a3 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Thu, 7 Nov 2024 07:09:35 -0500 Subject: [PATCH 55/76] =?UTF-8?q?=F0=9F=A4=96=20[backports-release-1.11]?= =?UTF-8?q?=20Bump=20the=20Pkg=20stdlib=20from=202c625417c=20to=2064bf95a4?= =?UTF-8?q?8=20(#56486)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/md5 | 1 - .../Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/sha512 | 1 - .../Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/md5 | 1 + .../Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/md5 create mode 100644 deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/sha512 diff --git a/deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/md5 b/deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/md5 deleted file mode 100644 index 965eff016641a..0000000000000 --- a/deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -600a9b3b22a7f2df75a3636ab561a1a9 diff --git a/deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/sha512 b/deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/sha512 deleted file mode 100644 index ff0728cde7342..0000000000000 --- a/deps/checksums/Pkg-2c625417c67f2d9ca59e46ef3a95022e0ec1eea8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -1b907a6cd06cbd30e8709ffdcbb2ebb07a32a0fc63203f6972d90c2e07842dd4435cb651cd83db4e322bc3725b77172e4c6727f4a0a77a6da29a491d39a3787a diff --git a/deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/md5 b/deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/md5 new file mode 100644 index 0000000000000..15b698352e581 --- /dev/null +++ b/deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/md5 @@ -0,0 +1 @@ +219e6af925739b706b73d74f2059b6a4 diff --git a/deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/sha512 b/deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/sha512 new file mode 100644 index 0000000000000..1583b39817edc --- /dev/null +++ b/deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/sha512 @@ -0,0 +1 @@ +23b24d5c1cbe6e16b2388e79c233877d77471d3766002e02d0160eb1020e11c42b0c300b70d85c6b89a11e5de83708a3165e40d44cc21f011286eb6f096f2496 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 1e4d2aa0b931f..6dee5ed981ffa 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.11 -PKG_SHA1 = 2c625417c67f2d9ca59e46ef3a95022e0ec1eea8 +PKG_SHA1 = 64bf95a4847898cf70de54d0c861c877ed5c1595 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 94488d86f61d1d28003277d81f5acdcc0442b5db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Thu, 7 Nov 2024 19:11:48 +0000 Subject: [PATCH 56/76] Fix compilation warning on aarch64-linux (#56480) This fixes the warning: ``` /cache/build/default-aws-aarch64-ci-1-3/julialang/julia-master/src/stackwalk.c: In function 'jl_simulate_longjmp': /cache/build/default-aws-aarch64-ci-1-3/julialang/julia-master/src/stackwalk.c:995:22: warning: initialization of 'mcontext_t *' {aka 'struct sigcontext *'} from incompatible pointer type 'struct unw_sigcontext *' [-Wincompatible-pointer-types] 995 | mcontext_t *mc = &c->uc_mcontext; | ^ ``` This is the last remaining warning during compilation on aarch64-linux. (cherry picked from commit 8593792f8f5212d5513fe0829253664c3ceedd2a) --- src/stackwalk.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/stackwalk.c b/src/stackwalk.c index 37f239609504e..0cf7b11d7cb86 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -919,7 +919,13 @@ static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT memset(&c, 0, sizeof(c)); #if defined(_OS_LINUX_) && defined(__GLIBC__) __jmp_buf *mctx = &t->ctx.ctx.uc_mcontext->__jmpbuf; + #if defined(_CPU_AARCH64_) + // Only on aarch64-linux libunwind uses a different struct than system's one: + // . + struct unw_sigcontext *mc = &c.uc_mcontext; + #else mcontext_t *mc = &c.uc_mcontext; + #endif #if defined(_CPU_X86_) // https://github.com/bminor/glibc/blame/master/sysdeps/i386/__longjmp.S // https://github.com/bminor/glibc/blame/master/sysdeps/i386/jmpbuf-offsets.h From 4877204324224187aff3b3505e4a8fb7fafb4998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Mon, 11 Mar 2024 20:54:46 +0000 Subject: [PATCH 57/76] Fix warning about comparison of integer expressions of different signedness (#53658) (cherry picked from commit 5fc16621822398419a45631a1c0fc421a5cbd88c) --- src/flisp/julia_extensions.c | 2 +- src/llvm-multiversioning.cpp | 2 +- src/processor.cpp | 8 ++++---- src/processor_arm.cpp | 10 +++++----- src/processor_fallback.cpp | 2 +- src/processor_x86.cpp | 6 +++--- src/smallintset.c | 4 ++-- src/support/utf8.h | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/flisp/julia_extensions.c b/src/flisp/julia_extensions.c index f29e3972755c5..79a007df8bfde 100644 --- a/src/flisp/julia_extensions.c +++ b/src/flisp/julia_extensions.c @@ -405,7 +405,7 @@ value_t fl_string_only_julia_char(fl_context_t *fl_ctx, value_t *args, uint32_t uint8_t *s = (uint8_t*)cvalue_data(args[0]); size_t len = cv_len((cvalue_t*)ptr(args[0])); uint32_t u = _string_only_julia_char(s, len); - if (u == (uint32_t)-1) + if (u == UINT32_MAX) return fl_ctx->F; return fl_list2(fl_ctx, fl_ctx->jl_char_sym, mk_uint32(fl_ctx, u)); } diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index c7175859acf83..1901a721e5e08 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -755,7 +755,7 @@ std::pair CloneCtx::get_reloc_slot(Function *F) const if (F->isDeclaration()) { auto extern_decl = extern_relocs.find(F); assert(extern_decl != extern_relocs.end() && "Missing extern relocation slot!"); - return {(uint32_t)-1, extern_decl->second}; + return {UINT32_MAX, extern_decl->second}; } else { auto id = get_func_id(F); diff --git a/src/processor.cpp b/src/processor.cpp index fc56a93be9787..730e470f4153d 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -389,7 +389,7 @@ JL_UNUSED static uint32_t find_feature_bit(const FeatureName *features, size_t n return feature.bit; } } - return (uint32_t)-1; + return UINT32_MAX; } // This is how we save the target identification. @@ -642,7 +642,7 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) jl_value_t* rejection_reason = nullptr; JL_GC_PUSH1(&rejection_reason); uint32_t target_idx = callback(ids, &rejection_reason); - if (target_idx == (uint32_t)-1) { + if (target_idx == UINT32_MAX) { jl_error(jl_string_ptr(rejection_reason)); } JL_GC_POP(); @@ -856,7 +856,7 @@ static inline void check_cmdline(T &&cmdline, bool imaging) } struct SysimgMatch { - uint32_t best_idx{(uint32_t)-1}; + uint32_t best_idx{UINT32_MAX}; int vreg_size{0}; }; @@ -911,7 +911,7 @@ static inline SysimgMatch match_sysimg_targets(S &&sysimg, T &&target, F &&max_v feature_size = new_feature_size; rejection_reasons.push_back("Updating best match to this target\n"); } - if (match.best_idx == (uint32_t)-1) { + if (match.best_idx == UINT32_MAX) { // Construct a nice error message for debugging purposes std::string error_msg = "Unable to find compatible target in cached code image.\n"; for (size_t i = 0; i < rejection_reasons.size(); i++) { diff --git a/src/processor_arm.cpp b/src/processor_arm.cpp index ba2ad225c0e60..0d9009afabec7 100644 --- a/src/processor_arm.cpp +++ b/src/processor_arm.cpp @@ -207,7 +207,7 @@ static constexpr auto feature_masks = get_feature_masks( #undef JL_FEATURE_DEF -1); static const auto real_feature_masks = - feature_masks & FeatureList{{(uint32_t)-1, (uint32_t)-1, 0}}; + feature_masks & FeatureList{{UINT32_MAX, UINT32_MAX, 0}}; namespace Feature { enum : uint32_t { @@ -473,7 +473,7 @@ static constexpr auto feature_masks = get_feature_masks( #undef JL_FEATURE_DEF -1); static const auto real_feature_masks = - feature_masks & FeatureList{{(uint32_t)-1, (uint32_t)-1, 0}}; + feature_masks & FeatureList{{UINT32_MAX, UINT32_MAX, 0}}; namespace Feature { enum : uint32_t { @@ -1522,7 +1522,7 @@ static const llvm::SmallVector, 0> &get_cmdline_targets(v } #endif auto fbit = find_feature_bit(feature_names, nfeature_names, str, len); - if (fbit == (uint32_t)-1) + if (fbit == UINT32_MAX) return false; set_bit(list, fbit, true); return true; @@ -1603,7 +1603,7 @@ static uint32_t sysimg_init_cb(const void *id, jl_value_t **rejection_reason) } } auto match = match_sysimg_targets(sysimg, target, max_vector_size, rejection_reason); - if (match.best_idx == -1) + if (match.best_idx == UINT32_MAX) return match.best_idx; // Now we've decided on which sysimg version to use. // Make sure the JIT target is compatible with it and save the JIT target. @@ -1865,7 +1865,7 @@ JL_DLLEXPORT jl_value_t* jl_check_pkgimage_clones(char *data) JL_GC_PUSH1(&rejection_reason); uint32_t match_idx = pkgimg_init_cb(data, &rejection_reason); JL_GC_POP(); - if (match_idx == (uint32_t)-1) + if (match_idx == UINT32_MAX) return rejection_reason; return jl_nothing; } diff --git a/src/processor_fallback.cpp b/src/processor_fallback.cpp index bb39b15018ad5..f8d9eb9fd9e73 100644 --- a/src/processor_fallback.cpp +++ b/src/processor_fallback.cpp @@ -194,7 +194,7 @@ JL_DLLEXPORT jl_value_t* jl_check_pkgimage_clones(char *data) JL_GC_PUSH1(&rejection_reason); uint32_t match_idx = pkgimg_init_cb(data, &rejection_reason); JL_GC_POP(); - if (match_idx == (uint32_t)-1) + if (match_idx == UINT32_MAX) return rejection_reason; return jl_nothing; } diff --git a/src/processor_x86.cpp b/src/processor_x86.cpp index 5d832c137337e..db954680289ea 100644 --- a/src/processor_x86.cpp +++ b/src/processor_x86.cpp @@ -786,7 +786,7 @@ static const llvm::SmallVector, 0> &get_cmdline_targets(v { auto feature_cb = [] (const char *str, size_t len, FeatureList &list) { auto fbit = find_feature_bit(feature_names, nfeature_names, str, len); - if (fbit == (uint32_t)-1) + if (fbit == UINT32_MAX) return false; set_bit(list, fbit, true); return true; @@ -880,7 +880,7 @@ static uint32_t sysimg_init_cb(const void *id, jl_value_t** rejection_reason) "https://docs.julialang.org/en/v1/devdocs/sysimg/ for more."); } auto match = match_sysimg_targets(sysimg, target, max_vector_size, rejection_reason); - if (match.best_idx == (uint32_t)-1) + if (match.best_idx == UINT32_MAX) return match.best_idx; // Now we've decided on which sysimg version to use. // Make sure the JIT target is compatible with it and save the JIT target. @@ -1060,7 +1060,7 @@ JL_DLLEXPORT jl_value_t* jl_check_pkgimage_clones(char *data) JL_GC_PUSH1(&rejection_reason); uint32_t match_idx = pkgimg_init_cb(data, &rejection_reason); JL_GC_POP(); - if (match_idx == (uint32_t)-1) + if (match_idx == UINT32_MAX) return rejection_reason; return jl_nothing; } diff --git a/src/smallintset.c b/src/smallintset.c index df67239f79fb5..a80a18009c9db 100644 --- a/src/smallintset.c +++ b/src/smallintset.c @@ -36,7 +36,7 @@ static inline size_t jl_intref(const jl_genericmemory_t *arr, size_t idx) JL_NOT else if (el == jl_memory_uint16_type) return ignore_tombstone(jl_atomic_load_relaxed(&((_Atomic(uint16_t)*)arr->ptr)[idx]), (uint16_t)-1); else if (el == jl_memory_uint32_type) - return ignore_tombstone(jl_atomic_load_relaxed(&((_Atomic(uint32_t)*)arr->ptr)[idx]), (uint32_t)-1); + return ignore_tombstone(jl_atomic_load_relaxed(&((_Atomic(uint32_t)*)arr->ptr)[idx]), UINT32_MAX); else abort(); } @@ -53,7 +53,7 @@ static inline size_t jl_intref_acquire(const jl_genericmemory_t *arr, size_t idx else if (el == jl_memory_uint16_type) return acquire_tombstone(jl_atomic_load_acquire(&((_Atomic(uint16_t)*)arr->ptr)[idx]), (uint16_t)-1); else if (el == jl_memory_uint32_type) - return acquire_tombstone(jl_atomic_load_acquire(&((_Atomic(uint32_t)*)arr->ptr)[idx]), (uint32_t)-1); + return acquire_tombstone(jl_atomic_load_acquire(&((_Atomic(uint32_t)*)arr->ptr)[idx]), UINT32_MAX); else abort(); } diff --git a/src/support/utf8.h b/src/support/utf8.h index f1c886e764064..eab86f602ee61 100644 --- a/src/support/utf8.h +++ b/src/support/utf8.h @@ -12,7 +12,7 @@ extern "C" { /* is c the start of a utf8 sequence? */ #define isutf(c) (((c)&0xC0)!=0x80) -#define UEOF ((uint32_t)-1) +#define UEOF (UINT32_MAX) /* convert UTF-8 data to wide character */ size_t u8_toucs(uint32_t *dest, size_t sz, const char *src, size_t srcsz); From cf965f3a6f52ed84c71e4f95010bbbe422741d8e Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Wed, 16 Oct 2024 13:46:22 +0200 Subject: [PATCH 58/76] Remove zero arg methods of `+` and `*` from linalg tests (#56184) There are tests elsewhere that i) make sure there is no zero-arg methods of these functions and ii) tests that e.g. `+()` throws a `MethodError`. Without this patch there are test errors whenever the same test process runs both of these tests. (cherry picked from commit f5937b432c51f6b8a5b28eaa5c1583f1350e12a3) --- stdlib/LinearAlgebra/test/matmul.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/test/matmul.jl b/stdlib/LinearAlgebra/test/matmul.jl index aab535cbe0303..4743c15803887 100644 --- a/stdlib/LinearAlgebra/test/matmul.jl +++ b/stdlib/LinearAlgebra/test/matmul.jl @@ -1116,8 +1116,8 @@ end Base.zero(::Thing) = Thing(0.) Base.one(::Type{Thing}) = Thing(1.) Base.one(::Thing) = Thing(1.) - Base.:+(t::Thing...) = +(getfield.(t, :data)...) - Base.:*(t::Thing...) = *(getfield.(t, :data)...) + Base.:+(t1::Thing, t::Thing...) = +(getfield.((t1, t...), :data)...) + Base.:*(t1::Thing, t::Thing...) = *(getfield.((t1, t...), :data)...) M = Float64[1 2; 3 4] A = Thing.(M) From 29e0a3e45322f9fe13fd88a5945debe0ce66ee9f Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Tue, 16 Jul 2024 06:35:52 -0500 Subject: [PATCH 59/76] Replace some occurrences of iteration over 1:length with more idiomatic structures (mostly eachindex) (#55137) Base should be a model for the ecosystem, and `eachindex(x)` is better than `1:length(x)` in almost all cases. I've updated many, but certainly not all examples. This is mostly a NFC, but also fixes #55136. (cherry picked from commit 0945b9d7740855c82a09fed42fbf6bc561e02c77) --- base/abstractarray.jl | 8 ++++---- base/errorshow.jl | 10 +++++----- base/expr.jl | 2 +- base/intfuncs.jl | 2 +- base/loading.jl | 9 ++++----- base/multidimensional.jl | 2 +- base/reflection.jl | 2 +- base/show.jl | 10 +++++----- base/tuple.jl | 6 +++--- 9 files changed, 25 insertions(+), 26 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index bb4aff0f6a411..45fff8dae0d24 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1634,10 +1634,10 @@ typed_vcat(::Type{T}) where {T} = Vector{T}() typed_hcat(::Type{T}) where {T} = Vector{T}() ## cat: special cases -vcat(X::T...) where {T} = T[ X[i] for i=1:length(X) ] -vcat(X::T...) where {T<:Number} = T[ X[i] for i=1:length(X) ] -hcat(X::T...) where {T} = T[ X[j] for i=1:1, j=1:length(X) ] -hcat(X::T...) where {T<:Number} = T[ X[j] for i=1:1, j=1:length(X) ] +vcat(X::T...) where {T} = T[ X[i] for i=eachindex(X) ] +vcat(X::T...) where {T<:Number} = T[ X[i] for i=eachindex(X) ] +hcat(X::T...) where {T} = T[ X[j] for i=1:1, j=eachindex(X) ] +hcat(X::T...) where {T<:Number} = T[ X[j] for i=1:1, j=eachindex(X) ] vcat(X::Number...) = hvcat_fill!(Vector{promote_typeof(X...)}(undef, length(X)), X) hcat(X::Number...) = hvcat_fill!(Matrix{promote_typeof(X...)}(undef, 1,length(X)), X) diff --git a/base/errorshow.jl b/base/errorshow.jl index f63c150336689..b9a9a473cce96 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -196,7 +196,7 @@ function showerror(io::IO, ex::CanonicalIndexError) print(io, "CanonicalIndexError: ", ex.func, " not defined for ", ex.type) end -typesof(@nospecialize args...) = Tuple{Any[ Core.Typeof(args[i]) for i in 1:length(args) ]...} +typesof(@nospecialize args...) = Tuple{Any[Core.Typeof(arg) for arg in args]...} function print_with_compare(io::IO, @nospecialize(a::DataType), @nospecialize(b::DataType), color::Symbol) if a.name === b.name @@ -273,7 +273,7 @@ function showerror(io::IO, ex::MethodError) arg_types_param = arg_types_param[3:end] san_arg_types_param = san_arg_types_param[3:end] keys = kwt.parameters[1]::Tuple - kwargs = Any[(keys[i], fieldtype(kwt, i)) for i in 1:length(keys)] + kwargs = Any[(keys[i], fieldtype(kwt, i)) for i in eachindex(keys)] arg_types = rewrap_unionall(Tuple{arg_types_param...}, arg_types) end if f === Base.convert && length(arg_types_param) == 2 && !is_arg_types @@ -687,7 +687,7 @@ function show_reduced_backtrace(io::IO, t::Vector) push!(repeated_cycle, (0,0,0)) # repeated_cycle is never empty frame_counter = 1 - for i in 1:length(displayed_stackframes) + for i in eachindex(displayed_stackframes) (frame, n) = displayed_stackframes[i] print_stackframe(io, frame_counter, frame, n, ndigits_max, STACKTRACE_FIXEDCOLORS, STACKTRACE_MODULECOLORS) @@ -864,7 +864,7 @@ end function _collapse_repeated_frames(trace) kept_frames = trues(length(trace)) last_frame = nothing - for i in 1:length(trace) + for i in eachindex(trace) frame::StackFrame, _ = trace[i] if last_frame !== nothing && frame.file == last_frame.file && frame.line == last_frame.line #= @@ -909,7 +909,7 @@ function _collapse_repeated_frames(trace) end if length(last_params) > length(params) issame = true - for i = 1:length(params) + for i = eachindex(params) issame &= params[i] == last_params[i] end if issame diff --git a/base/expr.jl b/base/expr.jl index c4ed916fe34aa..58435135c99ae 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -68,7 +68,7 @@ function copy_exprs(@nospecialize(x)) end return x end -copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(@inbounds x[i]) for i in 1:length(x)] +copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(@inbounds x[i]) for i in eachindex(x)] @eval exprarray(head::Symbol, arg::Array{Any,1}) = $(Expr(:new, :Expr, :head, :arg)) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 74b450990bc12..f2de94db5589e 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -976,7 +976,7 @@ end Return an array with element type `T` (default `Int`) of the digits of `n` in the given base, optionally padded with zeros to a specified size. More significant digits are at -higher indices, such that `n == sum(digits[k]*base^(k-1) for k=1:length(digits))`. +higher indices, such that `n == sum(digits[k]*base^(k-1) for k in eachindex(digits))`. See also [`ndigits`](@ref), [`digits!`](@ref), and for base 2 also [`bitstring`](@ref), [`count_ones`](@ref). diff --git a/base/loading.jl b/base/loading.jl index 558eb76d51e96..7f24ae48a3e33 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1228,7 +1228,7 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No t_comp_before = cumulative_compile_time_ns() end - for i in 1:length(depmods) + for i in eachindex(depmods) dep = depmods[i] dep isa Module && continue _, depkey, depbuild_id = dep::Tuple{String, PkgId, UInt128} @@ -1781,8 +1781,7 @@ function compilecache_path(pkg::PkgId; end staledeps, _, _ = staledeps::Tuple{Vector{Any}, Union{Nothing, String}, UInt128} # finish checking staledeps module graph - for i in 1:length(staledeps) - dep = staledeps[i] + for dep in staledeps dep isa Module && continue modpath, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128} modpaths = find_all_in_cache_path(modkey) @@ -1971,7 +1970,7 @@ end try staledeps, ocachefile, newbuild_id = staledeps::Tuple{Vector{Any}, Union{Nothing, String}, UInt128} # finish checking staledeps module graph - for i in 1:length(staledeps) + for i in eachindex(staledeps) dep = staledeps[i] dep isa Module && continue modpath, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128} @@ -2003,7 +2002,7 @@ end end # finish loading module graph into staledeps # TODO: call all start_loading calls (in reverse order) before calling any _include_from_serialized, since start_loading will drop the loading lock - for i in 1:length(staledeps) + for i in eachindex(staledeps) dep = staledeps[i] dep isa Module && continue modpath, modkey, modbuild_id, modcachepath, modstaledeps, modocachepath = dep::Tuple{String, PkgId, UInt128, String, Vector{Any}, Union{Nothing, String}} diff --git a/base/multidimensional.jl b/base/multidimensional.jl index b6c93fa318d78..730a165cde18f 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -1639,7 +1639,7 @@ function checkdims_perm(P::AbstractArray{TP,N}, B::AbstractArray{TB,N}, perm) wh length(perm) == N || throw(ArgumentError("expected permutation of size $N, but length(perm)=$(length(perm))")) isperm(perm) || throw(ArgumentError("input is not a permutation")) indsP = axes(P) - for i = 1:length(perm) + for i in eachindex(perm) indsP[i] == indsB[perm[i]] || throw(DimensionMismatch("destination tensor of incorrect size")) end nothing diff --git a/base/reflection.jl b/base/reflection.jl index fd3e7931b2ce8..d70683feaaae7 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -2367,7 +2367,7 @@ function hasmethod(f, t, kwnames::Tuple{Vararg{Symbol}}; world::UInt=get_world_c for kw in kws endswith(String(kw), "...") && return true end - kwnames = Symbol[kwnames[i] for i in 1:length(kwnames)] + kwnames = collect(kwnames) return issubset(kwnames, kws) end diff --git a/base/show.jl b/base/show.jl index 3bec8a5c885a2..66628d60f21bf 100644 --- a/base/show.jl +++ b/base/show.jl @@ -676,7 +676,7 @@ function show_can_elide(p::TypeVar, wheres::Vector, elide::Int, env::SimpleVecto has_typevar(v.lb, p) && return false has_typevar(v.ub, p) && return false end - for i = 1:length(env) + for i = eachindex(env) i == skip && continue has_typevar(env[i], p) && return false end @@ -1183,7 +1183,7 @@ end function show_at_namedtuple(io::IO, syms::Tuple, types::DataType) first = true - for i in 1:length(syms) + for i in eachindex(syms) if !first print(io, ", ") end @@ -2372,7 +2372,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In if get(io, beginsym, false) print(io, '(') ind = indent + indent_width - for i = 1:length(ex.args) + for i = eachindex(ex.args) if i > 1 # if there was only a comment before the first semicolon, the expression would get parsed as a NamedTuple if !(i == 2 && ex.args[1] isa LineNumberNode) @@ -2890,7 +2890,7 @@ function dump(io::IOContext, x::SimpleVector, n::Int, indent) end print(io, "SimpleVector") if n > 0 - for i = 1:length(x) + for i in eachindex(x) println(io) print(io, indent, " ", i, ": ") if isassigned(x,i) @@ -3000,7 +3000,7 @@ function dump(io::IOContext, x::DataType, n::Int, indent) end fields = fieldnames(x) fieldtypes = datatype_fieldtypes(x) - for idx in 1:length(fields) + for idx in eachindex(fields) println(io) print(io, indent, " ", fields[idx]) if isassigned(fieldtypes, idx) diff --git a/base/tuple.jl b/base/tuple.jl index 5ab5b4b1c7a26..4d0791e3303d6 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -502,7 +502,7 @@ end _findfirst_rec(f, i::Int, ::Tuple{}) = nothing _findfirst_rec(f, i::Int, t::Tuple) = (@inline; f(first(t)) ? i : _findfirst_rec(f, i+1, tail(t))) function _findfirst_loop(f::Function, t) - for i in 1:length(t) + for i in eachindex(t) f(t[i]) && return i end return nothing @@ -536,7 +536,7 @@ function _isequal(t1::Tuple{Any,Vararg{Any}}, t2::Tuple{Any,Vararg{Any}}) return isequal(t1[1], t2[1]) && _isequal(tail(t1), tail(t2)) end function _isequal(t1::Any32, t2::Any32) - for i = 1:length(t1) + for i in eachindex(t1, t2) if !isequal(t1[i], t2[i]) return false end @@ -567,7 +567,7 @@ function _eq_missing(t1::Tuple, t2::Tuple) end function _eq(t1::Any32, t2::Any32) anymissing = false - for i = 1:length(t1) + for i in eachindex(t1, t2) eq = (t1[i] == t2[i]) if ismissing(eq) anymissing = true From a25475cb093b3a686905ae7227319332df09b567 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 4 Oct 2024 12:03:53 -0400 Subject: [PATCH 60/76] add logic to prefer loading modules that are already loaded (#55908) Iterate over the list of existing loaded modules for PkgId whenever loading a new module for PkgId, so that we will use that existing build_id content if it otherwise passes the other stale_checks. (cherry picked from commit 7e2d8035990e67810ccbbc9fb5da810b1b83b774) --- base/Base.jl | 2 +- base/loading.jl | 184 +++++++++++++++++++++++++++--------------------- test/loading.jl | 36 +++++++++- 3 files changed, 138 insertions(+), 84 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 881e9f258dd5d..8e6a3ec17bdaf 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -605,7 +605,7 @@ function __init__() empty!(explicit_loaded_modules) empty!(loaded_precompiles) # If we load a packageimage when building the image this might not be empty for (mod, key) in module_keys - loaded_precompiles[key => module_build_id(mod)] = mod + push!(get!(Vector{Module}, loaded_precompiles, key), mod) end if haskey(ENV, "JULIA_MAX_NUM_PRECOMPILE_FILES") MAX_NUM_PRECOMPILE_FILES[] = parse(Int, ENV["JULIA_MAX_NUM_PRECOMPILE_FILES"]) diff --git a/base/loading.jl b/base/loading.jl index 7f24ae48a3e33..cbbd74e86f211 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1232,7 +1232,7 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No dep = depmods[i] dep isa Module && continue _, depkey, depbuild_id = dep::Tuple{String, PkgId, UInt128} - dep = loaded_precompiles[depkey => depbuild_id] + dep = something(maybe_loaded_precompile(depkey, depbuild_id)) @assert PkgId(dep) == depkey && module_build_id(dep) === depbuild_id depmods[i] = dep end @@ -1338,6 +1338,7 @@ end function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String) # This function is also used by PkgCacheInspector.jl + assert_havelock(require_lock) restored = sv[1]::Vector{Any} for M in restored M = M::Module @@ -1346,7 +1347,7 @@ function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String) end if parentmodule(M) === M push!(loaded_modules_order, M) - loaded_precompiles[pkg => module_build_id(M)] = M + push!(get!(Vector{Module}, loaded_precompiles, pkg), M) end end @@ -1962,90 +1963,102 @@ end assert_havelock(require_lock) paths = find_all_in_cache_path(pkg, DEPOT_PATH) newdeps = PkgId[] - for path_to_try in paths::Vector{String} - staledeps = stale_cachefile(pkg, build_id, sourcepath, path_to_try; reasons, stalecheck) - if staledeps === true - continue - end - try - staledeps, ocachefile, newbuild_id = staledeps::Tuple{Vector{Any}, Union{Nothing, String}, UInt128} - # finish checking staledeps module graph - for i in eachindex(staledeps) - dep = staledeps[i] - dep isa Module && continue - modpath, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128} - modpaths = find_all_in_cache_path(modkey, DEPOT_PATH) - for modpath_to_try in modpaths - modstaledeps = stale_cachefile(modkey, modbuild_id, modpath, modpath_to_try; stalecheck) - if modstaledeps === true - continue - end - modstaledeps, modocachepath, _ = modstaledeps::Tuple{Vector{Any}, Union{Nothing, String}, UInt128} - staledeps[i] = (modpath, modkey, modbuild_id, modpath_to_try, modstaledeps, modocachepath) - @goto check_next_dep + try_build_ids = UInt128[build_id] + if build_id == UInt128(0) + let loaded = get(loaded_precompiles, pkg, nothing) + if loaded !== nothing + for mod in loaded # try these in reverse original load order to see if one is already valid + pushfirst!(try_build_ids, module_build_id(mod)) end - @debug "Rejecting cache file $path_to_try because required dependency $modkey with build ID $(UUID(modbuild_id)) is missing from the cache." - @goto check_next_path - @label check_next_dep end - M = get(loaded_precompiles, pkg => newbuild_id, nothing) - if isa(M, Module) - stalecheck && register_root_module(M) - return M - end - if stalecheck - try - touch(path_to_try) # update timestamp of precompilation file - catch ex # file might be read-only and then we fail to update timestamp, which is fine - ex isa IOError || rethrow() - end + end + end + for build_id in try_build_ids + for path_to_try in paths::Vector{String} + staledeps = stale_cachefile(pkg, build_id, sourcepath, path_to_try; reasons, stalecheck) + if staledeps === true + continue end - # finish loading module graph into staledeps - # TODO: call all start_loading calls (in reverse order) before calling any _include_from_serialized, since start_loading will drop the loading lock - for i in eachindex(staledeps) - dep = staledeps[i] - dep isa Module && continue - modpath, modkey, modbuild_id, modcachepath, modstaledeps, modocachepath = dep::Tuple{String, PkgId, UInt128, String, Vector{Any}, Union{Nothing, String}} - dep = start_loading(modkey, modbuild_id, stalecheck) - while true - if dep isa Module - if PkgId(dep) == modkey && module_build_id(dep) === modbuild_id - break - else - @debug "Rejecting cache file $path_to_try because module $modkey got loaded at a different version than expected." - @goto check_next_path + try + staledeps, ocachefile, newbuild_id = staledeps::Tuple{Vector{Any}, Union{Nothing, String}, UInt128} + # finish checking staledeps module graph + for i in eachindex(staledeps) + dep = staledeps[i] + dep isa Module && continue + modpath, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128} + modpaths = find_all_in_cache_path(modkey, DEPOT_PATH) + for modpath_to_try in modpaths + modstaledeps = stale_cachefile(modkey, modbuild_id, modpath, modpath_to_try; stalecheck) + if modstaledeps === true + continue end + modstaledeps, modocachepath, _ = modstaledeps::Tuple{Vector{Any}, Union{Nothing, String}, UInt128} + staledeps[i] = (modpath, modkey, modbuild_id, modpath_to_try, modstaledeps, modocachepath) + @goto check_next_dep end - if dep === nothing - try - set_pkgorigin_version_path(modkey, modpath) - dep = _include_from_serialized(modkey, modcachepath, modocachepath, modstaledeps; register = stalecheck) - finally - end_loading(modkey, dep) + @debug "Rejecting cache file $path_to_try because required dependency $modkey with build ID $(UUID(modbuild_id)) is missing from the cache." + @goto check_next_path + @label check_next_dep + end + M = maybe_loaded_precompile(pkg, newbuild_id) + if isa(M, Module) + stalecheck && register_root_module(M) + return M + end + if stalecheck + try + touch(path_to_try) # update timestamp of precompilation file + catch ex # file might be read-only and then we fail to update timestamp, which is fine + ex isa IOError || rethrow() + end + end + # finish loading module graph into staledeps + # TODO: call all start_loading calls (in reverse order) before calling any _include_from_serialized, since start_loading will drop the loading lock + for i in eachindex(staledeps) + dep = staledeps[i] + dep isa Module && continue + modpath, modkey, modbuild_id, modcachepath, modstaledeps, modocachepath = dep::Tuple{String, PkgId, UInt128, String, Vector{Any}, Union{Nothing, String}} + dep = start_loading(modkey, modbuild_id, stalecheck) + while true + if dep isa Module + if PkgId(dep) == modkey && module_build_id(dep) === modbuild_id + break + else + @debug "Rejecting cache file $path_to_try because module $modkey got loaded at a different version than expected." + @goto check_next_path + end end - if !isa(dep, Module) - @debug "Rejecting cache file $path_to_try because required dependency $modkey failed to load from cache file for $modcachepath." exception=dep - @goto check_next_path - else - push!(newdeps, modkey) + if dep === nothing + try + set_pkgorigin_version_path(modkey, modpath) + dep = _include_from_serialized(modkey, modcachepath, modocachepath, modstaledeps; register = stalecheck) + finally + end_loading(modkey, dep) + end + if !isa(dep, Module) + @debug "Rejecting cache file $path_to_try because required dependency $modkey failed to load from cache file for $modcachepath." exception=dep + @goto check_next_path + else + push!(newdeps, modkey) + end end end + staledeps[i] = dep end - staledeps[i] = dep - end - restored = get(loaded_precompiles, pkg => newbuild_id, nothing) - if !isa(restored, Module) - restored = _include_from_serialized(pkg, path_to_try, ocachefile, staledeps; register = stalecheck) - end - isa(restored, Module) && return restored - @debug "Deserialization checks failed while attempting to load cache from $path_to_try" exception=restored - @label check_next_path - finally - for modkey in newdeps - insert_extension_triggers(modkey) - stalecheck && run_package_callbacks(modkey) + restored = maybe_loaded_precompile(pkg, newbuild_id) + if !isa(restored, Module) + restored = _include_from_serialized(pkg, path_to_try, ocachefile, staledeps; register = stalecheck) + end + isa(restored, Module) && return restored + @debug "Deserialization checks failed while attempting to load cache from $path_to_try" exception=restored + @label check_next_path + finally + for modkey in newdeps + insert_extension_triggers(modkey) + stalecheck && run_package_callbacks(modkey) + end + empty!(newdeps) end - empty!(newdeps) end end return nothing @@ -2065,7 +2078,7 @@ function start_loading(modkey::PkgId, build_id::UInt128, stalecheck::Bool) loaded = stalecheck ? maybe_root_module(modkey) : nothing loaded isa Module && return loaded if build_id != UInt128(0) - loaded = get(loaded_precompiles, modkey => build_id, nothing) + loaded = maybe_loaded_precompile(modkey, build_id) loaded isa Module && return loaded end loading = get(package_locks, modkey, nothing) @@ -2394,13 +2407,22 @@ const pkgorigins = Dict{PkgId,PkgOrigin}() const explicit_loaded_modules = Dict{PkgId,Module}() # Emptied on Julia start const loaded_modules = Dict{PkgId,Module}() # available to be explicitly loaded -const loaded_precompiles = Dict{Pair{PkgId,UInt128},Module}() # extended (complete) list of modules, available to be loaded +const loaded_precompiles = Dict{PkgId,Vector{Module}}() # extended (complete) list of modules, available to be loaded const loaded_modules_order = Vector{Module}() const module_keys = IdDict{Module,PkgId}() # the reverse of loaded_modules is_root_module(m::Module) = @lock require_lock haskey(module_keys, m) root_module_key(m::Module) = @lock require_lock module_keys[m] +function maybe_loaded_precompile(key::PkgId, buildid::UInt128) + assert_havelock(require_lock) + mods = get(loaded_precompiles, key, nothing) + mods === nothing && return + for mod in mods + module_build_id(mod) == buildid && return mod + end +end + function module_build_id(m::Module) hi, lo = ccall(:jl_module_build_id, NTuple{2,UInt64}, (Any,), m) return (UInt128(hi) << 64) | lo @@ -2421,7 +2443,7 @@ end end end end - haskey(loaded_precompiles, key => module_build_id(m)) || push!(loaded_modules_order, m) + maybe_loaded_precompile(key, module_build_id(m)) === nothing && push!(loaded_modules_order, m) loaded_modules[key] = m explicit_loaded_modules[key] = m module_keys[m] = key @@ -3785,8 +3807,8 @@ end for i in 1:ndeps req_key, req_build_id = required_modules[i] # Check if module is already loaded - if !stalecheck && haskey(loaded_precompiles, req_key => req_build_id) - M = loaded_precompiles[req_key => req_build_id] + M = stalecheck ? nothing : maybe_loaded_precompile(req_key, req_build_id) + if M !== nothing @assert PkgId(M) == req_key && module_build_id(M) === req_build_id depmods[i] = M elseif root_module_exists(req_key) diff --git a/test/loading.jl b/test/loading.jl index 8f0c4897e24eb..9cfd421a596cb 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1,10 +1,10 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -original_depot_path = copy(Base.DEPOT_PATH) - using Test # Tests for @__LINE__ inside and outside of macros +# NOTE: the __LINE__ numbers for these first couple tests are significant, so +# adding any lines here will make those tests fail @test (@__LINE__) == 8 macro macro_caller_lineno() @@ -33,6 +33,9 @@ end @test @nested_LINE_expansion() == ((@__LINE__() - 4, @__LINE__() - 12), @__LINE__()) @test @nested_LINE_expansion2() == ((@__LINE__() - 5, @__LINE__() - 9), @__LINE__()) +original_depot_path = copy(Base.DEPOT_PATH) +include("precompile_utils.jl") + loaded_files = String[] push!(Base.include_callbacks, (mod::Module, fn::String) -> push!(loaded_files, fn)) include("test_sourcepath.jl") @@ -1590,3 +1593,32 @@ end copy!(LOAD_PATH, old_load_path) end end + +@testset "require_stdlib loading duplication" begin + depot_path = mktempdir() + oldBase64 = nothing + try + push!(empty!(DEPOT_PATH), depot_path) + Base64_key = Base.PkgId(Base.UUID("2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"), "Base64") + oldBase64 = Base.unreference_module(Base64_key) + cc = Base.compilecache(Base64_key) + @test Base.isprecompiled(Base64_key, cachepaths=String[cc[1]]) + empty!(DEPOT_PATH) + Base.require_stdlib(Base64_key) + push!(DEPOT_PATH, depot_path) + append!(DEPOT_PATH, original_depot_path) + oldloaded = @lock(Base.require_lock, length(get(Base.loaded_precompiles, Base64_key, Module[]))) + Base.require(Base64_key) + @test @lock(Base.require_lock, length(get(Base.loaded_precompiles, Base64_key, Module[]))) == oldloaded + Base.unreference_module(Base64_key) + empty!(DEPOT_PATH) + push!(DEPOT_PATH, depot_path) + Base.require(Base64_key) + @test @lock(Base.require_lock, length(get(Base.loaded_precompiles, Base64_key, Module[]))) == oldloaded + 1 + Base.unreference_module(Base64_key) + finally + oldBase64 === nothing || Base.register_root_module(oldBase64) + copy!(DEPOT_PATH, original_depot_path) + rm(depot_path, force=true, recursive=true) + end +end From fac5efc723d120bb81000565dad82236a9070097 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 29 Oct 2024 15:42:48 +0100 Subject: [PATCH 61/76] fix a forgotten rename `readuntil` -> `copyuntil` (#56380) Fixes https://github.com/JuliaLang/julia/issues/56352, with the repro in that issue: ``` Master: 1.114874 seconds (13.01 M allocations: 539.592 MiB, 3.80% gc time) After: 0.369492 seconds (12.99 M allocations: 485.031 MiB, 10.73% gc time) 1.10: 0.341114 seconds (8.36 M allocations: 454.242 MiB, 2.69% gc time) ``` (cherry picked from commit 07530bcb23d1a576b8260500833b767b81442517) --- base/stream.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/stream.jl b/base/stream.jl index 712c7c7c30001..a31469af27e53 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -1027,7 +1027,7 @@ function readavailable(this::LibuvStream) return bytes end -function readuntil(x::LibuvStream, c::UInt8; keep::Bool=false) +function copyuntil(out::IO, x::LibuvStream, c::UInt8; keep::Bool=false) iolock_begin() buf = x.buffer @assert buf.seekable == false @@ -1057,9 +1057,9 @@ function readuntil(x::LibuvStream, c::UInt8; keep::Bool=false) end end end - bytes = readuntil(buf, c, keep=keep) + copyuntil(out, buf, c; keep) iolock_end() - return bytes + return out end uv_write(s::LibuvStream, p::Vector{UInt8}) = GC.@preserve p uv_write(s, pointer(p), UInt(sizeof(p))) From b3c3460ddb5ccffe612cd3a5bc2849bf6fb6338d Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 29 Oct 2024 13:18:01 -0400 Subject: [PATCH 62/76] precompile: fix performance issues with IO (#56370) The string API here rapidly becomes unusably slow if dumping much debug output during precompile. Fix the design here to use an intermediate IO instead to prevent that. (cherry picked from commit e4dc9d357a1a46ac4078ae81265f6edd9b593bdf) --- base/precompilation.jl | 51 +++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index c6fabf9b71a54..7f93b73b93060 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -633,7 +633,7 @@ function _precompilepkgs(pkgs::Vector{String}, return false end end - std_outputs = Dict{PkgConfig,String}() + std_outputs = Dict{PkgConfig,IOBuffer}() taskwaiting = Set{PkgConfig}() pkgspidlocked = Dict{PkgConfig,String}() pkg_liveprinted = nothing @@ -655,7 +655,7 @@ function _precompilepkgs(pkgs::Vector{String}, print(io, ansi_cleartoendofline, str) end end - std_outputs[pkg_config] = string(get(std_outputs, pkg_config, ""), str) + write(get!(IOBuffer, std_outputs, pkg_config), str) if !in(pkg_config, taskwaiting) && occursin("waiting for IO to finish", str) !fancyprint && lock(print_lock) do println(io, pkg.name, color_string(" Waiting for background task / IO / timer.", Base.warn_color())) @@ -850,8 +850,9 @@ function _precompilepkgs(pkgs::Vector{String}, close(std_pipe.in) # close pipe to end the std output monitor wait(t_monitor) if err isa ErrorException || (err isa ArgumentError && startswith(err.msg, "Invalid header in cache file")) - failed_deps[pkg_config] = (strict || is_direct_dep) ? string(sprint(showerror, err), "\n", strip(get(std_outputs, pkg_config, ""))) : "" + errmsg = String(take!(get(IOBuffer, std_outputs, pkg_config))) delete!(std_outputs, pkg_config) # so it's not shown as warnings, given error report + failed_deps[pkg_config] = (strict || is_direct_dep) ? string(sprint(showerror, err), "\n", strip(errmsg)) : "" !fancyprint && lock(print_lock) do println(io, " "^9, color_string(" ✗ ", Base.error_color()), name) end @@ -924,20 +925,22 @@ function _precompilepkgs(pkgs::Vector{String}, end # show any stderr output, even if Pkg.precompile has been interrupted (quick_exit=true), given user may be # interrupting a hanging precompile job with stderr output. julia#48371 - filter!(kv -> !isempty(strip(last(kv))), std_outputs) # remove empty output - if !isempty(std_outputs) - plural1 = length(std_outputs) == 1 ? "y" : "ies" - plural2 = length(std_outputs) == 1 ? "" : "s" - print(iostr, "\n ", color_string("$(length(std_outputs))", Base.warn_color()), " dependenc$(plural1) had output during precompilation:") - for (pkg_config, err) in std_outputs - pkg, config = pkg_config - err = if pkg == pkg_liveprinted - "[Output was shown above]" - else - join(split(strip(err), "\n"), color_string("\n│ ", Base.warn_color())) + let std_outputs = Tuple{PkgConfig,SubString{String}}[(pkg_config, strip(String(take!(io)))) for (pkg_config,io) in std_outputs] + filter!(kv -> !isempty(last(kv)), std_outputs) + if !isempty(std_outputs) + plural1 = length(std_outputs) == 1 ? "y" : "ies" + plural2 = length(std_outputs) == 1 ? "" : "s" + print(iostr, "\n ", color_string("$(length(std_outputs))", Base.warn_color()), " dependenc$(plural1) had output during precompilation:") + for (pkg_config, err) in std_outputs + pkg, config = pkg_config + err = if pkg == pkg_liveprinted + "[Output was shown above]" + else + join(split(err, "\n"), color_string("\n│ ", Base.warn_color())) + end + name = haskey(exts, pkg) ? string(exts[pkg], " → ", pkg.name) : pkg.name + print(iostr, color_string("\n┌ ", Base.warn_color()), name, color_string("\n│ ", Base.warn_color()), err, color_string("\n└ ", Base.warn_color())) end - name = haskey(exts, pkg) ? string(exts[pkg], " → ", pkg.name) : pkg.name - print(iostr, color_string("\n┌ ", Base.warn_color()), name, color_string("\n│ ", Base.warn_color()), err, color_string("\n└ ", Base.warn_color())) end end end @@ -947,20 +950,26 @@ function _precompilepkgs(pkgs::Vector{String}, end end quick_exit && return - err_str = "" + err_str = IOBuffer() n_direct_errs = 0 for (pkg_config, err) in failed_deps dep, config = pkg_config if strict || (dep in direct_deps) - config_str = isempty(config[1]) ? "" : "$(join(config[1], " ")) " - err_str = string(err_str, "\n$(dep.name) $config_str\n\n$err", (n_direct_errs > 0 ? "\n" : "")) + print(err_str, "\n", dep.name, " ") + for cfg in config[1] + print(err_str, cfg, " ") + end + print(err_str, "\n\n", err) + n_direct_errs > 0 && write(err_str, "\n") n_direct_errs += 1 end end - if err_str != "" + if position(err_str) > 0 + skip(err_str, -1) + truncate(err_str, position(err_str)) pluralde = n_direct_errs == 1 ? "y" : "ies" direct = strict ? "" : "direct " - err_msg = "The following $n_direct_errs $(direct)dependenc$(pluralde) failed to precompile:\n$(err_str[1:end-1])" + err_msg = "The following $n_direct_errs $(direct)dependenc$(pluralde) failed to precompile:\n$(String(take!(err_str)))" if internal_call # aka. auto-precompilation if isinteractive() && !get(ENV, "CI", false) plural1 = length(failed_deps) == 1 ? "y" : "ies" From bea9c9ccbf840f8b5970628df41ffebb0cd5f7b5 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 29 Oct 2024 22:02:37 +0100 Subject: [PATCH 63/76] cache the `find_all_in_cache_path` call during parallel precompilation (#56369) Before (in an environment with DifferentialEquations.jl): ```julia julia> @time Pkg.precompile() 0.733576 seconds (3.44 M allocations: 283.676 MiB, 6.24% gc time) julia> isfile_calls[1:10] 10-element Vector{Pair{String, Int64}}: "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/Printf/3FQLY_zHycD.ji" => 178 "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/Printf/3FQLY_xxrt3.ji" => 178 "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/Dates/p8See_xxrt3.ji" => 158 "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/Dates/p8See_zHycD.ji" => 158 "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/TOML/mjrwE_zHycD.ji" => 155 "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/TOML/mjrwE_xxrt3.ji" => 155 "/home/kc/.julia/compiled/v1.12/Preferences/pWSk8_4Qv86.ji" => 152 "/home/kc/.julia/compiled/v1.12/Preferences/pWSk8_juhqb.ji" => 152 "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/StyledStrings/UcVoM_zHycD.ji" => 144 "/home/kc/.julia/juliaup/julia-nightly/share/julia/compiled/v1.12/StyledStrings/UcVoM_xxrt3.ji" => 144 ``` After: ```julia julia> @time Pkg.precompile() 0.460077 seconds (877.59 k allocations: 108.075 MiB, 4.77% gc time) julia> isfile_calls[1:10] 10-element Vector{Pair{String, Int64}}: "/tmp/jl_a5xFWK/Project.toml" => 15 "/tmp/jl_a5xFWK/Manifest.toml" => 7 "/home/kc/.julia/registries/General.toml" => 6 "/home/kc/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/Markdown/src/Markdown.jl" => 3 "/home/kc/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/Serialization/src/Serialization.jl" => 3 "/home/kc/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/Distributed/src/Distributed.jl" => 3 "/home/kc/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/UUIDs/src/UUIDs.jl" => 3 "/home/kc/.julia/juliaup/julia-nightly/share/julia/stdlib/v1.12/LibCURL/src/LibCURL.jl" => 3 ``` Performance is improved and we are not calling `isfile` on a bunch of the same ji files hundreds times. Benchmark is made on a linux machine so performance diff should be a lot better on Windows where these `isfile_casesensitive` call is much more expensive. Fixes https://github.com/JuliaLang/julia/issues/56366 --------- Co-authored-by: KristofferC Co-authored-by: Ian Butterworth (cherry picked from commit 9850a3881221a57a382e98c9b9ae2bf97ac3966d) --- base/loading.jl | 10 ++++++---- base/precompilation.jl | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index cbbd74e86f211..a73715b6ca1a8 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1770,7 +1770,8 @@ const StaleCacheKey = Tuple{Base.PkgId, UInt128, String, String} function compilecache_path(pkg::PkgId; ignore_loaded::Bool=false, stale_cache::Dict{StaleCacheKey,Bool}=Dict{StaleCacheKey, Bool}(), - cachepaths::Vector{String}=Base.find_all_in_cache_path(pkg), + cachepath_cache::Dict{PkgId, Vector{String}}=Dict{PkgId, Vector{String}}(), + cachepaths::Vector{String}=get!(() -> find_all_in_cache_path(pkg), cachepath_cache, pkg), sourcepath::Union{String,Nothing}=Base.locate_package(pkg), flags::CacheFlags=CacheFlags()) path = nothing @@ -1785,7 +1786,7 @@ function compilecache_path(pkg::PkgId; for dep in staledeps dep isa Module && continue modpath, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128} - modpaths = find_all_in_cache_path(modkey) + modpaths = get!(() -> find_all_in_cache_path(modkey), cachepath_cache, modkey) for modpath_to_try in modpaths::Vector{String} stale_cache_key = (modkey, modbuild_id, modpath, modpath_to_try)::StaleCacheKey if get!(() -> stale_cachefile(stale_cache_key...; ignore_loaded, requested_flags=flags) === true, @@ -1827,10 +1828,11 @@ fresh julia session specify `ignore_loaded=true`. function isprecompiled(pkg::PkgId; ignore_loaded::Bool=false, stale_cache::Dict{StaleCacheKey,Bool}=Dict{StaleCacheKey, Bool}(), - cachepaths::Vector{String}=Base.find_all_in_cache_path(pkg), + cachepath_cache::Dict{PkgId, Vector{String}}=Dict{PkgId, Vector{String}}(), + cachepaths::Vector{String}=get!(() -> find_all_in_cache_path(pkg), cachepath_cache, pkg), sourcepath::Union{String,Nothing}=Base.locate_package(pkg), flags::CacheFlags=CacheFlags()) - path = compilecache_path(pkg; ignore_loaded, stale_cache, cachepaths, sourcepath, flags) + path = compilecache_path(pkg; ignore_loaded, stale_cache, cachepath_cache, cachepaths, sourcepath, flags) return !isnothing(path) end diff --git a/base/precompilation.jl b/base/precompilation.jl index 7f93b73b93060..7229a9f79f3e9 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -400,6 +400,8 @@ function _precompilepkgs(pkgs::Vector{String}, color_string(cstr::String, col::Union{Int64, Symbol}) = _color_string(cstr, col, hascolor) stale_cache = Dict{StaleCacheKey, Bool}() + cachepath_cache = Dict{PkgId, Vector{String}}() + exts = Dict{PkgId, String}() # ext -> parent # make a flat map of each dep and its direct deps depsmap = Dict{PkgId, Vector{PkgId}}() @@ -773,7 +775,7 @@ function _precompilepkgs(pkgs::Vector{String}, ## precompilation loop for (pkg, deps) in depsmap - cachepaths = Base.find_all_in_cache_path(pkg) + cachepaths = get!(() -> Base.find_all_in_cache_path(pkg), cachepath_cache, pkg) sourcepath = Base.locate_package(pkg) single_requested_pkg = length(pkgs) == 1 && only(pkgs) == pkg.name for config in configs @@ -796,7 +798,7 @@ function _precompilepkgs(pkgs::Vector{String}, wait(was_processed[(dep,config)]) end circular = pkg in circular_deps - is_stale = !Base.isprecompiled(pkg; ignore_loaded=true, stale_cache, cachepaths, sourcepath, flags=cacheflags) + is_stale = !Base.isprecompiled(pkg; ignore_loaded=true, stale_cache, cachepath_cache, cachepaths, sourcepath, flags=cacheflags) if !circular && is_stale Base.acquire(parallel_limiter) is_direct_dep = pkg in direct_deps From 515326a7704c71912b36a6e07056aac5012dec73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Wed, 30 Oct 2024 10:50:25 +0000 Subject: [PATCH 64/76] [docs] Fix note admonition in llvm-passes.md (#56392) At the moment this is rendered incorrectly: https://docs.julialang.org/en/v1.11.1/devdocs/llvm-passes/#JuliaLICM (cherry picked from commit a9342d679979b0f19522949b9b00200c5b2fc010) --- doc/src/devdocs/llvm-passes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/devdocs/llvm-passes.md b/doc/src/devdocs/llvm-passes.md index 36383acaef512..00993eee87aa4 100644 --- a/doc/src/devdocs/llvm-passes.md +++ b/doc/src/devdocs/llvm-passes.md @@ -156,6 +156,6 @@ This pass is used to hoist Julia-specific intrinsics out of loops. Specifically, 3. Hoist allocations out of loops when they do not escape the loop 1. We use a very conservative definition of escape here, the same as the one used in `AllocOptPass`. This transformation can reduce the number of allocations in the IR, even when an allocation escapes the function altogether. -!!!note +!!! note This pass is required to preserve LLVM's [MemorySSA](https://llvm.org/docs/MemorySSA.html) ([Short Video](https://www.youtube.com/watch?v=bdxWmryoHak), [Longer Video](https://www.youtube.com/watch?v=1e5y6WDbXCQ)) and [ScalarEvolution](https://baziotis.cs.illinois.edu/compilers/introduction-to-scalar-evolution.html) ([Newer Slides](https://llvm.org/devmtg/2018-04/slides/Absar-ScalarEvolution.pdf) [Older Slides](https://llvm.org/devmtg/2009-10/ScalarEvolutionAndLoopOptimization.pdf)) analyses. From 0baca2012a2ab5243e3718b4ace440e15a6cf15c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Fri, 1 Nov 2024 16:47:42 +0000 Subject: [PATCH 65/76] [docs] Fix rendering of warning admonition in llvm passes page (#56412) Follow up to #56392: also the warning in https://docs.julialang.org/en/v1.11.1/devdocs/llvm-passes/#Multiversioning is rendered incorrectly because of a missing space. (cherry picked from commit 3de1b1dc04dd6a0f4e2a0d32db89beb6b009164a) --- doc/src/devdocs/llvm-passes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/devdocs/llvm-passes.md b/doc/src/devdocs/llvm-passes.md index 00993eee87aa4..dc6178553c217 100644 --- a/doc/src/devdocs/llvm-passes.md +++ b/doc/src/devdocs/llvm-passes.md @@ -98,7 +98,7 @@ This pass performs modifications to a module to create functions that are optimi !!! warning - Use of `llvmcall` with multiversioning is dangerous. `llvmcall` enables access to features not typically exposed by the Julia APIs, and are therefore usually not available on all architectures. If multiversioning is enabled and code generation is requested for a target architecture that does not support the feature required by an `llvmcall` expression, LLVM will probably error out, likely with an abort and the message `LLVM ERROR: Do not know how to split the result of this operator!`. + Use of `llvmcall` with multiversioning is dangerous. `llvmcall` enables access to features not typically exposed by the Julia APIs, and are therefore usually not available on all architectures. If multiversioning is enabled and code generation is requested for a target architecture that does not support the feature required by an `llvmcall` expression, LLVM will probably error out, likely with an abort and the message `LLVM ERROR: Do not know how to split the result of this operator!`. ### GCInvariantVerifier From b06f294860a0cbbe59ef9ec7b6b602db3de64031 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 5 Nov 2024 07:53:20 -0500 Subject: [PATCH 66/76] Ensure that String(::Memory) returns only a String, not any owner (#56438) Fixes #56435 (cherry picked from commit 50ad4d96847c4a8153bc9435056d8d5e70e99716) --- src/genericmemory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/genericmemory.c b/src/genericmemory.c index ea52fca66ba48..b36852d53f9c8 100644 --- a/src/genericmemory.c +++ b/src/genericmemory.c @@ -197,7 +197,7 @@ JL_DLLEXPORT jl_value_t *jl_genericmemory_to_string(jl_genericmemory_t *m, size_ if (how != 0) { jl_value_t *o = jl_genericmemory_data_owner_field(m); jl_genericmemory_data_owner_field(m) = NULL; - if (how == 3 && + if (how == 3 && jl_is_string(o) && ((mlength + sizeof(void*) + 1 <= GC_MAX_SZCLASS) == (len + sizeof(void*) + 1 <= GC_MAX_SZCLASS))) { if (jl_string_data(o)[len] != '\0') jl_string_data(o)[len] = '\0'; From 57a292090e4d50fe763ce8f1cb3b6232dff6f284 Mon Sep 17 00:00:00 2001 From: Jerry Ling Date: Tue, 5 Nov 2024 09:52:14 -0500 Subject: [PATCH 67/76] Profile: mention `kill -s SIGUSR1 julia_pid` for Linux (#56441) currentlu this route is mentioned in docs https://docs.julialang.org/en/v1/stdlib/Profile/#Triggered-During-Execution but missing from the module docstring, this should help users who have little idea how to "send a kernel signal to a process" to get started --------- Co-authored-by: Ian Butterworth (cherry picked from commit 9af0dea9d2c7c957bec7a14acacf0b234447be31) --- stdlib/Profile/src/Profile.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index 98520b2267dd8..61acee765acc9 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -9,7 +9,7 @@ Profiling support. - `@profile foo()` to profile a specific call. - `Profile.print()` to print the report. - `Profile.clear()` to clear the buffer. -- Send a $(Sys.isbsd() ? "SIGINFO (ctrl-t)" : "SIGUSR1") signal to the process to automatically trigger a profile and print. +- Send a SIGUSR1 (on linux) or SIGINFO (on macOS/BSD) signal to the process to automatically trigger a profile and print. i.e. `kill -s SIGUSR1/SIGINFO 1234`, where 1234 is the pid of the julia process. On macOS & BSD platforms `ctrl-t` can be used directly. ## Memory profiling - `Profile.Allocs.@profile [sample_rate=0.1] foo()` to sample allocations within a specific call. A sample rate of 1.0 will record everything; 0.0 will record nothing. From 6cdcfef3b899544b25188e4d3ab126895c260123 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 6 Nov 2024 21:50:04 -0500 Subject: [PATCH 68/76] missing gc-root store in subtype (#56472) Fixes #56141 Introduced by #52228 (a624d445c02c) (cherry picked from commit 671cd5e1db70322d043680336d96259553e7f023) --- src/subtype.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 70c570ce5b8d7..2017954392ba6 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -4641,12 +4641,12 @@ static jl_value_t *insert_nondiagonal(jl_value_t *type, jl_varbinding_t *troot, jl_value_t *n = jl_unwrap_vararg_num(type); if (widen2ub == 0) widen2ub = !(n && jl_is_long(n)) || jl_unbox_long(n) > 1; - jl_value_t *newt; - JL_GC_PUSH2(&newt, &n); - newt = insert_nondiagonal(t, troot, widen2ub); - if (t != newt) + jl_value_t *newt = insert_nondiagonal(t, troot, widen2ub); + if (t != newt) { + JL_GC_PUSH1(&newt); type = (jl_value_t *)jl_wrap_vararg(newt, n, 0, 0); - JL_GC_POP(); + JL_GC_POP(); + } } else if (jl_is_datatype(type)) { if (jl_is_tuple_type(type)) { @@ -4683,7 +4683,7 @@ static jl_value_t *_widen_diagonal(jl_value_t *t, jl_varbinding_t *troot) { static jl_value_t *widen_diagonal(jl_value_t *t, jl_unionall_t *u, jl_varbinding_t *troot) { jl_varbinding_t vb = { u->var, NULL, NULL, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, troot }; - jl_value_t *nt; + jl_value_t *nt = NULL; JL_GC_PUSH2(&vb.innervars, &nt); if (jl_is_unionall(u->body)) nt = widen_diagonal(t, (jl_unionall_t *)u->body, &vb); From 5a280aa6af52e5d8c55ba1665faaac5944896c17 Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Sat, 9 Nov 2024 16:30:27 -0500 Subject: [PATCH 69/76] The `info` in LAPACK calls should be a Ref instead of a Ptr (#56511) Co-authored-by: Viral B. Shah (cherry picked from commit cd748a5c5b8575e00ce8e7f14ac4c79113d11171) --- stdlib/LinearAlgebra/src/lapack.jl | 164 ++++++++++++++--------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl index 1746254b92293..2bc38a673cb01 100644 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ b/stdlib/LinearAlgebra/src/lapack.jl @@ -160,7 +160,7 @@ for (gbtrf, gbtrs, elty) in info = Ref{BlasInt}() ccall((@blasfunc($gbtrf), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}), m, n, kl, ku, AB, max(1,stride(AB,2)), ipiv, info) chklapackerror(info[]) AB, ipiv @@ -187,7 +187,7 @@ for (gbtrf, gbtrs, elty) in ccall((@blasfunc($gbtrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Clong), + Ref{BlasInt}, Clong), trans, n, kl, ku, size(B,2), AB, max(1,stride(AB,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) @@ -242,7 +242,7 @@ for (gebal, gebak, elty, relty) in info = Ref{BlasInt}() ccall((@blasfunc($gebal), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ref{BlasInt}, Clong), job, n, A, max(1,stride(A,2)), ilo, ihi, scale, info, 1) chklapackerror(info[]) ilo[], ihi[], scale @@ -586,7 +586,7 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty info = Ref{BlasInt}() ccall((@blasfunc($getrf), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), + Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}), m, n, A, lda, ipiv, info) chkargsok(info[]) A, ipiv, info[] #Error code is stored in LU factorization type @@ -923,7 +923,7 @@ for (tzrzf, ormrz, elty) in (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), + Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), side, trans, m, n, k, l, A, lda, tau, C, ldc, work, @@ -987,7 +987,7 @@ for (gels, gesv, getrs, getri, elty) in ccall((@blasfunc($gels), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), (btrn ? 'T' : 'N'), m, n, size(B,2), A, max(1,stride(A,2)), B, max(1,stride(B,2)), work, lwork, info, 1) chklapackerror(info[]) @@ -1055,7 +1055,7 @@ for (gels, gesv, getrs, getri, elty) in info = Ref{BlasInt}() ccall((@blasfunc($getrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), trans, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B @@ -1181,7 +1181,7 @@ for (gesvx, elty) in Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{UInt8}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), + Ptr{$elty}, Ptr{BlasInt}, Ref{BlasInt}, Clong, Clong, Clong), fact, trans, n, nrhs, A, lda, AF, ldaf, ipiv, equed, R, C, B, ldb, X, n, rcond, ferr, berr, work, iwork, info, 1, 1, 1) chklapackerror(info[]) @@ -1253,7 +1253,7 @@ for (gesvx, elty, relty) in Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{UInt8}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$relty}, - Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}, Clong, Clong, Clong), + Ptr{$elty}, Ptr{$relty}, Ref{BlasInt}, Clong, Clong, Clong), fact, trans, n, nrhs, A, lda, AF, ldaf, ipiv, equed, R, C, B, ldb, X, n, rcond, ferr, berr, work, rwork, info, 1, 1, 1) chklapackerror(info[]) @@ -1634,7 +1634,7 @@ for (geev, gesvd, gesdd, ggsvd, elty, relty) in (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{BlasInt}, Clong, Clong), + Ptr{$relty}, Ref{BlasInt}, Clong, Clong), jobvl, jobvr, n, A, max(1,stride(A,2)), W, VL, n, VR, n, work, lwork, rwork, info, 1, 1) else @@ -1642,7 +1642,7 @@ for (geev, gesvd, gesdd, ggsvd, elty, relty) in (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), + Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), jobvl, jobvr, n, A, max(1,stride(A,2)), WR, WI, VL, n, VR, n, work, lwork, info, 1, 1) end @@ -1699,7 +1699,7 @@ for (geev, gesvd, gesdd, ggsvd, elty, relty) in (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{$relty}, Ptr{BlasInt}, Ref{BlasInt}, Clong), job, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), work, lwork, rwork, iwork, info, 1) else @@ -1707,7 +1707,7 @@ for (geev, gesvd, gesdd, ggsvd, elty, relty) in (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ref{BlasInt}, Clong), job, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), work, lwork, iwork, info, 1) end @@ -1771,7 +1771,7 @@ for (geev, gesvd, gesdd, ggsvd, elty, relty) in (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, Clong, Clong), + Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, Clong, Clong), jobu, jobvt, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), work, lwork, rwork, info, 1, 1) else @@ -1779,7 +1779,7 @@ for (geev, gesvd, gesdd, ggsvd, elty, relty) in (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), + Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), jobu, jobvt, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), work, lwork, info, 1, 1) end @@ -2047,7 +2047,7 @@ for (f, elty, relty) in ((:zggsvd3_, :ComplexF64, :Float64), Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, - Ptr{BlasInt}, Clong, Clong, Clong), + Ref{BlasInt}, Clong, Clong, Clong), jobu, jobv, jobq, m, n, p, k, l, A, lda, B, ldb, @@ -2691,7 +2691,7 @@ for (gtsv, gttrf, gttrs, elty) in info = Ref{BlasInt}() ccall((@blasfunc($gttrf), libblastrampoline), Cvoid, (Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}), + Ptr{BlasInt}, Ref{BlasInt}), n, dl, d, du, du2, ipiv, info) chklapackerror(info[]) dl, d, du, du2, ipiv @@ -2724,7 +2724,7 @@ for (gtsv, gttrf, gttrs, elty) in ccall((@blasfunc($gttrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), trans, n, size(B,2), dl, d, du, du2, ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B @@ -2946,7 +2946,7 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in ccall((@blasfunc($ormlq), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), + Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), side, trans, m, n, k, A, max(1,stride(A,2)), tau, C, max(1,stride(C,2)), work, lwork, info, 1, 1) chklapackerror(info[]) @@ -2994,7 +2994,7 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Clong, Clong), + Ref{BlasInt}, Clong, Clong), side, trans, m, n, k, A, max(1,stride(A,2)), tau, C, max(1, stride(C,2)), work, lwork, @@ -3044,7 +3044,7 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Clong, Clong), + Ref{BlasInt}, Clong, Clong), side, trans, m, n, k, A, max(1,stride(A,2)), tau, C, max(1, stride(C,2)), work, lwork, @@ -3093,7 +3093,7 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in ccall((@blasfunc($ormrq), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), + Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), side, trans, m, n, k, A, max(1,stride(A,2)), tau, C, max(1,stride(C,2)), work, lwork, info, 1, 1) chklapackerror(info[]) @@ -3150,7 +3150,7 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Clong, Clong), + Ptr{$elty}, Ref{BlasInt}, Clong, Clong), side, trans, m, n, k, nb, V, ldv, T, max(1,stride(T,2)), C, max(1,ldc), @@ -3267,7 +3267,7 @@ for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in info = Ref{BlasInt}() ccall((@blasfunc($posv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), B, max(1,stride(B,2)), info, 1) chkargsok(info[]) chkposdef(info[]) @@ -3291,7 +3291,7 @@ for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in end info = Ref{BlasInt}() ccall((@blasfunc($potrf), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, size(A,1), A, lda, info, 1) chkargsok(info[]) #info[] > 0 means the leading minor of order info[] is not positive definite @@ -3312,7 +3312,7 @@ for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in chkuplo(uplo) info = Ref{BlasInt}() ccall((@blasfunc($potri), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, size(A,1), A, max(1,stride(A,2)), info, 1) chkargsok(info[]) chknonsingular(info[]) @@ -3342,7 +3342,7 @@ for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in info = Ref{BlasInt}() ccall((@blasfunc($potrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, nrhs, A, lda, B, ldb, info, 1) chklapackerror(info[]) @@ -3368,7 +3368,7 @@ for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in info = Ref{BlasInt}() ccall((@blasfunc($pstrf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ref{$rtyp}, Ptr{$rtyp}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ref{$rtyp}, Ptr{$rtyp}, Ref{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), piv, rank, tol, work, info, 1) chkargsok(info[]) A, piv, rank[1], info[] #Stored in CholeskyPivoted @@ -3559,7 +3559,7 @@ for (pttrs, elty, relty) in info = Ref{BlasInt}() ccall((@blasfunc($pttrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, size(B,2), D, E, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B @@ -3599,7 +3599,7 @@ for (trtri, trtrs, elty) in info = Ref{BlasInt}() ccall((@blasfunc($trtri), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Clong, Clong), + Ref{BlasInt}, Clong, Clong), uplo, diag, n, A, lda, info, 1, 1) chklapackerror(info[]) A @@ -3729,7 +3729,7 @@ for (trcon, trevc, trrfs, elty) in (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Clong, Clong), + Ptr{$elty}, Ref{BlasInt}, Clong, Clong), side, howmny, select, n, T, ldt, VL, ldvl, VR, ldvr, mm, m, @@ -3785,7 +3785,7 @@ for (trcon, trevc, trrfs, elty) in ccall((@blasfunc($trrfs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), + Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ref{BlasInt}, Clong, Clong, Clong), uplo, trans, diag, n, nrhs, A, max(1,stride(A,2)), B, max(1,stride(B,2)), X, max(1,stride(X,2)), Ferr, Berr, work, iwork, info, 1, 1, 1) @@ -3866,7 +3866,7 @@ for (trcon, trevc, trrfs, elty, relty) in (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}, Clong, Clong), + Ptr{$elty}, Ptr{$relty}, Ref{BlasInt}, Clong, Clong), side, howmny, select, n, T, ldt, VL, ldvl, VR, ldvr, mm, m, @@ -3922,7 +3922,7 @@ for (trcon, trevc, trrfs, elty, relty) in ccall((@blasfunc($trrfs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}, Clong, Clong, Clong), + Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{$relty}, Ref{BlasInt}, Clong, Clong, Clong), uplo, trans, diag, n, nrhs, A, max(1,stride(A,2)), B, max(1,stride(B,2)), X, max(1,stride(X,2)), Ferr, Berr, work, rwork, info, 1, 1, 1) @@ -3993,7 +3993,7 @@ for (stev, stebz, stegr, stein, elty) in info = Ref{BlasInt}() ccall((@blasfunc($stev), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong), + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), job, n, dv, ev, Zmat, n, work, info, 1) chklapackerror(info[]) dv, Zmat @@ -4026,7 +4026,7 @@ for (stev, stebz, stegr, stein, elty) in Ref{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong), + Ptr{BlasInt}, Ref{BlasInt}, Clong, Clong), range, order, n, vl, vu, il, iu, abstol, dv, ev, m, nsplit, @@ -4220,7 +4220,7 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in info = Ref{BlasInt}() ccall((@blasfunc($syconv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong, Clong), uplo, 'C', n, A, max(1,stride(A,2)), ipiv, work, info, 1, 1) chklapackerror(info[]) A, work @@ -4249,7 +4249,7 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), work, lwork, info, 1) chkargsok(info[]) @@ -4283,7 +4283,7 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, A, stride(A,2), ipiv, work, lwork, info, 1) chkargsok(info[]) if i == 1 @@ -4319,7 +4319,7 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in # for i in 1:2 # ccall((@blasfunc($sytri), libblastrampoline), Cvoid, # (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, -# Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong), +# Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ref{BlasInt}, Clong), # &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info, 1) # @assertargsok # chknonsingular(info[]) @@ -4347,7 +4347,7 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in info = Ref{BlasInt}() ccall((@blasfunc($sytri), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) chkargsok(info[]) chknonsingular(info[]) @@ -4374,7 +4374,7 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in info = Ref{BlasInt}() ccall((@blasfunc($sytrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B @@ -4410,7 +4410,7 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty) in for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), work, lwork, info, 1) chkargsok(info[]) @@ -4445,7 +4445,7 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty) in for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, A, stride(A,2), ipiv, work, lwork, info, 1) chkargsok(info[]) if i == 1 @@ -4472,7 +4472,7 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty) in info = Ref{BlasInt}() ccall((@blasfunc($sytri), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) chkargsok(info[]) chknonsingular(info[]) @@ -4499,7 +4499,7 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty) in info = Ref{BlasInt}() ccall((@blasfunc($sytrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B @@ -4575,7 +4575,7 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in info = Ref{BlasInt}() ccall((@blasfunc($syconv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong, Clong), uplo, 'C', n, A, max(1,stride(A,2)), ipiv, work, info, 1, 1) chklapackerror(info[]) A, work @@ -4604,7 +4604,7 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hesv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), work, lwork, info, 1) chklapackerror(info[]) @@ -4635,7 +4635,7 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in for i in 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hetrf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info, 1) chkargsok(info[]) if i == 1 @@ -4672,7 +4672,7 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in # for i in 1:2 # ccall((@blasfunc($hetri), libblastrampoline), Cvoid, # (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, -# Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong), +# Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ref{BlasInt}, Clong), # &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info, 1) # chklapackerror(info[]) # if lwork < 0 @@ -4701,7 +4701,7 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in info = Ref{BlasInt}() ccall((@blasfunc($hetri), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) chklapackerror(info[]) A @@ -4727,7 +4727,7 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in info = Ref{BlasInt}() ccall((@blasfunc($hetrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B @@ -4762,7 +4762,7 @@ for (hesv, hetrf, hetri, hetrs, elty, relty) in for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hesv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), work, lwork, info, 1) chklapackerror(info[]) @@ -4794,7 +4794,7 @@ for (hesv, hetrf, hetri, hetrs, elty, relty) in for i in 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hetrf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info, 1) chkargsok(info[]) if i == 1 @@ -4822,7 +4822,7 @@ for (hesv, hetrf, hetri, hetrs, elty, relty) in info = Ref{BlasInt}() ccall((@blasfunc($hetri), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) chklapackerror(info[]) A @@ -4848,7 +4848,7 @@ for (hesv, hetrf, hetri, hetrs, elty, relty) in info = Ref{BlasInt}() ccall((@blasfunc($hetrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B @@ -4884,7 +4884,7 @@ for (sysv, sytrf, sytri, sytrs, elty, relty) in for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), work, lwork, info, 1) chkargsok(info[]) @@ -4919,7 +4919,7 @@ for (sysv, sytrf, sytri, sytrs, elty, relty) in for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info, 1) chkargsok(info[]) if i == 1 @@ -4956,7 +4956,7 @@ for (sysv, sytrf, sytri, sytrs, elty, relty) in # for i in 1:2 # ccall((@blasfunc($sytri), libblastrampoline), Cvoid, # (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, -# Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong), +# Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ref{BlasInt}, Clong), # &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info, 1) # chklapackerror(info[]) # if lwork < 0 @@ -4984,7 +4984,7 @@ for (sysv, sytrf, sytri, sytrs, elty, relty) in info = Ref{BlasInt}() ccall((@blasfunc($sytri), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) chklapackerror(info[]) A @@ -5010,7 +5010,7 @@ for (sysv, sytrf, sytri, sytrs, elty, relty) in info = Ref{BlasInt}() ccall((@blasfunc($sytrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B @@ -5046,7 +5046,7 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), work, lwork, info, 1) chkargsok(info[]) @@ -5082,7 +5082,7 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info, 1) chkargsok(info[]) if i == 1 @@ -5110,7 +5110,7 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in info = Ref{BlasInt}() ccall((@blasfunc($sytri), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) chklapackerror(info[]) A @@ -5136,7 +5136,7 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in info = Ref{BlasInt}() ccall((@blasfunc($sytrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B @@ -5342,7 +5342,7 @@ for (syev, syevr, syevd, sygvd, elty) in for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($syev), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), jobz, uplo, n, A, max(1,stride(A,2)), W, work, lwork, info, 1, 1) chklapackerror(info[]) if i == 1 @@ -5400,7 +5400,7 @@ for (syev, syevr, syevd, sygvd, elty) in Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, - Ptr{BlasInt}, Clong, Clong, Clong), + Ref{BlasInt}, Clong, Clong, Clong), jobz, range, uplo, n, A, max(1,lda), vl, vu, il, iu, abstol, m, @@ -5447,7 +5447,7 @@ for (syev, syevr, syevd, sygvd, elty) in ccall((@blasfunc($syevd), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, - Ptr{BlasInt}, Clong, Clong), + Ref{BlasInt}, Clong, Clong), jobz, uplo, n, A, max(1,lda), W, work, lwork, iwork, liwork, info, 1, 1) @@ -5495,7 +5495,7 @@ for (syev, syevr, syevd, sygvd, elty) in (Ref{BlasInt}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), + Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), itype, jobz, uplo, n, A, lda, B, ldb, w, work, lwork, iwork, @@ -5659,7 +5659,7 @@ for (syev, syevr, syevd, sygvd, elty, relty) in ccall((@blasfunc($syevd), liblapack), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, - Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), + Ptr{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), jobz, uplo, n, A, stride(A,2), W, work, lwork, rwork, lrwork, iwork, liwork, info, 1, 1) @@ -5827,7 +5827,7 @@ for (bdsqr, relty, elty) in (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, Clong), + Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, Clong), uplo, n, ncvt, nru, ncc, d, e_, Vt, ldvt, U, ldu, C, @@ -5951,7 +5951,7 @@ for (gecon, elty) in ccall((@blasfunc($gecon), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ref{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Clong), + Ref{BlasInt}, Clong), normtype, n, A, lda, anorm, rcond, work, iwork, info, 1) chklapackerror(info[]) @@ -5987,7 +5987,7 @@ for (gecon, elty, relty) in ccall((@blasfunc($gecon), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$relty}, Ref{$relty}, Ptr{$elty}, Ptr{$relty}, - Ptr{BlasInt}, Clong), + Ref{BlasInt}, Clong), normtype, n, A, lda, anorm, rcond, work, rwork, info, 1) chklapackerror(info[]) @@ -6142,7 +6142,7 @@ for (ormhr, elty) in (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), + Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), side, trans, mC, nC, ilo, ihi, A, max(1, stride(A, 2)), tau, C, max(1, stride(C, 2)), work, @@ -6290,7 +6290,7 @@ for (hetrd, elty) in ccall((@blasfunc($hetrd), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), uplo, n, A, max(1, stride(A, 2)), d, e, tau, work, lwork, info, 1) chklapackerror(info[]) if i == 1 @@ -6340,7 +6340,7 @@ for (orgtr, elty) in ccall((@blasfunc($orgtr), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Clong), + Ref{BlasInt}, Clong), uplo, n, A, max(1, stride(A, 2)), tau, work, lwork, info, 1) @@ -6401,7 +6401,7 @@ for (ormtr, elty) in (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), + Ref{BlasInt}, Ref{BlasInt}, Clong, Clong, Clong), side, uplo, trans, mC, nC, A, max(1, stride(A, 2)), tau, C, max(1, stride(C, 2)), work, @@ -6779,7 +6779,7 @@ for (trexc, trsen, tgsen, elty) in (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Clong), + Ptr{$elty}, Ref{BlasInt}, Clong), compq, n, T, ldt, Q, ldq, ifst, ilst, @@ -6825,7 +6825,7 @@ for (trexc, trsen, tgsen, elty) in Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, - Ptr{BlasInt}, Clong, Clong), + Ref{BlasInt}, Clong, Clong), job, compq, select, n, T, ldt, Q, ldq, wr, wi, m, s, sep, @@ -6936,7 +6936,7 @@ for (trexc, trsen, tgsen, elty, relty) in (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{BlasInt}, Clong), + Ref{BlasInt}, Clong), compq, n, T, ldt, Q, ldq, ifst, ilst, @@ -6978,7 +6978,7 @@ for (trexc, trsen, tgsen, elty, relty) in Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$relty}, Ref{$relty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Clong, Clong), + Ref{BlasInt}, Clong, Clong), job, compq, select, n, T, ldt, Q, ldq, w, m, s, sep, @@ -7127,7 +7127,7 @@ for (fn, elty, relty) in ((:dtrsyl_, :Float64, :Float64), ccall((@blasfunc($fn), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{BlasInt}, Clong, Clong), + Ptr{$relty}, Ref{BlasInt}, Clong, Clong), transa, transb, isgn, m, n, A, lda, B, ldb, C, ldc, scale, info, 1, 1) From 157ab5ac098bdd40e32ee7e420a1ad191b4e2256 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 7 Oct 2024 19:54:51 -0400 Subject: [PATCH 70/76] add require_lock call to maybe_loaded_precompile (#56027) If we expect this to be a public API (https://github.com/timholy/Revise.jl for some reason is trying to access this state), we should lock around it for consistency with the other similar functions. Needed for https://github.com/timholy/Revise.jl/pull/856 (cherry picked from commit 4cdd864e535b16e928c2eff43e6f1583bd77209d) --- base/loading.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/loading.jl b/base/loading.jl index a73715b6ca1a8..bc7ce64c945aa 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2417,12 +2417,13 @@ is_root_module(m::Module) = @lock require_lock haskey(module_keys, m) root_module_key(m::Module) = @lock require_lock module_keys[m] function maybe_loaded_precompile(key::PkgId, buildid::UInt128) - assert_havelock(require_lock) + @lock require_lock begin mods = get(loaded_precompiles, key, nothing) mods === nothing && return for mod in mods module_build_id(mod) == buildid && return mod end + end end function module_build_id(m::Module) From 6abe9a32d9d0dd17af70ac6d617c653fb53b6a66 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Tue, 12 Nov 2024 22:20:45 +0100 Subject: [PATCH 71/76] pin SparseArrays branch to release-1.11 --- stdlib/SparseArrays.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version index 5eca7be5ec9b7..1fe41c8974c60 100644 --- a/stdlib/SparseArrays.version +++ b/stdlib/SparseArrays.version @@ -1,4 +1,4 @@ -SPARSEARRAYS_BRANCH = main +SPARSEARRAYS_BRANCH = release-1.11 SPARSEARRAYS_SHA1 = cb602d7b7cf46057ddc87d23cda2bdd168a548ac SPARSEARRAYS_GIT_URL := https://github.com/JuliaSparse/SparseArrays.jl.git SPARSEARRAYS_TAR_URL = https://api.github.com/repos/JuliaSparse/SparseArrays.jl/tarball/$1 From 408ae90621b754604af2603512e796f2c5c835c5 Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Wed, 13 Nov 2024 13:39:43 +0100 Subject: [PATCH 72/76] remove spurious file include --- test/loading.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/loading.jl b/test/loading.jl index 9cfd421a596cb..816b836e71e0e 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -34,7 +34,6 @@ end @test @nested_LINE_expansion2() == ((@__LINE__() - 5, @__LINE__() - 9), @__LINE__()) original_depot_path = copy(Base.DEPOT_PATH) -include("precompile_utils.jl") loaded_files = String[] push!(Base.include_callbacks, (mod::Module, fn::String) -> push!(loaded_files, fn)) From 88382f190637b1227153dca18947cbe0cc860f44 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 14 Nov 2024 09:00:58 +0100 Subject: [PATCH 73/76] make failing to remove depot path in a loading test not error --- test/loading.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/loading.jl b/test/loading.jl index 816b836e71e0e..c77c5e1e19fdd 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1618,6 +1618,9 @@ end finally oldBase64 === nothing || Base.register_root_module(oldBase64) copy!(DEPOT_PATH, original_depot_path) - rm(depot_path, force=true, recursive=true) + try # don't error if removing depot failed + rm(depot_path, force=true, recursive=true) + catch + end end end From 3e0c47c849e7ce8ba660a174a26daeb53687c6be Mon Sep 17 00:00:00 2001 From: Morten Piibeleht Date: Thu, 14 Nov 2024 23:40:44 +1300 Subject: [PATCH 74/76] [backports-release-1.11] Update Documenter v1.3.0 => v1.8.0 (#56550) Manual "backport" of #56538. I assume we want this against https://github.com/JuliaLang/julia/pull/56228 ? --- doc/Manifest.toml | 75 +++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/doc/Manifest.toml b/doc/Manifest.toml index 5aedbbd7eec38..76bdc332ff36f 100644 --- a/doc/Manifest.toml +++ b/doc/Manifest.toml @@ -1,6 +1,6 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.11.0-DEV" +julia_version = "1.11.1" manifest_format = "2.0" project_hash = "e0c77beb18dc1f6cce661ebd60658c0c1a77390f" @@ -10,9 +10,9 @@ uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" version = "0.0.1" [[deps.AbstractTrees]] -git-tree-sha1 = "faa260e4cb5aba097a73fab382dd4b5819d8ec8c" +git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" -version = "0.4.4" +version = "0.4.5" [[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" @@ -28,9 +28,9 @@ version = "1.11.0" [[deps.CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "59939d8a997469ee05c4b4944560a820f9ba0d73" +git-tree-sha1 = "bce6804e5e6044c6daab27bb533d1295e4a2e759" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.4" +version = "0.7.6" [[deps.Dates]] deps = ["Printf"] @@ -45,9 +45,9 @@ version = "0.9.3" [[deps.Documenter]] deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] -git-tree-sha1 = "4a40af50e8b24333b9ec6892546d9ca5724228eb" +git-tree-sha1 = "d0ea2c044963ed6f37703cead7e29f70cba13d7e" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "1.3.0" +version = "1.8.0" [[deps.Downloads]] deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] @@ -56,9 +56,9 @@ version = "1.6.0" [[deps.Expat_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "4558ab818dcceaab612d1bb8c19cee87eda2b83c" +git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7" uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.5.0+0" +version = "2.6.2+0" [[deps.FileWatching]] uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" @@ -66,21 +66,21 @@ version = "1.11.0" [[deps.Git]] deps = ["Git_jll"] -git-tree-sha1 = "51764e6c2e84c37055e846c516e9015b4a291c7d" +git-tree-sha1 = "04eff47b1354d702c3a85e8ab23d539bb7d5957e" uuid = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2" -version = "1.3.0" +version = "1.3.1" [[deps.Git_jll]] deps = ["Artifacts", "Expat_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Libiconv_jll", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "bb8f7cc77ec1152414b2af6db533d9471cfbb2d1" +git-tree-sha1 = "ea372033d09e4552a04fd38361cd019f9003f4f4" uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" -version = "2.42.0+0" +version = "2.46.2+0" [[deps.IOCapture]] deps = ["Logging", "Random"] -git-tree-sha1 = "d75853a0bdbfb1ac815478bacd89cd27b550ace6" +git-tree-sha1 = "b6d6bfdd7ce25b0f9b2f6b3dd56b2673a66c8770" uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" -version = "0.2.3" +version = "0.2.5" [[deps.InteractiveUtils]] deps = ["Markdown"] @@ -89,9 +89,9 @@ version = "1.11.0" [[deps.JLLWrappers]] deps = ["Artifacts", "Preferences"] -git-tree-sha1 = "7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca" +git-tree-sha1 = "be3dc50a92e5a386872a493a10050136d4703f9b" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.5.0" +version = "1.6.1" [[deps.JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] @@ -100,9 +100,9 @@ uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" version = "0.21.4" [[deps.LazilyInitializedFields]] -git-tree-sha1 = "8f7f3cabab0fd1800699663533b6d5cb3fc0e612" +git-tree-sha1 = "0f2da712350b020bc3957f269c9caad516383ee0" uuid = "0e77f7df-68c5-4e49-93ce-4cd80f5598bf" -version = "1.2.2" +version = "1.3.0" [[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] @@ -135,9 +135,9 @@ version = "1.11.0" [[deps.Libiconv_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175" +git-tree-sha1 = "61dfdba58e585066d8bce214c5a51eaa0539f269" uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.17.0+0" +version = "1.17.0+1" [[deps.Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" @@ -173,9 +173,9 @@ version = "1.2.0" [[deps.OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "cc6e1927ac521b659af340e0ca45828a3ffc748f" +git-tree-sha1 = "7493f61f55a6cce7325f197443aa80d32554ba10" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.12+0" +version = "3.0.15+1" [[deps.PCRE2_jll]] deps = ["Artifacts", "Libdl"] @@ -184,12 +184,12 @@ version = "10.42.0+1" [[deps.Parsers]] deps = ["Dates", "PrecompileTools", "UUIDs"] -git-tree-sha1 = "a935806434c9d4c506ba941871b327b96d41f2bf" +git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.8.0" +version = "2.8.1" [[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" version = "1.11.0" weakdeps = ["REPL"] @@ -199,15 +199,15 @@ weakdeps = ["REPL"] [[deps.PrecompileTools]] deps = ["Preferences"] -git-tree-sha1 = "03b4c25b43cb84cee5c90aa9b5ea0a78fd848d2f" +git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" -version = "1.2.0" +version = "1.2.1" [[deps.Preferences]] deps = ["TOML"] -git-tree-sha1 = "00805cd429dcb4870060ff49ef443486c262e38e" +git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6" uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.4.1" +version = "1.4.3" [[deps.Printf]] deps = ["Unicode"] @@ -242,6 +242,10 @@ version = "1.11.0" uuid = "6462fe0b-24de-5631-8697-dd941f90decc" version = "1.11.0" +[[deps.StyledStrings]] +uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" +version = "1.11.0" + [[deps.TOML]] deps = ["Dates"] uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" @@ -252,22 +256,15 @@ deps = ["ArgTools", "SHA"] uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" version = "1.10.0" -[[deps.StyledStrings]] -uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" - [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" version = "1.11.0" [[deps.TranscodingStreams]] -git-tree-sha1 = "54194d92959d8ebaa8e26227dbe3cdefcdcd594f" +git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.10.3" -weakdeps = ["Random", "Test"] - - [deps.TranscodingStreams.extensions] - TestExt = ["Test", "Random"] +version = "0.11.3" [[deps.UUIDs]] deps = ["Random", "SHA"] From d59d80ba5332a5001621be3a058186f9ae1bba42 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Wed, 20 Nov 2024 04:56:03 -0500 Subject: [PATCH 75/76] =?UTF-8?q?=F0=9F=A4=96=20[backports-release-1.11]?= =?UTF-8?q?=20Bump=20the=20SparseArrays=20stdlib=20from=20cb602d7=20to=202?= =?UTF-8?q?420351=20(#56608)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: SparseArrays URL: https://github.com/JuliaSparse/SparseArrays.jl.git Stdlib branch: release-1.11 Julia branch: backports-release-1.11 Old commit: cb602d7 New commit: 2420351 Julia version: 1.11.1 SparseArrays version: 1.11.0(Does not match) Bump invoked by: @dkarrasch Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaSparse/SparseArrays.jl/compare/cb602d7b7cf46057ddc87d23cda2bdd168a548ac...242035184c0d539bdb5e64bf26eb7726b123db14 ``` $ git log --oneline cb602d7..2420351 2420351 Allow for duplicate docstrings in linalg docs (#584) 0a1c886 Merge pull request #571 from JuliaSparse/backports-release-1.11 298f5e1 Break recursion (#579) 7bc65ab Update CI fcc6102 Do not use nested dissection by default. (#550) 8534357 Update ci.yml: run CI and docs on v1.11 66d65fd Change default QR tolerance to match SPQR (#557) 8a48327 test: Don't use GPL module when Base.USE_GPL_LIBS=false (#535) 8dd8300 SparseMatrixCSC constructor with a Tuple of Integers (#523) aa66624 doc: move solvers doc to `src\solvers.md` (#576) 8a85fbe Inline sparse-times-dense in-place multiplication ``` Co-authored-by: Dilum Aluthge --- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - stdlib/SparseArrays.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/SparseArrays-242035184c0d539bdb5e64bf26eb7726b123db14.tar.gz/md5 create mode 100644 deps/checksums/SparseArrays-242035184c0d539bdb5e64bf26eb7726b123db14.tar.gz/sha512 delete mode 100644 deps/checksums/SparseArrays-cb602d7b7cf46057ddc87d23cda2bdd168a548ac.tar.gz/md5 delete mode 100644 deps/checksums/SparseArrays-cb602d7b7cf46057ddc87d23cda2bdd168a548ac.tar.gz/sha512 diff --git a/deps/checksums/SparseArrays-242035184c0d539bdb5e64bf26eb7726b123db14.tar.gz/md5 b/deps/checksums/SparseArrays-242035184c0d539bdb5e64bf26eb7726b123db14.tar.gz/md5 new file mode 100644 index 0000000000000..2f95a7d6f3947 --- /dev/null +++ b/deps/checksums/SparseArrays-242035184c0d539bdb5e64bf26eb7726b123db14.tar.gz/md5 @@ -0,0 +1 @@ +eb9ce9d568c5ff313c09c4f2caf26d13 diff --git a/deps/checksums/SparseArrays-242035184c0d539bdb5e64bf26eb7726b123db14.tar.gz/sha512 b/deps/checksums/SparseArrays-242035184c0d539bdb5e64bf26eb7726b123db14.tar.gz/sha512 new file mode 100644 index 0000000000000..c55bbe86e3e24 --- /dev/null +++ b/deps/checksums/SparseArrays-242035184c0d539bdb5e64bf26eb7726b123db14.tar.gz/sha512 @@ -0,0 +1 @@ +fbd3876eab248aee95b34e09fff4366db42fd2e1692e063ef8362256dccbc9b1548ea8f3106065e7310834edbe0ed176466ad2bb64a91af3386d0e9d0db1f929 diff --git a/deps/checksums/SparseArrays-cb602d7b7cf46057ddc87d23cda2bdd168a548ac.tar.gz/md5 b/deps/checksums/SparseArrays-cb602d7b7cf46057ddc87d23cda2bdd168a548ac.tar.gz/md5 deleted file mode 100644 index 5234c20cb4ff7..0000000000000 --- a/deps/checksums/SparseArrays-cb602d7b7cf46057ddc87d23cda2bdd168a548ac.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -47cb7d9dd6f3d8ae3cb497c202ae6411 diff --git a/deps/checksums/SparseArrays-cb602d7b7cf46057ddc87d23cda2bdd168a548ac.tar.gz/sha512 b/deps/checksums/SparseArrays-cb602d7b7cf46057ddc87d23cda2bdd168a548ac.tar.gz/sha512 deleted file mode 100644 index 1aa2d8146d78e..0000000000000 --- a/deps/checksums/SparseArrays-cb602d7b7cf46057ddc87d23cda2bdd168a548ac.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -5ff47ea50564375e5e926c3f592a9708b1d9862e4090a53c6b02fc09bc1872578e016a4231564a10dd17be174beed54fd0b8821430828e7148f09556f8034ed9 diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version index 1fe41c8974c60..41c5d6b42479d 100644 --- a/stdlib/SparseArrays.version +++ b/stdlib/SparseArrays.version @@ -1,4 +1,4 @@ SPARSEARRAYS_BRANCH = release-1.11 -SPARSEARRAYS_SHA1 = cb602d7b7cf46057ddc87d23cda2bdd168a548ac +SPARSEARRAYS_SHA1 = 242035184c0d539bdb5e64bf26eb7726b123db14 SPARSEARRAYS_GIT_URL := https://github.com/JuliaSparse/SparseArrays.jl.git SPARSEARRAYS_TAR_URL = https://api.github.com/repos/JuliaSparse/SparseArrays.jl/tarball/$1 From f55307c86a95330281daa951e9a862461c2fb303 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Wed, 20 Nov 2024 19:19:04 -0500 Subject: [PATCH 76/76] [backports 1.11] precompile: improve precision of extension dependencies (#56624) This is a slimmed-down version of #55910, without any of the re-factoring. This improves the parallelism of the pre-compile job and fixes a correctness bug, where unconditionally injecting a dependency after its parent could be in conflict with dependencies in between triggers, causing false extension cycles. Without this fix, adding `AMDGPU.jl` and `Tracker.jl` reports a false cycle on 1.11 --- base/precompilation.jl | 51 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index 7229a9f79f3e9..f681f0091c550 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -460,8 +460,6 @@ function _precompilepkgs(pkgs::Vector{String}, # consider exts of direct deps to be direct deps so that errors are reported append!(direct_deps, keys(filter(d->last(d) in keys(env.project_deps), exts))) - @debug "precompile: deps collected" - # An extension effectively depends on another extension if it has a strict superset of its triggers for ext_a in keys(exts) for ext_b in keys(exts) @@ -471,14 +469,49 @@ function _precompilepkgs(pkgs::Vector{String}, end end + function expand_indirect_dependencies(direct_deps) + function visit!(visited, node, all_deps) + if node in visited + return + end + push!(visited, node) + for dep in get(Set{Base.PkgId}, direct_deps, node) + if !(dep in all_deps) + push!(all_deps, dep) + visit!(visited, dep, all_deps) + end + end + end + + indirect_deps = Dict{Base.PkgId, Set{Base.PkgId}}() + for package in keys(direct_deps) + # Initialize a set to keep track of all dependencies for 'package' + all_deps = Set{Base.PkgId}() + visited = Set{Base.PkgId}() + visit!(visited, package, all_deps) + # Update direct_deps with the complete set of dependencies for 'package' + indirect_deps[package] = all_deps + end + return indirect_deps + end + + indirect_deps = expand_indirect_dependencies(depsmap) + @debug "precompile: deps collected" + # this loop must be run after the full depsmap has been populated - for (pkg, pkg_exts) in pkg_exts_map - # find any packages that depend on the extension(s)'s deps and replace those deps in their deps list with the extension(s), - # basically injecting the extension into the precompile order in the graph, to avoid race to precompile extensions - for (_pkg, deps) in depsmap # for each manifest dep - if !in(_pkg, keys(exts)) && pkg in deps # if not an extension and depends on pkg - append!(deps, pkg_exts) # add the package extensions to deps - filter!(!isequal(pkg), deps) # remove the pkg from deps + for ext in keys(exts) + ext_loadable_in_pkg = Dict{Base.PkgId,Bool}() + for pkg in keys(depsmap) + is_trigger = in(pkg, depsmap[ext]) + is_extension = in(pkg, keys(exts)) + has_triggers = issubset(depsmap[ext], indirect_deps[pkg]) + ext_loadable_in_pkg[pkg] = !is_extension && has_triggers && !is_trigger + end + for (pkg, ext_loadable) in ext_loadable_in_pkg + if ext_loadable && !any((dep)->ext_loadable_in_pkg[dep], depsmap[pkg]) + # add an edge if the extension is loadable by pkg, and was not loadable in any + # of the pkg's dependencies + push!(depsmap[pkg], ext) end end end