From c3449dc2ac93ca707322af07f313bdee31fe1a94 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Tue, 26 Nov 2024 08:16:32 +0800 Subject: [PATCH 01/24] subtype: fast path for Type == TypeVar (#56640) close #56606 (cherry picked from commit 7df1dfa2f1a51755eb35a2b5e51c810674dc10ee) --- src/subtype.c | 42 ++++++++++++++++++++++++++++++++++++++++++ test/subtype.jl | 16 ++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/subtype.c b/src/subtype.c index 2017954392ba6..8c3049d445647 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1600,6 +1600,42 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t return sub; } +static int equal_var(jl_tvar_t *v, jl_value_t *x, jl_stenv_t *e) +{ + assert(e->Loffset == 0); + // Theoretically bounds change would be merged for union inputs. + // But intersection is not happy as splitting helps to avoid circular env. + assert(!e->intersection || !jl_is_uniontype(x)); + jl_varbinding_t *vb = lookup(e, v); + if (e->intersection && vb != NULL && vb->lb == vb->ub && jl_is_typevar(vb->lb)) + return equal_var((jl_tvar_t *)vb->lb, x, e); + record_var_occurrence(vb, e, 2); + if (vb == NULL) + return e->ignore_free || ( + local_forall_exists_subtype(x, v->lb, e, 2, !jl_has_free_typevars(x)) && + local_forall_exists_subtype(v->ub, x, e, 0, 0)); + if (!vb->right) + return local_forall_exists_subtype(x, vb->lb, e, 2, !jl_has_free_typevars(x)) && + local_forall_exists_subtype(vb->ub, x, e, 0, 0); + if (vb->lb == x) + return var_lt(v, x, e, 0); + if (!subtype_ccheck(x, vb->ub, e)) + return 0; + jl_value_t *lb = simple_join(vb->lb, x); + JL_GC_PUSH1(&lb); + if (!e->intersection || !jl_is_typevar(lb) || !reachable_var(lb, v, e)) + vb->lb = lb; + JL_GC_POP(); + if (vb->ub == x) + return 1; + if (!subtype_ccheck(vb->lb, x, e)) + return 0; + // skip `simple_meet` here as we have proven `x <: vb->ub` + if (!e->intersection || !reachable_var(x, v, e)) + vb->ub = x; + return 1; +} + static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) { if (obviously_egal(x, y)) return 1; @@ -1630,6 +1666,12 @@ static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) } } + if (e->Loffset == 0 && jl_is_typevar(y) && jl_is_type(x) && (!e->intersection || !jl_is_uniontype(x))) { + // Fastpath for Type == TypeVar. + // Avoid duplicated `<:` check between adjacent `var_gt` and `var_lt` + return equal_var((jl_tvar_t *)y, x, e); + } + jl_saved_unionstate_t oldLunions; push_unionstate(&oldLunions, &e->Lunions); int sub = local_forall_exists_subtype(x, y, e, 2, -1); diff --git a/test/subtype.jl b/test/subtype.jl index dfa1487eaa55d..ba7f86bb86a14 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2730,3 +2730,19 @@ let S = Dict{V,V} where {V}, @test A <: typeintersect(S, T) @test A <: typeintersect(T, S) end + +#issue 56606 +let + A = Tuple{Val{1}} + B = Tuple{Val} + for _ in 1:30 + A = Tuple{Val{A}} + B = Tuple{Val{<:B}} + end + @test A <: B +end +@testintersect( + Val{Tuple{Int,S,T}} where {S<:Any,T<:Vector{Vector{Int}}}, + Val{Tuple{T,R,S}} where {T,R<:Vector{T},S<:Vector{R}}, + Val{Tuple{Int, Vector{Int}, T}} where T<:Vector{Vector{Int}}, +) From f987397ba288f83fdf301eb029ce48bd7e67929d Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 10 Dec 2024 03:51:46 -0500 Subject: [PATCH 02/24] Fix test report alignment (#56789) (cherry picked from commit 32ea18ecfb545c6abae8013d133d683987b9e323) --- stdlib/Test/src/Test.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 4004e7289a305..b3711398c2e83 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -1198,7 +1198,7 @@ function print_test_results(ts::AbstractTestSet, depth_pad=0) duration_width = max(textwidth("Time"), textwidth(tc.duration)) # Calculate the alignment of the test result counts by # recursively walking the tree of test sets - align = max(get_alignment(ts, 0), textwidth("Test Summary:")) + align = max(get_alignment(ts, depth_pad), textwidth("Test Summary:")) # Print the outer test set header once printstyled(rpad("Test Summary:", align, " "), " |", " "; bold=true) if pass_width > 0 From a788519ee9584dd0b7994136beb499994855563d Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Tue, 10 Dec 2024 09:33:41 -0500 Subject: [PATCH 03/24] fix `exp(weirdNaN)` (#56784) Fixes https://github.com/JuliaLang/julia/issues/56782 Fix `exp(reinterpret(Float64, 0x7ffbb14880000000))` returning non-NaN value. This should have minimal performance impact since it's already in the fallback branch. (cherry picked from commit 19e06d39141e1e2f0f9c4664ba9b88867b587a6e) --- base/special/exp.jl | 2 ++ test/math.jl | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/base/special/exp.jl b/base/special/exp.jl index 8e940a4d85ad9..32de6b9be296d 100644 --- a/base/special/exp.jl +++ b/base/special/exp.jl @@ -216,6 +216,7 @@ end small_part = muladd(jU, expm1b_kernel(base, r), jL) + jU if !(abs(x) <= SUBNORM_EXP(base, T)) + isnan(x) && return x x >= MAX_EXP(base, T) && return Inf x <= MIN_EXP(base, T) && return 0.0 if k <= -53 @@ -243,6 +244,7 @@ end hi, lo = Base.canonicalize2(1.0, kern) small_part = fma(jU, hi, muladd(jU, (lo+xlo), very_small)) if !(abs(x) <= SUBNORM_EXP(base, T)) + isnan(x) && return x x >= MAX_EXP(base, T) && return Inf x <= MIN_EXP(base, T) && return 0.0 if k <= -53 diff --git a/test/math.jl b/test/math.jl index be134adb15d34..c48a0c7f56323 100644 --- a/test/math.jl +++ b/test/math.jl @@ -377,6 +377,10 @@ end end end +@testset "https://github.com/JuliaLang/julia/issues/56782" begin + @test isnan(exp(reinterpret(Float64, 0x7ffbb14880000000))) +end + @testset "test abstractarray trig functions" begin TAA = rand(2,2) TAA = (TAA + TAA')/2. From 9a488155ed2458ba77131db062896991e2806121 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 11 Dec 2024 16:59:25 -0500 Subject: [PATCH 04/24] Fix generate_precompile statement grouping & avoid defining new func (#56317) (cherry picked from commit 582585bfb0a5494d41fd28b47a9774d498c608d9) --- contrib/generate_precompile.jl | 100 ++++++++++++++++----------------- 1 file changed, 48 insertions(+), 52 deletions(-) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 49bcf3f43ea41..43b6f89712d2b 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -131,60 +131,56 @@ for match = Base._methods(+, (Int, Int), -1, Base.get_world_counter()) m = match.method delete!(push!(Set{Method}(), m), m) copy(Core.Compiler.retrieve_code_info(Core.Compiler.specialize_method(match), typemax(UInt))) - - empty!(Set()) - push!(push!(Set{Union{GlobalRef,Symbol}}(), :two), GlobalRef(Base, :two)) - (setindex!(Dict{String,Base.PkgId}(), Base.PkgId(Base), "file.jl"))["file.jl"] - (setindex!(Dict{Symbol,Vector{Int}}(), [1], :two))[:two] - (setindex!(Dict{Base.PkgId,String}(), "file.jl", Base.PkgId(Base)))[Base.PkgId(Base)] - (setindex!(Dict{Union{GlobalRef,Symbol}, Vector{Int}}(), [1], :two))[:two] - (setindex!(IdDict{Type, Union{Missing, Vector{Tuple{LineNumberNode, Expr}}}}(), missing, Int))[Int] - Dict{Symbol, Union{Nothing, Bool, Symbol}}(:one => false)[:one] - Dict(Base => [:(1+1)])[Base] - Dict(:one => [1])[:one] - Dict("abc" => Set())["abc"] - pushfirst!([], sum) - get(Base.pkgorigins, Base.PkgId(Base), nothing) - sort!([1,2,3]) - unique!([1,2,3]) - cumsum([1,2,3]) - append!(Int[], BitSet()) - isempty(BitSet()) - delete!(BitSet([1,2]), 3) - deleteat!(Int32[1,2,3], [1,3]) - deleteat!(Any[1,2,3], [1,3]) - Core.svec(1, 2) == Core.svec(3, 4) - any(t->t[1].line > 1, [(LineNumberNode(2,:none), :(1+1))]) - - # Code loading uses this - sortperm(mtime.(readdir(".")), rev=true) - # JLLWrappers uses these - Dict{Base.UUID,Set{String}}()[Base.UUID("692b3bcd-3c85-4b1f-b108-f13ce0eb3210")] = Set{String}() - get!(Set{String}, Dict{Base.UUID,Set{String}}(), Base.UUID("692b3bcd-3c85-4b1f-b108-f13ce0eb3210")) - eachindex(IndexLinear(), Expr[]) - push!(Expr[], Expr(:return, false)) - vcat(String[], String[]) - k, v = (:hello => nothing) - Base.print_time_imports_report(Base) - Base.print_time_imports_report_init(Base) - - # Preferences uses these - get(Dict{String,Any}(), "missing", nothing) - delete!(Dict{String,Any}(), "missing") - for (k, v) in Dict{String,Any}() - println(k) - end - - # interactive startup uses this - write(IOBuffer(), "") - - # 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 +empty!(Set()) +push!(push!(Set{Union{GlobalRef,Symbol}}(), :two), GlobalRef(Base, :two)) +(setindex!(Dict{String,Base.PkgId}(), Base.PkgId(Base), "file.jl"))["file.jl"] +(setindex!(Dict{Symbol,Vector{Int}}(), [1], :two))[:two] +(setindex!(Dict{Base.PkgId,String}(), "file.jl", Base.PkgId(Base)))[Base.PkgId(Base)] +(setindex!(Dict{Union{GlobalRef,Symbol}, Vector{Int}}(), [1], :two))[:two] +(setindex!(IdDict{Type, Union{Missing, Vector{Tuple{LineNumberNode, Expr}}}}(), missing, Int))[Int] +Dict{Symbol, Union{Nothing, Bool, Symbol}}(:one => false)[:one] +Dict(Base => [:(1+1)])[Base] +Dict(:one => [1])[:one] +Dict("abc" => Set())["abc"] +pushfirst!([], sum) +get(Base.pkgorigins, Base.PkgId(Base), nothing) +sort!([1,2,3]) +unique!([1,2,3]) +cumsum([1,2,3]) +append!(Int[], BitSet()) +isempty(BitSet()) +delete!(BitSet([1,2]), 3) +deleteat!(Int32[1,2,3], [1,3]) +deleteat!(Any[1,2,3], [1,3]) +Core.svec(1, 2) == Core.svec(3, 4) +any(t->t[1].line > 1, [(LineNumberNode(2,:none), :(1+1))]) + +# Code loading uses this +sortperm(mtime.(readdir(".")), rev=true) +# JLLWrappers uses these +Dict{Base.UUID,Set{String}}()[Base.UUID("692b3bcd-3c85-4b1f-b108-f13ce0eb3210")] = Set{String}() +get!(Set{String}, Dict{Base.UUID,Set{String}}(), Base.UUID("692b3bcd-3c85-4b1f-b108-f13ce0eb3210")) +eachindex(IndexLinear(), Expr[]) +push!(Expr[], Expr(:return, false)) +vcat(String[], String[]) +k, v = (:hello => nothing) +Base.print_time_imports_report(Base) +Base.print_time_imports_report_init(Base) + +# Preferences uses these +get(Dict{String,Any}(), "missing", nothing) +delete!(Dict{String,Any}(), "missing") +for (k, v) in Dict{String,Any}() + println(k) +end + +# interactive startup uses this +write(IOBuffer(), "") + +# precompile @time report generation and printing +@time @eval Base.Experimental.@force_compile """ julia_exepath() = joinpath(Sys.BINDIR, Base.julia_exename()) From 3fb7f2ed7c50687ce10614f26f237319ca57f9c6 Mon Sep 17 00:00:00 2001 From: William Moses Date: Thu, 12 Dec 2024 07:46:25 -0600 Subject: [PATCH 05/24] Fix partially_inline for unreachable (#56787) (cherry picked from commit 9118ea7565feae13d5b47654a3c245c0df36e753) --- base/meta.jl | 5 +++++ test/meta.jl | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/base/meta.jl b/base/meta.jl index e648df29c12f9..78547aa305555 100644 --- a/base/meta.jl +++ b/base/meta.jl @@ -363,10 +363,15 @@ function _partially_inline!(@nospecialize(x), slot_replacements::Vector{Any}, return x end if isa(x, Core.ReturnNode) + # Unreachable doesn't have val defined + if !isdefined(x, :val) + return x + else return Core.ReturnNode( _partially_inline!(x.val, slot_replacements, type_signature, static_param_values, slot_offset, statement_offset, boundscheck), ) + end end if isa(x, Core.GotoIfNot) return Core.GotoIfNot( diff --git a/test/meta.jl b/test/meta.jl index 5fb1ebc0d3647..2b235d3cc1b0d 100644 --- a/test/meta.jl +++ b/test/meta.jl @@ -276,6 +276,11 @@ ci = code_lowered(g, Tuple{Val{true}})[1] @test Meta.partially_inline!(copy(ci.code), Any[isdefined_globalref, 1], Tuple{typeof(isdefined_globalref), Int}, [], 0, 0, :propagate)[1] == Expr(:isdefined, GlobalRef(Base, :foo)) + withunreachable(s::String) = sin(s) + ci = code_lowered(withunreachable, Tuple{String})[1] + ci.code[end] = Core.ReturnNode() + @test Meta.partially_inline!(copy(ci.code), Any[withunreachable, "foo"], Tuple{typeof(withunreachable), String}, + [], 0, 0, :propagate)[end] == Core.ReturnNode() end @testset "Base.Meta docstrings" begin From 790081c46f38d73790122ef9f4477027dca18497 Mon Sep 17 00:00:00 2001 From: KristofferC Date: Mon, 16 Dec 2024 13:04:43 +0100 Subject: [PATCH 06/24] bump Pkg to latest 1.11 --- .../Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/md5 | 1 - .../Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/sha512 | 1 - .../Pkg-bac7775db1791313237cf73616869390b4c42500.tar.gz/md5 | 1 + .../Pkg-bac7775db1791313237cf73616869390b4c42500.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-bac7775db1791313237cf73616869390b4c42500.tar.gz/md5 create mode 100644 deps/checksums/Pkg-bac7775db1791313237cf73616869390b4c42500.tar.gz/sha512 diff --git a/deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/md5 b/deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/md5 deleted file mode 100644 index 15b698352e581..0000000000000 --- a/deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -219e6af925739b706b73d74f2059b6a4 diff --git a/deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/sha512 b/deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/sha512 deleted file mode 100644 index 1583b39817edc..0000000000000 --- a/deps/checksums/Pkg-64bf95a4847898cf70de54d0c861c877ed5c1595.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -23b24d5c1cbe6e16b2388e79c233877d77471d3766002e02d0160eb1020e11c42b0c300b70d85c6b89a11e5de83708a3165e40d44cc21f011286eb6f096f2496 diff --git a/deps/checksums/Pkg-bac7775db1791313237cf73616869390b4c42500.tar.gz/md5 b/deps/checksums/Pkg-bac7775db1791313237cf73616869390b4c42500.tar.gz/md5 new file mode 100644 index 0000000000000..638542de0bd36 --- /dev/null +++ b/deps/checksums/Pkg-bac7775db1791313237cf73616869390b4c42500.tar.gz/md5 @@ -0,0 +1 @@ +de072e265659daa77b2722bd82dfe638 diff --git a/deps/checksums/Pkg-bac7775db1791313237cf73616869390b4c42500.tar.gz/sha512 b/deps/checksums/Pkg-bac7775db1791313237cf73616869390b4c42500.tar.gz/sha512 new file mode 100644 index 0000000000000..2b76f08f175e1 --- /dev/null +++ b/deps/checksums/Pkg-bac7775db1791313237cf73616869390b4c42500.tar.gz/sha512 @@ -0,0 +1 @@ +cbc18ea8aab91a4025f5d6ed085ccb5ce1f0ec4be4e8edd86ce37a900eff80ba6fa7f6e216c3af4ed0b2ffc059f9cb999fbdc90980759d3e8d044bf0791fea55 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 6dee5ed981ffa..e025824567f3e 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.11 -PKG_SHA1 = 64bf95a4847898cf70de54d0c861c877ed5c1595 +PKG_SHA1 = bac7775db1791313237cf73616869390b4c42500 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 5b83838e5def60e59f90c86807f96faaa3b24e97 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Sun, 15 Dec 2024 13:37:43 +0100 Subject: [PATCH 07/24] xref `UnionAll` in the doc string of `where` (#56411) (cherry picked from commit 8924e6f6c4f61946e72fde783c59a7996d749051) --- base/docs/basedocs.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index be0ce0f216515..c1ab4857e2928 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -1460,7 +1460,7 @@ kw"new" """ where -The `where` keyword creates a type that is an iterated union of other types, over all +The `where` keyword creates a [`UnionAll`](@ref) type, which may be thought of as an iterated union of other types, over all values of some variable. For example `Vector{T} where T<:Real` includes all [`Vector`](@ref)s where the element type is some kind of `Real` number. From 6f6bc95ac1af6db8faaf88d6ebf6b137ce95dc06 Mon Sep 17 00:00:00 2001 From: Priynsh <119518987+Priynsh@users.noreply.github.com> Date: Wed, 18 Dec 2024 20:04:45 +0530 Subject: [PATCH 08/24] docs: fix edge case in rational number conversion `float(a//b)` (#56772) Fixes #56726 added the changes that were suggested. fixing the mistake. --------- Co-authored-by: Max Horn Co-authored-by: Chengyu Han (cherry picked from commit fc9e7c4a6a36d7d54ae7dd8b89ac14a8334a59c4) --- .../manual/complex-and-rational-numbers.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/doc/src/manual/complex-and-rational-numbers.md b/doc/src/manual/complex-and-rational-numbers.md index 9cab2ed1e4f24..d1d6ffeca245f 100644 --- a/doc/src/manual/complex-and-rational-numbers.md +++ b/doc/src/manual/complex-and-rational-numbers.md @@ -254,13 +254,30 @@ julia> float(3//4) ``` Conversion from rational to floating-point respects the following identity for any integral values -of `a` and `b`, with the exception of the two cases `b == 0` and `a == 0 && b < 0`: +of `a` and `b`, except when `a==0 && b <= 0`: ```jldoctest julia> a = 1; b = 2; julia> isequal(float(a//b), a/b) true + +julia> a, b = 0, 0 +(0, 0) + +julia> float(a//b) +ERROR: ArgumentError: invalid rational: zero(Int64)//zero(Int64) +Stacktrace: +[...] + +julia> a/b +NaN + +julia> a, b = 0, -1 +(0, -1) + +julia> float(a//b), a/b +(0.0, -0.0) ``` Constructing infinite rational values is acceptable: From aa276c331543a4f2104baf56bda5f8d392fd7def Mon Sep 17 00:00:00 2001 From: cossio Date: Wed, 18 Dec 2024 15:40:34 +0100 Subject: [PATCH 09/24] dict docs: document that ordering of keys/values/pairs match iterate (#56842) Fix #56841. Currently the documentation states that keys(dict) and values(dict) iterate in the same order. But it is not stated whether this is the same order as that used by pairs(dict), or when looping, for (k,v) in dict. This PR makes this guarantee explicit. (cherry picked from commit 796d823b4fbb3ec94c09f7022f9b614fc846dced) --- base/abstractdict.jl | 12 ++++++++---- test/dict.jl | 7 +++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/base/abstractdict.jl b/base/abstractdict.jl index 62a5b3ee9e1b0..f291b8a582cf6 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -88,8 +88,8 @@ Return an iterator over all keys in a dictionary. When the keys are stored internally in a hash table, as is the case for `Dict`, the order in which they are returned may vary. -But `keys(a)` and `values(a)` both iterate `a` and -return the elements in the same order. +But `keys(a)`, `values(a)` and `pairs(a)` all iterate `a` +and return the elements in the same order. # Examples ```jldoctest @@ -114,8 +114,8 @@ Return an iterator over all values in a collection. When the values are stored internally in a hash table, as is the case for `Dict`, the order in which they are returned may vary. -But `keys(a)` and `values(a)` both iterate `a` and -return the elements in the same order. +But `keys(a)`, `values(a)` and `pairs(a)` all iterate `a` +and return the elements in the same order. # Examples ```jldoctest @@ -138,6 +138,10 @@ values(a::AbstractDict) = ValueIterator(a) Return an iterator over `key => value` pairs for any collection that maps a set of keys to a set of values. This includes arrays, where the keys are the array indices. +When the entries are stored internally in a hash table, +as is the case for `Dict`, the order in which they are returned may vary. +But `keys(a)`, `values(a)` and `pairs(a)` all iterate `a` +and return the elements in the same order. # Examples ```jldoctest diff --git a/test/dict.jl b/test/dict.jl index ca8a598de0b81..d33123237b0a1 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -787,6 +787,13 @@ end [v for (k, v) in d] == [d[x[1]] for (i, x) in enumerate(d)] end +@testset "consistency of dict iteration order (issue #56841)" begin + dict = Dict(randn() => randn() for _ = 1:100) + @test all(zip(dict, keys(dict), values(dict), pairs(dict))) do (d, k, v, p) + d == p && first(d) == first(p) == k && last(d) == last(p) == v + end +end + @testset "generators, similar" begin d = Dict(:a=>"a") # TODO: restore when 0.7 deprecation is removed From 06a372f62ab258e4374a5c7aa2f54251a886a9ed Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:38:45 +0100 Subject: [PATCH 10/24] Extend `Base.rationalize` instead of defining new function (#56793) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #55886 accidentally created a new function `Base.MathConstants.rationalize` instead of extending `Base.rationalize`, which is the reason why `Base.rationalize(Int, Ο€)` isn’t constant-folded in Julia 1.10 and 1.11: ``` julia> @btime rationalize(Int,Ο€); 1.837 ns (0 allocations: 0 bytes) # v1.9: constant-folded 88.416 ΞΌs (412 allocations: 15.00 KiB) # v1.10: not constant-folded ``` This PR fixes that. It should probably be backported to 1.10 and 1.11. (cherry picked from commit d3c26b7ecce7e8319788bc0a035241d6dfd21ac0) --- base/mathconstants.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/mathconstants.jl b/base/mathconstants.jl index de6b98cea634d..d26f5115b5ccb 100644 --- a/base/mathconstants.jl +++ b/base/mathconstants.jl @@ -29,7 +29,7 @@ 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.@assume_effects :foldable function Base.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) From 35ffb65bd5789c1a36be1b7c8edf3c2b76dec878 Mon Sep 17 00:00:00 2001 From: Simeon David Schaub Date: Wed, 25 Dec 2024 23:26:15 +0100 Subject: [PATCH 11/24] fix precompilation error printing if `CI` is set (#56905) Co-authored-by: Ian Butterworth (cherry picked from commit e3f90c4062af3a40ee548203242744424f2029fa) --- base/precompilation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index c822955f26bdd..95aa643e62bd2 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -1069,7 +1069,7 @@ function _precompilepkgs(pkgs::Vector{String}, direct = strict ? "" : "direct " 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) + if isinteractive() plural1 = length(failed_deps) == 1 ? "y" : "ies" println(io, " ", color_string("$(length(failed_deps))", Base.error_color()), " dependenc$(plural1) errored.") println(io, " For a report of the errors see `julia> err`. To retry use `pkg> precompile`") From 3dd91a1b0e63e42b8247afd4223702d14ec4d188 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 22 Oct 2024 05:23:04 +0900 Subject: [PATCH 12/24] inference: fix inference error from constructing invalid `TypeVar` (#56264) - fixes JuliaLang/julia#56248 (cherry picked from commit 08d11d041b22fe90380e56be4fb4d44aaf46ec85) --- base/compiler/tfuncs.jl | 12 ++++++++++-- test/compiler/inference.jl | 8 ++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 97d492304f678..baa364a69609b 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -587,8 +587,16 @@ add_tfunc(svec, 0, INT_INF, @nospecs((𝕃::AbstractLattice, args...)->SimpleVec return TypeVar end end - tv = TypeVar(nval, lb, ub) - return PartialTypeVar(tv, lb_certain, ub_certain) + lb_valid = lb isa Type || lb isa TypeVar + ub_valid = ub isa Type || ub isa TypeVar + if lb_valid && ub_valid + tv = TypeVar(nval, lb, ub) + return PartialTypeVar(tv, lb_certain, ub_certain) + elseif !lb_valid && lb_certain + return Union{} + elseif !ub_valid && ub_certain + return Union{} + end end return TypeVar end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index eadbe719cc961..76f1de3a08f54 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -5685,3 +5685,11 @@ t255751 = Array{Float32, 3} issue55882_nfields(x::Union{T,Nothing}) where T<:Number = nfields(x) @test Base.infer_return_type(issue55882_nfields) <: Int + +# JuliaLang/julia#56248 +@test Base.infer_return_type() do + TypeVar(:Issue56248, 1) +end === Union{} +@test Base.infer_return_type() do + TypeVar(:Issue56248, Any, 1) +end === Union{} From d03159589dec4a0327b5bac8ce7811d112e8b3a9 Mon Sep 17 00:00:00 2001 From: David Gleich Date: Thu, 24 Oct 2024 11:07:58 -0400 Subject: [PATCH 13/24] InteractiveUtils.jl: fixes issue where subtypes resolves bindings and causes deprecation warnings (#56306) The current version of `subtypes` will throw deprecation errors even if no one is using the deprecated bindings. A similar bug was fixed in Aqua.jl - https://github.com/JuliaTesting/Aqua.jl/pull/89/files See discussion here: - https://github.com/JuliaIO/ImageMagick.jl/issues/235 (for identifying the problem) - https://github.com/simonster/Reexport.jl/issues/42 (for pointing to the issue in Aqua.jl) - https://github.com/JuliaTesting/Aqua.jl/pull/89/files (for the fix in Aqua.jl) This adds the `isbindingresolved` test to the `subtypes` function to avoid throwing deprecation warnings. It also adds a test to check that this doesn't happen. --- On the current master branch (before the fix), the added test shows: ``` WARNING: using deprecated binding InternalModule.MyOldType in OuterModule. , use MyType instead. Subtypes and deprecations: Test Failed at /home/dgleich/devextern/julia/usr/share/julia/stdlib/v1.12/Test/src/Test.jl:932 Expression: isempty(stderr_content) Evaluated: isempty("WARNING: using deprecated binding InternalModule.MyOldType in OuterModule.\n, use MyType instead.\n") Test Summary: | Fail Total Time Subtypes and deprecations | 1 1 2.8s ERROR: LoadError: Some tests did not pass: 0 passed, 1 failed, 0 errored, 0 broken. in expression starting at /home/dgleich/devextern/julia/stdlib/InteractiveUtils/test/runtests.jl:841 ERROR: Package InteractiveUtils errored during testing ``` --- Using the results of this pull request: ``` @test_nowarn subtypes(Integer); ``` passes without error. The other tests pass too. (cherry picked from commit 20f933ae785423d3b94474e11453794c1fadc188) --- .../InteractiveUtils/src/InteractiveUtils.jl | 4 ++-- stdlib/InteractiveUtils/test/runtests.jl | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/stdlib/InteractiveUtils/src/InteractiveUtils.jl b/stdlib/InteractiveUtils/src/InteractiveUtils.jl index 519acfc8a2f33..661213567e0db 100644 --- a/stdlib/InteractiveUtils/src/InteractiveUtils.jl +++ b/stdlib/InteractiveUtils/src/InteractiveUtils.jl @@ -16,7 +16,7 @@ export apropos, edit, less, code_warntype, code_llvm, code_native, methodswith, import Base.Docs.apropos using Base: unwrap_unionall, rewrap_unionall, isdeprecated, Bottom, show_unquoted, summarysize, - signature_type, format_bytes + signature_type, format_bytes, isbindingresolved using Base.Libc using Markdown @@ -262,7 +262,7 @@ function _subtypes_in!(mods::Array, x::Type) m = pop!(mods) xt = xt::DataType for s in names(m, all = true) - if isdefined(m, s) && !isdeprecated(m, s) + if isbindingresolved(m, s) && !isdeprecated(m, s) && isdefined(m, s) t = getfield(m, s) dt = isa(t, UnionAll) ? unwrap_unionall(t) : t if isa(dt, DataType) diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index 346b111c785f7..72305ce66d308 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -728,3 +728,22 @@ end @testset "Docstrings" begin @test isempty(Docs.undocumented_names(InteractiveUtils)) end + +# issue https://github.com/JuliaIO/ImageMagick.jl/issues/235 +module OuterModule + module InternalModule + struct MyType + x::Int + end + + Base.@deprecate_binding MyOldType MyType + + export MyType + end + using .InternalModule + export MyType, MyOldType +end # module +@testset "Subtypes and deprecations" begin + using .OuterModule + @test_nowarn subtypes(Integer); +end From 0ff8f82893882673d3c6007da2970665b6b7bfbd Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 1 Nov 2024 15:02:11 +0900 Subject: [PATCH 14/24] irinterp: set `IR_FLAG_REFINED` for narrowed `PhiNode`s (#56391) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `adce_pass!` can transform a `Union`-type `PhiNode` into a narrower `PhiNode`, but in such cases, the `IR_FLAG_REFINED` flag isn’t set on that `PhiNode` statement. By setting this flag, irinterp can perform statement reprocessing using the narrowed `PhiNode`, enabling type stability in cases like JuliaLang/julia#56387. - fixes JuliaLang/julia#56387 (cherry picked from commit 671e1d87595d706ec2fa1659d990c507596f8fbc) --- base/compiler/ssair/passes.jl | 12 +++++++----- test/compiler/inference.jl | 12 ++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 152fc3be3d1c3..3f5943d36938d 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -2075,18 +2075,19 @@ function adce_pass!(ir::IRCode, inlining::Union{Nothing,InliningState}=nothing) unionphi = unionphis[i] phi = unionphi[1] t = unionphi[2] + inst = compact.result[phi] if t === Union{} - stmt = compact[SSAValue(phi)][:stmt]::PhiNode + stmt = inst[:stmt]::PhiNode kill_phi!(compact, phi_uses, 1:length(stmt.values), SSAValue(phi), stmt, true) made_changes = true continue elseif t === Any continue - elseif βŠ‘(𝕃ₒ, compact.result[phi][:type], t) - continue end + ⊏ = strictpartialorder(𝕃ₒ) + t ⊏ inst[:type] || continue to_drop = Int[] - stmt = compact[SSAValue(phi)][:stmt] + stmt = inst[:stmt] stmt === nothing && continue stmt = stmt::PhiNode for i = 1:length(stmt.values) @@ -2098,7 +2099,8 @@ function adce_pass!(ir::IRCode, inlining::Union{Nothing,InliningState}=nothing) push!(to_drop, i) end end - compact.result[phi][:type] = t + inst[:type] = t + add_flag!(inst, IR_FLAG_REFINED) # t ⊏ inst[:type] kill_phi!(compact, phi_uses, to_drop, SSAValue(phi), stmt, false) made_changes = true end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 76f1de3a08f54..02bd1daf8b82e 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -5693,3 +5693,15 @@ end === Union{} @test Base.infer_return_type() do TypeVar(:Issue56248, Any, 1) end === Union{} + +function issue56387(nt::NamedTuple, field::Symbol=:a) + NT = typeof(nt) + names = fieldnames(NT) + types = fieldtypes(NT) + index = findfirst(==(field), names) + if index === nothing + throw(ArgumentError("Field $field not found")) + end + types[index] +end +@test Base.infer_return_type(issue56387, (typeof((;a=1)),)) == Type{Int} From 45d32388f0534f7c6857b0e7a0221724f1c0bcf1 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Mon, 18 Nov 2024 15:40:02 -0500 Subject: [PATCH 15/24] lowering: don't reverse handler order in `(pop-handler-list ...)` (#55871) We were accidentally emitting a different pop order for `Expr(:leave, ...)` if you uncomment the `nothing` below: ```julia let src = Meta.@lower let try try return 1 catch end finally # nothing # <- uncomment me end end println.(filter(stmt->Base.isexpr(stmt, :leave), src.args[1].code)) nothing end ``` (cherry picked from commit deac82ad915db6c1fd6e9246fd4bb3310b7cd71f) --- src/julia-syntax.scm | 19 ++++++++++--------- test/syntax.jl | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 17092ca4cf751..280acf1223c5d 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -4363,15 +4363,16 @@ f(x) = yt(x) (define (pop-handler-list src-tokens dest-tokens lab) (if (eq? src-tokens dest-tokens) #f - (let loop ((s src-tokens) - (l '())) - (if (not (pair? s)) - (if (null? lab) - (error "Attempt to jump into catch block") - (error (string "cannot goto label \"" lab "\" inside try/catch block")))) - (if (eq? (cdr s) dest-tokens) - (cons (car s) l) - (loop (cdr s) (cons (car s) l)))))) + (reverse + (let loop ((s src-tokens) + (l '())) + (if (not (pair? s)) + (if (null? lab) + (error "Attempt to jump into catch block") + (error (string "cannot goto label \"" lab "\" inside try/catch block")))) + (if (eq? (cdr s) dest-tokens) + (cons (car s) l) + (loop (cdr s) (cons (car s) l))))))) (define (emit-return tail x) (define (emit- x) (let* ((tmp (if ((if (null? catch-token-stack) valid-ir-return? simple-atom?) x) diff --git a/test/syntax.jl b/test/syntax.jl index 1a2d61e821ef1..fe20189a56c81 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3691,6 +3691,42 @@ begin end @test Foreign54607.bar == 9 +# Test that lowering doesn't accidentally put a `Module` in the Method name slot +let src = @Meta.lower let capture=1 + global foo_lower_block + foo_lower_block() = capture +end + code = src.args[1].code + for i = length(code):-1:1 + expr = code[i] + Meta.isexpr(expr, :method) || continue + @test isa(expr.args[1], Union{GlobalRef, Symbol}) + end +end + +let src = Meta.@lower let + try + try + return 1 + catch + end + finally + nothing + end +end + code = src.args[1].code + for stmt in code + if Meta.isexpr(stmt, :leave) && length(stmt.args) > 1 + # Expr(:leave, ...) should list the arguments to pop from + # inner-most scope to outer-most + @test issorted(Int[ + (arg::Core.SSAValue).id + for arg in stmt.args + ]; rev=true) + end + end +end + # Test that globals can be `using`'d even if they are not yet defined module UndefGlobal54954 global theglobal54954::Int From 2adfacc406ca1604f365c8ec5c1209e578c84052 Mon Sep 17 00:00:00 2001 From: KristofferC Date: Thu, 2 Jan 2025 14:14:22 +0100 Subject: [PATCH 16/24] avoid memory leak in mallocarrays linked list --- src/gc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gc.c b/src/gc.c index fd9ad71d8afe0..d85c20c370eb0 100644 --- a/src/gc.c +++ b/src/gc.c @@ -1255,8 +1255,7 @@ static void sweep_malloced_memory(void) JL_NOTSAFEPOINT *pma = nxt; int isaligned = (uintptr_t)ma->a & 1; jl_gc_free_memory(a, isaligned); - ma->next = ptls2->heap.mafreelist; - ptls2->heap.mafreelist = ma; + free(ma); } gc_time_count_mallocd_memory(bits); ma = nxt; From 905e39a59a1c6fbcaf78fb0b05a120384a303398 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 17 Dec 2024 20:00:26 +0000 Subject: [PATCH 17/24] precompileplkgs: release parallel limiter when waiting for another process (#56844) (cherry picked from commit 37740148e1120ec833d6648dde245c0c5b79d76e) --- base/precompilation.jl | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/base/precompilation.jl b/base/precompilation.jl index 95aa643e62bd2..4c9847b61090d 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -920,7 +920,7 @@ function _precompilepkgs(pkgs::Vector{String}, try # allows processes to wait if another process is precompiling a given package to # a functionally identical package cache (except for preferences, which may differ) - t = @elapsed ret = precompile_pkgs_maybe_cachefile_lock(io, print_lock, fancyprint, pkg_config, pkgspidlocked, hascolor) do + t = @elapsed ret = precompile_pkgs_maybe_cachefile_lock(io, print_lock, fancyprint, pkg_config, pkgspidlocked, hascolor, parallel_limiter) do Base.with_logger(Base.NullLogger()) do # The false here means we ignore loaded modules, so precompile for a fresh session keep_loaded_modules = false @@ -1101,7 +1101,7 @@ function _color_string(cstr::String, col::Union{Int64, Symbol}, hascolor) end # Can be merged with `maybe_cachefile_lock` in loading? -function precompile_pkgs_maybe_cachefile_lock(f, io::IO, print_lock::ReentrantLock, fancyprint::Bool, pkg_config, pkgspidlocked, hascolor) +function precompile_pkgs_maybe_cachefile_lock(f, io::IO, print_lock::ReentrantLock, fancyprint::Bool, pkg_config, pkgspidlocked, hascolor, parallel_limiter::Base.Semaphore) pkg, config = pkg_config flags, cacheflags = config FileWatching = Base.loaded_modules[Base.PkgId(Base.UUID("7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"), "FileWatching")] @@ -1122,15 +1122,21 @@ function precompile_pkgs_maybe_cachefile_lock(f, io::IO, print_lock::ReentrantLo !fancyprint && lock(print_lock) do println(io, " ", pkg.name, _color_string(" Being precompiled by $(pkgspidlocked[pkg_config])", Base.info_color(), hascolor)) end - # wait until the lock is available - FileWatching.mkpidlock(pidfile; stale_age) do - # double-check in case the other process crashed or the lock expired - if Base.isprecompiled(pkg; ignore_loaded=true, flags=cacheflags) # don't use caches for this as the env state will have changed - return nothing # returning nothing indicates a process waited for another - else - delete!(pkgspidlocked, pkg_config) - return f() # precompile - end + Base.release(parallel_limiter) # release so other work can be done while waiting + try + # wait until the lock is available + @invokelatest Base.mkpidlock_hook(() -> begin + # double-check in case the other process crashed or the lock expired + if Base.isprecompiled(pkg; ignore_loaded=true, flags=cacheflags) # don't use caches for this as the env state will have changed + return nothing # returning nothing indicates a process waited for another + else + delete!(pkgspidlocked, pkg_config) + Base.acquire(f, parallel_limiter) # precompile + end + end, + pidfile; stale_age) + finally + Base.acquire(parallel_limiter) # re-acquire so the outer release is balanced end end return cachefile From 1b75f965a771ad8cd5222bdb200780912096db0c Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 22 Nov 2024 09:42:11 -0500 Subject: [PATCH 18/24] fix jl_mutex_lock deadlock under rr (#56644) (cherry picked from commit bdf78c9ece6f46d71ef78801410deb6fe99af642) --- src/gc.c | 6 +++--- src/julia_internal.h | 9 ++++----- src/safepoint.c | 34 +++++++++++++++++----------------- src/signals-unix.c | 2 +- src/signals-win.c | 2 +- src/threading.c | 17 ++++++++++------- 6 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/gc.c b/src/gc.c index d85c20c370eb0..c5bdb9eead8e4 100644 --- a/src/gc.c +++ b/src/gc.c @@ -3853,10 +3853,10 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) jl_atomic_store_release(&ptls->gc_state, JL_GC_STATE_WAITING); // `jl_safepoint_start_gc()` makes sure only one thread can run the GC. uint64_t t0 = jl_hrtime(); - if (!jl_safepoint_start_gc()) { + if (!jl_safepoint_start_gc(ct)) { // either another thread is running GC, or the GC got disabled just now. jl_gc_state_set(ptls, old_state, JL_GC_STATE_WAITING); - jl_safepoint_wait_thread_resume(); // block in thread-suspend now if requested, after clearing the gc_state + jl_safepoint_wait_thread_resume(ct); // block in thread-suspend now if requested, after clearing the gc_state return; } @@ -3910,7 +3910,7 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) jl_safepoint_end_gc(); jl_gc_state_set(ptls, old_state, JL_GC_STATE_WAITING); JL_PROBE_GC_END(); - jl_safepoint_wait_thread_resume(); // block in thread-suspend now if requested, after clearing the gc_state + jl_safepoint_wait_thread_resume(ct); // block in thread-suspend now if requested, after clearing the gc_state // Only disable finalizers on current thread // Doing this on all threads is racy (it's impossible to check diff --git a/src/julia_internal.h b/src/julia_internal.h index 4050410a1b1b2..1c2d071d1a6cd 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -967,7 +967,7 @@ void jl_safepoint_init(void); // before calling this function. If the calling thread is to run the GC, // it should also wait for the mutator threads to hit a safepoint **AFTER** // this function returns -int jl_safepoint_start_gc(void); +int jl_safepoint_start_gc(jl_task_t *ct); // Can only be called by the thread that have got a `1` return value from // `jl_safepoint_start_gc()`. This disables the safepoint (for GC, // the `mprotect` may not be removed if there's pending SIGINT) and wake @@ -977,9 +977,8 @@ void jl_safepoint_end_gc(void); // Wait for the GC to finish // This function does **NOT** modify the `gc_state` to inform the GC thread // The caller should set it **BEFORE** calling this function. -void jl_safepoint_wait_gc(void) JL_NOTSAFEPOINT; -void jl_safepoint_wait_thread_resume(void) JL_NOTSAFEPOINT; - +void jl_safepoint_wait_gc(jl_task_t *ct) JL_NOTSAFEPOINT; +void jl_safepoint_wait_thread_resume(jl_task_t *ct) JL_NOTSAFEPOINT; // Set pending sigint and enable the mechanisms to deliver the sigint. void jl_safepoint_enable_sigint(void); // If the safepoint is enabled to deliver sigint, disable it @@ -1006,7 +1005,7 @@ JL_DLLEXPORT void jl_pgcstack_getkey(jl_get_pgcstack_func **f, jl_pgcstack_key_t extern pthread_mutex_t in_signal_lock; #endif -void jl_set_gc_and_wait(void); // n.b. not used on _OS_DARWIN_ +void jl_set_gc_and_wait(jl_task_t *ct); // n.b. not used on _OS_DARWIN_ // Query if a Julia object is if a permalloc region (due to part of a sys- pkg-image) STATIC_INLINE size_t n_linkage_blobs(void) JL_NOTSAFEPOINT diff --git a/src/safepoint.c b/src/safepoint.c index 22cda0a89444d..b60745d1eb76f 100644 --- a/src/safepoint.c +++ b/src/safepoint.c @@ -158,17 +158,16 @@ void jl_gc_wait_for_the_world(jl_ptls_t* gc_all_tls_states, int gc_n_threads) } } -int jl_safepoint_start_gc(void) +int jl_safepoint_start_gc(jl_task_t *ct) { // The thread should have just set this before entry - assert(jl_atomic_load_relaxed(&jl_current_task->ptls->gc_state) == JL_GC_STATE_WAITING); + assert(jl_atomic_load_relaxed(&ct->ptls->gc_state) == JL_GC_STATE_WAITING); uv_mutex_lock(&safepoint_lock); uv_cond_broadcast(&safepoint_cond_begin); // make sure we are permitted to run GC now (we might be required to stop instead) - jl_task_t *ct = jl_current_task; while (jl_atomic_load_relaxed(&ct->ptls->suspend_count)) { uv_mutex_unlock(&safepoint_lock); - jl_safepoint_wait_thread_resume(); + jl_safepoint_wait_thread_resume(ct); uv_mutex_lock(&safepoint_lock); } // In case multiple threads enter the GC at the same time, only allow @@ -178,7 +177,7 @@ int jl_safepoint_start_gc(void) uint32_t running = 0; if (!jl_atomic_cmpswap(&jl_gc_running, &running, 1)) { uv_mutex_unlock(&safepoint_lock); - jl_safepoint_wait_gc(); + jl_safepoint_wait_gc(ct); return 0; } // Foreign thread adoption disables the GC and waits for it to finish, however, that may @@ -213,9 +212,8 @@ void jl_safepoint_end_gc(void) uv_cond_broadcast(&safepoint_cond_end); } -void jl_set_gc_and_wait(void) // n.b. not used on _OS_DARWIN_ +void jl_set_gc_and_wait(jl_task_t *ct) // n.b. not used on _OS_DARWIN_ { - jl_task_t *ct = jl_current_task; // reading own gc state doesn't need atomic ops since no one else // should store to it. int8_t state = jl_atomic_load_relaxed(&ct->ptls->gc_state); @@ -223,18 +221,19 @@ void jl_set_gc_and_wait(void) // n.b. not used on _OS_DARWIN_ uv_mutex_lock(&safepoint_lock); uv_cond_broadcast(&safepoint_cond_begin); uv_mutex_unlock(&safepoint_lock); - jl_safepoint_wait_gc(); + jl_safepoint_wait_gc(ct); jl_atomic_store_release(&ct->ptls->gc_state, state); - jl_safepoint_wait_thread_resume(); // block in thread-suspend now if requested, after clearing the gc_state + jl_safepoint_wait_thread_resume(ct); // block in thread-suspend now if requested, after clearing the gc_state } // this is the core of jl_set_gc_and_wait -void jl_safepoint_wait_gc(void) JL_NOTSAFEPOINT +void jl_safepoint_wait_gc(jl_task_t *ct) JL_NOTSAFEPOINT { - jl_task_t *ct = jl_current_task; (void)ct; - JL_TIMING_SUSPEND_TASK(GC_SAFEPOINT, ct); - // The thread should have set this is already - assert(jl_atomic_load_relaxed(&ct->ptls->gc_state) != JL_GC_STATE_UNSAFE); + if (ct) { + JL_TIMING_SUSPEND_TASK(GC_SAFEPOINT, ct); + // The thread should have set this is already + assert(jl_atomic_load_relaxed(&ct->ptls->gc_state) != JL_GC_STATE_UNSAFE); + } // Use normal volatile load in the loop for speed until GC finishes. // Then use an acquire load to make sure the GC result is visible on this thread. while (jl_atomic_load_relaxed(&jl_gc_running) || jl_atomic_load_acquire(&jl_gc_running)) { @@ -249,9 +248,8 @@ void jl_safepoint_wait_gc(void) JL_NOTSAFEPOINT } // equivalent to jl_set_gc_and_wait, but waiting on resume-thread lock instead -void jl_safepoint_wait_thread_resume(void) +void jl_safepoint_wait_thread_resume(jl_task_t *ct) { - jl_task_t *ct = jl_current_task; // n.b. we do not permit a fast-path here that skips the lock acquire since // we otherwise have no synchronization point to ensure that this thread // will observe the change to the safepoint, even though the other thread @@ -305,7 +303,9 @@ int jl_safepoint_suspend_thread(int tid, int waitstate) // not, so assume it is running GC and wait for GC to finish first. // It will be unable to reenter helping with GC because we have // changed its safepoint page. - jl_set_gc_and_wait(); + uv_mutex_unlock(&safepoint_lock); + jl_set_gc_and_wait(jl_current_task); + uv_mutex_lock(&safepoint_lock); } while (jl_atomic_load_acquire(&ptls2->suspend_count) != 0) { int8_t state2 = jl_atomic_load_acquire(&ptls2->gc_state); diff --git a/src/signals-unix.c b/src/signals-unix.c index 2aafd335a68b8..a889483804e3e 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -365,7 +365,7 @@ JL_NO_ASAN static void segv_handler(int sig, siginfo_t *info, void *context) return; } if (sig == SIGSEGV && info->si_code == SEGV_ACCERR && jl_addr_is_safepoint((uintptr_t)info->si_addr) && !is_write_fault(context)) { - jl_set_gc_and_wait(); + jl_set_gc_and_wait(ct); // Do not raise sigint on worker thread if (jl_atomic_load_relaxed(&ct->tid) != 0) return; diff --git a/src/signals-win.c b/src/signals-win.c index a2980a0c063d9..bca62f21bcec9 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -250,7 +250,7 @@ LONG WINAPI jl_exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo) break; case EXCEPTION_ACCESS_VIOLATION: if (jl_addr_is_safepoint(ExceptionInfo->ExceptionRecord->ExceptionInformation[1])) { - jl_set_gc_and_wait(); + jl_set_gc_and_wait(ct); // Do not raise sigint on worker thread if (ptls->tid != 0) return EXCEPTION_CONTINUE_EXECUTION; diff --git a/src/threading.c b/src/threading.c index dfc75b0af581e..b342bddc02b6a 100644 --- a/src/threading.c +++ b/src/threading.c @@ -411,11 +411,9 @@ JL_DLLEXPORT jl_gcframe_t **jl_adopt_thread(void) { // `jl_init_threadtls` puts us in a GC unsafe region, so ensure GC isn't running. // we can't use a normal safepoint because we don't have signal handlers yet. - // we also can't use jl_safepoint_wait_gc because that assumes we're in a task. jl_atomic_fetch_add(&jl_gc_disable_counter, 1); - while (jl_atomic_load_acquire(&jl_gc_running)) { - jl_cpu_pause(); - } + // pass NULL as a special token to indicate we are running on an unmanaged task + jl_safepoint_wait_gc(NULL); // this check is coupled with the one in `jl_safepoint_wait_gc`, where we observe if a // foreign thread has asked to disable the GC, guaranteeing the order of events. @@ -855,15 +853,20 @@ void _jl_mutex_wait(jl_task_t *self, jl_mutex_t *lock, int safepoint) jl_profile_lock_acquired(lock); return; } - if (safepoint) { - jl_gc_safepoint_(self->ptls); - } if (jl_running_under_rr(0)) { // when running under `rr`, use system mutexes rather than spin locking + int8_t gc_state; + if (safepoint) + gc_state = jl_gc_safe_enter(self->ptls); uv_mutex_lock(&tls_lock); if (jl_atomic_load_relaxed(&lock->owner)) uv_cond_wait(&cond, &tls_lock); uv_mutex_unlock(&tls_lock); + if (safepoint) + jl_gc_safe_leave(self->ptls, gc_state); + } + else if (safepoint) { + jl_gc_safepoint_(self->ptls); } jl_cpu_suspend(); owner = jl_atomic_load_relaxed(&lock->owner); From bcb1f114003dc417098bda2f1ccc7ecab6e26609 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 30 Dec 2024 15:52:23 -0500 Subject: [PATCH 19/24] precompilepkgs: respect loaded dependencies when precompiling for load (#56901) (cherry picked from commit 27ebbf01ccc44d78694516ff6daf55af65b7439b) --- base/loading.jl | 4 ++-- base/precompilation.jl | 24 ++++++++++++++---------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 3575bc5c0753f..bd4e3f3344dc4 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1782,7 +1782,7 @@ function compilecache_path(pkg::PkgId; path = nothing isnothing(sourcepath) && error("Cannot locate source for $(repr("text/plain", pkg))") for path_to_try in cachepaths - staledeps = stale_cachefile(sourcepath, path_to_try, ignore_loaded = true, requested_flags=flags) + staledeps = stale_cachefile(sourcepath, path_to_try; ignore_loaded, requested_flags=flags) if staledeps === true continue end @@ -2555,7 +2555,7 @@ function _require(pkg::PkgId, env=nothing) parallel_precompile_attempted = true unlock(require_lock) try - Precompilation.precompilepkgs([pkg.name]; _from_loading=true) + Precompilation.precompilepkgs([pkg.name]; _from_loading=true, ignore_loaded=false) finally lock(require_lock) end diff --git a/base/precompilation.jl b/base/precompilation.jl index 4c9847b61090d..254bab05a07dc 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -404,11 +404,12 @@ 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, + ignore_loaded::Bool=true) # 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) + IOContext{IO}(io), fancyprint, ignore_loaded) end function _precompilepkgs(pkgs::Vector{String}, @@ -419,7 +420,8 @@ function _precompilepkgs(pkgs::Vector{String}, _from_loading::Bool, configs::Vector{Config}, io::IOContext{IO}, - fancyprint::Bool) + fancyprint::Bool, + ignore_loaded::Bool) requested_pkgs = copy(pkgs) # for understanding user intent time_start = time_ns() @@ -894,7 +896,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, cachepath_cache, cachepaths, sourcepath, flags=cacheflags) + is_stale = !Base.isprecompiled(pkg; ignore_loaded, stale_cache, cachepath_cache, cachepaths, sourcepath, flags=cacheflags) if !circular && is_stale Base.acquire(parallel_limiter) is_direct_dep = pkg in direct_deps @@ -920,10 +922,10 @@ function _precompilepkgs(pkgs::Vector{String}, try # allows processes to wait if another process is precompiling a given package to # a functionally identical package cache (except for preferences, which may differ) - t = @elapsed ret = precompile_pkgs_maybe_cachefile_lock(io, print_lock, fancyprint, pkg_config, pkgspidlocked, hascolor, parallel_limiter) do + t = @elapsed ret = precompile_pkgs_maybe_cachefile_lock(io, print_lock, fancyprint, pkg_config, pkgspidlocked, hascolor, parallel_limiter, ignore_loaded) do Base.with_logger(Base.NullLogger()) do - # The false here means we ignore loaded modules, so precompile for a fresh session - keep_loaded_modules = false + # whether to respect already loaded dependency versions + keep_loaded_modules = !ignore_loaded # 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 @@ -1008,9 +1010,11 @@ function _precompilepkgs(pkgs::Vector{String}, plural1 = length(configs) > 1 ? "dependency configurations" : n_loaded == 1 ? "dependency" : "dependencies" plural2 = n_loaded == 1 ? "a different version is" : "different versions are" plural3 = n_loaded == 1 ? "" : "s" + plural4 = n_loaded == 1 ? "this package" : "these packages" print(iostr, "\n ", color_string(string(n_loaded), Base.warn_color()), - " $(plural1) precompiled but $(plural2) currently loaded. Restart julia to access the new version$(plural3)" + " $(plural1) precompiled but $(plural2) currently loaded. Restart julia to access the new version$(plural3). \ + Otherwise, loading dependents of $(plural4) may trigger further precompilation to work with the unexpected version$(plural3)." ) end if !isempty(precomperr_deps) @@ -1101,7 +1105,7 @@ function _color_string(cstr::String, col::Union{Int64, Symbol}, hascolor) end # Can be merged with `maybe_cachefile_lock` in loading? -function precompile_pkgs_maybe_cachefile_lock(f, io::IO, print_lock::ReentrantLock, fancyprint::Bool, pkg_config, pkgspidlocked, hascolor, parallel_limiter::Base.Semaphore) +function precompile_pkgs_maybe_cachefile_lock(f, io::IO, print_lock::ReentrantLock, fancyprint::Bool, pkg_config, pkgspidlocked, hascolor, parallel_limiter::Base.Semaphore, ignore_loaded::Bool) pkg, config = pkg_config flags, cacheflags = config FileWatching = Base.loaded_modules[Base.PkgId(Base.UUID("7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"), "FileWatching")] @@ -1127,7 +1131,7 @@ function precompile_pkgs_maybe_cachefile_lock(f, io::IO, print_lock::ReentrantLo # wait until the lock is available @invokelatest Base.mkpidlock_hook(() -> begin # double-check in case the other process crashed or the lock expired - if Base.isprecompiled(pkg; ignore_loaded=true, flags=cacheflags) # don't use caches for this as the env state will have changed + if Base.isprecompiled(pkg; ignore_loaded, flags=cacheflags) # don't use caches for this as the env state will have changed return nothing # returning nothing indicates a process waited for another else delete!(pkgspidlocked, pkg_config) From 6e28217cb6e1b9dec9669c6c4a44d3c6cf694ca2 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 2 Jan 2025 14:13:16 -0500 Subject: [PATCH 20/24] Don't report only-inferred methods as recompiles (#56914) --- src/jitlayers.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index d7fef443771bc..b3ed1e069de16 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -500,7 +500,15 @@ jl_code_instance_t *jl_generate_fptr_impl(jl_method_instance_t *mi JL_PROPAGATES } else { // identify whether this is an invalidated method that is being recompiled - is_recompile = jl_atomic_load_relaxed(&mi->cache) != NULL; + // Is a recompile if there is cached code, and it was compiled (not only inferred) before + jl_code_instance_t *codeinst_old = jl_atomic_load_relaxed(&mi->cache); + while (codeinst_old != NULL) { + if (jl_atomic_load_relaxed(&codeinst_old->invoke) != NULL) { + is_recompile = 1; + break; + } + codeinst_old = jl_atomic_load_relaxed(&codeinst_old->next); + } } if (src == NULL && jl_is_method(mi->def.method) && jl_symbol_name(mi->def.method->name)[0] != '@') { From 9e397557ccfb2fa429a010bd6333e50082de0df6 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 11 Oct 2024 13:23:52 -0400 Subject: [PATCH 21/24] rearrange jl_delete_thread to be thread-safe (#56097) Prior to this, especially on macOS, the gc-safepoint here would cause the process to segfault as we had already freed the current_task state. Rearrange this code so that the GC interactions (except for the atomic store to current_task) are all handled before entering GC safe, and then signaling the thread is deleted (via setting current_task = NULL, published by jl_unlock_profile_wr to other threads) is last. ``` ERROR: Exception handler triggered on unmanaged thread. Process 53827 stopped * thread #5, stop reason = EXC_BAD_ACCESS (code=2, address=0x100018008) frame #0: 0x0000000100b74344 libjulia-internal.1.12.0.dylib`jl_delete_thread [inlined] jl_gc_state_set(ptls=0x000000011f8b3200, state='\x02', old_state=) at julia_threads.h:272:9 [opt] 269 assert(old_state != JL_GC_CONCURRENT_COLLECTOR_THREAD); 270 jl_atomic_store_release(&ptls->gc_state, state); 271 if (state == JL_GC_STATE_UNSAFE || old_state == JL_GC_STATE_UNSAFE) -> 272 jl_gc_safepoint_(ptls); 273 return old_state; 274 } 275 STATIC_INLINE int8_t jl_gc_state_save_and_set(jl_ptls_t ptls, Target 0: (julia) stopped. (lldb) up frame #1: 0x0000000100b74320 libjulia-internal.1.12.0.dylib`jl_delete_thread [inlined] jl_gc_state_save_and_set(ptls=0x000000011f8b3200, state='\x02') at julia_threads.h:278:12 [opt] 275 STATIC_INLINE int8_t jl_gc_state_save_and_set(jl_ptls_t ptls, 276 int8_t state) 277 { -> 278 return jl_gc_state_set(ptls, state, jl_atomic_load_relaxed(&ptls->gc_state)); 279 } 280 #ifdef __clang_gcanalyzer__ 281 // these might not be a safepoint (if they are no-op safe=>safe transitions), but we have to assume it could be (statically) (lldb) frame #2: 0x0000000100b7431c libjulia-internal.1.12.0.dylib`jl_delete_thread(value=0x000000011f8b3200) at threading.c:537:11 [opt] 534 ptls->root_task = NULL; 535 jl_free_thread_gc_state(ptls); 536 // then park in safe-region -> 537 (void)jl_gc_safe_enter(ptls); 538 } ``` (test incorporated into https://github.com/JuliaLang/julia/pull/55793) (cherry picked from commit 0d09f3d0baa8843bd41fed013dad3fd9d69ae28d, resolving conflicts from not having backported https://github.com/JuliaLang/julia/pull/52198) --- src/gc-stacks.c | 6 +++--- src/julia.h | 2 +- src/threading.c | 41 ++++++++++++++++++++++++----------------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/gc-stacks.c b/src/gc-stacks.c index 82094035349b3..7746aa12fa2bf 100644 --- a/src/gc-stacks.c +++ b/src/gc-stacks.c @@ -43,7 +43,7 @@ static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT } -static void free_stack(void *stkbuf, size_t bufsz) +static void free_stack(void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT { VirtualFree(stkbuf, 0, MEM_RELEASE); jl_atomic_fetch_add(&num_stack_mappings, -1); @@ -68,7 +68,7 @@ static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT return stk; } -static void free_stack(void *stkbuf, size_t bufsz) +static void free_stack(void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT { munmap(stkbuf, bufsz); jl_atomic_fetch_add(&num_stack_mappings, -1); @@ -110,7 +110,7 @@ static unsigned select_pool(size_t nb) JL_NOTSAFEPOINT } -static void _jl_free_stack(jl_ptls_t ptls, void *stkbuf, size_t bufsz) +static void _jl_free_stack(jl_ptls_t ptls, void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT { #ifdef _COMPILER_ASAN_ENABLED_ __asan_unpoison_stack_memory((uintptr_t)stkbuf, bufsz); diff --git a/src/julia.h b/src/julia.h index e97b61f85a230..e321fe0ef9ee5 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1043,7 +1043,7 @@ JL_DLLEXPORT jl_value_t *jl_gc_alloc_2w(void); JL_DLLEXPORT jl_value_t *jl_gc_alloc_3w(void); JL_DLLEXPORT jl_value_t *jl_gc_allocobj(size_t sz); JL_DLLEXPORT void *jl_malloc_stack(size_t *bufsz, struct _jl_task_t *owner) JL_NOTSAFEPOINT; -JL_DLLEXPORT void jl_free_stack(void *stkbuf, size_t bufsz); +JL_DLLEXPORT void jl_free_stack(void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_gc_use(jl_value_t *a); // Set GC memory trigger in bytes for greedy memory collecting JL_DLLEXPORT void jl_gc_set_max_memory(uint64_t max_mem); diff --git a/src/threading.c b/src/threading.c index b342bddc02b6a..d8b6b784c6108 100644 --- a/src/threading.c +++ b/src/threading.c @@ -443,6 +443,29 @@ static void jl_delete_thread(void *value) JL_NOTSAFEPOINT_ENTER // prior unsafe-region (before we let it release the stack memory) (void)jl_gc_unsafe_enter(ptls); scheduler_delete_thread(ptls); + // need to clear pgcstack and eh, but we can clear everything now too + jl_task_t *ct = jl_atomic_load_relaxed(&ptls->current_task); + jl_task_frame_noreturn(ct); + if (jl_set_task_tid(ptls->root_task, ptls->tid)) { + // the system will probably free this stack memory soon + // so prevent any other thread from accessing it later + if (ct != ptls->root_task) + jl_task_frame_noreturn(ptls->root_task); + } + else { + // Uh oh. The user cleared the sticky bit so it started running + // elsewhere, then called pthread_exit on this thread from another + // Task, which will free the stack memory of that root task soon. This + // is not recoverable. Though we could just hang here, a fatal message + // is likely better. + jl_safe_printf("fatal: thread exited from wrong Task.\n"); + abort(); + } + ptls->previous_exception = NULL; + // allow the page root_task is on to be freed + ptls->root_task = NULL; + // park in safe-region from here on (this may run GC again) + (void)jl_gc_safe_enter(ptls); // try to free some state we do not need anymore #ifndef _OS_WINDOWS_ void *signal_stack = ptls->signal_stack; @@ -481,21 +504,7 @@ static void jl_delete_thread(void *value) JL_NOTSAFEPOINT_ENTER #else pthread_mutex_lock(&in_signal_lock); #endif - // need to clear pgcstack and eh, but we can clear everything now too - jl_task_frame_noreturn(jl_atomic_load_relaxed(&ptls->current_task)); - if (jl_set_task_tid(ptls->root_task, ptls->tid)) { - // the system will probably free this stack memory soon - // so prevent any other thread from accessing it later - jl_task_frame_noreturn(ptls->root_task); - } - else { - // Uh oh. The user cleared the sticky bit so it started running - // elsewhere, then called pthread_exit on this thread. This is not - // recoverable. Though we could just hang here, a fatal message is better. - jl_safe_printf("fatal: thread exited from wrong Task.\n"); - abort(); - } - jl_atomic_store_relaxed(&ptls->current_task, NULL); // dead + jl_atomic_store_relaxed(&ptls->current_task, NULL); // indicate dead // finally, release all of the locks we had grabbed #ifdef _OS_WINDOWS_ jl_unlock_profile_wr(); @@ -506,8 +515,6 @@ static void jl_delete_thread(void *value) JL_NOTSAFEPOINT_ENTER #else pthread_mutex_unlock(&in_signal_lock); #endif - // then park in safe-region - (void)jl_gc_safe_enter(ptls); } //// debugging hack: if we are exiting too fast for error message printing on threads, From bdf8219ee80557ea6035b421b00d91b1174234f2 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 9 Dec 2024 16:41:30 -0500 Subject: [PATCH 22/24] precompile: don't waste memory on useless inferred code (#56749) We never have a reason to reference this data again since we already have native code generated for it, so it is simply wasting memory and download space. $ du -sh {old,new}/usr/share/julia/compiled 256M old 227M new (cherry picked from commit dfe6a13e5038c8cbe0f1720d190629225ec1a19b) --- base/compiler/effects.jl | 1 + src/codegen.cpp | 8 ++++---- src/staticdata.c | 30 ++++++++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index 166df78f3130c..ece549eda7a6d 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -329,6 +329,7 @@ is_inaccessiblemem_or_argmemonly(effects::Effects) = effects.inaccessiblememonly is_consistent_overlay(effects::Effects) = effects.nonoverlayed === CONSISTENT_OVERLAY +# (sync this with codegen.cpp and staticdata.c effects_foldable functions) function encode_effects(e::Effects) return ((e.consistent % UInt32) << 0) | ((e.effect_free % UInt32) << 3) | diff --git a/src/codegen.cpp b/src/codegen.cpp index a0a4136ca6224..244bee17b2bbe 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -9674,10 +9674,10 @@ jl_llvm_functions_t jl_emit_codeinst( // Julia-level optimization will never need to see it else if (jl_is_method(def) && // don't delete toplevel code inferred != jl_nothing && // and there is something to delete (test this before calling jl_ir_inlining_cost) - !effects_foldable(codeinst->ipo_purity_bits) && // don't delete code we may want for irinterp - ((jl_ir_inlining_cost(inferred) == UINT16_MAX) || // don't delete inlineable code - jl_atomic_load_relaxed(&codeinst->invoke) == jl_fptr_const_return_addr) && // unless it is constant - !(params.imaging_mode || jl_options.incremental)) { // don't delete code when generating a precompile file + ((!effects_foldable(codeinst->ipo_purity_bits) && // don't delete code we may want for irinterp + (jl_ir_inlining_cost(inferred) == UINT16_MAX) && // don't delete inlineable code + !jl_generating_output()) || // don't delete code when generating a precompile file, trading memory in the short term for avoiding likely duplicating inference work for aotcompile + jl_atomic_load_relaxed(&codeinst->invoke) == jl_fptr_const_return_addr)) { // unless it is constant (although this shouldn't have had code in the first place) jl_atomic_store_release(&codeinst->inferred, jl_nothing); } } diff --git a/src/staticdata.c b/src/staticdata.c index 76bb488731a92..2fa500d053d4e 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -725,6 +725,16 @@ static uintptr_t jl_fptr_id(void *fptr) return *(uintptr_t*)pbp; } +static int effects_foldable(uint32_t effects) +{ + // N.B.: This needs to be kept in sync with Core.Compiler.is_foldable(effects, true) + return ((effects & 0x7) == 0) && // is_consistent(effects) + (((effects >> 10) & 0x03) == 0) && // is_noub(effects) + (((effects >> 3) & 0x03) == 0) && // is_effect_free(effects) + ((effects >> 6) & 0x01); // is_terminates(effects) +} + + // `jl_queue_for_serialization` adds items to `serialization_order` #define jl_queue_for_serialization(s, v) jl_queue_for_serialization_((s), (jl_value_t*)(v), 1, 0) static void jl_queue_for_serialization_(jl_serializer_state *s, jl_value_t *v, int recursive, int immediate) JL_GC_DISABLED; @@ -838,8 +848,24 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ // TODO: if (ci in ci->defs->cache) record_field_change((jl_value_t**)&ci->next, NULL); } - if (jl_atomic_load_relaxed(&ci->inferred) && !is_relocatable_ci(&relocatable_ext_cis, ci)) - record_field_change((jl_value_t**)&ci->inferred, jl_nothing); + jl_value_t *inferred = jl_atomic_load_relaxed(&ci->inferred); + if (inferred && inferred != jl_nothing) { // disregard if there is nothing here to delete (e.g. builtins, unspecialized) + if (!is_relocatable_ci(&relocatable_ext_cis, ci)) + record_field_change((jl_value_t**)&ci->inferred, jl_nothing); + else if (jl_is_method(ci->def->def.method) && // don't delete toplevel code + ci->def->def.method->source) { // don't delete code from optimized opaque closures that can't be reconstructed (and builtins) + if (jl_atomic_load_relaxed(&ci->max_world) != ~(size_t)0 || // delete all code that cannot run + jl_atomic_load_relaxed(&ci->invoke) == jl_fptr_const_return) { // delete all code that just returns a constant + record_field_change((jl_value_t**)&ci->inferred, jl_nothing); + } + else if (native_functions && // don't delete any code if making a ji file + !effects_foldable(ci->ipo_purity_bits) && // don't delete code we may want for irinterp + jl_ir_inlining_cost(inferred) == UINT16_MAX) { // don't delete inlineable code + // delete the code now: if we thought it was worth keeping, it would have been converted to object code + record_field_change((jl_value_t**)&ci->inferred, jl_nothing); + } + } + } } if (immediate) // must be things that can be recursively handled, and valid as type parameters From 8c63ce15cf37c68c2bd1f885aff0683cbe16486b Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 7 Jan 2025 14:34:12 +0100 Subject: [PATCH 23/24] fix backport of #56097 Co-authored-by: Jameson Nash --- src/safepoint.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/safepoint.c b/src/safepoint.c index b60745d1eb76f..d1012122e9541 100644 --- a/src/safepoint.c +++ b/src/safepoint.c @@ -303,9 +303,7 @@ int jl_safepoint_suspend_thread(int tid, int waitstate) // not, so assume it is running GC and wait for GC to finish first. // It will be unable to reenter helping with GC because we have // changed its safepoint page. - uv_mutex_unlock(&safepoint_lock); jl_set_gc_and_wait(jl_current_task); - uv_mutex_lock(&safepoint_lock); } while (jl_atomic_load_acquire(&ptls2->suspend_count) != 0) { int8_t state2 = jl_atomic_load_acquire(&ptls2->gc_state); From cbb7ebc257e28ddb15af21f34b6facc35c199909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Wed, 8 Jan 2025 14:09:14 +0000 Subject: [PATCH 24/24] [BinaryPlatforms] Parse `rc64`/`riscv64` triplets This is partial backport of #56105, only the part relative to parsing triplets. --- base/binaryplatforms.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/binaryplatforms.jl b/base/binaryplatforms.jl index e0ee107c4ec73..088d7b4c7e995 100644 --- a/base/binaryplatforms.jl +++ b/base/binaryplatforms.jl @@ -591,6 +591,7 @@ const arch_mapping = Dict( "armv7l" => "arm(v7l)?", # if we just see `arm-linux-gnueabihf`, we assume it's `armv7l` "armv6l" => "armv6l", "powerpc64le" => "p(ower)?pc64le", + "riscv64" => "(rv64|riscv64)", ) # Keep this in sync with `CPUID.ISAs_by_family` # These are the CPUID side of the microarchitectures targeted by GCC flags in BinaryBuilder.jl