From 96acd4d4e90647caab17af0f689723f5a2594cfd Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 2 Feb 2023 11:41:26 +0800 Subject: [PATCH 001/775] Subtype: avoid false alarm caused by eager `forall_exists_subtype`. (#48441) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Avoid earsing `Runion` within nested `forall_exists_subtype` If `Runion.more != 0` we‘d better not erase the local `Runion` as we need it if the subtyping fails after. This commit replaces `forall_exists_subtype` with a local version. It first tries `forall_exists_subtype` and estimates the "problem scale". If subtyping fails and the scale looks small then it switches to the slow path. TODO: At present, the "problem scale" only counts the number of checked `Lunion`s. But perhaps we need a more accurate result (e.g. sum of `Runion.depth`) * Change the reversed subtyping into a local check. Make sure we don't forget the bound in `env`. (And we can fuse `local_forall_exists_subtype`) * Optimization for non-union invariant parameter. --- src/subtype.c | 119 +++++++++++++++++++++++++++++++++--------------- test/subtype.jl | 13 ++++-- 2 files changed, 91 insertions(+), 41 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index a0896e9050ff2..9f6f6cb0add67 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -620,7 +620,7 @@ static jl_value_t *pick_union_element(jl_value_t *u JL_PROPAGATES_ROOT, jl_stenv return u; } -static int forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param); +static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param, int limit_slow); // subtype for variable bounds consistency check. needs its own forall/exists environment. static int subtype_ccheck(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) @@ -636,17 +636,7 @@ static int subtype_ccheck(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) if (x == (jl_value_t*)jl_any_type && jl_is_datatype(y)) return 0; jl_saved_unionstate_t oldLunions; push_unionstate(&oldLunions, &e->Lunions); - jl_saved_unionstate_t oldRunions; push_unionstate(&oldRunions, &e->Runions); - int sub; - e->Lunions.used = e->Runions.used = 0; - e->Runions.depth = 0; - e->Runions.more = 0; - e->Lunions.depth = 0; - e->Lunions.more = 0; - - sub = forall_exists_subtype(x, y, e, 0); - - pop_unionstate(&e->Runions, &oldRunions); + int sub = local_forall_exists_subtype(x, y, e, 0, 1); pop_unionstate(&e->Lunions, &oldLunions); return sub; } @@ -1431,6 +1421,72 @@ static int is_definite_length_tuple_type(jl_value_t *x) return k == JL_VARARG_NONE || k == JL_VARARG_INT; } +static int _forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param, int *count, int *noRmore); + +static int may_contain_union_decision(jl_value_t *x, jl_stenv_t *e, jl_typeenv_t *log) JL_NOTSAFEPOINT +{ + if (x == NULL || x == (jl_value_t*)jl_any_type || x == jl_bottom_type) + return 0; + if (jl_is_unionall(x)) + return may_contain_union_decision(((jl_unionall_t *)x)->body, e, log); + if (jl_is_datatype(x)) { + jl_datatype_t *xd = (jl_datatype_t *)x; + for (int i = 0; i < jl_nparams(xd); i++) { + jl_value_t *param = jl_tparam(xd, i); + if (jl_is_vararg(param)) + param = jl_unwrap_vararg(param); + if (may_contain_union_decision(param, e, log)) + return 1; + } + return 0; + } + if (!jl_is_typevar(x)) + return 1; + jl_typeenv_t *t = log; + while (t != NULL) { + if (x == (jl_value_t *)t->var) + return 1; + t = t->prev; + } + jl_typeenv_t newlog = { (jl_tvar_t*)x, NULL, log }; + jl_varbinding_t *xb = lookup(e, (jl_tvar_t *)x); + return may_contain_union_decision(xb ? xb->lb : ((jl_tvar_t *)x)->lb, e, &newlog) || + may_contain_union_decision(xb ? xb->ub : ((jl_tvar_t *)x)->ub, e, &newlog); +} + +static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param, int limit_slow) +{ + int16_t oldRmore = e->Runions.more; + int sub; + if (may_contain_union_decision(y, e, NULL) && pick_union_decision(e, 1) == 0) { + jl_saved_unionstate_t oldRunions; push_unionstate(&oldRunions, &e->Runions); + e->Lunions.used = e->Runions.used = 0; + e->Lunions.depth = e->Runions.depth = 0; + e->Lunions.more = e->Runions.more = 0; + int count = 0, noRmore = 0; + sub = _forall_exists_subtype(x, y, e, param, &count, &noRmore); + pop_unionstate(&e->Runions, &oldRunions); + // we should not try the slow path if `forall_exists_subtype` has tested all cases; + // Once limit_slow == 1, also skip it if + // 1) `forall_exists_subtype` return false + // 2) the left `Union` looks big + if (noRmore || (limit_slow && (count > 3 || !sub))) + e->Runions.more = oldRmore; + } + else { + // slow path + e->Lunions.used = 0; + while (1) { + e->Lunions.more = 0; + e->Lunions.depth = 0; + sub = subtype(x, y, e, param); + if (!sub || !next_union_state(e, 0)) + break; + } + } + return sub; +} + static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) { if (obviously_egal(x, y)) return 1; @@ -1449,33 +1505,13 @@ static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) } jl_saved_unionstate_t oldLunions; push_unionstate(&oldLunions, &e->Lunions); - e->Lunions.used = 0; - int sub; - - if (!jl_has_free_typevars(x) || !jl_has_free_typevars(y)) { - jl_saved_unionstate_t oldRunions; push_unionstate(&oldRunions, &e->Runions); - e->Runions.used = 0; - e->Runions.depth = 0; - e->Runions.more = 0; - e->Lunions.depth = 0; - e->Lunions.more = 0; - sub = forall_exists_subtype(x, y, e, 2); - - pop_unionstate(&e->Runions, &oldRunions); - } - else { - while (1) { - e->Lunions.more = 0; - e->Lunions.depth = 0; - sub = subtype(x, y, e, 2); - if (!sub || !next_union_state(e, 0)) - break; - } - } + int limit_slow = !jl_has_free_typevars(x) || !jl_has_free_typevars(y); + int sub = local_forall_exists_subtype(x, y, e, 2, limit_slow) && + local_forall_exists_subtype(y, x, e, 0, 0); pop_unionstate(&e->Lunions, &oldLunions); - return sub && subtype(y, x, e, 0); + return sub; } static int exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, jl_value_t *saved, jl_savedenv_t *se, int param) @@ -1502,7 +1538,7 @@ static int exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, jl_value_ } } -static int forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) +static int _forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param, int *count, int *noRmore) { // The depth recursion has the following shape, after simplification: // ∀₁ @@ -1515,8 +1551,12 @@ static int forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, in e->Lunions.used = 0; int sub; + if (count) *count = 0; + if (noRmore) *noRmore = 1; while (1) { sub = exists_subtype(x, y, e, saved, &se, param); + if (count) *count = (*count < 4) ? *count + 1 : 4; + if (noRmore) *noRmore = *noRmore && e->Runions.more == 0; if (!sub || !next_union_state(e, 0)) break; free_env(&se); @@ -1528,6 +1568,11 @@ static int forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, in return sub; } +static int forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) +{ + return _forall_exists_subtype(x, y, e, param, NULL, NULL); +} + static void init_stenv(jl_stenv_t *e, jl_value_t **env, int envsz) { e->vars = NULL; diff --git a/test/subtype.jl b/test/subtype.jl index 40c60670110fb..674608c4d0451 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -1484,6 +1484,8 @@ f24521(::Type{T}, ::Type{T}) where {T} = T @test !(Ref{Union{Int64, Val{Number}}} <: Ref{Union{Val{T}, T}} where T) @test !(Ref{Union{Ref{Number}, Int64}} <: Ref{Union{Ref{T}, T}} where T) @test !(Ref{Union{Val{Number}, Int64}} <: Ref{Union{Val{T}, T}} where T) +@test !(Val{Ref{Union{Int64, Ref{Number}}}} <: Val{S} where {S<:Ref{Union{Ref{T}, T}} where T}) +@test !(Tuple{Ref{Union{Int64, Ref{Number}}}} <: Tuple{S} where {S<:Ref{Union{Ref{T}, T}} where T}) # issue #26180 @test !(Ref{Union{Ref{Int64}, Ref{Number}}} <: Ref{Ref{T}} where T) @@ -2385,8 +2387,8 @@ abstract type P47654{A} end @test_broken typeintersect(Tuple{Vector{VT}, Vector{VT}} where {N1, VT<:AbstractVector{N1}}, Tuple{Vector{VN} where {N, VN<:AbstractVector{N}}, Vector{Vector{Float64}}}) !== Union{} #issue 40865 - @test_broken Tuple{Set{Ref{Int}}, Set{Ref{Int}}} <: Tuple{Set{KV}, Set{K}} where {K,KV<:Union{K,Ref{K}}} - @test_broken Tuple{Set{Val{Int}}, Set{Val{Int}}} <: Tuple{Set{KV}, Set{K}} where {K,KV<:Union{K,Val{K}}} + @test Tuple{Set{Ref{Int}}, Set{Ref{Int}}} <: Tuple{Set{KV}, Set{K}} where {K,KV<:Union{K,Ref{K}}} + @test Tuple{Set{Val{Int}}, Set{Val{Int}}} <: Tuple{Set{KV}, Set{K}} where {K,KV<:Union{K,Val{K}}} #issue 39099 A = Tuple{Tuple{Int, Int, Vararg{Int, N}}, Tuple{Int, Vararg{Int, N}}, Tuple{Vararg{Int, N}}} where N @@ -2420,8 +2422,7 @@ end # try to fool a greedy algorithm that picks X=Int, Y=String here @test Tuple{Ref{Union{Int,String}}, Ref{Union{Int,String}}} <: Tuple{Ref{Union{X,Y}}, Ref{X}} where {X,Y} -# this slightly more complex case has been broken since 1.0 (worked in 0.6) -@test_broken Tuple{Ref{Union{Int,String,Missing}}, Ref{Union{Int,String}}} <: Tuple{Ref{Union{X,Y}}, Ref{X}} where {X,Y} +@test Tuple{Ref{Union{Int,String,Missing}}, Ref{Union{Int,String}}} <: Tuple{Ref{Union{X,Y}}, Ref{X}} where {X,Y} @test !(Tuple{Any, Any, Any} <: Tuple{Any, Vararg{T}} where T) @@ -2435,3 +2436,7 @@ let A = Tuple{Type{T}, T} where T, C = Tuple{Type{MyType47877{W, V} where V<:Union{MyAbstract47877{W}, Base.BitInteger}}, MyType47877{W, V} where V<:Union{MyAbstract47877{W}, Base.BitInteger}} where W<:Base.BitInteger @test typeintersect(B, A) == C end + +let a = (isodd(i) ? Pair{Char, String} : Pair{String, String} for i in 1:2000) + @test Tuple{Type{Pair{Union{Char, String}, String}}, a...} <: Tuple{Type{Pair{K, V}}, Vararg{Pair{A, B} where B where A}} where V where K +end From e48a0c99bef949a84979c05dc33fd5578f684c1d Mon Sep 17 00:00:00 2001 From: Nicu Stiurca Date: Thu, 2 Feb 2023 02:09:03 -0600 Subject: [PATCH 002/775] Clarify usage of JULIA_PROJECT=@. envvar (#48492) When reading the docs, it's very easy to miss that there's a trailing dot in the magic string `@.` and that it's not just the singleton string `@` followed by a grammatical comma. I spent a very long time being frustrated by `export JULIA_PROJECT=@` not having any apparent effect. --- doc/src/manual/environment-variables.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/src/manual/environment-variables.md b/doc/src/manual/environment-variables.md index f29e5b7aaf8f7..a199112e934dd 100644 --- a/doc/src/manual/environment-variables.md +++ b/doc/src/manual/environment-variables.md @@ -78,6 +78,7 @@ and a global configuration search path of A directory path that indicates which project should be the initial active project. Setting this environment variable has the same effect as specifying the `--project` start-up option, but `--project` has higher precedence. If the variable is set to `@.` +(note the trailing dot) then Julia tries to find a project directory that contains `Project.toml` or `JuliaProject.toml` file from the current directory and its parents. See also the chapter on [Code Loading](@ref code-loading). From 458fbfa27814dab7072956537d1435caa6ac3dfa Mon Sep 17 00:00:00 2001 From: mikmoore <95002244+mikmoore@users.noreply.github.com> Date: Thu, 2 Feb 2023 08:13:47 -0700 Subject: [PATCH 003/775] faster isfinite for floats (#48462) Co-authored-by: mikmoore --- base/float.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/float.jl b/base/float.jl index 2677fd5dfba38..d9f35fdb329a9 100644 --- a/base/float.jl +++ b/base/float.jl @@ -619,7 +619,7 @@ See also: [`iszero`](@ref), [`isone`](@ref), [`isinf`](@ref), [`ismissing`](@ref isnan(x::AbstractFloat) = (x != x)::Bool isnan(x::Number) = false -isfinite(x::AbstractFloat) = x - x == 0 +isfinite(x::AbstractFloat) = !isnan(x - x) isfinite(x::Real) = decompose(x)[3] != 0 isfinite(x::Integer) = true From f2d9c0037e73085b40a0a17b69e71a2d04cbe99e Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Thu, 2 Feb 2023 15:15:11 -0300 Subject: [PATCH 004/775] gc: fix objarray generation computation (#48446) Fixes: #48405 Co-authored-by: Diogo Netto --- src/gc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gc.c b/src/gc.c index d1cc57cf787de..dc01af35c67c4 100644 --- a/src/gc.c +++ b/src/gc.c @@ -2242,7 +2242,7 @@ JL_DLLEXPORT int jl_gc_mark_queue_obj(jl_ptls_t ptls, jl_value_t *obj) JL_DLLEXPORT void jl_gc_mark_queue_objarray(jl_ptls_t ptls, jl_value_t *parent, jl_value_t **objs, size_t nobjs) { - uintptr_t nptr = (nobjs << 2) & (jl_astaggedvalue(parent)->bits.gc & 3); + uintptr_t nptr = (nobjs << 2) | (jl_astaggedvalue(parent)->bits.gc & 2); gc_mark_objarray(ptls, parent, objs, objs + nobjs, 1, nptr); } @@ -2540,7 +2540,7 @@ void gc_mark_loop_(jl_ptls_t ptls, jl_gc_markqueue_t *mq) while (1) { void *new_obj = (void *)gc_markqueue_pop(&ptls->mark_queue); // No more objects to mark - if (new_obj == NULL) { + if (__unlikely(new_obj == NULL)) { // TODO: work-stealing comes here... return; } From f35b753e042faf8c8b1460290a1a63ece0366325 Mon Sep 17 00:00:00 2001 From: Ujjwal Sarswat <76774914+vmpyr@users.noreply.github.com> Date: Fri, 3 Feb 2023 01:08:22 +0530 Subject: [PATCH 005/775] improve convert function for NamedTuples (#48476) Fix #47604 --- base/namedtuple.jl | 19 ++++++++++++++----- test/namedtuple.jl | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/base/namedtuple.jl b/base/namedtuple.jl index fe6f3f0e81ce3..dcd7855a402b7 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -176,11 +176,20 @@ empty(::NamedTuple) = NamedTuple() prevind(@nospecialize(t::NamedTuple), i::Integer) = Int(i)-1 nextind(@nospecialize(t::NamedTuple), i::Integer) = Int(i)+1 -convert(::Type{NamedTuple{names,T}}, nt::NamedTuple{names,T}) where {names,T<:Tuple} = nt -convert(::Type{NamedTuple{names}}, nt::NamedTuple{names}) where {names} = nt - -function convert(::Type{NamedTuple{names,T}}, nt::NamedTuple{names}) where {names,T<:Tuple} - NamedTuple{names,T}(T(nt))::NamedTuple{names,T} +convert(::Type{NT}, nt::NT) where {names, NT<:NamedTuple{names}} = nt +convert(::Type{NT}, nt::NT) where {names, T<:Tuple, NT<:NamedTuple{names,T}} = nt + +function convert(::Type{NT}, nt::NamedTuple{names}) where {names, T<:Tuple, NT<:NamedTuple{names,T}} + if !@isdefined T + # converting abstract NT to an abstract Tuple type, to a concrete NT1, is not straightforward, so this could just be an error, but we define it anyways + # _tuple_error(NT, nt) + T1 = Tuple{ntuple(i -> fieldtype(NT, i), Val(length(names)))...} + NT1 = NamedTuple{names, T1} + else + T1 = T + NT1 = NT + end + return NT1(T1(nt))::NT1::NT end if nameof(@__MODULE__) === :Base diff --git a/test/namedtuple.jl b/test/namedtuple.jl index b2101944d423b..039b93a216d47 100644 --- a/test/namedtuple.jl +++ b/test/namedtuple.jl @@ -76,6 +76,26 @@ let NT = NamedTuple{(:a,:b),Tuple{Int8,Int16}}, nt = (x=3,y=4) @test_throws MethodError convert(NT, nt) end +@testset "convert NamedTuple" begin + conv1 = convert(NamedTuple{(:a,),Tuple{I}} where I, (;a=1)) + @test conv1 === (a = 1,) + + conv2 = convert(NamedTuple{(:a,),Tuple{Any}}, (;a=1)) + @test conv2 === NamedTuple{(:a,), Tuple{Any}}((1,)) + + conv3 = convert(NamedTuple{(:a,),}, (;a=1)) + @test conv3 === (a = 1,) + + conv4 = convert(NamedTuple{(:a,),Tuple{I}} where I<:Unsigned, (;a=1)) + @test conv4 === NamedTuple{(:a,), Tuple{Unsigned}}((1,)) + + conv5 = convert(NamedTuple, (;a=1)) + @test conv1 === (a = 1,) + + conv_res = @test_throws MethodError convert(NamedTuple{(:a,),Tuple{I}} where I<:AbstractString, (;a=1)) + @test conv_res.value.f === convert && conv_res.value.args === (AbstractString, 1) +end + @test NamedTuple{(:a,:c)}((b=1,z=2,c=3,aa=4,a=5)) === (a=5, c=3) @test NamedTuple{(:a,)}(NamedTuple{(:b, :a), Tuple{Int, Union{Int,Nothing}}}((1, 2))) === NamedTuple{(:a,), Tuple{Union{Int,Nothing}}}((2,)) From f8d3fee2c266708576a904b72bcc6347a4e17671 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 3 Feb 2023 06:00:37 +0900 Subject: [PATCH 006/775] more precise return type descriptions for some methods in `Base` (#48484) --- base/file.jl | 4 ++-- base/libc.jl | 2 +- base/loading.jl | 4 ++-- base/path.jl | 10 +++++----- stdlib/InteractiveUtils/src/clipboard.jl | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/base/file.jl b/base/file.jl index b761e1d65ccb5..baccbdeaa5b07 100644 --- a/base/file.jl +++ b/base/file.jl @@ -32,7 +32,7 @@ export # get and set current directory """ - pwd() -> AbstractString + pwd() -> String Get the current working directory. @@ -1109,7 +1109,7 @@ function symlink(target::AbstractString, link::AbstractString; end """ - readlink(path::AbstractString) -> AbstractString + readlink(path::AbstractString) -> String Return the target location a symbolic link `path` points to. """ diff --git a/base/libc.jl b/base/libc.jl index 7d88e89bf605a..0a542ecbd1a82 100644 --- a/base/libc.jl +++ b/base/libc.jl @@ -260,7 +260,7 @@ getpid() = ccall(:uv_os_getpid, Int32, ()) ## network functions ## """ - gethostname() -> AbstractString + gethostname() -> String Get the local machine's host name. """ diff --git a/base/loading.jl b/base/loading.jl index e22142e0abe88..875127e995960 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2775,7 +2775,7 @@ end end """ - @__FILE__ -> AbstractString + @__FILE__ -> String Expand to a string with the path to the file containing the macrocall, or an empty string if evaluated by `julia -e `. @@ -2788,7 +2788,7 @@ macro __FILE__() end """ - @__DIR__ -> AbstractString + @__DIR__ -> String Expand to a string with the absolute path to the directory of the file containing the macrocall. diff --git a/base/path.jl b/base/path.jl index 73d91e60f8c03..1fac47432cda3 100644 --- a/base/path.jl +++ b/base/path.jl @@ -145,7 +145,7 @@ function _splitdir_nodrive(a::String, b::String) end """ - dirname(path::AbstractString) -> AbstractString + dirname(path::AbstractString) -> String Get the directory part of a path. Trailing characters ('/' or '\\') in the path are counted as part of the path. @@ -161,10 +161,10 @@ julia> dirname("/home/myuser/") See also [`basename`](@ref). """ - dirname(path::AbstractString) = splitdir(path)[1] +dirname(path::AbstractString) = splitdir(path)[1] """ - basename(path::AbstractString) -> AbstractString + basename(path::AbstractString) -> String Get the file name part of a path. @@ -186,7 +186,7 @@ See also [`dirname`](@ref). basename(path::AbstractString) = splitdir(path)[2] """ - splitext(path::AbstractString) -> (AbstractString, AbstractString) + splitext(path::AbstractString) -> (String, String) If the last component of a path contains one or more dots, split the path into everything before the last dot and everything including and after the dot. Otherwise, return a tuple of the argument @@ -542,7 +542,7 @@ contractuser(path::AbstractString) """ - relpath(path::AbstractString, startpath::AbstractString = ".") -> AbstractString + relpath(path::AbstractString, startpath::AbstractString = ".") -> String Return a relative filepath to `path` either from the current directory or from an optional start directory. This is a path computation: the filesystem is not accessed to confirm the diff --git a/stdlib/InteractiveUtils/src/clipboard.jl b/stdlib/InteractiveUtils/src/clipboard.jl index adf676cb8c55a..a4a5118acf8d7 100644 --- a/stdlib/InteractiveUtils/src/clipboard.jl +++ b/stdlib/InteractiveUtils/src/clipboard.jl @@ -154,7 +154,7 @@ Send a printed form of `x` to the operating system clipboard ("copy"). clipboard(x) """ - clipboard() -> AbstractString + clipboard() -> String Return a string with the contents of the operating system clipboard ("paste"). """ From 94e69381e622b9f06c8af37b87ba5ca3f0b9dae7 Mon Sep 17 00:00:00 2001 From: Ujjwal Sarswat <76774914+vmpyr@users.noreply.github.com> Date: Fri, 3 Feb 2023 05:25:36 +0530 Subject: [PATCH 007/775] Add test for the macro nall (#48472) * add(test/cartesian.jl): test for the macro nall * add(test/cartesian.jl): test for the macro nall * refactor the test to align with real world usage --- test/cartesian.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/cartesian.jl b/test/cartesian.jl index 772ce259c7d24..ed33f2c1035f7 100644 --- a/test/cartesian.jl +++ b/test/cartesian.jl @@ -515,6 +515,12 @@ end f39705() = Base.Cartesian.@nany 0 _ -> true @test f39705() === false +@testset "Cartesian @nall macro test" begin + i_1, i_2, i_3 = 1, 2, 3; + @test Base.Cartesian.@nall 2 d->(i_d <= 2) + @test !Base.Cartesian.@nall 3 d->(i_d <= 2) +end + @testset "CartesianIndices with Bool" begin @test @inferred(CartesianIndices((true,))) == CartesianIndices((1,)) @test @inferred(CartesianIndices((false,))) == CartesianIndices((0,)) From 383f48f17b44b8c146fe8d030d2e08222d82a522 Mon Sep 17 00:00:00 2001 From: Johan Montelius <32909783+johanmon@users.noreply.github.com> Date: Fri, 3 Feb 2023 00:57:28 +0100 Subject: [PATCH 008/775] adding dynamic width and precision to printf (#40105) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add dynamic width and precision to printf * fixed compat notice * rm blank lines * news * Update stdlib/Printf/test/runtests.jl Co-authored-by: Mosè Giordano * rm whitespace * Update stdlib/Printf/src/Printf.jl --------- Co-authored-by: Steven G. Johnson Co-authored-by: Steven G. Johnson Co-authored-by: Mosè Giordano --- NEWS.md | 2 +- stdlib/Printf/src/Printf.jl | 110 ++++++++-- stdlib/Printf/test/runtests.jl | 354 +++++++++++++++++++++++++++++++++ 3 files changed, 445 insertions(+), 21 deletions(-) diff --git a/NEWS.md b/NEWS.md index fc90e9a0746ea..1e9f86ebb14a4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -49,7 +49,7 @@ Standard library changes #### Printf - +* Format specifiers now support dynamic width and precision, e.g. `%*s` and `%*.*g` ([#40105]). #### Profile diff --git a/stdlib/Printf/src/Printf.jl b/stdlib/Printf/src/Printf.jl index 9f14961aa2acf..62a84d7d36984 100644 --- a/stdlib/Printf/src/Printf.jl +++ b/stdlib/Printf/src/Printf.jl @@ -34,19 +34,29 @@ struct Spec{T} # T => %type => Val{'type'} hash::Bool width::Int precision::Int + dynamic_width::Bool + dynamic_precision::Bool end # recreate the format specifier string from a typed Spec Base.string(f::Spec{T}; modifier::String="") where {T} = - string("%", f.leftalign ? "-" : "", f.plus ? "+" : "", f.space ? " " : "", - f.zero ? "0" : "", f.hash ? "#" : "", f.width > 0 ? f.width : "", - f.precision == 0 ? ".0" : f.precision > 0 ? ".$(f.precision)" : "", modifier, char(T)) + string("%", + f.leftalign ? "-" : "", + f.plus ? "+" : "", + f.space ? " " : "", + f.zero ? "0" : "", + f.hash ? "#" : "", + f.dynamic_width ? "*" : (f.width > 0 ? f.width : ""), + f.dynamic_precision ? ".*" : (f.precision == 0 ? ".0" : (f.precision > 0 ? ".$(f.precision)" : "")), + modifier, + char(T)) + Base.show(io::IO, f::Spec) = print(io, string(f)) floatfmt(s::Spec{T}) where {T} = - Spec{Val{'f'}}(s.leftalign, s.plus, s.space, s.zero, s.hash, s.width, 0) + Spec{Val{'f'}}(s.leftalign, s.plus, s.space, s.zero, s.hash, s.width, 0, s.dynamic_width, s.dynamic_precision) ptrfmt(s::Spec{T}, x) where {T} = - Spec{Val{'x'}}(s.leftalign, s.plus, s.space, s.zero, true, s.width, sizeof(x) == 8 ? 16 : 8) + Spec{Val{'x'}}(s.leftalign, s.plus, s.space, s.zero, true, s.width, sizeof(x) == 8 ? 16 : 8, s.dynamic_width, s.dynamic_precision) """ Printf.Format(format_str) @@ -75,6 +85,7 @@ struct Format{S, T} # and so on, then at the end, str[substringranges[end]] substringranges::Vector{UnitRange{Int}} formats::T # Tuple of Specs + numarguments::Int # required for dynamic format specifiers end # what number base should be used for a given format specifier? @@ -115,6 +126,8 @@ function Format(f::AbstractString) bytes = codeunits(f) len = length(bytes) pos = 1 + numarguments = 0 + b = 0x00 local last_percent_pos @@ -165,26 +178,43 @@ function Format(f::AbstractString) end # parse width width = 0 - while b - UInt8('0') < 0x0a - width = 10 * width + (b - UInt8('0')) + dynamic_width = false + if b == UInt8('*') + dynamic_width = true + numarguments += 1 b = bytes[pos] pos += 1 - pos > len && break + else + while b - UInt8('0') < 0x0a + width = 10 * width + (b - UInt8('0')) + b = bytes[pos] + pos += 1 + pos > len && break + end end # parse precision precision = 0 parsedprecdigits = false + dynamic_precision = false if b == UInt8('.') pos > len && throw(InvalidFormatStringError("Precision specifier is missing precision", f, last_percent_pos, pos-1)) parsedprecdigits = true b = bytes[pos] pos += 1 if pos <= len - while b - UInt8('0') < 0x0a - precision = 10precision + (b - UInt8('0')) + if b == UInt8('*') + dynamic_precision = true + numarguments += 1 b = bytes[pos] pos += 1 - pos > len && break + else + precision = 0 + while b - UInt8('0') < 0x0a + precision = 10precision + (b - UInt8('0')) + b = bytes[pos] + pos += 1 + pos > len && break + end end end end @@ -208,6 +238,8 @@ function Format(f::AbstractString) !(b in b"diouxXDOUeEfFgGaAcCsSpn") && throw(InvalidFormatStringError("'$(Char(b))' is not a valid type specifier", f, last_percent_pos, pos-1)) type = Val{Char(b)} if type <: Ints && precision > 0 + # note - we should also set zero to false if dynamic precison > 0 + # this is taken care of in fmt() for Ints zero = false elseif (type <: Strings || type <: Chars) && !parsedprecdigits precision = -1 @@ -216,7 +248,8 @@ function Format(f::AbstractString) elseif type <: Floats && !parsedprecdigits precision = 6 end - push!(fmts, Spec{type}(leftalign, plus, space, zero, hash, width, precision)) + numarguments += 1 + push!(fmts, Spec{type}(leftalign, plus, space, zero, hash, width, precision, dynamic_width, dynamic_precision)) start = pos while pos <= len b = bytes[pos] @@ -235,7 +268,7 @@ function Format(f::AbstractString) end push!(strs, start:pos - 1 - (b == UInt8('%'))) end - return Format(bytes, strs, Tuple(fmts)) + return Format(bytes, strs, Tuple(fmts), numarguments) end macro format_str(str) @@ -257,6 +290,28 @@ const HEX = b"0123456789ABCDEF" return pos end + +@inline function rmdynamic(spec::Spec{T}, args, argp) where {T} + zero, width, precision = spec.zero, spec.width, spec.precision + if spec.dynamic_width + width = args[argp] + argp += 1 + end + if spec.dynamic_precision + precision = args[argp] + if zero && T <: Ints && precision > 0 + zero = false + end + argp += 1 + end + (Spec{T}(spec.leftalign, spec.plus, spec.space, zero, spec.hash, width, precision, false, false), argp) +end + +@inline function fmt(buf, pos, args, argp, spec::Spec{T}) where {T} + spec, argp = rmdynamic(spec, args, argp) + (fmt(buf, pos, args[argp], spec), argp+1) +end + @inline function fmt(buf, pos, arg, spec::Spec{T}) where {T <: Chars} leftalign, width = spec.leftalign, spec.width c = Char(first(arg)) @@ -772,9 +827,10 @@ const UNROLL_UPTO = 16 # for each format, write out arg and next substring # unroll up to 16 formats N = length(f.formats) + argp = 1 Base.@nexprs 16 i -> begin if N >= i - pos = fmt(buf, pos, args[i], f.formats[i]) + pos, argp = fmt(buf, pos, args, argp, f.formats[i]) for j in f.substringranges[i + 1] b = f.str[j] if !escapechar @@ -789,7 +845,7 @@ const UNROLL_UPTO = 16 end if N > 16 for i = 17:length(f.formats) - pos = fmt(buf, pos, args[i], f.formats[i]) + pos, argp = fmt(buf, pos, args, argp, f.formats[i]) for j in f.substringranges[i + 1] b = f.str[j] if !escapechar @@ -805,11 +861,17 @@ const UNROLL_UPTO = 16 return pos end +@inline function plength(f::Spec{T}, args, argp) where {T} + f, argp = rmdynamic(f, args, argp) + (plength(f, args[argp]), argp+1) +end + function plength(f::Spec{T}, x) where {T <: Chars} c = Char(first(x)) w = textwidth(c) return max(f.width, w) + (ncodeunits(c) - w) end + plength(f::Spec{Pointer}, x) = max(f.width, 2 * sizeof(x) + 2) function plength(f::Spec{T}, x) where {T <: Strings} @@ -837,14 +899,17 @@ plength(::Spec{PositionCounter}, x) = 0 len = sum(length, substringranges) N = length(formats) # unroll up to 16 formats + argp = 1 Base.@nexprs 16 i -> begin if N >= i - len += plength(formats[i], args[i]) + l, argp = plength(formats[i], args, argp) + len += l end end if N > 16 for i = 17:length(formats) - len += plength(formats[i], args[i]) + l, argp = plength(formats[i], args, argp) + len += l end end return len @@ -864,7 +929,7 @@ for more details on C `printf` support. function format end function format(io::IO, f::Format, args...) # => Nothing - length(f.formats) == length(args) || argmismatch(length(f.formats), length(args)) + f.numarguments == length(args) || argmismatch(f.numarguments, length(args)) buf = Base.StringVector(computelen(f.substringranges, f.formats, args)) pos = format(buf, 1, f, args...) write(io, resize!(buf, pos - 1)) @@ -872,7 +937,7 @@ function format(io::IO, f::Format, args...) # => Nothing end function format(f::Format, args...) # => String - length(f.formats) == length(args) || argmismatch(length(f.formats), length(args)) + f.numarguments == length(args) || argmismatch(f.numarguments, length(args)) buf = Base.StringVector(computelen(f.substringranges, f.formats, args)) pos = format(buf, 1, f, args...) return String(resize!(buf, pos - 1)) @@ -906,8 +971,10 @@ Padded with zeros to length 6 000123 julia> @printf "Use shorter of decimal or scientific %g %g" 1.23 12300000.0 Use shorter of decimal or scientific 1.23 1.23e+07 -``` +julia> @printf "Use dynamic width and precision %*.*f" 10 2 0.12345 +Use dynamic width and precision 0.12 +``` For a systematic specification of the format, see [here](https://www.cplusplus.com/reference/cstdio/printf/). See also [`@sprintf`](@ref) to get the result as a `String` instead of it being printed. @@ -931,6 +998,9 @@ julia> @printf "%.0f %.1f %f" 0.5 0.025 -0.0078125 using [`textwidth`](@ref), which e.g. ignores zero-width characters (such as combining characters for diacritical marks) and treats certain "wide" characters (e.g. emoji) as width `2`. + +!!! compat "Julia 1.10" + Dynamic width specifiers like `%*s` and `%0*.*f` require Julia 1.10. """ macro printf(io_or_fmt, args...) if io_or_fmt isa String diff --git a/stdlib/Printf/test/runtests.jl b/stdlib/Printf/test/runtests.jl index 40a6a763e4eac..96d61b61d02e3 100644 --- a/stdlib/Printf/test/runtests.jl +++ b/stdlib/Printf/test/runtests.jl @@ -775,6 +775,7 @@ end @test Printf.@sprintf("%40d", typemax(Int128)) == " 170141183460469231731687303715884105727" end + @testset "%n" begin x = Ref{Int}() @test (Printf.@sprintf("%d4%n", 123, x); x[] == 4) @@ -782,6 +783,359 @@ end @test (Printf.@sprintf("%s%n", "1234", x); x[] == 4) end +@testset "dynamic" begin + + # dynamic width and precision + @test Printf.@sprintf("%*d", 10, 12) == " 12" + @test Printf.@sprintf("%.*d", 4, 12) == "0012" + @test Printf.@sprintf("%*.*d", 10, 4, 12) == " 0012" + @test Printf.@sprintf("%+*.*d", 10, 4, 12) == " +0012" + @test Printf.@sprintf("%0*.*d", 10, 4, 12) == " 0012" + + @test Printf.@sprintf("%*d%*d%*d", 4, 12, 4, 13, 4, 14) == " 12 13 14" + @test Printf.@sprintf("%*d%*d%*d", 4, 12, 5, 13, 6, 14) == " 12 13 14" + + # dynamic should return whatever the static width and precision returns + + + # pointers + @test Printf.@sprintf("%*p", 20, 0) == Printf.@sprintf("%20p", 0) + @test Printf.@sprintf("%-*p", 20, 0) == Printf.@sprintf("%-20p", 0) + @test Printf.@sprintf("%*p", 20, C_NULL) == Printf.@sprintf("%20p", C_NULL) + @test Printf.@sprintf("%-*p", 20, C_NULL) == Printf.@sprintf("%-20p", C_NULL) + + # hex float + @test Printf.@sprintf("%.*a", 0, 3.14) == Printf.@sprintf("%.0a", 3.14) + @test Printf.@sprintf("%.*a", 1, 3.14) == Printf.@sprintf("%.1a", 3.14) + @test Printf.@sprintf("%.*a", 2, 3.14) == Printf.@sprintf("%.2a", 3.14) + @test Printf.@sprintf("%#.*a", 0, 3.14) == Printf.@sprintf("%#.0a", 3.14) + @test Printf.@sprintf("%#.*a", 1, 3.14) == Printf.@sprintf("%#.1a", 3.14) + @test Printf.@sprintf("%#.*a", 2, 3.14) == Printf.@sprintf("%#.2a", 3.14) + @test Printf.@sprintf("%.*a", 6, 1.5) == Printf.@sprintf("%.6a", 1.5) + + # "%g" + @test Printf.@sprintf("%*.*g", 10, 5, -123.4 ) == Printf.@sprintf( "%10.5g", -123.4 ) + @test Printf.@sprintf("%0*.*g", 10, 5, -123.4 ) == Printf.@sprintf( "%010.5g", -123.4 ) + @test Printf.@sprintf("%.*g", 6, 12340000.0 ) == Printf.@sprintf( "%.6g", 12340000.0 ) + @test Printf.@sprintf("%#.*g", 6, 12340000.0 ) == Printf.@sprintf( "%#.6g", 12340000.0 ) + @test Printf.@sprintf("%*.*g", 10, 5, big"-123.4" ) == Printf.@sprintf( "%10.5g", big"-123.4" ) + @test Printf.@sprintf("%0*.*g", 10, 5, big"-123.4" ) == Printf.@sprintf( "%010.5g", big"-123.4" ) + @test Printf.@sprintf("%.*g", 6, big"12340000.0" ) == Printf.@sprintf( "%.6g", big"12340000.0" ) + @test Printf.@sprintf("%#.*g", 6, big"12340000.0") == Printf.@sprintf( "%#.6g", big"12340000.0") + + @test Printf.@sprintf("%.*g", 5, 42) == Printf.@sprintf( "%.5g", 42) + @test Printf.@sprintf("%#.*g", 2, 42) == Printf.@sprintf( "%#.2g", 42) + @test Printf.@sprintf("%#.*g", 5, 42) == Printf.@sprintf( "%#.5g", 42) + + @test Printf.@sprintf("%.*g", 15, 0) == Printf.@sprintf("%.15g", 0) + @test Printf.@sprintf("%#.*g", 15, 0) == Printf.@sprintf("%#.15g", 0) + + # "%f" + @test Printf.@sprintf("%.*f", 0, 3e142) == Printf.@sprintf( "%.0f", 3e142) + @test Printf.@sprintf("%.*f", 2, 1.234) == Printf.@sprintf("%.2f", 1.234) + @test Printf.@sprintf("%.*f", 2, 1.235) == Printf.@sprintf("%.2f", 1.235) + @test Printf.@sprintf("%.*f", 2, 0.235) == Printf.@sprintf("%.2f", 0.235) + @test Printf.@sprintf("%*.*f", 4, 1, 1.234) == Printf.@sprintf("%4.1f", 1.234) + @test Printf.@sprintf("%*.*f", 8, 1, 1.234) == Printf.@sprintf("%8.1f", 1.234) + @test Printf.@sprintf("%+*.*f", 8, 1, 1.234) == Printf.@sprintf("%+8.1f", 1.234) + @test Printf.@sprintf("% *.*f", 8, 1, 1.234) == Printf.@sprintf("% 8.1f", 1.234) + @test Printf.@sprintf("% *.*f", 7, 1, 1.234) == Printf.@sprintf("% 7.1f", 1.234) + @test Printf.@sprintf("% 0*.*f", 8, 1, 1.234) == Printf.@sprintf("% 08.1f", 1.234) + @test Printf.@sprintf("%0*.*f", 8, 1, 1.234) == Printf.@sprintf("%08.1f", 1.234) + @test Printf.@sprintf("%-0*.*f", 8, 1, 1.234) == Printf.@sprintf("%-08.1f", 1.234) + @test Printf.@sprintf("%-*.*f", 8, 1, 1.234) == Printf.@sprintf("%-8.1f", 1.234) + @test Printf.@sprintf("%0*.*f", 8, 1, -1.234) == Printf.@sprintf("%08.1f", -1.234) + @test Printf.@sprintf("%0*.*f", 9, 1, -1.234) == Printf.@sprintf("%09.1f", -1.234) + @test Printf.@sprintf("%0*.*f", 9, 1, 1.234) == Printf.@sprintf("%09.1f", 1.234) + @test Printf.@sprintf("%+0*.*f", 9, 1, 1.234) == Printf.@sprintf("%+09.1f", 1.234) + @test Printf.@sprintf("% 0*.*f", 9, 1, 1.234) == Printf.@sprintf("% 09.1f", 1.234) + @test Printf.@sprintf("%+ 0*.*f", 9, 1, 1.234) == Printf.@sprintf("%+ 09.1f", 1.234) + @test Printf.@sprintf("%+ 0*.*f", 9, 1, 1.234) == Printf.@sprintf("%+ 09.1f", 1.234) + @test Printf.@sprintf("%+ 0*.*f", 9, 0, 1.234) == Printf.@sprintf("%+ 09.0f", 1.234) + @test Printf.@sprintf("%+ #0*.*f", 9, 0, 1.234) == Printf.@sprintf("%+ #09.0f", 1.234) + + # "%e" + @test Printf.@sprintf("%*.*e", 10, 4, Inf) == Printf.@sprintf("%10.4e", Inf) + @test Printf.@sprintf("%*.*e", 10, 4, NaN) == Printf.@sprintf("%10.4e", NaN) + @test Printf.@sprintf("%*.*e", 10, 4, big"Inf") == Printf.@sprintf("%10.4e", big"Inf") + @test Printf.@sprintf("%*.*e", 10, 4, big"NaN") == Printf.@sprintf("%10.4e", big"NaN") + + @test Printf.@sprintf("%.*e", 0, 3e142) == Printf.@sprintf("%.0e",3e142) + @test Printf.@sprintf("%#.*e", 0, 3e142) == Printf.@sprintf("%#.0e", 3e142) + @test Printf.@sprintf("%.*e", 0, big"3e142") == Printf.@sprintf("%.0e", big"3e142") + + @test Printf.@sprintf("%#.*e", 0, big"3e142") == Printf.@sprintf("%#.0e", big"3e142") + @test Printf.@sprintf("%.*e", 0, big"3e1042") == Printf.@sprintf("%.0e", big"3e1042") + + @test Printf.@sprintf("%.*e", 2, 1.234) == Printf.@sprintf("%.2e", 1.234) + @test Printf.@sprintf("%.*e", 2, 1.235) == Printf.@sprintf("%.2e", 1.235) + @test Printf.@sprintf("%.*e", 2, 0.235) == Printf.@sprintf("%.2e", 0.235) + @test Printf.@sprintf("%*.*e", 4, 1, 1.234) == Printf.@sprintf("%4.1e", 1.234) + @test Printf.@sprintf("%*.*e", 8, 1, 1.234) == Printf.@sprintf("%8.1e", 1.234) + @test Printf.@sprintf("%+*.*e", 8, 1, 1.234) == Printf.@sprintf("%+8.1e", 1.234) + @test Printf.@sprintf("% *.*e", 8, 1, 1.234) == Printf.@sprintf("% 8.1e", 1.234) + @test Printf.@sprintf("% *.*e", 7, 1, 1.234) == Printf.@sprintf("% 7.1e", 1.234) + @test Printf.@sprintf("% 0*.*e", 8, 1, 1.234) == Printf.@sprintf("% 08.1e", 1.234) + @test Printf.@sprintf("%0*.*e", 8, 1, 1.234) == Printf.@sprintf("%08.1e", 1.234) + @test Printf.@sprintf("%-0*.*e", 8, 1, 1.234) == Printf.@sprintf("%-08.1e", 1.234) + @test Printf.@sprintf("%-*.*e", 8, 1, 1.234) == Printf.@sprintf("%-8.1e", 1.234) + @test Printf.@sprintf("%-*.*e", 8, 1, 1.234) == Printf.@sprintf("%-8.1e", 1.234) + @test Printf.@sprintf("%0*.*e", 8, 1, -1.234) == Printf.@sprintf("%08.1e", -1.234) + @test Printf.@sprintf("%0*.*e", 9, 1, -1.234) == Printf.@sprintf("%09.1e", -1.234) + @test Printf.@sprintf("%0*.*e", 9, 1, 1.234) == Printf.@sprintf("%09.1e", 1.234) + @test Printf.@sprintf("%+0*.*e", 9, 1, 1.234) == Printf.@sprintf("%+09.1e", 1.234) + @test Printf.@sprintf("% 0*.*e", 9, 1, 1.234) == Printf.@sprintf("% 09.1e", 1.234) + @test Printf.@sprintf("%+ 0*.*e", 9, 1, 1.234) == Printf.@sprintf("%+ 09.1e", 1.234) + @test Printf.@sprintf("%+ 0*.*e", 9, 1, 1.234) == Printf.@sprintf("%+ 09.1e", 1.234) + @test Printf.@sprintf("%+ 0*.*e", 9, 0, 1.234) == Printf.@sprintf("%+ 09.0e", 1.234) + @test Printf.@sprintf("%+ #0*.*e", 9, 0, 1.234) == Printf.@sprintf("%+ #09.0e", 1.234) + + # strings + @test Printf.@sprintf("%.*s", 1, "foo") == Printf.@sprintf("%.1s", "foo") + @test Printf.@sprintf("%*s", 1, "Hallo heimur") == Printf.@sprintf("%1s", "Hallo heimur") + @test Printf.@sprintf("%*s", 20, "Hallo") == Printf.@sprintf("%20s", "Hallo") + @test Printf.@sprintf("%-*s", 20, "Hallo") == Printf.@sprintf("%-20s", "Hallo") + @test Printf.@sprintf("%0-*s", 20, "Hallo") == Printf.@sprintf("%0-20s", "Hallo") + @test Printf.@sprintf("%.*s", 20, "Hallo heimur") == Printf.@sprintf("%.20s", "Hallo heimur") + @test Printf.@sprintf("%*.*s", 20, 5, "Hallo heimur") == Printf.@sprintf("%20.5s", "Hallo heimur") + @test Printf.@sprintf("%.*s", 0, "Hallo heimur") == Printf.@sprintf("%.0s", "Hallo heimur") + @test Printf.@sprintf("%*.*s", 20, 0, "Hallo heimur") == Printf.@sprintf("%20.0s", "Hallo heimur") + @test Printf.@sprintf("%.s", "Hallo heimur") == Printf.@sprintf("%.s", "Hallo heimur") + @test Printf.@sprintf("%*.s", 20, "Hallo heimur") == Printf.@sprintf("%20.s", "Hallo heimur") + @test Printf.@sprintf("%*sø", 4, "ø") == Printf.@sprintf("%4sø", "ø") + @test Printf.@sprintf("%-*sø", 4, "ø") == Printf.@sprintf("%-4sø", "ø") + + @test Printf.@sprintf("%*s", 8, "test") == Printf.@sprintf("%8s", "test") + @test Printf.@sprintf("%-*s", 8, "test") == Printf.@sprintf("%-8s", "test") + + @test Printf.@sprintf("%#*s", 8, :test) == Printf.@sprintf("%#8s", :test) + @test Printf.@sprintf("%#-*s", 8, :test) == Printf.@sprintf("%#-8s", :test) + + @test Printf.@sprintf("%*.*s", 8, 3, "test") == Printf.@sprintf("%8.3s", "test") + @test Printf.@sprintf("%#*.*s", 8, 3, "test") == Printf.@sprintf("%#8.3s", "test") + @test Printf.@sprintf("%-*.*s", 8, 3, "test") == Printf.@sprintf("%-8.3s", "test") + @test Printf.@sprintf("%#-*.*s", 8, 3, "test") == Printf.@sprintf("%#-8.3s", "test") + @test Printf.@sprintf("%.*s", 3, "test") == Printf.@sprintf("%.3s", "test") + @test Printf.@sprintf("%#.*s", 3, "test") == Printf.@sprintf("%#.3s", "test") + @test Printf.@sprintf("%-.*s", 3, "test") == Printf.@sprintf("%-.3s", "test") + @test Printf.@sprintf("%#-.*s", 3, "test") == Printf.@sprintf("%#-.3s", "test") + + # chars + @test Printf.@sprintf("%*c", 3, 'a') == Printf.@sprintf("%3c", 'a') + @test Printf.@sprintf("%*c", 1, 'x') == Printf.@sprintf("%1c", 'x') + @test Printf.@sprintf("%*c" , 20, 'x') == Printf.@sprintf("%20c" , 'x') + @test Printf.@sprintf("%-*c" , 20, 'x') == Printf.@sprintf("%-20c" , 'x') + @test Printf.@sprintf("%-0*c", 20, 'x') == Printf.@sprintf("%-020c", 'x') + @test Printf.@sprintf("%*c", 3, 'A') == Printf.@sprintf("%3c", 'A') + @test Printf.@sprintf("%-*c", 3, 'A') == Printf.@sprintf("%-3c", 'A') + + # more than 16 formats/args + @test Printf.@sprintf("%*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f %*.*f", 4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345,4,2,1.2345) == Printf.@sprintf("%4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f", 1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345,1.2345) + + # Check bug with trailing nul printing BigFloat + @test (Printf.@sprintf("%.*f", 330, BigFloat(1)))[end] != '\0' + + # Check bug with precision > length of string + @test Printf.@sprintf("%*.*s", 4, 2, "a") == Printf.@sprintf("%4.2s", "a") + + # issue #29662 + @test Printf.@sprintf("%*.*e", 12, 3, pi*1e100) == Printf.@sprintf("%12.3e", pi*1e100) + @test Printf.@sprintf("%*d", 2, 3.14) == Printf.@sprintf("%*d", 2, 3.14) + @test Printf.@sprintf("%*d", 2, big(3.14)) == Printf.@sprintf("%*d", 2, big(3.14)) + + # 37539 + @test Printf.@sprintf(" %.*e\n", 1, 0.999) == Printf.@sprintf(" %.1e\n", 0.999) + @test Printf.@sprintf(" %.*f", 1, 9.999) == Printf.@sprintf(" %.1f", 9.999) + + # integers + @test Printf.@sprintf("%*d", 10, 12) == (Printf.@sprintf("%10d", 12)) + @test Printf.@sprintf("%.*d", 4, 12) == (Printf.@sprintf("%.4d", 12)) + @test Printf.@sprintf("%*.*d", 10, 4, 12) == (Printf.@sprintf("%10.4d", 12)) + @test Printf.@sprintf("%+*.*d", 10, 4, 12) == (Printf.@sprintf("%+10.4d", 12)) + @test Printf.@sprintf("%0*.*d", 10, 4, 12) == (Printf.@sprintf("%010.4d", 12)) + + @test Printf.@sprintf( "% *d", 5, 42) == Printf.@sprintf( "% 5d", 42) + @test Printf.@sprintf( "% *d", 5, -42) == Printf.@sprintf( "% 5d", -42) + @test Printf.@sprintf( "% *d", 15, 42) == Printf.@sprintf( "% 15d", 42) + @test Printf.@sprintf( "% *d", 15, -42) == Printf.@sprintf( "% 15d", -42) + + @test Printf.@sprintf("%+*d", 5, 42) == Printf.@sprintf("%+5d", 42) + @test Printf.@sprintf("%+*d", 5, -42) == Printf.@sprintf("%+5d", -42) + @test Printf.@sprintf("%+*d", 15, 42) == Printf.@sprintf("%+15d", 42) + @test Printf.@sprintf("%+*d", 15, -42) == Printf.@sprintf("%+15d", -42) + @test Printf.@sprintf( "%*d", 0, 42) == Printf.@sprintf( "%0d", 42) + @test Printf.@sprintf( "%*d", 0, -42) == Printf.@sprintf( "%0d", -42) + + @test Printf.@sprintf("%-*d", 5, 42) == Printf.@sprintf("%-5d", 42) + @test Printf.@sprintf("%-*d", 5, -42) == Printf.@sprintf("%-5d", -42) + @test Printf.@sprintf("%-*d", 15, 42) == Printf.@sprintf("%-15d", 42) + @test Printf.@sprintf("%-*d", 15, -42) == Printf.@sprintf("%-15d", -42) + + @test Printf.@sprintf("%+*lld", 8, 100) == Printf.@sprintf("%+8lld", 100) + @test Printf.@sprintf("%+.*lld", 8, 100) == Printf.@sprintf("%+.8lld", 100) + @test Printf.@sprintf("%+*.*lld", 10, 8, 100) == Printf.@sprintf("%+10.8lld", 100) + + @test Printf.@sprintf("%-*.*lld", 1, 5, -100) == Printf.@sprintf("%-1.5lld", -100) + @test Printf.@sprintf("%*lld", 5, 100) == Printf.@sprintf("%5lld", 100) + @test Printf.@sprintf("%*lld", 5, -100) == Printf.@sprintf("%5lld", -100) + @test Printf.@sprintf("%-*lld", 5, 100) == Printf.@sprintf("%-5lld", 100) + @test Printf.@sprintf("%-*lld", 5, -100) == Printf.@sprintf("%-5lld", -100) + @test Printf.@sprintf("%-.*lld", 5, 100) == Printf.@sprintf("%-.5lld", 100) + @test Printf.@sprintf("%-.*lld", 5, -100) == Printf.@sprintf("%-.5lld", -100) + @test Printf.@sprintf("%-*.*lld", 8, 5, 100) == Printf.@sprintf("%-8.5lld", 100) + @test Printf.@sprintf("%-*.*lld", 8, 5, -100) == Printf.@sprintf("%-8.5lld", -100) + @test Printf.@sprintf("%0*lld", 5, 100) == Printf.@sprintf("%05lld", 100) + @test Printf.@sprintf("%0*lld", 5, -100) == Printf.@sprintf("%05lld", -100) + @test Printf.@sprintf("% *lld", 5, 100) == Printf.@sprintf("% 5lld", 100) + @test Printf.@sprintf("% *lld", 5, -100) == Printf.@sprintf("% 5lld", -100) + @test Printf.@sprintf("% .*lld", 5, 100) == Printf.@sprintf("% .5lld", 100) + @test Printf.@sprintf("% .*lld", 5, -100) == Printf.@sprintf("% .5lld", -100) + @test Printf.@sprintf("% *.*lld", 8, 5, 100) == Printf.@sprintf("% 8.5lld", 100) + @test Printf.@sprintf("% *.*lld", 8, 5, -100) == Printf.@sprintf("% 8.5lld", -100) + @test Printf.@sprintf("%.*lld", 0, 0) == Printf.@sprintf("%.0lld", 0) + @test Printf.@sprintf("%#+*.*llx", 21, 18, -100) == Printf.@sprintf("%#+21.18llx", -100) + @test Printf.@sprintf("%#.*llo", 25, -100) == Printf.@sprintf("%#.25llo", -100) + @test Printf.@sprintf("%#+*.*llo", 24, 20, -100) == Printf.@sprintf("%#+24.20llo", -100) + @test Printf.@sprintf("%#+*.*llX", 18, 21, -100) == Printf.@sprintf("%#+18.21llX", -100) + @test Printf.@sprintf("%#+*.*llo", 20, 24, -100) == Printf.@sprintf("%#+20.24llo", -100) + @test Printf.@sprintf("%#+*.*llu", 25, 22, -1) == Printf.@sprintf("%#+25.22llu", -1) + @test Printf.@sprintf("%#+*.*llu", 30, 25, -1) == Printf.@sprintf("%#+30.25llu", -1) + @test Printf.@sprintf("%+#*.*lld", 25, 22, -1) == Printf.@sprintf("%+#25.22lld", -1) + @test Printf.@sprintf("%#-*.*llo", 8, 5, 100) == Printf.@sprintf("%#-8.5llo", 100) + @test Printf.@sprintf("%#-+ 0*.*lld", 8, 5, 100) == Printf.@sprintf("%#-+ 08.5lld", 100) + @test Printf.@sprintf("%#-+ 0*.*lld", 8, 5, 100) == Printf.@sprintf("%#-+ 08.5lld", 100) + @test Printf.@sprintf("%.*lld", 40, 1) == Printf.@sprintf("%.40lld", 1) + @test Printf.@sprintf("% .*lld", 40, 1) == Printf.@sprintf("% .40lld", 1) + @test Printf.@sprintf("% .*d", 40, 1) == Printf.@sprintf("% .40d", 1) + + @test Printf.@sprintf("%#0*x", 12, 1) == Printf.@sprintf("%#012x", 1) + @test Printf.@sprintf("%#0*.*x", 4, 8, 1) == Printf.@sprintf("%#04.8x", 1) + + @test Printf.@sprintf("%#-0*.*x", 8, 2, 1) == Printf.@sprintf("%#-08.2x", 1) + @test Printf.@sprintf("%#0*o", 8, 1) == Printf.@sprintf("%#08o", 1) + + @test Printf.@sprintf("%*d", 20, 1024) == Printf.@sprintf("%20d", 1024) + @test Printf.@sprintf("%*d", 20,-1024) == Printf.@sprintf("%20d", -1024) + @test Printf.@sprintf("%*i", 20, 1024) == Printf.@sprintf("%20i", 1024) + @test Printf.@sprintf("%*i", 20,-1024) == Printf.@sprintf("%20i", -1024) + @test Printf.@sprintf("%*u", 20, 1024) == Printf.@sprintf("%20u", 1024) + @test Printf.@sprintf("%*u", 20, UInt(4294966272)) == Printf.@sprintf("%20u", UInt(4294966272)) + @test Printf.@sprintf("%*o", 20, 511) == Printf.@sprintf("%20o", 511) + @test Printf.@sprintf("%*o", 20, UInt(4294966785)) == Printf.@sprintf("%20o", UInt(4294966785)) + @test Printf.@sprintf("%*x", 20, 305441741) == Printf.@sprintf("%20x", 305441741) + @test Printf.@sprintf("%*x", 20, UInt(3989525555)) == Printf.@sprintf("%20x", UInt(3989525555)) + @test Printf.@sprintf("%*X", 20, 305441741) == Printf.@sprintf("%20X", 305441741) + @test Printf.@sprintf("%*X", 20, UInt(3989525555)) == Printf.@sprintf("%20X", UInt(3989525555)) + @test Printf.@sprintf("%-*d", 20, 1024) == Printf.@sprintf("%-20d", 1024) + @test Printf.@sprintf("%-*d", 20,-1024) == Printf.@sprintf("%-20d", -1024) + @test Printf.@sprintf("%-*i", 20, 1024) == Printf.@sprintf("%-20i", 1024) + @test Printf.@sprintf("%-*i", 20,-1024) == Printf.@sprintf("%-20i", -1024) + @test Printf.@sprintf("%-*u", 20, 1024) == Printf.@sprintf("%-20u", 1024) + @test Printf.@sprintf("%-*u", 20, UInt(4294966272)) == Printf.@sprintf("%-20u", UInt(4294966272)) + @test Printf.@sprintf("%-*o", 20, 511) == Printf.@sprintf("%-20o", 511) + @test Printf.@sprintf("%-*o", 20, UInt(4294966785)) == Printf.@sprintf("%-20o", UInt(4294966785)) + @test Printf.@sprintf("%-*x", 20, 305441741) == Printf.@sprintf("%-20x", 305441741) + @test Printf.@sprintf("%-*x", 20, UInt(3989525555)) == Printf.@sprintf("%-20x", UInt(3989525555)) + @test Printf.@sprintf("%-*X", 20, 305441741) == Printf.@sprintf("%-20X", 305441741) + @test Printf.@sprintf("%-*X", 20, UInt(3989525555)) == Printf.@sprintf("%-20X", UInt(3989525555)) + @test Printf.@sprintf("%0*d", 20, 1024) == Printf.@sprintf("%020d", 1024) + @test Printf.@sprintf("%0*d", 20,-1024) == Printf.@sprintf("%020d", -1024) + @test Printf.@sprintf("%0*i", 20, 1024) == Printf.@sprintf("%020i", 1024) + @test Printf.@sprintf("%0*i", 20,-1024) == Printf.@sprintf("%020i", -1024) + @test Printf.@sprintf("%0*u", 20, 1024) == Printf.@sprintf("%020u", 1024) + @test Printf.@sprintf("%0*u", 20, UInt(4294966272)) == Printf.@sprintf("%020u", UInt(4294966272)) + @test Printf.@sprintf("%0*o", 20, 511) == Printf.@sprintf("%020o", 511) + @test Printf.@sprintf("%0*o", 20, UInt(4294966785)) == Printf.@sprintf("%020o", UInt(4294966785)) + @test Printf.@sprintf("%0*x", 20, 305441741) == Printf.@sprintf("%020x", 305441741) + @test Printf.@sprintf("%0*x", 20, UInt(3989525555)) == Printf.@sprintf("%020x", UInt(3989525555)) + @test Printf.@sprintf("%0*X", 20, 305441741) == Printf.@sprintf("%020X", 305441741) + @test Printf.@sprintf("%0*X", 20, UInt(3989525555)) == Printf.@sprintf("%020X", UInt(3989525555)) + @test Printf.@sprintf("%#*o", 20, 511) == Printf.@sprintf("%#20o", 511) + @test Printf.@sprintf("%#*o", 20, UInt(4294966785)) == Printf.@sprintf("%#20o", UInt(4294966785)) + @test Printf.@sprintf("%#*x", 20, 305441741) == Printf.@sprintf("%#20x", 305441741) + @test Printf.@sprintf("%#*x", 20, UInt(3989525555)) == Printf.@sprintf("%#20x", UInt(3989525555)) + @test Printf.@sprintf("%#*X", 20, 305441741) == Printf.@sprintf("%#20X", 305441741) + @test Printf.@sprintf("%#*X", 20, UInt(3989525555)) == Printf.@sprintf("%#20X", UInt(3989525555)) + @test Printf.@sprintf("%#0*o", 20, 511) == Printf.@sprintf("%#020o", 511) + @test Printf.@sprintf("%#0*o", 20, UInt(4294966785)) == Printf.@sprintf("%#020o", UInt(4294966785)) + @test Printf.@sprintf("%#0*x", 20, 305441741) == Printf.@sprintf("%#020x", 305441741) + @test Printf.@sprintf("%#0*x", 20, UInt(3989525555)) == Printf.@sprintf("%#020x", UInt(3989525555)) + @test Printf.@sprintf("%#0*X", 20, 305441741) == Printf.@sprintf("%#020X", 305441741) + @test Printf.@sprintf("%#0*X", 20, UInt(3989525555)) == Printf.@sprintf("%#020X", UInt(3989525555)) + @test Printf.@sprintf("%0-*d", 20, 1024) == Printf.@sprintf("%0-20d", 1024) + @test Printf.@sprintf("%0-*d", 20,-1024) == Printf.@sprintf("%0-20d", -1024) + @test Printf.@sprintf("%0-*i", 20, 1024) == Printf.@sprintf("%0-20i", 1024) + @test Printf.@sprintf("%0-*i", 20,-1024) == Printf.@sprintf("%0-20i", -1024) + @test Printf.@sprintf("%0-*u", 20, 1024) == Printf.@sprintf("%0-20u", 1024) + @test Printf.@sprintf("%0-*u", 20, UInt(4294966272)) == Printf.@sprintf("%0-20u", UInt(4294966272)) + @test Printf.@sprintf("%-0*o", 20, 511) == Printf.@sprintf("%-020o", 511) + @test Printf.@sprintf("%-0*o", 20, UInt(4294966785)) == Printf.@sprintf("%-020o", UInt(4294966785)) + @test Printf.@sprintf("%-0*x", 20, 305441741) == Printf.@sprintf("%-020x", 305441741) + @test Printf.@sprintf("%-0*x", 20, UInt(3989525555)) == Printf.@sprintf("%-020x", UInt(3989525555)) + @test Printf.@sprintf("%-0*X", 20, 305441741) == Printf.@sprintf("%-020X", 305441741) + @test Printf.@sprintf("%-0*X", 20, UInt(3989525555)) == Printf.@sprintf("%-020X", UInt(3989525555)) + @test Printf.@sprintf("%.*d", 20, 1024) == Printf.@sprintf("%.20d", 1024) + @test Printf.@sprintf("%.*d", 20,-1024) == Printf.@sprintf("%.20d", -1024) + @test Printf.@sprintf("%.*i", 20, 1024) == Printf.@sprintf("%.20i", 1024) + @test Printf.@sprintf("%.*i", 20,-1024) == Printf.@sprintf("%.20i", -1024) + @test Printf.@sprintf("%.*u", 20, 1024) == Printf.@sprintf("%.20u", 1024) + @test Printf.@sprintf("%.*u", 20, UInt(4294966272)) == Printf.@sprintf("%.20u", UInt(4294966272)) + @test Printf.@sprintf("%.*o", 20, 511) == Printf.@sprintf("%.20o", 511) + @test Printf.@sprintf("%.*o", 20, UInt(4294966785)) == Printf.@sprintf("%.20o", UInt(4294966785)) + @test Printf.@sprintf("%.*x", 20, 305441741) == Printf.@sprintf("%.20x", 305441741) + @test Printf.@sprintf("%.*x", 20, UInt(3989525555)) == Printf.@sprintf("%.20x", UInt(3989525555)) + @test Printf.@sprintf("%.*X", 20, 305441741) == Printf.@sprintf("%.20X", 305441741) + @test Printf.@sprintf("%.*X", 20, UInt(3989525555)) == Printf.@sprintf("%.20X", UInt(3989525555)) + @test Printf.@sprintf("%*.*d", 20, 5, 1024) == Printf.@sprintf("%20.5d", 1024) + @test Printf.@sprintf("%*.*d", 20, 5, -1024) == Printf.@sprintf("%20.5d", -1024) + @test Printf.@sprintf("%*.*i", 20, 5, 1024) == Printf.@sprintf("%20.5i", 1024) + @test Printf.@sprintf("%*.*i", 20, 5,-1024) == Printf.@sprintf("%20.5i", -1024) + @test Printf.@sprintf("%*.*u", 20, 5, 1024) == Printf.@sprintf("%20.5u", 1024) + @test Printf.@sprintf("%*.*u", 20, 5, UInt(4294966272)) == Printf.@sprintf("%20.5u", UInt(4294966272)) + @test Printf.@sprintf("%*.*o", 20, 5, 511) == Printf.@sprintf("%20.5o", 511) + @test Printf.@sprintf("%*.*o", 20, 5, UInt(4294966785)) == Printf.@sprintf("%20.5o", UInt(4294966785)) + @test Printf.@sprintf("%*.*x", 20, 5, 305441741) == Printf.@sprintf("%20.5x", 305441741) + @test Printf.@sprintf("%*.*x", 20, 10, UInt(3989525555)) == Printf.@sprintf("%20.10x", UInt(3989525555)) + @test Printf.@sprintf("%*.*X", 20, 5, 305441741) == Printf.@sprintf("%20.5X", 305441741) + @test Printf.@sprintf("%*.*X", 20, 10, UInt(3989525555)) == Printf.@sprintf("%20.10X", UInt(3989525555)) + @test Printf.@sprintf("%0*.*d", 20, 5, 1024) == Printf.@sprintf("%020.5d", 1024) + @test Printf.@sprintf("%0*.*d", 20, 5,-1024) == Printf.@sprintf("%020.5d", -1024) + @test Printf.@sprintf("%0*.*i", 20, 5, 1024) == Printf.@sprintf("%020.5i", 1024) + @test Printf.@sprintf("%0*.*i", 20, 5,-1024) == Printf.@sprintf("%020.5i", -1024) + @test Printf.@sprintf("%0*.*u", 20, 5, 1024) == Printf.@sprintf("%020.5u", 1024) + @test Printf.@sprintf("%0*.*u", 20, 5, UInt(4294966272)) == Printf.@sprintf("%020.5u", UInt(4294966272)) + @test Printf.@sprintf("%0*.*o", 20, 5, 511) == Printf.@sprintf("%020.5o", 511) + @test Printf.@sprintf("%0*.*o", 20, 5, UInt(4294966785)) == Printf.@sprintf("%020.5o", UInt(4294966785)) + @test Printf.@sprintf("%0*.*x", 20, 5, 305441741) == Printf.@sprintf("%020.5x", 305441741) + @test Printf.@sprintf("%0*.*x", 20, 10, UInt(3989525555)) == Printf.@sprintf("%020.10x", UInt(3989525555)) + @test Printf.@sprintf("%0*.*X", 20, 5, 305441741) == Printf.@sprintf("%020.5X", 305441741) + @test Printf.@sprintf("%0*.*X", 20, 10, UInt(3989525555)) == Printf.@sprintf("%020.10X", UInt(3989525555)) + @test Printf.@sprintf("%*.0d", 20, 1024) == Printf.@sprintf("%20.0d", 1024) + @test Printf.@sprintf("%*.d", 20,-1024) == Printf.@sprintf("%20.d", -1024) + @test Printf.@sprintf("%*.d", 20, 0) == Printf.@sprintf("%20.d", 0) + @test Printf.@sprintf("%*.0i", 20, 1024) == Printf.@sprintf("%20.0i", 1024) + @test Printf.@sprintf("%*.i", 20,-1024) == Printf.@sprintf("%20.i", -1024) + @test Printf.@sprintf("%*.i", 20, 0) == Printf.@sprintf("%20.i", 0) + @test Printf.@sprintf("%*.u", 20, 1024) == Printf.@sprintf("%20.u", 1024) + @test Printf.@sprintf("%*.0u", 20, UInt(4294966272)) == Printf.@sprintf("%20.0u", UInt(4294966272)) + @test Printf.@sprintf("%*.u", 20, UInt(0)) == Printf.@sprintf("%20.u", UInt(0)) + @test Printf.@sprintf("%*.o", 20, 511) == Printf.@sprintf("%20.o", 511) + @test Printf.@sprintf("%*.0o", 20, UInt(4294966785)) == Printf.@sprintf("%20.0o", UInt(4294966785)) + @test Printf.@sprintf("%*.o", 20, UInt(0)) == Printf.@sprintf("%20.o", UInt(0)) + @test Printf.@sprintf("%*.x", 20, 305441741) == Printf.@sprintf("%20.x", 305441741) + @test Printf.@sprintf("%*.0x", 20, UInt(3989525555)) == Printf.@sprintf("%20.0x", UInt(3989525555)) + @test Printf.@sprintf("%*.x", 20, UInt(0)) == Printf.@sprintf("%20.x", UInt(0)) + @test Printf.@sprintf("%*.X", 20, 305441741) == Printf.@sprintf("%20.X", 305441741) + @test Printf.@sprintf("%*.0X", 20, UInt(3989525555)) == Printf.@sprintf("%20.0X", UInt(3989525555)) + @test Printf.@sprintf("%*.X", 20, UInt(0)) == Printf.@sprintf("%20.X", UInt(0)) + + x = Ref{Int}() + y = Ref{Int}() + @test (Printf.@sprintf("%10s%n", "😉", x); Printf.@sprintf("%*s%n", 10, "😉", y); x[] == y[]) + @test (Printf.@sprintf("%10s%n", "1234", x); Printf.@sprintf("%*s%n", 10, "1234", y); x[] == y[]) + +end + @testset "length modifiers" begin @test_throws Printf.InvalidFormatStringError Printf.Format("%h") @test_throws Printf.InvalidFormatStringError Printf.Format("%hh") From 9b5f39e5190a51667b36de5eef7b7d5c2f757f9b Mon Sep 17 00:00:00 2001 From: Lukas Schwerdt Date: Fri, 3 Feb 2023 02:25:02 +0100 Subject: [PATCH 009/775] Faster radix sort (#48497) Add offset once instead of repeatedly for each element. --- base/sort.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/sort.jl b/base/sort.jl index 2ecc7ff62b291..1af2bc79902ff 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1139,7 +1139,7 @@ function radix_sort_pass!(t, lo, hi, offset, counts, v, shift, chunk_size) counts[i] += 1 # increment that bucket's count end - counts[1] = lo # set target index for the first bucket + counts[1] = lo + offset # set target index for the first bucket cumsum!(counts, counts) # set target indices for subsequent buckets # counts[1:mask+1] now stores indices where the first member of each bucket # belongs, not the number of elements in each bucket. We will put the first element @@ -1150,7 +1150,7 @@ function radix_sort_pass!(t, lo, hi, offset, counts, v, shift, chunk_size) x = v[k] # lookup the element i = (x >> shift)&mask + 1 # compute its bucket's index for this pass j = counts[i] # lookup the target index - t[j + offset] = x # put the element where it belongs + t[j] = x # put the element where it belongs counts[i] = j + 1 # increment the target index for the next end # ↳ element in this bucket end From c0dd6ff8363f948237304821941b06d67014fa6a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 3 Feb 2023 01:07:00 -0500 Subject: [PATCH 010/775] automatically detect and debug deadlocks in package loading (#48504) --- base/loading.jl | 111 +++++++++++++++++++++++++++++++++--------------- test/loading.jl | 34 +++++++++++++++ 2 files changed, 110 insertions(+), 35 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 875127e995960..beaa5e6f73a2b 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1247,20 +1247,15 @@ function _tryrequire_from_serialized(modkey::PkgId, build_id::UInt128) if root_module_exists(modkey) loaded = root_module(modkey) else - loading = get(package_locks, modkey, false) - if loading !== false - # load already in progress for this module - loaded = wait(loading::Threads.Condition) - else - package_locks[modkey] = Threads.Condition(require_lock) + loaded = start_loading(modkey) + if loaded === nothing try modpath = locate_package(modkey) modpath === nothing && return nothing set_pkgorigin_version_path(modkey, String(modpath)) loaded = _require_search_from_serialized(modkey, String(modpath), build_id) finally - loading = pop!(package_locks, modkey) - notify(loading, loaded, all=true) + end_loading(modkey, loaded) end if loaded isa Module insert_extension_triggers(modkey) @@ -1282,26 +1277,21 @@ function _tryrequire_from_serialized(modkey::PkgId, path::String, ocachepath::Un if root_module_exists(modkey) loaded = root_module(modkey) else - loading = get(package_locks, modkey, false) - if loading !== false - # load already in progress for this module - loaded = wait(loading::Threads.Condition) - else - for i in 1:length(depmods) - dep = depmods[i] - dep isa Module && continue - _, depkey, depbuild_id = dep::Tuple{String, PkgId, UInt128} - @assert root_module_exists(depkey) - dep = root_module(depkey) - depmods[i] = dep - end - package_locks[modkey] = Threads.Condition(require_lock) + loaded = start_loading(modkey) + if loaded === nothing try + for i in 1:length(depmods) + dep = depmods[i] + dep isa Module && continue + _, depkey, depbuild_id = dep::Tuple{String, PkgId, UInt128} + @assert root_module_exists(depkey) + dep = root_module(depkey) + depmods[i] = dep + end set_pkgorigin_version_path(modkey, sourcepath) loaded = _include_from_serialized(modkey, path, ocachepath, depmods) finally - loading = pop!(package_locks, modkey) - notify(loading, loaded, all=true) + end_loading(modkey, loaded) end if loaded isa Module insert_extension_triggers(modkey) @@ -1424,7 +1414,65 @@ end end # to synchronize multiple tasks trying to import/using something -const package_locks = Dict{PkgId,Threads.Condition}() +const package_locks = Dict{PkgId,Pair{Task,Threads.Condition}}() + +debug_loading_deadlocks::Bool = true # Enable a slightly more expensive, but more complete algorithm that can handle simultaneous tasks. + # This only triggers if you have multiple tasks trying to load the same package at the same time, + # so it is unlikely to make a difference normally. +function start_loading(modkey::PkgId) + # handle recursive calls to require + assert_havelock(require_lock) + loading = get(package_locks, modkey, nothing) + if loading !== nothing + # load already in progress for this module on the task + task, cond = loading + deps = String[modkey.name] + pkgid = modkey + assert_havelock(cond.lock) + if debug_loading_deadlocks && current_task() !== task + waiters = Dict{Task,Pair{Task,PkgId}}() # invert to track waiting tasks => loading tasks + for each in package_locks + cond2 = each[2][2] + assert_havelock(cond2.lock) + for waiting in cond2.waitq + push!(waiters, waiting => (each[2][1] => each[1])) + end + end + while true + running = get(waiters, task, nothing) + running === nothing && break + task, pkgid = running + push!(deps, pkgid.name) + task === current_task() && break + end + end + if current_task() === task + others = String[modkey.name] # repeat this to emphasize the cycle here + for each in package_locks # list the rest of the packages being loaded too + if each[2][1] === task + other = each[1].name + other == modkey.name || other == pkgid.name || push!(others, other) + end + end + msg = sprint(deps, others) do io, deps, others + print(io, "deadlock detected in loading ") + join(io, deps, " -> ") + print(io, " -> ") + join(io, others, " && ") + end + throw(ConcurrencyViolationError(msg)) + end + return wait(cond) + end + package_locks[modkey] = current_task() => Threads.Condition(require_lock) + return +end + +function end_loading(modkey::PkgId, @nospecialize loaded) + loading = pop!(package_locks, modkey) + notify(loading[2], loaded, all=true) + nothing +end # to notify downstream consumers that a module was successfully loaded # Callbacks take the form (mod::Base.PkgId) -> nothing. @@ -1666,16 +1714,10 @@ end # Returns `nothing` or the new(ish) module function _require(pkg::PkgId, env=nothing) assert_havelock(require_lock) - # handle recursive calls to require - loading = get(package_locks, pkg, false) - if loading !== false - # load already in progress for this module - return wait(loading::Threads.Condition) - end - package_locks[pkg] = Threads.Condition(require_lock) + loaded = start_loading(pkg) + loaded === nothing || return loaded last = toplevel_load[] - loaded = nothing try toplevel_load[] = false # perform the search operation to select the module file require intends to load @@ -1753,8 +1795,7 @@ function _require(pkg::PkgId, env=nothing) end finally toplevel_load[] = last - loading = pop!(package_locks, pkg) - notify(loading, loaded, all=true) + end_loading(pkg, loaded) end return loaded end diff --git a/test/loading.jl b/test/loading.jl index 6b84bdc7bb3e1..bb6500ffb4eb8 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1087,3 +1087,37 @@ end empty!(Base.DEPOT_PATH) append!(Base.DEPOT_PATH, original_depot_path) + +@testset "loading deadlock detector" begin + pkid1 = Base.PkgId("pkgid1") + pkid2 = Base.PkgId("pkgid2") + pkid3 = Base.PkgId("pkgid3") + pkid4 = Base.PkgId("pkgid4") + e = Base.Event() + @test nothing === @lock Base.require_lock Base.start_loading(pkid4) # module pkgid4 + @test nothing === @lock Base.require_lock Base.start_loading(pkid1) # module pkgid1 + t1 = @async begin + @test nothing === @lock Base.require_lock Base.start_loading(pkid2) # @async module pkgid2; using pkgid1; end + notify(e) + @test "loaded_pkgid1" == @lock Base.require_lock Base.start_loading(pkid1) + @lock Base.require_lock Base.end_loading(pkid2, "loaded_pkgid2") + end + wait(e) + reset(e) + t2 = @async begin + @test nothing === @lock Base.require_lock Base.start_loading(pkid3) # @async module pkgid3; using pkgid2; end + notify(e) + @test "loaded_pkgid2" == @lock Base.require_lock Base.start_loading(pkid2) + @lock Base.require_lock Base.end_loading(pkid3, "loaded_pkgid3") + end + wait(e) + reset(e) + @test_throws(ConcurrencyViolationError("deadlock detected in loading pkgid3 -> pkgid2 -> pkgid1 -> pkgid3 && pkgid4"), + @lock Base.require_lock Base.start_loading(pkid3)).value # try using pkgid3 + @test_throws(ConcurrencyViolationError("deadlock detected in loading pkgid4 -> pkgid4 && pkgid1"), + @lock Base.require_lock Base.start_loading(pkid4)).value # try using pkgid4 + @lock Base.require_lock Base.end_loading(pkid1, "loaded_pkgid1") # end + @lock Base.require_lock Base.end_loading(pkid4, "loaded_pkgid4") # end + wait(t2) + wait(t1) +end From 960d771efaf8847f8583318f2e375e112ab08183 Mon Sep 17 00:00:00 2001 From: Steve Kelly Date: Fri, 3 Feb 2023 03:22:22 -0500 Subject: [PATCH 011/775] replace `/bin/date` with `date` in version_git.sh (#48489) This could primarily be an error on NixOS-like systems, where `/bin/date` does not exist. This could be for various reasons, such as reproducible builds and/or symlinked FSH. --- base/version_git.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/version_git.sh b/base/version_git.sh index 39ebb1b8ec5ee..e7a0e6e834b77 100644 --- a/base/version_git.sh +++ b/base/version_git.sh @@ -63,14 +63,14 @@ fi date_string=$git_time case $(uname) in Darwin | FreeBSD) - date_string="$(/bin/date -jr $git_time -u '+%Y-%m-%d %H:%M %Z')" + date_string="$(date -jr $git_time -u '+%Y-%m-%d %H:%M %Z')" ;; MINGW*) git_time=$(git log -1 --pretty=format:%ci) - date_string="$(/bin/date --date="$git_time" -u '+%Y-%m-%d %H:%M %Z')" + date_string="$(date --date="$git_time" -u '+%Y-%m-%d %H:%M %Z')" ;; *) - date_string="$(/bin/date --date="@$git_time" -u '+%Y-%m-%d %H:%M %Z')" + date_string="$(date --date="@$git_time" -u '+%Y-%m-%d %H:%M %Z')" ;; esac if [ $(git describe --tags --exact-match 2> /dev/null) ]; then From b82ef42807ca6c810cedbb6252aa2b014e3a72b3 Mon Sep 17 00:00:00 2001 From: Ujjwal Sarswat <76774914+vmpyr@users.noreply.github.com> Date: Fri, 3 Feb 2023 14:51:53 +0530 Subject: [PATCH 012/775] Add test for String <-> SubString conversions (#48482) --- test/strings/basic.jl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 33c64410454ef..d20c4890d0b0a 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -938,6 +938,21 @@ end end end +@testset "Conversion to Type{Union{String, SubString{String}}}" begin + str = "abc" + substr = SubString(str) + for T in [String, SubString{String}] + conv_str = convert(T, str) + conv_substr = convert(T, substr) + + if T == String + @test conv_str === conv_substr === str + elseif T == SubString{String} + @test conv_str === conv_substr === substr + end + end +end + @test unsafe_wrap(Vector{UInt8},"\xcc\xdd\xee\xff\x80") == [0xcc,0xdd,0xee,0xff,0x80] @test iterate("a", 1)[2] == 2 From 532643d6392268791a789608ee6029b881bdadd1 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 3 Feb 2023 07:15:56 -0500 Subject: [PATCH 013/775] only evaluate one iteration per broken test (#48478) --- test/abstractarray.jl | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 4928f35f5fad0..070e5d7a7b289 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -1160,8 +1160,9 @@ Base.unsafe_convert(::Type{Ptr{T}}, S::Strider{T}) where {T} = pointer(S.data, S Ps = Strider{Int, 3}(vec(A), 1, strides(A)[collect(perm)], sz[collect(perm)]) @test pointer(Ap) == pointer(Sp) == pointer(Ps) for i in 1:length(Ap) - # This is intentionally disabled due to ambiguity - @test_broken pointer(Ap, i) == pointer(Sp, i) == pointer(Ps, i) + # This is intentionally disabled due to ambiguity. See `Base.pointer(A::PermutedDimsArray, i::Integer)`. + # But only evaluate one iteration as broken to reduce test report noise + i == 1 && @test_broken pointer(Ap, i) == pointer(Sp, i) == pointer(Ps, i) @test P[i] == Ap[i] == Sp[i] == Ps[i] end Pv = view(P, idxs[collect(perm)]...) @@ -1180,8 +1181,9 @@ Base.unsafe_convert(::Type{Ptr{T}}, S::Strider{T}) where {T} = pointer(S.data, S Svp = Base.PermutedDimsArray(Sv, perm) @test pointer(Avp) == pointer(Svp) for i in 1:length(Avp) - # This is intentionally disabled due to ambiguity - @test_broken pointer(Avp, i) == pointer(Svp, i) + # This is intentionally disabled due to ambiguity. See `Base.pointer(A::PermutedDimsArray, i::Integer)` + # But only evaluate one iteration as broken to reduce test report noise + i == 1 && @test_broken pointer(Avp, i) == pointer(Svp, i) @test Ip[i] == Vp[i] == Avp[i] == Svp[i] end end @@ -1220,8 +1222,9 @@ end Ps = Strider{Int, 2}(vec(A), 1, strides(A)[collect(perm)], sz[collect(perm)]) @test pointer(Ap) == pointer(Sp) == pointer(Ps) == pointer(At) == pointer(Aa) for i in 1:length(Ap) - # This is intentionally disabled due to ambiguity - @test_broken pointer(Ap, i) == pointer(Sp, i) == pointer(Ps, i) == pointer(At, i) == pointer(Aa, i) == pointer(St, i) == pointer(Sa, i) + # This is intentionally disabled due to ambiguity. See `Base.pointer(A::PermutedDimsArray, i::Integer)` + # But only evaluate one iteration as broken to reduce test report noise + i == 1 && @test_broken pointer(Ap, i) == pointer(Sp, i) == pointer(Ps, i) == pointer(At, i) == pointer(Aa, i) == pointer(St, i) == pointer(Sa, i) @test pointer(Ps, i) == pointer(At, i) == pointer(Aa, i) == pointer(St, i) == pointer(Sa, i) @test P[i] == Ap[i] == Sp[i] == Ps[i] == At[i] == Aa[i] == St[i] == Sa[i] end @@ -1247,8 +1250,9 @@ end Svp = Base.PermutedDimsArray(Sv, perm) @test pointer(Avp) == pointer(Svp) == pointer(Avt) == pointer(Ava) for i in 1:length(Avp) - # This is intentionally disabled due to ambiguity - @test_broken pointer(Avp, i) == pointer(Svp, i) == pointer(Avt, i) == pointer(Ava, i) == pointer(Svt, i) == pointer(Sva, i) + # This is intentionally disabled due to ambiguity. See `Base.pointer(A::PermutedDimsArray, i::Integer)` + # But only evaluate one iteration as broken to reduce test report noise + i == 1 && @test_broken pointer(Avp, i) == pointer(Svp, i) == pointer(Avt, i) == pointer(Ava, i) == pointer(Svt, i) == pointer(Sva, i) @test pointer(Avt, i) == pointer(Ava, i) == pointer(Svt, i) == pointer(Sva, i) @test Vp[i] == Avp[i] == Svp[i] == Avt[i] == Ava[i] == Svt[i] == Sva[i] end From bebff47519759c752e49c25f6a4ac5431d2b97d3 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Fri, 3 Feb 2023 11:20:24 -0500 Subject: [PATCH 014/775] fix type stability of `powermod` (#48367) * type stability of powermod regressed in https://github.com/JuliaLang/julia/pull/48192 --- base/intfuncs.jl | 14 ++++++++------ test/intfuncs.jl | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 98a7098196500..5b3ada3b89877 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -375,18 +375,20 @@ function powermod(x::Integer, p::Integer, m::T) where T<:Integer # but will work for integer types like `BigInt` that don't have `typemin` defined # It needs special handling otherwise will cause overflow problem. if p == -p - t = powermod(invmod(x, m), -(p÷2), m) - t = mod(widemul(t, t), m) - iseven(p) && return t + imod = invmod(x, m) + rhalf = powermod(imod, -(p÷2), m) + r::T = mod(widemul(rhalf, rhalf), m) + isodd(p) && (r = mod(widemul(r, imod), m)) #else odd - return mod(widemul(t, invmod(x, m)), m) + return r + elseif p < 0 + return powermod(invmod(x, m), -p, m) end - p < 0 && return powermod(invmod(x, m), -p, m) (m == 1 || m == -1) && return zero(m) b = oftype(m,mod(x,m)) # this also checks for divide by zero t = prevpow(2, p) - r::T = 1 + r = 1 while true if p >= t r = mod(widemul(r,b),m) diff --git a/test/intfuncs.jl b/test/intfuncs.jl index 2215cbaf36a56..ceaac235a3da9 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -273,6 +273,8 @@ end @test powermod(2, big(3), 5) == 3 @test powermod(2, big(3), -5) == -2 + @inferred powermod(2, -2, -5) + @inferred powermod(big(2), -2, UInt(5)) end @testset "nextpow/prevpow" begin From d5911c02fa6c06af01ad8916c95cd9327c9c597e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Fri, 27 Jan 2023 23:02:20 +0000 Subject: [PATCH 015/775] Fix `Base.libblas_name`/`Base.liblapack_name` On Windows they need to include the major soversion of libblastrampoline. --- base/Base.jl | 7 ++++--- stdlib/libblastrampoline_jll/src/libblastrampoline_jll.jl | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 20e729256664f..8cb0295ab0e20 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -186,9 +186,10 @@ end include(strcat((length(Core.ARGS)>=2 ? Core.ARGS[2] : ""), "build_h.jl")) # include($BUILDROOT/base/build_h.jl) include(strcat((length(Core.ARGS)>=2 ? Core.ARGS[2] : ""), "version_git.jl")) # include($BUILDROOT/base/version_git.jl) -# These used to be in build_h.jl and are retained for backwards compatibility -const libblas_name = "libblastrampoline" -const liblapack_name = "libblastrampoline" +# These used to be in build_h.jl and are retained for backwards compatibility. +# NOTE: keep in sync with `libblastrampoline_jll.libblastrampoline`. +const libblas_name = "libblastrampoline" * (Sys.iswindows() ? "-5" : "") +const liblapack_name = libblas_name # numeric operations include("hashing.jl") diff --git a/stdlib/libblastrampoline_jll/src/libblastrampoline_jll.jl b/stdlib/libblastrampoline_jll/src/libblastrampoline_jll.jl index 108b7d6558079..c223b513382d7 100644 --- a/stdlib/libblastrampoline_jll/src/libblastrampoline_jll.jl +++ b/stdlib/libblastrampoline_jll/src/libblastrampoline_jll.jl @@ -18,6 +18,7 @@ artifact_dir = "" libblastrampoline_handle = C_NULL libblastrampoline_path = "" +# NOTE: keep in sync with `Base.libblas_name` and `Base.liblapack_name`. const libblastrampoline = if Sys.iswindows() "libblastrampoline-5.dll" elseif Sys.isapple() From f2d7055fc1f796e2c7a5e10bc61925a41d8eda13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Sat, 28 Jan 2023 00:08:18 +0000 Subject: [PATCH 016/775] Move `libblas_name`/`liblapack_name` to after `Sys` module --- base/Base.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 8cb0295ab0e20..3b02e674593c9 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -186,11 +186,6 @@ end include(strcat((length(Core.ARGS)>=2 ? Core.ARGS[2] : ""), "build_h.jl")) # include($BUILDROOT/base/build_h.jl) include(strcat((length(Core.ARGS)>=2 ? Core.ARGS[2] : ""), "version_git.jl")) # include($BUILDROOT/base/version_git.jl) -# These used to be in build_h.jl and are retained for backwards compatibility. -# NOTE: keep in sync with `libblastrampoline_jll.libblastrampoline`. -const libblas_name = "libblastrampoline" * (Sys.iswindows() ? "-5" : "") -const liblapack_name = libblas_name - # numeric operations include("hashing.jl") include("rounding.jl") @@ -291,6 +286,11 @@ include("sysinfo.jl") include("libc.jl") using .Libc: getpid, gethostname, time +# These used to be in build_h.jl and are retained for backwards compatibility. +# NOTE: keep in sync with `libblastrampoline_jll.libblastrampoline`. +const libblas_name = "libblastrampoline" * (Sys.iswindows() ? "-5" : "") +const liblapack_name = libblas_name + # Logging include("logging.jl") using .CoreLogging From a93ac543394a40e90fa7030e5edc6d845a718c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Sat, 28 Jan 2023 17:17:17 +0000 Subject: [PATCH 017/775] Add test for use of `Base.libblas_name` --- stdlib/LinearAlgebra/test/blas.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/stdlib/LinearAlgebra/test/blas.jl b/stdlib/LinearAlgebra/test/blas.jl index cbaf0e4628b9a..9139c8dcf79f1 100644 --- a/stdlib/LinearAlgebra/test/blas.jl +++ b/stdlib/LinearAlgebra/test/blas.jl @@ -4,6 +4,7 @@ module TestBLAS using Test, LinearAlgebra, Random using LinearAlgebra: BlasReal, BlasComplex +using Libdl: dlsym, dlopen fabs(x::Real) = abs(x) fabs(x::Complex) = abs(real(x)) + abs(imag(x)) @@ -713,4 +714,11 @@ end end end +# Make sure we can use `Base.libblas_name`. Avoid causing +# https://github.com/JuliaLang/julia/issues/48427 again. +@testset "libblas_name" begin + dot_sym = dlsym(dlopen(Base.libblas_name), "cblas_ddot" * (Sys.WORD_SIZE == 64 ? "64_" : "")) + @test 23.0 === @ccall $(dot_sym)(2::Cint, [2.0, 3.0]::Ref{Cdouble}, 1::Cint, [4.0, 5.0]::Ref{Cdouble}, 1::Cint)::Cdouble +end + end # module TestBLAS From cdc6eafb3681840ddef395aa7ffad45db3064e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Fri, 3 Feb 2023 16:38:53 +0000 Subject: [PATCH 018/775] [LinearAlgebra] Fix `libblas_name` test Use `Int` as type for integer arguments, instead of `Cint`. --- stdlib/LinearAlgebra/test/blas.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/test/blas.jl b/stdlib/LinearAlgebra/test/blas.jl index 9139c8dcf79f1..4252d9ee7938b 100644 --- a/stdlib/LinearAlgebra/test/blas.jl +++ b/stdlib/LinearAlgebra/test/blas.jl @@ -718,7 +718,7 @@ end # https://github.com/JuliaLang/julia/issues/48427 again. @testset "libblas_name" begin dot_sym = dlsym(dlopen(Base.libblas_name), "cblas_ddot" * (Sys.WORD_SIZE == 64 ? "64_" : "")) - @test 23.0 === @ccall $(dot_sym)(2::Cint, [2.0, 3.0]::Ref{Cdouble}, 1::Cint, [4.0, 5.0]::Ref{Cdouble}, 1::Cint)::Cdouble + @test 23.0 === @ccall $(dot_sym)(2::Int, [2.0, 3.0]::Ref{Cdouble}, 1::Int, [4.0, 5.0]::Ref{Cdouble}, 1::Int)::Cdouble end end # module TestBLAS From d7b20629241dbcbe1f6e8f4ab5cfb5e1d5dc087a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 3 Feb 2023 14:16:22 -0500 Subject: [PATCH 019/775] ensure extension triggers are only run by the package that satified them Oscar had noticed a problem where loading an extension might trigger loading another package, which might re-trigger attempting to load the same extension. And then that causes a deadlock from waiting for the extension to finish loading (it appears to be a recursive import triggered from multiple places). Instead alter the representation, to be similar to a semaphore, so that it will be loaded only exactly by the final package that satisfied all dependencies for it. This approach could still encounter an issue if the user imports a package (C) which it does not explicitly list as a dependency for extension. But it is unclear to me that we actually want to solve that, since it weakens and delays the premise that Bext is available shortly after A and B are both loaded. \# module C; using A, B; end;; module A; end;; module B; end;; module Bext; using C; end \# using C, Bext / A / B starts C -> requires A, B to load loads A -> defines Bext (extends B, but tries to also require C) loads B -> loads Bext (which waits for C -> deadlock!) finish C -> now safe to load Bext While using this order would have been fine. \# using A, B, Bext / C loads A -> defines Bext (extends B, but tries to also require C) loads B -> starts Bext loads C finish Bext --- base/loading.jl | 134 ++++++++++++++++++++++++++++++------------------ 1 file changed, 85 insertions(+), 49 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index beaa5e6f73a2b..cd3c2c7450747 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1076,9 +1076,9 @@ function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String) end function run_package_callbacks(modkey::PkgId) + run_extension_callbacks(modkey) assert_havelock(require_lock) unlock(require_lock) - run_extension_callbacks() try for callback in package_callbacks invokelatest(callback, modkey) @@ -1099,14 +1099,13 @@ end ############## mutable struct ExtensionId - const id::PkgId # Could be symbol? - const parentid::PkgId - const triggers::Vector{PkgId} # What packages have to be loaded for the extension to get loaded - triggered::Bool - succeeded::Bool + const id::PkgId + const parentid::PkgId # just need the name, for printing + ntriggers::Int # how many more packages must be defined until this is loaded end -const EXT_DORMITORY = ExtensionId[] +const EXT_DORMITORY = Dict{PkgId,Vector{ExtensionId}}() +const EXT_DORMITORY_FAILED = ExtensionId[] function insert_extension_triggers(pkg::PkgId) pkg.uuid === nothing && return @@ -1159,59 +1158,84 @@ end function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, <:Any}, weakdeps::Dict{String, <:Any}) for (ext::String, triggers::Union{String, Vector{String}}) in extensions triggers isa String && (triggers = [triggers]) - triggers_id = PkgId[] id = PkgId(uuid5(parent.uuid, ext), ext) + gid = ExtensionId(id, parent, 1 + length(triggers)) + trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, parent) + push!(trigger1, gid) for trigger in triggers # TODO: Better error message if this lookup fails? uuid_trigger = UUID(weakdeps[trigger]::String) - push!(triggers_id, PkgId(uuid_trigger, trigger)) + trigger_id = PkgId(uuid_trigger, trigger) + if !haskey(Base.loaded_modules, trigger_id) || haskey(package_locks, trigger_id) + trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, trigger_id) + push!(trigger1, gid) + else + gid.ntriggers -= 1 + end end - gid = ExtensionId(id, parent, triggers_id, false, false) - push!(EXT_DORMITORY, gid) end end -function run_extension_callbacks(; force::Bool=false) - try - # TODO, if `EXT_DORMITORY` becomes very long, do something smarter - for extid in EXT_DORMITORY - extid.succeeded && continue - !force && extid.triggered && continue - if all(x -> haskey(Base.loaded_modules, x) && !haskey(package_locks, x), extid.triggers) - ext_not_allowed_load = nothing - extid.triggered = true - # It is possible that some of the triggers were loaded in an environment - # below the one of the parent. This will cause a load failure when the - # pkg ext tries to load the triggers. Therefore, check this first - # before loading the pkg ext. - for trigger in extid.triggers - pkgenv = Base.identify_package_env(extid.id, trigger.name) - if pkgenv === nothing - ext_not_allowed_load = trigger - break - else - pkg, env = pkgenv - path = Base.locate_package(pkg, env) - if path === nothing - ext_not_allowed_load = trigger - break - end - end - end - if ext_not_allowed_load !== nothing - @debug "Extension $(extid.id.name) of $(extid.parentid.name) not loaded due to \ - $(ext_not_allowed_load.name) loaded in environment lower in load path" - else - require(extid.id) - @debug "Extension $(extid.id.name) of $(extid.parentid.name) loaded" - end - extid.succeeded = true - end - end +function run_extension_callbacks(extid::ExtensionId) + assert_havelock(require_lock) + succeeded = try + _require_prelocked(extid.id) + @debug "Extension $(extid.id.name) of $(extid.parentid.name) loaded" + true catch # Try to continue loading if loading an extension errors errs = current_exceptions() @error "Error during loading of extension" exception=errs + false + end + return succeeded +end + +function run_extension_callbacks(pkgid::PkgId) + assert_havelock(require_lock) + # take ownership of extids that depend on this pkgid + extids = pop!(EXT_DORMITORY, pkgid, nothing) + extids === nothing && return + for extid in extids + if extid.ntriggers > 0 + # It is possible that pkgid was loaded in an environment + # below the one of the parent. This will cause a load failure when the + # pkg ext tries to load the triggers. Therefore, check this first + # before loading the pkg ext. + pkgenv = Base.identify_package_env(extid.id, pkgid.name) + ext_not_allowed_load = false + if pkgenv === nothing + ext_not_allowed_load = true + else + pkg, env = pkgenv + path = Base.locate_package(pkg, env) + if path === nothing + ext_not_allowed_load = true + end + end + if ext_not_allowed_load + @debug "Extension $(extid.id.name) of $(extid.parentid.name) will not be loaded \ + since $(pkgid.name) loaded in environment lower in load path" + # indicate extid is expected to fail + extid.ntriggers *= -1 + else + # indicate pkgid is loaded + extid.ntriggers -= 1 + end + end + if extid.ntriggers < 0 + # indicate pkgid is loaded + extid.ntriggers += 1 + succeeded = false + else + succeeded = true + end + 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) + end end nothing end @@ -1224,7 +1248,19 @@ This is used in cases where the automatic loading of an extension failed due to some problem with the extension. Instead of restarting the Julia session, the extension can be fixed, and this function run. """ -retry_load_extensions() = run_extension_callbacks(; force=true) +function retry_load_extensions() + @lock require_lock begin + # this copy is desired since run_extension_callbacks will release this lock + # so this can still mutate the list to drop successful ones + failed = copy(EXT_DORMITORY_FAILED) + empty!(EXT_DORMITORY_FAILED) + filter!(failed) do extid + return !run_extension_callbacks(extid) + end + prepend!(EXT_DORMITORY_FAILED, failed) + end + nothing +end """ get_extension(parent::Module, extension::Symbol) From c4fd8a48383235e818c44231d7507168c27f393b Mon Sep 17 00:00:00 2001 From: Sukera <11753998+Seelengrab@users.noreply.github.com> Date: Sat, 4 Feb 2023 04:25:12 +0100 Subject: [PATCH 020/775] Use `sum` in `count` for constant folding of type based predicates (#48454) * Use `sum` in `count` for constant folding for type based predicates. * Use existing `_bool` functionality for type assertion --------- Co-authored-by: Sukera --- base/reduce.jl | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index ae2671a2e746a..61a0f466b2902 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -1354,15 +1354,7 @@ count(itr; init=0) = count(identity, itr; init) count(f, itr; init=0) = _simple_count(f, itr, init) -_simple_count(pred, itr, init) = _simple_count_helper(Generator(pred, itr), init) - -function _simple_count_helper(g, init::T) where {T} - n::T = init - for x in g - n += x::Bool - end - return n -end +_simple_count(pred, itr, init) = sum(_bool(pred), itr; init) function _simple_count(::typeof(identity), x::Array{Bool}, init::T=0) where {T} n::T = init From b9398a3a187914c79e9a627370a0a85d720fe7fb Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 4 Feb 2023 03:52:20 -0500 Subject: [PATCH 021/775] precompile: do not reanimate zombie external methods (#48475) Backedges are only applicable to cache objects with max_world==Inf Fix #48391 --- src/staticdata.c | 12 ++---- src/staticdata_utils.c | 84 ++++++++++++++++++++---------------------- test/precompile.jl | 79 +++++++++++++++++++++------------------ 3 files changed, 87 insertions(+), 88 deletions(-) diff --git a/src/staticdata.c b/src/staticdata.c index 804193ff90229..94173c2a8a162 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -2181,7 +2181,6 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new // edges: [caller1, ext_targets_indexes1, ...] for worklist-owned methods calling external methods assert(edges_map == NULL); - htable_new(&external_mis, 0); // we need external_mis until after `jl_collect_edges` finishes // Save the inferred code from newly inferred, external methods *new_specializations = queue_external_cis(newly_inferred); @@ -2209,14 +2208,9 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new *edges = jl_alloc_vec_any(0); *method_roots_list = jl_alloc_vec_any(0); // Collect the new method roots - htable_t methods_with_newspecs; - htable_new(&methods_with_newspecs, 0); - jl_collect_methods(&methods_with_newspecs, *new_specializations); - jl_collect_new_roots(*method_roots_list, &methods_with_newspecs, worklist_key); - htable_free(&methods_with_newspecs); - jl_collect_edges(*edges, *ext_targets); - } - htable_free(&external_mis); + jl_collect_new_roots(*method_roots_list, *new_specializations, worklist_key); + jl_collect_edges(*edges, *ext_targets, *new_specializations); + } assert(edges_map == NULL); // jl_collect_edges clears this when done JL_GC_POP(); diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index fc109836a03c0..cb97db1083e32 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -1,5 +1,5 @@ static htable_t new_code_instance_validate; -static htable_t external_mis; + // inverse of backedges graph (caller=>callees hash) jl_array_t *edges_map JL_GLOBALLY_ROOTED = NULL; // rooted for the duration of our uses of this @@ -108,11 +108,6 @@ JL_DLLEXPORT void jl_push_newly_inferred(jl_value_t* ci) } -static int method_instance_in_queue(jl_method_instance_t *mi) -{ - return ptrhash_get(&external_mis, mi) != HT_NOTFOUND; -} - // compute whether a type references something internal to worklist // and thus could not have existed before deserialize // and thus does not need delayed unique-ing @@ -216,10 +211,10 @@ static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited, return found; } -// given the list of CodeInstances that were inferred during the -// build, select those that are (1) external, and (2) are inferred to be called -// from the worklist or explicitly added by a `precompile` statement. -// Also prepares for method_instance_in_queue queries. +// Given the list of CodeInstances that were inferred during the build, select +// those that are (1) external, (2) still valid, and (3) are inferred to be +// called from the worklist or explicitly added by a `precompile` statement. +// These will be preserved in the image. static jl_array_t *queue_external_cis(jl_array_t *list) { if (list == NULL) @@ -238,17 +233,12 @@ static jl_array_t *queue_external_cis(jl_array_t *list) assert(jl_is_code_instance(ci)); jl_method_instance_t *mi = ci->def; jl_method_t *m = mi->def.method; - if (jl_is_method(m)) { - if (jl_object_in_image((jl_value_t*)m->module)) { - if (ptrhash_get(&external_mis, mi) == HT_NOTFOUND) { - int found = has_backedge_to_worklist(mi, &visited, &stack); - assert(found == 0 || found == 1); - assert(stack.len == 0); - if (found == 1) { - ptrhash_put(&external_mis, mi, mi); - jl_array_ptr_1d_push(new_specializations, (jl_value_t*)ci); - } - } + if (jl_is_method(m) && jl_object_in_image((jl_value_t*)m->module)) { + int found = has_backedge_to_worklist(mi, &visited, &stack); + assert(found == 0 || found == 1); + assert(stack.len == 0); + if (found == 1 && ci->max_world == ~(size_t)0) { + jl_array_ptr_1d_push(new_specializations, (jl_value_t*)ci); } } } @@ -259,31 +249,25 @@ static jl_array_t *queue_external_cis(jl_array_t *list) } // New roots for external methods -static void jl_collect_methods(htable_t *mset, jl_array_t *new_specializations) +static void jl_collect_new_roots(jl_array_t *roots, jl_array_t *new_specializations, uint64_t key) { - size_t i, l = new_specializations ? jl_array_len(new_specializations) : 0; - jl_value_t *v; - jl_method_t *m; - for (i = 0; i < l; i++) { - v = jl_array_ptr_ref(new_specializations, i); - assert(jl_is_code_instance(v)); - m = ((jl_code_instance_t*)v)->def->def.method; + htable_t mset; + htable_new(&mset, 0); + size_t l = new_specializations ? jl_array_len(new_specializations) : 0; + for (size_t i = 0; i < l; i++) { + jl_code_instance_t *ci = (jl_code_instance_t*)jl_array_ptr_ref(new_specializations, i); + assert(jl_is_code_instance(ci)); + jl_method_t *m = ci->def->def.method; assert(jl_is_method(m)); - ptrhash_put(mset, (void*)m, (void*)m); + ptrhash_put(&mset, (void*)m, (void*)m); } -} - -static void jl_collect_new_roots(jl_array_t *roots, const htable_t *mset, uint64_t key) -{ - size_t i, sz = mset->size; int nwithkey; - jl_method_t *m; - void *const *table = mset->table; + void *const *table = mset.table; jl_array_t *newroots = NULL; JL_GC_PUSH1(&newroots); - for (i = 0; i < sz; i += 2) { + for (size_t i = 0; i < mset.size; i += 2) { if (table[i+1] != HT_NOTFOUND) { - m = (jl_method_t*)table[i]; + jl_method_t *m = (jl_method_t*)table[i]; assert(jl_is_method(m)); nwithkey = nroots_with_key(m, key); if (nwithkey) { @@ -305,6 +289,7 @@ static void jl_collect_new_roots(jl_array_t *roots, const htable_t *mset, uint64 } } JL_GC_POP(); + htable_free(&mset); } // Create the forward-edge map (caller => callees) @@ -422,9 +407,18 @@ static void jl_record_edges(jl_method_instance_t *caller, arraylist_t *wq, jl_ar // Extract `edges` and `ext_targets` from `edges_map` // `edges` = [caller1, targets_indexes1, ...], the list of methods and their edges // `ext_targets` is [invokesig1, callee1, matches1, ...], the edges for each target -static void jl_collect_edges(jl_array_t *edges, jl_array_t *ext_targets) +static void jl_collect_edges(jl_array_t *edges, jl_array_t *ext_targets, jl_array_t *external_cis) { size_t world = jl_atomic_load_acquire(&jl_world_counter); + htable_t external_mis; + htable_new(&external_mis, 0); + if (external_cis) { + for (size_t i = 0; i < jl_array_len(external_cis); i++) { + jl_code_instance_t *ci = (jl_code_instance_t*)jl_array_ptr_ref(external_cis, i); + jl_method_instance_t *mi = ci->def; + ptrhash_put(&external_mis, (void*)mi, (void*)mi); + } + } arraylist_t wq; arraylist_new(&wq, 0); void **table = (void**)jl_array_data(edges_map); // edges_map is caller => callees @@ -438,10 +432,11 @@ static void jl_collect_edges(jl_array_t *edges, jl_array_t *ext_targets) continue; assert(jl_is_method_instance(caller) && jl_is_method(caller->def.method)); if (!jl_object_in_image((jl_value_t*)caller->def.method->module) || - method_instance_in_queue(caller)) { + ptrhash_get(&external_mis, caller) != HT_NOTFOUND) { jl_record_edges(caller, &wq, edges); } } + htable_free(&external_mis); while (wq.len) { jl_method_instance_t *caller = (jl_method_instance_t*)arraylist_pop(&wq); jl_record_edges(caller, &wq, edges); @@ -1061,6 +1056,7 @@ static void jl_insert_backedges(jl_array_t *edges, jl_array_t *ext_targets, jl_a htable_reset(&visited, l); for (i = 0; i < l; i++) { jl_code_instance_t *ci = (jl_code_instance_t*)jl_array_ptr_ref(ci_list, i); + assert(ci->max_world == 1 || ci->max_world == ~(size_t)0); assert(ptrhash_get(&visited, (void*)ci->def) == HT_NOTFOUND); // check that we don't have multiple cis for same mi ptrhash_put(&visited, (void*)ci->def, (void*)ci); } @@ -1121,7 +1117,7 @@ static void jl_insert_backedges(jl_array_t *edges, jl_array_t *ext_targets, jl_a assert(jl_is_code_instance(ci)); jl_code_instance_t *codeinst = (jl_code_instance_t*)ci; remove_code_instance_from_validation(codeinst); // mark it as handled - assert(codeinst->min_world >= world && codeinst->inferred); + assert(codeinst->min_world == world && codeinst->inferred); codeinst->max_world = ~(size_t)0; if (jl_rettype_inferred(caller, world, ~(size_t)0) == jl_nothing) { jl_mi_cache_insert(caller, codeinst); @@ -1161,8 +1157,8 @@ static void validate_new_code_instances(void) //assert(0 && "unexpected unprocessed CodeInstance found"); jl_code_instance_t *ci = (jl_code_instance_t*)new_code_instance_validate.table[i]; JL_GC_PROMISE_ROOTED(ci); // TODO: this needs a root (or restructuring to avoid it) - assert(ci->min_world >= world && ci->inferred); - ci->max_world = ~(size_t)0; + assert(ci->min_world == world && ci->inferred); + assert(ci->max_world == ~(size_t)0); jl_method_instance_t *caller = ci->def; if (jl_rettype_inferred(caller, world, ~(size_t)0) == jl_nothing) { jl_mi_cache_insert(caller, ci); diff --git a/test/precompile.jl b/test/precompile.jl index 0febfecb78b69..dbe40f3ba6204 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -1686,64 +1686,73 @@ end precompile_test_harness("DynamicExpressions") do load_path # https://github.com/JuliaLang/julia/pull/47184#issuecomment-1364716312 write(joinpath(load_path, "Float16MWE.jl"), - """ - module Float16MWE - struct Node{T} - val::T - end - doconvert(::Type{<:Node}, val) = convert(Float16, val) - precompile(Tuple{typeof(doconvert), Type{Node{Float16}}, Float64}) - end # module Float16MWE - """) + """ + module Float16MWE + struct Node{T} + val::T + end + doconvert(::Type{<:Node}, val) = convert(Float16, val) + precompile(Tuple{typeof(doconvert), Type{Node{Float16}}, Float64}) + end # module Float16MWE + """) Base.compilecache(Base.PkgId("Float16MWE")) - (@eval (using Float16MWE)) - Base.invokelatest() do - @test Float16MWE.doconvert(Float16MWE.Node{Float16}, -1.2) === Float16(-1.2) - end + @eval using Float16MWE + @test @invokelatest(Float16MWE.doconvert(Float16MWE.Node{Float16}, -1.2)) === Float16(-1.2) end precompile_test_harness("BadInvalidations") do load_path write(joinpath(load_path, "BadInvalidations.jl"), - """ - module BadInvalidations + """ + module BadInvalidations Base.Experimental.@compiler_options compile=min optimize=1 getval() = Base.a_method_to_overwrite_in_test() getval() - end # module BadInvalidations - """) + end # module BadInvalidations + """) Base.compilecache(Base.PkgId("BadInvalidations")) - (@eval Base a_method_to_overwrite_in_test() = inferencebarrier(2)) - (@eval (using BadInvalidations)) - Base.invokelatest() do - @test BadInvalidations.getval() === 2 - end + @eval Base a_method_to_overwrite_in_test() = inferencebarrier(2) + @eval using BadInvalidations + @test Base.invokelatest(BadInvalidations.getval) === 2 end # https://github.com/JuliaLang/julia/issues/48074 precompile_test_harness("WindowsCacheOverwrite") do load_path # https://github.com/JuliaLang/julia/pull/47184#issuecomment-1364716312 write(joinpath(load_path, "WindowsCacheOverwrite.jl"), - """ - module WindowsCacheOverwrite - - end # module - """) + """ + module WindowsCacheOverwrite + end # module + """) ji, ofile = Base.compilecache(Base.PkgId("WindowsCacheOverwrite")) - (@eval (using WindowsCacheOverwrite)) + @eval using WindowsCacheOverwrite write(joinpath(load_path, "WindowsCacheOverwrite.jl"), - """ - module WindowsCacheOverwrite - - f() = "something new" - - end # module - """) + """ + module WindowsCacheOverwrite + f() = "something new" + end # module + """) ji_2, ofile_2 = Base.compilecache(Base.PkgId("WindowsCacheOverwrite")) @test ofile_2 == Base.ocachefile_from_cachefile(ji_2) end +precompile_test_harness("Issue #48391") do load_path + write(joinpath(load_path, "I48391.jl"), + """ + module I48391 + struct SurrealFinite <: Real end + precompile(Tuple{typeof(Base.isless), SurrealFinite, SurrealFinite}) + Base.:(<)(x::SurrealFinite, y::SurrealFinite) = "good" + end + """) + ji, ofile = Base.compilecache(Base.PkgId("I48391")) + @eval using I48391 + x = Base.invokelatest(I48391.SurrealFinite) + @test Base.invokelatest(isless, x, x) === "good" + @test_throws ErrorException isless(x, x) +end + empty!(Base.DEPOT_PATH) append!(Base.DEPOT_PATH, original_depot_path) empty!(Base.LOAD_PATH) From 17dee39962e8aed2b2c3146819de8b366bcb98f8 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Sat, 4 Feb 2023 04:29:03 -0500 Subject: [PATCH 022/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=20957b55a89=20to=203ac94b211=20(#48517)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/md5 | 1 + .../Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/sha512 | 1 + .../Pkg-957b55a896d5cb496da134ea7bf3ee70de07ef2a.tar.gz/md5 | 1 - .../Pkg-957b55a896d5cb496da134ea7bf3ee70de07ef2a.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/md5 create mode 100644 deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-957b55a896d5cb496da134ea7bf3ee70de07ef2a.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-957b55a896d5cb496da134ea7bf3ee70de07ef2a.tar.gz/sha512 diff --git a/deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/md5 b/deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/md5 new file mode 100644 index 0000000000000..a01caf07381ee --- /dev/null +++ b/deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/md5 @@ -0,0 +1 @@ +65e3625975629eeb21f8a6522ce4e305 diff --git a/deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/sha512 b/deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/sha512 new file mode 100644 index 0000000000000..c4479e8fa3bb2 --- /dev/null +++ b/deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/sha512 @@ -0,0 +1 @@ +b2527e5005f56d812f24c85fd432d701c6777ae7fa8882b1ed5c49246225797ee58019135ea501aa61b4c7fa5f5795647b36b591f96b409c24c58874a988c921 diff --git a/deps/checksums/Pkg-957b55a896d5cb496da134ea7bf3ee70de07ef2a.tar.gz/md5 b/deps/checksums/Pkg-957b55a896d5cb496da134ea7bf3ee70de07ef2a.tar.gz/md5 deleted file mode 100644 index 29c5e1852d255..0000000000000 --- a/deps/checksums/Pkg-957b55a896d5cb496da134ea7bf3ee70de07ef2a.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -f4839f375eb20b675d01aa7c98137803 diff --git a/deps/checksums/Pkg-957b55a896d5cb496da134ea7bf3ee70de07ef2a.tar.gz/sha512 b/deps/checksums/Pkg-957b55a896d5cb496da134ea7bf3ee70de07ef2a.tar.gz/sha512 deleted file mode 100644 index 2ca3a097f4c7a..0000000000000 --- a/deps/checksums/Pkg-957b55a896d5cb496da134ea7bf3ee70de07ef2a.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -8149e5d0f34a0d64d06a3302841e44bb1663ed60a2321a136109d20a5fe5beca5fc2988ded4e5bb5e69eb8030577e655b1eff456b0dbdb3857615622b6d0b465 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 5660d343487fa..450cdad8413e8 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 957b55a896d5cb496da134ea7bf3ee70de07ef2a +PKG_SHA1 = 3ac94b211fa7917a6a77a6b51d81e4b807e278b5 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 4d9eb896db40684cbdf9c991a51642c3e6f99276 Mon Sep 17 00:00:00 2001 From: Philip Bittihn Date: Sun, 5 Feb 2023 01:43:23 +0100 Subject: [PATCH 023/775] Add path tracking options from #44359 to docs (#48527) --- doc/src/manual/command-line-interface.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/src/manual/command-line-interface.md b/doc/src/manual/command-line-interface.md index e35cbf5e313e7..54c56a354c7a3 100644 --- a/doc/src/manual/command-line-interface.md +++ b/doc/src/manual/command-line-interface.md @@ -125,8 +125,10 @@ The following is a complete list of command-line switches available when launchi |`--check-bounds={yes\|no\|auto*}` |Emit bounds checks always, never, or respect `@inbounds` declarations ($)| |`--math-mode={ieee,fast}` |Disallow or enable unsafe floating point optimizations (overrides `@fastmath` declaration)| |`--code-coverage[={none*\|user\|all}]` |Count executions of source lines (omitting setting is equivalent to `user`)| +|`--code-coverage=@` |Count executions but only in files that fall under the given file path/directory. The `@` prefix is required to select this option. A `@` with no path will track the current directory.| |`--code-coverage=tracefile.info` |Append coverage information to the LCOV tracefile (filename supports format tokens).| |`--track-allocation[={none*\|user\|all}]` |Count bytes allocated by each source line (omitting setting is equivalent to "user")| +|`--track-allocation=@` |Count bytes but only in files that fall under the given file path/directory. The `@` prefix is required to select this option. A `@` with no path will track the current directory.| |`--bug-report=KIND` |Launch a bug report session. It can be used to start a REPL, run a script, or evaluate expressions. It first tries to use BugReporting.jl installed in current environment and falls back to the latest compatible BugReporting.jl if not. For more information, see `--bug-report=help`.| |`--compile={yes*\|no\|all\|min}` |Enable or disable JIT compiler, or request exhaustive or minimal compilation| |`--output-o ` |Generate an object file (including system image data)| From d3355d24dcffa50807f17c0727e6ae10d4afae26 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 5 Feb 2023 02:23:40 +0100 Subject: [PATCH 024/775] fix `retry_load_extensions` docstring typo (#48530) --- base/loading.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/loading.jl b/base/loading.jl index beaa5e6f73a2b..351ce87c1dd68 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1217,7 +1217,7 @@ function run_extension_callbacks(; force::Bool=false) end """ - load_extensions() + retry_load_extensions() Loads all the (not yet loaded) extensions that have their extension-dependencies loaded. This is used in cases where the automatic loading of an extension failed From 07c4244caa8cd6611f42296891ac74f9d6ae25ff Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Sat, 4 Feb 2023 20:47:45 -0500 Subject: [PATCH 025/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=203ac94b211=20to=20ed505db0b=20(#48528)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/md5 | 1 - .../Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/sha512 | 1 - .../Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/md5 | 1 + .../Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/md5 create mode 100644 deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/sha512 diff --git a/deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/md5 b/deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/md5 deleted file mode 100644 index a01caf07381ee..0000000000000 --- a/deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -65e3625975629eeb21f8a6522ce4e305 diff --git a/deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/sha512 b/deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/sha512 deleted file mode 100644 index c4479e8fa3bb2..0000000000000 --- a/deps/checksums/Pkg-3ac94b211fa7917a6a77a6b51d81e4b807e278b5.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -b2527e5005f56d812f24c85fd432d701c6777ae7fa8882b1ed5c49246225797ee58019135ea501aa61b4c7fa5f5795647b36b591f96b409c24c58874a988c921 diff --git a/deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/md5 b/deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/md5 new file mode 100644 index 0000000000000..530e2397030e2 --- /dev/null +++ b/deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/md5 @@ -0,0 +1 @@ +13f881dc90d09d12675dafeef34f72c6 diff --git a/deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/sha512 b/deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/sha512 new file mode 100644 index 0000000000000..b6b0b1c462111 --- /dev/null +++ b/deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/sha512 @@ -0,0 +1 @@ +fd1fba3f022bfea61d1bf9dfb016a60ceb9be7d4bb87574e01acdb6ca9b818030bbc163c9bd823f0388ed3b0f200c43ac4aceda92b137f98605dcc37ed8c8e64 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 450cdad8413e8..f4a26b0554af1 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 3ac94b211fa7917a6a77a6b51d81e4b807e278b5 +PKG_SHA1 = ed505db0bb329df25fa0eaba9de53ecb5fa89c93 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 36d035c8a18ac625cde2a975109d5807b6692f19 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Fri, 3 Feb 2023 17:03:42 +0800 Subject: [PATCH 026/775] Intersect: remove dead code `jl_is_unionall(xi) == jl_is_unionall(yi) == 0` here. --- src/subtype.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 9f6f6cb0add67..ac939d088394a 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -968,17 +968,6 @@ static int check_vararg_length(jl_value_t *v, ssize_t n, jl_stenv_t *e) static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e); -struct subtype_tuple_env { - jl_datatype_t *xd, *yd; - jl_value_t *lastx, *lasty; - size_t lx, ly; - size_t i, j; - int vx, vy; - jl_value_t *vtx; - jl_value_t *vty; - jl_vararg_kind_t vvx, vvy; -} JL_ROOTED_VALUE_COLLECTION; - static int subtype_tuple_varargs( jl_vararg_t *vtx, jl_vararg_t *vty, size_t vx, size_t vy, @@ -3073,9 +3062,6 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten (yb && jl_is_long(yb->lb) && ly-1+jl_unbox_long(yb->lb) != len)) { res = jl_bottom_type; } - else if (param == 2 && jl_is_unionall(xi) != jl_is_unionall(yi)) { - res = jl_bottom_type; - } else { if (xb) set_var_to_const(xb, jl_box_long(len-lx+1), yb); if (yb) set_var_to_const(yb, jl_box_long(len-ly+1), xb); From 3d0278b7b2e4cb62323ef4d5b2222737522d2690 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 2 Feb 2023 14:13:49 +0800 Subject: [PATCH 027/775] Subtype: skip more identical check for `X::Tuple <: Y::Tuple` Should improve the performance where `Y` has fixed length. Fix the example from #39967. The root cause there is a exponential growth in Left union. Fortunately, we won't encounter this problem for concrete `X`, as there's no 0-depth `Union`. --- src/subtype.c | 20 ++++++++------------ test/subtype.jl | 3 +++ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index ac939d088394a..19a3e3286982b 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1081,7 +1081,7 @@ static int subtype_tuple_tail(jl_datatype_t *xd, jl_datatype_t *yd, int8_t R, jl { size_t lx = jl_nparams(xd); size_t ly = jl_nparams(yd); - size_t i = 0, j = 0, vx = 0, vy = 0, x_reps = 0; + size_t i = 0, j = 0, vx = 0, vy = 0, x_reps = 1; jl_value_t *lastx = NULL, *lasty = NULL; jl_value_t *xi = NULL, *yi = NULL; @@ -1131,21 +1131,17 @@ static int subtype_tuple_tail(jl_datatype_t *xd, jl_datatype_t *yd, int8_t R, jl return !!vx; xi = vx ? jl_unwrap_vararg(xi) : xi; - int x_same = lastx && jl_egal(xi, lastx); - if (vy) { - yi = jl_unwrap_vararg(yi); - // keep track of number of consecutive identical types compared to Vararg - if (x_same) - x_reps++; - else - x_reps = 1; - } + yi = vy ? jl_unwrap_vararg(yi) : yi; + int x_same = vx > 1 || (lastx && obviously_egal(xi, lastx)); + int y_same = vy > 1 || (lasty && obviously_egal(yi, lasty)); + // keep track of number of consecutive identical subtyping + x_reps = y_same && x_same ? x_reps + 1 : 1; if (x_reps > 2) { - // an identical type on the left doesn't need to be compared to a Vararg + // an identical type on the left doesn't need to be compared to the same // element type on the right more than twice. } else if (x_same && e->Runions.depth == 0 && - ((yi == lasty && !jl_has_free_typevars(xi) && !jl_has_free_typevars(yi)) || + ((y_same && !jl_has_free_typevars(xi) && !jl_has_free_typevars(yi)) || (yi == lastx && !vx && vy && jl_is_concrete_type(xi)))) { // fast path for repeated elements } diff --git a/test/subtype.jl b/test/subtype.jl index 674608c4d0451..c0fc41abda174 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2426,6 +2426,9 @@ end @test !(Tuple{Any, Any, Any} <: Tuple{Any, Vararg{T}} where T) +# issue #39967 +@test (NTuple{27, T} where {S, T<:Union{Array, Array{S}}}) <: Tuple{Array, Array, Vararg{AbstractArray, 25}} + abstract type MyAbstract47877{C}; end struct MyType47877{A,B} <: MyAbstract47877{A} end let A = Tuple{Type{T}, T} where T, From 0fffc2575fbeae5405f06711b42539e64d8f7ed1 Mon Sep 17 00:00:00 2001 From: AbhaySingh004 Date: Sun, 5 Feb 2023 15:30:51 +0530 Subject: [PATCH 028/775] Builder-installer.iss modified to show install button in wizard form instead of next button --- contrib/windows/build-installer.iss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contrib/windows/build-installer.iss b/contrib/windows/build-installer.iss index 4f5f0259d2f2c..8a6505526e9b0 100644 --- a/contrib/windows/build-installer.iss +++ b/contrib/windows/build-installer.iss @@ -147,6 +147,12 @@ end; procedure CurPageChanged(CurPageID: Integer); begin + ; Fixing bug in git Issue #48521 + if CurPageID = wpSelectProgramGroup then + WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall) + else + WizardForm.NextButton.Caption := SetupMessage(msgButtonNext); + case CurPageID of wpWelcome: WizardForm.Color := WizardForm.WelcomePage.Color; wpFinished: WizardForm.Color := WizardForm.FinishedPage.Color; From 6ea92b45ddc04be453312a3e7e57c8e4be84fd74 Mon Sep 17 00:00:00 2001 From: AbhaySingh004 Date: Sun, 5 Feb 2023 18:37:30 +0530 Subject: [PATCH 029/775] build-installer.iss modified the comments --- contrib/windows/build-installer.iss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/windows/build-installer.iss b/contrib/windows/build-installer.iss index 8a6505526e9b0..67ba4d7bcb3ef 100644 --- a/contrib/windows/build-installer.iss +++ b/contrib/windows/build-installer.iss @@ -147,7 +147,7 @@ end; procedure CurPageChanged(CurPageID: Integer); begin - ; Fixing bug in git Issue #48521 + ; change button text from "next" to "install" when ReadyPage is disabled. if CurPageID = wpSelectProgramGroup then WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall) else From 253e825ff1b2c318796e1c23640d868f22f3972c Mon Sep 17 00:00:00 2001 From: AbhaySingh004 <74596419+AbhaySingh004@users.noreply.github.com> Date: Sun, 5 Feb 2023 19:56:41 +0530 Subject: [PATCH 030/775] Update contrib/windows/build-installer.iss Co-authored-by: woclass --- contrib/windows/build-installer.iss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/windows/build-installer.iss b/contrib/windows/build-installer.iss index 67ba4d7bcb3ef..9f4f06747bba0 100644 --- a/contrib/windows/build-installer.iss +++ b/contrib/windows/build-installer.iss @@ -156,6 +156,8 @@ begin case CurPageID of wpWelcome: WizardForm.Color := WizardForm.WelcomePage.Color; wpFinished: WizardForm.Color := WizardForm.FinishedPage.Color; + // change button text from "next" to "install" when ReadyPage is disabled. + wpSelectTasks: WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall); else WizardForm.Color := WizardForm.InnerPage.Color; end; From 4cfa0fc927620954b78a25134c1a23484c377b3b Mon Sep 17 00:00:00 2001 From: AbhaySingh004 Date: Sun, 5 Feb 2023 20:12:46 +0530 Subject: [PATCH 031/775] build-installer.iss modified overiding the finish page bug removed --- contrib/windows/build-installer.iss | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/contrib/windows/build-installer.iss b/contrib/windows/build-installer.iss index 67ba4d7bcb3ef..345100cf8c0b4 100644 --- a/contrib/windows/build-installer.iss +++ b/contrib/windows/build-installer.iss @@ -145,17 +145,15 @@ begin end; end; + procedure CurPageChanged(CurPageID: Integer); begin - ; change button text from "next" to "install" when ReadyPage is disabled. - if CurPageID = wpSelectProgramGroup then - WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall) - else - WizardForm.NextButton.Caption := SetupMessage(msgButtonNext); - case CurPageID of wpWelcome: WizardForm.Color := WizardForm.WelcomePage.Color; wpFinished: WizardForm.Color := WizardForm.FinishedPage.Color; + + ; change button text from "next" to "install" when ReadyPage is disabled. + wpSelectTasks: WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall); else WizardForm.Color := WizardForm.InnerPage.Color; end; From a62d7bbf2112f6afed6ca25bea89f5379cdba2ee Mon Sep 17 00:00:00 2001 From: AbhaySingh004 Date: Sun, 5 Feb 2023 20:55:08 +0530 Subject: [PATCH 032/775] modified builder-installer.iss with removing if-else block --- contrib/windows/build-installer.iss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/windows/build-installer.iss b/contrib/windows/build-installer.iss index 345100cf8c0b4..e99d8c72f65ac 100644 --- a/contrib/windows/build-installer.iss +++ b/contrib/windows/build-installer.iss @@ -151,8 +151,8 @@ begin case CurPageID of wpWelcome: WizardForm.Color := WizardForm.WelcomePage.Color; wpFinished: WizardForm.Color := WizardForm.FinishedPage.Color; - - ; change button text from "next" to "install" when ReadyPage is disabled. + + ;change button text from "next" to "install" when ReadyPage is disabled. wpSelectTasks: WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall); else WizardForm.Color := WizardForm.InnerPage.Color; From 51e3bc33dec9be9ebe5ad5fe5f4da56a3bab33c9 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sun, 5 Feb 2023 11:18:18 -0500 Subject: [PATCH 033/775] typelimits: Prevent accidental infinite growth in size limiting (#48421) * typelimits: Prevent accidental infinite growth in size limiting There's a few related issues here: 1. Type size limiting depended on the identity of contained TypeVars. E.g. `Base.rewrap_unionall(Foo{Base.unwrap_unionall(A)}, A)`, would not be limited while the equivalent with typevars substituted would be. This is obviously undesirable because the identity of typevars is an implementation detail. 2. We were forcing `tupledepth` to 1 in the typevar case to allow replacing typevars by something concrete. However, this also allowed typevars being replaced by something derived from `sources`, which could be huge and itself contain TypeVars, allowing infinite growth. 3. There was a type query that was run on types containg free typevars. This would have asserted in a debug build and gave wrong answers in a release build. Fix all three issues, which together addresses an inference non-termination seen in #48329. * wip: fix `apply_type_tfunc` accuracy (#48329) --------- Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> --- base/compiler/tfuncs.jl | 36 ++++++++++++++++-------------------- base/compiler/typelimits.jl | 16 ++++++++-------- test/compiler/inference.jl | 27 ++++++++++++++++++++++----- 3 files changed, 46 insertions(+), 33 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 1673929df1129..71dcd0ff97ea0 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1757,14 +1757,12 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K, push!(tparams, ai.tv) else uncertain = true - # These blocks improve type info but make compilation a bit slower. - # XXX - #unw = unwrap_unionall(ai) - #isT = isType(unw) - #if isT && isa(ai,UnionAll) && contains_is(outervars, ai.var) - # ai = rename_unionall(ai) - # unw = unwrap_unionall(ai) - #end + unw = unwrap_unionall(ai) + isT = isType(unw) + if isT && isa(ai,UnionAll) && contains_is(outervars, ai.var) + ai = rename_unionall(ai) + unw = unwrap_unionall(ai) + end ai_w = widenconst(ai) ub = ai_w isa Type && ai_w <: Type ? instanceof_tfunc(ai)[1] : Any if istuple @@ -1772,19 +1770,17 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K, # then this could be a Vararg type. if i == largs && ub === Any push!(tparams, Vararg) - # XXX - #elseif isT - # push!(tparams, rewrap_unionall(unw.parameters[1], ai)) + elseif isT + push!(tparams, rewrap_unionall(unw.parameters[1], ai)) else push!(tparams, Any) end - # XXX - #elseif isT - # push!(tparams, unw.parameters[1]) - # while isa(ai, UnionAll) - # push!(outervars, ai.var) - # ai = ai.body - # end + elseif isT + push!(tparams, unw.parameters[1]) + while isa(ai, UnionAll) + push!(outervars, ai.var) + ai = ai.body + end else # Is this the second parameter to a NamedTuple? if isa(uw, DataType) && uw.name === _NAMEDTUPLE_NAME && isa(ua, UnionAll) && uw.parameters[2] === ua.var @@ -2278,7 +2274,7 @@ function builtin_effects(𝕃::AbstractLattice, @nospecialize(f::Builtin), argin else effect_free = ALWAYS_FALSE end - nothrow = (!(!isempty(argtypes) && isvarargtype(argtypes[end])) && builtin_nothrow(𝕃, f, argtypes, rt)) + nothrow = (isempty(argtypes) || !isvarargtype(argtypes[end])) && builtin_nothrow(𝕃, f, argtypes, rt) if contains_is(_INACCESSIBLEMEM_BUILTINS, f) inaccessiblememonly = ALWAYS_TRUE elseif contains_is(_ARGMEM_BUILTINS, f) @@ -2460,7 +2456,7 @@ function intrinsic_effects(f::IntrinsicFunction, argtypes::Vector{Any}) consistent = contains_is(_INCONSISTENT_INTRINSICS, f) ? ALWAYS_FALSE : ALWAYS_TRUE effect_free = !(f === Intrinsics.pointerset) ? ALWAYS_TRUE : ALWAYS_FALSE - nothrow = (!(!isempty(argtypes) && isvarargtype(argtypes[end])) && intrinsic_nothrow(f, argtypes)) + nothrow = (isempty(argtypes) || !isvarargtype(argtypes[end])) && intrinsic_nothrow(f, argtypes) return Effects(EFFECTS_TOTAL; consistent, effect_free, nothrow) end diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index ed9db007bdbc8..8bb87a38dd4c9 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -35,6 +35,12 @@ end # try to find `type` somewhere in `comparison` type # at a minimum nesting depth of `mindepth` function is_derived_type(@nospecialize(t), @nospecialize(c), mindepth::Int) + if has_free_typevars(t) || has_free_typevars(c) + # Don't allow finding types with free typevars. These strongly depend + # on identity and we do not make any effort to make sure this returns + # sensible results in that case. + return false + end if t === c return mindepth <= 1 end @@ -87,10 +93,7 @@ function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVec return t # fast path: unparameterized are always simple else ut = unwrap_unionall(t) - if isa(ut, DataType) && isa(c, Type) && c !== Union{} && c <: t - # TODO: need to check that the UnionAll bounds on t are limited enough too - return t # t is already wider than the comparison in the type lattice - elseif is_derived_type_from_any(ut, sources, depth) + if is_derived_type_from_any(ut, sources, depth) return t # t isn't something new end end @@ -208,9 +211,6 @@ function type_more_complex(@nospecialize(t), @nospecialize(c), sources::SimpleVe return false # Bottom is as simple as they come elseif isa(t, DataType) && isempty(t.parameters) return false # fastpath: unparameterized types are always finite - elseif tupledepth > 0 && isa(unwrap_unionall(t), DataType) && isa(c, Type) && c !== Union{} && c <: t - # TODO: need to check that the UnionAll bounds on t are limited enough too - return false # t is already wider than the comparison in the type lattice elseif tupledepth > 0 && is_derived_type_from_any(unwrap_unionall(t), sources, depth) return false # t isn't something new end @@ -227,7 +227,7 @@ function type_more_complex(@nospecialize(t), @nospecialize(c), sources::SimpleVe end # rules for various comparison types if isa(c, TypeVar) - tupledepth = 1 # allow replacing a TypeVar with a concrete value (since we know the UnionAll must be in covariant position) + tupledepth = 1 if isa(t, TypeVar) return !(t.lb === Union{} || t.lb === c.lb) || # simplify lb towards Union{} type_more_complex(t.ub, c.ub, sources, depth + 1, tupledepth, 0) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index f70b1f73f55ad..d35d0e4a68117 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -27,7 +27,6 @@ let comparison = Tuple{X, X} where X<:Tuple @test Core.Compiler.limit_type_size(sig, comparison, comparison, 100, 100) == Tuple{Tuple, Tuple} @test Core.Compiler.limit_type_size(sig, ref, comparison, 100, 100) == Tuple{Any, Any} @test Core.Compiler.limit_type_size(Tuple{sig}, Tuple{ref}, comparison, 100, 100) == Tuple{Tuple{Any, Any}} - @test Core.Compiler.limit_type_size(sig, ref, Tuple{comparison}, 100, 100) == Tuple{Tuple{X, X} where X<:Tuple, Tuple{X, X} where X<:Tuple} @test Core.Compiler.limit_type_size(ref, sig, Union{}, 100, 100) == ref end @@ -51,6 +50,13 @@ let va = ccall(:jl_type_intersection_with_env, Any, (Any, Any), Tuple{Tuple}, Tu @test Core.Compiler.__limit_type_size(Tuple, va, Core.svec(va, Union{}), 2, 2) === Tuple end +mutable struct TS14009{T}; end +let A = TS14009{TS14009{TS14009{TS14009{TS14009{T}}}}} where {T}, + B = Base.rewrap_unionall(TS14009{Base.unwrap_unionall(A)}, A) + + @test Core.Compiler.Compiler.limit_type_size(B, A, A, 2, 2) == TS14009 +end + # issue #42835 @test !Core.Compiler.type_more_complex(Int, Any, Core.svec(), 1, 1, 1) @test !Core.Compiler.type_more_complex(Int, Type{Int}, Core.svec(), 1, 1, 1) @@ -81,7 +87,7 @@ end @test !Core.Compiler.type_more_complex(Type{1}, Type{2}, Core.svec(), 1, 1, 1) @test Core.Compiler.type_more_complex(Type{Union{Float32,Float64}}, Union{Float32,Float64}, Core.svec(Union{Float32,Float64}), 1, 1, 1) @test !Core.Compiler.type_more_complex(Type{Union{Float32,Float64}}, Union{Float32,Float64}, Core.svec(Union{Float32,Float64}), 0, 1, 1) -@test_broken Core.Compiler.type_more_complex(Type{<:Union{Float32,Float64}}, Type{Union{Float32,Float64}}, Core.svec(Union{Float32,Float64}), 1, 1, 1) +@test Core.Compiler.type_more_complex(Type{<:Union{Float32,Float64}}, Type{Union{Float32,Float64}}, Core.svec(Union{Float32,Float64}), 1, 1, 1) @test Core.Compiler.type_more_complex(Type{<:Union{Float32,Float64}}, Any, Core.svec(Union{Float32,Float64}), 1, 1, 1) @@ -2741,6 +2747,17 @@ end |> only === Int @test only(Base.return_types(Core.apply_type, Tuple{Any})) == Any @test only(Base.return_types(Core.apply_type, Tuple{Any,Any})) == Any +# `apply_type_tfunc` accuracy for constrained type construction +# https://github.com/JuliaLang/julia/issues/47089 +import Core: Const +import Core.Compiler: apply_type_tfunc +struct Issue47089{A,B} end +let 𝕃 = Core.Compiler.fallback_lattice + A = Type{<:Integer} + @test apply_type_tfunc(𝕃, Const(Issue47089), A, A) <: (Type{Issue47089{A,B}} where {A<:Integer, B<:Integer}) +end +@test only(Base.return_types(keys, (Dict{String},))) == Base.KeySet{String, T} where T<:(Dict{String}) + # PR 27351, make sure optimized type intersection for method invalidation handles typevars abstract type AbstractT27351 end @@ -3690,10 +3707,10 @@ Base.iterate(::Itr41839_3 , i) = i < 16 ? (i, i + 1) : nothing # issue #32699 f32699(a) = (id = a[1],).id -@test Base.return_types(f32699, (Vector{Union{Int,Missing}},)) == Any[Union{Int,Missing}] +@test only(Base.return_types(f32699, (Vector{Union{Int,Missing}},))) == Union{Int,Missing} g32699(a) = Tuple{a} -@test Base.return_types(g32699, (Type{<:Integer},))[1] == Type{<:Tuple{Any}} -@test Base.return_types(g32699, (Type,))[1] == Type{<:Tuple} +@test only(Base.return_types(g32699, (Type{<:Integer},))) <: Type{<:Tuple{Any}} +@test only(Base.return_types(g32699, (Type,))) <: Type{<:Tuple} # Inference precision of union-split calls function f_apply_union_split(fs, x) From 4e5360c5b92cef63240b9aad8d4bb22d558441c1 Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Sun, 5 Feb 2023 13:18:56 -0300 Subject: [PATCH 034/775] timing (#46197) Co-authored-by: Diogo Netto Co-authored-by: Jameson Nash --- src/gc.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/gc.c b/src/gc.c index dc01af35c67c4..8ca028ed213d5 100644 --- a/src/gc.c +++ b/src/gc.c @@ -2850,16 +2850,6 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) gc_cblist_root_scanner, (collection)); } gc_mark_loop(ptls); - gc_num.since_sweep += gc_num.allocd; - JL_PROBE_GC_MARK_END(scanned_bytes, perm_scanned_bytes); - gc_settime_premark_end(); - gc_time_mark_pause(gc_start_time, scanned_bytes, perm_scanned_bytes); - uint64_t end_mark_time = jl_hrtime(); - uint64_t mark_time = end_mark_time - start_mark_time; - gc_num.mark_time = mark_time; - gc_num.total_mark_time += mark_time; - int64_t actual_allocd = gc_num.since_sweep; - // marking is over // 4. check for objects to finalize clear_weak_refs(); @@ -2897,7 +2887,18 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) gc_mark_finlist(mq, &to_finalize, 0); gc_mark_loop(ptls); mark_reset_age = 0; + + gc_num.since_sweep += gc_num.allocd; + JL_PROBE_GC_MARK_END(scanned_bytes, perm_scanned_bytes); + gc_settime_premark_end(); + gc_time_mark_pause(gc_start_time, scanned_bytes, perm_scanned_bytes); + uint64_t end_mark_time = jl_hrtime(); + uint64_t mark_time = end_mark_time - start_mark_time; + gc_num.mark_time = mark_time; + gc_num.total_mark_time += mark_time; + int64_t actual_allocd = gc_num.since_sweep; gc_settime_postmark_end(); + // marking is over // Flush everything in mark cache gc_sync_all_caches_nolock(ptls); From 7827b0886f1eb4660309d9697a95e67aa4ad342a Mon Sep 17 00:00:00 2001 From: robsmith11 Date: Mon, 6 Feb 2023 02:24:28 +0700 Subject: [PATCH 035/775] add CTRL_R to repl precompile (#48539) Co-authored-by: robsmith11 --- contrib/generate_precompile.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 76956fac24f18..e6acefd53753a 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -27,6 +27,7 @@ const fancyprint = (stdout isa Base.TTY) && Base.get_bool_env("CI", false) !== t ## CTRL_C = '\x03' +CTRL_R = '\x12' UP_ARROW = "\e[A" DOWN_ARROW = "\e[B" @@ -69,6 +70,7 @@ display([1 2; 3 4]) @time 1+1 ; pwd $CTRL_C +$CTRL_R$CTRL_C ? reinterpret using Ra\t$CTRL_C \\alpha\t$CTRL_C From baf9680360265b3c95bfeda47aa26e89db7be68b Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Sun, 5 Feb 2023 14:46:10 -0500 Subject: [PATCH 036/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Downloads=20stdlib=20from=20030cfb3=20to=200098e40=20(#48538)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dilum Aluthge --- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - stdlib/Downloads.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/md5 create mode 100644 deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/sha512 delete mode 100644 deps/checksums/Downloads-030cfb3fefd29e87405cb689fb8178613131f55c.tar.gz/md5 delete mode 100644 deps/checksums/Downloads-030cfb3fefd29e87405cb689fb8178613131f55c.tar.gz/sha512 diff --git a/deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/md5 b/deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/md5 new file mode 100644 index 0000000000000..c8013e35d1c0b --- /dev/null +++ b/deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/md5 @@ -0,0 +1 @@ +8b7ed41dce0e56996e20465a3ccdbd1f diff --git a/deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/sha512 b/deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/sha512 new file mode 100644 index 0000000000000..8e0eae1949d61 --- /dev/null +++ b/deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/sha512 @@ -0,0 +1 @@ +7fa5bd303971f5e0e0bc0ca48bf9850f26f909a3f8b2d8331d33ea200aa40ff812604af33a44b5b29dca9f03c43d279425ec16113d740b21d345b41c760a52e2 diff --git a/deps/checksums/Downloads-030cfb3fefd29e87405cb689fb8178613131f55c.tar.gz/md5 b/deps/checksums/Downloads-030cfb3fefd29e87405cb689fb8178613131f55c.tar.gz/md5 deleted file mode 100644 index 95b4a593a88b1..0000000000000 --- a/deps/checksums/Downloads-030cfb3fefd29e87405cb689fb8178613131f55c.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -dc5f63b5cdab35d1699bed558229ec83 diff --git a/deps/checksums/Downloads-030cfb3fefd29e87405cb689fb8178613131f55c.tar.gz/sha512 b/deps/checksums/Downloads-030cfb3fefd29e87405cb689fb8178613131f55c.tar.gz/sha512 deleted file mode 100644 index 9c6e0c43c24ef..0000000000000 --- a/deps/checksums/Downloads-030cfb3fefd29e87405cb689fb8178613131f55c.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -99d5fd3a41d8e17f6955b2ff379bf7d2b9fed388b9fe22358a3abf70f743da95301d6790f6d1a3a185b61f5d7e392ef663a3cf52552da8ae69f9d943aafb2df3 diff --git a/stdlib/Downloads.version b/stdlib/Downloads.version index 16fdbd047c4de..2d5477564f6c4 100644 --- a/stdlib/Downloads.version +++ b/stdlib/Downloads.version @@ -1,4 +1,4 @@ DOWNLOADS_BRANCH = master -DOWNLOADS_SHA1 = 030cfb3fefd29e87405cb689fb8178613131f55c +DOWNLOADS_SHA1 = 0098e40feb6d4992920ddfbed181150ef412f189 DOWNLOADS_GIT_URL := https://github.com/JuliaLang/Downloads.jl.git DOWNLOADS_TAR_URL = https://api.github.com/repos/JuliaLang/Downloads.jl/tarball/$1 From 2a9f4412a00cbaf366895083f651d5d7906cce1f Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Sun, 5 Feb 2023 14:46:50 -0500 Subject: [PATCH 037/775] More tests for rem2pi branches (#48541) --- test/numbers.jl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/numbers.jl b/test/numbers.jl index 870acd37c089c..351a244554732 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -2676,6 +2676,24 @@ end @test rem2pi(T(-8), RoundNearest) ≈ -8+2pi @test rem2pi(T(-8), RoundDown) ≈ -8+4pi @test rem2pi(T(-8), RoundUp) ≈ -8+2pi + # to hit n is even and n % 4 == 2 condition + @test rem2pi(T(3), RoundToZero) == 3 + @test rem2pi(T(3), RoundNearest) == 3 + @test rem2pi(T(3), RoundDown) == 3 + @test rem2pi(T(3), RoundUp) ≈ 3 - 2π + @test rem2pi(T(-3), RoundToZero) == -3 + @test rem2pi(T(-3), RoundNearest) == -3 + @test rem2pi(T(-3), RoundDown) ≈ -3 + 2π + @test rem2pi(T(-3), RoundUp) == -3 + # to hit even n condition and n % 4 != 2 condition + @test rem2pi(T(13), RoundToZero) ≈ 13-4π + @test rem2pi(T(13), RoundNearest) ≈ 13-4π + @test rem2pi(T(13), RoundDown) ≈ 13-4π + @test rem2pi(T(13), RoundUp) ≈ 13-6π + @test rem2pi(T(-13), RoundToZero) ≈ -13+4π + @test rem2pi(T(-13), RoundNearest) ≈ -13+4π + @test rem2pi(T(-13), RoundDown) ≈ -13+6π + @test rem2pi(T(-13), RoundUp) ≈ -13+4π end @testset "PR #36420 $T" for T in (Float16, Float32, Float64) From 8fb16e9f4bc16b21161acf503946c14ece014b72 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Sun, 5 Feb 2023 18:42:12 -0500 Subject: [PATCH 038/775] Missing isequal test for missing --- test/missing.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/missing.jl b/test/missing.jl index 474e10620732f..450b816ea3e57 100644 --- a/test/missing.jl +++ b/test/missing.jl @@ -66,6 +66,7 @@ end @test isequal(missing, missing) @test !isequal(1, missing) @test !isequal(missing, 1) + @test !isequal('c', missing) @test (missing < missing) === missing @test (missing < 1) === missing @test (1 < missing) === missing From e384c85b82da639e7c18ac9532bd7d5acd7356ea Mon Sep 17 00:00:00 2001 From: Ian Date: Sun, 5 Feb 2023 22:29:38 -0500 Subject: [PATCH 039/775] add more tests for julia_cmd() --- base/util.jl | 2 +- test/cmdlineargs.jl | 72 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/base/util.jl b/base/util.jl index 8d1a4a9fa02ef..939ed9ae72bbb 100644 --- a/base/util.jl +++ b/base/util.jl @@ -137,7 +137,7 @@ See also [`print`](@ref), [`println`](@ref), [`show`](@ref). Return a julia command similar to the one of the running process. Propagates any of the `--cpu-target`, `--sysimage`, `--compile`, `--sysimage-native-code`, -`--compiled-modules`, `--inline`, `--check-bounds`, `--optimize`, `-g`, +`--compiled-modules`, `--inline`, `--check-bounds`, `--optimize`, `--min-optlevel`, `-g`, `--code-coverage`, `--track-allocation`, `--color`, `--startup-file`, and `--depwarn` command line arguments that are not at their default values. diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 81478cd63836b..6740b621875c6 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -60,6 +60,78 @@ let @test format_filename("%a%%b") == "a%b" end +@testset "julia_cmd" begin + julia_basic = Base.julia_cmd() + opts = Base.JLOptions() + get_julia_cmd(arg) = strip(read(`$julia_basic $arg -e 'print(repr(Base.julia_cmd()))'`, String), ['`']) + + for (arg, default) in ( + ("-C$(unsafe_string(opts.cpu_target))", false), + + ("-J$(unsafe_string(opts.image_file))", false), + + ("--depwarn=yes", false), + ("--depwarn=error", false), + ("--depwarn=no", true), + + ("--check-bounds=yes", false), + ("--check-bounds=no", false), + ("--check-bounds=auto", true), + + ("--inline=no", false), + ("--inline=yes", true), + + ("-O0", false), + ("-O1", false), + ("-O2", true), + ("-O3", false), + + ("--min-optlevel=0", true), + ("--min-optlevel=1", false), + ("--min-optlevel=2", false), + ("--min-optlevel=3", false), + + ("-g0", false), + ("-g1", false), + ("-g2", false), + + ("--compile=no", false), + ("--compile=all", false), + ("--compile=min", false), + ("--compile=yes", true), + + ("--code-coverage=@", false), + ("--code-coverage=user", false), + ("--code-coverage=all", false), + ("--code-coverage=none", true), + + ("--track-allocation=@", false), + ("--track-allocation=user", false), + ("--track-allocation=all", false), + ("--track-allocation=none", true), + + ("--color=yes", false), + ("--color=no", false), + + ("--startup-file=no", false), + ("--startup-file=yes", true), + + # ("--sysimage-native-code=no", false), # takes a lot longer (30s) + ("--sysimage-native-code=yes", true), + + ("--pkgimages=yes", true), + ("--pkgimages=no", false), + ) + @testset "$arg" begin + if default + @test !occursin(arg, get_julia_cmd(arg)) + else + @test occursin(arg, get_julia_cmd(arg)) + end + end + end +end + let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` # tests for handling of ENV errors let v = writereadpipeline("println(\"REPL: \", @which(less), @isdefined(InteractiveUtils))", From bba0a1e5d25b73a31b87f7bd308a7484e167d4e1 Mon Sep 17 00:00:00 2001 From: AbhaySingh004 Date: Mon, 6 Feb 2023 20:55:08 +0530 Subject: [PATCH 040/775] modified builder-installer.iss correcting the comment with right syntax --- contrib/windows/build-installer.iss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contrib/windows/build-installer.iss b/contrib/windows/build-installer.iss index e99d8c72f65ac..a63cf853d4373 100644 --- a/contrib/windows/build-installer.iss +++ b/contrib/windows/build-installer.iss @@ -145,14 +145,13 @@ begin end; end; - procedure CurPageChanged(CurPageID: Integer); begin case CurPageID of wpWelcome: WizardForm.Color := WizardForm.WelcomePage.Color; wpFinished: WizardForm.Color := WizardForm.FinishedPage.Color; - ;change button text from "next" to "install" when ReadyPage is disabled. + //change button text from "next" to "install" when ReadyPage is disabled. wpSelectTasks: WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall); else WizardForm.Color := WizardForm.InnerPage.Color; From 5721ae700cd10c284ceb32e603792e5c487d6167 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 6 Feb 2023 12:25:44 -0600 Subject: [PATCH 041/775] Protect `cmd_gen` against invalidation (#48557) This gets used by `Base.require`, arguably the most painful of all invalidations. CSV is one package that invalidates it. --- base/cmd.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/cmd.jl b/base/cmd.jl index e6691835e80c9..9e274b61b5e9e 100644 --- a/base/cmd.jl +++ b/base/cmd.jl @@ -462,7 +462,7 @@ function cmd_gen(parsed) (ignorestatus, flags, env, dir) = (cmd.ignorestatus, cmd.flags, cmd.env, cmd.dir) append!(args, cmd.exec) for arg in tail(parsed) - append!(args, arg_gen(arg...)::Vector{String}) + append!(args, Base.invokelatest(arg_gen, arg...)::Vector{String}) end return Cmd(Cmd(args), ignorestatus, flags, env, dir) else From ed1ecea26a9c363818f77e3d90ec230697966882 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Mon, 6 Feb 2023 20:14:37 +0100 Subject: [PATCH 042/775] improve error message when an extension fails to load (#48550) --- base/loading.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index cd3c2c7450747..25c89c31caa30 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1184,8 +1184,7 @@ function run_extension_callbacks(extid::ExtensionId) true catch # Try to continue loading if loading an extension errors - errs = current_exceptions() - @error "Error during loading of extension" exception=errs + @error "Error during loading of extension $(extid.id.name) of $(extid.parentid.name)" false end return succeeded From 3fe69f4f4327dd4f9d39a8d8b5ff2d959113d680 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Mon, 6 Feb 2023 23:43:38 -0300 Subject: [PATCH 043/775] Change uses of int to size_t in MurmurHash (#48562) * Change uses of int to size_t in MurmurHash --- src/support/MurmurHash3.c | 18 +++++++++--------- src/support/MurmurHash3.h | 8 ++++---- test/hashing.jl | 8 ++++++++ 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/support/MurmurHash3.c b/src/support/MurmurHash3.c index a26f58ef40cfa..7eaded17c379f 100644 --- a/src/support/MurmurHash3.c +++ b/src/support/MurmurHash3.c @@ -57,11 +57,11 @@ FORCE_INLINE uint64_t fmix64 ( uint64_t k ) //----------------------------------------------------------------------------- -void MurmurHash3_x86_32 ( const void * key, int len, +void MurmurHash3_x86_32 ( const void * key, size_t len, uint32_t seed, void * out ) { const uint8_t * data = (const uint8_t*)key; - const int nblocks = len / 4; + const size_t nblocks = len / 4; uint32_t h1 = seed; @@ -73,7 +73,7 @@ void MurmurHash3_x86_32 ( const void * key, int len, const uint8_t * tail = data + nblocks*4; - for(int i = -nblocks; i; i++) + for(size_t i = -nblocks; i; i++) { uint32_t k1 = jl_load_unaligned_i32(tail + sizeof(uint32_t)*i); @@ -111,11 +111,11 @@ void MurmurHash3_x86_32 ( const void * key, int len, //----------------------------------------------------------------------------- -void MurmurHash3_x86_128 ( const void * key, const int len, +void MurmurHash3_x86_128 ( const void * key, const size_t len, uint32_t seed, void * out ) { const uint8_t * data = (const uint8_t*)key; - const int nblocks = len / 16; + const size_t nblocks = len / 16; uint32_t h1 = seed; uint32_t h2 = seed; @@ -132,7 +132,7 @@ void MurmurHash3_x86_128 ( const void * key, const int len, const uint8_t *tail = data + nblocks*16; - for(int i = -nblocks; i; i++) + for(size_t i = -nblocks; i; i++) { uint32_t k1 = jl_load_unaligned_i32(tail + sizeof(uint32_t)*(i*4 + 0)); uint32_t k2 = jl_load_unaligned_i32(tail + sizeof(uint32_t)*(i*4 + 1)); @@ -217,11 +217,11 @@ void MurmurHash3_x86_128 ( const void * key, const int len, //----------------------------------------------------------------------------- -void MurmurHash3_x64_128 ( const void * key, const int len, +void MurmurHash3_x64_128 ( const void * key, const size_t len, const uint32_t seed, void * out ) { const uint8_t * data = (const uint8_t*)key; - const int nblocks = len / 16; + const size_t nblocks = len / 16; uint64_t h1 = seed; uint64_t h2 = seed; @@ -232,7 +232,7 @@ void MurmurHash3_x64_128 ( const void * key, const int len, //---------- // body - for(int i = 0; i < nblocks; i++) + for(size_t i = 0; i < nblocks; i++) { uint64_t k1 = jl_load_unaligned_i64(data + sizeof(uint64_t)*(i*2 + 0)); uint64_t k2 = jl_load_unaligned_i64(data + sizeof(uint64_t)*(i*2 + 1)); diff --git a/src/support/MurmurHash3.h b/src/support/MurmurHash3.h index e3e7da9df62fa..6137098d6828c 100644 --- a/src/support/MurmurHash3.h +++ b/src/support/MurmurHash3.h @@ -8,14 +8,14 @@ //----------------------------------------------------------------------------- // Platform-specific functions and macros #include - +#include //----------------------------------------------------------------------------- -void MurmurHash3_x86_32 ( const void * key, int len, uint32_t seed, void * out ); +void MurmurHash3_x86_32 ( const void * key, size_t len, uint32_t seed, void * out ); -void MurmurHash3_x86_128 ( const void * key, int len, uint32_t seed, void * out ); +void MurmurHash3_x86_128 ( const void * key, size_t len, uint32_t seed, void * out ); -void MurmurHash3_x64_128 ( const void * key, int len, uint32_t seed, void * out ); +void MurmurHash3_x64_128 ( const void * key, size_t len, uint32_t seed, void * out ); //----------------------------------------------------------------------------- diff --git a/test/hashing.jl b/test/hashing.jl index 9bd076554962f..9f40e7a4a73ac 100644 --- a/test/hashing.jl +++ b/test/hashing.jl @@ -284,3 +284,11 @@ end end end end + +if Sys.WORD_SIZE >= 64 + @testset "very large string" begin + N = 2^31+1 + s = String('\0'^N); + objectid(s) + end +end From 849203173ffaa2f73a4f052e8a832e2c43dcf3b9 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Mon, 6 Feb 2023 21:56:36 -0500 Subject: [PATCH 044/775] Restrict `path` to be an `AbstractString` in `lbt_forward()` Some users tried to pass a `dlopen()` handle into `lbt_forward()` which sadly works just fine, despite `ccall()` declaring its first argument as being a `Cstring`. I guess it's trivial to convert a `Ptr{Cvoid}` into a `Cstring`, so this just goes through. To protect against this, restrict `path` to be an `AbstractString`. --- stdlib/LinearAlgebra/src/lbt.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/lbt.jl b/stdlib/LinearAlgebra/src/lbt.jl index 3099eb3b765ce..be3f880ae2093 100644 --- a/stdlib/LinearAlgebra/src/lbt.jl +++ b/stdlib/LinearAlgebra/src/lbt.jl @@ -207,9 +207,10 @@ function lbt_set_num_threads(nthreads) return ccall((:lbt_set_num_threads, libblastrampoline), Cvoid, (Int32,), nthreads) end -function lbt_forward(path; clear::Bool = false, verbose::Bool = false, suffix_hint::Union{String,Nothing} = nothing) +function lbt_forward(path::AbstractString; clear::Bool = false, verbose::Bool = false, suffix_hint::Union{String,Nothing} = nothing) _clear_config_with() do - return ccall((:lbt_forward, libblastrampoline), Int32, (Cstring, Int32, Int32, Cstring), path, clear ? 1 : 0, verbose ? 1 : 0, something(suffix_hint, C_NULL)) + return ccall((:lbt_forward, libblastrampoline), Int32, (Cstring, Int32, Int32, Cstring), + path, clear ? 1 : 0, verbose ? 1 : 0, something(suffix_hint, C_NULL)) end end From 0ab6e2f51ce3ddabda44eb17f358d039a3eb1f6b Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Mon, 6 Feb 2023 23:15:09 -0500 Subject: [PATCH 045/775] [compiler] Fix effects of supertype abstract interpretation (#48566) This `abstract_call_known` recursion inserted `EFFECTS_TOTAL` which is incorrect because `<:` and `>:` can throw if the arguments are not types. --- base/compiler/abstractinterpretation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 7761ef1ce6f90..58d4cbf10a82c 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2034,7 +2034,7 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), fargs = nothing end argtypes = Any[typeof(<:), argtypes[3], argtypes[2]] - return CallMeta(abstract_call_known(interp, <:, ArgInfo(fargs, argtypes), si, sv, max_methods).rt, EFFECTS_TOTAL, NoCallInfo()) + return abstract_call_known(interp, <:, ArgInfo(fargs, argtypes), si, sv, max_methods) elseif la == 2 && (a2 = argtypes[2]; isa(a2, Const)) && (svecval = a2.val; isa(svecval, SimpleVector)) && istopfunction(f, :length) From 1a754587be1c71248c3b3180383227f3e0436a8f Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 7 Feb 2023 13:28:35 +0900 Subject: [PATCH 046/775] minor NFC simplification on `_require_search_from_serialized` (#48486) --- base/loading.jl | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 351ce87c1dd68..60a8c05aef636 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1347,7 +1347,6 @@ end @constprop :none function _require_search_from_serialized(pkg::PkgId, sourcepath::String, build_id::UInt128) assert_havelock(require_lock) paths = find_all_in_cache_path(pkg) - ocachefile = nothing for path_to_try in paths::Vector{String} staledeps = stale_cachefile(pkg, build_id, sourcepath, path_to_try) if staledeps === true @@ -1360,26 +1359,18 @@ end dep isa Module && continue modpath, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128} modpaths = find_all_in_cache_path(modkey) - modfound = false - for modpath_to_try in modpaths::Vector{String} + for modpath_to_try in modpaths modstaledeps = stale_cachefile(modkey, modbuild_id, modpath, modpath_to_try) if modstaledeps === true continue end modstaledeps, modocachepath = modstaledeps::Tuple{Vector{Any}, Union{Nothing, String}} staledeps[i] = (modpath, modkey, modpath_to_try, modstaledeps, modocachepath) - modfound = true - break - end - if !modfound - @debug "Rejecting cache file $path_to_try because required dependency $modkey with build ID $(UUID(modbuild_id)) is missing from the cache." - staledeps = true - break + @goto check_next_dep end - end - if staledeps === true - ocachefile = nothing - continue + @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 try touch(path_to_try) # update timestamp of precompilation file @@ -1394,23 +1385,17 @@ end dep = _tryrequire_from_serialized(modkey, modcachepath, modocachepath, modpath, modstaledeps) 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 - staledeps = true - break + @goto check_next_path end - (staledeps::Vector{Any})[i] = dep - end - if staledeps === true - ocachefile = nothing - continue - end - restored = _include_from_serialized(pkg, path_to_try, ocachefile, staledeps::Vector{Any}) - if !isa(restored, Module) - @debug "Deserialization checks failed while attempting to load cache from $path_to_try" exception=restored - else - return restored + staledeps[i] = dep end + restored = _include_from_serialized(pkg, path_to_try, ocachefile, staledeps) + isa(restored, Module) && return restored + @debug "Deserialization checks failed while attempting to load cache from $path_to_try" exception=restored + continue + @label check_next_path end - return + return nothing end # to synchronize multiple tasks trying to import/using something From 628b15cbe7df2fd2e399e20c3b8fe0cf951e1dae Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 7 Feb 2023 08:48:28 +0100 Subject: [PATCH 047/775] only add extensions from one environment (#48558) (cherry picked from commit b2adb8548d7f2a38dc73ea2de1be271e688a545c) --- base/loading.jl | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 25c89c31caa30..2de3c9894a3ce 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1109,12 +1109,13 @@ const EXT_DORMITORY_FAILED = ExtensionId[] function insert_extension_triggers(pkg::PkgId) pkg.uuid === nothing && return + extensions_added = Set{PkgId}() for env in load_path() - insert_extension_triggers(env, pkg) + insert_extension_triggers!(extensions_added, env, pkg) end end -function insert_extension_triggers(env::String, pkg::PkgId)::Union{Nothing,Missing} +function insert_extension_triggers!(extensions_added::Set{PkgId}, env::String, pkg::PkgId)::Union{Nothing,Missing} project_file = env_project_file(env) if project_file isa String manifest_file = project_file_manifest_path(project_file) @@ -1132,7 +1133,7 @@ function insert_extension_triggers(env::String, pkg::PkgId)::Union{Nothing,Missi extensions === nothing && return weakdeps === nothing && return if weakdeps isa Dict{String, Any} - return _insert_extension_triggers(pkg, extensions, weakdeps) + return _insert_extension_triggers!(extensions_added, pkg, extensions, weakdeps) end d_weakdeps = Dict{String, String}() @@ -1147,7 +1148,7 @@ function insert_extension_triggers(env::String, pkg::PkgId)::Union{Nothing,Missi d_weakdeps[dep_name] = uuid end @assert length(d_weakdeps) == length(weakdeps) - return _insert_extension_triggers(pkg, extensions, d_weakdeps) + return _insert_extension_triggers!(extensions_added, pkg, extensions, d_weakdeps) end end end @@ -1155,10 +1156,13 @@ function insert_extension_triggers(env::String, pkg::PkgId)::Union{Nothing,Missi return nothing end -function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, <:Any}, weakdeps::Dict{String, <:Any}) +function _insert_extension_triggers!(extensions_added::Set{PkgId}, parent::PkgId, extensions::Dict{String, <:Any}, weakdeps::Dict{String, <:Any}) for (ext::String, triggers::Union{String, Vector{String}}) in extensions triggers isa String && (triggers = [triggers]) id = PkgId(uuid5(parent.uuid, ext), ext) + # Only add triggers for an extension from one env. + id in extensions_added && continue + push!(extensions_added, id) gid = ExtensionId(id, parent, 1 + length(triggers)) trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, parent) push!(trigger1, gid) From e32e20c7ca3b63dafa85a3a0eca9515d055e9657 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 7 Feb 2023 13:26:57 +0100 Subject: [PATCH 048/775] give a hint towards `Base.retry_load_extensions` when an extension fails to load (#48571) --- base/loading.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/loading.jl b/base/loading.jl index 340e4625a3188..7ad877153e45d 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1188,7 +1188,8 @@ function run_extension_callbacks(extid::ExtensionId) true catch # Try to continue loading if loading an extension errors - @error "Error during loading of extension $(extid.id.name) of $(extid.parentid.name)" + @error "Error during loading of extension $(extid.id.name) of $(extid.parentid.name), \ + use `Base.retry_load_extensions()` to retry." false end return succeeded From 0d1e7db95a5d0ca3902dd6569912c992021c25c1 Mon Sep 17 00:00:00 2001 From: Benjamin Lorenz Date: Tue, 7 Feb 2023 10:11:13 +0100 Subject: [PATCH 049/775] build: add libjulia-internal symlink as dependency for libjulia-codegen this is required to build libjulia-codegen.{so,dylib}.major.minor on UNIX and avoids race conditions during parallel make leading to: ld: cannot find -ljulia-internal: No such file or directory --- src/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Makefile b/src/Makefile index 0baa34fedf877..bb98f6766f470 100644 --- a/src/Makefile +++ b/src/Makefile @@ -382,6 +382,8 @@ $(build_shlibdir)/libjulia-internal.$(JL_MAJOR_SHLIB_EXT) $(build_shlibdir)/libj $(build_shlibdir)/libjulia-internal.$(SHLIB_EXT) $(build_shlibdir)/libjulia-internal-debug.$(SHLIB_EXT): $(build_shlibdir)/libjulia-internal%.$(SHLIB_EXT): \ $(build_shlibdir)/libjulia-internal%.$(JL_MAJOR_MINOR_SHLIB_EXT) @$(call PRINT_LINK, ln -sf $(notdir $<) $@) +$(build_shlibdir)/libjulia-codegen.$(JL_MAJOR_MINOR_SHLIB_EXT): $(build_shlibdir)/libjulia-internal.$(SHLIB_EXT) +$(build_shlibdir)/libjulia-codegen-debug.$(JL_MAJOR_MINOR_SHLIB_EXT): $(build_shlibdir)/libjulia-internal-debug.$(SHLIB_EXT) libjulia-internal-release: $(build_shlibdir)/libjulia-internal.$(JL_MAJOR_SHLIB_EXT) $(build_shlibdir)/libjulia-internal.$(SHLIB_EXT) libjulia-internal-debug: $(build_shlibdir)/libjulia-internal-debug.$(JL_MAJOR_SHLIB_EXT) $(build_shlibdir)/libjulia-internal-debug.$(SHLIB_EXT) endif From d5d490f0e55cbc288586a10793bbd7214f684fab Mon Sep 17 00:00:00 2001 From: Bob Cassels Date: Tue, 7 Feb 2023 14:54:04 -0500 Subject: [PATCH 050/775] Implement tanpi (#48575) See issue: https://github.com/JuliaLang/julia/issues/48226 --- base/exports.jl | 1 + base/math.jl | 2 +- base/mpfr.jl | 4 +-- base/special/trig.jl | 78 +++++++++++++++++++++++++++++++++++++++----- test/math.jl | 39 ++++++++++++++++++---- 5 files changed, 106 insertions(+), 18 deletions(-) diff --git a/base/exports.jl b/base/exports.jl index 600b36b6c37c6..26b24b85651a2 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -352,6 +352,7 @@ export tan, tand, tanh, + tanpi, trailing_ones, trailing_zeros, trunc, diff --git a/base/math.jl b/base/math.jl index f41057c76cfc2..0faa6a00c3f53 100644 --- a/base/math.jl +++ b/base/math.jl @@ -5,7 +5,7 @@ module Math export sin, cos, sincos, tan, sinh, cosh, tanh, asin, acos, atan, asinh, acosh, atanh, sec, csc, cot, asec, acsc, acot, sech, csch, coth, asech, acsch, acoth, - sinpi, cospi, sincospi, sinc, cosc, + sinpi, cospi, sincospi, tanpi, sinc, cosc, cosd, cotd, cscd, secd, sind, tand, sincosd, acosd, acotd, acscd, asecd, asind, atand, rad2deg, deg2rad, diff --git a/base/mpfr.jl b/base/mpfr.jl index d42beb0c59190..4485265b580de 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -17,7 +17,7 @@ import cbrt, typemax, typemin, unsafe_trunc, floatmin, floatmax, rounding, setrounding, maxintfloat, widen, significand, frexp, tryparse, iszero, isone, big, _string_n, decompose, minmax, - sinpi, cospi, sincospi, sind, cosd, tand, asind, acosd, atand + sinpi, cospi, sincospi, tanpi, sind, cosd, tand, asind, acosd, atand import ..Rounding: rounding_raw, setrounding_raw @@ -790,7 +790,7 @@ function sum(arr::AbstractArray{BigFloat}) end # Functions for which NaN results are converted to DomainError, following Base -for f in (:sin, :cos, :tan, :sec, :csc, :acos, :asin, :atan, :acosh, :asinh, :atanh, :sinpi, :cospi) +for f in (:sin, :cos, :tan, :sec, :csc, :acos, :asin, :atan, :acosh, :asinh, :atanh, :sinpi, :cospi, :tanpi) @eval begin function ($f)(x::BigFloat) isnan(x) && return x diff --git a/base/special/trig.jl b/base/special/trig.jl index 929e259913104..ed92f83bb52e2 100644 --- a/base/special/trig.jl +++ b/base/special/trig.jl @@ -725,29 +725,41 @@ end # Uses minimax polynomial of sin(π * x) for π * x in [0, .25] @inline function sinpi_kernel(x::Float64) + sinpi_kernel_wide(x) +end +@inline function sinpi_kernel_wide(x::Float64) x² = x*x x⁴ = x²*x² r = evalpoly(x², (2.5501640398773415, -0.5992645293202981, 0.08214588658006512, - -7.370429884921779e-3, 4.662827319453555e-4, -2.1717412523382308e-5)) + -7.370429884921779e-3, 4.662827319453555e-4, -2.1717412523382308e-5)) return muladd(3.141592653589793, x, x*muladd(-5.16771278004997, x², muladd(x⁴, r, 1.2245907532225998e-16))) end @inline function sinpi_kernel(x::Float32) + Float32(sinpi_kernel_wide(x)) +end +@inline function sinpi_kernel_wide(x::Float32) x = Float64(x) - return Float32(x*evalpoly(x*x, (3.1415926535762266, -5.167712769188119, - 2.5501626483206374, -0.5992021090314925, 0.08100185277841528))) + return x*evalpoly(x*x, (3.1415926535762266, -5.167712769188119, + 2.5501626483206374, -0.5992021090314925, 0.08100185277841528)) end @inline function sinpi_kernel(x::Float16) + Float16(sinpi_kernel_wide(x)) +end +@inline function sinpi_kernel_wide(x::Float16) x = Float32(x) - return Float16(x*evalpoly(x*x, (3.1415927f0, -5.1677127f0, 2.5501626f0, -0.5992021f0, 0.081001855f0))) + return x*evalpoly(x*x, (3.1415927f0, -5.1677127f0, 2.5501626f0, -0.5992021f0, 0.081001855f0)) end # Uses minimax polynomial of cos(π * x) for π * x in [0, .25] @inline function cospi_kernel(x::Float64) + cospi_kernel_wide(x) +end +@inline function cospi_kernel_wide(x::Float64) x² = x*x r = x²*evalpoly(x², (4.058712126416765, -1.3352627688537357, 0.23533063027900392, - -0.025806887811869204, 1.9294917136379183e-3, -1.0368935675474665e-4)) + -0.025806887811869204, 1.9294917136379183e-3, -1.0368935675474665e-4)) a_x² = 4.934802200544679 * x² a_x²lo = muladd(3.109686485461973e-16, x², muladd(4.934802200544679, x², -a_x²)) @@ -755,13 +767,19 @@ end return w + muladd(x², r, ((1.0-w)-a_x²) - a_x²lo) end @inline function cospi_kernel(x::Float32) + Float32(cospi_kernel_wide(x)) +end +@inline function cospi_kernel_wide(x::Float32) x = Float64(x) - return Float32(evalpoly(x*x, (1.0, -4.934802200541122, 4.058712123568637, - -1.3352624040152927, 0.23531426791507182, -0.02550710082498761))) + return evalpoly(x*x, (1.0, -4.934802200541122, 4.058712123568637, + -1.3352624040152927, 0.23531426791507182, -0.02550710082498761)) end @inline function cospi_kernel(x::Float16) + Float16(cospi_kernel_wide(x)) +end +@inline function cospi_kernel_wide(x::Float16) x = Float32(x) - return Float16(evalpoly(x*x, (1.0f0, -4.934802f0, 4.058712f0, -1.3352624f0, 0.23531426f0, -0.0255071f0))) + return evalpoly(x*x, (1.0f0, -4.934802f0, 4.058712f0, -1.3352624f0, 0.23531426f0, -0.0255071f0)) end """ @@ -867,12 +885,56 @@ function sincospi(_x::T) where T<:Union{IEEEFloat, Rational} return si, co end +""" + tanpi(x) + +Compute ``\\tan(\\pi x)`` more accurately than `tan(pi*x)`, especially for large `x`. + +See also [`tand`](@ref), [`sinpi`](@ref), [`cospi`](@ref), [`sincospi`](@ref). +""" + +function tanpi(_x::T) where T<:Union{IEEEFloat, Rational} + # This is modified from sincospi. + # Would it be faster or more accurate to make a tanpi_kernel? + x = abs(_x) + if !isfinite(x) + isnan(x) && return x + throw(DomainError(x, "`x` cannot be infinite.")) + end + # For large x, answers are all zero. + # All integer values for floats larger than maxintfloat are even. + if T <: AbstractFloat + x >= maxintfloat(T) && return copysign(zero(T), _x) + end + + # reduce to interval [0, 0.5] + n = round(2*x) + rx = float(muladd(T(-.5), n, x)) + n = Int64(n) & 3 + si, co = sinpi_kernel_wide(rx), cospi_kernel_wide(rx) + if n==0 + si, co = si, co + elseif n==1 + si, co = co, zero(T)-si + elseif n==2 + si, co = zero(T)-si, zero(T)-co + else + si, co = zero(T)-co, si + end + si = ifelse(signbit(_x), -si, si) + return float(T)(si / co) +end + sinpi(x::Integer) = x >= 0 ? zero(float(x)) : -zero(float(x)) cospi(x::Integer) = isodd(x) ? -one(float(x)) : one(float(x)) +tanpi(x::Integer) = x >= 0 ? (isodd(x) ? -zero(float(x)) : zero(float(x))) : + (isodd(x) ? zero(float(x)) : -zero(float(x))) sincospi(x::Integer) = (sinpi(x), cospi(x)) sinpi(x::Real) = sin(pi*x) cospi(x::Real) = cos(pi*x) sincospi(x::Real) = sincos(pi*x) +tanpi(x::Real) = tan(pi*x) +tanpi(x::Complex) = sinpi(x) / cospi(x) # Is there a better way to do this? function sinpi(z::Complex{T}) where T F = float(T) diff --git a/test/math.jl b/test/math.jl index f9af521de61ca..f08cc6c32ce15 100644 --- a/test/math.jl +++ b/test/math.jl @@ -69,8 +69,9 @@ end @test repr(Any[pi ℯ; ℯ pi]) == "Any[π ℯ; ℯ π]" @test string(pi) == "π" - @test sin(π) === sinpi(1) == tan(π) == sinpi(1 // 1) == 0 - @test cos(π) === cospi(1) == sec(π) == cospi(1 // 1) == -1 + @test sin(π) == sind(180) === sinpi(1) === sinpi(1//1) == tan(π) == 0 + @test tan(π) == tand(180) === tanpi(1) === tanpi(1//1) === -0.0 + @test cos(π) == cosd(180) === cospi(1) === cospi(1//1) == sec(π) == -1 @test csc(π) == 1/0 && cot(π) == -1/0 @test sincos(π) === sincospi(1) == (0, -1) end @@ -181,6 +182,7 @@ end @test cbrt(x) ≈ cbrt(big(x)) @test cos(x) ≈ cos(big(x)) @test cosh(x) ≈ cosh(big(x)) + @test cospi(x) ≈ cospi(big(x)) @test exp(x) ≈ exp(big(x)) @test exp10(x) ≈ exp10(big(x)) @test exp2(x) ≈ exp2(big(x)) @@ -194,9 +196,11 @@ end @test log2(x) ≈ log2(big(x)) @test sin(x) ≈ sin(big(x)) @test sinh(x) ≈ sinh(big(x)) + @test sinpi(x) ≈ sinpi(big(x)) @test sqrt(x) ≈ sqrt(big(x)) @test tan(x) ≈ tan(big(x)) @test tanh(x) ≈ tanh(big(x)) + @test tanpi(x) ≈ tanpi(big(x)) @test sec(x) ≈ sec(big(x)) @test csc(x) ≈ csc(big(x)) @test secd(x) ≈ secd(big(x)) @@ -499,6 +503,22 @@ end @test cospi(convert(T,-1.5))::fT ⩲ zero(fT) @test_throws DomainError cospi(convert(T,Inf)) end + @testset "trig pi functions accuracy" for numerator in -20:1:20 + for func in (sinpi, cospi, tanpi, + x -> sincospi(x)[1], + x -> sincospi(x)[2]) + x = numerator // 20 + # Check that rational function works + @test func(x) ≈ func(BigFloat(x)) + # Use short value so that wider values will be exactly equal + shortx = Float16(x) + # Compare to BigFloat value + bigvalue = func(BigFloat(shortx)) + for T in (Float16,Float32,Float64) + @test func(T(shortx)) ≈ T(bigvalue) + end + end + end @testset begin # If the machine supports fma (fused multiply add), we require exact equality. # Otherwise, we only require approximate equality. @@ -529,14 +549,18 @@ end @test ismissing(scdm[2]) end -@testset "Integer and Inf args for sinpi/cospi/sinc/cosc" begin +@testset "Integer and Inf args for sinpi/cospi/tanpi/sinc/cosc" begin for (sinpi, cospi) in ((sinpi, cospi), (x->sincospi(x)[1], x->sincospi(x)[2])) - @test sinpi(1) == 0 - @test sinpi(-1) == -0 + @test sinpi(1) === 0.0 + @test sinpi(-1) === -0.0 @test cospi(1) == -1 @test cospi(2) == 1 end + @test tanpi(1) === -0.0 + @test tanpi(-1) === 0.0 + @test tanpi(2) === 0.0 + @test tanpi(-2) === -0.0 @test sinc(1) == 0 @test sinc(complex(1,0)) == 0 @test sinc(0) == 1 @@ -589,7 +613,7 @@ end end end -@testset "Irrational args to sinpi/cospi/sinc/cosc" begin +@testset "Irrational args to sinpi/cospi/tanpi/sinc/cosc" begin for x in (pi, ℯ, Base.MathConstants.golden) for (sinpi, cospi) in ((sinpi, cospi), (x->sincospi(x)[1], x->sincospi(x)[2])) @test sinpi(x) ≈ Float64(sinpi(big(x))) @@ -597,6 +621,7 @@ end @test sinpi(complex(x, x)) ≈ ComplexF64(sinpi(complex(big(x), big(x)))) @test cospi(complex(x, x)) ≈ ComplexF64(cospi(complex(big(x), big(x)))) end + @test tanpi(x) ≈ Float64(tanpi(big(x))) @test sinc(x) ≈ Float64(sinc(big(x))) @test cosc(x) ≈ Float64(cosc(big(x))) @test sinc(complex(x, x)) ≈ ComplexF64(sinc(complex(big(x), big(x)))) @@ -626,7 +651,7 @@ end end @testset "trig function type stability" begin - @testset "$T $f" for T = (Float32,Float64,BigFloat,Rational{Int16},Complex{Int32},ComplexF16), f = (sind,cosd,sinpi,cospi) + @testset "$T $f" for T = (Float32,Float64,BigFloat,Rational{Int16},Complex{Int32},ComplexF16), f = (sind,cosd,sinpi,cospi,tanpi) @test Base.return_types(f,Tuple{T}) == [float(T)] end @testset "$T sincospi" for T = (Float32,Float64,BigFloat,Rational{Int16},Complex{Int32},ComplexF16) From 43493a046c9cdc5e259778c26f9fbc5500837e5b Mon Sep 17 00:00:00 2001 From: Jacob Quinn Date: Tue, 7 Feb 2023 14:43:44 -0700 Subject: [PATCH 051/775] Define iterate for RemoteChannel (#48515) --- stdlib/Distributed/src/Distributed.jl | 2 +- stdlib/Distributed/src/remotecall.jl | 20 ++++++++++++++++ stdlib/Distributed/test/distributed_exec.jl | 26 +++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/stdlib/Distributed/src/Distributed.jl b/stdlib/Distributed/src/Distributed.jl index 3bcbc7b67f60d..65bb6224a7bcd 100644 --- a/stdlib/Distributed/src/Distributed.jl +++ b/stdlib/Distributed/src/Distributed.jl @@ -7,7 +7,7 @@ module Distributed # imports for extension import Base: getindex, wait, put!, take!, fetch, isready, push!, length, - hash, ==, kill, close, isopen, showerror + hash, ==, kill, close, isopen, showerror, iterate, IteratorSize # imports for use using Base: Process, Semaphore, JLOptions, buffer_writes, @async_unwrap, diff --git a/stdlib/Distributed/src/remotecall.jl b/stdlib/Distributed/src/remotecall.jl index 0554f47670eb3..4c94a18cc08cd 100644 --- a/stdlib/Distributed/src/remotecall.jl +++ b/stdlib/Distributed/src/remotecall.jl @@ -778,3 +778,23 @@ function getindex(r::RemoteChannel, args...) end return remotecall_fetch(getindex, r.where, r, args...) end + +function iterate(c::RemoteChannel, state=nothing) + if isopen(c) || isready(c) + try + return (take!(c), nothing) + catch e + if isa(e, InvalidStateException) || + (isa(e, RemoteException) && + isa(e.captured.ex, InvalidStateException) && + e.captured.ex.state === :closed) + return nothing + end + rethrow() + end + else + return nothing + end +end + +IteratorSize(::Type{<:RemoteChannel}) = SizeUnknown() diff --git a/stdlib/Distributed/test/distributed_exec.jl b/stdlib/Distributed/test/distributed_exec.jl index 8471acade993b..b911f2778e535 100644 --- a/stdlib/Distributed/test/distributed_exec.jl +++ b/stdlib/Distributed/test/distributed_exec.jl @@ -456,6 +456,32 @@ function test_iteration(in_c, out_c) end test_iteration(Channel(10), Channel(10)) +test_iteration(RemoteChannel(() -> Channel(10)), RemoteChannel(() -> Channel(10))) + +@everywhere function test_iteration_take(ch) + count = 0 + for x in ch + count += 1 + end + return count +end + +@everywhere function test_iteration_put(ch, total) + for i in 1:total + put!(ch, i) + end + close(ch) +end + +let ch = RemoteChannel(() -> Channel(1)) + @async test_iteration_put(ch, 10) + @test 10 == @fetchfrom id_other test_iteration_take(ch) + # now reverse + ch = RemoteChannel(() -> Channel(1)) + @spawnat id_other test_iteration_put(ch, 10) + @test 10 == test_iteration_take(ch) +end + # make sure exceptions propagate when waiting on Tasks @test_throws CompositeException (@sync (@async error("oops"))) try From 8a46d850ee031d8f754a492e326e8d1fc2d49da3 Mon Sep 17 00:00:00 2001 From: Jacob Quinn Date: Tue, 7 Feb 2023 14:44:08 -0700 Subject: [PATCH 052/775] Allow keyword arg to control Test printing, defaults to global (#48512) --- stdlib/Test/src/Test.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index f1216371d0b27..c7ed96f4b5129 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -1035,8 +1035,8 @@ record(ts::DefaultTestSet, t::Pass) = (ts.n_passed += 1; t) # For the other result types, immediately print the error message # but do not terminate. Print a backtrace. -function record(ts::DefaultTestSet, t::Union{Fail, Error}) - if TESTSET_PRINT_ENABLE[] +function record(ts::DefaultTestSet, t::Union{Fail, Error}; print_result::Bool=TESTSET_PRINT_ENABLE[]) + if print_result print(ts.description, ": ") # don't print for interrupted tests if !(t isa Error) || t.test_type !== :test_interrupted @@ -1127,7 +1127,7 @@ const TESTSET_PRINT_ENABLE = Ref(true) # Called at the end of a @testset, behaviour depends on whether # this is a child of another testset, or the "root" testset -function finish(ts::DefaultTestSet) +function finish(ts::DefaultTestSet; print_results::Bool=TESTSET_PRINT_ENABLE[]) ts.time_end = time() # If we are a nested test set, do not print a full summary # now - let the parent test set do the printing @@ -1144,7 +1144,7 @@ function finish(ts::DefaultTestSet) total_broken = broken + c_broken total = total_pass + total_fail + total_error + total_broken - if TESTSET_PRINT_ENABLE[] + if print_results print_test_results(ts) end From 68ada71e3d26398100994a5e7c9b77400e391570 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Tue, 7 Feb 2023 18:34:49 -0500 Subject: [PATCH 053/775] [compiler] Teach type inference that `GotoIfNot` can throw (#48583) Previously, the effects system would ignore certain cases where `GotoIfNot` nodes would be capable of throwing; this resulted in simple examples such as the following being marked as `nothrow`: ``` julia> foo(x) = x > 0 ? x : 0 Base.infer_effects(foo, (Missing,)) (+c,+e,+n,+t,+s,+m,+i) ``` With this change, we correctly notice when a `GotoIfNot` node is given a non-`Bool` condition, annotate the basic block as possibly throwing, and further end type inference if the condition is provably non-`Bool`. --- base/compiler/abstractinterpretation.jl | 9 +++++++++ test/compiler/effects.jl | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 58d4cbf10a82c..ef49d56895890 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2893,6 +2893,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) empty!(frame.pclimitations) @goto find_next_bb end + orig_condt = condt if !(isa(condt, Const) || isa(condt, Conditional)) && isa(condx, SlotNumber) # if this non-`Conditional` object is a slot, we form and propagate # the conditional constraint on it @@ -2924,6 +2925,14 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) handle_control_backedge!(interp, frame, currpc, stmt.dest) @goto branch else + if !⊑(𝕃ᵢ, orig_condt, Bool) + merge_effects!(interp, frame, EFFECTS_THROWS) + if !hasintersect(widenconst(orig_condt), Bool) + ssavaluetypes[currpc] = Bottom + @goto find_next_bb + end + end + # We continue with the true branch, but process the false # branch here. if isa(condt, Conditional) diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index 656ac9268dcb4..048e5146e2dcb 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -743,3 +743,11 @@ end @test Base.ismutationfree(Type{Union{}}) @test Core.Compiler.is_total(Base.infer_effects(typejoin, ())) + + +# GotoIfNot should properly mark itself as throwing when given a non-Bool +# https://github.com/JuliaLang/julia/pull/48583 +gotoifnot_throw_check_48583(x) = x ? x : 0 +@test !Core.Compiler.is_nothrow(Base.infer_effects(gotoifnot_throw_check_48583, (Missing,))) +@test !Core.Compiler.is_nothrow(Base.infer_effects(gotoifnot_throw_check_48583, (Any,))) +@test Core.Compiler.is_nothrow(Base.infer_effects(gotoifnot_throw_check_48583, (Bool,))) From b8802d0eb97bcf7bef964a65b37c409e41436bae Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Wed, 8 Feb 2023 08:57:11 +0800 Subject: [PATCH 054/775] also drop JL_ROOTED_VALUE_COLLECTION --- src/clangsa/GCChecker.cpp | 12 +----------- src/support/analyzer_annotations.h | 2 -- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/clangsa/GCChecker.cpp b/src/clangsa/GCChecker.cpp index 9f7a5e22d22b4..086d925802f63 100644 --- a/src/clangsa/GCChecker.cpp +++ b/src/clangsa/GCChecker.cpp @@ -191,15 +191,6 @@ class GCChecker } return f(TD->getName()); } - static bool isValueCollection(QualType QT) { - if (QT->isPointerType() || QT->isArrayType()) - return isValueCollection( - clang::QualType(QT->getPointeeOrArrayElementType(), 0)); - const TagDecl *TD = QT->getUnqualifiedDesugaredType()->getAsTagDecl(); - if (!TD) - return false; - return declHasAnnotation(TD, "julia_rooted_value_collection"); - } template static SymbolRef walkToRoot(callback f, const ProgramStateRef &State, const MemRegion *Region); @@ -768,8 +759,7 @@ static bool isMutexUnlock(StringRef name) { #endif bool GCChecker::isGCTrackedType(QualType QT) { - return isValueCollection(QT) || - isJuliaType( + return isJuliaType( [](StringRef Name) { if (Name.endswith_lower("jl_value_t") || Name.endswith_lower("jl_svec_t") || diff --git a/src/support/analyzer_annotations.h b/src/support/analyzer_annotations.h index 3e577e6b45483..69827e4d77f37 100644 --- a/src/support/analyzer_annotations.h +++ b/src/support/analyzer_annotations.h @@ -22,7 +22,6 @@ #define JL_ALWAYS_LEAFTYPE JL_GLOBALLY_ROOTED #define JL_ROOTS_TEMPORARILY __attribute__((annotate("julia_temporarily_roots"))) #define JL_REQUIRE_ROOTED_SLOT __attribute__((annotate("julia_require_rooted_slot"))) -#define JL_ROOTED_VALUE_COLLECTION __attribute__((annotate("julia_rooted_value_collection"))) #ifdef __cplusplus extern "C" { #endif @@ -46,7 +45,6 @@ extern "C" { #define JL_ALWAYS_LEAFTYPE #define JL_ROOTS_TEMPORARILY #define JL_REQUIRE_ROOTED_SLOT -#define JL_ROOTED_VALUE_COLLECTION #define JL_GC_PROMISE_ROOTED(x) (void)(x) #define jl_may_leak(x) (void)(x) From 1b3b63089c4113766b2e0ab856ef21ece9b16606 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 8 Feb 2023 11:04:51 +0900 Subject: [PATCH 055/775] add anonymous function supports for `@constprop` and `@assume_effects` (#48569) --- base/expr.jl | 110 ++++++++++++++++++++++++++++++------- test/compiler/effects.jl | 15 +++++ test/compiler/inference.jl | 60 ++++++++++++++------ 3 files changed, 148 insertions(+), 37 deletions(-) diff --git a/base/expr.jl b/base/expr.jl index a0a9a5676c760..0e6d73c9722d1 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -193,7 +193,7 @@ Small functions typically do not need the `@inline` annotation, as the compiler does it automatically. By using `@inline` on bigger functions, an extra nudge can be given to the compiler to inline it. -`@inline` can be applied immediately before the definition or in its function body. +`@inline` can be applied immediately before a function definition or within a function body. ```julia # annotate long-form definition @@ -271,7 +271,7 @@ Small functions are typically inlined automatically. By using `@noinline` on small functions, auto-inlining can be prevented. -`@noinline` can be applied immediately before the definition or in its function body. +`@noinline` can be applied immediately before a function definition or within a function body. ```julia # annotate long-form definition @@ -358,32 +358,66 @@ macro pure(ex) end """ - @constprop setting ex + @constprop setting [ex] -`@constprop` controls the mode of interprocedural constant propagation for the -annotated function. Two `setting`s are supported: +Control the mode of interprocedural constant propagation for the annotated function. -- `@constprop :aggressive ex`: apply constant propagation aggressively. +Two `setting`s are supported: + +- `@constprop :aggressive [ex]`: apply constant propagation aggressively. For a method where the return type depends on the value of the arguments, this can yield improved inference results at the cost of additional compile time. -- `@constprop :none ex`: disable constant propagation. This can reduce compile +- `@constprop :none [ex]`: disable constant propagation. This can reduce compile times for functions that Julia might otherwise deem worthy of constant-propagation. Common cases are for functions with `Bool`- or `Symbol`-valued arguments or keyword arguments. + +`@constprop` can be applied immediately before a function definition or within a function body. + +```julia +# annotate long-form definition +@constprop :aggressive function longdef(x) + ... +end + +# annotate short-form definition +@constprop :aggressive shortdef(x) = ... + +# annotate anonymous function that a `do` block creates +f() do + @constprop :aggressive + ... +end +``` + +!!! compat "Julia 1.10" + The usage within a function body requires at least Julia 1.10. """ macro constprop(setting, ex) - if isa(setting, QuoteNode) - setting = setting.value + sym = constprop_setting(setting) + isa(ex, Expr) && return esc(pushmeta!(ex, sym)) + throw(ArgumentError(LazyString("Bad expression `", ex, "` in `@constprop settings ex`"))) +end +macro constprop(setting) + sym = constprop_setting(setting) + return Expr(:meta, sym) +end + +function constprop_setting(@nospecialize setting) + isa(setting, QuoteNode) && (setting = setting.value) + if setting === :aggressive + return :aggressive_constprop + elseif setting === :none + return :no_constprop end - setting === :aggressive && return esc(isa(ex, Expr) ? pushmeta!(ex, :aggressive_constprop) : ex) - setting === :none && return esc(isa(ex, Expr) ? pushmeta!(ex, :no_constprop) : ex) - throw(ArgumentError("@constprop $setting not supported")) + throw(ArgumentError(LazyString("@constprop "), setting, "not supported")) end """ - @assume_effects setting... ex + @assume_effects setting... [ex] -`@assume_effects` overrides the compiler's effect modeling for the given method. -`ex` must be a method definition or `@ccall` expression. +Override the compiler's effect modeling for the given method or foreign call. +`@assume_effects` can be applied immediately before a function definition or within a function body. +It can also be applied immediately before a `@ccall` expression. !!! compat "Julia 1.8" Using `Base.@assume_effects` requires Julia version 1.8. @@ -410,10 +444,31 @@ julia> code_typed() do 1 ─ return 479001600 ) => Int64 +julia> code_typed() do + map((2,3,4)) do x + # this :terminates_locally allows this anonymous function to be constant-folded + Base.@assume_effects :terminates_locally + res = 1 + 1 < x < 20 || error("bad pow") + while x > 1 + res *= x + x -= 1 + end + return res + end + end +1-element Vector{Any}: + CodeInfo( +1 ─ return (2, 6, 24) +) => Tuple{Int64, Int64, Int64} + julia> Base.@assume_effects :total !:nothrow @ccall jl_type_intersection(Vector{Int}::Any, Vector{<:Integer}::Any)::Any Vector{Int64} (alias for Array{Int64, 1}) ``` +!!! compat "Julia 1.10" + The usage within a function body requires at least Julia 1.10. + !!! warning Improper use of this macro causes undefined behavior (including crashes, incorrect answers, or other hard to track bugs). Use with care and only as a @@ -660,9 +715,21 @@ Another advantage is that effects introduced by `@assume_effects` are propagated callers interprocedurally while a purity defined by `@pure` is not. """ macro assume_effects(args...) + lastex = args[end] + inner = unwrap_macrocalls(lastex) + if is_function_def(inner) + ex = lastex + idx = length(args)-1 + elseif isexpr(lastex, :macrocall) && lastex.args[1] === Symbol("@ccall") + ex = lastex + idx = length(args)-1 + else # anonymous function case + ex = nothing + idx = length(args) + end (consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly) = (false, false, false, false, false, false, false, false) - for org_setting in args[1:end-1] + for org_setting in args[1:idx] (setting, val) = compute_assumed_setting(org_setting) if setting === :consistent consistent = val @@ -688,16 +755,19 @@ macro assume_effects(args...) throw(ArgumentError("@assume_effects $org_setting not supported")) end end - ex = args[end] - isa(ex, Expr) || throw(ArgumentError("Bad expression `$ex` in `@assume_effects [settings] ex`")) - if ex.head === :macrocall && ex.args[1] === Symbol("@ccall") + if is_function_def(inner) + return esc(pushmeta!(ex, :purity, + consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly)) + elseif isexpr(ex, :macrocall) && ex.args[1] === Symbol("@ccall") ex.args[1] = GlobalRef(Base, Symbol("@ccall_effects")) insert!(ex.args, 3, Core.Compiler.encode_effects_override(Core.Compiler.EffectsOverride( consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly, ))) return esc(ex) + else # anonymous function case + return Expr(:meta, Expr(:purity, + consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly)) end - return esc(pushmeta!(ex, :purity, consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly)) end function compute_assumed_setting(@nospecialize(setting), val::Bool=true) diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index 048e5146e2dcb..c7f75fbda9a78 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -109,6 +109,21 @@ recur_termination22(x) = x * recur_termination21(x-1) recur_termination21(12) + recur_termination22(12) end +# anonymous function support for `@assume_effects` +@test fully_eliminated() do + map((2,3,4)) do x + # this :terminates_locally allows this anonymous function to be constant-folded + Base.@assume_effects :terminates_locally + res = 1 + 1 < x < 20 || error("bad pow") + while x > 1 + res *= x + x -= 1 + end + return res + end +end + # control flow backedge should taint `terminates` @test Base.infer_effects((Int,)) do n for i = 1:n; end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index d35d0e4a68117..335899bdec54d 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -3957,23 +3957,49 @@ g38888() = S38888(Base.inferencebarrier(3), nothing) f_inf_error_bottom(x::Vector) = isempty(x) ? error(x[1]) : x @test only(Base.return_types(f_inf_error_bottom, Tuple{Vector{Any}})) == Vector{Any} -# @constprop :aggressive -@noinline g_nonaggressive(y, x) = Val{x}() -@noinline Base.@constprop :aggressive g_aggressive(y, x) = Val{x}() - -f_nonaggressive(x) = g_nonaggressive(x, 1) -f_aggressive(x) = g_aggressive(x, 1) - -# The first test just makes sure that improvements to the compiler don't -# render the annotation effectless. -@test Base.return_types(f_nonaggressive, Tuple{Int})[1] == Val -@test Base.return_types(f_aggressive, Tuple{Int})[1] == Val{1} - -# @constprop :none -@noinline Base.@constprop :none g_noaggressive(flag::Bool) = flag ? 1 : 1.0 -ftrue_noaggressive() = g_noaggressive(true) -@test only(Base.return_types(ftrue_noaggressive, Tuple{})) == Union{Int,Float64} - +# @constprop annotation +@noinline f_constprop_simple(f, x) = (f(x); Val{x}()) +Base.@constprop :aggressive f_constprop_aggressive(f, x) = (f(x); Val{x}()) +Base.@constprop :aggressive @noinline f_constprop_aggressive_noinline(f, x) = (f(x); Val{x}()) +Base.@constprop :none f_constprop_none(f, x) = (f(x); Val{x}()) +Base.@constprop :none @inline f_constprop_none_inline(f, x) = (f(x); Val{x}()) + +@test !Core.Compiler.is_aggressive_constprop(only(methods(f_constprop_simple))) +@test !Core.Compiler.is_no_constprop(only(methods(f_constprop_simple))) +@test Core.Compiler.is_aggressive_constprop(only(methods(f_constprop_aggressive))) +@test !Core.Compiler.is_no_constprop(only(methods(f_constprop_aggressive))) +@test Core.Compiler.is_aggressive_constprop(only(methods(f_constprop_aggressive_noinline))) +@test !Core.Compiler.is_no_constprop(only(methods(f_constprop_aggressive_noinline))) +@test !Core.Compiler.is_aggressive_constprop(only(methods(f_constprop_none))) +@test Core.Compiler.is_no_constprop(only(methods(f_constprop_none))) +@test !Core.Compiler.is_aggressive_constprop(only(methods(f_constprop_none_inline))) +@test Core.Compiler.is_no_constprop(only(methods(f_constprop_none_inline))) + +# make sure that improvements to the compiler don't render the annotation effectless. +@test Base.return_types((Function,)) do f + f_constprop_simple(f, 1) +end |> only == Val +@test Base.return_types((Function,)) do f + f_constprop_aggressive(f, 1) +end |> only == Val{1} +@test Base.return_types((Function,)) do f + f_constprop_aggressive_noinline(f, 1) +end |> only == Val{1} +@test Base.return_types((Function,)) do f + f_constprop_none(f, 1) +end |> only == Val +@test Base.return_types((Function,)) do f + f_constprop_none_inline(f, 1) +end |> only == Val + +# anonymous function support for `@constprop` +@test Base.return_types((Function,)) do f + map((1,2,3)) do x + Base.@constprop :aggressive + f(x) + return Val{x}() + end +end |> only == Tuple{Val{1},Val{2},Val{3}} function splat_lotta_unions() a = Union{Tuple{Int},Tuple{String,Vararg{Int}},Tuple{Int,Vararg{Int}}}[(2,)][1] From 3a92d385c46164618c23def8203d372c0ad8adf2 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 8 Feb 2023 14:53:15 +0900 Subject: [PATCH 056/775] add test cases for #48566 (#48584) --- test/compiler/effects.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index c7f75fbda9a78..daa476a627a2a 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -759,6 +759,10 @@ end @test Base.ismutationfree(Type{Union{}}) @test Core.Compiler.is_total(Base.infer_effects(typejoin, ())) +# nothrow-ness of subtyping operations +# https://github.com/JuliaLang/julia/pull/48566 +@test !Core.Compiler.is_nothrow(Base.infer_effects((A,B)->A<:B, (Any,Any))) +@test !Core.Compiler.is_nothrow(Base.infer_effects((A,B)->A>:B, (Any,Any))) # GotoIfNot should properly mark itself as throwing when given a non-Bool # https://github.com/JuliaLang/julia/pull/48583 From b5d17ea2b3d68f41ffca57a87527a337abc8b74d Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 8 Feb 2023 18:40:14 +0900 Subject: [PATCH 057/775] effects: taint `:nothrow` effect on unknown `:static_parameter` (#46791) * effects: taint `:nothrow` effect on unknown `:static_parameter` (conservatively) With this commit, we taint `:nothrow` effect property correctly on access to unknown `:static_parameter`, e.g.: ```julia unknown_sparam_throw(::Union{Nothing, Type{T}}) where T = (T; nothing) @test Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_throw, ((Type{Int},)))) @test !Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_throw, ((Nothing,)))) ``` This commit implements a very conservative analysis, and thus there is a room for improvement still, e.g.: ```julia unknown_sparam_nothrow(x::Ref{T}) where {T} = (T; nothing) @test_broken Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_nothrow, (Ref,))) ``` * inference: improve `:nothrow` modeling for `:static_parameter` (#46820) * Fix test with free type params * Test: Ignore ::Type{T} in detect_unbounded These are only technically unbounded because of the existence of ill-formed types. However, this function is supposed to be an API sanity check and ordinary users should never have ill-formed types, so for the purpose we want here, allow unboundedness in Type{T}. --------- Co-authored-by: Keno Fischer Co-authored-by: Elliot Saba --- base/compiler/abstractinterpretation.jl | 11 ++- base/compiler/inferencestate.jl | 103 ++++++++++++++++++++++-- base/compiler/optimize.jl | 6 +- base/compiler/ssair/slot2ssa.jl | 2 +- base/compiler/ssair/verify.jl | 2 +- stdlib/Test/src/Test.jl | 45 +---------- test/compiler/effects.jl | 15 ++++ test/compiler/inference.jl | 13 +++ 8 files changed, 138 insertions(+), 59 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index ef49d56895890..1bc2af92699be 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2189,9 +2189,17 @@ function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, vtypes:: head = e.head if head === :static_parameter n = e.args[1]::Int + nothrow = false if 1 <= n <= length(sv.sptypes) rt = sv.sptypes[n] + if is_maybeundefsp(rt) + rt = unwrap_maybeundefsp(rt) + else + nothrow = true + end end + merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; nothrow)) + return rt elseif head === :boundscheck if isa(sv, InferenceState) # If there is no particular `@inbounds` for this function, then we only taint `:noinbounds`, @@ -2452,8 +2460,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp elseif isexpr(sym, :static_parameter) n = sym.args[1]::Int if 1 <= n <= length(sv.sptypes) - spty = sv.sptypes[n] - if isa(spty, Const) + if !is_maybeundefsp(sv.sptypes, n) t = Const(true) end end diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 0ae7989c82c76..33363a903f0f8 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -348,15 +348,100 @@ function InferenceState(result::InferenceResult, cache::Symbol, interp::Abstract return InferenceState(result, src, cache, interp) end +""" + constrains_param(var::TypeVar, sig, covariant::Bool, type_constrains::Bool) + +Check if `var` will be constrained to have a definite value +in any concrete leaftype subtype of `sig`. + +It is used as a helper to determine whether type intersection is guaranteed to be able to +find a value for a particular type parameter. +A necessary condition for type intersection to not assign a parameter is that it only +appears in a `Union[All]` and during subtyping some other union component (that does not +constrain the type parameter) is selected. + +The `type_constrains` flag determines whether Type{T} is considered to be constraining +`T`. This is not true in general, because of the existence of types with free type +parameters, however, some callers would like to ignore this corner case. +""" +function constrains_param(var::TypeVar, @nospecialize(typ), covariant::Bool, type_constrains::Bool=false) + typ === var && return true + while typ isa UnionAll + covariant && constrains_param(var, typ.var.ub, covariant, type_constrains) && return true + # typ.var.lb doesn't constrain var + typ = typ.body + end + if typ isa Union + # for unions, verify that both options would constrain var + ba = constrains_param(var, typ.a, covariant, type_constrains) + bb = constrains_param(var, typ.b, covariant, type_constrains) + (ba && bb) && return true + elseif typ isa DataType + # return true if any param constrains var + fc = length(typ.parameters) + if fc > 0 + if typ.name === Tuple.name + # vararg tuple needs special handling + for i in 1:(fc - 1) + p = typ.parameters[i] + constrains_param(var, p, covariant, type_constrains) && return true + end + lastp = typ.parameters[fc] + vararg = unwrap_unionall(lastp) + if vararg isa Core.TypeofVararg && isdefined(vararg, :N) + constrains_param(var, vararg.N, covariant, type_constrains) && return true + # T = vararg.parameters[1] doesn't constrain var + else + constrains_param(var, lastp, covariant, type_constrains) && return true + end + else + if typ.name === typename(Type) && typ.parameters[1] === var && var.ub === Any + # Types with free type parameters are <: Type cause the typevar + # to be unconstrained because Type{T} with free typevars is illegal + return type_constrains + end + for i in 1:fc + p = typ.parameters[i] + constrains_param(var, p, false, type_constrains) && return true + end + end + end + end + return false +end + +""" + MaybeUndefSP(typ) + is_maybeundefsp(typ) -> Bool + unwrap_maybeundefsp(typ) -> Any + +A special wrapper that represents a static parameter that could be undefined at runtime. +This does not participate in the native type system nor the inference lattice, +and it thus should be always unwrapped when performing any type or lattice operations on it. +""" +struct MaybeUndefSP + typ + MaybeUndefSP(@nospecialize typ) = new(typ) +end +is_maybeundefsp(@nospecialize typ) = isa(typ, MaybeUndefSP) +unwrap_maybeundefsp(@nospecialize typ) = isa(typ, MaybeUndefSP) ? typ.typ : typ +is_maybeundefsp(sptypes::Vector{Any}, idx::Int) = is_maybeundefsp(sptypes[idx]) +unwrap_maybeundefsp(sptypes::Vector{Any}, idx::Int) = unwrap_maybeundefsp(sptypes[idx]) + +const EMPTY_SPTYPES = Any[] + function sptypes_from_meth_instance(linfo::MethodInstance) - toplevel = !isa(linfo.def, Method) - if !toplevel && isempty(linfo.sparam_vals) && isa(linfo.def.sig, UnionAll) + def = linfo.def + isa(def, Method) || return EMPTY_SPTYPES # toplevel + sig = def.sig + if isempty(linfo.sparam_vals) + isa(sig, UnionAll) || return EMPTY_SPTYPES # linfo is unspecialized sp = Any[] - sig = linfo.def.sig - while isa(sig, UnionAll) - push!(sp, sig.var) - sig = sig.body + sig′ = sig + while isa(sig′, UnionAll) + push!(sp, sig′.var) + sig′ = sig′.body end else sp = collect(Any, linfo.sparam_vals) @@ -364,7 +449,8 @@ function sptypes_from_meth_instance(linfo::MethodInstance) for i = 1:length(sp) v = sp[i] if v isa TypeVar - temp = linfo.def.sig + maybe_undef = !constrains_param(v, linfo.specTypes, #=covariant=#true) + temp = sig for j = 1:i-1 temp = temp.body end @@ -402,12 +488,13 @@ function sptypes_from_meth_instance(linfo::MethodInstance) tv = TypeVar(v.name, lb, ub) ty = UnionAll(tv, Type{tv}) end + @label ty_computed + maybe_undef && (ty = MaybeUndefSP(ty)) elseif isvarargtype(v) ty = Int else ty = Const(v) end - @label ty_computed sp[i] = ty end return sp diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 2c26848ac1ca1..473ee3899b9da 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -267,9 +267,9 @@ function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospe if isa(stmt, Expr) (; head, args) = stmt if head === :static_parameter - etyp = (isa(src, IRCode) ? src.sptypes : src.ir.sptypes)[args[1]::Int] # if we aren't certain enough about the type, it might be an UndefVarError at runtime - nothrow = isa(etyp, Const) + sptypes = isa(src, IRCode) ? src.sptypes : src.ir.sptypes + nothrow = !is_maybeundefsp(sptypes, args[1]::Int) return (true, nothrow, nothrow) end if head === :call @@ -377,7 +377,7 @@ function argextype( sptypes::Vector{Any}, slottypes::Vector{Any}) if isa(x, Expr) if x.head === :static_parameter - return sptypes[x.args[1]::Int] + return unwrap_maybeundefsp(sptypes, x.args[1]::Int) elseif x.head === :boundscheck return Bool elseif x.head === :copyast diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index 289cea14dc01d..60d04ff1bf601 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -216,7 +216,7 @@ end function typ_for_val(@nospecialize(x), ci::CodeInfo, sptypes::Vector{Any}, idx::Int, slottypes::Vector{Any}) if isa(x, Expr) if x.head === :static_parameter - return sptypes[x.args[1]::Int] + return unwrap_maybeundefsp(sptypes, x.args[1]::Int) elseif x.head === :boundscheck return Bool elseif x.head === :copyast diff --git a/base/compiler/ssair/verify.jl b/base/compiler/ssair/verify.jl index 7dc268f648bcc..b6c90c4528f23 100644 --- a/base/compiler/ssair/verify.jl +++ b/base/compiler/ssair/verify.jl @@ -268,7 +268,7 @@ function verify_ir(ir::IRCode, print::Bool=true, elseif stmt.head === :foreigncall isforeigncall = true elseif stmt.head === :isdefined && length(stmt.args) == 1 && - (stmt.args[1] isa GlobalRef || (stmt.args[1] isa Expr && stmt.args[1].head === :static_parameter)) + (stmt.args[1] isa GlobalRef || isexpr(stmt.args[1], :static_parameter)) # a GlobalRef or static_parameter isdefined check does not evaluate its argument continue elseif stmt.head === :call diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index c7ed96f4b5129..e1e6c5172a4e5 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -1970,54 +1970,11 @@ function detect_unbound_args(mods...; return collect(ambs) end -# find if var will be constrained to have a definite value -# in any concrete leaftype subtype of typ -function constrains_param(var::TypeVar, @nospecialize(typ), covariant::Bool) - typ === var && return true - while typ isa UnionAll - covariant && constrains_param(var, typ.var.ub, covariant) && return true - # typ.var.lb doesn't constrain var - typ = typ.body - end - if typ isa Union - # for unions, verify that both options would constrain var - ba = constrains_param(var, typ.a, covariant) - bb = constrains_param(var, typ.b, covariant) - (ba && bb) && return true - elseif typ isa DataType - # return true if any param constrains var - fc = length(typ.parameters) - if fc > 0 - if typ.name === Tuple.name - # vararg tuple needs special handling - for i in 1:(fc - 1) - p = typ.parameters[i] - constrains_param(var, p, covariant) && return true - end - lastp = typ.parameters[fc] - vararg = Base.unwrap_unionall(lastp) - if vararg isa Core.TypeofVararg && isdefined(vararg, :N) - constrains_param(var, vararg.N, covariant) && return true - # T = vararg.parameters[1] doesn't constrain var - else - constrains_param(var, lastp, covariant) && return true - end - else - for i in 1:fc - p = typ.parameters[i] - constrains_param(var, p, false) && return true - end - end - end - end - return false -end - function has_unbound_vars(@nospecialize sig) while sig isa UnionAll var = sig.var sig = sig.body - if !constrains_param(var, sig, true) + if !Core.Compiler.constrains_param(var, sig, #=covariant=#true, #=type_constrains=#true) return true end end diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index daa476a627a2a..dcc96ec21228d 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -770,3 +770,18 @@ gotoifnot_throw_check_48583(x) = x ? x : 0 @test !Core.Compiler.is_nothrow(Base.infer_effects(gotoifnot_throw_check_48583, (Missing,))) @test !Core.Compiler.is_nothrow(Base.infer_effects(gotoifnot_throw_check_48583, (Any,))) @test Core.Compiler.is_nothrow(Base.infer_effects(gotoifnot_throw_check_48583, (Bool,))) + + +# unknown :static_parameter should taint :nothrow +# https://github.com/JuliaLang/julia/issues/46771 +unknown_sparam_throw(::Union{Nothing, Type{T}}) where T = (T; nothing) +unknown_sparam_nothrow1(x::Ref{T}) where T = (T; nothing) +unknown_sparam_nothrow2(x::Ref{Ref{T}}) where T = (T; nothing) +@test Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_throw, (Type{Int},))) +@test Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_throw, (Type{<:Integer},))) +@test !Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_throw, (Type,))) +@test !Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_throw, (Nothing,))) +@test !Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_throw, (Union{Type{Int},Nothing},))) +@test !Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_throw, (Any,))) +@test Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_nothrow1, (Ref,))) +@test Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_nothrow2, (Ref{Ref{T}} where T,))) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 335899bdec54d..04de79292e4c6 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -4761,3 +4761,16 @@ g_no_bail_effects_any(x::Any) = f_no_bail_effects_any(x) # issue #48374 @test (() -> Union{<:Nothing})() == Nothing + +# :static_parameter accuracy +unknown_sparam_throw(::Union{Nothing, Type{T}}) where T = @isdefined(T) ? T::Type : nothing +unknown_sparam_nothrow1(x::Ref{T}) where T = @isdefined(T) ? T::Type : nothing +unknown_sparam_nothrow2(x::Ref{Ref{T}}) where T = @isdefined(T) ? T::Type : nothing +@test only(Base.return_types(unknown_sparam_throw, (Type{Int},))) == Type{Int} +@test only(Base.return_types(unknown_sparam_throw, (Type{<:Integer},))) == Type{<:Integer} +@test only(Base.return_types(unknown_sparam_throw, (Type,))) == Union{Nothing, Type} +@test_broken only(Base.return_types(unknown_sparam_throw, (Nothing,))) === Nothing +@test_broken only(Base.return_types(unknown_sparam_throw, (Union{Type{Int},Nothing},))) === Union{Nothing,Type{Int}} +@test only(Base.return_types(unknown_sparam_throw, (Any,))) === Union{Nothing,Type} +@test only(Base.return_types(unknown_sparam_nothrow1, (Ref,))) === Type +@test only(Base.return_types(unknown_sparam_nothrow2, (Ref{Ref{T}} where T,))) === Type From 43c6f7568bed8577c3ff93a73c970451859fae9d Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Wed, 8 Feb 2023 08:12:23 -0500 Subject: [PATCH 058/775] Update LLVM build to 14.0.6+2 (#48544) --- deps/checksums/clang | 232 ++++++++-------- deps/checksums/lld | 232 ++++++++-------- deps/checksums/llvm | 466 ++++++++++++++++---------------- deps/clang.version | 2 +- deps/lld.version | 2 +- deps/llvm-tools.version | 4 +- deps/llvm.version | 4 +- stdlib/libLLVM_jll/Project.toml | 2 +- 8 files changed, 471 insertions(+), 473 deletions(-) diff --git a/deps/checksums/clang b/deps/checksums/clang index 3cb8e59b6791a..6dd3cc5c84cea 100644 --- a/deps/checksums/clang +++ b/deps/checksums/clang @@ -1,116 +1,116 @@ -Clang.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/298e05bc189e33877b76a7a6c9ed9478 -Clang.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/c460899d649982b6cc6c79ccfdebdc98257f7077e2f2f04597f86f3be98f2643400258035614ff7d434639c5861671ca1410945662d00ba1be8f3a887e2e0f59 -Clang.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/7189c71fa493fa40253a7b0644869c55 -Clang.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/5bca9174562f8f1388321e501c9ae36389a7b07a112bddac3c25184b535dc5324b8c7c56f40c5b6a31772dcc87c411054d6817d9348e2d38375887c339426bdd -Clang.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/a170c242afd649b37bfe17196baa1455 -Clang.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/e77f0a8a268297d9bc7c164f7c89e351c1c839fc7ab52265552171d7d73b0c974b8a1c2ee200d7773a331293127b869d635b7cd6273e2db58bc4d60bc163296a -Clang.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/8234f867f72c39bd04cd47a4659a22a1 -Clang.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/accae8bef10e8499b86073d0d50b5dbc2accca7a5a0acccc214d55049da882d705ffa936627a339713fe1aab29e9078888fd474ee41c820316efedca1f35463e -Clang.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/42423636e6a7a726477cf86399227c88 -Clang.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/8cee8754ac597861ffd54b7a0f261efbe44ed3d3ed56711355b3c9f14a21fa0883b5665e4f55f82eabb2eea20a03ab738eaf32589322dce06f3788fbd943ee39 -Clang.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/0151cd8dcc8784154a51a333aa1dc4bd -Clang.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/b2cc6fbeb2ff4079596c09ced2b856ec7be2db64420b09f3b52b80cac1ba900967af611709834940ae3118adf82bdbcb2d5a90d8b9d5b5a1c1aded8e1b604dca -Clang.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/8ed62616ef3e1a583e9095130ebf2ce8 -Clang.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/0ffdae3ec39068e56749da47636dffc21a601df9c2bfc7421c97c6924c6107fe10d2eb641b660fde50ba5fc0a4ceb922041a0adf32cc8aa553d0ab9aa374f11f -Clang.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/a3518f44e475e1ac8b9fcd6fdf470bf3 -Clang.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/2fd3a8524f627a4d7967b6646a9ad9d973301d060883e2b488a67c2b4bb3242c44e46f78a63257cabbca000690e3659e7420173a40af6439106dc1806ce9cfa4 -Clang.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/598475afb9320f81dffd8c2af89564b8 -Clang.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/6a360ea886eca3c7a60bda7a41b305afdcef00593f7084c50a44456b1ccd079c2d6990e90c081c716eafb2c5226d880a9f1bb855e61941fa4acd0590b63dd2fd -Clang.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/22bdaa9b14a7ab40cc0de4c5fb174f20 -Clang.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/96f45265f947357cd3f79d42bc05c891570f62cfa4a84fef6c99a8db14a845a444d857d4e84985a2b741377798861e714252b61f7676269b98caa5e304e63ff6 -Clang.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/70f47a6695b81d157c87668ce3b1e453 -Clang.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/e1ef1e288aa7fad7dccc2bc4bdd0cbef0d134e97b41f85e5e85fc56e6a276beb358aecfe0d0791d745d2e3834ffba269b7bb289716d39ad3260568cc10e9b3da -Clang.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/3141a42822b55f86d0f075ded553be8a -Clang.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/cfae9ff35702119c0cc5de6cdde4640629d020512b086280c26c974b71c024f0555b910a29c95f00a9ffb602f12512f21dbaae10278dc15e6ff6546f66ad1a97 -Clang.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/8b1f2da8ec4768c9426b15cfeed00dbe -Clang.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/ca4efad2aea4339fda792e8ad3cff7ad891ff2ed4a2647d466a6aeab67d61054da79fb39c1f3bd0456226dac5eb8ef1306ff70f795e21725d3611846bdd124d3 -Clang.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/468e0f231d30f64f40866a719d281e5e -Clang.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/91e205aa53337a150d6c3c84edfb06e408aba0c39843db5c3defa18c6684055139c4c40c7714355cb6d7530d40c720a13d59e9a7f99ffbf2ee389ef090687845 -Clang.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/a4006665b5c955df38a251281429dd94 -Clang.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/f67892698dfee1861c0837b6439ad897502e3441f534b4027eb5fda6a73eda616343a8d8d8273f08b7cda0ecebf88eadeea1c2b9df96bc807767dbb455144e17 -Clang.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/6662732339b55dd2aac965b12db07969 -Clang.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/b66ca56ce67b653f903bf85a274b11077f4967946719b71a306ae249867cf22d2f22e8fe7adf67be29b4cff87ca54a8dc910aebcc8325f9188854da5907b3b2b -Clang.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/392e4b35796cd085840345d1078b6c43 -Clang.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/78aa79f0ede37280acd504ff32cad3ea862ee20118e00b65c53d6eb2c0a99d307be7961abc3e53b01a4e44a4a26541c62bc3ba6c1213f17335beee71e905f6bb -Clang.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/679b0cffef21e3e8a3ac41f9c0d1758b -Clang.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/3df8edf277753d5a27826d0614a7e359d44a48f5b086641998d9b0b1f4bf575c03cff03ff59b7dc3ca773af3b91a487755561a5964c7884896a885b40d5c40f3 -Clang.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/5a7526421f59c8626b84fbe3c7adb686 -Clang.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/ea8230025f212b081696dcdd094c7f44f86413c5b6547a31e166b05e119a82fc4afa811fb6907037c07de309f21e1b36c266a65d6f4fed49d815036ff578bcf1 -Clang.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/a385cf886ebf1036e465c54575ee45a8 -Clang.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/2c53f5c1bb19f33764c0c65d7e337fa0f96213fd98527db1680ab2f036ccdef0a51c008667892300413f7ede68b7220da9f36420c1188fb58164497aad41e22e -Clang.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/7c19b127843bfad76f981534395e9b2b -Clang.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/d5ebc8f781f1147a5e459856a2888dc0525f1f63f6f3e53599faaba20c5b6ef75ca01261c8cf8c6917178658e2f38a70720299c5bbbb316b4ef631a8129ed7d0 -Clang.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/5877d43223bb2629934394bcc803c580 -Clang.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/1ebeeb1f491463acaf12d2f1ba10220ed215df80ad79e392f99989466f34d395fdce87fa3502bfdaaca1e4feae7998d861bacd4fcfc12b5e23467d1608cc27cb -Clang.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/b277b57e63f1025bef77786400c30909 -Clang.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/fbf0eb469dd91f9d480417e86ce341215758c48adba98b4eb3b53d9321e2ed81cb551549614da722bdf62eefb8145b55d160a2563cd4523c43ff71276fd45f45 -Clang.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/d973a878a00a38fd892c9e697e4aefac -Clang.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/6557bc7ea57a09ae6dca45cf90f925983f30afabe4af597aa2a397a9a3182b61d0408bf16c4cee5ccab3907a644d6ad5542d30fa28cf3fb5b790f66f43031b91 -Clang.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/79284554223c4866f204bb7704e99bfe -Clang.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/b78b1c7234096d88f061253866300a8db94928ddb8867d9d5a03f263c32fb3ade36f77c351b04ef3ebfd07131e9dfef7afa0d81cf5cb88e87848cbea354f15ce -Clang.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/e9235c729a76570ad2a919f2c4cb2415 -Clang.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/651b512a8cae627cb30575f0907ad1488e3838fa106fe583962e8399883b5b02138e29bcfdb30456ec3e30360efce7283018005ac6352fae4e2564db3b50aac1 -Clang.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/7a7e56421b13e36ddda5119def5cf104 -Clang.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/fd45e46f44bfe2e6346e4795cd1e08bb775c243ba015394e5b9acda2fa0db704cf96191a79cd48c5bbecfc87118c6165ddc3b74f91ef1fa651e71df6f610042e -Clang.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/f59a37a860a38dbdd6f422d9eaf24642 -Clang.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/4e1eca8784e42ff1ac1fcb810579746f49bd54559ca9cb20776fb84e7e42f5fc924a975d4941c1e061b31901f3f9522fca3e8bbeac45fd8717556e5e70fb4b05 -Clang.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/e3c0338c9b592c67562afecaee5eee8e -Clang.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/0dda3627cfec247825103ce25d51457893eb699a6176151dbc0874ef1e087dcbad98790ba6400e2a8c5187b742d2e7a2671b15f3c63b9c17b9eaa8777795eb01 -Clang.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/2359790c7f6de7fbfe8208c3f3cddf34 -Clang.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/c4032bb8322f9daeb7d838ca66868bd5487242ac2e3854d47a789f17211a9255efe79e3893511099ea77f61e85550b56b8c2c3b206fb632c95527ad391584e51 -Clang.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/e8321015b108eace4abceedc289bd6fe -Clang.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/a760c2e1a6ff33225a42ee4beb566d38c78ccc333d58129635e96009ef92d8c96740e164e0305215542bdc3ae0339e698a899c7cc53c260411f1ff22b60d3dde -Clang.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/985f7225c38a5e7f68d759b2929d3fa1 -Clang.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/f74921af797ef0d3d1ec394ce1b672b72d4b25225207f2c7f7f227f0f033647afce139f710e6d4d23d474528d9f1e223f286d0c2b1b1bdf82c38b273bacc838e -Clang.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/9ab44ae551b230e83232de13e2a63203 -Clang.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/f017dfec0f088c7e0fb714164ca4e4f73cc290e8bfc4fa1838bfb5bc8f13d2cbddc1294863febefbf83dfdabf72b6b6493cf8b816b6a7c25d6a29b658d757e80 -Clang.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/d772e714293d4f4a49652413783ad4e4 -Clang.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/b45610cdb1907d9b1aabe6dabf2a6e7ee1e2e796caf5c62f504f17f098a61d2913b02f30570bd7ca62005276c2a2861f6eb347bc93c78e4878e433f13eb187b8 -Clang.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/a91febe6ea0dc6e45a1972084cfd9b55 -Clang.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/cbd826d9843db662e5ab74172380a7d1000822c9c5a821fcc54746909dca2fcdccc7190f723e6aca60d73fb204422c95edd01bbcbe0b355d998f84f40d899ccb -Clang.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/15dfb79ac059279303528fb9bd60417f -Clang.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/15236f71d74448a81f08e9cd9ac503e17e6e8ef679b11219f6d42b8b4a74a8fcb0093f3d3bdc36b8041ec67f1ab30754dc73bb54d498ee3ad52c519cd260cf09 -Clang.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/035734a134fd46a5fe558f264f838299 -Clang.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/2cb35b0907129d1d8d6468d8f9a981839afd829cd16fe5fb539fe50f79560e852e5f0873b577ef0827211a51b07e26bd6090c98cde24fadda58ed28735095fbc -Clang.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/262c482c5af85f15cacd7d63f645589a -Clang.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/ba70b123c105edd4ea1906f988582c8daaf0e625d645ad881976b68b98cd57717143f4b4bf35c3ca90f582ebfdc07c1ca208aa7c7aec330347f1baec74a79262 -Clang.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/935168f2b01f3fc9ab11396ed2d4a0bb -Clang.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/e602720c37e841f67ce2810908943a1bb68d59a2f17ca0ecf772de4a94880459a467fff263c15e310189c12bc573d1d3d2a0264274965d4c5f2957fd36daefee -Clang.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/76275a214835cf423b53bdb2d5d483ba -Clang.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/e231f87d73f32b9f08c1dfc5a7f6407b6a214b28c77d4604c1358ac0ffccb7391e005e4f4e88c03dc5fbe7decac6df77e5d9ec60cdfa18f47bf51c70b0ce3d32 -Clang.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/dd6eb853ba155972322f4f92cd019146 -Clang.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/c48026d655f5a8f896866299e8cbd4841bf3a1a2d00957309cbcdcf137bfd6e41bbbd8bfaae51265127e7922c3384512f6c086060e03e9bb1bcd22586969c9db -Clang.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/a8d6871f004cdca531abcd14a783f418 -Clang.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/e9e33eaa5af1485715fabf281cdf4c45f985904012db4f31a4d6ef70611a2ddecc13cc0dc4deb1ed75a6dd4da4b29b1cfae761c108f661e9df46f04ad9e011ed -Clang.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/12d3c0d8d84a41630198eb69a06651f5 -Clang.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/1ed2de110db980e6daaad2e821fba6653cf1e72ed3b69d41a423cd597eac5ac18f88cd83c2afcc000c41effd268bf8b545c292096449630ab2c091474be42261 -Clang.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/a5b6ba0d493b4542e3c5374e982b60ab -Clang.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/d65a6a6adc6a47be6dbc53b1b74e8ee0065cdc5e593a99f9fe40fdb8d23741146720c89de4dad9388dab801f4797e1524a39d778400e622bb9c03f23622d0708 -Clang.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/c7f5a6766b5f9aeeeff2a10748b35627 -Clang.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/1717d54c6328fd27a87aab0f120d85c066e74cc19f6e77f57f138d75d5da02ca9fc9956e620173385b89badfad12dbb6d5b90d676d000323024060b14a4a2212 -Clang.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/3e415c55a918b9fb20e7a9159f2a302f -Clang.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/be58bacc8fcd1f0cff745d8c7e555feba3e059274925b9361005603f93348d8c2d88c7f9249bc7d422e2bce52cdb280a2f1c5dab93044235cf8959ccfb193f95 -Clang.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/65b32b4dc28112dc57a1d62f5cd8740e -Clang.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/497eb5cfc826424468b3e53af7be3c0ee9c1d7a9ee85f30660dffbc728319301373617f9f7d9d09a300132fc93df8038fe2f099846d6b55ad07263afa2334b96 -Clang.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/066451584d69234c5096edf29421a713 -Clang.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/1e3c7dd8387cc5e5e0b1bc248c6d4dd7ca67bba2c681708593f395158546a305f9f7ea9a12be35376f020c768db834a3458625abe7c7ff3edfecb3b1425506a1 -Clang.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/51d8ed30230760dc228e3f4350cf8527 -Clang.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/37b0a003eaa618782f3ecf660829a1da8ec5d06bff9bdefdc6371a99156f0ab9778cc841c03b6ed1cb6e97e66123ce9f6d91b8c260a27f55e1d5d3371869d45c -Clang.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/4cac304533ee927f818f6f2e8804c6b4 -Clang.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/7f642a1da1074683ac8023d1f2bffeb7ae06d09bbdf31d6cfaa089ba44c459f71326585fce3587f0b1c98df122f635de46b3a2dcc9cd245449e453d47dd3f0f5 -Clang.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/33daf6fbfc468f3e0b013cc43b1482ba -Clang.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/1aae2a79622e821e6a8743786a0191ccafa7fe11c71741cb8cc41029890def518d41973f74568c6d8d4a6c8e3ddb37cbb29413c79517e4cc0458c2b636f92171 -Clang.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/debb98a67dfbac8e7f57ef6ab242816e -Clang.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/ed85d0a7d880193421f132082f46facfb9750da60c7860c611c37947212b7c7bd5393899906b0e21f58d98056f8d0611dbf25e06c6d6021acb4f79a7b6956100 -Clang.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/1456cf6c22c78537bd3feb556319a05a -Clang.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/0cd46d6e0e135db0f2961d614faa59b8442e56f7507f07a27675dd400078d6556060ac13ad40b55f41393ab5be6d1db027038e97d3fd32c833ca1ec64ea3dd4d -Clang.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/8b3ae4d75b49ce372f64011af803c32d -Clang.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/2de4e4521887d6dc951ab29bd25cbaf5d8dbd55630b63682acfb0929ea8a378e051f61f3d1b4cad127b8f67f65848dfd5aaa2ad38dcdee39a0c3f0d0a2962dbe -Clang.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/89f575a07f9b42b659af895d66d36dc0 -Clang.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/cd3a201eedc0c685d6f11537b050bbc8aa29583391790a4d54ba8662a4ddb27574bc588bba52cac899a45733807a879d57f1caac380c0cb6401a7012602aa345 -Clang.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/5f3acbfc31fc032a18799b9738643f44 -Clang.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/ee631163849ac5b8755477c0b485b3bc9a24ca07270e68b374beb5c2ae10aab1a44586ac4f40fcab80a08a3fdccee66584688e98859bf9a07d23c1e14e4a4ca6 -Clang.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/450e2f2c49f97fbc0e18ab3e0daa183d -Clang.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/4ef4adbef50c5bb77699c8aec3f29a8faffbf5114c3b45e6530b4180e97443133d19f02358de99feed58cee37c88830c76600d2bc81fdd0318c3f41540f3190c -Clang.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/ef9b53f0fbf0a71c45277a49104a3939 -Clang.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/326db9bdc978940e36580e3478bd64473bcf157840c9d6eff67ebc1f2452e00d41acc1fa6489c7ac536b000c3c6fa2e86198077d3f95bab32d71cfde6fc1a368 +Clang.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/d7af710da0dfe4a19bd0742499226f8a +Clang.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/b69deeff9169e58986ced189f5947d7de00872ee1d5301de381fba6b71237119ff431762e2f530bc37fee5d640178e2184d0c9f9c6a9b5a5896c210a405f5cc9 +Clang.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/b559523937532f608380ea0ef077e5ed +Clang.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/20f37272327b938d1fcb77538023d88fa799c8863db9a1348cb1e8ff883a8b50474e6496f9730139a1d6ce199b4e17ddbf7e1deba448a7303bae2efffd18d889 +Clang.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/f59591c24225c687863d8878068d5d4b +Clang.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/e1970c15dcad8e8a30d4f300ef597c12f4c953cfb656582dd4d75324a2d17bbf6500de288f80c53add9e6b148b3153abb9f331305ba176deb83504e59fab5c7a +Clang.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/8ee3ba6f2f5c6cbda9515fb3528a6267 +Clang.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/cae04df5b65aaf0f8413f0dcb150e29c276c2e7d60c071f41db1dd35c8c0a0c5d36441e6aaf9c8b32079442ce79a48379b72d86a6d1d2b59af6d9a2744ecc8d6 +Clang.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/f1cc3d1a27058db2427d6c1b7e762200 +Clang.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/5d63f84dd7d119fb2f5cd6e51a16e87557a8b54d2a1f733100e4ff296af496913c84c0c3a77c9f81cb27cf5d23e3ea72ea5be0d6cdb02bb4c3d65182bbc4a84a +Clang.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/6b4203de35b0ce42c7fd5990e0a7dbbc +Clang.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/261242b7ef98203e1cb0a77c3d56b87252c4647bda5229f08d586634ebf217613e434f0010fd088ac3a08e4393fc838a1c26573eb8549bb6bb6e14a0cdfaed26 +Clang.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/c1b0ab693bf840be628469afd62131d0 +Clang.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/ddea198aa6e465ef77e542b7ce4b614e9d08fde32095fc21d85125c7312944f13ce84a9d24ad8fa22caef995c1c70965342a6bb9336297ec305795e9bc457ba4 +Clang.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/dde818c965da13154b438f9a4c7bac2a +Clang.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/d11097c997851e3d4e396abd4e4e0c1cac867a50bde661ada7e4e7fac160de30bf15aeb0e3c38651d7c4e6d8f03eb649e2447a81f2ca0a6d4114bea89692e9ee +Clang.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/0437f8c3983b6762beba43c7f089199c +Clang.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/9bf004c4f21c2215505dfbd1fbca8d886d1fad87ce897cf9e982a0b8f10112cd0d080132aa2b575b5ad1ab681f1eaf8da7ddf026bb2e42d1b1a5a3d2a253f71f +Clang.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/684b5c8b283ab5a341603350cb4c815b +Clang.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/ee0f147da7dcb6bdde1a0c1929b46b442cd01e010cfdcc6f9d3c344f684ae3018faba3d88b46486b3d76ae1f456ba7e34ae15bb4a9432c0ad61eaf03045e2730 +Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/72c1d2de0b4628685067787fe81fd9ae +Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/c18086fa874240574d4084977831e13779e44b2690e810a662e2c9361413f6cfb74bc5aa9718c5b64370f9a797df7544c211ae7089c7323024c2973b6bb016f2 +Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/bdd3524a04ca1606ceedc990c828e7c8 +Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/93d2e22962993a032b9b589bd43a635eafb374b5cf3aabf28aaecb6a6617ea6684ac754f121889610b49efbc2cf18e4d25639a6fe80f5522a9b94ba6f4caaced +Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/36958bf224b804c2effe335f8057b404 +Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/8e7720e7d650a100152219731d92e22152bb58ed9a890c0bbf75aea089bbff80efd53f8f80cfc146457e0def0e4b58bc10997d22aa285c06c3de83b5bc9212b8 +Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/f4ca31683aa1445ecf0fb2166280f8ae +Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/1469ea69e864c77fa0df209d3db5c777c92baea7ed3473aff6c865e399aa3237883eda2d419bb224aac20db2cf43adf40bb0d1d3e53699968b7c5494bff40041 +Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/b6f7cd04f2442dc76b198d85bd419bf4 +Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/518aeb631b70ee0abec23bd79d9943b92ae3db61704533a6abfce75ebeccfe5a4b447a178afe9c331299e0c6d077eebda30ee67d93cdf98dacef62fe69587d46 +Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/e1bc81bf1fe6f27c60ca8c98948d26e3 +Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/8e7768abc0cc97f12580ee5f461868f55c8a4cafa274cb726c7da2fe20513f06d7d299e70b14a3aa290ca7d52d76a9a7a36271785e24379f9aaf8caf0b79cff1 +Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/443d936081749e8ff98c45391caf743c +Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/bfa78eeb3673c265a4c279547b015e9f3ea752d823e1b29e78315d5534b574d6aeaa2c25e5dfda0057b853546912ef56f8cce2a33c5ca400fc3f2fa278c1a9e0 +Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/5424303660411ca2ef318aff90688a35 +Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/06a5e3b8c3dcd28c9baf41b821b05e546b2c2c3e3351437a81c5b88bbc4098bc2cf2001fbd040c6afbcae1bc1764384204c5be787e5cc719a7247857110a0366 +Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/be1f870fc8298be34187262768a35a1d +Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/34069aa66c1016e14edc075ecf7ebe7e885a7aa611b809d7251c658d6c0f833f4b04898b6000a7612524959f35536744c1445b8e03e447601f6b8050ab8e0948 +Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/c40fa2e3499d476f6840cad60deb5562 +Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/297287ff1ef0d38a15361c8c653109caaa8baeb5964c75f033e76176ef5594b5cdf8b2c380ad8f270618d1e73f2a4c67aa6d4f9c971d837f5bb12402863d3258 +Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/010417352411a80c93d9538bf7adf7b5 +Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/60cc639eda70230ca41c63621b08ce00a9460d406c9dc626cdb7d24561fdd2f93cd593cd040694c4c0d4e98bcf5f2b514001a88f19d9a69943632c6f5ad0bd24 +Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/6a3ccd40a6487d2e0b4ccff250dc412c +Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/69443b2da14aee04f4d4772b65776c2e96b7e114f11ac055c38c01f56015e32c35940c0ee42eb216e405f9980178986551eaabe6f02fa2e27fddd7ae073f8830 +Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/3a4210ad734ea531f97c94ca1e8a76ed +Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/fa116c6a227c319facb466fc068df7e1802bbe21fc2267b6075f9aeb519d0306b4193381b7ae17faff2e7ab3e868e9fda80ab4dde44a47df180ef1de8df8d015 +Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/e0da7e68ed8cbbb4ffd624bda6c7aa19 +Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/394804f909bcdee79a1506462be80f071990e255b4b9075dc6e8048983a81794e7937236cbd13cf043679e69322b98282dff86f349ae762be04df130c9ae989b +Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/bd2668a1b87f51851b1d975175431277 +Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/cea19ca32b642c5d6f85b3e14b634be907af33677910c9a31e29539265bd516f8368569285f0d09b9ebe9d0175809d2a1460191dd0f3e1e2ce6bcf4ead83f857 +Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/117d0b2b77e4f62a4d4dfa4f89077b33 +Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/412e0f79382717bfac414db83af70fdaeeaa57b17e66c6525405f775fca922f5c02d01241be97dc493e78da10f3bad0db287ac4cf3450abdb1d7874df8f19ba7 +Clang.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/f325dfa797153a1d97e9b81ec74f2635 +Clang.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/8535ce903c71ed7b3a55d03473a2c921d1d8bf8d9c890f5bc0897af3042f62ee19d0841809daf51254d52aaa539d991d8b7222561d950b6e61894e910760cc30 +Clang.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/bde355360a60d90a9ac1f242b5c114e0 +Clang.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/176c08334a68d9a83feb0635173a3b4b292b6b03bde7c9c4c447ba629eb74685c7259268305c71b1d57d2943de5b152639b31b15347b24a07d2ec6628a37df4c +Clang.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/f50e7f89c8652930a56161d3ca703cf4 +Clang.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/b71cd857d1db0ce0e40bed58d5bbfa3807cf5e2f3c0bb102ec5d1d384aff7d6b45e1b4e895533cbc7176c8a3a2c617e96adf57e5f7f8347681bedabe5083129a +Clang.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/732e09b8c05100125c46947a01146a5a +Clang.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/ead28e4edae3d6bc77fb3c0410d87c468c474dd938551a70ff64a40df90aa1feaa90f088be683bddc688c5e298c5d988d7bba8b54d366490c9d07543b2f459af +Clang.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/10529d4bb4a5d183709c384be98b5ac7 +Clang.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/7b4bae0f72d6e2dce6f5e65f4c0baecfd4f3fe030c2cf731e4e4efb678435ea14d73bd1f3187630beefca734f10d10d9d3bbc76996c7f5cf82440de48be19195 +Clang.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/7403f85447c8e622b01d4ed76fab8b3a +Clang.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/0127fb603efad3229e3d70e5cb857cfe91fe5cd399091e756ebd45e3f0e0159aaea6eeff2e5e8d83bb79521040093de2c6cb4ac479f60a43332581e01dbdf6bd +Clang.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/d47f8ca3f45f485705232e42ad3c1213 +Clang.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/ffe1e0756f960475b12c08b55ae3d81fef3d9fce72be73db8d2f9a6e45ab73110366e1dcb1a7814b4abdcbcf128086c09fdad86a512251ea42169f977df85f8e +Clang.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/3dc043e50aa93e140c5ce9e38c483ee5 +Clang.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/200925daba6db1f10066f64dc5d6cd763c5f2f872ce0f69b1be620637d9eaa0c4865d8a251f385face6b4fe9423f677c83a613c0be2c80dd319ffe2c88ae5ce8 +Clang.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/a5860093b12609c8fae69fc2e280b43a +Clang.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/9d0fb968d834462b98eba5081c712dc23dac43e8f91ae8fca3600fa6aa60890409bc486eacc82cbeb646bc0cced788d0d532c2c87d4a824f5d53c0c99c0c6eb4 +Clang.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/95411432c7722b132f400f8d12d47345 +Clang.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/4191a4d6341177d73a28eef09c022a4d5f235a6106f1361c780b543d5393651aaa7c8da9d8072f5b270ac5673b2af9597eb3e9a3152918785f2c859bb1ca58da +Clang.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/9b410214e77f817d100419493403b667 +Clang.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/1b1db5fc0f201328502a57b0b62b6da740d46c665bedbdc2e1dbfdcc3559a3e833afc44057d595468099a85898ea9eddd27a19c2da1bb704942f343f8c8f92dd +Clang.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/987703541a76749eb03e1f678d0cae43 +Clang.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/7811d9e3784a48f235a63aa58e6253534b3c5b88ba15a574340b5059f4aced222318b152adad83eac0c1d637e1e61d1238c4f5315f1bdc406911e21b76e77064 +Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/77f794a2630074684c3e11e5ba424ce0 +Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/45c204483758b727d074f27ba630b3fef8f44bacaba1ea4a50b46d2bd6defcc30577fec6cfecfe6eb3e6b8d6fb9bf372f714cd5cff4423970ac2358f9ab62c40 +Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/dab80f64898fe9a5ffdffeac60ab9726 +Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/147d9860913196e2873f81098fadcd356adb7b365d12f8a4951d87ee7451ef7191d43841c909406a8ef0cd11a171bc1208bb3803035fb789d4c842b96be1b44c +Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/3ba24f9af99881963370b72b8865152e +Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/7f2b8fceabaa4a4c91edacd54ec7fb365567de4b51b42b34eb89faa2de481dfa75882c4066dc081d15f6aad4ea342b4226510a7d580667ef2b77d297409b9961 +Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/71dac37bbefd7a780592ec81a87864d9 +Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/b972be1373d5a3aec000902f7fa47ab241cdfdb0782dc4734c27310eb23cf457af11c794413482a43247cec744fd4283ed2bf81ea919eb825954bcae7cccd4f8 +Clang.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/f2b3ce2c6888649a1c50fbb54882bba6 +Clang.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/eddbd9db8fe7c7b6afc1ff81ad921f2fc00c81f06d57ce680071d2d115a813b0d8212b76a356cfe26d8495f88cbda3c0f42f32b17f676803706a81ed49749d51 +Clang.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/ece1f4eba3ebcbcaed2a05baa427031b +Clang.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/cc0c694e1365c1ba1521b6e971d850f83497074e5648dbc29503939f00713bb196cadf2d9bee992f7998cbd09e7805e7d3de4fec3e97e0df877c000bfae4cf1e +Clang.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/86b29b63f068c84f0882bc43c03d1f2e +Clang.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/4d6bbcd93d61e80db12218d072b25b2d467159edf2c22c72fad67b2eff7c76ac4635a7818d201734858cf1e936db4b46c7d89ac537f02546957323249f5d23c8 +Clang.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/0090fcfa2823e945b8ff3efc1e8d1a1e +Clang.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/3e665032abcc1c85bd30adc902fad22d198c7364c91906e823523d046291bcb94c7b23a364f66d68c5d1c8158e4397ebeb87c08d8d328c8b3af003fb0460f592 +Clang.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/2a66245fcbe1e9cf63615578f61d90c2 +Clang.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/d4662c7eb908a31a6e6e2d8ff7fbe2c74b6be7e7bd6a39999aa82c489ed50d7c7a11a5ec1d7046f67e6b18d2350fb51a77aeff91e3b18ee5d50acbc50be38e72 +Clang.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/b4ce7760690fe64a96cf8e6a17e70ae7 +Clang.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/4114b2c0bd895d151ab255bda0c24a7ef4e2851c746ef163fbd25cd31587e9fe51d5a770c727b7073ad17a4fdf64e10f0357a8919705be986d86b8ae1a7c3ff1 +Clang.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/35d24663842e2a0abbdb6be5ae0bc95b +Clang.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/5391d44fedb6fe296e24437d8765ed8b819c01be8822bef3bb2e5703fce4f0ebccc7a2aac2ef1ac65dbae6e54dff81be200bde799a0200baa4c06194bcb39504 +Clang.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/2b012d456b01cf6e8c6b95a624d44d5f +Clang.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/ddd340ef459c3214bdc56da19c771d95843ffcd3b7fa8e31f71af09afe7204563e892f5cf7b0a9e83d002b1215f03ba6ad323700c6a6ef7723c1be978b22a454 +Clang.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/70f4d3f4b44a71ce08913a173ecae6a3 +Clang.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/d24ab9e63f445658a8be91f2c9a884ce2567872462357648d16f3a8b9bb7d22f9d149ee26f168e5b40a362166fca875446f543334392bafec9fc34bd6952a4d6 +Clang.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/06a8977d439610b925519206cd95a426 +Clang.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/a90c5223ed234238f7a47c8681880a7dcc93454ef9d34e011a44c89c9282e4b082ae2f45c3c6ac7f4b30d11a7938b03b8be132aaa3d769f21a5d83aa6f2fa6fe +Clang.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/edaffef3a5feb62025a683296ab9f569 +Clang.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/84ceacbad23c900a1157e80534391fa127da4b3d99f280ea537a5c391196bfcbc82b9f2ebf877aa45be045afb3de8e6f43a560e2c20060340de3e4829e95fe6f +Clang.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/9dd123ced026b03f71e156c15ca6188d +Clang.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/375fd746dca3f3e7fae3d1d978f19ef84d6b17005f42094c1754fc40e4ffd0b7f775c64c47ff0ee3cfa1e1cbf75e1725cc84325b05600ffe23b1a0ea8340e3e0 +Clang.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/fcff2f4448512a0bb9b2591feda80171 +Clang.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/c7e53932ee0932aa50d10364da5bef677a708fd52f70b2bb55a97ee9e2b9e75d56d9cc4b18d7bd4567259a4e2733d2a9fe33261c35e373908390fbaf49985d40 +Clang.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/f6a4ba3f52ed4576b79e6316d4e24297 +Clang.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/a06e5985731b3f5f1597076141bc4a46c0369facbf5e7bfd9e869cd2a163a76a3c6e667c7b26730b3022b77db4603429940a5e6b1c1bcf116149a88692206c84 +Clang.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/7295e8afef38b3f6c508c0175c1f5603 +Clang.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/538021b18cc8f01cfb242a8ef4d8ec869337d7f1c697ce8ec96936c8d84020b7a2b88b954589cd839098d30a2f76f1a0c34eb2fc3c1a82e58e22e72543a5f5a5 +Clang.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/d243a90350e08a486ba39160261865e1 +Clang.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/51c63f86cdbc58b8dc6ff595ff3cb5020b98fc3b937113bcba59b8faf0cc5aea68e9aee978b325a485ce0168f0a93f6ce0cee412a75afdd2b58fe88bd8a75c22 diff --git a/deps/checksums/lld b/deps/checksums/lld index 588522e1cdb62..c215798eaf19f 100644 --- a/deps/checksums/lld +++ b/deps/checksums/lld @@ -1,116 +1,116 @@ -LLD.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/fc262d76d2c8b848713b39fda7d55544 -LLD.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/86d584699333feeb574e9c00116a9bcfb728ecd905e983ebf02eaeded052c03a148fcaed1b655c07edaebbfb256f376f6451e1167503b235bf557836a9ddf7f1 -LLD.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/cdf439f1bb444adc506fb844230709b7 -LLD.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/9cefd451e0282d9d787fb79d70430bf811297a81c045af386a0b685f34627a31631d036d1b67dd32360dfffc51450d0498e71a03302e0cbba3e60d45cbd3112b -LLD.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/0e2d3659de3c546073a52db675b2f00d -LLD.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/6d52a3b56f3bdbb59addca2c7d4b0776f8f414191579b59938c5715b14b1d1cc1e76b873c098ce98a28bed57a0a97974805f158ec952a83551adb61dbac3891b -LLD.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/c319caffaf1ae4271e86354661eac133 -LLD.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/f908988258050f06e4022b44dc9e49fd66221abe0c205a92e0fd270705b9b78ad7892ffc9adfc69b9c2a70f955e98678ca65dbcc3ebdd748d08ec1c414e90892 -LLD.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/69fd74156fd9d4c32596f8ec8743f24f -LLD.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/04220b61f3a9a93be147a8e73f044233bda56dce7500c2225089c1fd1e64092f8af7d91b9fd41b4f347950d787194e9ecda0fa3f09e9f0dd3f1f0836d39bcc95 -LLD.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/0848225be33d9f436d6cab9fe0b1a6ca -LLD.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/d1bf4cdb1f47c28f0ceb86606cdf073141e2e5a249756bbc4fb862aa4e3476b9b6c436e994c5702019b82b773c2c3d2f0e78d22a3cdd905e159c9ff753d2619c -LLD.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/8abd66714f15f7db949da104a1ad0fa5 -LLD.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/9edb1dcb32e5133634db932dbd04d29256a4ee636e44933f63c1585113b06dfa6b38eaf87b72a4b3efd044a25f0f173083360cdd15bb964d4f8ff3b4d5125d32 -LLD.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/4c8249e6976e75c7790b8a120a57d8f8 -LLD.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/405552d7d102a393c44d3386cef9a2a85916cdcab88b52bf3918f131b860bead5f6aadefb6794a879e9ae553a6b3a6d6444bb900c33acc77c1f79d60c024e772 -LLD.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/e86955bfda5ae339a22b959d1c97b7f0 -LLD.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/0cfb78274857b1f5f34ec0407dc52a5ec6083a00d9e9b959099839d7467f5ba304dda8a974ba4f3281b66ec3aee5d7ecf0cc774f26a6d059aeca39d850cdd17e -LLD.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/b17725f5c189699eb325506325ad7cc9 -LLD.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/cf7393cb10d023c7d1a04eee85e706c383ed8fe03b66b2e6a46f5a7cd0e76ef5cf065b94e612f6b46f4e2dade6782f8f2ea2b0885fa7dad2d2c83b049b376ce4 -LLD.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/73e8e847ec3126fadec0a6ba79974ec1 -LLD.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/7daface02ef1b12bf738ecc026c33b7568b415b91c452c64125d74db24f97f640a888c313156363de30b78d2c6a2593e3b4d683783b0a63d057b58ebd2a29047 -LLD.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/d141277d0d02d820c17634331bf0a40e -LLD.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/d0e0f145852fbd971ffbab7a92c24e435c581e6945d49588701f9e930d2d16bd6bd598b638b23f473f764bc57248ee9fa5bd725c35249a298ae30063d26ab0b3 -LLD.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/bfb86c885380c9bf0430ae21c5202057 -LLD.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/f5d7fc12102e8c728adf271b2ddddc67d766d1ef7477b57f8788c218f568bf93947137c0607008e9b8e8e7ec5c4ba9cc92688b0b8a15af96b3a54574b6d9f3a3 -LLD.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/054302b380b9b91d0ddfb09426ce44d3 -LLD.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/354c10a4705fad5c32a16140eba579603c077e725c35b1085e8d99a7b766b4a732b5b26f44bf4877f7bae477543f38c2222c3e4b610e901bcf70fa54828ea4e9 -LLD.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/50f02bd884e32ec088f279f99c4536ed -LLD.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/d0094821adc5254ca279f664199c76fc4754c5b1c4d676053acbd490ce1d84808817218b5e20c0e5a07243eb62e3876ab0b5cbfd1c3e80e0b0343153f0d85bd9 -LLD.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/ceb31cf8a3315a2d1c9ec314848ae5d7 -LLD.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/c83f85f36d61076c366ced1b03009e5695b7fbf77dedafbb5efa42e8a167a7841ad6e5c946d5d614e38f259bbc564bb24edf6a041b85ac52e12a4025d9cebc0a -LLD.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/2f51541d7a59b166d5c875c14ed9b5be -LLD.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/a092db3050dbae96a8e85dc5078c13fc415bfaf68800ed8c27871a04da19ac96ed5263366bdcf3f75b42d2c329ba473f1df6a38af3d3968bd1b165f9bdb50e13 -LLD.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/f2e13021c00a2ce98de6a153a3661944 -LLD.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/b1273890b740d9a9fe194d5351b74db261b7c1077e02c02bc6be47b4e61f5b069df248c140e46f5e4a8c735503ffb84dc7ea23f673f3b0943af1667cab836381 -LLD.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/4ded55f6eae1fa6a54e5765af6b99df9 -LLD.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/772c087814ba18418357799737ebf650ea5944e9c1a8f6b4c10770cf14f3ed8ea152854532b7975f6326b81d640021a63f8e0334e64ece776e41c5741591ae52 -LLD.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/bff02de59314ad554f2fd01924a80693 -LLD.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/114519b9ee153a495bedd174b42df227e1f41375511c8a4010a06013c73a3aa5db0938d764e0e639ceb86f9f13513c6416b3291f53eadfe0e1ef5b4a93b4ca03 -LLD.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/70e16637af23ce7f6c33b061e073dafe -LLD.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/2f3afd0cf1ae8a0c0f331a9dcca0e1e69d7b49397c226f1260ed38b2b5a2d400673578be0371cbb2a028827d9e22e6a8890e34110967250ef0f0f907f63d59f2 -LLD.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/b00c58a756363edfa9bcc6e26991ec74 -LLD.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/6e5d9448272aa69ec72a201f5d4b90b0a4804f654b510c4a6d98393cad3c1b352d6bb9f47b909ecf46a8afb4fc582176f0c26c028203cfc72ed6635255a1da4a -LLD.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/955be6224e721378e90a5c78c7c0557f -LLD.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/74c4bb8419a75782721c97d4af893ae4f976ddc7b159937bd6b3a1e00aa63708a227bd02b95685d681afe2955c7bec080873b1fc1fa4507bca24a09edf7adfb1 -LLD.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/5e3394ef7debe390219a4ce95df29741 -LLD.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/17b8f0a257f857093fc70c16821797107b5b1ac62239f28998d4c355e1d0e5541628e917464ad30ffd07f4c8ec3ce262125bcbabb0d39044fad73acdf96ef1e8 -LLD.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/9de7aad1857b8fffe7bd6476b0ce881f -LLD.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/876963d34b883ddfa13e59286d08ae7a6aecdf6a35f77b0d12867435e48468b65008d8c8b1f5bd931196076fffde615971efdb3774b5c7aa68ec08b1d6f0ebf2 -LLD.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/b9ac0071ec9b36819c77529559635998 -LLD.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/c6f7112b680c80d35feb633bfba3910405b0fc0914e05fbf5cf8fad001c5868973b7269b467aa1724d6c2b15278ff54a14aa09808b26104f54eb5452e3f78c43 -LLD.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/e387441aeaecb5f587f2e1edef3717c9 -LLD.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/e9b6e42dd132eff9f539108f9329ce29821d8101879d880e7cff587d3c7982c57eecd6e33d1af18edeb18664e77e6b5bca8f62d69fad57a176f7edcd43a51adc -LLD.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/70aea168615be9cf0868e9a504b2e572 -LLD.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/1a555b1c354ee690718ce08b1f736140925b06cee1b9533962ce7eb7f6332bbdb9e25e1281423772e0fdec8d34b5b690eccb6835cf6b764ada492ab20ad5088a -LLD.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/ca1e12e88613e2fa5f70a9b932306a5a -LLD.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/e2b669f1d5f24673f85f95dc53a041c3b5a34b05f3113803f53fddc9f8637cb92867a79fc02b19ce5e6cd99f0c0a7b6d351fd68994b244c1c35a1ed7058cb0d9 -LLD.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/b6833e7ca5dbf8c46ef536ec834b8f23 -LLD.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/995915b3cf655618176a98c41b54a3345797fb5ace72771ce963644dec67060ca84ba20779b94fc4bc48e8688d1f911b20abfeb459832b279ddcfc5afc998776 -LLD.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/ca9848c652737d4119d6f2f1b83bc807 -LLD.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/465c88336259a472fa49c6ce88d7965e44aaf34d0260e38a832f27ed5b99d77d9653c2390dc12f15db549325170c59be108eb9f41f99ef88d5fae47edd538abf -LLD.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/07c8437e8af4e120268242fe1ceee853 -LLD.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/ead33174285f85e4d8f40baf2f18c88ea51894dfac528be47db16a4885ad658ac5b92431693ef24690d9a8f7a9de7d3fdc348ea1f505e29f8e8455f1a4e57ca8 -LLD.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/13395b2c3c4077899229e5b7dec5e535 -LLD.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/a3f95af4b499f0b4426a109cafc1c9bb4fcf2a600d6aaedc8472d26aa61b04b1aaa3a801d39e165a9e7253eddca6009006e2b8030dded6e592cae7a477015d64 -LLD.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/02f86c3d3e21b8e4de49ee5632d42c1c -LLD.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/ec796d467089ebbb0e5a6085c0f5b15e5f43247335661b22fc95d7656b860ad34bf5dcbc3d3c14898bec871347eee565c18100a872f1150d25120e25702d5613 -LLD.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/055a29a5b3e7bfc69cc4455150d2a765 -LLD.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/0a51cf1f1b1c825cf397279916a0bdda789dc9f8917a9cca70e10050bd253f286fc296725ccc17651d72c304458356c9e0c7744e85ea0961fd5a895a2300eb26 -LLD.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/357377a9b28dbe542141528ff21df505 -LLD.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/199182506961dbb552cdb7a40bd53dd613f9a15bf824d96813bfcd26e0cce1081651314211f99dbeb7145d250ee90eaad760bdfee27ce8e14cc40561ff8e3028 -LLD.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/50e1465dfdd73cb4892dbc84dc2bd407 -LLD.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/bac02215501595510bd92f59bc5d6f707a79faa360823afc82893e7eb64b42ddf035ac3083dbe37f87b3dded5c5f06269b3fdedd2ea1eca0a41738178492fa46 -LLD.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/c86e047af65383a802f9f40f0366486d -LLD.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/24dc600035ac9fc7fc94dd47e3bcb197ea64557565a962bffe683ee040a089a4f0a6618e6ff06c9225ec0961adbfc810706d016a0dab659d77d2fcc73c1e302a -LLD.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/463c8a2b34c01c1964f9090d476ee1b5 -LLD.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/a2d8da10ad2b81b9fb5563ac98e992a7500d35c4999ff51e30dabf662199b4bf47c3b8191a87c6dcbd6fd3fb7917f680ca9d9dfcab92fc66afda42d93bfe7a1c -LLD.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/02871a4b77f564a1562fd1b8766341ec -LLD.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/acf16625e34e0f686bbd02da34515ab9ad1cebbc03fc2cc4793728d153c3d30d5e684179293e0df333bec54c35c02f63b4e8b39373c4a78b4dc496cb84168953 -LLD.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/6ccd870609a949083245a0a469a256c6 -LLD.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/e79381809dfbbb457f6ab542aef7bd20e758f92c6306d8efa900f3d951cc37857170fb41d6e264d8fac903aab6b1b3c2cb6cd7b29b12db05df23a3f0136d3149 -LLD.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/a26d060376eec9f52ca65cc9117de48d -LLD.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/b7ec3789ae9afa900a332e7d545576995de14ebb31b00ef9f8d16d6f8eabdb8d35a508c283b9dc49cbd2cbf0aa99c0c081750ac9d4d80a1fbff71e044361cf72 -LLD.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/c4063d74231b368d5e4dec1f8a110187 -LLD.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/0cedd30610c042e58a91e1f4a46fc973a781a0f432292d40fd87b4907dde868574dfe7cd372d8a05f7e56e73d507b20df8c89d49b1bcb5edea161365aaed04e5 -LLD.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/09f1070e327911a6eb38e4d7481be776 -LLD.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/47236e8449a479599dc03d198c28da352139cb62d08b7def13328a32b5209a29985d7f0044c74d716a3458adbeb8ce2845a760bfe3923a50a4d4eab1f832dbcf -LLD.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/214314e0359316fa00e5a770b55daacb -LLD.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/b25ef3505996442b5e4d4c20e3cd9b9fdf385b8e86a8f5598616943fc8aef8b96307206d6aa836f3f8d65818806eec6901b1d26fb339320f538e3ef7568b6859 -LLD.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/32760e37872e2353c33c175cf42fab39 -LLD.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/6b38c31a34cf4b1384f3b24cbf7e4ebb20a112465293bbb37e33bcf06d998f2ccc0393c94a95a1b39147c8e6eba84b107ae934f207aa56512f16e992a642714d -LLD.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/1e935c8f2b36fb537574c2c14baf51c6 -LLD.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/ffaeb5160830e5859b2f650d818422b80ca187f0cc43422915bdf1dc0b4ccc4b6d0cc8caaf570105ee531169fc494a6fbc9656ea4ba9f9cade8e38b7ee339fc9 -LLD.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/ceeefef634d597e201047041ac330f43 -LLD.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/f4b4ba04f2a72744f59dc26d894956f8af267e7b26a34a658fbf6ebf681b5d414775aa7137e2641ef0e9a0600269926c1a45d98d9ea2087677901c62b94cb414 -LLD.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/a9dbcac1935a74f3bb3ad3a879098ca6 -LLD.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/7975edea6954b6168de5d05f7fdd5d95bcdd3c826b5b098baff86c93928eb3d9b169b3948fd94f9194f01f859cef1f1bd3db7fb470c7296b0194c37adca1de71 -LLD.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/b9690d75d244393b4990c68ff9e4196f -LLD.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/d062cf3d89bbee1597871e2d7921cd4fef31e955434005f300a87fdb6d1245e399e417da7a1093f99ccf816f22873c517937bf7a139efce350e66a01368c0c7a -LLD.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/9c5651ed5d643dd3db02a7183453d3f6 -LLD.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/a6e99cb649cf7d6c81958ba1f2bc8460e3164e0cee4fd5a62bf62bd3040b8641b5665f0eb47933a4f13e1b1034ff6a167938088bac4b9b2eb75dc1060d53fe40 -LLD.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/6fa5219c6a38dffb193ff53d5b3a3d1d -LLD.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/f7191d625f35d8e8a147426c004b1c7bb327e3394b047478c8d003bdbcb1b2da492cfed0c71ca123fea68c500c17d10cb6f157080228ef1517d88a6a2c8103a8 -LLD.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/de8e66dcda15ce77c82350a0c708358f -LLD.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/ead4dd1b926ad903c99a3ca5280397f1f866356a3c2e0c92143165593288af8e29796cc0909e72123b64c58cc522bc49703f5039f731e8805944f8bc8f318104 -LLD.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/fb78f5da88875c423fe9c4e897db7547 -LLD.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/9a667f0d6d1436dcd61e6d83dbd749b5d156cea5b7286667f723d835a93db6409f5c3df3b77e8816707c8d779d9571a7ed1ad764409204a45cd4ff01df252e79 -LLD.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/ac4c0898727e017239bce35420ad80b1 -LLD.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/7e80a8b7583c28b3e92c7f0d1c8b8d5b3ffbe00d5df87e3a2c4a4877421f28e4a9b658672684d0f37164209a9e74191be687571db6c498edc902bd104bc2dc4c -LLD.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/b70020b2b3065478ae37e309cf4e9e8d -LLD.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/56b99742fc2ae442d3d3e3a80339fa016c4b6f53699798aed0351b1e6bf75c8300b18ce2e67416443f7eb8f110f98d3aefadc140d2c9f906e77b69ca349f954a -LLD.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/6ec819571dc37ca63d590bc0a3cb4e54 -LLD.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/e0acd7b407f8dd88572dab34e30d385fe23a8c98dcc4550811db5667e182f2ddbe773b992e4f83015033b0ab6c38071ffe0b6f68e0a01e8f9b9d627a233c46fe -LLD.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/0668a79e8d23e48aa5380fff43436d82 -LLD.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/bd78a518126c715861d7f27993ae26e8082452d4ad18a9d3a0fa39ef46fca8b6e98ca14ec715470161a1c9d64ee71c7ed4c815be1b3c480f1d003ed3377895d1 +LLD.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/5344b22c67a05a0e8f7a2a66b14f7c8d +LLD.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/95f3772927a4770d36817d3c681ea76ecc2f94256fde7866ce688b6631710b4d044952340f2cff08a81b1d8871beb242d0e4e980b5b4455a1b74cbb0786c51d1 +LLD.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/924d77f9440be4e13d88c8d59cb60ae3 +LLD.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/e26b186f49b0709b5f9ebb5485568a69b0bb078395071cb43c02a659dfbb8f25ce21b17806377ae2f8dbefe36bce1db8c29cec8cdcd140099bde76eda5585612 +LLD.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/9b5ff100b332d5f9fe67de97b635ca20 +LLD.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/2d5619d5db7ca68fa4a14693084bb76520cba1a3dcf608783899793eac296c653b84d3b88a559a93a0c95d92c58e4847c796a69207cce5899924381f308d73c9 +LLD.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/20a1e3619501ed9b45344b98551359bc +LLD.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/e517788ae12045fe2647613f7654bd6178af842c2c284178216b87cda67a97fa1f9002292bb4f792de48f3296fff477cb3a80b114be40cf2c6c502d81f350abd +LLD.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/70add6dad0a85be2f44aad61a50dac37 +LLD.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/78062a9f2a7bea15c6230d03011ec6257dc015c76cec0023edbb85709cdfe3a97e2e28fa96045d6b42d8f2849d88d19a2d8e936bb53aa5638dcde17472c1181b +LLD.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/1e4bf7f0827d9056c906fa2b09b381dc +LLD.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/6a06c95a4ad191e013b3ffb15fa8da49ad2c448ca8f17db3079969575b9fda01d98db84bb26eb2e0ad570d3de4f9a07cdc8f0784cf18be8271025bf724eead4d +LLD.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/24e3af3ecf857aafc759ef40b3a52825 +LLD.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/dd1c825a7689c9ffc49365529a5320b618209e617df4720d6664b28cad1d03ea5a1209ab43a356e2871b0d8e6d2fcca82ba75202762046ed6582ed148a387c9f +LLD.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/159d8aeac0680f895b83cb8574ec65df +LLD.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/f219694d0508eccda753d7ae374cbd1b06ceb76c26569ebcf09932ff710f49f5bb789b4cd516ab92e56f9eca727d93a01cfe0940e655029451f3b1e38ef43c82 +LLD.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/2900777df5ec9d27cb05ef802e8256a1 +LLD.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/300f899063db9d3c915413d396a888174b3925876e1180ff5e614ce6f725b859dc483faae836176b95d8e88cdfb0905c7495a692dc626e38de1facf9a28e884f +LLD.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/eee9f3818f04d3ad29d618856fb79659 +LLD.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/5ca354aca71f16304ddabcef24b63fae0716a1bfef1c5d96029175964a7486c7da2d4f0b927e3dd13afddec78ff8ea294464145c9ee19a010433114d2f7c18d5 +LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/d5b3edd7d22e2c65042404330bd3e591 +LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/54af1917db010a0032aa19e7e20ca096609a45b01968353af03121b6809fcac53d414d594ed1127016667ad331c7e76b8ffd305bb186f7c80a786676ee1132e1 +LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/351534948a7507501af4ba6c17fdfa87 +LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/0b4f4265f20414d8d1417913f38cd853fac91a097ac6be297e5472fa05850277dd316e0c91481529a9362873c53e1ff5eddf4c4d2f1bb25c2f8e15a80b5e6497 +LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/4d3ce4de0f8f21f2f2ff75acf20b0362 +LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/d3463be7353570543ef6613dd5e5fdd8790f4cd24362991039178755e9cbfa4b67123219e06b8eb3749dd31b7f759cb899f6d76f5a5155717e1fd83e1325ed65 +LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/847cffc463372eb8ee7ec4ab0e672227 +LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/24596a775492d8f6ced29caff28859a600a5f7c38e6d48e4f9ab0200f28081e43f528c2892c895cea44083331273752545f96c542e0c56209d90743affeb223d +LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/2c932f1a9fd559b9f49d3e0f92ac3302 +LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/f4efb89a3af33817ded7adcbf758c47413f16a94b06978be43ed4b397d212d7da04429d003f2298dcf2ae7ff67614ce83e3d159f0e2c9eaa9268ff5a4c9ec113 +LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/ba7dc5b1b3753b24f15c334a6c25b5c1 +LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/3f09d1e4befbdda185e5e9df153a7730c25d84063ab83f3fefffddab7f0d8402cf923b69cd72bef2d02f3b1fd166924ca0c557c8e2bf4fea8bbb82c80474e387 +LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/18b16c9c5cafbffc375d512b9f84363b +LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/4e17f053e58bad9974002fb1c58ea0546cc1acd475ce253739d2de9113912f15a130ba8cecaef3017202fdd7f5ad5fd9977140d766f659d43abaaa6cf53b23ea +LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/54fa708a76f4627f267d87a9d8f78a4b +LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/7b0ea14824e5144ef05087f88c8ac477a159f76fcb1f84cb59ca98cc7f2c0993ae9c4b57749a395c7ae90f7f37c6a4c4c14ee8fc381859d47736a87acc233349 +LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/576e9e5765a3696e0cd3153ebbef6147 +LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/c643fab9837dbb02ff9cb9202e53766b03649855410e7da15e7e9fe54f6f15b7172fb2d336fc68b315401b8b3f8e8b597204b739592cc2e7cd548a78a3baf8ec +LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/065255972031a362edb3f5fb74e530c6 +LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/a7411c9f44ce798bc5e1f9838b73efeeb97d338a50b605c93918b85c0848cdbccc71daedcaeb4348c7d1236a3b23680f20ab46dce1b2fdd156b29b3c3677f971 +LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/b0ba1697475e35735d0d94389b1524e6 +LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/aba3b866c41c821b9f26a5cc32910e2f976a596b388a14a0dca9326ad72f1d346208bb18f478a0919471e10d578c41190236a6a8cdb94fe394c7e7af120678f0 +LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/1c7e5428cf571721bb320677189f1545 +LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/a8ea619b58e49db4acaec9a0feb5e5aa93b4fc7d1d6190c85c76661b3f833d5fa988692c42e643590a49a804b928a3799c91ac4e0b5df7a826ae76446d8fe8fe +LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/1ccf9badd8d0cfdcad55e080175411f9 +LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/b48da6e3d6513397f861dd694dd2129c0bcc86e73cc44fb079f9e27a715a27ef568078240f5afec0220514221e3fe588854cfc390c432e5d06abffcade1488ed +LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/38d64d93d5d0358135cf98a6399f1eb8 +LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/8af6b61dd4e4b32bfb17e91aabcad7ca31044b31b0aa124bc19185c2125b1f4408fbd865c3d14e56406159e4ba5d8a78d347036b1db09ddba34676eb05e22e1f +LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/9b8be38bed8a91dce3ef2bee57036d3c +LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/45e1eb6a49940fd3f77e8e22ac0cb34b7132466decc9bc893ab697e66becf9d1bf6c74a86509c71c8424d418eb7498b9fe843c913a1bc5ffb897fece178bf8a5 +LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/0b94c549ff012015384d50fba8c0821a +LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/81c31f243e66b7e61cf839647fb765305c6fee0393216846ed8ca4402c1115222e2392b1e5f737b48f4655f18177d97e08f50674b632638dcac537e87e8fa5fb +LLD.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/31f75615fd3219b630d2987e25f93a5b +LLD.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/1699aeea86bd8a1df53fc58770c34b87637ae621c48ac9831874734cd330ac67e845cb55af9077f18068cbd0b66f9f89cc82a286b098e0f44c7bd2b6b0fcef18 +LLD.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/418db3048ce54f8fa3a0f89a11f765ab +LLD.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/f1ecf8c0cb2c6be879e8a860592bcd317821fa2d89978f96175f3bb7c9a3a2aa261123c147dcf6c796c3212871398f5e7cfb5dc153ab503aa8004e6017b842a6 +LLD.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/8a70cbfe920fe7dbec2129f7c5b440b8 +LLD.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/e332ff1e1e2383d0bc01b178ae5ea44efc1986d919cbe9b13e8d4ce0980b5fa97232851d1f2e17b6b4bca0490775cfb00cc0efe0d588bcfbf10ac9e69faf3f8d +LLD.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/53117bd03ceeaabf93aaf0702c30e1e4 +LLD.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/3123e605d2ba1d7bc4fb4aeb15740b651cd09b75a6ea9eba22757a07ebc9dfaeff126bda912187ba2c16e89521d6f94ef53f587a61a67f4deec021479319e21e +LLD.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/0aa6ecc9753e17f8e45f4a63824998df +LLD.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/fd423adc752e338cce486c554b33432920950ccd87a02af9e92afcc415e0dfda141e17ea3a25abdfe2ef03ed9b0415cbc9fc4f099c65e5406a6663e06b707867 +LLD.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/d2bf7a484c8fb31b3d72a18f6eacf042 +LLD.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/cd07d944f26b698a44dddcd0936189cbdd48b2edbb70cc84e9a267729fb672e1781356c56588770443c5523cc2c8900f81c9001be4e56595f6e2aac4a9ef36bb +LLD.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/21cfe7c71234c543e6ce99bd468602d4 +LLD.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/8c70edddf70231f53318781836ae329d20a869f0acac2f8994cb73a221734f8b5c34fe1f68442522a964cb014d972fa5b98773a65366ce02b497d57a23abf616 +LLD.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/dde8dd2112180963e8b2d1c688553ced +LLD.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/704550b2aec1d73c1279eb1131b530e3309c796204845dc1da4b7132c612b862ccb3525a1d7a7af1483ec8cd211d8be532d3a49dde6c7a8e55c617bb3b665455 +LLD.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/c501233e2134da56034390a296684fda +LLD.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/5ef2bfd95647aefdc71b28fc018c3f3c3b5762ce6c8be7c136d56e01f47fe48c4bb99d913501916388d6bb46bd3d9a905efd04cda5b031def2f703942374fddd +LLD.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/8758e9198b98a72ef6c6546afb8f5551 +LLD.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/741d09735e3cef28f9e45aaeb0f715d312fcd92dca3ab9623cdb3215163c9dd68f7252842e3760f9a65470e4a6a666e58ae4da4bf57122d1a64dd70a3856debc +LLD.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/fb6abded431a1bf2c22a2d94d13d6cb9 +LLD.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/182e24978f298057c9d6f2299ef3165b1930c0d42c60215f01ea2ea57e0c864ffd98c80464ef340a34a89b158375746c9b1e063bc89955689a6fc31bc097a4a0 +LLD.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/036e3d82650c9d863c7a2ae6b98007cf +LLD.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/ccde6a4f3407966fa4522568d24d78afde26a5ebbe85708d0d6a28e0ed29ddf47ede86c2abf174c527bfdde53f461733bb2298c0828f5f2b529e390916999da7 +LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/b9fe932890276f6a4e1d315a573ec6e0 +LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/34f8f8b653cd1f56ff049d3f63212133f9cf07f4b0362ab2a2b7140436031c2a8802512c12b004299cecb0d0c8bf515bde38bf4b52dda57039d76ac948eb4577 +LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/f38c4d018cacd0021560e02c3039f5ff +LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/af2f2c901b4e707373a76b2b625f3835fa4573607399303b347e4e6ec401083ba7255c99044bdf853050e5b28fd9a785cd90e3aca0d7beb73406a056fb98fe0a +LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/e5a85e96fe144073b05796728af8c24f +LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/d4f97e23828700a34f0bc2e1000ffa2bf21e8b9879b9d114118bfd42406e65589a6e807337e38701e95a1064dc2924280997c7e342cca55c232e6d841bb40aaf +LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/e318117921ee0dc2d1f3e43931c6d420 +LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/7a5f264c8dd94734cd495e843854cb9c7cd57cf3fe29f08f21d6e7d690da535b95e4e9285df12d89134fcda6c5cf2df5b189d378106fa05f6fd5af92a2096755 +LLD.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/10a67e175285b206e86e1713a38be6a8 +LLD.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/6f1d6455db15a03b54289366970bf63c136630a75bf8a25ef6f68d25f621992e3b0b48b1e02942b7b9953eb3435fa48e54cb801fee69c40e8616b272c9e2194c +LLD.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/a106b6b58bc5c8492334f540b0e427b0 +LLD.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/1d5340a1ffdefaa967af0fe1ac66f56e92fba7f85c5f63b783d224afc318394b3151514fc666c928ffa018db71dc2bc1e0b484a6e61918df9f3e971a4465cf81 +LLD.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/cc73a643641eb34a53a8c4dcc82dfa31 +LLD.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/7598cc478e25c6eb3328292aa6e50ae15454514790b6ae470fd492444018ba905df9e941846316907465c422be7fec0aee462cdc90c29d0dea09dff4d02f71ef +LLD.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/757b7170cc1213d01f0ff2a980372745 +LLD.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/f4a567cc1859b6cf85250e150131d8771b24a534469eb6ad6770f1e1594fd0bd2016cbeefed59dcacb50bc6328f8f462ad2200194afcf3f1eab8ae13eeeeb093 +LLD.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/f8f7b5a5ef2b93da4b1bf2932c098974 +LLD.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/f988ae43f3bfe56a3f6d54ac98420f053973406b07403bc850647698b23783cbfdc77f705299d2df114f7f58f6ef9d30a0eff789780d5e206c5dc5857a81f968 +LLD.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/9692fe8d0533df9243b6b9b49b158e9f +LLD.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/893c8fbe9483106fbf3b28f2490f1cf2faeceda29890f67f9d0adeb6d841dacf7b39300598c673b28157b1fe4a971e749bb71dc3df8e66983ed8ce9fd6973451 +LLD.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/ece7e11a288c20a156542bd31b3e642e +LLD.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/d032c3d738d9073ab92fab38ec47a45371e243963a5bd39a392be44e090189182be71f363ab8fc71116eb9d14835cae2bed70eb1a1ba5c6bda2972412719e0e7 +LLD.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/bec0b1fe84e4e754cf7a3f67362b2d87 +LLD.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/434030e81828df77b452e8d364359652266dedfa731395cc7cb5f41a8102ec614d1c147f25602a435c701c674095640279c59e3ab8ce3422a3694398880a2f7b +LLD.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/dadf9169ef4a6fde170430c5323cacf2 +LLD.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/cad9dd4973d83c6c46e32a04fe0b883d34c067922f14c569679dac9d8f04faeedbfc3aa4a17c6f3b461d12105fa7ef5dfaaccba3c5bdf518ffd06bcb44fe9132 +LLD.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/ad533cdc5d317ea7348efdb08724a9ad +LLD.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/baf8a1fb66ba41f88b42d0966f2b9f0a467dbbeed24f8854598916facdb6567130f379573851ffd137624687c9e5b6637b491a030f1a41916426360a1a9fcd8b +LLD.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/cafa8a847b297701952fa1cb0e50af0b +LLD.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/d7f55d8696fe882e4c275ea40f3774d39a27cede475e135d6dfa7f7f7f4c6633062e0f75f8414963c37e92ca4889ba9f8ad7970a10364b4bbc30d225fa66c901 +LLD.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/dd798db1f473b1ea2801258b98325969 +LLD.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/f4bba7ce56c6d8f55ca5777337d721bc2b663e9c2acd2e9636a86e6948815be1c0e7243c851811763d2c98a5918d454ebee6a48494b884aedce9afe8d9e1ffd5 +LLD.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/817201c1fd49c212881fb2de8971e0f4 +LLD.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/3cdbcd359859d22ecfba562367d3db522e6f25082b3bcdacfc29ad498398e86b2156a6c637aaad0010818771a815e6ab78c3e79227fc5e5de15b3c6de7544b4a +LLD.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/0253e0415d7fd046ece8a9f8b4583cde +LLD.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/f77c61f3a15d3782e1ec017580b782328dc0023b3c17d85a7deb5454e133de058778ac45703b509aaffdec4b58423e816803bb1bdca333b9846632b241f9595c +LLD.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/a2a132e6a57e03acb56a0dc4464f8511 +LLD.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/dfdd2755d341409bc12b097ce01848f99af24bb9bfeea0a687d016fd2167849f38ce4fb13fa87f7c70be0576771999e01f33c58883f56a50cd19ea7e963dffdd +LLD.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/eabba184b37f882bea4167870fa819f1 +LLD.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/2f75f55a31cf13a626070a2bbcb55c8eadeace453af70ed03377e5b4a2b9d7f0f7350aae0669e6702e3b650af6c0ca7c027534d99ae444431de5d3e6e7e53083 diff --git a/deps/checksums/llvm b/deps/checksums/llvm index 905a88f80a2e3..51bde45325990 100644 --- a/deps/checksums/llvm +++ b/deps/checksums/llvm @@ -1,119 +1,119 @@ -LLVM.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/533cdc265cf2625457f3655b8589b43f -LLVM.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/3a0c96b2fc7c16fc33741933654a564574a5059d806a3999f0c0c0af31f99acc5948ef09edb21eae9f6d4362a7968af55781048029a875ea92a981669c6e8cda -LLVM.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/4fbb96de11f9f64d5bc3f467a36b5584 -LLVM.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/bf08ae144e7fdc7f5055f98ff2a4e8d5b46670db00ed498cd3323e10df86506172ff41aa6f0815259018168bdee40a73775b962c5f0ba8639c28b18d65cbf927 -LLVM.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/73e92eaf551cc50ba8b1072ea5a177d8 -LLVM.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/691090c34cb5fe5217c44f1f7f0a411b733bd8197baab7c5cf2eadedb4a6838bd39935795a7715521c8edcf0e611c6555068b49e17c4b2465201aa1772010bab -LLVM.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/3f0783b752b25d2d47b557c3504f35fb -LLVM.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/ffce30b3286ddf0c09328b66876bf3c2f2330ec0adf5bccb4039be3f09cd55acead7c34feb6f9473892338768da4fbc3ee8589197f420d89fcfb2039ff15d889 -LLVM.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/3ec4084b5dcad58981a701fbeaab02e3 -LLVM.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/cf95a368f5a6b9ddcc368ca91631546a92ab374d9da74aa6e2036d61ac788f8348b50465c241853c37f64608bc2d067b96d17990c03ad71ce69032cc012ec433 -LLVM.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/cb4072b14022490456636e0fda20e569 -LLVM.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/1750a2be132a0db76db43b91592c5144ede76c5b205693d5eccc2fd340534fd5d90ab358a8c1af08deab8138e6c82d382e3e95c13ba027b1b92b6f955da1ced5 -LLVM.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/f2f7f1b86007e297c8827d5ab58f5c7d -LLVM.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/49a9efe8cb1352ae38169a3545ce1cb719d0f7fefc29a24b40fd3d59f99c98483ff33e869e283463f17fb63b883cca792f618296a840eeae82a5855a9dc67e86 -LLVM.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/24c659a871de64c3f8d54e7bea029e84 -LLVM.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/3e4487be7762672a4dfd5f7675945d2b640e81660153036ec2b5cf44fd278266233a94a0cfa337ec11c5b4ad6fd46f80406806bdd3a1f1eb9e3da43184af83d6 -LLVM.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/39412690e1c3da7fcf4416184feea3be -LLVM.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/142eaaf10bc19b5e786bd2f8edbab31cd5dfd6045e86c6244239fd7288b7556347adbede12cb40fff02da52295edd85c172fe17ea27126246ff4c8fec05b29d2 -LLVM.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/0fa28f8c44961f43899886a6b6b0c0dc -LLVM.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/9dae70462e8fab0fdb0cd589470bb058569c5640e60bf74e600821344561afbcbf1191e47df9d2117ff5934bf707e57e67fcb9d889e470531505bc18d996b2fa -LLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/1de340a831cbfcb7d026a77d6f91070e -LLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/a36758040ca5d84a514b4764dc60c97e4cb8ff7737d1ffeace3b9f0b0c73716ee7202672291d7bf24da03e193b52292b0c2cb74e200b2eb15b3b982c8f67c3ee -LLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/03042210289cd06ead94a0d84234d99e -LLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/7b16eff41381880a42c6c13f6241aae4184ebd9a5fd696afad4c030f815a210ef54eb877a4e375d9eaa31e53ba71594174edb4c17e60854034e190a6a6ad084f -LLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/c2de107822fb76243378e9de06278775 -LLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/e04a608ba0c7ea6bf827aef2f060241c0891908dd495dbdc675db81114f07c7ecaa27c0df630aa1118f56c71b59ec3f81e96e84336cfcf4cfc16464da0871675 -LLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/58766a44a4f74bba6204c20a6a00a10d -LLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/5776b898b4b988d1fc44a7961fd759646aad17d0f4b5a3544857183ae5e863a1f42e632cbbb7712b95fd418a2c680497ba2c23dc8fc5d6080e25ff94ae289646 -LLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/4a78da7a5b639353e61e47559072e190 -LLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/82dde74099a64b35e94f53765f2748eb65e815a7ccd51a8d288c37ecc306eded95cc4b424812e9e59f247f3f9183c3a1bc7f244ea51f2d1912445db4611c030f -LLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/c49efd85a0273ad684d13101f4dcfff3 -LLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/af25f90527744970458792697027250f543d8ab1ea068767cd1c240a251492ce33b2211e6850a7cf36f16f6b65ba11ccb799f6bbaa777fc92c51785d0188e101 -LLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/167f874b6eae226e02f32c7ac5859b2d -LLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/1a3b0b128b5b8fa26509d3b814f03ed1f1a6cfbc0017e5751761d0aa3b3821dfd4165e7687b09ba03d11c29ea533d866bc435e7187c1816405df37f897ae6d2d -LLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/29f396d657b0e5340443c71d59faf366 -LLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/d43fcdded977428b5938aaa6b6443326cee9b522ceaf5d871c0ef783773e20cd955baf95d0639db7273a8fcccaf17259b05d77a347aa6ac481c446969b436f24 -LLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/c17e06330348f30d3f74f26db2499612 -LLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/ce308261d0be8f4cdf3c639085a38779a214abfe6bfa38626810f9e99c696b133af20a592ccf9a301edd2a05a99195154a76910d8a120178764c8692ec9dc4fa -LLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/203772b8f6063cf6d8d4a7c249fba457 -LLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/ed7574ab6915db25c0b9675be8ab8db04f71cfd775626cf67142d82a2b32f73ba5e3689108bc10872863bcb6672b2cce3502e1bd941ef602559d7fe2c9d8d4e1 -LLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/6a08b5cec8c3147ba678db787fc4d2e1 -LLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/9e8c77bddb64c0bac8326750e81cecc38d54168e1d7760f69d17a1bab4b4b69305c2a75e03f5f10e40a2b2bdc0f07eb2cd5e48e3f8630722e7a30940091c7a69 -LLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/b018658105b8ff058a1c8aa04654e895 -LLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/a182645261fba4d41e89473fa18510778c236c27ac8bc5db1cebdfc1da2617e9b4b940f08045b057c271d44b9a61caee24f4204e1a98cac2c2f40284f14c3e05 -LLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/7100005784dc8202c966c4d9b0f8b4ff -LLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/f38e939bb1cdbb4d895cd8300022094e16b1940eaa450b4098c6822126e83389f52235dbbb22fa776930ef508770db074f5f378848057c693ad1690337ae43ca -LLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/4c8003cb2fac076627ec325340792f5e -LLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/cdb25936c38077b0b486821158a06a0d182e756cb7567cc9e0b0e696fcb10bc2597c41e7ae6316f4945771ddb18a03864ea2ee6ca93cd1eb737eb365933e3a4a -LLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/f6e0156ce3a0dd264668aeea0b6acfef -LLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/01c31900e79786b719d535bb1f57a36e54d56f0690361771ede98f2806fa30f825dcf6d4c176b33d73940c838d8e69440dd49180d3d29954ae02e1525ad05708 -LLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/5ed27717d90e862b22226a11bad4696c -LLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/47a8e6aadc5e736f4b78ff059c628488a685ad3d97a0ac2b8c5d048b116dd0116514399d66983f3f519e8701ea4a851986b94b17405ab31480f09acbd0edf9c0 -LLVM.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/7d154fd2adb1cba4312fa2ee20d2147c -LLVM.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/4a26b459baf5ba801ced522b4575c81328c9212cce9dbd1af233931c95c9b6d869e81964778dffb5d376dc4a258adb8f2986c868d9c90f480d6fdc021f252187 -LLVM.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/0deb9f1cb47d683fc4250071bd9490fe -LLVM.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/2717b2c15b715de323d4a7d914f191e017aaf38224e41554f60c68885b1aad625fa8fa8b3e305e8703e8772407284d03d229cb2d2f9ff219d7dbe5f91366ee9b -LLVM.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/4d9a0d826ea67ab20769999783641abc -LLVM.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/7cf33e40f6672703d88e8a0ce656e955c4a7d010b857cef89f7dc56291b1af1003c0dbb5ab32e0285260216b58e30a38cb78da28d1bf08ee66cd7a8218a835c9 -LLVM.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/54736c04b06b3e1f27673c5b552fd8de -LLVM.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/942f8f4c4191b9ab75e2a03174c0c1241c4c6af06b6f5833fd0c56d57ad195b45374af80089fdb1e2e431f9cbf256a7856ede7e8f76712e0d3189009cae5995b -LLVM.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/9738446d7d909cfaed0658cb104526b8 -LLVM.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/8c8db654a9d2da00195ec1e1beb89f10447a0a73e8d3e055b456f0f7d8e1dd90d7873ef9da2e2b27528b316b334166f2286755abb33acfc0a9eca06b23a26b0e -LLVM.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/16134c865661a0f29d9cc693ed3d5510 -LLVM.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/173407929822c567e7841f24040b82d9981c6bf176717df6942d14ad00757871c1d2a81ccc4467abcad59a1d874d611b7cb5f0cff83898a74fed637781ae0a5e -LLVM.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/d9a7eda0ebfd108c6a1cf435674be3ba -LLVM.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/381bae57c71c387c4608de3cc8a3477b826461a8df1b98fe06259c4a595066a816e96c6731565ea1c3166377a0d9aff722a483e47c76ba01293d408f2eb3b577 -LLVM.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/0d312cbea5545a03a49dabcf7519191b -LLVM.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/39c86e52d6298408fee6ab3de6416b710b782ec0810602ec76eb76a87facab57abdc9d8a60be9522c0665766a24ef0af8c83437493b778f028012577188572a5 -LLVM.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/bf5eb915b604825b04ca84b1ec3e9f1d -LLVM.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/0907a5bfb790c34a8acdd28aeb28ac36a6bec25210b85e2f617f7145ebd80a3d6d4718c633d45411218a5d49545c0adf69c922c19c4674b2db527ce7e7a0d084 -LLVM.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/0972a1aa6efa0accbdb1be9b799aaa6c -LLVM.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/4bcfd7cabdd5ce119cd848d8838644c8f4ff189e2998b4d3ae69193cc9c64ccffb31d08d66b2f81f86876b19266c6d2c362314f539f0612efb69b6b6df981469 -LLVM.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/df4f0d07cdf26759104686d4f36e2818 -LLVM.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/5983d1e9c1a072045c773dc438da13715faad6e0999fa9a3405821a4922ed8fab666186bf1a8dcc45743e27e5065825df8bc92a06cf3321354aaf022191f35c8 -LLVM.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/216857bad881f6a50678e2079d93f9bc -LLVM.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/17cec3034d17551eca798b6e6fc355f746ef71ce2337439b55c2f55b63f0f89168cdadfea578d7971bb1f6eb096bee47e94e34f85ae99d88e39d2052d2a51a6a -LLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/a2b9db6135bafc8f80d275d676859d13 -LLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/bb98b51aa3a3e2f4f58ab6ff0ad36536e4455a602045f811cf30e04e87efc4be4be27b905fc1716b4ed3e2971a5d9b4bd41c438541288ed4240e608adbbbddec -LLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/a3a189210c2b6e2bd32ad7ee6d353a82 -LLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/3304170ea4c369f24a99e6249401a2ed078693c9e6444a03c65dd033bd539326f0444e0ea71e4d8e84dda9cecefb46b7fba87a302365497115e4359370b5fd76 -LLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/dc0be3ad6e188d471bc1b0f7a07aba35 -LLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/0d9eef5b33ce0bd2bd6d7467d198e2f00f6c31ea0cf116491e368c78882f8437442cc18663d96f72e99fe201041d08e79d61c13b3998fdecffb1a7d6f2843a35 -LLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/2563d77bfb2317192f5cd0a00148b6cc -LLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/d0572501e91cce51d662a1a6c780adf148f34e0f4a151c1fb7bb55bc064f7f6f29a6423715f9e2332205e50f076a561ca4b0992e834b234a77f7709ab4c92786 -LLVM.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/a096186819d3f06c70d40498aafc5879 -LLVM.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/67c83539c0272d090e1a2221748eacb4fad15350bfc84f7839227d623ed234878752c38f412f0396b1dacae1543dfa9323e184b98cdec3fb9b436aa8a907bce3 -LLVM.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/c5402ce51e61f4aa46dc56942c374746 -LLVM.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/f9072dab2ee52b5d8116cefbc32b023745860af644de867eef658d0fb9308d5868a5a871489c399cd95efcef9075c7a20b877933e9a243454f0819b4b0cf5213 -LLVM.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/ec70c50570a56b50df05b140f320c475 -LLVM.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/df598d58bb083634b298edc0e4e9006ebfe76029206fda10a58481be1872ea42ee441ebd3c36dd59490c66e89d9db0f610799be4b5d4c96dc315099e2f19728c -LLVM.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/4688e7cb1c73e5957e0ecd0cc14ed53e -LLVM.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/b825067f87d3bebf6e50a472ca6629cce7272579d473e36231bb2b765e509d4fd23cb899ad14489ace12f5ba8531089392d5fb9f3541351b162664eb63ab1390 -LLVM.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/d9762bedfee132f62012b31c3cc4719b -LLVM.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/29a2f8b5e4084d1c10aa15ab7d25812508233cc53c1dedac89d5951bf6488641461507fd769be6e4449fe435c17e933c6a295b00093f158dac97b92b448cb149 -LLVM.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/c68eaa7c015201a8292e1f1d8cc65fd6 -LLVM.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/ccda3083ec0246969824d8c5cfdcb965585fcd1d38306ea160024259e54a433e421d058b6ac2a924f091e0042010ee0512e51af928a6b0762bda0cdb7f99f120 -LLVM.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/853391923e6372c3ec18ff5a44c338aa -LLVM.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/166189bc0b81ca270bb017e68cdb05d4c9d1d1664bd9fd24b9bc49e14dc1d811fc6565958628a062b509f8784d42632603de31df1d4bf1b1e9ef9ab9c5656122 -LLVM.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/2907097b73dcc8d8999b1df921c4b75b -LLVM.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/6574d330914a1535b6e1be83f889c6a2cdb474e83ddf00315662a146f1e29657bddcbbf261315446f749c9859d8fc496be084f3dc56572367b0ad8d25f09f06c -LLVM.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/33e00eb48fba5be418d76f1c1d0ace78 -LLVM.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/10908824a5dda3e69aedd03d0c7695379f465b284b78681d6f8419e7304702ede9c721ae0b54169716abbed429db199450e3fba5b0e5d56e21867defd9573cc1 -LLVM.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/6f8bc1a92fe8f3e85991739fdefaf1a8 -LLVM.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/4ff992b094a6d03256b4eaeebbcbd023a22b54b49976471c16ded0542e1a79e46da43cf0346d54760cd5d18e9b3f108f42f3caa37593a6c1037bcdb4d4461923 -LLVM.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/a3b82c7e8e9af0e7431d7b3a6b3e62a2 -LLVM.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/8fc9c0d2ae985d4da89734aa4b49fb368d245819c1fd4a345baf354d58a4a0b85d6390e1d6979b5ae757e29fdff58579cb7ab6388c596d9923e80d34fac4766d -LLVM.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/9bc7eb74f530e71a3d2dca02a200363d -LLVM.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/3beb680621d3862f18441471cb9318d38da108bb7114423475ca67d3e8998652e4046bf7ffa40692dbb63f377c506e41d3f6c621bc3b1f88366ff0fc6cefe59a -LLVM.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/ba22b4e204e585ff18c3cb57b8e2c87d -LLVM.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/037bcf1c9e0fe5faf40c4c5f8a06b9f90fd8ea36d3649f4faa6927df8615819a2231b4393963a8f29070b0dcef6e755106b12f9cdb2a9a61610dab35fa5aa4bb -LLVM.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/e33235975b1a6ec8f69d40ae91d0e4ef -LLVM.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/1ccbddc51c6d9df883ddb75257fc42ed95c8b3d3fc99d6bbe9aba508e142865bf96678272719f60cb28a3b6f49adf68d390ec50abce47b139e6f7db653537ef0 -LLVM.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/a620552217c5b3b5318be75a3ebe31fe -LLVM.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/5703664ffe0587035cc12de3bace722e7c93cb920810a36beab49d456ddc6d285abab70172f95a83e952f5c5254dbe4825e465d2efc905c6798d7c4cb258ebea -LLVM.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/4d90ccd98213c482f202034d16442be3 -LLVM.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/ba41bc5f229e61a87f517f552fce604ef4fce17523b6b1b856ae7aeba4827f114a0eea73bf05262fd58604fad3e746c8aa54e9fb87cd97aafa50cd9d3396126b +LLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/8eac4da97313ba075330c1df33b7a85f +LLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/7e49321c13c854ee43e7b5ec66951a2417debc79e2874bf35bf2a15238cac622bba5967d8e833152a2967b259b4075573f28b4b3e43817d599376c2ccdcb23bd +LLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/2677a6865f3e9b53069c231594468730 +LLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/dfc917b5f38fdc96492b0c80d82931a1a625452498aa0f9ad50f106673514e922c109a1ea7a44e69473abecd527a2ba47d8aec42ab22ef5ee6cc739813ba043e +LLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/6e89784f5981feea414f9d44e4443a92 +LLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/a37ad85acf99bad4aa435ca5ada57f75117211ebe68a9eb130702d256710ee3fc7c4de6f3dd058a06dc3e2f4474b9fabeefaa8eb0f8b9317aee0459d2d686610 +LLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/3b63e849255b7d5bc6b159b8a6612c44 +LLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/bc5d58c4be42fecd9e9a093eaa56160ff552f17caee2560705efe0532ebaf9f580f01fb44fed46860e6d3811f0386326f14e71b1d267a055484ed6620c2553ba +LLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/1ee1593da5b0a86425cf742335b055b4 +LLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/31bf4ae6b3d0eb836be946f3fd4e98d9ea4596a55311a9e37896a639dbfa4f2ef2ff3d2ee716c5f915c68d271646193995d4312975194264e4103a22b6c013b4 +LLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/bd1c69390d4ff6bc47ac7f20fb2fd596 +LLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/5409aca3de4c18f97367bc6d1f7b9761de80e1e7193f981b5870f6a4315ae6a7e0f7dd37482f237996759f4d04269aaaa7ea89b912b83fe2016dcf78f94b809d +LLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/8ab3ed766d7620b7ffb0995be9126dc0 +LLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/41fc3a758fe586aa16051ef7b88212ba68746ead8446d5af247f1a337b44a60a7f8eb14891aefe2195d4ac6eb4c0b97f047c5b16a3703053986ae50fde4e4854 +LLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/481cff2fac3420b66a73f8541cecf8ff +LLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/c0255dd7d30eda1183aaebdf0ef5d54f5b99c9529a9a36a29ebbdfe801dc6ca4be6ea491584d5b608be1e999c5504211e89880ad729177a47b195de37ef439d5 +LLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/6f664b22078386c19354371eff14cd7a +LLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/9bb3a3ec6f9b6a2a8daca946de94a0d7297880cecc695d39d049347b97a7c3bbe4e126c41c34c7ca26a2ab93b13d38c8054e0fcd373e7e73975c1a94988d43a5 +LLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/51d963d135d08f43897870a300428166 +LLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/7cadccf6838b87273e6afaeebfb71cc3c90bab9d0d3211371de9a86364a3543d97247608f36d48b598c25eb605c9ffb5eacbd1f55a6af55429caec3b099b6b53 +LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/6560ffb0cb4dd783fb658f0990b963f7 +LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/2c4e3775471d5d385b34471e700c2c2d607248f10112e3b95723181521b6ad6a560843bf047aede8b3c16dab7d1a1dfc7611b55b0b610d5dd42af2099bff70fa +LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/6cb2402baf7f4039b541e42644e5ae6f +LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/4ce2285b81650b75385a476125dae6c14548f4e70989aa171609fac2d633a0d0318762cb66331824d05faef381ebef4913ba70c97d12043cc96fdb32bdfce1f9 +LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/1c3ff1c0213d092e1977dc8903604d20 +LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/0abede35823168e4d1a6af03d5cb160a731f7e3e1ee022b915f9516958171a78d2cc536786c7f2d15f5a4684bcf0e4e33a22147999b53df77040f7d3b2fff8e9 +LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/18ae412527f6550ce7ff803ecfa367dc +LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/327b794da935648cec99acf4dc5aa729c1495ad102bd6a3bea9f579497cb10aad79d912640cb4064119cd2535b9dba20701a99a2ffd8cd6c8e48ab38613d62ea +LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/14f35dfafd979c4da4eeaebcdec3248c +LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/d33593b8aa8ce51343767c0de6e7be5ccc793089bb9fa86e13ba7a732368767b96bd5219fc890fd7c6c151a79419b0e8af5c7de1f29b79c36420c02dc988e636 +LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/432bdbb6bce6a0b0a00958b528eee64c +LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/727e8c254be12f8a8bd583100d72fe8792c9e5aab99e5e5d7fe07a5fba683792b8ac377c9929dfb9ff24da3044a8f4bf467d7d317ce85e8ec982aa53895bbe03 +LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/21b4883a4d2bbd287b85187cc93a8bd1 +LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/39b5b5668057afd60f9cb48aabf09d9fb404a4e14db5eb13ae6524802795cd5b23a2aadf7ab0f3b74c73a782ed72ed9a82c3ebd98a7c6785a4cb478fe128cc8b +LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/7c969cd27ebfd0ac55fd9965ec064999 +LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/5bf1aab8b3f48494ae0968434ae5b1670620a7df113f9b85a781e7ed9025e4a91b8f3bcac900657c8d72254d1d98c7399998b7b14fd164d303749e6816aedf67 +LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/4c0b0d214a471e2a1f1c95368c939da7 +LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/66e840c7117f6c4561050a8719ddf91e454f5209377357fcee15bb97e69735dfc1def3571adef292a52bdb0f6c9c36b99644b86770435a453273a7ceedadbac3 +LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/208e0fc55996d91e60a9b44ee5b3de04 +LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/21d75958120a8e279b8c7f05f06338c3904cfee2d6dca4ee2f98a7b391694f147577388dd4662e5b0b5bb54707d7bd9fd5e9b8011519e6c275017d856c640053 +LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/1b4d74a43a5f592f63fba7247caf2d38 +LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/5317ded3758e3568ad957892ef051460729d35d4da716d272b88de4789696d06b4598417beb9e076730e6e02a0d20f3dcf4222638986b8869ca9fb400a3d6808 +LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/aec5398120865028826330cf2d38f590 +LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/8e0acbe7561692f272858c6d9f918a374438f5cdef94c2522012dfe671594c83e6d63f91475c502c413df90b9cb04e5e8ef199111f179f6f1d2f840aedc51ca1 +LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/5d8977d45615cbb8fe0bd103364bb100 +LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/986952f4a445869b607ab634f9e871356f51992ac95a167b45a6d946cf7092df1babf45d409bfb86f4433167b9deec6b9e394a75c445a37740180730b14770d3 +LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/9ba14a5eac8c25a29553cf5c07a9c61e +LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/3f68b6348f05bb7bb3deac81678233b7804060a5dd9ba875e68fa4dd59a55ea8447326243c1dda24de5bbe551af31b7c94b66cbc047de55b7a21a85230fa642b +LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/02a3bf9ad33c815495be4d426281445b +LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/2197a60255b3cda2e7e0a00b81afddb552c5a65385d28013f33dc93067598d4246097a130fb18568fcfa5c70c8f7df04ebd1271bca40fbbd9c57907152d50f0f +LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/c02344cab90c0a78fe3e949c9ed6e9fd +LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/ece70d7ca2c5027deab4ec26e376a0d193d1a9c703c26a7d9c29e03a853741c3296560511a5b561535f04ee61fe571a07638c935211e1d34068c0bc108106455 +LLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/454166455da7bcd5472961645e44b517 +LLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/64ee815667e935f088ba7d215b825390da4d58843cc878b9474479f86829f8db92c14e7058d18cbf419b140208123a2bc3e5952bbf07dd03997fb2b2044b1111 +LLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/9930627a964dba67f8245159fa97c6c7 +LLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/8bdb26d9d2d5ef3fbf64782c318846a1d2a119ab0b8a068428937c71b04cb2ec18b6a7ca367848a3e9afb7b649dfef11954dab0e27757f41a515b188f4504941 +LLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/bf54dad4ab1c66fa0494eecc55689440 +LLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/9f1839e23b86266fea0a11863cfd874c256ceec062fac843bf49d780e3431439527421b747869e1aefd6038563a93815c62a935ae323e19aa61b33f6cf4a5b64 +LLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/1a711b82a5318c5b425df78280ed274a +LLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/df956f922bd1a9e2ec11c84154dc613dc424f3dadca20e908d39b5883e2668f8dae24cc828245b420a5fb88f8553f4393229b4dbd5b0b7f468885bef365609da +LLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/50f42d954ce79ae055b1d99e5e074f17 +LLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/9c338a86f77329b456b49b6b87a25f04fb39c7e818b65fee12cc83d7d9ef6199022899745920b5a0b9450537e1a5392463295472f63dbf317fa6f35cceb8a6f6 +LLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/af0adb5edc6f5573d911b285b90a3972 +LLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/fdafad0afdc123b0adacfa3dd546004659a30a4c28ed118056ee8e69820fe1f33e6f052bfcd39ef9fe7677ac23ab87e52c98c2a248e7bfdb58a3da651ed5fc16 +LLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/78373933d33910dd268eb66d77d7b9ff +LLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/bb9933b984fd494a44662a3b545700c99d166bf7995654c8347fdbd19226c1ea699a53c6d4dd9db17458ce6d34623475c8cae97ad12d21c48aaea8652d3029f9 +LLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/aa21cb389ccf5d49514e4742e34c3a8c +LLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/29e6b732a77f1bb2871d185bd9b8c79f721124a659257085b36a19a520ea9146d4b3d9749d01cbaf06eaf259f240ee634586b9abf53b580a8c0c9aa51575e7b1 +LLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/b4ee794dd157f1f2115a169176700eb2 +LLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/e33fb35cf3107255769d8a788d59d39a7c4fc5e50ae489887b767bdb53f5e2a212eba485af8c937e847dcc96fd987176ac5a97cbd7f72aa84707404f2d8b832f +LLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/272dc8e5a14ac5ccb513327fe2fffca1 +LLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/2b05f64ec0a6aac2210cdf569a32eec727270cf667f8e83167f7e45f62dae5f4e7b9d8c6c24fee970f168e5fa82fe7d8dd5f4a277feed410fbcb754257869c26 +LLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/aa4aa3280955c48432f5e44ede0cbab4 +LLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/4f47ee3a6c0d225108993bdf5586cc5071646513c9991765efff42b35c191e207e977673a688ac33b7d6bbe6b30b394b892cab8631e7c272f01ae24a86ae1f8e +LLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/f1d3a23804ee7383bb545bce4f1d2443 +LLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/5a7fd24e8406400431a2244e4c19dfd128b050f6e6968dd3df686969c3458d6446ebe09393e64989369e38fd4dd099e98ac5f359a7401b4cf56f1d5b777dc9a9 +LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/8971e352ad29a536d48938406b019eb9 +LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/f3a32879f34731c275bbe5d157f5776db176be2424ff11443bd9325d5b3f1d6d478cc9d8f5a8399756c38da6d253d1de13089f20113c24d2dbfb0e8b80e618ae +LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/5780b46302e5388d68f7f5dc0dcd5ab5 +LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/4a02b55aaaada0946c3f6dbae8bfe0eafb1abbf5d8b231bc426925ac28e9b5353f7bd5e12e723a54b72cf1a48193c5cf22cc68bdf4e9843bb4301b9ac1effdcc +LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/08fbaf91ff5c730977f15e869f73f582 +LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/6492619b05cb4857d17a07494b4d8bed87dc2fde4f54492f7ebac734a81bb6a6d854e8f0e3c9d44b5618e7aa446eb179c05d7e5e4388d8ce3d1e3a70bf5e2260 +LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/01982b5a59b24b1e06afce35a3657ab5 +LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/a78cb2d023b12379963a902df9eaaa6caed3832420862852731a830334177d7af38364c75ee216171ac3978474981a50153ce2f62e6e137cd8c1e403590002ec +LLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/45e76481a0816607a619cb74014ba50e +LLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/d92235a2f9f271f6b2f4d0c70b143cbb7a57fceafdef168f5c109e03aa060ca77374989f80958efe8f6750dfd38be64008c8401103d2240b6139d4ad55595610 +LLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/0b6ed0e46bfb8d11663600e755bb43f8 +LLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/5837590ee9955e670bfefc6746d896593233a4b46033a933369df70f9af86af2470986a9d0903e60e14966b3c65c4969d1e599fd08f7db3b42b985710c43a883 +LLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/82c3b519fe20a72f6a7a1540910acf1c +LLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/870978451a61ecd9d3fa294d6904e0dea1773032830b2a4e6489fc60a0a4d34e57224249f41e0292cb04647df2363dab0ab2a4f620503518c690b233c85a4d5a +LLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/86747234744c7de5d6216f852d175333 +LLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/c661e9f5a47fc8783365a3718b2e21dca72cf8bd7580885a2a7ac007eaa8921de3880da9112ec73f2f386a7af3ab9e71f183ce52840db0d0559d4e6b218cf93f +LLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/45c61ca66a89d592236803c896d1c4d3 +LLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/aebe144ec08cc61475f751e4ca869139df85960de6b8117abd0d4376de66f8383b1212d9a3c0591442feab83ac86e8ca6e4d3988be93efe69d605f3282e5fd1c +LLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/416bbafbf3c3b89f13bbf695786df058 +LLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/61fe4b7e027e2fab51d2309e4dc853beda1be305c521423376e30652c123779c8a8be927a32e857f15f74c23e8e1361dca7c28257526dc2cc5276624e4b3140e +LLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/cbb5ffd80e05099cc0897fe7e06833e3 +LLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/190394027a27b7ec49821dcb8bc9709505ac46c6d6231ab83d6229f93ada6029b81f0c7937c4a057c6d8e7e92a4485e32ee8d76b001b97d5408828a4a056240f +LLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/e9f97576575e03b500d593c06e983c1c +LLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/2598129b836060ef32680197cef40608b7e48ce87aaada571e457914628e0c7960c56cb2db8eb7e0468a865f9fe7fe74ea4099d7af3741d8314d37991dd172b2 +LLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/48e47f997aa9fa9ee29b67cecb54afb9 +LLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/1974d9fd350d2ea451eca64a3a4a51b1b84e9e4a9b6a86e41c2941addca787229ed464da25d55bebab903793af096272aa327391eabd14b1c780798fef72f440 +LLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/e1158414a12755d1edaf5f736408b851 +LLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/099c2eb3bf9e4aafbc8f5e206ec1aea48856549a7c48ba7a0cf9bcfcbedc64c8edf235906cd6a8917f673be4e7f01c6c6bc10fa1bf6eee8e3ce219f164134784 +LLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/4a34df9b4a0febb1211c0f245c0e8a2e +LLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/5f0adda9e9f7fb34f1d6451246fc2e815edc68f489b21efa6cfdb8ad5fea1ebfe147ee55dc1b7376aa23f30ea65a221af1350a2815c2ec54023cfe13e463aaab +LLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/4dbd878b1c71c502cfee553ca0817d69 +LLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/21246244ae7109f26de885965a39c88bf268476e7d2dd6139d793cefffbf84bb9a91e9bcde2656af7cff3c77c4b1e64a3dc53bc835e80e16d997103655c8fde4 +LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/1080ce8588dbed9f2035c919db69cb7c +LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/d5326a7d82f3fbc4451de483496197b7ac726bffa76c0ba19bf136752e75d150a0c4e7578dc51934eeb47371ae85e176971545525ff9af293e433f11ec12cc77 +LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/78147126c1cba62e1617b51ba5068618 +LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/6a295619bc6e314e9563f5d04a3e617a1e1e861724e9142e9df9063e57637ce881b38b4fded31356dde7f25d0e8192dc50be6aedb6c08d4b3f26aade5c020d4a +LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/3231888c0e1448c0da3c33c651ce7346 +LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/ebdd2a19e0f3a572a5546fa9adcc4353d1184ef8eb89f75ed92608b2e797211ecae59feec69ceafd79724785e4ed7b0893a8a21a1e5645404f9009684f48435f +LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/d55f62f9c35ca2132f319e6620fbb04e +LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/4f4848759df3feb37abaebb64abf56f43e8940df281f5816cbfb16e443edff16b6ea52e10dcb780d2e4c279cb45b9f50bc05f024ee96b4751276543095d00490 LLVMLibUnwind.v12.0.1+0.aarch64-apple-darwin.tar.gz/md5/b95ad4844e649bf46db43683b55b9f4f LLVMLibUnwind.v12.0.1+0.aarch64-apple-darwin.tar.gz/sha512/15e0996aebe6db91fe58121001aa7ea4b23685ead3c26b5d89afae34b535e34b4e801a971f4854d8e1a1fbc805cece06272470622eef863e225358113a127913 LLVMLibUnwind.v12.0.1+0.aarch64-linux-gnu.tar.gz/md5/6d8783dc9b86c9884e0877f0d8ac4167 @@ -146,123 +146,121 @@ LLVMLibUnwind.v12.0.1+0.x86_64-unknown-freebsd.tar.gz/md5/54ac594b4c8e7f261034a8 LLVMLibUnwind.v12.0.1+0.x86_64-unknown-freebsd.tar.gz/sha512/a43756afd92081e6dd7244d162862fc318b41ca110a5e8be6e4ee2d8fdfd8fb0f79961ae55e48913e055779791bd1c0ecd34fd59281fb66b3c4f24a1f44128f0 LLVMLibUnwind.v12.0.1+0.x86_64-w64-mingw32.tar.gz/md5/83cf8fc2a085a73b8af4245a82b7d32f LLVMLibUnwind.v12.0.1+0.x86_64-w64-mingw32.tar.gz/sha512/297a5c7b33bd3f57878871eccb3b9879ea5549639523a1b9db356b710cafb232906a74d668315340d60ba0c5087d3400f14ab92c3704e32e062e6b546abf7df6 -libLLVM.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/6c5ee2a38d4ea9eedb10ddb182f99e1b -libLLVM.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/471559396c160f7c69e39aa027955eeaa585382feccec79efe63e89d63ca2af0008d20fcd73b67444fca8ae17d48f9324f0d5d207eb1b3661f9562f7aeb4534a -libLLVM.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/12d98767b737c33206b8f06923932f7f -libLLVM.v14.0.6+0.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/08babffaa18e40c0df47d852bc063f6a1bd77ba0a3a73e77e4717c71ddd255ca6ed29924fb78fd61bfed64f29b343818d27da44f43400eb83da391d863473533 -libLLVM.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/c879bdb130e8a068a8474969ca7f23d7 -libLLVM.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/2fa94870a8e72222a72491efa584014d06185ee5c9ff7aef75ae02be246cc438f124020cbe6820023ba5cc823fa60188e054c171cfb80d240db7e0414c63c3f5 -libLLVM.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/bd13bcfb024f72a2df65afc61a305862 -libLLVM.v14.0.6+0.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/999f6199577362a9e607265cab518d8b0b0a9455e6bd7ef4fc80d77f57d81e6cca8ff3f6651eb3b8541d451297c9d85e38a09cb1bfb2960f1d2ffdeda4f657f7 -libLLVM.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/717afe277d8447cc8c9c354d31541ea0 -libLLVM.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/5ce37884f45f128fcf814b54b5b7d8cfc0ef2f4ab5dd38cf6bb8acad3d9accd568cdbcfe32f445890a11ddad4614c57e88a9d6c39787f4ee0738c781637811d8 -libLLVM.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/dbbd454cc983cfee2fbfd7861117ed4f -libLLVM.v14.0.6+0.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/81ea01a7f4d4da96e10abf027e8a2baa2ff8086bf923a9bac82af02416deb543c3528692bd8f470e137669ca58ad05c2224243afca8213cdcf794bb65ed0b452 -libLLVM.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/0b9e93cfc2e6a72aa3e7f1dee03c831e -libLLVM.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/f796a029452a6af689b06d0a7b2b746d49d4a95a0edb6b064baa41614c2f16f843cd613f29ced45f1e42d4c600b5ebc435f564adb2ac52632abb397b97517189 -libLLVM.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/2b259c04529102c161910fcc38ac79ea -libLLVM.v14.0.6+0.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/26f97b72b63bd93f71fc1b4dc56cbd66e05f6633bb35f4f033b10a150773e6550127a13bf49a67cc0492b6140ebf01c02254678eb4af9e2832f2c757ba89b7c2 -libLLVM.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/89e0c9030216fc05b932326ca1d065ec -libLLVM.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/b35b869afff05ecad66e102b1374b9e51a05284684d01a80259a08496bcd1b0d208b4015e64bb55c24f105bcbae0eaeadf188d76daac9bf0800e446782230ff4 -libLLVM.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/f61852a29c7f30562b6b9cb660fbb968 -libLLVM.v14.0.6+0.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/dd7adb9b8b7a7c8a667a3e021032ef601b459f6afff198853dead00879008d24cc67f6785d6ce1ff284ad9c7279e77e817613af40aef86fa1bb4a57c20006a36 -libLLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/91c9fc7bfec64c0309c1f3b7126bba76 -libLLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/d3b03c50a3d6b56b333efca03f7ba0f4633d2a12acf4685fc30cfcedf3d0483e34784aa1715973ace6ed12e0c2bf1a35645b931afa34adfd75f04959509d9218 -libLLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/8a4893e1a088c02f1af8171dbf1e8be9 -libLLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/606861d316759450060eaf81e924b8805494e0303cc8ce8e5b03e09e5c09294ceec274edaacb628eec1ac614ed68f64983b574841f56878b9e308d231ef363c5 -libLLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/1ed600f5af67def3fadac97fb008ad83 -libLLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/12b17d567f426260b6d9dc5c352a609d93a2c81c94287163d67628b3b227410920836a01292718052929f22028cc737cbe7885e430164e5b9bad7aa5fe048d46 -libLLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/7ef417149337a8ef1819dbbaf9ce0d67 -libLLVM.v14.0.6+0.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/031ceb1b9928429674f9c28899c6a0b66eb6f517af0760227587101ba0645e31a772619d8145301a10402784763c07a20046a7e455c4b912e3788e192017cf3b -libLLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/abc563cdf8e7cd16000b0ee872b8aaab -libLLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/45159941d6c9c7fd4e392e500f96dd7ee74fbda9dd29026463bae7d542bb3eb71ea8c4fca49c1738effc1439e54c166fb72962f3d96e351e811efa7aa1770a7f -libLLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/24f6b603f9a4ceb4939a662d212249fd -libLLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/3ebb6c078939191acd90560d1cdc3ba35d7c1d5b77ea699acb9a739b99fe8a2b832af3f9c98337ce435fca31dc7f267cb287a48ef12ca793fec4ffd1ff98e5f2 -libLLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/2cbdd592ab2d5a9ee5ccc68f730ef783 -libLLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/f976047b12cc6f0c09d6996f1986dd03014ae2848ef8287a9889bbc69fbb1e16118af682f83d1f33548ffbaddb6edf557f8b49639d4e63b8d0782dcfebde4420 -libLLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/f627500b3b0340b3510c105e5c26bdd1 -libLLVM.v14.0.6+0.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/3e60bd5d43996ddba4a8bab4e03f3a29370e3bfe147edd61bc26f82b5019e464e8131c20d336be104dfd067e80955f7fbf610e79550394011803b4a941628edb -libLLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/0eed22f302a580a21105b6111ece2760 -libLLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/e14ac2bf1b5087b904e453ab18bc5750f6a8b17a0e247b4e791bea967b288cd5890af748608ef4dfe74a6fbc588841c4c8c7b58587ba6088cff737f19b15af0b -libLLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/4f6f3bded0d4fde726cd2e8e90efcb31 -libLLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/a112057ae5b44373101a744ac6d65b4561f76435d7990961f0df692f4b44b792811a96df6d6307dd0abc3c35856ae456ca8f1fabfcc564d8f3c0e812f2793940 -libLLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/e922fe5db388a4493ea2c19bfb468931 -libLLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/9fa3b3956ac18d1735fc6b78c874415405d808754e96da3bd1a4e43cee7c0f7e6a65fc982f4868c47caf155d4f0f9df5dfee46bdddc6769b641c046d9fdd88af -libLLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/3f1d64de6acd05aaa93efb2c36fa0d6e -libLLVM.v14.0.6+0.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/fbf7e4d53bd052eee794de903cf17bd3abd570a0a4a98d14a5fcbe4fd2bc121a7ebcf04110a9c1c7907c61ef13b0d972ef085b7c5a294cd9613c23ac14079b1f -libLLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/9ef224e893cfef52494dc43787963aaa -libLLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/8b8678c14cafe937f3cd4619486c96fee651503571f552d77da9be49f77530d9df98db1863b3970ab3f400b5ca76df538887c2488ba4f6876b0f9853f3edb5ff -libLLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/307a2e7c5689ed7fa05aac413e932aaa -libLLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/8ef7ff87f11c3e6f0daabeab8e4b15e47bbfe3296b7d99aa15a4f2debca7527c5f8dec193bde4d96e0b979bf7438a75c4a6e8faed23acf08debac0ede679f493 -libLLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/881fd88344cf429e78519c48794e2118 -libLLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/2dae579a7c90d3528aaa30892c0f6f0d7c82c16eaf011bb460602cd80f7b806d114c3417664635919b28ee9c15ef4035cdefed047348472fe8d2190db4af41a1 -libLLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/ab01cffaabf4c788355e7cb25b51af45 -libLLVM.v14.0.6+0.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/c2cd6a426728a5b0dbdcdf7f67e4621aab0f89da1187bc9447d4e5a7cc6c716b649fc1dc957ab3fcc82d2287712ce9d1c1116dea85e0a9324909a68c12484e0c -libLLVM.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/ded7afb1e8c5be1189adcbf84a169475 -libLLVM.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/7013d0d414917cd38144afd7ea90d1718a9084d1b8702bb911060a24a106e3fb9e584094f541aff71dea4961b25698d15c1f9515cb44b137ce855defa5e47197 -libLLVM.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/abb91475af33c6a97948d4af2693d2e7 -libLLVM.v14.0.6+0.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/64b8ae8f399bac84ac092e20597767c79e3427e09b8a8ed8d3292c4d1a233bdef00d2b27b578d1192850b72480e8e9e9fe025cca8aae54208122b492cce1cf48 -libLLVM.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/df2c50462c36e9e3647cd6ac98f4f395 -libLLVM.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/bbd20df4071e221417724091527a54dede73490af03357b79db7a63c2d925a7b2126dd967eff4bec14b27ebe263f9d88d212eed82d7260f693c67ddf0839cfb2 -libLLVM.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/3844c7dd6f9d2e033bb5b98744a23213 -libLLVM.v14.0.6+0.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/380d56a1b872070a79c37c4d02948a008312a6ce262a38a94206d5b4209d9ee07cddc2536adb8c6dd109e4554ba16c490c96dae8307a1f7699d562b0d686d333 -libLLVM.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/294cae70e45a6a28d31dd524ca950976 -libLLVM.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/79cadf16603e388d9d0f9f925403ff7c34a04acdbd02f33dd4e823249f3dd02b6e37edfc247088220b8b333e3a8fd64c4ee267cff9d073c231109ea51514407e -libLLVM.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/fff69236c97a34f0fe9d50ed93b82fc3 -libLLVM.v14.0.6+0.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/4f7b03836bae2767ff910c5c7c79dccaae1c8478de597fb81e2b686f6e7dd195acf2140e3be72cae77509be9f25092fe8c19bd64af041469e45cf948a0baeff7 -libLLVM.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/527c35f304ab42a39df7e1fcecec26f3 -libLLVM.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/7ed37d57cf4f61bc29d7eec25579b17f449c734e658ce779fea923ccf997732b4d07c9d8bc68962fa42c0f66d739b8a9abafe0c5efb940e36c4bcf2bf6a1f0da -libLLVM.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/504a55204bb0f8b57debd377788aab7d -libLLVM.v14.0.6+0.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/03c3bd280c77356d168fcf5837dbe984f1be225a3576032de76dde1d9bb151e3b63efbd35542dff315999b1113c74ae466cc8d7e52ce12cb5d195b4bd24fca2a -libLLVM.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/3a6e6fa8ad2ea6665b184ecfb8e3f8d9 -libLLVM.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/21131866e0c4355e9de01c32415b337babf113386c293e55191070bf5b08d64c7cf00d15407e78a01f3a25d557c208774df99d46c9a858e35ce27c5609bf30c8 -libLLVM.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/ab3b323eee3d22829a74d966ec464196 -libLLVM.v14.0.6+0.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/cc297c791c7367756da76b2f0aa4b272a48d4bbd563f50b6e9d9f6c741b1a060bd3d1637838233a67dd12976d27b1d2e9a041cbdbcc42a23f7ca5da73e38db3d -libLLVM.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/0e8c5a7d07c21fa9070e3c9fdeade6ad -libLLVM.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/3306216a8edb2c91f7bec2fa65737e264fecbb5fa9b6431b493e5e42c9637f52d43018c3126e53d9963c88fc095de26b258e50e3f0a9ca5dd68d1530368e3776 -libLLVM.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/81922f4a88c94409b117e6fe1f8e9832 -libLLVM.v14.0.6+0.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/265aa30a53a68432bc254164598fba8fcd4909b07cfb2a8e80a6d3a71434d1851728329354c8c6b1a5da91559ca0da9718d49b711fb94ec9b974ea5efd97cc3d -libLLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/cd1ce9719cbcaca2ddf6bec9cb34c2eb -libLLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/849bce21013282edd2e32e4b6ab98f8586c2d986656498d6efd0092d4db9358a05f2a33c2200c17b1fb6cff2714318f7f322a5cf1ecf9a16c6bac5cac3517627 -libLLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/0d44242a7516c2808eca52cb54b5d01b -libLLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/3cea355f8d754cc712f16ad87f24f637381313f18963c8382cc79b3cf0237c4829f9c7d498d57d28c7aef5106ae7dafdfafabe90351ffb307adeb01e43bcf722 -libLLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/7b93f9f33beee190dbaa71508ef4d562 -libLLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/55eeba37a5b0724cbe813d5d9ec8e3b61f988c953896cc767866059c8d01e77163526935f16a8e30c6dde17999b2d6ea4088980ac118f6720f4bad8183debfcc -libLLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/24b8b6e559ea15a6bf90c39e2040f852 -libLLVM.v14.0.6+0.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/180307a7e17edd2ce4758595afa784cccdfc2b09d9a1c601a69ca3a0ac1be420bc1990b8773209410e8da5c5fc81bc3a2e54687898a6d3ef0d496a27a8b27222 -libLLVM.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/a74e1d380cde9950179a4b6a8e28ca61 -libLLVM.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/8f8ff44ed42b5315949d076b73abe91fdf958fd82a337dd15dd71182038e1643337159652e5fd911323af21a4668e46d3a401d85774f6d33fac982945d77022f -libLLVM.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/85b65eef488ec075920048bfa6d9a7a1 -libLLVM.v14.0.6+0.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/cf865396678dc95b9d514c286fdbe85ea2a74715f5888e4392250770eb9556e354ecd9e52fc28864df88f87e06b8b39c6b403eda2bf9efd46d205f4c982e1551 -libLLVM.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/411a28efef112b63a7e2cc5a7fa79432 -libLLVM.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/7d5eb694dd50f9f02d2cf0b2787c66f6b824c3928616759509326c7d85578f984d29ca888d3366cec9584466fcee063f236dcf0a353df280f7abb79352930f96 -libLLVM.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/1fa0b4eefa8a57c858dbd9788d222741 -libLLVM.v14.0.6+0.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/8c1b075ea01b661da518e309d521636c8e0dbeeaad688e9220db8b22965172050b9a0edb3b1098c3191873a516d03ab86b495550933ac680300ec0b42d3f31b3 -libLLVM.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/b4f68d0b04db7c8021a180fe1df14768 -libLLVM.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/5c43368be28fc7cfa14ba03cb76b3a30f854e2c34d6b2ba3b9d7887dd2f0e4944f16b6380617bf080fc7bd760629b87f1292b49c07c684bfaf33ff9a48ba22ce -libLLVM.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/e3b764196550adc33db2c15a74402dc4 -libLLVM.v14.0.6+0.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/1822f35862887d00c162a8fc39e70d9fbf73ff6f2fd5bed44b678a1f983bf20f7a11d524e7bdbd3774d919392b061d1f980dcc12b306fc95cd456e984e81d2ca -libLLVM.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/2d7837e11d7e65ca30878c25b38ff171 -libLLVM.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/62be4f20d35aa6efa2d52858d66150205f2b5fc1fc5faa2a49a48b27e78fd1587ed4b62123cdb25486d6a6f87951e628a45356df4263b7bdee820c850b727fe2 -libLLVM.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/65d72c92bbbedb202c5e26fb4bfab6be -libLLVM.v14.0.6+0.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/2ec91bc32d89d390d104ff248440435df9cc73d7df79892f93b800feede8bb8d43f2a3e6d4682d72850b038ca75a256d24c7e9e34a751288f89cf5a8d69dcba9 -libLLVM.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/f2eb226ef29f4ab2fa303f65253dac66 -libLLVM.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/61e2a4d02d113ea9df863a268268d3bdea5a9c9f481b9d4ae6c96a553e7516fdfb23896d4c17bbcfef931689d67daca61ef53f307713f3a583988420dc839af5 -libLLVM.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/d8cd2760cb5a2a332488a6d8170ce82b -libLLVM.v14.0.6+0.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/a11764fb22d64831ce97315877d10c760697b0aa8fd667c6f3763038037fbe220285db20c981aa63f028b4dd13a8b0b57b32be6c792c1afa93dc531aff297621 -libLLVM.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/33343f2e7fa1169eef570305a4d8837f -libLLVM.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/49b8189e8cac264f0d7cae54d78e65913f4dc047cc51f074a557073662203a96d15ef64452afb8069018f523bafd724951531d6b03c9034cdf16d359eeb9a850 -libLLVM.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/3d211319807cdbfb6e405f47ec2a1d42 -libLLVM.v14.0.6+0.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/0173f80d5d6912543b2c3932a47d667449a579aed7f2b291f49bba6dcd0b680705a8f10be6175517fa4e8aecf2cfd027ef15d526bae76c99893f7203b7cf620a -libLLVM.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/9018ceb966b33afecd3f9440e75393f9 -libLLVM.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/4bdc0e93f7d6be18c3522acbb016dc2e770d96be60a665f118866263366f1d6bc7702046d65e962d063b41b0d24f5a4fd0d4cfa5c4a9758052538e1404801708 -libLLVM.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/f15cfe02c92f22187b71d5f8508a1bca -libLLVM.v14.0.6+0.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/b73f4932df6c36b0e943d6c6048f7a238aa1d28347ee97b2a7daab622d173c23fbf452a026bdbb26eda102f99cd96a3d09a751a462f201d207dcffdafc4be429 -libLLVM.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/dfb780878726fc582a56ff433c27818e -libLLVM.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/59b7a0ecb36288149d19d72e79ab4cb27eba5e873617ca4ae020f281a764345aa9a9226c51ad6dbf8e5de3735ef73cbdd6a0447f7d7c58850fafba3c675695af -libLLVM.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/49fb02433cb908da55ab0413eb91b0ab -libLLVM.v14.0.6+0.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/f411737f06e3927b199a855802bb840d33471f7d9a011d49fb299916e313ddba10f635ce58080787345f387a9dddd18b368e9f45a233e5ff84426d101434e298 -llvm-julia-14.0.6-0.tar.gz/md5/b262d8da6940024a9f0f717d26023f31 -llvm-julia-14.0.6-0.tar.gz/sha512/19af997a93dee55fd7d53c73d85c29f479ec81343266a81a81fef5321c88455eb3a315c03664f1d9763f2cb3f286f202d77629cf528b3f7ae77d369dc3d2c8a4 +libLLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/ab5a9ae6d4f42d18fa71f95b4bc86513 +libLLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/977d4f159fda3d4c18ed920c1935f32cdca743b3fc8527b90b68ed3a6375903f9b614f0bd83b9dc35b40c96ec6e6ca4e4aba6aacb3e3cd051cae5a040fa6ffb4 +libLLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/c90bdd3e26729482f29164d72efefb3a +libLLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/c86257eaa3a9975cb0d61f526ae4852c66cdfde24f43e2aa7d75787dd84a620d2dccd623a540493f688a908db96d4f4ec36699927482b2612cc49dc694ae9656 +libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/4177bb0f67f4351b3e92383011e3d5e1 +libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/6f8a7795f9097e6108777fb999396a44360edbf50c889cb181512594b4ef717f0e3e4e03a92f1481b49077562685d13128ee4542a06a1b655113042c60af834b +libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/43e6b5e9edda8754abfe44a85958f4da +libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/f0fc52ec77f2f7570c00219784c2aa2e9642339c9a00fb8c3ccf0d0545068f8d55c126897ecc1244a22efde4840d2408f6377c8a1c6ad1d9c6ba3e6a5ac63e32 +libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/7545e9392b0f10ad60f36e7db7196a80 +libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/a56058b0d3b2415c1912364bc3f725f44d517d10e0d93462e0f7887f023da20b5f99c1262b176da46cc3acce9d79869775037b2594cff86697463ceacd48e26f +libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/39d57b2e6419fe728cf091580babe118 +libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/b149e3d239511fac07b55c9cdd5793529fdd5275b4680d4b3c80a187e4e97991e5285956ef2299332a36a66c8e2d4be67f21927f834e8046a462e0b54002c67a +libLLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/399fc68e348d561d95c986160294cbef +libLLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/7c03de6445393a2881367b9d346ec8e8c6216c0c1b94a3f07594abd68f6a1e4ae40edec8aba984363bbf48fe29679c67174b71b4ae15feb7cfb6cdd8f0e126e9 +libLLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/c3fd98354a09dec28f2c95b98b053c99 +libLLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/9f5571f87bf5ed390d37d6212899e8d2c51fa89a5f1cbb039d2dacbd6f1c7f2789787c5197dc82ede18a1ea868f3649a24449d563ff85333a43720a508af8d07 +libLLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/20198f989a900a79c8d590d9c6938ef2 +libLLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/f14f771acbaa04986e835062172675e5afa3a76d9b1e5c977aa8a1f7cf37d0b51cfed13a0f19a618fd14f540649d42c7d9a06a3bdfa32a7308498963cbf0a5dc +libLLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/ec8469b1ecd45be0f4ec11a51c332c64 +libLLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/52ca038d86a86b91e812b755901276ef5bc9b04cac7537216bb631f6394a46066240c76edef7e3db90d75b24e528634491f523bcd6a3686013fe3406506e8740 +libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/7fc93074bd66c0f8311318a031aeaa6b +libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/9db07db632c227d109f56fdbe503d07cdbc163a3f6b225498f700145782c4c846c2aca47a82cf174ebcef5b230f58419d1084d030932c85e671d3b5f43a5c0bf +libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/a3b134c87c5a8eb24d02caf9a0d11ede +libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/fa4eb2f09f58cd3248018efce795d64f7b8e31a65fb90cb5e53fba5cc175c69e082adbaf7ba87f645508b7a7b7223d49602c8013c1d5beceaf614d66565dabd9 +libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/de80f238ab1969b16b26998d5d7b3f43 +libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/e6a626ea5731abaddf56d28e0be7080b82662471878b6e0c67dff9d10c461ab24ffbdfa1a45e91dd24277ed85d5f55126ab59139b54fcc291deeef2c5dcd72ad +libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/75be85afadc2d42833993521ee415b58 +libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/fd0176144abdac37c02d3e1f2c4f27297033fa331f99d26e103b87957107afcf2e6a8db6b8beeae037f528fa5c1eec29c1079c0d62d082e8a613e63223b0f888 +libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/4e20e425d15b5a8be8c1f700471b0b99 +libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/7aa390adfe0cf046173ccf2eb4e077585fec64cb82cae1569543183be276306191b2c4bf42e7eeab536ee7886f94716936568ccac82a5f37a2633d58bcdfb539 +libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/7c81a3e495428aa7ea58e247471f642b +libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/15c13bec336ec071f14640baa2ca4723212ef5a97fdae9a5b90b7a07aa1380eedfa72af27ea13fa694921be45cc799eb06622345e87eedfece2780f8b5677293 +libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/c365fa356c069d232c0fb58dd82ae2e0 +libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/afb435457c0220d3e06c6aa9361060d8fa04e1a3b0e406e1ab9b1f18c60f2e2464d08b5afd2e2251c1e38a0e4ea0470c6af05201708a51ebd55a0a37da3662fd +libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/6d2f02e0fb213a6683e6fd229cb39458 +libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/6dd9f7ad806f17182d1e43c1b59fb00a3f230d9747924351c199028db6beb7c1e66fba41d58fd2c24f0aaa8a94ff2e95b8d35486a6e0b5c0f2abb37eedf593fe +libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/c68f787fae3c30b11b0adbc38572b4f3 +libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/0a094431baec6f84b8fef56446ea95c1d7e1be309d6bd71c80e7ff069573d83c1595e88f8a44c9982c87b6ce5d3d97a4def8b17c79e2fa4a1c556c64695e4be7 +libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/4930ed9f72d3aa896a7c22bede0abfa7 +libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/68166a7941bd9c4121b3263ca881123958c9f589b826c0eaea2d06982566898e213aa586af44901d04bdab3c99c2bdc9e6d6d9534ac1ffe9a00eeb9ef311e056 +libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/493d597530b6352f52e06267b96faad2 +libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/bdc3ca526a84b273df908ed7deec3ecea73c66f3991e8b5d0fbf8e29b6376f6f8bc7e789615467ab2d3828d8fb76e61a22cf87fd589fa04c4910ae18944b705b +libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/256d30ff71f384240704b543fce2471c +libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/353c553a5991e893a329e565919da707d3dc9ab61297c27f5fae7c74bdd1cadeedeaf3601b27357cab11a6b113bfe66f2d39f31ad328b205662e1a5d07c9c5bd +libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/47bbde3f4962518804468a5d7bbe79b3 +libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/a9e4b806cfd1d45d2e43d899a9e89e74b5c30fa82e9b83274241d919a635a5f30863573b1d509b3c61a67bc53486e5c85068e2d6221aad992ecd673e51dd53b7 +libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/ac0a061b0b0502ecbdcba24727b05c26 +libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/888e440c19296f79470a46859c551e283f1cee953dc197494189e2fd7ce03f5eff07b2dd504fe8d7e0b1d522bac14f518c7803795e84dbfa33966fae965b6f90 +libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/6c163cf961ee1f95b365f1e8462841e4 +libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/e15909f72f481c242b06c7bd9fdf7d6acede33661cd383e4b33a29bbe4c1727f86255ae0c9519967e90d2606cc2446544c00eb6bc072f705fca122910cf63e16 +libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/358d52a901a9d2ec6e97b6cf3ec324a4 +libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/35cd3064a55ec7bf7824146bdba1905ac065e1d9e014a836b15af6ad17b23a426bb3274971ae9ce7efd8cec7845af2897feae3db8f586772e1abe9e8bcf5143f +libLLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/8ee57951124a8370b86d6c4af30300ba +libLLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/9af5f771b94092e28bf36f7f8f1d5f7c510815f5af12b16215f39e2f377f206f82f9d37de35a142f89b2092125647073f1d0ede9345122b696752063c881c82a +libLLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/95383b63e38ada6a4c36230a4ca9496c +libLLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/7b7047fa5292a6ceb7d540006cd1f07b00d6e0f5f00c7090178e867f6f62ee0c15d6029c31a3f328f99fe3aaa8a1581f1791a212f79ce42e737f9feeaf58616b +libLLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/4248ff578e33399c33c2a39c2f3d0b05 +libLLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/f856964e9ab29f4b73414041eba95637506d8c01dfd6e97a982d8e5f8845e30487b3492da776a9d35696c14a9e027beb3752e2946de6e9db11070b683ca7e6c0 +libLLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/d77c1501a19329d95545178f63697015 +libLLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/d46d163e5bb7720b2cafab0dc664c882729307d446610753e1d9269a4f524bcec1643fce603415b8c61e11589bbc0cdf4664cb58b433eec646eea6564f78b3f9 +libLLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/f89572a9c10f8913b7c009ed39f41d68 +libLLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/8924341540b7d57389c2b4274a51a1a7c7543783442a3b98add990a87f3c0a97a785a5106df68a5495b322b6eb3af3025526d77fbe04f6f2af57186588bedac0 +libLLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/da07aba109f41f6fa7e8c8d9da6b3e1d +libLLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/82aa09aa9e2f26cd84d962d02cf239e3845a151ea1a85f770b35e25c2706f269aceaee582fb98f4cc143847ae19eb75853182cc3f30e96a064c07ae126de0666 +libLLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/5579bf880fcd0b8c48dab90b839e5b04 +libLLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/3bbf718caf0e734a266b7930e7a23f65c6ee3ef164c9304c93c6b67066e78f5eef7b1cb7181b2043ed2cf58f1008c4d948330372304260b1f488b3c3a0538eb3 +libLLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/dc25070c072e28204fc8eb8d14086301 +libLLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/8eeb20ab8a173bd0e72bed1578270b4b69d1e179aa0a1b980b7ffd9a683e9a52a611828647cccaa817a7aefbcc794ea0c586613ec7f91774a7433e8bf93fe1a2 +libLLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/152a803e4a3da4b9a9f178a08ff3bf32 +libLLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/7088b49f0662e6f8cdaeb241d1ee3ef9779f7e9ae612f396121d9a7c1dcea7a0aef1c7313621055f255f17b16a50624d5ff288c8f8ce33d87bdf432d9263b246 +libLLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/482c643fbd9865a2c8f494a3888579b7 +libLLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/2e07d0169ec7d3f2cce10f3aad7936ee12e74cd8574700d561cd935751948e8a79bdaa40bc631ace2a485190bc450dae27835f6e9bb7c11a642e562c8f34439d +libLLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/a42b7bf02170b1c69767e4081e43b70d +libLLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/3e23ad9db83b341d04b869f67ed9f9ea3c090d3759175a2f76c614580100d1e08e66609324510696146e776c54dd3bb0f0b1a3cb175631cfd94c90e395da59db +libLLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/5463c05d5da79b3f2fe0b176152e97c6 +libLLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/7d0e4ac637e04c6cd13af590c7dc4a2725541c226d7acc18d0d649c5f5689eb587a4235d6b09cf884837fd8a4d1aa71d23c33fdb1c4e61abae2bed931f8afc97 +libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/f92359aa26292bda057b12cc08c78420 +libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/cff0bef9edcf77511b8a16acb40def5e8667cb36c6e6d2f554ebc38389c93f3ed2746e2cbe6e5cd70596daa9bfcd0729392484c7f6056860fdbe1045521fcc67 +libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/e49088c9b21583546e9985e5fdf66905 +libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/6a5d449519e054ae30068417840dccda2fe03f82d95ec822ee6e22bd691c57ecd7950e4483964c5baed520a911b3c68c80f1240679eeec0b2b6f396d72d71466 +libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/ef83ddeb912459a190cccd257622f28f +libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/6aa4eee7b201b954b21c88de7ee85dfb940c8c07c44cf080dcac5269ab68e39276c42064d574cd5a3f6205494406be906883da1d75d8a1d84a8379387d309986 +libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/2cd11ed664ecd61ba35a59941af4d5c7 +libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/7364be94ec5dcc312fd1a356f05823715af2346435e1638117d9fd33f725508402a77c93eb820be41d85155dd8ba0e81cc803c74c48ace1ae92dbb826cfaa6df +libLLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/0202223794e21f778f99dcaeece42613 +libLLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/ae5cac8c68c68d7a3acd480341e8d6678ad1ddaea40864e252c46f865d64cdd3f2032f7a765fa7cdd498f1b8a5fa8881a10500d497de50b2703a695814ff5604 +libLLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/5eefefcb73961052d706981f62f4257a +libLLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/129da5989609018a7c6960c1fb86268e35809b062efb25d52276b21e99494272bbc55ceb584c7a761e5557d6fc21788340bd50bebef60d2e4007111e6aaae237 +libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/ccc65aa88718939d370f7a2843c0a7ca +libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/6c7dc9da261ae6d0a1a12ce03bb893492a9677f289df6b1b9e40fa35cfbebb5bc31169fe5d7291f893ee74ed7d86899488ea121b0d8b1403e615f104ab7f569d +libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/f0576c099c77c09c5f27e7d3f2723d47 +libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/3a5a6e39e0f8f253de61c9fa0dee1d01cc10d3a17ed583cc2c263e743be3f83f29c5e5d59a11d64da5768159c990c61996358d26576925a7f9fedc460303b511 +libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/73a29eb63f6d834d59776c4d9138475e +libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/0545ac95a2ac4d2f23a200778a13c64c2a80eacde553f5cc38dc90c5de84b3f9d0dbfcd9e3b16cf38c047e9e154c044e0c798850affdf5f917a28d08d3fc5827 +libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/28eea26109c23a3059cd6e4250cb532b +libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/1f42b9a15cdb2e0e0faf33ca134a90e73b61573e951a1efb284623c42111df6b8db9871cb13765cb04290caa05f3c69e80752dbe3df5f94b917d1b424d88f923 +libLLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/188d846f846556d33b6adf48408a41c9 +libLLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/93dfe6af424dd2a2f7ea9e5894c88050d55c6e0b7d8b20ca44793dca36d584a49b3fc4ddb5183881b69e86285b8baa93a6f0cf1e3de54fcb283f6f18a421277c +libLLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/bec621675c0d62e1e5884289e3e84b69 +libLLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/f7b20e8cc0534c59f3b7cb422df545ee963736c9fcc2941ae14294bc5bbf4adbb13ca72622518ada4fb5871b67fb2c313c4532eb17046bce9b9fe8458cac4ce8 +libLLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/f204eb9d4f696cb5e7e85e539d1a2d1a +libLLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/df9697817a96e1b6bb7d69413969990cd2203aead52eaad3f576f57702d3a657e10ffd531a68b0995642f9cb3fa6961c297659351501e3a163e6cf228d4234d2 +libLLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/cba1e24a29a5e490ded6eab85383c6b1 +libLLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/63319d17c6ef3313219eca4f46dc7d879c955a7e4ce5b56896f7f4b230b657718829e3f892433818493457850a2a3573fdde2298b290932bf1d0c34923f99339 +libLLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/6355fcef3bfddb656c5ec91f755ddb0f +libLLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/4a1ddb2230cd62fcff69e17e223f5c8b0a191ebacae1bbf262c159381749522a1efafde0a57663ed659b0e53b6c1a9032a14342b239f95e8ae007a619dfade62 +libLLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/070a6bb4575b60e5a14d959ce34556d1 +libLLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/0a47fd12c936b7cf3a9a97f2127627a44c2f577e2fb5d8bcb2b96e3d2d78a602770966a37c733b1a1bf663e37a15fe1743e0d683111d7b1fdb7dfc4510027827 +libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/605116c3315105515acb70c9b3ecf9f7 +libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/3fb6762a8215f230a63d100feb882efd08e668dc47b5b4cca1c9565b0926b4920f5f671fc5db903d6fc3b6c445b00d2822d179ee999c614ae22ebff7a2d73659 +libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/8921804e0461aeeaf1e6a484c5b392a7 +libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/af9510843a9083e363e67bc1b1fed6eca4d594c34d6a9fb8f920dff5b726dbee376f33dafaf040989e83aaced066d35f3fd90b89f4c3e0e6a1f3a11a471ad8a7 +libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/2bbad5e9373fc2354b9e0878663169a9 +libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/adfa1edc0a4138d977fde832aaa6549b5ee38a1c0bb3b59dd9c05740569bd108c2b2b2de4e81ac06d367c9f834662fa5238972affee8bc638309e4470cd980f1 +libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/605ba226b4d0d82802590eadf31d50ce +libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/992dd8cf723b986d506743f2ea345391752893b07fc0be35129afbeb4cd01d41f32c56a99b0f6a25b51787bee9a56c60ce66fce60123e8fd3fe0fa11ba051b3d llvmunwind-12.0.1.tar.xz/md5/4ec327cee517fdb1f6a20e83748e2c7b llvmunwind-12.0.1.tar.xz/sha512/847b6ba03010a43f4fdbfdc49bf16d18fd18474d01584712e651b11191814bf7c1cf53475021d9ee447ed78413202b4ed97973d7bdd851d3e49f8d06f55a7af4 diff --git a/deps/clang.version b/deps/clang.version index 2fa84f679cb19..d10ae4340ce6c 100644 --- a/deps/clang.version +++ b/deps/clang.version @@ -1,4 +1,4 @@ ## jll artifact # Clang (paired with LLVM, only here as a JLL download) CLANG_JLL_NAME := Clang -CLANG_JLL_VER := 14.0.6+0 +CLANG_JLL_VER := 14.0.6+2 diff --git a/deps/lld.version b/deps/lld.version index 2b34a5d3012ad..d52ceb34552e2 100644 --- a/deps/lld.version +++ b/deps/lld.version @@ -1,3 +1,3 @@ ## jll artifact LLD_JLL_NAME := LLD -LLD_JLL_VER := 14.0.6+0 +LLD_JLL_VER := 14.0.6+2 diff --git a/deps/llvm-tools.version b/deps/llvm-tools.version index 5da312d32f0af..236c76ca407ab 100644 --- a/deps/llvm-tools.version +++ b/deps/llvm-tools.version @@ -1,5 +1,5 @@ ## jll artifact # LLVM_tools (downloads LLVM_jll to get things like `lit` and `opt`) LLVM_TOOLS_JLL_NAME := LLVM -LLVM_TOOLS_JLL_VER := 14.0.6+0 -LLVM_TOOLS_ASSERT_JLL_VER := 14.0.6+0 +LLVM_TOOLS_JLL_VER := 14.0.6+2 +LLVM_TOOLS_ASSERT_JLL_VER := 14.0.6+2 diff --git a/deps/llvm.version b/deps/llvm.version index 2dbcd0f510f81..64ed012bc9989 100644 --- a/deps/llvm.version +++ b/deps/llvm.version @@ -4,5 +4,5 @@ LLVM_ASSERT_JLL_VER := 14.0.5+3 ## source build LLVM_VER := 14.0.5 -LLVM_BRANCH=julia-14.0.6-0 -LLVM_SHA1=julia-14.0.6-0 +LLVM_BRANCH=julia-14.0.6-2 +LLVM_SHA1=julia-14.0.6-2 diff --git a/stdlib/libLLVM_jll/Project.toml b/stdlib/libLLVM_jll/Project.toml index 8332d68102f8e..119eb8755424d 100644 --- a/stdlib/libLLVM_jll/Project.toml +++ b/stdlib/libLLVM_jll/Project.toml @@ -1,6 +1,6 @@ name = "libLLVM_jll" uuid = "8f36deef-c2a5-5394-99ed-8e07531fb29a" -version = "14.0.6+0" +version = "14.0.6+2" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" From 8b4b1f03149904d399d1887ffe3c2b3b60375c9c Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 8 Feb 2023 19:59:39 +0100 Subject: [PATCH 059/775] More helpful error messages for calling log/sqrt with negative real arguments (#48347) * More helpful error messages for log/sqrt with negative real arguments --- base/docs/basedocs.jl | 2 +- base/math.jl | 20 +++++++++++-------- base/mpfr.jl | 6 ++++-- base/special/log.jl | 4 ++-- .../manual/complex-and-rational-numbers.md | 2 +- doc/src/manual/control-flow.md | 4 ++-- doc/src/manual/faq.md | 2 +- stdlib/Distributed/src/remotecall.jl | 2 +- stdlib/Distributed/test/distributed_exec.jl | 2 +- stdlib/Test/src/Test.jl | 2 +- 10 files changed, 26 insertions(+), 20 deletions(-) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 956dc9987e2d8..c6e0f867723a4 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -1724,7 +1724,7 @@ The argument `val` to a function or constructor is outside the valid domain. ```jldoctest julia> sqrt(-1) ERROR: DomainError with -1.0: -sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). +sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). Stacktrace: [...] ``` diff --git a/base/math.jl b/base/math.jl index 0faa6a00c3f53..27a66c36fe884 100644 --- a/base/math.jl +++ b/base/math.jl @@ -31,7 +31,11 @@ using .Base: IEEEFloat @noinline function throw_complex_domainerror(f::Symbol, x) throw(DomainError(x, - LazyString(f," will only return a complex result if called with a complex argument. Try ", f,"(Complex(x))."))) + LazyString(f," was called with a negative real argument but will only return a complex result if called with a complex argument. Try ", f,"(Complex(x))."))) +end +@noinline function throw_complex_domainerror_neg1(f::Symbol, x) + throw(DomainError(x, + LazyString(f," was called with a real argument < -1 but will only return a complex result if called with a complex argument. Try ", f,"(Complex(x))."))) end @noinline function throw_exp_domainerror(x) throw(DomainError(x, LazyString( @@ -358,14 +362,14 @@ julia> log(4,2) julia> log(-2, 3) ERROR: DomainError with -2.0: -log will only return a complex result if called with a complex argument. Try log(Complex(x)). +log was called with a negative real argument but will only return a complex result if called with a complex argument. Try log(Complex(x)). Stacktrace: [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 [...] julia> log(2, -3) ERROR: DomainError with -3.0: -log will only return a complex result if called with a complex argument. Try log(Complex(x)). +log was called with a negative real argument but will only return a complex result if called with a complex argument. Try log(Complex(x)). Stacktrace: [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 [...] @@ -579,7 +583,7 @@ julia> log(2) julia> log(-3) ERROR: DomainError with -3.0: -log will only return a complex result if called with a complex argument. Try log(Complex(x)). +log was called with a negative real argument but will only return a complex result if called with a complex argument. Try log(Complex(x)). Stacktrace: [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 [...] @@ -611,7 +615,7 @@ julia> log2(10) julia> log2(-2) ERROR: DomainError with -2.0: -log2 will only return a complex result if called with a complex argument. Try log2(Complex(x)). +log2 was called with a negative real argument but will only return a complex result if called with a complex argument. Try log2(Complex(x)). Stacktrace: [1] throw_complex_domainerror(f::Symbol, x::Float64) at ./math.jl:31 [...] @@ -641,7 +645,7 @@ julia> log10(2) julia> log10(-2) ERROR: DomainError with -2.0: -log10 will only return a complex result if called with a complex argument. Try log10(Complex(x)). +log10 was called with a negative real argument but will only return a complex result if called with a complex argument. Try log10(Complex(x)). Stacktrace: [1] throw_complex_domainerror(f::Symbol, x::Float64) at ./math.jl:31 [...] @@ -665,7 +669,7 @@ julia> log1p(0) julia> log1p(-2) ERROR: DomainError with -2.0: -log1p will only return a complex result if called with a complex argument. Try log1p(Complex(x)). +log1p was called with a real argument < -1 but will only return a complex result if called with a complex argument. Try log1p(Complex(x)). Stacktrace: [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 [...] @@ -737,7 +741,7 @@ julia> hypot(a, a) julia> √(a^2 + a^2) # a^2 overflows ERROR: DomainError with -2.914184810805068e18: -sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). +sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). Stacktrace: [...] diff --git a/base/mpfr.jl b/base/mpfr.jl index 4485265b580de..601d17490a77c 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -722,7 +722,8 @@ end for f in (:log, :log2, :log10) @eval function $f(x::BigFloat) if x < 0 - throw(DomainError(x, string($f, " will only return a complex result if called ", + throw(DomainError(x, string($f, " was called with a negative real argument but ", + "will only return a complex result if called ", "with a complex argument. Try ", $f, "(complex(x))."))) end z = BigFloat() @@ -733,7 +734,8 @@ end function log1p(x::BigFloat) if x < -1 - throw(DomainError(x, string("log1p will only return a complex result if called ", + throw(DomainError(x, string("log1p was called with a real argument < -1 but ", + "will only return a complex result if called ", "with a complex argument. Try log1p(complex(x))."))) end z = BigFloat() diff --git a/base/special/log.jl b/base/special/log.jl index 5e20cdbaa06a6..5d7f1c8118724 100644 --- a/base/special/log.jl +++ b/base/special/log.jl @@ -367,7 +367,7 @@ function log1p(x::Float64) elseif isnan(x) NaN else - throw_complex_domainerror(:log1p, x) + throw_complex_domainerror_neg1(:log1p, x) end end @@ -399,7 +399,7 @@ function log1p(x::Float32) elseif isnan(x) NaN32 else - throw_complex_domainerror(:log1p, x) + throw_complex_domainerror_neg1(:log1p, x) end end diff --git a/doc/src/manual/complex-and-rational-numbers.md b/doc/src/manual/complex-and-rational-numbers.md index ca6c241ca0583..9cab2ed1e4f24 100644 --- a/doc/src/manual/complex-and-rational-numbers.md +++ b/doc/src/manual/complex-and-rational-numbers.md @@ -140,7 +140,7 @@ when applied to `-1` versus `-1 + 0im` even though `-1 == -1 + 0im`: ```jldoctest julia> sqrt(-1) ERROR: DomainError with -1.0: -sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). +sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). Stacktrace: [...] diff --git a/doc/src/manual/control-flow.md b/doc/src/manual/control-flow.md index ba78a8c5b1670..5d12530892b1e 100644 --- a/doc/src/manual/control-flow.md +++ b/doc/src/manual/control-flow.md @@ -623,7 +623,7 @@ real value: ```jldoctest julia> sqrt(-1) ERROR: DomainError with -1.0: -sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). +sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). Stacktrace: [...] ``` @@ -797,7 +797,7 @@ julia> sqrt_second(9) julia> sqrt_second(-9) ERROR: DomainError with -9.0: -sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). +sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). Stacktrace: [...] ``` diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index 741843bca33e5..0ac53a0233402 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -402,7 +402,7 @@ Certain operations make mathematical sense but result in errors: ```jldoctest julia> sqrt(-2.0) ERROR: DomainError with -2.0: -sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). +sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). Stacktrace: [...] ``` diff --git a/stdlib/Distributed/src/remotecall.jl b/stdlib/Distributed/src/remotecall.jl index 4c94a18cc08cd..0b1143d855510 100644 --- a/stdlib/Distributed/src/remotecall.jl +++ b/stdlib/Distributed/src/remotecall.jl @@ -485,7 +485,7 @@ julia> remotecall_fetch(sqrt, 2, 4) julia> remotecall_fetch(sqrt, 2, -4) ERROR: On worker 2: DomainError with -4.0: -sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). +sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). ... ``` """ diff --git a/stdlib/Distributed/test/distributed_exec.jl b/stdlib/Distributed/test/distributed_exec.jl index b911f2778e535..548ac73d2fb4c 100644 --- a/stdlib/Distributed/test/distributed_exec.jl +++ b/stdlib/Distributed/test/distributed_exec.jl @@ -1453,7 +1453,7 @@ let thrown = false thrown = true local b = IOBuffer() showerror(b, e) - @test occursin("sqrt will only return", String(take!(b))) + @test occursin("sqrt was called with a negative real argument", String(take!(b))) end @test thrown end diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index e1e6c5172a4e5..1c5f84303dd6d 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -713,7 +713,7 @@ Test Passed julia> @test_throws "Try sqrt(Complex" sqrt(-1) Test Passed - Message: "DomainError with -1.0:\\nsqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x))." + Message: "DomainError with -1.0:\\nsqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x))." ``` In the final example, instead of matching a single string it could alternatively have been performed with: From ee82caa02b8753e0030675d9a8787cdc22bf8f20 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 8 Feb 2023 19:23:22 -0300 Subject: [PATCH 060/775] Add Float16 to supported x86 processors (#46499) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add float16 multiversioning for x86 Co-authored-by: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Co-authored-by: Mosè Giordano --- src/features_x86.h | 2 ++ src/llvm-demote-float16.cpp | 17 ++----------- src/llvm-multiversioning.cpp | 13 ++++------ src/processor_x86.cpp | 18 ++++++++++---- test/llvmpasses/float16.ll | 46 +++++++++++++++++++++++++++++++++--- 5 files changed, 66 insertions(+), 30 deletions(-) diff --git a/src/features_x86.h b/src/features_x86.h index acacaa68751d3..08f979df546b7 100644 --- a/src/features_x86.h +++ b/src/features_x86.h @@ -74,11 +74,13 @@ JL_FEATURE_DEF(enqcmd, 32 * 3 + 29, 0) // EAX=7,ECX=0: EDX // JL_FEATURE_DEF(avx5124vnniw, 32 * 4 + 2, ?????) // JL_FEATURE_DEF(avx5124fmaps, 32 * 4 + 3, ?????) +JL_FEATURE_DEF(uintr, 32 * 4 + 5, 140000) JL_FEATURE_DEF(avx512vp2intersect, 32 * 4 + 8, 0) JL_FEATURE_DEF(serialize, 32 * 4 + 14, 110000) JL_FEATURE_DEF(tsxldtrk, 32 * 4 + 16, 110000) JL_FEATURE_DEF(pconfig, 32 * 4 + 18, 0) JL_FEATURE_DEF_NAME(amx_bf16, 32 * 4 + 22, 110000, "amx-bf16") +JL_FEATURE_DEF(avx512fp16, 32 * 4 + 23, 140000) JL_FEATURE_DEF_NAME(amx_tile, 32 * 4 + 24, 110000, "amx-tile") JL_FEATURE_DEF_NAME(amx_int8, 32 * 4 + 25, 110000, "amx-int8") diff --git a/src/llvm-demote-float16.cpp b/src/llvm-demote-float16.cpp index 57ec30ca57947..3d9f0664b2001 100644 --- a/src/llvm-demote-float16.cpp +++ b/src/llvm-demote-float16.cpp @@ -47,23 +47,9 @@ INST_STATISTIC(FCmp); extern JuliaOJIT *jl_ExecutionEngine; -Optional always_have_fp16() { -#if defined(_CPU_X86_) || defined(_CPU_X86_64_) - // x86 doesn't support fp16 - // TODO: update for sapphire rapids when it comes out - return false; -#else - return {}; -#endif -} - namespace { bool have_fp16(Function &caller) { - auto unconditional = always_have_fp16(); - if (unconditional.hasValue()) - return unconditional.getValue(); - Attribute FSAttr = caller.getFnAttribute("target-features"); StringRef FS = FSAttr.isValid() ? FSAttr.getValueAsString() : jl_ExecutionEngine->getTargetFeatureString(); @@ -71,11 +57,12 @@ bool have_fp16(Function &caller) { if (FS.find("+fp16fml") != llvm::StringRef::npos || FS.find("+fullfp16") != llvm::StringRef::npos){ return true; } -#else +#elif defined(_CPU_X86_64_) if (FS.find("+avx512fp16") != llvm::StringRef::npos){ return true; } #endif + (void)FS; return false; } diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index bb1f6590a3207..242b0c454ad0a 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -45,8 +45,6 @@ using namespace llvm; extern Optional always_have_fma(Function&); -extern Optional always_have_fp16(); - void replaceUsesWithLoad(Function &F, function_ref should_replace, MDNode *tbaa_const); namespace { @@ -490,13 +488,12 @@ uint32_t CloneCtx::collect_func_info(Function &F) flag |= JL_TARGET_CLONE_MATH; } } - if(!always_have_fp16().hasValue()){ - for (size_t i = 0; i < I.getNumOperands(); i++) { - if(I.getOperand(i)->getType()->isHalfTy()){ - flag |= JL_TARGET_CLONE_FLOAT16; - } - // Check for BFloat16 when they are added to julia can be done here + + for (size_t i = 0; i < I.getNumOperands(); i++) { + if(I.getOperand(i)->getType()->isHalfTy()){ + flag |= JL_TARGET_CLONE_FLOAT16; } + // Check for BFloat16 when they are added to julia can be done here } if (has_veccall && (flag & JL_TARGET_CLONE_SIMD) && (flag & JL_TARGET_CLONE_MATH) && (flag & JL_TARGET_CLONE_CPU) && (flag & JL_TARGET_CLONE_FLOAT16)) { diff --git a/src/processor_x86.cpp b/src/processor_x86.cpp index c61712ada787a..6b3e7d5b63678 100644 --- a/src/processor_x86.cpp +++ b/src/processor_x86.cpp @@ -154,6 +154,9 @@ static constexpr FeatureDep deps[] = { {avx512vnni, avx512f}, {avx512vp2intersect, avx512f}, {avx512vpopcntdq, avx512f}, + {avx512fp16, avx512bw}, + {avx512fp16, avx512dq}, + {avx512fp16, avx512vl}, {amx_int8, amx_tile}, {amx_bf16, amx_tile}, {sse4a, sse3}, @@ -208,8 +211,8 @@ constexpr auto tigerlake = icelake | get_feature_masks(avx512vp2intersect, movdi constexpr auto alderlake = skylake | get_feature_masks(clwb, sha, waitpkg, shstk, gfni, vaes, vpclmulqdq, pconfig, rdpid, movdiri, pku, movdir64b, serialize, ptwrite, avxvnni); constexpr auto sapphirerapids = icelake_server | - get_feature_masks(amx_tile, amx_int8, amx_bf16, avx512bf16, serialize, cldemote, waitpkg, - ptwrite, tsxldtrk, enqcmd, shstk, avx512vp2intersect, movdiri, movdir64b); + get_feature_masks(amx_tile, amx_int8, amx_bf16, avx512bf16, avx512fp16, serialize, cldemote, waitpkg, + avxvnni, uintr, ptwrite, tsxldtrk, enqcmd, shstk, avx512vp2intersect, movdiri, movdir64b); constexpr auto k8_sse3 = get_feature_masks(sse3, cx16); constexpr auto amdfam10 = k8_sse3 | get_feature_masks(sse4a, lzcnt, popcnt, sahf); @@ -930,10 +933,10 @@ static void ensure_jit_target(bool imaging) Feature::avx512pf, Feature::avx512er, Feature::avx512cd, Feature::avx512bw, Feature::avx512vl, Feature::avx512vbmi, - Feature::avx512vpopcntdq, + Feature::avx512vpopcntdq, Feature::avxvnni, Feature::avx512vbmi2, Feature::avx512vnni, Feature::avx512bitalg, Feature::avx512bf16, - Feature::avx512vp2intersect}; + 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; @@ -946,6 +949,13 @@ static void ensure_jit_target(bool imaging) 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; + } + } } } diff --git a/test/llvmpasses/float16.ll b/test/llvmpasses/float16.ll index 14bae9ff8a8f1..668c6ff3dd261 100644 --- a/test/llvmpasses/float16.ll +++ b/test/llvmpasses/float16.ll @@ -1,8 +1,9 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: -p -; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -DemoteFloat16 -S %s | FileCheck %s -; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='DemoteFloat16' -S %s | FileCheck %s +; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -DemoteFloat16 -S %s | FileCheck %s +; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='DemoteFloat16' -S %s | FileCheck %s -define half @demotehalf_test(half %a, half %b) { +define half @demotehalf_test(half %a, half %b) #0 { +top: ; CHECK-LABEL: @demotehalf_test( ; CHECK-NEXT: top: ; CHECK-NEXT: %0 = fpext half %a to float @@ -44,6 +45,42 @@ define half @demotehalf_test(half %a, half %b) { ; CHECK-NEXT: %36 = fadd float %34, %35 ; CHECK-NEXT: %37 = fptrunc float %36 to half ; CHECK-NEXT: ret half %37 +; + %0 = fadd half %a, %b + %1 = fadd half %0, %b + %2 = fadd half %1, %b + %3 = fmul half %2, %b + %4 = fdiv half %3, %b + %5 = insertelement <2 x half> undef, half %a, i32 0 + %6 = insertelement <2 x half> %5, half %b, i32 1 + %7 = insertelement <2 x half> undef, half %b, i32 0 + %8 = insertelement <2 x half> %7, half %b, i32 1 + %9 = fadd <2 x half> %6, %8 + %10 = extractelement <2 x half> %9, i32 0 + %11 = extractelement <2 x half> %9, i32 1 + %12 = fadd half %10, %11 + %13 = fadd half %12, %4 + ret half %13 +} + +define half @native_half_test(half %a, half %b) #1 { +; CHECK-LABEL: @native_half_test( +; CHECK-NEXT top: +; CHECK-NEXT %0 = fadd half %a, %b +; CHECK-NEXT %1 = fadd half %0, %b +; CHECK-NEXT %2 = fadd half %1, %b +; CHECK-NEXT %3 = fmul half %2, %b +; CHECK-NEXT %4 = fdiv half %3, %b +; CHECK-NEXT %5 = insertelement <2 x half> undef, half %a, i32 0 +; CHECK-NEXT %6 = insertelement <2 x half> %5, half %b, i32 1 +; CHECK-NEXT %7 = insertelement <2 x half> undef, half %b, i32 0 +; CHECK-NEXT %8 = insertelement <2 x half> %7, half %b, i32 1 +; CHECK-NEXT %9 = fadd <2 x half> %6, %8 +; CHECK-NEXT %10 = extractelement <2 x half> %9, i32 0 +; CHECK-NEXT %11 = extractelement <2 x half> %9, i32 1 +; CHECK-NEXT %12 = fadd half %10, %11 +; CHECK-NEXT %13 = fadd half %12, %4 +; CHECK-NEXT ret half %13 ; top: %0 = fadd half %a, %b @@ -62,3 +99,6 @@ top: %13 = fadd half %12, %4 ret half %13 } + +attributes #0 = { "target-features"="-avx512fp16" } +attributes #1 = { "target-features"="+avx512fp16" } From 578c4320d1321024142960feabf7199dece02ca2 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 8 Feb 2023 17:23:48 -0500 Subject: [PATCH 061/775] remove `REPL.__init__` (#48594) --- base/docs/Docs.jl | 3 ++- base/loading.jl | 5 +++++ stdlib/REPL/src/REPL.jl | 4 ---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 61b5786298475..e0d21715c2147 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -517,11 +517,12 @@ function docm(source::LineNumberNode, mod::Module, ex) @nospecialize ex if isexpr(ex, :->) && length(ex.args) > 1 return docm(source, mod, ex.args...) - else + elseif isassigned(Base.REPL_MODULE_REF) # TODO: this is a shim to continue to allow `@doc` for looking up docstrings REPL = Base.REPL_MODULE_REF[] return REPL.lookup_doc(ex) end + return nothing end # Drop incorrect line numbers produced by nested macro calls. docm(source::LineNumberNode, mod::Module, _, _, x...) = docm(source, mod, x...) diff --git a/base/loading.jl b/base/loading.jl index 7ad877153e45d..41998bbf694a3 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1637,6 +1637,8 @@ end require(uuidkey::PkgId) = @lock require_lock _require_prelocked(uuidkey) +const REPL_PKGID = PkgId(UUID("3fa0cd96-eef1-5676-8a61-b3b8758bbffb"), "REPL") + function _require_prelocked(uuidkey::PkgId, env=nothing) assert_havelock(require_lock) if !root_module_exists(uuidkey) @@ -1648,6 +1650,9 @@ function _require_prelocked(uuidkey::PkgId, env=nothing) insert_extension_triggers(uuidkey) # After successfully loading, notify downstream consumers run_package_callbacks(uuidkey) + if uuidkey == REPL_PKGID + REPL_MODULE_REF[] = newm + end else newm = root_module(uuidkey) end diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index b2eb8cf63c8da..438dd75371b12 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -71,10 +71,6 @@ include("docview.jl") @nospecialize # use only declared type signatures -function __init__() - Base.REPL_MODULE_REF[] = REPL -end - answer_color(::AbstractREPL) = "" const JULIA_PROMPT = "julia> " From 4b1977d5ff943a41005afca20b5a14d31d04c056 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Thu, 9 Feb 2023 06:16:08 -0500 Subject: [PATCH 062/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?SparseArrays=20stdlib=20from=204eaa458=20to=20c4eeb2f=20(#48602?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dilum Aluthge --- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + stdlib/SparseArrays.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/SparseArrays-4eaa4582569a76c3199849d8194582d948b7a70f.tar.gz/md5 delete mode 100644 deps/checksums/SparseArrays-4eaa4582569a76c3199849d8194582d948b7a70f.tar.gz/sha512 create mode 100644 deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/md5 create mode 100644 deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/sha512 diff --git a/deps/checksums/SparseArrays-4eaa4582569a76c3199849d8194582d948b7a70f.tar.gz/md5 b/deps/checksums/SparseArrays-4eaa4582569a76c3199849d8194582d948b7a70f.tar.gz/md5 deleted file mode 100644 index 9d5d598bbac18..0000000000000 --- a/deps/checksums/SparseArrays-4eaa4582569a76c3199849d8194582d948b7a70f.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -c203f4a174f2ec017f3d11dab55d7b6c diff --git a/deps/checksums/SparseArrays-4eaa4582569a76c3199849d8194582d948b7a70f.tar.gz/sha512 b/deps/checksums/SparseArrays-4eaa4582569a76c3199849d8194582d948b7a70f.tar.gz/sha512 deleted file mode 100644 index 14ed393bd5aea..0000000000000 --- a/deps/checksums/SparseArrays-4eaa4582569a76c3199849d8194582d948b7a70f.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -fb3372f4ab06ad376509f5992b54571249ff21cf4bcfaba9f9c629e89d09eed4da8ffb6f0053f650904a39c591f4e9f602d365d93a466dc3d34a2c41db071049 diff --git a/deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/md5 b/deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/md5 new file mode 100644 index 0000000000000..4cba54175c3fd --- /dev/null +++ b/deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/md5 @@ -0,0 +1 @@ +9cdb1adf09239aaa1717825c1e7b14db diff --git a/deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/sha512 b/deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/sha512 new file mode 100644 index 0000000000000..922de677bede5 --- /dev/null +++ b/deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/sha512 @@ -0,0 +1 @@ +489163ca058520b067cd78aeb8338212db7cf64f3c15f96b70ca2eb81de31afb192744788488abfda04b185d93841d1014f10556079604f9e7fae97b3b308181 diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version index 347369ea45bc4..6b47310f52811 100644 --- a/stdlib/SparseArrays.version +++ b/stdlib/SparseArrays.version @@ -1,4 +1,4 @@ SPARSEARRAYS_BRANCH = main -SPARSEARRAYS_SHA1 = 4eaa4582569a76c3199849d8194582d948b7a70f +SPARSEARRAYS_SHA1 = c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f SPARSEARRAYS_GIT_URL := https://github.com/JuliaSparse/SparseArrays.jl.git SPARSEARRAYS_TAR_URL = https://api.github.com/repos/JuliaSparse/SparseArrays.jl/tarball/$1 From ed80b5edd51c19d409c28ec9dff02a72c7d30826 Mon Sep 17 00:00:00 2001 From: Ujjwal Sarswat <76774914+vmpyr@users.noreply.github.com> Date: Thu, 9 Feb 2023 18:22:21 +0530 Subject: [PATCH 063/775] Increase test coverage of typemax/typemin in `int.jl` (#48587) --- test/int.jl | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/int.jl b/test/int.jl index 3bfa6adc99301..07438dc8bc8b9 100644 --- a/test/int.jl +++ b/test/int.jl @@ -300,6 +300,29 @@ end end end +@testset "typemin typemax" begin + @test typemin(Int8 ) === Int8(-128) + @test typemax(Int8 ) === Int8(127) + @test typemin(UInt8 ) === UInt8(0) + @test typemax(UInt8 ) === UInt8(255) + @test typemin(Int16 ) === Int16(-32768) + @test typemax(Int16 ) === Int16(32767) + @test typemin(UInt16 ) === UInt16(0) + @test typemax(UInt16 ) === UInt16(65535) + @test typemin(Int32 ) === Int32(-2147483648) + @test typemax(Int32 ) === Int32(2147483647) + @test typemin(UInt32 ) === UInt32(0) + @test typemax(UInt32 ) === UInt32(4294967295) + @test typemin(Int64 ) === Int64(-9223372036854775808) + @test typemax(Int64 ) === Int64(9223372036854775807) + @test typemin(UInt64 ) === UInt64(0) + @test typemax(UInt64 ) === UInt64(0xffff_ffff_ffff_ffff) + @test typemin(UInt128) === UInt128(0) + @test typemax(UInt128) === UInt128(0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff) + @test typemin(Int128 ) === Int128(-170141183460469231731687303715884105728) + @test typemax(Int128 ) === Int128(170141183460469231731687303715884105727) +end + @testset "issue #15489" begin @test 0x00007ffea27edaa0 + (-40) === (-40) + 0x00007ffea27edaa0 === 0x00007ffea27eda78 @test UInt64(1) * Int64(-1) === typemax(UInt64) From 7caa49903893750d71df0984aac1a384a7320d77 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 9 Feb 2023 14:41:23 +0100 Subject: [PATCH 064/775] Add more examples to JULIA_LLVM_ARGS section (#48604) --- doc/src/devdocs/llvm.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/src/devdocs/llvm.md b/doc/src/devdocs/llvm.md index 6c6b33c2281dc..93b241d703714 100644 --- a/doc/src/devdocs/llvm.md +++ b/doc/src/devdocs/llvm.md @@ -82,6 +82,8 @@ Here are example settings using `bash` syntax: * `export JULIA_LLVM_ARGS=-debug-only=loop-vectorize` dumps LLVM `DEBUG(...)` diagnostics for loop vectorizer. If you get warnings about "Unknown command line argument", rebuild LLVM with `LLVM_ASSERTIONS = 1`. + * `export JULIA_LLVM_ARGS=-help` shows a list of available options. + * `export JULIA_LLVM_ARGS="-fatal-warnings -print-options"` is an example how to use multiple options. ## Debugging LLVM transformations in isolation From 2ab7b6a8472c948097bfef080b724698525ba3ab Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 9 Feb 2023 23:40:10 +0800 Subject: [PATCH 065/775] Avoid split `y::Union` when comparing with a forall var. If `y` has no free typevar. Then `x <: y` wont cause any env change. Thus there's no need to split `y`. --- src/subtype.c | 4 +++- test/subtype.jl | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/subtype.c b/src/subtype.c index 19a3e3286982b..c3b48df783be6 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1254,7 +1254,9 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) // of unions and vars: if matching `typevar <: union`, first try to match the whole // union against the variable before trying to take it apart to see if there are any // variables lurking inside. - ui = pick_union_decision(e, 1); + // note: for forall var, there's no need to split y if it has no free typevars. + jl_varbinding_t *xx = lookup(e, (jl_tvar_t *)x); + ui = ((xx && xx->right) || jl_has_free_typevars(y)) && pick_union_decision(e, 1); } if (ui == 1) y = pick_union_element(y, e, 1); diff --git a/test/subtype.jl b/test/subtype.jl index c0fc41abda174..ca00947eb3b46 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2443,3 +2443,7 @@ end let a = (isodd(i) ? Pair{Char, String} : Pair{String, String} for i in 1:2000) @test Tuple{Type{Pair{Union{Char, String}, String}}, a...} <: Tuple{Type{Pair{K, V}}, Vararg{Pair{A, B} where B where A}} where V where K end + +#issue 48582 +@test !<:(Tuple{Pair{<:T,<:T}, Val{S} where {S}} where {T<:Base.BitInteger}, + Tuple{Pair{<:T,<:T}, Val{Int}} where {T<:Base.BitInteger}) From ec229a495c353a50a624fb22f610f6d40fbc0104 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Thu, 9 Feb 2023 08:49:12 -0800 Subject: [PATCH 066/775] Fix bounds on floating point range testing (#48561) * Fix bounds on floating point range testing Follow up on #48465. Addresses CI failure noted here https://github.com/JuliaLang/julia/pull/48465#issuecomment-1419261473. * Use simpler error bound --- test/ranges.jl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/ranges.jl b/test/ranges.jl index bef600338a61d..bbdd303adb3c5 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -887,7 +887,15 @@ function range_fuzztests(::Type{T}, niter, nrange) where {T} @test m == length(r) @test strt == first(r) @test Δ == step(r) - @test stop ≈ last(r) atol = eps((n-1)*Δ) + eps(stop) # account for intermediate rounding in computation of stop + # potential floating point error: + # stop = strt + (n-1)*Δ + # * error <= eps((n-1)*Δ)/2 <= abs((n-1)*Δ)/2 * eps(T) + # + error <= eps(stop)/2 <= abs(stop)/2 * eps(T) + # last(r) + # rat(strt) error <= eps(strt)/2 <= abs(strt)/2 * eps(T) + # rat(Δ) error <= (n-1)*eps(Δ)/2 <= abs((n-1)*Δ)/2 * eps(T) + # T(...) error <= eps(last(r))/2 <= abs(stop)/2 * eps(T) + @test stop ≈ last(r) atol = (abs(strt)/2 + (n-1)*abs(Δ) + abs(stop)) * eps(T) l = range(strt, stop=stop, length=n) @test n == length(l) @test strt == first(l) From d72a9a1d2a2bd181cabd5294f21ef3242dd09efd Mon Sep 17 00:00:00 2001 From: Bob Cassels Date: Thu, 9 Feb 2023 12:01:50 -0500 Subject: [PATCH 067/775] Note tanpi in NEWS.md; add compat annotation to doc string. (#48606) --- NEWS.md | 2 +- base/special/trig.jl | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 1e9f86ebb14a4..e875921f827fd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -26,7 +26,7 @@ Build system changes New library functions --------------------- - +* `tanpi` is now defined. It computes tan(πx) more accurately than `tan(pi*x)` ([#48575]). New library features -------------------- diff --git a/base/special/trig.jl b/base/special/trig.jl index ed92f83bb52e2..6dae3ed351503 100644 --- a/base/special/trig.jl +++ b/base/special/trig.jl @@ -890,6 +890,9 @@ end Compute ``\\tan(\\pi x)`` more accurately than `tan(pi*x)`, especially for large `x`. +!!! compat "Julia 1.10" + This function requires at least Julia 1.10. + See also [`tand`](@ref), [`sinpi`](@ref), [`cospi`](@ref), [`sincospi`](@ref). """ From 642216b54fc7c028a64de8699fa4f3a53172fdf7 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 9 Feb 2023 15:12:54 -0500 Subject: [PATCH 068/775] fix error message for passing an argument to a cmdline option that doesn't accept one (#48597) --- src/jloptions.c | 11 +++++++---- test/cmdlineargs.jl | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/jloptions.c b/src/jloptions.c index 90bb39955ee42..7f41aeefd1195 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -351,11 +351,14 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) c = o->val; goto restart_switch; } - else if (o->val <= 0xff && strchr(shortopts, o->val)) { - jl_errorf("option `-%c/--%s` is missing an argument", o->val, o->name); - } else { - jl_errorf("option `--%s` is missing an argument", o->name); + const char *problem = o->has_arg ? "is missing an argument" : "does not accept an argument"; + if (o->val <= 0xff && strchr(shortopts, o->val)) { + jl_errorf("option `-%c/--%s` %s", o->val, o->name, problem); + } + else { + jl_errorf("option `--%s` %s", o->name, problem); + } } } } diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 6740b621875c6..0917f4c4257f3 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -697,6 +697,8 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` (false, "", "ERROR: option `--inline` is missing an argument") @test readchomperrors(`$exename --startup-file=no -e "@show ARGS" -now -- julia RUN.jl`) == (false, "", "ERROR: unknown option `-n`") + @test readchomperrors(`$exename --interactive=yes`) == + (false, "", "ERROR: option `-i/--interactive` does not accept an argument") # --compiled-modules={yes|no} @test readchomp(`$exename -E "Bool(Base.JLOptions().use_compiled_modules)"`) == "true" From 8e19fbbfdb3b59de552ccc4d29534e6c0bab26ac Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 10 Feb 2023 09:06:14 +0900 Subject: [PATCH 069/775] avoid using keyword argument for `InferenceResult` (#48605) --- base/compiler/abstractinterpretation.jl | 2 +- base/compiler/typeinfer.jl | 10 +++++----- base/compiler/types.jl | 13 +++++-------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 1bc2af92699be..ddfac03275fb1 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1045,7 +1045,7 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, return nothing end argtypes = has_conditional(𝕃ᵢ) ? ConditionalArgtypes(arginfo, sv) : SimpleArgtypes(arginfo.argtypes) - inf_result = InferenceResult(mi, argtypes) + inf_result = InferenceResult(mi, argtypes, typeinf_lattice(interp)) if !any(inf_result.overridden_by_const) add_remark!(interp, sv, "[constprop] Could not handle constant info in matching_cache_argtypes") return nothing diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index d9443045e9e89..3092ef8838b29 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -911,7 +911,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize if frame === false # completely new lock_mi_inference(interp, mi) - result = InferenceResult(mi) + result = InferenceResult(mi, typeinf_lattice(interp)) frame = InferenceState(result, cache, interp) # always use the cache for edge targets if frame === nothing add_remark!(interp, caller, "Failed to retrieve source") @@ -985,7 +985,7 @@ end function typeinf_frame(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, run_optimizer::Bool) mi = specialize_method(method, atype, sparams)::MethodInstance ccall(:jl_typeinf_timing_begin, Cvoid, ()) - result = InferenceResult(mi) + result = InferenceResult(mi, typeinf_lattice(interp)) frame = InferenceState(result, run_optimizer ? :global : :no, interp) frame === nothing && return nothing typeinf(interp, frame) @@ -1044,7 +1044,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) return retrieve_code_info(mi) end lock_mi_inference(interp, mi) - result = InferenceResult(mi) + result = InferenceResult(mi, typeinf_lattice(interp)) frame = InferenceState(result, #=cache=#:global, interp) frame === nothing && return nothing typeinf(interp, frame) @@ -1068,7 +1068,7 @@ function typeinf_type(interp::AbstractInterpreter, method::Method, @nospecialize return code.rettype end end - result = InferenceResult(mi) + result = InferenceResult(mi, typeinf_lattice(interp)) typeinf(interp, result, :global) ccall(:jl_typeinf_timing_end, Cvoid, ()) result.result isa InferenceState && return nothing @@ -1087,7 +1087,7 @@ function typeinf_ext_toplevel(interp::AbstractInterpreter, linfo::MethodInstance # toplevel lambda - infer directly ccall(:jl_typeinf_timing_begin, Cvoid, ()) if !src.inferred - result = InferenceResult(linfo) + result = InferenceResult(linfo, typeinf_lattice(interp)) frame = InferenceState(result, src, #=cache=#:global, interp) typeinf(interp, frame) @assert frame.inferred # TODO: deal with this better diff --git a/base/compiler/types.jl b/base/compiler/types.jl index 4d5a77f4ee70d..4de48346cbee3 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -35,8 +35,7 @@ end abstract type ForwardableArgtypes end """ - InferenceResult(linfo::MethodInstance) - InferenceResult(linfo::MethodInstance, argtypes::ForwardableArgtypes) + InferenceResult(linfo::MethodInstance, [argtypes::ForwardableArgtypes, 𝕃::AbstractLattice]) A type that represents the result of running type inference on a chunk of code. @@ -58,12 +57,10 @@ mutable struct InferenceResult WorldRange(), Effects(), Effects(), nothing, true) end end -function InferenceResult(linfo::MethodInstance; lattice::AbstractLattice=fallback_lattice) - return InferenceResult(linfo, matching_cache_argtypes(lattice, linfo)...) -end -function InferenceResult(linfo::MethodInstance, argtypes::ForwardableArgtypes; lattice::AbstractLattice=fallback_lattice) - return InferenceResult(linfo, matching_cache_argtypes(lattice, linfo, argtypes)...) -end +InferenceResult(linfo::MethodInstance, 𝕃::AbstractLattice=fallback_lattice) = + InferenceResult(linfo, matching_cache_argtypes(𝕃, linfo)...) +InferenceResult(linfo::MethodInstance, argtypes::ForwardableArgtypes, 𝕃::AbstractLattice=fallback_lattice) = + InferenceResult(linfo, matching_cache_argtypes(𝕃, linfo, argtypes)...) """ inf_params::InferenceParams From ce292c1ba4f5ec771479228169383f6378d35d45 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Thu, 9 Feb 2023 21:32:52 -0500 Subject: [PATCH 070/775] [compiler] Remove ConstAPI struct (#48598) * Avoid split `y::Union` when comparing with a forall var. If `y` has no free typevar. Then `x <: y` wont cause any env change. Thus there's no need to split `y`. * [compiler] Remove `ConstAPI` struct `ConstAPI` predates the effect analysis framework, and was relying on an ad-hoc implementation of effect analysis that only worked for functions of less than 15 statements. As a result, it wasn't used much anymore, but it could cause trouble with external `AbstractInterpreters` that may want to cache additional code metadata within `CodeInstance`s. This commit simply removes this, expecting that the existing effect analysis will pick up the slack. * Add short-circuit `has_offset_axes(::Array)` This ensures that `Base.require_one_based_indexing()` is always fully eliminated, since previously `has_offset_axes(::Array)` is effect-tainted as non-consistent due to calculating `size(::Array)`. This dodges the effect taint by adding this short-circuit, fixing broken tests due to using effects analysis more extensively. * Move coverage effects analysis into inference Code coverage is implemented as an optimization pass that inserts an SSA IR node during optimization, however effects analysis is perfomed during inference, and so the desired effects tainting that should occur from insertion of the code coverage IR node is ignored. This teaches `InferenceState` to taint itself with the effects flag `effects_free = ALWAYS_FALSE` when coverage is applicable to this inference state's method. We just assume that you don't want code coverage if you're using this constructor. This may not always be true. --------- Co-authored-by: N5N3 <2642243996@qq.com> Co-authored-by: Oscar Smith --- base/abstractarray.jl | 1 + base/compiler/inferencestate.jl | 31 +++++++++- base/compiler/optimize.jl | 101 +++----------------------------- base/compiler/ssair/inlining.jl | 14 +---- base/compiler/ssair/irinterp.jl | 4 +- base/compiler/typeinfer.jl | 17 ++---- test/compiler/inference.jl | 16 +---- test/reducedim.jl | 11 +++- 8 files changed, 61 insertions(+), 134 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 256c5262b9bcd..a1d910e38e169 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -113,6 +113,7 @@ has_offset_axes(A::AbstractVector) = Int(firstindex(A))::Int != 1 # improve perf # note: this could call `any` directly if the compiler can infer it has_offset_axes(As...) = _any_tuple(has_offset_axes, false, As...) has_offset_axes(::Colon) = false +has_offset_axes(::Array) = false """ require_one_based_indexing(A::AbstractArray) diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 33363a903f0f8..49fafdc7e4727 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -129,6 +129,7 @@ mutable struct InferenceState # Set by default for toplevel frame. restrict_abstract_call_sites::Bool cached::Bool # TODO move this to InferenceResult? + insert_coverage::Bool # The interpreter that created this inference state. Not looked at by # NativeInterpreter. But other interpreters may use this to detect cycles @@ -136,7 +137,7 @@ mutable struct InferenceState # src is assumed to be a newly-allocated CodeInfo, that can be modified in-place to contain intermediate results function InferenceState(result::InferenceResult, src::CodeInfo, cache::Symbol, - interp::AbstractInterpreter) + interp::AbstractInterpreter) linfo = result.linfo world = get_world_counter(interp) def = linfo.def @@ -179,6 +180,32 @@ mutable struct InferenceState bestguess = Bottom ipo_effects = EFFECTS_TOTAL + # check if coverage mode is enabled + insert_coverage = coverage_enabled(mod) + if !insert_coverage && JLOptions().code_coverage == 3 # path-specific coverage mode + linetable = src.linetable + if isa(linetable, Vector{Any}) + for line in linetable + line = line::LineInfoNode + if is_file_tracked(line.file) + # if any line falls in a tracked file enable coverage for all + insert_coverage = true + break + end + end + elseif isa(linetable, Vector{LineInfo}) + for line in linetable + if is_file_tracked(line.file) + insert_coverage = true + break + end + end + end + end + if insert_coverage + ipo_effects = Effects(ipo_effects; effect_free = ALWAYS_FALSE) + end + params = InferenceParams(interp) restrict_abstract_call_sites = isa(linfo.def, Module) @assert cache === :no || cache === :local || cache === :global @@ -189,7 +216,7 @@ mutable struct InferenceState currbb, currpc, ip, handler_at, ssavalue_uses, bb_vartables, ssavaluetypes, stmt_edges, stmt_info, pclimitations, limitations, cycle_backedges, callers_in_cycle, dont_work_on_me, parent, inferred, result, valid_worlds, bestguess, ipo_effects, - params, restrict_abstract_call_sites, cached, + params, restrict_abstract_call_sites, cached, insert_coverage, interp) # some more setups diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 473ee3899b9da..2a69fda85109d 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -149,13 +149,14 @@ mutable struct OptimizationState{Interp<:AbstractInterpreter} slottypes::Vector{Any} inlining::InliningState{Interp} cfg::Union{Nothing,CFG} + insert_coverage::Bool end function OptimizationState(frame::InferenceState, params::OptimizationParams, interp::AbstractInterpreter, recompute_cfg::Bool=true) inlining = InliningState(frame, params, interp) cfg = recompute_cfg ? nothing : frame.cfg return OptimizationState(frame.linfo, frame.src, nothing, frame.stmt_info, frame.mod, - frame.sptypes, frame.slottypes, inlining, cfg) + frame.sptypes, frame.slottypes, inlining, cfg, frame.insert_coverage) end function OptimizationState(linfo::MethodInstance, src::CodeInfo, params::OptimizationParams, interp::AbstractInterpreter) @@ -179,7 +180,7 @@ function OptimizationState(linfo::MethodInstance, src::CodeInfo, params::Optimiz # Allow using the global MI cache, but don't track edges. # This method is mostly used for unit testing the optimizer inlining = InliningState(params, interp) - return OptimizationState(linfo, src, nothing, stmt_info, mod, sptypes, slottypes, inlining, nothing) + return OptimizationState(linfo, src, nothing, stmt_info, mod, sptypes, slottypes, inlining, nothing, false) end function OptimizationState(linfo::MethodInstance, params::OptimizationParams, interp::AbstractInterpreter) src = retrieve_code_info(linfo) @@ -228,23 +229,6 @@ is_stmt_inline(stmt_flag::UInt8) = stmt_flag & IR_FLAG_INLINE ≠ 0 is_stmt_noinline(stmt_flag::UInt8) = stmt_flag & IR_FLAG_NOINLINE ≠ 0 is_stmt_throw_block(stmt_flag::UInt8) = stmt_flag & IR_FLAG_THROW_BLOCK ≠ 0 -# These affect control flow within the function (so may not be removed -# if there is no usage within the function), but don't affect the purity -# of the function as a whole. -function stmt_affects_purity(@nospecialize(stmt), ir) - if isa(stmt, GotoNode) || isa(stmt, ReturnNode) - return false - end - if isa(stmt, GotoIfNot) - t = argextype(stmt.cond, ir) - return !(t ⊑ Bool) - end - if isa(stmt, Expr) - return stmt.head !== :loopinfo && stmt.head !== :enter - end - return true -end - """ stmt_effect_flags(stmt, rt, src::Union{IRCode,IncrementalCompact}) -> (consistent::Bool, effect_free_and_nothrow::Bool, nothrow::Bool) @@ -406,80 +390,26 @@ function argextype( end abstract_eval_ssavalue(s::SSAValue, src::Union{IRCode,IncrementalCompact}) = types(src)[s] -struct ConstAPI - val - ConstAPI(@nospecialize val) = new(val) -end """ finish(interp::AbstractInterpreter, opt::OptimizationState, - params::OptimizationParams, ir::IRCode, caller::InferenceResult) -> analyzed::Union{Nothing,ConstAPI} + params::OptimizationParams, ir::IRCode, caller::InferenceResult) -> analyzed::Nothing Post process information derived by Julia-level optimizations for later uses: - computes "purity", i.e. side-effect-freeness - computes inlining cost - -In a case when the purity is proven, `finish` can return `ConstAPI` object wrapping the constant -value so that the runtime system will use the constant calling convention for the method calls. """ function finish(interp::AbstractInterpreter, opt::OptimizationState, params::OptimizationParams, ir::IRCode, caller::InferenceResult) (; src, linfo) = opt (; def, specTypes) = linfo - analyzed = nothing # `ConstAPI` if this call can use constant calling convention force_noinline = is_declared_noinline(src) # compute inlining and other related optimizations result = caller.result @assert !(result isa LimitedAccuracy) result = widenslotwrapper(result) - if (isa(result, Const) || isconstType(result)) - proven_pure = false - # must be proven pure to use constant calling convention; - # otherwise we might skip throwing errors (issue #20704) - # TODO: Improve this analysis; if a function is marked @pure we should really - # only care about certain errors (e.g. method errors and type errors). - if length(ir.stmts) < 15 - proven_pure = true - for i in 1:length(ir.stmts) - node = ir.stmts[i] - stmt = node[:inst] - if stmt_affects_purity(stmt, ir) && !stmt_effect_flags(optimizer_lattice(interp), stmt, node[:type], ir)[2] - proven_pure = false - break - end - end - if proven_pure - for fl in src.slotflags - if (fl & SLOT_USEDUNDEF) != 0 - proven_pure = false - break - end - end - end - end - - if proven_pure - # use constant calling convention - # Do not emit `jl_fptr_const_return` if coverage is enabled - # so that we don't need to add coverage support - # to the `jl_call_method_internal` fast path - # Still set pure flag to make sure `inference` tests pass - # and to possibly enable more optimization in the future - src.pure = true - if isa(result, Const) - val = result.val - if is_inlineable_constant(val) - analyzed = ConstAPI(val) - end - else - @assert isconstType(result) - analyzed = ConstAPI(result.parameters[1]) - end - force_noinline || set_inlineable!(src, true) - end - end opt.ir = ir @@ -528,8 +458,7 @@ function finish(interp::AbstractInterpreter, opt::OptimizationState, src.inlining_cost = inline_cost(ir, params, union_penalties, cost_threshold) end end - - return analyzed + return nothing end # run the optimization work @@ -612,18 +541,6 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState) linetable = collect(LineInfoNode, linetable::Vector{Any})::Vector{LineInfoNode} end - # check if coverage mode is enabled - coverage = coverage_enabled(sv.mod) - if !coverage && JLOptions().code_coverage == 3 # path-specific coverage mode - for line in linetable - if is_file_tracked(line.file) - # if any line falls in a tracked file enable coverage for all - coverage = true - break - end - end - end - # Go through and add an unreachable node after every # Union{} call. Then reindex labels. code = copy_exprargs(ci.code) @@ -639,7 +556,7 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState) prevloc = zero(eltype(ci.codelocs)) while idx <= length(code) codeloc = codelocs[idx] - if coverage && codeloc != prevloc && codeloc != 0 + if sv.insert_coverage && codeloc != prevloc && codeloc != 0 # insert a side-effect instruction before the current instruction in the same basic block insert!(code, idx, Expr(:code_coverage_effect)) insert!(codelocs, idx, codeloc) @@ -650,7 +567,7 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState) ssachangemap = fill(0, nstmts) end if labelchangemap === nothing - labelchangemap = coverage ? fill(0, nstmts) : ssachangemap + labelchangemap = fill(0, nstmts) end ssachangemap[oldidx] += 1 if oldidx < length(labelchangemap) @@ -671,11 +588,11 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState) ssachangemap = fill(0, nstmts) end if labelchangemap === nothing - labelchangemap = coverage ? fill(0, nstmts) : ssachangemap + labelchangemap = sv.insert_coverage ? fill(0, nstmts) : ssachangemap end if oldidx < length(ssachangemap) ssachangemap[oldidx + 1] += 1 - coverage && (labelchangemap[oldidx + 1] += 1) + sv.insert_coverage && (labelchangemap[oldidx + 1] += 1) end idx += 1 end diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index efb30c05811d0..5ca8d51dba4a8 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -843,10 +843,10 @@ function resolve_todo(mi::MethodInstance, result::Union{MethodMatch,InferenceRes #XXX: update_valid_age!(min_valid[1], max_valid[1], sv) if isa(result, InferenceResult) src = result.src - if isa(src, ConstAPI) + if is_total(result.ipo_effects) && isa(result.result, Const) # use constant calling convention add_inlining_backedge!(et, mi) - return ConstantCase(quoted(src.val)) + return ConstantCase(quoted(result.result.val)) end effects = result.ipo_effects else @@ -866,16 +866,6 @@ function resolve_todo(mi::MethodInstance, result::Union{MethodMatch,InferenceRes end src = inlining_policy(state.interp, src, info, flag, mi, argtypes) - - if isa(src, ConstAPI) - # duplicates the check above in case inlining_policy has a better idea. - # We still keep the check above to make sure we can inline to ConstAPI - # even if is_stmt_noinline. This doesn't currently happen in Base, but - # can happen with external AbstractInterpreter. - add_inlining_backedge!(et, mi) - return ConstantCase(quoted(src.val)) - end - src === nothing && return compileable_specialization(result, effects, et, info; compilesig_invokes=state.params.compilesig_invokes) diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index 7b2df1b39dd51..aba5bee48afa3 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -144,7 +144,9 @@ function concrete_eval_invoke(interp::AbstractInterpreter, inst::Expr, mi::MethodInstance, irsv::IRInterpretationState) mi_cache = WorldView(code_cache(interp), irsv.world) code = get(mi_cache, mi, nothing) - code === nothing && return Pair{Any, Bool}(nothing, false) + if code === nothing + return Pair{Any, Bool}(nothing, false) + end argtypes = collect_argtypes(interp, inst.args[2:end], nothing, irsv.ir) argtypes === nothing && return Pair{Any, Bool}(Union{}, false) effects = decode_effects(code.ipo_purity_bits) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 3092ef8838b29..656350202b964 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -269,14 +269,6 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) opt = caller.src if opt isa OptimizationState{typeof(interp)} # implies `may_optimize(interp) === true` analyzed = optimize(interp, opt, OptimizationParams(interp), caller) - if isa(analyzed, ConstAPI) - # XXX: The work in ir_to_codeinf! is essentially wasted. The only reason - # we're doing it is so that code_llvm can return the code - # for the `return ...::Const` (which never runs anyway). We should do this - # as a post processing step instead. - ir_to_codeinf!(opt) - caller.src = analyzed - end caller.valid_worlds = (opt.inlining.et::EdgeTracker).valid_worlds[] end end @@ -300,9 +292,12 @@ function CodeInstance( local const_flags::Int32 result_type = result.result @assert !(result_type isa LimitedAccuracy) - if inferred_result isa ConstAPI + + if isa(result_type, Const) && is_foldable(result.ipo_effects) && + is_nothrow(result.ipo_effects) && + is_inlineable_constant(result_type.val) # use constant calling convention - rettype_const = inferred_result.val + rettype_const = result_type.val const_flags = 0x3 inferred_result = nothing else @@ -379,7 +374,7 @@ function transform_result_for_cache(interp::AbstractInterpreter, inferred_result = maybe_compress_codeinfo(interp, linfo, inferred_result) end # The global cache can only handle objects that codegen understands - if !isa(inferred_result, Union{CodeInfo, Vector{UInt8}, ConstAPI}) + if !isa(inferred_result, MaybeCompressed) inferred_result = nothing end return inferred_result diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 04de79292e4c6..3471b6534bdef 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -569,16 +569,9 @@ Base.@pure function fpure(a=rand(); b=rand()) # but would be too big to inline return a + b + rand() end -gpure() = fpure() -gpure(x::Irrational) = fpure(x) + @test which(fpure, ()).pure @test which(fpure, (typeof(pi),)).pure -@test !which(gpure, ()).pure -@test !which(gpure, (typeof(pi),)).pure -@test code_typed(gpure, ())[1][1].pure -@test code_typed(gpure, (typeof(π),))[1][1].pure -@test gpure() == gpure() == gpure() -@test gpure(π) == gpure(π) == gpure(π) # Make sure @pure works for functions using the new syntax Base.@pure (fpure2(x::T) where T) = T @@ -941,13 +934,6 @@ Base.@pure c20704() = (f20704(1.0); 1) d20704() = c20704() @test_throws MethodError d20704() -Base.@pure function a20704(x) - rand() - 42 -end -aa20704(x) = x(nothing) -@test code_typed(aa20704, (typeof(a20704),))[1][1].pure - #issue #21065, elision of _apply_iterate when splatted expression is not effect_free function f21065(x,y) println("x=$x, y=$y") diff --git a/test/reducedim.jl b/test/reducedim.jl index 5402376744e82..daa0a3fbe1f92 100644 --- a/test/reducedim.jl +++ b/test/reducedim.jl @@ -6,7 +6,16 @@ using Random # issue #35800 # tested very early since it can be state-dependent -@test @inferred(mapreduce(x->count(!iszero,x), +, [rand(1)]; init = 0.)) == 1.0 + +function my_simple_count(pred, g::Vector{T}) where {T} + n::T = zero(T) + for x in g + n += pred(x) + end + return n +end + +@test @inferred(mapreduce(x->my_simple_count(!iszero,x), +, [rand(1)]; init = 0.)) == 1.0 function safe_mapslices(op, A, region) newregion = intersect(region, 1:ndims(A)) From 714adc63acb0bbad9e33335522e1b939de811161 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Fri, 10 Feb 2023 02:12:24 -0500 Subject: [PATCH 071/775] avoid copy in getindex(::RegexMatch,::String) (#48618) --- base/regex.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/base/regex.jl b/base/regex.jl index 820fc3eca502a..dd013e3c79ad1 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -243,19 +243,17 @@ end # Capture group extraction getindex(m::RegexMatch, idx::Integer) = m.captures[idx] -function getindex(m::RegexMatch, name::Symbol) +function getindex(m::RegexMatch, name::Union{AbstractString,Symbol}) idx = PCRE.substring_number_from_name(m.regex.regex, name) idx <= 0 && error("no capture group named $name found in regex") m[idx] end -getindex(m::RegexMatch, name::AbstractString) = m[Symbol(name)] haskey(m::RegexMatch, idx::Integer) = idx in eachindex(m.captures) -function haskey(m::RegexMatch, name::Symbol) +function haskey(m::RegexMatch, name::Union{AbstractString,Symbol}) idx = PCRE.substring_number_from_name(m.regex.regex, name) return idx > 0 end -haskey(m::RegexMatch, name::AbstractString) = haskey(m, Symbol(name)) iterate(m::RegexMatch, args...) = iterate(m.captures, args...) length(m::RegexMatch) = length(m.captures) From f2273c4bca574cfc53e5bf723d8382bc89c3c86f Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Fri, 10 Feb 2023 02:13:03 -0500 Subject: [PATCH 072/775] document regex supertypes (#48619) --- base/regex.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/regex.jl b/base/regex.jl index dd013e3c79ad1..d1ef3c9d13d48 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -8,7 +8,7 @@ const DEFAULT_COMPILER_OPTS = PCRE.UTF | PCRE.MATCH_INVALID_UTF | PCRE.ALT_BSUX const DEFAULT_MATCH_OPTS = PCRE.NO_UTF_CHECK """ - Regex(pattern[, flags]) + Regex(pattern[, flags]) <: AbstractPattern A type representing a regular expression. `Regex` objects can be used to match strings with [`match`](@ref). @@ -139,7 +139,7 @@ in a string using an `AbstractPattern`. abstract type AbstractMatch end """ - RegexMatch + RegexMatch <: AbstractMatch A type representing a single match to a `Regex` found in a string. Typically created from the [`match`](@ref) function. @@ -499,7 +499,7 @@ function count(t::Union{AbstractChar,AbstractString,AbstractPattern}, s::Abstrac end """ - SubstitutionString(substr) + SubstitutionString(substr) <: AbstractString Stores the given string `substr` as a `SubstitutionString`, for use in regular expression substitutions. Most commonly constructed using the [`@s_str`](@ref) macro. From a662248bafa730889716e20ce267cdd361fda6e3 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Fri, 10 Feb 2023 02:14:08 -0500 Subject: [PATCH 073/775] permute! and invpermute! - explain fully in-place alternatives (#48609) --- base/combinatorics.jl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/base/combinatorics.jl b/base/combinatorics.jl index 9c753560e3f82..d09a5b6c0ce83 100644 --- a/base/combinatorics.jl +++ b/base/combinatorics.jl @@ -164,7 +164,10 @@ end Permute vector `v` in-place, according to permutation `p`. No checking is done to verify that `p` is a permutation. -To return a new permutation, use `v[p]`. Note that this is faster than `permute!(v, p)`. +To return a new permutation, use `v[p]`. This is generally faster than `permute!(v, p)`; +it is even faster to write into a pre-allocated output array with `u .= @view v[p]`. +(Even though `permute!` overwrites `v` in-place, it internally requires some allocation +to keep track of which elements have been moved.) See also [`invpermute!`](@ref). @@ -215,6 +218,10 @@ end Like [`permute!`](@ref), but the inverse of the given permutation is applied. +Note that if you have a pre-allocated output array (e.g. `u = similar(v)`), +it is quicker to instead employ `u[p] = v`. (`invpermute!` internally +allocates a copy of the data.) + # Examples ```jldoctest julia> A = [1, 1, 3, 4]; From 28e0f46299612840ec28f0aad85a33cb8fb5f9f3 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 10 Feb 2023 16:41:12 +0900 Subject: [PATCH 074/775] NFC followups on #48598 (#48622) - added missing `is_inlineable_constant` check in the inlining code of `InferenceResult` - rename `is_total` to `is_foldable_nothrow` (since `:total` subsumes the other program properties too now) - updated docstring of `Core.Compiler.finish` --- base/compiler/effects.jl | 2 +- base/compiler/inferencestate.jl | 46 +++++++++++++++++---------------- base/compiler/optimize.jl | 7 +++-- base/compiler/ssair/inlining.jl | 13 ++++++---- base/compiler/typeinfer.jl | 6 ++--- test/compiler/effects.jl | 8 +++--- test/compiler/inference.jl | 2 +- test/int.jl | 2 +- test/reflection.jl | 4 +-- 9 files changed, 46 insertions(+), 44 deletions(-) diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index 27e41bf04865d..16e02cad81cb9 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -157,7 +157,7 @@ is_foldable(effects::Effects) = is_effect_free(effects) && is_terminates(effects) -is_total(effects::Effects) = +is_foldable_nothrow(effects::Effects) = is_foldable(effects) && is_nothrow(effects) diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 49fafdc7e4727..f8d35e2b7c136 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -180,28 +180,7 @@ mutable struct InferenceState bestguess = Bottom ipo_effects = EFFECTS_TOTAL - # check if coverage mode is enabled - insert_coverage = coverage_enabled(mod) - if !insert_coverage && JLOptions().code_coverage == 3 # path-specific coverage mode - linetable = src.linetable - if isa(linetable, Vector{Any}) - for line in linetable - line = line::LineInfoNode - if is_file_tracked(line.file) - # if any line falls in a tracked file enable coverage for all - insert_coverage = true - break - end - end - elseif isa(linetable, Vector{LineInfo}) - for line in linetable - if is_file_tracked(line.file) - insert_coverage = true - break - end - end - end - end + insert_coverage = should_insert_coverage(mod, src) if insert_coverage ipo_effects = Effects(ipo_effects; effect_free = ALWAYS_FALSE) end @@ -344,6 +323,29 @@ function compute_trycatch(code::Vector{Any}, ip::BitSet) return handler_at end +# check if coverage mode is enabled +function should_insert_coverage(mod::Module, src::CodeInfo) + coverage_enabled(mod) && return true + JLOptions().code_coverage == 3 || return false + # path-specific coverage mode: if any line falls in a tracked file enable coverage for all + linetable = src.linetable + if isa(linetable, Vector{Any}) + for line in linetable + line = line::LineInfoNode + if is_file_tracked(line.file) + return true + end + end + elseif isa(linetable, Vector{LineInfo}) + for line in linetable + if is_file_tracked(line.file) + return true + end + end + end + return false +end + """ Iterate through all callers of the given InferenceState in the abstract interpretation stack (including the given InferenceState itself), vising diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 2a69fda85109d..13e99e6976f7b 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -393,11 +393,10 @@ abstract_eval_ssavalue(s::SSAValue, src::Union{IRCode,IncrementalCompact}) = typ """ finish(interp::AbstractInterpreter, opt::OptimizationState, - params::OptimizationParams, ir::IRCode, caller::InferenceResult) -> analyzed::Nothing + params::OptimizationParams, ir::IRCode, caller::InferenceResult) -Post process information derived by Julia-level optimizations for later uses: -- computes "purity", i.e. side-effect-freeness -- computes inlining cost +Post-process information derived by Julia-level optimizations for later use. +In particular, this function determines the inlineability of the optimized code. """ function finish(interp::AbstractInterpreter, opt::OptimizationState, params::OptimizationParams, ir::IRCode, caller::InferenceResult) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 5ca8d51dba4a8..2cc9927740065 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -843,10 +843,13 @@ function resolve_todo(mi::MethodInstance, result::Union{MethodMatch,InferenceRes #XXX: update_valid_age!(min_valid[1], max_valid[1], sv) if isa(result, InferenceResult) src = result.src - if is_total(result.ipo_effects) && isa(result.result, Const) - # use constant calling convention - add_inlining_backedge!(et, mi) - return ConstantCase(quoted(result.result.val)) + if is_foldable_nothrow(result.ipo_effects) + res = result.result + if isa(res, Const) && is_inlineable_constant(res.val) + # use constant calling convention + add_inlining_backedge!(et, mi) + return ConstantCase(quoted(res.val)) + end end effects = result.ipo_effects else @@ -978,7 +981,7 @@ function handle_single_case!(todo::Vector{Pair{Int,Any}}, if isa(case, ConstantCase) ir[SSAValue(idx)][:inst] = case.val elseif isa(case, InvokeCase) - is_total(case.effects) && inline_const_if_inlineable!(ir[SSAValue(idx)]) && return nothing + is_foldable_nothrow(case.effects) && inline_const_if_inlineable!(ir[SSAValue(idx)]) && return nothing isinvoke && rewrite_invoke_exprargs!(stmt) stmt.head = :invoke pushfirst!(stmt.args, case.invoke) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 656350202b964..c8086bc239cbc 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -293,9 +293,7 @@ function CodeInstance( result_type = result.result @assert !(result_type isa LimitedAccuracy) - if isa(result_type, Const) && is_foldable(result.ipo_effects) && - is_nothrow(result.ipo_effects) && - is_inlineable_constant(result_type.val) + if isa(result_type, Const) && is_foldable_nothrow(result.ipo_effects) && is_inlineable_constant(result_type.val) # use constant calling convention rettype_const = result_type.val const_flags = 0x3 @@ -1007,7 +1005,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) tree.slotflags = fill(IR_FLAG_NULL, nargs) tree.ssavaluetypes = 1 tree.codelocs = Int32[1] - tree.linetable = [LineInfoNode(method.module, method.name, method.file, method.line, Int32(0))] + tree.linetable = LineInfoNode[LineInfoNode(method.module, method.name, method.file, method.line, Int32(0))] tree.inferred = true tree.ssaflags = UInt8[0] tree.pure = true diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index dcc96ec21228d..599da1225cf52 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -723,7 +723,7 @@ end # Effects for getfield of type instance @test Base.infer_effects(Tuple{Nothing}) do x WrapperOneField{typeof(x)}.instance -end |> Core.Compiler.is_total +end |> Core.Compiler.is_foldable_nothrow @test Base.infer_effects(Tuple{WrapperOneField{Float64}, Symbol}) do w, s getfield(w, s) end |> Core.Compiler.is_foldable @@ -735,14 +735,14 @@ end |> Core.Compiler.is_foldable # Flow-sensitive consistenct for _typevar @test Base.infer_effects() do return WrapperOneField == (WrapperOneField{T} where T) -end |> Core.Compiler.is_total +end |> Core.Compiler.is_foldable_nothrow # Test that dead `@inbounds` does not taint consistency # https://github.com/JuliaLang/julia/issues/48243 @test Base.infer_effects() do false && @inbounds (1,2,3)[1] return 1 -end |> Core.Compiler.is_total +end |> Core.Compiler.is_foldable_nothrow @test Base.infer_effects(Tuple{Int64}) do i @inbounds (1,2,3)[i] @@ -757,7 +757,7 @@ end @test Core.Compiler.is_foldable(Base.infer_effects(ImmutRef, Tuple{Any})) @test Base.ismutationfree(Type{Union{}}) -@test Core.Compiler.is_total(Base.infer_effects(typejoin, ())) +@test Core.Compiler.is_foldable_nothrow(Base.infer_effects(typejoin, ())) # nothrow-ness of subtyping operations # https://github.com/JuliaLang/julia/pull/48566 diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 3471b6534bdef..b136f91db8ca2 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -4743,7 +4743,7 @@ type_level_recurse_entry() = Val{type_level_recurse1(1)}() f_no_bail_effects_any(x::Any) = x f_no_bail_effects_any(x::NamedTuple{(:x,), Tuple{Any}}) = getfield(x, 1) g_no_bail_effects_any(x::Any) = f_no_bail_effects_any(x) -@test Core.Compiler.is_total(Base.infer_effects(g_no_bail_effects_any, Tuple{Any})) +@test Core.Compiler.is_foldable_nothrow(Base.infer_effects(g_no_bail_effects_any, Tuple{Any})) # issue #48374 @test (() -> Union{<:Nothing})() == Nothing diff --git a/test/int.jl b/test/int.jl index 07438dc8bc8b9..f1292c98faf40 100644 --- a/test/int.jl +++ b/test/int.jl @@ -202,7 +202,7 @@ end for T2 in Base.BitInteger_types for op in (>>, <<, >>>) if sizeof(T2)==sizeof(Int) || T <: Signed || (op==>>>) || T2 <: Unsigned - @test Core.Compiler.is_total(Base.infer_effects(op, (T, T2))) + @test Core.Compiler.is_foldable_nothrow(Base.infer_effects(op, (T, T2))) else @test Core.Compiler.is_foldable(Base.infer_effects(op, (T, T2))) # #47835, TODO implement interval arithmetic analysis diff --git a/test/reflection.jl b/test/reflection.jl index 1f49cd7d0be02..0c1081ba2c42f 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -1007,8 +1007,8 @@ ambig_effects_test(a, b) = 1 @test Base.infer_effects(ambig_effects_test, (Int,Int)) |> !Core.Compiler.is_nothrow # ambiguity error @test Base.infer_effects(ambig_effects_test, (Int,Any)) |> !Core.Compiler.is_nothrow # ambiguity error # builtins - @test Base.infer_effects(typeof, (Any,)) |> Core.Compiler.is_total - @test Base.infer_effects(===, (Any,Any)) |> Core.Compiler.is_total + @test Base.infer_effects(typeof, (Any,)) |> Core.Compiler.is_foldable_nothrow + @test Base.infer_effects(===, (Any,Any)) |> Core.Compiler.is_foldable_nothrow @test (Base.infer_effects(setfield!, ()); true) # `builtin_effects` shouldn't throw on empty `argtypes` @test (Base.infer_effects(Core.Intrinsics.arraylen, ()); true) # `intrinsic_effects` shouldn't throw on empty `argtypes` end From ef768e91ee0d6a6a34ce9a3cfeccf14a9b136a16 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 10 Feb 2023 17:05:06 +0900 Subject: [PATCH 075/775] inference: use `VarState` to represent static parameter (#48601) This allows us to represent strict undefined-ness of a static parameter. --- base/compiler/abstractinterpretation.jl | 14 ++--- base/compiler/inferencestate.jl | 68 +++++++----------------- base/compiler/optimize.jl | 22 ++++---- base/compiler/ssair/ir.jl | 6 +-- base/compiler/ssair/irinterp.jl | 2 +- base/compiler/ssair/legacy.jl | 14 +++-- base/compiler/ssair/slot2ssa.jl | 6 +-- base/compiler/typeinfer.jl | 4 +- base/compiler/types.jl | 16 ++++++ base/compiler/typeutils.jl | 9 +++- base/reflection.jl | 3 +- stdlib/InteractiveUtils/test/runtests.jl | 2 +- test/compiler/irpasses.jl | 7 +-- test/compiler/irutils.jl | 4 +- test/compiler/ssair.jl | 4 +- test/opaque_closure.jl | 4 +- 16 files changed, 91 insertions(+), 94 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index ddfac03275fb1..621de1354f769 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2191,12 +2191,9 @@ function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, vtypes:: n = e.args[1]::Int nothrow = false if 1 <= n <= length(sv.sptypes) - rt = sv.sptypes[n] - if is_maybeundefsp(rt) - rt = unwrap_maybeundefsp(rt) - else - nothrow = true - end + sp = sv.sptypes[n] + rt = sp.typ + nothrow = !sp.undef end merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; nothrow)) return rt @@ -2460,8 +2457,11 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp elseif isexpr(sym, :static_parameter) n = sym.args[1]::Int if 1 <= n <= length(sv.sptypes) - if !is_maybeundefsp(sv.sptypes, n) + sp = sv.sptypes[n] + if !sp.undef t = Const(true) + elseif sp.typ === Bottom + t = Const(false) end end end diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index f8d35e2b7c136..b75cffc72bbe4 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -1,13 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -# The type of a variable load is either a value or an UndefVarError -# (only used in abstractinterpret, doesn't appear in optimize) -struct VarState - typ - undef::Bool - VarState(@nospecialize(typ), undef::Bool) = new(typ, undef) -end - """ const VarTable = Vector{VarState} @@ -91,7 +83,7 @@ mutable struct InferenceState linfo::MethodInstance world::UInt mod::Module - sptypes::Vector{Any} + sptypes::Vector{VarState} slottypes::Vector{Any} src::CodeInfo cfg::CFG @@ -439,25 +431,7 @@ function constrains_param(var::TypeVar, @nospecialize(typ), covariant::Bool, typ return false end -""" - MaybeUndefSP(typ) - is_maybeundefsp(typ) -> Bool - unwrap_maybeundefsp(typ) -> Any - -A special wrapper that represents a static parameter that could be undefined at runtime. -This does not participate in the native type system nor the inference lattice, -and it thus should be always unwrapped when performing any type or lattice operations on it. -""" -struct MaybeUndefSP - typ - MaybeUndefSP(@nospecialize typ) = new(typ) -end -is_maybeundefsp(@nospecialize typ) = isa(typ, MaybeUndefSP) -unwrap_maybeundefsp(@nospecialize typ) = isa(typ, MaybeUndefSP) ? typ.typ : typ -is_maybeundefsp(sptypes::Vector{Any}, idx::Int) = is_maybeundefsp(sptypes[idx]) -unwrap_maybeundefsp(sptypes::Vector{Any}, idx::Int) = unwrap_maybeundefsp(sptypes[idx]) - -const EMPTY_SPTYPES = Any[] +const EMPTY_SPTYPES = VarState[] function sptypes_from_meth_instance(linfo::MethodInstance) def = linfo.def @@ -466,28 +440,26 @@ function sptypes_from_meth_instance(linfo::MethodInstance) if isempty(linfo.sparam_vals) isa(sig, UnionAll) || return EMPTY_SPTYPES # linfo is unspecialized - sp = Any[] + spvals = Any[] sig′ = sig while isa(sig′, UnionAll) - push!(sp, sig′.var) + push!(spvals, sig′.var) sig′ = sig′.body end else - sp = collect(Any, linfo.sparam_vals) + spvals = linfo.sparam_vals end - for i = 1:length(sp) - v = sp[i] + nvals = length(spvals) + sptypes = Vector{VarState}(undef, nvals) + for i = 1:nvals + v = spvals[i] if v isa TypeVar - maybe_undef = !constrains_param(v, linfo.specTypes, #=covariant=#true) temp = sig for j = 1:i-1 temp = temp.body end vᵢ = (temp::UnionAll).var - while temp isa UnionAll - temp = temp.body - end - sigtypes = (temp::DataType).parameters + sigtypes = (unwrap_unionall(temp)::DataType).parameters for j = 1:length(sigtypes) sⱼ = sigtypes[j] if isType(sⱼ) && sⱼ.parameters[1] === vᵢ @@ -497,36 +469,32 @@ function sptypes_from_meth_instance(linfo::MethodInstance) @goto ty_computed end end - ub = v.ub - while ub isa TypeVar - ub = ub.ub - end + ub = unwraptv_ub(v) if has_free_typevars(ub) ub = Any end - lb = v.lb - while lb isa TypeVar - lb = lb.lb - end + lb = unwraptv_lb(v) if has_free_typevars(lb) lb = Bottom end - if Any <: ub && lb <: Bottom + if Any === ub && lb === Bottom ty = Any else tv = TypeVar(v.name, lb, ub) ty = UnionAll(tv, Type{tv}) end @label ty_computed - maybe_undef && (ty = MaybeUndefSP(ty)) + undef = !constrains_param(v, linfo.specTypes, #=covariant=#true) elseif isvarargtype(v) ty = Int + undef = false else ty = Const(v) + undef = false end - sp[i] = ty + sptypes[i] = VarState(ty, undef) end - return sp + return sptypes end _topmod(sv::InferenceState) = _topmod(sv.mod) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 13e99e6976f7b..d0303168c834f 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -145,7 +145,7 @@ mutable struct OptimizationState{Interp<:AbstractInterpreter} ir::Union{Nothing, IRCode} stmt_info::Vector{CallInfo} mod::Module - sptypes::Vector{Any} + sptypes::Vector{VarState} slottypes::Vector{Any} inlining::InliningState{Interp} cfg::Union{Nothing,CFG} @@ -253,7 +253,7 @@ function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospe if head === :static_parameter # if we aren't certain enough about the type, it might be an UndefVarError at runtime sptypes = isa(src, IRCode) ? src.sptypes : src.ir.sptypes - nothrow = !is_maybeundefsp(sptypes, args[1]::Int) + nothrow = !sptypes[args[1]::Int].undef return (true, nothrow, nothrow) end if head === :call @@ -343,25 +343,25 @@ end """ argextype(x, src::Union{IRCode,IncrementalCompact}) -> t - argextype(x, src::CodeInfo, sptypes::Vector{Any}) -> t + argextype(x, src::CodeInfo, sptypes::Vector{VarState}) -> t Return the type of value `x` in the context of inferred source `src`. Note that `t` might be an extended lattice element. Use `widenconst(t)` to get the native Julia type of `x`. """ -argextype(@nospecialize(x), ir::IRCode, sptypes::Vector{Any} = ir.sptypes) = +argextype(@nospecialize(x), ir::IRCode, sptypes::Vector{VarState} = ir.sptypes) = argextype(x, ir, sptypes, ir.argtypes) -function argextype(@nospecialize(x), compact::IncrementalCompact, sptypes::Vector{Any} = compact.ir.sptypes) +function argextype(@nospecialize(x), compact::IncrementalCompact, sptypes::Vector{VarState} = compact.ir.sptypes) isa(x, AnySSAValue) && return types(compact)[x] return argextype(x, compact, sptypes, compact.ir.argtypes) end -argextype(@nospecialize(x), src::CodeInfo, sptypes::Vector{Any}) = argextype(x, src, sptypes, src.slottypes::Vector{Any}) +argextype(@nospecialize(x), src::CodeInfo, sptypes::Vector{VarState}) = argextype(x, src, sptypes, src.slottypes::Vector{Any}) function argextype( @nospecialize(x), src::Union{IRCode,IncrementalCompact,CodeInfo}, - sptypes::Vector{Any}, slottypes::Vector{Any}) + sptypes::Vector{VarState}, slottypes::Vector{Any}) if isa(x, Expr) if x.head === :static_parameter - return unwrap_maybeundefsp(sptypes, x.args[1]::Int) + return sptypes[x.args[1]::Int].typ elseif x.head === :boundscheck return Bool elseif x.head === :copyast @@ -645,7 +645,7 @@ plus_saturate(x::Int, y::Int) = max(x, y, x+y) # known return type isknowntype(@nospecialize T) = (T === Union{}) || isa(T, Const) || isconcretetype(widenconst(T)) -function statement_cost(ex::Expr, line::Int, src::Union{CodeInfo, IRCode}, sptypes::Vector{Any}, +function statement_cost(ex::Expr, line::Int, src::Union{CodeInfo, IRCode}, sptypes::Vector{VarState}, union_penalties::Bool, params::OptimizationParams, error_path::Bool = false) head = ex.head if is_meta_expr_head(head) @@ -736,7 +736,7 @@ function statement_cost(ex::Expr, line::Int, src::Union{CodeInfo, IRCode}, sptyp return 0 end -function statement_or_branch_cost(@nospecialize(stmt), line::Int, src::Union{CodeInfo, IRCode}, sptypes::Vector{Any}, +function statement_or_branch_cost(@nospecialize(stmt), line::Int, src::Union{CodeInfo, IRCode}, sptypes::Vector{VarState}, union_penalties::Bool, params::OptimizationParams) thiscost = 0 dst(tgt) = isa(src, IRCode) ? first(src.cfg.blocks[tgt].stmts) : tgt @@ -766,7 +766,7 @@ function inline_cost(ir::IRCode, params::OptimizationParams, union_penalties::Bo return inline_cost_clamp(bodycost) end -function statement_costs!(cost::Vector{Int}, body::Vector{Any}, src::Union{CodeInfo, IRCode}, sptypes::Vector{Any}, unionpenalties::Bool, params::OptimizationParams) +function statement_costs!(cost::Vector{Int}, body::Vector{Any}, src::Union{CodeInfo, IRCode}, sptypes::Vector{VarState}, unionpenalties::Bool, params::OptimizationParams) maxcost = 0 for line = 1:length(body) stmt = body[line] diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index 1d6be5a2b09d8..a013745b8c773 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -334,13 +334,13 @@ end struct IRCode stmts::InstructionStream argtypes::Vector{Any} - sptypes::Vector{Any} + sptypes::Vector{VarState} linetable::Vector{LineInfoNode} cfg::CFG new_nodes::NewNodeStream meta::Vector{Expr} - function IRCode(stmts::InstructionStream, cfg::CFG, linetable::Vector{LineInfoNode}, argtypes::Vector{Any}, meta::Vector{Expr}, sptypes::Vector{Any}) + function IRCode(stmts::InstructionStream, cfg::CFG, linetable::Vector{LineInfoNode}, argtypes::Vector{Any}, meta::Vector{Expr}, sptypes::Vector{VarState}) return new(stmts, argtypes, sptypes, linetable, cfg, NewNodeStream(), meta) end function IRCode(ir::IRCode, stmts::InstructionStream, cfg::CFG, new_nodes::NewNodeStream) @@ -358,7 +358,7 @@ for debugging and unit testing of IRCode APIs. The compiler itself should genera from the frontend or one of the caches. """ function IRCode() - ir = IRCode(InstructionStream(1), CFG([BasicBlock(1:1, Int[], Int[])], Int[1]), LineInfoNode[], Any[], Expr[], Any[]) + ir = IRCode(InstructionStream(1), CFG([BasicBlock(1:1, Int[], Int[])], Int[1]), LineInfoNode[], Any[], Expr[], VarState[]) ir[SSAValue(1)][:inst] = ReturnNode(nothing) ir[SSAValue(1)][:type] = Nothing ir[SSAValue(1)][:flag] = 0x00 diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index aba5bee48afa3..90260530f9bd4 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -463,7 +463,7 @@ end function ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IRInterpretationState) if __measure_typeinf__[] - inf_frame = Timings.InferenceFrameInfo(irsv.mi, irsv.world, Any[], Any[], length(irsv.ir.argtypes)) + inf_frame = Timings.InferenceFrameInfo(irsv.mi, irsv.world, VarState[], Any[], length(irsv.ir.argtypes)) Timings.enter_new_timer(inf_frame) v = _ir_abstract_constant_propagation(interp, irsv) append!(inf_frame.slottypes, irsv.ir.argtypes) diff --git a/base/compiler/ssair/legacy.jl b/base/compiler/ssair/legacy.jl index 0ddefa4483eb1..0539d79fc17e7 100644 --- a/base/compiler/ssair/legacy.jl +++ b/base/compiler/ssair/legacy.jl @@ -2,7 +2,7 @@ """ inflate_ir!(ci::CodeInfo, linfo::MethodInstance) -> ir::IRCode - inflate_ir!(ci::CodeInfo, sptypes::Vector{Any}, argtypes::Vector{Any}) -> ir::IRCode + inflate_ir!(ci::CodeInfo, sptypes::Vector{VarState}, argtypes::Vector{Any}) -> ir::IRCode Inflates `ci::CodeInfo`-IR to `ir::IRCode`-format. This should be used with caution as it is a in-place transformation where the fields of @@ -17,7 +17,7 @@ function inflate_ir!(ci::CodeInfo, linfo::MethodInstance) end return inflate_ir!(ci, sptypes, argtypes) end -function inflate_ir!(ci::CodeInfo, sptypes::Vector{Any}, argtypes::Vector{Any}) +function inflate_ir!(ci::CodeInfo, sptypes::Vector{VarState}, argtypes::Vector{Any}) code = ci.code cfg = compute_basic_blocks(code) for i = 1:length(code) @@ -51,15 +51,19 @@ end """ inflate_ir(ci::CodeInfo, linfo::MethodInstance) -> ir::IRCode - inflate_ir(ci::CodeInfo, sptypes::Vector{Any}, argtypes::Vector{Any}) -> ir::IRCode + inflate_ir(ci::CodeInfo, sptypes::Vector{VarState}, argtypes::Vector{Any}) -> ir::IRCode inflate_ir(ci::CodeInfo) -> ir::IRCode Non-destructive version of `inflate_ir!`. Mainly used for testing or interactive use. """ inflate_ir(ci::CodeInfo, linfo::MethodInstance) = inflate_ir!(copy(ci), linfo) -inflate_ir(ci::CodeInfo, sptypes::Vector{Any}, argtypes::Vector{Any}) = inflate_ir!(copy(ci), sptypes, argtypes) -inflate_ir(ci::CodeInfo) = inflate_ir(ci, Any[], Any[ ci.slottypes === nothing ? Any : (ci.slottypes::Vector{Any})[i] for i = 1:length(ci.slotflags) ]) +inflate_ir(ci::CodeInfo, sptypes::Vector{VarState}, argtypes::Vector{Any}) = inflate_ir!(copy(ci), sptypes, argtypes) +function inflate_ir(ci::CodeInfo) + slottypes = ci.slottypes + argtypes = Any[ slottypes === nothing ? Any : slottypes[i] for i = 1:length(ci.slotflags) ] + return inflate_ir(ci, VarState[], argtypes) +end function replace_code_newstyle!(ci::CodeInfo, ir::IRCode, nargs::Int) @assert isempty(ir.new_nodes) diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index 60d04ff1bf601..07ec86eb2d18a 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -213,10 +213,10 @@ struct DelayedTyp end # maybe use expr_type? -function typ_for_val(@nospecialize(x), ci::CodeInfo, sptypes::Vector{Any}, idx::Int, slottypes::Vector{Any}) +function typ_for_val(@nospecialize(x), ci::CodeInfo, sptypes::Vector{VarState}, idx::Int, slottypes::Vector{Any}) if isa(x, Expr) if x.head === :static_parameter - return unwrap_maybeundefsp(sptypes, x.args[1]::Int) + return sptypes[x.args[1]::Int].typ elseif x.head === :boundscheck return Bool elseif x.head === :copyast @@ -567,7 +567,7 @@ function compute_live_ins(cfg::CFG, defs::Vector{Int}, uses::Vector{Int}) end function recompute_type(node::Union{PhiNode, PhiCNode}, ci::CodeInfo, ir::IRCode, - sptypes::Vector{Any}, slottypes::Vector{Any}, nstmts::Int, 𝕃ₒ::AbstractLattice) + sptypes::Vector{VarState}, slottypes::Vector{Any}, nstmts::Int, 𝕃ₒ::AbstractLattice) new_typ = Union{} for i = 1:length(node.values) if isa(node, PhiNode) && !isassigned(node.values, i) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index c8086bc239cbc..45c8d1567d8cb 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -29,7 +29,7 @@ using Core.Compiler: -, +, :, Vector, length, first, empty!, push!, pop!, @inlin struct InferenceFrameInfo mi::Core.MethodInstance world::UInt64 - sptypes::Vector{Any} + sptypes::Vector{Core.Compiler.VarState} slottypes::Vector{Any} nargs::Int end @@ -89,7 +89,7 @@ function reset_timings() empty!(_timings) push!(_timings, Timing( # The MethodInstance for ROOT(), and default empty values for other fields. - InferenceFrameInfo(ROOTmi, 0x0, Any[], Any[Core.Const(ROOT)], 1), + InferenceFrameInfo(ROOTmi, 0x0, Core.Compiler.VarState[], Any[Core.Const(ROOT)], 1), _time_ns())) return nothing end diff --git a/base/compiler/types.jl b/base/compiler/types.jl index 4de48346cbee3..cac15e9a69513 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -32,6 +32,22 @@ struct StmtInfo used::Bool end +""" + v::VarState + +A special wrapper that represents a local variable of a method being analyzed. +This does not participate in the native type system nor the inference lattice, and it thus +should be always unwrapped to `v.typ` when performing any type or lattice operations on it. +`v.undef` represents undefined-ness of this static parameter. If `true`, it means that the +variable _may_ be undefined at runtime, otherwise it is guaranteed to be defined. +If `v.typ === Bottom` it means that the variable is strictly undefined. +""" +struct VarState + typ + undef::Bool + VarState(@nospecialize(typ), undef::Bool) = new(typ, undef) +end + abstract type ForwardableArgtypes end """ diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index 8f256ea88b78f..c94bc0ca2aa75 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -324,12 +324,19 @@ function unswitchtupleunion(u::Union) Tuple{Any[ Union{Any[(t::DataType).parameters[i] for t in ts]...} for i in 1:n ]...} end -function unwraptv(@nospecialize t) +function unwraptv_ub(@nospecialize t) while isa(t, TypeVar) t = t.ub end return t end +function unwraptv_lb(@nospecialize t) + while isa(t, TypeVar) + t = t.lb + end + return t +end +const unwraptv = unwraptv_ub # this query is specially written for `adjust_effects` and returns true if a value of this type # never involves inconsistency of mutable objects that are allocated somewhere within a call graph diff --git a/base/reflection.jl b/base/reflection.jl index 102c1ca9605e3..5326aea9ef87f 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1548,7 +1548,8 @@ function print_statement_costs(io::IO, @nospecialize(tt::Type); else empty!(cst) resize!(cst, length(code.code)) - maxcost = Core.Compiler.statement_costs!(cst, code.code, code, Any[match.sparams...], false, params) + sptypes = Core.Compiler.VarState[Core.Compiler.VarState(sp, false) for sp in match.sparams] + maxcost = Core.Compiler.statement_costs!(cst, code.code, code, sptypes, false, params) nd = ndigits(maxcost) irshow_config = IRShow.IRShowConfig() do io, linestart, idx print(io, idx > 0 ? lpad(cst[idx], nd+1) : " "^(nd+1), " ") diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index 02fb4b25ec43f..cd1e660377230 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -699,7 +699,7 @@ end @testset "code_llvm on opaque_closure" begin let ci = code_typed(+, (Int, Int))[1][1] - ir = Core.Compiler.inflate_ir(ci, Any[], Any[Tuple{}, Int, Int]) + ir = Core.Compiler.inflate_ir(ci) oc = Core.OpaqueClosure(ir) @test (code_llvm(devnull, oc, Tuple{Int, Int}); true) let io = IOBuffer() diff --git a/test/compiler/irpasses.jl b/test/compiler/irpasses.jl index bc2cb0d3507f3..43371b51f6a64 100644 --- a/test/compiler/irpasses.jl +++ b/test/compiler/irpasses.jl @@ -708,9 +708,10 @@ let m = Meta.@lower 1 + 1 Any ] nstmts = length(src.code) - src.codelocs = fill(Int32(1), nstmts) - src.ssaflags = fill(Int32(0), nstmts) - ir = Core.Compiler.inflate_ir(src, Any[], Any[Any, Any]) + src.codelocs = fill(one(Int32), nstmts) + src.ssaflags = fill(one(Int32), nstmts) + src.slotflags = fill(zero(UInt8), 3) + ir = Core.Compiler.inflate_ir(src) @test Core.Compiler.verify_ir(ir) === nothing ir = @test_nowarn Core.Compiler.sroa_pass!(ir) @test Core.Compiler.verify_ir(ir) === nothing diff --git a/test/compiler/irutils.jl b/test/compiler/irutils.jl index ef8fe3efbb315..95ac0d555ef88 100644 --- a/test/compiler/irutils.jl +++ b/test/compiler/irutils.jl @@ -1,8 +1,8 @@ import Core: CodeInfo, ReturnNode, MethodInstance -import Core.Compiler: IRCode, IncrementalCompact, argextype, singleton_type +import Core.Compiler: IRCode, IncrementalCompact, VarState, argextype, singleton_type import Base.Meta: isexpr -argextype(@nospecialize args...) = argextype(args..., Any[]) +argextype(@nospecialize args...) = argextype(args..., VarState[]) code_typed1(args...; kwargs...) = first(only(code_typed(args...; kwargs...)))::CodeInfo get_code(args...; kwargs...) = code_typed1(args...; kwargs...).code diff --git a/test/compiler/ssair.jl b/test/compiler/ssair.jl index 6f485a7c781ec..97ec58ce4c6c1 100644 --- a/test/compiler/ssair.jl +++ b/test/compiler/ssair.jl @@ -117,8 +117,8 @@ let cfg = CFG(BasicBlock[ make_bb([2, 3] , [] ), ], Int[]) insts = Compiler.InstructionStream([], [], Any[], Int32[], UInt8[]) - code = Compiler.IRCode(insts, cfg, LineInfoNode[], [], Expr[], []) - compact = Compiler.IncrementalCompact(code, true) + ir = Compiler.IRCode(insts, cfg, Core.LineInfoNode[], Any[], Expr[], Compiler.VarState[]) + compact = Compiler.IncrementalCompact(ir, true) @test length(compact.result_bbs) == 4 && 0 in compact.result_bbs[3].preds end diff --git a/test/opaque_closure.jl b/test/opaque_closure.jl index 530c5b3a844eb..b5d5f9ed522ac 100644 --- a/test/opaque_closure.jl +++ b/test/opaque_closure.jl @@ -247,7 +247,7 @@ let ci = code_typed(+, (Int, Int))[1][1] @test OpaqueClosure(ir; nargs=2, isva=false)(40, 2) == 42 @test OpaqueClosure(ci)(40, 2) == 42 - ir = Core.Compiler.inflate_ir(ci, Any[], Any[Tuple{}, Int, Int]) + ir = Core.Compiler.inflate_ir(ci) @test OpaqueClosure(ir; nargs=2, isva=false)(40, 2) == 42 @test isa(OpaqueClosure(ir; nargs=2, isva=false), Core.OpaqueClosure{Tuple{Int, Int}, Int}) @test_throws TypeError OpaqueClosure(ir; nargs=2, isva=false)(40.0, 2) @@ -264,7 +264,7 @@ let ci = code_typed((x, y...)->(x, y), (Int, Int))[1][1] @test_throws MethodError oc(1,2,3) end - ir = Core.Compiler.inflate_ir(ci, Any[], Any[Tuple{}, Int, Tuple{Int}]) + ir = Core.Compiler.inflate_ir(ci) let oc = OpaqueClosure(ir; nargs=2, isva=true) @test oc(40, 2) === (40, (2,)) @test_throws MethodError oc(1,2,3) From c0d2c574d44231dba39f90d6a6b27448f71763ca Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 10 Feb 2023 07:18:50 -0500 Subject: [PATCH 076/775] force singlethreading during precompilation (#48592) --- base/loading.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/loading.jl b/base/loading.jl index 41998bbf694a3..35240b1cd500f 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2075,7 +2075,8 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: --color=$(have_color === nothing ? "auto" : have_color ? "yes" : "no") $trace -`, - "OPENBLAS_NUM_THREADS" => 1), + "OPENBLAS_NUM_THREADS" => 1, + "JULIA_NUM_THREADS" => 1), stderr = internal_stderr, stdout = internal_stdout), "w", stdout) # write data over stdin to avoid the (unlikely) case of exceeding max command line size From 3db0cc20eba14978c45cd91f05a9b89b1dc0e38a Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Fri, 10 Feb 2023 08:40:50 -0500 Subject: [PATCH 077/775] subtyping: Relax conditions for separable tuple fast-path (#48578) This check is unnecessary, since the LHS type is a subtype iff all union-decision-paths are subtypes of the RHS type. The RHS check needs to stay, since multiple union-decision-paths on the RHS may have to be considered together, even when none of the individual paths are sufficient on their own to conclude `a <: b`. --- src/subtype.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subtype.c b/src/subtype.c index c3b48df783be6..eb26ed69eb89e 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1145,7 +1145,7 @@ static int subtype_tuple_tail(jl_datatype_t *xd, jl_datatype_t *yd, int8_t R, jl (yi == lastx && !vx && vy && jl_is_concrete_type(xi)))) { // fast path for repeated elements } - else if (e->Runions.depth == 0 && e->Lunions.depth == 0 && !jl_has_free_typevars(xi) && !jl_has_free_typevars(yi)) { + else if (e->Runions.depth == 0 && !jl_has_free_typevars(xi) && !jl_has_free_typevars(yi)) { // fast path for separable sub-formulas if (!jl_subtype(xi, yi)) return 0; From a44b576faee61e0bf615bce88e6a1698bc83a4ad Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sat, 11 Feb 2023 00:38:26 +0900 Subject: [PATCH 078/775] allow external `AbstractInterpreter` to keep source of const-folded cache (#48626) After #48598, the `CodeInstance` constructor discards `inferred_result` when the cache can use the constant calling convention. This is generally fine, but external `AbstractInterpreter` may still want to keep the discarded source, that may be arbitrary custom data structure. This commit makes use of the `may_discard_trees` interface to configure that behavior. --- base/compiler/typeinfer.jl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 45c8d1567d8cb..55ae33c87e96b 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -287,8 +287,8 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) return true end -function CodeInstance( - result::InferenceResult, @nospecialize(inferred_result), valid_worlds::WorldRange) +function CodeInstance(interp::AbstractInterpreter, result::InferenceResult, + @nospecialize(inferred_result), valid_worlds::WorldRange) local const_flags::Int32 result_type = result.result @assert !(result_type isa LimitedAccuracy) @@ -297,7 +297,9 @@ function CodeInstance( # use constant calling convention rettype_const = result_type.val const_flags = 0x3 - inferred_result = nothing + if may_discard_trees(interp) + inferred_result = nothing + end else if isa(result_type, Const) rettype_const = result_type.val @@ -396,7 +398,7 @@ function cache_result!(interp::AbstractInterpreter, result::InferenceResult) # TODO: also don't store inferred code if we've previously decided to interpret this function if !already_inferred inferred_result = transform_result_for_cache(interp, linfo, valid_worlds, result) - code_cache(interp)[linfo] = ci = CodeInstance(result, inferred_result, valid_worlds) + code_cache(interp)[linfo] = ci = CodeInstance(interp, result, inferred_result, valid_worlds) if track_newly_inferred[] m = linfo.def if isa(m, Method) && m.module != Core From 05b99af2b78b0ee88e1e501d70e31430043dbbb5 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Fri, 10 Feb 2023 18:25:09 +0100 Subject: [PATCH 079/775] Increase `base/loading.jl` coverage (#48629) --- test/loading.jl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/loading.jl b/test/loading.jl index bb6500ffb4eb8..8b3b90a9b6053 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -42,6 +42,7 @@ thefname = "the fname!//\\&\1*" include_string_test_func = include_string(@__MODULE__, "include_string_test() = @__FILE__", thefname) @test include_string_test_func() == thefname @test include_string(@__MODULE__, "Base.source_path()", thefname) == Base.source_path() +@test isdir(Base.source_dir()) @test basename(@__FILE__) == "loading.jl" @test isabspath(@__FILE__) @@ -982,6 +983,8 @@ end # Package in manifest in current env not present in depot @test Base.locate_package(pkg) !== nothing + @test Base.find_package("Baz") !== nothing # coverage + pushfirst!(LOAD_PATH, joinpath(tmp, "Env1")) @test Base.locate_package(pkg) === nothing @@ -1083,6 +1086,17 @@ end cmd = `$julia $(pkgimage(P)) $(opt_level(O)) $(debug_level(D)) $(check_bounds(C)) $(inline(I)) -e $script` @test success(pipeline(cmd; stdout, stderr)) end + + cf = Base.CacheFlags(255) + @test cf.use_pkgimages + @test cf.debug_level == 3 + @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" end empty!(Base.DEPOT_PATH) From 93022852b0792ade052b71624630d4ddab99dc96 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Sat, 11 Feb 2023 03:13:47 +0800 Subject: [PATCH 080/775] Limit the `src` style in structured broadcast (#48467) And make `StructuredMatrixStyle` less complex. We can fuse the style checking code via `BroadcastStyle` --- .../LinearAlgebra/src/structuredbroadcast.jl | 78 +++++++++++-------- .../LinearAlgebra/test/structuredbroadcast.jl | 2 + 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/stdlib/LinearAlgebra/src/structuredbroadcast.jl b/stdlib/LinearAlgebra/src/structuredbroadcast.jl index ccf95f88a1bee..1fa73ba4d9c57 100644 --- a/stdlib/LinearAlgebra/src/structuredbroadcast.jl +++ b/stdlib/LinearAlgebra/src/structuredbroadcast.jl @@ -9,35 +9,41 @@ StructuredMatrixStyle{T}(::Val{2}) where {T} = StructuredMatrixStyle{T}() StructuredMatrixStyle{T}(::Val{N}) where {T,N} = Broadcast.DefaultArrayStyle{N}() const StructuredMatrix = Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal,LowerTriangular,UnitLowerTriangular,UpperTriangular,UnitUpperTriangular} -Broadcast.BroadcastStyle(::Type{T}) where {T<:StructuredMatrix} = StructuredMatrixStyle{T}() +for ST in Base.uniontypes(StructuredMatrix) + @eval Broadcast.BroadcastStyle(::Type{<:$ST}) = $(StructuredMatrixStyle{ST}()) +end # Promotion of broadcasts between structured matrices. This is slightly unusual # as we define them symmetrically. This allows us to have a fallback to DefaultArrayStyle{2}(). # Diagonal can cavort with all the other structured matrix types. # Bidiagonal doesn't know if it's upper or lower, so it becomes Tridiagonal -Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Diagonal}, ::StructuredMatrixStyle{<:Diagonal}) = +Broadcast.BroadcastStyle(::StructuredMatrixStyle{Diagonal}, ::StructuredMatrixStyle{Diagonal}) = StructuredMatrixStyle{Diagonal}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Diagonal}, ::StructuredMatrixStyle{<:Union{Bidiagonal,SymTridiagonal,Tridiagonal}}) = +Broadcast.BroadcastStyle(::StructuredMatrixStyle{Diagonal}, ::StructuredMatrixStyle{Bidiagonal}) = + StructuredMatrixStyle{Bidiagonal}() +Broadcast.BroadcastStyle(::StructuredMatrixStyle{Diagonal}, ::StructuredMatrixStyle{<:Union{SymTridiagonal,Tridiagonal}}) = StructuredMatrixStyle{Tridiagonal}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Diagonal}, ::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLowerTriangular}}) = +Broadcast.BroadcastStyle(::StructuredMatrixStyle{Diagonal}, ::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLowerTriangular}}) = StructuredMatrixStyle{LowerTriangular}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Diagonal}, ::StructuredMatrixStyle{<:Union{UpperTriangular,UnitUpperTriangular}}) = +Broadcast.BroadcastStyle(::StructuredMatrixStyle{Diagonal}, ::StructuredMatrixStyle{<:Union{UpperTriangular,UnitUpperTriangular}}) = StructuredMatrixStyle{UpperTriangular}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Bidiagonal}, ::StructuredMatrixStyle{<:Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal}}) = +Broadcast.BroadcastStyle(::StructuredMatrixStyle{Bidiagonal}, ::StructuredMatrixStyle{Diagonal}) = + StructuredMatrixStyle{Bidiagonal}() +Broadcast.BroadcastStyle(::StructuredMatrixStyle{Bidiagonal}, ::StructuredMatrixStyle{<:Union{Bidiagonal,SymTridiagonal,Tridiagonal}}) = StructuredMatrixStyle{Tridiagonal}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:SymTridiagonal}, ::StructuredMatrixStyle{<:Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal}}) = +Broadcast.BroadcastStyle(::StructuredMatrixStyle{SymTridiagonal}, ::StructuredMatrixStyle{<:Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal}}) = StructuredMatrixStyle{Tridiagonal}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Tridiagonal}, ::StructuredMatrixStyle{<:Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal}}) = +Broadcast.BroadcastStyle(::StructuredMatrixStyle{Tridiagonal}, ::StructuredMatrixStyle{<:Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal}}) = StructuredMatrixStyle{Tridiagonal}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:LowerTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,LowerTriangular,UnitLowerTriangular}}) = +Broadcast.BroadcastStyle(::StructuredMatrixStyle{LowerTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,LowerTriangular,UnitLowerTriangular}}) = StructuredMatrixStyle{LowerTriangular}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:UpperTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,UpperTriangular,UnitUpperTriangular}}) = +Broadcast.BroadcastStyle(::StructuredMatrixStyle{UpperTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,UpperTriangular,UnitUpperTriangular}}) = StructuredMatrixStyle{UpperTriangular}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:UnitLowerTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,LowerTriangular,UnitLowerTriangular}}) = +Broadcast.BroadcastStyle(::StructuredMatrixStyle{UnitLowerTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,LowerTriangular,UnitLowerTriangular}}) = StructuredMatrixStyle{LowerTriangular}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:UnitUpperTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,UpperTriangular,UnitUpperTriangular}}) = +Broadcast.BroadcastStyle(::StructuredMatrixStyle{UnitUpperTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,UpperTriangular,UnitUpperTriangular}}) = StructuredMatrixStyle{UpperTriangular}() Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLowerTriangular}}, ::StructuredMatrixStyle{<:Union{UpperTriangular,UnitUpperTriangular}}) = @@ -45,17 +51,17 @@ Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLow Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Union{UpperTriangular,UnitUpperTriangular}}, ::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLowerTriangular}}) = StructuredMatrixStyle{Matrix}() -# Make sure that `StructuredMatrixStyle{<:Matrix}` doesn't ever end up falling +# Make sure that `StructuredMatrixStyle{Matrix}` doesn't ever end up falling # through and give back `DefaultArrayStyle{2}` -Broadcast.BroadcastStyle(T::StructuredMatrixStyle{<:Matrix}, ::StructuredMatrixStyle) = T -Broadcast.BroadcastStyle(::StructuredMatrixStyle, T::StructuredMatrixStyle{<:Matrix}) = T -Broadcast.BroadcastStyle(T::StructuredMatrixStyle{<:Matrix}, ::StructuredMatrixStyle{<:Matrix}) = T +Broadcast.BroadcastStyle(T::StructuredMatrixStyle{Matrix}, ::StructuredMatrixStyle) = T +Broadcast.BroadcastStyle(::StructuredMatrixStyle, T::StructuredMatrixStyle{Matrix}) = T +Broadcast.BroadcastStyle(T::StructuredMatrixStyle{Matrix}, ::StructuredMatrixStyle{Matrix}) = T # All other combinations fall back to the default style Broadcast.BroadcastStyle(::StructuredMatrixStyle, ::StructuredMatrixStyle) = DefaultArrayStyle{2}() # And a definition akin to similar using the structured type: -structured_broadcast_alloc(bc, ::Type{<:Diagonal}, ::Type{ElType}, n) where {ElType} = +structured_broadcast_alloc(bc, ::Type{Diagonal}, ::Type{ElType}, n) where {ElType} = Diagonal(Array{ElType}(undef, n)) # Bidiagonal is tricky as we need to know if it's upper or lower. The promotion # system will return Tridiagonal when there's more than one Bidiagonal, but when @@ -67,9 +73,9 @@ merge_uplos(a, b) = a == b ? a : 'T' find_uplo(a::Bidiagonal) = a.uplo find_uplo(a) = nothing -find_uplo(bc::Broadcasted) = mapreduce(find_uplo, merge_uplos, bc.args, init=nothing) +find_uplo(bc::Broadcasted) = mapfoldl(find_uplo, merge_uplos, Broadcast.cat_nested(bc), init=nothing) -function structured_broadcast_alloc(bc, ::Type{<:Bidiagonal}, ::Type{ElType}, n) where {ElType} +function structured_broadcast_alloc(bc, ::Type{Bidiagonal}, ::Type{ElType}, n) where {ElType} uplo = n > 0 ? find_uplo(bc) : 'U' n1 = max(n - 1, 0) if uplo == 'T' @@ -77,19 +83,19 @@ function structured_broadcast_alloc(bc, ::Type{<:Bidiagonal}, ::Type{ElType}, n) end return Bidiagonal(Array{ElType}(undef, n),Array{ElType}(undef, n1), uplo) end -structured_broadcast_alloc(bc, ::Type{<:SymTridiagonal}, ::Type{ElType}, n) where {ElType} = +structured_broadcast_alloc(bc, ::Type{SymTridiagonal}, ::Type{ElType}, n) where {ElType} = SymTridiagonal(Array{ElType}(undef, n),Array{ElType}(undef, n-1)) -structured_broadcast_alloc(bc, ::Type{<:Tridiagonal}, ::Type{ElType}, n) where {ElType} = +structured_broadcast_alloc(bc, ::Type{Tridiagonal}, ::Type{ElType}, n) where {ElType} = Tridiagonal(Array{ElType}(undef, n-1),Array{ElType}(undef, n),Array{ElType}(undef, n-1)) -structured_broadcast_alloc(bc, ::Type{<:LowerTriangular}, ::Type{ElType}, n) where {ElType} = +structured_broadcast_alloc(bc, ::Type{LowerTriangular}, ::Type{ElType}, n) where {ElType} = LowerTriangular(Array{ElType}(undef, n, n)) -structured_broadcast_alloc(bc, ::Type{<:UpperTriangular}, ::Type{ElType}, n) where {ElType} = +structured_broadcast_alloc(bc, ::Type{UpperTriangular}, ::Type{ElType}, n) where {ElType} = UpperTriangular(Array{ElType}(undef, n, n)) -structured_broadcast_alloc(bc, ::Type{<:UnitLowerTriangular}, ::Type{ElType}, n) where {ElType} = +structured_broadcast_alloc(bc, ::Type{UnitLowerTriangular}, ::Type{ElType}, n) where {ElType} = UnitLowerTriangular(Array{ElType}(undef, n, n)) -structured_broadcast_alloc(bc, ::Type{<:UnitUpperTriangular}, ::Type{ElType}, n) where {ElType} = +structured_broadcast_alloc(bc, ::Type{UnitUpperTriangular}, ::Type{ElType}, n) where {ElType} = UnitUpperTriangular(Array{ElType}(undef, n, n)) -structured_broadcast_alloc(bc, ::Type{<:Matrix}, ::Type{ElType}, n) where {ElType} = +structured_broadcast_alloc(bc, ::Type{Matrix}, ::Type{ElType}, n) where {ElType} = Matrix(Array{ElType}(undef, n, n)) # A _very_ limited list of structure-preserving functions known at compile-time. This list is @@ -152,8 +158,16 @@ function Base.similar(bc::Broadcasted{StructuredMatrixStyle{T}}, ::Type{ElType}) return similar(convert(Broadcasted{DefaultArrayStyle{ndims(bc)}}, bc), ElType) end +isvalidstructbc(dest, bc::Broadcasted{T}) where {T<:StructuredMatrixStyle} = + Broadcast.combine_styles(dest, bc) === Broadcast.combine_styles(dest) && + (isstructurepreserving(bc) || fzeropreserving(bc)) + +isvalidstructbc(dest::Bidiagonal, bc::Broadcasted{StructuredMatrixStyle{Bidiagonal}}) = + (size(dest, 1) < 2 || find_uplo(bc) == dest.uplo) && + (isstructurepreserving(bc) || fzeropreserving(bc)) + function copyto!(dest::Diagonal, bc::Broadcasted{<:StructuredMatrixStyle}) - !isstructurepreserving(bc) && !fzeropreserving(bc) && return copyto!(dest, convert(Broadcasted{Nothing}, bc)) + isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for i in axs[1] @@ -163,7 +177,7 @@ function copyto!(dest::Diagonal, bc::Broadcasted{<:StructuredMatrixStyle}) end function copyto!(dest::Bidiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) - !isstructurepreserving(bc) && !fzeropreserving(bc) && return copyto!(dest, convert(Broadcasted{Nothing}, bc)) + isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for i in axs[1] @@ -182,7 +196,7 @@ function copyto!(dest::Bidiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) end function copyto!(dest::SymTridiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) - !isstructurepreserving(bc) && !fzeropreserving(bc) && return copyto!(dest, convert(Broadcasted{Nothing}, bc)) + isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for i in axs[1] @@ -197,7 +211,7 @@ function copyto!(dest::SymTridiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) end function copyto!(dest::Tridiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) - !isstructurepreserving(bc) && !fzeropreserving(bc) && return copyto!(dest, convert(Broadcasted{Nothing}, bc)) + isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for i in axs[1] @@ -211,7 +225,7 @@ function copyto!(dest::Tridiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) end function copyto!(dest::LowerTriangular, bc::Broadcasted{<:StructuredMatrixStyle}) - !isstructurepreserving(bc) && !fzeropreserving(bc) && return copyto!(dest, convert(Broadcasted{Nothing}, bc)) + isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for j in axs[2] @@ -223,7 +237,7 @@ function copyto!(dest::LowerTriangular, bc::Broadcasted{<:StructuredMatrixStyle} end function copyto!(dest::UpperTriangular, bc::Broadcasted{<:StructuredMatrixStyle}) - !isstructurepreserving(bc) && !fzeropreserving(bc) && return copyto!(dest, convert(Broadcasted{Nothing}, bc)) + isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for j in axs[2] diff --git a/stdlib/LinearAlgebra/test/structuredbroadcast.jl b/stdlib/LinearAlgebra/test/structuredbroadcast.jl index 4855446bc194b..2ca1904b2ff2d 100644 --- a/stdlib/LinearAlgebra/test/structuredbroadcast.jl +++ b/stdlib/LinearAlgebra/test/structuredbroadcast.jl @@ -100,6 +100,8 @@ end @test_throws ArgumentError broadcast!(+, copy(T), T, A) == Tridiagonal(broadcast(*, T, A)) @test_throws ArgumentError broadcast!(+, copy(◣), ◣, A) == LowerTriangular(broadcast(*, ◣, A)) @test_throws ArgumentError broadcast!(+, copy(◥), ◥, A) == UpperTriangular(broadcast(*, ◥, A)) + @test_throws ArgumentError broadcast!(*, copy(◥), ◣, 2) + @test_throws ArgumentError broadcast!(*, copy(Bu), Bl, 2) end @testset "map[!] over combinations of structured matrices" begin From 1ef78a214c26d3570783067f30cc00cb1a1eba36 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Fri, 10 Feb 2023 11:17:29 -0800 Subject: [PATCH 081/775] Document WeakRef type (#48565) Fixes #26745. Based on suggestion by stevengj (https://github.com/JuliaLang/julia/issues/26745#issuecomment-379513967). --- base/gcutils.jl | 31 +++++++++++++++++++++++++++++++ base/weakkeydict.jl | 2 ++ doc/src/base/base.md | 1 + 3 files changed, 34 insertions(+) diff --git a/base/gcutils.jl b/base/gcutils.jl index 0e5d4c16e550a..a7cee0762bebe 100644 --- a/base/gcutils.jl +++ b/base/gcutils.jl @@ -1,5 +1,36 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + WeakRef(x) + +`w = WeakRef(x)` constructs a [weak reference](https://en.wikipedia.org/wiki/Weak_reference) +to the Julia value `x`: although `w` contains a reference to `x`, it does not prevent `x` from being +garbage collected. `w.value` is either `x` (if `x` has not been garbage-collected yet) or `nothing` +(if `x` has been garbage-collected). + +```jldoctest +julia> x = "a string" +"a string" + +julia> w = WeakRef(x) +WeakRef("a string") + +julia> GC.gc() + +julia> w # a reference is maintained via `x` +WeakRef("a string") + +julia> x = nothing # clear reference + +julia> GC.gc() + +julia> w +WeakRef(nothing) +``` +""" +WeakRef + ==(w::WeakRef, v::WeakRef) = isequal(w.value, v.value) ==(w::WeakRef, v) = isequal(w.value, v) ==(w, v::WeakRef) = isequal(w, v.value) diff --git a/base/weakkeydict.jl b/base/weakkeydict.jl index 0a9987671ea9b..7cfc65ab0c66b 100644 --- a/base/weakkeydict.jl +++ b/base/weakkeydict.jl @@ -12,6 +12,8 @@ referenced in a hash table. See [`Dict`](@ref) for further help. Note, unlike [`Dict`](@ref), `WeakKeyDict` does not convert keys on insertion, as this would imply the key object was unreferenced anywhere before insertion. + +See also [`WeakRef`](@ref). """ mutable struct WeakKeyDict{K,V} <: AbstractDict{K,V} ht::Dict{WeakRef,V} diff --git a/doc/src/base/base.md b/doc/src/base/base.md index 7922dd7d67861..86218450bee4b 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -152,6 +152,7 @@ Base.promote Base.oftype Base.widen Base.identity +Base.WeakRef ``` ## Properties of Types From 500f561f728d0be3d5f6916f274cdf14387cf375 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Fri, 10 Feb 2023 16:20:27 -0300 Subject: [PATCH 082/775] Allow for use 60% of constrained/total system memory (#48614) Remove the high watermark logic, because it doesn't really make sense, and allow for use of 60% of system memory before aggressive GC kicks in. Should fix #48473 --- src/gc.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/gc.c b/src/gc.c index 8ca028ed213d5..59cd18e8bd87f 100644 --- a/src/gc.c +++ b/src/gc.c @@ -3252,14 +3252,9 @@ void jl_gc_init(void) uint64_t constrained_mem = uv_get_constrained_memory(); if (constrained_mem > 0 && constrained_mem < total_mem) total_mem = constrained_mem; + max_total_memory = total_mem / 10 * 6; #endif - // We allocate with abandon until we get close to the free memory on the machine. - uint64_t free_mem = uv_get_available_memory(); - uint64_t high_water_mark = free_mem / 10 * 7; // 70% high water mark - - if (high_water_mark < max_total_memory) - max_total_memory = high_water_mark; t_start = jl_hrtime(); } From 98ad272797d6583b0ee70021d8990c6e0beb5960 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Fri, 10 Feb 2023 16:35:44 -0300 Subject: [PATCH 083/775] Correct bug in read_sub (#48188) Fix #48180 (although this function could probably be deleted now, since it only has one use it could be trivially inlined into) --- base/iobuffer.jl | 2 +- test/iobuffer.jl | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/base/iobuffer.jl b/base/iobuffer.jl index e08a019d84a2c..2516201943a5e 100644 --- a/base/iobuffer.jl +++ b/base/iobuffer.jl @@ -203,7 +203,7 @@ function read_sub(from::GenericIOBuffer, a::AbstractArray{T}, offs, nel) where T GC.@preserve a unsafe_read(from, pointer(a, offs), nb) else for i = offs:offs+nel-1 - a[i] = read(to, T) + a[i] = read(from, T) end end return a diff --git a/test/iobuffer.jl b/test/iobuffer.jl index d8211aa7086b3..ec77903b4a5b8 100644 --- a/test/iobuffer.jl +++ b/test/iobuffer.jl @@ -348,3 +348,12 @@ end @testset "bytesavailable devnull" begin @test bytesavailable(devnull) == 0 end + +@testset "#48188 read_sub for non Array AbstractArray" begin + a = [0,0,0] + v = @view a[1:2] + io = IOBuffer() + write(io,1) + seek(io,0) + @test Base.read_sub(io,v,1,1) == [1,0] +end From 7d9cf6d643b0703094a5474a35b538adaa3befc3 Mon Sep 17 00:00:00 2001 From: Udoh Jeremiah Date: Fri, 10 Feb 2023 20:42:41 +0100 Subject: [PATCH 084/775] provide documentation for the as keyword (#47838) Co-authored-by: Steven G. Johnson --- base/docs/basedocs.jl | 21 +++++++++++++++++++++ doc/src/base/base.md | 6 ++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index c6e0f867723a4..fa4d9992b6000 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -59,6 +59,27 @@ See the [manual section about modules](@ref modules) for details. """ kw"export" +""" + as + +`as` is used as a keyword to rename an identifier brought into scope by +`import` or `using`, for the purpose of working around name conflicts as +well as for shortening names. (Outside of `import` or `using` statements, +`as` is not a keyword and can be used as an ordinary identifier.) + +`import LinearAlgebra as LA` brings the imported `LinearAlgebra` standard library +into scope as `LA`. + +`import LinearAlgebra: eigen as eig, cholesky as chol` brings the `eigen` and `cholesky` methods +from `LinearAlgebra` into scope as `eig` and `chol` respectively. + +`as` works with `using` only when individual identifiers are brought into scope. +For example, `using LinearAlgebra: eigen as eig` or `using LinearAlgebra: eigen as eig, cholesky as chol` works, +but `using LinearAlgebra as LA` is invalid syntax, since it is nonsensical to +rename *all* exported names from `LinearAlgebra` to `LA`. +""" +kw"as" + """ abstract type diff --git a/doc/src/base/base.md b/doc/src/base/base.md index 86218450bee4b..2159a9ad9a9fc 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -59,14 +59,16 @@ However, you can create variables with names: Finally: `where` is parsed as an infix operator for writing parametric method and type definitions; `in` and `isa` are parsed as infix operators; -and `outer` is parsed as a keyword when used to modify the scope of a variable in an iteration specification of a `for` loop. -Creation of variables named `where`, `in`, `isa` or `outer` is allowed though. +`outer` is parsed as a keyword when used to modify the scope of a variable in an iteration specification of a `for` loop; +and `as` is used as a keyword to rename an identifier brought into scope by `import` or `using`. +Creation of variables named `where`, `in`, `isa`, `outer` and `as` is allowed, though. ```@docs module export import using +as baremodule function macro From 2b43930fea0393810a46c2f650ae0939a3ccb908 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 10 Feb 2023 20:49:58 +0100 Subject: [PATCH 085/775] document that `get` and `replace` accept a type as first argument (#47692) --- base/dict.jl | 8 ++++---- base/set.jl | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/base/dict.jl b/base/dict.jl index 57598522224dd..b3d2527aaa6d9 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -424,7 +424,7 @@ Dict{String, Int64} with 4 entries: get!(collection, key, default) """ - get!(f::Function, collection, key) + get!(f::Union{Function, Type}, collection, key) Return the value stored for the given key, or if no mapping for the key is present, store `key => f()`, and return `f()`. @@ -450,7 +450,7 @@ Dict{Int64, Int64} with 1 entry: 2 => 4 ``` """ -get!(f::Function, collection, key) +get!(f::Callable, collection, key) function get!(default::Callable, h::Dict{K,V}, key0) where V where K key = convert(K, key0) @@ -513,7 +513,7 @@ function get(h::Dict{K,V}, key, default) where V where K end """ - get(f::Function, collection, key) + get(f::Union{Function, Type}, collection, key) Return the value stored for the given key, or if no mapping for the key is present, return `f()`. Use [`get!`](@ref) to also store the default value in the dictionary. @@ -527,7 +527,7 @@ get(dict, key) do end ``` """ -get(::Function, collection, key) +get(::Callable, collection, key) function get(default::Callable, h::Dict{K,V}, key) where V where K index = ht_keyindex(h, key) diff --git a/base/set.jl b/base/set.jl index 6f8580e222e40..5be7eaf004352 100644 --- a/base/set.jl +++ b/base/set.jl @@ -617,7 +617,7 @@ function replace_pairs!(res, A, count::Int, old_new::Tuple{Vararg{Pair}}) end """ - replace!(new::Function, A; [count::Integer]) + replace!(new::Union{Function, Type}, A; [count::Integer]) Replace each element `x` in collection `A` by `new(x)`. If `count` is specified, then replace at most `count` values in total @@ -710,7 +710,7 @@ subtract_singletontype(::Type{T}, x::Pair{K}, y::Pair...) where {T, K} = subtract_singletontype(subtract_singletontype(T, y...), x) """ - replace(new::Function, A; [count::Integer]) + replace(new::Union{Function, Type}, A; [count::Integer]) Return a copy of `A` where each value `x` in `A` is replaced by `new(x)`. If `count` is specified, then replace at most `count` values in total From f7875a2d0fbef82585abbbbb3d3597c9fa4398cd Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 10 Feb 2023 20:50:26 +0100 Subject: [PATCH 086/775] mktempdir docstring: clarify when `X` characters may be replaced (#47691) --- base/file.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/base/file.jl b/base/file.jl index baccbdeaa5b07..8754f3fad9c6d 100644 --- a/base/file.jl +++ b/base/file.jl @@ -675,8 +675,9 @@ mktemp(parent) mktempdir(parent=tempdir(); prefix=$(repr(temp_prefix)), cleanup=true) -> path Create a temporary directory in the `parent` directory with a name -constructed from the given prefix and a random suffix, and return its path. -Additionally, any trailing `X` characters may be replaced with random characters. +constructed from the given `prefix` and a random suffix, and return its path. +Additionally, on some platforms, any trailing `'X'` characters in `prefix` may be replaced +with random characters. If `parent` does not exist, throw an error. The `cleanup` option controls whether the temporary directory is automatically deleted when the process exits. From a4a148becaf1e106c7e461d18f7738da3632e9fd Mon Sep 17 00:00:00 2001 From: Raymond Huffman Date: Fri, 10 Feb 2023 14:52:27 -0500 Subject: [PATCH 087/775] Support building some dependencies with Emscripten (#47680) --- deps/dsfmt.mk | 5 ++++- deps/gmp.mk | 3 +++ deps/libuv.mk | 16 ++++++++++++++++ deps/mpfr.mk | 3 +++ deps/pcre.mk | 9 ++++++++- 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/deps/dsfmt.mk b/deps/dsfmt.mk index 203c5f1917c91..da57799053933 100644 --- a/deps/dsfmt.mk +++ b/deps/dsfmt.mk @@ -5,10 +5,13 @@ ifneq ($(USE_BINARYBUILDER_DSFMT),1) DSFMT_CFLAGS := $(CFLAGS) -DNDEBUG -DDSFMT_MEXP=19937 $(fPIC) -DDSFMT_DO_NOT_USE_OLD_NAMES -DDSFMT_SHLIB $(SANITIZE_OPTS) DSFMT_CFLAGS += -O3 -finline-functions -fomit-frame-pointer -fno-strict-aliasing \ - --param max-inline-insns-single=1800 -Wall -std=c99 -shared + -Wall -std=c99 -shared ifeq ($(ARCH), x86_64) DSFMT_CFLAGS += -msse2 -DHAVE_SSE2 endif +ifneq ($(OS), emscripten) +DSFMT_CFLAGS += --param max-inline-insns-single=1800 +endif $(SRCCACHE)/dsfmt-$(DSFMT_VER).tar.gz: | $(SRCCACHE) $(JLDOWNLOAD) $@ https://github.com/MersenneTwister-Lab/dSFMT/archive/v$(DSFMT_VER).tar.gz diff --git a/deps/gmp.mk b/deps/gmp.mk index 2354a6ca44a9f..12ba15f8aa0f6 100644 --- a/deps/gmp.mk +++ b/deps/gmp.mk @@ -19,6 +19,9 @@ ifeq ($(BUILD_OS),WINNT) GMP_CONFIGURE_OPTS += --srcdir="$(subst \,/,$(call mingw_to_dos,$(SRCCACHE)/gmp-$(GMP_VER)))" endif +ifeq ($(OS),emscripten) +GMP_CONFIGURE_OPTS += CFLAGS="-fPIC" +endif $(SRCCACHE)/gmp-$(GMP_VER).tar.bz2: | $(SRCCACHE) $(JLDOWNLOAD) $@ https://gmplib.org/download/gmp/$(notdir $@) diff --git a/deps/libuv.mk b/deps/libuv.mk index cdcd12d8db4fa..eacabac55e34f 100644 --- a/deps/libuv.mk +++ b/deps/libuv.mk @@ -18,6 +18,21 @@ LIBUV_BUILDDIR := $(BUILDDIR)/$(LIBUV_SRC_DIR) ifneq ($(CLDFLAGS)$(SANITIZE_LDFLAGS),) $(LIBUV_BUILDDIR)/build-configured: LDFLAGS:=$(LDFLAGS) $(CLDFLAGS) $(SANITIZE_LDFLAGS) endif + +ifeq ($(OS), emscripten) +$(LIBUV_BUILDDIR)/build-configured: $(SRCCACHE)/$(LIBUV_SRC_DIR)/source-extracted + mkdir -p $(dir $@) + cd $(dir $@) && cmake -E env \ + CMAKE_C_FLAGS="-pthread" \ + CMAKE_SHARED_LINKER_FLAGS="-sTOTAL_MEMORY=65536000 -pthread" \ + CMAKE_EXE_LINKER_FLAGS="-sTOTAL_MEMORY=65536000 -pthread" \ + emcmake cmake $(dir $<) $(CMAKE_COMMON) -DBUILD_TESTING=OFF + echo 1 > $@ + +$(LIBUV_BUILDDIR)/build-compiled: $(LIBUV_BUILDDIR)/build-configured + emmake $(MAKE) -C $(dir $<) $(UV_MFLAGS) + echo 1 > $@ +else $(LIBUV_BUILDDIR)/build-configured: $(SRCCACHE)/$(LIBUV_SRC_DIR)/source-extracted touch -c $(SRCCACHE)/$(LIBUV_SRC_DIR)/aclocal.m4 # touch a few files to prevent autogen from getting called touch -c $(SRCCACHE)/$(LIBUV_SRC_DIR)/Makefile.in @@ -30,6 +45,7 @@ $(LIBUV_BUILDDIR)/build-configured: $(SRCCACHE)/$(LIBUV_SRC_DIR)/source-extracte $(LIBUV_BUILDDIR)/build-compiled: $(LIBUV_BUILDDIR)/build-configured $(MAKE) -C $(dir $<) $(UV_MFLAGS) echo 1 > $@ +endif $(LIBUV_BUILDDIR)/build-checked: $(LIBUV_BUILDDIR)/build-compiled ifeq ($(OS),$(BUILD_OS)) diff --git a/deps/mpfr.mk b/deps/mpfr.mk index 36a4f77c6a929..dc10764002bd4 100644 --- a/deps/mpfr.mk +++ b/deps/mpfr.mk @@ -20,6 +20,9 @@ ifeq ($(SANITIZE),1) MPFR_CONFIGURE_OPTS += --host=none-unknown-linux endif +ifeq ($(OS),emscripten) +MPFR_CONFIGURE_OPTS += CFLAGS="-fPIC" +endif $(SRCCACHE)/mpfr-$(MPFR_VER).tar.bz2: | $(SRCCACHE) $(JLDOWNLOAD) $@ https://www.mpfr.org/mpfr-$(MPFR_VER)/$(notdir $@) diff --git a/deps/pcre.mk b/deps/pcre.mk index 5ff91b6bc44ac..cd1180d992885 100644 --- a/deps/pcre.mk +++ b/deps/pcre.mk @@ -6,6 +6,13 @@ ifneq ($(USE_BINARYBUILDER_PCRE),1) PCRE_CFLAGS := -O3 PCRE_LDFLAGS := $(RPATH_ESCAPED_ORIGIN) +ifeq ($(OS),emscripten) +PCRE_CFLAGS += -fPIC +PCRE_JIT = --disable-jit +else +PCRE_JIT = --enable-jit +endif + $(SRCCACHE)/pcre2-$(PCRE_VER).tar.bz2: | $(SRCCACHE) $(JLDOWNLOAD) $@ https://github.com/PCRE2Project/pcre2/releases/download/pcre2-$(PCRE_VER)/pcre2-$(PCRE_VER).tar.bz2 @@ -20,7 +27,7 @@ checksum-pcre: $(SRCCACHE)/pcre2-$(PCRE_VER).tar.bz2 $(BUILDDIR)/pcre2-$(PCRE_VER)/build-configured: $(SRCCACHE)/pcre2-$(PCRE_VER)/source-extracted mkdir -p $(dir $@) cd $(dir $@) && \ - $(dir $<)/configure $(CONFIGURE_COMMON) --enable-jit --includedir=$(build_includedir) CFLAGS="$(CFLAGS) $(PCRE_CFLAGS) -g -O0" LDFLAGS="$(LDFLAGS) $(PCRE_LDFLAGS)" + $(dir $<)/configure $(CONFIGURE_COMMON) $(PCRE_JIT) --includedir=$(build_includedir) CFLAGS="$(CFLAGS) $(PCRE_CFLAGS) -g -O0" LDFLAGS="$(LDFLAGS) $(PCRE_LDFLAGS)" echo 1 > $@ $(BUILDDIR)/pcre2-$(PCRE_VER)/build-compiled: $(BUILDDIR)/pcre2-$(PCRE_VER)/build-configured From 7e57dc7bcf60eab24d89497d5ac4077c2603d120 Mon Sep 17 00:00:00 2001 From: William Moses Date: Fri, 10 Feb 2023 15:02:21 -0500 Subject: [PATCH 088/775] Fix final gc lowering on dynamically sized allocation (#48620) --- src/llvm-final-gc-lowering.cpp | 49 +++++++++++++++++++------------ src/llvm-pass-helpers.cpp | 19 ++++++++++++ src/llvm-pass-helpers.h | 3 ++ test/llvmpasses/final-lower-gc.ll | 15 ++++++++++ 4 files changed, 67 insertions(+), 19 deletions(-) diff --git a/src/llvm-final-gc-lowering.cpp b/src/llvm-final-gc-lowering.cpp index 3b8533c6d0115..e4e8ff69ee2da 100644 --- a/src/llvm-final-gc-lowering.cpp +++ b/src/llvm-final-gc-lowering.cpp @@ -48,6 +48,7 @@ struct FinalLowerGC: private JuliaPassContext { Function *queueRootFunc; Function *poolAllocFunc; Function *bigAllocFunc; + Function *allocTypedFunc; Instruction *pgcstack; // Lowers a `julia.new_gc_frame` intrinsic. @@ -208,26 +209,35 @@ Value *FinalLowerGC::lowerGCAllocBytes(CallInst *target, Function &F) { ++GCAllocBytesCount; assert(target->arg_size() == 2); - auto sz = (size_t)cast(target->getArgOperand(1))->getZExtValue(); - // This is strongly architecture and OS dependent - int osize; - int offset = jl_gc_classify_pools(sz, &osize); + CallInst *newI; + IRBuilder<> builder(target); builder.SetCurrentDebugLocation(target->getDebugLoc()); auto ptls = target->getArgOperand(0); - CallInst *newI; Attribute derefAttr; - if (offset < 0) { - newI = builder.CreateCall( - bigAllocFunc, - { ptls, ConstantInt::get(getSizeTy(F.getContext()), sz + sizeof(void*)) }); - derefAttr = Attribute::getWithDereferenceableBytes(F.getContext(), sz + sizeof(void*)); - } - else { - auto pool_offs = ConstantInt::get(Type::getInt32Ty(F.getContext()), offset); - auto pool_osize = ConstantInt::get(Type::getInt32Ty(F.getContext()), osize); - newI = builder.CreateCall(poolAllocFunc, { ptls, pool_offs, pool_osize }); - derefAttr = Attribute::getWithDereferenceableBytes(F.getContext(), osize); + + if (auto CI = dyn_cast(target->getArgOperand(1))) { + size_t sz = (size_t)CI->getZExtValue(); + // This is strongly architecture and OS dependent + int osize; + int offset = jl_gc_classify_pools(sz, &osize); + if (offset < 0) { + newI = builder.CreateCall( + bigAllocFunc, + { ptls, ConstantInt::get(getSizeTy(F.getContext()), sz + sizeof(void*)) }); + derefAttr = Attribute::getWithDereferenceableBytes(F.getContext(), sz + sizeof(void*)); + } + else { + auto pool_offs = ConstantInt::get(Type::getInt32Ty(F.getContext()), offset); + auto pool_osize = ConstantInt::get(Type::getInt32Ty(F.getContext()), osize); + newI = builder.CreateCall(poolAllocFunc, { ptls, pool_offs, pool_osize }); + derefAttr = Attribute::getWithDereferenceableBytes(F.getContext(), osize); + } + } else { + auto size = builder.CreateZExtOrTrunc(target->getArgOperand(1), getSizeTy(F.getContext())); + size = builder.CreateAdd(size, ConstantInt::get(getSizeTy(F.getContext()), sizeof(void*))); + newI = builder.CreateCall(allocTypedFunc, { ptls, size, ConstantPointerNull::get(Type::getInt8PtrTy(F.getContext())) }); + derefAttr = Attribute::getWithDereferenceableBytes(F.getContext(), sizeof(void*)); } newI->setAttributes(newI->getCalledFunction()->getAttributes()); newI->addRetAttr(derefAttr); @@ -243,8 +253,9 @@ bool FinalLowerGC::doInitialization(Module &M) { queueRootFunc = getOrDeclare(jl_well_known::GCQueueRoot); poolAllocFunc = getOrDeclare(jl_well_known::GCPoolAlloc); bigAllocFunc = getOrDeclare(jl_well_known::GCBigAlloc); + allocTypedFunc = getOrDeclare(jl_well_known::GCAllocTyped); - GlobalValue *functionList[] = {queueRootFunc, poolAllocFunc, bigAllocFunc}; + GlobalValue *functionList[] = {queueRootFunc, poolAllocFunc, bigAllocFunc, allocTypedFunc}; unsigned j = 0; for (unsigned i = 0; i < sizeof(functionList) / sizeof(void*); i++) { if (!functionList[i]) @@ -260,8 +271,8 @@ bool FinalLowerGC::doInitialization(Module &M) { bool FinalLowerGC::doFinalization(Module &M) { - GlobalValue *functionList[] = {queueRootFunc, poolAllocFunc, bigAllocFunc}; - queueRootFunc = poolAllocFunc = bigAllocFunc = nullptr; + GlobalValue *functionList[] = {queueRootFunc, poolAllocFunc, bigAllocFunc, allocTypedFunc}; + queueRootFunc = poolAllocFunc = bigAllocFunc = allocTypedFunc = nullptr; auto used = M.getGlobalVariable("llvm.compiler.used"); if (!used) return false; diff --git a/src/llvm-pass-helpers.cpp b/src/llvm-pass-helpers.cpp index ea390f01010fd..e69a7d32bda9b 100644 --- a/src/llvm-pass-helpers.cpp +++ b/src/llvm-pass-helpers.cpp @@ -229,6 +229,7 @@ namespace jl_well_known { static const char *GC_BIG_ALLOC_NAME = XSTR(jl_gc_big_alloc); static const char *GC_POOL_ALLOC_NAME = XSTR(jl_gc_pool_alloc); static const char *GC_QUEUE_ROOT_NAME = XSTR(jl_gc_queue_root); + static const char *GC_ALLOC_TYPED_NAME = XSTR(jl_gc_alloc_typed); using jl_intrinsics::addGCAllocAttributes; @@ -276,4 +277,22 @@ namespace jl_well_known { func->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly); return func; }); + + const WellKnownFunctionDescription GCAllocTyped( + GC_ALLOC_TYPED_NAME, + [](const JuliaPassContext &context) { + auto allocTypedFunc = Function::Create( + FunctionType::get( + context.T_prjlvalue, + { Type::getInt8PtrTy(context.getLLVMContext()), + sizeof(size_t) == sizeof(uint32_t) ? + Type::getInt32Ty(context.getLLVMContext()) : + Type::getInt64Ty(context.getLLVMContext()), + Type::getInt8PtrTy(context.getLLVMContext()) }, + false), + Function::ExternalLinkage, + GC_ALLOC_TYPED_NAME); + allocTypedFunc->addFnAttr(Attribute::getWithAllocSizeArgs(context.getLLVMContext(), 1, None)); + return addGCAllocAttributes(allocTypedFunc, context.getLLVMContext()); + }); } diff --git a/src/llvm-pass-helpers.h b/src/llvm-pass-helpers.h index 2b2bd50cd0e4d..3388e6d485181 100644 --- a/src/llvm-pass-helpers.h +++ b/src/llvm-pass-helpers.h @@ -149,6 +149,9 @@ namespace jl_well_known { // `jl_gc_queue_root`: queues a GC root. extern const WellKnownFunctionDescription GCQueueRoot; + + // `jl_gc_alloc_typed`: allocates bytes. + extern const WellKnownFunctionDescription GCAllocTyped; } #endif diff --git a/test/llvmpasses/final-lower-gc.ll b/test/llvmpasses/final-lower-gc.ll index 95e88f9feac9e..7fc2d1b04ca2d 100644 --- a/test/llvmpasses/final-lower-gc.ll +++ b/test/llvmpasses/final-lower-gc.ll @@ -67,6 +67,21 @@ top: ret {} addrspace(10)* %v } +define {} addrspace(10)* @gc_alloc_lowering_var(i64 %size) { +top: +; CHECK-LABEL: @gc_alloc_lowering_var + %pgcstack = call {}*** @julia.get_pgcstack() + %ptls = call {}*** @julia.ptls_states() + %ptls_i8 = bitcast {}*** %ptls to i8* +; CHECK: %0 = add i64 %size, 8 +; CHECK: %v = call noalias nonnull dereferenceable(8) {} addrspace(10)* @ijl_gc_alloc_typed(i8* %ptls_i8, i64 %0, i8* null) + %v = call {} addrspace(10)* @julia.gc_alloc_bytes(i8* %ptls_i8, i64 %size) + %0 = bitcast {} addrspace(10)* %v to {} addrspace(10)* addrspace(10)* + %1 = getelementptr {} addrspace(10)*, {} addrspace(10)* addrspace(10)* %0, i64 -1 + store {} addrspace(10)* @tag, {} addrspace(10)* addrspace(10)* %1, align 8, !tbaa !0 + ret {} addrspace(10)* %v +} + !0 = !{!1, !1, i64 0} !1 = !{!"jtbaa_gcframe", !2, i64 0} !2 = !{!"jtbaa"} From a647575ff68ffec44ef7980545479915fcf3d62e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Belmant?= Date: Fri, 10 Feb 2023 21:05:05 +0100 Subject: [PATCH 089/775] Add \veedot operator (#47647) --- doc/src/base/math.md | 2 +- src/julia-parser.scm | 2 +- stdlib/REPL/src/latex_symbols.jl | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/src/base/math.md b/doc/src/base/math.md index bdf91c991183f..62368424629c6 100644 --- a/doc/src/base/math.md +++ b/doc/src/base/math.md @@ -208,5 +208,5 @@ The complete list is in the parser code: Those that are parsed like `*` (in terms of precedence) include `* / ÷ % & ⋅ ∘ × |\\| ∩ ∧ ⊗ ⊘ ⊙ ⊚ ⊛ ⊠ ⊡ ⊓ ∗ ∙ ∤ ⅋ ≀ ⊼ ⋄ ⋆ ⋇ ⋉ ⋊ ⋋ ⋌ ⋏ ⋒ ⟑ ⦸ ⦼ ⦾ ⦿ ⧶ ⧷ ⨇ ⨰ ⨱ ⨲ ⨳ ⨴ ⨵ ⨶ ⨷ ⨸ ⨻ ⨼ ⨽ ⩀ ⩃ ⩄ ⩋ ⩍ ⩎ ⩑ ⩓ ⩕ ⩘ ⩚ ⩜ ⩞ ⩟ ⩠ ⫛ ⊍ ▷ ⨝ ⟕ ⟖ ⟗` and those that are parsed like `+` include -`+ - |\|| ⊕ ⊖ ⊞ ⊟ |++| ∪ ∨ ⊔ ± ∓ ∔ ∸ ≏ ⊎ ⊻ ⊽ ⋎ ⋓ ⧺ ⧻ ⨈ ⨢ ⨣ ⨤ ⨥ ⨦ ⨧ ⨨ ⨩ ⨪ ⨫ ⨬ ⨭ ⨮ ⨹ ⨺ ⩁ ⩂ ⩅ ⩊ ⩌ ⩏ ⩐ ⩒ ⩔ ⩖ ⩗ ⩛ ⩝ ⩡ ⩢ ⩣` +`+ - |\|| ⊕ ⊖ ⊞ ⊟ |++| ∪ ∨ ⊔ ± ∓ ∔ ∸ ≏ ⊎ ⊻ ⊽ ⋎ ⋓ ⟇ ⧺ ⧻ ⨈ ⨢ ⨣ ⨤ ⨥ ⨦ ⨧ ⨨ ⨩ ⨪ ⨫ ⨬ ⨭ ⨮ ⨹ ⨺ ⩁ ⩂ ⩅ ⩊ ⩌ ⩏ ⩐ ⩒ ⩔ ⩖ ⩗ ⩛ ⩝ ⩡ ⩢ ⩣` There are many others that are related to arrows, comparisons, and powers. diff --git a/src/julia-parser.scm b/src/julia-parser.scm index 710ddc2f3bdd5..02d1c333404a6 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -20,7 +20,7 @@ (define prec-pipe> '(|.\|>| |\|>|)) (define prec-colon (append! '(: |..|) (add-dots '(… ⁝ ⋮ ⋱ ⋰ ⋯)))) (define prec-plus (append! '($) - (add-dots '(+ - − ¦ |\|| ⊕ ⊖ ⊞ ⊟ |++| ∪ ∨ ⊔ ± ∓ ∔ ∸ ≏ ⊎ ⊻ ⊽ ⋎ ⋓ ⧺ ⧻ ⨈ ⨢ ⨣ ⨤ ⨥ ⨦ ⨧ ⨨ ⨩ ⨪ ⨫ ⨬ ⨭ ⨮ ⨹ ⨺ ⩁ ⩂ ⩅ ⩊ ⩌ ⩏ ⩐ ⩒ ⩔ ⩖ ⩗ ⩛ ⩝ ⩡ ⩢ ⩣)))) + (add-dots '(+ - − ¦ |\|| ⊕ ⊖ ⊞ ⊟ |++| ∪ ∨ ⊔ ± ∓ ∔ ∸ ≏ ⊎ ⊻ ⊽ ⋎ ⋓ ⟇ ⧺ ⧻ ⨈ ⨢ ⨣ ⨤ ⨥ ⨦ ⨧ ⨨ ⨩ ⨪ ⨫ ⨬ ⨭ ⨮ ⨹ ⨺ ⩁ ⩂ ⩅ ⩊ ⩌ ⩏ ⩐ ⩒ ⩔ ⩖ ⩗ ⩛ ⩝ ⩡ ⩢ ⩣)))) (define prec-times (add-dots '(* / ⌿ ÷ % & · · ⋅ ∘ × |\\| ∩ ∧ ⊗ ⊘ ⊙ ⊚ ⊛ ⊠ ⊡ ⊓ ∗ ∙ ∤ ⅋ ≀ ⊼ ⋄ ⋆ ⋇ ⋉ ⋊ ⋋ ⋌ ⋏ ⋒ ⟑ ⦸ ⦼ ⦾ ⦿ ⧶ ⧷ ⨇ ⨰ ⨱ ⨲ ⨳ ⨴ ⨵ ⨶ ⨷ ⨸ ⨻ ⨼ ⨽ ⩀ ⩃ ⩄ ⩋ ⩍ ⩎ ⩑ ⩓ ⩕ ⩘ ⩚ ⩜ ⩞ ⩟ ⩠ ⫛ ⊍ ▷ ⨝ ⟕ ⟖ ⟗ ⨟))) (define prec-rational (add-dots '(//))) (define prec-bitshift (add-dots '(<< >> >>>))) diff --git a/stdlib/REPL/src/latex_symbols.jl b/stdlib/REPL/src/latex_symbols.jl index 87a3c289661d9..a184bcbc8e910 100644 --- a/stdlib/REPL/src/latex_symbols.jl +++ b/stdlib/REPL/src/latex_symbols.jl @@ -1289,6 +1289,7 @@ const latex_symbols = Dict( "\\bsolhsub" => "\u27c8", # reverse solidus preceding subset "\\suphsol" => "\u27c9", # superset preceding solidus "\\wedgedot" => "⟑", # and with dot + "\\veedot" => "⟇", # or with dot "\\upin" => "⟒", # element of opening upwards "\\bigbot" => "⟘", # large up tack "\\bigtop" => "⟙", # large down tack From 3283e2047483f75f932c298d37934a1bfa474a50 Mon Sep 17 00:00:00 2001 From: Udoh Jeremiah Date: Fri, 10 Feb 2023 21:08:24 +0100 Subject: [PATCH 090/775] doc `:` may return UnitRange, StepRange, & StepRangeLen (#47670) Co-authored-by: Sebastian Stock <42280794+sostock@users.noreply.github.com> --- base/range.jl | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/base/range.jl b/base/range.jl index 9d12ae1001784..ce3660c0ab141 100644 --- a/base/range.jl +++ b/base/range.jl @@ -32,9 +32,14 @@ _colon(::Any, ::Any, start::T, step, stop::T) where {T} = (:)(start, [step], stop) Range operator. `a:b` constructs a range from `a` to `b` with a step size -of 1 (often a [`UnitRange`](@ref)), and `a:s:b` is similar but uses a step -size of `s` (a [`StepRange`](@ref) or [`StepRangeLen`](@ref)). -See also [`range`](@ref) for more control. +equal to 1, which produces: + +* a [`UnitRange`](@ref) when `a` and `b` are integers, or +* a [`StepRange`](@ref) when `a` and `b` are characters, or +* a [`StepRangeLen`](@ref) when `a` and/or `b` are floating-point. + +`a:s:b` is similar but uses a step size of `s` (a [`StepRange`](@ref) or +[`StepRangeLen`](@ref)). See also [`range`](@ref) for more control. The operator `:` is also used in indexing to select whole dimensions, e.g. in `A[:, 1]`. @@ -469,9 +474,11 @@ A range `r` where `r[i]` produces values of type `T` (in the first form, `T` is deduced automatically), parameterized by a `ref`erence value, a `step`, and the `len`gth. By default `ref` is the starting value `r[1]`, but alternatively you can supply it as the value of -`r[offset]` for some other index `1 <= offset <= len`. In conjunction -with `TwicePrecision` this can be used to implement ranges that are -free of roundoff error. +`r[offset]` for some other index `1 <= offset <= len`. The syntax `a:b` +or `a:b:c`, where any of `a`, `b`, or `c` are floating-point numbers, creates a +`StepRangeLen`. +In conjunction with `TwicePrecision` this can be used to implement ranges that +are free of roundoff error. !!! compat "Julia 1.7" The 4th type parameter `L` requires at least Julia 1.7. From d6baad629e9103a47c6286421f12a334a0b59fc5 Mon Sep 17 00:00:00 2001 From: Udoh Jeremiah Date: Fri, 10 Feb 2023 21:11:03 +0100 Subject: [PATCH 091/775] doc: clarify FAQ difference between "using" and "import" (#47614) Fix #43425 Co-authored-by: Allen Hill --- doc/src/manual/faq.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index 0ac53a0233402..e3960ee1a4690 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -818,10 +818,13 @@ to strings); similarly, `repeat` can be used instead of `^` to repeat strings. T ### What is the difference between "using" and "import"? -There is only one difference, and on the surface (syntax-wise) it may seem very minor. The difference -between `using` and `import` is that with `using` you need to say `function Foo.bar(..` to -extend module Foo's function bar with a new method, but with `import Foo.bar`, -you only need to say `function bar(...` and it automatically extends module Foo's function bar. +There are several differences between `using` and `import` +(see the [Modules section](https://docs.julialang.org/en/v1/manual/modules/#modules)), +but there is an important difference that may not seem intuitive at first glance, +and on the surface (i.e. syntax-wise) it may seem very minor. When loading modules with `using`, +you need to say `function Foo.bar(...` to extend module `Foo`'s function `bar` with a new method, +but with `import Foo.bar`, you only need to say `function bar(...` and it automatically extends +module `Foo`'s function `bar`. The reason this is important enough to have been given separate syntax is that you don't want to accidentally extend a function that you didn't know existed, because that could easily cause From 6cbcee9cb05578d02443f7e1c2d6f1cacef8a9dd Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sat, 11 Feb 2023 10:49:03 +0530 Subject: [PATCH 092/775] UniformScaling: `copyto!` banded matrices (#48445) * copy UniformScaling to banded matrices * Add test for finite Fill --- stdlib/LinearAlgebra/src/uniformscaling.jl | 16 +++++++++ stdlib/LinearAlgebra/test/bidiag.jl | 21 ++++++++++++ stdlib/LinearAlgebra/test/diagonal.jl | 19 ++++++++++ stdlib/LinearAlgebra/test/tridiag.jl | 40 ++++++++++++++++++++++ test/testhelpers/FillArrays.jl | 33 ++++++++++++++++++ test/testhelpers/InfiniteArrays.jl | 3 ++ 6 files changed, 132 insertions(+) create mode 100644 test/testhelpers/FillArrays.jl diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index 428acf469c9b2..e691caf696e8c 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -381,6 +381,22 @@ function copyto!(A::AbstractMatrix, J::UniformScaling) return A end +function copyto!(A::Diagonal, J::UniformScaling) + A.diag .= J.λ + return A +end +function copyto!(A::Union{Bidiagonal, SymTridiagonal}, J::UniformScaling) + A.ev .= 0 + A.dv .= J.λ + return A +end +function copyto!(A::Tridiagonal, J::UniformScaling) + A.dl .= 0 + A.du .= 0 + A.d .= J.λ + return A +end + function cond(J::UniformScaling{T}) where T onereal = inv(one(real(J.λ))) return J.λ ≠ zero(T) ? onereal : oftype(onereal, Inf) diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index 9866fce047dd1..b16bf9c62f0c0 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -13,6 +13,12 @@ using .Main.Furlongs isdefined(Main, :Quaternions) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Quaternions.jl")) using .Main.Quaternions +isdefined(Main, :InfiniteArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "InfiniteArrays.jl")) +using .Main.InfiniteArrays + +isdefined(Main, :FillArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "FillArrays.jl")) +using .Main.FillArrays + include("testutils.jl") # test_approx_eq_modphase n = 10 #Size of test matrix @@ -794,4 +800,19 @@ end end end +@testset "copyto! with UniformScaling" begin + @testset "Fill" begin + for len in (4, InfiniteArrays.Infinity()) + d = FillArrays.Fill(1, len) + ud = FillArrays.Fill(0, len-1) + B = Bidiagonal(d, ud, :U) + @test copyto!(B, I) === B + end + end + B = Bidiagonal(fill(2, 4), fill(3, 3), :U) + copyto!(B, I) + @test all(isone, diag(B)) + @test all(iszero, diag(B, 1)) +end + end # module TestBidiagonal diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 83a2a896e736c..f57578b42bf9c 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -12,6 +12,12 @@ using .Main.Furlongs isdefined(Main, :OffsetArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "OffsetArrays.jl")) using .Main.OffsetArrays +isdefined(Main, :InfiniteArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "InfiniteArrays.jl")) +using .Main.InfiniteArrays + +isdefined(Main, :FillArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "FillArrays.jl")) +using .Main.FillArrays + const n=12 # Size of matrix problem to test Random.seed!(1) @@ -1133,4 +1139,17 @@ Base.size(::SMatrix1) = (1, 1) @test C isa Matrix{SMatrix1{String}} end +@testset "copyto! with UniformScaling" begin + @testset "Fill" begin + for len in (4, InfiniteArrays.Infinity()) + d = FillArrays.Fill(1, len) + D = Diagonal(d) + @test copyto!(D, I) === D + end + end + D = Diagonal(fill(2, 2)) + copyto!(D, I) + @test all(isone, diag(D)) +end + end # module TestDiagonal diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index 590870d4dad0a..e45fc9a65dba0 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -9,6 +9,12 @@ const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") isdefined(Main, :Quaternions) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Quaternions.jl")) using .Main.Quaternions +isdefined(Main, :InfiniteArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "InfiniteArrays.jl")) +using .Main.InfiniteArrays + +isdefined(Main, :FillArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "FillArrays.jl")) +using .Main.FillArrays + include("testutils.jl") # test_approx_eq_modphase #Test equivalence of eigenvectors/singular vectors taking into account possible phase (sign) differences @@ -738,4 +744,38 @@ using .Main.SizedArrays @test S !== Tridiagonal(diag(Sdense, 1), diag(Sdense), diag(Sdense, 1)) !== S end end + +@testset "copyto! with UniformScaling" begin + @testset "Tridiagonal" begin + @testset "Fill" begin + for len in (4, InfiniteArrays.Infinity()) + d = FillArrays.Fill(1, len) + ud = FillArrays.Fill(0, len-1) + T = Tridiagonal(ud, d, ud) + @test copyto!(T, I) === T + end + end + T = Tridiagonal(fill(3, 3), fill(2, 4), fill(3, 3)) + copyto!(T, I) + @test all(isone, diag(T)) + @test all(iszero, diag(T, 1)) + @test all(iszero, diag(T, -1)) + end + @testset "SymTridiagonal" begin + @testset "Fill" begin + for len in (4, InfiniteArrays.Infinity()) + d = FillArrays.Fill(1, len) + ud = FillArrays.Fill(0, len-1) + ST = SymTridiagonal(d, ud) + @test copyto!(ST, I) === ST + end + end + ST = SymTridiagonal(fill(2, 4), fill(3, 3)) + copyto!(ST, I) + @test all(isone, diag(ST)) + @test all(iszero, diag(ST, 1)) + @test all(iszero, diag(ST, -1)) + end +end + end # module TestTridiagonal diff --git a/test/testhelpers/FillArrays.jl b/test/testhelpers/FillArrays.jl new file mode 100644 index 0000000000000..c0f91b2d0fa1d --- /dev/null +++ b/test/testhelpers/FillArrays.jl @@ -0,0 +1,33 @@ +module FillArrays + +struct Fill{T, N, S<:NTuple{N,Integer}} <: AbstractArray{T,N} + value::T + size::S +end + +Fill(v, size::Vararg{Integer}) = Fill(v, size) + +Base.size(F::Fill) = F.size + +@inline getindex_value(F::Fill) = F.value + +@inline function Base.getindex(F::Fill{<:Any,N}, i::Vararg{Int,N}) where {N} + @boundscheck checkbounds(F, i...) + getindex_value(F) +end + +@inline function Base.setindex!(F::Fill, v, k::Integer) + @boundscheck checkbounds(F, k) + v == getindex_value(F) || throw(ArgumentError("Cannot setindex! to $v for a Fill with value $(getindex_value(F)).")) + F +end + +@inline function Base.fill!(F::Fill, v) + v == getindex_value(F) || throw(ArgumentError("Cannot fill! with $v a Fill with value $(getindex_value(F)).")) + F +end + +Base.show(io::IO, F::Fill) = print(io, "Fill($(F.value), $(F.size))") +Base.show(io::IO, ::MIME"text/plain", F::Fill) = show(io, F) + +end diff --git a/test/testhelpers/InfiniteArrays.jl b/test/testhelpers/InfiniteArrays.jl index d69130f4d726a..14b2e56daf1c6 100644 --- a/test/testhelpers/InfiniteArrays.jl +++ b/test/testhelpers/InfiniteArrays.jl @@ -21,11 +21,14 @@ Base.:(==)(::Infinity, ::Int) = false Base.:(==)(::Int, ::Infinity) = false Base.:(<)(::Int, ::Infinity) = true Base.:(≤)(::Int, ::Infinity) = true +Base.:(<)(::Infinity, ::Int) = false Base.:(≤)(::Infinity, ::Int) = false Base.:(≤)(::Infinity, ::Infinity) = true Base.:(-)(::Infinity, ::Int) = Infinity() Base.:(+)(::Infinity, ::Int) = Infinity() Base.:(:)(::Infinity, ::Infinity) = 1:0 +Base.max(::Infinity, ::Int) = Infinity() +Base.max(::Int, ::Infinity) = Infinity() """ OneToInf(n) From d8c225007498a68d1a1c9f3229d0dbc11b98f0cf Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Sat, 11 Feb 2023 08:07:34 -0300 Subject: [PATCH 093/775] Bump LLD to get dsymutil and use it for pkgimgs (#48628) * Run dsymutil on pkgimgs for apple platforms + LLD bump to get dsymutil from the jll --- Makefile | 3 + base/linking.jl | 22 ++++ base/loading.jl | 6 + deps/checksums/lld | 232 ++++++++++++++++++------------------ deps/lld.version | 2 +- stdlib/LLD_jll/Project.toml | 4 +- 6 files changed, 150 insertions(+), 119 deletions(-) diff --git a/Makefile b/Makefile index c080f0d144cf6..0d85d195672cd 100644 --- a/Makefile +++ b/Makefile @@ -337,6 +337,9 @@ endif # Install `lld` into libexec/ $(INSTALL_M) $(build_depsbindir)/lld$(EXE) $(DESTDIR)$(libexecdir)/ + # Install `dsymutil` into libexec/ + $(INSTALL_M) $(build_depsbindir)/dsymutil$(EXE) $(DESTDIR)$(libexecdir)/ + # Copy public headers cp -R -L $(build_includedir)/julia/* $(DESTDIR)$(includedir)/julia # Copy system image diff --git a/base/linking.jl b/base/linking.jl index fb9f6d087a2d0..18cf128b342de 100644 --- a/base/linking.jl +++ b/base/linking.jl @@ -11,6 +11,8 @@ const PATH_list = String[] const LIBPATH_list = String[] const lld_path = Ref{String}() const lld_exe = Sys.iswindows() ? "lld.exe" : "lld" +const dsymutil_path = Ref{String}() +const dsymutil_exe = Sys.iswindows() ? "dsymutil.exe" : "dsymutil" if Sys.iswindows() const LIBPATH_env = "PATH" @@ -60,12 +62,27 @@ function __init_lld_path() return end +function __init_dsymutil_path() + #Same as with lld but for dsymutil + for bundled_dsymutil_path in (joinpath(Sys.BINDIR, Base.LIBEXECDIR, dsymutil_exe), + joinpath(Sys.BINDIR, "..", "tools", dsymutil_exe), + joinpath(Sys.BINDIR, dsymutil_exe)) + if isfile(bundled_dsymutil_path) + dsymutil_path[] = abspath(bundled_dsymutil_path) + return + end + end + dsymutil_path[] = something(Sys.which(dsymutil_exe), dsymutil_exe) + return +end + const VERBOSE = Ref{Bool}(false) function __init__() VERBOSE[] = Base.get_bool_env("JULIA_VERBOSE_LINKING", false) __init_lld_path() + __init_dsymutil_path() PATH[] = dirname(lld_path[]) if Sys.iswindows() # On windows, the dynamic libraries (.dll) are in Sys.BINDIR ("usr\\bin") @@ -82,6 +99,11 @@ function lld(; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true) return Cmd(Cmd([lld_path[]]); env) end +function dsymutil(; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true) + env = adjust_ENV!(copy(ENV), PATH[], LIBPATH[], adjust_PATH, adjust_LIBPATH) + return Cmd(Cmd([dsymutil_path[]]); env) +end + function ld() default_args = `` @static if Sys.iswindows() diff --git a/base/loading.jl b/base/loading.jl index 35240b1cd500f..fcc7330e5dd63 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2225,6 +2225,9 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in rm(evicted_cachefile; force=true) try rm(ocachefile_from_cachefile(evicted_cachefile); force=true) + @static if Sys.isapple() + rm(ocachefile_from_cachefile(evicted_cachefile) * ".dSYM"; force=true, recursive=true) + end catch end end @@ -2252,6 +2255,9 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in cachefile = cachefile_from_ocachefile(ocachefile) rename(tmppath_so, ocachefile::String; force=true) end + @static if Sys.isapple() + run(`$(Linking.dsymutil()) $ocachefile`, Base.DevNull(), Base.DevNull(), Base.DevNull()) + end end # this is atomic according to POSIX (not Win32): rename(tmppath, cachefile; force=true) diff --git a/deps/checksums/lld b/deps/checksums/lld index c215798eaf19f..25a89e959d297 100644 --- a/deps/checksums/lld +++ b/deps/checksums/lld @@ -1,116 +1,116 @@ -LLD.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/5344b22c67a05a0e8f7a2a66b14f7c8d -LLD.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/95f3772927a4770d36817d3c681ea76ecc2f94256fde7866ce688b6631710b4d044952340f2cff08a81b1d8871beb242d0e4e980b5b4455a1b74cbb0786c51d1 -LLD.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/924d77f9440be4e13d88c8d59cb60ae3 -LLD.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/e26b186f49b0709b5f9ebb5485568a69b0bb078395071cb43c02a659dfbb8f25ce21b17806377ae2f8dbefe36bce1db8c29cec8cdcd140099bde76eda5585612 -LLD.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/9b5ff100b332d5f9fe67de97b635ca20 -LLD.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/2d5619d5db7ca68fa4a14693084bb76520cba1a3dcf608783899793eac296c653b84d3b88a559a93a0c95d92c58e4847c796a69207cce5899924381f308d73c9 -LLD.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/20a1e3619501ed9b45344b98551359bc -LLD.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/e517788ae12045fe2647613f7654bd6178af842c2c284178216b87cda67a97fa1f9002292bb4f792de48f3296fff477cb3a80b114be40cf2c6c502d81f350abd -LLD.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/70add6dad0a85be2f44aad61a50dac37 -LLD.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/78062a9f2a7bea15c6230d03011ec6257dc015c76cec0023edbb85709cdfe3a97e2e28fa96045d6b42d8f2849d88d19a2d8e936bb53aa5638dcde17472c1181b -LLD.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/1e4bf7f0827d9056c906fa2b09b381dc -LLD.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/6a06c95a4ad191e013b3ffb15fa8da49ad2c448ca8f17db3079969575b9fda01d98db84bb26eb2e0ad570d3de4f9a07cdc8f0784cf18be8271025bf724eead4d -LLD.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/24e3af3ecf857aafc759ef40b3a52825 -LLD.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/dd1c825a7689c9ffc49365529a5320b618209e617df4720d6664b28cad1d03ea5a1209ab43a356e2871b0d8e6d2fcca82ba75202762046ed6582ed148a387c9f -LLD.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/159d8aeac0680f895b83cb8574ec65df -LLD.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/f219694d0508eccda753d7ae374cbd1b06ceb76c26569ebcf09932ff710f49f5bb789b4cd516ab92e56f9eca727d93a01cfe0940e655029451f3b1e38ef43c82 -LLD.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/2900777df5ec9d27cb05ef802e8256a1 -LLD.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/300f899063db9d3c915413d396a888174b3925876e1180ff5e614ce6f725b859dc483faae836176b95d8e88cdfb0905c7495a692dc626e38de1facf9a28e884f -LLD.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/eee9f3818f04d3ad29d618856fb79659 -LLD.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/5ca354aca71f16304ddabcef24b63fae0716a1bfef1c5d96029175964a7486c7da2d4f0b927e3dd13afddec78ff8ea294464145c9ee19a010433114d2f7c18d5 -LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/d5b3edd7d22e2c65042404330bd3e591 -LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/54af1917db010a0032aa19e7e20ca096609a45b01968353af03121b6809fcac53d414d594ed1127016667ad331c7e76b8ffd305bb186f7c80a786676ee1132e1 -LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/351534948a7507501af4ba6c17fdfa87 -LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/0b4f4265f20414d8d1417913f38cd853fac91a097ac6be297e5472fa05850277dd316e0c91481529a9362873c53e1ff5eddf4c4d2f1bb25c2f8e15a80b5e6497 -LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/4d3ce4de0f8f21f2f2ff75acf20b0362 -LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/d3463be7353570543ef6613dd5e5fdd8790f4cd24362991039178755e9cbfa4b67123219e06b8eb3749dd31b7f759cb899f6d76f5a5155717e1fd83e1325ed65 -LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/847cffc463372eb8ee7ec4ab0e672227 -LLD.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/24596a775492d8f6ced29caff28859a600a5f7c38e6d48e4f9ab0200f28081e43f528c2892c895cea44083331273752545f96c542e0c56209d90743affeb223d -LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/2c932f1a9fd559b9f49d3e0f92ac3302 -LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/f4efb89a3af33817ded7adcbf758c47413f16a94b06978be43ed4b397d212d7da04429d003f2298dcf2ae7ff67614ce83e3d159f0e2c9eaa9268ff5a4c9ec113 -LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/ba7dc5b1b3753b24f15c334a6c25b5c1 -LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/3f09d1e4befbdda185e5e9df153a7730c25d84063ab83f3fefffddab7f0d8402cf923b69cd72bef2d02f3b1fd166924ca0c557c8e2bf4fea8bbb82c80474e387 -LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/18b16c9c5cafbffc375d512b9f84363b -LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/4e17f053e58bad9974002fb1c58ea0546cc1acd475ce253739d2de9113912f15a130ba8cecaef3017202fdd7f5ad5fd9977140d766f659d43abaaa6cf53b23ea -LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/54fa708a76f4627f267d87a9d8f78a4b -LLD.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/7b0ea14824e5144ef05087f88c8ac477a159f76fcb1f84cb59ca98cc7f2c0993ae9c4b57749a395c7ae90f7f37c6a4c4c14ee8fc381859d47736a87acc233349 -LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/576e9e5765a3696e0cd3153ebbef6147 -LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/c643fab9837dbb02ff9cb9202e53766b03649855410e7da15e7e9fe54f6f15b7172fb2d336fc68b315401b8b3f8e8b597204b739592cc2e7cd548a78a3baf8ec -LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/065255972031a362edb3f5fb74e530c6 -LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/a7411c9f44ce798bc5e1f9838b73efeeb97d338a50b605c93918b85c0848cdbccc71daedcaeb4348c7d1236a3b23680f20ab46dce1b2fdd156b29b3c3677f971 -LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/b0ba1697475e35735d0d94389b1524e6 -LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/aba3b866c41c821b9f26a5cc32910e2f976a596b388a14a0dca9326ad72f1d346208bb18f478a0919471e10d578c41190236a6a8cdb94fe394c7e7af120678f0 -LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/1c7e5428cf571721bb320677189f1545 -LLD.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/a8ea619b58e49db4acaec9a0feb5e5aa93b4fc7d1d6190c85c76661b3f833d5fa988692c42e643590a49a804b928a3799c91ac4e0b5df7a826ae76446d8fe8fe -LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/1ccf9badd8d0cfdcad55e080175411f9 -LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/b48da6e3d6513397f861dd694dd2129c0bcc86e73cc44fb079f9e27a715a27ef568078240f5afec0220514221e3fe588854cfc390c432e5d06abffcade1488ed -LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/38d64d93d5d0358135cf98a6399f1eb8 -LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/8af6b61dd4e4b32bfb17e91aabcad7ca31044b31b0aa124bc19185c2125b1f4408fbd865c3d14e56406159e4ba5d8a78d347036b1db09ddba34676eb05e22e1f -LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/9b8be38bed8a91dce3ef2bee57036d3c -LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/45e1eb6a49940fd3f77e8e22ac0cb34b7132466decc9bc893ab697e66becf9d1bf6c74a86509c71c8424d418eb7498b9fe843c913a1bc5ffb897fece178bf8a5 -LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/0b94c549ff012015384d50fba8c0821a -LLD.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/81c31f243e66b7e61cf839647fb765305c6fee0393216846ed8ca4402c1115222e2392b1e5f737b48f4655f18177d97e08f50674b632638dcac537e87e8fa5fb -LLD.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/31f75615fd3219b630d2987e25f93a5b -LLD.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/1699aeea86bd8a1df53fc58770c34b87637ae621c48ac9831874734cd330ac67e845cb55af9077f18068cbd0b66f9f89cc82a286b098e0f44c7bd2b6b0fcef18 -LLD.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/418db3048ce54f8fa3a0f89a11f765ab -LLD.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/f1ecf8c0cb2c6be879e8a860592bcd317821fa2d89978f96175f3bb7c9a3a2aa261123c147dcf6c796c3212871398f5e7cfb5dc153ab503aa8004e6017b842a6 -LLD.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/8a70cbfe920fe7dbec2129f7c5b440b8 -LLD.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/e332ff1e1e2383d0bc01b178ae5ea44efc1986d919cbe9b13e8d4ce0980b5fa97232851d1f2e17b6b4bca0490775cfb00cc0efe0d588bcfbf10ac9e69faf3f8d -LLD.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/53117bd03ceeaabf93aaf0702c30e1e4 -LLD.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/3123e605d2ba1d7bc4fb4aeb15740b651cd09b75a6ea9eba22757a07ebc9dfaeff126bda912187ba2c16e89521d6f94ef53f587a61a67f4deec021479319e21e -LLD.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/0aa6ecc9753e17f8e45f4a63824998df -LLD.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/fd423adc752e338cce486c554b33432920950ccd87a02af9e92afcc415e0dfda141e17ea3a25abdfe2ef03ed9b0415cbc9fc4f099c65e5406a6663e06b707867 -LLD.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/d2bf7a484c8fb31b3d72a18f6eacf042 -LLD.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/cd07d944f26b698a44dddcd0936189cbdd48b2edbb70cc84e9a267729fb672e1781356c56588770443c5523cc2c8900f81c9001be4e56595f6e2aac4a9ef36bb -LLD.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/21cfe7c71234c543e6ce99bd468602d4 -LLD.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/8c70edddf70231f53318781836ae329d20a869f0acac2f8994cb73a221734f8b5c34fe1f68442522a964cb014d972fa5b98773a65366ce02b497d57a23abf616 -LLD.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/dde8dd2112180963e8b2d1c688553ced -LLD.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/704550b2aec1d73c1279eb1131b530e3309c796204845dc1da4b7132c612b862ccb3525a1d7a7af1483ec8cd211d8be532d3a49dde6c7a8e55c617bb3b665455 -LLD.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/c501233e2134da56034390a296684fda -LLD.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/5ef2bfd95647aefdc71b28fc018c3f3c3b5762ce6c8be7c136d56e01f47fe48c4bb99d913501916388d6bb46bd3d9a905efd04cda5b031def2f703942374fddd -LLD.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/8758e9198b98a72ef6c6546afb8f5551 -LLD.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/741d09735e3cef28f9e45aaeb0f715d312fcd92dca3ab9623cdb3215163c9dd68f7252842e3760f9a65470e4a6a666e58ae4da4bf57122d1a64dd70a3856debc -LLD.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/fb6abded431a1bf2c22a2d94d13d6cb9 -LLD.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/182e24978f298057c9d6f2299ef3165b1930c0d42c60215f01ea2ea57e0c864ffd98c80464ef340a34a89b158375746c9b1e063bc89955689a6fc31bc097a4a0 -LLD.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/036e3d82650c9d863c7a2ae6b98007cf -LLD.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/ccde6a4f3407966fa4522568d24d78afde26a5ebbe85708d0d6a28e0ed29ddf47ede86c2abf174c527bfdde53f461733bb2298c0828f5f2b529e390916999da7 -LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/b9fe932890276f6a4e1d315a573ec6e0 -LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/34f8f8b653cd1f56ff049d3f63212133f9cf07f4b0362ab2a2b7140436031c2a8802512c12b004299cecb0d0c8bf515bde38bf4b52dda57039d76ac948eb4577 -LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/f38c4d018cacd0021560e02c3039f5ff -LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/af2f2c901b4e707373a76b2b625f3835fa4573607399303b347e4e6ec401083ba7255c99044bdf853050e5b28fd9a785cd90e3aca0d7beb73406a056fb98fe0a -LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/e5a85e96fe144073b05796728af8c24f -LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/d4f97e23828700a34f0bc2e1000ffa2bf21e8b9879b9d114118bfd42406e65589a6e807337e38701e95a1064dc2924280997c7e342cca55c232e6d841bb40aaf -LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/e318117921ee0dc2d1f3e43931c6d420 -LLD.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/7a5f264c8dd94734cd495e843854cb9c7cd57cf3fe29f08f21d6e7d690da535b95e4e9285df12d89134fcda6c5cf2df5b189d378106fa05f6fd5af92a2096755 -LLD.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/10a67e175285b206e86e1713a38be6a8 -LLD.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/6f1d6455db15a03b54289366970bf63c136630a75bf8a25ef6f68d25f621992e3b0b48b1e02942b7b9953eb3435fa48e54cb801fee69c40e8616b272c9e2194c -LLD.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/a106b6b58bc5c8492334f540b0e427b0 -LLD.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/1d5340a1ffdefaa967af0fe1ac66f56e92fba7f85c5f63b783d224afc318394b3151514fc666c928ffa018db71dc2bc1e0b484a6e61918df9f3e971a4465cf81 -LLD.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/cc73a643641eb34a53a8c4dcc82dfa31 -LLD.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/7598cc478e25c6eb3328292aa6e50ae15454514790b6ae470fd492444018ba905df9e941846316907465c422be7fec0aee462cdc90c29d0dea09dff4d02f71ef -LLD.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/757b7170cc1213d01f0ff2a980372745 -LLD.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/f4a567cc1859b6cf85250e150131d8771b24a534469eb6ad6770f1e1594fd0bd2016cbeefed59dcacb50bc6328f8f462ad2200194afcf3f1eab8ae13eeeeb093 -LLD.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/f8f7b5a5ef2b93da4b1bf2932c098974 -LLD.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/f988ae43f3bfe56a3f6d54ac98420f053973406b07403bc850647698b23783cbfdc77f705299d2df114f7f58f6ef9d30a0eff789780d5e206c5dc5857a81f968 -LLD.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/9692fe8d0533df9243b6b9b49b158e9f -LLD.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/893c8fbe9483106fbf3b28f2490f1cf2faeceda29890f67f9d0adeb6d841dacf7b39300598c673b28157b1fe4a971e749bb71dc3df8e66983ed8ce9fd6973451 -LLD.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/ece7e11a288c20a156542bd31b3e642e -LLD.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/d032c3d738d9073ab92fab38ec47a45371e243963a5bd39a392be44e090189182be71f363ab8fc71116eb9d14835cae2bed70eb1a1ba5c6bda2972412719e0e7 -LLD.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/bec0b1fe84e4e754cf7a3f67362b2d87 -LLD.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/434030e81828df77b452e8d364359652266dedfa731395cc7cb5f41a8102ec614d1c147f25602a435c701c674095640279c59e3ab8ce3422a3694398880a2f7b -LLD.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/dadf9169ef4a6fde170430c5323cacf2 -LLD.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/cad9dd4973d83c6c46e32a04fe0b883d34c067922f14c569679dac9d8f04faeedbfc3aa4a17c6f3b461d12105fa7ef5dfaaccba3c5bdf518ffd06bcb44fe9132 -LLD.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/ad533cdc5d317ea7348efdb08724a9ad -LLD.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/baf8a1fb66ba41f88b42d0966f2b9f0a467dbbeed24f8854598916facdb6567130f379573851ffd137624687c9e5b6637b491a030f1a41916426360a1a9fcd8b -LLD.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/cafa8a847b297701952fa1cb0e50af0b -LLD.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/d7f55d8696fe882e4c275ea40f3774d39a27cede475e135d6dfa7f7f7f4c6633062e0f75f8414963c37e92ca4889ba9f8ad7970a10364b4bbc30d225fa66c901 -LLD.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/dd798db1f473b1ea2801258b98325969 -LLD.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/f4bba7ce56c6d8f55ca5777337d721bc2b663e9c2acd2e9636a86e6948815be1c0e7243c851811763d2c98a5918d454ebee6a48494b884aedce9afe8d9e1ffd5 -LLD.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/817201c1fd49c212881fb2de8971e0f4 -LLD.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/3cdbcd359859d22ecfba562367d3db522e6f25082b3bcdacfc29ad498398e86b2156a6c637aaad0010818771a815e6ab78c3e79227fc5e5de15b3c6de7544b4a -LLD.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/0253e0415d7fd046ece8a9f8b4583cde -LLD.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/f77c61f3a15d3782e1ec017580b782328dc0023b3c17d85a7deb5454e133de058778ac45703b509aaffdec4b58423e816803bb1bdca333b9846632b241f9595c -LLD.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/a2a132e6a57e03acb56a0dc4464f8511 -LLD.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/dfdd2755d341409bc12b097ce01848f99af24bb9bfeea0a687d016fd2167849f38ce4fb13fa87f7c70be0576771999e01f33c58883f56a50cd19ea7e963dffdd -LLD.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/eabba184b37f882bea4167870fa819f1 -LLD.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/2f75f55a31cf13a626070a2bbcb55c8eadeace453af70ed03377e5b4a2b9d7f0f7350aae0669e6702e3b650af6c0ca7c027534d99ae444431de5d3e6e7e53083 +LLD.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/abbfdf9218efbe8fdd4664e07f1e6f00 +LLD.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/cd43f50368d708c345fc7250cbdfd236f25c9bb0f0082d5309e0d194aa4c5daf808aafa9074ce85ee11474ffee16fd5015830cd4e672281404b10f403e3bd288 +LLD.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/eeb0349137a82c86ca14b6e2d27318a2 +LLD.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/5971d1b5b4d6928261ffa2b8d4e1ed849057090a6b7d6582e5c69c0cf089fa18b3f3e8cc6de4f3733a5e4fcfaefa2fcde440a1d20df4c2f82b745dfdfbdb0644 +LLD.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/ec6f983f4ae41f7f10155acdafba1d7d +LLD.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/3fe71a15d14ccaca75cbde3b6bdfe914f537ef0a691bb9fbf98f3c09d0097b90dd9da9aabdeaa78aec216983975e822331c93e66e83516e02e315c74b062e408 +LLD.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/c71d04c38d1e814cf947998485833410 +LLD.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/5644ff89d652b2bb786cedec65238a964c744da2182107412c0d8ec71e323b1c55ee4b62999ec2382bb6e645c6de53cc3ab1ecc57f03a16b0e6b1debfb004836 +LLD.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/9d9f8cb68b812a65220bc9d2ffea0944 +LLD.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/8677f1a1d3960e484c888bffa9c19c52d3598fc332eb379d8a8f0ba8adbd7feb44e11625450da8993bb3b2566528b5264e90142ec5dcf58443e19ca14f3d6133 +LLD.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/6e90c88e855a18670d62bd6241c78564 +LLD.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/a4607a96c48295e92d324a92c5db2179a23a3e9d0181bdae26cbc7ca60db3decf3bd718af7b194f31fe9a6c0b494103b6d96d15b7c4b0f37087b37212b1497c2 +LLD.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/a19391c4dc753a30b340ba2c9084cd82 +LLD.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/ba06f94fe7b9a291764d9533124c4c9f36c29120c13c26f2fa0a1f8a304b3b20ee8784bd9d58ba7fd4d04c04a05bc2dbd230d9819fb9959a0f947b9eadc5011a +LLD.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/edd05eca7bb95da8ba3ce0f6013bb2a1 +LLD.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/ec14a7cb3be014c775a1d99564e3bf42ff3e6397435107a52958a0e3871b217d1b3e12289d794b6c15c6215998af7df366bdf6392b1d9bedcca832f881c2b07c +LLD.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/c966609ec3fe4bb9212986bc18448c7b +LLD.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/f0f6fe833e9a230e109d70dcaff968ad39a0e28f321c4f50fa4a022d0a9850aa8760daeb7467a9b4b27534f58e58add6f503fb1ac9e945c4e312777ea61225e9 +LLD.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/671db578f79cea4b1a72cd1901a07f45 +LLD.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/5bd28897d65149d19d5d7d8b096c6cd7b243a4c86dcea2648a6b7b2e846a8b757aba745f97cabc9f63b19a084204b7a7f58be2c208b18b12e9dceef9e68834dc +LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/602961c40a7194bd2afeafaef2023773 +LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/f32d73c06b4ea4444ce0e392bc272d639c53f77960247c0a8fed73f9569b6b99f4b2596b69f0333b657605198b02685ec1f3295f4ee5c6c703e7949a1a8e155e +LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/a6bdf7874e8825131077911c66d3cea2 +LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/aeb1a246b2edae8dc72b8e1f959e445ad41bb5de58fdab61f09c84d89061291ba0d16a93e6810911a4e20a7b1cef7b95fc4f0849dd298ea4586f060d594dc0ee +LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/71874632d597130e09ac021cedcafe3e +LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/811dcd2c42cceed51b0778d65bf7f0a471983f8261ccc36454acc8e819015d712ad88b9b327e8a56b7ae9cd8e2cc9df4aa0cfebe8402b9757f23d245b1140af3 +LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/83b5ca3bbc2d0cd18297dbb53fb167c8 +LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/2c669fb67712cdf34f8217a517ecc6b755ff57e7f80f239865c65962a000dcba0c8619593d4bfde966354bdd11e16ebf1f0cabc6353fe98a26c6c55188be7d4e +LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/c9e8b4975f75ceafc114fa92fc7b7ee9 +LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/f5bb1f0299eb0ef03d527fab752a5d3660182bf49155ec8811d71b8c04cbdd7da3d9aa7673f81be3ad380ac4e46c98402805a0fbd14c2fffd6109d0d5091d7eb +LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/0e4e04c4c2bcfd91c5204e68bff5204a +LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/8ba4e5765f5b6c5da33b2e38fa9a33cbd9d94d03652431ec8555f07d851b732abc1958e0ecc9f572c3575adb8f5938170a96224b1b7f62c6f14a60c19388f376 +LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/003d935795c66643b40320a4ae5db113 +LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/f74cf1548a70256dc5094c52a31e68c6e66ecac1842d59bdb195f692574dc12d1a2c8f85d0010796297b8053d391c60310afd283893d80cc8d262ba12707c2eb +LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/25316c13c11368dd17dd58684bf58ce8 +LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/d19eb96dad3f389a6a10e27cfdabbbf768c799202c0b8d463e1d6c06befd5725024b4c1debe1b6f18503fff1eff56f96f673ce8f176d560db24cf68b7555d179 +LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/1c5c481d3f47329d2b3d8d618b9ebb0e +LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/4cedf9e17bc537486c1b1f19ff5b72ebd5c831c7672aaee425e2ca69cfef0cf64bda1e1f2a9035dd0350b7a9f493c8113fd0b79f749fa6628487bfffb6a625ae +LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/1f5d02129718e929698b564c287c96b1 +LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/8a0233ea231fd3a6a3de5b2af6dace416de90527ba0b38fa3a67ccf73493254f530555597dc17d093b4893c32d4f698f724b933cba7af1be90ced62320589064 +LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/6d329f8f0c9395cef1fb49bac38f6d84 +LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/225537c9795ecb5544f8eb3332ecbe95773b6dcc98ecdae3d7c41b58ef9723ea910fa02434296b7372bb6db0f90d55f92c205890d3055c0899e707a2ec759bf0 +LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/1697711f610424891b474bde4ebe0597 +LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/99a22ad6ce172679b9df50e6b76286bcf400fac5029f363877fd1c56d04c3c9ae6494e5ec6d62cef2aff6a3bb4195e598fd6403d70d55f6f313f56aa7e49718b +LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/7c72c26830b45fee5158a0319efc8fd5 +LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/838b7b333abb297c95441deb35e132b44a9d96bb4e5aca05ee63b67b642eaea23cc7fef37ade7265cc698d74881e84c680eb6e1c3a491e0227ca1c8767e8ff17 +LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/9c7b5c02360c3311e093e9326fa29e87 +LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/d11c5e60b1a7b1e8ee1cb99dc22080f5fc618389a8fa9877cad898f92718a6e61d0a53fc05039a7a975db433cb410f72a64e3d057669755dc127d02c4a640e0d +LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/b25f1433044cbe45b9ad733bca0f6922 +LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/960e4a5c8f699cceb4d3a57b993e98c97315ab877f88f744089e657a9dc65e68cea91a2bbbf999a88ec7da9c5ea312d3c154f4c14c4d9b92b003c93a008819e7 +LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/6f18f7e9b48d84bdb8a4d557c0c286e5 +LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/fbc3124fa86776bc60fc20f15d4470c1165da4e97bc6376b2613bc4f2b283d411f191e4cfe7f8e12d189e5031d7ebd4763760afeec7968b9a3501ec090d51778 +LLD.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/fde2b3a5fd283cb5c04c42299b25ac54 +LLD.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/854ec57f923c920570242abc300eb4f06417b77dcb4891747fee9fe19d8d67a88ab19c70a31ad4ebee7a3ba3276ec5085f461e85654f095e8f10c72ab7488393 +LLD.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/061820b5ca0b3524af981a0f17adcfae +LLD.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/64e40be1fcbdd57e900d424f3d9080b228b0f3a7770a0f4f81a6ebeb1623ed97606cb4a4588fa6ac516b8dc16e128c0d1638e48d3ca20aac61e2c929d0d42793 +LLD.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/51bbd26ec8a4466c5d188302f74574f2 +LLD.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/c600217aa0d9d475ae5a75d3c4dde10abaf4874b24e600f1714f4f221b74e1f20776795ffe7c134e06bd23ea7a91f8c60c2e8e82822e38fd720a890f6b6ec8e4 +LLD.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/2d79c8f3c0bf435937fb1ab631ed907e +LLD.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/21233bad163596c4e80fdcb5faa2e8aeaa4d86f0646f50ade9a6f395cda826e1b6af004fced6dc5acd0950e83e8f39f84453c2c98c658f8f1c93165dda11ceee +LLD.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/8e41116e2d61292fcef755476f3b02f7 +LLD.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/36b8944a39ff35804894bf64f5c9a40b03a240c43d36c09238efd2d27056d2e69a18913ea23d4b5344347c4eeb51d993e80d2e581b54fd95eb14dae25bdbd82e +LLD.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/c723d619bde9504d10726b05f0cf3c5e +LLD.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/a167654c2b1d6a81d13dd32787b5cdf9fc23ffdb3bda3db597b3a795b578e17a0730b555f7ac013816f7e9e083f49e660f6f93138ba7fcd181ef69cdd378979b +LLD.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/2bbd5a6e70cb97138a52ace47dd9d059 +LLD.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/fbffb8ee87bf18d9a6ffe6c1fff4b67c2e6345faa2de3238bf87643d368d7a7adba3bb8c01c179783e47eff370f5e8f094bba60b6073a47023277961a4b11d91 +LLD.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/1bd7e097fd221a1cb209345f3b753029 +LLD.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/855bdb1c6a586f86a194cc4590b12ae9c6fb0d666966f59d16d6ae5b9c04af435a8ac95863a6f2453fdaf752769e926dc47c61a54dd398d935d15427b17dedd1 +LLD.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/9fd3429f3eedaffab75a1f1eb5387726 +LLD.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/52cee403e0463fe4052cca0695390baea91a4a05ec05245cc12b9970384e8d1a9b58e963526076da7f387c61e6bcd63066a17221460fa85bf56e3d10fe86465f +LLD.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/118ddb25eb2da940e0a24464f1dd7825 +LLD.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/9a302c9f588d4e1b80c224fe3239b6b584f2393150a6f5ee457ee88e5bce59f3c87415bee51848d5c7b742126a5404a0f1fccf812394d1d6d1c432e3bc13535e +LLD.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/e8918ebffdb6abc39156d35b5f2f9e48 +LLD.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/387085aa9c01163da2634186ca0b498d79d8a44c14e683e8fee540ee32f1ff9594a66fa3cb3c49cd49d06a9f327c37e3ad36cedfb8a862a43f278480a97da3e5 +LLD.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/caad7e36b4bd734878ffa6ac630da181 +LLD.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/67f94119d100df7c1236414b398f5bc39d9e891d32abf0b5bfc03f605841ec39a5c4a2bdb9093a8032baba6677b92378c9b5372fc7d3b0d02d7064752c24a0da +LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/ab1a611cdfdd94d862b49a8d74e4fdee +LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/ce5a3183c53f8c47c3e9f9e9a8a78653ebf475aa31bffa91e1c695fc526323835cf65a1013cdb800eac3b6e5288128846f0d7862c771c3e0f94d022ed3e98c81 +LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/b48f84396ccc0227194416432b068967 +LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/cbef6f8ab00e279980fa7de6633508bb473f292daedfc822adbc9dc1965c6887cda3eedab9444d50aeec259f92640d45c9aee099ca188be03848f524e92fd1b6 +LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/3ece870b36d697253e8f32ce7467f081 +LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/c5477fa66990b374b3cd27afc64f343042841f05f89aecde646835aee5db8e1d1e6a4c64049ed662c5bebc13ebc6730b3aa40e3cf4484d8877544ac66d651bdb +LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/07514a3db1b06f13c3965140ce7e7fda +LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/2072f7626285d2b4dda2d7d6727287bb0b2048a5f5a6c46c45f3981faeb7405618d7d2375037104f5d57756f275058c5c8c561110b6adf0d5e674f5499339551 +LLD.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/d4ef5ec6287d636011640f6da7ad4882 +LLD.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/2875f3ede77081dc5fbbca3df7da171df6bca3bd339a5f74fbf1acf016203293fc46263c119981b5364e7156e92aa76a89641656132c3c1e52af9d5b62d32ccb +LLD.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/58f8723778b33cb8087cd17bb384cbc3 +LLD.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/38a3eda402f855bf8db33a84564b1dd4788b04b0048ab5070d58dd5e693c4855a97ce3052be6c775c5b8e663d57bfd30049f044e456368cc2ef69c71d463fbfb +LLD.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/c117ee72749bc5dba3802af434860679 +LLD.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/914521adb94db85b408665c3552de2dffa1247c7d7036c9df1716e0cc6401281d90378416b9af16efe04ff975f5e0cc05c9616d7c8da36e1566bee9e38347207 +LLD.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/1c7eebbd69b1ef2525be0fdd26abcf59 +LLD.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/b0cb3a271e7b4590692e37f830479d7065467bfbcaf1abf04c9148d6107def27c10a3f18ce3d6014513c8577484021d6c1f59bdf8acc87f1897e00869bb20cc7 +LLD.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/0019ccaa2c44c163e2fe7367b15f37ac +LLD.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/212bb2b4db2b9b557cb47e57f83e9d8835ac6a608226ff9ccddda217dd41c8b6932fd990073f51565dab6c893145fb6ce3d19317396bf9631778f04c8cc32014 +LLD.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/e9b0c8a37889346dd9514dd218a21bb2 +LLD.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/2ec2d3948c2a206fb46539a96b689646433f6d35142d1094a35642a55a42710121c50de0faa068dea83e124843582495c10fb7aa183573b6a9db784e8472e12f +LLD.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/3a53529de043f7cda9af0c8edbd2fe7b +LLD.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/36be33f081cefcaaaa632b731762e6d2d7b36046f2c8d47aa694a55be8ff14c2214efd99573c67d441268df906974329f0b0b39987d39d226765070f6bbd6b14 +LLD.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/31514c0017e92afd8ca35c9d8baff6f0 +LLD.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/b9d1322d6816075c18d2a2eb0c270cc66f311a45a777bd8079ab36a585942593e0b8a951282cdaae7484d6177ebf64695683814dd0a808ffc5006fa99853935c +LLD.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/e8180f2bc4193503b4234f47bad81da6 +LLD.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/b73aebc7f36657b2e99b6b1b12846a2a5cd2a630fef836658b96a7f71711b40897a9c56593859b45820c122f166441ebbc0139153de52535c66ebe432638c113 +LLD.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/5cade62c0c5966c58170fc4cc5edebe7 +LLD.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/f3aac09eb5cfda3300498eeffea5d31e413d65067394ffc1ba8b3416b8db05ef7a810d2d3f75e7a47ba8e15b0be7eb7da933f67b820150daeccb14ac78de12da +LLD.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/a31b9c5ed38ac28ad93cade4f3f3620e +LLD.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/9bc26bed1d8bf0cf3854c06397cac893d6bdc02e7c7f9736b1fa873e6afda0c4bfab3c724508382d4304137f8f7d7a77adeaead91844380254c8a35a072b3c68 +LLD.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/13da89b15a554d17f279143442ed6c2a +LLD.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/9e74f62580141f1cb09cfe21af016bc5b56128e03782a9727fb82f7f7bd802c0373750648ba4d43e6ded39180106ef3df190486029544c4a8f1d57518c45e118 +LLD.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/11806c7a732d195e84fef72d659a9055 +LLD.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/17c0c69d51b088b773d34016f66723a913d6db5ae82f5c3ae1f2b3429dd916b565a8b119d0c032f09f77105393052ab6206312789428cd52f1c397956d0a6d11 +LLD.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/1e8a08e4837a889cfca48bd7504f20e6 +LLD.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/30bdcb49693f23c439cecad3b7a66b6a6f7533d85cfb43344894c67de11809f4065784db95b968b013ab73dba0ded761e1925299e2606282c9fa1c9799e00978 +LLD.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/cc35b93f1dd6917e4975a743ec103c1b +LLD.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/9abbe63e95b358384a5188ce945a69671484ac68f07a6327ac1610555114b699f2c1b9844f31bdc83c297fb565c8270ffa030588bcdeb52576314ab1686cf07a +LLD.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/3f2c4935fc699fe54cbc1ce6071dea46 +LLD.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/f47ef724f2cecfc1c64b588d8bc6367427e3c59cfc34af6fe4a02e6d9b267814227938386313368702d69bd0e5d973d8ed517dee5b76f484087592b7fde57bea diff --git a/deps/lld.version b/deps/lld.version index d52ceb34552e2..dbb446d9e99be 100644 --- a/deps/lld.version +++ b/deps/lld.version @@ -1,3 +1,3 @@ ## jll artifact LLD_JLL_NAME := LLD -LLD_JLL_VER := 14.0.6+2 +LLD_JLL_VER := 14.0.6+3 diff --git a/stdlib/LLD_jll/Project.toml b/stdlib/LLD_jll/Project.toml index 7fbb85963f798..923f2e578e0d7 100644 --- a/stdlib/LLD_jll/Project.toml +++ b/stdlib/LLD_jll/Project.toml @@ -1,6 +1,6 @@ name = "LLD_jll" uuid = "d55e3150-da41-5e91-b323-ecfd1eec6109" -version = "14.0.5+3" +version = "14.0.6+3" [deps] Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" @@ -12,7 +12,7 @@ Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" [compat] julia = "1.9" -libLLVM_jll = "14.0.5" +libLLVM_jll = "14.0.6" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" From 94ad62892f8e9c7e5b77e5b6ec669acad31b6e6f Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Sun, 12 Feb 2023 11:35:27 -0500 Subject: [PATCH 094/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?SparseArrays=20stdlib=20from=20c4eeb2f=20to=20760989e=20(#48656?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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-760989ec650761541deff039efb5b767a051c172.tar.gz/md5 create mode 100644 deps/checksums/SparseArrays-760989ec650761541deff039efb5b767a051c172.tar.gz/sha512 delete mode 100644 deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/md5 delete mode 100644 deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/sha512 diff --git a/deps/checksums/SparseArrays-760989ec650761541deff039efb5b767a051c172.tar.gz/md5 b/deps/checksums/SparseArrays-760989ec650761541deff039efb5b767a051c172.tar.gz/md5 new file mode 100644 index 0000000000000..5b193948d6ff0 --- /dev/null +++ b/deps/checksums/SparseArrays-760989ec650761541deff039efb5b767a051c172.tar.gz/md5 @@ -0,0 +1 @@ +aec6c28d14142264e40929f9698e00e1 diff --git a/deps/checksums/SparseArrays-760989ec650761541deff039efb5b767a051c172.tar.gz/sha512 b/deps/checksums/SparseArrays-760989ec650761541deff039efb5b767a051c172.tar.gz/sha512 new file mode 100644 index 0000000000000..007b9d7c1f71a --- /dev/null +++ b/deps/checksums/SparseArrays-760989ec650761541deff039efb5b767a051c172.tar.gz/sha512 @@ -0,0 +1 @@ +7dd85f6835fccc2448f2213873ef79bd54712b33d4f1a4a3716e9c223ffa157f3fdfedafd971e4f5a2316dc447af1a44e81038459ec7b575587faf7e4dffac28 diff --git a/deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/md5 b/deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/md5 deleted file mode 100644 index 4cba54175c3fd..0000000000000 --- a/deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -9cdb1adf09239aaa1717825c1e7b14db diff --git a/deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/sha512 b/deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/sha512 deleted file mode 100644 index 922de677bede5..0000000000000 --- a/deps/checksums/SparseArrays-c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -489163ca058520b067cd78aeb8338212db7cf64f3c15f96b70ca2eb81de31afb192744788488abfda04b185d93841d1014f10556079604f9e7fae97b3b308181 diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version index 6b47310f52811..cd571a2237103 100644 --- a/stdlib/SparseArrays.version +++ b/stdlib/SparseArrays.version @@ -1,4 +1,4 @@ SPARSEARRAYS_BRANCH = main -SPARSEARRAYS_SHA1 = c4eeb2fc0d3c61d8587599faa2a8dfe1368b674f +SPARSEARRAYS_SHA1 = 760989ec650761541deff039efb5b767a051c172 SPARSEARRAYS_GIT_URL := https://github.com/JuliaSparse/SparseArrays.jl.git SPARSEARRAYS_TAR_URL = https://api.github.com/repos/JuliaSparse/SparseArrays.jl/tarball/$1 From a7121cf70ccc430013a12737f1388c5948873c39 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sun, 12 Feb 2023 15:28:54 -0500 Subject: [PATCH 095/775] Avoid creating :invoke for unbound tparams (#48652) A `MethodInstance` whose static parameter values are unknown will have `TypeVar`s in the corresponding slot in their sparam_vals object and have their code instance's `->invoke` method set to `jl_fptr_sparam`. I think the original expectation here was that at some point the runtime would externally compute the set of static parameters and pass them in as an argument (just as it would for regular arguments). However, as far as I can tell, no place in the runtime actually does this and instead static paramters are always tied to a particular MethodInstance. This is enforced by making sure that compilable signatures never have unbound typevars (unless that is the true answer). However, when we added the `compilesig_invokes` optimizer option, this allowed bypassing `get_compileable_sig`, causing unexpected errors around type parameters. This commit simply institutes a check that fixes this particular case, though I don't think the idea of wanting to :invoke MethodInstances with unknown sparams is all that unreasonable (especially since we can now inline such cases) and in the future we may want to revisit the runtime support for actually passing through sparams. --- base/compiler/ssair/inlining.jl | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 2cc9927740065..9b233c9fee036 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -795,7 +795,19 @@ function rewrite_apply_exprargs!(todo::Vector{Pair{Int,Any}}, end function compileable_specialization(match::MethodMatch, effects::Effects, - et::InliningEdgeTracker, @nospecialize(info::CallInfo); compilesig_invokes::Bool=true) + et::InliningEdgeTracker, @nospecialize(info::CallInfo); compilesig_invokes::Bool=true) + if !compilesig_invokes + # If there are unknown typevars, this MethodInstance is illegal to + # :invoke, but we only check for compilesig usually, so check here to + # avoid generating bad code. + # TODO: We could also compute the correct type parameters in the runtime + # and let this go through, but that requires further changes, because + # currently the runtime assumes that a MethodInstance with the appropriate + # sparams is created. + if _any(t->isa(t, TypeVar), match.sparams) + return nothing + end + end mi = specialize_method(match; compilesig=compilesig_invokes) mi === nothing && return nothing add_inlining_backedge!(et, mi) @@ -803,11 +815,9 @@ function compileable_specialization(match::MethodMatch, effects::Effects, end function compileable_specialization(linfo::MethodInstance, effects::Effects, - et::InliningEdgeTracker, @nospecialize(info::CallInfo); compilesig_invokes::Bool=true) - mi = specialize_method(linfo.def::Method, linfo.specTypes, linfo.sparam_vals; compilesig=compilesig_invokes) - mi === nothing && return nothing - add_inlining_backedge!(et, mi) - return InvokeCase(mi, effects, info) + et::InliningEdgeTracker, @nospecialize(info::CallInfo); compilesig_invokes::Bool=true) + return compileable_specialization(MethodMatch(linfo.specTypes, + linfo.sparam_vals, linfo.def::Method, false), effects, et, info; compilesig_invokes) end compileable_specialization(result::InferenceResult, args...; kwargs...) = (@nospecialize; From ecfaef3394be9d22459c855969dcc655fc7e6e91 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Sun, 12 Feb 2023 18:02:37 -0500 Subject: [PATCH 096/775] Update checksums for llvm 14.0.6+2 (#48659) --- deps/checksums/llvm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deps/checksums/llvm b/deps/checksums/llvm index 51bde45325990..d96d2da078b1f 100644 --- a/deps/checksums/llvm +++ b/deps/checksums/llvm @@ -262,5 +262,7 @@ libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/2b libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/adfa1edc0a4138d977fde832aaa6549b5ee38a1c0bb3b59dd9c05740569bd108c2b2b2de4e81ac06d367c9f834662fa5238972affee8bc638309e4470cd980f1 libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/605ba226b4d0d82802590eadf31d50ce libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/992dd8cf723b986d506743f2ea345391752893b07fc0be35129afbeb4cd01d41f32c56a99b0f6a25b51787bee9a56c60ce66fce60123e8fd3fe0fa11ba051b3d +llvm-julia-14.0.6-2.tar.gz/md5/1401091c768e6c4aef468bb3fb1fac83 +llvm-julia-14.0.6-2.tar.gz/sha512/42feedbfc5866ed1fde7e15810ba5224d46e61122d5fcbb4e4c4dfe72cb898e429bdfcdf6b0712fceefd8cc5b910857d7babfd73ce65e7f8a43cec42424a7c3d llvmunwind-12.0.1.tar.xz/md5/4ec327cee517fdb1f6a20e83748e2c7b llvmunwind-12.0.1.tar.xz/sha512/847b6ba03010a43f4fdbfdc49bf16d18fd18474d01584712e651b11191814bf7c1cf53475021d9ee447ed78413202b4ed97973d7bdd851d3e49f8d06f55a7af4 From b9b47aff93880367a1afa7359f29b03802efa792 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Sun, 12 Feb 2023 17:03:07 -0600 Subject: [PATCH 097/775] Remove unused "deps" mechanism in internal sorting keywords [NFC] (#48634) --- base/sort.jl | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/base/sort.jl b/base/sort.jl index 1af2bc79902ff..cb03298da43de 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -429,19 +429,18 @@ macro getkw(syms...) Expr(:block, (:($(esc(:((kw, $sym) = $getter(v, o, kw))))) for (sym, getter) in zip(syms, getters))...) end -for (sym, deps, exp, type) in [ - (:lo, (), :(firstindex(v)), Integer), - (:hi, (), :(lastindex(v)), Integer), - (:mn, (), :(throw(ArgumentError("mn is needed but has not been computed"))), :(eltype(v))), - (:mx, (), :(throw(ArgumentError("mx is needed but has not been computed"))), :(eltype(v))), - (:scratch, (), nothing, :(Union{Nothing, Vector})), # could have different eltype - (:allow_legacy_dispatch, (), true, Bool)] +for (sym, exp, type) in [ + (:lo, :(firstindex(v)), Integer), + (:hi, :(lastindex(v)), Integer), + (:mn, :(throw(ArgumentError("mn is needed but has not been computed"))), :(eltype(v))), + (:mx, :(throw(ArgumentError("mx is needed but has not been computed"))), :(eltype(v))), + (:scratch, nothing, :(Union{Nothing, Vector})), # could have different eltype + (:allow_legacy_dispatch, true, Bool)] usym = Symbol(:_, sym) @eval function $usym(v, o, kw) # using missing instead of nothing because scratch could === nothing. res = get(kw, $(Expr(:quote, sym)), missing) res !== missing && return kw, res::$type - @getkw $(deps...) $sym = $exp (;kw..., $sym), $sym::$type end From e8b9b5b7432a5215a78e9819c56433718fb7db22 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 12 Feb 2023 15:03:35 -0800 Subject: [PATCH 098/775] Include Test.{Result, Pass, Fail, Error, Broken} in documentation (#31854) --- stdlib/Test/docs/src/index.md | 10 ++++++++++ stdlib/Test/src/Test.jl | 10 +++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/stdlib/Test/docs/src/index.md b/stdlib/Test/docs/src/index.md index 1db2f1ab967f1..62d6fedbefc03 100644 --- a/stdlib/Test/docs/src/index.md +++ b/stdlib/Test/docs/src/index.md @@ -258,6 +258,16 @@ in the test set reporting. The test will not run but gives a `Broken` `Result`. Test.@test_skip ``` +## Test result types + +```@docs +Test.Result +Test.Pass +Test.Fail +Test.Error +Test.Broken +``` + ## Creating Custom `AbstractTestSet` Types Packages can create their own `AbstractTestSet` subtypes by implementing the `record` and `finish` diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 1c5f84303dd6d..8ac625e1eb8ff 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -70,7 +70,7 @@ end @nospecialize """ - Result + Test.Result All tests produce a result object. This object may or may not be stored, depending on whether the test is part of a test set. @@ -78,7 +78,7 @@ stored, depending on whether the test is part of a test set. abstract type Result end """ - Pass + Test.Pass <: Test.Result The test condition was true, i.e. the expression evaluated to true or the correct exception was thrown. @@ -108,7 +108,7 @@ function Base.show(io::IO, t::Pass) end """ - Fail + Test.Fail <: Test.Result The test condition was false, i.e. the expression evaluated to false or the correct exception was not thrown. @@ -168,7 +168,7 @@ function Base.show(io::IO, t::Fail) end """ - Error + Test.Error <: Test.Result The test condition couldn't be evaluated due to an exception, or it evaluated to something other than a [`Bool`](@ref). @@ -249,7 +249,7 @@ function Base.show(io::IO, t::Error) end """ - Broken + Test.Broken <: Test.Result The test condition is the expected (failed) result of a broken test, or was explicitly skipped with `@test_skip`. From 7f4b78d9088b4d8e32ff0c5bd7e1e18c4fad1c58 Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Mon, 13 Feb 2023 00:49:34 -0300 Subject: [PATCH 099/775] replace mfence with lock'ed inst on x86 (#48123) Co-authored-by: Valentin Churavy --- src/julia_atomics.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/julia_atomics.h b/src/julia_atomics.h index cb14e535cd010..4da2e4f7a9994 100644 --- a/src/julia_atomics.h +++ b/src/julia_atomics.h @@ -73,7 +73,18 @@ enum jl_memory_order { * are). We also need to access these atomic variables from the LLVM JIT code * which is very hard unless the layout of the object is fully specified. */ -#define jl_fence() atomic_thread_fence(memory_order_seq_cst) + +/** + * On modern Intel and AMD platforms `lock orq` on the SP is faster than + * `mfence`. GCC 11 did switch to this representation. See #48123 + */ +#if defined(_CPU_X86_64_) && \ + ((defined(__GNUC__) && __GNUC__ < 11) || \ + (defined(__clang__))) + #define jl_fence() __asm__ volatile("lock orq $0 , (%rsp)") +#else + #define jl_fence() atomic_thread_fence(memory_order_seq_cst) +#endif #define jl_fence_release() atomic_thread_fence(memory_order_release) #define jl_signal_fence() atomic_signal_fence(memory_order_seq_cst) From dd616c68b22c55bd0a638f866e79522705ccfd57 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Mon, 13 Feb 2023 16:48:17 +0900 Subject: [PATCH 100/775] restrict `code_cache(::AbstractInterpreter)` interface (#48632) Previously we had fallback code for case when `code_cache(::AbstractInterpreter)` returns custom user data structure other than `CodeInstance`. But now we require it to return `CodeInstance` in several places (e.g. `typeinf_edge`) so I don't think we want to keep it. This commit simply removes the fallback code and streamlines our cache interface a bit. Also added missing `@atomic` annotation for `codeinst_to_ir`. --- base/compiler/abstractinterpretation.jl | 8 ++------ base/compiler/ssair/inlining.jl | 3 +-- base/compiler/ssair/irinterp.jl | 2 +- base/compiler/ssair/passes.jl | 2 +- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 621de1354f769..27c88c7a87562 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1281,12 +1281,8 @@ function const_prop_methodinstance_heuristic(interp::AbstractInterpreter, # If so, there will be a good chance we might be able to const prop # all the way through and learn something new. code = get(code_cache(interp), mi, nothing) - if isdefined(code, :inferred) - if isa(code, CodeInstance) - inferred = @atomic :monotonic code.inferred - else - inferred = code.inferred - end + if isa(code, CodeInstance) + inferred = @atomic :monotonic code.inferred # TODO propagate a specific `CallInfo` that conveys information about this call if inlining_policy(interp, inferred, NoCallInfo(), IR_FLAG_NULL, mi, arginfo.argtypes) !== nothing return true diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 9b233c9fee036..e7fa8856c6101 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -839,9 +839,8 @@ end end effects = decode_effects(code.ipo_purity_bits) return CachedResult(src, effects) - else # fallback pass for external AbstractInterpreter cache - return CachedResult(code, Effects()) end + return CachedResult(nothing, Effects()) end # the general resolver for usual and const-prop'ed calls diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index 90260530f9bd4..c91a0fe014eac 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -119,7 +119,7 @@ struct IRInterpretationState end function codeinst_to_ir(interp::AbstractInterpreter, code::CodeInstance) - src = code.inferred + src = @atomic :monotonic code.inferred mi = code.def if isa(src, Vector{UInt8}) src = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), mi.def, C_NULL, src)::CodeInfo diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 4e93d6364749a..c19f290fd80f3 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1083,7 +1083,7 @@ function try_inline_finalizer!(ir::IRCode, argexprs::Vector{Any}, idx::Int, end src = @atomic :monotonic code.inferred else - src = code + src = nothing end src = inlining_policy(inlining.interp, src, info, IR_FLAG_NULL, mi, Any[]) From 80210e8b2091cbb3787e5039512d4ac48495f55a Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Mon, 13 Feb 2023 03:00:28 -0600 Subject: [PATCH 101/775] Additional sorting stability unit test (#48637) --- test/sorting.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/sorting.jl b/test/sorting.jl index 691f0a0e2bc39..3e7a2c793f4f1 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -84,6 +84,8 @@ end @test issorted(sort(1:2000, alg=Alg, by=x->0)) @test issorted(sort(1:2000, alg=Alg, by=x->x÷100)) end + @test sort(1:2000, by=x->x÷100, rev=true) == sort(1:2000, by=x->-x÷100) == + vcat(2000, (x:x+99 for x in 1900:-100:100)..., 1:99) end @testset "partialsort" begin From 6eabe5725086862c41276c666d2a0ec149828172 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Mon, 13 Feb 2023 04:10:56 -0500 Subject: [PATCH 102/775] add new precompile kwarg `timing=true` to the news (#48633) * add `timing=true` to the news * Update NEWS.md Co-authored-by: Christopher Rackauckas --------- Co-authored-by: Christopher Rackauckas --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index e875921f827fd..5f4a9cbb543cb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -45,6 +45,8 @@ Standard library changes packages being loaded in the Julia session. This has similar applications as the Requires.jl package but also supports precompilation and setting compatibility. +- `Pkg.precompile` now accepts `timing` as a keyword argument which displays per package timing information for precompilation (e.g. `Pkg.precompile(timing=true)`) + #### LinearAlgebra From 2a0b9dd50657beddb7a28d9ecf66b86367ca7084 Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Wed, 8 Feb 2023 11:23:40 +0100 Subject: [PATCH 103/775] make `insert_extension_triggers` idempotent --- base/loading.jl | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 7ad877153e45d..054f300e31027 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1104,18 +1104,18 @@ mutable struct ExtensionId ntriggers::Int # how many more packages must be defined until this is loaded end -const EXT_DORMITORY = Dict{PkgId,Vector{ExtensionId}}() +const EXT_PRIMED = Dict{PkgId, PkgId}() # Extension -> Parent +const EXT_DORMITORY = Dict{PkgId,Vector{ExtensionId}}() # Trigger -> Extensions that can be triggered by it const EXT_DORMITORY_FAILED = ExtensionId[] function insert_extension_triggers(pkg::PkgId) pkg.uuid === nothing && return - extensions_added = Set{PkgId}() for env in load_path() - insert_extension_triggers!(extensions_added, env, pkg) + insert_extension_triggers(env, pkg) end end -function insert_extension_triggers!(extensions_added::Set{PkgId}, env::String, pkg::PkgId)::Union{Nothing,Missing} +function insert_extension_triggers(env::String, pkg::PkgId)::Union{Nothing,Missing} project_file = env_project_file(env) if project_file isa String manifest_file = project_file_manifest_path(project_file) @@ -1133,7 +1133,7 @@ function insert_extension_triggers!(extensions_added::Set{PkgId}, env::String, p extensions === nothing && return weakdeps === nothing && return if weakdeps isa Dict{String, Any} - return _insert_extension_triggers!(extensions_added, pkg, extensions, weakdeps) + return _insert_extension_triggers(pkg, extensions, weakdeps) end d_weakdeps = Dict{String, String}() @@ -1148,7 +1148,7 @@ function insert_extension_triggers!(extensions_added::Set{PkgId}, env::String, p d_weakdeps[dep_name] = uuid end @assert length(d_weakdeps) == length(weakdeps) - return _insert_extension_triggers!(extensions_added, pkg, extensions, d_weakdeps) + return _insert_extension_triggers(pkg, extensions, d_weakdeps) end end end @@ -1156,13 +1156,14 @@ function insert_extension_triggers!(extensions_added::Set{PkgId}, env::String, p return nothing end -function _insert_extension_triggers!(extensions_added::Set{PkgId}, parent::PkgId, extensions::Dict{String, <:Any}, weakdeps::Dict{String, <:Any}) +function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, <:Any}, weakdeps::Dict{String, <:Any}) for (ext::String, triggers::Union{String, Vector{String}}) in extensions triggers isa String && (triggers = [triggers]) id = PkgId(uuid5(parent.uuid, ext), ext) - # Only add triggers for an extension from one env. - id in extensions_added && continue - push!(extensions_added, id) + if id in keys(EXT_PRIMED) || haskey(Base.loaded_modules, id) + continue # extension is already primed or loaded, don't add it again + end + EXT_PRIMED[id] = parent gid = ExtensionId(id, parent, 1 + length(triggers)) trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, parent) push!(trigger1, gid) From 6bd658eaf4f3b7495f17c5f28c2d2f1c5c0e3894 Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Fri, 10 Feb 2023 15:45:47 +0100 Subject: [PATCH 104/775] fix printing of parents to extensions in `@time_imports` --- base/loading.jl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 054f300e31027..5325516fc2462 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1020,11 +1020,9 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No elapsed = round((time_ns() - t_before) / 1e6, digits = 1) comp_time, recomp_time = cumulative_compile_time_ns() .- t_comp_before print(lpad(elapsed, 9), " ms ") - for extid in EXT_DORMITORY - if extid.id == pkg - print(extid.parentid.name, " → ") - break - end + parentid = get(EXT_PRIMED, pkg, nothing) + if parentid !== nothing + print(parentid.name, " → ") end print(pkg.name) if comp_time > 0 From eb18cb5d8f67e5d659235d343587a9db66601711 Mon Sep 17 00:00:00 2001 From: Ujjwal Sarswat <76774914+vmpyr@users.noreply.github.com> Date: Mon, 13 Feb 2023 14:57:38 +0530 Subject: [PATCH 105/775] Add complete documentation for `Base.countlines()` (#48503) * add complete documentation for Base.countlines() * change eol for better understanding --- base/io.jl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/base/io.jl b/base/io.jl index c2d6ad592bf0c..3bcae2e8d7836 100644 --- a/base/io.jl +++ b/base/io.jl @@ -1304,6 +1304,7 @@ end """ countlines(io::IO; eol::AbstractChar = '\\n') + countlines(filename::AbstractString; eol::AbstractChar = '\\n') Read `io` until the end of the stream/file and count the number of lines. To specify a file pass the filename as the first argument. EOL markers other than `'\\n'` are supported by @@ -1331,6 +1332,19 @@ julia> io = IOBuffer("JuliaLang is a GitHub organization."); julia> countlines(io, eol = '.') 1 +``` +```jldoctest +julia> write("my_file.txt", "JuliaLang is a GitHub organization.\\n") +36 + +julia> countlines("my_file.txt") +1 + +julia> countlines("my_file.txt", eol = 'n') +4 + +julia> rm("my_file.txt") + ``` """ function countlines(io::IO; eol::AbstractChar='\n') From f071673e08152f483eef1eed457f640df64cb319 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Mon, 13 Feb 2023 18:29:42 +0900 Subject: [PATCH 106/775] add missing atomic annotation on accesses to `MethodInstance.cache` (#48450) --- src/staticdata.c | 10 +++++----- src/staticdata_utils.c | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/staticdata.c b/src/staticdata.c index 94173c2a8a162..8f11d90b155f3 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -2025,7 +2025,7 @@ static jl_value_t *strip_codeinfo_meta(jl_method_t *m, jl_value_t *ci_, int orig static void strip_specializations_(jl_method_instance_t *mi) { assert(jl_is_method_instance(mi)); - jl_code_instance_t *codeinst = mi->cache; + jl_code_instance_t *codeinst = jl_atomic_load_relaxed(&mi->cache); while (codeinst) { jl_value_t *inferred = jl_atomic_load_relaxed(&codeinst->inferred); if (inferred && inferred != jl_nothing) { @@ -3058,13 +3058,13 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl } else if (ci->max_world) { // It's valid, but it may not be connected - if (!ci->def->cache) - ci->def->cache = ci; + if (!jl_atomic_load_relaxed(&ci->def->cache)) + jl_atomic_store_release(&ci->def->cache, ci); } else { // Ensure this code instance is not connected - if (ci->def->cache == ci) - ci->def->cache = NULL; + if (jl_atomic_load_relaxed(&ci->def->cache) == ci) + jl_atomic_store_release(&ci->def->cache, NULL); } } else if (jl_is_globalref(obj)) { diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index cb97db1083e32..8ba6136e54b0e 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -1075,7 +1075,7 @@ static void jl_insert_backedges(jl_array_t *edges, jl_array_t *ext_targets, jl_a remove_code_instance_from_validation((jl_code_instance_t*)ci); // mark it as handled } else { - jl_code_instance_t *codeinst = caller->cache; + jl_code_instance_t *codeinst = jl_atomic_load_relaxed(&caller->cache); while (codeinst) { remove_code_instance_from_validation(codeinst); // should be left invalid codeinst = jl_atomic_load_relaxed(&codeinst->next); @@ -1124,7 +1124,7 @@ static void jl_insert_backedges(jl_array_t *edges, jl_array_t *ext_targets, jl_a } } else { - jl_code_instance_t *codeinst = caller->cache; + jl_code_instance_t *codeinst = jl_atomic_load_relaxed(&caller->cache); while (codeinst) { if (remove_code_instance_from_validation(codeinst)) { // mark it as handled assert(codeinst->min_world >= world && codeinst->inferred); From 3e37734aad46dbb74598a87f0f86124db55a16f7 Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Mon, 13 Feb 2023 11:30:07 +0100 Subject: [PATCH 107/775] docs sort: clarify link between `Ordering` and `sort`. (#48413) --- doc/src/base/sort.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/src/base/sort.md b/doc/src/base/sort.md index 41b7096391a04..16e1839cf64a2 100644 --- a/doc/src/base/sort.md +++ b/doc/src/base/sort.md @@ -168,9 +168,11 @@ Base.Sort.defalg(::AbstractArray{<:Union{SmallInlineStrings, Missing}}) = Inline By default, `sort` and related functions use [`isless`](@ref) to compare two elements in order to determine which should come first. The [`Base.Order.Ordering`](@ref) abstract type provides a mechanism for defining -alternate orderings on the same set of elements. Instances of `Ordering` define -a [total order](https://en.wikipedia.org/wiki/Total_order) on a set of elements, -so that for any elements `a`, `b`, `c` the following hold: +alternate orderings on the same set of elements: when calling a sorting function like +`sort`, an instance of `Ordering` can be provided with the keyword argument `order`. + +Instances of `Ordering` define a [total order](https://en.wikipedia.org/wiki/Total_order) +on a set of elements, so that for any elements `a`, `b`, `c` the following hold: * Exactly one of the following is true: `a` is less than `b`, `b` is less than `a`, or `a` and `b` are equal (according to [`isequal`](@ref)). From cc1c6a650eb5a93812b6ff1ef777bcdc3bb9f6e7 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Mon, 13 Feb 2023 09:24:20 -0500 Subject: [PATCH 108/775] Throw an error if trying to access unassigned data in Base.RefValue (#45829) Co-authored-by: Kristoffer Carlsson --- base/refvalue.jl | 5 ++++- test/core.jl | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/base/refvalue.jl b/base/refvalue.jl index 7cbb651d41aee..000088ff0ce76 100644 --- a/base/refvalue.jl +++ b/base/refvalue.jl @@ -45,7 +45,10 @@ function unsafe_convert(P::Union{Type{Ptr{T}},Type{Ptr{Cvoid}}}, b::RefValue{T}) # If it is actually an immutable, then we can't take it's pointer directly # Instead, explicitly load the pointer from the `RefValue`, # which also ensures this returns same pointer as the one rooted in the `RefValue` object. - p = pointerref(Ptr{Ptr{Cvoid}}(pointer_from_objref(b)), 1, Core.sizeof(Ptr{Cvoid})) + p = atomic_pointerref(Ptr{Ptr{Cvoid}}(pointer_from_objref(b)), :monotonic) + end + if p == C_NULL + throw(UndefRefError()) end return p end diff --git a/test/core.jl b/test/core.jl index a6926860ed8db..adf895ba471ea 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7847,6 +7847,17 @@ import .Foo45350: x45350 f45350() = (global x45350 = 2) @test_throws ErrorException f45350() +@testset "Error behavior of unsafe_convert for RefValue" begin + b = Base.RefValue{Int}() + @test Base.unsafe_convert(Ptr{Int}, b) !== C_NULL + b = Base.RefValue{Base.RefValue{Int}}() + # throws because we hit `b.x` + @test_throws Core.UndefRefError Base.unsafe_convert(Ptr{Base.RefValue{Int}}, b) + # throws because we hit `b.x` + b = Base.RefValue{Integer}() + @test_throws Core.UndefRefError Base.unsafe_convert(Ptr{Integer}, b) +end + # #46503 - redefine `invoke`d methods foo46503(@nospecialize(a), b::Union{Vector{Any}, Float64, Nothing}) = rand() foo46503(a::Int, b::Nothing) = @invoke foo46503(a::Any, b) From 4a75129efe00ebe1880e3ab86f6901524ca063bc Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Mon, 13 Feb 2023 15:26:31 +0100 Subject: [PATCH 109/775] julia_cmd() docstring: include pkgimages (#48392) --- base/util.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/base/util.jl b/base/util.jl index 939ed9ae72bbb..f5290c025e0f1 100644 --- a/base/util.jl +++ b/base/util.jl @@ -137,7 +137,7 @@ See also [`print`](@ref), [`println`](@ref), [`show`](@ref). Return a julia command similar to the one of the running process. Propagates any of the `--cpu-target`, `--sysimage`, `--compile`, `--sysimage-native-code`, -`--compiled-modules`, `--inline`, `--check-bounds`, `--optimize`, `--min-optlevel`, `-g`, +`--compiled-modules`, `--pkgimages`, `--inline`, `--check-bounds`, `--optimize`, `--min-optlevel`, `-g`, `--code-coverage`, `--track-allocation`, `--color`, `--startup-file`, and `--depwarn` command line arguments that are not at their default values. @@ -151,6 +151,8 @@ Among others, `--math-mode`, `--warn-overwrite`, and `--trace-compile` are notab !!! compat "Julia 1.9" The keyword argument `cpu_target` was added. + + The flag `--pkgimages` was added in Julia 1.9. """ function julia_cmd(julia=joinpath(Sys.BINDIR, julia_exename()); cpu_target::Union{Nothing,String} = nothing) opts = JLOptions() From 43cc94d619c9509285de8ffe66bce505ca81409d Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Mon, 13 Feb 2023 09:30:15 -0600 Subject: [PATCH 110/775] Add unit tests for presorted inputs to sorting (#48638) * Add unit tests for presorted inputs to sorting --- base/sort.jl | 2 +- test/sorting.jl | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/base/sort.jl b/base/sort.jl index cb03298da43de..5ee247e0a0484 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -785,7 +785,7 @@ function _sort!(v::AbstractVector, a::CheckSorted, o::Ordering, kw) # For most large arrays, a reverse-sorted check is essentially free (overhead < 1%) if hi-lo >= 500 && _issorted(v, lo, hi, ReverseOrdering(o)) - # If reversing is valid, do so. This does violates stability. + # If reversing is valid, do so. This violates stability. reverse!(v, lo, hi) return scratch end diff --git a/test/sorting.jl b/test/sorting.jl index 3e7a2c793f4f1..100c8ada35c53 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -950,6 +950,17 @@ end test_allocs() end +@testset "Presorted and reverse-presorted" begin + for len in [7, 92, 412, 780] + x = sort(randn(len)) + for _ in 1:2 + @test issorted(sort(x)) + @test issorted(sort(x), by=x -> x+7) + reverse!(x) + end + end +end + # This testset is at the end of the file because it is slow. @testset "searchsorted" begin numTypes = [ Int8, Int16, Int32, Int64, Int128, From f6879913d1890d0f50e7f935feeddba4cb699481 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Mon, 13 Feb 2023 09:30:50 -0600 Subject: [PATCH 111/775] Add unit tests for WithoutMissingVector (#48636) * Add unit tests for WithoutMissingVector --- test/sorting.jl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/sorting.jl b/test/sorting.jl index 100c8ada35c53..15eec92166ec7 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -766,6 +766,18 @@ end @testset "Unions with missing" begin @test issorted(sort(shuffle!(vcat(fill(missing, 10), rand(Int, 100))))) @test issorted(sort(vcat(rand(Int8, 600), [missing]))) + + # Because we define defalg(::AbstractArray{Missing}) + @test all(fill(missing, 10) .=== sort(fill(missing, 10))) + + # Unit tests for WithoutMissingVector + a = [1,7,missing,4] + @test_throws ArgumentError Base.Sort.WithoutMissingVector(a) + @test eltype(a[[1,2,4]]) == eltype(a) + @test eltype(Base.Sort.WithoutMissingVector(a[[1,2,4]])) == Int + am = Base.Sort.WithoutMissingVector(a, unsafe=true) + @test am[2] == 7 + @test eltype(am) == Int end @testset "Specific algorithms" begin From b4e6b030a4c4318ffdae25df12619422f0cb7443 Mon Sep 17 00:00:00 2001 From: Sukera <11753998+Seelengrab@users.noreply.github.com> Date: Mon, 13 Feb 2023 16:31:40 +0100 Subject: [PATCH 112/775] Add UInt/Int to _str_sizehint for printing to strings (#40718) * Add UInt/Int to _str_sizehint for printing to strings Co-authored-by: Kristoffer Carlsson --- base/strings/io.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/base/strings/io.jl b/base/strings/io.jl index e800002076d54..b68bfd2deaaf7 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -125,6 +125,10 @@ function _str_sizehint(x) return sizeof(x) elseif x isa Char return ncodeunits(x) + elseif x isa UInt64 || x isa UInt32 + return ndigits(x) + elseif x isa Int64 || x isa Int32 + return ndigits(x) + (x < zero(x)) else return 8 end From aaab409957e32110cbc80318fee7215e616a555c Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 13 Feb 2023 17:23:04 +0100 Subject: [PATCH 113/775] [REPL] Meta-e: Don't execute the edited code when exiting editor (#46153) --- stdlib/REPL/src/LineEdit.jl | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 0dffcc6c1e276..8deb51de048a7 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -1357,19 +1357,22 @@ function edit_input(s, f = (filename, line, column) -> InteractiveUtils.edit(fil col += 1 end + # Write current input to temp file, edit, read back write(filename, str) f(filename, line, col) str_mod = readchomp(filename) rm(filename) - if str != str_mod # something was changed, run the input - write(buf, str_mod) - commit_line(s) - :done - else # no change, the edit session probably unsuccessful - write(buf, str) - seek(buf, pos) # restore state from before edit - refresh_line(s) + + # Write updated content + write(buf, str_mod) + if str == str_mod + # If input was not modified: reset cursor + seek(buf, pos) + else + # If input was modified: move cursor to end + move_input_end(s) end + refresh_line(s) end # return the identifier under the cursor, possibly with other words concatenated From 8a927d1fa06405538b30e8f752f0b968501a1209 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Mon, 13 Feb 2023 13:57:29 -0500 Subject: [PATCH 114/775] faster digits(::BigInt, base=2^n) via mpz_export (#47774) --- base/gmp.jl | 45 +++++++++++++++++++++++++++++++++------------ test/gmp.jl | 6 ++++-- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/base/gmp.jl b/base/gmp.jl index 0a71de28fffe9..76821716c0195 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -247,6 +247,17 @@ get_str!(x, a, b::BigInt) = (ccall((:__gmpz_get_str,libgmp), Ptr{Cchar}, (Ptr{Cc set_str!(x::BigInt, a, b) = Int(ccall((:__gmpz_set_str, libgmp), Cint, (mpz_t, Ptr{UInt8}, Cint), x, a, b)) get_d(a::BigInt) = ccall((:__gmpz_get_d, libgmp), Cdouble, (mpz_t,), a) +function export!(a::AbstractVector{T}, n::BigInt; order::Integer=-1, nails::Integer=0, endian::Integer=0) where {T<:Base.BitInteger} + stride(a, 1) == 1 || throw(ArgumentError("a must have stride 1")) + ndigits = cld(sizeinbase(n, 2), 8*sizeof(T) - nails) + length(a) < ndigits && resize!(a, ndigits) + count = Ref{Csize_t}() + ccall((:__gmpz_export, libgmp), Ptr{T}, (Ptr{T}, Ref{Csize_t}, Cint, Csize_t, Cint, Csize_t, mpz_t), + a, count, order, sizeof(T), endian, nails, n) + @assert count[] ≤ length(a) + return a, Int(count[]) +end + limbs_write!(x::BigInt, a) = ccall((:__gmpz_limbs_write, libgmp), Ptr{Limb}, (mpz_t, Clong), x, a) limbs_finish!(x::BigInt, a) = ccall((:__gmpz_limbs_finish, libgmp), Cvoid, (mpz_t, Clong), x, a) import!(x::BigInt, a, b, c, d, e, f) = ccall((:__gmpz_import, libgmp), Cvoid, @@ -750,19 +761,29 @@ function string(n::BigInt; base::Integer = 10, pad::Integer = 1) end function digits!(a::AbstractVector{T}, n::BigInt; base::Integer = 10) where {T<:Integer} - if 2 ≤ base ≤ 62 - s = codeunits(string(n; base)) - i, j = firstindex(a)-1, length(s)+1 - lasti = min(lastindex(a), firstindex(a) + length(s)-1 - isneg(n)) - while i < lasti - # base ≤ 36: 0-9, plus a-z for 10-35 - # base > 36: 0-9, plus A-Z for 10-35 and a-z for 36..61 - x = s[j -= 1] - a[i += 1] = base ≤ 36 ? (x>0x39 ? x-0x57 : x-0x30) : (x>0x39 ? (x>0x60 ? x-0x3d : x-0x37) : x-0x30) + if base ≥ 2 + if base ≤ 62 + # fast path using mpz_get_str via string(n; base) + s = codeunits(string(n; base)) + i, j = firstindex(a)-1, length(s)+1 + lasti = min(lastindex(a), firstindex(a) + length(s)-1 - isneg(n)) + while i < lasti + # base ≤ 36: 0-9, plus a-z for 10-35 + # base > 36: 0-9, plus A-Z for 10-35 and a-z for 36..61 + x = s[j -= 1] + a[i += 1] = base ≤ 36 ? (x>0x39 ? x-0x57 : x-0x30) : (x>0x39 ? (x>0x60 ? x-0x3d : x-0x37) : x-0x30) + end + lasti = lastindex(a) + while i < lasti; a[i+=1] = zero(T); end + return isneg(n) ? map!(-,a,a) : a + elseif a isa StridedVector{<:Base.BitInteger} && stride(a,1) == 1 && ispow2(base) && base-1 ≤ typemax(T) + # fast path using mpz_export + origlen = length(a) + _, writelen = MPZ.export!(a, n; nails = 8sizeof(T) - trailing_zeros(base)) + length(a) != origlen && resize!(a, origlen) # truncate to least-significant digits + a[begin+writelen:end] .= zero(T) + return isneg(n) ? map!(-,a,a) : a end - lasti = lastindex(a) - while i < lasti; a[i+=1] = zero(T); end - return isneg(n) ? map!(-,a,a) : a end return invoke(digits!, Tuple{typeof(a), Integer}, a, n; base) # slow generic fallback end diff --git a/test/gmp.jl b/test/gmp.jl index be11c70e5064f..8f6be13c38054 100644 --- a/test/gmp.jl +++ b/test/gmp.jl @@ -336,11 +336,13 @@ end @testset "digits" begin n = Int64(2080310129088201558) N = big(n) - for base in (2,7,10,11,16,30,50,62,64,100), pad in (0,1,10,100) - @test digits(n; base, pad) == digits(N; base, pad) + for base in (2,7,10,11,16,30,50,62,64,100,128), pad in (0,1,10,100) + @test digits(n; base, pad) == digits(N; base, pad) == digits(UInt8, N; base, pad) @test digits(-n; base, pad) == digits(-N; base, pad) @test digits!(Vector{Int}(undef, pad), n; base) == digits!(Vector{Int}(undef, pad), N; base) end + @test digits(UInt8, n; base=1<<8) == digits(UInt8, N; base=1<<8) + @test digits(UInt16, n; base=1<<16) == digits(UInt16, N; base=1<<16) end # serialization (#5133) From 488ec2cc3de51061c086dc2a4ce302651d2bb2a6 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 13 Feb 2023 22:19:59 +0100 Subject: [PATCH 115/775] Support italic output in `printstyled` (#45164) --- NEWS.md | 1 + base/util.jl | 27 +++++++++++++++++++-------- test/misc.jl | 8 ++++++-- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/NEWS.md b/NEWS.md index 5f4a9cbb543cb..33ab22f5ef07e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -34,6 +34,7 @@ New library features is now a no-op ([#47979]). It previously exposed unsafe behavior ([#47977]). * `binomial(x, k)` now supports non-integer `x` ([#48124]). * A `CartesianIndex` is now treated as a "scalar" for broadcasting ([#47044]). +* `printstyled` now supports italic output ([#45164]). Standard library changes ------------------------ diff --git a/base/util.jl b/base/util.jl index f5290c025e0f1..bce768c61f335 100644 --- a/base/util.jl +++ b/base/util.jl @@ -22,6 +22,7 @@ const text_colors = Dict{Union{Symbol,Int},String}( :normal => "\033[0m", :default => "\033[39m", :bold => "\033[1m", + :italic => "\033[3m", :underline => "\033[4m", :blink => "\033[5m", :reverse => "\033[7m", @@ -35,6 +36,7 @@ end const disable_text_style = Dict{Symbol,String}( :bold => "\033[22m", + :italic => "\033[23m", :underline => "\033[24m", :blink => "\033[25m", :reverse => "\033[27m", @@ -47,7 +49,7 @@ const disable_text_style = Dict{Symbol,String}( # Create a docstring with an automatically generated list # of colors. let color_syms = collect(Iterators.filter(x -> !isa(x, Integer), keys(text_colors))), - formatting_syms = [:normal, :bold, :default] + formatting_syms = [:normal, :bold, :italic, :default] global const available_text_colors = cat( sort!(intersect(color_syms, formatting_syms), rev=true), sort!(setdiff( color_syms, formatting_syms)); @@ -69,7 +71,7 @@ Printing with the color `:nothing` will print the string without modifications. text_colors function with_output_color(@nospecialize(f::Function), color::Union{Int, Symbol}, io::IO, args...; - bold::Bool = false, underline::Bool = false, blink::Bool = false, + bold::Bool = false, italic::Bool = false, underline::Bool = false, blink::Bool = false, reverse::Bool = false, hidden::Bool = false) buf = IOBuffer() iscolor = get(io, :color, false)::Bool @@ -80,12 +82,14 @@ function with_output_color(@nospecialize(f::Function), color::Union{Int, Symbol} print(io, str) else bold && color === :bold && (color = :nothing) + italic && color === :italic && (color = :nothing) underline && color === :underline && (color = :nothing) blink && color === :blink && (color = :nothing) reverse && color === :reverse && (color = :nothing) hidden && color === :hidden && (color = :nothing) enable_ansi = get(text_colors, color, text_colors[:default]) * (bold ? text_colors[:bold] : "") * + (italic ? text_colors[:italic] : "") * (underline ? text_colors[:underline] : "") * (blink ? text_colors[:blink] : "") * (reverse ? text_colors[:reverse] : "") * @@ -96,6 +100,7 @@ function with_output_color(@nospecialize(f::Function), color::Union{Int, Symbol} (blink ? disable_text_style[:blink] : "") * (underline ? disable_text_style[:underline] : "") * (bold ? disable_text_style[:bold] : "") * + (italic ? disable_text_style[:italic] : "") * get(disable_text_style, color, text_colors[:default]) first = true for line in eachsplit(str, '\n') @@ -110,27 +115,33 @@ function with_output_color(@nospecialize(f::Function), color::Union{Int, Symbol} end """ - printstyled([io], xs...; bold::Bool=false, underline::Bool=false, blink::Bool=false, reverse::Bool=false, hidden::Bool=false, color::Union{Symbol,Int}=:normal) + printstyled([io], xs...; bold::Bool=false, italic::Bool=false, underline::Bool=false, blink::Bool=false, reverse::Bool=false, hidden::Bool=false, color::Union{Symbol,Int}=:normal) Print `xs` in a color specified as a symbol or integer, optionally in bold. Keyword `color` may take any of the values $(Base.available_text_colors_docstring) or an integer between 0 and 255 inclusive. Note that not all terminals support 256 colors. -Keywords `bold=true`, `underline=true`, `blink=true` are self-explanatory. +Keywords `bold=true`, `italic=true`, `underline=true`, `blink=true` are self-explanatory. Keyword `reverse=true` prints with foreground and background colors exchanged, and `hidden=true` should be invisible in the terminal but can still be copied. These properties can be used in any combination. See also [`print`](@ref), [`println`](@ref), [`show`](@ref). +!!! note + Not all terminals support italic output. Some terminals interpret italic as reverse or + blink. + !!! compat "Julia 1.7" Keywords except `color` and `bold` were added in Julia 1.7. +!!! compat "Julia 1.9" + Support for italic output was added in Julia 1.9. """ -@constprop :none printstyled(io::IO, msg...; bold::Bool=false, underline::Bool=false, blink::Bool=false, reverse::Bool=false, hidden::Bool=false, color::Union{Int,Symbol}=:normal) = - with_output_color(print, color, io, msg...; bold=bold, underline=underline, blink=blink, reverse=reverse, hidden=hidden) -@constprop :none printstyled(msg...; bold::Bool=false, underline::Bool=false, blink::Bool=false, reverse::Bool=false, hidden::Bool=false, color::Union{Int,Symbol}=:normal) = - printstyled(stdout, msg...; bold=bold, underline=underline, blink=blink, reverse=reverse, hidden=hidden, color=color) +@constprop :none printstyled(io::IO, msg...; bold::Bool=false, italic::Bool=false, underline::Bool=false, blink::Bool=false, reverse::Bool=false, hidden::Bool=false, color::Union{Int,Symbol}=:normal) = + with_output_color(print, color, io, msg...; bold=bold, italic=italic, underline=underline, blink=blink, reverse=reverse, hidden=hidden) +@constprop :none printstyled(msg...; bold::Bool=false, italic::Bool=false, underline::Bool=false, blink::Bool=false, reverse::Bool=false, hidden::Bool=false, color::Union{Int,Symbol}=:normal) = + printstyled(stdout, msg...; bold=bold, italic=italic, underline=underline, blink=blink, reverse=reverse, hidden=hidden, color=color) """ Base.julia_cmd(juliapath=joinpath(Sys.BINDIR, julia_exename()); cpu_target) diff --git a/test/misc.jl b/test/misc.jl index 7c9fa3c1fbc41..03a17c84c3c66 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -859,6 +859,10 @@ let buf = IOBuffer() printstyled(buf_color, "foo"; bold=true, color=:red) @test String(take!(buf)) == "\e[31m\e[1mfoo\e[22m\e[39m" + # Check that italic is turned off + printstyled(buf_color, "foo"; italic=true, color=:red) + @test String(take!(buf)) == "\e[31m\e[3mfoo\e[23m\e[39m" + # Check that underline is turned off printstyled(buf_color, "foo"; color = :red, underline = true) @test String(take!(buf)) == "\e[31m\e[4mfoo\e[24m\e[39m" @@ -876,8 +880,8 @@ let buf = IOBuffer() @test String(take!(buf)) == "\e[31m\e[8mfoo\e[28m\e[39m" # Check that all options can be turned on simultaneously - printstyled(buf_color, "foo"; color = :red, bold = true, underline = true, blink = true, reverse = true, hidden = true) - @test String(take!(buf)) == "\e[31m\e[1m\e[4m\e[5m\e[7m\e[8mfoo\e[28m\e[27m\e[25m\e[24m\e[22m\e[39m" + printstyled(buf_color, "foo"; color = :red, bold = true, italic = true, underline = true, blink = true, reverse = true, hidden = true) + @test String(take!(buf)) == "\e[31m\e[1m\e[3m\e[4m\e[5m\e[7m\e[8mfoo\e[28m\e[27m\e[25m\e[24m\e[22m\e[23m\e[39m" end abstract type DA_19281{T, N} <: AbstractArray{T, N} end From 9c4a40c7b9e4e1ebb6bfb70fe66fc6fb8e4f4b6d Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Mon, 13 Feb 2023 17:06:17 -0500 Subject: [PATCH 116/775] Disable frame-pointer-optimiation on Linux (#48660) --- src/codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 29e4d30040a98..889e84175d40c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6,7 +6,7 @@ #if defined(_CPU_X86_) #define JL_NEED_FLOATTEMP_VAR 1 #endif -#if defined(_OS_WINDOWS_) || defined(_OS_FREEBSD_) || defined(_COMPILER_MSAN_ENABLED_) +#if defined(_OS_LINUX_) || defined(_OS_WINDOWS_) || defined(_OS_FREEBSD_) || defined(_COMPILER_MSAN_ENABLED_) #define JL_DISABLE_FPO #endif From dd832c0a2be4dbdd7895080aa1547a3ab0783f9f Mon Sep 17 00:00:00 2001 From: Simone Carlo Surace <51025924+simsurace@users.noreply.github.com> Date: Mon, 13 Feb 2023 23:08:14 +0100 Subject: [PATCH 117/775] docs: correct example of nthreads (#48643) The manual was saying that invoking `Threads.nthreads()` in a Julia session started with `-t 3,1` should return 4. It does not, and probably should not. It gives the number of threads in the default thread pool, which in the example is 3. --- doc/src/manual/multi-threading.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/src/manual/multi-threading.md b/doc/src/manual/multi-threading.md index b012de27ac81f..7c48581bd4bea 100644 --- a/doc/src/manual/multi-threading.md +++ b/doc/src/manual/multi-threading.md @@ -104,19 +104,26 @@ the `:interactive` threadpool: ```julia-repl julia> using Base.Threads -julia> nthreads() -4 - julia> nthreadpools() 2 julia> threadpool() :default +julia> nthreads(:default) +3 + julia> nthreads(:interactive) 1 + +julia> nthreads() +3 ``` +!!! note + The zero-argument version of `nthreads` returns the number of threads + in the default pool. + Either or both numbers can be replaced with the word `auto`, which causes Julia to choose a reasonable default. From 154151945783883b47ff23b2a71fb821ce798fcd Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Mon, 13 Feb 2023 19:19:06 -0500 Subject: [PATCH 118/775] compiler: Fix typo in ConstAPI removal (#48666) --- base/compiler/inferencestate.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index b75cffc72bbe4..7ff740a23a540 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -328,7 +328,7 @@ function should_insert_coverage(mod::Module, src::CodeInfo) return true end end - elseif isa(linetable, Vector{LineInfo}) + elseif isa(linetable, Vector{LineInfoNode}) for line in linetable if is_file_tracked(line.file) return true From 9930263bc69ba47a95ee4c1e6e8fee039aa01fe6 Mon Sep 17 00:00:00 2001 From: Chris Elrod Date: Mon, 13 Feb 2023 19:27:51 -0500 Subject: [PATCH 119/775] Faster binary gcd (#45577) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Faster binary gcd Implementation following https://en.algorithmica.org/hpc/algorithms/gcd/ On 1024 random inputs ```julia julia> @benchmark @. $z = oldgcd($x, $y) BenchmarkTools.Trial: 10000 samples with 1 evaluation. Range (min … max): 84.290 μs … 109.642 μs ┊ GC (min … max): 0.00% … 0.00% Time (median): 91.439 μs ┊ GC (median): 0.00% Time (mean ± σ): 91.573 μs ± 508.629 ns ┊ GC (mean ± σ): 0.00% ± 0.00% █ ▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▂▂██▁▁▁▁▁▁▂▂▃▃ ▂ 84.3 μs Histogram: frequency by time 93.1 μs < Memory estimate: 0 bytes, allocs estimate: 0. julia> @benchmark @. $z = newgcd($x, $y) BenchmarkTools.Trial: 10000 samples with 1 evaluation. Range (min … max): 74.241 μs … 101.972 μs ┊ GC (min … max): 0.00% … 0.00% Time (median): 79.521 μs ┊ GC (median): 0.00% Time (mean ± σ): 79.646 μs ± 566.683 ns ┊ GC (mean ± σ): 0.00% ± 0.00% █▅ ▄▂ ▁ ▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▆██▁▃▄▁▁▁▁▁▁▁███ █ 74.2 μs Histogram: log(frequency) by time 81.2 μs < Memory estimate: 0 bytes, allocs estimate: 0. ``` Something to consider: do we want to special case certain inputs like `1` or `0`? * Update intfuncs.jl Try to handle gcd overflow correctly. * Update base/intfuncs.jl Co-authored-by: Sukera <11753998+Seelengrab@users.noreply.github.com> * Update base/intfuncs.jl Co-authored-by: Sukera <11753998+Seelengrab@users.noreply.github.com> * Update base/intfuncs.jl Co-authored-by: Sukera <11753998+Seelengrab@users.noreply.github.com> * Update base/intfuncs.jl Co-authored-by: Sukera <11753998+Seelengrab@users.noreply.github.com> * fix for unsigned and simplify overflow check --------- Co-authored-by: Sukera <11753998+Seelengrab@users.noreply.github.com> Co-authored-by: Oscar Smith --- base/intfuncs.jl | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 5b3ada3b89877..4ecb2b36be63b 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -48,33 +48,46 @@ function gcd(a::T, b::T) where T<:Integer end function gcd(a::T, b::T) where T<:BitInteger - a == 0 && return checked_abs(b) - b == 0 && return checked_abs(a) - r = _gcd(a, b) - signbit(r) && __throw_gcd_overflow(a, b) - return r + a == 0 && return Base.checked_abs(b) + b == 0 && return Base.checked_abs(a) + if a isa Signed && a == typemin(T) + if a == b + Base.__throw_gcd_overflow(a, b) + else + a, b = b, a + end + end + return _gcd(a, b) end @noinline __throw_gcd_overflow(a, b) = throw(OverflowError(LazyString("gcd(", a, ", ", b, ") overflows"))) +function absdiff(x::T,y::T) where {T<:Unsigned} + d = max(x,y) - min(x,y) + d, d +end +function absdiff(x::T,y::T) where {T<:Signed} + d = x - y + abs(d), d +end # binary GCD (aka Stein's) algorithm # about 1.7x (2.1x) faster for random Int64s (Int128s) # Unfortunately, we need to manually annotate this as `@assume_effects :terminates_locally` to work around #41694. # Since this is used in the Rational constructor, constant folding is something we do care about here. -@assume_effects :terminates_locally function _gcd(a::T, b::T) where T<:BitInteger - za = trailing_zeros(a) - zb = trailing_zeros(b) +@assume_effects :terminates_locally function _gcd(ain::T, bin::T) where T<:BitInteger + zb = trailing_zeros(bin) + za = trailing_zeros(ain) + a = abs(ain) + b = abs(bin >> zb) k = min(za, zb) - u = unsigned(abs(a >> za)) - v = unsigned(abs(b >> zb)) - while u != v - if u > v - u, v = v, u - end - v -= u - v >>= trailing_zeros(v) + while a != 0 + a >>= za + absd, diff = absdiff(a, b) + za = trailing_zeros(diff) + b = min(a, b) + a = absd end - r = u << k + r = b << k return r % T end From cd955e15fa6f55cd24a7b759d64b4ba72595e1bb Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Tue, 14 Feb 2023 15:23:50 +0800 Subject: [PATCH 120/775] Inference: more thorough `UnionAll` renaming in `apply_type_tfunc` (#48662) --- base/compiler/tfuncs.jl | 26 ++++++++++++++++++++++---- test/compiler/inference.jl | 1 + 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 71dcd0ff97ea0..a8f1726e9f0fc 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1741,6 +1741,17 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K, canconst = true tparams = Any[] outervars = TypeVar[] + + # first push the tailing vars from headtype into outervars + outer_start, ua = 0, headtype + while isa(ua, UnionAll) + if (outer_start += 1) > largs + push!(outervars, ua.var) + end + ua = ua.body + end + outer_start = outer_start - largs + 1 + varnamectr = 1 ua = headtype for i = 1:largs @@ -1759,9 +1770,16 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K, uncertain = true unw = unwrap_unionall(ai) isT = isType(unw) - if isT && isa(ai,UnionAll) && contains_is(outervars, ai.var) - ai = rename_unionall(ai) - unw = unwrap_unionall(ai) + if isT + tai = ai + while isa(tai, UnionAll) + if contains_is(outervars, tai.var) + ai = rename_unionall(ai) + unw = unwrap_unionall(ai) + break + end + tai = tai.body + end end ai_w = widenconst(ai) ub = ai_w isa Type && ai_w <: Type ? instanceof_tfunc(ai)[1] : Any @@ -1822,7 +1840,7 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K, return Type{<:appl} end ans = Type{appl} - for i = length(outervars):-1:1 + for i = length(outervars):-1:outer_start ans = UnionAll(outervars[i], ans) end return ans diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index b136f91db8ca2..841add157e5f9 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2743,6 +2743,7 @@ let 𝕃 = Core.Compiler.fallback_lattice @test apply_type_tfunc(𝕃, Const(Issue47089), A, A) <: (Type{Issue47089{A,B}} where {A<:Integer, B<:Integer}) end @test only(Base.return_types(keys, (Dict{String},))) == Base.KeySet{String, T} where T<:(Dict{String}) +@test only(Base.return_types((r)->similar(Array{typeof(r[])}, 1), (Base.RefValue{Array{Int}},))) == Vector{T} where T<:(Array{Int}) # PR 27351, make sure optimized type intersection for method invalidation handles typevars From 6b85327d94fd4d5513e7eda7d0e1e078e7143ca0 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Tue, 14 Feb 2023 08:32:59 -0600 Subject: [PATCH 121/775] Simplify error handling in BitSet (#47732) --- base/bitset.jl | 22 +++++----------------- test/bitset.jl | 14 ++++++++++---- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/base/bitset.jl b/base/bitset.jl index 8727b857bd36b..6e18f7f472d42 100644 --- a/base/bitset.jl +++ b/base/bitset.jl @@ -125,7 +125,7 @@ end function union!(s::BitSet, r::AbstractUnitRange{<:Integer}) isempty(r) && return s - a, b = _check_bitset_bounds(first(r)), _check_bitset_bounds(last(r)) + a, b = Int(first(r)), Int(last(r)) cidxa = _div64(a) cidxb = _div64(b) if s.offset == NO_OFFSET @@ -247,20 +247,7 @@ function _matched_map!(f, a1::Bits, b1::Int, a2::Bits, b2::Int, b1 # the new offset end - -@noinline _throw_bitset_bounds_err() = - throw(ArgumentError("elements of BitSet must be between typemin(Int) and typemax(Int)")) - -@inline _is_convertible_Int(n) = typemin(Int) <= n <= typemax(Int) - -@inline _check_bitset_bounds(n) = - _is_convertible_Int(n) ? Int(n) : _throw_bitset_bounds_err() - -@inline _check_bitset_bounds(n::Int) = n - -@noinline _throw_keyerror(n) = throw(KeyError(n)) - -@inline push!(s::BitSet, n::Integer) = _setint!(s, _check_bitset_bounds(n), true) +@inline push!(s::BitSet, n::Integer) = _setint!(s, Int(n), true) push!(s::BitSet, ns::Integer...) = (for n in ns; push!(s, n); end; s) @@ -271,7 +258,7 @@ push!(s::BitSet, ns::Integer...) = (for n in ns; push!(s, n); end; s) delete!(s, n) n else - _throw_keyerror(n) + throw(KeyError(n)) end end @@ -284,6 +271,7 @@ end end end +@inline _is_convertible_Int(n) = typemin(Int) <= n <= typemax(Int) @inline delete!(s::BitSet, n::Int) = _setint!(s, n, false) @inline delete!(s::BitSet, n::Integer) = _is_convertible_Int(n) ? delete!(s, Int(n)) : s @@ -324,7 +312,7 @@ function symdiff!(s::BitSet, ns::AbstractSet) end function int_symdiff!(s::BitSet, n::Integer) - n0 = _check_bitset_bounds(n) + n0 = Int(n) val = !(n0 in s) _setint!(s, n0, val) s diff --git a/test/bitset.jl b/test/bitset.jl index ca8e06adc1ec4..f8c5d3fffd7d2 100644 --- a/test/bitset.jl +++ b/test/bitset.jl @@ -38,9 +38,12 @@ end @test !in(1,s) @test in(2,s) @test !in(10002,s) - @test in(10000,s) + @test in(UInt128(10000),s) + @test in(Int32(10000),s) @test in(10000.0,s) @test !in(10002.0,s) + @test !in(typemax(UInt), s) + @test !in(typemin(Int)-Int128(14), s) @test_throws ArgumentError first(BitSet()) @test_throws ArgumentError last(BitSet()) t = copy(s) @@ -157,13 +160,16 @@ end for n in -20:0 @test length(delete!(s, n)) == len end + @test length(delete!(s, typemax(UInt))) == len @test pop!(s, 1) === 1 @test !(1 in s) @test_throws KeyError pop!(s, 1) @test_throws KeyError pop!(s, -1) @test pop!(s, -1, 1) === 1 @test pop!(s, 1, 0) === 0 - @test s === delete!(s, 1) + @test 5 in s + @test s === delete!(s, 1) === delete!(s, Int8(5)) + @test !(5 in s) for i in s; pop!(s, i); end @test isempty(s) push!(s, 100) @@ -348,8 +354,8 @@ end x = BitSet(rand(-1000:1000, 500)) y = copy(x) @test union!(x, BitSet(a:b)) == union!(y, BitSet(a:1:b)) - @test_throws ArgumentError BitSet(Int128(typemin(Int))-1:typemin(Int)) - @test_throws ArgumentError BitSet(typemax(Int):Int128(typemax(Int))+1) + @test_throws InexactError BitSet(Int128(typemin(Int))-1:typemin(Int)) + @test_throws InexactError BitSet(typemax(Int):Int128(typemax(Int))+1) # union! with an empty range doesn't modify the BitSet @test union!(x, b:a) == y end From 1c5b80faa2105a239a7779d373aec7d6bde07ec3 Mon Sep 17 00:00:00 2001 From: Arun sanganal <74652697+ArunSanganal@users.noreply.github.com> Date: Tue, 14 Feb 2023 20:04:27 +0530 Subject: [PATCH 122/775] Make startswith work with IO objects (#43055) Co-authored-by: Steven G. Johnson --- NEWS.md | 1 + base/essentials.jl | 2 +- base/strings/util.jl | 19 +++++++++++++++++++ test/strings/basic.jl | 6 ++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 33ab22f5ef07e..630793e914a11 100644 --- a/NEWS.md +++ b/NEWS.md @@ -39,6 +39,7 @@ New library features Standard library changes ------------------------ +* `startswith` now supports seekable `IO` streams ([#43055]) #### Package Manager diff --git a/base/essentials.jl b/base/essentials.jl index a9794f372a0d5..14ccce0d125fb 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -946,7 +946,7 @@ function popfirst! end peek(stream[, T=UInt8]) Read and return a value of type `T` from a stream without advancing the current position -in the stream. +in the stream. See also [`startswith(stream, char_or_string)`](@ref). # Examples diff --git a/base/strings/util.jl b/base/strings/util.jl index dabb84ae65639..7a42d7fecfc91 100644 --- a/base/strings/util.jl +++ b/base/strings/util.jl @@ -67,6 +67,25 @@ function startswith(a::Union{String, SubString{String}}, end end +""" + startswith(io::IO, prefix::Union{AbstractString,Base.Chars}) + +Check if an `IO` object starts with a prefix. See also [`peek`](@ref). +""" +function Base.startswith(io::IO, prefix::Base.Chars) + mark(io) + c = read(io, Char) + reset(io) + return c in prefix +end +function Base.startswith(io::IO, prefix::Union{String,SubString{String}}) + mark(io) + s = read(io, ncodeunits(prefix)) + reset(io) + return s == codeunits(prefix) +end +Base.startswith(io::IO, prefix::AbstractString) = startswith(io, String(prefix)) + function endswith(a::Union{String, SubString{String}}, b::Union{String, SubString{String}}) cub = ncodeunits(b) diff --git a/test/strings/basic.jl b/test/strings/basic.jl index d20c4890d0b0a..ed3a0fe858051 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -164,6 +164,12 @@ end @test endswith(y)(y) @test endswith(z, z) @test endswith(z)(z) + #40616 startswith for IO objects + let s = "JuliaLang", io = IOBuffer(s) + for prefix in ("Julia", "July", s^2, "Ju", 'J', 'x', ('j','J')) + @test startswith(io, prefix) == startswith(s, prefix) + end + end end @testset "SubStrings and Views" begin From 48572afbe0c739bdef22f7b942713ca2af83b366 Mon Sep 17 00:00:00 2001 From: Jim Garrison Date: Tue, 14 Feb 2023 09:35:01 -0500 Subject: [PATCH 123/775] Use `cld` instead of float division in `range.jl` `_findin` (#48653) --- base/range.jl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/base/range.jl b/base/range.jl index ce3660c0ab141..50b1ef4751528 100644 --- a/base/range.jl +++ b/base/range.jl @@ -1243,19 +1243,17 @@ end # _findin (the index of intersection) function _findin(r::AbstractRange{<:Integer}, span::AbstractUnitRange{<:Integer}) - local ifirst - local ilast fspan = first(span) lspan = last(span) fr = first(r) lr = last(r) sr = step(r) if sr > 0 - ifirst = fr >= fspan ? 1 : ceil(Integer,(fspan-fr)/sr)+1 - ilast = lr <= lspan ? length(r) : length(r) - ceil(Integer,(lr-lspan)/sr) + ifirst = fr >= fspan ? 1 : cld(fspan-fr, sr)+1 + ilast = lr <= lspan ? length(r) : length(r) - cld(lr-lspan, sr) elseif sr < 0 - ifirst = fr <= lspan ? 1 : ceil(Integer,(lspan-fr)/sr)+1 - ilast = lr >= fspan ? length(r) : length(r) - ceil(Integer,(lr-fspan)/sr) + ifirst = fr <= lspan ? 1 : cld(lspan-fr, sr)+1 + ilast = lr >= fspan ? length(r) : length(r) - cld(lr-fspan, sr) else ifirst = fr >= fspan ? 1 : length(r)+1 ilast = fr <= lspan ? length(r) : 0 From ff6a3cf3aa91938d6e228ce44b253f1cd671817a Mon Sep 17 00:00:00 2001 From: Jim Garrison Date: Tue, 14 Feb 2023 09:35:45 -0500 Subject: [PATCH 124/775] Use `fld` in `_colon` method in `range.jl` (#48654) --- base/range.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/range.jl b/base/range.jl index 50b1ef4751528..ea29516f64d24 100644 --- a/base/range.jl +++ b/base/range.jl @@ -24,9 +24,9 @@ _colon(::Ordered, ::Any, start::T, step, stop::T) where {T} = StepRange(start, step, stop) # for T<:Union{Float16,Float32,Float64} see twiceprecision.jl _colon(::Ordered, ::ArithmeticRounds, start::T, step, stop::T) where {T} = - StepRangeLen(start, step, floor(Integer, (stop-start)/step)+1) + StepRangeLen(start, step, convert(Integer, fld(stop - start, step)) + 1) _colon(::Any, ::Any, start::T, step, stop::T) where {T} = - StepRangeLen(start, step, floor(Integer, (stop-start)/step)+1) + StepRangeLen(start, step, convert(Integer, fld(stop - start, step)) + 1) """ (:)(start, [step], stop) From 6976bacd66e606865f656312bf6d991f3a6e4507 Mon Sep 17 00:00:00 2001 From: Wesley Jenkins <103457588+wesjenkins@users.noreply.github.com> Date: Tue, 14 Feb 2023 11:35:11 -0500 Subject: [PATCH 125/775] Fix Base.libllvm_path and jl_get_libllvm don't support non-ASCII characters in path on Windows (#45126) (#45127) * Fix jl_get_libllvm_impl to support non-ASCII characters * Fix jl_get_libllvm_impl to support non-ASCII characters, fix whitespace * Fix jl_get_libllvm_impl to support non-ASCII characters, fix null and buffer --- src/codegen.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 889e84175d40c..36d0b6c85fb12 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8960,11 +8960,15 @@ extern "C" JL_DLLEXPORT jl_value_t *jl_get_libllvm_impl(void) JL_NOTSAFEPOINT HMODULE mod; if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCSTR)&llvm::DebugFlag, &mod)) return jl_nothing; - - char path[MAX_PATH]; - if (!GetModuleFileNameA(mod, path, sizeof(path))) + wchar_t path16[MAX_PATH]; + DWORD n16 = GetModuleFileNameW(mod, path16, MAX_PATH); + if (n16 <= 0) + return jl_nothing; + path16[n16++] = 0; + char path8[MAX_PATH * 3]; + if (!WideCharToMultiByte(CP_UTF8, 0, path16, n16, path8, MAX_PATH * 3, NULL, NULL)) return jl_nothing; - return (jl_value_t*) jl_symbol(path); + return (jl_value_t*) jl_symbol(path8); #else Dl_info dli; if (!dladdr((void*)LLVMContextCreate, &dli)) From 25e3f0eaccd313ccb6988ce40cacd74ac6fe2ed8 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 9 Feb 2023 14:44:34 -0500 Subject: [PATCH 126/775] define applicable for kwargs fix #36611 --- base/Base.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/base/Base.jl b/base/Base.jl index 3b02e674593c9..f48ac684cdb39 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -114,6 +114,14 @@ end # invoke does not have its own call cache, but kwcall for invoke does setfield!(typeof(invoke).name.mt, :max_args, 3, :monotonic) # invoke, f, T, args... +# define applicable(f, T, args...; kwargs...), without kwargs wrapping +# to forward to applicable +function Core.kwcall(kwargs, ::typeof(applicable), @nospecialize(args...)) + @inline + return applicable(Core.kwcall, kwargs, args...) +end + + # core operations & types include("promotion.jl") include("tuple.jl") From f9e1c6cb5963a26ccbd8fdf4603fdb817aebd263 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 10 Feb 2023 12:38:23 -0500 Subject: [PATCH 127/775] define tfuncs for applicable and hasmethod Since these tfuncs are pretty simple for inference, but hard for anyone else, we define these tfuncs here rather than letting package authors re-implement it poorly to create duplicates like static_hasmethod. Fix #39613 while we are here --- base/Base.jl | 6 +- base/boot.jl | 5 ++ base/compiler/abstractinterpretation.jl | 6 +- base/compiler/tfuncs.jl | 90 ++++++++++++++++++++++++- base/methodshow.jl | 5 +- base/reflection.jl | 28 +++++--- test/compiler/inference.jl | 57 ++++++++++++++++ 7 files changed, 183 insertions(+), 14 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index f48ac684cdb39..1409cf8068039 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -108,7 +108,7 @@ include("options.jl") function Core.kwcall(kwargs, ::typeof(invoke), f, T, args...) @inline # prepend kwargs and f to the invoked from the user - T = rewrap_unionall(Tuple{Any, Core.Typeof(f), (unwrap_unionall(T)::DataType).parameters...}, T) + T = rewrap_unionall(Tuple{Core.Typeof(kwargs), Core.Typeof(f), (unwrap_unionall(T)::DataType).parameters...}, T) return invoke(Core.kwcall, T, kwargs, f, args...) end # invoke does not have its own call cache, but kwcall for invoke does @@ -120,6 +120,10 @@ function Core.kwcall(kwargs, ::typeof(applicable), @nospecialize(args...)) @inline return applicable(Core.kwcall, kwargs, args...) end +function Core._hasmethod(@nospecialize(f), @nospecialize(t)) # this function has a special tfunc (TODO: make this a Builtin instead like applicable) + tt = rewrap_unionall(Tuple{Core.Typeof(f), (unwrap_unionall(t)::DataType).parameters...}, t) + return Core._hasmethod(tt) +end # core operations & types diff --git a/base/boot.jl b/base/boot.jl index 8e2ecd02bdf3c..8a5286951e281 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -850,4 +850,9 @@ struct Pair{A, B} end end +function _hasmethod(@nospecialize(tt)) # this function has a special tfunc + world = ccall(:jl_get_tls_world_age, UInt, ()) + return Intrinsics.not_int(ccall(:jl_gf_invoke_lookup, Any, (Any, Any, UInt), tt, nothing, world) === nothing) +end + ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Core, true) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 27c88c7a87562..094ce7e259859 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1889,7 +1889,7 @@ function abstract_invoke(interp::AbstractInterpreter, (; fargs, argtypes)::ArgIn (types, isexact, isconcrete, istype) = instanceof_tfunc(argtype_by_index(argtypes, 3)) isexact || return CallMeta(Any, Effects(), NoCallInfo()) unwrapped = unwrap_unionall(types) - if types === Bottom || types === Any || !(unwrapped isa DataType) + if types === Bottom || !(unwrapped isa DataType) || unwrapped.name !== Tuple.name return CallMeta(Bottom, EFFECTS_THROWS, NoCallInfo()) end argtype = argtypes_to_type(argtype_tail(argtypes, 4)) @@ -1971,6 +1971,8 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), return abstract_modifyfield!(interp, argtypes, si, sv) elseif f === Core.finalizer return abstract_finalizer(interp, argtypes, sv) + elseif f === applicable + return abstract_applicable(interp, argtypes, sv, max_methods) end rt = abstract_call_builtin(interp, f, arginfo, sv, max_methods) effects = builtin_effects(𝕃ᵢ, f, arginfo, rt) @@ -2051,6 +2053,8 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), val = _pure_eval_call(f, arginfo) return CallMeta(val === nothing ? Type : val, EFFECTS_TOTAL, MethodResultPure()) end + elseif f === Core._hasmethod + return _hasmethod_tfunc(interp, argtypes, sv) end atype = argtypes_to_type(argtypes) return abstract_call_gf_by_type(interp, f, arginfo, si, atype, sv, max_methods) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index a8f1726e9f0fc..29ab00b33d94d 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -582,7 +582,6 @@ end return true end add_tfunc(Core._typevar, 3, 3, typevar_tfunc, 100) -add_tfunc(applicable, 1, INT_INF, @nospecs((𝕃::AbstractLattice, f, args...)->Bool), 100) @nospecs function arraysize_tfunc(𝕃::AbstractLattice, ary, dim) hasintersect(widenconst(ary), Array) || return Bottom @@ -2107,7 +2106,7 @@ end end # known to be always effect-free (in particular nothrow) -const _PURE_BUILTINS = Any[tuple, svec, ===, typeof, nfields] +const _PURE_BUILTINS = Any[tuple, svec, ===, typeof, nfields, applicable] # known to be effect-free (but not necessarily nothrow) const _EFFECT_FREE_BUILTINS = [ @@ -2539,6 +2538,93 @@ function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, s return CallMeta(Type, EFFECTS_THROWS, NoCallInfo()) end +# a simplified model of abstract_call_gf_by_type for applicable +function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, + sv::InferenceState, max_methods::Int) + length(argtypes) < 2 && return CallMeta(Union{}, EFFECTS_UNKNOWN, NoCallInfo()) + isvarargtype(argtypes[2]) && return CallMeta(Bool, EFFECTS_UNKNOWN, NoCallInfo()) + argtypes = argtypes[2:end] + atype = argtypes_to_type(argtypes) + matches = find_matching_methods(argtypes, atype, method_table(interp), + InferenceParams(interp).max_union_splitting, max_methods) + if isa(matches, FailedMethodMatch) + rt = Bool # too many matches to analyze + else + (; valid_worlds, applicable) = matches + update_valid_age!(sv, valid_worlds) + + # also need an edge to the method table in case something gets + # added that did not intersect with any existing method + if isa(matches, MethodMatches) + matches.fullmatch || add_mt_backedge!(sv, matches.mt, atype) + else + for (thisfullmatch, mt) in zip(matches.fullmatches, matches.mts) + thisfullmatch || add_mt_backedge!(sv, mt, atype) + end + end + + napplicable = length(applicable) + if napplicable == 0 + rt = Const(false) # never any matches + else + rt = Const(true) # has applicable matches + for i in 1:napplicable + match = applicable[i]::MethodMatch + edge = specialize_method(match) + add_backedge!(sv, edge) + end + + if isa(matches, MethodMatches) ? (!matches.fullmatch || any_ambig(matches)) : + (!all(matches.fullmatches) || any_ambig(matches)) + # Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. + rt = Bool + end + end + end + return CallMeta(rt, EFFECTS_TOTAL, NoCallInfo()) +end +add_tfunc(applicable, 1, INT_INF, @nospecs((𝕃::AbstractLattice, f, args...)->Bool), 40) + +# a simplified model of abstract_invoke for Core._hasmethod +function _hasmethod_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::InferenceState) + if length(argtypes) == 3 && !isvarargtype(argtypes[3]) + ft′ = argtype_by_index(argtypes, 2) + ft = widenconst(ft′) + ft === Bottom && return CallMeta(Bool, EFFECTS_THROWS, NoCallInfo()) + typeidx = 3 + elseif length(argtypes) == 2 && !isvarargtype(argtypes[2]) + typeidx = 2 + else + return CallMeta(Any, Effects(), NoCallInfo()) + end + (types, isexact, isconcrete, istype) = instanceof_tfunc(argtype_by_index(argtypes, typeidx)) + isexact || return CallMeta(Bool, Effects(), NoCallInfo()) + unwrapped = unwrap_unionall(types) + if types === Bottom || !(unwrapped isa DataType) || unwrapped.name !== Tuple.name + return CallMeta(Bool, EFFECTS_THROWS, NoCallInfo()) + end + if typeidx == 3 + isdispatchelem(ft) || return CallMeta(Bool, Effects(), NoCallInfo()) # check that we might not have a subtype of `ft` at runtime, before doing supertype lookup below + types = rewrap_unionall(Tuple{ft, unwrapped.parameters...}, types)::Type + end + mt = ccall(:jl_method_table_for, Any, (Any,), types) + if !isa(mt, Core.MethodTable) + return CallMeta(Bool, EFFECTS_THROWS, NoCallInfo()) + end + match, valid_worlds, overlayed = findsup(types, method_table(interp)) + update_valid_age!(sv, valid_worlds) + if match === nothing + rt = Const(false) + add_mt_backedge!(sv, mt, types) # this should actually be an invoke-type backedge + else + rt = Const(true) + edge = specialize_method(match) + add_invoke_backedge!(sv, types, edge) + end + return CallMeta(rt, EFFECTS_TOTAL, NoCallInfo()) +end + + # N.B.: typename maps type equivalence classes to a single value function typename_static(@nospecialize(t)) t isa Const && return _typename(t.val) diff --git a/base/methodshow.jl b/base/methodshow.jl index 237ee006edce9..d3a40db665d1c 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -93,6 +93,7 @@ function kwarg_decl(m::Method, kwtype = nothing) push!(kws, kws[i]) deleteat!(kws, i) end + isempty(kws) && push!(kws, :var"...") return kws end end @@ -194,7 +195,9 @@ end function sym_to_string(sym) s = String(sym) - if endswith(s, "...") + if s === :var"..." + return "..." + elseif endswith(s, "...") return string(sprint(show_sym, Symbol(s[1:end-3])), "...") else return sprint(show_sym, sym) diff --git a/base/reflection.jl b/base/reflection.jl index 5326aea9ef87f..22c4698e5c31c 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1709,20 +1709,30 @@ julia> hasmethod(g, Tuple{}, (:a, :b, :c, :d)) # g accepts arbitrary kwargs true ``` """ -function hasmethod(@nospecialize(f), @nospecialize(t); world::UInt=get_world_counter()) - t = signature_type(f, t) - return ccall(:jl_gf_invoke_lookup, Any, (Any, Any, UInt), t, nothing, world) !== nothing +function hasmethod(@nospecialize(f), @nospecialize(t)) + return Core._hasmethod(f, t isa Type ? t : to_tuple_type(t)) end -function hasmethod(@nospecialize(f), @nospecialize(t), kwnames::Tuple{Vararg{Symbol}}; world::UInt=get_world_counter()) - # TODO: this appears to be doing the wrong queries - hasmethod(f, t, world=world) || return false - isempty(kwnames) && return true - m = which(f, t) - kws = kwarg_decl(m) +function Core.kwcall(kwargs, ::typeof(hasmethod), @nospecialize(f), @nospecialize(t)) + world = kwargs.world::UInt # make sure this is the only local, to avoid confusing kwarg_decl() + return ccall(:jl_gf_invoke_lookup, Any, (Any, Any, UInt), signature_type(f, t), nothing, world) !== nothing +end + +function hasmethod(f, t, kwnames::Tuple{Vararg{Symbol}}; world::UInt=get_world_counter()) + @nospecialize + isempty(kwnames) && return hasmethod(f, t; world) + t = to_tuple_type(t) + ft = Core.Typeof(f) + u = unwrap_unionall(t)::DataType + tt = rewrap_unionall(Tuple{typeof(Core.kwcall), typeof(pairs((;))), ft, u.parameters...}, t) + match = ccall(:jl_gf_invoke_lookup, Any, (Any, Any, UInt), tt, nothing, world) + match === nothing && return false + kws = ccall(:jl_uncompress_argnames, Array{Symbol,1}, (Any,), (match::Method).slot_syms) + isempty(kws) && return true # some kwfuncs simply forward everything directly for kw in kws endswith(String(kw), "...") && return true end + kwnames = Symbol[kwnames[i] for i in 1:length(kwnames)] return issubset(kwnames, kws) end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 841add157e5f9..d34dfb3999f82 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -4761,3 +4761,60 @@ unknown_sparam_nothrow2(x::Ref{Ref{T}}) where T = @isdefined(T) ? T::Type : noth @test only(Base.return_types(unknown_sparam_throw, (Any,))) === Union{Nothing,Type} @test only(Base.return_types(unknown_sparam_nothrow1, (Ref,))) === Type @test only(Base.return_types(unknown_sparam_nothrow2, (Ref{Ref{T}} where T,))) === Type + +function fapplicable end +gapplicable() = Val(applicable(fapplicable)) +gapplicable(x) = Val(applicable(fapplicable; x)) +@test only(Base.return_types(gapplicable, ())) === Val{false} +@test only(Base.return_types(gapplicable, (Int,))) === Val{false} +fapplicable() = 1 +@test only(Base.return_types(gapplicable, ())) === Val{true} +@test only(Base.return_types(gapplicable, (Int,))) === Val{false} +Base.delete_method(which(fapplicable, ())) +@test only(Base.return_types(gapplicable, ())) === Val{false} +@test only(Base.return_types(gapplicable, (Int,))) === Val{false} +fapplicable(; x) = x +@test only(Base.return_types(gapplicable, ())) === Val{true} +@test only(Base.return_types(gapplicable, (Int,))) === Val{true} +@test only(Base.return_types(()) do; applicable(); end) === Union{} +@test only(Base.return_types((Any,)) do x; Val(applicable(x...)); end) == Val +@test only(Base.return_types((Tuple{Vararg{Int}},)) do x; Val(applicable(+, 1, 2, x...)); end) == Val # could be improved to Val{true} +@test only(Base.return_types((Tuple{Vararg{Int}},)) do x; Val(applicable(+, 1, 2, 3, x...)); end) === Val{true} +@test only(Base.return_types((Int,)) do x; Val(applicable(+, 1, x)); end) === Val{true} +@test only(Base.return_types((Union{Int32,Int64},)) do x; Val(applicable(+, 1, x)); end) === Val{true} +@test only(Base.return_types((String,)) do x; Val(applicable(+, 1, x)); end) === Val{false} +fapplicable(::Int, ::Integer) = 2 +fapplicable(::Integer, ::Int32) = 3 +@test only(Base.return_types((Int32,)) do x; Val(applicable(fapplicable, 1, x)); end) === Val{false} +@test only(Base.return_types((Int64,)) do x; Val(applicable(fapplicable, 1, x)); end) === Val{true} +@test only(Base.return_types((Tuple{Vararg{Int}},)) do x; Val(applicable(tuple, x...)); end) === Val{true} +@test only(Base.return_types((Tuple{Vararg{Int}},)) do x; Val(applicable(sin, 1, x...)); end) == Val +@test only(Base.return_types((Tuple{Vararg{Int}},)) do x; Val(applicable(sin, 1, 2, x...)); end) === Val{false} + +function fhasmethod end +ghasmethod() = Val(hasmethod(fhasmethod, Tuple{})) +@test only(Base.return_types(ghasmethod, ())) === Val{false} +fhasmethod() = 1 +@test only(Base.return_types(ghasmethod, ())) === Val{true} +Base.delete_method(which(fhasmethod, ())) +@test only(Base.return_types(ghasmethod, ())) === Val{false} +@test only(Base.return_types(()) do; Core._hasmethod(); end) === Any +@test only(Base.return_types(()) do; Core._hasmethod(+, Tuple, 1); end) === Any +@test only(Base.return_types(()) do; Core._hasmethod(+, 1); end) === Bool +@test only(Base.return_types(()) do; Core._hasmethod(+, Tuple{1}); end) === Bool +@test only(Base.return_types((Any,)) do x; Val(hasmethod(x...)); end) == Val +@test only(Base.return_types(()) do; Val(hasmethod(+, Tuple{Int, Int})); end) === Val{true} +@test only(Base.return_types(()) do; Val(hasmethod(+, Tuple{Int, Int, Vararg{Int}})); end) === Val{false} +@test only(Base.return_types(()) do; Val(hasmethod(+, Tuple{Int, Int, Int, Vararg{Int}})); end) === Val{true} +@test only(Base.return_types(()) do; Val(hasmethod(+, Tuple{Int, Int})); end) === Val{true} +@test only(Base.return_types(()) do; Val(hasmethod(+, Tuple{Int, Union{Int32,Int64}})); end) === Val{true} +@test only(Base.return_types(()) do; Val(hasmethod(+, Tuple{Int, Union{Int,String}})); end) === Val{false} +@test only(Base.return_types(()) do; Val(hasmethod(+, Tuple{Int, Any})); end) === Val{false} +@test only(Base.return_types() do; Val(hasmethod(+, Tuple{Int, String})); end) === Val{false} +fhasmethod(::Int, ::Integer) = 2 +fhasmethod(::Integer, ::Int32) = 3 +@test only(Base.return_types(()) do; Val(hasmethod(fhasmethod, Tuple{Int, Int32})); end) === Val{false} +@test only(Base.return_types(()) do; Val(hasmethod(fhasmethod, Tuple{Int, Int64})); end) === Val{true} +@test only(Base.return_types(()) do; Val(hasmethod(tuple, Tuple{Vararg{Int}})); end) === Val{true} +@test only(Base.return_types(()) do; Val(hasmethod(sin, Tuple{Int, Vararg{Int}})); end) == Val{false} +@test only(Base.return_types(()) do; Val(hasmethod(sin, Tuple{Int, Int, Vararg{Int}})); end) === Val{false} From afeda9f8cf0776461eaf565333e2159122d70bc4 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 14 Feb 2023 17:13:39 -0500 Subject: [PATCH 128/775] restore kwcall_mt optimizations (#48672) Deleted in cbfdb3facd0f2ece4088f43ef97533e9e0921081, probably by accident. Refs #48670. --- src/staticdata.c | 3 ++- test/keywordargs.jl | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/staticdata.c b/src/staticdata.c index 8f11d90b155f3..51ef3d50f767f 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -98,7 +98,7 @@ extern "C" { // TODO: put WeakRefs on the weak_refs list during deserialization // TODO: handle finalizers -#define NUM_TAGS 158 +#define NUM_TAGS 159 // An array of references that need to be restored from the sysimg // This is a manually constructed dual of the gvars array, which would be produced by codegen for Julia code, for C. @@ -241,6 +241,7 @@ jl_value_t **const*const get_tags(void) { INSERT_TAG(jl_typeinf_func); INSERT_TAG(jl_type_type_mt); INSERT_TAG(jl_nonfunction_mt); + INSERT_TAG(jl_kwcall_mt); INSERT_TAG(jl_kwcall_func); // some Core.Builtin Functions that we want to be able to reference: diff --git a/test/keywordargs.jl b/test/keywordargs.jl index a5116afa3c31d..7211cfa85701d 100644 --- a/test/keywordargs.jl +++ b/test/keywordargs.jl @@ -387,3 +387,10 @@ f41416(a...="a"; b=true) = (b, a) @test f41416(;b=false) === (false, ("a",)) @test f41416(33) === (true, (33,)) @test f41416(3; b=false) === (false, (3,)) + +Core.kwcall(i::Int) = "hi $i" +let m = first(methods(Core.kwcall, (Any,typeof(kwf1),Vararg))) + @test m.name === :kwf1 + @test Core.kwcall(1) == "hi 1" + @test which(Core.kwcall, (Int,)).name === :kwcall +end From 113c2f3c95bf2ae67ac1e8214a17b7d9404dbbfa Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 14 Feb 2023 19:51:12 -0500 Subject: [PATCH 129/775] Put back getfield :boundscheck hack (#48677) We used to have this hack before #48246, but I removed it because I had hoped we don't need. Unfortunately, we weren't able to infer consistency of ``` @inbounds (1,2)[1] ``` With this hack, constprop is able to independently prove inbounded-ness, overriding the usual consistency taintaing that happens for inbounds. --- base/compiler/abstractinterpretation.jl | 18 ++++++++++++++++++ test/compiler/effects.jl | 8 ++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 27c88c7a87562..2fabbc18a83f9 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1974,6 +1974,14 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), end rt = abstract_call_builtin(interp, f, arginfo, sv, max_methods) effects = builtin_effects(𝕃ᵢ, f, arginfo, rt) + if f === getfield && (fargs !== nothing && isexpr(fargs[end], :boundscheck)) && !is_nothrow(effects) && isa(sv, InferenceState) + # As a special case, we delayed tainting `noinbounds` for getfield calls in case we can prove + # in-boundedness indepedently. Here we need to put that back in other cases. + # N.B.: This isn't about the effects of the call itself, but a delayed contribution of the :boundscheck + # statement, so we need to merge this directly into sv, rather than modifying thte effects. + merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; noinbounds=false, + consistent = (get_curr_ssaflag(sv) & IR_FLAG_INBOUNDS) != 0 ? ALWAYS_FALSE : ALWAYS_TRUE)) + end return CallMeta(rt, effects, NoCallInfo()) elseif isa(f, Core.OpaqueClosure) # calling an OpaqueClosure about which we have no information returns no information @@ -2195,6 +2203,15 @@ function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, vtypes:: return rt elseif head === :boundscheck if isa(sv, InferenceState) + stmt = sv.src.code[sv.currpc] + if isexpr(stmt, :call) + f = abstract_eval_value(interp, stmt.args[1], vtypes, sv) + if f isa Const && f.val === getfield + # boundscheck of `getfield` call is analyzed by tfunc potentially without + # tainting :inbounds or :consistent when it's known to be nothrow + @goto delay_effects_analysis + end + end # If there is no particular `@inbounds` for this function, then we only taint `:noinbounds`, # which will subsequently taint `:consistent`-cy if this function is called from another # function that uses `@inbounds`. However, if this `:boundscheck` is itself within an @@ -2203,6 +2220,7 @@ function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, vtypes:: merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; noinbounds=false, consistent = (get_curr_ssaflag(sv) & IR_FLAG_INBOUNDS) != 0 ? ALWAYS_FALSE : ALWAYS_TRUE)) end + @label delay_effects_analysis rt = Bool elseif head === :inbounds @assert false && "Expected this to have been moved into flags" diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index 599da1225cf52..dceb737ed6ae7 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -739,8 +739,8 @@ end |> Core.Compiler.is_foldable_nothrow # Test that dead `@inbounds` does not taint consistency # https://github.com/JuliaLang/julia/issues/48243 -@test Base.infer_effects() do - false && @inbounds (1,2,3)[1] +@test Base.infer_effects(Tuple{Int64}) do i + false && @inbounds (1,2,3)[i] return 1 end |> Core.Compiler.is_foldable_nothrow @@ -748,6 +748,10 @@ end |> Core.Compiler.is_foldable_nothrow @inbounds (1,2,3)[i] end |> !Core.Compiler.is_consistent +@test Base.infer_effects(Tuple{Tuple{Int64}}) do x + @inbounds x[1] +end |> Core.Compiler.is_foldable_nothrow + # Test that :new of non-concrete, but otherwise known type # does not taint consistency. @eval struct ImmutRef{T} From faf795435bf7ac6a17f91748239b2ac2a702abbd Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Wed, 15 Feb 2023 05:04:04 -0800 Subject: [PATCH 130/775] Add matrix symmetrizing functions (#31836) Co-authored-by: Steven G. Johnson Co-authored-by: Alex Arslan Co-authored-by: Daniel Karrasch --- NEWS.md | 6 +- stdlib/LinearAlgebra/docs/src/index.md | 2 + stdlib/LinearAlgebra/src/LinearAlgebra.jl | 2 + stdlib/LinearAlgebra/src/symmetric.jl | 131 ++++++++++++++++------ stdlib/LinearAlgebra/test/symmetric.jl | 34 ++++++ 5 files changed, 140 insertions(+), 35 deletions(-) diff --git a/NEWS.md b/NEWS.md index 630793e914a11..c9387613d6203 100644 --- a/NEWS.md +++ b/NEWS.md @@ -43,14 +43,16 @@ Standard library changes #### Package Manager -- "Package Extensions": support for loading a piece of code based on other +* "Package Extensions": support for loading a piece of code based on other packages being loaded in the Julia session. This has similar applications as the Requires.jl package but also supports precompilation and setting compatibility. -- `Pkg.precompile` now accepts `timing` as a keyword argument which displays per package timing information for precompilation (e.g. `Pkg.precompile(timing=true)`) +* `Pkg.precompile` now accepts `timing` as a keyword argument which displays per package timing information for precompilation (e.g. `Pkg.precompile(timing=true)`) #### LinearAlgebra +* New functions `hermitianpart` and `hermitianpart!` for extracting the Hermitian + (real symmetric) part of a matrix ([#31836]). #### Printf * Format specifiers now support dynamic width and precision, e.g. `%*s` and `%*.*g` ([#40105]). diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index 9f12af174a4ff..8eb5af5605b91 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -467,6 +467,8 @@ Base.copy(::Union{Transpose,Adjoint}) LinearAlgebra.stride1 LinearAlgebra.checksquare LinearAlgebra.peakflops +LinearAlgebra.hermitianpart +LinearAlgebra.hermitianpart! ``` ## Low-level matrix operations diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 624cca69b84d9..96c2e38d187e8 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -94,6 +94,8 @@ export eigvecs, factorize, givens, + hermitianpart, + hermitianpart!, hessenberg, hessenberg!, isdiag, diff --git a/stdlib/LinearAlgebra/src/symmetric.jl b/stdlib/LinearAlgebra/src/symmetric.jl index 376c1f7820b6f..f96ca812ea0ec 100644 --- a/stdlib/LinearAlgebra/src/symmetric.jl +++ b/stdlib/LinearAlgebra/src/symmetric.jl @@ -17,34 +17,45 @@ end Construct a `Symmetric` view of the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of the matrix `A`. +`Symmetric` views are mainly useful for real-symmetric matrices, for which +specialized algorithms (e.g. for eigenproblems) are enabled for `Symmetric` types. +More generally, see also [`Hermitian(A)`](@ref) for Hermitian matrices `A == A'`, which +is effectively equivalent to `Symmetric` for real matrices but is also useful for +complex matrices. (Whereas complex `Symmetric` matrices are supported but have few +if any specialized algorithms.) + +To compute the symmetric part of a real matrix, or more generally the Hermitian part `(A + A') / 2` of +a real or complex matrix `A`, use [`hermitianpart`](@ref). + # Examples ```jldoctest -julia> A = [1 0 2 0 3; 0 4 0 5 0; 6 0 7 0 8; 0 9 0 1 0; 2 0 3 0 4] -5×5 Matrix{Int64}: - 1 0 2 0 3 - 0 4 0 5 0 - 6 0 7 0 8 - 0 9 0 1 0 - 2 0 3 0 4 +julia> A = [1 2 3; 4 5 6; 7 8 9] +3×3 Matrix{Int64}: + 1 2 3 + 4 5 6 + 7 8 9 julia> Supper = Symmetric(A) -5×5 Symmetric{Int64, Matrix{Int64}}: - 1 0 2 0 3 - 0 4 0 5 0 - 2 0 7 0 8 - 0 5 0 1 0 - 3 0 8 0 4 +3×3 Symmetric{Int64, Matrix{Int64}}: + 1 2 3 + 2 5 6 + 3 6 9 julia> Slower = Symmetric(A, :L) -5×5 Symmetric{Int64, Matrix{Int64}}: - 1 0 6 0 2 - 0 4 0 9 0 - 6 0 7 0 3 - 0 9 0 1 0 - 2 0 3 0 4 +3×3 Symmetric{Int64, Matrix{Int64}}: + 1 4 7 + 4 5 8 + 7 8 9 + +julia> hermitianpart(A) +3×3 Hermitian{Float64, Matrix{Float64}}: + 1.0 3.0 5.0 + 3.0 5.0 7.0 + 5.0 7.0 9.0 ``` -Note that `Supper` will not be equal to `Slower` unless `A` is itself symmetric (e.g. if `A == transpose(A)`). +Note that `Supper` will not be equal to `Slower` unless `A` is itself symmetric (e.g. if +`A == transpose(A)`). """ function Symmetric(A::AbstractMatrix, uplo::Symbol=:U) checksquare(A) @@ -99,25 +110,33 @@ end Construct a `Hermitian` view of the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of the matrix `A`. +To compute the Hermitian part of `A`, use [`hermitianpart`](@ref). + # Examples ```jldoctest -julia> A = [1 0 2+2im 0 3-3im; 0 4 0 5 0; 6-6im 0 7 0 8+8im; 0 9 0 1 0; 2+2im 0 3-3im 0 4]; +julia> A = [1 2+2im 3-3im; 4 5 6-6im; 7 8+8im 9] +3×3 Matrix{Complex{Int64}}: + 1+0im 2+2im 3-3im + 4+0im 5+0im 6-6im + 7+0im 8+8im 9+0im julia> Hupper = Hermitian(A) -5×5 Hermitian{Complex{Int64}, Matrix{Complex{Int64}}}: - 1+0im 0+0im 2+2im 0+0im 3-3im - 0+0im 4+0im 0+0im 5+0im 0+0im - 2-2im 0+0im 7+0im 0+0im 8+8im - 0+0im 5+0im 0+0im 1+0im 0+0im - 3+3im 0+0im 8-8im 0+0im 4+0im +3×3 Hermitian{Complex{Int64}, Matrix{Complex{Int64}}}: + 1+0im 2+2im 3-3im + 2-2im 5+0im 6-6im + 3+3im 6+6im 9+0im julia> Hlower = Hermitian(A, :L) -5×5 Hermitian{Complex{Int64}, Matrix{Complex{Int64}}}: - 1+0im 0+0im 6+6im 0+0im 2-2im - 0+0im 4+0im 0+0im 9+0im 0+0im - 6-6im 0+0im 7+0im 0+0im 3+3im - 0+0im 9+0im 0+0im 1+0im 0+0im - 2+2im 0+0im 3-3im 0+0im 4+0im +3×3 Hermitian{Complex{Int64}, Matrix{Complex{Int64}}}: + 1+0im 4+0im 7+0im + 4+0im 5+0im 8-8im + 7+0im 8+8im 9+0im + +julia> hermitianpart(A) +3×3 Hermitian{ComplexF64, Matrix{ComplexF64}}: + 1.0+0.0im 3.0+1.0im 5.0-1.5im + 3.0-1.0im 5.0+0.0im 7.0-7.0im + 5.0+1.5im 7.0+7.0im 9.0+0.0im ``` Note that `Hupper` will not be equal to `Hlower` unless `A` is itself Hermitian (e.g. if `A == adjoint(A)`). @@ -863,3 +882,49 @@ for func in (:log, :sqrt) end end end + +""" + hermitianpart(A, uplo=:U) -> Hermitian + +Return the Hermitian part of the square matrix `A`, defined as `(A + A') / 2`, as a +[`Hermitian`](@ref) matrix. For real matrices `A`, this is also known as the symmetric part +of `A`; it is also sometimes called the "operator real part". The optional argument `uplo` controls the corresponding argument of the +[`Hermitian`](@ref) view. For real matrices, the latter is equivalent to a +[`Symmetric`](@ref) view. + +See also [`hermitianpart!`](@ref) for the corresponding in-place operation. + +!!! compat "Julia 1.10" + This function requires Julia 1.10 or later. +""" +hermitianpart(A::AbstractMatrix, uplo::Symbol=:U) = Hermitian(_hermitianpart(A), uplo) + +""" + hermitianpart!(A, uplo=:U) -> Hermitian + +Overwrite the square matrix `A` in-place with its Hermitian part `(A + A') / 2`, and return +[`Hermitian(A, uplo)`](@ref). For real matrices `A`, this is also known as the symmetric +part of `A`. + +See also [`hermitianpart`](@ref) for the corresponding out-of-place operation. + +!!! compat "Julia 1.10" + This function requires Julia 1.10 or later. +""" +hermitianpart!(A::AbstractMatrix, uplo::Symbol=:U) = Hermitian(_hermitianpart!(A), uplo) + +_hermitianpart(A::AbstractMatrix) = _hermitianpart!(copy_similar(A, Base.promote_op(/, eltype(A), Int))) +_hermitianpart(a::Number) = real(a) + +function _hermitianpart!(A::AbstractMatrix) + require_one_based_indexing(A) + n = checksquare(A) + @inbounds for j in 1:n + A[j, j] = _hermitianpart(A[j, j]) + for i in 1:j-1 + A[i, j] = val = (A[i, j] + adjoint(A[j, i])) / 2 + A[j, i] = adjoint(val) + end + end + return A +end diff --git a/stdlib/LinearAlgebra/test/symmetric.jl b/stdlib/LinearAlgebra/test/symmetric.jl index 880c9d7c0d747..04621c4b49e86 100644 --- a/stdlib/LinearAlgebra/test/symmetric.jl +++ b/stdlib/LinearAlgebra/test/symmetric.jl @@ -790,4 +790,38 @@ end end end +@testset "hermitian part" begin + for T in [Float32, Complex{Float32}, Int32, Rational{Int32}, + Complex{Int32}, Complex{Rational{Int32}}] + f, f!, t = hermitianpart, hermitianpart!, T <: Real ? transpose : adjoint + X = T[1 2 3; 4 5 6; 7 8 9] + T <: Complex && (X .+= im .* X) + Xc = copy(X) + Y = (X + t(X)) / 2 + U = f(X) + L = f(X, :L) + @test U isa Hermitian + @test L isa Hermitian + @test U.uplo == 'U' + @test L.uplo == 'L' + @test U == L == Y + if T <: AbstractFloat || real(T) <: AbstractFloat + HU = f!(X) + @test HU == Y + @test triu(X) == triu(Y) + HL = f!(Xc, :L) + @test HL == Y + @test tril(Xc) == tril(Y) + end + end + @test_throws DimensionMismatch hermitianpart(ones(1,2)) + for T in (Float64, ComplexF64), uplo in (:U, :L) + A = [randn(T, 2, 2) for _ in 1:2, _ in 1:2] + Aherm = hermitianpart(A, uplo) + @test Aherm == Aherm.data == (A + A')/2 + @test Aherm isa Hermitian + @test Aherm.uplo == LinearAlgebra.char_uplo(uplo) + end +end + end # module TestSymmetric From 1270b34b64f367a6c5da5576b85c13cc0c26ffea Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 15 Feb 2023 18:07:22 +0100 Subject: [PATCH 131/775] Introduce `AdjointFactorization` not subtyping `AbstractMatrix` (#46874) --- NEWS.md | 4 + stdlib/LinearAlgebra/docs/src/index.md | 9 +- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 15 ++- stdlib/LinearAlgebra/src/adjtrans.jl | 3 + stdlib/LinearAlgebra/src/factorization.jl | 117 +++++++++++++++------ stdlib/LinearAlgebra/src/hessenberg.jl | 10 +- stdlib/LinearAlgebra/src/lq.jl | 9 +- stdlib/LinearAlgebra/src/lu.jl | 47 +++------ stdlib/LinearAlgebra/src/qr.jl | 6 +- stdlib/LinearAlgebra/test/adjtrans.jl | 8 +- stdlib/LinearAlgebra/test/factorization.jl | 21 +++- stdlib/LinearAlgebra/test/qr.jl | 6 +- 12 files changed, 164 insertions(+), 91 deletions(-) diff --git a/NEWS.md b/NEWS.md index c9387613d6203..ff1501104e6be 100644 --- a/NEWS.md +++ b/NEWS.md @@ -51,6 +51,10 @@ Standard library changes #### LinearAlgebra +* Adjoints and transposes of `Factorization` objects are no longer wrapped in `Adjoint` + and `Transpose` wrappers, respectively. Instead, they are wrapped in + `AdjointFactorization` and `TranposeFactorization` types, which themselves subtype + `Factorization` ([#46874]). * New functions `hermitianpart` and `hermitianpart!` for extracting the Hermitian (real symmetric) part of a matrix ([#31836]). diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index 8eb5af5605b91..5b5c43da9875d 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -276,12 +276,11 @@ to first compute the Hessenberg factorization `F` of `A` via the [`hessenberg`]( Given `F`, Julia employs an efficient algorithm for `(F+μ*I) \ b` (equivalent to `(A+μ*I)x \ b`) and related operations like determinants. - ## [Matrix factorizations](@id man-linalg-factorizations) [Matrix factorizations (a.k.a. matrix decompositions)](https://en.wikipedia.org/wiki/Matrix_decomposition) compute the factorization of a matrix into a product of matrices, and are one of the central concepts -in linear algebra. +in (numerical) linear algebra. The following table summarizes the types of matrix factorizations that have been implemented in Julia. Details of their associated methods can be found in the [Standard functions](@ref) section @@ -306,6 +305,10 @@ of the Linear Algebra documentation. | `Schur` | [Schur decomposition](https://en.wikipedia.org/wiki/Schur_decomposition) | | `GeneralizedSchur` | [Generalized Schur decomposition](https://en.wikipedia.org/wiki/Schur_decomposition#Generalized_Schur_decomposition) | +Adjoints and transposes of [`Factorization`](@ref) objects are lazily wrapped in +`AdjointFactorization` and `TransposeFactorization` objects, respectively. Generically, +transpose of real `Factorization`s are wrapped as `AdjointFactorization`. + ## Standard functions Linear algebra functions in Julia are largely implemented by calling functions from [LAPACK](http://www.netlib.org/lapack/). @@ -460,9 +463,11 @@ LinearAlgebra.ishermitian Base.transpose LinearAlgebra.transpose! LinearAlgebra.Transpose +LinearAlgebra.TransposeFactorization Base.adjoint LinearAlgebra.adjoint! LinearAlgebra.Adjoint +LinearAlgebra.AdjointFactorization Base.copy(::Union{Transpose,Adjoint}) LinearAlgebra.stride1 LinearAlgebra.checksquare diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 96c2e38d187e8..ed5e8d24ba80f 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -503,7 +503,7 @@ _initarray(op, ::Type{TA}, ::Type{TB}, C) where {TA,TB} = # While this definition is pretty general, it does e.g. promote to common element type of lhs and rhs # which is required by LAPACK but not SuiteSparse which allows real-complex solves in some cases. Hence, # we restrict this method to only the LAPACK factorizations in LinearAlgebra. -# The definition is put here since it explicitly references all the Factorizion structs so it has +# The definition is put here since it explicitly references all the Factorization structs so it has # to be located after all the files that define the structs. const LAPACKFactorizations{T,S} = Union{ BunchKaufman{T,S}, @@ -514,7 +514,12 @@ const LAPACKFactorizations{T,S} = Union{ QRCompactWY{T,S}, QRPivoted{T,S}, SVD{T,<:Real,S}} -function (\)(F::Union{<:LAPACKFactorizations,Adjoint{<:Any,<:LAPACKFactorizations}}, B::AbstractVecOrMat) + +(\)(F::LAPACKFactorizations, B::AbstractVecOrMat) = ldiv(F, B) +(\)(F::AdjointFactorization{<:Any,<:LAPACKFactorizations}, B::AbstractVecOrMat) = ldiv(F, B) +(\)(F::TransposeFactorization{<:Any,<:LU}, B::AbstractVecOrMat) = ldiv(F, B) + +function ldiv(F::Factorization, B::AbstractVecOrMat) require_one_based_indexing(B) m, n = size(F) if m != size(B, 1) @@ -544,7 +549,11 @@ function (\)(F::Union{<:LAPACKFactorizations,Adjoint{<:Any,<:LAPACKFactorization end # disambiguate (\)(F::LAPACKFactorizations{T}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} = - invoke(\, Tuple{Factorization{T}, VecOrMat{Complex{T}}}, F, B) + @invoke \(F::Factorization{T}, B::VecOrMat{Complex{T}}) +(\)(F::AdjointFactorization{T,<:LAPACKFactorizations}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} = + ldiv(F, B) +(\)(F::TransposeFactorization{T,<:LU}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} = + ldiv(F, B) """ LinearAlgebra.peakflops(n::Integer=2000; parallel::Bool=false) diff --git a/stdlib/LinearAlgebra/src/adjtrans.jl b/stdlib/LinearAlgebra/src/adjtrans.jl index 1e9687ef0f31a..9a872497fdbae 100644 --- a/stdlib/LinearAlgebra/src/adjtrans.jl +++ b/stdlib/LinearAlgebra/src/adjtrans.jl @@ -291,6 +291,9 @@ wrapperop(_) = identity wrapperop(::Adjoint) = adjoint wrapperop(::Transpose) = transpose +# the following fallbacks can be removed if Adjoint/Transpose are restricted to AbstractVecOrMat +size(A::AdjOrTrans) = reverse(size(A.parent)) +axes(A::AdjOrTrans) = reverse(axes(A.parent)) # AbstractArray interface, basic definitions length(A::AdjOrTrans) = length(A.parent) size(v::AdjOrTransAbsVec) = (1, length(v.parent)) diff --git a/stdlib/LinearAlgebra/src/factorization.jl b/stdlib/LinearAlgebra/src/factorization.jl index 44668bfe9c212..8c35a23e6b6d5 100644 --- a/stdlib/LinearAlgebra/src/factorization.jl +++ b/stdlib/LinearAlgebra/src/factorization.jl @@ -11,9 +11,58 @@ matrix factorizations. """ abstract type Factorization{T} end +""" + AdjointFactorization + +Lazy wrapper type for the adjoint of the underlying `Factorization` object. Usually, the +`AdjointFactorization` constructor should not be called directly, use +[`adjoint(:: Factorization)`](@ref) instead. +""" +struct AdjointFactorization{T,S<:Factorization} <: Factorization{T} + parent::S +end +AdjointFactorization(F::Factorization) = + AdjointFactorization{Base.promote_op(adjoint,eltype(F)),typeof(F)}(F) + +""" + TransposeFactorization + +Lazy wrapper type for the transpose of the underlying `Factorization` object. Usually, the +`TransposeFactorization` constructor should not be called directly, use +[`transpose(:: Factorization)`](@ref) instead. +""" +struct TransposeFactorization{T,S<:Factorization} <: Factorization{T} + parent::S +end +TransposeFactorization(F::Factorization) = + TransposeFactorization{Base.promote_op(adjoint,eltype(F)),typeof(F)}(F) + eltype(::Type{<:Factorization{T}}) where {T} = T -size(F::Adjoint{<:Any,<:Factorization}) = reverse(size(parent(F))) -size(F::Transpose{<:Any,<:Factorization}) = reverse(size(parent(F))) +size(F::AdjointFactorization) = reverse(size(parent(F))) +size(F::TransposeFactorization) = reverse(size(parent(F))) +size(F::Union{AdjointFactorization,TransposeFactorization}, d::Integer) = d in (1, 2) ? size(F)[d] : 1 +parent(F::Union{AdjointFactorization,TransposeFactorization}) = F.parent + +""" + adjoint(F::Factorization) + +Lazy adjoint of the factorization `F`. By default, returns an +[`AdjointFactorization`](@ref) wrapper. +""" +adjoint(F::Factorization) = AdjointFactorization(F) +""" + transpose(F::Factorization) + +Lazy transpose of the factorization `F`. By default, returns a [`TransposeFactorization`](@ref), +except for `Factorization`s with real `eltype`, in which case returns an [`AdjointFactorization`](@ref). +""" +transpose(F::Factorization) = TransposeFactorization(F) +transpose(F::Factorization{<:Real}) = AdjointFactorization(F) +adjoint(F::AdjointFactorization) = F.parent +transpose(F::TransposeFactorization) = F.parent +transpose(F::AdjointFactorization{<:Real}) = F.parent +conj(A::TransposeFactorization) = adjoint(A.parent) +conj(A::AdjointFactorization) = transpose(A.parent) checkpositivedefinite(info) = info == 0 || throw(PosDefException(info)) checknonsingular(info, ::RowMaximum) = info == 0 || throw(SingularException(info)) @@ -60,64 +109,77 @@ convert(::Type{T}, f::Factorization) where {T<:AbstractArray} = T(f)::T ### General promotion rules Factorization{T}(F::Factorization{T}) where {T} = F -# This is a bit odd since the return is not a Factorization but it works well in generic code -Factorization{T}(A::Adjoint{<:Any,<:Factorization}) where {T} = +# This no longer looks odd since the return _is_ a Factorization! +Factorization{T}(A::AdjointFactorization) where {T} = adjoint(Factorization{T}(parent(A))) +Factorization{T}(A::TransposeFactorization) where {T} = + transpose(Factorization{T}(parent(A))) inv(F::Factorization{T}) where {T} = (n = size(F, 1); ldiv!(F, Matrix{T}(I, n, n))) Base.hash(F::Factorization, h::UInt) = mapreduce(f -> hash(getfield(F, f)), hash, 1:nfields(F); init=h) Base.:(==)( F::T, G::T) where {T<:Factorization} = all(f -> getfield(F, f) == getfield(G, f), 1:nfields(F)) Base.isequal(F::T, G::T) where {T<:Factorization} = all(f -> isequal(getfield(F, f), getfield(G, f)), 1:nfields(F))::Bool -function Base.show(io::IO, x::Adjoint{<:Any,<:Factorization}) - print(io, "Adjoint of ") +function Base.show(io::IO, x::AdjointFactorization) + print(io, "adjoint of ") show(io, parent(x)) end -function Base.show(io::IO, x::Transpose{<:Any,<:Factorization}) - print(io, "Transpose of ") +function Base.show(io::IO, x::TransposeFactorization) + print(io, "transpose of ") show(io, parent(x)) end -function Base.show(io::IO, ::MIME"text/plain", x::Adjoint{<:Any,<:Factorization}) - print(io, "Adjoint of ") +function Base.show(io::IO, ::MIME"text/plain", x::AdjointFactorization) + print(io, "adjoint of ") show(io, MIME"text/plain"(), parent(x)) end -function Base.show(io::IO, ::MIME"text/plain", x::Transpose{<:Any,<:Factorization}) - print(io, "Transpose of ") +function Base.show(io::IO, ::MIME"text/plain", x::TransposeFactorization) + print(io, "transpose of ") show(io, MIME"text/plain"(), parent(x)) end # With a real lhs and complex rhs with the same precision, we can reinterpret # the complex rhs as a real rhs with twice the number of columns or rows -function (\)(F::Factorization{T}, B::VecOrMat{Complex{T}}) where T<:BlasReal +function (\)(F::Factorization{T}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} require_one_based_indexing(B) c2r = reshape(copy(transpose(reinterpret(T, reshape(B, (1, length(B)))))), size(B, 1), 2*size(B, 2)) x = ldiv!(F, c2r) return reshape(copy(reinterpret(Complex{T}, copy(transpose(reshape(x, div(length(x), 2), 2))))), _ret_size(F, B)) end -function (/)(B::VecOrMat{Complex{T}}, F::Factorization{T}) where T<:BlasReal +# don't do the reinterpretation for [Adjoint/Transpose]Factorization +(\)(F::TransposeFactorization{T}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} = + conj!(adjoint(parent(F)) \ conj.(B)) +(\)(F::AdjointFactorization{T}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} = + @invoke \(F::typeof(F), B::VecOrMat) + +function (/)(B::VecOrMat{Complex{T}}, F::Factorization{T}) where {T<:BlasReal} require_one_based_indexing(B) x = rdiv!(copy(reinterpret(T, B)), F) return copy(reinterpret(Complex{T}, x)) end +# don't do the reinterpretation for [Adjoint/Transpose]Factorization +(/)(B::VecOrMat{Complex{T}}, F::TransposeFactorization{T}) where {T<:BlasReal} = + conj!(adjoint(parent(F)) \ conj.(B)) +(/)(B::VecOrMat{Complex{T}}, F::AdjointFactorization{T}) where {T<:BlasReal} = + @invoke /(B::VecOrMat{Complex{T}}, F::Factorization{T}) -function \(F::Union{Factorization, Adjoint{<:Any,<:Factorization}}, B::AbstractVecOrMat) +function (\)(F::Factorization, B::AbstractVecOrMat) require_one_based_indexing(B) - TFB = typeof(oneunit(eltype(B)) / oneunit(eltype(F))) + TFB = typeof(oneunit(eltype(F)) \ oneunit(eltype(B))) ldiv!(F, copy_similar(B, TFB)) end +(\)(F::TransposeFactorization, B::AbstractVecOrMat) = conj!(adjoint(F.parent) \ conj.(B)) -function /(B::AbstractMatrix, F::Union{Factorization, Adjoint{<:Any,<:Factorization}}) +function (/)(B::AbstractMatrix, F::Factorization) require_one_based_indexing(B) TFB = typeof(oneunit(eltype(B)) / oneunit(eltype(F))) rdiv!(copy_similar(B, TFB), F) end -/(adjB::AdjointAbsVec, adjF::Adjoint{<:Any,<:Factorization}) = adjoint(adjF.parent \ adjB.parent) -/(B::TransposeAbsVec, adjF::Adjoint{<:Any,<:Factorization}) = adjoint(adjF.parent \ adjoint(B)) - +(/)(A::AbstractMatrix, F::AdjointFactorization) = adjoint(adjoint(F) \ adjoint(A)) +(/)(A::AbstractMatrix, F::TransposeFactorization) = transpose(transpose(F) \ transpose(A)) function ldiv!(Y::AbstractVector, A::Factorization, B::AbstractVector) require_one_based_indexing(Y, B) - m, n = size(A, 1), size(A, 2) + m, n = size(A) if m > n Bc = copy(B) ldiv!(A, Bc) @@ -128,7 +190,7 @@ function ldiv!(Y::AbstractVector, A::Factorization, B::AbstractVector) end function ldiv!(Y::AbstractMatrix, A::Factorization, B::AbstractMatrix) require_one_based_indexing(Y, B) - m, n = size(A, 1), size(A, 2) + m, n = size(A) if m > n Bc = copy(B) ldiv!(A, Bc) @@ -138,14 +200,3 @@ function ldiv!(Y::AbstractMatrix, A::Factorization, B::AbstractMatrix) return ldiv!(A, Y) end end - -# fallback methods for transposed solves -\(F::Transpose{<:Any,<:Factorization{<:Real}}, B::AbstractVecOrMat) = adjoint(F.parent) \ B -\(F::Transpose{<:Any,<:Factorization}, B::AbstractVecOrMat) = conj.(adjoint(F.parent) \ conj.(B)) - -/(B::AbstractMatrix, F::Transpose{<:Any,<:Factorization{<:Real}}) = B / adjoint(F.parent) -/(B::AbstractMatrix, F::Transpose{<:Any,<:Factorization}) = conj.(conj.(B) / adjoint(F.parent)) -/(B::AdjointAbsVec, F::Transpose{<:Any,<:Factorization{<:Real}}) = B / adjoint(F.parent) -/(B::TransposeAbsVec, F::Transpose{<:Any,<:Factorization{<:Real}}) = transpose(transpose(F) \ transpose(B)) -/(B::AdjointAbsVec, F::Transpose{<:Any,<:Factorization}) = conj.(conj.(B) / adjoint(F.parent)) -/(B::TransposeAbsVec, F::Transpose{<:Any,<:Factorization}) = transpose(transpose(F) \ transpose(B)) diff --git a/stdlib/LinearAlgebra/src/hessenberg.jl b/stdlib/LinearAlgebra/src/hessenberg.jl index 17d630765e424..44cd0873c1ee5 100644 --- a/stdlib/LinearAlgebra/src/hessenberg.jl +++ b/stdlib/LinearAlgebra/src/hessenberg.jl @@ -422,10 +422,12 @@ Hessenberg(F::Hessenberg, μ::Number) = Hessenberg(F.factors, F.τ, F.H, F.uplo; copy(F::Hessenberg{<:Any,<:UpperHessenberg}) = Hessenberg(copy(F.factors), copy(F.τ); μ=F.μ) copy(F::Hessenberg{<:Any,<:SymTridiagonal}) = Hessenberg(copy(F.factors), copy(F.τ), copy(F.H), F.uplo; μ=F.μ) -size(F::Hessenberg, d) = size(F.H, d) +size(F::Hessenberg, d::Integer) = size(F.H, d) size(F::Hessenberg) = size(F.H) -adjoint(F::Hessenberg) = Adjoint(F) +transpose(F::Hessenberg{<:Real}) = F' +transpose(::Hessenberg) = + throw(ArgumentError("transpose of Hessenberg decomposition is not supported, consider using adjoint")) # iteration for destructuring into components Base.iterate(S::Hessenberg) = (S.Q, Val(:H)) @@ -687,8 +689,8 @@ function rdiv!(B::AbstractVecOrMat{<:Complex}, F::Hessenberg{<:Complex,<:Any,<:A return B .= Complex.(Br,Bi) end -ldiv!(F::Adjoint{<:Any,<:Hessenberg}, B::AbstractVecOrMat) = rdiv!(B', F')' -rdiv!(B::AbstractMatrix, F::Adjoint{<:Any,<:Hessenberg}) = ldiv!(F', B')' +ldiv!(F::AdjointFactorization{<:Any,<:Hessenberg}, B::AbstractVecOrMat) = rdiv!(B', F')' +rdiv!(B::AbstractMatrix, F::AdjointFactorization{<:Any,<:Hessenberg}) = ldiv!(F', B')' det(F::Hessenberg) = det(F.H; shift=F.μ) logabsdet(F::Hessenberg) = logabsdet(F.H; shift=F.μ) diff --git a/stdlib/LinearAlgebra/src/lq.jl b/stdlib/LinearAlgebra/src/lq.jl index acc68192ed715..a162333be339a 100644 --- a/stdlib/LinearAlgebra/src/lq.jl +++ b/stdlib/LinearAlgebra/src/lq.jl @@ -135,8 +135,11 @@ AbstractArray(A::LQ) = AbstractMatrix(A) Matrix(A::LQ) = Array(AbstractArray(A)) Array(A::LQ) = Matrix(A) -adjoint(A::LQ) = Adjoint(A) -Base.copy(F::Adjoint{T,<:LQ{T}}) where {T} = +transpose(F::LQ{<:Real}) = F' +transpose(::LQ) = + throw(ArgumentError("transpose of LQ decomposition is not supported, consider using adjoint")) + +Base.copy(F::AdjointFactorization{T,<:LQ{T}}) where {T} = QR{T,typeof(F.parent.factors),typeof(F.parent.τ)}(copy(adjoint(F.parent.factors)), copy(F.parent.τ)) function getproperty(F::LQ, d::Symbol) @@ -343,7 +346,7 @@ function ldiv!(A::LQ, B::AbstractVecOrMat) return lmul!(adjoint(A.Q), B) end -function ldiv!(Fadj::Adjoint{<:Any,<:LQ}, B::AbstractVecOrMat) +function ldiv!(Fadj::AdjointFactorization{<:Any,<:LQ}, B::AbstractVecOrMat) require_one_based_indexing(B) m, n = size(Fadj) m >= n || throw(DimensionMismatch("solver does not support underdetermined systems (more columns than rows)")) diff --git a/stdlib/LinearAlgebra/src/lu.jl b/stdlib/LinearAlgebra/src/lu.jl index df4154b00e9ac..a93803ca2ea45 100644 --- a/stdlib/LinearAlgebra/src/lu.jl +++ b/stdlib/LinearAlgebra/src/lu.jl @@ -72,8 +72,9 @@ Base.iterate(S::LU, ::Val{:U}) = (S.U, Val(:p)) Base.iterate(S::LU, ::Val{:p}) = (S.p, Val(:done)) Base.iterate(S::LU, ::Val{:done}) = nothing -adjoint(F::LU) = Adjoint(F) -transpose(F::LU) = Transpose(F) +# LU prefers transpose over adjoint in the real case, override the generic fallback +adjoint(F::LU{<:Real}) = TransposeFactorization(F) +transpose(F::LU{<:Real}) = TransposeFactorization(F) # the following method is meant to catch calls to lu!(A::LAPACKArray) without a pivoting stategy lu!(A::StridedMatrix{<:BlasFloat}; check::Bool = true) = lu!(A, RowMaximum(); check=check) @@ -324,7 +325,7 @@ Factorization{T}(F::LU) where {T} = LU{T}(F) copy(A::LU{T,S,P}) where {T,S,P} = LU{T,S,P}(copy(A.factors), copy(A.ipiv), A.info) size(A::LU) = size(getfield(A, :factors)) -size(A::LU, i) = size(getfield(A, :factors), i) +size(A::LU, i::Integer) = size(getfield(A, :factors), i) function ipiv2perm(v::AbstractVector{T}, maxi::Integer) where T require_one_based_indexing(v) @@ -429,49 +430,29 @@ function ldiv!(A::LU, B::AbstractVecOrMat) ldiv!(UpperTriangular(A.factors), ldiv!(UnitLowerTriangular(A.factors), B)) end -ldiv!(transA::Transpose{T,<:LU{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = +ldiv!(transA::TransposeFactorization{T,<:LU{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = (A = transA.parent; LAPACK.getrs!('T', A.factors, A.ipiv, B)) -function ldiv!(transA::Transpose{<:Any,<:LU}, B::AbstractVecOrMat) +function ldiv!(transA::TransposeFactorization{<:Any,<:LU}, B::AbstractVecOrMat) A = transA.parent ldiv!(transpose(UnitLowerTriangular(A.factors)), ldiv!(transpose(UpperTriangular(A.factors)), B)) _apply_inverse_ipiv_rows!(A, B) end -ldiv!(adjF::Adjoint{T,<:LU{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:Real} = - (F = adjF.parent; ldiv!(transpose(F), B)) -ldiv!(adjA::Adjoint{T,<:LU{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = +ldiv!(adjA::AdjointFactorization{T,<:LU{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = (A = adjA.parent; LAPACK.getrs!('C', A.factors, A.ipiv, B)) -function ldiv!(adjA::Adjoint{<:Any,<:LU}, B::AbstractVecOrMat) +function ldiv!(adjA::AdjointFactorization{<:Any,<:LU}, B::AbstractVecOrMat) A = adjA.parent ldiv!(adjoint(UnitLowerTriangular(A.factors)), ldiv!(adjoint(UpperTriangular(A.factors)), B)) _apply_inverse_ipiv_rows!(A, B) end -(\)(A::Adjoint{<:Any,<:LU}, B::Adjoint{<:Any,<:AbstractVecOrMat}) = A \ copy(B) -(\)(A::Transpose{<:Any,<:LU}, B::Transpose{<:Any,<:AbstractVecOrMat}) = A \ copy(B) -(\)(A::Adjoint{T,<:LU{T,<:StridedMatrix}}, B::Adjoint{T,<:StridedVecOrMat{T}}) where {T<:BlasComplex} = +(\)(A::AdjointFactorization{T,<:LU{T,<:StridedMatrix}}, B::Adjoint{T,<:StridedVecOrMat{T}}) where {T<:BlasComplex} = LAPACK.getrs!('C', A.parent.factors, A.parent.ipiv, copy(B)) -(\)(A::Transpose{T,<:LU{T,<:StridedMatrix}}, B::Transpose{T,<:StridedVecOrMat{T}}) where {T<:BlasFloat} = +(\)(A::TransposeFactorization{T,<:LU{T,<:StridedMatrix}}, B::Transpose{T,<:StridedVecOrMat{T}}) where {T<:BlasFloat} = LAPACK.getrs!('T', A.parent.factors, A.parent.ipiv, copy(B)) -function (/)(A::AbstractMatrix, F::Adjoint{<:Any,<:LU}) - T = promote_type(eltype(A), eltype(F)) - return adjoint(ldiv!(F.parent, copy_similar(adjoint(A), T))) -end -# To avoid ambiguities with definitions in adjtrans.jl and factorizations.jl -(/)(adjA::AdjointAbsVec, F::Adjoint{<:Any,<:LU}) = adjoint(F.parent \ adjA.parent) -(/)(adjA::AdjointAbsMat, F::Adjoint{<:Any,<:LU}) = adjoint(F.parent \ adjA.parent) -function (/)(trA::TransposeAbsVec, F::Adjoint{<:Any,<:LU}) - T = promote_type(eltype(trA), eltype(F)) - return adjoint(ldiv!(F.parent, conj!(copy_similar(trA.parent, T)))) -end -function (/)(trA::TransposeAbsMat, F::Adjoint{<:Any,<:LU}) - T = promote_type(eltype(trA), eltype(F)) - return adjoint(ldiv!(F.parent, conj!(copy_similar(trA.parent, T)))) -end - function det(F::LU{T}) where T n = checksquare(F) issuccess(F) || return zero(T) @@ -654,7 +635,7 @@ function ldiv!(A::LU{T,Tridiagonal{T,V}}, B::AbstractVecOrMat) where {T,V} return B end -function ldiv!(transA::Transpose{<:Any,<:LU{T,Tridiagonal{T,V}}}, B::AbstractVecOrMat) where {T,V} +function ldiv!(transA::TransposeFactorization{<:Any,<:LU{T,Tridiagonal{T,V}}}, B::AbstractVecOrMat) where {T,V} require_one_based_indexing(B) A = transA.parent n = size(A,1) @@ -691,7 +672,7 @@ function ldiv!(transA::Transpose{<:Any,<:LU{T,Tridiagonal{T,V}}}, B::AbstractVec end # Ac_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where {T<:Real} = At_ldiv_B!(A,B) -function ldiv!(adjA::Adjoint{<:Any,<:LU{T,Tridiagonal{T,V}}}, B::AbstractVecOrMat) where {T,V} +function ldiv!(adjA::AdjointFactorization{<:Any,<:LU{T,Tridiagonal{T,V}}}, B::AbstractVecOrMat) where {T,V} require_one_based_indexing(B) A = adjA.parent n = size(A,1) @@ -728,8 +709,8 @@ function ldiv!(adjA::Adjoint{<:Any,<:LU{T,Tridiagonal{T,V}}}, B::AbstractVecOrMa end rdiv!(B::AbstractMatrix, A::LU) = transpose(ldiv!(transpose(A), transpose(B))) -rdiv!(B::AbstractMatrix, A::Transpose{<:Any,<:LU}) = transpose(ldiv!(A.parent, transpose(B))) -rdiv!(B::AbstractMatrix, A::Adjoint{<:Any,<:LU}) = adjoint(ldiv!(A.parent, adjoint(B))) +rdiv!(B::AbstractMatrix, A::TransposeFactorization{<:Any,<:LU}) = transpose(ldiv!(A.parent, transpose(B))) +rdiv!(B::AbstractMatrix, A::AdjointFactorization{<:Any,<:LU}) = adjoint(ldiv!(A.parent, adjoint(B))) # Conversions AbstractMatrix(F::LU) = (F.L * F.U)[invperm(F.p),:] diff --git a/stdlib/LinearAlgebra/src/qr.jl b/stdlib/LinearAlgebra/src/qr.jl index 1de2c2edadf99..30deb785e6019 100644 --- a/stdlib/LinearAlgebra/src/qr.jl +++ b/stdlib/LinearAlgebra/src/qr.jl @@ -514,7 +514,9 @@ end Base.propertynames(F::QRPivoted, private::Bool=false) = (:R, :Q, :p, :P, (private ? fieldnames(typeof(F)) : ())...) -adjoint(F::Union{QR,QRPivoted,QRCompactWY}) = Adjoint(F) +transpose(F::Union{QR{<:Real},QRPivoted{<:Real},QRCompactWY{<:Real}}) = F' +transpose(::Union{QR,QRPivoted,QRCompactWY}) = + throw(ArgumentError("transpose of QR decomposition is not supported, consider using adjoint")) abstract type AbstractQ{T} <: AbstractMatrix{T} end @@ -1002,7 +1004,7 @@ function _apply_permutation!(F::QRPivoted, B::AbstractVecOrMat) end _apply_permutation!(F::Factorization, B::AbstractVecOrMat) = B -function ldiv!(Fadj::Adjoint{<:Any,<:Union{QR,QRCompactWY,QRPivoted}}, B::AbstractVecOrMat) +function ldiv!(Fadj::AdjointFactorization{<:Any,<:Union{QR,QRCompactWY,QRPivoted}}, B::AbstractVecOrMat) require_one_based_indexing(B) m, n = size(Fadj) diff --git a/stdlib/LinearAlgebra/test/adjtrans.jl b/stdlib/LinearAlgebra/test/adjtrans.jl index 7479057d9f027..e40beb29787cf 100644 --- a/stdlib/LinearAlgebra/test/adjtrans.jl +++ b/stdlib/LinearAlgebra/test/adjtrans.jl @@ -489,13 +489,13 @@ end @test B == A .* A' end -@testset "test show methods for $t of Factorizations" for t in (Adjoint, Transpose) - A = randn(4, 4) +@testset "test show methods for $t of Factorizations" for t in (adjoint, transpose) + A = randn(ComplexF64, 4, 4) F = lu(A) Fop = t(F) - @test "LinearAlgebra."*sprint(show, Fop) == + @test sprint(show, Fop) == "$t of "*sprint(show, parent(Fop)) - @test "LinearAlgebra."*sprint((io, t) -> show(io, MIME"text/plain"(), t), Fop) == + @test sprint((io, t) -> show(io, MIME"text/plain"(), t), Fop) == "$t of "*sprint((io, t) -> show(io, MIME"text/plain"(), t), parent(Fop)) end diff --git a/stdlib/LinearAlgebra/test/factorization.jl b/stdlib/LinearAlgebra/test/factorization.jl index d200eff2f17bf..72233293ff515 100644 --- a/stdlib/LinearAlgebra/test/factorization.jl +++ b/stdlib/LinearAlgebra/test/factorization.jl @@ -56,11 +56,24 @@ end A = randn(3, 3) A = A * A' # ensure A is pos. def. and symmetric F = f(A) - tF = Transpose(F) - aF = Adjoint(F) @test size(F) == size(A) - @test size(tF) == size(Transpose(A)) - @test size(aF) == size(Adjoint(A)) + @test size(F') == size(A') +end + +@testset "size for transpose factorizations - $f" for f in Any[ + bunchkaufman, + cholesky, + x -> cholesky(x, RowMaximum()), + hessenberg, + lq, + lu, + svd, +] + A = randn(3, 3) + A = A * A' # ensure A is pos. def. and symmetric + F = f(A) + @test size(F) == size(A) + @test size(transpose(F)) == size(transpose(A)) end @testset "equality of QRCompactWY" begin diff --git a/stdlib/LinearAlgebra/test/qr.jl b/stdlib/LinearAlgebra/test/qr.jl index c8db95b8c34b6..9ccc42dfb3259 100644 --- a/stdlib/LinearAlgebra/test/qr.jl +++ b/stdlib/LinearAlgebra/test/qr.jl @@ -211,9 +211,9 @@ rectangularQ(Q::LinearAlgebra.AbstractQ) = convert(Array, Q) end @testset "transpose errors" begin - @test_throws MethodError transpose(qr(randn(3,3))) - @test_throws MethodError transpose(qr(randn(3,3), NoPivot())) - @test_throws MethodError transpose(qr(big.(randn(3,3)))) + @test_throws ArgumentError transpose(qr(randn(ComplexF64,3,3))) + @test_throws ArgumentError transpose(qr(randn(ComplexF64,3,3), NoPivot())) + @test_throws ArgumentError transpose(qr(big.(randn(ComplexF64,3,3)))) end @testset "Issue 7304" begin From 4d3607a53bf4cc2f4ece12dc331fe8691f655768 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 15 Feb 2023 14:01:58 -0500 Subject: [PATCH 132/775] move ans and err variables to MainInclude module (#48308) This hides them from `names()` unfortunately, but means we will not accidentally discard a variable from the user in Main, either because the import will fail or the assignment will fail. If we later have world-versioned bindings, this would also mean you could toggle (between worlds) between being module-local and a special import value. This also needed some improvements to docs, so that it would print docstrings for unassigned globals without also simultaneously claiming that it did not exist. Fix #43172 Fix #48299 --- base/Base.jl | 8 +++----- base/client.jl | 25 ++++++++++++++++++++++--- base/docs/basedocs.jl | 15 --------------- base/sysimg.jl | 1 + stdlib/REPL/src/REPL.jl | 21 +++++++++++++++++---- stdlib/REPL/src/docview.jl | 9 +++++++-- stdlib/REPL/test/repl.jl | 8 +++++--- 7 files changed, 55 insertions(+), 32 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 1409cf8068039..85a9c8d5048e3 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -437,17 +437,15 @@ include("loading.jl") # misc useful functions & macros include("timing.jl") include("util.jl") - +include("client.jl") include("asyncmap.jl") # deprecated functions include("deprecated.jl") - -# Some basic documentation +# +# Some additional basic documentation include("docs/basedocs.jl") -include("client.jl") - # Documentation -- should always be included last in sysimg. include("docs/Docs.jl") using .Docs diff --git a/base/client.jl b/base/client.jl index 7cf6dc334b240..dd529dad5281e 100644 --- a/base/client.jl +++ b/base/client.jl @@ -132,14 +132,14 @@ function eval_user_input(errio, @nospecialize(ast), show_value::Bool) end if lasterr !== nothing lasterr = scrub_repl_backtrace(lasterr) - istrivialerror(lasterr) || setglobal!(Main, :err, lasterr) + istrivialerror(lasterr) || setglobal!(Base.MainInclude, :err, lasterr) invokelatest(display_error, errio, lasterr) errcount = 0 lasterr = nothing else ast = Meta.lower(Main, ast) value = Core.eval(Main, ast) - setglobal!(Main, :ans, value) + setglobal!(Base.MainInclude, :ans, value) if !(value === nothing) && show_value if have_color print(answer_color()) @@ -159,7 +159,7 @@ function eval_user_input(errio, @nospecialize(ast), show_value::Bool) end errcount += 1 lasterr = scrub_repl_backtrace(current_exceptions()) - setglobal!(Main, :err, lasterr) + setglobal!(Base.MainInclude, :err, lasterr) if errcount > 2 @error "It is likely that something important is broken, and Julia will not be able to continue normally" errcount break @@ -478,6 +478,25 @@ function include(fname::AbstractString) Base._include(identity, Main, fname) end eval(x) = Core.eval(Main, x) + +""" + ans + +A variable referring to the last computed value, automatically imported to the interactive prompt. +""" +global ans = nothing + +""" + err + +A variable referring to the last thrown errors, automatically imported to the interactive prompt. +The thrown errors are collected in a stack of exceptions. +""" +global err = nothing + +# weakly exposes ans and err variables to Main +export ans, err + end """ diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index fa4d9992b6000..7f2c05501edb8 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -1470,21 +1470,6 @@ parser rather than being implemented as a normal string macro `@var_str`. """ kw"var\"name\"", kw"@var_str" -""" - ans - -A variable referring to the last computed value, automatically set at the interactive prompt. -""" -kw"ans" - -""" - err - -A variable referring to the last thrown errors, automatically set at the interactive prompt. -The thrown errors are collected in a stack of exceptions. -""" -kw"err" - """ devnull diff --git a/base/sysimg.jl b/base/sysimg.jl index ef7bad929b743..0d89754e7c11c 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -5,6 +5,7 @@ Core.include(Main, "Base.jl") using .Base # Set up Main module +using Base.MainInclude # ans, err, and sometimes Out import Base.MainInclude: eval, include # Ensure this file is also tracked diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 438dd75371b12..a6958db88f460 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -148,7 +148,7 @@ function eval_user_input(@nospecialize(ast), backend::REPLBackend, mod::Module) end value = Core.eval(mod, ast) backend.in_eval = false - setglobal!(mod, :ans, value) + setglobal!(Base.MainInclude, :ans, value) put!(backend.response_channel, Pair{Any, Bool}(value, false)) end break @@ -290,7 +290,7 @@ function print_response(errio::IO, response, show_value::Bool, have_color::Bool, Base.sigatomic_end() if iserr val = Base.scrub_repl_backtrace(val) - Base.istrivialerror(val) || setglobal!(Main, :err, val) + Base.istrivialerror(val) || setglobal!(Base.MainInclude, :err, val) Base.invokelatest(Base.display_error, errio, val) else if val !== nothing && show_value @@ -313,7 +313,7 @@ function print_response(errio::IO, response, show_value::Bool, have_color::Bool, println(errio, "SYSTEM (REPL): showing an error caused an error") try excs = Base.scrub_repl_backtrace(current_exceptions()) - setglobal!(Main, :err, excs) + setglobal!(Base.MainInclude, :err, excs) Base.invokelatest(Base.display_error, errio, excs) catch e # at this point, only print the name of the type as a Symbol to @@ -1416,8 +1416,10 @@ end function capture_result(n::Ref{Int}, @nospecialize(x)) n = n[] - mod = REPL.active_module() + mod = Base.MainInclude if !isdefined(mod, :Out) + @eval mod global Out + @eval mod export Out setglobal!(mod, :Out, Dict{Int, Any}()) end if x !== getglobal(mod, :Out) && x !== nothing # remove this? @@ -1460,6 +1462,17 @@ function ipython_mode!(repl::LineEditREPL=Base.active_repl, backend=nothing) push!(__current_ast_transforms(backend), @nospecialize(ast) -> out_transform(ast, n)) return end + +""" + Out[n] + +A variable referring to all previously computed values, automatically imported to the interactive prompt. +Only defined and exists while using [IPython mode](@ref IPython-mode). + +See also [`ans`](@ref). +""" +Base.MainInclude.Out + end import .IPython.ipython_mode! diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index ea663fa16007f..db28c84b07cb6 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -255,7 +255,11 @@ function summarize(binding::Binding, sig) else println(io, "No documentation found.\n") quot = any(isspace, sprint(print, binding)) ? "'" : "" - println(io, "Binding ", quot, "`", binding, "`", quot, " does not exist.") + if Base.isbindingresolved(binding.mod, binding.var) + println(io, "Binding ", quot, "`", binding, "`", quot, " exists, but has not been assigned a value.") + else + println(io, "Binding ", quot, "`", binding, "`", quot, " does not exist.") + end end md = Markdown.parse(seekstart(io)) # Save metadata in the generated markdown. @@ -475,7 +479,8 @@ function repl(io::IO, s::Symbol; brief::Bool=true, mod::Module=Main) quote repl_latex($io, $str) repl_search($io, $str, $mod) - $(if !isdefined(mod, s) && !haskey(keywords, s) && !Base.isoperator(s) + $(if !isdefined(mod, s) && !Base.isbindingresolved(mod, s) && !haskey(keywords, s) && !Base.isoperator(s) + # n.b. we call isdefined for the side-effect of resolving the binding, if possible :(repl_corrections($io, $str, $mod)) end) $(_repl(s, brief)) diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index edcb91defc9ab..9e6ab515daba3 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -1164,7 +1164,7 @@ fake_repl() do stdin_write, stdout_read, repl Base.wait(repltask) end -help_result(line, mod::Module=Base) = mod.eval(REPL._helpmode(IOBuffer(), line)) +help_result(line, mod::Module=Base) = Core.eval(mod, REPL._helpmode(IOBuffer(), line)) # Docs.helpmode tests: we test whether the correct expressions are being generated here, # rather than complete integration with Julia's REPL mode system. @@ -1203,7 +1203,9 @@ end @test occursin("broadcast", sprint(show, help_result(".<="))) # Issue 39427 -@test occursin("does not exist", sprint(show, help_result(":="))) +@test occursin("does not exist.", sprint(show, help_result(":="))) +global some_undef_global +@test occursin("exists,", sprint(show, help_result("some_undef_global", @__MODULE__))) # Issue #40563 @test occursin("does not exist", sprint(show, help_result(".."))) @@ -1481,7 +1483,7 @@ fake_repl() do stdin_write, stdout_read, repl end # initialize `err` to `nothing` t = @async (readline(stdout_read); readuntil(stdout_read, "\e[0m\n")) - write(stdin_write, "global err = nothing\n") + write(stdin_write, "setglobal!(Base.MainInclude, :err, nothing)\n") wait(t) readuntil(stdout_read, "julia> ", keep=true) # generate top-level error From 80dc13595c0aa29062705547debab679b5735c08 Mon Sep 17 00:00:00 2001 From: Bob Cassels Date: Wed, 15 Feb 2023 16:29:12 -0500 Subject: [PATCH 133/775] Tabs to spaces. Oops, sorry. (#48687) --- test/math.jl | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/math.jl b/test/math.jl index f08cc6c32ce15..9dd634da2b4f3 100644 --- a/test/math.jl +++ b/test/math.jl @@ -182,7 +182,7 @@ end @test cbrt(x) ≈ cbrt(big(x)) @test cos(x) ≈ cos(big(x)) @test cosh(x) ≈ cosh(big(x)) - @test cospi(x) ≈ cospi(big(x)) + @test cospi(x) ≈ cospi(big(x)) @test exp(x) ≈ exp(big(x)) @test exp10(x) ≈ exp10(big(x)) @test exp2(x) ≈ exp2(big(x)) @@ -200,7 +200,7 @@ end @test sqrt(x) ≈ sqrt(big(x)) @test tan(x) ≈ tan(big(x)) @test tanh(x) ≈ tanh(big(x)) - @test tanpi(x) ≈ tanpi(big(x)) + @test tanpi(x) ≈ tanpi(big(x)) @test sec(x) ≈ sec(big(x)) @test csc(x) ≈ csc(big(x)) @test secd(x) ≈ secd(big(x)) @@ -503,22 +503,22 @@ end @test cospi(convert(T,-1.5))::fT ⩲ zero(fT) @test_throws DomainError cospi(convert(T,Inf)) end - @testset "trig pi functions accuracy" for numerator in -20:1:20 - for func in (sinpi, cospi, tanpi, - x -> sincospi(x)[1], - x -> sincospi(x)[2]) + @testset "trig pi functions accuracy" for numerator in -20:1:20 + for func in (sinpi, cospi, tanpi, + x -> sincospi(x)[1], + x -> sincospi(x)[2]) x = numerator // 20 - # Check that rational function works - @test func(x) ≈ func(BigFloat(x)) - # Use short value so that wider values will be exactly equal - shortx = Float16(x) - # Compare to BigFloat value - bigvalue = func(BigFloat(shortx)) - for T in (Float16,Float32,Float64) - @test func(T(shortx)) ≈ T(bigvalue) + # Check that rational function works + @test func(x) ≈ func(BigFloat(x)) + # Use short value so that wider values will be exactly equal + shortx = Float16(x) + # Compare to BigFloat value + bigvalue = func(BigFloat(shortx)) + for T in (Float16,Float32,Float64) + @test func(T(shortx)) ≈ T(bigvalue) end end - end + end @testset begin # If the machine supports fma (fused multiply add), we require exact equality. # Otherwise, we only require approximate equality. @@ -621,7 +621,7 @@ end @test sinpi(complex(x, x)) ≈ ComplexF64(sinpi(complex(big(x), big(x)))) @test cospi(complex(x, x)) ≈ ComplexF64(cospi(complex(big(x), big(x)))) end - @test tanpi(x) ≈ Float64(tanpi(big(x))) + @test tanpi(x) ≈ Float64(tanpi(big(x))) @test sinc(x) ≈ Float64(sinc(big(x))) @test cosc(x) ≈ Float64(cosc(big(x))) @test sinc(complex(x, x)) ≈ ComplexF64(sinc(complex(big(x), big(x)))) From 75215fadf3314888ad06bd7b2c47382ed4698266 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Wed, 15 Feb 2023 20:01:55 -0500 Subject: [PATCH 134/775] remove lgamma(Float16) definition (#48675) --- base/math.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/math.jl b/base/math.jl index 27a66c36fe884..004f66f70c74e 100644 --- a/base/math.jl +++ b/base/math.jl @@ -1489,7 +1489,7 @@ include("special/log.jl") # Float16 definitions for func in (:sin,:cos,:tan,:asin,:acos,:atan,:cosh,:tanh,:asinh,:acosh, - :atanh,:log,:log2,:log10,:sqrt,:lgamma,:log1p) + :atanh,:log,:log2,:log10,:sqrt,:log1p) @eval begin $func(a::Float16) = Float16($func(Float32(a))) $func(a::ComplexF16) = ComplexF16($func(ComplexF32(a))) From 2be7f29265a973561dd216ed704451a6c3959e0d Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 16 Feb 2023 16:17:52 +0800 Subject: [PATCH 135/775] Make sure `rename_unionall` rename all enclosing `UnionAll`s (#48691) The previous `===` always return true thus we only rename the outmost typevar on master. Also add some test for this. --- base/essentials.jl | 7 +------ test/compiler/inference.jl | 11 ++++++++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/base/essentials.jl b/base/essentials.jl index 14ccce0d125fb..07e8db31fb0f1 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -367,13 +367,8 @@ function rename_unionall(@nospecialize(u)) if !isa(u, UnionAll) return u end - body = rename_unionall(u.body) - if body === u.body - body = u - else - body = UnionAll(u.var, body) - end var = u.var::TypeVar + body = UnionAll(var, rename_unionall(u.body)) nv = TypeVar(var.name, var.lb, var.ub) return UnionAll(nv, body{nv}) end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index d34dfb3999f82..09c687ecadf3b 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2743,7 +2743,16 @@ let 𝕃 = Core.Compiler.fallback_lattice @test apply_type_tfunc(𝕃, Const(Issue47089), A, A) <: (Type{Issue47089{A,B}} where {A<:Integer, B<:Integer}) end @test only(Base.return_types(keys, (Dict{String},))) == Base.KeySet{String, T} where T<:(Dict{String}) -@test only(Base.return_types((r)->similar(Array{typeof(r[])}, 1), (Base.RefValue{Array{Int}},))) == Vector{T} where T<:(Array{Int}) +@test only(Base.return_types((r)->similar(Array{typeof(r[])}, 1), (Base.RefValue{Array{Int}},))) == Vector{<:Array{Int}} +@test only(Base.return_types((r)->similar(Array{typeof(r[])}, 1), (Base.RefValue{Array{<:Real}},))) == Vector{<:Array{<:Real}} + +let A = Tuple{A,B,C,D,E,F,G,H} where {A,B,C,D,E,F,G,H} + B = Core.Compiler.rename_unionall(A) + for i in 1:8 + @test A.var != B.var && (i == 1 ? A == B : A != B) + A, B = A.body, B.body + end +end # PR 27351, make sure optimized type intersection for method invalidation handles typevars From e45bb08ea560d960ac55f1dd9a1d63882abc21e7 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Thu, 16 Feb 2023 03:52:36 -0500 Subject: [PATCH 136/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=20ed505db0b=20to=205ad4b9e92=20(#48690)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dilum Aluthge --- .../Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/md5 | 1 + .../Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/sha512 | 1 + .../Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/md5 | 1 - .../Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/md5 create mode 100644 deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/sha512 diff --git a/deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/md5 b/deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/md5 new file mode 100644 index 0000000000000..9b3ef595fe59d --- /dev/null +++ b/deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/md5 @@ -0,0 +1 @@ +94e0a5b63c14d98b2f4d05fd5c6ff1e5 diff --git a/deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/sha512 b/deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/sha512 new file mode 100644 index 0000000000000..8a584a2460102 --- /dev/null +++ b/deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/sha512 @@ -0,0 +1 @@ +fd4b47f78acb5981da78609bfba37fbe0ae066f6b0bc65dd090f42677e5e90d19e2deb16b624d9722820fc78b7ffb0d019089bb9032daf08aa083cf1f69e5dd7 diff --git a/deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/md5 b/deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/md5 deleted file mode 100644 index 530e2397030e2..0000000000000 --- a/deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -13f881dc90d09d12675dafeef34f72c6 diff --git a/deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/sha512 b/deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/sha512 deleted file mode 100644 index b6b0b1c462111..0000000000000 --- a/deps/checksums/Pkg-ed505db0bb329df25fa0eaba9de53ecb5fa89c93.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -fd1fba3f022bfea61d1bf9dfb016a60ceb9be7d4bb87574e01acdb6ca9b818030bbc163c9bd823f0388ed3b0f200c43ac4aceda92b137f98605dcc37ed8c8e64 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index f4a26b0554af1..6bb8ba26baf06 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = ed505db0bb329df25fa0eaba9de53ecb5fa89c93 +PKG_SHA1 = 5ad4b9e928620cd807f22ae0865ae452cd1bea81 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From d7cbf395c442d5ecd71af2cecd97f66f8799278a Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Thu, 16 Feb 2023 11:01:34 +0100 Subject: [PATCH 137/775] Don't subtype `AbstractQ <: AbstractMatrix` (#46196) --- NEWS.md | 9 + stdlib/LinearAlgebra/docs/src/index.md | 101 +++- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 7 +- stdlib/LinearAlgebra/src/abstractq.jl | 628 +++++++++++++++++++++ stdlib/LinearAlgebra/src/diagonal.jl | 8 +- stdlib/LinearAlgebra/src/hessenberg.jl | 82 +-- stdlib/LinearAlgebra/src/lq.jl | 167 +----- stdlib/LinearAlgebra/src/qr.jl | 405 +------------ stdlib/LinearAlgebra/src/special.jl | 87 +-- stdlib/LinearAlgebra/src/uniformscaling.jl | 9 - stdlib/LinearAlgebra/test/abstractq.jl | 84 +++ stdlib/LinearAlgebra/test/hessenberg.jl | 4 +- stdlib/LinearAlgebra/test/lq.jl | 15 +- stdlib/LinearAlgebra/test/qr.jl | 42 +- stdlib/LinearAlgebra/test/testgroups | 1 + test/bitarray.jl | 2 +- 16 files changed, 911 insertions(+), 740 deletions(-) create mode 100644 stdlib/LinearAlgebra/src/abstractq.jl create mode 100644 stdlib/LinearAlgebra/test/abstractq.jl diff --git a/NEWS.md b/NEWS.md index ff1501104e6be..976c88ee65cb8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -51,6 +51,15 @@ Standard library changes #### LinearAlgebra +* `AbstractQ` no longer subtypes to `AbstractMatrix`. Moreover, `adjoint(Q::AbstractQ)` + no longer wraps `Q` in an `Adjoint` type, but instead in an `AdjointQ`, that itself + subtypes `AbstractQ`. This change accounts for the fact that typically `AbstractQ` + instances behave like function-based, matrix-backed linear operators, and hence don't + allow for efficient indexing. Also, many `AbstractQ` types can act on vectors/matrices + of different size, acting like a matrix with context-dependent size. With this change, + `AbstractQ` has a well-defined API that is described in detail in the + [Julia documentation](https://docs.julialang.org/en/v1/stdlib/LinearAlgebra/#man-linalg-abstractq) + ([#46196]). * Adjoints and transposes of `Factorization` objects are no longer wrapped in `Adjoint` and `Transpose` wrappers, respectively. Instead, they are wrapped in `AdjointFactorization` and `TranposeFactorization` types, which themselves subtype diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index 5b5c43da9875d..cd317b4e36df6 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -150,7 +150,10 @@ julia> sB\x -1.1086956521739126 -1.4565217391304346 ``` -The `\` operation here performs the linear solution. The left-division operator is pretty powerful and it's easy to write compact, readable code that is flexible enough to solve all sorts of systems of linear equations. + +The `\` operation here performs the linear solution. The left-division operator is pretty +powerful and it's easy to write compact, readable code that is flexible enough to solve all +sorts of systems of linear equations. ## Special matrices @@ -309,6 +312,94 @@ Adjoints and transposes of [`Factorization`](@ref) objects are lazily wrapped in `AdjointFactorization` and `TransposeFactorization` objects, respectively. Generically, transpose of real `Factorization`s are wrapped as `AdjointFactorization`. +## [Orthogonal matrices (`AbstractQ`)](@id man-linalg-abstractq) + +Some matrix factorizations generate orthogonal/unitary "matrix" factors. These +factorizations include QR-related factorizations obtained from calls to [`qr`](@ref), i.e., +`QR`, `QRCompactWY` and `QRPivoted`, the Hessenberg factorization obtained from calls to +[`hessenberg`](@ref), and the LQ factorization obtained from [`lq`](@ref). While these +orthogonal/unitary factors admit a matrix representation, their internal representation +is, for performance and memory reasons, different. Hence, they should be rather viewed as +matrix-backed, function-based linear operators. In particular, reading, for instance, a +column of its matrix representation requires running "matrix"-vector multiplication code, +rather than simply reading out data from memory (possibly filling parts of the vector with +structural zeros). Another clear distinction from other, non-triangular matrix types is +that the underlying multiplication code allows for in-place modification during multiplication. +Furthermore, objects of specific `AbstractQ` subtypes as those created via [`qr`](@ref), +[`hessenberg`](@ref) and [`lq`](@ref) can behave like a square or a rectangular matrix +depending on context: + +```julia +julia> using LinearAlgebra + +julia> Q = qr(rand(3,2)).Q +3×3 LinearAlgebra.QRCompactWYQ{Float64, Matrix{Float64}, Matrix{Float64}} + +julia> Matrix(Q) +3×2 Matrix{Float64}: + -0.320597 0.865734 + -0.765834 -0.475694 + -0.557419 0.155628 + +julia> Q*I +3×3 Matrix{Float64}: + -0.320597 0.865734 -0.384346 + -0.765834 -0.475694 -0.432683 + -0.557419 0.155628 0.815514 + +julia> Q*ones(2) +3-element Vector{Float64}: + 0.5451367118802273 + -1.241527373086654 + -0.40179067589600226 + +julia> Q*ones(3) +3-element Vector{Float64}: + 0.16079054743832022 + -1.674209978965636 + 0.41372375588835797 + +julia> ones(1,2) * Q' +1×3 Matrix{Float64}: + 0.545137 -1.24153 -0.401791 + +julia> ones(1,3) * Q' +1×3 Matrix{Float64}: + 0.160791 -1.67421 0.413724 +``` + +Due to this distinction from dense or structured matrices, the abstract `AbstractQ` type +does not subtype `AbstractMatrix`, but instead has its own type hierarchy. Custom types +that subtype `AbstractQ` can rely on generic fallbacks if the following interface is satisfied. +For example, for + +```julia +struct MyQ{T} <: LinearAlgebra.AbstractQ{T} + # required fields +end +``` + +provide overloads for + +```julia +Base.size(Q::MyQ) # size of corresponding square matrix representation +Base.convert(::Type{AbstractQ{T}}, Q::MyQ) # eltype promotion [optional] +LinearAlgebra.lmul!(Q::MyQ, x::AbstractVecOrMat) # left-multiplication +LinearAlgebra.rmul!(A::AbstractMatrix, Q::MyQ) # right-multiplication +``` + +If `eltype` promotion is not of interest, the `convert` method is unnecessary, since by +default `convert(::Type{AbstractQ{T}}, Q::AbstractQ{T})` returns `Q` itself. +Adjoints of `AbstractQ`-typed objects are lazily wrapped in an `AdjointQ` wrapper type, +which requires its own `LinearAlgebra.lmul!` and `LinearAlgebra.rmul!` methods. Given this +set of methods, any `Q::MyQ` can be used like a matrix, preferably in a multiplicative +context: multiplication via `*` with scalars, vectors and matrices from left and right, +obtaining a matrix representation of `Q` via `Matrix(Q)` (or `Q*I`) and indexing into the +matrix representation all work. In contrast, addition and subtraction as well as more +generally broadcasting over elements in the matrix representation fail because that would +be highly inefficient. For such use cases, consider computing the matrix representation +up front and cache it for future reuse. + ## Standard functions Linear algebra functions in Julia are largely implemented by calling functions from [LAPACK](http://www.netlib.org/lapack/). @@ -505,6 +596,7 @@ four methods defined, for [`Float32`](@ref), [`Float64`](@ref), [`ComplexF32`](@ and [`ComplexF64`](@ref Complex) arrays. ### [BLAS character arguments](@id stdlib-blas-chars) + Many BLAS functions accept arguments that determine whether to transpose an argument (`trans`), which triangle of a matrix to reference (`uplo` or `ul`), whether the diagonal of a triangular matrix can be assumed to @@ -512,18 +604,21 @@ be all ones (`dA`) or which side of a matrix multiplication the input argument belongs on (`side`). The possibilities are: #### [Multiplication order](@id stdlib-blas-side) + | `side` | Meaning | |:-------|:--------------------------------------------------------------------| | `'L'` | The argument goes on the *left* side of a matrix-matrix operation. | | `'R'` | The argument goes on the *right* side of a matrix-matrix operation. | #### [Triangle referencing](@id stdlib-blas-uplo) + | `uplo`/`ul` | Meaning | |:------------|:------------------------------------------------------| | `'U'` | Only the *upper* triangle of the matrix will be used. | | `'L'` | Only the *lower* triangle of the matrix will be used. | #### [Transposition operation](@id stdlib-blas-trans) + | `trans`/`tX` | Meaning | |:-------------|:--------------------------------------------------------| | `'N'` | The input matrix `X` is not transposed or conjugated. | @@ -531,12 +626,12 @@ the input argument belongs on (`side`). The possibilities are: | `'C'` | The input matrix `X` will be conjugated and transposed. | #### [Unit diagonal](@id stdlib-blas-diag) + | `diag`/`dX` | Meaning | |:------------|:----------------------------------------------------------| | `'N'` | The diagonal values of the matrix `X` will be read. | | `'U'` | The diagonal of the matrix `X` is assumed to be all ones. | - ```@docs LinearAlgebra.BLAS LinearAlgebra.BLAS.set_num_threads @@ -582,6 +677,7 @@ and define matrix-vector operations. [Dongarra-1988]: https://dl.acm.org/doi/10.1145/42288.42291 **return a vector** + ```@docs LinearAlgebra.BLAS.gemv! LinearAlgebra.BLAS.gemv(::Any, ::Any, ::Any, ::Any) @@ -611,6 +707,7 @@ LinearAlgebra.BLAS.trsv ``` **return a matrix** + ```@docs LinearAlgebra.BLAS.ger! # xGERU diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index ed5e8d24ba80f..a29c259dae607 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -15,7 +15,7 @@ import Base: USE_BLAS64, abs, acos, acosh, acot, acoth, acsc, acsch, adjoint, as length, log, map, ndims, one, oneunit, parent, permutedims, power_by_squaring, print_matrix, promote_rule, real, round, sec, sech, setindex!, show, similar, sin, sincos, sinh, size, sqrt, strides, stride, tan, tanh, transpose, trunc, typed_hcat, - vec, zero + vec, view, zero using Base: IndexLinear, promote_eltype, promote_op, promote_typeof, @propagate_inbounds, reduce, typed_hvcat, typed_vcat, require_one_based_indexing, splat @@ -431,8 +431,6 @@ include("tridiag.jl") include("triangular.jl") include("factorization.jl") -include("qr.jl") -include("lq.jl") include("eigen.jl") include("svd.jl") include("symmetric.jl") @@ -443,7 +441,10 @@ include("diagonal.jl") include("symmetriceigen.jl") include("bidiag.jl") include("uniformscaling.jl") +include("qr.jl") +include("lq.jl") include("hessenberg.jl") +include("abstractq.jl") include("givens.jl") include("special.jl") include("bitarray.jl") diff --git a/stdlib/LinearAlgebra/src/abstractq.jl b/stdlib/LinearAlgebra/src/abstractq.jl new file mode 100644 index 0000000000000..e591ca69fe429 --- /dev/null +++ b/stdlib/LinearAlgebra/src/abstractq.jl @@ -0,0 +1,628 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +abstract type AbstractQ{T} end + +struct AdjointQ{T,S<:AbstractQ{T}} <: AbstractQ{T} + Q::S +end + +parent(adjQ::AdjointQ) = adjQ.Q +eltype(::Type{<:AbstractQ{T}}) where {T} = T +ndims(::AbstractQ) = 2 + +# inversion/adjoint/transpose +inv(Q::AbstractQ) = Q' +adjoint(Q::AbstractQ) = AdjointQ(Q) +transpose(Q::AbstractQ{<:Real}) = AdjointQ(Q) +transpose(Q::AbstractQ) = error("transpose not implemented for $(typeof(Q)). Consider using adjoint instead of transpose.") +adjoint(adjQ::AdjointQ) = adjQ.Q + +# promotion with AbstractMatrix, at least for equal eltypes +promote_rule(::Type{<:AbstractMatrix{T}}, ::Type{<:AbstractQ{T}}) where {T} = + (@inline; Union{AbstractMatrix{T},AbstractQ{T}}) + +# conversion +AbstractQ{S}(Q::AbstractQ{S}) where {S} = Q +# the following eltype promotion needs to be defined for each subtype +# convert(::Type{AbstractQ{T}}, Q::QType) where {T} = QType{T}(Q) +convert(::Type{AbstractQ{T}}, Q::AbstractQ{T}) where {T} = Q +convert(::Type{AbstractQ{T}}, adjQ::AdjointQ{T}) where {T} = adjQ +convert(::Type{AbstractQ{T}}, adjQ::AdjointQ) where {T} = convert(AbstractQ{T}, adjQ.Q)' + +# ... to matrix +Matrix{T}(Q::AbstractQ) where {T} = convert(Matrix{T}, Q*I) # generic fallback, yields square matrix +Matrix{T}(adjQ::AdjointQ{S}) where {T,S} = convert(Matrix{T}, lmul!(adjQ, Matrix{S}(I, size(adjQ)))) +Matrix(Q::AbstractQ{T}) where {T} = Matrix{T}(Q) +Array{T}(Q::AbstractQ) where {T} = Matrix{T}(Q) +Array(Q::AbstractQ) = Matrix(Q) +convert(::Type{T}, Q::AbstractQ) where {T<:Array} = T(Q) +convert(::Type{T}, Q::AbstractQ) where {T<:Matrix} = T(Q) +# legacy +@deprecate(convert(::Type{AbstractMatrix{T}}, Q::AbstractQ) where {T}, + convert(LinearAlgebra.AbstractQ{T}, Q)) + +function size(Q::AbstractQ, dim::Integer) + if dim < 1 + throw(BoundsError()) + elseif dim <= 2 # && 1 <= dim + return size(Q)[dim] + else # 2 < dim + return 1 + end +end +size(adjQ::AdjointQ) = reverse(size(adjQ.Q)) + +# pseudo-array behaviour, required for indexing with `begin` or `end` +axes(Q::AbstractQ) = map(Base.oneto, size(Q)) +axes(Q::AbstractQ, d::Integer) = d in (1, 2) ? axes(Q)[d] : Base.OneTo(1) + +copymutable(Q::AbstractQ{T}) where {T} = lmul!(Q, Matrix{T}(I, size(Q))) +copy(Q::AbstractQ) = copymutable(Q) + +# getindex +@inline function getindex(Q::AbstractQ, inds...) + @boundscheck Base.checkbounds_indices(Bool, axes(Q), inds) || Base.throw_boundserror(Q, inds) + return _getindex(Q, inds...) +end +@inline getindex(Q::AbstractQ, ::Colon) = copymutable(Q)[:] +@inline getindex(Q::AbstractQ, ::Colon, ::Colon) = copy(Q) + +@inline _getindex(Q::AbstractQ, inds...) = @inbounds copymutable(Q)[inds...] +@inline function _getindex(Q::AbstractQ, ::Colon, J::AbstractVector{<:Integer}) + Y = zeros(eltype(Q), size(Q, 2), length(J)) + @inbounds for (i,j) in enumerate(J) + Y[j,i] = oneunit(eltype(Q)) + end + lmul!(Q, Y) +end +@inline _getindex(Q::AbstractQ, I::AbstractVector{Int}, J::AbstractVector{Int}) = @inbounds Q[:,J][I,:] +@inline function _getindex(Q::AbstractQ, ::Colon, j::Int) + y = zeros(eltype(Q), size(Q, 2)) + y[j] = oneunit(eltype(Q)) + lmul!(Q, y) +end +@inline _getindex(Q::AbstractQ, i::Int, j::Int) = @inbounds Q[:,j][i] + +# needed because AbstractQ does not subtype AbstractMatrix +qr(Q::AbstractQ{T}, arg...; kwargs...) where {T} = qr!(Matrix{_qreltype(T)}(Q), arg...; kwargs...) +lq(Q::AbstractQ{T}, arg...; kwargs...) where {T} = lq!(Matrix{lq_eltype(T)}(Q), arg...; kwargs...) +hessenberg(Q::AbstractQ{T}) where {T} = hessenberg!(Matrix{eigtype(T)}(Q)) + +# needed when used interchangeably with AbstractMatrix (analogous to views of ranges) +view(A::AbstractQ, I...) = getindex(A, I...) + +# specialization avoiding the fallback using slow `getindex` +function copyto!(dest::AbstractMatrix, src::AbstractQ) + copyto!(dest, I) + lmul!(src, dest) +end +# needed to resolve method ambiguities +function copyto!(dest::PermutedDimsArray{T,2,perm}, src::AbstractQ) where {T,perm} + if perm == (1, 2) + copyto!(parent(dest), src) + else + @assert perm == (2, 1) # there are no other permutations of two indices + if T <: Real + copyto!(parent(dest), I) + lmul!(src', parent(dest)) + else + # LAPACK does not offer inplace lmul!(transpose(Q), B) for complex Q + tmp = similar(parent(dest)) + copyto!(tmp, I) + rmul!(tmp, src) + permutedims!(parent(dest), tmp, (2, 1)) + end + end + return dest +end + +function show(io::IO, ::MIME{Symbol("text/plain")}, Q::AbstractQ) + print(io, Base.dims2string(size(Q)), ' ', summary(Q)) +end + +# multiplication +(*)(Q::AbstractQ, J::UniformScaling) = Q*J.λ +function (*)(Q::AbstractQ, b::Number) + T = promote_type(eltype(Q), typeof(b)) + lmul!(convert(AbstractQ{T}, Q), Matrix{T}(b*I, size(Q))) +end +function (*)(A::AbstractQ, B::AbstractVecOrMat) + T = promote_type(eltype(A), eltype(B)) + lmul!(convert(AbstractQ{T}, A), copy_similar(B, T)) +end + +(*)(J::UniformScaling, Q::AbstractQ) = J.λ*Q +function (*)(a::Number, Q::AbstractQ) + T = promote_type(typeof(a), eltype(Q)) + rmul!(Matrix{T}(a*I, size(Q)), convert(AbstractQ{T}, Q)) +end +*(a::AbstractVector, Q::AbstractQ) = reshape(a, length(a), 1) * Q +function (*)(A::AbstractMatrix, Q::AbstractQ) + T = promote_type(eltype(A), eltype(Q)) + return rmul!(copy_similar(A, T), convert(AbstractQ{T}, Q)) +end +(*)(u::AdjointAbsVec, Q::AbstractQ) = (Q'u')' + +# AbstractQ * Triangular +lmul!(Q::AbstractQ, B::AbstractTriangular) = lmul!(Q, full!(B)) +rmul!(A::AbstractTriangular, Q::AbstractQ) = rmul!(full!(A), Q) + +### Q*Q (including adjoints) +*(Q::AbstractQ, P::AbstractQ) = Q * (P*I) + +### mul! +function mul!(C::AbstractVecOrMat{T}, Q::AbstractQ{T}, B::Union{AbstractVecOrMat{T},AbstractQ{T}}) where {T} + require_one_based_indexing(C, B) + mB = size(B, 1) + mC = size(C, 1) + if mB < mC + inds = CartesianIndices(axes(B)) + copyto!(view(C, inds), B) + C[CartesianIndices((mB+1:mC, axes(C, 2)))] .= zero(T) + return lmul!(Q, C) + else + return lmul!(Q, copyto!(C, B)) + end +end +mul!(C::AbstractVecOrMat{T}, A::AbstractVecOrMat{T}, Q::AbstractQ{T}) where {T} = rmul!(copyto!(C, A), Q) +mul!(C::AbstractVecOrMat{T}, adjQ::AdjointQ{T}, B::AbstractVecOrMat{T}) where {T} = lmul!(adjQ, copyto!(C, B)) +mul!(C::AbstractVecOrMat{T}, A::AbstractVecOrMat{T}, adjQ::AdjointQ{T}) where {T} = rmul!(copyto!(C, A), adjQ) + +### division +\(Q::AbstractQ, A::AbstractVecOrMat) = Q'*A +/(A::AbstractVecOrMat, Q::AbstractQ) = A*Q' +ldiv!(Q::AbstractQ, A::AbstractVecOrMat) = lmul!(Q', A) +ldiv!(C::AbstractVecOrMat, Q::AbstractQ, A::AbstractVecOrMat) = mul!(C, Q', A) +rdiv!(A::AbstractVecOrMat, Q::AbstractQ) = rmul!(A, Q') + +logabsdet(Q::AbstractQ) = (d = det(Q); return log(abs(d)), sign(d)) +function logdet(A::AbstractQ) + d, s = logabsdet(A) + return d + log(s) +end + +########################################################### +################ Q from QR decompositions ################# +########################################################### + +""" + QRPackedQ <: LinearAlgebra.AbstractQ + +The orthogonal/unitary ``Q`` matrix of a QR factorization stored in [`QR`](@ref) or +[`QRPivoted`](@ref) format. +""" +struct QRPackedQ{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: AbstractQ{T} + factors::S + τ::C + + function QRPackedQ{T,S,C}(factors, τ) where {T,S<:AbstractMatrix{T},C<:AbstractVector{T}} + require_one_based_indexing(factors, τ) + new{T,S,C}(factors, τ) + end +end +QRPackedQ(factors::AbstractMatrix{T}, τ::AbstractVector{T}) where {T} = + QRPackedQ{T,typeof(factors),typeof(τ)}(factors, τ) +QRPackedQ{T}(factors::AbstractMatrix, τ::AbstractVector) where {T} = + QRPackedQ(convert(AbstractMatrix{T}, factors), convert(AbstractVector{T}, τ)) +# backwards-compatible constructors (remove with Julia 2.0) +@deprecate(QRPackedQ{T,S}(factors::AbstractMatrix{T}, τ::AbstractVector{T}) where {T,S}, + QRPackedQ{T,S,typeof(τ)}(factors, τ), false) + +""" + QRCompactWYQ <: LinearAlgebra.AbstractQ + +The orthogonal/unitary ``Q`` matrix of a QR factorization stored in [`QRCompactWY`](@ref) +format. +""" +struct QRCompactWYQ{S, M<:AbstractMatrix{S}, C<:AbstractMatrix{S}} <: AbstractQ{S} + factors::M + T::C + + function QRCompactWYQ{S,M,C}(factors, T) where {S,M<:AbstractMatrix{S},C<:AbstractMatrix{S}} + require_one_based_indexing(factors, T) + new{S,M,C}(factors, T) + end +end +QRCompactWYQ(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S} = + QRCompactWYQ{S,typeof(factors),typeof(T)}(factors, T) +QRCompactWYQ{S}(factors::AbstractMatrix, T::AbstractMatrix) where {S} = + QRCompactWYQ(convert(AbstractMatrix{S}, factors), convert(AbstractMatrix{S}, T)) +# backwards-compatible constructors (remove with Julia 2.0) +@deprecate(QRCompactWYQ{S,M}(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S,M}, + QRCompactWYQ{S,M,typeof(T)}(factors, T), false) + +QRPackedQ{T}(Q::QRPackedQ) where {T} = QRPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(Vector{T}, Q.τ)) +QRCompactWYQ{S}(Q::QRCompactWYQ) where {S} = QRCompactWYQ(convert(AbstractMatrix{S}, Q.factors), convert(AbstractMatrix{S}, Q.T)) + +AbstractQ{S}(Q::QRPackedQ) where {S} = QRPackedQ{S}(Q) +AbstractQ{S}(Q::QRCompactWYQ) where {S} = QRCompactWYQ{S}(Q) +# override generic square fallback +Matrix{T}(Q::Union{QRCompactWYQ{S},QRPackedQ{S}}) where {T,S} = + convert(Matrix{T}, lmul!(Q, Matrix{S}(I, size(Q, 1), min(size(Q.factors)...)))) +Matrix(Q::Union{QRCompactWYQ{S},QRPackedQ{S}}) where {S} = Matrix{S}(Q) + +convert(::Type{AbstractQ{T}}, Q::QRPackedQ) where {T} = QRPackedQ{T}(Q) +convert(::Type{AbstractQ{T}}, Q::QRCompactWYQ) where {T} = QRCompactWYQ{T}(Q) + +size(Q::Union{QRCompactWYQ,QRPackedQ}, dim::Integer) = + size(Q.factors, dim == 2 ? 1 : dim) +size(Q::Union{QRCompactWYQ,QRPackedQ}) = (n = size(Q.factors, 1); (n, n)) + +## Multiplication +### QB +lmul!(A::QRCompactWYQ{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = + LAPACK.gemqrt!('L', 'N', A.factors, A.T, B) +lmul!(A::QRPackedQ{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = + LAPACK.ormqr!('L', 'N', A.factors, A.τ, B) +function lmul!(A::QRPackedQ, B::AbstractVecOrMat) + require_one_based_indexing(B) + mA, nA = size(A.factors) + mB, nB = size(B,1), size(B,2) + if mA != mB + throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)")) + end + Afactors = A.factors + @inbounds begin + for k = min(mA,nA):-1:1 + for j = 1:nB + vBj = B[k,j] + for i = k+1:mB + vBj += conj(Afactors[i,k])*B[i,j] + end + vBj = A.τ[k]*vBj + B[k,j] -= vBj + for i = k+1:mB + B[i,j] -= Afactors[i,k]*vBj + end + end + end + end + B +end +lmul!(Q::QRPackedQ, B::AbstractTriangular) = lmul!(Q, full!(B)) # disambiguation + +### QcB +lmul!(adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasReal} = + (Q = adjQ.Q; LAPACK.gemqrt!('L', 'T', Q.factors, Q.T, B)) +lmul!(adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = + (Q = adjQ.Q; LAPACK.gemqrt!('L', 'C', Q.factors, Q.T, B)) +lmul!(adjQ::AdjointQ{<:Any,<:QRPackedQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasReal} = + (Q = adjQ.Q; LAPACK.ormqr!('L', 'T', Q.factors, Q.τ, B)) +lmul!(adjQ::AdjointQ{<:Any,<:QRPackedQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = + (Q = adjQ.Q; LAPACK.ormqr!('L', 'C', Q.factors, Q.τ, B)) +function lmul!(adjA::AdjointQ{<:Any,<:QRPackedQ}, B::AbstractVecOrMat) + require_one_based_indexing(B) + A = adjA.Q + mA, nA = size(A.factors) + mB, nB = size(B,1), size(B,2) + if mA != mB + throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)")) + end + Afactors = A.factors + @inbounds begin + for k = 1:min(mA,nA) + for j = 1:nB + vBj = B[k,j] + for i = k+1:mB + vBj += conj(Afactors[i,k])*B[i,j] + end + vBj = conj(A.τ[k])*vBj + B[k,j] -= vBj + for i = k+1:mB + B[i,j] -= Afactors[i,k]*vBj + end + end + end + end + B +end +lmul!(Q::AdjointQ{<:Any,<:QRPackedQ}, B::AbstractTriangular) = lmul!(Q, full!(B)) # disambiguation + +### AQ +rmul!(A::StridedVecOrMat{T}, B::QRCompactWYQ{T,<:StridedMatrix}) where {T<:BlasFloat} = + LAPACK.gemqrt!('R', 'N', B.factors, B.T, A) +rmul!(A::StridedVecOrMat{T}, B::QRPackedQ{T,<:StridedMatrix}) where {T<:BlasFloat} = + LAPACK.ormqr!('R', 'N', B.factors, B.τ, A) +function rmul!(A::AbstractMatrix, Q::QRPackedQ) + require_one_based_indexing(A) + mQ, nQ = size(Q.factors) + mA, nA = size(A,1), size(A,2) + if nA != mQ + throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)")) + end + Qfactors = Q.factors + @inbounds begin + for k = 1:min(mQ,nQ) + for i = 1:mA + vAi = A[i,k] + for j = k+1:mQ + vAi += A[i,j]*Qfactors[j,k] + end + vAi = vAi*Q.τ[k] + A[i,k] -= vAi + for j = k+1:nA + A[i,j] -= vAi*conj(Qfactors[j,k]) + end + end + end + end + A +end +rmul!(A::AbstractTriangular, Q::QRPackedQ) = rmul!(full!(A), Q) # disambiguation + +### AQc +rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T}}) where {T<:BlasReal} = + (Q = adjQ.Q; LAPACK.gemqrt!('R', 'T', Q.factors, Q.T, A)) +rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T}}) where {T<:BlasComplex} = + (Q = adjQ.Q; LAPACK.gemqrt!('R', 'C', Q.factors, Q.T, A)) +rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRPackedQ{T}}) where {T<:BlasReal} = + (Q = adjQ.Q; LAPACK.ormqr!('R', 'T', Q.factors, Q.τ, A)) +rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRPackedQ{T}}) where {T<:BlasComplex} = + (Q = adjQ.Q; LAPACK.ormqr!('R', 'C', Q.factors, Q.τ, A)) +function rmul!(A::AbstractMatrix, adjQ::AdjointQ{<:Any,<:QRPackedQ}) + require_one_based_indexing(A) + Q = adjQ.Q + mQ, nQ = size(Q.factors) + mA, nA = size(A,1), size(A,2) + if nA != mQ + throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)")) + end + Qfactors = Q.factors + @inbounds begin + for k = min(mQ,nQ):-1:1 + for i = 1:mA + vAi = A[i,k] + for j = k+1:mQ + vAi += A[i,j]*Qfactors[j,k] + end + vAi = vAi*conj(Q.τ[k]) + A[i,k] -= vAi + for j = k+1:nA + A[i,j] -= vAi*conj(Qfactors[j,k]) + end + end + end + end + A +end +rmul!(A::AbstractTriangular, Q::AdjointQ{<:Any,<:QRPackedQ}) = rmul!(full!(A), Q) # disambiguation + +det(Q::QRPackedQ) = _det_tau(Q.τ) +det(Q::QRCompactWYQ) = + prod(i -> _det_tau(_diagview(Q.T[:, i:min(i + size(Q.T, 1), size(Q.T, 2))])), + 1:size(Q.T, 1):size(Q.T, 2)) + +_diagview(A) = @view A[diagind(A)] + +# Compute `det` from the number of Householder reflections. Handle +# the case `Q.τ` contains zeros. +_det_tau(τs::AbstractVector{<:Real}) = + isodd(count(!iszero, τs)) ? -one(eltype(τs)) : one(eltype(τs)) + +# In complex case, we need to compute the non-unit eigenvalue `λ = 1 - c*τ` +# (where `c = v'v`) of each Householder reflector. As we know that the +# reflector must have the determinant of 1, it must satisfy `abs2(λ) == 1`. +# Combining this with the constraint `c > 0`, it turns out that the eigenvalue +# (hence the determinant) can be computed as `λ = -sign(τ)^2`. +# See: https://github.com/JuliaLang/julia/pull/32887#issuecomment-521935716 +_det_tau(τs) = prod(τ -> iszero(τ) ? one(τ) : -sign(τ)^2, τs) + +########################################################### +######## Q from Hessenberg decomposition ################## +########################################################### + +""" + HessenbergQ <: AbstractQ + +Given a [`Hessenberg`](@ref) factorization object `F`, `F.Q` returns +a `HessenbergQ` object, which is an implicit representation of the unitary +matrix `Q` in the Hessenberg factorization `QHQ'` represented by `F`. +This `F.Q` object can be efficiently multiplied by matrices or vectors, +and can be converted to an ordinary matrix type with `Matrix(F.Q)`. +""" +struct HessenbergQ{T,S<:AbstractMatrix,W<:AbstractVector,sym} <: AbstractQ{T} + uplo::Char + factors::S + τ::W + function HessenbergQ{T,S,W,sym}(uplo::AbstractChar, factors, τ) where {T,S<:AbstractMatrix,W<:AbstractVector,sym} + new(uplo, factors, τ) + end +end +HessenbergQ(F::Hessenberg{<:Any,<:UpperHessenberg,S,W}) where {S,W} = HessenbergQ{eltype(F.factors),S,W,false}(F.uplo, F.factors, F.τ) +HessenbergQ(F::Hessenberg{<:Any,<:SymTridiagonal,S,W}) where {S,W} = HessenbergQ{eltype(F.factors),S,W,true}(F.uplo, F.factors, F.τ) + +size(Q::HessenbergQ, dim::Integer) = size(getfield(Q, :factors), dim == 2 ? 1 : dim) +size(Q::HessenbergQ) = size(Q, 1), size(Q, 2) + +# HessenbergQ from LAPACK/BLAS (as opposed to Julia libraries like GenericLinearAlgebra) +const BlasHessenbergQ{T,sym} = HessenbergQ{T,<:StridedMatrix{T},<:StridedVector{T},sym} where {T<:BlasFloat,sym} + +## reconstruct the original matrix +Matrix{T}(Q::BlasHessenbergQ{<:Any,false}) where {T} = convert(Matrix{T}, LAPACK.orghr!(1, size(Q.factors, 1), copy(Q.factors), Q.τ)) +Matrix{T}(Q::BlasHessenbergQ{<:Any,true}) where {T} = convert(Matrix{T}, LAPACK.orgtr!(Q.uplo, copy(Q.factors), Q.τ)) + +lmul!(Q::BlasHessenbergQ{T,false}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = + LAPACK.ormhr!('L', 'N', 1, size(Q.factors, 1), Q.factors, Q.τ, X) +rmul!(X::StridedVecOrMat{T}, Q::BlasHessenbergQ{T,false}) where {T<:BlasFloat} = + LAPACK.ormhr!('R', 'N', 1, size(Q.factors, 1), Q.factors, Q.τ, X) +lmul!(adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,false}}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = + (Q = adjQ.Q; LAPACK.ormhr!('L', ifelse(T<:Real, 'T', 'C'), 1, size(Q.factors, 1), Q.factors, Q.τ, X)) +rmul!(X::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,false}}) where {T<:BlasFloat} = + (Q = adjQ.Q; LAPACK.ormhr!('R', ifelse(T<:Real, 'T', 'C'), 1, size(Q.factors, 1), Q.factors, Q.τ, X)) + +lmul!(Q::BlasHessenbergQ{T,true}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = + LAPACK.ormtr!('L', Q.uplo, 'N', Q.factors, Q.τ, X) +rmul!(X::StridedVecOrMat{T}, Q::BlasHessenbergQ{T,true}) where {T<:BlasFloat} = + LAPACK.ormtr!('R', Q.uplo, 'N', Q.factors, Q.τ, X) +lmul!(adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,true}}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = + (Q = adjQ.Q; LAPACK.ormtr!('L', Q.uplo, ifelse(T<:Real, 'T', 'C'), Q.factors, Q.τ, X)) +rmul!(X::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,true}}) where {T<:BlasFloat} = + (Q = adjQ.Q; LAPACK.ormtr!('R', Q.uplo, ifelse(T<:Real, 'T', 'C'), Q.factors, Q.τ, X)) + +lmul!(Q::HessenbergQ{T}, X::Adjoint{T,<:StridedVecOrMat{T}}) where {T} = rmul!(X', Q')' +rmul!(X::Adjoint{T,<:StridedVecOrMat{T}}, Q::HessenbergQ{T}) where {T} = lmul!(Q', X')' +lmul!(adjQ::AdjointQ{<:Any,<:HessenbergQ{T}}, X::Adjoint{T,<:StridedVecOrMat{T}}) where {T} = rmul!(X', adjQ')' +rmul!(X::Adjoint{T,<:StridedVecOrMat{T}}, adjQ::AdjointQ{<:Any,<:HessenbergQ{T}}) where {T} = lmul!(adjQ', X')' + +# flexible left-multiplication (and adjoint right-multiplication) +function (*)(Q::Union{QRPackedQ,QRCompactWYQ,HessenbergQ}, b::AbstractVector) + T = promote_type(eltype(Q), eltype(b)) + if size(Q.factors, 1) == length(b) + bnew = copy_similar(b, T) + elseif size(Q.factors, 2) == length(b) + bnew = [b; zeros(T, size(Q.factors, 1) - length(b))] + else + throw(DimensionMismatch("vector must have length either $(size(Q.factors, 1)) or $(size(Q.factors, 2))")) + end + lmul!(convert(AbstractQ{T}, Q), bnew) +end +function (*)(Q::Union{QRPackedQ,QRCompactWYQ,HessenbergQ}, B::AbstractMatrix) + T = promote_type(eltype(Q), eltype(B)) + if size(Q.factors, 1) == size(B, 1) + Bnew = copy_similar(B, T) + elseif size(Q.factors, 2) == size(B, 1) + Bnew = [B; zeros(T, size(Q.factors, 1) - size(B,1), size(B, 2))] + else + throw(DimensionMismatch("first dimension of matrix must have size either $(size(Q.factors, 1)) or $(size(Q.factors, 2))")) + end + lmul!(convert(AbstractQ{T}, Q), Bnew) +end +function (*)(A::AbstractMatrix, adjQ::AdjointQ{<:Any,<:Union{QRPackedQ,QRCompactWYQ,HessenbergQ}}) + Q = adjQ.Q + T = promote_type(eltype(A), eltype(adjQ)) + adjQQ = convert(AbstractQ{T}, adjQ) + if size(A, 2) == size(Q.factors, 1) + AA = copy_similar(A, T) + return rmul!(AA, adjQQ) + elseif size(A, 2) == size(Q.factors, 2) + return rmul!([A zeros(T, size(A, 1), size(Q.factors, 1) - size(Q.factors, 2))], adjQQ) + else + throw(DimensionMismatch("matrix A has dimensions $(size(A)) but Q-matrix B has dimensions $(size(adjQ))")) + end +end +(*)(u::AdjointAbsVec, Q::AdjointQ{<:Any,<:Union{QRPackedQ,QRCompactWYQ,HessenbergQ}}) = (Q'u')' + +det(Q::HessenbergQ) = _det_tau(Q.τ) + +########################################################### +################ Q from LQ decomposition ################## +########################################################### + +struct LQPackedQ{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: AbstractQ{T} + factors::S + τ::C +end + +LQPackedQ{T}(Q::LQPackedQ) where {T} = LQPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(Vector{T}, Q.τ)) +@deprecate(AbstractMatrix{T}(Q::LQPackedQ) where {T}, + convert(AbstractQ{T}, Q), + false) +Matrix{T}(A::LQPackedQ) where {T} = convert(Matrix{T}, LAPACK.orglq!(copy(A.factors), A.τ)) +convert(::Type{AbstractQ{T}}, Q::LQPackedQ) where {T} = LQPackedQ{T}(Q) + +# size(Q::LQPackedQ) yields the shape of Q's square form +size(Q::LQPackedQ) = (n = size(Q.factors, 2); return n, n) + +## Multiplication +### QB / QcB +lmul!(A::LQPackedQ{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.ormlq!('L','N',A.factors,A.τ,B) +lmul!(adjA::AdjointQ{<:Any,<:LQPackedQ{T}}, B::StridedVecOrMat{T}) where {T<:BlasReal} = + (A = adjA.Q; LAPACK.ormlq!('L', 'T', A.factors, A.τ, B)) +lmul!(adjA::AdjointQ{<:Any,<:LQPackedQ{T}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = + (A = adjA.Q; LAPACK.ormlq!('L', 'C', A.factors, A.τ, B)) + +function (*)(adjA::AdjointQ{<:Any,<:LQPackedQ}, B::AbstractVector) + A = adjA.Q + T = promote_type(eltype(A), eltype(B)) + if length(B) == size(A.factors, 2) + C = copy_similar(B, T) + elseif length(B) == size(A.factors, 1) + C = [B; zeros(T, size(A.factors, 2) - size(A.factors, 1), size(B, 2))] + else + throw(DimensionMismatch("length of B, $(length(B)), must equal one of the dimensions of A, $(size(A))")) + end + lmul!(convert(AbstractQ{T}, adjA), C) +end +function (*)(adjA::AdjointQ{<:Any,<:LQPackedQ}, B::AbstractMatrix) + A = adjA.Q + T = promote_type(eltype(A), eltype(B)) + if size(B,1) == size(A.factors,2) + C = copy_similar(B, T) + elseif size(B,1) == size(A.factors,1) + C = [B; zeros(T, size(A.factors, 2) - size(A.factors, 1), size(B, 2))] + else + throw(DimensionMismatch("first dimension of B, $(size(B,1)), must equal one of the dimensions of A, $(size(A))")) + end + lmul!(convert(AbstractQ{T}, adjA), C) +end + +# in-place right-application of LQPackedQs +# these methods require that the applied-to matrix's (A's) number of columns +# match the number of columns (nQ) of the LQPackedQ (Q) (necessary for in-place +# operation, and the underlying LAPACK routine (ormlq) treats the implicit Q +# as its (nQ-by-nQ) square form) +rmul!(A::StridedMatrix{T}, B::LQPackedQ{T}) where {T<:BlasFloat} = + LAPACK.ormlq!('R', 'N', B.factors, B.τ, A) +rmul!(A::StridedMatrix{T}, adjB::AdjointQ{<:Any,<:LQPackedQ{T}}) where {T<:BlasReal} = + (B = adjB.Q; LAPACK.ormlq!('R', 'T', B.factors, B.τ, A)) +rmul!(A::StridedMatrix{T}, adjB::AdjointQ{<:Any,<:LQPackedQ{T}}) where {T<:BlasComplex} = + (B = adjB.Q; LAPACK.ormlq!('R', 'C', B.factors, B.τ, A)) + +# out-of-place right application of LQPackedQs +# +# these methods: (1) check whether the applied-to matrix's (A's) appropriate dimension +# (columns for A_*, rows for Ac_*) matches the number of columns (nQ) of the LQPackedQ (Q), +# and if so effectively apply Q's square form to A without additional shenanigans; and +# (2) if the preceding dimensions do not match, check whether the appropriate dimension of +# A instead matches the number of rows of the matrix of which Q is a factor (i.e. +# size(Q.factors, 1)), and if so implicitly apply Q's truncated form to A by zero extending +# A as necessary for check (1) to pass (if possible) and then applying Q's square form +# +function (*)(A::AbstractVector, Q::LQPackedQ) + T = promote_type(eltype(A), eltype(Q)) + if 1 == size(Q.factors, 2) + C = copy_similar(A, T) + elseif 1 == size(Q.factors, 1) + C = zeros(T, length(A), size(Q.factors, 2)) + copyto!(C, 1, A, 1, length(A)) + else + _rightappdimmismatch("columns") + end + return rmul!(C, convert(AbstractQ{T}, Q)) +end +function (*)(A::AbstractMatrix, Q::LQPackedQ) + T = promote_type(eltype(A), eltype(Q)) + if size(A, 2) == size(Q.factors, 2) + C = copy_similar(A, T) + elseif size(A, 2) == size(Q.factors, 1) + C = zeros(T, size(A, 1), size(Q.factors, 2)) + copyto!(C, 1, A, 1, length(A)) + else + _rightappdimmismatch("columns") + end + return rmul!(C, convert(AbstractQ{T}, Q)) +end +function (*)(adjA::AdjointAbsMat, Q::LQPackedQ) + A = adjA.parent + T = promote_type(eltype(A), eltype(Q)) + if size(A, 1) == size(Q.factors, 2) + C = copy_similar(adjA, T) + elseif size(A, 1) == size(Q.factors, 1) + C = zeros(T, size(A, 2), size(Q.factors, 2)) + adjoint!(view(C, :, 1:size(A, 1)), A) + else + _rightappdimmismatch("rows") + end + return rmul!(C, convert(AbstractQ{T}, Q)) +end +(*)(u::AdjointAbsVec, Q::LQPackedQ) = (Q'u')' + +_rightappdimmismatch(rowsorcols) = + throw(DimensionMismatch(string("the number of $(rowsorcols) of the matrix on the left ", + "must match either (1) the number of columns of the (LQPackedQ) matrix on the right ", + "or (2) the number of rows of that (LQPackedQ) matrix's internal representation ", + "(the factorization's originating matrix's number of rows)"))) + +# In LQ factorization, `Q` is expressed as the product of the adjoint of the +# reflectors. Thus, `det` has to be conjugated. +det(Q::LQPackedQ) = conj(_det_tau(Q.τ)) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 03c5a2bbdeba4..9bb7219965d5d 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -281,15 +281,11 @@ end rmul!(A::AbstractMatrix, D::Diagonal) = @inline mul!(A, A, D) lmul!(D::Diagonal, B::AbstractVecOrMat) = @inline mul!(B, D, B) -function *(A::AdjOrTransAbsMat, D::Diagonal) +function (*)(A::AdjOrTransAbsMat, D::Diagonal) Ac = copy_similar(A, promote_op(*, eltype(A), eltype(D.diag))) rmul!(Ac, D) end - -*(D::Diagonal, adjQ::Adjoint{<:Any,<:Union{QRCompactWYQ,QRPackedQ}}) = - rmul!(Array{promote_type(eltype(D), eltype(adjQ))}(D), adjQ) - -function *(D::Diagonal, A::AdjOrTransAbsMat) +function (*)(D::Diagonal, A::AdjOrTransAbsMat) Ac = copy_similar(A, promote_op(*, eltype(A), eltype(D.diag))) lmul!(D, Ac) end diff --git a/stdlib/LinearAlgebra/src/hessenberg.jl b/stdlib/LinearAlgebra/src/hessenberg.jl index 44cd0873c1ee5..ef7570731197a 100644 --- a/stdlib/LinearAlgebra/src/hessenberg.jl +++ b/stdlib/LinearAlgebra/src/hessenberg.jl @@ -167,29 +167,6 @@ function \(U::UnitUpperTriangular, H::UpperHessenberg) UpperHessenberg(HH) end -function *(H::UpperHessenberg, B::Bidiagonal) - TS = promote_op(matprod, eltype(H), eltype(B)) - A = mul!(similar(H, TS, size(H)), H, B) - return B.uplo == 'U' ? UpperHessenberg(A) : A -end -function *(B::Bidiagonal, H::UpperHessenberg) - TS = promote_op(matprod, eltype(B), eltype(H)) - A = mul!(similar(H, TS, size(H)), B, H) - return B.uplo == 'U' ? UpperHessenberg(A) : A -end - -function /(H::UpperHessenberg, B::Bidiagonal) - T = typeof(oneunit(eltype(H))/oneunit(eltype(B))) - A = _rdiv!(similar(H, T, size(H)), H, B) - return B.uplo == 'U' ? UpperHessenberg(A) : A -end - -function \(B::Bidiagonal, H::UpperHessenberg) - T = typeof(oneunit(eltype(B))\oneunit(eltype(H))) - A = ldiv!(similar(H, T, size(H)), B, H) - return B.uplo == 'U' ? UpperHessenberg(A) : A -end - # Solving (H+µI)x = b: we can do this in O(m²) time and O(m) memory # (in-place in x) by the RQ algorithm from: # @@ -482,10 +459,7 @@ julia> A = [4. 9. 7.; 4. 4. 1.; 4. 3. 2.] julia> F = hessenberg(A) Hessenberg{Float64, UpperHessenberg{Float64, Matrix{Float64}}, Matrix{Float64}, Vector{Float64}, Bool} Q factor: -3×3 LinearAlgebra.HessenbergQ{Float64, Matrix{Float64}, Vector{Float64}, false}: - 1.0 0.0 0.0 - 0.0 -0.707107 -0.707107 - 0.0 -0.707107 0.707107 +3×3 LinearAlgebra.HessenbergQ{Float64, Matrix{Float64}, Vector{Float64}, false} H factor: 3×3 UpperHessenberg{Float64, Matrix{Float64}}: 4.0 -11.3137 -1.41421 @@ -518,43 +492,14 @@ function show(io::IO, mime::MIME"text/plain", F::Hessenberg) show(io, mime, F.H) end -""" - HessenbergQ <: AbstractQ - -Given a [`Hessenberg`](@ref) factorization object `F`, `F.Q` returns -a `HessenbergQ` object, which is an implicit representation of the unitary -matrix `Q` in the Hessenberg factorization `QHQ'` represented by `F`. -This `F.Q` object can be efficiently multiplied by matrices or vectors, -and can be converted to an ordinary matrix type with `Matrix(F.Q)`. -""" -struct HessenbergQ{T,S<:AbstractMatrix,W<:AbstractVector,sym} <: AbstractQ{T} - uplo::Char - factors::S - τ::W - function HessenbergQ{T,S,W,sym}(uplo::AbstractChar, factors, τ) where {T,S<:AbstractMatrix,W<:AbstractVector,sym} - new(uplo, factors, τ) - end -end -HessenbergQ(F::Hessenberg{<:Any,<:UpperHessenberg,S,W}) where {S,W} = HessenbergQ{eltype(F.factors),S,W,false}(F.uplo, F.factors, F.τ) -HessenbergQ(F::Hessenberg{<:Any,<:SymTridiagonal,S,W}) where {S,W} = HessenbergQ{eltype(F.factors),S,W,true}(F.uplo, F.factors, F.τ) - function getproperty(F::Hessenberg, d::Symbol) d === :Q && return HessenbergQ(F) return getfield(F, d) end -size(Q::HessenbergQ, dim::Integer) = size(getfield(Q, :factors), dim == 2 ? 1 : dim) -size(Q::HessenbergQ) = size(Q, 1), size(Q, 2) - Base.propertynames(F::Hessenberg, private::Bool=false) = (:Q, :H, :μ, (private ? (:τ, :factors, :uplo) : ())...) -# HessenbergQ from LAPACK/BLAS (as opposed to Julia libraries like GenericLinearAlgebra) -const BlasHessenbergQ{T,sym} = HessenbergQ{T,<:StridedMatrix{T},<:StridedVector{T},sym} where {T<:BlasFloat,sym} - -## reconstruct the original matrix -Matrix{T}(Q::BlasHessenbergQ{<:Any,false}) where {T} = convert(Matrix{T}, LAPACK.orghr!(1, size(Q.factors, 1), copy(Q.factors), Q.τ)) -Matrix{T}(Q::BlasHessenbergQ{<:Any,true}) where {T} = convert(Matrix{T}, LAPACK.orgtr!(Q.uplo, copy(Q.factors), Q.τ)) AbstractArray(F::Hessenberg) = AbstractMatrix(F) Matrix(F::Hessenberg) = Array(AbstractArray(F)) Array(F::Hessenberg) = Matrix(F) @@ -574,31 +519,6 @@ function AbstractMatrix(F::Hessenberg) end end -# adjoint(Q::HessenbergQ{<:Real}) - -lmul!(Q::BlasHessenbergQ{T,false}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = - LAPACK.ormhr!('L', 'N', 1, size(Q.factors, 1), Q.factors, Q.τ, X) -rmul!(X::StridedVecOrMat{T}, Q::BlasHessenbergQ{T,false}) where {T<:BlasFloat} = - LAPACK.ormhr!('R', 'N', 1, size(Q.factors, 1), Q.factors, Q.τ, X) -lmul!(adjQ::Adjoint{<:Any,<:BlasHessenbergQ{T,false}}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = - (Q = adjQ.parent; LAPACK.ormhr!('L', ifelse(T<:Real, 'T', 'C'), 1, size(Q.factors, 1), Q.factors, Q.τ, X)) -rmul!(X::StridedVecOrMat{T}, adjQ::Adjoint{<:Any,<:BlasHessenbergQ{T,false}}) where {T<:BlasFloat} = - (Q = adjQ.parent; LAPACK.ormhr!('R', ifelse(T<:Real, 'T', 'C'), 1, size(Q.factors, 1), Q.factors, Q.τ, X)) - -lmul!(Q::BlasHessenbergQ{T,true}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = - LAPACK.ormtr!('L', Q.uplo, 'N', Q.factors, Q.τ, X) -rmul!(X::StridedVecOrMat{T}, Q::BlasHessenbergQ{T,true}) where {T<:BlasFloat} = - LAPACK.ormtr!('R', Q.uplo, 'N', Q.factors, Q.τ, X) -lmul!(adjQ::Adjoint{<:Any,<:BlasHessenbergQ{T,true}}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = - (Q = adjQ.parent; LAPACK.ormtr!('L', Q.uplo, ifelse(T<:Real, 'T', 'C'), Q.factors, Q.τ, X)) -rmul!(X::StridedVecOrMat{T}, adjQ::Adjoint{<:Any,<:BlasHessenbergQ{T,true}}) where {T<:BlasFloat} = - (Q = adjQ.parent; LAPACK.ormtr!('R', Q.uplo, ifelse(T<:Real, 'T', 'C'), Q.factors, Q.τ, X)) - -lmul!(Q::HessenbergQ{T}, X::Adjoint{T,<:StridedVecOrMat{T}}) where {T} = rmul!(X', Q')' -rmul!(X::Adjoint{T,<:StridedVecOrMat{T}}, Q::HessenbergQ{T}) where {T} = lmul!(Q', X')' -lmul!(adjQ::Adjoint{<:Any,<:HessenbergQ{T}}, X::Adjoint{T,<:StridedVecOrMat{T}}) where {T} = rmul!(X', adjQ')' -rmul!(X::Adjoint{T,<:StridedVecOrMat{T}}, adjQ::Adjoint{<:Any,<:HessenbergQ{T}}) where {T} = lmul!(adjQ', X')' - # multiply x by the entries of M in the upper-k triangle, which contains # the entries of the upper-Hessenberg matrix H for k=-1 function rmul_triu!(M::AbstractMatrix, x, k::Integer=0) diff --git a/stdlib/LinearAlgebra/src/lq.jl b/stdlib/LinearAlgebra/src/lq.jl index a162333be339a..33d794906c7e6 100644 --- a/stdlib/LinearAlgebra/src/lq.jl +++ b/stdlib/LinearAlgebra/src/lq.jl @@ -28,9 +28,7 @@ L factor: -8.60233 0.0 4.41741 -0.697486 Q factor: -2×2 LinearAlgebra.LQPackedQ{Float64, Matrix{Float64}, Vector{Float64}}: - -0.581238 -0.813733 - -0.813733 0.581238 +2×2 LinearAlgebra.LQPackedQ{Float64, Matrix{Float64}, Vector{Float64}} julia> S.L * S.Q 2×2 Matrix{Float64}: @@ -65,12 +63,6 @@ Base.iterate(S::LQ) = (S.L, Val(:Q)) Base.iterate(S::LQ, ::Val{:Q}) = (S.Q, Val(:done)) Base.iterate(S::LQ, ::Val{:done}) = nothing -struct LQPackedQ{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: AbstractMatrix{T} - factors::S - τ::C -end - - """ lq!(A) -> LQ @@ -78,6 +70,7 @@ Compute the [`LQ`](@ref) factorization of `A`, using the input matrix as a workspace. See also [`lq`](@ref). """ lq!(A::StridedMatrix{<:BlasFloat}) = LQ(LAPACK.gelqf!(A)...) + """ lq(A) -> S::LQ @@ -105,9 +98,7 @@ L factor: -8.60233 0.0 4.41741 -0.697486 Q factor: -2×2 LinearAlgebra.LQPackedQ{Float64, Matrix{Float64}, Vector{Float64}}: - -0.581238 -0.813733 - -0.813733 0.581238 +2×2 LinearAlgebra.LQPackedQ{Float64, Matrix{Float64}, Vector{Float64}} julia> S.L * S.Q 2×2 Matrix{Float64}: @@ -156,8 +147,8 @@ end Base.propertynames(F::LQ, private::Bool=false) = (:L, :Q, (private ? fieldnames(typeof(F)) : ())...) -getindex(A::LQPackedQ, i::Integer, j::Integer) = - lmul!(A, setindex!(zeros(eltype(A), size(A, 2)), 1, j))[i] +# getindex(A::LQPackedQ, i::Integer, j::Integer) = +# lmul!(A, setindex!(zeros(eltype(A), size(A, 2)), 1, j))[i] function show(io::IO, mime::MIME{Symbol("text/plain")}, F::LQ) summary(io, F); println(io) @@ -167,32 +158,9 @@ function show(io::IO, mime::MIME{Symbol("text/plain")}, F::LQ) show(io, mime, F.Q) end -LQPackedQ{T}(Q::LQPackedQ) where {T} = LQPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(Vector{T}, Q.τ)) -AbstractMatrix{T}(Q::LQPackedQ) where {T} = LQPackedQ{T}(Q) -Matrix{T}(A::LQPackedQ) where {T} = convert(Matrix{T}, LAPACK.orglq!(copy(A.factors),A.τ)) -Matrix(A::LQPackedQ{T}) where {T} = Matrix{T}(A) -Array{T}(A::LQPackedQ{T}) where {T} = Matrix{T}(A) -Array(A::LQPackedQ) = Matrix(A) - size(F::LQ, dim::Integer) = size(getfield(F, :factors), dim) size(F::LQ) = size(getfield(F, :factors)) -# size(Q::LQPackedQ) yields the shape of Q's square form -function size(Q::LQPackedQ) - n = size(Q.factors, 2) - return n, n -end -function size(Q::LQPackedQ, dim::Integer) - if dim < 1 - throw(BoundsError()) - elseif dim <= 2 # && 1 <= dim - return size(Q.factors, 2) - else # 2 < dim - return 1 - end -end - - ## Multiplication by LQ function lmul!(A::LQ, B::AbstractVecOrMat) lmul!(LowerTriangular(A.L), view(lmul!(A.Q, B), 1:size(A,1), axes(B,2))) @@ -203,127 +171,6 @@ function *(A::LQ{TA}, B::AbstractVecOrMat{TB}) where {TA,TB} _cut_B(lmul!(convert(Factorization{TAB}, A), copy_similar(B, TAB)), 1:size(A,1)) end -## Multiplication by Q -### QB -lmul!(A::LQPackedQ{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.ormlq!('L','N',A.factors,A.τ,B) -function (*)(A::LQPackedQ, B::StridedVecOrMat) - TAB = promote_type(eltype(A), eltype(B)) - lmul!(AbstractMatrix{TAB}(A), copymutable_oftype(B, TAB)) -end - -### QcB -lmul!(adjA::Adjoint{<:Any,<:LQPackedQ{T}}, B::StridedVecOrMat{T}) where {T<:BlasReal} = - (A = adjA.parent; LAPACK.ormlq!('L', 'T', A.factors, A.τ, B)) -lmul!(adjA::Adjoint{<:Any,<:LQPackedQ{T}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = - (A = adjA.parent; LAPACK.ormlq!('L', 'C', A.factors, A.τ, B)) - -function *(adjA::Adjoint{<:Any,<:LQPackedQ}, B::StridedVecOrMat) - A = adjA.parent - TAB = promote_type(eltype(A), eltype(B)) - if size(B,1) == size(A.factors,2) - lmul!(adjoint(AbstractMatrix{TAB}(A)), copymutable_oftype(B, TAB)) - elseif size(B,1) == size(A.factors,1) - lmul!(adjoint(AbstractMatrix{TAB}(A)), [B; zeros(TAB, size(A.factors, 2) - size(A.factors, 1), size(B, 2))]) - else - throw(DimensionMismatch("first dimension of B, $(size(B,1)), must equal one of the dimensions of A, $(size(A))")) - end -end - -### QBc/QcBc -function *(A::LQPackedQ, adjB::Adjoint{<:Any,<:StridedVecOrMat}) - B = adjB.parent - TAB = promote_type(eltype(A), eltype(B)) - BB = similar(B, TAB, (size(B, 2), size(B, 1))) - adjoint!(BB, B) - return lmul!(A, BB) -end -function *(adjA::Adjoint{<:Any,<:LQPackedQ}, adjB::Adjoint{<:Any,<:StridedVecOrMat}) - B = adjB.parent - TAB = promote_type(eltype(adjA.parent), eltype(B)) - BB = similar(B, TAB, (size(B, 2), size(B, 1))) - adjoint!(BB, B) - return lmul!(adjA, BB) -end - -# in-place right-application of LQPackedQs -# these methods require that the applied-to matrix's (A's) number of columns -# match the number of columns (nQ) of the LQPackedQ (Q) (necessary for in-place -# operation, and the underlying LAPACK routine (ormlq) treats the implicit Q -# as its (nQ-by-nQ) square form) -rmul!(A::StridedMatrix{T}, B::LQPackedQ{T}) where {T<:BlasFloat} = - LAPACK.ormlq!('R', 'N', B.factors, B.τ, A) -rmul!(A::StridedMatrix{T}, adjB::Adjoint{<:Any,<:LQPackedQ{T}}) where {T<:BlasReal} = - (B = adjB.parent; LAPACK.ormlq!('R', 'T', B.factors, B.τ, A)) -rmul!(A::StridedMatrix{T}, adjB::Adjoint{<:Any,<:LQPackedQ{T}}) where {T<:BlasComplex} = - (B = adjB.parent; LAPACK.ormlq!('R', 'C', B.factors, B.τ, A)) - -# out-of-place right application of LQPackedQs -# -# LQPackedQ's out-of-place multiplication behavior is context dependent. specifically, -# if the inner dimension in the multiplication is the LQPackedQ's second dimension, -# the LQPackedQ behaves like its square form. if the inner dimension in the -# multiplication is the LQPackedQ's first dimension, the LQPackedQ behaves like either -# its square form or its truncated form depending on the shape of the other object -# involved in the multiplication. we treat these cases separately. -# -# (1) the inner dimension in the multiplication is the LQPackedQ's second dimension. -# in this case, the LQPackedQ behaves like its square form. -# -function *(A::StridedVecOrMat, adjQ::Adjoint{<:Any,<:LQPackedQ}) - Q = adjQ.parent - TR = promote_type(eltype(A), eltype(Q)) - return rmul!(copymutable_oftype(A, TR), adjoint(AbstractMatrix{TR}(Q))) -end -function *(adjA::Adjoint{<:Any,<:StridedMatrix}, adjQ::Adjoint{<:Any,<:LQPackedQ}) - A, Q = adjA.parent, adjQ.parent - TR = promote_type(eltype(A), eltype(Q)) - C = adjoint!(similar(A, TR, reverse(size(A))), A) - return rmul!(C, adjoint(AbstractMatrix{TR}(Q))) -end -# -# (2) the inner dimension in the multiplication is the LQPackedQ's first dimension. -# in this case, the LQPackedQ behaves like either its square form or its -# truncated form depending on the shape of the other object in the multiplication. -# -# these methods: (1) check whether the applied-to matrix's (A's) appropriate dimension -# (columns for A_*, rows for Ac_*) matches the number of columns (nQ) of the LQPackedQ (Q), -# and if so effectively apply Q's square form to A without additional shenanigans; and -# (2) if the preceding dimensions do not match, check whether the appropriate dimension of -# A instead matches the number of rows of the matrix of which Q is a factor (i.e. -# size(Q.factors, 1)), and if so implicitly apply Q's truncated form to A by zero extending -# A as necessary for check (1) to pass (if possible) and then applying Q's square form -# -function *(A::StridedVecOrMat, Q::LQPackedQ) - TR = promote_type(eltype(A), eltype(Q)) - if size(A, 2) == size(Q.factors, 2) - C = copymutable_oftype(A, TR) - elseif size(A, 2) == size(Q.factors, 1) - C = zeros(TR, size(A, 1), size(Q.factors, 2)) - copyto!(C, 1, A, 1, length(A)) - else - _rightappdimmismatch("columns") - end - return rmul!(C, AbstractMatrix{TR}(Q)) -end -function *(adjA::Adjoint{<:Any,<:StridedMatrix}, Q::LQPackedQ) - A = adjA.parent - TR = promote_type(eltype(A), eltype(Q)) - if size(A, 1) == size(Q.factors, 2) - C = adjoint!(similar(A, TR, reverse(size(A))), A) - elseif size(A, 1) == size(Q.factors, 1) - C = zeros(TR, size(A, 2), size(Q.factors, 2)) - adjoint!(view(C, :, 1:size(A, 1)), A) - else - _rightappdimmismatch("rows") - end - return rmul!(C, AbstractMatrix{TR}(Q)) -end -_rightappdimmismatch(rowsorcols) = - throw(DimensionMismatch(string("the number of $(rowsorcols) of the matrix on the left ", - "must match either (1) the number of columns of the (LQPackedQ) matrix on the right ", - "or (2) the number of rows of that (LQPackedQ) matrix's internal representation ", - "(the factorization's originating matrix's number of rows)"))) - # With a real lhs and complex rhs with the same precision, we can reinterpret # the complex rhs as a real rhs with twice the number of columns function (\)(F::LQ{T}, B::VecOrMat{Complex{T}}) where T<:BlasReal @@ -356,7 +203,3 @@ function ldiv!(Fadj::AdjointFactorization{<:Any,<:LQ}, B::AbstractVecOrMat) ldiv!(UpperTriangular(adjoint(F.L)), view(B, 1:size(F,1), axes(B,2))) return B end - -# In LQ factorization, `Q` is expressed as the product of the adjoint of the -# reflectors. Thus, `det` has to be conjugated. -det(Q::LQPackedQ) = conj(_det_tau(Q.τ)) diff --git a/stdlib/LinearAlgebra/src/qr.jl b/stdlib/LinearAlgebra/src/qr.jl index 30deb785e6019..d2420fb78edef 100644 --- a/stdlib/LinearAlgebra/src/qr.jl +++ b/stdlib/LinearAlgebra/src/qr.jl @@ -32,7 +32,6 @@ The object has two fields: ``v_i`` is the ``i``th column of the matrix `V = I + tril(F.factors, -1)`. * `τ` is a vector of length `min(m,n)` containing the coefficients ``\tau_i``. - """ struct QR{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: Factorization{T} factors::S @@ -298,7 +297,7 @@ qr!(A::StridedMatrix{<:BlasFloat}, ::ColumnNorm) = QRPivoted(LAPACK.geqp3!(A)... """ qr!(A, pivot = NoPivot(); blocksize) -`qr!` is the same as [`qr`](@ref) when `A` is a subtype of [`StridedMatrix`](@ref), +`qr!` is the same as [`qr`](@ref) when `A` is a subtype of [`AbstractMatrix`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. An [`InexactError`](@ref) exception is thrown if the factorization produces a number not representable by the element type of `A`, e.g. for integer types. @@ -316,9 +315,7 @@ julia> a = [1. 2.; 3. 4.] julia> qr!(a) LinearAlgebra.QRCompactWY{Float64, Matrix{Float64}, Matrix{Float64}} Q factor: -2×2 LinearAlgebra.QRCompactWYQ{Float64, Matrix{Float64}, Matrix{Float64}}: - -0.316228 -0.948683 - -0.948683 0.316228 +2×2 LinearAlgebra.QRCompactWYQ{Float64, Matrix{Float64}, Matrix{Float64}} R factor: 2×2 Matrix{Float64}: -3.16228 -4.42719 @@ -387,7 +384,7 @@ orthogonal matrix. The block size for QR decomposition can be specified by keyword argument `blocksize :: Integer` when `pivot == NoPivot()` and `A isa StridedMatrix{<:BlasFloat}`. -It is ignored when `blocksize > minimum(size(A))`. See [`QRCompactWY`](@ref). +It is ignored when `blocksize > minimum(size(A))`. See [`QRCompactWY`](@ref). !!! compat "Julia 1.4" The `blocksize` keyword argument requires Julia 1.4 or later. @@ -403,10 +400,7 @@ julia> A = [3.0 -6.0; 4.0 -8.0; 0.0 1.0] julia> F = qr(A) LinearAlgebra.QRCompactWY{Float64, Matrix{Float64}, Matrix{Float64}} Q factor: -3×3 LinearAlgebra.QRCompactWYQ{Float64, Matrix{Float64}, Matrix{Float64}}: - -0.6 0.0 0.8 - -0.8 0.0 -0.6 - 0.0 -1.0 0.0 +3×3 LinearAlgebra.QRCompactWYQ{Float64, Matrix{Float64}, Matrix{Float64}} R factor: 2×2 Matrix{Float64}: -5.0 10.0 @@ -518,372 +512,25 @@ transpose(F::Union{QR{<:Real},QRPivoted{<:Real},QRCompactWY{<:Real}}) = F' transpose(::Union{QR,QRPivoted,QRCompactWY}) = throw(ArgumentError("transpose of QR decomposition is not supported, consider using adjoint")) -abstract type AbstractQ{T} <: AbstractMatrix{T} end - -inv(Q::AbstractQ) = Q' - -""" - QRPackedQ <: AbstractMatrix - -The orthogonal/unitary ``Q`` matrix of a QR factorization stored in [`QR`](@ref) or -[`QRPivoted`](@ref) format. -""" -struct QRPackedQ{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: AbstractQ{T} - factors::S - τ::C - - function QRPackedQ{T,S,C}(factors, τ) where {T,S<:AbstractMatrix{T},C<:AbstractVector{T}} - require_one_based_indexing(factors) - new{T,S,C}(factors, τ) - end -end -QRPackedQ(factors::AbstractMatrix{T}, τ::AbstractVector{T}) where {T} = - QRPackedQ{T,typeof(factors),typeof(τ)}(factors, τ) -QRPackedQ{T}(factors::AbstractMatrix, τ::AbstractVector) where {T} = - QRPackedQ(convert(AbstractMatrix{T}, factors), convert(AbstractVector{T}, τ)) -# backwards-compatible constructors (remove with Julia 2.0) -@deprecate(QRPackedQ{T,S}(factors::AbstractMatrix{T}, τ::AbstractVector{T}) where {T,S}, - QRPackedQ{T,S,typeof(τ)}(factors, τ), false) - -""" - QRCompactWYQ <: AbstractMatrix - -The orthogonal/unitary ``Q`` matrix of a QR factorization stored in [`QRCompactWY`](@ref) -format. -""" -struct QRCompactWYQ{S, M<:AbstractMatrix{S}, C<:AbstractMatrix{S}} <: AbstractQ{S} - factors::M - T::C - - function QRCompactWYQ{S,M,C}(factors, T) where {S,M<:AbstractMatrix{S},C<:AbstractMatrix{S}} - require_one_based_indexing(factors) - new{S,M,C}(factors, T) - end -end -QRCompactWYQ(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S} = - QRCompactWYQ{S,typeof(factors),typeof(T)}(factors, T) -QRCompactWYQ{S}(factors::AbstractMatrix, T::AbstractMatrix) where {S} = - QRCompactWYQ(convert(AbstractMatrix{S}, factors), convert(AbstractMatrix{S}, T)) -# backwards-compatible constructors (remove with Julia 2.0) -@deprecate(QRCompactWYQ{S,M}(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S,M}, - QRCompactWYQ{S,M,typeof(T)}(factors, T), false) - -QRPackedQ{T}(Q::QRPackedQ) where {T} = QRPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(Vector{T}, Q.τ)) -AbstractMatrix{T}(Q::QRPackedQ{T}) where {T} = Q -AbstractMatrix{T}(Q::QRPackedQ) where {T} = QRPackedQ{T}(Q) -QRCompactWYQ{S}(Q::QRCompactWYQ) where {S} = QRCompactWYQ(convert(AbstractMatrix{S}, Q.factors), convert(AbstractMatrix{S}, Q.T)) -AbstractMatrix{S}(Q::QRCompactWYQ{S}) where {S} = Q -AbstractMatrix{S}(Q::QRCompactWYQ) where {S} = QRCompactWYQ{S}(Q) -Matrix{T}(Q::AbstractQ{S}) where {T,S} = convert(Matrix{T}, lmul!(Q, Matrix{S}(I, size(Q, 1), min(size(Q.factors)...)))) -Matrix(Q::AbstractQ{T}) where {T} = Matrix{T}(Q) -Array{T}(Q::AbstractQ) where {T} = Matrix{T}(Q) -Array(Q::AbstractQ) = Matrix(Q) - -size(F::Union{QR,QRCompactWY,QRPivoted}, dim::Integer) = size(getfield(F, :factors), dim) size(F::Union{QR,QRCompactWY,QRPivoted}) = size(getfield(F, :factors)) -size(Q::Union{QRCompactWYQ,QRPackedQ}, dim::Integer) = - size(getfield(Q, :factors), dim == 2 ? 1 : dim) -size(Q::Union{QRCompactWYQ,QRPackedQ}) = size(Q, 1), size(Q, 2) - -copymutable(Q::AbstractQ{T}) where {T} = lmul!(Q, Matrix{T}(I, size(Q))) -copy(Q::AbstractQ) = copymutable(Q) -getindex(Q::AbstractQ, inds...) = copymutable(Q)[inds...] -getindex(Q::AbstractQ, ::Colon, ::Colon) = copy(Q) - -function getindex(Q::AbstractQ, ::Colon, j::Int) - y = zeros(eltype(Q), size(Q, 2)) - y[j] = 1 - lmul!(Q, y) -end - -getindex(Q::AbstractQ, i::Int, j::Int) = Q[:, j][i] - -# specialization avoiding the fallback using slow `getindex` -function copyto!(dest::AbstractMatrix, src::AbstractQ) - copyto!(dest, I) - lmul!(src, dest) -end -# needed to resolve method ambiguities -function copyto!(dest::PermutedDimsArray{T,2,perm}, src::AbstractQ) where {T,perm} - if perm == (1, 2) - copyto!(parent(dest), src) - else - @assert perm == (2, 1) # there are no other permutations of two indices - if T <: Real - copyto!(parent(dest), I) - lmul!(src', parent(dest)) - else - # LAPACK does not offer inplace lmul!(transpose(Q), B) for complex Q - tmp = similar(parent(dest)) - copyto!(tmp, I) - rmul!(tmp, src) - permutedims!(parent(dest), tmp, (2, 1)) - end - end - return dest -end - -## Multiplication by Q -### QB -lmul!(A::QRCompactWYQ{T,S}, B::StridedVecOrMat{T}) where {T<:BlasFloat, S<:StridedMatrix} = - LAPACK.gemqrt!('L', 'N', A.factors, A.T, B) -lmul!(A::QRPackedQ{T,S}, B::StridedVecOrMat{T}) where {T<:BlasFloat, S<:StridedMatrix} = - LAPACK.ormqr!('L', 'N', A.factors, A.τ, B) -function lmul!(A::QRPackedQ, B::AbstractVecOrMat) - require_one_based_indexing(B) - mA, nA = size(A.factors) - mB, nB = size(B,1), size(B,2) - if mA != mB - throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)")) - end - Afactors = A.factors - @inbounds begin - for k = min(mA,nA):-1:1 - for j = 1:nB - vBj = B[k,j] - for i = k+1:mB - vBj += conj(Afactors[i,k])*B[i,j] - end - vBj = A.τ[k]*vBj - B[k,j] -= vBj - for i = k+1:mB - B[i,j] -= Afactors[i,k]*vBj - end - end - end - end - B -end - -function (*)(A::AbstractQ, b::StridedVector) - TAb = promote_type(eltype(A), eltype(b)) - Anew = convert(AbstractMatrix{TAb}, A) - if size(A.factors, 1) == length(b) - bnew = copymutable_oftype(b, TAb) - elseif size(A.factors, 2) == length(b) - bnew = [b; zeros(TAb, size(A.factors, 1) - length(b))] - else - throw(DimensionMismatch("vector must have length either $(size(A.factors, 1)) or $(size(A.factors, 2))")) - end - lmul!(Anew, bnew) -end -function (*)(A::AbstractQ, B::StridedMatrix) - TAB = promote_type(eltype(A), eltype(B)) - Anew = convert(AbstractMatrix{TAB}, A) - if size(A.factors, 1) == size(B, 1) - Bnew = copymutable_oftype(B, TAB) - elseif size(A.factors, 2) == size(B, 1) - Bnew = [B; zeros(TAB, size(A.factors, 1) - size(B,1), size(B, 2))] - else - throw(DimensionMismatch("first dimension of matrix must have size either $(size(A.factors, 1)) or $(size(A.factors, 2))")) - end - lmul!(Anew, Bnew) -end - -function (*)(A::AbstractQ, b::Number) - TAb = promote_type(eltype(A), typeof(b)) - dest = similar(A, TAb) - copyto!(dest, b*I) - lmul!(A, dest) -end - -### QcB -lmul!(adjA::Adjoint{<:Any,<:QRCompactWYQ{T,S}}, B::StridedVecOrMat{T}) where {T<:BlasReal,S<:StridedMatrix} = - (A = adjA.parent; LAPACK.gemqrt!('L', 'T', A.factors, A.T, B)) -lmul!(adjA::Adjoint{<:Any,<:QRCompactWYQ{T,S}}, B::StridedVecOrMat{T}) where {T<:BlasComplex,S<:StridedMatrix} = - (A = adjA.parent; LAPACK.gemqrt!('L', 'C', A.factors, A.T, B)) -lmul!(adjA::Adjoint{<:Any,<:QRPackedQ{T,S}}, B::StridedVecOrMat{T}) where {T<:BlasReal,S<:StridedMatrix} = - (A = adjA.parent; LAPACK.ormqr!('L', 'T', A.factors, A.τ, B)) -lmul!(adjA::Adjoint{<:Any,<:QRPackedQ{T,S}}, B::StridedVecOrMat{T}) where {T<:BlasComplex,S<:StridedMatrix} = - (A = adjA.parent; LAPACK.ormqr!('L', 'C', A.factors, A.τ, B)) -function lmul!(adjA::Adjoint{<:Any,<:QRPackedQ}, B::AbstractVecOrMat) - require_one_based_indexing(B) - A = adjA.parent - mA, nA = size(A.factors) - mB, nB = size(B,1), size(B,2) - if mA != mB - throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)")) - end - Afactors = A.factors - @inbounds begin - for k = 1:min(mA,nA) - for j = 1:nB - vBj = B[k,j] - for i = k+1:mB - vBj += conj(Afactors[i,k])*B[i,j] - end - vBj = conj(A.τ[k])*vBj - B[k,j] -= vBj - for i = k+1:mB - B[i,j] -= Afactors[i,k]*vBj - end - end - end - end - B -end -function *(adjQ::Adjoint{<:Any,<:AbstractQ}, B::StridedVecOrMat) - Q = adjQ.parent - TQB = promote_type(eltype(Q), eltype(B)) - return lmul!(adjoint(convert(AbstractMatrix{TQB}, Q)), copymutable_oftype(B, TQB)) -end - -### QBc/QcBc -function *(Q::AbstractQ, adjB::Adjoint{<:Any,<:StridedVecOrMat}) - B = adjB.parent - TQB = promote_type(eltype(Q), eltype(B)) - Bc = similar(B, TQB, (size(B, 2), size(B, 1))) - adjoint!(Bc, B) - return lmul!(convert(AbstractMatrix{TQB}, Q), Bc) -end -function *(adjQ::Adjoint{<:Any,<:AbstractQ}, adjB::Adjoint{<:Any,<:StridedVecOrMat}) - Q, B = adjQ.parent, adjB.parent - TQB = promote_type(eltype(Q), eltype(B)) - Bc = similar(B, TQB, (size(B, 2), size(B, 1))) - adjoint!(Bc, B) - return lmul!(adjoint(convert(AbstractMatrix{TQB}, Q)), Bc) -end - -### AQ -rmul!(A::StridedVecOrMat{T}, B::QRCompactWYQ{T,S}) where {T<:BlasFloat,S<:StridedMatrix} = - LAPACK.gemqrt!('R', 'N', B.factors, B.T, A) -rmul!(A::StridedVecOrMat{T}, B::QRPackedQ{T,S}) where {T<:BlasFloat,S<:StridedMatrix} = - LAPACK.ormqr!('R', 'N', B.factors, B.τ, A) -function rmul!(A::StridedMatrix,Q::QRPackedQ) - mQ, nQ = size(Q.factors) - mA, nA = size(A,1), size(A,2) - if nA != mQ - throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)")) - end - Qfactors = Q.factors - @inbounds begin - for k = 1:min(mQ,nQ) - for i = 1:mA - vAi = A[i,k] - for j = k+1:mQ - vAi += A[i,j]*Qfactors[j,k] - end - vAi = vAi*Q.τ[k] - A[i,k] -= vAi - for j = k+1:nA - A[i,j] -= vAi*conj(Qfactors[j,k]) - end - end - end - end - A -end - -function (*)(A::StridedMatrix, Q::AbstractQ) - TAQ = promote_type(eltype(A), eltype(Q)) - - return rmul!(copymutable_oftype(A, TAQ), convert(AbstractMatrix{TAQ}, Q)) -end - -function (*)(a::Number, B::AbstractQ) - TaB = promote_type(typeof(a), eltype(B)) - dest = similar(B, TaB) - copyto!(dest, a*I) - rmul!(dest, B) -end - -### AQc -rmul!(A::StridedVecOrMat{T}, adjB::Adjoint{<:Any,<:QRCompactWYQ{T}}) where {T<:BlasReal} = - (B = adjB.parent; LAPACK.gemqrt!('R', 'T', B.factors, B.T, A)) -rmul!(A::StridedVecOrMat{T}, adjB::Adjoint{<:Any,<:QRCompactWYQ{T}}) where {T<:BlasComplex} = - (B = adjB.parent; LAPACK.gemqrt!('R', 'C', B.factors, B.T, A)) -rmul!(A::StridedVecOrMat{T}, adjB::Adjoint{<:Any,<:QRPackedQ{T}}) where {T<:BlasReal} = - (B = adjB.parent; LAPACK.ormqr!('R', 'T', B.factors, B.τ, A)) -rmul!(A::StridedVecOrMat{T}, adjB::Adjoint{<:Any,<:QRPackedQ{T}}) where {T<:BlasComplex} = - (B = adjB.parent; LAPACK.ormqr!('R', 'C', B.factors, B.τ, A)) -function rmul!(A::StridedMatrix, adjQ::Adjoint{<:Any,<:QRPackedQ}) - Q = adjQ.parent - mQ, nQ = size(Q.factors) - mA, nA = size(A,1), size(A,2) - if nA != mQ - throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)")) - end - Qfactors = Q.factors - @inbounds begin - for k = min(mQ,nQ):-1:1 - for i = 1:mA - vAi = A[i,k] - for j = k+1:mQ - vAi += A[i,j]*Qfactors[j,k] - end - vAi = vAi*conj(Q.τ[k]) - A[i,k] -= vAi - for j = k+1:nA - A[i,j] -= vAi*conj(Qfactors[j,k]) - end - end - end - end - A -end -function *(A::StridedMatrix, adjB::Adjoint{<:Any,<:AbstractQ}) - B = adjB.parent - TAB = promote_type(eltype(A),eltype(B)) - BB = convert(AbstractMatrix{TAB}, B) - if size(A,2) == size(B.factors, 1) - AA = copy_similar(A, TAB) - return rmul!(AA, adjoint(BB)) - elseif size(A,2) == size(B.factors,2) - return rmul!([A zeros(TAB, size(A, 1), size(B.factors, 1) - size(B.factors, 2))], adjoint(BB)) - else - throw(DimensionMismatch("matrix A has dimensions $(size(A)) but matrix B has dimensions $(size(B))")) - end -end -*(u::AdjointAbsVec, A::Adjoint{<:Any,<:AbstractQ}) = adjoint(A.parent * u.parent) - - -### AcQ/AcQc -function *(adjA::Adjoint{<:Any,<:StridedVecOrMat}, Q::AbstractQ) - A = adjA.parent - TAQ = promote_type(eltype(A), eltype(Q)) - Ac = similar(A, TAQ, (size(A, 2), size(A, 1))) - adjoint!(Ac, A) - return rmul!(Ac, convert(AbstractMatrix{TAQ}, Q)) -end -function *(adjA::Adjoint{<:Any,<:StridedVecOrMat}, adjQ::Adjoint{<:Any,<:AbstractQ}) - A, Q = adjA.parent, adjQ.parent - TAQ = promote_type(eltype(A), eltype(Q)) - Ac = similar(A, TAQ, (size(A, 2), size(A, 1))) - adjoint!(Ac, A) - return rmul!(Ac, adjoint(convert(AbstractMatrix{TAQ}, Q))) -end +size(F::Union{QR,QRCompactWY,QRPivoted}, dim::Integer) = size(getfield(F, :factors), dim) -### mul! -function mul!(C::StridedVecOrMat{T}, Q::AbstractQ{T}, B::StridedVecOrMat{T}) where {T} - require_one_based_indexing(C, B) - mB = size(B, 1) - mC = size(C, 1) - if mB < mC - inds = CartesianIndices(B) - copyto!(C, inds, B, inds) - C[CartesianIndices((mB+1:mC, axes(C, 2)))] .= zero(T) - return lmul!(Q, C) - else - return lmul!(Q, copyto!(C, B)) - end -end -mul!(C::StridedVecOrMat{T}, A::StridedVecOrMat{T}, Q::AbstractQ{T}) where {T} = rmul!(copyto!(C, A), Q) -mul!(C::StridedVecOrMat{T}, adjQ::Adjoint{<:Any,<:AbstractQ{T}}, B::StridedVecOrMat{T}) where {T} = lmul!(adjQ, copyto!(C, B)) -mul!(C::StridedVecOrMat{T}, A::StridedVecOrMat{T}, adjQ::Adjoint{<:Any,<:AbstractQ{T}}) where {T} = rmul!(copyto!(C, A), adjQ) -function ldiv!(A::QRCompactWY{T}, b::AbstractVector{T}) where {T<:BlasFloat} - m,n = size(A) +function ldiv!(A::QRCompactWY{T}, b::AbstractVector{T}) where {T} + require_one_based_indexing(b) + m, n = size(A) ldiv!(UpperTriangular(view(A.factors, 1:min(m,n), 1:n)), view(lmul!(adjoint(A.Q), b), 1:size(A, 2))) return b end -function ldiv!(A::QRCompactWY{T}, B::AbstractMatrix{T}) where {T<:BlasFloat} - m,n = size(A) +function ldiv!(A::QRCompactWY{T}, B::AbstractMatrix{T}) where {T} + require_one_based_indexing(B) + m, n = size(A) ldiv!(UpperTriangular(view(A.factors, 1:min(m,n), 1:n)), view(lmul!(adjoint(A.Q), B), 1:size(A, 2), 1:size(B, 2))) return B end # Julia implementation similar to xgelsy -function ldiv!(A::QRPivoted{T}, B::AbstractMatrix{T}, rcond::Real) where T<:BlasFloat +function ldiv!(A::QRPivoted{T,<:StridedMatrix}, B::AbstractMatrix{T}, rcond::Real) where {T<:BlasFloat} mA, nA = size(A.factors) nr = min(mA,nA) nrhs = size(B, 2) @@ -919,9 +566,9 @@ function ldiv!(A::QRPivoted{T}, B::AbstractMatrix{T}, rcond::Real) where T<:Blas B[A.p,:] = B[1:nA,:] return B, rnk end -ldiv!(A::QRPivoted{T}, B::AbstractVector{T}) where {T<:BlasFloat} = +ldiv!(A::QRPivoted{T,<:StridedMatrix}, B::AbstractVector{T}) where {T<:BlasFloat} = vec(ldiv!(A, reshape(B, length(B), 1))) -ldiv!(A::QRPivoted{T}, B::AbstractVecOrMat{T}) where {T<:BlasFloat} = +ldiv!(A::QRPivoted{T,<:StridedMatrix}, B::AbstractMatrix{T}) where {T<:BlasFloat} = ldiv!(A, B, min(size(A)...)*eps(real(float(one(eltype(B))))))[1] function _wide_qr_ldiv!(A::QR{T}, B::AbstractMatrix{T}) where T m, n = size(A) @@ -1002,7 +649,7 @@ function _apply_permutation!(F::QRPivoted, B::AbstractVecOrMat) B[1:length(F.p), :] = B[F.p, :] return B end -_apply_permutation!(F::Factorization, B::AbstractVecOrMat) = B +_apply_permutation!(::Factorization, B::AbstractVecOrMat) = B function ldiv!(Fadj::AdjointFactorization{<:Any,<:Union{QR,QRCompactWY,QRPivoted}}, B::AbstractVecOrMat) require_one_based_indexing(B) @@ -1063,25 +710,3 @@ end ## Lower priority: Add LQ, QL and RQ factorizations # FIXME! Should add balancing option through xgebal - - -det(Q::QRPackedQ) = _det_tau(Q.τ) - -det(Q::QRCompactWYQ) = - prod(i -> _det_tau(_diagview(Q.T[:, i:min(i + size(Q.T, 1), size(Q.T, 2))])), - 1:size(Q.T, 1):size(Q.T, 2)) - -_diagview(A) = @view A[diagind(A)] - -# Compute `det` from the number of Householder reflections. Handle -# the case `Q.τ` contains zeros. -_det_tau(τs::AbstractVector{<:Real}) = - isodd(count(!iszero, τs)) ? -one(eltype(τs)) : one(eltype(τs)) - -# In complex case, we need to compute the non-unit eigenvalue `λ = 1 - c*τ` -# (where `c = v'v`) of each Householder reflector. As we know that the -# reflector must have the determinant of 1, it must satisfy `abs2(λ) == 1`. -# Combining this with the constraint `c > 0`, it turns out that the eigenvalue -# (hence the determinant) can be computed as `λ = -sign(τ)^2`. -# See: https://github.com/JuliaLang/julia/pull/32887#issuecomment-521935716 -_det_tau(τs) = prod(τ -> iszero(τ) ? one(τ) : -sign(τ)^2, τs) diff --git a/stdlib/LinearAlgebra/src/special.jl b/stdlib/LinearAlgebra/src/special.jl index e4f28286b6aaa..19b1057f9d6b8 100644 --- a/stdlib/LinearAlgebra/src/special.jl +++ b/stdlib/LinearAlgebra/src/special.jl @@ -107,6 +107,29 @@ for op in (:+, :-) end end +function *(H::UpperHessenberg, B::Bidiagonal) + T = promote_op(matprod, eltype(H), eltype(B)) + A = mul!(similar(H, T, size(H)), H, B) + return B.uplo == 'U' ? UpperHessenberg(A) : A +end +function *(B::Bidiagonal, H::UpperHessenberg) + T = promote_op(matprod, eltype(B), eltype(H)) + A = mul!(similar(H, T, size(H)), B, H) + return B.uplo == 'U' ? UpperHessenberg(A) : A +end + +function /(H::UpperHessenberg, B::Bidiagonal) + T = typeof(oneunit(eltype(H))/oneunit(eltype(B))) + A = _rdiv!(similar(H, T, size(H)), H, B) + return B.uplo == 'U' ? UpperHessenberg(A) : A +end + +function \(B::Bidiagonal, H::UpperHessenberg) + T = typeof(oneunit(eltype(B))\oneunit(eltype(H))) + A = ldiv!(similar(H, T, size(H)), B, H) + return B.uplo == 'U' ? UpperHessenberg(A) : A +end + # specialized +/- for structured matrices. If these are removed, it falls # back to broadcasting which has ~2-10x speed regressions. # For the other structure matrix pairs, broadcasting works well. @@ -235,65 +258,15 @@ function (-)(A::UniformScaling, B::Diagonal) Diagonal(Ref(A) .- B.diag) end -lmul!(Q::AbstractQ, B::AbstractTriangular) = lmul!(Q, full!(B)) -lmul!(Q::QRPackedQ, B::AbstractTriangular) = lmul!(Q, full!(B)) # disambiguation -lmul!(Q::Adjoint{<:Any,<:AbstractQ}, B::AbstractTriangular) = lmul!(Q, full!(B)) -lmul!(Q::Adjoint{<:Any,<:QRPackedQ}, B::AbstractTriangular) = lmul!(Q, full!(B)) # disambiguation - -function _qlmul(Q::AbstractQ, B) - TQB = promote_type(eltype(Q), eltype(B)) - if size(Q.factors, 1) == size(B, 1) - Bnew = Matrix{TQB}(B) - elseif size(Q.factors, 2) == size(B, 1) - Bnew = [Matrix{TQB}(B); zeros(TQB, size(Q.factors, 1) - size(B,1), size(B, 2))] - else - throw(DimensionMismatch("first dimension of matrix must have size either $(size(Q.factors, 1)) or $(size(Q.factors, 2))")) - end - lmul!(convert(AbstractMatrix{TQB}, Q), Bnew) -end -function _qlmul(adjQ::Adjoint{<:Any,<:AbstractQ}, B) - TQB = promote_type(eltype(adjQ), eltype(B)) - lmul!(adjoint(convert(AbstractMatrix{TQB}, parent(adjQ))), Matrix{TQB}(B)) -end - -*(Q::AbstractQ, B::AbstractTriangular) = _qlmul(Q, B) -*(Q::Adjoint{<:Any,<:AbstractQ}, B::AbstractTriangular) = _qlmul(Q, B) -*(Q::AbstractQ, B::BiTriSym) = _qlmul(Q, B) -*(Q::Adjoint{<:Any,<:AbstractQ}, B::BiTriSym) = _qlmul(Q, B) -*(Q::AbstractQ, B::Diagonal) = _qlmul(Q, B) -*(Q::Adjoint{<:Any,<:AbstractQ}, B::Diagonal) = _qlmul(Q, B) - -rmul!(A::AbstractTriangular, Q::AbstractQ) = rmul!(full!(A), Q) -rmul!(A::AbstractTriangular, Q::Adjoint{<:Any,<:AbstractQ}) = rmul!(full!(A), Q) - -function _qrmul(A, Q::AbstractQ) - TAQ = promote_type(eltype(A), eltype(Q)) - return rmul!(Matrix{TAQ}(A), convert(AbstractMatrix{TAQ}, Q)) -end -function _qrmul(A, adjQ::Adjoint{<:Any,<:AbstractQ}) - Q = adjQ.parent - TAQ = promote_type(eltype(A), eltype(Q)) - if size(A,2) == size(Q.factors, 1) - Anew = Matrix{TAQ}(A) - elseif size(A,2) == size(Q.factors,2) - Anew = [Matrix{TAQ}(A) zeros(TAQ, size(A, 1), size(Q.factors, 1) - size(Q.factors, 2))] - else - throw(DimensionMismatch("matrix A has dimensions $(size(A)) but matrix B has dimensions $(size(Q))")) - end - return rmul!(Anew, adjoint(convert(AbstractMatrix{TAQ}, Q))) -end +## Diagonal construction from UniformScaling +Diagonal{T}(s::UniformScaling, m::Integer) where {T} = Diagonal{T}(fill(T(s.λ), m)) +Diagonal(s::UniformScaling, m::Integer) = Diagonal{eltype(s)}(s, m) -*(A::AbstractTriangular, Q::AbstractQ) = _qrmul(A, Q) -*(A::AbstractTriangular, Q::Adjoint{<:Any,<:AbstractQ}) = _qrmul(A, Q) -*(A::BiTriSym, Q::AbstractQ) = _qrmul(A, Q) -*(A::BiTriSym, Q::Adjoint{<:Any,<:AbstractQ}) = _qrmul(A, Q) -*(A::Diagonal, Q::AbstractQ) = _qrmul(A, Q) -*(A::Diagonal, Q::Adjoint{<:Any,<:AbstractQ}) = _qrmul(A, Q) +Base.muladd(A::Union{Diagonal, UniformScaling}, B::Union{Diagonal, UniformScaling}, z::Union{Diagonal, UniformScaling}) = + Diagonal(_diag_or_value(A) .* _diag_or_value(B) .+ _diag_or_value(z)) -*(Q::AbstractQ, B::AbstractQ) = Q * (B * I) -*(Q::Adjoint{<:Any,<:AbstractQ}, B::AbstractQ) = Q * (B * I) -*(Q::AbstractQ, B::Adjoint{<:Any,<:AbstractQ}) = Q * (B * I) -*(Q::Adjoint{<:Any,<:AbstractQ}, B::Adjoint{<:Any,<:AbstractQ}) = Q * (B * I) +_diag_or_value(A::Diagonal) = A.diag +_diag_or_value(A::UniformScaling) = A.λ # fill[stored]! methods fillstored!(A::Diagonal, x) = (fill!(A.diag, x); A) diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index e691caf696e8c..f5c8bdd6f2e75 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -525,10 +525,6 @@ Array{T}(s::UniformScaling, m::Integer, n::Integer) where {T} = Matrix{T}(s, m, Array(s::UniformScaling, m::Integer, n::Integer) = Matrix(s, m, n) Array(s::UniformScaling, dims::Dims{2}) = Matrix(s, dims) -## Diagonal construction from UniformScaling -Diagonal{T}(s::UniformScaling, m::Integer) where {T} = Diagonal{T}(fill(T(s.λ), m)) -Diagonal(s::UniformScaling, m::Integer) = Diagonal{eltype(s)}(s, m) - dot(A::AbstractMatrix, J::UniformScaling) = dot(tr(A), J.λ) dot(J::UniformScaling, A::AbstractMatrix) = dot(J.λ, tr(A)) @@ -539,8 +535,3 @@ dot(x::AbstractVector, a::Union{Real,Complex}, y::AbstractVector) = a*dot(x, y) # muladd Base.muladd(A::UniformScaling, B::UniformScaling, z::UniformScaling) = UniformScaling(A.λ * B.λ + z.λ) -Base.muladd(A::Union{Diagonal, UniformScaling}, B::Union{Diagonal, UniformScaling}, z::Union{Diagonal, UniformScaling}) = - Diagonal(_diag_or_value(A) .* _diag_or_value(B) .+ _diag_or_value(z)) - -_diag_or_value(A::Diagonal) = A.diag -_diag_or_value(A::UniformScaling) = A.λ diff --git a/stdlib/LinearAlgebra/test/abstractq.jl b/stdlib/LinearAlgebra/test/abstractq.jl new file mode 100644 index 0000000000000..252a632fc97b9 --- /dev/null +++ b/stdlib/LinearAlgebra/test/abstractq.jl @@ -0,0 +1,84 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module TestAbstractQ + +using Test +using LinearAlgebra +using LinearAlgebra: AbstractQ, AdjointQ +import LinearAlgebra: lmul!, rmul! +import Base: size, convert + +n = 5 + +@testset "custom AbstractQ type" begin + struct MyQ{T,S<:AbstractQ{T}} <: AbstractQ{T} + Q::S + end + MyQ{T}(Q::AbstractQ) where {T} = (P = convert(AbstractQ{T}, Q); MyQ{T,typeof(P)}(P)) + MyQ(Q::MyQ) = Q + + Base.size(Q::MyQ) = size(Q.Q) + LinearAlgebra.lmul!(Q::MyQ, B::AbstractVecOrMat) = lmul!(Q.Q, B) + LinearAlgebra.lmul!(adjQ::AdjointQ{<:Any,<:MyQ}, B::AbstractVecOrMat) = lmul!(parent(adjQ).Q', B) + LinearAlgebra.rmul!(A::AbstractMatrix, Q::MyQ) = rmul!(A, Q.Q) + LinearAlgebra.rmul!(A::AbstractMatrix, adjQ::AdjointQ{<:Any,<:MyQ}) = rmul!(A, parent(adjQ).Q') + Base.convert(::Type{AbstractQ{T}}, Q::MyQ) where {T} = MyQ{T}(Q.Q) + LinearAlgebra.det(Q::MyQ) = det(Q.Q) + + for T in (Float64, ComplexF64) + A = rand(T, n, n) + F = qr(A) + Q = MyQ(F.Q) + @test convert(AbstractQ{complex(T)}, Q) isa MyQ{complex(T)} + @test convert(AbstractQ{complex(T)}, Q') isa AdjointQ{<:complex(T),<:MyQ{complex(T)}} + @test Q*I ≈ Q.Q*I rtol=2eps(real(T)) + @test Q'*I ≈ Q.Q'*I rtol=2eps(real(T)) + @test I*Q ≈ Q.Q*I rtol=2eps(real(T)) + @test I*Q' ≈ I*Q.Q' rtol=2eps(real(T)) + @test abs(det(Q)) ≈ 1 + @test logabsdet(Q)[1] ≈ 0 atol=2eps(real(T)) + y = rand(T, n) + @test Q * y ≈ Q.Q * y ≈ Q' \ y ≈ ldiv!(Q', copy(y)) ≈ ldiv!(zero(y), Q', y) + @test Q'y ≈ Q.Q' * y ≈ Q \ y ≈ ldiv!(Q, copy(y)) ≈ ldiv!(zero(y), Q, y) + @test y'Q ≈ y'Q.Q ≈ y' / Q' + @test y'Q' ≈ y'Q.Q' ≈ y' / Q + y = Matrix(y') + @test y*Q ≈ y*Q.Q ≈ y / Q' ≈ rdiv!(copy(y), Q') + @test y*Q' ≈ y*Q.Q' ≈ y / Q ≈ rdiv!(copy(y), Q) + Y = rand(T, n, n); X = similar(Y) + for transQ in (identity, adjoint), transY in (identity, adjoint), Y in (Y, Y') + @test mul!(X, transQ(Q), transY(Y)) ≈ transQ(Q) * transY(Y) ≈ transQ(Q.Q) * transY(Y) + @test mul!(X, transY(Y), transQ(Q)) ≈ transY(Y) * transQ(Q) ≈ transY(Y) * transQ(Q.Q) + end + @test Matrix(Q) ≈ Q[:,:] ≈ copyto!(zeros(T, size(Q)), Q) ≈ Q.Q*I + @test Matrix(Q') ≈ (Q')[:,:] ≈ copyto!(zeros(T, size(Q)), Q') ≈ Q.Q'*I + @test Q[1,:] == Q.Q[1,:] + @test Q[:,1] == Q.Q[:,1] + @test Q[1,1] == Q.Q[1,1] + @test Q[:] == Q.Q[:] + @test Q[:,1:3] == Q.Q[:,1:3] == Matrix(Q)[:,1:3] + @test_throws BoundsError Q[0,1] + @test_throws BoundsError Q[n+1,1] + @test_throws BoundsError Q[1,0] + @test_throws BoundsError Q[1,n+1] + @test_throws BoundsError Q[:,1:n+1] + @test_throws BoundsError Q[:,0:n] + for perm in ((1, 2), (2, 1)) + P = PermutedDimsArray(zeros(T, size(Q)), perm) + @test copyto!(P, Q) ≈ Matrix(Q) + end + x = randn(T) + @test x * Q ≈ (x*I)*Q ≈ x * Q.Q + @test Q * x ≈ Q*(x*I) ≈ Q.Q * x + @test x * Q' ≈ (x*I)* Q' ≈ x * Q.Q' + @test Q' * x ≈ Q'*(x*I) ≈ Q.Q' * x + x = rand(T, 1) + Q = MyQ(qr(rand(T, 1, 1)).Q) + @test x * Q ≈ x * Q.Q + @test x * Q' ≈ x * Q.Q' + @test Q * x ≈ Q.Q * x + @test Q' * x ≈ Q.Q' * x + end +end + +end # module diff --git a/stdlib/LinearAlgebra/test/hessenberg.jl b/stdlib/LinearAlgebra/test/hessenberg.jl index fd1fefb97cab7..a9436b5ba8bdd 100644 --- a/stdlib/LinearAlgebra/test/hessenberg.jl +++ b/stdlib/LinearAlgebra/test/hessenberg.jl @@ -149,9 +149,11 @@ let n = 10 @test_throws ErrorException H.Z @test convert(Array, H) ≈ A @test (H.Q * H.H) * H.Q' ≈ A ≈ (Matrix(H.Q) * Matrix(H.H)) * Matrix(H.Q)' - @test (H.Q' *A) * H.Q ≈ H.H + @test (H.Q' * A) * H.Q ≈ H.H #getindex for HessenbergQ @test H.Q[1,1] ≈ Array(H.Q)[1,1] + @test det(H.Q) ≈ det(Matrix(H.Q)) + @test logabsdet(H.Q)[1] ≈ logabsdet(Matrix(H.Q))[1] atol=2n*eps(float(real(eltya))) # REPL show hessstring = sprint((t, s) -> show(t, "text/plain", s), H) diff --git a/stdlib/LinearAlgebra/test/lq.jl b/stdlib/LinearAlgebra/test/lq.jl index c340317a7cc23..8b4af6a0a5f8d 100644 --- a/stdlib/LinearAlgebra/test/lq.jl +++ b/stdlib/LinearAlgebra/test/lq.jl @@ -71,13 +71,11 @@ rectangularQ(Q::LinearAlgebra.LQPackedQ) = convert(Array, Q) @test lqa*x ≈ a*x rtol=3000ε @test (sq = size(q.factors, 2); *(Matrix{eltyb}(I, sq, sq), adjoint(q))*squareQ(q)) ≈ Matrix(I, n, n) rtol=5000ε if eltya != Int - @test Matrix{eltyb}(I, n, n)*q ≈ convert(AbstractMatrix{tab},q) + @test Matrix{eltyb}(I, n, n)*q ≈ Matrix(I, n, n) * convert(LinearAlgebra.AbstractQ{tab}, q) end @test q*x ≈ squareQ(q)*x rtol=100ε - @test transpose(q)*x ≈ transpose(squareQ(q))*x rtol=100ε @test q'*x ≈ squareQ(q)'*x rtol=100ε @test a*q ≈ a*squareQ(q) rtol=100ε - @test a*transpose(q) ≈ a*transpose(squareQ(q)) rtol=100ε @test a*q' ≈ a*squareQ(q)' rtol=100ε @test q*a'≈ squareQ(q)*a' rtol=100ε @test q'*a' ≈ squareQ(q)'*a' rtol=100ε @@ -89,7 +87,6 @@ rectangularQ(Q::LinearAlgebra.LQPackedQ) = convert(Array, Q) pad_a = vcat(I, a) pad_x = hcat(I, x) @test pad_a*q ≈ pad_a*squareQ(q) rtol=100ε - @test transpose(q)*pad_x ≈ transpose(squareQ(q))*pad_x rtol=100ε @test q'*pad_x ≈ squareQ(q)'*pad_x rtol=100ε end end @@ -193,12 +190,12 @@ end @testset for n in 1:3, m in 1:3 @testset "real" begin _, Q = lq(randn(n, m)) - @test det(Q) ≈ det(collect(Q)) + @test det(Q) ≈ det(Q*I) @test abs(det(Q)) ≈ 1 end @testset "complex" begin _, Q = lq(randn(ComplexF64, n, m)) - @test det(Q) ≈ det(collect(Q)) + @test det(Q) ≈ det(Q*I) @test abs(det(Q)) ≈ 1 end end @@ -217,11 +214,7 @@ L factor: 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 Q factor: -4×4 LinearAlgebra.LQPackedQ{Float64, Matrix{Float64}, Vector{Float64}}: - 1.0 0.0 0.0 0.0 - 0.0 1.0 0.0 0.0 - 0.0 0.0 1.0 0.0 - 0.0 0.0 0.0 1.0""" +4×4 LinearAlgebra.LQPackedQ{Float64, Matrix{Float64}, Vector{Float64}}""" end @testset "adjoint of LQ" begin diff --git a/stdlib/LinearAlgebra/test/qr.jl b/stdlib/LinearAlgebra/test/qr.jl index 9ccc42dfb3259..cb15d94d08dcb 100644 --- a/stdlib/LinearAlgebra/test/qr.jl +++ b/stdlib/LinearAlgebra/test/qr.jl @@ -21,8 +21,8 @@ breal = randn(n,2)/2 bimg = randn(n,2)/2 # helper functions to unambiguously recover explicit forms of an implicit QR Q -squareQ(Q::LinearAlgebra.AbstractQ) = (sq = size(Q.factors, 1); lmul!(Q, Matrix{eltype(Q)}(I, sq, sq))) -rectangularQ(Q::LinearAlgebra.AbstractQ) = convert(Array, Q) +squareQ(Q::LinearAlgebra.AbstractQ) = Q*I +rectangularQ(Q::LinearAlgebra.AbstractQ) = Matrix(Q) @testset for eltya in (Float32, Float64, ComplexF32, ComplexF64, BigFloat, Int) raw_a = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(areal, aimg) : areal) @@ -62,7 +62,7 @@ rectangularQ(Q::LinearAlgebra.AbstractQ) = convert(Array, Q) sq = size(q.factors, 2) @test *(Matrix{eltyb}(I, sq, sq), adjoint(q)) * squareQ(q) ≈ Matrix(I, sq, sq) atol=5000ε if eltya != Int - @test Matrix{eltyb}(I, a_1, a_1)*q ≈ convert(AbstractMatrix{tab}, q) + @test Matrix{eltyb}(I, a_1, a_1)*q ≈ squareQ(convert(LinearAlgebra.AbstractQ{tab}, q)) ac = copy(a) @test qr!(a[:, 1:5])\b == qr!(view(ac, :, 1:5))\b end @@ -86,14 +86,14 @@ rectangularQ(Q::LinearAlgebra.AbstractQ) = convert(Array, Q) @test q*b[1:n1] ≈ rectangularQ(q)*b[1:n1] atol=100ε @test q*b ≈ squareQ(q)*b atol=100ε if eltya != Int - @test Array{eltya}(q) ≈ Matrix(q) + @test Array{eltya}(q) ≈ rectangularQ(q) end @test_throws DimensionMismatch q*b[1:n1 + 1] @test_throws DimensionMismatch b[1:n1 + 1]*q' sq = size(q.factors, 2) @test *(UpperTriangular(Matrix{eltyb}(I, sq, sq)), adjoint(q))*squareQ(q) ≈ Matrix(I, n1, a_1) atol=5000ε if eltya != Int - @test Matrix{eltyb}(I, a_1, a_1)*q ≈ convert(AbstractMatrix{tab},q) + @test Matrix{eltyb}(I, a_1, a_1)*q ≈ squareQ(convert(LinearAlgebra.AbstractQ{tab},q)) end # iterate q, r = qra @@ -123,7 +123,7 @@ rectangularQ(Q::LinearAlgebra.AbstractQ) = convert(Array, Q) @test_throws DimensionMismatch q*b[1:n1+1] @test_throws DimensionMismatch b[1:n1+1]*q' if eltya != Int - @test Matrix{eltyb}(I, n1, n1)*q ≈ convert(AbstractMatrix{tab},q) + @test Matrix{eltyb}(I, n1, n1)*q ≈ squareQ(convert(LinearAlgebra.AbstractQ{tab},q)) end # iterate q, r, p = qrpa @@ -149,7 +149,7 @@ rectangularQ(Q::LinearAlgebra.AbstractQ) = convert(Array, Q) sq = size(q.factors, 2) @test *(UpperTriangular(Matrix{eltyb}(I, sq, sq)), adjoint(q))*squareQ(q) ≈ Matrix(I, n1, a_1) atol=5000ε if eltya != Int - @test Matrix{eltyb}(I, a_1, a_1)*q ≈ convert(AbstractMatrix{tab},q) + @test Matrix{eltyb}(I, a_1, a_1)*q ≈ squareQ(convert(LinearAlgebra.AbstractQ{tab},q)) end qrstring = sprint((t, s) -> show(t, "text/plain", s), qrpa) rstring = sprint((t, s) -> show(t, "text/plain", s), r) @@ -205,6 +205,13 @@ rectangularQ(Q::LinearAlgebra.AbstractQ) = convert(Array, Q) @test mul!(c, b, q) ≈ b*q @test mul!(c, b, q') ≈ b*q' @test_throws DimensionMismatch mul!(Matrix{eltya}(I, n+1, n), q, b) + + b = similar(a[:,1]); rand!(b) + c = similar(a[:,1]) + d = similar(a[:,1]) + @test mul!(c, q, b) ≈ q*b + @test mul!(c, q', b) ≈ q'*b + @test_throws DimensionMismatch mul!(Vector{eltya}(undef, n+1), q, b) end end end @@ -228,7 +235,7 @@ end for T in (Tr, Complex{Tr}) v = convert(Vector{T}, vr) nv, nm = qr(v) - @test norm(nv - [-0.6 -0.8; -0.8 0.6], Inf) < eps(Tr) + @test norm(nv*Matrix(I, (2,2)) - [-0.6 -0.8; -0.8 0.6], Inf) < eps(Tr) @test nm == fill(-5.0, 1, 1) end end @@ -261,7 +268,7 @@ end @testset "Issue 24589. Promotion of rational matrices" begin A = rand(1//1:5//5, 4,3) - @test first(qr(A)) == first(qr(float(A))) + @test Matrix(first(qr(A))) == Matrix(first(qr(float(A)))) end @testset "Issue Test Factorization fallbacks for rectangular problems" begin @@ -303,7 +310,7 @@ end @testset for k in 0:min(n, m, 5) A = cat(Array(I(k)), randn(n - k, m - k); dims=(1, 2)) Q, = qr(A, pivot) - @test det(Q) ≈ det(collect(Q)) + @test det(Q) ≈ det(Q*Matrix(I, size(Q, 1), size(Q, 1))) @test abs(det(Q)) ≈ 1 end end @@ -311,7 +318,7 @@ end @testset for k in 0:min(n, m, 5) A = cat(Array(I(k)), randn(ComplexF64, n - k, m - k); dims=(1, 2)) Q, = qr(A, pivot) - @test det(Q) ≈ det(collect(Q)) + @test det(Q) ≈ det(Q*Matrix(I, size(Q, 1), size(Q, 1))) @test abs(det(Q)) ≈ 1 end end @@ -322,6 +329,7 @@ end for T in (Float64, ComplexF64) Q = qr(randn(T,5,5)).Q @test inv(Q) === Q' + @test inv(Q)' === inv(Q') === Q end end @@ -329,7 +337,7 @@ end for T in (Float32, Float64, ComplexF32, ComplexF64) Q1, R1 = qr(randn(T,5,5)) Q2, R2 = qr(Q1) - @test Q1 ≈ Q2 + @test Matrix(Q1) ≈ Matrix(Q2) @test R2 ≈ I end end @@ -362,13 +370,13 @@ end n = 5 Q, R = qr(randn(T,n,n)) Qmat = Matrix(Q) - dest1 = similar(Q) + dest1 = Matrix{T}(undef, size(Q)) copyto!(dest1, Q) @test dest1 ≈ Qmat - dest2 = PermutedDimsArray(similar(Q), (1, 2)) + dest2 = PermutedDimsArray(Matrix{T}(undef, size(Q)), (1, 2)) copyto!(dest2, Q) @test dest2 ≈ Qmat - dest3 = PermutedDimsArray(similar(Q), (2, 1)) + dest3 = PermutedDimsArray(Matrix{T}(undef, size(Q)), (2, 1)) copyto!(dest3, Q) @test dest3 ≈ Qmat end @@ -419,8 +427,8 @@ end A = qr(ones(3, 1)) B = I(3) C = B*A.Q' - @test C ≈ A.Q - @test A.Q' * B ≈ A.Q + @test C ≈ A.Q * Matrix(I, 3, 3) + @test A.Q' * B ≈ A.Q * Matrix(I, 3, 3) end @testset "convert between eltypes" begin diff --git a/stdlib/LinearAlgebra/test/testgroups b/stdlib/LinearAlgebra/test/testgroups index 2648016e453a8..8258573969cbb 100644 --- a/stdlib/LinearAlgebra/test/testgroups +++ b/stdlib/LinearAlgebra/test/testgroups @@ -25,4 +25,5 @@ bunchkaufman givens pinv factorization +abstractq ldlt diff --git a/test/bitarray.jl b/test/bitarray.jl index dd1d0d7d6c5a4..5d0bff62ab6e1 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -1652,7 +1652,7 @@ timesofar("cat") @test ((svdb1, svdb1A) = (svd(b1), svd(Array(b1))); svdb1.U == svdb1A.U && svdb1.S == svdb1A.S && svdb1.V == svdb1A.V) @test ((qrb1, qrb1A) = (qr(b1), qr(Array(b1))); - qrb1.Q == qrb1A.Q && qrb1.R == qrb1A.R) + Matrix(qrb1.Q) == Matrix(qrb1A.Q) && qrb1.R == qrb1A.R) b1 = bitrand(v1) @check_bit_operation diagm(0 => b1) BitMatrix From a6e6fc9ff3d073006a0222c04bf9b4136abd4c35 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Thu, 16 Feb 2023 10:25:43 -0300 Subject: [PATCH 138/775] Remove the rem_float intrinsics (#48685) --- base/compiler/tfuncs.jl | 3 --- base/fastmath.jl | 8 ++++++-- src/intrinsics.cpp | 4 ---- src/intrinsics.h | 2 -- src/julia_internal.h | 1 - src/runtime_intrinsics.c | 1 - test/intrinsics.jl | 1 - 7 files changed, 6 insertions(+), 14 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 29ab00b33d94d..e01071d9073fa 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -185,7 +185,6 @@ add_tfunc(add_float, 2, 2, math_tfunc, 1) add_tfunc(sub_float, 2, 2, math_tfunc, 1) add_tfunc(mul_float, 2, 2, math_tfunc, 4) add_tfunc(div_float, 2, 2, math_tfunc, 20) -add_tfunc(rem_float, 2, 2, math_tfunc, 20) add_tfunc(fma_float, 3, 3, math_tfunc, 5) add_tfunc(muladd_float, 3, 3, math_tfunc, 5) @@ -195,7 +194,6 @@ add_tfunc(add_float_fast, 2, 2, math_tfunc, 1) add_tfunc(sub_float_fast, 2, 2, math_tfunc, 1) add_tfunc(mul_float_fast, 2, 2, math_tfunc, 2) add_tfunc(div_float_fast, 2, 2, math_tfunc, 10) -add_tfunc(rem_float_fast, 2, 2, math_tfunc, 10) # bitwise operators # ----------------- @@ -2178,7 +2176,6 @@ const _INCONSISTENT_INTRINSICS = Any[ Intrinsics.mul_float_fast, Intrinsics.ne_float_fast, Intrinsics.neg_float_fast, - Intrinsics.rem_float_fast, Intrinsics.sqrt_llvm_fast, Intrinsics.sub_float_fast # TODO needs to revive #31193 to mark this as inconsistent to be accurate diff --git a/base/fastmath.jl b/base/fastmath.jl index a969bcaaa6ae0..7865736f8a776 100644 --- a/base/fastmath.jl +++ b/base/fastmath.jl @@ -28,7 +28,7 @@ module FastMath export @fastmath import Core.Intrinsics: sqrt_llvm_fast, neg_float_fast, - add_float_fast, sub_float_fast, mul_float_fast, div_float_fast, rem_float_fast, + add_float_fast, sub_float_fast, mul_float_fast, div_float_fast, eq_float_fast, ne_float_fast, lt_float_fast, le_float_fast const fast_op = @@ -173,7 +173,6 @@ add_fast(x::T, y::T) where {T<:FloatTypes} = add_float_fast(x, y) sub_fast(x::T, y::T) where {T<:FloatTypes} = sub_float_fast(x, y) mul_fast(x::T, y::T) where {T<:FloatTypes} = mul_float_fast(x, y) div_fast(x::T, y::T) where {T<:FloatTypes} = div_float_fast(x, y) -rem_fast(x::T, y::T) where {T<:FloatTypes} = rem_float_fast(x, y) add_fast(x::T, y::T, zs::T...) where {T<:FloatTypes} = add_fast(add_fast(x, y), zs...) @@ -304,6 +303,11 @@ sincos_fast(v::AbstractFloat) = (sin_fast(v), cos_fast(v)) sincos_fast(v::Real) = sincos_fast(float(v)::AbstractFloat) sincos_fast(v) = (sin_fast(v), cos_fast(v)) + +function rem_fast(x::T, y::T) where {T<:FloatTypes} + return @fastmath copysign(Base.rem_internal(abs(x), abs(y)), x) +end + @fastmath begin hypot_fast(x::T, y::T) where {T<:FloatTypes} = sqrt(x*x + y*y) diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index b822907e63524..0b95056baeaf8 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -79,12 +79,10 @@ const auto &float_func() { float_func[sub_float] = true; float_func[mul_float] = true; float_func[div_float] = true; - float_func[rem_float] = true; float_func[add_float_fast] = true; float_func[sub_float_fast] = true; float_func[mul_float_fast] = true; float_func[div_float_fast] = true; - float_func[rem_float_fast] = true; float_func[fma_float] = true; float_func[muladd_float] = true; float_func[eq_float] = true; @@ -1365,12 +1363,10 @@ static Value *emit_untyped_intrinsic(jl_codectx_t &ctx, intrinsic f, Value **arg case sub_float: return math_builder(ctx)().CreateFSub(x, y); case mul_float: return math_builder(ctx)().CreateFMul(x, y); case div_float: return math_builder(ctx)().CreateFDiv(x, y); - case rem_float: return math_builder(ctx)().CreateFRem(x, y); case add_float_fast: return math_builder(ctx, true)().CreateFAdd(x, y); case sub_float_fast: return math_builder(ctx, true)().CreateFSub(x, y); case mul_float_fast: return math_builder(ctx, true)().CreateFMul(x, y); case div_float_fast: return math_builder(ctx, true)().CreateFDiv(x, y); - case rem_float_fast: return math_builder(ctx, true)().CreateFRem(x, y); case fma_float: { assert(y->getType() == x->getType()); assert(z->getType() == y->getType()); diff --git a/src/intrinsics.h b/src/intrinsics.h index bb67460bbb31f..93747faa74160 100644 --- a/src/intrinsics.h +++ b/src/intrinsics.h @@ -19,7 +19,6 @@ ADD_I(sub_float, 2) \ ADD_I(mul_float, 2) \ ADD_I(div_float, 2) \ - ADD_I(rem_float, 2) \ ADD_I(fma_float, 3) \ ADD_I(muladd_float, 3) \ /* fast arithmetic */ \ @@ -28,7 +27,6 @@ ALIAS(sub_float_fast, sub_float) \ ALIAS(mul_float_fast, mul_float) \ ALIAS(div_float_fast, div_float) \ - ALIAS(rem_float_fast, rem_float) \ /* same-type comparisons */ \ ADD_I(eq_int, 2) \ ADD_I(ne_int, 2) \ diff --git a/src/julia_internal.h b/src/julia_internal.h index 8f7b49239c5e3..21ac6dc4205ea 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1344,7 +1344,6 @@ JL_DLLEXPORT jl_value_t *jl_add_float(jl_value_t *a, jl_value_t *b); JL_DLLEXPORT jl_value_t *jl_sub_float(jl_value_t *a, jl_value_t *b); JL_DLLEXPORT jl_value_t *jl_mul_float(jl_value_t *a, jl_value_t *b); JL_DLLEXPORT jl_value_t *jl_div_float(jl_value_t *a, jl_value_t *b); -JL_DLLEXPORT jl_value_t *jl_rem_float(jl_value_t *a, jl_value_t *b); JL_DLLEXPORT jl_value_t *jl_fma_float(jl_value_t *a, jl_value_t *b, jl_value_t *c); JL_DLLEXPORT jl_value_t *jl_muladd_float(jl_value_t *a, jl_value_t *b, jl_value_t *c); diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index 40ba036edebfd..9536ed1f02fb3 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -1182,7 +1182,6 @@ bi_fintrinsic(add,add_float) bi_fintrinsic(sub,sub_float) bi_fintrinsic(mul,mul_float) bi_fintrinsic(div,div_float) -bi_fintrinsic(frem,rem_float) // ternary operators // // runtime fma is broken on windows, define julia_fma(f) ourself with fma_emulated as reference. diff --git a/test/intrinsics.jl b/test/intrinsics.jl index dec4412ffd4d5..aa2a9649857c4 100644 --- a/test/intrinsics.jl +++ b/test/intrinsics.jl @@ -148,7 +148,6 @@ end @test_intrinsic Core.Intrinsics.sub_float Float16(3.3) Float16(2) Float16(1.301) @test_intrinsic Core.Intrinsics.mul_float Float16(3.3) Float16(2) Float16(6.6) @test_intrinsic Core.Intrinsics.div_float Float16(3.3) Float16(2) Float16(1.65) - @test_intrinsic Core.Intrinsics.rem_float Float16(3.3) Float16(2) Float16(1.301) # ternary @test_intrinsic Core.Intrinsics.fma_float Float16(3.3) Float16(4.4) Float16(5.5) Float16(20.02) From fe6307dc0696f2101de9cf986c942552b3da9063 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 16 Feb 2023 22:51:23 +0900 Subject: [PATCH 139/775] inference: remove special handlings of quasi-builtin functions (#48684) And fix their effect modeling. --- base/compiler/abstractinterpretation.jl | 21 --------------------- base/compiler/ssair/inlining.jl | 10 +--------- base/essentials.jl | 16 +++++++++++++++- base/promotion.jl | 4 ++-- base/reflection.jl | 13 ------------- test/core.jl | 24 ++++++++++++++++++++++++ 6 files changed, 42 insertions(+), 46 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index b6d1fea655e89..9a4859682cf31 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -813,9 +813,6 @@ is_method_pure(match::MethodMatch) = is_method_pure(match.method, match.spec_typ function pure_eval_call(interp::AbstractInterpreter, @nospecialize(f), applicable::Vector{Any}, arginfo::ArgInfo) pure_eval_eligible(interp, f, applicable, arginfo) || return nothing - return _pure_eval_call(f, arginfo) -end -function _pure_eval_call(@nospecialize(f), arginfo::ArgInfo) args = collect_const_args(arginfo, #=start=#2) value = try Core._apply_pure(f, args) @@ -2041,26 +2038,8 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), end argtypes = Any[typeof(<:), argtypes[3], argtypes[2]] return abstract_call_known(interp, <:, ArgInfo(fargs, argtypes), si, sv, max_methods) - elseif la == 2 && - (a2 = argtypes[2]; isa(a2, Const)) && (svecval = a2.val; isa(svecval, SimpleVector)) && - istopfunction(f, :length) - # mark length(::SimpleVector) as @pure - return CallMeta(Const(length(svecval)), EFFECTS_TOTAL, MethodResultPure()) - elseif la == 3 && - (a2 = argtypes[2]; isa(a2, Const)) && (svecval = a2.val; isa(svecval, SimpleVector)) && - (a3 = argtypes[3]; isa(a3, Const)) && (idx = a3.val; isa(idx, Int)) && - istopfunction(f, :getindex) - # mark getindex(::SimpleVector, i::Int) as @pure - if 1 <= idx <= length(svecval) && isassigned(svecval, idx) - return CallMeta(Const(getindex(svecval, idx)), EFFECTS_TOTAL, MethodResultPure()) - end elseif la == 2 && istopfunction(f, :typename) return CallMeta(typename_static(argtypes[2]), EFFECTS_TOTAL, MethodResultPure()) - elseif la == 3 && istopfunction(f, :typejoin) - if is_all_const_arg(arginfo, #=start=#2) - val = _pure_eval_call(f, arginfo) - return CallMeta(val === nothing ? Type : val, EFFECTS_TOTAL, MethodResultPure()) - end elseif f === Core._hasmethod return _hasmethod_tfunc(interp, argtypes, sv) end diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index e7fa8856c6101..486f5b2c0c625 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -1693,14 +1693,6 @@ function linear_inline_eligible(ir::IRCode) return true end -# Check for a number of functions known to be pure -function ispuretopfunction(@nospecialize(f)) - return istopfunction(f, :typejoin) || - istopfunction(f, :isbits) || - istopfunction(f, :isbitstype) || - istopfunction(f, :promote_type) -end - function early_inline_special_case( ir::IRCode, stmt::Expr, @nospecialize(type), sig::Signature, state::InliningState) @@ -1714,7 +1706,7 @@ function early_inline_special_case( if is_pure_intrinsic_infer(f) && intrinsic_nothrow(f, argtypes[2:end]) return SomeCase(quoted(val)) end - elseif ispuretopfunction(f) || contains_is(_PURE_BUILTINS, f) + elseif contains_is(_PURE_BUILTINS, f) return SomeCase(quoted(val)) elseif contains_is(_EFFECT_FREE_BUILTINS, f) if _builtin_nothrow(optimizer_lattice(state.interp), f, argtypes[2:end], type) diff --git a/base/essentials.jl b/base/essentials.jl index 07e8db31fb0f1..a0d00c942ecd5 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -177,6 +177,19 @@ macro isdefined(s::Symbol) return Expr(:escape, Expr(:isdefined, s)) end +""" + nameof(m::Module) -> Symbol + +Get the name of a `Module` as a [`Symbol`](@ref). + +# Examples +```jldoctest +julia> nameof(Base.Broadcast) +:Broadcast +``` +""" +nameof(m::Module) = ccall(:jl_module_name, Ref{Symbol}, (Any,), m) + function _is_internal(__module__) if ccall(:jl_base_relative_to, Any, (Any,), __module__)::Module === Core.Compiler || nameof(__module__) === :Base @@ -696,8 +709,9 @@ end # SimpleVector -@eval getindex(v::SimpleVector, i::Int) = Core._svec_ref($(Expr(:boundscheck)), v, i) +@eval getindex(v::SimpleVector, i::Int) = (@_foldable_meta; Core._svec_ref($(Expr(:boundscheck)), v, i)) function length(v::SimpleVector) + @_total_meta t = @_gc_preserve_begin v len = unsafe_load(Ptr{Int}(pointer_from_objref(v))) @_gc_preserve_end t diff --git a/base/promotion.jl b/base/promotion.jl index fb5c5b83864ae..993f0be6c0924 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -19,9 +19,9 @@ Number """ typejoin() = Bottom typejoin(@nospecialize(t)) = t -typejoin(@nospecialize(t), ts...) = (@_total_meta; typejoin(t, typejoin(ts...))) +typejoin(@nospecialize(t), ts...) = (@_foldable_meta; typejoin(t, typejoin(ts...))) function typejoin(@nospecialize(a), @nospecialize(b)) - @_total_meta + @_foldable_meta if isa(a, TypeVar) return typejoin(a.ub, b) elseif isa(b, TypeVar) diff --git a/base/reflection.jl b/base/reflection.jl index 22c4698e5c31c..9e2615a16a190 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -2,19 +2,6 @@ # name and module reflection -""" - nameof(m::Module) -> Symbol - -Get the name of a `Module` as a [`Symbol`](@ref). - -# Examples -```jldoctest -julia> nameof(Base.Broadcast) -:Broadcast -``` -""" -nameof(m::Module) = ccall(:jl_module_name, Ref{Symbol}, (Any,), m) - """ parentmodule(m::Module) -> Module diff --git a/test/core.jl b/test/core.jl index adf895ba471ea..f1a7c3fafeab3 100644 --- a/test/core.jl +++ b/test/core.jl @@ -275,6 +275,30 @@ let mi = T26321{3,NTuple{3,Int}}((1,2,3)), mf = T26321{3,NTuple{3,Float64}}((1.0 @test a isa Vector{<:T26321{3}} end +@test Base.return_types() do + typejoin(Int, UInt) +end |> only == Type{typejoin(Int, UInt)} +@test Base.return_types() do + typejoin(Int, UInt, Float64) +end |> only == Type{typejoin(Int, UInt, Float64)} + +let res = @test_throws TypeError let + Base.Experimental.@force_compile + typejoin(1, 2) + nothing + end + err = res.value + @test err.func === :<: +end +let res = @test_throws TypeError let + Base.Experimental.@force_compile + typejoin(1, 2, 3) + nothing + end + err = res.value + @test err.func === :<: +end + # promote_typejoin returns a Union only with Nothing/Missing combined with concrete types for T in (Nothing, Missing) @test Base.promote_typejoin(Int, Float64) === Real From 94022b11ab49b1b95f858685bbc9f939eadd5ba7 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Thu, 16 Feb 2023 11:00:29 -0500 Subject: [PATCH 140/775] [Openblas] Remove spurious local directory --- deps/openblas.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/openblas.mk b/deps/openblas.mk index f949143f393b1..e2837bc47232a 100644 --- a/deps/openblas.mk +++ b/deps/openblas.mk @@ -5,7 +5,7 @@ OPENBLAS_GIT_URL := https://github.com/xianyi/OpenBLAS.git OPENBLAS_TAR_URL = https://api.github.com/repos/xianyi/OpenBLAS/tarball/$1 $(eval $(call git-external,openblas,OPENBLAS,,,$(BUILDDIR))) -OPENBLAS_BUILD_OPTS := CC="$(CC) $(SANITIZE_OPTS)" FC="$(FC) $(SANITIZE_OPTS) -L/home/keno/julia-msan/usr/lib" LD="$(LD) $(SANITIZE_LDFLAGS)" RANLIB="$(RANLIB)" BINARY=$(BINARY) +OPENBLAS_BUILD_OPTS := CC="$(CC) $(SANITIZE_OPTS)" FC="$(FC) $(SANITIZE_OPTS)" LD="$(LD) $(SANITIZE_LDFLAGS)" RANLIB="$(RANLIB)" BINARY=$(BINARY) # Thread support ifeq ($(OPENBLAS_USE_THREAD), 1) From 2ce9060b13b4455dec73d07b12a3a270d16ba371 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 17 Feb 2023 01:14:07 +0900 Subject: [PATCH 141/775] deprecate `@pure` and make it just alias to `@assume_effects :foldable` (#48682) Co-authored-by: Oscar Smith Co-authored-by: Valentin Churavy --- NEWS.md | 2 +- base/compiler/abstractinterpretation.jl | 40 +--------------- .../ssair/EscapeAnalysis/EscapeAnalysis.jl | 1 + .../ssair/EscapeAnalysis/interprocedural.jl | 1 + base/compiler/typeinfer.jl | 1 - base/deprecated.jl | 28 +++++++++++ base/essentials.jl | 4 -- base/expr.jl | 27 ----------- doc/src/base/base.md | 1 - doc/src/devdocs/ast.md | 7 +-- src/ast.c | 2 - src/ircode.c | 16 +------ src/jl_exported_funcs.inc | 1 - src/jltypes.c | 12 ++--- src/julia.h | 3 -- src/julia_internal.h | 2 - src/method.c | 6 +-- stdlib/Serialization/src/Serialization.jl | 7 +-- test/compiler/AbstractInterpreter.jl | 4 +- test/compiler/contextual.jl | 12 ----- test/compiler/inference.jl | 47 +------------------ test/compiler/inline.jl | 12 ----- test/core.jl | 2 +- 23 files changed, 49 insertions(+), 189 deletions(-) diff --git a/NEWS.md b/NEWS.md index 976c88ee65cb8..91b47d832eb29 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,7 +10,7 @@ Language changes Compiler/Runtime improvements ----------------------------- - +* The `@pure` macro is now deprecated. Use `Base.@assume_effects :foldable` instead ([#48682]). Command-line option changes --------------------------- diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 9a4859682cf31..0d0280e40e817 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -96,15 +96,11 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), all_effects = EFFECTS_TOTAL if !matches.nonoverlayed # currently we don't have a good way to execute the overlayed method definition, - # so we should give up pure/concrete eval when any of the matched methods is overlayed + # so we should give up concrete eval when any of the matched methods is overlayed f = nothing all_effects = Effects(all_effects; nonoverlayed=false) end - # try pure-evaluation - val = pure_eval_call(interp, f, applicable, arginfo) - val !== nothing && return CallMeta(val, all_effects, MethodResultPure(info)) # TODO: add some sort of edge(s) - 𝕃ₚ = ipo_lattice(interp) for i in 1:napplicable match = applicable[i]::MethodMatch @@ -788,40 +784,6 @@ struct MethodCallResult end end -function pure_eval_eligible(interp::AbstractInterpreter, - @nospecialize(f), applicable::Vector{Any}, arginfo::ArgInfo) - # XXX we need to check that this pure function doesn't call any overlayed method - return f !== nothing && - length(applicable) == 1 && - is_method_pure(applicable[1]::MethodMatch) && - is_all_const_arg(arginfo, #=start=#2) -end - -function is_method_pure(method::Method, @nospecialize(sig), sparams::SimpleVector) - if isdefined(method, :generator) - method.generator.expand_early || return false - mi = specialize_method(method, sig, sparams) - isa(mi, MethodInstance) || return false - staged = get_staged(mi) - (staged isa CodeInfo && (staged::CodeInfo).pure) || return false - return true - end - return method.pure -end -is_method_pure(match::MethodMatch) = is_method_pure(match.method, match.spec_types, match.sparams) - -function pure_eval_call(interp::AbstractInterpreter, - @nospecialize(f), applicable::Vector{Any}, arginfo::ArgInfo) - pure_eval_eligible(interp, f, applicable, arginfo) || return nothing - args = collect_const_args(arginfo, #=start=#2) - value = try - Core._apply_pure(f, args) - catch - return nothing - end - return Const(value) -end - # - true: eligible for concrete evaluation # - false: eligible for semi-concrete evaluation # - nothing: not eligible for either of it diff --git a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl index 729e9a9a49b94..8bc173add6eaa 100644 --- a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl +++ b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl @@ -791,6 +791,7 @@ function compute_frameinfo(ir::IRCode, call_resolved::Bool) check_effect_free!(ir, idx, stmt, inst[:type], 𝕃ₒ) end if callinfo !== nothing && isexpr(stmt, :call) + # TODO: pass effects here callinfo[idx] = resolve_call(ir, stmt, inst[:info]) elseif isexpr(stmt, :enter) @assert idx ≤ nstmts "try/catch inside new_nodes unsupported" diff --git a/base/compiler/ssair/EscapeAnalysis/interprocedural.jl b/base/compiler/ssair/EscapeAnalysis/interprocedural.jl index d87b0edaf295e..26b0e5b404641 100644 --- a/base/compiler/ssair/EscapeAnalysis/interprocedural.jl +++ b/base/compiler/ssair/EscapeAnalysis/interprocedural.jl @@ -14,6 +14,7 @@ struct EACallInfo end function resolve_call(ir::IRCode, stmt::Expr, @nospecialize(info::CallInfo)) + # TODO: if effect free, return true sig = call_sig(ir, stmt) if sig === nothing return missing diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 55ae33c87e96b..f7723a968da3e 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -1010,7 +1010,6 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) tree.linetable = LineInfoNode[LineInfoNode(method.module, method.name, method.file, method.line, Int32(0))] tree.inferred = true tree.ssaflags = UInt8[0] - tree.pure = true set_inlineable!(tree, true) tree.parent = mi tree.rettype = Core.Typeof(rettype_const) diff --git a/base/deprecated.jl b/base/deprecated.jl index 79ae852ff22b1..3e18cfcf918fb 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -362,3 +362,31 @@ end end # END 1.9 deprecations + +# BEGIN 1.10 deprecations + +""" + @pure ex + +`@pure` gives the compiler a hint for the definition of a pure function, +helping for type inference. + +!!! warning + This macro is intended for internal compiler use and may be subject to changes. + +!!! warning + In Julia 1.8 and higher, it is favorable to use [`@assume_effects`](@ref) instead of `@pure`. + This is because `@assume_effects` allows a finer grained control over Julia's purity + modeling and the effect system enables a wider range of optimizations. + +!!! note + In Julia 1.10 this is deprecated in favor of [`@assume_effects`](@ref). + Specifically, `@assume_effects :foldable` provides similar guarentees. +""" +macro pure(ex) + f, l = __source__.file, __source__.line + @warn "`Base.@pure ex` at $f:$l is deprecated, use `Base.@assume_effects :foldable ex` instead." + return esc(:(Base.@assume_effects :foldable $ex)) +end + +# END 1.10 deprecations diff --git a/base/essentials.jl b/base/essentials.jl index a0d00c942ecd5..fc79f88f5c0b8 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -198,10 +198,6 @@ function _is_internal(__module__) return false end -# can be used in place of `@pure` (supposed to be used for bootstrapping) -macro _pure_meta() - return _is_internal(__module__) && Expr(:meta, :pure) -end # can be used in place of `@assume_effects :total` (supposed to be used for bootstrapping) macro _total_meta() return _is_internal(__module__) && Expr(:meta, Expr(:purity, diff --git a/base/expr.jl b/base/expr.jl index 0e6d73c9722d1..46e89bf64da8a 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -339,23 +339,6 @@ macro noinline(x) return annotate_meta_def_or_block(x, :noinline) end -""" - @pure ex - -`@pure` gives the compiler a hint for the definition of a pure function, -helping for type inference. - -!!! warning - This macro is intended for internal compiler use and may be subject to changes. - -!!! warning - In Julia 1.8 and higher, it is favorable to use [`@assume_effects`](@ref) instead of `@pure`. - This is because `@assume_effects` allows a finer grained control over Julia's purity - modeling and the effect system enables a wider range of optimizations. -""" -macro pure(ex) - esc(isa(ex, Expr) ? pushmeta!(ex, :pure) : ex) -end """ @constprop setting [ex] @@ -703,16 +686,6 @@ the following other `setting`s: Effect names may be prefixed by `!` to indicate that the effect should be removed from an earlier meta effect. For example, `:total !:nothrow` indicates that while the call is generally total, it may however throw. - ---- -## Comparison to `@pure` - -`@assume_effects :foldable` is similar to [`@pure`](@ref) with the primary -distinction that the `:consistent`-cy requirement applies world-age wise rather -than globally as described above. However, in particular, a method annotated -`@pure` should always be at least `:foldable`. -Another advantage is that effects introduced by `@assume_effects` are propagated to -callers interprocedurally while a purity defined by `@pure` is not. """ macro assume_effects(args...) lastex = args[end] diff --git a/doc/src/base/base.md b/doc/src/base/base.md index 2159a9ad9a9fc..7e45e2176478d 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -293,7 +293,6 @@ Base.@label Base.@simd Base.@polly Base.@generated -Base.@pure Base.@assume_effects Base.@deprecate ``` diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index 9ada683b1ddb0..6984a68cc5508 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -581,7 +581,7 @@ A unique'd container describing the shared metadata for a single method. Pointers to non-AST things that have been interpolated into the AST, required by compression of the AST, type-inference, or the generation of native code. - * `nargs`, `isva`, `called`, `isstaged`, `pure` + * `nargs`, `isva`, `called`, `is_for_opaque_closure`, Descriptive bit-fields for the source code of this Method. @@ -759,11 +759,6 @@ Boolean properties: Whether this should propagate `@inbounds` when inlined for the purpose of eliding `@boundscheck` blocks. - * `pure` - - Whether this is known to be a pure function of its arguments, without respect to the - state of the method caches or other mutable global state. - `UInt8` settings: diff --git a/src/ast.c b/src/ast.c index 0ff7c882ab8e7..ca22fb463ce9c 100644 --- a/src/ast.c +++ b/src/ast.c @@ -66,7 +66,6 @@ JL_DLLEXPORT jl_sym_t *jl_boundscheck_sym; JL_DLLEXPORT jl_sym_t *jl_inbounds_sym; JL_DLLEXPORT jl_sym_t *jl_copyast_sym; JL_DLLEXPORT jl_sym_t *jl_cfunction_sym; -JL_DLLEXPORT jl_sym_t *jl_pure_sym; JL_DLLEXPORT jl_sym_t *jl_loopinfo_sym; JL_DLLEXPORT jl_sym_t *jl_meta_sym; JL_DLLEXPORT jl_sym_t *jl_inert_sym; @@ -328,7 +327,6 @@ void jl_init_common_symbols(void) jl_newvar_sym = jl_symbol("newvar"); jl_copyast_sym = jl_symbol("copyast"); jl_loopinfo_sym = jl_symbol("loopinfo"); - jl_pure_sym = jl_symbol("pure"); jl_meta_sym = jl_symbol("meta"); jl_list_sym = jl_symbol("list"); jl_unused_sym = jl_symbol("#unused#"); diff --git a/src/ircode.c b/src/ircode.c index 0e74f7700ebf2..20f826b4fcc7f 100644 --- a/src/ircode.c +++ b/src/ircode.c @@ -434,13 +434,12 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) } } -static jl_code_info_flags_t code_info_flags(uint8_t inferred, uint8_t propagate_inbounds, uint8_t pure, +static jl_code_info_flags_t code_info_flags(uint8_t inferred, uint8_t propagate_inbounds, uint8_t has_fcall, uint8_t inlining, uint8_t constprop) { jl_code_info_flags_t flags; flags.bits.inferred = inferred; flags.bits.propagate_inbounds = propagate_inbounds; - flags.bits.pure = pure; flags.bits.has_fcall = has_fcall; flags.bits.inlining = inlining; flags.bits.constprop = constprop; @@ -781,7 +780,7 @@ JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) 1 }; - jl_code_info_flags_t flags = code_info_flags(code->inferred, code->propagate_inbounds, code->pure, + jl_code_info_flags_t flags = code_info_flags(code->inferred, code->propagate_inbounds, code->has_fcall, code->inlining, code->constprop); write_uint8(s.s, flags.packed); write_uint8(s.s, code->purity.bits); @@ -880,7 +879,6 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t code->constprop = flags.bits.constprop; code->inferred = flags.bits.inferred; code->propagate_inbounds = flags.bits.propagate_inbounds; - code->pure = flags.bits.pure; code->has_fcall = flags.bits.has_fcall; code->purity.bits = read_uint8(s.s); code->inlining_cost = read_uint16(s.s); @@ -958,16 +956,6 @@ JL_DLLEXPORT uint8_t jl_ir_flag_inlining(jl_array_t *data) return flags.bits.inlining; } -JL_DLLEXPORT uint8_t jl_ir_flag_pure(jl_array_t *data) -{ - if (jl_is_code_info(data)) - return ((jl_code_info_t*)data)->pure; - assert(jl_typeis(data, jl_array_uint8_type)); - jl_code_info_flags_t flags; - flags.packed = ((uint8_t*)data->data)[0]; - return flags.bits.pure; -} - JL_DLLEXPORT uint8_t jl_ir_flag_has_fcall(jl_array_t *data) { if (jl_is_code_info(data)) diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index c475184573faa..c5389978217d6 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -274,7 +274,6 @@ XX(jl_ios_fd) \ XX(jl_ios_get_nbyte_int) \ XX(jl_ir_flag_inferred) \ - XX(jl_ir_flag_pure) \ XX(jl_ir_flag_has_fcall) \ XX(jl_ir_flag_inlining) \ XX(jl_ir_inlining_cost) \ diff --git a/src/jltypes.c b/src/jltypes.c index d9f50d67d3f73..8d586a429d4a4 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2433,7 +2433,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_code_info_type = jl_new_datatype(jl_symbol("CodeInfo"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(22, + jl_perm_symsvec(21, "code", "codelocs", "ssavaluetypes", @@ -2450,13 +2450,12 @@ void jl_init_types(void) JL_GC_DISABLED "max_world", "inferred", "propagate_inbounds", - "pure", "has_fcall", "inlining", "constprop", "purity", "inlining_cost"), - jl_svec(22, + jl_svec(21, jl_array_any_type, jl_array_int32_type, jl_any_type, @@ -2474,7 +2473,6 @@ void jl_init_types(void) JL_GC_DISABLED jl_bool_type, jl_bool_type, jl_bool_type, - jl_bool_type, jl_uint8_type, jl_uint8_type, jl_uint8_type, @@ -2485,7 +2483,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_method_type = jl_new_datatype(jl_symbol("Method"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(29, + jl_perm_symsvec(28, "name", "module", "file", @@ -2511,11 +2509,10 @@ void jl_init_types(void) JL_GC_DISABLED "nospecialize", "nkw", "isva", - "pure", "is_for_opaque_closure", "constprop", "purity"), - jl_svec(29, + jl_svec(28, jl_symbol_type, jl_module_type, jl_symbol_type, @@ -2542,7 +2539,6 @@ void jl_init_types(void) JL_GC_DISABLED jl_int32_type, jl_bool_type, jl_bool_type, - jl_bool_type, jl_uint8_type, jl_uint8_type), jl_emptysvec, diff --git a/src/julia.h b/src/julia.h index 03efa773d026c..b84f3305dd021 100644 --- a/src/julia.h +++ b/src/julia.h @@ -284,7 +284,6 @@ typedef struct _jl_code_info_t { // various boolean properties: uint8_t inferred; uint8_t propagate_inbounds; - uint8_t pure; uint8_t has_fcall; // uint8 settings uint8_t inlining; // 0 = default; 1 = @inline; 2 = @noinline @@ -342,7 +341,6 @@ typedef struct _jl_method_t { // of another method. // various boolean properties uint8_t isva; - uint8_t pure; uint8_t is_for_opaque_closure; // uint8 settings uint8_t constprop; // 0x00 = use heuristic; 0x01 = aggressive; 0x02 = none @@ -1843,7 +1841,6 @@ JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code); JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t *metadata, jl_array_t *data); JL_DLLEXPORT uint8_t jl_ir_flag_inferred(jl_array_t *data) JL_NOTSAFEPOINT; JL_DLLEXPORT uint8_t jl_ir_flag_inlining(jl_array_t *data) JL_NOTSAFEPOINT; -JL_DLLEXPORT uint8_t jl_ir_flag_pure(jl_array_t *data) JL_NOTSAFEPOINT; JL_DLLEXPORT uint8_t jl_ir_flag_has_fcall(jl_array_t *data) JL_NOTSAFEPOINT; JL_DLLEXPORT uint16_t jl_ir_inlining_cost(jl_array_t *data) JL_NOTSAFEPOINT; JL_DLLEXPORT ssize_t jl_ir_nslots(jl_array_t *data) JL_NOTSAFEPOINT; diff --git a/src/julia_internal.h b/src/julia_internal.h index 21ac6dc4205ea..8751281a9d174 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -598,7 +598,6 @@ STATIC_INLINE jl_value_t *undefref_check(jl_datatype_t *dt, jl_value_t *v) JL_NO typedef struct { uint8_t inferred:1; uint8_t propagate_inbounds:1; - uint8_t pure:1; uint8_t has_fcall:1; uint8_t inlining:2; // 0 = use heuristic; 1 = aggressive; 2 = none uint8_t constprop:2; // 0 = use heuristic; 1 = aggressive; 2 = none @@ -1570,7 +1569,6 @@ extern JL_DLLEXPORT jl_sym_t *jl_boundscheck_sym; extern JL_DLLEXPORT jl_sym_t *jl_inbounds_sym; extern JL_DLLEXPORT jl_sym_t *jl_copyast_sym; extern JL_DLLEXPORT jl_sym_t *jl_cfunction_sym; -extern JL_DLLEXPORT jl_sym_t *jl_pure_sym; extern JL_DLLEXPORT jl_sym_t *jl_loopinfo_sym; extern JL_DLLEXPORT jl_sym_t *jl_meta_sym; extern JL_DLLEXPORT jl_sym_t *jl_inert_sym; diff --git a/src/method.c b/src/method.c index b1f4051e28a82..cce217230968c 100644 --- a/src/method.c +++ b/src/method.c @@ -312,9 +312,7 @@ static void jl_code_info_set_ir(jl_code_info_t *li, jl_expr_t *ir) jl_array_t *meta = ((jl_expr_t*)st)->args; for (k = 0; k < na; k++) { jl_value_t *ma = jl_array_ptr_ref(meta, k); - if (ma == (jl_value_t*)jl_pure_sym) - li->pure = 1; - else if (ma == (jl_value_t*)jl_inline_sym) + if (ma == (jl_value_t*)jl_inline_sym) li->inlining = 1; else if (ma == (jl_value_t*)jl_noinline_sym) li->inlining = 2; @@ -474,7 +472,6 @@ JL_DLLEXPORT jl_code_info_t *jl_new_code_info_uninit(void) src->max_world = ~(size_t)0; src->inferred = 0; src->propagate_inbounds = 0; - src->pure = 0; src->has_fcall = 0; src->edges = jl_nothing; src->constprop = 0; @@ -678,7 +675,6 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) } } m->called = called; - m->pure = src->pure; m->constprop = src->constprop; m->purity.bits = src->purity.bits; jl_add_function_name_to_lineinfo(src, (jl_value_t*)m->name); diff --git a/stdlib/Serialization/src/Serialization.jl b/stdlib/Serialization/src/Serialization.jl index a45523bc94d7d..b11df48daa08f 100644 --- a/stdlib/Serialization/src/Serialization.jl +++ b/stdlib/Serialization/src/Serialization.jl @@ -79,7 +79,7 @@ const TAGS = Any[ @assert length(TAGS) == 255 -const ser_version = 22 # do not make changes without bumping the version #! +const ser_version = 23 # do not make changes without bumping the version #! format_version(::AbstractSerializer) = ser_version format_version(s::Serializer) = s.version @@ -1060,7 +1060,6 @@ function deserialize(s::AbstractSerializer, ::Type{Method}) if template !== nothing # TODO: compress template meth.source = template::CodeInfo - meth.pure = template.pure if !@isdefined(slot_syms) slot_syms = ccall(:jl_compress_argnames, Ref{String}, (Any,), meth.source.slotnames) end @@ -1191,7 +1190,9 @@ function deserialize(s::AbstractSerializer, ::Type{CodeInfo}) end end ci.propagate_inbounds = deserialize(s) - ci.pure = deserialize(s) + if format_version(s) < 23 + deserialize(s) # `pure` field has been removed + end if format_version(s) >= 20 ci.has_fcall = deserialize(s) end diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index ac1f34743e18e..57281fa3ad723 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -93,7 +93,7 @@ overlay_match(::Any) = nothing overlay_match(x) end |> only === Union{Nothing,Missing} -# partial pure/concrete evaluation +# partial concrete evaluation @test Base.return_types(; interp=MTOverlayInterp()) do isbitstype(Int) ? nothing : missing end |> only === Nothing @@ -110,7 +110,7 @@ end issue41694(3) == 6 ? nothing : missing end |> only === Nothing -# disable partial pure/concrete evaluation when tainted by any overlayed call +# disable partial concrete evaluation when tainted by any overlayed call Base.@assume_effects :total totalcall(f, args...) = f(args...) @test Base.return_types(; interp=MTOverlayInterp()) do if totalcall(strangesin, 1.0) == cos(1.0) diff --git a/test/compiler/contextual.jl b/test/compiler/contextual.jl index 79285f62b0947..ba13f175c674b 100644 --- a/test/compiler/contextual.jl +++ b/test/compiler/contextual.jl @@ -119,23 +119,11 @@ f() = 2 # Test that MiniCassette is at least somewhat capable by overdubbing gcd @test overdub(Ctx(), gcd, 10, 20) === gcd(10, 20) -# Test that pure propagates for Cassette -Base.@pure isbitstype(T) = Base.isbitstype(T) -f31012(T) = Val(isbitstype(T)) -@test @inferred(overdub(Ctx(), f31012, Int64)) == Val(true) - @generated bar(::Val{align}) where {align} = :(42) foo(i) = i+bar(Val(1)) @test @inferred(overdub(Ctx(), foo, 1)) == 43 -# Check that misbehaving pure functions propagate their error -Base.@pure func1() = 42 -Base.@pure func2() = (this_is_an_exception; func1()) -func3() = func2() -@test_throws UndefVarError func3() - - # overlay method tables # ===================== diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 09c687ecadf3b..48105f2bb8029 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -563,20 +563,6 @@ f18450() = ifelse(true, Tuple{Vararg{Int}}, Tuple{Vararg}) # issue #18569 @test !Core.Compiler.isconstType(Type{Tuple}) -# ensure pure attribute applies correctly to all signatures of fpure -Base.@pure function fpure(a=rand(); b=rand()) - # use the `rand` function since it is known to be `@inline` - # but would be too big to inline - return a + b + rand() -end - -@test which(fpure, ()).pure -@test which(fpure, (typeof(pi),)).pure - -# Make sure @pure works for functions using the new syntax -Base.@pure (fpure2(x::T) where T) = T -@test which(fpure2, (Int64,)).pure - # issue #10880 function cat10880(a, b) Tuple{a.parameters..., b.parameters...} @@ -912,28 +898,6 @@ end f20267(x::T20267{T}, y::T) where (T) = f20267(Any[1][1], x.inds) @test Base.return_types(f20267, (Any, Any)) == Any[Union{}] -# issue #20704 -f20704(::Int) = 1 -Base.@pure b20704(@nospecialize(x)) = f20704(x) -@test b20704(42) === 1 -@test_throws MethodError b20704(42.0) - -bb20704() = b20704(Any[1.0][1]) -@test_throws MethodError bb20704() - -v20704() = Val{b20704(Any[1.0][1])} -@test_throws MethodError v20704() -@test Base.return_types(v20704, ()) == Any[Type{Val{1}}] - -Base.@pure g20704(::Int) = 1 -h20704(@nospecialize(x)) = g20704(x) -@test g20704(1) === 1 -@test_throws MethodError h20704(1.2) - -Base.@pure c20704() = (f20704(1.0); 1) -d20704() = c20704() -@test_throws MethodError d20704() - #issue #21065, elision of _apply_iterate when splatted expression is not effect_free function f21065(x,y) println("x=$x, y=$y") @@ -1209,13 +1173,6 @@ let typeargs = Tuple{Type{Int},Type{Int},Type{Int},Type{Int},Type{Int},Type{Int} @test only(Base.return_types(promote_type, typeargs)) === Type{Int} end -# demonstrate that inference must converge -# while doing constant propagation -Base.@pure plus1(x) = x + 1 -f21933(x::Val{T}) where {T} = f(Val(plus1(T))) -code_typed(f21933, (Val{1},)) -Base.return_types(f21933, (Val{1},)) - function count_specializations(method::Method) specs = method.specializations n = count(i -> isassigned(specs, i), 1:length(specs)) @@ -4712,8 +4669,8 @@ end |> only === Type{Float64} global it_count47688 = 0 struct CountsIterate47688{N}; end function Base.iterate(::CountsIterate47688{N}, n=0) where N - global it_count47688 += 1 - n <= N ? (n, n+1) : nothing + global it_count47688 += 1 + n <= N ? (n, n+1) : nothing end foo47688() = tuple(CountsIterate47688{5}()...) bar47688() = foo47688() diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index cfcfc7228b3ed..28713ea857c07 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -360,18 +360,6 @@ struct RealConstrained{T <: Real}; end @test !fully_eliminated(x->(RealConstrained{x}; nothing), Tuple{Int}) @test !fully_eliminated(x->(RealConstrained{x}; nothing), Tuple{Type{Vector{T}} where T}) -# Check that pure functions with non-inlineable results still get deleted -struct Big - x::NTuple{1024, Int} -end -Base.@pure Big() = Big(ntuple(identity, 1024)) -function pure_elim_full() - Big() - nothing -end - -@test fully_eliminated(pure_elim_full, Tuple{}) - # Union splitting of convert f_convert_missing(x) = convert(Int64, x) let ci = code_typed(f_convert_missing, Tuple{Union{Int64, Missing}})[1][1], diff --git a/test/core.jl b/test/core.jl index f1a7c3fafeab3..8af7421ba7501 100644 --- a/test/core.jl +++ b/test/core.jl @@ -15,7 +15,7 @@ include("testenv.jl") for (T, c) in ( (Core.CodeInfo, []), (Core.CodeInstance, [:def, :rettype, :rettype_const, :ipo_purity_bits, :argescapes]), - (Core.Method, [#=:name, :module, :file, :line, :primary_world, :sig, :slot_syms, :external_mt, :nargs, :called, :nospecialize, :nkw, :isva, :pure, :is_for_opaque_closure, :constprop=#]), + (Core.Method, [#=:name, :module, :file, :line, :primary_world, :sig, :slot_syms, :external_mt, :nargs, :called, :nospecialize, :nkw, :isva, :is_for_opaque_closure, :constprop=#]), (Core.MethodInstance, [#=:def, :specTypes, :sparam_vals=#]), (Core.MethodTable, [:module]), (Core.TypeMapEntry, [:sig, :simplesig, :guardsigs, :min_world, :max_world, :func, :isleafsig, :issimplesig, :va]), From 49385b9545fb79df16cee08a1c9eae5b2e04286f Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki Date: Thu, 16 Feb 2023 19:07:34 +0900 Subject: [PATCH 142/775] remove `Slot` type from the compiler Since `TypedSlot` is now used as a very temporary object that only appears in a very specific point of inference. --- base/boot.jl | 11 ++++++----- base/compiler/optimize.jl | 8 ++++---- base/compiler/ssair/passes.jl | 4 ++-- base/compiler/utilities.jl | 7 +++++-- base/compiler/validation.jl | 18 +++++++++--------- base/show.jl | 4 ++-- doc/src/devdocs/ast.md | 17 +++++++++-------- src/ast.c | 2 +- src/builtins.c | 1 - src/ircode.c | 2 +- src/jl_exported_data.inc | 1 - src/jltypes.c | 7 ++----- src/julia.h | 1 - src/staticdata.c | 3 +-- test/compiler/contextual.jl | 4 ++-- test/compiler/inline.jl | 2 +- test/syntax.jl | 2 +- 17 files changed, 46 insertions(+), 48 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index 8a5286951e281..1f1e4535fe55c 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -521,17 +521,18 @@ Symbol(s::Symbol) = s # module providing the IR object model module IR + export CodeInfo, MethodInstance, CodeInstance, GotoNode, GotoIfNot, ReturnNode, - NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot, Argument, + NewvarNode, SSAValue, SlotNumber, TypedSlot, Argument, PiNode, PhiNode, PhiCNode, UpsilonNode, LineInfoNode, - Const, PartialStruct + Const, PartialStruct, InterConditional import Core: CodeInfo, MethodInstance, CodeInstance, GotoNode, GotoIfNot, ReturnNode, - NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot, Argument, + NewvarNode, SSAValue, SlotNumber, TypedSlot, Argument, PiNode, PhiNode, PhiCNode, UpsilonNode, LineInfoNode, - Const, PartialStruct + Const, PartialStruct, InterConditional -end +end # module IR # docsystem basics const unescape = Symbol("hygienic-scope") diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index d0303168c834f..6e75aad23dee6 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -242,13 +242,13 @@ function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospe isa(stmt, ReturnNode) && return (true, false, true) isa(stmt, GotoNode) && return (true, false, true) isa(stmt, GotoIfNot) && return (true, false, ⊑(𝕃ₒ, argextype(stmt.cond, src), Bool)) - isa(stmt, Slot) && return (true, false, false) # Slots shouldn't occur in the IR at this point, but let's be defensive here - if isa(stmt, GlobalRef) + if isa(stmt, SlotNumber) || isa(stmt, TypedSlot) + return (true, false, false) # they shouldn't occur in the IR at this point, but let's be defensive here + elseif isa(stmt, GlobalRef) nothrow = isdefined(stmt.mod, stmt.name) consistent = nothrow && isconst(stmt.mod, stmt.name) return (consistent, nothrow, nothrow) - end - if isa(stmt, Expr) + elseif isa(stmt, Expr) (; head, args) = stmt if head === :static_parameter # if we aren't certain enough about the type, it might be an UndefVarError at runtime diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index c19f290fd80f3..aa83b00d32ee5 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1708,8 +1708,8 @@ function type_lift_pass!(ir::IRCode) # a phi node (or an UpsilonNode() argument to a PhiC node), so lift # all these nodes that have maybe undef values val = stmt.args[(stmt.head === :isdefined) ? 1 : 2] - if stmt.head === :isdefined && (val isa Slot || val isa GlobalRef || - isexpr(val, :static_parameter) || val isa Argument || val isa Symbol) + if stmt.head === :isdefined && (val isa GlobalRef || isexpr(val, :static_parameter) || + val isa Argument || val isa Symbol) # this is a legal node, so assume it was not introduced by # slot2ssa (at worst, we might leave in a runtime check that # shouldn't have been there) diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 0c263931d8fd2..6cf600560902d 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -482,8 +482,11 @@ function find_throw_blocks(code::Vector{Any}, handler_at::Vector{Int}) end # using a function to ensure we can infer this -@inline slot_id(s) = isa(s, SlotNumber) ? (s::SlotNumber).id : - isa(s, Argument) ? (s::Argument).n : (s::TypedSlot).id +@inline function slot_id(s) + isa(s, SlotNumber) && return s.id + isa(s, Argument) && return s.n + return (s::TypedSlot).id +end ########### # options # diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl index 0931686184a2e..61cc2c5dcfcf8 100644 --- a/base/compiler/validation.jl +++ b/base/compiler/validation.jl @@ -103,11 +103,11 @@ function _validate_val!(@nospecialize(x), errors, ssavals::BitSet) end """ - validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo) + validate_code!(errors::Vector{InvalidCodeError}, c::CodeInfo) Validate `c`, logging any violation by pushing an `InvalidCodeError` into `errors`. """ -function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_level::Bool = false) +function validate_code!(errors::Vector{InvalidCodeError}, c::CodeInfo, is_top_level::Bool = false) ssavals = BitSet() lhs_slotnums = BitSet() @@ -199,7 +199,7 @@ function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_ end """ - validate_code!(errors::Vector{>:InvalidCodeError}, mi::MethodInstance, + validate_code!(errors::Vector{InvalidCodeError}, mi::MethodInstance, c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi)) Validate `mi`, logging any violation by pushing an `InvalidCodeError` into `errors`. @@ -207,7 +207,7 @@ Validate `mi`, logging any violation by pushing an `InvalidCodeError` into `erro If `isa(c, CodeInfo)`, also call `validate_code!(errors, c)`. It is assumed that `c` is the `CodeInfo` instance associated with `mi`. """ -function validate_code!(errors::Vector{>:InvalidCodeError}, mi::Core.MethodInstance, +function validate_code!(errors::Vector{InvalidCodeError}, mi::Core.MethodInstance, c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi)) is_top_level = mi.def isa Module if is_top_level @@ -231,13 +231,13 @@ end validate_code(args...) = validate_code!(Vector{InvalidCodeError}(), args...) -is_valid_lvalue(@nospecialize(x)) = isa(x, Slot) || isa(x, GlobalRef) +is_valid_lvalue(@nospecialize(x)) = isa(x, SlotNumber) || isa(x, TypedSlot) || isa(x, GlobalRef) function is_valid_argument(@nospecialize(x)) - if isa(x, Slot) || isa(x, Argument) || isa(x, SSAValue) || isa(x, GlobalRef) || isa(x, QuoteNode) || - (isa(x,Expr) && (x.head in (:static_parameter, :boundscheck))) || - isa(x, Number) || isa(x, AbstractString) || isa(x, AbstractChar) || isa(x, Tuple) || - isa(x, Type) || isa(x, Core.Box) || isa(x, Module) || x === nothing + if isa(x, SlotNumber) || isa(x, TypedSlot) || isa(x, Argument) || isa(x, SSAValue) || + isa(x, GlobalRef) || isa(x, QuoteNode) || isexpr(x, (:static_parameter, :boundscheck)) || + isa(x, Number) || isa(x, AbstractString) || isa(x, AbstractChar) || isa(x, Tuple) || + isa(x, Type) || isa(x, Core.Box) || isa(x, Module) || x === nothing return true end # TODO: consider being stricter about what needs to be wrapped with QuoteNode diff --git a/base/show.jl b/base/show.jl index 3dcdac77afb89..ef354badd8733 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1372,7 +1372,7 @@ show(io::IO, s::Symbol) = show_unquoted_quote_expr(io, s, 0, 0, 0) # eval(Meta.parse("Set{Int64}([2,3,1])")) # ==> An actual set # While this isn’t true of ALL show methods, it is of all ASTs. -const ExprNode = Union{Expr, QuoteNode, Slot, LineNumberNode, SSAValue, +const ExprNode = Union{Expr, QuoteNode, SlotNumber, TypedSlot, LineNumberNode, SSAValue, GotoNode, GlobalRef, PhiNode, PhiCNode, UpsilonNode, Core.Compiler.GotoIfNot, Core.Compiler.ReturnNode} # Operators have precedence levels from 1-N, and show_unquoted defaults to a @@ -1723,7 +1723,7 @@ function show_globalref(io::IO, ex::GlobalRef; allow_macroname=false) nothing end -function show_unquoted(io::IO, ex::Slot, ::Int, ::Int) +function show_unquoted(io::IO, ex::Union{SlotNumber,TypedSlot}, ::Int, ::Int) typ = isa(ex, TypedSlot) ? ex.typ : Any slotid = ex.id slotnames = get(io, :SOURCE_SLOTNAMES, false) diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index 6984a68cc5508..dc1500e913dce 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -249,14 +249,15 @@ types exist in lowered form: While almost every part of a surface AST is represented by an `Expr`, the IR uses only a limited number of `Expr`s, mostly for calls and some top-level-only forms. - * `Slot` + * `SlotNumber` - Identifies arguments and local variables by consecutive numbering. `Slot` is an abstract type - with subtypes `SlotNumber` and `TypedSlot`. Both types have an integer-valued `id` field giving - the slot index. Most slots have the same type at all uses, and so are represented with `SlotNumber`. - The types of these slots are found in the `slottypes` field of their `CodeInfo` object. - Slots that require per-use type annotations are represented with `TypedSlot`, which has a `typ` - field. + Identifies arguments and local variables by consecutive numbering. It has an + integer-valued `id` field giving the slot index. + The types of these slots can be found in the `slottypes` field of their `CodeInfo` object. + When a slot has different types at different uses and thus requires per-use type annotations, + they are converted to temporary `TypedSlot` object. This object has an additional `typ` + field as well as the `id` field. Note that `TypedSlot` only appears in an unoptimized + lowered form that is scheduled for optimization, and it never appears elsewhere. * `Argument` @@ -322,7 +323,7 @@ These symbols appear in the `head` field of [`Expr`](@ref)s in lowered form. * `=` - Assignment. In the IR, the first argument is always a Slot or a GlobalRef. + Assignment. In the IR, the first argument is always a `SlotNumber` or a `GlobalRef`. * `method` diff --git a/src/ast.c b/src/ast.c index ca22fb463ce9c..53dd135082da5 100644 --- a/src/ast.c +++ b/src/ast.c @@ -687,7 +687,7 @@ static value_t julia_to_scm_noalloc2(fl_context_t *fl_ctx, jl_value_t *v, int ch if (jl_is_ssavalue(v)) lerror(fl_ctx, symbol(fl_ctx, "error"), "SSAValue objects should not occur in an AST"); if (jl_is_slot(v)) - lerror(fl_ctx, symbol(fl_ctx, "error"), "Slot objects should not occur in an AST"); + lerror(fl_ctx, symbol(fl_ctx, "error"), "SlotNumber objects should not occur in an AST"); } value_t opaque = cvalue(fl_ctx, jl_ast_ctx(fl_ctx)->jvtype, sizeof(void*)); *(jl_value_t**)cv_data((cvalue_t*)ptr(opaque)) = v; diff --git a/src/builtins.c b/src/builtins.c index b090e952cc1cf..f2acf29299ae0 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -2029,7 +2029,6 @@ void jl_init_primitives(void) JL_GC_DISABLED add_builtin("TypeMapLevel", (jl_value_t*)jl_typemap_level_type); add_builtin("Symbol", (jl_value_t*)jl_symbol_type); add_builtin("SSAValue", (jl_value_t*)jl_ssavalue_type); - add_builtin("Slot", (jl_value_t*)jl_abstractslot_type); add_builtin("SlotNumber", (jl_value_t*)jl_slotnumber_type); add_builtin("TypedSlot", (jl_value_t*)jl_typedslot_type); add_builtin("Argument", (jl_value_t*)jl_argument_type); diff --git a/src/ircode.c b/src/ircode.c index 20f826b4fcc7f..0634d83870fa7 100644 --- a/src/ircode.c +++ b/src/ircode.c @@ -1102,7 +1102,7 @@ void jl_init_serializer(void) jl_densearray_type, jl_function_type, jl_typename_type, jl_builtin_type, jl_task_type, jl_uniontype_type, jl_array_any_type, jl_intrinsic_type, - jl_abstractslot_type, jl_methtable_type, jl_typemap_level_type, + jl_methtable_type, jl_typemap_level_type, jl_voidpointer_type, jl_newvarnode_type, jl_abstractstring_type, jl_array_symbol_type, jl_anytuple_type, jl_tparam0(jl_anytuple_type), jl_emptytuple_type, jl_array_uint8_type, jl_code_info_type, diff --git a/src/jl_exported_data.inc b/src/jl_exported_data.inc index dd38560af1414..d2bc282a59a1b 100644 --- a/src/jl_exported_data.inc +++ b/src/jl_exported_data.inc @@ -3,7 +3,6 @@ // Pointers that are exposed through the public libjulia #define JL_EXPORTED_DATA_POINTERS(XX) \ XX(jl_abstractarray_type) \ - XX(jl_abstractslot_type) \ XX(jl_abstractstring_type) \ XX(jl_an_empty_string) \ XX(jl_an_empty_vec_any) \ diff --git a/src/jltypes.c b/src/jltypes.c index 8d586a429d4a4..e29de852f2442 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2231,15 +2231,12 @@ void jl_init_types(void) JL_GC_DISABLED jl_svec1(jl_long_type), jl_emptysvec, 0, 0, 1); - jl_abstractslot_type = jl_new_abstracttype((jl_value_t*)jl_symbol("Slot"), core, jl_any_type, - jl_emptysvec); - - jl_slotnumber_type = jl_new_datatype(jl_symbol("SlotNumber"), core, jl_abstractslot_type, jl_emptysvec, + jl_slotnumber_type = jl_new_datatype(jl_symbol("SlotNumber"), core, jl_any_type, jl_emptysvec, jl_perm_symsvec(1, "id"), jl_svec1(jl_long_type), jl_emptysvec, 0, 0, 1); - jl_typedslot_type = jl_new_datatype(jl_symbol("TypedSlot"), core, jl_abstractslot_type, jl_emptysvec, + jl_typedslot_type = jl_new_datatype(jl_symbol("TypedSlot"), core, jl_any_type, jl_emptysvec, jl_perm_symsvec(2, "id", "typ"), jl_svec(2, jl_long_type, jl_any_type), jl_emptysvec, 0, 0, 2); diff --git a/src/julia.h b/src/julia.h index b84f3305dd021..be0e76ec11e97 100644 --- a/src/julia.h +++ b/src/julia.h @@ -693,7 +693,6 @@ extern JL_DLLIMPORT jl_datatype_t *jl_typename_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_typename_t *jl_type_typename JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_symbol_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_ssavalue_type JL_GLOBALLY_ROOTED; -extern JL_DLLIMPORT jl_datatype_t *jl_abstractslot_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_slotnumber_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_typedslot_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_argument_type JL_GLOBALLY_ROOTED; diff --git a/src/staticdata.c b/src/staticdata.c index 51ef3d50f767f..5fb3442a6e347 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -98,7 +98,7 @@ extern "C" { // TODO: put WeakRefs on the weak_refs list during deserialization // TODO: handle finalizers -#define NUM_TAGS 159 +#define NUM_TAGS 158 // An array of references that need to be restored from the sysimg // This is a manually constructed dual of the gvars array, which would be produced by codegen for Julia code, for C. @@ -165,7 +165,6 @@ jl_value_t **const*const get_tags(void) { INSERT_TAG(jl_abstractstring_type); INSERT_TAG(jl_array_any_type); INSERT_TAG(jl_intrinsic_type); - INSERT_TAG(jl_abstractslot_type); INSERT_TAG(jl_methtable_type); INSERT_TAG(jl_typemap_level_type); INSERT_TAG(jl_typemap_entry_type); diff --git a/test/compiler/contextual.jl b/test/compiler/contextual.jl index ba13f175c674b..740e985e388df 100644 --- a/test/compiler/contextual.jl +++ b/test/compiler/contextual.jl @@ -8,7 +8,7 @@ module MiniCassette # fancy features, but sufficient to exercise this code path in the compiler. using Core.Compiler: method_instances, retrieve_code_info, CodeInfo, - MethodInstance, SSAValue, GotoNode, GotoIfNot, ReturnNode, Slot, SlotNumber, quoted, + MethodInstance, SSAValue, GotoNode, GotoIfNot, ReturnNode, SlotNumber, quoted, signature_type using Base: _methods_by_ftype using Base.Meta: isexpr @@ -33,7 +33,7 @@ module MiniCassette return Expr(expr.head, map(transform, expr.args)...) elseif isa(expr, GotoNode) return GotoNode(map_ssa_value(SSAValue(expr.label)).id) - elseif isa(expr, Slot) + elseif isa(expr, SlotNumber) return map_slot_number(expr.id) elseif isa(expr, SSAValue) return map_ssa_value(expr) diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 28713ea857c07..37e5bde9d9a48 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -28,7 +28,7 @@ function test_inlined_symbols(func, argtypes) ast = Expr(:block) ast.args = src.code walk(ast) do e - if isa(e, Core.Slot) + if isa(e, Core.SlotNumber) @test 1 <= e.id <= nl end if isa(e, Core.NewvarNode) diff --git a/test/syntax.jl b/test/syntax.jl index 32f343f4a392e..756af45e6b3c7 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -2879,7 +2879,7 @@ end @test eval(:(x = $(QuoteNode(Core.SSAValue(1))))) == Core.SSAValue(1) @test eval(:(x = $(QuoteNode(Core.SlotNumber(1))))) == Core.SlotNumber(1) @test_throws ErrorException("syntax: SSAValue objects should not occur in an AST") eval(:(x = $(Core.SSAValue(1)))) -@test_throws ErrorException("syntax: Slot objects should not occur in an AST") eval(:(x = $(Core.SlotNumber(1)))) +@test_throws ErrorException("syntax: SlotNumber objects should not occur in an AST") eval(:(x = $(Core.SlotNumber(1)))) # juxtaposition of radical symbols (#40094) @test Meta.parse("2√3") == Expr(:call, :*, 2, Expr(:call, :√, 3)) From c751080e8145fc5d0430ab6a23ba011fe8bb0984 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki Date: Thu, 16 Feb 2023 19:28:18 +0900 Subject: [PATCH 143/775] define `TypedSlot` in `Core.Compiler` --- base/boot.jl | 5 ++-- base/compiler/optimize.jl | 5 ++-- base/compiler/ssair/slot2ssa.jl | 8 +++++- base/compiler/ssair/verify.jl | 2 +- base/compiler/validation.jl | 4 +-- base/show.jl | 12 ++++---- doc/src/devdocs/ast.md | 7 +++-- src/ast.c | 2 +- src/builtins.c | 1 - src/codegen.cpp | 35 +++++++++-------------- src/interpreter.c | 8 +++--- src/ircode.c | 4 +-- src/jl_exported_data.inc | 1 - src/jltypes.c | 5 ---- src/julia.h | 3 +- src/method.c | 4 +-- src/staticdata.c | 3 +- stdlib/Serialization/src/Serialization.jl | 15 +++++----- test/compiler/inference.jl | 14 ++------- test/hashing.jl | 6 ++-- 20 files changed, 62 insertions(+), 82 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index 1f1e4535fe55c..b4e01b0c884c1 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -415,7 +415,6 @@ eval(Core, quote LineInfoNode(mod::Module, @nospecialize(method), file::Symbol, line::Int32, inlined_at::Int32) = $(Expr(:new, :LineInfoNode, :mod, :method, :file, :line, :inlined_at)) SlotNumber(n::Int) = $(Expr(:new, :SlotNumber, :n)) - TypedSlot(n::Int, @nospecialize(t)) = $(Expr(:new, :TypedSlot, :n, :t)) PhiNode(edges::Array{Int32, 1}, values::Array{Any, 1}) = $(Expr(:new, :PhiNode, :edges, :values)) PiNode(@nospecialize(val), @nospecialize(typ)) = $(Expr(:new, :PiNode, :val, :typ)) PhiCNode(values::Array{Any, 1}) = $(Expr(:new, :PhiCNode, :values)) @@ -523,12 +522,12 @@ Symbol(s::Symbol) = s module IR export CodeInfo, MethodInstance, CodeInstance, GotoNode, GotoIfNot, ReturnNode, - NewvarNode, SSAValue, SlotNumber, TypedSlot, Argument, + NewvarNode, SSAValue, SlotNumber, Argument, PiNode, PhiNode, PhiCNode, UpsilonNode, LineInfoNode, Const, PartialStruct, InterConditional import Core: CodeInfo, MethodInstance, CodeInstance, GotoNode, GotoIfNot, ReturnNode, - NewvarNode, SSAValue, SlotNumber, TypedSlot, Argument, + NewvarNode, SSAValue, SlotNumber, Argument, PiNode, PhiNode, PhiCNode, UpsilonNode, LineInfoNode, Const, PartialStruct, InterConditional diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 6e75aad23dee6..dc321be5108cf 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -242,9 +242,7 @@ function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospe isa(stmt, ReturnNode) && return (true, false, true) isa(stmt, GotoNode) && return (true, false, true) isa(stmt, GotoIfNot) && return (true, false, ⊑(𝕃ₒ, argextype(stmt.cond, src), Bool)) - if isa(stmt, SlotNumber) || isa(stmt, TypedSlot) - return (true, false, false) # they shouldn't occur in the IR at this point, but let's be defensive here - elseif isa(stmt, GlobalRef) + if isa(stmt, GlobalRef) nothrow = isdefined(stmt.mod, stmt.name) consistent = nothrow && isconst(stmt.mod, stmt.name) return (consistent, nothrow, nothrow) @@ -338,6 +336,7 @@ function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospe return (false, false, false) end end + isa(stmt, UnoptSlot) && error("unexpected IR elements") return (true, true, true) end diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index 07ec86eb2d18a..22e4be12a72cf 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -1,5 +1,11 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +struct TypedSlot + id::Int + typ + TypedSlot(id::Int, @nospecialize(typ)) = new(id, typ) +end + const UnoptSlot = Union{SlotNumber, TypedSlot} mutable struct SlotInfo @@ -229,7 +235,7 @@ function typ_for_val(@nospecialize(x), ci::CodeInfo, sptypes::Vector{VarState}, isa(x, Argument) && return slottypes[x.n] isa(x, NewSSAValue) && return DelayedTyp(x) isa(x, QuoteNode) && return Const(x.value) - isa(x, Union{Symbol, PiNode, PhiNode, SlotNumber, TypedSlot}) && error("unexpected val type") + isa(x, Union{Symbol, PiNode, PhiNode, UnoptSlot}) && error("unexpected val type") return Const(x) end diff --git a/base/compiler/ssair/verify.jl b/base/compiler/ssair/verify.jl index b6c90c4528f23..66fde426347bd 100644 --- a/base/compiler/ssair/verify.jl +++ b/base/compiler/ssair/verify.jl @@ -63,7 +63,7 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, elseif isa(op, Union{OldSSAValue, NewSSAValue}) @verify_error "Left over SSA marker" error("") - elseif isa(op, Union{SlotNumber, TypedSlot}) + elseif isa(op, UnoptSlot) @verify_error "Left over slot detected in converted IR" error("") end diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl index 61cc2c5dcfcf8..22a21a4559985 100644 --- a/base/compiler/validation.jl +++ b/base/compiler/validation.jl @@ -231,10 +231,10 @@ end validate_code(args...) = validate_code!(Vector{InvalidCodeError}(), args...) -is_valid_lvalue(@nospecialize(x)) = isa(x, SlotNumber) || isa(x, TypedSlot) || isa(x, GlobalRef) +is_valid_lvalue(@nospecialize(x)) = isa(x, UnoptSlot) || isa(x, GlobalRef) function is_valid_argument(@nospecialize(x)) - if isa(x, SlotNumber) || isa(x, TypedSlot) || isa(x, Argument) || isa(x, SSAValue) || + if isa(x, UnoptSlot) || isa(x, Argument) || isa(x, SSAValue) || isa(x, GlobalRef) || isa(x, QuoteNode) || isexpr(x, (:static_parameter, :boundscheck)) || isa(x, Number) || isa(x, AbstractString) || isa(x, AbstractChar) || isa(x, Tuple) || isa(x, Type) || isa(x, Core.Box) || isa(x, Module) || x === nothing diff --git a/base/show.jl b/base/show.jl index ef354badd8733..3ed5db6c2ef63 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1372,9 +1372,11 @@ show(io::IO, s::Symbol) = show_unquoted_quote_expr(io, s, 0, 0, 0) # eval(Meta.parse("Set{Int64}([2,3,1])")) # ==> An actual set # While this isn’t true of ALL show methods, it is of all ASTs. -const ExprNode = Union{Expr, QuoteNode, SlotNumber, TypedSlot, LineNumberNode, SSAValue, - GotoNode, GlobalRef, PhiNode, PhiCNode, UpsilonNode, - Core.Compiler.GotoIfNot, Core.Compiler.ReturnNode} +using Core.Compiler: TypedSlot, UnoptSlot + +const ExprNode = Union{Expr, QuoteNode, UnoptSlot, LineNumberNode, SSAValue, + GotoNode, GotoIfNot, GlobalRef, PhiNode, PhiCNode, UpsilonNode, + ReturnNode} # Operators have precedence levels from 1-N, and show_unquoted defaults to a # precedence level of 0 (the fourth argument). The top-level print and show # methods use a precedence of -1 to specially allow space-separated macro syntax. @@ -1723,7 +1725,7 @@ function show_globalref(io::IO, ex::GlobalRef; allow_macroname=false) nothing end -function show_unquoted(io::IO, ex::Union{SlotNumber,TypedSlot}, ::Int, ::Int) +function show_unquoted(io::IO, ex::UnoptSlot, ::Int, ::Int) typ = isa(ex, TypedSlot) ? ex.typ : Any slotid = ex.id slotnames = get(io, :SOURCE_SLOTNAMES, false) @@ -2588,7 +2590,7 @@ module IRShow const Compiler = Core.Compiler using Core.IR import ..Base - import .Compiler: IRCode, ReturnNode, GotoIfNot, CFG, scan_ssa_use!, Argument, + import .Compiler: IRCode, TypedSlot, CFG, scan_ssa_use!, isexpr, compute_basic_blocks, block_for_inst, IncrementalCompact, Effects, ALWAYS_TRUE, ALWAYS_FALSE Base.getindex(r::Compiler.StmtRange, ind::Integer) = Compiler.getindex(r, ind) diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index dc1500e913dce..df6a2224c2a48 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -255,9 +255,10 @@ types exist in lowered form: integer-valued `id` field giving the slot index. The types of these slots can be found in the `slottypes` field of their `CodeInfo` object. When a slot has different types at different uses and thus requires per-use type annotations, - they are converted to temporary `TypedSlot` object. This object has an additional `typ` - field as well as the `id` field. Note that `TypedSlot` only appears in an unoptimized - lowered form that is scheduled for optimization, and it never appears elsewhere. + they are converted to temporary `Core.Compiler.TypedSlot` object. This object has an + additional `typ` field as well as the `id` field. Note that `Core.Compiler.TypedSlot` + only appears in an unoptimized lowered form that is scheduled for optimization, + and it never appears elsewhere. * `Argument` diff --git a/src/ast.c b/src/ast.c index 53dd135082da5..cb03b7efb6eb7 100644 --- a/src/ast.c +++ b/src/ast.c @@ -686,7 +686,7 @@ static value_t julia_to_scm_noalloc2(fl_context_t *fl_ctx, jl_value_t *v, int ch if (check_valid) { if (jl_is_ssavalue(v)) lerror(fl_ctx, symbol(fl_ctx, "error"), "SSAValue objects should not occur in an AST"); - if (jl_is_slot(v)) + if (jl_is_slotnumber(v)) lerror(fl_ctx, symbol(fl_ctx, "error"), "SlotNumber objects should not occur in an AST"); } value_t opaque = cvalue(fl_ctx, jl_ast_ctx(fl_ctx)->jvtype, sizeof(void*)); diff --git a/src/builtins.c b/src/builtins.c index f2acf29299ae0..471b06e559dc5 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -2030,7 +2030,6 @@ void jl_init_primitives(void) JL_GC_DISABLED add_builtin("Symbol", (jl_value_t*)jl_symbol_type); add_builtin("SSAValue", (jl_value_t*)jl_ssavalue_type); add_builtin("SlotNumber", (jl_value_t*)jl_slotnumber_type); - add_builtin("TypedSlot", (jl_value_t*)jl_typedslot_type); add_builtin("Argument", (jl_value_t*)jl_argument_type); add_builtin("Const", (jl_value_t*)jl_const_type); add_builtin("PartialStruct", (jl_value_t*)jl_partial_struct_type); diff --git a/src/codegen.cpp b/src/codegen.cpp index 36d0b6c85fb12..408fa7c226863 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2500,7 +2500,7 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) return jl_get_global(ctx.module, sym); return NULL; } - if (jl_is_slot(ex) || jl_is_argument(ex)) + if (jl_is_slotnumber(ex) || jl_is_argument(ex)) return NULL; if (jl_is_ssavalue(ex)) { ssize_t idx = ((jl_ssavalue_t*)ex)->id - 1; @@ -2594,7 +2594,7 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) static bool slot_eq(jl_value_t *e, int sl) { - return (jl_is_slot(e) || jl_is_argument(e)) && jl_slot_number(e)-1 == sl; + return (jl_is_slotnumber(e) || jl_is_argument(e)) && jl_slot_number(e)-1 == sl; } // --- code gen for intrinsic functions --- @@ -2637,7 +2637,7 @@ static std::set assigned_in_try(jl_array_t *stmts, int s, long l) if (jl_is_expr(st)) { if (((jl_expr_t*)st)->head == jl_assign_sym) { jl_value_t *ar = jl_exprarg(st, 0); - if (jl_is_slot(ar)) { + if (jl_is_slotnumber(ar)) { av.insert(jl_slot_number(ar)-1); } } @@ -2740,7 +2740,7 @@ static void general_use_analysis(jl_codectx_t &ctx, jl_value_t *expr, callback & static void simple_use_analysis(jl_codectx_t &ctx, jl_value_t *expr) { auto scan_slot_arg = [&](jl_value_t *expr) { - if (jl_is_slot(expr) || jl_is_argument(expr)) { + if (jl_is_slotnumber(expr) || jl_is_argument(expr)) { int i = jl_slot_number(expr) - 1; ctx.slots[i].used = true; return true; @@ -4517,7 +4517,7 @@ static jl_cgval_t emit_sparam(jl_codectx_t &ctx, size_t i) static jl_cgval_t emit_isdefined(jl_codectx_t &ctx, jl_value_t *sym) { Value *isnull = NULL; - if (jl_is_slot(sym) || jl_is_argument(sym)) { + if (jl_is_slotnumber(sym) || jl_is_argument(sym)) { size_t sl = jl_slot_number(sym) - 1; jl_varinfo_t &vi = ctx.slots[sl]; if (!vi.usedUndef) @@ -4597,8 +4597,8 @@ static jl_cgval_t emit_isdefined(jl_codectx_t &ctx, jl_value_t *sym) return mark_julia_type(ctx, isnull, false, jl_bool_type); } -static jl_cgval_t emit_varinfo(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_sym_t *varname, jl_value_t *better_typ=NULL) { - jl_value_t *typ = better_typ ? better_typ : vi.value.typ; +static jl_cgval_t emit_varinfo(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_sym_t *varname) { + jl_value_t *typ = vi.value.typ; jl_cgval_t v; Value *isnull = NULL; if (vi.boxroot == NULL || vi.pTIndex != NULL) { @@ -4675,14 +4675,7 @@ static jl_cgval_t emit_local(jl_codectx_t &ctx, jl_value_t *slotload) size_t sl = jl_slot_number(slotload) - 1; jl_varinfo_t &vi = ctx.slots[sl]; jl_sym_t *sym = slot_symbol(ctx, sl); - jl_value_t *typ = NULL; - if (jl_typeis(slotload, jl_typedslot_type)) { - // use the better type from inference for this load - typ = jl_typedslot_get_type(slotload); - if (jl_is_typevar(typ)) - typ = ((jl_tvar_t*)typ)->ub; - } - return emit_varinfo(ctx, vi, sym, typ); + return emit_varinfo(ctx, vi, sym); } static void emit_vi_assignment_unboxed(jl_codectx_t &ctx, jl_varinfo_t &vi, Value *isboxed, jl_cgval_t rval_info) @@ -4929,7 +4922,7 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r, ssi assert(!jl_is_ssavalue(l)); jl_cgval_t rval_info = emit_expr(ctx, r, ssaval); - if (jl_is_slot(l)) { + if (jl_is_slotnumber(l)) { int sl = jl_slot_number(l) - 1; // it's a local variable jl_varinfo_t &vi = ctx.slots[sl]; @@ -5035,7 +5028,7 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr, int ssaval_result) { if (jl_is_ssavalue(expr) && ssaval_result == -1) return; // value not used, no point in attempting codegen for it - if (jl_is_slot(expr) && ssaval_result == -1) { + if (jl_is_slotnumber(expr) && ssaval_result == -1) { size_t sl = jl_slot_number(expr) - 1; jl_varinfo_t &vi = ctx.slots[sl]; if (vi.usedUndef) @@ -5047,7 +5040,7 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr, int ssaval_result) } if (jl_is_newvarnode(expr)) { jl_value_t *var = jl_fieldref(expr, 0); - assert(jl_is_slot(var)); + assert(jl_is_slotnumber(var)); jl_varinfo_t &vi = ctx.slots[jl_slot_number(var)-1]; if (vi.usedUndef) { // create a new uninitialized variable @@ -5169,7 +5162,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ jl_sym_t *sym = (jl_sym_t*)expr; return emit_globalref(ctx, ctx.module, sym, AtomicOrdering::Unordered); } - if (jl_is_slot(expr) || jl_is_argument(expr)) { + if (jl_is_slotnumber(expr) || jl_is_argument(expr)) { return emit_local(ctx, expr); } if (jl_is_ssavalue(expr)) { @@ -5286,7 +5279,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ else if (head == jl_method_sym) { if (nargs == 1) { jl_value_t *mn = args[0]; - assert(jl_is_symbol(mn) || jl_is_slot(mn)); + assert(jl_is_symbol(mn) || jl_is_slotnumber(mn)); Value *bp = NULL, *name; jl_binding_t *bnd = NULL; @@ -5314,7 +5307,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ bp = julia_binding_gv(ctx, bnd); bp = julia_binding_pvalue(ctx, bp); } - else if (jl_is_slot(mn) || jl_is_argument(mn)) { + else if (jl_is_slotnumber(mn) || jl_is_argument(mn)) { // XXX: eval_methoddef does not have this code branch int sl = jl_slot_number(mn)-1; jl_varinfo_t &vi = ctx.slots[sl]; diff --git a/src/interpreter.c b/src/interpreter.c index bf41a2eaa9fbd..08cb87791c5a3 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -184,7 +184,7 @@ static jl_value_t *eval_value(jl_value_t *e, interpreter_state *s) else return s->locals[jl_source_nslots(src) + id]; } - if (jl_is_slot(e) || jl_is_argument(e)) { + if (jl_is_slotnumber(e) || jl_is_argument(e)) { ssize_t n = jl_slot_number(e); if (src == NULL || n > jl_source_nslots(src) || n < 1 || s->locals == NULL) jl_error("access to invalid slot number"); @@ -230,7 +230,7 @@ static jl_value_t *eval_value(jl_value_t *e, interpreter_state *s) else if (head == jl_isdefined_sym) { jl_value_t *sym = args[0]; int defined = 0; - if (jl_is_slot(sym) || jl_is_argument(sym)) { + if (jl_is_slotnumber(sym) || jl_is_argument(sym)) { ssize_t n = jl_slot_number(sym); if (src == NULL || n > jl_source_nslots(src) || n < 1 || s->locals == NULL) jl_error("access to invalid slot number"); @@ -472,7 +472,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, if (head == jl_assign_sym) { jl_value_t *lhs = jl_exprarg(stmt, 0); jl_value_t *rhs = eval_value(jl_exprarg(stmt, 1), s); - if (jl_is_slot(lhs)) { + if (jl_is_slotnumber(lhs)) { ssize_t n = jl_slot_number(lhs); assert(n <= jl_source_nslots(s->src) && n > 0); s->locals[n - 1] = rhs; @@ -608,7 +608,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, } else if (jl_is_newvarnode(stmt)) { jl_value_t *var = jl_fieldref(stmt, 0); - assert(jl_is_slot(var)); + assert(jl_is_slotnumber(var)); ssize_t n = jl_slot_number(var); assert(n <= jl_source_nslots(s->src) && n > 0); s->locals[n - 1] = NULL; diff --git a/src/ircode.c b/src/ircode.c index 0634d83870fa7..f967dd1a29f51 100644 --- a/src/ircode.c +++ b/src/ircode.c @@ -385,7 +385,7 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) } } else if (as_literal || jl_is_uniontype(v) || jl_is_newvarnode(v) || jl_is_linenode(v) || - jl_is_upsilonnode(v) || jl_is_pinode(v) || jl_is_slot(v) || jl_is_ssavalue(v) || + jl_is_upsilonnode(v) || jl_is_pinode(v) || jl_is_slotnumber(v) || jl_is_ssavalue(v) || (jl_isbits(jl_typeof(v)) && jl_datatype_size(jl_typeof(v)) <= 64)) { jl_datatype_t *t = (jl_datatype_t*)jl_typeof(v); size_t tsz = jl_datatype_size(t); @@ -1108,7 +1108,7 @@ void jl_init_serializer(void) jl_emptytuple_type, jl_array_uint8_type, jl_code_info_type, jl_typeofbottom_type, jl_typeofbottom_type->super, jl_namedtuple_type, jl_array_int32_type, - jl_typedslot_type, jl_uint32_type, jl_uint64_type, + jl_uint32_type, jl_uint64_type, jl_type_type_mt, jl_nonfunction_mt, jl_opaque_closure_type, diff --git a/src/jl_exported_data.inc b/src/jl_exported_data.inc index d2bc282a59a1b..cd8abc9b230cd 100644 --- a/src/jl_exported_data.inc +++ b/src/jl_exported_data.inc @@ -103,7 +103,6 @@ XX(jl_true) \ XX(jl_tuple_typename) \ XX(jl_tvar_type) \ - XX(jl_typedslot_type) \ XX(jl_typeerror_type) \ XX(jl_typemap_entry_type) \ XX(jl_typemap_level_type) \ diff --git a/src/jltypes.c b/src/jltypes.c index e29de852f2442..cc10485e64247 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2236,11 +2236,6 @@ void jl_init_types(void) JL_GC_DISABLED jl_svec1(jl_long_type), jl_emptysvec, 0, 0, 1); - jl_typedslot_type = jl_new_datatype(jl_symbol("TypedSlot"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(2, "id", "typ"), - jl_svec(2, jl_long_type, jl_any_type), - jl_emptysvec, 0, 0, 2); - jl_argument_type = jl_new_datatype(jl_symbol("Argument"), core, jl_any_type, jl_emptysvec, jl_perm_symsvec(1, "n"), jl_svec1(jl_long_type), diff --git a/src/julia.h b/src/julia.h index be0e76ec11e97..8c3332fe50e12 100644 --- a/src/julia.h +++ b/src/julia.h @@ -694,7 +694,6 @@ extern JL_DLLIMPORT jl_typename_t *jl_type_typename JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_symbol_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_ssavalue_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_slotnumber_type JL_GLOBALLY_ROOTED; -extern JL_DLLIMPORT jl_datatype_t *jl_typedslot_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_argument_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_const_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_partial_struct_type JL_GLOBALLY_ROOTED; @@ -1238,7 +1237,7 @@ static inline int jl_is_layout_opaque(const jl_datatype_layout_t *l) JL_NOTSAFEP #define jl_is_bool(v) jl_typeis(v,jl_bool_type) #define jl_is_symbol(v) jl_typeis(v,jl_symbol_type) #define jl_is_ssavalue(v) jl_typeis(v,jl_ssavalue_type) -#define jl_is_slot(v) (jl_typeis(v,jl_slotnumber_type) || jl_typeis(v,jl_typedslot_type)) +#define jl_is_slotnumber(v) jl_typeis(v,jl_slotnumber_type) #define jl_is_expr(v) jl_typeis(v,jl_expr_type) #define jl_is_binding(v) jl_typeis(v,jl_binding_type) #define jl_is_globalref(v) jl_typeis(v,jl_globalref_type) diff --git a/src/method.c b/src/method.c index cce217230968c..f66cca698d2d5 100644 --- a/src/method.c +++ b/src/method.c @@ -696,7 +696,7 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) size_t j; for (j = 1; j < nargs; j++) { jl_value_t *aj = jl_exprarg(st, j); - if (!jl_is_slot(aj) && !jl_is_argument(aj)) + if (!jl_is_slotnumber(aj) && !jl_is_argument(aj)) continue; int sn = (int)jl_slot_number(aj) - 2; if (sn < 0) // @nospecialize on self is valid but currently ignored @@ -719,7 +719,7 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) m->nospecialize = 0; for (j = 1; j < nargs; j++) { jl_value_t *aj = jl_exprarg(st, j); - if (!jl_is_slot(aj) && !jl_is_argument(aj)) + if (!jl_is_slotnumber(aj) && !jl_is_argument(aj)) continue; int sn = (int)jl_slot_number(aj) - 2; if (sn < 0) // @specialize on self is valid but currently ignored diff --git a/src/staticdata.c b/src/staticdata.c index 5fb3442a6e347..e36ea92c3ef00 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -98,7 +98,7 @@ extern "C" { // TODO: put WeakRefs on the weak_refs list during deserialization // TODO: handle finalizers -#define NUM_TAGS 158 +#define NUM_TAGS 157 // An array of references that need to be restored from the sysimg // This is a manually constructed dual of the gvars array, which would be produced by codegen for Julia code, for C. @@ -118,7 +118,6 @@ jl_value_t **const*const get_tags(void) { INSERT_TAG(jl_slotnumber_type); INSERT_TAG(jl_simplevector_type); INSERT_TAG(jl_array_type); - INSERT_TAG(jl_typedslot_type); INSERT_TAG(jl_expr_type); INSERT_TAG(jl_binding_type); INSERT_TAG(jl_globalref_type); diff --git a/stdlib/Serialization/src/Serialization.jl b/stdlib/Serialization/src/Serialization.jl index b11df48daa08f..630185ebd575a 100644 --- a/stdlib/Serialization/src/Serialization.jl +++ b/stdlib/Serialization/src/Serialization.jl @@ -39,7 +39,7 @@ const TAGS = Any[ Float16, Float32, Float64, Char, DataType, Union, UnionAll, Core.TypeName, Tuple, Array, Expr, LineNumberNode, :__LabelNode__, GotoNode, QuoteNode, CodeInfo, TypeVar, Core.Box, Core.MethodInstance, Module, Task, String, SimpleVector, Method, - GlobalRef, SlotNumber, TypedSlot, NewvarNode, SSAValue, + GlobalRef, SlotNumber, Const, NewvarNode, SSAValue, # dummy entries for tags that don't correspond directly to types Symbol, # UNDEFREF_TAG @@ -77,15 +77,14 @@ const TAGS = Any[ (Int64(0):Int64(n_int_literals-1))... ] -@assert length(TAGS) == 255 +const NTAGS = length(TAGS) +@assert NTAGS == 255 const ser_version = 23 # do not make changes without bumping the version #! format_version(::AbstractSerializer) = ser_version format_version(s::Serializer) = s.version -const NTAGS = length(TAGS) - function sertag(@nospecialize(v)) # NOTE: we use jl_value_ptr directly since we know at least one of the arguments # in the comparison below is a singleton. @@ -194,7 +193,7 @@ serialize(s::AbstractSerializer, ::Tuple{}) = writetag(s.io, EMPTYTUPLE_TAG) function serialize(s::AbstractSerializer, t::Tuple) l = length(t) - if l <= 255 + if l <= NTAGS writetag(s.io, TUPLE_TAG) write(s.io, UInt8(l)) else @@ -224,7 +223,7 @@ function serialize(s::AbstractSerializer, x::Symbol) if len > 7 serialize_cycle(s, x) && return end - if len <= 255 + if len <= NTAGS writetag(s.io, SYMBOL_TAG) write(s.io, UInt8(len)) else @@ -295,7 +294,7 @@ function serialize(s::AbstractSerializer, ss::String) serialize_cycle(s, ss) && return writetag(s.io, SHARED_REF_TAG) end - if len <= 255 + if len <= NTAGS writetag(s.io, STRING_TAG) write(s.io, UInt8(len)) else @@ -327,7 +326,7 @@ end function serialize(s::AbstractSerializer, ex::Expr) serialize_cycle(s, ex) && return l = length(ex.args) - if l <= 255 + if l <= NTAGS writetag(s.io, EXPR_TAG) write(s.io, UInt8(l)) else diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 48105f2bb8029..182776d79d7ec 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -582,7 +582,6 @@ function is_typed_expr(e::Expr) end is_typed_expr(@nospecialize other) = false test_inferred_static(@nospecialize(other)) = true -test_inferred_static(slot::TypedSlot) = @test isdispatchelem(slot.typ) function test_inferred_static(expr::Expr) for a in expr.args test_inferred_static(a) @@ -639,17 +638,8 @@ for (codetype, all_ssa) in Any[ (code_typed(h18679, ())[1], true), (code_typed(g19348, (typeof((1, 2.0)),))[1], true)] code = codetype[1] - local notconst(@nospecialize(other)) = true - notconst(slot::TypedSlot) = @test isa(slot.typ, Type) - function notconst(expr::Expr) - for a in expr.args - notconst(a) - end - end local i - for i = 1:length(code.code) - e = code.code[i] - notconst(e) + for i = 1:length(code.ssavaluetypes) typ = code.ssavaluetypes[i] typ isa Core.Compiler.MaybeUndef && (typ = typ.typ) @test isa(typ, Type) || isa(typ, Const) || isa(typ, Conditional) || typ @@ -1947,7 +1937,7 @@ let opt25261 = code_typed(foo25261, Tuple{}, optimize=false)[1].first.code end foundslot = false for expr25261 in opt25261[i:end] - if expr25261 isa TypedSlot && expr25261.typ === Tuple{Int, Int} + if expr25261 isa Core.Compiler.TypedSlot && expr25261.typ === Tuple{Int, Int} # This should be the assignment to the SSAValue into the getfield # call - make sure it's a TypedSlot foundslot = true diff --git a/test/hashing.jl b/test/hashing.jl index 9f40e7a4a73ac..0266b2f06e168 100644 --- a/test/hashing.jl +++ b/test/hashing.jl @@ -201,9 +201,9 @@ let a = QuoteNode(1), b = QuoteNode(1.0) @test (hash(a)==hash(b)) == (a==b) end -let a = Expr(:block, Core.TypedSlot(1, Any)), - b = Expr(:block, Core.TypedSlot(1, Any)), - c = Expr(:block, Core.TypedSlot(3, Any)) +let a = Expr(:block, Core.SlotNumber(1)), + b = Expr(:block, Core.SlotNumber(1)), + c = Expr(:block, Core.SlotNumber(3)) @test a == b && hash(a) == hash(b) @test a != c && hash(a) != hash(c) @test b != c && hash(b) != hash(c) From 8b85fbddf06bcdfe45309a71f162a28ed5be5f82 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Fri, 17 Feb 2023 03:15:22 +0800 Subject: [PATCH 144/775] Intersect: avoid re-wrapping inner-var (#48695) If 2 var hold the same inner-var, the result `UnionAll` might contain 2 identical var, which should be avoided in general. Co-authored-by: Jameson Nash --- src/subtype.c | 18 ++++++++++++------ test/subtype.jl | 11 +++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index eb26ed69eb89e..a0f1785271b6e 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2715,7 +2715,7 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind // remove/replace/rewrap free occurrences of this var in the environment jl_varbinding_t *btemp = e->vars; - int wrap = 1; + jl_varbinding_t *wrap = NULL; while (btemp != NULL) { if (jl_has_typevar(btemp->lb, vb->var)) { if (vb->lb == (jl_value_t*)btemp->var) { @@ -2736,14 +2736,11 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind !jl_has_typevar(vb->ub, btemp->var) && jl_has_typevar(btemp->ub, vb->var)) { // if our variable is T, and some outer variable has constraint S = Ref{T}, // move the `where T` outside `where S` instead of putting it here. issue #21243. - if (btemp->innervars == NULL) - btemp->innervars = jl_alloc_array_1d(jl_array_any_type, 0); if (newvar != vb->var) { btemp->lb = jl_substitute_var(btemp->lb, vb->var, (jl_value_t*)newvar); btemp->ub = jl_substitute_var(btemp->ub, vb->var, (jl_value_t*)newvar); } - jl_array_ptr_1d_push(btemp->innervars, (jl_value_t*)newvar); - wrap = 0; + wrap = btemp; btemp = btemp->prev; continue; } @@ -2776,6 +2773,15 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind btemp = btemp->prev; } + if (wrap) { + // We only assign the newvar with the outmost var. + // This make sure we never create a UnionAll with 2 identical vars. + if (wrap->innervars == NULL) + wrap->innervars = jl_alloc_array_1d(jl_array_any_type, 0); + jl_array_ptr_1d_push(wrap->innervars, (jl_value_t*)newvar); + } + + // if `v` still occurs, re-wrap body in `UnionAll v` or eliminate the UnionAll if (jl_has_typevar(res, vb->var)) { if (varval) { @@ -2796,7 +2802,7 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind if (newvar != vb->var) res = jl_substitute_var(res, vb->var, (jl_value_t*)newvar); varval = (jl_value_t*)newvar; - if (wrap) + if (!wrap) res = jl_type_unionall((jl_tvar_t*)newvar, res); } } diff --git a/test/subtype.jl b/test/subtype.jl index ca00947eb3b46..c7a2dcdcc113f 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2447,3 +2447,14 @@ end #issue 48582 @test !<:(Tuple{Pair{<:T,<:T}, Val{S} where {S}} where {T<:Base.BitInteger}, Tuple{Pair{<:T,<:T}, Val{Int}} where {T<:Base.BitInteger}) + +struct T48695{T, N, H<:AbstractArray} <: AbstractArray{Union{Missing, T}, N} end +struct S48695{T, N, H<:AbstractArray{T, N}} <: AbstractArray{T, N} end +let S = Tuple{Type{S48695{T, 2, T48695{B, 2, C}}} where {T<:(Union{Missing, A} where A), B, C}, T48695{T, 2} where T}, + T = Tuple{Type{S48695{T, N, H}}, H} where {T, N, H<:AbstractArray{T, N}} + V = typeintersect(S, T) + vars_in_unionall(s) = s isa UnionAll ? (s.var, vars_in_unionall(s.body)...) : () + @test V != Union{} + @test allunique(vars_in_unionall(V)) + @test typeintersect(V, T) != Union{} +end From 8068e44190c297b7dde1fee4f947549865a9651c Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Thu, 16 Feb 2023 21:34:41 +0100 Subject: [PATCH 145/775] Relax abstractq test (#48694) --- stdlib/LinearAlgebra/src/adjtrans.jl | 7 ++----- stdlib/LinearAlgebra/test/abstractq.jl | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/stdlib/LinearAlgebra/src/adjtrans.jl b/stdlib/LinearAlgebra/src/adjtrans.jl index 9a872497fdbae..6003339735fcf 100644 --- a/stdlib/LinearAlgebra/src/adjtrans.jl +++ b/stdlib/LinearAlgebra/src/adjtrans.jl @@ -1,8 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -using Base: @propagate_inbounds -import Base: length, size, axes, IndexStyle, getindex, setindex!, parent, vec, convert, similar - ### basic definitions (types, aliases, constructors, abstractarray interface, sundry similar) # note that Adjoint and Transpose must be able to wrap not only vectors and matrices @@ -12,7 +9,7 @@ import Base: length, size, axes, IndexStyle, getindex, setindex!, parent, vec, c Adjoint Lazy wrapper type for an adjoint view of the underlying linear algebra object, -usually an `AbstractVector`/`AbstractMatrix`, but also some `Factorization`, for instance. +usually an `AbstractVector`/`AbstractMatrix`. Usually, the `Adjoint` constructor should not be called directly, use [`adjoint`](@ref) instead. To materialize the view use [`copy`](@ref). @@ -39,7 +36,7 @@ end Transpose Lazy wrapper type for a transpose view of the underlying linear algebra object, -usually an `AbstractVector`/`AbstractMatrix`, but also some `Factorization`, for instance. +usually an `AbstractVector`/`AbstractMatrix`. Usually, the `Transpose` constructor should not be called directly, use [`transpose`](@ref) instead. To materialize the view use [`copy`](@ref). diff --git a/stdlib/LinearAlgebra/test/abstractq.jl b/stdlib/LinearAlgebra/test/abstractq.jl index 252a632fc97b9..cb28629424194 100644 --- a/stdlib/LinearAlgebra/test/abstractq.jl +++ b/stdlib/LinearAlgebra/test/abstractq.jl @@ -36,7 +36,7 @@ n = 5 @test I*Q ≈ Q.Q*I rtol=2eps(real(T)) @test I*Q' ≈ I*Q.Q' rtol=2eps(real(T)) @test abs(det(Q)) ≈ 1 - @test logabsdet(Q)[1] ≈ 0 atol=2eps(real(T)) + @test logabsdet(Q)[1] ≈ 0 atol=2n*eps(real(T)) y = rand(T, n) @test Q * y ≈ Q.Q * y ≈ Q' \ y ≈ ldiv!(Q', copy(y)) ≈ ldiv!(zero(y), Q', y) @test Q'y ≈ Q.Q' * y ≈ Q \ y ≈ ldiv!(Q, copy(y)) ≈ ldiv!(zero(y), Q, y) From cbbfc68f81c357059300dab91873da11ef6999fd Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 17 Feb 2023 11:33:57 +0100 Subject: [PATCH 146/775] Silence the at-pure deprecation warning. (#48701) --- base/deprecated.jl | 6 ------ 1 file changed, 6 deletions(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index 3e18cfcf918fb..6d1e4283c814d 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -378,14 +378,8 @@ helping for type inference. In Julia 1.8 and higher, it is favorable to use [`@assume_effects`](@ref) instead of `@pure`. This is because `@assume_effects` allows a finer grained control over Julia's purity modeling and the effect system enables a wider range of optimizations. - -!!! note - In Julia 1.10 this is deprecated in favor of [`@assume_effects`](@ref). - Specifically, `@assume_effects :foldable` provides similar guarentees. """ macro pure(ex) - f, l = __source__.file, __source__.line - @warn "`Base.@pure ex` at $f:$l is deprecated, use `Base.@assume_effects :foldable ex` instead." return esc(:(Base.@assume_effects :foldable $ex)) end From 892cd4ff8a1cf96bad9dbd39e0d1f5829e33a1d0 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 17 Feb 2023 10:58:05 -0500 Subject: [PATCH 147/775] ensure the path regexes will accept all valid paths (#48686) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, we might try to interpret the random bytes in a path as UTF-8 and excluding \n, causing the regex match to fail or be incomplete in some cases. But those are valid in a path, so we want PCRE2 to treat them as transparent bytes. Accordingly, change r""a to specify all flags needed to interpret the values simply as ASCII. Note, this would be breaking if someone was previously trying to match a Unicode character by `\u` while also disabling UCP matching of \w and \s, but that seems an odd specific choice to need. julia> match(r"\u03b1"a, "α") ERROR: PCRE compilation error: character code point value in \u.... sequence is too large at offset 6 (this would have previously worked). Note that explicitly starting the regex with (*UTF) or using a literal α in the regex would continue to work as before however. Note that `s` (DOTALL) is a more efficient matcher (if the pattern contains `.`), as is `a`, so it is often preferable to set both when in doubt: http://man.he.net/man3/pcre2perform Refs: #48648 --- base/binaryplatforms.jl | 10 +++--- base/compiler/ssair/show.jl | 2 +- base/deprecated.jl | 2 +- base/libc.jl | 2 +- base/methodshow.jl | 4 +-- base/path.jl | 20 +++++------ base/regex.jl | 66 ++++++++++++++++++++++++++----------- base/set.jl | 4 +-- base/shell.jl | 10 +++--- test/path.jl | 3 ++ test/regex.jl | 20 ++++++++--- 11 files changed, 91 insertions(+), 52 deletions(-) diff --git a/base/binaryplatforms.jl b/base/binaryplatforms.jl index eb4bcfd8c76fc..fb9feba41c636 100644 --- a/base/binaryplatforms.jl +++ b/base/binaryplatforms.jl @@ -741,10 +741,10 @@ function Base.parse(::Type{Platform}, triplet::String; validate_strict::Bool = f end os_version = nothing if os == "macos" - os_version = extract_os_version("macos", r".*darwin([\d\.]+)") + os_version = extract_os_version("macos", r".*darwin([\d\.]+)"sa) end if os == "freebsd" - os_version = extract_os_version("freebsd", r".*freebsd([\d.]+)") + os_version = extract_os_version("freebsd", r".*freebsd([\d.]+)"sa) end tags["os_version"] = os_version @@ -798,13 +798,13 @@ function parse_dl_name_version(path::String, os::String) local dlregex if os == "windows" # On Windows, libraries look like `libnettle-6.dll` - dlregex = r"^(.*?)(?:-((?:[\.\d]+)*))?\.dll$" + dlregex = r"^(.*?)(?:-((?:[\.\d]+)*))?\.dll$"sa elseif os == "macos" # On OSX, libraries look like `libnettle.6.3.dylib` - dlregex = r"^(.*?)((?:\.[\d]+)*)\.dylib$" + dlregex = r"^(.*?)((?:\.[\d]+)*)\.dylib$"sa else # On Linux and FreeBSD, libraries look like `libnettle.so.6.3.0` - dlregex = r"^(.*?)\.so((?:\.[\d]+)*)$" + dlregex = r"^(.*?)\.so((?:\.[\d]+)*)$"sa end m = match(dlregex, basename(path)) diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index f4d240f423e89..0d17746c6d928 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -796,7 +796,7 @@ function inline_linfo_printer(code::IRCode) end end -_strip_color(s::String) = replace(s, r"\e\[\d+m" => "") +_strip_color(s::String) = replace(s, r"\e\[\d+m"a => "") function statementidx_lineinfo_printer(f, code::IRCode) printer = f(code.linetable) diff --git a/base/deprecated.jl b/base/deprecated.jl index 6d1e4283c814d..1b661716cc2d9 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -48,7 +48,7 @@ arguments of type `Any`. To restrict deprecation to a specific signature, annotate the arguments of `old`. For example, -```jldoctest; filter = r"@ .*" +```jldoctest; filter = r"@ .*"a julia> new(x::Int) = x; julia> new(x::Float64) = 2x; diff --git a/base/libc.jl b/base/libc.jl index 0a542ecbd1a82..5b508e00bf3e0 100644 --- a/base/libc.jl +++ b/base/libc.jl @@ -225,7 +225,7 @@ function strptime(fmt::AbstractString, timestr::AbstractString) @static if Sys.isapple() # if we didn't explicitly parse the weekday or year day, use mktime # to fill them in automatically. - if !occursin(r"([^%]|^)%(a|A|j|w|Ow)", fmt) + if !occursin(r"([^%]|^)%(a|A|j|w|Ow)"a, fmt) ccall(:mktime, Int, (Ref{TmStruct},), tm) end end diff --git a/base/methodshow.jl b/base/methodshow.jl index d3a40db665d1c..a45b89c6ccf63 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -7,7 +7,7 @@ function strip_gensym(sym) if sym === :var"#self#" || sym === :var"#unused#" return empty_sym end - return Symbol(replace(String(sym), r"^(.*)#(.*#)?\d+$" => s"\1")) + return Symbol(replace(String(sym), r"^(.*)#(.*#)?\d+$"sa => s"\1")) end function argtype_decl(env, n, @nospecialize(sig::DataType), i::Int, nargs, isva::Bool) # -> (argname, argtype) @@ -364,7 +364,7 @@ function url(m::Method) (m.file === :null || m.file === :string) && return "" file = string(m.file) line = m.line - line <= 0 || occursin(r"In\[[0-9]+\]", file) && return "" + line <= 0 || occursin(r"In\[[0-9]+\]"a, file) && return "" Sys.iswindows() && (file = replace(file, '\\' => '/')) libgit2_id = PkgId(UUID((0x76f85450_5226_5b5a,0x8eaa_529ad045b433)), "LibGit2") if inbase(M) diff --git a/base/path.jl b/base/path.jl index 1fac47432cda3..c439a2800acce 100644 --- a/base/path.jl +++ b/base/path.jl @@ -20,22 +20,22 @@ export if Sys.isunix() const path_separator = "/" - const path_separator_re = r"/+" - const path_directory_re = r"(?:^|/)\.{0,2}$" - const path_dir_splitter = r"^(.*?)(/+)([^/]*)$" - const path_ext_splitter = r"^((?:.*/)?(?:\.|[^/\.])[^/]*?)(\.[^/\.]*|)$" + const path_separator_re = r"/+"sa + const path_directory_re = r"(?:^|/)\.{0,2}$"sa + const path_dir_splitter = r"^(.*?)(/+)([^/]*)$"sa + const path_ext_splitter = r"^((?:.*/)?(?:\.|[^/\.])[^/]*?)(\.[^/\.]*|)$"sa splitdrive(path::String) = ("",path) elseif Sys.iswindows() const path_separator = "\\" - const path_separator_re = r"[/\\]+" - const path_absolute_re = r"^(?:[A-Za-z]+:)?[/\\]" - const path_directory_re = r"(?:^|[/\\])\.{0,2}$" - const path_dir_splitter = r"^(.*?)([/\\]+)([^/\\]*)$" - const path_ext_splitter = r"^((?:.*[/\\])?(?:\.|[^/\\\.])[^/\\]*?)(\.[^/\\\.]*|)$" + const path_separator_re = r"[/\\]+"sa + const path_absolute_re = r"^(?:[A-Za-z]+:)?[/\\]"sa + const path_directory_re = r"(?:^|[/\\])\.{0,2}$"sa + const path_dir_splitter = r"^(.*?)([/\\]+)([^/\\]*)$"sa + const path_ext_splitter = r"^((?:.*[/\\])?(?:\.|[^/\\\.])[^/\\]*?)(\.[^/\\\.]*|)$"sa function splitdrive(path::String) - m = match(r"^([^\\]+:|\\\\[^\\]+\\[^\\]+|\\\\\?\\UNC\\[^\\]+\\[^\\]+|\\\\\?\\[^\\]+:|)(.*)$"s, path)::AbstractMatch + m = match(r"^([^\\]+:|\\\\[^\\]+\\[^\\]+|\\\\\?\\UNC\\[^\\]+\\[^\\]+|\\\\\?\\[^\\]+:|)(.*)$"sa, path)::AbstractMatch String(something(m.captures[1])), String(something(m.captures[2])) end else diff --git a/base/regex.jl b/base/regex.jl index d1ef3c9d13d48..400784e1b27d7 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -46,19 +46,24 @@ mutable struct Regex <: AbstractPattern end function Regex(pattern::AbstractString, flags::AbstractString) - options = DEFAULT_COMPILER_OPTS + compile_options = DEFAULT_COMPILER_OPTS + match_options = DEFAULT_MATCH_OPTS for f in flags if f == 'a' - options &= ~PCRE.UCP + # instruct pcre2 to treat the strings as simple bytes (aka "ASCII"), not char encodings + compile_options &= ~PCRE.UCP # user can re-enable with (*UCP) + compile_options &= ~PCRE.UTF # user can re-enable with (*UTF) + compile_options &= ~PCRE.MATCH_INVALID_UTF # this would force on UTF + match_options &= ~PCRE.NO_UTF_CHECK # if the user did force on UTF, we should check it for safety else - options |= f=='i' ? PCRE.CASELESS : - f=='m' ? PCRE.MULTILINE : - f=='s' ? PCRE.DOTALL : - f=='x' ? PCRE.EXTENDED : - throw(ArgumentError("unknown regex flag: $f")) + compile_options |= f=='i' ? PCRE.CASELESS : + f=='m' ? PCRE.MULTILINE : + f=='s' ? PCRE.DOTALL : + f=='x' ? PCRE.EXTENDED : + throw(ArgumentError("unknown regex flag: $f")) end end - Regex(pattern, options, DEFAULT_MATCH_OPTS) + Regex(pattern, compile_options, match_options) end Regex(pattern::AbstractString) = Regex(pattern, DEFAULT_COMPILER_OPTS, DEFAULT_MATCH_OPTS) @@ -96,9 +101,15 @@ listed after the ending quote, to change its behaviour: - `s` allows the `.` modifier to match newlines. - `x` enables "comment mode": whitespace is enabled except when escaped with `\\`, and `#` is treated as starting a comment. -- `a` disables `UCP` mode (enables ASCII mode). By default `\\B`, `\\b`, `\\D`, `\\d`, `\\S`, - `\\s`, `\\W`, `\\w`, etc. match based on Unicode character properties. With this option, - these sequences only match ASCII characters. +- `a` enables ASCII mode (disables `UTF` and `UCP` modes). By default `\\B`, `\\b`, `\\D`, + `\\d`, `\\S`, `\\s`, `\\W`, `\\w`, etc. match based on Unicode character properties. With + this option, these sequences only match ASCII characters. This includes `\\u` also, which + will emit the specified character value directly as a single byte, and not attempt to + encode it into UTF-8. Importantly, this option allows matching against invalid UTF-8 + strings, by treating both matcher and target as simple bytes (as if they were ISO/IEC + 8859-1 / Latin-1 bytes) instead of as character encodings. In this case, this option is + often combined with `s`. This option can be further refined by starting the pattern with + (*UCP) or (*UTF). See [`Regex`](@ref) if interpolation is needed. @@ -112,23 +123,38 @@ This regex has the first three flags enabled. macro r_str(pattern, flags...) Regex(pattern, flags...) end function show(io::IO, re::Regex) - imsxa = PCRE.CASELESS|PCRE.MULTILINE|PCRE.DOTALL|PCRE.EXTENDED|PCRE.UCP + imsx = PCRE.CASELESS|PCRE.MULTILINE|PCRE.DOTALL|PCRE.EXTENDED + ac = PCRE.UTF|PCRE.MATCH_INVALID_UTF|PCRE.UCP + am = PCRE.NO_UTF_CHECK opts = re.compile_options - if (opts & ~imsxa) == (DEFAULT_COMPILER_OPTS & ~imsxa) + mopts = re.match_options + default = ((opts & ~imsx) | ac) == DEFAULT_COMPILER_OPTS + if default + if (opts & ac) == ac + default = mopts == DEFAULT_MATCH_OPTS + elseif (opts & ac) == 0 + default = mopts == (DEFAULT_MATCH_OPTS & ~am) + else + default = false + end + end + if default print(io, "r\"") escape_raw_string(io, re.pattern) print(io, "\"") - if (opts & PCRE.CASELESS ) != 0; print(io, 'i'); end - if (opts & PCRE.MULTILINE) != 0; print(io, 'm'); end - if (opts & PCRE.DOTALL ) != 0; print(io, 's'); end - if (opts & PCRE.EXTENDED ) != 0; print(io, 'x'); end - if (opts & PCRE.UCP ) == 0; print(io, 'a'); end + if (opts & PCRE.CASELESS ) != 0; print(io, "i"); end + if (opts & PCRE.MULTILINE) != 0; print(io, "m"); end + if (opts & PCRE.DOTALL ) != 0; print(io, "s"); end + if (opts & PCRE.EXTENDED ) != 0; print(io, "x"); end + if (opts & ac ) == 0; print(io, "a"); end else print(io, "Regex(") show(io, re.pattern) - print(io, ',') + print(io, ", ") show(io, opts) - print(io, ')') + print(io, ", ") + show(io, mopts) + print(io, ")") end end diff --git a/base/set.jl b/base/set.jl index 5be7eaf004352..a91bf328bd911 100644 --- a/base/set.jl +++ b/base/set.jl @@ -13,7 +13,7 @@ See also: [`AbstractSet`](@ref), [`BitSet`](@ref), [`Dict`](@ref), [`push!`](@ref), [`empty!`](@ref), [`union!`](@ref), [`in`](@ref), [`isequal`](@ref) # Examples -```jldoctest filter = r"^\\S.+" +```jldoctest; filter = r"^ '.'"ma julia> s = Set("aaBca") Set{Char} with 3 elements: 'a' @@ -23,9 +23,9 @@ Set{Char} with 3 elements: julia> push!(s, 'b') Set{Char} with 4 elements: 'a' - 'c' 'b' 'B' + 'c' julia> s = Set([NaN, 0.0, 1.0, 2.0]); diff --git a/base/shell.jl b/base/shell.jl index f443a1f9c094a..7c973ab289c7f 100644 --- a/base/shell.jl +++ b/base/shell.jl @@ -292,9 +292,9 @@ function shell_escape_csh(io::IO, args::AbstractString...) first = false i = 1 while true - for (r,e) = (r"^[A-Za-z0-9/\._-]+\z" => "", - r"^[^']*\z" => "'", r"^[^\$\`\"]*\z" => "\"", - r"^[^']+" => "'", r"^[^\$\`\"]+" => "\"") + for (r,e) = (r"^[A-Za-z0-9/\._-]+\z"sa => "", + r"^[^']*\z"sa => "'", r"^[^\$\`\"]*\z"sa => "\"", + r"^[^']+"sa => "'", r"^[^\$\`\"]+"sa => "\"") if ((m = match(r, SubString(arg, i))) !== nothing) write(io, e) write(io, replace(m.match, '\n' => "\\\n")) @@ -391,7 +391,7 @@ julia> Base.shell_escape_wincmd("a^\\"^o\\"^u\\"") """ function shell_escape_wincmd(io::IO, s::AbstractString) # https://stackoverflow.com/a/4095133/1990689 - occursin(r"[\r\n\0]", s) && + occursin(r"[\r\n\0]"sa, s) && throw(ArgumentError("control character unsupported by CMD.EXE")) i = 1 len = ncodeunits(s) @@ -446,7 +446,7 @@ function escape_microsoft_c_args(io::IO, args::AbstractString...) else write(io, ' ') # separator end - if isempty(arg) || occursin(r"[ \t\"]", arg) + if isempty(arg) || occursin(r"[ \t\"]"sa, arg) # Julia raw strings happen to use the same escaping convention # as the argv[] parser in Microsoft's C runtime library. write(io, '"') diff --git a/test/path.jl b/test/path.jl index 4a4caa6b0b115..2f4f2d0983a58 100644 --- a/test/path.jl +++ b/test/path.jl @@ -171,6 +171,9 @@ @test string(splitdrive(S(homedir()))...) == homedir() @test splitdrive("a\nb") == ("", "a\nb") + @test splitdir("a/\xfe/\n/b/c.ext") == ("a/\xfe/\n/b", "c.ext") + @test splitext("a/\xfe/\n/b/c.ext") == ("a/\xfe/\n/b/c", ".ext") + if Sys.iswindows() @test splitdrive(S("\\\\servername\\hello.world\\filename.ext")) == ("\\\\servername\\hello.world","\\filename.ext") diff --git a/test/regex.jl b/test/regex.jl index 70f620cad7141..e5f1428527512 100644 --- a/test/regex.jl +++ b/test/regex.jl @@ -59,6 +59,11 @@ @test repr(r"\\\"") == raw"r\"\\\\\\\"\"" @test repr(s"\\\"\\") == raw"s\"\\\\\\\"\\\\\"" + @test repr(r""a) == "r\"\"a" + @test repr(r""imsxa) == "r\"\"imsxa" + @test repr(Regex("", Base.DEFAULT_COMPILER_OPTS, UInt32(0))) == """Regex("", $(repr(Base.DEFAULT_COMPILER_OPTS)), $(repr(UInt32(0))))""" + @test repr(Regex("", UInt32(0), Base.DEFAULT_MATCH_OPTS)) == """Regex("", $(repr(UInt32(0))), $(repr(Base.DEFAULT_MATCH_OPTS)))""" + # findall @test findall(r"\w+", "foo bar") == [1:3, 5:7] @test findall(r"\w+", "foo bar", overlap=true) == [1:3, 2:3, 3:3, 5:7, 6:7, 7:7] @@ -122,18 +127,24 @@ # Backcapture reference in substitution string @test replace("abcde", r"(..)(?Pd)" => s"\gxy\\\1") == "adxy\\bce" - @test_throws ErrorException replace("a", r"(?P)" => s"\g") + @test_throws(ErrorException("Bad replacement string: Group y not found in regex r\"(?P)\""), + replace("a", r"(?P)" => s"\g")) # test replace with invalid substitution group pattern - @test_throws ErrorException replace("s", r"(?.)" => s"\gg1>") + @test_throws(ErrorException("Bad replacement string: \\gg1>"), + replace("s", r"(?.)" => s"\gg1>")) # test replace with 2-digit substitution group @test replace(("0" ^ 9) * "1", Regex(("(0)" ^ 9) * "(1)") => s"10th group: \10") == "10th group: 1" # Proper unicode handling @test match(r"∀∀", "∀x∀∀∀").match == "∀∀" - # 'a' flag to disable UCP + # 'a' flag to disable UCP and UTF @test match(r"\w+", "Düsseldorf").match == "Düsseldorf" @test match(r"\w+"a, "Düsseldorf").match == "D" + @test match(r".+"a, "Düsseldorf").match == "Düsseldorf" + @test match(r".+"a, "Dü\xefsseldorf").match == "Dü\xefsseldorf" + @test_throws(ErrorException("PCRE.exec error: $(Base.PCRE.err_message(Base.PCRE.ERROR_UTF8_ERR6))"), + match(r"(*UTF).+"a, "Dü\xefsseldorf")) # Regex behaves like a scalar in broadcasting @test occursin.(r"Hello", ["Hello", "World"]) == [true, false] @@ -211,8 +222,7 @@ end # Test that PCRE throws the correct kind of error - # TODO: Uncomment this once the corresponding change has propagated to CI - #@test_throws ErrorException Base.PCRE.info(C_NULL, Base.PCRE.INFO_NAMECOUNT, UInt32) + @test_throws ErrorException("PCRE error: NULL regex object") Base.PCRE.info(C_NULL, Base.PCRE.INFO_NAMECOUNT, UInt32) # test that we can get the error message of negative error codes @test Base.PCRE.err_message(Base.PCRE.ERROR_NOMEMORY) isa String From 8e3e97074e34f86e7c796536a46fcdeca53a52dc Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 17 Feb 2023 10:59:02 -0500 Subject: [PATCH 148/775] staticdata: encode link_id in tagged linkage (#48673) On 64-bit, we have enough space to encode (1) the tag, (2) the `depmods` index, and (3) the offset all in a single 64-bit pointer field. This means we don't need the external `link_id` arrays, which reduces the size of many pkgimages by ~5%. On 32-bit, we don't have enough bits to implement this strategy. However, most linkages seem to be against the sysimage, and so by giving that a separate tag we can achieve similar compression because the `link_id` lists will be much shorter. Co-authored-by: Tim Holy --- src/codegen.cpp | 6 +- src/gc.c | 3 +- src/jl_exported_data.inc | 2 - src/julia_internal.h | 10 +-- src/staticdata.c | 163 +++++++++++++++++++++------------------ src/staticdata_utils.c | 42 ++++++++++ 6 files changed, 138 insertions(+), 88 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 408fa7c226863..88c27366fa7d6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4251,7 +4251,7 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const bool cache_valid = ctx.use_cache; bool external = false; if (ctx.external_linkage) { - if (jl_object_in_image((jl_value_t*)codeinst)) { + if (0 && jl_object_in_image((jl_value_t*)codeinst)) { // Target is present in another pkgimage cache_valid = true; external = true; @@ -5617,7 +5617,7 @@ static Function *emit_tojlinvoke(jl_code_instance_t *codeinst, Module *M, jl_cod bool cache_valid = params.cache; if (params.external_linkage) { - if (jl_object_in_image((jl_value_t*)codeinst)) { + if (0 && jl_object_in_image((jl_value_t*)codeinst)) { // Target is present in another pkgimage cache_valid = true; } @@ -8529,7 +8529,7 @@ void jl_compile_workqueue( auto invoke = jl_atomic_load_relaxed(&codeinst->invoke); bool cache_valid = params.cache; if (params.external_linkage) { - cache_valid = jl_object_in_image((jl_value_t*)codeinst); + cache_valid = 0 && jl_object_in_image((jl_value_t*)codeinst); } // WARNING: isspecsig is protected by the codegen-lock. If that lock is removed, then the isspecsig load needs to be properly atomically sequenced with this. if (cache_valid && invoke != NULL) { diff --git a/src/gc.c b/src/gc.c index 59cd18e8bd87f..743c5704a53cb 100644 --- a/src/gc.c +++ b/src/gc.c @@ -1985,6 +1985,7 @@ STATIC_INLINE void gc_mark_array8(jl_ptls_t ptls, jl_value_t *ary8_parent, jl_va jl_gc_markqueue_t *mq = &ptls->mark_queue; jl_value_t *new_obj; size_t elsize = ((jl_array_t *)ary8_parent)->elsize / sizeof(jl_value_t *); + assert(elsize > 0); // Decide whether need to chunk ary8 size_t nrefs = (ary8_end - ary8_begin) / elsize; if (nrefs > MAX_REFS_AT_ONCE) { @@ -2016,6 +2017,7 @@ STATIC_INLINE void gc_mark_array16(jl_ptls_t ptls, jl_value_t *ary16_parent, jl_ jl_gc_markqueue_t *mq = &ptls->mark_queue; jl_value_t *new_obj; size_t elsize = ((jl_array_t *)ary16_parent)->elsize / sizeof(jl_value_t *); + assert(elsize > 0); // Decide whether need to chunk ary16 size_t nrefs = (ary16_end - ary16_begin) / elsize; if (nrefs > MAX_REFS_AT_ONCE) { @@ -2668,7 +2670,6 @@ static void gc_mark_roots(jl_gc_markqueue_t *mq) } gc_try_claim_and_push(mq, jl_all_methods, NULL); gc_try_claim_and_push(mq, _jl_debug_method_invalidation, NULL); - gc_try_claim_and_push(mq, jl_build_ids, NULL); // constants gc_try_claim_and_push(mq, jl_emptytuple_type, NULL); gc_try_claim_and_push(mq, cmpswap_names, NULL); diff --git a/src/jl_exported_data.inc b/src/jl_exported_data.inc index cd8abc9b230cd..52f6cb11d8c0f 100644 --- a/src/jl_exported_data.inc +++ b/src/jl_exported_data.inc @@ -126,8 +126,6 @@ XX(jl_voidpointer_type) \ XX(jl_void_type) \ XX(jl_weakref_type) \ - XX(jl_build_ids) \ - XX(jl_linkage_blobs) \ // Data symbols that are defined inside the public libjulia #define JL_EXPORTED_DATA_SYMBOLS(XX) \ diff --git a/src/julia_internal.h b/src/julia_internal.h index 8751281a9d174..8c041701c966d 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -313,9 +313,8 @@ extern tracer_cb jl_newmeth_tracer; void jl_call_tracer(tracer_cb callback, jl_value_t *tracee); void print_func_loc(JL_STREAM *s, jl_method_t *m); extern jl_array_t *_jl_debug_method_invalidation JL_GLOBALLY_ROOTED; -extern arraylist_t jl_linkage_blobs; // external linkage: sysimg/pkgimages -extern jl_array_t *jl_build_ids JL_GLOBALLY_ROOTED; // external linkage: corresponding build_ids -extern arraylist_t jl_image_relocs; // external linkage: sysimg/pkgimages +JL_DLLEXPORT extern arraylist_t jl_linkage_blobs; // external linkage: sysimg/pkgimages +JL_DLLEXPORT extern arraylist_t jl_image_relocs; // external linkage: sysimg/pkgimages extern JL_DLLEXPORT size_t jl_page_size; extern jl_function_t *jl_typeinf_func JL_GLOBALLY_ROOTED; @@ -951,10 +950,7 @@ static inline void jl_set_gc_and_wait(void) // 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 { - if (!jl_build_ids) - return 0; - assert(jl_is_array(jl_build_ids)); - return jl_array_len(jl_build_ids); + return jl_image_relocs.len; } // TODO: Makes this a binary search diff --git a/src/staticdata.c b/src/staticdata.c index e36ea92c3ef00..179df35ff568a 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -302,12 +302,11 @@ static arraylist_t layout_table; // cache of `position(s)` for each `id` in static arraylist_t object_worklist; // used to mimic recursion by jl_serialize_reachable // Permanent list of void* (begin, end+1) pairs of system/package images we've loaded previously -// togther with their module build_ids (used for external linkage) -// jl_linkage_blobs.items[2i:2i+1] correspond to jl_build_ids[i] (0-offset indexing) +// together with their module build_ids (used for external linkage) +// jl_linkage_blobs.items[2i:2i+1] correspond to build_ids[i] (0-offset indexing) // TODO: Keep this sorted so that we can use binary-search arraylist_t jl_linkage_blobs; arraylist_t jl_image_relocs; -jl_array_t *jl_build_ids JL_GLOBALLY_ROOTED = NULL; // hash of definitions for predefined function pointers static htable_t fptr_to_id; @@ -355,9 +354,11 @@ typedef struct { arraylist_t fixup_types; // a list of locations of types requiring (re)caching arraylist_t fixup_objs; // a list of locations of objects requiring (re)caching arraylist_t ccallable_list; // @ccallable entry points to install + // mapping from a buildid_idx to a depmods_idx + jl_array_t *buildid_depmods_idxs; // record of build_ids for all external linkages, in order of serialization for the current sysimg/pkgimg // conceptually, the base pointer for the jth externally-linked item is determined from - // i = findfirst(==(link_ids[j]), jl_build_ids) + // i = findfirst(==(link_ids[j]), build_ids) // blob_base = jl_linkage_blobs.items[2i] # 0-offset indexing // We need separate lists since they are intermingled at creation but split when written. jl_array_t *link_ids_relocs; @@ -376,6 +377,16 @@ static jl_value_t *jl_bigint_type = NULL; static int gmp_limb_size = 0; static jl_sym_t *jl_docmeta_sym = NULL; +#ifdef _P64 +#define RELOC_TAG_OFFSET 61 +#define DEPS_IDX_OFFSET 40 // only on 64-bit can we encode the dependency-index as part of the tagged reloc +#else +// this supports up to 8 RefTags, 512MB of pointer data, and 4/2 (64/32-bit) GB of constant data. +#define RELOC_TAG_OFFSET 29 +#define DEPS_IDX_OFFSET RELOC_TAG_OFFSET +#endif + + // Tags of category `t` are located at offsets `t << RELOC_TAG_OFFSET` // Consequently there is room for 2^RELOC_TAG_OFFSET pointers, etc enum RefTags { @@ -383,9 +394,9 @@ enum RefTags { ConstDataRef, // constant data (e.g., layouts) TagRef, // items serialized via their tags SymbolRef, // symbols - FunctionRef, // generic functions - BuiltinFunctionRef, // builtin functions - ExternalLinkage // items defined externally (used when serializing packages) + FunctionRef, // functions + SysimageLinkage, // reference to the sysimage (from pkgimage) + ExternalLinkage // reference to some other pkgimage }; // calling conventions for internal entry points. @@ -400,13 +411,9 @@ typedef enum { JL_API_MAX } jl_callingconv_t; +// Sub-divisions of some RefTags +const uintptr_t BuiltinFunctionTag = ((uintptr_t)1 << (RELOC_TAG_OFFSET - 1)); -#ifdef _P64 -#define RELOC_TAG_OFFSET 61 -#else -// this supports up to 8 RefTags, 512MB of pointer data, and 4/2 (64/32-bit) GB of constant data. -#define RELOC_TAG_OFFSET 29 -#endif #if RELOC_TAG_OFFSET <= 32 typedef uint32_t reloc_t; @@ -862,20 +869,22 @@ static void write_pointer(ios_t *s) JL_NOTSAFEPOINT static uintptr_t add_external_linkage(jl_serializer_state *s, jl_value_t *v, jl_array_t *link_ids) { size_t i = external_blob_index(v); if (i < n_linkage_blobs()) { - assert(link_ids && jl_is_array(link_ids)); - assert(jl_build_ids && jl_is_array(jl_build_ids)); - uint64_t *build_id_data = (uint64_t*)jl_array_data(jl_build_ids); // We found the sysimg/pkg that this item links against - // Store the image key in `link_ids` - jl_array_grow_end(link_ids, 1); - uint64_t *link_id_data = (uint64_t*)jl_array_data(link_ids); - link_id_data[jl_array_len(link_ids)-1] = build_id_data[i]; // Compute the relocation code size_t offset = (uintptr_t)v - (uintptr_t)jl_linkage_blobs.items[2*i]; offset /= sizeof(void*); - assert(offset < ((uintptr_t)1 << RELOC_TAG_OFFSET) && "offset to external image too large"); - // jl_printf(JL_STDOUT, "External link %ld against blob %d with key %ld at position 0x%lx with offset 0x%lx to \n", jl_array_len(link_ids), i, build_id_data[i>>1], ios_pos(s->s), offset); - // jl_(v); + assert(offset < ((uintptr_t)1 << DEPS_IDX_OFFSET) && "offset to external image too large"); + assert(n_linkage_blobs() == jl_array_len(s->buildid_depmods_idxs)); + size_t depsidx = ((uint32_t*)jl_array_data(s->buildid_depmods_idxs))[i]; // map from build_id_idx -> deps_idx + assert(depsidx < INT32_MAX); + if (depsidx < ((uintptr_t)1 << (RELOC_TAG_OFFSET - DEPS_IDX_OFFSET)) && offset < ((uintptr_t)1 << DEPS_IDX_OFFSET)) + // if it fits in a SysimageLinkage type, use that representation + return ((uintptr_t)SysimageLinkage << RELOC_TAG_OFFSET) + ((uintptr_t)depsidx << DEPS_IDX_OFFSET) + offset; + // otherwise, we store the image key in `link_ids` + assert(link_ids && jl_is_array(link_ids)); + jl_array_grow_end(link_ids, 1); + uint32_t *link_id_data = (uint32_t*)jl_array_data(link_ids); // wait until after the `grow` + link_id_data[jl_array_len(link_ids) - 1] = depsidx; return ((uintptr_t)ExternalLinkage << RELOC_TAG_OFFSET) + offset; } return 0; @@ -1397,12 +1406,13 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED } newm->invoke = NULL; // relocation offset if (fptr_id != JL_API_NULL) { + assert(fptr_id < BuiltinFunctionTag && "too many functions to serialize"); arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_code_instance_t, invoke))); // relocation location arraylist_push(&s->relocs_list, (void*)(((uintptr_t)FunctionRef << RELOC_TAG_OFFSET) + fptr_id)); // relocation target } if (builtin_id >= 2) { arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_code_instance_t, specptr.fptr))); // relocation location - arraylist_push(&s->relocs_list, (void*)(((uintptr_t)BuiltinFunctionRef << RELOC_TAG_OFFSET) + builtin_id - 2)); // relocation target + arraylist_push(&s->relocs_list, (void*)(((uintptr_t)FunctionRef << RELOC_TAG_OFFSET) + BuiltinFunctionTag + builtin_id - 2)); // relocation target } } else if (jl_is_datatype(v)) { @@ -1526,11 +1536,16 @@ static uintptr_t get_reloc_for_item(uintptr_t reloc_item, size_t reloc_offset) case TagRef: assert(offset < 2 * NBOX_C + 258 && "corrupt relocation item id"); break; - case BuiltinFunctionRef: - assert(offset < sizeof(id_to_fptrs) / sizeof(*id_to_fptrs) && "unknown function pointer id"); - break; case FunctionRef: - assert(offset < JL_API_MAX && "unknown function pointer id"); + if (offset & BuiltinFunctionTag) { + offset &= ~BuiltinFunctionTag; + assert(offset < sizeof(id_to_fptrs) / sizeof(*id_to_fptrs) && "unknown function pointer id"); + } + else { + assert(offset < JL_API_MAX && "unknown function pointer id"); + } + break; + case SysimageLinkage: break; case ExternalLinkage: break; @@ -1574,10 +1589,12 @@ static inline uintptr_t get_item_for_reloc(jl_serializer_state *s, uintptr_t bas // offset -= 256; assert(0 && "corrupt relocation item id"); jl_unreachable(); // terminate control flow if assertion is disabled. - case BuiltinFunctionRef: - assert(offset < sizeof(id_to_fptrs) / sizeof(*id_to_fptrs) && "unknown function pointer ID"); - return (uintptr_t)id_to_fptrs[offset]; case FunctionRef: + if (offset & BuiltinFunctionTag) { + offset &= ~BuiltinFunctionTag; + assert(offset < sizeof(id_to_fptrs) / sizeof(*id_to_fptrs) && "unknown function pointer ID"); + return (uintptr_t)id_to_fptrs[offset]; + } switch ((jl_callingconv_t)offset) { case JL_API_BOXED: if (s->image->fptrs.base) @@ -1598,25 +1615,30 @@ static inline uintptr_t get_item_for_reloc(jl_serializer_state *s, uintptr_t bas //default: assert("corrupt relocation item id"); } - case ExternalLinkage: + case SysimageLinkage: { +#ifdef _P64 + size_t depsidx = offset >> DEPS_IDX_OFFSET; + offset &= ((size_t)1 << DEPS_IDX_OFFSET) - 1; +#else + size_t depsidx = 0; +#endif + assert(depsidx < jl_array_len(s->buildid_depmods_idxs)); + size_t i = ((uint32_t*)jl_array_data(s->buildid_depmods_idxs))[depsidx]; + assert(2*i < jl_linkage_blobs.len); + return (uintptr_t)jl_linkage_blobs.items[2*i] + offset*sizeof(void*); + } + case ExternalLinkage: { assert(link_ids); assert(link_index); - assert(jl_build_ids); - uint64_t *link_id_data = (uint64_t*)jl_array_data(link_ids); - uint64_t *build_id_data = (uint64_t*)jl_array_data(jl_build_ids); assert(0 <= *link_index && *link_index < jl_array_len(link_ids)); - uint64_t build_id = link_id_data[*link_index]; + uint32_t depsidx = ((uint32_t*)jl_array_data(link_ids))[*link_index]; *link_index += 1; - size_t i = 0, nids = jl_array_len(jl_build_ids); - while (i < nids) { - if (build_id == build_id_data[i]) - break; - i++; - } - assert(i < nids); + assert(depsidx < jl_array_len(s->buildid_depmods_idxs)); + size_t i = ((uint32_t*)jl_array_data(s->buildid_depmods_idxs))[depsidx]; assert(2*i < jl_linkage_blobs.len); return (uintptr_t)jl_linkage_blobs.items[2*i] + offset*sizeof(void*); } + } abort(); } @@ -1799,7 +1821,7 @@ static jl_value_t *jl_delayed_reloc(jl_serializer_state *s, uintptr_t offset) JL size_t size = s->s->size; int link_index = 0; jl_value_t *ret = (jl_value_t*)get_item_for_reloc(s, base, size, offset, s->link_ids_relocs, &link_index); - assert(link_index < jl_array_len(s->link_ids_relocs)); + assert(!s->link_ids_relocs || link_index < jl_array_len(s->link_ids_relocs)); return ret; } @@ -1894,7 +1916,8 @@ static void jl_update_all_gvars(jl_serializer_state *s, jl_image_t *image, uint3 uintptr_t v = 0; if (i < external_fns_begin) { v = get_item_for_reloc(s, base, size, offset, s->link_ids_gvars, &gvar_link_index); - } else { + } + else { v = get_item_for_reloc(s, base, size, offset, s->link_ids_external_fnvars, &external_fns_link_index); } uintptr_t *gv = sysimg_gvars(image->gvars_base, image->gvars_offsets, i); @@ -2216,7 +2239,7 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new } // In addition to the system image (where `worklist = NULL`), this can also save incremental images with external linkage -static void jl_save_system_image_to_stream(ios_t *f, +static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, jl_array_t *worklist, jl_array_t *extext_methods, jl_array_t *new_specializations, jl_array_t *method_roots_list, jl_array_t *ext_targets, jl_array_t *edges) JL_GC_DISABLED @@ -2264,10 +2287,11 @@ static void jl_save_system_image_to_stream(ios_t *f, arraylist_new(&s.fixup_types, 0); arraylist_new(&s.fixup_objs, 0); arraylist_new(&s.ccallable_list, 0); - s.link_ids_relocs = jl_alloc_array_1d(jl_array_uint64_type, 0); - s.link_ids_gctags = jl_alloc_array_1d(jl_array_uint64_type, 0); - s.link_ids_gvars = jl_alloc_array_1d(jl_array_uint64_type, 0); - s.link_ids_external_fnvars = jl_alloc_array_1d(jl_array_uint64_type, 0); + s.buildid_depmods_idxs = image_to_depmodidx(mod_array); + s.link_ids_relocs = jl_alloc_array_1d(jl_array_int32_type, 0); + s.link_ids_gctags = jl_alloc_array_1d(jl_array_int32_type, 0); + s.link_ids_gvars = jl_alloc_array_1d(jl_array_int32_type, 0); + s.link_ids_external_fnvars = jl_alloc_array_1d(jl_array_int32_type, 0); htable_new(&s.callers_with_edges, 0); jl_value_t **const*const tags = get_tags(); // worklist == NULL ? get_tags() : NULL; @@ -2457,22 +2481,16 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_write_value(&s, edges); } write_uint32(f, jl_array_len(s.link_ids_gctags)); - ios_write(f, (char*)jl_array_data(s.link_ids_gctags), jl_array_len(s.link_ids_gctags)*sizeof(uint64_t)); + ios_write(f, (char*)jl_array_data(s.link_ids_gctags), jl_array_len(s.link_ids_gctags) * sizeof(uint32_t)); write_uint32(f, jl_array_len(s.link_ids_relocs)); - ios_write(f, (char*)jl_array_data(s.link_ids_relocs), jl_array_len(s.link_ids_relocs)*sizeof(uint64_t)); + ios_write(f, (char*)jl_array_data(s.link_ids_relocs), jl_array_len(s.link_ids_relocs) * sizeof(uint32_t)); write_uint32(f, jl_array_len(s.link_ids_gvars)); - ios_write(f, (char*)jl_array_data(s.link_ids_gvars), jl_array_len(s.link_ids_gvars)*sizeof(uint64_t)); + ios_write(f, (char*)jl_array_data(s.link_ids_gvars), jl_array_len(s.link_ids_gvars) * sizeof(uint32_t)); write_uint32(f, jl_array_len(s.link_ids_external_fnvars)); - ios_write(f, (char*)jl_array_data(s.link_ids_external_fnvars), jl_array_len(s.link_ids_external_fnvars)*sizeof(uint64_t)); + ios_write(f, (char*)jl_array_data(s.link_ids_external_fnvars), jl_array_len(s.link_ids_external_fnvars) * sizeof(uint32_t)); write_uint32(f, external_fns_begin); jl_write_arraylist(s.s, &s.ccallable_list); } - // Write the build_id key - uint64_t buildid = 0; - if (worklist) - buildid = jl_worklist_key(worklist); - write_uint32(f, buildid >> 32); - write_uint32(f, buildid & (((uint64_t)1 << 32) - 1)); assert(object_worklist.len == 0); arraylist_free(&object_worklist); @@ -2590,7 +2608,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli } if (_native_data != NULL) native_functions = *_native_data; - jl_save_system_image_to_stream(ff, worklist, extext_methods, new_specializations, method_roots_list, ext_targets, edges); + jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_specializations, method_roots_list, ext_targets, edges); if (_native_data != NULL) native_functions = NULL; // make sure we don't run any Julia code concurrently before this point @@ -2763,25 +2781,26 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl offset_ext_targets = jl_read_offset(&s); offset_edges = jl_read_offset(&s); } + s.buildid_depmods_idxs = depmod_to_imageidx(depmods); size_t nlinks_gctags = read_uint32(f); if (nlinks_gctags > 0) { - s.link_ids_gctags = jl_alloc_array_1d(jl_array_uint64_type, nlinks_gctags); - ios_read(f, (char*)jl_array_data(s.link_ids_gctags), nlinks_gctags * sizeof(uint64_t)); + s.link_ids_gctags = jl_alloc_array_1d(jl_array_int32_type, nlinks_gctags); + ios_read(f, (char*)jl_array_data(s.link_ids_gctags), nlinks_gctags * sizeof(uint32_t)); } size_t nlinks_relocs = read_uint32(f); if (nlinks_relocs > 0) { - s.link_ids_relocs = jl_alloc_array_1d(jl_array_uint64_type, nlinks_relocs); - ios_read(f, (char*)jl_array_data(s.link_ids_relocs), nlinks_relocs * sizeof(uint64_t)); + s.link_ids_relocs = jl_alloc_array_1d(jl_array_int32_type, nlinks_relocs); + ios_read(f, (char*)jl_array_data(s.link_ids_relocs), nlinks_relocs * sizeof(uint32_t)); } size_t nlinks_gvars = read_uint32(f); if (nlinks_gvars > 0) { - s.link_ids_gvars = jl_alloc_array_1d(jl_array_uint64_type, nlinks_gvars); - ios_read(f, (char*)jl_array_data(s.link_ids_gvars), nlinks_gvars * sizeof(uint64_t)); + s.link_ids_gvars = jl_alloc_array_1d(jl_array_int32_type, nlinks_gvars); + ios_read(f, (char*)jl_array_data(s.link_ids_gvars), nlinks_gvars * sizeof(uint32_t)); } size_t nlinks_external_fnvars = read_uint32(f); if (nlinks_external_fnvars > 0) { - s.link_ids_external_fnvars = jl_alloc_array_1d(jl_array_uint64_type, nlinks_external_fnvars); - ios_read(f, (char*)jl_array_data(s.link_ids_external_fnvars), nlinks_external_fnvars * sizeof(uint64_t)); + s.link_ids_external_fnvars = jl_alloc_array_1d(jl_array_int32_type, nlinks_external_fnvars); + ios_read(f, (char*)jl_array_data(s.link_ids_external_fnvars), nlinks_external_fnvars * sizeof(uint32_t)); } uint32_t external_fns_begin = read_uint32(f); jl_read_arraylist(s.s, ccallable_list ? ccallable_list : &s.ccallable_list); @@ -3168,12 +3187,6 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl arraylist_push(&jl_image_relocs, (void*)relocs_base); // jl_printf(JL_STDOUT, "%ld blobs to link against\n", jl_linkage_blobs.len >> 1); - uint64_t buildid = (((uint64_t)read_uint32(f)) << 32) | read_uint32(f); - if (!jl_build_ids) - jl_build_ids = jl_alloc_array_1d(jl_array_uint64_type, 0); - jl_array_grow_end(jl_build_ids, 1); - uint64_t *build_id_data = (uint64_t*)jl_array_data(jl_build_ids); - build_id_data[jl_array_len(jl_build_ids)-1] = buildid; jl_gc_enable(en); } diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 8ba6136e54b0e..e577394c67fae 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -1231,3 +1231,45 @@ JL_DLLEXPORT uint64_t jl_read_verify_header(ios_t *s, uint8_t *pkgimage, int64_t } return checksum; } + +// Returns `depmodidxs` where `j = depmodidxs[i]` corresponds to the blob `depmods[j]` in `write_mod_list` +static jl_array_t *image_to_depmodidx(jl_array_t *depmods) +{ + if (!depmods) + return NULL; + assert(jl_array_len(depmods) < INT32_MAX && "too many dependencies to serialize"); + size_t lbids = n_linkage_blobs(); + size_t ldeps = jl_array_len(depmods); + jl_array_t *depmodidxs = jl_alloc_array_1d(jl_array_int32_type, lbids); + int32_t *dmidxs = (int32_t*)jl_array_data(depmodidxs); + memset(dmidxs, -1, lbids * sizeof(int32_t)); + dmidxs[0] = 0; // the sysimg can also be found at idx 0, by construction + for (size_t i = 0, j = 0; i < ldeps; i++) { + jl_value_t *depmod = jl_array_ptr_ref(depmods, i); + size_t idx = external_blob_index(depmod); + if (idx < lbids) { // jl_object_in_image + j++; + if (dmidxs[idx] == -1) + dmidxs[idx] = j; + } + } + return depmodidxs; +} + +// Returns `imageidxs` where `j = imageidxs[i]` is the blob corresponding to `depmods[j]` +static jl_array_t *depmod_to_imageidx(jl_array_t *depmods) +{ + if (!depmods) + return NULL; + size_t ldeps = jl_array_len(depmods); + jl_array_t *imageidxs = jl_alloc_array_1d(jl_array_int32_type, ldeps + 1); + int32_t *imgidxs = (int32_t*)jl_array_data(imageidxs); + imgidxs[0] = 0; + for (size_t i = 0; i < ldeps; i++) { + jl_value_t *depmod = jl_array_ptr_ref(depmods, i); + size_t j = external_blob_index(depmod); + assert(j < INT32_MAX); + imgidxs[i + 1] = (int32_t)j; + } + return imageidxs; +} From 12d329b6e3db76091688d73ea3e5b81f0de9e1fe Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Fri, 17 Feb 2023 15:01:51 -0500 Subject: [PATCH 149/775] do lazy reallocation after take!(iobuffer) (#48676) closes #27741. closes #48651 --- base/iobuffer.jl | 60 ++++++++++++++++++++++++++++-------- base/strings/basic.jl | 2 +- base/strings/io.jl | 6 ++-- stdlib/REPL/src/LineEdit.jl | 7 +++-- stdlib/REPL/test/lineedit.jl | 6 ++-- 5 files changed, 58 insertions(+), 23 deletions(-) diff --git a/base/iobuffer.jl b/base/iobuffer.jl index 2516201943a5e..6c95285f232f2 100644 --- a/base/iobuffer.jl +++ b/base/iobuffer.jl @@ -5,6 +5,7 @@ # Stateful string mutable struct GenericIOBuffer{T<:AbstractVector{UInt8}} <: IO data::T # T should support: getindex, setindex!, length, copyto!, and resize! + reinit::Bool # if true, data needs to be re-allocated (after take!) readable::Bool writable::Bool seekable::Bool # if not seekable, implementation is free to destroy (compact) past read data @@ -17,7 +18,7 @@ mutable struct GenericIOBuffer{T<:AbstractVector{UInt8}} <: IO function GenericIOBuffer{T}(data::T, readable::Bool, writable::Bool, seekable::Bool, append::Bool, maxsize::Integer) where T<:AbstractVector{UInt8} require_one_based_indexing(data) - new(data,readable,writable,seekable,append,length(data),maxsize,1,-1) + new(data,false,readable,writable,seekable,append,length(data),maxsize,1,-1) end end const IOBuffer = GenericIOBuffer{Vector{UInt8}} @@ -137,8 +138,12 @@ PipeBuffer(data::Vector{UInt8}=UInt8[]; maxsize::Int = typemax(Int)) = GenericIOBuffer(data,true,true,false,true,maxsize) PipeBuffer(maxsize::Integer) = (x = PipeBuffer(StringVector(maxsize), maxsize = maxsize); x.size=0; x) +_similar_data(b::GenericIOBuffer, len::Int) = similar(b.data, len) +_similar_data(b::IOBuffer, len::Int) = StringVector(len) + function copy(b::GenericIOBuffer) - ret = typeof(b)(b.writable ? copy(b.data) : b.data, + ret = typeof(b)(b.reinit ? _similar_data(b, 0) : b.writable ? + copyto!(_similar_data(b, length(b.data)), b.data) : b.data, b.readable, b.writable, b.seekable, b.append, b.maxsize) ret.size = b.size ret.ptr = b.ptr @@ -270,7 +275,10 @@ function truncate(io::GenericIOBuffer, n::Integer) io.seekable || throw(ArgumentError("truncate failed, IOBuffer is not seekable")) n < 0 && throw(ArgumentError("truncate failed, n bytes must be ≥ 0, got $n")) n > io.maxsize && throw(ArgumentError("truncate failed, $(n) bytes is exceeds IOBuffer maxsize $(io.maxsize)")) - if n > length(io.data) + if io.reinit + io.data = _similar_data(io, n) + io.reinit = false + elseif n > length(io.data) resize!(io.data, n) end io.data[io.size+1:n] .= 0 @@ -325,9 +333,14 @@ end ensureroom_slowpath(io, nshort) end n = min((nshort % Int) + (io.append ? io.size : io.ptr-1), io.maxsize) - l = length(io.data) - if n > l - _growend!(io.data, (n - l) % UInt) + if io.reinit + io.data = _similar_data(io, n) + io.reinit = false + else + l = length(io.data) + if n > l + _growend!(io.data, (n - l) % UInt) + end end return io end @@ -390,18 +403,26 @@ end function take!(io::IOBuffer) ismarked(io) && unmark(io) if io.seekable - data = io.data if io.writable - maxsize = (io.maxsize == typemax(Int) ? 0 : min(length(io.data),io.maxsize)) - io.data = StringVector(maxsize) + if io.reinit + data = StringVector(0) + else + data = resize!(io.data, io.size) + io.reinit = true + end else - data = copy(data) + data = copyto!(StringVector(io.size), 1, io.data, 1, io.size) end - resize!(data,io.size) else nbytes = bytesavailable(io) - a = StringVector(nbytes) - data = read!(io, a) + if io.writable + data = io.data + io.reinit = true + _deletebeg!(data, io.ptr-1) + resize!(data, nbytes) + else + data = read!(io, StringVector(nbytes)) + end end if io.writable io.ptr = 1 @@ -410,6 +431,19 @@ function take!(io::IOBuffer) return data end +""" + _unsafe_take!(io::IOBuffer) + +This simply returns the raw resized `io.data`, with no checks to be +sure that `io` is readable etcetera, and leaves `io` in an inconsistent +state. This should only be used internally for performance-critical +`String` routines that immediately discard `io` afterwards, and it +*assumes* that `io` is writable and seekable. + +It saves no allocations compared to `take!`, it just omits some checks. +""" +_unsafe_take!(io::IOBuffer) = resize!(io.data, io.size) + function write(to::IO, from::GenericIOBuffer) if to === from from.ptr = from.size + 1 diff --git a/base/strings/basic.jl b/base/strings/basic.jl index 26d4eb6b91798..ebd6907d7e96c 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -636,7 +636,7 @@ function filter(f, s::AbstractString) for c in s f(c) && write(out, c) end - String(take!(out)) + String(_unsafe_take!(out)) end ## string first and last ## diff --git a/base/strings/io.jl b/base/strings/io.jl index b68bfd2deaaf7..5ae67fc8c841c 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -113,7 +113,7 @@ function sprint(f::Function, args...; context=nothing, sizehint::Integer=0) else f(s, args...) end - String(resize!(s.data, s.size)) + String(_unsafe_take!(s)) end function _str_sizehint(x) @@ -147,7 +147,7 @@ function print_to_string(xs...) for x in xs print(s, x) end - String(resize!(s.data, s.size)) + String(_unsafe_take!(s)) end function string_with_env(env, xs...) @@ -164,7 +164,7 @@ function string_with_env(env, xs...) for x in xs print(env_io, x) end - String(resize!(s.data, s.size)) + String(_unsafe_take!(s)) end """ diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 8deb51de048a7..f6e8da6fa71d9 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -761,10 +761,11 @@ function edit_splice!(s::BufferLike, r::Region=region(s), ins::String = ""; rigi elseif buf.mark >= B buf.mark += sizeof(ins) - B + A end + ensureroom(buf, B) # handle !buf.reinit from take! ret = splice!(buf.data, A+1:B, codeunits(String(ins))) # position(), etc, are 0-indexed buf.size = buf.size + sizeof(ins) - B + A adjust_pos && seek(buf, position(buf) + sizeof(ins)) - return String(ret) + return String(copy(ret)) end edit_splice!(s::MIState, ins::AbstractString) = edit_splice!(s, region(s), ins) @@ -1281,7 +1282,7 @@ end # compute the number of spaces from b till the next non-space on the right # (which can also be "end of line" or "end of buffer") function leadingspaces(buf::IOBuffer, b::Int) - ls = something(findnext(_notspace, buf.data, b+1), 0)-1 + @views ls = something(findnext(_notspace, buf.data[1:buf.size], b+1), 0)-1 ls == -1 && (ls = buf.size) ls -= b return ls @@ -2238,7 +2239,7 @@ end function move_line_end(buf::IOBuffer) eof(buf) && return - pos = findnext(isequal(UInt8('\n')), buf.data, position(buf)+1) + @views pos = findnext(isequal(UInt8('\n')), buf.data[1:buf.size], position(buf)+1) if pos === nothing move_input_end(buf) return diff --git a/stdlib/REPL/test/lineedit.jl b/stdlib/REPL/test/lineedit.jl index 649e294f7c07d..e43b9fdb1c3e1 100644 --- a/stdlib/REPL/test/lineedit.jl +++ b/stdlib/REPL/test/lineedit.jl @@ -306,21 +306,21 @@ seek(buf,0) ## edit_delete_prev_word ## -buf = IOBuffer("type X\n ") +buf = IOBuffer(Vector{UInt8}("type X\n "), read=true, write=true) seekend(buf) @test !isempty(@inferred(LineEdit.edit_delete_prev_word(buf))) @test position(buf) == 5 @test buf.size == 5 @test content(buf) == "type " -buf = IOBuffer("4 +aaa+ x") +buf = IOBuffer(Vector{UInt8}("4 +aaa+ x"), read=true, write=true) seek(buf,8) @test !isempty(LineEdit.edit_delete_prev_word(buf)) @test position(buf) == 3 @test buf.size == 4 @test content(buf) == "4 +x" -buf = IOBuffer("x = func(arg1,arg2 , arg3)") +buf = IOBuffer(Vector{UInt8}("x = func(arg1,arg2 , arg3)"), read=true, write=true) seekend(buf) LineEdit.char_move_word_left(buf) @test position(buf) == 21 From f64463d5936e60d45498d8ad1dac8b2deb77e7bf Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Sat, 18 Feb 2023 16:00:49 -0600 Subject: [PATCH 150/775] Document numerical error in `rank` (#48127) Co-authored-by: Daniel Karrasch <@dkarrasch> Co-authored-by: Fredrik Ekre Co-authored-by: Steven G. Johnson --- stdlib/LinearAlgebra/src/generic.jl | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index 4759f352035f6..ede42c2dbf9d7 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -947,13 +947,22 @@ dot(x::AbstractVector, transA::Transpose{<:Real}, y::AbstractVector) = adjoint(d rank(A::AbstractMatrix; atol::Real=0, rtol::Real=atol>0 ? 0 : n*ϵ) rank(A::AbstractMatrix, rtol::Real) -Compute the rank of a matrix by counting how many singular -values of `A` have magnitude greater than `max(atol, rtol*σ₁)` where `σ₁` is -`A`'s largest singular value. `atol` and `rtol` are the absolute and relative +Compute the numerical rank of a matrix by counting how many outputs of +`svdvals(A)` are greater than `max(atol, rtol*σ₁)` where `σ₁` is `A`'s largest +calculated singular value. `atol` and `rtol` are the absolute and relative tolerances, respectively. The default relative tolerance is `n*ϵ`, where `n` is the size of the smallest dimension of `A`, and `ϵ` is the [`eps`](@ref) of the element type of `A`. +!!! note + Numerical rank can be a sensitive and imprecise characterization of + ill-conditioned matrices with singular values that are close to the threshold + tolerance `max(atol, rtol*σ₁)`. In such cases, slight perturbations to the + singular-value computation or to the matrix can change the result of `rank` + by pushing one or more singular values across the threshold. These variations + can even occur due to changes in floating-point errors between different Julia + versions, architectures, compilers, or operating systems. + !!! compat "Julia 1.1" The `atol` and `rtol` keyword arguments requires at least Julia 1.1. In Julia 1.0 `rtol` is available as a positional argument, but this @@ -981,7 +990,7 @@ function rank(A::AbstractMatrix; atol::Real = 0.0, rtol::Real = (min(size(A)...) isempty(A) && return 0 # 0-dimensional case s = svdvals(A) tol = max(atol, rtol*s[1]) - count(x -> x > tol, s) + count(>(tol), s) end rank(x::Union{Number,AbstractVector}) = iszero(x) ? 0 : 1 From c82aeb71f08703ec7c5929be5d94bec40c90f678 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sun, 19 Feb 2023 08:36:01 +0530 Subject: [PATCH 151/775] `@inbounds` in `copyto!` for structured broadcasting (#48437) * `@inbounds` in diagonal broadcasting * inbounds copyto for bi/tridiag and triangular * Move inbounds to broadcast getindex --------- Co-authored-by: Daniel Karrasch --- .../LinearAlgebra/src/structuredbroadcast.jl | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/stdlib/LinearAlgebra/src/structuredbroadcast.jl b/stdlib/LinearAlgebra/src/structuredbroadcast.jl index 1fa73ba4d9c57..02e39b199679b 100644 --- a/stdlib/LinearAlgebra/src/structuredbroadcast.jl +++ b/stdlib/LinearAlgebra/src/structuredbroadcast.jl @@ -171,7 +171,7 @@ function copyto!(dest::Diagonal, bc::Broadcasted{<:StructuredMatrixStyle}) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for i in axs[1] - dest.diag[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, i)) + dest.diag[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i, i)) end return dest end @@ -181,15 +181,15 @@ function copyto!(dest::Bidiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for i in axs[1] - dest.dv[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, i)) + dest.dv[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i, i)) end if dest.uplo == 'U' for i = 1:size(dest, 1)-1 - dest.ev[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, i+1)) + dest.ev[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i, i+1)) end else for i = 1:size(dest, 1)-1 - dest.ev[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i+1, i)) + dest.ev[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i+1, i)) end end return dest @@ -200,11 +200,11 @@ function copyto!(dest::SymTridiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for i in axs[1] - dest.dv[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, i)) + dest.dv[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i, i)) end for i = 1:size(dest, 1)-1 - v = Broadcast._broadcast_getindex(bc, CartesianIndex(i, i+1)) - v == Broadcast._broadcast_getindex(bc, CartesianIndex(i+1, i)) || throw(ArgumentError("broadcasted assignment breaks symmetry between locations ($i, $(i+1)) and ($(i+1), $i)")) + v = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i, i+1)) + v == (@inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i+1, i))) || throw(ArgumentError("broadcasted assignment breaks symmetry between locations ($i, $(i+1)) and ($(i+1), $i)")) dest.ev[i] = v end return dest @@ -215,11 +215,11 @@ function copyto!(dest::Tridiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for i in axs[1] - dest.d[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, i)) + dest.d[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i, i)) end for i = 1:size(dest, 1)-1 - dest.du[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, i+1)) - dest.dl[i] = Broadcast._broadcast_getindex(bc, CartesianIndex(i+1, i)) + dest.du[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i, i+1)) + dest.dl[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i+1, i)) end return dest end @@ -230,7 +230,7 @@ function copyto!(dest::LowerTriangular, bc::Broadcasted{<:StructuredMatrixStyle} axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for j in axs[2] for i in j:axs[1][end] - dest.data[i,j] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, j)) + @inbounds dest.data[i,j] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, j)) end end return dest @@ -242,7 +242,7 @@ function copyto!(dest::UpperTriangular, bc::Broadcasted{<:StructuredMatrixStyle} axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for j in axs[2] for i in 1:j - dest.data[i,j] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, j)) + @inbounds dest.data[i,j] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, j)) end end return dest From 200c96272a22a5c9d2434a0d091658cb0e7d0fe4 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Mon, 20 Feb 2023 09:49:16 +0100 Subject: [PATCH 152/775] only load extension triggers from the evnironment where the parent package is loaded (#48703) --- base/loading.jl | 56 +++++++++++-------- test/loading.jl | 23 +++++++- .../EnvWithHasExtensions/Manifest.toml | 29 ++++++++++ .../EnvWithHasExtensions/Project.toml | 4 ++ .../EnvWithHasExtensionsv2/Manifest.toml | 25 +++++++++ .../EnvWithHasExtensionsv2/Project.toml | 4 ++ .../HasExtensions_v2.jl/Project.toml | 9 +++ .../HasExtensions_v2.jl/ext/Extension2.jl | 3 + .../HasExtensions_v2.jl/src/HasExtensions.jl | 10 ++++ 9 files changed, 137 insertions(+), 26 deletions(-) create mode 100644 test/project/Extensions/EnvWithHasExtensions/Manifest.toml create mode 100644 test/project/Extensions/EnvWithHasExtensions/Project.toml create mode 100644 test/project/Extensions/EnvWithHasExtensionsv2/Manifest.toml create mode 100644 test/project/Extensions/EnvWithHasExtensionsv2/Project.toml create mode 100644 test/project/Extensions/HasExtensions_v2.jl/Project.toml create mode 100644 test/project/Extensions/HasExtensions_v2.jl/ext/Extension2.jl create mode 100644 test/project/Extensions/HasExtensions_v2.jl/src/HasExtensions.jl diff --git a/base/loading.jl b/base/loading.jl index 447b7839980b5..374f4ccbed4fb 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -258,7 +258,7 @@ struct LoadingCache require_parsed::Set{String} identified_where::Dict{Tuple{PkgId, String}, Union{Nothing, Tuple{PkgId, Union{Nothing, String}}}} identified::Dict{String, Union{Nothing, Tuple{PkgId, Union{Nothing, String}}}} - located::Dict{Tuple{PkgId, Union{String, Nothing}}, Union{String, Nothing}} + located::Dict{Tuple{PkgId, Union{String, Nothing}}, Union{Tuple{Union{String, Nothing}, Union{String, Nothing}}, Nothing}} end const LOADING_CACHE = Ref{Union{LoadingCache, Nothing}}(nothing) LoadingCache() = LoadingCache(load_path(), Dict(), Dict(), Dict(), Set(), Dict(), Dict(), Dict()) @@ -390,30 +390,17 @@ identify_package(where::Module, name::String) = _nothing_or_first(identify_packa identify_package(where::PkgId, name::String) = _nothing_or_first(identify_package_env(where, name)) identify_package(name::String) = _nothing_or_first(identify_package_env(name)) - -""" - Base.locate_package(pkg::PkgId)::Union{String, Nothing} - -The path to the entry-point file for the package corresponding to the identifier -`pkg`, or `nothing` if not found. See also [`identify_package`](@ref). - -```julia-repl -julia> pkg = Base.identify_package("Pkg") -Pkg [44cfe95a-1eb2-52ea-b672-e2afdf69b78f] - -julia> Base.locate_package(pkg) -"/path/to/julia/stdlib/v$(VERSION.major).$(VERSION.minor)/Pkg/src/Pkg.jl" -``` -""" -function locate_package(pkg::PkgId, stopenv::Union{String, Nothing}=nothing)::Union{Nothing,String} +function locate_package_env(pkg::PkgId, stopenv::Union{String, Nothing}=nothing) cache = LOADING_CACHE[] if cache !== nothing - path = get(cache.located, (pkg, stopenv), nothing) - path === nothing || return path + pathenv = get(cache.located, (pkg, stopenv), nothing) + pathenv === nothing || return pathenv end path = nothing + env′ = nothing if pkg.uuid === nothing for env in load_path() + env′ = env # look for the toplevel pkg `pkg.name` in this entry found = project_deps_get(env, pkg.name) if found !== nothing @@ -430,6 +417,7 @@ function locate_package(pkg::PkgId, stopenv::Union{String, Nothing}=nothing)::Un end else for env in load_path() + env′ = env path = manifest_uuid_path(env, pkg) # missing is used as a sentinel to stop looking further down in envs if path === missing @@ -452,9 +440,27 @@ function locate_package(pkg::PkgId, stopenv::Union{String, Nothing}=nothing)::Un end @label done if cache !== nothing - cache.located[(pkg, stopenv)] = path + cache.located[(pkg, stopenv)] = path, env′ end - return path + return path, env′ +end + +""" + Base.locate_package(pkg::PkgId)::Union{String, Nothing} + +The path to the entry-point file for the package corresponding to the identifier +`pkg`, or `nothing` if not found. See also [`identify_package`](@ref). + +```julia-repl +julia> pkg = Base.identify_package("Pkg") +Pkg [44cfe95a-1eb2-52ea-b672-e2afdf69b78f] + +julia> Base.locate_package(pkg) +"/path/to/julia/stdlib/v$(VERSION.major).$(VERSION.minor)/Pkg/src/Pkg.jl" +``` +""" +function locate_package(pkg::PkgId, stopenv::Union{String, Nothing}=nothing)::Union{Nothing,String} + _nothing_or_first(locate_package_env(pkg, stopenv)) end """ @@ -1108,9 +1114,13 @@ const EXT_DORMITORY_FAILED = ExtensionId[] function insert_extension_triggers(pkg::PkgId) pkg.uuid === nothing && return - for env in load_path() - insert_extension_triggers(env, pkg) + path_env_loc = locate_package_env(pkg) + path_env_loc === nothing && return + path, env_loc = path_env_loc + if path === nothing || env_loc === nothing + return end + insert_extension_triggers(env_loc, pkg) end function insert_extension_triggers(env::String, pkg::PkgId)::Union{Nothing,Missing} diff --git a/test/loading.jl b/test/loading.jl index 8b3b90a9b6053..fd32eb51afee3 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1011,10 +1011,10 @@ end begin push!(empty!(DEPOT_PATH), '$(repr(depot_path))') using HasExtensions - # Base.get_extension(HasExtensions, :Extension) === nothing || error("unexpectedly got an extension") + Base.get_extension(HasExtensions, :Extension) === nothing || error("unexpectedly got an extension") HasExtensions.ext_loaded && error("ext_loaded set") using HasDepWithExtensions - # Base.get_extension(HasExtensions, :Extension).extvar == 1 || error("extvar in Extension not set") + Base.get_extension(HasExtensions, :Extension).extvar == 1 || error("extvar in Extension not set") HasExtensions.ext_loaded || error("ext_loaded not set") HasExtensions.ext_folder_loaded && error("ext_folder_loaded set") HasDepWithExtensions.do_something() || error("do_something errored") @@ -1032,12 +1032,29 @@ end @test success(cmd) end - # 48351 sep = Sys.iswindows() ? ';' : ':' + + # 48351 cmd = gen_extension_cmd(``) cmd = addenv(cmd, "JULIA_LOAD_PATH" => join([mktempdir(), proj], sep)) cmd = pipeline(cmd; stdout, stderr) @test success(cmd) + + # Only load env from where package is loaded + envs = [joinpath(@__DIR__, "project", "Extensions", "EnvWithHasExtensionsv2"), joinpath(@__DIR__, "project", "Extensions", "EnvWithHasExtensions")] + cmd = addenv(```$(Base.julia_cmd()) --startup-file=no -e ' + begin + + + push!(empty!(DEPOT_PATH), '$(repr(depot_path))') + using HasExtensions + using ExtDep + Base.get_extension(HasExtensions, :Extension) === nothing || error("unexpectedly loaded ext from other env") + Base.get_extension(HasExtensions, :Extension2) === nothing && error("did not load ext from active env") + end + ' + ```, "JULIA_LOAD_PATH" => join(envs, sep)) + @test success(cmd) finally try rm(depot_path, force=true, recursive=true) diff --git a/test/project/Extensions/EnvWithHasExtensions/Manifest.toml b/test/project/Extensions/EnvWithHasExtensions/Manifest.toml new file mode 100644 index 0000000000000..8ac961fa1a9a9 --- /dev/null +++ b/test/project/Extensions/EnvWithHasExtensions/Manifest.toml @@ -0,0 +1,29 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.9.0-beta4" +manifest_format = "2.0" +project_hash = "caa716752e6dff3d77c3de929ebbb5d2024d04ef" + +[[deps.ExtDep]] +deps = ["SomePackage"] +path = "../ExtDep.jl" +uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" +version = "0.1.0" + +[[deps.HasExtensions]] +path = "../HasExtensions.jl" +uuid = "4d3288b3-3afc-4bb6-85f3-489fffe514c8" +version = "0.1.0" + + [deps.HasExtensions.extensions] + Extension = "ExtDep" + ExtensionFolder = ["ExtDep", "ExtDep2"] + + [deps.HasExtensions.weakdeps] + ExtDep = "fa069be4-f60b-4d4c-8b95-f8008775090c" + ExtDep2 = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" + +[[deps.SomePackage]] +path = "../SomePackage" +uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" +version = "0.1.0" diff --git a/test/project/Extensions/EnvWithHasExtensions/Project.toml b/test/project/Extensions/EnvWithHasExtensions/Project.toml new file mode 100644 index 0000000000000..8639881ae95c0 --- /dev/null +++ b/test/project/Extensions/EnvWithHasExtensions/Project.toml @@ -0,0 +1,4 @@ +[deps] +ExtDep = "fa069be4-f60b-4d4c-8b95-f8008775090c" +HasExtensions = "4d3288b3-3afc-4bb6-85f3-489fffe514c8" +SomePackage = "678608ae-7bb3-42c7-98b1-82102067a3d8" diff --git a/test/project/Extensions/EnvWithHasExtensionsv2/Manifest.toml b/test/project/Extensions/EnvWithHasExtensionsv2/Manifest.toml new file mode 100644 index 0000000000000..66781a5701363 --- /dev/null +++ b/test/project/Extensions/EnvWithHasExtensionsv2/Manifest.toml @@ -0,0 +1,25 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.10.0-DEV" +manifest_format = "2.0" +project_hash = "caa716752e6dff3d77c3de929ebbb5d2024d04ef" + +[[deps.ExtDep]] +deps = ["SomePackage"] +path = "../ExtDep.jl" +uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" +version = "0.1.0" + +[[deps.HasExtensions]] +path = "../HasExtensions_v2.jl" +uuid = "4d3288b3-3afc-4bb6-85f3-489fffe514c8" +version = "0.2.0" +weakdeps = ["ExtDep"] + + [deps.HasExtensions.extensions] + Extension2 = "ExtDep" + +[[deps.SomePackage]] +path = "../SomePackage" +uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" +version = "0.1.0" diff --git a/test/project/Extensions/EnvWithHasExtensionsv2/Project.toml b/test/project/Extensions/EnvWithHasExtensionsv2/Project.toml new file mode 100644 index 0000000000000..8639881ae95c0 --- /dev/null +++ b/test/project/Extensions/EnvWithHasExtensionsv2/Project.toml @@ -0,0 +1,4 @@ +[deps] +ExtDep = "fa069be4-f60b-4d4c-8b95-f8008775090c" +HasExtensions = "4d3288b3-3afc-4bb6-85f3-489fffe514c8" +SomePackage = "678608ae-7bb3-42c7-98b1-82102067a3d8" diff --git a/test/project/Extensions/HasExtensions_v2.jl/Project.toml b/test/project/Extensions/HasExtensions_v2.jl/Project.toml new file mode 100644 index 0000000000000..5d92a4b138058 --- /dev/null +++ b/test/project/Extensions/HasExtensions_v2.jl/Project.toml @@ -0,0 +1,9 @@ +name = "HasExtensions" +uuid = "4d3288b3-3afc-4bb6-85f3-489fffe514c8" +version = "0.2.0" + +[weakdeps] +ExtDep = "fa069be4-f60b-4d4c-8b95-f8008775090c" + +[extensions] +Extension2 = "ExtDep" diff --git a/test/project/Extensions/HasExtensions_v2.jl/ext/Extension2.jl b/test/project/Extensions/HasExtensions_v2.jl/ext/Extension2.jl new file mode 100644 index 0000000000000..d027adec9c223 --- /dev/null +++ b/test/project/Extensions/HasExtensions_v2.jl/ext/Extension2.jl @@ -0,0 +1,3 @@ +module Extension2 + +end diff --git a/test/project/Extensions/HasExtensions_v2.jl/src/HasExtensions.jl b/test/project/Extensions/HasExtensions_v2.jl/src/HasExtensions.jl new file mode 100644 index 0000000000000..dbfaeec4f8812 --- /dev/null +++ b/test/project/Extensions/HasExtensions_v2.jl/src/HasExtensions.jl @@ -0,0 +1,10 @@ +module HasExtensions + +struct HasExtensionsStruct end + +foo(::HasExtensionsStruct) = 1 + +ext_loaded = false +ext_folder_loaded = false + +end # module From d20364c22fdc21071b723e10582fc91e2b195756 Mon Sep 17 00:00:00 2001 From: Shen Muxing <70497715+shenmuxing@users.noreply.github.com> Date: Mon, 20 Feb 2023 18:25:19 +0800 Subject: [PATCH 153/775] Delete duplicate codes in `test/int.jl` (#48730) code in Line 303 `@testset "typemin typemax"` do excactly the same thing --- test/int.jl | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/test/int.jl b/test/int.jl index f1292c98faf40..f79bc5a9781d0 100644 --- a/test/int.jl +++ b/test/int.jl @@ -445,30 +445,6 @@ end @test bitreverse(Int32(456618293)) === Int32(-1399919400) end -@testset "min/max of datatype" begin - @test typemin(Int8) === Int8(-128) - @test typemin(UInt8) === UInt8(0) - @test typemin(Int16) === Int16(-32768) - @test typemin(UInt16) === UInt16(0) - @test typemin(Int32) === Int32(-2147483648) - @test typemin(UInt32) === UInt32(0) - @test typemin(Int64) === Int64(-9223372036854775808) - @test typemin(UInt64) === UInt64(0) - @test typemin(Int128) === Int128(-170141183460469231731687303715884105728) - @test typemin(UInt128) === UInt128(0) - - @test typemax(Int8) === Int8(127) - @test typemax(UInt8) === UInt8(255) - @test typemax(Int16) === Int16(32767) - @test typemax(UInt16) === UInt16(65535) - @test typemax(Int32) === Int32(2147483647) - @test typemax(UInt32) === UInt32(4294967295) - @test typemax(Int64) === Int64(9223372036854775807) - @test typemax(UInt64) === UInt64(0xffffffffffffffff) - @test typemax(Int128) === Int128(170141183460469231731687303715884105727) - @test typemax(UInt128) === UInt128(0xffffffffffffffffffffffffffffffff) -end - @testset "BitIntegerType" begin @test Int isa Base.BitIntegerType @test Base.BitIntegerType === Union{ From aacfcf0afcd325c2ad2e59fc16497405a07b4f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Mon, 20 Feb 2023 14:47:04 +0000 Subject: [PATCH 154/775] [OpenBLAS_jll] Upgrade to new build optimised for PowerPC (#48689) * [OpenBLAS_jll] Upgrade to new build optimised for PowerPC This only difference compared to previous build is that this one enables use of dynamic architecture detection also for the PowerPC architecture. --------- Co-authored-by: Valentin Churavy --- Make.inc | 1 - deps/checksums/openblas | 184 ++++++++++++------------- deps/openblas.mk | 7 +- deps/patches/openblas-power-test.patch | 55 ++++++++ stdlib/OpenBLAS_jll/Project.toml | 2 +- 5 files changed, 154 insertions(+), 95 deletions(-) create mode 100644 deps/patches/openblas-power-test.patch diff --git a/Make.inc b/Make.inc index bb1922c32bc44..b6172c2eb3e72 100644 --- a/Make.inc +++ b/Make.inc @@ -831,7 +831,6 @@ endif # If we are running on powerpc64le or ppc64le, set certain options automatically ifneq (,$(filter $(ARCH), powerpc64le ppc64le)) JCFLAGS += -fsigned-char -OPENBLAS_DYNAMIC_ARCH:=0 OPENBLAS_TARGET_ARCH:=POWER8 BINARY:=64 # GCC doesn't do -march= on ppc64le diff --git a/deps/checksums/openblas b/deps/checksums/openblas index 5c9194f5dd404..96098b9fccf2c 100644 --- a/deps/checksums/openblas +++ b/deps/checksums/openblas @@ -1,94 +1,94 @@ -OpenBLAS.v0.3.21+0.aarch64-apple-darwin-libgfortran5.tar.gz/md5/a89e1eeae1d9539c21598c98da5ac91c -OpenBLAS.v0.3.21+0.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/181334363669482ac78b17ed9797ecc62ead35c07348eddd674c06252a7b36a356db48c62da77e73072df4cc21d0c25e0fb568c4dc7fe98e5db8e0f76eed7183 -OpenBLAS.v0.3.21+0.aarch64-linux-gnu-libgfortran3.tar.gz/md5/7ed3100359f9ed7da4959ecee3b4fd1e -OpenBLAS.v0.3.21+0.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/329e26dae7f2e5ba81ba2271257c03426a349b89831147458a71d91da062bd11fab1b846f77922f3bc65a9f7d3b1914f15aaa0c14f696ba7bf43b55628a5464d -OpenBLAS.v0.3.21+0.aarch64-linux-gnu-libgfortran4.tar.gz/md5/ae71b44c62d42c246a21385d0691dcfa -OpenBLAS.v0.3.21+0.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/0598da4b6410f59a69690e6908c80724df4a8c4761194993c1b127f84418f046d8fa0d367fda8a7faed5cec2d6c57bd8872ba216e38b5418bc9ff20af27528c6 -OpenBLAS.v0.3.21+0.aarch64-linux-gnu-libgfortran5.tar.gz/md5/fff9e118d250bebd55723e77c492280c -OpenBLAS.v0.3.21+0.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/29d831f773cb6a75119c7cc2363fd72d38e32eaef6124505f8b5a1b64fa3ae7a6ffe199aae851de0893259d3bdc480aa377294688ee55d20f0da7dfc49fce747 -OpenBLAS.v0.3.21+0.aarch64-linux-musl-libgfortran3.tar.gz/md5/e93af05f98be926b3000dac3accf5f56 -OpenBLAS.v0.3.21+0.aarch64-linux-musl-libgfortran3.tar.gz/sha512/de3e9cee786d1a37dd5807aca81728d67d60fdace68aa17c69efcc7ebfe36dd3a240dea16f7cd3c5021f0f967f15f1154a3d32350f91165a9fcdd08285917196 -OpenBLAS.v0.3.21+0.aarch64-linux-musl-libgfortran4.tar.gz/md5/5a0226c6781c748a8f4d144b0ae4609b -OpenBLAS.v0.3.21+0.aarch64-linux-musl-libgfortran4.tar.gz/sha512/83d9ff97a5651b682ee1839cf0e1aa8dcd7c2e2d32b6cadb184b8d71123649a31519516b1c7d98c329ab9902538a01ffc14ec28f95ada35ba8da77241d74c2d2 -OpenBLAS.v0.3.21+0.aarch64-linux-musl-libgfortran5.tar.gz/md5/f09c8409b0f4e5e3ee9d471538289e45 -OpenBLAS.v0.3.21+0.aarch64-linux-musl-libgfortran5.tar.gz/sha512/b41234be953779db6601d5bffe43ab9ea23bb542583b16af48fe3a3400b1e50b45d3c91152895c92f6a1f4844ac018c8003f0fd10e9473c503e70e9fc4ad11b0 -OpenBLAS.v0.3.21+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/78ea013e0ba52d289c919df3d5b79946 -OpenBLAS.v0.3.21+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/50ffb9d99d283db2880e767a3ebedbdc7ca67b18782717f5085e0cfc9f6cc55bdeb112e8dca0011e31954a22272936043ca538204fc9be81cb7a0f22d6726f12 -OpenBLAS.v0.3.21+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/47d016b3a50c0c9b2ed1eb5e49904169 -OpenBLAS.v0.3.21+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/d38fe6df24d658a0f54ab007ac6f9b30c0a02fbf86c0592f2e5cf5a8375b654a7428b75f74c20d97d6c953ae9998664c82800813dfa806a5f16dfc20c798c419 -OpenBLAS.v0.3.21+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/17f22b32a0715f82fd48cc5d82f6512c -OpenBLAS.v0.3.21+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/d9318a4cd232abc58907522c20ac666e6db2a92e6a25c9ddd1db0f0169be6f94aadc808014545e92e6168eaa7fa20de4235072186c48ade2e7fc672a700485ad -OpenBLAS.v0.3.21+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/d5a83c8835ad8553d304bf5260b53985 -OpenBLAS.v0.3.21+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/3b506824730c7269eb49e90dc11bfde2b17338ef1504da63e84562433c68586a71b022ad37de3096e06ac24e98828b48638c672789320f76cb33eda4c8e8c9eb -OpenBLAS.v0.3.21+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/59cc4a5aeb63dd84c0dc12cbef7d37af -OpenBLAS.v0.3.21+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/1e9cadcf65232d4a4fba9cda0226e5a5450227e16bf2c27a3268452d5c4e4d5d1321601fd6e1a5c5d92fbc3fc4de21c92e231b5ad3b25dd71eb49d5940fcf243 -OpenBLAS.v0.3.21+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/c04f400d9aca82aac956e94d9fc6fc51 -OpenBLAS.v0.3.21+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/8751300546ccb059fb7755e3f745e7e195cfaf90daf28b151ea2a3d540edf8910c97351d428dda196707599361a200f1a647098fdf5d7163380b4ad2b4a4f733 -OpenBLAS.v0.3.21+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/f251abd323b6bc463ae4a1989670aefb -OpenBLAS.v0.3.21+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/4f179ed09a5f5b71033d09db3894ad78d58a4429d65203225ab7a2a8c887222924910756a5fc4e3974a7cc6f9d994af287490f53cd05fe90f86c4bd4c4023b6d -OpenBLAS.v0.3.21+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/f8ffa30a958448028d1294da9d15f3b2 -OpenBLAS.v0.3.21+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/0a2d9a73be439d78b4af7c70345bdffd531d5687adeea28049adba3c8c9ab7b6ed221703f2a8aff9e7391305355a596dc9a846c84d36d1b4cdfda521f7c05e8c -OpenBLAS.v0.3.21+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/56a4aa4f3eafff290d38dc251a5966cb -OpenBLAS.v0.3.21+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/fce03b1c1becdac66208de20e66949aba113ce2695217d34703a9ba4fd79d364cdf05424282b9d1f25ad9c315baffca3a8bd0af239f6284bd37cbdb2ec3463c6 -OpenBLAS.v0.3.21+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/aca7ef7f854cfe45bedbf1f6b5a97aaf -OpenBLAS.v0.3.21+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/16821728c7a0e56589b60176a06543240f2a061b437dc1cf38723dc56910c6add114a4a5e65eda051e5e88ff7b82b8613ffaf5dad7864f1208eb381159bacc8c -OpenBLAS.v0.3.21+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/9f2c39eef42e5c80f56d36aee156e2b0 -OpenBLAS.v0.3.21+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/441a7833e4e2d1334aeda58d87560e613244138005bc54c74af00d81c26e1e508ce874fccdcd3b3114a74f5e2a102eb520a2e4165b461861ba79fbaff81e4ae3 -OpenBLAS.v0.3.21+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/bf1a9f3e553d6855133b4de3ffc841ee -OpenBLAS.v0.3.21+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/2002b305ef3f3721287ed839d6d08f34127058e6295233f8280cc3ebd06d91cb326ea83f13c0158820c381fa8a2cc74ec1360a65c99bc525f492be561b15cc09 -OpenBLAS.v0.3.21+0.i686-linux-gnu-libgfortran3.tar.gz/md5/6051a0545d272bf19988e2a889531acd -OpenBLAS.v0.3.21+0.i686-linux-gnu-libgfortran3.tar.gz/sha512/21706d2fd8dde6e1e938b0c7a53c6073d52d830d7672702d828d5606582e2c98bdb39fc7ff1fa67188942713e9d718fdf5f014812115d0d0853066c2df21f297 -OpenBLAS.v0.3.21+0.i686-linux-gnu-libgfortran4.tar.gz/md5/4fc17cff9c7a4512245ffead4d75c678 -OpenBLAS.v0.3.21+0.i686-linux-gnu-libgfortran4.tar.gz/sha512/5a28c5a587be439cd2bdf4880caf967cdec14945d26c978fa5c59ce251d5811d460bebc038808e0e8dd2eb4b6a0fdfcaacca4718e2aeb7855f466bd13d1974a7 -OpenBLAS.v0.3.21+0.i686-linux-gnu-libgfortran5.tar.gz/md5/06fa8dff91cff8ba91e2b4bc896e776c -OpenBLAS.v0.3.21+0.i686-linux-gnu-libgfortran5.tar.gz/sha512/493110d06c4361df13ba8e0839b9410551b0bba4fe6e3cdcb53c0dff41a03b3e34ec1c2e73cd4671516631492a16b8dd140a59fa3ac71c348e670858654f2d8a -OpenBLAS.v0.3.21+0.i686-linux-musl-libgfortran3.tar.gz/md5/1b16814a10900c96537b5bfed19e71c2 -OpenBLAS.v0.3.21+0.i686-linux-musl-libgfortran3.tar.gz/sha512/603b2a2fd92286143cb0cb573e3c085db485cf3c4f54987d255efedaea2a8a3d84b83e28f6b2db9dbf05cd31f061845a749b8402d145cc5e8cc2eb369b38e3f5 -OpenBLAS.v0.3.21+0.i686-linux-musl-libgfortran4.tar.gz/md5/20ec87e486f1154d253bc251c1ec0bce -OpenBLAS.v0.3.21+0.i686-linux-musl-libgfortran4.tar.gz/sha512/a2d1736e0f632bddc5140ea88840113b80fedcad51bf5ea93445053eb07c1ae304a1510a85cf964d3a0e087390b8526a0df2bcd24e356b4693a41e5dfc8a671c -OpenBLAS.v0.3.21+0.i686-linux-musl-libgfortran5.tar.gz/md5/df9f4898d550130b0c0c5cede6dd4db3 -OpenBLAS.v0.3.21+0.i686-linux-musl-libgfortran5.tar.gz/sha512/c4c3133904e7b401c5d3013d0ef38b13d7a9804fb4ba38a2c0a062f1badb4d9150214bfc2a1bf55df1299e4151d71a6dbfce7063d9d80a19fe321878c0e59309 -OpenBLAS.v0.3.21+0.i686-w64-mingw32-libgfortran3.tar.gz/md5/3d83d839f80abbd17f84631fbb8f6624 -OpenBLAS.v0.3.21+0.i686-w64-mingw32-libgfortran3.tar.gz/sha512/e51680b4b97f404f244b4d943148f506f84a21f0f59f4d41a3a0cf81d545658d9cc59592a2538c4c077606fc1e6f87eda23063e49828f066e743205c5e6aee8e -OpenBLAS.v0.3.21+0.i686-w64-mingw32-libgfortran4.tar.gz/md5/2ca3ebd61038a5e422a946ede3d17535 -OpenBLAS.v0.3.21+0.i686-w64-mingw32-libgfortran4.tar.gz/sha512/c692b7c00aa0eda4a3fa989f84c797126b1596e13514117ad898166f941f040df67826ba33d0af93673c7a0b478fe4392f9a53d7859b7371878b6608dcb0556b -OpenBLAS.v0.3.21+0.i686-w64-mingw32-libgfortran5.tar.gz/md5/55995848e4bc9ef739e8ba17962787d3 -OpenBLAS.v0.3.21+0.i686-w64-mingw32-libgfortran5.tar.gz/sha512/e274dcfc6593786ac4c32c3f49ec69ab3a0c7278c67bbd04373d4845bff2dfaf17300d4a71e48ebd508e299fa629190ffe70ce074a68e83bac0eafa51f4db2a0 -OpenBLAS.v0.3.21+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/2a3d5f0240a27cf1617d9d4abba6df78 -OpenBLAS.v0.3.21+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/489128c884a0c2b9eb5145b18c68f9c7de9a9cc4131f4635689dc67765f87bec852b0547ebea4ecfdad4eec38063aabe8f6c3e96e5856e08c0c26130c2f11897 -OpenBLAS.v0.3.21+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/bb3501965c26519ecf30830465b12672 -OpenBLAS.v0.3.21+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/bcc78c10f7aba973f1159443e8d408465833ef43530c1c2514715db5a1bb84c0f48510c12b2ac1211b2777328ec682e01ab668818025651f00db7ca747f5674e -OpenBLAS.v0.3.21+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/dc6192b5130e114b3cb1183690c7e398 -OpenBLAS.v0.3.21+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/95d69ace7b145c02acbe13c52d1e7835fdb1e8788c0b03f0f967d88575b322988e4f4acef3b6ad3e505c895f8d19566b8eb9add02f0250cf2d4a14c9f1428f3f -OpenBLAS.v0.3.21+0.x86_64-apple-darwin-libgfortran3.tar.gz/md5/27a9117002f96c41c7907be0475a8d86 -OpenBLAS.v0.3.21+0.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/2247f3691cc552f65a353412c46a76c1ac4b4d994a5725044ba300f0944f15b37144ceff438d77022b441c25eaf530709a4d3ed4523b97d292991b6407a72970 -OpenBLAS.v0.3.21+0.x86_64-apple-darwin-libgfortran4.tar.gz/md5/30dea9ca8b658ff6a9db9245d8ad7500 -OpenBLAS.v0.3.21+0.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/3289e766bfad456b35efae6d341a77698d4d36908ac8d802f47777feed5eef224fde1cb4799b5bd4e8e216c28c78ab4407b92906ddac0bdd1cfb674136c69aaa -OpenBLAS.v0.3.21+0.x86_64-apple-darwin-libgfortran5.tar.gz/md5/ed820c420a67b32df0a9f34760ce605c -OpenBLAS.v0.3.21+0.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/63f43eb0b1c037849fd5addda66d818c0b8188f9758391cd9929e78593c69ec61729be0efce6a9f943ebac178c634cdddffe172ad681ad1c4717949b075a1de7 -OpenBLAS.v0.3.21+0.x86_64-linux-gnu-libgfortran3.tar.gz/md5/a5393eb8e1799b6c089a82d8dde39fb0 -OpenBLAS.v0.3.21+0.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/8ce9ad2f55f131592a87789ec6a824cbe1d23c3be32fb2ba59e107045f75c34684b85d3bab2913923f5a19414a072b5e855c86fddd44a4319a9b5e7b28d5c169 -OpenBLAS.v0.3.21+0.x86_64-linux-gnu-libgfortran4.tar.gz/md5/652aa333440219a4ec17d94dd4e6d358 -OpenBLAS.v0.3.21+0.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/89d7740b462d4216e920dcd5c7867520b2f49c3cb74bd8424efd287927c92a08492c9fa413506248207f9532c7bb9ea2af587a4f70d7db8ea42ac1bc144e8a12 -OpenBLAS.v0.3.21+0.x86_64-linux-gnu-libgfortran5.tar.gz/md5/9e0831544d02a39565a2d0714b1e121a -OpenBLAS.v0.3.21+0.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/9e57a08a09d5fc47d881f9f7ed2e52fbdc7301908cf1be384fe767e6b7771a5980827417049dd37df4d71a861b2cf2a05f25df892b15ed03458910b0bc53101a -OpenBLAS.v0.3.21+0.x86_64-linux-musl-libgfortran3.tar.gz/md5/dde15d3a2f26601cd7ac0a803efbe503 -OpenBLAS.v0.3.21+0.x86_64-linux-musl-libgfortran3.tar.gz/sha512/fa48e51a59b6fb213f88ce8b2778ca5eef73f0721a5c71e27cd8952a34a5003d69710571deb5c0c06526fa8016cfdacabdc2b343342ad0d1e523903fa94a8d42 -OpenBLAS.v0.3.21+0.x86_64-linux-musl-libgfortran4.tar.gz/md5/55b80d13104f4ddc9eefa0424d71330b -OpenBLAS.v0.3.21+0.x86_64-linux-musl-libgfortran4.tar.gz/sha512/fcd816e4dcd767963ae555846cee846c19f0b7d666042d59757eb2eebe115d30af60072c134c3025049712520705dbe9d2862a1f07c955780f9a0de69e6e00b5 -OpenBLAS.v0.3.21+0.x86_64-linux-musl-libgfortran5.tar.gz/md5/e22e46b304962a1edb550e19914cc5e6 -OpenBLAS.v0.3.21+0.x86_64-linux-musl-libgfortran5.tar.gz/sha512/38f8ce1baee4082fd2432dbc1905fd03d8efbcc378aefc9312e90b6054341717ea46bc4d33f9f517e67af0fca2da55b5c5b112850e6071ba18753a4936d78da2 -OpenBLAS.v0.3.21+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/b8b7a9f9aff94b154e8479a84d7abe4b -OpenBLAS.v0.3.21+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/afe4c45d4bf4e38fdbbf00b9e86a65244aadaa2b74e59f9a43f1860c130f721bba2f36186519b2573ff0819bd2b30414cc23800634847db2ecd2107f985495ad -OpenBLAS.v0.3.21+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/8fceea42a8524fef29a54b88ea0a721b -OpenBLAS.v0.3.21+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/e8195597057ab6de0aa5978b4d13b3248ac6acde3f86fc55d9e1c76ec39d464fc2eefea1096cfb5dffbd623f47b06be163c4c74981d2eb13387bc8499b9053fe -OpenBLAS.v0.3.21+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/6f88d96c05663b4aeb81ba8a63882066 -OpenBLAS.v0.3.21+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/f1f516d8d16a2f86bfb26129e0530146de3a4bcb62abcd2c7b9bf64cc09c069e5eeb66658b1cc0cdcc665de98246ad8ed20a7d8b132fe0f0e4d0651d3b4516d4 -OpenBLAS.v0.3.21+0.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/4fb99984ec612a090b294c6b349a7cdb -OpenBLAS.v0.3.21+0.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/81bf55c6398a6cf4a61add084332e7cb79e6d550737641f6c0bc77aa61bd8187603a6100b78c2ef80109c3c5b21f7ba618a4780a5b68e5168a461af521f26c52 -OpenBLAS.v0.3.21+0.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/a1a2c3623d583ab995ac86df07ab73bb -OpenBLAS.v0.3.21+0.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/ec5fab349607862c9e0badaa1fd433e057ac7d056008af683bbb37bf43fef5322e598cd71a6d9c3dd55ef857b39ca634e64572e9ae6e263022dc7f89083f9bca -OpenBLAS.v0.3.21+0.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/05ef0ede7d565247a936c3110c25c83c -OpenBLAS.v0.3.21+0.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/34d2812bc0c6605558cbd6244c41d0805fc9a943cd91a74994bcacdd5ff19612eac8751832e3ee761089a853cf16069e67e13507ca75bbe4b7dc4517e41515e0 +OpenBLAS.v0.3.21+4.aarch64-apple-darwin-libgfortran5.tar.gz/md5/86311621de9f353cdc4495718544c4cc +OpenBLAS.v0.3.21+4.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/7b4b0159d120f3f822d59432091a64c8556fcc2d1c99c8d366400768bf31a463393027f8caeec2bacbb221d3c09142814bd7ffd1f8f479a8942aa177256642ae +OpenBLAS.v0.3.21+4.aarch64-linux-gnu-libgfortran3.tar.gz/md5/eff07fb30c3e35d26fa3a7d0fc837c20 +OpenBLAS.v0.3.21+4.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/70000c634af84974e96ac13481ba729c4668342cd06cb98032c1ad4d23890bafc7f9c2692e8ebae7127d88fc27553e382b80edb078f1a1ba6c13535f56535685 +OpenBLAS.v0.3.21+4.aarch64-linux-gnu-libgfortran4.tar.gz/md5/0015556833727545b248985acf9a79dd +OpenBLAS.v0.3.21+4.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/5b0007054c67629b86520298b462b07908c57a0c42871db99ce834673f7f2164e0cea0b952c4dcf2678cbf75354bcf0a8feee37266ddd41ef2c33a848666b00f +OpenBLAS.v0.3.21+4.aarch64-linux-gnu-libgfortran5.tar.gz/md5/1888c11bfc930840fe57d29ac4fb1dbd +OpenBLAS.v0.3.21+4.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/571f985d34ee403f9af09a143c695c4c626f32b363ae2439d8ca4057041fc966217c6d912089ad0c675e573253cd9327099e71f293cbe5f06be4e077da9f48ea +OpenBLAS.v0.3.21+4.aarch64-linux-musl-libgfortran3.tar.gz/md5/ec3bb6b7d998e37b52ae61439f4e4c58 +OpenBLAS.v0.3.21+4.aarch64-linux-musl-libgfortran3.tar.gz/sha512/7ad0bbfa194f7c8c30533d81a15c4229d300ed1c108af975f381a7b644c1da377b11cff60b60ee193152b91e0d29967f79d9b32d44b54d9b2230a768427ab28a +OpenBLAS.v0.3.21+4.aarch64-linux-musl-libgfortran4.tar.gz/md5/45bffcba3c88e111d903b95d503bead6 +OpenBLAS.v0.3.21+4.aarch64-linux-musl-libgfortran4.tar.gz/sha512/be76750ff245ad91d5e6fad268619ec8732ad1fc1236e02c8e4ff65ecbf5502fa1cae89b026e0414dfe8ec38d002acf79d18f4ae8a320da6dedb3760db969c04 +OpenBLAS.v0.3.21+4.aarch64-linux-musl-libgfortran5.tar.gz/md5/6932c7351aed13bfc25ba56a283595d5 +OpenBLAS.v0.3.21+4.aarch64-linux-musl-libgfortran5.tar.gz/sha512/2df1fd87da29d1ee85bc3208e3435756bfe615df49385072a2a18e15ba54fba754e94be0fdce9f7c370af5ff746b5841beda9a3f5f011b8472b2b579ca2eded5 +OpenBLAS.v0.3.21+4.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/367657c9e84b6016d3b835d98af3dd2a +OpenBLAS.v0.3.21+4.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/3a660c0096f086742e2bac219db732244f35bf527537302e8590ea6e6a369438268ebc479a67096e0ac872f5792f149c6097c64a8afb2624e09687fa4f3bf023 +OpenBLAS.v0.3.21+4.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/86309e18c22fc5fa5e437bc5b8814f28 +OpenBLAS.v0.3.21+4.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/ca51805940efadb27fcdd845551f57a936fbdfbc3caf71fb04eb70360686b75ec5eaf8957e860622d5cbfa4305edacdcfd49bbb48134cd05b96b499faa8e2fd4 +OpenBLAS.v0.3.21+4.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/993d99064baa38498d0c40829bc0899a +OpenBLAS.v0.3.21+4.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/267ebe796afa475a41bb57d6eed1397a5b434945446cd2e612024218fa93f9342bcc4fb8cee0957422aa31ee89c77fe4b07f3f573eb01b6fad0d52d859c7df6c +OpenBLAS.v0.3.21+4.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/243ee32320336ada3524545164ba1fd3 +OpenBLAS.v0.3.21+4.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/2e6b95d82004b8e411441d13e90dd39e39b57288f3502d077daf811709ca1f2eab10fed66648de7cbd0ee37bebb9eef46bd5f476e9ff942f1110b4cde337cea6 +OpenBLAS.v0.3.21+4.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/586032d64e693a46dfe7ae5f56cc6bb3 +OpenBLAS.v0.3.21+4.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/1bc53988dcb22dc82d434a151eb74eadca00ffe3822575497a10229fda967c333828137e76a4afbcc8576ac9261f492ccb4e1e70eb22da977a189b39a72bde63 +OpenBLAS.v0.3.21+4.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/a6b2acfa27a2cf042f228e3f79288161 +OpenBLAS.v0.3.21+4.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/e4b04da8a2002946ca674e191851c506723a91837232025c9e23115348df3187b041725d2406a592e48a595aa3fbe8ff9da9ae70ad8d366e4c118fdba52c9411 +OpenBLAS.v0.3.21+4.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/367657c9e84b6016d3b835d98af3dd2a +OpenBLAS.v0.3.21+4.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/3a660c0096f086742e2bac219db732244f35bf527537302e8590ea6e6a369438268ebc479a67096e0ac872f5792f149c6097c64a8afb2624e09687fa4f3bf023 +OpenBLAS.v0.3.21+4.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/86309e18c22fc5fa5e437bc5b8814f28 +OpenBLAS.v0.3.21+4.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/ca51805940efadb27fcdd845551f57a936fbdfbc3caf71fb04eb70360686b75ec5eaf8957e860622d5cbfa4305edacdcfd49bbb48134cd05b96b499faa8e2fd4 +OpenBLAS.v0.3.21+4.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/993d99064baa38498d0c40829bc0899a +OpenBLAS.v0.3.21+4.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/267ebe796afa475a41bb57d6eed1397a5b434945446cd2e612024218fa93f9342bcc4fb8cee0957422aa31ee89c77fe4b07f3f573eb01b6fad0d52d859c7df6c +OpenBLAS.v0.3.21+4.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/243ee32320336ada3524545164ba1fd3 +OpenBLAS.v0.3.21+4.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/2e6b95d82004b8e411441d13e90dd39e39b57288f3502d077daf811709ca1f2eab10fed66648de7cbd0ee37bebb9eef46bd5f476e9ff942f1110b4cde337cea6 +OpenBLAS.v0.3.21+4.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/586032d64e693a46dfe7ae5f56cc6bb3 +OpenBLAS.v0.3.21+4.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/1bc53988dcb22dc82d434a151eb74eadca00ffe3822575497a10229fda967c333828137e76a4afbcc8576ac9261f492ccb4e1e70eb22da977a189b39a72bde63 +OpenBLAS.v0.3.21+4.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/a6b2acfa27a2cf042f228e3f79288161 +OpenBLAS.v0.3.21+4.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/e4b04da8a2002946ca674e191851c506723a91837232025c9e23115348df3187b041725d2406a592e48a595aa3fbe8ff9da9ae70ad8d366e4c118fdba52c9411 +OpenBLAS.v0.3.21+4.i686-linux-gnu-libgfortran3.tar.gz/md5/e752b2e67a151ea7cfb76c9b15b687e1 +OpenBLAS.v0.3.21+4.i686-linux-gnu-libgfortran3.tar.gz/sha512/230a5fade6d4a205d932362ce40b5455229247ed279e6a0d7105d6207c28d699094c227216aad267aa053878b914043284c7dd9d1c2e4d26d0978efc9071bb48 +OpenBLAS.v0.3.21+4.i686-linux-gnu-libgfortran4.tar.gz/md5/478d429202eb287a840df5bbf191b878 +OpenBLAS.v0.3.21+4.i686-linux-gnu-libgfortran4.tar.gz/sha512/31f98104f1355c01aa86f2fb01165fdf93035319285987d93045518fc4ecc84b59a911369169fc534a4ab3b18c27fe49dc80125263d73ad7d265e6e3913d25a4 +OpenBLAS.v0.3.21+4.i686-linux-gnu-libgfortran5.tar.gz/md5/c8821f59bc938244b2bdc46278c041a5 +OpenBLAS.v0.3.21+4.i686-linux-gnu-libgfortran5.tar.gz/sha512/475413dda79b2263a19eeb657f2c31e36f2f915a702327b3331d294c8783a4f6f4f31b672edfed14cdbdbd2b4f5bf36273f70fa2dd1ec5611925f7a55971780f +OpenBLAS.v0.3.21+4.i686-linux-musl-libgfortran3.tar.gz/md5/9ba93d57e2cf8695699b881715f32f2c +OpenBLAS.v0.3.21+4.i686-linux-musl-libgfortran3.tar.gz/sha512/eb0ea7be9853ab1c220545f628cea4047deacf1fd704c889950f7aeafb58dc75e6cccd1b0c85c30ca12007cce05773382bf4a944f61aa9a0ed0f51621b45fc64 +OpenBLAS.v0.3.21+4.i686-linux-musl-libgfortran4.tar.gz/md5/a4f7aa370d7023f0b062a255a48ff81f +OpenBLAS.v0.3.21+4.i686-linux-musl-libgfortran4.tar.gz/sha512/cc8fd2dd011d4007ea5bd99d909776ca2a3700f6457a92cb43f77684b0dfa5a13f808e03db142647766c385e2bbd4db97f90701f286f5cb04276153d8f6a94fa +OpenBLAS.v0.3.21+4.i686-linux-musl-libgfortran5.tar.gz/md5/5b33f48568c3347a98a72d44786f14c7 +OpenBLAS.v0.3.21+4.i686-linux-musl-libgfortran5.tar.gz/sha512/684ac712c035b14ec1b9b1f0ebad1b813b19155852b7b2af75e5956533d8b90e09bc9652a2beb908d2a1c7e2d22fccbf5479aab10d95528aa173d603f60f6135 +OpenBLAS.v0.3.21+4.i686-w64-mingw32-libgfortran3.tar.gz/md5/7f49b3f4847564539398d50abd8c8f47 +OpenBLAS.v0.3.21+4.i686-w64-mingw32-libgfortran3.tar.gz/sha512/8d57f41a3c092f2373e5ecb91ef7e30176d72686d9b7906cf2a84ebb4d0ed0e4a9303b0814d1bcca13d9345ae424ea4bc40ede656b2084050ca76ce2e0bf1fa9 +OpenBLAS.v0.3.21+4.i686-w64-mingw32-libgfortran4.tar.gz/md5/f75f688f483d0a782365f95e8463f804 +OpenBLAS.v0.3.21+4.i686-w64-mingw32-libgfortran4.tar.gz/sha512/cbb4e0a82f3f003f390241af787ff5579e75a0b37c8174976b19760410718cacbacf40f352e6ec56ab7a88ef56357523606400ded23e0931d8d6c791196e30c5 +OpenBLAS.v0.3.21+4.i686-w64-mingw32-libgfortran5.tar.gz/md5/16e0538b1492142f35e3aefa19d95a8c +OpenBLAS.v0.3.21+4.i686-w64-mingw32-libgfortran5.tar.gz/sha512/21190512ce1c6d5267e01ccf92c111be7bc8807a6114d57b2a5ac74d9be749a82143ad269a293164bf040dc5f5e5353702d49ed48a72dbe9e996e59ac226b407 +OpenBLAS.v0.3.21+4.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/a6d6d1f7bf381edcdc01b47a08ef31f7 +OpenBLAS.v0.3.21+4.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/99bb2a5736eb6c04be445f3bde5e4e3d1010053d615c02d1de69ec65a69f08c5a1a77e146489b69112bb830702d56fa2a3111f44b30f1369e71fbd69afa1e9d2 +OpenBLAS.v0.3.21+4.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/71b32f5fa2b38cc49e08f10c6d28610a +OpenBLAS.v0.3.21+4.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/0cc9c46b299ecf81d7174f962f75562b78ab9c2df794f3dc2dc7c3c15ccafe4baf3f5d23fa3e9ae754b3a0d40f493433829efa15228bf046548d32ee5a90f020 +OpenBLAS.v0.3.21+4.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/2ede77add3d501c98d2b38aebf7f82b5 +OpenBLAS.v0.3.21+4.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/97e4588004ad8b74f72c64fafef150ef0f774b50d48a8b83e269217fccea6b63e8fd9ec9a4907b14d96fee2bd24cd1a347f2744367b2de22d5b7f5a753140892 +OpenBLAS.v0.3.21+4.x86_64-apple-darwin-libgfortran3.tar.gz/md5/c51184816f4b576e0b79b776a631b064 +OpenBLAS.v0.3.21+4.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/cc67ee691f93cf15057ee513a30ff31e440a6438d8ab9eb9a3a6bd10b44cc8ba6428c18dd454f70cf2066f2bbab99374e373b5bda9a3201b0c97239efad135e8 +OpenBLAS.v0.3.21+4.x86_64-apple-darwin-libgfortran4.tar.gz/md5/e5ff0d607488f582317554f856c825f5 +OpenBLAS.v0.3.21+4.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/466801af19cf67bf2755adbc2c895eda7adf67af94e58a21b1415175b3ff0e4913b6b4c14f0d57aa589cdea1972dc597cdd7c345a6fa881b64aa999dc55b22e9 +OpenBLAS.v0.3.21+4.x86_64-apple-darwin-libgfortran5.tar.gz/md5/f35f0f769d71f1bb603f7213a2898d2b +OpenBLAS.v0.3.21+4.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/72e80a685c21d7570d4127ee88baa1900ea64af1c91d307bfe3049a02ad5672c1330549625f10db91ca8dfa45c664cd35bf1e702b110b8317e7c4392cbcfc323 +OpenBLAS.v0.3.21+4.x86_64-linux-gnu-libgfortran3.tar.gz/md5/9c7563a8df3c1d27e4db9f8e5288fc45 +OpenBLAS.v0.3.21+4.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/c6f81ec1da096f6e27e6179235042f0558bc5e3ade27a230bafb2f121b48bc96d7d9e0c45b0e4dc8fee10257913eccabcfcaf67d515e0ba4f373f1ca4869773c +OpenBLAS.v0.3.21+4.x86_64-linux-gnu-libgfortran4.tar.gz/md5/206f6302a757a1a30ab6716b6e508741 +OpenBLAS.v0.3.21+4.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/35adbec09c4a636131a757f4b51d7b458f23e7b7118194be63a116746b2e1e7a15990bd76e8ecbbe2a6010cb6e54bca3a296f02d83b27c6394b42bdffa041653 +OpenBLAS.v0.3.21+4.x86_64-linux-gnu-libgfortran5.tar.gz/md5/9aa48579f0c50500796de228f00909d8 +OpenBLAS.v0.3.21+4.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/ef1b1578259b055a23df4e57bb4bf2c1ef94e4d0d1e63eda622db291256ef8cde8694befcdc83b507cf6c3f47db142b51e6bac614ec32bae92ae576ddd6e2f15 +OpenBLAS.v0.3.21+4.x86_64-linux-musl-libgfortran3.tar.gz/md5/ba5ac7e48691965b3d5045785d37618e +OpenBLAS.v0.3.21+4.x86_64-linux-musl-libgfortran3.tar.gz/sha512/0e37f806faa5bc5cd8e5096efbbbef458720c267ed69028e66ced3269baac53c9e0a92ab728ceab499f6aed3edbcbca6c2b1caaa6e145c386bafb57657a6c353 +OpenBLAS.v0.3.21+4.x86_64-linux-musl-libgfortran4.tar.gz/md5/e9b1ab8fcd6b62b92db6e064054a02ea +OpenBLAS.v0.3.21+4.x86_64-linux-musl-libgfortran4.tar.gz/sha512/75706214b660df35daa3865cc267087ca9aecd04b658d5b9d867d15bad901f00677766c04d7701b45d5238aaf4ed09fafff0ca07e9f501dded9479eea26a5112 +OpenBLAS.v0.3.21+4.x86_64-linux-musl-libgfortran5.tar.gz/md5/d86ecacd9904ef70fe17b762e70986e7 +OpenBLAS.v0.3.21+4.x86_64-linux-musl-libgfortran5.tar.gz/sha512/12edb53c88d7deb4585092ca9d9e3c0578d0ca0e91b388c2eddf84dc080c971dba122195b750e2aa4c3a532a7df2c9da7b56b565f3c2002282621cc63425954e +OpenBLAS.v0.3.21+4.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/494fccd04e83345dc20bd753319b8ed3 +OpenBLAS.v0.3.21+4.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/3000b9ec7b66e09597759570eb77645dec1e82a0b4f56ab9439f2fa3e55bbc2863cc61cbbe77752415fd5926010b1647fffedb8dacaa77b87f815d98bb47d86b +OpenBLAS.v0.3.21+4.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/df1133b14f2561b02f4f2e52106635ed +OpenBLAS.v0.3.21+4.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/ed2c18a9f0768412128dd2e370d85868e536cf3ca4d0d03bc626e4690ba81965c540f08d00b46208a7969bd07b9848e6ff8a14e91a4e927d2b44e9ba5b374e8c +OpenBLAS.v0.3.21+4.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/0ed230111e50d49609d631a2f205dff5 +OpenBLAS.v0.3.21+4.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/1940a500b25f1c2613d2f2ab6533309991808622082a7910985fc0573d41358c5c9311c9bb65a00d5508e7d9e4612773e97adb860cba2c2f4f3957782d695f21 +OpenBLAS.v0.3.21+4.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/f156c495d81f4c70690f7e5be02d8816 +OpenBLAS.v0.3.21+4.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/30b575c6f17b3b59a1d21feba9922a230021a99cd19126bb1c955025906ff7539ac8427b9ec82a950988fa251409b045007a1a2223f6e710b0a6f734f8c00ad5 +OpenBLAS.v0.3.21+4.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/57c919083397ddb90621148635b11bb7 +OpenBLAS.v0.3.21+4.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/7fc6dc8aaea7ed725018e0aabcf3185559ce196f75ec2f18405eaac641d3decb1aae577ace684ffd2539346dcd1444f8f17414291085878c5f80a56550e338cb +OpenBLAS.v0.3.21+4.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/1cdb4c9a5ae04c7c9127acb06a7acbc2 +OpenBLAS.v0.3.21+4.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/a479cef47e8aff321ee9835dfcd69f89db2921afd1e253103d43e0a5b9d831259b07ca99adffd76d412a296e447b58c6585ea29a5905a6266d1d853b50067203 openblas-b89fb708caa5a5a32de8f4306c4ff132e0228e9a.tar.gz/md5/716ebe95d4b491253cdde8308b8adb83 openblas-b89fb708caa5a5a32de8f4306c4ff132e0228e9a.tar.gz/sha512/00e7bde49525c2c28bf07b47290e00b53bff446be63f09e90c51724c6350e5ddc90f5a071ae6de057b3fbb107060e70bf16683fcefcf48ae37ba1d0758be553b diff --git a/deps/openblas.mk b/deps/openblas.mk index e2837bc47232a..e7e53ad139657 100644 --- a/deps/openblas.mk +++ b/deps/openblas.mk @@ -100,7 +100,12 @@ $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/neoverse-generic-kernels.patch-applied: $(BUILDD patch -p1 -f < $(SRCDIR)/patches/neoverse-generic-kernels.patch echo 1 > $@ -$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-configured: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/neoverse-generic-kernels.patch-applied +$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-power-test.patch-applied: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/neoverse-generic-kernels.patch-applied + cd $(BUILDDIR)/$(OPENBLAS_SRC_DIR) && \ + patch -p1 -f < $(SRCDIR)/patches/openblas-power-test.patch + echo 1 > $@ + +$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-configured: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-power-test.patch-applied echo 1 > $@ $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-compiled: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-configured diff --git a/deps/patches/openblas-power-test.patch b/deps/patches/openblas-power-test.patch new file mode 100644 index 0000000000000..aaf5c0ba32639 --- /dev/null +++ b/deps/patches/openblas-power-test.patch @@ -0,0 +1,55 @@ +From d9dc015cfc78fc32f555995a89d6957ef0184ea2 Mon Sep 17 00:00:00 2001 +From: Martin Kroeker +Date: Mon, 8 Aug 2022 14:52:10 +0200 +Subject: [PATCH 1/2] Use blasint for INTERFACE64 compatibility + +--- + test/compare_sgemm_sbgemm.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/test/compare_sgemm_sbgemm.c b/test/compare_sgemm_sbgemm.c +index a2c358cfa7..d4b5914506 100644 +--- a/test/compare_sgemm_sbgemm.c ++++ b/test/compare_sgemm_sbgemm.c +@@ -76,9 +76,9 @@ float16to32 (bfloat16_bits f16) + int + main (int argc, char *argv[]) + { +- int m, n, k; ++ blasint m, n, k; + int i, j, l; +- int x; ++ blasint x; + int ret = 0; + int loop = 100; + char transA = 'N', transB = 'N'; + +From 3d338b57de1837f1e2264a1262a9ee9203f31c6c Mon Sep 17 00:00:00 2001 +From: Martin Kroeker +Date: Mon, 8 Aug 2022 17:09:45 +0200 +Subject: [PATCH 2/2] remove spurious loops + +--- + test/compare_sgemm_sbgemm.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/test/compare_sgemm_sbgemm.c b/test/compare_sgemm_sbgemm.c +index d4b5914506..276fecae9d 100644 +--- a/test/compare_sgemm_sbgemm.c ++++ b/test/compare_sgemm_sbgemm.c +@@ -112,7 +112,6 @@ main (int argc, char *argv[]) + &m, BB, &k, &beta, CC, &m); + for (i = 0; i < n; i++) + for (j = 0; j < m; j++) +- for (l = 0; l < k; l++) + if (fabs (CC[i * m + j] - C[i * m + j]) > 1.0) + ret++; + if (transA == 'N' && transB == 'N') +@@ -126,7 +125,6 @@ main (int argc, char *argv[]) + } + for (i = 0; i < n; i++) + for (j = 0; j < m; j++) +- for (l = 0; l < k; l++) + if (CC[i * m + j] != DD[i * m + j]) + ret++; + } diff --git a/stdlib/OpenBLAS_jll/Project.toml b/stdlib/OpenBLAS_jll/Project.toml index f04e3491ad22b..c117bf553bb73 100644 --- a/stdlib/OpenBLAS_jll/Project.toml +++ b/stdlib/OpenBLAS_jll/Project.toml @@ -1,6 +1,6 @@ name = "OpenBLAS_jll" uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.21+0" +version = "0.3.21+4" [deps] CompilerSupportLibraries_jll = "e66e0078-7015-5450-92f7-15fbd957f2ae" From 1083815fbd84f4b13fc735960a888a1f89f27b9f Mon Sep 17 00:00:00 2001 From: mikmoore <95002244+mikmoore@users.noreply.github.com> Date: Tue, 21 Feb 2023 03:38:39 -0700 Subject: [PATCH 155/775] faster `hypot(::IEEEFloat...)` (re-do) (#48645) Co-authored-by: mikmoore Co-authored-by: Dilum Aluthge Co-authored-by: N5N3 <2642243996@qq.com> --- base/math.jl | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/base/math.jl b/base/math.jl index 004f66f70c74e..88691a5686c17 100644 --- a/base/math.jl +++ b/base/math.jl @@ -845,6 +845,20 @@ function _hypot(x::NTuple{N,<:Number}) where {N} end end +function _hypot(x::NTuple{N,<:IEEEFloat}) where {N} + T = eltype(x) + infT = convert(T, Inf) + x = abs.(x) # doesn't change result but enables computational shortcuts + # note: any() was causing this to not inline for N=3 but mapreduce() was not + mapreduce(==(infT), |, x) && return infT # return Inf even if an argument is NaN + maxabs = reinterpret(T, maximum(z -> reinterpret(Signed, z), x)) # for abs(::IEEEFloat) values, a ::BitInteger cast does not change the result + maxabs > zero(T) || return maxabs # catch NaN before the @fastmath below, but also shortcut 0 since we can (remove if no more @fastmath below) + scale,invscale = scaleinv(maxabs) + # @fastmath(+) to allow reassociation (see #48129) + add_fast(x, y) = Core.Intrinsics.add_float_fast(x, y) # @fastmath is not available during bootstrap + return scale * sqrt(mapreduce(y -> abs2(y * invscale), add_fast, x)) +end + atan(y::Real, x::Real) = atan(promote(float(y),float(x))...) atan(y::T, x::T) where {T<:AbstractFloat} = Base.no_op_err("atan", T) @@ -1070,6 +1084,40 @@ function frexp(x::T) where T<:IEEEFloat return reinterpret(T, xu), k end +""" + $(@__MODULE__).scaleinv(x) + +Compute `(scale, invscale)` where `scale` and `invscale` are non-subnormal +(https://en.wikipedia.org/wiki/Subnormal_number) finite powers of two such that +`scale * invscale == 1` and `scale` is roughly on the order of `abs(x)`. +Inf, NaN, and zero inputs also result in finite nonzero outputs. +These values are useful to scale computations to prevent overflow and underflow +without round-off errors or division. + +UNSTABLE DETAIL: For `x isa IEEEFLoat`, `scale` is chosen to be the +`prevpow(2,abs(x))` when possible, but is never less than floatmin(x) or greater +than inv(floatmin(x)). `Inf` and `NaN` resolve to `inv(floatmin(x))`. This +behavior is subject to change. + +# Examples +```jldoctest +julia> $(@__MODULE__).scaleinv(7.5) +(4.0, 0.25) +``` +""" +function scaleinv(x::T) where T<:IEEEFloat + # by removing the sign and significand and restricting values to a limited range, + # we can invert a number using bit-twiddling instead of division + U = uinttype(T) + umin = reinterpret(U, floatmin(T)) + umax = reinterpret(U, inv(floatmin(T))) + emask = exponent_mask(T) # used to strip sign and significand + u = clamp(reinterpret(U, x) & emask, umin, umax) + scale = reinterpret(T, u) + invscale = reinterpret(T, umin + umax - u) # inv(scale) + return scale, invscale +end + # NOTE: This `rem` method is adapted from the msun `remainder` and `remainderf` # functions, which are under the following license: # From 6523b0cf5b3bb216ad105195c570e0312ea1de6a Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Tue, 21 Feb 2023 05:56:57 -0500 Subject: [PATCH 156/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=205ad4b9e92=20to=203c073aa25=20(#48743)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dilum Aluthge --- .../Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/md5 | 1 + .../Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/sha512 | 1 + .../Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/md5 | 1 - .../Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/md5 create mode 100644 deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/sha512 diff --git a/deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/md5 b/deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/md5 new file mode 100644 index 0000000000000..f88fb6889864b --- /dev/null +++ b/deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/md5 @@ -0,0 +1 @@ +9bdea947b1817ff90de784ddaf767ca0 diff --git a/deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/sha512 b/deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/sha512 new file mode 100644 index 0000000000000..af2939ed3ddaa --- /dev/null +++ b/deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/sha512 @@ -0,0 +1 @@ +c1a0c83967926e8a806f0f8a3dd53b33d98135dff0c5db73fe7f706b49a91222725ecc09c06ccac4531911b4d576afca2eaa166c266d732605e4374c4fc92751 diff --git a/deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/md5 b/deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/md5 deleted file mode 100644 index 9b3ef595fe59d..0000000000000 --- a/deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -94e0a5b63c14d98b2f4d05fd5c6ff1e5 diff --git a/deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/sha512 b/deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/sha512 deleted file mode 100644 index 8a584a2460102..0000000000000 --- a/deps/checksums/Pkg-5ad4b9e928620cd807f22ae0865ae452cd1bea81.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -fd4b47f78acb5981da78609bfba37fbe0ae066f6b0bc65dd090f42677e5e90d19e2deb16b624d9722820fc78b7ffb0d019089bb9032daf08aa083cf1f69e5dd7 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 6bb8ba26baf06..852079f33ddaa 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 5ad4b9e928620cd807f22ae0865ae452cd1bea81 +PKG_SHA1 = 3c073aa253fa4dda70f2d4433989652c8b69e171 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 56667bb47af73c9f6d574dd96ef946983854e1ee Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 21 Feb 2023 13:52:31 +0100 Subject: [PATCH 157/775] put back DelimitedFiles as a non-sysimage "stdlib" (#48671) --- contrib/cache_stdlibs.jl | 1 + .../md5 | 1 + .../sha512 | 1 + stdlib/.gitignore | 2 ++ stdlib/DelimitedFiles.version | 4 ++++ stdlib/Makefile | 2 +- test/loading.jl | 4 ++++ 7 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 deps/checksums/DelimitedFiles-db79c842f95f55b1f8d8037c0d3363ab21cd3b90.tar.gz/md5 create mode 100644 deps/checksums/DelimitedFiles-db79c842f95f55b1f8d8037c0d3363ab21cd3b90.tar.gz/sha512 create mode 100644 stdlib/DelimitedFiles.version diff --git a/contrib/cache_stdlibs.jl b/contrib/cache_stdlibs.jl index bdcc3d9535fa4..09cf2ba0dcb42 100644 --- a/contrib/cache_stdlibs.jl +++ b/contrib/cache_stdlibs.jl @@ -14,6 +14,7 @@ stdlibs = [ :Zlib_jll, :dSFMT_jll, :libLLVM_jll, + :DelimitedFiles, # 2-depth packages :LibSSH2_jll, diff --git a/deps/checksums/DelimitedFiles-db79c842f95f55b1f8d8037c0d3363ab21cd3b90.tar.gz/md5 b/deps/checksums/DelimitedFiles-db79c842f95f55b1f8d8037c0d3363ab21cd3b90.tar.gz/md5 new file mode 100644 index 0000000000000..9c6e4e44927fe --- /dev/null +++ b/deps/checksums/DelimitedFiles-db79c842f95f55b1f8d8037c0d3363ab21cd3b90.tar.gz/md5 @@ -0,0 +1 @@ +ee5afca99801e37fd3a42a9455ae986b diff --git a/deps/checksums/DelimitedFiles-db79c842f95f55b1f8d8037c0d3363ab21cd3b90.tar.gz/sha512 b/deps/checksums/DelimitedFiles-db79c842f95f55b1f8d8037c0d3363ab21cd3b90.tar.gz/sha512 new file mode 100644 index 0000000000000..69a50a7282781 --- /dev/null +++ b/deps/checksums/DelimitedFiles-db79c842f95f55b1f8d8037c0d3363ab21cd3b90.tar.gz/sha512 @@ -0,0 +1 @@ +2adec92de521df1668eb13f2903ffdb01efd6afa5f04ce6fbd1737caa4948f7b629cdda7f75a895853a0cd49dccf8b388860d5c19c29e4d4aad6c7f8fa6b7209 diff --git a/stdlib/.gitignore b/stdlib/.gitignore index ffbc2f12f52da..d159427c40d7c 100644 --- a/stdlib/.gitignore +++ b/stdlib/.gitignore @@ -1,4 +1,6 @@ /srccache +/DelimitedFiles-* +/DelimitedFiles /Pkg-* /Pkg /Statistics-* diff --git a/stdlib/DelimitedFiles.version b/stdlib/DelimitedFiles.version new file mode 100644 index 0000000000000..d741690a96838 --- /dev/null +++ b/stdlib/DelimitedFiles.version @@ -0,0 +1,4 @@ +DELIMITEDFILES_BRANCH = main +DELIMITEDFILES_SHA1 = db79c842f95f55b1f8d8037c0d3363ab21cd3b90 +DELIMITEDFILES_GIT_URL := https://github.com/JuliaData/DelimitedFiles.jl.git +DELIMITEDFILES_TAR_URL = https://api.github.com/repos/JuliaData/DelimitedFiles.jl/tarball/$1 diff --git a/stdlib/Makefile b/stdlib/Makefile index 427bf7fe29ec7..e42061d593905 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -46,7 +46,7 @@ STDLIBS = Artifacts Base64 CRC32c Dates Distributed FileWatching \ SharedArrays Sockets Test TOML Unicode UUIDs \ $(JLL_NAMES) -STDLIBS_EXT = Pkg Statistics LibCURL Downloads ArgTools Tar NetworkOptions SuiteSparse SparseArrays SHA +STDLIBS_EXT = Pkg Statistics LibCURL DelimitedFiles Downloads ArgTools Tar NetworkOptions SuiteSparse SparseArrays SHA $(foreach module, $(STDLIBS_EXT), $(eval $(call stdlib-external,$(module),$(shell echo $(module) | tr a-z A-Z)))) diff --git a/test/loading.jl b/test/loading.jl index fd32eb51afee3..5540728c70b7d 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1152,3 +1152,7 @@ append!(Base.DEPOT_PATH, original_depot_path) wait(t2) wait(t1) end + +@testset "Upgradable stdlibs" begin + @test success(`$(Base.julia_cmd()) --startup-file=no -e 'using DelimitedFiles'`) +end From ba1e568966f82f4732f9a906ab186b02741eccb0 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 21 Feb 2023 08:02:28 -0500 Subject: [PATCH 158/775] Test: add space after test failure for visual clarity (#48526) --- stdlib/Test/docs/src/index.md | 2 ++ stdlib/Test/src/Test.jl | 2 ++ 2 files changed, 4 insertions(+) diff --git a/stdlib/Test/docs/src/index.md b/stdlib/Test/docs/src/index.md index 62d6fedbefc03..c865891daf7c8 100644 --- a/stdlib/Test/docs/src/index.md +++ b/stdlib/Test/docs/src/index.md @@ -55,6 +55,7 @@ julia> @test foo("f") == 20 Test Failed at none:1 Expression: foo("f") == 20 Evaluated: 1 == 20 + ERROR: There was an error during testing ``` @@ -224,6 +225,7 @@ julia> @test 1 ≈ 0.999999 Test Failed at none:1 Expression: 1 ≈ 0.999999 Evaluated: 1 ≈ 0.999999 + ERROR: There was an error during testing ``` You can specify relative and absolute tolerances by setting the `rtol` and `atol` keyword arguments of `isapprox`, respectively, diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 8ac625e1eb8ff..7922bc4d82463 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -165,6 +165,7 @@ function Base.show(io::IO, t::Fail) print(io, "\n Context: ", t.context) end end + println(io) # add some visual space to separate sequential failures end """ @@ -1405,6 +1406,7 @@ julia> @testset let logi = log(im) Test Failed at none:3 Expression: !(iszero(real(logi))) Context: logi = 0.0 + 1.5707963267948966im + ERROR: There was an error during testing ``` """ From 09759069203b06024d87dc49071af7a1519c7559 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Tue, 21 Feb 2023 17:05:24 +0100 Subject: [PATCH 159/775] Add world argument to jl_create_native. (#48746) --- src/aotcompile.cpp | 4 ++-- src/codegen-stubs.c | 2 +- src/julia_internal.h | 2 +- src/precompile_utils.c | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 3f986cbbc489d..d1694eaf9e0d5 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -271,7 +271,7 @@ void replaceUsesWithLoad(Function &F, function_ref Date: Tue, 21 Feb 2023 11:41:48 -0500 Subject: [PATCH 160/775] Make opaque closure constructor more robust (#48739) By type checking the nargs argument rather than just assuming. It's easy for people to end up with the wrong integer type here. --- src/method.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/method.c b/src/method.c index f66cca698d2d5..946f9d986a1ed 100644 --- a/src/method.c +++ b/src/method.c @@ -99,14 +99,16 @@ static jl_value_t *resolve_globals(jl_value_t *expr, jl_module_t *module, jl_sve jl_error("opaque_closure_method: invalid syntax"); } jl_value_t *name = jl_exprarg(e, 0); - jl_value_t *nargs = jl_exprarg(e, 1); + jl_value_t *oc_nargs = jl_exprarg(e, 1); int isva = jl_exprarg(e, 2) == jl_true; jl_value_t *functionloc = jl_exprarg(e, 3); jl_value_t *ci = jl_exprarg(e, 4); if (!jl_is_code_info(ci)) { jl_error("opaque_closure_method: lambda should be a CodeInfo"); + } else if (!jl_is_long(oc_nargs)) { + jl_type_error("opaque_closure_method", (jl_value_t*)jl_long_type, oc_nargs); } - jl_method_t *m = jl_make_opaque_closure_method(module, name, jl_unbox_long(nargs), functionloc, (jl_code_info_t*)ci, isva); + jl_method_t *m = jl_make_opaque_closure_method(module, name, jl_unbox_long(oc_nargs), functionloc, (jl_code_info_t*)ci, isva); return (jl_value_t*)m; } if (e->head == jl_cfunction_sym) { From 81f366d7d731ecaaad34ad4dbacdfe53f4f06fa4 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 21 Feb 2023 16:34:52 -0700 Subject: [PATCH 161/775] staticdata: make hookup of code instances correct (#48751) Previously we would double-account for many of these, leading to occasional chaos. Try to avoid serializing code that does not belong to this incremental compilation session package. Refs: #48723 --- src/method.c | 2 + src/staticdata.c | 55 ++++++++++-------- src/staticdata_utils.c | 128 ++++++++++++++--------------------------- 3 files changed, 77 insertions(+), 108 deletions(-) diff --git a/src/method.c b/src/method.c index 946f9d986a1ed..d5f56a5921358 100644 --- a/src/method.c +++ b/src/method.c @@ -610,6 +610,8 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo) for (int i = 0; i < jl_array_len(func->code); ++i) { jl_value_t *stmt = jl_array_ptr_ref(func->code, i); if (jl_is_expr(stmt) && ((jl_expr_t*)stmt)->head == jl_new_opaque_closure_sym) { + if (jl_options.incremental && jl_generating_output()) + jl_error("Impossible to correctly handle OpaqueClosure inside @generated returned during precompile process."); jl_value_t *uninferred = jl_copy_ast((jl_value_t*)func); jl_value_t *old = NULL; if (jl_atomic_cmpswap(&linfo->uninferred, &old, uninferred)) { diff --git a/src/staticdata.c b/src/staticdata.c index 179df35ff568a..c4ed2b531ae0f 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -600,10 +600,10 @@ static uintptr_t jl_fptr_id(void *fptr) // `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); +static void jl_queue_for_serialization_(jl_serializer_state *s, jl_value_t *v, int recursive, int immediate) JL_GC_DISABLED; -static void jl_queue_module_for_serialization(jl_serializer_state *s, jl_module_t *m) +static void jl_queue_module_for_serialization(jl_serializer_state *s, jl_module_t *m) JL_GC_DISABLED { jl_queue_for_serialization(s, m->name); jl_queue_for_serialization(s, m->parent); @@ -634,7 +634,7 @@ static void jl_queue_module_for_serialization(jl_serializer_state *s, jl_module_ // you want to handle uniquing of `Dict{String,Float64}` before you tackle `Vector{Dict{String,Float64}}`. // Uniquing is done in `serialization_order`, so the very first mention of such an object must // be the "source" rather than merely a cross-reference. -static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_t *v, int recursive, int immediate) +static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_t *v, int recursive, int immediate) JL_GC_DISABLED { jl_datatype_t *t = (jl_datatype_t*)jl_typeof(v); jl_queue_for_serialization_(s, (jl_value_t*)t, 1, immediate); @@ -659,6 +659,7 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ } if (s->incremental && jl_is_method_instance(v)) { jl_method_instance_t *mi = (jl_method_instance_t*)v; + jl_value_t *def = mi->def.value; if (needs_uniquing(v)) { // we only need 3 specific fields of this (the rest are not used) jl_queue_for_serialization(s, mi->def.value); @@ -667,13 +668,24 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ recursive = 0; goto done_fields; } - else if (needs_recaching(v)) { + else if (jl_is_method(def) && jl_object_in_image(def)) { // we only need 3 specific fields of this (the rest are restored afterward, if valid) + // in particular, cache is repopulated by jl_mi_cache_insert for all foreign function, + // so must not be present here record_field_change((jl_value_t**)&mi->uninferred, NULL); record_field_change((jl_value_t**)&mi->backedges, NULL); record_field_change((jl_value_t**)&mi->callbacks, NULL); record_field_change((jl_value_t**)&mi->cache, NULL); } + else { + assert(!needs_recaching(v)); + } + // n.b. opaque closures cannot be inspected and relied upon like a + // normal method since they can get improperly introduced by generated + // functions, so if they appeared at all, we will probably serialize + // them wrong and segfault. The jl_code_for_staged function should + // prevent this from happening, so we do not need to detect that user + // error now. } if (s->incremental && jl_is_globalref(v)) { jl_globalref_t *gr = (jl_globalref_t*)v; @@ -691,6 +703,15 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ assert(!jl_object_in_image((jl_value_t*)tn->wrapper)); } } + if (s->incremental && jl_is_code_instance(v)) { + jl_code_instance_t *ci = (jl_code_instance_t*)v; + // make sure we don't serialize other reachable cache entries of foreign methods + if (jl_object_in_image((jl_value_t*)ci->def->def.value)) { + // TODO: if (ci in ci->defs->cache) + record_field_change((jl_value_t**)&ci->next, NULL); + } + } + if (immediate) // must be things that can be recursively handled, and valid as type parameters assert(jl_is_immutable(t) || jl_is_typevar(v) || jl_is_symbol(v) || jl_is_svec(v)); @@ -769,7 +790,7 @@ done_fields: ; *bp = (void*)((char*)HT_NOTFOUND + 1 + idx); } -static void jl_queue_for_serialization_(jl_serializer_state *s, jl_value_t *v, int recursive, int immediate) +static void jl_queue_for_serialization_(jl_serializer_state *s, jl_value_t *v, int recursive, int immediate) JL_GC_DISABLED { if (!jl_needs_serialization(s, v)) return; @@ -812,7 +833,7 @@ static void jl_queue_for_serialization_(jl_serializer_state *s, jl_value_t *v, i // Do a pre-order traversal of the to-serialize worklist, in the identical order // to the calls to jl_queue_for_serialization would occur in a purely recursive // implementation, but without potentially running out of stack. -static void jl_serialize_reachable(jl_serializer_state *s) +static void jl_serialize_reachable(jl_serializer_state *s) JL_GC_DISABLED { size_t i, prevlen = 0; while (object_worklist.len) { @@ -2813,10 +2834,11 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl *method_roots_list = (jl_array_t*)jl_delayed_reloc(&s, offset_method_roots_list); *ext_targets = (jl_array_t*)jl_delayed_reloc(&s, offset_ext_targets); *edges = (jl_array_t*)jl_delayed_reloc(&s, offset_edges); + if (!*new_specializations) + *new_specializations = jl_alloc_vec_any(0); } s.s = NULL; - // step 3: apply relocations assert(!ios_eof(f)); jl_read_symbols(&s); @@ -3071,19 +3093,8 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl jl_code_instance_t *ci = (jl_code_instance_t*)obj; assert(s.incremental); ci->min_world = world; - if (ci->max_world == 1) { // sentinel value: has edges to external callables - ptrhash_put(&new_code_instance_validate, ci, (void*)(~(uintptr_t)HT_NOTFOUND)); // "HT_FOUND" - } - else if (ci->max_world) { - // It's valid, but it may not be connected - if (!jl_atomic_load_relaxed(&ci->def->cache)) - jl_atomic_store_release(&ci->def->cache, ci); - } - else { - // Ensure this code instance is not connected - if (jl_atomic_load_relaxed(&ci->def->cache) == ci) - jl_atomic_store_release(&ci->def->cache, NULL); - } + if (ci->max_world != 0) + jl_array_ptr_1d_push(*new_specializations, (jl_value_t*)ci); } else if (jl_is_globalref(obj)) { continue; // wait until all the module binding tables have been initialized @@ -3249,7 +3260,6 @@ static jl_value_t *jl_restore_package_image_from_stream(ios_t *f, jl_image_t *im else { ios_close(f); ios_static_buffer(f, sysimg, len); - htable_new(&new_code_instance_validate, 0); pkgcachesizes cachesizes; jl_restore_system_image_from_stream_(f, image, depmods, checksum, (jl_array_t**)&restored, &init_order, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges, &base, &ccallable_list, &cachesizes); JL_SIGATOMIC_END(); @@ -3261,12 +3271,9 @@ static jl_value_t *jl_restore_package_image_from_stream(ios_t *f, jl_image_t *im jl_copy_roots(method_roots_list, jl_worklist_key((jl_array_t*)restored)); // Handle edges jl_insert_backedges((jl_array_t*)edges, (jl_array_t*)ext_targets, (jl_array_t*)new_specializations); // restore external backedges (needs to be last) - // check new CodeInstances and validate any that lack external backedges - validate_new_code_instances(); // reinit ccallables jl_reinit_ccallable(&ccallable_list, base, NULL); arraylist_free(&ccallable_list); - htable_free(&new_code_instance_validate); if (complete) { cachesizes_sv = jl_alloc_svec_uninit(7); jl_svec_data(cachesizes_sv)[0] = jl_box_long(cachesizes.sysdata); diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index e577394c67fae..41d0bdc216fba 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -1,6 +1,3 @@ -static htable_t new_code_instance_validate; - - // inverse of backedges graph (caller=>callees hash) jl_array_t *edges_map JL_GLOBALLY_ROOTED = NULL; // rooted for the duration of our uses of this @@ -172,32 +169,33 @@ static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited, // HT_NOTFOUND: not yet analyzed // HT_NOTFOUND + 1: no link back // HT_NOTFOUND + 2: does link back - // HT_NOTFOUND + 3 + depth: in-progress + // HT_NOTFOUND + 3: does link back, and included in new_specializations already + // HT_NOTFOUND + 4 + depth: in-progress int found = (char*)*bp - (char*)HT_NOTFOUND; if (found) return found - 1; arraylist_push(stack, (void*)mi); int depth = stack->len; - *bp = (void*)((char*)HT_NOTFOUND + 3 + depth); // preliminarily mark as in-progress + *bp = (void*)((char*)HT_NOTFOUND + 4 + depth); // preliminarily mark as in-progress size_t i = 0, n = jl_array_len(mi->backedges); int cycle = 0; while (i < n) { jl_method_instance_t *be; i = get_next_edge(mi->backedges, i, NULL, &be); int child_found = has_backedge_to_worklist(be, visited, stack); - if (child_found == 1) { + if (child_found == 1 || child_found == 2) { // found what we were looking for, so terminate early found = 1; break; } - else if (child_found >= 2 && child_found - 2 < cycle) { + else if (child_found >= 3 && child_found - 3 < cycle) { // record the cycle will resolve at depth "cycle" - cycle = child_found - 2; + cycle = child_found - 3; assert(cycle); } } if (!found && cycle && cycle != depth) - return cycle + 2; + return cycle + 3; // If we are the top of the current cycle, now mark all other parts of // our cycle with what we found. // Or if we found a backedge, also mark all of the other parts of the @@ -205,15 +203,16 @@ static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited, while (stack->len >= depth) { void *mi = arraylist_pop(stack); bp = ptrhash_bp(visited, mi); - assert((char*)*bp - (char*)HT_NOTFOUND == 4 + stack->len); + assert((char*)*bp - (char*)HT_NOTFOUND == 5 + stack->len); *bp = (void*)((char*)HT_NOTFOUND + 1 + found); } return found; } // Given the list of CodeInstances that were inferred during the build, select -// those that are (1) external, (2) still valid, and (3) are inferred to be -// called from the worklist or explicitly added by a `precompile` statement. +// those that are (1) external, (2) still valid, (3) are inferred to be called +// from the worklist or explicitly added by a `precompile` statement, and +// (4) are the most recently computed result for that method. // These will be preserved in the image. static jl_array_t *queue_external_cis(jl_array_t *list) { @@ -228,23 +227,35 @@ static jl_array_t *queue_external_cis(jl_array_t *list) arraylist_new(&stack, 0); jl_array_t *new_specializations = jl_alloc_vec_any(0); JL_GC_PUSH1(&new_specializations); - for (i = 0; i < n0; i++) { + for (i = n0; i-- > 0; ) { jl_code_instance_t *ci = (jl_code_instance_t*)jl_array_ptr_ref(list, i); assert(jl_is_code_instance(ci)); jl_method_instance_t *mi = ci->def; jl_method_t *m = mi->def.method; - if (jl_is_method(m) && jl_object_in_image((jl_value_t*)m->module)) { + if (ci->inferred && jl_is_method(m) && jl_object_in_image((jl_value_t*)m->module)) { int found = has_backedge_to_worklist(mi, &visited, &stack); - assert(found == 0 || found == 1); + assert(found == 0 || found == 1 || found == 2); assert(stack.len == 0); if (found == 1 && ci->max_world == ~(size_t)0) { - jl_array_ptr_1d_push(new_specializations, (jl_value_t*)ci); + void **bp = ptrhash_bp(&visited, mi); + if (*bp != (void*)((char*)HT_NOTFOUND + 3)) { + *bp = (void*)((char*)HT_NOTFOUND + 3); + jl_array_ptr_1d_push(new_specializations, (jl_value_t*)ci); + } } } } htable_free(&visited); arraylist_free(&stack); JL_GC_POP(); + // reverse new_specializations + n0 = jl_array_len(new_specializations); + jl_value_t **news = (jl_value_t**)jl_array_data(new_specializations); + for (i = 0; i < n0; i++) { + jl_value_t *temp = news[i]; + news[i] = news[n0 - i - 1]; + news[n0 - i - 1] = temp; + } return new_specializations; } @@ -810,11 +821,6 @@ static void jl_copy_roots(jl_array_t *method_roots_list, uint64_t key) } } -static int remove_code_instance_from_validation(jl_code_instance_t *codeinst) -{ - return ptrhash_remove(&new_code_instance_validate, codeinst); -} - // verify that these edges intersect with the same methods as before static jl_array_t *jl_verify_edges(jl_array_t *targets) { @@ -1045,45 +1051,30 @@ static void jl_insert_backedges(jl_array_t *edges, jl_array_t *ext_targets, jl_a htable_new(&visited, 0); jl_verify_methods(edges, valids, &visited); // consumes valids, creates visited valids = jl_verify_graph(edges, &visited); // consumes visited, creates valids - size_t i, l = jl_array_len(edges) / 2; + size_t i, l; // next build a map from external MethodInstances to their CodeInstance for insertion - if (ci_list == NULL) { - htable_reset(&visited, 0); - } - else { - size_t i, l = jl_array_len(ci_list); - htable_reset(&visited, l); - for (i = 0; i < l; i++) { - jl_code_instance_t *ci = (jl_code_instance_t*)jl_array_ptr_ref(ci_list, i); - assert(ci->max_world == 1 || ci->max_world == ~(size_t)0); - assert(ptrhash_get(&visited, (void*)ci->def) == HT_NOTFOUND); // check that we don't have multiple cis for same mi - ptrhash_put(&visited, (void*)ci->def, (void*)ci); - } - } - - // next disable any invalid codes, so we do not try to enable them + l = jl_array_len(ci_list); + htable_reset(&visited, l); for (i = 0; i < l; i++) { - jl_method_instance_t *caller = (jl_method_instance_t*)jl_array_ptr_ref(edges, 2 * i); - assert(jl_is_method_instance(caller) && jl_is_method(caller->def.method)); - int valid = jl_array_uint8_ref(valids, i); - if (valid) - continue; - void *ci = ptrhash_get(&visited, (void*)caller); - if (ci != HT_NOTFOUND) { - assert(jl_is_code_instance(ci)); - remove_code_instance_from_validation((jl_code_instance_t*)ci); // mark it as handled + jl_code_instance_t *ci = (jl_code_instance_t*)jl_array_ptr_ref(ci_list, i); + assert(ci->min_world == world); + if (ci->max_world == 1) { // sentinel value: has edges to external callables + ptrhash_put(&visited, (void*)ci->def, (void*)ci); } else { - jl_code_instance_t *codeinst = jl_atomic_load_relaxed(&caller->cache); - while (codeinst) { - remove_code_instance_from_validation(codeinst); // should be left invalid - codeinst = jl_atomic_load_relaxed(&codeinst->next); + assert(ci->max_world == ~(size_t)0); + jl_method_instance_t *caller = ci->def; + if (ci->inferred && jl_rettype_inferred(caller, world, ~(size_t)0) == jl_nothing) { + jl_mi_cache_insert(caller, ci); } + //jl_static_show((jl_stream*)ios_stderr, (jl_value_t*)caller); + //ios_puts("free\n", ios_stderr); } } - // finally enable any applicable new codes + // next enable any applicable new codes + l = jl_array_len(edges) / 2; for (i = 0; i < l; i++) { jl_method_instance_t *caller = (jl_method_instance_t*)jl_array_ptr_ref(edges, 2 * i); int valid = jl_array_uint8_ref(valids, i); @@ -1110,29 +1101,19 @@ static void jl_insert_backedges(jl_array_t *edges, jl_array_t *ext_targets, jl_a jl_method_table_add_backedge(mt, sig, (jl_value_t*)caller); } } - // then enable it + // then enable any methods associated with it void *ci = ptrhash_get(&visited, (void*)caller); + //assert(ci != HT_NOTFOUND); if (ci != HT_NOTFOUND) { // have some new external code to use assert(jl_is_code_instance(ci)); jl_code_instance_t *codeinst = (jl_code_instance_t*)ci; - remove_code_instance_from_validation(codeinst); // mark it as handled assert(codeinst->min_world == world && codeinst->inferred); codeinst->max_world = ~(size_t)0; if (jl_rettype_inferred(caller, world, ~(size_t)0) == jl_nothing) { jl_mi_cache_insert(caller, codeinst); } } - else { - jl_code_instance_t *codeinst = jl_atomic_load_relaxed(&caller->cache); - while (codeinst) { - if (remove_code_instance_from_validation(codeinst)) { // mark it as handled - assert(codeinst->min_world >= world && codeinst->inferred); - codeinst->max_world = ~(size_t)0; - } - codeinst = jl_atomic_load_relaxed(&codeinst->next); - } - } } htable_free(&visited); @@ -1148,27 +1129,6 @@ static void classify_callers(htable_t *callers_with_edges, jl_array_t *edges) } } -static void validate_new_code_instances(void) -{ - size_t world = jl_atomic_load_acquire(&jl_world_counter); - size_t i; - for (i = 0; i < new_code_instance_validate.size; i += 2) { - if (new_code_instance_validate.table[i+1] != HT_NOTFOUND) { - //assert(0 && "unexpected unprocessed CodeInstance found"); - jl_code_instance_t *ci = (jl_code_instance_t*)new_code_instance_validate.table[i]; - JL_GC_PROMISE_ROOTED(ci); // TODO: this needs a root (or restructuring to avoid it) - assert(ci->min_world == world && ci->inferred); - assert(ci->max_world == ~(size_t)0); - jl_method_instance_t *caller = ci->def; - if (jl_rettype_inferred(caller, world, ~(size_t)0) == jl_nothing) { - jl_mi_cache_insert(caller, ci); - } - //jl_static_show((JL_STREAM*)ios_stderr, (jl_value_t*)caller); - //ios_puts("FREE\n", ios_stderr); - } - } -} - static jl_value_t *read_verify_mod_list(ios_t *s, jl_array_t *depmods) { if (!jl_main_module->build_id.lo) { From 6412a56223e38824ce6eff78bf34662637971e1c Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Tue, 21 Feb 2023 23:07:54 -0500 Subject: [PATCH 162/775] Atomically order specsigflags, specptr, and invoke (#47832) Co-authored-by: Jameson Nash Co-authored-by: Dilum Aluthge --- src/codegen.cpp | 30 ++++++++++---- src/gf.c | 96 ++++++++++++++++++++++++++++++-------------- src/jitlayers.cpp | 46 +++++++++++++-------- src/julia.h | 3 +- src/julia_atomics.h | 9 +++++ src/opaque_closure.c | 27 ++++++++----- src/staticdata.c | 6 +-- 7 files changed, 146 insertions(+), 71 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 88c27366fa7d6..c2a042967c97a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4236,7 +4236,7 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const jl_value_t *ci = ctx.params->lookup(mi, ctx.world, ctx.world); // TODO: need to use the right pair world here jl_code_instance_t *codeinst = (jl_code_instance_t*)ci; if (ci != jl_nothing) { - auto invoke = jl_atomic_load_relaxed(&codeinst->invoke); + auto invoke = jl_atomic_load_acquire(&codeinst->invoke); // check if we know how to handle this specptr if (invoke == jl_fptr_const_return_addr) { result = mark_julia_const(ctx, codeinst->rettype_const); @@ -4262,10 +4262,13 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const // optimization: emit the correct name immediately, if we know it // TODO: use `emitted` map here too to try to consolidate names? // WARNING: isspecsig is protected by the codegen-lock. If that lock is removed, then the isspecsig load needs to be properly atomically sequenced with this. - auto invoke = jl_atomic_load_relaxed(&codeinst->invoke); auto fptr = jl_atomic_load_relaxed(&codeinst->specptr.fptr); if (fptr) { - if (specsig ? codeinst->isspecsig : invoke == jl_fptr_args_addr) { + while (!(jl_atomic_load_acquire(&codeinst->specsigflags) & 0b10)) { + jl_cpu_pause(); + } + invoke = jl_atomic_load_relaxed(&codeinst->invoke); + if (specsig ? jl_atomic_load_relaxed(&codeinst->specsigflags) & 0b1 : invoke == jl_fptr_args_addr) { protoname = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)fptr, codeinst); need_to_emit = false; } @@ -5783,9 +5786,15 @@ static Function* gen_cfun_wrapper( if (lam && params.cache) { // TODO: this isn't ideal to be unconditionally calling type inference (and compile) from here codeinst = jl_compile_method_internal(lam, world); - auto invoke = jl_atomic_load_relaxed(&codeinst->invoke); + auto invoke = jl_atomic_load_acquire(&codeinst->invoke); auto fptr = jl_atomic_load_relaxed(&codeinst->specptr.fptr); assert(invoke); + if (fptr) { + while (!(jl_atomic_load_acquire(&codeinst->specsigflags) & 0b10)) { + jl_cpu_pause(); + } + invoke = jl_atomic_load_relaxed(&codeinst->invoke); + } // WARNING: this invoke load is protected by the codegen-lock. If that lock is removed, then the isspecsig load needs to be properly atomically sequenced with this. if (invoke == jl_fptr_args_addr) { callptr = fptr; @@ -5796,7 +5805,7 @@ static Function* gen_cfun_wrapper( callptr = (void*)codeinst->rettype_const; calltype = 2; } - else if (codeinst->isspecsig) { + else if (jl_atomic_load_relaxed(&codeinst->specsigflags) & 0b1) { callptr = fptr; calltype = 3; } @@ -8526,7 +8535,7 @@ void jl_compile_workqueue( "invalid world for code-instance"); StringRef preal_decl = ""; bool preal_specsig = false; - auto invoke = jl_atomic_load_relaxed(&codeinst->invoke); + auto invoke = jl_atomic_load_acquire(&codeinst->invoke); bool cache_valid = params.cache; if (params.external_linkage) { cache_valid = 0 && jl_object_in_image((jl_value_t*)codeinst); @@ -8534,10 +8543,17 @@ void jl_compile_workqueue( // WARNING: isspecsig is protected by the codegen-lock. If that lock is removed, then the isspecsig load needs to be properly atomically sequenced with this. if (cache_valid && invoke != NULL) { auto fptr = jl_atomic_load_relaxed(&codeinst->specptr.fptr); + if (fptr) { + while (!(jl_atomic_load_acquire(&codeinst->specsigflags) & 0b10)) { + jl_cpu_pause(); + } + // in case we are racing with another thread that is emitting this function + invoke = jl_atomic_load_relaxed(&codeinst->invoke); + } if (invoke == jl_fptr_args_addr) { preal_decl = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)fptr, codeinst); } - else if (codeinst->isspecsig) { + else if (jl_atomic_load_relaxed(&codeinst->specsigflags) & 0b1) { preal_decl = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)fptr, codeinst); preal_specsig = true; } diff --git a/src/gf.c b/src/gf.c index 894a8a415e002..42990baf7ad24 100644 --- a/src/gf.c +++ b/src/gf.c @@ -414,13 +414,13 @@ JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst( if ((const_flags & 2) == 0) inferred_const = NULL; codeinst->rettype_const = inferred_const; - jl_atomic_store_relaxed(&codeinst->invoke, NULL); jl_atomic_store_relaxed(&codeinst->specptr.fptr, NULL); + jl_atomic_store_relaxed(&codeinst->invoke, NULL); if ((const_flags & 1) != 0) { assert(const_flags & 2); jl_atomic_store_relaxed(&codeinst->invoke, jl_fptr_const_return); } - codeinst->isspecsig = 0; + jl_atomic_store_relaxed(&codeinst->specsigflags, 0); jl_atomic_store_relaxed(&codeinst->precompile, 0); jl_atomic_store_relaxed(&codeinst->next, NULL); codeinst->ipo_purity_bits = ipo_effects; @@ -2218,12 +2218,33 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t mi, codeinst2->rettype, codeinst2->min_world, codeinst2->max_world); if (jl_atomic_load_relaxed(&codeinst->invoke) == NULL) { - // once set, don't change invoke-ptr, as that leads to race conditions - // with the (not) simultaneous updates to invoke and specptr - codeinst->isspecsig = codeinst2->isspecsig; codeinst->rettype_const = codeinst2->rettype_const; - jl_atomic_store_release(&codeinst->specptr.fptr, jl_atomic_load_relaxed(&codeinst2->specptr.fptr)); - jl_atomic_store_release(&codeinst->invoke, jl_atomic_load_relaxed(&codeinst2->invoke)); + uint8_t specsigflags = jl_atomic_load_acquire(&codeinst2->specsigflags); + jl_callptr_t invoke = jl_atomic_load_acquire(&codeinst2->invoke); + void *fptr = jl_atomic_load_relaxed(&codeinst2->specptr.fptr); + if (fptr != NULL) { + while (!(specsigflags & 0b10)) { + jl_cpu_pause(); + specsigflags = jl_atomic_load_acquire(&codeinst2->specsigflags); + } + invoke = jl_atomic_load_relaxed(&codeinst2->invoke); + void *prev_fptr = NULL; + // see jitlayers.cpp for the ordering restrictions here + if (jl_atomic_cmpswap_acqrel(&codeinst->specptr.fptr, &prev_fptr, fptr)) { + jl_atomic_store_relaxed(&codeinst->specsigflags, specsigflags & 0b1); + jl_atomic_store_release(&codeinst->invoke, invoke); + jl_atomic_store_release(&codeinst->specsigflags, specsigflags); + } else { + // someone else already compiled it + while (!(jl_atomic_load_acquire(&codeinst->specsigflags) & 0b10)) { + jl_cpu_pause(); + } + // codeinst is now set up fully, safe to return + } + } else { + jl_callptr_t prev = NULL; + jl_atomic_cmpswap_acqrel(&codeinst->invoke, &prev, invoke); + } } // don't call record_precompile_statement here, since we already compiled it as mi2 which is better return codeinst; @@ -2248,14 +2269,22 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t jl_method_instance_t *unspecmi = jl_atomic_load_relaxed(&def->unspecialized); if (unspecmi) { jl_code_instance_t *unspec = jl_atomic_load_relaxed(&unspecmi->cache); - if (unspec && jl_atomic_load_acquire(&unspec->invoke)) { + jl_callptr_t unspec_invoke = NULL; + if (unspec && (unspec_invoke = jl_atomic_load_acquire(&unspec->invoke))) { jl_code_instance_t *codeinst = jl_new_codeinst(mi, (jl_value_t*)jl_any_type, NULL, NULL, 0, 1, ~(size_t)0, 0, 0, jl_nothing, 0); - codeinst->isspecsig = 0; - codeinst->specptr = unspec->specptr; + void *unspec_fptr = jl_atomic_load_relaxed(&unspec->specptr.fptr); + if (unspec_fptr) { + // wait until invoke and specsigflags are properly set + while (!(jl_atomic_load_acquire(&unspec->specsigflags) & 0b10)) { + jl_cpu_pause(); + } + unspec_invoke = jl_atomic_load_relaxed(&unspec->invoke); + } + jl_atomic_store_release(&codeinst->specptr.fptr, unspec_fptr); codeinst->rettype_const = unspec->rettype_const; - jl_atomic_store_relaxed(&codeinst->invoke, jl_atomic_load_relaxed(&unspec->invoke)); + jl_atomic_store_release(&codeinst->invoke, unspec_invoke); jl_mi_cache_insert(mi, codeinst); record_precompile_statement(mi); return codeinst; @@ -2272,7 +2301,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t jl_code_instance_t *codeinst = jl_new_codeinst(mi, (jl_value_t*)jl_any_type, NULL, NULL, 0, 1, ~(size_t)0, 0, 0, jl_nothing, 0); - jl_atomic_store_relaxed(&codeinst->invoke, jl_fptr_interpret_call); + jl_atomic_store_release(&codeinst->invoke, jl_fptr_interpret_call); jl_mi_cache_insert(mi, codeinst); record_precompile_statement(mi); return codeinst; @@ -2289,7 +2318,8 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t jl_method_instance_t *unspec = jl_get_unspecialized_from_mi(mi); jl_code_instance_t *ucache = jl_get_method_inferred(unspec, (jl_value_t*)jl_any_type, 1, ~(size_t)0); // ask codegen to make the fptr for unspec - if (jl_atomic_load_acquire(&ucache->invoke) == NULL) { + jl_callptr_t ucache_invoke = jl_atomic_load_acquire(&ucache->invoke); + if (ucache_invoke == NULL) { if (def->source == jl_nothing && (jl_atomic_load_relaxed(&ucache->def->uninferred) == jl_nothing || jl_atomic_load_relaxed(&ucache->def->uninferred) == NULL)) { jl_printf(JL_STDERR, "source not available for "); @@ -2298,19 +2328,29 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t jl_error("source missing for method that needs to be compiled"); } jl_generate_fptr_for_unspecialized(ucache); + ucache_invoke = jl_atomic_load_acquire(&ucache->invoke); } - assert(jl_atomic_load_relaxed(&ucache->invoke) != NULL); - if (jl_atomic_load_relaxed(&ucache->invoke) != jl_fptr_sparam && - jl_atomic_load_relaxed(&ucache->invoke) != jl_fptr_interpret_call) { + assert(ucache_invoke != NULL); + if (ucache_invoke != jl_fptr_sparam && + ucache_invoke != jl_fptr_interpret_call) { // only these care about the exact specTypes, otherwise we can use it directly return ucache; } codeinst = jl_new_codeinst(mi, (jl_value_t*)jl_any_type, NULL, NULL, 0, 1, ~(size_t)0, 0, 0, jl_nothing, 0); - codeinst->isspecsig = 0; - codeinst->specptr = ucache->specptr; + void *unspec_fptr = jl_atomic_load_relaxed(&ucache->specptr.fptr); + if (unspec_fptr) { + // wait until invoke and specsigflags are properly set + while (!(jl_atomic_load_acquire(&ucache->specsigflags) & 0b10)) { + jl_cpu_pause(); + } + ucache_invoke = jl_atomic_load_relaxed(&ucache->invoke); + } + // unspec is always not specsig, but might use specptr + jl_atomic_store_relaxed(&codeinst->specsigflags, jl_atomic_load_relaxed(&ucache->specsigflags) & 0b10); + jl_atomic_store_relaxed(&codeinst->specptr.fptr, unspec_fptr); codeinst->rettype_const = ucache->rettype_const; - jl_atomic_store_relaxed(&codeinst->invoke, jl_atomic_load_relaxed(&ucache->invoke)); + jl_atomic_store_release(&codeinst->invoke, ucache_invoke); jl_mi_cache_insert(mi, codeinst); } else { @@ -2328,11 +2368,8 @@ jl_value_t *jl_fptr_const_return(jl_value_t *f, jl_value_t **args, uint32_t narg jl_value_t *jl_fptr_args(jl_value_t *f, jl_value_t **args, uint32_t nargs, jl_code_instance_t *m) { jl_fptr_args_t invoke = jl_atomic_load_relaxed(&m->specptr.fptr1); - while (1) { - if (invoke) - return invoke(f, args, nargs); - invoke = jl_atomic_load_acquire(&m->specptr.fptr1); // require forward progress with acquire annotation - } + assert(invoke && "Forgot to set specptr for jl_fptr_args!"); + return invoke(f, args, nargs); } jl_value_t *jl_fptr_sparam(jl_value_t *f, jl_value_t **args, uint32_t nargs, jl_code_instance_t *m) @@ -2340,11 +2377,8 @@ jl_value_t *jl_fptr_sparam(jl_value_t *f, jl_value_t **args, uint32_t nargs, jl_ jl_svec_t *sparams = m->def->sparam_vals; assert(sparams != jl_emptysvec); jl_fptr_sparam_t invoke = jl_atomic_load_relaxed(&m->specptr.fptr3); - while (1) { - if (invoke) - return invoke(f, args, nargs, sparams); - invoke = jl_atomic_load_acquire(&m->specptr.fptr3); // require forward progress with acquire annotation - } + assert(invoke && "Forgot to set specptr for jl_fptr_sparam!"); + return invoke(f, args, nargs, sparams); } JL_DLLEXPORT jl_callptr_t jl_fptr_args_addr = &jl_fptr_args; @@ -2667,7 +2701,7 @@ STATIC_INLINE jl_value_t *_jl_invoke(jl_value_t *F, jl_value_t **args, uint32_t jl_code_instance_t *codeinst = jl_atomic_load_relaxed(&mfunc->cache); while (codeinst) { if (codeinst->min_world <= world && world <= codeinst->max_world) { - jl_callptr_t invoke = jl_atomic_load_relaxed(&codeinst->invoke); + jl_callptr_t invoke = jl_atomic_load_acquire(&codeinst->invoke); if (invoke != NULL) { jl_value_t *res = invoke(F, args, nargs, codeinst); return verify_type(res); @@ -2687,7 +2721,7 @@ STATIC_INLINE jl_value_t *_jl_invoke(jl_value_t *F, jl_value_t **args, uint32_t errno = last_errno; if (jl_options.malloc_log) jl_gc_sync_total_bytes(last_alloc); // discard allocation count from compilation - jl_callptr_t invoke = jl_atomic_load_relaxed(&codeinst->invoke); + jl_callptr_t invoke = jl_atomic_load_acquire(&codeinst->invoke); jl_value_t *res = invoke(F, args, nargs, codeinst); return verify_type(res); } diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index b744f9fcbd3f2..b489665f5629d 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -262,18 +262,31 @@ static jl_callptr_t _jl_compile_codeinst( addr = (jl_callptr_t)getAddressForFunction(decls.functionObject); isspecsig = true; } - if (jl_atomic_load_relaxed(&this_code->invoke) == NULL) { - // once set, don't change invoke-ptr, as that leads to race conditions - // with the (not) simultaneous updates to invoke and specptr - if (!decls.specFunctionObject.empty()) { - jl_atomic_store_release(&this_code->specptr.fptr, (void*)getAddressForFunction(decls.specFunctionObject)); - this_code->isspecsig = isspecsig; + if (!decls.specFunctionObject.empty()) { + void *prev_specptr = NULL; + auto spec = (void*)getAddressForFunction(decls.specFunctionObject); + if (jl_atomic_cmpswap_acqrel(&this_code->specptr.fptr, &prev_specptr, spec)) { + // only set specsig and invoke if we were the first to set specptr + jl_atomic_store_relaxed(&this_code->specsigflags, (uint8_t) isspecsig); + // we might overwrite invokeptr here; that's ok, anybody who relied on the identity of invokeptr + // either assumes that specptr was null, doesn't care about specptr, + // or will wait until specsigflags has 0b10 set before reloading invoke + jl_atomic_store_release(&this_code->invoke, addr); + jl_atomic_store_release(&this_code->specsigflags, (uint8_t) (0b10 | isspecsig)); + } else { + //someone else beat us, don't commit any results + while (!(jl_atomic_load_acquire(&this_code->specsigflags) & 0b10)) { + jl_cpu_pause(); + } + addr = jl_atomic_load_relaxed(&this_code->invoke); + } + } else { + jl_callptr_t prev_invoke = NULL; + if (!jl_atomic_cmpswap_acqrel(&this_code->invoke, &prev_invoke, addr)) { + addr = prev_invoke; + //TODO do we want to potentially promote invoke anyways? (e.g. invoke is jl_interpret_call or some other + //known lesser function) } - jl_atomic_store_release(&this_code->invoke, addr); - } - else if (jl_atomic_load_relaxed(&this_code->invoke) == jl_fptr_const_return_addr && !decls.specFunctionObject.empty()) { - // hack to export this pointer value to jl_dump_method_disasm - jl_atomic_store_release(&this_code->specptr.fptr, (void*)getAddressForFunction(decls.specFunctionObject)); } if (this_code == codeinst) fptr = addr; @@ -497,10 +510,9 @@ void jl_generate_fptr_for_unspecialized_impl(jl_code_instance_t *unspec) assert(src && jl_is_code_info(src)); ++UnspecFPtrCount; _jl_compile_codeinst(unspec, src, unspec->min_world, *jl_ExecutionEngine->getContext()); - if (jl_atomic_load_relaxed(&unspec->invoke) == NULL) { - // if we hit a codegen bug (or ran into a broken generated function or llvmcall), fall back to the interpreter as a last resort - jl_atomic_store_release(&unspec->invoke, jl_fptr_interpret_call_addr); - } + jl_callptr_t null = nullptr; + // if we hit a codegen bug (or ran into a broken generated function or llvmcall), fall back to the interpreter as a last resort + jl_atomic_cmpswap(&unspec->invoke, &null, jl_fptr_interpret_call_addr); JL_GC_POP(); } JL_UNLOCK(&jl_codegen_lock); // Might GC @@ -519,7 +531,7 @@ jl_value_t *jl_dump_method_asm_impl(jl_method_instance_t *mi, size_t world, // printing via disassembly jl_code_instance_t *codeinst = jl_generate_fptr(mi, world); if (codeinst) { - uintptr_t fptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->invoke); + uintptr_t fptr = (uintptr_t)jl_atomic_load_acquire(&codeinst->invoke); if (getwrapper) return jl_dump_fptr_asm(fptr, raw_mc, asm_variant, debuginfo, binary); uintptr_t specfptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->specptr.fptr); @@ -547,7 +559,7 @@ jl_value_t *jl_dump_method_asm_impl(jl_method_instance_t *mi, size_t world, if (src && (jl_value_t*)src != jl_nothing) src = jl_uncompress_ir(mi->def.method, codeinst, (jl_array_t*)src); } - fptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->invoke); + fptr = (uintptr_t)jl_atomic_load_acquire(&codeinst->invoke); specfptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->specptr.fptr); if (src && jl_is_code_info(src)) { if (fptr == (uintptr_t)jl_fptr_const_return_addr && specfptr == 0) { diff --git a/src/julia.h b/src/julia.h index 8c3332fe50e12..19dab5cd3a704 100644 --- a/src/julia.h +++ b/src/julia.h @@ -424,8 +424,7 @@ typedef struct _jl_code_instance_t { jl_value_t *argescapes; // escape information of call arguments // compilation state cache - // WARNING: isspecsig is protected by the codegen-lock. If that lock is removed, then the isspecsig load needs to be properly atomically sequenced with the invoke pointers. - uint8_t isspecsig; // if specptr is a specialized function signature for specTypes->rettype + _Atomic(uint8_t) specsigflags; // & 0b1 == specptr is a specialized function signature for specTypes->rettype, &0b10 == invokeptr matches specptr _Atomic(uint8_t) precompile; // if set, this will be added to the output system image uint8_t relocatability; // nonzero if all roots are built into sysimg or tagged by module key _Atomic(jl_callptr_t) invoke; // jlcall entry point diff --git a/src/julia_atomics.h b/src/julia_atomics.h index 4da2e4f7a9994..959491f1ac048 100644 --- a/src/julia_atomics.h +++ b/src/julia_atomics.h @@ -160,6 +160,11 @@ bool jl_atomic_cmpswap_explicit(std::atomic *ptr, T *expected, S val, std::me { return std::atomic_compare_exchange_strong_explicit(ptr, expected, val, order, order); } +template +bool jl_atomic_cmpswap_acqrel(std::atomic *ptr, T *expected, S val) +{ + return std::atomic_compare_exchange_strong_explicit(ptr, expected, val, memory_order_acq_rel, memory_order_acquire); +} #define jl_atomic_cmpswap_relaxed(ptr, expected, val) jl_atomic_cmpswap_explicit(ptr, expected, val, memory_order_relaxed) template T jl_atomic_exchange(std::atomic *ptr, S desired) @@ -191,6 +196,8 @@ extern "C" { atomic_compare_exchange_strong(obj, expected, desired) # define jl_atomic_cmpswap_relaxed(obj, expected, desired) \ atomic_compare_exchange_strong_explicit(obj, expected, desired, memory_order_relaxed, memory_order_relaxed) +#define jl_atomic_cmpswap_acqrel(obj, expected, desired) \ + atomic_compare_exchange_strong_explicit(obj, expected, desired, memory_order_acq_rel, memory_order_acquire) // TODO: Maybe add jl_atomic_cmpswap_weak for spin lock # define jl_atomic_exchange(obj, desired) \ atomic_exchange(obj, desired) @@ -251,6 +258,7 @@ extern "C" { #define jl_atomic_exchange_relaxed jl_atomic_exchange #undef jl_atomic_cmpswap +#undef jl_atomic_cmpswap_acqrel #undef jl_atomic_cmpswap_relaxed #define jl_atomic_cmpswap(obj, expected, desired) \ (__extension__({ \ @@ -264,6 +272,7 @@ extern "C" { *x__analyzer__ = temp__analyzer__; \ eq__analyzer__; \ })) +#define jl_atomic_cmpswap_acqrel jl_atomic_cmpswap #define jl_atomic_cmpswap_relaxed jl_atomic_cmpswap #undef jl_atomic_store diff --git a/src/opaque_closure.c b/src/opaque_closure.c index db596c2bb893f..6772290c8ab89 100644 --- a/src/opaque_closure.c +++ b/src/opaque_closure.c @@ -75,18 +75,23 @@ static jl_opaque_closure_t *new_opaque_closure(jl_tupletype_t *argt, jl_value_t oc->source = source; oc->captures = captures; oc->specptr = NULL; - if (!ci || jl_atomic_load_relaxed(&ci->invoke) == jl_fptr_interpret_call) { + if (!ci) { oc->invoke = (jl_fptr_args_t)jl_interpret_opaque_closure; - } - else if (jl_atomic_load_relaxed(&ci->invoke) == jl_fptr_args) { - oc->invoke = jl_atomic_load_relaxed(&ci->specptr.fptr1); - } - else if (jl_atomic_load_relaxed(&ci->invoke) == jl_fptr_const_return) { - oc->invoke = (jl_fptr_args_t)jl_fptr_const_opaque_closure; - oc->captures = ci->rettype_const; - } - else { - oc->invoke = (jl_fptr_args_t)jl_atomic_load_relaxed(&ci->invoke); + } else { + jl_callptr_t invoke = jl_atomic_load_acquire(&ci->invoke); + if (invoke == jl_fptr_interpret_call) { + oc->invoke = (jl_fptr_args_t)jl_interpret_opaque_closure; + } + else if (invoke == jl_fptr_args) { + oc->invoke = jl_atomic_load_relaxed(&ci->specptr.fptr1); + } + else if (invoke == jl_fptr_const_return) { + oc->invoke = (jl_fptr_args_t)jl_fptr_const_opaque_closure; + oc->captures = ci->rettype_const; + } + else { + oc->invoke = (jl_fptr_args_t) invoke; + } } oc->world = world; return oc; diff --git a/src/staticdata.c b/src/staticdata.c index c4ed2b531ae0f..9ae00b395a0e8 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1376,7 +1376,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED } newm->invoke = NULL; - newm->isspecsig = 0; + newm->specsigflags = 0; newm->specptr.fptr = NULL; int8_t fptr_id = JL_API_NULL; int8_t builtin_id = 0; @@ -1889,7 +1889,7 @@ static void jl_update_all_fptrs(jl_serializer_state *s, jl_image_t *image) void *fptr = (void*)(base + offset); if (specfunc) { codeinst->specptr.fptr = fptr; - codeinst->isspecsig = 1; // TODO: set only if confirmed to be true + codeinst->specsigflags = 0b11; // TODO: set only if confirmed to be true } else { codeinst->invoke = (jl_callptr_t)fptr; @@ -1962,7 +1962,7 @@ static void jl_root_new_gvars(jl_serializer_state *s, jl_image_t *image, uint32_ v = (uintptr_t)jl_as_global_root((jl_value_t*)v); } else { jl_code_instance_t *codeinst = (jl_code_instance_t*) v; - assert(codeinst && codeinst->isspecsig); + assert(codeinst && (codeinst->specsigflags & 0b01)); v = (uintptr_t)codeinst->specptr.fptr; } *gv = v; From e3d366f1966595ba737220df49e220610823b331 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 22 Feb 2023 05:58:37 -0700 Subject: [PATCH 163/775] generators: expose caller world to GeneratedFunctionStub (#48611) Expose the demanded world to the GeneratedFunctionStub caller, for users such as Cassette. If this argument is used, the user must return a CodeInfo with the min/max world field set correctly. Make the internal representation a tiny bit more compact also, removing a little bit of unnecessary metadata. Remove support for returning `body isa CodeInfo` via this wrapper, since it is impossible to return a correct object via the GeneratedFunctionStub since it strips off the world argument, which is required for it to do so. This also removes support for not inferring these fully (expand_early=false). Also answer method lookup queries about the future correctly, by refusing to answer them. This helps keeps execution correct as methods get added to the system asynchronously. This reverts "fix #25678: return matters for generated functions (#40778)" (commit 92c84bf3865403355af463b5a1dee42bf7143592), since this is no longer sensible to return here anyways, so it is no longer permitted or supported by this macro. Fixes various issues where we failed to specify the correct world. --- base/Base.jl | 2 +- base/boot.jl | 27 ++++----- base/compiler/abstractinterpretation.jl | 13 +++-- base/compiler/bootstrap.jl | 2 +- base/compiler/inferencestate.jl | 3 +- base/compiler/optimize.jl | 3 +- base/compiler/typeinfer.jl | 2 +- base/compiler/types.jl | 2 +- base/compiler/utilities.jl | 8 +-- base/compiler/validation.jl | 7 +-- base/expr.jl | 5 +- base/reflection.jl | 38 ++++++++----- doc/src/devdocs/ast.md | 8 +-- src/aotcompile.cpp | 2 +- src/ast.c | 4 +- src/gf.c | 7 ++- src/interpreter.c | 8 ++- src/jitlayers.cpp | 4 +- src/julia-syntax.scm | 9 +-- src/julia.h | 2 +- src/julia_internal.h | 2 +- src/method.c | 47 +++++++--------- test/ambiguous.jl | 6 +- test/compiler/contextual.jl | 33 +++++------ test/compiler/inference.jl | 74 +++++++++++++------------ test/compiler/validation.jl | 9 ++- test/core.jl | 3 +- test/opaque_closure.jl | 31 +++++------ test/reflection.jl | 2 +- test/staged.jl | 40 ++++++++----- test/syntax.jl | 3 - 31 files changed, 203 insertions(+), 203 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 85a9c8d5048e3..58d527f365aa7 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -478,7 +478,7 @@ in_sysimage(pkgid::PkgId) = pkgid in _sysimage_modules for match = _methods(+, (Int, Int), -1, get_world_counter()) m = match.method delete!(push!(Set{Method}(), m), m) - copy(Core.Compiler.retrieve_code_info(Core.Compiler.specialize_method(match))) + 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)) diff --git a/base/boot.jl b/base/boot.jl index b4e01b0c884c1..ca6e6c81405e2 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -590,28 +590,25 @@ println(@nospecialize a...) = println(stdout, a...) struct GeneratedFunctionStub gen - argnames::Array{Any,1} - spnames::Union{Nothing, Array{Any,1}} - line::Int - file::Symbol - expand_early::Bool + argnames::SimpleVector + spnames::SimpleVector end -# invoke and wrap the results of @generated -function (g::GeneratedFunctionStub)(@nospecialize args...) +# invoke and wrap the results of @generated expression +function (g::GeneratedFunctionStub)(world::UInt, source::LineNumberNode, @nospecialize args...) + # args is (spvals..., argtypes...) body = g.gen(args...) - if body isa CodeInfo - return body - end - lam = Expr(:lambda, g.argnames, - Expr(Symbol("scope-block"), + file = source.file + file isa Symbol || (file = :none) + lam = Expr(:lambda, Expr(:argnames, g.argnames...).args, + Expr(:var"scope-block", Expr(:block, - LineNumberNode(g.line, g.file), - Expr(:meta, :push_loc, g.file, Symbol("@generated body")), + source, + Expr(:meta, :push_loc, file, :var"@generated body"), Expr(:return, body), Expr(:meta, :pop_loc)))) spnames = g.spnames - if spnames === nothing + if spnames === svec() return lam else return Expr(Symbol("with-static-parameters"), lam, spnames...) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 0d0280e40e817..5fac62eb7578d 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -569,7 +569,7 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp break end topmost === nothing || continue - if edge_matches_sv(infstate, method, sig, sparams, hardlimit, sv) + if edge_matches_sv(interp, infstate, method, sig, sparams, hardlimit, sv) topmost = infstate edgecycle = true end @@ -677,12 +677,13 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp return MethodCallResult(rt, edgecycle, edgelimited, edge, effects) end -function edge_matches_sv(frame::InferenceState, method::Method, @nospecialize(sig), sparams::SimpleVector, hardlimit::Bool, sv::InferenceState) +function edge_matches_sv(interp::AbstractInterpreter, frame::InferenceState, method::Method, @nospecialize(sig), sparams::SimpleVector, hardlimit::Bool, sv::InferenceState) # The `method_for_inference_heuristics` will expand the given method's generator if # necessary in order to retrieve this field from the generated `CodeInfo`, if it exists. # The other `CodeInfo`s we inspect will already have this field inflated, so we just # access it directly instead (to avoid regeneration). - callee_method2 = method_for_inference_heuristics(method, sig, sparams) # Union{Method, Nothing} + world = get_world_counter(interp) + callee_method2 = method_for_inference_heuristics(method, sig, sparams, world) # Union{Method, Nothing} inf_method2 = frame.src.method_for_inference_limit_heuristics # limit only if user token match inf_method2 isa Method || (inf_method2 = nothing) @@ -719,11 +720,11 @@ function edge_matches_sv(frame::InferenceState, method::Method, @nospecialize(si end # This function is used for computing alternate limit heuristics -function method_for_inference_heuristics(method::Method, @nospecialize(sig), sparams::SimpleVector) - if isdefined(method, :generator) && method.generator.expand_early && may_invoke_generator(method, sig, sparams) +function method_for_inference_heuristics(method::Method, @nospecialize(sig), sparams::SimpleVector, world::UInt) + if isdefined(method, :generator) && !(method.generator isa Core.GeneratedFunctionStub) && may_invoke_generator(method, sig, sparams) method_instance = specialize_method(method, sig, sparams) if isa(method_instance, MethodInstance) - cinfo = get_staged(method_instance) + cinfo = get_staged(method_instance, world) if isa(cinfo, CodeInfo) method2 = cinfo.method_for_inference_limit_heuristics if method2 isa Method diff --git a/base/compiler/bootstrap.jl b/base/compiler/bootstrap.jl index 77b36cb9c7f71..1f62d21c9d2d9 100644 --- a/base/compiler/bootstrap.jl +++ b/base/compiler/bootstrap.jl @@ -36,7 +36,7 @@ let interp = NativeInterpreter() else tt = Tuple{typeof(f), Vararg{Any}} end - for m in _methods_by_ftype(tt, 10, typemax(UInt))::Vector + for m in _methods_by_ftype(tt, 10, get_world_counter())::Vector # remove any TypeVars from the intersection m = m::MethodMatch typ = Any[m.spec_types.parameters...] diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 7ff740a23a540..959d2d157f219 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -363,7 +363,8 @@ end function InferenceState(result::InferenceResult, cache::Symbol, interp::AbstractInterpreter) # prepare an InferenceState object for inferring lambda - src = retrieve_code_info(result.linfo) + world = get_world_counter(interp) + src = retrieve_code_info(result.linfo, world) src === nothing && return nothing validate_code_in_debug_mode(result.linfo, src, "lowered") return InferenceState(result, src, cache, interp) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index dc321be5108cf..edf678e919b61 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -183,7 +183,8 @@ function OptimizationState(linfo::MethodInstance, src::CodeInfo, params::Optimiz return OptimizationState(linfo, src, nothing, stmt_info, mod, sptypes, slottypes, inlining, nothing, false) end function OptimizationState(linfo::MethodInstance, params::OptimizationParams, interp::AbstractInterpreter) - src = retrieve_code_info(linfo) + world = get_world_counter(interp) + src = retrieve_code_info(linfo, world) src === nothing && return nothing return OptimizationState(linfo, src, params, interp) end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index f7723a968da3e..734c357201d25 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -1035,7 +1035,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) end end if ccall(:jl_get_module_infer, Cint, (Any,), method.module) == 0 && !generating_sysimg() - return retrieve_code_info(mi) + return retrieve_code_info(mi, get_world_counter(interp)) end lock_mi_inference(interp, mi) result = InferenceResult(mi, typeinf_lattice(interp)) diff --git a/base/compiler/types.jl b/base/compiler/types.jl index cac15e9a69513..7bdbdbd8bdf01 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -330,7 +330,7 @@ struct NativeInterpreter <: AbstractInterpreter cache = Vector{InferenceResult}() # Initially empty cache # Sometimes the caller is lazy and passes typemax(UInt). - # we cap it to the current world age + # we cap it to the current world age for correctness if world == typemax(UInt) world = get_world_counter() end diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 6cf600560902d..0acbc926ae671 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -114,23 +114,23 @@ end invoke_api(li::CodeInstance) = ccall(:jl_invoke_api, Cint, (Any,), li) use_const_api(li::CodeInstance) = invoke_api(li) == 2 -function get_staged(mi::MethodInstance) +function get_staged(mi::MethodInstance, world::UInt) may_invoke_generator(mi) || return nothing try # user code might throw errors – ignore them - ci = ccall(:jl_code_for_staged, Any, (Any,), mi)::CodeInfo + ci = ccall(:jl_code_for_staged, Any, (Any, UInt), mi, world)::CodeInfo return ci catch return nothing end end -function retrieve_code_info(linfo::MethodInstance) +function retrieve_code_info(linfo::MethodInstance, world::UInt) m = linfo.def::Method c = nothing if isdefined(m, :generator) # user code might throw errors – ignore them - c = get_staged(linfo) + c = get_staged(linfo, world) end if c === nothing && isdefined(m, :source) src = m.source diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl index 22a21a4559985..68eb2ab15c59d 100644 --- a/base/compiler/validation.jl +++ b/base/compiler/validation.jl @@ -200,15 +200,14 @@ end """ validate_code!(errors::Vector{InvalidCodeError}, mi::MethodInstance, - c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi)) + c::Union{Nothing,CodeInfo}) Validate `mi`, logging any violation by pushing an `InvalidCodeError` into `errors`. If `isa(c, CodeInfo)`, also call `validate_code!(errors, c)`. It is assumed that `c` is -the `CodeInfo` instance associated with `mi`. +a `CodeInfo` instance associated with `mi`. """ -function validate_code!(errors::Vector{InvalidCodeError}, mi::Core.MethodInstance, - c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi)) +function validate_code!(errors::Vector{InvalidCodeError}, mi::Core.MethodInstance, c::Union{Nothing,CodeInfo}) is_top_level = mi.def isa Module if is_top_level mnargs = 0 diff --git a/base/expr.jl b/base/expr.jl index 46e89bf64da8a..5649303b41ef4 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -962,10 +962,7 @@ macro generated(f) Expr(:block, lno, Expr(:if, Expr(:generated), - # https://github.com/JuliaLang/julia/issues/25678 - Expr(:block, - :(local $tmp = $body), - :(if $tmp isa $(GlobalRef(Core, :CodeInfo)); return $tmp; else $tmp; end)), + body, Expr(:block, Expr(:meta, :generated_only), Expr(:return, nothing)))))) diff --git a/base/reflection.jl b/base/reflection.jl index 9e2615a16a190..bb7dfc0f0cf00 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -961,10 +961,11 @@ function code_lowered(@nospecialize(f), @nospecialize(t=Tuple); generated::Bool= if debuginfo !== :source && debuginfo !== :none throw(ArgumentError("'debuginfo' must be either :source or :none")) end - return map(method_instances(f, t)) do m + world = get_world_counter() + return map(method_instances(f, t, world)) do m if generated && hasgenerator(m) if may_invoke_generator(m) - return ccall(:jl_code_for_staged, Any, (Any,), m)::CodeInfo + return ccall(:jl_code_for_staged, Any, (Any, UInt), m, world)::CodeInfo else error("Could not expand generator for `@generated` method ", m, ". ", "This can happen if the provided argument types (", t, ") are ", @@ -1053,6 +1054,8 @@ methods(@nospecialize(f), @nospecialize(t), mod::Module) = methods(f, t, (mod,)) function methods_including_ambiguous(@nospecialize(f), @nospecialize(t)) tt = signature_type(f, t) world = get_world_counter() + (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && + error("code reflection cannot be used from generated functions") min = RefValue{UInt}(typemin(UInt)) max = RefValue{UInt}(typemax(UInt)) ms = _methods_by_ftype(tt, nothing, -1, world, true, min, max, Ptr{Int32}(C_NULL))::Vector @@ -1125,9 +1128,11 @@ _uncompressed_ir(ci::Core.CodeInstance, s::Array{UInt8,1}) = ccall(:jl_uncompres const uncompressed_ast = uncompressed_ir const _uncompressed_ast = _uncompressed_ir -function method_instances(@nospecialize(f), @nospecialize(t), world::UInt=get_world_counter()) +function method_instances(@nospecialize(f), @nospecialize(t), world::UInt) tt = signature_type(f, t) results = Core.MethodInstance[] + # this make a better error message than the typeassert that follows + world == typemax(UInt) && error("code reflection cannot be used from generated functions") for match in _methods_by_ftype(tt, -1, world)::Vector instance = Core.Compiler.specialize_method(match) push!(results, instance) @@ -1198,20 +1203,22 @@ function may_invoke_generator(method::Method, @nospecialize(atype), sparams::Sim # generator only has one method generator = method.generator isa(generator, Core.GeneratedFunctionStub) || return false - gen_mthds = methods(generator.gen)::MethodList - length(gen_mthds) == 1 || return false + gen_mthds = _methods_by_ftype(Tuple{typeof(generator.gen), Vararg{Any}}, 1, method.primary_world) + (gen_mthds isa Vector && length(gen_mthds) == 1) || return false - generator_method = first(gen_mthds) + generator_method = first(gen_mthds).method nsparams = length(sparams) isdefined(generator_method, :source) || return false code = generator_method.source nslots = ccall(:jl_ir_nslots, Int, (Any,), code) - at = unwrap_unionall(atype)::DataType + at = unwrap_unionall(atype) + at isa DataType || return false (nslots >= 1 + length(sparams) + length(at.parameters)) || return false + firstarg = 1 for i = 1:nsparams if isa(sparams[i], TypeVar) - if (ast_slotflag(code, 1 + i) & SLOT_USED) != 0 + if (ast_slotflag(code, firstarg + i) & SLOT_USED) != 0 return false end end @@ -1220,7 +1227,7 @@ function may_invoke_generator(method::Method, @nospecialize(atype), sparams::Sim non_va_args = method.isva ? nargs - 1 : nargs for i = 1:non_va_args if !isdispatchelem(at.parameters[i]) - if (ast_slotflag(code, 1 + i + nsparams) & SLOT_USED) != 0 + if (ast_slotflag(code, firstarg + i + nsparams) & SLOT_USED) != 0 return false end end @@ -1228,7 +1235,7 @@ function may_invoke_generator(method::Method, @nospecialize(atype), sparams::Sim if method.isva # If the va argument is used, we need to ensure that all arguments that # contribute to the va tuple are dispatchelemes - if (ast_slotflag(code, 1 + nargs + nsparams) & SLOT_USED) != 0 + if (ast_slotflag(code, firstarg + nargs + nsparams) & SLOT_USED) != 0 for i = (non_va_args+1):length(at.parameters) if !isdispatchelem(at.parameters[i]) return false @@ -1318,7 +1325,8 @@ function code_typed_by_type(@nospecialize(tt::Type); debuginfo::Symbol=:default, world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) - ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") + (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && + error("code reflection cannot be used from generated functions") if @isdefined(IRShow) debuginfo = IRShow.debuginfo(debuginfo) elseif debuginfo === :default @@ -1427,7 +1435,7 @@ function code_ircode_by_type( interp = Core.Compiler.NativeInterpreter(world), optimize_until::Union{Integer,AbstractString,Nothing} = nothing, ) - ccall(:jl_is_in_pure_context, Bool, ()) && + (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && error("code reflection cannot be used from generated functions") tt = to_tuple_type(tt) matches = _methods_by_ftype(tt, -1, world)::Vector @@ -1454,7 +1462,8 @@ end function return_types(@nospecialize(f), @nospecialize(types=default_tt(f)); world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) - ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") + (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && + error("code reflection cannot be used from generated functions") if isa(f, Core.OpaqueClosure) _, rt = only(code_typed_opaque_closure(f)) return Any[rt] @@ -1478,7 +1487,8 @@ end function infer_effects(@nospecialize(f), @nospecialize(types=default_tt(f)); world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) - ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") + (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && + error("code reflection cannot be used from generated functions") if isa(f, Core.Builtin) types = to_tuple_type(types) argtypes = Any[Core.Compiler.Const(f), types.parameters...] diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index df6a2224c2a48..76b0cc97df5bf 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -685,10 +685,10 @@ A (usually temporary) container for holding lowered source code. A `UInt8` array of slot properties, represented as bit flags: - * 2 - assigned (only false if there are *no* assignment statements with this var on the left) - * 8 - const (currently unused for local variables) - * 16 - statically assigned once - * 32 - might be used before assigned. This flag is only valid after type inference. + * 0x02 - assigned (only false if there are *no* assignment statements with this var on the left) + * 0x08 - used (if there is any read or write of the slot) + * 0x10 - statically assigned once + * 0x20 - might be used before assigned. This flag is only valid after type inference. * `ssavaluetypes` diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index d1694eaf9e0d5..866d9dabe5100 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1088,7 +1088,7 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, siz if (src) jlrettype = src->rettype; else if (jl_is_method(mi->def.method)) { - src = mi->def.method->generator ? jl_code_for_staged(mi) : (jl_code_info_t*)mi->def.method->source; + src = mi->def.method->generator ? jl_code_for_staged(mi, world) : (jl_code_info_t*)mi->def.method->source; if (src && !jl_is_code_info(src) && jl_is_method(mi->def.method)) src = jl_uncompress_ir(mi->def.method, NULL, (jl_array_t*)src); } diff --git a/src/ast.c b/src/ast.c index cb03b7efb6eb7..3f3d6176d342e 100644 --- a/src/ast.c +++ b/src/ast.c @@ -1024,10 +1024,10 @@ static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule jl_value_t *result; JL_TRY { margs[0] = jl_toplevel_eval(*ctx, margs[0]); - jl_method_instance_t *mfunc = jl_method_lookup(margs, nargs, world); + jl_method_instance_t *mfunc = jl_method_lookup(margs, nargs, ct->world_age); JL_GC_PROMISE_ROOTED(mfunc); if (mfunc == NULL) { - jl_method_error(margs[0], &margs[1], nargs, world); + jl_method_error(margs[0], &margs[1], nargs, ct->world_age); // unreachable } *ctx = mfunc->def.method->module; diff --git a/src/gf.c b/src/gf.c index 42990baf7ad24..d8c5e571e820f 100644 --- a/src/gf.c +++ b/src/gf.c @@ -27,6 +27,9 @@ extern "C" { JL_DLLEXPORT _Atomic(size_t) jl_world_counter = 1; // uses atomic acquire/release JL_DLLEXPORT size_t jl_get_world_counter(void) JL_NOTSAFEPOINT { + jl_task_t *ct = jl_current_task; + if (ct->ptls->in_pure_callback) + return ~(size_t)0; return jl_atomic_load_acquire(&jl_world_counter); } @@ -2296,7 +2299,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t // if that didn't work and compilation is off, try running in the interpreter if (compile_option == JL_OPTIONS_COMPILE_OFF || compile_option == JL_OPTIONS_COMPILE_MIN) { - jl_code_info_t *src = jl_code_for_interpreter(mi); + jl_code_info_t *src = jl_code_for_interpreter(mi, world); if (!jl_code_requires_compiler(src, 0)) { jl_code_instance_t *codeinst = jl_new_codeinst(mi, (jl_value_t*)jl_any_type, NULL, NULL, @@ -3139,6 +3142,8 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, int intersections, size_t world, int cache_result, size_t *min_valid, size_t *max_valid, int *ambig) { + if (world > jl_atomic_load_acquire(&jl_world_counter)) + return jl_nothing; // the future is not enumerable int has_ambiguity = 0; jl_value_t *unw = jl_unwrap_unionall((jl_value_t*)type); assert(jl_is_datatype(unw)); diff --git a/src/interpreter.c b/src/interpreter.c index 08cb87791c5a3..713887f234898 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -626,7 +626,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, // preparing method IR for interpreter -jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *mi) +jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *mi, size_t world) { jl_code_info_t *src = (jl_code_info_t*)jl_atomic_load_relaxed(&mi->uninferred); if (jl_is_method(mi->def.value)) { @@ -636,7 +636,7 @@ jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *mi) } else { assert(mi->def.method->generator); - src = jl_code_for_staged(mi); + src = jl_code_for_staged(mi, world); } } if (src && (jl_value_t*)src != jl_nothing) { @@ -659,7 +659,9 @@ jl_value_t *NOINLINE jl_fptr_interpret_call(jl_value_t *f, jl_value_t **args, ui { interpreter_state *s; jl_method_instance_t *mi = codeinst->def; - jl_code_info_t *src = jl_code_for_interpreter(mi); + jl_task_t *ct = jl_current_task; + size_t world = ct->world_age; + jl_code_info_t *src = jl_code_for_interpreter(mi, world); jl_array_t *stmts = src->code; assert(jl_typeis(stmts, jl_array_any_type)); unsigned nroots = jl_source_nslots(src) + jl_source_nssavalues(src) + 2; diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index b489665f5629d..940613fd596f4 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -499,7 +499,7 @@ void jl_generate_fptr_for_unspecialized_impl(jl_code_instance_t *unspec) // TODO: this is wrong assert(def->generator); // TODO: jl_code_for_staged can throw - src = jl_code_for_staged(unspec->def); + src = jl_code_for_staged(unspec->def, unspec->min_world); } if (src && (jl_value_t*)src != jl_nothing) src = jl_uncompress_ir(def, NULL, (jl_array_t*)src); @@ -554,7 +554,7 @@ jl_value_t *jl_dump_method_asm_impl(jl_method_instance_t *mi, size_t world, if (jl_is_method(def)) { if (!src) { // TODO: jl_code_for_staged can throw - src = def->generator ? jl_code_for_staged(mi) : (jl_code_info_t*)def->source; + src = def->generator ? jl_code_for_staged(mi, world) : (jl_code_info_t*)def->source; } if (src && (jl_value_t*)src != jl_nothing) src = jl_uncompress_ir(mi->def.method, codeinst, (jl_array_t*)src); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 4a0407e019432..2a4e95ab1da86 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -382,13 +382,8 @@ `((meta generated (new (core GeneratedFunctionStub) ,gname - ,(cons 'list anames) - ,(if (null? sparams) - 'nothing - (cons 'list (map car sparams))) - ,(cadr loc) - (inert ,(caddr loc)) - (false)))))) + (call (core svec) ,@(map quotify anames)) + (call (core svec) ,@(map quotify names))))))) (list gf)) '())) (types (llist-types argl)) diff --git a/src/julia.h b/src/julia.h index 19dab5cd3a704..083e4ef2eab02 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1495,7 +1495,7 @@ JL_DLLEXPORT jl_value_t *jl_generic_function_def(jl_sym_t *name, _Atomic(jl_value_t*) *bp, jl_binding_t *bnd); JL_DLLEXPORT jl_method_t *jl_method_def(jl_svec_t *argdata, jl_methtable_t *mt, jl_code_info_t *f, jl_module_t *module); -JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo); +JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo, size_t world); JL_DLLEXPORT jl_code_info_t *jl_copy_code_info(jl_code_info_t *src); JL_DLLEXPORT size_t jl_get_world_counter(void) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_box_bool(int8_t x) JL_NOTSAFEPOINT; diff --git a/src/julia_internal.h b/src/julia_internal.h index b77de64732116..492c0ec8a8984 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -621,7 +621,7 @@ jl_method_instance_t *jl_get_unspecialized(jl_method_t *def JL_PROPAGATES_ROOT); JL_DLLEXPORT void jl_compile_method_instance(jl_method_instance_t *mi, jl_tupletype_t *types, size_t world); JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types); -jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *lam JL_PROPAGATES_ROOT); +jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *lam JL_PROPAGATES_ROOT, size_t world); int jl_code_requires_compiler(jl_code_info_t *src, int include_force_compile); jl_code_info_t *jl_new_code_info_from_ir(jl_expr_t *ast); JL_DLLEXPORT jl_code_info_t *jl_new_code_info_uninit(void); diff --git a/src/method.c b/src/method.c index d5f56a5921358..64bec989251a8 100644 --- a/src/method.c +++ b/src/method.c @@ -517,21 +517,21 @@ void jl_add_function_name_to_lineinfo(jl_code_info_t *ci, jl_value_t *name) } // invoke (compiling if necessary) the jlcall function pointer for a method template -STATIC_INLINE jl_value_t *jl_call_staged(jl_method_t *def, jl_value_t *generator, jl_svec_t *sparam_vals, - jl_value_t **args, uint32_t nargs) +static jl_value_t *jl_call_staged(jl_method_t *def, jl_value_t *generator, + size_t world, jl_svec_t *sparam_vals, jl_value_t **args, uint32_t nargs) { size_t n_sparams = jl_svec_len(sparam_vals); jl_value_t **gargs; - size_t totargs = 1 + n_sparams + nargs + def->isva; + size_t totargs = 2 + n_sparams + def->nargs; JL_GC_PUSHARGS(gargs, totargs); - gargs[0] = generator; - memcpy(&gargs[1], jl_svec_data(sparam_vals), n_sparams * sizeof(void*)); - memcpy(&gargs[1 + n_sparams], args, nargs * sizeof(void*)); - if (def->isva) { - gargs[totargs-1] = jl_f_tuple(NULL, &gargs[1 + n_sparams + def->nargs - 1], nargs - (def->nargs - 1)); - gargs[1 + n_sparams + def->nargs - 1] = gargs[totargs - 1]; - } - jl_value_t *code = jl_apply(gargs, 1 + n_sparams + def->nargs); + gargs[0] = jl_box_ulong(world); + gargs[1] = jl_box_long(def->line); + gargs[1] = jl_new_struct(jl_linenumbernode_type, gargs[1], def->file); + memcpy(&gargs[2], jl_svec_data(sparam_vals), n_sparams * sizeof(void*)); + memcpy(&gargs[2 + n_sparams], args, (def->nargs - def->isva) * sizeof(void*)); + if (def->isva) + gargs[totargs - 1] = jl_f_tuple(NULL, &args[def->nargs - 1], nargs - def->nargs + 1); + jl_value_t *code = jl_apply_generic(generator, gargs, totargs); JL_GC_POP(); return code; } @@ -555,7 +555,7 @@ JL_DLLEXPORT jl_code_info_t *jl_expand_and_resolve(jl_value_t *ex, jl_module_t * // Return a newly allocated CodeInfo for the function signature // effectively described by the tuple (specTypes, env, Method) inside linfo -JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo) +JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo, size_t world) { jl_value_t *uninferred = jl_atomic_load_relaxed(&linfo->uninferred); if (uninferred) { @@ -579,13 +579,13 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo) JL_TRY { ct->ptls->in_pure_callback = 1; - // and the right world ct->world_age = def->primary_world; // invoke code generator jl_tupletype_t *ttdt = (jl_tupletype_t*)jl_unwrap_unionall(tt); - ex = jl_call_staged(def, generator, linfo->sparam_vals, jl_svec_data(ttdt->parameters), jl_nparams(ttdt)); + ex = jl_call_staged(def, generator, world, linfo->sparam_vals, jl_svec_data(ttdt->parameters), jl_nparams(ttdt)); + // do some post-processing if (jl_is_code_info(ex)) { func = (jl_code_info_t*)ex; jl_array_t *stmts = (jl_array_t*)func->code; @@ -602,7 +602,6 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo) jl_error("The function body AST defined by this @generated function is not pure. This likely means it contains a closure, a comprehension or a generator."); } } - jl_add_function_name_to_lineinfo(func, (jl_value_t*)def->name); // If this generated function has an opaque closure, cache it for @@ -742,20 +741,12 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) st = jl_nothing; } else if (nargs == 2 && jl_exprarg(st, 0) == (jl_value_t*)jl_generated_sym) { - m->generator = NULL; + if (m->generator != NULL) + jl_error("duplicate @generated function body"); jl_value_t *gexpr = jl_exprarg(st, 1); - if (jl_expr_nargs(gexpr) == 7) { - // expects (new (core GeneratedFunctionStub) funcname argnames sp line file expandearly) - jl_value_t *funcname = jl_exprarg(gexpr, 1); - assert(jl_is_symbol(funcname)); - if (jl_get_global(m->module, (jl_sym_t*)funcname) != NULL) { - m->generator = jl_toplevel_eval(m->module, gexpr); - jl_gc_wb(m, m->generator); - } - } - if (m->generator == NULL) { - jl_error("invalid @generated function; try placing it in global scope"); - } + // the frontend would put (new (core GeneratedFunctionStub) funcname argnames sp) here, for example + m->generator = jl_toplevel_eval(m->module, gexpr); + jl_gc_wb(m, m->generator); st = jl_nothing; } else if (nargs == 1 && jl_exprarg(st, 0) == (jl_value_t*)jl_generated_only_sym) { diff --git a/test/ambiguous.jl b/test/ambiguous.jl index e96954299b702..4ab779d76e6f0 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -357,7 +357,7 @@ f35983(::Type, ::Type) = 2 @test length(Base.methods(f35983, (Any, Any))) == 2 @test first(Base.methods(f35983, (Any, Any))).sig == Tuple{typeof(f35983), Type, Type} let ambig = Ref{Int32}(0) - ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, typemax(UInt), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, Base.get_world_counter(), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) @test ms isa Vector @test length(ms) == 1 @test ambig[] == 0 @@ -366,7 +366,7 @@ f35983(::Type{Int16}, ::Any) = 3 @test length(Base.methods_including_ambiguous(f35983, (Type, Type))) == 2 @test length(Base.methods(f35983, (Type, Type))) == 1 let ambig = Ref{Int32}(0) - ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, typemax(UInt), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, Base.get_world_counter(), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) @test ms isa Vector @test length(ms) == 2 @test ambig[] == 1 @@ -374,7 +374,7 @@ end struct B38280 <: Real; val; end let ambig = Ref{Int32}(0) - ms = Base._methods_by_ftype(Tuple{Type{B38280}, Any}, nothing, 1, typemax(UInt), false, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + ms = Base._methods_by_ftype(Tuple{Type{B38280}, Any}, nothing, 1, Base.get_world_counter(), false, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) @test ms isa Vector @test length(ms) == 1 @test ambig[] == 1 diff --git a/test/compiler/contextual.jl b/test/compiler/contextual.jl index 740e985e388df..9db7ae1aeaa5d 100644 --- a/test/compiler/contextual.jl +++ b/test/compiler/contextual.jl @@ -7,7 +7,7 @@ module MiniCassette # A minimal demonstration of the cassette mechanism. Doesn't support all the # fancy features, but sufficient to exercise this code path in the compiler. - using Core.Compiler: method_instances, retrieve_code_info, CodeInfo, + using Core.Compiler: retrieve_code_info, CodeInfo, MethodInstance, SSAValue, GotoNode, GotoIfNot, ReturnNode, SlotNumber, quoted, signature_type using Base: _methods_by_ftype @@ -69,24 +69,28 @@ module MiniCassette end end - function overdub_generator(self, c, f, args) + function overdub_generator(world::UInt, source, self, c, f, args) + @nospecialize if !Base.issingletontype(f) - return :(return f(args...)) + # (c, f, args..) -> f(args...) + code_info = :(return f(args...)) + return Core.GeneratedFunctionStub(identity, Core.svec(:overdub, :c, :f, :args), Core.svec())(world, source, code_info) end tt = Tuple{f, args...} - match = Base._which(tt; world=typemax(UInt)) + match = Base._which(tt; world) mi = Core.Compiler.specialize_method(match) # Unsupported in this mini-cassette @assert !mi.def.isva - code_info = retrieve_code_info(mi) + code_info = retrieve_code_info(mi, world) @assert isa(code_info, CodeInfo) code_info = copy(code_info) - if isdefined(code_info, :edges) - code_info.edges = MethodInstance[mi] - end + @assert code_info.edges === nothing + code_info.edges = MethodInstance[mi] transform!(code_info, length(args), match.sparams) - code_info + # TODO: this is mandatory: code_info.min_world = max(code_info.min_world, min_world[]) + # TODO: this is mandatory: code_info.max_world = min(code_info.max_world, max_world[]) + return code_info end @inline function overdub(c::Ctx, f::Union{Core.Builtin, Core.IntrinsicFunction}, args...) @@ -95,16 +99,7 @@ module MiniCassette @eval function overdub(c::Ctx, f, args...) $(Expr(:meta, :generated_only)) - $(Expr(:meta, - :generated, - Expr(:new, - Core.GeneratedFunctionStub, - :overdub_generator, - Any[:overdub, :ctx, :f, :args], - Any[], - @__LINE__, - QuoteNode(Symbol(@__FILE__)), - true))) + $(Expr(:meta, :generated, overdub_generator)) end end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 182776d79d7ec..5209ef879324e 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1787,9 +1787,17 @@ bar_22708(x) = f_22708(x) @test bar_22708(1) == "x" +struct EarlyGeneratedFunctionStub + stub::Core.GeneratedFunctionStub +end +(stub::EarlyGeneratedFunctionStub)(args...) = (@nospecialize; stub.stub(args...)) + # mechanism for spoofing work-limiting heuristics and early generator expansion (#24852) -function _generated_stub(gen::Symbol, args::Vector{Any}, params::Vector{Any}, line, file, expand_early) - stub = Expr(:new, Core.GeneratedFunctionStub, gen, args, params, line, file, expand_early) +function _generated_stub(gen::Symbol, args::Core.SimpleVector, params::Core.SimpleVector, expand_early::Bool) + stub = Expr(:new, Core.GeneratedFunctionStub, gen, args, params) + if expand_early + stub = Expr(:new, EarlyGeneratedFunctionStub, stub) + end return Expr(:meta, :generated, stub) end @@ -1798,10 +1806,21 @@ f24852_kernel2(x, y::Tuple) = f24852_kernel1(x, (y,)) f24852_kernel3(x, y::Tuple) = f24852_kernel2(x, (y,)) f24852_kernel(x, y::Number) = f24852_kernel3(x, (y,)) -function f24852_kernel_cinfo(fsig::Type) - world = typemax(UInt) # FIXME - match = Base._methods_by_ftype(fsig, -1, world)[1] - isdefined(match.method, :source) || return (nothing, :(f(x, y))) +function f24852_kernel_cinfo(world::UInt, source, fsig::Type) + matches = Base._methods_by_ftype(fsig, -1, world) + if matches === nothing || length(matches) != 1 + match = nothing + else + match = matches[1] + if !isdefined(match.method, :source) + match = nothing + end + end + if match === nothing + code_info = :(f(x, y)) + code_info = Core.GeneratedFunctionStub(identity, Core.svec(:self, :f, :x, :y), Core.svec(:X, :Y))(world, source, code_info) + return (nothing, code_info) + end code_info = Base.uncompressed_ir(match.method) Meta.partially_inline!(code_info.code, Any[], match.spec_types, Any[match.sparams...], 1, 0, :propagate) if startswith(String(match.method.name), "f24852") @@ -1816,21 +1835,23 @@ function f24852_kernel_cinfo(fsig::Type) end pushfirst!(code_info.slotnames, Symbol("#self#")) pushfirst!(code_info.slotflags, 0x00) + # TODO: this is mandatory: code_info.min_world = max(code_info.min_world, min_world[]) + # TODO: this is mandatory: code_info.max_world = min(code_info.max_world, max_world[]) return match.method, code_info end -function f24852_gen_cinfo_uninflated(X, Y, _, f, x, y) - _, code_info = f24852_kernel_cinfo(Tuple{f, x, y}) +function f24852_gen_cinfo_uninflated(world::UInt, source, X, Y, _, f, x, y) + _, code_info = f24852_kernel_cinfo(world, source, Tuple{f, x, y}) return code_info end -function f24852_gen_cinfo_inflated(X, Y, _, f, x, y) - method, code_info = f24852_kernel_cinfo(Tuple{f, x, y}) +function f24852_gen_cinfo_inflated(world::UInt, source, X, Y, _, f, x, y) + method, code_info = f24852_kernel_cinfo(world, source, Tuple{f, x, y}) code_info.method_for_inference_limit_heuristics = method return code_info end -function f24852_gen_expr(X, Y, _, f, x, y) # deparse f(x::X, y::Y) where {X, Y} +function f24852_gen_expr(X, Y, _, f, x, y) # deparse of f(x::X, y::Y) where {X, Y} if f === typeof(f24852_kernel) f2 = :f24852_kernel3 elseif f === typeof(f24852_kernel3) @@ -1847,20 +1868,8 @@ end @eval begin function f24852_late_expr(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_expr, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), false)) - $(Expr(:meta, :generated_only)) - #= no body =# - end - function f24852_late_inflated(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_cinfo_inflated, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), false)) - $(Expr(:meta, :generated_only)) - #= no body =# - end - function f24852_late_uninflated(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_cinfo_uninflated, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), false)) + $(_generated_stub(:f24852_gen_expr, Core.svec(:self, :f, :x, :y), + Core.svec(:X, :Y), false)) $(Expr(:meta, :generated_only)) #= no body =# end @@ -1868,20 +1877,18 @@ end @eval begin function f24852_early_expr(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_expr, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), true)) + $(_generated_stub(:f24852_gen_expr, Core.svec(:self, :f, :x, :y), + Core.svec(:X, :Y), true)) $(Expr(:meta, :generated_only)) #= no body =# end function f24852_early_inflated(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_cinfo_inflated, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), true)) + $(Expr(:meta, :generated, f24852_gen_cinfo_inflated)) $(Expr(:meta, :generated_only)) #= no body =# end function f24852_early_uninflated(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_cinfo_uninflated, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), true)) + $(Expr(:meta, :generated, f24852_gen_cinfo_uninflated)) $(Expr(:meta, :generated_only)) #= no body =# end @@ -1892,10 +1899,6 @@ result = f24852_kernel(x, y) @test result === f24852_late_expr(f24852_kernel, x, y) @test Base.return_types(f24852_late_expr, typeof((f24852_kernel, x, y))) == Any[Any] -@test result === f24852_late_uninflated(f24852_kernel, x, y) -@test Base.return_types(f24852_late_uninflated, typeof((f24852_kernel, x, y))) == Any[Any] -@test result === f24852_late_uninflated(f24852_kernel, x, y) -@test Base.return_types(f24852_late_uninflated, typeof((f24852_kernel, x, y))) == Any[Any] @test result === f24852_early_expr(f24852_kernel, x, y) @test Base.return_types(f24852_early_expr, typeof((f24852_kernel, x, y))) == Any[Any] @@ -1903,7 +1906,6 @@ result = f24852_kernel(x, y) @test Base.return_types(f24852_early_uninflated, typeof((f24852_kernel, x, y))) == Any[Any] @test result === @inferred f24852_early_inflated(f24852_kernel, x, y) @test Base.return_types(f24852_early_inflated, typeof((f24852_kernel, x, y))) == Any[Float64] - # TODO: test that `expand_early = true` + inflated `method_for_inference_limit_heuristics` # can be used to tighten up some inference result. diff --git a/test/compiler/validation.jl b/test/compiler/validation.jl index c25aae71ab157..5fd074fee73ae 100644 --- a/test/compiler/validation.jl +++ b/test/compiler/validation.jl @@ -22,10 +22,9 @@ msig = Tuple{typeof(f22938),Int,Int,Int,Int} world = Base.get_world_counter() match = only(Base._methods_by_ftype(msig, -1, world)) mi = Core.Compiler.specialize_method(match) -c0 = Core.Compiler.retrieve_code_info(mi) +c0 = Core.Compiler.retrieve_code_info(mi, world) -@test isempty(Core.Compiler.validate_code(mi)) -@test isempty(Core.Compiler.validate_code(c0)) +@test isempty(Core.Compiler.validate_code(mi, c0)) @testset "INVALID_EXPR_HEAD" begin c = copy(c0) @@ -116,7 +115,7 @@ end @testset "SIGNATURE_NARGS_MISMATCH" begin old_sig = mi.def.sig mi.def.sig = Tuple{1,2} - errors = Core.Compiler.validate_code(mi) + errors = Core.Compiler.validate_code(mi, nothing) mi.def.sig = old_sig @test length(errors) == 1 @test errors[1].kind === Core.Compiler.SIGNATURE_NARGS_MISMATCH @@ -132,7 +131,7 @@ end @testset "SLOTNAMES_NARGS_MISMATCH" begin mi.def.nargs += 20 - errors = Core.Compiler.validate_code(mi) + errors = Core.Compiler.validate_code(mi, c0) mi.def.nargs -= 20 @test length(errors) == 2 @test count(e.kind === Core.Compiler.SLOTNAMES_NARGS_MISMATCH for e in errors) == 1 diff --git a/test/core.jl b/test/core.jl index 8af7421ba7501..8507f2bdb8a01 100644 --- a/test/core.jl +++ b/test/core.jl @@ -3883,7 +3883,8 @@ PossiblyInvalidUnion{T} = Union{T,Int} # issue #13007 call13007(::Type{Array{T,N}}) where {T,N} = 0 call13007(::Type{Array}) = 1 -@test length(Base._methods(call13007, Tuple{Type{x} where x<:Array}, 4, typemax(UInt))) == 2 +@test Base._methods(call13007, Tuple{Type{x} where x<:Array}, 4, typemax(UInt)) === nothing +@test length(Base._methods(call13007, Tuple{Type{x} where x<:Array}, 4, Base.get_world_counter())) == 2 # detecting cycles during type intersection, e.g. #1631 cycle_in_solve_tvar_constraints(::Type{Some{S}}, x::S) where {S} = 0 diff --git a/test/opaque_closure.jl b/test/opaque_closure.jl index b5d5f9ed522ac..d54d38403913a 100644 --- a/test/opaque_closure.jl +++ b/test/opaque_closure.jl @@ -173,28 +173,23 @@ mk_va_opaque() = @opaque (x...)->x @test repr(@opaque x->1) == "(::Any)::Any->◌" # Opaque closure in CodeInfo returned from generated functions -function mk_ocg(args...) - ci = @code_lowered const_int() - cig = Meta.lower(@__MODULE__, Expr(:new_opaque_closure, Tuple{}, Any, Any, - Expr(:opaque_closure_method, nothing, 0, false, lno, ci))).args[1] - cig.slotnames = Symbol[Symbol("#self#")] - cig.slottypes = Any[Any] - cig.slotflags = UInt8[0x00] - cig +let ci = @code_lowered const_int() + global function mk_ocg(world::UInt, source, args...) + @nospecialize + cig = Meta.lower(@__MODULE__, Expr(:new_opaque_closure, Tuple{}, Any, Any, + Expr(:opaque_closure_method, nothing, 0, false, lno, ci))).args[1] + cig.slotnames = Symbol[Symbol("#self#")] + cig.slottypes = Any[Any] + cig.slotflags = UInt8[0x00] + @assert cig.min_world == UInt(1) + @assert cig.max_world == typemax(UInt) + return cig + end end @eval function oc_trivial_generated() $(Expr(:meta, :generated_only)) - $(Expr(:meta, - :generated, - Expr(:new, - Core.GeneratedFunctionStub, - :mk_ocg, - Any[:oc_trivial_generated], - Any[], - @__LINE__, - QuoteNode(Symbol(@__FILE__)), - true))) + $(Expr(:meta, :generated, mk_ocg)) end @test isa(oc_trivial_generated(), Core.OpaqueClosure{Tuple{}, Any}) @test oc_trivial_generated()() == 1 diff --git a/test/reflection.jl b/test/reflection.jl index 0c1081ba2c42f..f31b77ad26aed 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -648,7 +648,7 @@ let world = Core.Compiler.get_world_counter() match = Base._methods_by_ftype(T22979, -1, world)[1] instance = Core.Compiler.specialize_method(match) - cinfo_generated = Core.Compiler.get_staged(instance) + cinfo_generated = Core.Compiler.get_staged(instance, world) @test_throws ErrorException Base.uncompressed_ir(match.method) test_similar_codeinfo(code_lowered(f22979, typeof(x22979))[1], cinfo_generated) diff --git a/test/staged.jl b/test/staged.jl index 4a7fa3d7f4c84..0fa8ecb182cff 100644 --- a/test/staged.jl +++ b/test/staged.jl @@ -196,12 +196,11 @@ let gf_err2 return nothing end Expected = ErrorException("code reflection cannot be used from generated functions") + @test_throws Expected gf_err2(code_lowered) @test_throws Expected gf_err2(code_typed) @test_throws Expected gf_err2(code_llvm) @test_throws Expected gf_err2(code_native) - @test gf_err_ref[] == 66 - @test gf_err2(code_lowered) === nothing - @test gf_err_ref[] == 1077 + @test gf_err_ref[] == 88 end # issue #15043 @@ -246,12 +245,18 @@ f22440kernel(x::AbstractFloat) = x * x f22440kernel(::Type{T}) where {T} = one(T) f22440kernel(::Type{T}) where {T<:AbstractFloat} = zero(T) -@generated function f22440(y) - match = Base._methods_by_ftype(Tuple{typeof(f22440kernel),y}, -1, typemax(UInt))[1] +function f22440_gen(world::UInt, source, _, y) + match = only(Base._methods_by_ftype(Tuple{typeof(f22440kernel),y}, -1, world)) code_info = Base.uncompressed_ir(match.method) Meta.partially_inline!(code_info.code, Any[], match.spec_types, Any[match.sparams...], 0, 0, :propagate) + # TODO: this is mandatory: code_info.min_world = max(code_info.min_world, min_world[]) + # TODO: this is mandatory: code_info.max_world = min(code_info.max_world, max_world[]) return code_info end +@eval function f22440(y) + $(Expr(:meta, :generated, f22440_gen)) + $(Expr(:meta, :generated_only)) +end @test f22440(Int) === f22440kernel(Int) @test f22440(Float64) === f22440kernel(Float64) @@ -309,26 +314,33 @@ end # https://github.com/JuliaDebug/CassetteOverlay.jl/issues/12 # generated function with varargs and unfortunately placed unused slot @generated function f_vararg_generated(args...) + local unusedslot4 + local unusedslot5 + local unusedslot6 :($args) end g_vararg_generated() = f_vararg_generated((;), (;), Base.inferencebarrier((;))) let tup = g_vararg_generated() @test all(==(typeof((;))), tup) - # This is just to make sure that the test is actually testing what we want - - # the test only works if there's an unused that matches the position of the - # inferencebarrier argument above (N.B. the generator function itself + # This is just to make sure that the test is actually testing what we want: + # the test only works if there is an unused that matches the position of + # the inferencebarrier argument above (N.B. the generator function itself # shifts everything over by 1) - @test only(code_lowered(only(methods(f_vararg_generated)).generator.gen)).slotflags[5] == UInt8(0x00) + @test_broken only(code_lowered(only(methods(f_vararg_generated)).generator.gen)).slotflags[5] == 0x00 end # respect a given linetable in code generation # https://github.com/JuliaLang/julia/pull/47750 -let match = Base._which(Tuple{typeof(sin),Int}) +let world = Base.get_world_counter() + match = Base._which(Tuple{typeof(sin), Int}; world) mi = Core.Compiler.specialize_method(match) - lwr = Core.Compiler.retrieve_code_info(mi) - @test all(lin->lin.method===:sin, lwr.linetable) - @generated sin_generated(a) = lwr + lwr = Core.Compiler.retrieve_code_info(mi, world) + @test all(lin->lin.method === :sin, lwr.linetable) + @eval function sin_generated(a) + $(Expr(:meta, :generated, Returns(lwr))) + $(Expr(:meta, :generated_only)) + end src = only(code_lowered(sin_generated, (Int,))) - @test all(lin->lin.method===:sin, src.linetable) + @test all(lin->lin.method === :sin, src.linetable) @test sin_generated(42) == sin(42) end diff --git a/test/syntax.jl b/test/syntax.jl index 756af45e6b3c7..e60bbd81a04ab 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3053,9 +3053,6 @@ end end # issue 25678 -@generated f25678(x::T) where {T} = code_lowered(sin, Tuple{x})[] -@test f25678(pi/6) === sin(pi/6) - @generated g25678(x) = return :x @test g25678(7) === 7 From b600f51eab283333ede6e3120b104b3aa4d9043b Mon Sep 17 00:00:00 2001 From: Lukas Schwerdt Date: Thu, 23 Feb 2023 02:14:06 +0100 Subject: [PATCH 164/775] Document stability for rev=true in sort! (#48759) --- base/sort.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/sort.jl b/base/sort.jl index 5ee247e0a0484..4605ccd06d734 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1342,7 +1342,8 @@ specific algorithm to use via the `alg` keyword (see [Sorting Algorithms](@ref) available algorithms). The `by` keyword lets you provide a function that will be applied to each element before comparison; the `lt` keyword allows providing a custom "less than" function (note that for every `x` and `y`, only one of `lt(x,y)` and `lt(y,x)` can return -`true`); use `rev=true` to reverse the sorting order. These options are independent and can +`true`); use `rev=true` to reverse the sorting order. `rev=true` preserves forward stability: +Elements that compare equal are not reversed. These options are independent and can be used together in all possible combinations: if both `by` and `lt` are specified, the `lt` function is applied to the result of the `by` function; `rev=true` reverses whatever ordering specified via the `by` and `lt` keywords. From 7823552ee41421324613a9f8ae6ec674c8708a8a Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Thu, 23 Feb 2023 10:37:05 +0100 Subject: [PATCH 165/775] Revert "generators: expose caller world to GeneratedFunctionStub (#48611)" (#48763) This reverts commit e3d366f1966595ba737220df49e220610823b331. --- base/Base.jl | 2 +- base/boot.jl | 27 +++++---- base/compiler/abstractinterpretation.jl | 13 ++--- base/compiler/bootstrap.jl | 2 +- base/compiler/inferencestate.jl | 3 +- base/compiler/optimize.jl | 3 +- base/compiler/typeinfer.jl | 2 +- base/compiler/types.jl | 2 +- base/compiler/utilities.jl | 8 +-- base/compiler/validation.jl | 7 ++- base/expr.jl | 5 +- base/reflection.jl | 38 +++++-------- doc/src/devdocs/ast.md | 8 +-- src/aotcompile.cpp | 2 +- src/ast.c | 4 +- src/gf.c | 7 +-- src/interpreter.c | 8 +-- src/jitlayers.cpp | 4 +- src/julia-syntax.scm | 9 ++- src/julia.h | 2 +- src/julia_internal.h | 2 +- src/method.c | 47 +++++++++------- test/ambiguous.jl | 6 +- test/compiler/contextual.jl | 33 ++++++----- test/compiler/inference.jl | 74 ++++++++++++------------- test/compiler/validation.jl | 9 +-- test/core.jl | 3 +- test/opaque_closure.jl | 31 ++++++----- test/reflection.jl | 2 +- test/staged.jl | 40 +++++-------- test/syntax.jl | 3 + 31 files changed, 203 insertions(+), 203 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 58d527f365aa7..85a9c8d5048e3 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -478,7 +478,7 @@ in_sysimage(pkgid::PkgId) = pkgid in _sysimage_modules for match = _methods(+, (Int, Int), -1, 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))) + copy(Core.Compiler.retrieve_code_info(Core.Compiler.specialize_method(match))) empty!(Set()) push!(push!(Set{Union{GlobalRef,Symbol}}(), :two), GlobalRef(Base, :two)) diff --git a/base/boot.jl b/base/boot.jl index ca6e6c81405e2..b4e01b0c884c1 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -590,25 +590,28 @@ println(@nospecialize a...) = println(stdout, a...) struct GeneratedFunctionStub gen - argnames::SimpleVector - spnames::SimpleVector + argnames::Array{Any,1} + spnames::Union{Nothing, Array{Any,1}} + line::Int + file::Symbol + expand_early::Bool end -# invoke and wrap the results of @generated expression -function (g::GeneratedFunctionStub)(world::UInt, source::LineNumberNode, @nospecialize args...) - # args is (spvals..., argtypes...) +# invoke and wrap the results of @generated +function (g::GeneratedFunctionStub)(@nospecialize args...) body = g.gen(args...) - file = source.file - file isa Symbol || (file = :none) - lam = Expr(:lambda, Expr(:argnames, g.argnames...).args, - Expr(:var"scope-block", + if body isa CodeInfo + return body + end + lam = Expr(:lambda, g.argnames, + Expr(Symbol("scope-block"), Expr(:block, - source, - Expr(:meta, :push_loc, file, :var"@generated body"), + LineNumberNode(g.line, g.file), + Expr(:meta, :push_loc, g.file, Symbol("@generated body")), Expr(:return, body), Expr(:meta, :pop_loc)))) spnames = g.spnames - if spnames === svec() + if spnames === nothing return lam else return Expr(Symbol("with-static-parameters"), lam, spnames...) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 5fac62eb7578d..0d0280e40e817 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -569,7 +569,7 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp break end topmost === nothing || continue - if edge_matches_sv(interp, infstate, method, sig, sparams, hardlimit, sv) + if edge_matches_sv(infstate, method, sig, sparams, hardlimit, sv) topmost = infstate edgecycle = true end @@ -677,13 +677,12 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp return MethodCallResult(rt, edgecycle, edgelimited, edge, effects) end -function edge_matches_sv(interp::AbstractInterpreter, frame::InferenceState, method::Method, @nospecialize(sig), sparams::SimpleVector, hardlimit::Bool, sv::InferenceState) +function edge_matches_sv(frame::InferenceState, method::Method, @nospecialize(sig), sparams::SimpleVector, hardlimit::Bool, sv::InferenceState) # The `method_for_inference_heuristics` will expand the given method's generator if # necessary in order to retrieve this field from the generated `CodeInfo`, if it exists. # The other `CodeInfo`s we inspect will already have this field inflated, so we just # access it directly instead (to avoid regeneration). - world = get_world_counter(interp) - callee_method2 = method_for_inference_heuristics(method, sig, sparams, world) # Union{Method, Nothing} + callee_method2 = method_for_inference_heuristics(method, sig, sparams) # Union{Method, Nothing} inf_method2 = frame.src.method_for_inference_limit_heuristics # limit only if user token match inf_method2 isa Method || (inf_method2 = nothing) @@ -720,11 +719,11 @@ function edge_matches_sv(interp::AbstractInterpreter, frame::InferenceState, met end # This function is used for computing alternate limit heuristics -function method_for_inference_heuristics(method::Method, @nospecialize(sig), sparams::SimpleVector, world::UInt) - if isdefined(method, :generator) && !(method.generator isa Core.GeneratedFunctionStub) && may_invoke_generator(method, sig, sparams) +function method_for_inference_heuristics(method::Method, @nospecialize(sig), sparams::SimpleVector) + if isdefined(method, :generator) && method.generator.expand_early && may_invoke_generator(method, sig, sparams) method_instance = specialize_method(method, sig, sparams) if isa(method_instance, MethodInstance) - cinfo = get_staged(method_instance, world) + cinfo = get_staged(method_instance) if isa(cinfo, CodeInfo) method2 = cinfo.method_for_inference_limit_heuristics if method2 isa Method diff --git a/base/compiler/bootstrap.jl b/base/compiler/bootstrap.jl index 1f62d21c9d2d9..77b36cb9c7f71 100644 --- a/base/compiler/bootstrap.jl +++ b/base/compiler/bootstrap.jl @@ -36,7 +36,7 @@ let interp = NativeInterpreter() else tt = Tuple{typeof(f), Vararg{Any}} end - for m in _methods_by_ftype(tt, 10, get_world_counter())::Vector + for m in _methods_by_ftype(tt, 10, typemax(UInt))::Vector # remove any TypeVars from the intersection m = m::MethodMatch typ = Any[m.spec_types.parameters...] diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 959d2d157f219..7ff740a23a540 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -363,8 +363,7 @@ end function InferenceState(result::InferenceResult, cache::Symbol, interp::AbstractInterpreter) # prepare an InferenceState object for inferring lambda - world = get_world_counter(interp) - src = retrieve_code_info(result.linfo, world) + src = retrieve_code_info(result.linfo) src === nothing && return nothing validate_code_in_debug_mode(result.linfo, src, "lowered") return InferenceState(result, src, cache, interp) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index edf678e919b61..dc321be5108cf 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -183,8 +183,7 @@ function OptimizationState(linfo::MethodInstance, src::CodeInfo, params::Optimiz return OptimizationState(linfo, src, nothing, stmt_info, mod, sptypes, slottypes, inlining, nothing, false) end function OptimizationState(linfo::MethodInstance, params::OptimizationParams, interp::AbstractInterpreter) - world = get_world_counter(interp) - src = retrieve_code_info(linfo, world) + src = retrieve_code_info(linfo) src === nothing && return nothing return OptimizationState(linfo, src, params, interp) end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 734c357201d25..f7723a968da3e 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -1035,7 +1035,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) end end if ccall(:jl_get_module_infer, Cint, (Any,), method.module) == 0 && !generating_sysimg() - return retrieve_code_info(mi, get_world_counter(interp)) + return retrieve_code_info(mi) end lock_mi_inference(interp, mi) result = InferenceResult(mi, typeinf_lattice(interp)) diff --git a/base/compiler/types.jl b/base/compiler/types.jl index 7bdbdbd8bdf01..cac15e9a69513 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -330,7 +330,7 @@ struct NativeInterpreter <: AbstractInterpreter cache = Vector{InferenceResult}() # Initially empty cache # Sometimes the caller is lazy and passes typemax(UInt). - # we cap it to the current world age for correctness + # we cap it to the current world age if world == typemax(UInt) world = get_world_counter() end diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 0acbc926ae671..6cf600560902d 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -114,23 +114,23 @@ end invoke_api(li::CodeInstance) = ccall(:jl_invoke_api, Cint, (Any,), li) use_const_api(li::CodeInstance) = invoke_api(li) == 2 -function get_staged(mi::MethodInstance, world::UInt) +function get_staged(mi::MethodInstance) may_invoke_generator(mi) || return nothing try # user code might throw errors – ignore them - ci = ccall(:jl_code_for_staged, Any, (Any, UInt), mi, world)::CodeInfo + ci = ccall(:jl_code_for_staged, Any, (Any,), mi)::CodeInfo return ci catch return nothing end end -function retrieve_code_info(linfo::MethodInstance, world::UInt) +function retrieve_code_info(linfo::MethodInstance) m = linfo.def::Method c = nothing if isdefined(m, :generator) # user code might throw errors – ignore them - c = get_staged(linfo, world) + c = get_staged(linfo) end if c === nothing && isdefined(m, :source) src = m.source diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl index 68eb2ab15c59d..22a21a4559985 100644 --- a/base/compiler/validation.jl +++ b/base/compiler/validation.jl @@ -200,14 +200,15 @@ end """ validate_code!(errors::Vector{InvalidCodeError}, mi::MethodInstance, - c::Union{Nothing,CodeInfo}) + c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi)) Validate `mi`, logging any violation by pushing an `InvalidCodeError` into `errors`. If `isa(c, CodeInfo)`, also call `validate_code!(errors, c)`. It is assumed that `c` is -a `CodeInfo` instance associated with `mi`. +the `CodeInfo` instance associated with `mi`. """ -function validate_code!(errors::Vector{InvalidCodeError}, mi::Core.MethodInstance, c::Union{Nothing,CodeInfo}) +function validate_code!(errors::Vector{InvalidCodeError}, mi::Core.MethodInstance, + c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi)) is_top_level = mi.def isa Module if is_top_level mnargs = 0 diff --git a/base/expr.jl b/base/expr.jl index 5649303b41ef4..46e89bf64da8a 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -962,7 +962,10 @@ macro generated(f) Expr(:block, lno, Expr(:if, Expr(:generated), - body, + # https://github.com/JuliaLang/julia/issues/25678 + Expr(:block, + :(local $tmp = $body), + :(if $tmp isa $(GlobalRef(Core, :CodeInfo)); return $tmp; else $tmp; end)), Expr(:block, Expr(:meta, :generated_only), Expr(:return, nothing)))))) diff --git a/base/reflection.jl b/base/reflection.jl index bb7dfc0f0cf00..9e2615a16a190 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -961,11 +961,10 @@ function code_lowered(@nospecialize(f), @nospecialize(t=Tuple); generated::Bool= if debuginfo !== :source && debuginfo !== :none throw(ArgumentError("'debuginfo' must be either :source or :none")) end - world = get_world_counter() - return map(method_instances(f, t, world)) do m + return map(method_instances(f, t)) do m if generated && hasgenerator(m) if may_invoke_generator(m) - return ccall(:jl_code_for_staged, Any, (Any, UInt), m, world)::CodeInfo + return ccall(:jl_code_for_staged, Any, (Any,), m)::CodeInfo else error("Could not expand generator for `@generated` method ", m, ". ", "This can happen if the provided argument types (", t, ") are ", @@ -1054,8 +1053,6 @@ methods(@nospecialize(f), @nospecialize(t), mod::Module) = methods(f, t, (mod,)) function methods_including_ambiguous(@nospecialize(f), @nospecialize(t)) tt = signature_type(f, t) world = get_world_counter() - (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && - error("code reflection cannot be used from generated functions") min = RefValue{UInt}(typemin(UInt)) max = RefValue{UInt}(typemax(UInt)) ms = _methods_by_ftype(tt, nothing, -1, world, true, min, max, Ptr{Int32}(C_NULL))::Vector @@ -1128,11 +1125,9 @@ _uncompressed_ir(ci::Core.CodeInstance, s::Array{UInt8,1}) = ccall(:jl_uncompres const uncompressed_ast = uncompressed_ir const _uncompressed_ast = _uncompressed_ir -function method_instances(@nospecialize(f), @nospecialize(t), world::UInt) +function method_instances(@nospecialize(f), @nospecialize(t), world::UInt=get_world_counter()) tt = signature_type(f, t) results = Core.MethodInstance[] - # this make a better error message than the typeassert that follows - world == typemax(UInt) && error("code reflection cannot be used from generated functions") for match in _methods_by_ftype(tt, -1, world)::Vector instance = Core.Compiler.specialize_method(match) push!(results, instance) @@ -1203,22 +1198,20 @@ function may_invoke_generator(method::Method, @nospecialize(atype), sparams::Sim # generator only has one method generator = method.generator isa(generator, Core.GeneratedFunctionStub) || return false - gen_mthds = _methods_by_ftype(Tuple{typeof(generator.gen), Vararg{Any}}, 1, method.primary_world) - (gen_mthds isa Vector && length(gen_mthds) == 1) || return false + gen_mthds = methods(generator.gen)::MethodList + length(gen_mthds) == 1 || return false - generator_method = first(gen_mthds).method + generator_method = first(gen_mthds) nsparams = length(sparams) isdefined(generator_method, :source) || return false code = generator_method.source nslots = ccall(:jl_ir_nslots, Int, (Any,), code) - at = unwrap_unionall(atype) - at isa DataType || return false + at = unwrap_unionall(atype)::DataType (nslots >= 1 + length(sparams) + length(at.parameters)) || return false - firstarg = 1 for i = 1:nsparams if isa(sparams[i], TypeVar) - if (ast_slotflag(code, firstarg + i) & SLOT_USED) != 0 + if (ast_slotflag(code, 1 + i) & SLOT_USED) != 0 return false end end @@ -1227,7 +1220,7 @@ function may_invoke_generator(method::Method, @nospecialize(atype), sparams::Sim non_va_args = method.isva ? nargs - 1 : nargs for i = 1:non_va_args if !isdispatchelem(at.parameters[i]) - if (ast_slotflag(code, firstarg + i + nsparams) & SLOT_USED) != 0 + if (ast_slotflag(code, 1 + i + nsparams) & SLOT_USED) != 0 return false end end @@ -1235,7 +1228,7 @@ function may_invoke_generator(method::Method, @nospecialize(atype), sparams::Sim if method.isva # If the va argument is used, we need to ensure that all arguments that # contribute to the va tuple are dispatchelemes - if (ast_slotflag(code, firstarg + nargs + nsparams) & SLOT_USED) != 0 + if (ast_slotflag(code, 1 + nargs + nsparams) & SLOT_USED) != 0 for i = (non_va_args+1):length(at.parameters) if !isdispatchelem(at.parameters[i]) return false @@ -1325,8 +1318,7 @@ function code_typed_by_type(@nospecialize(tt::Type); debuginfo::Symbol=:default, world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) - (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && - error("code reflection cannot be used from generated functions") + ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") if @isdefined(IRShow) debuginfo = IRShow.debuginfo(debuginfo) elseif debuginfo === :default @@ -1435,7 +1427,7 @@ function code_ircode_by_type( interp = Core.Compiler.NativeInterpreter(world), optimize_until::Union{Integer,AbstractString,Nothing} = nothing, ) - (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && + ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") tt = to_tuple_type(tt) matches = _methods_by_ftype(tt, -1, world)::Vector @@ -1462,8 +1454,7 @@ end function return_types(@nospecialize(f), @nospecialize(types=default_tt(f)); world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) - (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && - error("code reflection cannot be used from generated functions") + ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") if isa(f, Core.OpaqueClosure) _, rt = only(code_typed_opaque_closure(f)) return Any[rt] @@ -1487,8 +1478,7 @@ end function infer_effects(@nospecialize(f), @nospecialize(types=default_tt(f)); world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) - (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && - error("code reflection cannot be used from generated functions") + ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") if isa(f, Core.Builtin) types = to_tuple_type(types) argtypes = Any[Core.Compiler.Const(f), types.parameters...] diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index 76b0cc97df5bf..df6a2224c2a48 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -685,10 +685,10 @@ A (usually temporary) container for holding lowered source code. A `UInt8` array of slot properties, represented as bit flags: - * 0x02 - assigned (only false if there are *no* assignment statements with this var on the left) - * 0x08 - used (if there is any read or write of the slot) - * 0x10 - statically assigned once - * 0x20 - might be used before assigned. This flag is only valid after type inference. + * 2 - assigned (only false if there are *no* assignment statements with this var on the left) + * 8 - const (currently unused for local variables) + * 16 - statically assigned once + * 32 - might be used before assigned. This flag is only valid after type inference. * `ssavaluetypes` diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 866d9dabe5100..d1694eaf9e0d5 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1088,7 +1088,7 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, siz if (src) jlrettype = src->rettype; else if (jl_is_method(mi->def.method)) { - src = mi->def.method->generator ? jl_code_for_staged(mi, world) : (jl_code_info_t*)mi->def.method->source; + src = mi->def.method->generator ? jl_code_for_staged(mi) : (jl_code_info_t*)mi->def.method->source; if (src && !jl_is_code_info(src) && jl_is_method(mi->def.method)) src = jl_uncompress_ir(mi->def.method, NULL, (jl_array_t*)src); } diff --git a/src/ast.c b/src/ast.c index 3f3d6176d342e..cb03b7efb6eb7 100644 --- a/src/ast.c +++ b/src/ast.c @@ -1024,10 +1024,10 @@ static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule jl_value_t *result; JL_TRY { margs[0] = jl_toplevel_eval(*ctx, margs[0]); - jl_method_instance_t *mfunc = jl_method_lookup(margs, nargs, ct->world_age); + jl_method_instance_t *mfunc = jl_method_lookup(margs, nargs, world); JL_GC_PROMISE_ROOTED(mfunc); if (mfunc == NULL) { - jl_method_error(margs[0], &margs[1], nargs, ct->world_age); + jl_method_error(margs[0], &margs[1], nargs, world); // unreachable } *ctx = mfunc->def.method->module; diff --git a/src/gf.c b/src/gf.c index d8c5e571e820f..42990baf7ad24 100644 --- a/src/gf.c +++ b/src/gf.c @@ -27,9 +27,6 @@ extern "C" { JL_DLLEXPORT _Atomic(size_t) jl_world_counter = 1; // uses atomic acquire/release JL_DLLEXPORT size_t jl_get_world_counter(void) JL_NOTSAFEPOINT { - jl_task_t *ct = jl_current_task; - if (ct->ptls->in_pure_callback) - return ~(size_t)0; return jl_atomic_load_acquire(&jl_world_counter); } @@ -2299,7 +2296,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t // if that didn't work and compilation is off, try running in the interpreter if (compile_option == JL_OPTIONS_COMPILE_OFF || compile_option == JL_OPTIONS_COMPILE_MIN) { - jl_code_info_t *src = jl_code_for_interpreter(mi, world); + jl_code_info_t *src = jl_code_for_interpreter(mi); if (!jl_code_requires_compiler(src, 0)) { jl_code_instance_t *codeinst = jl_new_codeinst(mi, (jl_value_t*)jl_any_type, NULL, NULL, @@ -3142,8 +3139,6 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, int intersections, size_t world, int cache_result, size_t *min_valid, size_t *max_valid, int *ambig) { - if (world > jl_atomic_load_acquire(&jl_world_counter)) - return jl_nothing; // the future is not enumerable int has_ambiguity = 0; jl_value_t *unw = jl_unwrap_unionall((jl_value_t*)type); assert(jl_is_datatype(unw)); diff --git a/src/interpreter.c b/src/interpreter.c index 713887f234898..08cb87791c5a3 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -626,7 +626,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, // preparing method IR for interpreter -jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *mi, size_t world) +jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *mi) { jl_code_info_t *src = (jl_code_info_t*)jl_atomic_load_relaxed(&mi->uninferred); if (jl_is_method(mi->def.value)) { @@ -636,7 +636,7 @@ jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *mi, size_t world) } else { assert(mi->def.method->generator); - src = jl_code_for_staged(mi, world); + src = jl_code_for_staged(mi); } } if (src && (jl_value_t*)src != jl_nothing) { @@ -659,9 +659,7 @@ jl_value_t *NOINLINE jl_fptr_interpret_call(jl_value_t *f, jl_value_t **args, ui { interpreter_state *s; jl_method_instance_t *mi = codeinst->def; - jl_task_t *ct = jl_current_task; - size_t world = ct->world_age; - jl_code_info_t *src = jl_code_for_interpreter(mi, world); + jl_code_info_t *src = jl_code_for_interpreter(mi); jl_array_t *stmts = src->code; assert(jl_typeis(stmts, jl_array_any_type)); unsigned nroots = jl_source_nslots(src) + jl_source_nssavalues(src) + 2; diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 940613fd596f4..b489665f5629d 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -499,7 +499,7 @@ void jl_generate_fptr_for_unspecialized_impl(jl_code_instance_t *unspec) // TODO: this is wrong assert(def->generator); // TODO: jl_code_for_staged can throw - src = jl_code_for_staged(unspec->def, unspec->min_world); + src = jl_code_for_staged(unspec->def); } if (src && (jl_value_t*)src != jl_nothing) src = jl_uncompress_ir(def, NULL, (jl_array_t*)src); @@ -554,7 +554,7 @@ jl_value_t *jl_dump_method_asm_impl(jl_method_instance_t *mi, size_t world, if (jl_is_method(def)) { if (!src) { // TODO: jl_code_for_staged can throw - src = def->generator ? jl_code_for_staged(mi, world) : (jl_code_info_t*)def->source; + src = def->generator ? jl_code_for_staged(mi) : (jl_code_info_t*)def->source; } if (src && (jl_value_t*)src != jl_nothing) src = jl_uncompress_ir(mi->def.method, codeinst, (jl_array_t*)src); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 2a4e95ab1da86..4a0407e019432 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -382,8 +382,13 @@ `((meta generated (new (core GeneratedFunctionStub) ,gname - (call (core svec) ,@(map quotify anames)) - (call (core svec) ,@(map quotify names))))))) + ,(cons 'list anames) + ,(if (null? sparams) + 'nothing + (cons 'list (map car sparams))) + ,(cadr loc) + (inert ,(caddr loc)) + (false)))))) (list gf)) '())) (types (llist-types argl)) diff --git a/src/julia.h b/src/julia.h index 083e4ef2eab02..19dab5cd3a704 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1495,7 +1495,7 @@ JL_DLLEXPORT jl_value_t *jl_generic_function_def(jl_sym_t *name, _Atomic(jl_value_t*) *bp, jl_binding_t *bnd); JL_DLLEXPORT jl_method_t *jl_method_def(jl_svec_t *argdata, jl_methtable_t *mt, jl_code_info_t *f, jl_module_t *module); -JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo, size_t world); +JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo); JL_DLLEXPORT jl_code_info_t *jl_copy_code_info(jl_code_info_t *src); JL_DLLEXPORT size_t jl_get_world_counter(void) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_box_bool(int8_t x) JL_NOTSAFEPOINT; diff --git a/src/julia_internal.h b/src/julia_internal.h index 492c0ec8a8984..b77de64732116 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -621,7 +621,7 @@ jl_method_instance_t *jl_get_unspecialized(jl_method_t *def JL_PROPAGATES_ROOT); JL_DLLEXPORT void jl_compile_method_instance(jl_method_instance_t *mi, jl_tupletype_t *types, size_t world); JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types); -jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *lam JL_PROPAGATES_ROOT, size_t world); +jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *lam JL_PROPAGATES_ROOT); int jl_code_requires_compiler(jl_code_info_t *src, int include_force_compile); jl_code_info_t *jl_new_code_info_from_ir(jl_expr_t *ast); JL_DLLEXPORT jl_code_info_t *jl_new_code_info_uninit(void); diff --git a/src/method.c b/src/method.c index 64bec989251a8..d5f56a5921358 100644 --- a/src/method.c +++ b/src/method.c @@ -517,21 +517,21 @@ void jl_add_function_name_to_lineinfo(jl_code_info_t *ci, jl_value_t *name) } // invoke (compiling if necessary) the jlcall function pointer for a method template -static jl_value_t *jl_call_staged(jl_method_t *def, jl_value_t *generator, - size_t world, jl_svec_t *sparam_vals, jl_value_t **args, uint32_t nargs) +STATIC_INLINE jl_value_t *jl_call_staged(jl_method_t *def, jl_value_t *generator, jl_svec_t *sparam_vals, + jl_value_t **args, uint32_t nargs) { size_t n_sparams = jl_svec_len(sparam_vals); jl_value_t **gargs; - size_t totargs = 2 + n_sparams + def->nargs; + size_t totargs = 1 + n_sparams + nargs + def->isva; JL_GC_PUSHARGS(gargs, totargs); - gargs[0] = jl_box_ulong(world); - gargs[1] = jl_box_long(def->line); - gargs[1] = jl_new_struct(jl_linenumbernode_type, gargs[1], def->file); - memcpy(&gargs[2], jl_svec_data(sparam_vals), n_sparams * sizeof(void*)); - memcpy(&gargs[2 + n_sparams], args, (def->nargs - def->isva) * sizeof(void*)); - if (def->isva) - gargs[totargs - 1] = jl_f_tuple(NULL, &args[def->nargs - 1], nargs - def->nargs + 1); - jl_value_t *code = jl_apply_generic(generator, gargs, totargs); + gargs[0] = generator; + memcpy(&gargs[1], jl_svec_data(sparam_vals), n_sparams * sizeof(void*)); + memcpy(&gargs[1 + n_sparams], args, nargs * sizeof(void*)); + if (def->isva) { + gargs[totargs-1] = jl_f_tuple(NULL, &gargs[1 + n_sparams + def->nargs - 1], nargs - (def->nargs - 1)); + gargs[1 + n_sparams + def->nargs - 1] = gargs[totargs - 1]; + } + jl_value_t *code = jl_apply(gargs, 1 + n_sparams + def->nargs); JL_GC_POP(); return code; } @@ -555,7 +555,7 @@ JL_DLLEXPORT jl_code_info_t *jl_expand_and_resolve(jl_value_t *ex, jl_module_t * // Return a newly allocated CodeInfo for the function signature // effectively described by the tuple (specTypes, env, Method) inside linfo -JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo, size_t world) +JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo) { jl_value_t *uninferred = jl_atomic_load_relaxed(&linfo->uninferred); if (uninferred) { @@ -579,13 +579,13 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo, siz JL_TRY { ct->ptls->in_pure_callback = 1; + // and the right world ct->world_age = def->primary_world; // invoke code generator jl_tupletype_t *ttdt = (jl_tupletype_t*)jl_unwrap_unionall(tt); - ex = jl_call_staged(def, generator, world, linfo->sparam_vals, jl_svec_data(ttdt->parameters), jl_nparams(ttdt)); + ex = jl_call_staged(def, generator, linfo->sparam_vals, jl_svec_data(ttdt->parameters), jl_nparams(ttdt)); - // do some post-processing if (jl_is_code_info(ex)) { func = (jl_code_info_t*)ex; jl_array_t *stmts = (jl_array_t*)func->code; @@ -602,6 +602,7 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo, siz jl_error("The function body AST defined by this @generated function is not pure. This likely means it contains a closure, a comprehension or a generator."); } } + jl_add_function_name_to_lineinfo(func, (jl_value_t*)def->name); // If this generated function has an opaque closure, cache it for @@ -741,12 +742,20 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) st = jl_nothing; } else if (nargs == 2 && jl_exprarg(st, 0) == (jl_value_t*)jl_generated_sym) { - if (m->generator != NULL) - jl_error("duplicate @generated function body"); + m->generator = NULL; jl_value_t *gexpr = jl_exprarg(st, 1); - // the frontend would put (new (core GeneratedFunctionStub) funcname argnames sp) here, for example - m->generator = jl_toplevel_eval(m->module, gexpr); - jl_gc_wb(m, m->generator); + if (jl_expr_nargs(gexpr) == 7) { + // expects (new (core GeneratedFunctionStub) funcname argnames sp line file expandearly) + jl_value_t *funcname = jl_exprarg(gexpr, 1); + assert(jl_is_symbol(funcname)); + if (jl_get_global(m->module, (jl_sym_t*)funcname) != NULL) { + m->generator = jl_toplevel_eval(m->module, gexpr); + jl_gc_wb(m, m->generator); + } + } + if (m->generator == NULL) { + jl_error("invalid @generated function; try placing it in global scope"); + } st = jl_nothing; } else if (nargs == 1 && jl_exprarg(st, 0) == (jl_value_t*)jl_generated_only_sym) { diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 4ab779d76e6f0..e96954299b702 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -357,7 +357,7 @@ f35983(::Type, ::Type) = 2 @test length(Base.methods(f35983, (Any, Any))) == 2 @test first(Base.methods(f35983, (Any, Any))).sig == Tuple{typeof(f35983), Type, Type} let ambig = Ref{Int32}(0) - ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, Base.get_world_counter(), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, typemax(UInt), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) @test ms isa Vector @test length(ms) == 1 @test ambig[] == 0 @@ -366,7 +366,7 @@ f35983(::Type{Int16}, ::Any) = 3 @test length(Base.methods_including_ambiguous(f35983, (Type, Type))) == 2 @test length(Base.methods(f35983, (Type, Type))) == 1 let ambig = Ref{Int32}(0) - ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, Base.get_world_counter(), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, typemax(UInt), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) @test ms isa Vector @test length(ms) == 2 @test ambig[] == 1 @@ -374,7 +374,7 @@ end struct B38280 <: Real; val; end let ambig = Ref{Int32}(0) - ms = Base._methods_by_ftype(Tuple{Type{B38280}, Any}, nothing, 1, Base.get_world_counter(), false, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + ms = Base._methods_by_ftype(Tuple{Type{B38280}, Any}, nothing, 1, typemax(UInt), false, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) @test ms isa Vector @test length(ms) == 1 @test ambig[] == 1 diff --git a/test/compiler/contextual.jl b/test/compiler/contextual.jl index 9db7ae1aeaa5d..740e985e388df 100644 --- a/test/compiler/contextual.jl +++ b/test/compiler/contextual.jl @@ -7,7 +7,7 @@ module MiniCassette # A minimal demonstration of the cassette mechanism. Doesn't support all the # fancy features, but sufficient to exercise this code path in the compiler. - using Core.Compiler: retrieve_code_info, CodeInfo, + using Core.Compiler: method_instances, retrieve_code_info, CodeInfo, MethodInstance, SSAValue, GotoNode, GotoIfNot, ReturnNode, SlotNumber, quoted, signature_type using Base: _methods_by_ftype @@ -69,28 +69,24 @@ module MiniCassette end end - function overdub_generator(world::UInt, source, self, c, f, args) - @nospecialize + function overdub_generator(self, c, f, args) if !Base.issingletontype(f) - # (c, f, args..) -> f(args...) - code_info = :(return f(args...)) - return Core.GeneratedFunctionStub(identity, Core.svec(:overdub, :c, :f, :args), Core.svec())(world, source, code_info) + return :(return f(args...)) end tt = Tuple{f, args...} - match = Base._which(tt; world) + match = Base._which(tt; world=typemax(UInt)) mi = Core.Compiler.specialize_method(match) # Unsupported in this mini-cassette @assert !mi.def.isva - code_info = retrieve_code_info(mi, world) + code_info = retrieve_code_info(mi) @assert isa(code_info, CodeInfo) code_info = copy(code_info) - @assert code_info.edges === nothing - code_info.edges = MethodInstance[mi] + if isdefined(code_info, :edges) + code_info.edges = MethodInstance[mi] + end transform!(code_info, length(args), match.sparams) - # TODO: this is mandatory: code_info.min_world = max(code_info.min_world, min_world[]) - # TODO: this is mandatory: code_info.max_world = min(code_info.max_world, max_world[]) - return code_info + code_info end @inline function overdub(c::Ctx, f::Union{Core.Builtin, Core.IntrinsicFunction}, args...) @@ -99,7 +95,16 @@ module MiniCassette @eval function overdub(c::Ctx, f, args...) $(Expr(:meta, :generated_only)) - $(Expr(:meta, :generated, overdub_generator)) + $(Expr(:meta, + :generated, + Expr(:new, + Core.GeneratedFunctionStub, + :overdub_generator, + Any[:overdub, :ctx, :f, :args], + Any[], + @__LINE__, + QuoteNode(Symbol(@__FILE__)), + true))) end end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 5209ef879324e..182776d79d7ec 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1787,17 +1787,9 @@ bar_22708(x) = f_22708(x) @test bar_22708(1) == "x" -struct EarlyGeneratedFunctionStub - stub::Core.GeneratedFunctionStub -end -(stub::EarlyGeneratedFunctionStub)(args...) = (@nospecialize; stub.stub(args...)) - # mechanism for spoofing work-limiting heuristics and early generator expansion (#24852) -function _generated_stub(gen::Symbol, args::Core.SimpleVector, params::Core.SimpleVector, expand_early::Bool) - stub = Expr(:new, Core.GeneratedFunctionStub, gen, args, params) - if expand_early - stub = Expr(:new, EarlyGeneratedFunctionStub, stub) - end +function _generated_stub(gen::Symbol, args::Vector{Any}, params::Vector{Any}, line, file, expand_early) + stub = Expr(:new, Core.GeneratedFunctionStub, gen, args, params, line, file, expand_early) return Expr(:meta, :generated, stub) end @@ -1806,21 +1798,10 @@ f24852_kernel2(x, y::Tuple) = f24852_kernel1(x, (y,)) f24852_kernel3(x, y::Tuple) = f24852_kernel2(x, (y,)) f24852_kernel(x, y::Number) = f24852_kernel3(x, (y,)) -function f24852_kernel_cinfo(world::UInt, source, fsig::Type) - matches = Base._methods_by_ftype(fsig, -1, world) - if matches === nothing || length(matches) != 1 - match = nothing - else - match = matches[1] - if !isdefined(match.method, :source) - match = nothing - end - end - if match === nothing - code_info = :(f(x, y)) - code_info = Core.GeneratedFunctionStub(identity, Core.svec(:self, :f, :x, :y), Core.svec(:X, :Y))(world, source, code_info) - return (nothing, code_info) - end +function f24852_kernel_cinfo(fsig::Type) + world = typemax(UInt) # FIXME + match = Base._methods_by_ftype(fsig, -1, world)[1] + isdefined(match.method, :source) || return (nothing, :(f(x, y))) code_info = Base.uncompressed_ir(match.method) Meta.partially_inline!(code_info.code, Any[], match.spec_types, Any[match.sparams...], 1, 0, :propagate) if startswith(String(match.method.name), "f24852") @@ -1835,23 +1816,21 @@ function f24852_kernel_cinfo(world::UInt, source, fsig::Type) end pushfirst!(code_info.slotnames, Symbol("#self#")) pushfirst!(code_info.slotflags, 0x00) - # TODO: this is mandatory: code_info.min_world = max(code_info.min_world, min_world[]) - # TODO: this is mandatory: code_info.max_world = min(code_info.max_world, max_world[]) return match.method, code_info end -function f24852_gen_cinfo_uninflated(world::UInt, source, X, Y, _, f, x, y) - _, code_info = f24852_kernel_cinfo(world, source, Tuple{f, x, y}) +function f24852_gen_cinfo_uninflated(X, Y, _, f, x, y) + _, code_info = f24852_kernel_cinfo(Tuple{f, x, y}) return code_info end -function f24852_gen_cinfo_inflated(world::UInt, source, X, Y, _, f, x, y) - method, code_info = f24852_kernel_cinfo(world, source, Tuple{f, x, y}) +function f24852_gen_cinfo_inflated(X, Y, _, f, x, y) + method, code_info = f24852_kernel_cinfo(Tuple{f, x, y}) code_info.method_for_inference_limit_heuristics = method return code_info end -function f24852_gen_expr(X, Y, _, f, x, y) # deparse of f(x::X, y::Y) where {X, Y} +function f24852_gen_expr(X, Y, _, f, x, y) # deparse f(x::X, y::Y) where {X, Y} if f === typeof(f24852_kernel) f2 = :f24852_kernel3 elseif f === typeof(f24852_kernel3) @@ -1868,8 +1847,20 @@ end @eval begin function f24852_late_expr(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_expr, Core.svec(:self, :f, :x, :y), - Core.svec(:X, :Y), false)) + $(_generated_stub(:f24852_gen_expr, Any[:self, :f, :x, :y], + Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), false)) + $(Expr(:meta, :generated_only)) + #= no body =# + end + function f24852_late_inflated(f, x::X, y::Y) where {X, Y} + $(_generated_stub(:f24852_gen_cinfo_inflated, Any[:self, :f, :x, :y], + Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), false)) + $(Expr(:meta, :generated_only)) + #= no body =# + end + function f24852_late_uninflated(f, x::X, y::Y) where {X, Y} + $(_generated_stub(:f24852_gen_cinfo_uninflated, Any[:self, :f, :x, :y], + Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), false)) $(Expr(:meta, :generated_only)) #= no body =# end @@ -1877,18 +1868,20 @@ end @eval begin function f24852_early_expr(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_expr, Core.svec(:self, :f, :x, :y), - Core.svec(:X, :Y), true)) + $(_generated_stub(:f24852_gen_expr, Any[:self, :f, :x, :y], + Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), true)) $(Expr(:meta, :generated_only)) #= no body =# end function f24852_early_inflated(f, x::X, y::Y) where {X, Y} - $(Expr(:meta, :generated, f24852_gen_cinfo_inflated)) + $(_generated_stub(:f24852_gen_cinfo_inflated, Any[:self, :f, :x, :y], + Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), true)) $(Expr(:meta, :generated_only)) #= no body =# end function f24852_early_uninflated(f, x::X, y::Y) where {X, Y} - $(Expr(:meta, :generated, f24852_gen_cinfo_uninflated)) + $(_generated_stub(:f24852_gen_cinfo_uninflated, Any[:self, :f, :x, :y], + Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), true)) $(Expr(:meta, :generated_only)) #= no body =# end @@ -1899,6 +1892,10 @@ result = f24852_kernel(x, y) @test result === f24852_late_expr(f24852_kernel, x, y) @test Base.return_types(f24852_late_expr, typeof((f24852_kernel, x, y))) == Any[Any] +@test result === f24852_late_uninflated(f24852_kernel, x, y) +@test Base.return_types(f24852_late_uninflated, typeof((f24852_kernel, x, y))) == Any[Any] +@test result === f24852_late_uninflated(f24852_kernel, x, y) +@test Base.return_types(f24852_late_uninflated, typeof((f24852_kernel, x, y))) == Any[Any] @test result === f24852_early_expr(f24852_kernel, x, y) @test Base.return_types(f24852_early_expr, typeof((f24852_kernel, x, y))) == Any[Any] @@ -1906,6 +1903,7 @@ result = f24852_kernel(x, y) @test Base.return_types(f24852_early_uninflated, typeof((f24852_kernel, x, y))) == Any[Any] @test result === @inferred f24852_early_inflated(f24852_kernel, x, y) @test Base.return_types(f24852_early_inflated, typeof((f24852_kernel, x, y))) == Any[Float64] + # TODO: test that `expand_early = true` + inflated `method_for_inference_limit_heuristics` # can be used to tighten up some inference result. diff --git a/test/compiler/validation.jl b/test/compiler/validation.jl index 5fd074fee73ae..c25aae71ab157 100644 --- a/test/compiler/validation.jl +++ b/test/compiler/validation.jl @@ -22,9 +22,10 @@ msig = Tuple{typeof(f22938),Int,Int,Int,Int} world = Base.get_world_counter() match = only(Base._methods_by_ftype(msig, -1, world)) mi = Core.Compiler.specialize_method(match) -c0 = Core.Compiler.retrieve_code_info(mi, world) +c0 = Core.Compiler.retrieve_code_info(mi) -@test isempty(Core.Compiler.validate_code(mi, c0)) +@test isempty(Core.Compiler.validate_code(mi)) +@test isempty(Core.Compiler.validate_code(c0)) @testset "INVALID_EXPR_HEAD" begin c = copy(c0) @@ -115,7 +116,7 @@ end @testset "SIGNATURE_NARGS_MISMATCH" begin old_sig = mi.def.sig mi.def.sig = Tuple{1,2} - errors = Core.Compiler.validate_code(mi, nothing) + errors = Core.Compiler.validate_code(mi) mi.def.sig = old_sig @test length(errors) == 1 @test errors[1].kind === Core.Compiler.SIGNATURE_NARGS_MISMATCH @@ -131,7 +132,7 @@ end @testset "SLOTNAMES_NARGS_MISMATCH" begin mi.def.nargs += 20 - errors = Core.Compiler.validate_code(mi, c0) + errors = Core.Compiler.validate_code(mi) mi.def.nargs -= 20 @test length(errors) == 2 @test count(e.kind === Core.Compiler.SLOTNAMES_NARGS_MISMATCH for e in errors) == 1 diff --git a/test/core.jl b/test/core.jl index 8507f2bdb8a01..8af7421ba7501 100644 --- a/test/core.jl +++ b/test/core.jl @@ -3883,8 +3883,7 @@ PossiblyInvalidUnion{T} = Union{T,Int} # issue #13007 call13007(::Type{Array{T,N}}) where {T,N} = 0 call13007(::Type{Array}) = 1 -@test Base._methods(call13007, Tuple{Type{x} where x<:Array}, 4, typemax(UInt)) === nothing -@test length(Base._methods(call13007, Tuple{Type{x} where x<:Array}, 4, Base.get_world_counter())) == 2 +@test length(Base._methods(call13007, Tuple{Type{x} where x<:Array}, 4, typemax(UInt))) == 2 # detecting cycles during type intersection, e.g. #1631 cycle_in_solve_tvar_constraints(::Type{Some{S}}, x::S) where {S} = 0 diff --git a/test/opaque_closure.jl b/test/opaque_closure.jl index d54d38403913a..b5d5f9ed522ac 100644 --- a/test/opaque_closure.jl +++ b/test/opaque_closure.jl @@ -173,23 +173,28 @@ mk_va_opaque() = @opaque (x...)->x @test repr(@opaque x->1) == "(::Any)::Any->◌" # Opaque closure in CodeInfo returned from generated functions -let ci = @code_lowered const_int() - global function mk_ocg(world::UInt, source, args...) - @nospecialize - cig = Meta.lower(@__MODULE__, Expr(:new_opaque_closure, Tuple{}, Any, Any, - Expr(:opaque_closure_method, nothing, 0, false, lno, ci))).args[1] - cig.slotnames = Symbol[Symbol("#self#")] - cig.slottypes = Any[Any] - cig.slotflags = UInt8[0x00] - @assert cig.min_world == UInt(1) - @assert cig.max_world == typemax(UInt) - return cig - end +function mk_ocg(args...) + ci = @code_lowered const_int() + cig = Meta.lower(@__MODULE__, Expr(:new_opaque_closure, Tuple{}, Any, Any, + Expr(:opaque_closure_method, nothing, 0, false, lno, ci))).args[1] + cig.slotnames = Symbol[Symbol("#self#")] + cig.slottypes = Any[Any] + cig.slotflags = UInt8[0x00] + cig end @eval function oc_trivial_generated() $(Expr(:meta, :generated_only)) - $(Expr(:meta, :generated, mk_ocg)) + $(Expr(:meta, + :generated, + Expr(:new, + Core.GeneratedFunctionStub, + :mk_ocg, + Any[:oc_trivial_generated], + Any[], + @__LINE__, + QuoteNode(Symbol(@__FILE__)), + true))) end @test isa(oc_trivial_generated(), Core.OpaqueClosure{Tuple{}, Any}) @test oc_trivial_generated()() == 1 diff --git a/test/reflection.jl b/test/reflection.jl index f31b77ad26aed..0c1081ba2c42f 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -648,7 +648,7 @@ let world = Core.Compiler.get_world_counter() match = Base._methods_by_ftype(T22979, -1, world)[1] instance = Core.Compiler.specialize_method(match) - cinfo_generated = Core.Compiler.get_staged(instance, world) + cinfo_generated = Core.Compiler.get_staged(instance) @test_throws ErrorException Base.uncompressed_ir(match.method) test_similar_codeinfo(code_lowered(f22979, typeof(x22979))[1], cinfo_generated) diff --git a/test/staged.jl b/test/staged.jl index 0fa8ecb182cff..4a7fa3d7f4c84 100644 --- a/test/staged.jl +++ b/test/staged.jl @@ -196,11 +196,12 @@ let gf_err2 return nothing end Expected = ErrorException("code reflection cannot be used from generated functions") - @test_throws Expected gf_err2(code_lowered) @test_throws Expected gf_err2(code_typed) @test_throws Expected gf_err2(code_llvm) @test_throws Expected gf_err2(code_native) - @test gf_err_ref[] == 88 + @test gf_err_ref[] == 66 + @test gf_err2(code_lowered) === nothing + @test gf_err_ref[] == 1077 end # issue #15043 @@ -245,18 +246,12 @@ f22440kernel(x::AbstractFloat) = x * x f22440kernel(::Type{T}) where {T} = one(T) f22440kernel(::Type{T}) where {T<:AbstractFloat} = zero(T) -function f22440_gen(world::UInt, source, _, y) - match = only(Base._methods_by_ftype(Tuple{typeof(f22440kernel),y}, -1, world)) +@generated function f22440(y) + match = Base._methods_by_ftype(Tuple{typeof(f22440kernel),y}, -1, typemax(UInt))[1] code_info = Base.uncompressed_ir(match.method) Meta.partially_inline!(code_info.code, Any[], match.spec_types, Any[match.sparams...], 0, 0, :propagate) - # TODO: this is mandatory: code_info.min_world = max(code_info.min_world, min_world[]) - # TODO: this is mandatory: code_info.max_world = min(code_info.max_world, max_world[]) return code_info end -@eval function f22440(y) - $(Expr(:meta, :generated, f22440_gen)) - $(Expr(:meta, :generated_only)) -end @test f22440(Int) === f22440kernel(Int) @test f22440(Float64) === f22440kernel(Float64) @@ -314,33 +309,26 @@ end # https://github.com/JuliaDebug/CassetteOverlay.jl/issues/12 # generated function with varargs and unfortunately placed unused slot @generated function f_vararg_generated(args...) - local unusedslot4 - local unusedslot5 - local unusedslot6 :($args) end g_vararg_generated() = f_vararg_generated((;), (;), Base.inferencebarrier((;))) let tup = g_vararg_generated() @test all(==(typeof((;))), tup) - # This is just to make sure that the test is actually testing what we want: - # the test only works if there is an unused that matches the position of - # the inferencebarrier argument above (N.B. the generator function itself + # This is just to make sure that the test is actually testing what we want - + # the test only works if there's an unused that matches the position of the + # inferencebarrier argument above (N.B. the generator function itself # shifts everything over by 1) - @test_broken only(code_lowered(only(methods(f_vararg_generated)).generator.gen)).slotflags[5] == 0x00 + @test only(code_lowered(only(methods(f_vararg_generated)).generator.gen)).slotflags[5] == UInt8(0x00) end # respect a given linetable in code generation # https://github.com/JuliaLang/julia/pull/47750 -let world = Base.get_world_counter() - match = Base._which(Tuple{typeof(sin), Int}; world) +let match = Base._which(Tuple{typeof(sin),Int}) mi = Core.Compiler.specialize_method(match) - lwr = Core.Compiler.retrieve_code_info(mi, world) - @test all(lin->lin.method === :sin, lwr.linetable) - @eval function sin_generated(a) - $(Expr(:meta, :generated, Returns(lwr))) - $(Expr(:meta, :generated_only)) - end + lwr = Core.Compiler.retrieve_code_info(mi) + @test all(lin->lin.method===:sin, lwr.linetable) + @generated sin_generated(a) = lwr src = only(code_lowered(sin_generated, (Int,))) - @test all(lin->lin.method === :sin, src.linetable) + @test all(lin->lin.method===:sin, src.linetable) @test sin_generated(42) == sin(42) end diff --git a/test/syntax.jl b/test/syntax.jl index e60bbd81a04ab..756af45e6b3c7 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3053,6 +3053,9 @@ end end # issue 25678 +@generated f25678(x::T) where {T} = code_lowered(sin, Tuple{x})[] +@test f25678(pi/6) === sin(pi/6) + @generated g25678(x) = return :x @test g25678(7) === 7 From a1b546ad04612272787d9ad70444ebe2dc58ac9f Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Thu, 23 Feb 2023 02:31:12 -0800 Subject: [PATCH 166/775] [LibCURL_jll] Add major extension to SONAME (#48758) We should be consistent in the SONAMEs we use when loading, so that we are properly declaring the ABI we support when loading `libcurl`. --- stdlib/LibCURL_jll/src/LibCURL_jll.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/LibCURL_jll/src/LibCURL_jll.jl b/stdlib/LibCURL_jll/src/LibCURL_jll.jl index 0911e68678657..9d019bb784584 100644 --- a/stdlib/LibCURL_jll/src/LibCURL_jll.jl +++ b/stdlib/LibCURL_jll/src/LibCURL_jll.jl @@ -23,7 +23,7 @@ if Sys.iswindows() elseif Sys.isapple() const libcurl = "@rpath/libcurl.4.dylib" else - const libcurl = "libcurl.so" + const libcurl = "libcurl.so.4" end function __init__() From 1543cdd5ffc734cae640e45b9bb6ada73dd81c02 Mon Sep 17 00:00:00 2001 From: matthias314 <56549971+matthias314@users.noreply.github.com> Date: Fri, 24 Feb 2023 03:08:42 -0500 Subject: [PATCH 167/775] Faster iteration over non-offset `AbstractVector` (#48720) This change simplifies the boundscheck in loop as LLVM would lift the const subtraction. Simd block would be generated in more cases. Co-authored-by: N5N3 <2642243996@qq.com> --- base/abstractarray.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index a1d910e38e169..f79872818de31 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -767,6 +767,8 @@ false checkindex(::Type{Bool}, inds::AbstractUnitRange, i) = throw(ArgumentError("unable to check bounds for indices of type $(typeof(i))")) checkindex(::Type{Bool}, inds::AbstractUnitRange, i::Real) = (first(inds) <= i) & (i <= last(inds)) +checkindex(::Type{Bool}, inds::IdentityUnitRange, i::Real) = checkindex(Bool, inds.indices, i) +checkindex(::Type{Bool}, inds::OneTo{T}, i::T) where {T<:BitInteger} = unsigned(i - one(i)) < unsigned(last(inds)) checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Colon) = true checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Slice) = true function checkindex(::Type{Bool}, inds::AbstractUnitRange, r::AbstractRange) From 16801fbac1240f7fe7fd4b5390885f25d55cce4c Mon Sep 17 00:00:00 2001 From: Maurizio Tomasi Date: Fri, 24 Feb 2023 09:10:47 +0100 Subject: [PATCH 168/775] Mention "make debug" in Windows build instructions (#48771) --- doc/src/devdocs/build/windows.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/src/devdocs/build/windows.md b/doc/src/devdocs/build/windows.md index 4f7f40a030488..7192bb8a7a544 100644 --- a/doc/src/devdocs/build/windows.md +++ b/doc/src/devdocs/build/windows.md @@ -90,7 +90,8 @@ MinGW-w64 compilers available through Cygwin's package manager. 3. Start the build ```sh - make -j 4 # Adjust the number of threads (4) to match your build environment. + make -j 4 # Adjust the number of threads (4) to match your build environment. + make -j 4 debug # This builds julia-debug.exe ``` From b5482c82d486aaa68939871eb1d1bc71bb421096 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 24 Feb 2023 22:49:19 +0900 Subject: [PATCH 169/775] tidy up `InferenceState` definition (#48775) --- base/compiler/abstractinterpretation.jl | 12 ++++++------ base/compiler/inferencestate.jl | 6 ++---- base/compiler/typeinfer.jl | 12 ++++++------ 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 0d0280e40e817..7eb6c5f991306 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -30,8 +30,8 @@ function get_max_methods(@nospecialize(f), mod::Module, interp::AbstractInterpre return get_max_methods(mod, interp) end -function should_infer_this_call(sv::InferenceState) - if sv.params.unoptimize_throw_blocks +function should_infer_this_call(interp::AbstractInterpreter, sv::InferenceState) + if InferenceParams(interp).unoptimize_throw_blocks # Disable inference of calls in throw blocks, since we're unlikely to # need their types. There is one exception however: If up until now, the # function has not seen any side effects, we would like to make sure there @@ -52,7 +52,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), arginfo::ArgInfo, si::StmtInfo, @nospecialize(atype), sv::InferenceState, max_methods::Int) ⊑ₚ = ⊑(ipo_lattice(interp)) - if !should_infer_this_call(sv) + if !should_infer_this_call(interp, sv) add_remark!(interp, sv, "Skipped call in throw block") nonoverlayed = false if isoverlayed(method_table(interp)) && is_nonoverlayed(sv.ipo_effects) @@ -2404,13 +2404,13 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp elseif isa(sym, Symbol) if isdefined(sv.mod, sym) t = Const(true) - elseif sv.params.assume_bindings_static + elseif InferenceParams(interp).assume_bindings_static t = Const(false) end elseif isa(sym, GlobalRef) if isdefined(sym.mod, sym.name) t = Const(true) - elseif sv.params.assume_bindings_static + elseif InferenceParams(interp).assume_bindings_static t = Const(false) end elseif isexpr(sym, :static_parameter) @@ -2551,7 +2551,7 @@ function abstract_eval_globalref(interp::AbstractInterpreter, g::GlobalRef, fram end elseif isdefined_globalref(g) nothrow = true - elseif isa(frame, InferenceState) && frame.params.assume_bindings_static + elseif InferenceParams(interp).assume_bindings_static consistent = inaccessiblememonly = ALWAYS_TRUE rt = Union{} end diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 7ff740a23a540..44be8b199d9c3 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -116,7 +116,6 @@ mutable struct InferenceState ipo_effects::Effects #= flags =# - params::InferenceParams # Whether to restrict inference of abstract call sites to avoid excessive work # Set by default for toplevel frame. restrict_abstract_call_sites::Bool @@ -177,7 +176,6 @@ mutable struct InferenceState ipo_effects = Effects(ipo_effects; effect_free = ALWAYS_FALSE) end - params = InferenceParams(interp) restrict_abstract_call_sites = isa(linfo.def, Module) @assert cache === :no || cache === :local || cache === :global cached = cache === :global @@ -187,11 +185,11 @@ mutable struct InferenceState currbb, currpc, ip, handler_at, ssavalue_uses, bb_vartables, ssavaluetypes, stmt_edges, stmt_info, pclimitations, limitations, cycle_backedges, callers_in_cycle, dont_work_on_me, parent, inferred, result, valid_worlds, bestguess, ipo_effects, - params, restrict_abstract_call_sites, cached, insert_coverage, + restrict_abstract_call_sites, cached, insert_coverage, interp) # some more setups - params.unoptimize_throw_blocks && mark_throw_blocks!(src, handler_at) + InferenceParams(interp).unoptimize_throw_blocks && mark_throw_blocks!(src, handler_at) result.result = frame cache !== :no && push!(get_inference_cache(interp), result) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index f7723a968da3e..35a2e25e62d11 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -626,7 +626,7 @@ function record_bestguess!(sv::InferenceState) return nothing end -function annotate_slot_load!(undefs::Vector{Bool}, idx::Int, sv::InferenceState, @nospecialize x) +function annotate_slot_load!(interp::AbstractInterpreter, undefs::Vector{Bool}, idx::Int, sv::InferenceState, @nospecialize x) if isa(x, SlotNumber) id = slot_id(x) pc = find_dominating_assignment(id, idx, sv) @@ -641,7 +641,7 @@ function annotate_slot_load!(undefs::Vector{Bool}, idx::Int, sv::InferenceState, @assert typ !== NOT_FOUND "active slot in unreached region" end # add type annotations where needed - if !⊑(typeinf_lattice(sv.interp), sv.slottypes[id], typ) + if !⊑(typeinf_lattice(interp), sv.slottypes[id], typ) return TypedSlot(id, typ) end return x @@ -655,13 +655,13 @@ function annotate_slot_load!(undefs::Vector{Bool}, idx::Int, sv::InferenceState, i0 = 2 end for i = i0:length(x.args) - x.args[i] = annotate_slot_load!(undefs, idx, sv, x.args[i]) + x.args[i] = annotate_slot_load!(interp, undefs, idx, sv, x.args[i]) end return x elseif isa(x, ReturnNode) && isdefined(x, :val) - return ReturnNode(annotate_slot_load!(undefs, idx, sv, x.val)) + return ReturnNode(annotate_slot_load!(interp, undefs, idx, sv, x.val)) elseif isa(x, GotoIfNot) - return GotoIfNot(annotate_slot_load!(undefs, idx, sv, x.cond), x.dest) + return GotoIfNot(annotate_slot_load!(interp, undefs, idx, sv, x.cond), x.dest) end return x end @@ -734,7 +734,7 @@ function type_annotate!(interp::AbstractInterpreter, sv::InferenceState, run_opt end end end - body[i] = annotate_slot_load!(undefs, i, sv, expr) # 1&2 + body[i] = annotate_slot_load!(interp, undefs, i, sv, expr) # 1&2 ssavaluetypes[i] = widenslotwrapper(ssavaluetypes[i]) # 4 else # i.e. any runtime execution will never reach this statement any_unreachable = true From 249c742f8d0f54244850074788909a270e339238 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Fri, 24 Feb 2023 09:10:44 -0500 Subject: [PATCH 170/775] cross-reference names from varinfo docstring --- stdlib/InteractiveUtils/src/InteractiveUtils.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stdlib/InteractiveUtils/src/InteractiveUtils.jl b/stdlib/InteractiveUtils/src/InteractiveUtils.jl index 1f0b05c29c3b5..87895aafa2ed9 100644 --- a/stdlib/InteractiveUtils/src/InteractiveUtils.jl +++ b/stdlib/InteractiveUtils/src/InteractiveUtils.jl @@ -33,6 +33,9 @@ The memory consumption estimate is an approximate lower bound on the size of the - `recursive` : recursively include objects in sub-modules, observing the same settings in each. - `sortby` : the column to sort results by. Options are `:name` (default), `:size`, and `:summary`. - `minsize` : only includes objects with size at least `minsize` bytes. Defaults to `0`. + +The output of `varinfo` is intended for display purposes only. See also [`names`](@ref) to get an array of symbols defined in +a module, which is suitable for more general manipulations. """ function varinfo(m::Module=Base.active_module(), pattern::Regex=r""; all::Bool = false, imported::Bool = false, sortby::Symbol = :name, recursive::Bool = false, minsize::Int=0) sortby in (:name, :size, :summary) || throw(ArgumentError("Unrecognized `sortby` value `:$sortby`. Possible options are `:name`, `:size`, and `:summary`")) From f0eadd076f2728797cf5be88b61cda1d9f1dd515 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Fri, 24 Feb 2023 11:50:30 -0500 Subject: [PATCH 171/775] make `MultiplicativeInverse<:Number` (#48708) * make `MultiplicativeInverse<:Number` This makes operations like `divrem.(3, Base.MultiplicativeInverses.multiplicativeinverse(3))` work. --- base/multinverses.jl | 2 +- test/numbers.jl | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/base/multinverses.jl b/base/multinverses.jl index 4342a9a5f5cf7..1d2eab6e78501 100644 --- a/base/multinverses.jl +++ b/base/multinverses.jl @@ -14,7 +14,7 @@ unsigned(::Type{Int64}) = UInt64 unsigned(::Type{Int128}) = UInt128 unsigned(::Type{T}) where {T<:Unsigned} = T -abstract type MultiplicativeInverse{T} end +abstract type MultiplicativeInverse{T} <: Number end # Computes integer division by a constant using multiply, add, and bitshift. diff --git a/test/numbers.jl b/test/numbers.jl index 351a244554732..1505725a7f4cf 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -2493,6 +2493,8 @@ Base.abs(x::TestNumber) = TestNumber(abs(x.inner)) @test_throws ArgumentError Base.multiplicativeinverse(0) testmi(map(UInt32, 0:1000), map(UInt32, 1:100)) testmi(typemax(UInt32)-UInt32(1000):typemax(UInt32), map(UInt32, 1:100)) + # test broadcasting works. + @test div.(3, Base.multiplicativeinverse(3)) == 1 end @testset "ndims/indices/size/length" begin @test ndims(1) == 0 From 0aab8d8002f62d418e5c315b36a0b19bca4af1ab Mon Sep 17 00:00:00 2001 From: Francois-Xavier Coudert Date: Sat, 25 Feb 2023 14:24:43 +0100 Subject: [PATCH 172/775] Remove old config.sub hack --- deps/mpfr.mk | 1 - deps/patches/config.sub | 1851 --------------------------------------- 2 files changed, 1852 deletions(-) delete mode 100755 deps/patches/config.sub diff --git a/deps/mpfr.mk b/deps/mpfr.mk index dc10764002bd4..5a0605ba6b601 100644 --- a/deps/mpfr.mk +++ b/deps/mpfr.mk @@ -30,7 +30,6 @@ $(SRCCACHE)/mpfr-$(MPFR_VER).tar.bz2: | $(SRCCACHE) $(SRCCACHE)/mpfr-$(MPFR_VER)/source-extracted: $(SRCCACHE)/mpfr-$(MPFR_VER).tar.bz2 $(JLCHECKSUM) $< cd $(dir $<) && $(TAR) -jxf $< - cp $(SRCDIR)/patches/config.sub $(SRCCACHE)/mpfr-$(MPFR_VER)/config.sub touch -c $(SRCCACHE)/mpfr-$(MPFR_VER)/configure # old target echo 1 > $@ diff --git a/deps/patches/config.sub b/deps/patches/config.sub deleted file mode 100755 index 3d9a8dc3d5a76..0000000000000 --- a/deps/patches/config.sub +++ /dev/null @@ -1,1851 +0,0 @@ -#! /bin/sh -# Configuration validation subroutine script. -# Copyright 1992-2020 Free Software Foundation, Inc. - -timestamp='2020-07-10' - -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that -# program. This Exception is an additional permission under section 7 -# of the GNU General Public License, version 3 ("GPLv3"). - - -# Please send patches to . -# -# Configuration subroutine to validate and canonicalize a configuration type. -# Supply the specified configuration type as an argument. -# If it is invalid, we print an error message on stderr and exit with code 1. -# Otherwise, we print the canonical config type on stdout and succeed. - -# You can get the latest version of this script from: -# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub - -# This file is supposed to be the same for all GNU packages -# and recognize all the CPU types, system types and aliases -# that are meaningful with *any* GNU software. -# Each package is responsible for reporting which valid configurations -# it does not support. The user should be able to distinguish -# a failure to support a valid configuration from a meaningless -# configuration. - -# The goal of this file is to map all the various variations of a given -# machine specification into a single specification in the form: -# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM -# or in some cases, the newer four-part form: -# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM -# It is wrong to echo any other type of specification. - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS - -Canonicalize a configuration name. - -Options: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.sub ($timestamp) - -Copyright 1992-2020 Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" >&2 - exit 1 ;; - - *local*) - # First pass through any local machine types. - echo "$1" - exit ;; - - * ) - break ;; - esac -done - -case $# in - 0) echo "$me: missing argument$help" >&2 - exit 1;; - 1) ;; - *) echo "$me: too many arguments$help" >&2 - exit 1;; -esac - -# Split fields of configuration type -# shellcheck disable=SC2162 -IFS="-" read field1 field2 field3 field4 <&2 - exit 1 - ;; - *-*-*-*) - basic_machine=$field1-$field2 - basic_os=$field3-$field4 - ;; - *-*-*) - # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two - # parts - maybe_os=$field2-$field3 - case $maybe_os in - nto-qnx* | linux-* | uclinux-uclibc* \ - | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ - | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ - | storm-chaos* | os2-emx* | rtmk-nova*) - basic_machine=$field1 - basic_os=$maybe_os - ;; - android-linux) - basic_machine=$field1-unknown - basic_os=linux-android - ;; - *) - basic_machine=$field1-$field2 - basic_os=$field3 - ;; - esac - ;; - *-*) - # A lone config we happen to match not fitting any pattern - case $field1-$field2 in - decstation-3100) - basic_machine=mips-dec - basic_os= - ;; - *-*) - # Second component is usually, but not always the OS - case $field2 in - # Prevent following clause from handling this valid os - sun*os*) - basic_machine=$field1 - basic_os=$field2 - ;; - # Manufacturers - dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ - | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ - | unicom* | ibm* | next | hp | isi* | apollo | altos* \ - | convergent* | ncr* | news | 32* | 3600* | 3100* \ - | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ - | ultra | tti* | harris | dolphin | highlevel | gould \ - | cbm | ns | masscomp | apple | axis | knuth | cray \ - | microblaze* | sim | cisco \ - | oki | wec | wrs | winbond) - basic_machine=$field1-$field2 - basic_os= - ;; - *) - basic_machine=$field1 - basic_os=$field2 - ;; - esac - ;; - esac - ;; - *) - # Convert single-component short-hands not valid as part of - # multi-component configurations. - case $field1 in - 386bsd) - basic_machine=i386-pc - basic_os=bsd - ;; - a29khif) - basic_machine=a29k-amd - basic_os=udi - ;; - adobe68k) - basic_machine=m68010-adobe - basic_os=scout - ;; - alliant) - basic_machine=fx80-alliant - basic_os= - ;; - altos | altos3068) - basic_machine=m68k-altos - basic_os= - ;; - am29k) - basic_machine=a29k-none - basic_os=bsd - ;; - amdahl) - basic_machine=580-amdahl - basic_os=sysv - ;; - amiga) - basic_machine=m68k-unknown - basic_os= - ;; - amigaos | amigados) - basic_machine=m68k-unknown - basic_os=amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - basic_os=sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - basic_os=sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - basic_os=bsd - ;; - aros) - basic_machine=i386-pc - basic_os=aros - ;; - aux) - basic_machine=m68k-apple - basic_os=aux - ;; - balance) - basic_machine=ns32k-sequent - basic_os=dynix - ;; - blackfin) - basic_machine=bfin-unknown - basic_os=linux - ;; - cegcc) - basic_machine=arm-unknown - basic_os=cegcc - ;; - convex-c1) - basic_machine=c1-convex - basic_os=bsd - ;; - convex-c2) - basic_machine=c2-convex - basic_os=bsd - ;; - convex-c32) - basic_machine=c32-convex - basic_os=bsd - ;; - convex-c34) - basic_machine=c34-convex - basic_os=bsd - ;; - convex-c38) - basic_machine=c38-convex - basic_os=bsd - ;; - cray) - basic_machine=j90-cray - basic_os=unicos - ;; - crds | unos) - basic_machine=m68k-crds - basic_os= - ;; - da30) - basic_machine=m68k-da30 - basic_os= - ;; - decstation | pmax | pmin | dec3100 | decstatn) - basic_machine=mips-dec - basic_os= - ;; - delta88) - basic_machine=m88k-motorola - basic_os=sysv3 - ;; - dicos) - basic_machine=i686-pc - basic_os=dicos - ;; - djgpp) - basic_machine=i586-pc - basic_os=msdosdjgpp - ;; - ebmon29k) - basic_machine=a29k-amd - basic_os=ebmon - ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - basic_os=ose - ;; - gmicro) - basic_machine=tron-gmicro - basic_os=sysv - ;; - go32) - basic_machine=i386-pc - basic_os=go32 - ;; - h8300hms) - basic_machine=h8300-hitachi - basic_os=hms - ;; - h8300xray) - basic_machine=h8300-hitachi - basic_os=xray - ;; - h8500hms) - basic_machine=h8500-hitachi - basic_os=hms - ;; - harris) - basic_machine=m88k-harris - basic_os=sysv3 - ;; - hp300 | hp300hpux) - basic_machine=m68k-hp - basic_os=hpux - ;; - hp300bsd) - basic_machine=m68k-hp - basic_os=bsd - ;; - hppaosf) - basic_machine=hppa1.1-hp - basic_os=osf - ;; - hppro) - basic_machine=hppa1.1-hp - basic_os=proelf - ;; - i386mach) - basic_machine=i386-mach - basic_os=mach - ;; - isi68 | isi) - basic_machine=m68k-isi - basic_os=sysv - ;; - m68knommu) - basic_machine=m68k-unknown - basic_os=linux - ;; - magnum | m3230) - basic_machine=mips-mips - basic_os=sysv - ;; - merlin) - basic_machine=ns32k-utek - basic_os=sysv - ;; - mingw64) - basic_machine=x86_64-pc - basic_os=mingw64 - ;; - mingw32) - basic_machine=i686-pc - basic_os=mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - basic_os=mingw32ce - ;; - monitor) - basic_machine=m68k-rom68k - basic_os=coff - ;; - morphos) - basic_machine=powerpc-unknown - basic_os=morphos - ;; - moxiebox) - basic_machine=moxie-unknown - basic_os=moxiebox - ;; - msdos) - basic_machine=i386-pc - basic_os=msdos - ;; - msys) - basic_machine=i686-pc - basic_os=msys - ;; - mvs) - basic_machine=i370-ibm - basic_os=mvs - ;; - nacl) - basic_machine=le32-unknown - basic_os=nacl - ;; - ncr3000) - basic_machine=i486-ncr - basic_os=sysv4 - ;; - netbsd386) - basic_machine=i386-pc - basic_os=netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - basic_os=linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - basic_os=newsos - ;; - news1000) - basic_machine=m68030-sony - basic_os=newsos - ;; - necv70) - basic_machine=v70-nec - basic_os=sysv - ;; - nh3000) - basic_machine=m68k-harris - basic_os=cxux - ;; - nh[45]000) - basic_machine=m88k-harris - basic_os=cxux - ;; - nindy960) - basic_machine=i960-intel - basic_os=nindy - ;; - mon960) - basic_machine=i960-intel - basic_os=mon960 - ;; - nonstopux) - basic_machine=mips-compaq - basic_os=nonstopux - ;; - os400) - basic_machine=powerpc-ibm - basic_os=os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - basic_os=ose - ;; - os68k) - basic_machine=m68k-none - basic_os=os68k - ;; - paragon) - basic_machine=i860-intel - basic_os=osf - ;; - parisc) - basic_machine=hppa-unknown - basic_os=linux - ;; - psp) - basic_machine=mipsallegrexel-sony - basic_os=psp - ;; - pw32) - basic_machine=i586-unknown - basic_os=pw32 - ;; - rdos | rdos64) - basic_machine=x86_64-pc - basic_os=rdos - ;; - rdos32) - basic_machine=i386-pc - basic_os=rdos - ;; - rom68k) - basic_machine=m68k-rom68k - basic_os=coff - ;; - sa29200) - basic_machine=a29k-amd - basic_os=udi - ;; - sei) - basic_machine=mips-sei - basic_os=seiux - ;; - sequent) - basic_machine=i386-sequent - basic_os= - ;; - sps7) - basic_machine=m68k-bull - basic_os=sysv2 - ;; - st2000) - basic_machine=m68k-tandem - basic_os= - ;; - stratus) - basic_machine=i860-stratus - basic_os=sysv4 - ;; - sun2) - basic_machine=m68000-sun - basic_os= - ;; - sun2os3) - basic_machine=m68000-sun - basic_os=sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - basic_os=sunos4 - ;; - sun3) - basic_machine=m68k-sun - basic_os= - ;; - sun3os3) - basic_machine=m68k-sun - basic_os=sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - basic_os=sunos4 - ;; - sun4) - basic_machine=sparc-sun - basic_os= - ;; - sun4os3) - basic_machine=sparc-sun - basic_os=sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - basic_os=sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - basic_os=solaris2 - ;; - sun386 | sun386i | roadrunner) - basic_machine=i386-sun - basic_os= - ;; - sv1) - basic_machine=sv1-cray - basic_os=unicos - ;; - symmetry) - basic_machine=i386-sequent - basic_os=dynix - ;; - t3e) - basic_machine=alphaev5-cray - basic_os=unicos - ;; - t90) - basic_machine=t90-cray - basic_os=unicos - ;; - toad1) - basic_machine=pdp10-xkl - basic_os=tops20 - ;; - tpf) - basic_machine=s390x-ibm - basic_os=tpf - ;; - udi29k) - basic_machine=a29k-amd - basic_os=udi - ;; - ultra3) - basic_machine=a29k-nyu - basic_os=sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - basic_os=none - ;; - vaxv) - basic_machine=vax-dec - basic_os=sysv - ;; - vms) - basic_machine=vax-dec - basic_os=vms - ;; - vsta) - basic_machine=i386-pc - basic_os=vsta - ;; - vxworks960) - basic_machine=i960-wrs - basic_os=vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - basic_os=vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - basic_os=vxworks - ;; - xbox) - basic_machine=i686-pc - basic_os=mingw32 - ;; - ymp) - basic_machine=ymp-cray - basic_os=unicos - ;; - *) - basic_machine=$1 - basic_os= - ;; - esac - ;; -esac - -# Decode 1-component or ad-hoc basic machines -case $basic_machine in - # Here we handle the default manufacturer of certain CPU types. It is in - # some cases the only manufacturer, in others, it is the most popular. - w89k) - cpu=hppa1.1 - vendor=winbond - ;; - op50n) - cpu=hppa1.1 - vendor=oki - ;; - op60c) - cpu=hppa1.1 - vendor=oki - ;; - ibm*) - cpu=i370 - vendor=ibm - ;; - orion105) - cpu=clipper - vendor=highlevel - ;; - mac | mpw | mac-mpw) - cpu=m68k - vendor=apple - ;; - pmac | pmac-mpw) - cpu=powerpc - vendor=apple - ;; - - # Recognize the various machine names and aliases which stand - # for a CPU type and a company and sometimes even an OS. - 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - cpu=m68000 - vendor=att - ;; - 3b*) - cpu=we32k - vendor=att - ;; - bluegene*) - cpu=powerpc - vendor=ibm - basic_os=cnk - ;; - decsystem10* | dec10*) - cpu=pdp10 - vendor=dec - basic_os=tops10 - ;; - decsystem20* | dec20*) - cpu=pdp10 - vendor=dec - basic_os=tops20 - ;; - delta | 3300 | motorola-3300 | motorola-delta \ - | 3300-motorola | delta-motorola) - cpu=m68k - vendor=motorola - ;; - dpx2*) - cpu=m68k - vendor=bull - basic_os=sysv3 - ;; - encore | umax | mmax) - cpu=ns32k - vendor=encore - ;; - elxsi) - cpu=elxsi - vendor=elxsi - basic_os=${basic_os:-bsd} - ;; - fx2800) - cpu=i860 - vendor=alliant - ;; - genix) - cpu=ns32k - vendor=ns - ;; - h3050r* | hiux*) - cpu=hppa1.1 - vendor=hitachi - basic_os=hiuxwe2 - ;; - hp3k9[0-9][0-9] | hp9[0-9][0-9]) - cpu=hppa1.0 - vendor=hp - ;; - hp9k2[0-9][0-9] | hp9k31[0-9]) - cpu=m68000 - vendor=hp - ;; - hp9k3[2-9][0-9]) - cpu=m68k - vendor=hp - ;; - hp9k6[0-9][0-9] | hp6[0-9][0-9]) - cpu=hppa1.0 - vendor=hp - ;; - hp9k7[0-79][0-9] | hp7[0-79][0-9]) - cpu=hppa1.1 - vendor=hp - ;; - hp9k78[0-9] | hp78[0-9]) - # FIXME: really hppa2.0-hp - cpu=hppa1.1 - vendor=hp - ;; - hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) - # FIXME: really hppa2.0-hp - cpu=hppa1.1 - vendor=hp - ;; - hp9k8[0-9][13679] | hp8[0-9][13679]) - cpu=hppa1.1 - vendor=hp - ;; - hp9k8[0-9][0-9] | hp8[0-9][0-9]) - cpu=hppa1.0 - vendor=hp - ;; - i*86v32) - cpu=`echo "$1" | sed -e 's/86.*/86/'` - vendor=pc - basic_os=sysv32 - ;; - i*86v4*) - cpu=`echo "$1" | sed -e 's/86.*/86/'` - vendor=pc - basic_os=sysv4 - ;; - i*86v) - cpu=`echo "$1" | sed -e 's/86.*/86/'` - vendor=pc - basic_os=sysv - ;; - i*86sol2) - cpu=`echo "$1" | sed -e 's/86.*/86/'` - vendor=pc - basic_os=solaris2 - ;; - j90 | j90-cray) - cpu=j90 - vendor=cray - basic_os=${basic_os:-unicos} - ;; - iris | iris4d) - cpu=mips - vendor=sgi - case $basic_os in - irix*) - ;; - *) - basic_os=irix4 - ;; - esac - ;; - miniframe) - cpu=m68000 - vendor=convergent - ;; - *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) - cpu=m68k - vendor=atari - basic_os=mint - ;; - news-3600 | risc-news) - cpu=mips - vendor=sony - basic_os=newsos - ;; - next | m*-next) - cpu=m68k - vendor=next - case $basic_os in - openstep*) - ;; - nextstep*) - ;; - ns2*) - basic_os=nextstep2 - ;; - *) - basic_os=nextstep3 - ;; - esac - ;; - np1) - cpu=np1 - vendor=gould - ;; - op50n-* | op60c-*) - cpu=hppa1.1 - vendor=oki - basic_os=proelf - ;; - pa-hitachi) - cpu=hppa1.1 - vendor=hitachi - basic_os=hiuxwe2 - ;; - pbd) - cpu=sparc - vendor=tti - ;; - pbb) - cpu=m68k - vendor=tti - ;; - pc532) - cpu=ns32k - vendor=pc532 - ;; - pn) - cpu=pn - vendor=gould - ;; - power) - cpu=power - vendor=ibm - ;; - ps2) - cpu=i386 - vendor=ibm - ;; - rm[46]00) - cpu=mips - vendor=siemens - ;; - rtpc | rtpc-*) - cpu=romp - vendor=ibm - ;; - sde) - cpu=mipsisa32 - vendor=sde - basic_os=${basic_os:-elf} - ;; - simso-wrs) - cpu=sparclite - vendor=wrs - basic_os=vxworks - ;; - tower | tower-32) - cpu=m68k - vendor=ncr - ;; - vpp*|vx|vx-*) - cpu=f301 - vendor=fujitsu - ;; - w65) - cpu=w65 - vendor=wdc - ;; - w89k-*) - cpu=hppa1.1 - vendor=winbond - basic_os=proelf - ;; - none) - cpu=none - vendor=none - ;; - leon|leon[3-9]) - cpu=sparc - vendor=$basic_machine - ;; - leon-*|leon[3-9]-*) - cpu=sparc - vendor=`echo "$basic_machine" | sed 's/-.*//'` - ;; - - *-*) - # shellcheck disable=SC2162 - IFS="-" read cpu vendor <&2 - exit 1 - ;; - esac - ;; -esac - -# Here we canonicalize certain aliases for manufacturers. -case $vendor in - digital*) - vendor=dec - ;; - commodore*) - vendor=cbm - ;; - *) - ;; -esac - -# Decode manufacturer-specific aliases for certain operating systems. - -if [ x$basic_os != x ] -then - -# First recognize some ad-hoc caes, or perhaps split kernel-os, or else just -# set os. -case $basic_os in - gnu/linux*) - kernel=linux - os=`echo $basic_os | sed -e 's|gnu/linux|gnu|'` - ;; - nto-qnx*) - kernel=nto - os=`echo $basic_os | sed -e 's|nto-qnx|qnx|'` - ;; - *-*) - # shellcheck disable=SC2162 - IFS="-" read kernel os <&2 - exit 1 - ;; -esac - -# As a final step for OS-related things, validate the OS-kernel combination -# (given a valid OS), if there is a kernel. -case $kernel-$os in - linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* | linux-musl* | linux-uclibc* ) - ;; - -dietlibc* | -newlib* | -musl* | -uclibc* ) - # These are just libc implementations, not actual OSes, and thus - # require a kernel. - echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 - exit 1 - ;; - kfreebsd*-gnu* | kopensolaris*-gnu*) - ;; - nto-qnx*) - ;; - *-eabi* | *-gnueabi*) - ;; - -*) - # Blank kernel with real OS is always fine. - ;; - *-*) - echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 - exit 1 - ;; -esac - -# Here we handle the case where we know the os, and the CPU type, but not the -# manufacturer. We pick the logical manufacturer. -case $vendor in - unknown) - case $cpu-$os in - *-riscix*) - vendor=acorn - ;; - *-sunos*) - vendor=sun - ;; - *-cnk* | *-aix*) - vendor=ibm - ;; - *-beos*) - vendor=be - ;; - *-hpux*) - vendor=hp - ;; - *-mpeix*) - vendor=hp - ;; - *-hiux*) - vendor=hitachi - ;; - *-unos*) - vendor=crds - ;; - *-dgux*) - vendor=dg - ;; - *-luna*) - vendor=omron - ;; - *-genix*) - vendor=ns - ;; - *-clix*) - vendor=intergraph - ;; - *-mvs* | *-opened*) - vendor=ibm - ;; - *-os400*) - vendor=ibm - ;; - s390-* | s390x-*) - vendor=ibm - ;; - *-ptx*) - vendor=sequent - ;; - *-tpf*) - vendor=ibm - ;; - *-vxsim* | *-vxworks* | *-windiss*) - vendor=wrs - ;; - *-aux*) - vendor=apple - ;; - *-hms*) - vendor=hitachi - ;; - *-mpw* | *-macos*) - vendor=apple - ;; - *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) - vendor=atari - ;; - *-vos*) - vendor=stratus - ;; - esac - ;; -esac - -echo "$cpu-$vendor-${kernel:+$kernel-}$os" -exit - -# Local variables: -# eval: (add-hook 'before-save-hook 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: From 219ef2184723ba0c2bb89af96d79c368c4a0f673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Sat, 25 Feb 2023 11:20:42 +0000 Subject: [PATCH 173/775] [Make.inc] Define new variable `PATCHELF_SET_RPATH_ARG` --- Make.inc | 4 ++++ Makefile | 14 +++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Make.inc b/Make.inc index b6172c2eb3e72..65a38fc927bc5 100644 --- a/Make.inc +++ b/Make.inc @@ -1018,6 +1018,10 @@ PATCHELF := patchelf else PATCHELF := $(build_depsbindir)/patchelf endif +# In the standard build system we want to patch files with `--set-rpath`, but downstream +# packagers like Spack may want to use `--add-rpath` instead, leave them the possibility to +# choose the command. +PATCHELF_SET_RPATH_ARG := --set-rpath ifeq ($(USE_SYSTEM_LIBWHICH), 1) LIBWHICH := libwhich diff --git a/Makefile b/Makefile index 0d85d195672cd..ad4ad4ad871c2 100644 --- a/Makefile +++ b/Makefile @@ -388,7 +388,7 @@ ifneq ($(DARWIN_FRAMEWORK),1) endif else ifneq (,$(findstring $(OS),Linux FreeBSD)) for j in $(JL_TARGETS) ; do \ - $(PATCHELF) --set-rpath '$$ORIGIN/$(private_libdir_rel):$$ORIGIN/$(libdir_rel)' $(DESTDIR)$(bindir)/$$j; \ + $(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN/$(private_libdir_rel):$$ORIGIN/$(libdir_rel)' $(DESTDIR)$(bindir)/$$j; \ done endif @@ -420,17 +420,17 @@ endif endif else ifneq (,$(findstring $(OS),Linux FreeBSD)) ifeq ($(JULIA_BUILD_MODE),release) - $(PATCHELF) --set-rpath '$$ORIGIN:$$ORIGIN/$(reverse_private_libdir_rel)' $(DESTDIR)$(private_libdir)/libjulia-internal.$(SHLIB_EXT) - $(PATCHELF) --set-rpath '$$ORIGIN:$$ORIGIN/$(reverse_private_libdir_rel)' $(DESTDIR)$(private_libdir)/libjulia-codegen.$(SHLIB_EXT) + $(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN:$$ORIGIN/$(reverse_private_libdir_rel)' $(DESTDIR)$(private_libdir)/libjulia-internal.$(SHLIB_EXT) + $(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN:$$ORIGIN/$(reverse_private_libdir_rel)' $(DESTDIR)$(private_libdir)/libjulia-codegen.$(SHLIB_EXT) else ifeq ($(JULIA_BUILD_MODE),debug) - $(PATCHELF) --set-rpath '$$ORIGIN:$$ORIGIN/$(reverse_private_libdir_rel)' $(DESTDIR)$(private_libdir)/libjulia-internal-debug.$(SHLIB_EXT) - $(PATCHELF) --set-rpath '$$ORIGIN:$$ORIGIN/$(reverse_private_libdir_rel)' $(DESTDIR)$(private_libdir)/libjulia-codegen-debug.$(SHLIB_EXT) + $(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN:$$ORIGIN/$(reverse_private_libdir_rel)' $(DESTDIR)$(private_libdir)/libjulia-internal-debug.$(SHLIB_EXT) + $(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN:$$ORIGIN/$(reverse_private_libdir_rel)' $(DESTDIR)$(private_libdir)/libjulia-codegen-debug.$(SHLIB_EXT) endif endif # Fix rpaths for dependencies. This should be fixed in BinaryBuilder later. ifeq ($(OS), Linux) - -$(PATCHELF) --set-rpath '$$ORIGIN' $(DESTDIR)$(private_shlibdir)/libLLVM.$(SHLIB_EXT) + -$(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN' $(DESTDIR)$(private_shlibdir)/libLLVM.$(SHLIB_EXT) endif ifneq ($(LOADER_BUILD_DEP_LIBS),$(LOADER_INSTALL_DEP_LIBS)) @@ -460,7 +460,7 @@ ifeq ($(OS),FreeBSD) # don't set libgfortran's RPATH, it won't be able to find its friends on systems # that don't have the exact GCC port installed used for the build. for lib in $(DESTDIR)$(private_libdir)/libgfortran*$(SHLIB_EXT)*; do \ - $(PATCHELF) --set-rpath '$$ORIGIN' $$lib; \ + $(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN' $$lib; \ done endif From c136b7e787aee1dffefcea6afce51d0bdd6d5dd1 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 25 Feb 2023 15:31:33 -0700 Subject: [PATCH 174/775] enable re-using external code in pkgimages (#48723) * enable using external code in pkgimages This was unintentionally disabled (and incomplete) in the original PR for pkgimages. Co-authored-by: Valentin Churavy --- base/linking.jl | 4 +- src/aotcompile.cpp | 24 ++-------- src/codegen.cpp | 111 +++++++++++++++++++++++++++------------------ src/jitlayers.h | 2 +- src/julia.h | 4 +- src/staticdata.c | 9 ++-- 6 files changed, 80 insertions(+), 74 deletions(-) diff --git a/base/linking.jl b/base/linking.jl index 18cf128b342de..b2eea94b3cf26 100644 --- a/base/linking.jl +++ b/base/linking.jl @@ -162,8 +162,8 @@ function link_image_cmd(path, out) `$(ld()) $V $SHARED -o $out $WHOLE_ARCHIVE $path $NO_WHOLE_ARCHIVE $LIBDIR $PRIVATE_LIBDIR $SHLIBDIR $LIBS` end -function link_image(path, out, internal_stderr::IO = stderr, internal_stdout::IO = stdout) - run(link_image_cmd(path, out), Base.DevNull(), stderr, stdout) +function link_image(path, out, internal_stderr::IO=stderr, internal_stdout::IO=stdout) + run(link_image_cmd(path, out), Base.DevNull(), internal_stderr, internal_stdout) end end # module Linking diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index d1694eaf9e0d5..907735dfa0128 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -369,34 +369,16 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm size_t offset = gvars.size(); data->jl_external_to_llvm.resize(params.external_fns.size()); - auto tbaa_const = tbaa_make_child_with_context(*ctxt.getContext(), "jtbaa_const", nullptr, true).first; for (auto &extern_fn : params.external_fns) { jl_code_instance_t *this_code = std::get<0>(extern_fn.first); bool specsig = std::get<1>(extern_fn.first); assert(specsig && "Error external_fns doesn't handle non-specsig yet"); - (void)specsig; - Function *F = extern_fn.second; - Module *M = F->getParent(); - - Type *T_funcp = F->getFunctionType()->getPointerTo(); - // Can't create a GC with type FunctionType. Alias also doesn't work - GlobalVariable *GV = new GlobalVariable(*M, T_funcp, false, - GlobalVariable::ExternalLinkage, - Constant::getNullValue(T_funcp), - F->getName()); - - - // Need to insert load instruction, thus we can't use replace all uses with - replaceUsesWithLoad(*F, [GV](Instruction &) { return GV; }, tbaa_const); - - assert(F->getNumUses() == 0); // declaration counts as use - GV->takeName(F); - F->eraseFromParent(); - + (void) specsig; + GlobalVariable *F = extern_fn.second; size_t idx = gvars.size() - offset; assert(idx >= 0); data->jl_external_to_llvm.at(idx) = this_code; - gvars.push_back(std::string(GV->getName())); + gvars.push_back(std::string(F->getName())); } // clones the contents of the module `m` to the shadow_output collector diff --git a/src/codegen.cpp b/src/codegen.cpp index c2a042967c97a..47b9519bbb2f0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1542,7 +1542,7 @@ class jl_codectx_t { jl_codegen_params_t &emission_context; llvm::MapVector call_targets; std::map &global_targets; - std::map, Function*> &external_calls; + std::map, GlobalVariable*> &external_calls; Function *f = NULL; // local var info. globals are not in here. std::vector slots; @@ -1704,7 +1704,7 @@ static Value *get_current_task(jl_codectx_t &ctx); static Value *get_current_ptls(jl_codectx_t &ctx); static Value *get_last_age_field(jl_codectx_t &ctx); static void CreateTrap(IRBuilder<> &irbuilder, bool create_new_block = true); -static CallInst *emit_jlcall(jl_codectx_t &ctx, Function *theFptr, Value *theF, +static CallInst *emit_jlcall(jl_codectx_t &ctx, FunctionCallee theFptr, Value *theF, const jl_cgval_t *args, size_t nargs, JuliaFunction *trampoline); static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction *theFptr, Value *theF, const jl_cgval_t *args, size_t nargs, JuliaFunction *trampoline); @@ -4039,14 +4039,14 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } // Returns ctx.types().T_prjlvalue -static CallInst *emit_jlcall(jl_codectx_t &ctx, Function *theFptr, Value *theF, +static CallInst *emit_jlcall(jl_codectx_t &ctx, FunctionCallee theFptr, Value *theF, const jl_cgval_t *argv, size_t nargs, JuliaFunction *trampoline) { ++EmittedJLCalls; Function *TheTrampoline = prepare_call(trampoline); // emit arguments SmallVector theArgs; - theArgs.push_back(theFptr); + theArgs.push_back(theFptr.getCallee()); if (theF) theArgs.push_back(theF); for (size_t i = 0; i < nargs; i++) { @@ -4067,7 +4067,7 @@ static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction *theFptr, Value *t } -static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_t *mi, jl_value_t *jlretty, StringRef specFunctionObject, +static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_t *mi, jl_value_t *jlretty, StringRef specFunctionObject, jl_code_instance_t *fromexternal, const jl_cgval_t *argv, size_t nargs, jl_returninfo_t::CallingConv *cc, unsigned *return_roots, jl_value_t *inferred_retty) { ++EmittedSpecfunCalls; @@ -4143,7 +4143,22 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_ idx++; } assert(idx == nfargs); - CallInst *call = ctx.builder.CreateCall(returninfo.decl, ArrayRef(&argvals[0], nfargs)); + Value *callee = returninfo.decl; + if (fromexternal) { + std::string namep("p"); + namep += returninfo.decl->getName(); + GlobalVariable *GV = cast_or_null(jl_Module->getNamedValue(namep)); + if (GV == nullptr) { + GV = new GlobalVariable(*jl_Module, callee->getType(), false, + GlobalVariable::ExternalLinkage, + Constant::getNullValue(callee->getType()), + namep); + ctx.external_calls[std::make_tuple(fromexternal, true)] = GV; + } + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); + callee = ai.decorateInst(ctx.builder.CreateAlignedLoad(callee->getType(), GV, Align(sizeof(void*)))); + } + CallInst *call = ctx.builder.CreateCall(cft, callee, ArrayRef(&argvals[0], nfargs)); call->setAttributes(returninfo.decl->getAttributes()); jl_cgval_t retval; @@ -4182,13 +4197,30 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_ return update_julia_type(ctx, retval, inferred_retty); } -static jl_cgval_t emit_call_specfun_boxed(jl_codectx_t &ctx, jl_value_t *jlretty, StringRef specFunctionObject, +static jl_cgval_t emit_call_specfun_boxed(jl_codectx_t &ctx, jl_value_t *jlretty, StringRef specFunctionObject, jl_code_instance_t *fromexternal, const jl_cgval_t *argv, size_t nargs, jl_value_t *inferred_retty) { - auto theFptr = cast( - jl_Module->getOrInsertFunction(specFunctionObject, ctx.types().T_jlfunc).getCallee()); - addRetAttr(theFptr, Attribute::NonNull); - Value *ret = emit_jlcall(ctx, theFptr, nullptr, argv, nargs, julia_call); + Value *theFptr; + if (fromexternal) { + std::string namep("p"); + namep += specFunctionObject; + GlobalVariable *GV = cast_or_null(jl_Module->getNamedValue(namep)); + Type *pfunc = ctx.types().T_jlfunc->getPointerTo(); + if (GV == nullptr) { + GV = new GlobalVariable(*jl_Module, pfunc, false, + GlobalVariable::ExternalLinkage, + Constant::getNullValue(pfunc), + namep); + ctx.external_calls[std::make_tuple(fromexternal, false)] = GV; + } + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); + theFptr = ai.decorateInst(ctx.builder.CreateAlignedLoad(pfunc, GV, Align(sizeof(void*)))); + } + else { + theFptr = jl_Module->getOrInsertFunction(specFunctionObject, ctx.types().T_jlfunc).getCallee(); + addRetAttr(cast(theFptr), Attribute::NonNull); + } + Value *ret = emit_jlcall(ctx, FunctionCallee(ctx.types().T_jlfunc, theFptr), nullptr, argv, nargs, julia_call); return update_julia_type(ctx, mark_julia_type(ctx, ret, true, jlretty), inferred_retty); } @@ -4223,12 +4255,12 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const FunctionType *ft = ctx.f->getFunctionType(); StringRef protoname = ctx.f->getName(); if (ft == ctx.types().T_jlfunc) { - result = emit_call_specfun_boxed(ctx, ctx.rettype, protoname, argv, nargs, rt); + result = emit_call_specfun_boxed(ctx, ctx.rettype, protoname, nullptr, argv, nargs, rt); handled = true; } else if (ft != ctx.types().T_jlfuncparams) { unsigned return_roots = 0; - result = emit_call_specfun_other(ctx, mi, ctx.rettype, protoname, argv, nargs, &cc, &return_roots, rt); + result = emit_call_specfun_other(ctx, mi, ctx.rettype, protoname, nullptr, argv, nargs, &cc, &return_roots, rt); handled = true; } } @@ -4248,16 +4280,17 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const std::string name; StringRef protoname; bool need_to_emit = true; - bool cache_valid = ctx.use_cache; + bool cache_valid = ctx.use_cache || ctx.external_linkage; bool external = false; - if (ctx.external_linkage) { - if (0 && jl_object_in_image((jl_value_t*)codeinst)) { - // Target is present in another pkgimage - cache_valid = true; - external = true; - } + + // Check if we already queued this up + auto it = ctx.call_targets.find(codeinst); + if (need_to_emit && it != ctx.call_targets.end()) { + protoname = std::get<2>(it->second)->getName(); + need_to_emit = cache_valid = false; } + // Check if it is already compiled (either JIT or externally) if (cache_valid) { // optimization: emit the correct name immediately, if we know it // TODO: use `emitted` map here too to try to consolidate names? @@ -4270,15 +4303,20 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const invoke = jl_atomic_load_relaxed(&codeinst->invoke); if (specsig ? jl_atomic_load_relaxed(&codeinst->specsigflags) & 0b1 : invoke == jl_fptr_args_addr) { protoname = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)fptr, codeinst); - need_to_emit = false; + if (ctx.external_linkage) { + // TODO: Add !specsig support to aotcompile.cpp + // Check that the codeinst is containing native code + if (specsig && jl_atomic_load_relaxed(&codeinst->specsigflags) & 0b100) { + external = true; + need_to_emit = false; + } + } + else { // ctx.use_cache + need_to_emit = false; + } } } } - auto it = ctx.call_targets.find(codeinst); - if (need_to_emit && it != ctx.call_targets.end()) { - protoname = std::get<2>(it->second)->getName(); - need_to_emit = false; - } if (need_to_emit) { raw_string_ostream(name) << (specsig ? "j_" : "j1_") << name_from_method_instance(mi) << "_" << jl_atomic_fetch_add(&globalUniqueGeneratedNames, 1); protoname = StringRef(name); @@ -4286,16 +4324,9 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const jl_returninfo_t::CallingConv cc = jl_returninfo_t::CallingConv::Boxed; unsigned return_roots = 0; if (specsig) - result = emit_call_specfun_other(ctx, mi, codeinst->rettype, protoname, argv, nargs, &cc, &return_roots, rt); + result = emit_call_specfun_other(ctx, mi, codeinst->rettype, protoname, external ? codeinst : nullptr, argv, nargs, &cc, &return_roots, rt); else - result = emit_call_specfun_boxed(ctx, codeinst->rettype, protoname, argv, nargs, rt); - if (external) { - assert(!need_to_emit); - auto calledF = jl_Module->getFunction(protoname); - assert(calledF); - // TODO: Check if already present? - ctx.external_calls[std::make_tuple(codeinst, specsig)] = calledF; - } + result = emit_call_specfun_boxed(ctx, codeinst->rettype, protoname, external ? codeinst : nullptr, argv, nargs, rt); handled = true; if (need_to_emit) { Function *trampoline_decl = cast(jl_Module->getNamedValue(protoname)); @@ -5617,14 +5648,7 @@ static Function *emit_tojlinvoke(jl_code_instance_t *codeinst, Module *M, jl_cod Function *theFunc; Value *theFarg; auto invoke = jl_atomic_load_relaxed(&codeinst->invoke); - bool cache_valid = params.cache; - if (params.external_linkage) { - if (0 && jl_object_in_image((jl_value_t*)codeinst)) { - // Target is present in another pkgimage - cache_valid = true; - } - } if (cache_valid && invoke != NULL) { StringRef theFptrName = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)invoke, codeinst); @@ -8537,9 +8561,6 @@ void jl_compile_workqueue( bool preal_specsig = false; auto invoke = jl_atomic_load_acquire(&codeinst->invoke); bool cache_valid = params.cache; - if (params.external_linkage) { - cache_valid = 0 && jl_object_in_image((jl_value_t*)codeinst); - } // WARNING: isspecsig is protected by the codegen-lock. If that lock is removed, then the isspecsig load needs to be properly atomically sequenced with this. if (cache_valid && invoke != NULL) { auto fptr = jl_atomic_load_relaxed(&codeinst->specptr.fptr); diff --git a/src/jitlayers.h b/src/jitlayers.h index f62ee595a843b..db6f68bd3f3b6 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -190,7 +190,7 @@ typedef struct _jl_codegen_params_t { // outputs std::vector> workqueue; std::map globals; - std::map, Function*> external_fns; + std::map, GlobalVariable*> external_fns; std::map ditypes; std::map llvmtypes; DenseMap mergedConstants; diff --git a/src/julia.h b/src/julia.h index 19dab5cd3a704..784f16485967c 100644 --- a/src/julia.h +++ b/src/julia.h @@ -424,7 +424,9 @@ typedef struct _jl_code_instance_t { jl_value_t *argescapes; // escape information of call arguments // compilation state cache - _Atomic(uint8_t) specsigflags; // & 0b1 == specptr is a specialized function signature for specTypes->rettype, &0b10 == invokeptr matches specptr + _Atomic(uint8_t) specsigflags; // & 0b001 == specptr is a specialized function signature for specTypes->rettype + // & 0b010 == invokeptr matches specptr + // & 0b100 == From image _Atomic(uint8_t) precompile; // if set, this will be added to the output system image uint8_t relocatability; // nonzero if all roots are built into sysimg or tagged by module key _Atomic(jl_callptr_t) invoke; // jlcall entry point diff --git a/src/staticdata.c b/src/staticdata.c index 9ae00b395a0e8..4b947f38f6356 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1077,7 +1077,7 @@ static void record_external_fns(jl_serializer_state *s, arraylist_t *external_fn #ifndef JL_NDEBUG for (size_t i = 0; i < external_fns->len; i++) { jl_code_instance_t *ci = (jl_code_instance_t*)external_fns->items[i]; - assert(jl_object_in_image((jl_value_t*)ci)); + assert(jl_atomic_load_relaxed(&ci->specsigflags) & 0b100); } #endif } @@ -1889,7 +1889,7 @@ static void jl_update_all_fptrs(jl_serializer_state *s, jl_image_t *image) void *fptr = (void*)(base + offset); if (specfunc) { codeinst->specptr.fptr = fptr; - codeinst->specsigflags = 0b11; // TODO: set only if confirmed to be true + codeinst->specsigflags = 0b111; // TODO: set only if confirmed to be true } else { codeinst->invoke = (jl_callptr_t)fptr; @@ -1913,11 +1913,12 @@ static uint32_t write_gvars(jl_serializer_state *s, arraylist_t *globals, arrayl } for (size_t i = 0; i < external_fns->len; i++) { jl_code_instance_t *ci = (jl_code_instance_t*)external_fns->items[i]; + assert(ci && (jl_atomic_load_relaxed(&ci->specsigflags) & 0b001)); uintptr_t item = backref_id(s, (void*)ci, s->link_ids_external_fnvars); uintptr_t reloc = get_reloc_for_item(item, 0); write_reloc_t(s->gvar_record, reloc); } - return globals->len + 1; + return globals->len; } // Pointer relocation for native-code referenced global variables @@ -1962,7 +1963,7 @@ static void jl_root_new_gvars(jl_serializer_state *s, jl_image_t *image, uint32_ v = (uintptr_t)jl_as_global_root((jl_value_t*)v); } else { jl_code_instance_t *codeinst = (jl_code_instance_t*) v; - assert(codeinst && (codeinst->specsigflags & 0b01)); + assert(codeinst && (codeinst->specsigflags & 0b01) && codeinst->specptr.fptr); v = (uintptr_t)codeinst->specptr.fptr; } *gv = v; From d6431a38913f0b2c44f1147e354fa9a773fabc56 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Sun, 26 Feb 2023 07:01:21 -0500 Subject: [PATCH 175/775] Accept weakdeps as a source for UUIDS for Preferences --- base/loading.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 374f4ccbed4fb..8b104af41aec5 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2494,7 +2494,7 @@ end # Test to see if this UUID is mentioned in this `Project.toml`; either as # the top-level UUID (e.g. that of the project itself), as a dependency, -# or as an extra for Preferences. +# or as an extra/weakdep for Preferences. function get_uuid_name(project::Dict{String, Any}, uuid::UUID) uuid_p = get(project, "uuid", nothing)::Union{Nothing, String} name = get(project, "name", nothing)::Union{Nothing, String} @@ -2509,7 +2509,7 @@ function get_uuid_name(project::Dict{String, Any}, uuid::UUID) end end end - for subkey in ("deps", "extras") + for subkey in ("deps", "extras", "weakdeps") subsection = get(project, subkey, nothing)::Union{Nothing, Dict{String, Any}} if subsection !== nothing for (k, v) in subsection From 2dcf9cced7169212b4cd040c825dafc10ed02484 Mon Sep 17 00:00:00 2001 From: woclass Date: Mon, 27 Feb 2023 05:34:29 +0800 Subject: [PATCH 176/775] Update Codecov badge links in README.md (#48794) --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c8d93f70bf72..f822f7b1a2364 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,8 @@ Code coverage - + Coverage Status + From 6905c8ebf1f1d20f281cb9a434f4d4996adf4236 Mon Sep 17 00:00:00 2001 From: FX Coudert Date: Mon, 27 Feb 2023 00:51:12 +0100 Subject: [PATCH 177/775] [LibGit2_jll] Update to v1.6.1 (#48789) * [LibGit2_jll] Update to v1.6.1 * Update checksums * Update MozillaCACerts_jll to 2023.01.10 * Update cacert checksums --- deps/checksums/cacert-2022-10-11.pem/md5 | 1 - deps/checksums/cacert-2022-10-11.pem/sha512 | 1 - deps/checksums/cacert-2023-01-10.pem/md5 | 1 + deps/checksums/cacert-2023-01-10.pem/sha512 | 1 + deps/checksums/libgit2 | 68 +++++++++---------- deps/libgit2.mk | 17 ----- deps/libgit2.version | 6 +- deps/patches/libgit2-agent-nonfatal.patch | 25 ------- deps/patches/libgit2-hostkey.patch | 32 --------- .../patches/libgit2-lowercase-windows-h.patch | 22 ------ stdlib/LibGit2_jll/Project.toml | 2 +- stdlib/LibGit2_jll/src/LibGit2_jll.jl | 4 +- stdlib/LibGit2_jll/test/runtests.jl | 2 +- stdlib/MozillaCACerts_jll/Project.toml | 2 +- 14 files changed, 44 insertions(+), 140 deletions(-) delete mode 100644 deps/checksums/cacert-2022-10-11.pem/md5 delete mode 100644 deps/checksums/cacert-2022-10-11.pem/sha512 create mode 100644 deps/checksums/cacert-2023-01-10.pem/md5 create mode 100644 deps/checksums/cacert-2023-01-10.pem/sha512 delete mode 100644 deps/patches/libgit2-agent-nonfatal.patch delete mode 100644 deps/patches/libgit2-hostkey.patch delete mode 100644 deps/patches/libgit2-lowercase-windows-h.patch diff --git a/deps/checksums/cacert-2022-10-11.pem/md5 b/deps/checksums/cacert-2022-10-11.pem/md5 deleted file mode 100644 index 877aa5a716378..0000000000000 --- a/deps/checksums/cacert-2022-10-11.pem/md5 +++ /dev/null @@ -1 +0,0 @@ -1363ae92d22e83c42a7f82ab6c5b0711 diff --git a/deps/checksums/cacert-2022-10-11.pem/sha512 b/deps/checksums/cacert-2022-10-11.pem/sha512 deleted file mode 100644 index 5c7b990cb9e4b..0000000000000 --- a/deps/checksums/cacert-2022-10-11.pem/sha512 +++ /dev/null @@ -1 +0,0 @@ -fbbd8d33932a5d65dd548d91927fc5bac5218d5a44b8d992591bef2eab22b09cc2154b6effb2df1c61e1aa233816e3c3e7acfb27b3e3f90672a7752bb05b710f diff --git a/deps/checksums/cacert-2023-01-10.pem/md5 b/deps/checksums/cacert-2023-01-10.pem/md5 new file mode 100644 index 0000000000000..92063050b50f3 --- /dev/null +++ b/deps/checksums/cacert-2023-01-10.pem/md5 @@ -0,0 +1 @@ +e7cf471ba7c88f4e313f492a76e624b3 diff --git a/deps/checksums/cacert-2023-01-10.pem/sha512 b/deps/checksums/cacert-2023-01-10.pem/sha512 new file mode 100644 index 0000000000000..d3322e5890f81 --- /dev/null +++ b/deps/checksums/cacert-2023-01-10.pem/sha512 @@ -0,0 +1 @@ +08cd35277bf2260cb3232d7a7ca3cce6b2bd58af9221922d2c6e9838a19c2f96d1ca6d77f3cc2a3ab611692f9fec939e9b21f67442282e867a487b0203ee0279 diff --git a/deps/checksums/libgit2 b/deps/checksums/libgit2 index 383d1142ecbd8..a70a404ae6843 100644 --- a/deps/checksums/libgit2 +++ b/deps/checksums/libgit2 @@ -1,34 +1,34 @@ -LibGit2.v1.5.0+1.aarch64-apple-darwin.tar.gz/md5/a6f909d459a3783abd181b105deddcb9 -LibGit2.v1.5.0+1.aarch64-apple-darwin.tar.gz/sha512/4576464d1a9b64beac0d5a7067b6afccee4bbe1debc7dd340b1bf4b4cbc916ecef7b4feaaebabde151bd0d9ca92536f30edc05a928e36c1741ed4e5fbcf3aeba -LibGit2.v1.5.0+1.aarch64-linux-gnu.tar.gz/md5/ac3f90441013850c5b65c951e7d7a987 -LibGit2.v1.5.0+1.aarch64-linux-gnu.tar.gz/sha512/a945e7bcfeb41471c8c687f6f28aa340bd78c5a7aeaf5c3ab35fe8c7aebee4f3d823bbf5e3d0f44cf566fe1f7a7f5dbd2e5b3007aa158af863e89f7a77357984 -LibGit2.v1.5.0+1.aarch64-linux-musl.tar.gz/md5/6892a30e270b2fb8c46fbe3b60f152db -LibGit2.v1.5.0+1.aarch64-linux-musl.tar.gz/sha512/f43029515e457d21d4dee8fc9c0c79ffde7143af2df1c12ab788b6dd7ac3ee28028de4f3e70ef71f30332d35a939012142f26a680864b4d8befae3c821ddd3d2 -LibGit2.v1.5.0+1.armv6l-linux-gnueabihf.tar.gz/md5/c9e1133af6a095b3288603f4591c9814 -LibGit2.v1.5.0+1.armv6l-linux-gnueabihf.tar.gz/sha512/a571a6e7fcf9e02a222c27fd1d0eb3e47a380493e2225d8e7879972c34ee571463a2c3995c1c6b7b723f1f7957a0b230ec0fff1eef06b7bed0641c4bb4594817 -LibGit2.v1.5.0+1.armv6l-linux-musleabihf.tar.gz/md5/99deac8e5abe948c9e0c13035a851c2f -LibGit2.v1.5.0+1.armv6l-linux-musleabihf.tar.gz/sha512/10a8b77dc9dee91046093145ad3b602a8da4aaee1bc68198557ca7197206a8c6a158300610fae5d4d0f5e276cab3411ba29304ac5eaf8d63ea41b5b7085ca241 -LibGit2.v1.5.0+1.armv7l-linux-gnueabihf.tar.gz/md5/58bfcbf4b3adf5736149c26dc14f429b -LibGit2.v1.5.0+1.armv7l-linux-gnueabihf.tar.gz/sha512/4bf37cdee3e79a5686563b875056f168c6e33c40b5099896601b190a569a027815e1da5168c0cd03ebe2ec952e0673e5e9d9bda22372ae12a74e16d219e5b537 -LibGit2.v1.5.0+1.armv7l-linux-musleabihf.tar.gz/md5/d82a259ea8979479471483e64b2edc10 -LibGit2.v1.5.0+1.armv7l-linux-musleabihf.tar.gz/sha512/9a5738e811a016dfe242bdacbc6b34c54df2bf6c7564dd91e79b76ed22b291665aa403b24ebda0979193c4b2f0d402306cb351360a89627f332409d3d8fea00f -LibGit2.v1.5.0+1.i686-linux-gnu.tar.gz/md5/5d7da5ec4132390905c7c26f3a4c8ed0 -LibGit2.v1.5.0+1.i686-linux-gnu.tar.gz/sha512/bb1437e08bbf30f39bdfe87e2a1e2259bef0ac53802ee507c613d32874f9f2a0e30966fbb621edeb0ce62be805b9af11753861523f2059a697c2132d96187913 -LibGit2.v1.5.0+1.i686-linux-musl.tar.gz/md5/b3233a398ffd6d635f2fdf6f5af775b1 -LibGit2.v1.5.0+1.i686-linux-musl.tar.gz/sha512/83bde361346b28e4a7ba6922cef90f40c6ea6f03b0ea5f491b8cc0de815f62ca3a37020cde05c6bb3fda701cf8c06fd2e05c70857fc916ec0220cb11f6121422 -LibGit2.v1.5.0+1.i686-w64-mingw32.tar.gz/md5/a6ffdeac30e97e684bfc460677d7f222 -LibGit2.v1.5.0+1.i686-w64-mingw32.tar.gz/sha512/29ac5f44bc16b32c33c68fb02c08bdbcf4762c288d4b9fe901c33beeacaa972db5c2c1b0a63cf307b9d1658a6e2fe71cd76ec8b1a7c6ae57ef1a7c20ed6bfd1a -LibGit2.v1.5.0+1.powerpc64le-linux-gnu.tar.gz/md5/c87f1d5e5d49414b6ac39b9f02a39446 -LibGit2.v1.5.0+1.powerpc64le-linux-gnu.tar.gz/sha512/5e7982caee4c6093f58f6ce438974f4c5d2ea7c41f33ed75dec3e169f55ab547b15fe96771a278f80c31c847c42489a2e3c0e9c2c9745bc8f228c7f5dafe60c3 -LibGit2.v1.5.0+1.x86_64-apple-darwin.tar.gz/md5/4679839c80fe653fbc007ada1f84054a -LibGit2.v1.5.0+1.x86_64-apple-darwin.tar.gz/sha512/d66b8686b591968d4cac2c2e0d2013d37f4b73043cd77908b6716e5647ae9d092cc874a616a8862dbc0e114f19a3ccd596b669e72cbd37f3371dcc518d48aa40 -LibGit2.v1.5.0+1.x86_64-linux-gnu.tar.gz/md5/5d0cb8c5746a4417ce51437c5dcb75bf -LibGit2.v1.5.0+1.x86_64-linux-gnu.tar.gz/sha512/1a0aa9b537d03a0849401551e1a34b938879c2bf70c30dbf43cbf76b1e4cc1dd4dbda561741b7f1a48ad33d8bbec200252f50583b3aacab10cdc128e48bd7744 -LibGit2.v1.5.0+1.x86_64-linux-musl.tar.gz/md5/bb54d5e1b903f90f0c7dbf323f819ed1 -LibGit2.v1.5.0+1.x86_64-linux-musl.tar.gz/sha512/72717ef4c6c7385db3fdba192201f0e2fe7b680bea837f27b5b35aaedbbe43e527f72cd447d061848061e06ed0e6ab348d4b28c9e3dceee6d913949923c0e317 -LibGit2.v1.5.0+1.x86_64-unknown-freebsd.tar.gz/md5/9b16f78a52838c68716eb0f311edd309 -LibGit2.v1.5.0+1.x86_64-unknown-freebsd.tar.gz/sha512/fe29f9dac5bde9e3f95e1720ad44f34dfb0b269aeb2859bff9cde46adec99104869a7dc4e536e3276491c3a01273c42223e37e5ba6694581c27b588029903158 -LibGit2.v1.5.0+1.x86_64-w64-mingw32.tar.gz/md5/84a38431d01ccd6b0f01181e9ecaf5ef -LibGit2.v1.5.0+1.x86_64-w64-mingw32.tar.gz/sha512/ffccbc6bc01eb9900b2a43cbfdafef7b1d1997285d46786b1373def1f091a41d8fbc3fc746fa20bd70ee619d6cfd357fb5cd6d9ac040f1c301fe6ed49d07a3fd -libgit2-fbea439d4b6fc91c6b619d01b85ab3b7746e4c19.tar.gz/md5/b76d9e4cd2d5fa636143ce9252a6eb3e -libgit2-fbea439d4b6fc91c6b619d01b85ab3b7746e4c19.tar.gz/sha512/97ce3066cd7de077c3ccf0921a29afb20250b354ab02d3ced4a80ed2a294784e07933072ce8f819c3ef8200249d0a7ea8b500957ace498ef64e9a072c92782fc +LibGit2.v1.6.1+0.aarch64-apple-darwin.tar.gz/md5/62bb842de0ede8a7c2b119cfa7402a61 +LibGit2.v1.6.1+0.aarch64-apple-darwin.tar.gz/sha512/e5117912419fd73138779322d5cb84454c641aad87d0df7d44b5074c96576fe1ee3822dba18c8207dacc9bae2b74cef87353d5c519fb7fba8ea89c858415f993 +LibGit2.v1.6.1+0.aarch64-linux-gnu.tar.gz/md5/3f42f283a9f550841b285216d681f3d0 +LibGit2.v1.6.1+0.aarch64-linux-gnu.tar.gz/sha512/0a793bb239976946941af5794cb45cfd7d1d99b9aa125800aee9337bf9d9c5152bcad258f75d987a7af9b547ea906ee2beebe7b8d2c8cea111e6878df0eb3ea9 +LibGit2.v1.6.1+0.aarch64-linux-musl.tar.gz/md5/0f20cee604380bfa789334b5544b1cab +LibGit2.v1.6.1+0.aarch64-linux-musl.tar.gz/sha512/86d7e6a64bf24f3e69dfa4383ed896c5d8a915e19f6f0351e8cf38361352347c827f79032fd8576ca9bfb94dc8db4704d35540ae67b46d671f44ab549c6ceb49 +LibGit2.v1.6.1+0.armv6l-linux-gnueabihf.tar.gz/md5/5c025b4c9065c0b481c7b0f6dd7666a0 +LibGit2.v1.6.1+0.armv6l-linux-gnueabihf.tar.gz/sha512/5b1d3472df47462b3e38c5a5b3400d90038b1637a7f479e9fe04ef046849c14d12301328498429a9f290ff82b6343ccd9ae7616c5ff1d5fd83f35559bedf8747 +LibGit2.v1.6.1+0.armv6l-linux-musleabihf.tar.gz/md5/8015b63706e6d5826779f870681ff865 +LibGit2.v1.6.1+0.armv6l-linux-musleabihf.tar.gz/sha512/e3c8c46d8da8df409b2dc7c476da638da2c79974270390b84473ebefb66f26cf60647445c2b141f7b6cf45655de12404deea30731b812952fd9156acbd7344a1 +LibGit2.v1.6.1+0.armv7l-linux-gnueabihf.tar.gz/md5/74672b31da80507609e59b19448ec415 +LibGit2.v1.6.1+0.armv7l-linux-gnueabihf.tar.gz/sha512/6c6365501abeffc7e796f3b67a139e93262dab1550ba5fe6ead179c0a9d32c62bab7b422b81524d7a367ca1032c7bfd2b3385155e364fc267f660dffa8eee39a +LibGit2.v1.6.1+0.armv7l-linux-musleabihf.tar.gz/md5/057c22b3fc988a98551fc319eb080c39 +LibGit2.v1.6.1+0.armv7l-linux-musleabihf.tar.gz/sha512/edfb8c57aad5499fae88f09a17e905b4c009e2a8781727566321a858f3ed8a4bcb75b990ae5ad4ac57bcb2b01bd2dfbe0375b01a41405c161106881c8859aa78 +LibGit2.v1.6.1+0.i686-linux-gnu.tar.gz/md5/ecde35f4ca6b4a03f8491d90480f33b3 +LibGit2.v1.6.1+0.i686-linux-gnu.tar.gz/sha512/ca77a1b3c381be2286be9134d7adfde51fb38c4bc9dcb3f56cf1840809c40c484c843cf4ed8d77c538889e06cbef2e5d1b4468739bf761cc91c676a0dc5a34ee +LibGit2.v1.6.1+0.i686-linux-musl.tar.gz/md5/1a56e7832761479fe911b8efd66b5b73 +LibGit2.v1.6.1+0.i686-linux-musl.tar.gz/sha512/e929261ba9564762d2b3c3191dde216caede5c436b84a00d08706a708436023430a9a762cbd94bf96e903a230c690ea28787ee08208d5b50e51d98e56587b30f +LibGit2.v1.6.1+0.i686-w64-mingw32.tar.gz/md5/671a1c045725877e1a4f55b42fbb15b9 +LibGit2.v1.6.1+0.i686-w64-mingw32.tar.gz/sha512/5b0e78b5f5f24b7ee8c88d704bf58043626174d9e8e28226b72873f62d0ff6a6f87d6200adfd613e35c27f6d127d967f49a1f7ef26ded8d1b08c89589b59ce85 +LibGit2.v1.6.1+0.powerpc64le-linux-gnu.tar.gz/md5/4ffc17733025ac94e525f8d9416713a4 +LibGit2.v1.6.1+0.powerpc64le-linux-gnu.tar.gz/sha512/a382f7f15484426d6e913c9cd54facd63573650449f1a2d7b180f1905b79dc75280fdb48ff9e47ffc1ef70c9941d43a6ca35e21bc9746172689886fbbc9d65a4 +LibGit2.v1.6.1+0.x86_64-apple-darwin.tar.gz/md5/af4192c866787ce226fb7a6d5229bfa2 +LibGit2.v1.6.1+0.x86_64-apple-darwin.tar.gz/sha512/18bac55bd7bcd9ea66002c98717ef358710aa689c9bff63be77de1cce4db2082f023ee577060f6ed11e3830c2e751bf2adae1a9b232570a090031c5246f29edf +LibGit2.v1.6.1+0.x86_64-linux-gnu.tar.gz/md5/d26008f39b244ab0caa804ae0365d69b +LibGit2.v1.6.1+0.x86_64-linux-gnu.tar.gz/sha512/3d6068d2165c012ce66317cc0993c374df43cdb2dcd584ec7966f602062428d4f5e18d157c7aa19572affa1e9dcb0346105a01c64f8e5ac01546aaf7b5d99439 +LibGit2.v1.6.1+0.x86_64-linux-musl.tar.gz/md5/fcbfc9f15ffe3c4b2ea055e198795e96 +LibGit2.v1.6.1+0.x86_64-linux-musl.tar.gz/sha512/16bb30defa9d23e6025e3729e313766940105e02f00168e61bff81ae38beae9ae050a5fbf2307083b3cd89d364aa70a7042b94062160fda2174aaf5018f3e2f3 +LibGit2.v1.6.1+0.x86_64-unknown-freebsd.tar.gz/md5/a4fe2ed51c1ac1aaaa4f46a00714d85a +LibGit2.v1.6.1+0.x86_64-unknown-freebsd.tar.gz/sha512/bba31901fcd8b2e69f43e9645c028be4c840b3d9afb4e92e64c9ea46c7fb44dfecf14f99cde586380ae0508fdb8402d3bbe93ec7b38219fe7806299b70576949 +LibGit2.v1.6.1+0.x86_64-w64-mingw32.tar.gz/md5/11ed8da2cb4c7ef924b50768cbb54678 +LibGit2.v1.6.1+0.x86_64-w64-mingw32.tar.gz/sha512/b39f12931d638809af27e446d7ac25b17bfd5c003cac89bcf83dc4c5331d14ec12b07ae410cfdc636546a3b1edf0f7d360bd194aa58c835261642b51edb4afd1 +libgit2-8a871d13b7f4e186b8ad943ae5a7fcf30be52e67.tar.gz/md5/831f4d09a6a22662dc0043063d0305cb +libgit2-8a871d13b7f4e186b8ad943ae5a7fcf30be52e67.tar.gz/sha512/17ad43e6f80e87e8115cef89919475a9d9ea11d679e107221e6d82623577fc8e4002876a33c7eb2a52a47e3d8142976777bc79f81e4c4cf2da6adb1553d17b00 diff --git a/deps/libgit2.mk b/deps/libgit2.mk index 30d94aeca7b7d..9bd7bd555d89d 100644 --- a/deps/libgit2.mk +++ b/deps/libgit2.mk @@ -35,23 +35,6 @@ endif LIBGIT2_SRC_PATH := $(SRCCACHE)/$(LIBGIT2_SRC_DIR) -$(LIBGIT2_SRC_PATH)/libgit2-agent-nonfatal.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted - cd $(LIBGIT2_SRC_PATH) && \ - patch -p1 -f < $(SRCDIR)/patches/libgit2-agent-nonfatal.patch - echo 1 > $@ - -$(LIBGIT2_SRC_PATH)/libgit2-hostkey.patch-applied: $(LIBGIT2_SRC_PATH)/libgit2-agent-nonfatal.patch-applied - cd $(LIBGIT2_SRC_PATH) && \ - patch -p1 -f < $(SRCDIR)/patches/libgit2-hostkey.patch - echo 1 > $@ - -$(LIBGIT2_SRC_PATH)/libgit2-lowercase-windows-h.patch-applied: $(LIBGIT2_SRC_PATH)/libgit2-hostkey.patch-applied - cd $(LIBGIT2_SRC_PATH) && \ - patch -p1 -f < $(SRCDIR)/patches/libgit2-lowercase-windows-h.patch - echo 1 > $@ - -$(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: $(LIBGIT2_SRC_PATH)/libgit2-lowercase-windows-h.patch-applied - $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: $(LIBGIT2_SRC_PATH)/source-extracted mkdir -p $(dir $@) cd $(dir $@) && \ diff --git a/deps/libgit2.version b/deps/libgit2.version index 62633db62409f..b8cefc3c5c6f3 100644 --- a/deps/libgit2.version +++ b/deps/libgit2.version @@ -3,11 +3,11 @@ LIBGIT2_JLL_NAME := LibGit2 ## source build -LIBGIT2_BRANCH=v1.5.0 -LIBGIT2_SHA1=fbea439d4b6fc91c6b619d01b85ab3b7746e4c19 +LIBGIT2_BRANCH=v1.6.1 +LIBGIT2_SHA1=8a871d13b7f4e186b8ad943ae5a7fcf30be52e67 ## Other deps # Specify the version of the Mozilla CA Certificate Store to obtain. # The versions of cacert.pem are identified by the date (YYYY-MM-DD) of their changes. # See https://curl.haxx.se/docs/caextract.html for more details. -MOZILLA_CACERT_VERSION := 2022-10-11 +MOZILLA_CACERT_VERSION := 2023-01-10 diff --git a/deps/patches/libgit2-agent-nonfatal.patch b/deps/patches/libgit2-agent-nonfatal.patch deleted file mode 100644 index 4d46965f27bf1..0000000000000 --- a/deps/patches/libgit2-agent-nonfatal.patch +++ /dev/null @@ -1,25 +0,0 @@ -commit 70020247d1903c7a1262d967cf205a44dc6f6ebe -Author: Keno Fischer -Date: Wed Jul 20 19:59:00 2016 -0400 - - Make failure to connect to ssh-agent non-fatal - - Julia issue: https://github.com/JuliaLang/julia/pull/17459 - Upstream: https://github.com/libgit2/libgit2/issues/3866 - -diff --git a/src/libgit2/transports/ssh.c b/src/libgit2/transports/ssh.c -index cfd5736..82d2c63 100644 ---- a/src/libgit2/transports/ssh.c -+++ b/src/libgit2/transports/ssh.c -@@ -296,8 +296,10 @@ static int ssh_agent_auth(LIBSSH2_SESSION *session, git_cred_ssh_key *c) { - - rc = libssh2_agent_connect(agent); - -- if (rc != LIBSSH2_ERROR_NONE) -+ if (rc != LIBSSH2_ERROR_NONE) { -+ rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED; - goto shutdown; -+ } - - rc = libssh2_agent_list_identities(agent); - diff --git a/deps/patches/libgit2-hostkey.patch b/deps/patches/libgit2-hostkey.patch deleted file mode 100644 index b53484fc07951..0000000000000 --- a/deps/patches/libgit2-hostkey.patch +++ /dev/null @@ -1,32 +0,0 @@ -diff --git a/src/libgit2/transports/ssh.c b/src/libgit2/transports/ssh.c -index 89f085230..b8bdca61a 100644 ---- a/src/libgit2/transports/ssh.c -+++ b/src/libgit2/transports/ssh.c -@@ -467,6 +467,7 @@ static int _git_ssh_setup_conn( - git_credential *cred = NULL; - LIBSSH2_SESSION *session=NULL; - LIBSSH2_CHANNEL *channel=NULL; -+ char *host_and_port; - - t->current_stream = NULL; - -@@ -567,10 +568,18 @@ static int _git_ssh_setup_conn( - - cert_ptr = &cert; - -+ if (atoi(s->url.port) == SSH_DEFAULT_PORT) { -+ host_and_port = s->url.host; -+ } else { -+ size_t n = strlen(s->url.host) + strlen(s->url.port) + 2; -+ host_and_port = alloca(n); -+ sprintf(host_and_port, "%s:%s", s->url.host, s->url.port); -+ } -+ - error = t->owner->connect_opts.callbacks.certificate_check( - (git_cert *)cert_ptr, - 0, -- s->url.host, -+ host_and_port, - t->owner->connect_opts.callbacks.payload); - - if (error < 0 && error != GIT_PASSTHROUGH) { diff --git a/deps/patches/libgit2-lowercase-windows-h.patch b/deps/patches/libgit2-lowercase-windows-h.patch deleted file mode 100644 index f7c79a7e59e11..0000000000000 --- a/deps/patches/libgit2-lowercase-windows-h.patch +++ /dev/null @@ -1,22 +0,0 @@ -From d64f3d0992ec278d843c397b4b52e3434962c197 Mon Sep 17 00:00:00 2001 -From: Vinz2008 <68145293+Vinz2008@users.noreply.github.com> -Date: Thu, 11 Aug 2022 00:25:31 +0200 -Subject: [PATCH] Fix #6365 - ---- - src/cli/opt.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/cli/opt.c b/src/cli/opt.c -index 72df5877fbf..62a3430d16e 100644 ---- a/src/cli/opt.c -+++ b/src/cli/opt.c -@@ -23,7 +23,7 @@ - #include "opt.h" - - #ifdef _WIN32 --# include -+# include - #else - # include - # include diff --git a/stdlib/LibGit2_jll/Project.toml b/stdlib/LibGit2_jll/Project.toml index 676653de04a62..4c16c1fb72e42 100644 --- a/stdlib/LibGit2_jll/Project.toml +++ b/stdlib/LibGit2_jll/Project.toml @@ -1,6 +1,6 @@ name = "LibGit2_jll" uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" -version = "1.5.0+1" +version = "1.6.1+0" [deps] MbedTLS_jll = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" diff --git a/stdlib/LibGit2_jll/src/LibGit2_jll.jl b/stdlib/LibGit2_jll/src/LibGit2_jll.jl index d672996f96ad9..29d12fc5343e1 100644 --- a/stdlib/LibGit2_jll/src/LibGit2_jll.jl +++ b/stdlib/LibGit2_jll/src/LibGit2_jll.jl @@ -21,9 +21,9 @@ libgit2_path = "" if Sys.iswindows() const libgit2 = "libgit2.dll" elseif Sys.isapple() - const libgit2 = "@rpath/libgit2.1.5.dylib" + const libgit2 = "@rpath/libgit2.1.6.dylib" else - const libgit2 = "libgit2.so.1.5" + const libgit2 = "libgit2.so.1.6" end function __init__() diff --git a/stdlib/LibGit2_jll/test/runtests.jl b/stdlib/LibGit2_jll/test/runtests.jl index 402b61a4581ab..32ada173f01a0 100644 --- a/stdlib/LibGit2_jll/test/runtests.jl +++ b/stdlib/LibGit2_jll/test/runtests.jl @@ -7,5 +7,5 @@ using Test, Libdl, LibGit2_jll minor = Ref{Cint}(0) patch = Ref{Cint}(0) @test ccall((:git_libgit2_version, libgit2), Cint, (Ref{Cint}, Ref{Cint}, Ref{Cint}), major, minor, patch) == 0 - @test VersionNumber(major[], minor[], patch[]) == v"1.5.0" + @test VersionNumber(major[], minor[], patch[]) == v"1.6.1" end diff --git a/stdlib/MozillaCACerts_jll/Project.toml b/stdlib/MozillaCACerts_jll/Project.toml index db6e85252e17f..cef860fda4acd 100644 --- a/stdlib/MozillaCACerts_jll/Project.toml +++ b/stdlib/MozillaCACerts_jll/Project.toml @@ -1,6 +1,6 @@ name = "MozillaCACerts_jll" uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2022.10.11" +version = "2023.01.10" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" From bfacf2cdf9ea195b8f09f84443e40d6f9de304c6 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Mon, 27 Feb 2023 02:30:14 -0600 Subject: [PATCH 178/775] Fastpath for comparing tuples of two bitintegers (#48753) --- base/operators.jl | 7 +++++++ test/operators.jl | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/base/operators.jl b/base/operators.jl index 7ac5637951b16..3b34e549ea849 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -178,6 +178,13 @@ isless(x::AbstractFloat, y::AbstractFloat) = (!isnan(x) & (isnan(y) | signless(x isless(x::Real, y::AbstractFloat) = (!isnan(x) & (isnan(y) | signless(x, y))) | (x < y) isless(x::AbstractFloat, y::Real ) = (!isnan(x) & (isnan(y) | signless(x, y))) | (x < y) +# Performance optimization to reduce branching +# This is useful for sorting tuples of integers +# TODO: remove this when the compiler can optimize the generic version better +# See #48724 and #48753 +isless(a::Tuple{BitInteger, BitInteger}, b::Tuple{BitInteger, BitInteger}) = + isless(a[1], b[1]) | (isequal(a[1], b[1]) & isless(a[2], b[2])) + """ isgreater(x, y) diff --git a/test/operators.jl b/test/operators.jl index 6a93f70cc21f0..46cf6c7526299 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -93,6 +93,23 @@ end @test isless('a','b') +@testset "isless on pairs of integers (because there is a fastpath)" begin + @test isless((1,2), (1,3)) + @test isless((0,-2), (0,2)) + @test isless((-1,2), (1,2)) + @test isless((-1,-2), (1,2)) + @test !isless((1,3), (1,2)) + @test !isless((0,2), (0,-2)) + @test !isless((1,2), (-1,2)) + @test !isless((1,2), (-1,-2)) + @test !isless((-1,-2), (-1,-2)) + + @test isless((typemin(Int), typemin(Int)), (0,0)) + @test isless((1, 1), (Int8(2), Int8(2))) + @test !isless((UInt8(200),Int8(-1)), (UInt8(200),Int8(-1))) + @test isless((1, 1), (1, unsigned(2))) +end + @testset "isgreater" begin # isgreater should be compatible with min. min1(a, b) = Base.isgreater(a, b) ? b : a From 6bf77c0ed86293a05760d9a2ce7f405e1f87b50c Mon Sep 17 00:00:00 2001 From: FX Coudert Date: Mon, 27 Feb 2023 14:20:46 +0100 Subject: [PATCH 179/775] [nghttp2_jll] Upgrade to v1.52.0 (#48795) * [nghttp2_jll] update to v1.52.0 * Update checksums --- deps/checksums/nghttp2 | 68 ++++++++++++++--------------- deps/nghttp2.version | 2 +- stdlib/nghttp2_jll/Project.toml | 2 +- stdlib/nghttp2_jll/test/runtests.jl | 2 +- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/deps/checksums/nghttp2 b/deps/checksums/nghttp2 index 5cc0b22d2d778..6113b23d68c14 100644 --- a/deps/checksums/nghttp2 +++ b/deps/checksums/nghttp2 @@ -1,34 +1,34 @@ -nghttp2-1.48.0.tar.bz2/md5/887336a68dbf6e2fa78dd4fc2a515e01 -nghttp2-1.48.0.tar.bz2/sha512/319b8c4f5f276e699fb04cf2a9aadd07bb0a26b78d8b37eb84e6dab603718b3d2c9bf6dca54816d4644cd6833177d842d7f7d3a1438a1c723d2b73e4ec2fb344 -nghttp2.v1.48.0+0.aarch64-apple-darwin.tar.gz/md5/362b35eecbb86a49b956fa57168ec61c -nghttp2.v1.48.0+0.aarch64-apple-darwin.tar.gz/sha512/d8c35686ac6baf4ba6038355f1d3a275f2c3a9696d1b751a54c6e671cbd96c38b4600c6ac00d77e43efc4fbb01c7672d917142530efb0360c38a4159703b9156 -nghttp2.v1.48.0+0.aarch64-linux-gnu.tar.gz/md5/2eb064be49b1990250a7c8ebffcc4a1e -nghttp2.v1.48.0+0.aarch64-linux-gnu.tar.gz/sha512/0fcef4bfa0cea2d7727241961551b0ff73337aefbe8f29a6ca06f856b142681e251af57795ba26edc25784a1845040a0a3865dd0ba26ea65c43478a02ea02080 -nghttp2.v1.48.0+0.aarch64-linux-musl.tar.gz/md5/80f505a5b1f092e9a2e4609ff4b16b9f -nghttp2.v1.48.0+0.aarch64-linux-musl.tar.gz/sha512/3e260d9bb34058c7c841034d874dec2141e71f40c0e75fb751740dc46fe1cd5668c713e7efc154f1e7c964ed41b8fed9a08b780370e4a4fb44eb564eff1a2c72 -nghttp2.v1.48.0+0.armv6l-linux-gnueabihf.tar.gz/md5/6b167502a95dac6f55cf2d312af09b91 -nghttp2.v1.48.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/da620c8e50ce4ca2fd150c7b83b0d1d40d3d9e184cb5dfff6883723b574e8c68ffd121a74154a0544e5beb7991740634c19916bb66b1349f46d772ddff3ceddf -nghttp2.v1.48.0+0.armv6l-linux-musleabihf.tar.gz/md5/b9079b10a7f0e190232426cbed35f8e9 -nghttp2.v1.48.0+0.armv6l-linux-musleabihf.tar.gz/sha512/dd0afaa8eed6df8c0b4f78c3408e6a0b881957d183b5dfa4e6d9aa131d92a7485198da316dfbb28280b6e5e281432ee1dc1bbff5906a29cc29afa77390d83e09 -nghttp2.v1.48.0+0.armv7l-linux-gnueabihf.tar.gz/md5/cfacf5fcb143757b6fa64081011807d6 -nghttp2.v1.48.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/5b9acc860474722c07f73b3d049c5d131c86212264a49270253861b897b165e4a8cd608ac3735ee72c90cdd36ea9342208e1eee48e9e2407b3b10ca2cf23f2d1 -nghttp2.v1.48.0+0.armv7l-linux-musleabihf.tar.gz/md5/76dfdc217fb52e74955b6dd95bace880 -nghttp2.v1.48.0+0.armv7l-linux-musleabihf.tar.gz/sha512/05b7b6ae7cee062409eb941147e45e0b1b68a0ddcd8a022bd008a2f04a1249051a6e69dba511398b3e98e1144004bf0e6580fb4417f5500746f5b4b3eb65179f -nghttp2.v1.48.0+0.i686-linux-gnu.tar.gz/md5/8ec510d34f87830be0cea46378474a0c -nghttp2.v1.48.0+0.i686-linux-gnu.tar.gz/sha512/c3262180298ebfe1aee5fa3b25a491f4fc6122d0936c0fcfdd1d3f7f884dbcdbc9cbca05df986782e200334c4d97bd5ed5b75a9286910d04b00eac9efa43d67a -nghttp2.v1.48.0+0.i686-linux-musl.tar.gz/md5/90fa7935261e782dbd14aa858ae2d511 -nghttp2.v1.48.0+0.i686-linux-musl.tar.gz/sha512/790bcac85995a2e5caddaf19023c90a5b9566d166da48b98581de2e12d84c7beaa74e7ef9ae55bcf4a68c91e1e873204328c8672634e5ed3fc79721a9939b480 -nghttp2.v1.48.0+0.i686-w64-mingw32.tar.gz/md5/b7654776af03333caf4ba1517ffd2636 -nghttp2.v1.48.0+0.i686-w64-mingw32.tar.gz/sha512/b8f82c7a8f3ca6cb3cd8ab760d8299b0dcc6a03c7e51be965168c01de07b900891e48e13fbcee67856afddb10c41b402a4b384a06d3fbee41c4d5f3b6e352c53 -nghttp2.v1.48.0+0.powerpc64le-linux-gnu.tar.gz/md5/eaee75e48bb77137c09abc5abccc6db1 -nghttp2.v1.48.0+0.powerpc64le-linux-gnu.tar.gz/sha512/4b99d91a7f751c05835c73bb6b0f49c851ca36ead41c5137aedf5e96bd48d894768b9fdb65f83560ea86e0c3f854e52bf66f8859dcd920446db1a121c7a5e0f2 -nghttp2.v1.48.0+0.x86_64-apple-darwin.tar.gz/md5/1720e70d0e72afbf36900ed75cba45d0 -nghttp2.v1.48.0+0.x86_64-apple-darwin.tar.gz/sha512/4c07a7d78bb1366a913321d8258d0cbd0d0b7d85f43b5980617fd1451dc39e7859134e86ec59b06b3b6dc8b62b71f9890eecf2737f8cf4e441bf08c2e61cefc6 -nghttp2.v1.48.0+0.x86_64-linux-gnu.tar.gz/md5/a94aab74d021578fcda21836c8030c9b -nghttp2.v1.48.0+0.x86_64-linux-gnu.tar.gz/sha512/c1c31e32e60087fe7facbfea4bd4897649c8eeef92101093df4897f41847461851497e436c4a4e1c847c9bf5ac678934aba1eca0d8a6e17302d9474ca3064fb5 -nghttp2.v1.48.0+0.x86_64-linux-musl.tar.gz/md5/677ad574f615b2d77fecdac0c75111db -nghttp2.v1.48.0+0.x86_64-linux-musl.tar.gz/sha512/737637a68364096ea6c507e37c9305df875c8830d58a05404ceb2e76d69bd6e44c82483e0f8228cdc7a64b0419de75d2d99151fac369bacd42fc06a71b35ec54 -nghttp2.v1.48.0+0.x86_64-unknown-freebsd.tar.gz/md5/b65cf09003912eb4201db80253fc5b04 -nghttp2.v1.48.0+0.x86_64-unknown-freebsd.tar.gz/sha512/fdf7c733f4247f66733dd36e314cf6772abfecb82ec99c613db66910eb956849851587d74b9e940e1f0d743142555ccf96bf7b990b3502e17028cbdd8bc504d8 -nghttp2.v1.48.0+0.x86_64-w64-mingw32.tar.gz/md5/cfb494369553277c10a7b1eaf1c116fd -nghttp2.v1.48.0+0.x86_64-w64-mingw32.tar.gz/sha512/066b8a9cbf3fe710704b56af2917279f32cd3cef69808bb56872d367061402832dc1cbb01988b35652751e66c937d29a0190b98bfcd846a50fd80392b5a7e1bd +nghttp2-1.52.0.tar.bz2/md5/bde5874bd8e7e8be3512a621de27b9d5 +nghttp2-1.52.0.tar.bz2/sha512/019ec7a904d1baf8755ffcea0b38acf45ea9c6829d989a530ab35807338ba78d3328b86eebb3106b8372b7a8c51b466974d423e0cd786b6d6d020f0840c160bf +nghttp2.v1.52.0+0.aarch64-apple-darwin.tar.gz/md5/e3d9e07029e184cc55b7e0c4d2e27c7f +nghttp2.v1.52.0+0.aarch64-apple-darwin.tar.gz/sha512/cd098db984f751b00d2cc99d7f7eba0fa830ba178dd85a9dfa679a591e62d57364dcfd74e6a55ef513a0436a8e520b1a5474d4bfa9a8bdcd70e398482b7c9985 +nghttp2.v1.52.0+0.aarch64-linux-gnu.tar.gz/md5/73fe75f3cfa2bd3e804ea39a4eb884a9 +nghttp2.v1.52.0+0.aarch64-linux-gnu.tar.gz/sha512/71f4b2a23ba148b66432797b0db954dbd98fc900045d4572f488b43779aae125f71929e5bba6bbadd30c7998a133c5e5beb70888968bf3b01bb5fe9c9ea0e451 +nghttp2.v1.52.0+0.aarch64-linux-musl.tar.gz/md5/736a24a7eee567851a965558e31489fb +nghttp2.v1.52.0+0.aarch64-linux-musl.tar.gz/sha512/ab36182b04a590b092fae9e3a912a87467e8b01ad40a628a1d2e52910ee513ab327d5d2836df598d5aa8203f60a605d19d0b9636eb35d12a84a1c9d87124604b +nghttp2.v1.52.0+0.armv6l-linux-gnueabihf.tar.gz/md5/56fd32e8d77d4c9d9e2355565f4db19b +nghttp2.v1.52.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/85718e0e5cee35d91a8684ea33d8f965bb30d62dbd6b74a574a2fbc4c1027b1ef23ef68f1dec3f037fa6c5739287329567df9591a69f8f23b23fab2516a0b644 +nghttp2.v1.52.0+0.armv6l-linux-musleabihf.tar.gz/md5/283273d3bf4d53b56d12ef6af2e72f20 +nghttp2.v1.52.0+0.armv6l-linux-musleabihf.tar.gz/sha512/5c1d92cbf5f2f4e1ceb4ee13634c0bceb6ca28abaf9d87cc673f264d274bb96aa095648295e9aa76f86eb0890a426f47c0b942e72610daf722ed8e86b5f0df69 +nghttp2.v1.52.0+0.armv7l-linux-gnueabihf.tar.gz/md5/d7ae84e5365759a42d0fe0360f679b61 +nghttp2.v1.52.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/63212e3ad94d2bc54ca9ebd452d8de8e67aa53c03a3b3033d36da765303e714d8d5c24156ea4fb985acc72fe52e2977e8e8a658cdd9409bd41ecf401c08c1aee +nghttp2.v1.52.0+0.armv7l-linux-musleabihf.tar.gz/md5/a6ad0f25f43b7f1832faeaaadf683ed4 +nghttp2.v1.52.0+0.armv7l-linux-musleabihf.tar.gz/sha512/64b9075c0d819288345d53c5ce88b360d2ca4d24c3d2e81fb53c55f86054b1a3e95d7831b363a4100965cdbf479268a5993d66ef59089a219a97b4151d8fef60 +nghttp2.v1.52.0+0.i686-linux-gnu.tar.gz/md5/9781f6eeb4d24a291d6737e59e74edc1 +nghttp2.v1.52.0+0.i686-linux-gnu.tar.gz/sha512/2b542cb67e78993ef881694dc50c980b57db3761c5f4e11c381afb1b31d1fb8ab0a8b20e1279303a602c07912f21e8ef9d732366b76ab3f356a74b444a5dc78c +nghttp2.v1.52.0+0.i686-linux-musl.tar.gz/md5/08603b9364179ab4cbe0637b9b1b63b5 +nghttp2.v1.52.0+0.i686-linux-musl.tar.gz/sha512/0a5b79709482548c6a713843b670695b4b13d2b219b592d029719da0b4187fe884798fb44e2c511c300f02bab03f2b0b289d49d6256e3ce0b9602a66ea2382bd +nghttp2.v1.52.0+0.i686-w64-mingw32.tar.gz/md5/1abdf0cad466ed0ca0da137809999d8e +nghttp2.v1.52.0+0.i686-w64-mingw32.tar.gz/sha512/04680895ead989fda56b284d8963e7ca31680492c8f77f4c6bd7ca03b9a66ee7529b78cf35e07b2e106f43c9aa543dffd4081b034339803ba95021293d3df997 +nghttp2.v1.52.0+0.powerpc64le-linux-gnu.tar.gz/md5/ae411e40e24cb3f3b07fe8de211b58c6 +nghttp2.v1.52.0+0.powerpc64le-linux-gnu.tar.gz/sha512/7433502d76646e5761ea2707fa65ea5a412c513c70908a4d9ceb504f08121b1f39bcff984543370c221814785b7064f85dedc777a22df5e30a64a64e510e0978 +nghttp2.v1.52.0+0.x86_64-apple-darwin.tar.gz/md5/59f0de0affaa17898e837b5074de68fc +nghttp2.v1.52.0+0.x86_64-apple-darwin.tar.gz/sha512/e639c813373b17d95220640ec2a568e9731cfc32df826610357ec9ff8e9d7e7abe10291140eaeb9342ae69215798bf3f999db7647c23efb4f815b54f4da9cfe4 +nghttp2.v1.52.0+0.x86_64-linux-gnu.tar.gz/md5/6bc8501392d47b349c7463e984dc5909 +nghttp2.v1.52.0+0.x86_64-linux-gnu.tar.gz/sha512/522cc2a8464ee5770c01b83a6b4ecbbcce322efffbd738f7c907643fe85342e785bbc805028d41c2b7404d6241168d1ab37a9db15018623c265b53905bcf060f +nghttp2.v1.52.0+0.x86_64-linux-musl.tar.gz/md5/725a6adc23880b28303017597b974535 +nghttp2.v1.52.0+0.x86_64-linux-musl.tar.gz/sha512/ede5a34b7f71310e4c3cd99b9b61b2453db5dc8117675de12adb1e68c9283cdf821614f49f4d04bdd3b0f17d51a52972ec1e226d0dbdc5462b1a4a1fcc9f39e7 +nghttp2.v1.52.0+0.x86_64-unknown-freebsd.tar.gz/md5/a2b89913c1057ff67e7be6086619a65f +nghttp2.v1.52.0+0.x86_64-unknown-freebsd.tar.gz/sha512/6b4efd2a0807f19cecf1f1e97b23ade11ed39f651e29586bb21185e17d0c50dcb63e26233ff994bfa934b383468e29f680b1ebe0cc2a2dd09768b14dead399a4 +nghttp2.v1.52.0+0.x86_64-w64-mingw32.tar.gz/md5/e1c8ec6ec2d69b2ac64b114ebf09f8b4 +nghttp2.v1.52.0+0.x86_64-w64-mingw32.tar.gz/sha512/cb43cb138f14717501e852ed388a44d41012e2bb70b6887584b37b4e0f42827d74f17ea85ba4aa0bc09d623dedeef73eee80815c1db2b6858b31251feb0b5580 diff --git a/deps/nghttp2.version b/deps/nghttp2.version index e87b6781433ad..200e08bf4bfd9 100644 --- a/deps/nghttp2.version +++ b/deps/nghttp2.version @@ -3,4 +3,4 @@ NGHTTP2_JLL_NAME := nghttp2 ## source build -NGHTTP2_VER := 1.48.0 +NGHTTP2_VER := 1.52.0 diff --git a/stdlib/nghttp2_jll/Project.toml b/stdlib/nghttp2_jll/Project.toml index 32ea7d0f34134..b8a9394c50e37 100644 --- a/stdlib/nghttp2_jll/Project.toml +++ b/stdlib/nghttp2_jll/Project.toml @@ -1,6 +1,6 @@ name = "nghttp2_jll" uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.48.0+0" +version = "1.52.0+0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/nghttp2_jll/test/runtests.jl b/stdlib/nghttp2_jll/test/runtests.jl index d752251f98ebc..2f9af6d6a3338 100644 --- a/stdlib/nghttp2_jll/test/runtests.jl +++ b/stdlib/nghttp2_jll/test/runtests.jl @@ -11,5 +11,5 @@ end @testset "nghttp2_jll" begin info = unsafe_load(ccall((:nghttp2_version,libnghttp2), Ptr{nghttp2_info}, (Cint,), 0)) - @test VersionNumber(unsafe_string(info.version_str)) == v"1.48.0" + @test VersionNumber(unsafe_string(info.version_str)) == v"1.52.0" end From 832a67c3a3aa3a3de6a2aa1789b829d942dbac80 Mon Sep 17 00:00:00 2001 From: originalsouth Date: Tue, 28 Feb 2023 05:37:47 +0100 Subject: [PATCH 180/775] Cover binomial cases for large K (BigInt) (#48073) * Cover binomial cases for large K (BigInt) Co-authored-by: Sebastian Stock <42280794+sostock@users.noreply.github.com> --- base/gmp.jl | 12 ++++++++++-- test/combinatorics.jl | 10 ++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/base/gmp.jl b/base/gmp.jl index 76821716c0195..69926f4ad0d06 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -707,8 +707,16 @@ end factorial(x::BigInt) = isneg(x) ? BigInt(0) : MPZ.fac_ui(x) -binomial(n::BigInt, k::UInt) = MPZ.bin_ui(n, k) -binomial(n::BigInt, k::Integer) = k < 0 ? BigInt(0) : binomial(n, UInt(k)) +function binomial(n::BigInt, k::Integer) + k < 0 && return BigInt(0) + k <= typemax(Culong) && return binomial(n, Culong(k)) + n < 0 && return isodd(k) ? -binomial(k - n - 1, k) : binomial(k - n - 1, k) + κ = n - k + κ < 0 && return BigInt(0) + κ <= typemax(Culong) && return binomial(n, Culong(κ)) + throw(OverflowError("Computation would exceed memory")) +end +binomial(n::BigInt, k::Culong) = MPZ.bin_ui(n, k) ==(x::BigInt, y::BigInt) = cmp(x,y) == 0 ==(x::BigInt, i::Integer) = cmp(x,i) == 0 diff --git a/test/combinatorics.jl b/test/combinatorics.jl index b04259f397304..f8fe4e0bd0829 100644 --- a/test/combinatorics.jl +++ b/test/combinatorics.jl @@ -16,6 +16,16 @@ using Random: randcycle @test binomial(Int64(67), Int64(29)) == binomial(BigInt(67), BigInt(29)) == 7886597962249166160 @test binomial(Int128(131), Int128(62)) == binomial(BigInt(131), BigInt(62)) == 157311720980559117816198361912717812000 @test_throws OverflowError binomial(Int64(67), Int64(30)) + + #Issue 48072 + ∐ = parse(BigInt, "1" * "0"^13 * "666" * "0"^13 * "1") + @test binomial(∐, ∐ - 1) == ∐ + @test binomial(∐, ∐ - 2) == 500000000000066600000000002218280000000000033300000000000000 + @test binomial(∐, ∐ - 3) == binomial(∐, 3) + @test binomial(-big(2), ∐ - 3) == 1000000000000066599999999999999 + @test_throws OverflowError binomial(big(2)^65, big(2)^64) + @test_throws OverflowError binomial(-big(2)^65, big(2)^64) + @test binomial(∐, 2 * ∐) == BigInt(0) end @testset "permutations" begin From 85709517be408f32cf102234b906f3d918fafdf2 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 28 Feb 2023 12:26:50 +0100 Subject: [PATCH 181/775] add a type assert to logging to prevent Base.require invalidation (#48810) --- base/logging.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/logging.jl b/base/logging.jl index dd45d05a084af..c2f243bcabf46 100644 --- a/base/logging.jl +++ b/base/logging.jl @@ -445,7 +445,7 @@ function default_group_code(file) QuoteNode(default_group(file)) # precompute if we can else ref = Ref{Symbol}() # memoized run-time execution - :(isassigned($ref) ? $ref[] : $ref[] = default_group(something($file, ""))) + :(isassigned($ref) ? $ref[] : $ref[] = default_group(something($file, ""))::Symbol) end end From d6a77b607b7f021c0e03e258bdc9bdafd79cf097 Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Tue, 28 Feb 2023 09:28:45 -0500 Subject: [PATCH 182/775] Simplify name reversal, add # to separate name from number (#48710) --- src/cgutils.cpp | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 7acfb24b91615..f5dce85053ef5 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -325,29 +325,29 @@ static Value *julia_pgv(jl_codectx_t &ctx, const char *cname, void *addr) static Value *julia_pgv(jl_codectx_t &ctx, const char *prefix, jl_sym_t *name, jl_module_t *mod, void *addr) { // emit a GlobalVariable for a jl_value_t, using the prefix, name, and module to - // to create a readable name of the form prefixModA.ModB.name - size_t len = strlen(jl_symbol_name(name)) + strlen(prefix) + 1; + // to create a readable name of the form prefixModA.ModB.name# + // reverse-of-reverse algorithm + std::string finalname; + StringRef name_str(jl_symbol_name(name)); + finalname.resize(name_str.size() + 1); + finalname[0] = '#'; + std::reverse_copy(name_str.begin(), name_str.end(), finalname.begin() + 1); jl_module_t *parent = mod, *prev = NULL; - while (parent != NULL && parent != prev) { - len += strlen(jl_symbol_name(parent->name))+1; + while (parent && parent != prev) { + size_t orig_end = finalname.size() + 1; + StringRef parent_name(jl_symbol_name(parent->name)); + finalname.resize(orig_end + parent_name.size()); + finalname[orig_end - 1] = '.'; + std::reverse_copy(parent_name.begin(), parent_name.end(), finalname.begin() + orig_end); prev = parent; parent = parent->parent; } - char *fullname = (char*)alloca(len); - strcpy(fullname, prefix); - len -= strlen(jl_symbol_name(name)) + 1; - strcpy(fullname + len, jl_symbol_name(name)); - parent = mod; - prev = NULL; - while (parent != NULL && parent != prev) { - size_t part = strlen(jl_symbol_name(parent->name)) + 1; - strcpy(fullname + len - part, jl_symbol_name(parent->name)); - fullname[len - 1] = '.'; - len -= part; - prev = parent; - parent = parent->parent; - } - return julia_pgv(ctx, fullname, addr); + size_t orig_end = finalname.size(); + StringRef prefix_name(prefix); + finalname.resize(orig_end + prefix_name.size()); + std::reverse_copy(prefix_name.begin(), prefix_name.end(), finalname.begin() + orig_end); + std::reverse(finalname.begin(), finalname.end()); + return julia_pgv(ctx, finalname.c_str(), addr); } static JuliaVariable *julia_const_gv(jl_value_t *val); From fd45943e993ea851edcc4b1f2cc7e002438be07e Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 28 Feb 2023 11:14:40 -0700 Subject: [PATCH 183/775] Add libjulia-internal to ORCJIT symbol resolution (#48712) We need to make sure that libjulia-internal is loaded and placed first in the DynamicLibrary search order so that calls to runtime intrinsics are resolved to the correct library when multiple libjulia-*'s have been loaded (e.g. when we `ccall` into a PackageCompiler.jl-created shared library). --- src/jitlayers.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index b489665f5629d..95c9cf2aaa86c 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -1288,13 +1288,29 @@ JuliaOJIT::JuliaOJIT() }); #endif + std::string ErrorStr; + + // Make sure that libjulia-internal is loaded and placed first in the + // DynamicLibrary order so that calls to runtime intrinsics are resolved + // to the correct library when multiple libjulia-*'s have been loaded + // (e.g. when we `ccall` into a PackageCompiler.jl-created shared library) + sys::DynamicLibrary libjulia_internal_dylib = sys::DynamicLibrary::addPermanentLibrary( + jl_libjulia_internal_handle, &ErrorStr); + if(!ErrorStr.empty()) + report_fatal_error(llvm::Twine("FATAL: unable to dlopen libjulia-internal\n") + ErrorStr); + // Make sure SectionMemoryManager::getSymbolAddressInProcess can resolve // symbols in the program as well. The nullptr argument to the function // tells DynamicLibrary to load the program, not a library. - std::string ErrorStr; if (sys::DynamicLibrary::LoadLibraryPermanently(nullptr, &ErrorStr)) report_fatal_error(llvm::Twine("FATAL: unable to dlopen self\n") + ErrorStr); + GlobalJD.addGenerator( + std::make_unique( + libjulia_internal_dylib, + DL.getGlobalPrefix(), + orc::DynamicLibrarySearchGenerator::SymbolPredicate())); + GlobalJD.addGenerator( cantFail(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess( DL.getGlobalPrefix()))); From 127fb73ef5804e0c040aefadb47eb07763e36d9c Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Tue, 28 Feb 2023 14:47:46 -0500 Subject: [PATCH 184/775] Update MbedTLS to 2.28.2 (#48806) * Update MbedTLS to 2.28.2 * Update deps/mbedtls.mk * Update mbedtls checksum --- deps/checksums/mbedtls | 68 ++++++++++++++--------------- deps/mbedtls.mk | 2 +- deps/mbedtls.version | 2 +- stdlib/MbedTLS_jll/Project.toml | 2 +- stdlib/MbedTLS_jll/test/runtests.jl | 2 +- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/deps/checksums/mbedtls b/deps/checksums/mbedtls index 723b9012bfe00..d0b43ad80ea70 100644 --- a/deps/checksums/mbedtls +++ b/deps/checksums/mbedtls @@ -1,34 +1,34 @@ -MbedTLS.v2.28.0+0.aarch64-apple-darwin.tar.gz/md5/ba33f960c7bcc3fda818c84f5e716df7 -MbedTLS.v2.28.0+0.aarch64-apple-darwin.tar.gz/sha512/3878531424317954417d09090b0a7618c6c0a6907bb04db34aef37d55a033972371455fcffca548ac03be41c0b0d1f8e51a9fe6e8f8fb4d8ef4fcbf91f15b3ea -MbedTLS.v2.28.0+0.aarch64-linux-gnu.tar.gz/md5/9e7c78fc7c39fd19dcb170d57c8c0ec6 -MbedTLS.v2.28.0+0.aarch64-linux-gnu.tar.gz/sha512/59eaeec1a772265e62fa4049e0bc8c96cd7403d954213ac6098921acf6e128b624d6bc1ba5c6062c88ecb92aa8bf9d0a06e365eee241b6516ef0bfe2b4c47188 -MbedTLS.v2.28.0+0.aarch64-linux-musl.tar.gz/md5/44f939956834d5d8130ccb3bd5962b0c -MbedTLS.v2.28.0+0.aarch64-linux-musl.tar.gz/sha512/f9797a44851222c005fd4068df6e0bcee68133c9a48e19e16d188b8a6927be56c620fec83264398d682eb5c89b7f01683e5898d3cbcb7aecf53e5ce678464db6 -MbedTLS.v2.28.0+0.armv6l-linux-gnueabihf.tar.gz/md5/fc07035dddd51e9c57e62edfc3fc5691 -MbedTLS.v2.28.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/ffb707ba7439050862654316b4388f52e8bd09bbeb7076cf6cdc924cb60c61f871c01ccfe14e1ae1e62a5733490487324ba60e8545d60902f3317039264db83b -MbedTLS.v2.28.0+0.armv6l-linux-musleabihf.tar.gz/md5/fc54575519130bd468ee4dbe23da0ea9 -MbedTLS.v2.28.0+0.armv6l-linux-musleabihf.tar.gz/sha512/d4b9e1bd8877f7d93d1b4e0d1c4c3d4e5d2af6920e39222667e689ec84cf9817988c91a826755a734a60ce05fed913e5421b8aa9980f257450da7f51c5e9342a -MbedTLS.v2.28.0+0.armv7l-linux-gnueabihf.tar.gz/md5/0753a99f4645ba7e1ceb27a03c65a107 -MbedTLS.v2.28.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/a7a65338ee6f93117d44975651d77c351f0c919a3ae2eea6e220719dd084f71617946adf04a08a82d55c22af0275d21fce3c692becf87ccf2d932c8aa32af7af -MbedTLS.v2.28.0+0.armv7l-linux-musleabihf.tar.gz/md5/ff335caa1cec22366cfa2c2bf87f61f7 -MbedTLS.v2.28.0+0.armv7l-linux-musleabihf.tar.gz/sha512/a3ff7d53b45134165347dec209bc27f48be984b4fb58ddd54286a146b837d038ab21e22033f1e0713d359c72adc0b97e979532ebaa734495eb88bfceaf3c2155 -MbedTLS.v2.28.0+0.i686-linux-gnu.tar.gz/md5/c4c9728ee9d875685765eb4c9c3bf731 -MbedTLS.v2.28.0+0.i686-linux-gnu.tar.gz/sha512/214142ee7ca3a5b447a97928ffcbe0389fbb8c1fa68de387656e5c0e4406f02411e4183fb051b2107600b222bd5279b9fd3a5aec43a9d97a9556b08c5338cb7b -MbedTLS.v2.28.0+0.i686-linux-musl.tar.gz/md5/2684f2bc8a04234ae67603150e6d0917 -MbedTLS.v2.28.0+0.i686-linux-musl.tar.gz/sha512/a533afd26893464bee62dbfa9babf6e4e1119a4be31ecb242e2ff28f5f6e3a3969057e2ce653c98c1b8d2a19e340df7a17dac8693fce270399df92cfbf3a32ca -MbedTLS.v2.28.0+0.i686-w64-mingw32.tar.gz/md5/f205fd351e94f42cd38d34d3eff6e69a -MbedTLS.v2.28.0+0.i686-w64-mingw32.tar.gz/sha512/cfdb819d3e6fa9ce3985e29ac733c2af6c988230ae49bbdc13f0fc234e82444d17ce5da4d3b6d8cc6ac45ea4a999f0ce03ac42533223c87bea066a371487ef1e -MbedTLS.v2.28.0+0.powerpc64le-linux-gnu.tar.gz/md5/41b1f61ebda30a8e8f02dcd955ae0d40 -MbedTLS.v2.28.0+0.powerpc64le-linux-gnu.tar.gz/sha512/25b62106404cb3b9be3e0f778ed953bdcf9d18cb289be823f97f7a1759012c84cfe7240fc936f2e6e858273ce2022d75ecc2554d5696cea110eda6d059362416 -MbedTLS.v2.28.0+0.x86_64-apple-darwin.tar.gz/md5/e7b286dac94bef06915930180b2d3bac -MbedTLS.v2.28.0+0.x86_64-apple-darwin.tar.gz/sha512/a2acaacb77ca6e2704144d8d99e51df49b1fc69c8751e43973e0c41219d023676d35ae05bd4ff7a3680dc0edf5438e51b67baa76f5b78947560dcc420623a3da -MbedTLS.v2.28.0+0.x86_64-linux-gnu.tar.gz/md5/39662265088efadb142fdc7255a0b7a3 -MbedTLS.v2.28.0+0.x86_64-linux-gnu.tar.gz/sha512/a3648c78bebf4c024ddf491965cb7707df887ce10dec6f9e42eb6493bc7d1220e5b23c53f5e4e73dfe94e8d8dcf35ffc6860d1992deb9b63a0c4691d4167e59f -MbedTLS.v2.28.0+0.x86_64-linux-musl.tar.gz/md5/1fbe9f2593bc11af031075b58a108bc8 -MbedTLS.v2.28.0+0.x86_64-linux-musl.tar.gz/sha512/d185ced64d471fba9ae1aa495b2eba0e60738e8e5ef918670b1c40cc8981389ecd48e4f17506229bafab4a11f7a257d3d544cfe87ad198482778931c2a7a8aa9 -MbedTLS.v2.28.0+0.x86_64-unknown-freebsd.tar.gz/md5/26beed62ee2abe8c6e52c1dbddbe0b1a -MbedTLS.v2.28.0+0.x86_64-unknown-freebsd.tar.gz/sha512/f04a417d99e3b908383d3c14cf8512b2f13e4b226d07235e2334090aadb6aecce40a23ae8f8df9c0ed9618707e839aaac6de64d5fee6d7e3955b290bc564d3a2 -MbedTLS.v2.28.0+0.x86_64-w64-mingw32.tar.gz/md5/cc55fe5537719aa8bf3bbee981c01413 -MbedTLS.v2.28.0+0.x86_64-w64-mingw32.tar.gz/sha512/3436647e81fdb9db138063229f20f47e2c8405e6379ca3e7cf38fb9fde84d2b6618a5f29b8df19cbffe75af7f99e00e9583d67be7b53dcce27bff453b96dcf13 -mbedtls-2.28.0.tar.gz/md5/d64054513df877458493dbb28e2935fa -mbedtls-2.28.0.tar.gz/sha512/907867edf532ba3b099f4fb7ce31f5773ceceb072a8d067b1d830e879d541f92f401d64f13bbe6b4eb0845e58bb765d7d28896be414bb0fc7ac5b3876066be5f +MbedTLS.v2.28.2+0.aarch64-apple-darwin.tar.gz/md5/ef83fb4706100ee678cd8af3f7a5c762 +MbedTLS.v2.28.2+0.aarch64-apple-darwin.tar.gz/sha512/03dda8cc9afa3d79c3c733e45c77891e75d939dc2bcca5ba8eb7aa3bd01fb52011ea9323df9cf7294fe6dcf87eb86c1b1c4b2f3b8af6116929b3371698559fe4 +MbedTLS.v2.28.2+0.aarch64-linux-gnu.tar.gz/md5/ac46c3840d2d0cc7c573f31c2f3d0d61 +MbedTLS.v2.28.2+0.aarch64-linux-gnu.tar.gz/sha512/bb458f1dc9b8684a38f603136ee4ba1c51b47f5047c5a5cfe2c552be266e79dfcd8243b216b0831abf24390eeb6f4524bc7e43b2642eb2ad0227399222cd0d8a +MbedTLS.v2.28.2+0.aarch64-linux-musl.tar.gz/md5/d74732e0bbcd03666243605e60bb345a +MbedTLS.v2.28.2+0.aarch64-linux-musl.tar.gz/sha512/90b0699477b697b94c0ab1ba0607fb3e1cd40d66a80a51cb1e0f3b927de03ba201e7e280d453db672e6265db5b07d0145846e53ddbcb4b550afcabef1716470b +MbedTLS.v2.28.2+0.armv6l-linux-gnueabihf.tar.gz/md5/65ce7c51884b50dcb8343a945644b862 +MbedTLS.v2.28.2+0.armv6l-linux-gnueabihf.tar.gz/sha512/e9df753e9f3a08fd645b15422be7cc0ec3aeac3f8d5f76e0c4c5ec24c54e1b653db320ed0c6799411802a05801241a5363bb449a8765fda7856413c7e3297721 +MbedTLS.v2.28.2+0.armv6l-linux-musleabihf.tar.gz/md5/7b7fc8eafc95416d75e3f1bfb2640e09 +MbedTLS.v2.28.2+0.armv6l-linux-musleabihf.tar.gz/sha512/68362114808fb4f986dea673ef1c7f104caad8233bed1c7f6a365d5d69bb7f7c92b234d6b1bfa5b014e7096411841c115a5cfe9932ae9ce642293cab962f8d38 +MbedTLS.v2.28.2+0.armv7l-linux-gnueabihf.tar.gz/md5/4a477379b15fafbf0c05435f5ab370ac +MbedTLS.v2.28.2+0.armv7l-linux-gnueabihf.tar.gz/sha512/fd34b475bf94b411e3155f5a5166d1ad081fef3622d7b99f4915b592d4235f63a0b910e0559ba2a0c3d596df9ccc2d7ecb61984091debb20bd4b995942857132 +MbedTLS.v2.28.2+0.armv7l-linux-musleabihf.tar.gz/md5/fc6551ef5f189010a84230dd48f6bdfe +MbedTLS.v2.28.2+0.armv7l-linux-musleabihf.tar.gz/sha512/d3a7199f3e1ffb1c289c5f0a4384f3b5d1af6e868eb1081d66d6cbfc60e6415e68a7e22afb497f2e7c7900678a19bf1ba2a4c888efa1019c03bce376af62154c +MbedTLS.v2.28.2+0.i686-linux-gnu.tar.gz/md5/335c3ac146bbe8cd862e4737bc362037 +MbedTLS.v2.28.2+0.i686-linux-gnu.tar.gz/sha512/f12ef67a92af27f4021f73171cdf2ef5558f734fcb185e4417fd7e16752dafe3f75be4291854b5ce346abda674252d58064d9186122eb4f9b15ff89156d221ce +MbedTLS.v2.28.2+0.i686-linux-musl.tar.gz/md5/435b864b02d1d2c96e5d8dc32b433ae1 +MbedTLS.v2.28.2+0.i686-linux-musl.tar.gz/sha512/52e3a79a70b3ff4617c93cafdeb702105c13b34687fc0fa31eebc91aa5cacea356d5b6a6bdbbfd81417d77debe256ea8f0f2a43c8d140154099bde097740dce7 +MbedTLS.v2.28.2+0.i686-w64-mingw32.tar.gz/md5/a238801f7e0d14f4b693aa4b74645263 +MbedTLS.v2.28.2+0.i686-w64-mingw32.tar.gz/sha512/431db4c388d3c52b08795d6fee6e6696cf383506a603816d6a63dc3571dbdc2b673837a1df1d9003c5009f8f8dc6eaaef3f80aaea396dc2fdf54b7e6a3c6aad6 +MbedTLS.v2.28.2+0.powerpc64le-linux-gnu.tar.gz/md5/26c8f09aa65e5b70be528311519d4376 +MbedTLS.v2.28.2+0.powerpc64le-linux-gnu.tar.gz/sha512/2d47567388b8554ce7714f4ded013fcbffbf94726dbc6a1b7287dc17b27d1fa35baba55cf7dac17c555892a5f4c74119afdf552b42b0e8f80f26621adaa4dbca +MbedTLS.v2.28.2+0.x86_64-apple-darwin.tar.gz/md5/dfc263208b1a8d4c29b4ec3b6f10e5ce +MbedTLS.v2.28.2+0.x86_64-apple-darwin.tar.gz/sha512/3b2941c4b151206a56a9a795f0f30519676ea4bc0c93f66b419b15568edc91bb976954f584116accb7f9bd067580712e61b3c580a249332640e27e6346ca51ff +MbedTLS.v2.28.2+0.x86_64-linux-gnu.tar.gz/md5/94b908036eecbe59372722b41f0b1985 +MbedTLS.v2.28.2+0.x86_64-linux-gnu.tar.gz/sha512/c37a4c34eb450bd716c076c4105bd6022892731c470d64a854ac0fca6653dcf5a70b23982050e7d82cdfd67d02902d9efe4c94d2cf5e0d29d497c3c5ac03f8e8 +MbedTLS.v2.28.2+0.x86_64-linux-musl.tar.gz/md5/217866be499144eeb2e0944b0b60cc09 +MbedTLS.v2.28.2+0.x86_64-linux-musl.tar.gz/sha512/144180e1968da627c92173277a130283aea711157a04a2655786658234232e397985f63d5407166377fc5f38a7447c19797c51b66a9c4b1773601d9e7e01d0e0 +MbedTLS.v2.28.2+0.x86_64-unknown-freebsd.tar.gz/md5/74316c624c8106faf7c04e05149b5c38 +MbedTLS.v2.28.2+0.x86_64-unknown-freebsd.tar.gz/sha512/9eca254c9b663b2f5799705c2e0aebb5529a7ff7759b0f3b67516e622dd4561169fface1d08340666453e779133498eacb8ef2dae1ef6332ceb4d8052d3614d3 +MbedTLS.v2.28.2+0.x86_64-w64-mingw32.tar.gz/md5/cdd28912607781f5e6ea6cad73c7dba2 +MbedTLS.v2.28.2+0.x86_64-w64-mingw32.tar.gz/sha512/e5793778d57b725a0cab48dd7e8f45022699b654bb8e890620efa73628140e453c80601e43647a700d6090a4b66d3c30b11634c4224c016c11c7bfde6b8a1b2a +mbedtls-2.28.2.tar.gz/md5/421c47c18ef46095e3ad38ffc0543e11 +mbedtls-2.28.2.tar.gz/sha512/93cdb44f764b200131b8dbefb9363e5fa38760eaf01473a512f93673cc55db3515830e16b813e03b39cb819323ad78cee4cb7f3fa85861ec5e72e0f89541c7fc diff --git a/deps/mbedtls.mk b/deps/mbedtls.mk index 0f654dfd04c58..b4147c2c2684e 100644 --- a/deps/mbedtls.mk +++ b/deps/mbedtls.mk @@ -3,7 +3,7 @@ include $(SRCDIR)/mbedtls.version ifneq ($(USE_BINARYBUILDER_MBEDTLS), 1) MBEDTLS_SRC = mbedtls-$(MBEDTLS_VER) -MBEDTLS_URL = https://github.com/ARMmbed/mbedtls/archive/v$(MBEDTLS_VER).tar.gz +MBEDTLS_URL = https://github.com/Mbed-TLS/mbedtls/archive/v$(MBEDTLS_VER).tar.gz MBEDTLS_OPTS := $(CMAKE_COMMON) -DUSE_SHARED_MBEDTLS_LIBRARY=ON \ -DUSE_STATIC_MBEDTLS_LIBRARY=OFF -DENABLE_PROGRAMS=OFF -DCMAKE_BUILD_TYPE=Release diff --git a/deps/mbedtls.version b/deps/mbedtls.version index eaf3bca011e1f..f262476af1684 100644 --- a/deps/mbedtls.version +++ b/deps/mbedtls.version @@ -2,4 +2,4 @@ MBEDTLS_JLL_NAME := MbedTLS ## source build -MBEDTLS_VER := 2.28.0 +MBEDTLS_VER := 2.28.2 diff --git a/stdlib/MbedTLS_jll/Project.toml b/stdlib/MbedTLS_jll/Project.toml index 00a6b29426d91..2e8d0d384f88a 100644 --- a/stdlib/MbedTLS_jll/Project.toml +++ b/stdlib/MbedTLS_jll/Project.toml @@ -1,6 +1,6 @@ name = "MbedTLS_jll" uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.0+0" +version = "2.28.2+0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/MbedTLS_jll/test/runtests.jl b/stdlib/MbedTLS_jll/test/runtests.jl index b731d7f833043..2d82fa564cd18 100644 --- a/stdlib/MbedTLS_jll/test/runtests.jl +++ b/stdlib/MbedTLS_jll/test/runtests.jl @@ -6,5 +6,5 @@ using Test, Libdl, MbedTLS_jll vstr = zeros(UInt8, 32) ccall((:mbedtls_version_get_string, libmbedcrypto), Cvoid, (Ref{UInt8},), vstr) vn = VersionNumber(unsafe_string(pointer(vstr))) - @test vn == v"2.28.0" + @test vn == v"2.28.2" end From c7e4b192e0a49e3bbecc9e8c908eacd139cfea3f Mon Sep 17 00:00:00 2001 From: Tim Stahlhut Date: Tue, 28 Feb 2023 16:48:46 -0500 Subject: [PATCH 185/775] Fix make check-whitespace under Cygwin --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ad4ad4ad871c2..5a2f8b0210a03 100644 --- a/Makefile +++ b/Makefile @@ -116,7 +116,7 @@ check-whitespace: ifneq ($(NO_GIT), 1) @# Append the directory containing the julia we just built to the end of `PATH`, @# to give us the best chance of being able to run this check. - @PATH="$(PATH):$(dir $(JULIA_EXECUTABLE))" $(JULIAHOME)/contrib/check-whitespace.jl + @PATH="$(PATH):$(dir $(JULIA_EXECUTABLE))" $(JULIA_EXECUTABLE) $(call cygpath_w,$(JULIAHOME)/contrib/check-whitespace.jl) else $(warn "Skipping whitespace check because git is unavailable") endif From 61960d74a3b36a97c7bf38bb0ae8345da98e5cde Mon Sep 17 00:00:00 2001 From: stahta01 Date: Tue, 28 Feb 2023 17:33:36 -0500 Subject: [PATCH 186/775] Update Makefile Co-authored-by: Elliot Saba --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5a2f8b0210a03..1fe101f939090 100644 --- a/Makefile +++ b/Makefile @@ -116,7 +116,7 @@ check-whitespace: ifneq ($(NO_GIT), 1) @# Append the directory containing the julia we just built to the end of `PATH`, @# to give us the best chance of being able to run this check. - @PATH="$(PATH):$(dir $(JULIA_EXECUTABLE))" $(JULIA_EXECUTABLE) $(call cygpath_w,$(JULIAHOME)/contrib/check-whitespace.jl) + @PATH="$(PATH):$(dir $(JULIA_EXECUTABLE))" julia $(call cygpath_w,$(JULIAHOME)/contrib/check-whitespace.jl) else $(warn "Skipping whitespace check because git is unavailable") endif From bdcd5e2c6d0fdea879a961f1a72d774b743cc846 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 28 Feb 2023 20:09:15 -0500 Subject: [PATCH 187/775] gf: cache cache_with_orig decision (#48833) Memoizing this can save a lot of repeated effort for queries such as `@which eltype(String)`. Otherwise we repeatedly try to check if `eltype(::Type)` is a good way to cache that result, even though it never gets better the more we check it. --- src/gf.c | 6 ++++++ src/jltypes.c | 8 +++++--- src/julia.h | 1 + src/method.c | 1 + 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/gf.c b/src/gf.c index 42990baf7ad24..78ffa1a4143ef 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1195,6 +1195,8 @@ static jl_method_instance_t *cache_method( } // TODO: maybe assert(jl_isa_compileable_sig(compilationsig, sparams, definition)); newmeth = jl_specializations_get_linfo(definition, (jl_value_t*)compilationsig, sparams); + if (newmeth->cache_with_orig) + cache_with_orig = 1; jl_tupletype_t *cachett = tt; jl_svec_t* guardsigs = jl_emptysvec; @@ -1261,6 +1263,10 @@ static jl_method_instance_t *cache_method( max_valid = max_valid2; cachett = compilationsig; } + else { + // do not revisit this decision + newmeth->cache_with_orig = 1; + } } // now scan `cachett` and ensure that `Type{T}` in the cache will be matched exactly by `typeof(T)` diff --git a/src/jltypes.c b/src/jltypes.c index cc10485e64247..5ce67085198ad 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2541,7 +2541,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_method_instance_type = jl_new_datatype(jl_symbol("MethodInstance"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(9, + jl_perm_symsvec(10, "def", "specTypes", "sparam_vals", @@ -2550,8 +2550,9 @@ void jl_init_types(void) JL_GC_DISABLED "callbacks", "cache", "inInference", + "cache_with_orig", "precompiled"), - jl_svec(9, + jl_svec(10, jl_new_struct(jl_uniontype_type, jl_method_type, jl_module_type), jl_any_type, jl_simplevector_type, @@ -2560,12 +2561,13 @@ void jl_init_types(void) JL_GC_DISABLED jl_any_type, jl_any_type, jl_bool_type, + jl_bool_type, jl_bool_type), jl_emptysvec, 0, 1, 3); // These fields should be constant, but Serialization wants to mutate them in initialization //const static uint32_t method_instance_constfields[1] = { 0x00000007 }; // (1<<0)|(1<<1)|(1<<2); - const static uint32_t method_instance_atomicfields[1] = { 0x00000148 }; // (1<<3)|(1<<6)|(1<<8); + const static uint32_t method_instance_atomicfields[1] = { 0x00000248 }; // (1<<3)|(1<<6)|(1<<9); //Fields 4 and 5 must be protected by method->write_lock, and thus all operations on jl_method_instance_t are threadsafe. TODO: except inInference //jl_method_instance_type->name->constfields = method_instance_constfields; jl_method_instance_type->name->atomicfields = method_instance_atomicfields; diff --git a/src/julia.h b/src/julia.h index 784f16485967c..4374c8a7ceeed 100644 --- a/src/julia.h +++ b/src/julia.h @@ -371,6 +371,7 @@ struct _jl_method_instance_t { jl_array_t *callbacks; // list of callback functions to inform external caches about invalidations _Atomic(struct _jl_code_instance_t*) cache; uint8_t inInference; // flags to tell if inference is running on this object + uint8_t cache_with_orig; // !cache_with_specTypes _Atomic(uint8_t) precompiled; // true if this instance was generated by an explicit `precompile(...)` call }; diff --git a/src/method.c b/src/method.c index d5f56a5921358..c5f50cdf883a5 100644 --- a/src/method.c +++ b/src/method.c @@ -449,6 +449,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_new_method_instance_uninit(void) mi->callbacks = NULL; jl_atomic_store_relaxed(&mi->cache, NULL); mi->inInference = 0; + mi->cache_with_orig = 0; jl_atomic_store_relaxed(&mi->precompiled, 0); return mi; } From 0608824bd9553514d71fa3a67d6c9fafecbe15be Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 28 Feb 2023 20:10:17 -0500 Subject: [PATCH 188/775] staticdata: make completeinfo memory-/gc-safe (#48832) There is actually almost no cases where `jl_alloc_svec_uninit` is safe, since if was safe, you would likely would prefer to use the `jl_svec` constructor instead. --- src/staticdata.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/staticdata.c b/src/staticdata.c index 4b947f38f6356..7f7b4882b994f 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -3229,7 +3229,7 @@ static jl_value_t *jl_validate_cache_file(ios_t *f, jl_array_t *depmods, uint64_ } // TODO?: refactor to make it easier to create the "package inspector" -static jl_value_t *jl_restore_package_image_from_stream(ios_t *f, jl_image_t *image, jl_array_t *depmods, int complete) +static jl_value_t *jl_restore_package_image_from_stream(ios_t *f, jl_image_t *image, jl_array_t *depmods, int completeinfo) { uint64_t checksum = 0; int64_t dataendpos = 0; @@ -3275,19 +3275,22 @@ static jl_value_t *jl_restore_package_image_from_stream(ios_t *f, jl_image_t *im // reinit ccallables jl_reinit_ccallable(&ccallable_list, base, NULL); arraylist_free(&ccallable_list); - if (complete) { - cachesizes_sv = jl_alloc_svec_uninit(7); - jl_svec_data(cachesizes_sv)[0] = jl_box_long(cachesizes.sysdata); - jl_svec_data(cachesizes_sv)[1] = jl_box_long(cachesizes.isbitsdata); - jl_svec_data(cachesizes_sv)[2] = jl_box_long(cachesizes.symboldata); - jl_svec_data(cachesizes_sv)[3] = jl_box_long(cachesizes.tagslist); - jl_svec_data(cachesizes_sv)[4] = jl_box_long(cachesizes.reloclist); - jl_svec_data(cachesizes_sv)[5] = jl_box_long(cachesizes.gvarlist); - jl_svec_data(cachesizes_sv)[6] = jl_box_long(cachesizes.fptrlist); + + if (completeinfo) { + cachesizes_sv = jl_alloc_svec(7); + jl_svecset(cachesizes_sv, 0, jl_box_long(cachesizes.sysdata)); + jl_svecset(cachesizes_sv, 1, jl_box_long(cachesizes.isbitsdata)); + jl_svecset(cachesizes_sv, 2, jl_box_long(cachesizes.symboldata)); + jl_svecset(cachesizes_sv, 3, jl_box_long(cachesizes.tagslist)); + jl_svecset(cachesizes_sv, 4, jl_box_long(cachesizes.reloclist)); + jl_svecset(cachesizes_sv, 5, jl_box_long(cachesizes.gvarlist)); + jl_svecset(cachesizes_sv, 6, jl_box_long(cachesizes.fptrlist)); restored = (jl_value_t*)jl_svec(8, restored, init_order, extext_methods, new_specializations, method_roots_list, ext_targets, edges, cachesizes_sv); - } else + } + else { restored = (jl_value_t*)jl_svec(2, restored, init_order); + } } } @@ -3301,16 +3304,16 @@ static void jl_restore_system_image_from_stream(ios_t *f, jl_image_t *image) jl_restore_system_image_from_stream_(f, image, NULL, checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } -JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(const char *buf, jl_image_t *image, size_t sz, jl_array_t *depmods, int complete) +JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(const char *buf, jl_image_t *image, size_t sz, jl_array_t *depmods, int completeinfo) { ios_t f; ios_static_buffer(&f, (char*)buf, sz); - jl_value_t *ret = jl_restore_package_image_from_stream(&f, image, depmods, complete); + jl_value_t *ret = jl_restore_package_image_from_stream(&f, image, depmods, completeinfo); ios_close(&f); return ret; } -JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *depmods, int complete) +JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *depmods, int completeinfo) { ios_t f; if (ios_file(&f, fname, 1, 0, 0, 0) == NULL) { @@ -3318,7 +3321,7 @@ JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *d "Cache file \"%s\" not found.\n", fname); } jl_image_t pkgimage = {}; - jl_value_t *ret = jl_restore_package_image_from_stream(&f, &pkgimage, depmods, complete); + jl_value_t *ret = jl_restore_package_image_from_stream(&f, &pkgimage, depmods, completeinfo); ios_close(&f); return ret; } @@ -3366,7 +3369,7 @@ JL_DLLEXPORT void jl_restore_system_image_data(const char *buf, size_t len) JL_SIGATOMIC_END(); } -JL_DLLEXPORT jl_value_t *jl_restore_package_image_from_file(const char *fname, jl_array_t *depmods, int complete) +JL_DLLEXPORT jl_value_t *jl_restore_package_image_from_file(const char *fname, jl_array_t *depmods, int completeinfo) { void *pkgimg_handle = jl_dlopen(fname, JL_RTLD_LAZY); if (!pkgimg_handle) { @@ -3418,7 +3421,7 @@ JL_DLLEXPORT jl_value_t *jl_restore_package_image_from_file(const char *fname, j } #endif - jl_value_t* mod = jl_restore_incremental_from_buf(pkgimg_data, &pkgimage, *plen, depmods, complete); + jl_value_t* mod = jl_restore_incremental_from_buf(pkgimg_data, &pkgimage, *plen, depmods, completeinfo); return mod; } From be70dabcff7017abeecb7f28d30a483d5b21402d Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 1 Mar 2023 11:29:43 +0900 Subject: [PATCH 189/775] effects: fix unknown effects (#48827) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `noinbounds` bit of `EFFECTS_UNKNOWN` should be `false` since arbitrary method may have `:boundscheck`. This commit also replaces all the usages of `EFFECTS_UNKNOWN′` by `Effects()` and renames it to `_EFFECTS_UNKNOWN` to indicate that it is very internal and is supposed to be used by the `Effects(...)` constructor only. --- base/compiler/abstractinterpretation.jl | 8 ++++---- base/compiler/effects.jl | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 7eb6c5f991306..9d2a37a87d5e5 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1343,7 +1343,7 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) if isa(tti, Union) utis = uniontypes(tti) if any(@nospecialize(t) -> !isa(t, DataType) || !(t <: Tuple) || !isknownlength(t), utis) - return AbstractIterationResult(Any[Vararg{Any}], nothing, EFFECTS_UNKNOWN′) + return AbstractIterationResult(Any[Vararg{Any}], nothing, Effects()) end ltp = length((utis[1]::DataType).parameters) for t in utis @@ -1378,7 +1378,7 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) elseif tti0 === SimpleVector return AbstractIterationResult(Any[Vararg{Any}], nothing) elseif tti0 === Any - return AbstractIterationResult(Any[Vararg{Any}], nothing, EFFECTS_UNKNOWN′) + return AbstractIterationResult(Any[Vararg{Any}], nothing, Effects()) elseif tti0 <: Array return AbstractIterationResult(Any[Vararg{eltype(tti0)}], nothing) else @@ -1391,7 +1391,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n if isa(itft, Const) iteratef = itft.val else - return AbstractIterationResult(Any[Vararg{Any}], nothing, EFFECTS_UNKNOWN′) + return AbstractIterationResult(Any[Vararg{Any}], nothing, Effects()) end @assert !isvarargtype(itertype) call = abstract_call_known(interp, iteratef, ArgInfo(nothing, Any[itft, itertype]), StmtInfo(true), sv) @@ -1453,7 +1453,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n # ... but cannot terminate if !may_have_terminated # ... and cannot have terminated prior to this loop - return AbstractIterationResult(Any[Bottom], AbstractIterationInfo(calls, false), EFFECTS_UNKNOWN′) + return AbstractIterationResult(Any[Bottom], AbstractIterationInfo(calls, false), Effects()) else # iterator may have terminated prior to this loop, but not during it valtype = Bottom diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index 16e02cad81cb9..fdb65037ce507 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -98,12 +98,12 @@ const EFFECT_FREE_IF_INACCESSIBLEMEMONLY = 0x01 << 1 # :inaccessiblememonly bits const INACCESSIBLEMEM_OR_ARGMEMONLY = 0x01 << 1 -const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, true, true) -const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, true, true) -const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, true, true) # unknown mostly, but it's not overlayed at least (e.g. it's not a call) -const EFFECTS_UNKNOWN′ = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, false, true) # unknown really +const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, true, true) +const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, true, true) +const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, true, false) # unknown mostly, but it's not overlayed at least (e.g. it's not a call) +const _EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, false, false) # unknown really -function Effects(e::Effects = EFFECTS_UNKNOWN′; +function Effects(e::Effects = _EFFECTS_UNKNOWN; consistent::UInt8 = e.consistent, effect_free::UInt8 = e.effect_free, nothrow::Bool = e.nothrow, From 533a09417cea8ee6e1105ee55bcad40830123977 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 1 Mar 2023 15:37:54 +0900 Subject: [PATCH 190/775] inference: fix the correctness of inference bail out interface (#48826) Since we allow overloading of the `bail_out_xxx` hooks, we need to make sure that we widen both type and effects to the top when bailing on inference regardless of the condition presumed by a hook. This commit particularly fixes the correctness of `bail_out_apply` (fixes #48807). I wanted to make a simplified test case for this, but it turns out to be a bit tricky since it relies on the details of multiple match analysis and the bail out logic. --- base/compiler/abstractinterpretation.jl | 29 +++++++++++++++---------- base/compiler/inferencestate.jl | 21 +++++++++++++----- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 9d2a37a87d5e5..76c11a6aa3941 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -106,10 +106,9 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), match = applicable[i]::MethodMatch method = match.method sig = match.spec_types - if bail_out_toplevel_call(interp, sig, sv) + if bail_out_toplevel_call(interp, InferenceLoopState(sig, rettype, all_effects), sv) # only infer concrete call sites in top-level expressions add_remark!(interp, sv, "Refusing to infer non-concrete call site in top-level expression") - rettype = Any break end this_rt = Bottom @@ -190,8 +189,8 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), conditionals[2][i] = tmerge(conditionals[2][i], cnd.elsetype) end end - if bail_out_call(interp, rettype, sv, effects) - add_remark!(interp, sv, "One of the matched returned maximally imprecise information. Bailing on call.") + if bail_out_call(interp, InferenceLoopState(sig, rettype, all_effects), sv) + add_remark!(interp, sv, "Call inference reached maximally imprecise information. Bailing on.") break end end @@ -201,7 +200,9 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), info = ConstCallInfo(info, const_results) end - if seen != napplicable + if seen ≠ napplicable + # there is unanalyzed candidate, widen type and effects to the top + rettype = Any # there may be unanalyzed effects within unseen dispatch candidate, # but we can still ignore nonoverlayed effect here since we already accounted for it all_effects = merge_effects(all_effects, EFFECTS_UNKNOWN) @@ -1545,7 +1546,9 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, si:: end retinfos = ApplyCallInfo[] retinfo = UnionSplitApplyCallInfo(retinfos) - for i = 1:length(ctypes) + napplicable = length(ctypes) + seen = 0 + for i = 1:napplicable ct = ctypes[i] arginfo = infos[i] lct = length(ct) @@ -1559,17 +1562,21 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, si:: end end call = abstract_call(interp, ArgInfo(nothing, ct), si, sv, max_methods) + seen += 1 push!(retinfos, ApplyCallInfo(call.info, arginfo)) res = tmerge(res, call.rt) effects = merge_effects(effects, call.effects) - if bail_out_apply(interp, res, sv) - if i != length(ctypes) - # No point carrying forward the info, we're not gonna inline it anyway - retinfo = NoCallInfo() - end + if bail_out_apply(interp, InferenceLoopState(ct, res, effects), sv) + add_remark!(interp, sv, "_apply_iterate inference reached maximally imprecise information. Bailing on.") break end end + if seen ≠ napplicable + # there is unanalyzed candidate, widen type and effects to the top + res = Any + effects = Effects() + retinfo = NoCallInfo() # NOTE this is necessary to prevent the inlining processing + end # TODO: Add a special info type to capture all the iteration info. # For now, only propagate info if we don't also union-split the iteration return CallMeta(res, effects, retinfo) diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 44be8b199d9c3..df7bb67d625e8 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -217,14 +217,23 @@ is_effect_overridden(override::EffectsOverride, effect::Symbol) = getfield(overr add_remark!(::AbstractInterpreter, sv::Union{InferenceState, IRCode}, remark) = return -function bail_out_toplevel_call(::AbstractInterpreter, @nospecialize(callsig), sv::Union{InferenceState, IRCode}) - return isa(sv, InferenceState) && sv.restrict_abstract_call_sites && !isdispatchtuple(callsig) +struct InferenceLoopState + sig + rt + effects::Effects + function InferenceLoopState(@nospecialize(sig), @nospecialize(rt), effects::Effects) + new(sig, rt, effects) + end +end + +function bail_out_toplevel_call(::AbstractInterpreter, state::InferenceLoopState, sv::Union{InferenceState, IRCode}) + return isa(sv, InferenceState) && sv.restrict_abstract_call_sites && !isdispatchtuple(state.sig) end -function bail_out_call(::AbstractInterpreter, @nospecialize(rt), sv::Union{InferenceState, IRCode}, effects::Effects) - return rt === Any && !is_foldable(effects) +function bail_out_call(::AbstractInterpreter, state::InferenceLoopState, sv::Union{InferenceState, IRCode}) + return state.rt === Any && !is_foldable(state.effects) end -function bail_out_apply(::AbstractInterpreter, @nospecialize(rt), sv::Union{InferenceState, IRCode}) - return rt === Any +function bail_out_apply(::AbstractInterpreter, state::InferenceLoopState, sv::Union{InferenceState, IRCode}) + return state.rt === Any end was_reached(sv::InferenceState, pc::Int) = sv.ssavaluetypes[pc] !== NOT_FOUND From e239059724125f95b36a0a30467b1d35728b0c30 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 1 Mar 2023 19:07:59 +0900 Subject: [PATCH 191/775] effects: taint `nonoverlayed` when bailing out inference (#48838) --- base/compiler/abstractinterpretation.jl | 4 +--- test/compiler/AbstractInterpreter.jl | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 76c11a6aa3941..6e843f460ac5d 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -203,9 +203,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), if seen ≠ napplicable # there is unanalyzed candidate, widen type and effects to the top rettype = Any - # there may be unanalyzed effects within unseen dispatch candidate, - # but we can still ignore nonoverlayed effect here since we already accounted for it - all_effects = merge_effects(all_effects, EFFECTS_UNKNOWN) + all_effects = Effects() elseif isa(matches, MethodMatches) ? (!matches.fullmatch || any_ambig(matches)) : (!all(matches.fullmatches) || any_ambig(matches)) # Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index 57281fa3ad723..1926e23c7dbc2 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -51,6 +51,14 @@ import Base.Experimental: @MethodTable, @overlay @MethodTable(OverlayedMT) CC.method_table(interp::MTOverlayInterp) = CC.OverlayMethodTable(CC.get_world_counter(interp), OverlayedMT) +function CC.add_remark!(interp::MTOverlayInterp, ::CC.InferenceState, remark) + if interp.meta !== nothing + # Core.println(remark) + push!(interp.meta, remark) + end + return nothing +end + strangesin(x) = sin(x) @overlay OverlayedMT strangesin(x::Float64) = iszero(x) ? nothing : cos(x) @@ -70,6 +78,21 @@ end |> !Core.Compiler.is_nonoverlayed @invoke strangesin(x::Float64) end |> !Core.Compiler.is_nonoverlayed +# account for overlay possibility in unanalyzed matching method +callstrange(::Nothing) = Core.compilerbarrier(:type, nothing) # trigger inference bail out +callstrange(::Float64) = strangesin(x) +callstrange_entry(x) = callstrange(x) # needs to be defined here because of world age +let interp = MTOverlayInterp(; meta=Set{Any}()) + matches = Core.Compiler.findall(Tuple{typeof(callstrange),Any}, Core.Compiler.method_table(interp)).matches + @test Core.Compiler.length(matches) == 2 + if Core.Compiler.getindex(matches, 1).method == which(callstrange, (Nothing,)) + @test Base.infer_effects(callstrange_entry, (Any,); interp) |> !Core.Compiler.is_nonoverlayed + @test "Call inference reached maximally imprecise information. Bailing on." in interp.meta + else + @warn "`nonoverlayed` test for inference bailing out is skipped since the method match sort order is changed." + end +end + # but it should never apply for the native compilation @test Base.infer_effects((Float64,)) do x strangesin(x) From 151cd23bafa2aab693b15fc1358090465f5b18ab Mon Sep 17 00:00:00 2001 From: Paul Berg Date: Wed, 1 Mar 2023 13:13:41 +0100 Subject: [PATCH 192/775] Replace `strides(a, d)` with `stride(a, d)` in `StridedArray` docs (#48839) --- 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 7f2c05501edb8..684ed8b48f734 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -3108,7 +3108,7 @@ with elements of type `T` and `N` dimensions. If `A` is a `StridedArray`, then its elements are stored in memory with offsets, which may vary between dimensions but are constant within a dimension. For example, `A` could have stride 2 in dimension 1, and stride 3 in dimension 2. Incrementing `A` along -dimension `d` jumps in memory by [`strides(A, d)`] slots. Strided arrays are +dimension `d` jumps in memory by [`stride(A, d)`] slots. Strided arrays are particularly important and useful because they can sometimes be passed directly as pointers to foreign language libraries like BLAS. """ From fb8676882b756edd62efe3b23e27a705e631fa68 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Wed, 1 Mar 2023 09:14:44 -0500 Subject: [PATCH 193/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Downloads=20stdlib=20from=200098e40=20to=20f97c72f=20(#48840)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dilum Aluthge --- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + stdlib/Downloads.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/md5 delete mode 100644 deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/sha512 create mode 100644 deps/checksums/Downloads-f97c72fbd726e208a04c53791b35cc34c747569f.tar.gz/md5 create mode 100644 deps/checksums/Downloads-f97c72fbd726e208a04c53791b35cc34c747569f.tar.gz/sha512 diff --git a/deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/md5 b/deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/md5 deleted file mode 100644 index c8013e35d1c0b..0000000000000 --- a/deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -8b7ed41dce0e56996e20465a3ccdbd1f diff --git a/deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/sha512 b/deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/sha512 deleted file mode 100644 index 8e0eae1949d61..0000000000000 --- a/deps/checksums/Downloads-0098e40feb6d4992920ddfbed181150ef412f189.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -7fa5bd303971f5e0e0bc0ca48bf9850f26f909a3f8b2d8331d33ea200aa40ff812604af33a44b5b29dca9f03c43d279425ec16113d740b21d345b41c760a52e2 diff --git a/deps/checksums/Downloads-f97c72fbd726e208a04c53791b35cc34c747569f.tar.gz/md5 b/deps/checksums/Downloads-f97c72fbd726e208a04c53791b35cc34c747569f.tar.gz/md5 new file mode 100644 index 0000000000000..4e70641a4a08b --- /dev/null +++ b/deps/checksums/Downloads-f97c72fbd726e208a04c53791b35cc34c747569f.tar.gz/md5 @@ -0,0 +1 @@ +fa2c90db0e7aa73186c491aa2f03bb2b diff --git a/deps/checksums/Downloads-f97c72fbd726e208a04c53791b35cc34c747569f.tar.gz/sha512 b/deps/checksums/Downloads-f97c72fbd726e208a04c53791b35cc34c747569f.tar.gz/sha512 new file mode 100644 index 0000000000000..3f54f39d35ac6 --- /dev/null +++ b/deps/checksums/Downloads-f97c72fbd726e208a04c53791b35cc34c747569f.tar.gz/sha512 @@ -0,0 +1 @@ +d36737b946af5e720402ce4f25e4c69c740bdbdc174385d6448c3660b26fffe34c14af7c4dd4d26ad864ad12771cabdf922c8b3cf4423167a46cdf3001ede125 diff --git a/stdlib/Downloads.version b/stdlib/Downloads.version index 2d5477564f6c4..c6db08779e947 100644 --- a/stdlib/Downloads.version +++ b/stdlib/Downloads.version @@ -1,4 +1,4 @@ DOWNLOADS_BRANCH = master -DOWNLOADS_SHA1 = 0098e40feb6d4992920ddfbed181150ef412f189 +DOWNLOADS_SHA1 = f97c72fbd726e208a04c53791b35cc34c747569f DOWNLOADS_GIT_URL := https://github.com/JuliaLang/Downloads.jl.git DOWNLOADS_TAR_URL = https://api.github.com/repos/JuliaLang/Downloads.jl/tarball/$1 From a07f51298994299eaeeae3bb254f2079aa6b6e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Wed, 1 Mar 2023 15:44:14 +0000 Subject: [PATCH 194/775] Always use `-Wl,--warn-unresolved-symbols` in MSAN builds (#48835) * Always use `-Wl,--warn-unresolved-symbols` in MSAN builds * Use `-Wl,--warn-unresolved-symbols` only on Linux and FreeBSD --- Make.inc | 5 ++++- deps/libsuitesparse.mk | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Make.inc b/Make.inc index 65a38fc927bc5..f0034a2d3be62 100644 --- a/Make.inc +++ b/Make.inc @@ -674,7 +674,10 @@ SANITIZE_LDFLAGS := ifeq ($(SANITIZE_MEMORY),1) SANITIZE_OPTS += -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer SANITIZE_LDFLAGS += $(SANITIZE_OPTS) -endif +ifneq ($(findstring $(OS),Linux FreeBSD),) +SANITIZE_LDFLAGS += -Wl,--warn-unresolved-symbols +endif # OS Linux or FreeBSD +endif # SANITIZE_MEMORY=1 ifeq ($(SANITIZE_ADDRESS),1) SANITIZE_OPTS += -fsanitize=address SANITIZE_LDFLAGS += -fsanitize=address diff --git a/deps/libsuitesparse.mk b/deps/libsuitesparse.mk index 8900390d24c24..7d79e03ee8d0e 100644 --- a/deps/libsuitesparse.mk +++ b/deps/libsuitesparse.mk @@ -26,7 +26,7 @@ LIBSUITESPARSE_MFLAGS := CC="$(CC) $(SANITIZE_OPTS)" CXX="$(CXX) $(SANITIZE_OPTS AR="$(AR)" RANLIB="$(RANLIB)" \ BLAS="-L$(build_shlibdir) -lblastrampoline" \ LAPACK="-L$(build_shlibdir) -lblastrampoline" \ - LDFLAGS="$(SUITESPARSE_LIB) $(SANITIZE_LDFLAGS) -Wl,--warn-unresolved-symbols" CFOPENMP="" CUDA=no CUDA_PATH="" \ + LDFLAGS="$(SUITESPARSE_LIB) $(SANITIZE_LDFLAGS)" CFOPENMP="" CUDA=no CUDA_PATH="" \ UMFPACK_CONFIG="$(UMFPACK_CONFIG)" \ CHOLMOD_CONFIG="$(CHOLMOD_CONFIG)" \ SPQR_CONFIG="$(SPQR_CONFIG)" From fb2e9d326cd1f8731c78fc92e48e11601d035e14 Mon Sep 17 00:00:00 2001 From: FX Coudert Date: Wed, 1 Mar 2023 17:34:24 +0100 Subject: [PATCH 195/775] [LibCURL_jll] Update to v7.88.1 (#48800) --- deps/checksums/curl | 68 ++++++++++++++++----------------- deps/curl.version | 2 +- stdlib/LibCURL_jll/Project.toml | 2 +- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/deps/checksums/curl b/deps/checksums/curl index 0f235d8238e8e..acbcb749ac5e2 100644 --- a/deps/checksums/curl +++ b/deps/checksums/curl @@ -1,36 +1,36 @@ LibCURL-fd8af649b38ae20c3ff7f5dca53753512ca00376.tar.gz/md5/f082283e6a35fcba5b63c9a6219d8003 LibCURL-fd8af649b38ae20c3ff7f5dca53753512ca00376.tar.gz/sha512/3bea5fa3fb6d29651daa923ae6bcb8eeb356ab9f2a1f3e005a6b746b617b0cf609aed4cadda4181783959840873c04b18e34e45ab973549169d19775a05ea01e -LibCURL.v7.84.0+0.aarch64-apple-darwin.tar.gz/md5/0e1d2884864419df574b61a6db15ef9d -LibCURL.v7.84.0+0.aarch64-apple-darwin.tar.gz/sha512/18986ce04a39a8935d3b2e595e9c7b6ecd38340f1f886cb5b16880ad72b9889a5bba8720c30c2775add115c0385ca1f98956df2cb89cd4ffa92d67e433a8f12b -LibCURL.v7.84.0+0.aarch64-linux-gnu.tar.gz/md5/e4d57ee8f1304b8fde272a373a13cdf6 -LibCURL.v7.84.0+0.aarch64-linux-gnu.tar.gz/sha512/88ee9129a3053b8221808f977561541be573068c5abf388a78b1c748b6c7cca2cd23f8bfcb779541fc83dff07a7a3c979194359f6cd4d0cb6d6696affac03c11 -LibCURL.v7.84.0+0.aarch64-linux-musl.tar.gz/md5/f40a48d02ee841d7393477ef63163c43 -LibCURL.v7.84.0+0.aarch64-linux-musl.tar.gz/sha512/9998db3a896fa46a51d2da2a07b48470a9719fe301fb0589f04e2bd0e1bd116c5c74ca8f03d4dff6529339fdf68a42788ed33c629794bc3886e5147f51c53eb7 -LibCURL.v7.84.0+0.armv6l-linux-gnueabihf.tar.gz/md5/223727927aff997175d1d8bdcea39c79 -LibCURL.v7.84.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/f856ca8a63f55d455ae161e58cd5e195ffb80ceaeeaa7cf306a3d192ae51a1ebfb93e87e27aa90f513294e27beb8e1358c7a07eb5a3a85d434327b4331211426 -LibCURL.v7.84.0+0.armv6l-linux-musleabihf.tar.gz/md5/efc2bcc500edaaf59542f86119b9a090 -LibCURL.v7.84.0+0.armv6l-linux-musleabihf.tar.gz/sha512/297f2999f1544816e2edd1fb78aa5f8abf9dde9b782a62054b0f61974f3dbde7ae67cf4d8dd63c21082de5f89dfeb32aa099e2228851242c3379a811883f92e4 -LibCURL.v7.84.0+0.armv7l-linux-gnueabihf.tar.gz/md5/e5a0a5b7f1e664675bc2ac4970b39297 -LibCURL.v7.84.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/bd9c602b69841dd1b8625627c774dbf99e7c3fcf951b00299dbe8007e8ea2bf5a312fa34f0be9e21a7ac947332652ffa55fdbcdf21096449a8ab982c9a7ce776 -LibCURL.v7.84.0+0.armv7l-linux-musleabihf.tar.gz/md5/05f04c53e4a04ced1d6aefc1e9493332 -LibCURL.v7.84.0+0.armv7l-linux-musleabihf.tar.gz/sha512/7ea517a048d8d7a940f5e32d1476366d9e63bf0103276c8208cd23e1ae7e4dd70e0acba4cdeafd1e9a5db90dfc213bd0895ebef755ea237cab3fc9d39808c325 -LibCURL.v7.84.0+0.i686-linux-gnu.tar.gz/md5/97cffa9e6e771e5b96d77a0acff157af -LibCURL.v7.84.0+0.i686-linux-gnu.tar.gz/sha512/84b81c69c021e8aad542c909c81ace41ea96650ef1dcd46b1ef29b683a870abddff96b8d2ecde593c8cea427256dfa194cf5bd4e5b610b0b8ce779e383aadb76 -LibCURL.v7.84.0+0.i686-linux-musl.tar.gz/md5/3dccdbc2cde661c7d868f2bd7d5c0316 -LibCURL.v7.84.0+0.i686-linux-musl.tar.gz/sha512/7625d1ba19e69cce185d61ef09374af4d433730f4908f1ce5da7d3352c96a58e1543dc66a0cb01000c4ced9033e2b2137877a4d7c9f8f0fa551613e436cb574c -LibCURL.v7.84.0+0.i686-w64-mingw32.tar.gz/md5/bd2b06eadacaf984cc25993c242517eb -LibCURL.v7.84.0+0.i686-w64-mingw32.tar.gz/sha512/21aee096ff42e3c4dfbf6b8c9e3cbdcc4cae234ac784e871d4ca55424263eb59cfd2b159287861a076373017ab5454d0c9f93c99d87e90f263563ddee28d737d -LibCURL.v7.84.0+0.powerpc64le-linux-gnu.tar.gz/md5/221f481553cdb28d97a7caa69a895b12 -LibCURL.v7.84.0+0.powerpc64le-linux-gnu.tar.gz/sha512/90caf2fe245a0e1f5816fadf2c0b8e7bda5df38d716c309aadb37721923f57919af09c6a7396ce2888dc02ae02670da9300c0e5814d5ad851bdb4e661c48bc48 -LibCURL.v7.84.0+0.x86_64-apple-darwin.tar.gz/md5/9f609374291fe24ec9bd752c967d3072 -LibCURL.v7.84.0+0.x86_64-apple-darwin.tar.gz/sha512/8a8461a8cf7591a798d7ed32423a33b38425d32e3a7fd4feda06095237ae6dc43c6737dcc55bb86e260080198d5295f11fee88883354425b132c8e04bfa9feaf -LibCURL.v7.84.0+0.x86_64-linux-gnu.tar.gz/md5/c1cc01bbc7aec5b272f7dbe803fda257 -LibCURL.v7.84.0+0.x86_64-linux-gnu.tar.gz/sha512/e6f9ff29a8ab46537054e1fa364ece163fd4376d16fe7e22dc94c0a640397b45659c143b8e170b1b01ef800ab7f53a9f4087197f2fae9002e061530cefe6157b -LibCURL.v7.84.0+0.x86_64-linux-musl.tar.gz/md5/20dec1cebca3b2ef188a31ae50a40b42 -LibCURL.v7.84.0+0.x86_64-linux-musl.tar.gz/sha512/9d5675f90eb348ecb637ee7ed31d68701504efa7871c9f55eacb331b6717eae893e88c63cb5abd6ca9d13d34a055d67d0cf36ca173f2bd58e19b65cabbd816e7 -LibCURL.v7.84.0+0.x86_64-unknown-freebsd.tar.gz/md5/a57884bfdcbca83c1f14ece9d501224f -LibCURL.v7.84.0+0.x86_64-unknown-freebsd.tar.gz/sha512/f8bf1755b3a758b351532ede8f19af6ace8cfcf59b656067ddfd1135533052b340ca35e9cb0e134e1f082cea19860af2029448fc1ca231a32bf03bd07698d4da -LibCURL.v7.84.0+0.x86_64-w64-mingw32.tar.gz/md5/71182295492b38bb419a71489f01fa54 -LibCURL.v7.84.0+0.x86_64-w64-mingw32.tar.gz/sha512/9d84bfad36ca69b3ed2519bef8845cece4d9b3e8c9e1e040f744c6163469c732cfd1301cf5e5c9e23c25420b1b17a844bcb43bde858a501eb6133dbc266f2f75 -curl-7.84.0.tar.bz2/md5/35fca80437f32dd7ef6c2e30b4916f06 -curl-7.84.0.tar.bz2/sha512/57823295e2c036355d9170b9409d698e1cece882b2cb55ce33fcf384dd30a75c00e68d6550f3b3faba4ef38443e2172c731ddfef6e508b99476f4e36d25bdd1c +LibCURL.v7.88.1+0.aarch64-apple-darwin.tar.gz/md5/f2f284f0497d7bc23946de9fc672180c +LibCURL.v7.88.1+0.aarch64-apple-darwin.tar.gz/sha512/a3e87f3a185112b7145bf9b34d8289835bf3d9195d0cb9fb1c0448037a8d4f5891d0904749e6792f7d75b686a7abab9f34c77c0e4576a1264f2b346ea3dce8a4 +LibCURL.v7.88.1+0.aarch64-linux-gnu.tar.gz/md5/1a74a07a6092b0d849a3f3f1f18a9c82 +LibCURL.v7.88.1+0.aarch64-linux-gnu.tar.gz/sha512/9b855e800b2ae7613efd645328d406165fe80148695f9a4b3ad5c2c3f473bee41bf0d96a33d6396ab31352abf5ece1179544d13b9b6cf47968fe2bcb2f21375e +LibCURL.v7.88.1+0.aarch64-linux-musl.tar.gz/md5/8901469d4141c53e51fe4b69323ad69d +LibCURL.v7.88.1+0.aarch64-linux-musl.tar.gz/sha512/d45ffb9c217c283644a31d59c605ada731b7ecd8dcd56bc27b026ca9a76c3cb715a105fc5ecd724eb2113b734d840cd3eb1e747a5a0e33df1a192ef5db33e843 +LibCURL.v7.88.1+0.armv6l-linux-gnueabihf.tar.gz/md5/10b64832ec940f96010ecdb0c24433c9 +LibCURL.v7.88.1+0.armv6l-linux-gnueabihf.tar.gz/sha512/892a6a615547d0676ec60af0af626477891641b105f1b3aaf8dc7d0daf27bf12d7e6bc78da9d54af8effb0453b69a5491bc9240955e600fc4fbe3c83d9ed1226 +LibCURL.v7.88.1+0.armv6l-linux-musleabihf.tar.gz/md5/1e91598375bd6cc1dfad682d610d3bb2 +LibCURL.v7.88.1+0.armv6l-linux-musleabihf.tar.gz/sha512/67275f960c47e42e9143eb22907f7c448abd107b18963538a3125adf3fb8afdb33a16134c1e601cccb93afa564c7071e9ef47be132260ecd209c3a67f0909189 +LibCURL.v7.88.1+0.armv7l-linux-gnueabihf.tar.gz/md5/0884c064608ddae6a057527612699eb5 +LibCURL.v7.88.1+0.armv7l-linux-gnueabihf.tar.gz/sha512/0f3b59bc751682cd6027ffdd94f6e9468a729d39f12702b6adfb025c888044920fab232f02718c90e9033dd2456e1e514285e27c297595670e2b6fd96cf4a9d4 +LibCURL.v7.88.1+0.armv7l-linux-musleabihf.tar.gz/md5/6e34064f4e3a54cb7df69063eff41098 +LibCURL.v7.88.1+0.armv7l-linux-musleabihf.tar.gz/sha512/aa76cec424e245194c381390dec0f934cf3e29f0656c0bfc3993490b8fcc893557d1e4028f602cb08cce148c4dd74ca822f8c962d106cffeb611dc782176e621 +LibCURL.v7.88.1+0.i686-linux-gnu.tar.gz/md5/650b714017567cd82da67a5e67a2e221 +LibCURL.v7.88.1+0.i686-linux-gnu.tar.gz/sha512/16b6db47d14ff3e3950d3ae277fbc22235a9f11db457dd119e09150a446d5c35a7a851bc030082a7147b46d882d0a87ba1248b34866be4e0d88584c197de35ac +LibCURL.v7.88.1+0.i686-linux-musl.tar.gz/md5/d3ab851beb2442f182a69bd14008e027 +LibCURL.v7.88.1+0.i686-linux-musl.tar.gz/sha512/b124d27cdd8457ad39cbef5ef34dc567f0a846b69db3c701b85f03c3902f22e56c820bf4c3368b59a46473dfb2bcbaf0f36ee92fcfd1ff1d8273a07d6a741a8c +LibCURL.v7.88.1+0.i686-w64-mingw32.tar.gz/md5/b333120c19e085a7ff4c5a1d542c7601 +LibCURL.v7.88.1+0.i686-w64-mingw32.tar.gz/sha512/e6fbad17d3f53fda8e5df4d9087af1a9bb6d6179c569b017c29e800631db71a74ed81161dd38a502a81affc0dcdf94a75ee677b27e57a6641247c7c876c889fa +LibCURL.v7.88.1+0.powerpc64le-linux-gnu.tar.gz/md5/e1b329350e0214a9f7cb8b06a02f8868 +LibCURL.v7.88.1+0.powerpc64le-linux-gnu.tar.gz/sha512/7c29b66d2e2ffce79da1b4d8dcc8b54137e340509b36283797d2890219e8ca61168856768d57db586d44469451c0e05055d314ef55e40717edb86c5378f079a6 +LibCURL.v7.88.1+0.x86_64-apple-darwin.tar.gz/md5/9a5f92e8512aed1483b42d7d36c4839d +LibCURL.v7.88.1+0.x86_64-apple-darwin.tar.gz/sha512/424be4468268aba40eb43940ab2dee976148ee2caea62167228ced615fc6ea348bb326a1261766a1bb2c56346b02881404a392b8c67563a4060cee1b1c533ff2 +LibCURL.v7.88.1+0.x86_64-linux-gnu.tar.gz/md5/d8cdbbbde5ae302e2b119b199d5c50a8 +LibCURL.v7.88.1+0.x86_64-linux-gnu.tar.gz/sha512/ba748b7e28b258efebec8018b6d8488e57b192731f63e9159dfafd868d4d681cae2197bba0cd8ac56c8e811eca4d7ba2560f9c3edf4e8d11ef88d6445d900c78 +LibCURL.v7.88.1+0.x86_64-linux-musl.tar.gz/md5/3345f4b110bbf1bf24ed386f3b9b33dc +LibCURL.v7.88.1+0.x86_64-linux-musl.tar.gz/sha512/770bbbeb858aab352746acadc65b287178c51678bec1c939707491da47db014009b2eb3535c17ed54384766f40abcc3049525498d72a9a4afa618fcef43c3be6 +LibCURL.v7.88.1+0.x86_64-unknown-freebsd.tar.gz/md5/18eea62ac368923ecb1bffaaa9a28dd3 +LibCURL.v7.88.1+0.x86_64-unknown-freebsd.tar.gz/sha512/72cbbfb57c2869439ddacde8f013d926d7bc25c43b5ebeb92d7eabcf7386055a691b32857c58ecd611fec6ad52cc8b3a6e18d01810928ecd4a967f045fe052f2 +LibCURL.v7.88.1+0.x86_64-w64-mingw32.tar.gz/md5/55836fd5e1daad9c19b489f56644530b +LibCURL.v7.88.1+0.x86_64-w64-mingw32.tar.gz/sha512/918ec33a8c90fba1e014a9b44f4f76ea563ecd15dbce7b74f8f4ad502e7466920da095bbea3e60a7900ca5d0872dd8c1d332303d6aab39e231c32f4d96765136 +curl-7.88.1.tar.bz2/md5/e90619abb4d275f767e6bfceab5ddabb +curl-7.88.1.tar.bz2/sha512/998fbfc65733b7c97edfc6fbb322757ab2d3116af9c7077a7435ecb27189f4c0052602551fa7f723ecb788177c229399f0342d8070d0dec226646acd43a67963 diff --git a/deps/curl.version b/deps/curl.version index 96bc09263156f..632aa80d01683 100644 --- a/deps/curl.version +++ b/deps/curl.version @@ -3,4 +3,4 @@ CURL_JLL_NAME := LibCURL ## source build -CURL_VER := 7.84.0 +CURL_VER := 7.88.1 diff --git a/stdlib/LibCURL_jll/Project.toml b/stdlib/LibCURL_jll/Project.toml index 45dbb45830837..5970ed28cecf0 100644 --- a/stdlib/LibCURL_jll/Project.toml +++ b/stdlib/LibCURL_jll/Project.toml @@ -1,6 +1,6 @@ name = "LibCURL_jll" uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "7.84.0+0" +version = "7.88.1+0" [deps] LibSSH2_jll = "29816b5a-b9ab-546f-933c-edad1886dfa8" From eb8bb4e060fef13dd0e9e35796fa8722baf6202b Mon Sep 17 00:00:00 2001 From: Kaushik-Iyer <84177184+Kaushik-Iyer@users.noreply.github.com> Date: Thu, 2 Mar 2023 00:11:18 +0530 Subject: [PATCH 196/775] Update link in docs for Dates (#48814) Update the 'Edit in Github' link in docs for Dates. Earlier pointed to a 404 error --- stdlib/Dates/docs/src/index.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stdlib/Dates/docs/src/index.md b/stdlib/Dates/docs/src/index.md index e0e09a919a085..aa46f7b827f10 100644 --- a/stdlib/Dates/docs/src/index.md +++ b/stdlib/Dates/docs/src/index.md @@ -1,3 +1,7 @@ +```@meta +EditURL = "https://github.com/JuliaLang/julia/blob/master/stdlib/Dates/docs/src/index.md" +``` + # Dates ```@meta From 4c323819cfd165222608bb07614a272d043b845d Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 1 Mar 2023 16:17:45 -0500 Subject: [PATCH 197/775] generalize insert-backedges to insert in any world (#48785) Rather than a binary valid/not-valid, we track the exact later world that deleted it, relative to when we first assumed it may be valid. --- src/staticdata.c | 6 +- src/staticdata_utils.c | 250 +++++++++++++++++++++-------------------- 2 files changed, 133 insertions(+), 123 deletions(-) diff --git a/src/staticdata.c b/src/staticdata.c index 7f7b4882b994f..df1171d6a44a9 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -2244,6 +2244,7 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new } if (edges) { + size_t world = jl_atomic_load_acquire(&jl_world_counter); jl_collect_missing_backedges(jl_type_type_mt); jl_collect_missing_backedges(jl_nonfunction_mt); // jl_collect_extext_methods_from_mod and jl_collect_missing_backedges also accumulate data in callers_with_edges. @@ -2253,7 +2254,7 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new *method_roots_list = jl_alloc_vec_any(0); // Collect the new method roots jl_collect_new_roots(*method_roots_list, *new_specializations, worklist_key); - jl_collect_edges(*edges, *ext_targets, *new_specializations); + jl_collect_edges(*edges, *ext_targets, *new_specializations, world); } assert(edges_map == NULL); // jl_collect_edges clears this when done @@ -3271,7 +3272,8 @@ static jl_value_t *jl_restore_package_image_from_stream(ios_t *f, jl_image_t *im // Add roots to methods jl_copy_roots(method_roots_list, jl_worklist_key((jl_array_t*)restored)); // Handle edges - jl_insert_backedges((jl_array_t*)edges, (jl_array_t*)ext_targets, (jl_array_t*)new_specializations); // restore external backedges (needs to be last) + size_t world = jl_atomic_load_acquire(&jl_world_counter); + jl_insert_backedges((jl_array_t*)edges, (jl_array_t*)ext_targets, (jl_array_t*)new_specializations, world); // restore external backedges (needs to be last) // reinit ccallables jl_reinit_ccallable(&ccallable_list, base, NULL); arraylist_free(&ccallable_list); diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 41d0bdc216fba..32c735ab4f626 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -418,9 +418,8 @@ static void jl_record_edges(jl_method_instance_t *caller, arraylist_t *wq, jl_ar // Extract `edges` and `ext_targets` from `edges_map` // `edges` = [caller1, targets_indexes1, ...], the list of methods and their edges // `ext_targets` is [invokesig1, callee1, matches1, ...], the edges for each target -static void jl_collect_edges(jl_array_t *edges, jl_array_t *ext_targets, jl_array_t *external_cis) +static void jl_collect_edges(jl_array_t *edges, jl_array_t *ext_targets, jl_array_t *external_cis, size_t world) { - size_t world = jl_atomic_load_acquire(&jl_world_counter); htable_t external_mis; htable_new(&external_mis, 0); if (external_cis) { @@ -821,38 +820,40 @@ static void jl_copy_roots(jl_array_t *method_roots_list, uint64_t key) } } + // verify that these edges intersect with the same methods as before -static jl_array_t *jl_verify_edges(jl_array_t *targets) +static jl_array_t *jl_verify_edges(jl_array_t *targets, size_t minworld) { - size_t world = jl_atomic_load_acquire(&jl_world_counter); size_t i, l = jl_array_len(targets) / 3; - jl_array_t *valids = jl_alloc_array_1d(jl_array_uint8_type, l); - memset(jl_array_data(valids), 1, l); + static jl_value_t *ulong_array JL_ALWAYS_LEAFTYPE = NULL; + if (ulong_array == NULL) + ulong_array = jl_apply_array_type((jl_value_t*)jl_ulong_type, 1); + jl_array_t *maxvalids = jl_alloc_array_1d(ulong_array, l); + memset(jl_array_data(maxvalids), 0, l * sizeof(size_t)); jl_value_t *loctag = NULL; jl_value_t *matches = NULL; - JL_GC_PUSH3(&valids, &matches, &loctag); + JL_GC_PUSH3(&maxvalids, &matches, &loctag); for (i = 0; i < l; i++) { jl_value_t *invokesig = jl_array_ptr_ref(targets, i * 3); jl_value_t *callee = jl_array_ptr_ref(targets, i * 3 + 1); jl_value_t *expected = jl_array_ptr_ref(targets, i * 3 + 2); - int valid = 1; size_t min_valid = 0; size_t max_valid = ~(size_t)0; if (invokesig) { assert(callee && "unsupported edge"); jl_methtable_t *mt = jl_method_get_table(((jl_method_instance_t*)callee)->def.method); if ((jl_value_t*)mt == jl_nothing) { - valid = 0; + max_valid = 0; } else { - matches = jl_gf_invoke_lookup_worlds(invokesig, (jl_value_t*)mt, world, &min_valid, &max_valid); + matches = jl_gf_invoke_lookup_worlds(invokesig, (jl_value_t*)mt, minworld, &min_valid, &max_valid); if (matches == jl_nothing) { - valid = 0; + max_valid = 0; } else { matches = (jl_value_t*)((jl_method_match_t*)matches)->method; if (matches != expected) { - valid = 0; + max_valid = 0; } } } @@ -867,15 +868,15 @@ static jl_array_t *jl_verify_edges(jl_array_t *targets) int ambig = 0; // TODO: possibly need to included ambiguities too (for the optimizer correctness)? matches = jl_matching_methods((jl_tupletype_t*)sig, jl_nothing, - -1, 0, world, &min_valid, &max_valid, &ambig); + -1, 0, minworld, &min_valid, &max_valid, &ambig); if (matches == jl_nothing) { - valid = 0; + max_valid = 0; } else { // setdiff!(matches, expected) size_t j, k, ins = 0; if (jl_array_len(matches) != jl_array_len(expected)) { - valid = 0; + max_valid = 0; } for (k = 0; k < jl_array_len(matches); k++) { jl_method_t *match = ((jl_method_match_t*)jl_array_ptr_ref(matches, k))->method; @@ -887,18 +888,18 @@ static jl_array_t *jl_verify_edges(jl_array_t *targets) // intersection has a new method or a method was // deleted--this is now probably no good, just invalidate // everything about it now - valid = 0; + max_valid = 0; if (!_jl_debug_method_invalidation) break; jl_array_ptr_set(matches, ins++, match); } } - if (!valid && _jl_debug_method_invalidation) + if (max_valid != ~(size_t)0 && _jl_debug_method_invalidation) jl_array_del_end((jl_array_t*)matches, jl_array_len(matches) - ins); } } - jl_array_uint8_set(valids, i, valid); - if (!valid && _jl_debug_method_invalidation) { + ((size_t*)(jl_array_data(maxvalids)))[i] = max_valid; + if (max_valid != ~(size_t)0 && _jl_debug_method_invalidation) { jl_array_ptr_1d_push(_jl_debug_method_invalidation, invokesig ? (jl_value_t*)invokesig : callee); loctag = jl_cstr_to_string("insert_backedges_callee"); jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); @@ -911,161 +912,168 @@ static jl_array_t *jl_verify_edges(jl_array_t *targets) //ios_puts(valid ? "valid\n" : "INVALID\n", ios_stderr); } JL_GC_POP(); - return valids; + return maxvalids; } -// Combine all edges relevant to a method into the visited table -static void jl_verify_methods(jl_array_t *edges, jl_array_t *valids, htable_t *visited) +// Combine all edges relevant to a method to initialize the maxvalids list +static jl_array_t *jl_verify_methods(jl_array_t *edges, jl_array_t *maxvalids) { jl_value_t *loctag = NULL; - JL_GC_PUSH1(&loctag); + jl_array_t *maxvalids2 = NULL; + JL_GC_PUSH2(&loctag, &maxvalids2); size_t i, l = jl_array_len(edges) / 2; - htable_new(visited, l); + maxvalids2 = jl_alloc_array_1d(jl_typeof(maxvalids), l); + size_t *maxvalids2_data = (size_t*)jl_array_data(maxvalids2); + memset(maxvalids2_data, 0, l * sizeof(size_t)); for (i = 0; i < l; i++) { jl_method_instance_t *caller = (jl_method_instance_t*)jl_array_ptr_ref(edges, 2 * i); assert(jl_is_method_instance(caller) && jl_is_method(caller->def.method)); jl_array_t *callee_ids = (jl_array_t*)jl_array_ptr_ref(edges, 2 * i + 1); assert(jl_typeis((jl_value_t*)callee_ids, jl_array_int32_type)); - int valid = 1; if (callee_ids == NULL) { // serializing the edges had failed - valid = 0; + maxvalids2_data[i] = 0; } else { int32_t *idxs = (int32_t*)jl_array_data(callee_ids); size_t j; - for (j = 0; valid && j < idxs[0]; j++) { + maxvalids2_data[i] = ~(size_t)0; + for (j = 0; j < idxs[0]; j++) { int32_t idx = idxs[j + 1]; - valid = jl_array_uint8_ref(valids, idx); - if (!valid && _jl_debug_method_invalidation) { + size_t max_valid = ((size_t*)(jl_array_data(maxvalids)))[idx]; + if (max_valid != ~(size_t)0 && _jl_debug_method_invalidation) { jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)caller); loctag = jl_cstr_to_string("verify_methods"); jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); loctag = jl_box_int32((int32_t)idx); jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); } + if (max_valid < maxvalids2_data[i]) + maxvalids2_data[i] = max_valid; + if (max_valid == 0) + break; } } - ptrhash_put(visited, caller, (void*)(((char*)HT_NOTFOUND) + valid + 1)); //jl_static_show((JL_STREAM*)ios_stderr, (jl_value_t*)caller); - //ios_puts(valid ? "valid\n" : "INVALID\n", ios_stderr); - // HT_NOTFOUND: valid (no invalid edges) - // HT_NOTFOUND + 1: invalid - // HT_NOTFOUND + 2: need to scan - // HT_NOTFOUND + 3 + depth: in-progress + //ios_puts(maxvalid2_data[i] == ~(size_t)0 ? "valid\n" : "INVALID\n", ios_stderr); } JL_GC_POP(); + return maxvalids2; } // Visit the entire call graph, starting from edges[idx] to determine if that method is valid // Implements Tarjan's SCC (strongly connected components) algorithm, simplified to remove the count variable -static int jl_verify_graph_edge(jl_array_t *edges, int idx, htable_t *visited, arraylist_t *stack) +// and slightly modified with an early termination option once the computation reaches its minimum +static int jl_verify_graph_edge(size_t *maxvalids2_data, jl_array_t *edges, size_t idx, arraylist_t *visited, arraylist_t *stack) { - jl_method_instance_t *caller = (jl_method_instance_t*)jl_array_ptr_ref(edges, idx * 2); - assert(jl_is_method_instance(caller) && jl_is_method(caller->def.method)); - int found = (char*)ptrhash_get(visited, (void*)caller) - (char*)HT_NOTFOUND; - if (found == 0) - return 1; // NOTFOUND == valid - if (found == 1) - return 0; // invalid - if (found != 2) - return found - 1; // depth - found = 0; + if (maxvalids2_data[idx] == 0) { + visited->items[idx] = (void*)1; + return 0; + } + size_t cycle = (size_t)visited->items[idx]; + if (cycle != 0) + return cycle - 1; // depth remaining jl_value_t *cause = NULL; - arraylist_push(stack, (void*)caller); - int depth = stack->len; - ptrhash_put(visited, (void*)caller, (void*)((char*)HT_NOTFOUND + 3 + depth)); // change 2 to in-progress at depth + arraylist_push(stack, (void*)idx); + size_t depth = stack->len; + visited->items[idx] = (void*)(1 + depth); jl_array_t *callee_ids = (jl_array_t*)jl_array_ptr_ref(edges, idx * 2 + 1); assert(jl_typeis((jl_value_t*)callee_ids, jl_array_int32_type)); int32_t *idxs = (int32_t*)jl_array_data(callee_ids); - int cycle = 0; size_t i, n = jl_array_len(callee_ids); for (i = idxs[0] + 1; i < n; i++) { - int32_t idx = idxs[i]; - int child_found = jl_verify_graph_edge(edges, idx, visited, stack); - if (child_found == 0) { + int32_t childidx = idxs[i]; + int child_cycle = jl_verify_graph_edge(maxvalids2_data, edges, childidx, visited, stack); + size_t child_max_valid = maxvalids2_data[childidx]; + if (child_max_valid < maxvalids2_data[idx]) { + maxvalids2_data[idx] = child_max_valid; + cause = jl_array_ptr_ref(edges, childidx * 2); + } + if (child_max_valid == 0) { // found what we were looking for, so terminate early - found = 1; - cause = jl_array_ptr_ref(edges, idx * 2); break; } - else if (child_found >= 2 && child_found - 2 < cycle) { + else if (child_cycle && child_cycle < cycle) { // record the cycle will resolve at depth "cycle" - cycle = child_found - 2; - assert(cycle); + cycle = child_cycle; } } - if (!found && cycle && cycle != depth) - return cycle + 2; + size_t max_valid = maxvalids2_data[idx]; + if (max_valid != 0 && cycle && cycle != depth) + return cycle; // If we are the top of the current cycle, now mark all other parts of // our cycle with what we found. - // Or if we found a backedge, also mark all of the other parts of the - // cycle as also having an backedge. + // Or if we found a failed edge, also mark all of the other parts of the + // cycle as also having an failed edge. while (stack->len >= depth) { - void *mi = arraylist_pop(stack); - assert((char*)ptrhash_get(visited, mi) - (char*)HT_NOTFOUND == 4 + stack->len); - if (found) - ptrhash_put(visited, mi, (void*)((char*)HT_NOTFOUND + 1 + found)); - else - ptrhash_remove(visited, mi); // assign as NOTFOUND in table - if (_jl_debug_method_invalidation && found) { - jl_value_t *loctag = NULL; - JL_GC_PUSH1(&loctag); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)mi); - loctag = jl_cstr_to_string("verify_methods"); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)cause); - JL_GC_POP(); + size_t childidx = (size_t)arraylist_pop(stack); + assert(visited->items[childidx] == (void*)(2 + stack->len)); + if (idx != childidx) { + if (max_valid < maxvalids2_data[childidx]) + maxvalids2_data[childidx] = max_valid; + if (_jl_debug_method_invalidation && max_valid != ~(size_t)0) { + jl_method_instance_t *mi = (jl_method_instance_t*)jl_array_ptr_ref(edges, childidx * 2); + jl_value_t *loctag = NULL; + JL_GC_PUSH1(&loctag); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)mi); + loctag = jl_cstr_to_string("verify_methods"); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)cause); + JL_GC_POP(); + } } + visited->items[childidx] = (void*)1; } - return found ? 0 : 1; + return 0; } // Visit all entries in edges, verify if they are valid -static jl_array_t *jl_verify_graph(jl_array_t *edges, htable_t *visited) +static void jl_verify_graph(jl_array_t *edges, jl_array_t *maxvalids2) { - arraylist_t stack; + arraylist_t stack, visited; arraylist_new(&stack, 0); size_t i, n = jl_array_len(edges) / 2; - jl_array_t *valids = jl_alloc_array_1d(jl_array_uint8_type, n); - JL_GC_PUSH1(&valids); - int8_t *valids_data = (int8_t*)jl_array_data(valids); - for (i = 0; i < n; i++) - valids_data[i] = jl_verify_graph_edge(edges, i, visited, &stack); + arraylist_new(&visited, n); + memset(visited.items, 0, n * sizeof(size_t)); + size_t *maxvalids2_data = (size_t*)jl_array_data(maxvalids2); + for (i = 0; i < n; i++) { + assert(visited.items[i] == (void*)0 || visited.items[i] == (void*)1); + int child_cycle = jl_verify_graph_edge(maxvalids2_data, edges, i, &visited, &stack); + assert(child_cycle == 0); (void)child_cycle; + assert(stack.len == 0); + assert(visited.items[i] == (void*)1); + } arraylist_free(&stack); - JL_GC_POP(); - return valids; + arraylist_free(&visited); } // Restore backedges to external targets // `edges` = [caller1, targets_indexes1, ...], the list of worklist-owned methods calling external methods. // `ext_targets` is [invokesig1, callee1, matches1, ...], the global set of non-worklist callees of worklist-owned methods. -static void jl_insert_backedges(jl_array_t *edges, jl_array_t *ext_targets, jl_array_t *ci_list) +static void jl_insert_backedges(jl_array_t *edges, jl_array_t *ext_targets, jl_array_t *ci_list, size_t minworld) { // determine which CodeInstance objects are still valid in our image - size_t world = jl_atomic_load_acquire(&jl_world_counter); - jl_array_t *valids = jl_verify_edges(ext_targets); + jl_array_t *valids = jl_verify_edges(ext_targets, minworld); JL_GC_PUSH1(&valids); - htable_t visited; - htable_new(&visited, 0); - jl_verify_methods(edges, valids, &visited); // consumes valids, creates visited - valids = jl_verify_graph(edges, &visited); // consumes visited, creates valids + valids = jl_verify_methods(edges, valids); // consumes edges valids, initializes methods valids + jl_verify_graph(edges, valids); // propagates methods valids for each edge size_t i, l; // next build a map from external MethodInstances to their CodeInstance for insertion l = jl_array_len(ci_list); - htable_reset(&visited, l); + htable_t visited; + htable_new(&visited, l); for (i = 0; i < l; i++) { jl_code_instance_t *ci = (jl_code_instance_t*)jl_array_ptr_ref(ci_list, i); - assert(ci->min_world == world); + assert(ci->min_world == minworld); if (ci->max_world == 1) { // sentinel value: has edges to external callables ptrhash_put(&visited, (void*)ci->def, (void*)ci); } else { assert(ci->max_world == ~(size_t)0); jl_method_instance_t *caller = ci->def; - if (ci->inferred && jl_rettype_inferred(caller, world, ~(size_t)0) == jl_nothing) { + if (ci->inferred && jl_rettype_inferred(caller, minworld, ~(size_t)0) == jl_nothing) { jl_mi_cache_insert(caller, ci); } //jl_static_show((jl_stream*)ios_stderr, (jl_value_t*)caller); @@ -1077,28 +1085,28 @@ static void jl_insert_backedges(jl_array_t *edges, jl_array_t *ext_targets, jl_a l = jl_array_len(edges) / 2; for (i = 0; i < l; i++) { jl_method_instance_t *caller = (jl_method_instance_t*)jl_array_ptr_ref(edges, 2 * i); - int valid = jl_array_uint8_ref(valids, i); - if (!valid) - continue; - // if this callee is still valid, add all the backedges - jl_array_t *callee_ids = (jl_array_t*)jl_array_ptr_ref(edges, 2 * i + 1); - int32_t *idxs = (int32_t*)jl_array_data(callee_ids); - for (size_t j = 0; j < idxs[0]; j++) { - int32_t idx = idxs[j + 1]; - jl_value_t *invokesig = jl_array_ptr_ref(ext_targets, idx * 3); - jl_value_t *callee = jl_array_ptr_ref(ext_targets, idx * 3 + 1); - if (callee && jl_is_method_instance(callee)) { - jl_method_instance_add_backedge((jl_method_instance_t*)callee, invokesig, caller); - } - else { - jl_value_t *sig = callee == NULL ? invokesig : callee; - jl_methtable_t *mt = jl_method_table_for(sig); - // FIXME: rarely, `callee` has an unexpected `Union` signature, - // see https://github.com/JuliaLang/julia/pull/43990#issuecomment-1030329344 - // Fix the issue and turn this back into an `assert((jl_value_t*)mt != jl_nothing)` - // This workaround exposes us to (rare) 265-violations. - if ((jl_value_t*)mt != jl_nothing) - jl_method_table_add_backedge(mt, sig, (jl_value_t*)caller); + size_t maxvalid = ((size_t*)(jl_array_data(valids)))[i]; + if (maxvalid == ~(size_t)0) { + // if this callee is still valid, add all the backedges + jl_array_t *callee_ids = (jl_array_t*)jl_array_ptr_ref(edges, 2 * i + 1); + int32_t *idxs = (int32_t*)jl_array_data(callee_ids); + for (size_t j = 0; j < idxs[0]; j++) { + int32_t idx = idxs[j + 1]; + jl_value_t *invokesig = jl_array_ptr_ref(ext_targets, idx * 3); + jl_value_t *callee = jl_array_ptr_ref(ext_targets, idx * 3 + 1); + if (callee && jl_is_method_instance(callee)) { + jl_method_instance_add_backedge((jl_method_instance_t*)callee, invokesig, caller); + } + else { + jl_value_t *sig = callee == NULL ? invokesig : callee; + jl_methtable_t *mt = jl_method_table_for(sig); + // FIXME: rarely, `callee` has an unexpected `Union` signature, + // see https://github.com/JuliaLang/julia/pull/43990#issuecomment-1030329344 + // Fix the issue and turn this back into an `assert((jl_value_t*)mt != jl_nothing)` + // This workaround exposes us to (rare) 265-violations. + if ((jl_value_t*)mt != jl_nothing) + jl_method_table_add_backedge(mt, sig, (jl_value_t*)caller); + } } } // then enable any methods associated with it @@ -1108,9 +1116,9 @@ static void jl_insert_backedges(jl_array_t *edges, jl_array_t *ext_targets, jl_a // have some new external code to use assert(jl_is_code_instance(ci)); jl_code_instance_t *codeinst = (jl_code_instance_t*)ci; - assert(codeinst->min_world == world && codeinst->inferred); - codeinst->max_world = ~(size_t)0; - if (jl_rettype_inferred(caller, world, ~(size_t)0) == jl_nothing) { + assert(codeinst->min_world == minworld && codeinst->inferred); + codeinst->max_world = maxvalid; + if (jl_rettype_inferred(caller, minworld, maxvalid) == jl_nothing) { jl_mi_cache_insert(caller, codeinst); } } From 0687b656661ebf3e240391f87c30ce006ff4068a Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Thu, 23 Feb 2023 10:02:32 -0700 Subject: [PATCH 198/775] Add Tracy as optional dependency This commit adds libTracyClient as an optional build dependency, to be used for profiling the base Julia runtime. Support can be enabled by adding `WITH_TRACY := 1` to Make.user Here's an example instrumentation: ```c TracyCZoneN(ctx, "jl_subtype_env", true); int r = jl_subtype_env(x, y, NULL, 0); TracyCZoneEnd(ctx); return r; ``` This only adds Tracy itself. In a follow-up commit, I'll update the JL_TIMING macros to hook into Tracy when available. --- Make.inc | 12 +++- contrib/refresh_checksums.mk | 2 +- deps/Makefile | 11 ++- deps/checksums/libtracyclient | 34 +++++++++ deps/libtracyclient.mk | 72 +++++++++++++++++++ deps/libtracyclient.version | 8 +++ .../patches/libTracyClient-freebsd-elfw.patch | 33 +++++++++ .../libTracyClient-no-crash-handler.patch | 21 ++++++ src/Makefile | 4 +- 9 files changed, 189 insertions(+), 8 deletions(-) create mode 100644 deps/checksums/libtracyclient create mode 100644 deps/libtracyclient.mk create mode 100644 deps/libtracyclient.version create mode 100644 deps/patches/libTracyClient-freebsd-elfw.patch create mode 100644 deps/patches/libTracyClient-no-crash-handler.patch diff --git a/Make.inc b/Make.inc index f0034a2d3be62..c066869322a85 100644 --- a/Make.inc +++ b/Make.inc @@ -89,6 +89,9 @@ WITH_GC_DEBUG_ENV := 0 # Enable DTrace support WITH_DTRACE := 0 +# Enable Tracy support +WITH_TRACY := 0 + # Prevent picking up $ARCH from the environment variables ARCH:= @@ -716,7 +719,12 @@ ifeq ($(WITH_DTRACE), 1) JCXXFLAGS += -DUSE_DTRACE JCFLAGS += -DUSE_DTRACE DTRACE := dtrace -else +endif + +ifeq ($(WITH_TRACY), 1) +JCXXFLAGS += -DUSE_TRACY -DTRACY_ENABLE +JCFLAGS += -DUSE_TRACY -DTRACY_ENABLE +LIBTRACYCLIENT:=-lTracyClient endif # =========================================================================== @@ -1170,7 +1178,7 @@ CSL_NEXT_GLIBCXX_VERSION=GLIBCXX_3\.4\.31|GLIBCXX_3\.5\.|GLIBCXX_4\. # Note: we explicitly _do not_ define `CSL` here, since it requires some more # advanced techniques to decide whether it should be installed from a BB source # or not. See `deps/csl.mk` for more detail. -BB_PROJECTS := BLASTRAMPOLINE OPENBLAS LLVM LIBSUITESPARSE OPENLIBM GMP MBEDTLS LIBSSH2 NGHTTP2 MPFR CURL LIBGIT2 PCRE LIBUV LIBUNWIND DSFMT OBJCONV ZLIB P7ZIP LLD +BB_PROJECTS := BLASTRAMPOLINE OPENBLAS LLVM LIBSUITESPARSE OPENLIBM GMP MBEDTLS LIBSSH2 NGHTTP2 MPFR CURL LIBGIT2 PCRE LIBUV LIBUNWIND DSFMT OBJCONV ZLIB P7ZIP LLD LIBTRACYCLIENT define SET_BB_DEFAULT # First, check to see if BB is disabled on a global setting ifeq ($$(USE_BINARYBUILDER),0) diff --git a/contrib/refresh_checksums.mk b/contrib/refresh_checksums.mk index fc632728e9a9e..710ecbdf121be 100644 --- a/contrib/refresh_checksums.mk +++ b/contrib/refresh_checksums.mk @@ -24,7 +24,7 @@ CLANG_TRIPLETS=$(filter %-darwin %-freebsd,$(TRIPLETS)) NON_CLANG_TRIPLETS=$(filter-out %-darwin %-freebsd,$(TRIPLETS)) # These are the projects currently using BinaryBuilder; both GCC-expanded and non-GCC-expanded: -BB_PROJECTS=mbedtls libssh2 nghttp2 mpfr curl libgit2 pcre libuv unwind llvmunwind dsfmt objconv p7zip zlib libsuitesparse openlibm blastrampoline +BB_PROJECTS=mbedtls libssh2 nghttp2 mpfr curl libgit2 pcre libuv unwind llvmunwind dsfmt objconv p7zip zlib libsuitesparse openlibm blastrampoline libtracyclient BB_GCC_EXPANDED_PROJECTS=openblas csl BB_CXX_EXPANDED_PROJECTS=gmp llvm clang llvm-tools lld # These are non-BB source-only deps diff --git a/deps/Makefile b/deps/Makefile index 244d9a2b588a0..0d013272ad23d 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -24,9 +24,9 @@ BUILDDIR := $(BUILDDIR)$(MAYBE_HOST) # # autoconf configure-driven scripts: pcre unwind gmp mpfr patchelf libuv curl # custom Makefile rules: openlibm dsfmt libsuitesparse lapack blastrampoline openblas utf8proc objconv libwhich -# CMake libs: llvm llvmunwind libgit2 libssh2 mbedtls +# CMake libs: llvm llvmunwind libgit2 libssh2 mbedtls libtracyclient # -# downloadable via git: llvm-svn, libuv, libopenlibm, utf8proc, libgit2, libssh2 +# downloadable via git: llvm-svn, libuv, libopenlibm, utf8proc, libgit2, libssh2, libtracyclient # # to debug 'define' rules, replace eval at the usage site with info or error @@ -50,6 +50,10 @@ ifeq ($(USE_SYSTEM_LIBUV), 0) DEP_LIBS += libuv endif +ifeq ($(WITH_TRACY), 1) +DEP_LIBS += libtracyclient +endif + ifeq ($(DISABLE_LIBUNWIND), 0) ifeq ($(USE_SYSTEM_LIBUNWIND), 0) ifeq ($(OS), Linux) @@ -174,7 +178,7 @@ DEP_LIBS_STAGED := $(DEP_LIBS) DEP_LIBS_STAGED_ALL := llvm llvm-tools clang llvmunwind unwind libuv pcre \ openlibm dsfmt blastrampoline openblas lapack gmp mpfr patchelf utf8proc \ objconv mbedtls libssh2 nghttp2 curl libgit2 libwhich zlib p7zip csl \ - libsuitesparse lld + libsuitesparse lld libtracyclient DEP_LIBS_ALL := $(DEP_LIBS_STAGED_ALL) ifneq ($(USE_BINARYBUILDER_OPENBLAS),0) @@ -231,5 +235,6 @@ include $(SRCDIR)/curl.mk include $(SRCDIR)/libgit2.mk include $(SRCDIR)/libwhich.mk include $(SRCDIR)/p7zip.mk +include $(SRCDIR)/libtracyclient.mk include $(SRCDIR)/tools/uninstallers.mk diff --git a/deps/checksums/libtracyclient b/deps/checksums/libtracyclient new file mode 100644 index 0000000000000..24c847192a5ea --- /dev/null +++ b/deps/checksums/libtracyclient @@ -0,0 +1,34 @@ +LibTracyClient.v0.9.0+0.aarch64-apple-darwin.tar.gz/md5/bd0f07099a6e4be61b7029d4ae28b869 +LibTracyClient.v0.9.0+0.aarch64-apple-darwin.tar.gz/sha512/ac24ba5d2b022330835d1246244282eb5f1dd2260557c0b2713aee7103d358d79dec7f7dda6ac223f3920e9d35fbfec2a20bd2895ca6869b6764979854326cda +LibTracyClient.v0.9.0+0.aarch64-linux-gnu.tar.gz/md5/da32a3fc61b7a1c4c263a7da5d8e9606 +LibTracyClient.v0.9.0+0.aarch64-linux-gnu.tar.gz/sha512/b5eb438c3d1f4e8c9aa21de06f33524b32d99a9300685fc59b150f3585a14e462407f1a39a99a7b2d3ef6127c744f404ec754a97aea908f0177b531df0be278a +LibTracyClient.v0.9.0+0.aarch64-linux-musl.tar.gz/md5/90fa248761866972b15162033c637993 +LibTracyClient.v0.9.0+0.aarch64-linux-musl.tar.gz/sha512/6c27052e45ff6948b1407decf09fe13cc113fae3ea9b6db6bfe962e0bfa94c3a80d89da2e185cec6cd9ead1a518e211279f74ad3e712af45bf33658b2ed7eb52 +LibTracyClient.v0.9.0+0.armv6l-linux-gnueabihf.tar.gz/md5/c066826c09742790ef3fc62ce8c82a6a +LibTracyClient.v0.9.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/f92b7a4d155a8b3494320d29e4fa9b4133c5a1bac5e27ac876123a34e64273c82b032c15a1e828f01e22ad0501d31ada63f8187c4dcabaf134647e8674ee4b0f +LibTracyClient.v0.9.0+0.armv6l-linux-musleabihf.tar.gz/md5/162f8da840c6770977a3e320818f5ade +LibTracyClient.v0.9.0+0.armv6l-linux-musleabihf.tar.gz/sha512/dcaae4a9f42c279842c137f7c24559d21b58b3e16835b19b73d3c53cf68e8a8f5fad55ab9d19f732004624ba21f8d2d0894597548c837735b69d5d93f1339df6 +LibTracyClient.v0.9.0+0.armv7l-linux-gnueabihf.tar.gz/md5/7a6e8a98755427b435087e999d5bb4b0 +LibTracyClient.v0.9.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/e12757fe58c515c325ae42ebc1556484cf57de5b1f607e58db8fef72ad89d29a9d93ba12d5cacf67be237479c50504a7c9389d7e6e7b712f0667b3e31996619b +LibTracyClient.v0.9.0+0.armv7l-linux-musleabihf.tar.gz/md5/c6336fd1e929fc5004db9b3bcdcc19d5 +LibTracyClient.v0.9.0+0.armv7l-linux-musleabihf.tar.gz/sha512/1c46bf341f55de2214ede3c69947f1477b90856385a92d26c613cb080c92a1210046d1b0baa89b57d17a80326532ba7961b117d52c75c312c91307b81f9578f9 +LibTracyClient.v0.9.0+0.i686-linux-gnu.tar.gz/md5/a22947288d659746e637fff59889e8eb +LibTracyClient.v0.9.0+0.i686-linux-gnu.tar.gz/sha512/620d36879f0adf60dd2f030680e898991049d44e4c67b427f41eb9a68860aafcf8a5bea1a5e16210fc0ddd1d33b903189d56e1f67001d9d5e41a63c557ca96fc +LibTracyClient.v0.9.0+0.i686-linux-musl.tar.gz/md5/99d2c2c5dbe82cf9101b45388ea4d010 +LibTracyClient.v0.9.0+0.i686-linux-musl.tar.gz/sha512/d6451c1dd2ff72e672f87090a66880b87f8b97c71915f34923d62eed5e31ea3bb509eed3d3b8490b8744a4260ed0293aabe6ac8225b6af80043e2812efdb0b85 +LibTracyClient.v0.9.0+0.i686-w64-mingw32.tar.gz/md5/4ce065d5ab6628cca0660611297f2e73 +LibTracyClient.v0.9.0+0.i686-w64-mingw32.tar.gz/sha512/bfb19288c7ceec88b81853208bc91192f9d7cef379e48fa61d6cd4f4e6fad77577155f76692ba9674d417ca0cc408b1897eabe9e422d0c9ad6ac77916ea87e51 +LibTracyClient.v0.9.0+0.powerpc64le-linux-gnu.tar.gz/md5/ada91bbeb4cc43df561a966b6742634b +LibTracyClient.v0.9.0+0.powerpc64le-linux-gnu.tar.gz/sha512/95083234a82e2340533a0addcbe9e5a394aa0ee52e04abad5fe0f82001fdd3973785a83202421f71e47a11421f7d0d847febfb73bf128f73a0e22bccc4ce64c8 +LibTracyClient.v0.9.0+0.x86_64-apple-darwin.tar.gz/md5/fdb95488c9f56590ff102d833ee7064c +LibTracyClient.v0.9.0+0.x86_64-apple-darwin.tar.gz/sha512/ce01492d74bfef00f389a188d3b96dd13baaf3e2fd8e8320c1609c71509673324bc38b6e9a4605f9990bd86c991db07dd7e7d799dceb8a5d607635b8da129a12 +LibTracyClient.v0.9.0+0.x86_64-linux-gnu.tar.gz/md5/da530f0264cc9c5975a90d501b82198e +LibTracyClient.v0.9.0+0.x86_64-linux-gnu.tar.gz/sha512/6b6d2af9ab58e293fdb153e16d81033f6ef647f96b9dd9195023b5031e0167cfaf66f4baa6779db16370fe5757c546d2490eb3addff6be759f99f0404eed0072 +LibTracyClient.v0.9.0+0.x86_64-linux-musl.tar.gz/md5/b332c4ae28b6555b24fa8030173c614e +LibTracyClient.v0.9.0+0.x86_64-linux-musl.tar.gz/sha512/03f33289f641e68898965d5f45a1ddebb37f6a980b1b311aaf982114692125b7a13d3343549cc63fee41cbbc5879ce85b44db83d69cd01de3e799084025b0aab +LibTracyClient.v0.9.0+0.x86_64-unknown-freebsd.tar.gz/md5/bb3f52662452cde95a3653b9cc59ea99 +LibTracyClient.v0.9.0+0.x86_64-unknown-freebsd.tar.gz/sha512/10e548d47b5945a96eaa887d6748593bc78bc232c2b86746c21dfa747b59af91ef755a6d3f4223cc8f5c18c5f6e18aeaca1a9b5fee639211adb7d374c1b7f5ad +LibTracyClient.v0.9.0+0.x86_64-w64-mingw32.tar.gz/md5/c4592ca7188e22396f0a242b78d966d3 +LibTracyClient.v0.9.0+0.x86_64-w64-mingw32.tar.gz/sha512/8e7e7d8b972739823b387eae95df9086700f331e232fbe6571a447df0cce9867d042bfedbae421a1234469e3faad524bdb4e6b1b1608a1348c2d1362b504ca91 +libtracyclient-5a1f5371b792c12aea324213e1dc738b2923ae21.tar.gz/md5/62791801e0ffb11a7d70c2d724a230be +libtracyclient-5a1f5371b792c12aea324213e1dc738b2923ae21.tar.gz/sha512/b0570e048eee08ba5e21e0f855b2346cad408b0b86cdf79c8bb3727bb0ab57167dc7988f4dd1ee4157b371135201b87d1491237c09a2934de65eb3b9e26fcdc2 diff --git a/deps/libtracyclient.mk b/deps/libtracyclient.mk new file mode 100644 index 0000000000000..b8f33ed47b913 --- /dev/null +++ b/deps/libtracyclient.mk @@ -0,0 +1,72 @@ +## LIBTRACYCLIENT ## +ifneq ($(USE_BINARYBUILDER_LIBTRACYCLIENT),1) +LIBTRACYCLIENT_GIT_URL:=https://github.com/wolfpld/tracy.git +LIBTRACYCLIENT_TAR_URL=https://api.github.com/repos/wolfpld/tracy/tarball/$1 +$(eval $(call git-external,libtracyclient,LIBTRACYCLIENT,,,$(SRCCACHE))) + +LIBTRACYCLIENT_BUILDDIR := $(BUILDDIR)/$(LIBTRACYCLIENT_SRC_DIR) +LIBTRACYCLIENT_SRCCACHE := $(SRCCACHE)/$(LIBTRACYCLIENT_SRC_DIR) + +LIBTRACYCLIENT_CMAKE := +LIBTRACYCLIENT_CMAKE += -DBUILD_SHARED_LIBS=ON +LIBTRACYCLIENT_CMAKE += -DTRACY_FIBERS=ON +LIBTRACYCLIENT_CMAKE += -DTRACY_NO_BROADCAST=ON +LIBTRACYCLIENT_CMAKE += -DTRACY_ONLY_LOCALHOST=ON +LIBTRACYCLIENT_CMAKE += -DTRACY_NO_CODE_TRANSFER=ON +LIBTRACYCLIENT_CMAKE += -DTRACY_NO_FRAME_IMAGE=ON +LIBTRACYCLIENT_CMAKE += -DTRACY_NO_CRASH_HANDLER=ON +LIBTRACYCLIENT_CMAKE += -DTRACY_ON_DEMAND=ON + +$(LIBTRACYCLIENT_SRCCACHE)/cmake-patch-applied: $(LIBTRACYCLIENT_SRCCACHE)/source-extracted +ifneq ($(OS),WINNT) + echo "target_compile_definitions(TracyClient PUBLIC __STDC_FORMAT_MACROS)" >> $(LIBTRACYCLIENT_SRCCACHE)/CMakeLists.txt +else + echo "target_compile_definitions(TracyClient PUBLIC WINVER=0x0602 _WIN32_WINNT=0x0602)" >> $(LIBTRACYCLIENT_SRCCACHE)/CMakeLists.txt +endif + echo 1 > $@ + +$(LIBTRACYCLIENT_SRCCACHE)/libTracyClient-freebsd-elfw.patch-applied: $(LIBTRACYCLIENT_SRCCACHE)/cmake-patch-applied + cd $(LIBTRACYCLIENT_SRCCACHE) && \ + patch -p1 -f < $(SRCDIR)/patches/libTracyClient-freebsd-elfw.patch + echo 1 > $@ + +$(LIBTRACYCLIENT_SRCCACHE)/libTracyClient-no-crash-handler.patch-applied: $(LIBTRACYCLIENT_SRCCACHE)/libTracyClient-freebsd-elfw.patch-applied + cd $(LIBTRACYCLIENT_SRCCACHE) && \ + patch -p1 -f < $(SRCDIR)/patches/libTracyClient-no-crash-handler.patch + echo 1 > $@ + +$(LIBTRACYCLIENT_BUILDDIR)/build-configured: $(LIBTRACYCLIENT_SRCCACHE)/libTracyClient-no-crash-handler.patch-applied + mkdir -p $(dir $@) + cd $(dir $@) && \ + $(CMAKE) $(LIBTRACYCLIENT_SRCCACHE) $(CMAKE_GENERATOR_COMMAND) $(CMAKE_COMMON) $(LIBTRACYCLIENT_CMAKE) \ + || { echo '*** To install a newer version of cmake, run contrib/download_cmake.sh ***' && false; } + echo 1 > $@ + +$(LIBTRACYCLIENT_BUILDDIR)/build-compiled: $(LIBTRACYCLIENT_BUILDDIR)/build-configured + cd $(LIBTRACYCLIENT_BUILDDIR) && \ + $(if $(filter $(CMAKE_GENERATOR),make), \ + $(MAKE), \ + $(CMAKE) --build .) + echo 1 > $@ + +$(eval $(call staged-install, \ + libtracyclient,$$(LIBTRACYCLIENT_SRC_DIR), \ + MAKE_INSTALL,,, \ + $$(INSTALL_NAME_CMD)libtracyclient.$$(SHLIB_EXT) $$(build_shlibdir)/libtracyclient.$$(SHLIB_EXT))) + +clean-libtracyclient: + rm -rf $(LIBTRACYCLIENT_BUILDDIR)/build-configured $(LIBTRACYCLIENT_BUILDDIR)/build-compiled + -$(MAKE) -C $(LIBTRACYCLIENT_BUILDDIR) clean + +get-libtracyclient: $(LIBTRACYCLIENT_SRC_FILE) +extract-libtracyclient: $(LIBTRACYCLIENT_SRCCACHE)/source-extracted +configure-libtracyclient: $(LIBTRACYCLIENT_BUILDDIR)/build-configured +compile-libtracyclient: $(LIBTRACYCLIENT_BUILDDIR)/build-compiled +fastcheck-libtracyclient: check-libtracyclient +check-libtracyclient: compile-libtracyclient + +else # USE_BINARYBUILDER_LIBTRACYCLIENT + +$(eval $(call bb-install,libtracyclient,LIBTRACYCLIENT,false)) + +endif diff --git a/deps/libtracyclient.version b/deps/libtracyclient.version new file mode 100644 index 0000000000000..49f879b96204b --- /dev/null +++ b/deps/libtracyclient.version @@ -0,0 +1,8 @@ +## jll artifact +LIBTRACYCLIENT_JLL_NAME := LibTracyClient +LIBTRACYCLIENT_JLL_VER := 0.9.0+0 + +## source build +LIBTRACYCLIENT_VER := 0.9.0 +LIBTRACYCLIENT_BRANCH=v0.9 +LIBTRACYCLIENT_SHA1=5a1f5371b792c12aea324213e1dc738b2923ae21 diff --git a/deps/patches/libTracyClient-freebsd-elfw.patch b/deps/patches/libTracyClient-freebsd-elfw.patch new file mode 100644 index 0000000000000..8feb738714e11 --- /dev/null +++ b/deps/patches/libTracyClient-freebsd-elfw.patch @@ -0,0 +1,33 @@ +diff --git a/public/TracyClient.cpp b/public/TracyClient.cpp +index 77f81a4a..ebeb65c9 100644 +--- a/public/TracyClient.cpp ++++ b/public/TracyClient.cpp +@@ -19,6 +19,28 @@ + # pragma warning(push, 0) + #endif + ++#ifndef ElfW ++# if defined(FREEBSD) ++# if __ELF_WORD_SIZE == 32 ++# define ElfW(type) Elf32_##type ++# else ++# define ElfW(type) Elf64_##type ++# endif ++# elif defined(NETBSD) || defined(OPENBSD) ++# if ELFSIZE == 32 ++# define ElfW(type) Elf32_##type ++# else ++# define ElfW(type) Elf64_##type ++# endif ++# else ++# if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32 ++# define ElfW(type) Elf32_##type ++# else ++# define ElfW(type) Elf64_##type ++# endif ++# endif ++#endif ++ + #include "common/tracy_lz4.cpp" + #include "client/TracyProfiler.cpp" + #include "client/TracyCallstack.cpp" diff --git a/deps/patches/libTracyClient-no-crash-handler.patch b/deps/patches/libTracyClient-no-crash-handler.patch new file mode 100644 index 0000000000000..259b20fcff650 --- /dev/null +++ b/deps/patches/libTracyClient-no-crash-handler.patch @@ -0,0 +1,21 @@ +diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp +index ea168e4f..9287d433 100644 +--- a/public/client/TracyProfiler.cpp ++++ b/public/client/TracyProfiler.cpp +@@ -1454,7 +1454,7 @@ Profiler::~Profiler() + if( m_crashHandlerInstalled ) RemoveVectoredExceptionHandler( m_exceptionHandler ); + #endif + +-#ifdef __linux__ ++#if defined __linux__ && !defined TRACY_NO_CRASH_HANDLER + if( m_crashHandlerInstalled ) + { + sigaction( TRACY_CRASH_SIGNAL, &m_prevSignal.pwr, nullptr ); +@@ -1520,7 +1520,7 @@ bool Profiler::ShouldExit() + + void Profiler::Worker() + { +-#ifdef __linux__ ++#if defined __linux__ && !defined TRACY_NO_CRASH_HANDLER + s_profilerTid = syscall( SYS_gettid ); + #endif diff --git a/src/Makefile b/src/Makefile index bb98f6766f470..a100355864f51 100644 --- a/src/Makefile +++ b/src/Makefile @@ -157,8 +157,8 @@ LIBJULIA_PATH_REL := libjulia endif COMMON_LIBPATHS := -L$(build_libdir) -L$(build_shlibdir) -RT_LIBS := $(WHOLE_ARCHIVE) $(LIBUV) $(WHOLE_ARCHIVE) $(LIBUTF8PROC) $(NO_WHOLE_ARCHIVE) $(LIBUNWIND) $(RT_LLVMLINK) $(OSLIBS) -CG_LIBS := $(LIBUNWIND) $(CG_LLVMLINK) $(OSLIBS) +RT_LIBS := $(WHOLE_ARCHIVE) $(LIBUV) $(WHOLE_ARCHIVE) $(LIBUTF8PROC) $(NO_WHOLE_ARCHIVE) $(LIBUNWIND) $(RT_LLVMLINK) $(OSLIBS) $(LIBTRACYCLIENT) +CG_LIBS := $(LIBUNWIND) $(CG_LLVMLINK) $(OSLIBS) $(LIBTRACYCLIENT) RT_DEBUG_LIBS := $(COMMON_LIBPATHS) $(WHOLE_ARCHIVE) $(BUILDDIR)/flisp/libflisp-debug.a $(WHOLE_ARCHIVE) $(BUILDDIR)/support/libsupport-debug.a -ljulia-debug $(RT_LIBS) CG_DEBUG_LIBS := $(COMMON_LIBPATHS) $(CG_LIBS) -ljulia-debug -ljulia-internal-debug RT_RELEASE_LIBS := $(COMMON_LIBPATHS) $(WHOLE_ARCHIVE) $(BUILDDIR)/flisp/libflisp.a $(WHOLE_ARCHIVE) $(BUILDDIR)/support/libsupport.a -ljulia $(RT_LIBS) From 5632105d8e6d5940502fa4d8b960d34c8bfa47b8 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 28 Feb 2023 00:23:36 -0700 Subject: [PATCH 199/775] Update Tracy JLL to v0.9.0+1 This version includes the full set of feature flags that we use to customize Tracy to, e.g., accept on-demand connections instead of queuing profiling data until receiving a connection. --- deps/checksums/libtracyclient | 64 +++++++++++++++++------------------ deps/libtracyclient.version | 2 +- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/deps/checksums/libtracyclient b/deps/checksums/libtracyclient index 24c847192a5ea..506377a3f4e13 100644 --- a/deps/checksums/libtracyclient +++ b/deps/checksums/libtracyclient @@ -1,34 +1,34 @@ -LibTracyClient.v0.9.0+0.aarch64-apple-darwin.tar.gz/md5/bd0f07099a6e4be61b7029d4ae28b869 -LibTracyClient.v0.9.0+0.aarch64-apple-darwin.tar.gz/sha512/ac24ba5d2b022330835d1246244282eb5f1dd2260557c0b2713aee7103d358d79dec7f7dda6ac223f3920e9d35fbfec2a20bd2895ca6869b6764979854326cda -LibTracyClient.v0.9.0+0.aarch64-linux-gnu.tar.gz/md5/da32a3fc61b7a1c4c263a7da5d8e9606 -LibTracyClient.v0.9.0+0.aarch64-linux-gnu.tar.gz/sha512/b5eb438c3d1f4e8c9aa21de06f33524b32d99a9300685fc59b150f3585a14e462407f1a39a99a7b2d3ef6127c744f404ec754a97aea908f0177b531df0be278a -LibTracyClient.v0.9.0+0.aarch64-linux-musl.tar.gz/md5/90fa248761866972b15162033c637993 -LibTracyClient.v0.9.0+0.aarch64-linux-musl.tar.gz/sha512/6c27052e45ff6948b1407decf09fe13cc113fae3ea9b6db6bfe962e0bfa94c3a80d89da2e185cec6cd9ead1a518e211279f74ad3e712af45bf33658b2ed7eb52 -LibTracyClient.v0.9.0+0.armv6l-linux-gnueabihf.tar.gz/md5/c066826c09742790ef3fc62ce8c82a6a -LibTracyClient.v0.9.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/f92b7a4d155a8b3494320d29e4fa9b4133c5a1bac5e27ac876123a34e64273c82b032c15a1e828f01e22ad0501d31ada63f8187c4dcabaf134647e8674ee4b0f -LibTracyClient.v0.9.0+0.armv6l-linux-musleabihf.tar.gz/md5/162f8da840c6770977a3e320818f5ade -LibTracyClient.v0.9.0+0.armv6l-linux-musleabihf.tar.gz/sha512/dcaae4a9f42c279842c137f7c24559d21b58b3e16835b19b73d3c53cf68e8a8f5fad55ab9d19f732004624ba21f8d2d0894597548c837735b69d5d93f1339df6 -LibTracyClient.v0.9.0+0.armv7l-linux-gnueabihf.tar.gz/md5/7a6e8a98755427b435087e999d5bb4b0 -LibTracyClient.v0.9.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/e12757fe58c515c325ae42ebc1556484cf57de5b1f607e58db8fef72ad89d29a9d93ba12d5cacf67be237479c50504a7c9389d7e6e7b712f0667b3e31996619b -LibTracyClient.v0.9.0+0.armv7l-linux-musleabihf.tar.gz/md5/c6336fd1e929fc5004db9b3bcdcc19d5 -LibTracyClient.v0.9.0+0.armv7l-linux-musleabihf.tar.gz/sha512/1c46bf341f55de2214ede3c69947f1477b90856385a92d26c613cb080c92a1210046d1b0baa89b57d17a80326532ba7961b117d52c75c312c91307b81f9578f9 -LibTracyClient.v0.9.0+0.i686-linux-gnu.tar.gz/md5/a22947288d659746e637fff59889e8eb -LibTracyClient.v0.9.0+0.i686-linux-gnu.tar.gz/sha512/620d36879f0adf60dd2f030680e898991049d44e4c67b427f41eb9a68860aafcf8a5bea1a5e16210fc0ddd1d33b903189d56e1f67001d9d5e41a63c557ca96fc -LibTracyClient.v0.9.0+0.i686-linux-musl.tar.gz/md5/99d2c2c5dbe82cf9101b45388ea4d010 -LibTracyClient.v0.9.0+0.i686-linux-musl.tar.gz/sha512/d6451c1dd2ff72e672f87090a66880b87f8b97c71915f34923d62eed5e31ea3bb509eed3d3b8490b8744a4260ed0293aabe6ac8225b6af80043e2812efdb0b85 -LibTracyClient.v0.9.0+0.i686-w64-mingw32.tar.gz/md5/4ce065d5ab6628cca0660611297f2e73 -LibTracyClient.v0.9.0+0.i686-w64-mingw32.tar.gz/sha512/bfb19288c7ceec88b81853208bc91192f9d7cef379e48fa61d6cd4f4e6fad77577155f76692ba9674d417ca0cc408b1897eabe9e422d0c9ad6ac77916ea87e51 -LibTracyClient.v0.9.0+0.powerpc64le-linux-gnu.tar.gz/md5/ada91bbeb4cc43df561a966b6742634b -LibTracyClient.v0.9.0+0.powerpc64le-linux-gnu.tar.gz/sha512/95083234a82e2340533a0addcbe9e5a394aa0ee52e04abad5fe0f82001fdd3973785a83202421f71e47a11421f7d0d847febfb73bf128f73a0e22bccc4ce64c8 -LibTracyClient.v0.9.0+0.x86_64-apple-darwin.tar.gz/md5/fdb95488c9f56590ff102d833ee7064c -LibTracyClient.v0.9.0+0.x86_64-apple-darwin.tar.gz/sha512/ce01492d74bfef00f389a188d3b96dd13baaf3e2fd8e8320c1609c71509673324bc38b6e9a4605f9990bd86c991db07dd7e7d799dceb8a5d607635b8da129a12 -LibTracyClient.v0.9.0+0.x86_64-linux-gnu.tar.gz/md5/da530f0264cc9c5975a90d501b82198e -LibTracyClient.v0.9.0+0.x86_64-linux-gnu.tar.gz/sha512/6b6d2af9ab58e293fdb153e16d81033f6ef647f96b9dd9195023b5031e0167cfaf66f4baa6779db16370fe5757c546d2490eb3addff6be759f99f0404eed0072 -LibTracyClient.v0.9.0+0.x86_64-linux-musl.tar.gz/md5/b332c4ae28b6555b24fa8030173c614e -LibTracyClient.v0.9.0+0.x86_64-linux-musl.tar.gz/sha512/03f33289f641e68898965d5f45a1ddebb37f6a980b1b311aaf982114692125b7a13d3343549cc63fee41cbbc5879ce85b44db83d69cd01de3e799084025b0aab -LibTracyClient.v0.9.0+0.x86_64-unknown-freebsd.tar.gz/md5/bb3f52662452cde95a3653b9cc59ea99 -LibTracyClient.v0.9.0+0.x86_64-unknown-freebsd.tar.gz/sha512/10e548d47b5945a96eaa887d6748593bc78bc232c2b86746c21dfa747b59af91ef755a6d3f4223cc8f5c18c5f6e18aeaca1a9b5fee639211adb7d374c1b7f5ad -LibTracyClient.v0.9.0+0.x86_64-w64-mingw32.tar.gz/md5/c4592ca7188e22396f0a242b78d966d3 -LibTracyClient.v0.9.0+0.x86_64-w64-mingw32.tar.gz/sha512/8e7e7d8b972739823b387eae95df9086700f331e232fbe6571a447df0cce9867d042bfedbae421a1234469e3faad524bdb4e6b1b1608a1348c2d1362b504ca91 +LibTracyClient.v0.9.0+1.aarch64-apple-darwin.tar.gz/md5/621591ea1b72b07a0be82f87ed29a456 +LibTracyClient.v0.9.0+1.aarch64-apple-darwin.tar.gz/sha512/d053db12a0256bd60730f9b9a73ed6308c4cecb3f4a31cb979e1ecd8afbec5e3217b4a4f6355e24fc0c3bcc90dc9a83bf1be1dee467544e15ae6597d9d1a8d01 +LibTracyClient.v0.9.0+1.aarch64-linux-gnu.tar.gz/md5/7e2183c4cba6108e39c58e57ba31eb53 +LibTracyClient.v0.9.0+1.aarch64-linux-gnu.tar.gz/sha512/a912d329e065aae7a9d5b4392f6c292b68fed5cbd83b06bfddf925601f84bde4a76993864ecf3750fd216313630632157ff2f3f9e659caa71530e31aa738c72d +LibTracyClient.v0.9.0+1.aarch64-linux-musl.tar.gz/md5/a9d1b9700f9ed3c8c70480da7ebf326d +LibTracyClient.v0.9.0+1.aarch64-linux-musl.tar.gz/sha512/e9e928dda72f0b1aa9a92809f6f8b6c9d3c7e99f30d1944725e6d0eae0eeba34928e0262172f6e1ccd10f99dfb44d2e39537663a4ab72ebb3ce65f8f1b001c13 +LibTracyClient.v0.9.0+1.armv6l-linux-gnueabihf.tar.gz/md5/7c1541edbe31bfb9e43f4ec09a3aa748 +LibTracyClient.v0.9.0+1.armv6l-linux-gnueabihf.tar.gz/sha512/ab8c2502c0fa743538b8929756f283514ee4c69a6fc65555dca7b95021c36ce827ee33e8594d0447f15fa9bd1df873b1a1a75f876989813386f46a803c504c06 +LibTracyClient.v0.9.0+1.armv6l-linux-musleabihf.tar.gz/md5/2904a775192b8bb53c170f28d3588ea0 +LibTracyClient.v0.9.0+1.armv6l-linux-musleabihf.tar.gz/sha512/1b1288619a72e30a1e414295591d93e122c9c478e574e31c09f49e6ee3b665a64a883cd367566cec9ba95abb5fdcc51056d9853400f441ddd0f27a369a20bae3 +LibTracyClient.v0.9.0+1.armv7l-linux-gnueabihf.tar.gz/md5/7773f17dab1acdcb6b9e749dfb04f727 +LibTracyClient.v0.9.0+1.armv7l-linux-gnueabihf.tar.gz/sha512/49b7a433aa9945cfd20702584916ab24cf2e35a67804635c11726576763a09c5f2e578002e175d9ca3e109e29c454b4ad5db2e267ed5aeb002eff45965a74704 +LibTracyClient.v0.9.0+1.armv7l-linux-musleabihf.tar.gz/md5/9c1799102529603793bf180c2fd432ec +LibTracyClient.v0.9.0+1.armv7l-linux-musleabihf.tar.gz/sha512/ab2fcde7a59754b15d36f39e88fddbf1f198e15221680b9cd0dcb7eb43becc498d17ca1763ec576479646f0d4a1947a9b39f340db800e859751105d7d7aa5ed6 +LibTracyClient.v0.9.0+1.i686-linux-gnu.tar.gz/md5/4f64c950d319cdeeec379cef58f41a13 +LibTracyClient.v0.9.0+1.i686-linux-gnu.tar.gz/sha512/9258ec31e5023e7b503c36f1d76d4e6c0b7492abeb177ffe772a64da1d5db6f199031d22956a7702a809b1263b49aef614763816d1a18f5590d0a00b3f6243f1 +LibTracyClient.v0.9.0+1.i686-linux-musl.tar.gz/md5/d5524ddb8c8537b02b737bd7f2a68275 +LibTracyClient.v0.9.0+1.i686-linux-musl.tar.gz/sha512/f149ea48cff01f6cd9da40692eed9900d5b2ff3a3e10e27bb10b62a89034f37f9a68fc080b97d41efb95718472898c8cc6271a8934f907275cde19f44582de08 +LibTracyClient.v0.9.0+1.i686-w64-mingw32.tar.gz/md5/c415459220b24c0a67553e4691801639 +LibTracyClient.v0.9.0+1.i686-w64-mingw32.tar.gz/sha512/57c8826be9fb049fa418a72dc8fbf576b6fbf45da1662f07ed041b9e55c36e487c02c43a1e64003d76a0040f0e998201e8b0d3853960023ea440a2daadcb4f77 +LibTracyClient.v0.9.0+1.powerpc64le-linux-gnu.tar.gz/md5/5eacaa3672522f45733595182ba643fc +LibTracyClient.v0.9.0+1.powerpc64le-linux-gnu.tar.gz/sha512/4edf154a9ac126fe31879b7962af127d99e5afd19dc046275ddb26b9f455431fd6fd398373a01d6f8865b090cb87ed521a5919b8037d569c569c36c7a8a2f72f +LibTracyClient.v0.9.0+1.x86_64-apple-darwin.tar.gz/md5/bb517fdccbf51c7f0889919735889d65 +LibTracyClient.v0.9.0+1.x86_64-apple-darwin.tar.gz/sha512/a3587248776c859e60d367e91908e36fd9f7fd3e27320dfac2c7527ee120c1a2652b2da1c0c1a006d2c28c7f93992dd4a3b2565e7f2c5feec6a5661cc4cf080e +LibTracyClient.v0.9.0+1.x86_64-linux-gnu.tar.gz/md5/2dd1cbcf917c7df9df110b99f393b916 +LibTracyClient.v0.9.0+1.x86_64-linux-gnu.tar.gz/sha512/3d0dc4cd3df2528678a0305baa23d5cda97406f73a3def3ff2fd8ee2517f07051e19719faf99604fddb3aa5b20574b92b240f7898d392d9e21431f275c0a8aa8 +LibTracyClient.v0.9.0+1.x86_64-linux-musl.tar.gz/md5/d1b02aaf45f13ba34f4f1138b83f8ce7 +LibTracyClient.v0.9.0+1.x86_64-linux-musl.tar.gz/sha512/7c8a3748238d015de4be7074c1efe72b2cda9dbc23c2ab722750885cd01cd4a6ea6e37b241fc997d41ab3949154b4a5bddbfd8f3a59ca153e9b42136a154a02a +LibTracyClient.v0.9.0+1.x86_64-unknown-freebsd.tar.gz/md5/7bb6f98ab2a39a062293c95f91b959f0 +LibTracyClient.v0.9.0+1.x86_64-unknown-freebsd.tar.gz/sha512/72935612fbfb339003b9be38c64a53c6a19a58b8427485b4351f18933a2ec7a4f7bf00556996501ccd3857e8085910af72020e4507951eb8ee094287a82208ae +LibTracyClient.v0.9.0+1.x86_64-w64-mingw32.tar.gz/md5/f3b60a51e8f64ec62c07597f6c4e52f7 +LibTracyClient.v0.9.0+1.x86_64-w64-mingw32.tar.gz/sha512/3ef5916f9a441e8655569c803392987a39c3baa79ac9ac446760cc60577619a616ee1365673d5323eb1c5884a6bd9e283b4094cdcbf42eba6b409a0348643b25 libtracyclient-5a1f5371b792c12aea324213e1dc738b2923ae21.tar.gz/md5/62791801e0ffb11a7d70c2d724a230be libtracyclient-5a1f5371b792c12aea324213e1dc738b2923ae21.tar.gz/sha512/b0570e048eee08ba5e21e0f855b2346cad408b0b86cdf79c8bb3727bb0ab57167dc7988f4dd1ee4157b371135201b87d1491237c09a2934de65eb3b9e26fcdc2 diff --git a/deps/libtracyclient.version b/deps/libtracyclient.version index 49f879b96204b..52bc74cedb660 100644 --- a/deps/libtracyclient.version +++ b/deps/libtracyclient.version @@ -1,6 +1,6 @@ ## jll artifact LIBTRACYCLIENT_JLL_NAME := LibTracyClient -LIBTRACYCLIENT_JLL_VER := 0.9.0+0 +LIBTRACYCLIENT_JLL_VER := 0.9.0+1 ## source build LIBTRACYCLIENT_VER := 0.9.0 From ae5799a5782c051f2c46e0275865e552b741568f Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 28 Feb 2023 15:07:48 -0700 Subject: [PATCH 200/775] Setup `pthread_sigmask` before loading libjulia-internal Loading libjulia-internal and libjulia-codegen can trigger constructors in their dependencies, including some which may spawn threads (e.g. Tracy). It is important that these not be configured to receive any signals that libjulia-internal is expecting to `sigwait()` on, esp. when their default action is to terminate the process. Technically, the masked signal set should probably be synchronized with the `sigwait_sigs` set in `signals-unix.c`, but for now this simply masks all signals. --- cli/loader.h | 1 + cli/loader_lib.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/cli/loader.h b/cli/loader.h index 66e990e623460..d68b07da00050 100644 --- a/cli/loader.h +++ b/cli/loader.h @@ -45,6 +45,7 @@ #include #include #include +#include #endif diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 1fd28674bc8eb..b959d176bb382 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -353,6 +353,17 @@ static char *libstdcxxprobe(void) void *libjulia_internal = NULL; void *libjulia_codegen = NULL; __attribute__((constructor)) void jl_load_libjulia_internal(void) { +#if defined(_OS_LINUX_) + // Julia uses `sigwait()` to handle signals, and all threads are required + // to mask the corresponding handlers so that the signals can be waited on. + // Here, we setup that masking early, so that it is inherited by any threads + // spawned (e.g. by constructors) when loading deps of libjulia-internal. + + sigset_t all_signals, prev_mask; + sigfillset(&all_signals); + pthread_sigmask(SIG_BLOCK, &all_signals, &prev_mask); +#endif + // Only initialize this once if (libjulia_internal != NULL) { return; @@ -521,6 +532,13 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { // jl_options must be initialized very early, in case an embedder sets some // values there before calling jl_init ((void (*)(void))jl_init_options_addr)(); + +#if defined(_OS_LINUX_) + // Restore the original signal mask. `jl_init()` will later setup blocking + // for the specific set of signals we `sigwait()` on, and any threads spawned + // during loading above will still retain their inherited signal mask. + pthread_sigmask(SIG_SETMASK, &prev_mask, NULL); +#endif } // Load libjulia and run the REPL with the given arguments (in UTF-8 format) From 03b1c26af4c3a9c3e479d238cf00b153252cd289 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Wed, 1 Mar 2023 20:36:12 -0700 Subject: [PATCH 201/775] Use BUILDDIR instead of SRCCACHE for libTracyCore This is the right strategy according to #31530, so that the patching process doesn't hit any races between multiple builds running from the same SRCCACHE. --- deps/libtracyclient.mk | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/deps/libtracyclient.mk b/deps/libtracyclient.mk index b8f33ed47b913..29427889c1d6a 100644 --- a/deps/libtracyclient.mk +++ b/deps/libtracyclient.mk @@ -2,7 +2,7 @@ ifneq ($(USE_BINARYBUILDER_LIBTRACYCLIENT),1) LIBTRACYCLIENT_GIT_URL:=https://github.com/wolfpld/tracy.git LIBTRACYCLIENT_TAR_URL=https://api.github.com/repos/wolfpld/tracy/tarball/$1 -$(eval $(call git-external,libtracyclient,LIBTRACYCLIENT,,,$(SRCCACHE))) +$(eval $(call git-external,libtracyclient,LIBTRACYCLIENT,,,$(BUILDDIR))) LIBTRACYCLIENT_BUILDDIR := $(BUILDDIR)/$(LIBTRACYCLIENT_SRC_DIR) LIBTRACYCLIENT_SRCCACHE := $(SRCCACHE)/$(LIBTRACYCLIENT_SRC_DIR) @@ -17,28 +17,28 @@ LIBTRACYCLIENT_CMAKE += -DTRACY_NO_FRAME_IMAGE=ON LIBTRACYCLIENT_CMAKE += -DTRACY_NO_CRASH_HANDLER=ON LIBTRACYCLIENT_CMAKE += -DTRACY_ON_DEMAND=ON -$(LIBTRACYCLIENT_SRCCACHE)/cmake-patch-applied: $(LIBTRACYCLIENT_SRCCACHE)/source-extracted +$(LIBTRACYCLIENT_BUILDDIR)/cmake-patch-applied: $(LIBTRACYCLIENT_BUILDDIR)/source-extracted ifneq ($(OS),WINNT) - echo "target_compile_definitions(TracyClient PUBLIC __STDC_FORMAT_MACROS)" >> $(LIBTRACYCLIENT_SRCCACHE)/CMakeLists.txt + echo "target_compile_definitions(TracyClient PUBLIC __STDC_FORMAT_MACROS)" >> $(LIBTRACYCLIENT_BUILDDIR)/CMakeLists.txt else - echo "target_compile_definitions(TracyClient PUBLIC WINVER=0x0602 _WIN32_WINNT=0x0602)" >> $(LIBTRACYCLIENT_SRCCACHE)/CMakeLists.txt + echo "target_compile_definitions(TracyClient PUBLIC WINVER=0x0602 _WIN32_WINNT=0x0602)" >> $(LIBTRACYCLIENT_BUILDDIR)/CMakeLists.txt endif echo 1 > $@ -$(LIBTRACYCLIENT_SRCCACHE)/libTracyClient-freebsd-elfw.patch-applied: $(LIBTRACYCLIENT_SRCCACHE)/cmake-patch-applied - cd $(LIBTRACYCLIENT_SRCCACHE) && \ +$(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-freebsd-elfw.patch-applied: $(LIBTRACYCLIENT_BUILDDIR)/cmake-patch-applied + cd $(LIBTRACYCLIENT_BUILDDIR) && \ patch -p1 -f < $(SRCDIR)/patches/libTracyClient-freebsd-elfw.patch echo 1 > $@ -$(LIBTRACYCLIENT_SRCCACHE)/libTracyClient-no-crash-handler.patch-applied: $(LIBTRACYCLIENT_SRCCACHE)/libTracyClient-freebsd-elfw.patch-applied - cd $(LIBTRACYCLIENT_SRCCACHE) && \ +$(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-no-crash-handler.patch-applied: $(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-freebsd-elfw.patch-applied + cd $(LIBTRACYCLIENT_BUILDDIR) && \ patch -p1 -f < $(SRCDIR)/patches/libTracyClient-no-crash-handler.patch echo 1 > $@ -$(LIBTRACYCLIENT_BUILDDIR)/build-configured: $(LIBTRACYCLIENT_SRCCACHE)/libTracyClient-no-crash-handler.patch-applied +$(LIBTRACYCLIENT_BUILDDIR)/build-configured: $(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-no-crash-handler.patch-applied mkdir -p $(dir $@) cd $(dir $@) && \ - $(CMAKE) $(LIBTRACYCLIENT_SRCCACHE) $(CMAKE_GENERATOR_COMMAND) $(CMAKE_COMMON) $(LIBTRACYCLIENT_CMAKE) \ + $(CMAKE) . $(CMAKE_GENERATOR_COMMAND) $(CMAKE_COMMON) $(LIBTRACYCLIENT_CMAKE) \ || { echo '*** To install a newer version of cmake, run contrib/download_cmake.sh ***' && false; } echo 1 > $@ @@ -59,7 +59,7 @@ clean-libtracyclient: -$(MAKE) -C $(LIBTRACYCLIENT_BUILDDIR) clean get-libtracyclient: $(LIBTRACYCLIENT_SRC_FILE) -extract-libtracyclient: $(LIBTRACYCLIENT_SRCCACHE)/source-extracted +extract-libtracyclient: $(LIBTRACYCLIENT_BUILDDIR)/source-extracted configure-libtracyclient: $(LIBTRACYCLIENT_BUILDDIR)/build-configured compile-libtracyclient: $(LIBTRACYCLIENT_BUILDDIR)/build-compiled fastcheck-libtracyclient: check-libtracyclient From afae4db2cfa6be1af91ed3e8b97496cf72ea0e9b Mon Sep 17 00:00:00 2001 From: Tim Stahlhut Date: Thu, 2 Mar 2023 08:21:23 -0500 Subject: [PATCH 202/775] deps/curl: Remove "without-ssl" because of configure: error: --without-ssl has been set together with an explicit option to use an ssl library --- deps/curl.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/curl.mk b/deps/curl.mk index 435ee278e3468..a063dfe07fba0 100644 --- a/deps/curl.mk +++ b/deps/curl.mk @@ -36,7 +36,7 @@ checksum-curl: $(SRCCACHE)/curl-$(CURL_VER).tar.bz2 ## xref: https://github.com/JuliaPackaging/Yggdrasil/blob/master/L/LibCURL/common.jl # Disable....almost everything CURL_CONFIGURE_FLAGS := $(CONFIGURE_COMMON) \ - --without-ssl --without-gnutls --without-libidn2 --without-librtmp \ + --without-gnutls --without-libidn2 --without-librtmp \ --without-nss --without-libpsl --without-libgsasl --without-fish-functions-dir \ --disable-ares --disable-manual --disable-ldap --disable-ldaps --disable-static \ --without-gssapi --without-brotli From dbcac4335f79f8c8f42a07061088ad07f0cee71b Mon Sep 17 00:00:00 2001 From: Udoh Jeremiah Date: Thu, 2 Mar 2023 16:46:02 +0100 Subject: [PATCH 203/775] complete function signature for `varinfo` (#48852) --- stdlib/InteractiveUtils/src/InteractiveUtils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/InteractiveUtils/src/InteractiveUtils.jl b/stdlib/InteractiveUtils/src/InteractiveUtils.jl index 1f0b05c29c3b5..2b8990764509a 100644 --- a/stdlib/InteractiveUtils/src/InteractiveUtils.jl +++ b/stdlib/InteractiveUtils/src/InteractiveUtils.jl @@ -21,7 +21,7 @@ include("macros.jl") include("clipboard.jl") """ - varinfo(m::Module=Main, pattern::Regex=r""; all::Bool = false, imported::Bool = false, sortby::Symbol = :name, minsize::Int = 0) + varinfo(m::Module=Main, pattern::Regex=r""; all::Bool = false, imported::Bool = false, recursive::Bool = false, sortby::Symbol = :name, minsize::Int = 0) Return a markdown table giving information about exported global variables in a module, optionally restricted to those matching `pattern`. From a4d94a3db3c73eb23e97507faabdd69d2cdf3a77 Mon Sep 17 00:00:00 2001 From: Udoh Jeremiah Date: Fri, 3 Mar 2023 00:11:16 +0100 Subject: [PATCH 204/775] Close code block for `div12` docstring (#48866) --- base/twiceprecision.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index 6a1232cdcd810..b6d702d9242b8 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -141,6 +141,7 @@ julia> hi, lo = Base.div12(x, y) julia> Float64(hi) + Float64(lo) 1.0134170444063066 +``` """ function div12(x::T, y::T) where {T<:AbstractFloat} # We lose precision if any intermediate calculation results in a subnormal. From 903dbd5f59a7883df041d87cfac9ca446e39a922 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 3 Mar 2023 09:40:31 +0900 Subject: [PATCH 205/775] effects: handle va-method properly when refining `:inaccessiblememonly` (#48854) Previously the `:inaccessiblememonly` effect bit may be wrongly refined when analyzing va-method, e.g.: ```julia julia> callgetfield1(xs...) = getfield(getfield(xs, 1), 1) callgetfield1 (generic function with 1 method) julia> Base.infer_effects(callgetfield1, (Base.RefValue{Symbol},)) (+c,+e,!n,+t,+s,+m,+i) # inaccessiblememonly is wrongly refined here ``` This leads to wrong concrete evaluation of `callgetfield1` and thus may result in a problem like below: ```julia julia> const GLOBAL_XS = Ref(:julia); julia> global_getfield() = callgetfield1(GLOBAL_XS); julia> @test let Base.Experimental.@force_compile global_getfield() end === :julia Test Passed julia> GLOBAL_XS[] = :julia2; julia> @test let Base.Experimental.@force_compile global_getfield() end === :julia2 # this fails Test Failed at REPL[8]:1 Expression: let #= REPL[8]:2 =# Base.Experimental.@force_compile global_getfield() end === :julia2 Evaluated: julia === julia2 ``` This commit fixes it up. --- base/compiler/abstractinterpretation.jl | 2 +- base/compiler/inferencestate.jl | 8 +++++--- base/compiler/typeinfer.jl | 2 +- test/compiler/effects.jl | 15 +++++++++++++++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 6e843f460ac5d..93ead3207fce2 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2823,7 +2823,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) @assert !frame.inferred frame.dont_work_on_me = true # mark that this function is currently on the stack W = frame.ip - nargs = narguments(frame) + nargs = narguments(frame, #=include_va=#false) slottypes = frame.slottypes ssavaluetypes = frame.ssavaluetypes bbs = frame.cfg.blocks diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index df7bb67d625e8..e3f4cb2866aa3 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -607,9 +607,11 @@ get_curr_ssaflag(sv::InferenceState) = sv.src.ssaflags[sv.currpc] add_curr_ssaflag!(sv::InferenceState, flag::UInt8) = sv.src.ssaflags[sv.currpc] |= flag sub_curr_ssaflag!(sv::InferenceState, flag::UInt8) = sv.src.ssaflags[sv.currpc] &= ~flag -function narguments(sv::InferenceState) +function narguments(sv::InferenceState, include_va::Bool=true) def = sv.linfo.def - isva = isa(def, Method) && def.isva - nargs = length(sv.result.argtypes) - isva + nargs = length(sv.result.argtypes) + if !include_va + nargs -= isa(def, Method) && def.isva + end return nargs end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 35a2e25e62d11..ab7ca4b56a9bd 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -444,7 +444,7 @@ function adjust_effects(sv::InferenceState) # always throwing an error counts or never returning both count as consistent ipo_effects = Effects(ipo_effects; consistent=ALWAYS_TRUE) end - if is_inaccessiblemem_or_argmemonly(ipo_effects) && all(1:narguments(sv)) do i::Int + if is_inaccessiblemem_or_argmemonly(ipo_effects) && all(1:narguments(sv, #=include_va=#true)) do i::Int return is_mutation_free_argtype(sv.slottypes[i]) end ipo_effects = Effects(ipo_effects; inaccessiblememonly=ALWAYS_TRUE) diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index dceb737ed6ae7..33420efc91d3c 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -603,6 +603,21 @@ global inconsistent_condition_ref = Ref{Bool}(false) end end |> !Core.Compiler.is_consistent +# should handle va-method properly +callgetfield1(xs...) = getfield(getfield(xs, 1), 1) +@test !Core.Compiler.is_inaccessiblememonly(Base.infer_effects(callgetfield1, (Base.RefValue{Symbol},))) +const GLOBAL_XS = Ref(:julia) +global_getfield() = callgetfield1(GLOBAL_XS) +@test let + Base.Experimental.@force_compile + global_getfield() +end === :julia +GLOBAL_XS[] = :julia2 +@test let + Base.Experimental.@force_compile + global_getfield() +end === :julia2 + # the `:inaccessiblememonly` helper effect allows us to prove `:effect_free`-ness of frames # including `setfield!` modifying local mutable object From 3c7ca401c3ee6366ace1e686749ac55fbba2869f Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Thu, 2 Mar 2023 22:07:09 -0300 Subject: [PATCH 206/775] Vector{Any} is not mutation free so make it so (#48868) --- src/jltypes.c | 1 + test/compiler/effects.jl | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/jltypes.c b/src/jltypes.c index 5ce67085198ad..0b7ef9424b6bf 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2805,6 +2805,7 @@ void jl_init_types(void) JL_GC_DISABLED // Array's mutable data is hidden, so we need to override it ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_array_type))->ismutationfree = 0; + ((jl_datatype_t*)jl_array_any_type)->ismutationfree = 0; // override the preferred layout for a couple types jl_lineinfonode_type->name->mayinlinealloc = 0; // FIXME: assumed to be a pointer by codegen diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index 33420efc91d3c..03f60062a9ebe 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -804,3 +804,6 @@ unknown_sparam_nothrow2(x::Ref{Ref{T}}) where T = (T; nothing) @test !Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_throw, (Any,))) @test Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_nothrow1, (Ref,))) @test Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_nothrow2, (Ref{Ref{T}} where T,))) + +# https://github.com/JuliaLang/julia/issues/48856 +@test Base.ismutationfree(Vector{Any}) == false From 48b4caaf94c6dcfc9d28e9929ebb27d480b7b6cc Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 2 Mar 2023 22:42:18 -0600 Subject: [PATCH 207/775] Calculate a checksum for the system image (#48869) --- src/staticdata.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/staticdata.c b/src/staticdata.c index df1171d6a44a9..cd9ed8b0db088 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -3300,10 +3300,9 @@ static jl_value_t *jl_restore_package_image_from_stream(ios_t *f, jl_image_t *im return restored; } -static void jl_restore_system_image_from_stream(ios_t *f, jl_image_t *image) +static void jl_restore_system_image_from_stream(ios_t *f, jl_image_t *image, uint32_t checksum) { - uint64_t checksum = 0; // TODO: make this real - jl_restore_system_image_from_stream_(f, image, NULL, checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + jl_restore_system_image_from_stream_(f, image, NULL, checksum | ((uint64_t)0xfdfcfbfa << 32), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(const char *buf, jl_image_t *image, size_t sz, jl_array_t *depmods, int completeinfo) @@ -3354,8 +3353,9 @@ JL_DLLEXPORT void jl_restore_system_image(const char *fname) if (ios_readall(&f, sysimg, len) != len) jl_errorf("Error reading system image file."); ios_close(&f); + uint32_t checksum = jl_crc32c(0, sysimg, len); ios_static_buffer(&f, sysimg, len); - jl_restore_system_image_from_stream(&f, &sysimage); + jl_restore_system_image_from_stream(&f, &sysimage, checksum); ios_close(&f); JL_SIGATOMIC_END(); } @@ -3366,7 +3366,8 @@ JL_DLLEXPORT void jl_restore_system_image_data(const char *buf, size_t len) ios_t f; JL_SIGATOMIC_BEGIN(); ios_static_buffer(&f, (char*)buf, len); - jl_restore_system_image_from_stream(&f, &sysimage); + uint32_t checksum = jl_crc32c(0, buf, len); + jl_restore_system_image_from_stream(&f, &sysimage, checksum); ios_close(&f); JL_SIGATOMIC_END(); } From 51db8af409f0a386dc26a1fa674f923db0dcc6a3 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Fri, 3 Mar 2023 14:32:51 -0300 Subject: [PATCH 208/775] Fix heapsize hint and use a line for max memory (#48747) * Fix heapsize hint and use a line so that large machines utilize more of their ram --- src/gc.c | 16 +++++++++++++--- src/jl_exported_funcs.inc | 1 + src/julia.h | 1 + test/cmdlineargs.jl | 2 ++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/gc.c b/src/gc.c index 743c5704a53cb..88df8cfeb7aa4 100644 --- a/src/gc.c +++ b/src/gc.c @@ -3228,8 +3228,6 @@ void jl_init_thread_heap(jl_ptls_t ptls) // System-wide initializations void jl_gc_init(void) { - if (jl_options.heap_size_hint) - jl_gc_set_max_memory(jl_options.heap_size_hint); JL_MUTEX_INIT(&heapsnapshot_lock); JL_MUTEX_INIT(&finalizers_lock); @@ -3253,8 +3251,15 @@ void jl_gc_init(void) uint64_t constrained_mem = uv_get_constrained_memory(); if (constrained_mem > 0 && constrained_mem < total_mem) total_mem = constrained_mem; - max_total_memory = total_mem / 10 * 6; + double percent; + if (total_mem < 128e9) + percent = total_mem * 2.34375e-12 + 0.6; // 60% at 0 gigs and 90% at 128 to not + else // overcommit too much on memory contrained devices + percent = 0.9; + max_total_memory = total_mem * percent; #endif + if (jl_options.heap_size_hint) + jl_gc_set_max_memory(jl_options.heap_size_hint); t_start = jl_hrtime(); } @@ -3267,6 +3272,11 @@ JL_DLLEXPORT void jl_gc_set_max_memory(uint64_t max_mem) } } +JL_DLLEXPORT uint64_t jl_gc_get_max_memory(void) +{ + return max_total_memory; +} + // callback for passing OOM errors from gmp JL_DLLEXPORT void jl_throw_out_of_memory_error(void) { diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index c5389978217d6..b84c3338e401a 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -172,6 +172,7 @@ XX(jl_gc_external_obj_hdr_size) \ XX(jl_gc_find_taggedvalue_pool) \ XX(jl_gc_get_total_bytes) \ + XX(jl_gc_get_max_memory) \ XX(jl_gc_internal_obj_base_ptr) \ XX(jl_gc_is_enabled) \ XX(jl_gc_live_bytes) \ diff --git a/src/julia.h b/src/julia.h index 4374c8a7ceeed..f2b6823a133e3 100644 --- a/src/julia.h +++ b/src/julia.h @@ -921,6 +921,7 @@ JL_DLLEXPORT void jl_free_stack(void *stkbuf, size_t bufsz); 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); +JL_DLLEXPORT uint64_t jl_gc_get_max_memory(void); JL_DLLEXPORT void jl_clear_malloc_data(void); diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 0917f4c4257f3..eafcb4ebe89d0 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -901,4 +901,6 @@ end @test lines[3] == "foo" @test lines[4] == "bar" end +#heap-size-hint +@test readchomp(`$(Base.julia_cmd()) --startup-file=no --heap-size-hint=500M -e "println(@ccall jl_gc_get_max_memory()::UInt64)"`) == "524288000" end From 3f3be09d10d17f8ca9d6cdc09fbe43a903e086d9 Mon Sep 17 00:00:00 2001 From: numbermaniac <5206120+numbermaniac@users.noreply.github.com> Date: Sat, 4 Mar 2023 05:57:52 +1100 Subject: [PATCH 209/775] Small grammar fix in sysimg.md (#48879) --- doc/src/devdocs/sysimg.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/devdocs/sysimg.md b/doc/src/devdocs/sysimg.md index a21e3ba265f9b..3058834e927d0 100644 --- a/doc/src/devdocs/sysimg.md +++ b/doc/src/devdocs/sysimg.md @@ -101,7 +101,7 @@ See code comments for each components for more implementation details. (see comments in `MultiVersioning::runOnModule` for how this is done), the pass also generates metadata so that the runtime can load and initialize the system image correctly. - A detail description of the metadata is available in `src/processor.h`. + A detailed description of the metadata is available in `src/processor.h`. 2. System image loading From ef077475456a87223caf9718cf8461ffd1e46171 Mon Sep 17 00:00:00 2001 From: Felix Cremer Date: Fri, 3 Mar 2023 20:15:51 +0100 Subject: [PATCH 210/775] Link to VS Code instead of Juno in Profile manual (#48778) According to the Juno website, development focus has been shifted to VS Code. --- doc/src/manual/profile.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/profile.md b/doc/src/manual/profile.md index b2c9d722f57e6..f3438d2a80524 100644 --- a/doc/src/manual/profile.md +++ b/doc/src/manual/profile.md @@ -59,7 +59,7 @@ julia> @profile myfunc() To see the profiling results, there are several graphical browsers. One "family" of visualizers is based on [FlameGraphs.jl](https://github.com/timholy/FlameGraphs.jl), with each family member providing a different user interface: -- [Juno](https://junolab.org/) is a full IDE with built-in support for profile visualization +- [VS Code](https://www.julia-vscode.org/) is a full IDE with built-in support for profile visualization - [ProfileView.jl](https://github.com/timholy/ProfileView.jl) is a stand-alone visualizer based on GTK - [ProfileVega.jl](https://github.com/davidanthoff/ProfileVega.jl) uses VegaLight and integrates well with Jupyter notebooks - [StatProfilerHTML.jl](https://github.com/tkluck/StatProfilerHTML.jl) produces HTML and presents some additional summaries, and also integrates well with Jupyter notebooks From 778947fa029f26121eded40da508906289ad91a4 Mon Sep 17 00:00:00 2001 From: ndinsmore <45537276+ndinsmore@users.noreply.github.com> Date: Fri, 3 Mar 2023 15:23:46 -0500 Subject: [PATCH 211/775] Vectorized isascii using simple loop 25+bytes/cycle for large strings (#48568) Co-authored-by: matthias314 <56549971+matthias314@users.noreply.github.com> --- base/strings/basic.jl | 32 ++++++++++++++++++++++++++++++++ base/strings/string.jl | 7 +------ base/strings/substring.jl | 2 ++ test/strings/basic.jl | 26 ++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index ebd6907d7e96c..2609edeaaaa18 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -613,6 +613,38 @@ isascii(c::Char) = bswap(reinterpret(UInt32, c)) < 0x80 isascii(s::AbstractString) = all(isascii, s) isascii(c::AbstractChar) = UInt32(c) < 0x80 +@inline function _isascii(code_units::AbstractVector{CU}, first, last) where {CU} + r = zero(CU) + for n = first:last + @inbounds r |= code_units[n] + end + return 0 ≤ r < 0x80 +end + +#The chunking algorithm makes the last two chunks overlap inorder to keep the size fixed +@inline function _isascii_chunks(chunk_size,cu::AbstractVector{CU}, first,last) where {CU} + n=first + while n <= last - chunk_size + _isascii(cu,n,n+chunk_size-1) || return false + n += chunk_size + end + return _isascii(cu,last-chunk_size+1,last) +end +""" + isascii(cu::AbstractVector{CU}) where {CU <: Integer} -> Bool + +Test whether all values in the vector belong to the ASCII character set (0x00 to 0x7f). +This function is intended to be used by other string implementations that need a fast ASCII check. +""" +function isascii(cu::AbstractVector{CU}) where {CU <: Integer} + chunk_size = 1024 + chunk_threshold = chunk_size + (chunk_size ÷ 2) + first = firstindex(cu); last = lastindex(cu) + l = last - first + 1 + l < chunk_threshold && return _isascii(cu,first,last) + return _isascii_chunks(chunk_size,cu,first,last) +end + ## string map, filter ## function map(f, s::AbstractString) diff --git a/base/strings/string.jl b/base/strings/string.jl index 3d8db74d7b795..59241223f4d49 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -326,12 +326,7 @@ end isvalid(s::String, i::Int) = checkbounds(Bool, s, i) && thisind(s, i) == i -function isascii(s::String) - @inbounds for i = 1:ncodeunits(s) - codeunit(s, i) >= 0x80 && return false - end - return true -end +isascii(s::String) = isascii(codeunits(s)) """ repeat(c::AbstractChar, r::Integer) -> String diff --git a/base/strings/substring.jl b/base/strings/substring.jl index baaea038b2cfe..76658b377c7b4 100644 --- a/base/strings/substring.jl +++ b/base/strings/substring.jl @@ -92,6 +92,8 @@ function getindex(s::SubString, i::Integer) @inbounds return getindex(s.string, s.offset + i) end +isascii(ss::SubString{String}) = isascii(codeunits(ss)) + function isvalid(s::SubString, i::Integer) ib = true @boundscheck ib = checkbounds(Bool, s, i) diff --git a/test/strings/basic.jl b/test/strings/basic.jl index ed3a0fe858051..168a01caac207 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -1125,6 +1125,32 @@ end @test sprint(summary, "") == "empty String" end +@testset "isascii" begin + N = 1 + @test isascii("S"^N) == true + @test isascii("S"^(N - 1)) == true + @test isascii("S"^(N + 1)) == true + + @test isascii("λ" * ("S"^(N))) == false + @test isascii(("S"^(N)) * "λ") == false + + for p = 1:16 + N = 2^p + @test isascii("S"^N) == true + @test isascii("S"^(N - 1)) == true + @test isascii("S"^(N + 1)) == true + + @test isascii("λ" * ("S"^(N))) == false + @test isascii(("S"^(N)) * "λ") == false + @test isascii("λ"*("S"^(N - 1))) == false + @test isascii(("S"^(N - 1)) * "λ") == false + if N > 4 + @test isascii("λ" * ("S"^(N - 3))) == false + @test isascii(("S"^(N - 3)) * "λ") == false + end + end +end + @testset "Plug holes in test coverage" begin @test_throws MethodError checkbounds(Bool, "abc", [1.0, 2.0]) From 615b142c88a074399bac08a0e8fd8f48b491c1fd Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Tue, 3 Jan 2023 16:03:18 -0500 Subject: [PATCH 212/775] Simplify multiversioning --- src/llvm-multiversioning.cpp | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 242b0c454ad0a..68042700bb1d0 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -222,8 +222,6 @@ struct CloneCtx { int idx; uint32_t flags; std::unique_ptr vmap; // ValueToValueMapTy is not movable.... - // function ids that needs relocation to be initialized - std::set relocs{}; Target(int idx, const jl_target_spec_t &spec) : idx(idx), flags(spec.flags), @@ -290,8 +288,6 @@ struct CloneCtx { std::vector> gv_relocs{}; // Mapping from function id (i.e. 0-based index in `fvars`) to GVs to be initialized. std::map const_relocs; - // Functions that were referred to by a global alias, and might not have other uses. - std::set alias_relocs; bool has_veccall{false}; bool has_cloneall{false}; bool allow_bad_fvars{false}; @@ -734,13 +730,6 @@ void CloneCtx::rewrite_alias(GlobalAlias *alias, Function *F) uint32_t id; GlobalVariable *slot; std::tie(id, slot) = get_reloc_slot(F); - for (auto &grp: groups) { - grp.relocs.insert(id); - for (auto &tgt: grp.clones) { - tgt.relocs.insert(id); - } - } - alias_relocs.insert(id); auto BB = BasicBlock::Create(F->getContext(), "top", trampoline); IRBuilder<> irbuilder(BB); @@ -884,15 +873,6 @@ void CloneCtx::fix_inst_uses() if (!use_f->getName().endswith(suffix)) return nullptr; std::tie(id, slot) = get_reloc_slot(orig_f); - - grp.relocs.insert(id); - for (auto &tgt: grp.clones) { - // The enclosing function of the use is cloned, - // no need to deal with this use on this target. - if (map_get(*tgt.vmap, use_f)) - continue; - tgt.relocs.insert(id); - } return slot; }, tbaa_const); } @@ -1018,12 +998,10 @@ void CloneCtx::emit_metadata() } auto it = const_relocs.find(id); if (it != const_relocs.end()) { + shared_relocs.insert(id); values.push_back(id_v); values.push_back(get_ptrdiff32(it->second, gbase)); } - if (alias_relocs.find(id) != alias_relocs.end()) { - shared_relocs.insert(id); - } } values[0] = ConstantInt::get(T_int32, values.size() / 2); ArrayType *vars_type = ArrayType::get(T_int32, values.size()); @@ -1046,7 +1024,7 @@ void CloneCtx::emit_metadata() auto grp = static_cast(tgt); count = jl_sysimg_tag_mask; for (uint32_t j = 0; j < nfvars; j++) { - if (shared_relocs.count(j) || tgt->relocs.count(j)) { + if (shared_relocs.count(j)) { count++; idxs.push_back(j); } @@ -1061,7 +1039,7 @@ void CloneCtx::emit_metadata() idxs.push_back(baseidx); for (uint32_t j = 0; j < nfvars; j++) { auto base_f = grp->base_func(fvars[j]); - if (shared_relocs.count(j) || tgt->relocs.count(j)) { + if (shared_relocs.count(j)) { count++; idxs.push_back(jl_sysimg_tag_mask | j); auto f = map_get(*tgt->vmap, base_f, base_f); From 27808e136757c19c9d7accfafae3958f3f48b7f1 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Tue, 3 Jan 2023 18:17:46 -0500 Subject: [PATCH 213/775] Refactor aotcompile --- src/aotcompile.cpp | 231 ++++++++++++++++++++--------------- src/llvm-multiversioning.cpp | 31 +---- 2 files changed, 140 insertions(+), 122 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 907735dfa0128..d3d4529d32c30 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -496,7 +496,8 @@ static void injectCRTAlias(Module &M, StringRef name, StringRef alias, FunctionT if (!target) { target = Function::Create(FT, Function::ExternalLinkage, alias, M); } - Function *interposer = Function::Create(FT, Function::InternalLinkage, name, M); + Function *interposer = Function::Create(FT, Function::ExternalLinkage, name, M); + interposer->setVisibility(GlobalValue::HiddenVisibility); appendToCompilerUsed(M, {interposer}); llvm::IRBuilder<> builder(BasicBlock::Create(M.getContext(), "top", interposer)); @@ -532,7 +533,7 @@ void jl_dump_native_impl(void *native_code, TheTriple.setObjectFormat(Triple::MachO); TheTriple.setOS(llvm::Triple::MacOSX); #endif - std::unique_ptr TM( + std::unique_ptr SourceTM( jl_ExecutionEngine->getTarget().createTargetMachine( TheTriple.getTriple(), jl_ExecutionEngine->getTargetCPU(), @@ -554,53 +555,16 @@ void jl_dump_native_impl(void *native_code, )); - // set up optimization passes - SmallVector bc_Buffer; - SmallVector obj_Buffer; - SmallVector asm_Buffer; - SmallVector unopt_bc_Buffer; - raw_svector_ostream bc_OS(bc_Buffer); - raw_svector_ostream obj_OS(obj_Buffer); - raw_svector_ostream asm_OS(asm_Buffer); - raw_svector_ostream unopt_bc_OS(unopt_bc_Buffer); std::vector bc_Archive; std::vector obj_Archive; std::vector asm_Archive; std::vector unopt_bc_Archive; std::vector outputs; - PassBuilder emptyPB; - AnalysisManagers empty(emptyPB); - ModulePassManager preopt, postopt; - legacy::PassManager emitter; // MC emission is only supported on legacy PM - - if (unopt_bc_fname) - preopt.addPass(BitcodeWriterPass(unopt_bc_OS)); - - if (bc_fname) - postopt.addPass(BitcodeWriterPass(bc_OS)); - //Is this necessary for TM? - addTargetPasses(&emitter, TM->getTargetTriple(), TM->getTargetIRAnalysis()); - if (obj_fname) - if (TM->addPassesToEmitFile(emitter, obj_OS, nullptr, CGFT_ObjectFile, false)) - jl_safe_printf("ERROR: target does not support generation of object files\n"); - if (asm_fname) - if (TM->addPassesToEmitFile(emitter, asm_OS, nullptr, CGFT_AssemblyFile, false)) - jl_safe_printf("ERROR: target does not support generation of object files\n"); - // Reset the target triple to make sure it matches the new target machine auto dataM = data->M.getModuleUnlocked(); - dataM->setTargetTriple(TM->getTargetTriple().str()); - dataM->setDataLayout(jl_create_datalayout(*TM)); - -#ifndef JL_USE_NEW_PM - legacy::PassManager optimizer; - addTargetPasses(&optimizer, TM->getTargetTriple(), TM->getTargetIRAnalysis()); - addOptimizationPasses(&optimizer, jl_options.opt_level, true, true); - addMachinePasses(&optimizer, jl_options.opt_level); -#else - NewPM optimizer{std::move(TM), getOptLevel(jl_options.opt_level), OptimizationOptions::defaults(true, true)}; -#endif + dataM->setTargetTriple(SourceTM->getTargetTriple().str()); + dataM->setDataLayout(jl_create_datalayout(*SourceTM)); Type *T_size; if (sizeof(size_t) == 8) @@ -609,8 +573,10 @@ void jl_dump_native_impl(void *native_code, T_size = Type::getInt32Ty(Context); Type *T_psize = T_size->getPointerTo(); + bool imaging_mode = imaging_default() || jl_options.outputo; + // add metadata information - if (imaging_default() || jl_options.outputo) { + if (imaging_mode) { emit_offset_table(*dataM, data->jl_sysimg_gvars, "jl_sysimg_gvars", T_psize); emit_offset_table(*dataM, data->jl_sysimg_fvars, "jl_sysimg_fvars", T_psize); @@ -626,70 +592,87 @@ void jl_dump_native_impl(void *native_code, } // do the actual work - auto add_output = [&] (Module &M, StringRef unopt_bc_Name, StringRef bc_Name, StringRef obj_Name, StringRef asm_Name, bool inject_crt) { - preopt.run(M, empty.MAM); - if (bc_fname || obj_fname || asm_fname) { - assert(!verifyModule(M, &errs())); - optimizer.run(M); - assert(!verifyModule(M, &errs())); + auto add_output = [&] (Module &M, StringRef unopt_bc_Name, StringRef bc_Name, StringRef obj_Name, StringRef asm_Name) { + + auto TM = std::unique_ptr( + SourceTM->getTarget().createTargetMachine( + SourceTM->getTargetTriple().str(), + SourceTM->getTargetCPU(), + SourceTM->getTargetFeatureString(), + SourceTM->Options, + SourceTM->getRelocationModel(), + SourceTM->getCodeModel(), + SourceTM->getOptLevel())); + + if (unopt_bc_fname) { + SmallVector Buffer; + raw_svector_ostream OS(Buffer); + PassBuilder PB; + AnalysisManagers AM{*TM, PB, OptimizationLevel::O0}; + ModulePassManager MPM; + MPM.addPass(BitcodeWriterPass(OS)); + emit_result(unopt_bc_Archive, Buffer, unopt_bc_Name, outputs); } + if (!bc_fname && !obj_fname && !asm_fname) { + return; + } + assert(!verifyModule(M, &errs())); - if (inject_crt) { - // We would like to emit an alias or an weakref alias to redirect these symbols - // but LLVM doesn't let us emit a GlobalAlias to a declaration... - // So for now we inject a definition of these functions that calls our runtime - // functions. We do so after optimization to avoid cloning these functions. - injectCRTAlias(M, "__gnu_h2f_ieee", "julia__gnu_h2f_ieee", - FunctionType::get(Type::getFloatTy(Context), { Type::getHalfTy(Context) }, false)); - injectCRTAlias(M, "__extendhfsf2", "julia__gnu_h2f_ieee", - FunctionType::get(Type::getFloatTy(Context), { Type::getHalfTy(Context) }, false)); - injectCRTAlias(M, "__gnu_f2h_ieee", "julia__gnu_f2h_ieee", - FunctionType::get(Type::getHalfTy(Context), { Type::getFloatTy(Context) }, false)); - injectCRTAlias(M, "__truncsfhf2", "julia__gnu_f2h_ieee", - FunctionType::get(Type::getHalfTy(Context), { Type::getFloatTy(Context) }, false)); - injectCRTAlias(M, "__truncdfhf2", "julia__truncdfhf2", - FunctionType::get(Type::getHalfTy(Context), { Type::getDoubleTy(Context) }, false)); +#ifndef JL_USE_NEW_PM + legacy::PassManager optimizer; + addTargetPasses(&optimizer, TM->getTargetTriple(), TM->getTargetIRAnalysis()); + addOptimizationPasses(&optimizer, jl_options.opt_level, true, true); + addMachinePasses(&optimizer, jl_options.opt_level); +#else -#if defined(_OS_WINDOWS_) - // Windows expect that the function `_DllMainStartup` is present in an dll. - // Normal compilers use something like Zig's crtdll.c instead we provide a - // a stub implementation. - auto T_pvoid = Type::getInt8Ty(Context)->getPointerTo(); - auto T_int32 = Type::getInt32Ty(Context); - auto FT = FunctionType::get(T_int32, {T_pvoid, T_int32, T_pvoid}, false); - auto F = Function::Create(FT, Function::ExternalLinkage, "_DllMainCRTStartup", M); - F->setCallingConv(CallingConv::X86_StdCall); - - llvm::IRBuilder<> builder(BasicBlock::Create(M.getContext(), "top", F)); - builder.CreateRet(ConstantInt::get(T_int32, 1)); + auto PMTM = std::unique_ptr( + SourceTM->getTarget().createTargetMachine( + SourceTM->getTargetTriple().str(), + SourceTM->getTargetCPU(), + SourceTM->getTargetFeatureString(), + SourceTM->Options, + SourceTM->getRelocationModel(), + SourceTM->getCodeModel(), + SourceTM->getOptLevel())); + NewPM optimizer{std::move(PMTM), getOptLevel(jl_options.opt_level), OptimizationOptions::defaults(true, true)}; #endif + optimizer.run(M); + assert(!verifyModule(M, &errs())); + + if (bc_fname) { + SmallVector Buffer; + raw_svector_ostream OS(Buffer); + PassBuilder PB; + AnalysisManagers AM{*TM, PB, OptimizationLevel::O0}; + ModulePassManager MPM; + MPM.addPass(BitcodeWriterPass(OS)); + emit_result(bc_Archive, Buffer, bc_Name, outputs); } - postopt.run(M, empty.MAM); - - // Get target by snooping on multiversioning - GlobalVariable *target_ids = M.getNamedGlobal("jl_dispatch_target_ids"); - if (s && target_ids) { - if(auto targets = dyn_cast(target_ids->getInitializer())) { - auto rawTargets = targets->getRawDataValues(); - write_int32(s, rawTargets.size()); - ios_write(s, rawTargets.data(), rawTargets.size()); - }; + if (obj_fname) { + SmallVector Buffer; + raw_svector_ostream OS(Buffer); + legacy::PassManager emitter; + addTargetPasses(&emitter, TM->getTargetTriple(), TM->getTargetIRAnalysis()); + if (TM->addPassesToEmitFile(emitter, OS, nullptr, CGFT_ObjectFile, false)) + jl_safe_printf("ERROR: target does not support generation of object files\n"); + emitter.run(M); + emit_result(obj_Archive, Buffer, obj_Name, outputs); } - emitter.run(M); - - if (unopt_bc_fname) - emit_result(unopt_bc_Archive, unopt_bc_Buffer, unopt_bc_Name, outputs); - if (bc_fname) - emit_result(bc_Archive, bc_Buffer, bc_Name, outputs); - if (obj_fname) - emit_result(obj_Archive, obj_Buffer, obj_Name, outputs); - if (asm_fname) - emit_result(asm_Archive, asm_Buffer, asm_Name, outputs); + if (asm_fname) { + SmallVector Buffer; + raw_svector_ostream OS(Buffer); + legacy::PassManager emitter; + addTargetPasses(&emitter, TM->getTargetTriple(), TM->getTargetIRAnalysis()); + if (TM->addPassesToEmitFile(emitter, OS, nullptr, CGFT_AssemblyFile, false)) + jl_safe_printf("ERROR: target does not support generation of assembly files\n"); + emitter.run(M); + emit_result(asm_Archive, Buffer, asm_Name, outputs); + } }; - add_output(*dataM, "unopt.bc", "text.bc", "text.o", "text.s", true); + add_output(*dataM, "unopt.bc", "text.bc", "text.o", "text.s"); orc::ThreadSafeModule sysimage(std::make_unique("sysimage", Context), TSCtx); auto sysimageM = sysimage.getModuleUnlocked(); @@ -699,6 +682,35 @@ void jl_dump_native_impl(void *native_code, sysimageM->setStackProtectorGuard(dataM->getStackProtectorGuard()); sysimageM->setOverrideStackAlignment(dataM->getOverrideStackAlignment()); #endif + // We would like to emit an alias or an weakref alias to redirect these symbols + // but LLVM doesn't let us emit a GlobalAlias to a declaration... + // So for now we inject a definition of these functions that calls our runtime + // functions. We do so after optimization to avoid cloning these functions. + injectCRTAlias(*sysimageM, "__gnu_h2f_ieee", "julia__gnu_h2f_ieee", + FunctionType::get(Type::getFloatTy(Context), { Type::getHalfTy(Context) }, false)); + injectCRTAlias(*sysimageM, "__extendhfsf2", "julia__gnu_h2f_ieee", + FunctionType::get(Type::getFloatTy(Context), { Type::getHalfTy(Context) }, false)); + injectCRTAlias(*sysimageM, "__gnu_f2h_ieee", "julia__gnu_f2h_ieee", + FunctionType::get(Type::getHalfTy(Context), { Type::getFloatTy(Context) }, false)); + injectCRTAlias(*sysimageM, "__truncsfhf2", "julia__gnu_f2h_ieee", + FunctionType::get(Type::getHalfTy(Context), { Type::getFloatTy(Context) }, false)); + injectCRTAlias(*sysimageM, "__truncdfhf2", "julia__truncdfhf2", + FunctionType::get(Type::getHalfTy(Context), { Type::getDoubleTy(Context) }, false)); + + if (TheTriple.isOSWindows()) { + // Windows expect that the function `_DllMainStartup` is present in an dll. + // Normal compilers use something like Zig's crtdll.c instead we provide a + // a stub implementation. + auto T_pvoid = Type::getInt8Ty(Context)->getPointerTo(); + auto T_int32 = Type::getInt32Ty(Context); + auto FT = FunctionType::get(T_int32, {T_pvoid, T_int32, T_pvoid}, false); + auto F = Function::Create(FT, Function::ExternalLinkage, "_DllMainCRTStartup", *sysimageM); + F->setCallingConv(CallingConv::X86_StdCall); + + llvm::IRBuilder<> builder(BasicBlock::Create(Context, "top", F)); + builder.CreateRet(ConstantInt::get(T_int32, 1)); + } + bool has_veccall = dataM->getModuleFlag("julia.mv.veccall"); data->M = orc::ThreadSafeModule(); // free memory for data->M if (sysimg_data) { @@ -712,7 +724,32 @@ void jl_dump_native_impl(void *native_code, GlobalVariable::ExternalLinkage, len, "jl_system_image_size")); } - add_output(*sysimageM, "data.bc", "data.bc", "data.o", "data.s", false); + if (imaging_mode) { + auto specs = jl_get_llvm_clone_targets(); + const uint32_t base_flags = has_veccall ? JL_TARGET_VEC_CALL : 0; + std::vector data; + auto push_i32 = [&] (uint32_t v) { + uint8_t buff[4]; + memcpy(buff, &v, 4); + data.insert(data.end(), buff, buff + 4); + }; + push_i32(specs.size()); + for (uint32_t i = 0; i < specs.size(); i++) { + push_i32(base_flags | (specs[i].flags & JL_TARGET_UNKNOWN_NAME)); + auto &specdata = specs[i].data; + data.insert(data.end(), specdata.begin(), specdata.end()); + } + auto value = ConstantDataArray::get(Context, data); + addComdat(new GlobalVariable(*sysimageM, value->getType(), true, + GlobalVariable::ExternalLinkage, + value, "jl_dispatch_target_ids")); + + if (s) { + write_int32(s, data.size()); + ios_write(s, (const char *)data.data(), data.size()); + } + } + add_output(*sysimageM, "data.bc", "data.bc", "data.o", "data.s"); object::Archive::Kind Kind = getDefaultForHost(TheTriple); if (unopt_bc_fname) diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 68042700bb1d0..c94aee9927540 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -289,7 +289,6 @@ struct CloneCtx { // Mapping from function id (i.e. 0-based index in `fvars`) to GVs to be initialized. std::map const_relocs; bool has_veccall{false}; - bool has_cloneall{false}; bool allow_bad_fvars{false}; }; @@ -345,7 +344,6 @@ CloneCtx::CloneCtx(Module &M, function_ref GetLI, function for (uint32_t i = 1; i < ntargets; i++) { auto &spec = specs[i]; if (spec.flags & JL_TARGET_CLONE_ALL) { - has_cloneall = true; groups.emplace_back(i, spec); } else { @@ -404,7 +402,7 @@ void CloneCtx::clone_function(Function *F, Function *new_f, ValueToValueMapTy &v // Clone all clone_all targets. Makes sure that the base targets are all available. void CloneCtx::clone_bases() { - if (!has_cloneall) + if (groups.size() == 1) return; uint32_t ngrps = groups.size(); for (uint32_t gid = 1; gid < ngrps; gid++) { @@ -553,7 +551,7 @@ void CloneCtx::check_partial(Group &grp, Target &tgt) F->getName() + suffix, &M); new_f->copyAttributesFrom(F); vmap[F] = new_f; - if (!has_cloneall) + if (groups.size() == 1) cloned.insert(orig_f); grp.clone_fs.insert(i); all_origs.insert(orig_f); @@ -607,7 +605,7 @@ void CloneCtx::check_partial(Group &grp, Target &tgt) continue; auto orig_f = orig_funcs[i]; if (all_origs.count(orig_f)) { - if (!has_cloneall) + if (groups.size() == 1) cloned.insert(orig_f); grp.clone_fs.insert(i); } @@ -787,7 +785,7 @@ void CloneCtx::fix_gv_uses() return changed; }; for (auto orig_f: orig_funcs) { - if (!has_cloneall && !cloned.count(orig_f)) + if (groups.size() == 1 && !cloned.count(orig_f)) continue; while (single_pass(orig_f)) { } @@ -952,25 +950,8 @@ void CloneCtx::emit_metadata() } } - // Generate `jl_dispatch_target_ids` - { - const uint32_t base_flags = has_veccall ? JL_TARGET_VEC_CALL : 0; - std::vector data; - auto push_i32 = [&] (uint32_t v) { - uint8_t buff[4]; - memcpy(buff, &v, 4); - data.insert(data.end(), buff, buff + 4); - }; - push_i32(ntargets); - for (uint32_t i = 0; i < ntargets; i++) { - push_i32(base_flags | (specs[i].flags & JL_TARGET_UNKNOWN_NAME)); - auto &specdata = specs[i].data; - data.insert(data.end(), specdata.begin(), specdata.end()); - } - auto value = ConstantDataArray::get(M.getContext(), data); - add_comdat(new GlobalVariable(M, value->getType(), true, - GlobalVariable::ExternalLinkage, - value, "jl_dispatch_target_ids")); + if (has_veccall) { + M.addModuleFlag(Module::Max, "julia.mv.veccall", 1); } // Generate `jl_dispatch_reloc_slots` From 4524987a384f444e02c0e21afacfc3c4f4d68a4e Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Tue, 3 Jan 2023 18:41:07 -0500 Subject: [PATCH 214/775] Timing print statements --- src/aotcompile.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index d3d4529d32c30..2c9edecae7df7 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -273,6 +273,8 @@ void replaceUsesWithLoad(Function &F, function_refreleaseContext(std::move(ctx)); } + end = jl_hrtime(); + dbgs() << "jl_create_native: " << (end - start) / 1e9 << "s\n"; return (void*)data; } @@ -517,6 +521,8 @@ void jl_dump_native_impl(void *native_code, const char *asm_fname, const char *sysimg_data, size_t sysimg_len, ios_t *s) { + uint64_t start = jl_hrtime(); + uint64_t end = 0; JL_TIMING(NATIVE_DUMP); jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code; auto TSCtx = data->M.getContext(); @@ -575,6 +581,12 @@ void jl_dump_native_impl(void *native_code, bool imaging_mode = imaging_default() || jl_options.outputo; + end = jl_hrtime(); + + dbgs() << "setup time: " << (end - start) / 1e9 << "s\n"; + + start = jl_hrtime(); + // add metadata information if (imaging_mode) { emit_offset_table(*dataM, data->jl_sysimg_gvars, "jl_sysimg_gvars", T_psize); @@ -591,6 +603,12 @@ void jl_dump_native_impl(void *native_code, "jl_RTLD_DEFAULT_handle_pointer")); } + end = jl_hrtime(); + + dbgs() << "metadata time: " << (end - start) / 1e9 << "s\n"; + + start = jl_hrtime(); + // do the actual work auto add_output = [&] (Module &M, StringRef unopt_bc_Name, StringRef bc_Name, StringRef obj_Name, StringRef asm_Name) { @@ -618,6 +636,9 @@ void jl_dump_native_impl(void *native_code, } assert(!verifyModule(M, &errs())); + uint64_t start = jl_hrtime(); + end = 0; + #ifndef JL_USE_NEW_PM legacy::PassManager optimizer; addTargetPasses(&optimizer, TM->getTargetTriple(), TM->getTargetIRAnalysis()); @@ -639,6 +660,10 @@ void jl_dump_native_impl(void *native_code, optimizer.run(M); assert(!verifyModule(M, &errs())); + end = jl_hrtime(); + + dbgs() << "optimize time: " << (end - start) / 1e9 << "s\n"; + if (bc_fname) { SmallVector Buffer; raw_svector_ostream OS(Buffer); @@ -649,6 +674,8 @@ void jl_dump_native_impl(void *native_code, emit_result(bc_Archive, Buffer, bc_Name, outputs); } + start = jl_hrtime(); + if (obj_fname) { SmallVector Buffer; raw_svector_ostream OS(Buffer); @@ -660,6 +687,10 @@ void jl_dump_native_impl(void *native_code, emit_result(obj_Archive, Buffer, obj_Name, outputs); } + end = jl_hrtime(); + + dbgs() << "codegen time: " << (end - start) / 1e9 << "s\n"; + if (asm_fname) { SmallVector Buffer; raw_svector_ostream OS(Buffer); @@ -674,6 +705,12 @@ void jl_dump_native_impl(void *native_code, add_output(*dataM, "unopt.bc", "text.bc", "text.o", "text.s"); + end = jl_hrtime(); + + dbgs() << "text output time: " << (end - start) / 1e9 << "s\n"; + + start = jl_hrtime(); + orc::ThreadSafeModule sysimage(std::make_unique("sysimage", Context), TSCtx); auto sysimageM = sysimage.getModuleUnlocked(); sysimageM->setTargetTriple(dataM->getTargetTriple()); @@ -751,6 +788,12 @@ void jl_dump_native_impl(void *native_code, } add_output(*sysimageM, "data.bc", "data.bc", "data.o", "data.s"); + end = jl_hrtime(); + + dbgs() << "data module time: " << (end - start) / 1e9 << "s\n"; + + start = jl_hrtime(); + object::Archive::Kind Kind = getDefaultForHost(TheTriple); if (unopt_bc_fname) handleAllErrors(writeArchive(unopt_bc_fname, unopt_bc_Archive, true, @@ -764,6 +807,10 @@ void jl_dump_native_impl(void *native_code, if (asm_fname) handleAllErrors(writeArchive(asm_fname, asm_Archive, true, Kind, true, false), reportWriterError); + + end = jl_hrtime(); + + dbgs() << "archive time: " << (end - start) / 1e9 << "s\n"; delete data; } From 094269c8c1e506e36f0b4bd7ddc6ec38f279bb3c Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Thu, 5 Jan 2023 14:36:16 -0500 Subject: [PATCH 215/775] Move image init to processor.cpp --- src/llvm-multiversioning.cpp | 67 ++++++++++++++---------------- src/processor.cpp | 79 +++++++++++++++++++++++++----------- src/processor.h | 11 ++++- src/processor_arm.cpp | 4 +- src/processor_fallback.cpp | 4 +- src/processor_x86.cpp | 4 +- src/staticdata.c | 69 ++----------------------------- 7 files changed, 103 insertions(+), 135 deletions(-) diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index c94aee9927540..3325cb47147a6 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -253,21 +253,14 @@ struct CloneCtx { void emit_metadata(); private: void prepare_vmap(ValueToValueMapTy &vmap); - bool is_vector(FunctionType *ty) const; void clone_function(Function *F, Function *new_f, ValueToValueMapTy &vmap); uint32_t collect_func_info(Function &F); void check_partial(Group &grp, Target &tgt); void clone_partial(Group &grp, Target &tgt); - void add_features(Function *F, StringRef name, StringRef features, uint32_t flags) const; - template - T *add_comdat(T *G) const; uint32_t get_func_id(Function *F); template Constant *rewrite_gv_init(const Stack& stack); std::pair get_reloc_slot(Function *F); - Constant *get_ptrdiff32(Constant *ptr, Constant *base) const; - template - Constant *emit_offset_table(const std::vector &vars, StringRef name) const; void rewrite_alias(GlobalAlias *alias, Function* F); MDNode *tbaa_const; @@ -424,7 +417,7 @@ void CloneCtx::clone_bases() } } -bool CloneCtx::is_vector(FunctionType *ty) const +static bool is_vector(FunctionType *ty) { if (ty->getReturnType()->isVectorTy()) return true; @@ -507,6 +500,29 @@ void CloneCtx::collect_func_infos() } } +static void add_features(Function *F, StringRef name, StringRef features, uint32_t flags) +{ + auto attr = F->getFnAttribute("target-features"); + if (attr.isStringAttribute()) { + std::string new_features(attr.getValueAsString()); + new_features += ","; + new_features += features; + F->addFnAttr("target-features", new_features); + } + else { + F->addFnAttr("target-features", features); + } + F->addFnAttr("target-cpu", name); + if (!F->hasFnAttribute(Attribute::OptimizeNone)) { + if (flags & JL_TARGET_OPTSIZE) { + F->addFnAttr(Attribute::OptimizeForSize); + } + else if (flags & JL_TARGET_MINSIZE) { + F->addFnAttr(Attribute::MinSize); + } + } +} + void CloneCtx::clone_all_partials() { // First decide what to clone @@ -632,29 +648,6 @@ void CloneCtx::clone_partial(Group &grp, Target &tgt) } } -void CloneCtx::add_features(Function *F, StringRef name, StringRef features, uint32_t flags) const -{ - auto attr = F->getFnAttribute("target-features"); - if (attr.isStringAttribute()) { - std::string new_features(attr.getValueAsString()); - new_features += ","; - new_features += features; - F->addFnAttr("target-features", new_features); - } - else { - F->addFnAttr("target-features", features); - } - F->addFnAttr("target-cpu", name); - if (!F->hasFnAttribute(Attribute::OptimizeNone)) { - if (flags & JL_TARGET_OPTSIZE) { - F->addFnAttr(Attribute::OptimizeForSize); - } - else if (flags & JL_TARGET_MINSIZE) { - F->addFnAttr(Attribute::MinSize); - } - } -} - uint32_t CloneCtx::get_func_id(Function *F) { auto &ref = func_ids[F]; @@ -878,7 +871,7 @@ void CloneCtx::fix_inst_uses() } template -inline T *CloneCtx::add_comdat(T *G) const +static inline T *add_comdat(T *G) { #if defined(_OS_WINDOWS_) // add __declspec(dllexport) to everything marked for export @@ -890,7 +883,7 @@ inline T *CloneCtx::add_comdat(T *G) const return G; } -Constant *CloneCtx::get_ptrdiff32(Constant *ptr, Constant *base) const +static Constant *get_ptrdiff32(Constant *ptr, Constant *base) { if (ptr->getType()->isPointerTy()) ptr = ConstantExpr::getPtrToInt(ptr, getSizeTy(ptr->getContext())); @@ -899,7 +892,7 @@ Constant *CloneCtx::get_ptrdiff32(Constant *ptr, Constant *base) const } template -Constant *CloneCtx::emit_offset_table(const std::vector &vars, StringRef name) const +static Constant *emit_offset_table(Module &M, const std::vector &vars, StringRef name) { auto T_int32 = Type::getInt32Ty(M.getContext()); auto T_size = getSizeTy(M.getContext()); @@ -911,7 +904,7 @@ Constant *CloneCtx::emit_offset_table(const std::vector &vars, StringRef nam name + "_base", base, &M)); } else { - base = ConstantExpr::getNullValue(T_size->getPointerTo()); + base = add_comdat(new GlobalVariable(M, T_size, true, GlobalValue::ExternalLinkage, Constant::getNullValue(T_size), name + "_base")); } auto vbase = ConstantExpr::getPtrToInt(base, T_size); std::vector offsets(nvars + 1); @@ -938,8 +931,8 @@ void CloneCtx::emit_metadata() } // Store back the information about exported functions. - auto fbase = emit_offset_table(fvars, "jl_sysimg_fvars"); - auto gbase = emit_offset_table(gvars, "jl_sysimg_gvars"); + auto fbase = emit_offset_table(M, fvars, "jl_sysimg_fvars"); + auto gbase = emit_offset_table(M, gvars, "jl_sysimg_gvars"); uint32_t ntargets = specs.size(); SmallVector targets(ntargets); diff --git a/src/processor.cpp b/src/processor.cpp index 13b40ec4f7363..a8aca2a64ab19 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -17,6 +17,10 @@ #include "julia_assert.h" +#ifndef _OS_WINDOWS_ +#include +#endif + // CPU target string is a list of strings separated by `;` each string starts with a CPU // or architecture name and followed by an optional list of features separated by `,`. // A "generic" or empty CPU name means the basic required feature set of the target ISA @@ -621,44 +625,53 @@ static inline std::vector> &get_cmdline_targets(F &&feature_cb) // Load sysimg, use the `callback` for dispatch and perform all relocations // for the selected target. template -static inline jl_image_fptrs_t parse_sysimg(void *hdl, F &&callback) +static inline jl_image_t parse_sysimg(void *hdl, F &&callback) { - jl_image_fptrs_t res = {nullptr, 0, nullptr, 0, nullptr, nullptr}; + jl_image_t res{}; // .data base char *data_base; - if (!jl_dlsym(hdl, "jl_sysimg_gvars_base", (void**)&data_base, 0)) { - data_base = NULL; + jl_dlsym(hdl, "jl_sysimg_gvars_base", (void**)&data_base, 1); + + { + void *pgcstack_func_slot; + if (jl_dlsym(hdl, "jl_pgcstack_func_slot", &pgcstack_func_slot, 0)) { + void *pgcstack_key_slot; + jl_dlsym(hdl, "jl_pgcstack_key_slot", &pgcstack_key_slot, 1); + jl_pgcstack_getkey((jl_get_pgcstack_func**)pgcstack_func_slot, (jl_pgcstack_key_t*)pgcstack_key_slot); + + size_t *tls_offset_idx; + jl_dlsym(hdl, "jl_tls_offset", (void **)&tls_offset_idx, 1); + *tls_offset_idx = (uintptr_t)(jl_tls_offset == -1 ? 0 : jl_tls_offset); + } } + // .text base char *text_base; - if (!jl_dlsym(hdl, "jl_sysimg_fvars_base", (void**)&text_base, 0)) { - text_base = NULL; - } - res.base = text_base; + jl_dlsym(hdl, "jl_sysimg_fvars_base", (void**)&text_base, 1); - int32_t *offsets; + const int32_t *offsets; jl_dlsym(hdl, "jl_sysimg_fvars_offsets", (void**)&offsets, 1); uint32_t nfunc = offsets[0]; - res.offsets = offsets + 1; + offsets++; - void *ids; - jl_dlsym(hdl, "jl_dispatch_target_ids", &ids, 1); + const void *ids; + jl_dlsym(hdl, "jl_dispatch_target_ids", (void**)&ids, 1); uint32_t target_idx = callback(ids); - int32_t *reloc_slots; + const int32_t *reloc_slots; jl_dlsym(hdl, "jl_dispatch_reloc_slots", (void **)&reloc_slots, 1); const uint32_t nreloc = reloc_slots[0]; reloc_slots += 1; - uint32_t *clone_idxs; - int32_t *clone_offsets; + const uint32_t *clone_idxs; + const int32_t *clone_offsets; jl_dlsym(hdl, "jl_dispatch_fvars_idxs", (void**)&clone_idxs, 1); jl_dlsym(hdl, "jl_dispatch_fvars_offsets", (void**)&clone_offsets, 1); uint32_t tag_len = clone_idxs[0]; clone_idxs += 1; assert(tag_len & jl_sysimg_tag_mask); - std::vector base_offsets = {res.offsets}; + std::vector base_offsets = {offsets}; // Find target for (uint32_t i = 0;i < target_idx;i++) { uint32_t len = jl_sysimg_val_mask & tag_len; @@ -680,20 +693,20 @@ static inline jl_image_fptrs_t parse_sysimg(void *hdl, F &&callback) if (clone_all) { // clone_all if (target_idx != 0) { - res.offsets = clone_offsets; + offsets = clone_offsets; } } else { uint32_t base_idx = clone_idxs[0]; assert(base_idx < target_idx); if (target_idx != 0) { - res.offsets = base_offsets[base_idx]; - assert(res.offsets); + offsets = base_offsets[base_idx]; + assert(offsets); } clone_idxs++; - res.nclones = tag_len; - res.clone_offsets = clone_offsets; - res.clone_idxs = clone_idxs; + res.fptrs.nclones = tag_len; + res.fptrs.clone_offsets = clone_offsets; + res.fptrs.clone_idxs = clone_idxs; } // Do relocation uint32_t reloc_i = 0; @@ -702,7 +715,7 @@ static inline jl_image_fptrs_t parse_sysimg(void *hdl, F &&callback) uint32_t idx = clone_idxs[i]; int32_t offset; if (clone_all) { - offset = res.offsets[idx]; + offset = offsets[idx]; } else if (idx & jl_sysimg_tag_mask) { idx = idx & jl_sysimg_val_mask; @@ -718,7 +731,7 @@ static inline jl_image_fptrs_t parse_sysimg(void *hdl, F &&callback) found = true; auto slot = (const void**)(data_base + reloc_slots[reloc_i * 2 + 1]); assert(slot); - *slot = offset + res.base; + *slot = offset + text_base; } else if (reloc_idx > idx) { break; @@ -728,6 +741,24 @@ static inline jl_image_fptrs_t parse_sysimg(void *hdl, F &&callback) (void)found; } + res.fptrs.base = text_base; + res.fptrs.offsets = offsets; + res.gvars_base = (uintptr_t *)data_base; + jl_dlsym(hdl, "jl_sysimg_gvars_offsets", (void **)&res.gvars_offsets, 1); + res.gvars_offsets += 1; + +#ifdef _OS_WINDOWS_ + res.base = (intptr_t)hdl; +#else + Dl_info dlinfo; + if (dladdr((void*)res.gvars_base, &dlinfo) != 0) { + res.base = (intptr_t)dlinfo.dli_fbase; + } + else { + res.base = 0; + } +#endif + return res; } diff --git a/src/processor.h b/src/processor.h index e3f3bd512c910..f76722e885a1d 100644 --- a/src/processor.h +++ b/src/processor.h @@ -155,6 +155,13 @@ typedef struct _jl_image_fptrs_t { const uint32_t *clone_idxs; } jl_image_fptrs_t; +typedef struct { + uint64_t base; + uintptr_t *gvars_base; + const int32_t *gvars_offsets; + jl_image_fptrs_t fptrs; +} jl_image_t; + /** * Initialize the processor dispatch system with sysimg `hdl` (also initialize the sysimg itself). * The dispatch system will find the best implementation to be used in this session. @@ -165,8 +172,8 @@ typedef struct _jl_image_fptrs_t { * * Return the data about the function pointers selected. */ -jl_image_fptrs_t jl_init_processor_sysimg(void *hdl); -jl_image_fptrs_t jl_init_processor_pkgimg(void *hdl); +jl_image_t jl_init_processor_sysimg(void *hdl); +jl_image_t jl_init_processor_pkgimg(void *hdl); // Return the name of the host CPU as a julia string. JL_DLLEXPORT jl_value_t *jl_get_cpu_name(void); diff --git a/src/processor_arm.cpp b/src/processor_arm.cpp index 3e7b22caf00d4..0797fa4381f9d 100644 --- a/src/processor_arm.cpp +++ b/src/processor_arm.cpp @@ -1802,14 +1802,14 @@ JL_DLLEXPORT jl_value_t *jl_get_cpu_name(void) return jl_cstr_to_string(host_cpu_name().c_str()); } -jl_image_fptrs_t jl_init_processor_sysimg(void *hdl) +jl_image_t jl_init_processor_sysimg(void *hdl) { if (!jit_targets.empty()) jl_error("JIT targets already initialized"); return parse_sysimg(hdl, sysimg_init_cb); } -jl_image_fptrs_t jl_init_processor_pkgimg(void *hdl) +jl_image_t jl_init_processor_pkgimg(void *hdl) { if (jit_targets.empty()) jl_error("JIT targets not initialized"); diff --git a/src/processor_fallback.cpp b/src/processor_fallback.cpp index c1353e1bb43b0..1aebde6dab90a 100644 --- a/src/processor_fallback.cpp +++ b/src/processor_fallback.cpp @@ -112,14 +112,14 @@ get_llvm_target_str(const TargetData<1> &data) using namespace Fallback; -jl_image_fptrs_t jl_init_processor_sysimg(void *hdl) +jl_image_t jl_init_processor_sysimg(void *hdl) { if (!jit_targets.empty()) jl_error("JIT targets already initialized"); return parse_sysimg(hdl, sysimg_init_cb); } -jl_image_fptrs_t jl_init_processor_pkgimg(void *hdl) +jl_image_t jl_init_processor_pkgimg(void *hdl) { if (jit_targets.empty()) jl_error("JIT targets not initialized"); diff --git a/src/processor_x86.cpp b/src/processor_x86.cpp index 6b3e7d5b63678..30a6ff9b3dede 100644 --- a/src/processor_x86.cpp +++ b/src/processor_x86.cpp @@ -1039,14 +1039,14 @@ JL_DLLEXPORT jl_value_t *jl_get_cpu_name(void) return jl_cstr_to_string(host_cpu_name().c_str()); } -jl_image_fptrs_t jl_init_processor_sysimg(void *hdl) +jl_image_t jl_init_processor_sysimg(void *hdl) { if (!jit_targets.empty()) jl_error("JIT targets already initialized"); return parse_sysimg(hdl, sysimg_init_cb); } -jl_image_fptrs_t jl_init_processor_pkgimg(void *hdl) +jl_image_t jl_init_processor_pkgimg(void *hdl) { if (jit_targets.empty()) jl_error("JIT targets not initialized"); diff --git a/src/staticdata.c b/src/staticdata.c index cd9ed8b0db088..94e93f4198b4c 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -315,13 +315,6 @@ void *native_functions; // opaque jl_native_code_desc_t blob used for fetching // table of struct field addresses to rewrite during saving static htable_t field_replace; -typedef struct { - uint64_t base; - uintptr_t *gvars_base; - int32_t *gvars_offsets; - jl_image_fptrs_t fptrs; -} jl_image_t; - // array of definitions for the predefined function pointers // (reverse of fptr_to_id) // This is a manually constructed dual of the fvars array, which would be produced by codegen for Julia code, for C. @@ -446,7 +439,7 @@ typedef struct { static void *jl_sysimg_handle = NULL; static jl_image_t sysimage; -static inline uintptr_t *sysimg_gvars(uintptr_t *base, int32_t *offsets, size_t idx) +static inline uintptr_t *sysimg_gvars(uintptr_t *base, const int32_t *offsets, size_t idx) { return base + offsets[idx] / sizeof(base[0]); } @@ -461,32 +454,7 @@ static void jl_load_sysimg_so(void) int imaging_mode = jl_generating_output() && !jl_options.incremental; // in --build mode only use sysimg data, not precompiled native code if (!imaging_mode && jl_options.use_sysimage_native_code==JL_OPTIONS_USE_SYSIMAGE_NATIVE_CODE_YES) { - jl_dlsym(jl_sysimg_handle, "jl_sysimg_gvars_base", (void **)&sysimage.gvars_base, 1); - jl_dlsym(jl_sysimg_handle, "jl_sysimg_gvars_offsets", (void **)&sysimage.gvars_offsets, 1); - sysimage.gvars_offsets += 1; assert(sysimage.fptrs.base); - - void *pgcstack_func_slot; - jl_dlsym(jl_sysimg_handle, "jl_pgcstack_func_slot", &pgcstack_func_slot, 1); - void *pgcstack_key_slot; - jl_dlsym(jl_sysimg_handle, "jl_pgcstack_key_slot", &pgcstack_key_slot, 1); - jl_pgcstack_getkey((jl_get_pgcstack_func**)pgcstack_func_slot, (jl_pgcstack_key_t*)pgcstack_key_slot); - - size_t *tls_offset_idx; - jl_dlsym(jl_sysimg_handle, "jl_tls_offset", (void **)&tls_offset_idx, 1); - *tls_offset_idx = (uintptr_t)(jl_tls_offset == -1 ? 0 : jl_tls_offset); - -#ifdef _OS_WINDOWS_ - sysimage.base = (intptr_t)jl_sysimg_handle; -#else - Dl_info dlinfo; - if (dladdr((void*)sysimage.gvars_base, &dlinfo) != 0) { - sysimage.base = (intptr_t)dlinfo.dli_fbase; - } - else { - sysimage.base = 0; - } -#endif } else { memset(&sysimage.fptrs, 0, sizeof(sysimage.fptrs)); @@ -2693,7 +2661,7 @@ JL_DLLEXPORT void jl_set_sysimg_so(void *handle) if (jl_options.cpu_target == NULL) jl_options.cpu_target = "native"; jl_sysimg_handle = handle; - sysimage.fptrs = jl_init_processor_sysimg(handle); + sysimage = jl_init_processor_sysimg(handle); } #ifndef JL_NDEBUG @@ -3391,38 +3359,7 @@ JL_DLLEXPORT jl_value_t *jl_restore_package_image_from_file(const char *fname, j size_t *plen; jl_dlsym(pkgimg_handle, "jl_system_image_size", (void **)&plen, 1); - jl_image_t pkgimage; - pkgimage.fptrs = jl_init_processor_pkgimg(pkgimg_handle); - if (!jl_dlsym(pkgimg_handle, "jl_sysimg_gvars_base", (void **)&pkgimage.gvars_base, 0)) { - pkgimage.gvars_base = NULL; - } - - jl_dlsym(pkgimg_handle, "jl_sysimg_gvars_offsets", (void **)&pkgimage.gvars_offsets, 1); - pkgimage.gvars_offsets += 1; - - void *pgcstack_func_slot; - jl_dlsym(pkgimg_handle, "jl_pgcstack_func_slot", &pgcstack_func_slot, 0); - if (pgcstack_func_slot) { // Empty package images might miss these - void *pgcstack_key_slot; - jl_dlsym(pkgimg_handle, "jl_pgcstack_key_slot", &pgcstack_key_slot, 1); - jl_pgcstack_getkey((jl_get_pgcstack_func**)pgcstack_func_slot, (jl_pgcstack_key_t*)pgcstack_key_slot); - - size_t *tls_offset_idx; - jl_dlsym(pkgimg_handle, "jl_tls_offset", (void **)&tls_offset_idx, 1); - *tls_offset_idx = (uintptr_t)(jl_tls_offset == -1 ? 0 : jl_tls_offset); - } - - #ifdef _OS_WINDOWS_ - pkgimage.base = (intptr_t)pkgimg_handle; - #else - Dl_info dlinfo; - if (dladdr((void*)pkgimage.gvars_base, &dlinfo) != 0) { - pkgimage.base = (intptr_t)dlinfo.dli_fbase; - } - else { - pkgimage.base = 0; - } - #endif + jl_image_t pkgimage = jl_init_processor_pkgimg(pkgimg_handle); jl_value_t* mod = jl_restore_incremental_from_buf(pkgimg_data, &pkgimage, *plen, depmods, completeinfo); From 2c7375cbb0c5ab7d331829d7a55d97881cd33255 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Thu, 5 Jan 2023 19:33:30 -0500 Subject: [PATCH 216/775] Annotate information before running optimization --- src/aotcompile.cpp | 12 + src/llvm-multiversioning.cpp | 667 +++++++++++++++++------------------ 2 files changed, 344 insertions(+), 335 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 2c9edecae7df7..527b793f142c8 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -512,6 +512,7 @@ static void injectCRTAlias(Module &M, StringRef name, StringRef alias, FunctionT builder.CreateRet(val); } +void multiversioning_preannotate(Module &M); // takes the running content that has collected in the shadow module and dump it to disk // this builds the object file portion of the sysimage files for fast startup @@ -589,6 +590,17 @@ void jl_dump_native_impl(void *native_code, // add metadata information if (imaging_mode) { + multiversioning_preannotate(*dataM); + { + DenseSet fvars(data->jl_sysimg_fvars.begin(), data->jl_sysimg_fvars.end()); + for (auto &F : *dataM) { + if (F.hasFnAttribute("julia.mv.reloc") || F.hasFnAttribute("julia.mv.fvar")) { + if (fvars.insert(&F).second) { + data->jl_sysimg_fvars.push_back(&F); + } + } + } + } emit_offset_table(*dataM, data->jl_sysimg_gvars, "jl_sysimg_gvars", T_psize); emit_offset_table(*dataM, data->jl_sysimg_fvars, "jl_sysimg_fvars", T_psize); diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 3325cb47147a6..1a1dc297b2702 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -217,25 +218,211 @@ void ConstantUses::forward() } } +static bool is_vector(FunctionType *ty) +{ + if (ty->getReturnType()->isVectorTy()) + return true; + for (auto arg: ty->params()) { + if (arg->isVectorTy()) { + return true; + } + } + return false; +} + +static uint32_t collect_func_info(Function &F, bool &has_veccall) +{ + DominatorTree DT(F); + LoopInfo LI(DT); + uint32_t flag = 0; + if (!LI.empty()) + flag |= JL_TARGET_CLONE_LOOP; + if (is_vector(F.getFunctionType())) { + flag |= JL_TARGET_CLONE_SIMD; + has_veccall = true; + } + for (auto &bb: F) { + for (auto &I: bb) { + if (auto call = dyn_cast(&I)) { + if (is_vector(call->getFunctionType())) { + has_veccall = true; + flag |= JL_TARGET_CLONE_SIMD; + } + if (auto callee = call->getCalledFunction()) { + auto name = callee->getName(); + if (name.startswith("llvm.muladd.") || name.startswith("llvm.fma.")) { + flag |= JL_TARGET_CLONE_MATH; + } + else if (name.startswith("julia.cpu.")) { + if (name.startswith("julia.cpu.have_fma.")) { + // for some platforms we know they always do (or don't) support + // FMA. in those cases we don't need to clone the function. + if (!always_have_fma(*callee).hasValue()) + flag |= JL_TARGET_CLONE_CPU; + } else { + flag |= JL_TARGET_CLONE_CPU; + } + } + } + } + else if (auto store = dyn_cast(&I)) { + if (store->getValueOperand()->getType()->isVectorTy()) { + flag |= JL_TARGET_CLONE_SIMD; + } + } + else if (I.getType()->isVectorTy()) { + flag |= JL_TARGET_CLONE_SIMD; + } + if (auto mathOp = dyn_cast(&I)) { + if (mathOp->getFastMathFlags().any()) { + flag |= JL_TARGET_CLONE_MATH; + } + } + + for (size_t i = 0; i < I.getNumOperands(); i++) { + if(I.getOperand(i)->getType()->isHalfTy()){ + flag |= JL_TARGET_CLONE_FLOAT16; + } + // Check for BFloat16 when they are added to julia can be done here + } + if (has_veccall && (flag & JL_TARGET_CLONE_SIMD) && (flag & JL_TARGET_CLONE_MATH)) { + return flag; + } + } + } + return flag; +} + +static void annotate_module_clones(Module &M) { + CallGraph CG(M); + std::vector orig_funcs; + for (auto &F: M) { + if (F.isDeclaration()) + continue; + orig_funcs.push_back(&F); + } + bool has_veccall = false; + auto specs = jl_get_llvm_clone_targets(); + std::vector clones(orig_funcs.size(), APInt(specs.size(), 0)); + BitVector subtarget_cloned(orig_funcs.size()); + bool check_relocs = false; + + std::vector func_infos(orig_funcs.size()); + for (unsigned i = 0; i < orig_funcs.size(); i++) { + func_infos[i] = collect_func_info(*orig_funcs[i], has_veccall); + } + for (unsigned i = 1; i < specs.size(); i++) { + if (specs[i].flags & JL_TARGET_CLONE_ALL) { + for (unsigned j = 0; j < orig_funcs.size(); j++) { + clones[j].setBit(i); + } + check_relocs = true; + } else { + unsigned flag = specs[i].flags & clone_mask; + std::set sets[2]; + for (unsigned j = 0; j < orig_funcs.size(); j++) { + if (!(func_infos[j] & flag)) { + continue; + } + sets[0].insert(orig_funcs[j]); + } + std::set all_origs(sets[0]); + auto *cur_set = &sets[0]; + auto *next_set = &sets[1]; + // Reduce dispatch by expand the cloning set to functions that are directly called by + // and calling cloned functions. + while (!cur_set->empty()) { + for (auto orig_f: *cur_set) { + // Use the uncloned function since it's already in the call graph + auto node = CG[orig_f]; + for (const auto &I: *node) { + auto child_node = I.second; + auto orig_child_f = child_node->getFunction(); + if (!orig_child_f) + continue; + // Already cloned + if (all_origs.count(orig_child_f)) + continue; + bool calling_clone = false; + for (const auto &I2: *child_node) { + auto orig_child_f2 = I2.second->getFunction(); + if (!orig_child_f2) + continue; + if (all_origs.count(orig_child_f2)) { + calling_clone = true; + break; + } + } + if (!calling_clone) + continue; + next_set->insert(orig_child_f); + all_origs.insert(orig_child_f); + } + } + std::swap(cur_set, next_set); + next_set->clear(); + } + for (unsigned j = 0; j < orig_funcs.size(); j++) { + if (all_origs.count(orig_funcs[j])) { + clones[j].setBit(i); + subtarget_cloned.set(j); + } + } + } + } + if (check_relocs) { + for (unsigned i = 0; i < orig_funcs.size(); i++) { + auto &F = *orig_funcs[i]; + if (subtarget_cloned[i] && !ConstantUses(orig_funcs[i], M).done()) { + F.addFnAttr("julia.mv.reloc", ""); + } else { + auto uses = ConstantUses(orig_funcs[i], M); + if (!uses.done()) { + bool slot = false; + for (; !uses.done(); uses.next()) { + if (isa(uses.get_info().val)) { + slot = true; + break; + } + } + if (slot) { + F.addFnAttr("julia.mv.reloc", ""); + } else { + F.addFnAttr("julia.mv.fvar", ""); + } + } + } + } + } + SmallString<128> cloneset; + for (unsigned i = 0; i < orig_funcs.size(); i++) { + if (!clones[i].isZero()) { + auto &F = *orig_funcs[i]; + cloneset.clear(); + clones[i].toStringUnsigned(cloneset, 16); + F.addFnAttr("julia.mv.clones", cloneset); + } + } + if (has_veccall) { + M.addModuleFlag(Module::Max, "julia.mv.veccall", 1); + } +} + struct CloneCtx { struct Target { int idx; - uint32_t flags; std::unique_ptr vmap; // ValueToValueMapTy is not movable.... - Target(int idx, const jl_target_spec_t &spec) : + explicit Target(int idx) : idx(idx), - flags(spec.flags), vmap(new ValueToValueMapTy) { } }; struct Group : Target { std::vector clones; - std::set clone_fs; - Group(int base, const jl_target_spec_t &spec) : - Target(base, spec), - clones{}, - clone_fs{} + explicit Group(int base) : + Target(base), + clones{} {} Function *base_func(Function *orig_f) const { @@ -243,34 +430,38 @@ struct CloneCtx { return orig_f; return cast(vmap->lookup(orig_f)); } + + bool has_subtarget_clone(Function *orig_f) const + { + auto base = base_func(orig_f); + for (auto &clone: clones) { + if (map_get(*clone.vmap, base)) + return true; + } + return false; + } }; - CloneCtx(Module &M, function_ref GetLI, function_ref GetCG, bool allow_bad_fvars); - void clone_bases(); - void collect_func_infos(); - void clone_all_partials(); + CloneCtx(Module &M, bool allow_bad_fvars); + void prepare_slots(); + void clone_decls(); + void clone_bodies(); void fix_gv_uses(); void fix_inst_uses(); void emit_metadata(); private: void prepare_vmap(ValueToValueMapTy &vmap); - void clone_function(Function *F, Function *new_f, ValueToValueMapTy &vmap); - uint32_t collect_func_info(Function &F); - void check_partial(Group &grp, Target &tgt); void clone_partial(Group &grp, Target &tgt); - uint32_t get_func_id(Function *F); - template - Constant *rewrite_gv_init(const Stack& stack); - std::pair get_reloc_slot(Function *F); + uint32_t get_func_id(Function *F) const; + std::pair get_reloc_slot(Function *F) const; void rewrite_alias(GlobalAlias *alias, Function* F); MDNode *tbaa_const; std::vector specs; std::vector groups{}; + std::vector linearized; std::vector fvars; std::vector gvars; Module &M; - function_ref GetLI; - function_ref GetCG; // Map from original function to one based index in `fvars` std::map func_ids{}; @@ -281,7 +472,7 @@ struct CloneCtx { std::vector> gv_relocs{}; // Mapping from function id (i.e. 0-based index in `fvars`) to GVs to be initialized. std::map const_relocs; - bool has_veccall{false}; + std::map extern_relocs; bool allow_bad_fvars{false}; }; @@ -322,36 +513,36 @@ static inline std::vector consume_gv(Module &M, const char *name, bool allow } // Collect basic information about targets and functions. -CloneCtx::CloneCtx(Module &M, function_ref GetLI, function_ref GetCG, bool allow_bad_fvars) +CloneCtx::CloneCtx(Module &M, bool allow_bad_fvars) : tbaa_const(tbaa_make_child_with_context(M.getContext(), "jtbaa_const", nullptr, true).first), specs(jl_get_llvm_clone_targets()), fvars(consume_gv(M, "jl_sysimg_fvars", allow_bad_fvars)), gvars(consume_gv(M, "jl_sysimg_gvars", false)), M(M), - GetLI(GetLI), - GetCG(GetCG), allow_bad_fvars(allow_bad_fvars) { - groups.emplace_back(0, specs[0]); + groups.emplace_back(0); + linearized.resize(specs.size()); + linearized[0] = &groups[0]; + std::vector group_ids(specs.size(), 0); uint32_t ntargets = specs.size(); for (uint32_t i = 1; i < ntargets; i++) { auto &spec = specs[i]; if (spec.flags & JL_TARGET_CLONE_ALL) { - groups.emplace_back(i, spec); + group_ids[i] = groups.size(); + groups.emplace_back(i); } else { - auto base = spec.base; - bool found = false; - for (auto &grp: groups) { - if (grp.idx == base) { - found = true; - grp.clones.emplace_back(i, spec); - break; - } - } - (void)found; + assert(0 <= spec.base && (unsigned) spec.base < i); + group_ids[i] = group_ids[spec.base]; + groups[group_ids[i]].clones.emplace_back(i); } } + for (auto &grp: groups) { + for (auto &tgt: grp.clones) + linearized[tgt.idx] = &tgt; + linearized[grp.idx] = &grp; + } uint32_t nfvars = fvars.size(); for (uint32_t i = 0; i < nfvars; i++) func_ids[fvars[i]] = i + 1; @@ -376,128 +567,64 @@ void CloneCtx::prepare_vmap(ValueToValueMapTy &vmap) } } -void CloneCtx::clone_function(Function *F, Function *new_f, ValueToValueMapTy &vmap) -{ - Function::arg_iterator DestI = new_f->arg_begin(); - for (Function::const_arg_iterator J = F->arg_begin(); J != F->arg_end(); ++J) { - DestI->setName(J->getName()); - vmap[&*J] = &*DestI++; - } - SmallVector Returns; -#if JL_LLVM_VERSION >= 130000 - // We are cloning into the same module - CloneFunctionInto(new_f, F, vmap, CloneFunctionChangeType::GlobalChanges, Returns); -#else - CloneFunctionInto(new_f, F, vmap, true, Returns); -#endif -} - -// Clone all clone_all targets. Makes sure that the base targets are all available. -void CloneCtx::clone_bases() +void CloneCtx::prepare_slots() { - if (groups.size() == 1) - return; - uint32_t ngrps = groups.size(); - for (uint32_t gid = 1; gid < ngrps; gid++) { - auto &grp = groups[gid]; - auto suffix = ".clone_" + std::to_string(grp.idx); - auto &vmap = *grp.vmap; - // Fill in old->new mapping. We need to do this before cloning the function so that - // the intra target calls are automatically fixed up on cloning. - for (auto F: orig_funcs) { - Function *new_f = Function::Create(F->getFunctionType(), F->getLinkage(), - F->getName() + suffix, &M); - new_f->copyAttributesFrom(F); - vmap[F] = new_f; - } - prepare_vmap(vmap); - for (auto F: orig_funcs) { - clone_function(F, cast(vmap.lookup(F)), vmap); - } - } -} - -static bool is_vector(FunctionType *ty) -{ - if (ty->getReturnType()->isVectorTy()) - return true; - for (auto arg: ty->params()) { - if (arg->isVectorTy()) { - return true; + for (auto &F : orig_funcs) { + if (F->hasFnAttribute("julia.mv.reloc")) { + assert(F->hasFnAttribute("julia.mv.clones")); + if (F->isDeclaration()) { + auto GV = new GlobalVariable(M, F->getType(), false, GlobalValue::ExternalLinkage, nullptr, F->getName() + ".reloc_slot"); + GV->setVisibility(GlobalValue::HiddenVisibility); + extern_relocs[F] = GV; + } else { + auto id = get_func_id(F); + auto GV = new GlobalVariable(M, F->getType(), false, GlobalValue::InternalLinkage, Constant::getNullValue(F->getType()), F->getName() + ".reloc_slot"); + GV->setVisibility(GlobalValue::HiddenVisibility); + const_relocs[id] = GV; + } } } - return false; } -uint32_t CloneCtx::collect_func_info(Function &F) +void CloneCtx::clone_decls() { - uint32_t flag = 0; - if (!GetLI(F).empty()) - flag |= JL_TARGET_CLONE_LOOP; - if (is_vector(F.getFunctionType())) { - flag |= JL_TARGET_CLONE_SIMD; - has_veccall = true; + std::vector suffixes(specs.size()); + for (unsigned i = 1; i < specs.size(); i++) { + suffixes[i] = "." + std::to_string(i); } - for (auto &bb: F) { - for (auto &I: bb) { - if (auto call = dyn_cast(&I)) { - if (is_vector(call->getFunctionType())) { - has_veccall = true; - flag |= JL_TARGET_CLONE_SIMD; - } - if (auto callee = call->getCalledFunction()) { - auto name = callee->getName(); - if (name.startswith("llvm.muladd.") || name.startswith("llvm.fma.")) { - flag |= JL_TARGET_CLONE_MATH; - } - else if (name.startswith("julia.cpu.")) { - if (name.startswith("julia.cpu.have_fma.")) { - // for some platforms we know they always do (or don't) support - // FMA. in those cases we don't need to clone the function. - if (!always_have_fma(*callee).hasValue()) - flag |= JL_TARGET_CLONE_CPU; - } else { - flag |= JL_TARGET_CLONE_CPU; - } - } - } - } - else if (auto store = dyn_cast(&I)) { - if (store->getValueOperand()->getType()->isVectorTy()) { - flag |= JL_TARGET_CLONE_SIMD; - } - } - else if (I.getType()->isVectorTy()) { - flag |= JL_TARGET_CLONE_SIMD; - } - if (auto mathOp = dyn_cast(&I)) { - if (mathOp->getFastMathFlags().any()) { - flag |= JL_TARGET_CLONE_MATH; - } - } - - for (size_t i = 0; i < I.getNumOperands(); i++) { - if(I.getOperand(i)->getType()->isHalfTy()){ - flag |= JL_TARGET_CLONE_FLOAT16; - } - // Check for BFloat16 when they are added to julia can be done here - } - if (has_veccall && (flag & JL_TARGET_CLONE_SIMD) && (flag & JL_TARGET_CLONE_MATH) && - (flag & JL_TARGET_CLONE_CPU) && (flag & JL_TARGET_CLONE_FLOAT16)) { - return flag; + for (auto &F : orig_funcs) { + if (!F->hasFnAttribute("julia.mv.clones")) + continue; + APInt clones(specs.size(), F->getFnAttribute("julia.mv.clones").getValueAsString(), 16); + for (unsigned i = 1; i < specs.size(); i++) { + if (!clones[i]) { + continue; } + auto new_F = Function::Create(F->getFunctionType(), F->getLinkage(), F->getName() + suffixes[i], &M); + new_F->copyAttributesFrom(F); + new_F->setVisibility(F->getVisibility()); + auto base_func = F; + if (specs[i].flags & JL_TARGET_CLONE_ALL) + base_func = static_cast(linearized[specs[i].base])->base_func(F); + (*linearized[i]->vmap)[base_func] = new_F; } } - return flag; } -void CloneCtx::collect_func_infos() +static void clone_function(Function *F, Function *new_f, ValueToValueMapTy &vmap) { - uint32_t nfuncs = orig_funcs.size(); - func_infos.resize(nfuncs); - for (uint32_t i = 0; i < nfuncs; i++) { - func_infos[i] = collect_func_info(*orig_funcs[i]); + Function::arg_iterator DestI = new_f->arg_begin(); + for (Function::const_arg_iterator J = F->arg_begin(); J != F->arg_end(); ++J) { + DestI->setName(J->getName()); + vmap[&*J] = &*DestI++; } + SmallVector Returns; +#if JL_LLVM_VERSION >= 130000 + // We are cloning into the same module + CloneFunctionInto(new_f, F, vmap, CloneFunctionChangeType::GlobalChanges, Returns); +#else + CloneFunctionInto(new_f, F, vmap, true, Returns); +#endif } static void add_features(Function *F, StringRef name, StringRef features, uint32_t flags) @@ -523,149 +650,48 @@ static void add_features(Function *F, StringRef name, StringRef features, uint32 } } -void CloneCtx::clone_all_partials() -{ - // First decide what to clone - // Do this before actually cloning the functions - // so that the call graph is easier to understand - for (auto &grp: groups) { - for (auto &tgt: grp.clones) { - check_partial(grp, tgt); - } - } - for (auto &grp: groups) { - for (auto &tgt: grp.clones) - clone_partial(grp, tgt); - // Also set feature strings for base target functions - // now that all the actual cloning is done. - auto &base_spec = specs[grp.idx]; - for (auto orig_f: orig_funcs) { - add_features(grp.base_func(orig_f), base_spec.cpu_name, - base_spec.cpu_features, base_spec.flags); - } - } - func_infos.clear(); // We don't need this anymore -} - -void CloneCtx::check_partial(Group &grp, Target &tgt) +void CloneCtx::clone_bodies() { - auto flag = specs[tgt.idx].flags & clone_mask; - auto suffix = ".clone_" + std::to_string(tgt.idx); - auto &vmap = *tgt.vmap; - uint32_t nfuncs = func_infos.size(); - - std::set all_origs; - // Use a simple heuristic to decide which function we need to clone. - for (uint32_t i = 0; i < nfuncs; i++) { - if (!(func_infos[i] & flag)) - continue; - auto orig_f = orig_funcs[i]; - // Fill in old->new mapping. We need to do this before cloning the function so that - // the intra target calls are automatically fixed up on cloning. - auto F = grp.base_func(orig_f); - Function *new_f = Function::Create(F->getFunctionType(), F->getLinkage(), - F->getName() + suffix, &M); - new_f->copyAttributesFrom(F); - vmap[F] = new_f; - if (groups.size() == 1) - cloned.insert(orig_f); - grp.clone_fs.insert(i); - all_origs.insert(orig_f); - } - std::set sets[2]{all_origs, std::set{}}; - auto *cur_set = &sets[0]; - auto *next_set = &sets[1]; - // Reduce dispatch by expand the cloning set to functions that are directly called by - // and calling cloned functions. - auto &graph = GetCG(); - while (!cur_set->empty()) { - for (auto orig_f: *cur_set) { - // Use the uncloned function since it's already in the call graph - auto node = graph[orig_f]; - for (const auto &I: *node) { - auto child_node = I.second; - auto orig_child_f = child_node->getFunction(); - if (!orig_child_f) - continue; - // Already cloned - if (all_origs.count(orig_child_f)) - continue; - bool calling_clone = false; - for (const auto &I2: *child_node) { - auto orig_child_f2 = I2.second->getFunction(); - if (!orig_child_f2) - continue; - if (all_origs.count(orig_child_f2)) { - calling_clone = true; - break; + for (auto F : orig_funcs) { + for (unsigned i = 0; i < groups.size(); i++) { + Function *group_F = F; + if (i != 0) { + group_F = groups[i].base_func(F); + if (!F->isDeclaration()) { + clone_function(F, group_F, *groups[i].vmap); + } + } + for (auto &target : groups[i].clones) { + prepare_vmap(*target.vmap); + auto target_F = cast_or_null(map_get(*target.vmap, F)); + if (target_F) { + if (!F->isDeclaration()) { + clone_function(group_F, target_F, *target.vmap); } + add_features(target_F, specs[target.idx].cpu_name, + specs[target.idx].cpu_features, specs[target.idx].flags); + target_F->addFnAttr("julia.mv.clone", std::to_string(i)); } - if (!calling_clone) - continue; - next_set->insert(orig_child_f); - all_origs.insert(orig_child_f); - auto child_f = grp.base_func(orig_child_f); - Function *new_f = Function::Create(child_f->getFunctionType(), - child_f->getLinkage(), - child_f->getName() + suffix, &M); - new_f->copyAttributesFrom(child_f); - vmap[child_f] = new_f; } - } - std::swap(cur_set, next_set); - next_set->clear(); - } - for (uint32_t i = 0; i < nfuncs; i++) { - // Only need to handle expanded functions - if (func_infos[i] & flag) - continue; - auto orig_f = orig_funcs[i]; - if (all_origs.count(orig_f)) { - if (groups.size() == 1) - cloned.insert(orig_f); - grp.clone_fs.insert(i); - } - } -} - -void CloneCtx::clone_partial(Group &grp, Target &tgt) -{ - auto &spec = specs[tgt.idx]; - auto &vmap = *tgt.vmap; - uint32_t nfuncs = orig_funcs.size(); - prepare_vmap(vmap); - for (uint32_t i = 0; i < nfuncs; i++) { - auto orig_f = orig_funcs[i]; - auto F = grp.base_func(orig_f); - if (auto new_v = map_get(vmap, F)) { - auto new_f = cast(new_v); - assert(new_f != F); - clone_function(F, new_f, vmap); - // We can set the feature strings now since no one is going to - // clone these functions again. - add_features(new_f, spec.cpu_name, spec.cpu_features, spec.flags); + if (i != 0) { + //TODO should we also do this for target 0? + add_features(group_F, specs[groups[i].idx].cpu_name, + specs[groups[i].idx].cpu_features, specs[groups[i].idx].flags); + } + group_F->addFnAttr("julia.mv.clone", std::to_string(i)); } } } -uint32_t CloneCtx::get_func_id(Function *F) +uint32_t CloneCtx::get_func_id(Function *F) const { - auto &ref = func_ids[F]; - if (!ref) { - if (allow_bad_fvars && F->isDeclaration()) { - // This should never happen in regular use, but can happen if - // bugpoint deletes the function. Just do something here to - // allow bugpoint to proceed. - return (uint32_t)-1; - } - fvars.push_back(F); - ref = fvars.size(); - } - return ref - 1; + auto ref = func_ids.find(F); + assert(ref != func_ids.end() && "Requesting id of non-fvar!"); + return ref->second - 1; } template -Constant *CloneCtx::rewrite_gv_init(const Stack& stack) +static Constant *rewrite_gv_init(const Stack& stack) { // Null initialize so that LLVM put it in the correct section. SmallVector args; @@ -785,16 +811,18 @@ void CloneCtx::fix_gv_uses() } } -std::pair CloneCtx::get_reloc_slot(Function *F) +std::pair CloneCtx::get_reloc_slot(Function *F) const { - // Null initialize so that LLVM put it in the correct section. - auto id = get_func_id(F); - auto &slot = const_relocs[id]; - if (!slot) - slot = new GlobalVariable(M, F->getType(), false, GlobalVariable::InternalLinkage, - ConstantPointerNull::get(F->getType()), - F->getName() + ".reloc_slot"); - return std::make_pair(id, slot); + 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}; + } else { + auto id = get_func_id(F); + auto slot = const_relocs.find(id); + assert(slot != const_relocs.end() && "Missing relocation slot!"); + return {id, slot->second}; + } } template @@ -851,17 +879,17 @@ void CloneCtx::fix_inst_uses() { uint32_t nfuncs = orig_funcs.size(); for (auto &grp: groups) { - auto suffix = ".clone_" + std::to_string(grp.idx); for (uint32_t i = 0; i < nfuncs; i++) { - if (!grp.clone_fs.count(i)) - continue; auto orig_f = orig_funcs[i]; + if (!grp.has_subtarget_clone(orig_f)) + continue; auto F = grp.base_func(orig_f); + auto grpidx = std::to_string(grp.idx); replaceUsesWithLoad(*F, [&](Instruction &I) -> GlobalVariable * { uint32_t id; GlobalVariable *slot; auto use_f = I.getFunction(); - if (!use_f->getName().endswith(suffix)) + if (!use_f->hasFnAttribute("julia.mv.clone") || use_f->getFnAttribute("julia.mv.clone").getValueAsString() != grpidx) return nullptr; std::tie(id, slot) = get_reloc_slot(orig_f); return slot; @@ -935,17 +963,6 @@ void CloneCtx::emit_metadata() auto gbase = emit_offset_table(M, gvars, "jl_sysimg_gvars"); uint32_t ntargets = specs.size(); - SmallVector targets(ntargets); - for (auto &grp: groups) { - targets[grp.idx] = &grp; - for (auto &tgt: grp.clones) { - targets[tgt.idx] = &tgt; - } - } - - if (has_veccall) { - M.addModuleFlag(Module::Max, "julia.mv.veccall", 1); - } // Generate `jl_dispatch_reloc_slots` std::set shared_relocs; @@ -989,7 +1006,7 @@ void CloneCtx::emit_metadata() std::vector idxs; std::vector offsets; for (uint32_t i = 0; i < ntargets; i++) { - auto tgt = targets[i]; + auto tgt = linearized[i]; auto &spec = specs[i]; uint32_t len_idx = idxs.size(); idxs.push_back(0); // We will fill in the real value later. @@ -1009,7 +1026,7 @@ void CloneCtx::emit_metadata() } else { auto baseidx = spec.base; - auto grp = static_cast(targets[baseidx]); + auto grp = static_cast(linearized[baseidx]); idxs.push_back(baseidx); for (uint32_t j = 0; j < nfvars; j++) { auto base_f = grp->base_func(fvars[j]); @@ -1040,7 +1057,7 @@ void CloneCtx::emit_metadata() } } -static bool runMultiVersioning(Module &M, function_ref GetLI, function_ref GetCG, bool allow_bad_fvars) +static bool runMultiVersioning(Module &M, bool allow_bad_fvars) { // Group targets and identify cloning bases. // Also initialize function info maps (we'll update these maps as we go) @@ -1059,19 +1076,13 @@ static bool runMultiVersioning(Module &M, function_ref Get !gvars || !gvars->hasInitializer() || !isa(gvars->getInitializer()))) return false; - CloneCtx clone(M, GetLI, GetCG, allow_bad_fvars); + CloneCtx clone(M, allow_bad_fvars); + + clone.prepare_slots(); - // Collect a list of original functions and clone base functions - clone.clone_bases(); + clone.clone_decls(); - // Collect function info (type of instruction used) - clone.collect_func_infos(); - - // If any partially cloned target exist decide which functions to clone for these targets. - // Clone functions for each group and collect a list of them. - // We can also add feature strings for cloned functions - // now that no additional cloning needs to be done. - clone.clone_all_partials(); + clone.clone_bodies(); // Scan **ALL** cloned functions (including full cloning for base target) // for global variables initialization use. @@ -1108,24 +1119,12 @@ struct MultiVersioningLegacy: public ModulePass { private: bool runOnModule(Module &M) override; - void getAnalysisUsage(AnalysisUsage &AU) const override - { - AU.addRequired(); - AU.addRequired(); - AU.addPreserved(); - } bool allow_bad_fvars; }; bool MultiVersioningLegacy::runOnModule(Module &M) { - auto GetLI = [this](Function &F) -> LoopInfo & { - return getAnalysis(F).getLoopInfo(); - }; - auto GetCG = [this]() -> CallGraph & { - return getAnalysis().getCallGraph(); - }; - return runMultiVersioning(M, GetLI, GetCG, allow_bad_fvars); + return runMultiVersioning(M, allow_bad_fvars); } @@ -1136,6 +1135,11 @@ static RegisterPass X("JuliaMultiVersioning", "JuliaMulti } // anonymous namespace +void multiversioning_preannotate(Module &M) +{ + annotate_module_clones(M); +} + void replaceUsesWithLoad(Function &F, function_ref should_replace, MDNode *tbaa_const) { bool changed; do { @@ -1162,14 +1166,7 @@ void replaceUsesWithLoad(Function &F, function_ref(M).getManager(); - auto GetLI = [&](Function &F) -> LoopInfo & { - return FAM.getResult(F); - }; - auto GetCG = [&]() -> CallGraph & { - return AM.getResult(M); - }; - if (runMultiVersioning(M, GetLI, GetCG, external_use)) { + if (runMultiVersioning(M, external_use)) { auto preserved = PreservedAnalyses::allInSet(); preserved.preserve(); return preserved; From 6ab1862106bc7f48afa54bac792cb7909df35cd7 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Thu, 5 Jan 2023 22:17:52 -0500 Subject: [PATCH 217/775] Table-based dlsym --- src/aotcompile.cpp | 112 ++++++++++++++++++++++++++++++++--- src/llvm-multiversioning.cpp | 68 ++++++++++----------- src/llvm-ptls.cpp | 19 +----- src/processor.cpp | 72 ++++++++++++---------- src/processor.h | 32 ++++++++++ 5 files changed, 214 insertions(+), 89 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 527b793f142c8..5873c1ca56477 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -424,7 +424,8 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm //Safe b/c context is locked by params GlobalVariable *G = cast(clone.getModuleUnlocked()->getNamedValue(global)); G->setInitializer(ConstantPointerNull::get(cast(G->getValueType()))); - G->setLinkage(GlobalVariable::InternalLinkage); + G->setLinkage(GlobalValue::ExternalLinkage); + G->setVisibility(GlobalValue::HiddenVisibility); data->jl_sysimg_gvars.push_back(G); } CreateNativeGlobals += gvars.size(); @@ -446,9 +447,9 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm //Safe b/c context is locked by params for (GlobalObject &G : clone.getModuleUnlocked()->global_objects()) { if (!G.isDeclaration()) { - G.setLinkage(Function::InternalLinkage); + G.setLinkage(GlobalValue::ExternalLinkage); + G.setVisibility(GlobalValue::HiddenVisibility); makeSafeName(G); - addComdat(&G); #if defined(_OS_WINDOWS_) && defined(_CPU_X86_64_) // Add unwind exception personalities to functions to handle async exceptions if (Function *F = dyn_cast(&G)) @@ -514,6 +515,63 @@ static void injectCRTAlias(Module &M, StringRef name, StringRef alias, FunctionT void multiversioning_preannotate(Module &M); +static GlobalVariable *emit_shard_table(Module &M, Type *T_size, Type *T_psize, unsigned threads) { + SmallVector tables(sizeof(jl_image_shard_t) / sizeof(void *) * threads); + for (unsigned i = 0; i < threads; i++) { + auto suffix = "_" + std::to_string(i); + auto create_gv = [&](StringRef name, bool constant) { + auto gv = new GlobalVariable(M, T_size, constant, + GlobalValue::ExternalLinkage, nullptr, name + suffix); + gv->setVisibility(GlobalValue::HiddenVisibility); + return gv; + }; + auto table = tables.data() + i * sizeof(jl_image_shard_t) / sizeof(void *); + table[offsetof(jl_image_shard_t, fvar_base) / sizeof(void*)] = create_gv("jl_fvar_base", false); + table[offsetof(jl_image_shard_t, fvar_offsets) / sizeof(void*)] = create_gv("jl_fvar_offsets", true); + table[offsetof(jl_image_shard_t, fvar_idxs) / sizeof(void*)] = create_gv("jl_fvar_idxs", true); + table[offsetof(jl_image_shard_t, gvar_base) / sizeof(void*)] = create_gv("jl_gvar_base", false); + table[offsetof(jl_image_shard_t, gvar_offsets) / sizeof(void*)] = create_gv("jl_gvar_offsets", true); + table[offsetof(jl_image_shard_t, gvar_idxs) / sizeof(void*)] = create_gv("jl_gvar_idxs", true); + table[offsetof(jl_image_shard_t, clone_slots) / sizeof(void*)] = create_gv("jl_clone_slots", true); + table[offsetof(jl_image_shard_t, clone_offsets) / sizeof(void*)] = create_gv("jl_clone_offsets", true); + table[offsetof(jl_image_shard_t, clone_idxs) / sizeof(void*)] = create_gv("jl_clone_idxs", true); + } + auto tables_arr = ConstantArray::get(ArrayType::get(T_psize, tables.size()), tables); + auto tables_gv = new GlobalVariable(M, tables_arr->getType(), false, + GlobalValue::ExternalLinkage, tables_arr, "jl_shard_tables"); + tables_gv->setVisibility(GlobalValue::HiddenVisibility); + return tables_gv; +} + +static GlobalVariable *emit_ptls_table(Module &M, Type *T_size, Type *T_psize) { + std::array ptls_table{ + new GlobalVariable(M, T_size, false, GlobalValue::ExternalLinkage, Constant::getNullValue(T_size), "jl_pgcstack_func_slot"), + new GlobalVariable(M, T_size, false, GlobalValue::ExternalLinkage, Constant::getNullValue(T_size), "jl_pgcstack_key_slot"), + new GlobalVariable(M, T_size, false, GlobalValue::ExternalLinkage, Constant::getNullValue(T_size), "jl_tls_offset"), + }; + for (auto &gv : ptls_table) + cast(gv)->setVisibility(GlobalValue::HiddenVisibility); + auto ptls_table_arr = ConstantArray::get(ArrayType::get(T_psize, ptls_table.size()), ptls_table); + auto ptls_table_gv = new GlobalVariable(M, ptls_table_arr->getType(), false, + GlobalValue::ExternalLinkage, ptls_table_arr, "jl_ptls_table"); + ptls_table_gv->setVisibility(GlobalValue::HiddenVisibility); + return ptls_table_gv; +} + +static GlobalVariable *emit_image_header(Module &M, unsigned threads, unsigned nfvars, unsigned ngvars) { + constexpr uint32_t version = 1; + std::array header{ + version, + threads, + nfvars, + ngvars, + }; + auto header_arr = ConstantDataArray::get(M.getContext(), header); + auto header_gv = new GlobalVariable(M, header_arr->getType(), false, + GlobalValue::InternalLinkage, header_arr, "jl_image_header"); + return header_gv; +} + // takes the running content that has collected in the shadow module and dump it to disk // this builds the object file portion of the sysimage files for fast startup extern "C" JL_DLLEXPORT @@ -588,6 +646,10 @@ void jl_dump_native_impl(void *native_code, start = jl_hrtime(); + unsigned threads = 1; + unsigned nfvars = 0; + unsigned ngvars = 0; + // add metadata information if (imaging_mode) { multiversioning_preannotate(*dataM); @@ -601,8 +663,27 @@ void jl_dump_native_impl(void *native_code, } } } - emit_offset_table(*dataM, data->jl_sysimg_gvars, "jl_sysimg_gvars", T_psize); - emit_offset_table(*dataM, data->jl_sysimg_fvars, "jl_sysimg_fvars", T_psize); + nfvars = data->jl_sysimg_fvars.size(); + ngvars = data->jl_sysimg_gvars.size(); + emit_offset_table(*dataM, data->jl_sysimg_gvars, "jl_gvars", T_psize); + emit_offset_table(*dataM, data->jl_sysimg_fvars, "jl_fvars", T_psize); + std::vector idxs; + idxs.resize(data->jl_sysimg_gvars.size()); + std::iota(idxs.begin(), idxs.end(), 0); + auto gidxs = ConstantDataArray::get(Context, idxs); + auto gidxs_var = new GlobalVariable(*dataM, gidxs->getType(), true, + GlobalVariable::ExternalLinkage, + gidxs, "jl_gvar_idxs"); + gidxs_var->setVisibility(GlobalValue::HiddenVisibility); + idxs.clear(); + idxs.resize(data->jl_sysimg_fvars.size()); + std::iota(idxs.begin(), idxs.end(), 0); + auto fidxs = ConstantDataArray::get(Context, idxs); + auto fidxs_var = new GlobalVariable(*dataM, fidxs->getType(), true, + GlobalVariable::ExternalLinkage, + fidxs, "jl_fvar_idxs"); + fidxs_var->setVisibility(GlobalValue::HiddenVisibility); + dataM->addModuleFlag(Module::Error, "julia.mv.suffix", MDString::get(Context, "_0")); // reflect the address of the jl_RTLD_DEFAULT_handle variable // back to the caller, so that we can check for consistency issues @@ -789,10 +870,23 @@ void jl_dump_native_impl(void *native_code, data.insert(data.end(), specdata.begin(), specdata.end()); } auto value = ConstantDataArray::get(Context, data); - addComdat(new GlobalVariable(*sysimageM, value->getType(), true, - GlobalVariable::ExternalLinkage, - value, "jl_dispatch_target_ids")); - + auto target_ids = new GlobalVariable(*sysimageM, value->getType(), true, + GlobalVariable::InternalLinkage, + value, "jl_dispatch_target_ids"); + auto shards = emit_shard_table(*sysimageM, T_size, T_psize, threads); + auto ptls = emit_ptls_table(*sysimageM, T_size, T_psize); + auto header = emit_image_header(*sysimageM, threads, nfvars, ngvars); + auto AT = ArrayType::get(T_psize, 4); + auto pointers = new GlobalVariable(*sysimageM, AT, false, + GlobalVariable::ExternalLinkage, + ConstantArray::get(AT, { + ConstantExpr::getBitCast(header, T_psize), + ConstantExpr::getBitCast(shards, T_psize), + ConstantExpr::getBitCast(ptls, T_psize), + ConstantExpr::getBitCast(target_ids, T_psize) + }), + "jl_image_pointers"); + addComdat(pointers); if (s) { write_int32(s, data.size()); ios_write(s, (const char *)data.data(), data.size()); diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 1a1dc297b2702..44c83502e0537 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -516,8 +516,8 @@ static inline std::vector consume_gv(Module &M, const char *name, bool allow CloneCtx::CloneCtx(Module &M, bool allow_bad_fvars) : tbaa_const(tbaa_make_child_with_context(M.getContext(), "jtbaa_const", nullptr, true).first), specs(jl_get_llvm_clone_targets()), - fvars(consume_gv(M, "jl_sysimg_fvars", allow_bad_fvars)), - gvars(consume_gv(M, "jl_sysimg_gvars", false)), + fvars(consume_gv(M, "jl_fvars", allow_bad_fvars)), + gvars(consume_gv(M, "jl_gvars", false)), M(M), allow_bad_fvars(allow_bad_fvars) { @@ -547,7 +547,7 @@ CloneCtx::CloneCtx(Module &M, bool allow_bad_fvars) for (uint32_t i = 0; i < nfvars; i++) func_ids[fvars[i]] = i + 1; for (auto &F: M) { - if (F.empty()) + if (F.empty() && !F.hasFnAttribute("julia.mv.clones")) continue; orig_funcs.push_back(&F); } @@ -898,19 +898,6 @@ void CloneCtx::fix_inst_uses() } } -template -static inline T *add_comdat(T *G) -{ -#if defined(_OS_WINDOWS_) - // add __declspec(dllexport) to everything marked for export - if (G->getLinkage() == GlobalValue::ExternalLinkage) - G->setDLLStorageClass(GlobalValue::DLLExportStorageClass); - else - G->setDLLStorageClass(GlobalValue::DefaultStorageClass); -#endif - return G; -} - static Constant *get_ptrdiff32(Constant *ptr, Constant *base) { if (ptr->getType()->isPointerTy()) @@ -920,7 +907,7 @@ static Constant *get_ptrdiff32(Constant *ptr, Constant *base) } template -static Constant *emit_offset_table(Module &M, const std::vector &vars, StringRef name) +static Constant *emit_offset_table(Module &M, const std::vector &vars, StringRef name, StringRef suffix) { auto T_int32 = Type::getInt32Ty(M.getContext()); auto T_size = getSizeTy(M.getContext()); @@ -928,11 +915,14 @@ static Constant *emit_offset_table(Module &M, const std::vector &vars, Strin Constant *base = nullptr; if (nvars > 0) { base = ConstantExpr::getBitCast(vars[0], T_size->getPointerTo()); - add_comdat(GlobalAlias::create(T_size, 0, GlobalVariable::ExternalLinkage, - name + "_base", - base, &M)); + auto ga = GlobalAlias::create(T_size, 0, GlobalVariable::ExternalLinkage, + name + "_base" + suffix, + base, &M); + ga->setVisibility(GlobalValue::HiddenVisibility); } else { - base = add_comdat(new GlobalVariable(M, T_size, true, GlobalValue::ExternalLinkage, Constant::getNullValue(T_size), name + "_base")); + auto gv = new GlobalVariable(M, T_size, true, GlobalValue::ExternalLinkage, Constant::getNullValue(T_size), name + "_base" + suffix); + gv->setVisibility(GlobalValue::HiddenVisibility); + base = gv; } auto vbase = ConstantExpr::getPtrToInt(base, T_size); std::vector offsets(nvars + 1); @@ -943,10 +933,11 @@ static Constant *emit_offset_table(Module &M, const std::vector &vars, Strin offsets[i + 1] = get_ptrdiff32(vars[i], vbase); } ArrayType *vars_type = ArrayType::get(T_int32, nvars + 1); - add_comdat(new GlobalVariable(M, vars_type, true, + auto gv = new GlobalVariable(M, vars_type, true, GlobalVariable::ExternalLinkage, ConstantArray::get(vars_type, offsets), - name + "_offsets")); + name + "_offsets" + suffix); + gv->setVisibility(GlobalValue::HiddenVisibility); return vbase; } @@ -958,9 +949,17 @@ void CloneCtx::emit_metadata() return; } + StringRef suffix; + if (auto suffix_md = M.getModuleFlag("julia.mv.suffix")) { + suffix = cast(suffix_md)->getString(); + } + // Store back the information about exported functions. - auto fbase = emit_offset_table(M, fvars, "jl_sysimg_fvars"); - auto gbase = emit_offset_table(M, gvars, "jl_sysimg_gvars"); + auto fbase = emit_offset_table(M, fvars, "jl_fvar", suffix); + auto gbase = emit_offset_table(M, gvars, "jl_gvar", suffix); + + M.getGlobalVariable("jl_fvar_idxs")->setName("jl_fvar_idxs" + suffix); + M.getGlobalVariable("jl_gvar_idxs")->setName("jl_gvar_idxs" + suffix); uint32_t ntargets = specs.size(); @@ -996,9 +995,10 @@ void CloneCtx::emit_metadata() } values[0] = ConstantInt::get(T_int32, values.size() / 2); ArrayType *vars_type = ArrayType::get(T_int32, values.size()); - add_comdat(new GlobalVariable(M, vars_type, true, GlobalVariable::ExternalLinkage, + auto gv = new GlobalVariable(M, vars_type, true, GlobalVariable::ExternalLinkage, ConstantArray::get(vars_type, values), - "jl_dispatch_reloc_slots")); + "jl_clone_slots" + suffix); + gv->setVisibility(GlobalValue::HiddenVisibility); } // Generate `jl_dispatch_fvars_idxs` and `jl_dispatch_fvars_offsets` @@ -1046,14 +1046,16 @@ void CloneCtx::emit_metadata() idxs[len_idx] = count; } auto idxval = ConstantDataArray::get(M.getContext(), idxs); - add_comdat(new GlobalVariable(M, idxval->getType(), true, + auto gv1 = new GlobalVariable(M, idxval->getType(), true, GlobalVariable::ExternalLinkage, - idxval, "jl_dispatch_fvars_idxs")); + idxval, "jl_clone_idxs" + suffix); + gv1->setVisibility(GlobalValue::HiddenVisibility); ArrayType *offsets_type = ArrayType::get(Type::getInt32Ty(M.getContext()), offsets.size()); - add_comdat(new GlobalVariable(M, offsets_type, true, + auto gv2 = new GlobalVariable(M, offsets_type, true, GlobalVariable::ExternalLinkage, ConstantArray::get(offsets_type, offsets), - "jl_dispatch_fvars_offsets")); + "jl_clone_offsets" + suffix); + gv2->setVisibility(GlobalValue::HiddenVisibility); } } @@ -1070,8 +1072,8 @@ static bool runMultiVersioning(Module &M, bool allow_bad_fvars) if (M.getName() == "sysimage") return false; - GlobalVariable *fvars = M.getGlobalVariable("jl_sysimg_fvars"); - GlobalVariable *gvars = M.getGlobalVariable("jl_sysimg_gvars"); + GlobalVariable *fvars = M.getGlobalVariable("jl_fvars"); + GlobalVariable *gvars = M.getGlobalVariable("jl_gvars"); if (allow_bad_fvars && (!fvars || !fvars->hasInitializer() || !isa(fvars->getInitializer()) || !gvars || !gvars->hasInitializer() || !isa(gvars->getInitializer()))) return false; diff --git a/src/llvm-ptls.cpp b/src/llvm-ptls.cpp index ea92e1709c597..e49b992ded50f 100644 --- a/src/llvm-ptls.cpp +++ b/src/llvm-ptls.cpp @@ -140,26 +140,11 @@ GlobalVariable *LowerPTLS::create_aliased_global(Type *T, StringRef name) const // the address is visible externally but LLVM can still assume that the // address of this variable doesn't need dynamic relocation // (can be accessed with a single PC-rel load). - auto GV = new GlobalVariable(*M, T, false, GlobalVariable::InternalLinkage, - Constant::getNullValue(T), name + ".real"); - add_comdat(GlobalAlias::create(T, 0, GlobalVariable::ExternalLinkage, - name, GV, M)); + auto GV = new GlobalVariable(*M, T, false, GlobalVariable::ExternalLinkage, + nullptr, name); return GV; } -template -inline T *LowerPTLS::add_comdat(T *G) const -{ -#if defined(_OS_WINDOWS_) - // add __declspec(dllexport) to everything marked for export - if (G->getLinkage() == GlobalValue::ExternalLinkage) - G->setDLLStorageClass(GlobalValue::DLLExportStorageClass); - else - G->setDLLStorageClass(GlobalValue::DefaultStorageClass); -#endif - return G; -} - void LowerPTLS::fix_pgcstack_use(CallInst *pgcstack, Function *pgcstack_getter, bool or_new, bool *CFGModified) { if (pgcstack->use_empty()) { diff --git a/src/processor.cpp b/src/processor.cpp index a8aca2a64ab19..ea8e4101e6c33 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -21,6 +21,8 @@ #include #endif +#include + // CPU target string is a list of strings separated by `;` each string starts with a CPU // or architecture name and followed by an optional list of features separated by `,`. // A "generic" or empty CPU name means the basic required feature set of the target ISA @@ -629,47 +631,42 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) { jl_image_t res{}; - // .data base - char *data_base; - jl_dlsym(hdl, "jl_sysimg_gvars_base", (void**)&data_base, 1); + const jl_image_pointers_t *pointers; + jl_dlsym(hdl, "jl_image_pointers", (void**)&pointers, 1); - { - void *pgcstack_func_slot; - if (jl_dlsym(hdl, "jl_pgcstack_func_slot", &pgcstack_func_slot, 0)) { - void *pgcstack_key_slot; - jl_dlsym(hdl, "jl_pgcstack_key_slot", &pgcstack_key_slot, 1); - jl_pgcstack_getkey((jl_get_pgcstack_func**)pgcstack_func_slot, (jl_pgcstack_key_t*)pgcstack_key_slot); - - size_t *tls_offset_idx; - jl_dlsym(hdl, "jl_tls_offset", (void **)&tls_offset_idx, 1); - *tls_offset_idx = (uintptr_t)(jl_tls_offset == -1 ? 0 : jl_tls_offset); - } - } + const void *ids = pointers->target_data; + uint32_t target_idx = callback(ids); + + std::cout << "Finished callback\n"; + + auto shard = pointers->shards[0]; + + std::cout << "Shard access is ok\n"; + + // .data base + char *data_base = (char *)shard.gvar_base; // .text base - char *text_base; - jl_dlsym(hdl, "jl_sysimg_fvars_base", (void**)&text_base, 1); + const char *text_base = shard.fvar_base; - const int32_t *offsets; - jl_dlsym(hdl, "jl_sysimg_fvars_offsets", (void**)&offsets, 1); + const int32_t *offsets = shard.fvar_offsets; uint32_t nfunc = offsets[0]; offsets++; - const void *ids; - jl_dlsym(hdl, "jl_dispatch_target_ids", (void**)&ids, 1); - uint32_t target_idx = callback(ids); + std::cout << "Initial offsets\n"; - const int32_t *reloc_slots; - jl_dlsym(hdl, "jl_dispatch_reloc_slots", (void **)&reloc_slots, 1); + const int32_t *reloc_slots = shard.clone_slots; + std::cout << reloc_slots << "\n"; const uint32_t nreloc = reloc_slots[0]; reloc_slots += 1; - const uint32_t *clone_idxs; - const int32_t *clone_offsets; - jl_dlsym(hdl, "jl_dispatch_fvars_idxs", (void**)&clone_idxs, 1); - jl_dlsym(hdl, "jl_dispatch_fvars_offsets", (void**)&clone_offsets, 1); + std::cout << "Set reloc_slots\n"; + const uint32_t *clone_idxs = shard.clone_idxs; + const int32_t *clone_offsets = shard.clone_offsets; uint32_t tag_len = clone_idxs[0]; clone_idxs += 1; + std::cout << "Set clone_idxs\n"; + assert(tag_len & jl_sysimg_tag_mask); std::vector base_offsets = {offsets}; // Find target @@ -688,6 +685,8 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) base_offsets.push_back(tag_len & jl_sysimg_tag_mask ? clone_offsets : nullptr); } + std::cout << "Set offsets\n"; + bool clone_all = (tag_len & jl_sysimg_tag_mask) != 0; // Fill in return value if (clone_all) { @@ -741,17 +740,19 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) (void)found; } + std::cout << "Finished relocation\n"; + res.fptrs.base = text_base; res.fptrs.offsets = offsets; res.gvars_base = (uintptr_t *)data_base; - jl_dlsym(hdl, "jl_sysimg_gvars_offsets", (void **)&res.gvars_offsets, 1); + res.gvars_offsets = shard.gvar_offsets; res.gvars_offsets += 1; #ifdef _OS_WINDOWS_ res.base = (intptr_t)hdl; #else Dl_info dlinfo; - if (dladdr((void*)res.gvars_base, &dlinfo) != 0) { + if (dladdr((void*)pointers, &dlinfo) != 0) { res.base = (intptr_t)dlinfo.dli_fbase; } else { @@ -759,6 +760,17 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) } #endif + std::cout << "Starting ptls\n"; + + { + void *pgcstack_func_slot = pointers->ptls->pgcstack_func_slot; + void *pgcstack_key_slot = pointers->ptls->pgcstack_key_slot; + jl_pgcstack_getkey((jl_get_pgcstack_func**)pgcstack_func_slot, (jl_pgcstack_key_t*)pgcstack_key_slot); + + size_t *tls_offset_idx = pointers->ptls->tls_offset; + *tls_offset_idx = (uintptr_t)(jl_tls_offset == -1 ? 0 : jl_tls_offset); + } + return res; } diff --git a/src/processor.h b/src/processor.h index f76722e885a1d..73271290eff76 100644 --- a/src/processor.h +++ b/src/processor.h @@ -162,6 +162,38 @@ typedef struct { jl_image_fptrs_t fptrs; } jl_image_t; +typedef struct { + uint32_t version; + uint32_t nshards; + uint32_t nfvars; + uint32_t ngvars; +} jl_image_header_t; + +typedef struct { + const char *fvar_base; + const int32_t *fvar_offsets; + const uint32_t *fvar_idxs; + uintptr_t *gvar_base; + const int32_t *gvar_offsets; + const uint32_t *gvar_idxs; + const int32_t *clone_slots; + const int32_t *clone_offsets; + const uint32_t *clone_idxs; +} jl_image_shard_t; + +typedef struct { + void *pgcstack_func_slot; + void *pgcstack_key_slot; + size_t *tls_offset; +} jl_image_ptls_t; + +typedef struct { + const jl_image_header_t *header; + const jl_image_shard_t *shards; // nshards-length array + const jl_image_ptls_t *ptls; + const void *target_data; +} jl_image_pointers_t; + /** * Initialize the processor dispatch system with sysimg `hdl` (also initialize the sysimg itself). * The dispatch system will find the best implementation to be used in this session. From 798ee2245b6aae597a99d25f27aa3ed96cf3c2aa Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Thu, 5 Jan 2023 23:54:39 -0500 Subject: [PATCH 218/775] Allow loader to deal with multiple shards --- src/processor.cpp | 232 ++++++++++++++++++++++++++-------------------- 1 file changed, 133 insertions(+), 99 deletions(-) diff --git a/src/processor.cpp b/src/processor.cpp index ea8e4101e6c33..55b2cd2b4ab55 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -636,117 +636,153 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) const void *ids = pointers->target_data; uint32_t target_idx = callback(ids); + + if (pointers->header->version != 1) { + jl_error("Image file is not compatible with this version of Julia"); + } - std::cout << "Finished callback\n"; - - auto shard = pointers->shards[0]; - - std::cout << "Shard access is ok\n"; - - // .data base - char *data_base = (char *)shard.gvar_base; - - // .text base - const char *text_base = shard.fvar_base; - - const int32_t *offsets = shard.fvar_offsets; - uint32_t nfunc = offsets[0]; - offsets++; - - std::cout << "Initial offsets\n"; - - const int32_t *reloc_slots = shard.clone_slots; - std::cout << reloc_slots << "\n"; - const uint32_t nreloc = reloc_slots[0]; - reloc_slots += 1; - std::cout << "Set reloc_slots\n"; - const uint32_t *clone_idxs = shard.clone_idxs; - const int32_t *clone_offsets = shard.clone_offsets; - uint32_t tag_len = clone_idxs[0]; - clone_idxs += 1; - - std::cout << "Set clone_idxs\n"; + std::vector fvars(pointers->header->nfvars); + std::vector gvars(pointers->header->ngvars); + + std::vector> clones; + + for (unsigned i = 0; i < pointers->header->nshards; i++) { + auto shard = pointers->shards[0]; + + // .data base + char *data_base = (char *)shard.gvar_base; + + // .text base + const char *text_base = shard.fvar_base; + + const int32_t *offsets = shard.fvar_offsets; + uint32_t nfunc = offsets[0]; + offsets++; + const int32_t *reloc_slots = shard.clone_slots; + const uint32_t nreloc = reloc_slots[0]; + reloc_slots += 1; + const uint32_t *clone_idxs = shard.clone_idxs; + const int32_t *clone_offsets = shard.clone_offsets; + uint32_t tag_len = clone_idxs[0]; + clone_idxs += 1; + + assert(tag_len & jl_sysimg_tag_mask); + std::vector base_offsets = {offsets}; + // Find target + for (uint32_t i = 0;i < target_idx;i++) { + uint32_t len = jl_sysimg_val_mask & tag_len; + if (jl_sysimg_tag_mask & tag_len) { + if (i != 0) + clone_offsets += nfunc; + clone_idxs += len + 1; + } + else { + clone_offsets += len; + clone_idxs += len + 2; + } + tag_len = clone_idxs[-1]; + base_offsets.push_back(tag_len & jl_sysimg_tag_mask ? clone_offsets : nullptr); + } - assert(tag_len & jl_sysimg_tag_mask); - std::vector base_offsets = {offsets}; - // Find target - for (uint32_t i = 0;i < target_idx;i++) { - uint32_t len = jl_sysimg_val_mask & tag_len; - if (jl_sysimg_tag_mask & tag_len) { - if (i != 0) - clone_offsets += nfunc; - clone_idxs += len + 1; + bool clone_all = (tag_len & jl_sysimg_tag_mask) != 0; + // Fill in return value + if (clone_all) { + // clone_all + if (target_idx != 0) { + offsets = clone_offsets; + } } else { - clone_offsets += len; - clone_idxs += len + 2; + uint32_t base_idx = clone_idxs[0]; + assert(base_idx < target_idx); + if (target_idx != 0) { + offsets = base_offsets[base_idx]; + assert(offsets); + } + clone_idxs++; + unsigned start = clones.size(); + clones.resize(start + tag_len); + auto idxs = shard.fvar_idxs; + for (unsigned i = 0; i < tag_len; i++) { + clones[start + i] = {(clone_idxs[i] & ~jl_sysimg_val_mask) | idxs[clone_idxs[i] & jl_sysimg_val_mask], clone_offsets[i] + text_base}; + } + } + // Do relocation + uint32_t reloc_i = 0; + uint32_t len = jl_sysimg_val_mask & tag_len; + for (uint32_t i = 0; i < len; i++) { + uint32_t idx = clone_idxs[i]; + int32_t offset; + if (clone_all) { + offset = offsets[idx]; + } + else if (idx & jl_sysimg_tag_mask) { + idx = idx & jl_sysimg_val_mask; + offset = clone_offsets[i]; + } + else { + continue; + } + bool found = false; + for (; reloc_i < nreloc; reloc_i++) { + auto reloc_idx = ((const uint32_t*)reloc_slots)[reloc_i * 2]; + if (reloc_idx == idx) { + found = true; + auto slot = (const void**)(data_base + reloc_slots[reloc_i * 2 + 1]); + assert(slot); + *slot = offset + text_base; + } + else if (reloc_idx > idx) { + break; + } + } + assert(found && "Cannot find GOT entry for cloned function."); + (void)found; } - tag_len = clone_idxs[-1]; - base_offsets.push_back(tag_len & jl_sysimg_tag_mask ? clone_offsets : nullptr); - } - std::cout << "Set offsets\n"; + auto fidxs = shard.fvar_idxs; + for (uint32_t i = 0; i < nfunc; i++) { + fvars[fidxs[i]] = text_base + offsets[i]; + } - bool clone_all = (tag_len & jl_sysimg_tag_mask) != 0; - // Fill in return value - if (clone_all) { - // clone_all - if (target_idx != 0) { - offsets = clone_offsets; + auto gidxs = shard.gvar_idxs; + unsigned ngvars = shard.gvar_offsets[0]; + for (uint32_t i = 0; i < ngvars; i++) { + gvars[gidxs[i]] = data_base + shard.gvar_offsets[i+1]; } } - else { - uint32_t base_idx = clone_idxs[0]; - assert(base_idx < target_idx); - if (target_idx != 0) { - offsets = base_offsets[base_idx]; - assert(offsets); + + if (!fvars.empty()) { + auto offsets = (int32_t *) malloc(sizeof(int32_t) * fvars.size()); + res.fptrs.base = fvars[0]; + for (size_t i = 0; i < fvars.size(); i++) { + offsets[i] = fvars[i] - res.fptrs.base; } - clone_idxs++; - res.fptrs.nclones = tag_len; - res.fptrs.clone_offsets = clone_offsets; - res.fptrs.clone_idxs = clone_idxs; + res.fptrs.offsets = offsets; + res.fptrs.noffsets = fvars.size(); } - // Do relocation - uint32_t reloc_i = 0; - uint32_t len = jl_sysimg_val_mask & tag_len; - for (uint32_t i = 0; i < len; i++) { - uint32_t idx = clone_idxs[i]; - int32_t offset; - if (clone_all) { - offset = offsets[idx]; - } - else if (idx & jl_sysimg_tag_mask) { - idx = idx & jl_sysimg_val_mask; - offset = clone_offsets[i]; - } - else { - continue; - } - bool found = false; - for (; reloc_i < nreloc; reloc_i++) { - auto reloc_idx = ((const uint32_t*)reloc_slots)[reloc_i * 2]; - if (reloc_idx == idx) { - found = true; - auto slot = (const void**)(data_base + reloc_slots[reloc_i * 2 + 1]); - assert(slot); - *slot = offset + text_base; - } - else if (reloc_idx > idx) { - break; - } + + if (!gvars.empty()) { + auto offsets = (int32_t *) malloc(sizeof(int32_t) * gvars.size()); + res.gvars_base = (uintptr_t *)gvars[0]; + for (size_t i = 0; i < gvars.size(); i++) { + offsets[i] = gvars[i] - (const char *)res.gvars_base; } - assert(found && "Cannot find GOT entry for cloned function."); - (void)found; + res.gvars_offsets = offsets; } - std::cout << "Finished relocation\n"; - - res.fptrs.base = text_base; - res.fptrs.offsets = offsets; - res.gvars_base = (uintptr_t *)data_base; - res.gvars_offsets = shard.gvar_offsets; - res.gvars_offsets += 1; + if (!clones.empty()) { + std::sort(clones.begin(), clones.end()); + auto clone_offsets = (int32_t *) malloc(sizeof(int32_t) * clones.size()); + auto clone_idxs = (uint32_t *) malloc(sizeof(uint32_t) * clones.size()); + for (size_t i = 0; i < clones.size(); i++) { + clone_idxs[i] = clones[i].first; + clone_offsets[i] = clones[i].second - res.fptrs.base; + } + res.fptrs.clone_idxs = clone_idxs; + res.fptrs.clone_offsets = clone_offsets; + res.fptrs.nclones = clones.size(); + } #ifdef _OS_WINDOWS_ res.base = (intptr_t)hdl; @@ -760,8 +796,6 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) } #endif - std::cout << "Starting ptls\n"; - { void *pgcstack_func_slot = pointers->ptls->pgcstack_func_slot; void *pgcstack_key_slot = pointers->ptls->pgcstack_key_slot; From 3915101dc65d3d0844cf8e0f5d5a1e39ddf97407 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Fri, 6 Jan 2023 19:21:47 -0500 Subject: [PATCH 219/775] Multithreaded image builder --- src/aotcompile.cpp | 729 +++++++++++++++++++++++++++++------ src/llvm-codegen-shared.h | 152 ++++++++ src/llvm-multiversioning.cpp | 155 -------- src/processor.cpp | 7 +- 4 files changed, 764 insertions(+), 279 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 5873c1ca56477..8ef715235fb04 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -51,6 +51,7 @@ // for outputting code #include #include +#include #include "llvm/Object/ArchiveWriter.h" #include @@ -74,19 +75,13 @@ STATISTIC(CreateNativeMethods, "Number of methods compiled for jl_create_native" STATISTIC(CreateNativeMax, "Max number of methods compiled at once for jl_create_native"); STATISTIC(CreateNativeGlobals, "Number of globals compiled for jl_create_native"); -template // for GlobalObject's -static T *addComdat(T *G) +static void addComdat(GlobalValue *G, Triple &T) { -#if defined(_OS_WINDOWS_) - if (!G->isDeclaration()) { + if (T.isOSBinFormatCOFF() && !G->isDeclaration()) { // add __declspec(dllexport) to everything marked for export - if (G->getLinkage() == GlobalValue::ExternalLinkage) - G->setDLLStorageClass(GlobalValue::DLLExportStorageClass); - else - G->setDLLStorageClass(GlobalValue::DefaultStorageClass); + assert(G->hasExternalLinkage() && "Cannot set DLLExport on non-external linkage!"); + G->setDLLStorageClass(GlobalValue::DLLExportStorageClass); } -#endif - return G; } @@ -472,15 +467,6 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm return (void*)data; } - -static void emit_result(std::vector &Archive, SmallVectorImpl &OS, - StringRef Name, std::vector &outputs) -{ - outputs.push_back({ OS.data(), OS.size() }); - Archive.push_back(NewArchiveMember(MemoryBufferRef(outputs.back(), Name))); - OS.clear(); -} - static object::Archive::Kind getDefaultForHost(Triple &triple) { if (triple.isOSDarwin()) @@ -572,6 +558,584 @@ static GlobalVariable *emit_image_header(Module &M, unsigned threads, unsigned n return header_gv; } +struct Partition { + StringSet<> globals; + StringMap fvars; + StringMap gvars; + size_t weight; +}; + +static void get_fvars_gvars(Module &M, DenseMap &fvars, DenseMap &gvars) { + auto fvars_gv = M.getGlobalVariable("jl_fvars"); + auto gvars_gv = M.getGlobalVariable("jl_gvars"); + assert(fvars_gv); + assert(gvars_gv); + auto fvars_init = cast(fvars_gv->getInitializer()); + auto gvars_init = cast(gvars_gv->getInitializer()); + std::string suffix; + if (auto md = M.getModuleFlag("julia.mv.suffix")) { + suffix = cast(md)->getString().str(); + } + auto fvars_idxs = M.getGlobalVariable("jl_fvar_idxs"); + auto gvars_idxs = M.getGlobalVariable("jl_gvar_idxs"); + assert(fvars_idxs); + assert(gvars_idxs); + auto fvars_idxs_init = cast(fvars_idxs->getInitializer()); + auto gvars_idxs_init = cast(gvars_idxs->getInitializer()); + for (unsigned i = 0; i < fvars_init->getNumOperands(); ++i) { + auto gv = cast(fvars_init->getOperand(i)->stripPointerCasts()); + auto idx = fvars_idxs_init->getElementAsInteger(i); + fvars[gv] = idx; + } + for (unsigned i = 0; i < gvars_init->getNumOperands(); ++i) { + auto gv = cast(gvars_init->getOperand(i)->stripPointerCasts()); + auto idx = gvars_idxs_init->getElementAsInteger(i); + gvars[gv] = idx; + } + fvars_gv->eraseFromParent(); + gvars_gv->eraseFromParent(); + fvars_idxs->eraseFromParent(); + gvars_idxs->eraseFromParent(); +} + +static size_t getFunctionWeight(const Function &F) +{ + size_t weight = 1; + for (const BasicBlock &BB : F) { + weight += BB.size(); + } + // more basic blocks = more complex than just sum of insts, + // add some weight to it + weight += F.size(); + if (F.hasFnAttribute("julia.mv.clones")) { + weight *= F.getFnAttribute("julia.mv.clones").getValueAsString().count(',') + 1; + } + return weight; +} + + +static bool verify_partitioning(const SmallVectorImpl &partitions, const Module &M) { + StringMap GVNames; + bool bad = false; + for (uint32_t i = 0; i < partitions.size(); i++) { + for (auto &name : partitions[i].globals) { + if (GVNames.count(name.getKey())) { + bad = true; + dbgs() << "Duplicate global name " << name.getKey() << " in partitions " << i << " and " << GVNames[name.getKey()] << "\n"; + } + GVNames[name.getKey()] = i; + } + dbgs() << "partition: " << i << " fvars: " << partitions[i].fvars.size() << " gvars: " << partitions[i].gvars.size() << "\n"; + } + for (auto &GV : M.globals()) { + if (GV.isDeclaration()) { + if (GVNames.count(GV.getName())) { + bad = true; + dbgs() << "Global " << GV.getName() << " is a declaration but is in partition " << GVNames[GV.getName()] << "\n"; + } + } else { + if (!GVNames.count(GV.getName())) { + bad = true; + dbgs() << "Global " << GV << " not in any partition\n"; + } + if (!GV.hasExternalLinkage()) { + bad = true; + dbgs() << "Global " << GV << " has non-external linkage " << GV.getLinkage() << " but is in partition " << GVNames[GV.getName()] << "\n"; + } + } + } + return !bad; +} + +// Chop a module up as equally as possible into threads partitions +static SmallVector partitionModule(Module &M, unsigned threads) { + //Start by stripping fvars and gvars, which helpfully removes their uses as well + DenseMap fvars, gvars; + get_fvars_gvars(M, fvars, gvars); + + // Partition by union-find, since we only have def->use traversal right now + struct Partitioner { + struct Node { + GlobalValue *GV; + unsigned parent; + unsigned size; + size_t weight; + }; + std::vector nodes; + DenseMap node_map; + unsigned merged; + + unsigned make(GlobalValue *GV, size_t weight) { + unsigned idx = nodes.size(); + nodes.push_back({GV, idx, 1, weight}); + node_map[GV] = idx; + return idx; + } + + unsigned find(unsigned idx) { + while (nodes[idx].parent != idx) { + nodes[idx].parent = nodes[nodes[idx].parent].parent; + idx = nodes[idx].parent; + } + return idx; + } + + unsigned merge(unsigned x, unsigned y) { + x = find(x); + y = find(y); + if (x == y) + return x; + if (nodes[x].size < nodes[y].size) + std::swap(x, y); + nodes[y].parent = x; + nodes[x].size += nodes[y].size; + nodes[x].weight += nodes[y].weight; + merged++; + return x; + } + }; + + Partitioner partitioner; + + for (auto &G : M.global_values()) { + if (G.isDeclaration()) + continue; + if (isa(G)) { + partitioner.make(&G, getFunctionWeight(cast(G))); + } else { + partitioner.make(&G, 1); + } + } + + // Merge all uses to go together into the same partition + for (unsigned i = 0; i < partitioner.nodes.size(); ++i) { + for (ConstantUses uses(partitioner.nodes[i].GV, M); !uses.done(); uses.next()) { + auto val = uses.get_info().val; + auto idx = partitioner.node_map.find(val); + assert(idx != partitioner.node_map.end()); + partitioner.merge(i, idx->second); + } + } + + SmallVector partitions(threads); + // always get the smallest partition first + auto pcomp = [](const Partition *p1, const Partition *p2) { + return p1->weight > p2->weight; + }; + std::priority_queue, decltype(pcomp)> pq(pcomp); + for (unsigned i = 0; i < threads; ++i) { + pq.push(&partitions[i]); + } + + // Assign the root of each partition to a partition, then assign its children to the same one + for (unsigned i = 0; i < partitioner.nodes.size(); ++i) { + auto root = partitioner.find(i); + if (partitioner.nodes[root].GV) { + auto &node = partitioner.nodes[root]; + auto &P = *pq.top(); + pq.pop(); + auto name = node.GV->getName(); + P.globals.insert(name); + if (fvars.count(node.GV)) + P.fvars[name] = fvars[node.GV]; + if (gvars.count(node.GV)) + P.gvars[name] = gvars[node.GV]; + P.weight += node.weight; + node.GV = nullptr; + node.size = &P - partitions.data(); + pq.push(&P); + } + if (root != i) { + auto &node = partitioner.nodes[i]; + assert(node.GV != nullptr); + // we assigned its root already, so just add it to the root's partition + // don't touch the priority queue, since we're not changing the weight + auto &P = partitions[partitioner.nodes[root].size]; + auto name = node.GV->getName(); + P.globals.insert(name); + if (fvars.count(node.GV)) + P.fvars[name] = fvars[node.GV]; + if (gvars.count(node.GV)) + P.gvars[name] = gvars[node.GV]; + node.GV = nullptr; + node.size = partitioner.nodes[root].size; + } + } + + assert(verify_partitioning(partitions, M) && "Partitioning failed to partition globals correctly"); + + return partitions; +} + +static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *outputs, StringRef name, + NewArchiveMember *unopt, NewArchiveMember *opt, NewArchiveMember *obj, NewArchiveMember *asm_) { + auto TM = std::unique_ptr( + SourceTM.getTarget().createTargetMachine( + SourceTM.getTargetTriple().str(), + SourceTM.getTargetCPU(), + SourceTM.getTargetFeatureString(), + SourceTM.Options, + SourceTM.getRelocationModel(), + SourceTM.getCodeModel(), + SourceTM.getOptLevel())); + + if (unopt) { + raw_string_ostream OS(*outputs); + PassBuilder PB; + AnalysisManagers AM{*TM, PB, OptimizationLevel::O0}; + ModulePassManager MPM; + MPM.addPass(BitcodeWriterPass(OS)); + outputs++; + *outputs = (name + "_unopt.bc").str(); + *unopt = NewArchiveMember(MemoryBufferRef(OS.str(), *outputs)); + outputs++; + } + if (!opt && !obj && !asm_) { + return; + } + assert(!verifyModule(M, &errs())); + + uint64_t start = jl_hrtime(); + uint64_t end = 0; + +#ifndef JL_USE_NEW_PM + legacy::PassManager optimizer; + addTargetPasses(&optimizer, TM->getTargetTriple(), TM->getTargetIRAnalysis()); + addOptimizationPasses(&optimizer, jl_options.opt_level, true, true); + addMachinePasses(&optimizer, jl_options.opt_level); +#else + + auto PMTM = std::unique_ptr( + SourceTM.getTarget().createTargetMachine( + SourceTM.getTargetTriple().str(), + SourceTM.getTargetCPU(), + SourceTM.getTargetFeatureString(), + SourceTM.Options, + SourceTM.getRelocationModel(), + SourceTM.getCodeModel(), + SourceTM.getOptLevel())); + NewPM optimizer{std::move(PMTM), getOptLevel(jl_options.opt_level), OptimizationOptions::defaults(true, true)}; +#endif + optimizer.run(M); + assert(!verifyModule(M, &errs())); + + end = jl_hrtime(); + + dbgs() << "optimize time: " << (end - start) / 1e9 << "s\n"; + + if (opt) { + raw_string_ostream OS(*outputs); + PassBuilder PB; + AnalysisManagers AM{*TM, PB, OptimizationLevel::O0}; + ModulePassManager MPM; + MPM.addPass(BitcodeWriterPass(OS)); + outputs++; + *outputs = (name + "_opt.bc").str(); + *opt = NewArchiveMember(MemoryBufferRef(OS.str(), *outputs)); + outputs++; + } + + start = jl_hrtime(); + + if (obj) { + SmallVector Buffer; + raw_svector_ostream OS(Buffer); + legacy::PassManager emitter; + addTargetPasses(&emitter, TM->getTargetTriple(), TM->getTargetIRAnalysis()); + if (TM->addPassesToEmitFile(emitter, OS, nullptr, CGFT_ObjectFile, false)) + jl_safe_printf("ERROR: target does not support generation of object files\n"); + emitter.run(M); + *outputs = { Buffer.data(), Buffer.size() }; + outputs++; + *outputs = (name + ".o").str(); + *obj = NewArchiveMember(MemoryBufferRef(outputs[-1], *outputs)); + outputs++; + } + + end = jl_hrtime(); + + dbgs() << "codegen time: " << (end - start) / 1e9 << "s\n"; + + if (asm_) { + SmallVector Buffer; + raw_svector_ostream OS(Buffer); + legacy::PassManager emitter; + addTargetPasses(&emitter, TM->getTargetTriple(), TM->getTargetIRAnalysis()); + if (TM->addPassesToEmitFile(emitter, OS, nullptr, CGFT_AssemblyFile, false)) + jl_safe_printf("ERROR: target does not support generation of assembly files\n"); + emitter.run(M); + *outputs = { Buffer.data(), Buffer.size() }; + outputs++; + *outputs = (name + ".s").str(); + *asm_ = NewArchiveMember(MemoryBufferRef(outputs[-1], *outputs)); + outputs++; + } +} + +static auto serializeModule(const Module &M) { + SmallVector ClonedModuleBuffer; + BitcodeWriter BCWriter(ClonedModuleBuffer); + BCWriter.writeModule(M); + BCWriter.writeSymtab(); + BCWriter.writeStrtab(); + return ClonedModuleBuffer; +} + +static void materializePreserved(Module &M, Partition &partition) { + DenseSet Preserve; + for (auto &GV : M.global_values()) { + if (!GV.isDeclaration()) { + if (partition.globals.count(GV.getName())) { + Preserve.insert(&GV); + } + } + } + for (auto &F : M.functions()) { + if (!F.isDeclaration()) { + if (!Preserve.contains(&F)) { + F.deleteBody(); + F.setLinkage(GlobalValue::ExternalLinkage); + } + } + } + for (auto &GV : M.globals()) { + if (!GV.isDeclaration()) { + if (!Preserve.contains(&GV)) { + GV.setInitializer(nullptr); + GV.setLinkage(GlobalValue::ExternalLinkage); + } + } + } + SmallVector> DeletedAliases; + for (auto &GA : M.aliases()) { + if (!GA.isDeclaration()) { + if (!Preserve.contains(&GA)) { + if (GA.getValueType()->isFunctionTy()) { + DeletedAliases.push_back({ &GA, Function::Create(cast(GA.getValueType()), GlobalValue::ExternalLinkage, "", &M) }); + } else { + DeletedAliases.push_back({ &GA, new GlobalVariable(M, GA.getValueType(), false, GlobalValue::ExternalLinkage, nullptr) }); + } + } + } + } + cantFail(M.materializeAll()); + for (auto &Deleted : DeletedAliases) { + Deleted.second->takeName(Deleted.first); + Deleted.first->replaceAllUsesWith(Deleted.second); + Deleted.first->eraseFromParent(); + } +} + +static void construct_vars(Module &M, Partition &partition) { + std::vector> fvar_pairs; + fvar_pairs.reserve(partition.fvars.size()); + for (auto &fvar : partition.fvars) { + auto F = M.getFunction(fvar.first()); + assert(F); + assert(!F->isDeclaration()); + fvar_pairs.push_back({ fvar.second, F }); + } + std::vector fvars; + std::vector fvar_idxs; + fvars.reserve(fvar_pairs.size()); + fvar_idxs.reserve(fvar_pairs.size()); + std::sort(fvar_pairs.begin(), fvar_pairs.end()); + for (auto &fvar : fvar_pairs) { + fvars.push_back(fvar.second); + fvar_idxs.push_back(fvar.first); + } + std::vector> gvar_pairs; + gvar_pairs.reserve(partition.gvars.size()); + for (auto &gvar : partition.gvars) { + auto GV = M.getGlobalVariable(gvar.first()); + assert(GV); + assert(!GV->isDeclaration()); + gvar_pairs.push_back({ gvar.second, GV }); + } + std::vector gvars; + std::vector gvar_idxs; + gvars.reserve(gvar_pairs.size()); + gvar_idxs.reserve(gvar_pairs.size()); + std::sort(gvar_pairs.begin(), gvar_pairs.end()); + for (auto &gvar : gvar_pairs) { + gvars.push_back(gvar.second); + gvar_idxs.push_back(gvar.first); + } + + // Now commit the fvars, gvars, and idxs + auto T_psize = M.getDataLayout().getIntPtrType(M.getContext())->getPointerTo(); + emit_offset_table(M, fvars, "jl_fvars", T_psize); + emit_offset_table(M, gvars, "jl_gvars", T_psize); + auto fidxs = ConstantDataArray::get(M.getContext(), fvar_idxs); + auto fidxs_var = new GlobalVariable(M, fidxs->getType(), true, + GlobalVariable::ExternalLinkage, + fidxs, "jl_fvar_idxs"); + fidxs_var->setVisibility(GlobalValue::HiddenVisibility); + auto gidxs = ConstantDataArray::get(M.getContext(), gvar_idxs); + auto gidxs_var = new GlobalVariable(M, gidxs->getType(), true, + GlobalVariable::ExternalLinkage, + gidxs, "jl_gvar_idxs"); + gidxs_var->setVisibility(GlobalValue::HiddenVisibility); +} + +static void dropUnusedDeclarations(Module &M) { + SmallVector unused; + for (auto &G : M.global_values()) { + if (G.isDeclaration()) { + if (G.use_empty()) { + unused.push_back(&G); + } else { + G.setDSOLocal(false); // These are never going to be seen in the same module again + G.setVisibility(GlobalValue::DefaultVisibility); + } + } + } + for (auto &G : unused) + G->eraseFromParent(); +} + +static void add_output(Module &M, TargetMachine &TM, std::vector &outputs, StringRef name, + std::vector &unopt, std::vector &opt, + std::vector &obj, std::vector &asm_, + bool unopt_out, bool opt_out, bool obj_out, bool asm_out, + unsigned threads) { + uint64_t start = 0, end = 0; + unsigned outcount = unopt_out + opt_out + obj_out + asm_out; + assert(outcount); + outputs.resize(outputs.size() + outcount * threads * 2); + unopt.resize(unopt.size() + unopt_out * threads); + opt.resize(opt.size() + opt_out * threads); + obj.resize(obj.size() + obj_out * threads); + asm_.resize(asm_.size() + asm_out * threads); + if (threads == 1) { + start = jl_hrtime(); + add_output_impl(M, TM, outputs.data() + outputs.size() - outcount * 2, name, + unopt_out ? unopt.data() + unopt.size() - 1 : nullptr, + opt_out ? opt.data() + opt.size() - 1 : nullptr, + obj_out ? obj.data() + obj.size() - 1 : nullptr, + asm_out ? asm_.data() + asm_.size() - 1 : nullptr); + end = jl_hrtime(); + dbgs() << "Time to add output: " << (end - start) / 1e9 << "s\n"; + return; + } + + start = jl_hrtime(); + uint64_t counter = 0; + for (auto &G : M.global_values()) { + if (!G.isDeclaration() && !G.hasName()) { + G.setName("jl_ext_" + Twine(counter++)); + } + } + auto partitions = partitionModule(M, threads); + end = jl_hrtime(); + dbgs() << "Time to partition module: " << (end - start) / 1e9 << "s\n"; + start = jl_hrtime(); + auto serialized = serializeModule(M); + end = jl_hrtime(); + dbgs() << "Time to serialize module: " << (end - start) / 1e9 << "s\n"; + + auto outstart = outputs.data() + outputs.size() - outcount * threads * 2; + auto unoptstart = unopt_out ? unopt.data() + unopt.size() - threads : nullptr; + auto optstart = opt_out ? opt.data() + opt.size() - threads : nullptr; + auto objstart = obj_out ? obj.data() + obj.size() - threads : nullptr; + auto asmstart = asm_out ? asm_.data() + asm_.size() - threads : nullptr; + + std::vector workers(threads); + for (unsigned i = 0; i < threads; i++) { + workers[i] = std::thread([&, i](){ + LLVMContext ctx; + uint64_t start = 0; + uint64_t end = 0; + start = jl_hrtime(); + auto M = cantFail(getLazyBitcodeModule(MemoryBufferRef(StringRef(serialized.data(), serialized.size()), "Optimized"), ctx), "Error loading module"); + end = jl_hrtime(); + dbgs() << "Deserialization time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + + dbgs() << "Starting shard " << i << " with weight=" << partitions[i].weight << "\n"; + + start = jl_hrtime(); + materializePreserved(*M, partitions[i]); + end = jl_hrtime(); + dbgs() << "Materialization time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + + start = jl_hrtime(); + construct_vars(*M, partitions[i]); + M->setModuleFlag(Module::Error, "julia.mv.suffix", MDString::get(M->getContext(), "_" + std::to_string(i))); + end = jl_hrtime(); + + dbgs() << "Construction time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + + start = jl_hrtime(); + dropUnusedDeclarations(*M); + end = jl_hrtime(); + + dbgs() << "Declaration deletion time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + + start = jl_hrtime(); + add_output_impl(*M, TM, outstart + i * outcount * 2, name, + unoptstart ? unoptstart + i : nullptr, + optstart ? optstart + i : nullptr, + objstart ? objstart + i : nullptr, + asmstart ? asmstart + i : nullptr); + end = jl_hrtime(); + + dbgs() << "Output time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + }); + } + + start = jl_hrtime(); + for (auto &w : workers) + w.join(); + end = jl_hrtime(); + + dbgs() << "Total time for parallel output: " << (end - start) / 1e9 << "s\n"; +} + +unsigned compute_image_thread_count(Module &M) { + // 32-bit systems are very memory-constrained +#ifdef _P32 + return 1; +#endif + unsigned threads = std::max(llvm::hardware_concurrency().compute_thread_count() / 2, 1u); + + // memory limit check + // many threads use a lot of memory, so limit on constrained memory systems + size_t available = uv_get_available_memory(); + size_t weight = 0; + for (auto &GV : M.global_values()) { + if (GV.isDeclaration()) + continue; + if (isa(GV)) { + weight += getFunctionWeight(cast(GV)); + } else { + weight += 1; + } + } + if (weight == 0) { + dbgs() << "No globals in module, using 1 thread\n"; + return 1; + } + // crude estimate, available / (weight * fudge factor) = max threads + size_t fudge = 10; + unsigned max_threads = std::max(available / (weight * fudge), (size_t)1); + dbgs() << "Weight: " << weight << ", available: " << available << ", wanted: " << threads << ", max threads: " << max_threads << "\n"; + threads = std::min(threads, max_threads); + + // environment variable override + const char *env_threads = getenv("JULIA_IMAGE_THREADS"); + if (env_threads) { + char *endptr; + unsigned long requested = strtoul(env_threads, &endptr, 10); + if (*endptr || !requested) { + jl_safe_printf("WARNING: invalid value '%s' for JULIA_IMAGE_THREADS\n", env_threads); + } else { + threads = requested; + } + } + + return threads; +} + // takes the running content that has collected in the shadow module and dump it to disk // this builds the object file portion of the sysimage files for fast startup extern "C" JL_DLLEXPORT @@ -584,6 +1148,11 @@ void jl_dump_native_impl(void *native_code, uint64_t end = 0; JL_TIMING(NATIVE_DUMP); jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code; + if (!bc_fname && !unopt_bc_fname && !obj_fname && !asm_fname) { + dbgs() << "No output requested, skipping native code dump?\n"; + delete data; + return; + } auto TSCtx = data->M.getContext(); auto lock = TSCtx.getLock(); LLVMContext &Context = *TSCtx.getContext(); @@ -646,7 +1215,7 @@ void jl_dump_native_impl(void *native_code, start = jl_hrtime(); - unsigned threads = 1; + unsigned threads = compute_image_thread_count(*dataM); unsigned nfvars = 0; unsigned ngvars = 0; @@ -693,7 +1262,7 @@ void jl_dump_native_impl(void *native_code, true, GlobalVariable::ExternalLinkage, jlRTLD_DEFAULT_var, - "jl_RTLD_DEFAULT_handle_pointer")); + "jl_RTLD_DEFAULT_handle_pointer"), TheTriple); } end = jl_hrtime(); @@ -702,101 +1271,14 @@ void jl_dump_native_impl(void *native_code, start = jl_hrtime(); - // do the actual work - auto add_output = [&] (Module &M, StringRef unopt_bc_Name, StringRef bc_Name, StringRef obj_Name, StringRef asm_Name) { - - auto TM = std::unique_ptr( - SourceTM->getTarget().createTargetMachine( - SourceTM->getTargetTriple().str(), - SourceTM->getTargetCPU(), - SourceTM->getTargetFeatureString(), - SourceTM->Options, - SourceTM->getRelocationModel(), - SourceTM->getCodeModel(), - SourceTM->getOptLevel())); - - if (unopt_bc_fname) { - SmallVector Buffer; - raw_svector_ostream OS(Buffer); - PassBuilder PB; - AnalysisManagers AM{*TM, PB, OptimizationLevel::O0}; - ModulePassManager MPM; - MPM.addPass(BitcodeWriterPass(OS)); - emit_result(unopt_bc_Archive, Buffer, unopt_bc_Name, outputs); - } - if (!bc_fname && !obj_fname && !asm_fname) { - return; - } - assert(!verifyModule(M, &errs())); - - uint64_t start = jl_hrtime(); - end = 0; - -#ifndef JL_USE_NEW_PM - legacy::PassManager optimizer; - addTargetPasses(&optimizer, TM->getTargetTriple(), TM->getTargetIRAnalysis()); - addOptimizationPasses(&optimizer, jl_options.opt_level, true, true); - addMachinePasses(&optimizer, jl_options.opt_level); -#else - - auto PMTM = std::unique_ptr( - SourceTM->getTarget().createTargetMachine( - SourceTM->getTargetTriple().str(), - SourceTM->getTargetCPU(), - SourceTM->getTargetFeatureString(), - SourceTM->Options, - SourceTM->getRelocationModel(), - SourceTM->getCodeModel(), - SourceTM->getOptLevel())); - NewPM optimizer{std::move(PMTM), getOptLevel(jl_options.opt_level), OptimizationOptions::defaults(true, true)}; -#endif - optimizer.run(M); - assert(!verifyModule(M, &errs())); - - end = jl_hrtime(); - - dbgs() << "optimize time: " << (end - start) / 1e9 << "s\n"; - - if (bc_fname) { - SmallVector Buffer; - raw_svector_ostream OS(Buffer); - PassBuilder PB; - AnalysisManagers AM{*TM, PB, OptimizationLevel::O0}; - ModulePassManager MPM; - MPM.addPass(BitcodeWriterPass(OS)); - emit_result(bc_Archive, Buffer, bc_Name, outputs); - } - - start = jl_hrtime(); - - if (obj_fname) { - SmallVector Buffer; - raw_svector_ostream OS(Buffer); - legacy::PassManager emitter; - addTargetPasses(&emitter, TM->getTargetTriple(), TM->getTargetIRAnalysis()); - if (TM->addPassesToEmitFile(emitter, OS, nullptr, CGFT_ObjectFile, false)) - jl_safe_printf("ERROR: target does not support generation of object files\n"); - emitter.run(M); - emit_result(obj_Archive, Buffer, obj_Name, outputs); - } - - end = jl_hrtime(); - - dbgs() << "codegen time: " << (end - start) / 1e9 << "s\n"; - - if (asm_fname) { - SmallVector Buffer; - raw_svector_ostream OS(Buffer); - legacy::PassManager emitter; - addTargetPasses(&emitter, TM->getTargetTriple(), TM->getTargetIRAnalysis()); - if (TM->addPassesToEmitFile(emitter, OS, nullptr, CGFT_AssemblyFile, false)) - jl_safe_printf("ERROR: target does not support generation of assembly files\n"); - emitter.run(M); - emit_result(asm_Archive, Buffer, asm_Name, outputs); - } - }; - - add_output(*dataM, "unopt.bc", "text.bc", "text.o", "text.s"); + auto compile = [&](Module &M, StringRef name, unsigned threads) { add_output( + M, *SourceTM, outputs, name, + unopt_bc_Archive, bc_Archive, obj_Archive, asm_Archive, + !!unopt_bc_fname, !!bc_fname, !!obj_fname, !!asm_fname, + threads + ); }; + + compile(*dataM, "text", threads); end = jl_hrtime(); @@ -804,8 +1286,7 @@ void jl_dump_native_impl(void *native_code, start = jl_hrtime(); - orc::ThreadSafeModule sysimage(std::make_unique("sysimage", Context), TSCtx); - auto sysimageM = sysimage.getModuleUnlocked(); + auto sysimageM = std::make_unique("sysimage", Context); sysimageM->setTargetTriple(dataM->getTargetTriple()); sysimageM->setDataLayout(dataM->getDataLayout()); #if JL_LLVM_VERSION >= 130000 @@ -846,13 +1327,15 @@ void jl_dump_native_impl(void *native_code, if (sysimg_data) { Constant *data = ConstantDataArray::get(Context, ArrayRef((const unsigned char*)sysimg_data, sysimg_len)); - addComdat(new GlobalVariable(*sysimageM, data->getType(), false, + auto sysdata = new GlobalVariable(*sysimageM, data->getType(), false, GlobalVariable::ExternalLinkage, - data, "jl_system_image_data"))->setAlignment(Align(64)); + data, "jl_system_image_data"); + sysdata->setAlignment(Align(64)); + addComdat(sysdata, TheTriple); Constant *len = ConstantInt::get(T_size, sysimg_len); addComdat(new GlobalVariable(*sysimageM, len->getType(), true, GlobalVariable::ExternalLinkage, - len, "jl_system_image_size")); + len, "jl_system_image_size"), TheTriple); } if (imaging_mode) { auto specs = jl_get_llvm_clone_targets(); @@ -886,13 +1369,13 @@ void jl_dump_native_impl(void *native_code, ConstantExpr::getBitCast(target_ids, T_psize) }), "jl_image_pointers"); - addComdat(pointers); + addComdat(pointers, TheTriple); if (s) { write_int32(s, data.size()); ios_write(s, (const char *)data.data(), data.size()); } } - add_output(*sysimageM, "data.bc", "data.bc", "data.o", "data.s"); + compile(*sysimageM, "data", 1); end = jl_hrtime(); diff --git a/src/llvm-codegen-shared.h b/src/llvm-codegen-shared.h index e0edb792d7645..732871b12ff23 100644 --- a/src/llvm-codegen-shared.h +++ b/src/llvm-codegen-shared.h @@ -449,4 +449,156 @@ inline Attribute getAttributeAtIndex(const AttributeList &L, unsigned Index, Att return L.getAttribute(Index, Kind); #endif } + +// Iterate through uses of a particular type. +// Recursively scan through `ConstantExpr` and `ConstantAggregate` use. +template +struct ConstantUses { + template + struct Info { + llvm::Use *use; + T *val; + // If `samebits == true`, the offset the original value appears in the constant. + size_t offset; + // This specify whether the original value appears in the current value in exactly + // the same bit pattern (with possibly an offset determined by `offset`). + bool samebits; + Info(llvm::Use *use, T *val, size_t offset, bool samebits) : + use(use), + val(val), + offset(offset), + samebits(samebits) + { + } + Info(llvm::Use *use, size_t offset, bool samebits) : + use(use), + val(cast(use->getUser())), + offset(offset), + samebits(samebits) + { + } + }; + using UseInfo = Info; + struct Frame : Info { + template + Frame(Args &&... args) : + Info(std::forward(args)...), + cur(this->val->use_empty() ? nullptr : &*this->val->use_begin()), + _next(cur ? cur->getNext() : nullptr) + { + } + private: + void next() + { + cur = _next; + if (!cur) + return; + _next = cur->getNext(); + } + llvm::Use *cur; + llvm::Use *_next; + friend struct ConstantUses; + }; + ConstantUses(llvm::Constant *c, llvm::Module &M) + : stack{Frame(nullptr, c, 0u, true)}, + M(M) + { + forward(); + } + UseInfo get_info() const + { + auto &top = stack.back(); + return UseInfo(top.cur, top.offset, top.samebits); + } + const auto &get_stack() const + { + return stack; + } + void next() + { + stack.back().next(); + forward(); + } + bool done() + { + return stack.empty(); + } +private: + void forward(); + llvm::SmallVector stack; + llvm::Module &M; +}; + +template +void ConstantUses::forward() +{ + assert(!stack.empty()); + auto frame = &stack.back(); + const auto &DL = M.getDataLayout(); + auto pop = [&] { + stack.pop_back(); + if (stack.empty()) { + return false; + } + frame = &stack.back(); + return true; + }; + auto push = [&] (llvm::Use *use, llvm::Constant *c, size_t offset, bool samebits) { + stack.emplace_back(use, c, offset, samebits); + frame = &stack.back(); + }; + auto handle_constaggr = [&] (llvm::Use *use, llvm::ConstantAggregate *aggr) { + if (!frame->samebits) { + push(use, aggr, 0, false); + return; + } + if (auto strct = dyn_cast(aggr)) { + auto layout = DL.getStructLayout(strct->getType()); + push(use, strct, frame->offset + layout->getElementOffset(use->getOperandNo()), true); + } + else if (auto ary = dyn_cast(aggr)) { + auto elty = ary->getType()->getElementType(); + push(use, ary, frame->offset + DL.getTypeAllocSize(elty) * use->getOperandNo(), true); + } + else if (auto vec = dyn_cast(aggr)) { + auto elty = vec->getType()->getElementType(); + push(use, vec, frame->offset + DL.getTypeAllocSize(elty) * use->getOperandNo(), true); + } + else { + abort(); + } + }; + auto handle_constexpr = [&] (llvm::Use *use, llvm::ConstantExpr *expr) { + if (!frame->samebits) { + push(use, expr, 0, false); + return; + } + auto opcode = expr->getOpcode(); + if (opcode == llvm::Instruction::PtrToInt || opcode == llvm::Instruction::IntToPtr || + opcode == llvm::Instruction::AddrSpaceCast || opcode == llvm::Instruction::BitCast) { + push(use, expr, frame->offset, true); + } + else { + push(use, expr, 0, false); + } + }; + while (true) { + auto use = frame->cur; + if (!use) { + if (!pop()) + return; + continue; + } + auto user = use->getUser(); + if (isa(user)) + return; + frame->next(); + if (auto aggr = dyn_cast(user)) { + handle_constaggr(use, aggr); + } + else if (auto expr = dyn_cast(user)) { + handle_constexpr(use, expr); + } + } +} } diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 44c83502e0537..a172579f8ae4b 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -64,160 +64,6 @@ Value *map_get(T &&vmap, Value *key, Value *def=nullptr) return val; } -// Iterate through uses of a particular type. -// Recursively scan through `ConstantExpr` and `ConstantAggregate` use. -template -struct ConstantUses { - template - struct Info { - Use *use; - T *val; - // If `samebits == true`, the offset the original value appears in the constant. - size_t offset; - // This specify whether the original value appears in the current value in exactly - // the same bit pattern (with possibly an offset determined by `offset`). - bool samebits; - Info(Use *use, T *val, size_t offset, bool samebits) : - use(use), - val(val), - offset(offset), - samebits(samebits) - { - } - Info(Use *use, size_t offset, bool samebits) : - use(use), - val(cast(use->getUser())), - offset(offset), - samebits(samebits) - { - } - }; - using UseInfo = Info; - struct Frame : Info { - template - Frame(Args &&... args) : - Info(std::forward(args)...), - cur(this->val->use_empty() ? nullptr : &*this->val->use_begin()), - _next(cur ? cur->getNext() : nullptr) - { - } - private: - void next() - { - cur = _next; - if (!cur) - return; - _next = cur->getNext(); - } - Use *cur; - Use *_next; - friend struct ConstantUses; - }; - ConstantUses(Constant *c, Module &M) - : stack{Frame(nullptr, c, 0u, true)}, - M(M) - { - forward(); - } - UseInfo get_info() const - { - auto &top = stack.back(); - return UseInfo(top.cur, top.offset, top.samebits); - } - const SmallVector &get_stack() const - { - return stack; - } - void next() - { - stack.back().next(); - forward(); - } - bool done() - { - return stack.empty(); - } -private: - void forward(); - SmallVector stack; - Module &M; -}; - -template -void ConstantUses::forward() -{ - assert(!stack.empty()); - auto frame = &stack.back(); - const DataLayout &DL = M.getDataLayout(); - auto pop = [&] { - stack.pop_back(); - if (stack.empty()) { - return false; - } - frame = &stack.back(); - return true; - }; - auto push = [&] (Use *use, Constant *c, size_t offset, bool samebits) { - stack.emplace_back(use, c, offset, samebits); - frame = &stack.back(); - }; - auto handle_constaggr = [&] (Use *use, ConstantAggregate *aggr) { - if (!frame->samebits) { - push(use, aggr, 0, false); - return; - } - if (auto strct = dyn_cast(aggr)) { - auto layout = DL.getStructLayout(strct->getType()); - push(use, strct, frame->offset + layout->getElementOffset(use->getOperandNo()), true); - } - else if (auto ary = dyn_cast(aggr)) { - auto elty = ary->getType()->getElementType(); - push(use, ary, frame->offset + DL.getTypeAllocSize(elty) * use->getOperandNo(), true); - } - else if (auto vec = dyn_cast(aggr)) { - auto elty = vec->getType()->getElementType(); - push(use, vec, frame->offset + DL.getTypeAllocSize(elty) * use->getOperandNo(), true); - } - else { - jl_safe_printf("Unknown ConstantAggregate:\n"); - llvm_dump(aggr); - abort(); - } - }; - auto handle_constexpr = [&] (Use *use, ConstantExpr *expr) { - if (!frame->samebits) { - push(use, expr, 0, false); - return; - } - auto opcode = expr->getOpcode(); - if (opcode == Instruction::PtrToInt || opcode == Instruction::IntToPtr || - opcode == Instruction::AddrSpaceCast || opcode == Instruction::BitCast) { - push(use, expr, frame->offset, true); - } - else { - push(use, expr, 0, false); - } - }; - while (true) { - auto use = frame->cur; - if (!use) { - if (!pop()) - return; - continue; - } - auto user = use->getUser(); - if (isa(user)) - return; - frame->next(); - if (auto aggr = dyn_cast(user)) { - handle_constaggr(use, aggr); - } - else if (auto expr = dyn_cast(user)) { - handle_constexpr(use, expr); - } - } -} - static bool is_vector(FunctionType *ty) { if (ty->getReturnType()->isVectorTy()) @@ -574,7 +420,6 @@ void CloneCtx::prepare_slots() assert(F->hasFnAttribute("julia.mv.clones")); if (F->isDeclaration()) { auto GV = new GlobalVariable(M, F->getType(), false, GlobalValue::ExternalLinkage, nullptr, F->getName() + ".reloc_slot"); - GV->setVisibility(GlobalValue::HiddenVisibility); extern_relocs[F] = GV; } else { auto id = get_func_id(F); diff --git a/src/processor.cpp b/src/processor.cpp index 55b2cd2b4ab55..3a791778a3b21 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -647,7 +647,7 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) std::vector> clones; for (unsigned i = 0; i < pointers->header->nshards; i++) { - auto shard = pointers->shards[0]; + auto shard = pointers->shards[i]; // .data base char *data_base = (char *)shard.gvar_base; @@ -657,6 +657,7 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) const int32_t *offsets = shard.fvar_offsets; uint32_t nfunc = offsets[0]; + assert(nfunc <= pointers->header->nfvars); offsets++; const int32_t *reloc_slots = shard.clone_slots; const uint32_t nreloc = reloc_slots[0]; @@ -747,6 +748,7 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) auto gidxs = shard.gvar_idxs; unsigned ngvars = shard.gvar_offsets[0]; + assert(ngvars <= pointers->header->ngvars); for (uint32_t i = 0; i < ngvars; i++) { gvars[gidxs[i]] = data_base + shard.gvar_offsets[i+1]; } @@ -756,6 +758,7 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) auto offsets = (int32_t *) malloc(sizeof(int32_t) * fvars.size()); res.fptrs.base = fvars[0]; for (size_t i = 0; i < fvars.size(); i++) { + assert(fvars[i] && "Missing function pointer!"); offsets[i] = fvars[i] - res.fptrs.base; } res.fptrs.offsets = offsets; @@ -766,12 +769,14 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) auto offsets = (int32_t *) malloc(sizeof(int32_t) * gvars.size()); res.gvars_base = (uintptr_t *)gvars[0]; for (size_t i = 0; i < gvars.size(); i++) { + assert(gvars[i] && "Missing global variable pointer!"); offsets[i] = gvars[i] - (const char *)res.gvars_base; } res.gvars_offsets = offsets; } if (!clones.empty()) { + assert(!fvars.empty()); std::sort(clones.begin(), clones.end()); auto clone_offsets = (int32_t *) malloc(sizeof(int32_t) * clones.size()); auto clone_idxs = (uint32_t *) malloc(sizeof(uint32_t) * clones.size()); From 4ad943da621f5d696ebfdf853ac03aa742edec65 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Fri, 6 Jan 2023 20:19:12 -0500 Subject: [PATCH 220/775] Don't try to extract indexes during partitioning --- src/aotcompile.cpp | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 8ef715235fb04..85e7481b21722 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -568,34 +568,27 @@ struct Partition { static void get_fvars_gvars(Module &M, DenseMap &fvars, DenseMap &gvars) { auto fvars_gv = M.getGlobalVariable("jl_fvars"); auto gvars_gv = M.getGlobalVariable("jl_gvars"); - assert(fvars_gv); - assert(gvars_gv); - auto fvars_init = cast(fvars_gv->getInitializer()); - auto gvars_init = cast(gvars_gv->getInitializer()); - std::string suffix; - if (auto md = M.getModuleFlag("julia.mv.suffix")) { - suffix = cast(md)->getString().str(); - } auto fvars_idxs = M.getGlobalVariable("jl_fvar_idxs"); auto gvars_idxs = M.getGlobalVariable("jl_gvar_idxs"); + assert(fvars_gv); + assert(gvars_gv); assert(fvars_idxs); assert(gvars_idxs); - auto fvars_idxs_init = cast(fvars_idxs->getInitializer()); - auto gvars_idxs_init = cast(gvars_idxs->getInitializer()); + auto fvars_init = cast(fvars_gv->getInitializer()); + auto gvars_init = cast(gvars_gv->getInitializer()); for (unsigned i = 0; i < fvars_init->getNumOperands(); ++i) { auto gv = cast(fvars_init->getOperand(i)->stripPointerCasts()); - auto idx = fvars_idxs_init->getElementAsInteger(i); - fvars[gv] = idx; + fvars[gv] = i; } for (unsigned i = 0; i < gvars_init->getNumOperands(); ++i) { auto gv = cast(gvars_init->getOperand(i)->stripPointerCasts()); - auto idx = gvars_idxs_init->getElementAsInteger(i); - gvars[gv] = idx; + gvars[gv] = i; } fvars_gv->eraseFromParent(); gvars_gv->eraseFromParent(); fvars_idxs->eraseFromParent(); gvars_idxs->eraseFromParent(); + dbgs() << "Finished getting fvars/gvars\n"; } static size_t getFunctionWeight(const Function &F) From d717fa7023ab104290a136b955511c38e1416a4c Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Fri, 6 Jan 2023 20:28:00 -0500 Subject: [PATCH 221/775] Fix whitespace --- src/aotcompile.cpp | 10 +++++----- src/llvm-multiversioning.cpp | 2 +- src/processor.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 85e7481b21722..233e94bf13346 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -719,7 +719,7 @@ static SmallVector partitionModule(Module &M, unsigned threads) { for (unsigned i = 0; i < threads; ++i) { pq.push(&partitions[i]); } - + // Assign the root of each partition to a partition, then assign its children to the same one for (unsigned i = 0; i < partitioner.nodes.size(); ++i) { auto root = partitioner.find(i); @@ -1011,7 +1011,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o dbgs() << "Time to add output: " << (end - start) / 1e9 << "s\n"; return; } - + start = jl_hrtime(); uint64_t counter = 0; for (auto &G : M.global_values()) { @@ -1050,7 +1050,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o materializePreserved(*M, partitions[i]); end = jl_hrtime(); dbgs() << "Materialization time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; - + start = jl_hrtime(); construct_vars(*M, partitions[i]); M->setModuleFlag(Module::Error, "julia.mv.suffix", MDString::get(M->getContext(), "_" + std::to_string(i))); @@ -1270,7 +1270,7 @@ void jl_dump_native_impl(void *native_code, !!unopt_bc_fname, !!bc_fname, !!obj_fname, !!asm_fname, threads ); }; - + compile(*dataM, "text", threads); end = jl_hrtime(); @@ -1389,7 +1389,7 @@ void jl_dump_native_impl(void *native_code, if (asm_fname) handleAllErrors(writeArchive(asm_fname, asm_Archive, true, Kind, true, false), reportWriterError); - + end = jl_hrtime(); dbgs() << "archive time: " << (end - start) / 1e9 << "s\n"; diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index a172579f8ae4b..418201f0825a1 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -924,7 +924,7 @@ static bool runMultiVersioning(Module &M, bool allow_bad_fvars) return false; CloneCtx clone(M, allow_bad_fvars); - + clone.prepare_slots(); clone.clone_decls(); diff --git a/src/processor.cpp b/src/processor.cpp index 3a791778a3b21..851cbec62560a 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -636,7 +636,7 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) const void *ids = pointers->target_data; uint32_t target_idx = callback(ids); - + if (pointers->header->version != 1) { jl_error("Image file is not compatible with this version of Julia"); } From fe0600d2dc74e1381ac3018fe12835fafc25d529 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Fri, 6 Jan 2023 20:38:39 -0500 Subject: [PATCH 222/775] Fix warnings --- src/aotcompile.cpp | 4 ++-- src/llvm-multiversioning.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 233e94bf13346..323577c693b51 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -606,8 +606,8 @@ static size_t getFunctionWeight(const Function &F) return weight; } - -static bool verify_partitioning(const SmallVectorImpl &partitions, const Module &M) { +//Inline to fool gcc into not complaining about unused function when asserts are disabled +static inline bool verify_partitioning(const SmallVectorImpl &partitions, const Module &M) { StringMap GVNames; bool bad = false; for (uint32_t i = 0; i < partitions.size(); i++) { diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 418201f0825a1..971b0f338bdf8 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -131,7 +131,8 @@ static uint32_t collect_func_info(Function &F, bool &has_veccall) } // Check for BFloat16 when they are added to julia can be done here } - if (has_veccall && (flag & JL_TARGET_CLONE_SIMD) && (flag & JL_TARGET_CLONE_MATH)) { + uint32_t veccall_flags = JL_TARGET_CLONE_SIMD | JL_TARGET_CLONE_MATH | JL_TARGET_CLONE_CPU | JL_TARGET_CLONE_FLOAT16; + if (has_veccall && (flag & veccall_flags) == veccall_flags) { return flag; } } From bdf65f4b4e8a1c8f1a058fe8fc69a6b4c56acf80 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Fri, 6 Jan 2023 21:01:56 -0500 Subject: [PATCH 223/775] Set reloc slot to be external linkage --- src/llvm-multiversioning.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 971b0f338bdf8..1a38511f34ffb 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -424,7 +424,7 @@ void CloneCtx::prepare_slots() extern_relocs[F] = GV; } else { auto id = get_func_id(F); - auto GV = new GlobalVariable(M, F->getType(), false, GlobalValue::InternalLinkage, Constant::getNullValue(F->getType()), F->getName() + ".reloc_slot"); + auto GV = new GlobalVariable(M, F->getType(), false, GlobalValue::ExternalLinkage, Constant::getNullValue(F->getType()), F->getName() + ".reloc_slot"); GV->setVisibility(GlobalValue::HiddenVisibility); const_relocs[id] = GV; } From 4fc5bed6b5fa58f056ee5ee87730bea2ac17fa8c Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Tue, 10 Jan 2023 00:41:53 -0500 Subject: [PATCH 224/775] Formalize printing more, correct module weight estimation with multiversioning --- src/aotcompile.cpp | 75 +++++++++++++++++++++++++----------- src/llvm-multiversioning.cpp | 6 +-- 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 323577c693b51..701ecdfc925e8 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -601,7 +601,10 @@ static size_t getFunctionWeight(const Function &F) // add some weight to it weight += F.size(); if (F.hasFnAttribute("julia.mv.clones")) { - weight *= F.getFnAttribute("julia.mv.clones").getValueAsString().count(',') + 1; + auto val = F.getFnAttribute("julia.mv.clones").getValueAsString(); + // base16, so must be at most 4 * length bits long + // popcount gives number of clones + weight *= APInt(val.size() * 4, val, 16).countPopulation() + 1; } return weight; } @@ -761,7 +764,8 @@ static SmallVector partitionModule(Module &M, unsigned threads) { } static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *outputs, StringRef name, - NewArchiveMember *unopt, NewArchiveMember *opt, NewArchiveMember *obj, NewArchiveMember *asm_) { + NewArchiveMember *unopt, NewArchiveMember *opt, NewArchiveMember *obj, NewArchiveMember *asm_, + std::stringstream &stream, unsigned i) { auto TM = std::unique_ptr( SourceTM.getTarget().createTargetMachine( SourceTM.getTargetTriple().str(), @@ -814,7 +818,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out end = jl_hrtime(); - dbgs() << "optimize time: " << (end - start) / 1e9 << "s\n"; + stream << "optimize time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; if (opt) { raw_string_ostream OS(*outputs); @@ -847,7 +851,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out end = jl_hrtime(); - dbgs() << "codegen time: " << (end - start) / 1e9 << "s\n"; + stream << "codegen time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; if (asm_) { SmallVector Buffer; @@ -1002,11 +1006,14 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o asm_.resize(asm_.size() + asm_out * threads); if (threads == 1) { start = jl_hrtime(); + std::stringstream stream; add_output_impl(M, TM, outputs.data() + outputs.size() - outcount * 2, name, unopt_out ? unopt.data() + unopt.size() - 1 : nullptr, opt_out ? opt.data() + opt.size() - 1 : nullptr, obj_out ? obj.data() + obj.size() - 1 : nullptr, - asm_out ? asm_.data() + asm_.size() - 1 : nullptr); + asm_out ? asm_.data() + asm_.size() - 1 : nullptr, + stream, 0); + dbgs() << stream.str(); end = jl_hrtime(); dbgs() << "Time to add output: " << (end - start) / 1e9 << "s\n"; return; @@ -1034,6 +1041,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o auto asmstart = asm_out ? asm_.data() + asm_.size() - threads : nullptr; std::vector workers(threads); + std::vector stderrs(threads); for (unsigned i = 0; i < threads; i++) { workers[i] = std::thread([&, i](){ LLVMContext ctx; @@ -1042,43 +1050,46 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o start = jl_hrtime(); auto M = cantFail(getLazyBitcodeModule(MemoryBufferRef(StringRef(serialized.data(), serialized.size()), "Optimized"), ctx), "Error loading module"); end = jl_hrtime(); - dbgs() << "Deserialization time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + stderrs[i] << "Deserialization time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; - dbgs() << "Starting shard " << i << " with weight=" << partitions[i].weight << "\n"; + stderrs[i] << "Starting shard " << i << " with weight=" << partitions[i].weight << "\n"; start = jl_hrtime(); materializePreserved(*M, partitions[i]); end = jl_hrtime(); - dbgs() << "Materialization time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + stderrs[i] << "Materialization time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; start = jl_hrtime(); construct_vars(*M, partitions[i]); M->setModuleFlag(Module::Error, "julia.mv.suffix", MDString::get(M->getContext(), "_" + std::to_string(i))); end = jl_hrtime(); - dbgs() << "Construction time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + stderrs[i] << "Construction time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; start = jl_hrtime(); dropUnusedDeclarations(*M); end = jl_hrtime(); - dbgs() << "Declaration deletion time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + stderrs[i] << "Declaration deletion time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; start = jl_hrtime(); add_output_impl(*M, TM, outstart + i * outcount * 2, name, unoptstart ? unoptstart + i : nullptr, optstart ? optstart + i : nullptr, objstart ? objstart + i : nullptr, - asmstart ? asmstart + i : nullptr); + asmstart ? asmstart + i : nullptr, + stderrs[i], i); end = jl_hrtime(); - dbgs() << "Output time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + stderrs[i] << "Output time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; }); } start = jl_hrtime(); for (auto &w : workers) w.join(); + for (auto &str : stderrs) + dbgs() << str.str(); end = jl_hrtime(); dbgs() << "Total time for parallel output: " << (end - start) / 1e9 << "s\n"; @@ -1087,32 +1098,46 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o unsigned compute_image_thread_count(Module &M) { // 32-bit systems are very memory-constrained #ifdef _P32 + dbgs() << "Threads: 1\n"; return 1; #endif - unsigned threads = std::max(llvm::hardware_concurrency().compute_thread_count() / 2, 1u); - - // memory limit check - // many threads use a lot of memory, so limit on constrained memory systems - size_t available = uv_get_available_memory(); size_t weight = 0; + size_t globals = 0; for (auto &GV : M.global_values()) { if (GV.isDeclaration()) continue; + globals++; if (isa(GV)) { weight += getFunctionWeight(cast(GV)); } else { weight += 1; } } - if (weight == 0) { - dbgs() << "No globals in module, using 1 thread\n"; + dbgs() << "Module weight: " << weight << "\n"; + if (weight < 1000) { + dbgs() << "Low module complexity bailout\n"; + dbgs() << "Threads: 1\n"; return 1; } + + unsigned threads = std::max(llvm::hardware_concurrency().compute_thread_count() / 2, 1u); + + // memory limit check + // many threads use a lot of memory, so limit on constrained memory systems + size_t available = uv_get_available_memory(); // crude estimate, available / (weight * fudge factor) = max threads size_t fudge = 10; unsigned max_threads = std::max(available / (weight * fudge), (size_t)1); - dbgs() << "Weight: " << weight << ", available: " << available << ", wanted: " << threads << ", max threads: " << max_threads << "\n"; - threads = std::min(threads, max_threads); + if (max_threads < threads) { + dbgs() << "Memory limiting threads to " << max_threads << "\n"; + threads = max_threads; + } + + max_threads = globals / 100; + if (max_threads < threads) { + dbgs() << "Low global count limiting threads to " << max_threads << " (" << globals << "globals)\n"; + threads = max_threads; + } // environment variable override const char *env_threads = getenv("JULIA_IMAGE_THREADS"); @@ -1122,10 +1147,15 @@ unsigned compute_image_thread_count(Module &M) { if (*endptr || !requested) { jl_safe_printf("WARNING: invalid value '%s' for JULIA_IMAGE_THREADS\n", env_threads); } else { + dbgs() << "Overriding threads to " << requested << "\n"; threads = requested; } } + threads = std::max(threads, 1u); + + dbgs() << "Threads: " << threads << "\n"; + return threads; } @@ -1208,7 +1238,7 @@ void jl_dump_native_impl(void *native_code, start = jl_hrtime(); - unsigned threads = compute_image_thread_count(*dataM); + unsigned threads = 1; unsigned nfvars = 0; unsigned ngvars = 0; @@ -1225,6 +1255,7 @@ void jl_dump_native_impl(void *native_code, } } } + threads = compute_image_thread_count(*dataM); nfvars = data->jl_sysimg_fvars.size(); ngvars = data->jl_sysimg_gvars.size(); emit_offset_table(*dataM, data->jl_sysimg_gvars, "jl_gvars", T_psize); diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 1a38511f34ffb..527c17e826ce9 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -313,8 +313,6 @@ struct CloneCtx { // Map from original function to one based index in `fvars` std::map func_ids{}; std::vector orig_funcs{}; - std::vector func_infos{}; - std::set cloned{}; // GV addresses and their corresponding function id (i.e. 0-based index in `fvars`) std::vector> gv_relocs{}; // Mapping from function id (i.e. 0-based index in `fvars`) to GVs to be initialized. @@ -650,7 +648,7 @@ void CloneCtx::fix_gv_uses() return changed; }; for (auto orig_f: orig_funcs) { - if (groups.size() == 1 && !cloned.count(orig_f)) + if (!orig_f->hasFnAttribute("julia.mv.clones")) continue; while (single_pass(orig_f)) { } @@ -813,7 +811,7 @@ void CloneCtx::emit_metadata() std::set shared_relocs; { auto T_int32 = Type::getInt32Ty(M.getContext()); - std::stable_sort(gv_relocs.begin(), gv_relocs.end(), + std::sort(gv_relocs.begin(), gv_relocs.end(), [] (const std::pair &lhs, const std::pair &rhs) { return lhs.second < rhs.second; From 0c68e4af4d80e4dccfb682274b3130a699be2309 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Thu, 26 Jan 2023 16:14:42 -0500 Subject: [PATCH 225/775] Alter naming, sort partitions --- src/Makefile | 2 +- src/aotcompile.cpp | 64 +++++++++++++++++++++++++++++----------------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/Makefile b/src/Makefile index bb98f6766f470..dea033c0661d9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -287,7 +287,7 @@ $(BUILDDIR)/julia_flisp.boot: $(addprefix $(SRCDIR)/,jlfrontend.scm flisp/aliase # additional dependency links $(BUILDDIR)/codegen-stubs.o $(BUILDDIR)/codegen-stubs.dbg.obj: $(SRCDIR)/intrinsics.h -$(BUILDDIR)/aotcompile.o $(BUILDDIR)/aotcompile.dbg.obj: $(SRCDIR)/jitlayers.h $(SRCDIR)/llvm-codegen-shared.h +$(BUILDDIR)/aotcompile.o $(BUILDDIR)/aotcompile.dbg.obj: $(SRCDIR)/jitlayers.h $(SRCDIR)/llvm-codegen-shared.h $(SRCDIR)/processor.h $(BUILDDIR)/ast.o $(BUILDDIR)/ast.dbg.obj: $(BUILDDIR)/julia_flisp.boot.inc $(SRCDIR)/flisp/*.h $(BUILDDIR)/builtins.o $(BUILDDIR)/builtins.dbg.obj: $(SRCDIR)/iddict.c $(SRCDIR)/builtin_proto.h $(BUILDDIR)/codegen.o $(BUILDDIR)/codegen.dbg.obj: $(addprefix $(SRCDIR)/,\ diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 701ecdfc925e8..7eeaeb94cf2da 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -66,6 +66,7 @@ using namespace llvm; #include "serialize.h" #include "julia_assert.h" #include "llvm-codegen-shared.h" +#include "processor.h" #define DEBUG_TYPE "julia_aotcompile" @@ -723,9 +724,19 @@ static SmallVector partitionModule(Module &M, unsigned threads) { pq.push(&partitions[i]); } + std::vector idxs(partitioner.nodes.size()); + std::iota(idxs.begin(), idxs.end(), 0); + std::sort(idxs.begin(), idxs.end(), [&](unsigned a, unsigned b) { + //because roots have more weight than their children, + //we can sort by weight and get the roots first + return partitioner.nodes[a].weight > partitioner.nodes[b].weight; + }); + // Assign the root of each partition to a partition, then assign its children to the same one - for (unsigned i = 0; i < partitioner.nodes.size(); ++i) { + for (unsigned idx = 0; idx < idxs.size(); ++idx) { + auto i = idxs[idx]; auto root = partitioner.find(i); + assert(root == i || partitioner.nodes[root].GV == nullptr); if (partitioner.nodes[root].GV) { auto &node = partitioner.nodes[root]; auto &P = *pq.top(); @@ -763,9 +774,10 @@ static SmallVector partitionModule(Module &M, unsigned threads) { return partitions; } -static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *outputs, StringRef name, +static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *outputs, ArrayRef names, NewArchiveMember *unopt, NewArchiveMember *opt, NewArchiveMember *obj, NewArchiveMember *asm_, std::stringstream &stream, unsigned i) { + assert(names.size() == 4); auto TM = std::unique_ptr( SourceTM.getTarget().createTargetMachine( SourceTM.getTargetTriple().str(), @@ -782,9 +794,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out AnalysisManagers AM{*TM, PB, OptimizationLevel::O0}; ModulePassManager MPM; MPM.addPass(BitcodeWriterPass(OS)); - outputs++; - *outputs = (name + "_unopt.bc").str(); - *unopt = NewArchiveMember(MemoryBufferRef(OS.str(), *outputs)); + *unopt = NewArchiveMember(MemoryBufferRef(*outputs, names[0])); outputs++; } if (!opt && !obj && !asm_) { @@ -826,9 +836,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out AnalysisManagers AM{*TM, PB, OptimizationLevel::O0}; ModulePassManager MPM; MPM.addPass(BitcodeWriterPass(OS)); - outputs++; - *outputs = (name + "_opt.bc").str(); - *opt = NewArchiveMember(MemoryBufferRef(OS.str(), *outputs)); + *opt = NewArchiveMember(MemoryBufferRef(*outputs, names[1])); outputs++; } @@ -843,9 +851,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out jl_safe_printf("ERROR: target does not support generation of object files\n"); emitter.run(M); *outputs = { Buffer.data(), Buffer.size() }; - outputs++; - *outputs = (name + ".o").str(); - *obj = NewArchiveMember(MemoryBufferRef(outputs[-1], *outputs)); + *obj = NewArchiveMember(MemoryBufferRef(*outputs, names[2])); outputs++; } @@ -862,9 +868,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out jl_safe_printf("ERROR: target does not support generation of assembly files\n"); emitter.run(M); *outputs = { Buffer.data(), Buffer.size() }; - outputs++; - *outputs = (name + ".s").str(); - *asm_ = NewArchiveMember(MemoryBufferRef(outputs[-1], *outputs)); + *asm_ = NewArchiveMember(MemoryBufferRef(*outputs, names[3])); outputs++; } } @@ -991,7 +995,7 @@ static void dropUnusedDeclarations(Module &M) { G->eraseFromParent(); } -static void add_output(Module &M, TargetMachine &TM, std::vector &outputs, StringRef name, +static void add_output(Module &M, TargetMachine &TM, std::vector &outputs, ArrayRef names, std::vector &unopt, std::vector &opt, std::vector &obj, std::vector &asm_, bool unopt_out, bool opt_out, bool obj_out, bool asm_out, @@ -999,7 +1003,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o uint64_t start = 0, end = 0; unsigned outcount = unopt_out + opt_out + obj_out + asm_out; assert(outcount); - outputs.resize(outputs.size() + outcount * threads * 2); + outputs.resize(outputs.size() + outcount * threads); unopt.resize(unopt.size() + unopt_out * threads); opt.resize(opt.size() + opt_out * threads); obj.resize(obj.size() + obj_out * threads); @@ -1007,7 +1011,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o if (threads == 1) { start = jl_hrtime(); std::stringstream stream; - add_output_impl(M, TM, outputs.data() + outputs.size() - outcount * 2, name, + add_output_impl(M, TM, outputs.data() + outputs.size() - outcount, names, unopt_out ? unopt.data() + unopt.size() - 1 : nullptr, opt_out ? opt.data() + opt.size() - 1 : nullptr, obj_out ? obj.data() + obj.size() - 1 : nullptr, @@ -1034,7 +1038,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o end = jl_hrtime(); dbgs() << "Time to serialize module: " << (end - start) / 1e9 << "s\n"; - auto outstart = outputs.data() + outputs.size() - outcount * threads * 2; + auto outstart = outputs.data() + outputs.size() - outcount * threads; auto unoptstart = unopt_out ? unopt.data() + unopt.size() - threads : nullptr; auto optstart = opt_out ? opt.data() + opt.size() - threads : nullptr; auto objstart = obj_out ? obj.data() + obj.size() - threads : nullptr; @@ -1073,7 +1077,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o stderrs[i] << "Declaration deletion time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; start = jl_hrtime(); - add_output_impl(*M, TM, outstart + i * outcount * 2, name, + add_output_impl(*M, TM, outstart + i * outcount, names, unoptstart ? unoptstart + i : nullptr, optstart ? optstart + i : nullptr, objstart ? objstart + i : nullptr, @@ -1295,14 +1299,21 @@ void jl_dump_native_impl(void *native_code, start = jl_hrtime(); - auto compile = [&](Module &M, StringRef name, unsigned threads) { add_output( - M, *SourceTM, outputs, name, + auto compile = [&](Module &M, ArrayRef names, unsigned threads) { add_output( + M, *SourceTM, outputs, names, unopt_bc_Archive, bc_Archive, obj_Archive, asm_Archive, !!unopt_bc_fname, !!bc_fname, !!obj_fname, !!asm_fname, threads ); }; + + std::array text_names = { + "text_unopt.bc", + "text_opt.bc", + "text.o", + "text.s" + }; - compile(*dataM, "text", threads); + compile(*dataM, text_names, threads); end = jl_hrtime(); @@ -1399,7 +1410,14 @@ void jl_dump_native_impl(void *native_code, ios_write(s, (const char *)data.data(), data.size()); } } - compile(*sysimageM, "data", 1); + + std::array data_names = { + "data_unopt.bc", + "data_opt.bc", + "data.o", + "data.s" + }; + compile(*sysimageM, data_names, 1); end = jl_hrtime(); From f9da0e261abf505af4a5841d7114efd7f499c755 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Thu, 26 Jan 2023 16:21:58 -0500 Subject: [PATCH 226/775] Fix whitespace --- src/aotcompile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 7eeaeb94cf2da..28b13445c8e2a 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1305,7 +1305,7 @@ void jl_dump_native_impl(void *native_code, !!unopt_bc_fname, !!bc_fname, !!obj_fname, !!asm_fname, threads ); }; - + std::array text_names = { "text_unopt.bc", "text_opt.bc", From 9a72be669b5cac14cd9a2228563e12494d6d7717 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Thu, 26 Jan 2023 23:33:41 -0500 Subject: [PATCH 227/775] Avoid unused function warning --- src/aotcompile.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 28b13445c8e2a..6d0509ac05bbc 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -610,7 +610,8 @@ static size_t getFunctionWeight(const Function &F) return weight; } -//Inline to fool gcc into not complaining about unused function when asserts are disabled +#ifndef NDEBUG + static inline bool verify_partitioning(const SmallVectorImpl &partitions, const Module &M) { StringMap GVNames; bool bad = false; @@ -644,6 +645,8 @@ static inline bool verify_partitioning(const SmallVectorImpl &partiti return !bad; } +#endif + // Chop a module up as equally as possible into threads partitions static SmallVector partitionModule(Module &M, unsigned threads) { //Start by stripping fvars and gvars, which helpfully removes their uses as well From 1f07ea51faecfdcad80aeafa99e93bb65249f369 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Fri, 27 Jan 2023 02:48:12 -0500 Subject: [PATCH 228/775] Check relocations for generic target as well --- src/llvm-multiversioning.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 527c17e826ce9..cd90699e05aad 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -152,7 +152,6 @@ static void annotate_module_clones(Module &M) { auto specs = jl_get_llvm_clone_targets(); std::vector clones(orig_funcs.size(), APInt(specs.size(), 0)); BitVector subtarget_cloned(orig_funcs.size()); - bool check_relocs = false; std::vector func_infos(orig_funcs.size()); for (unsigned i = 0; i < orig_funcs.size(); i++) { @@ -163,7 +162,6 @@ static void annotate_module_clones(Module &M) { for (unsigned j = 0; j < orig_funcs.size(); j++) { clones[j].setBit(i); } - check_relocs = true; } else { unsigned flag = specs[i].flags & clone_mask; std::set sets[2]; @@ -217,7 +215,11 @@ static void annotate_module_clones(Module &M) { } } } - if (check_relocs) { + // if there's only one target, we won't need any relocation slots + // but even if there is one clone_all and one non-clone_all, we still need + // to check for relocation slots because we must fixup instruction uses to + // point at the right function. + if (specs.size() > 1) { for (unsigned i = 0; i < orig_funcs.size(); i++) { auto &F = *orig_funcs[i]; if (subtarget_cloned[i] && !ConstantUses(orig_funcs[i], M).done()) { From 83b196758b9fabbe64b36750f56d50e083f87020 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Fri, 27 Jan 2023 03:27:27 -0500 Subject: [PATCH 229/775] Debug macos linker --- src/aotcompile.cpp | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 6d0509ac05bbc..f3c45241c4a0a 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -610,11 +610,11 @@ static size_t getFunctionWeight(const Function &F) return weight; } -#ifndef NDEBUG static inline bool verify_partitioning(const SmallVectorImpl &partitions, const Module &M) { - StringMap GVNames; bool bad = false; +#ifdef JL_DEBUG_BUILD + StringMap GVNames; for (uint32_t i = 0; i < partitions.size(); i++) { for (auto &name : partitions[i].globals) { if (GVNames.count(name.getKey())) { @@ -642,11 +642,10 @@ static inline bool verify_partitioning(const SmallVectorImpl &partiti } } } +#endif return !bad; } -#endif - // Chop a module up as equally as possible into threads partitions static SmallVector partitionModule(Module &M, unsigned threads) { //Start by stripping fvars and gvars, which helpfully removes their uses as well @@ -772,7 +771,9 @@ static SmallVector partitionModule(Module &M, unsigned threads) { } } - assert(verify_partitioning(partitions, M) && "Partitioning failed to partition globals correctly"); + bool verified = verify_partitioning(partitions, M); + assert(verified && "Partitioning failed to partition globals correctly"); + (void) verified; return partitions; } @@ -1135,10 +1136,14 @@ unsigned compute_image_thread_count(Module &M) { // crude estimate, available / (weight * fudge factor) = max threads size_t fudge = 10; unsigned max_threads = std::max(available / (weight * fudge), (size_t)1); - if (max_threads < threads) { - dbgs() << "Memory limiting threads to " << max_threads << "\n"; - threads = max_threads; - } + dbgs() << "Available memory: " << available << " bytes\n"; + dbgs() << "Max threads: " << max_threads << "\n"; + dbgs() << "Temporarily disabling memory limiting threads\n"; + //TODO reenable + // if (max_threads < threads) { + // dbgs() << "Memory limiting threads to " << max_threads << "\n"; + // threads = max_threads; + // } max_threads = globals / 100; if (max_threads < threads) { @@ -1420,7 +1425,11 @@ void jl_dump_native_impl(void *native_code, "data.o", "data.s" }; + dbgs() << "Dumping sysimage data module\n"; + dbgs() << *sysimageM << "\n"; compile(*sysimageM, data_names, 1); + dbgs() << "Post-optimization sysimageM\n"; + dbgs() << *sysimageM << "\n"; end = jl_hrtime(); From c98ff304ab697f0eb492a150833c12baa499f29e Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Fri, 27 Jan 2023 17:41:11 -0500 Subject: [PATCH 230/775] Respect JULIA_CPU_THREADS --- src/aotcompile.cpp | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index f3c45241c4a0a..88c54d228c307 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1153,14 +1153,33 @@ unsigned compute_image_thread_count(Module &M) { // environment variable override const char *env_threads = getenv("JULIA_IMAGE_THREADS"); + bool env_threads_set = false; if (env_threads) { char *endptr; unsigned long requested = strtoul(env_threads, &endptr, 10); if (*endptr || !requested) { jl_safe_printf("WARNING: invalid value '%s' for JULIA_IMAGE_THREADS\n", env_threads); } else { - dbgs() << "Overriding threads to " << requested << "\n"; + dbgs() << "Overriding threads to " << requested << " due to JULIA_IMAGE_THREADS\n"; threads = requested; + env_threads_set = true; + } + } + + // more defaults + if (!env_threads_set && threads > 1) { + if (jl_options.nthreads && jl_options.nthreads < threads) { + dbgs() << "Overriding threads to " << jl_options.nthreads << " due to -t option\n"; + threads = jl_options.nthreads; + } else if (auto fallbackenv = getenv(NUM_THREADS_NAME)) { + char *endptr; + unsigned long requested = strtoul(fallbackenv, &endptr, 10); + if (*endptr || !requested) { + jl_safe_printf("WARNING: invalid value '%s' for %s\m", fallbackenv, NUM_THREADS_NAME); + } else if (requested < threads) { + dbgs() << "Overriding threads to " << requested << " due to " << NUM_THREADS_NAME << "\n"; + threads = requested; + } } } @@ -1426,10 +1445,15 @@ void jl_dump_native_impl(void *native_code, "data.s" }; dbgs() << "Dumping sysimage data module\n"; + for (auto &F : *sysimageM) { + dbgs() << F << "\n"; + } dbgs() << *sysimageM << "\n"; compile(*sysimageM, data_names, 1); dbgs() << "Post-optimization sysimageM\n"; - dbgs() << *sysimageM << "\n"; + for (auto &F : *sysimageM) { + dbgs() << F << "\n"; + } end = jl_hrtime(); From 8cf48f2369a4197c46858e0bc6c166ad69d3e8d4 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Fri, 27 Jan 2023 18:23:03 -0500 Subject: [PATCH 231/775] Don't inject CRT aliases on macos --- src/aotcompile.cpp | 54 +++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 88c54d228c307..c40868af11c58 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1168,16 +1168,18 @@ unsigned compute_image_thread_count(Module &M) { // more defaults if (!env_threads_set && threads > 1) { - if (jl_options.nthreads && jl_options.nthreads < threads) { - dbgs() << "Overriding threads to " << jl_options.nthreads << " due to -t option\n"; - threads = jl_options.nthreads; - } else if (auto fallbackenv = getenv(NUM_THREADS_NAME)) { + if (jl_options.nthreads) { + if (static_cast(jl_options.nthreads) < threads) { + dbgs() << "Overriding threads to " << jl_options.nthreads << " due to -t option\n"; + threads = jl_options.nthreads; + } + } else if (auto fallbackenv = getenv("JULIA_CPU_THREADS")) { char *endptr; unsigned long requested = strtoul(fallbackenv, &endptr, 10); if (*endptr || !requested) { - jl_safe_printf("WARNING: invalid value '%s' for %s\m", fallbackenv, NUM_THREADS_NAME); + jl_safe_printf("WARNING: invalid value '%s' for JULIA_CPU_THREADS\n", fallbackenv); } else if (requested < threads) { - dbgs() << "Overriding threads to " << requested << " due to " << NUM_THREADS_NAME << "\n"; + dbgs() << "Overriding threads to " << requested << " due to JULIA_CPU_THREADS\n"; threads = requested; } } @@ -1355,20 +1357,23 @@ void jl_dump_native_impl(void *native_code, sysimageM->setStackProtectorGuard(dataM->getStackProtectorGuard()); sysimageM->setOverrideStackAlignment(dataM->getOverrideStackAlignment()); #endif - // We would like to emit an alias or an weakref alias to redirect these symbols - // but LLVM doesn't let us emit a GlobalAlias to a declaration... - // So for now we inject a definition of these functions that calls our runtime - // functions. We do so after optimization to avoid cloning these functions. - injectCRTAlias(*sysimageM, "__gnu_h2f_ieee", "julia__gnu_h2f_ieee", - FunctionType::get(Type::getFloatTy(Context), { Type::getHalfTy(Context) }, false)); - injectCRTAlias(*sysimageM, "__extendhfsf2", "julia__gnu_h2f_ieee", - FunctionType::get(Type::getFloatTy(Context), { Type::getHalfTy(Context) }, false)); - injectCRTAlias(*sysimageM, "__gnu_f2h_ieee", "julia__gnu_f2h_ieee", - FunctionType::get(Type::getHalfTy(Context), { Type::getFloatTy(Context) }, false)); - injectCRTAlias(*sysimageM, "__truncsfhf2", "julia__gnu_f2h_ieee", - FunctionType::get(Type::getHalfTy(Context), { Type::getFloatTy(Context) }, false)); - injectCRTAlias(*sysimageM, "__truncdfhf2", "julia__truncdfhf2", - FunctionType::get(Type::getHalfTy(Context), { Type::getDoubleTy(Context) }, false)); + + if (!TheTriple.isOSDarwin()) { + // We would like to emit an alias or an weakref alias to redirect these symbols + // but LLVM doesn't let us emit a GlobalAlias to a declaration... + // So for now we inject a definition of these functions that calls our runtime + // functions. We do so after optimization to avoid cloning these functions. + injectCRTAlias(*sysimageM, "__gnu_h2f_ieee", "julia__gnu_h2f_ieee", + FunctionType::get(Type::getFloatTy(Context), { Type::getHalfTy(Context) }, false)); + injectCRTAlias(*sysimageM, "__extendhfsf2", "julia__gnu_h2f_ieee", + FunctionType::get(Type::getFloatTy(Context), { Type::getHalfTy(Context) }, false)); + injectCRTAlias(*sysimageM, "__gnu_f2h_ieee", "julia__gnu_f2h_ieee", + FunctionType::get(Type::getHalfTy(Context), { Type::getFloatTy(Context) }, false)); + injectCRTAlias(*sysimageM, "__truncsfhf2", "julia__gnu_f2h_ieee", + FunctionType::get(Type::getHalfTy(Context), { Type::getFloatTy(Context) }, false)); + injectCRTAlias(*sysimageM, "__truncdfhf2", "julia__truncdfhf2", + FunctionType::get(Type::getHalfTy(Context), { Type::getDoubleTy(Context) }, false)); + } if (TheTriple.isOSWindows()) { // Windows expect that the function `_DllMainStartup` is present in an dll. @@ -1444,16 +1449,7 @@ void jl_dump_native_impl(void *native_code, "data.o", "data.s" }; - dbgs() << "Dumping sysimage data module\n"; - for (auto &F : *sysimageM) { - dbgs() << F << "\n"; - } - dbgs() << *sysimageM << "\n"; compile(*sysimageM, data_names, 1); - dbgs() << "Post-optimization sysimageM\n"; - for (auto &F : *sysimageM) { - dbgs() << F << "\n"; - } end = jl_hrtime(); From 4e35f416bb71f0a33ad3605742bfa8f6bc82a85b Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Wed, 1 Feb 2023 03:02:47 -0500 Subject: [PATCH 232/775] Clean up timers and prints, link to JULIA_IMAGE_TIMINGS --- src/aotcompile.cpp | 272 +++++++++++++++++++++-------------- src/llvm-multiversioning.cpp | 4 +- 2 files changed, 168 insertions(+), 108 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index c40868af11c58..79e9ea07eb592 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -57,6 +57,7 @@ #include #include +#include #include @@ -269,8 +270,6 @@ void replaceUsesWithLoad(Function &F, function_refreleaseContext(std::move(ctx)); } - end = jl_hrtime(); - dbgs() << "jl_create_native: " << (end - start) / 1e9 << "s\n"; return (void*)data; } @@ -589,7 +586,6 @@ static void get_fvars_gvars(Module &M, DenseMap &fvars, gvars_gv->eraseFromParent(); fvars_idxs->eraseFromParent(); gvars_idxs->eraseFromParent(); - dbgs() << "Finished getting fvars/gvars\n"; } static size_t getFunctionWeight(const Function &F) @@ -778,9 +774,74 @@ static SmallVector partitionModule(Module &M, unsigned threads) { return partitions; } +struct ImageTimer { + uint64_t elapsed = 0; + std::string name; + std::string desc; + + void startTimer() { + elapsed = jl_hrtime(); + } + + void stopTimer() { + elapsed = jl_hrtime() - elapsed; + } + + void init(const Twine &name, const Twine &desc) { + this->name = name.str(); + this->desc = desc.str(); + } + + operator bool() const { + return elapsed != 0; + } + + void print(raw_ostream &out, bool clear=false) { + if (!*this) + return; + out << llvm::formatv("{0:F3} ", elapsed / 1e9) << name << " " << desc << "\n"; + if (clear) + elapsed = 0; + } +}; + +struct ShardTimers { + ImageTimer deserialize; + ImageTimer materialize; + ImageTimer construct; + ImageTimer deletion; + // impl timers + ImageTimer unopt; + ImageTimer optimize; + ImageTimer opt; + ImageTimer obj; + ImageTimer asm_; + + std::string name; + std::string desc; + + void print(raw_ostream &out, bool clear=false) { + StringRef sep = "===-------------------------------------------------------------------------==="; + out << formatv("{0}\n{1}\n{0}\n", sep, fmt_align(name + " : " + desc, AlignStyle::Center, sep.size())); + auto total = deserialize.elapsed + materialize.elapsed + construct.elapsed + deletion.elapsed + + unopt.elapsed + optimize.elapsed + opt.elapsed + obj.elapsed + asm_.elapsed; + out << "Time (s) Name Description\n"; + deserialize.print(out, clear); + materialize.print(out, clear); + construct.print(out, clear); + deletion.print(out, clear); + unopt.print(out, clear); + optimize.print(out, clear); + opt.print(out, clear); + obj.print(out, clear); + asm_.print(out, clear); + out << llvm::formatv("{0:F3} total Total time taken\n", total / 1e9); + } +}; + static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *outputs, ArrayRef names, NewArchiveMember *unopt, NewArchiveMember *opt, NewArchiveMember *obj, NewArchiveMember *asm_, - std::stringstream &stream, unsigned i) { + ShardTimers &timers, unsigned shardidx) { assert(names.size() == 4); auto TM = std::unique_ptr( SourceTM.getTarget().createTargetMachine( @@ -793,6 +854,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out SourceTM.getOptLevel())); if (unopt) { + timers.unopt.startTimer(); raw_string_ostream OS(*outputs); PassBuilder PB; AnalysisManagers AM{*TM, PB, OptimizationLevel::O0}; @@ -800,14 +862,14 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out MPM.addPass(BitcodeWriterPass(OS)); *unopt = NewArchiveMember(MemoryBufferRef(*outputs, names[0])); outputs++; + timers.unopt.stopTimer(); } if (!opt && !obj && !asm_) { return; } assert(!verifyModule(M, &errs())); - uint64_t start = jl_hrtime(); - uint64_t end = 0; + timers.optimize.startTimer(); #ifndef JL_USE_NEW_PM legacy::PassManager optimizer; @@ -829,12 +891,11 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out #endif optimizer.run(M); assert(!verifyModule(M, &errs())); - - end = jl_hrtime(); - - stream << "optimize time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + + timers.optimize.stopTimer(); if (opt) { + timers.opt.startTimer(); raw_string_ostream OS(*outputs); PassBuilder PB; AnalysisManagers AM{*TM, PB, OptimizationLevel::O0}; @@ -842,11 +903,11 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out MPM.addPass(BitcodeWriterPass(OS)); *opt = NewArchiveMember(MemoryBufferRef(*outputs, names[1])); outputs++; + timers.opt.stopTimer(); } - start = jl_hrtime(); - if (obj) { + timers.obj.startTimer(); SmallVector Buffer; raw_svector_ostream OS(Buffer); legacy::PassManager emitter; @@ -857,13 +918,11 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out *outputs = { Buffer.data(), Buffer.size() }; *obj = NewArchiveMember(MemoryBufferRef(*outputs, names[2])); outputs++; + timers.obj.stopTimer(); } - end = jl_hrtime(); - - stream << "codegen time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; - if (asm_) { + timers.asm_.startTimer(); SmallVector Buffer; raw_svector_ostream OS(Buffer); legacy::PassManager emitter; @@ -874,6 +933,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out *outputs = { Buffer.data(), Buffer.size() }; *asm_ = NewArchiveMember(MemoryBufferRef(*outputs, names[3])); outputs++; + timers.asm_.stopTimer(); } } @@ -1004,7 +1064,6 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o std::vector &obj, std::vector &asm_, bool unopt_out, bool opt_out, bool obj_out, bool asm_out, unsigned threads) { - uint64_t start = 0, end = 0; unsigned outcount = unopt_out + opt_out + obj_out + asm_out; assert(outcount); outputs.resize(outputs.size() + outcount * threads); @@ -1012,22 +1071,64 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o opt.resize(opt.size() + opt_out * threads); obj.resize(obj.size() + obj_out * threads); asm_.resize(asm_.size() + asm_out * threads); + auto name = names[2]; + name.consume_back(".o"); + TimerGroup timer_group("add_output", ("Time to optimize and emit LLVM module " + name).str()); + SmallVector timers(threads); + for (unsigned i = 0; i < threads; ++i) { + auto idx = std::to_string(i); + timers[i].name = "shard_" + idx; + timers[i].desc = ("Timings for " + name + " module shard " + idx).str(); + timers[i].deserialize.init("deserialize_" + idx, "Deserialize module"); + timers[i].materialize.init("materialize_" + idx, "Materialize declarations"); + timers[i].construct.init("construct_" + idx, "Construct partitioned definitions"); + timers[i].deletion.init("deletion_" + idx, "Delete dead declarations"); + timers[i].unopt.init("unopt_" + idx, "Emit unoptimized bitcode"); + timers[i].optimize.init("optimize_" + idx, "Optimize shard"); + timers[i].opt.init("opt_" + idx, "Emit optimized bitcode"); + timers[i].obj.init("obj_" + idx, "Emit object file"); + timers[i].asm_.init("asm_" + idx, "Emit assembly file"); + } + Timer partition_timer("partition", "Partition module", timer_group); + Timer serialize_timer("serialize", "Serialize module", timer_group); + Timer output_timer("output", "Add outputs", timer_group); + bool report_timings = false; + if (auto env = getenv("JULIA_IMAGE_TIMINGS")) { + char *endptr; + unsigned long val = strtoul(env, &endptr, 10); + if (endptr != env && !*endptr && val <= 1) { + report_timings = val; + } else { + if (StringRef("true").compare_insensitive(env) == 0) + report_timings = true; + else if (StringRef("false").compare_insensitive(env) == 0) + report_timings = false; + else + errs() << "WARNING: Invalid value for JULIA_IMAGE_TIMINGS: " << env << "\n"; + } + } if (threads == 1) { - start = jl_hrtime(); - std::stringstream stream; + output_timer.startTimer(); add_output_impl(M, TM, outputs.data() + outputs.size() - outcount, names, unopt_out ? unopt.data() + unopt.size() - 1 : nullptr, opt_out ? opt.data() + opt.size() - 1 : nullptr, obj_out ? obj.data() + obj.size() - 1 : nullptr, asm_out ? asm_.data() + asm_.size() - 1 : nullptr, - stream, 0); - dbgs() << stream.str(); - end = jl_hrtime(); - dbgs() << "Time to add output: " << (end - start) / 1e9 << "s\n"; + timers[0], 0); + output_timer.stopTimer(); + + if (!report_timings) { + timer_group.clear(); + } else { + timer_group.print(dbgs(), true); + for (auto &t : timers) { + t.print(dbgs(), true); + } + } return; } - start = jl_hrtime(); + partition_timer.startTimer(); uint64_t counter = 0; for (auto &G : M.global_values()) { if (!G.isDeclaration() && !G.hasName()) { @@ -1035,12 +1136,12 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o } } auto partitions = partitionModule(M, threads); - end = jl_hrtime(); - dbgs() << "Time to partition module: " << (end - start) / 1e9 << "s\n"; - start = jl_hrtime(); + partition_timer.stopTimer(); + serialize_timer.startTimer(); auto serialized = serializeModule(M); - end = jl_hrtime(); - dbgs() << "Time to serialize module: " << (end - start) / 1e9 << "s\n"; + serialize_timer.stopTimer(); + + output_timer.startTimer(); auto outstart = outputs.data() + outputs.size() - outcount * threads; auto unoptstart = unopt_out ? unopt.data() + unopt.size() - threads : nullptr; @@ -1049,64 +1150,56 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o auto asmstart = asm_out ? asm_.data() + asm_.size() - threads : nullptr; std::vector workers(threads); - std::vector stderrs(threads); for (unsigned i = 0; i < threads; i++) { workers[i] = std::thread([&, i](){ LLVMContext ctx; - uint64_t start = 0; - uint64_t end = 0; - start = jl_hrtime(); + timers[i].deserialize.startTimer(); auto M = cantFail(getLazyBitcodeModule(MemoryBufferRef(StringRef(serialized.data(), serialized.size()), "Optimized"), ctx), "Error loading module"); - end = jl_hrtime(); - stderrs[i] << "Deserialization time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + timers[i].deserialize.stopTimer(); - stderrs[i] << "Starting shard " << i << " with weight=" << partitions[i].weight << "\n"; + // dbgs() << "Starting shard " << i << " with weight=" << partitions[i].weight << "\n"; - start = jl_hrtime(); + timers[i].materialize.startTimer(); materializePreserved(*M, partitions[i]); - end = jl_hrtime(); - stderrs[i] << "Materialization time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + timers[i].materialize.stopTimer(); - start = jl_hrtime(); + timers[i].construct.startTimer(); construct_vars(*M, partitions[i]); M->setModuleFlag(Module::Error, "julia.mv.suffix", MDString::get(M->getContext(), "_" + std::to_string(i))); - end = jl_hrtime(); - - stderrs[i] << "Construction time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + timers[i].construct.stopTimer(); - start = jl_hrtime(); + timers[i].deletion.startTimer(); dropUnusedDeclarations(*M); - end = jl_hrtime(); - - stderrs[i] << "Declaration deletion time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + timers[i].deletion.stopTimer(); - start = jl_hrtime(); add_output_impl(*M, TM, outstart + i * outcount, names, unoptstart ? unoptstart + i : nullptr, optstart ? optstart + i : nullptr, objstart ? objstart + i : nullptr, asmstart ? asmstart + i : nullptr, - stderrs[i], i); - end = jl_hrtime(); - - stderrs[i] << "Output time for shard " << i << ": " << (end - start) / 1e9 << "s\n"; + timers[i], i); }); } - start = jl_hrtime(); for (auto &w : workers) w.join(); - for (auto &str : stderrs) - dbgs() << str.str(); - end = jl_hrtime(); - dbgs() << "Total time for parallel output: " << (end - start) / 1e9 << "s\n"; + output_timer.stopTimer(); + + if (!report_timings) { + timer_group.clear(); + } else { + timer_group.print(dbgs(), true); + for (auto &t : timers) { + t.print(dbgs(), true); + } + } } unsigned compute_image_thread_count(Module &M) { // 32-bit systems are very memory-constrained #ifdef _P32 - dbgs() << "Threads: 1\n"; + // dbgs() << "Threads: 1\n"; return 1; #endif size_t weight = 0; @@ -1121,10 +1214,10 @@ unsigned compute_image_thread_count(Module &M) { weight += 1; } } - dbgs() << "Module weight: " << weight << "\n"; + // dbgs() << "Module weight: " << weight << "\n"; if (weight < 1000) { - dbgs() << "Low module complexity bailout\n"; - dbgs() << "Threads: 1\n"; + // dbgs() << "Low module complexity bailout\n"; + // dbgs() << "Threads: 1\n"; return 1; } @@ -1136,9 +1229,9 @@ unsigned compute_image_thread_count(Module &M) { // crude estimate, available / (weight * fudge factor) = max threads size_t fudge = 10; unsigned max_threads = std::max(available / (weight * fudge), (size_t)1); - dbgs() << "Available memory: " << available << " bytes\n"; - dbgs() << "Max threads: " << max_threads << "\n"; - dbgs() << "Temporarily disabling memory limiting threads\n"; + // dbgs() << "Available memory: " << available << " bytes\n"; + // dbgs() << "Max threads: " << max_threads << "\n"; + // dbgs() << "Temporarily disabling memory limiting threads\n"; //TODO reenable // if (max_threads < threads) { // dbgs() << "Memory limiting threads to " << max_threads << "\n"; @@ -1147,7 +1240,7 @@ unsigned compute_image_thread_count(Module &M) { max_threads = globals / 100; if (max_threads < threads) { - dbgs() << "Low global count limiting threads to " << max_threads << " (" << globals << "globals)\n"; + // dbgs() << "Low global count limiting threads to " << max_threads << " (" << globals << "globals)\n"; threads = max_threads; } @@ -1160,7 +1253,7 @@ unsigned compute_image_thread_count(Module &M) { if (*endptr || !requested) { jl_safe_printf("WARNING: invalid value '%s' for JULIA_IMAGE_THREADS\n", env_threads); } else { - dbgs() << "Overriding threads to " << requested << " due to JULIA_IMAGE_THREADS\n"; + // dbgs() << "Overriding threads to " << requested << " due to JULIA_IMAGE_THREADS\n"; threads = requested; env_threads_set = true; } @@ -1168,18 +1261,13 @@ unsigned compute_image_thread_count(Module &M) { // more defaults if (!env_threads_set && threads > 1) { - if (jl_options.nthreads) { - if (static_cast(jl_options.nthreads) < threads) { - dbgs() << "Overriding threads to " << jl_options.nthreads << " due to -t option\n"; - threads = jl_options.nthreads; - } - } else if (auto fallbackenv = getenv("JULIA_CPU_THREADS")) { + if (auto fallbackenv = getenv("JULIA_CPU_THREADS")) { char *endptr; unsigned long requested = strtoul(fallbackenv, &endptr, 10); if (*endptr || !requested) { jl_safe_printf("WARNING: invalid value '%s' for JULIA_CPU_THREADS\n", fallbackenv); } else if (requested < threads) { - dbgs() << "Overriding threads to " << requested << " due to JULIA_CPU_THREADS\n"; + // dbgs() << "Overriding threads to " << requested << " due to JULIA_CPU_THREADS\n"; threads = requested; } } @@ -1187,7 +1275,7 @@ unsigned compute_image_thread_count(Module &M) { threads = std::max(threads, 1u); - dbgs() << "Threads: " << threads << "\n"; + // dbgs() << "Threads: " << threads << "\n"; return threads; } @@ -1200,12 +1288,10 @@ void jl_dump_native_impl(void *native_code, const char *asm_fname, const char *sysimg_data, size_t sysimg_len, ios_t *s) { - uint64_t start = jl_hrtime(); - uint64_t end = 0; JL_TIMING(NATIVE_DUMP); jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code; if (!bc_fname && !unopt_bc_fname && !obj_fname && !asm_fname) { - dbgs() << "No output requested, skipping native code dump?\n"; + // dbgs() << "No output requested, skipping native code dump?\n"; delete data; return; } @@ -1265,12 +1351,6 @@ void jl_dump_native_impl(void *native_code, bool imaging_mode = imaging_default() || jl_options.outputo; - end = jl_hrtime(); - - dbgs() << "setup time: " << (end - start) / 1e9 << "s\n"; - - start = jl_hrtime(); - unsigned threads = 1; unsigned nfvars = 0; unsigned ngvars = 0; @@ -1322,12 +1402,6 @@ void jl_dump_native_impl(void *native_code, "jl_RTLD_DEFAULT_handle_pointer"), TheTriple); } - end = jl_hrtime(); - - dbgs() << "metadata time: " << (end - start) / 1e9 << "s\n"; - - start = jl_hrtime(); - auto compile = [&](Module &M, ArrayRef names, unsigned threads) { add_output( M, *SourceTM, outputs, names, unopt_bc_Archive, bc_Archive, obj_Archive, asm_Archive, @@ -1344,12 +1418,6 @@ void jl_dump_native_impl(void *native_code, compile(*dataM, text_names, threads); - end = jl_hrtime(); - - dbgs() << "text output time: " << (end - start) / 1e9 << "s\n"; - - start = jl_hrtime(); - auto sysimageM = std::make_unique("sysimage", Context); sysimageM->setTargetTriple(dataM->getTargetTriple()); sysimageM->setDataLayout(dataM->getDataLayout()); @@ -1451,12 +1519,6 @@ void jl_dump_native_impl(void *native_code, }; compile(*sysimageM, data_names, 1); - end = jl_hrtime(); - - dbgs() << "data module time: " << (end - start) / 1e9 << "s\n"; - - start = jl_hrtime(); - object::Archive::Kind Kind = getDefaultForHost(TheTriple); if (unopt_bc_fname) handleAllErrors(writeArchive(unopt_bc_fname, unopt_bc_Archive, true, @@ -1471,10 +1533,6 @@ void jl_dump_native_impl(void *native_code, handleAllErrors(writeArchive(asm_fname, asm_Archive, true, Kind, true, false), reportWriterError); - end = jl_hrtime(); - - dbgs() << "archive time: " << (end - start) / 1e9 << "s\n"; - delete data; } diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index cd90699e05aad..42aa34d3bdb4f 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -915,8 +915,9 @@ static bool runMultiVersioning(Module &M, bool allow_bad_fvars) // * Cloned function -> Original function (add as we clone functions) // * Original function -> Base function (target specific and updated by LLVM) // * ID -> relocation slots (const). - if (M.getName() == "sysimage") + if (!M.getModuleFlag("julia.mv.enable")) { return false; + } GlobalVariable *fvars = M.getGlobalVariable("jl_fvars"); GlobalVariable *gvars = M.getGlobalVariable("jl_gvars"); @@ -986,6 +987,7 @@ static RegisterPass X("JuliaMultiVersioning", "JuliaMulti void multiversioning_preannotate(Module &M) { annotate_module_clones(M); + M.addModuleFlag(Module::ModFlagBehavior::Error, "julia.mv.enable", 1); } void replaceUsesWithLoad(Function &F, function_ref should_replace, MDNode *tbaa_const) { From a723211c3106d6eebfbbbb680615269995cab0ec Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Wed, 1 Feb 2023 03:16:18 -0500 Subject: [PATCH 233/775] Fix whitespace --- src/aotcompile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 79e9ea07eb592..428f397c35aed 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -891,7 +891,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out #endif optimizer.run(M); assert(!verifyModule(M, &errs())); - + timers.optimize.stopTimer(); if (opt) { @@ -1185,7 +1185,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o w.join(); output_timer.stopTimer(); - + if (!report_timings) { timer_group.clear(); } else { From 7cf839aaeb636e27b75c9857fe52913477ac1734 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Wed, 1 Feb 2023 06:30:22 -0500 Subject: [PATCH 234/775] Don't leave aliases to extern global objects --- src/aotcompile.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 428f397c35aed..fffc7839d74c9 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -938,6 +938,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out } static auto serializeModule(const Module &M) { + assert(!verifyModule(M, &errs()) && "Serializing invalid module!"); SmallVector ClonedModuleBuffer; BitcodeWriter BCWriter(ClonedModuleBuffer); BCWriter.writeModule(M); @@ -976,9 +977,16 @@ static void materializePreserved(Module &M, Partition &partition) { if (!GA.isDeclaration()) { if (!Preserve.contains(&GA)) { if (GA.getValueType()->isFunctionTy()) { - DeletedAliases.push_back({ &GA, Function::Create(cast(GA.getValueType()), GlobalValue::ExternalLinkage, "", &M) }); + auto F = Function::Create(cast(GA.getValueType()), GlobalValue::ExternalLinkage, "", &M); + // This is an extremely sad hack to make sure the global alias never points to an extern function + auto BB = BasicBlock::Create(M.getContext(), "", F); + new UnreachableInst(M.getContext(), BB); + GA.setAliasee(F); + + DeletedAliases.push_back({ &GA, F }); } else { - DeletedAliases.push_back({ &GA, new GlobalVariable(M, GA.getValueType(), false, GlobalValue::ExternalLinkage, nullptr) }); + auto GV = new GlobalVariable(M, GA.getValueType(), false, GlobalValue::ExternalLinkage, Constant::getNullValue(GA.getValueType())); + DeletedAliases.push_back({ &GA, GV }); } } } @@ -988,6 +996,12 @@ static void materializePreserved(Module &M, Partition &partition) { Deleted.second->takeName(Deleted.first); Deleted.first->replaceAllUsesWith(Deleted.second); Deleted.first->eraseFromParent(); + // undo our previous sad hack + if (auto F = dyn_cast(Deleted.second)) { + F->deleteBody(); + } else { + cast(Deleted.second)->setInitializer(nullptr); + } } } From fa208d43a95e1336c6e793795aad8134cb72883b Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Wed, 1 Feb 2023 10:41:27 -0500 Subject: [PATCH 235/775] Break multiversioning's dependency on jl_get_llvm_clone_targets --- src/llvm-multiversioning.cpp | 110 ++++++++++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 14 deletions(-) diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 42aa34d3bdb4f..b4f67ebe22c7d 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -140,6 +140,64 @@ static uint32_t collect_func_info(Function &F, bool &has_veccall) return flag; } +struct TargetSpec { + std::string cpu_name; + std::string cpu_features; + uint32_t base; + uint32_t flags; + + TargetSpec() = default; + + static TargetSpec fromSpec(jl_target_spec_t &spec) { + TargetSpec out; + out.cpu_name = spec.cpu_name; + out.cpu_features = spec.cpu_features; + out.base = spec.base; + out.flags = spec.flags; + return out; + } + + static TargetSpec fromMD(MDTuple *tup) { + TargetSpec out; + assert(tup->getNumOperands() == 4); + out.cpu_name = cast(tup->getOperand(0))->getString().str(); + out.cpu_features = cast(tup->getOperand(1))->getString().str(); + out.base = cast(cast(tup->getOperand(2))->getValue())->getZExtValue(); + out.flags = cast(cast(tup->getOperand(3))->getValue())->getZExtValue(); + return out; + } + + MDNode *toMD(LLVMContext &ctx) const { + return MDTuple::get(ctx, { + MDString::get(ctx, cpu_name), + MDString::get(ctx, cpu_features), + ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(ctx), base)), + ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(ctx), flags)) + }); + } +}; + +static Optional> get_target_specs(Module &M) { + auto md = M.getModuleFlag("julia.mv.specs"); + if (!md) + return None; + auto tup = cast(md); + std::vector out(tup->getNumOperands()); + for (unsigned i = 0; i < tup->getNumOperands(); i++) { + out[i] = TargetSpec::fromMD(cast(tup->getOperand(i).get())); + } + return out; +} + +static void set_target_specs(Module &M, ArrayRef specs) { + std::vector md; + md.reserve(specs.size()); + for (auto &spec: specs) { + md.push_back(spec.toMD(M.getContext())); + } + M.addModuleFlag(Module::Error, "julia.mv.specs", MDTuple::get(M.getContext(), md)); +} + static void annotate_module_clones(Module &M) { CallGraph CG(M); std::vector orig_funcs; @@ -149,7 +207,17 @@ static void annotate_module_clones(Module &M) { orig_funcs.push_back(&F); } bool has_veccall = false; - auto specs = jl_get_llvm_clone_targets(); + std::vector specs; + if (auto maybe_specs = get_target_specs(M)) { + specs = std::move(*maybe_specs); + } else { + auto full_specs = jl_get_llvm_clone_targets(); + specs.reserve(full_specs.size()); + for (auto &spec: full_specs) { + specs.push_back(TargetSpec::fromSpec(spec)); + } + set_target_specs(M, specs); + } std::vector clones(orig_funcs.size(), APInt(specs.size(), 0)); BitVector subtarget_cloned(orig_funcs.size()); @@ -255,6 +323,7 @@ static void annotate_module_clones(Module &M) { if (has_veccall) { M.addModuleFlag(Module::Max, "julia.mv.veccall", 1); } + M.addModuleFlag(Module::Error, "julia.mv.annotated", 1); } struct CloneCtx { @@ -305,7 +374,7 @@ struct CloneCtx { void rewrite_alias(GlobalAlias *alias, Function* F); MDNode *tbaa_const; - std::vector specs; + std::vector specs; std::vector groups{}; std::vector linearized; std::vector fvars; @@ -362,7 +431,7 @@ static inline std::vector consume_gv(Module &M, const char *name, bool allow // Collect basic information about targets and functions. CloneCtx::CloneCtx(Module &M, bool allow_bad_fvars) : tbaa_const(tbaa_make_child_with_context(M.getContext(), "jtbaa_const", nullptr, true).first), - specs(jl_get_llvm_clone_targets()), + specs(*get_target_specs(M)), fvars(consume_gv(M, "jl_fvars", allow_bad_fvars)), gvars(consume_gv(M, "jl_gvars", false)), M(M), @@ -473,24 +542,24 @@ static void clone_function(Function *F, Function *new_f, ValueToValueMapTy &vmap #endif } -static void add_features(Function *F, StringRef name, StringRef features, uint32_t flags) +static void add_features(Function *F, TargetSpec &spec) { auto attr = F->getFnAttribute("target-features"); if (attr.isStringAttribute()) { std::string new_features(attr.getValueAsString()); new_features += ","; - new_features += features; + new_features += spec.cpu_features; F->addFnAttr("target-features", new_features); } else { - F->addFnAttr("target-features", features); + F->addFnAttr("target-features", spec.cpu_features); } - F->addFnAttr("target-cpu", name); + F->addFnAttr("target-cpu", spec.cpu_name); if (!F->hasFnAttribute(Attribute::OptimizeNone)) { - if (flags & JL_TARGET_OPTSIZE) { + if (spec.flags & JL_TARGET_OPTSIZE) { F->addFnAttr(Attribute::OptimizeForSize); } - else if (flags & JL_TARGET_MINSIZE) { + else if (spec.flags & JL_TARGET_MINSIZE) { F->addFnAttr(Attribute::MinSize); } } @@ -514,18 +583,19 @@ void CloneCtx::clone_bodies() if (!F->isDeclaration()) { clone_function(group_F, target_F, *target.vmap); } - add_features(target_F, specs[target.idx].cpu_name, - specs[target.idx].cpu_features, specs[target.idx].flags); + add_features(target_F, specs[target.idx]); target_F->addFnAttr("julia.mv.clone", std::to_string(i)); } } + // don't set the original function's features yet, + // since we may clone it for later groups if (i != 0) { - //TODO should we also do this for target 0? - add_features(group_F, specs[groups[i].idx].cpu_name, - specs[groups[i].idx].cpu_features, specs[groups[i].idx].flags); + add_features(group_F, specs[groups[i].idx]); } group_F->addFnAttr("julia.mv.clone", std::to_string(i)); } + // Add features to the original function + add_features(F, specs[0]); } } @@ -919,6 +989,18 @@ static bool runMultiVersioning(Module &M, bool allow_bad_fvars) return false; } + // for opt testing purposes + bool annotated = !!M.getModuleFlag("julia.mv.annotated"); + if (!annotated) { + annotate_module_clones(M); + } + + // also for opt testing purposes + if (M.getModuleFlag("julia.mv.skipcloning")) { + assert(!annotated && "Multiversioning was enabled and annotations were added, but cloning was skipped!"); + return true; + } + GlobalVariable *fvars = M.getGlobalVariable("jl_fvars"); GlobalVariable *gvars = M.getGlobalVariable("jl_gvars"); if (allow_bad_fvars && (!fvars || !fvars->hasInitializer() || !isa(fvars->getInitializer()) || From 3dcd1a23ac16748daa6770d44bf0825fa3981767 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Wed, 1 Feb 2023 12:50:33 -0500 Subject: [PATCH 236/775] Add multiversioning annotation test --- .../multiversioning-annotate-only.ll | 217 ++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 test/llvmpasses/multiversioning-annotate-only.ll diff --git a/test/llvmpasses/multiversioning-annotate-only.ll b/test/llvmpasses/multiversioning-annotate-only.ll new file mode 100644 index 0000000000000..38af146c078f5 --- /dev/null +++ b/test/llvmpasses/multiversioning-annotate-only.ll @@ -0,0 +1,217 @@ +; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -JuliaMultiVersioning -S %s | FileCheck %s +; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='JuliaMultiVersioning' -S %s | FileCheck %s + +; COM: This test checks that multiversioning correctly picks up on features that should trigger cloning +; COM: Note that for annotations alone, we don't need jl_fvars or jl_gvars + +; COM: Copied from src/processor.h +; COM: JL_TARGET_VEC_CALL = 1 << 0, +; COM: // Clone all functions +; COM: JL_TARGET_CLONE_ALL = 1 << 1, +; COM: // Clone when there's scalar math operations that can benefit from target-specific +; COM: // optimizations. This includes `muladd`, `fma`, `fast`/`contract` flags. +; COM: JL_TARGET_CLONE_MATH = 1 << 2, +; COM: // Clone when the function has a loop +; COM: JL_TARGET_CLONE_LOOP = 1 << 3, +; COM: // Clone when the function uses any vectors +; COM: // When this is specified, the cloning pass should also record if any of the cloned functions +; COM: // used this in any function call (including the signature of the function itself) +; COM: JL_TARGET_CLONE_SIMD = 1 << 4, +; COM: // The CPU name is unknown +; COM: JL_TARGET_UNKNOWN_NAME = 1 << 5, +; COM: // Optimize for size for this target +; COM: JL_TARGET_OPTSIZE = 1 << 6, +; COM: // Only optimize for size for this target +; COM: JL_TARGET_MINSIZE = 1 << 7, +; COM: // Clone when the function queries CPU features +; COM: JL_TARGET_CLONE_CPU = 1 << 8, +; COM: // Clone when the function uses fp16 +; COM: JL_TARGET_CLONE_FLOAT16 = 1 << 9, + +; COM: start with the basics, just one feature per function + +; COM: boring should only be cloned if clone_all is enabled on the target +; CHECK: @boring{{.*}}#[[BORING_ATTRS:[0-9]+]] +define noundef i32 @boring(i32 noundef %0) { + ret i32 %0 +} + +; CHECK: @fastmath_test{{.*}}#[[FASTMATH_TEST_ATTRS:[0-9]+]] +define noundef float @fastmath_test(float noundef %0, float noundef %1) { + %3 = fadd fast float %0, %1 + ret float %3 +} + +; CHECK: @loop_test{{.*}}#[[LOOP_TEST_ATTRS:[0-9]+]] +define noundef i32 @loop_test(i32 noundef %0) { + %2 = icmp sgt i32 %0, 0 + br i1 %2, label %5, label %3 + +3: ; preds = %5, %1 + %4 = phi i32 [ 0, %1 ], [ %9, %5 ] + ret i32 %4 + +5: ; preds = %1, %5 + %6 = phi i32 [ %10, %5 ], [ 0, %1 ] + %7 = phi i32 [ %9, %5 ], [ 0, %1 ] + %8 = lshr i32 %6, 1 + %9 = add nuw nsw i32 %8, %7 + %10 = add nuw nsw i32 %6, 1 + %11 = icmp eq i32 %10, %0 + br i1 %11, label %3, label %5, !llvm.loop !9 +} + +; CHECK: @simd_test{{.*}}#[[SIMD_TEST_ATTRS:[0-9]+]] +define noundef i32 @simd_test(<4 x i32> noundef %0) { + %2 = extractelement <4 x i32> %0, i64 0 + ret i32 %2 +} + +; COM: now check all the combinations + +; CHECK: @simd_fastmath_test{{.*}}#[[SIMD_FASTMATH_TEST_ATTRS:[0-9]+]] +define noundef float @simd_fastmath_test(<4 x float> noundef %0) { + %2 = extractelement <4 x float> %0, i64 0 + %3 = extractelement <4 x float> %0, i64 1 + %4 = fadd fast float %2, %3 + ret float %4 +} + +; CHECK: @loop_fastmath_test{{.*}}#[[LOOP_FASTMATH_TEST_ATTRS:[0-9]+]] +define noundef i32 @loop_fastmath_test(i32 noundef %0) { + %2 = icmp sgt i32 %0, 0 + br i1 %2, label %7, label %5 + +3: ; preds = %7 + %4 = fptosi float %12 to i32 + br label %5 + +5: ; preds = %3, %1 + %6 = phi i32 [ 0, %1 ], [ %4, %3 ] + ret i32 %6 + +7: ; preds = %1, %7 + %8 = phi i32 [ %13, %7 ], [ 0, %1 ] + %9 = phi float [ %12, %7 ], [ 0.000000e+00, %1 ] + %10 = lshr i32 %8, 1 + %11 = sitofp i32 %10 to float + %12 = fadd fast float %9, %11 + %13 = add nuw nsw i32 %8, 1 + %14 = icmp eq i32 %13, %0 + br i1 %14, label %3, label %7, !llvm.loop !9 +} + +; CHECK: @simd_loop_test{{.*}}#[[SIMD_LOOP_TEST_ATTRS:[0-9]+]] +define dso_local noundef i32 @simd_loop_test(<4 x i32> noundef %0) { + %2 = extractelement <4 x i32> %0, i64 0 + %3 = icmp sgt i32 %2, 0 + br i1 %3, label %6, label %4 + +4: ; preds = %6, %1 + %5 = phi i32 [ 0, %1 ], [ %10, %6 ] + ret i32 %5 + +6: ; preds = %1, %6 + %7 = phi i32 [ %11, %6 ], [ 0, %1 ] + %8 = phi i32 [ %10, %6 ], [ 0, %1 ] + %9 = lshr i32 %7, 1 + %10 = add nuw nsw i32 %9, %8 + %11 = add nuw nsw i32 %7, 1 + %12 = icmp eq i32 %11, %2 + br i1 %12, label %4, label %6, !llvm.loop !9 +} + +; CHECK: @simd_loop_fastmath_test{{.*}}#[[SIMD_LOOP_FASTMATH_TEST_ATTRS:[0-9]+]] +define noundef i32 @simd_loop_fastmath_test(<4 x i32> noundef %0) { + %2 = extractelement <4 x i32> %0, i64 0 + %3 = icmp sgt i32 %2, 0 + br i1 %3, label %8, label %6 + +4: ; preds = %8 + %5 = fptosi float %13 to i32 + br label %6 + +6: ; preds = %4, %1 + %7 = phi i32 [ 0, %1 ], [ %5, %4 ] + ret i32 %7 + +8: ; preds = %1, %8 + %9 = phi i32 [ %14, %8 ], [ 0, %1 ] + %10 = phi float [ %13, %8 ], [ 0.000000e+00, %1 ] + %11 = lshr i32 %9, 1 + %12 = sitofp i32 %11 to float + %13 = fadd fast float %10, %12 + %14 = add nuw nsw i32 %9, 1 + %15 = icmp eq i32 %14, %2 + br i1 %15, label %4, label %8, !llvm.loop !9 +} + +; COM: check for fvar and reloc annotations on functions used by other globals + +@func_gv = global i32 (i32)* @func_in_gv, align 8 + +; CHECK: @func_in_gv{{.*}}#[[FUNC_IN_GV_ATTRS:[0-9]+]] +define noundef i32 @func_in_gv(i32 noundef returned %0) { + ret i32 %0 +} + +@aliaser = alias i32 (i32)*, bitcast (i32 (i32)* @aliasee to i32 (i32)**) + +; CHECK: @aliasee{{.*}}#[[ALIASEE_ATTRS:[0-9]+]] +define i32 @aliasee(i32 noundef returned %0) { + ret i32 %0 +} + +; COM: check for reloc annotations on functions used by other functions +; CHECK: @cloned{{.*}}#[[CLONED_RELOC_ATTRS:[0-9]+]] +define noundef float @cloned(float noundef %0, float noundef %1) { + %3 = fadd fast float %0, %1 + ret float %3 +} + +define noundef i32 @uncloned(i32 noundef %0) { + %2 = sitofp i32 %0 to float + %3 = call noundef float @cloned(float noundef %2, float noundef %2) + %4 = fptosi float %3 to i32 + ret i32 %4 +} + +; COM: Note that these strings are hex-encoded bits of the target indices that will be cloned +; CHECK-DAG: attributes #[[BORING_ATTRS]] = { "julia.mv.clones"="2" } +; CHECK-DAG: attributes #[[FASTMATH_TEST_ATTRS]] = { "julia.mv.clones"="6" } +; CHECK-DAG: attributes #[[LOOP_TEST_ATTRS]] = { "julia.mv.clones"="A" } +; CHECK-DAG: attributes #[[SIMD_TEST_ATTRS]] = { "julia.mv.clones"="12" } +; CHECK-DAG: attributes #[[SIMD_FASTMATH_TEST_ATTRS]] = { "julia.mv.clones"="16" } +; CHECK-DAG: attributes #[[LOOP_FASTMATH_TEST_ATTRS]] = { "julia.mv.clones"="E" } +; CHECK-DAG: attributes #[[SIMD_LOOP_TEST_ATTRS]] = { "julia.mv.clones"="1A" } +; CHECK-DAG: attributes #[[SIMD_LOOP_FASTMATH_TEST_ATTRS]] = { "julia.mv.clones"="1E" } +; CHECK-DAG: attributes #[[FUNC_IN_GV_ATTRS]] +; CHECK-SAME: "julia.mv.clones"="2" +; CHECK-SAME: "julia.mv.fvar" +; CHECK-DAG: attributes #[[ALIASEE_ATTRS]] +; CHECK-SAME: "julia.mv.clones"="2" +; CHECK-SAME: "julia.mv.reloc" +; CHECK-DAG: attributes #[[CLONED_RELOC_ATTRS]] +; CHECK-SAME: "julia.mv.clones"="6" +; CHECK-SAME: "julia.mv.reloc" + +; CHECK-LABEL: !llvm.module.flags + +!llvm.module.flags = !{!0, !1, !2} + +; CHECK-DAG: julia.mv.enable +; CHECK-DAG: julia.mv.skipcloning +; CHECK-DAG: julia.mv.specs +; CHECK-DAG: julia.mv.annotated +; CHECK-DAG: julia.mv.veccall + +!0 = !{i32 1, !"julia.mv.enable", i32 1} +!1 = !{i32 1, !"julia.mv.skipcloning", i32 1} +!2 = !{i32 1, !"julia.mv.specs", !3} +!3 = !{!4, !5, !6, !7, !8} +!4 = !{!"cpubase", !"nofeatures", i32 0, i32 2} +!5 = !{!"cpucloneall", !"cloneall", i32 0, i32 2} +!6 = !{!"cpufastmath", !"fastmathclone", i32 0, i32 4} +!7 = !{!"cpuloop", !"loopclone", i32 0, i32 8} +!8 = !{!"cpusimd", !"simdclone", i32 0, i32 16} +!9 = !{!9} From b3d3ffbc3384451819aa9d1886f9c7230969411e Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Thu, 2 Feb 2023 10:48:52 -0500 Subject: [PATCH 237/775] Couple more tests for multiversioning --- test/llvmpasses/multiversioning-clone-only.ll | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 test/llvmpasses/multiversioning-clone-only.ll diff --git a/test/llvmpasses/multiversioning-clone-only.ll b/test/llvmpasses/multiversioning-clone-only.ll new file mode 100644 index 0000000000000..61bcdb8613306 --- /dev/null +++ b/test/llvmpasses/multiversioning-clone-only.ll @@ -0,0 +1,50 @@ +; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -JuliaMultiVersioning -S %s | FileCheck %s --allow-unused-prefixes=false +; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='JuliaMultiVersioning' -S %s | FileCheck %s --allow-unused-prefixes=false + +@jl_fvars = global [0 x i64] zeroinitializer, align 16 +@jl_gvars = global [0 x i64] zeroinitializer, align 16 +@jl_fvar_idxs = global [0 x i32] zeroinitializer, align 16 +@jl_gvar_idxs = global [0 x i32] zeroinitializer, align 16 + +; CHECK-DAG: define{{.*}}@boring({{.*}}#[[BORING_DEFAULT_ATTRS:[0-9]+]] +; CHECK-DAG-NEXT: ret i32 %0 +; CHECK-DAG: define{{.*}}@boring.1({{.*}}#[[BORING_CLONEALL_ATTRS:[0-9]+]] +; CHECK-DAG-NEXT: ret i32 %0 +define noundef i32 @boring(i32 noundef %0) #0 { + ret i32 %0 +} + +; CHECK-DAG: declare{{.*}}@declaration({{.*}}#[[DECLARATION_DEFAULT_ATTRS:[0-9]+]] +; CHECK-DAG: declare{{.*}}@declaration.1({{.*}}#[[DECLARATION_CLONEALL_ATTRS:[0-9]+]] +declare i32 @declaration(i32 %0) #1 + +; CHECK: } + +; CHECK-DAG: attributes #[[BORING_DEFAULT_ATTRS:[0-9]+]] +; CHECK-SAME: { +; CHECK-DAG: "julia.mv.clones"="2" +; CHECK-DAG: "julia.mv.clone"="0" +; CHECK-DAG: "target-cpu"="cpubase" +; CHECK-DAG: "target-features"="nofeatures" +; CHECK-SAME: } +; CHECK-DAG: attributes #[[BORING_CLONEALL_ATTRS:[0-9]+]] +; CHECK-SAME: { +; CHECK-DAG: "julia.mv.clones"="2" +; CHECK-DAG: "julia.mv.clone"="1" +; CHECK-DAG: "target-cpu"="cpucloneall" +; CHECK-DAG: "target-features"="cloneall" +; CHECK-SAME: } +attributes #0 = {"julia.mv.clones"="2"} +attributes #1 = {"julia.mv.clones"="2" "test.unique"="1"} + +!llvm.module.flags = !{!0, !1, !2} + +!0 = !{i32 1, !"julia.mv.enable", i32 1} +!1 = !{i32 1, !"julia.mv.annotated", i32 1} +!2 = !{i32 1, !"julia.mv.specs", !3} +!3 = !{!4, !5, !6, !7, !8} +!4 = !{!"cpubase", !"nofeatures", i32 0, i32 2} +!5 = !{!"cpucloneall", !"cloneall", i32 0, i32 2} +!6 = !{!"cpufastmath", !"fastmathclone", i32 0, i32 4} +!7 = !{!"cpuloop", !"loopclone", i32 0, i32 8} +!8 = !{!"cpusimd", !"simdclone", i32 0, i32 16} \ No newline at end of file From e75e362dc936b2bf98028e887ec075f50c928c6b Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Thu, 2 Feb 2023 11:01:51 -0500 Subject: [PATCH 238/775] Inject CRT aliases with internal linkage within every shard --- src/aotcompile.cpp | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index fffc7839d74c9..5e8618d637b3e 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -485,8 +485,7 @@ static void injectCRTAlias(Module &M, StringRef name, StringRef alias, FunctionT if (!target) { target = Function::Create(FT, Function::ExternalLinkage, alias, M); } - Function *interposer = Function::Create(FT, Function::ExternalLinkage, name, M); - interposer->setVisibility(GlobalValue::HiddenVisibility); + Function *interposer = Function::Create(FT, Function::InternalLinkage, name, M); appendToCompilerUsed(M, {interposer}); llvm::IRBuilder<> builder(BasicBlock::Create(M.getContext(), "top", interposer)); @@ -891,6 +890,30 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out #endif optimizer.run(M); assert(!verifyModule(M, &errs())); + bool inject_aliases = false; + for (auto &F : M.functions()) { + if (!F.isDeclaration() && F.getName() != "_DllMainCRTStartup") { + inject_aliases = true; + break; + } + } + // no need to inject aliases if we have no functions + if (inject_aliases) { + // We would like to emit an alias or an weakref alias to redirect these symbols + // but LLVM doesn't let us emit a GlobalAlias to a declaration... + // So for now we inject a definition of these functions that calls our runtime + // functions. We do so after optimization to avoid cloning these functions. + injectCRTAlias(M, "__gnu_h2f_ieee", "julia__gnu_h2f_ieee", + FunctionType::get(Type::getFloatTy(M.getContext()), { Type::getHalfTy(M.getContext()) }, false)); + injectCRTAlias(M, "__extendhfsf2", "julia__gnu_h2f_ieee", + FunctionType::get(Type::getFloatTy(M.getContext()), { Type::getHalfTy(M.getContext()) }, false)); + injectCRTAlias(M, "__gnu_f2h_ieee", "julia__gnu_f2h_ieee", + FunctionType::get(Type::getHalfTy(M.getContext()), { Type::getFloatTy(M.getContext()) }, false)); + injectCRTAlias(M, "__truncsfhf2", "julia__gnu_f2h_ieee", + FunctionType::get(Type::getHalfTy(M.getContext()), { Type::getFloatTy(M.getContext()) }, false)); + injectCRTAlias(M, "__truncdfhf2", "julia__truncdfhf2", + FunctionType::get(Type::getHalfTy(M.getContext()), { Type::getDoubleTy(M.getContext()) }, false)); + } timers.optimize.stopTimer(); @@ -1440,23 +1463,6 @@ void jl_dump_native_impl(void *native_code, sysimageM->setOverrideStackAlignment(dataM->getOverrideStackAlignment()); #endif - if (!TheTriple.isOSDarwin()) { - // We would like to emit an alias or an weakref alias to redirect these symbols - // but LLVM doesn't let us emit a GlobalAlias to a declaration... - // So for now we inject a definition of these functions that calls our runtime - // functions. We do so after optimization to avoid cloning these functions. - injectCRTAlias(*sysimageM, "__gnu_h2f_ieee", "julia__gnu_h2f_ieee", - FunctionType::get(Type::getFloatTy(Context), { Type::getHalfTy(Context) }, false)); - injectCRTAlias(*sysimageM, "__extendhfsf2", "julia__gnu_h2f_ieee", - FunctionType::get(Type::getFloatTy(Context), { Type::getHalfTy(Context) }, false)); - injectCRTAlias(*sysimageM, "__gnu_f2h_ieee", "julia__gnu_f2h_ieee", - FunctionType::get(Type::getHalfTy(Context), { Type::getFloatTy(Context) }, false)); - injectCRTAlias(*sysimageM, "__truncsfhf2", "julia__gnu_f2h_ieee", - FunctionType::get(Type::getHalfTy(Context), { Type::getFloatTy(Context) }, false)); - injectCRTAlias(*sysimageM, "__truncdfhf2", "julia__truncdfhf2", - FunctionType::get(Type::getHalfTy(Context), { Type::getDoubleTy(Context) }, false)); - } - if (TheTriple.isOSWindows()) { // Windows expect that the function `_DllMainStartup` is present in an dll. // Normal compilers use something like Zig's crtdll.c instead we provide a From 65e6de2a6265243cb750b3551d5ac86029e7ffad Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Fri, 3 Feb 2023 00:58:25 -0500 Subject: [PATCH 239/775] Expand on the multiversioning tests --- src/llvm-multiversioning.cpp | 42 +++- test/llvmpasses/multiversioning-clone-only.ll | 193 ++++++++++++++++-- 2 files changed, 216 insertions(+), 19 deletions(-) diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index b4f67ebe22c7d..6e9bbe85aa7f6 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -364,7 +364,9 @@ struct CloneCtx { void clone_decls(); void clone_bodies(); void fix_gv_uses(); + void finalize_orig_clone_attr(); void fix_inst_uses(); + void finalize_orig_features(); void emit_metadata(); private: void prepare_vmap(ValueToValueMapTy &vmap); @@ -399,6 +401,8 @@ static inline std::vector consume_gv(Module &M, const char *name, bool allow // Strip them from the Module so that it's easier to handle the uses. GlobalVariable *gv = M.getGlobalVariable(name); assert(gv && gv->hasInitializer()); + dbgs() << "Consume " << *gv << ":\n"; + dbgs() << *gv->getType() << "\n"; ArrayType *Ty = cast(gv->getInitializer()->getType()); unsigned nele = Ty->getArrayNumElements(); std::vector res(nele); @@ -417,6 +421,7 @@ static inline std::vector consume_gv(Module &M, const char *name, bool allow nele--; continue; } + dbgs() << *val << ": " << *val->getType() << "\n"; res[i++] = cast(val); } res.resize(nele); @@ -584,18 +589,20 @@ void CloneCtx::clone_bodies() clone_function(group_F, target_F, *target.vmap); } add_features(target_F, specs[target.idx]); - target_F->addFnAttr("julia.mv.clone", std::to_string(i)); + target_F->addFnAttr("julia.mv.clone", std::to_string(target.idx)); } } // don't set the original function's features yet, // since we may clone it for later groups if (i != 0) { add_features(group_F, specs[groups[i].idx]); + group_F->addFnAttr("julia.mv.clone", std::to_string(groups[i].idx)); } - group_F->addFnAttr("julia.mv.clone", std::to_string(i)); } - // Add features to the original function - add_features(F, specs[0]); + // still don't set the original function's features yet, + // since we'll copy function attributes if we need to rewrite + // the alias, and target specific attributes are illegal on + // alias trampolines unless the user explicitly specifies them } } @@ -658,6 +665,11 @@ void CloneCtx::rewrite_alias(GlobalAlias *alias, Function *F) Function::Create(F->getFunctionType(), alias->getLinkage(), "", &M); trampoline->copyAttributesFrom(F); trampoline->takeName(alias); + trampoline->setVisibility(alias->getVisibility()); + // drop multiversioning attributes, add alias attribute for testing purposes + trampoline->removeFnAttr("julia.mv.reloc"); + trampoline->removeFnAttr("julia.mv.clones"); + trampoline->addFnAttr("julia.mv.alias"); alias->eraseFromParent(); uint32_t id; @@ -727,6 +739,15 @@ void CloneCtx::fix_gv_uses() } } +void CloneCtx::finalize_orig_clone_attr() +{ + for (auto orig_f: orig_funcs) { + if (!orig_f->hasFnAttribute("julia.mv.clones")) + continue; + orig_f->addFnAttr("julia.mv.clone", "0"); + } +} + std::pair CloneCtx::get_reloc_slot(Function *F) const { if (F->isDeclaration()) { @@ -814,6 +835,12 @@ void CloneCtx::fix_inst_uses() } } +void CloneCtx::finalize_orig_features() { + for (auto F : orig_funcs) { + add_features(F, specs[0]); + } +} + static Constant *get_ptrdiff32(Constant *ptr, Constant *base) { if (ptr->getType()->isPointerTy()) @@ -1021,6 +1048,10 @@ static bool runMultiVersioning(Module &M, bool allow_bad_fvars) // These relocations must be initialized for **ALL** targets. clone.fix_gv_uses(); + // Now we have all the cloned functions, we can set the original functions' + // clone attribute to be 0 + clone.finalize_orig_clone_attr(); + // For each group, scan all functions cloned by **PARTIALLY** cloned targets for // instruction use. // A function needs a const relocation slot if it is cloned and is called by a @@ -1031,6 +1062,9 @@ static bool runMultiVersioning(Module &M, bool allow_bad_fvars) // A target needs a slot to be initialized iff at least one caller is not initialized. clone.fix_inst_uses(); + //Now set the original functions' target-specific attributes, since nobody will look at those again + clone.finalize_orig_features(); + // Store back sysimg information with the correct format. // At this point, we should have fixed up all the uses of the cloned functions // and collected all the shared/target-specific relocations. diff --git a/test/llvmpasses/multiversioning-clone-only.ll b/test/llvmpasses/multiversioning-clone-only.ll index 61bcdb8613306..a5c327548d702 100644 --- a/test/llvmpasses/multiversioning-clone-only.ll +++ b/test/llvmpasses/multiversioning-clone-only.ll @@ -1,41 +1,202 @@ ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -JuliaMultiVersioning -S %s | FileCheck %s --allow-unused-prefixes=false ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='JuliaMultiVersioning' -S %s | FileCheck %s --allow-unused-prefixes=false -@jl_fvars = global [0 x i64] zeroinitializer, align 16 -@jl_gvars = global [0 x i64] zeroinitializer, align 16 -@jl_fvar_idxs = global [0 x i32] zeroinitializer, align 16 -@jl_gvar_idxs = global [0 x i32] zeroinitializer, align 16 - -; CHECK-DAG: define{{.*}}@boring({{.*}}#[[BORING_DEFAULT_ATTRS:[0-9]+]] -; CHECK-DAG-NEXT: ret i32 %0 -; CHECK-DAG: define{{.*}}@boring.1({{.*}}#[[BORING_CLONEALL_ATTRS:[0-9]+]] -; CHECK-DAG-NEXT: ret i32 %0 +; CHECK: @jl_fvar_idxs = hidden constant [1 x i32] zeroinitializer +; CHECK: @jl_gvar_idxs = hidden constant [0 x i32] zeroinitializer +; CHECK: @subtarget_cloned_gv = hidden global i64* null +; CHECK: @subtarget_cloned.reloc_slot = hidden global i32 (i32)* null +; CHECK: @jl_fvar_offsets = hidden constant [2 x i32] [i32 1, i32 0] +; CHECK: @jl_gvar_base = hidden constant i64 0 +; CHECK: @jl_gvar_offsets = hidden constant [1 x i32] zeroinitializer +; CHECK: @jl_clone_slots = hidden constant [5 x i32] +; CHECK-SAME: i32 2, i32 0, {{.*}} sub {{.*}}@subtarget_cloned.reloc_slot{{.*}}@jl_gvar_base +; CHECK: @jl_clone_idxs = hidden constant [13 x i32] +; COM: TODO actually check the clone idxs maybe? +; CHECK: @jl_clone_offsets = hidden constant [4 x i32] +; CHECK-SAME: sub +; CHECK-SAME: @subtarget_cloned.1 +; CHECK-SAME: @subtarget_cloned +; CHECK-SAME: sub +; CHECK-SAME: @subtarget_cloned.2 +; CHECK-SAME: @subtarget_cloned +; CHECK-SAME: sub + +@jl_fvars = global [1 x i64*] [i64* bitcast (i32 (i32)* @subtarget_cloned to i64*)], align 16 +@jl_gvars = global [0 x i64*] zeroinitializer, align 16 +@jl_fvar_idxs = hidden constant [1 x i32] [i32 0], align 16 +@jl_gvar_idxs = hidden constant [0 x i32] zeroinitializer, align 16 +@subtarget_cloned_gv = hidden global i64* bitcast (i32 (i32)* @subtarget_cloned to i64*), align 16 + +@subtarget_cloned_aliased = alias i32 (i32), i32 (i32)* @subtarget_cloned + +; CHECK: define{{.*}}@boring({{.*}}#[[BORING_DEFAULT_ATTRS:[0-9]+]] +; CHECK-NEXT: ret i32 %0 define noundef i32 @boring(i32 noundef %0) #0 { ret i32 %0 } -; CHECK-DAG: declare{{.*}}@declaration({{.*}}#[[DECLARATION_DEFAULT_ATTRS:[0-9]+]] -; CHECK-DAG: declare{{.*}}@declaration.1({{.*}}#[[DECLARATION_CLONEALL_ATTRS:[0-9]+]] +; CHECK: declare{{.*}}@declaration({{.*}}#[[DECLARATION_DEFAULT_ATTRS:[0-9]+]] declare i32 @declaration(i32 %0) #1 -; CHECK: } +; CHECK: define{{.*}}@call_boring({{.*}}#[[BORING_DEFAULT_ATTRS]] +; CHECK-NEXT: %2 = call noundef i32 @boring(i32 noundef %0) +define noundef i32 @call_boring(i32 noundef %0) #0 { + %2 = call noundef i32 @boring(i32 noundef %0) + ret i32 %2 +} + +; CHECK: define{{.*}}@call_declaration({{.*}}#[[DECLARATION_DEFAULT_ATTRS]] +; CHECK-NEXT: %2 = call noundef i32 @declaration(i32 noundef %0) +define noundef i32 @call_declaration(i32 noundef %0) #1 { + %2 = call noundef i32 @declaration(i32 noundef %0) + ret i32 %2 +} + +; CHECK: define{{.*}}@subtarget_cloned({{.*}}#[[SUBTARGET_CLONED_DEFAULT_ATTRS:[0-9]+]] +; CHECK-NEXT: ret i32 0 +define noundef i32 @subtarget_cloned(i32 noundef %0) #2 { + ret i32 0 +} + +; COM: should fixup this callsite since 2 is cloned for a subtarget +; CHECK: define{{.*}}@call_subtarget_cloned({{.*}}#[[CALL_SUBTARGET_CLONED_DEFAULT_ATTRS:[0-9]+]] +; CHECK-NEXT: [[FUNC_PTR:%[0-9]+]] = load{{.*}}@subtarget_cloned.reloc_slot{{.*}}!tbaa ![[TBAA_CONST_METADATA:[0-9]+]], !invariant.load +; CHECK-NEXT: call{{.*}}[[FUNC_PTR]] +; CHECK: ret i32 +define noundef i32 @call_subtarget_cloned(i32 noundef %0) #3 { + %2 = call noundef i32 @subtarget_cloned(i32 noundef %0) + ret i32 %2 +} + +; CHECK: define{{.*}}@call_subtarget_cloned_but_not_cloned({{.*}}#[[BORING_DEFAULT_ATTRS]] +; CHECK-NEXT: [[FUNC_PTR:%[0-9]+]] = load{{.*}}@subtarget_cloned.reloc_slot{{.*}}!tbaa ![[TBAA_CONST_METADATA]], !invariant.load +; CHECK-NEXT: call{{.*}}[[FUNC_PTR]] +; CHECK: ret i32 +define noundef i32 @call_subtarget_cloned_but_not_cloned(i32 noundef %0) #0 { + %2 = call noundef i32 @subtarget_cloned(i32 noundef %0) + ret i32 %2 +} + +; CHECK: define{{.*}}@boring.1({{.*}}#[[BORING_CLONEALL_ATTRS:[0-9]+]] +; CHECK-NEXT: ret i32 %0 + +; CHECK: declare{{.*}}@declaration.1({{.*}}#[[DECLARATION_CLONEALL_ATTRS:[0-9]+]] + +; COM: should not fixup this callsite since boring is not cloned for a subtarget +; COM: also should call boring.1 instead of boring +; CHECK: define{{.*}}@call_boring.1({{.*}}#[[BORING_CLONEALL_ATTRS]] +; CHECK-NEXT: %2 = call noundef i32 @boring.1(i32 noundef %0) + +; CHECK: define{{.*}}@call_declaration.1({{.*}}#[[DECLARATION_CLONEALL_ATTRS]] +; CHECK-NEXT: %2 = call noundef i32 @declaration.1(i32 noundef %0) -; CHECK-DAG: attributes #[[BORING_DEFAULT_ATTRS:[0-9]+]] +; CHECK: define{{.*}}@subtarget_cloned.1({{.*}}#[[SUBTARGET_CLONED_CLONEALL_ATTRS:[0-9]+]] +; CHECK-NEXT: ret i32 0 + +; CHECK: define{{.*}}@subtarget_cloned.2({{.*}}#[[SUBTARGET_CLONED_FASTMATH_ATTRS:[0-9]+]] +; CHECK-NEXT: ret i32 0 + +; COM: should *NOT* fixup this callsite since subtarget_cloned is not cloned for a subtarget of the cloneall +; CHECK: define{{.*}}@call_subtarget_cloned.1({{.*}}#[[CALL_SUBTARGET_CLONED_CLONEALL_ATTRS:[0-9]+]] +; CHECK-NEXT: %2 = call noundef i32 @subtarget_cloned.1(i32 noundef %0) + +; CHECK: define {{.*}}@call_subtarget_cloned.2({{.*}}#[[CALL_SUBTARGET_CLONED_FASTMATH_ATTRS:[0-9]+]] +; CHECK-NEXT: %2 = call noundef i32 @subtarget_cloned.2(i32 noundef %0) + +; CHECK: define{{.*}}@call_subtarget_cloned_but_not_cloned.1({{.*}}#[[BORING_CLONEALL_ATTRS]] +; CHECK-NEXT: %2 = call noundef i32 @subtarget_cloned.1(i32 noundef %0) + +; COM: should not have cloned for fastmath +; CHECK-NOT: @subtarget_cloned_but_not_cloned.2 + +; COM: check for alias being rewritten to a function trampoline +; CHECK: define{{.*}}@subtarget_cloned_aliased{{.*}}#[[SUBTARGET_ALIASED_ATTRS:[0-9]+]] +; CHECK-NOT: } +; CHECK: [[FUNC_PTR:%[0-9]+]] = load{{.*}}@subtarget_cloned.reloc_slot{{.*}}!tbaa ![[TBAA_CONST_METADATA]], !invariant.load +; CHECK-NEXT: call{{.*}}[[FUNC_PTR]] +; CHECK: ret i32 + +; CHECK: attributes #[[BORING_DEFAULT_ATTRS]] ; CHECK-SAME: { ; CHECK-DAG: "julia.mv.clones"="2" ; CHECK-DAG: "julia.mv.clone"="0" ; CHECK-DAG: "target-cpu"="cpubase" ; CHECK-DAG: "target-features"="nofeatures" ; CHECK-SAME: } -; CHECK-DAG: attributes #[[BORING_CLONEALL_ATTRS:[0-9]+]] +; CHECK: attributes #[[DECLARATION_DEFAULT_ATTRS]] ; CHECK-SAME: { ; CHECK-DAG: "julia.mv.clones"="2" +; CHECK-DAG: "julia.mv.clone"="0" +; CHECK-DAG: "target-cpu"="cpubase" +; CHECK-DAG: "target-features"="nofeatures" +; CHECK-SAME: } +; CHECK: attributes #[[SUBTARGET_CLONED_DEFAULT_ATTRS]] +; CHECK-SAME: { +; CHECK-DAG: "julia.mv.clones"="6" +; CHECK-DAG: "julia.mv.clone"="0" +; CHECK-DAG: "target-cpu"="cpubase" +; CHECK-DAG: "target-features"="nofeatures" +; CHECK-DAG: "julia.mv.reloc" +; CHECK-SAME: } +; CHECK: attributes #[[CALL_SUBTARGET_CLONED_DEFAULT_ATTRS]] +; CHECK-SAME: { +; CHECK-DAG: "julia.mv.clones"="6" +; CHECK-DAG: "julia.mv.clone"="0" +; CHECK-DAG: "target-cpu"="cpubase" +; CHECK-DAG: "target-features"="nofeatures" +; CHECK-SAME: } +; CHECK: attributes #[[BORING_CLONEALL_ATTRS]] +; CHECK-SAME: { +; CHECK-DAG: "julia.mv.clones"="2" +; CHECK-DAG: "julia.mv.clone"="1" +; CHECK-DAG: "target-cpu"="cpucloneall" +; CHECK-DAG: "target-features"="cloneall" +; CHECK-SAME: } +; CHECK: attributes #[[DECLARATION_CLONEALL_ATTRS]] +; CHECK-SAME: { +; CHECK-DAG: "julia.mv.clones"="2" +; CHECK-DAG: "julia.mv.clone"="1" +; CHECK-DAG: "target-cpu"="cpucloneall" +; CHECK-DAG: "target-features"="cloneall" +; CHECK-SAME: } +; CHECK: attributes #[[SUBTARGET_CLONED_CLONEALL_ATTRS]] +; CHECK-SAME: { +; CHECK-DAG: "julia.mv.clones"="6" ; CHECK-DAG: "julia.mv.clone"="1" ; CHECK-DAG: "target-cpu"="cpucloneall" ; CHECK-DAG: "target-features"="cloneall" +; CHECK-DAG: "julia.mv.reloc" +; CHECK-SAME: } +; CHECK: attributes #[[SUBTARGET_CLONED_FASTMATH_ATTRS]] +; CHECK-SAME: { +; CHECK-DAG: "julia.mv.clones"="6" +; CHECK-DAG: "julia.mv.clone"="2" +; CHECK-DAG: "target-cpu"="cpufastmath" +; CHECK-DAG: "target-features"="fastmathclone" +; CHECK-DAG: "julia.mv.reloc" +; CHECK-SAME: } +; CHECK: attributes #[[CALL_SUBTARGET_CLONED_CLONEALL_ATTRS]] +; CHECK-SAME: { +; CHECK-DAG: "julia.mv.clones"="6" +; CHECK-DAG: "julia.mv.clone"="1" +; CHECK-DAG: "target-cpu"="cpucloneall" +; CHECK-DAG: "target-features"="cloneall" +; CHECK-SAME: } +; CHECK: attributes #[[CALL_SUBTARGET_CLONED_FASTMATH_ATTRS]] +; CHECK-SAME: { +; CHECK-DAG: "julia.mv.clones"="6" +; CHECK-DAG: "julia.mv.clone"="2" +; CHECK-DAG: "target-cpu"="cpufastmath" +; CHECK-DAG: "target-features"="fastmathclone" +; CHECK-SAME: } +; CHECK: attributes #[[SUBTARGET_ALIASED_ATTRS]] +; CHECK-SAME: { +; CHECK-SAME: "julia.mv.alias" ; CHECK-SAME: } attributes #0 = {"julia.mv.clones"="2"} attributes #1 = {"julia.mv.clones"="2" "test.unique"="1"} +attributes #2 = {"julia.mv.clones"="6" "julia.mv.reloc"} +attributes #3 = {"julia.mv.clones"="6"} !llvm.module.flags = !{!0, !1, !2} @@ -47,4 +208,6 @@ attributes #1 = {"julia.mv.clones"="2" "test.unique"="1"} !5 = !{!"cpucloneall", !"cloneall", i32 0, i32 2} !6 = !{!"cpufastmath", !"fastmathclone", i32 0, i32 4} !7 = !{!"cpuloop", !"loopclone", i32 0, i32 8} -!8 = !{!"cpusimd", !"simdclone", i32 0, i32 16} \ No newline at end of file +!8 = !{!"cpusimd", !"simdclone", i32 0, i32 16} +; CHECK-DAG: ![[TBAA_CONST_METADATA]] = !{![[JTBAA_CONST_METADATA:[0-9]+]], ![[JTBAA_CONST_METADATA]] +; CHECK-DAG: ![[JTBAA_CONST_METADATA]] = !{!"jtbaa_const" From 556122393ab3762f6d19fc3f19c83739065b8c28 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Fri, 3 Feb 2023 03:42:59 -0500 Subject: [PATCH 240/775] Remove stray debug prints --- src/llvm-multiversioning.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 6e9bbe85aa7f6..cbce76d702119 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -401,8 +401,6 @@ static inline std::vector consume_gv(Module &M, const char *name, bool allow // Strip them from the Module so that it's easier to handle the uses. GlobalVariable *gv = M.getGlobalVariable(name); assert(gv && gv->hasInitializer()); - dbgs() << "Consume " << *gv << ":\n"; - dbgs() << *gv->getType() << "\n"; ArrayType *Ty = cast(gv->getInitializer()->getType()); unsigned nele = Ty->getArrayNumElements(); std::vector res(nele); @@ -421,7 +419,6 @@ static inline std::vector consume_gv(Module &M, const char *name, bool allow nele--; continue; } - dbgs() << *val << ": " << *val->getType() << "\n"; res[i++] = cast(val); } res.resize(nele); From fef319cf11394caf3526460758d4e57196bd2322 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Mon, 13 Feb 2023 13:42:27 -0500 Subject: [PATCH 241/775] Track gvar count --- src/processor.cpp | 1 + src/processor.h | 1 + src/staticdata.c | 1 + 3 files changed, 3 insertions(+) diff --git a/src/processor.cpp b/src/processor.cpp index 851cbec62560a..fec2b77102f55 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -773,6 +773,7 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) offsets[i] = gvars[i] - (const char *)res.gvars_base; } res.gvars_offsets = offsets; + res.ngvars = gvars.size(); } if (!clones.empty()) { diff --git a/src/processor.h b/src/processor.h index 73271290eff76..6445f221882ba 100644 --- a/src/processor.h +++ b/src/processor.h @@ -159,6 +159,7 @@ typedef struct { uint64_t base; uintptr_t *gvars_base; const int32_t *gvars_offsets; + uint32_t ngvars; jl_image_fptrs_t fptrs; } jl_image_t; diff --git a/src/staticdata.c b/src/staticdata.c index 94e93f4198b4c..d832cda995a94 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1901,6 +1901,7 @@ static void jl_update_all_gvars(jl_serializer_state *s, jl_image_t *image, uint3 reloc_t *gvars = (reloc_t*)&s->gvar_record->buf[0]; int gvar_link_index = 0; int external_fns_link_index = 0; + assert(l == image->ngvars); for (i = 0; i < l; i++) { uintptr_t offset = gvars[i]; uintptr_t v = 0; From acc54d9e93567de0077ba248206f1a02f4d7b0cd Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Fri, 17 Feb 2023 15:40:21 -0500 Subject: [PATCH 242/775] Add more assertions --- src/aotcompile.cpp | 51 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 5e8618d637b3e..f74c9f92d3093 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -354,10 +354,14 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm // process the globals array, before jl_merge_module destroys them std::vector gvars(params.globals.size()); data->jl_value_to_llvm.resize(params.globals.size()); + StringSet<> gvars_names; + DenseSet gvars_set; size_t idx = 0; for (auto &global : params.globals) { gvars[idx] = global.second->getName().str(); + assert(gvars_set.insert(global.second).second && "Duplicate gvar in params!"); + assert(gvars_names.insert(gvars[idx]).second && "Duplicate gvar name in params!"); data->jl_value_to_llvm[idx] = global.first; idx++; } @@ -374,7 +378,10 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm GlobalVariable *F = extern_fn.second; size_t idx = gvars.size() - offset; assert(idx >= 0); - data->jl_external_to_llvm.at(idx) = this_code; + assert(idx < data->jl_external_to_llvm.size()); + data->jl_external_to_llvm[idx] = this_code; + assert(gvars_set.insert(F).second && "Duplicate gvar in params!"); + assert(gvars_names.insert(F->getName()).second && "Duplicate gvar name in params!"); gvars.push_back(std::string(F->getName())); } @@ -575,12 +582,18 @@ static void get_fvars_gvars(Module &M, DenseMap &fvars, auto gvars_init = cast(gvars_gv->getInitializer()); for (unsigned i = 0; i < fvars_init->getNumOperands(); ++i) { auto gv = cast(fvars_init->getOperand(i)->stripPointerCasts()); + assert(gv && gv->hasName() && "fvar must be a named global"); + assert(!fvars.count(gv) && "Duplicate fvar"); fvars[gv] = i; } + assert(fvars.size() == fvars_init->getNumOperands()); for (unsigned i = 0; i < gvars_init->getNumOperands(); ++i) { auto gv = cast(gvars_init->getOperand(i)->stripPointerCasts()); + assert(gv && gv->hasName() && "gvar must be a named global"); + assert(!gvars.count(gv) && "Duplicate gvar"); gvars[gv] = i; } + assert(gvars.size() == gvars_init->getNumOperands()); fvars_gv->eraseFromParent(); gvars_gv->eraseFromParent(); fvars_idxs->eraseFromParent(); @@ -606,9 +619,11 @@ static size_t getFunctionWeight(const Function &F) } -static inline bool verify_partitioning(const SmallVectorImpl &partitions, const Module &M) { +static inline bool verify_partitioning(const SmallVectorImpl &partitions, const Module &M, size_t fvars_size, size_t gvars_size) { bool bad = false; -#ifdef JL_DEBUG_BUILD +#ifndef JL_NDEBUG + SmallVector fvars(fvars_size); + SmallVector gvars(gvars_size); StringMap GVNames; for (uint32_t i = 0; i < partitions.size(); i++) { for (auto &name : partitions[i].globals) { @@ -618,7 +633,21 @@ static inline bool verify_partitioning(const SmallVectorImpl &partiti } GVNames[name.getKey()] = i; } - dbgs() << "partition: " << i << " fvars: " << partitions[i].fvars.size() << " gvars: " << partitions[i].gvars.size() << "\n"; + for (auto &fvar : partitions[i].fvars) { + if (fvars[fvar.second] != 0) { + bad = true; + dbgs() << "Duplicate fvar " << fvar.first() << " in partitions " << i << " and " << fvars[fvar.second] - 1 << "\n"; + } + fvars[fvar.second] = i+1; + } + for (auto &gvar : partitions[i].gvars) { + if (gvars[gvar.second] != 0) { + bad = true; + dbgs() << "Duplicate gvar " << gvar.first() << " in partitions " << i << " and " << gvars[gvar.second] - 1 << "\n"; + } + gvars[gvar.second] = i+1; + } + // dbgs() << "partition: " << i << " fvars: " << partitions[i].fvars.size() << " gvars: " << partitions[i].gvars.size() << "\n"; } for (auto &GV : M.globals()) { if (GV.isDeclaration()) { @@ -637,6 +666,18 @@ static inline bool verify_partitioning(const SmallVectorImpl &partiti } } } + for (uint32_t i = 0; i < fvars_size; i++) { + if (fvars[i] == 0) { + bad = true; + dbgs() << "fvar " << i << " not in any partition\n"; + } + } + for (uint32_t i = 0; i < gvars_size; i++) { + if (gvars[i] == 0) { + bad = true; + dbgs() << "gvar " << i << " not in any partition\n"; + } + } #endif return !bad; } @@ -766,7 +807,7 @@ static SmallVector partitionModule(Module &M, unsigned threads) { } } - bool verified = verify_partitioning(partitions, M); + bool verified = verify_partitioning(partitions, M, fvars.size(), gvars.size()); assert(verified && "Partitioning failed to partition globals correctly"); (void) verified; From 5c3a2e2644a26a7804f4e356e36599903671a3dc Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 3 Mar 2023 18:07:46 -0500 Subject: [PATCH 243/775] hoist "obvious" fastpaths into morespecific (#48847) This helps avoid allocations of the unionall wrappers, by checking for "obvious" subtyping conflicts before allocating them (instead of afterwards). --- src/subtype.c | 212 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 156 insertions(+), 56 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index a0f1785271b6e..dfb90df06074f 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -3894,39 +3894,139 @@ int jl_subtype_matching(jl_value_t *a, jl_value_t *b, jl_svec_t **penv) // specificity comparison -static int eq_msp(jl_value_t *a, jl_value_t *b, jl_typeenv_t *env) +static int eq_msp(jl_value_t *a, jl_value_t *b, jl_value_t *a0, jl_value_t *b0, jl_typeenv_t *env) { if (!(jl_is_type(a) || jl_is_typevar(a)) || !(jl_is_type(b) || jl_is_typevar(b))) return jl_egal(a, b); + if (a == b) // assume the TypeVar env is the same?? + return 1; + if (jl_typeof(a) == jl_typeof(b) && jl_types_egal(a, b)) + return 1; + if (obviously_unequal(a, b)) + return 0; + // the following is an interleaved version of: + // return jl_type_equal(a, b) + // where we try to do the fast checks before the expensive ones + if (jl_is_datatype(a) && !jl_is_concrete_type(b)) { + // if one type looks simpler, check it on the right + // first in order to reject more quickly. + jl_value_t *temp = a; + a = b; + b = temp; + } + // first check if a <: b has an obvious answer + int subtype_ab = 2; + if (b == (jl_value_t*)jl_any_type || a == jl_bottom_type) { + subtype_ab = 1; + } + else if (obvious_subtype(a, b, b0, &subtype_ab)) { +#ifdef NDEBUG + if (subtype_ab == 0) + return 0; +#endif + } + else { + subtype_ab = 3; + } + // next check if b <: a has an obvious answer + int subtype_ba = 2; + if (a == (jl_value_t*)jl_any_type || b == jl_bottom_type) { + subtype_ba = 1; + } + else if (obvious_subtype(b, a, a0, &subtype_ba)) { +#ifdef NDEBUG + if (subtype_ba == 0) + return 0; +#endif + } + else { + subtype_ba = 3; + } + // finally, do full subtyping for any inconclusive test JL_GC_PUSH2(&a, &b); - jl_typeenv_t *e = env; - while (e != NULL) { - a = jl_type_unionall(e->var, a); - b = jl_type_unionall(e->var, b); - e = e->prev; + jl_typeenv_t *env2 = env; + while (env2 != NULL) { + a = jl_type_unionall(env2->var, a); + b = jl_type_unionall(env2->var, b); + env2 = env2->prev; + } + jl_stenv_t e; +#ifdef NDEBUG + if (subtype_ab != 1) +#endif + { + init_stenv(&e, NULL, 0); + int subtype = forall_exists_subtype(a, b, &e, 0); + assert(subtype_ab == 3 || subtype_ab == subtype || jl_has_free_typevars(a) || jl_has_free_typevars(b)); +#ifndef NDEBUG + if (subtype_ab != 0 && subtype_ab != 1) // ensures that running in a debugger doesn't change the result +#endif + subtype_ab = subtype; +#ifdef NDEBUG + if (subtype_ab == 0) { + JL_GC_POP(); + return 0; + } +#endif + } +#ifdef NDEBUG + if (subtype_ba != 1) +#endif + { + init_stenv(&e, NULL, 0); + int subtype = forall_exists_subtype(b, a, &e, 0); + assert(subtype_ba == 3 || subtype_ba == subtype || jl_has_free_typevars(a) || jl_has_free_typevars(b)); +#ifndef NDEBUG + if (subtype_ba != 0 && subtype_ba != 1) // ensures that running in a debugger doesn't change the result +#endif + subtype_ba = subtype; } - int eq = jl_types_equal(a, b); JL_GC_POP(); - return eq; + // all tests successful + return subtype_ab && subtype_ba; } -static int sub_msp(jl_value_t *a, jl_value_t *b, jl_typeenv_t *env) +static int sub_msp(jl_value_t *x, jl_value_t *y, jl_value_t *y0, jl_typeenv_t *env) { - JL_GC_PUSH2(&a, &b); + jl_stenv_t e; + if (y == (jl_value_t*)jl_any_type || x == jl_bottom_type) + return 1; + if (x == y || + (jl_typeof(x) == jl_typeof(y) && + (jl_is_unionall(y) || jl_is_uniontype(y)) && + jl_types_egal(x, y))) { + return 1; + } + int obvious_sub = 2; + if (obvious_subtype(x, y, y0, &obvious_sub)) { +#ifdef NDEBUG + return obvious_sub; +#endif + } + else { + obvious_sub = 3; + } + JL_GC_PUSH2(&x, &y); while (env != NULL) { - if (jl_is_type(a) || jl_is_typevar(a)) - a = jl_type_unionall(env->var, a); - if (jl_is_type(b) || jl_is_typevar(b)) - b = jl_type_unionall(env->var, b); + if (jl_is_type(x) || jl_is_typevar(x)) + x = jl_type_unionall(env->var, x); + if (jl_is_type(y) || jl_is_typevar(y)) + y = jl_type_unionall(env->var, y); env = env->prev; } - int sub = jl_subtype(a, b); + init_stenv(&e, NULL, 0); + int subtype = forall_exists_subtype(x, y, &e, 0); + assert(obvious_sub == 3 || obvious_sub == subtype || jl_has_free_typevars(x) || jl_has_free_typevars(y)); +#ifndef NDEBUG + if (obvious_sub == 0 || obvious_sub == 1) + subtype = obvious_sub; // this ensures that running in a debugger doesn't change the result +#endif JL_GC_POP(); - return sub; + return subtype; } -static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_typeenv_t *env); +static int type_morespecific_(jl_value_t *a, jl_value_t *b, jl_value_t *a0, jl_value_t *b0, int invariant, jl_typeenv_t *env); static int num_occurs(jl_tvar_t *v, jl_typeenv_t *env); @@ -3949,7 +4049,7 @@ static jl_value_t *nth_tuple_elt(jl_datatype_t *t JL_PROPAGATES_ROOT, size_t i) return NULL; } -static int tuple_morespecific(jl_datatype_t *cdt, jl_datatype_t *pdt, int invariant, jl_typeenv_t *env) +static int tuple_morespecific(jl_datatype_t *cdt, jl_datatype_t *pdt, jl_value_t *c0, jl_value_t *p0, int invariant, jl_typeenv_t *env) { size_t plen = jl_nparams(pdt); if (plen == 0) return 0; @@ -3979,8 +4079,8 @@ static int tuple_morespecific(jl_datatype_t *cdt, jl_datatype_t *pdt, int invari break; } - if (type_morespecific_(pe, ce, invariant, env)) { - assert(!type_morespecific_(ce, pe, invariant, env)); + if (type_morespecific_(pe, ce, p0, c0, invariant, env)) { + assert(!type_morespecific_(ce, pe, c0, p0, invariant, env)); return 0; } @@ -3993,9 +4093,9 @@ static int tuple_morespecific(jl_datatype_t *cdt, jl_datatype_t *pdt, int invari if (cva && pva && i >= clen-1 && i >= plen-1 && (some_morespecific || (cdiag && !pdiag))) return 1; - int cms = type_morespecific_(ce, pe, invariant, env); + int cms = type_morespecific_(ce, pe, c0, p0, invariant, env); - if (!cms && !sub_msp(ce, pe, env)) { + if (!cms && !sub_msp(ce, pe, p0, env)) { /* A bound vararg tuple can be more specific despite disjoint elements in order to preserve transitivity. For example in @@ -4008,7 +4108,7 @@ static int tuple_morespecific(jl_datatype_t *cdt, jl_datatype_t *pdt, int invari } // Tuple{..., T} not more specific than Tuple{..., Vararg{S}} if S is diagonal - if (!cms && i == clen-1 && clen == plen && !cva && pva && eq_msp(ce, pe, env) && + if (!cms && i == clen-1 && clen == plen && !cva && pva && eq_msp(ce, pe, c0, p0, env) && jl_is_typevar(ce) && jl_is_typevar(pe) && !cdiag && pdiag) return 0; @@ -4037,7 +4137,7 @@ static size_t tuple_full_length(jl_value_t *t) // Called when a is a bound-vararg and b is not a vararg. Sets the vararg length // in a to match b, as long as this makes some earlier argument more specific. -static int args_morespecific_fix1(jl_value_t *a, jl_value_t *b, int swap, jl_typeenv_t *env) +static int args_morespecific_fix1(jl_value_t *a, jl_value_t *b, jl_value_t *a0, jl_value_t *b0, int swap, jl_typeenv_t *env) { size_t n = jl_nparams(a); int taillen = tuple_full_length(b)-n+1; @@ -4057,12 +4157,12 @@ static int args_morespecific_fix1(jl_value_t *a, jl_value_t *b, int swap, jl_typ } int ret = -1; if (changed) { - if (eq_msp(b, (jl_value_t*)new_a, env)) + if (eq_msp(b, (jl_value_t*)new_a, b0, a0, env)) ret = swap; else if (swap) - ret = type_morespecific_(b, (jl_value_t*)new_a, 0, env); + ret = type_morespecific_(b, (jl_value_t*)new_a, b0, a0, 0, env); else - ret = type_morespecific_((jl_value_t*)new_a, b, 0, env); + ret = type_morespecific_((jl_value_t*)new_a, b, a0, b0, 0, env); } JL_GC_POP(); return ret; @@ -4111,15 +4211,15 @@ static int num_occurs(jl_tvar_t *v, jl_typeenv_t *env) jl_unionall_t *ua = (jl_unionall_t*)a; \ jl_typeenv_t newenv = { ua->var, 0x0, env }; \ newenv.val = (jl_value_t*)(intptr_t)count_occurs(ua->body, ua->var); \ - return type_morespecific_(ua->body, b, invariant, &newenv) + return type_morespecific_(ua->body, b, a0, b0, invariant, &newenv) #define HANDLE_UNIONALL_B \ jl_unionall_t *ub = (jl_unionall_t*)b; \ jl_typeenv_t newenv = { ub->var, 0x0, env }; \ newenv.val = (jl_value_t*)(intptr_t)count_occurs(ub->body, ub->var); \ - return type_morespecific_(a, ub->body, invariant, &newenv) + return type_morespecific_(a, ub->body, a0, b0, invariant, &newenv) -static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_typeenv_t *env) +static int type_morespecific_(jl_value_t *a, jl_value_t *b, jl_value_t *a0, jl_value_t *b0, int invariant, jl_typeenv_t *env) { if (a == b) return 0; @@ -4131,14 +4231,14 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_ty jl_vararg_kind_t bkind = jl_va_tuple_kind((jl_datatype_t*)b); int ans = -1; if (akind == JL_VARARG_BOUND && bkind < JL_VARARG_BOUND) { - ans = args_morespecific_fix1(a, b, 0, env); + ans = args_morespecific_fix1(a, b, a0, b0, 0, env); if (ans == 1) return 1; } if (bkind == JL_VARARG_BOUND && akind < JL_VARARG_BOUND) { - ans = args_morespecific_fix1(b, a, 1, env); + ans = args_morespecific_fix1(b, a, b0, a0, 1, env); if (ans == 0) return 0; } - return tuple_morespecific((jl_datatype_t*)a, (jl_datatype_t*)b, invariant, env); + return tuple_morespecific((jl_datatype_t*)a, (jl_datatype_t*)b, a0, b0, invariant, env); } if (!invariant) { @@ -4152,13 +4252,13 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_ty } // Union a is more specific than b if some element of a is more specific than b, but // not vice-versa. - if (sub_msp(b, a, env)) + if (sub_msp(b, a, a0, env)) return 0; jl_uniontype_t *u = (jl_uniontype_t*)a; - if (type_morespecific_(u->a, b, invariant, env) || type_morespecific_(u->b, b, invariant, env)) { + if (type_morespecific_(u->a, b, a0, b0, invariant, env) || type_morespecific_(u->b, b, a0, b0, invariant, env)) { if (jl_is_uniontype(b)) { jl_uniontype_t *v = (jl_uniontype_t*)b; - if (type_morespecific_(v->a, a, invariant, env) || type_morespecific_(v->b, a, invariant, env)) + if (type_morespecific_(v->a, a, b0, a0, invariant, env) || type_morespecific_(v->b, a, b0, a0, invariant, env)) return 0; } return 1; @@ -4172,11 +4272,11 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_ty jl_value_t *tp0a = jl_tparam0(a); if (jl_is_typevar(tp0a)) { jl_value_t *ub = ((jl_tvar_t*)tp0a)->ub; - if (jl_is_kind(b) && !sub_msp((jl_value_t*)jl_any_type, ub, env)) + if (jl_is_kind(b) && !sub_msp((jl_value_t*)jl_any_type, ub, b0, env)) return 1; } else if (tp0a == jl_bottom_type) { - if (sub_msp(b, (jl_value_t*)jl_type_type, env)) + if (sub_msp(b, (jl_value_t*)jl_type_type, (jl_value_t*)jl_type_type, env)) return 1; } else if (b == (jl_value_t*)jl_datatype_type || b == (jl_value_t*)jl_unionall_type || @@ -4190,8 +4290,8 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_ty HANDLE_UNIONALL_A; } jl_uniontype_t *u = (jl_uniontype_t*)b; - if (type_morespecific_(a, u->a, invariant, env) || type_morespecific_(a, u->b, invariant, env)) - return !type_morespecific_(b, a, invariant, env); + if (type_morespecific_(a, u->a, a0, b0, invariant, env) || type_morespecific_(a, u->b, a0, b0, invariant, env)) + return !type_morespecific_(b, a, b0, a0, invariant, env); return 0; } @@ -4207,7 +4307,7 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_ty if (tta->name != jl_type_typename) return 1; jl_value_t *tp0 = jl_tparam0(b); if (jl_is_typevar(tp0)) { - if (sub_msp((jl_value_t*)jl_any_type, ((jl_tvar_t*)tp0)->ub, env)) + if (sub_msp((jl_value_t*)jl_any_type, ((jl_tvar_t*)tp0)->ub, b0, env)) return 1; } } @@ -4220,11 +4320,11 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_ty int bfree = jl_has_free_typevars(bpara); if (!afree && !bfree && !jl_types_equal(apara, bpara)) return 0; - if (type_morespecific_(apara, bpara, 1, env) && (jl_is_typevar(apara) || !afree || bfree)) + if (type_morespecific_(apara, bpara, a0, b0, 1, env) && (jl_is_typevar(apara) || !afree || bfree)) ascore += 1; - else if (type_morespecific_(bpara, apara, 1, env) && (jl_is_typevar(bpara) || !bfree || afree)) + else if (type_morespecific_(bpara, apara, b0, a0, 1, env) && (jl_is_typevar(bpara) || !bfree || afree)) bscore += 1; - else if (eq_msp(apara, bpara, env)) { + else if (eq_msp(apara, bpara, a0, b0, env)) { if (!afree && bfree) ascore += 1; else if (afree && !bfree) @@ -4263,13 +4363,13 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_ty if (jl_is_typevar(a)) { if (jl_is_typevar(b)) { return (( type_morespecific_((jl_value_t*)((jl_tvar_t*)a)->ub, - (jl_value_t*)((jl_tvar_t*)b)->ub, 0, env) && + (jl_value_t*)((jl_tvar_t*)b)->ub, a0, b0, 0, env) && !type_morespecific_((jl_value_t*)((jl_tvar_t*)a)->lb, - (jl_value_t*)((jl_tvar_t*)b)->lb, 0, env)) || + (jl_value_t*)((jl_tvar_t*)b)->lb, a0, b0, 0, env)) || ( type_morespecific_((jl_value_t*)((jl_tvar_t*)b)->lb, - (jl_value_t*)((jl_tvar_t*)a)->lb, 0, env) && + (jl_value_t*)((jl_tvar_t*)a)->lb, b0, a0, 0, env) && !type_morespecific_((jl_value_t*)((jl_tvar_t*)b)->ub, - (jl_value_t*)((jl_tvar_t*)a)->ub, 0, env))); + (jl_value_t*)((jl_tvar_t*)a)->ub, b0, a0, 0, env))); } if (!jl_is_type(b)) return 0; @@ -4278,7 +4378,7 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_ty return 1; if (!jl_has_free_typevars(b)) return 0; - if (eq_msp(((jl_tvar_t*)a)->ub, b, env)) + if (eq_msp(((jl_tvar_t*)a)->ub, b, a0, b0, env)) return num_occurs((jl_tvar_t*)a, env) >= 2; } else { @@ -4287,7 +4387,7 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_ty num_occurs((jl_tvar_t*)a, env) >= 2) return 1; } - return type_morespecific_(((jl_tvar_t*)a)->ub, b, 0, env); + return type_morespecific_(((jl_tvar_t*)a)->ub, b, a0, b0, 0, env); } if (jl_is_typevar(b)) { if (!jl_is_type(a)) @@ -4296,21 +4396,21 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_ty if (((jl_tvar_t*)b)->ub == jl_bottom_type) return 0; if (jl_has_free_typevars(a)) { - if (type_morespecific_(a, ((jl_tvar_t*)b)->ub, 0, env)) + if (type_morespecific_(a, ((jl_tvar_t*)b)->ub, a0, b0, 0, env)) return 1; - if (eq_msp(a, ((jl_tvar_t*)b)->ub, env)) + if (eq_msp(a, ((jl_tvar_t*)b)->ub, a0, b0, env)) return num_occurs((jl_tvar_t*)b, env) < 2; return 0; } else { if (obviously_disjoint(a, ((jl_tvar_t*)b)->ub, 1)) return 0; - if (type_morespecific_(((jl_tvar_t*)b)->ub, a, 0, env)) + if (type_morespecific_(((jl_tvar_t*)b)->ub, a, b0, a0, 0, env)) return 0; return 1; } } - return type_morespecific_(a, ((jl_tvar_t*)b)->ub, 0, env); + return type_morespecific_(a, ((jl_tvar_t*)b)->ub, a0, b0, 0, env); } if (jl_is_unionall(a)) { @@ -4333,12 +4433,12 @@ JL_DLLEXPORT int jl_type_morespecific(jl_value_t *a, jl_value_t *b) return 0; if (jl_subtype(a, b)) return 1; - return type_morespecific_(a, b, 0, NULL); + return type_morespecific_(a, b, a, b, 0, NULL); } JL_DLLEXPORT int jl_type_morespecific_no_subtype(jl_value_t *a, jl_value_t *b) { - return type_morespecific_(a, b, 0, NULL); + return type_morespecific_(a, b, a, b, 0, NULL); } #ifdef __cplusplus From 0ec704e2669f7190e8b3f346b1ff8bab85205472 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Fri, 3 Mar 2023 17:15:33 -0600 Subject: [PATCH 244/775] staticdata: set method.nroots_sysimg in `jl_write_values` (#48875) This ensures that the value gets set after all possible compilation has occurred. --- src/staticdata.c | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/src/staticdata.c b/src/staticdata.c index cd9ed8b0db088..2d03e668ade2b 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1346,6 +1346,8 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED else arraylist_push(&s->fixup_objs, (void*)reloc_offset); newm->primary_world = ~(size_t)0; + } else { + newm->nroots_sysimg = m->roots ? jl_array_len(m->roots) : 0; } if (m->ccallable) arraylist_push(&s->ccallable_list, (void*)reloc_offset); @@ -2146,28 +2148,6 @@ static void jl_strip_all_codeinfos(void) jl_foreach_reachable_mtable(strip_all_codeinfos_, NULL); } -// Method roots created during sysimg construction are exempted from -// triggering non-relocatability of compressed CodeInfos. -// Set the number of such roots in each method when the sysimg is -// serialized. -// TODO: move this to `jl_write_values` -static int set_nroots_sysimg__(jl_typemap_entry_t *def, void *_env) -{ - jl_method_t *m = def->func.method; - m->nroots_sysimg = m->roots ? jl_array_len(m->roots) : 0; - return 1; -} - -static int set_nroots_sysimg_(jl_methtable_t *mt, void *_env) -{ - return jl_typemap_visitor(mt->defs, set_nroots_sysimg__, NULL); -} - -static void jl_set_nroots_sysimg(void) -{ - jl_foreach_reachable_mtable(set_nroots_sysimg_, NULL); -} - // --- entry points --- jl_array_t *jl_global_roots_table; @@ -2271,8 +2251,6 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, // strip metadata and IR when requested if (jl_options.strip_metadata || jl_options.strip_ir) jl_strip_all_codeinfos(); - if (worklist == NULL) - jl_set_nroots_sysimg(); int en = jl_gc_enable(0); nsym_tag = 0; From 89f30fff275a9abbc99c9513922e6cff62622cfa Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Fri, 3 Mar 2023 17:21:01 -0600 Subject: [PATCH 245/775] staticdata: check ci->relocatability before caching external specializations (#48882) Fixes #48837 --- src/staticdata_utils.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 32c735ab4f626..7c53183deae17 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -230,6 +230,8 @@ static jl_array_t *queue_external_cis(jl_array_t *list) for (i = n0; i-- > 0; ) { jl_code_instance_t *ci = (jl_code_instance_t*)jl_array_ptr_ref(list, i); assert(jl_is_code_instance(ci)); + if (!ci->relocatability) + continue; jl_method_instance_t *mi = ci->def; jl_method_t *m = mi->def.method; if (ci->inferred && jl_is_method(m) && jl_object_in_image((jl_value_t*)m->module)) { From 5f8fcd9423685102640b2b37b16c34877241b92b Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Fri, 3 Mar 2023 20:31:02 -0600 Subject: [PATCH 246/775] Show typeinfo for arrays with >2 dimensions (#48884) --- base/arrayshow.jl | 4 +++- test/show.jl | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/base/arrayshow.jl b/base/arrayshow.jl index 7d63375ab3549..af65df3c97b9d 100644 --- a/base/arrayshow.jl +++ b/base/arrayshow.jl @@ -462,8 +462,10 @@ function _show_nonempty(io::IO, @nospecialize(X::AbstractMatrix), prefix::String end -_show_nonempty(io::IO, X::AbstractArray, prefix::String) = +function _show_nonempty(io::IO, X::AbstractArray, prefix::String) + print(io, prefix) show_nd(io, X, (io, slice) -> _show_nonempty(io, inferencebarrier(slice), prefix, true, axes(slice)), false) +end # a specific call path is used to show vectors (show_vector) _show_nonempty(::IO, ::AbstractVector, ::String) = diff --git a/test/show.jl b/test/show.jl index 5e5583135915b..058b14951e260 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1867,6 +1867,10 @@ end @test replstr((; var"#var#"=1)) == """(var"#var#" = 1,)""" @test replstr((; var"a"=1, b=2)) == "(a = 1, b = 2)" @test replstr((; a=1, b=2)) == "(a = 1, b = 2)" + + # issue 48828, typeinfo missing for arrays with >2 dimensions + @test showstr(Float16[1.0 3.0; 2.0 4.0;;; 5.0 7.0; 6.0 8.0]) == + "Float16[1.0 3.0; 2.0 4.0;;; 5.0 7.0; 6.0 8.0]" end @testset "#14684: `display` should print associative types in full" begin From 53c5a5a5d6cfb73f012ffd275da1c713a9bd2352 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Sat, 4 Mar 2023 18:29:42 +0100 Subject: [PATCH 247/775] Remove `[l/r]mul!` of `AbstractQ` with `*Triangular` (#48716) --- stdlib/LinearAlgebra/src/abstractq.jl | 8 -------- stdlib/LinearAlgebra/test/special.jl | 8 ++++---- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/stdlib/LinearAlgebra/src/abstractq.jl b/stdlib/LinearAlgebra/src/abstractq.jl index e591ca69fe429..853286f34048f 100644 --- a/stdlib/LinearAlgebra/src/abstractq.jl +++ b/stdlib/LinearAlgebra/src/abstractq.jl @@ -143,10 +143,6 @@ function (*)(A::AbstractMatrix, Q::AbstractQ) end (*)(u::AdjointAbsVec, Q::AbstractQ) = (Q'u')' -# AbstractQ * Triangular -lmul!(Q::AbstractQ, B::AbstractTriangular) = lmul!(Q, full!(B)) -rmul!(A::AbstractTriangular, Q::AbstractQ) = rmul!(full!(A), Q) - ### Q*Q (including adjoints) *(Q::AbstractQ, P::AbstractQ) = Q * (P*I) @@ -279,7 +275,6 @@ function lmul!(A::QRPackedQ, B::AbstractVecOrMat) end B end -lmul!(Q::QRPackedQ, B::AbstractTriangular) = lmul!(Q, full!(B)) # disambiguation ### QcB lmul!(adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasReal} = @@ -316,7 +311,6 @@ function lmul!(adjA::AdjointQ{<:Any,<:QRPackedQ}, B::AbstractVecOrMat) end B end -lmul!(Q::AdjointQ{<:Any,<:QRPackedQ}, B::AbstractTriangular) = lmul!(Q, full!(B)) # disambiguation ### AQ rmul!(A::StridedVecOrMat{T}, B::QRCompactWYQ{T,<:StridedMatrix}) where {T<:BlasFloat} = @@ -348,7 +342,6 @@ function rmul!(A::AbstractMatrix, Q::QRPackedQ) end A end -rmul!(A::AbstractTriangular, Q::QRPackedQ) = rmul!(full!(A), Q) # disambiguation ### AQc rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T}}) where {T<:BlasReal} = @@ -385,7 +378,6 @@ function rmul!(A::AbstractMatrix, adjQ::AdjointQ{<:Any,<:QRPackedQ}) end A end -rmul!(A::AbstractTriangular, Q::AdjointQ{<:Any,<:QRPackedQ}) = rmul!(full!(A), Q) # disambiguation det(Q::QRPackedQ) = _det_tau(Q.τ) det(Q::QRCompactWYQ) = diff --git a/stdlib/LinearAlgebra/test/special.jl b/stdlib/LinearAlgebra/test/special.jl index df845ba3110da..eaa297e05d957 100644 --- a/stdlib/LinearAlgebra/test/special.jl +++ b/stdlib/LinearAlgebra/test/special.jl @@ -228,10 +228,10 @@ end b = rand(n,n) for pivot in (ColumnNorm(), NoPivot()) qrb = qr(b, pivot) - @test atri * qrb.Q ≈ matri * qrb.Q ≈ rmul!(copy(atri), qrb.Q) - @test atri * qrb.Q' ≈ matri * qrb.Q' ≈ rmul!(copy(atri), qrb.Q') - @test qrb.Q * atri ≈ qrb.Q * matri ≈ lmul!(qrb.Q, copy(atri)) - @test qrb.Q' * atri ≈ qrb.Q' * matri ≈ lmul!(qrb.Q', copy(atri)) + @test atri * qrb.Q ≈ matri * qrb.Q + @test atri * qrb.Q' ≈ matri * qrb.Q' + @test qrb.Q * atri ≈ qrb.Q * matri + @test qrb.Q' * atri ≈ qrb.Q' * matri end end end From ff5bd4b7e3bcda9681b8878c22881fdefdda1ebb Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 4 Mar 2023 13:29:00 -0500 Subject: [PATCH 248/775] ambiguity detection: more optimal code order (#48846) --- src/gf.c | 107 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 25 deletions(-) diff --git a/src/gf.c b/src/gf.c index 78ffa1a4143ef..357487d723e9b 100644 --- a/src/gf.c +++ b/src/gf.c @@ -3435,22 +3435,9 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, } if (ti != jl_bottom_type) { disjoint = 0; - // m and m2 are ambiguous, but let's see if we can find another method (m3) - // that dominates their intersection, and means we can ignore this - size_t k; - for (k = i; k > 0; k--) { - jl_method_match_t *matc3 = (jl_method_match_t*)jl_array_ptr_ref(env.t, k - 1); - jl_method_t *m3 = matc3->method; - if ((jl_subtype(ti, m3->sig) || (isect2 && jl_subtype(isect2, m3->sig))) - && jl_type_morespecific((jl_value_t*)m3->sig, (jl_value_t*)m->sig) - && jl_type_morespecific((jl_value_t*)m3->sig, (jl_value_t*)m2->sig)) - break; - } - if (k == 0) { - ambig_groupid[j - 1] = i; // ambiguity covering range [i:j) - isect2 = NULL; - break; - } + ambig_groupid[j - 1] = i; // ambiguity covering range [i:j) + isect2 = NULL; + break; } isect2 = NULL; } @@ -3529,19 +3516,89 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, // Compute whether anything could be ambiguous by seeing if any two // remaining methods in the result are in the same ambiguity group. assert(len > 0); - uint32_t agid = ambig_groupid[0]; - for (i = 1; i < len; i++) { - if (!skip[i]) { - if (agid == ambig_groupid[i]) { - has_ambiguity = 1; - break; + if (!has_ambiguity) { + // quick test + uint32_t agid = ambig_groupid[0]; + for (i = 1; i < len; i++) { + if (!skip[i]) { + if (agid == ambig_groupid[i]) { + has_ambiguity = 1; + break; + } + agid = ambig_groupid[i]; + } + } + // laborious test, checking for existence and coverage of m3 + if (has_ambiguity) { + // some method is ambiguous, but let's see if we can find another method (m3) + // outside of the ambiguity group that dominates any ambiguous methods, + // and means we can ignore this for has_ambiguity + has_ambiguity = 0; + for (i = 0; i < len; i++) { + if (skip[i]) + continue; + uint32_t agid = ambig_groupid[i]; + jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, i); + jl_method_t *m = matc->method; + int subt = matc->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m->sig) + for (j = agid; j < len && ambig_groupid[j] == agid; j++) { + // n.b. even if we skipped them earlier, they still might + // contribute to the ambiguities (due to lock of transitivity of + // morespecific over subtyping) + if (j == i) + continue; + jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(env.t, j); + jl_method_t *m2 = matc2->method; + int subt2 = matc2->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) + // if they aren't themselves simply ordered + if (jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig) || + jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig)) + continue; + jl_value_t *ti; + if (subt) { + ti = (jl_value_t*)matc2->spec_types; + isect2 = NULL; + } + else if (subt2) { + ti = (jl_value_t*)matc->spec_types; + isect2 = NULL; + } + else { + jl_type_intersection2((jl_value_t*)matc->spec_types, (jl_value_t*)matc2->spec_types, &env.match.ti, &isect2); + ti = env.match.ti; + } + // and their intersection contributes to the ambiguity cycle + if (ti != jl_bottom_type) { + // now look for a third method m3 outside of this ambiguity group that fully resolves this intersection + size_t k; + for (k = agid; k > 0; k--) { + jl_method_match_t *matc3 = (jl_method_match_t*)jl_array_ptr_ref(env.t, k); + jl_method_t *m3 = matc3->method; + if ((jl_subtype(ti, m3->sig) || (isect2 && jl_subtype(isect2, m3->sig))) + && jl_type_morespecific((jl_value_t*)m3->sig, (jl_value_t*)m->sig) + && jl_type_morespecific((jl_value_t*)m3->sig, (jl_value_t*)m2->sig)) { + //if (jl_subtype(matc->spec_types, ti) || jl_subtype(matc->spec_types, matc3->m3->sig)) + // // check if it covered not only this intersection, but all intersections with matc + // // if so, we do not need to check all of them separately + // j = len; + break; + } + } + if (k == 0) + has_ambiguity = 1; + isect2 = NULL; + } + if (has_ambiguity) + break; + } + if (has_ambiguity) + break; } - agid = ambig_groupid[i]; } } // If we're only returning possible matches, now filter out any method // whose intersection is fully ambiguous with the group it is in. - if (!include_ambiguous) { + if (!include_ambiguous && has_ambiguity) { for (i = 0; i < len; i++) { if (skip[i]) continue; @@ -3559,7 +3616,7 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, int subt2 = matc2->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) // if their intersection contributes to the ambiguity cycle if (subt || subt2 || !jl_has_empty_intersection((jl_value_t*)ti, m2->sig)) { - // and the contribution of m is ambiguous with the portion of the cycle from m2 + // and the contribution of m is fully ambiguous with the portion of the cycle from m2 if (subt2 || jl_subtype((jl_value_t*)ti, m2->sig)) { // but they aren't themselves simply ordered (here // we don't consider that a third method might be From 6124987df34ae5c5e018eb1e44ae619d30a13718 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 4 Mar 2023 13:29:57 -0500 Subject: [PATCH 249/775] array: fix some atomic orderings (#48888) --- base/array.jl | 6 +++--- src/array.c | 2 +- src/datatype.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/base/array.jl b/base/array.jl index 694a3913cacf4..213698c754bb3 100644 --- a/base/array.jl +++ b/base/array.jl @@ -177,11 +177,11 @@ function _unsetindex!(A::Array{T}, i::Int) where {T} t = @_gc_preserve_begin A p = Ptr{Ptr{Cvoid}}(pointer(A, i)) if !allocatedinline(T) - unsafe_store!(p, C_NULL) + Intrinsics.atomic_pointerset(p, C_NULL, :monotonic) elseif T isa DataType if !datatype_pointerfree(T) - for j = 1:(Core.sizeof(T) ÷ Core.sizeof(Ptr{Cvoid})) - unsafe_store!(p, C_NULL, j) + for j = 1:Core.sizeof(Ptr{Cvoid}):Core.sizeof(T) + Intrinsics.atomic_pointerset(p + j - 1, C_NULL, :monotonic) end end end diff --git a/src/array.c b/src/array.c index ae89087502627..0b582296774b5 100644 --- a/src/array.c +++ b/src/array.c @@ -627,7 +627,7 @@ JL_DLLEXPORT void jl_arrayunset(jl_array_t *a, size_t i) if (i >= jl_array_len(a)) jl_bounds_error_int((jl_value_t*)a, i + 1); if (a->flags.ptrarray) - jl_atomic_store_release(((_Atomic(jl_value_t*)*)a->data) + i, NULL); + jl_atomic_store_relaxed(((_Atomic(jl_value_t*)*)a->data) + i, NULL); else if (a->flags.hasptr) { size_t elsize = a->elsize; jl_assume(elsize >= sizeof(void*) && elsize % sizeof(void*) == 0); diff --git a/src/datatype.c b/src/datatype.c index 6e71c6573c91f..ae1e3029aa0e1 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -68,7 +68,7 @@ JL_DLLEXPORT jl_typename_t *jl_new_typename_in(jl_sym_t *name, jl_module_t *modu tn->name = name; tn->module = module; tn->wrapper = NULL; - jl_atomic_store_release(&tn->Typeofwrapper, NULL); + jl_atomic_store_relaxed(&tn->Typeofwrapper, NULL); jl_atomic_store_relaxed(&tn->cache, jl_emptysvec); jl_atomic_store_relaxed(&tn->linearcache, jl_emptysvec); tn->names = NULL; From eb36cb9e7d01e230dfe0371a77476d4867c0e615 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 4 Mar 2023 13:30:47 -0500 Subject: [PATCH 250/775] pkgimage: use faster flags to jl_matching_methods (#48841) This does a better job of filtering out duplicates, which can both save a lot of time and avoid spurious invalidations. --- src/staticdata_utils.c | 4 ++-- test/precompile.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 7c53183deae17..4ec60b26a11b4 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -520,7 +520,7 @@ static void jl_collect_edges(jl_array_t *edges, jl_array_t *ext_targets, jl_arra sig = callee; int ambig = 0; matches = jl_matching_methods((jl_tupletype_t*)sig, jl_nothing, - -1, 0, world, &min_valid, &max_valid, &ambig); + INT32_MAX, 0, world, &min_valid, &max_valid, &ambig); if (matches == jl_nothing) { callee_ids = NULL; // invalid break; @@ -870,7 +870,7 @@ static jl_array_t *jl_verify_edges(jl_array_t *targets, size_t minworld) int ambig = 0; // TODO: possibly need to included ambiguities too (for the optimizer correctness)? matches = jl_matching_methods((jl_tupletype_t*)sig, jl_nothing, - -1, 0, minworld, &min_valid, &max_valid, &ambig); + jl_array_len(expected), 0, minworld, &min_valid, &max_valid, &ambig); if (matches == jl_nothing) { max_valid = 0; } diff --git a/test/precompile.jl b/test/precompile.jl index dbe40f3ba6204..1ee32cb39e37d 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -936,7 +936,7 @@ precompile_test_harness("code caching") do dir j = findfirst(==(tagbad), invalidations) @test invalidations[j-1] == "insert_backedges_callee" @test isa(invalidations[j-2], Type) - @test isa(invalidations[j+1], Vector{Any}) # [nbits(::UInt8)] + @test invalidations[j+1] === nothing || isa(invalidations[j+1], Vector{Any}) # [nbits(::UInt8)] m = only(methods(MB.map_nbits)) @test !hasvalid(m.specializations[1], world+1) # insert_backedges invalidations also trigger their backedges From 39e34ee6f890ce2923aa37ff0a6fb76e17f1c78b Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Sun, 5 Mar 2023 03:37:58 -0500 Subject: [PATCH 251/775] Initialize JIT bytes --- src/jitlayers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jitlayers.h b/src/jitlayers.h index db6f68bd3f3b6..4cff48b93f7da 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -524,7 +524,7 @@ class JuliaOJIT { #ifndef JL_USE_JITLINK const std::shared_ptr MemMgr; #else - std::atomic total_size; + std::atomic total_size{0}; const std::unique_ptr MemMgr; #endif ObjLayerT ObjectLayer; From 24b9dd08a70dc587bcd23c35db093a6b7a1d0dc5 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Sat, 4 Mar 2023 21:12:29 -0800 Subject: [PATCH 252/775] Make `jit_total_bytes()` return a `UInt` instead of an `Int` There is not a good reason why we should return an `Int` here instead of a `UInt`, especially since the C API returns a `size_t`. --- base/timing.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/timing.jl b/base/timing.jl index e082c09156b84..4b3d72143e7f4 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -103,7 +103,7 @@ Return the total amount (in bytes) allocated by the just-in-time compiler for e.g. native code and data. """ function jit_total_bytes() - return Int(ccall(:jl_jit_total_bytes, Csize_t, ())) + return ccall(:jl_jit_total_bytes, Csize_t, ()) end # print elapsed time, return expression value From bba9082d380147e03d49149a5712e67697297e1a Mon Sep 17 00:00:00 2001 From: Patrick Bouffard Date: Sun, 5 Mar 2023 10:00:48 -0800 Subject: [PATCH 253/775] Add mention of keywords in tab completion to HISTORY (#48905) --- HISTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/HISTORY.md b/HISTORY.md index 0db48d5f960e3..f98073351e2f2 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -137,6 +137,7 @@ Standard library changes the keybinding Alt-m ([#33872]). * An "IPython mode" which mimics the behaviour of the prompts and storing the evaluated result in `Out` can be activated with `REPL.ipython_mode!()`. See the manual for how to enable this at startup ([#46474]). +* Tab completion displays available keyword arguments ([#43536]) #### SuiteSparse From ae751e1cce813a948e1b3b9897de291746cfd634 Mon Sep 17 00:00:00 2001 From: Udoh Jeremiah Date: Sun, 5 Mar 2023 20:08:48 +0100 Subject: [PATCH 254/775] complete code block for `issubnormal` (#48893) --- base/float.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/float.jl b/base/float.jl index d9f35fdb329a9..4190bfa18bb2b 100644 --- a/base/float.jl +++ b/base/float.jl @@ -924,6 +924,7 @@ false julia> issubnormal(1.0f-38) true +``` """ function issubnormal(x::T) where {T<:IEEEFloat} y = reinterpret(Unsigned, x) From 7eb961588f204e1866aea0bdee63f5dd91d42ac3 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Sun, 5 Mar 2023 21:35:44 -0600 Subject: [PATCH 255/775] Fix sorting missing values with offset indices (#48864) * Fix sorting missing values with offset indices * Add tests --- base/sort.jl | 1 + test/sorting.jl | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/base/sort.jl b/base/sort.jl index 4605ccd06d734..4fdd08931e800 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -533,6 +533,7 @@ Base.@propagate_inbounds function Base.setindex!(v::WithoutMissingVector, x, i) v end Base.size(v::WithoutMissingVector) = size(v.data) +Base.axes(v::WithoutMissingVector) = axes(v.data) """ send_to_end!(f::Function, v::AbstractVector; [lo, hi]) diff --git a/test/sorting.jl b/test/sorting.jl index 15eec92166ec7..ec1666dabb2fb 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -562,6 +562,13 @@ end end end +@testset "Offset with missing (#48862)" begin + v = [-1.0, missing, 1.0, 0.0, missing, -0.5, 0.5, 1.0, -0.5, missing, 0.5, -0.8, 1.5, NaN] + vo = OffsetArray(v, (firstindex(v):lastindex(v)).+100) + @test issorted(sort!(vo)) + @test issorted(v) +end + @testset "searchsortedfirst/last with generalized indexing" begin o = OffsetVector(1:3, -2) @test searchsortedfirst(o, 4) == lastindex(o) + 1 From 27f1ccdf55078670d4f4f0a7407e53df580134d5 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Sun, 5 Mar 2023 23:14:01 -0500 Subject: [PATCH 256/775] Move dbgs under LLVM_DEBUG --- src/aotcompile.cpp | 152 ++++++++++++++++++++++++++++----------------- 1 file changed, 95 insertions(+), 57 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index f74c9f92d3093..d512ad586a680 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -600,24 +600,72 @@ static void get_fvars_gvars(Module &M, DenseMap &fvars, gvars_idxs->eraseFromParent(); } -static size_t getFunctionWeight(const Function &F) +struct FunctionInfo { + size_t weight; + size_t bbs; + size_t insts; + size_t clones; +}; + +static FunctionInfo getFunctionWeight(const Function &F) { - size_t weight = 1; + FunctionInfo info; + info.weight = 1; + info.bbs = F.size(); + info.insts = 0; + info.clones = 1; for (const BasicBlock &BB : F) { - weight += BB.size(); + info.insts += BB.size(); } - // more basic blocks = more complex than just sum of insts, - // add some weight to it - weight += F.size(); if (F.hasFnAttribute("julia.mv.clones")) { auto val = F.getFnAttribute("julia.mv.clones").getValueAsString(); // base16, so must be at most 4 * length bits long // popcount gives number of clones - weight *= APInt(val.size() * 4, val, 16).countPopulation() + 1; + info.clones = APInt(val.size() * 4, val, 16).countPopulation() + 1; } - return weight; + info.weight += info.insts; + // more basic blocks = more complex than just sum of insts, + // add some weight to it + info.weight += info.bbs; + info.weight *= info.clones; + return info; } +struct ModuleInfo { + size_t globals; + size_t funcs; + size_t bbs; + size_t insts; + size_t clones; + size_t weight; +}; + +ModuleInfo compute_module_info(Module &M) { + ModuleInfo info; + info.globals = 0; + info.funcs = 0; + info.bbs = 0; + info.insts = 0; + info.clones = 0; + info.weight = 0; + for (auto &G : M.global_values()) { + if (G.isDeclaration()) { + continue; + } + info.globals++; + if (auto F = dyn_cast(&G)) { + info.funcs++; + auto func_info = getFunctionWeight(*F); + info.bbs += func_info.bbs; + info.insts += func_info.insts; + info.clones += func_info.clones; + info.weight += func_info.weight; + } else { + info.weight += 1; + } + } + return info; +} static inline bool verify_partitioning(const SmallVectorImpl &partitions, const Module &M, size_t fvars_size, size_t gvars_size) { bool bad = false; @@ -647,7 +695,6 @@ static inline bool verify_partitioning(const SmallVectorImpl &partiti } gvars[gvar.second] = i+1; } - // dbgs() << "partition: " << i << " fvars: " << partitions[i].fvars.size() << " gvars: " << partitions[i].gvars.size() << "\n"; } for (auto &GV : M.globals()) { if (GV.isDeclaration()) { @@ -736,7 +783,7 @@ static SmallVector partitionModule(Module &M, unsigned threads) { if (G.isDeclaration()) continue; if (isa(G)) { - partitioner.make(&G, getFunctionWeight(cast(G))); + partitioner.make(&G, getFunctionWeight(cast(G)).weight); } else { partitioner.make(&G, 1); } @@ -1141,7 +1188,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o std::vector &unopt, std::vector &opt, std::vector &obj, std::vector &asm_, bool unopt_out, bool opt_out, bool obj_out, bool asm_out, - unsigned threads) { + unsigned threads, ModuleInfo module_info) { unsigned outcount = unopt_out + opt_out + obj_out + asm_out; assert(outcount); outputs.resize(outputs.size() + outcount * threads); @@ -1235,8 +1282,6 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o auto M = cantFail(getLazyBitcodeModule(MemoryBufferRef(StringRef(serialized.data(), serialized.size()), "Optimized"), ctx), "Error loading module"); timers[i].deserialize.stopTimer(); - // dbgs() << "Starting shard " << i << " with weight=" << partitions[i].weight << "\n"; - timers[i].materialize.startTimer(); materializePreserved(*M, partitions[i]); timers[i].materialize.stopTimer(); @@ -1271,54 +1316,37 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o for (auto &t : timers) { t.print(dbgs(), true); } + dbgs() << "Partition weights: ["; + bool comma = false; + for (auto &p : partitions) { + if (comma) + dbgs() << ", "; + else + comma = true; + dbgs() << p.weight; + } + dbgs() << "]\n"; } } -unsigned compute_image_thread_count(Module &M) { +static unsigned compute_image_thread_count(const ModuleInfo &info) { // 32-bit systems are very memory-constrained #ifdef _P32 - // dbgs() << "Threads: 1\n"; + LLVM_DEBUG(dbgs() << "32-bit systems are restricted to a single thread\n"); return 1; #endif - size_t weight = 0; - size_t globals = 0; - for (auto &GV : M.global_values()) { - if (GV.isDeclaration()) - continue; - globals++; - if (isa(GV)) { - weight += getFunctionWeight(cast(GV)); - } else { - weight += 1; - } - } - // dbgs() << "Module weight: " << weight << "\n"; - if (weight < 1000) { - // dbgs() << "Low module complexity bailout\n"; - // dbgs() << "Threads: 1\n"; + // This is not overridable because empty modules do occasionally appear, but they'll be very small and thus exit early to + // known easy behavior. Plus they really don't warrant multiple threads + if (info.weight < 1000) { + LLVM_DEBUG(dbgs() << "Small module, using a single thread\n"); return 1; } - unsigned threads = std::max(llvm::hardware_concurrency().compute_thread_count() / 2, 1u); - - // memory limit check - // many threads use a lot of memory, so limit on constrained memory systems - size_t available = uv_get_available_memory(); - // crude estimate, available / (weight * fudge factor) = max threads - size_t fudge = 10; - unsigned max_threads = std::max(available / (weight * fudge), (size_t)1); - // dbgs() << "Available memory: " << available << " bytes\n"; - // dbgs() << "Max threads: " << max_threads << "\n"; - // dbgs() << "Temporarily disabling memory limiting threads\n"; - //TODO reenable - // if (max_threads < threads) { - // dbgs() << "Memory limiting threads to " << max_threads << "\n"; - // threads = max_threads; - // } - - max_threads = globals / 100; + unsigned threads = std::max(jl_cpu_threads() / 2, 1); + + auto max_threads = info.globals / 100; if (max_threads < threads) { - // dbgs() << "Low global count limiting threads to " << max_threads << " (" << globals << "globals)\n"; + LLVM_DEBUG(dbgs() << "Low global count limiting threads to " << max_threads << " (" << info.globals << "globals)\n"); threads = max_threads; } @@ -1331,7 +1359,7 @@ unsigned compute_image_thread_count(Module &M) { if (*endptr || !requested) { jl_safe_printf("WARNING: invalid value '%s' for JULIA_IMAGE_THREADS\n", env_threads); } else { - // dbgs() << "Overriding threads to " << requested << " due to JULIA_IMAGE_THREADS\n"; + LLVM_DEBUG(dbgs() << "Overriding threads to " << requested << " due to JULIA_IMAGE_THREADS\n"); threads = requested; env_threads_set = true; } @@ -1345,7 +1373,7 @@ unsigned compute_image_thread_count(Module &M) { if (*endptr || !requested) { jl_safe_printf("WARNING: invalid value '%s' for JULIA_CPU_THREADS\n", fallbackenv); } else if (requested < threads) { - // dbgs() << "Overriding threads to " << requested << " due to JULIA_CPU_THREADS\n"; + LLVM_DEBUG(dbgs() << "Overriding threads to " << requested << " due to JULIA_CPU_THREADS\n"); threads = requested; } } @@ -1353,8 +1381,6 @@ unsigned compute_image_thread_count(Module &M) { threads = std::max(threads, 1u); - // dbgs() << "Threads: " << threads << "\n"; - return threads; } @@ -1369,7 +1395,7 @@ void jl_dump_native_impl(void *native_code, JL_TIMING(NATIVE_DUMP); jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code; if (!bc_fname && !unopt_bc_fname && !obj_fname && !asm_fname) { - // dbgs() << "No output requested, skipping native code dump?\n"; + LLVM_DEBUG(dbgs() << "No output requested, skipping native code dump?\n"); delete data; return; } @@ -1433,6 +1459,17 @@ void jl_dump_native_impl(void *native_code, unsigned nfvars = 0; unsigned ngvars = 0; + ModuleInfo module_info = compute_module_info(*dataM); + LLVM_DEBUG(dbgs() + << "Dumping module with stats:\n" + << " globals: " << module_info.globals << "\n" + << " functions: " << module_info.funcs << "\n" + << " basic blocks: " << module_info.bbs << "\n" + << " instructions: " << module_info.insts << "\n" + << " clones: " << module_info.clones << "\n" + << " weight: " << module_info.weight << "\n" + ); + // add metadata information if (imaging_mode) { multiversioning_preannotate(*dataM); @@ -1446,7 +1483,8 @@ void jl_dump_native_impl(void *native_code, } } } - threads = compute_image_thread_count(*dataM); + threads = compute_image_thread_count(module_info); + LLVM_DEBUG(dbgs() << "Using " << threads << " to emit aot image\n"); nfvars = data->jl_sysimg_fvars.size(); ngvars = data->jl_sysimg_gvars.size(); emit_offset_table(*dataM, data->jl_sysimg_gvars, "jl_gvars", T_psize); @@ -1484,7 +1522,7 @@ void jl_dump_native_impl(void *native_code, M, *SourceTM, outputs, names, unopt_bc_Archive, bc_Archive, obj_Archive, asm_Archive, !!unopt_bc_fname, !!bc_fname, !!obj_fname, !!asm_fname, - threads + threads, module_info ); }; std::array text_names = { From 6b8ec27dbc582ba67f717e400b1bcff8f886c6d3 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Sun, 5 Mar 2023 23:57:51 -0500 Subject: [PATCH 257/775] Add some documentation --- src/aotcompile.cpp | 52 +++++++++-- src/llvm-multiversioning.cpp | 2 + src/processor.h | 164 +++++++++++++++++++---------------- 3 files changed, 133 insertions(+), 85 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index d512ad586a680..0337602cde27e 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -505,6 +505,7 @@ static void injectCRTAlias(Module &M, StringRef name, StringRef alias, FunctionT void multiversioning_preannotate(Module &M); +// See src/processor.h for documentation about this table. Corresponds to jl_image_shard_t. static GlobalVariable *emit_shard_table(Module &M, Type *T_size, Type *T_psize, unsigned threads) { SmallVector tables(sizeof(jl_image_shard_t) / sizeof(void *) * threads); for (unsigned i = 0; i < threads; i++) { @@ -533,6 +534,7 @@ static GlobalVariable *emit_shard_table(Module &M, Type *T_size, Type *T_psize, return tables_gv; } +// See src/processor.h for documentation about this table. Corresponds to jl_image_ptls_t. static GlobalVariable *emit_ptls_table(Module &M, Type *T_size, Type *T_psize) { std::array ptls_table{ new GlobalVariable(M, T_size, false, GlobalValue::ExternalLinkage, Constant::getNullValue(T_size), "jl_pgcstack_func_slot"), @@ -548,6 +550,7 @@ static GlobalVariable *emit_ptls_table(Module &M, Type *T_size, Type *T_psize) { return ptls_table_gv; } +// See src/processor.h for documentation about this table. Corresponds to jl_image_header_t. static GlobalVariable *emit_image_header(Module &M, unsigned threads, unsigned nfvars, unsigned ngvars) { constexpr uint32_t version = 1; std::array header{ @@ -562,13 +565,7 @@ static GlobalVariable *emit_image_header(Module &M, unsigned threads, unsigned n return header_gv; } -struct Partition { - StringSet<> globals; - StringMap fvars; - StringMap gvars; - size_t weight; -}; - +// Grab fvars and gvars data from the module static void get_fvars_gvars(Module &M, DenseMap &fvars, DenseMap &gvars) { auto fvars_gv = M.getGlobalVariable("jl_fvars"); auto gvars_gv = M.getGlobalVariable("jl_gvars"); @@ -600,6 +597,11 @@ static void get_fvars_gvars(Module &M, DenseMap &fvars, gvars_idxs->eraseFromParent(); } +// Weight computation +// It is important for multithreaded image building to be able to split work up +// among the threads equally. The weight calculated here is an estimation of +// how expensive a particular function is going to be to compile. + struct FunctionInfo { size_t weight; size_t bbs; @@ -667,6 +669,13 @@ ModuleInfo compute_module_info(Module &M) { return info; } +struct Partition { + StringSet<> globals; + StringMap fvars; + StringMap gvars; + size_t weight; +}; + static inline bool verify_partitioning(const SmallVectorImpl &partitions, const Module &M, size_t fvars_size, size_t gvars_size) { bool bad = false; #ifndef JL_NDEBUG @@ -729,7 +738,7 @@ static inline bool verify_partitioning(const SmallVectorImpl &partiti return !bad; } -// Chop a module up as equally as possible into threads partitions +// Chop a module up as equally as possible by weight into threads partitions static SmallVector partitionModule(Module &M, unsigned threads) { //Start by stripping fvars and gvars, which helpfully removes their uses as well DenseMap fvars, gvars; @@ -926,6 +935,7 @@ struct ShardTimers { } }; +// Perform the actual optimization and emission of the output files static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *outputs, ArrayRef names, NewArchiveMember *unopt, NewArchiveMember *opt, NewArchiveMember *obj, NewArchiveMember *asm_, ShardTimers &timers, unsigned shardidx) { @@ -1048,6 +1058,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out } } +// serialize module to bitcode static auto serializeModule(const Module &M) { assert(!verifyModule(M, &errs()) && "Serializing invalid module!"); SmallVector ClonedModuleBuffer; @@ -1058,6 +1069,12 @@ static auto serializeModule(const Module &M) { return ClonedModuleBuffer; } +// Modules are deserialized lazily by LLVM, to avoid deserializing +// unnecessary functions. We take advantage of this by serializing +// the entire module once, then deleting the bodies of functions +// that are not in this partition. Once unnecesary functions are +// deleted, we then materialize the entire module to make use-lists +// consistent. static void materializePreserved(Module &M, Partition &partition) { DenseSet Preserve; for (auto &GV : M.global_values()) { @@ -1083,6 +1100,12 @@ static void materializePreserved(Module &M, Partition &partition) { } } } + // Global aliases are a pain to deal with. It is illegal to have an alias to a declaration, + // so we need to replace them with either a function or a global variable declaration. However, + // we can't just delete the alias, because that would break the users of the alias. Therefore, + // we do a dance where we point each global alias to a dummy function or global variable, + // then materialize the module to access use-lists, then replace all the uses, and finally commit + // to deleting the old alias. SmallVector> DeletedAliases; for (auto &GA : M.aliases()) { if (!GA.isDeclaration()) { @@ -1116,6 +1139,7 @@ static void materializePreserved(Module &M, Partition &partition) { } } +// Reconstruct jl_fvars, jl_gvars, jl_fvars_idxs, and jl_gvars_idxs from the partition static void construct_vars(Module &M, Partition &partition) { std::vector> fvar_pairs; fvar_pairs.reserve(partition.fvars.size()); @@ -1168,6 +1192,8 @@ static void construct_vars(Module &M, Partition &partition) { gidxs_var->setVisibility(GlobalValue::HiddenVisibility); } +// Materialization will leave many unused declarations, which multiversioning would otherwise clone. +// This function removes them to avoid unnecessary cloning of declarations. static void dropUnusedDeclarations(Module &M) { SmallVector unused; for (auto &G : M.global_values()) { @@ -1184,6 +1210,8 @@ static void dropUnusedDeclarations(Module &M) { G->eraseFromParent(); } +// Entrypoint to optionally-multithreaded image compilation. This handles global coordination of the threading, +// as well as partitioning, serialization, and deserialization. static void add_output(Module &M, TargetMachine &TM, std::vector &outputs, ArrayRef names, std::vector &unopt, std::vector &opt, std::vector &obj, std::vector &asm_, @@ -1198,6 +1226,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o asm_.resize(asm_.size() + asm_out * threads); auto name = names[2]; name.consume_back(".o"); + // Timers for timing purposes TimerGroup timer_group("add_output", ("Time to optimize and emit LLVM module " + name).str()); SmallVector timers(threads); for (unsigned i = 0; i < threads; ++i) { @@ -1232,6 +1261,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o errs() << "WARNING: Invalid value for JULIA_IMAGE_TIMINGS: " << env << "\n"; } } + // Single-threaded case if (threads == 1) { output_timer.startTimer(); add_output_impl(M, TM, outputs.data() + outputs.size() - outcount, names, @@ -1255,6 +1285,8 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o partition_timer.startTimer(); uint64_t counter = 0; + // Partitioning requires all globals to have names. + // We use a prefix to avoid name conflicts with user code. for (auto &G : M.global_values()) { if (!G.isDeclaration() && !G.hasName()) { G.setName("jl_ext_" + Twine(counter++)); @@ -1262,6 +1294,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o } auto partitions = partitionModule(M, threads); partition_timer.stopTimer(); + serialize_timer.startTimer(); auto serialized = serializeModule(M); serialize_timer.stopTimer(); @@ -1274,10 +1307,12 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o auto objstart = obj_out ? obj.data() + obj.size() - threads : nullptr; auto asmstart = asm_out ? asm_.data() + asm_.size() - threads : nullptr; + // Start all of the worker threads std::vector workers(threads); for (unsigned i = 0; i < threads; i++) { workers[i] = std::thread([&, i](){ LLVMContext ctx; + // Lazily deserialize the entire module timers[i].deserialize.startTimer(); auto M = cantFail(getLazyBitcodeModule(MemoryBufferRef(StringRef(serialized.data(), serialized.size()), "Optimized"), ctx), "Error loading module"); timers[i].deserialize.stopTimer(); @@ -1304,6 +1339,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o }); } + // Wait for all of the worker threads to finish for (auto &w : workers) w.join(); diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index cbce76d702119..0474cb0c7add7 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -3,6 +3,8 @@ // Function multi-versioning // LLVM pass to clone function for different archs +//see src/processor.h for documentation of the relevant globals inserted here + #include "llvm-version.h" #include "passes.h" diff --git a/src/processor.h b/src/processor.h index 6445f221882ba..497a93d40e11f 100644 --- a/src/processor.h +++ b/src/processor.h @@ -14,82 +14,9 @@ extern "C" { #endif -/** - * Related sysimg exported symbols - * - * In the following text, function refers to an abstract entity. - * It corresponds to a `Function` that we emit in the codegen, and there might be multiple copies - * of it in the system image. Only one of those copies will be used in a given session. - * Function pointers refer to a real piece of code in the system image. - * Each function might have multiple function pointers in the system image - * and each function pointer will correspond to only one function. - * - * # Global function and base pointers - * `jl_sysimg_gvars_base`: - * The address of this symbol is the base data pointer - * (all other data pointers are stored as offsets to this address) - * `jl_sysimg_fvars_base`: - * The address of this symbol is the base function pointer - * (all other function pointers are stored as offsets to this address) - * `jl_sysimg_fvars_offsets`: [static data] - * The array of function pointer offsets (`int32_t`) from the base pointer. - * This includes all julia functions in sysimg as well as all other functions that are cloned. - * The default function pointer is used if the function is cloned. - * The first element is the size of the array, which should **NOT** be used as the number - * of julia functions in the sysimg. - * Each entry in this array uniquely identifies a function we are interested in - * (the function may have multiple function pointers corresponding to different versions). - * In other sysimg info, all references to functions are stored as their `uint32_t` index - * in this array. - * - * # Target data and dispatch slots (Only needed by runtime during loading) - * `jl_dispatch_target_ids`: [static data] serialize target data. - * This contains the number of targets which is needed to decode `jl_dispatch_fvars_idxs` - * in addition to the name and feature set of each target. - * `jl_dispatch_reloc_slots`: [static data] location and index of relocation slots. - * Stored as pairs of function indices and `int32_t` offsets from `jl_sysimg_gvars_base`. - * The first element is an `uint32_t` giving the number of relocations. - * This is needed for functions whose address is used in a way that requires dispatch. - * We currently only support one type of relocation (i.e. absolute pointer) which is enough - * for all use in functions as well as GOT slot (for "PLT" callback). - * Note that not all functions being cloned are assigned a slot. - * This array is sorted by the function indices. - * There can be more than one slot per-function, - * i.e. there can be duplicated function indices. - * - * # Target functions - * `jl_dispatch_fvars_idxs`: [static data] Target-specific function indices. - * For each target, this includes a tagged `uint32_t` length, an optional `uint32_t` index - * of the base target followed by an array of tagged function indices. - * The base target index is required to be smaller than the index of the current target - * and must be the default (`0`) or a `clone_all` target. - * If it's not `0`, the function pointer array for the `clone_all` target will be used as - * the base function pointer offsets instead. - * The tag bits for both the length and the indices are the top bit. - * A tagged length indicates that all of the functions are cloned and the indices follows - * are the ones that requires relocation. The base target index is omitted in this case. - * Otherwise, the length is the total number of functions that we are interested in - * for this target, which includes all cloned julia functions and - * all other cloned functions that requires relocation. - * A tagged index means that the function pointer should be filled into the GOT slots - * identified by `jl_dispatch_reloc_slots`. There could be more than one slot per function. - * (Note that a tagged index could corresponds to a functions pointer that's the same as - * the base one since this is the only way we currently represent relocations.) - * A tagged length implicitly tags all the indices and the indices will not have the tag bit - * set. The lengths in this variable is needed to decode `jl_dispatch_fvars_offsets`. - * `jl_dispatch_fvars_offsets`: [static data] Target-specific function pointer offsets. - * This contains all the cloned functions that we are interested in and it needs to be decoded - * and used along with `jl_dispatch_fvars_idxs`. - * For the default target, there's no entries in this variable, if there's any relocations - * needed for the default target, the function pointers are taken from the global offset - * arrays directly. - * For a `clone_all` target (i.e. with the length in `jl_dispatch_fvars_idxs` tagged), this - * variable contains an offset array of the same length as the global one. Only the indices - * appearing in `jl_dispatch_fvars_idxs` need relocation and the dispatch code should return - * this array as the original/base function offsets. - * For other targets, this variable contains an offset array with the length defined in - * `jl_dispatch_fvars_idxs`. Tagged indices need relocations. - */ +// Image metadata +// Every image exports a `jl_image_pointers_t` as a global symbol `jl_image_pointers`. +// This symbol acts as a root for all other code-related symbols in the image. enum { JL_TARGET_VEC_CALL = 1 << 0, @@ -163,35 +90,118 @@ typedef struct { jl_image_fptrs_t fptrs; } jl_image_t; +// The header for each image +// Details important counts about the image typedef struct { + // The version of the image format + // Most up-to-date version is 1 uint32_t version; + // The number of shards in this image uint32_t nshards; + // The total number of fvars in this image among all shards uint32_t nfvars; + // The total number of gvars in this image among all shards uint32_t ngvars; } jl_image_header_t; +// Per-shard data for image shards. Each image contains header->nshards of these. typedef struct { + + // This is the base function pointer + // (all other function pointers are stored as offsets to this address) const char *fvar_base; + + // The array of function pointer offsets (`int32_t`) from the base pointer. + // This includes all julia functions in sysimg as well as all other functions that are cloned. + // The default function pointer is used if the function is cloned. + // The first element is the size of the array, which should **NOT** be used as the number + // of julia functions in the sysimg. + // Each entry in this array uniquely identifies a function we are interested in + // (the function may have multiple function pointers corresponding to different versions). + // In other sysimg info, all references to functions are stored as their `uint32_t` index + // in this array. const int32_t *fvar_offsets; + // This is the mapping of shard function index -> global function index + // staticdata.c relies on the same order of functions in the global function array being + // the same as what it saw when serializing the global function array. However, partitioning + // into multiple shards will cause functions to be reordered. This array is used to map + // back to the original function array for loading. const uint32_t *fvar_idxs; + // This is the base data pointer + // (all other data pointers in this shard are stored as offsets to this address) uintptr_t *gvar_base; + // This array of global variable offsets (`int32_t`) from the base pointer. + // Similar to fvar_offsets, but for gvars const int32_t *gvar_offsets; + // This is the mapping of shard global variable index -> global global variable index + // Similar to fvar_idxs, but for gvars const uint32_t *gvar_idxs; + + // location and index of relocation slots. + // Stored as pairs of function indices and `int32_t` offsets from `jl_sysimg_gvars_base`. + // The first element is an `uint32_t` giving the number of relocations. + // This is needed for functions whose address is used in a way that requires dispatch. + // We currently only support one type of relocation (i.e. absolute pointer) which is enough + // for all use in functions as well as GOT slot (for "PLT" callback). + // Note that not all functions being cloned are assigned a slot. + // This array is sorted by the function indices. + // There can be more than one slot per-function, + // i.e. there can be duplicated function indices. const int32_t *clone_slots; + // Target-specific function pointer offsets. + // This contains all the cloned functions that we are interested in and it needs to be decoded + // and used along with `jl_dispatch_fvars_idxs`. + // For the default target, there's no entries in this variable, if there's any relocations + // needed for the default target, the function pointers are taken from the global offset + // arrays directly. + // For a `clone_all` target (i.e. with the length in `jl_dispatch_fvars_idxs` tagged), this + // variable contains an offset array of the same length as the global one. Only the indices + // appearing in `jl_dispatch_fvars_idxs` need relocation and the dispatch code should return + // this array as the original/base function offsets. + // For other targets, this variable contains an offset array with the length defined in + // `jl_dispatch_fvars_idxs`. Tagged indices need relocations. const int32_t *clone_offsets; + // Target-specific function indices. + // For each target, this includes a tagged `uint32_t` length, an optional `uint32_t` index + // of the base target followed by an array of tagged function indices. + // The base target index is required to be smaller than the index of the current target + // and must be the default (`0`) or a `clone_all` target. + // If it's not `0`, the function pointer array for the `clone_all` target will be used as + // the base function pointer offsets instead. + // The tag bits for both the length and the indices are the top bit. + // A tagged length indicates that all of the functions are cloned and the indices follows + // are the ones that requires relocation. The base target index is omitted in this case. + // Otherwise, the length is the total number of functions that we are interested in + // for this target, which includes all cloned julia functions and + // all other cloned functions that requires relocation. + // A tagged index means that the function pointer should be filled into the GOT slots + // identified by `jl_dispatch_reloc_slots`. There could be more than one slot per function. + // (Note that a tagged index could corresponds to a functions pointer that's the same as + // the base one since this is the only way we currently represent relocations.) + // A tagged length implicitly tags all the indices and the indices will not have the tag bit + // set. The lengths in this variable is needed to decode `jl_dispatch_fvars_offsets`. const uint32_t *clone_idxs; } jl_image_shard_t; +// The TLS data for each image typedef struct { void *pgcstack_func_slot; void *pgcstack_key_slot; size_t *tls_offset; } jl_image_ptls_t; +//The root struct for images, points to all the other globals typedef struct { + // The image header, contains numerical global data const jl_image_header_t *header; - const jl_image_shard_t *shards; // nshards-length array + // The shard table, contains per-shard data + const jl_image_shard_t *shards; // points to header->nshards length array + // The TLS data const jl_image_ptls_t *ptls; + + // serialized target data + // This contains the number of targets + // in addition to the name and feature set of each target. const void *target_data; } jl_image_pointers_t; From 5108b4036d7610fff1ef6e56c80f760a05d2c4d0 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Mon, 6 Mar 2023 00:38:41 -0500 Subject: [PATCH 258/775] Update documentation --- doc/src/devdocs/sysimg.md | 3 +++ doc/src/manual/environment-variables.md | 15 ++++++++++++++- src/aotcompile.cpp | 6 +++--- src/processor.h | 6 ++---- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/doc/src/devdocs/sysimg.md b/doc/src/devdocs/sysimg.md index 3058834e927d0..6706e30ce97b1 100644 --- a/doc/src/devdocs/sysimg.md +++ b/doc/src/devdocs/sysimg.md @@ -8,6 +8,9 @@ as many platforms as possible, so as to give vastly improved startup times. On not ship with a precompiled system image file, one can be generated from the source files shipped in Julia's `DATAROOTDIR/julia/base` folder. +Julia will by default generate its system image on half of the available system threads. This +may be controlled by the [`JULIA_IMAGE_THREADS`](@ref env-image-threads) environment variable. + This operation is useful for multiple reasons. A user may: * Build a precompiled shared library system image on a platform that did not ship with one, thereby diff --git a/doc/src/manual/environment-variables.md b/doc/src/manual/environment-variables.md index a199112e934dd..a5f4efc28e965 100644 --- a/doc/src/manual/environment-variables.md +++ b/doc/src/manual/environment-variables.md @@ -277,7 +277,7 @@ To use Visual Studio Code on Windows, set `$JULIA_EDITOR` to `code.cmd`. ## Parallelization -### `JULIA_CPU_THREADS` +### [`JULIA_CPU_THREADS`](@id env-cpu-threads) Overrides the global variable [`Base.Sys.CPU_THREADS`](@ref), the number of logical CPU cores available. @@ -316,6 +316,19 @@ then spinning threads never sleep. Otherwise, `$JULIA_THREAD_SLEEP_THRESHOLD` is interpreted as an unsigned 64-bit integer (`uint64_t`) and gives, in nanoseconds, the amount of time after which spinning threads should sleep. +### [`JULIA_IMAGE_THREADS`](@id env-image-threads) + +An unsigned 32-bit integer that sets the number of threads used by image +compilation in this Julia process. The value of this variable may be +ignored if the module is a small module. If left unspecified, the smaller +of the value of [`JULIA_CPU_THREADS`](@ref env-cpu-threads) or half the +number of logical CPU cores is used in its place. + +### `JULIA_IMAGE_TIMINGS` + +A boolean value that determines if detailed timing information is printed during +during image compilation. Defaults to 0. + ### `JULIA_EXCLUSIVE` If set to anything besides `0`, then Julia's thread policy is consistent with diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 0337602cde27e..dd49e6b466474 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -600,7 +600,7 @@ static void get_fvars_gvars(Module &M, DenseMap &fvars, // Weight computation // It is important for multithreaded image building to be able to split work up // among the threads equally. The weight calculated here is an estimation of -// how expensive a particular function is going to be to compile. +// how expensive a particular function is going to be to compile. struct FunctionInfo { size_t weight; @@ -1193,7 +1193,7 @@ static void construct_vars(Module &M, Partition &partition) { } // Materialization will leave many unused declarations, which multiversioning would otherwise clone. -// This function removes them to avoid unnecessary cloning of declarations. +// This function removes them to avoid unnecessary cloning of declarations. static void dropUnusedDeclarations(Module &M) { SmallVector unused; for (auto &G : M.global_values()) { @@ -1211,7 +1211,7 @@ static void dropUnusedDeclarations(Module &M) { } // Entrypoint to optionally-multithreaded image compilation. This handles global coordination of the threading, -// as well as partitioning, serialization, and deserialization. +// as well as partitioning, serialization, and deserialization. static void add_output(Module &M, TargetMachine &TM, std::vector &outputs, ArrayRef names, std::vector &unopt, std::vector &opt, std::vector &obj, std::vector &asm_, diff --git a/src/processor.h b/src/processor.h index 497a93d40e11f..d2280068fb67d 100644 --- a/src/processor.h +++ b/src/processor.h @@ -106,11 +106,10 @@ typedef struct { // Per-shard data for image shards. Each image contains header->nshards of these. typedef struct { - + // This is the base function pointer // (all other function pointers are stored as offsets to this address) const char *fvar_base; - // The array of function pointer offsets (`int32_t`) from the base pointer. // This includes all julia functions in sysimg as well as all other functions that are cloned. // The default function pointer is used if the function is cloned. @@ -125,7 +124,7 @@ typedef struct { // staticdata.c relies on the same order of functions in the global function array being // the same as what it saw when serializing the global function array. However, partitioning // into multiple shards will cause functions to be reordered. This array is used to map - // back to the original function array for loading. + // back to the original function array for loading. const uint32_t *fvar_idxs; // This is the base data pointer // (all other data pointers in this shard are stored as offsets to this address) @@ -136,7 +135,6 @@ typedef struct { // This is the mapping of shard global variable index -> global global variable index // Similar to fvar_idxs, but for gvars const uint32_t *gvar_idxs; - // location and index of relocation slots. // Stored as pairs of function indices and `int32_t` offsets from `jl_sysimg_gvars_base`. // The first element is an `uint32_t` giving the number of relocations. From a70bbdf29e7820569316d0a0d03c22757c910142 Mon Sep 17 00:00:00 2001 From: Udoh Jeremiah Date: Mon, 6 Mar 2023 16:08:36 +0100 Subject: [PATCH 259/775] doc: fix admonition syntax in ENV and withenv docstrings (#48909) --- base/env.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/env.jl b/base/env.jl index 10f57f3fb9dc7..a4a55d9dad013 100644 --- a/base/env.jl +++ b/base/env.jl @@ -74,7 +74,7 @@ all keys to uppercase for display, iteration, and copying. Portable code should ability to distinguish variables by case, and should beware that setting an ostensibly lowercase variable may result in an uppercase `ENV` key.) - !!! warning +!!! warning Mutating the environment is not thread-safe. # Examples @@ -222,7 +222,7 @@ by zero or more `"var"=>val` arguments `kv`. `withenv` is generally used via the environment variable (if it is set). When `withenv` returns, the original environment has been restored. - !!! warning +!!! warning Changing the environment is not thread-safe. For running external commands with a different environment from the parent process, prefer using [`addenv`](@ref) over `withenv`. """ From dc68ffcae356554bd18cde6a147e777c7690cf12 Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Mon, 6 Mar 2023 12:31:00 -0500 Subject: [PATCH 260/775] Extend `Threads.threadpoolsize` Allow specifying which thread pool's size to retrieve. --- base/threadingconstructs.jl | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl index 643cd95e57ebf..bb088ef8b01eb 100644 --- a/base/threadingconstructs.jl +++ b/base/threadingconstructs.jl @@ -66,15 +66,25 @@ Returns the number of threadpools currently configured. nthreadpools() = Int(unsafe_load(cglobal(:jl_n_threadpools, Cint))) """ - Threads.threadpoolsize() + Threads.threadpoolsize(pool::Symbol = :default) -> Int -Get the number of threads available to the Julia default worker-thread pool. +Get the number of threads available to the default thread pool (or to the +specified thread pool). See also: `BLAS.get_num_threads` and `BLAS.set_num_threads` in the [`LinearAlgebra`](@ref man-linalg) standard library, and `nprocs()` in the [`Distributed`](@ref man-distributed) standard library. """ -threadpoolsize() = Threads._nthreads_in_pool(Int8(0)) +function threadpoolsize(pool::Symbol = :default) + if pool === :default + tpid = Int8(0) + elseif pool === :interactive + tpid = Int8(1) + else + error("invalid threadpool specified") + end + return _nthreads_in_pool(tpid) +end function threading_run(fun, static) ccall(:jl_enter_threaded_region, Cvoid, ()) From 944dce960a8828a63eb9949001b0b7ae958139a0 Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Mon, 6 Mar 2023 12:34:29 -0500 Subject: [PATCH 261/775] Update `nthreads(pool)` Now returns `threadpoolsize(pool)`. --- base/threadingconstructs.jl | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl index bb088ef8b01eb..ccee6476f5a80 100644 --- a/base/threadingconstructs.jl +++ b/base/threadingconstructs.jl @@ -32,16 +32,7 @@ See also `BLAS.get_num_threads` and `BLAS.set_num_threads` in the [`LinearAlgebr man-linalg) standard library, and `nprocs()` in the [`Distributed`](@ref man-distributed) standard library and [`Threads.maxthreadid()`](@ref). """ -function nthreads(pool::Symbol) - if pool === :default - tpid = Int8(0) - elseif pool === :interactive - tpid = Int8(1) - else - error("invalid threadpool specified") - end - return _nthreads_in_pool(tpid) -end +nthreads(pool::Symbol) = threadpoolsize(pool) function _nthreads_in_pool(tpid::Int8) p = unsafe_load(cglobal(:jl_n_threads_per_pool, Ptr{Cint})) From 38727be9d7a1d1ca3a2cd407470cf6374ed3c2c6 Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Mon, 6 Mar 2023 12:44:15 -0500 Subject: [PATCH 262/775] Fix task thread pool assignment If a task is spawned with `:interactive` but there are no interactive threads, set the task's thread pool to `:default` so that we don't have to keep checking it in other places. --- base/threadingconstructs.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl index ccee6476f5a80..e7257759b15a9 100644 --- a/base/threadingconstructs.jl +++ b/base/threadingconstructs.jl @@ -344,7 +344,11 @@ macro spawn(args...) let $(letargs...) local task = Task($thunk) task.sticky = false - ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), task, $tpid) + local tpid_actual = $tpid + if _nthreads_in_pool(tpid_actual) == 0 + tpid_actual = Int8(0) + end + ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), task, tpid_actual) if $(Expr(:islocal, var)) put!($var, task) end From 015301ab59d7561ba42596e38574a74485c694cd Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Mon, 6 Mar 2023 20:21:38 +0100 Subject: [PATCH 263/775] avoid some invalidations from untyped dict code in TOML print (#48908) --- stdlib/TOML/src/print.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/TOML/src/print.jl b/stdlib/TOML/src/print.jl index f5bef8344f64f..1fa9f97405504 100644 --- a/stdlib/TOML/src/print.jl +++ b/stdlib/TOML/src/print.jl @@ -171,7 +171,8 @@ function print_table(f::MbyFunc, io::IO, a::AbstractDict, end if is_table(value) push!(ks, String(key)) - header = isempty(value) || !all(is_tabular(v) for v in values(value))::Bool + _values = @invokelatest values(value) + header = isempty(value) || !all(is_tabular(v) for v in _values)::Bool if header # print table first_block || println(io) From 1fe6f2b03b38976f9e940aa188b73209ef6b30e0 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Mon, 6 Mar 2023 02:25:34 -0500 Subject: [PATCH 264/775] Remove most ifdefs from codegen --- src/aotcompile.cpp | 10 ++++- src/ccall.cpp | 72 ++++++++++++++++++-------------- src/codegen.cpp | 101 ++++++++++++++++++++++++--------------------- src/jitlayers.cpp | 60 ++++++++++++++++----------- src/jitlayers.h | 17 ++++---- 5 files changed, 147 insertions(+), 113 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index dd49e6b466474..79cba30e5f23b 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -300,7 +300,10 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm // compile all methods for the current world and type-inference world JL_LOCK(&jl_codegen_lock); - jl_codegen_params_t params(ctxt); + auto target_info = clone.withModuleDo([&](Module &M) { + return std::make_pair(M.getDataLayout(), Triple(M.getTargetTriple())); + }); + jl_codegen_params_t params(ctxt, std::move(target_info.first), std::move(target_info.second)); params.params = cgparams; params.imaging = imaging; params.external_linkage = _external_linkage; @@ -2027,7 +2030,10 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, siz if (measure_compile_time_enabled) compiler_start_time = jl_hrtime(); JL_LOCK(&jl_codegen_lock); - jl_codegen_params_t output(*ctx); + auto target_info = m.withModuleDo([&](Module &M) { + return std::make_pair(M.getDataLayout(), Triple(M.getTargetTriple())); + }); + jl_codegen_params_t output(*ctx, std::move(target_info.first), std::move(target_info.second)); output.world = world; output.params = ¶ms; auto decls = jl_emit_code(m, mi, src, jlrettype, output); diff --git a/src/ccall.cpp b/src/ccall.cpp index 2dea1e07ca45b..3daff76bc262e 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -57,7 +57,7 @@ GlobalVariable *jl_emit_RTLD_DEFAULT_var(Module *M) static bool runtime_sym_gvs(jl_codectx_t &ctx, const char *f_lib, const char *f_name, GlobalVariable *&lib, GlobalVariable *&sym) { - auto M = &ctx.emission_context.shared_module(*jl_Module); + auto M = &ctx.emission_context.shared_module(); bool runtime_lib = false; GlobalVariable *libptrgv; jl_codegen_params_t::SymMapGV *symMap; @@ -236,7 +236,7 @@ static GlobalVariable *emit_plt_thunk( bool runtime_lib) { ++PLTThunks; - auto M = &ctx.emission_context.shared_module(*jl_Module); + auto M = &ctx.emission_context.shared_module(); PointerType *funcptype = PointerType::get(functype, 0); libptrgv = prepare_global_in(M, libptrgv); llvmgv = prepare_global_in(M, llvmgv); @@ -279,14 +279,13 @@ static GlobalVariable *emit_plt_thunk( else { // musttail support is very bad on ARM, PPC, PPC64 (as of LLVM 3.9) // Known failures includes vararg (not needed here) and sret. - -#if (defined(_CPU_X86_) || defined(_CPU_X86_64_) || (defined(_CPU_AARCH64_) && !defined(_OS_DARWIN_))) - // Ref https://bugs.llvm.org/show_bug.cgi?id=47058 - // LLVM, as of 10.0.1 emits wrong/worse code when musttail is set - // Apple silicon macs give an LLVM ERROR if musttail is set here #44107. - if (!attrs.hasAttrSomewhere(Attribute::ByVal)) - ret->setTailCallKind(CallInst::TCK_MustTail); -#endif + if (ctx.emission_context.TargetTriple.isX86() || (ctx.emission_context.TargetTriple.isAArch64() && !ctx.emission_context.TargetTriple.isOSDarwin())) { + // Ref https://bugs.llvm.org/show_bug.cgi?id=47058 + // LLVM, as of 10.0.1 emits wrong/worse code when musttail is set + // Apple silicon macs give an LLVM ERROR if musttail is set here #44107. + if (!attrs.hasAttrSomewhere(Attribute::ByVal)) + ret->setTailCallKind(CallInst::TCK_MustTail); + } if (functype->getReturnType() == getVoidTy(irbuilder.getContext())) { irbuilder.CreateRetVoid(); } @@ -1512,22 +1511,28 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) assert(lrt == getVoidTy(ctx.builder.getContext())); assert(!isVa && !llvmcall && nccallargs == 0); #ifdef __MIC__ - // TODO -#elif defined(_CPU_X86_64_) || defined(_CPU_X86_) /* !__MIC__ */ - auto pauseinst = InlineAsm::get(FunctionType::get(getVoidTy(ctx.builder.getContext()), false), "pause", - "~{memory}", true); - ctx.builder.CreateCall(pauseinst); - JL_GC_POP(); - return ghostValue(ctx, jl_nothing_type); -#elif defined(_CPU_AARCH64_) || (defined(_CPU_ARM_) && __ARM_ARCH >= 7) - auto wfeinst = InlineAsm::get(FunctionType::get(getVoidTy(ctx.builder.getContext()), false), "wfe", - "~{memory}", true); - ctx.builder.CreateCall(wfeinst); - JL_GC_POP(); - return ghostValue(ctx, jl_nothing_type); + //TODO #else - JL_GC_POP(); - return ghostValue(ctx, jl_nothing_type); + if (ctx.emission_context.TargetTriple.isX86()) { + auto pauseinst = InlineAsm::get(FunctionType::get(getVoidTy(ctx.builder.getContext()), false), "pause", + "~{memory}", true); + ctx.builder.CreateCall(pauseinst); + JL_GC_POP(); + return ghostValue(ctx, jl_nothing_type); + } else if (ctx.emission_context.TargetTriple.isAArch64() + || (ctx.emission_context.TargetTriple.isARM() + && ctx.emission_context.TargetTriple.getSubArch() != Triple::SubArchType::NoSubArch + // ARMv7 and above is < armv6 + && ctx.emission_context.TargetTriple.getSubArch() < Triple::SubArchType::ARMSubArch_v6)) { + auto wfeinst = InlineAsm::get(FunctionType::get(getVoidTy(ctx.builder.getContext()), false), "wfe", + "~{memory}", true); + ctx.builder.CreateCall(wfeinst); + JL_GC_POP(); + return ghostValue(ctx, jl_nothing_type); + } else { + JL_GC_POP(); + return ghostValue(ctx, jl_nothing_type); + } #endif } else if (is_libjulia_func(jl_cpu_wake)) { @@ -1538,13 +1543,18 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) #if JL_CPU_WAKE_NOOP == 1 JL_GC_POP(); return ghostValue(ctx, jl_nothing_type); -#elif defined(_CPU_AARCH64_) || (defined(_CPU_ARM_) && __ARM_ARCH >= 7) - auto sevinst = InlineAsm::get(FunctionType::get(getVoidTy(ctx.builder.getContext()), false), "sev", - "~{memory}", true); - ctx.builder.CreateCall(sevinst); - JL_GC_POP(); - return ghostValue(ctx, jl_nothing_type); #endif + if (ctx.emission_context.TargetTriple.isAArch64() + || (ctx.emission_context.TargetTriple.isARM() + && ctx.emission_context.TargetTriple.getSubArch() != Triple::SubArchType::NoSubArch + // ARMv7 and above is < armv6 + && ctx.emission_context.TargetTriple.getSubArch() < Triple::SubArchType::ARMSubArch_v6)) { + auto sevinst = InlineAsm::get(FunctionType::get(getVoidTy(ctx.builder.getContext()), false), "sev", + "~{memory}", true); + ctx.builder.CreateCall(sevinst); + JL_GC_POP(); + return ghostValue(ctx, jl_nothing_type); + } } else if (is_libjulia_func(jl_gc_safepoint)) { ++CCALL_STAT(jl_gc_safepoint); diff --git a/src/codegen.cpp b/src/codegen.cpp index 47b9519bbb2f0..f71684340b3ee 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6,9 +6,6 @@ #if defined(_CPU_X86_) #define JL_NEED_FLOATTEMP_VAR 1 #endif -#if defined(_OS_LINUX_) || defined(_OS_WINDOWS_) || defined(_OS_FREEBSD_) || defined(_COMPILER_MSAN_ENABLED_) -#define JL_DISABLE_FPO -#endif #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS @@ -88,6 +85,17 @@ using namespace llvm; +static bool jl_fpo_disabled(const Triple &TT) { +#ifdef _COMPILER_MSAN_ENABLED_ + // MSAN doesn't support FPO + return true; +#endif + if (TT.isOSLinux() || TT.isOSWindows() || TT.isOSFreeBSD()) { + return true; + } + return false; +} + //Drag some useful type functions into our namespace //to reduce verbosity of our code auto getInt1Ty(LLVMContext &ctxt) { @@ -2300,19 +2308,20 @@ std::unique_ptr jl_create_llvm_module(StringRef name, LLVMContext &conte m->setDataLayout(DL); m->setTargetTriple(triple.str()); -#if defined(_OS_WINDOWS_) && !defined(_CPU_X86_64_) && JL_LLVM_VERSION >= 130000 - // tell Win32 to assume the stack is always 16-byte aligned, - // and to ensure that it is 16-byte aligned for out-going calls, - // to ensure compatibility with GCC codes - m->setOverrideStackAlignment(16); -#endif + if (triple.isOSWindows() && triple.getArch() == Triple::x86) { + // tell Win32 to assume the stack is always 16-byte aligned, + // and to ensure that it is 16-byte aligned for out-going calls, + // to ensure compatibility with GCC codes + m->setOverrideStackAlignment(16); + } + #if defined(JL_DEBUG_BUILD) && JL_LLVM_VERSION >= 130000 m->setStackProtectorGuard("global"); #endif return m; } -static void jl_init_function(Function *F) +static void jl_init_function(Function *F, const Triple &TT) { // set any attributes that *must* be set on all functions #if JL_LLVM_VERSION >= 140000 @@ -2320,27 +2329,27 @@ static void jl_init_function(Function *F) #else AttrBuilder attr; #endif -#if defined(_OS_WINDOWS_) && !defined(_CPU_X86_64_) - // tell Win32 to realign the stack to the next 16-byte boundary - // upon entry to any function. This achieves compatibility - // with both MinGW-GCC (which assumes an 16-byte-aligned stack) and - // i686 Windows (which uses a 4-byte-aligned stack) - attr.addStackAlignmentAttr(16); -#endif -#if defined(_OS_WINDOWS_) && defined(_CPU_X86_64_) - attr.addAttribute(Attribute::UWTable); // force NeedsWinEH -#endif -#ifdef JL_DISABLE_FPO - attr.addAttribute("frame-pointer", "all"); -#endif -#if !defined(_COMPILER_ASAN_ENABLED_) && !defined(_OS_WINDOWS_) - // ASAN won't like us accessing undefined memory causing spurious issues, - // and Windows has platform-specific handling which causes it to mishandle - // this annotation. Other platforms should just ignore this if they don't - // implement it. - attr.addAttribute("probe-stack", "inline-asm"); - //attr.addAttribute("stack-probe-size", "4096"); // can use this to change the default + if (TT.isOSWindows() && TT.getArch() == Triple::x86) { + // tell Win32 to assume the stack is always 16-byte aligned, + // and to ensure that it is 16-byte aligned for out-going calls, + // to ensure compatibility with GCC codes + attr.addStackAlignmentAttr(16); + } + if (TT.isOSWindows() && TT.getArch() == Triple::x86_64) { + attr.addAttribute(Attribute::UWTable); // force NeedsWinEH + } + if (jl_fpo_disabled(TT)) + attr.addAttribute("frame-pointer", "all"); + if (!TT.isOSWindows()) { +#if !defined(_COMPILER_ASAN_ENABLED_) + // ASAN won't like us accessing undefined memory causing spurious issues, + // and Windows has platform-specific handling which causes it to mishandle + // this annotation. Other platforms should just ignore this if they don't + // implement it. + attr.addAttribute("probe-stack", "inline-asm"); + //attr.addAttribute("stack-probe-size", "4096"); // can use this to change the default #endif + } #if defined(_COMPILER_ASAN_ENABLED_) attr.addAttribute(Attribute::SanitizeAddress); #endif @@ -5168,7 +5177,7 @@ static std::pair get_oc_function(jl_codectx_t &ctx, jl_met F = Function::Create(get_func_sig(ctx.builder.getContext()), Function::ExternalLinkage, fname, jl_Module); - jl_init_function(F); + jl_init_function(F, ctx.emission_context.TargetTriple); F->setAttributes(AttributeList::get(ctx.builder.getContext(), {get_func_attrs(ctx.builder.getContext()), F->getAttributes()})); } Function *specF = NULL; @@ -5640,7 +5649,7 @@ static Function *emit_tojlinvoke(jl_code_instance_t *codeinst, Module *M, jl_cod Function *f = Function::Create(ctx.types().T_jlfunc, GlobalVariable::InternalLinkage, name, M); - jl_init_function(f); + jl_init_function(f, params.TargetTriple); //f->setAlwaysInline(); ctx.f = f; // for jl_Module BasicBlock *b0 = BasicBlock::Create(ctx.builder.getContext(), "top", f); @@ -5914,7 +5923,7 @@ static Function* gen_cfun_wrapper( Function *cw = Function::Create(functype, GlobalVariable::ExternalLinkage, funcName, M); - jl_init_function(cw); + jl_init_function(cw, params.TargetTriple); cw->setAttributes(AttributeList::get(M->getContext(), {attributes, cw->getAttributes()})); jl_codectx_t ctx(M->getContext(), params); @@ -6123,7 +6132,7 @@ static Function* gen_cfun_wrapper( if (!theFptr) { theFptr = Function::Create(ctx.types().T_jlfunc, GlobalVariable::ExternalLinkage, fname, jl_Module); - jl_init_function(theFptr); + jl_init_function(theFptr, ctx.emission_context.TargetTriple); addRetAttr(theFptr, Attribute::NonNull); } else { @@ -6226,7 +6235,7 @@ static Function* gen_cfun_wrapper( funcName += "_gfthunk"; Function *gf_thunk = Function::Create(returninfo.decl->getFunctionType(), GlobalVariable::InternalLinkage, funcName, M); - jl_init_function(gf_thunk); + jl_init_function(gf_thunk, ctx.emission_context.TargetTriple); gf_thunk->setAttributes(AttributeList::get(M->getContext(), {returninfo.decl->getAttributes(), gf_thunk->getAttributes()})); // build a specsig -> jl_apply_generic converter thunk // this builds a method that calls jl_apply_generic (as a closure over a singleton function pointer), @@ -6320,7 +6329,7 @@ static Function* gen_cfun_wrapper( FunctionType::get(getInt8PtrTy(ctx.builder.getContext()), { getInt8PtrTy(ctx.builder.getContext()), ctx.types().T_ppjlvalue }, false), GlobalVariable::ExternalLinkage, funcName, M); - jl_init_function(cw_make); + jl_init_function(cw_make, ctx.emission_context.TargetTriple); BasicBlock *b0 = BasicBlock::Create(ctx.builder.getContext(), "top", cw_make); IRBuilder<> cwbuilder(b0); Function::arg_iterator AI = cw_make->arg_begin(); @@ -6432,12 +6441,12 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con } bool nest = (!fexpr_rt.constant || unionall_env); -#if defined(_CPU_AARCH64_) || defined(_CPU_ARM_) || defined(_CPU_PPC64_) - if (nest) { - emit_error(ctx, "cfunction: closures are not supported on this platform"); - return jl_cgval_t(); + if (ctx.emission_context.TargetTriple.isAArch64() || ctx.emission_context.TargetTriple.isARM() || ctx.emission_context.TargetTriple.isPPC64()) { + if (nest) { + emit_error(ctx, "cfunction: closures are not supported on this platform"); + return jl_cgval_t(); + } } -#endif size_t world = jl_atomic_load_acquire(&jl_world_counter); size_t min_valid = 0; size_t max_valid = ~(size_t)0; @@ -6562,7 +6571,7 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret { ++GeneratedInvokeWrappers; Function *w = Function::Create(get_func_sig(M->getContext()), GlobalVariable::ExternalLinkage, funcName, M); - jl_init_function(w); + jl_init_function(w, params.TargetTriple); w->setAttributes(AttributeList::get(M->getContext(), {get_func_attrs(M->getContext()), w->getAttributes()})); Function::arg_iterator AI = w->arg_begin(); Value *funcArg = &*AI++; @@ -6828,7 +6837,7 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, String Function *f = M ? cast_or_null(M->getNamedValue(name)) : NULL; if (f == NULL) { f = Function::Create(ftype, GlobalVariable::ExternalLinkage, name, M); - jl_init_function(f); + jl_init_function(f, ctx.emission_context.TargetTriple); f->setAttributes(AttributeList::get(f->getContext(), {attributes, f->getAttributes()})); } else { @@ -7055,7 +7064,7 @@ static jl_llvm_functions_t returninfo = get_specsig_function(ctx, M, declarations.specFunctionObject, lam->specTypes, jlrettype, ctx.is_opaque_closure); f = returninfo.decl; has_sret = (returninfo.cc == jl_returninfo_t::SRet || returninfo.cc == jl_returninfo_t::Union); - jl_init_function(f); + jl_init_function(f, ctx.emission_context.TargetTriple); // common pattern: see if all return statements are an argument in that // case the apply-generic call can re-use the original box for the return @@ -7093,7 +7102,7 @@ static jl_llvm_functions_t f = Function::Create(needsparams ? ctx.types().T_jlfuncparams : ctx.types().T_jlfunc, GlobalVariable::ExternalLinkage, declarations.specFunctionObject, M); - jl_init_function(f); + jl_init_function(f, ctx.emission_context.TargetTriple); f->setAttributes(AttributeList::get(ctx.builder.getContext(), {get_func_attrs(ctx.builder.getContext()), f->getAttributes()})); returninfo.decl = f; declarations.functionObject = needsparams ? "jl_fptr_sparam" : "jl_fptr_args"; @@ -8633,7 +8642,7 @@ void jl_compile_workqueue( Function *preal = emit_tojlinvoke(codeinst, mod, params); protodecl->setLinkage(GlobalVariable::InternalLinkage); //protodecl->setAlwaysInline(); - jl_init_function(protodecl); + jl_init_function(protodecl, params.TargetTriple); size_t nrealargs = jl_nparams(codeinst->def->specTypes); // number of actual arguments being passed // TODO: maybe this can be cached in codeinst->specfptr? emit_cfunc_invalidate(protodecl, proto_cc, proto_return_roots, codeinst->def->specTypes, codeinst->rettype, nrealargs, params, preal); diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 95c9cf2aaa86c..90f01a5b4f731 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -194,13 +194,13 @@ static jl_callptr_t _jl_compile_codeinst( jl_callptr_t fptr = NULL; // emit the code in LLVM IR form - jl_codegen_params_t params(std::move(context)); // Locks the context + jl_codegen_params_t params(std::move(context), jl_ExecutionEngine->getDataLayout(), jl_ExecutionEngine->getTargetTriple()); // Locks the context params.cache = true; params.world = world; jl_workqueue_t emitted; { orc::ThreadSafeModule result_m = - jl_create_ts_module(name_from_method_instance(codeinst->def), params.tsctx, params.imaging); + jl_create_ts_module(name_from_method_instance(codeinst->def), params.tsctx, params.imaging, params.DL, params.TargetTriple); jl_llvm_functions_t decls = jl_emit_codeinst(result_m, codeinst, src, params); if (result_m) emitted[codeinst] = {std::move(result_m), std::move(decls)}; @@ -334,7 +334,10 @@ int jl_compile_extern_c_impl(LLVMOrcThreadSafeModuleRef llvmmod, void *p, void * into = &backing; } JL_LOCK(&jl_codegen_lock); - jl_codegen_params_t params(into->getContext()); + auto target_info = into->withModuleDo([&](Module &M) { + return std::make_pair(M.getDataLayout(), Triple(M.getTargetTriple())); + }); + jl_codegen_params_t params(into->getContext(), std::move(target_info.first), std::move(target_info.second)); if (pparams == NULL) pparams = ¶ms; assert(pparams->tsctx.getContext() == into->getContext().getContext()); @@ -960,13 +963,24 @@ namespace { // use ELF because RuntimeDyld COFF X86_64 doesn't seem to work (fails to generate function pointers)? #define FORCE_ELF #endif + + Triple TheTriple(sys::getProcessTriple()); + bool force_elf = TheTriple.isOSWindows(); +#ifdef FORCE_ELF + force_elf = true; +#endif + if (force_elf) { + TheTriple.setObjectFormat(Triple::ELF); + } //options.PrintMachineCode = true; //Print machine code produced during JIT compiling -#if defined(_OS_WINDOWS_) && !defined(_CPU_X86_64_) && JL_LLVM_VERSION < 130000 - // tell Win32 to assume the stack is always 16-byte aligned, - // and to ensure that it is 16-byte aligned for out-going calls, - // to ensure compatibility with GCC codes - // In LLVM 13 and onwards this has turned into a module option - options.StackAlignmentOverride = 16; +#if JL_LLVM_VERSION < 130000 + if (TheTriple.isOSWindows() && TheTriple.getArch() == Triple::x86) { + // tell Win32 to assume the stack is always 16-byte aligned, + // and to ensure that it is 16-byte aligned for out-going calls, + // to ensure compatibility with GCC codes + // In LLVM 13 and onwards this has turned into a module option + options.StackAlignmentOverride = 16; + } #endif #if defined(JL_DEBUG_BUILD) && JL_LLVM_VERSION < 130000 // LLVM defaults to tls stack guard, which causes issues with Julia's tls implementation @@ -976,11 +990,6 @@ namespace { options.EmulatedTLS = true; options.ExplicitEmulatedTLS = true; #endif - - Triple TheTriple(sys::getProcessTriple()); -#if defined(FORCE_ELF) - TheTriple.setObjectFormat(Triple::ELF); -#endif uint32_t target_flags = 0; auto target = jl_get_llvm_target(imaging_default(), target_flags); auto &TheCPU = target.first; @@ -1033,11 +1042,11 @@ namespace { ); assert(TM && "Failed to select target machine -" " Is the LLVM backend for this CPU enabled?"); - #if (!defined(_CPU_ARM_) && !defined(_CPU_PPC64_)) - // FastISel seems to be buggy for ARM. Ref #13321 - if (jl_options.opt_level < 2) - TM->setFastISel(true); - #endif + if (!TheTriple.isARM() && !TheTriple.isPPC64()) { + // FastISel seems to be buggy for ARM. Ref #13321 + if (jl_options.opt_level < 2) + TM->setFastISel(true); + } return std::unique_ptr(TM); } } // namespace @@ -1741,11 +1750,12 @@ TargetIRAnalysis JuliaOJIT::getTargetIRAnalysis() const { } static void jl_decorate_module(Module &M) { -#if defined(_CPU_X86_64_) && defined(_OS_WINDOWS_) - // Add special values used by debuginfo to build the UnwindData table registration for Win64 - // This used to be GV, but with https://reviews.llvm.org/D100944 we no longer can emit GV into `.text` - // TODO: The data is set in debuginfo.cpp but it should be okay to actually emit it here. - M.appendModuleInlineAsm("\ + auto TT = Triple(M.getTargetTriple()); + if (TT.isOSWindows() && TT.getArch() == Triple::x86_64) { + // Add special values used by debuginfo to build the UnwindData table registration for Win64 + // This used to be GV, but with https://reviews.llvm.org/D100944 we no longer can emit GV into `.text` + // TODO: The data is set in debuginfo.cpp but it should be okay to actually emit it here. + M.appendModuleInlineAsm("\ .section .text \n\ .type __UnwindData,@object \n\ .p2align 2, 0x90 \n\ @@ -1758,7 +1768,7 @@ static void jl_decorate_module(Module &M) { __catchjmp: \n\ .zero 12 \n\ .size __catchjmp, 12"); -#endif + } } // Implements Tarjan's SCC (strongly connected components) algorithm, simplified to remove the count variable diff --git a/src/jitlayers.h b/src/jitlayers.h index 4cff48b93f7da..9532d06bdf9cf 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -182,6 +182,8 @@ typedef std::tuple, GlobalVariable*>> allPltMap; std::unique_ptr _shared_module; - inline Module &shared_module(Module &from); + inline Module &shared_module(); // inputs size_t world = 0; const jl_cgparams_t *params = &jl_default_cgparams; bool cache = false; bool external_linkage = false; bool imaging; - _jl_codegen_params_t(orc::ThreadSafeContext ctx) : tsctx(std::move(ctx)), tsctx_lock(tsctx.getLock()), imaging(imaging_default()) {} + _jl_codegen_params_t(orc::ThreadSafeContext ctx, DataLayout DL, Triple triple) + : tsctx(std::move(ctx)), tsctx_lock(tsctx.getLock()), + DL(std::move(DL)), TargetTriple(std::move(triple)), imaging(imaging_default()) {} } jl_codegen_params_t; jl_llvm_functions_t jl_emit_code( @@ -539,14 +543,9 @@ inline orc::ThreadSafeModule jl_create_ts_module(StringRef name, orc::ThreadSafe return orc::ThreadSafeModule(jl_create_llvm_module(name, *ctx.getContext(), imaging_mode, DL, triple), ctx); } -Module &jl_codegen_params_t::shared_module(Module &from) JL_NOTSAFEPOINT { +Module &jl_codegen_params_t::shared_module() JL_NOTSAFEPOINT { if (!_shared_module) { - _shared_module = jl_create_llvm_module("globals", getContext(), imaging, from.getDataLayout(), Triple(from.getTargetTriple())); - assert(&from.getContext() == tsctx.getContext() && "Module context differs from codegen_params context!"); - } else { - assert(&from.getContext() == &getContext() && "Module context differs from shared module context!"); - assert(from.getDataLayout() == _shared_module->getDataLayout() && "Module data layout differs from shared module data layout!"); - assert(from.getTargetTriple() == _shared_module->getTargetTriple() && "Module target triple differs from shared module target triple!"); + _shared_module = jl_create_llvm_module("globals", getContext(), imaging, DL, TargetTriple); } return *_shared_module; } From f456621d766d5aa386d5120ca5708feea43131d3 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Mon, 6 Mar 2023 02:41:10 -0500 Subject: [PATCH 265/775] RAUW getSizeTy(ctx.builder.getContext()) with ctx.types().T_size --- src/ccall.cpp | 70 ++++++++++++++-------------- src/cgutils.cpp | 68 +++++++++++++-------------- src/codegen.cpp | 114 +++++++++++++++++++++++---------------------- src/intrinsics.cpp | 16 +++---- 4 files changed, 135 insertions(+), 133 deletions(-) diff --git a/src/ccall.cpp b/src/ccall.cpp index 3daff76bc262e..bd2e2f5fd78e2 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -168,7 +168,7 @@ static Value *runtime_sym_lookup( } else { // f_lib is actually one of the special sentinel values - libname = ConstantExpr::getIntToPtr(ConstantInt::get(getSizeTy(irbuilder.getContext()), (uintptr_t)f_lib), getInt8PtrTy(irbuilder.getContext())); + libname = ConstantExpr::getIntToPtr(ConstantInt::get(emission_context.DL.getIntPtrType(irbuilder.getContext()), (uintptr_t)f_lib), getInt8PtrTy(irbuilder.getContext())); } llvmf = irbuilder.CreateCall(prepare_call_in(jl_builderModule(irbuilder), jldlsym_func), { libname, nameval, libptrgv }); @@ -469,7 +469,7 @@ static Value *runtime_apply_type_env(jl_codectx_t &ctx, jl_value_t *ty) ctx.builder.CreateInBoundsGEP( ctx.types().T_prjlvalue, ctx.spvals_ptr, - ConstantInt::get(getSizeTy(ctx.builder.getContext()), sizeof(jl_svec_t) / sizeof(jl_value_t*))) + ConstantInt::get(ctx.types().T_size, sizeof(jl_svec_t) / sizeof(jl_value_t*))) }; auto call = ctx.builder.CreateCall(prepare_call(jlapplytype_func), makeArrayRef(args)); addRetAttr(call, Attribute::getWithAlignment(ctx.builder.getContext(), Align(16))); @@ -605,7 +605,7 @@ static void interpret_symbol_arg(jl_codectx_t &ctx, native_sym_arg_t &out, jl_va emit_cpointercheck(ctx, arg1, errmsg); } arg1 = update_julia_type(ctx, arg1, (jl_value_t*)jl_voidpointer_type); - jl_ptr = emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), arg1, (jl_value_t*)jl_voidpointer_type); + jl_ptr = emit_unbox(ctx, ctx.types().T_size, arg1, (jl_value_t*)jl_voidpointer_type); } else { out.gcroot = ptr; @@ -693,7 +693,7 @@ static jl_cgval_t emit_cglobal(jl_codectx_t &ctx, jl_value_t **args, size_t narg else { rt = (jl_value_t*)jl_voidpointer_type; } - Type *lrt = getSizeTy(ctx.builder.getContext()); + Type *lrt = ctx.types().T_size; assert(lrt == julia_type_to_llvm(ctx, rt)); interpret_symbol_arg(ctx, sym, args[1], "cglobal", false); @@ -1467,7 +1467,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) (void)isVa; // prevent compiler warning if (is_libjulia_func(jl_array_ptr)) { ++CCALL_STAT(jl_array_ptr); - assert(lrt == getSizeTy(ctx.builder.getContext())); + assert(lrt == ctx.types().T_size); assert(!isVa && !llvmcall && nccallargs == 1); const jl_cgval_t &ary = argv[0]; JL_GC_POP(); @@ -1476,14 +1476,14 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) } else if (is_libjulia_func(jl_value_ptr)) { ++CCALL_STAT(jl_value_ptr); - assert(retboxed ? lrt == ctx.types().T_prjlvalue : lrt == getSizeTy(ctx.builder.getContext())); + assert(retboxed ? lrt == ctx.types().T_prjlvalue : lrt == ctx.types().T_size); assert(!isVa && !llvmcall && nccallargs == 1); jl_value_t *tti = jl_svecref(at, 0); Type *largty; bool isboxed; if (jl_is_abstract_ref_type(tti)) { tti = (jl_value_t*)jl_voidpointer_type; - largty = getSizeTy(ctx.builder.getContext()); + largty = ctx.types().T_size; isboxed = false; } else { @@ -1567,7 +1567,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) } else if (is_libjulia_func("jl_get_ptls_states")) { ++CCALL_STAT(jl_get_ptls_states); - assert(lrt == getSizeTy(ctx.builder.getContext())); + assert(lrt == ctx.types().T_size); assert(!isVa && !llvmcall && nccallargs == 0); JL_GC_POP(); return mark_or_box_ccall_result(ctx, @@ -1581,7 +1581,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) JL_GC_POP(); Value *ptask_i16 = emit_bitcast(ctx, get_current_task(ctx), getInt16PtrTy(ctx.builder.getContext())); const int tid_offset = offsetof(jl_task_t, tid); - Value *ptid = ctx.builder.CreateInBoundsGEP(getInt16Ty(ctx.builder.getContext()), ptask_i16, ConstantInt::get(getSizeTy(ctx.builder.getContext()), tid_offset / sizeof(int16_t))); + Value *ptid = ctx.builder.CreateInBoundsGEP(getInt16Ty(ctx.builder.getContext()), ptask_i16, ConstantInt::get(ctx.types().T_size, tid_offset / sizeof(int16_t))); LoadInst *tid = ctx.builder.CreateAlignedLoad(getInt16Ty(ctx.builder.getContext()), ptid, Align(sizeof(int16_t))); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); ai.decorateInst(tid); @@ -1595,7 +1595,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) JL_GC_POP(); Value *ptls_i32 = emit_bitcast(ctx, get_current_ptls(ctx), getInt32PtrTy(ctx.builder.getContext())); const int finh_offset = offsetof(jl_tls_states_t, finalizers_inhibited); - Value *pfinh = ctx.builder.CreateInBoundsGEP(getInt32Ty(ctx.builder.getContext()), ptls_i32, ConstantInt::get(getSizeTy(ctx.builder.getContext()), finh_offset / 4)); + Value *pfinh = ctx.builder.CreateInBoundsGEP(getInt32Ty(ctx.builder.getContext()), ptls_i32, ConstantInt::get(ctx.types().T_size, finh_offset / 4)); LoadInst *finh = ctx.builder.CreateAlignedLoad(getInt32Ty(ctx.builder.getContext()), pfinh, Align(sizeof(int32_t))); Value *newval; if (is_libjulia_func(jl_gc_disable_finalizers_internal)) { @@ -1624,7 +1624,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) JL_GC_POP(); Value *ptls_pv = emit_bitcast(ctx, get_current_ptls(ctx), ctx.types().T_ppjlvalue); const int nt_offset = offsetof(jl_tls_states_t, next_task); - Value *pnt = ctx.builder.CreateInBoundsGEP(ctx.types().T_pjlvalue, ptls_pv, ConstantInt::get(getSizeTy(ctx.builder.getContext()), nt_offset / sizeof(void*))); + Value *pnt = ctx.builder.CreateInBoundsGEP(ctx.types().T_pjlvalue, ptls_pv, ConstantInt::get(ctx.types().T_size, nt_offset / sizeof(void*))); ctx.builder.CreateStore(emit_pointer_from_objref(ctx, boxed(ctx, argv[0])), pnt); return ghostValue(ctx, jl_nothing_type); } @@ -1665,8 +1665,8 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) checkBB, contBB); ctx.builder.SetInsertPoint(checkBB); ctx.builder.CreateLoad( - getSizeTy(ctx.builder.getContext()), - ctx.builder.CreateConstInBoundsGEP1_32(getSizeTy(ctx.builder.getContext()), + ctx.types().T_size, + ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_size, get_current_signal_page_from_ptls(ctx.builder, get_current_ptls(ctx), ctx.tbaa().tbaa_const), -1), true); ctx.builder.CreateBr(contBB); @@ -1681,11 +1681,11 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) Value *len; if (svecv.constant && svecv.typ == (jl_value_t*)jl_simplevector_type) { // Check the type as well before we call - len = ConstantInt::get(getSizeTy(ctx.builder.getContext()), jl_svec_len(svecv.constant)); + len = ConstantInt::get(ctx.types().T_size, jl_svec_len(svecv.constant)); } else { auto ptr = emit_bitcast(ctx, boxed(ctx, svecv), getSizePtrTy(ctx.builder.getContext())); - len = ctx.builder.CreateAlignedLoad(getSizeTy(ctx.builder.getContext()), ptr, Align(sizeof(size_t))); + len = ctx.builder.CreateAlignedLoad(ctx.types().T_size, ptr, Align(sizeof(size_t))); // Only mark with TBAA if we are sure about the type. // This could otherwise be in a dead branch if (svecv.typ == (jl_value_t*)jl_simplevector_type) { @@ -1694,7 +1694,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) } MDBuilder MDB(ctx.builder.getContext()); auto rng = MDB.createRange( - Constant::getNullValue(getSizeTy(ctx.builder.getContext())), ConstantInt::get(getSizeTy(ctx.builder.getContext()), INTPTR_MAX / sizeof(void*) - 1)); + Constant::getNullValue(ctx.types().T_size), ConstantInt::get(ctx.types().T_size, INTPTR_MAX / sizeof(void*) - 1)); cast(len)->setMetadata(LLVMContext::MD_range, rng); } JL_GC_POP(); @@ -1706,8 +1706,8 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) assert(!isVa && !llvmcall && nccallargs == 2); const jl_cgval_t &svecv = argv[0]; const jl_cgval_t &idxv = argv[1]; - Value *idx = emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), idxv, (jl_value_t*)jl_long_type); - idx = ctx.builder.CreateAdd(idx, ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1)); + Value *idx = emit_unbox(ctx, ctx.types().T_size, idxv, (jl_value_t*)jl_long_type); + idx = ctx.builder.CreateAdd(idx, ConstantInt::get(ctx.types().T_size, 1)); auto ptr = emit_bitcast(ctx, boxed(ctx, svecv), ctx.types().T_pprjlvalue); Value *slot_addr = ctx.builder.CreateInBoundsGEP(ctx.types().T_prjlvalue, decay_derived(ctx, ptr), idx); @@ -1740,15 +1740,15 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) false, rt, unionall, static_rt); } else if (!jl_has_free_typevars(ety)) { - Value *idx = emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), idxv, (jl_value_t*)jl_ulong_type); + Value *idx = emit_unbox(ctx, ctx.types().T_size, idxv, (jl_value_t*)jl_ulong_type); Value *arrayptr = emit_bitcast(ctx, emit_arrayptr(ctx, aryv, aryex), ctx.types().T_pprjlvalue); if (!ptrarray) { size_t elsz = jl_datatype_size(ety); unsigned align = jl_datatype_align(ety); size_t stride = LLT_ALIGN(elsz, align) / sizeof(jl_value_t*); if (stride != 1) - idx = ctx.builder.CreateMul(idx, ConstantInt::get(getSizeTy(ctx.builder.getContext()), stride)); - idx = ctx.builder.CreateAdd(idx, ConstantInt::get(getSizeTy(ctx.builder.getContext()), ((jl_datatype_t*)ety)->layout->first_ptr)); + idx = ctx.builder.CreateMul(idx, ConstantInt::get(ctx.types().T_size, stride)); + idx = ctx.builder.CreateAdd(idx, ConstantInt::get(ctx.types().T_size, ((jl_datatype_t*)ety)->layout->first_ptr)); } Value *slot_addr = ctx.builder.CreateInBoundsGEP(ctx.types().T_prjlvalue, arrayptr, idx); LoadInst *load = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, slot_addr, Align(sizeof(void*))); @@ -1763,20 +1763,20 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) } else if (is_libjulia_func(jl_string_ptr)) { ++CCALL_STAT(jl_string_ptr); - assert(lrt == getSizeTy(ctx.builder.getContext())); + assert(lrt == ctx.types().T_size); assert(!isVa && !llvmcall && nccallargs == 1); auto obj = emit_bitcast(ctx, emit_pointer_from_objref(ctx, boxed(ctx, argv[0])), ctx.types().T_pprjlvalue); // The inbounds gep makes it more clear to LLVM that the resulting value is not // a null pointer. auto strp = ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_prjlvalue, obj, 1); - strp = ctx.builder.CreatePtrToInt(strp, getSizeTy(ctx.builder.getContext())); + strp = ctx.builder.CreatePtrToInt(strp, ctx.types().T_size); JL_GC_POP(); return mark_or_box_ccall_result(ctx, strp, retboxed, rt, unionall, static_rt); } else if (is_libjulia_func(jl_symbol_name)) { ++CCALL_STAT(jl_symbol_name); - assert(lrt == getSizeTy(ctx.builder.getContext())); + assert(lrt == ctx.types().T_size); assert(!isVa && !llvmcall && nccallargs == 1); auto obj = emit_bitcast(ctx, emit_pointer_from_objref(ctx, boxed(ctx, argv[0])), ctx.types().T_pprjlvalue); @@ -1784,7 +1784,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) // a null pointer. auto strp = ctx.builder.CreateConstInBoundsGEP1_32( ctx.types().T_prjlvalue, obj, (sizeof(jl_sym_t) + sizeof(void*) - 1) / sizeof(void*)); - strp = ctx.builder.CreatePtrToInt(strp, getSizeTy(ctx.builder.getContext())); + strp = ctx.builder.CreatePtrToInt(strp, ctx.types().T_size); JL_GC_POP(); return mark_or_box_ccall_result(ctx, strp, retboxed, rt, unionall, static_rt); } @@ -1793,16 +1793,16 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) const jl_cgval_t &dst = argv[0]; const jl_cgval_t &src = argv[1]; const jl_cgval_t &n = argv[2]; - Value *destp = emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), dst, (jl_value_t*)jl_voidpointer_type); + Value *destp = emit_unbox(ctx, ctx.types().T_size, dst, (jl_value_t*)jl_voidpointer_type); ctx.builder.CreateMemCpy( emit_inttoptr(ctx, destp, getInt8PtrTy(ctx.builder.getContext())), MaybeAlign(1), emit_inttoptr(ctx, - emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), src, (jl_value_t*)jl_voidpointer_type), + emit_unbox(ctx, ctx.types().T_size, src, (jl_value_t*)jl_voidpointer_type), getInt8PtrTy(ctx.builder.getContext())), MaybeAlign(0), - emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), n, (jl_value_t*)jl_ulong_type), + emit_unbox(ctx, ctx.types().T_size, n, (jl_value_t*)jl_ulong_type), false); JL_GC_POP(); return rt == (jl_value_t*)jl_nothing_type ? ghostValue(ctx, jl_nothing_type) : @@ -1813,13 +1813,13 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) const jl_cgval_t &dst = argv[0]; const jl_cgval_t &val = argv[1]; const jl_cgval_t &n = argv[2]; - Value *destp = emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), dst, (jl_value_t*)jl_voidpointer_type); + Value *destp = emit_unbox(ctx, ctx.types().T_size, dst, (jl_value_t*)jl_voidpointer_type); Value *val32 = emit_unbox(ctx, getInt32Ty(ctx.builder.getContext()), val, (jl_value_t*)jl_uint32_type); Value *val8 = ctx.builder.CreateTrunc(val32, getInt8Ty(ctx.builder.getContext()), "memset_val"); ctx.builder.CreateMemSet( emit_inttoptr(ctx, destp, getInt8PtrTy(ctx.builder.getContext())), val8, - emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), n, (jl_value_t*)jl_ulong_type), + emit_unbox(ctx, ctx.types().T_size, n, (jl_value_t*)jl_ulong_type), MaybeAlign(1) ); JL_GC_POP(); @@ -1831,16 +1831,16 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) const jl_cgval_t &dst = argv[0]; const jl_cgval_t &src = argv[1]; const jl_cgval_t &n = argv[2]; - Value *destp = emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), dst, (jl_value_t*)jl_voidpointer_type); + Value *destp = emit_unbox(ctx, ctx.types().T_size, dst, (jl_value_t*)jl_voidpointer_type); ctx.builder.CreateMemMove( emit_inttoptr(ctx, destp, getInt8PtrTy(ctx.builder.getContext())), MaybeAlign(0), emit_inttoptr(ctx, - emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), src, (jl_value_t*)jl_voidpointer_type), + emit_unbox(ctx, ctx.types().T_size, src, (jl_value_t*)jl_voidpointer_type), getInt8PtrTy(ctx.builder.getContext())), MaybeAlign(0), - emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), n, (jl_value_t*)jl_ulong_type), + emit_unbox(ctx, ctx.types().T_size, n, (jl_value_t*)jl_ulong_type), false); JL_GC_POP(); return rt == (jl_value_t*)jl_nothing_type ? ghostValue(ctx, jl_nothing_type) : @@ -1854,8 +1854,8 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) JL_GC_POP(); const int hash_offset = offsetof(jl_sym_t, hash); Value *ph1 = emit_bitcast(ctx, decay_derived(ctx, boxed(ctx, val)), getSizePtrTy(ctx.builder.getContext())); - Value *ph2 = ctx.builder.CreateInBoundsGEP(getSizeTy(ctx.builder.getContext()), ph1, ConstantInt::get(getSizeTy(ctx.builder.getContext()), hash_offset / sizeof(size_t))); - LoadInst *hashval = ctx.builder.CreateAlignedLoad(getSizeTy(ctx.builder.getContext()), ph2, Align(sizeof(size_t))); + Value *ph2 = ctx.builder.CreateInBoundsGEP(ctx.types().T_size, ph1, ConstantInt::get(ctx.types().T_size, hash_offset / sizeof(size_t))); + LoadInst *hashval = ctx.builder.CreateAlignedLoad(ctx.types().T_size, ph2, Align(sizeof(size_t))); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); ai.decorateInst(hashval); return mark_or_box_ccall_result(ctx, hashval, retboxed, rt, unionall, static_rt); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index f5dce85053ef5..6453798593e5b 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -521,7 +521,7 @@ static Value *maybe_bitcast(jl_codectx_t &ctx, Value *V, Type *to) { static Value *julia_binding_pvalue(jl_codectx_t &ctx, Value *bv) { bv = emit_bitcast(ctx, bv, ctx.types().T_pprjlvalue); - Value *offset = ConstantInt::get(getSizeTy(ctx.builder.getContext()), offsetof(jl_binding_t, value) / sizeof(size_t)); + Value *offset = ConstantInt::get(ctx.types().T_size, offsetof(jl_binding_t, value) / sizeof(size_t)); return ctx.builder.CreateInBoundsGEP(ctx.types().T_prjlvalue, bv, offset); } @@ -1085,7 +1085,7 @@ static Value *emit_typeof_boxed(jl_codectx_t &ctx, const jl_cgval_t &p, bool may static Value *emit_datatype_types(jl_codectx_t &ctx, Value *dt) { Value *Ptr = emit_bitcast(ctx, decay_derived(ctx, dt), ctx.types().T_ppjlvalue); - Value *Idx = ConstantInt::get(getSizeTy(ctx.builder.getContext()), offsetof(jl_datatype_t, types) / sizeof(void*)); + Value *Idx = ConstantInt::get(ctx.types().T_size, offsetof(jl_datatype_t, types) / sizeof(void*)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); return ai.decorateInst(ctx.builder.CreateAlignedLoad( ctx.types().T_pjlvalue, ctx.builder.CreateInBoundsGEP(ctx.types().T_pjlvalue, Ptr, Idx), Align(sizeof(void*)))); @@ -1095,17 +1095,17 @@ static Value *emit_datatype_nfields(jl_codectx_t &ctx, Value *dt) { Value *type_svec = emit_bitcast(ctx, emit_datatype_types(ctx, dt), getSizePtrTy(ctx.builder.getContext())); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); - return ai.decorateInst(ctx.builder.CreateAlignedLoad(getSizeTy(ctx.builder.getContext()), type_svec, Align(sizeof(void*)))); + return ai.decorateInst(ctx.builder.CreateAlignedLoad(ctx.types().T_size, type_svec, Align(sizeof(void*)))); } static Value *emit_datatype_size(jl_codectx_t &ctx, Value *dt) { jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); Value *Ptr = emit_bitcast(ctx, decay_derived(ctx, dt), getInt32PtrTy(ctx.builder.getContext())->getPointerTo()); - Value *Idx = ConstantInt::get(getSizeTy(ctx.builder.getContext()), offsetof(jl_datatype_t, layout) / sizeof(int32_t*)); + Value *Idx = ConstantInt::get(ctx.types().T_size, offsetof(jl_datatype_t, layout) / sizeof(int32_t*)); Ptr = ctx.builder.CreateInBoundsGEP(getInt32PtrTy(ctx.builder.getContext()), Ptr, Idx); Ptr = ai.decorateInst(ctx.builder.CreateAlignedLoad(getInt32PtrTy(ctx.builder.getContext()), Ptr, Align(sizeof(int32_t*)))); - Idx = ConstantInt::get(getSizeTy(ctx.builder.getContext()), offsetof(jl_datatype_layout_t, size) / sizeof(int32_t)); + Idx = ConstantInt::get(ctx.types().T_size, offsetof(jl_datatype_layout_t, size) / sizeof(int32_t)); Ptr = ctx.builder.CreateInBoundsGEP(getInt32Ty(ctx.builder.getContext()), Ptr, Idx); return ai.decorateInst(ctx.builder.CreateAlignedLoad(getInt32Ty(ctx.builder.getContext()), Ptr, Align(sizeof(int32_t)))); } @@ -1163,10 +1163,10 @@ static Value *emit_datatype_mutabl(jl_codectx_t &ctx, Value *dt) { jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); Value *Ptr = emit_bitcast(ctx, decay_derived(ctx, dt), ctx.types().T_ppint8); - Value *Idx = ConstantInt::get(getSizeTy(ctx.builder.getContext()), offsetof(jl_datatype_t, name)); + Value *Idx = ConstantInt::get(ctx.types().T_size, offsetof(jl_datatype_t, name)); Value *Nam = ai.decorateInst( ctx.builder.CreateAlignedLoad(getInt8PtrTy(ctx.builder.getContext()), ctx.builder.CreateInBoundsGEP(getInt8PtrTy(ctx.builder.getContext()), Ptr, Idx), Align(sizeof(int8_t*)))); - Value *Idx2 = ConstantInt::get(getSizeTy(ctx.builder.getContext()), offsetof(jl_typename_t, n_uninitialized) + sizeof(((jl_typename_t*)nullptr)->n_uninitialized)); + Value *Idx2 = ConstantInt::get(ctx.types().T_size, offsetof(jl_typename_t, n_uninitialized) + sizeof(((jl_typename_t*)nullptr)->n_uninitialized)); Value *mutabl = ai.decorateInst( ctx.builder.CreateAlignedLoad(getInt8Ty(ctx.builder.getContext()), ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), Nam, Idx2), Align(1))); mutabl = ctx.builder.CreateLShr(mutabl, 1); @@ -1191,7 +1191,7 @@ static Value *emit_datatype_name(jl_codectx_t &ctx, Value *dt) Value *vptr = ctx.builder.CreateInBoundsGEP( ctx.types().T_pjlvalue, emit_bitcast(ctx, maybe_decay_tracked(ctx, dt), ctx.types().T_ppjlvalue), - ConstantInt::get(getSizeTy(ctx.builder.getContext()), n)); + ConstantInt::get(ctx.types().T_size, n)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); return ai.decorateInst(ctx.builder.CreateAlignedLoad(ctx.types().T_pjlvalue, vptr, Align(sizeof(void*)))); } @@ -1627,7 +1627,7 @@ static bool bounds_check_enabled(jl_codectx_t &ctx, jl_value_t *inbounds) { static Value *emit_bounds_check(jl_codectx_t &ctx, const jl_cgval_t &ainfo, jl_value_t *ty, Value *i, Value *len, jl_value_t *boundscheck) { - Value *im1 = ctx.builder.CreateSub(i, ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1)); + Value *im1 = ctx.builder.CreateSub(i, ConstantInt::get(ctx.types().T_size, 1)); #if CHECK_BOUNDS==1 if (bounds_check_enabled(ctx, boundscheck)) { ++EmittedBoundschecks; @@ -2220,7 +2220,7 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, size_t nfields = jl_datatype_nfields(stt); bool maybe_null = (unsigned)stt->name->n_uninitialized != 0; auto idx0 = [&]() { - return emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(getSizeTy(ctx.builder.getContext()), nfields), inbounds); + return emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(ctx.types().T_size, nfields), inbounds); }; if (nfields == 0) { (void)idx0(); @@ -2344,7 +2344,7 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, return true; } else if (strct.isboxed) { - idx = ctx.builder.CreateSub(idx, ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1)); + idx = ctx.builder.CreateSub(idx, ConstantInt::get(ctx.types().T_size, 1)); Value *fld = ctx.builder.CreateCall(prepare_call(jlgetnthfieldchecked_func), { boxed(ctx, strct), idx }); *ret = mark_julia_type(ctx, fld, true, jl_any_type); return true; @@ -2422,7 +2422,7 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st addr = ctx.builder.CreateInBoundsGEP( getInt8Ty(ctx.builder.getContext()), emit_bitcast(ctx, staddr, getInt8PtrTy(ctx.builder.getContext())), - ConstantInt::get(getSizeTy(ctx.builder.getContext()), byte_offset)); + ConstantInt::get(ctx.types().T_size, byte_offset)); } else { addr = staddr; @@ -2621,7 +2621,7 @@ static Value *emit_arraysize(jl_codectx_t &ctx, const jl_cgval_t &tinfo, Value * MDNode *tbaa = ctx.tbaa().tbaa_arraysize; if (arraytype_constdim(tinfo.typ, &ndim)) { if (ndim == 0) - return ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1); + return ConstantInt::get(ctx.types().T_size, 1); if (ndim == 1) { if (auto d = dyn_cast(dim)) { if (d->getZExtValue() == 1) { @@ -2632,7 +2632,7 @@ static Value *emit_arraysize(jl_codectx_t &ctx, const jl_cgval_t &tinfo, Value * if (ndim > 1) { if (tinfo.constant && isa(dim)) { auto n = cast(dim)->getZExtValue() - 1; - return ConstantInt::get(getSizeTy(ctx.builder.getContext()), jl_array_dim(tinfo.constant, n)); + return ConstantInt::get(ctx.types().T_size, jl_array_dim(tinfo.constant, n)); } tbaa = ctx.tbaa().tbaa_const; } @@ -2643,9 +2643,9 @@ static Value *emit_arraysize(jl_codectx_t &ctx, const jl_cgval_t &tinfo, Value * auto load = emit_nthptr_recast(ctx, t, ctx.builder.CreateAdd(dim, ConstantInt::get(dim->getType(), o)), - tbaa, getSizeTy(ctx.builder.getContext())); + tbaa, ctx.types().T_size); MDBuilder MDB(ctx.builder.getContext()); - auto rng = MDB.createRange(Constant::getNullValue(getSizeTy(ctx.builder.getContext())), ConstantInt::get(getSizeTy(ctx.builder.getContext()), arraytype_maxsize(tinfo.typ))); + auto rng = MDB.createRange(Constant::getNullValue(ctx.types().T_size), ConstantInt::get(ctx.types().T_size, arraytype_maxsize(tinfo.typ))); load->setMetadata(LLVMContext::MD_range, rng); return load; } @@ -2667,10 +2667,10 @@ static Value *emit_arraylen_prim(jl_codectx_t &ctx, const jl_cgval_t &tinfo) MDNode *tbaa = ctx.tbaa().tbaa_arraylen; if (arraytype_constdim(ty, &ndim)) { if (ndim == 0) - return ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1); + return ConstantInt::get(ctx.types().T_size, 1); if (ndim != 1) { if (tinfo.constant) - return ConstantInt::get(getSizeTy(ctx.builder.getContext()), jl_array_len(tinfo.constant)); + return ConstantInt::get(ctx.types().T_size, jl_array_len(tinfo.constant)); tbaa = ctx.tbaa().tbaa_const; } } @@ -2679,10 +2679,10 @@ static Value *emit_arraylen_prim(jl_codectx_t &ctx, const jl_cgval_t &tinfo) Value *addr = ctx.builder.CreateStructGEP(ctx.types().T_jlarray, emit_bitcast(ctx, decay_derived(ctx, t), ctx.types().T_pjlarray), 1); //index (not offset) of length field in ctx.types().T_pjlarray - LoadInst *len = ctx.builder.CreateAlignedLoad(getSizeTy(ctx.builder.getContext()), addr, Align(sizeof(size_t))); + LoadInst *len = ctx.builder.CreateAlignedLoad(ctx.types().T_size, addr, Align(sizeof(size_t))); len->setOrdering(AtomicOrdering::NotAtomic); MDBuilder MDB(ctx.builder.getContext()); - auto rng = MDB.createRange(Constant::getNullValue(getSizeTy(ctx.builder.getContext())), ConstantInt::get(getSizeTy(ctx.builder.getContext()), arraytype_maxsize(tinfo.typ))); + auto rng = MDB.createRange(Constant::getNullValue(ctx.types().T_size), ConstantInt::get(ctx.types().T_size, arraytype_maxsize(tinfo.typ))); len->setMetadata(LLVMContext::MD_range, rng); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); return ai.decorateInst(len); @@ -2803,7 +2803,7 @@ static Value *emit_arrayoffset(jl_codectx_t &ctx, const jl_cgval_t &tinfo, int n static Value *emit_arraysize_for_unsafe_dim(jl_codectx_t &ctx, const jl_cgval_t &tinfo, jl_value_t *ex, size_t dim, size_t nd) { - return dim > nd ? ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1) : emit_arraysize(ctx, tinfo, ex, dim); + return dim > nd ? ConstantInt::get(ctx.types().T_size, 1) : emit_arraysize(ctx, tinfo, ex, dim); } // `nd == -1` means the dimension is unknown. @@ -2813,8 +2813,8 @@ static Value *emit_array_nd_index( { ++EmittedArrayNdIndex; Value *a = boxed(ctx, ainfo); - Value *i = Constant::getNullValue(getSizeTy(ctx.builder.getContext())); - Value *stride = ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1); + Value *i = Constant::getNullValue(ctx.types().T_size); + Value *stride = ConstantInt::get(ctx.types().T_size, 1); #if CHECK_BOUNDS==1 bool bc = bounds_check_enabled(ctx, inbounds); BasicBlock *failBB = NULL, *endBB = NULL; @@ -2825,11 +2825,11 @@ static Value *emit_array_nd_index( #endif Value **idxs = (Value**)alloca(sizeof(Value*) * nidxs); for (size_t k = 0; k < nidxs; k++) { - idxs[k] = emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), argv[k], (jl_value_t*)jl_long_type); // type asserted by caller + idxs[k] = emit_unbox(ctx, ctx.types().T_size, argv[k], (jl_value_t*)jl_long_type); // type asserted by caller } Value *ii = NULL; for (size_t k = 0; k < nidxs; k++) { - ii = ctx.builder.CreateSub(idxs[k], ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1)); + ii = ctx.builder.CreateSub(idxs[k], ConstantInt::get(ctx.types().T_size, 1)); i = ctx.builder.CreateAdd(i, ctx.builder.CreateMul(ii, stride)); if (k < nidxs - 1) { assert(nd >= 0); @@ -2874,23 +2874,23 @@ static Value *emit_array_nd_index( for (size_t k = nidxs+1; k < (size_t)nd; k++) { BasicBlock *dimsokBB = BasicBlock::Create(ctx.builder.getContext(), "dimsok"); Value *dim = emit_arraysize_for_unsafe_dim(ctx, ainfo, ex, k, nd); - ctx.builder.CreateCondBr(ctx.builder.CreateICmpEQ(dim, ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1)), dimsokBB, failBB); + ctx.builder.CreateCondBr(ctx.builder.CreateICmpEQ(dim, ConstantInt::get(ctx.types().T_size, 1)), dimsokBB, failBB); ctx.f->getBasicBlockList().push_back(dimsokBB); ctx.builder.SetInsertPoint(dimsokBB); } Value *dim = emit_arraysize_for_unsafe_dim(ctx, ainfo, ex, nd, nd); - ctx.builder.CreateCondBr(ctx.builder.CreateICmpEQ(dim, ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1)), endBB, failBB); + ctx.builder.CreateCondBr(ctx.builder.CreateICmpEQ(dim, ConstantInt::get(ctx.types().T_size, 1)), endBB, failBB); } ctx.f->getBasicBlockList().push_back(failBB); ctx.builder.SetInsertPoint(failBB); // CreateAlloca is OK here since we are on an error branch - Value *tmp = ctx.builder.CreateAlloca(getSizeTy(ctx.builder.getContext()), ConstantInt::get(getSizeTy(ctx.builder.getContext()), nidxs)); + Value *tmp = ctx.builder.CreateAlloca(ctx.types().T_size, ConstantInt::get(ctx.types().T_size, nidxs)); for (size_t k = 0; k < nidxs; k++) { - ctx.builder.CreateAlignedStore(idxs[k], ctx.builder.CreateInBoundsGEP(getSizeTy(ctx.builder.getContext()), tmp, ConstantInt::get(getSizeTy(ctx.builder.getContext()), k)), Align(sizeof(size_t))); + ctx.builder.CreateAlignedStore(idxs[k], ctx.builder.CreateInBoundsGEP(ctx.types().T_size, tmp, ConstantInt::get(ctx.types().T_size, k)), Align(sizeof(size_t))); } ctx.builder.CreateCall(prepare_call(jlboundserrorv_func), - { mark_callee_rooted(ctx, a), tmp, ConstantInt::get(getSizeTy(ctx.builder.getContext()), nidxs) }); + { mark_callee_rooted(ctx, a), tmp, ConstantInt::get(ctx.types().T_size, nidxs) }); ctx.builder.CreateUnreachable(); ctx.f->getBasicBlockList().push_back(endBB); @@ -3484,7 +3484,7 @@ static Value *emit_allocobj(jl_codectx_t &ctx, size_t static_size, Value *jt) ++EmittedAllocObjs; Value *current_task = get_current_task(ctx); Function *F = prepare_call(jl_alloc_obj_func); - auto call = ctx.builder.CreateCall(F, {current_task, ConstantInt::get(getSizeTy(ctx.builder.getContext()), static_size), maybe_decay_untracked(ctx, jt)}); + auto call = ctx.builder.CreateCall(F, {current_task, ConstantInt::get(ctx.types().T_size, static_size), maybe_decay_untracked(ctx, jt)}); call->setAttributes(F->getAttributes()); if (static_size > 0) { @@ -3571,7 +3571,7 @@ static jl_cgval_t emit_setfield(jl_codectx_t &ctx, addr = ctx.builder.CreateInBoundsGEP( getInt8Ty(ctx.builder.getContext()), emit_bitcast(ctx, addr, getInt8PtrTy(ctx.builder.getContext())), - ConstantInt::get(getSizeTy(ctx.builder.getContext()), byte_offset)); // TODO: use emit_struct_gep + ConstantInt::get(ctx.types().T_size, byte_offset)); // TODO: use emit_struct_gep } jl_value_t *jfty = jl_field_type(sty, idx0); if (!jl_field_isptr(sty, idx0) && jl_is_uniontype(jfty)) { @@ -3585,7 +3585,7 @@ static jl_cgval_t emit_setfield(jl_codectx_t &ctx, return jl_cgval_t(); Value *ptindex = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), emit_bitcast(ctx, addr, getInt8PtrTy(ctx.builder.getContext())), - ConstantInt::get(getSizeTy(ctx.builder.getContext()), fsz)); + ConstantInt::get(ctx.types().T_size, fsz)); if (needlock) emit_lockstate_value(ctx, strct, true); BasicBlock *ModifyBB = NULL; @@ -3885,7 +3885,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg ai.decorateInst(ctx.builder.CreateAlignedStore( ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0), ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), emit_bitcast(ctx, strct, getInt8PtrTy(ctx.builder.getContext())), - ConstantInt::get(getSizeTy(ctx.builder.getContext()), jl_field_offset(sty, i) + jl_field_size(sty, i) - 1)), + ConstantInt::get(ctx.types().T_size, jl_field_offset(sty, i) + jl_field_size(sty, i) - 1)), Align(1))); } } diff --git a/src/codegen.cpp b/src/codegen.cpp index f71684340b3ee..d24e6883ae6b4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -233,6 +233,7 @@ extern void _chkstk(void); // types struct jl_typecache_t { + Type *T_size; Type *T_jlvalue; Type *T_pjlvalue; Type *T_prjlvalue; @@ -255,13 +256,14 @@ struct jl_typecache_t { T_pjlarray(nullptr), T_jlfunc(nullptr), T_jlfuncparams(nullptr), T_sigatomic(nullptr), T_ppint8(nullptr), initialized(false) {} - void initialize(LLVMContext &context) { + void initialize(LLVMContext &context, const DataLayout &DL) { if (initialized) { return; } initialized = true; T_ppint8 = PointerType::get(getInt8PtrTy(context), 0); T_sigatomic = Type::getIntNTy(context, sizeof(sig_atomic_t) * 8); + T_size = DL.getIntPtrType(context); T_jlvalue = JuliaType::get_jlvalue_ty(context); T_pjlvalue = PointerType::get(T_jlvalue, 0); T_prjlvalue = PointerType::get(T_jlvalue, AddressSpace::Tracked); @@ -274,7 +276,7 @@ struct jl_typecache_t { assert(T_jlfuncparams != NULL); Type *vaelts[] = {PointerType::get(getInt8Ty(context), AddressSpace::Loaded) - , getSizeTy(context) + , T_size , getInt16Ty(context) , getInt16Ty(context) , getInt32Ty(context) @@ -1604,7 +1606,7 @@ class jl_codectx_t { params(params.params) { } jl_typecache_t &types() { - type_cache.initialize(builder.getContext()); + type_cache.initialize(builder.getContext(), emission_context.DL); return type_cache; } @@ -1811,7 +1813,7 @@ static void undef_derived_strct(jl_codectx_t &ctx, Value *ptr, jl_datatype_t *st static Value *emit_inttoptr(jl_codectx_t &ctx, Value *v, Type *ty) { - // Almost all of our inttoptr are generated due to representing `Ptr` with `getSizeTy(ctx.builder.getContext())` + // Almost all of our inttoptr are generated due to representing `Ptr` with `ctx.types().T_size` // in LLVM and most of these integers are generated from `ptrtoint` in the first place. if (auto I = dyn_cast(v)) { auto ptr = I->getOperand(0); @@ -2426,7 +2428,7 @@ JL_DLLEXPORT uint64_t *jl_malloc_data_pointer(StringRef filename, int line); static void visitLine(jl_codectx_t &ctx, uint64_t *ptr, Value *addend, const char *name) { Value *pv = ConstantExpr::getIntToPtr( - ConstantInt::get(getSizeTy(ctx.builder.getContext()), (uintptr_t)ptr), + ConstantInt::get(ctx.types().T_size, (uintptr_t)ptr), getInt64PtrTy(ctx.builder.getContext())); Value *v = ctx.builder.CreateLoad(getInt64Ty(ctx.builder.getContext()), pv, true, name); v = ctx.builder.CreateAdd(v, addend); @@ -2964,7 +2966,7 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t a auto answer = ctx.builder.CreateCall(prepare_call(memcmp_func), { ctx.builder.CreateBitCast(varg1, getInt8PtrTy(ctx.builder.getContext())), ctx.builder.CreateBitCast(varg2, getInt8PtrTy(ctx.builder.getContext())), - ConstantInt::get(getSizeTy(ctx.builder.getContext()), sz) }, + ConstantInt::get(ctx.types().T_size, sz) }, ArrayRef(&OpBundle, nroots ? 1 : 0)); if (arg1.tbaa || arg2.tbaa) { @@ -3335,7 +3337,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, #ifdef _P64 nva = ctx.builder.CreateTrunc(nva, getInt32Ty(ctx.builder.getContext())); #endif - Value *theArgs = ctx.builder.CreateInBoundsGEP(ctx.types().T_prjlvalue, ctx.argArray, ConstantInt::get(getSizeTy(ctx.builder.getContext()), ctx.nReqArgs)); + Value *theArgs = ctx.builder.CreateInBoundsGEP(ctx.types().T_prjlvalue, ctx.argArray, ConstantInt::get(ctx.types().T_size, ctx.nReqArgs)); Value *r = ctx.builder.CreateCall(prepare_call(jlapplygeneric_func), { theF, theArgs, nva }); *ret = mark_julia_type(ctx, r, true, jl_any_type); return true; @@ -3377,22 +3379,22 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, return true; } else if (idx_const > ndims) { - *ret = mark_julia_type(ctx, ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1), false, jl_long_type); + *ret = mark_julia_type(ctx, ConstantInt::get(ctx.types().T_size, 1), false, jl_long_type); return true; } } else { - Value *idx_dyn = emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), idx, (jl_value_t*)jl_long_type); - error_unless(ctx, ctx.builder.CreateICmpSGT(idx_dyn, Constant::getNullValue(getSizeTy(ctx.builder.getContext()))), + Value *idx_dyn = emit_unbox(ctx, ctx.types().T_size, idx, (jl_value_t*)jl_long_type); + error_unless(ctx, ctx.builder.CreateICmpSGT(idx_dyn, Constant::getNullValue(ctx.types().T_size)), "arraysize: dimension out of range"); BasicBlock *outBB = BasicBlock::Create(ctx.builder.getContext(), "outofrange", ctx.f); BasicBlock *inBB = BasicBlock::Create(ctx.builder.getContext(), "inrange"); BasicBlock *ansBB = BasicBlock::Create(ctx.builder.getContext(), "arraysize"); ctx.builder.CreateCondBr(ctx.builder.CreateICmpSLE(idx_dyn, - ConstantInt::get(getSizeTy(ctx.builder.getContext()), ndims)), + ConstantInt::get(ctx.types().T_size, ndims)), inBB, outBB); ctx.builder.SetInsertPoint(outBB); - Value *v_one = ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1); + Value *v_one = ConstantInt::get(ctx.types().T_size, 1); ctx.builder.CreateBr(ansBB); ctx.f->getBasicBlockList().push_back(inBB); ctx.builder.SetInsertPoint(inBB); @@ -3401,7 +3403,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, inBB = ctx.builder.GetInsertBlock(); // could have changed ctx.f->getBasicBlockList().push_back(ansBB); ctx.builder.SetInsertPoint(ansBB); - PHINode *result = ctx.builder.CreatePHI(getSizeTy(ctx.builder.getContext()), 2); + PHINode *result = ctx.builder.CreatePHI(ctx.types().T_size, 2); result->addIncoming(v_one, outBB); result->addIncoming(v_sz, inBB); *ret = mark_julia_type(ctx, result, false, jl_long_type); @@ -3452,7 +3454,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, // isbits union selector bytes are stored after a->maxsize Value *ndims = (nd == -1 ? emit_arrayndims(ctx, ary) : ConstantInt::get(getInt16Ty(ctx.builder.getContext()), nd)); Value *is_vector = ctx.builder.CreateICmpEQ(ndims, ConstantInt::get(getInt16Ty(ctx.builder.getContext()), 1)); - Value *selidx_v = ctx.builder.CreateSub(emit_vectormaxsize(ctx, ary), ctx.builder.CreateZExt(offset, getSizeTy(ctx.builder.getContext()))); + Value *selidx_v = ctx.builder.CreateSub(emit_vectormaxsize(ctx, ary), ctx.builder.CreateZExt(offset, ctx.types().T_size)); Value *selidx_m = emit_arraylen(ctx, ary); Value *selidx = ctx.builder.CreateSelect(is_vector, selidx_v, selidx_m); ptindex = ctx.builder.CreateInBoundsGEP(AT, data, selidx); @@ -3560,7 +3562,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, else { Value *ndims = (nd == -1 ? emit_arrayndims(ctx, ary) : ConstantInt::get(getInt16Ty(ctx.builder.getContext()), nd)); Value *is_vector = ctx.builder.CreateICmpEQ(ndims, ConstantInt::get(getInt16Ty(ctx.builder.getContext()), 1)); - Value *selidx_v = ctx.builder.CreateSub(emit_vectormaxsize(ctx, ary), ctx.builder.CreateZExt(offset, getSizeTy(ctx.builder.getContext()))); + Value *selidx_v = ctx.builder.CreateSub(emit_vectormaxsize(ctx, ary), ctx.builder.CreateZExt(offset, ctx.types().T_size)); Value *selidx_m = emit_arraylen(ctx, ary); Value *selidx = ctx.builder.CreateSelect(is_vector, selidx_v, selidx_m); ptindex = ctx.builder.CreateInBoundsGEP(AT, data, selidx); @@ -3661,11 +3663,11 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (load->getPointerOperand() == ctx.slots[ctx.vaSlot].boxroot && ctx.argArray) { Value *valen = emit_n_varargs(ctx); jl_cgval_t va_ary( // fake instantiation of a cgval, in order to call emit_bounds_check (it only checks the `.V` field) - ctx.builder.CreateInBoundsGEP(ctx.types().T_prjlvalue, ctx.argArray, ConstantInt::get(getSizeTy(ctx.builder.getContext()), ctx.nReqArgs)), + ctx.builder.CreateInBoundsGEP(ctx.types().T_prjlvalue, ctx.argArray, ConstantInt::get(ctx.types().T_size, ctx.nReqArgs)), NULL, NULL); - Value *idx = emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), fld, (jl_value_t*)jl_long_type); + Value *idx = emit_unbox(ctx, ctx.types().T_size, fld, (jl_value_t*)jl_long_type); idx = emit_bounds_check(ctx, va_ary, NULL, idx, valen, boundscheck); - idx = ctx.builder.CreateAdd(idx, ConstantInt::get(getSizeTy(ctx.builder.getContext()), ctx.nReqArgs)); + idx = ctx.builder.CreateAdd(idx, ConstantInt::get(ctx.types().T_size, ctx.nReqArgs)); Instruction *v = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, ctx.builder.CreateInBoundsGEP(ctx.types().T_prjlvalue, ctx.argArray, idx), Align(sizeof(void*))); // if we know the result type of this load, we will mark that information here too jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_value); @@ -3690,13 +3692,13 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } else { // unknown index - Value *vidx = emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), fld, (jl_value_t*)jl_long_type); + Value *vidx = emit_unbox(ctx, ctx.types().T_size, fld, (jl_value_t*)jl_long_type); if (emit_getfield_unknownidx(ctx, ret, obj, vidx, utt, boundscheck, order)) { return true; } } } - Value *vidx = emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), fld, (jl_value_t*)jl_long_type); + Value *vidx = emit_unbox(ctx, ctx.types().T_size, fld, (jl_value_t*)jl_long_type); if (jl_is_tuple_type(utt) && is_tupletype_homogeneous(utt->parameters, true)) { // For tuples, we can emit code even if we don't know the exact // type (e.g. because we don't know the length). This is possible @@ -3715,7 +3717,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, // This is not necessary for correctness, but allows to omit // the extra code for getting the length of the tuple if (!bounds_check_enabled(ctx, boundscheck)) { - vidx = ctx.builder.CreateSub(vidx, ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1)); + vidx = ctx.builder.CreateSub(vidx, ConstantInt::get(ctx.types().T_size, 1)); } else { vidx = emit_bounds_check(ctx, obj, (jl_value_t*)obj.typ, vidx, emit_datatype_nfields(ctx, emit_typeof_boxed(ctx, obj)), @@ -3731,7 +3733,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } // Unknown object, but field known to be integer - vidx = ctx.builder.CreateSub(vidx, ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1)); + vidx = ctx.builder.CreateSub(vidx, ConstantInt::get(ctx.types().T_size, 1)); Value *fld_val = ctx.builder.CreateCall(prepare_call(jlgetnthfieldchecked_func), { boxed(ctx, obj), vidx }); *ret = mark_julia_type(ctx, fld_val, true, jl_any_type); return true; @@ -3809,7 +3811,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } Value *sz; if (nf != -1) - sz = ConstantInt::get(getSizeTy(ctx.builder.getContext()), nf); + sz = ConstantInt::get(ctx.types().T_size, nf); else sz = emit_datatype_nfields(ctx, emit_typeof_boxed(ctx, obj)); *ret = mark_julia_type(ctx, sz, false, jl_long_type); @@ -3826,7 +3828,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, Value *tyv = boxed(ctx, typ); Value *types_svec = emit_datatype_types(ctx, tyv); Value *types_len = emit_datatype_nfields(ctx, tyv); - Value *idx = emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), fld, (jl_value_t*)jl_long_type); + Value *idx = emit_unbox(ctx, ctx.types().T_size, fld, (jl_value_t*)jl_long_type); jl_value_t *boundscheck = (nargs == 3 ? argv[3].constant : jl_true); if (nargs == 3) emit_typecheck(ctx, argv[3], (jl_value_t*)jl_bool_type, "fieldtype"); @@ -3853,23 +3855,23 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, else { sz = (1 + jl_svec_len(obj.constant)) * sizeof(void*); } - *ret = mark_julia_type(ctx, ConstantInt::get(getSizeTy(ctx.builder.getContext()), sz), false, jl_long_type); + *ret = mark_julia_type(ctx, ConstantInt::get(ctx.types().T_size, sz), false, jl_long_type); return true; } // String and SimpleVector's length fields have the same layout auto ptr = emit_bitcast(ctx, boxed(ctx, obj), getSizePtrTy(ctx.builder.getContext())); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); - Value *len = ai.decorateInst(ctx.builder.CreateAlignedLoad(getSizeTy(ctx.builder.getContext()), ptr, Align(sizeof(size_t)))); + Value *len = ai.decorateInst(ctx.builder.CreateAlignedLoad(ctx.types().T_size, ptr, Align(sizeof(size_t)))); MDBuilder MDB(ctx.builder.getContext()); if (sty == jl_simplevector_type) { auto rng = MDB.createRange( - Constant::getNullValue(getSizeTy(ctx.builder.getContext())), ConstantInt::get(getSizeTy(ctx.builder.getContext()), INTPTR_MAX / sizeof(void*) - 1)); + Constant::getNullValue(ctx.types().T_size), ConstantInt::get(ctx.types().T_size, INTPTR_MAX / sizeof(void*) - 1)); cast(len)->setMetadata(LLVMContext::MD_range, rng); - len = ctx.builder.CreateMul(len, ConstantInt::get(getSizeTy(ctx.builder.getContext()), sizeof(void*))); - len = ctx.builder.CreateAdd(len, ConstantInt::get(getSizeTy(ctx.builder.getContext()), sizeof(void*))); + len = ctx.builder.CreateMul(len, ConstantInt::get(ctx.types().T_size, sizeof(void*))); + len = ctx.builder.CreateAdd(len, ConstantInt::get(ctx.types().T_size, sizeof(void*))); } else { - auto rng = MDB.createRange(Constant::getNullValue(getSizeTy(ctx.builder.getContext())), ConstantInt::get(getSizeTy(ctx.builder.getContext()), INTPTR_MAX)); + auto rng = MDB.createRange(Constant::getNullValue(ctx.types().T_size), ConstantInt::get(ctx.types().T_size, INTPTR_MAX)); cast(len)->setMetadata(LLVMContext::MD_range, rng); } *ret = mark_julia_type(ctx, len, false, jl_long_type); @@ -3880,10 +3882,10 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, Value *elsize; size_t elsz; if (arraytype_constelsize(sty, &elsz)) { - elsize = ConstantInt::get(getSizeTy(ctx.builder.getContext()), elsz); + elsize = ConstantInt::get(ctx.types().T_size, elsz); } else { - elsize = ctx.builder.CreateZExt(emit_arrayelsize(ctx, obj), getSizeTy(ctx.builder.getContext())); + elsize = ctx.builder.CreateZExt(emit_arrayelsize(ctx, obj), ctx.types().T_size); } *ret = mark_julia_type(ctx, ctx.builder.CreateMul(len, elsize), false, jl_long_type); return true; @@ -3933,8 +3935,8 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, isdefined_unknown_idx: if (nargs == 3 || fld.typ != (jl_value_t*)jl_long_type) return false; - Value *vidx = emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), fld, (jl_value_t*)jl_long_type); - vidx = ctx.builder.CreateSub(vidx, ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1)); + Value *vidx = emit_unbox(ctx, ctx.types().T_size, fld, (jl_value_t*)jl_long_type); + vidx = ctx.builder.CreateSub(vidx, ConstantInt::get(ctx.types().T_size, 1)); Value *isd = ctx.builder.CreateCall(prepare_call(jlfieldisdefinedchecked_func), { boxed(ctx, obj), vidx }); isd = ctx.builder.CreateTrunc(isd, getInt8Ty(ctx.builder.getContext())); *ret = mark_julia_type(ctx, isd, false, jl_bool_type); @@ -5116,7 +5118,7 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr, int ssaval_result) } else if (head == jl_pop_exception_sym) { jl_cgval_t excstack_state = emit_expr(ctx, jl_exprarg(expr, 0)); - assert(excstack_state.V && excstack_state.V->getType() == getSizeTy(ctx.builder.getContext())); + assert(excstack_state.V && excstack_state.V->getType() == ctx.types().T_size); ctx.builder.CreateCall(prepare_call(jl_restore_excstack_func), excstack_state.V); return; } @@ -5479,13 +5481,13 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ if (F) { jl_cgval_t jlcall_ptr = mark_julia_type(ctx, F, false, jl_voidpointer_type); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); - Instruction *I = ctx.builder.CreateAlignedLoad(getSizeTy(ctx.builder.getContext()), get_last_age_field(ctx), Align(sizeof(size_t))); + Instruction *I = ctx.builder.CreateAlignedLoad(ctx.types().T_size, get_last_age_field(ctx), Align(sizeof(size_t))); jl_cgval_t world_age = mark_julia_type(ctx, ai.decorateInst(I), false, jl_long_type); jl_cgval_t fptr; if (specF) fptr = mark_julia_type(ctx, specF, false, jl_voidpointer_type); else - fptr = mark_julia_type(ctx, (llvm::Value*)Constant::getNullValue(getSizeTy(ctx.builder.getContext())), false, jl_voidpointer_type); + fptr = mark_julia_type(ctx, (llvm::Value*)Constant::getNullValue(ctx.types().T_size), false, jl_voidpointer_type); // TODO: Inline the env at the end of the opaque closure and generate a descriptor for GC jl_cgval_t env = emit_new_struct(ctx, (jl_value_t*)env_t, nargs-4, &argv.data()[4]); @@ -5634,9 +5636,9 @@ static Value *get_last_age_field(jl_codectx_t &ctx) { Value *ct = get_current_task(ctx); return ctx.builder.CreateInBoundsGEP( - getSizeTy(ctx.builder.getContext()), + ctx.types().T_size, ctx.builder.CreateBitCast(ct, getSizePtrTy(ctx.builder.getContext())), - ConstantInt::get(getSizeTy(ctx.builder.getContext()), offsetof(jl_task_t, world_age) / sizeof(size_t)), + ConstantInt::get(ctx.types().T_size, offsetof(jl_task_t, world_age) / sizeof(size_t)), "world_age"); } @@ -5941,18 +5943,18 @@ static Function* gen_cfun_wrapper( Value *world_age_field = get_last_age_field(ctx); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); Value *last_age = ai.decorateInst( - ctx.builder.CreateAlignedLoad(getSizeTy(ctx.builder.getContext()), world_age_field, Align(sizeof(size_t)))); + ctx.builder.CreateAlignedLoad(ctx.types().T_size, world_age_field, Align(sizeof(size_t)))); - Value *world_v = ctx.builder.CreateAlignedLoad(getSizeTy(ctx.builder.getContext()), + Value *world_v = ctx.builder.CreateAlignedLoad(ctx.types().T_size, prepare_global_in(jl_Module, jlgetworld_global), Align(sizeof(size_t))); cast(world_v)->setOrdering(AtomicOrdering::Acquire); Value *age_ok = NULL; if (calltype) { LoadInst *lam_max = ctx.builder.CreateAlignedLoad( - getSizeTy(ctx.builder.getContext()), + ctx.types().T_size, ctx.builder.CreateConstInBoundsGEP1_32( - getSizeTy(ctx.builder.getContext()), + ctx.types().T_size, emit_bitcast(ctx, literal_pointer_val(ctx, (jl_value_t*)codeinst), getSizePtrTy(ctx.builder.getContext())), offsetof(jl_code_instance_t, max_world) / sizeof(size_t)), Align(sizeof(size_t))); @@ -6472,7 +6474,7 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con } fill = (jl_svec_t*)jl_ensure_rooted(ctx, (jl_value_t*)fill); } - Type *T_htable = ArrayType::get(getSizeTy(ctx.builder.getContext()), sizeof(htable_t) / sizeof(void*)); + Type *T_htable = ArrayType::get(ctx.types().T_size, sizeof(htable_t) / sizeof(void*)); Value *cache = new GlobalVariable(*jl_Module, T_htable, false, GlobalVariable::PrivateLinkage, ConstantAggregateZero::get(T_htable)); @@ -6488,7 +6490,7 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con outboxed = true; } else { - F = ctx.builder.CreatePtrToInt(F, getSizeTy(ctx.builder.getContext())); + F = ctx.builder.CreatePtrToInt(F, ctx.types().T_size); outboxed = (output_type != (jl_value_t*)jl_voidpointer_type); if (outboxed) { assert(jl_datatype_size(output_type) == sizeof(void*) * 4); @@ -6499,12 +6501,12 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); ai.decorateInst(ctx.builder.CreateStore(F, derived_strct)); ai.decorateInst(ctx.builder.CreateStore( - ctx.builder.CreatePtrToInt(literal_pointer_val(ctx, fexpr_rt.constant), getSizeTy(ctx.builder.getContext())), - ctx.builder.CreateConstInBoundsGEP1_32(getSizeTy(ctx.builder.getContext()), derived_strct, 1))); - ai.decorateInst(ctx.builder.CreateStore(Constant::getNullValue(getSizeTy(ctx.builder.getContext())), - ctx.builder.CreateConstInBoundsGEP1_32(getSizeTy(ctx.builder.getContext()), derived_strct, 2))); - ai.decorateInst(ctx.builder.CreateStore(Constant::getNullValue(getSizeTy(ctx.builder.getContext())), - ctx.builder.CreateConstInBoundsGEP1_32(getSizeTy(ctx.builder.getContext()), derived_strct, 3))); + ctx.builder.CreatePtrToInt(literal_pointer_val(ctx, fexpr_rt.constant), ctx.types().T_size), + ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_size, derived_strct, 1))); + ai.decorateInst(ctx.builder.CreateStore(Constant::getNullValue(ctx.types().T_size), + ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_size, derived_strct, 2))); + ai.decorateInst(ctx.builder.CreateStore(Constant::getNullValue(ctx.types().T_size), + ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_size, derived_strct, 3))); F = strct; } } @@ -7283,7 +7285,7 @@ static jl_llvm_functions_t if (toplevel || ctx.is_opaque_closure) { jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); last_age = ai.decorateInst(ctx.builder.CreateAlignedLoad( - getSizeTy(ctx.builder.getContext()), world_age_field, Align(sizeof(size_t)))); + ctx.types().T_size, world_age_field, Align(sizeof(size_t)))); } // step 7. allocate local variables slots @@ -7549,7 +7551,7 @@ static jl_llvm_functions_t Value *argaddr = emit_bitcast(ctx, data_pointer(ctx, theArg), getInt8PtrTy(ctx.builder.getContext())); Value *worldaddr = ctx.builder.CreateInBoundsGEP( getInt8Ty(ctx.builder.getContext()), argaddr, - ConstantInt::get(getSizeTy(ctx.builder.getContext()), offsetof(jl_opaque_closure_t, world))); + ConstantInt::get(ctx.types().T_size, offsetof(jl_opaque_closure_t, world))); jl_cgval_t closure_world = typed_load(ctx, worldaddr, NULL, (jl_value_t*)jl_long_type, theArg.tbaa, nullptr, false, AtomicOrdering::NotAtomic, false, sizeof(size_t)); @@ -7558,7 +7560,7 @@ static jl_llvm_functions_t // Load closure env Value *envaddr = ctx.builder.CreateInBoundsGEP( getInt8Ty(ctx.builder.getContext()), argaddr, - ConstantInt::get(getSizeTy(ctx.builder.getContext()), offsetof(jl_opaque_closure_t, captures))); + ConstantInt::get(ctx.types().T_size, offsetof(jl_opaque_closure_t, captures))); jl_cgval_t closure_env = typed_load(ctx, envaddr, NULL, (jl_value_t*)jl_any_type, theArg.tbaa, nullptr, true, AtomicOrdering::NotAtomic, false, sizeof(void*)); @@ -7627,7 +7629,7 @@ static jl_llvm_functions_t ctx.builder.CreateCall(F, { Constant::getNullValue(ctx.types().T_prjlvalue), ctx.builder.CreateInBoundsGEP(ctx.types().T_prjlvalue, argArray, - ConstantInt::get(getSizeTy(ctx.builder.getContext()), nreq - 1)), + ConstantInt::get(ctx.types().T_size, nreq - 1)), ctx.builder.CreateSub(argCount, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), nreq - 1)) }); restTuple->setAttributes(F->getAttributes()); @@ -8096,7 +8098,7 @@ static jl_llvm_functions_t else { if (!jl_is_method(ctx.linfo->def.method) && !ctx.is_opaque_closure) { // TODO: inference is invalid if this has any effect (which it often does) - LoadInst *world = ctx.builder.CreateAlignedLoad(getSizeTy(ctx.builder.getContext()), + LoadInst *world = ctx.builder.CreateAlignedLoad(ctx.types().T_size, prepare_global_in(jl_Module, jlgetworld_global), Align(sizeof(size_t))); world->setOrdering(AtomicOrdering::Acquire); ctx.builder.CreateAlignedStore(world, world_age_field, Align(sizeof(size_t))); diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 0b95056baeaf8..3f6c602e3ec99 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -659,8 +659,8 @@ static jl_cgval_t emit_pointerref(jl_codectx_t &ctx, jl_cgval_t *argv) return jl_cgval_t(); } - Value *idx = emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), i, (jl_value_t*)jl_long_type); - Value *im1 = ctx.builder.CreateSub(idx, ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1)); + Value *idx = emit_unbox(ctx, ctx.types().T_size, i, (jl_value_t*)jl_long_type); + Value *im1 = ctx.builder.CreateSub(idx, ConstantInt::get(ctx.types().T_size, 1)); if (ety == (jl_value_t*)jl_any_type) { Value *thePtr = emit_unbox(ctx, ctx.types().T_pprjlvalue, e, e.typ); @@ -674,7 +674,7 @@ static jl_cgval_t emit_pointerref(jl_codectx_t &ctx, jl_cgval_t *argv) uint64_t size = jl_datatype_size(ety); Value *strct = emit_allocobj(ctx, size, literal_pointer_val(ctx, ety)); - im1 = ctx.builder.CreateMul(im1, ConstantInt::get(getSizeTy(ctx.builder.getContext()), + im1 = ctx.builder.CreateMul(im1, ConstantInt::get(ctx.types().T_size, LLT_ALIGN(size, jl_datatype_align(ety)))); Value *thePtr = emit_unbox(ctx, getInt8PtrTy(ctx.builder.getContext()), e, e.typ); thePtr = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), emit_bitcast(ctx, thePtr, getInt8PtrTy(ctx.builder.getContext())), im1); @@ -729,23 +729,23 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, jl_cgval_t *argv) } emit_typecheck(ctx, x, ety, "pointerset"); - Value *idx = emit_unbox(ctx, getSizeTy(ctx.builder.getContext()), i, (jl_value_t*)jl_long_type); - Value *im1 = ctx.builder.CreateSub(idx, ConstantInt::get(getSizeTy(ctx.builder.getContext()), 1)); + Value *idx = emit_unbox(ctx, ctx.types().T_size, i, (jl_value_t*)jl_long_type); + Value *im1 = ctx.builder.CreateSub(idx, ConstantInt::get(ctx.types().T_size, 1)); Value *thePtr; if (ety == (jl_value_t*)jl_any_type) { // unsafe_store to Ptr{Any} is allowed to implicitly drop GC roots. thePtr = emit_unbox(ctx, getSizePtrTy(ctx.builder.getContext()), e, e.typ); Instruction *store = ctx.builder.CreateAlignedStore( - ctx.builder.CreatePtrToInt(emit_pointer_from_objref(ctx, boxed(ctx, x)), getSizeTy(ctx.builder.getContext())), - ctx.builder.CreateInBoundsGEP(getSizeTy(ctx.builder.getContext()), thePtr, im1), Align(align_nb)); + ctx.builder.CreatePtrToInt(emit_pointer_from_objref(ctx, boxed(ctx, x)), ctx.types().T_size), + ctx.builder.CreateInBoundsGEP(ctx.types().T_size, thePtr, im1), Align(align_nb)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_data); ai.decorateInst(store); } else if (!jl_isbits(ety)) { thePtr = emit_unbox(ctx, getInt8PtrTy(ctx.builder.getContext()), e, e.typ); uint64_t size = jl_datatype_size(ety); - im1 = ctx.builder.CreateMul(im1, ConstantInt::get(getSizeTy(ctx.builder.getContext()), + im1 = ctx.builder.CreateMul(im1, ConstantInt::get(ctx.types().T_size, LLT_ALIGN(size, jl_datatype_align(ety)))); emit_memcpy(ctx, ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), thePtr, im1), jl_aliasinfo_t::fromTBAA(ctx, nullptr), x, size, align_nb); } From 03d16f6d840a9f8e2768e0192085b5f2f419ba83 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Mon, 6 Mar 2023 03:40:55 -0500 Subject: [PATCH 266/775] Remove getSizeTy from many optimization passes --- src/llvm-alloc-opt.cpp | 4 ++-- src/llvm-codegen-shared.h | 2 ++ src/llvm-final-gc-lowering.cpp | 13 +++++++------ src/llvm-late-gc-lowering.cpp | 22 ++++++++++------------ src/llvm-ptls.cpp | 18 ++++++++++-------- 5 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index f6a2593724f57..b044e2351f512 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -975,7 +975,7 @@ void Optimizer::splitOnStack(CallInst *orig_inst) assert(slot.offset == offset); auto T_pjlvalue = JuliaType::get_pjlvalue_ty(builder.getContext()); if (!isa(store_ty)) { - store_val = builder.CreateBitCast(store_val, getSizeTy(builder.getContext())); + store_val = builder.CreateBitCast(store_val, pass.DL->getIntPtrType(builder.getContext(), T_pjlvalue->getAddressSpace())); store_val = builder.CreateIntToPtr(store_val, T_pjlvalue); store_ty = T_pjlvalue; } @@ -1038,7 +1038,7 @@ void Optimizer::splitOnStack(CallInst *orig_inst) else { uint64_t intval; memset(&intval, val, 8); - Constant *val = ConstantInt::get(getSizeTy(builder.getContext()), intval); + Constant *val = ConstantInt::get(pass.DL->getIntPtrType(builder.getContext(), pass.T_prjlvalue->getAddressSpace()), intval); val = ConstantExpr::getIntToPtr(val, JuliaType::get_pjlvalue_ty(builder.getContext())); ptr = ConstantExpr::getAddrSpaceCast(val, pass.T_prjlvalue); } diff --git a/src/llvm-codegen-shared.h b/src/llvm-codegen-shared.h index 732871b12ff23..e013bfc962c11 100644 --- a/src/llvm-codegen-shared.h +++ b/src/llvm-codegen-shared.h @@ -22,6 +22,8 @@ enum AddressSpace { LastSpecial = Loaded, }; +// Do not use (only for migration purposes) +// Prefer DataLayout::getIntPtrType with addrspace argument to support cross-compilation static inline auto getSizeTy(llvm::LLVMContext &ctxt) { //return M.getDataLayout().getIntPtrType(M.getContext()); if (sizeof(size_t) > sizeof(uint32_t)) { diff --git a/src/llvm-final-gc-lowering.cpp b/src/llvm-final-gc-lowering.cpp index e4e8ff69ee2da..fc9f76ef27fa7 100644 --- a/src/llvm-final-gc-lowering.cpp +++ b/src/llvm-final-gc-lowering.cpp @@ -50,6 +50,7 @@ struct FinalLowerGC: private JuliaPassContext { Function *bigAllocFunc; Function *allocTypedFunc; Instruction *pgcstack; + Type *T_size; // Lowers a `julia.new_gc_frame` intrinsic. Value *lowerNewGCFrame(CallInst *target, Function &F); @@ -125,10 +126,10 @@ void FinalLowerGC::lowerPushGCFrame(CallInst *target, Function &F) IRBuilder<> builder(target->getContext()); builder.SetInsertPoint(&*(++BasicBlock::iterator(target))); StoreInst *inst = builder.CreateAlignedStore( - ConstantInt::get(getSizeTy(F.getContext()), JL_GC_ENCODE_PUSHARGS(nRoots)), + ConstantInt::get(T_size, JL_GC_ENCODE_PUSHARGS(nRoots)), builder.CreateBitCast( builder.CreateConstInBoundsGEP1_32(T_prjlvalue, gcframe, 0), - getSizeTy(F.getContext())->getPointerTo()), + T_size->getPointerTo()), Align(sizeof(void*))); inst->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe); auto T_ppjlvalue = JuliaType::get_ppjlvalue_ty(F.getContext()); @@ -199,7 +200,6 @@ Value *FinalLowerGC::lowerSafepoint(CallInst *target, Function &F) assert(target->arg_size() == 1); IRBuilder<> builder(target->getContext()); builder.SetInsertPoint(target); - auto T_size = getSizeTy(builder.getContext()); Value* signal_page = target->getOperand(0); Value* load = builder.CreateLoad(T_size, signal_page, true); return load; @@ -224,7 +224,7 @@ Value *FinalLowerGC::lowerGCAllocBytes(CallInst *target, Function &F) if (offset < 0) { newI = builder.CreateCall( bigAllocFunc, - { ptls, ConstantInt::get(getSizeTy(F.getContext()), sz + sizeof(void*)) }); + { ptls, ConstantInt::get(T_size, sz + sizeof(void*)) }); derefAttr = Attribute::getWithDereferenceableBytes(F.getContext(), sz + sizeof(void*)); } else { @@ -234,8 +234,8 @@ Value *FinalLowerGC::lowerGCAllocBytes(CallInst *target, Function &F) derefAttr = Attribute::getWithDereferenceableBytes(F.getContext(), osize); } } else { - auto size = builder.CreateZExtOrTrunc(target->getArgOperand(1), getSizeTy(F.getContext())); - size = builder.CreateAdd(size, ConstantInt::get(getSizeTy(F.getContext()), sizeof(void*))); + auto size = builder.CreateZExtOrTrunc(target->getArgOperand(1), T_size); + size = builder.CreateAdd(size, ConstantInt::get(T_size, sizeof(void*))); newI = builder.CreateCall(allocTypedFunc, { ptls, size, ConstantPointerNull::get(Type::getInt8PtrTy(F.getContext())) }); derefAttr = Attribute::getWithDereferenceableBytes(F.getContext(), sizeof(void*)); } @@ -254,6 +254,7 @@ bool FinalLowerGC::doInitialization(Module &M) { poolAllocFunc = getOrDeclare(jl_well_known::GCPoolAlloc); bigAllocFunc = getOrDeclare(jl_well_known::GCBigAlloc); allocTypedFunc = getOrDeclare(jl_well_known::GCAllocTyped); + T_size = M.getDataLayout().getIntPtrType(M.getContext()); GlobalValue *functionList[] = {queueRootFunc, poolAllocFunc, bigAllocFunc, allocTypedFunc}; unsigned j = 0; diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index 6837dc505a503..a67b76c1ba918 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -371,8 +371,8 @@ struct LateLowerGCFrame: private JuliaPassContext { SmallVector GetPHIRefinements(PHINode *phi, State &S); void FixUpRefinements(ArrayRef PHINumbers, State &S); void RefineLiveSet(LargeSparseBitVector &LS, State &S, const std::vector &CalleeRoots); - Value *EmitTagPtr(IRBuilder<> &builder, Type *T, Value *V); - Value *EmitLoadTag(IRBuilder<> &builder, Value *V); + Value *EmitTagPtr(IRBuilder<> &builder, Type *T, Type *T_size, Value *V); + Value *EmitLoadTag(IRBuilder<> &builder, Type *T_size, Value *V); }; static unsigned getValueAddrSpace(Value *V) { @@ -2207,19 +2207,17 @@ std::vector LateLowerGCFrame::ColorRoots(const State &S) { } // Size of T is assumed to be `sizeof(void*)` -Value *LateLowerGCFrame::EmitTagPtr(IRBuilder<> &builder, Type *T, Value *V) +Value *LateLowerGCFrame::EmitTagPtr(IRBuilder<> &builder, Type *T, Type *T_size, Value *V) { - auto T_size = getSizeTy(T->getContext()); assert(T == T_size || isa(T)); auto TV = cast(V->getType()); auto cast = builder.CreateBitCast(V, T->getPointerTo(TV->getAddressSpace())); return builder.CreateInBoundsGEP(T, cast, ConstantInt::get(T_size, -1)); } -Value *LateLowerGCFrame::EmitLoadTag(IRBuilder<> &builder, Value *V) +Value *LateLowerGCFrame::EmitLoadTag(IRBuilder<> &builder, Type *T_size, Value *V) { - auto T_size = getSizeTy(builder.getContext()); - auto addr = EmitTagPtr(builder, T_size, V); + auto addr = EmitTagPtr(builder, T_size, T_size, V); LoadInst *load = builder.CreateAlignedLoad(T_size, addr, Align(sizeof(size_t))); load->setOrdering(AtomicOrdering::Unordered); load->setMetadata(LLVMContext::MD_tbaa, tbaa_tag); @@ -2278,7 +2276,7 @@ MDNode *createMutableTBAAAccessTag(MDNode *Tag) { bool LateLowerGCFrame::CleanupIR(Function &F, State *S, bool *CFGModified) { auto T_int32 = Type::getInt32Ty(F.getContext()); - auto T_size = getSizeTy(F.getContext()); + auto T_size = F.getParent()->getDataLayout().getIntPtrType(F.getContext()); bool ChangesMade = false; // We create one alloca for all the jlcall frames that haven't been processed // yet. LLVM would merge them anyway later, so might as well save it a bit @@ -2399,7 +2397,7 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S, bool *CFGModified) { } // Set the tag. StoreInst *store = builder.CreateAlignedStore( - tag, EmitTagPtr(builder, tag_type, newI), Align(sizeof(size_t))); + tag, EmitTagPtr(builder, tag_type, T_size, newI), Align(sizeof(size_t))); store->setOrdering(AtomicOrdering::Unordered); store->setMetadata(LLVMContext::MD_tbaa, tbaa_tag); @@ -2413,7 +2411,7 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S, bool *CFGModified) { assert(CI->arg_size() == 1); IRBuilder<> builder(CI); builder.SetCurrentDebugLocation(CI->getDebugLoc()); - auto tag = EmitLoadTag(builder, CI->getArgOperand(0)); + auto tag = EmitLoadTag(builder, T_size, CI->getArgOperand(0)); auto masked = builder.CreateAnd(tag, ConstantInt::get(T_size, ~(uintptr_t)15)); auto typ = builder.CreateAddrSpaceCast(builder.CreateIntToPtr(masked, JuliaType::get_pjlvalue_ty(masked->getContext())), T_prjlvalue); @@ -2512,14 +2510,14 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S, bool *CFGModified) { } IRBuilder<> builder(CI); builder.SetCurrentDebugLocation(CI->getDebugLoc()); - auto parBits = builder.CreateAnd(EmitLoadTag(builder, parent), 3); + auto parBits = builder.CreateAnd(EmitLoadTag(builder, T_size, parent), 3); auto parOldMarked = builder.CreateICmpEQ(parBits, ConstantInt::get(T_size, 3)); auto mayTrigTerm = SplitBlockAndInsertIfThen(parOldMarked, CI, false); builder.SetInsertPoint(mayTrigTerm); Value *anyChldNotMarked = NULL; for (unsigned i = 1; i < CI->arg_size(); i++) { Value *child = CI->getArgOperand(i); - Value *chldBit = builder.CreateAnd(EmitLoadTag(builder, child), 1); + Value *chldBit = builder.CreateAnd(EmitLoadTag(builder, T_size, child), 1); Value *chldNotMarked = builder.CreateICmpEQ(chldBit, ConstantInt::get(T_size, 0)); anyChldNotMarked = anyChldNotMarked ? builder.CreateOr(anyChldNotMarked, chldNotMarked) : chldNotMarked; } diff --git a/src/llvm-ptls.cpp b/src/llvm-ptls.cpp index e49b992ded50f..5cc8036217e44 100644 --- a/src/llvm-ptls.cpp +++ b/src/llvm-ptls.cpp @@ -49,6 +49,7 @@ struct LowerPTLS { FunctionType *FT_pgcstack_getter{nullptr}; PointerType *T_pgcstack_getter{nullptr}; PointerType *T_pppjlvalue{nullptr}; + Type *T_size{nullptr}; GlobalVariable *pgcstack_func_slot{nullptr}; GlobalVariable *pgcstack_key_slot{nullptr}; GlobalVariable *pgcstack_offset{nullptr}; @@ -125,7 +126,7 @@ Instruction *LowerPTLS::emit_pgcstack_tp(Value *offset, Instruction *insertBefor assert(0 && "Cannot emit thread pointer for this architecture."); #endif if (!offset) - offset = ConstantInt::getSigned(getSizeTy(insertBefore->getContext()), jl_tls_offset); + offset = ConstantInt::getSigned(T_size, jl_tls_offset); auto tp = InlineAsm::get(FunctionType::get(Type::getInt8PtrTy(insertBefore->getContext()), false), asm_str, "=r", false); tls = CallInst::Create(tp, "thread_ptr", insertBefore); tls = GetElementPtrInst::Create(Type::getInt8Ty(insertBefore->getContext()), tls, {offset}, "ppgcstack_i8", insertBefore); @@ -216,7 +217,7 @@ void LowerPTLS::fix_pgcstack_use(CallInst *pgcstack, Function *pgcstack_getter, // pgcstack = tp + offset; // fast // else // pgcstack = getter(); // slow - auto offset = new LoadInst(getSizeTy(pgcstack->getContext()), pgcstack_offset, "", false, pgcstack); + auto offset = new LoadInst(T_size, pgcstack_offset, "", false, pgcstack); offset->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const); offset->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None)); auto cmp = new ICmpInst(pgcstack, CmpInst::ICMP_NE, offset, @@ -253,7 +254,7 @@ void LowerPTLS::fix_pgcstack_use(CallInst *pgcstack, Function *pgcstack_getter, getter->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const); getter->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None)); #if defined(_OS_DARWIN_) - auto key = new LoadInst(getSizeTy(pgcstack->getContext()), pgcstack_key_slot, "", false, pgcstack); + auto key = new LoadInst(T_size, pgcstack_key_slot, "", false, pgcstack); key->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const); key->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None)); auto new_pgcstack = CallInst::Create(FT_pgcstack_getter, getter, {key}, "", pgcstack); @@ -275,11 +276,11 @@ void LowerPTLS::fix_pgcstack_use(CallInst *pgcstack, Function *pgcstack_getter, jl_get_pgcstack_func *f; jl_pgcstack_key_t k; jl_pgcstack_getkey(&f, &k); - Constant *val = ConstantInt::get(getSizeTy(pgcstack->getContext()), (uintptr_t)f); + Constant *val = ConstantInt::get(T_size, (uintptr_t)f); val = ConstantExpr::getIntToPtr(val, T_pgcstack_getter); #if defined(_OS_DARWIN_) assert(sizeof(k) == sizeof(uintptr_t)); - Constant *key = ConstantInt::get(getSizeTy(pgcstack->getContext()), (uintptr_t)k); + Constant *key = ConstantInt::get(T_size, (uintptr_t)k); auto new_pgcstack = CallInst::Create(FT_pgcstack_getter, val, {key}, "", pgcstack); new_pgcstack->takeName(pgcstack); pgcstack->replaceAllUsesWith(new_pgcstack); @@ -303,18 +304,19 @@ bool LowerPTLS::run(bool *CFGModified) if (need_init) { tbaa_const = tbaa_make_child_with_context(M->getContext(), "jtbaa_const", nullptr, true).first; tbaa_gcframe = tbaa_make_child_with_context(M->getContext(), "jtbaa_gcframe").first; + T_size = M->getDataLayout().getIntPtrType(M->getContext()); FT_pgcstack_getter = pgcstack_getter->getFunctionType(); #if defined(_OS_DARWIN_) assert(sizeof(jl_pgcstack_key_t) == sizeof(uintptr_t)); - FT_pgcstack_getter = FunctionType::get(FT_pgcstack_getter->getReturnType(), {getSizeTy(M->getContext())}, false); + FT_pgcstack_getter = FunctionType::get(FT_pgcstack_getter->getReturnType(), {T_size}, false); #endif T_pgcstack_getter = FT_pgcstack_getter->getPointerTo(); T_pppjlvalue = cast(FT_pgcstack_getter->getReturnType()); if (imaging_mode) { pgcstack_func_slot = create_aliased_global(T_pgcstack_getter, "jl_pgcstack_func_slot"); - pgcstack_key_slot = create_aliased_global(getSizeTy(M->getContext()), "jl_pgcstack_key_slot"); // >= sizeof(jl_pgcstack_key_t) - pgcstack_offset = create_aliased_global(getSizeTy(M->getContext()), "jl_tls_offset"); + pgcstack_key_slot = create_aliased_global(T_size, "jl_pgcstack_key_slot"); // >= sizeof(jl_pgcstack_key_t) + pgcstack_offset = create_aliased_global(T_size, "jl_tls_offset"); } need_init = false; } From 9c7016554efecd14e1b496ec01361d5c4c3cd56e Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Mon, 6 Mar 2023 04:00:01 -0500 Subject: [PATCH 267/775] Remove OS ifdefs from codegen --- src/ccall.cpp | 7 ++++--- src/codegen.cpp | 10 ++++------ src/jitlayers.cpp | 19 ++++++------------- src/jitlayers.h | 5 ++--- 4 files changed, 16 insertions(+), 25 deletions(-) diff --git a/src/ccall.cpp b/src/ccall.cpp index bd2e2f5fd78e2..1b71d9b6d2e70 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1100,9 +1100,10 @@ std::string generate_func_sig(const char *fname) #else AttrBuilder retattrs; #endif -#if !defined(_OS_WINDOWS_) // llvm used to use the old mingw ABI, skipping this marking works around that difference - retattrs.addStructRetAttr(lrt); -#endif + if (!ctx->TargetTriple.isOSWindows()) { + // llvm used to use the old mingw ABI, skipping this marking works around that difference + retattrs.addStructRetAttr(lrt); + } retattrs.addAttribute(Attribute::NoAlias); paramattrs.push_back(AttributeSet::get(LLVMCtx, retattrs)); fargt_sig.push_back(PointerType::get(lrt, 0)); diff --git a/src/codegen.cpp b/src/codegen.cpp index d24e6883ae6b4..c92a9f787af2f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -601,7 +601,6 @@ static const auto jlRTLD_DEFAULT_var = new JuliaVariable{ true, [](LLVMContext &C) { return static_cast(getInt8PtrTy(C)); }, }; -#ifdef _OS_WINDOWS_ static const auto jlexe_var = new JuliaVariable{ XSTR(jl_exe_handle), true, @@ -617,7 +616,6 @@ static const auto jldlli_var = new JuliaVariable{ true, [](LLVMContext &C) { return static_cast(getInt8PtrTy(C)); }, }; -#endif //_OS_WINDOWS_ static const auto jlstack_chk_guard_var = new JuliaVariable{ XSTR(__stack_chk_guard), @@ -7047,10 +7045,10 @@ static jl_llvm_functions_t else funcName << "japi1_"; const char* unadorned_name = ctx.name; -#if defined(_OS_LINUX_) - if (unadorned_name[0] == '@') - unadorned_name++; -#endif + if (ctx.emission_context.TargetTriple.isOSLinux()) { + if (unadorned_name[0] == '@') + unadorned_name++; + } funcName << unadorned_name << "_" << jl_atomic_fetch_add(&globalUniqueGeneratedNames, 1); declarations.specFunctionObject = funcName.str(); diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 90f01a5b4f731..dd305772899dd 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -958,13 +958,10 @@ void registerRTDyldJITObject(const object::ObjectFile &Object, namespace { static std::unique_ptr createTargetMachine() JL_NOTSAFEPOINT { TargetOptions options = TargetOptions(); -#if defined(_OS_WINDOWS_) - // use ELF because RuntimeDyld COFF i686 support didn't exist - // use ELF because RuntimeDyld COFF X86_64 doesn't seem to work (fails to generate function pointers)? -#define FORCE_ELF -#endif Triple TheTriple(sys::getProcessTriple()); + // use ELF because RuntimeDyld COFF i686 support didn't exist + // use ELF because RuntimeDyld COFF X86_64 doesn't seem to work (fails to generate function pointers)? bool force_elf = TheTriple.isOSWindows(); #ifdef FORCE_ELF force_elf = true; @@ -1326,14 +1323,10 @@ JuliaOJIT::JuliaOJIT() // Resolve non-lock free atomic functions in the libatomic1 library. // This is the library that provides support for c11/c++11 atomic operations. - const char *const libatomic = -#if defined(_OS_LINUX_) || defined(_OS_FREEBSD_) - "libatomic.so.1"; -#elif defined(_OS_WINDOWS_) - "libatomic-1.dll"; -#else - NULL; -#endif + auto TT = getTargetTriple(); + const char *const libatomic = TT.isOSLinux() || TT.isOSFreeBSD() ? + "libatomic.so.1" : TT.isOSWindows() ? + "libatomic-1.dll" : nullptr; if (libatomic) { static void *atomic_hdl = jl_load_dynamic_library(libatomic, JL_RTLD_LOCAL, 0); if (atomic_hdl != NULL) { diff --git a/src/jitlayers.h b/src/jitlayers.h index 9532d06bdf9cf..1b62c87910a7f 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -199,12 +199,11 @@ typedef struct _jl_codegen_params_t { // Map from symbol name (in a certain library) to its GV in sysimg and the // DL handle address in the current session. StringMap> libMapGV; -#ifdef _OS_WINDOWS_ + SymMapGV symMapDefault; + // These symMaps are Windows-only SymMapGV symMapExe; SymMapGV symMapDll; SymMapGV symMapDlli; -#endif - SymMapGV symMapDefault; // Map from distinct callee's to its GOT entry. // In principle the attribute, function type and calling convention // don't need to be part of the key but it seems impossible to forward From 6d5775c96f62bd7f8b3eecca9d2128c3212cac88 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Mon, 6 Mar 2023 04:17:50 -0500 Subject: [PATCH 268/775] Remove OS ifdefs from some optimization passes --- src/llvm-lower-handlers.cpp | 28 +++++++++--------- src/llvm-ptls.cpp | 57 +++++++++++++++++++------------------ 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/src/llvm-lower-handlers.cpp b/src/llvm-lower-handlers.cpp index a5b05cb4f9066..919128769019b 100644 --- a/src/llvm-lower-handlers.cpp +++ b/src/llvm-lower-handlers.cpp @@ -81,7 +81,7 @@ namespace { * If the module doesn't have declarations for the jl_enter_handler and setjmp * functions, insert them. */ -static void ensure_enter_function(Module &M) +static void ensure_enter_function(Module &M, const Triple &TT) { auto T_int8 = Type::getInt8Ty(M.getContext()); auto T_pint8 = PointerType::get(T_int8, 0); @@ -96,9 +96,9 @@ static void ensure_enter_function(Module &M) if (!M.getNamedValue(jl_setjmp_name)) { std::vector args2(0); args2.push_back(T_pint8); -#ifndef _OS_WINDOWS_ - args2.push_back(T_int32); -#endif + if (!TT.isOSWindows()) { + args2.push_back(T_int32); + } Function::Create(FunctionType::get(T_int32, args2, false), Function::ExternalLinkage, jl_setjmp_name, &M) ->addFnAttr(Attribute::ReturnsTwice); @@ -107,10 +107,11 @@ static void ensure_enter_function(Module &M) static bool lowerExcHandlers(Function &F) { Module &M = *F.getParent(); + Triple TT(M.getTargetTriple()); Function *except_enter_func = M.getFunction("julia.except_enter"); if (!except_enter_func) return false; // No EH frames in this module - ensure_enter_function(M); + ensure_enter_function(M, TT); Function *leave_func = M.getFunction(XSTR(jl_pop_handler)); Function *jlenter_func = M.getFunction(XSTR(jl_enter_handler)); Function *setjmp_func = M.getFunction(jl_setjmp_name); @@ -197,14 +198,15 @@ static bool lowerExcHandlers(Function &F) { buff }; CallInst::Create(lifetime_start, lifetime_args, "", new_enter); -#ifndef _OS_WINDOWS_ - // For LLVM 3.3 compatibility - Value *args[] = {buff, - ConstantInt::get(Type::getInt32Ty(F.getContext()), 0)}; - auto sj = CallInst::Create(setjmp_func, args, "", enter); -#else - auto sj = CallInst::Create(setjmp_func, buff, "", enter); -#endif + CallInst *sj; + if (!TT.isOSWindows()) { + // For LLVM 3.3 compatibility + Value *args[] = {buff, + ConstantInt::get(Type::getInt32Ty(F.getContext()), 0)}; + sj = CallInst::Create(setjmp_func, args, "", enter); + } else { + sj = CallInst::Create(setjmp_func, buff, "", enter); + } // We need to mark this on the call site as well. See issue #6757 sj->setCanReturnTwice(); if (auto dbg = enter->getMetadata(LLVMContext::MD_dbg)) { diff --git a/src/llvm-ptls.cpp b/src/llvm-ptls.cpp index 5cc8036217e44..844a668f185ed 100644 --- a/src/llvm-ptls.cpp +++ b/src/llvm-ptls.cpp @@ -37,13 +37,14 @@ namespace { struct LowerPTLS { LowerPTLS(Module &M, bool imaging_mode=false) - : imaging_mode(imaging_mode), M(&M) + : imaging_mode(imaging_mode), M(&M), TargetTriple(M.getTargetTriple()) {} bool run(bool *CFGModified); private: const bool imaging_mode; Module *M; + Triple TargetTriple; MDNode *tbaa_const{nullptr}; MDNode *tbaa_gcframe{nullptr}; FunctionType *FT_pgcstack_getter{nullptr}; @@ -253,18 +254,18 @@ void LowerPTLS::fix_pgcstack_use(CallInst *pgcstack, Function *pgcstack_getter, auto getter = new LoadInst(T_pgcstack_getter, pgcstack_func_slot, "", false, pgcstack); getter->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const); getter->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None)); -#if defined(_OS_DARWIN_) - auto key = new LoadInst(T_size, pgcstack_key_slot, "", false, pgcstack); - key->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const); - key->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None)); - auto new_pgcstack = CallInst::Create(FT_pgcstack_getter, getter, {key}, "", pgcstack); - new_pgcstack->takeName(pgcstack); - pgcstack->replaceAllUsesWith(new_pgcstack); - pgcstack->eraseFromParent(); - pgcstack = new_pgcstack; -#else - pgcstack->setCalledFunction(pgcstack->getFunctionType(), getter); -#endif + if (TargetTriple.isOSDarwin()) { + auto key = new LoadInst(T_size, pgcstack_key_slot, "", false, pgcstack); + key->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const); + key->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None)); + auto new_pgcstack = CallInst::Create(FT_pgcstack_getter, getter, {key}, "", pgcstack); + new_pgcstack->takeName(pgcstack); + pgcstack->replaceAllUsesWith(new_pgcstack); + pgcstack->eraseFromParent(); + pgcstack = new_pgcstack; + } else { + pgcstack->setCalledFunction(pgcstack->getFunctionType(), getter); + } set_pgcstack_attrs(pgcstack); } else if (jl_tls_offset != -1) { @@ -278,17 +279,17 @@ void LowerPTLS::fix_pgcstack_use(CallInst *pgcstack, Function *pgcstack_getter, jl_pgcstack_getkey(&f, &k); Constant *val = ConstantInt::get(T_size, (uintptr_t)f); val = ConstantExpr::getIntToPtr(val, T_pgcstack_getter); -#if defined(_OS_DARWIN_) - assert(sizeof(k) == sizeof(uintptr_t)); - Constant *key = ConstantInt::get(T_size, (uintptr_t)k); - auto new_pgcstack = CallInst::Create(FT_pgcstack_getter, val, {key}, "", pgcstack); - new_pgcstack->takeName(pgcstack); - pgcstack->replaceAllUsesWith(new_pgcstack); - pgcstack->eraseFromParent(); - pgcstack = new_pgcstack; -#else - pgcstack->setCalledFunction(pgcstack->getFunctionType(), val); -#endif + if (TargetTriple.isOSDarwin()) { + assert(sizeof(k) == sizeof(uintptr_t)); + Constant *key = ConstantInt::get(T_size, (uintptr_t)k); + auto new_pgcstack = CallInst::Create(FT_pgcstack_getter, val, {key}, "", pgcstack); + new_pgcstack->takeName(pgcstack); + pgcstack->replaceAllUsesWith(new_pgcstack); + pgcstack->eraseFromParent(); + pgcstack = new_pgcstack; + } else { + pgcstack->setCalledFunction(pgcstack->getFunctionType(), val); + } set_pgcstack_attrs(pgcstack); } } @@ -307,10 +308,10 @@ bool LowerPTLS::run(bool *CFGModified) T_size = M->getDataLayout().getIntPtrType(M->getContext()); FT_pgcstack_getter = pgcstack_getter->getFunctionType(); -#if defined(_OS_DARWIN_) - assert(sizeof(jl_pgcstack_key_t) == sizeof(uintptr_t)); - FT_pgcstack_getter = FunctionType::get(FT_pgcstack_getter->getReturnType(), {T_size}, false); -#endif + if (TargetTriple.isOSDarwin()) { + assert(sizeof(jl_pgcstack_key_t) == sizeof(uintptr_t)); + FT_pgcstack_getter = FunctionType::get(FT_pgcstack_getter->getReturnType(), {T_size}, false); + } T_pgcstack_getter = FT_pgcstack_getter->getPointerTo(); T_pppjlvalue = cast(FT_pgcstack_getter->getReturnType()); if (imaging_mode) { From 7bc670fdc23f5c3158c2528ffb1f9b20427de31b Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Mon, 6 Mar 2023 15:58:39 -0500 Subject: [PATCH 269/775] Remove a few more OS and CPPU ifdefs --- src/aotcompile.cpp | 64 ++++++++++++++++++------------------ src/codegen.cpp | 15 ++++++--- src/intrinsics.cpp | 48 +++++++++++++-------------- src/llvm-cpufeatures.cpp | 37 ++++++++++----------- src/llvm-demote-float16.cpp | 22 ++++++------- src/llvm-multiversioning.cpp | 21 ++++++------ src/llvm-ptls.cpp | 53 ++++++++++++----------------- 7 files changed, 128 insertions(+), 132 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 79cba30e5f23b..2e64362599d7a 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -436,15 +436,16 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm CreateNativeGlobals += gvars.size(); //Safe b/c context is locked by params -#if defined(_OS_WINDOWS_) && defined(_CPU_X86_64_) - // setting the function personality enables stack unwinding and catching exceptions - // so make sure everything has something set - Type *T_int32 = Type::getInt32Ty(clone.getModuleUnlocked()->getContext()); - Function *juliapersonality_func = - Function::Create(FunctionType::get(T_int32, true), - Function::ExternalLinkage, "__julia_personality", clone.getModuleUnlocked()); - juliapersonality_func->setDLLStorageClass(GlobalValue::DLLImportStorageClass); -#endif + auto TT = Triple(clone.getModuleUnlocked()->getTargetTriple()); + Function *juliapersonality_func = nullptr; + if (TT.isOSWindows() && TT.getArch() == Triple::x86_64) { + // setting the function personality enables stack unwinding and catching exceptions + // so make sure everything has something set + Type *T_int32 = Type::getInt32Ty(clone.getModuleUnlocked()->getContext()); + juliapersonality_func = Function::Create(FunctionType::get(T_int32, true), + Function::ExternalLinkage, "__julia_personality", clone.getModuleUnlocked()); + juliapersonality_func->setDLLStorageClass(GlobalValue::DLLImportStorageClass); + } // move everything inside, now that we've merged everything // (before adding the exported headers) @@ -455,11 +456,11 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm G.setLinkage(GlobalValue::ExternalLinkage); G.setVisibility(GlobalValue::HiddenVisibility); makeSafeName(G); -#if defined(_OS_WINDOWS_) && defined(_CPU_X86_64_) - // Add unwind exception personalities to functions to handle async exceptions - if (Function *F = dyn_cast(&G)) - F->setPersonalityFn(juliapersonality_func); -#endif + if (TT.isOSWindows() && TT.getArch() == Triple::x86_64) { + // Add unwind exception personalities to functions to handle async exceptions + if (Function *F = dyn_cast(&G)) + F->setPersonalityFn(juliapersonality_func); + } } } } @@ -1446,30 +1447,29 @@ void jl_dump_native_impl(void *native_code, // want less optimizations there. Triple TheTriple = Triple(jl_ExecutionEngine->getTargetTriple()); // make sure to emit the native object format, even if FORCE_ELF was set in codegen -#if defined(_OS_WINDOWS_) - TheTriple.setObjectFormat(Triple::COFF); -#elif defined(_OS_DARWIN_) - TheTriple.setObjectFormat(Triple::MachO); - TheTriple.setOS(llvm::Triple::MacOSX); -#endif + if (TheTriple.isOSWindows()) { + TheTriple.setObjectFormat(Triple::COFF); + } else if (TheTriple.isOSDarwin()) { + TheTriple.setObjectFormat(Triple::MachO); + TheTriple.setOS(llvm::Triple::MacOSX); + } + Optional RelocModel; + if (TheTriple.isOSLinux() || TheTriple.isOSFreeBSD()) { + RelocModel = Reloc::PIC_; + } + CodeModel::Model CMModel = CodeModel::Small; + if (TheTriple.isPPC()) { + // On PPC the small model is limited to 16bit offsets + CMModel = CodeModel::Medium; + } std::unique_ptr SourceTM( jl_ExecutionEngine->getTarget().createTargetMachine( TheTriple.getTriple(), jl_ExecutionEngine->getTargetCPU(), jl_ExecutionEngine->getTargetFeatureString(), jl_ExecutionEngine->getTargetOptions(), -#if defined(_OS_LINUX_) || defined(_OS_FREEBSD_) - Reloc::PIC_, -#else - Optional(), -#endif -#if defined(_CPU_PPC_) || defined(_CPU_PPC64_) - // On PPC the small model is limited to 16bit offsets - CodeModel::Medium, -#else - // Use small model so that we can use signed 32bits offset in the function and GV tables - CodeModel::Small, -#endif + RelocModel, + CMModel, CodeGenOpt::Aggressive // -O3 TODO: respect command -O0 flag? )); diff --git a/src/codegen.cpp b/src/codegen.cpp index c92a9f787af2f..9539733c68fd7 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3,9 +3,6 @@ #undef DEBUG #include "llvm-version.h" #include "platform.h" -#if defined(_CPU_X86_) -#define JL_NEED_FLOATTEMP_VAR 1 -#endif #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS @@ -86,6 +83,9 @@ using namespace llvm; static bool jl_fpo_disabled(const Triple &TT) { +#ifdef JL_DISABLE_FPO + return true; +#endif #ifdef _COMPILER_MSAN_ENABLED_ // MSAN doesn't support FPO return true; @@ -96,6 +96,13 @@ static bool jl_fpo_disabled(const Triple &TT) { return false; } +static bool jl_floattemp_var_needed(const Triple &TT) { +#ifdef JL_NEED_FLOATTEMP_VAR + return true; +#endif + return TT.getArch() == Triple::x86; +} + //Drag some useful type functions into our namespace //to reduce verbosity of our code auto getInt1Ty(LLVMContext &ctxt) { @@ -2920,7 +2927,7 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t a return ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 1); if (at->isIntegerTy() || at->isPointerTy() || at->isFloatingPointTy()) { - Type *at_int = INTT(at); + Type *at_int = INTT(at, ctx.emission_context.DL); Value *varg1 = emit_unbox(ctx, at_int, arg1, arg1.typ); Value *varg2 = emit_unbox(ctx, at_int, arg2, arg2.typ); return ctx.builder.CreateICmpEQ(varg1, varg2); diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 3f6c602e3ec99..8aec4a4990e6f 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -154,13 +154,13 @@ static Type *FLOATT(Type *t) } // convert an llvm type to same-size int type -static Type *INTT(Type *t) +static Type *INTT(Type *t, const DataLayout &DL) { auto &ctxt = t->getContext(); if (t->isIntegerTy()) return t; if (t->isPointerTy()) - return getSizeTy(ctxt); + return DL.getIntPtrType(t); if (t == getDoubleTy(ctxt)) return getInt64Ty(ctxt); if (t == getFloatTy(ctxt)) @@ -343,22 +343,19 @@ static Value *emit_unboxed_coercion(jl_codectx_t &ctx, Type *to, Value *unboxed) unboxed = emit_bitcast(ctx, unboxed, to); } else if (!ty->isIntOrPtrTy() && !ty->isFloatingPointTy()) { -#ifndef JL_NDEBUG - const DataLayout &DL = jl_Module->getDataLayout(); -#endif assert(DL.getTypeSizeInBits(ty) == DL.getTypeSizeInBits(to)); AllocaInst *cast = ctx.builder.CreateAlloca(ty); ctx.builder.CreateStore(unboxed, cast); unboxed = ctx.builder.CreateLoad(to, ctx.builder.CreateBitCast(cast, to->getPointerTo())); } else if (frompointer) { - Type *INTT_to = INTT(to); + Type *INTT_to = INTT(to, DL); unboxed = ctx.builder.CreatePtrToInt(unboxed, INTT_to); if (INTT_to != to) unboxed = ctx.builder.CreateBitCast(unboxed, to); } else if (topointer) { - Type *INTT_to = INTT(to); + Type *INTT_to = INTT(to, DL); if (to != INTT_to) unboxed = ctx.builder.CreateBitCast(unboxed, INTT_to); unboxed = emit_inttoptr(ctx, unboxed, to); @@ -584,6 +581,8 @@ static jl_cgval_t generic_cast( intrinsic f, Instruction::CastOps Op, const jl_cgval_t *argv, bool toint, bool fromint) { + auto &TT = ctx.emission_context.TargetTriple; + auto &DL = ctx.emission_context.DL; const jl_cgval_t &targ = argv[0]; const jl_cgval_t &v = argv[1]; jl_datatype_t *jlto = staticeval_bitstype(targ); @@ -593,11 +592,11 @@ static jl_cgval_t generic_cast( Type *to = bitstype_to_llvm((jl_value_t*)jlto, ctx.builder.getContext(), true); Type *vt = bitstype_to_llvm(v.typ, ctx.builder.getContext(), true); if (toint) - to = INTT(to); + to = INTT(to, DL); else to = FLOATT(to); if (fromint) - vt = INTT(vt); + vt = INTT(vt, DL); else vt = FLOATT(vt); if (!to || !vt) @@ -606,17 +605,17 @@ static jl_cgval_t generic_cast( if (!CastInst::castIsValid(Op, from, to)) return emit_runtime_call(ctx, f, argv, 2); if (Op == Instruction::FPExt) { -#ifdef JL_NEED_FLOATTEMP_VAR - // Target platform might carry extra precision. - // Force rounding to single precision first. The reason is that it's - // fine to keep working in extended precision as long as it's - // understood that everything is implicitly rounded to 23 bits, - // but if we start looking at more bits we need to actually do the - // rounding first instead of carrying around incorrect low bits. - Value *jlfloattemp_var = emit_static_alloca(ctx, from->getType()); - ctx.builder.CreateStore(from, jlfloattemp_var); - from = ctx.builder.CreateLoad(from->getType(), jlfloattemp_var, /*force this to load from the stack*/true); -#endif + if (jl_floattemp_var_needed(TT)) { + // Target platform might carry extra precision. + // Force rounding to single precision first. The reason is that it's + // fine to keep working in extended precision as long as it's + // understood that everything is implicitly rounded to 23 bits, + // but if we start looking at more bits we need to actually do the + // rounding first instead of carrying around incorrect low bits. + Value *jlfloattemp_var = emit_static_alloca(ctx, from->getType()); + ctx.builder.CreateStore(from, jlfloattemp_var); + from = ctx.builder.CreateLoad(from->getType(), jlfloattemp_var, /*force this to load from the stack*/true); + } } Value *ans = ctx.builder.CreateCast(Op, from, to); if (f == fptosi || f == fptoui) @@ -1126,6 +1125,7 @@ static jl_cgval_t emit_ifelse(jl_codectx_t &ctx, jl_cgval_t c, jl_cgval_t x, jl_ static jl_cgval_t emit_intrinsic(jl_codectx_t &ctx, intrinsic f, jl_value_t **args, size_t nargs) { + auto &DL = ctx.emission_context.DL; assert(f < num_intrinsics); if (f == cglobal && nargs == 1) f = cglobal_auto; @@ -1231,7 +1231,7 @@ static jl_cgval_t emit_intrinsic(jl_codectx_t &ctx, intrinsic f, jl_value_t **ar const jl_cgval_t &x = argv[0]; if (!jl_is_primitivetype(x.typ)) return emit_runtime_call(ctx, f, argv, nargs); - Type *xt = INTT(bitstype_to_llvm(x.typ, ctx.builder.getContext(), true)); + Type *xt = INTT(bitstype_to_llvm(x.typ, ctx.builder.getContext(), true), DL); Value *from = emit_unbox(ctx, xt, x, x.typ); Value *ans = ctx.builder.CreateNot(from); return mark_julia_type(ctx, ans, false, x.typ); @@ -1270,7 +1270,7 @@ static jl_cgval_t emit_intrinsic(jl_codectx_t &ctx, intrinsic f, jl_value_t **ar if (float_func()[f]) xtyp = FLOATT(xtyp); else - xtyp = INTT(xtyp); + xtyp = INTT(xtyp, DL); if (!xtyp) return emit_runtime_call(ctx, f, argv, nargs); ////Bool are required to be in the range [0,1] @@ -1289,7 +1289,7 @@ static jl_cgval_t emit_intrinsic(jl_codectx_t &ctx, intrinsic f, jl_value_t **ar if (f == shl_int || f == lshr_int || f == ashr_int) { if (!jl_is_primitivetype(argv[1].typ)) return emit_runtime_call(ctx, f, argv, nargs); - argt[1] = INTT(bitstype_to_llvm(argv[1].typ, ctx.builder.getContext(), true)); + argt[1] = INTT(bitstype_to_llvm(argv[1].typ, ctx.builder.getContext(), true), DL); } else { for (size_t i = 1; i < nargs; ++i) { @@ -1465,7 +1465,7 @@ static Value *emit_untyped_intrinsic(jl_codectx_t &ctx, intrinsic f, Value **arg case fpiseq: { *newtyp = jl_bool_type; - Type *it = INTT(t); + Type *it = INTT(t, ctx.emission_context.DL); Value *xi = ctx.builder.CreateBitCast(x, it); Value *yi = ctx.builder.CreateBitCast(y, it); return ctx.builder.CreateOr(ctx.builder.CreateAnd(ctx.builder.CreateFCmpUNO(x, x), diff --git a/src/llvm-cpufeatures.cpp b/src/llvm-cpufeatures.cpp index 0f39bf06d2101..b9bff66092d75 100644 --- a/src/llvm-cpufeatures.cpp +++ b/src/llvm-cpufeatures.cpp @@ -38,20 +38,18 @@ STATISTIC(LoweredWithoutFMA, "Number of have_fma's that were lowered to false"); extern JuliaOJIT *jl_ExecutionEngine; // whether this platform unconditionally (i.e. without needing multiversioning) supports FMA -Optional always_have_fma(Function &intr) JL_NOTSAFEPOINT { - auto intr_name = intr.getName(); - auto typ = intr_name.substr(strlen("julia.cpu.have_fma.")); - -#if defined(_CPU_AARCH64_) - return typ == "f32" || typ == "f64"; -#else - (void)typ; - return {}; -#endif +Optional always_have_fma(Function &intr, const Triple &TT) JL_NOTSAFEPOINT { + if (TT.isAArch64()) { + auto intr_name = intr.getName(); + auto typ = intr_name.substr(strlen("julia.cpu.have_fma.")); + return typ == "f32" || typ == "f64"; + } else { + return {}; + } } -bool have_fma(Function &intr, Function &caller) JL_NOTSAFEPOINT { - auto unconditional = always_have_fma(intr); +static bool have_fma(Function &intr, Function &caller, const Triple &TT) JL_NOTSAFEPOINT { + auto unconditional = always_have_fma(intr, TT); if (unconditional.hasValue()) return unconditional.getValue(); @@ -65,21 +63,21 @@ bool have_fma(Function &intr, Function &caller) JL_NOTSAFEPOINT { SmallVector Features; FS.split(Features, ','); for (StringRef Feature : Features) -#if defined _CPU_ARM_ + if (TT.isARM()) { if (Feature == "+vfp4") - return typ == "f32" || typ == "f64";lowerCPUFeatures + return typ == "f32" || typ == "f64"; else if (Feature == "+vfp4sp") return typ == "f32"; -#else + } else { if (Feature == "+fma" || Feature == "+fma4") return typ == "f32" || typ == "f64"; -#endif + } return false; } -void lowerHaveFMA(Function &intr, Function &caller, CallInst *I) JL_NOTSAFEPOINT { - if (have_fma(intr, caller)) { +void lowerHaveFMA(Function &intr, Function &caller, const Triple &TT, CallInst *I) JL_NOTSAFEPOINT { + if (have_fma(intr, caller, TT)) { ++LoweredWithFMA; I->replaceAllUsesWith(ConstantInt::get(I->getType(), 1)); } else { @@ -91,6 +89,7 @@ void lowerHaveFMA(Function &intr, Function &caller, CallInst *I) JL_NOTSAFEPOINT bool lowerCPUFeatures(Module &M) JL_NOTSAFEPOINT { + auto TT = Triple(M.getTargetTriple()); SmallVector Materialized; for (auto &F: M.functions()) { @@ -100,7 +99,7 @@ bool lowerCPUFeatures(Module &M) JL_NOTSAFEPOINT for (Use &U: F.uses()) { User *RU = U.getUser(); CallInst *I = cast(RU); - lowerHaveFMA(F, *I->getParent()->getParent(), I); + lowerHaveFMA(F, *I->getParent()->getParent(), TT, I); Materialized.push_back(I); } } diff --git a/src/llvm-demote-float16.cpp b/src/llvm-demote-float16.cpp index 3d9f0664b2001..e27d3f19b3f21 100644 --- a/src/llvm-demote-float16.cpp +++ b/src/llvm-demote-float16.cpp @@ -49,26 +49,26 @@ extern JuliaOJIT *jl_ExecutionEngine; namespace { -bool have_fp16(Function &caller) { +static bool have_fp16(Function &caller, const Triple &TT) { Attribute FSAttr = caller.getFnAttribute("target-features"); StringRef FS = FSAttr.isValid() ? FSAttr.getValueAsString() : jl_ExecutionEngine->getTargetFeatureString(); -#if defined(_CPU_AARCH64_) - if (FS.find("+fp16fml") != llvm::StringRef::npos || FS.find("+fullfp16") != llvm::StringRef::npos){ - return true; - } -#elif defined(_CPU_X86_64_) - if (FS.find("+avx512fp16") != llvm::StringRef::npos){ - return true; + if (TT.isAArch64()) { + if (FS.find("+fp16fml") != llvm::StringRef::npos || FS.find("+fullfp16") != llvm::StringRef::npos){ + return true; + } + } else if (TT.getArch() == Triple::x86_64) { + if (FS.find("+avx512fp16") != llvm::StringRef::npos){ + return true; + } } -#endif - (void)FS; return false; } static bool demoteFloat16(Function &F) { - if (have_fp16(F)) + auto TT = Triple(F.getParent()->getTargetTriple()); + if (have_fp16(F, TT)) return false; auto &ctx = F.getContext(); diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 0474cb0c7add7..1041fdb540028 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -46,7 +46,7 @@ using namespace llvm; -extern Optional always_have_fma(Function&); +extern Optional always_have_fma(Function&, const Triple &TT); void replaceUsesWithLoad(Function &F, function_ref should_replace, MDNode *tbaa_const); @@ -78,7 +78,7 @@ static bool is_vector(FunctionType *ty) return false; } -static uint32_t collect_func_info(Function &F, bool &has_veccall) +static uint32_t collect_func_info(Function &F, const Triple &TT, bool &has_veccall) { DominatorTree DT(F); LoopInfo LI(DT); @@ -105,7 +105,7 @@ static uint32_t collect_func_info(Function &F, bool &has_veccall) if (name.startswith("julia.cpu.have_fma.")) { // for some platforms we know they always do (or don't) support // FMA. in those cases we don't need to clone the function. - if (!always_have_fma(*callee).hasValue()) + if (!always_have_fma(*callee, TT).hasValue()) flag |= JL_TARGET_CLONE_CPU; } else { flag |= JL_TARGET_CLONE_CPU; @@ -201,6 +201,7 @@ static void set_target_specs(Module &M, ArrayRef specs) { } static void annotate_module_clones(Module &M) { + auto TT = Triple(M.getTargetTriple()); CallGraph CG(M); std::vector orig_funcs; for (auto &F: M) { @@ -225,7 +226,7 @@ static void annotate_module_clones(Module &M) { std::vector func_infos(orig_funcs.size()); for (unsigned i = 0; i < orig_funcs.size(); i++) { - func_infos[i] = collect_func_info(*orig_funcs[i], has_veccall); + func_infos[i] = collect_func_info(*orig_funcs[i], TT, has_veccall); } for (unsigned i = 1; i < specs.size(); i++) { if (specs[i].flags & JL_TARGET_CLONE_ALL) { @@ -384,6 +385,7 @@ struct CloneCtx { std::vector fvars; std::vector gvars; Module &M; + Triple TT; // Map from original function to one based index in `fvars` std::map func_ids{}; @@ -439,6 +441,7 @@ CloneCtx::CloneCtx(Module &M, bool allow_bad_fvars) fvars(consume_gv(M, "jl_fvars", allow_bad_fvars)), gvars(consume_gv(M, "jl_gvars", false)), M(M), + TT(M.getTargetTriple()), allow_bad_fvars(allow_bad_fvars) { groups.emplace_back(0); @@ -686,14 +689,12 @@ void CloneCtx::rewrite_alias(GlobalAlias *alias, Function *F) for (auto &arg : trampoline->args()) Args.push_back(&arg); auto call = irbuilder.CreateCall(F->getFunctionType(), ptr, makeArrayRef(Args)); - if (F->isVarArg()) -#if (defined(_CPU_ARM_) || defined(_CPU_PPC_) || defined(_CPU_PPC64_)) - abort(); // musttail support is very bad on ARM, PPC, PPC64 (as of LLVM 3.9) -#else + if (F->isVarArg()) { + assert(!TT.isARM() && !TT.isPPC() && "musttail not supported on ARM/PPC!"); call->setTailCallKind(CallInst::TCK_MustTail); -#endif - else + } else { call->setTailCallKind(CallInst::TCK_Tail); + } if (F->getReturnType() == Type::getVoidTy(F->getContext())) irbuilder.CreateRetVoid(); diff --git a/src/llvm-ptls.cpp b/src/llvm-ptls.cpp index 844a668f185ed..3c73b23dddac6 100644 --- a/src/llvm-ptls.cpp +++ b/src/llvm-ptls.cpp @@ -70,25 +70,17 @@ void LowerPTLS::set_pgcstack_attrs(CallInst *pgcstack) const Instruction *LowerPTLS::emit_pgcstack_tp(Value *offset, Instruction *insertBefore) const { Value *tls; -#if defined(_CPU_X86_64_) || defined(_CPU_X86_) - if (insertBefore->getFunction()->callsFunctionThatReturnsTwice()) { + if (TargetTriple.isX86() && insertBefore->getFunction()->callsFunctionThatReturnsTwice()) { // Workaround LLVM bug by hiding the offset computation // (and therefore the optimization opportunity) from LLVM. // Ref https://github.com/JuliaLang/julia/issues/17288 - static const std::string const_asm_str = [&] () { - std::string stm; -# if defined(_CPU_X86_64_) - raw_string_ostream(stm) << "movq %fs:0, $0;\naddq $$" << jl_tls_offset << ", $0"; -# else - raw_string_ostream(stm) << "movl %gs:0, $0;\naddl $$" << jl_tls_offset << ", $0"; -# endif - return stm; - }(); -# if defined(_CPU_X86_64_) - const char *dyn_asm_str = "movq %fs:0, $0;\naddq $1, $0"; -# else - const char *dyn_asm_str = "movl %gs:0, $0;\naddl $1, $0"; -# endif + std::string const_asm_str; + raw_string_ostream(const_asm_str) << (TargetTriple.getArch() == Triple::x86_64 ? + "movq %fs:0, $0;\naddq $$" : "movl %gs:0, $0;\naddl $$") + << jl_tls_offset << ", $0"; + const char *dyn_asm_str = TargetTriple.getArch() == Triple::x86_64 ? + "movq %fs:0, $0;\naddq $1, $0" : + "movl %gs:0, $0;\naddl $1, $0"; // The add instruction clobbers flags if (offset) { @@ -104,28 +96,25 @@ Instruction *LowerPTLS::emit_pgcstack_tp(Value *offset, Instruction *insertBefor false); tls = CallInst::Create(tp, "pgcstack_i8", insertBefore); } - } - else -#endif - { + } else { // AArch64/ARM doesn't seem to have this issue. // (Possibly because there are many more registers and the offset is // positive and small) // It's also harder to emit the offset in a generic way on ARM/AArch64 // (need to generate one or two `add` with shift) so let llvm emit // the add for now. -#if defined(_CPU_AARCH64_) - const char *asm_str = "mrs $0, tpidr_el0"; -#elif defined(__ARM_ARCH) && __ARM_ARCH >= 7 - const char *asm_str = "mrc p15, 0, $0, c13, c0, 3"; -#elif defined(_CPU_X86_64_) - const char *asm_str = "movq %fs:0, $0"; -#elif defined(_CPU_X86_) - const char *asm_str = "movl %gs:0, $0"; -#else - const char *asm_str = nullptr; - assert(0 && "Cannot emit thread pointer for this architecture."); -#endif + const char *asm_str; + if (TargetTriple.isAArch64()) { + asm_str = "mrs $0, tpidr_el0"; + } else if (TargetTriple.isARM()) { + asm_str = "mrc p15, 0, $0, c13, c0, 3"; + } else if (TargetTriple.getArch() == Triple::x86_64) { + asm_str = "movq %fs:0, $0"; + } else if (TargetTriple.getArch() == Triple::x86) { + asm_str = "movl %gs:0, $0"; + } else { + llvm_unreachable("Cannot emit thread pointer for this architecture."); + } if (!offset) offset = ConstantInt::getSigned(T_size, jl_tls_offset); auto tp = InlineAsm::get(FunctionType::get(Type::getInt8PtrTy(insertBefore->getContext()), false), asm_str, "=r", false); From a3a92e8dd82146155c15cb7f5dc411fe29a6fc32 Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Thu, 16 Feb 2023 20:49:31 -0500 Subject: [PATCH 270/775] Fix `enq_work` behavior when single-threaded If there's only one thread in the task's preferred thread pool, use that thread's work queue. --- base/task.jl | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/base/task.jl b/base/task.jl index ce34d2f179fc5..63d0e9b6bd757 100644 --- a/base/task.jl +++ b/base/task.jl @@ -767,22 +767,33 @@ end function enq_work(t::Task) (t._state === task_state_runnable && t.queue === nothing) || error("schedule: Task not runnable") - if t.sticky || Threads.threadpoolsize() == 1 + + # Sticky tasks go into their thread's work queue. + if t.sticky tid = Threads.threadid(t) if tid == 0 - # Issue #41324 - # t.sticky && tid == 0 is a task that needs to be co-scheduled with - # the parent task. If the parent (current_task) is not sticky we must - # set it to be sticky. - # XXX: Ideally we would be able to unset this - current_task().sticky = true + # The task is not yet stuck to a thread. Stick it to the current + # thread and do the same to the parent task (the current task) so + # that the tasks are correctly co-scheduled (issue #41324). + # XXX: Ideally we would be able to unset this. tid = Threads.threadid() ccall(:jl_set_task_tid, Cint, (Any, Cint), t, tid-1) + current_task().sticky = true end push!(workqueue_for(tid), t) else - Partr.multiq_insert(t, t.priority) - tid = 0 + tp = Threads.threadpool(t) + if Threads.threadpoolsize(tp) == 1 + # There's only one thread in the task's assigned thread pool; + # use its work queue. + tid = (tp === :default) ? 1 : Threads.threadpoolsize(:default)+1 + ccall(:jl_set_task_tid, Cint, (Any, Cint), t, tid-1) + push!(workqueue_for(tid), t) + else + # Otherwise, put the task in the multiqueue. + Partr.multiq_insert(t, t.priority) + tid = 0 + end end ccall(:jl_wakeup_thread, Cvoid, (Int16,), (tid - 1) % Int16) return t From 55422d988c3a50ca0d272644e456c3959c62b097 Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Thu, 16 Feb 2023 21:19:42 -0500 Subject: [PATCH 271/775] Fix test for threadpool use --- test/threadpool_use.jl | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/test/threadpool_use.jl b/test/threadpool_use.jl index 47c45bdd71eb8..64227c8a8110b 100644 --- a/test/threadpool_use.jl +++ b/test/threadpool_use.jl @@ -6,11 +6,6 @@ using Base.Threads @test nthreadpools() == 2 @test threadpool() === :default @test threadpool(2) === :interactive -dtask() = @test threadpool(current_task()) === :default -itask() = @test threadpool(current_task()) === :interactive -dt1 = @spawn dtask() -dt2 = @spawn :default dtask() -it = @spawn :interactive itask() -wait(dt1) -wait(dt2) -wait(it) +@test fetch(Threads.@spawn Threads.threadpool()) === :default +@test fetch(Threads.@spawn :default Threads.threadpool()) === :default +@test fetch(Threads.@spawn :interactive Threads.threadpool()) === :interactive From 608e26dcd1577906424df37390d761417bfbb809 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 7 Mar 2023 10:57:22 +0900 Subject: [PATCH 272/775] remove `inferred` field from `InferenceState` (#48915) --- base/compiler/abstractinterpretation.jl | 6 +++--- base/compiler/inferencestate.jl | 20 +++++++++----------- base/compiler/typeinfer.jl | 12 +++++------- base/compiler/types.jl | 10 +++++----- 4 files changed, 22 insertions(+), 26 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 93ead3207fce2..439942816fd47 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1018,9 +1018,9 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, add_remark!(interp, sv, "[constprop] Fresh constant inference hit a cycle") return nothing end - @assert !isa(inf_result.result, InferenceState) + @assert inf_result.result !== nothing else - if isa(inf_result.result, InferenceState) + if inf_result.result === nothing add_remark!(interp, sv, "[constprop] Found cached constant inference in a cycle") return nothing end @@ -2820,7 +2820,7 @@ end # make as much progress on `frame` as possible (without handling cycles) function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) - @assert !frame.inferred + @assert !is_inferred(frame) frame.dont_work_on_me = true # mark that this function is currently on the stack W = frame.ip nargs = narguments(frame, #=include_va=#false) diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index e3f4cb2866aa3..cacc358327e7a 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -107,7 +107,6 @@ mutable struct InferenceState callers_in_cycle::Vector{InferenceState} dont_work_on_me::Bool parent::Union{Nothing, InferenceState} - inferred::Bool # TODO move this to InferenceResult? #= results =# result::InferenceResult # remember where to put the result @@ -165,7 +164,6 @@ mutable struct InferenceState callers_in_cycle = Vector{InferenceState}() dont_work_on_me = false parent = nothing - inferred = false valid_worlds = WorldRange(src.min_world, src.max_world == typemax(UInt) ? get_world_counter() : src.max_world) bestguess = Bottom @@ -180,23 +178,23 @@ mutable struct InferenceState @assert cache === :no || cache === :local || cache === :global cached = cache === :global - frame = new( + # some more setups + InferenceParams(interp).unoptimize_throw_blocks && mark_throw_blocks!(src, handler_at) + cache !== :no && push!(get_inference_cache(interp), result) + + return new( linfo, world, mod, sptypes, slottypes, src, cfg, currbb, currpc, ip, handler_at, ssavalue_uses, bb_vartables, ssavaluetypes, stmt_edges, stmt_info, - pclimitations, limitations, cycle_backedges, callers_in_cycle, dont_work_on_me, parent, inferred, + pclimitations, limitations, cycle_backedges, callers_in_cycle, dont_work_on_me, parent, result, valid_worlds, bestguess, ipo_effects, restrict_abstract_call_sites, cached, insert_coverage, interp) - - # some more setups - InferenceParams(interp).unoptimize_throw_blocks && mark_throw_blocks!(src, handler_at) - result.result = frame - cache !== :no && push!(get_inference_cache(interp), result) - - return frame end end +is_inferred(sv::InferenceState) = is_inferred(sv.result) +is_inferred(result::InferenceResult) = result.result !== nothing + Effects(state::InferenceState) = state.ipo_effects function merge_effects!(::AbstractInterpreter, caller::InferenceState, effects::Effects) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index ab7ca4b56a9bd..0395afa6629ac 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -255,8 +255,6 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) for caller in frames caller.valid_worlds = valid_worlds finish(caller, interp) - # finalize and record the linfo result - caller.inferred = true end # collect results for the new expanded frame results = Tuple{InferenceResult, Vector{Any}, Bool}[ @@ -291,7 +289,7 @@ function CodeInstance(interp::AbstractInterpreter, result::InferenceResult, @nospecialize(inferred_result), valid_worlds::WorldRange) local const_flags::Int32 result_type = result.result - @assert !(result_type isa LimitedAccuracy) + @assert !(result_type === nothing || result_type isa LimitedAccuracy) if isa(result_type, Const) && is_foldable_nothrow(result.ipo_effects) && is_inlineable_constant(result_type.val) # use constant calling convention @@ -919,7 +917,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize end typeinf(interp, frame) update_valid_age!(frame, caller) - edge = frame.inferred ? mi : nothing + edge = is_inferred(frame) ? mi : nothing return EdgeCallResult(frame.bestguess, edge, Effects(frame)) # effects are adjusted already within `finish` elseif frame === true # unresolvable cycle @@ -937,7 +935,7 @@ end function typeinf_code(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, run_optimizer::Bool) frame = typeinf_frame(interp, method, atype, sparams, run_optimizer) frame === nothing && return nothing, Any - frame.inferred || return nothing, Any + is_inferred(frame) || return nothing, Any code = frame.src rt = widenconst(ignorelimited(frame.result.result)) return code, rt @@ -1065,7 +1063,7 @@ function typeinf_type(interp::AbstractInterpreter, method::Method, @nospecialize result = InferenceResult(mi, typeinf_lattice(interp)) typeinf(interp, result, :global) ccall(:jl_typeinf_timing_end, Cvoid, ()) - result.result isa InferenceState && return nothing + is_inferred(result) || return nothing return widenconst(ignorelimited(result.result)) end @@ -1084,7 +1082,7 @@ function typeinf_ext_toplevel(interp::AbstractInterpreter, linfo::MethodInstance result = InferenceResult(linfo, typeinf_lattice(interp)) frame = InferenceState(result, src, #=cache=#:global, interp) typeinf(interp, frame) - @assert frame.inferred # TODO: deal with this better + @assert is_inferred(frame) # TODO: deal with this better src = frame.src end ccall(:jl_typeinf_timing_end, Cvoid, ()) diff --git a/base/compiler/types.jl b/base/compiler/types.jl index cac15e9a69513..6496f97a76248 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -58,10 +58,10 @@ A type that represents the result of running type inference on a chunk of code. See also [`matching_cache_argtypes`](@ref). """ mutable struct InferenceResult - linfo::MethodInstance - argtypes::Vector{Any} - overridden_by_const::BitVector - result # ::Type, or InferenceState if WIP + const linfo::MethodInstance + const argtypes::Vector{Any} + const overridden_by_const::BitVector + result # extended lattice element if inferred, nothing otherwise src # ::Union{CodeInfo, IRCode, OptimizationState} if inferred copy is available, nothing otherwise valid_worlds::WorldRange # if inference and optimization is finished ipo_effects::Effects # if inference is finished @@ -69,7 +69,7 @@ mutable struct InferenceResult argescapes # ::ArgEscapeCache if optimized, nothing otherwise must_be_codeinf::Bool # if this must come out as CodeInfo or leaving it as IRCode is ok function InferenceResult(linfo::MethodInstance, cache_argtypes::Vector{Any}, overridden_by_const::BitVector) - return new(linfo, cache_argtypes, overridden_by_const, Any, nothing, + return new(linfo, cache_argtypes, overridden_by_const, nothing, nothing, WorldRange(), Effects(), Effects(), nothing, true) end end From 54eda4170dfea0135f170d6705923aca44a3bea0 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Tue, 7 Mar 2023 13:00:44 +0100 Subject: [PATCH 273/775] Let Base handle concatenation of arrays and numbers --- base/abstractarray.jl | 6 ++++++ stdlib/LinearAlgebra/src/uniformscaling.jl | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index f79872818de31..7be3f39d16def 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1985,12 +1985,16 @@ julia> cat(1, [2], [3;;]; dims=Val(2)) # The specializations for 1 and 2 inputs are important # especially when running with --inline=no, see #11158 +# The specializations for Union{AbstractVecOrMat,Number} are necessary +# to have more specialized methods here than in LinearAlgebra/uniformscaling.jl vcat(A::AbstractArray) = cat(A; dims=Val(1)) vcat(A::AbstractArray, B::AbstractArray) = cat(A, B; dims=Val(1)) vcat(A::AbstractArray...) = cat(A...; dims=Val(1)) +vcat(A::Union{AbstractVecOrMat,Number}...) = cat(A...; dims=Val(1)) hcat(A::AbstractArray) = cat(A; dims=Val(2)) hcat(A::AbstractArray, B::AbstractArray) = cat(A, B; dims=Val(2)) hcat(A::AbstractArray...) = cat(A...; dims=Val(2)) +hcat(A::Union{AbstractVecOrMat,Number}...) = cat(A...; dims=Val(2)) typed_vcat(T::Type, A::AbstractArray) = _cat_t(Val(1), T, A) typed_vcat(T::Type, A::AbstractArray, B::AbstractArray) = _cat_t(Val(1), T, A, B) @@ -2140,6 +2144,8 @@ end hvcat(rows::Tuple{Vararg{Int}}, xs::Number...) = typed_hvcat(promote_typeof(xs...), rows, xs...) hvcat(rows::Tuple{Vararg{Int}}, xs...) = typed_hvcat(promote_eltypeof(xs...), rows, xs...) +# the following method is needed to provide a more specific one compared to LinearAlgebra/uniformscaling.jl +hvcat(rows::Tuple{Vararg{Int}}, xs::Union{AbstractVecOrMat,Number}...) = typed_hvcat(promote_eltypeof(xs...), rows, xs...) function typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}, xs::Number...) where T nr = length(rows) diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index f5c8bdd6f2e75..191b0088adc51 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -419,10 +419,14 @@ promote_to_arrays(n,k, ::Type{T}, A, B, Cs...) where {T} = (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays(n,k+2, T, Cs...)...) promote_to_array_type(A::Tuple{Vararg{Union{AbstractVecOrMat,UniformScaling,Number}}}) = Matrix +_us2number(A::AbstractArray) = A +_us2number(J::UniformScaling) = J.λ +_us2number(x::Number) = x + for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols")) @eval begin @inline $f(A::Union{AbstractVecOrMat,UniformScaling}...) = $_f(A...) - @inline $f(A::Union{AbstractVecOrMat,UniformScaling,Number}...) = $_f(A...) + @inline $f(A::Union{AbstractVecOrMat,UniformScaling,Number}...) = $f(map(_us2number, A)...) function $_f(A::Union{AbstractVecOrMat,UniformScaling,Number}...; array_type = promote_to_array_type(A)) n = -1 for a in A From 03fdd0fa670fd7eb6e1624fe0658644bcb3edc2a Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Tue, 7 Mar 2023 08:29:11 -0500 Subject: [PATCH 274/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=203c073aa25=20to=2040e07927f=20(#48929)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/md5 | 1 - .../Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/sha512 | 1 - .../Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/md5 | 1 + .../Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/md5 create mode 100644 deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/sha512 diff --git a/deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/md5 b/deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/md5 deleted file mode 100644 index f88fb6889864b..0000000000000 --- a/deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -9bdea947b1817ff90de784ddaf767ca0 diff --git a/deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/sha512 b/deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/sha512 deleted file mode 100644 index af2939ed3ddaa..0000000000000 --- a/deps/checksums/Pkg-3c073aa253fa4dda70f2d4433989652c8b69e171.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -c1a0c83967926e8a806f0f8a3dd53b33d98135dff0c5db73fe7f706b49a91222725ecc09c06ccac4531911b4d576afca2eaa166c266d732605e4374c4fc92751 diff --git a/deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/md5 b/deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/md5 new file mode 100644 index 0000000000000..60a30227b807f --- /dev/null +++ b/deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/md5 @@ -0,0 +1 @@ +3bd0dbbc226bf80afe98a58d2e7e3a16 diff --git a/deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/sha512 b/deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/sha512 new file mode 100644 index 0000000000000..a7a6bf0d7e365 --- /dev/null +++ b/deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/sha512 @@ -0,0 +1 @@ +038c9c58c1fac83f48ca0b9606f523ef168acba382b411a3aa7c956b96ec1707a5c88417100c3522629820f81e672af4ed28b089218682cdf4fde7c6cc36b440 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 852079f33ddaa..899147f1a2089 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 3c073aa253fa4dda70f2d4433989652c8b69e171 +PKG_SHA1 = 40e07927f47ec63bb663cdebd4679ebecaf142b8 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 7142d59d7e1739f949d6107b95830e0eec2eb6b5 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Tue, 7 Mar 2023 15:45:16 +0100 Subject: [PATCH 275/775] simplify --- stdlib/LinearAlgebra/src/uniformscaling.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index 191b0088adc51..5ff76e7bfbd2b 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -419,9 +419,8 @@ promote_to_arrays(n,k, ::Type{T}, A, B, Cs...) where {T} = (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays(n,k+2, T, Cs...)...) promote_to_array_type(A::Tuple{Vararg{Union{AbstractVecOrMat,UniformScaling,Number}}}) = Matrix -_us2number(A::AbstractArray) = A +_us2number(A) = A _us2number(J::UniformScaling) = J.λ -_us2number(x::Number) = x for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols")) @eval begin From 6008f8c703207ea6c34f62bd2d6d7be12937ee8f Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Tue, 7 Mar 2023 18:29:39 +0100 Subject: [PATCH 276/775] include review comments --- stdlib/LinearAlgebra/src/uniformscaling.jl | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index 5ff76e7bfbd2b..1e7713288c7b5 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -419,19 +419,18 @@ promote_to_arrays(n,k, ::Type{T}, A, B, Cs...) where {T} = (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays(n,k+2, T, Cs...)...) promote_to_array_type(A::Tuple{Vararg{Union{AbstractVecOrMat,UniformScaling,Number}}}) = Matrix -_us2number(A) = A -_us2number(J::UniformScaling) = J.λ +szfun(::UniformScaling, _) = -1 +szfun(A, dim) = (require_one_based_indexing(A); return size(A, dim)) for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols")) @eval begin @inline $f(A::Union{AbstractVecOrMat,UniformScaling}...) = $_f(A...) - @inline $f(A::Union{AbstractVecOrMat,UniformScaling,Number}...) = $f(map(_us2number, A)...) + @inline $f(A::Union{AbstractVecOrMat,UniformScaling,Number}...) = $_f(A...) function $_f(A::Union{AbstractVecOrMat,UniformScaling,Number}...; array_type = promote_to_array_type(A)) n = -1 - for a in A - if !isa(a, UniformScaling) - require_one_based_indexing(a) - na = size(a,$dim) + sizes = map(a -> szfun(a, $dim), A) + for na in sizes + if na != -1 n >= 0 && n != na && throw(DimensionMismatch(string("number of ", $name, " of each array must match (got ", n, " and ", na, ")"))) @@ -455,9 +454,9 @@ function _hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractVecOrMat,UniformScali j = 0 for i = 1:nr # infer UniformScaling sizes from row counts, if possible: ni = -1 # number of rows in this block-row, -1 indicates unknown - for k = 1:rows[i] - if !isa(A[j+k], UniformScaling) - na = size(A[j+k], 1) + sizes = map(a -> szfun(a, 1), A[j+1:j+rows[i]]) + for na in sizes + if na != -1 ni >= 0 && ni != na && throw(DimensionMismatch("mismatch in number of rows")) ni = na From 2cd167f0e117ce6b12f8a4398fe0732a97818a09 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Tue, 7 Mar 2023 19:46:28 +0100 Subject: [PATCH 277/775] hoist out of loop, rename internal function --- stdlib/LinearAlgebra/src/uniformscaling.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index 1e7713288c7b5..987ad84ae2f24 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -419,8 +419,8 @@ promote_to_arrays(n,k, ::Type{T}, A, B, Cs...) where {T} = (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays(n,k+2, T, Cs...)...) promote_to_array_type(A::Tuple{Vararg{Union{AbstractVecOrMat,UniformScaling,Number}}}) = Matrix -szfun(::UniformScaling, _) = -1 -szfun(A, dim) = (require_one_based_indexing(A); return size(A, dim)) +_catsize(::UniformScaling, _) = -1 +_catsize(A, dim) = (require_one_based_indexing(A); return size(A, dim)) for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols")) @eval begin @@ -428,7 +428,7 @@ for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols" @inline $f(A::Union{AbstractVecOrMat,UniformScaling,Number}...) = $_f(A...) function $_f(A::Union{AbstractVecOrMat,UniformScaling,Number}...; array_type = promote_to_array_type(A)) n = -1 - sizes = map(a -> szfun(a, $dim), A) + sizes = map(a -> _catsize(a, $dim), A) for na in sizes if na != -1 n >= 0 && n != na && @@ -452,10 +452,10 @@ function _hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractVecOrMat,UniformScali n = fill(-1, length(A)) needcols = false # whether we also need to infer some sizes from the column count j = 0 + sizes = map(a -> _catsize(a, 1), A) for i = 1:nr # infer UniformScaling sizes from row counts, if possible: ni = -1 # number of rows in this block-row, -1 indicates unknown - sizes = map(a -> szfun(a, 1), A[j+1:j+rows[i]]) - for na in sizes + for na in sizes[j+1:j+rows[i]] if na != -1 ni >= 0 && ni != na && throw(DimensionMismatch("mismatch in number of rows")) From a3fe0a8c6715f44fcd85591c0a20a2cbaace722c Mon Sep 17 00:00:00 2001 From: Udoh Jeremiah Date: Tue, 7 Mar 2023 23:33:48 +0100 Subject: [PATCH 278/775] close code block for in `PartialQuickSort` docstring (#48937) --- base/sort.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/sort.jl b/base/sort.jl index 4fdd08931e800..cbbcf948b8b4c 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1927,6 +1927,7 @@ julia> map(x->issorted(x[k]), (s1, s2)) julia> s1[k] == s2[k] true +``` """ struct PartialQuickSort{T <: Union{Integer,OrdinalRange}} <: Algorithm k::T From 7ace0fbb3f148a9ff051e731d583674c5b2cca9c Mon Sep 17 00:00:00 2001 From: Udoh Jeremiah Date: Wed, 8 Mar 2023 01:40:49 +0100 Subject: [PATCH 279/775] Fix typos in `Base.ccall_macro_parse` (#48936) --- base/c.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/c.jl b/base/c.jl index cfff070973f25..d94447650b9fb 100644 --- a/base/c.jl +++ b/base/c.jl @@ -565,9 +565,9 @@ end """ ccall_macro_parse(expression) -`ccall_macro_parse` is an implementation detail of `@ccall +`ccall_macro_parse` is an implementation detail of `@ccall`. -it takes an expression like `:(printf("%d"::Cstring, value::Cuint)::Cvoid)` +It takes an expression like `:(printf("%d"::Cstring, value::Cuint)::Cvoid)` returns: a tuple of `(function_name, return_type, arg_types, args)` The above input outputs this: From eb4b1a78388c4a2d2c93dac303c61bdb98052dbe Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 7 Mar 2023 21:21:00 -0500 Subject: [PATCH 280/775] typemap: improve quality of leaf split (#48925) Introduce another optional layer of indirection, doing lookup on first typename always, then leaftypes. This lets the intersection-iterators skip a lot of jl_isa queries later. --- base/reflection.jl | 32 ++++--- src/julia.h | 8 +- src/typemap.c | 226 +++++++++++++++++++++++++++++++++------------ 3 files changed, 190 insertions(+), 76 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index 9e2615a16a190..0e3d5e1fb82a7 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1070,29 +1070,31 @@ function visit(f, mt::Core.MethodTable) nothing end function visit(f, mc::Core.TypeMapLevel) - if mc.targ !== nothing - e = mc.targ::Vector{Any} + function avisit(f, e::Array{Any,1}) for i in 2:2:length(e) - isassigned(e, i) && visit(f, e[i]) + isassigned(e, i) || continue + ei = e[i] + if ei isa Vector{Any} + for j in 2:2:length(ei) + isassigned(ei, j) || continue + visit(f, ei[j]) + end + else + visit(f, ei) + end end end + if mc.targ !== nothing + avisit(f, mc.targ::Vector{Any}) + end if mc.arg1 !== nothing - e = mc.arg1::Vector{Any} - for i in 2:2:length(e) - isassigned(e, i) && visit(f, e[i]) - end + avisit(f, mc.arg1::Vector{Any}) end if mc.tname !== nothing - e = mc.tname::Vector{Any} - for i in 2:2:length(e) - isassigned(e, i) && visit(f, e[i]) - end + avisit(f, mc.tname::Vector{Any}) end if mc.name1 !== nothing - e = mc.name1::Vector{Any} - for i in 2:2:length(e) - isassigned(e, i) && visit(f, e[i]) - end + avisit(f, mc.name1::Vector{Any}) end mc.list !== nothing && visit(f, mc.list) mc.any !== nothing && visit(f, mc.any) diff --git a/src/julia.h b/src/julia.h index f2b6823a133e3..6b0d6aec85cab 100644 --- a/src/julia.h +++ b/src/julia.h @@ -639,10 +639,10 @@ typedef struct _jl_typemap_level_t { // next split may be on Type{T} as LeafTypes then TypeName's parents up to Any // next split may be on LeafType // next split may be on TypeName - _Atomic(jl_array_t*) arg1; // contains LeafType - _Atomic(jl_array_t*) targ; // contains Type{LeafType} - _Atomic(jl_array_t*) name1; // contains non-abstract TypeName, for parents up to (excluding) Any - _Atomic(jl_array_t*) tname; // contains a dict of Type{TypeName}, for parents up to Any + _Atomic(jl_array_t*) arg1; // contains LeafType (in a map of non-abstract TypeName) + _Atomic(jl_array_t*) targ; // contains Type{LeafType} (in a map of non-abstract TypeName) + _Atomic(jl_array_t*) name1; // a map for a map for TypeName, for parents up to (excluding) Any + _Atomic(jl_array_t*) tname; // a map for Type{TypeName}, for parents up to (including) Any // next a linear list of things too complicated at this level for analysis (no more levels) _Atomic(jl_typemap_entry_t*) linear; // finally, start a new level if the type at offs is Any diff --git a/src/typemap.c b/src/typemap.c index 3afa1ffc1e212..49fc2277bc23d 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -249,7 +249,7 @@ static inline int sig_match_simple(jl_value_t *arg1, jl_value_t **args, size_t n // predicate to fast-test if this type is a leaf type that can exist in the cache // and does not need a more expensive linear scan to find all intersections -// be careful not to put non-leaf types or DataType/UnionAll/Union in the +// we try not to put non-leaf types or DataType/UnionAll/Union in the // argument cache, since they should have a lower priority and so will go in some // later list static int is_cache_leaf(jl_value_t *ty, int tparam) @@ -259,11 +259,11 @@ static int is_cache_leaf(jl_value_t *ty, int tparam) return (jl_is_concrete_type(ty) && (tparam || !jl_is_kind(ty))); } -static _Atomic(jl_typemap_t*) *mtcache_hash_lookup_bp(jl_array_t *cache JL_PROPAGATES_ROOT, jl_value_t *ty) JL_NOTSAFEPOINT +static _Atomic(jl_value_t*) *mtcache_hash_lookup_bp(jl_array_t *cache JL_PROPAGATES_ROOT, jl_value_t *ty) JL_NOTSAFEPOINT { if (cache == (jl_array_t*)jl_an_empty_vec_any) return NULL; - _Atomic(jl_typemap_t*) *pml = jl_table_peek_bp(cache, ty); + _Atomic(jl_value_t*) *pml = jl_table_peek_bp(cache, ty); JL_GC_PROMISE_ROOTED(pml); // clang-sa doesn't trust our JL_PROPAGATES_ROOT claim return pml; } @@ -275,13 +275,15 @@ static void mtcache_hash_insert(_Atomic(jl_array_t*) *cache, jl_value_t *parent, if (a == (jl_array_t*)jl_an_empty_vec_any) { a = jl_alloc_vec_any(16); jl_atomic_store_release(cache, a); - jl_gc_wb(parent, a); + if (parent) + jl_gc_wb(parent, a); } a = jl_eqtable_put(a, key, val, &inserted); assert(inserted); if (a != jl_atomic_load_relaxed(cache)) { jl_atomic_store_release(cache, a); - jl_gc_wb(parent, a); + if (parent) + jl_gc_wb(parent, a); } } @@ -302,8 +304,16 @@ static int jl_typemap_array_visitor(jl_array_t *a, jl_typemap_visitor_fptr fptr, for (i = 1; i < l; i += 2) { jl_value_t *d = jl_atomic_load_relaxed(&data[i]); JL_GC_PROMISE_ROOTED(d); - if (d && !jl_typemap_visitor(d, fptr, closure)) - return 0; + if (d == NULL) + continue; + if (jl_is_array(d)) { + if (!jl_typemap_array_visitor((jl_array_t*)d, fptr, closure)) + return 0; + } + else { + if (!jl_typemap_visitor(d, fptr, closure)) + return 0; + } } return 1; } @@ -387,13 +397,23 @@ static int tname_intersection(jl_datatype_t *a, jl_typename_t *bname, unsigned h // tparam bit 1 is ::Type{T} (vs. T) // tparam bit 2 is typename(T) (vs. T) -static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, int tparam, - int offs, struct typemap_intersection_env *closure) +static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, int8_t tparam, + int8_t offs, struct typemap_intersection_env *closure) { JL_GC_PUSH1(&a); size_t i, l = jl_array_len(a); _Atomic(jl_typemap_t*) *data = (_Atomic(jl_typemap_t*)*)jl_array_data(a); - unsigned height = tparam & 2 ? jl_supertype_height((jl_datatype_t*)ty) : 0; + unsigned height = 0; + jl_datatype_t *tydt = NULL; + if (jl_is_kind(ty)) + ty = (jl_value_t*)jl_any_type; + if (tparam & 2) { + tydt = (jl_datatype_t*)jl_unwrap_unionall(ty); + if (jl_is_datatype(ty)) + height = jl_supertype_height(tydt); + else + tydt = jl_any_type; + } for (i = 0; i < l; i += 2) { jl_value_t *t = jl_atomic_load_relaxed(&data[i]); JL_GC_PROMISE_ROOTED(t); @@ -402,17 +422,23 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, if (tparam & 2) { jl_typemap_t *ml = jl_atomic_load_relaxed(&data[i + 1]); JL_GC_PROMISE_ROOTED(ml); - if (ty == (jl_value_t*)jl_any_type || // easy case: Any always matches - tname_intersection((jl_datatype_t*)ty, (jl_typename_t*)t, height)) { - if (!jl_typemap_intersection_visitor(ml, offs + 1, closure)) - goto exit; + if (tydt == jl_any_type || // easy case: Any always matches + tname_intersection(tydt, (jl_typename_t*)t, height)) { + if (jl_is_array(ml)) { + if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, 1, offs, closure)) + goto exit; + } + else { + if (!jl_typemap_intersection_visitor(ml, offs + 1, closure)) + goto exit; + } } } else { - // `t` is a leaftype, so intersection test becomes subtype + // `t` is a leaftype, so intersection test becomes subtype (after excluding kinds) if (ty == (jl_value_t*)jl_any_type || // easy case: Any always matches (tparam & 1 - ? (jl_typeof(t) == ty || jl_isa(t, ty)) // (Type{t} <: ty), where is_leaf_type(t) => isa(t, ty) + ? jl_isa(t, ty) // (Type{t} <: ty), where is_leaf_type(t) => isa(t, ty) : (t == ty || jl_subtype(t, ty)))) { jl_typemap_t *ml = jl_atomic_load_relaxed(&data[i + 1]); JL_GC_PROMISE_ROOTED(ml); @@ -508,6 +534,7 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, ty = ((jl_tvar_t*)ty)->ub; jl_value_t *typetype = jl_unwrap_unionall(ty); typetype = jl_is_type_type(typetype) ? jl_tparam0(typetype) : NULL; + jl_value_t *name = typetype ? jl_type_extract_name(typetype) : NULL; // approxify the tparam until we have a valid type if (jl_has_free_typevars(ty)) { ty = jl_unwrap_unionall(ty); @@ -517,42 +544,81 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, ty = (jl_value_t*)jl_any_type; } jl_array_t *targ = jl_atomic_load_relaxed(&cache->targ); - if (targ != (jl_array_t*)jl_an_empty_vec_any) { - if (typetype && !jl_has_free_typevars(typetype)) { - if (is_cache_leaf(typetype, 1)) { - // direct lookup of leaf types - jl_typemap_t *ml = mtcache_hash_lookup(targ, typetype); - if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; + if (targ != (jl_array_t*)jl_an_empty_vec_any + && !(typetype && !jl_has_free_typevars(typetype) && !is_cache_leaf(typetype, 1))) { // cannot contain this, so don't bother with checking + if (name && !jl_is_typevar(typetype)) { + // semi-direct lookup of types via their names + if (jl_type_extract_name_precise(typetype, 1)) { + // consider the type name first + jl_value_t *ml = mtcache_hash_lookup(targ, (jl_value_t*)name); + if (jl_is_array(ml)) { + if (typetype && !jl_has_free_typevars(typetype)) { + // direct lookup of leaf types + if (is_cache_leaf(typetype, 1)) { + ml = mtcache_hash_lookup((jl_array_t*)ml, typetype); + if (ml != jl_nothing) { + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; + } + } + } + else { + if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, (jl_value_t*)ty, 1, offs, closure)) return 0; + } + } + else if (ml != jl_nothing) { + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; } } + else { + // consider all of the possible subtypes + // TODO: the possibility of encountering `Type{Union{}}` in this intersection may + // be forcing us to do some extra work here whenever we see a typevar, even though + // the likelihood of that value actually occurring is frequently likely to be + // zero (or result in an ambiguous match) + if (!jl_typemap_intersection_array_visitor((jl_array_t*)targ, (jl_value_t*)ty, 3, offs, closure)) return 0; + } } else { // else an array scan is required to check subtypes // first, fast-path: optimized pre-intersection test to see if `ty` could intersect with any Type if (typetype || !jl_has_empty_intersection((jl_value_t*)jl_type_type, ty)) { targ = jl_atomic_load_relaxed(&cache->targ); // may be GC'd during type-intersection - if (!jl_typemap_intersection_array_visitor(targ, ty, 1, offs, closure)) return 0; + if (!jl_typemap_intersection_array_visitor(targ, ty, 3, offs, closure)) return 0; } } } jl_array_t *cachearg1 = jl_atomic_load_relaxed(&cache->arg1); if (cachearg1 != (jl_array_t*)jl_an_empty_vec_any) { if (is_cache_leaf(ty, 0)) { + jl_typename_t *name = ty == jl_bottom_type ? jl_typeofbottom_type->name : ((jl_datatype_t*)ty)->name; // direct lookup of leaf types - jl_typemap_t *ml = mtcache_hash_lookup(cachearg1, ty); + jl_value_t *ml = mtcache_hash_lookup(cachearg1, (jl_value_t*)name); + if (jl_is_array(ml)) + ml = mtcache_hash_lookup((jl_array_t*)ml, ty); if (ml != jl_nothing) { if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; } } else { - // else an array scan is required to check subtypes - if (!jl_typemap_intersection_array_visitor(cachearg1, ty, 0, offs, closure)) return 0; + jl_value_t *name = jl_type_extract_name(ty); + if (name && jl_type_extract_name_precise(ty, 0)) { + // direct lookup of leaf types + jl_value_t *ml = mtcache_hash_lookup(cachearg1, name); + if (jl_is_array(ml)) { + if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, (jl_value_t*)ty, 0, offs, closure)) return 0; + } + else { + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; + } + } + else { + // else an array scan is required to check subtypes + if (!jl_typemap_intersection_array_visitor(cachearg1, ty, 2, offs, closure)) return 0; + } } } jl_array_t *tname = jl_atomic_load_relaxed(&cache->tname); if (tname != (jl_array_t*)jl_an_empty_vec_any) { - jl_value_t *name = typetype ? jl_type_extract_name(typetype) : NULL; if (name && !jl_is_typevar(typetype)) { // semi-direct lookup of types // TODO: the possibility of encountering `Type{Union{}}` in this intersection may @@ -799,9 +865,12 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type( if (is_cache_leaf(a0, 1)) { jl_array_t *targ = jl_atomic_load_relaxed(&cache->targ); if (targ != (jl_array_t*)jl_an_empty_vec_any) { - jl_typemap_t *ml = mtcache_hash_lookup(targ, a0); + jl_typename_t *name = a0 == jl_bottom_type ? jl_typeofbottom_type->name : ((jl_datatype_t*)a0)->name; + jl_value_t *ml = mtcache_hash_lookup(targ, (jl_value_t*)name); + if (jl_is_array(ml)) + ml = mtcache_hash_lookup((jl_array_t*)ml, a0); if (ml != jl_nothing) { - jl_typemap_entry_t *li = jl_typemap_assoc_by_type(ml, search, offs + 1, subtype); + jl_typemap_entry_t *li = jl_typemap_assoc_by_type((jl_typemap_t*)ml, search, offs + 1, subtype); if (li) return li; } } @@ -811,9 +880,12 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type( if (is_cache_leaf(ty, 0)) { jl_array_t *cachearg1 = jl_atomic_load_relaxed(&cache->arg1); if (cachearg1 != (jl_array_t*)jl_an_empty_vec_any) { - jl_typemap_t *ml = mtcache_hash_lookup(cachearg1, ty); + jl_typename_t *name = ty == jl_bottom_type ? jl_typeofbottom_type->name : ((jl_datatype_t*)ty)->name; + jl_value_t *ml = mtcache_hash_lookup(cachearg1, (jl_value_t*)name); + if (jl_is_array(ml)) + ml = mtcache_hash_lookup((jl_array_t*)ml, ty); if (ml != jl_nothing) { - jl_typemap_entry_t *li = jl_typemap_assoc_by_type(ml, search, offs + 1, subtype); + jl_typemap_entry_t *li = jl_typemap_assoc_by_type((jl_typemap_t*)ml, search, offs + 1, subtype); if (li) return li; } } @@ -1001,15 +1073,21 @@ jl_typemap_entry_t *jl_typemap_level_assoc_exact(jl_typemap_level_t *cache, jl_v jl_value_t *ty = jl_typeof(a1); assert(jl_is_datatype(ty)); jl_array_t *targ = jl_atomic_load_relaxed(&cache->targ); - if (ty == (jl_value_t*)jl_datatype_type && targ != (jl_array_t*)jl_an_empty_vec_any && is_cache_leaf(a1, 1)) { - jl_typemap_t *ml_or_cache = mtcache_hash_lookup(targ, a1); + if (targ != (jl_array_t*)jl_an_empty_vec_any && is_cache_leaf(a1, 1)) { + jl_typename_t *name = a1 == jl_bottom_type ? jl_typeofbottom_type->name : ((jl_datatype_t*)a1)->name; + jl_value_t *ml_or_cache = mtcache_hash_lookup(targ, (jl_value_t*)name); + if (jl_is_array(ml_or_cache)) + ml_or_cache = mtcache_hash_lookup((jl_array_t*)ml_or_cache, a1); jl_typemap_entry_t *ml = jl_typemap_assoc_exact(ml_or_cache, arg1, args, n, offs+1, world); if (ml) return ml; } jl_array_t *cachearg1 = jl_atomic_load_relaxed(&cache->arg1); if (cachearg1 != (jl_array_t*)jl_an_empty_vec_any && is_cache_leaf(ty, 0)) { - jl_typemap_t *ml_or_cache = mtcache_hash_lookup(cachearg1, ty); - jl_typemap_entry_t *ml = jl_typemap_assoc_exact(ml_or_cache, arg1, args, n, offs+1, world); + jl_typename_t *name = ty == jl_bottom_type ? jl_typeofbottom_type->name : ((jl_datatype_t*)ty)->name; + jl_value_t *ml_or_cache = mtcache_hash_lookup(cachearg1, (jl_value_t*)name); + if (jl_is_array(ml_or_cache)) + ml_or_cache = mtcache_hash_lookup((jl_array_t*)ml_or_cache, ty); + jl_typemap_entry_t *ml = jl_typemap_assoc_exact((jl_typemap_t*)ml_or_cache, arg1, args, n, offs+1, world); if (ml) return ml; } jl_array_t *tname = jl_atomic_load_relaxed(&cache->tname); @@ -1102,10 +1180,14 @@ static jl_typemap_level_t *jl_new_typemap_level(void) return cache; } -static jl_typemap_level_t *jl_method_convert_list_to_cache( - jl_typemap_t *map, jl_typemap_entry_t *ml, int8_t offs) +static void jl_typemap_array_insert_( + jl_typemap_t *map, _Atomic(jl_array_t*) *pcache, jl_value_t *key, jl_typemap_entry_t *newrec, + jl_value_t *parent, int8_t tparam, int8_t offs, jl_value_t *doublesplit); + +static jl_value_t *jl_method_convert_list_to_cache( + jl_typemap_t *map, jl_typemap_entry_t *ml, int8_t tparam, int8_t offs, int8_t doublesplit) { - jl_typemap_level_t *cache = jl_new_typemap_level(); + jl_value_t *cache = doublesplit ? jl_an_empty_vec_any : (jl_value_t*)jl_new_typemap_level(); jl_typemap_entry_t *next = NULL; JL_GC_PUSH3(&cache, &next, &ml); while (ml != (void*)jl_nothing) { @@ -1113,7 +1195,25 @@ static jl_typemap_level_t *jl_method_convert_list_to_cache( jl_atomic_store_relaxed(&ml->next, (jl_typemap_entry_t*)jl_nothing); // n.b. this is being done concurrently with lookups! // TODO: is it safe to be doing this concurrently with lookups? - jl_typemap_level_insert_(map, cache, ml, offs); + if (doublesplit) { + jl_value_t *key = jl_unwrap_unionall((jl_value_t*)ml->sig); + size_t len = jl_nparams(key); + if (offs < len-1) + key = jl_tparam(key, offs); + else + key = jl_tparam(key, len-1); + if (jl_is_vararg(key)) + key = jl_unwrap_vararg(key); + if (key == (jl_value_t*)jl_typeofbottom_type) + key = (jl_value_t*)jl_assume(jl_typeofbottom_type)->super; + if (tparam) { + assert(jl_is_type_type(key)); + key = jl_tparam0(key); + } + jl_typemap_array_insert_(map, (_Atomic(jl_array_t*)*)&cache, key, ml, NULL, 0, offs, NULL); + } + else + jl_typemap_level_insert_(map, (jl_typemap_level_t*)cache, ml, offs); ml = next; } JL_GC_POP(); @@ -1139,23 +1239,33 @@ static void jl_typemap_list_insert_( jl_gc_wb(parent, newrec); } +// n.b. tparam value only needed if doublesplit is set (for jl_method_convert_list_to_cache) static void jl_typemap_insert_generic( - jl_typemap_t *map, _Atomic(jl_typemap_t*) *pml, jl_value_t *parent, - jl_typemap_entry_t *newrec, int8_t offs) + jl_typemap_t *map, _Atomic(jl_value_t*) *pml, jl_value_t *parent, + jl_typemap_entry_t *newrec, int8_t tparam, int8_t offs, jl_value_t *doublesplit) { - jl_typemap_t *ml = jl_atomic_load_relaxed(pml); + jl_value_t *ml = jl_atomic_load_relaxed(pml); + if (jl_is_array(ml)) { + assert(doublesplit); + jl_typemap_array_insert_(map, (_Atomic(jl_array_t*)*)pml, doublesplit, newrec, parent, 0, offs, NULL); + return; + } if (jl_typeof(ml) == (jl_value_t*)jl_typemap_level_type) { + assert(!doublesplit); jl_typemap_level_insert_(map, (jl_typemap_level_t*)ml, newrec, offs); return; } unsigned count = jl_typemap_list_count_locked((jl_typemap_entry_t*)ml); if (count > MAX_METHLIST_COUNT) { - ml = (jl_typemap_t*)jl_method_convert_list_to_cache( - map, (jl_typemap_entry_t*)ml, offs); + ml = jl_method_convert_list_to_cache( + map, (jl_typemap_entry_t*)ml, tparam, offs, doublesplit != NULL); jl_atomic_store_release(pml, ml); jl_gc_wb(parent, ml); - jl_typemap_level_insert_(map, (jl_typemap_level_t*)ml, newrec, offs); + if (doublesplit) + jl_typemap_array_insert_(map, (_Atomic(jl_array_t*)*)pml, doublesplit, newrec, parent, 0, offs, NULL); + else + jl_typemap_level_insert_(map, (jl_typemap_level_t*)ml, newrec, offs); return; } @@ -1165,14 +1275,14 @@ static void jl_typemap_insert_generic( static void jl_typemap_array_insert_( jl_typemap_t *map, _Atomic(jl_array_t*) *pcache, jl_value_t *key, jl_typemap_entry_t *newrec, - jl_value_t *parent, int8_t offs) + jl_value_t *parent, int8_t tparam, int8_t offs, jl_value_t *doublesplit) { jl_array_t *cache = jl_atomic_load_relaxed(pcache); - _Atomic(jl_typemap_t*) *pml = mtcache_hash_lookup_bp(cache, key); - if (pml != NULL) - jl_typemap_insert_generic(map, pml, (jl_value_t*)cache, newrec, offs+1); - else + _Atomic(jl_value_t*) *pml = mtcache_hash_lookup_bp(cache, key); + if (pml == NULL) mtcache_hash_insert(pcache, parent, key, (jl_typemap_t*)newrec); + else + jl_typemap_insert_generic(map, pml, (jl_value_t*)cache, newrec, tparam, offs + (doublesplit ? 0 : 1), doublesplit); } static void jl_typemap_level_insert_( @@ -1203,7 +1313,7 @@ static void jl_typemap_level_insert_( t1 = (jl_value_t*)jl_assume(jl_typeofbottom_type)->super; // If the type at `offs` is Any, put it in the Any list if (t1 && jl_is_any(t1)) { - jl_typemap_insert_generic(map, &cache->any, (jl_value_t*)cache, newrec, offs+1); + jl_typemap_insert_generic(map, &cache->any, (jl_value_t*)cache, newrec, 0, offs+1, NULL); return; } // Don't put Varargs in the optimized caches (too hard to handle in lookup and bp) @@ -1214,12 +1324,14 @@ static void jl_typemap_level_insert_( // and we use the table indexed for that purpose. jl_value_t *a0 = jl_tparam0(t1); if (is_cache_leaf(a0, 1)) { - jl_typemap_array_insert_(map, &cache->targ, a0, newrec, (jl_value_t*)cache, offs); + jl_typename_t *name = a0 == jl_bottom_type ? jl_typeofbottom_type->name : ((jl_datatype_t*)a0)->name; + jl_typemap_array_insert_(map, &cache->targ, (jl_value_t*)name, newrec, (jl_value_t*)cache, 1, offs, jl_is_datatype(name->wrapper) ? NULL : a0); return; } } if (is_cache_leaf(t1, 0)) { - jl_typemap_array_insert_(map, &cache->arg1, t1, newrec, (jl_value_t*)cache, offs); + jl_typename_t *name = t1 == jl_bottom_type ? jl_typeofbottom_type->name : ((jl_datatype_t*)t1)->name; + jl_typemap_array_insert_(map, &cache->arg1, (jl_value_t*)name, newrec, (jl_value_t*)cache, 0, offs, jl_is_datatype(name->wrapper) ? NULL : t1); return; } @@ -1229,12 +1341,12 @@ static void jl_typemap_level_insert_( if (jl_is_type_type(t1)) { a0 = jl_type_extract_name(jl_tparam0(t1)); jl_datatype_t *super = a0 ? (jl_datatype_t*)jl_unwrap_unionall(((jl_typename_t*)a0)->wrapper) : jl_any_type; - jl_typemap_array_insert_(map, &cache->tname, (jl_value_t*)super->name, newrec, (jl_value_t*)cache, offs); + jl_typemap_array_insert_(map, &cache->tname, (jl_value_t*)super->name, newrec, (jl_value_t*)cache, 1, offs, NULL); return; } a0 = jl_type_extract_name(t1); if (a0 && a0 != (jl_value_t*)jl_any_type->name) { - jl_typemap_array_insert_(map, &cache->name1, a0, newrec, (jl_value_t*)cache, offs); + jl_typemap_array_insert_(map, &cache->name1, a0, newrec, (jl_value_t*)cache, 0, offs, NULL); return; } } @@ -1290,7 +1402,7 @@ void jl_typemap_insert(_Atomic(jl_typemap_t *) *pcache, jl_value_t *parent, jl_typemap_entry_t *newrec, int8_t offs) { jl_typemap_t *cache = jl_atomic_load_relaxed(pcache); - jl_typemap_insert_generic(cache, pcache, parent, newrec, offs); + jl_typemap_insert_generic(cache, pcache, parent, newrec, 0, offs, NULL); } #ifdef __cplusplus From a2912e2148b23601316b2b3135e223d9b3dd1c16 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 8 Mar 2023 11:35:08 +0900 Subject: [PATCH 281/775] tidy up compiler implementation (#48930) - remove `update_valid_age!(edge::InferenceState, sv::InferenceState)` and replace all the usages with `update_valid_age!(sv, edge.valid_worlds)`: this will simplify the incoming `AbsIntState` interface (see #48913) - remove `Effects(sv::InferenceState)` utility: replace all the usages with `sv.ipo_effects`, which is more explictly saying that we are looking at IPO-valid effects - normalize more `li::MethodInstance` to `mi::MethodInstance` - import `Core.MethodTable` - fix up `setindex!` return values --- base/compiler/abstractinterpretation.jl | 14 ++++++------- base/compiler/cicache.jl | 12 ++++++----- base/compiler/compiler.jl | 2 +- base/compiler/inferencestate.jl | 24 +++++++++------------ base/compiler/methodtable.jl | 6 +++--- base/compiler/ssair/inlining.jl | 4 ++-- base/compiler/ssair/ir.jl | 4 ++-- base/compiler/tfuncs.jl | 2 +- base/compiler/typeinfer.jl | 28 ++++++++++++------------- base/compiler/utilities.jl | 14 ++++++------- 10 files changed, 54 insertions(+), 56 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 439942816fd47..de8d26720fd5a 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -44,7 +44,7 @@ function should_infer_this_call(interp::AbstractInterpreter, sv::InferenceState) end function should_infer_for_effects(sv::InferenceState) - effects = Effects(sv) + effects = sv.ipo_effects return is_terminates(effects) && is_effect_free(effects) end @@ -255,7 +255,7 @@ struct MethodMatches applicable::Vector{Any} info::MethodMatchInfo valid_worlds::WorldRange - mt::Core.MethodTable + mt::MethodTable fullmatch::Bool nonoverlayed::Bool end @@ -267,7 +267,7 @@ struct UnionSplitMethodMatches applicable_argtypes::Vector{Vector{Any}} info::UnionSplitInfo valid_worlds::WorldRange - mts::Vector{Core.MethodTable} + mts::Vector{MethodTable} fullmatches::Vector{Bool} nonoverlayed::Bool end @@ -282,7 +282,7 @@ function find_matching_methods(argtypes::Vector{Any}, @nospecialize(atype), meth applicable = Any[] applicable_argtypes = Vector{Any}[] # arrays like `argtypes`, including constants, for each match valid_worlds = WorldRange() - mts = Core.MethodTable[] + mts = MethodTable[] fullmatches = Bool[] nonoverlayed = true for i in 1:length(split_argtypes) @@ -290,7 +290,7 @@ function find_matching_methods(argtypes::Vector{Any}, @nospecialize(atype), meth sig_n = argtypes_to_type(arg_n) mt = ccall(:jl_method_table_for, Any, (Any,), sig_n) mt === nothing && return FailedMethodMatch("Could not identify method table for call") - mt = mt::Core.MethodTable + mt = mt::MethodTable result = findall(sig_n, method_table; limit = max_methods) if result === nothing return FailedMethodMatch("For one of the union split cases, too many methods matched") @@ -329,7 +329,7 @@ function find_matching_methods(argtypes::Vector{Any}, @nospecialize(atype), meth if mt === nothing return FailedMethodMatch("Could not identify method table for call") end - mt = mt::Core.MethodTable + mt = mt::MethodTable result = findall(atype, method_table; limit = max_methods) if result === nothing # this means too many methods matched @@ -3081,7 +3081,7 @@ function typeinf_nocycle(interp::AbstractInterpreter, frame::InferenceState) typeinf_local(interp, caller) no_active_ips_in_callers = false end - caller.valid_worlds = intersect(caller.valid_worlds, frame.valid_worlds) + update_valid_age!(caller, frame.valid_worlds) end end return true diff --git a/base/compiler/cicache.jl b/base/compiler/cicache.jl index 294b1f0055f79..8332777e6d5bc 100644 --- a/base/compiler/cicache.jl +++ b/base/compiler/cicache.jl @@ -7,11 +7,11 @@ Internally, each `MethodInstance` keep a unique global cache of code instances that have been created for the given method instance, stratified by world age ranges. This struct abstracts over access to this cache. """ -struct InternalCodeCache -end +struct InternalCodeCache end function setindex!(cache::InternalCodeCache, ci::CodeInstance, mi::MethodInstance) ccall(:jl_mi_cache_insert, Cvoid, (Any, Any), mi, ci) + return cache end const GLOBAL_CI_CACHE = InternalCodeCache() @@ -49,11 +49,11 @@ WorldView(wvc::WorldView, wr::WorldRange) = WorldView(wvc.cache, wr) WorldView(wvc::WorldView, args...) = WorldView(wvc.cache, args...) function haskey(wvc::WorldView{InternalCodeCache}, mi::MethodInstance) - ccall(:jl_rettype_inferred, Any, (Any, UInt, UInt), mi, first(wvc.worlds), last(wvc.worlds))::Union{Nothing, CodeInstance} !== nothing + return ccall(:jl_rettype_inferred, Any, (Any, UInt, UInt), mi, first(wvc.worlds), last(wvc.worlds)) !== nothing end function get(wvc::WorldView{InternalCodeCache}, mi::MethodInstance, default) - r = ccall(:jl_rettype_inferred, Any, (Any, UInt, UInt), mi, first(wvc.worlds), last(wvc.worlds))::Union{Nothing, CodeInstance} + r = ccall(:jl_rettype_inferred, Any, (Any, UInt, UInt), mi, first(wvc.worlds), last(wvc.worlds)) if r === nothing return default end @@ -66,5 +66,7 @@ function getindex(wvc::WorldView{InternalCodeCache}, mi::MethodInstance) return r::CodeInstance end -setindex!(wvc::WorldView{InternalCodeCache}, ci::CodeInstance, mi::MethodInstance) = +function setindex!(wvc::WorldView{InternalCodeCache}, ci::CodeInstance, mi::MethodInstance) setindex!(wvc.cache, ci, mi) + return wvc +end diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 7213b3615e8e1..9229f54f143f2 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -6,7 +6,7 @@ using Core.Intrinsics, Core.IR import Core: print, println, show, write, unsafe_write, stdout, stderr, _apply_iterate, svec, apply_type, Builtin, IntrinsicFunction, - MethodInstance, CodeInstance, MethodMatch, PartialOpaque, + MethodInstance, CodeInstance, MethodTable, MethodMatch, PartialOpaque, TypeofVararg const getproperty = Core.getfield diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index cacc358327e7a..74db247e472b6 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -195,8 +195,6 @@ end is_inferred(sv::InferenceState) = is_inferred(sv.result) is_inferred(result::InferenceResult) = result.result !== nothing -Effects(state::InferenceState) = state.ipo_effects - function merge_effects!(::AbstractInterpreter, caller::InferenceState, effects::Effects) caller.ipo_effects = merge_effects(caller.ipo_effects, effects) end @@ -505,14 +503,12 @@ end _topmod(sv::InferenceState) = _topmod(sv.mod) # work towards converging the valid age range for sv -function update_valid_age!(sv::InferenceState, worlds::WorldRange) - sv.valid_worlds = intersect(worlds, sv.valid_worlds) - @assert(sv.world in sv.valid_worlds, "invalid age range update") - nothing +function update_valid_age!(sv::InferenceState, valid_worlds::WorldRange) + valid_worlds = sv.valid_worlds = intersect(valid_worlds, sv.valid_worlds) + @assert(sv.world in valid_worlds, "invalid age range update") + return valid_worlds end -update_valid_age!(edge::InferenceState, sv::InferenceState) = update_valid_age!(sv, edge.valid_worlds) - function record_ssa_assign!(𝕃ᵢ::AbstractLattice, ssa_id::Int, @nospecialize(new), frame::InferenceState) ssavaluetypes = frame.ssavaluetypes old = ssavaluetypes[ssa_id] @@ -538,7 +534,7 @@ function record_ssa_assign!(𝕃ᵢ::AbstractLattice, ssa_id::Int, @nospecialize end function add_cycle_backedge!(caller::InferenceState, frame::InferenceState, currpc::Int) - update_valid_age!(frame, caller) + update_valid_age!(caller, frame.valid_worlds) backedge = (caller, currpc) contains_is(frame.cycle_backedges, backedge) || push!(frame.cycle_backedges, backedge) add_backedge!(caller, frame.linfo) @@ -546,24 +542,24 @@ function add_cycle_backedge!(caller::InferenceState, frame::InferenceState, curr end # temporarily accumulate our edges to later add as backedges in the callee -function add_backedge!(caller::InferenceState, li::MethodInstance) +function add_backedge!(caller::InferenceState, mi::MethodInstance) edges = get_stmt_edges!(caller) if edges !== nothing - push!(edges, li) + push!(edges, mi) end return nothing end -function add_invoke_backedge!(caller::InferenceState, @nospecialize(invokesig::Type), li::MethodInstance) +function add_invoke_backedge!(caller::InferenceState, @nospecialize(invokesig::Type), mi::MethodInstance) edges = get_stmt_edges!(caller) if edges !== nothing - push!(edges, invokesig, li) + push!(edges, invokesig, mi) end return nothing end # used to temporarily accumulate our no method errors to later add as backedges in the callee method table -function add_mt_backedge!(caller::InferenceState, mt::Core.MethodTable, @nospecialize(typ)) +function add_mt_backedge!(caller::InferenceState, mt::MethodTable, @nospecialize(typ)) edges = get_stmt_edges!(caller) if edges !== nothing push!(edges, mt, typ) diff --git a/base/compiler/methodtable.jl b/base/compiler/methodtable.jl index 7f344aeb0e6de..8c79b2d8a8468 100644 --- a/base/compiler/methodtable.jl +++ b/base/compiler/methodtable.jl @@ -39,7 +39,7 @@ external table, e.g., to override existing method. """ struct OverlayMethodTable <: MethodTableView world::UInt - mt::Core.MethodTable + mt::MethodTable end struct MethodMatchKey @@ -98,7 +98,7 @@ function findall(@nospecialize(sig::Type), table::OverlayMethodTable; limit::Int !isempty(result)) end -function _findall(@nospecialize(sig::Type), mt::Union{Nothing,Core.MethodTable}, world::UInt, limit::Int) +function _findall(@nospecialize(sig::Type), mt::Union{Nothing,MethodTable}, world::UInt, limit::Int) _min_val = RefValue{UInt}(typemin(UInt)) _max_val = RefValue{UInt}(typemax(UInt)) _ambig = RefValue{Int32}(0) @@ -155,7 +155,7 @@ function findsup(@nospecialize(sig::Type), table::OverlayMethodTable) false) end -function _findsup(@nospecialize(sig::Type), mt::Union{Nothing,Core.MethodTable}, world::UInt) +function _findsup(@nospecialize(sig::Type), mt::Union{Nothing,MethodTable}, world::UInt) min_valid = RefValue{UInt}(typemin(UInt)) max_valid = RefValue{UInt}(typemax(UInt)) match = ccall(:jl_gf_invoke_lookup_worlds, Any, (Any, Any, UInt, Ptr{Csize_t}, Ptr{Csize_t}), diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 486f5b2c0c625..ffd0569bc3814 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -852,7 +852,8 @@ function resolve_todo(mi::MethodInstance, result::Union{MethodMatch,InferenceRes #XXX: update_valid_age!(min_valid[1], max_valid[1], sv) if isa(result, InferenceResult) src = result.src - if is_foldable_nothrow(result.ipo_effects) + effects = result.ipo_effects + if is_foldable_nothrow(effects) res = result.result if isa(res, Const) && is_inlineable_constant(res.val) # use constant calling convention @@ -860,7 +861,6 @@ function resolve_todo(mi::MethodInstance, result::Union{MethodMatch,InferenceRes return ConstantCase(quoted(res.val)) end end - effects = result.ipo_effects else cached_result = get_cached_result(state, mi) if cached_result isa ConstantCase diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index a013745b8c773..56fac0e3cc1a7 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -936,7 +936,7 @@ end function setindex!(compact::IncrementalCompact, @nospecialize(v), idx::SSAValue) @assert idx.id < compact.result_idx - (compact.result[idx.id][:inst] === v) && return + (compact.result[idx.id][:inst] === v) && return compact # Kill count for current uses kill_current_uses!(compact, compact.result[idx.id][:inst]) compact.result[idx.id][:inst] = v @@ -949,7 +949,7 @@ function setindex!(compact::IncrementalCompact, @nospecialize(v), idx::OldSSAVal id = idx.id if id < compact.idx new_idx = compact.ssa_rename[id] - (compact.result[new_idx][:inst] === v) && return + (compact.result[new_idx][:inst] === v) && return compact kill_current_uses!(compact, compact.result[new_idx][:inst]) compact.result[new_idx][:inst] = v count_added_node!(compact, v) && push!(compact.late_fixup, new_idx) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index e01071d9073fa..41da17c19d6d2 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -2605,7 +2605,7 @@ function _hasmethod_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, sv types = rewrap_unionall(Tuple{ft, unwrapped.parameters...}, types)::Type end mt = ccall(:jl_method_table_for, Any, (Any,), types) - if !isa(mt, Core.MethodTable) + if !isa(mt, MethodTable) return CallMeta(Bool, EFFECTS_THROWS, NoCallInfo()) end match, valid_worlds, overlayed = findsup(types, method_table(interp)) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 0395afa6629ac..70c2d27b5528b 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -431,7 +431,7 @@ function cycle_fix_limited(@nospecialize(typ), sv::InferenceState) end function adjust_effects(sv::InferenceState) - ipo_effects = Effects(sv) + ipo_effects = sv.ipo_effects # refine :consistent-cy effect using the return type information # TODO this adjustment tries to compromise imprecise :consistent-cy information, @@ -577,7 +577,7 @@ function store_backedges(frame::MethodInstance, edges::Vector{Any}) if isa(caller, MethodInstance) ccall(:jl_method_instance_add_backedge, Cvoid, (Any, Any, Any), caller, sig, frame) else - typeassert(caller, Core.MethodTable) + typeassert(caller, MethodTable) ccall(:jl_method_table_add_backedge, Cvoid, (Any, Any, Any), caller, sig, frame) end end @@ -792,8 +792,8 @@ function merge_call_chain!(interp::AbstractInterpreter, parent::InferenceState, end end -function is_same_frame(interp::AbstractInterpreter, linfo::MethodInstance, frame::InferenceState) - return linfo === frame.linfo +function is_same_frame(interp::AbstractInterpreter, mi::MethodInstance, frame::InferenceState) + return mi === frame.linfo end function poison_callstack(infstate::InferenceState, topmost::InferenceState) @@ -801,19 +801,19 @@ function poison_callstack(infstate::InferenceState, topmost::InferenceState) nothing end -# Walk through `linfo`'s upstream call chain, starting at `parent`. If a parent -# frame matching `linfo` is encountered, then there is a cycle in the call graph -# (i.e. `linfo` is a descendant callee of itself). Upon encountering this cycle, +# Walk through `mi`'s upstream call chain, starting at `parent`. If a parent +# frame matching `mi` is encountered, then there is a cycle in the call graph +# (i.e. `mi` is a descendant callee of itself). Upon encountering this cycle, # we "resolve" it by merging the call chain, which entails unioning each intermediary # frame's `callers_in_cycle` field and adding the appropriate backedges. Finally, -# we return `linfo`'s pre-existing frame. If no cycles are found, `nothing` is +# we return `mi`'s pre-existing frame. If no cycles are found, `nothing` is # returned instead. -function resolve_call_cycle!(interp::AbstractInterpreter, linfo::MethodInstance, parent::InferenceState) +function resolve_call_cycle!(interp::AbstractInterpreter, mi::MethodInstance, parent::InferenceState) frame = parent uncached = false while isa(frame, InferenceState) uncached |= !frame.cached # ensure we never add an uncached frame to a cycle - if is_same_frame(interp, linfo, frame) + if is_same_frame(interp, mi, frame) if uncached # our attempt to speculate into a constant call lead to an undesired self-cycle # that cannot be converged: poison our call-stack (up to the discovered duplicate frame) @@ -825,7 +825,7 @@ function resolve_call_cycle!(interp::AbstractInterpreter, linfo::MethodInstance, return frame end for caller in frame.callers_in_cycle - if is_same_frame(interp, linfo, caller) + if is_same_frame(interp, mi, caller) if uncached poison_callstack(parent, frame) return true @@ -916,16 +916,16 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize frame.parent = caller end typeinf(interp, frame) - update_valid_age!(frame, caller) + update_valid_age!(caller, frame.valid_worlds) edge = is_inferred(frame) ? mi : nothing - return EdgeCallResult(frame.bestguess, edge, Effects(frame)) # effects are adjusted already within `finish` + return EdgeCallResult(frame.bestguess, edge, frame.ipo_effects) # effects are adjusted already within `finish` elseif frame === true # unresolvable cycle return EdgeCallResult(Any, nothing, Effects()) end # return the current knowledge about this cycle frame = frame::InferenceState - update_valid_age!(frame, caller) + update_valid_age!(caller, frame.valid_worlds) return EdgeCallResult(frame.bestguess, nothing, adjust_effects(frame)) end diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 6cf600560902d..92458c443479b 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -271,8 +271,8 @@ Return an iterator over a list of backedges. Iteration returns `(sig, caller)` e which will be one of the following: - `BackedgePair(nothing, caller::MethodInstance)`: a call made by ordinary inferable dispatch -- `BackedgePair(invokesig, caller::MethodInstance)`: a call made by `invoke(f, invokesig, args...)` -- `BackedgePair(specsig, mt::MethodTable)`: an abstract call +- `BackedgePair(invokesig::Type, caller::MethodInstance)`: a call made by `invoke(f, invokesig, args...)` +- `BackedgePair(specsig::Type, mt::MethodTable)`: an abstract call # Examples @@ -305,17 +305,17 @@ const empty_backedge_iter = BackedgeIterator(Any[]) struct BackedgePair sig # ::Union{Nothing,Type} - caller::Union{MethodInstance,Core.MethodTable} - BackedgePair(@nospecialize(sig), caller::Union{MethodInstance,Core.MethodTable}) = new(sig, caller) + caller::Union{MethodInstance,MethodTable} + BackedgePair(@nospecialize(sig), caller::Union{MethodInstance,MethodTable}) = new(sig, caller) end function iterate(iter::BackedgeIterator, i::Int=1) backedges = iter.backedges i > length(backedges) && return nothing item = backedges[i] - isa(item, MethodInstance) && return BackedgePair(nothing, item), i+1 # regular dispatch - isa(item, Core.MethodTable) && return BackedgePair(backedges[i+1], item), i+2 # abstract dispatch - return BackedgePair(item, backedges[i+1]::MethodInstance), i+2 # `invoke` calls + isa(item, MethodInstance) && return BackedgePair(nothing, item), i+1 # regular dispatch + isa(item, MethodTable) && return BackedgePair(backedges[i+1], item), i+2 # abstract dispatch + return BackedgePair(item, backedges[i+1]::MethodInstance), i+2 # `invoke` calls end ######### From e08689c57751f4288e6a148e324b639743ac6bb6 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 8 Mar 2023 04:00:41 +0000 Subject: [PATCH 282/775] Add an inference option to ignore the recursion hardlimit We currently very aggressively limit recursion if we came from a union split. The reason for this choice is to avoid accidental exponential inference times from excessive exploration of the call graph. Unfortunately, certain packages like Diffractor really like triggering the recursion heuristic at the moment, causing any unions to immediately cause imprecise inference. In the fullness of time, we should improve the recursion heuristic to do better in this case, but for the time being, add an inference option that simply lets it ignore the hardlimit recursion case and proceed anyway. --- base/compiler/abstractinterpretation.jl | 2 +- base/compiler/types.jl | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 93ead3207fce2..13ee9abc14387 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -688,7 +688,7 @@ function edge_matches_sv(frame::InferenceState, method::Method, @nospecialize(si if callee_method2 !== inf_method2 return false end - if !hardlimit + if !hardlimit || InferenceParams(sv.interp).ignore_recursion_hardlimit # if this is a soft limit, # also inspect the parent of this edge, # to see if they are the same Method as sv diff --git a/base/compiler/types.jl b/base/compiler/types.jl index cac15e9a69513..b59159301d620 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -141,6 +141,7 @@ struct InferenceParams aggressive_constant_propagation::Bool unoptimize_throw_blocks::Bool assume_bindings_static::Bool + ignore_recursion_hardlimit::Bool function InferenceParams( max_methods::Int, @@ -151,7 +152,8 @@ struct InferenceParams ipo_constant_propagation::Bool, aggressive_constant_propagation::Bool, unoptimize_throw_blocks::Bool, - assume_bindings_static::Bool) + assume_bindings_static::Bool, + ignore_recursion_hardlimit::Bool) return new( max_methods, max_union_splitting, @@ -161,7 +163,8 @@ struct InferenceParams ipo_constant_propagation, aggressive_constant_propagation, unoptimize_throw_blocks, - assume_bindings_static) + assume_bindings_static, + ignore_recursion_hardlimit) end end function InferenceParams( @@ -174,7 +177,8 @@ function InferenceParams( #=ipo_constant_propagation::Bool=# true, #=aggressive_constant_propagation::Bool=# false, #=unoptimize_throw_blocks::Bool=# true, - #=assume_bindings_static::Bool=# false); + #=assume_bindings_static::Bool=# false, + #=ignore_recursion_hardlimit::Bool=# false); max_methods::Int = params.max_methods, max_union_splitting::Int = params.max_union_splitting, max_apply_union_enum::Int = params.max_apply_union_enum, @@ -183,7 +187,8 @@ function InferenceParams( ipo_constant_propagation::Bool = params.ipo_constant_propagation, aggressive_constant_propagation::Bool = params.aggressive_constant_propagation, unoptimize_throw_blocks::Bool = params.unoptimize_throw_blocks, - assume_bindings_static::Bool = params.assume_bindings_static) + assume_bindings_static::Bool = params.assume_bindings_static, + ignore_recursion_hardlimit::Bool = params.ignore_recursion_hardlimit) return InferenceParams( max_methods, max_union_splitting, @@ -193,7 +198,8 @@ function InferenceParams( ipo_constant_propagation, aggressive_constant_propagation, unoptimize_throw_blocks, - assume_bindings_static) + assume_bindings_static, + ignore_recursion_hardlimit) end """ From b87c226305ea181d81aefe436d72c1c4337f782a Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Tue, 7 Mar 2023 23:04:47 -0500 Subject: [PATCH 283/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?SparseArrays=20stdlib=20from=20760989e=20to=204aab8df=20(#48941?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/md5 create mode 100644 deps/checksums/SparseArrays-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/sha512 delete mode 100644 deps/checksums/SparseArrays-760989ec650761541deff039efb5b767a051c172.tar.gz/md5 delete mode 100644 deps/checksums/SparseArrays-760989ec650761541deff039efb5b767a051c172.tar.gz/sha512 diff --git a/deps/checksums/SparseArrays-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/md5 b/deps/checksums/SparseArrays-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/md5 new file mode 100644 index 0000000000000..7f3de8bd0abc7 --- /dev/null +++ b/deps/checksums/SparseArrays-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/md5 @@ -0,0 +1 @@ +1276e70aa3fa0fcad3c000d4d1602d5b diff --git a/deps/checksums/SparseArrays-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/sha512 b/deps/checksums/SparseArrays-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/sha512 new file mode 100644 index 0000000000000..121570ff159ab --- /dev/null +++ b/deps/checksums/SparseArrays-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/sha512 @@ -0,0 +1 @@ +44ae6bb25f605749e4fd1b3a7a9e10c01a4506396e099f54a46cd922858642bce5a2ce7ecc650e0470a17d5d35ab66054a67df6e28c0524bd16d39e3d7c01fc2 diff --git a/deps/checksums/SparseArrays-760989ec650761541deff039efb5b767a051c172.tar.gz/md5 b/deps/checksums/SparseArrays-760989ec650761541deff039efb5b767a051c172.tar.gz/md5 deleted file mode 100644 index 5b193948d6ff0..0000000000000 --- a/deps/checksums/SparseArrays-760989ec650761541deff039efb5b767a051c172.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -aec6c28d14142264e40929f9698e00e1 diff --git a/deps/checksums/SparseArrays-760989ec650761541deff039efb5b767a051c172.tar.gz/sha512 b/deps/checksums/SparseArrays-760989ec650761541deff039efb5b767a051c172.tar.gz/sha512 deleted file mode 100644 index 007b9d7c1f71a..0000000000000 --- a/deps/checksums/SparseArrays-760989ec650761541deff039efb5b767a051c172.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -7dd85f6835fccc2448f2213873ef79bd54712b33d4f1a4a3716e9c223ffa157f3fdfedafd971e4f5a2316dc447af1a44e81038459ec7b575587faf7e4dffac28 diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version index cd571a2237103..9c9f2d4f77a88 100644 --- a/stdlib/SparseArrays.version +++ b/stdlib/SparseArrays.version @@ -1,4 +1,4 @@ SPARSEARRAYS_BRANCH = main -SPARSEARRAYS_SHA1 = 760989ec650761541deff039efb5b767a051c172 +SPARSEARRAYS_SHA1 = 4aab8df5d3f0f19f16de52e92023c2971e60f9f9 SPARSEARRAYS_GIT_URL := https://github.com/JuliaSparse/SparseArrays.jl.git SPARSEARRAYS_TAR_URL = https://api.github.com/repos/JuliaSparse/SparseArrays.jl/tarball/$1 From 99d01ea0a040c12693f91657d642498ce01a6d4e Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 8 Mar 2023 14:09:41 +0900 Subject: [PATCH 284/775] use `OptimizationParams` parameterized by `interp::AbstractInterpreter` (#48225) This should allow constant propagation of `OptimizationParams`. --- base/compiler/optimize.jl | 34 ++++++++++++------------- base/compiler/ssair/inlining.jl | 34 ++++++++++++------------- base/compiler/ssair/passes.jl | 2 +- base/compiler/typeinfer.jl | 7 +++-- test/compiler/EscapeAnalysis/EAUtils.jl | 4 +-- test/compiler/inference.jl | 8 +++--- test/compiler/inline.jl | 3 +-- 7 files changed, 43 insertions(+), 49 deletions(-) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index dc321be5108cf..0d19391fba5be 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -121,17 +121,16 @@ function inlining_policy(interp::AbstractInterpreter, end struct InliningState{Interp<:AbstractInterpreter} - params::OptimizationParams et::Union{EdgeTracker,Nothing} world::UInt interp::Interp end -function InliningState(frame::InferenceState, params::OptimizationParams, interp::AbstractInterpreter) +function InliningState(frame::InferenceState, interp::AbstractInterpreter) et = EdgeTracker(frame.stmt_edges[1]::Vector{Any}, frame.valid_worlds) - return InliningState(params, et, frame.world, interp) + return InliningState(et, frame.world, interp) end -function InliningState(params::OptimizationParams, interp::AbstractInterpreter) - return InliningState(params, nothing, get_world_counter(interp), interp) +function InliningState(interp::AbstractInterpreter) + return InliningState(nothing, get_world_counter(interp), interp) end # get `code_cache(::AbstractInterpreter)` from `state::InliningState` @@ -151,15 +150,14 @@ mutable struct OptimizationState{Interp<:AbstractInterpreter} cfg::Union{Nothing,CFG} insert_coverage::Bool end -function OptimizationState(frame::InferenceState, params::OptimizationParams, - interp::AbstractInterpreter, recompute_cfg::Bool=true) - inlining = InliningState(frame, params, interp) +function OptimizationState(frame::InferenceState, interp::AbstractInterpreter, + recompute_cfg::Bool=true) + inlining = InliningState(frame, interp) cfg = recompute_cfg ? nothing : frame.cfg return OptimizationState(frame.linfo, frame.src, nothing, frame.stmt_info, frame.mod, frame.sptypes, frame.slottypes, inlining, cfg, frame.insert_coverage) end -function OptimizationState(linfo::MethodInstance, src::CodeInfo, params::OptimizationParams, - interp::AbstractInterpreter) +function OptimizationState(linfo::MethodInstance, src::CodeInfo, interp::AbstractInterpreter) # prepare src for running optimization passes if it isn't already nssavalues = src.ssavaluetypes if nssavalues isa Int @@ -179,13 +177,13 @@ function OptimizationState(linfo::MethodInstance, src::CodeInfo, params::Optimiz mod = isa(def, Method) ? def.module : def # Allow using the global MI cache, but don't track edges. # This method is mostly used for unit testing the optimizer - inlining = InliningState(params, interp) + inlining = InliningState(interp) return OptimizationState(linfo, src, nothing, stmt_info, mod, sptypes, slottypes, inlining, nothing, false) end -function OptimizationState(linfo::MethodInstance, params::OptimizationParams, interp::AbstractInterpreter) +function OptimizationState(linfo::MethodInstance, interp::AbstractInterpreter) src = retrieve_code_info(linfo) src === nothing && return nothing - return OptimizationState(linfo, src, params, interp) + return OptimizationState(linfo, src, interp) end function ir_to_codeinf!(opt::OptimizationState) @@ -392,13 +390,13 @@ abstract_eval_ssavalue(s::SSAValue, src::Union{IRCode,IncrementalCompact}) = typ """ finish(interp::AbstractInterpreter, opt::OptimizationState, - params::OptimizationParams, ir::IRCode, caller::InferenceResult) + ir::IRCode, caller::InferenceResult) Post-process information derived by Julia-level optimizations for later use. In particular, this function determines the inlineability of the optimized code. """ function finish(interp::AbstractInterpreter, opt::OptimizationState, - params::OptimizationParams, ir::IRCode, caller::InferenceResult) + ir::IRCode, caller::InferenceResult) (; src, linfo) = opt (; def, specTypes) = linfo @@ -438,6 +436,7 @@ function finish(interp::AbstractInterpreter, opt::OptimizationState, set_inlineable!(src, true) else # compute the cost (size) of inlining this code + params = OptimizationParams(interp) cost_threshold = default = params.inline_cost_threshold if ⊑(optimizer_lattice(interp), result, Tuple) && !isconcretetype(widenconst(result)) cost_threshold += params.inline_tupleret_bonus @@ -460,10 +459,9 @@ function finish(interp::AbstractInterpreter, opt::OptimizationState, end # run the optimization work -function optimize(interp::AbstractInterpreter, opt::OptimizationState, - params::OptimizationParams, caller::InferenceResult) +function optimize(interp::AbstractInterpreter, opt::OptimizationState, caller::InferenceResult) @timeit "optimizer" ir = run_passes(opt.src, opt, caller) - return finish(interp, opt, params, ir, caller) + return finish(interp, opt, ir, caller) end using .EscapeAnalysis diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index ffd0569bc3814..79f8c1ad51b10 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -79,7 +79,7 @@ function ssa_inlining_pass!(ir::IRCode, state::InliningState, propagate_inbounds @timeit "analysis" todo = assemble_inline_todo!(ir, state) isempty(todo) && return ir # Do the actual inlining for every call we identified - @timeit "execution" ir = batch_inline!(ir, todo, propagate_inbounds, state.params) + @timeit "execution" ir = batch_inline!(ir, todo, propagate_inbounds, OptimizationParams(state.interp)) return ir end @@ -872,14 +872,14 @@ function resolve_todo(mi::MethodInstance, result::Union{MethodMatch,InferenceRes # the duplicated check might have been done already within `analyze_method!`, but still # we need it here too since we may come here directly using a constant-prop' result - if !state.params.inlining || is_stmt_noinline(flag) + if !OptimizationParams(state.interp).inlining || is_stmt_noinline(flag) return compileable_specialization(result, effects, et, info; - compilesig_invokes=state.params.compilesig_invokes) + compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) end src = inlining_policy(state.interp, src, info, flag, mi, argtypes) src === nothing && return compileable_specialization(result, effects, et, info; - compilesig_invokes=state.params.compilesig_invokes) + compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) add_inlining_backedge!(et, mi) return InliningTodo(mi, retrieve_ir_for_inlining(mi, src), effects) @@ -888,7 +888,7 @@ end # the special resolver for :invoke-d call function resolve_todo(mi::MethodInstance, argtypes::Vector{Any}, @nospecialize(info::CallInfo), flag::UInt8, state::InliningState) - if !state.params.inlining || is_stmt_noinline(flag) + if !OptimizationParams(state.interp).inlining || is_stmt_noinline(flag) return nothing end @@ -958,7 +958,7 @@ function analyze_method!(match::MethodMatch, argtypes::Vector{Any}, et = InliningEdgeTracker(state.et, invokesig) effects = info_effects(nothing, match, state) return compileable_specialization(match, effects, et, info; - compilesig_invokes=state.params.compilesig_invokes) + compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) end return resolve_todo(mi, match, argtypes, info, flag, state; invokesig) @@ -1124,7 +1124,7 @@ function inline_apply!(todo::Vector{Pair{Int,Any}}, arginfos = MaybeAbstractIterationInfo[] for i = (arg_start + 1):length(argtypes) thisarginfo = nothing - if !is_valid_type_for_apply_rewrite(argtypes[i], state.params) + if !is_valid_type_for_apply_rewrite(argtypes[i], OptimizationParams(state.interp)) isa(info, ApplyCallInfo) || return nothing thisarginfo = info.arginfo[i-arg_start] if thisarginfo === nothing || !thisarginfo.complete @@ -1173,13 +1173,13 @@ function handle_invoke_call!(todo::Vector{Pair{Int,Any}}, validate_sparams(mi.sparam_vals) || return nothing if argtypes_to_type(argtypes) <: mi.def.sig item = resolve_todo(mi, result.result, argtypes, info, flag, state; invokesig) - handle_single_case!(todo, ir, idx, stmt, item, state.params, true) + handle_single_case!(todo, ir, idx, stmt, item, OptimizationParams(state.interp), true) return nothing end end item = analyze_method!(match, argtypes, info, flag, state; allow_typevars=false, invokesig) end - handle_single_case!(todo, ir, idx, stmt, item, state.params, true) + handle_single_case!(todo, ir, idx, stmt, item, OptimizationParams(state.interp), true) return nothing end @@ -1433,7 +1433,7 @@ function handle_call!(todo::Vector{Pair{Int,Any}}, cases === nothing && return nothing cases, all_covered, joint_effects = cases handle_cases!(todo, ir, idx, stmt, argtypes_to_type(sig.argtypes), cases, - all_covered, joint_effects, state.params) + all_covered, joint_effects, OptimizationParams(state.interp)) end function handle_match!(cases::Vector{InliningCase}, @@ -1471,10 +1471,10 @@ end function semiconcrete_result_item(result::SemiConcreteResult, @nospecialize(info::CallInfo), flag::UInt8, state::InliningState) mi = result.mi - if !state.params.inlining || is_stmt_noinline(flag) + if !OptimizationParams(state.interp).inlining || is_stmt_noinline(flag) et = InliningEdgeTracker(state.et, nothing) return compileable_specialization(mi, result.effects, et, info; - compilesig_invokes=state.params.compilesig_invokes) + compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) else return InliningTodo(mi, result.ir, result.effects) end @@ -1507,7 +1507,7 @@ function concrete_result_item(result::ConcreteResult, @nospecialize(info::CallIn if !may_inline_concrete_result(result) et = InliningEdgeTracker(state.et, invokesig) case = compileable_specialization(result.mi, result.effects, et, info; - compilesig_invokes=state.params.compilesig_invokes) + compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) @assert case !== nothing "concrete evaluation should never happen for uncompileable callsite" return case end @@ -1555,7 +1555,7 @@ function handle_opaque_closure_call!(todo::Vector{Pair{Int,Any}}, item = analyze_method!(info.match, sig.argtypes, info, flag, state; allow_typevars=false) end end - handle_single_case!(todo, ir, idx, stmt, item, state.params) + handle_single_case!(todo, ir, idx, stmt, item, OptimizationParams(state.interp)) return nothing end @@ -1568,7 +1568,7 @@ function handle_modifyfield!_call!(ir::IRCode, idx::Int, stmt::Expr, info::Modif match = info.results[1]::MethodMatch match.fully_covers || return nothing case = compileable_specialization(match, Effects(), InliningEdgeTracker(state.et), info; - compilesig_invokes=state.params.compilesig_invokes) + compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) case === nothing && return nothing stmt.head = :invoke_modify pushfirst!(stmt.args, case.invoke) @@ -1696,7 +1696,7 @@ end function early_inline_special_case( ir::IRCode, stmt::Expr, @nospecialize(type), sig::Signature, state::InliningState) - state.params.inlining || return nothing + OptimizationParams(state.interp).inlining || return nothing (; f, ft, argtypes) = sig if isa(type, Const) # || isconstType(type) @@ -1749,7 +1749,7 @@ end function late_inline_special_case!( ir::IRCode, idx::Int, stmt::Expr, @nospecialize(type), sig::Signature, state::InliningState) - state.params.inlining || return nothing + OptimizationParams(state.interp).inlining || return nothing (; f, ft, argtypes) = sig if length(argtypes) == 3 && istopfunction(f, :!==) # special-case inliner for !== that precedes _methods_by_ftype union splitting diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index aa83b00d32ee5..ab6bbf6a001d4 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1203,7 +1203,7 @@ function try_resolve_finalizer!(ir::IRCode, idx::Int, finalizer_idx::Int, defuse # Check #3 dominates(domtree, finalizer_bb, bb_insert_block) || return nothing - if !inlining.params.assume_fatal_throw + if !OptimizationParams(inlining.interp).assume_fatal_throw # Collect all reachable blocks between the finalizer registration and the # insertion point blocks = finalizer_bb == bb_insert_block ? Int[finalizer_bb] : diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 70c2d27b5528b..052d691ee29c1 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -266,7 +266,7 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) for (caller, _, _) in results opt = caller.src if opt isa OptimizationState{typeof(interp)} # implies `may_optimize(interp) === true` - analyzed = optimize(interp, opt, OptimizationParams(interp), caller) + analyzed = optimize(interp, opt, caller) caller.valid_worlds = (opt.inlining.et::EdgeTracker).valid_worlds[] end end @@ -551,7 +551,7 @@ function finish(me::InferenceState, interp::AbstractInterpreter) doopt = (me.cached || me.parent !== nothing) recompute_cfg = type_annotate!(interp, me, doopt) if doopt && may_optimize(interp) - me.result.src = OptimizationState(me, OptimizationParams(interp), interp, recompute_cfg) + me.result.src = OptimizationState(me, interp, recompute_cfg) else me.result.src = me.src::CodeInfo # stash a convenience copy of the code (e.g. for reflection) end @@ -966,8 +966,7 @@ function typeinf_ircode( return nothing, Any end (; result) = frame - opt_params = OptimizationParams(interp) - opt = OptimizationState(frame, opt_params, interp) + opt = OptimizationState(frame, interp) ir = run_passes(opt.src, opt, result, optimize_until) rt = widenconst(ignorelimited(result.result)) ccall(:jl_typeinf_timing_end, Cvoid, ()) diff --git a/test/compiler/EscapeAnalysis/EAUtils.jl b/test/compiler/EscapeAnalysis/EAUtils.jl index 51b4b66c22643..6894733e0fa45 100644 --- a/test/compiler/EscapeAnalysis/EAUtils.jl +++ b/test/compiler/EscapeAnalysis/EAUtils.jl @@ -141,9 +141,9 @@ function invalidate_cache!(replaced, max_world, depth = 0) end function CC.optimize(interp::EscapeAnalyzer, - opt::OptimizationState, params::OptimizationParams, caller::InferenceResult) + opt::OptimizationState, caller::InferenceResult) ir = run_passes_with_ea(interp, opt.src, opt, caller) - return CC.finish(interp, opt, params, ir, caller) + return CC.finish(interp, opt, ir, caller) end function CC.cache_result!(interp::EscapeAnalyzer, caller::InferenceResult) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 182776d79d7ec..b0ab6a9a7434c 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1590,7 +1590,7 @@ gg13183(x::X...) where {X} = (_false13183 ? gg13183(x, x) : 0) let linfo = get_linfo(Base.convert, Tuple{Type{Int64}, Int32}), world = UInt(23) # some small-numbered world that should be valid interp = Core.Compiler.NativeInterpreter() - opt = Core.Compiler.OptimizationState(linfo, Core.Compiler.OptimizationParams(interp), interp) + opt = Core.Compiler.OptimizationState(linfo, interp) # make sure the state of the properties look reasonable @test opt.src !== linfo.def.source @test length(opt.src.slotflags) == linfo.def.nargs <= length(opt.src.slotnames) @@ -4125,16 +4125,14 @@ function f_convert_me_to_ir(b, x) return a end -let - # Test the presence of PhiNodes in lowered IR by taking the above function, +let # Test the presence of PhiNodes in lowered IR by taking the above function, # running it through SSA conversion and then putting it into an opaque # closure. mi = Core.Compiler.specialize_method(first(methods(f_convert_me_to_ir)), Tuple{Bool, Float64}, Core.svec()) ci = Base.uncompressed_ast(mi.def) ci.ssavaluetypes = Any[Any for i = 1:ci.ssavaluetypes] - sv = Core.Compiler.OptimizationState(mi, Core.Compiler.OptimizationParams(), - Core.Compiler.NativeInterpreter()) + sv = Core.Compiler.OptimizationState(mi, Core.Compiler.NativeInterpreter()) ir = Core.Compiler.convert_to_ircode(ci, sv) ir = Core.Compiler.slot2reg(ir, ci, sv) ir = Core.Compiler.compact!(ir) diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 37e5bde9d9a48..fa0d1ae2b1753 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -1754,8 +1754,7 @@ let interp = Core.Compiler.NativeInterpreter() # ok, now delete the callsite flag, and see the second inlining pass can inline the call @eval Core.Compiler $ir.stmts[$i][:flag] &= ~IR_FLAG_NOINLINE - inlining = Core.Compiler.InliningState(Core.Compiler.OptimizationParams(interp), nothing, - Core.Compiler.get_world_counter(interp), interp) + inlining = Core.Compiler.InliningState(interp) ir = Core.Compiler.ssa_inlining_pass!(ir, inlining, false) @test count(isinvoke(:*), ir.stmts.inst) == 0 @test count(iscall((ir, Core.Intrinsics.mul_int)), ir.stmts.inst) == 1 From 931660663fb470e355b4e020bb604ab11e070e47 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 8 Mar 2023 14:45:47 +0900 Subject: [PATCH 285/775] refactor the `at-newinterp` test utility (#48943) --- test/compiler/AbstractInterpreter.jl | 75 +++------------------------- test/compiler/inference.jl | 59 ++++------------------ test/compiler/newinterp.jl | 45 +++++++++++++++++ 3 files changed, 63 insertions(+), 116 deletions(-) create mode 100644 test/compiler/newinterp.jl diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index 1926e23c7dbc2..fcb00eaa45019 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -2,45 +2,9 @@ using Test const CC = Core.Compiler -import Core: MethodInstance, CodeInstance -import .CC: WorldRange, WorldView include("irutils.jl") - -""" - @newinterp NewInterpreter - -Defines new `NewInterpreter <: AbstractInterpreter` whose cache is separated -from the native code cache, satisfying the minimum interface requirements. -""" -macro newinterp(name) - cachename = Symbol(string(name, "Cache")) - name = esc(name) - quote - struct $cachename - dict::IdDict{MethodInstance,CodeInstance} - end - struct $name <: CC.AbstractInterpreter - interp::CC.NativeInterpreter - cache::$cachename - meta # additional information - $name(world = Base.get_world_counter(); - interp = CC.NativeInterpreter(world), - cache = $cachename(IdDict{MethodInstance,CodeInstance}()), - meta = nothing, - ) = new(interp, cache, meta) - end - CC.InferenceParams(interp::$name) = CC.InferenceParams(interp.interp) - CC.OptimizationParams(interp::$name) = CC.OptimizationParams(interp.interp) - CC.get_world_counter(interp::$name) = CC.get_world_counter(interp.interp) - CC.get_inference_cache(interp::$name) = CC.get_inference_cache(interp.interp) - CC.code_cache(interp::$name) = WorldView(interp.cache, WorldRange(CC.get_world_counter(interp))) - CC.get(wvc::WorldView{<:$cachename}, mi::MethodInstance, default) = get(wvc.cache.dict, mi, default) - CC.getindex(wvc::WorldView{<:$cachename}, mi::MethodInstance) = getindex(wvc.cache.dict, mi) - CC.haskey(wvc::WorldView{<:$cachename}, mi::MethodInstance) = haskey(wvc.cache.dict, mi) - CC.setindex!(wvc::WorldView{<:$cachename}, ci::CodeInstance, mi::MethodInstance) = setindex!(wvc.cache.dict, ci, mi) - end -end +include("newinterp.jl") # OverlayMethodTable # ================== @@ -82,7 +46,7 @@ end |> !Core.Compiler.is_nonoverlayed callstrange(::Nothing) = Core.compilerbarrier(:type, nothing) # trigger inference bail out callstrange(::Float64) = strangesin(x) callstrange_entry(x) = callstrange(x) # needs to be defined here because of world age -let interp = MTOverlayInterp(; meta=Set{Any}()) +let interp = MTOverlayInterp(Set{Any}()) matches = Core.Compiler.findall(Tuple{typeof(callstrange),Any}, Core.Compiler.method_table(interp)).matches @test Core.Compiler.length(matches) == 2 if Core.Compiler.getindex(matches, 1).method == which(callstrange, (Nothing,)) @@ -290,35 +254,10 @@ end |> only === Any # CallInfo × inlining # =================== -import .CC: CallInfo - -struct NoinlineInterpreterCache - dict::IdDict{MethodInstance,CodeInstance} -end +@newinterp NoinlineInterpreter +noinline_modules(interp::NoinlineInterpreter) = interp.meta::Set{Module} -""" - NoinlineInterpreter(noinline_modules::Set{Module}) <: AbstractInterpreter - -An `AbstractInterpreter` that has additional inlineability rules based on caller module context. -""" -struct NoinlineInterpreter <: CC.AbstractInterpreter - noinline_modules::Set{Module} - interp::CC.NativeInterpreter - cache::NoinlineInterpreterCache - NoinlineInterpreter(noinline_modules::Set{Module}, world = Base.get_world_counter(); - interp = CC.NativeInterpreter(world), - cache = NoinlineInterpreterCache(IdDict{MethodInstance,CodeInstance}()) - ) = new(noinline_modules, interp, cache) -end -CC.InferenceParams(interp::NoinlineInterpreter) = CC.InferenceParams(interp.interp) -CC.OptimizationParams(interp::NoinlineInterpreter) = CC.OptimizationParams(interp.interp) -CC.get_world_counter(interp::NoinlineInterpreter) = CC.get_world_counter(interp.interp) -CC.get_inference_cache(interp::NoinlineInterpreter) = CC.get_inference_cache(interp.interp) -CC.code_cache(interp::NoinlineInterpreter) = WorldView(interp.cache, WorldRange(CC.get_world_counter(interp))) -CC.get(wvc::WorldView{<:NoinlineInterpreterCache}, mi::MethodInstance, default) = get(wvc.cache.dict, mi, default) -CC.getindex(wvc::WorldView{<:NoinlineInterpreterCache}, mi::MethodInstance) = getindex(wvc.cache.dict, mi) -CC.haskey(wvc::WorldView{<:NoinlineInterpreterCache}, mi::MethodInstance) = haskey(wvc.cache.dict, mi) -CC.setindex!(wvc::WorldView{<:NoinlineInterpreterCache}, ci::CodeInstance, mi::MethodInstance) = setindex!(wvc.cache.dict, ci, mi) +import .CC: CallInfo struct NoinlineCallInfo <: CallInfo info::CallInfo # wrapped call @@ -331,7 +270,7 @@ function CC.abstract_call(interp::NoinlineInterpreter, arginfo::CC.ArgInfo, si::CC.StmtInfo, sv::CC.InferenceState, max_methods::Union{Int,Nothing}) ret = @invoke CC.abstract_call(interp::CC.AbstractInterpreter, arginfo::CC.ArgInfo, si::CC.StmtInfo, sv::CC.InferenceState, max_methods::Union{Int,Nothing}) - if sv.mod in interp.noinline_modules + if sv.mod in noinline_modules(interp) return CC.CallMeta(ret.rt, ret.effects, NoinlineCallInfo(ret.info)) end return ret @@ -372,7 +311,7 @@ let NoinlineModule = Module() # it should work for cached results method = only(methods(inlined_usually, (Float64,Float64,Float64,))) mi = CC.specialize_method(method, Tuple{typeof(inlined_usually),Float64,Float64,Float64}, Core.svec()) - @test haskey(interp.cache.dict, mi) + @test haskey(interp.code_cache.dict, mi) let src = code_typed1((Float64,Float64,Float64); interp) do x, y, z inlined_usually(x, y, z) end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index b0ab6a9a7434c..e6be51542c205 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2152,45 +2152,6 @@ end # ========================= # `MustAlias` propagates constraints imposed on aliased fields -import Core: MethodInstance, CodeInstance -const CC = Core.Compiler -import .CC: WorldRange, WorldView - -""" - @newinterp NewInterpreter - -Defines new `NewInterpreter <: AbstractInterpreter` whose cache is separated -from the native code cache, satisfying the minimum interface requirements. -""" -macro newinterp(name) - cachename = Symbol(string(name, "Cache")) - name = esc(name) - quote - struct $cachename - dict::IdDict{MethodInstance,CodeInstance} - end - struct $name <: CC.AbstractInterpreter - interp::CC.NativeInterpreter - cache::$cachename - meta # additional information - $name(world = Base.get_world_counter(); - interp = CC.NativeInterpreter(world), - cache = $cachename(IdDict{MethodInstance,CodeInstance}()), - meta = nothing, - ) = new(interp, cache, meta) - end - CC.InferenceParams(interp::$name) = CC.InferenceParams(interp.interp) - CC.OptimizationParams(interp::$name) = CC.OptimizationParams(interp.interp) - CC.get_world_counter(interp::$name) = CC.get_world_counter(interp.interp) - CC.get_inference_cache(interp::$name) = CC.get_inference_cache(interp.interp) - CC.code_cache(interp::$name) = WorldView(interp.cache, WorldRange(CC.get_world_counter(interp))) - CC.get(wvc::WorldView{<:$cachename}, mi::MethodInstance, default) = get(wvc.cache.dict, mi, default) - CC.getindex(wvc::WorldView{<:$cachename}, mi::MethodInstance) = getindex(wvc.cache.dict, mi) - CC.haskey(wvc::WorldView{<:$cachename}, mi::MethodInstance) = haskey(wvc.cache.dict, mi) - CC.setindex!(wvc::WorldView{<:$cachename}, ci::CodeInstance, mi::MethodInstance) = setindex!(wvc.cache.dict, ci, mi) - end -end - struct AliasableField{T} f::T end @@ -2203,18 +2164,20 @@ mutable struct AliasableConstField{S,T} f2::T end -# lattice -# ------- - import Core.Compiler: - AbstractLattice, ConstsLattice, PartialsLattice, InferenceLattice, OptimizerLattice, - MustAliasesLattice, InterMustAliasesLattice, BaseInferenceLattice, IPOResultLattice, - typeinf_lattice, ipo_lattice, optimizer_lattice + InferenceLattice, OptimizerLattice, MustAliasesLattice, InterMustAliasesLattice, + BaseInferenceLattice, IPOResultLattice, typeinf_lattice, ipo_lattice, optimizer_lattice +include("newinterp.jl") @newinterp MustAliasInterpreter -CC.typeinf_lattice(::MustAliasInterpreter) = InferenceLattice(MustAliasesLattice(BaseInferenceLattice.instance)) -CC.ipo_lattice(::MustAliasInterpreter) = InferenceLattice(InterMustAliasesLattice(IPOResultLattice.instance)) -CC.optimizer_lattice(::MustAliasInterpreter) = OptimizerLattice() +let CC = Core.Compiler + CC.typeinf_lattice(::MustAliasInterpreter) = InferenceLattice(MustAliasesLattice(BaseInferenceLattice.instance)) + CC.ipo_lattice(::MustAliasInterpreter) = InferenceLattice(InterMustAliasesLattice(IPOResultLattice.instance)) + CC.optimizer_lattice(::MustAliasInterpreter) = OptimizerLattice() +end + +# lattice +# ------- import Core.Compiler: MustAlias, Const, PartialStruct, ⊑, tmerge let 𝕃ᵢ = InferenceLattice(MustAliasesLattice(BaseInferenceLattice.instance)) diff --git a/test/compiler/newinterp.jl b/test/compiler/newinterp.jl new file mode 100644 index 0000000000000..56a68f2a09545 --- /dev/null +++ b/test/compiler/newinterp.jl @@ -0,0 +1,45 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + @newinterp NewInterpreter + +Defines new `NewInterpreter <: AbstractInterpreter` whose cache is separated +from the native code cache, satisfying the minimum interface requirements. +""" +macro newinterp(InterpName) + InterpCacheName = esc(Symbol(string(InterpName, "Cache"))) + InterpName = esc(InterpName) + C = Core + CC = Core.Compiler + quote + struct $InterpCacheName + dict::IdDict{$C.MethodInstance,$C.CodeInstance} + end + $InterpCacheName() = $InterpCacheName(IdDict{$C.MethodInstance,$C.CodeInstance}()) + struct $InterpName <: $CC.AbstractInterpreter + meta # additional information + world::UInt + inf_params::$CC.InferenceParams + opt_params::$CC.OptimizationParams + inf_cache::Vector{$CC.InferenceResult} + code_cache::$InterpCacheName + function $InterpName(meta = nothing; + world::UInt = Base.get_world_counter(), + inf_params::$CC.InferenceParams = $CC.InferenceParams(), + opt_params::$CC.OptimizationParams = $CC.OptimizationParams(), + inf_cache::Vector{$CC.InferenceResult} = $CC.InferenceResult[], + code_cache::$InterpCacheName = $InterpCacheName()) + return new(meta, world, inf_params, opt_params, inf_cache, code_cache) + end + end + $CC.InferenceParams(interp::$InterpName) = interp.inf_params + $CC.OptimizationParams(interp::$InterpName) = interp.opt_params + $CC.get_world_counter(interp::$InterpName) = interp.world + $CC.get_inference_cache(interp::$InterpName) = interp.inf_cache + $CC.code_cache(interp::$InterpName) = $CC.WorldView(interp.code_cache, $CC.WorldRange(interp.world)) + $CC.get(wvc::$CC.WorldView{$InterpCacheName}, mi::$C.MethodInstance, default) = get(wvc.cache.dict, mi, default) + $CC.getindex(wvc::$CC.WorldView{$InterpCacheName}, mi::$C.MethodInstance) = getindex(wvc.cache.dict, mi) + $CC.haskey(wvc::$CC.WorldView{$InterpCacheName}, mi::$C.MethodInstance) = haskey(wvc.cache.dict, mi) + $CC.setindex!(wvc::$CC.WorldView{$InterpCacheName}, ci::$C.CodeInstance, mi::$C.MethodInstance) = setindex!(wvc.cache.dict, ci, mi) + end +end From 77f52e3b1815a813f97ff3c922cc50df0703d559 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 8 Mar 2023 11:41:08 +0100 Subject: [PATCH 286/775] revert all uniformscaling.jl changes --- stdlib/LinearAlgebra/src/uniformscaling.jl | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index 987ad84ae2f24..f5c8bdd6f2e75 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -419,18 +419,16 @@ promote_to_arrays(n,k, ::Type{T}, A, B, Cs...) where {T} = (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays(n,k+2, T, Cs...)...) promote_to_array_type(A::Tuple{Vararg{Union{AbstractVecOrMat,UniformScaling,Number}}}) = Matrix -_catsize(::UniformScaling, _) = -1 -_catsize(A, dim) = (require_one_based_indexing(A); return size(A, dim)) - for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols")) @eval begin @inline $f(A::Union{AbstractVecOrMat,UniformScaling}...) = $_f(A...) @inline $f(A::Union{AbstractVecOrMat,UniformScaling,Number}...) = $_f(A...) function $_f(A::Union{AbstractVecOrMat,UniformScaling,Number}...; array_type = promote_to_array_type(A)) n = -1 - sizes = map(a -> _catsize(a, $dim), A) - for na in sizes - if na != -1 + for a in A + if !isa(a, UniformScaling) + require_one_based_indexing(a) + na = size(a,$dim) n >= 0 && n != na && throw(DimensionMismatch(string("number of ", $name, " of each array must match (got ", n, " and ", na, ")"))) @@ -452,11 +450,11 @@ function _hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractVecOrMat,UniformScali n = fill(-1, length(A)) needcols = false # whether we also need to infer some sizes from the column count j = 0 - sizes = map(a -> _catsize(a, 1), A) for i = 1:nr # infer UniformScaling sizes from row counts, if possible: ni = -1 # number of rows in this block-row, -1 indicates unknown - for na in sizes[j+1:j+rows[i]] - if na != -1 + for k = 1:rows[i] + if !isa(A[j+k], UniformScaling) + na = size(A[j+k], 1) ni >= 0 && ni != na && throw(DimensionMismatch("mismatch in number of rows")) ni = na From e7b0b557811a7eee126c9e478f109cf4d5753357 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Wed, 8 Mar 2023 12:37:18 +0100 Subject: [PATCH 287/775] Make example in string interpolation independent of previous section (#48949) When linking directly to this section it is not obvious where the variables `greet` and `whom` are defined. This patch simply (re)defines these variables closer the their use. --- doc/src/manual/strings.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index f73093e9c0b91..72d1eda644720 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -535,7 +535,9 @@ Constructing strings using concatenation can become a bit cumbersome, however. T verbose calls to [`string`](@ref) or repeated multiplications, Julia allows interpolation into string literals using `$`, as in Perl: -```jldoctest stringconcat +```jldoctest +julia> greet = "Hello"; whom = "world"; + julia> "$greet, $whom.\n" "Hello, world.\n" ``` From 8851fc89e88266ac8fc4ce8074b9d642db16f673 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 8 Mar 2023 22:29:27 +0900 Subject: [PATCH 288/775] effects: ignore `:noinbounds` effect bit when collecting backedges (#48932) We can ignore callee's `:noinbounds` effect when it is ensured to not taint `:consistent`-cy since a new method is also known to not taint it. Tests are added. --- base/compiler/abstractinterpretation.jl | 24 ++- base/compiler/effects.jl | 2 +- test/choosetests.jl | 4 +- test/compiler/invalidation.jl | 258 ++++++++++++++++++++++++ 4 files changed, 275 insertions(+), 13 deletions(-) create mode 100644 test/compiler/invalidation.jl diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index de8d26720fd5a..3964f813a0a00 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -502,22 +502,24 @@ function conditional_argtype(@nospecialize(rt), @nospecialize(sig), argtypes::Ve end end -function add_call_backedges!(interp::AbstractInterpreter, - @nospecialize(rettype), all_effects::Effects, +function add_call_backedges!(interp::AbstractInterpreter, @nospecialize(rettype), all_effects::Effects, edges::Vector{MethodInstance}, matches::Union{MethodMatches,UnionSplitMethodMatches}, @nospecialize(atype), sv::InferenceState) - # we don't need to add backedges when: - # - a new method couldn't refine (widen) this type and - # - the effects are known to not provide any useful IPO information + # don't bother to add backedges when both type and effects information are already + # maximized to the top since a new method couldn't refine or widen them anyway if rettype === Any + # ignore the `:nonoverlayed` property if `interp` doesn't use overlayed method table + # since it will never be tainted anyway if !isoverlayed(method_table(interp)) - # we can ignore the `nonoverlayed` property if `interp` doesn't use - # overlayed method table at all since it will never be tainted anyway all_effects = Effects(all_effects; nonoverlayed=false) end - if all_effects === Effects() - return + if (# ignore the `:noinbounds` property if `:consistent`-cy is tainted already + sv.ipo_effects.consistent === ALWAYS_FALSE || all_effects.consistent === ALWAYS_FALSE || + # or this `:noinbounds` doesn't taint it + !stmt_taints_inbounds_consistency(sv)) + all_effects = Effects(all_effects; noinbounds=false) end + all_effects === Effects() && return nothing end for edge in edges add_backedge!(sv, edge) @@ -531,6 +533,7 @@ function add_call_backedges!(interp::AbstractInterpreter, thisfullmatch || add_mt_backedge!(sv, mt, atype) end end + return nothing end const RECURSION_UNUSED_MSG = "Bounded recursion detected with unused result. Annotated return type may be wider than true result." @@ -2475,7 +2478,8 @@ function abstract_eval_foreigncall(interp::AbstractInterpreter, e::Expr, vtypes: override.terminates_globally ? true : effects.terminates, override.notaskstate ? true : effects.notaskstate, override.inaccessiblememonly ? ALWAYS_TRUE : effects.inaccessiblememonly, - effects.nonoverlayed) + effects.nonoverlayed, + effects.noinbounds) end return RTEffects(t, effects) end diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index fdb65037ce507..ace47df9153ef 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -72,7 +72,7 @@ struct Effects notaskstate::Bool, inaccessiblememonly::UInt8, nonoverlayed::Bool, - noinbounds::Bool = true) + noinbounds::Bool) return new( consistent, effect_free, diff --git a/test/choosetests.jl b/test/choosetests.jl index 34737fe255343..627771206b727 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -157,8 +157,8 @@ function choosetests(choices = []) "compiler/datastructures", "compiler/inference", "compiler/effects", "compiler/validation", "compiler/ssair", "compiler/irpasses", "compiler/codegen", "compiler/inline", "compiler/contextual", - "compiler/AbstractInterpreter", "compiler/EscapeAnalysis/local", - "compiler/EscapeAnalysis/interprocedural"]) + "compiler/invalidation", "compiler/AbstractInterpreter", + "compiler/EscapeAnalysis/local", "compiler/EscapeAnalysis/interprocedural"]) filtertests!(tests, "compiler/EscapeAnalysis", [ "compiler/EscapeAnalysis/local", "compiler/EscapeAnalysis/interprocedural"]) filtertests!(tests, "stdlib", STDLIBS) diff --git a/test/compiler/invalidation.jl b/test/compiler/invalidation.jl new file mode 100644 index 0000000000000..20ab2483aa378 --- /dev/null +++ b/test/compiler/invalidation.jl @@ -0,0 +1,258 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# setup +# ----- + +include("irutils.jl") + +using Test +const CC = Core.Compiler +import Core: MethodInstance, CodeInstance +import .CC: WorldRange, WorldView + +struct InvalidationTesterCache + dict::IdDict{MethodInstance,CodeInstance} +end +InvalidationTesterCache() = InvalidationTesterCache(IdDict{MethodInstance,CodeInstance}()) + +const INVALIDATION_TESTER_CACHE = InvalidationTesterCache() + +struct InvalidationTester <: CC.AbstractInterpreter + callback! + world::UInt + inf_params::CC.InferenceParams + opt_params::CC.OptimizationParams + inf_cache::Vector{CC.InferenceResult} + code_cache::InvalidationTesterCache + function InvalidationTester(callback! = nothing; + world::UInt = Base.get_world_counter(), + inf_params::CC.InferenceParams = CC.InferenceParams(), + opt_params::CC.OptimizationParams = CC.OptimizationParams(), + inf_cache::Vector{CC.InferenceResult} = CC.InferenceResult[], + code_cache::InvalidationTesterCache = INVALIDATION_TESTER_CACHE) + if callback! === nothing + callback! = function (replaced::MethodInstance) + # Core.println(replaced) # debug + delete!(code_cache.dict, replaced) + end + end + return new(callback!, world, inf_params, opt_params, inf_cache, code_cache) + end +end + +struct InvalidationTesterCacheView + interp::InvalidationTester + dict::IdDict{MethodInstance,CodeInstance} +end + +CC.InferenceParams(interp::InvalidationTester) = interp.inf_params +CC.OptimizationParams(interp::InvalidationTester) = interp.opt_params +CC.get_world_counter(interp::InvalidationTester) = interp.world +CC.get_inference_cache(interp::InvalidationTester) = interp.inf_cache +CC.code_cache(interp::InvalidationTester) = WorldView(InvalidationTesterCacheView(interp, interp.code_cache.dict), WorldRange(interp.world)) +CC.get(wvc::WorldView{InvalidationTesterCacheView}, mi::MethodInstance, default) = get(wvc.cache.dict, mi, default) +CC.getindex(wvc::WorldView{InvalidationTesterCacheView}, mi::MethodInstance) = getindex(wvc.cache.dict, mi) +CC.haskey(wvc::WorldView{InvalidationTesterCacheView}, mi::MethodInstance) = haskey(wvc.cache.dict, mi) +function CC.setindex!(wvc::WorldView{InvalidationTesterCacheView}, ci::CodeInstance, mi::MethodInstance) + add_callback!(wvc.cache.interp.callback!, mi) + setindex!(wvc.cache.dict, ci, mi) +end + +function add_callback!(@nospecialize(callback!), mi::MethodInstance) + callback = function (replaced::MethodInstance, max_world, + seen::Base.IdSet{MethodInstance} = Base.IdSet{MethodInstance}()) + push!(seen, replaced) + callback!(replaced) + if isdefined(replaced, :backedges) + for item in replaced.backedges + isa(item, MethodInstance) || continue # might be `Type` object representing an `invoke` signature + mi = item + mi in seen && continue # otherwise fail into an infinite loop + var"#self#"(mi, max_world, seen) + end + end + return nothing + end + + if !isdefined(mi, :callbacks) + mi.callbacks = Any[callback] + else + callbacks = mi.callbacks::Vector{Any} + if !any(@nospecialize(cb)->cb===callback, callbacks) + push!(callbacks, callback) + end + end + return nothing +end + + +# basic functionality test +# ------------------------ + +basic_callee(x) = x +basic_caller(x) = basic_callee(x) + +# run inference and check that cache exist +@test Base.return_types((Float64,); interp=InvalidationTester()) do x + basic_caller(x) +end |> only === Float64 +@test any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :basic_callee +end +@test any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :basic_caller +end + +# this redefinition below should invalidate the cache +basic_callee(x) = x, x +@test !any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :basic_callee +end +@test !any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :basic_caller +end + +# re-run inference and check the result is updated (and new cache exists) +@test Base.return_types((Float64,); interp=InvalidationTester()) do x + basic_caller(x) +end |> only === Tuple{Float64,Float64} +@test any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :basic_callee +end +@test any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :basic_caller +end + +# backedge optimization +# --------------------- + +const GLOBAL_BUFFER = IOBuffer() + +# test backedge optimization when the callee's type and effects information are maximized +begin take!(GLOBAL_BUFFER) + + pr48932_callee(x) = (print(GLOBAL_BUFFER, x); Base.inferencebarrier(x)) + pr48932_caller(x) = pr48932_callee(Base.inferencebarrier(x)) + + # assert that type and effects information inferred from `pr48932_callee(::Any)` are the top + let rt = only(Base.return_types(pr48932_callee, (Any,))) + @test rt === Any + effects = Base.infer_effects(pr48932_callee, (Any,)) + @test Core.Compiler.Effects(effects; noinbounds=false) == Core.Compiler.Effects() + end + + # run inference on both `pr48932_caller` and `pr48932_callee` + let (src, rt) = code_typed((Int,); interp=InvalidationTester()) do x + @inline pr48932_caller(x) + end |> only + @test rt === Any + @test any(iscall((src, pr48932_callee)), src.code) + end + @test any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :pr48932_callee + end + @test any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :pr48932_caller + end + @test 42 == pr48932_caller(42) + @test "42" == String(take!(GLOBAL_BUFFER)) + + # test that we didn't add the backedge from `pr48932_callee` to `pr48932_caller`: + # this redefinition below should invalidate the cache of `pr48932_callee` but not that of `pr48932_caller` + pr48932_callee(x) = (print(GLOBAL_BUFFER, x); nothing) + @test !any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :pr48932_callee + end + @test any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :pr48932_caller + end + @test isnothing(pr48932_caller(42)) + @test "42" == String(take!(GLOBAL_BUFFER)) +end + +# we can avoid adding backedge even if the callee's return type is not the top +# when the return value is not used within the caller +begin take!(GLOBAL_BUFFER) + + pr48932_callee_inferrable(x) = (print(GLOBAL_BUFFER, x); nothing) + pr48932_caller_unuse(x) = (pr48932_callee_inferrable(Base.inferencebarrier(x)); nothing) + + # assert that type and effects information inferred from `pr48932_callee(::Any)` are the top + let rt = only(Base.return_types(pr48932_callee_inferrable, (Any,))) + @test rt === Nothing + effects = Base.infer_effects(pr48932_callee_inferrable, (Any,)) + @test Core.Compiler.Effects(effects; noinbounds=false) == Core.Compiler.Effects() + end + + # run inference on both `pr48932_caller` and `pr48932_callee`: + # we don't need to add backedge to `pr48932_callee` from `pr48932_caller` + # since the inference result of `pr48932_callee` is maximized and it's not inlined + let (src, rt) = code_typed((Int,); interp=InvalidationTester()) do x + @inline pr48932_caller_unuse(x) + end |> only + @test rt === Nothing + @test any(iscall((src, pr48932_callee_inferrable)), src.code) + end + @test any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :pr48932_callee_inferrable + end + @test any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :pr48932_caller_unuse + end + @test isnothing(pr48932_caller_unuse(42)) + @test "42" == String(take!(GLOBAL_BUFFER)) + + # test that we didn't add the backedge from `pr48932_callee_inferrable` to `pr48932_caller_unuse`: + # this redefinition below should invalidate the cache of `pr48932_callee_inferrable` but not that of `pr48932_caller_unuse` + pr48932_callee_inferrable(x) = (print(GLOBAL_BUFFER, "foo"); x) + @test !any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :pr48932_callee_inferrable + end + @test any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :pr48932_caller_unuse + end + @test isnothing(pr48932_caller_unuse(42)) + @test "foo" == String(take!(GLOBAL_BUFFER)) +end + +# we need to add backedge when the callee is inlined +begin take!(GLOBAL_BUFFER) + + @noinline pr48932_callee_inlined(@nospecialize x) = (print(GLOBAL_BUFFER, x); Base.inferencebarrier(x)) + pr48932_caller_inlined(x) = pr48932_callee_inlined(Base.inferencebarrier(x)) + + # assert that type and effects information inferred from `pr48932_callee(::Any)` are the top + let rt = only(Base.return_types(pr48932_callee_inlined, (Any,))) + @test rt === Any + effects = Base.infer_effects(pr48932_callee_inlined, (Any,)) + @test Core.Compiler.Effects(effects; noinbounds=false) == Core.Compiler.Effects() + end + + # run inference on `pr48932_caller_inlined` and `pr48932_callee_inlined` + let (src, rt) = code_typed((Int,); interp=InvalidationTester()) do x + @inline pr48932_caller_inlined(x) + end |> only + @test rt === Any + @test any(isinvoke(:pr48932_callee_inlined), src.code) + end + @test any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :pr48932_callee_inlined + end + @test any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :pr48932_caller_inlined + end + @test 42 == pr48932_caller_inlined(42) + @test "42" == String(take!(GLOBAL_BUFFER)) + + # test that we added the backedge from `pr48932_callee_inlined` to `pr48932_caller_inlined`: + # this redefinition below should invalidate the cache of `pr48932_callee_inlined` but not that of `pr48932_caller_inlined` + @noinline pr48932_callee_inlined(@nospecialize x) = (print(GLOBAL_BUFFER, x); nothing) + @test !any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :pr48932_callee_inlined + end + @test !any(INVALIDATION_TESTER_CACHE.dict) do (mi, ci) + mi.def.name === :pr48932_caller_inlined + end + @test isnothing(pr48932_caller_inlined(42)) + @test "42" == String(take!(GLOBAL_BUFFER)) +end From 9ef2c90ed7d4270d1439f01d70117d27eb0baa84 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 8 Mar 2023 13:05:32 -0300 Subject: [PATCH 289/775] codegen: add missing ssavalue check for store hoisting (#48939) Fix #48917 --- src/codegen.cpp | 2 +- test/compiler/codegen.jl | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 47b9519bbb2f0..60bc08326b759 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5284,7 +5284,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ jl_cgval_t res = emit_call(ctx, ex, expr_t, is_promotable); // some intrinsics (e.g. typeassert) can return a wider type // than what's actually possible - if (is_promotable && res.promotion_point) { + if (is_promotable && res.promotion_point && res.promotion_ssa == -1) { res.promotion_ssa = ssaidx_0based; } res = update_julia_type(ctx, res, expr_t); diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 09f6c772fea52..2279856cf141c 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -821,3 +821,7 @@ function F48394(a, b, i) end @test F48394(X48394(nothing,true), Y48394(nothing, missing), true) @test occursin("llvm.trap", get_llvm(F48394, Tuple{X48394, Y48394, Bool})) + +# issue 48917, hoisting load to above the parent +f48917(x, w) = (y = (a=1, b=x); z = (; a=(a=(1, w), b=(3, y)))) +@test f48917(1,2) == (a = (a = (1, 2), b = (3, (a = 1, b = 1))),) From a580556d9859738a522888d8bf04c921fd2a3e5c Mon Sep 17 00:00:00 2001 From: Dilum Aluthge Date: Wed, 8 Mar 2023 11:09:31 -0500 Subject: [PATCH 290/775] Release checklist: the release manager should wait between step 13 and step 14 (#48942) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1fe101f939090..35bc195512ade 100644 --- a/Makefile +++ b/Makefile @@ -152,7 +152,7 @@ release-candidate: release testall @echo 10. Follow packaging instructions in doc/build/distributing.md to create binary packages for all platforms @echo 11. Upload to AWS, update https://julialang.org/downloads and http://status.julialang.org/stable links @echo 12. Update checksums on AWS for tarball and packaged binaries - @echo 13. Update versions.json + @echo 13. Update versions.json. Wait at least 60 minutes before proceeding to step 14. @echo 14. Push to Juliaup (https://github.com/JuliaLang/juliaup/wiki/Adding-a-Julia-version) @echo 15. Announce on mailing lists @echo 16. Change master to release-0.X in base/version.jl and base/version_git.sh as in 4cb1e20 From eb2e9687d0ac694d0aa25434b30396ee2cfa5cd3 Mon Sep 17 00:00:00 2001 From: Milan Bouchet-Valat Date: Wed, 8 Mar 2023 17:10:33 +0100 Subject: [PATCH 291/775] Install 7z, lld and dsymutils to private libexec/julia directory (#48931) * Install 7z, lld, dsymutil to private libexec/julia directory Installing files directly into /usr/libexec pollutes the install tree and can create conflicts if other apps do the same. Instead, install them to `private_libdir`, which defaults to `$(libexecdir)/julia`. This is similar to what we do with `private_libdir`. --- Make.inc | 5 ++++- Makefile | 18 +++++++++--------- base/Makefile | 2 ++ base/linking.jl | 6 +++--- stdlib/LLD_jll/src/LLD_jll.jl | 4 ++-- stdlib/p7zip_jll/src/p7zip_jll.jl | 4 ++-- test/osutils.jl | 2 +- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/Make.inc b/Make.inc index f0034a2d3be62..b12f1a0d17937 100644 --- a/Make.inc +++ b/Make.inc @@ -286,6 +286,9 @@ private_libdir := $(libdir)/julia endif build_private_libdir := $(build_libdir)/julia +private_libexecdir := $(libexecdir)/julia +build_private_libexecdir := $(build_libexecdir)/julia + # A helper functions for dealing with lazily-evaluated, expensive operations.. Spinning # up a python process to, for exaxmple, parse a TOML file is expensive, and we must wait # until the TOML files are on-disk before we can parse them. This means that we cannot @@ -310,7 +313,7 @@ define cache_rel_path $(1)_rel_eval = $(call rel_path,$(2),$($(1))) $(1)_rel = $$(call hit_cache,$(1)_rel_eval) endef -$(foreach D,libdir private_libdir datarootdir libexecdir docdir sysconfdir includedir,$(eval $(call cache_rel_path,$(D),$(bindir)))) +$(foreach D,libdir private_libdir datarootdir libexecdir private_libexecdir docdir sysconfdir includedir,$(eval $(call cache_rel_path,$(D),$(bindir)))) $(foreach D,build_libdir build_private_libdir,$(eval $(call cache_rel_path,$(D),$(build_bindir)))) # Save a special one: reverse_private_libdir_rel: usually just `../`, but good to be general: diff --git a/Makefile b/Makefile index 35bc195512ade..84f08f47761ca 100644 --- a/Makefile +++ b/Makefile @@ -253,7 +253,7 @@ endef install: $(build_depsbindir)/stringreplace docs @$(MAKE) $(QUIET_MAKE) $(JULIA_BUILD_MODE) - @for subdir in $(bindir) $(datarootdir)/julia/stdlib/$(VERSDIR) $(docdir) $(man1dir) $(includedir)/julia $(libdir) $(private_libdir) $(sysconfdir) $(libexecdir); do \ + @for subdir in $(bindir) $(datarootdir)/julia/stdlib/$(VERSDIR) $(docdir) $(man1dir) $(includedir)/julia $(libdir) $(private_libdir) $(sysconfdir) $(private_libexecdir); do \ mkdir -p $(DESTDIR)$$subdir; \ done @@ -268,8 +268,8 @@ else ifeq ($(JULIA_BUILD_MODE),debug) -$(INSTALL_M) $(build_libdir)/libjulia-internal-debug.dll.a $(DESTDIR)$(libdir)/ endif - # We have a single exception; we want 7z.dll to live in libexec, not bin, so that 7z.exe can find it. - -mv $(DESTDIR)$(bindir)/7z.dll $(DESTDIR)$(libexecdir)/ + # We have a single exception; we want 7z.dll to live in private_libexecdir, not bindir, so that 7z.exe can find it. + -mv $(DESTDIR)$(bindir)/7z.dll $(DESTDIR)$(private_libexecdir)/ -$(INSTALL_M) $(build_bindir)/libopenlibm.dll.a $(DESTDIR)$(libdir)/ -$(INSTALL_M) $(build_libdir)/libssp.dll.a $(DESTDIR)$(libdir)/ # The rest are compiler dependencies, as an example memcpy is exported by msvcrt @@ -331,14 +331,14 @@ endif done \ done endif - # Install `7z` into libexec/ - $(INSTALL_M) $(build_bindir)/7z$(EXE) $(DESTDIR)$(libexecdir)/ + # Install `7z` into private_libexecdir + $(INSTALL_M) $(build_bindir)/7z$(EXE) $(DESTDIR)$(private_libexecdir)/ - # Install `lld` into libexec/ - $(INSTALL_M) $(build_depsbindir)/lld$(EXE) $(DESTDIR)$(libexecdir)/ + # Install `lld` into private_libexecdir + $(INSTALL_M) $(build_depsbindir)/lld$(EXE) $(DESTDIR)$(private_libexecdir)/ - # Install `dsymutil` into libexec/ - $(INSTALL_M) $(build_depsbindir)/dsymutil$(EXE) $(DESTDIR)$(libexecdir)/ + # Install `dsymutil` into private_libexecdir/ + $(INSTALL_M) $(build_depsbindir)/dsymutil$(EXE) $(DESTDIR)$(private_libexecdir)/ # Copy public headers cp -R -L $(build_includedir)/julia/* $(DESTDIR)$(includedir)/julia diff --git a/base/Makefile b/base/Makefile index d92302b766988..0ea0359c8cc8e 100644 --- a/base/Makefile +++ b/base/Makefile @@ -66,6 +66,7 @@ ifeq ($(OS),WINNT) @printf 'const LIBDIR = "%s"\n' '$(subst /,\\,$(libdir_rel))' >> $@ @printf 'const LIBEXECDIR = "%s"\n' '$(subst /,\\,$(libexecdir_rel))' >> $@ @printf 'const PRIVATE_LIBDIR = "%s"\n' '$(subst /,\\,$(private_libdir_rel))' >> $@ + @printf 'const PRIVATE_LIBEXECDIR = "%s"\n' '$(subst /,\\,$(private_libexecdir_rel))' >> $@ @printf 'const INCLUDEDIR = "%s"\n' '$(subst /,\\,$(includedir_rel))' >> $@ else @echo "const SYSCONFDIR = \"$(sysconfdir_rel)\"" >> $@ @@ -74,6 +75,7 @@ else @echo "const LIBDIR = \"$(libdir_rel)\"" >> $@ @echo "const LIBEXECDIR = \"$(libexecdir_rel)\"" >> $@ @echo "const PRIVATE_LIBDIR = \"$(private_libdir_rel)\"" >> $@ + @echo "const PRIVATE_LIBEXECDIR = \"$(private_libexecdir_rel)\"" >> $@ @echo "const INCLUDEDIR = \"$(includedir_rel)\"" >> $@ endif ifeq ($(DARWIN_FRAMEWORK), 1) diff --git a/base/linking.jl b/base/linking.jl index b2eea94b3cf26..fd21ce74c9268 100644 --- a/base/linking.jl +++ b/base/linking.jl @@ -49,8 +49,8 @@ end function __init_lld_path() # Prefer our own bundled lld, but if we don't have one, pick it up off of the PATH - # If this is an in-tree build, `lld` will live in `tools`. Otherwise, it'll be in `libexec` - for bundled_lld_path in (joinpath(Sys.BINDIR, Base.LIBEXECDIR, lld_exe), + # If this is an in-tree build, `lld` will live in `tools`. Otherwise, it'll be in `private_libexecdir` + for bundled_lld_path in (joinpath(Sys.BINDIR, Base.PRIVATE_LIBEXECDIR, lld_exe), joinpath(Sys.BINDIR, "..", "tools", lld_exe), joinpath(Sys.BINDIR, lld_exe)) if isfile(bundled_lld_path) @@ -64,7 +64,7 @@ end function __init_dsymutil_path() #Same as with lld but for dsymutil - for bundled_dsymutil_path in (joinpath(Sys.BINDIR, Base.LIBEXECDIR, dsymutil_exe), + for bundled_dsymutil_path in (joinpath(Sys.BINDIR, Base.PRIVATE_LIBEXECDIR, dsymutil_exe), joinpath(Sys.BINDIR, "..", "tools", dsymutil_exe), joinpath(Sys.BINDIR, dsymutil_exe)) if isfile(bundled_dsymutil_path) diff --git a/stdlib/LLD_jll/src/LLD_jll.jl b/stdlib/LLD_jll/src/LLD_jll.jl index 80653353a7c17..a59d8deb8c7b5 100644 --- a/stdlib/LLD_jll/src/LLD_jll.jl +++ b/stdlib/LLD_jll/src/LLD_jll.jl @@ -70,8 +70,8 @@ end function init_lld_path() # Prefer our own bundled lld, but if we don't have one, pick it up off of the PATH - # If this is an in-tree build, `lld` will live in `tools`. Otherwise, it'll be in `libexec` - for bundled_lld_path in (joinpath(Sys.BINDIR, Base.LIBEXECDIR, lld_exe), + # If this is an in-tree build, `lld` will live in `tools`. Otherwise, it'll be in `private_libexecdir` + for bundled_lld_path in (joinpath(Sys.BINDIR, Base.PRIVATE_LIBEXECDIR, lld_exe), joinpath(Sys.BINDIR, "..", "tools", lld_exe), joinpath(Sys.BINDIR, lld_exe)) if isfile(bundled_lld_path) diff --git a/stdlib/p7zip_jll/src/p7zip_jll.jl b/stdlib/p7zip_jll/src/p7zip_jll.jl index 4320003b282f7..eaa709735c383 100644 --- a/stdlib/p7zip_jll/src/p7zip_jll.jl +++ b/stdlib/p7zip_jll/src/p7zip_jll.jl @@ -69,8 +69,8 @@ end function init_p7zip_path() # Prefer our own bundled p7zip, but if we don't have one, pick it up off of the PATH - # If this is an in-tree build, `7z` will live in `bin`. Otherwise, it'll be in `libexec` - for bundled_p7zip_path in (joinpath(Sys.BINDIR, Base.LIBEXECDIR, p7zip_exe), + # If this is an in-tree build, `7z` will live in `bindir`. Otherwise, it'll be in `private_libexecdir` + for bundled_p7zip_path in (joinpath(Sys.BINDIR, Base.PRIVATE_LIBEXECDIR, p7zip_exe), joinpath(Sys.BINDIR, p7zip_exe)) if isfile(bundled_p7zip_path) global p7zip_path = abspath(bundled_p7zip_path) diff --git a/test/osutils.jl b/test/osutils.jl index 36f2878017129..5e72675279cbc 100644 --- a/test/osutils.jl +++ b/test/osutils.jl @@ -51,7 +51,7 @@ end if Sys.iswindows() @testset "path variables use correct path delimiters on windows" begin for path in (Base.SYSCONFDIR, Base.DATAROOTDIR, Base.DOCDIR, - Base.LIBDIR, Base.PRIVATE_LIBDIR, Base.INCLUDEDIR, Base.LIBEXECDIR) + Base.LIBDIR, Base.PRIVATE_LIBDIR, Base.INCLUDEDIR, Base.LIBEXECDIR, Base.PRIVATE_LIBEXECDIR) @test !occursin("/", path) @test !occursin("\\\\", path) end From 89f323a0e9ca18a0866ff1024884b59648d25d17 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 8 Mar 2023 17:52:58 +0100 Subject: [PATCH 292/775] resolve more type piracies --- base/array.jl | 10 ++++++++++ stdlib/LinearAlgebra/src/special.jl | 2 -- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/base/array.jl b/base/array.jl index 213698c754bb3..93335153954f6 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1930,6 +1930,9 @@ function hcat(V::Vector{T}...) where T end return [ V[j][i]::T for i=1:length(V[1]), j=1:length(V) ] end +hcat(A::Union{Number,Vector,Matrix}...) = cat(A...; dims=Val(2)) # more special than LinAlg/special.jl +hcat(A::VecOrMat{T}...) where {T} = typed_hcat(T, A...) # more special than LinAlg/special.jl +hcat(A::Vector...) = cat(A...; dims=Val(2)) # more special than SparseArrays's vcat function vcat(arrays::Vector{T}...) where T n = 0 @@ -1946,6 +1949,13 @@ function vcat(arrays::Vector{T}...) where T end return arr end +vcat(A::Vector...) = cat(A...; dims=Val(1)) # more special than SparseArrays's vcat +vcat(A::Union{Number,Vector,Matrix}...) = cat(A...; dims=Val(1)) # more special than LinAlg/special.jl +vcat(A::VecOrMat{T}...) where {T} = typed_vcat(T, A...) # more special than LinAlg/special.jl + +# disambiguation with LinAlg/special.jl +hvcat(rows::Tuple{Vararg{Int}}, xs::Union{Number,Vector,Matrix}...) = typed_hvcat(promote_eltypeof(xs...), rows, xs...) +hvcat(rows::Tuple{Vararg{Int}}, xs::VecOrMat{T}...) where {T} = typed_hvcat(T, rows, xs...) _cat(n::Integer, x::Integer...) = reshape([x...], (ntuple(Returns(1), n-1)..., length(x))) diff --git a/stdlib/LinearAlgebra/src/special.jl b/stdlib/LinearAlgebra/src/special.jl index 19b1057f9d6b8..06227c782002a 100644 --- a/stdlib/LinearAlgebra/src/special.jl +++ b/stdlib/LinearAlgebra/src/special.jl @@ -335,9 +335,7 @@ const _TypedDenseConcatGroup{T} = Union{Vector{T}, Adjoint{T,Vector{T}}, Transpo promote_to_array_type(::Tuple{Vararg{Union{_DenseConcatGroup,UniformScaling}}}) = Matrix Base._cat(dims, xs::_DenseConcatGroup...) = Base._cat_t(dims, promote_eltype(xs...), xs...) -vcat(A::Vector...) = Base.typed_vcat(promote_eltype(A...), A...) vcat(A::_DenseConcatGroup...) = Base.typed_vcat(promote_eltype(A...), A...) -hcat(A::Vector...) = Base.typed_hcat(promote_eltype(A...), A...) hcat(A::_DenseConcatGroup...) = Base.typed_hcat(promote_eltype(A...), A...) hvcat(rows::Tuple{Vararg{Int}}, xs::_DenseConcatGroup...) = Base.typed_hvcat(promote_eltype(xs...), rows, xs...) # For performance, specially handle the case where the matrices/vectors have homogeneous eltype From 554b5a6e72977e7004ba4ad9c19797fb0790458b Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 8 Mar 2023 17:53:12 +0100 Subject: [PATCH 293/775] add safe optimization --- stdlib/LinearAlgebra/src/uniformscaling.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index f5c8bdd6f2e75..2f18dc45a2909 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -419,10 +419,14 @@ promote_to_arrays(n,k, ::Type{T}, A, B, Cs...) where {T} = (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays(n,k+2, T, Cs...)...) promote_to_array_type(A::Tuple{Vararg{Union{AbstractVecOrMat,UniformScaling,Number}}}) = Matrix +_us2number(A) = A +_us2number(J::UniformScaling) = J.λ + for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols")) @eval begin @inline $f(A::Union{AbstractVecOrMat,UniformScaling}...) = $_f(A...) - @inline $f(A::Union{AbstractVecOrMat,UniformScaling,Number}...) = $_f(A...) + # if there's a Number present, J::UniformScaling must be 1x1-dimensional + @inline $f(A::Union{AbstractVecOrMat,UniformScaling,Number}...) = $f(map(_us2number, A)...) function $_f(A::Union{AbstractVecOrMat,UniformScaling,Number}...; array_type = promote_to_array_type(A)) n = -1 for a in A From 2709dcf018f7da4087b08aaffebd3790d209ea90 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 8 Mar 2023 18:33:01 +0100 Subject: [PATCH 294/775] rearrange code --- base/array.jl | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/base/array.jl b/base/array.jl index 93335153954f6..333aea83ce7ba 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1916,7 +1916,7 @@ function reverse!(v::AbstractVector, start::Integer, stop::Integer=lastindex(v)) return v end -# concatenations of homogeneous combinations of vectors, horizontal and vertical +# concatenations of (in)homogeneous combinations of vectors, horizontal and vertical vcat() = Vector{Any}() hcat() = Vector{Any}() @@ -1930,9 +1930,7 @@ function hcat(V::Vector{T}...) where T end return [ V[j][i]::T for i=1:length(V[1]), j=1:length(V) ] end -hcat(A::Union{Number,Vector,Matrix}...) = cat(A...; dims=Val(2)) # more special than LinAlg/special.jl -hcat(A::VecOrMat{T}...) where {T} = typed_hcat(T, A...) # more special than LinAlg/special.jl -hcat(A::Vector...) = cat(A...; dims=Val(2)) # more special than SparseArrays's vcat +hcat(A::Vector...) = cat(A...; dims=Val(2)) # more special than SparseArrays's hcat function vcat(arrays::Vector{T}...) where T n = 0 @@ -1950,12 +1948,18 @@ function vcat(arrays::Vector{T}...) where T return arr end vcat(A::Vector...) = cat(A...; dims=Val(1)) # more special than SparseArrays's vcat -vcat(A::Union{Number,Vector,Matrix}...) = cat(A...; dims=Val(1)) # more special than LinAlg/special.jl -vcat(A::VecOrMat{T}...) where {T} = typed_vcat(T, A...) # more special than LinAlg/special.jl # disambiguation with LinAlg/special.jl -hvcat(rows::Tuple{Vararg{Int}}, xs::Union{Number,Vector,Matrix}...) = typed_hvcat(promote_eltypeof(xs...), rows, xs...) -hvcat(rows::Tuple{Vararg{Int}}, xs::VecOrMat{T}...) where {T} = typed_hvcat(T, rows, xs...) +# Union{Number,Vector,Matrix} is for LinearAlgebra._DenseConcatGroup +# VecOrMat{T} is for LinearAlgebra._TypedDenseConcatGroup +hcat(A::Union{Number,Vector,Matrix}...) = cat(A...; dims=Val(2)) +hcat(A::VecOrMat{T}...) where {T} = typed_hcat(T, A...) +vcat(A::Union{Number,Vector,Matrix}...) = cat(A...; dims=Val(1)) +vcat(A::VecOrMat{T}...) where {T} = typed_vcat(T, A...) +hvcat(rows::Tuple{Vararg{Int}}, xs::Union{Number,Vector,Matrix}...) = + typed_hvcat(promote_eltypeof(xs...), rows, xs...) +hvcat(rows::Tuple{Vararg{Int}}, xs::VecOrMat{T}...) where {T} = + typed_hvcat(T, rows, xs...) _cat(n::Integer, x::Integer...) = reshape([x...], (ntuple(Returns(1), n-1)..., length(x))) From c811cf50af6c7b32bee9fec86dd8d0dc9fff78df Mon Sep 17 00:00:00 2001 From: Julian P Samaroo Date: Thu, 2 Mar 2023 09:03:08 -0600 Subject: [PATCH 295/775] Add Base.in_finalizer query --- base/gcutils.jl | 17 +++++++++++++++++ src/gc.c | 5 +++++ src/jl_exported_funcs.inc | 1 + src/julia_threads.h | 1 + test/misc.jl | 15 +++++++++++++++ 5 files changed, 39 insertions(+) diff --git a/base/gcutils.jl b/base/gcutils.jl index a7cee0762bebe..fed30befd7d5c 100644 --- a/base/gcutils.jl +++ b/base/gcutils.jl @@ -162,6 +162,23 @@ function disable_finalizers() @inline ccall(:jl_gc_disable_finalizers_internal, Cvoid, ()) end +""" + GC.in_finalizer()::Bool + +Returns `true` if the current task is running a finalizer, returns `false` +otherwise. Will also return `false` within a finalizer which was inlined by the +compiler's eager finalization optimization, or if `finalize` is called on the +finalizer directly. + +The result of this function may be useful, for example, when a finalizer must +wait on a resource to become available; instead of polling the resource in a +`yield` loop (which is not legal to execute within a task running finalizers), +busy polling or an `@async` continuation could be used instead. +""" +function in_finalizer() @inline + ccall(:jl_gc_is_in_finalizer, Int8, ()) > 0 +end + """ GC.@preserve x1 x2 ... xn expr diff --git a/src/gc.c b/src/gc.c index 88df8cfeb7aa4..3afddc4afb3d8 100644 --- a/src/gc.c +++ b/src/gc.c @@ -488,6 +488,11 @@ JL_DLLEXPORT void jl_gc_enable_finalizers(jl_task_t *ct, int on) } } +JL_DLLEXPORT int8_t jl_gc_is_in_finalizer(void) +{ + return jl_current_task->ptls->in_finalizer; +} + static void schedule_all_finalizers(arraylist_t *flist) JL_NOTSAFEPOINT { void **items = flist->items; diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index b84c3338e401a..d3acb7d2ad92a 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -175,6 +175,7 @@ XX(jl_gc_get_max_memory) \ XX(jl_gc_internal_obj_base_ptr) \ XX(jl_gc_is_enabled) \ + XX(jl_gc_is_in_finalizer) \ XX(jl_gc_live_bytes) \ XX(jl_gc_managed_malloc) \ XX(jl_gc_managed_realloc) \ diff --git a/src/julia_threads.h b/src/julia_threads.h index 719de95c9e375..6439caa0aa2ee 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -365,6 +365,7 @@ JL_DLLEXPORT void jl_gc_disable_finalizers_internal(void); JL_DLLEXPORT void jl_gc_enable_finalizers_internal(void); JL_DLLEXPORT void jl_gc_run_pending_finalizers(struct _jl_task_t *ct); extern JL_DLLEXPORT _Atomic(int) jl_gc_have_pending_finalizers; +JL_DLLEXPORT int8_t jl_gc_is_in_finalizer(void); JL_DLLEXPORT void jl_wakeup_thread(int16_t tid); diff --git a/test/misc.jl b/test/misc.jl index 03a17c84c3c66..c70868552d9c0 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -1386,3 +1386,18 @@ end @test (@allocations "a") == 0 @test (@allocations "a" * "b") == 1 end + +@testset "in_finalizer" begin + @test !GC.in_finalizer() + + in_fin = Ref{Any}() + wait(@async begin + r = Ref(1) + finalizer(r) do _ + in_fin[] = GC.in_finalizer() + end + nothing + end) + GC.gc(true); yield() + @test in_fin[] +end From 33593886391d8a4eb911974f328e4c515705bc11 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Wed, 8 Mar 2023 13:12:23 -0500 Subject: [PATCH 296/775] Use module's triple in dump_native --- src/aotcompile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 2e64362599d7a..d00cf143a4a7d 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1445,8 +1445,8 @@ void jl_dump_native_impl(void *native_code, // We don't want to use MCJIT's target machine because // it uses the large code model and we may potentially // want less optimizations there. - Triple TheTriple = Triple(jl_ExecutionEngine->getTargetTriple()); // make sure to emit the native object format, even if FORCE_ELF was set in codegen + Triple TheTriple(data->M.getModuleUnlocked()->getTargetTriple()); if (TheTriple.isOSWindows()) { TheTriple.setObjectFormat(Triple::COFF); } else if (TheTriple.isOSDarwin()) { From 3f7c0467c79d7da6b52a609c99e78127bb2b76a6 Mon Sep 17 00:00:00 2001 From: Morten Piibeleht Date: Thu, 9 Mar 2023 08:27:06 +1300 Subject: [PATCH 297/775] Add compat for set_active_project (#48947) --- base/initdefs.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/base/initdefs.jl b/base/initdefs.jl index 97a67c88fe713..89ebecaefbdc4 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -315,6 +315,9 @@ end set_active_project(projfile::Union{AbstractString,Nothing}) Set the active `Project.toml` file to `projfile`. See also [`Base.active_project`](@ref). + +!!! compat "Julia 1.8" + This function requires at least Julia 1.8. """ function set_active_project(projfile::Union{AbstractString,Nothing}) ACTIVE_PROJECT[] = projfile From 4da03592d120ce5f8f7ca4ce524da28b2353d727 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 9 Mar 2023 05:30:47 +0800 Subject: [PATCH 298/775] Fix `Vararg` methods widening with unused type variable (#48953) Avoid possible nested `Vararg`. Fix #48950 --- src/gf.c | 3 +-- test/core.jl | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/gf.c b/src/gf.c index 357487d723e9b..c57afa192216a 100644 --- a/src/gf.c +++ b/src/gf.c @@ -642,8 +642,7 @@ static jl_value_t *inst_varargp_in_env(jl_value_t *decl, jl_svec_t *sparams) int T_has_tv = T && jl_has_typevar(T, v); int N_has_tv = N && jl_has_typevar(N, v); // n.b. JL_VARARG_UNBOUND check means this should be false assert(!N_has_tv || N == (jl_value_t*)v); - if (T_has_tv) - vm = jl_type_unionall(v, T); + vm = T_has_tv ? jl_type_unionall(v, T) : T; if (N_has_tv) N = NULL; vm = (jl_value_t*)jl_wrap_vararg(vm, N); // this cannot throw for these inputs diff --git a/test/core.jl b/test/core.jl index 8af7421ba7501..f3ab922868a53 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7973,3 +7973,6 @@ let spec = only(methods(g47476)).specializations @test any(mi -> mi !== nothing && Base.isvatuple(mi.specTypes), spec) @test all(mi -> mi === nothing || !Base.has_free_typevars(mi.specTypes), spec) end + +f48950(::Union{Int,d}, ::Union{c,Nothing}...) where {c,d} = 1 +@test f48950(1, 1, 1) == 1 From 78ec99b585c13bad56668162434e4266b85cca7e Mon Sep 17 00:00:00 2001 From: Tomas Chor Date: Wed, 8 Mar 2023 18:33:22 -0800 Subject: [PATCH 299/775] =?UTF-8?q?Add=20a=20`fourthroot`=20function=20wit?= =?UTF-8?q?h=20unicode=20symbol=20=E2=88=9C=20(#48899)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * adds fourthroot function --- NEWS.md | 2 ++ base/Base.jl | 1 + base/exports.jl | 2 ++ base/math.jl | 12 ++++++++++-- test/math.jl | 24 +++++++++++++++++++++++- 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 91b47d832eb29..72f1a39428206 100644 --- a/NEWS.md +++ b/NEWS.md @@ -27,6 +27,8 @@ Build system changes New library functions --------------------- * `tanpi` is now defined. It computes tan(πx) more accurately than `tan(pi*x)` ([#48575]). +* `fourthroot(x)` is now defined in `Base.Math` and can be used to compute the fourth root of `x`. + It can also be accessed using the unicode character `∜`, which can be typed by `\fourthroot` ([#48899]). New library features -------------------- diff --git a/base/Base.jl b/base/Base.jl index 85a9c8d5048e3..789fb7c0da05c 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -342,6 +342,7 @@ include("math.jl") using .Math const (√)=sqrt const (∛)=cbrt +const (∜)=fourthroot # now switch to a simple, race-y TLS, relative include for the rest of Base delete_method(which(include, (Module, String))) diff --git a/base/exports.jl b/base/exports.jl index 26b24b85651a2..ec151df0bfde2 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -238,6 +238,7 @@ export bitrotate, bswap, cbrt, + fourthroot, ceil, cis, cispi, @@ -364,6 +365,7 @@ export zero, √, ∛, + ∜, ≈, ≉, diff --git a/base/math.jl b/base/math.jl index 88691a5686c17..3394ab1e2f436 100644 --- a/base/math.jl +++ b/base/math.jl @@ -10,7 +10,7 @@ export sin, cos, sincos, tan, sinh, cosh, tanh, asin, acos, atan, acosd, acotd, acscd, asecd, asind, atand, rad2deg, deg2rad, log, log2, log10, log1p, exponent, exp, exp2, exp10, expm1, - cbrt, sqrt, significand, + cbrt, sqrt, fourthroot, significand, hypot, max, min, minmax, ldexp, frexp, clamp, clamp!, modf, ^, mod2pi, rem2pi, @evalpoly, evalpoly @@ -715,6 +715,13 @@ julia> .√(1:4) """ sqrt(x) +""" + fourthroot(x) + +Return the fourth root of `x` by applying `sqrt` twice successively. +""" +fourthroot(x::Number) = sqrt(sqrt(x)) + """ hypot(x, y) @@ -1537,7 +1544,7 @@ include("special/log.jl") # Float16 definitions for func in (:sin,:cos,:tan,:asin,:acos,:atan,:cosh,:tanh,:asinh,:acosh, - :atanh,:log,:log2,:log10,:sqrt,:log1p) + :atanh,:log,:log2,:log10,:sqrt,:fourthroot,:log1p) @eval begin $func(a::Float16) = Float16($func(Float32(a))) $func(a::ComplexF16) = ComplexF16($func(ComplexF32(a))) @@ -1573,5 +1580,6 @@ end exp2(x::AbstractFloat) = 2^x exp10(x::AbstractFloat) = 10^x clamp(::Missing, lo, hi) = missing +fourthroot(::Missing) = missing end # module diff --git a/test/math.jl b/test/math.jl index 9dd634da2b4f3..19d9f7893a496 100644 --- a/test/math.jl +++ b/test/math.jl @@ -180,6 +180,7 @@ end @test atan(x,y) ≈ atan(big(x),big(y)) @test atanh(x) ≈ atanh(big(x)) @test cbrt(x) ≈ cbrt(big(x)) + @test fourthroot(x) ≈ fourthroot(big(x)) @test cos(x) ≈ cos(big(x)) @test cosh(x) ≈ cosh(big(x)) @test cospi(x) ≈ cospi(big(x)) @@ -219,6 +220,9 @@ end @test isequal(cbrt(T(0)), T(0)) @test isequal(cbrt(T(1)), T(1)) @test isequal(cbrt(T(1000000000))^3, T(1000)^3) + @test isequal(fourthroot(T(0)), T(0)) + @test isequal(fourthroot(T(1)), T(1)) + @test isequal(fourthroot(T(100000000))^4, T(100)^4) @test isequal(cos(T(0)), T(1)) @test cos(T(pi)/2) ≈ T(0) atol=eps(T) @test isequal(cos(T(pi)), T(-1)) @@ -271,6 +275,8 @@ end @test asin(sin(x)) ≈ x @test cbrt(x)^3 ≈ x @test cbrt(x^3) ≈ x + @test fourthroot(x)^4 ≈ x + @test fourthroot(x^4) ≈ x @test asinh(sinh(x)) ≈ x @test atan(tan(x)) ≈ x @test atan(x,y) ≈ atan(x/y) @@ -1255,6 +1261,22 @@ end end end +@testset "fourthroot" begin + for T in (Float32, Float64) + @test fourthroot(zero(T)) === zero(T) + @test fourthroot(one(T)) === one(T) + @test fourthroot(T(Inf)) === T(Inf) + @test isnan_type(T, fourthroot(T(NaN))) + for x in (pcnfloat(nextfloat(nextfloat(zero(T))))..., + 0.45, 0.6, 0.98, + map(x->x^3, 1.0:1.0:1024.0)..., + prevfloat(T(Inf))) + by = fourthroot(big(T(x))) + @test fourthroot(T(x)) ≈ by rtol=eps(T) + end + end +end + @testset "hypot" begin @test hypot(0, 0) == 0.0 @test hypot(3, 4) == 5.0 @@ -1519,7 +1541,7 @@ end end # test constant-foldability -for fn in (:sin, :cos, :tan, :log, :log2, :log10, :log1p, :exponent, :sqrt, :cbrt, +for fn in (:sin, :cos, :tan, :log, :log2, :log10, :log1p, :exponent, :sqrt, :cbrt, :fourthroot, :asin, :atan, :acos, :sinh, :cosh, :tanh, :asinh, :acosh, :atanh, :exp, :exp2, :exp10, :expm1 ) From 2ed1cfeb01204a1997182cad0a7f268bf8320f3d Mon Sep 17 00:00:00 2001 From: Tianyi Pu <44583944+putianyi889@users.noreply.github.com> Date: Thu, 9 Mar 2023 14:26:26 +0000 Subject: [PATCH 300/775] add a Diagonal constructor and a conversion (#48895) * add a Diagonal constructor and a conversion Co-authored-by: woclass --- stdlib/LinearAlgebra/src/diagonal.jl | 5 +++++ stdlib/LinearAlgebra/test/diagonal.jl | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 9bb7219965d5d..df37e8935d2af 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -96,6 +96,11 @@ julia> diag(A, 2) ``` """ Diagonal(A::AbstractMatrix) = Diagonal(diag(A)) +Diagonal{T}(A::AbstractMatrix) where T = Diagonal{T}(diag(A)) +function convert(::Type{T}, A::AbstractMatrix) where T<:Diagonal + checksquare(A) + isdiag(A) ? T(A) : throw(InexactError(:convert, T, A)) +end Diagonal(D::Diagonal) = D Diagonal{T}(D::Diagonal{T}) where {T} = D diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index f57578b42bf9c..bcfed234a51ee 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -43,11 +43,14 @@ Random.seed!(1) end @test eltype(Diagonal{elty}([1,2,3,4])) == elty @test isa(Diagonal{elty,Vector{elty}}(GenericArray([1,2,3,4])), Diagonal{elty,Vector{elty}}) + @test isa(Diagonal{elty}(rand(Int,n,n)), Diagonal{elty,Vector{elty}}) DI = Diagonal([1,2,3,4]) @test Diagonal(DI) === DI @test isa(Diagonal{elty}(DI), Diagonal{elty}) # issue #26178 - @test_throws MethodError convert(Diagonal, [1, 2, 3, 4]) + @test_throws MethodError convert(Diagonal, [1,2,3,4]) + @test_throws DimensionMismatch convert(Diagonal, [1 2 3 4]) + @test_throws InexactError convert(Diagonal, ones(2,2)) end @testset "Basic properties" begin From 5b2240cd321a1e12bc921ab1fc833ee28235ff5c Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Thu, 9 Mar 2023 17:07:22 +0100 Subject: [PATCH 301/775] Show rationals without `//1` when typeinfo <: Rational (#45396) Co-authored-by: Jeff Bezanson Co-authored-by: Elliot Saba Co-authored-by: Lilith Orion Hafner --- NEWS.md | 1 + base/rational.jl | 5 +++++ test/rational.jl | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/NEWS.md b/NEWS.md index 72f1a39428206..c96d1a2365410 100644 --- a/NEWS.md +++ b/NEWS.md @@ -42,6 +42,7 @@ Standard library changes ------------------------ * `startswith` now supports seekable `IO` streams ([#43055]) +* printing integral `Rational`s will skip the denominator in `Rational`-typed IO context (e.g. in `Arrays`) ([#45396]) #### Package Manager diff --git a/base/rational.jl b/base/rational.jl index 26746ad0b4bc2..3cfb038ab3b38 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -83,6 +83,11 @@ end function show(io::IO, x::Rational) show(io, numerator(x)) + + if isone(denominator(x)) && get(io, :typeinfo, Any) <: Rational + return + end + print(io, "//") show(io, denominator(x)) end diff --git a/test/rational.jl b/test/rational.jl index 9f47f2cb9dd16..a0833d08eb218 100644 --- a/test/rational.jl +++ b/test/rational.jl @@ -253,6 +253,10 @@ end rational2 = Rational(-4500, 9000) @test sprint(show, rational1) == "1465//8593" @test sprint(show, rational2) == "-1//2" + @test sprint(show, -2//2) == "-1//1" + @test sprint(show, [-2//2,]) == "Rational{$Int}[-1]" + @test sprint(show, MIME"text/plain"(), Union{Int, Rational{Int}}[7 3//6; 6//3 2]) == + "2×2 Matrix{Union{Rational{$Int}, $Int}}:\n 7 1//2\n 2//1 2" let io1 = IOBuffer() write(io1, rational1) From 386b1b67d7817a8125eaac5c6c2330dbd663bc5e Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 9 Mar 2023 12:16:57 -0500 Subject: [PATCH 302/775] gf: add support for invalidating invoke edges (#48954) Apparently we never actually implemented support for invalidation detection on invoke edges, and furthermore, it had erased part of the support for regular edges. Generalize our code for detecting replacement of a method, to be used when computing replacement of an invoke edge. Fix #48802 --- src/gf.c | 109 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/src/gf.c b/src/gf.c index c57afa192216a..092e17cd1bcf5 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1816,6 +1816,36 @@ static int jl_type_intersection2(jl_value_t *t1, jl_value_t *t2, jl_value_t **is return 1; } +enum morespec_options { + morespec_unknown, + morespec_isnot, + morespec_is +}; + +// check if `type` is replacing `m` with an ambiguity here, given other methods in `d` that already match it +// precondition: type is not more specific than `m` +static int is_replacing(jl_value_t *type, jl_method_t *m, jl_method_t *const *d, size_t n, jl_value_t *isect, jl_value_t *isect2, char *morespec) +{ + size_t k; + for (k = 0; k < n; k++) { + jl_method_t *m2 = d[k]; + // see if m2 also fully covered this intersection + if (m == m2 || !(jl_subtype(isect, m2->sig) || (isect2 && jl_subtype(isect2, m2->sig)))) + continue; + if (morespec[k] == (char)morespec_unknown) + morespec[k] = (char)(jl_type_morespecific(m2->sig, type) ? morespec_is : morespec_isnot); + if (morespec[k] == (char)morespec_is) + // not actually shadowing this--m2 will still be better + return 0; + // since m2 was also a previous match over isect, + // see if m was also previously dominant over all m2 + if (!jl_type_morespecific(m->sig, m2->sig)) + // m and m2 were previously ambiguous over the full intersection of mi with type, and will still be ambiguous with type + return 0; + } + return 1; +} + JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method, jl_tupletype_t *simpletype) { JL_TIMING(ADD_METHOD); @@ -1850,7 +1880,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method oldvalue = get_intersect_matches(jl_atomic_load_relaxed(&mt->defs), newentry); int invalidated = 0; - jl_method_t **d; + jl_method_t *const *d; size_t j, n; if (oldvalue == NULL) { d = NULL; @@ -1879,6 +1909,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method // -> less specific or ambiguous with any one of them: can ignore the missing edge (not missing) // -> some may have been ambiguous: still are // -> some may have been called: they may be partly replaced (will be detected in the loop later) + // c.f. `is_replacing`, which is a similar query, but with an existing method match to compare against missing = 1; size_t j; for (j = 0; j < n; j++) { @@ -1913,11 +1944,6 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method } if (oldvalue) { oldmi = jl_alloc_vec_any(0); - enum morespec_options { - morespec_unknown, - morespec_isnot, - morespec_is - }; char *morespec = (char*)alloca(n); memset(morespec, morespec_unknown, n); for (j = 0; j < n; j++) { @@ -1934,6 +1960,11 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method continue; isect3 = jl_type_intersection(m->sig, (jl_value_t*)mi->specTypes); if (jl_type_intersection2(type, isect3, &isect, &isect2)) { + // TODO: this only checks pair-wise for ambiguities, but the ambiguities could arise from the interaction of multiple methods + // and thus might miss a case where we introduce an ambiguity between two existing methods + // We could instead work to sort this into 3 groups `morespecific .. ambiguous .. lesspecific`, with `type` in ambiguous, + // such that everything in `morespecific` dominates everything in `ambiguous`, and everything in `ambiguous` dominates everything in `lessspecific` + // And then compute where each isect falls, and whether it changed group--necessitating invalidation--or not. if (morespec[j] == (char)morespec_unknown) morespec[j] = (char)(jl_type_morespecific(m->sig, type) ? morespec_is : morespec_isnot); if (morespec[j] == (char)morespec_is) @@ -1942,62 +1973,38 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method if (ambig == morespec_unknown) ambig = jl_type_morespecific(type, m->sig) ? morespec_is : morespec_isnot; // replacing a method--see if this really was the selected method previously - // over the intersection - if (ambig == morespec_isnot) { - size_t k; - for (k = 0; k < n; k++) { - jl_method_t *m2 = d[k]; - if (m == m2 || !(jl_subtype(isect, m2->sig) || (isect && jl_subtype(isect, m2->sig)))) - continue; - if (morespec[k] == (char)morespec_unknown) - morespec[k] = (char)(jl_type_morespecific(m2->sig, type) ? morespec_is : morespec_isnot); - if (morespec[k] == (char)morespec_is) - // not actually shadowing this--m2 will still be better - break; - // since m2 was also a previous match over isect, - // see if m was also previously dominant over all m2 - if (!jl_type_morespecific(m->sig, m2->sig)) - break; - } - if (k != n) - continue; - } - // Before deciding whether to invalidate `mi`, check each backedge for `invoke`s - if (mi->backedges) { - jl_array_t *backedges = mi->backedges; + // over the intersection (not ambiguous) and the new method will be selected now (morespec_is) + int replaced_dispatch = ambig == morespec_is || is_replacing(type, m, d, n, isect, isect2, morespec); + // found that this specialization dispatch got replaced by m + // call invalidate_backedges(&do_nothing_with_codeinst, mi, max_world, "jl_method_table_insert"); + // but ignore invoke-type edges + jl_array_t *backedges = mi->backedges; + if (backedges) { size_t ib = 0, insb = 0, nb = jl_array_len(backedges); jl_value_t *invokeTypes; jl_method_instance_t *caller; while (ib < nb) { ib = get_next_edge(backedges, ib, &invokeTypes, &caller); - if (!invokeTypes) { - // ordinary dispatch, invalidate + int replaced_edge; + if (invokeTypes) { + // n.b. normally we must have mi.specTypes <: invokeTypes <: m.sig (though it might not strictly hold), so we only need to check the other subtypes + replaced_edge = jl_subtype(invokeTypes, type) && (ambig == morespec_is || is_replacing(type, m, d, n, invokeTypes, NULL, morespec)); + } + else { + replaced_edge = replaced_dispatch; + } + if (replaced_edge) { invalidate_method_instance(&do_nothing_with_codeinst, caller, max_world, 1); invalidated = 1; - } else { - // invoke-dispatch, check invokeTypes for validity - struct jl_typemap_assoc search = {invokeTypes, method->primary_world, NULL, 0, ~(size_t)0}; - oldentry = jl_typemap_assoc_by_type(jl_atomic_load_relaxed(&mt->defs), &search, /*offs*/0, /*subtype*/0); - if (oldentry && oldentry->func.method == mi->def.method) { - // We can safely keep this method - jl_array_ptr_set(backedges, insb++, invokeTypes); - jl_array_ptr_set(backedges, insb++, caller); - } else { - invalidate_method_instance(&do_nothing_with_codeinst, caller, max_world, 1); - invalidated = 1; - } + } + else { + insb = set_next_edge(backedges, insb, invokeTypes, caller); } } jl_array_del_end(backedges, nb - insb); } - if (!mi->backedges || jl_array_len(mi->backedges) == 0) { - jl_array_ptr_1d_push(oldmi, (jl_value_t*)mi); - invalidate_external(mi, max_world); - if (mi->backedges) { - invalidated = 1; - invalidate_backedges(&do_nothing_with_codeinst, mi, max_world, "jl_method_table_insert"); - } - } + jl_array_ptr_1d_push(oldmi, (jl_value_t*)mi); + invalidate_external(mi, max_world); } } } From 162b9e900bb0ec3bc32527dae9a0983743242766 Mon Sep 17 00:00:00 2001 From: Alexander Plavin Date: Fri, 10 Mar 2023 10:58:29 +0300 Subject: [PATCH 303/775] more consistent findall output (#45538) fix #45495 --- base/array.jl | 6 +++++- test/functional.jl | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/base/array.jl b/base/array.jl index 333aea83ce7ba..eab043a6439a9 100644 --- a/base/array.jl +++ b/base/array.jl @@ -2352,7 +2352,11 @@ julia> findall(x -> x >= 0, d) ``` """ -findall(testf::Function, A) = collect(first(p) for p in pairs(A) if testf(last(p))) +function findall(testf::Function, A) + T = eltype(keys(A)) + gen = (first(p) for p in pairs(A) if testf(last(p))) + isconcretetype(T) ? collect(T, gen) : collect(gen) +end # Broadcasting is much faster for small testf, and computing # integer indices from logical index using findall has a negligible cost diff --git a/test/functional.jl b/test/functional.jl index c9b0b270baeb7..19355d13ff335 100644 --- a/test/functional.jl +++ b/test/functional.jl @@ -139,6 +139,13 @@ end @test findall(!iszero, x^2 for x in -1:0.5:1) == [1, 2, 4, 5] @test argmin(x^2 for x in -1:0.5:1) == 3 +# findall return type, see #45495 +let gen = (i for i in 1:3); + @test @inferred(findall(x -> true, gen))::Vector{Int} == [1, 2, 3] + @test @inferred(findall(x -> false, gen))::Vector{Int} == Int[] + @test @inferred(findall(x -> x < 0, gen))::Vector{Int} == Int[] +end + # inference on vararg generator of a type (see #22907 comments) let f(x) = collect(Base.Generator(=>, x, x)) @test @inferred(f((1,2))) == [1=>1, 2=>2] From ba1d5656a248b55e63997ed3f4d86b131d135f83 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Fri, 10 Mar 2023 12:22:38 -0500 Subject: [PATCH 304/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?SparseArrays=20stdlib=20from=204aab8df=20to=208affe9e=20(#48965?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dilum Aluthge --- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + stdlib/SparseArrays.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/SparseArrays-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/md5 delete mode 100644 deps/checksums/SparseArrays-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/sha512 create mode 100644 deps/checksums/SparseArrays-8affe9e499379616e33fc60a24bb31500e8423d7.tar.gz/md5 create mode 100644 deps/checksums/SparseArrays-8affe9e499379616e33fc60a24bb31500e8423d7.tar.gz/sha512 diff --git a/deps/checksums/SparseArrays-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/md5 b/deps/checksums/SparseArrays-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/md5 deleted file mode 100644 index 7f3de8bd0abc7..0000000000000 --- a/deps/checksums/SparseArrays-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -1276e70aa3fa0fcad3c000d4d1602d5b diff --git a/deps/checksums/SparseArrays-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/sha512 b/deps/checksums/SparseArrays-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/sha512 deleted file mode 100644 index 121570ff159ab..0000000000000 --- a/deps/checksums/SparseArrays-4aab8df5d3f0f19f16de52e92023c2971e60f9f9.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -44ae6bb25f605749e4fd1b3a7a9e10c01a4506396e099f54a46cd922858642bce5a2ce7ecc650e0470a17d5d35ab66054a67df6e28c0524bd16d39e3d7c01fc2 diff --git a/deps/checksums/SparseArrays-8affe9e499379616e33fc60a24bb31500e8423d7.tar.gz/md5 b/deps/checksums/SparseArrays-8affe9e499379616e33fc60a24bb31500e8423d7.tar.gz/md5 new file mode 100644 index 0000000000000..96861ba265b5f --- /dev/null +++ b/deps/checksums/SparseArrays-8affe9e499379616e33fc60a24bb31500e8423d7.tar.gz/md5 @@ -0,0 +1 @@ +e6dc511b49e07a167848adc4e12690d8 diff --git a/deps/checksums/SparseArrays-8affe9e499379616e33fc60a24bb31500e8423d7.tar.gz/sha512 b/deps/checksums/SparseArrays-8affe9e499379616e33fc60a24bb31500e8423d7.tar.gz/sha512 new file mode 100644 index 0000000000000..f503304f810e4 --- /dev/null +++ b/deps/checksums/SparseArrays-8affe9e499379616e33fc60a24bb31500e8423d7.tar.gz/sha512 @@ -0,0 +1 @@ +f40fd137ccd6651fc8b697f57cfcbd8e3feccb99f6a6b32fbaa69cc0160b78cefc662b914ff8f4e48478ca48f9583318a6030d922d43ed66f8db59fd5985f768 diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version index 9c9f2d4f77a88..d4a548daef5d7 100644 --- a/stdlib/SparseArrays.version +++ b/stdlib/SparseArrays.version @@ -1,4 +1,4 @@ SPARSEARRAYS_BRANCH = main -SPARSEARRAYS_SHA1 = 4aab8df5d3f0f19f16de52e92023c2971e60f9f9 +SPARSEARRAYS_SHA1 = 8affe9e499379616e33fc60a24bb31500e8423d7 SPARSEARRAYS_GIT_URL := https://github.com/JuliaSparse/SparseArrays.jl.git SPARSEARRAYS_TAR_URL = https://api.github.com/repos/JuliaSparse/SparseArrays.jl/tarball/$1 From ad304ea490a50c7a38a36ea01db8f8b7c00aeb8d Mon Sep 17 00:00:00 2001 From: Ujjwal Sarswat <76774914+vmpyr@users.noreply.github.com> Date: Fri, 10 Mar 2023 22:57:33 +0530 Subject: [PATCH 305/775] add overflow checking for `abs(::Rational)` (#48912) --- base/rational.jl | 4 ++-- test/hashing.jl | 3 +++ test/rational.jl | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/base/rational.jl b/base/rational.jl index 3cfb038ab3b38..6ab022736388e 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -277,7 +277,7 @@ signbit(x::Rational) = signbit(x.num) copysign(x::Rational, y::Real) = unsafe_rational(copysign(x.num, y), x.den) copysign(x::Rational, y::Rational) = unsafe_rational(copysign(x.num, y.num), x.den) -abs(x::Rational) = Rational(abs(x.num), x.den) +abs(x::Rational) = unsafe_rational(checked_abs(x.num), x.den) typemin(::Type{Rational{T}}) where {T<:Signed} = unsafe_rational(T, -one(T), zero(T)) typemin(::Type{Rational{T}}) where {T<:Integer} = unsafe_rational(T, zero(T), one(T)) @@ -545,7 +545,7 @@ function hash(x::Rational{<:BitInteger64}, h::UInt) pow = trailing_zeros(den) den >>= pow pow = -pow - if den == 1 && abs(num) < 9007199254740992 + if den == 1 && uabs(num) < UInt64(maxintfloat(Float64)) return hash(ldexp(Float64(num),pow),h) end end diff --git a/test/hashing.jl b/test/hashing.jl index 0266b2f06e168..b672c3de817c6 100644 --- a/test/hashing.jl +++ b/test/hashing.jl @@ -60,6 +60,9 @@ end @test hash(nextfloat(2.0^63)) == hash(UInt64(nextfloat(2.0^63))) @test hash(prevfloat(2.0^64)) == hash(UInt64(prevfloat(2.0^64))) +# issue #48744 +@test hash(typemin(Int)//1) === hash(big(typemin(Int)//1)) + # issue #9264 @test hash(1//6,zero(UInt)) == invoke(hash, Tuple{Real, UInt}, 1//6, zero(UInt)) @test hash(1//6) == hash(big(1)//big(6)) diff --git a/test/rational.jl b/test/rational.jl index a0833d08eb218..a1af6eda64516 100644 --- a/test/rational.jl +++ b/test/rational.jl @@ -269,6 +269,9 @@ end @test read(io2, typeof(rational2)) == rational2 end end +@testset "abs overflow for Rational" begin + @test_throws OverflowError abs(typemin(Int) // 1) +end @testset "parse" begin # Non-negative Int in which parsing is expected to work @test parse(Rational{Int}, string(10)) == 10 // 1 From fd5b5e38e014c9bc3a37d51b489990d09f7b8e84 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 10 Mar 2023 17:28:54 -0500 Subject: [PATCH 306/775] Fix CFG corruption in CFG simplify (#48962) IncrementalCompact ordinarily takes ownership of the CFG in order to to its transform. cfg_simplify! separate constructs the CFG transform structures ahead of time and was assuming this meant that the original CFG remained untouched (since it was using it for lookup operations). Unfortunately, the IncrementalCompact constructor was already doing some CFG manipulation cuasing the CFG to be corrupted and cfg_simplify! to create invalid IR. Fix that by refactoring the IncrementalCompact constructor to allow passing in the CFG transformation state explicitly, rather than poking it into the fields afterwards. --- base/compiler/ssair/inlining.jl | 7 +- base/compiler/ssair/ir.jl | 174 ++++++++++++++++++-------------- base/compiler/ssair/passes.jl | 6 +- base/compiler/ssair/show.jl | 2 +- test/compiler/ssair.jl | 2 +- 5 files changed, 102 insertions(+), 89 deletions(-) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 79f8c1ad51b10..c72293911814d 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -596,7 +596,7 @@ function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, case = case::ConstantCase val = case.val end - if !isempty(compact.result_bbs[bb].preds) + if !isempty(compact.cfg_transform.result_bbs[bb].preds) push!(pn.edges, bb) push!(pn.values, val) insert_node_here!(compact, @@ -648,8 +648,7 @@ function batch_inline!(ir::IRCode, todo::Vector{Pair{Int,Any}}, propagate_inboun boundscheck = :propagate end - let compact = IncrementalCompact(ir, false) - compact.result_bbs = state.new_cfg_blocks + let compact = IncrementalCompact(ir, CFGTransformState!(state.new_cfg_blocks, false)) # This needs to be a minimum and is more of a size hint nn = 0 for (_, item) in todo @@ -670,7 +669,7 @@ function batch_inline!(ir::IRCode, todo::Vector{Pair{Int,Any}}, propagate_inboun argexprs = copy(stmt.args) end refinish = false - if compact.result_idx == first(compact.result_bbs[compact.active_result_bb].stmts) + if compact.result_idx == first(compact.cfg_transform.result_bbs[compact.active_result_bb].stmts) compact.active_result_bb -= 1 refinish = true end diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index 56fac0e3cc1a7..b1b7beec3f3c5 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -562,14 +562,60 @@ end insert_node!(ir::IRCode, pos::Int, newinst::NewInstruction, attach_after::Bool=false) = insert_node!(ir, SSAValue(pos), newinst, attach_after) +struct CFGTransformState + cfg_transforms_enabled::Bool + fold_constant_branches::Bool + result_bbs::Vector{BasicBlock} + bb_rename_pred::Vector{Int} + bb_rename_succ::Vector{Int} +end + +# N.B.: Takes ownership of the CFG array +function CFGTransformState!(blocks::Vector{BasicBlock}, allow_cfg_transforms::Bool=false) + if allow_cfg_transforms + bb_rename = Vector{Int}(undef, length(blocks)) + cur_bb = 1 + domtree = construct_domtree(blocks) + for i = 1:length(bb_rename) + if bb_unreachable(domtree, i) + bb_rename[i] = -1 + else + bb_rename[i] = cur_bb + cur_bb += 1 + end + end + for i = 1:length(bb_rename) + bb_rename[i] == -1 && continue + preds, succs = blocks[i].preds, blocks[i].succs + # Rename preds + for j = 1:length(preds) + if preds[j] != 0 + preds[j] = bb_rename[preds[j]] + end + end + # Dead blocks get removed from the predecessor list + filter!(x->x !== -1, preds) + # Rename succs + for j = 1:length(succs) + succs[j] = bb_rename[succs[j]] + end + end + let blocks = blocks, bb_rename = bb_rename + result_bbs = BasicBlock[blocks[i] for i = 1:length(blocks) if bb_rename[i] != -1] + end + else + bb_rename = Vector{Int}() + result_bbs = blocks + end + return CFGTransformState(allow_cfg_transforms, allow_cfg_transforms, result_bbs, bb_rename, bb_rename) +end + mutable struct IncrementalCompact ir::IRCode result::InstructionStream - result_bbs::Vector{BasicBlock} + cfg_transform::CFGTransformState ssa_rename::Vector{Any} - bb_rename_pred::Vector{Int} - bb_rename_succ::Vector{Int} used_ssas::Vector{Int} late_fixup::Vector{Int} @@ -587,10 +633,8 @@ mutable struct IncrementalCompact active_bb::Int active_result_bb::Int renamed_new_nodes::Bool - cfg_transforms_enabled::Bool - fold_constant_branches::Bool - function IncrementalCompact(code::IRCode, allow_cfg_transforms::Bool=false) + function IncrementalCompact(code::IRCode, cfg_transform::CFGTransformState) # Sort by position with attach after nodes after regular ones info = code.new_nodes.info perm = sort!(collect(eachindex(info)); by=i->(2info[i].pos+info[i].attach_after, i)) @@ -599,49 +643,14 @@ mutable struct IncrementalCompact used_ssas = fill(0, new_len) new_new_used_ssas = Vector{Int}() blocks = code.cfg.blocks - if allow_cfg_transforms - bb_rename = Vector{Int}(undef, length(blocks)) - cur_bb = 1 - domtree = construct_domtree(blocks) - for i = 1:length(bb_rename) - if bb_unreachable(domtree, i) - bb_rename[i] = -1 - else - bb_rename[i] = cur_bb - cur_bb += 1 - end - end - for i = 1:length(bb_rename) - bb_rename[i] == -1 && continue - preds, succs = blocks[i].preds, blocks[i].succs - # Rename preds - for j = 1:length(preds) - if preds[j] != 0 - preds[j] = bb_rename[preds[j]] - end - end - # Dead blocks get removed from the predecessor list - filter!(x->x !== -1, preds) - # Rename succs - for j = 1:length(succs) - succs[j] = bb_rename[succs[j]] - end - end - let blocks = blocks, bb_rename = bb_rename - result_bbs = BasicBlock[blocks[i] for i = 1:length(blocks) if bb_rename[i] != -1] - end - else - bb_rename = Vector{Int}() - result_bbs = code.cfg.blocks - end ssa_rename = Any[SSAValue(i) for i = 1:new_len] late_fixup = Vector{Int}() new_new_nodes = NewNodeStream() pending_nodes = NewNodeStream() pending_perm = Int[] - return new(code, result, result_bbs, ssa_rename, bb_rename, bb_rename, used_ssas, late_fixup, perm, 1, + return new(code, result, cfg_transform, ssa_rename, used_ssas, late_fixup, perm, 1, new_new_nodes, new_new_used_ssas, pending_nodes, pending_perm, - 1, 1, 1, 1, false, allow_cfg_transforms, allow_cfg_transforms) + 1, 1, 1, 1, false) end # For inlining @@ -653,14 +662,18 @@ mutable struct IncrementalCompact bb_rename = Vector{Int}() pending_nodes = NewNodeStream() pending_perm = Int[] - return new(code, parent.result, - parent.result_bbs, ssa_rename, bb_rename, bb_rename, parent.used_ssas, + return new(code, parent.result, CFGTransformState(false, false, parent.cfg_transform.result_bbs, bb_rename, bb_rename), + ssa_rename, parent.used_ssas, parent.late_fixup, perm, 1, parent.new_new_nodes, parent.new_new_used_ssas, pending_nodes, pending_perm, - 1, result_offset, 1, parent.active_result_bb, false, false, false) + 1, result_offset, 1, parent.active_result_bb, false) end end +function IncrementalCompact(code::IRCode, allow_cfg_transforms::Bool=false) + return IncrementalCompact(code, CFGTransformState!(code.cfg.blocks, allow_cfg_transforms)) +end + struct TypesView{T} ir::T # ::Union{IRCode, IncrementalCompact} end @@ -698,7 +711,7 @@ end function block_for_inst(compact::IncrementalCompact, idx::SSAValue) id = idx.id if id < compact.result_idx # if ssa within result - return searchsortedfirst(compact.result_bbs, BasicBlock(StmtRange(id, id)), + return searchsortedfirst(compact.cfg_transform.result_bbs, BasicBlock(StmtRange(id, id)), 1, compact.active_result_bb, bb_ordering())-1 else return block_for_inst(compact.ir.cfg, id) @@ -883,9 +896,10 @@ function insert_node_here!(compact::IncrementalCompact, newinst::NewInstruction, newline = newinst.line::Int32 refinish = false result_idx = compact.result_idx + result_bbs = compact.cfg_transform.result_bbs if reverse_affinity && - ((compact.active_result_bb == length(compact.result_bbs) + 1) || - result_idx == first(compact.result_bbs[compact.active_result_bb].stmts)) + ((compact.active_result_bb == length(result_bbs) + 1) || + result_idx == first(result_bbs[compact.active_result_bb].stmts)) compact.active_result_bb -= 1 refinish = true end @@ -1173,18 +1187,19 @@ function kill_edge!(compact::IncrementalCompact, active_bb::Int, from::Int, to:: # Note: We recursively kill as many edges as are obviously dead. However, this # may leave dead loops in the IR. We kill these later in a CFG cleanup pass (or # worstcase during codegen). - preds = compact.result_bbs[compact.bb_rename_succ[to]].preds - succs = compact.result_bbs[compact.bb_rename_pred[from]].succs - deleteat!(preds, findfirst(x->x === compact.bb_rename_pred[from], preds)::Int) - deleteat!(succs, findfirst(x->x === compact.bb_rename_succ[to], succs)::Int) + (; bb_rename_pred, bb_rename_succ, result_bbs) = compact.cfg_transform + preds = result_bbs[bb_rename_succ[to]].preds + succs = result_bbs[bb_rename_pred[from]].succs + deleteat!(preds, findfirst(x->x === bb_rename_pred[from], preds)::Int) + deleteat!(succs, findfirst(x->x === bb_rename_succ[to], succs)::Int) # Check if the block is now dead if length(preds) == 0 - for succ in copy(compact.result_bbs[compact.bb_rename_succ[to]].succs) - kill_edge!(compact, active_bb, to, findfirst(x->x === succ, compact.bb_rename_pred)::Int) + for succ in copy(result_bbs[bb_rename_succ[to]].succs) + kill_edge!(compact, active_bb, to, findfirst(x->x === succ, bb_rename_pred)::Int) end if to < active_bb # Kill all statements in the block - stmts = compact.result_bbs[compact.bb_rename_succ[to]].stmts + stmts = result_bbs[bb_rename_succ[to]].stmts for stmt in stmts compact.result[stmt][:inst] = nothing end @@ -1194,20 +1209,20 @@ function kill_edge!(compact::IncrementalCompact, active_bb::Int, from::Int, to:: # indicates that the block is not to be scheduled, but there should # still be an (unreachable) BB inserted into the final IR to avoid # disturbing the BB numbering. - compact.bb_rename_succ[to] = -2 + bb_rename_succ[to] = -2 end else # Remove this edge from all phi nodes in `to` block # NOTE: It is possible for `to` to contain only `nothing` statements, # so we must be careful to stop at its last statement if to < active_bb - stmts = compact.result_bbs[compact.bb_rename_succ[to]].stmts + stmts = result_bbs[bb_rename_succ[to]].stmts idx = first(stmts) while idx <= last(stmts) stmt = compact.result[idx][:inst] stmt === nothing && continue isa(stmt, PhiNode) || break - i = findfirst(x-> x == compact.bb_rename_pred[from], stmt.edges) + i = findfirst(x-> x == bb_rename_pred[from], stmt.edges) if i !== nothing deleteat!(stmt.edges, i) deleteat!(stmt.values, i) @@ -1232,14 +1247,15 @@ end function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instruction, idx::Int, processed_idx::Int, active_bb::Int, do_rename_ssa::Bool) stmt = inst[:inst] - (; result, ssa_rename, late_fixup, used_ssas, new_new_used_ssas, cfg_transforms_enabled, fold_constant_branches) = compact + (; result, ssa_rename, late_fixup, used_ssas, new_new_used_ssas) = compact + (; cfg_transforms_enabled, fold_constant_branches, bb_rename_succ, bb_rename_pred, result_bbs) = compact.cfg_transform ssa_rename[idx] = SSAValue(result_idx) if stmt === nothing ssa_rename[idx] = stmt elseif isa(stmt, OldSSAValue) ssa_rename[idx] = ssa_rename[stmt.id] elseif isa(stmt, GotoNode) && cfg_transforms_enabled - label = compact.bb_rename_succ[stmt.label] + label = bb_rename_succ[stmt.label] @assert label > 0 result[result_idx][:inst] = GotoNode(label) result_idx += 1 @@ -1272,7 +1288,7 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr kill_edge!(compact, active_bb, active_bb, stmt.dest) # Don't increment result_idx => Drop this statement else - label = compact.bb_rename_succ[stmt.dest] + label = bb_rename_succ[stmt.dest] @assert label > 0 result[result_idx][:inst] = GotoNode(label) kill_edge!(compact, active_bb, active_bb, active_bb+1) @@ -1280,7 +1296,7 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr end else @label bail - label = compact.bb_rename_succ[stmt.dest] + label = bb_rename_succ[stmt.dest] @assert label > 0 result[result_idx][:inst] = GotoIfNot(cond, label) result_idx += 1 @@ -1288,7 +1304,7 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr elseif isa(stmt, Expr) stmt = renumber_ssa2!(stmt, ssa_rename, used_ssas, new_new_used_ssas, late_fixup, result_idx, do_rename_ssa)::Expr if cfg_transforms_enabled && isexpr(stmt, :enter) - label = compact.bb_rename_succ[stmt.args[1]::Int] + label = bb_rename_succ[stmt.args[1]::Int] @assert label > 0 stmt.args[1] = label elseif isexpr(stmt, :throw_undef_if_not) @@ -1333,7 +1349,7 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr elseif isa(stmt, PhiNode) if cfg_transforms_enabled # Rename phi node edges - map!(i -> compact.bb_rename_pred[i], stmt.edges, stmt.edges) + map!(i -> bb_rename_pred[i], stmt.edges, stmt.edges) # Remove edges and values associated with dead blocks. Entries in # `values` can be undefined when the phi node refers to something @@ -1375,7 +1391,7 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr before_def = isassigned(values, 1) && (v = values[1]; isa(v, OldSSAValue)) && idx < v.id if length(edges) == 1 && isassigned(values, 1) && !before_def && length(cfg_transforms_enabled ? - compact.result_bbs[compact.bb_rename_succ[active_bb]].preds : + result_bbs[bb_rename_succ[active_bb]].preds : compact.ir.cfg.blocks[active_bb].preds) == 1 # There's only one predecessor left - just replace it v = values[1] @@ -1417,15 +1433,16 @@ function resize!(compact::IncrementalCompact, nnewnodes) end function finish_current_bb!(compact::IncrementalCompact, active_bb, old_result_idx=compact.result_idx, unreachable=false) - if compact.active_result_bb > length(compact.result_bbs) + (;result_bbs, cfg_transforms_enabled, bb_rename_succ) = compact.cfg_transform + if compact.active_result_bb > length(result_bbs) #@assert compact.bb_rename[active_bb] == -1 return true end - bb = compact.result_bbs[compact.active_result_bb] + bb = result_bbs[compact.active_result_bb] # If this was the last statement in the BB and we decided to skip it, insert a # dummy `nothing` node, to prevent changing the structure of the CFG skipped = false - if !compact.cfg_transforms_enabled || active_bb == 0 || active_bb > length(compact.bb_rename_succ) || compact.bb_rename_succ[active_bb] != -1 + if !cfg_transforms_enabled || active_bb == 0 || active_bb > length(bb_rename_succ) || bb_rename_succ[active_bb] != -1 if compact.result_idx == first(bb.stmts) length(compact.result) < old_result_idx && resize!(compact, old_result_idx) node = compact.result[old_result_idx] @@ -1435,17 +1452,17 @@ function finish_current_bb!(compact::IncrementalCompact, active_bb, old_result_i node[:inst], node[:type], node[:line] = nothing, Nothing, 0 end compact.result_idx = old_result_idx + 1 - elseif compact.cfg_transforms_enabled && compact.result_idx - 1 == first(bb.stmts) + elseif cfg_transforms_enabled && compact.result_idx - 1 == first(bb.stmts) # Optimization: If this BB consists of only a branch, eliminate this bb end - compact.result_bbs[compact.active_result_bb] = BasicBlock(bb, StmtRange(first(bb.stmts), compact.result_idx-1)) + result_bbs[compact.active_result_bb] = BasicBlock(bb, StmtRange(first(bb.stmts), compact.result_idx-1)) compact.active_result_bb += 1 else skipped = true end - if compact.active_result_bb <= length(compact.result_bbs) - new_bb = compact.result_bbs[compact.active_result_bb] - compact.result_bbs[compact.active_result_bb] = BasicBlock(new_bb, + if compact.active_result_bb <= length(result_bbs) + new_bb = result_bbs[compact.active_result_bb] + result_bbs[compact.active_result_bb] = BasicBlock(new_bb, StmtRange(compact.result_idx, last(new_bb.stmts))) end return skipped @@ -1537,7 +1554,8 @@ function iterate_compact(compact::IncrementalCompact) resize!(compact, old_result_idx) end bb = compact.ir.cfg.blocks[active_bb] - if compact.cfg_transforms_enabled && active_bb > 1 && active_bb <= length(compact.bb_rename_succ) && compact.bb_rename_succ[active_bb] <= -1 + (; cfg_transforms_enabled, bb_rename_succ) = compact.cfg_transform + if cfg_transforms_enabled && active_bb > 1 && active_bb <= length(bb_rename_succ) && bb_rename_succ[active_bb] <= -1 # Dead block, so kill the entire block. compact.idx = last(bb.stmts) # Pop any remaining insertion nodes @@ -1739,8 +1757,8 @@ function non_dce_finish!(compact::IncrementalCompact) result_idx = compact.result_idx resize!(compact.result, result_idx - 1) just_fixup!(compact) - bb = compact.result_bbs[end] - compact.result_bbs[end] = BasicBlock(bb, + bb = compact.cfg_transform.result_bbs[end] + compact.cfg_transform.result_bbs[end] = BasicBlock(bb, StmtRange(first(bb.stmts), result_idx-1)) compact.renamed_new_nodes = true nothing @@ -1753,7 +1771,7 @@ function finish(compact::IncrementalCompact) end function complete(compact::IncrementalCompact) - result_bbs = resize!(compact.result_bbs, compact.active_result_bb-1) + result_bbs = resize!(compact.cfg_transform.result_bbs, compact.active_result_bb-1) cfg = CFG(result_bbs, Int[first(result_bbs[i].stmts) for i in 2:length(result_bbs)]) if should_check_ssa_counts() oracle_check(compact) diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index ab6bbf6a001d4..7a9c877b6c2f3 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -2185,14 +2185,10 @@ function cfg_simplify!(ir::IRCode) end end - compact = IncrementalCompact(ir, true) # Run instruction compaction to produce the result, # but we're messing with the CFG # so we don't want compaction to do so independently - compact.fold_constant_branches = false - compact.bb_rename_succ = bb_rename_succ - compact.bb_rename_pred = bb_rename_pred - compact.result_bbs = cresult_bbs + compact = IncrementalCompact(ir, CFGTransformState(true, false, cresult_bbs, bb_rename_pred, bb_rename_succ)) result_idx = 1 for (idx, orig_bb) in enumerate(result_bbs) ms = orig_bb diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index 0d17746c6d928..f6fac22886046 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -902,7 +902,7 @@ function show_ir(io::IO, compact::IncrementalCompact, config::IRShowConfig=defau # while compacting, the end of the active result bb will not have been determined # (this is done post-hoc by `finish_current_bb!`), so determine it here from scratch. - result_bbs = copy(compact.result_bbs) + result_bbs = copy(compact.cfg_transform.result_bbs) if compact.active_result_bb <= length(result_bbs) # count the total number of nodes we'll add to this block input_bb_idx = block_for_inst(compact.ir.cfg, compact.idx) diff --git a/test/compiler/ssair.jl b/test/compiler/ssair.jl index 97ec58ce4c6c1..5829752c20420 100644 --- a/test/compiler/ssair.jl +++ b/test/compiler/ssair.jl @@ -119,7 +119,7 @@ let cfg = CFG(BasicBlock[ insts = Compiler.InstructionStream([], [], Any[], Int32[], UInt8[]) ir = Compiler.IRCode(insts, cfg, Core.LineInfoNode[], Any[], Expr[], Compiler.VarState[]) compact = Compiler.IncrementalCompact(ir, true) - @test length(compact.result_bbs) == 4 && 0 in compact.result_bbs[3].preds + @test length(compact.cfg_transform.result_bbs) == 4 && 0 in compact.cfg_transform.result_bbs[3].preds end # Issue #32579 - Optimizer bug involving type constraints From 369660fd8e787a999f8a67f6360abee0914dbea4 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 10 Mar 2023 18:04:38 -0500 Subject: [PATCH 307/775] gf: accelerate adding backedges back to old performance (#48966) Since it does not compute and branch on typeof, in my measurements, this implementation is up to 10x faster! --- src/gf.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/gf.c b/src/gf.c index 092e17cd1bcf5..174a41d137ffe 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1610,31 +1610,32 @@ JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, JL_LOCK(&callee->def.method->writelock); if (invokesig == jl_nothing) invokesig = NULL; // julia uses `nothing` but C uses NULL (#undef) + int found = 0; + // TODO: use jl_cache_type_(invokesig) like cache_method does to save memory if (!callee->backedges) { // lazy-init the backedges array callee->backedges = jl_alloc_vec_any(0); jl_gc_wb(callee, callee->backedges); - push_edge(callee->backedges, invokesig, caller); } else { size_t i = 0, l = jl_array_len(callee->backedges); - int found = 0; - jl_value_t *invokeTypes; - jl_method_instance_t *mi; - while (i < l) { - i = get_next_edge(callee->backedges, i, &invokeTypes, &mi); - // TODO: it would be better to canonicalize (how?) the Tuple-type so - // that we don't have to call `jl_egal` - if (mi == caller && ((invokesig == NULL && invokeTypes == NULL) || - (invokesig && invokeTypes && jl_egal(invokesig, invokeTypes)))) { + for (i = 0; i < l; i++) { + // optimized version of while (i < l) i = get_next_edge(callee->backedges, i, &invokeTypes, &mi); + jl_value_t *mi = jl_array_ptr_ref(callee->backedges, i); + if (mi != (jl_value_t*)caller) + continue; + jl_value_t *invokeTypes = i > 0 ? jl_array_ptr_ref(callee->backedges, i - 1) : NULL; + if (invokeTypes && jl_is_method_instance(invokeTypes)) + invokeTypes = NULL; + if ((invokesig == NULL && invokeTypes == NULL) || + (invokesig && invokeTypes && jl_types_equal(invokesig, invokeTypes))) { found = 1; break; } } - if (!found) { - push_edge(callee->backedges, invokesig, caller); - } } + if (!found) + push_edge(callee->backedges, invokesig, caller); JL_UNLOCK(&callee->def.method->writelock); } @@ -1650,6 +1651,7 @@ JL_DLLEXPORT void jl_method_table_add_backedge(jl_methtable_t *mt, jl_value_t *t jl_array_ptr_set(mt->backedges, 1, caller); } else { + // TODO: use jl_cache_type_(tt) like cache_method does, instead of a linear scan size_t i, l = jl_array_len(mt->backedges); for (i = 1; i < l; i += 2) { if (jl_types_equal(jl_array_ptr_ref(mt->backedges, i - 1), typ)) { From 441c57057306ec63ed874e610bc53ba2a145c17e Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 10 Mar 2023 21:16:10 -0500 Subject: [PATCH 308/775] Defensively copy IRCode from semi-concrete eval for inlining (#48963) Currently, semi-concrete eval results are one-to-one associated with a particular callsite, so in theory we can destroy them during inlining and nothing bad will happen. However, since we propagate them using the `:info` field, this breaks the assumption that we can copy IRCode around and re-run the compiler on it without ill effect. In general, the `:info` field is assumed generally immutable and mutating the IR containted therein breaks all sorts of assumptions. Perhaps in the future, we can avoid destroying the IRCode that we're about to inline, which would make all this copying unnecessary (note that we're already copying for every case other than semi-concrete eval), but for now just fix the robustness issue here. --- base/compiler/ssair/inlining.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index c72293911814d..21647933d348e 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -1475,7 +1475,7 @@ function semiconcrete_result_item(result::SemiConcreteResult, return compileable_specialization(mi, result.effects, et, info; compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) else - return InliningTodo(mi, result.ir, result.effects) + return InliningTodo(mi, retrieve_ir_for_inlining(mi, result.ir), result.effects) end end From 9f997fcc7fb65dbb2f4906e7a94ab0250ff58231 Mon Sep 17 00:00:00 2001 From: Julian Samaroo Date: Mon, 13 Mar 2023 16:24:55 -0500 Subject: [PATCH 309/775] tasks: Don't make parent sticky in finalizer (#48919) --- base/condition.jl | 2 +- base/task.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/base/condition.jl b/base/condition.jl index ca39b6ea148a4..9d4f382064a2f 100644 --- a/base/condition.jl +++ b/base/condition.jl @@ -87,7 +87,7 @@ function _wait2(c::GenericCondition, waiter::Task, first::Bool=false) push!(c.waitq, waiter) end # since _wait2 is similar to schedule, we should observe the sticky bit now - if waiter.sticky && Threads.threadid(waiter) == 0 + if waiter.sticky && Threads.threadid(waiter) == 0 && !GC.in_finalizer() # Issue #41324 # t.sticky && tid == 0 is a task that needs to be co-scheduled with # the parent task. If the parent (current_task) is not sticky we must diff --git a/base/task.jl b/base/task.jl index 63d0e9b6bd757..ffe8e5665b041 100644 --- a/base/task.jl +++ b/base/task.jl @@ -323,7 +323,7 @@ function _wait2(t::Task, waiter::Task) unlock(t.donenotify) # since _wait2 is similar to schedule, we should observe the sticky # bit, even if we aren't calling `schedule` due to this early-return - if waiter.sticky && Threads.threadid(waiter) == 0 + if waiter.sticky && Threads.threadid(waiter) == 0 && !GC.in_finalizer() # Issue #41324 # t.sticky && tid == 0 is a task that needs to be co-scheduled with # the parent task. If the parent (current_task) is not sticky we must @@ -771,7 +771,7 @@ function enq_work(t::Task) # Sticky tasks go into their thread's work queue. if t.sticky tid = Threads.threadid(t) - if tid == 0 + if tid == 0 && !GC.in_finalizer() # The task is not yet stuck to a thread. Stick it to the current # thread and do the same to the parent task (the current task) so # that the tasks are correctly co-scheduled (issue #41324). From 6c2410361a6e413652ed89fb206d4dc6c2af89c9 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 14 Mar 2023 06:39:10 -0500 Subject: [PATCH 310/775] Add test for issue #48802 (#48984) Was fixed by #48954 --- test/reflection.jl | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/reflection.jl b/test/reflection.jl index 0c1081ba2c42f..7fa73d56df2b2 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -726,6 +726,31 @@ Base.delete_method(m) @test faz4(1) == 1 @test faz4(1.0) == 1 +# Deletion & invoke (issue #48802) +function f48802!(log, x::Integer) + log[] = "default" + return x + 1 +end +function addmethod_48802() + @eval function f48802!(log, x::Int) + ret = invoke(f48802!, Tuple{Any, Integer}, log, x) + log[] = "specialized" + return ret + end +end +log = Ref{String}() +@test f48802!(log, 1) == 2 +@test log[] == "default" +addmethod_48802() +@test f48802!(log, 1) == 2 +@test log[] == "specialized" +Base.delete_method(which(f48802!, Tuple{Any, Int})) +@test f48802!(log, 1) == 2 +@test log[] == "default" +addmethod_48802() +@test f48802!(log, 1) == 2 +@test log[] == "specialized" + # Methods with keyword arguments fookw(x; direction=:up) = direction fookw(y::Int) = 2 From 7ba7e326293bd3eddede81567bbe98078e81f775 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 14 Mar 2023 10:42:01 -0400 Subject: [PATCH 311/775] irinterp: Allow setting all IR flags (#48993) Currently, `IR_FLAG_NOTHROW` is the only flag that irinterp is allowed to set on statements, under the assumption that in order for a call to be irinterp-eligible, it must have been proven `:foldable`, thus `:effect_free`, and thus `IR_FLAG_EFFECT_FREE` was assumed to have been set. That reasoning was sound at the time this code was written, but have since introduced `EFFECT_FREE_IF_INACCESSIBLEMEMONLY`, which breaks the reasoning that an `:effect_free` inference for the whole function implies the flag on every statement. As a result, we were failing to DCE otherwise dead statements if the IR came from irinterp. --- base/compiler/ssair/irinterp.jl | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index c91a0fe014eac..49c2f54278545 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -238,12 +238,9 @@ function reprocess_instruction!(interp::AbstractInterpreter, head = inst.head if head === :call || head === :foreigncall || head === :new || head === :splatnew (; rt, effects) = abstract_eval_statement_expr(interp, inst, nothing, ir, irsv.mi) - # All other effects already guaranteed effect free by construction - if is_nothrow(effects) - ir.stmts[idx][:flag] |= IR_FLAG_NOTHROW - if isa(rt, Const) && is_inlineable_constant(rt.val) - ir.stmts[idx][:inst] = quoted(rt.val) - end + ir.stmts[idx][:flag] |= flags_for_effects(effects) + if is_foldable(effects) && isa(rt, Const) && is_inlineable_constant(rt.val) + ir.stmts[idx][:inst] = quoted(rt.val) end elseif head === :invoke mi′ = inst.args[1]::MethodInstance From 4beea98a8828fa8478e64a1495d978d8d4d50e11 Mon Sep 17 00:00:00 2001 From: Sukera <11753998+Seelengrab@users.noreply.github.com> Date: Wed, 15 Mar 2023 14:45:42 +0100 Subject: [PATCH 312/775] Enable the AVR backend of LLVM (#49006) Co-authored-by: Sukera --- deps/llvm.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/llvm.mk b/deps/llvm.mk index 81dcff1ce4c84..d7ff5bd71e980 100644 --- a/deps/llvm.mk +++ b/deps/llvm.mk @@ -64,7 +64,7 @@ endif LLVM_LIB_FILE := libLLVMCodeGen.a # Figure out which targets to build -LLVM_TARGETS := host;NVPTX;AMDGPU;WebAssembly;BPF +LLVM_TARGETS := host;NVPTX;AMDGPU;WebAssembly;BPF;AVR LLVM_EXPERIMENTAL_TARGETS := LLVM_CFLAGS := From ae86b246b05c70a59c20b1110cd893916521bdbe Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Wed, 15 Mar 2023 10:48:31 -0400 Subject: [PATCH 313/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=2040e07927f=20to=20429175914=20(#49009)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dilum Aluthge --- .../Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/md5 | 1 - .../Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/sha512 | 1 - .../Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/md5 | 1 + .../Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/md5 create mode 100644 deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/sha512 diff --git a/deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/md5 b/deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/md5 deleted file mode 100644 index 60a30227b807f..0000000000000 --- a/deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -3bd0dbbc226bf80afe98a58d2e7e3a16 diff --git a/deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/sha512 b/deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/sha512 deleted file mode 100644 index a7a6bf0d7e365..0000000000000 --- a/deps/checksums/Pkg-40e07927f47ec63bb663cdebd4679ebecaf142b8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -038c9c58c1fac83f48ca0b9606f523ef168acba382b411a3aa7c956b96ec1707a5c88417100c3522629820f81e672af4ed28b089218682cdf4fde7c6cc36b440 diff --git a/deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/md5 b/deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/md5 new file mode 100644 index 0000000000000..80f94f607268b --- /dev/null +++ b/deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/md5 @@ -0,0 +1 @@ +dff8afa625321af081b4567102e3f91f diff --git a/deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/sha512 b/deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/sha512 new file mode 100644 index 0000000000000..65ca28ff83f4a --- /dev/null +++ b/deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/sha512 @@ -0,0 +1 @@ +25e9c430beac4c13e5fa5d1edc2eb09ae5c17f766651b5c8cf22e8c1720a3fda4fc7f73e4d209ca1726f8a64ec6b7cefc303e671949c974d20ae21347184ed79 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 899147f1a2089..2440d23eda0ab 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 40e07927f47ec63bb663cdebd4679ebecaf142b8 +PKG_SHA1 = 429175914e8d44f6675ae82865b9d140735cb001 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From fd67fe858682e8ab341907b8870cd7218b676f26 Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Wed, 15 Mar 2023 12:58:59 -0400 Subject: [PATCH 314/775] aotcompile: name each output shards uniquely (#48992) This should prevent conflicts in the archive file, causing loss of DWARF information. * Upfront declare all the names * Upfront reserve outputs capacity Co-authored-by: Prem Chintalapudi --- src/aotcompile.cpp | 71 +++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index dd49e6b466474..6bd6d30c640a8 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -936,10 +936,9 @@ struct ShardTimers { }; // Perform the actual optimization and emission of the output files -static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *outputs, ArrayRef names, +static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *outputs, StringRef *names, NewArchiveMember *unopt, NewArchiveMember *opt, NewArchiveMember *obj, NewArchiveMember *asm_, ShardTimers &timers, unsigned shardidx) { - assert(names.size() == 4); auto TM = std::unique_ptr( SourceTM.getTarget().createTargetMachine( SourceTM.getTargetTriple().str(), @@ -957,8 +956,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out AnalysisManagers AM{*TM, PB, OptimizationLevel::O0}; ModulePassManager MPM; MPM.addPass(BitcodeWriterPass(OS)); - *unopt = NewArchiveMember(MemoryBufferRef(*outputs, names[0])); - outputs++; + *unopt = NewArchiveMember(MemoryBufferRef(*outputs++, *names++)); timers.unopt.stopTimer(); } if (!opt && !obj && !asm_) { @@ -1022,8 +1020,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out AnalysisManagers AM{*TM, PB, OptimizationLevel::O0}; ModulePassManager MPM; MPM.addPass(BitcodeWriterPass(OS)); - *opt = NewArchiveMember(MemoryBufferRef(*outputs, names[1])); - outputs++; + *opt = NewArchiveMember(MemoryBufferRef(*outputs++, *names++)); timers.opt.stopTimer(); } @@ -1037,8 +1034,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out jl_safe_printf("ERROR: target does not support generation of object files\n"); emitter.run(M); *outputs = { Buffer.data(), Buffer.size() }; - *obj = NewArchiveMember(MemoryBufferRef(*outputs, names[2])); - outputs++; + *obj = NewArchiveMember(MemoryBufferRef(*outputs++, *names++)); timers.obj.stopTimer(); } @@ -1052,8 +1048,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out jl_safe_printf("ERROR: target does not support generation of assembly files\n"); emitter.run(M); *outputs = { Buffer.data(), Buffer.size() }; - *asm_ = NewArchiveMember(MemoryBufferRef(*outputs, names[3])); - outputs++; + *asm_ = NewArchiveMember(MemoryBufferRef(*outputs++, *names++)); timers.asm_.stopTimer(); } } @@ -1212,20 +1207,18 @@ static void dropUnusedDeclarations(Module &M) { // Entrypoint to optionally-multithreaded image compilation. This handles global coordination of the threading, // as well as partitioning, serialization, and deserialization. -static void add_output(Module &M, TargetMachine &TM, std::vector &outputs, ArrayRef names, +static void add_output(Module &M, TargetMachine &TM, std::vector &outputs, StringRef name, std::vector &unopt, std::vector &opt, std::vector &obj, std::vector &asm_, bool unopt_out, bool opt_out, bool obj_out, bool asm_out, unsigned threads, ModuleInfo module_info) { unsigned outcount = unopt_out + opt_out + obj_out + asm_out; assert(outcount); - outputs.resize(outputs.size() + outcount * threads); + outputs.resize(outputs.size() + outcount * threads * 2); unopt.resize(unopt.size() + unopt_out * threads); opt.resize(opt.size() + opt_out * threads); obj.resize(obj.size() + obj_out * threads); asm_.resize(asm_.size() + asm_out * threads); - auto name = names[2]; - name.consume_back(".o"); // Timers for timing purposes TimerGroup timer_group("add_output", ("Time to optimize and emit LLVM module " + name).str()); SmallVector timers(threads); @@ -1261,10 +1254,25 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o errs() << "WARNING: Invalid value for JULIA_IMAGE_TIMINGS: " << env << "\n"; } } + for (unsigned i = 0; i < threads; ++i) { + auto start = &outputs[outputs.size() - outcount * threads * 2 + i]; + auto istr = std::to_string(i); + if (unopt_out) + *start++ = (name + "_unopt#" + istr + ".bc").str(); + if (opt_out) + *start++ = (name + "_opt#" + istr + ".bc").str(); + if (obj_out) + *start++ = (name + "#" + istr + ".o").str(); + if (asm_out) + *start++ = (name + "#" + istr + ".s").str(); + } // Single-threaded case if (threads == 1) { output_timer.startTimer(); - add_output_impl(M, TM, outputs.data() + outputs.size() - outcount, names, + SmallVector names; + for (unsigned i = 0; i < outcount; ++i) + names.push_back(outputs[i]); + add_output_impl(M, TM, outputs.data() + outputs.size() - outcount, names.data(), unopt_out ? unopt.data() + unopt.size() - 1 : nullptr, opt_out ? opt.data() + opt.size() - 1 : nullptr, obj_out ? obj.data() + obj.size() - 1 : nullptr, @@ -1330,7 +1338,11 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o dropUnusedDeclarations(*M); timers[i].deletion.stopTimer(); - add_output_impl(*M, TM, outstart + i * outcount, names, + SmallVector names(outcount); + for (unsigned j = 0; j < outcount; ++j) + names[j] = outputs[i * outcount + j]; + + add_output_impl(*M, TM, outstart + i * outcount, names.data(), unoptstart ? unoptstart + i : nullptr, optstart ? optstart + i : nullptr, objstart ? objstart + i : nullptr, @@ -1554,21 +1566,20 @@ void jl_dump_native_impl(void *native_code, "jl_RTLD_DEFAULT_handle_pointer"), TheTriple); } - auto compile = [&](Module &M, ArrayRef names, unsigned threads) { add_output( - M, *SourceTM, outputs, names, + // Reserve space for the output files and names + // DO NOT DELETE, this is necessary to ensure memorybuffers + // have a stable backing store for both their object files and + // their names + outputs.reserve(threads * (!!unopt_bc_fname + !!bc_fname + !!obj_fname + !!asm_fname) * 2 + 2); + + auto compile = [&](Module &M, StringRef name, unsigned threads) { add_output( + M, *SourceTM, outputs, name, unopt_bc_Archive, bc_Archive, obj_Archive, asm_Archive, !!unopt_bc_fname, !!bc_fname, !!obj_fname, !!asm_fname, threads, module_info ); }; - std::array text_names = { - "text_unopt.bc", - "text_opt.bc", - "text.o", - "text.s" - }; - - compile(*dataM, text_names, threads); + compile(*dataM, "text", threads); auto sysimageM = std::make_unique("sysimage", Context); sysimageM->setTargetTriple(dataM->getTargetTriple()); @@ -1646,13 +1657,7 @@ void jl_dump_native_impl(void *native_code, } } - std::array data_names = { - "data_unopt.bc", - "data_opt.bc", - "data.o", - "data.s" - }; - compile(*sysimageM, data_names, 1); + compile(*sysimageM, "data", 1); object::Archive::Kind Kind = getDefaultForHost(TheTriple); if (unopt_bc_fname) From a7a5468b3ff7356d8abded983e51563414dc899b Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Wed, 15 Mar 2023 15:06:50 -0400 Subject: [PATCH 315/775] Parse semantic version string correctly The previous `awk` pattern did not split out the 'build' portion of the version correctly. --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 7319caea04955..7159e4f210b59 100644 --- a/src/Makefile +++ b/src/Makefile @@ -353,7 +353,7 @@ $(BUILDDIR)/julia_version.h: $(JULIAHOME)/VERSION @echo "#ifndef JL_VERSION_H" >> $@.$(JULIA_BUILD_MODE).tmp @echo "#define JL_VERSION_H" >> $@.$(JULIA_BUILD_MODE).tmp @echo "#define JULIA_VERSION_STRING" \"$(JULIA_VERSION)\" >> $@.$(JULIA_BUILD_MODE).tmp - @echo $(JULIA_VERSION) | awk 'BEGIN {FS="[.,-]"} \ + @echo $(JULIA_VERSION) | awk 'BEGIN {FS="[.,+-]"} \ {print "#define JULIA_VERSION_MAJOR " $$1 "\n" \ "#define JULIA_VERSION_MINOR " $$2 "\n" \ "#define JULIA_VERSION_PATCH " $$3 ; \ From b5aa05706e42d476556d72e9556c5cbe679b7599 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 15 Mar 2023 17:52:50 -0400 Subject: [PATCH 316/775] codegen: fix use-after-free of module name (#49011) --- src/codegen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 8e5ec99da34af..b7d1bae7411a6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8459,11 +8459,11 @@ jl_llvm_functions_t jl_emit_code( JL_CATCH { // Something failed! This is very, very bad. // Try to pretend that it isn't and attempt to recover. - const char *mname = m.getModuleUnlocked()->getModuleIdentifier().data(); + std::string mname = m.getModuleUnlocked()->getModuleIdentifier(); m = orc::ThreadSafeModule(); decls.functionObject = ""; decls.specFunctionObject = ""; - jl_printf((JL_STREAM*)STDERR_FILENO, "Internal error: encountered unexpected error during compilation of %s:\n", mname); + jl_printf((JL_STREAM*)STDERR_FILENO, "Internal error: encountered unexpected error during compilation of %s:\n", mname.c_str()); jl_static_show((JL_STREAM*)STDERR_FILENO, jl_current_exception()); jl_printf((JL_STREAM*)STDERR_FILENO, "\n"); jlbacktrace(); // written to STDERR_FILENO From 5960b523e7a3cef0c07ba094ee2cd07ff7c32eda Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 16 Mar 2023 10:03:42 +0800 Subject: [PATCH 317/775] Add test for #48961 (#49014) --- test/subtype.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/subtype.jl b/test/subtype.jl index c7a2dcdcc113f..dfd1a62d5f895 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2458,3 +2458,6 @@ let S = Tuple{Type{S48695{T, 2, T48695{B, 2, C}}} where {T<:(Union{Missing, A} w @test allunique(vars_in_unionall(V)) @test typeintersect(V, T) != Union{} end + +#issue 48961 +@test !<:(Type{Union{Missing, Int}}, Type{Union{Missing, Nothing, Int}}) From 669d6ca0ace86a0de977b8a32c7dfdecdc384da3 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 16 Mar 2023 16:44:56 +0900 Subject: [PATCH 318/775] add more test case for inference with overlayed method table (#48988) --- test/compiler/AbstractInterpreter.jl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index fcb00eaa45019..ffd7117cd99be 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -107,6 +107,17 @@ Base.@assume_effects :total totalcall(f, args...) = f(args...) end end |> only === Nothing +# GPUCompiler needs accurate inference through kwfunc with the overlay of `Core.throw_inexacterror` +# https://github.com/JuliaLang/julia/issues/48097 +@newinterp Issue48097Interp +@MethodTable Issue48097MT +CC.method_table(interp::Issue48097Interp) = CC.OverlayMethodTable(CC.get_world_counter(interp), Issue48097MT) +@overlay Issue48097MT @noinline Core.throw_inexacterror(f::Symbol, ::Type{T}, val) where {T} = return +issue48097(; kwargs...) = return 42 +@test fully_eliminated(; interp=Issue48097Interp(), retval=42) do + issue48097(; a=1f0, b=1.0) +end + # AbstractLattice # =============== From c37fc2798d9bb7349ff8eadb350ae68cf17cee61 Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Thu, 16 Mar 2023 10:53:06 +0000 Subject: [PATCH 319/775] Add *(::Diagonal, ::Diagonal, ::Diagonal) (#49005) (#49007) --- stdlib/LinearAlgebra/src/diagonal.jl | 6 ++++++ stdlib/LinearAlgebra/test/diagonal.jl | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index df37e8935d2af..7c54ce4009e33 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -385,6 +385,12 @@ function (*)(Da::Diagonal, A::AbstractMatrix, Db::Diagonal) return broadcast(*, Da.diag, A, permutedims(Db.diag)) end +function (*)(Da::Diagonal, Db::Diagonal, Dc::Diagonal) + _muldiag_size_check(Da, Db) + _muldiag_size_check(Db, Dc) + return Diagonal(Da.diag .* Db.diag .* Dc.diag) +end + # Get ambiguous method if try to unify AbstractVector/AbstractMatrix here using AbstractVecOrMat @inline mul!(out::AbstractVector, D::Diagonal, V::AbstractVector, alpha::Number, beta::Number) = _muldiag!(out, D, V, alpha, beta) diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index bcfed234a51ee..b8f3789ae2674 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -1155,4 +1155,15 @@ end @test all(isone, diag(D)) end +@testset "diagonal triple multiplication (#49005)" begin + n = 10 + @test *(Diagonal(ones(n)), Diagonal(1:n), Diagonal(ones(n))) isa Diagonal + @test_throws DimensionMismatch (*(Diagonal(ones(n)), Diagonal(1:n), Diagonal(ones(n+1)))) + @test_throws DimensionMismatch (*(Diagonal(ones(n)), Diagonal(1:n+1), Diagonal(ones(n+1)))) + @test_throws DimensionMismatch (*(Diagonal(ones(n+1)), Diagonal(1:n), Diagonal(ones(n)))) + + # currently falls back to two-term * + @test *(Diagonal(ones(n)), Diagonal(1:n), Diagonal(ones(n)), Diagonal(1:n)) isa Diagonal +end + end # module TestDiagonal From e3043a875d432ec4bd7c073874a654b55421438f Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 16 Mar 2023 15:49:59 +0100 Subject: [PATCH 320/775] delay loading of extensions as much as possible (#48674) --- base/loading.jl | 87 ++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 40 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 8b104af41aec5..b7acda85fea0d 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1080,7 +1080,6 @@ function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String) end function run_package_callbacks(modkey::PkgId) - run_extension_callbacks(modkey) assert_havelock(require_lock) unlock(require_lock) try @@ -1204,53 +1203,57 @@ function run_extension_callbacks(extid::ExtensionId) return succeeded end -function run_extension_callbacks(pkgid::PkgId) +function run_extension_callbacks() assert_havelock(require_lock) - # take ownership of extids that depend on this pkgid - extids = pop!(EXT_DORMITORY, pkgid, nothing) - extids === nothing && return - for extid in extids - if extid.ntriggers > 0 - # It is possible that pkgid was loaded in an environment - # below the one of the parent. This will cause a load failure when the - # pkg ext tries to load the triggers. Therefore, check this first - # before loading the pkg ext. - pkgenv = Base.identify_package_env(extid.id, pkgid.name) - ext_not_allowed_load = false - if pkgenv === nothing - ext_not_allowed_load = true - else - pkg, env = pkgenv - path = Base.locate_package(pkg, env) - if path === nothing + loaded_triggers = collect(intersect(keys(Base.loaded_modules), keys(Base.EXT_DORMITORY))) + sort!(loaded_triggers; by=x->x.uuid) + for pkgid in loaded_triggers + # take ownership of extids that depend on this pkgid + extids = pop!(EXT_DORMITORY, pkgid, nothing) + extids === nothing && continue + for extid in extids + if extid.ntriggers > 0 + # It is possible that pkgid was loaded in an environment + # below the one of the parent. This will cause a load failure when the + # pkg ext tries to load the triggers. Therefore, check this first + # before loading the pkg ext. + pkgenv = Base.identify_package_env(extid.id, pkgid.name) + ext_not_allowed_load = false + if pkgenv === nothing ext_not_allowed_load = true + else + pkg, env = pkgenv + path = Base.locate_package(pkg, env) + if path === nothing + ext_not_allowed_load = true + end + end + if ext_not_allowed_load + @debug "Extension $(extid.id.name) of $(extid.parentid.name) will not be loaded \ + since $(pkgid.name) loaded in environment lower in load path" + # indicate extid is expected to fail + extid.ntriggers *= -1 + else + # indicate pkgid is loaded + extid.ntriggers -= 1 end end - if ext_not_allowed_load - @debug "Extension $(extid.id.name) of $(extid.parentid.name) will not be loaded \ - since $(pkgid.name) loaded in environment lower in load path" - # indicate extid is expected to fail - extid.ntriggers *= -1 - else + if extid.ntriggers < 0 # indicate pkgid is loaded - extid.ntriggers -= 1 + extid.ntriggers += 1 + succeeded = false + else + succeeded = true + end + 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) end - end - if extid.ntriggers < 0 - # indicate pkgid is loaded - extid.ntriggers += 1 - succeeded = false - else - succeeded = true - end - 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) end end - nothing + return end """ @@ -1665,6 +1668,10 @@ function _require_prelocked(uuidkey::PkgId, env=nothing) else newm = root_module(uuidkey) end + # Load extensions when not precompiling and not in a nested package load + if JLOptions().incremental == 0 && isempty(package_locks) + run_extension_callbacks() + end return newm end From 38d9d837ad2d4b0438c36c60c17c0c0f8265bf8a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 16 Mar 2023 12:36:32 -0400 Subject: [PATCH 321/775] reuse existing typemap search for method overwrite detection (#48968) It does not really make sense to scan the tree twice, when we already will extra this info near completely from the intersection search. The cost was small, but non-negligible. --- src/gf.c | 62 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/src/gf.c b/src/gf.c index 174a41d137ffe..ddc4fcb8ad91e 100644 --- a/src/gf.c +++ b/src/gf.c @@ -207,6 +207,9 @@ JL_DLLEXPORT jl_value_t *jl_specializations_lookup(jl_method_t *m, jl_value_t *t JL_DLLEXPORT jl_value_t *jl_methtable_lookup(jl_methtable_t *mt, jl_value_t *type, size_t world) { + // TODO: this is sort of an odd lookup strategy (and the only user of + // jl_typemap_assoc_by_type with subtype=0), while normally jl_gf_invoke_lookup would be + // expected to be used instead struct jl_typemap_assoc search = {type, world, NULL, 0, ~(size_t)0}; jl_typemap_entry_t *sf = jl_typemap_assoc_by_type(jl_atomic_load_relaxed(&mt->defs), &search, /*offs*/0, /*subtype*/0); if (!sf) @@ -1379,7 +1382,9 @@ struct matches_env { struct typemap_intersection_env match; jl_typemap_entry_t *newentry; jl_value_t *shadowed; + jl_typemap_entry_t *replaced; }; + static int get_intersect_visitor(jl_typemap_entry_t *oldentry, struct typemap_intersection_env *closure0) { struct matches_env *closure = container_of(closure0, struct matches_env, match); @@ -1390,13 +1395,17 @@ static int get_intersect_visitor(jl_typemap_entry_t *oldentry, struct typemap_in // also be careful not to try to scan something from the current dump-reload though return 1; jl_method_t *oldmethod = oldentry->func.method; + if (closure->match.issubty // e.g. jl_subtype(closure->newentry.sig, oldentry->sig) + && jl_subtype(oldmethod->sig, (jl_value_t*)closure->newentry->sig)) { // e.g. jl_type_equal(closure->newentry->sig, oldentry->sig) + closure->replaced = oldentry; + } if (closure->shadowed == NULL) closure->shadowed = (jl_value_t*)jl_alloc_vec_any(0); jl_array_ptr_1d_push((jl_array_t*)closure->shadowed, (jl_value_t*)oldmethod); return 1; } -static jl_value_t *get_intersect_matches(jl_typemap_t *defs, jl_typemap_entry_t *newentry) +static jl_value_t *get_intersect_matches(jl_typemap_t *defs, jl_typemap_entry_t *newentry, jl_typemap_entry_t **replaced) { jl_tupletype_t *type = newentry->sig; jl_tupletype_t *ttypes = (jl_tupletype_t*)jl_unwrap_unionall((jl_value_t*)type); @@ -1411,9 +1420,12 @@ static jl_value_t *get_intersect_matches(jl_typemap_t *defs, jl_typemap_entry_t } struct matches_env env = {{get_intersect_visitor, (jl_value_t*)type, va, /* .ti = */ NULL, /* .env = */ jl_emptysvec, /* .issubty = */ 0}, - /* .newentry = */ newentry, /* .shadowed */ NULL}; + /* .newentry = */ newentry, /* .shadowed */ NULL, /* .replaced */ NULL}; JL_GC_PUSH3(&env.match.env, &env.match.ti, &env.shadowed); jl_typemap_intersection_visitor(defs, 0, &env.match); + env.match.env = NULL; + env.match.ti = NULL; + *replaced = env.replaced; JL_GC_POP(); return env.shadowed; } @@ -1735,8 +1747,9 @@ static jl_typemap_entry_t *do_typemap_search(jl_methtable_t *mt JL_PROPAGATES_RO } #endif -static void jl_method_table_invalidate(jl_methtable_t *mt, jl_typemap_entry_t *methodentry, jl_method_t *method, size_t max_world) +static void jl_method_table_invalidate(jl_methtable_t *mt, jl_typemap_entry_t *methodentry, size_t max_world) { + jl_method_t *method = methodentry->func.method; assert(!method->is_for_opaque_closure); method->deleted_world = methodentry->max_world = max_world; // drop this method from mt->cache @@ -1760,16 +1773,18 @@ static void jl_method_table_invalidate(jl_methtable_t *mt, jl_typemap_entry_t *m } // Invalidate the backedges int invalidated = 0; - jl_svec_t *specializations = jl_atomic_load_relaxed(&methodentry->func.method->specializations); + jl_svec_t *specializations = jl_atomic_load_relaxed(&method->specializations); l = jl_svec_len(specializations); for (i = 0; i < l; i++) { jl_method_instance_t *mi = (jl_method_instance_t*)jl_svecref(specializations, i); if ((jl_value_t*)mi != jl_nothing) { invalidated = 1; - invalidate_external(mi, methodentry->max_world); - invalidate_backedges(&do_nothing_with_codeinst, mi, methodentry->max_world, "jl_method_table_disable"); + invalidate_external(mi, max_world); + invalidate_backedges(&do_nothing_with_codeinst, mi, max_world, "jl_method_table_disable"); } } + // XXX: this might have resolved an ambiguity, for which we have not tracked the edge here, + // and thus now introduce a mistake into inference if (invalidated && _jl_debug_method_invalidation) { jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)method); jl_value_t *loctag = jl_cstr_to_string("jl_method_table_disable"); @@ -1788,7 +1803,7 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho JL_LOCK(&mt->writelock); // Narrow the world age on the method to make it uncallable size_t world = jl_atomic_fetch_add(&jl_world_counter, 1); - jl_method_table_invalidate(mt, methodentry, method, world); + jl_method_table_invalidate(mt, methodentry, world); JL_UNLOCK(&mt->writelock); } @@ -1866,22 +1881,21 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method jl_typemap_entry_t *newentry = NULL; JL_GC_PUSH7(&oldvalue, &oldmi, &newentry, &loctag, &isect, &isect2, &isect3); JL_LOCK(&mt->writelock); - // first find if we have an existing entry to delete - struct jl_typemap_assoc search = {(jl_value_t*)type, method->primary_world, NULL, 0, ~(size_t)0}; - jl_typemap_entry_t *oldentry = jl_typemap_assoc_by_type(jl_atomic_load_relaxed(&mt->defs), &search, /*offs*/0, /*subtype*/0); - // then add our new entry + // add our new entry newentry = jl_typemap_alloc((jl_tupletype_t*)type, simpletype, jl_emptysvec, (jl_value_t*)method, method->primary_world, method->deleted_world); jl_typemap_insert(&mt->defs, (jl_value_t*)mt, newentry, 0); - if (oldentry) { - jl_method_t *m = oldentry->func.method; - method_overwrite(newentry, m); - jl_method_table_invalidate(mt, oldentry, m, max_world); + jl_typemap_entry_t *replaced = NULL; + // then check what entries we replaced + oldvalue = get_intersect_matches(jl_atomic_load_relaxed(&mt->defs), newentry, &replaced); + int invalidated = 0; + if (replaced) { + oldvalue = (jl_value_t*)replaced; + invalidated = 1; + method_overwrite(newentry, replaced->func.method); + jl_method_table_invalidate(mt, replaced, max_world); } else { - oldvalue = get_intersect_matches(jl_atomic_load_relaxed(&mt->defs), newentry); - - int invalidated = 0; jl_method_t *const *d; size_t j, n; if (oldvalue == NULL) { @@ -2033,13 +2047,13 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method } } } - if (invalidated && _jl_debug_method_invalidation) { - jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)method); - loctag = jl_cstr_to_string("jl_method_table_insert"); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); - } - update_max_args(mt, type); } + if (invalidated && _jl_debug_method_invalidation) { + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)method); + loctag = jl_cstr_to_string("jl_method_table_insert"); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); + } + update_max_args(mt, type); JL_UNLOCK(&mt->writelock); JL_GC_POP(); } From 3919a89aaac87fab772721df9f7bbd9d647ce554 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 16 Mar 2023 12:37:26 -0400 Subject: [PATCH 322/775] fix problem with string_concatenation_hint_handler (#49016) Quiets an error on CI when running missing tests. Refs #45823 --- base/errorshow.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index e99253656d4e4..6530dd51d67c1 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -921,7 +921,7 @@ Experimental.register_error_hint(noncallable_number_hint_handler, MethodError) # (probably attempting concatenation) function string_concatenation_hint_handler(io, ex, arg_types, kwargs) @nospecialize - if (ex.f == +) && all(i -> i <: AbstractString, arg_types) + if (ex.f === +) && all(i -> i <: AbstractString, arg_types) print(io, "\nString concatenation is performed with ") printstyled(io, "*", color=:cyan) print(io, " (See also: https://docs.julialang.org/en/v1/manual/strings/#man-concatenation).") From 4ea2d2facb9f7056ab460ee8ee86a1205dc2ee39 Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Thu, 2 Mar 2023 13:22:00 -0500 Subject: [PATCH 323/775] Fix typo in GC stat name --- base/timing.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/timing.jl b/base/timing.jl index 4b3d72143e7f4..9e1b7863dd9f9 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -18,8 +18,8 @@ struct GC_Num full_sweep ::Cint max_pause ::Int64 max_memory ::Int64 - time_to_safepoint ::Int64 - max_time_to_safepointp ::Int64 + time_to_safepoint ::Int64 + max_time_to_safepoint ::Int64 sweep_time ::Int64 mark_time ::Int64 total_sweep_time ::Int64 From 34b7155fdd5ffa1781c6f8c766806d477faa3183 Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Thu, 2 Mar 2023 13:24:17 -0500 Subject: [PATCH 324/775] Add `total_time_to_safepoint` GC stat --- base/timing.jl | 1 + src/gc.c | 1 + src/gc.h | 1 + 3 files changed, 3 insertions(+) diff --git a/base/timing.jl b/base/timing.jl index 9e1b7863dd9f9..0afe65227d4a4 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -20,6 +20,7 @@ struct GC_Num max_memory ::Int64 time_to_safepoint ::Int64 max_time_to_safepoint ::Int64 + total_time_to_safepoint ::Int64 sweep_time ::Int64 mark_time ::Int64 total_sweep_time ::Int64 diff --git a/src/gc.c b/src/gc.c index 3afddc4afb3d8..298194b59b632 100644 --- a/src/gc.c +++ b/src/gc.c @@ -3132,6 +3132,7 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) if (duration > gc_num.max_time_to_safepoint) gc_num.max_time_to_safepoint = duration; gc_num.time_to_safepoint = duration; + gc_num.total_time_to_safepoint += duration; gc_invoke_callbacks(jl_gc_cb_pre_gc_t, gc_cblist_pre_gc, (collection)); diff --git a/src/gc.h b/src/gc.h index 930f7f3c30594..e0510d9bc3917 100644 --- a/src/gc.h +++ b/src/gc.h @@ -76,6 +76,7 @@ typedef struct { uint64_t max_memory; uint64_t time_to_safepoint; uint64_t max_time_to_safepoint; + uint64_t total_time_to_safepoint; uint64_t sweep_time; uint64_t mark_time; uint64_t total_sweep_time; From c1d1bde5918c07fc377b7942c50329487d3e70ce Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Fri, 17 Mar 2023 01:46:44 +0800 Subject: [PATCH 325/775] =?UTF-8?q?Unwrap=20`y::UnionALL`=20eagerly=20when?= =?UTF-8?q?=20`x<:y`=20if=20`x`=20isa=20=E2=88=80-var.=20(#49023)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Close #45874 --- src/subtype.c | 10 ++++++++++ test/subtype.jl | 12 +++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index dfb90df06074f..cb6c9531c33ca 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1296,6 +1296,16 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) // to other left-side variables, so using || here is safe. return subtype(xub, y, e, param) || subtype(x, ylb, e, param); } + if (jl_is_unionall(y)) { + jl_varbinding_t *xb = lookup(e, (jl_tvar_t*)x); + if (xb == NULL ? !e->ignore_free : !xb->right) { + // We'd better unwrap `y::UnionAll` eagerly if `x` isa ∀-var. + // This makes sure the following cases work correct: + // 1) `∀T <: Union{∃S, SomeType{P}} where {P}`: `S == Any` ==> `S >: T` + // 2) `∀T <: Union{∀T, SomeType{P}} where {P}`: + return subtype_unionall(x, (jl_unionall_t*)y, e, 1, param); + } + } return var_lt((jl_tvar_t*)x, y, e, param); } if (jl_is_typevar(y)) diff --git a/test/subtype.jl b/test/subtype.jl index dfd1a62d5f895..b03c577bf7194 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2282,7 +2282,7 @@ struct Z38497{T>:Int} <: Y38497{T} end @test only(intersection_env(Union{S, Matrix{Int}} where S<:Matrix, Matrix)[2]) isa TypeVar T46784{B<:Val, M<:AbstractMatrix} = Tuple{<:Union{B, <:Val{<:B}}, M, Union{AbstractMatrix{B}, AbstractMatrix{<:Vector{<:B}}}} @testintersect(T46784{T,S} where {T,S}, T46784, !Union{}) -@test_broken T46784 <: T46784{T,S} where {T,S} +@test T46784 <: T46784{T,S} where {T,S} #issue 36185 let S = Tuple{Type{T},Array{Union{T,Missing},N}} where {T,N}, @@ -2376,12 +2376,10 @@ abstract type P47654{A} end @testset "known subtype/intersect issue" begin #issue 45874 - # Causes a hang due to jl_critical_error calling back into malloc... - # let S = Pair{Val{P}, AbstractVector{<:Union{P,<:AbstractMatrix{P}}}} where P, - # T = Pair{Val{R}, AbstractVector{<:Union{P,<:AbstractMatrix{P}}}} where {P,R} - # @test_broken S <: T - # @test_broken typeintersect(S,T) === S - # end + let S = Pair{Val{P}, AbstractVector{<:Union{P,<:AbstractMatrix{P}}}} where P, + T = Pair{Val{R}, AbstractVector{<:Union{P,<:AbstractMatrix{P}}}} where {P,R} + @test S <: T + end #issue 41561 @test_broken typeintersect(Tuple{Vector{VT}, Vector{VT}} where {N1, VT<:AbstractVector{N1}}, From abae35c00f55446a1d31c72090829e61a744fd17 Mon Sep 17 00:00:00 2001 From: Henrique Ferrolho Date: Fri, 17 Mar 2023 04:38:56 +0000 Subject: [PATCH 326/775] Update inference.md (#49031) Update URLs to JuliaHub blog --- doc/src/devdocs/inference.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/devdocs/inference.md b/doc/src/devdocs/inference.md index 5c54ae3c7121d..b6614d060a0c8 100644 --- a/doc/src/devdocs/inference.md +++ b/doc/src/devdocs/inference.md @@ -6,8 +6,8 @@ In Julia compiler, "type inference" refers to the process of deducing the types values from the types of input values. Julia's approach to inference has been described in the blog posts below: 1. [Shows a simplified implementation of the data-flow analysis algorithm, that Julia's type inference routine is based on.](https://aviatesk.github.io/posts/data-flow-problem/) -2. [Gives a high level view of inference with a focus on its inter-procedural convergence guarantee.](https://juliacomputing.com/blog/2016/04/inference-convergence/) -3. [Explains a refinement on the algorithm introduced in 2.](https://juliacomputing.com/blog/2017/05/inference-converage2/) +2. [Gives a high level view of inference with a focus on its inter-procedural convergence guarantee.](https://info.juliahub.com/inference-convergence-algorithm-in-julia) +3. [Explains a refinement on the algorithm introduced in 2.](https://info.juliahub.com/inference-convergence-algorithm-in-julia-revisited) ## Debugging compiler.jl From 5e4669c7403c301985f35d2c8754b184cd73ab05 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Fri, 17 Mar 2023 00:07:05 -0500 Subject: [PATCH 327/775] Ensure accurate invalidation logging data (#48982) * Ensure accurate invalidation logging data This modifies #48841 to restore the required logging data. By collecting at least one additional match, we retain the possibility of identifying at least one trigger of invalidation. * Bump number of invalidation causes * Update src/staticdata_utils.c Co-authored-by: Jameson Nash --------- Co-authored-by: Jameson Nash --- src/staticdata_utils.c | 4 +++- test/precompile.jl | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 4ec60b26a11b4..df6bcfd61d9f6 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -869,8 +869,10 @@ static jl_array_t *jl_verify_edges(jl_array_t *targets, size_t minworld) assert(jl_is_array(expected)); int ambig = 0; // TODO: possibly need to included ambiguities too (for the optimizer correctness)? + // len + 1 is to allow us to log causes of invalidation (SnoopCompile's @snoopr) matches = jl_matching_methods((jl_tupletype_t*)sig, jl_nothing, - jl_array_len(expected), 0, minworld, &min_valid, &max_valid, &ambig); + _jl_debug_method_invalidation ? INT32_MAX : jl_array_len(expected), + 0, minworld, &min_valid, &max_valid, &ambig); if (matches == jl_nothing) { max_valid = 0; } diff --git a/test/precompile.jl b/test/precompile.jl index 1ee32cb39e37d..059e6943efa60 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -915,7 +915,7 @@ precompile_test_harness("code caching") do dir mi = m.specializations[1] @test hasvalid(mi, world) # was compiled with the new method - # Reporting test + # Reporting test (ensure SnoopCompile works) @test all(i -> isassigned(invalidations, i), eachindex(invalidations)) m = only(methods(MB.call_nbits)) for mi in m.specializations @@ -936,7 +936,7 @@ precompile_test_harness("code caching") do dir j = findfirst(==(tagbad), invalidations) @test invalidations[j-1] == "insert_backedges_callee" @test isa(invalidations[j-2], Type) - @test invalidations[j+1] === nothing || isa(invalidations[j+1], Vector{Any}) # [nbits(::UInt8)] + @test isa(invalidations[j+1], Vector{Any}) # [nbits(::UInt8)] m = only(methods(MB.map_nbits)) @test !hasvalid(m.specializations[1], world+1) # insert_backedges invalidations also trigger their backedges From 403e4e2adc4823f8fc7ab66aa6fadb70696d9ae6 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 17 Mar 2023 22:31:23 +0900 Subject: [PATCH 328/775] compiler: fix performance issue introduced in #48962 (#49036) `bb_rename_pred` was captured by #48962, so this commit fixes it up. This was successfully detected by: `JET.report_opt(CC.batch_inline!, (CC.IRCode,Vector{Pair{Int,Any}},Bool,CC.OptimizationParams))` --- base/compiler/ssair/ir.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index b1b7beec3f3c5..c0146c1dd2d23 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -1349,7 +1349,9 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr elseif isa(stmt, PhiNode) if cfg_transforms_enabled # Rename phi node edges - map!(i -> bb_rename_pred[i], stmt.edges, stmt.edges) + let bb_rename_pred=bb_rename_pred + map!(i::Int32 -> bb_rename_pred[i], stmt.edges, stmt.edges) + end # Remove edges and values associated with dead blocks. Entries in # `values` can be undefined when the phi node refers to something From 77231345182b23b8d5b4ba7d1d4eda4d31fa3b3a Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sat, 18 Mar 2023 02:48:26 +0900 Subject: [PATCH 329/775] irinterp: make sure to use `optimizer_lattice` when performing irinterp (#49024) While the base abstract interpretation routine uses `typeinf_lattice`, irinterp only needs `optimizer_lattice`. Therefore, there are unnecessary computations involved in handling `Conditional` in irinterp. This commit adds `irinterp::Bool` field to `NativeInterpreter`, indicating if a native interpreter is performing irinterp. This allows it to switch its lattice to `optimizer_lattice` during irinterp. --- base/compiler/abstractinterpretation.jl | 5 +- base/compiler/types.jl | 77 +++++++++++++++++-------- 2 files changed, 55 insertions(+), 27 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 0edba39607179..dad6afbdf6c73 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -979,8 +979,9 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, if code !== nothing ir = codeinst_to_ir(interp, code) if isa(ir, IRCode) - irsv = IRInterpretationState(interp, ir, mi, sv.world, arginfo.argtypes) - rt, nothrow = ir_abstract_constant_propagation(interp, irsv) + irinterp = switch_to_irinterp(interp) + irsv = IRInterpretationState(irinterp, ir, mi, sv.world, arginfo.argtypes) + rt, nothrow = ir_abstract_constant_propagation(irinterp, irsv) @assert !(rt isa Conditional || rt isa MustAlias) "invalid lattice element returned from IR interpretation" if !isa(rt, Type) || typeintersect(rt, Bool) === Union{} new_effects = Effects(result.effects; nothrow=nothrow) diff --git a/base/compiler/types.jl b/base/compiler/types.jl index 825ccca032948..89235d3a5dd91 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -312,51 +312,64 @@ function OptimizationParams( end """ - NativeInterpreter + NativeInterpreter <: AbstractInterpreter This represents Julia's native type inference algorithm and the Julia-LLVM codegen backend. -It contains many parameters used by the compilation pipeline. """ struct NativeInterpreter <: AbstractInterpreter - # Cache of inference results for this particular interpreter - cache::Vector{InferenceResult} # The world age we're working inside of world::UInt + # method table to lookup for during inference on this world age method_table::CachedMethodTable{InternalMethodTable} + # Cache of inference results for this particular interpreter + inf_cache::Vector{InferenceResult} + # Parameters for inference and optimization inf_params::InferenceParams opt_params::OptimizationParams - function NativeInterpreter(world::UInt = get_world_counter(); - inf_params = InferenceParams(), - opt_params = OptimizationParams(), - ) - cache = Vector{InferenceResult}() # Initially empty cache + # a boolean flag to indicate if this interpreter is performing semi concrete interpretation + irinterp::Bool +end + +function NativeInterpreter(world::UInt = get_world_counter(); + inf_params::InferenceParams = InferenceParams(), + opt_params::OptimizationParams = OptimizationParams()) + # Sometimes the caller is lazy and passes typemax(UInt). + # we cap it to the current world age + if world == typemax(UInt) + world = get_world_counter() + end - # Sometimes the caller is lazy and passes typemax(UInt). - # we cap it to the current world age - if world == typemax(UInt) - world = get_world_counter() - end + # If they didn't pass typemax(UInt) but passed something more subtly + # incorrect, fail out loudly. + @assert world <= get_world_counter() - method_table = CachedMethodTable(InternalMethodTable(world)) + method_table = CachedMethodTable(InternalMethodTable(world)) - # If they didn't pass typemax(UInt) but passed something more subtly - # incorrect, fail out loudly. - @assert world <= get_world_counter() + inf_cache = Vector{InferenceResult}() # Initially empty cache - return new(cache, world, method_table, inf_params, opt_params) - end + return NativeInterpreter(world, method_table, inf_cache, inf_params, opt_params, #=irinterp=#false) +end + +function NativeInterpreter(interp::NativeInterpreter; + world::UInt = interp.world, + method_table::CachedMethodTable{InternalMethodTable} = interp.method_table, + inf_cache::Vector{InferenceResult} = interp.inf_cache, + inf_params::InferenceParams = interp.inf_params, + opt_params::OptimizationParams = interp.opt_params, + irinterp::Bool = interp.irinterp) + return NativeInterpreter(world, method_table, inf_cache, inf_params, opt_params, irinterp) end # Quickly and easily satisfy the AbstractInterpreter API contract -InferenceParams(ni::NativeInterpreter) = ni.inf_params -OptimizationParams(ni::NativeInterpreter) = ni.opt_params -get_world_counter(ni::NativeInterpreter) = ni.world -get_inference_cache(ni::NativeInterpreter) = ni.cache -code_cache(ni::NativeInterpreter) = WorldView(GLOBAL_CI_CACHE, get_world_counter(ni)) +InferenceParams(interp::NativeInterpreter) = interp.inf_params +OptimizationParams(interp::NativeInterpreter) = interp.opt_params +get_world_counter(interp::NativeInterpreter) = interp.world +get_inference_cache(interp::NativeInterpreter) = interp.inf_cache +code_cache(interp::NativeInterpreter) = WorldView(GLOBAL_CI_CACHE, get_world_counter(interp)) """ already_inferred_quick_test(::AbstractInterpreter, ::MethodInstance) @@ -442,6 +455,20 @@ typeinf_lattice(::AbstractInterpreter) = InferenceLattice(BaseInferenceLattice.i ipo_lattice(::AbstractInterpreter) = InferenceLattice(IPOResultLattice.instance) optimizer_lattice(::AbstractInterpreter) = OptimizerLattice(SimpleInferenceLattice.instance) +typeinf_lattice(interp::NativeInterpreter) = interp.irinterp ? optimizer_lattice(interp) : InferenceLattice(BaseInferenceLattice.instance) +ipo_lattice(interp::NativeInterpreter) = interp.irinterp ? optimizer_lattice(interp) : InferenceLattice(IPOResultLattice.instance) +optimizer_lattice(interp::NativeInterpreter) = OptimizerLattice(SimpleInferenceLattice.instance) + +""" + switch_to_irinterp(interp::AbstractInterpreter) -> irinterp::AbstractInterpreter + +Optionally convert `interp` to new `irinterp::AbstractInterpreter` to perform semi-concrete +interpretation. `NativeInterpreter` uses this interface to switch its lattice to +`optimizer_lattice` during semi-concrete interpretation on `IRCode`. +""" +switch_to_irinterp(interp::AbstractInterpreter) = interp +switch_to_irinterp(interp::NativeInterpreter) = NativeInterpreter(interp; irinterp=true) + abstract type CallInfo end @nospecialize From ebdb187f3677324f3be16801a4ba38ce56102806 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 17 Mar 2023 18:11:38 -0400 Subject: [PATCH 330/775] avoid creating a few MethodInstances for Missing (#49015) Union-typed call edges are a little bit more expensive than other edges, but we can easily avoid calling these functions when we know they are not applicable, since this is one of the most common to observe. --- base/promotion.jl | 7 ++++++- base/sort.jl | 11 ++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/base/promotion.jl b/base/promotion.jl index 993f0be6c0924..b9ab5ed7254f7 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -172,7 +172,12 @@ function promote_typejoin(@nospecialize(a), @nospecialize(b)) c = typejoin(_promote_typesubtract(a), _promote_typesubtract(b)) return Union{a, b, c}::Type end -_promote_typesubtract(@nospecialize(a)) = typesplit(a, Union{Nothing, Missing}) +_promote_typesubtract(@nospecialize(a)) = + a === Any ? a : + a >: Union{Nothing, Missing} ? typesplit(a, Union{Nothing, Missing}) : + a >: Nothing ? typesplit(a, Nothing) : + a >: Missing ? typesplit(a, Missing) : + a function promote_typejoin_union(::Type{T}) where T if T === Union{} diff --git a/base/sort.jl b/base/sort.jl index cbbcf948b8b4c..1041c7e4288b3 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -578,19 +578,20 @@ elements that are not function _sort!(v::AbstractVector, a::MissingOptimization, o::Ordering, kw) @getkw lo hi - if nonmissingtype(eltype(v)) != eltype(v) && o isa DirectOrdering + if o isa DirectOrdering && eltype(v) >: Missing && nonmissingtype(eltype(v)) != eltype(v) lo, hi = send_to_end!(ismissing, v, o; lo, hi) _sort!(WithoutMissingVector(v, unsafe=true), a.next, o, (;kw..., lo, hi)) - elseif eltype(v) <: Integer && o isa Perm && o.order isa DirectOrdering && - nonmissingtype(eltype(o.data)) != eltype(o.data) && + elseif o isa Perm && o.order isa DirectOrdering && eltype(v) <: Integer && + eltype(o.data) >: Missing && nonmissingtype(eltype(o.data)) != eltype(o.data) && all(i === j for (i,j) in zip(v, eachindex(o.data))) # TODO make this branch known at compile time # This uses a custom function because we need to ensure stability of both sides and # we can assume v is equal to eachindex(o.data) which allows a copying partition # without allocations. lo_i, hi_i = lo, hi - for (i,x) in zip(eachindex(o.data), o.data) - if ismissing(x) == (o.order == Reverse) # should i go at the beginning? + for i in eachindex(o.data) # equal to copy(v) + x = o.data[i] + if ismissing(x) == (o.order == Reverse) # should x go at the beginning/end? v[lo_i] = i lo_i += 1 else From 4486bc40b42b350260bd4016297dd3adf2186651 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 17 Mar 2023 18:17:29 -0400 Subject: [PATCH 331/775] opaque_closure: Properly set world for OC from inferred CodeInfo (#49029) For OpaqueClosures constructed from inferred code, there is only one valid age. We were incorrectly setting the primary world age of the method to `1`, rather than the construction world age of the opaque closure, causing codegen to fail to emit direct calls for :invoke'd statements. --- src/opaque_closure.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/opaque_closure.c b/src/opaque_closure.c index 6772290c8ab89..1cc7bd23b6c1a 100644 --- a/src/opaque_closure.c +++ b/src/opaque_closure.c @@ -64,7 +64,7 @@ static jl_opaque_closure_t *new_opaque_closure(jl_tupletype_t *argt, jl_value_t JL_GC_PROMISE_ROOTED(oc_type); jl_method_instance_t *mi = jl_specializations_get_linfo(source, sigtype, jl_emptysvec); - size_t world = jl_atomic_load_acquire(&jl_world_counter); + size_t world = jl_current_task->world_age; jl_code_instance_t *ci = NULL; if (do_compile) ci = jl_compile_method_internal(mi, world); @@ -127,12 +127,14 @@ JL_DLLEXPORT jl_opaque_closure_t *jl_new_opaque_closure_from_code_info(jl_tuplet JL_GC_PUSH3(&root, &sigtype, &inst); root = jl_box_long(lineno); root = jl_new_struct(jl_linenumbernode_type, root, file); - root = (jl_value_t*)jl_make_opaque_closure_method(mod, jl_nothing, nargs, root, ci, isva); + jl_method_t *meth = jl_make_opaque_closure_method(mod, jl_nothing, nargs, root, ci, isva); + root = (jl_value_t*)meth; + meth->primary_world = jl_current_task->world_age; sigtype = prepend_type(jl_typeof(env), argt); jl_method_instance_t *mi = jl_specializations_get_linfo((jl_method_t*)root, sigtype, jl_emptysvec); inst = jl_new_codeinst(mi, rt_ub, NULL, (jl_value_t*)ci, - 0, ((jl_method_t*)root)->primary_world, -1, 0, 0, jl_nothing, 0); + 0, meth->primary_world, -1, 0, 0, jl_nothing, 0); jl_mi_cache_insert(mi, inst); jl_opaque_closure_t *oc = new_opaque_closure(argt, rt_lb, rt_ub, root, env, do_compile); From 0a9abc1919a69b7b4d668e3f841b53b186448e41 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 18 Mar 2023 00:56:26 -0400 Subject: [PATCH 332/775] Allow external lattice elements to properly union split (#49030) Currently `MustAlias` is the only lattice element that is allowed to widen to union types. However, there are others in external packages. Expand the support we have for this in order to allow union splitting of lattice elements. Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> --- base/compiler/abstractinterpretation.jl | 13 ++++++------ base/compiler/abstractlattice.jl | 4 ++++ base/compiler/tfuncs.jl | 2 +- base/compiler/typelattice.jl | 2 ++ base/compiler/typeutils.jl | 28 +++++++++++++------------ test/compiler/inference.jl | 14 ++++++------- 6 files changed, 36 insertions(+), 27 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index dad6afbdf6c73..2087ad96f27ce 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -59,7 +59,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), # as we may want to concrete-evaluate this frame in cases when there are # no overlayed calls, try an additional effort now to check if this call # isn't overlayed rather than just handling it conservatively - matches = find_matching_methods(arginfo.argtypes, atype, method_table(interp), + matches = find_matching_methods(typeinf_lattice(interp), arginfo.argtypes, atype, method_table(interp), InferenceParams(interp).max_union_splitting, max_methods) if !isa(matches, FailedMethodMatch) nonoverlayed = matches.nonoverlayed @@ -75,7 +75,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), end argtypes = arginfo.argtypes - matches = find_matching_methods(argtypes, atype, method_table(interp), + matches = find_matching_methods(typeinf_lattice(interp), argtypes, atype, method_table(interp), InferenceParams(interp).max_union_splitting, max_methods) if isa(matches, FailedMethodMatch) add_remark!(interp, sv, matches.reason) @@ -273,11 +273,12 @@ struct UnionSplitMethodMatches end any_ambig(m::UnionSplitMethodMatches) = any(any_ambig, m.info.matches) -function find_matching_methods(argtypes::Vector{Any}, @nospecialize(atype), method_table::MethodTableView, +function find_matching_methods(𝕃::AbstractLattice, + argtypes::Vector{Any}, @nospecialize(atype), method_table::MethodTableView, max_union_splitting::Int, max_methods::Int) # NOTE this is valid as far as any "constant" lattice element doesn't represent `Union` type - if 1 < unionsplitcost(argtypes) <= max_union_splitting - split_argtypes = switchtupleunion(argtypes) + if 1 < unionsplitcost(𝕃, argtypes) <= max_union_splitting + split_argtypes = switchtupleunion(𝕃, argtypes) infos = MethodMatchInfo[] applicable = Any[] applicable_argtypes = Vector{Any}[] # arrays like `argtypes`, including constants, for each match @@ -1496,7 +1497,7 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, si:: end res = Union{} nargs = length(aargtypes) - splitunions = 1 < unionsplitcost(aargtypes) <= InferenceParams(interp).max_apply_union_enum + splitunions = 1 < unionsplitcost(typeinf_lattice(interp), aargtypes) <= InferenceParams(interp).max_apply_union_enum ctypes = [Any[aft]] infos = Vector{MaybeAbstractIterationInfo}[MaybeAbstractIterationInfo[]] effects = EFFECTS_TOTAL diff --git a/base/compiler/abstractlattice.jl b/base/compiler/abstractlattice.jl index f578ec8d6f60d..a84050816cb21 100644 --- a/base/compiler/abstractlattice.jl +++ b/base/compiler/abstractlattice.jl @@ -293,6 +293,10 @@ has_mustalias(𝕃::AbstractLattice) = has_mustalias(widenlattice(𝕃)) has_mustalias(::AnyMustAliasesLattice) = true has_mustalias(::JLTypeLattice) = false +has_extended_unionsplit(𝕃::AbstractLattice) = has_extended_unionsplit(widenlattice(𝕃)) +has_extended_unionsplit(::AnyMustAliasesLattice) = true +has_extended_unionsplit(::JLTypeLattice) = false + # Curried versions ⊑(lattice::AbstractLattice) = (@nospecialize(a), @nospecialize(b)) -> ⊑(lattice, a, b) ⊏(lattice::AbstractLattice) = (@nospecialize(a), @nospecialize(b)) -> ⊏(lattice, a, b) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 41da17c19d6d2..a89d9b89826b5 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -2542,7 +2542,7 @@ function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, isvarargtype(argtypes[2]) && return CallMeta(Bool, EFFECTS_UNKNOWN, NoCallInfo()) argtypes = argtypes[2:end] atype = argtypes_to_type(argtypes) - matches = find_matching_methods(argtypes, atype, method_table(interp), + matches = find_matching_methods(typeinf_lattice(interp), argtypes, atype, method_table(interp), InferenceParams(interp).max_union_splitting, max_methods) if isa(matches, FailedMethodMatch) rt = Bool # too many matches to analyze diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index 33d4d37e9c936..23f39d8b44f5e 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -120,6 +120,8 @@ end MustAlias(var::SlotNumber, @nospecialize(vartyp), fldidx::Int, @nospecialize(fldtyp)) = MustAlias(slot_id(var), vartyp, fldidx, fldtyp) +_uniontypes(x::MustAlias, ts) = _uniontypes(widenconst(x), ts) + """ alias::InterMustAlias diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index c94bc0ca2aa75..293ef5797888b 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -165,7 +165,7 @@ function typesubtract(@nospecialize(a), @nospecialize(b), max_union_splitting::I if ub isa DataType if a.name === ub.name === Tuple.name && length(a.parameters) == length(ub.parameters) - if 1 < unionsplitcost(a.parameters) <= max_union_splitting + if 1 < unionsplitcost(JLTypeLattice(), a.parameters) <= max_union_splitting ta = switchtupleunion(a) return typesubtract(Union{ta...}, b, 0) elseif b isa DataType @@ -227,12 +227,11 @@ end # or outside of the Tuple/Union nesting, though somewhat more expensive to be # outside than inside because the representation is larger (because and it # informs the callee whether any splitting is possible). -function unionsplitcost(argtypes::Union{SimpleVector,Vector{Any}}) +function unionsplitcost(𝕃::AbstractLattice, argtypes::Union{SimpleVector,Vector{Any}}) nu = 1 max = 2 for ti in argtypes - # TODO remove this to implement callsite refinement of MustAlias - if isa(ti, MustAlias) && isa(widenconst(ti), Union) + if has_extended_unionsplit(𝕃) && !isvarargtype(ti) ti = widenconst(ti) end if isa(ti, Union) @@ -252,12 +251,12 @@ end # and `Union{return...} == ty` function switchtupleunion(@nospecialize(ty)) tparams = (unwrap_unionall(ty)::DataType).parameters - return _switchtupleunion(Any[tparams...], length(tparams), [], ty) + return _switchtupleunion(JLTypeLattice(), Any[tparams...], length(tparams), [], ty) end -switchtupleunion(argtypes::Vector{Any}) = _switchtupleunion(argtypes, length(argtypes), [], nothing) +switchtupleunion(𝕃::AbstractLattice, argtypes::Vector{Any}) = _switchtupleunion(𝕃, argtypes, length(argtypes), [], nothing) -function _switchtupleunion(t::Vector{Any}, i::Int, tunion::Vector{Any}, @nospecialize(origt)) +function _switchtupleunion(𝕃::AbstractLattice, t::Vector{Any}, i::Int, tunion::Vector{Any}, @nospecialize(origt)) if i == 0 if origt === nothing push!(tunion, copy(t)) @@ -268,17 +267,20 @@ function _switchtupleunion(t::Vector{Any}, i::Int, tunion::Vector{Any}, @nospeci else origti = ti = t[i] # TODO remove this to implement callsite refinement of MustAlias - if isa(ti, MustAlias) && isa(widenconst(ti), Union) - ti = widenconst(ti) - end if isa(ti, Union) - for ty in uniontypes(ti::Union) + for ty in uniontypes(ti) + t[i] = ty + _switchtupleunion(𝕃, t, i - 1, tunion, origt) + end + t[i] = origti + elseif has_extended_unionsplit(𝕃) && !isa(ti, Const) && !isvarargtype(ti) && isa(widenconst(ti), Union) + for ty in uniontypes(ti) t[i] = ty - _switchtupleunion(t, i - 1, tunion, origt) + _switchtupleunion(𝕃, t, i - 1, tunion, origt) end t[i] = origti else - _switchtupleunion(t, i - 1, tunion, origt) + _switchtupleunion(𝕃, t, i - 1, tunion, origt) end end return tunion diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index e6be51542c205..8f8598c82bded 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2944,11 +2944,11 @@ end # issue #28356 # unit test to make sure countunionsplit overflows gracefully # we don't care what number is returned as long as it's large -@test Core.Compiler.unionsplitcost(Any[Union{Int32, Int64} for i=1:80]) > 100000 -@test Core.Compiler.unionsplitcost(Any[Union{Int8, Int16, Int32, Int64}]) == 2 -@test Core.Compiler.unionsplitcost(Any[Union{Int8, Int16, Int32, Int64}, Union{Int8, Int16, Int32, Int64}, Int8]) == 8 -@test Core.Compiler.unionsplitcost(Any[Union{Int8, Int16, Int32, Int64}, Union{Int8, Int16, Int32}, Int8]) == 6 -@test Core.Compiler.unionsplitcost(Any[Union{Int8, Int16, Int32}, Union{Int8, Int16, Int32, Int64}, Int8]) == 6 +@test Core.Compiler.unionsplitcost(Core.Compiler.JLTypeLattice(), Any[Union{Int32, Int64} for i=1:80]) > 100000 +@test Core.Compiler.unionsplitcost(Core.Compiler.JLTypeLattice(), Any[Union{Int8, Int16, Int32, Int64}]) == 2 +@test Core.Compiler.unionsplitcost(Core.Compiler.JLTypeLattice(), Any[Union{Int8, Int16, Int32, Int64}, Union{Int8, Int16, Int32, Int64}, Int8]) == 8 +@test Core.Compiler.unionsplitcost(Core.Compiler.JLTypeLattice(), Any[Union{Int8, Int16, Int32, Int64}, Union{Int8, Int16, Int32}, Int8]) == 6 +@test Core.Compiler.unionsplitcost(Core.Compiler.JLTypeLattice(), Any[Union{Int8, Int16, Int32}, Union{Int8, Int16, Int32, Int64}, Int8]) == 6 # make sure compiler doesn't hang in union splitting @@ -3949,13 +3949,13 @@ end # argtypes let - tunion = Core.Compiler.switchtupleunion(Any[Union{Int32,Int64}, Core.Const(nothing)]) + tunion = Core.Compiler.switchtupleunion(Core.Compiler.ConstsLattice(), Any[Union{Int32,Int64}, Core.Const(nothing)]) @test length(tunion) == 2 @test Any[Int32, Core.Const(nothing)] in tunion @test Any[Int64, Core.Const(nothing)] in tunion end let - tunion = Core.Compiler.switchtupleunion(Any[Union{Int32,Int64}, Union{Float32,Float64}, Core.Const(nothing)]) + tunion = Core.Compiler.switchtupleunion(Core.Compiler.ConstsLattice(), Any[Union{Int32,Int64}, Union{Float32,Float64}, Core.Const(nothing)]) @test length(tunion) == 4 @test Any[Int32, Float32, Core.Const(nothing)] in tunion @test Any[Int32, Float64, Core.Const(nothing)] in tunion From 66c58505e9083a53e29f7bc1d476c14a847c8f7e Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Sun, 19 Mar 2023 11:18:53 -0400 Subject: [PATCH 333/775] Remove SparseArrays from system image (#48979) * Remove SparseArrays from system image and add it to cache_stdlibs.jl --- base/sysimg.jl | 1 - contrib/cache_stdlibs.jl | 7 +++++-- test/precompile.jl | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/base/sysimg.jl b/base/sysimg.jl index 0d89754e7c11c..7d3826eb13bdc 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -60,7 +60,6 @@ let :InteractiveUtils, :LibGit2, :Profile, - :SparseArrays, :UUIDs, # 3-depth packages diff --git a/contrib/cache_stdlibs.jl b/contrib/cache_stdlibs.jl index 09cf2ba0dcb42..37f002e043dde 100644 --- a/contrib/cache_stdlibs.jl +++ b/contrib/cache_stdlibs.jl @@ -23,13 +23,16 @@ stdlibs = [ # 3-depth packages :LibGit2_jll, + # 4-depth packages + :SparseArrays, + # 7-depth packages :LLD_jll, - :SuiteSparse_jll, + :SuiteSparse, # 9-depth packages :Statistics, - :SuiteSparse, + :SuiteSparse_jll, ] depot = abspath(Sys.BINDIR, "..", "share", "julia") diff --git a/test/precompile.jl b/test/precompile.jl index 059e6943efa60..eb1666355d701 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -399,7 +399,7 @@ precompile_test_harness(false) do dir :LazyArtifacts, :LibCURL, :LibCURL_jll, :LibGit2, :Libdl, :LinearAlgebra, :Logging, :Markdown, :Mmap, :MozillaCACerts_jll, :NetworkOptions, :OpenBLAS_jll, :Pkg, :Printf, :Profile, :p7zip_jll, :REPL, :Random, :SHA, :Serialization, :SharedArrays, :Sockets, - :SparseArrays, :TOML, :Tar, :Test, :UUIDs, :Unicode, + :TOML, :Tar, :Test, :UUIDs, :Unicode, :nghttp2_jll] ), ) From e8774637a3a65edd55edf0e9f259762e95ea9c47 Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Mon, 20 Mar 2023 08:39:14 -0400 Subject: [PATCH 334/775] Exclude 'build' from 'patch' in version parsing So as to not break `ld` on Darwin. --- Make.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Make.inc b/Make.inc index cb062a46a68cb..11f5fc7a88ce4 100644 --- a/Make.inc +++ b/Make.inc @@ -178,7 +178,7 @@ endif JULIA_VERSION := $(shell cat $(JULIAHOME)/VERSION) JULIA_MAJOR_VERSION := $(shell echo $(JULIA_VERSION) | cut -d'-' -f 1 | cut -d'.' -f 1) JULIA_MINOR_VERSION := $(shell echo $(JULIA_VERSION) | cut -d'-' -f 1 | cut -d'.' -f 2) -JULIA_PATCH_VERSION := $(shell echo $(JULIA_VERSION) | cut -d'-' -f 1 | cut -d'.' -f 3) +JULIA_PATCH_VERSION := $(shell echo $(JULIA_VERSION) | cut -d'-' -f 1 | cut -d'+' -f 1 | cut -d'.' -f 3) # libjulia's SONAME will follow the format libjulia.so.$(SOMAJOR). Before v1.0.0, # SOMAJOR will be a two-decimal value, e.g. libjulia.so.0.5, whereas at and beyond From 114e675fc26c5bfda5ac48f5efbe4fd03ec59116 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Mon, 20 Mar 2023 13:49:16 +0100 Subject: [PATCH 335/775] Dict: rehesh when there are too many tombstones (#49045) --- base/dict.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/dict.jl b/base/dict.jl index b3d2527aaa6d9..66329e9184646 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -351,8 +351,8 @@ ht_keyindex2!(h::Dict, key) = ht_keyindex2_shorthash!(h, key)[1] sz = length(h.keys) # Rehash now if necessary - if h.ndel >= ((3*sz)>>2) || h.count*3 > sz*2 - # > 3/4 deleted or > 2/3 full + if (h.count + h.ndel)*3 > sz*2 + # > 2/3 full (including tombstones) rehash!(h, h.count > 64000 ? h.count*2 : h.count*4) end nothing From 6337cf6a137f4a89e94661b8fcf9d33f1798bb96 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 20 Mar 2023 13:34:06 -0400 Subject: [PATCH 336/775] datatype: handle concrete type intersections that happen (#49017) This is actually very similar to the current might_intersect_concrete, but for subtyping and memoized. It replaces cached_by_hash, which was a confusingly-named incomplete prior work towards this. This gives NamedTuple{(:names)} hashes, which lets them go into the faster type lookup tables. This is a fairly common type for some packages to create, so we need this to avoid polluting our cache tables. Reverts efafd8388675d65096e0f088ddfe96f4e8077567, since these types have no intersection, the morespecific algorithm is no longer required to have any opinion on them. --- src/datatype.c | 2 +- src/jl_exported_funcs.inc | 1 - src/jltypes.c | 241 ++++++++++++++++++++++++++------------ src/julia.h | 3 +- src/julia_internal.h | 1 + src/subtype.c | 23 ++-- test/specificity.jl | 2 +- 7 files changed, 176 insertions(+), 97 deletions(-) diff --git a/src/datatype.c b/src/datatype.c index ae1e3029aa0e1..91fd24495f299 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -103,7 +103,7 @@ jl_datatype_t *jl_new_uninitialized_datatype(void) t->isprimitivetype = 0; t->zeroinit = 0; t->has_concrete_subtype = 1; - t->cached_by_hash = 0; + t->maybe_subtype_of_cache = 1; t->ismutationfree = 0; t->isidentityfree = 0; t->name = NULL; diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index d3acb7d2ad92a..863f5d5686fb7 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -487,7 +487,6 @@ XX(jl_typename_str) \ XX(jl_typeof_str) \ XX(jl_types_equal) \ - XX(jl_type_equality_is_identity) \ XX(jl_type_error) \ XX(jl_type_error_rt) \ XX(jl_type_intersection) \ diff --git a/src/jltypes.c b/src/jltypes.c index 0b7ef9424b6bf..1b602e58b215a 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -581,8 +581,8 @@ static int typekey_eq(jl_datatype_t *tt, jl_value_t **key, size_t n) if (tt->name == jl_type_typename) { // for Type{T}, require `typeof(T)` to match also, to avoid incorrect // dispatch from changing the type of something. - // this should work because `Type`s don't have uids, and aren't the - // direct tags of values so we don't rely on pointer equality. + // this should work because `Type`s don't need unique pointers, and aren't the + // direct tags of values (concrete) so we don't rely on pointer equality. jl_value_t *kj = key[0]; jl_value_t *tj = jl_tparam0(tt); return (kj == tj || (jl_typeof(tj) == jl_typeof(kj) && jl_types_equal(tj, kj))); @@ -591,11 +591,14 @@ static int typekey_eq(jl_datatype_t *tt, jl_value_t **key, size_t n) jl_value_t *kj = key[j]; jl_value_t *tj = jl_svecref(tt->parameters, j); if (tj != kj) { - // require exact same Type{T}. see e.g. issue #22842 - if (jl_is_type_type(tj) || jl_is_type_type(kj)) - return 0; - if ((jl_is_concrete_type(tj) || jl_is_concrete_type(kj)) && - jl_type_equality_is_identity(tj, kj)) + if (tt->name == jl_tuple_typename) { + // require exact same Type{T} in covariant context. see e.g. issue #22842 + // this should work because `Tuple{Type}`s don't need unique pointers, and aren't the + // direct tags of values (concrete) so we don't rely on pointer equality. + if (jl_is_type_type(tj) || jl_is_type_type(kj)) + return 0; + } + if (jl_type_equality_is_identity(tj, kj)) return 0; if (!jl_types_equal(tj, kj)) return 0; @@ -905,16 +908,88 @@ jl_datatype_t *jl_lookup_cache_type_(jl_datatype_t *type) return (jl_datatype_t*)lookup_type(type->name, key, n); } -JL_DLLEXPORT int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2) +// compute whether kj might actually be a subtype of something in the cache +// (which otherwise would normally be comparable with pointer-egal) +static int maybe_subtype_of_cache(jl_value_t *kj, int covariant) JL_NOTSAFEPOINT { - if (t1 == t2) + jl_value_t *uw = jl_is_unionall(kj) ? jl_unwrap_unionall(kj) : kj; + if (jl_is_datatype(uw)) { + jl_datatype_t *dt = (jl_datatype_t*)uw; + return dt->maybe_subtype_of_cache; + } + else if (jl_is_uniontype(uw)) { + int ca = maybe_subtype_of_cache(((jl_uniontype_t*)uw)->a, covariant); + int cb = maybe_subtype_of_cache(((jl_uniontype_t*)uw)->b, covariant); + return ca && cb; + } + else if (uw == jl_bottom_type) { return 1; - if (!jl_is_datatype(t1) || !jl_is_datatype(t2)) - return 0; - jl_datatype_t *dt1 = (jl_datatype_t *) t1; - jl_datatype_t *dt2 = (jl_datatype_t *) t2; + } + else if (jl_is_typevar(uw) && !covariant) { // assume Tuple's bounds are always degenerate + // TODO: improve this bound if we can prove that typeintersect(lb,ub) is a leaftype + jl_tvar_t *tv = (jl_tvar_t*)uw; + return tv->lb == tv->ub || + tv->lb != jl_bottom_type; + } + return 1; +} + +// compute whether kj might have a supertype which is actually concrete +static int has_concrete_supertype(jl_value_t *kj) JL_NOTSAFEPOINT +{ + jl_value_t *uw = jl_is_unionall(kj) ? jl_unwrap_unionall(kj) : kj; + if (jl_is_datatype(uw)) { + jl_datatype_t *dt = (jl_datatype_t*)uw; + if (dt->name->abstract && dt->name != jl_type_typename) + return 0; + if (!dt->maybe_subtype_of_cache) + return 0; + if (dt->name == jl_tuple_typename) { + // check tuple parameters recursively for has_concrete_supertype + size_t i, n = jl_nparams(dt); + for (i = 0; i < n; i++) { + jl_value_t *p = jl_tparam(dt, i); + if (jl_is_vararg(p)) + p = jl_unwrap_vararg(p); + if (!has_concrete_supertype(p)) + return 0; + } + } + return 1; + } + else if (jl_is_uniontype(uw)) { + int ca = has_concrete_supertype(((jl_uniontype_t*)uw)->a); + int cb = has_concrete_supertype(((jl_uniontype_t*)uw)->b); + return ca && cb; + } + else if (uw == jl_bottom_type) { + return 1; + } + else if (jl_is_typevar(uw)) { + jl_tvar_t *tv = (jl_tvar_t*)uw; + return has_concrete_supertype(tv->ub); + } + return 0; +} - return dt1->cached_by_hash == dt2->cached_by_hash; +int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2) JL_NOTSAFEPOINT +{ + int c1 = jl_is_concrete_type(t1); + int c2 = jl_is_concrete_type(t2); + if (c1 && c2) { + if (((jl_datatype_t*)t1)->name != jl_tuple_typename) + return 1; + if (((jl_datatype_t*)t2)->name != jl_tuple_typename) + return 1; + if (((jl_datatype_t*)t1)->has_concrete_subtype && ((jl_datatype_t*)t2)->has_concrete_subtype) + return 1; + // e.g. Tuple{Union{}} and Tuple{Int} are both concrete! + } + if (c1 && !has_concrete_supertype(t2)) + return 1; + if (c2 && !has_concrete_supertype(t1)) + return 1; + return 0; } // type instantiation @@ -1147,7 +1222,7 @@ static jl_value_t *lookup_type_stack(jl_typestack_t *stack, jl_datatype_t *tt, s } // stable numbering for types--starts with name->hash, then falls back to objectid -// sets failed if the hash value isn't stable (if not set on entry) +// sets *failed if the hash value isn't stable (if this param not set on entry) static unsigned type_hash(jl_value_t *kj, int *failed) JL_NOTSAFEPOINT { jl_value_t *uw = jl_is_unionall(kj) ? jl_unwrap_unionall(kj) : kj; @@ -1159,32 +1234,21 @@ static unsigned type_hash(jl_value_t *kj, int *failed) JL_NOTSAFEPOINT *failed = 1; return 0; } + // compute a hash now, only for the parent object we are putting in the cache hash = typekey_hash(dt->name, jl_svec_data(dt->parameters), jl_svec_len(dt->parameters), *failed); } return hash; } else if (jl_is_typevar(uw)) { - if (!*failed) { - *failed = 1; - return 0; - } // ignore var and lb, since those might get normalized out in equality testing return type_hash(((jl_tvar_t*)uw)->ub, failed); } - else if (jl_is_vararg(uw)) { - if (!*failed) { - *failed = 1; - return 0; - } - jl_vararg_t *vm = (jl_vararg_t *)uw; - // 0x064eeaab is just a randomly chosen constant - return bitmix(type_hash(vm->T ? vm->T : (jl_value_t*)jl_any_type, failed), vm->N ? type_hash(vm->N, failed) : 0x064eeaab); - } else if (jl_is_uniontype(uw)) { if (!*failed) { *failed = 1; return 0; } + // compute a hash now, only for the parent object we are putting in the cache unsigned hasha = type_hash(((jl_uniontype_t*)uw)->a, failed); unsigned hashb = type_hash(((jl_uniontype_t*)uw)->b, failed); // use a associative mixing function, with well-defined overflow @@ -1204,7 +1268,18 @@ static unsigned typekey_hash(jl_typename_t *tn, jl_value_t **key, size_t n, int unsigned hash = 3; int failed = nofail; for (j = 0; j < n; j++) { - hash = bitmix(type_hash(key[j], &failed), hash); + jl_value_t *p = key[j]; + if (jl_is_vararg(p)) { + jl_vararg_t *vm = (jl_vararg_t*)p; + if (!nofail && vm->N) + return 0; + // 0x064eeaab is just a randomly chosen constant + hash = bitmix(vm->N ? type_hash(vm->N, &failed) : 0x064eeaab, hash); + if (failed && !nofail) + return 0; + p = vm->T ? vm->T : (jl_value_t*)jl_any_type; + } + hash = bitmix(type_hash(p, &failed), hash); if (failed && !nofail) return 0; } @@ -1237,6 +1312,7 @@ void jl_precompute_memoized_dt(jl_datatype_t *dt, int cacheable) { int istuple = (dt->name == jl_tuple_typename); dt->hasfreetypevars = 0; + dt->maybe_subtype_of_cache = 1; dt->isconcretetype = !dt->name->abstract; dt->isdispatchtuple = istuple; size_t i, l = jl_nparams(dt); @@ -1247,30 +1323,38 @@ void jl_precompute_memoized_dt(jl_datatype_t *dt, int cacheable) if (dt->hasfreetypevars) dt->isconcretetype = 0; } - if (istuple && dt->isconcretetype) - dt->isconcretetype = (jl_is_datatype(p) && ((jl_datatype_t*)p)->isconcretetype) || p == jl_bottom_type; - if (dt->isdispatchtuple) { - dt->isdispatchtuple = jl_is_datatype(p) && - ((!jl_is_kind(p) && ((jl_datatype_t*)p)->isconcretetype) || - (p == (jl_value_t*)jl_typeofbottom_type) || // == Type{Union{}}, so needs to be consistent - (((jl_datatype_t*)p)->name == jl_type_typename && !((jl_datatype_t*)p)->hasfreetypevars)); + if (istuple) { + if (dt->isconcretetype) + dt->isconcretetype = (jl_is_datatype(p) && ((jl_datatype_t*)p)->isconcretetype) || p == jl_bottom_type; + if (dt->isdispatchtuple) { + dt->isdispatchtuple = jl_is_datatype(p) && + ((!jl_is_kind(p) && ((jl_datatype_t*)p)->isconcretetype) || + (p == (jl_value_t*)jl_typeofbottom_type) || // == Type{Union{}}, so needs to be consistent + (((jl_datatype_t*)p)->name == jl_type_typename && !((jl_datatype_t*)p)->hasfreetypevars)); + } } + if (jl_is_vararg(p)) + p = ((jl_vararg_t*)p)->T; if (istuple && dt->has_concrete_subtype) { - if (jl_is_vararg(p)) - p = ((jl_vararg_t*)p)->T; - // tuple types like Tuple{:x} cannot have instances + // tuple types like Tuple{:x} and Tuple{Union{}} cannot have instances if (p && !jl_is_type(p) && !jl_is_typevar(p)) dt->has_concrete_subtype = 0; + if (p == jl_bottom_type) + dt->has_concrete_subtype = 0; + } + if (dt->maybe_subtype_of_cache) { + dt->maybe_subtype_of_cache = !p || maybe_subtype_of_cache(p, istuple) || !jl_has_free_typevars(p); } } + assert(dt->isconcretetype || dt->isdispatchtuple ? dt->maybe_subtype_of_cache : 1); if (dt->name == jl_type_typename) { - cacheable = 0; // the cache for Type ignores parameter normalization, so it can't be used as a regular hash + cacheable = 0; // n.b. the cache for Type ignores parameter normalization, so it can't be used to make a stable hash value jl_value_t *p = jl_tparam(dt, 0); if (!jl_is_type(p) && !jl_is_typevar(p)) // Type{v} has no subtypes, if v is not a Type dt->has_concrete_subtype = 0; + dt->maybe_subtype_of_cache = 1; } dt->hash = typekey_hash(dt->name, jl_svec_data(dt->parameters), l, cacheable); - dt->cached_by_hash = cacheable ? (typekey_hash(dt->name, jl_svec_data(dt->parameters), l, 0) != 0) : (dt->hash != 0); } static void check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, size_t np) @@ -2046,8 +2130,9 @@ void jl_init_types(void) JL_GC_DISABLED jl_nonfunction_mt = jl_any_type->name->mt; jl_any_type->name->mt = NULL; - jl_type_type = (jl_unionall_t*)jl_new_abstracttype((jl_value_t*)jl_symbol("Type"), core, jl_any_type, jl_emptysvec); - jl_type_typename = ((jl_datatype_t*)jl_type_type)->name; + jl_datatype_t *type_type = jl_new_abstracttype((jl_value_t*)jl_symbol("Type"), core, jl_any_type, jl_emptysvec); + jl_type_type = (jl_unionall_t*)type_type; + jl_type_typename = type_type->name; jl_type_type_mt = jl_new_method_table(jl_type_typename->name, core); jl_type_typename->mt = jl_type_type_mt; @@ -2055,7 +2140,7 @@ void jl_init_types(void) JL_GC_DISABLED // NOTE: types are not actually mutable, but we want to ensure they are heap-allocated with stable addresses jl_datatype_type->name = jl_new_typename_in(jl_symbol("DataType"), core, 0, 1); jl_datatype_type->name->wrapper = (jl_value_t*)jl_datatype_type; - jl_datatype_type->super = (jl_datatype_t*)jl_type_type; + jl_datatype_type->super = type_type; jl_datatype_type->parameters = jl_emptysvec; jl_datatype_type->name->n_uninitialized = 8 - 3; jl_datatype_type->name->names = jl_perm_symsvec(8, @@ -2066,7 +2151,7 @@ void jl_init_types(void) JL_GC_DISABLED "instance", "layout", "hash", - "flags"); // "hasfreetypevars", "isconcretetype", "isdispatchtuple", "isbitstype", "zeroinit", "has_concrete_subtype", "cached_by_hash" + "flags"); // "hasfreetypevars", "isconcretetype", "isdispatchtuple", "isbitstype", "zeroinit", "has_concrete_subtype", "maybe_subtype_of_cache" jl_datatype_type->types = jl_svec(8, jl_typename_type, jl_datatype_type, @@ -2095,6 +2180,11 @@ void jl_init_types(void) JL_GC_DISABLED "hash", "n_uninitialized", "flags", // "abstract", "mutable", "mayinlinealloc", "max_methods"); + const static uint32_t typename_constfields[1] = { 0x00003a3f }; // (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<9)|(1<<11)|(1<<12)|(1<<13) + const static uint32_t typename_atomicfields[1] = { 0x00000180 }; // (1<<7)|(1<<8) + jl_typename_type->name->constfields = typename_constfields; + jl_typename_type->name->atomicfields = typename_atomicfields; + jl_precompute_memoized_dt(jl_typename_type, 1); jl_typename_type->types = jl_svec(15, jl_symbol_type, jl_any_type /*jl_module_type*/, jl_simplevector_type, jl_any_type/*jl_voidpointer_type*/, jl_any_type/*jl_voidpointer_type*/, jl_type_type, jl_type_type, jl_simplevector_type, jl_simplevector_type, @@ -2102,11 +2192,6 @@ void jl_init_types(void) JL_GC_DISABLED jl_any_type /*jl_long_type*/, jl_any_type /*jl_int32_type*/, jl_any_type /*jl_uint8_type*/, jl_any_type /*jl_uint8_type*/); - const static uint32_t typename_constfields[1] = { 0x00003a3f }; // (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<9)|(1<<11)|(1<<12)|(1<<13) - const static uint32_t typename_atomicfields[1] = { 0x00000180 }; // (1<<7)|(1<<8) - jl_typename_type->name->constfields = typename_constfields; - jl_typename_type->name->atomicfields = typename_atomicfields; - jl_precompute_memoized_dt(jl_typename_type, 1); jl_methtable_type->name = jl_new_typename_in(jl_symbol("MethodTable"), core, 0, 1); jl_methtable_type->name->wrapper = (jl_value_t*)jl_methtable_type; @@ -2118,16 +2203,16 @@ void jl_init_types(void) JL_GC_DISABLED "leafcache", "cache", "max_args", "module", "backedges", "", "", "offs", ""); - jl_methtable_type->types = jl_svec(11, jl_symbol_type, jl_any_type, jl_any_type, - jl_any_type, jl_any_type/*jl_long*/, - jl_any_type/*module*/, jl_any_type/*any vector*/, - jl_any_type/*voidpointer*/, jl_any_type/*int32*/, - jl_any_type/*uint8*/, jl_any_type/*uint8*/); const static uint32_t methtable_constfields[1] = { 0x00000020 }; // (1<<5); const static uint32_t methtable_atomicfields[1] = { 0x0000001e }; // (1<<1)|(1<<2)|(1<<3)|(1<<4); jl_methtable_type->name->constfields = methtable_constfields; jl_methtable_type->name->atomicfields = methtable_atomicfields; jl_precompute_memoized_dt(jl_methtable_type, 1); + jl_methtable_type->types = jl_svec(11, jl_symbol_type, jl_any_type, jl_any_type, + jl_any_type, jl_any_type/*jl_long*/, + jl_any_type/*module*/, jl_any_type/*any vector*/, + jl_any_type/*voidpointer*/, jl_any_type/*int32*/, + jl_any_type/*uint8*/, jl_any_type/*uint8*/); jl_symbol_type->name = jl_new_typename_in(jl_symbol("Symbol"), core, 0, 1); jl_symbol_type->name->wrapper = (jl_value_t*)jl_symbol_type; @@ -2156,19 +2241,6 @@ void jl_init_types(void) JL_GC_DISABLED jl_astaggedvalue(jl_nothing)->header = ((uintptr_t)jl_nothing_type) | GC_OLD_MARKED; jl_nothing_type->instance = jl_nothing; - jl_datatype_t *type_type = (jl_datatype_t*)jl_type_type; - jl_typeofbottom_type = jl_new_datatype(jl_symbol("TypeofBottom"), core, type_type, jl_emptysvec, - jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 0, 0); - jl_bottom_type = jl_new_struct(jl_typeofbottom_type); - jl_typeofbottom_type->instance = jl_bottom_type; - - jl_uniontype_type = jl_new_datatype(jl_symbol("Union"), core, type_type, jl_emptysvec, - jl_perm_symsvec(2, "a", "b"), - jl_svec(2, jl_any_type, jl_any_type), - jl_emptysvec, 0, 0, 2); - // It seems like we probably usually end up needing the box for kinds (used in an Any context), so force it to exist - jl_uniontype_type->name->mayinlinealloc = 0; - jl_tvar_type = jl_new_datatype(jl_symbol("TypeVar"), core, jl_any_type, jl_emptysvec, jl_perm_symsvec(3, "name", "lb", "ub"), jl_svec(3, jl_symbol_type, jl_any_type, jl_any_type), @@ -2176,16 +2248,38 @@ void jl_init_types(void) JL_GC_DISABLED const static uint32_t tvar_constfields[1] = { 0x00000007 }; // all fields are constant, even though TypeVar itself has identity jl_tvar_type->name->constfields = tvar_constfields; + jl_typeofbottom_type = jl_new_datatype(jl_symbol("TypeofBottom"), core, type_type, jl_emptysvec, + jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 0, 0); + jl_bottom_type = jl_new_struct(jl_typeofbottom_type); + jl_typeofbottom_type->instance = jl_bottom_type; + jl_unionall_type = jl_new_datatype(jl_symbol("UnionAll"), core, type_type, jl_emptysvec, jl_perm_symsvec(2, "var", "body"), jl_svec(2, jl_tvar_type, jl_any_type), jl_emptysvec, 0, 0, 2); + // It seems like we probably usually end up needing the box for kinds (often used in an Any context), so force it to exist jl_unionall_type->name->mayinlinealloc = 0; + jl_uniontype_type = jl_new_datatype(jl_symbol("Union"), core, type_type, jl_emptysvec, + jl_perm_symsvec(2, "a", "b"), + jl_svec(2, jl_any_type, jl_any_type), + jl_emptysvec, 0, 0, 2); + // It seems like we probably usually end up needing the box for kinds (often used in an Any context), so force it to exist + jl_uniontype_type->name->mayinlinealloc = 0; + + jl_tvar_t *tttvar = tvar("T"); + type_type->parameters = jl_svec(1, tttvar); + jl_precompute_memoized_dt(type_type, 0); // update the hash value ASAP + type_type->hasfreetypevars = 1; + type_type->ismutationfree = 1; + jl_type_typename->wrapper = jl_new_struct(jl_unionall_type, tttvar, (jl_value_t*)jl_type_type); + jl_type_type = (jl_unionall_t*)jl_type_typename->wrapper; + jl_vararg_type = jl_new_datatype(jl_symbol("TypeofVararg"), core, jl_any_type, jl_emptysvec, jl_perm_symsvec(2, "T", "N"), jl_svec(2, jl_any_type, jl_any_type), jl_emptysvec, 0, 0, 0); + // It seems like we probably usually end up needing the box for kinds (often used in an Any context), so force it to exist jl_vararg_type->name->mayinlinealloc = 0; jl_svec_t *anytuple_params = jl_svec(1, jl_wrap_vararg((jl_value_t*)jl_any_type, (jl_value_t*)NULL)); @@ -2195,19 +2289,10 @@ void jl_init_types(void) JL_GC_DISABLED // fix some miscomputed values, since we didn't know this was going to be a Tuple in jl_precompute_memoized_dt jl_tuple_typename->wrapper = (jl_value_t*)jl_anytuple_type; // remove UnionAll wrappers jl_anytuple_type->isconcretetype = 0; + jl_anytuple_type->maybe_subtype_of_cache = 0; jl_anytuple_type->layout = NULL; - jl_anytuple_type->cached_by_hash = 0; - - jl_tvar_t *tttvar = tvar("T"); - ((jl_datatype_t*)jl_type_type)->parameters = jl_svec(1, tttvar); - ((jl_datatype_t*)jl_type_type)->hasfreetypevars = 1; - ((jl_datatype_t*)jl_type_type)->cached_by_hash = 0; - jl_type_typename->wrapper = jl_new_struct(jl_unionall_type, tttvar, (jl_value_t*)jl_type_type); - jl_type_type = (jl_unionall_t*)jl_type_typename->wrapper; - ((jl_datatype_t*)jl_type_type->body)->ismutationfree = 1; jl_typeofbottom_type->super = jl_wrap_Type(jl_bottom_type); - jl_emptytuple_type = jl_apply_tuple_type(jl_emptysvec); jl_emptytuple = jl_gc_permobj(0, jl_emptytuple_type); jl_emptytuple_type->instance = jl_emptytuple; diff --git a/src/julia.h b/src/julia.h index 6b0d6aec85cab..8a9e4ada487e2 100644 --- a/src/julia.h +++ b/src/julia.h @@ -548,7 +548,7 @@ typedef struct _jl_datatype_t { uint16_t isbitstype:1; // relevant query for C-api and type-parameters uint16_t zeroinit:1; // if one or more fields requires zero-initialization uint16_t has_concrete_subtype:1; // If clear, no value will have this datatype - uint16_t cached_by_hash:1; // stored in hash-based set cache (instead of linear cache) + uint16_t maybe_subtype_of_cache:1; // Computational bit for has_concrete_supertype. See description in jltypes.c. uint16_t isprimitivetype:1; // whether this is declared with 'primitive type' keyword (sized, no fields, and immutable) uint16_t ismutationfree:1; // whether any mutable memory is reachable through this type (in the type or via fields) uint16_t isidentityfree:1; // whether this type or any object reachable through its fields has non-content-based identity @@ -1414,7 +1414,6 @@ STATIC_INLINE int jl_egal_(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value #define jl_egal(a, b) jl_egal_((a), (b)) // type predicates and basic operations -JL_DLLEXPORT int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2) JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_has_free_typevars(jl_value_t *v) JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_has_typevar(jl_value_t *t, jl_tvar_t *v) JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_has_typevar_from_unionall(jl_value_t *t, jl_unionall_t *ua); diff --git a/src/julia_internal.h b/src/julia_internal.h index b77de64732116..0e5e3b6cdd6ef 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -722,6 +722,7 @@ void jl_init_main_module(void); JL_DLLEXPORT int jl_is_submodule(jl_module_t *child, jl_module_t *parent) JL_NOTSAFEPOINT; jl_array_t *jl_get_loaded_modules(void); JL_DLLEXPORT int jl_datatype_isinlinealloc(jl_datatype_t *ty, int pointerfree); +int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2) JL_NOTSAFEPOINT; void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type); jl_value_t *jl_toplevel_eval_flex(jl_module_t *m, jl_value_t *e, int fast, int expanded); diff --git a/src/subtype.c b/src/subtype.c index cb6c9531c33ca..10a9f9c924421 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -306,11 +306,8 @@ static int obviously_unequal(jl_value_t *a, jl_value_t *b) if (ad->name != bd->name) return 1; int istuple = (ad->name == jl_tuple_typename); - if ((jl_is_concrete_type(a) || jl_is_concrete_type(b)) && - jl_type_equality_is_identity(a, b)) { - if (!istuple && ad->name != jl_type_typename) // HACK: can't properly normalize Tuple{Float64} == Tuple{<:Float64} like types or Type{T} types - return 1; - } + if (jl_type_equality_is_identity(a, b)) + return 1; size_t i, np; if (istuple) { size_t na = jl_nparams(ad), nb = jl_nparams(bd); @@ -395,10 +392,7 @@ static int obviously_disjoint(jl_value_t *a, jl_value_t *b, int specificity) return 0; if (specificity && a == (jl_value_t*)jl_typeofbottom_type) return 0; - if (jl_is_concrete_type(a) && jl_is_concrete_type(b) && - jl_type_equality_is_identity(a, b) && - (((jl_datatype_t*)a)->name != jl_tuple_typename || - ((jl_datatype_t*)b)->name != jl_tuple_typename)) + if (jl_is_concrete_type(a) && jl_is_concrete_type(b) && jl_type_equality_is_identity(a, b)) return 1; if (jl_is_unionall(a)) a = jl_unwrap_unionall(a); if (jl_is_unionall(b)) b = jl_unwrap_unionall(b); @@ -1766,7 +1760,7 @@ static int obvious_subtype(jl_value_t *x, jl_value_t *y, jl_value_t *y0, int *su if (jl_is_datatype(y)) { int istuple = (((jl_datatype_t*)y)->name == jl_tuple_typename); int iscov = istuple; - // TODO: this would be a nice fast-path to have, unfortuanately, + // TODO: this would be a nice fast-path to have, unfortunately, // datatype allocation fails to correctly hash-cons them // and the subtyping tests include tests for this case //if (!iscov && ((jl_datatype_t*)y)->isconcretetype && !jl_is_type_type(x)) { @@ -2253,7 +2247,7 @@ JL_DLLEXPORT int jl_isa(jl_value_t *x, jl_value_t *t) return 0; } } - if (jl_is_concrete_type(t) && jl_type_equality_is_identity(jl_typeof(x), t)) + if (jl_is_concrete_type(t)) return 0; return jl_subtype(jl_typeof(x), t); } @@ -3735,6 +3729,7 @@ static jl_value_t *switch_union_tuple(jl_value_t *a, jl_value_t *b) // `a` might have a non-empty intersection with some concrete type b even if !(a<:b) and !(b<:a) // For example a=`Tuple{Type{<:Vector}}` and b=`Tuple{DataType}` +// TODO: this query is partly available memoized as jl_type_equality_is_identity static int might_intersect_concrete(jl_value_t *a) { if (jl_is_unionall(a)) @@ -3784,9 +3779,9 @@ jl_value_t *jl_type_intersection_env_s(jl_value_t *a, jl_value_t *b, jl_svec_t * *ans = a; sz = szb; if (issubty) *issubty = 1; } - else if (lta && ltb) { - goto bot; - } + // else if (lta && ltb) { // !jl_type_equality_is_identity known in this case because obviously_disjoint returned false + // goto bot; + // } else if (jl_subtype(b, a)) { *ans = b; } diff --git a/test/specificity.jl b/test/specificity.jl index 1a5c117ce5d9d..5808ac71fa54b 100644 --- a/test/specificity.jl +++ b/test/specificity.jl @@ -214,7 +214,7 @@ f27361(::M) where M <: Tuple{3} = nothing @test length(methods(f27361)) == 2 # specificity of TypeofBottom -@test args_morespecific(Tuple{Core.TypeofBottom}, Tuple{DataType}) +@test !args_morespecific(Tuple{DataType}, Tuple{Core.TypeofBottom}) @test args_morespecific(Tuple{Core.TypeofBottom}, Tuple{Type{<:Tuple}}) @test args_morespecific(Tuple{Type{Any}, Type}, Tuple{Type{T}, Type{T}} where T) From 99fce415079e8545a6b3246d8f998c8a50449ef7 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 20 Mar 2023 18:42:08 -0500 Subject: [PATCH 337/775] Ensure invalidated target gets logged (#49048) To interpret causes of invalidation, it's helpful to understand what signature got invalidated. #48954 inadvertently dropped this info from the logging stream; this commit restores it. --- src/gf.c | 5 +++++ test/worlds.jl | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/gf.c b/src/gf.c index ddc4fcb8ad91e..9f6f342ef7273 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2021,6 +2021,11 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method } jl_array_ptr_1d_push(oldmi, (jl_value_t*)mi); invalidate_external(mi, max_world); + if (_jl_debug_method_invalidation) { + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)mi); + loctag = jl_cstr_to_string("jl_method_table_insert"); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); + } } } } diff --git a/test/worlds.jl b/test/worlds.jl index 39a9dc4d9a788..a2baa741b592a 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -408,3 +408,27 @@ wc_aiw2 = get_world_counter() @test Base.invoke_in_world(wc_aiw2, f_inworld, 2) == "world two; x=2" @test Base.invoke_in_world(wc_aiw1, g_inworld, 2, y=3) == "world one; x=2, y=3" @test Base.invoke_in_world(wc_aiw2, g_inworld, 2, y=3) == "world two; x=2, y=3" + +# logging +mc48954(x, y) = false +mc48954(x::Int, y::Int) = x == y +mc48954(x::Symbol, y::Symbol) = x == y +function mcc48954(container, y) + x = container[1] + return mc48954(x, y) +end + +mcc48954(Any[1], 1) +mc48954i = method_instance(mc48954, (Any, Int)) +mcc48954i = method_instance(mcc48954, (Vector{Any}, Int)) +list48954 = ccall(:jl_debug_method_invalidation, Any, (Cint,), 1) +mc48954(x::AbstractFloat, y::Int) = x == y +ccall(:jl_debug_method_invalidation, Any, (Cint,), 0) +@test list48954 == [ + mcc48954i, + 1, + mc48954i, + "jl_method_table_insert", + which(mc48954, (AbstractFloat, Int)), + "jl_method_table_insert" +] From ceeefc8fce0f57ab6404415e7a2e490505d5257e Mon Sep 17 00:00:00 2001 From: Lionel Zoubritzky Date: Tue, 21 Mar 2023 00:43:31 +0100 Subject: [PATCH 338/775] Fix show_method_candidate for Builtins (#49053) Fix #49002 With this PR: ```julia julia> typeof(; a=3, b=4) ERROR: MethodError: no method matching typeof(; a::Int64, b::Int64) Stacktrace: [1] top-level scope @ REPL[41]:1 ``` --- base/errorshow.jl | 8 ++++---- test/errorshow.jl | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index 6530dd51d67c1..f7c6a0a0f84b9 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -416,17 +416,17 @@ function show_method_candidates(io::IO, ex::MethodError, @nospecialize kwargs=() end sig0 = sig0::DataType s1 = sig0.parameters[1] - sig = sig0.parameters[2:end] - print(iob, " ") - if !isa(func, rewrap_unionall(s1, method.sig)) - # function itself doesn't match + if sig0 === Tuple || !isa(func, rewrap_unionall(s1, method.sig)) + # function itself doesn't match or is a builtin continue else + print(iob, " ") show_signature_function(iob, s1) end print(iob, "(") t_i = copy(arg_types_param) right_matches = 0 + sig = sig0.parameters[2:end] for i = 1 : min(length(t_i), length(sig)) i > 1 && print(iob, ", ") # If isvarargtype then it checks whether the rest of the input arguments matches diff --git a/test/errorshow.jl b/test/errorshow.jl index e081695f2f15d..a751a3a9951b5 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -933,3 +933,11 @@ let err_str err_str = @except_str "a" + "b" MethodError @test occursin("String concatenation is performed with *", err_str) end + +# issue #49002 +let buf = IOBuffer() + Base.show_method_candidates(buf, Base.MethodError(typeof, (17,)), pairs((foo = :bar,))) + @test isempty(take!(buf)) + Base.show_method_candidates(buf, Base.MethodError(isa, ()), pairs((a = 5,))) + @test isempty(take!(buf)) +end From 1323d2f784686815ad7f45cee83c578b91e8e7ba Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 20 Mar 2023 19:45:51 -0400 Subject: [PATCH 339/775] fix multiq_check_empty to only look at the caller's pool (#49065) Co-authored-by: Jameson Nash --- base/partr.jl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/base/partr.jl b/base/partr.jl index c5bb6603d53af..a02272ceab202 100644 --- a/base/partr.jl +++ b/base/partr.jl @@ -179,13 +179,12 @@ function multiq_deletemin() return task end - function multiq_check_empty() - for j = UInt32(1):length(heaps) - for i = UInt32(1):length(heaps[j]) - if heaps[j][i].ntasks != 0 - return false - end + tid = Threads.threadid() + tp = ccall(:jl_threadpoolid, Int8, (Int16,), tid-1) + 1 + for i = UInt32(1):length(heaps[tp]) + if heaps[tp][i].ntasks != 0 + return false end end return true From 826674cf7d21ff5940ecc4dd6c06103cccbed392 Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Mon, 20 Mar 2023 21:06:03 -0400 Subject: [PATCH 340/775] Fix name indexing issues (#49070) --- src/aotcompile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 7d1b916d44b39..b6fd7aa92463e 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1259,7 +1259,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o } } for (unsigned i = 0; i < threads; ++i) { - auto start = &outputs[outputs.size() - outcount * threads * 2 + i]; + auto start = &outputs[outputs.size() - outcount * threads * 2 + i * outcount]; auto istr = std::to_string(i); if (unopt_out) *start++ = (name + "_unopt#" + istr + ".bc").str(); @@ -1274,7 +1274,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o if (threads == 1) { output_timer.startTimer(); SmallVector names; - for (unsigned i = 0; i < outcount; ++i) + for (unsigned i = outputs.size() - outcount * 2; i < outputs.size() - outcount; ++i) names.push_back(outputs[i]); add_output_impl(M, TM, outputs.data() + outputs.size() - outcount, names.data(), unopt_out ? unopt.data() + unopt.size() - 1 : nullptr, From ceffaee345aa622dcfcb002c3890e548258d64c9 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Tue, 21 Mar 2023 16:14:31 +0800 Subject: [PATCH 341/775] Fix invdepth within existential subtyping. (#49049) * Remove `Rinvdepth` from `stenv` --- src/subtype.c | 92 +++++++++++++++++-------------------------------- test/subtype.jl | 16 +++++---- 2 files changed, 41 insertions(+), 67 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 10a9f9c924421..1cb77e301e874 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -101,8 +101,7 @@ typedef struct jl_stenv_t { jl_value_t **envout; // for passing caller the computed bounds of right-side variables int envsz; // length of envout int envidx; // current index in envout - int invdepth; // # of invariant constructors we're nested in on the left - int Rinvdepth; // # of invariant constructors we're nested in on the right + int invdepth; // current number of invariant constructors we're nested in int ignore_free; // treat free vars as black boxes; used during intersection int intersection; // true iff subtype is being called from intersection int emptiness_only; // true iff intersection only needs to test for emptiness @@ -658,7 +657,7 @@ static void record_var_occurrence(jl_varbinding_t *vb, jl_stenv_t *e, int param) vb->occurs = 1; if (vb != NULL && param) { // saturate counters at 2; we don't need values bigger than that - if (param == 2 && (vb->right ? e->Rinvdepth : e->invdepth) > vb->depth0) { + if (param == 2 && e->invdepth > vb->depth0) { if (vb->occurs_inv < 2) vb->occurs_inv++; } @@ -680,7 +679,7 @@ static int var_outside(jl_stenv_t *e, jl_tvar_t *x, jl_tvar_t *y) return 0; } -static jl_value_t *intersect_aside(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int R, int d); +static jl_value_t *intersect_aside(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int depth); static int reachable_var(jl_value_t *x, jl_tvar_t *y, jl_stenv_t *e); @@ -700,7 +699,7 @@ static int var_lt(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int param) // for this to work we need to compute issub(left,right) before issub(right,left), // since otherwise the issub(a, bb.ub) check in var_gt becomes vacuous. if (e->intersection) { - jl_value_t *ub = intersect_aside(bb->ub, a, e, 0, bb->depth0); + jl_value_t *ub = intersect_aside(a, bb->ub, e, bb->depth0); JL_GC_PUSH1(&ub); if (ub != (jl_value_t*)b && (!jl_is_typevar(ub) || !reachable_var(ub, b, e))) bb->ub = ub; @@ -849,7 +848,7 @@ static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8 { u = unalias_unionall(u, e); jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, 0, 0, 0, - R ? e->Rinvdepth : e->invdepth, 0, NULL, e->vars }; + e->invdepth, 0, NULL, e->vars }; JL_GC_PUSH4(&u, &vb.lb, &vb.ub, &vb.innervars); e->vars = &vb; int ans; @@ -949,10 +948,8 @@ static int check_vararg_length(jl_value_t *v, ssize_t n, jl_stenv_t *e) jl_value_t *nn = jl_box_long(n); JL_GC_PUSH1(&nn); e->invdepth++; - e->Rinvdepth++; int ans = subtype(nn, N, e, 2) && subtype(N, nn, e, 0); e->invdepth--; - e->Rinvdepth--; JL_GC_POP(); if (!ans) return 0; @@ -1049,16 +1046,13 @@ static int subtype_tuple_varargs( // set lb to Any. Since `intvalued` is set, we'll interpret that // appropriately. e->invdepth++; - e->Rinvdepth++; int ans = subtype((jl_value_t*)jl_any_type, yp1, e, 2); e->invdepth--; - e->Rinvdepth--; return ans; } // Vararg{T,N} <: Vararg{T2,N2}; equate N and N2 e->invdepth++; - e->Rinvdepth++; JL_GC_PUSH2(&xp1, &yp1); if (xp1 && jl_is_long(xp1) && vx != 1) xp1 = jl_box_long(jl_unbox_long(xp1) - vx + 1); @@ -1067,7 +1061,6 @@ static int subtype_tuple_varargs( int ans = forall_exists_equal(xp1, yp1, e); JL_GC_POP(); e->invdepth--; - e->Rinvdepth--; return ans; } @@ -1354,10 +1347,7 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) // The answer is true iff `T` has full bounds (as in `Type`), but this needs to // be checked at the same depth where `Type{T}` occurs --- the depth of the LHS // doesn't matter because it (e.g. `DataType`) doesn't actually contain the variable. - int saved = e->invdepth; - e->invdepth = e->Rinvdepth; int issub = subtype((jl_value_t*)jl_type_type, y, e, param); - e->invdepth = saved; return issub; } while (xd != jl_any_type && xd->name != yd->name) { @@ -1373,7 +1363,6 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) size_t i, np = jl_nparams(xd); int ans = 1; e->invdepth++; - e->Rinvdepth++; for (i=0; i < np; i++) { jl_value_t *xi = jl_tparam(xd, i), *yi = jl_tparam(yd, i); if (!(xi == yi || forall_exists_equal(xi, yi, e))) { @@ -1381,7 +1370,6 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) } } e->invdepth--; - e->Rinvdepth--; return ans; } if (jl_is_type(y)) @@ -1573,7 +1561,7 @@ static void init_stenv(jl_stenv_t *e, jl_value_t **env, int envsz) if (envsz) memset(env, 0, envsz*sizeof(void*)); e->envidx = 0; - e->invdepth = e->Rinvdepth = 0; + e->invdepth = 0; e->ignore_free = 0; e->intersection = 0; e->emptiness_only = 0; @@ -2028,31 +2016,20 @@ JL_DLLEXPORT int jl_subtype_env(jl_value_t *x, jl_value_t *y, jl_value_t **env, return subtype; } -static int subtype_in_env_(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int invdepth, int Rinvdepth) +static int subtype_in_env(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) { jl_stenv_t e2; init_stenv(&e2, NULL, 0); e2.vars = e->vars; e2.intersection = e->intersection; e2.ignore_free = e->ignore_free; - e2.invdepth = invdepth; - e2.Rinvdepth = Rinvdepth; + e2.invdepth = e->invdepth; e2.envsz = e->envsz; e2.envout = e->envout; e2.envidx = e->envidx; return forall_exists_subtype(x, y, &e2, 0); } -static int subtype_in_env(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) -{ - return subtype_in_env_(x, y, e, e->invdepth, e->Rinvdepth); -} - -static int subtype_bounds_in_env(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int R, int d) -{ - return subtype_in_env_(x, y, e, R ? e->invdepth : d, R ? d : e->Rinvdepth); -} - JL_DLLEXPORT int jl_subtype(jl_value_t *x, jl_value_t *y) { return jl_subtype_env(x, y, NULL, 0); @@ -2259,7 +2236,7 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e); // intersect in nested union environment, similar to subtype_ccheck -static jl_value_t *intersect_aside(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int R, int d) +static jl_value_t *intersect_aside(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int depth) { // band-aid for #30335 if (x == (jl_value_t*)jl_any_type && !jl_is_typevar(y)) @@ -2267,19 +2244,15 @@ static jl_value_t *intersect_aside(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, if (y == (jl_value_t*)jl_any_type && !jl_is_typevar(x)) return x; // band-aid for #46736 - if (jl_egal(x, y)) + if (obviously_egal(x, y)) return x; jl_saved_unionstate_t oldRunions; push_unionstate(&oldRunions, &e->Runions); - int savedepth = e->invdepth, Rsavedepth = e->Rinvdepth; - // TODO: this doesn't quite make sense - e->invdepth = e->Rinvdepth = d; - + int savedepth = e->invdepth; + e->invdepth = depth; jl_value_t *res = intersect_all(x, y, e); - - pop_unionstate(&e->Runions, &oldRunions); e->invdepth = savedepth; - e->Rinvdepth = Rsavedepth; + pop_unionstate(&e->Runions, &oldRunions); return res; } @@ -2380,14 +2353,14 @@ static int try_subtype_by_bounds(jl_value_t *a, jl_value_t *b, jl_stenv_t *e) return 0; } -static int try_subtype_in_env(jl_value_t *a, jl_value_t *b, jl_stenv_t *e, int R, int d) +static int try_subtype_in_env(jl_value_t *a, jl_value_t *b, jl_stenv_t *e, int flip) { if (a == jl_bottom_type || b == (jl_value_t *)jl_any_type || try_subtype_by_bounds(a, b, e)) return 1; jl_value_t *root=NULL; jl_savedenv_t se; JL_GC_PUSH1(&root); save_env(e, &root, &se); - int ret = subtype_bounds_in_env(a, b, e, R, d); + int ret = subtype_in_env(a, b, e); restore_env(e, root, &se); free_env(&se); JL_GC_POP(); @@ -2409,7 +2382,7 @@ static void set_bound(jl_value_t **bound, jl_value_t *val, jl_tvar_t *v, jl_sten } // subtype, treating all vars as existential -static int subtype_in_env_existential(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int R, int d) +static int subtype_in_env_existential(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int flip) { jl_varbinding_t *v = e->vars; int len = 0; @@ -2428,7 +2401,7 @@ static int subtype_in_env_existential(jl_value_t *x, jl_value_t *y, jl_stenv_t * v->right = 1; v = v->prev; } - int issub = subtype_bounds_in_env(x, y, e, R, d); + int issub = subtype_in_env(x, y, e); n = 0; v = e->vars; while (n < len) { assert(v != NULL); @@ -2506,25 +2479,23 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int { jl_varbinding_t *bb = lookup(e, b); if (bb == NULL) - return R ? intersect_aside(a, b->ub, e, 1, 0) : intersect_aside(b->ub, a, e, 0, 0); + return R ? intersect_aside(a, b->ub, e, 0) : intersect_aside(b->ub, a, e, 0); if (reachable_var(bb->lb, b, e) || reachable_var(bb->ub, b, e)) return a; - if (bb->lb == bb->ub && jl_is_typevar(bb->lb)) { - return intersect(a, bb->lb, e, param); - } + if (bb->lb == bb->ub && jl_is_typevar(bb->lb)) + return R ? intersect(a, bb->lb, e, param) : intersect(bb->lb, a, e, param); if (!jl_is_type(a) && !jl_is_typevar(a)) return set_var_to_const(bb, a, NULL); - int d = bb->depth0; jl_value_t *root=NULL; jl_savedenv_t se; if (param == 2) { jl_value_t *ub = NULL; JL_GC_PUSH2(&ub, &root); if (!jl_has_free_typevars(a)) { save_env(e, &root, &se); - int issub = subtype_in_env_existential(bb->lb, a, e, 0, d); + int issub = subtype_in_env_existential(bb->lb, a, e, R); restore_env(e, root, &se); if (issub) { - issub = subtype_in_env_existential(a, bb->ub, e, 1, d); + issub = subtype_in_env_existential(a, bb->ub, e, !R); restore_env(e, root, &se); } free_env(&se); @@ -2536,10 +2507,10 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int } else { e->triangular++; - ub = R ? intersect_aside(a, bb->ub, e, 1, d) : intersect_aside(bb->ub, a, e, 0, d); + ub = R ? intersect_aside(a, bb->ub, e, bb->depth0) : intersect_aside(bb->ub, a, e, bb->depth0); e->triangular--; save_env(e, &root, &se); - int issub = subtype_in_env_existential(bb->lb, ub, e, 0, d); + int issub = subtype_in_env_existential(bb->lb, ub, e, R); restore_env(e, root, &se); free_env(&se); if (!issub) { @@ -2570,7 +2541,7 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int JL_GC_POP(); return ub; } - jl_value_t *ub = R ? intersect_aside(a, bb->ub, e, 1, d) : intersect_aside(bb->ub, a, e, 0, d); + jl_value_t *ub = R ? intersect_aside(a, bb->ub, e, bb->depth0) : intersect_aside(bb->ub, a, e, bb->depth0); if (ub == jl_bottom_type) return jl_bottom_type; if (bb->constraintkind == 1 || e->triangular) { @@ -2581,7 +2552,7 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int } else if (bb->constraintkind == 0) { JL_GC_PUSH1(&ub); - if (!jl_is_typevar(a) && try_subtype_in_env(bb->ub, a, e, 0, d)) { + if (!jl_is_typevar(a) && try_subtype_in_env(bb->ub, a, e, R)) { JL_GC_POP(); return (jl_value_t*)b; } @@ -2911,7 +2882,7 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_ jl_value_t *res=NULL, *save=NULL; jl_savedenv_t se; jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, 0, 0, 0, - R ? e->Rinvdepth : e->invdepth, 0, NULL, e->vars }; + e->invdepth, 0, NULL, e->vars }; JL_GC_PUSH5(&res, &vb.lb, &vb.ub, &save, &vb.innervars); save_env(e, &save, &se); res = intersect_unionall_(t, u, e, R, param, &vb); @@ -3122,10 +3093,8 @@ static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t return (jl_subtype(x,y) && jl_subtype(y,x)) ? y : NULL; } e->invdepth++; - e->Rinvdepth++; jl_value_t *ii = intersect(x, y, e, 2); e->invdepth--; - e->Rinvdepth--; // Skip the following subtype check if `ii` was returned from `set_vat_to_const`. // As `var_gt`/`var_lt` might not handle `Vararg` length offset correctly. // TODO: fix this on subtype side and remove this branch. @@ -3148,11 +3117,11 @@ static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t jl_savedenv_t se; JL_GC_PUSH2(&ii, &root); save_env(e, &root, &se); - if (!subtype_in_env_existential(x, y, e, 0, e->invdepth)) + if (!subtype_in_env_existential(x, y, e, 0)) ii = NULL; else { restore_env(e, root, &se); - if (!subtype_in_env_existential(y, x, e, 0, e->invdepth)) + if (!subtype_in_env_existential(y, x, e, 1)) ii = NULL; } restore_env(e, root, &se); @@ -3314,7 +3283,8 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa } jl_value_t *ub=NULL, *lb=NULL; JL_GC_PUSH2(&lb, &ub); - ub = intersect_aside(xub, yub, e, 0, xx ? xx->depth0 : 0); + int d = xx ? xx->depth0 : yy ? yy->depth0 : 0; + ub = R ? intersect_aside(yub, xub, e, d) : intersect_aside(xub, yub, e, d); if (reachable_var(xlb, (jl_tvar_t*)y, e)) lb = ylb; else diff --git a/test/subtype.jl b/test/subtype.jl index b03c577bf7194..d58ad2bd922b8 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -1804,8 +1804,14 @@ end #end # issue #32386 -@test typeintersect(Type{S} where S<:(Vector{Pair{_A,N} where N} where _A), - Type{Vector{T}} where T) == Type{Vector{Pair{_A,N} where N}} where _A +@testintersect(Type{S} where S<:(Vector{Pair{_A,N} where N} where _A), + Type{Vector{T}} where T, + Type{Vector{Pair{_A,N} where N}} where _A) + +# pr #49049 +@testintersect(Tuple{Type{Pair{T, A} where {T, A<:Array{T}}}, Int, Any}, + Tuple{Type{F}, Any, Int} where {F<:(Pair{T, A} where {T, A<:Array{T}})}, + Tuple{Type{Pair{T, A} where {T, A<:(Array{T})}}, Int, Int}) # issue #32488 struct S32488{S <: Tuple, T, N, L} @@ -2431,11 +2437,9 @@ abstract type MyAbstract47877{C}; end struct MyType47877{A,B} <: MyAbstract47877{A} end let A = Tuple{Type{T}, T} where T, B = Tuple{Type{MyType47877{W, V} where V<:Union{Base.BitInteger, MyAbstract47877{W}}}, MyAbstract47877{<:Base.BitInteger}} where W - C = Tuple{Type{MyType47877{W, V} where V<:Union{MyAbstract47877{W1}, Base.BitInteger}}, MyType47877{W, V} where V<:Union{MyAbstract47877{W1}, Base.BitInteger}} where {W<:Base.BitInteger, W1<:Base.BitInteger} - # ensure that merge_env for innervars does not blow up (the large Unions ensure this will take excessive memory if it does) - @test typeintersect(A, B) == C # suboptimal, but acceptable C = Tuple{Type{MyType47877{W, V} where V<:Union{MyAbstract47877{W}, Base.BitInteger}}, MyType47877{W, V} where V<:Union{MyAbstract47877{W}, Base.BitInteger}} where W<:Base.BitInteger - @test typeintersect(B, A) == C + # ensure that merge_env for innervars does not blow up (the large Unions ensure this will take excessive memory if it does) + @testintersect(A, B, C) end let a = (isodd(i) ? Pair{Char, String} : Pair{String, String} for i in 1:2000) From e528b304f02cc1411fab3085b95b95f132605e86 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Tue, 21 Mar 2023 20:48:42 +0800 Subject: [PATCH 342/775] code clean for `intvalued`. This commit drops the usage of `intvalued==2` We now records the correct `occurs_inv` (== 2), thus re-intersection would always happen if needed. --- src/subtype.c | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 1cb77e301e874..d3f5c8a986177 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -75,11 +75,7 @@ typedef struct jl_varbinding_t { // 1 - var.ub = ub; return var // 2 - either (var.ub = ub; return var), or return ub int8_t constraintkind; - // intvalued: must be integer-valued; i.e. occurs as N in Vararg{_,N} - // 0: No restriction - // 1: must be unbounded/ or fixed to a `Int`/typevar - // 2: we have some imprecise vararg length intersection that can be improved if this var is const valued. - int8_t intvalued; + int8_t intvalued; // intvalued: must be integer-valued; i.e. occurs as N in Vararg{_,N} int8_t limited; int16_t depth0; // # of invariant constructors nested around the UnionAll type for this var // when this variable's integer value is compared to that of another, @@ -2312,7 +2308,9 @@ static jl_value_t *bound_var_below(jl_tvar_t *tv, jl_varbinding_t *bb, jl_stenv_ return (jl_value_t*)tv; if (bb->depth0 != e->invdepth) return jl_bottom_type; + e->invdepth++; record_var_occurrence(bb, e, 2); + e->invdepth--; if (jl_is_long(bb->lb)) { ssize_t blb = jl_unbox_long(bb->lb); if ((blb < bb->offset) || (blb < 0)) @@ -2322,10 +2320,8 @@ static jl_value_t *bound_var_below(jl_tvar_t *tv, jl_varbinding_t *bb, jl_stenv_ return bb->lb; return jl_box_long(blb - bb->offset); } - if (bb->offset > 0) { - bb->intvalued = 2; + if (bb->offset > 0) return NULL; - } return (jl_value_t*)tv; } @@ -2680,10 +2676,6 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind } } - // vb is still unbounded. - if (vb->intvalued == 2 && !(varval && jl_is_long(varval))) - vb->intvalued = 1; - // TODO: this can prevent us from matching typevar identities later if (!varval && (vb->lb != vb->var->lb || vb->ub != vb->var->ub)) newvar = jl_new_typevar(vb->var->name, vb->lb, vb->ub); @@ -2910,6 +2902,8 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_ return res; } +static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t *e); + // check n = (length of vararg type v) static int intersect_vararg_length(jl_value_t *v, ssize_t n, jl_stenv_t *e, int8_t R) { @@ -2918,15 +2912,14 @@ static int intersect_vararg_length(jl_value_t *v, ssize_t n, jl_stenv_t *e, int8 if (N && jl_is_typevar(N)) { jl_value_t *len = jl_box_long(n); JL_GC_PUSH1(&len); - jl_value_t *il = R ? intersect(len, N, e, 2) : intersect(N, len, e, 2); + jl_value_t *il = R ? intersect_invariant(len, N, e) : intersect_invariant(N, len, e); JL_GC_POP(); - if (il == jl_bottom_type) + if (il == NULL || il == jl_bottom_type) return 0; } return 1; } -static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t *e); static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, ssize_t offset, jl_stenv_t *e, int param) { // Vararg: covariant in first parameter, invariant in second @@ -2949,7 +2942,7 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, ssize_t if (xp2 && jl_is_typevar(xp2)) { xb = lookup(e, (jl_tvar_t*)xp2); if (xb) { - if (xb->intvalued == 0) xb->intvalued = 1; + xb->intvalued = 1; xb->offset = offset; } if (!yp2) @@ -2958,7 +2951,7 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, ssize_t if (yp2 && jl_is_typevar(yp2)) { yb = lookup(e, (jl_tvar_t*)yp2); if (yb) { - if (yb->intvalued == 0) yb->intvalued = 1; + yb->intvalued = 1; yb->offset = -offset; } if (!xp2) @@ -3303,10 +3296,8 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa } JL_GC_POP(); // Here we always return the shorter `Vararg`'s length. - if ((xx && xx->offset < 0) || (yy && yy->offset > 0)) { - if (yy) yy->intvalued = 2; + if ((xx && xx->offset < 0) || (yy && yy->offset > 0)) return x; - } return y; } record_var_occurrence(xx, e, param); From 25e2c9c0c9a17a8c6fda92c37ddc24de8d063abf Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Tue, 21 Mar 2023 20:46:33 +0800 Subject: [PATCH 343/775] `restore_env` for re-intersection of concrete var. --- src/subtype.c | 15 +++++++-------- test/subtype.jl | 4 ++++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index d3f5c8a986177..3275483430c95 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2884,16 +2884,15 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_ e->vars->limited = 1; } else if (res != jl_bottom_type) { - if (vb.concrete || vb.occurs_inv>1 || vb.intvalued > 1 || u->var->lb != jl_bottom_type || (vb.occurs_inv && vb.occurs_cov)) { - restore_env(e, NULL, &se); - vb.occurs = vb.occurs_cov = vb.occurs_inv = 0; + if (vb.concrete || vb.occurs_inv>1 || (vb.occurs_inv && vb.occurs_cov)) vb.constraintkind = vb.concrete ? 1 : 2; - res = intersect_unionall_(t, u, e, R, param, &vb); - } - else if (vb.occurs_cov && !var_occurs_invariant(u->body, u->var, 0)) { - restore_env(e, save, &se); - vb.occurs = vb.occurs_cov = vb.occurs_inv = 0; + else if (u->var->lb != jl_bottom_type) + vb.constraintkind = 2; + else if (vb.occurs_cov && !var_occurs_invariant(u->body, u->var, 0)) vb.constraintkind = 1; + if (vb.constraintkind) { + restore_env(e, vb.constraintkind == 1 ? save : NULL, &se); + vb.occurs = vb.occurs_cov = vb.occurs_inv = 0; res = intersect_unionall_(t, u, e, R, param, &vb); } } diff --git a/test/subtype.jl b/test/subtype.jl index d58ad2bd922b8..e2bb49a6e0123 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -1813,6 +1813,10 @@ end Tuple{Type{F}, Any, Int} where {F<:(Pair{T, A} where {T, A<:Array{T}})}, Tuple{Type{Pair{T, A} where {T, A<:(Array{T})}}, Int, Int}) +@testintersect(Type{Ref{Union{Int, Tuple{S,S} where S<:T}}} where T, + Type{F} where F<:(Base.RefValue{Union{Int, Tuple{S,S} where S<:T}} where T), + Union{}) + # issue #32488 struct S32488{S <: Tuple, T, N, L} data::NTuple{L,T} From be94b87d0ba268a8b61a8660c044a0c6bfa6b3ee Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Tue, 21 Mar 2023 21:18:26 +0800 Subject: [PATCH 344/775] NFC clean for #49049 --- src/subtype.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 3275483430c95..8760fa94e351d 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2349,7 +2349,7 @@ static int try_subtype_by_bounds(jl_value_t *a, jl_value_t *b, jl_stenv_t *e) return 0; } -static int try_subtype_in_env(jl_value_t *a, jl_value_t *b, jl_stenv_t *e, int flip) +static int try_subtype_in_env(jl_value_t *a, jl_value_t *b, jl_stenv_t *e) { if (a == jl_bottom_type || b == (jl_value_t *)jl_any_type || try_subtype_by_bounds(a, b, e)) return 1; @@ -2378,7 +2378,7 @@ static void set_bound(jl_value_t **bound, jl_value_t *val, jl_tvar_t *v, jl_sten } // subtype, treating all vars as existential -static int subtype_in_env_existential(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int flip) +static int subtype_in_env_existential(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) { jl_varbinding_t *v = e->vars; int len = 0; @@ -2488,10 +2488,10 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int JL_GC_PUSH2(&ub, &root); if (!jl_has_free_typevars(a)) { save_env(e, &root, &se); - int issub = subtype_in_env_existential(bb->lb, a, e, R); + int issub = subtype_in_env_existential(bb->lb, a, e); restore_env(e, root, &se); if (issub) { - issub = subtype_in_env_existential(a, bb->ub, e, !R); + issub = subtype_in_env_existential(a, bb->ub, e); restore_env(e, root, &se); } free_env(&se); @@ -2506,7 +2506,7 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int ub = R ? intersect_aside(a, bb->ub, e, bb->depth0) : intersect_aside(bb->ub, a, e, bb->depth0); e->triangular--; save_env(e, &root, &se); - int issub = subtype_in_env_existential(bb->lb, ub, e, R); + int issub = subtype_in_env_existential(bb->lb, ub, e); restore_env(e, root, &se); free_env(&se); if (!issub) { @@ -2548,7 +2548,7 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int } else if (bb->constraintkind == 0) { JL_GC_PUSH1(&ub); - if (!jl_is_typevar(a) && try_subtype_in_env(bb->ub, a, e, R)) { + if (!jl_is_typevar(a) && try_subtype_in_env(bb->ub, a, e)) { JL_GC_POP(); return (jl_value_t*)b; } @@ -3109,11 +3109,11 @@ static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t jl_savedenv_t se; JL_GC_PUSH2(&ii, &root); save_env(e, &root, &se); - if (!subtype_in_env_existential(x, y, e, 0)) + if (!subtype_in_env_existential(x, y, e)) ii = NULL; else { restore_env(e, root, &se); - if (!subtype_in_env_existential(y, x, e, 1)) + if (!subtype_in_env_existential(y, x, e)) ii = NULL; } restore_env(e, root, &se); From c8a4fa43e72ae96b6480735b3c4a8540bda09ee3 Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Tue, 21 Mar 2023 11:41:02 -0400 Subject: [PATCH 345/775] Remove inference timing fields from TLS (#49000) * Avoid incrementing reentrant_timing, store start times on stack * Remove reentrant_inference, encode in reentrant_timing * Remove test-and-lock-and-test idiom --- base/compiler/typeinfer.jl | 110 ++++++++++++++++++------------------- src/aotcompile.cpp | 13 +++-- src/gf.c | 39 ++++++++----- src/jitlayers.cpp | 56 +++++++++++++------ src/julia.h | 15 ++++- src/staticdata.c | 6 +- src/task.c | 4 -- 7 files changed, 139 insertions(+), 104 deletions(-) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 052d691ee29c1..72f1b7b76c8fd 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -959,76 +959,74 @@ function typeinf_ircode( sparams::SimpleVector, optimize_until::Union{Integer,AbstractString,Nothing}, ) - ccall(:jl_typeinf_timing_begin, Cvoid, ()) + start_time = ccall(:jl_typeinf_timing_begin, UInt64, ()) frame = typeinf_frame(interp, method, atype, sparams, false) if frame === nothing - ccall(:jl_typeinf_timing_end, Cvoid, ()) + ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) return nothing, Any end (; result) = frame opt = OptimizationState(frame, interp) ir = run_passes(opt.src, opt, result, optimize_until) rt = widenconst(ignorelimited(result.result)) - ccall(:jl_typeinf_timing_end, Cvoid, ()) + ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) return ir, rt end # compute an inferred frame function typeinf_frame(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, run_optimizer::Bool) mi = specialize_method(method, atype, sparams)::MethodInstance - ccall(:jl_typeinf_timing_begin, Cvoid, ()) + start_time = ccall(:jl_typeinf_timing_begin, UInt64, ()) result = InferenceResult(mi, typeinf_lattice(interp)) frame = InferenceState(result, run_optimizer ? :global : :no, interp) frame === nothing && return nothing typeinf(interp, frame) - ccall(:jl_typeinf_timing_end, Cvoid, ()) + ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) return frame end # compute (and cache) an inferred AST and return type function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) method = mi.def::Method - for i = 1:2 # test-and-lock-and-test - i == 2 && ccall(:jl_typeinf_timing_begin, Cvoid, ()) - code = get(code_cache(interp), mi, nothing) - if code isa CodeInstance - # see if this code already exists in the cache - inf = @atomic :monotonic code.inferred - if use_const_api(code) - i == 2 && ccall(:jl_typeinf_timing_end, Cvoid, ()) - tree = ccall(:jl_new_code_info_uninit, Ref{CodeInfo}, ()) - rettype_const = code.rettype_const - tree.code = Any[ ReturnNode(quoted(rettype_const)) ] - nargs = Int(method.nargs) - tree.slotnames = ccall(:jl_uncompress_argnames, Vector{Symbol}, (Any,), method.slot_syms) - tree.slotflags = fill(IR_FLAG_NULL, nargs) - tree.ssavaluetypes = 1 - tree.codelocs = Int32[1] - tree.linetable = LineInfoNode[LineInfoNode(method.module, method.name, method.file, method.line, Int32(0))] - tree.inferred = true - tree.ssaflags = UInt8[0] - set_inlineable!(tree, true) - tree.parent = mi - tree.rettype = Core.Typeof(rettype_const) - tree.min_world = code.min_world - tree.max_world = code.max_world - return tree - elseif isa(inf, CodeInfo) - i == 2 && ccall(:jl_typeinf_timing_end, Cvoid, ()) - if !(inf.min_world == code.min_world && - inf.max_world == code.max_world && - inf.rettype === code.rettype) - inf = copy(inf) - inf.min_world = code.min_world - inf.max_world = code.max_world - inf.rettype = code.rettype - end - return inf - elseif isa(inf, Vector{UInt8}) - i == 2 && ccall(:jl_typeinf_timing_end, Cvoid, ()) - inf = _uncompressed_ir(code, inf) - return inf + start_time = ccall(:jl_typeinf_timing_begin, UInt64, ()) + code = get(code_cache(interp), mi, nothing) + if code isa CodeInstance + # see if this code already exists in the cache + inf = @atomic :monotonic code.inferred + if use_const_api(code) + ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) + tree = ccall(:jl_new_code_info_uninit, Ref{CodeInfo}, ()) + rettype_const = code.rettype_const + tree.code = Any[ ReturnNode(quoted(rettype_const)) ] + nargs = Int(method.nargs) + tree.slotnames = ccall(:jl_uncompress_argnames, Vector{Symbol}, (Any,), method.slot_syms) + tree.slotflags = fill(IR_FLAG_NULL, nargs) + tree.ssavaluetypes = 1 + tree.codelocs = Int32[1] + tree.linetable = LineInfoNode[LineInfoNode(method.module, method.name, method.file, method.line, Int32(0))] + tree.inferred = true + tree.ssaflags = UInt8[0] + set_inlineable!(tree, true) + tree.parent = mi + tree.rettype = Core.Typeof(rettype_const) + tree.min_world = code.min_world + tree.max_world = code.max_world + return tree + elseif isa(inf, CodeInfo) + ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) + if !(inf.min_world == code.min_world && + inf.max_world == code.max_world && + inf.rettype === code.rettype) + inf = copy(inf) + inf.min_world = code.min_world + inf.max_world = code.max_world + inf.rettype = code.rettype end + return inf + elseif isa(inf, Vector{UInt8}) + ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) + inf = _uncompressed_ir(code, inf) + return inf end end if ccall(:jl_get_module_infer, Cint, (Any,), method.module) == 0 && !generating_sysimg() @@ -1039,7 +1037,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) frame = InferenceState(result, #=cache=#:global, interp) frame === nothing && return nothing typeinf(interp, frame) - ccall(:jl_typeinf_timing_end, Cvoid, ()) + ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) frame.src.inferred || return nothing return frame.src end @@ -1050,18 +1048,16 @@ function typeinf_type(interp::AbstractInterpreter, method::Method, @nospecialize return Union{} # don't ask: it does weird and unnecessary things, if it occurs during bootstrap end mi = specialize_method(method, atype, sparams)::MethodInstance - for i = 1:2 # test-and-lock-and-test - i == 2 && ccall(:jl_typeinf_timing_begin, Cvoid, ()) - code = get(code_cache(interp), mi, nothing) - if code isa CodeInstance - # see if this rettype already exists in the cache - i == 2 && ccall(:jl_typeinf_timing_end, Cvoid, ()) - return code.rettype - end + start_time = ccall(:jl_typeinf_timing_begin, UInt64, ()) + code = get(code_cache(interp), mi, nothing) + if code isa CodeInstance + # see if this rettype already exists in the cache + ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) + return code.rettype end result = InferenceResult(mi, typeinf_lattice(interp)) typeinf(interp, result, :global) - ccall(:jl_typeinf_timing_end, Cvoid, ()) + ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) is_inferred(result) || return nothing return widenconst(ignorelimited(result.result)) end @@ -1076,7 +1072,7 @@ function typeinf_ext_toplevel(interp::AbstractInterpreter, linfo::MethodInstance src = linfo.uninferred::CodeInfo if !src.inferred # toplevel lambda - infer directly - ccall(:jl_typeinf_timing_begin, Cvoid, ()) + start_time = ccall(:jl_typeinf_timing_begin, UInt64, ()) if !src.inferred result = InferenceResult(linfo, typeinf_lattice(interp)) frame = InferenceState(result, src, #=cache=#:global, interp) @@ -1084,7 +1080,7 @@ function typeinf_ext_toplevel(interp::AbstractInterpreter, linfo::MethodInstance @assert is_inferred(frame) # TODO: deal with this better src = frame.src end - ccall(:jl_typeinf_timing_end, Cvoid, ()) + ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) end end return src diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index b6fd7aa92463e..43f933aed28c7 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -282,7 +282,9 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm jl_code_info_t *src = NULL; JL_GC_PUSH1(&src); auto ct = jl_current_task; - ct->reentrant_timing++; + bool timed = (ct->reentrant_timing & 1) == 0; + if (timed) + ct->reentrant_timing |= 1; orc::ThreadSafeContext ctx; orc::ThreadSafeModule backing; if (!llvmmod) { @@ -466,9 +468,12 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm } data->M = std::move(clone); - if (!ct->reentrant_timing-- && measure_compile_time_enabled) { - auto end = jl_hrtime(); - jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, end - compiler_start_time); + if (timed) { + if (measure_compile_time_enabled) { + auto end = jl_hrtime(); + jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, end - compiler_start_time); + } + ct->reentrant_timing &= ~1ull; } if (ctx.getContext()) { jl_ExecutionEngine->releaseContext(std::move(ctx)); diff --git a/src/gf.c b/src/gf.c index 9f6f342ef7273..ec0f48168707e 100644 --- a/src/gf.c +++ b/src/gf.c @@ -283,12 +283,13 @@ jl_code_info_t *jl_type_infer(jl_method_instance_t *mi, size_t world, int force) if (jl_typeinf_func == NULL) return NULL; jl_task_t *ct = jl_current_task; - if (ct->reentrant_inference == (uint16_t)-1) { + if (ct->reentrant_timing & 0b1000) { // We must avoid attempting to re-enter inference here assert(0 && "attempted to enter inference while writing out image"); abort(); } - if (ct->reentrant_inference > 2) + // In case we use higher bits later, mask them out + if ((ct->reentrant_timing & 0b1111) >= 0b110) return NULL; jl_code_info_t *src = NULL; @@ -315,7 +316,14 @@ jl_code_info_t *jl_type_infer(jl_method_instance_t *mi, size_t world, int force) size_t last_age = ct->world_age; ct->world_age = jl_typeinf_world; mi->inInference = 1; - ct->reentrant_inference++; + // first bit is for reentrant timing, + // so adding 1 to the bit above performs + // inference reentrancy counter addition. + // Note that this is only safe because + // the counter varies from 0-3; if we + // increase that limit, we'll need to + // allocate another bit for the counter. + ct->reentrant_timing += 0b10; JL_TRY { src = (jl_code_info_t*)jl_apply(fargs, 3); } @@ -336,7 +344,7 @@ jl_code_info_t *jl_type_infer(jl_method_instance_t *mi, size_t world, int force) src = NULL; } ct->world_age = last_age; - ct->reentrant_inference--; + ct->reentrant_timing -= 0b10; mi->inInference = 0; #ifdef _OS_WINDOWS_ SetLastError(last_error); @@ -3709,23 +3717,24 @@ int jl_has_concrete_subtype(jl_value_t *typ) return ((jl_datatype_t*)typ)->has_concrete_subtype; } -JL_DLLEXPORT void jl_typeinf_timing_begin(void) +JL_DLLEXPORT uint64_t jl_typeinf_timing_begin(void) { jl_task_t *ct = jl_current_task; - if (!ct->reentrant_timing++) { - ct->inference_start_time = jl_hrtime(); - } + if (ct->reentrant_timing & 1) + return 0; + ct->reentrant_timing |= 1; + return jl_hrtime(); } -JL_DLLEXPORT void jl_typeinf_timing_end(void) +JL_DLLEXPORT void jl_typeinf_timing_end(uint64_t start) { + if (!start) + return; jl_task_t *ct = jl_current_task; - if (!--ct->reentrant_timing) { - if (jl_atomic_load_relaxed(&jl_measure_compile_time_enabled)) { - uint64_t inftime = jl_hrtime() - ct->inference_start_time; - jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, inftime); - } - ct->inference_start_time = 0; + ct->reentrant_timing &= ~1u; + if (jl_atomic_load_relaxed(&jl_measure_compile_time_enabled)) { + uint64_t inftime = jl_hrtime() - start; + jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, inftime); } } diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index dd305772899dd..5d19e67024f99 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -317,7 +317,9 @@ extern "C" JL_DLLEXPORT int jl_compile_extern_c_impl(LLVMOrcThreadSafeModuleRef llvmmod, void *p, void *sysimg, jl_value_t *declrt, jl_value_t *sigt) { auto ct = jl_current_task; - ct->reentrant_timing++; + bool timed = (ct->reentrant_timing & 1) == 0; + if (timed) + ct->reentrant_timing |= 1; uint64_t compiler_start_time = 0; uint8_t measure_compile_time_enabled = jl_atomic_load_relaxed(&jl_measure_compile_time_enabled); if (measure_compile_time_enabled) @@ -357,9 +359,12 @@ int jl_compile_extern_c_impl(LLVMOrcThreadSafeModuleRef llvmmod, void *p, void * jl_ExecutionEngine->addModule(std::move(*into)); } JL_UNLOCK(&jl_codegen_lock); - if (!--ct->reentrant_timing && measure_compile_time_enabled) { - auto end = jl_hrtime(); - jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, end - compiler_start_time); + if (timed) { + if (measure_compile_time_enabled) { + auto end = jl_hrtime(); + jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, end - compiler_start_time); + } + ct->reentrant_timing &= ~1ull; } if (ctx.getContext()) { jl_ExecutionEngine->releaseContext(std::move(ctx)); @@ -415,7 +420,9 @@ extern "C" JL_DLLEXPORT jl_code_instance_t *jl_generate_fptr_impl(jl_method_instance_t *mi JL_PROPAGATES_ROOT, size_t world) { auto ct = jl_current_task; - ct->reentrant_timing++; + bool timed = (ct->reentrant_timing & 1) == 0; + if (timed) + ct->reentrant_timing |= 1; uint64_t compiler_start_time = 0; uint8_t measure_compile_time_enabled = jl_atomic_load_relaxed(&jl_measure_compile_time_enabled); bool is_recompile = false; @@ -468,12 +475,15 @@ jl_code_instance_t *jl_generate_fptr_impl(jl_method_instance_t *mi JL_PROPAGATES codeinst = NULL; } JL_UNLOCK(&jl_codegen_lock); - if (!--ct->reentrant_timing && measure_compile_time_enabled) { - uint64_t t_comp = jl_hrtime() - compiler_start_time; - if (is_recompile) { - jl_atomic_fetch_add_relaxed(&jl_cumulative_recompile_time, t_comp); + if (timed) { + if (measure_compile_time_enabled) { + uint64_t t_comp = jl_hrtime() - compiler_start_time; + if (is_recompile) { + jl_atomic_fetch_add_relaxed(&jl_cumulative_recompile_time, t_comp); + } + jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, t_comp); } - jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, t_comp); + ct->reentrant_timing &= ~1ull; } JL_GC_POP(); return codeinst; @@ -486,7 +496,9 @@ void jl_generate_fptr_for_unspecialized_impl(jl_code_instance_t *unspec) return; } auto ct = jl_current_task; - ct->reentrant_timing++; + bool timed = (ct->reentrant_timing & 1) == 0; + if (timed) + ct->reentrant_timing |= 1; uint64_t compiler_start_time = 0; uint8_t measure_compile_time_enabled = jl_atomic_load_relaxed(&jl_measure_compile_time_enabled); if (measure_compile_time_enabled) @@ -519,9 +531,12 @@ void jl_generate_fptr_for_unspecialized_impl(jl_code_instance_t *unspec) JL_GC_POP(); } JL_UNLOCK(&jl_codegen_lock); // Might GC - if (!--ct->reentrant_timing && measure_compile_time_enabled) { - auto end = jl_hrtime(); - jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, end - compiler_start_time); + if (timed) { + if (measure_compile_time_enabled) { + auto end = jl_hrtime(); + jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, end - compiler_start_time); + } + ct->reentrant_timing &= ~1ull; } } @@ -543,7 +558,9 @@ jl_value_t *jl_dump_method_asm_impl(jl_method_instance_t *mi, size_t world, // (using sentinel value `1` instead) // so create an exception here so we can print pretty our lies auto ct = jl_current_task; - ct->reentrant_timing++; + bool timed = (ct->reentrant_timing & 1) == 0; + if (timed) + ct->reentrant_timing |= 1; uint64_t compiler_start_time = 0; uint8_t measure_compile_time_enabled = jl_atomic_load_relaxed(&jl_measure_compile_time_enabled); if (measure_compile_time_enabled) @@ -573,9 +590,12 @@ jl_value_t *jl_dump_method_asm_impl(jl_method_instance_t *mi, size_t world, JL_GC_POP(); } JL_UNLOCK(&jl_codegen_lock); - if (!--ct->reentrant_timing && measure_compile_time_enabled) { - auto end = jl_hrtime(); - jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, end - compiler_start_time); + if (timed) { + if (measure_compile_time_enabled) { + auto end = jl_hrtime(); + jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, end - compiler_start_time); + } + ct->reentrant_timing &= ~1ull; } } if (specfptr != 0) diff --git a/src/julia.h b/src/julia.h index 8a9e4ada487e2..430cc0cef68af 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1919,10 +1919,14 @@ typedef struct _jl_task_t { jl_value_t *result; jl_value_t *logstate; jl_function_t *start; + // 4 byte padding on 32-bit systems + // uint32_t padding0; uint64_t rngState[4]; _Atomic(uint8_t) _state; uint8_t sticky; // record whether this Task can be migrated to a new thread _Atomic(uint8_t) _isexception; // set if `result` is an exception to throw or that we exited with + // 1 byte padding + // uint8_t padding1; // multiqueue priority uint16_t priority; @@ -1931,6 +1935,14 @@ typedef struct _jl_task_t { _Atomic(int16_t) tid; // threadpool id int8_t threadpoolid; + // Reentrancy bits + // Bit 0: 1 if we are currently running inference/codegen + // Bit 1-2: 0-3 counter of how many times we've reentered inference + // Bit 3: 1 if we are writing the image and inference is illegal + uint8_t reentrant_timing; + // 2 bytes of padding on 32-bit, 6 bytes on 64-bit + // uint16_t padding2_32; + // uint48_t padding2_64; // saved gc stack top for context switches jl_gcframe_t *gcstack; size_t world_age; @@ -1944,9 +1956,6 @@ typedef struct _jl_task_t { jl_ucontext_t ctx; void *stkbuf; // malloc'd memory (either copybuf or stack) size_t bufsz; // actual sizeof stkbuf - uint64_t inference_start_time; // time when inference started - uint16_t reentrant_inference; // How many times we've reentered inference - uint16_t reentrant_timing; // How many times we've reentered timing unsigned int copy_stack:31; // sizeof stack for copybuf unsigned int started:1; } jl_task_t; diff --git a/src/staticdata.c b/src/staticdata.c index 30c0f2c4fbe8a..93b1356029bc5 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -2562,8 +2562,8 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli // Make sure we don't run any Julia code concurrently after this point // since it will invalidate our serialization preparations jl_gc_enable_finalizers(ct, 0); - assert(ct->reentrant_inference == 0); - ct->reentrant_inference = (uint16_t)-1; + assert((ct->reentrant_timing & 0b1110) == 0); + ct->reentrant_timing |= 0b1000; if (worklist) { jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist), &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges); @@ -2584,7 +2584,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli // make sure we don't run any Julia code concurrently before this point // Re-enable running julia code for postoutput hooks, atexit, etc. jl_gc_enable_finalizers(ct, 1); - ct->reentrant_inference = 0; + ct->reentrant_timing &= ~0b1000u; jl_precompile_toplevel_module = NULL; if (worklist) { diff --git a/src/task.c b/src/task.c index 7373de937b9ae..37dc9372cf705 100644 --- a/src/task.c +++ b/src/task.c @@ -940,8 +940,6 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion t->ptls = NULL; t->world_age = ct->world_age; t->reentrant_timing = 0; - t->reentrant_inference = 0; - t->inference_start_time = 0; #ifdef COPY_STACKS if (!t->copy_stack) { @@ -1528,8 +1526,6 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) ct->ptls = ptls; ct->world_age = 1; // OK to run Julia code on this task ct->reentrant_timing = 0; - ct->reentrant_inference = 0; - ct->inference_start_time = 0; ptls->root_task = ct; jl_atomic_store_relaxed(&ptls->current_task, ct); JL_GC_PROMISE_ROOTED(ct); From 0b877b7027b089cc39db15b672f34f69915ec2aa Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 21 Mar 2023 11:46:50 -0400 Subject: [PATCH 346/775] polymorphic specializations field: either svec or value (#49071) Attempt an experiment: remove arrays that only have one value. Seems to save about 0.8% (1MB) in the sysimg, since we were rather significantly over-sizing these (about 16 slots initialized) before. Also a small lookup-failure optimization for the hashing-failed case to avoid duplicated work. --- base/compiler/utilities.jl | 2 +- src/gf.c | 165 ++++++++++++++++-------- src/jltypes.c | 2 +- src/julia.h | 2 +- src/method.c | 2 +- src/precompile_utils.c | 34 +++-- src/staticdata.c | 18 ++- src/staticdata_utils.c | 18 ++- stdlib/InteractiveUtils/src/codeview.jl | 2 +- test/compiler/inference.jl | 5 +- test/compiler/inline.jl | 3 +- test/core.jl | 2 +- test/precompile.jl | 65 +++++----- test/reflection.jl | 2 +- test/worlds.jl | 10 +- 15 files changed, 207 insertions(+), 125 deletions(-) diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 92458c443479b..3c8357cf3a414 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -286,7 +286,7 @@ callyou (generic function with 1 method) julia> callyou(2.0) 3.0 -julia> mi = first(which(callme, (Any,)).specializations) +julia> mi = which(callme, (Any,)).specializations MethodInstance for callme(::Float64) julia> @eval Core.Compiler for (; sig, caller) in BackedgeIterator(Main.mi.backedges) diff --git a/src/gf.c b/src/gf.c index ec0f48168707e..fe858d15af1cc 100644 --- a/src/gf.c +++ b/src/gf.c @@ -106,12 +106,32 @@ static jl_method_instance_t *jl_specializations_get_linfo_(jl_method_t *m JL_PRO jl_value_t *ut = jl_is_unionall(type) ? jl_unwrap_unionall(type) : type; JL_TYPECHK(specializations, datatype, ut); uint_t hv = ((jl_datatype_t*)ut)->hash; - for (int locked = 0; ; locked++) { - jl_array_t *speckeyset = jl_atomic_load_acquire(&m->speckeyset); - jl_svec_t *specializations = jl_atomic_load_relaxed(&m->specializations); - size_t i = -1, cl = jl_svec_len(specializations); + jl_array_t *speckeyset = NULL; + jl_value_t *specializations = NULL; + size_t i = -1, cl = 0, lastcl; + for (int locked = 0; locked < 2; locked++) { + if (locked) { + if (!sparams) // can't insert without knowing this + return NULL; + JL_LOCK(&m->writelock); + } + lastcl = cl; + speckeyset = jl_atomic_load_acquire(&m->speckeyset); + specializations = jl_atomic_load_relaxed(&m->specializations); + if (specializations == (jl_value_t*)jl_emptysvec) + continue; + if (!jl_is_svec(specializations)) { + jl_method_instance_t *mi = (jl_method_instance_t*)specializations; + if (jl_types_equal(mi->specTypes, type)) { + if (locked) + JL_UNLOCK(&m->writelock); + return mi; + } + continue; + } + cl = jl_svec_len(specializations); if (hv) { - ssize_t idx = jl_smallintset_lookup(speckeyset, speccache_eq, type, specializations, hv); + ssize_t idx = jl_smallintset_lookup(speckeyset, speccache_eq, type, (jl_svec_t*)specializations, hv); if (idx != -1) { jl_method_instance_t *mi = (jl_method_instance_t*)jl_svecref(specializations, idx); if (locked) @@ -122,8 +142,9 @@ static jl_method_instance_t *jl_specializations_get_linfo_(jl_method_t *m JL_PRO else { _Atomic(jl_method_instance_t*) *data = (_Atomic(jl_method_instance_t*)*)jl_svec_data(specializations); JL_GC_PUSH1(&specializations); // clang-sa doesn't realize this loop uses specializations - for (i = cl; i > 0; i--) { - jl_method_instance_t *mi = jl_atomic_load_relaxed(&data[i - 1]); + // the last lastcl-i-1 elements are already checked when locked, so start search with the new elements only + for (i += cl - lastcl; i > 0; i--) { + jl_method_instance_t *mi = jl_atomic_load_relaxed(&data[i]); if ((jl_value_t*)mi == jl_nothing) break; if (jl_types_equal(mi->specTypes, type)) { @@ -133,55 +154,66 @@ static jl_method_instance_t *jl_specializations_get_linfo_(jl_method_t *m JL_PRO return mi; } } + // i points to the first unchecked element, or the place to insert JL_GC_POP(); } - if (!sparams) // can't insert without knowing this - return NULL; - if (!locked) { - JL_LOCK(&m->writelock); + } + jl_method_instance_t *mi = mi_insert ? mi_insert : jl_get_specialized(m, type, sparams); + if (specializations == (jl_value_t*)jl_emptysvec) { + jl_atomic_store_release(&m->specializations, (jl_value_t*)mi); + jl_gc_wb(m, mi); + } + else { + JL_GC_PUSH1(&mi); + if (!jl_is_svec(specializations)) { + jl_method_instance_t *mi = (jl_method_instance_t*)specializations; + jl_value_t *type = mi->specTypes; + jl_value_t *ut = jl_is_unionall(type) ? jl_unwrap_unionall(type) : type; + uint_t hv = ((jl_datatype_t*)ut)->hash; + cl = 7; + i = cl - 1; + specializations = (jl_value_t*)jl_svec_fill(cl, jl_nothing); + jl_svecset(specializations, hv ? 0 : i--, mi); + jl_atomic_store_release(&m->specializations, specializations); + jl_gc_wb(m, specializations); + if (hv) + jl_smallintset_insert(&m->speckeyset, (jl_value_t*)m, speccache_hash, 0, (jl_svec_t*)specializations); } - else { - if (hv) { - _Atomic(jl_method_instance_t*) *data = (_Atomic(jl_method_instance_t*)*)jl_svec_data(specializations); - for (i = 0; i < cl; i++) { - jl_method_instance_t *mi = jl_atomic_load_relaxed(&data[i]); - if ((jl_value_t*)mi == jl_nothing) - break; - assert(!jl_types_equal(mi->specTypes, type)); - } - } - jl_method_instance_t *mi = mi_insert ? mi_insert : jl_get_specialized(m, type, sparams); - JL_GC_PUSH1(&mi); - if (hv ? (i + 1 >= cl || jl_svecref(specializations, i + 1) != jl_nothing) : (i <= 1 || jl_svecref(specializations, i - 2) != jl_nothing)) { - size_t ncl = cl < 8 ? 8 : (cl*3)>>1; - jl_svec_t *nc = jl_alloc_svec_uninit(ncl); - if (i > 0) - memcpy((char*)jl_svec_data(nc), jl_svec_data(specializations), sizeof(void*) * i); - for (int j = 0; j < ncl - cl; j++) - jl_svecset(nc, j+i, jl_nothing); - if (i < cl) - memcpy((char*)jl_svec_data(nc) + sizeof(void*) * (i + ncl - cl), - (char*)jl_svec_data(specializations) + sizeof(void*) * i, - sizeof(void*) * (cl - i)); - jl_atomic_store_release(&m->specializations, nc); - jl_gc_wb(m, nc); - specializations = nc; - if (!hv) - i += ncl - cl; + if (hv) { + _Atomic(jl_method_instance_t*) *data = (_Atomic(jl_method_instance_t*)*)jl_svec_data(specializations); + for (i = 0; i < cl; i++) { + jl_method_instance_t *mi = jl_atomic_load_relaxed(&data[i]); + if ((jl_value_t*)mi == jl_nothing) + break; + assert(!jl_types_equal(mi->specTypes, type)); } + // i points at the place to insert + } + if (hv ? (i + 1 >= cl || jl_svecref(specializations, i + 1) != jl_nothing) : (i <= 1 || jl_svecref(specializations, i - 2) != jl_nothing)) { + size_t ncl = cl < 7 ? 7 : (cl*3)>>1; + jl_svec_t *nc = jl_alloc_svec_uninit(ncl); + if (i > 0) + memcpy((char*)jl_svec_data(nc), jl_svec_data(specializations), sizeof(void*) * i); + for (int j = 0; j < ncl - cl; j++) + jl_svecset(nc, j+i, jl_nothing); + if (i < cl) + memcpy((char*)jl_svec_data(nc) + sizeof(void*) * (i + ncl - cl), + (char*)jl_svec_data(specializations) + sizeof(void*) * i, + sizeof(void*) * (cl - i)); + specializations = (jl_value_t*)nc; + jl_atomic_store_release(&m->specializations, specializations); + jl_gc_wb(m, specializations); if (!hv) - i -= 1; - assert(jl_svecref(specializations, i) == jl_nothing); - jl_svecset(specializations, i, mi); // jl_atomic_store_relaxed? - if (hv) { - // TODO: fuse lookup and insert steps? - jl_smallintset_insert(&m->speckeyset, (jl_value_t*)m, speccache_hash, i, specializations); - } - JL_UNLOCK(&m->writelock); - JL_GC_POP(); - return mi; + i += ncl - cl; } + assert(jl_svecref(specializations, i) == jl_nothing); + jl_svecset(specializations, i, mi); + if (hv) + jl_smallintset_insert(&m->speckeyset, (jl_value_t*)m, speccache_hash, i, (jl_svec_t*)specializations); + JL_GC_POP(); } + JL_UNLOCK(&m->writelock); // may gc + return mi; } JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo(jl_method_t *m JL_PROPAGATES_ROOT, jl_value_t *type, jl_svec_t *sparams) @@ -461,9 +493,19 @@ JL_DLLEXPORT void jl_mi_cache_insert(jl_method_instance_t *mi JL_ROOTING_ARGUMEN static int get_method_unspec_list(jl_typemap_entry_t *def, void *closure) { - jl_svec_t *specializations = jl_atomic_load_relaxed(&def->func.method->specializations); - size_t i, l = jl_svec_len(specializations); size_t world = jl_atomic_load_acquire(&jl_world_counter); + jl_value_t *specializations = jl_atomic_load_relaxed(&def->func.method->specializations); + if (specializations == (jl_value_t*)jl_emptysvec) + return 1; + if (!jl_is_svec(specializations)) { + jl_method_instance_t *mi = (jl_method_instance_t*)specializations; + assert(jl_is_method_instance(mi)); + if (jl_rettype_inferred(mi, world, world) == jl_nothing) + jl_array_ptr_1d_push((jl_array_t*)closure, (jl_value_t*)mi); + return 1; + } + size_t i, l = jl_svec_len(specializations); + JL_GC_PUSH1(&specializations); for (i = 0; i < l; i++) { jl_method_instance_t *mi = (jl_method_instance_t*)jl_svecref(specializations, i); if ((jl_value_t*)mi != jl_nothing) { @@ -472,6 +514,7 @@ static int get_method_unspec_list(jl_typemap_entry_t *def, void *closure) jl_array_ptr_1d_push((jl_array_t*)closure, (jl_value_t*)mi); } } + JL_GC_POP(); return 1; } @@ -1781,7 +1824,10 @@ static void jl_method_table_invalidate(jl_methtable_t *mt, jl_typemap_entry_t *m } // Invalidate the backedges int invalidated = 0; - jl_svec_t *specializations = jl_atomic_load_relaxed(&method->specializations); + jl_value_t *specializations = jl_atomic_load_relaxed(&method->specializations); + JL_GC_PUSH1(&specializations); + if (!jl_is_svec(specializations)) + specializations = (jl_value_t*)jl_svec1(specializations); l = jl_svec_len(specializations); for (i = 0; i < l; i++) { jl_method_instance_t *mi = (jl_method_instance_t*)jl_svecref(specializations, i); @@ -1791,6 +1837,7 @@ static void jl_method_table_invalidate(jl_methtable_t *mt, jl_typemap_entry_t *m invalidate_backedges(&do_nothing_with_codeinst, mi, max_world, "jl_method_table_disable"); } } + JL_GC_POP(); // XXX: this might have resolved an ambiguity, for which we have not tracked the edge here, // and thus now introduce a mistake into inference if (invalidated && _jl_debug_method_invalidation) { @@ -1974,9 +2021,17 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method jl_method_t *m = d[j]; if (morespec[j] == (char)morespec_is) continue; - jl_svec_t *specializations = jl_atomic_load_relaxed(&m->specializations); - _Atomic(jl_method_instance_t*) *data = (_Atomic(jl_method_instance_t*)*)jl_svec_data(specializations); - size_t i, l = jl_svec_len(specializations); + loctag = jl_atomic_load_relaxed(&m->specializations); // use loctag for a gcroot + _Atomic(jl_method_instance_t*) *data; + size_t i, l; + if (jl_is_svec(loctag)) { + data = (_Atomic(jl_method_instance_t*)*)jl_svec_data(loctag); + l = jl_svec_len(loctag); + } + else { + data = (_Atomic(jl_method_instance_t*)*) &loctag; + l = 1; + } enum morespec_options ambig = morespec_unknown; for (i = 0; i < l; i++) { jl_method_instance_t *mi = jl_atomic_load_relaxed(&data[i]); diff --git a/src/jltypes.c b/src/jltypes.c index 1b602e58b215a..0439c8d034e6b 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2597,7 +2597,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_ulong_type, jl_ulong_type, jl_type_type, - jl_simplevector_type, + jl_any_type, // union(jl_simplevector_type, jl_method_instance_type), jl_array_type, jl_string_type, jl_any_type, diff --git a/src/julia.h b/src/julia.h index 430cc0cef68af..4bb34cf653381 100644 --- a/src/julia.h +++ b/src/julia.h @@ -308,7 +308,7 @@ typedef struct _jl_method_t { jl_value_t *sig; // table of all jl_method_instance_t specializations we have - _Atomic(jl_svec_t*) specializations; // allocated as [hashable, ..., NULL, linear, ....] + _Atomic(jl_value_t*) specializations; // allocated as [hashable, ..., NULL, linear, ....], or a single item _Atomic(jl_array_t*) speckeyset; // index lookup by hash into specializations jl_value_t *slot_syms; // compacted list of slot names (String) diff --git a/src/method.c b/src/method.c index c5f50cdf883a5..02855e593ef7a 100644 --- a/src/method.c +++ b/src/method.c @@ -791,7 +791,7 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t *module) jl_task_t *ct = jl_current_task; jl_method_t *m = (jl_method_t*)jl_gc_alloc(ct->ptls, sizeof(jl_method_t), jl_method_type); - jl_atomic_store_relaxed(&m->specializations, jl_emptysvec); + jl_atomic_store_relaxed(&m->specializations, (jl_value_t*)jl_emptysvec); jl_atomic_store_relaxed(&m->speckeyset, (jl_array_t*)jl_an_empty_vec_any); m->sig = NULL; m->slot_syms = NULL; diff --git a/src/precompile_utils.c b/src/precompile_utils.c index b8b48ca839f01..5b0c231b04345 100644 --- a/src/precompile_utils.c +++ b/src/precompile_utils.c @@ -214,12 +214,17 @@ static int precompile_enq_all_specializations__(jl_typemap_entry_t *def, void *c jl_array_ptr_1d_push((jl_array_t*)closure, (jl_value_t*)mi); } else { - jl_svec_t *specializations = jl_atomic_load_relaxed(&def->func.method->specializations); - size_t i, l = jl_svec_len(specializations); - for (i = 0; i < l; i++) { - jl_value_t *mi = jl_svecref(specializations, i); - if (mi != jl_nothing) - precompile_enq_specialization_((jl_method_instance_t*)mi, closure); + jl_value_t *specializations = jl_atomic_load_relaxed(&def->func.method->specializations); + if (!jl_is_svec(specializations)) { + precompile_enq_specialization_((jl_method_instance_t*)specializations, closure); + } + else { + size_t i, l = jl_svec_len(specializations); + for (i = 0; i < l; i++) { + jl_value_t *mi = jl_svecref(specializations, i); + if (mi != jl_nothing) + precompile_enq_specialization_((jl_method_instance_t*)mi, closure); + } } } if (m->ccallable) @@ -292,12 +297,17 @@ static void *jl_precompile_worklist(jl_array_t *worklist, jl_array_t *extext_met for (i = 0; i < n; i++) { jl_method_t *method = (jl_method_t*)jl_array_ptr_ref(extext_methods, i); assert(jl_is_method(method)); - jl_svec_t *specializations = jl_atomic_load_relaxed(&method->specializations); - size_t j, l = jl_svec_len(specializations); - for (j = 0; j < l; j++) { - jl_value_t *mi = jl_svecref(specializations, j); - if (mi != jl_nothing) - precompile_enq_specialization_((jl_method_instance_t*)mi, m); + jl_value_t *specializations = jl_atomic_load_relaxed(&method->specializations); + if (!jl_is_svec(specializations)) { + precompile_enq_specialization_((jl_method_instance_t*)specializations, m); + } + else { + size_t j, l = jl_svec_len(specializations); + for (j = 0; j < l; j++) { + jl_value_t *mi = jl_svecref(specializations, j); + if (mi != jl_nothing) + precompile_enq_specialization_((jl_method_instance_t*)mi, m); + } } } n = jl_array_len(new_specializations); diff --git a/src/staticdata.c b/src/staticdata.c index 93b1356029bc5..2fd0c6fe17f07 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1945,6 +1945,7 @@ static void jl_root_new_gvars(jl_serializer_state *s, jl_image_t *image, uint32_ static void jl_compile_extern(jl_method_t *m, void *sysimg_handle) JL_GC_DISABLED { // install ccallable entry point in JIT + assert(m); // makes clang-sa happy jl_svec_t *sv = m->ccallable; int success = jl_compile_extern_c(NULL, NULL, sysimg_handle, jl_svecref(sv, 0), jl_svecref(sv, 1)); if (!success) @@ -2091,12 +2092,17 @@ static int strip_all_codeinfos__(jl_typemap_entry_t *def, void *_env) jl_gc_wb(m, m->source); } } - jl_svec_t *specializations = m->specializations; - size_t i, l = jl_svec_len(specializations); - for (i = 0; i < l; i++) { - jl_value_t *mi = jl_svecref(specializations, i); - if (mi != jl_nothing) - strip_specializations_((jl_method_instance_t*)mi); + jl_value_t *specializations = m->specializations; + if (!jl_is_svec(specializations)) { + strip_specializations_((jl_method_instance_t*)specializations); + } + else { + size_t i, l = jl_svec_len(specializations); + for (i = 0; i < l; i++) { + jl_value_t *mi = jl_svecref(specializations, i); + if (mi != jl_nothing) + strip_specializations_((jl_method_instance_t*)mi); + } } if (m->unspecialized) strip_specializations_(m->unspecialized); diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index df6bcfd61d9f6..e01ba40c63aed 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -369,12 +369,18 @@ static int jl_collect_methcache_from_mod(jl_typemap_entry_t *ml, void *closure) } if (edges_map == NULL) return 1; - jl_svec_t *specializations = m->specializations; - size_t i, l = jl_svec_len(specializations); - for (i = 0; i < l; i++) { - jl_method_instance_t *callee = (jl_method_instance_t*)jl_svecref(specializations, i); - if ((jl_value_t*)callee != jl_nothing) - collect_backedges(callee, !s); + jl_value_t *specializations = jl_atomic_load_relaxed(&m->specializations); + if (!jl_is_svec(specializations)) { + jl_method_instance_t *callee = (jl_method_instance_t*)specializations; + collect_backedges(callee, !s); + } + else { + size_t i, l = jl_svec_len(specializations); + for (i = 0; i < l; i++) { + jl_method_instance_t *callee = (jl_method_instance_t*)jl_svecref(specializations, i); + if ((jl_value_t*)callee != jl_nothing) + collect_backedges(callee, !s); + } } return 1; } diff --git a/stdlib/InteractiveUtils/src/codeview.jl b/stdlib/InteractiveUtils/src/codeview.jl index 8c0658142c019..29a64343b8370 100644 --- a/stdlib/InteractiveUtils/src/codeview.jl +++ b/stdlib/InteractiveUtils/src/codeview.jl @@ -189,7 +189,7 @@ function _dump_function(@nospecialize(f), @nospecialize(t), native::Bool, wrappe # OC was constructed from inferred source. There's only one # specialization and we can't infer anything more precise either. world = f.source.primary_world - linfo = f.source.specializations[1] + linfo = f.source.specializations::Core.MethodInstance Core.Compiler.hasintersect(typeof(f).parameters[1], t) || (warning = OC_MISMATCH_WARNING) else linfo = Core.Compiler.specialize_method(f.source, Tuple{typeof(f.captures), t.parameters...}, Core.svec()) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 8f8598c82bded..89ff2067a3d57 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1165,7 +1165,8 @@ end function count_specializations(method::Method) specs = method.specializations - n = count(i -> isassigned(specs, i), 1:length(specs)) + specs isa Core.MethodInstance && return 1 + n = count(!isnothing, specs::Core.SimpleVector) return n end @@ -1180,7 +1181,7 @@ copy_dims_pair(out) = () copy_dims_pair(out, dim::Int, tail...) = copy_dims_pair(out => dim, tail...) copy_dims_pair(out, dim::Colon, tail...) = copy_dims_pair(out => dim, tail...) @test Base.return_types(copy_dims_pair, (Tuple{}, Vararg{Union{Int,Colon}})) == Any[Tuple{}, Tuple{}, Tuple{}] -@test all(m -> 5 < count_specializations(m) < 15, methods(copy_dims_pair)) # currently about 7 +@test all(m -> 3 < count_specializations(m) < 15, methods(copy_dims_pair)) # currently about 5 # splatting an ::Any should still allow inference to use types of parameters preceding it f22364(::Int, ::Any...) = 0 diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index fa0d1ae2b1753..b8bbba78e360f 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -616,8 +616,7 @@ let f42078(a) end let # make sure to discard the inferred source - specs = collect(only(methods(f42078)).specializations) - mi = specs[findfirst(!isnothing, specs)]::Core.MethodInstance + mi = only(methods(f42078)).specializations::Core.MethodInstance codeinf = getcache(mi)::Core.CodeInstance @atomic codeinf.inferred = nothing end diff --git a/test/core.jl b/test/core.jl index f3ab922868a53..83ede23b9850e 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7968,7 +7968,7 @@ vect47476(::Type{T}) where {T} = T g47476(::Union{Nothing,Int,Val{T}}...) where {T} = T @test_throws UndefVarError(:T) g47476(nothing, 1, nothing, 2, nothing, 3, nothing, 4, nothing, 5) @test g47476(nothing, 1, nothing, 2, nothing, 3, nothing, 4, nothing, 5, Val(6)) === 6 -let spec = only(methods(g47476)).specializations +let spec = only(methods(g47476)).specializations::Core.SimpleVector @test !isempty(spec) @test any(mi -> mi !== nothing && Base.isvatuple(mi.specTypes), spec) @test all(mi -> mi === nothing || !Base.has_free_typevars(mi.specTypes), spec) diff --git a/test/precompile.jl b/test/precompile.jl index eb1666355d701..2b5405a06c88d 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -665,8 +665,9 @@ precompile_test_harness("code caching") do dir # size(::Vector) has an inferred specialization for Vector{X} msize = which(size, (Vector{<:Any},)) hasspec = false - for i = 1:length(msize.specializations) - mi = msize.specializations[i] + msizespecs = msize.specializations::Core.SimpleVector + for i = 1:length(msizespecs) + mi = msizespecs[i] if isa(mi, Core.MethodInstance) && mi.specTypes == Tuple{typeof(size),Vector{Cacheb8321416e8a3e2f1.X}} if isdefined(mi, :cache) && isa(mi.cache, Core.CodeInstance) && mi.cache.max_world == typemax(UInt) && mi.cache.inferred !== nothing hasspec = true @@ -689,7 +690,7 @@ precompile_test_harness("code caching") do dir @test !isempty(groups[Bid]) # Check that internal methods and their roots are accounted appropriately minternal = which(M.getelsize, (Vector,)) - mi = minternal.specializations[1] + mi = minternal.specializations::Core.MethodInstance @test mi.specTypes == Tuple{typeof(M.getelsize),Vector{Int32}} ci = mi.cache @test ci.relocatability == 1 @@ -698,7 +699,9 @@ precompile_test_harness("code caching") do dir Base.invokelatest() do M.getelsize(M.X2[]) end - mi = minternal.specializations[2] + mispecs = minternal.specializations::Core.SimpleVector + @test mispecs[1] === mi + mi = mispecs[2]::Core.MethodInstance ci = mi.cache @test ci.relocatability == 0 # PkgA loads PkgB, and both add roots to the same `push!` method (both before and after loading B) @@ -783,8 +786,11 @@ precompile_test_harness("code caching") do dir MB = getfield(@__MODULE__, RootB) M = getfield(MA, RootModule) m = which(M.f, (Any,)) - for mi in m.specializations + mspecs = m.specializations + mspecs isa Core.SimpleVector || (mspecs = Core.svec(mspecs)) + for mi in mspecs mi === nothing && continue + mi = mi::Core.MethodInstance if mi.specTypes.parameters[2] === Int8 # external callers mods = Module[] @@ -894,12 +900,13 @@ precompile_test_harness("code caching") do dir MC = getfield(@__MODULE__, StaleC) world = Base.get_world_counter() m = only(methods(MA.use_stale)) - mi = m.specializations[1] + mi = m.specializations::Core.MethodInstance @test hasvalid(mi, world) # it was re-inferred by StaleC m = only(methods(MA.build_stale)) - mis = filter(!isnothing, collect(m.specializations)) + mis = filter(!isnothing, collect(m.specializations::Core.SimpleVector)) @test length(mis) == 2 for mi in mis + mi = mi::Core.MethodInstance if mi.specTypes.parameters[2] == Int @test mi.cache.max_world < world else @@ -909,16 +916,16 @@ precompile_test_harness("code caching") do dir end end m = only(methods(MB.useA)) - mi = m.specializations[1] + mi = m.specializations::Core.MethodInstance @test !hasvalid(mi, world) # invalidated by the stale(x::String) method in StaleC m = only(methods(MC.call_buildstale)) - mi = m.specializations[1] + mi = m.specializations::Core.MethodInstance @test hasvalid(mi, world) # was compiled with the new method # Reporting test (ensure SnoopCompile works) @test all(i -> isassigned(invalidations, i), eachindex(invalidations)) m = only(methods(MB.call_nbits)) - for mi in m.specializations + for mi in m.specializations::Core.SimpleVector mi === nothing && continue hv = hasvalid(mi, world) @test mi.specTypes.parameters[end] === Integer ? !hv : hv @@ -939,7 +946,7 @@ precompile_test_harness("code caching") do dir @test isa(invalidations[j+1], Vector{Any}) # [nbits(::UInt8)] m = only(methods(MB.map_nbits)) - @test !hasvalid(m.specializations[1], world+1) # insert_backedges invalidations also trigger their backedges + @test !hasvalid(m.specializations::Core.MethodInstance, world+1) # insert_backedges invalidations also trigger their backedges end precompile_test_harness("invoke") do dir @@ -1065,7 +1072,7 @@ precompile_test_harness("invoke") do dir for func in (M.f, M.g, M.internal, M.fnc, M.gnc, M.internalnc) m = get_method_for_type(func, Real) - mi = m.specializations[1] + mi = m.specializations::Core.MethodInstance @test length(mi.backedges) == 2 @test mi.backedges[1] === Tuple{typeof(func), Real} @test isa(mi.backedges[2], Core.MethodInstance) @@ -1073,7 +1080,7 @@ precompile_test_harness("invoke") do dir end for func in (M.q, M.qnc) m = get_method_for_type(func, Integer) - mi = m.specializations[1] + mi = m.specializations::Core.MethodInstance @test length(mi.backedges) == 2 @test mi.backedges[1] === Tuple{typeof(func), Integer} @test isa(mi.backedges[2], Core.MethodInstance) @@ -1081,31 +1088,31 @@ precompile_test_harness("invoke") do dir end m = get_method_for_type(M.h, Real) - @test isempty(m.specializations) + @test m.specializations === Core.svec() m = get_method_for_type(M.hnc, Real) - @test isempty(m.specializations) + @test m.specializations === Core.svec() m = only(methods(M.callq)) - @test isempty(m.specializations) || nvalid(m.specializations[1]) == 0 + @test m.specializations === Core.svec() || nvalid(m.specializations::Core.MethodInstance) == 0 m = only(methods(M.callqnc)) - @test isempty(m.specializations) || nvalid(m.specializations[1]) == 0 + @test m.specializations === Core.svec() || nvalid(m.specializations::Core.MethodInstance) == 0 m = only(methods(M.callqi)) - @test m.specializations[1].specTypes == Tuple{typeof(M.callqi), Int} + @test (m.specializations::Core.MethodInstance).specTypes == Tuple{typeof(M.callqi), Int} m = only(methods(M.callqnci)) - @test m.specializations[1].specTypes == Tuple{typeof(M.callqnci), Int} + @test (m.specializations::Core.MethodInstance).specTypes == Tuple{typeof(M.callqnci), Int} m = only(methods(M.g44320)) - @test m.specializations[1].cache.max_world == typemax(UInt) + @test (m.specializations::Core.MethodInstance).cache.max_world == typemax(UInt) m = which(MI.getlast, (Any,)) - @test m.specializations[1].cache.max_world == typemax(UInt) + @test (m.specializations::Core.MethodInstance).cache.max_world == typemax(UInt) # Precompile specific methods for arbitrary arg types invokeme(x) = 1 invokeme(::Int) = 2 m_any, m_int = sort(collect(methods(invokeme)); by=m->(m.file,m.line)) @test precompile(invokeme, (Int,), m_any) - @test m_any.specializations[1].specTypes === Tuple{typeof(invokeme), Int} - @test isempty(m_int.specializations) + @test (m_any.specializations::Core.MethodInstance).specTypes === Tuple{typeof(invokeme), Int} + @test m_int.specializations === Core.svec() end # test --compiled-modules=no command line option @@ -1510,10 +1517,10 @@ precompile_test_harness("No external edges") do load_path Base.compilecache(Base.PkgId("NoExternalEdges")) @eval begin using NoExternalEdges - @test only(methods(NoExternalEdges.foo1)).specializations[1].cache.max_world != 0 - @test only(methods(NoExternalEdges.foo2)).specializations[1].cache.max_world != 0 - @test only(methods(NoExternalEdges.foo3)).specializations[1].cache.max_world != 0 - @test only(methods(NoExternalEdges.foo4)).specializations[1].cache.max_world != 0 + @test (only(methods(NoExternalEdges.foo1)).specializations::Core.MethodInstance).cache.max_world != 0 + @test (only(methods(NoExternalEdges.foo2)).specializations::Core.MethodInstance).cache.max_world != 0 + @test (only(methods(NoExternalEdges.foo3)).specializations::Core.MethodInstance).cache.max_world != 0 + @test (only(methods(NoExternalEdges.foo4)).specializations::Core.MethodInstance).cache.max_world != 0 end end @@ -1527,7 +1534,7 @@ end @test precompile(M.f, (Int, Any)) @test precompile(M.f, (AbstractFloat, Any)) mis = map(methods(M.f)) do m - m.specializations[1] + m.specializations::Core.MethodInstance end @test any(mi -> mi.specTypes.parameters[2] === Any, mis) @test all(mi -> isa(mi.cache, Core.CodeInstance), mis) @@ -1608,7 +1615,7 @@ end f46778(::Any, ::Type{Int}) = 1 f46778(::Any, ::DataType) = 2 @test precompile(Tuple{typeof(f46778), Int, DataType}) - @test which(f46778, Tuple{Any,DataType}).specializations[1].cache.invoke != C_NULL + @test (which(f46778, Tuple{Any,DataType}).specializations::Core.MethodInstance).cache.invoke != C_NULL end diff --git a/test/reflection.jl b/test/reflection.jl index 7fa73d56df2b2..e926cdf0a2b9f 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -532,7 +532,7 @@ let ft = typeof(f18888) code_typed(f18888, Tuple{}; optimize=false) - @test !isempty(m.specializations) # uncached, but creates the specializations entry + @test m.specializations !== Core.svec() # uncached, but creates the specializations entry mi = Core.Compiler.specialize_method(m, Tuple{ft}, Core.svec()) interp = Core.Compiler.NativeInterpreter(world) @test !Core.Compiler.haskey(Core.Compiler.code_cache(interp), mi) diff --git a/test/worlds.jl b/test/worlds.jl index a2baa741b592a..ff6b0197e8051 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -234,8 +234,7 @@ function method_instance(f, types=Base.default_tt(f)) inst = nothing tt = Base.signature_type(f, types) specs = m.specializations - if isa(specs, Nothing) - elseif isa(specs, Core.SimpleVector) + if isa(specs, Core.SimpleVector) for i = 1:length(specs) mi = specs[i] if mi isa Core.MethodInstance @@ -246,10 +245,9 @@ function method_instance(f, types=Base.default_tt(f)) end end else - Base.visit(specs) do mi - if mi.specTypes === tt - inst = mi - end + mi = specs::Core.MethodInstance + if mi.specTypes === tt + inst = mi end end return inst From 9c19f40962751acc5b8dc26558247cb621a90a58 Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Tue, 21 Mar 2023 08:53:02 -0700 Subject: [PATCH 347/775] Use the POSIX timers API rather than itimer on FreeBSD (#49072) POSIX.1-2008 marks `getitimer`/`setitimer` obsolete in favor of `timer_gettime`/`timer_settime`. Additionally, POSIX.1-2017 marks `SIGPROF` obsolete. Thus we can make simply have FreeBSD use the same signals code paths as Linux, which already uses the timer API and uses `SIGUSR1` rather than `SIGPROF`. The code conditional on the `HAVE_ITIMER` flag, as well as the flag itself, have been removed since they're no longer used. --- src/signals-unix.c | 46 +--------------------------------------------- 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/src/signals-unix.c b/src/signals-unix.c index 6ed664199fd2b..c35fe0079f1a0 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -27,9 +27,7 @@ #ifdef __APPLE__ // Darwin's mach ports allow signal-free thread management #define HAVE_MACH #define HAVE_KEVENT -#elif defined(__FreeBSD__) // generic bsd -#define HAVE_ITIMER -#else // generic linux +#else // generic Linux or BSD #define HAVE_TIMER #endif @@ -597,37 +595,6 @@ JL_DLLEXPORT void jl_profile_stop_timer(void) } } -#elif defined(HAVE_ITIMER) -// BSD-style timers -#include -#include -struct itimerval timerprof; - -JL_DLLEXPORT int jl_profile_start_timer(void) -{ - timerprof.it_interval.tv_sec = 0; - timerprof.it_interval.tv_usec = 0; - timerprof.it_value.tv_sec = nsecprof / GIGA; - timerprof.it_value.tv_usec = ((nsecprof % GIGA) + 999) / 1000; - // Because SIGUSR1 is multipurpose, set `running` before so that we know that the first SIGUSR1 came from the timer - running = 1; - if (setitimer(ITIMER_PROF, &timerprof, NULL) == -1) { - running = 0; - return -3; - } - return 0; -} - -JL_DLLEXPORT void jl_profile_stop_timer(void) -{ - if (running) { - memset(&timerprof, 0, sizeof(timerprof)); - setitimer(ITIMER_PROF, &timerprof, NULL); - last_timer_delete_time = jl_hrtime(); - running = 0; - } -} - #else #error no profile tools available @@ -686,8 +653,6 @@ const static int sigwait_sigs[] = { #endif #if defined(HAVE_TIMER) SIGUSR1, -#elif defined(HAVE_ITIMER) - SIGPROF, #endif 0 }; @@ -805,8 +770,6 @@ static void *signal_listener(void *arg) info.si_value.sival_ptr == &timerprof)) profile = 0; #endif -#elif defined(HAVE_ITIMER) - profile = (sig == SIGPROF); #endif #endif @@ -954,8 +917,6 @@ static void *signal_listener(void *arg) jl_check_profile_autostop(); #if defined(HAVE_TIMER) timer_settime(timerprof, 0, &itsprof, NULL); -#elif defined(HAVE_ITIMER) - setitimer(ITIMER_PROF, &timerprof, NULL); #endif } #endif @@ -1089,11 +1050,6 @@ void jl_install_default_signal_handlers(void) } // need to ensure the following signals are not SIG_IGN, even though they will be blocked act_die.sa_flags = SA_SIGINFO | SA_RESTART | SA_RESETHAND; -#if defined(HAVE_ITIMER) - if (sigaction(SIGPROF, &act_die, NULL) < 0) { - jl_errorf("fatal error: sigaction: %s", strerror(errno)); - } -#endif #ifdef SIGINFO if (sigaction(SIGINFO, &act_die, NULL) < 0) { jl_errorf("fatal error: sigaction: %s", strerror(errno)); From 2f687a20ca99e09dd400a309db60c7edf97251ee Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 21 Mar 2023 12:08:42 -0400 Subject: [PATCH 348/775] improve `apply_type_tfunc` accuracy in rare case (#49069) In the unlikely event this call fails, we can either confidently conclude the result will always fail and stop inference immediately there. Or we can at least conclude that the base type is confidently known, which can potentially improve ml-matches performance later by excluding Union{} or other subtypes. --- base/compiler/tfuncs.jl | 37 +++++++++++++++++++++++++++++++------ test/compiler/inference.jl | 6 +++++- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index a89d9b89826b5..b23d1b17efd42 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1628,6 +1628,7 @@ function apply_type_nothrow(𝕃::AbstractLattice, argtypes::Vector{Any}, @nospe (headtype === Union) && return true isa(rt, Const) && return true u = headtype + # TODO: implement optimization for isvarargtype(u) and istuple occurences (which are valid but are not UnionAll) for i = 2:length(argtypes) isa(u, UnionAll) || return false ai = widenconditional(argtypes[i]) @@ -1747,6 +1748,9 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K, end ua = ua.body end + if largs > outer_start && isa(headtype, UnionAll) # e.g. !isvarargtype(ua) && !istuple + return Bottom # too many arguments + end outer_start = outer_start - largs + 1 varnamectr = 1 @@ -1815,19 +1819,40 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K, push!(outervars, v) end end - if isa(ua, UnionAll) + if ua isa UnionAll ua = ua.body - else - ua = nothing + #otherwise, sometimes ua isa Vararg (Core.TypeofVararg) or Tuple (DataType) end end local appl try appl = apply_type(headtype, tparams...) catch ex - # type instantiation might fail if one of the type parameters - # doesn't match, which could happen if a type estimate is too coarse - return isvarargtype(headtype) ? TypeofVararg : Type{<:headtype} + # type instantiation might fail if one of the type parameters doesn't + # match, which could happen only if a type estimate is too coarse + # and might guess a concrete value while the actual type for it is Bottom + if !uncertain + return Union{} + end + canconst = false + uncertain = true + empty!(outervars) + outer_start = 1 + # FIXME: if these vars are substituted with TypeVar here, the result + # might be wider than the input, so should we use the `.name.wrapper` + # object here instead, to replace all of these outervars with + # unconstrained ones? Note that this code is nearly unreachable though, + # and possibly should simply return Union{} here also, since + # `apply_type` is already quite conservative about detecting and + # throwing errors. + appl = headtype + if isa(appl, UnionAll) + for _ = 1:largs + appl = appl::UnionAll + push!(outervars, appl.var) + appl = appl.body + end + end end !uncertain && canconst && return Const(appl) if isvarargtype(appl) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 89ff2067a3d57..85509ebf57199 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2648,10 +2648,14 @@ end |> only === Int # https://github.com/JuliaLang/julia/issues/47089 import Core: Const import Core.Compiler: apply_type_tfunc -struct Issue47089{A,B} end +struct Issue47089{A<:Number,B<:Number} end let 𝕃 = Core.Compiler.fallback_lattice A = Type{<:Integer} @test apply_type_tfunc(𝕃, Const(Issue47089), A, A) <: (Type{Issue47089{A,B}} where {A<:Integer, B<:Integer}) + @test apply_type_tfunc(𝕃, Const(Issue47089), Const(Int), Const(Int), Const(Int)) === Union{} + @test apply_type_tfunc(𝕃, Const(Issue47089), Const(String)) === Union{} + @test apply_type_tfunc(𝕃, Const(Issue47089), Const(AbstractString)) === Union{} + @test apply_type_tfunc(𝕃, Const(Issue47089), Type{Ptr}, Type{Ptr{T}} where T) === Base.rewrap_unionall(Type{Issue47089.body.body}, Issue47089) end @test only(Base.return_types(keys, (Dict{String},))) == Base.KeySet{String, T} where T<:(Dict{String}) @test only(Base.return_types((r)->similar(Array{typeof(r[])}, 1), (Base.RefValue{Array{Int}},))) == Vector{<:Array{Int}} From 4d8c22f7f69a2767224290b4bd9baf52da7600e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Tue, 21 Mar 2023 20:43:16 +0000 Subject: [PATCH 349/775] [LibCURL_jll] Upgrade to v8.0.1 (#49063) --- deps/checksums/curl | 68 ++++++++++++++++----------------- deps/curl.version | 2 +- stdlib/LibCURL_jll/Project.toml | 2 +- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/deps/checksums/curl b/deps/checksums/curl index acbcb749ac5e2..0156cdc1588eb 100644 --- a/deps/checksums/curl +++ b/deps/checksums/curl @@ -1,36 +1,36 @@ LibCURL-fd8af649b38ae20c3ff7f5dca53753512ca00376.tar.gz/md5/f082283e6a35fcba5b63c9a6219d8003 LibCURL-fd8af649b38ae20c3ff7f5dca53753512ca00376.tar.gz/sha512/3bea5fa3fb6d29651daa923ae6bcb8eeb356ab9f2a1f3e005a6b746b617b0cf609aed4cadda4181783959840873c04b18e34e45ab973549169d19775a05ea01e -LibCURL.v7.88.1+0.aarch64-apple-darwin.tar.gz/md5/f2f284f0497d7bc23946de9fc672180c -LibCURL.v7.88.1+0.aarch64-apple-darwin.tar.gz/sha512/a3e87f3a185112b7145bf9b34d8289835bf3d9195d0cb9fb1c0448037a8d4f5891d0904749e6792f7d75b686a7abab9f34c77c0e4576a1264f2b346ea3dce8a4 -LibCURL.v7.88.1+0.aarch64-linux-gnu.tar.gz/md5/1a74a07a6092b0d849a3f3f1f18a9c82 -LibCURL.v7.88.1+0.aarch64-linux-gnu.tar.gz/sha512/9b855e800b2ae7613efd645328d406165fe80148695f9a4b3ad5c2c3f473bee41bf0d96a33d6396ab31352abf5ece1179544d13b9b6cf47968fe2bcb2f21375e -LibCURL.v7.88.1+0.aarch64-linux-musl.tar.gz/md5/8901469d4141c53e51fe4b69323ad69d -LibCURL.v7.88.1+0.aarch64-linux-musl.tar.gz/sha512/d45ffb9c217c283644a31d59c605ada731b7ecd8dcd56bc27b026ca9a76c3cb715a105fc5ecd724eb2113b734d840cd3eb1e747a5a0e33df1a192ef5db33e843 -LibCURL.v7.88.1+0.armv6l-linux-gnueabihf.tar.gz/md5/10b64832ec940f96010ecdb0c24433c9 -LibCURL.v7.88.1+0.armv6l-linux-gnueabihf.tar.gz/sha512/892a6a615547d0676ec60af0af626477891641b105f1b3aaf8dc7d0daf27bf12d7e6bc78da9d54af8effb0453b69a5491bc9240955e600fc4fbe3c83d9ed1226 -LibCURL.v7.88.1+0.armv6l-linux-musleabihf.tar.gz/md5/1e91598375bd6cc1dfad682d610d3bb2 -LibCURL.v7.88.1+0.armv6l-linux-musleabihf.tar.gz/sha512/67275f960c47e42e9143eb22907f7c448abd107b18963538a3125adf3fb8afdb33a16134c1e601cccb93afa564c7071e9ef47be132260ecd209c3a67f0909189 -LibCURL.v7.88.1+0.armv7l-linux-gnueabihf.tar.gz/md5/0884c064608ddae6a057527612699eb5 -LibCURL.v7.88.1+0.armv7l-linux-gnueabihf.tar.gz/sha512/0f3b59bc751682cd6027ffdd94f6e9468a729d39f12702b6adfb025c888044920fab232f02718c90e9033dd2456e1e514285e27c297595670e2b6fd96cf4a9d4 -LibCURL.v7.88.1+0.armv7l-linux-musleabihf.tar.gz/md5/6e34064f4e3a54cb7df69063eff41098 -LibCURL.v7.88.1+0.armv7l-linux-musleabihf.tar.gz/sha512/aa76cec424e245194c381390dec0f934cf3e29f0656c0bfc3993490b8fcc893557d1e4028f602cb08cce148c4dd74ca822f8c962d106cffeb611dc782176e621 -LibCURL.v7.88.1+0.i686-linux-gnu.tar.gz/md5/650b714017567cd82da67a5e67a2e221 -LibCURL.v7.88.1+0.i686-linux-gnu.tar.gz/sha512/16b6db47d14ff3e3950d3ae277fbc22235a9f11db457dd119e09150a446d5c35a7a851bc030082a7147b46d882d0a87ba1248b34866be4e0d88584c197de35ac -LibCURL.v7.88.1+0.i686-linux-musl.tar.gz/md5/d3ab851beb2442f182a69bd14008e027 -LibCURL.v7.88.1+0.i686-linux-musl.tar.gz/sha512/b124d27cdd8457ad39cbef5ef34dc567f0a846b69db3c701b85f03c3902f22e56c820bf4c3368b59a46473dfb2bcbaf0f36ee92fcfd1ff1d8273a07d6a741a8c -LibCURL.v7.88.1+0.i686-w64-mingw32.tar.gz/md5/b333120c19e085a7ff4c5a1d542c7601 -LibCURL.v7.88.1+0.i686-w64-mingw32.tar.gz/sha512/e6fbad17d3f53fda8e5df4d9087af1a9bb6d6179c569b017c29e800631db71a74ed81161dd38a502a81affc0dcdf94a75ee677b27e57a6641247c7c876c889fa -LibCURL.v7.88.1+0.powerpc64le-linux-gnu.tar.gz/md5/e1b329350e0214a9f7cb8b06a02f8868 -LibCURL.v7.88.1+0.powerpc64le-linux-gnu.tar.gz/sha512/7c29b66d2e2ffce79da1b4d8dcc8b54137e340509b36283797d2890219e8ca61168856768d57db586d44469451c0e05055d314ef55e40717edb86c5378f079a6 -LibCURL.v7.88.1+0.x86_64-apple-darwin.tar.gz/md5/9a5f92e8512aed1483b42d7d36c4839d -LibCURL.v7.88.1+0.x86_64-apple-darwin.tar.gz/sha512/424be4468268aba40eb43940ab2dee976148ee2caea62167228ced615fc6ea348bb326a1261766a1bb2c56346b02881404a392b8c67563a4060cee1b1c533ff2 -LibCURL.v7.88.1+0.x86_64-linux-gnu.tar.gz/md5/d8cdbbbde5ae302e2b119b199d5c50a8 -LibCURL.v7.88.1+0.x86_64-linux-gnu.tar.gz/sha512/ba748b7e28b258efebec8018b6d8488e57b192731f63e9159dfafd868d4d681cae2197bba0cd8ac56c8e811eca4d7ba2560f9c3edf4e8d11ef88d6445d900c78 -LibCURL.v7.88.1+0.x86_64-linux-musl.tar.gz/md5/3345f4b110bbf1bf24ed386f3b9b33dc -LibCURL.v7.88.1+0.x86_64-linux-musl.tar.gz/sha512/770bbbeb858aab352746acadc65b287178c51678bec1c939707491da47db014009b2eb3535c17ed54384766f40abcc3049525498d72a9a4afa618fcef43c3be6 -LibCURL.v7.88.1+0.x86_64-unknown-freebsd.tar.gz/md5/18eea62ac368923ecb1bffaaa9a28dd3 -LibCURL.v7.88.1+0.x86_64-unknown-freebsd.tar.gz/sha512/72cbbfb57c2869439ddacde8f013d926d7bc25c43b5ebeb92d7eabcf7386055a691b32857c58ecd611fec6ad52cc8b3a6e18d01810928ecd4a967f045fe052f2 -LibCURL.v7.88.1+0.x86_64-w64-mingw32.tar.gz/md5/55836fd5e1daad9c19b489f56644530b -LibCURL.v7.88.1+0.x86_64-w64-mingw32.tar.gz/sha512/918ec33a8c90fba1e014a9b44f4f76ea563ecd15dbce7b74f8f4ad502e7466920da095bbea3e60a7900ca5d0872dd8c1d332303d6aab39e231c32f4d96765136 -curl-7.88.1.tar.bz2/md5/e90619abb4d275f767e6bfceab5ddabb -curl-7.88.1.tar.bz2/sha512/998fbfc65733b7c97edfc6fbb322757ab2d3116af9c7077a7435ecb27189f4c0052602551fa7f723ecb788177c229399f0342d8070d0dec226646acd43a67963 +LibCURL.v8.0.1+0.aarch64-apple-darwin.tar.gz/md5/f697b4391608c2916ef159187e0d0b29 +LibCURL.v8.0.1+0.aarch64-apple-darwin.tar.gz/sha512/41da87eed77ffac391a60a4af7fdc707f117affebe54960eaf43e3077440ce17d95fbe0f47de41bb1456e222e7a126d687fa0beb26cf98713b3472e9b3ba9e57 +LibCURL.v8.0.1+0.aarch64-linux-gnu.tar.gz/md5/9d3e7e7601ac21a587bbb4289e149225 +LibCURL.v8.0.1+0.aarch64-linux-gnu.tar.gz/sha512/67ac7bc108cc274ee5e088411dd9d652a969952892236d6c37a6dcd710a1887f9ff83df2c01ca0f5b16b2086852077d6c62ae7a13f7b9ac4b9e257cd1aacb0ea +LibCURL.v8.0.1+0.aarch64-linux-musl.tar.gz/md5/bd2b62cd40b9e87fe149d842d4ff55ca +LibCURL.v8.0.1+0.aarch64-linux-musl.tar.gz/sha512/7c6bff3dbe341e2a271b61e02767a25768b74631894c789fffdef580605d821518274a04d9441c9b5d3255b9a9297d0d35f22310dccaab367aa92d928f25c062 +LibCURL.v8.0.1+0.armv6l-linux-gnueabihf.tar.gz/md5/9effcc21c5074ef88ad54c8b6b7a3f8f +LibCURL.v8.0.1+0.armv6l-linux-gnueabihf.tar.gz/sha512/9327fc8e0db9edcf941548b0291e0bafe9b956e92f6edf47795ca961303a24ed305b30b09f29478a70149056411c4ca4652facbeca89c2bb3db41a6c97df14a9 +LibCURL.v8.0.1+0.armv6l-linux-musleabihf.tar.gz/md5/9cb716973ec75e2a2fa7379201aad59f +LibCURL.v8.0.1+0.armv6l-linux-musleabihf.tar.gz/sha512/3e4d22be628af7b478862593653a5d34c2d69623b70f128d9f15641ab3366282aadee96bc46ffacafa0dcbc539fbbda4e92f6ff5c7a4e65f59040948233eabce +LibCURL.v8.0.1+0.armv7l-linux-gnueabihf.tar.gz/md5/95bd98a64034f8dfc5e1dda8fb7ac94e +LibCURL.v8.0.1+0.armv7l-linux-gnueabihf.tar.gz/sha512/6a7898670e71efd7f06e614cdf535cf390eb6def9e93409d4ce2d9811a8e1f892959c0f6ca8e370f49e215df495ee8f95e1b7d9f92e2708ca548344b6ef9cc22 +LibCURL.v8.0.1+0.armv7l-linux-musleabihf.tar.gz/md5/42aeb569e80865377c65bba6cc84b262 +LibCURL.v8.0.1+0.armv7l-linux-musleabihf.tar.gz/sha512/fa46e52d8abd49e22636e48fb43f11be95bfdabbc13142e0cdaf4bb892ff982eb09abd9f3bf1c33ad374efc18ce21ab9968ed22c084411a55afddec0c459ab3d +LibCURL.v8.0.1+0.i686-linux-gnu.tar.gz/md5/ded5d6d6580b979c372992c0fcf0aad6 +LibCURL.v8.0.1+0.i686-linux-gnu.tar.gz/sha512/f8a40285a25d61878e87d525bebcfe6e8c30cc5a40f38297de774c8e3191490c38716b3938cf81582afb23714a38405c20ed0241bcd3d41c68a5594822498b70 +LibCURL.v8.0.1+0.i686-linux-musl.tar.gz/md5/cd2bcf96545c783f5012611824169a93 +LibCURL.v8.0.1+0.i686-linux-musl.tar.gz/sha512/318dd3adcbf36c7979df9f394e78b7fb876dc60c9ec87d6b0edf47676c69df4dc3e73c07b2434b15c6e7497b385dc0fbf3fe7e3235b291a369f6f1d883c99645 +LibCURL.v8.0.1+0.i686-w64-mingw32.tar.gz/md5/276cc56eaf744ac0a5cec6c8c396ede7 +LibCURL.v8.0.1+0.i686-w64-mingw32.tar.gz/sha512/55cd7882ad976aeed1acaab7b1d59279ff3a0d2456d0bffa6240957ac6f152e903485f0ca05baafa5e97e0d1474cb204987eb9c94b1b2ddd657b52864a44c646 +LibCURL.v8.0.1+0.powerpc64le-linux-gnu.tar.gz/md5/cfdc41294b2f4aa85bb8b27beced17ca +LibCURL.v8.0.1+0.powerpc64le-linux-gnu.tar.gz/sha512/24f92091ab44a3be40228a9d9a57febc026f49b12c538c98e46a06dbcd679086332b773662126c68dbe4a60dd90a77c970c8a398237afbcf06c660fdbea16a76 +LibCURL.v8.0.1+0.x86_64-apple-darwin.tar.gz/md5/10a19a4f428951adbca7cfee91406498 +LibCURL.v8.0.1+0.x86_64-apple-darwin.tar.gz/sha512/28ddbad4310ed886c65edf28ccf01a5aba77fe11784740600aaec2aaa5c10c5e5915e297a4d72dd85bbc5304bb2027f5d18b95f13868b4bb1353fafed7bce4e0 +LibCURL.v8.0.1+0.x86_64-linux-gnu.tar.gz/md5/a68df850605cc9ec24268887e4b4ea77 +LibCURL.v8.0.1+0.x86_64-linux-gnu.tar.gz/sha512/f532dfcc84dbb4b92229a79b5629b16198061158e1f12d2dd37948cd0ceccc095221b5fc9a8e2de30de19727c727ee500c8ea4508722c677c7938ddef1c40350 +LibCURL.v8.0.1+0.x86_64-linux-musl.tar.gz/md5/023a2d8271173de0a02bdca8d1d55bbe +LibCURL.v8.0.1+0.x86_64-linux-musl.tar.gz/sha512/e3195f917c250f31ce9669c304918b33664c5b03583f328929e73377f4feff525cedac42dc74adc9ba98a704630294a5697f07eb95ca520c6db4a67f0f83383f +LibCURL.v8.0.1+0.x86_64-unknown-freebsd.tar.gz/md5/ecd39a1cc45ee76751e1e3c5edf469d7 +LibCURL.v8.0.1+0.x86_64-unknown-freebsd.tar.gz/sha512/fa06afb1173bc23474f8f7992268ae9a0df52bc3c1af86d2b60da2cfff43371bb029b51debe638d81d8a1dd334a95dcd3c53dc12923220ad9b1336fcdad1ff8a +LibCURL.v8.0.1+0.x86_64-w64-mingw32.tar.gz/md5/d9a735335e3603635a56eb3b86e6ea87 +LibCURL.v8.0.1+0.x86_64-w64-mingw32.tar.gz/sha512/8fc6677b1be27a900d2a984cf9f9f4b3aa1555bfd732da2bd6553c28da98048c4c86216b57744d7156de94c522b013768e57f42e662845002e5bd9f730c818a8 +curl-8.0.1.tar.bz2/md5/b2e694208b4891d7396d118712148ff3 +curl-8.0.1.tar.bz2/sha512/24e84e922612ebf19341525c5f12f36e730cd21a5279cbea6421742d1ba61e5fa404f2add2e71d64e5692a1feabfa92c5a5d56501f161d1e157718fee467e0a5 diff --git a/deps/curl.version b/deps/curl.version index 632aa80d01683..f704bc2bebc61 100644 --- a/deps/curl.version +++ b/deps/curl.version @@ -3,4 +3,4 @@ CURL_JLL_NAME := LibCURL ## source build -CURL_VER := 7.88.1 +CURL_VER := 8.0.1 diff --git a/stdlib/LibCURL_jll/Project.toml b/stdlib/LibCURL_jll/Project.toml index 5970ed28cecf0..0ef46598b3118 100644 --- a/stdlib/LibCURL_jll/Project.toml +++ b/stdlib/LibCURL_jll/Project.toml @@ -1,6 +1,6 @@ name = "LibCURL_jll" uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "7.88.1+0" +version = "8.0.1+0" [deps] LibSSH2_jll = "29816b5a-b9ab-546f-933c-edad1886dfa8" From 5b49c0376522fe42f9682731eeb6a52663c859a0 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 22 Mar 2023 08:46:48 +0900 Subject: [PATCH 350/775] inlining: allow non-compileable result when handling `ConcreteResult` (#49074) * inlining: allow non-compileable result when handling `ConcreteResult` In rare cases, the system might decide to widen the signature of a call that is determined to throw by concrete-evaluation. We should remove this unnecessary assertion here. closes #49050 * Update test/compiler/inline.jl Co-authored-by: Jameson Nash --------- Co-authored-by: Jameson Nash --- base/compiler/ssair/inlining.jl | 5 ++--- test/compiler/inline.jl | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 21647933d348e..b91652e478636 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -1494,6 +1494,7 @@ end function handle_concrete_result!(cases::Vector{InliningCase}, result::ConcreteResult, @nospecialize(info::CallInfo), state::InliningState) case = concrete_result_item(result, info, state) + case === nothing && return false push!(cases, InliningCase(result.mi.specTypes, case)) return true end @@ -1505,10 +1506,8 @@ function concrete_result_item(result::ConcreteResult, @nospecialize(info::CallIn invokesig::Union{Nothing,Vector{Any}}=nothing) if !may_inline_concrete_result(result) et = InliningEdgeTracker(state.et, invokesig) - case = compileable_specialization(result.mi, result.effects, et, info; + return compileable_specialization(result.mi, result.effects, et, info; compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) - @assert case !== nothing "concrete evaluation should never happen for uncompileable callsite" - return case end @assert result.effects === EFFECTS_TOTAL return ConstantCase(quoted(result.result)) diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index b8bbba78e360f..4f85527e04cb4 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -1921,3 +1921,22 @@ let res = @test_throws MethodError let err = res.value @test err.f === convert && err.args === (Union{Bool,Tuple{String,String}}, g48397) end + +# https://github.com/JuliaLang/julia/issues/49050 +abstract type Issue49050AbsTop{T,N} end +abstract type Issue49050Abs1{T, N} <: Issue49050AbsTop{T,N} end +abstract type Issue49050Abs2{T} <: Issue49050Abs1{T,3} end +struct Issue49050Concrete{T} <: Issue49050Abs2{T} + x::T +end +issue49074(::Type{Issue49050AbsTop{T,N}}) where {T,N} = Issue49050AbsTop{T,N} +Base.@assume_effects :foldable issue49074(::Type{C}) where {C<:Issue49050AbsTop} = issue49074(supertype(C)) +let src = code_typed1() do + issue49074(Issue49050Concrete) + end + @test any(isinvoke(:issue49074), src.code) +end +let result = @test_throws MethodError issue49074(Issue49050Concrete) + @test result.value.f === issue49074 + @test result.value.args === (Any,) +end From 7a13258a5e2ededb8a3ac9f6833808b47eee2220 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 21 Mar 2023 23:11:27 -0400 Subject: [PATCH 351/775] Temp cleanup log tweaks (#49092) --- base/file.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/base/file.jl b/base/file.jl index 8754f3fad9c6d..921119c6074f7 100644 --- a/base/file.jl +++ b/base/file.jl @@ -544,7 +544,10 @@ function temp_cleanup_purge(; force::Bool=false) end !ispath(path) && delete!(TEMP_CLEANUP, path) catch ex - @warn "temp cleanup" _group=:file exception=(ex, catch_backtrace()) + @warn """ + Failed to clean up temporary path $(repr(path)) + $ex + """ _group=:file end end end From 5f5d2040511b42ba74bd7529a0eac9cf817ad496 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 22 Mar 2023 08:16:06 +0100 Subject: [PATCH 352/775] dont show `#unused#` for unnamed arguments in stacktraces (#49058) --- base/stacktraces.jl | 1 + test/errorshow.jl | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/base/stacktraces.jl b/base/stacktraces.jl index d74d47e1eb292..1d0f6996ec42e 100644 --- a/base/stacktraces.jl +++ b/base/stacktraces.jl @@ -224,6 +224,7 @@ function show_spec_linfo(io::IO, frame::StackFrame) if isa(def, Method) sig = linfo.specTypes argnames = Base.method_argnames(def) + argnames = replace(argnames, :var"#unused#" => :var"") if def.nkw > 0 # rearrange call kw_impl(kw_args..., func, pos_args...) to func(pos_args...) kwarg_types = Any[ fieldtype(sig, i) for i = 2:(1+def.nkw) ] diff --git a/test/errorshow.jl b/test/errorshow.jl index a751a3a9951b5..2c1f1a5ffc669 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -934,6 +934,12 @@ let err_str @test occursin("String concatenation is performed with *", err_str) end +@testset "unused argument names" begin + g(::Int) = backtrace() + bt = g(1) + @test !contains(sprint(Base.show_backtrace, bt), "#unused#") +end + # issue #49002 let buf = IOBuffer() Base.show_method_candidates(buf, Base.MethodError(typeof, (17,)), pairs((foo = :bar,))) From 70f6f7f0acdbcfc3fe9b8c6392a0badbaed9448b Mon Sep 17 00:00:00 2001 From: jondeuce <20175323+jondeuce@users.noreply.github.com> Date: Wed, 22 Mar 2023 01:21:40 -0700 Subject: [PATCH 353/775] `LinearAlgebra.norm(x::Union{Transpose, Adjoint})` should default to `norm(parent(x))` (#49020) --- NEWS.md | 2 ++ stdlib/LinearAlgebra/src/generic.jl | 2 +- stdlib/LinearAlgebra/test/generic.jl | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index c96d1a2365410..028c388d754aa 100644 --- a/NEWS.md +++ b/NEWS.md @@ -69,6 +69,8 @@ Standard library changes `Factorization` ([#46874]). * New functions `hermitianpart` and `hermitianpart!` for extracting the Hermitian (real symmetric) part of a matrix ([#31836]). +* The `norm` of the adjoint or transpose of an `AbstractMatrix` now returns the norm of the + parent matrix by default, matching the current behaviour for `AbstractVector`s ([#49020]). #### Printf * Format specifiers now support dynamic width and precision, e.g. `%*s` and `%*.*g` ([#40105]). diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index ede42c2dbf9d7..0c947936dee6b 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -805,7 +805,7 @@ opnorm(v::AdjointAbsVec, q::Real) = q == Inf ? norm(conj(v.parent), 1) : norm(co opnorm(v::AdjointAbsVec) = norm(conj(v.parent)) opnorm(v::TransposeAbsVec) = norm(v.parent) -norm(v::Union{TransposeAbsVec,AdjointAbsVec}, p::Real) = norm(v.parent, p) +norm(v::AdjOrTrans, p::Real) = norm(v.parent, p) """ dot(x, y) diff --git a/stdlib/LinearAlgebra/test/generic.jl b/stdlib/LinearAlgebra/test/generic.jl index a95827867cd18..108d3aec8f069 100644 --- a/stdlib/LinearAlgebra/test/generic.jl +++ b/stdlib/LinearAlgebra/test/generic.jl @@ -269,6 +269,24 @@ end @test norm(x, 3) ≈ cbrt(5^3 +sqrt(5)^3) end +@testset "norm of transpose/adjoint equals norm of parent #32739" begin + for t in (transpose, adjoint), elt in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}) + # Vector/matrix of scalars + for sz in ((2,), (2, 3)) + A = rand(elt, sz...) + Aᵀ = t(A) + @test norm(Aᵀ) ≈ norm(Matrix(Aᵀ)) + end + + # Vector/matrix of vectors/matrices + for sz_outer in ((2,), (2, 3)), sz_inner in ((3,), (1, 2)) + A = [rand(elt, sz_inner...) for _ in CartesianIndices(sz_outer)] + Aᵀ = t(A) + @test norm(Aᵀ) ≈ norm(Matrix(Matrix.(Aᵀ))) + end + end +end + @testset "rotate! and reflect!" begin x = rand(ComplexF64, 10) y = rand(ComplexF64, 10) From 6d678fec0ad0c78e80f357aac1b1e99ff0ff47ca Mon Sep 17 00:00:00 2001 From: woclass Date: Wed, 22 Mar 2023 18:39:53 +0800 Subject: [PATCH 354/775] doc: remove outdata const (#49096) --- doc/src/base/constants.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/src/base/constants.md b/doc/src/base/constants.md index 4ba0e627b0c54..14ddbc02698d0 100644 --- a/doc/src/base/constants.md +++ b/doc/src/base/constants.md @@ -23,6 +23,3 @@ See also: * [`stderr`](@ref) * [`ENV`](@ref) * [`ENDIAN_BOM`](@ref) - * `Libc.MS_ASYNC` - * `Libc.MS_INVALIDATE` - * `Libc.MS_SYNC` From e2c0be3369829fb0477e7df8b934513e1ceca4d2 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 23 Mar 2023 09:58:29 +0900 Subject: [PATCH 355/775] clean up test/opaque_closure (#49099) - removed unnecessary `Core.` accessor - use the default `nargs`/`isva` values of `OpaqueClosure` constructor where possible - wrap the world age test in a separate module so that `include("test/opaque_closure")` works multiple times in the same session --- test/opaque_closure.jl | 64 ++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/test/opaque_closure.jl b/test/opaque_closure.jl index b5d5f9ed522ac..7cd962d38bfc4 100644 --- a/test/opaque_closure.jl +++ b/test/opaque_closure.jl @@ -1,6 +1,7 @@ using Test using InteractiveUtils using Core: OpaqueClosure +using Base.Experimental: @opaque const_int() = 1 @@ -12,7 +13,7 @@ let ci = @code_lowered const_int() Expr(:opaque_closure_method, nothing, 0, false, lno, ci))) end end -@test isa(oc_trivial(), Core.OpaqueClosure{Tuple{}, Any}) +@test isa(oc_trivial(), OpaqueClosure{Tuple{}, Any}) @test oc_trivial()() == 1 let ci = @code_lowered const_int() @@ -21,7 +22,7 @@ let ci = @code_lowered const_int() Expr(:opaque_closure_method, nothing, 0, false, lno, ci))) end end -@test isa(oc_simple_inf(), Core.OpaqueClosure{Tuple{}, Int}) +@test isa(oc_simple_inf(), OpaqueClosure{Tuple{}, Int}) @test oc_simple_inf()() == 1 struct OcClos2Int @@ -72,8 +73,8 @@ let ci = @code_lowered OcClos1Any(1)() :x)) end end -@test isa(oc_infer_pass_clos(1), Core.OpaqueClosure{Tuple{}, typeof(1)}) -@test isa(oc_infer_pass_clos("a"), Core.OpaqueClosure{Tuple{}, typeof("a")}) +@test isa(oc_infer_pass_clos(1), OpaqueClosure{Tuple{}, typeof(1)}) +@test isa(oc_infer_pass_clos("a"), OpaqueClosure{Tuple{}, typeof("a")}) @test oc_infer_pass_clos(1)() == 1 @test oc_infer_pass_clos("a")() == "a" @@ -115,8 +116,6 @@ let A = [1 2] end end -using Base.Experimental: @opaque - @test @opaque(x->2x)(8) == 16 let f = @opaque (x::Int, y::Float64)->(2x, 3y) @test_throws TypeError f(1, 1) @@ -128,18 +127,26 @@ end @test uses_frontend_opaque(10)(8) == 18 # World age mechanism +module test_world_age + +using Test +using Core: OpaqueClosure +using Base.Experimental: @opaque + function test_oc_world_age end mk_oc_world_age() = @opaque ()->test_oc_world_age() g_world_age = @opaque ()->test_oc_world_age() h_world_age = mk_oc_world_age() -@test isa(h_world_age, Core.OpaqueClosure{Tuple{}, Union{}}) +@test isa(h_world_age, OpaqueClosure{Tuple{}, Union{}}) test_oc_world_age() = 1 @test_throws MethodError g_world_age() @test_throws MethodError h_world_age() @test mk_oc_world_age()() == 1 g_world_age = @opaque ()->test_oc_world_age() @test g_world_age() == 1 -@test isa(mk_oc_world_age(), Core.OpaqueClosure{Tuple{}, Int}) +@test isa(mk_oc_world_age(), OpaqueClosure{Tuple{}, Int}) + +end # module test_world_age function maybe_vararg(isva::Bool) T = isva ? Vararg{Int} : Int @@ -196,7 +203,7 @@ end QuoteNode(Symbol(@__FILE__)), true))) end -@test isa(oc_trivial_generated(), Core.OpaqueClosure{Tuple{}, Any}) +@test isa(oc_trivial_generated(), OpaqueClosure{Tuple{}, Any}) @test oc_trivial_generated()() == 1 # Constprop through varargs OpaqueClosure @@ -242,31 +249,28 @@ let oc = @opaque a->sin(a) end # constructing an opaque closure from IRCode -let ci = code_typed(+, (Int, Int))[1][1] - ir = Core.Compiler.inflate_ir(ci) - @test OpaqueClosure(ir; nargs=2, isva=false)(40, 2) == 42 - @test OpaqueClosure(ci)(40, 2) == 42 - - ir = Core.Compiler.inflate_ir(ci) - @test OpaqueClosure(ir; nargs=2, isva=false)(40, 2) == 42 - @test isa(OpaqueClosure(ir; nargs=2, isva=false), Core.OpaqueClosure{Tuple{Int, Int}, Int}) - @test_throws TypeError OpaqueClosure(ir; nargs=2, isva=false)(40.0, 2) +let src = first(only(code_typed(+, (Int, Int)))) + ir = Core.Compiler.inflate_ir(src) + @test OpaqueClosure(src)(40, 2) == 42 + oc = OpaqueClosure(ir) + @test oc(40, 2) == 42 + @test isa(oc, OpaqueClosure{Tuple{Int,Int}, Int}) + @test_throws TypeError oc("40", 2) + ir = Core.Compiler.inflate_ir(src) + @test OpaqueClosure(ir)(40, 2) == 42 end -let ci = code_typed((x, y...)->(x, y), (Int, Int))[1][1] - ir = Core.Compiler.inflate_ir(ci) - let oc = OpaqueClosure(ir; nargs=2, isva=true) - @test oc(40, 2) === (40, (2,)) +# variadic arguments +let src = code_typed((Int,Int)) do x, y... + return (x, y) + end |> only |> first + let oc = OpaqueClosure(src) + @test oc(1,2) === (1,(2,)) @test_throws MethodError oc(1,2,3) end - let oc = OpaqueClosure(ci) - @test oc(40, 2) === (40, (2,)) - @test_throws MethodError oc(1,2,3) - end - - ir = Core.Compiler.inflate_ir(ci) - let oc = OpaqueClosure(ir; nargs=2, isva=true) - @test oc(40, 2) === (40, (2,)) + ir = Core.Compiler.inflate_ir(src) + let oc = OpaqueClosure(ir; isva=true) + @test oc(1,2) === (1,(2,)) @test_throws MethodError oc(1,2,3) end end From 046915437ed2afc10c8d2bf878dd286996186a22 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 23 Mar 2023 10:22:28 +0900 Subject: [PATCH 356/775] make `Core.OpaqueClosure(ir::IRCode)` non-destructive (#49100) Currently the constructor is destructive because it calls `replace_code_newstyle!(src, ir, ...)` etc. This commit made it non-destructive by calling `Core.Compiler.copy(ir)` beforehand. We can define a destructive version of this `Core.OpaqueClosure!` later if we really want it. --- base/opaque_closure.jl | 1 + test/opaque_closure.jl | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/base/opaque_closure.jl b/base/opaque_closure.jl index 2bccd613d0009..730cbb735b2fc 100644 --- a/base/opaque_closure.jl +++ b/base/opaque_closure.jl @@ -63,6 +63,7 @@ function Core.OpaqueClosure(ir::IRCode, env...; if (isva && nargs > length(ir.argtypes)) || (!isva && nargs != length(ir.argtypes)-1) throw(ArgumentError("invalid argument count")) end + ir = Core.Compiler.copy(ir) src = ccall(:jl_new_code_info_uninit, Ref{CodeInfo}, ()) src.slotflags = UInt8[] src.slotnames = fill(:none, nargs+1) diff --git a/test/opaque_closure.jl b/test/opaque_closure.jl index 7cd962d38bfc4..767e9ed8bc1cd 100644 --- a/test/opaque_closure.jl +++ b/test/opaque_closure.jl @@ -256,8 +256,7 @@ let src = first(only(code_typed(+, (Int, Int)))) @test oc(40, 2) == 42 @test isa(oc, OpaqueClosure{Tuple{Int,Int}, Int}) @test_throws TypeError oc("40", 2) - ir = Core.Compiler.inflate_ir(src) - @test OpaqueClosure(ir)(40, 2) == 42 + @test OpaqueClosure(ir)(40, 2) == 42 # OpaqueClosure constructor should be non-destructive end # variadic arguments From 489d076452130c718c7d77b157b0d503bfc31602 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 23 Mar 2023 10:23:34 +0900 Subject: [PATCH 357/775] fix incorrect static parameter definedness check (#49097) We use the specialized signature of a method for accurate analysis of whether a static parameter is constrained or not. However, it turns out that we can only use it when it doesn't contain any free type variables (which it sometimes does, e.g., when the inference entry is given a signature with a free type variable). In such cases, we should use the method signature, which, by design, never contains any free type variables. --- base/compiler/inferencestate.jl | 12 +++++++++++- test/compiler/inference.jl | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 74db247e472b6..aea657404b249 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -487,7 +487,17 @@ function sptypes_from_meth_instance(linfo::MethodInstance) ty = UnionAll(tv, Type{tv}) end @label ty_computed - undef = !constrains_param(v, linfo.specTypes, #=covariant=#true) + undef = !(let sig=sig + # if the specialized signature `linfo.specTypes` doesn't contain any free + # type variables, we can use it for a more accurate analysis of whether `v` + # is constrained or not, otherwise we should use `def.sig` which always + # doesn't contain any free type variables + if !has_free_typevars(linfo.specTypes) + sig = linfo.specTypes + end + @assert !has_free_typevars(sig) + constrains_param(v, sig, #=covariant=#true) + end) elseif isvarargtype(v) ty = Int undef = false diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 85509ebf57199..e3d66c7531021 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -4684,6 +4684,26 @@ unknown_sparam_nothrow2(x::Ref{Ref{T}}) where T = @isdefined(T) ? T::Type : noth @test only(Base.return_types(unknown_sparam_nothrow1, (Ref,))) === Type @test only(Base.return_types(unknown_sparam_nothrow2, (Ref{Ref{T}} where T,))) === Type +struct Issue49027{Ty<:Number} + x::Ty +end +function issue49027(::Type{<:Issue49027{Ty}}) where Ty + if @isdefined Ty # should be false when `Ty` is given as a free type var. + return Ty::DataType + end + return nothing +end +@test only(Base.return_types(issue49027, (Type{Issue49027{TypeVar(:Ty)}},))) >: Nothing +@test isnothing(issue49027(Issue49027{TypeVar(:Ty)})) +function issue49027_integer(::Type{<:Issue49027{Ty}}) where Ty<:Integer + if @isdefined Ty # should be false when `Ty` is given as a free type var. + return Ty::DataType + end + nothing +end +@test only(Base.return_types(issue49027_integer, (Type{Issue49027{TypeVar(:Ty,Int)}},))) >: Nothing +@test isnothing(issue49027_integer(Issue49027{TypeVar(:Ty,Int)})) + function fapplicable end gapplicable() = Val(applicable(fapplicable)) gapplicable(x) = Val(applicable(fapplicable; x)) From 703b3f8c7f9df3a1e0def32c947d6debd90a9f8a Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 23 Mar 2023 14:25:11 +0100 Subject: [PATCH 358/775] hide internally created methods in stacktraces that occur as a result of argument forwarding (#49102) --- base/errorshow.jl | 71 ++++++++++++++++++++++++++++++++++++++++++++++- test/errorshow.jl | 40 ++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index f7c6a0a0f84b9..ab0c22120d741 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -823,6 +823,73 @@ function _simplify_include_frames(trace) return trace[kept_frames] end +# Collapse frames that have the same location (in some cases) +function _collapse_repeated_frames(trace) + kept_frames = trues(length(trace)) + last_frame = nothing + for i in 1:length(trace) + frame::StackFrame, _ = trace[i] + if last_frame !== nothing && frame.file == last_frame.file && frame.line == last_frame.line + #= + Handles this case: + + f(g, a; kw...) = error(); + @inline f(a; kw...) = f(identity, a; kw...); + f(1) + + which otherwise ends up as: + + [4] #f#4 <-- useless + @ ./REPL[2]:1 [inlined] + [5] f(a::Int64) + @ Main ./REPL[2]:1 + =# + if startswith(sprint(show, last_frame), "#") + kept_frames[i-1] = false + end + + #= Handles this case + g(x, y=1, z=2) = error(); + g(1) + + which otherwise ends up as: + + [2] g(x::Int64, y::Int64, z::Int64) + @ Main ./REPL[1]:1 + [3] g(x::Int64) <-- useless + @ Main ./REPL[1]:1 + =# + if frame.linfo isa MethodInstance && last_frame.linfo isa MethodInstance && + frame.linfo.def isa Method && last_frame.linfo.def isa Method + m, last_m = frame.linfo.def::Method, last_frame.linfo.def::Method + params, last_params = Base.unwrap_unionall(m.sig).parameters, Base.unwrap_unionall(last_m.sig).parameters + + if last_m.nkw != 0 + pos_sig_params = Base.rewrap_unionall(Tuple{last_params[(last_m.nkw+2):end]...}, last_m.sig).parameters + issame = true + if pos_sig_params == params + kept_frames[i] = false + end + end + if length(last_params) > length(params) + issame = true + for i = 1:length(params) + issame &= params[i] == last_params[i] + end + if issame + kept_frames[i] = false + end + end + end + + # TODO: Detect more cases that can be collapsed + end + last_frame = frame + end + return trace[kept_frames] +end + + function process_backtrace(t::Vector, limit::Int=typemax(Int); skipC = true) n = 0 last_frame = StackTraces.UNKNOWN @@ -875,7 +942,9 @@ function process_backtrace(t::Vector, limit::Int=typemax(Int); skipC = true) if n > 0 push!(ret, (last_frame, n)) end - return _simplify_include_frames(ret) + trace = _simplify_include_frames(ret) + trace = _collapse_repeated_frames(trace) + return trace end function show_exception_stack(io::IO, stack) diff --git a/test/errorshow.jl b/test/errorshow.jl index 2c1f1a5ffc669..78e9a30241c9f 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -947,3 +947,43 @@ let buf = IOBuffer() Base.show_method_candidates(buf, Base.MethodError(isa, ()), pairs((a = 5,))) @test isempty(take!(buf)) end + +f_internal_wrap(g, a; kw...) = error(); +@inline f_internal_wrap(a; kw...) = f_internal_wrap(identity, a; kw...); +bt = try + f_internal_wrap(1) +catch + catch_backtrace() +end +@test !occursin("#f_internal_wrap#", sprint(Base.show_backtrace, bt)) + +g_collapse_pos(x, y=1.0, z=2.0) = error() +bt = try + g_collapse_pos(1.0) +catch + catch_backtrace() +end +bt_str = sprint(Base.show_backtrace, bt) +@test occursin("g_collapse_pos(x::Float64, y::Float64, z::Float64)", bt_str) +@test !occursin("g_collapse_pos(x::Float64)", bt_str) + +g_collapse_kw(x; y=2.0) = error() +bt = try + g_collapse_kw(1.0) +catch + catch_backtrace() +end +bt_str = sprint(Base.show_backtrace, bt) +@test occursin("g_collapse_kw(x::Float64; y::Float64)", bt_str) +@test !occursin("g_collapse_kw(x::Float64)", bt_str) + +g_collapse_pos_kw(x, y=1.0; z=2.0) = error() +bt = try + g_collapse_pos_kw(1.0) +catch + catch_backtrace() +end +bt_str = sprint(Base.show_backtrace, bt) +@test occursin("g_collapse_pos_kw(x::Float64, y::Float64; z::Float64)", bt_str) +@test !occursin("g_collapse_pos_kw(x::Float64, y::Float64)", bt_str) +@test !occursin("g_collapse_pos_kw(x::Float64)", bt_str) From f8a332ec622b1efbc71eab54abd7784c85e0cc79 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 23 Mar 2023 19:07:46 +0100 Subject: [PATCH 359/775] use simpler way of computing positional parameters for stacktrace collapsing (#49118) fixup to https://github.com/JuliaLang/julia/pull/49102 --- base/errorshow.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index ab0c22120d741..d3d2feda1f9b5 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -863,9 +863,8 @@ function _collapse_repeated_frames(trace) frame.linfo.def isa Method && last_frame.linfo.def isa Method m, last_m = frame.linfo.def::Method, last_frame.linfo.def::Method params, last_params = Base.unwrap_unionall(m.sig).parameters, Base.unwrap_unionall(last_m.sig).parameters - if last_m.nkw != 0 - pos_sig_params = Base.rewrap_unionall(Tuple{last_params[(last_m.nkw+2):end]...}, last_m.sig).parameters + pos_sig_params = last_params[(last_m.nkw+2):end] issame = true if pos_sig_params == params kept_frames[i] = false From e33b31bcb7dcf264cc9711d35b0ab6717f952fa8 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 23 Mar 2023 14:52:52 -0400 Subject: [PATCH 360/775] Improve robustness in Dates/conversions test (#49086) * Improve robustness in Dates/conversions test In [1], this test was observed to fail, with a 1 second pause between the two Dates.now calls. In general, particularly on high-load CI systems, we cannot make assumptions that calls will complete within any particular amount of time. Instead, we should be asserting properties that should be universally true. This test wants to assert that Dates.now() and Dates.now(UTC) give the same answer if the TZ environment variable is set appropriately, but of course we can't guarantee that these calls run at the same time. Rather than setting an arbitrary limit of 1 second, just run `Dates.now` again. Our semantics guarantee ordering of these calls (in the absence of leap days/seconds at least), so the test is more robust. [1] https://buildkite.com/julialang/julia-master/builds/22064#0186ff11-f07e-4ba4-9b54-21bdf738d35e * now: Consistently truncate DateTime rather than rounding The operating system clock provides the current time in microseconds, but `DateTime` has milisecond precision. The question is what rounding to apply. `now` was using truncation, while `now(UTC)` was using rounding. I think truncation is probably the better default, since you're often interested in whether a particular `DateTime` has passed, so change `now(UTC)` to trucate as well. --- stdlib/Dates/src/conversions.jl | 4 +++- stdlib/Dates/test/conversions.jl | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/stdlib/Dates/src/conversions.jl b/stdlib/Dates/src/conversions.jl index 8493218cc4086..30f1f2581d1fa 100644 --- a/stdlib/Dates/src/conversions.jl +++ b/stdlib/Dates/src/conversions.jl @@ -46,9 +46,11 @@ Take the number of seconds since unix epoch `1970-01-01T00:00:00` and convert to corresponding `DateTime`. """ function unix2datetime(x) - rata = UNIXEPOCH + round(Int64, Int64(1000) * x) + # Rounding should match `now` below + rata = UNIXEPOCH + trunc(Int64, Int64(1000) * x) return DateTime(UTM(rata)) end + """ datetime2unix(dt::DateTime) -> Float64 diff --git a/stdlib/Dates/test/conversions.jl b/stdlib/Dates/test/conversions.jl index 488af4110e884..99572b41b4f90 100644 --- a/stdlib/Dates/test/conversions.jl +++ b/stdlib/Dates/test/conversions.jl @@ -60,10 +60,16 @@ end if Sys.isapple() withenv("TZ" => "UTC") do - @test abs(Dates.now() - now(Dates.UTC)) < Dates.Second(1) + a = Dates.now() + b = Dates.now(Dates.UTC) + c = Dates.now() + @test a <= b <= c end end - @test abs(Dates.now() - now(Dates.UTC)) < Dates.Hour(16) + a = Dates.now() + b = now(Dates.UTC) + c = Dates.now() + @test abs(a - b) < Dates.Hour(16) + abs(c - a) end @testset "Issue #9171, #9169" begin let t = Dates.Period[Dates.Week(2), Dates.Day(14), Dates.Hour(14 * 24), Dates.Minute(14 * 24 * 60), Dates.Second(14 * 24 * 60 * 60), Dates.Millisecond(14 * 24 * 60 * 60 * 1000)] From 20b8139ecbc5ae27a005675fcfd6dfd47b34a27c Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 23 Mar 2023 14:53:41 -0400 Subject: [PATCH 361/775] Add retry for socket race condition in libgit2 tests (#49089) We have a race condition in the libgit2 tests where we're probing for an open port and then try to acquire it later. However, that leaves a window for a different process to grab the open port in the meantime and we regularly see this failure on CI. A proper fix would be to let s_server do its own probing, add the ability to pass in a file descriptor rather than a port number or switch to a different SSL server, but absent somebody wanting to that work, this does the next best thing of just retrying the server start. --- stdlib/LibGit2/test/libgit2-tests.jl | 107 +++++++++++++++------------ 1 file changed, 60 insertions(+), 47 deletions(-) diff --git a/stdlib/LibGit2/test/libgit2-tests.jl b/stdlib/LibGit2/test/libgit2-tests.jl index 4ace98a0b1ac8..7dbbd10af6f67 100644 --- a/stdlib/LibGit2/test/libgit2-tests.jl +++ b/stdlib/LibGit2/test/libgit2-tests.jl @@ -3151,63 +3151,76 @@ mktempdir() do dir run(pipeline(`openssl req -new -x509 -newkey rsa:2048 -sha256 -nodes -keyout $key -out $cert -days 1 -subj "/CN=$common_name"`, stderr=devnull)) run(`openssl x509 -in $cert -out $pem -outform PEM`) - # Find an available port by listening - port, server = listenany(49152) - close(server) - - # Make a fake Julia package and minimal HTTPS server with our generated - # certificate. The minimal server can't actually serve a Git repository. - mkdir(joinpath(root, "Example.jl")) - pobj = cd(root) do - run(pipeline(`openssl s_server -key $key -cert $cert -WWW -accept $port`, stderr=RawFD(2)), wait=false) - end - - errfile = joinpath(root, "error") - repo_url = "https://$common_name:$port/Example.jl" - repo_dir = joinpath(root, "dest") - code = """ - using Serialization - import LibGit2 - dest_dir = "$repo_dir" - open("$errfile", "w+") do f - try - repo = LibGit2.clone("$repo_url", dest_dir) - catch err - serialize(f, err) - finally - isdir(dest_dir) && rm(dest_dir, recursive=true) - end + local pobj, port + for attempt in 1:10 + # Find an available port by listening, but there's a race condition where + # another process could grab this port, so retry on failure + port, server = listenany(49152) + close(server) + + # Make a fake Julia package and minimal HTTPS server with our generated + # certificate. The minimal server can't actually serve a Git repository. + mkdir(joinpath(root, "Example.jl")) + pobj = cd(root) do + run(pipeline(`openssl s_server -key $key -cert $cert -WWW -accept $port`, stderr=RawFD(2)), wait=false) end - """ - cmd = `$(Base.julia_cmd()) --startup-file=no -e $code` + @test readuntil(pobj, "ACCEPT") == "" - try - # The generated certificate is normally invalid - run(cmd) - err = open(errfile, "r") do f - deserialize(f) - end - @test err.code == LibGit2.Error.ERROR - @test startswith(lowercase(err.msg), - lowercase("user rejected certificate for localhost")) + # Two options: Either we reached "ACCEPT" and the process is running and ready + # or it failed to listen and exited, in which case we try again. + process_running(pobj) && break + end - rm(errfile) + @test process_running(pobj) + + if process_running(pobj) + errfile = joinpath(root, "error") + repo_url = "https://$common_name:$port/Example.jl" + repo_dir = joinpath(root, "dest") + code = """ + using Serialization + import LibGit2 + dest_dir = "$repo_dir" + open("$errfile", "w+") do f + try + repo = LibGit2.clone("$repo_url", dest_dir) + catch err + serialize(f, err) + finally + isdir(dest_dir) && rm(dest_dir, recursive=true) + end + end + """ + cmd = `$(Base.julia_cmd()) --startup-file=no -e $code` - # Specify that Julia use only the custom certificate. Note: we need to - # spawn a new Julia process in order for this ENV variable to take effect. - withenv("SSL_CERT_FILE" => pem) do + try + # The generated certificate is normally invalid run(cmd) err = open(errfile, "r") do f deserialize(f) end @test err.code == LibGit2.Error.ERROR - @test occursin(r"invalid content-type: '?text/plain'?"i, err.msg) - end + @test startswith(lowercase(err.msg), + lowercase("user rejected certificate for localhost")) + + rm(errfile) + + # Specify that Julia use only the custom certificate. Note: we need to + # spawn a new Julia process in order for this ENV variable to take effect. + withenv("SSL_CERT_FILE" => pem) do + run(cmd) + err = open(errfile, "r") do f + deserialize(f) + end + @test err.code == LibGit2.Error.ERROR + @test occursin(r"invalid content-type: '?text/plain'?"i, err.msg) + end - # OpenSSL s_server should still be running - @test process_running(pobj) - finally - kill(pobj) + # OpenSSL s_server should still be running + @test process_running(pobj) + finally + kill(pobj) + end end end end From 56950e270863c0225b860e313451f70defa82c6d Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Thu, 23 Mar 2023 15:22:32 -0400 Subject: [PATCH 362/775] improve string effects (#48996) --- base/hashing.jl | 2 +- base/strings/string.jl | 38 ++++++++++++++++++++++---------------- base/strings/substring.jl | 34 ++++++++++++++++++++++++++-------- test/misc.jl | 3 ++- test/strings/basic.jl | 39 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 26 deletions(-) diff --git a/base/hashing.jl b/base/hashing.jl index 0989fecb29839..d47d88eedabd6 100644 --- a/base/hashing.jl +++ b/base/hashing.jl @@ -110,7 +110,7 @@ end const memhash = UInt === UInt64 ? :memhash_seed : :memhash32_seed const memhash_seed = UInt === UInt64 ? 0x71e729fd56419c81 : 0x56419c81 -function hash(s::String, h::UInt) +@assume_effects :total function hash(s::String, h::UInt) h += memhash_seed ccall(memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), s, sizeof(s), h % UInt32) + h end diff --git a/base/strings/string.jl b/base/strings/string.jl index 59241223f4d49..f0400d8cf3672 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -113,7 +113,7 @@ pointer(s::String, i::Integer) = pointer(s) + Int(i)::Int - 1 ncodeunits(s::String) = Core.sizeof(s) codeunit(s::String) = UInt8 -@inline function codeunit(s::String, i::Integer) +@assume_effects :foldable @inline function codeunit(s::String, i::Integer) @boundscheck checkbounds(s, i) b = GC.@preserve s unsafe_load(pointer(s, i)) return b @@ -121,20 +121,20 @@ end ## comparison ## -_memcmp(a::Union{Ptr{UInt8},AbstractString}, b::Union{Ptr{UInt8},AbstractString}, len) = +@assume_effects :total _memcmp(a::String, b::String) = @invoke _memcmp(a::Union{Ptr{UInt8},AbstractString},b::Union{Ptr{UInt8},AbstractString}) + +_memcmp(a::Union{Ptr{UInt8},AbstractString}, b::Union{Ptr{UInt8},AbstractString}) = _memcmp(a, b, min(sizeof(a), sizeof(b))) +function _memcmp(a::Union{Ptr{UInt8},AbstractString}, b::Union{Ptr{UInt8},AbstractString}, len::Int) ccall(:memcmp, Cint, (Ptr{UInt8}, Ptr{UInt8}, Csize_t), a, b, len % Csize_t) % Int +end function cmp(a::String, b::String) al, bl = sizeof(a), sizeof(b) - c = _memcmp(a, b, min(al,bl)) + c = _memcmp(a, b) return c < 0 ? -1 : c > 0 ? +1 : cmp(al,bl) end -function ==(a::String, b::String) - pointer_from_objref(a) == pointer_from_objref(b) && return true - al = sizeof(a) - return al == sizeof(b) && 0 == _memcmp(a, b, al) -end +==(a::String, b::String) = a===b typemin(::Type{String}) = "" typemin(::String) = typemin(String) @@ -284,9 +284,11 @@ getindex(s::String, r::AbstractUnitRange{<:Integer}) = s[Int(first(r)):Int(last( return ss end -length(s::String) = length_continued(s, 1, ncodeunits(s), ncodeunits(s)) +# nothrow because we know the start and end indices are valid +@assume_effects :nothrow length(s::String) = length_continued(s, 1, ncodeunits(s), ncodeunits(s)) -@inline function length(s::String, i::Int, j::Int) +# effects needed because @inbounds +@assume_effects :consistent :effect_free @inline function length(s::String, i::Int, j::Int) @boundscheck begin 0 < i ≤ ncodeunits(s)+1 || throw(BoundsError(s, i)) 0 ≤ j < ncodeunits(s)+1 || throw(BoundsError(s, j)) @@ -294,13 +296,13 @@ length(s::String) = length_continued(s, 1, ncodeunits(s), ncodeunits(s)) j < i && return 0 @inbounds i, k = thisind(s, i), i c = j - i + (i == k) - length_continued(s, i, j, c) + @inbounds length_continued(s, i, j, c) end -@inline function length_continued(s::String, i::Int, n::Int, c::Int) +@assume_effects :terminates_locally @inline @propagate_inbounds function length_continued(s::String, i::Int, n::Int, c::Int) i < n || return c - @inbounds b = codeunit(s, i) - @inbounds while true + b = codeunit(s, i) + while true while true (i += 1) ≤ n || return c 0xc0 ≤ b ≤ 0xf7 && break @@ -328,6 +330,10 @@ isvalid(s::String, i::Int) = checkbounds(Bool, s, i) && thisind(s, i) == i isascii(s::String) = isascii(codeunits(s)) +# don't assume effects for general integers since we cannot know their implementation +@assume_effects :foldable function repeat(c::Char, r::BitInteger) + @invoke repeat(c, r::Integer) +end """ repeat(c::AbstractChar, r::Integer) -> String @@ -340,8 +346,8 @@ julia> repeat('A', 3) "AAA" ``` """ -repeat(c::AbstractChar, r::Integer) = repeat(Char(c), r) # fallback -function repeat(c::Char, r::Integer) +function repeat(c::AbstractChar, r::Integer) + c = Char(c) r == 0 && return "" r < 0 && throw(ArgumentError("can't repeat a character $r times")) u = bswap(reinterpret(UInt32, c)) diff --git a/base/strings/substring.jl b/base/strings/substring.jl index 76658b377c7b4..0346294d1b472 100644 --- a/base/strings/substring.jl +++ b/base/strings/substring.jl @@ -110,15 +110,12 @@ thisind(s::SubString{String}, i::Int) = _thisind_str(s, i) nextind(s::SubString{String}, i::Int) = _nextind_str(s, i) function ==(a::Union{String, SubString{String}}, b::Union{String, SubString{String}}) - s = sizeof(a) - s == sizeof(b) && 0 == _memcmp(a, b, s) + sizeof(a) == sizeof(b) && _memcmp(a, b) == 0 end function cmp(a::SubString{String}, b::SubString{String}) - na = sizeof(a) - nb = sizeof(b) - c = _memcmp(a, b, min(na, nb)) - return c < 0 ? -1 : c > 0 ? +1 : cmp(na, nb) + c = _memcmp(a, b) + return c < 0 ? -1 : c > 0 ? +1 : cmp(sizeof(a), sizeof(b)) end # don't make unnecessary copies when passing substrings to C functions @@ -209,19 +206,34 @@ end return n end -@inline function __unsafe_string!(out, s::Union{String, SubString{String}}, offs::Integer) +@assume_effects :nothrow @inline function __unsafe_string!(out, s::String, offs::Integer) n = sizeof(s) GC.@preserve s out unsafe_copyto!(pointer(out, offs), pointer(s), n) return n end -@inline function __unsafe_string!(out, s::Symbol, offs::Integer) +@inline function __unsafe_string!(out, s::SubString{String}, offs::Integer) + n = sizeof(s) + GC.@preserve s out unsafe_copyto!(pointer(out, offs), pointer(s), n) + return n +end + +@assume_effects :nothrow @inline function __unsafe_string!(out, s::Symbol, offs::Integer) n = sizeof(s) GC.@preserve s out unsafe_copyto!(pointer(out, offs), unsafe_convert(Ptr{UInt8},s), n) return n end +# nothrow needed here because for v in a can't prove the indexing is inbounds. +@assume_effects :foldable :nothrow function string(a::Union{Char, String, Symbol}...) + _string(a...) +end + function string(a::Union{Char, String, SubString{String}, Symbol}...) + _string(a...) +end + +function _string(a::Union{Char, String, SubString{String}, Symbol}...) n = 0 for v in a # 4 types is too many for automatic Union-splitting, so we split manually @@ -250,6 +262,12 @@ function string(a::Union{Char, String, SubString{String}, Symbol}...) return out end +# don't assume effects for general integers since we cannot know their implementation +# not nothrow because r<0 throws +@assume_effects :foldable function repeat(s::String, r::BitInteger) + @invoke repeat(s, r::Integer) +end + function repeat(s::Union{String, SubString{String}}, r::Integer) r < 0 && throw(ArgumentError("can't repeat a string $r times")) r == 0 && return "" diff --git a/test/misc.jl b/test/misc.jl index c70868552d9c0..79b684badf1e0 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -1384,7 +1384,8 @@ end # sanity check `@allocations` returns what we expect in some very simple cases @test (@allocations "a") == 0 - @test (@allocations "a" * "b") == 1 + @test (@allocations "a" * "b") == 0 # constant propagation + @test (@allocations "a" * Base.inferencebarrier("b")) == 1 end @testset "in_finalizer" begin diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 168a01caac207..5f27df006b093 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -1192,3 +1192,42 @@ end return a end |> Core.Compiler.is_foldable end + +@testset "String Effects" begin + for (f, Ts) in [(*, (String, String)), + (*, (Char, String)), + (*, (Char, Char)), + (string, (Symbol, String, Char)), + (==, (String, String)), + (cmp, (String, String)), + (==, (Symbol, Symbol)), + (cmp, (Symbol, Symbol)), + (String, (Symbol,)), + (length, (String,)), + (hash, (String,UInt)), + (hash, (Char,UInt)),] + e = Base.infer_effects(f, Ts) + @test Core.Compiler.is_foldable(e) || (f, Ts) + @test Core.Compiler.is_removable_if_unused(e) || (f, Ts) + end + for (f, Ts) in [(^, (String, Int)), + (^, (Char, Int)), + (codeunit, (String, Int)), + ] + e = Base.infer_effects(f, Ts) + @test Core.Compiler.is_foldable(e) || (f, Ts) + @test !Core.Compiler.is_removable_if_unused(e) || (f, Ts) + end + # Substrings don't have any nice effects because the compiler can + # invent fake indices leading to out of bounds + for (f, Ts) in [(^, (SubString{String}, Int)), + (string, (String, SubString{String})), + (string, (Symbol, SubString{String})), + (hash, (SubString{String},UInt)), + ] + e = Base.infer_effects(f, Ts) + @test !Core.Compiler.is_foldable(e) || (f, Ts) + @test !Core.Compiler.is_removable_if_unused(e) || (f, Ts) + end + @test_throws ArgumentError Symbol("a\0a") +end From 124abaa73c06e4c73c6cc6d470dbaa08eb9d4e28 Mon Sep 17 00:00:00 2001 From: anand jain <33790004+anandijain@users.noreply.github.com> Date: Thu, 23 Mar 2023 15:47:40 -0400 Subject: [PATCH 363/775] define `InteractiveUtils.@infer_effects` (#49062) --- stdlib/InteractiveUtils/src/macros.jl | 4 ++-- stdlib/InteractiveUtils/test/runtests.jl | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/stdlib/InteractiveUtils/src/macros.jl b/stdlib/InteractiveUtils/src/macros.jl index 135c207654ca0..53242a422140b 100644 --- a/stdlib/InteractiveUtils/src/macros.jl +++ b/stdlib/InteractiveUtils/src/macros.jl @@ -2,7 +2,7 @@ # macro wrappers for various reflection functions -import Base: typesof, insert!, replace_ref_begin_end! +import Base: typesof, insert!, replace_ref_begin_end!, infer_effects separate_kwargs(args...; kwargs...) = (args, values(kwargs)) @@ -212,7 +212,7 @@ macro which(ex0::Symbol) return :(which($__module__, $ex0)) end -for fname in [:code_warntype, :code_llvm, :code_native] +for fname in [:code_warntype, :code_llvm, :code_native, :infer_effects] @eval begin macro ($fname)(ex0...) gen_call_with_extracted_types_and_kwargs(__module__, $(Expr(:quote, fname)), ex0) diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index cd1e660377230..5f90491fd8151 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -719,3 +719,5 @@ end end end end + +@test Base.infer_effects(sin, (Int,)) == InteractiveUtils.@infer_effects sin(42) From 97cf96a75e7293e7c3615644e20e2729a8dc9f89 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 24 Mar 2023 12:57:52 +0900 Subject: [PATCH 364/775] rework on `src.slottypes` and `ir.argtypes` (#49113) This commit reworks our handling of `(src::CodeInfo).slottypes` and `(ir::IRCode).argtypes`. Currently `ir.argtypes` contains types for call arguments and local slots even after the SSA conversion (`slot2reg`), which can be quite confusing. Similarly, `src.slot[names|flags|types]` contains information about local slots regardless of whether `src` is optimized or not, even though local slots never appear within `src`. With this commit, we resize `ir.argtypes` so that it only contains information about call arguments after `slot2reg`, as well as resize `src.slot[names|flags|types]` after optimization. This commit removes unnecessary information upon appropriate timings and allows us to use `Core.OpaqueClosure(ir::IRCode)` constructor for such `ir` that is retrieved by `Base.code_ircode`: ```julia let ir = first(only(Base.code_ircode(sin, (Int,)))) @test OpaqueClosure(ir)(42) == sin(42) ir = first(only(Base.code_ircode(sin, (Float64,)))) @test OpaqueClosure(ir)(42.) == sin(42.) end ``` Co-authored-by: Jameson Nash --- base/compiler/abstractinterpretation.jl | 2 +- base/compiler/optimize.jl | 20 ++++++--- base/compiler/ssair/legacy.jl | 18 ++++---- base/compiler/typeinfer.jl | 2 +- base/compiler/types.jl | 3 ++ base/opaque_closure.jl | 60 ++++++++++++++----------- test/compiler/inference.jl | 6 +-- test/compiler/ssair.jl | 19 ++++++++ test/opaque_closure.jl | 9 +++- 9 files changed, 94 insertions(+), 45 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 2087ad96f27ce..ed74fd84ac71c 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2060,7 +2060,7 @@ function most_general_argtypes(closure::PartialOpaque) if !isa(argt, DataType) || argt.name !== typename(Tuple) argt = Tuple end - return most_general_argtypes(closure.source, argt, false) + return most_general_argtypes(closure.source, argt, #=withfirst=#false) end # call where the function is any lattice element diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 0d19391fba5be..4bdde801614be 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -188,13 +188,16 @@ end function ir_to_codeinf!(opt::OptimizationState) (; linfo, src) = opt - optdef = linfo.def - replace_code_newstyle!(src, opt.ir::IRCode, isa(optdef, Method) ? Int(optdef.nargs) : 0) + src = ir_to_codeinf!(src, opt.ir::IRCode) opt.ir = nothing + validate_code_in_debug_mode(linfo, src, "optimized") + return src +end + +function ir_to_codeinf!(src::CodeInfo, ir::IRCode) + replace_code_newstyle!(src, ir) widen_all_consts!(src) src.inferred = true - # finish updating the result struct - validate_code_in_debug_mode(linfo, src, "optimized") return src end @@ -612,7 +615,11 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState) if cfg === nothing cfg = compute_basic_blocks(code) end - return IRCode(stmts, cfg, linetable, sv.slottypes, meta, sv.sptypes) + # NOTE this `argtypes` contains types of slots yet: it will be modified to contain the + # types of call arguments only once `slot2reg` converts this `IRCode` to the SSA form + # and eliminates slots (see below) + argtypes = sv.slottypes + return IRCode(stmts, cfg, linetable, argtypes, meta, sv.sptypes) end function process_meta!(meta::Vector{Expr}, @nospecialize stmt) @@ -631,6 +638,9 @@ function slot2reg(ir::IRCode, ci::CodeInfo, sv::OptimizationState) defuse_insts = scan_slot_def_use(nargs, ci, ir.stmts.inst) 𝕃ₒ = optimizer_lattice(sv.inlining.interp) @timeit "construct_ssa" ir = construct_ssa!(ci, ir, domtree, defuse_insts, sv.slottypes, 𝕃ₒ) # consumes `ir` + # NOTE now we have converted `ir` to the SSA form and eliminated slots + # let's resize `argtypes` now and remove unnecessary types for the eliminated slots + resize!(ir.argtypes, nargs) return ir end diff --git a/base/compiler/ssair/legacy.jl b/base/compiler/ssair/legacy.jl index 0539d79fc17e7..e2c924d60cb83 100644 --- a/base/compiler/ssair/legacy.jl +++ b/base/compiler/ssair/legacy.jl @@ -10,11 +10,7 @@ the original `ci::CodeInfo` are modified. """ function inflate_ir!(ci::CodeInfo, linfo::MethodInstance) sptypes = sptypes_from_meth_instance(linfo) - if ci.inferred - argtypes, _ = matching_cache_argtypes(fallback_lattice, linfo) - else - argtypes = Any[ Any for i = 1:length(ci.slotflags) ] - end + argtypes, _ = matching_cache_argtypes(fallback_lattice, linfo) return inflate_ir!(ci, sptypes, argtypes) end function inflate_ir!(ci::CodeInfo, sptypes::Vector{VarState}, argtypes::Vector{Any}) @@ -60,15 +56,21 @@ Mainly used for testing or interactive use. inflate_ir(ci::CodeInfo, linfo::MethodInstance) = inflate_ir!(copy(ci), linfo) inflate_ir(ci::CodeInfo, sptypes::Vector{VarState}, argtypes::Vector{Any}) = inflate_ir!(copy(ci), sptypes, argtypes) function inflate_ir(ci::CodeInfo) - slottypes = ci.slottypes - argtypes = Any[ slottypes === nothing ? Any : slottypes[i] for i = 1:length(ci.slotflags) ] + parent = ci.parent + isa(parent, MethodInstance) && return inflate_ir(ci, parent) + # XXX the length of `ci.slotflags` may be different from the actual number of call + # arguments, but we really don't know that information in this case + argtypes = Any[ Any for i = 1:length(ci.slotflags) ] return inflate_ir(ci, VarState[], argtypes) end -function replace_code_newstyle!(ci::CodeInfo, ir::IRCode, nargs::Int) +function replace_code_newstyle!(ci::CodeInfo, ir::IRCode) @assert isempty(ir.new_nodes) # All but the first `nargs` slots will now be unused + nargs = length(ir.argtypes) + resize!(ci.slotnames, nargs) resize!(ci.slotflags, nargs) + resize!(ci.slottypes, nargs) stmts = ir.stmts code = ci.code = stmts.inst ssavaluetypes = ci.ssavaluetypes = stmts.type diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 72f1b7b76c8fd..4cea01644737c 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -1004,13 +1004,13 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) tree.ssavaluetypes = 1 tree.codelocs = Int32[1] tree.linetable = LineInfoNode[LineInfoNode(method.module, method.name, method.file, method.line, Int32(0))] - tree.inferred = true tree.ssaflags = UInt8[0] set_inlineable!(tree, true) tree.parent = mi tree.rettype = Core.Typeof(rettype_const) tree.min_world = code.min_world tree.max_world = code.max_world + tree.inferred = true return tree elseif isa(inf, CodeInfo) ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) diff --git a/base/compiler/types.jl b/base/compiler/types.jl index 89235d3a5dd91..d3a13ec937f88 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -69,6 +69,9 @@ mutable struct InferenceResult argescapes # ::ArgEscapeCache if optimized, nothing otherwise must_be_codeinf::Bool # if this must come out as CodeInfo or leaving it as IRCode is ok function InferenceResult(linfo::MethodInstance, cache_argtypes::Vector{Any}, overridden_by_const::BitVector) + # def = linfo.def + # nargs = def isa Method ? Int(def.nargs) : 0 + # @assert length(cache_argtypes) == nargs return new(linfo, cache_argtypes, overridden_by_const, nothing, nothing, WorldRange(), Effects(), Effects(), nothing, true) end diff --git a/base/opaque_closure.jl b/base/opaque_closure.jl index 730cbb735b2fc..bb0ae8935b06c 100644 --- a/base/opaque_closure.jl +++ b/base/opaque_closure.jl @@ -40,10 +40,11 @@ function compute_ir_rettype(ir::IRCode) return Core.Compiler.widenconst(rt) end -function compute_oc_argtypes(ir, nargs, isva) - argtypes = ir.argtypes[2:end] - @assert nargs == length(argtypes) - argtypes = Core.Compiler.anymap(Core.Compiler.widenconst, argtypes) +function compute_oc_signature(ir::IRCode, nargs::Int, isva::Bool) + argtypes = Vector{Any}(undef, nargs) + for i = 1:nargs + argtypes[i] = Core.Compiler.widenconst(ir.argtypes[i+1]) + end if isva lastarg = pop!(argtypes) if lastarg <: Tuple @@ -52,35 +53,42 @@ function compute_oc_argtypes(ir, nargs, isva) push!(argtypes, Vararg{Any}) end end - Tuple{argtypes...} + return Tuple{argtypes...} end -function Core.OpaqueClosure(ir::IRCode, env...; - nargs::Int = length(ir.argtypes)-1, - isva::Bool = false, - rt = compute_ir_rettype(ir), - do_compile::Bool = true) - if (isva && nargs > length(ir.argtypes)) || (!isva && nargs != length(ir.argtypes)-1) - throw(ArgumentError("invalid argument count")) - end +function Core.OpaqueClosure(ir::IRCode, @nospecialize env...; + isva::Bool = false, + do_compile::Bool = true) + # NOTE: we need ir.argtypes[1] == typeof(env) ir = Core.Compiler.copy(ir) + nargs = length(ir.argtypes)-1 + sig = compute_oc_signature(ir, nargs, isva) + rt = compute_ir_rettype(ir) src = ccall(:jl_new_code_info_uninit, Ref{CodeInfo}, ()) - src.slotflags = UInt8[] src.slotnames = fill(:none, nargs+1) + src.slotflags = fill(zero(UInt8), length(ir.argtypes)) src.slottypes = copy(ir.argtypes) - Core.Compiler.replace_code_newstyle!(src, ir, nargs+1) - Core.Compiler.widen_all_consts!(src) - src.inferred = true - # NOTE: we need ir.argtypes[1] == typeof(env) - - ccall(:jl_new_opaque_closure_from_code_info, Any, (Any, Any, Any, Any, Any, Cint, Any, Cint, Cint, Any, Cint), - compute_oc_argtypes(ir, nargs, isva), Union{}, rt, @__MODULE__, src, 0, nothing, nargs, isva, env, do_compile) + src.rettype = rt + src = Core.Compiler.ir_to_codeinf!(src, ir) + return generate_opaque_closure(sig, Union{}, rt, src, nargs, isva, env...; do_compile) end -function Core.OpaqueClosure(src::CodeInfo, env...) - M = src.parent.def - sig = Base.tuple_type_tail(src.parent.specTypes) +function Core.OpaqueClosure(src::CodeInfo, @nospecialize env...) + src.inferred || throw(ArgumentError("Expected inferred src::CodeInfo")) + mi = src.parent::Core.MethodInstance + sig = Base.tuple_type_tail(mi.specTypes) + method = mi.def::Method + nargs = method.nargs-1 + isva = method.isva + return generate_opaque_closure(sig, Union{}, src.rettype, src, nargs, isva, env...) +end - ccall(:jl_new_opaque_closure_from_code_info, Any, (Any, Any, Any, Any, Any, Cint, Any, Cint, Cint, Any, Cint), - sig, Union{}, src.rettype, @__MODULE__, src, 0, nothing, M.nargs - 1, M.isva, env, true) +function generate_opaque_closure(@nospecialize(sig), @nospecialize(rt_lb), @nospecialize(rt_ub), + src::CodeInfo, nargs::Int, isva::Bool, @nospecialize env...; + mod::Module=@__MODULE__, + lineno::Int=0, + file::Union{Nothing,Symbol}=nothing, + do_compile::Bool=true) + return ccall(:jl_new_opaque_closure_from_code_info, Any, (Any, Any, Any, Any, Any, Cint, Any, Cint, Cint, Any, Cint), + sig, rt_lb, rt_ub, mod, src, lineno, file, nargs, isva, env, do_compile) end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index e3d66c7531021..e466dc8a3739a 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -4099,18 +4099,18 @@ let # Test the presence of PhiNodes in lowered IR by taking the above function, mi = Core.Compiler.specialize_method(first(methods(f_convert_me_to_ir)), Tuple{Bool, Float64}, Core.svec()) ci = Base.uncompressed_ast(mi.def) + ci.slottypes = Any[ Any for i = 1:length(ci.slotflags) ] ci.ssavaluetypes = Any[Any for i = 1:ci.ssavaluetypes] sv = Core.Compiler.OptimizationState(mi, Core.Compiler.NativeInterpreter()) ir = Core.Compiler.convert_to_ircode(ci, sv) ir = Core.Compiler.slot2reg(ir, ci, sv) ir = Core.Compiler.compact!(ir) - Core.Compiler.replace_code_newstyle!(ci, ir, 4) - ci.ssavaluetypes = length(ci.code) + Core.Compiler.replace_code_newstyle!(ci, ir) + ci.ssavaluetypes = length(ci.ssavaluetypes) @test any(x->isa(x, Core.PhiNode), ci.code) oc = @eval b->$(Expr(:new_opaque_closure, Tuple{Bool, Float64}, Any, Any, Expr(:opaque_closure_method, nothing, 2, false, LineNumberNode(0, nothing), ci)))(b, 1.0) @test Base.return_types(oc, Tuple{Bool}) == Any[Float64] - oc = @eval ()->$(Expr(:new_opaque_closure, Tuple{Bool, Float64}, Any, Any, Expr(:opaque_closure_method, nothing, 2, false, LineNumberNode(0, nothing), ci)))(true, 1.0) @test Base.return_types(oc, Tuple{}) == Any[Float64] diff --git a/test/compiler/ssair.jl b/test/compiler/ssair.jl index 5829752c20420..38862c123f160 100644 --- a/test/compiler/ssair.jl +++ b/test/compiler/ssair.jl @@ -358,6 +358,25 @@ end @test first(only(Base.code_ircode(demo; optimize_until = "SROA"))) isa Compiler.IRCode end +# slots after SSA conversion +function f_with_slots(a, b) + # `c` and `d` are local variables + c = a + b + d = c > 0 + return (c, d) +end +let # #self#, a, b, c, d + unopt = code_typed1(f_with_slots, (Int,Int); optimize=false) + @test length(unopt.slotnames) == length(unopt.slotflags) == length(unopt.slottypes) == 5 + ir_withslots = first(only(Base.code_ircode(f_with_slots, (Int,Int); optimize_until="convert"))) + @test length(ir_withslots.argtypes) == 5 + # #self#, a, b + opt = code_typed1(f_with_slots, (Int,Int); optimize=true) + @test length(opt.slotnames) == length(opt.slotflags) == length(opt.slottypes) == 3 + ir_ssa = first(only(Base.code_ircode(f_with_slots, (Int,Int); optimize_until="slot2reg"))) + @test length(ir_ssa.argtypes) == 3 +end + let function test_useref(stmt, v, op) if isa(stmt, Expr) diff --git a/test/opaque_closure.jl b/test/opaque_closure.jl index 767e9ed8bc1cd..7db55ff7f0615 100644 --- a/test/opaque_closure.jl +++ b/test/opaque_closure.jl @@ -256,7 +256,14 @@ let src = first(only(code_typed(+, (Int, Int)))) @test oc(40, 2) == 42 @test isa(oc, OpaqueClosure{Tuple{Int,Int}, Int}) @test_throws TypeError oc("40", 2) - @test OpaqueClosure(ir)(40, 2) == 42 # OpaqueClosure constructor should be non-destructive + @test OpaqueClosure(ir)(40, 2) == 42 # the `OpaqueClosure(::IRCode)` constructor should be non-destructive +end +let ir = first(only(Base.code_ircode(sin, (Int,)))) + @test OpaqueClosure(ir)(42) == sin(42) + @test OpaqueClosure(ir)(42) == sin(42) # the `OpaqueClosure(::IRCode)` constructor should be non-destructive + ir = first(only(Base.code_ircode(sin, (Float64,)))) + @test OpaqueClosure(ir)(42.) == sin(42.) + @test OpaqueClosure(ir)(42.) == sin(42.) # the `OpaqueClosure(::IRCode)` constructor should be non-destructive end # variadic arguments From db7971f49912d1abba703345ca6eb43249607f32 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 24 Mar 2023 05:10:22 +0100 Subject: [PATCH 365/775] Type-assert the value type in getindex(::AbstractDict, key) (#49115) Closes https://github.com/JuliaCollections/OrderedCollections.jl/issues/101 --- base/abstractdict.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/abstractdict.jl b/base/abstractdict.jl index c523a25cecd3f..ab1ad2464cb4c 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -536,12 +536,12 @@ function hash(a::AbstractDict, h::UInt) hash(hv, h) end -function getindex(t::AbstractDict, key) +function getindex(t::AbstractDict{<:Any,V}, key) where V v = get(t, key, secret_table_token) if v === secret_table_token throw(KeyError(key)) end - return v + return v::V end # t[k1,k2,ks...] is syntactic sugar for t[(k1,k2,ks...)]. (Note From f8c6be4ec57b055903946ac81b905c53bf0012e2 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 9 Feb 2023 00:09:14 +0800 Subject: [PATCH 366/775] Make subtype handle offset from intersection correctly. This enable more subtyping check during intersection. Also move L-R offset into `jl_stenv_t` to save 16bits for each varblinding. --- src/subtype.c | 274 ++++++++++++++++++++++++++++++++---------------- test/subtype.jl | 2 +- 2 files changed, 186 insertions(+), 90 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 8760fa94e351d..83bf1c0977cf8 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -78,9 +78,6 @@ typedef struct jl_varbinding_t { int8_t intvalued; // intvalued: must be integer-valued; i.e. occurs as N in Vararg{_,N} int8_t limited; int16_t depth0; // # of invariant constructors nested around the UnionAll type for this var - // when this variable's integer value is compared to that of another, - // it equals `other + offset`. used by vararg length parameters. - int16_t offset; // array of typevars that our bounds depend on, whose UnionAlls need to be // moved outside ours. jl_array_t *innervars; @@ -102,6 +99,9 @@ typedef struct jl_stenv_t { int intersection; // true iff subtype is being called from intersection int emptiness_only; // true iff intersection only needs to test for emptiness int triangular; // when intersecting Ref{X} with Ref{<:Y} + // Used to represent the length difference between 2 vararg. + // intersect(X, Y) ==> X = Y + Loffset + int Loffset; } jl_stenv_t; // state manipulation utilities @@ -234,6 +234,8 @@ static void clean_occurs(jl_stenv_t *e) } } +#define flip_offset(e) ((e)->Loffset *= -1) + // type utilities // quickly test that two types are identical @@ -614,6 +616,8 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t // subtype for variable bounds consistency check. needs its own forall/exists environment. static int subtype_ccheck(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) { + if (jl_is_long(x) && jl_is_long(y)) + return jl_unbox_long(x) == jl_unbox_long(y) + e->Loffset; if (x == y) return 1; if (x == jl_bottom_type && jl_is_type(y)) @@ -632,6 +636,8 @@ static int subtype_ccheck(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) static int subtype_left_var(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) { + if (jl_is_long(x) && jl_is_long(y)) + return jl_unbox_long(x) == jl_unbox_long(y) + e->Loffset; if (x == y && !(jl_is_unionall(y))) return 1; if (x == jl_bottom_type && jl_is_type(y)) @@ -686,6 +692,10 @@ static int var_lt(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int param) if (bb == NULL) return e->ignore_free || subtype_left_var(b->ub, a, e, param); record_var_occurrence(bb, e, param); + assert(!jl_is_long(a) || e->Loffset == 0); + if (e->Loffset != 0 && !jl_is_typevar(a) && + a != jl_bottom_type && a != (jl_value_t *)jl_any_type) + return 0; if (!bb->right) // check ∀b . b<:a return subtype_left_var(bb->ub, a, e, param); if (bb->ub == a) @@ -723,14 +733,14 @@ static int var_gt(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int param) if (bb == NULL) return e->ignore_free || subtype_left_var(a, b->lb, e, param); record_var_occurrence(bb, e, param); + assert(!jl_is_long(a) || e->Loffset == 0); + if (e->Loffset != 0 && !jl_is_typevar(a) && + a != jl_bottom_type && a != (jl_value_t *)jl_any_type) + return 0; if (!bb->right) // check ∀b . b>:a return subtype_left_var(a, bb->lb, e, param); - if (bb->lb == bb->ub) { - if (jl_is_typevar(bb->lb) && !jl_is_type(a) && !jl_is_typevar(a)) - return var_gt((jl_tvar_t*)bb->lb, a, e, param); - if (jl_is_typevar(a) && !jl_is_type(bb->lb) && !jl_is_typevar(bb->lb)) - return var_lt((jl_tvar_t*)a, bb->lb, e, param); - } + if (bb->lb == a) + return 1; if (!((bb->ub == (jl_value_t*)jl_any_type && !jl_is_type(a) && !jl_is_typevar(a)) || subtype_ccheck(a, bb->ub, e))) return 0; jl_value_t *lb = simple_join(bb->lb, a); @@ -748,6 +758,30 @@ static int var_gt(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int param) return 1; } +static int subtype_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int R, int param) +{ + if (e->intersection) { + jl_varbinding_t *bb = lookup(e, (jl_tvar_t*)b); + jl_value_t *bub = bb ? bb->ub : ((jl_tvar_t*)b)->ub; + jl_value_t *blb = bb ? bb->lb : ((jl_tvar_t*)b)->lb; + if (bub == blb && jl_is_typevar(bub)) { + int sub = subtype_var((jl_tvar_t *)bub, a, e, R, param); + return sub; + } + } + if (e->Loffset != 0 && jl_is_long(a)) { + int old_offset = R ? -e->Loffset : e->Loffset; + jl_value_t *na = jl_box_long(jl_unbox_long(a) + old_offset); + JL_GC_PUSH1(&na); + e->Loffset = 0; + int sub = R ? var_gt(b, na, e, param) : var_lt(b, na, e, param); + e->Loffset = R ? -old_offset : old_offset; + JL_GC_POP(); + return sub; + } + return R ? var_gt(b, a, e, param) : var_lt(b, a, e, param); +} + // check that a type is concrete or quasi-concrete (Type{T}). // this is used to check concrete typevars: // issubtype is false if the lower bound of a concrete type var is not concrete. @@ -844,7 +878,7 @@ static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8 { u = unalias_unionall(u, e); jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, 0, 0, 0, - e->invdepth, 0, NULL, e->vars }; + e->invdepth, NULL, e->vars }; JL_GC_PUSH4(&u, &vb.lb, &vb.ub, &vb.innervars); e->vars = &vb; int ans; @@ -1050,11 +1084,38 @@ static int subtype_tuple_varargs( // Vararg{T,N} <: Vararg{T2,N2}; equate N and N2 e->invdepth++; JL_GC_PUSH2(&xp1, &yp1); - if (xp1 && jl_is_long(xp1) && vx != 1) - xp1 = jl_box_long(jl_unbox_long(xp1) - vx + 1); - if (jl_is_long(yp1) && vy != 1) - yp1 = jl_box_long(jl_unbox_long(yp1) - vy + 1); - int ans = forall_exists_equal(xp1, yp1, e); + int ans; + jl_varbinding_t *bxp1 = jl_is_typevar(xp1) ? lookup(e, (jl_tvar_t *)xp1) : NULL; + jl_varbinding_t *byp1 = jl_is_typevar(yp1) ? lookup(e, (jl_tvar_t *)yp1) : NULL; + if (bxp1) { + if (bxp1->intvalued == 0) + bxp1->intvalued = 1; + if (jl_is_long(bxp1->lb)) + xp1 = bxp1->lb; + } + if (byp1) { + if (byp1->intvalued == 0) + byp1->intvalued = 1; + if (jl_is_long(byp1->lb)) + yp1 = byp1->lb; + } + if (jl_is_long(xp1) && jl_is_long(yp1)) + ans = jl_unbox_long(xp1) - vx == jl_unbox_long(yp1) - vy; + else { + if (jl_is_long(xp1) && vx != vy) { + xp1 = jl_box_long(jl_unbox_long(xp1) + vy - vx); + vx = vy; + } + if (jl_is_long(yp1) && vy != vx) { + yp1 = jl_box_long(jl_unbox_long(yp1) + vx - vy); + vy = vx; + } + assert(e->Loffset == 0); + e->Loffset = vx - vy; + ans = forall_exists_equal(xp1, yp1, e); + assert(e->Loffset == vx - vy); + e->Loffset = 0; + } JL_GC_POP(); e->invdepth--; return ans; @@ -1094,7 +1155,8 @@ static int subtype_tuple_tail(jl_datatype_t *xd, jl_datatype_t *yd, int8_t R, jl xi = jl_tparam(xd, lx-1); if (jl_is_vararg(xi)) { all_varargs = 1; - vy += lx - i; + // count up to lx-2 rather than lx-1. + vy += lx - i - 1; vx = 1; } else { break; @@ -1289,10 +1351,10 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) return subtype_unionall(x, (jl_unionall_t*)y, e, 1, param); } } - return var_lt((jl_tvar_t*)x, y, e, param); + return subtype_var((jl_tvar_t*)x, y, e, 0, param); } if (jl_is_typevar(y)) - return var_gt((jl_tvar_t*)y, x, e, param); + return subtype_var((jl_tvar_t*)y, x, e, 1, param); if (y == (jl_value_t*)jl_any_type && !jl_has_free_typevars(x)) return 1; if (x == jl_bottom_type && !jl_has_free_typevars(y)) @@ -1370,6 +1432,8 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) } if (jl_is_type(y)) return x == jl_bottom_type; + if (jl_is_long(x) && jl_is_long(y)) + return jl_unbox_long(x) == jl_unbox_long(y) + e->Loffset; return jl_egal(x, y); } @@ -1482,9 +1546,12 @@ static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) jl_saved_unionstate_t oldLunions; push_unionstate(&oldLunions, &e->Lunions); int limit_slow = !jl_has_free_typevars(x) || !jl_has_free_typevars(y); - int sub = local_forall_exists_subtype(x, y, e, 2, limit_slow) && - local_forall_exists_subtype(y, x, e, 0, 0); - + int sub = local_forall_exists_subtype(x, y, e, 2, limit_slow); + if (sub) { + flip_offset(e); + sub = local_forall_exists_subtype(y, x, e, 0, 0); + flip_offset(e); + } pop_unionstate(&e->Lunions, &oldLunions); return sub; } @@ -1562,6 +1629,7 @@ static void init_stenv(jl_stenv_t *e, jl_value_t **env, int envsz) e->intersection = 0; e->emptiness_only = 0; e->triangular = 0; + e->Loffset = 0; e->Lunions.depth = 0; e->Runions.depth = 0; e->Lunions.more = 0; e->Runions.more = 0; e->Lunions.used = 0; e->Runions.used = 0; @@ -2023,6 +2091,7 @@ static int subtype_in_env(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) e2.envsz = e->envsz; e2.envout = e->envout; e2.envidx = e->envidx; + e2.Loffset = e->Loffset; return forall_exists_subtype(x, y, &e2, 0); } @@ -2271,12 +2340,9 @@ static jl_value_t *intersect_union(jl_value_t *x, jl_uniontype_t *u, jl_stenv_t } // set a variable to a non-type constant -static jl_value_t *set_var_to_const(jl_varbinding_t *bb, jl_value_t *v JL_MAYBE_UNROOTED, jl_varbinding_t *othervar) +static jl_value_t *set_var_to_const(jl_varbinding_t *bb, jl_value_t *v JL_MAYBE_UNROOTED, jl_stenv_t *e, int R) { - int offset = bb->offset; - if (othervar && offset == 0) - offset = -othervar->offset; - assert(!othervar || othervar->offset == -offset); + int offset = R ? -e->Loffset : e->Loffset; if (bb->lb == jl_bottom_type && bb->ub == (jl_value_t*)jl_any_type) { if (offset == 0) bb->lb = bb->ub = v; @@ -2303,7 +2369,7 @@ static jl_value_t *set_var_to_const(jl_varbinding_t *bb, jl_value_t *v JL_MAYBE_ return v; } -static jl_value_t *bound_var_below(jl_tvar_t *tv, jl_varbinding_t *bb, jl_stenv_t *e) { +static jl_value_t *bound_var_below(jl_tvar_t *tv, jl_varbinding_t *bb, jl_stenv_t *e, int R) { if (!bb) return (jl_value_t*)tv; if (bb->depth0 != e->invdepth) @@ -2311,16 +2377,17 @@ static jl_value_t *bound_var_below(jl_tvar_t *tv, jl_varbinding_t *bb, jl_stenv_ e->invdepth++; record_var_occurrence(bb, e, 2); e->invdepth--; + int offset = R ? -e->Loffset : e->Loffset; if (jl_is_long(bb->lb)) { ssize_t blb = jl_unbox_long(bb->lb); - if ((blb < bb->offset) || (blb < 0)) + if (blb < offset || blb < 0) return jl_bottom_type; // Here we always return the shorter `Vararg`'s length. - if (bb->offset <= 0) + if (offset <= 0) return bb->lb; - return jl_box_long(blb - bb->offset); + return jl_box_long(blb - offset); } - if (bb->offset > 0) + if (offset > 0) return NULL; return (jl_value_t*)tv; } @@ -2481,7 +2548,7 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int if (bb->lb == bb->ub && jl_is_typevar(bb->lb)) return R ? intersect(a, bb->lb, e, param) : intersect(bb->lb, a, e, param); if (!jl_is_type(a) && !jl_is_typevar(a)) - return set_var_to_const(bb, a, NULL); + return set_var_to_const(bb, a, e, R); jl_value_t *root=NULL; jl_savedenv_t se; if (param == 2) { jl_value_t *ub = NULL; @@ -2874,7 +2941,7 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_ jl_value_t *res=NULL, *save=NULL; jl_savedenv_t se; jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, 0, 0, 0, - e->invdepth, 0, NULL, e->vars }; + e->invdepth, NULL, e->vars }; JL_GC_PUSH5(&res, &vb.lb, &vb.ub, &save, &vb.innervars); save_env(e, &save, &se); res = intersect_unionall_(t, u, e, R, param, &vb); @@ -2937,24 +3004,20 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, ssize_t JL_GC_POP(); return ii; } + assert(e->Loffset == 0); + e->Loffset = offset; jl_varbinding_t *xb = NULL, *yb = NULL; if (xp2 && jl_is_typevar(xp2)) { xb = lookup(e, (jl_tvar_t*)xp2); - if (xb) { - xb->intvalued = 1; - xb->offset = offset; - } + if (xb) xb->intvalued = 1; if (!yp2) - i2 = bound_var_below((jl_tvar_t*)xp2, xb, e); + i2 = bound_var_below((jl_tvar_t*)xp2, xb, e, 0); } if (yp2 && jl_is_typevar(yp2)) { yb = lookup(e, (jl_tvar_t*)yp2); - if (yb) { - yb->intvalued = 1; - yb->offset = -offset; - } + if (yb) yb->intvalued = 1; if (!xp2) - i2 = bound_var_below((jl_tvar_t*)yp2, yb, e); + i2 = bound_var_below((jl_tvar_t*)yp2, yb, e, 1); } if (xp2 && yp2) { // Vararg{T,N} <: Vararg{T2,N2}; equate N and N2 @@ -2965,8 +3028,8 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, ssize_t i2 = jl_bottom_type; } } - if (xb) xb->offset = 0; - if (yb) yb->offset = 0; + assert(e->Loffset == offset); + e->Loffset = 0; ii = i2 == jl_bottom_type ? (jl_value_t*)jl_bottom_type : (jl_value_t*)jl_wrap_vararg(ii, i2); JL_GC_POP(); return ii; @@ -3034,8 +3097,9 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten res = jl_bottom_type; } else { - if (xb) set_var_to_const(xb, jl_box_long(len-lx+1), yb); - if (yb) set_var_to_const(yb, jl_box_long(len-ly+1), xb); + assert(e->Loffset == 0); + if (xb) set_var_to_const(xb, jl_box_long(len-lx+1), e, 0); + if (yb) set_var_to_const(yb, jl_box_long(len-ly+1), e, 1); res = (jl_value_t*)jl_apply_tuple_type_v(jl_svec_data(params), len); } } @@ -3072,6 +3136,7 @@ static jl_value_t *intersect_sub_datatype(jl_datatype_t *xd, jl_datatype_t *yd, // attempt to populate additional constraints into `e` // if that attempt fails, then return bottom // otherwise return xd (finish_unionall will later handle propagating those constraints) + assert(e->Loffset == 0); jl_value_t *isuper = R ? intersect((jl_value_t*)yd, (jl_value_t*)xd->super, e, param) : intersect((jl_value_t*)xd->super, (jl_value_t*)yd, e, param); if (isuper == jl_bottom_type) @@ -3081,28 +3146,23 @@ static jl_value_t *intersect_sub_datatype(jl_datatype_t *xd, jl_datatype_t *yd, static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) { - if (!jl_has_free_typevars(x) && !jl_has_free_typevars(y)) { + if (e->Loffset == 0 && !jl_has_free_typevars(x) && !jl_has_free_typevars(y)) { return (jl_subtype(x,y) && jl_subtype(y,x)) ? y : NULL; } e->invdepth++; jl_value_t *ii = intersect(x, y, e, 2); e->invdepth--; - // Skip the following subtype check if `ii` was returned from `set_vat_to_const`. - // As `var_gt`/`var_lt` might not handle `Vararg` length offset correctly. - // TODO: fix this on subtype side and remove this branch. - if (jl_is_long(ii) && ((jl_is_typevar(x) && jl_is_long(y)) || (jl_is_typevar(y) && jl_is_long(x)))) - return ii; - if (jl_is_typevar(x) && jl_is_typevar(y) && (jl_is_typevar(ii) || !jl_is_type(ii))) - return ii; + if (jl_is_typevar(x) && jl_is_typevar(y) && jl_is_typevar(ii)) + return ii; // skip the following check due to possible circular constraints. if (ii == jl_bottom_type) { if (!subtype_in_env(x, jl_bottom_type, e)) return NULL; - flip_vars(e); + flip_vars(e); flip_offset(e); if (!subtype_in_env(y, jl_bottom_type, e)) { - flip_vars(e); + flip_vars(e); flip_offset(e); return NULL; } - flip_vars(e); + flip_vars(e); flip_offset(e); return jl_bottom_type; } jl_value_t *root=NULL; @@ -3113,8 +3173,10 @@ static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t ii = NULL; else { restore_env(e, root, &se); + flip_offset(e); if (!subtype_in_env_existential(y, x, e)) ii = NULL; + flip_offset(e); } restore_env(e, root, &se); free_env(&se); @@ -3125,6 +3187,7 @@ static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t // intersection where x == Type{...} and y is not static jl_value_t *intersect_type_type(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int8_t R) { + assert(e->Loffset == 0); jl_value_t *p0 = jl_tparam0(x); if (!jl_is_typevar(p0)) return (jl_typeof(p0) == y) ? x : jl_bottom_type; @@ -3199,54 +3262,82 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa jl_value_t *xub = xx ? xx->ub : ((jl_tvar_t*)x)->ub; jl_value_t *ylb = yy ? yy->lb : ((jl_tvar_t*)y)->lb; jl_value_t *yub = yy ? yy->ub : ((jl_tvar_t*)y)->ub; - record_var_occurrence(xx, e, param); if (xx && yy && xx->depth0 != yy->depth0) { + record_var_occurrence(xx, e, param); record_var_occurrence(yy, e, param); return subtype_in_env(yy->ub, yy->lb, e) ? y : jl_bottom_type; } if (xub == xlb && jl_is_typevar(xub)) { + record_var_occurrence(xx, e, param); if (y == xub) { record_var_occurrence(yy, e, param); return y; } - if (!xx || xx->offset == 0) - return intersect(y, xub, e, param); - // try to propagate the x's offset to xub. - jl_varbinding_t *tvb = lookup(e, (jl_tvar_t*)xub); - assert(tvb && tvb->offset == 0); - tvb->offset = xx->offset; - jl_value_t *res = intersect(y, xub, e, param); - tvb->offset = 0; + if (R) flip_offset(e); + jl_value_t *res = intersect(xub, y, e, param); + if (R) flip_offset(e); + return res; + } + if (yub == ylb && jl_is_typevar(yub)) { + record_var_occurrence(yy, e, param); + if (R) flip_offset(e); + jl_value_t *res = intersect(x, yub, e, param); + if (R) flip_offset(e); + return res; + } + if (xub == xlb && jl_is_typevar(xub)) { + record_var_occurrence(xx, e, param); + if (y == xub) { + record_var_occurrence(yy, e, param); + return y; + } + if (R) flip_offset(e); + jl_value_t *res = intersect(xub, y, e, param); + if (R) flip_offset(e); return res; } - record_var_occurrence(yy, e, param); if (yub == ylb && jl_is_typevar(yub)) { - // We always set inner var equal to outer. - if (!yy || yy->offset == 0) - return intersect(x, yub, e, param); - // try to propagate the y's offset to yub. - jl_varbinding_t *tvb = lookup(e, (jl_tvar_t*)yub); - assert(tvb && tvb->offset == 0); - tvb->offset = yy->offset; + record_var_occurrence(yy, e, param); + if (R) flip_offset(e); jl_value_t *res = intersect(x, yub, e, param); - tvb->offset = 0; + if (R) flip_offset(e); return res; } + record_var_occurrence(xx, e, param); + record_var_occurrence(yy, e, param); + int xoffset = R ? -e->Loffset : e->Loffset; if (!jl_is_type(ylb) && !jl_is_typevar(ylb)) { if (xx) - return set_var_to_const(xx, ylb, yy); - if ((xlb == jl_bottom_type && xub == (jl_value_t*)jl_any_type) || jl_egal(xlb, ylb)) - return ylb; + return set_var_to_const(xx, ylb, e, R); + if ((xlb == jl_bottom_type && xub == (jl_value_t*)jl_any_type) || jl_egal(xlb, ylb)) { + if (xoffset == 0) + return ylb; + else if (jl_is_long(ylb)) { + if (xoffset > 0) + return ylb; + else + return jl_box_long(jl_unbox_long(ylb) + xoffset); + } + } return jl_bottom_type; } if (!jl_is_type(xlb) && !jl_is_typevar(xlb)) { if (yy) - return set_var_to_const(yy, xlb, xx); - if (ylb == jl_bottom_type && yub == (jl_value_t*)jl_any_type) - return xlb; + return set_var_to_const(yy, xlb, e, !R); + if (ylb == jl_bottom_type && yub == (jl_value_t*)jl_any_type) { + if (xoffset == 0) + return xlb; + else if (jl_is_long(xlb)) { + if (xoffset < 0) + return xlb; + else + return jl_box_long(jl_unbox_long(ylb) - xoffset); + } + } return jl_bottom_type; } int ccheck; + if (R) flip_offset(e); if (xlb == xub && ylb == yub && jl_has_typevar(xlb, (jl_tvar_t *)y) && jl_has_typevar(ylb, (jl_tvar_t *)x)) { @@ -3261,9 +3352,15 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa } else { if (R) flip_vars(e); - ccheck = subtype_in_env(xlb, yub, e) && subtype_in_env(ylb, xub, e); + ccheck = subtype_in_env(xlb, yub, e); + if (ccheck) { + flip_offset(e); + ccheck = subtype_in_env(ylb, xub, e); + flip_offset(e); + } if (R) flip_vars(e); } + if (R) flip_offset(e); if (!ccheck) return jl_bottom_type; if ((jl_has_typevar(xub, (jl_tvar_t*)y) || jl_has_typevar(xub, (jl_tvar_t*)x)) && @@ -3281,24 +3378,23 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa lb = ylb; else lb = simple_join(xlb, ylb); - if (yy && yy->offset == 0) { + if (yy && xoffset == 0) { yy->lb = lb; if (!reachable_var(ub, (jl_tvar_t*)y, e)) yy->ub = ub; assert(yy->ub != y); assert(yy->lb != y); } - if (xx && xx->offset == 0 && !reachable_var(y, (jl_tvar_t*)x, e)) { + if (xx && xoffset == 0 && !reachable_var(y, (jl_tvar_t*)x, e)) { xx->lb = y; xx->ub = y; assert(xx->ub != x); } JL_GC_POP(); // Here we always return the shorter `Vararg`'s length. - if ((xx && xx->offset < 0) || (yy && yy->offset > 0)) - return x; - return y; + return xoffset < 0 ? x : y; } + assert(e->Loffset == 0); record_var_occurrence(xx, e, param); record_var_occurrence(yy, e, param); if (xx && yy && xx->concrete && !yy->concrete) { @@ -3313,7 +3409,7 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa record_var_occurrence(lookup(e, (jl_tvar_t*)y), e, param); return intersect_var((jl_tvar_t*)y, x, e, 1, param); } - if (!jl_has_free_typevars(x) && !jl_has_free_typevars(y)) { + if (e->Loffset == 0 && !jl_has_free_typevars(x) && !jl_has_free_typevars(y)) { if (jl_subtype(x, y)) return x; if (jl_subtype(y, x)) return y; } diff --git a/test/subtype.jl b/test/subtype.jl index e2bb49a6e0123..56fcf394a639b 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2415,7 +2415,7 @@ abstract type P47654{A} end # issue 22123 t1 = Ref{Ref{Ref{Union{Int64, T}}} where T} t2 = Ref{Ref{Ref{Union{T, S}}} where T} where S - @test_broken t1 <: t2 + @test t1 <: t2 # issue 21153 @test_broken (Tuple{T1,T1} where T1<:(Val{T2} where T2)) <: (Tuple{Val{S},Val{S}} where S) From b74529361854205856c1a44ea7c3878365648406 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Fri, 24 Mar 2023 16:02:01 +0800 Subject: [PATCH 367/775] Fix intersection bug caused by intvalued clean. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After e528b304f0, intersection would increase `N`‘s `occurs_inv` when compare `Vararg{Any}` and `Vararg{Any,N}` for reintersection hint. Here we just need to check if `ylv` is unbounded. --- src/subtype.c | 5 ++++- test/subtype.jl | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/subtype.c b/src/subtype.c index 83bf1c0977cf8..357334f97960e 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1069,7 +1069,9 @@ static int subtype_tuple_varargs( } if (ylv) { - if (ylv->depth0 != e->invdepth || ylv->occurs_inv) + if (ylv->depth0 != e->invdepth || + ylv->lb != jl_bottom_type || + ylv->ub != (jl_value_t *)jl_any_type) return 0; ylv->intvalued = 1; } @@ -2518,6 +2520,7 @@ static int check_unsat_bound(jl_value_t *t, jl_tvar_t *v, jl_stenv_t *e) JL_NOTS return 0; } +//TODO: This doesn't work for nested `Tuple`. static int has_free_vararg_length(jl_value_t *a, jl_stenv_t *e) { if (jl_is_unionall(a)) a = jl_unwrap_unionall(a); diff --git a/test/subtype.jl b/test/subtype.jl index 56fcf394a639b..cece00c252cdb 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2218,6 +2218,12 @@ end # Example from pr#39098 @testintersect(NTuple, Tuple{Any,Vararg}, Tuple{T, Vararg{T}} where {T}) +let S = Val{T} where T<:Tuple{Tuple{Any, Vararg{Any}}} + T = Val{Tuple{Tuple{Vararg{Any, N}}}} where {N} + @testintersect(S, T, !Union{}) + @test_broken typeintersect(S, T) != Val{Tuple{Tuple{Any, Vararg{Any}}}} +end + let A = Pair{NTuple{N, Int}, Val{N}} where N, Bs = (Pair{<:Tuple{Int, Vararg{Int}}, <:Val}, Pair{Tuple{Int, Vararg{Int,N1}}, Val{N2}} where {N1,N2}) From d7df15d1d001192871105d61f5116ddade070e8f Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 24 Mar 2023 14:50:21 +0100 Subject: [PATCH 368/775] add back the exception to the error logging when loading an extension fails (#49075) --- base/loading.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/loading.jl b/base/loading.jl index b7acda85fea0d..ed6f170a55575 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1196,8 +1196,9 @@ function run_extension_callbacks(extid::ExtensionId) true catch # Try to continue loading if loading an extension errors + errs = current_exceptions() @error "Error during loading of extension $(extid.id.name) of $(extid.parentid.name), \ - use `Base.retry_load_extensions()` to retry." + use `Base.retry_load_extensions()` to retry." exception=errs false end return succeeded From dc3953db43b3c4c8e9c13cb92a7a4c4690d03325 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 24 Mar 2023 14:56:41 +0100 Subject: [PATCH 369/775] Reland: expose caller world to GeneratedFunctionStub (#48766) Expose the demanded world to the GeneratedFunctionStub caller, for users such as Cassette. If this argument is used, the user must return a CodeInfo with the min/max world field set correctly. Make the internal representation a tiny bit more compact also, removing a little bit of unnecessary metadata. Remove support for returning `body isa CodeInfo` via this wrapper, since it is impossible to return a correct object via the GeneratedFunctionStub since it strips off the world argument, which is required for it to do so. This also removes support for not inferring these fully (expand_early=false). Also answer method lookup queries about the future correctly, by refusing to answer them. This helps keeps execution correct as methods get added to the system asynchronously. This reverts "fix #25678: return matters for generated functions (#40778)" (commit 92c84bf3865403355af463b5a1dee42bf7143592), since this is no longer sensible to return here anyways, so it is no longer permitted or supported by this macro. Fixes various issues where we failed to specify the correct world. Note that the passed world may be `typemax(UInt)`, which demands that the generator must return code that is unspecialized (guaranteed to run correctly in any world). Co-authored-by: Jameson Nash --- base/Base.jl | 2 +- base/boot.jl | 27 ++++----- base/compiler/abstractinterpretation.jl | 13 +++-- base/compiler/bootstrap.jl | 2 +- base/compiler/inferencestate.jl | 3 +- base/compiler/optimize.jl | 3 +- base/compiler/typeinfer.jl | 2 +- base/compiler/types.jl | 2 +- base/compiler/utilities.jl | 8 +-- base/compiler/validation.jl | 7 +-- base/expr.jl | 5 +- base/reflection.jl | 39 ++++++++----- doc/src/devdocs/ast.md | 8 +-- src/aotcompile.cpp | 2 +- src/ast.c | 4 +- src/gf.c | 7 ++- src/interpreter.c | 8 ++- src/jitlayers.cpp | 4 +- src/julia-syntax.scm | 9 +-- src/julia.h | 2 +- src/julia_internal.h | 2 +- src/method.c | 47 +++++++--------- test/ambiguous.jl | 6 +- test/compiler/contextual.jl | 33 +++++------ test/compiler/inference.jl | 74 +++++++++++++------------ test/compiler/validation.jl | 9 ++- test/core.jl | 3 +- test/opaque_closure.jl | 31 +++++------ test/reflection.jl | 2 +- test/staged.jl | 40 ++++++++----- test/syntax.jl | 3 - 31 files changed, 204 insertions(+), 203 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 789fb7c0da05c..5e3e64a104ee6 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -479,7 +479,7 @@ in_sysimage(pkgid::PkgId) = pkgid in _sysimage_modules for match = _methods(+, (Int, Int), -1, get_world_counter()) m = match.method delete!(push!(Set{Method}(), m), m) - copy(Core.Compiler.retrieve_code_info(Core.Compiler.specialize_method(match))) + 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)) diff --git a/base/boot.jl b/base/boot.jl index b4e01b0c884c1..ca6e6c81405e2 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -590,28 +590,25 @@ println(@nospecialize a...) = println(stdout, a...) struct GeneratedFunctionStub gen - argnames::Array{Any,1} - spnames::Union{Nothing, Array{Any,1}} - line::Int - file::Symbol - expand_early::Bool + argnames::SimpleVector + spnames::SimpleVector end -# invoke and wrap the results of @generated -function (g::GeneratedFunctionStub)(@nospecialize args...) +# invoke and wrap the results of @generated expression +function (g::GeneratedFunctionStub)(world::UInt, source::LineNumberNode, @nospecialize args...) + # args is (spvals..., argtypes...) body = g.gen(args...) - if body isa CodeInfo - return body - end - lam = Expr(:lambda, g.argnames, - Expr(Symbol("scope-block"), + file = source.file + file isa Symbol || (file = :none) + lam = Expr(:lambda, Expr(:argnames, g.argnames...).args, + Expr(:var"scope-block", Expr(:block, - LineNumberNode(g.line, g.file), - Expr(:meta, :push_loc, g.file, Symbol("@generated body")), + source, + Expr(:meta, :push_loc, file, :var"@generated body"), Expr(:return, body), Expr(:meta, :pop_loc)))) spnames = g.spnames - if spnames === nothing + if spnames === svec() return lam else return Expr(Symbol("with-static-parameters"), lam, spnames...) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index ed74fd84ac71c..4980f051d1459 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -572,7 +572,7 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp break end topmost === nothing || continue - if edge_matches_sv(infstate, method, sig, sparams, hardlimit, sv) + if edge_matches_sv(interp, infstate, method, sig, sparams, hardlimit, sv) topmost = infstate edgecycle = true end @@ -680,12 +680,13 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp return MethodCallResult(rt, edgecycle, edgelimited, edge, effects) end -function edge_matches_sv(frame::InferenceState, method::Method, @nospecialize(sig), sparams::SimpleVector, hardlimit::Bool, sv::InferenceState) +function edge_matches_sv(interp::AbstractInterpreter, frame::InferenceState, method::Method, @nospecialize(sig), sparams::SimpleVector, hardlimit::Bool, sv::InferenceState) # The `method_for_inference_heuristics` will expand the given method's generator if # necessary in order to retrieve this field from the generated `CodeInfo`, if it exists. # The other `CodeInfo`s we inspect will already have this field inflated, so we just # access it directly instead (to avoid regeneration). - callee_method2 = method_for_inference_heuristics(method, sig, sparams) # Union{Method, Nothing} + world = get_world_counter(interp) + callee_method2 = method_for_inference_heuristics(method, sig, sparams, world) # Union{Method, Nothing} inf_method2 = frame.src.method_for_inference_limit_heuristics # limit only if user token match inf_method2 isa Method || (inf_method2 = nothing) @@ -722,11 +723,11 @@ function edge_matches_sv(frame::InferenceState, method::Method, @nospecialize(si end # This function is used for computing alternate limit heuristics -function method_for_inference_heuristics(method::Method, @nospecialize(sig), sparams::SimpleVector) - if isdefined(method, :generator) && method.generator.expand_early && may_invoke_generator(method, sig, sparams) +function method_for_inference_heuristics(method::Method, @nospecialize(sig), sparams::SimpleVector, world::UInt) + if isdefined(method, :generator) && !(method.generator isa Core.GeneratedFunctionStub) && may_invoke_generator(method, sig, sparams) method_instance = specialize_method(method, sig, sparams) if isa(method_instance, MethodInstance) - cinfo = get_staged(method_instance) + cinfo = get_staged(method_instance, world) if isa(cinfo, CodeInfo) method2 = cinfo.method_for_inference_limit_heuristics if method2 isa Method diff --git a/base/compiler/bootstrap.jl b/base/compiler/bootstrap.jl index 77b36cb9c7f71..1f62d21c9d2d9 100644 --- a/base/compiler/bootstrap.jl +++ b/base/compiler/bootstrap.jl @@ -36,7 +36,7 @@ let interp = NativeInterpreter() else tt = Tuple{typeof(f), Vararg{Any}} end - for m in _methods_by_ftype(tt, 10, typemax(UInt))::Vector + for m in _methods_by_ftype(tt, 10, get_world_counter())::Vector # remove any TypeVars from the intersection m = m::MethodMatch typ = Any[m.spec_types.parameters...] diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index aea657404b249..48d25243417fe 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -366,7 +366,8 @@ end function InferenceState(result::InferenceResult, cache::Symbol, interp::AbstractInterpreter) # prepare an InferenceState object for inferring lambda - src = retrieve_code_info(result.linfo) + world = get_world_counter(interp) + src = retrieve_code_info(result.linfo, world) src === nothing && return nothing validate_code_in_debug_mode(result.linfo, src, "lowered") return InferenceState(result, src, cache, interp) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 4bdde801614be..1b5f45fac3e2e 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -181,7 +181,8 @@ function OptimizationState(linfo::MethodInstance, src::CodeInfo, interp::Abstrac return OptimizationState(linfo, src, nothing, stmt_info, mod, sptypes, slottypes, inlining, nothing, false) end function OptimizationState(linfo::MethodInstance, interp::AbstractInterpreter) - src = retrieve_code_info(linfo) + world = get_world_counter(interp) + src = retrieve_code_info(linfo, world) src === nothing && return nothing return OptimizationState(linfo, src, interp) end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 4cea01644737c..7bebd9d7d64e5 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -1030,7 +1030,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) end end if ccall(:jl_get_module_infer, Cint, (Any,), method.module) == 0 && !generating_sysimg() - return retrieve_code_info(mi) + return retrieve_code_info(mi, get_world_counter(interp)) end lock_mi_inference(interp, mi) result = InferenceResult(mi, typeinf_lattice(interp)) diff --git a/base/compiler/types.jl b/base/compiler/types.jl index d3a13ec937f88..91d585cdd76ff 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -341,7 +341,7 @@ function NativeInterpreter(world::UInt = get_world_counter(); inf_params::InferenceParams = InferenceParams(), opt_params::OptimizationParams = OptimizationParams()) # Sometimes the caller is lazy and passes typemax(UInt). - # we cap it to the current world age + # we cap it to the current world age for correctness if world == typemax(UInt) world = get_world_counter() end diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 3c8357cf3a414..bae8ef5bae242 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -114,23 +114,23 @@ end invoke_api(li::CodeInstance) = ccall(:jl_invoke_api, Cint, (Any,), li) use_const_api(li::CodeInstance) = invoke_api(li) == 2 -function get_staged(mi::MethodInstance) +function get_staged(mi::MethodInstance, world::UInt) may_invoke_generator(mi) || return nothing try # user code might throw errors – ignore them - ci = ccall(:jl_code_for_staged, Any, (Any,), mi)::CodeInfo + ci = ccall(:jl_code_for_staged, Any, (Any, UInt), mi, world)::CodeInfo return ci catch return nothing end end -function retrieve_code_info(linfo::MethodInstance) +function retrieve_code_info(linfo::MethodInstance, world::UInt) m = linfo.def::Method c = nothing if isdefined(m, :generator) # user code might throw errors – ignore them - c = get_staged(linfo) + c = get_staged(linfo, world) end if c === nothing && isdefined(m, :source) src = m.source diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl index 22a21a4559985..68eb2ab15c59d 100644 --- a/base/compiler/validation.jl +++ b/base/compiler/validation.jl @@ -200,15 +200,14 @@ end """ validate_code!(errors::Vector{InvalidCodeError}, mi::MethodInstance, - c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi)) + c::Union{Nothing,CodeInfo}) Validate `mi`, logging any violation by pushing an `InvalidCodeError` into `errors`. If `isa(c, CodeInfo)`, also call `validate_code!(errors, c)`. It is assumed that `c` is -the `CodeInfo` instance associated with `mi`. +a `CodeInfo` instance associated with `mi`. """ -function validate_code!(errors::Vector{InvalidCodeError}, mi::Core.MethodInstance, - c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi)) +function validate_code!(errors::Vector{InvalidCodeError}, mi::Core.MethodInstance, c::Union{Nothing,CodeInfo}) is_top_level = mi.def isa Module if is_top_level mnargs = 0 diff --git a/base/expr.jl b/base/expr.jl index 46e89bf64da8a..5649303b41ef4 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -962,10 +962,7 @@ macro generated(f) Expr(:block, lno, Expr(:if, Expr(:generated), - # https://github.com/JuliaLang/julia/issues/25678 - Expr(:block, - :(local $tmp = $body), - :(if $tmp isa $(GlobalRef(Core, :CodeInfo)); return $tmp; else $tmp; end)), + body, Expr(:block, Expr(:meta, :generated_only), Expr(:return, nothing)))))) diff --git a/base/reflection.jl b/base/reflection.jl index 0e3d5e1fb82a7..5ac74e1c75bab 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -961,10 +961,11 @@ function code_lowered(@nospecialize(f), @nospecialize(t=Tuple); generated::Bool= if debuginfo !== :source && debuginfo !== :none throw(ArgumentError("'debuginfo' must be either :source or :none")) end - return map(method_instances(f, t)) do m + world = get_world_counter() + return map(method_instances(f, t, world)) do m if generated && hasgenerator(m) if may_invoke_generator(m) - return ccall(:jl_code_for_staged, Any, (Any,), m)::CodeInfo + return ccall(:jl_code_for_staged, Any, (Any, UInt), m, world)::CodeInfo else error("Could not expand generator for `@generated` method ", m, ". ", "This can happen if the provided argument types (", t, ") are ", @@ -1053,6 +1054,8 @@ methods(@nospecialize(f), @nospecialize(t), mod::Module) = methods(f, t, (mod,)) function methods_including_ambiguous(@nospecialize(f), @nospecialize(t)) tt = signature_type(f, t) world = get_world_counter() + (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && + error("code reflection cannot be used from generated functions") min = RefValue{UInt}(typemin(UInt)) max = RefValue{UInt}(typemax(UInt)) ms = _methods_by_ftype(tt, nothing, -1, world, true, min, max, Ptr{Int32}(C_NULL))::Vector @@ -1127,9 +1130,11 @@ _uncompressed_ir(ci::Core.CodeInstance, s::Array{UInt8,1}) = ccall(:jl_uncompres const uncompressed_ast = uncompressed_ir const _uncompressed_ast = _uncompressed_ir -function method_instances(@nospecialize(f), @nospecialize(t), world::UInt=get_world_counter()) +function method_instances(@nospecialize(f), @nospecialize(t), world::UInt) tt = signature_type(f, t) results = Core.MethodInstance[] + # this make a better error message than the typeassert that follows + world == typemax(UInt) && error("code reflection cannot be used from generated functions") for match in _methods_by_ftype(tt, -1, world)::Vector instance = Core.Compiler.specialize_method(match) push!(results, instance) @@ -1200,20 +1205,22 @@ function may_invoke_generator(method::Method, @nospecialize(atype), sparams::Sim # generator only has one method generator = method.generator isa(generator, Core.GeneratedFunctionStub) || return false - gen_mthds = methods(generator.gen)::MethodList - length(gen_mthds) == 1 || return false + gen_mthds = _methods_by_ftype(Tuple{typeof(generator.gen), Vararg{Any}}, 1, method.primary_world) + (gen_mthds isa Vector && length(gen_mthds) == 1) || return false - generator_method = first(gen_mthds) + generator_method = first(gen_mthds).method nsparams = length(sparams) isdefined(generator_method, :source) || return false code = generator_method.source nslots = ccall(:jl_ir_nslots, Int, (Any,), code) - at = unwrap_unionall(atype)::DataType + at = unwrap_unionall(atype) + at isa DataType || return false (nslots >= 1 + length(sparams) + length(at.parameters)) || return false + firstarg = 1 for i = 1:nsparams if isa(sparams[i], TypeVar) - if (ast_slotflag(code, 1 + i) & SLOT_USED) != 0 + if (ast_slotflag(code, firstarg + i) & SLOT_USED) != 0 return false end end @@ -1222,7 +1229,7 @@ function may_invoke_generator(method::Method, @nospecialize(atype), sparams::Sim non_va_args = method.isva ? nargs - 1 : nargs for i = 1:non_va_args if !isdispatchelem(at.parameters[i]) - if (ast_slotflag(code, 1 + i + nsparams) & SLOT_USED) != 0 + if (ast_slotflag(code, firstarg + i + nsparams) & SLOT_USED) != 0 return false end end @@ -1230,7 +1237,7 @@ function may_invoke_generator(method::Method, @nospecialize(atype), sparams::Sim if method.isva # If the va argument is used, we need to ensure that all arguments that # contribute to the va tuple are dispatchelemes - if (ast_slotflag(code, 1 + nargs + nsparams) & SLOT_USED) != 0 + if (ast_slotflag(code, firstarg + nargs + nsparams) & SLOT_USED) != 0 for i = (non_va_args+1):length(at.parameters) if !isdispatchelem(at.parameters[i]) return false @@ -1320,7 +1327,8 @@ function code_typed_by_type(@nospecialize(tt::Type); debuginfo::Symbol=:default, world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) - ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") + (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && + error("code reflection cannot be used from generated functions") if @isdefined(IRShow) debuginfo = IRShow.debuginfo(debuginfo) elseif debuginfo === :default @@ -1429,7 +1437,7 @@ function code_ircode_by_type( interp = Core.Compiler.NativeInterpreter(world), optimize_until::Union{Integer,AbstractString,Nothing} = nothing, ) - ccall(:jl_is_in_pure_context, Bool, ()) && + (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && error("code reflection cannot be used from generated functions") tt = to_tuple_type(tt) matches = _methods_by_ftype(tt, -1, world)::Vector @@ -1456,7 +1464,8 @@ end function return_types(@nospecialize(f), @nospecialize(types=default_tt(f)); world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) - ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") + (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && + error("code reflection cannot be used from generated functions") if isa(f, Core.OpaqueClosure) _, rt = only(code_typed_opaque_closure(f)) return Any[rt] @@ -1480,7 +1489,8 @@ end function infer_effects(@nospecialize(f), @nospecialize(types=default_tt(f)); world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) - ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") + (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && + error("code reflection cannot be used from generated functions") if isa(f, Core.Builtin) types = to_tuple_type(types) argtypes = Any[Core.Compiler.Const(f), types.parameters...] @@ -1556,6 +1566,7 @@ function _which(@nospecialize(tt::Type); method_table::Union{Nothing,Core.MethodTable,Core.Compiler.MethodTableView}=nothing, world::UInt=get_world_counter(), raise::Bool=true) + world == typemax(UInt) && error("code reflection cannot be used from generated functions") if method_table === nothing table = Core.Compiler.InternalMethodTable(world) elseif method_table isa Core.MethodTable diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index df6a2224c2a48..76b0cc97df5bf 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -685,10 +685,10 @@ A (usually temporary) container for holding lowered source code. A `UInt8` array of slot properties, represented as bit flags: - * 2 - assigned (only false if there are *no* assignment statements with this var on the left) - * 8 - const (currently unused for local variables) - * 16 - statically assigned once - * 32 - might be used before assigned. This flag is only valid after type inference. + * 0x02 - assigned (only false if there are *no* assignment statements with this var on the left) + * 0x08 - used (if there is any read or write of the slot) + * 0x10 - statically assigned once + * 0x20 - might be used before assigned. This flag is only valid after type inference. * `ssavaluetypes` diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 43f933aed28c7..76d8967133629 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -2023,7 +2023,7 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, siz if (src) jlrettype = src->rettype; else if (jl_is_method(mi->def.method)) { - src = mi->def.method->generator ? jl_code_for_staged(mi) : (jl_code_info_t*)mi->def.method->source; + src = mi->def.method->generator ? jl_code_for_staged(mi, world) : (jl_code_info_t*)mi->def.method->source; if (src && !jl_is_code_info(src) && jl_is_method(mi->def.method)) src = jl_uncompress_ir(mi->def.method, NULL, (jl_array_t*)src); } diff --git a/src/ast.c b/src/ast.c index cb03b7efb6eb7..3f3d6176d342e 100644 --- a/src/ast.c +++ b/src/ast.c @@ -1024,10 +1024,10 @@ static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule jl_value_t *result; JL_TRY { margs[0] = jl_toplevel_eval(*ctx, margs[0]); - jl_method_instance_t *mfunc = jl_method_lookup(margs, nargs, world); + jl_method_instance_t *mfunc = jl_method_lookup(margs, nargs, ct->world_age); JL_GC_PROMISE_ROOTED(mfunc); if (mfunc == NULL) { - jl_method_error(margs[0], &margs[1], nargs, world); + jl_method_error(margs[0], &margs[1], nargs, ct->world_age); // unreachable } *ctx = mfunc->def.method->module; diff --git a/src/gf.c b/src/gf.c index fe858d15af1cc..0b01f5e8e6ee2 100644 --- a/src/gf.c +++ b/src/gf.c @@ -27,6 +27,9 @@ extern "C" { JL_DLLEXPORT _Atomic(size_t) jl_world_counter = 1; // uses atomic acquire/release JL_DLLEXPORT size_t jl_get_world_counter(void) JL_NOTSAFEPOINT { + jl_task_t *ct = jl_current_task; + if (ct->ptls->in_pure_callback) + return ~(size_t)0; return jl_atomic_load_acquire(&jl_world_counter); } @@ -2392,7 +2395,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t // if that didn't work and compilation is off, try running in the interpreter if (compile_option == JL_OPTIONS_COMPILE_OFF || compile_option == JL_OPTIONS_COMPILE_MIN) { - jl_code_info_t *src = jl_code_for_interpreter(mi); + jl_code_info_t *src = jl_code_for_interpreter(mi, world); if (!jl_code_requires_compiler(src, 0)) { jl_code_instance_t *codeinst = jl_new_codeinst(mi, (jl_value_t*)jl_any_type, NULL, NULL, @@ -3235,6 +3238,8 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, int intersections, size_t world, int cache_result, size_t *min_valid, size_t *max_valid, int *ambig) { + if (world > jl_atomic_load_acquire(&jl_world_counter)) + return jl_nothing; // the future is not enumerable int has_ambiguity = 0; jl_value_t *unw = jl_unwrap_unionall((jl_value_t*)type); assert(jl_is_datatype(unw)); diff --git a/src/interpreter.c b/src/interpreter.c index 08cb87791c5a3..713887f234898 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -626,7 +626,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, // preparing method IR for interpreter -jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *mi) +jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *mi, size_t world) { jl_code_info_t *src = (jl_code_info_t*)jl_atomic_load_relaxed(&mi->uninferred); if (jl_is_method(mi->def.value)) { @@ -636,7 +636,7 @@ jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *mi) } else { assert(mi->def.method->generator); - src = jl_code_for_staged(mi); + src = jl_code_for_staged(mi, world); } } if (src && (jl_value_t*)src != jl_nothing) { @@ -659,7 +659,9 @@ jl_value_t *NOINLINE jl_fptr_interpret_call(jl_value_t *f, jl_value_t **args, ui { interpreter_state *s; jl_method_instance_t *mi = codeinst->def; - jl_code_info_t *src = jl_code_for_interpreter(mi); + jl_task_t *ct = jl_current_task; + size_t world = ct->world_age; + jl_code_info_t *src = jl_code_for_interpreter(mi, world); jl_array_t *stmts = src->code; assert(jl_typeis(stmts, jl_array_any_type)); unsigned nroots = jl_source_nslots(src) + jl_source_nssavalues(src) + 2; diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 5d19e67024f99..79e2ef1436704 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -514,7 +514,7 @@ void jl_generate_fptr_for_unspecialized_impl(jl_code_instance_t *unspec) // TODO: this is wrong assert(def->generator); // TODO: jl_code_for_staged can throw - src = jl_code_for_staged(unspec->def); + src = jl_code_for_staged(unspec->def, ~(size_t)0); } if (src && (jl_value_t*)src != jl_nothing) src = jl_uncompress_ir(def, NULL, (jl_array_t*)src); @@ -574,7 +574,7 @@ jl_value_t *jl_dump_method_asm_impl(jl_method_instance_t *mi, size_t world, if (jl_is_method(def)) { if (!src) { // TODO: jl_code_for_staged can throw - src = def->generator ? jl_code_for_staged(mi) : (jl_code_info_t*)def->source; + src = def->generator ? jl_code_for_staged(mi, world) : (jl_code_info_t*)def->source; } if (src && (jl_value_t*)src != jl_nothing) src = jl_uncompress_ir(mi->def.method, codeinst, (jl_array_t*)src); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 4a0407e019432..2a4e95ab1da86 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -382,13 +382,8 @@ `((meta generated (new (core GeneratedFunctionStub) ,gname - ,(cons 'list anames) - ,(if (null? sparams) - 'nothing - (cons 'list (map car sparams))) - ,(cadr loc) - (inert ,(caddr loc)) - (false)))))) + (call (core svec) ,@(map quotify anames)) + (call (core svec) ,@(map quotify names))))))) (list gf)) '())) (types (llist-types argl)) diff --git a/src/julia.h b/src/julia.h index 4bb34cf653381..2186ee346418d 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1498,7 +1498,7 @@ JL_DLLEXPORT jl_value_t *jl_generic_function_def(jl_sym_t *name, _Atomic(jl_value_t*) *bp, jl_binding_t *bnd); JL_DLLEXPORT jl_method_t *jl_method_def(jl_svec_t *argdata, jl_methtable_t *mt, jl_code_info_t *f, jl_module_t *module); -JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo); +JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo, size_t world); JL_DLLEXPORT jl_code_info_t *jl_copy_code_info(jl_code_info_t *src); JL_DLLEXPORT size_t jl_get_world_counter(void) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_box_bool(int8_t x) JL_NOTSAFEPOINT; diff --git a/src/julia_internal.h b/src/julia_internal.h index 0e5e3b6cdd6ef..223a245b5a7b1 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -621,7 +621,7 @@ jl_method_instance_t *jl_get_unspecialized(jl_method_t *def JL_PROPAGATES_ROOT); JL_DLLEXPORT void jl_compile_method_instance(jl_method_instance_t *mi, jl_tupletype_t *types, size_t world); JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types); -jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *lam JL_PROPAGATES_ROOT); +jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *lam JL_PROPAGATES_ROOT, size_t world); int jl_code_requires_compiler(jl_code_info_t *src, int include_force_compile); jl_code_info_t *jl_new_code_info_from_ir(jl_expr_t *ast); JL_DLLEXPORT jl_code_info_t *jl_new_code_info_uninit(void); diff --git a/src/method.c b/src/method.c index 02855e593ef7a..ee333bebccedf 100644 --- a/src/method.c +++ b/src/method.c @@ -518,21 +518,21 @@ void jl_add_function_name_to_lineinfo(jl_code_info_t *ci, jl_value_t *name) } // invoke (compiling if necessary) the jlcall function pointer for a method template -STATIC_INLINE jl_value_t *jl_call_staged(jl_method_t *def, jl_value_t *generator, jl_svec_t *sparam_vals, - jl_value_t **args, uint32_t nargs) +static jl_value_t *jl_call_staged(jl_method_t *def, jl_value_t *generator, + size_t world, jl_svec_t *sparam_vals, jl_value_t **args, uint32_t nargs) { size_t n_sparams = jl_svec_len(sparam_vals); jl_value_t **gargs; - size_t totargs = 1 + n_sparams + nargs + def->isva; + size_t totargs = 2 + n_sparams + def->nargs; JL_GC_PUSHARGS(gargs, totargs); - gargs[0] = generator; - memcpy(&gargs[1], jl_svec_data(sparam_vals), n_sparams * sizeof(void*)); - memcpy(&gargs[1 + n_sparams], args, nargs * sizeof(void*)); - if (def->isva) { - gargs[totargs-1] = jl_f_tuple(NULL, &gargs[1 + n_sparams + def->nargs - 1], nargs - (def->nargs - 1)); - gargs[1 + n_sparams + def->nargs - 1] = gargs[totargs - 1]; - } - jl_value_t *code = jl_apply(gargs, 1 + n_sparams + def->nargs); + gargs[0] = jl_box_ulong(world); + gargs[1] = jl_box_long(def->line); + gargs[1] = jl_new_struct(jl_linenumbernode_type, gargs[1], def->file); + memcpy(&gargs[2], jl_svec_data(sparam_vals), n_sparams * sizeof(void*)); + memcpy(&gargs[2 + n_sparams], args, (def->nargs - def->isva) * sizeof(void*)); + if (def->isva) + gargs[totargs - 1] = jl_f_tuple(NULL, &args[def->nargs - 1], nargs - def->nargs + 1); + jl_value_t *code = jl_apply_generic(generator, gargs, totargs); JL_GC_POP(); return code; } @@ -556,7 +556,7 @@ JL_DLLEXPORT jl_code_info_t *jl_expand_and_resolve(jl_value_t *ex, jl_module_t * // Return a newly allocated CodeInfo for the function signature // effectively described by the tuple (specTypes, env, Method) inside linfo -JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo) +JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo, size_t world) { jl_value_t *uninferred = jl_atomic_load_relaxed(&linfo->uninferred); if (uninferred) { @@ -580,13 +580,13 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo) JL_TRY { ct->ptls->in_pure_callback = 1; - // and the right world ct->world_age = def->primary_world; // invoke code generator jl_tupletype_t *ttdt = (jl_tupletype_t*)jl_unwrap_unionall(tt); - ex = jl_call_staged(def, generator, linfo->sparam_vals, jl_svec_data(ttdt->parameters), jl_nparams(ttdt)); + ex = jl_call_staged(def, generator, world, linfo->sparam_vals, jl_svec_data(ttdt->parameters), jl_nparams(ttdt)); + // do some post-processing if (jl_is_code_info(ex)) { func = (jl_code_info_t*)ex; jl_array_t *stmts = (jl_array_t*)func->code; @@ -603,7 +603,6 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo) jl_error("The function body AST defined by this @generated function is not pure. This likely means it contains a closure, a comprehension or a generator."); } } - jl_add_function_name_to_lineinfo(func, (jl_value_t*)def->name); // If this generated function has an opaque closure, cache it for @@ -743,20 +742,12 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) st = jl_nothing; } else if (nargs == 2 && jl_exprarg(st, 0) == (jl_value_t*)jl_generated_sym) { - m->generator = NULL; + if (m->generator != NULL) + jl_error("duplicate @generated function body"); jl_value_t *gexpr = jl_exprarg(st, 1); - if (jl_expr_nargs(gexpr) == 7) { - // expects (new (core GeneratedFunctionStub) funcname argnames sp line file expandearly) - jl_value_t *funcname = jl_exprarg(gexpr, 1); - assert(jl_is_symbol(funcname)); - if (jl_get_global(m->module, (jl_sym_t*)funcname) != NULL) { - m->generator = jl_toplevel_eval(m->module, gexpr); - jl_gc_wb(m, m->generator); - } - } - if (m->generator == NULL) { - jl_error("invalid @generated function; try placing it in global scope"); - } + // the frontend would put (new (core GeneratedFunctionStub) funcname argnames sp) here, for example + m->generator = jl_toplevel_eval(m->module, gexpr); + jl_gc_wb(m, m->generator); st = jl_nothing; } else if (nargs == 1 && jl_exprarg(st, 0) == (jl_value_t*)jl_generated_only_sym) { diff --git a/test/ambiguous.jl b/test/ambiguous.jl index e96954299b702..4ab779d76e6f0 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -357,7 +357,7 @@ f35983(::Type, ::Type) = 2 @test length(Base.methods(f35983, (Any, Any))) == 2 @test first(Base.methods(f35983, (Any, Any))).sig == Tuple{typeof(f35983), Type, Type} let ambig = Ref{Int32}(0) - ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, typemax(UInt), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, Base.get_world_counter(), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) @test ms isa Vector @test length(ms) == 1 @test ambig[] == 0 @@ -366,7 +366,7 @@ f35983(::Type{Int16}, ::Any) = 3 @test length(Base.methods_including_ambiguous(f35983, (Type, Type))) == 2 @test length(Base.methods(f35983, (Type, Type))) == 1 let ambig = Ref{Int32}(0) - ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, typemax(UInt), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + ms = Base._methods_by_ftype(Tuple{typeof(f35983), Type, Type}, nothing, -1, Base.get_world_counter(), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) @test ms isa Vector @test length(ms) == 2 @test ambig[] == 1 @@ -374,7 +374,7 @@ end struct B38280 <: Real; val; end let ambig = Ref{Int32}(0) - ms = Base._methods_by_ftype(Tuple{Type{B38280}, Any}, nothing, 1, typemax(UInt), false, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + ms = Base._methods_by_ftype(Tuple{Type{B38280}, Any}, nothing, 1, Base.get_world_counter(), false, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) @test ms isa Vector @test length(ms) == 1 @test ambig[] == 1 diff --git a/test/compiler/contextual.jl b/test/compiler/contextual.jl index 740e985e388df..9db7ae1aeaa5d 100644 --- a/test/compiler/contextual.jl +++ b/test/compiler/contextual.jl @@ -7,7 +7,7 @@ module MiniCassette # A minimal demonstration of the cassette mechanism. Doesn't support all the # fancy features, but sufficient to exercise this code path in the compiler. - using Core.Compiler: method_instances, retrieve_code_info, CodeInfo, + using Core.Compiler: retrieve_code_info, CodeInfo, MethodInstance, SSAValue, GotoNode, GotoIfNot, ReturnNode, SlotNumber, quoted, signature_type using Base: _methods_by_ftype @@ -69,24 +69,28 @@ module MiniCassette end end - function overdub_generator(self, c, f, args) + function overdub_generator(world::UInt, source, self, c, f, args) + @nospecialize if !Base.issingletontype(f) - return :(return f(args...)) + # (c, f, args..) -> f(args...) + code_info = :(return f(args...)) + return Core.GeneratedFunctionStub(identity, Core.svec(:overdub, :c, :f, :args), Core.svec())(world, source, code_info) end tt = Tuple{f, args...} - match = Base._which(tt; world=typemax(UInt)) + match = Base._which(tt; world) mi = Core.Compiler.specialize_method(match) # Unsupported in this mini-cassette @assert !mi.def.isva - code_info = retrieve_code_info(mi) + code_info = retrieve_code_info(mi, world) @assert isa(code_info, CodeInfo) code_info = copy(code_info) - if isdefined(code_info, :edges) - code_info.edges = MethodInstance[mi] - end + @assert code_info.edges === nothing + code_info.edges = MethodInstance[mi] transform!(code_info, length(args), match.sparams) - code_info + # TODO: this is mandatory: code_info.min_world = max(code_info.min_world, min_world[]) + # TODO: this is mandatory: code_info.max_world = min(code_info.max_world, max_world[]) + return code_info end @inline function overdub(c::Ctx, f::Union{Core.Builtin, Core.IntrinsicFunction}, args...) @@ -95,16 +99,7 @@ module MiniCassette @eval function overdub(c::Ctx, f, args...) $(Expr(:meta, :generated_only)) - $(Expr(:meta, - :generated, - Expr(:new, - Core.GeneratedFunctionStub, - :overdub_generator, - Any[:overdub, :ctx, :f, :args], - Any[], - @__LINE__, - QuoteNode(Symbol(@__FILE__)), - true))) + $(Expr(:meta, :generated, overdub_generator)) end end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index e466dc8a3739a..12fedf2792a61 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1788,9 +1788,17 @@ bar_22708(x) = f_22708(x) @test bar_22708(1) == "x" +struct EarlyGeneratedFunctionStub + stub::Core.GeneratedFunctionStub +end +(stub::EarlyGeneratedFunctionStub)(args...) = (@nospecialize; stub.stub(args...)) + # mechanism for spoofing work-limiting heuristics and early generator expansion (#24852) -function _generated_stub(gen::Symbol, args::Vector{Any}, params::Vector{Any}, line, file, expand_early) - stub = Expr(:new, Core.GeneratedFunctionStub, gen, args, params, line, file, expand_early) +function _generated_stub(gen::Symbol, args::Core.SimpleVector, params::Core.SimpleVector, expand_early::Bool) + stub = Expr(:new, Core.GeneratedFunctionStub, gen, args, params) + if expand_early + stub = Expr(:new, EarlyGeneratedFunctionStub, stub) + end return Expr(:meta, :generated, stub) end @@ -1799,10 +1807,21 @@ f24852_kernel2(x, y::Tuple) = f24852_kernel1(x, (y,)) f24852_kernel3(x, y::Tuple) = f24852_kernel2(x, (y,)) f24852_kernel(x, y::Number) = f24852_kernel3(x, (y,)) -function f24852_kernel_cinfo(fsig::Type) - world = typemax(UInt) # FIXME - match = Base._methods_by_ftype(fsig, -1, world)[1] - isdefined(match.method, :source) || return (nothing, :(f(x, y))) +function f24852_kernel_cinfo(world::UInt, source, fsig::Type) + matches = Base._methods_by_ftype(fsig, -1, world) + if matches === nothing || length(matches) != 1 + match = nothing + else + match = matches[1] + if !isdefined(match.method, :source) + match = nothing + end + end + if match === nothing + code_info = :(f(x, y)) + code_info = Core.GeneratedFunctionStub(identity, Core.svec(:self, :f, :x, :y), Core.svec(:X, :Y))(world, source, code_info) + return (nothing, code_info) + end code_info = Base.uncompressed_ir(match.method) Meta.partially_inline!(code_info.code, Any[], match.spec_types, Any[match.sparams...], 1, 0, :propagate) if startswith(String(match.method.name), "f24852") @@ -1817,21 +1836,23 @@ function f24852_kernel_cinfo(fsig::Type) end pushfirst!(code_info.slotnames, Symbol("#self#")) pushfirst!(code_info.slotflags, 0x00) + # TODO: this is mandatory: code_info.min_world = max(code_info.min_world, min_world[]) + # TODO: this is mandatory: code_info.max_world = min(code_info.max_world, max_world[]) return match.method, code_info end -function f24852_gen_cinfo_uninflated(X, Y, _, f, x, y) - _, code_info = f24852_kernel_cinfo(Tuple{f, x, y}) +function f24852_gen_cinfo_uninflated(world::UInt, source, X, Y, _, f, x, y) + _, code_info = f24852_kernel_cinfo(world, source, Tuple{f, x, y}) return code_info end -function f24852_gen_cinfo_inflated(X, Y, _, f, x, y) - method, code_info = f24852_kernel_cinfo(Tuple{f, x, y}) +function f24852_gen_cinfo_inflated(world::UInt, source, X, Y, _, f, x, y) + method, code_info = f24852_kernel_cinfo(world, source, Tuple{f, x, y}) code_info.method_for_inference_limit_heuristics = method return code_info end -function f24852_gen_expr(X, Y, _, f, x, y) # deparse f(x::X, y::Y) where {X, Y} +function f24852_gen_expr(X, Y, _, f, x, y) # deparse of f(x::X, y::Y) where {X, Y} if f === typeof(f24852_kernel) f2 = :f24852_kernel3 elseif f === typeof(f24852_kernel3) @@ -1848,20 +1869,8 @@ end @eval begin function f24852_late_expr(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_expr, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), false)) - $(Expr(:meta, :generated_only)) - #= no body =# - end - function f24852_late_inflated(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_cinfo_inflated, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), false)) - $(Expr(:meta, :generated_only)) - #= no body =# - end - function f24852_late_uninflated(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_cinfo_uninflated, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), false)) + $(_generated_stub(:f24852_gen_expr, Core.svec(:self, :f, :x, :y), + Core.svec(:X, :Y), false)) $(Expr(:meta, :generated_only)) #= no body =# end @@ -1869,20 +1878,18 @@ end @eval begin function f24852_early_expr(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_expr, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), true)) + $(_generated_stub(:f24852_gen_expr, Core.svec(:self, :f, :x, :y), + Core.svec(:X, :Y), true)) $(Expr(:meta, :generated_only)) #= no body =# end function f24852_early_inflated(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_cinfo_inflated, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), true)) + $(Expr(:meta, :generated, f24852_gen_cinfo_inflated)) $(Expr(:meta, :generated_only)) #= no body =# end function f24852_early_uninflated(f, x::X, y::Y) where {X, Y} - $(_generated_stub(:f24852_gen_cinfo_uninflated, Any[:self, :f, :x, :y], - Any[:X, :Y], @__LINE__, QuoteNode(Symbol(@__FILE__)), true)) + $(Expr(:meta, :generated, f24852_gen_cinfo_uninflated)) $(Expr(:meta, :generated_only)) #= no body =# end @@ -1893,10 +1900,6 @@ result = f24852_kernel(x, y) @test result === f24852_late_expr(f24852_kernel, x, y) @test Base.return_types(f24852_late_expr, typeof((f24852_kernel, x, y))) == Any[Any] -@test result === f24852_late_uninflated(f24852_kernel, x, y) -@test Base.return_types(f24852_late_uninflated, typeof((f24852_kernel, x, y))) == Any[Any] -@test result === f24852_late_uninflated(f24852_kernel, x, y) -@test Base.return_types(f24852_late_uninflated, typeof((f24852_kernel, x, y))) == Any[Any] @test result === f24852_early_expr(f24852_kernel, x, y) @test Base.return_types(f24852_early_expr, typeof((f24852_kernel, x, y))) == Any[Any] @@ -1904,7 +1907,6 @@ result = f24852_kernel(x, y) @test Base.return_types(f24852_early_uninflated, typeof((f24852_kernel, x, y))) == Any[Any] @test result === @inferred f24852_early_inflated(f24852_kernel, x, y) @test Base.return_types(f24852_early_inflated, typeof((f24852_kernel, x, y))) == Any[Float64] - # TODO: test that `expand_early = true` + inflated `method_for_inference_limit_heuristics` # can be used to tighten up some inference result. diff --git a/test/compiler/validation.jl b/test/compiler/validation.jl index c25aae71ab157..5fd074fee73ae 100644 --- a/test/compiler/validation.jl +++ b/test/compiler/validation.jl @@ -22,10 +22,9 @@ msig = Tuple{typeof(f22938),Int,Int,Int,Int} world = Base.get_world_counter() match = only(Base._methods_by_ftype(msig, -1, world)) mi = Core.Compiler.specialize_method(match) -c0 = Core.Compiler.retrieve_code_info(mi) +c0 = Core.Compiler.retrieve_code_info(mi, world) -@test isempty(Core.Compiler.validate_code(mi)) -@test isempty(Core.Compiler.validate_code(c0)) +@test isempty(Core.Compiler.validate_code(mi, c0)) @testset "INVALID_EXPR_HEAD" begin c = copy(c0) @@ -116,7 +115,7 @@ end @testset "SIGNATURE_NARGS_MISMATCH" begin old_sig = mi.def.sig mi.def.sig = Tuple{1,2} - errors = Core.Compiler.validate_code(mi) + errors = Core.Compiler.validate_code(mi, nothing) mi.def.sig = old_sig @test length(errors) == 1 @test errors[1].kind === Core.Compiler.SIGNATURE_NARGS_MISMATCH @@ -132,7 +131,7 @@ end @testset "SLOTNAMES_NARGS_MISMATCH" begin mi.def.nargs += 20 - errors = Core.Compiler.validate_code(mi) + errors = Core.Compiler.validate_code(mi, c0) mi.def.nargs -= 20 @test length(errors) == 2 @test count(e.kind === Core.Compiler.SLOTNAMES_NARGS_MISMATCH for e in errors) == 1 diff --git a/test/core.jl b/test/core.jl index 83ede23b9850e..8f8b8c1a28bcd 100644 --- a/test/core.jl +++ b/test/core.jl @@ -3883,7 +3883,8 @@ PossiblyInvalidUnion{T} = Union{T,Int} # issue #13007 call13007(::Type{Array{T,N}}) where {T,N} = 0 call13007(::Type{Array}) = 1 -@test length(Base._methods(call13007, Tuple{Type{x} where x<:Array}, 4, typemax(UInt))) == 2 +@test Base._methods(call13007, Tuple{Type{x} where x<:Array}, 4, typemax(UInt)) === nothing +@test length(Base._methods(call13007, Tuple{Type{x} where x<:Array}, 4, Base.get_world_counter())) == 2 # detecting cycles during type intersection, e.g. #1631 cycle_in_solve_tvar_constraints(::Type{Some{S}}, x::S) where {S} = 0 diff --git a/test/opaque_closure.jl b/test/opaque_closure.jl index 7db55ff7f0615..6bd86f7f3a4d5 100644 --- a/test/opaque_closure.jl +++ b/test/opaque_closure.jl @@ -180,28 +180,23 @@ mk_va_opaque() = @opaque (x...)->x @test repr(@opaque x->1) == "(::Any)::Any->◌" # Opaque closure in CodeInfo returned from generated functions -function mk_ocg(args...) - ci = @code_lowered const_int() - cig = Meta.lower(@__MODULE__, Expr(:new_opaque_closure, Tuple{}, Any, Any, - Expr(:opaque_closure_method, nothing, 0, false, lno, ci))).args[1] - cig.slotnames = Symbol[Symbol("#self#")] - cig.slottypes = Any[Any] - cig.slotflags = UInt8[0x00] - cig +let ci = @code_lowered const_int() + global function mk_ocg(world::UInt, source, args...) + @nospecialize + cig = Meta.lower(@__MODULE__, Expr(:new_opaque_closure, Tuple{}, Any, Any, + Expr(:opaque_closure_method, nothing, 0, false, lno, ci))).args[1] + cig.slotnames = Symbol[Symbol("#self#")] + cig.slottypes = Any[Any] + cig.slotflags = UInt8[0x00] + @assert cig.min_world == UInt(1) + @assert cig.max_world == typemax(UInt) + return cig + end end @eval function oc_trivial_generated() $(Expr(:meta, :generated_only)) - $(Expr(:meta, - :generated, - Expr(:new, - Core.GeneratedFunctionStub, - :mk_ocg, - Any[:oc_trivial_generated], - Any[], - @__LINE__, - QuoteNode(Symbol(@__FILE__)), - true))) + $(Expr(:meta, :generated, mk_ocg)) end @test isa(oc_trivial_generated(), OpaqueClosure{Tuple{}, Any}) @test oc_trivial_generated()() == 1 diff --git a/test/reflection.jl b/test/reflection.jl index e926cdf0a2b9f..95965bf1725a7 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -648,7 +648,7 @@ let world = Core.Compiler.get_world_counter() match = Base._methods_by_ftype(T22979, -1, world)[1] instance = Core.Compiler.specialize_method(match) - cinfo_generated = Core.Compiler.get_staged(instance) + cinfo_generated = Core.Compiler.get_staged(instance, world) @test_throws ErrorException Base.uncompressed_ir(match.method) test_similar_codeinfo(code_lowered(f22979, typeof(x22979))[1], cinfo_generated) diff --git a/test/staged.jl b/test/staged.jl index 4a7fa3d7f4c84..0fa8ecb182cff 100644 --- a/test/staged.jl +++ b/test/staged.jl @@ -196,12 +196,11 @@ let gf_err2 return nothing end Expected = ErrorException("code reflection cannot be used from generated functions") + @test_throws Expected gf_err2(code_lowered) @test_throws Expected gf_err2(code_typed) @test_throws Expected gf_err2(code_llvm) @test_throws Expected gf_err2(code_native) - @test gf_err_ref[] == 66 - @test gf_err2(code_lowered) === nothing - @test gf_err_ref[] == 1077 + @test gf_err_ref[] == 88 end # issue #15043 @@ -246,12 +245,18 @@ f22440kernel(x::AbstractFloat) = x * x f22440kernel(::Type{T}) where {T} = one(T) f22440kernel(::Type{T}) where {T<:AbstractFloat} = zero(T) -@generated function f22440(y) - match = Base._methods_by_ftype(Tuple{typeof(f22440kernel),y}, -1, typemax(UInt))[1] +function f22440_gen(world::UInt, source, _, y) + match = only(Base._methods_by_ftype(Tuple{typeof(f22440kernel),y}, -1, world)) code_info = Base.uncompressed_ir(match.method) Meta.partially_inline!(code_info.code, Any[], match.spec_types, Any[match.sparams...], 0, 0, :propagate) + # TODO: this is mandatory: code_info.min_world = max(code_info.min_world, min_world[]) + # TODO: this is mandatory: code_info.max_world = min(code_info.max_world, max_world[]) return code_info end +@eval function f22440(y) + $(Expr(:meta, :generated, f22440_gen)) + $(Expr(:meta, :generated_only)) +end @test f22440(Int) === f22440kernel(Int) @test f22440(Float64) === f22440kernel(Float64) @@ -309,26 +314,33 @@ end # https://github.com/JuliaDebug/CassetteOverlay.jl/issues/12 # generated function with varargs and unfortunately placed unused slot @generated function f_vararg_generated(args...) + local unusedslot4 + local unusedslot5 + local unusedslot6 :($args) end g_vararg_generated() = f_vararg_generated((;), (;), Base.inferencebarrier((;))) let tup = g_vararg_generated() @test all(==(typeof((;))), tup) - # This is just to make sure that the test is actually testing what we want - - # the test only works if there's an unused that matches the position of the - # inferencebarrier argument above (N.B. the generator function itself + # This is just to make sure that the test is actually testing what we want: + # the test only works if there is an unused that matches the position of + # the inferencebarrier argument above (N.B. the generator function itself # shifts everything over by 1) - @test only(code_lowered(only(methods(f_vararg_generated)).generator.gen)).slotflags[5] == UInt8(0x00) + @test_broken only(code_lowered(only(methods(f_vararg_generated)).generator.gen)).slotflags[5] == 0x00 end # respect a given linetable in code generation # https://github.com/JuliaLang/julia/pull/47750 -let match = Base._which(Tuple{typeof(sin),Int}) +let world = Base.get_world_counter() + match = Base._which(Tuple{typeof(sin), Int}; world) mi = Core.Compiler.specialize_method(match) - lwr = Core.Compiler.retrieve_code_info(mi) - @test all(lin->lin.method===:sin, lwr.linetable) - @generated sin_generated(a) = lwr + lwr = Core.Compiler.retrieve_code_info(mi, world) + @test all(lin->lin.method === :sin, lwr.linetable) + @eval function sin_generated(a) + $(Expr(:meta, :generated, Returns(lwr))) + $(Expr(:meta, :generated_only)) + end src = only(code_lowered(sin_generated, (Int,))) - @test all(lin->lin.method===:sin, src.linetable) + @test all(lin->lin.method === :sin, src.linetable) @test sin_generated(42) == sin(42) end diff --git a/test/syntax.jl b/test/syntax.jl index 756af45e6b3c7..e60bbd81a04ab 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3053,9 +3053,6 @@ end end # issue 25678 -@generated f25678(x::T) where {T} = code_lowered(sin, Tuple{x})[] -@test f25678(pi/6) === sin(pi/6) - @generated g25678(x) = return :x @test g25678(7) === 7 From 685da8f1fddd1503500a027a782f3bfa57371d66 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 24 Mar 2023 10:46:55 -0400 Subject: [PATCH 370/775] fix malloc-stack CI failure (#49082) We see Windows CI fail often here, and we don't actually need this memory--only the first thread should initialize it. (indeed, we were temporarily corrupting these data-structures in the process by reinitializing them, though that was not typically detectable.) From worker 7: Exception: EXCEPTION_ACCESS_VIOLATION at 0x7ffa26726e87 -- jl_makecontext at C:/workdir/src\win32_ucontext.c:67 From worker 7: in expression starting at C:\buildkite-agent\builds\win2k22-amdci6-6\julialang\julia-master\julia-ceffaee345\share\julia\test\ccall.jl:1066 From worker 7: jl_makecontext at C:/workdir/src\win32_ucontext.c:67 From worker 7: jl_install_thread_signal_handler at C:/workdir/src\signals-win.c:490 From worker 7: jl_init_root_task at C:/workdir/src\task.c:1568 From worker 7: ijl_adopt_thread at C:/workdir/src\threading.c:414 From worker 7: unknown function (ip: 000001d791a58969) From worker 7: uv__queue_work at /workspace/srcdir/libuv\src\threadpool.c:305 From worker 7: worker at /workspace/srcdir/libuv\src\threadpool.c:122 From worker 7: uv__thread_start at /workspace/srcdir/libuv\src/win\thread.c:111 From worker 7: beginthreadex at C:\Windows\System32\msvcrt.dll (unknown line) From worker 7: endthreadex at C:\Windows\System32\msvcrt.dll (unknown line) From worker 7: BaseThreadInitThunk at C:\Windows\System32\KERNEL32.DLL (unknown line) From worker 7: RtlUserThreadStart at C:\Windows\SYSTEM32\ntdll.dll (unknown line) From worker 7: Allocations: 352796158 (Pool: 352389694; Big: 406464); GC: 722 --- src/gc-stacks.c | 4 +++- src/signals-unix.c | 2 +- src/signals-win.c | 18 +++++++++++------- src/task.c | 13 ++++++++----- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/gc-stacks.c b/src/gc-stacks.c index 40292cf472037..b35c1722c82ff 100644 --- a/src/gc-stacks.c +++ b/src/gc-stacks.c @@ -165,9 +165,11 @@ JL_DLLEXPORT void *jl_malloc_stack(size_t *bufsz, jl_task_t *owner) JL_NOTSAFEPO ssize = LLT_ALIGN(ssize, jl_page_size); } if (stk == NULL) { - if (jl_atomic_load_relaxed(&num_stack_mappings) >= MAX_STACK_MAPPINGS) + if (jl_atomic_load_relaxed(&num_stack_mappings) >= MAX_STACK_MAPPINGS) { // we accept that this can go over by as much as nthreads since it's not a CAS + errno = ENOMEM; return NULL; + } // TODO: allocate blocks of stacks? but need to mprotect individually anyways stk = malloc_stack(ssize); if (stk == MAP_FAILED) diff --git a/src/signals-unix.c b/src/signals-unix.c index c35fe0079f1a0..79300567b4bce 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -621,7 +621,7 @@ static void allocate_segv_handler(void) static void *alloc_sigstack(size_t *ssize) { void *stk = jl_malloc_stack(ssize, NULL); - if (stk == MAP_FAILED) + if (stk == NULL) jl_errorf("fatal error allocating signal stack: mmap: %s", strerror(errno)); return stk; } diff --git a/src/signals-win.c b/src/signals-win.c index f20a4d5287669..5dd6b34558ca6 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -483,11 +483,15 @@ void jl_install_default_signal_handlers(void) void jl_install_thread_signal_handler(jl_ptls_t ptls) { - size_t ssize = sig_stack_size; - void *stk = jl_malloc_stack(&ssize, NULL); - collect_backtrace_fiber.uc_stack.ss_sp = (void*)stk; - collect_backtrace_fiber.uc_stack.ss_size = ssize; - jl_makecontext(&collect_backtrace_fiber, start_backtrace_fiber); - uv_mutex_init(&backtrace_lock); - have_backtrace_fiber = 1; + if (!have_backtrace_fiber) { + size_t ssize = sig_stack_size; + void *stk = jl_malloc_stack(&ssize, NULL); + if (stk == NULL) + jl_errorf("fatal error allocating signal stack: mmap: %s", strerror(errno)); + collect_backtrace_fiber.uc_stack.ss_sp = (void*)stk; + collect_backtrace_fiber.uc_stack.ss_size = ssize; + jl_makecontext(&collect_backtrace_fiber, start_backtrace_fiber); + uv_mutex_init(&backtrace_lock); + have_backtrace_fiber = 1; + } } diff --git a/src/task.c b/src/task.c index 37dc9372cf705..7102cdec35d08 100644 --- a/src/task.c +++ b/src/task.c @@ -1552,12 +1552,15 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) #endif if (jl_setjmp(ptls->copy_stack_ctx.uc_mcontext, 0)) start_task(); // sanitizer_finish_switch_fiber is part of start_task - return ct; } - ssize = JL_STACK_SIZE; - char *stkbuf = jl_alloc_fiber(&ptls->base_ctx, &ssize, NULL); - ptls->stackbase = stkbuf + ssize; - ptls->stacksize = ssize; + else { + ssize = JL_STACK_SIZE; + char *stkbuf = jl_alloc_fiber(&ptls->base_ctx, &ssize, NULL); + if (stkbuf != NULL) { + ptls->stackbase = stkbuf + ssize; + ptls->stacksize = ssize; + } + } #endif if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON) From 60e7d18403c76e8b7144155c08e6b5b0d7d5ef11 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 24 Mar 2023 11:08:00 -0400 Subject: [PATCH 371/775] restore support for cross-compile builds (#49081) Defaults to gcc after autodetection. Regression caused by ee6115bbb2c. --- Make.inc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Make.inc b/Make.inc index 11f5fc7a88ce4..bf71e6ee3a21e 100644 --- a/Make.inc +++ b/Make.inc @@ -452,7 +452,11 @@ endif # Compiler specific stuff -CC_VERSION_STRING = $(shell $(CC) --version) +CC := $(CROSS_COMPILE)$(CC) # attempt to add cross-compiler prefix, if the user + # is not overriding the default, to form target-triple-cc (which + # may not exist), and use that to decide what compiler the user + # is using for the target build (or default to gcc) +CC_VERSION_STRING = $(shell $(CC) --version 2>/dev/null) ifneq (,$(findstring clang,$(CC_VERSION_STRING))) USECLANG := 1 USEGCC := 0 From b9a334fec1098e9bae2067768ea43c8006dc3b9c Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 24 Mar 2023 11:08:26 -0400 Subject: [PATCH 372/775] small cleanup to loading code (#49078) --- base/loading.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index ed6f170a55575..4c5a7df320dbe 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1218,7 +1218,7 @@ function run_extension_callbacks() # below the one of the parent. This will cause a load failure when the # pkg ext tries to load the triggers. Therefore, check this first # before loading the pkg ext. - pkgenv = Base.identify_package_env(extid.id, pkgid.name) + pkgenv = identify_package_env(extid.id, pkgid.name) ext_not_allowed_load = false if pkgenv === nothing ext_not_allowed_load = true @@ -2245,7 +2245,8 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in @static if Sys.isapple() rm(ocachefile_from_cachefile(evicted_cachefile) * ".dSYM"; force=true, recursive=true) end - catch + catch e + e isa IOError || rethrow() end end end From 7d320993d9bf03a588dda5834246363e477b9449 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 24 Mar 2023 11:45:23 -0400 Subject: [PATCH 373/775] Quieter tests (#49093) --- test/ambiguous.jl | 9 +++++---- test/core.jl | 4 ++-- test/deprecation_exec.jl | 4 ++-- test/syntax.jl | 2 +- test/threads_exec.jl | 8 ++++++-- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 4ab779d76e6f0..67fb16d3b7458 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -46,8 +46,8 @@ let err = try @test occursin("Possible fix, define\n ambig(::Integer, ::Integer)", errstr) end -ambig_with_bounds(x, ::Int, ::T) where {T<:Integer,S} = 0 -ambig_with_bounds(::Int, x, ::T) where {T<:Integer,S} = 1 +@test_warn "declares type variable S but does not use it" @eval ambig_with_bounds(x, ::Int, ::T) where {T<:Integer,S} = 0 +@test_warn "declares type variable S but does not use it" @eval ambig_with_bounds(::Int, x, ::T) where {T<:Integer,S} = 1 let err = try ambig_with_bounds(1, 2, 3) catch _e_ @@ -385,7 +385,7 @@ f11407(::Dict{K,V}, ::Dict{Any,V}) where {K,V} = 1 f11407(::Dict{K,V}, ::Dict{K,Any}) where {K,V} = 2 @test_throws MethodError f11407(Dict{Any,Any}(), Dict{Any,Any}()) # ambiguous @test f11407(Dict{Any,Int}(), Dict{Any,Int}()) == 1 -f11407(::Dict{Any,Any}, ::Dict{Any,Any}) where {K,V} = 3 +@test_warn "declares type variable V but does not use it" @eval f11407(::Dict{Any,Any}, ::Dict{Any,Any}) where {K,V} = 3 @test f11407(Dict{Any,Any}(), Dict{Any,Any}()) == 3 # issue #12814 @@ -399,8 +399,9 @@ end # issue #43040 module M43040 + using Test struct C end - stripType(::Type{C}) where {T} = C # where {T} is intentionally incorrect + @test_warn "declares type variable T but does not use it" @eval M43040 stripType(::Type{C}) where {T} = C # where {T} is intentionally incorrect end @test isempty(detect_ambiguities(M43040; recursive=true)) diff --git a/test/core.jl b/test/core.jl index 8f8b8c1a28bcd..7b989cfd0759d 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5268,10 +5268,10 @@ end GC.enable(true) # issue #18710 -bad_tvars() where {T} = 1 +@test_warn "declares type variable T but does not use it" @eval bad_tvars() where {T} = 1 @test isa(which(bad_tvars, ()), Method) @test bad_tvars() === 1 -bad_tvars2() where {T} = T +@test_warn "declares type variable T but does not use it" @eval bad_tvars2() where {T} = T @test_throws UndefVarError(:T) bad_tvars2() missing_tvar(::T...) where {T} = T @test_throws UndefVarError(:T) missing_tvar() diff --git a/test/deprecation_exec.jl b/test/deprecation_exec.jl index 4f19f9415ba29..5b465e05f0a12 100644 --- a/test/deprecation_exec.jl +++ b/test/deprecation_exec.jl @@ -116,8 +116,8 @@ begin # @deprecate # test that positional and keyword arguments are forwarded when # there is no explicit type annotation - @test DeprecationTests.old_return_args(1, 2, 3) == ((1, 2, 3),(;)) - @test DeprecationTests.old_return_args(1, 2, 3; a = 4, b = 5) == ((1, 2, 3), (a = 4, b = 5)) + @test_logs (:warn,) @test DeprecationTests.old_return_args(1, 2, 3) == ((1, 2, 3),(;)) + @test_logs (:warn,) @test DeprecationTests.old_return_args(1, 2, 3; a = 4, b = 5) == ((1, 2, 3), (a = 4, b = 5)) end f24658() = depwarn24658() diff --git a/test/syntax.jl b/test/syntax.jl index e60bbd81a04ab..fac3479315c03 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -2007,7 +2007,7 @@ end @test Meta.parse("import Base.Foo.:(==).bar") == :(import Base.Foo.==.bar) # issue #33135 -function f33135(x::T) where {C1, T} +@test_warn "declares type variable C1 but does not use it" @eval function f33135(x::T) where {C1, T} let C1 = 1, C2 = 2 C1 end diff --git a/test/threads_exec.jl b/test/threads_exec.jl index 68ba9377cf955..e8a81f7fc2683 100644 --- a/test/threads_exec.jl +++ b/test/threads_exec.jl @@ -27,6 +27,8 @@ end # (expected test duration is about 18-180 seconds) Timer(t -> killjob("KILLING BY THREAD TEST WATCHDOG\n"), 1200) +@testset """threads_exec.jl with JULIA_NUM_THREADS == $(ENV["JULIA_NUM_THREADS"])""" begin + @test Threads.threadid() == 1 @test 1 <= threadpoolsize() <= Threads.maxthreadid() @@ -232,7 +234,7 @@ end # Make sure that eval'ing in a different module doesn't mess up other threads orig_curmodule14726 = @__MODULE__ main_var14726 = 1 -module M14726 +@eval Main module M14726 module_var14726 = 1 end @@ -252,7 +254,7 @@ end @test @__MODULE__() == orig_curmodule14726 end -module M14726_2 +@eval Main module M14726_2 using Test using Base.Threads @threads for i in 1:100 @@ -1067,3 +1069,5 @@ end popfirst!(LOAD_PATH) end end + +end # main testset From cac267c102dd79cc73285d3f63556b0f18051a83 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Fri, 24 Mar 2023 12:04:03 -0400 Subject: [PATCH 374/775] Add pkgimage.mk to auto-parallelize pkgimage caching for stdlibs (#48069) --- Makefile | 12 ++-- contrib/cache_stdlibs.jl | 53 ---------------- pkgimage.mk | 122 ++++++++++++++++++++++++++++++++++++ stdlib/.gitignore | 1 + stdlib/LLD_jll/Project.toml | 2 - 5 files changed, 128 insertions(+), 62 deletions(-) delete mode 100644 contrib/cache_stdlibs.jl create mode 100644 pkgimage.mk diff --git a/Makefile b/Makefile index 84f08f47761ca..c6c1717732056 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ all: debug release DIRS := $(sort $(build_bindir) $(build_depsbindir) $(build_libdir) $(build_private_libdir) $(build_libexecdir) $(build_includedir) $(build_includedir)/julia $(build_sysconfdir)/julia $(build_datarootdir)/julia $(build_datarootdir)/julia/stdlib $(build_man1dir)) ifneq ($(BUILDROOT),$(JULIAHOME)) BUILDDIRS := $(BUILDROOT) $(addprefix $(BUILDROOT)/,base src src/flisp src/support src/clangsa cli doc deps stdlib test test/clangsa test/embedding test/gcext test/llvmpasses) -BUILDDIRMAKE := $(addsuffix /Makefile,$(BUILDDIRS)) $(BUILDROOT)/sysimage.mk +BUILDDIRMAKE := $(addsuffix /Makefile,$(BUILDDIRS)) $(BUILDROOT)/sysimage.mk $(BUILDROOT)/pkgimage.mk DIRS += $(BUILDDIRS) $(BUILDDIRMAKE): | $(BUILDDIRS) @# add Makefiles to the build directories for convenience (pointing back to the source location of each) @@ -104,7 +104,10 @@ julia-sysimg-release julia-sysimg-debug : julia-sysimg-% : julia-sysimg-ji julia julia-debug julia-release : julia-% : julia-sysimg-% julia-src-% julia-symlink julia-libccalltest julia-libllvmcalltest julia-base-cache -debug release : % : julia-% +stdlibs-cache-release stdlibs-cache-debug : stdlibs-cache-% : julia-% + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT) -f pkgimage.mk all-$* + +debug release : % : julia-% stdlibs-cache-% docs: julia-sysimg-$(JULIA_BUILD_MODE) @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/doc JULIA_EXECUTABLE='$(call spawn,$(JULIA_EXECUTABLE_$(JULIA_BUILD_MODE))) --startup-file=no' @@ -349,11 +352,6 @@ else ifeq ($(JULIA_BUILD_MODE),debug) $(INSTALL_M) $(build_private_libdir)/sys-debug.$(SHLIB_EXT) $(DESTDIR)$(private_libdir) endif - # Cache stdlibs - @$(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no $(JULIAHOME)/contrib/cache_stdlibs.jl) - # CI uses `--check-bounds=yes` which impacts the cache flags - @$(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes $(JULIAHOME)/contrib/cache_stdlibs.jl) - # Copy in all .jl sources as well mkdir -p $(DESTDIR)$(datarootdir)/julia/base $(DESTDIR)$(datarootdir)/julia/test cp -R -L $(JULIAHOME)/base/* $(DESTDIR)$(datarootdir)/julia/base diff --git a/contrib/cache_stdlibs.jl b/contrib/cache_stdlibs.jl deleted file mode 100644 index 37f002e043dde..0000000000000 --- a/contrib/cache_stdlibs.jl +++ /dev/null @@ -1,53 +0,0 @@ -# Stdlibs sorted in dependency, then alphabetical, order by contrib/print_sorted_stdlibs.jl -# Run with the `--exclude-sysimage` option to filter out all packages included in the system image -stdlibs = [ - # No dependencies - - # 1-depth packages - :GMP_jll, - :LLVMLibUnwind_jll, - :LibUV_jll, - :LibUnwind_jll, - :MbedTLS_jll, - :OpenLibm_jll, - :PCRE2_jll, - :Zlib_jll, - :dSFMT_jll, - :libLLVM_jll, - :DelimitedFiles, - - # 2-depth packages - :LibSSH2_jll, - :MPFR_jll, - - # 3-depth packages - :LibGit2_jll, - - # 4-depth packages - :SparseArrays, - - # 7-depth packages - :LLD_jll, - :SuiteSparse, - - # 9-depth packages - :Statistics, - :SuiteSparse_jll, -] - -depot = abspath(Sys.BINDIR, "..", "share", "julia") - -if haskey(ENV, "JULIA_CPU_TARGET") - target = ENV["JULIA_CPU_TARGET"] -else - target = "native" -end - -@info "Caching stdlibrary to" depot target -empty!(Base.DEPOT_PATH) -push!(Base.DEPOT_PATH, depot) - -for pkg in stdlibs - pkgid = Base.identify_package(string(pkg)) - Base.compilecache(pkgid) -end diff --git a/pkgimage.mk b/pkgimage.mk new file mode 100644 index 0000000000000..aa6e6d1c266ce --- /dev/null +++ b/pkgimage.mk @@ -0,0 +1,122 @@ +SRCDIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +BUILDDIR := . +JULIAHOME := $(SRCDIR) +include $(JULIAHOME)/Make.inc + +VERSDIR := v$(shell cut -d. -f1-2 < $(JULIAHOME)/VERSION) + +JULIA_DEPOT_PATH := $(build_prefix)/share/julia + +$(JULIA_DEPOT_PATH): + mkdir -p $@ + +STDLIBS := ArgTools Artifacts Base64 CRC32c FileWatching Libdl NetworkOptions SHA Serialization \ + GMP_jll LLVMLibUnwind_jll LibUV_jll LibUnwind_jll MbedTLS_jll OpenLibm_jll PCRE2_jll \ + Zlib_jll dSFMT_jll libLLVM_jll libblastrampoline_jll OpenBLAS_jll Printf Random Tar \ + LibSSH2_jll MPFR_jll LinearAlgebra Dates Distributed Future LibGit2 Profile SparseArrays UUIDs \ + SharedArrays TOML Test LibCURL Downloads Pkg Dates LazyArtifacts Sockets Unicode Markdown \ + InteractiveUtils REPL DelimitedFiles + +all-release: $(addprefix cache-release-, $(STDLIBS)) +all-debug: $(addprefix cache-debug-, $(STDLIBS)) + +define pkgimg_builder +$1_SRCS := $$(shell find $$(build_datarootdir)/julia/stdlib/$$(VERSDIR)/$1/src -name \*.jl) \ + $$(wildcard $$(build_prefix)/manifest/$$(VERSDIR)/$1) +$$(BUILDDIR)/stdlib/$1.release.image: $$($1_SRCS) $$(addsuffix .release.image,$$(addprefix $$(BUILDDIR)/stdlib/,$2)) + @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.compilecache(Base.identify_package("$1"))') + @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes -e 'Base.compilecache(Base.identify_package("$1"))') + touch $$@ +cache-release-$1: $$(BUILDDIR)/stdlib/$1.release.image +$$(BUILDDIR)/stdlib/$1.debug.image: $$($1_SRCS) $$(addsuffix .debug.image,$$(addprefix $$(BUILDDIR)/stdlib/,$2)) + @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.compilecache(Base.identify_package("$1"))') + @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes -e 'Base.compilecache(Base.identify_package("$1"))') +cache-debug-$1: $$(BUILDDIR)/stdlib/$1.debug.image +.SECONDARY: $$(BUILDDIR)/stdlib/$1.release.image $$(BUILDDIR)/stdlib/$1.debug.image +endef + +# Used to just define them in the dependency graph +# reside in the system image +define sysimg_builder +$$(BUILDDIR)/stdlib/$1.release.image: + touch $$@ +cache-release-$1: $$(BUILDDIR)/stdlib/$1.release.image +$$(BUILDDIR)/stdlib/$1.debug.image: + touch $$@ +cache-debug-$1: $$(BUILDDIR)/stdlib/$1.debug.image +.SECONDARY: $$(BUILDDIR)/stdlib/$1.release.image $$(BUILDDIR)/stdlib/$1.debug.image +endef + +# no dependencies +$(eval $(call pkgimg_builder,MozillaCACerts_jll,)) +$(eval $(call sysimg_builder,ArgTools,)) +$(eval $(call sysimg_builder,Artifacts,)) +$(eval $(call sysimg_builder,Base64,)) +$(eval $(call sysimg_builder,CRC32c,)) +$(eval $(call sysimg_builder,FileWatching,)) +$(eval $(call sysimg_builder,Libdl,)) +$(eval $(call sysimg_builder,Logging,)) +$(eval $(call sysimg_builder,Mmap,)) +$(eval $(call sysimg_builder,NetworkOptions,)) +$(eval $(call sysimg_builder,SHA,)) +$(eval $(call sysimg_builder,Serialization,)) +$(eval $(call sysimg_builder,Sockets,)) +$(eval $(call sysimg_builder,Unicode,)) + +# 1-depth packages +$(eval $(call pkgimg_builder,GMP_jll,Artifacts Libdl)) +$(eval $(call pkgimg_builder,LLVMLibUnwind_jll,Artifacts Libdl)) +$(eval $(call pkgimg_builder,LibUV_jll,Artifacts Libdl)) +$(eval $(call pkgimg_builder,LibUnwind_jll,Artifacts Libdl)) +$(eval $(call pkgimg_builder,MbedTLS_jll,Artifacts Libdl)) +$(eval $(call pkgimg_builder,nghttp2_jll,Artifacts Libdl)) +$(eval $(call pkgimg_builder,OpenLibm_jll,Artifacts Libdl)) +$(eval $(call pkgimg_builder,PCRE2_jll,Artifacts Libdl)) +$(eval $(call pkgimg_builder,Zlib_jll,Artifacts Libdl)) +$(eval $(call pkgimg_builder,dSFMT_jll,Artifacts Libdl)) +$(eval $(call pkgimg_builder,libLLVM_jll,Artifacts Libdl)) +$(eval $(call sysimg_builder,libblastrampoline_jll,Artifacts Libdl)) +$(eval $(call sysimg_builder,OpenBLAS_jll,Artifacts Libdl)) +$(eval $(call sysimg_builder,Markdown,Base64)) +$(eval $(call sysimg_builder,Printf,Unicode)) +$(eval $(call sysimg_builder,Random,Serialization SHA)) +$(eval $(call sysimg_builder,Tar,ArgTools,SHA)) +$(eval $(call pkgimg_builder,DelimitedFiles,Mmap)) + +# 2-depth packages +$(eval $(call pkgimg_builder,LLD_jll,Zlib_jll libLLVM_jll Artifacts Libdl)) +$(eval $(call pkgimg_builder,LibSSH2_jll,Artifacts Libdl MbedTLS_jll)) +$(eval $(call pkgimg_builder,MPFR_jll,Artifacts Libdl GMP_jll)) +$(eval $(call sysimg_builder,LinearAlgebra,Libdl libblastrampoline_jll OpenBLAS_jll)) +$(eval $(call sysimg_builder,Dates,Printf)) +$(eval $(call sysimg_builder,Distributed,Random Serialization Sockets)) +$(eval $(call sysimg_builder,Future,Random)) +$(eval $(call sysimg_builder,InteractiveUtils,Markdown)) +$(eval $(call sysimg_builder,LibGit2,NetworkOptions Printf SHA Base64)) +$(eval $(call sysimg_builder,Profile,Printf)) +$(eval $(call sysimg_builder,UUIDs,Random SHA)) + + + # 3-depth packages + # LibGit2_jll +$(eval $(call pkgimg_builder,LibCURL_jll,LibSSH2_jll nghttp2_jll MbedTLS_jll Zlib_jll Artifacts Libdl)) +$(eval $(call sysimg_builder,REPL,InteractiveUtils Markdown Sockets Unicode)) +$(eval $(call sysimg_builder,SharedArrays,Distributed Mmap Random Serialization)) +$(eval $(call sysimg_builder,TOML,Dates)) +$(eval $(call sysimg_builder,Test,Logging Random Serialization InteractiveUtils)) + +# 4-depth packages +$(eval $(call sysimg_builder,LibCURL,LibCURL_jll MozillaCACerts_jll)) + +# 5-depth packages +$(eval $(call sysimg_builder,Downloads,ArgTools FileWatching LibCURL NetworkOptions)) + +# 6-depth packages +$(eval $(call sysimg_builder,Pkg,Dates LibGit2 Libdl Logging Printf Random SHA UUIDs)) # Markdown REPL + +# 7-depth packages +$(eval $(call sysimg_builder,LazyArtifacts,Artifacts Pkg)) + +$(eval $(call pkgimg_builder,SparseArrays,Libdl LinearAlgebra Random Serialization)) +# SuiteSparse_jll +# Statistics diff --git a/stdlib/.gitignore b/stdlib/.gitignore index d159427c40d7c..dec1745520d4c 100644 --- a/stdlib/.gitignore +++ b/stdlib/.gitignore @@ -23,3 +23,4 @@ /SHA /*_jll/StdlibArtifacts.toml /*/Manifest.toml +/*.image diff --git a/stdlib/LLD_jll/Project.toml b/stdlib/LLD_jll/Project.toml index 923f2e578e0d7..52d4b67de3765 100644 --- a/stdlib/LLD_jll/Project.toml +++ b/stdlib/LLD_jll/Project.toml @@ -3,10 +3,8 @@ uuid = "d55e3150-da41-5e91-b323-ecfd1eec6109" version = "14.0.6+3" [deps] -Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Zlib_jll = "83775a58-1f1d-513f-b197-d71354ab007a" Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" -TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" libLLVM_jll = "8f36deef-c2a5-5394-99ed-8e07531fb29a" Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" From f78b3a6677db37123d98bb9ed43d6a8eb68aac8b Mon Sep 17 00:00:00 2001 From: Evert Provoost Date: Fri, 24 Mar 2023 19:21:08 +0100 Subject: [PATCH 375/775] Prefer blocked LAPACK routines for generalized eigen (#49037) --- stdlib/LinearAlgebra/docs/src/index.md | 2 + stdlib/LinearAlgebra/src/eigen.jl | 24 +- stdlib/LinearAlgebra/src/lapack.jl | 291 +++++++++++++++++++++++-- stdlib/LinearAlgebra/src/schur.jl | 9 +- stdlib/LinearAlgebra/test/lapack.jl | 8 +- 5 files changed, 306 insertions(+), 28 deletions(-) diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index cd317b4e36df6..305fa6fa2562d 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -797,6 +797,7 @@ LinearAlgebra.LAPACK.ggsvd! LinearAlgebra.LAPACK.ggsvd3! LinearAlgebra.LAPACK.geevx! LinearAlgebra.LAPACK.ggev! +LinearAlgebra.LAPACK.ggev3! LinearAlgebra.LAPACK.gtsv! LinearAlgebra.LAPACK.gttrf! LinearAlgebra.LAPACK.gttrs! @@ -845,6 +846,7 @@ LinearAlgebra.LAPACK.gehrd! LinearAlgebra.LAPACK.orghr! LinearAlgebra.LAPACK.gees! LinearAlgebra.LAPACK.gges! +LinearAlgebra.LAPACK.gges3! LinearAlgebra.LAPACK.trexc! LinearAlgebra.LAPACK.trsen! LinearAlgebra.LAPACK.tgsen! diff --git a/stdlib/LinearAlgebra/src/eigen.jl b/stdlib/LinearAlgebra/src/eigen.jl index 14676ad6df6eb..224b7d4b86245 100644 --- a/stdlib/LinearAlgebra/src/eigen.jl +++ b/stdlib/LinearAlgebra/src/eigen.jl @@ -440,7 +440,11 @@ det(A::Eigen) = prod(A.values) function eigen!(A::StridedMatrix{T}, B::StridedMatrix{T}; sortby::Union{Function,Nothing}=eigsortby) where T<:BlasReal issymmetric(A) && isposdef(B) && return eigen!(Symmetric(A), Symmetric(B), sortby=sortby) n = size(A, 1) - alphar, alphai, beta, _, vr = LAPACK.ggev!('N', 'V', A, B) + if LAPACK.version() < v"3.6.0" + alphar, alphai, beta, _, vr = LAPACK.ggev!('N', 'V', A, B) + else + alphar, alphai, beta, _, vr = LAPACK.ggev3!('N', 'V', A, B) + end iszero(alphai) && return GeneralizedEigen(sorteig!(alphar ./ beta, vr, sortby)...) vecs = zeros(Complex{T}, n, n) @@ -462,7 +466,11 @@ end function eigen!(A::StridedMatrix{T}, B::StridedMatrix{T}; sortby::Union{Function,Nothing}=eigsortby) where T<:BlasComplex ishermitian(A) && isposdef(B) && return eigen!(Hermitian(A), Hermitian(B), sortby=sortby) - alpha, beta, _, vr = LAPACK.ggev!('N', 'V', A, B) + if LAPACK.version() < v"3.6.0" + alpha, beta, _, vr = LAPACK.ggev!('N', 'V', A, B) + else + alpha, beta, _, vr = LAPACK.ggev3!('N', 'V', A, B) + end return GeneralizedEigen(sorteig!(alpha./beta, vr, sortby)...) end @@ -565,12 +573,20 @@ julia> B """ function eigvals!(A::StridedMatrix{T}, B::StridedMatrix{T}; sortby::Union{Function,Nothing}=eigsortby) where T<:BlasReal issymmetric(A) && isposdef(B) && return sorteig!(eigvals!(Symmetric(A), Symmetric(B)), sortby) - alphar, alphai, beta, vl, vr = LAPACK.ggev!('N', 'N', A, B) + if LAPACK.version() < v"3.6.0" + alphar, alphai, beta, vl, vr = LAPACK.ggev!('N', 'N', A, B) + else + alphar, alphai, beta, vl, vr = LAPACK.ggev3!('N', 'N', A, B) + end return sorteig!((iszero(alphai) ? alphar : complex.(alphar, alphai))./beta, sortby) end function eigvals!(A::StridedMatrix{T}, B::StridedMatrix{T}; sortby::Union{Function,Nothing}=eigsortby) where T<:BlasComplex ishermitian(A) && isposdef(B) && return sorteig!(eigvals!(Hermitian(A), Hermitian(B)), sortby) - alpha, beta, vl, vr = LAPACK.ggev!('N', 'N', A, B) + if LAPACK.version() < v"3.6.0" + alpha, beta, vl, vr = LAPACK.ggev!('N', 'N', A, B) + else + alpha, beta, vl, vr = LAPACK.ggev3!('N', 'N', A, B) + end return sorteig!(alpha./beta, sortby) end diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl index 82ce01fd8428b..c498e9b51bc19 100644 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ b/stdlib/LinearAlgebra/src/lapack.jl @@ -2023,9 +2023,9 @@ the orthogonal/unitary matrix `Q` is computed. If `jobu`, `jobv`, or `jobq` is ggsvd3! ## Expert driver and generalized eigenvalue problem -for (geevx, ggev, elty) in - ((:dgeevx_,:dggev_,:Float64), - (:sgeevx_,:sggev_,:Float32)) +for (geevx, ggev, ggev3, elty) in + ((:dgeevx_,:dggev_,:dggev3_,:Float64), + (:sgeevx_,:sggev_,:sggev3_,:Float32)) @eval begin # SUBROUTINE DGEEVX( BALANC, JOBVL, JOBVR, SENSE, N, A, LDA, WR, WI, # VL, LDVL, VR, LDVR, ILO, IHI, SCALE, ABNRM, @@ -2093,7 +2093,7 @@ for (geevx, ggev, elty) in Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, + Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Clong, Clong, Clong, Clong), balanc, jobvl, jobvr, sense, n, A, lda, wr, @@ -2160,7 +2160,71 @@ for (geevx, ggev, elty) in Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Clong, Clong), + Ref{BlasInt}, Clong, Clong), + jobvl, jobvr, n, A, + lda, B, ldb, alphar, + alphai, beta, vl, ldvl, + vr, ldvr, work, lwork, + info, 1, 1) + chklapackerror(info[]) + if i == 1 + lwork = BlasInt(work[1]) + resize!(work, lwork) + end + end + alphar, alphai, beta, vl, vr + end + + # SUBROUTINE DGGEV3( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHAR, ALPHAI, + # $ BETA, VL, LDVL, VR, LDVR, WORK, LWORK, INFO ) + # * .. Scalar Arguments .. + # CHARACTER JOBVL, JOBVR + # INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N + # * .. + # * .. Array Arguments .. + # DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), + # $ B( LDB, * ), BETA( * ), VL( LDVL, * ), + # $ VR( LDVR, * ), WORK( * ) + function ggev3!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) + require_one_based_indexing(A, B) + chkstride1(A,B) + n, m = checksquare(A,B) + if n != m + throw(DimensionMismatch("A has dimensions $(size(A)), and B has dimensions $(size(B)), but A and B must have the same size")) + end + lda = max(1, stride(A, 2)) + ldb = max(1, stride(B, 2)) + alphar = similar(A, $elty, n) + alphai = similar(A, $elty, n) + beta = similar(A, $elty, n) + ldvl = 0 + if jobvl == 'V' + ldvl = n + elseif jobvl == 'N' + ldvl = 1 + else + throw(ArgumentError("jobvl must be 'V' or 'N', but $jobvl was passed")) + end + vl = similar(A, $elty, ldvl, n) + ldvr = 0 + if jobvr == 'V' + ldvr = n + elseif jobvr == 'N' + ldvr = 1 + else + throw(ArgumentError("jobvr must be 'V' or 'N', but $jobvr was passed")) + end + vr = similar(A, $elty, ldvr, n) + work = Vector{$elty}(undef, 1) + lwork = BlasInt(-1) + info = Ref{BlasInt}() + for i = 1:2 # first call returns lwork as work[1] + ccall((@blasfunc($ggev3), libblastrampoline), Cvoid, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{BlasInt}, Clong, Clong), jobvl, jobvr, n, A, lda, B, ldb, alphar, alphai, beta, vl, ldvl, @@ -2177,9 +2241,9 @@ for (geevx, ggev, elty) in end end -for (geevx, ggev, elty, relty) in - ((:zgeevx_,:zggev_,:ComplexF64,:Float64), - (:cgeevx_,:cggev_,:ComplexF32,:Float32)) +for (geevx, ggev, ggev3, elty, relty) in + ((:zgeevx_,:zggev_,:zggev3_,:ComplexF64,:Float64), + (:cgeevx_,:cggev_,:cggev3_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE ZGEEVX( BALANC, JOBVL, JOBVR, SENSE, N, A, LDA, W, VL, # LDVL, VR, LDVR, ILO, IHI, SCALE, ABNRM, RCONDE, @@ -2241,7 +2305,7 @@ for (geevx, ggev, elty, relty) in Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{BlasInt}, Clong, Clong, Clong, Clong), + Ptr{$relty}, Ref{BlasInt}, Clong, Clong, Clong, Clong), balanc, jobvl, jobvr, sense, n, A, lda, w, VL, max(1,ldvl), VR, max(1,ldvr), @@ -2307,7 +2371,72 @@ for (geevx, ggev, elty, relty) in Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, - Ptr{BlasInt}, Clong, Clong), + Ref{BlasInt}, Clong, Clong), + jobvl, jobvr, n, A, + lda, B, ldb, alpha, + beta, vl, ldvl, vr, + ldvr, work, lwork, rwork, + info, 1, 1) + chklapackerror(info[]) + if i == 1 + lwork = BlasInt(work[1]) + resize!(work, lwork) + end + end + alpha, beta, vl, vr + end + + # SUBROUTINE ZGGEV3( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHA, BETA, + # $ VL, LDVL, VR, LDVR, WORK, LWORK, RWORK, INFO ) + # * .. Scalar Arguments .. + # CHARACTER JOBVL, JOBVR + # INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N + # * .. + # * .. Array Arguments .. + # DOUBLE PRECISION RWORK( * ) + # COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), + # $ BETA( * ), VL( LDVL, * ), VR( LDVR, * ), + # $ WORK( * ) + function ggev3!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) + require_one_based_indexing(A, B) + chkstride1(A, B) + n, m = checksquare(A, B) + if n != m + throw(DimensionMismatch("A has dimensions $(size(A)), and B has dimensions $(size(B)), but A and B must have the same size")) + end + lda = max(1, stride(A, 2)) + ldb = max(1, stride(B, 2)) + alpha = similar(A, $elty, n) + beta = similar(A, $elty, n) + ldvl = 0 + if jobvl == 'V' + ldvl = n + elseif jobvl == 'N' + ldvl = 1 + else + throw(ArgumentError("jobvl must be 'V' or 'N', but $jobvl was passed")) + end + vl = similar(A, $elty, ldvl, n) + ldvr = 0 + if jobvr == 'V' + ldvr = n + elseif jobvr == 'N' + ldvr = 1 + else + throw(ArgumentError("jobvr must be 'V' or 'N', but $jobvr was passed")) + end + vr = similar(A, $elty, ldvr, n) + work = Vector{$elty}(undef, 1) + lwork = BlasInt(-1) + rwork = Vector{$relty}(undef, 8n) + info = Ref{BlasInt}() + for i = 1:2 # first call returns lwork as work[1] + ccall((@blasfunc($ggev3), libblastrampoline), Cvoid, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, + Ref{BlasInt}, Clong, Clong), jobvl, jobvr, n, A, lda, B, ldb, alpha, beta, vl, ldvl, vr, @@ -2353,6 +2482,17 @@ corresponding eigenvectors are computed. """ ggev!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) +""" + ggev3!(jobvl, jobvr, A, B) -> (alpha, beta, vl, vr) + +Finds the generalized eigendecomposition of `A` and `B` using a blocked +algorithm. If `jobvl = N`, the left eigenvectors aren't computed. If +`jobvr = N`, the right eigenvectors aren't computed. If `jobvl = V` or +`jobvr = V`, the corresponding eigenvectors are computed. This function +requires LAPACK 3.6.0. +""" +ggev3!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) + # One step incremental condition estimation of max/min singular values for (laic1, elty) in ((:dlaic1_,:Float64), @@ -5993,9 +6133,9 @@ for (ormtr, elty) in end end -for (gees, gges, elty) in - ((:dgees_,:dgges_,:Float64), - (:sgees_,:sgges_,:Float32)) +for (gees, gges, gges3, elty) in + ((:dgees_,:dgges_,:dgges3_,:Float64), + (:sgees_,:sgges_,:sgges3_,:Float32)) @eval begin # .. Scalar Arguments .. # CHARACTER JOBVS, SORT @@ -6022,7 +6162,7 @@ for (gees, gges, elty) in (Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{Cvoid}, Ptr{BlasInt}, Clong, Clong), + Ref{BlasInt}, Ptr{Cvoid}, Ref{BlasInt}, Clong, Clong), jobvs, 'N', C_NULL, n, A, max(1, stride(A, 2)), sdim, wr, wi, vs, ldvs, work, @@ -6069,7 +6209,56 @@ for (gees, gges, elty) in Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{Cvoid}, - Ptr{BlasInt}, Clong, Clong, Clong), + Ref{BlasInt}, Clong, Clong, Clong), + jobvsl, jobvsr, 'N', C_NULL, + n, A, max(1,stride(A, 2)), B, + max(1,stride(B, 2)), sdim, alphar, alphai, + beta, vsl, ldvsl, vsr, + ldvsr, work, lwork, C_NULL, + info, 1, 1, 1) + chklapackerror(info[]) + if i == 1 + lwork = BlasInt(real(work[1])) + resize!(work, lwork) + end + end + A, B, complex.(alphar, alphai), beta, vsl[1:(jobvsl == 'V' ? n : 0),:], vsr[1:(jobvsr == 'V' ? n : 0),:] + end + + # * .. Scalar Arguments .. + # CHARACTER JOBVSL, JOBVSR, SORT + # INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM + # * .. + # * .. Array Arguments .. + # LOGICAL BWORK( * ) + # DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), + # $ B( LDB, * ), BETA( * ), VSL( LDVSL, * ), + # $ VSR( LDVSR, * ), WORK( * ) + function gges3!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) + chkstride1(A, B) + n, m = checksquare(A, B) + if n != m + throw(DimensionMismatch("dimensions of A, ($n,$n), and B, ($m,$m), must match")) + end + sdim = BlasInt(0) + alphar = similar(A, $elty, n) + alphai = similar(A, $elty, n) + beta = similar(A, $elty, n) + ldvsl = jobvsl == 'V' ? max(1, n) : 1 + vsl = similar(A, $elty, ldvsl, n) + ldvsr = jobvsr == 'V' ? max(1, n) : 1 + vsr = similar(A, $elty, ldvsr, n) + work = Vector{$elty}(undef, 1) + lwork = BlasInt(-1) + info = Ref{BlasInt}() + for i = 1:2 # first call returns lwork as work[1] + ccall((@blasfunc($gges3), libblastrampoline), Cvoid, + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{Cvoid}, + Ref{BlasInt}, Clong, Clong, Clong), jobvsl, jobvsr, 'N', C_NULL, n, A, max(1,stride(A, 2)), B, max(1,stride(B, 2)), sdim, alphar, alphai, @@ -6087,9 +6276,9 @@ for (gees, gges, elty) in end end -for (gees, gges, elty, relty) in - ((:zgees_,:zgges_,:ComplexF64,:Float64), - (:cgees_,:cgges_,:ComplexF32,:Float32)) +for (gees, gges, gges3, elty, relty) in + ((:zgees_,:zgges_,:zgges3_,:ComplexF64,:Float64), + (:cgees_,:cgges_,:cgges3_,:ComplexF32,:Float32)) @eval begin # * .. Scalar Arguments .. # CHARACTER JOBVS, SORT @@ -6117,7 +6306,7 @@ for (gees, gges, elty, relty) in (Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{Cvoid}, Ptr{BlasInt}, Clong, Clong), + Ptr{$relty}, Ptr{Cvoid}, Ref{BlasInt}, Clong, Clong), jobvs, sort, C_NULL, n, A, max(1, stride(A, 2)), sdim, w, vs, ldvs, work, lwork, @@ -6165,7 +6354,57 @@ for (gees, gges, elty, relty) in Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{Cvoid}, - Ptr{BlasInt}, Clong, Clong, Clong), + Ref{BlasInt}, Clong, Clong, Clong), + jobvsl, jobvsr, 'N', C_NULL, + n, A, max(1, stride(A, 2)), B, + max(1, stride(B, 2)), sdim, alpha, beta, + vsl, ldvsl, vsr, ldvsr, + work, lwork, rwork, C_NULL, + info, 1, 1, 1) + chklapackerror(info[]) + if i == 1 + lwork = BlasInt(real(work[1])) + resize!(work, lwork) + end + end + A, B, alpha, beta, vsl[1:(jobvsl == 'V' ? n : 0),:], vsr[1:(jobvsr == 'V' ? n : 0),:] + end + + # * .. Scalar Arguments .. + # CHARACTER JOBVSL, JOBVSR, SORT + # INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM + # * .. + # * .. Array Arguments .. + # LOGICAL BWORK( * ) + # DOUBLE PRECISION RWORK( * ) + # COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), + # $ BETA( * ), VSL( LDVSL, * ), VSR( LDVSR, * ), + # $ WORK( * ) + function gges3!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) + chkstride1(A, B) + n, m = checksquare(A, B) + if n != m + throw(DimensionMismatch("dimensions of A, ($n,$n), and B, ($m,$m), must match")) + end + sdim = BlasInt(0) + alpha = similar(A, $elty, n) + beta = similar(A, $elty, n) + ldvsl = jobvsl == 'V' ? max(1, n) : 1 + vsl = similar(A, $elty, ldvsl, n) + ldvsr = jobvsr == 'V' ? max(1, n) : 1 + vsr = similar(A, $elty, ldvsr, n) + work = Vector{$elty}(undef, 1) + lwork = BlasInt(-1) + rwork = Vector{$relty}(undef, 8n) + info = Ref{BlasInt}() + for i = 1:2 # first call returns lwork as work[1] + ccall((@blasfunc($gges3), libblastrampoline), Cvoid, + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{Cvoid}, + Ref{BlasInt}, Clong, Clong, Clong), jobvsl, jobvsr, 'N', C_NULL, n, A, max(1, stride(A, 2)), B, max(1, stride(B, 2)), sdim, alpha, beta, @@ -6207,6 +6446,18 @@ vectors are returned in `vsl` and the right Schur vectors are returned in `vsr`. """ gges!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) +""" + gges3!(jobvsl, jobvsr, A, B) -> (A, B, alpha, beta, vsl, vsr) + +Computes the generalized eigenvalues, generalized Schur form, left Schur +vectors (`jobsvl = V`), or right Schur vectors (`jobvsr = V`) of `A` and +`B` using a blocked algorithm. This function requires LAPACK 3.6.0. + +The generalized eigenvalues are returned in `alpha` and `beta`. The left Schur +vectors are returned in `vsl` and the right Schur vectors are returned in `vsr`. +""" +gges3!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) + for (trexc, trsen, tgsen, elty) in ((:dtrexc_, :dtrsen_, :dtgsen_, :Float64), (:strexc_, :strsen_, :stgsen_, :Float32)) diff --git a/stdlib/LinearAlgebra/src/schur.jl b/stdlib/LinearAlgebra/src/schur.jl index 53741adb48cf9..7257544ff872e 100644 --- a/stdlib/LinearAlgebra/src/schur.jl +++ b/stdlib/LinearAlgebra/src/schur.jl @@ -345,8 +345,13 @@ Base.iterate(S::GeneralizedSchur, ::Val{:done}) = nothing Same as [`schur`](@ref) but uses the input matrices `A` and `B` as workspace. """ -schur!(A::StridedMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} = - GeneralizedSchur(LinearAlgebra.LAPACK.gges!('V', 'V', A, B)...) +function schur!(A::StridedMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} + if LAPACK.version() < v"3.6.0" + GeneralizedSchur(LinearAlgebra.LAPACK.gges!('V', 'V', A, B)...) + else + GeneralizedSchur(LinearAlgebra.LAPACK.gges3!('V', 'V', A, B)...) + end +end """ schur(A, B) -> F::GeneralizedSchur diff --git a/stdlib/LinearAlgebra/test/lapack.jl b/stdlib/LinearAlgebra/test/lapack.jl index e0e75f0a88413..1e9e2a2e31e65 100644 --- a/stdlib/LinearAlgebra/test/lapack.jl +++ b/stdlib/LinearAlgebra/test/lapack.jl @@ -180,7 +180,7 @@ end end end -@testset "geevx, ggev errors" begin +@testset "geevx, ggev, ggev3 errors" begin @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) A = rand(elty,10,10) B = rand(elty,10,10) @@ -191,6 +191,9 @@ end @test_throws ArgumentError LAPACK.ggev!('N','B',A,B) @test_throws ArgumentError LAPACK.ggev!('B','N',A,B) @test_throws DimensionMismatch LAPACK.ggev!('N','N',A,zeros(elty,12,12)) + @test_throws ArgumentError LAPACK.ggev3!('N','B',A,B) + @test_throws ArgumentError LAPACK.ggev3!('B','N',A,B) + @test_throws DimensionMismatch LAPACK.ggev3!('N','N',A,zeros(elty,12,12)) end end @@ -590,11 +593,12 @@ end end end -@testset "gees, gges error throwing" begin +@testset "gees, gges, gges3 error throwing" begin @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) A = rand(elty,10,10) B = rand(elty,11,11) @test_throws DimensionMismatch LAPACK.gges!('V','V',A,B) + @test_throws DimensionMismatch LAPACK.gges3!('V','V',A,B) end end From 1b7f98a79a0a627cef1d06ae92e801917fad11cf Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 24 Mar 2023 14:51:42 -0400 Subject: [PATCH 376/775] channels: Don't rely on tight timing constraints (#49090) The test was relying on two particular non-guaranteed timing races: 1. That the 0.5s wait task was started within 0.5s of the timedwait starting. 2. That the timedwait finished within 0.5 seconds of its deadline. Neither of these are guaranteed. The second of these was already made non-fatal with a warning print. However, I don't like warning prints in tests. They should either pass or fail. Nobody looks at the output unless the tests fail, so change the tests to: 1. Add extra synchronization to solve the race condition (1) by making sure the sleep starts before the timedwait. 2. Instead of doing a long wait, just have a synchronization channel that will never be made active. In particular, the thing that this test checks is that the timeout in `timedwait` actually works, and doesn't return too early. This arrangement checks both of those properties without unduly tight timing constraints. --- test/channels.jl | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/test/channels.jl b/test/channels.jl index eb82a20686ae9..dbda5cf069081 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -311,6 +311,7 @@ end @testset "timedwait on multiple channels" begin Experimental.@sync begin + sync = Channel(1) rr1 = Channel(1) rr2 = Channel(1) rr3 = Channel(1) @@ -320,20 +321,17 @@ end @test !callback() @test timedwait(callback, 0) === :timed_out - @async begin sleep(0.5); put!(rr1, :ok) end + @async begin put!(sync, :ready); sleep(0.5); put!(rr1, :ok) end @async begin sleep(1.0); put!(rr2, :ok) end - @async begin sleep(2.0); put!(rr3, :ok) end + @async begin @test take!(rr3) == :done end + @test take!(sync) == :ready et = @elapsed timedwait(callback, 1) - # assuming that 0.5 seconds is a good enough buffer on a typical modern CPU - try - @assert (et >= 1.0) && (et <= 1.5) - @assert !isready(rr3) - catch - @warn "`timedwait` tests delayed. et=$et, isready(rr3)=$(isready(rr3))" - end + @test et >= 1.0 + @test isready(rr1) + put!(rr3, :done) end end From e90f6a3d9c075966000c227e4269c9111243f475 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Fri, 24 Mar 2023 15:40:34 -0400 Subject: [PATCH 377/775] restrict codeunit effects slightly (#49136) * restrict codeunit effects slightly --- base/strings/string.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/strings/string.jl b/base/strings/string.jl index f0400d8cf3672..585c57714a559 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -113,7 +113,8 @@ pointer(s::String, i::Integer) = pointer(s) + Int(i)::Int - 1 ncodeunits(s::String) = Core.sizeof(s) codeunit(s::String) = UInt8 -@assume_effects :foldable @inline function codeunit(s::String, i::Integer) +codeunit(s::String, i::Integer) = codeunit(s, Int(i)) +@assume_effects :foldable @inline function codeunit(s::String, i::Int) @boundscheck checkbounds(s, i) b = GC.@preserve s unsafe_load(pointer(s, i)) return b From a922b5966c32102915676013cf7a867c40da7c79 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 24 Mar 2023 17:24:18 -0400 Subject: [PATCH 378/775] ensure C++ does not free the DebugRegistry while we are using it (#49139) --- src/debuginfo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index 2d087178afef1..87bd822a9e818 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -41,10 +41,10 @@ using namespace llvm; #include "julia_assert.h" #include "debug-registry.h" -static JITDebugInfoRegistry DebugRegistry; +static JITDebugInfoRegistry *DebugRegistry = new JITDebugInfoRegistry; static JITDebugInfoRegistry &getJITDebugRegistry() JL_NOTSAFEPOINT { - return DebugRegistry; + return *DebugRegistry; } struct debug_link_info { From 508992c355c887f859d189f1dc8d0e3701dee5b6 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Fri, 24 Mar 2023 17:04:21 +0800 Subject: [PATCH 379/775] replace `NULL` with a innervar --- src/subtype.c | 48 +++++++++++++++--------------------------------- test/subtype.jl | 24 ++++++++---------------- 2 files changed, 23 insertions(+), 49 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 357334f97960e..a9aeb9209a031 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2389,8 +2389,16 @@ static jl_value_t *bound_var_below(jl_tvar_t *tv, jl_varbinding_t *bb, jl_stenv_ return bb->lb; return jl_box_long(blb - offset); } - if (offset > 0) - return NULL; + if (offset > 0) { + if (bb->innervars == NULL) + bb->innervars = jl_alloc_array_1d(jl_array_any_type, 0); + jl_value_t *ntv = NULL; + JL_GC_PUSH1(&ntv); + ntv = (jl_value_t *)jl_new_typevar(tv->name, jl_bottom_type, (jl_value_t *)jl_any_type); + jl_array_ptr_1d_push(bb->innervars, ntv); + JL_GC_POP(); + return ntv; + } return (jl_value_t*)tv; } @@ -2520,26 +2528,6 @@ static int check_unsat_bound(jl_value_t *t, jl_tvar_t *v, jl_stenv_t *e) JL_NOTS return 0; } -//TODO: This doesn't work for nested `Tuple`. -static int has_free_vararg_length(jl_value_t *a, jl_stenv_t *e) { - if (jl_is_unionall(a)) - a = jl_unwrap_unionall(a); - if (jl_is_datatype(a) && jl_is_tuple_type((jl_datatype_t *)a)) { - size_t lx = jl_nparams((jl_datatype_t *)a); - if (lx > 0) { - jl_value_t *la = jl_tparam((jl_datatype_t *)a, lx-1); - if (jl_is_vararg(la)) { - jl_value_t *len = jl_unwrap_vararg_num((jl_vararg_t *)la); - // return 1 if we meet a vararg with Null length - if (!len) return 1; - // or a typevar not in the current env. - if (jl_is_typevar(len)) - return lookup(e, (jl_tvar_t *)len) == NULL; - } - } - } - return 0; -} static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int8_t R, int param) { @@ -2583,11 +2571,6 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int JL_GC_POP(); return jl_bottom_type; } - if (jl_is_uniontype(ub) && !jl_is_uniontype(a)) { - bb->ub = ub; - bb->lb = jl_bottom_type; - ub = (jl_value_t*)b; - } } if (ub != (jl_value_t*)b) { if (jl_has_free_typevars(ub)) { @@ -2597,12 +2580,11 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int } } bb->ub = ub; - // We get a imprecise Tuple here. Don't change `lb` and return the typevar directly. - if (has_free_vararg_length(ub, e) && !has_free_vararg_length(a, e)) { - JL_GC_POP(); - return (jl_value_t*)b; - } - bb->lb = ub; + if ((jl_is_uniontype(ub) && !jl_is_uniontype(a)) || + (jl_is_unionall(ub) && !jl_is_unionall(a))) + ub = (jl_value_t*)b; + else + bb->lb = ub; } JL_GC_POP(); return ub; diff --git a/test/subtype.jl b/test/subtype.jl index cece00c252cdb..948bd78387028 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -1043,11 +1043,7 @@ function test_intersection() Type{Tuple{Int,T}} where T<:Integer) @testintersect(Type{<:Tuple{Any,Vararg{Any}}}, Type{Tuple{Vararg{Int,N}}} where N, - !Union{}) - - @test typeintersect(Type{<:Tuple{Any,Vararg{Any}}}, Type{Tuple{Vararg{Int,N}}} where N) != Type{Tuple{Int,Vararg{Int}}} - @test_broken typeintersect(Type{<:Tuple{Any,Vararg{Any}}}, Type{Tuple{Vararg{Int,N}}} where N) == Type{Tuple{Int,Vararg{Int,N}}} where N - @test_broken typeintersect(Type{<:Tuple{Any,Vararg{Any}}}, Type{Tuple{Vararg{Int,N}}} where N) != Type{<:Tuple{Int,Vararg{Int}}} + Type{Tuple{Int,Vararg{Int,N}}} where N) @testintersect(Type{<:Array}, Type{AbstractArray{T}} where T, @@ -2206,23 +2202,19 @@ let A = Pair{NTuple{N, Int}, NTuple{N, Int}} where N, Bs = (Pair{<:Tuple{Int, Vararg{Int}}, <:Tuple{Int, Int, Vararg{Int}}}, Pair{Tuple{Int, Vararg{Int,N1}}, Tuple{Int, Int, Vararg{Int,N2}}} where {N1,N2}, Pair{<:Tuple{Int, Vararg{Int,N}} where {N}, <:Tuple{Int, Int, Vararg{Int,N}} where {N}}) - Cerr = Pair{Tuple{Int, Vararg{Int, N}}, Tuple{Int, Int, Vararg{Int, N}}} where {N} - for B in Bs - C = typeintersect(A, B) - @test C == typeintersect(B, A) != Union{} - @test C != Cerr - @test_broken C != B + Cs = (Bs[2], Bs[2], Bs[3]) + for (B, C) in zip(Bs, Cs) + # TODO: The ideal result is Pair{Tuple{Int, Int, Vararg{Int, N}}, Tuple{Int, Int, Vararg{Int, N}}} where {N} + @testintersect(A, B, C) end end # Example from pr#39098 @testintersect(NTuple, Tuple{Any,Vararg}, Tuple{T, Vararg{T}} where {T}) -let S = Val{T} where T<:Tuple{Tuple{Any, Vararg{Any}}} - T = Val{Tuple{Tuple{Vararg{Any, N}}}} where {N} - @testintersect(S, T, !Union{}) - @test_broken typeintersect(S, T) != Val{Tuple{Tuple{Any, Vararg{Any}}}} -end +@testintersect(Val{T} where T<:Tuple{Tuple{Any, Vararg{Any}}}, + Val{Tuple{Tuple{Vararg{Any, N}}}} where {N}, + Val{Tuple{Tuple{Any, Vararg{Any, N}}}} where {N}) let A = Pair{NTuple{N, Int}, Val{N}} where N, Bs = (Pair{<:Tuple{Int, Vararg{Int}}, <:Val}, From 16e9f18833f01a26f58ac4ddaaa20bedceacf5e7 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sat, 25 Mar 2023 12:47:29 +0900 Subject: [PATCH 380/775] minor follow up on #48996 (#49129) - removed unnecessary `Union`-signature - use one-liner definition when applicable - add type assertion to make it more robust against invalidation --- base/strings/string.jl | 7 +++---- base/strings/substring.jl | 12 +++--------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/base/strings/string.jl b/base/strings/string.jl index 585c57714a559..ac1403f01a4a1 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -332,9 +332,8 @@ isvalid(s::String, i::Int) = checkbounds(Bool, s, i) && thisind(s, i) == i isascii(s::String) = isascii(codeunits(s)) # don't assume effects for general integers since we cannot know their implementation -@assume_effects :foldable function repeat(c::Char, r::BitInteger) - @invoke repeat(c, r::Integer) -end +@assume_effects :foldable repeat(c::Char, r::BitInteger) = @invoke repeat(c::Char, r::Integer) + """ repeat(c::AbstractChar, r::Integer) -> String @@ -348,7 +347,7 @@ julia> repeat('A', 3) ``` """ function repeat(c::AbstractChar, r::Integer) - c = Char(c) + c = Char(c)::Char r == 0 && return "" r < 0 && throw(ArgumentError("can't repeat a character $r times")) u = bswap(reinterpret(UInt32, c)) diff --git a/base/strings/substring.jl b/base/strings/substring.jl index 0346294d1b472..ea132402447be 100644 --- a/base/strings/substring.jl +++ b/base/strings/substring.jl @@ -225,13 +225,9 @@ end end # nothrow needed here because for v in a can't prove the indexing is inbounds. -@assume_effects :foldable :nothrow function string(a::Union{Char, String, Symbol}...) - _string(a...) -end +@assume_effects :foldable :nothrow string(a::Union{Char, String, Symbol}...) = _string(a...) -function string(a::Union{Char, String, SubString{String}, Symbol}...) - _string(a...) -end +string(a::Union{Char, String, SubString{String}, Symbol}...) = _string(a...) function _string(a::Union{Char, String, SubString{String}, Symbol}...) n = 0 @@ -264,9 +260,7 @@ end # don't assume effects for general integers since we cannot know their implementation # not nothrow because r<0 throws -@assume_effects :foldable function repeat(s::String, r::BitInteger) - @invoke repeat(s, r::Integer) -end +@assume_effects :foldable repeat(s::String, r::BitInteger) = @invoke repeat(s::String, r::Integer) function repeat(s::Union{String, SubString{String}}, r::Integer) r < 0 && throw(ArgumentError("can't repeat a string $r times")) From 88a1fc7dcda4ebd23331221bdfc2f66e141190f0 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sat, 25 Mar 2023 16:37:24 +0530 Subject: [PATCH 381/775] Offset to indices in (unsafe_)copyto docstring (#49147) --- base/array.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/array.jl b/base/array.jl index eab043a6439a9..84399b9a43480 100644 --- a/base/array.jl +++ b/base/array.jl @@ -267,7 +267,7 @@ end """ unsafe_copyto!(dest::Array, do, src::Array, so, N) -Copy `N` elements from a source array to a destination, starting at offset `so` in the +Copy `N` elements from a source array to a destination, starting at the linear index `so` in the source and `do` in the destination (1-indexed). The `unsafe` prefix on this function indicates that no validation is performed to ensure @@ -307,8 +307,8 @@ unsafe_copyto!(dest::Array, doffs, src::Array, soffs, n) = """ copyto!(dest, do, src, so, N) -Copy `N` elements from collection `src` starting at offset `so`, to array `dest` starting at -offset `do`. Return `dest`. +Copy `N` elements from collection `src` starting at the linear index `so`, to array `dest` starting at +the index `do`. Return `dest`. """ function copyto!(dest::Array, doffs::Integer, src::Array, soffs::Integer, n::Integer) return _copyto_impl!(dest, doffs, src, soffs, n) From 4853a52d3e697d51fd03143d193302d53e14868b Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Sat, 25 Mar 2023 18:21:21 -0400 Subject: [PATCH 382/775] be careful about signed zero for complex real isequal (#48997) --- base/complex.jl | 2 ++ test/complex.jl | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/base/complex.jl b/base/complex.jl index a32ccaa5219a6..4ce43687aa932 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -245,6 +245,8 @@ bswap(z::Complex) = Complex(bswap(real(z)), bswap(imag(z))) ==(x::Real, z::Complex) = isreal(z) && real(z) == x isequal(z::Complex, w::Complex) = isequal(real(z),real(w)) & isequal(imag(z),imag(w)) +isequal(z::Complex, w::Real) = isequal(real(z),w) & isequal(imag(z),zero(w)) +isequal(z::Real, w::Complex) = isequal(z,real(w)) & isequal(zero(z),imag(w)) in(x::Complex, r::AbstractRange{<:Real}) = isreal(x) && real(x) in r diff --git a/test/complex.jl b/test/complex.jl index 40b45870feafc..2b87655f1ebe0 100644 --- a/test/complex.jl +++ b/test/complex.jl @@ -44,7 +44,12 @@ end @testset for T in (Float16, Float32, Float64, BigFloat) t = true f = false - + @testset "equality" begin + @test isequal(T(0.0)*im, T(0.0)) + @test !isequal(T(0.0)*im, T(-0.0)) + @test isequal(Complex(T(-0.0), T(0.0)), T(-0.0)) + @test !isequal(T(-0.0)*im, T(-0.0)) + end @testset "add and subtract" begin @test isequal(T(+0.0) + im, Complex(T(+0.0), T(+1.0))) @test isequal(T(-0.0) + im, Complex(T(-0.0), T(+1.0))) From 1944ef65a4697a8cbe954b1a14e4e28744afb137 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Sun, 26 Mar 2023 08:09:50 +0200 Subject: [PATCH 383/775] print NamedTuple types using the macro format (#49117) --- base/namedtuple.jl | 8 ++++---- base/show.jl | 30 ++++++++++++++++++++++++++---- doc/src/manual/types.md | 25 +++++++++++++++---------- test/namedtuple.jl | 2 +- test/show.jl | 12 ++++++++++-- 5 files changed, 56 insertions(+), 21 deletions(-) diff --git a/base/namedtuple.jl b/base/namedtuple.jl index dcd7855a402b7..ad36607660960 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -461,20 +461,20 @@ This macro gives a more convenient syntax for declaring `NamedTuple` types. It r type with the given keys and types, equivalent to `NamedTuple{(:key1, :key2, ...), Tuple{Type1,Type2,...}}`. If the `::Type` declaration is omitted, it is taken to be `Any`. The `begin ... end` form allows the declarations to be split across multiple lines (similar to a `struct` declaration), but is otherwise -equivalent. +equivalent. The `NamedTuple` macro is used when printing `NamedTuple` types to e.g. the REPL. -For example, the tuple `(a=3.1, b="hello")` has a type `NamedTuple{(:a, :b),Tuple{Float64,String}}`, which +For example, the tuple `(a=3.1, b="hello")` has a type `NamedTuple{(:a, :b), Tuple{Float64, String}}`, which can also be declared via `@NamedTuple` as: ```jldoctest julia> @NamedTuple{a::Float64, b::String} -NamedTuple{(:a, :b), Tuple{Float64, String}} +@NamedTuple{a::Float64, b::String} julia> @NamedTuple begin a::Float64 b::String end -NamedTuple{(:a, :b), Tuple{Float64, String}} +@NamedTuple{a::Float64, b::String} ``` !!! compat "Julia 1.5" diff --git a/base/show.jl b/base/show.jl index 3ed5db6c2ef63..3a17a5e4197ad 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1060,11 +1060,12 @@ end function show_datatype(io::IO, x::DataType, wheres::Vector{TypeVar}=TypeVar[]) parameters = x.parameters::SimpleVector istuple = x.name === Tuple.name + isnamedtuple = x.name === typename(NamedTuple) n = length(parameters) # Print tuple types with homogeneous tails longer than max_n compactly using `NTuple` or `Vararg` - max_n = 3 if istuple + max_n = 3 taillen = 1 for i in (n-1):-1:1 if parameters[i] === parameters[n] @@ -1090,10 +1091,31 @@ function show_datatype(io::IO, x::DataType, wheres::Vector{TypeVar}=TypeVar[]) end print(io, "}") end - else - show_type_name(io, x.name) - show_typeparams(io, parameters, (unwrap_unionall(x.name.wrapper)::DataType).parameters, wheres) + return + elseif isnamedtuple + syms, types = parameters + first = true + if syms isa Tuple && types isa DataType + print(io, "@NamedTuple{") + for i in 1:length(syms) + if !first + print(io, ", ") + end + print(io, syms[i]) + typ = types.parameters[i] + if typ !== Any + print(io, "::") + show(io, typ) + end + first = false + end + print(io, "}") + return + end end + + show_type_name(io, x.name) + show_typeparams(io, parameters, (unwrap_unionall(x.name.wrapper)::DataType).parameters, wheres) end function show_supertypes(io::IO, typ::DataType) diff --git a/doc/src/manual/types.md b/doc/src/manual/types.md index ce61b1a25a0dc..430a006c67788 100644 --- a/doc/src/manual/types.md +++ b/doc/src/manual/types.md @@ -978,24 +978,29 @@ alias for `Tuple{Vararg{T,N}}`, i.e. a tuple type containing exactly `N` element Named tuples are instances of the [`NamedTuple`](@ref) type, which has two parameters: a tuple of symbols giving the field names, and a tuple type giving the field types. +For convenience, `NamedTuple` types are printed using the [`@NamedTuple`](@ref) macro which provides a +convenient `struct`-like syntax for declaring these types via `key::Type` declarations, +where an omitted `::Type` corresponds to `::Any`. + ```jldoctest -julia> typeof((a=1,b="hello")) -NamedTuple{(:a, :b), Tuple{Int64, String}} +julia> typeof((a=1,b="hello")) # prints in macro form +@NamedTuple{a::Int64, b::String} + +julia> NamedTuple{(:a, :b), Tuple{Int64, String}} # long form of the type +@NamedTuple{a::Int64, b::String} ``` -The [`@NamedTuple`](@ref) macro provides a more convenient `struct`-like syntax for declaring -`NamedTuple` types via `key::Type` declarations, where an omitted `::Type` corresponds to `::Any`. +The `begin ... end` form of the `@NamedTuple` macro allows the declarations to be +split across multiple lines (similar to a struct declaration), but is otherwise equivalent: -```jldoctest -julia> @NamedTuple{a::Int, b::String} -NamedTuple{(:a, :b), Tuple{Int64, String}} +```jldoctest julia> @NamedTuple begin a::Int b::String end -NamedTuple{(:a, :b), Tuple{Int64, String}} +@NamedTuple{a::Int64, b::String} ``` A `NamedTuple` type can be used as a constructor, accepting a single tuple argument. @@ -1003,10 +1008,10 @@ The constructed `NamedTuple` type can be either a concrete type, with both param or a type that specifies only field names: ```jldoctest -julia> @NamedTuple{a::Float32,b::String}((1,"")) +julia> @NamedTuple{a::Float32,b::String}((1, "")) (a = 1.0f0, b = "") -julia> NamedTuple{(:a, :b)}((1,"")) +julia> NamedTuple{(:a, :b)}((1, "")) (a = 1, b = "") ``` diff --git a/test/namedtuple.jl b/test/namedtuple.jl index 039b93a216d47..ea3a5cdbb8ee4 100644 --- a/test/namedtuple.jl +++ b/test/namedtuple.jl @@ -144,7 +144,7 @@ end let nt = merge(NamedTuple{(:a,:b),Tuple{Int32,Union{Int32,Nothing}}}((1,Int32(2))), NamedTuple{(:a,:c),Tuple{Union{Int8,Nothing},Float64}}((nothing,1.0))) @test typeof(nt) == NamedTuple{(:a,:b,:c),Tuple{Union{Int8,Nothing},Union{Int32,Nothing},Float64}} - @test repr(nt) == "NamedTuple{(:a, :b, :c), Tuple{Union{Nothing, Int8}, Union{Nothing, Int32}, Float64}}((nothing, 2, 1.0))" + @test repr(nt) == "@NamedTuple{a::Union{Nothing, Int8}, b::Union{Nothing, Int32}, c::Float64}((nothing, 2, 1.0))" end @test merge(NamedTuple(), [:a=>1, :b=>2, :c=>3, :a=>4, :c=>5]) == (a=4, b=2, c=5) diff --git a/test/show.jl b/test/show.jl index 058b14951e260..b78816c077f60 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1348,6 +1348,14 @@ test_repr("(:).a") @test repr(Tuple{String, Int64, Int64, Int64}) == "Tuple{String, Int64, Int64, Int64}" @test repr(Tuple{String, Int64, Int64, Int64, Int64}) == "Tuple{String, Vararg{Int64, 4}}" +# Test printing of NamedTuples using the macro syntax +@test repr(@NamedTuple{kw::Int64}) == "@NamedTuple{kw::Int64}" +@test repr(@NamedTuple{kw::Union{Float64, Int64}, kw2::Int64}) == "@NamedTuple{kw::Union{Float64, Int64}, kw2::Int64}" +@test repr(@NamedTuple{kw::@NamedTuple{kw2::Int64}}) == "@NamedTuple{kw::@NamedTuple{kw2::Int64}}" +@test repr(@NamedTuple{kw::NTuple{7, Int64}}) == "@NamedTuple{kw::NTuple{7, Int64}}" +@test repr(@NamedTuple{a::Float64, b}) == "@NamedTuple{a::Float64, b}" + + @testset "issue #42931" begin @test repr(NTuple{4, :A}) == "NTuple{4, :A}" @test repr(NTuple{3, :A}) == "Tuple{:A, :A, :A}" @@ -1827,8 +1835,8 @@ end # issue #27747 let t = (x = Integer[1, 2],) v = [t, t] - @test showstr(v) == "NamedTuple{(:x,), Tuple{Vector{Integer}}}[(x = [1, 2],), (x = [1, 2],)]" - @test replstr(v) == "2-element Vector{NamedTuple{(:x,), Tuple{Vector{Integer}}}}:\n (x = [1, 2],)\n (x = [1, 2],)" + @test showstr(v) == "@NamedTuple{x::Vector{Integer}}[(x = [1, 2],), (x = [1, 2],)]" + @test replstr(v) == "2-element Vector{@NamedTuple{x::Vector{Integer}}}:\n (x = [1, 2],)\n (x = [1, 2],)" end # issue #25857 From 4fd52b49656945cbc760ff001009f3205ed5a950 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Sun, 26 Mar 2023 17:08:22 -0400 Subject: [PATCH 384/775] Export JULIA_DEPOT_PATH in pkgimage.mk (#49161) --- pkgimage.mk | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkgimage.mk b/pkgimage.mk index aa6e6d1c266ce..462767b07d550 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -5,11 +5,14 @@ include $(JULIAHOME)/Make.inc VERSDIR := v$(shell cut -d. -f1-2 < $(JULIAHOME)/VERSION) -JULIA_DEPOT_PATH := $(build_prefix)/share/julia +export JULIA_DEPOT_PATH := $(build_prefix)/share/julia $(JULIA_DEPOT_PATH): mkdir -p $@ +print-depot-path: + @$(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e '@show Base.DEPOT_PATH') + STDLIBS := ArgTools Artifacts Base64 CRC32c FileWatching Libdl NetworkOptions SHA Serialization \ GMP_jll LLVMLibUnwind_jll LibUV_jll LibUnwind_jll MbedTLS_jll OpenLibm_jll PCRE2_jll \ Zlib_jll dSFMT_jll libLLVM_jll libblastrampoline_jll OpenBLAS_jll Printf Random Tar \ From fa6db2fe7651d90b8376c197b8b47c89e284865b Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Mon, 27 Mar 2023 00:28:17 +0000 Subject: [PATCH 385/775] codegen: decide opaque pointer usage at runtime (#49128) Co-authored-by: Prem Chintalapudi --- src/cgutils.cpp | 14 ++------------ src/codegen.cpp | 8 -------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 6453798593e5b..c0f4d3a7da794 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -902,9 +902,8 @@ static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const // If the types are small and simple, use load and store directly. // Going through memcpy can cause LLVM (e.g. SROA) to create bitcasts between float and int // that interferes with other optimizations. -#ifndef JL_LLVM_OPAQUE_POINTERS // TODO: Restore this for opaque pointers? Needs extra type information from the caller. - if (sz <= 64) { + if (ctx.builder.getContext().supportsTypedPointers() && sz <= 64) { // The size limit is arbitrary but since we mainly care about floating points and // machine size vectors this should be enough. const DataLayout &DL = jl_Module->getDataLayout(); @@ -942,7 +941,6 @@ static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const return; } } -#endif ++EmittedMemcpys; // the memcpy intrinsic does not allow to specify different alias tags @@ -3292,13 +3290,11 @@ static void recursively_adjust_ptr_type(llvm::Value *Val, unsigned FromAS, unsig IntrinsicInst *call = cast(User); call->setCalledFunction(mangleIntrinsic(call)); } -#ifndef JL_LLVM_OPAQUE_POINTERS else if (isa(User)) { BitCastInst *Inst = cast(User); Inst->mutateType(PointerType::getWithSamePointeeType(cast(Inst->getType()), ToAS)); recursively_adjust_ptr_type(Inst, FromAS, ToAS); } -#endif } } @@ -3342,9 +3338,7 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo, bool is_promotab box = emit_allocobj(ctx, jl_datatype_size(jt), literal_pointer_val(ctx, (jl_value_t*)jt)); Value *decayed = decay_derived(ctx, box); AllocaInst *originalAlloca = cast(vinfo.V); -#ifndef JL_LLVM_OPAQUE_POINTERS - decayed = maybe_bitcast(ctx, decayed, PointerType::get(originalAlloca->getType()->getPointerElementType(), AddressSpace::Derived)); -#endif + decayed = maybe_bitcast(ctx, decayed, PointerType::getWithSamePointeeType(originalAlloca->getType(), AddressSpace::Derived)); // Warning: Very illegal IR here temporarily originalAlloca->mutateType(decayed->getType()); recursively_adjust_ptr_type(originalAlloca, 0, AddressSpace::Derived); @@ -3735,11 +3729,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg // avoid unboxing the argument explicitly // and use memcpy instead Instruction *inst; -#ifndef JL_LLVM_OPAQUE_POINTERS dest = inst = cast(ctx.builder.CreateConstInBoundsGEP2_32(lt, strct, 0, llvm_idx)); -#else - dest = inst = cast(ctx.builder.CreateConstInBoundsGEP1_32(getInt8Ty(ctx.builder.getContext()), strct, offs)); -#endif // Our promotion point needs to come before // A) All of our arguments' promotion points // B) Any instructions we insert at any of our arguments' promotion points diff --git a/src/codegen.cpp b/src/codegen.cpp index b7d1bae7411a6..6b573051f3b97 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1201,11 +1201,7 @@ static const auto julia_call = new JuliaFunction{ [](LLVMContext &C) { auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); return FunctionType::get(T_prjlvalue, -#ifdef JL_LLVM_OPAQUE_POINTERS - {PointerType::get(C, 0), -#else {get_func_sig(C)->getPointerTo(), -#endif T_prjlvalue}, // %f true); }, // %args get_attrs_basic, @@ -1218,11 +1214,7 @@ static const auto julia_call2 = new JuliaFunction{ [](LLVMContext &C) { auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); return FunctionType::get(T_prjlvalue, -#ifdef JL_LLVM_OPAQUE_POINTERS - {PointerType::get(C, 0), -#else {get_func2_sig(C)->getPointerTo(), -#endif T_prjlvalue, // %arg1 T_prjlvalue}, // %f true); }, // %args From 6b934f91d1b9c50c5b783b6aa36cf2648999461c Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Mon, 27 Mar 2023 10:28:49 -0400 Subject: [PATCH 386/775] Add ITTAPI source for offline (#49022) * Add ITTAPI sources for offline build Co-authored-by: Milan Bouchet-Valat --- Make.inc | 9 +++++++ THIRDPARTY.md | 4 ++++ contrib/refresh_checksums.mk | 2 +- deps/Makefile | 13 +++++++++- deps/checksums/ittapi | 2 ++ deps/ittapi.mk | 43 ++++++++++++++++++++++++++++++++++ deps/ittapi.version | 3 +++ deps/llvm.mk | 7 +++++- doc/src/devdocs/build/build.md | 2 ++ 9 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/ittapi create mode 100644 deps/ittapi.mk create mode 100644 deps/ittapi.version diff --git a/Make.inc b/Make.inc index bf71e6ee3a21e..1285de0dc3590 100644 --- a/Make.inc +++ b/Make.inc @@ -89,6 +89,9 @@ WITH_GC_DEBUG_ENV := 0 # Enable DTrace support WITH_DTRACE := 0 +# Enable ITTAPI integration +WITH_ITTAPI := 0 + # Enable Tracy support WITH_TRACY := 0 @@ -728,6 +731,12 @@ JCFLAGS += -DUSE_DTRACE DTRACE := dtrace endif +ifeq ($(WITH_ITTAPI), 1) +JCXXFLAGS += -DUSE_ITTAPI +JCFLAGS += -DUSE_ITTAPI +LIBITTAPI:=-littnotify +endif + ifeq ($(WITH_TRACY), 1) JCXXFLAGS += -DUSE_TRACY -DTRACY_ENABLE JCFLAGS += -DUSE_TRACY -DTRACY_ENABLE diff --git a/THIRDPARTY.md b/THIRDPARTY.md index 4a35bbdb1b7ce..51950d9e2c6a1 100644 --- a/THIRDPARTY.md +++ b/THIRDPARTY.md @@ -24,6 +24,10 @@ own licenses: - [LLVM](https://releases.llvm.org/12.0.1/LICENSE.TXT) [APACHE 2.0 with LLVM Exception] - [UTF8PROC](https://github.com/JuliaStrings/utf8proc) [MIT] +and optionally: + +- [ITTAPI](https://github.com/intel/ittapi/blob/master/LICENSES/BSD-3-Clause.txt) [BSD-3] + Julia's `stdlib` uses the following external libraries, which have their own licenses: - [DSFMT](https://github.com/MersenneTwister-Lab/dSFMT/blob/master/LICENSE.txt) [BSD-3] diff --git a/contrib/refresh_checksums.mk b/contrib/refresh_checksums.mk index 710ecbdf121be..f67088141ccd4 100644 --- a/contrib/refresh_checksums.mk +++ b/contrib/refresh_checksums.mk @@ -28,7 +28,7 @@ BB_PROJECTS=mbedtls libssh2 nghttp2 mpfr curl libgit2 pcre libuv unwind llvmunwi BB_GCC_EXPANDED_PROJECTS=openblas csl BB_CXX_EXPANDED_PROJECTS=gmp llvm clang llvm-tools lld # These are non-BB source-only deps -NON_BB_PROJECTS=patchelf mozillacert lapack libwhich utf8proc +NON_BB_PROJECTS=patchelf mozillacert lapack libwhich utf8proc ittapi ifneq ($(VERBOSE),1) QUIET_MAKE := -s diff --git a/deps/Makefile b/deps/Makefile index 0d013272ad23d..62bb85e72c492 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -155,6 +155,16 @@ ifeq ($(USE_SYSTEM_P7ZIP), 0) DEP_LIBS += p7zip endif +ifeq ($(USE_INTEL_JITEVENTS), 1) +ifeq ($(USE_BINARYBUILDER_LLVM), 0) +DEP_LIBS += ittapi +endif +endif + +ifeq ($(WITH_ITTAPI),1) +DEP_LIBS += ittapi +endif + # Only compile standalone LAPACK if we are not using OpenBLAS. # OpenBLAS otherwise compiles LAPACK as part of its build. @@ -178,7 +188,7 @@ DEP_LIBS_STAGED := $(DEP_LIBS) DEP_LIBS_STAGED_ALL := llvm llvm-tools clang llvmunwind unwind libuv pcre \ openlibm dsfmt blastrampoline openblas lapack gmp mpfr patchelf utf8proc \ objconv mbedtls libssh2 nghttp2 curl libgit2 libwhich zlib p7zip csl \ - libsuitesparse lld libtracyclient + libsuitesparse lld libtracyclient ittapi DEP_LIBS_ALL := $(DEP_LIBS_STAGED_ALL) ifneq ($(USE_BINARYBUILDER_OPENBLAS),0) @@ -213,6 +223,7 @@ distcleanall: $(addprefix distclean-, $(DEP_LIBS_ALL)) getall: $(addprefix get-, $(DEP_LIBS_ALL)) include $(SRCDIR)/csl.mk +include $(SRCDIR)/ittapi.mk include $(SRCDIR)/llvm.mk include $(SRCDIR)/libuv.mk include $(SRCDIR)/pcre.mk diff --git a/deps/checksums/ittapi b/deps/checksums/ittapi new file mode 100644 index 0000000000000..896e44d8f2907 --- /dev/null +++ b/deps/checksums/ittapi @@ -0,0 +1,2 @@ +ittapi-0014aec56fea2f30c1374f40861e1bccdd53d0cb.tar.gz/md5/932501cdb0e1c7841e23c12da7740419 +ittapi-0014aec56fea2f30c1374f40861e1bccdd53d0cb.tar.gz/sha512/4dd3343837398ada0cdcdaaff630d8d91738d166897d86b77770facde30da99dbb90931b58a4a887399e6bc9a7a1c245057d0a0f63762230d577d71da871701f diff --git a/deps/ittapi.mk b/deps/ittapi.mk new file mode 100644 index 0000000000000..1a47c3ae89390 --- /dev/null +++ b/deps/ittapi.mk @@ -0,0 +1,43 @@ +## ittapi ## +include $(SRCDIR)/ittapi.version + +ITTAPI_GIT_URL := https://github.com/intel/ittapi.git +ITTAPI_TAR_URL = https://api.github.com/repos/intel/ittapi/tarball/$1 +$(eval $(call git-external,ittapi,ITTAPI,CMakeLists.txt,,$(SRCCACHE))) + +ITTAPI_OPTS := $(CMAKE_COMMON) -DCMAKE_BUILD_TYPE=Release -DITT_API_IPT_SUPPORT= -DITT_API_FORTRAN_SUPPORT=0 + +$(BUILDDIR)/$(ITTAPI_SRC_DIR)/build-configured: $(SRCCACHE)/$(ITTAPI_SRC_DIR)/source-extracted + mkdir -p $(dir $@) + cd $(dir $@) && \ + $(CMAKE) $(dir $<) $(ITTAPI_OPTS) + echo 1 > $@ + +$(BUILDDIR)/$(ITTAPI_SRC_DIR)/build-compiled: $(BUILDDIR)/$(ITTAPI_SRC_DIR)/build-configured + $(MAKE) -C $(dir $<) + echo 1 > $@ + +define ITTAPI_INSTALL + mkdir -p $2/$$(build_libdir) + mkdir -p $2/$$(build_includedir)/ittapi + cp -a $1/bin/libittnotify.a $2/$$(build_libdir) + cp -a $1/bin/libjitprofiling.a $2/$$(build_libdir) + # cp -a $1/bin/libadvisor.a $2/$$(build_libdir) + cp -a $(SRCCACHE)/$(ITTAPI_SRC_DIR)/include/ittnotify.h $2/$$(build_includedir)/ittapi/ + cp -a $(SRCCACHE)/$(ITTAPI_SRC_DIR)/include/ittnotify-zca.h $2/$$(build_includedir)/ittapi/ + cp -a $(SRCCACHE)/$(ITTAPI_SRC_DIR)/include/jitprofiling.h $2/$$(build_includedir)/ittapi/ +endef + +$(eval $(call staged-install, \ + ittapi,$(ITTAPI_SRC_DIR), \ + ITTAPI_INSTALL,,,)) + +get-ittapi: $(ITTAPI_SRC_FILE) +extract-ittapi: $(SRCCACHE)/$(ITTAPI_SRC_DIR)/source-extracted +configure-ittapi: $(BUILDDIR)/$(ITTAPI_SRC_DIR)/build-configured +compile-ittapi: $(BUILDDIR)/$(ITTAPI_SRC_DIR)/build-compiled +fastcheck-ittapi: #none +check-ittapi: #none + +clean-ittapi: + -rm -f $(BUILDDIR)/$(ITTAPI_SRC_DIR)/build-compiled $(build_libdir)/libopenlibm.a diff --git a/deps/ittapi.version b/deps/ittapi.version new file mode 100644 index 0000000000000..81afb6de2add2 --- /dev/null +++ b/deps/ittapi.version @@ -0,0 +1,3 @@ +## source build +ITTAPI_BRANCH=v3.24.0 +ITTAPI_SHA1=0014aec56fea2f30c1374f40861e1bccdd53d0cb diff --git a/deps/llvm.mk b/deps/llvm.mk index d7ff5bd71e980..83b9a66ec608e 100644 --- a/deps/llvm.mk +++ b/deps/llvm.mk @@ -120,7 +120,7 @@ ifeq ($(USE_LLVM_SHLIB),1) LLVM_CMAKE += -DLLVM_BUILD_LLVM_DYLIB:BOOL=ON -DLLVM_LINK_LLVM_DYLIB:BOOL=ON endif ifeq ($(USE_INTEL_JITEVENTS), 1) -LLVM_CMAKE += -DLLVM_USE_INTEL_JITEVENTS:BOOL=ON +LLVM_CMAKE += -DLLVM_USE_INTEL_JITEVENTS:BOOL=ON -DITTAPI_SOURCE_DIR=$(SRCCACHE)/$(ITTAPI_SRC_DIR) endif # USE_INTEL_JITEVENTS ifeq ($(USE_OPROFILE_JITEVENTS), 1) @@ -286,6 +286,11 @@ configure-llvm: $(LLVM_BUILDDIR_withtype)/build-configured compile-llvm: $(LLVM_BUILDDIR_withtype)/build-compiled fastcheck-llvm: #none check-llvm: $(LLVM_BUILDDIR_withtype)/build-checked + +ifeq ($(USE_INTEL_JITEVENTS),1) +extract-llvm: $(SRCCACHE)/$(ITTAPI_SRC_DIR)/source-extracted +endif + #todo: LLVM make check target is broken on julia.mit.edu (and really slow elsewhere) else # USE_BINARYBUILDER_LLVM diff --git a/doc/src/devdocs/build/build.md b/doc/src/devdocs/build/build.md index e812e383c0592..6d5d4a54c8d64 100644 --- a/doc/src/devdocs/build/build.md +++ b/doc/src/devdocs/build/build.md @@ -187,6 +187,7 @@ uses are listed in [`deps/$(libname).version`](https://github.com/JuliaLang/juli - **[mbedtls]** — library used for cryptography and transport layer security, used by libssh2 - **[utf8proc]** — a library for processing UTF-8 encoded Unicode strings. - **[LLVM libunwind]** — LLVM's fork of [libunwind], a library that determines the call-chain of a program. +- **[ITTAPI]** — Intel's Instrumentation and Tracing Technology and Just-In-Time API. [GNU make]: https://www.gnu.org/software/make [patch]: https://www.gnu.org/software/patch @@ -222,6 +223,7 @@ uses are listed in [`deps/$(libname).version`](https://github.com/JuliaLang/juli [pkg-config]: https://www.freedesktop.org/wiki/Software/pkg-config/ [powershell]: https://docs.microsoft.com/en-us/powershell/scripting/wmf/overview [which]: https://carlowood.github.io/which/ +[ITTAPI]: https://github.com/intel/ittapi ## Build dependencies From 39fd7ddbeeaee6335ab15adc8e40d00f8aae5389 Mon Sep 17 00:00:00 2001 From: apaz Date: Mon, 27 Mar 2023 22:29:23 -0500 Subject: [PATCH 387/775] Make sure read hasn't failed before updating position. (#49158) I noticed this when I was yoinking the code for my own project. When `read()` fails and `errno` is `EINTR`, it re-reads from the wrong position. --- cli/loader_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index b959d176bb382..e2f615c684637 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -227,13 +227,13 @@ static void read_wrapper(int fd, char **ret, size_t *ret_len) size_t have_read = 0; while (1) { ssize_t n = read(fd, buf + have_read, len - have_read); - have_read += n; if (n == 0) break; if (n == -1 && errno != EINTR) { perror("(julia) libstdcxxprobe read"); exit(1); } if (n == -1 && errno == EINTR) continue; + have_read += n; if (have_read == len) { buf = (char *)realloc(buf, 1 + (len *= 2)); if (!buf) { From 7341fb9517d290be02fdc54ae453999843a0dc7e Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Tue, 28 Mar 2023 13:15:43 +0800 Subject: [PATCH 388/775] Subtype: Add a fastpath for nested constructor with identity name. (#49159) * Subtype: Add a fastpath for nested constructor with identity name. * Update src/subtype.c --------- Co-authored-by: Jameson Nash --- src/subtype.c | 13 +++++++++++++ test/subtype.jl | 11 ++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/subtype.c b/src/subtype.c index 8760fa94e351d..c74998a244815 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1470,9 +1470,22 @@ static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) (is_definite_length_tuple_type(x) && is_indefinite_length_tuple_type(y))) return 0; + if (jl_is_datatype(x) && jl_is_datatype(y)) { + // Fastpath for nested constructor. Skip the unneeded `>:` check. + // Note: since there is no changes to the environment or union stack implied by `x` or `y`, this will simply forward to calling + // `forall_exists_equal(xi, yi, e)` on each parameter `(xi, yi)` of `(x, y)`, + // which means this subtype call will give the same result for `subtype(x, y)` and `subtype(y, x)`. + jl_datatype_t *xd = (jl_datatype_t*)x, *yd = (jl_datatype_t*)y; + if (xd->name != yd->name) + return 0; + if (xd->name != jl_tuple_typename) + return subtype(x, y, e, 2); + } + if ((jl_is_uniontype(x) && jl_is_uniontype(y))) { // For 2 unions, first try a more efficient greedy algorithm that compares the unions // componentwise. If failed, `exists_subtype` would memorize that this branch should be skipped. + // Note: this is valid because the normal path checks `>:` locally. if (pick_union_decision(e, 1) == 0) { return forall_exists_equal(((jl_uniontype_t *)x)->a, ((jl_uniontype_t *)y)->a, e) && forall_exists_equal(((jl_uniontype_t *)x)->b, ((jl_uniontype_t *)y)->b, e); diff --git a/test/subtype.jl b/test/subtype.jl index e2bb49a6e0123..226aa24770f42 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2415,7 +2415,7 @@ abstract type P47654{A} end # issue 22123 t1 = Ref{Ref{Ref{Union{Int64, T}}} where T} t2 = Ref{Ref{Ref{Union{T, S}}} where T} where S - @test_broken t1 <: t2 + @test t1 <: t2 # issue 21153 @test_broken (Tuple{T1,T1} where T1<:(Val{T2} where T2)) <: (Tuple{Val{S},Val{S}} where S) @@ -2467,3 +2467,12 @@ end #issue 48961 @test !<:(Type{Union{Missing, Int}}, Type{Union{Missing, Nothing, Int}}) + +#issue 49127 +struct F49127{m,n} <: Function end +let a = [TypeVar(:V, Union{}, Function) for i in 1:32] + b = a[1:end-1] + S = foldr((v, d) -> UnionAll(v, d), a; init = foldl((i, j) -> F49127{i, j}, a)) + T = foldr((v, d) -> UnionAll(v, d), b; init = foldl((i, j) -> F49127{i, j}, b)) + @test S <: T +end From 2da4c3ec39300243ade319b96359891f06b27037 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 28 Mar 2023 10:19:44 -0400 Subject: [PATCH 389/775] effects: Do not over-taint :terminates in abstract cycle (#49119) We already infer non-termination as part of the conservative effects we assume at the point in the call-graph that recursion is detected. As a result, it should be sound to allow this to propagate through the Effects system naturally rather than eagerly marking our callers as non-terminating. Doing this is important to avoid tainting non-termination from purely abstract cycles, where inference is forced to analyze methods that do not correspond to real calls at runtime, such as `Base._return_type`. Resolves #48983. --- base/compiler/typeinfer.jl | 4 ---- test/compiler/effects.jl | 11 +++++++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 7bebd9d7d64e5..38859f62cbbeb 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -779,13 +779,9 @@ function merge_call_chain!(interp::AbstractInterpreter, parent::InferenceState, # then add all backedges of parent <- parent.parent # and merge all of the callers into ancestor.callers_in_cycle # and ensure that walking the parent list will get the same result (DAG) from everywhere - # Also taint the termination effect, because we can no longer guarantee the absence - # of recursion. - merge_effects!(interp, parent, Effects(EFFECTS_TOTAL; terminates=false)) while true add_cycle_backedge!(parent, child, parent.currpc) union_caller_cycle!(ancestor, child) - merge_effects!(interp, child, Effects(EFFECTS_TOTAL; terminates=false)) child = parent child === ancestor && break parent = child.parent::InferenceState diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index 03f60062a9ebe..53c558cc95106 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -805,5 +805,16 @@ unknown_sparam_nothrow2(x::Ref{Ref{T}}) where T = (T; nothing) @test Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_nothrow1, (Ref,))) @test Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_nothrow2, (Ref{Ref{T}} where T,))) +# purely abstract recursion should not taint :terminates +# https://github.com/JuliaLang/julia/issues/48983 +abstractly_recursive1() = abstractly_recursive2() +abstractly_recursive2() = (Core.Compiler._return_type(abstractly_recursive1, Tuple{}); 1) +abstractly_recursive3() = abstractly_recursive2() +@test Core.Compiler.is_terminates(Base.infer_effects(abstractly_recursive3, ())) +actually_recursive1(x) = actually_recursive2(x) +actually_recursive2(x) = (x <= 0) ? 1 : actually_recursive1(x - 1) +actually_recursive3(x) = actually_recursive2(x) +@test !Core.Compiler.is_terminates(Base.infer_effects(actually_recursive3, (Int,))) + # https://github.com/JuliaLang/julia/issues/48856 @test Base.ismutationfree(Vector{Any}) == false From 94c884eb747790c7f1f401f58c4b1a31c98aee5a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 28 Mar 2023 11:34:41 -0400 Subject: [PATCH 390/775] simplify default outer constructor for struct (#49146) Removes the `convert` calls in new for default outer constructor. I don't expect this will have a visible difference in behavior, but may help avoid some edges and specializations improving latency. --- src/julia-syntax.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 2a4e95ab1da86..b82d8ef0cbdf8 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -788,7 +788,7 @@ (map (lambda (b) (cons 'var-bounds b)) bounds)) (block ,@locs - (call (curly ,name ,@params) ,@field-names))))) + (new (curly ,name ,@params) ,@field-names))))) (define (num-non-varargs args) (count (lambda (a) (not (vararg? a))) args)) From 7d2491d6ff8f9c07f6b1874cd7735d22744b7e6e Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 28 Mar 2023 11:35:46 -0400 Subject: [PATCH 391/775] simplify default inner constructor for struct without types (#49137) Removes the `convert` calls in new for default constructor of structs defined without any types declared. --- src/julia-syntax.scm | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index b82d8ef0cbdf8..99d51f2ec3432 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -751,8 +751,18 @@ (define (default-inner-ctors name field-names field-types params bounds locs) (let* ((field-names (safe-field-names field-names field-types)) - (any-ctor + (all-ctor (if (null? params) + ;; definition with exact types for all arguments + `(function (call ,name + ,@(map make-decl field-names field-types)) + (block + ,@locs + (new (outerref ,name) ,@field-names))) + #f)) + (any-ctor (if (or (not all-ctor) (any (lambda (t) (not (equal? t '(core Any)))) + field-types)) ;; definition with Any for all arguments + ;; only if any field type is not Any, checked at runtime `(function (call (|::| |#ctor-self#| ,(with-wheres `(curly (core Type) ,(if (pair? params) @@ -762,23 +772,18 @@ ,@field-names) (block ,@locs - (call new ,@field-names))))) - (if (and (null? params) (any (lambda (t) (not (equal? t '(core Any)))) - field-types)) - (list - ;; definition with field types for all arguments - ;; only if any field type is not Any, checked at runtime - `(if ,(foldl (lambda (t u) - `(&& ,u (call (core ===) (core Any) ,t))) - `(call (core ===) (core Any) ,(car field-types)) - (cdr field-types)) - (block) - (function (call ,name - ,@(map make-decl field-names field-types)) - (block - ,@locs - (new (outerref ,name) ,@field-names)))) - any-ctor) + (call new ,@field-names))) ; this will add convert calls later + #f))) + (if all-ctor + (if any-ctor + (list all-ctor + `(if ,(foldl (lambda (t u) + `(&& ,u (call (core ===) (core Any) ,t))) + `(call (core ===) (core Any) ,(car field-types)) + (cdr field-types)) + '(block) + ,any-ctor)) + (list all-ctor)) (list any-ctor)))) (define (default-outer-ctor name field-names field-types params bounds locs) From 045a392cdb7f4e79e4519fc050b85b7c12d09d35 Mon Sep 17 00:00:00 2001 From: Robert Feldt Date: Tue, 28 Mar 2023 18:11:08 +0200 Subject: [PATCH 392/775] doc: clarify that the unsigned type is promoted to (#48973) Clarify that the unsigned type is promoted to, if the types differ only in signedness. This choice was explained by Karpinski in https://github.com/JuliaLang/julia/issues/9292#issuecomment-943740160 but wasn't fully clear in the documentation. --- doc/src/manual/conversion-and-promotion.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/src/manual/conversion-and-promotion.md b/doc/src/manual/conversion-and-promotion.md index 82073c1446bf8..f0c156f21ea62 100644 --- a/doc/src/manual/conversion-and-promotion.md +++ b/doc/src/manual/conversion-and-promotion.md @@ -233,11 +233,11 @@ julia> promote(1 + 2im, 3//4) ``` Floating-point values are promoted to the largest of the floating-point argument types. Integer -values are promoted to the larger of either the native machine word size or the largest integer -argument type. Mixtures of integers and floating-point values are promoted to a floating-point -type big enough to hold all the values. Integers mixed with rationals are promoted to rationals. -Rationals mixed with floats are promoted to floats. Complex values mixed with real values are -promoted to the appropriate kind of complex value. +values are promoted to the largest of the integer argument types. If the types are the same size +but differ in signedness, the unsigned type is chosen. Mixtures of integers and floating-point +values are promoted to a floating-point type big enough to hold all the values. Integers mixed +with rationals are promoted to rationals. Rationals mixed with floats are promoted to floats. +Complex values mixed with real values are promoted to the appropriate kind of complex value. That is really all there is to using promotions. The rest is just a matter of clever application, the most typical "clever" application being the definition of catch-all methods for numeric operations From d8fa3c85600518045e0ac939ab706cfe0164c7a0 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Tue, 28 Mar 2023 14:23:08 -0400 Subject: [PATCH 393/775] Mark llvm::Any::TypeId as global in julia.expmap (#49124) The dynamic linker needs to unify `llvm::Any::TypeId` across DSOs. In our case `libjulia-codegen` and `libLLVM`. See https://github.com/llvm/llvm-project/blob/2bc4c3e920ee078ef2879b00c40440e0867f0b9e/llvm/include/llvm/ADT/Any.h#L30 Fixes: #49121 --- src/julia.expmap | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/julia.expmap b/src/julia.expmap index 35cc5eac48b6a..7df813498182b 100644 --- a/src/julia.expmap +++ b/src/julia.expmap @@ -35,6 +35,9 @@ LLVMExtra*; llvmGetPassPluginInfo; + /* Make visible so that linker will merge duplicate definitions across DSO boundaries */ + _ZN4llvm3Any6TypeId*; + /* freebsd */ environ; __progname; From 68ab859b32e0770eeb5aab6d650c8a7d8b1410ab Mon Sep 17 00:00:00 2001 From: Orestis Ousoultzoglou Date: Tue, 28 Mar 2023 23:49:11 +0300 Subject: [PATCH 394/775] Format arg-name in ast.scm (#49163) --- src/ast.scm | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ast.scm b/src/ast.scm index bbb2180a8a92f..88220c03a7aa6 100644 --- a/src/ast.scm +++ b/src/ast.scm @@ -226,13 +226,13 @@ "")) "") (string.rep " " ilvl) "end")) - ((do) - (let ((call (cadr e)) - (args (cdr (cadr (caddr e)))) - (body (caddr (caddr e)))) - (deparse-block (string (deparse call) " do" (if (null? args) "" " ") - (deparse-arglist args)) - (cdr body) ilvl))) + ((do) + (let ((call (cadr e)) + (args (cdr (cadr (caddr e)))) + (body (caddr (caddr e)))) + (deparse-block (string (deparse call) " do" (if (null? args) "" " ") + (deparse-arglist args)) + (cdr body) ilvl))) ((struct) (string (if (equal? (cadr e) '(true)) "mutable " "") "struct " @@ -329,8 +329,8 @@ (else (case (car v) ((...) - (arg-name (cadr v)) ;; to check for errors - (decl-var (cadr v))) + (arg-name (cadr v)) ;; to check for errors + (decl-var (cadr v))) ((|::|) (if (not (symbol? (cadr v))) (bad-formal-argument (cadr v))) From 58b7e0ad188ee2434697e23e3773700ea3c95cb2 Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Tue, 28 Mar 2023 21:27:52 +0000 Subject: [PATCH 395/775] Emit bitcode, reserve more space for native code dumping (#49173) --- src/aotcompile.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 76d8967133629..db0d284df4254 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -945,7 +945,7 @@ struct ShardTimers { }; // Perform the actual optimization and emission of the output files -static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *outputs, StringRef *names, +static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *outputs, const std::string *names, NewArchiveMember *unopt, NewArchiveMember *opt, NewArchiveMember *obj, NewArchiveMember *asm_, ShardTimers &timers, unsigned shardidx) { auto TM = std::unique_ptr( @@ -965,6 +965,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out AnalysisManagers AM{*TM, PB, OptimizationLevel::O0}; ModulePassManager MPM; MPM.addPass(BitcodeWriterPass(OS)); + MPM.run(M, AM.MAM); *unopt = NewArchiveMember(MemoryBufferRef(*outputs++, *names++)); timers.unopt.stopTimer(); } @@ -1029,6 +1030,7 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out AnalysisManagers AM{*TM, PB, OptimizationLevel::O0}; ModulePassManager MPM; MPM.addPass(BitcodeWriterPass(OS)); + MPM.run(M, AM.MAM); *opt = NewArchiveMember(MemoryBufferRef(*outputs++, *names++)); timers.opt.stopTimer(); } @@ -1224,6 +1226,8 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o unsigned outcount = unopt_out + opt_out + obj_out + asm_out; assert(outcount); outputs.resize(outputs.size() + outcount * threads * 2); + auto names_start = outputs.data() + outputs.size() - outcount * threads * 2; + auto outputs_start = names_start + outcount * threads; unopt.resize(unopt.size() + unopt_out * threads); opt.resize(opt.size() + opt_out * threads); obj.resize(obj.size() + obj_out * threads); @@ -1264,7 +1268,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o } } for (unsigned i = 0; i < threads; ++i) { - auto start = &outputs[outputs.size() - outcount * threads * 2 + i * outcount]; + auto start = names_start + i * outcount; auto istr = std::to_string(i); if (unopt_out) *start++ = (name + "_unopt#" + istr + ".bc").str(); @@ -1278,10 +1282,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o // Single-threaded case if (threads == 1) { output_timer.startTimer(); - SmallVector names; - for (unsigned i = outputs.size() - outcount * 2; i < outputs.size() - outcount; ++i) - names.push_back(outputs[i]); - add_output_impl(M, TM, outputs.data() + outputs.size() - outcount, names.data(), + add_output_impl(M, TM, outputs_start, names_start, unopt_out ? unopt.data() + unopt.size() - 1 : nullptr, opt_out ? opt.data() + opt.size() - 1 : nullptr, obj_out ? obj.data() + obj.size() - 1 : nullptr, @@ -1318,7 +1319,6 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o output_timer.startTimer(); - auto outstart = outputs.data() + outputs.size() - outcount * threads; auto unoptstart = unopt_out ? unopt.data() + unopt.size() - threads : nullptr; auto optstart = opt_out ? opt.data() + opt.size() - threads : nullptr; auto objstart = obj_out ? obj.data() + obj.size() - threads : nullptr; @@ -1347,11 +1347,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o dropUnusedDeclarations(*M); timers[i].deletion.stopTimer(); - SmallVector names(outcount); - for (unsigned j = 0; j < outcount; ++j) - names[j] = outputs[i * outcount + j]; - - add_output_impl(*M, TM, outstart + i * outcount, names.data(), + add_output_impl(*M, TM, outputs_start + i * outcount, names_start + i * outcount, unoptstart ? unoptstart + i : nullptr, optstart ? optstart + i : nullptr, objstart ? objstart + i : nullptr, @@ -1578,7 +1574,7 @@ void jl_dump_native_impl(void *native_code, // DO NOT DELETE, this is necessary to ensure memorybuffers // have a stable backing store for both their object files and // their names - outputs.reserve(threads * (!!unopt_bc_fname + !!bc_fname + !!obj_fname + !!asm_fname) * 2 + 2); + outputs.reserve((threads + 1) * (!!unopt_bc_fname + !!bc_fname + !!obj_fname + !!asm_fname) * 2); auto compile = [&](Module &M, StringRef name, unsigned threads) { add_output( M, *SourceTM, outputs, name, From f79fdf942cbc4c83351866277953025f3344fbf9 Mon Sep 17 00:00:00 2001 From: Paul Berg Date: Wed, 29 Mar 2023 00:47:39 +0200 Subject: [PATCH 396/775] fix several invalid code blocks in docstrings (#49174) --- base/file.jl | 2 +- base/namedtuple.jl | 1 + base/shell.jl | 4 ++-- stdlib/LinearAlgebra/src/svd.jl | 1 - stdlib/REPL/src/REPL.jl | 15 ++++++++------- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/base/file.jl b/base/file.jl index 921119c6074f7..ba50a9deb3335 100644 --- a/base/file.jl +++ b/base/file.jl @@ -844,7 +844,7 @@ julia> readdir("base", join=true) ⋮ "base/version_git.sh" "base/views.jl" - "base/weakkeydict.jl"``` + "base/weakkeydict.jl" julia> readdir(abspath("base"), join=true) 145-element Array{String,1}: diff --git a/base/namedtuple.jl b/base/namedtuple.jl index ad36607660960..e2a78c6ea0444 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -69,6 +69,7 @@ The name-value pairs can also be provided by splatting a named tuple or any iterator that yields two-value collections holding each a symbol as first value: +```jldoctest julia> keys = (:a, :b, :c); values = (1, 2, 3); julia> NamedTuple{keys}(values) diff --git a/base/shell.jl b/base/shell.jl index 7c973ab289c7f..5bfd11fb46d29 100644 --- a/base/shell.jl +++ b/base/shell.jl @@ -361,12 +361,12 @@ cmdargs = Base.shell_escape_wincmd("Passing args with %cmdargs% works 100%!") run(setenv(`cmd /C echo %cmdargs%`, "cmdargs" => cmdargs)) ``` -!warning +!!! warning The argument parsing done by CMD when calling batch files (either inside `.bat` files or as arguments to them) is not fully compatible with the output of this function. In particular, the processing of `%` is different. -!important +!!! important Due to a peculiar behavior of the CMD parser/interpreter, each command after a literal `|` character (indicating a command pipeline) must have `shell_escape_wincmd` applied twice since it will be parsed twice by CMD. diff --git a/stdlib/LinearAlgebra/src/svd.jl b/stdlib/LinearAlgebra/src/svd.jl index 86f322524d13d..c1b886f616f02 100644 --- a/stdlib/LinearAlgebra/src/svd.jl +++ b/stdlib/LinearAlgebra/src/svd.jl @@ -213,7 +213,6 @@ Base.propertynames(F::SVD, private::Bool=false) = Return the singular values of `A`, saving space by overwriting the input. See also [`svdvals`](@ref) and [`svd`](@ref). -``` """ svdvals!(A::StridedMatrix{T}) where {T<:BlasFloat} = isempty(A) ? zeros(real(T), 0) : LAPACK.gesdd!('N', A)[2] svdvals!(A::StridedVector{T}) where {T<:BlasFloat} = svdvals!(reshape(A, (length(A), 1))) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index a6958db88f460..5149f1f30df8b 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -3,13 +3,14 @@ """ Run Evaluate Print Loop (REPL) - Example minimal code - ``` - import REPL - term = REPL.Terminals.TTYTerminal("dumb", stdin, stdout, stderr) - repl = REPL.LineEditREPL(term, true) - REPL.run_repl(repl) - ``` +Example minimal code + +```julia +import REPL +term = REPL.Terminals.TTYTerminal("dumb", stdin, stdout, stderr) +repl = REPL.LineEditREPL(term, true) +REPL.run_repl(repl) +``` """ module REPL From e95839f13ba314395e42b23d7a368867a637ea67 Mon Sep 17 00:00:00 2001 From: David Valente <74915610+DavidAfonsoValente@users.noreply.github.com> Date: Wed, 29 Mar 2023 02:09:26 +0100 Subject: [PATCH 397/775] Optimize reverse(::CartesianIndices, dims=...) (#49112) * Optimize reverse(::CartesianIndices, dims=...) Optimize reverse(::CartesianIndices, dims=...) Correct tests and add stability test Remove whitespaces in reversed CartesianIndices Make funcs constpropagable and refactor Add tests for reverse(CartesianIndices; dims=:) * Typo * fix ambiguity and const-propagation * Fix empty `dims` --------- Co-authored-by: N5N3 <2642243996@qq.com> --- base/multidimensional.jl | 24 +++++++++++++++++++++++- test/arrayops.jl | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index cad9b088acf50..70b7354c18bd2 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -509,8 +509,30 @@ module IteratorsMD end # reversed CartesianIndices iteration + @inline function Base._reverse(iter::CartesianIndices, ::Colon) + CartesianIndices(reverse.(iter.indices)) + end + + Base.@constprop :aggressive function Base._reverse(iter::CartesianIndices, dim::Integer) + 1 <= dim <= ndims(iter) || throw(ArgumentError(Base.LazyString("invalid dimension ", dim, " in reverse"))) + ndims(iter) == 1 && return Base._reverse(iter, :) + indices = iter.indices + return CartesianIndices(Base.setindex(indices, reverse(indices[dim]), dim)) + end + + Base.@constprop :aggressive function Base._reverse(iter::CartesianIndices, dims::Tuple{Vararg{Integer}}) + indices = iter.indices + # use `sum` to force const fold + dimrev = ntuple(i -> sum(==(i), dims; init = 0) == 1, Val(length(indices))) + length(dims) == sum(dimrev) || throw(ArgumentError(Base.LazyString("invalid dimensions ", dims, " in reverse"))) + length(dims) == length(indices) && return Base._reverse(iter, :) + indices′ = map((i, f) -> f ? reverse(i) : i, indices, dimrev) + return CartesianIndices(indices′) + end - Base.reverse(iter::CartesianIndices) = CartesianIndices(reverse.(iter.indices)) + # fix ambiguity with array.jl: + Base._reverse(iter::CartesianIndices{1}, dims::Tuple{Integer}) = + Base._reverse(iter, first(dims)) @inline function iterate(r::Reverse{<:CartesianIndices}) iterfirst = last(r.itr) diff --git a/test/arrayops.jl b/test/arrayops.jl index e7ac6a1132568..770cec3705038 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1705,6 +1705,39 @@ end @test istriu([1 2 0; 0 4 1]) end +#issue 49021 +@testset "reverse cartesian indices" begin + @test reverse(CartesianIndices((2, 3))) === CartesianIndices((2:-1:1, 3:-1:1)) + @test reverse(CartesianIndices((2:5, 3:7))) === CartesianIndices((5:-1:2, 7:-1:3)) + @test reverse(CartesianIndices((5:-1:2, 7:-1:3))) === CartesianIndices((2:1:5, 3:1:7)) +end + +@testset "reverse cartesian indices dim" begin + A = CartesianIndices((2, 3, 5:-1:1)) + @test reverse(A, dims=1) === CartesianIndices((2:-1:1, 3, 5:-1:1)) + @test reverse(A, dims=3) === CartesianIndices((2, 3, 1:1:5)) + @test_throws ArgumentError reverse(A, dims=0) + @test_throws ArgumentError reverse(A, dims=4) +end + +@testset "reverse cartesian indices multiple dims" begin + A = CartesianIndices((2, 3, 5:-1:1)) + @test reverse(A, dims=(1, 3)) === CartesianIndices((2:-1:1, 3, 1:1:5)) + @test reverse(A, dims=(3, 1)) === CartesianIndices((2:-1:1, 3, 1:1:5)) + @test_throws ArgumentError reverse(A, dims=(1, 2, 4)) + @test_throws ArgumentError reverse(A, dims=(0, 1, 2)) + @test_throws ArgumentError reverse(A, dims=(1, 1)) +end + +@testset "stability of const propagation" begin + A = CartesianIndices((2, 3, 5:-1:1)) + f1(x) = reverse(x; dims=1) + f2(x) = reverse(x; dims=(1, 3)) + @test @inferred(f1(A)) === CartesianIndices((2:-1:1, 3, 5:-1:1)) + @test @inferred(f2(A)) === CartesianIndices((2:-1:1, 3, 1:1:5)) + @test @inferred(reverse(A; dims=())) === A +end + # issue 4228 let A = [[i i; i i] for i=1:2] @test cumsum(A) == Any[[1 1; 1 1], [3 3; 3 3]] From 887ff5a0ddc700f719ccc1c0687782b4863f8c3b Mon Sep 17 00:00:00 2001 From: "Zachary P. Christensen" Date: Wed, 29 Mar 2023 00:52:08 -0400 Subject: [PATCH 398/775] Fix LLVMPtr subtyping --- src/jltypes.c | 5 +++-- test/llvmcall.jl | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/jltypes.c b/src/jltypes.c index 0439c8d034e6b..fd2f1eb17f8f9 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2739,8 +2739,9 @@ void jl_init_types(void) JL_GC_DISABLED jl_pointer_typename = ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_pointer_type))->name; // LLVMPtr{T, AS} where {T, AS} - tv = jl_svec2(tvar("T"), tvar("AS")); - jl_svec_t *tv_base = jl_svec1(tvar("T")); + jl_tvar_t *elvar = tvar("T"); + tv = jl_svec2(elvar, tvar("AS")); + jl_svec_t *tv_base = jl_svec1(elvar); jl_llvmpointer_type = (jl_unionall_t*) jl_new_primitivetype((jl_value_t*)jl_symbol("LLVMPtr"), core, (jl_datatype_t*)jl_apply_type((jl_value_t*)jl_ref_type, jl_svec_data(tv_base), 1), tv, diff --git a/test/llvmcall.jl b/test/llvmcall.jl index a89696ed9c6c2..f7f6b44b29e62 100644 --- a/test/llvmcall.jl +++ b/test/llvmcall.jl @@ -264,5 +264,7 @@ MyStruct(kern) = MyStruct(kern, reinterpret(Core.LLVMPtr{UInt8,1}, 0)) MyStruct() = MyStruct(0) s = MyStruct() +# ensure LLVMPtr properly subtypes +@test eltype(supertype(Core.LLVMPtr{UInt8,1})) <: UInt8 @test s.kern == 0 @test reinterpret(Int, s.ptr) == 0 From bc33c81b5d2d8397be92bf5d488dfe721b116d7c Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Wed, 29 Mar 2023 15:09:42 +0000 Subject: [PATCH 399/775] Binary search for pkgimage metadata (#48940) Co-authored-by: Jameson Nash --- src/gc.c | 126 ++++++++++++++++++++++++++++++++++++++++++- src/julia_internal.h | 23 +------- src/staticdata.c | 7 +++ 3 files changed, 134 insertions(+), 22 deletions(-) diff --git a/src/gc.c b/src/gc.c index 298194b59b632..9d5d49fa4fc53 100644 --- a/src/gc.c +++ b/src/gc.c @@ -173,6 +173,13 @@ pagetable_t memory_map; // List of marked big objects. Not per-thread. Accessed only by master thread. bigval_t *big_objects_marked = NULL; +// Eytzinger tree of images. Used for very fast jl_object_in_image queries during gc +// See https://algorithmica.org/en/eytzinger +static arraylist_t eytzinger_image_tree; +static arraylist_t eytzinger_idxs; +static uintptr_t gc_img_min; +static uintptr_t gc_img_max; + // -- Finalization -- // `ptls->finalizers` and `finalizer_list_marked` might have tagged pointers. // If an object pointer has the lowest bit set, the next pointer is an unboxed c function pointer. @@ -183,6 +190,118 @@ arraylist_t finalizer_list_marked; arraylist_t to_finalize; JL_DLLEXPORT _Atomic(int) jl_gc_have_pending_finalizers = 0; +static int ptr_cmp(const void *l, const void *r) +{ + uintptr_t left = *(const uintptr_t*)l; + uintptr_t right = *(const uintptr_t*)r; + // jl_safe_printf("cmp %p %p\n", (void*)left, (void*)right); + return (left > right) - (left < right); +} + +// Build an eytzinger tree from a sorted array +static int eytzinger(uintptr_t *src, uintptr_t *dest, size_t i, size_t k, size_t n) +{ + if (k <= n) { + i = eytzinger(src, dest, i, 2 * k, n); + dest[k-1] = src[i]; + i++; + i = eytzinger(src, dest, i, 2 * k + 1, n); + } + return i; +} + +static size_t eyt_obj_idx(jl_value_t *obj) JL_NOTSAFEPOINT +{ + size_t n = eytzinger_image_tree.len - 1; + if (n == 0) + return n; + assert(n % 2 == 0 && "Eytzinger tree not even length!"); + uintptr_t cmp = (uintptr_t) obj; + if (cmp <= gc_img_min || cmp > gc_img_max) + return n; + uintptr_t *tree = (uintptr_t*)eytzinger_image_tree.items; + size_t k = 1; + // note that k preserves the history of how we got to the current node + while (k <= n) { + int greater = (cmp > tree[k - 1]); + k <<= 1; + k |= greater; + } + // Free to assume k is nonzero, since we start with k = 1 + // and cmp > gc_img_min + // This shift does a fast revert of the path until we get + // to a node that evaluated less than cmp. + k >>= (__builtin_ctzll(k) + 1); + assert(k != 0); + assert(k <= n && "Eytzinger tree index out of bounds!"); + assert(tree[k - 1] < cmp && "Failed to find lower bound for object!"); + return k - 1; +} + +//used in staticdata.c after we add an image +void rebuild_image_blob_tree(void) +{ + size_t inc = 1 + jl_linkage_blobs.len - eytzinger_image_tree.len; + assert(eytzinger_idxs.len == eytzinger_image_tree.len); + assert(eytzinger_idxs.max == eytzinger_image_tree.max); + arraylist_grow(&eytzinger_idxs, inc); + arraylist_grow(&eytzinger_image_tree, inc); + eytzinger_idxs.items[eytzinger_idxs.len - 1] = (void*)jl_linkage_blobs.len; + eytzinger_image_tree.items[eytzinger_image_tree.len - 1] = (void*)1; // outside image + for (size_t i = 0; i < jl_linkage_blobs.len; i++) { + assert((uintptr_t) jl_linkage_blobs.items[i] % 4 == 0 && "Linkage blob not 4-byte aligned!"); + // We abuse the pointer here a little so that a couple of properties are true: + // 1. a start and an end are never the same value. This simplifies the binary search. + // 2. ends are always after starts. This also simplifies the binary search. + // We assume that there exist no 0-size blobs, but that's a safe assumption + // since it means nothing could be there anyways + uintptr_t val = (uintptr_t) jl_linkage_blobs.items[i]; + eytzinger_idxs.items[i] = (void*)(val + (i & 1)); + } + qsort(eytzinger_idxs.items, eytzinger_idxs.len - 1, sizeof(void*), ptr_cmp); + gc_img_min = (uintptr_t) eytzinger_idxs.items[0]; + gc_img_max = (uintptr_t) eytzinger_idxs.items[eytzinger_idxs.len - 2] + 1; + eytzinger((uintptr_t*)eytzinger_idxs.items, (uintptr_t*)eytzinger_image_tree.items, 0, 1, eytzinger_idxs.len - 1); + // Reuse the scratch memory to store the indices + // Still O(nlogn) because binary search + for (size_t i = 0; i < jl_linkage_blobs.len; i ++) { + uintptr_t val = (uintptr_t) jl_linkage_blobs.items[i]; + // This is the same computation as in the prior for loop + uintptr_t eyt_val = val + (i & 1); + size_t eyt_idx = eyt_obj_idx((jl_value_t*)(eyt_val + 1)); assert(eyt_idx < eytzinger_idxs.len - 1); + assert(eytzinger_image_tree.items[eyt_idx] == (void*)eyt_val && "Eytzinger tree failed to find object!"); + if (i & 1) + eytzinger_idxs.items[eyt_idx] = (void*)n_linkage_blobs(); + else + eytzinger_idxs.items[eyt_idx] = (void*)(i / 2); + } +} + +static int eyt_obj_in_img(jl_value_t *obj) JL_NOTSAFEPOINT +{ + assert((uintptr_t) obj % 4 == 0 && "Object not 4-byte aligned!"); + int idx = eyt_obj_idx(obj); + // Now we use a tiny trick: tree[idx] & 1 is whether or not tree[idx] is a + // start (0) or an end (1) of a blob. If it's a start, then the object is + // in the image, otherwise it is not. + int in_image = ((uintptr_t)eytzinger_image_tree.items[idx] & 1) == 0; + return in_image; +} + +size_t external_blob_index(jl_value_t *v) JL_NOTSAFEPOINT +{ + assert((uintptr_t) v % 4 == 0 && "Object not 4-byte aligned!"); + int eyt_idx = eyt_obj_idx(v); + // We fill the invalid slots with the length, so we can just return that + size_t idx = (size_t) eytzinger_idxs.items[eyt_idx]; + return idx; +} + +uint8_t jl_object_in_image(jl_value_t *obj) JL_NOTSAFEPOINT +{ + return eyt_obj_in_img(obj); +} + NOINLINE uintptr_t gc_get_stack_ptr(void) { return (uintptr_t)jl_get_frame_addr(); @@ -2270,7 +2389,8 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ uint8_t bits = (gc_old(o->header) && !mark_reset_age) ? GC_OLD_MARKED : GC_MARKED; int update_meta = __likely(!meta_updated && !gc_verifying); int foreign_alloc = 0; - if (update_meta && jl_object_in_image(new_obj)) { + // directly point at eyt_obj_in_img to encourage inlining + if (update_meta && eyt_obj_in_img(new_obj)) { foreign_alloc = 1; update_meta = 0; } @@ -3245,6 +3365,10 @@ void jl_gc_init(void) arraylist_new(&finalizer_list_marked, 0); arraylist_new(&to_finalize, 0); + arraylist_new(&eytzinger_image_tree, 0); + arraylist_new(&eytzinger_idxs, 0); + arraylist_push(&eytzinger_idxs, (void*)0); + arraylist_push(&eytzinger_image_tree, (void*)1); // outside image gc_num.interval = default_collect_interval; last_long_collect_interval = default_collect_interval; diff --git a/src/julia_internal.h b/src/julia_internal.h index 223a245b5a7b1..2e1aef50d9a55 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -954,28 +954,9 @@ STATIC_INLINE size_t n_linkage_blobs(void) JL_NOTSAFEPOINT return jl_image_relocs.len; } -// TODO: Makes this a binary search -STATIC_INLINE size_t external_blob_index(jl_value_t *v) JL_NOTSAFEPOINT { - size_t i, nblobs = n_linkage_blobs(); - assert(jl_linkage_blobs.len == 2*nblobs); - for (i = 0; i < nblobs; i++) { - uintptr_t left = (uintptr_t)jl_linkage_blobs.items[2*i]; - uintptr_t right = (uintptr_t)jl_linkage_blobs.items[2*i + 1]; - if (left < (uintptr_t)v && (uintptr_t)v <= right) { - // the last object may be a singleton (v is shifted by a type tag, so we use exclusive bounds here) - break; - } - } - return i; -} +size_t external_blob_index(jl_value_t *v) JL_NOTSAFEPOINT; -STATIC_INLINE uint8_t jl_object_in_image(jl_value_t* v) JL_NOTSAFEPOINT { - size_t blob = external_blob_index(v); - if (blob == n_linkage_blobs()) { - return 0; - } - return 1; -} +uint8_t jl_object_in_image(jl_value_t* v) JL_NOTSAFEPOINT; typedef struct { LLVMOrcThreadSafeModuleRef TSM; diff --git a/src/staticdata.c b/src/staticdata.c index 2fd0c6fe17f07..ddc27f83cc0ee 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -2366,6 +2366,10 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, jl_write_relocations(&s); } + // This ensures that we can use the low bit of addresses for + // identifying end pointers in gc's eytzinger search. + write_padding(&sysimg, 4 - (sysimg.size % 4)); + if (sysimg.size > ((uintptr_t)1 << RELOC_TAG_OFFSET)) { jl_printf( JL_STDERR, @@ -2658,6 +2662,8 @@ JL_DLLEXPORT void jl_set_sysimg_so(void *handle) // } #endif +extern void rebuild_image_blob_tree(void); + static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl_array_t *depmods, uint64_t checksum, /* outputs */ jl_array_t **restored, jl_array_t **init_order, jl_array_t **extext_methods, @@ -3151,6 +3157,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl arraylist_push(&jl_linkage_blobs, (void*)image_base); arraylist_push(&jl_linkage_blobs, (void*)(image_base + sizeof_sysimg + sizeof(uintptr_t))); arraylist_push(&jl_image_relocs, (void*)relocs_base); + rebuild_image_blob_tree(); // jl_printf(JL_STDOUT, "%ld blobs to link against\n", jl_linkage_blobs.len >> 1); jl_gc_enable(en); From 8f78a94d5b97ebb7264059c03bec426f254e9ba8 Mon Sep 17 00:00:00 2001 From: Paul Berg Date: Wed, 29 Mar 2023 20:43:37 +0200 Subject: [PATCH 400/775] throw runtime `TypeError` on invalid ccall symbol (#49142) Throw runtime `TypeError` on invalid ccall or cglobal symbol, rather than throwing an internal compilation error. Closes #49141 Closes #45187 Co-authored-by: Jameson Nash --- src/ccall.cpp | 42 ++++++++++++++++++++++++++-------------- src/cgutils.cpp | 17 ++++++++++------ src/runtime_intrinsics.c | 4 ++-- test/ccall.jl | 16 +++++++++++++++ 4 files changed, 57 insertions(+), 22 deletions(-) diff --git a/src/ccall.cpp b/src/ccall.cpp index 1b71d9b6d2e70..8d054bb5faa41 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -516,7 +516,7 @@ static void typeassert_input(jl_codectx_t &ctx, const jl_cgval_t &jvinfo, jl_val ctx.builder.CreateCondBr(istype, passBB, failBB); ctx.builder.SetInsertPoint(failBB); - emit_type_error(ctx, mark_julia_type(ctx, vx, true, jl_any_type), boxed(ctx, jlto_runtime), msg); + just_emit_type_error(ctx, mark_julia_type(ctx, vx, true, jl_any_type), boxed(ctx, jlto_runtime), msg); ctx.builder.CreateUnreachable(); ctx.builder.SetInsertPoint(passBB); } @@ -568,8 +568,15 @@ typedef struct { jl_value_t *gcroot; } native_sym_arg_t; +static inline const char *invalid_symbol_err_msg(bool ccall) +{ + return ccall ? + "ccall: first argument not a pointer or valid constant expression" : + "cglobal: first argument not a pointer or valid constant expression"; +} + // --- parse :sym or (:sym, :lib) argument into address info --- -static void interpret_symbol_arg(jl_codectx_t &ctx, native_sym_arg_t &out, jl_value_t *arg, const char *fname, bool llvmcall) +static void interpret_symbol_arg(jl_codectx_t &ctx, native_sym_arg_t &out, jl_value_t *arg, bool ccall, bool llvmcall) { Value *&jl_ptr = out.jl_ptr; void (*&fptr)(void) = out.fptr; @@ -599,9 +606,7 @@ static void interpret_symbol_arg(jl_codectx_t &ctx, native_sym_arg_t &out, jl_va jl_cgval_t arg1 = emit_expr(ctx, arg); jl_value_t *ptr_ty = arg1.typ; if (!jl_is_cpointer_type(ptr_ty)) { - const char *errmsg = !strcmp(fname, "ccall") ? - "ccall: first argument not a pointer or valid constant expression" : - "cglobal: first argument not a pointer or valid constant expression"; + const char *errmsg = invalid_symbol_err_msg(ccall); emit_cpointercheck(ctx, arg1, errmsg); } arg1 = update_julia_type(ctx, arg1, (jl_value_t*)jl_voidpointer_type); @@ -647,8 +652,6 @@ static void interpret_symbol_arg(jl_codectx_t &ctx, native_sym_arg_t &out, jl_va f_name = jl_symbol_name((jl_sym_t*)t0); else if (jl_is_string(t0)) f_name = jl_string_data(t0); - else - JL_TYPECHKS(fname, symbol, t0); jl_value_t *t1 = jl_fieldref(ptr, 1); if (jl_is_symbol(t1)) @@ -656,10 +659,7 @@ static void interpret_symbol_arg(jl_codectx_t &ctx, native_sym_arg_t &out, jl_va else if (jl_is_string(t1)) f_lib = jl_string_data(t1); else - JL_TYPECHKS(fname, symbol, t1); - } - else { - JL_TYPECHKS(fname, pointer, ptr); + f_name = NULL; } } } @@ -696,7 +696,15 @@ static jl_cgval_t emit_cglobal(jl_codectx_t &ctx, jl_value_t **args, size_t narg Type *lrt = ctx.types().T_size; assert(lrt == julia_type_to_llvm(ctx, rt)); - interpret_symbol_arg(ctx, sym, args[1], "cglobal", false); + interpret_symbol_arg(ctx, sym, args[1], /*ccall=*/false, false); + + if (sym.f_name == NULL && sym.fptr == NULL && sym.jl_ptr == NULL && sym.gcroot != NULL) { + const char *errmsg = invalid_symbol_err_msg(/*ccall=*/false); + jl_cgval_t arg1 = emit_expr(ctx, args[1]); + emit_type_error(ctx, arg1, literal_pointer_val(ctx, (jl_value_t *)jl_pointer_type), errmsg); + JL_GC_POP(); + return jl_cgval_t(); + } if (sym.jl_ptr != NULL) { res = ctx.builder.CreateBitCast(sym.jl_ptr, lrt); @@ -1346,14 +1354,20 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) bool llvmcall = false; std::tie(cc, llvmcall) = convert_cconv(cc_sym); - interpret_symbol_arg(ctx, symarg, args[1], "ccall", llvmcall); + interpret_symbol_arg(ctx, symarg, args[1], /*ccall=*/true, llvmcall); Value *&jl_ptr = symarg.jl_ptr; void (*&fptr)(void) = symarg.fptr; const char *&f_name = symarg.f_name; const char *&f_lib = symarg.f_lib; if (f_name == NULL && fptr == NULL && jl_ptr == NULL) { - emit_error(ctx, "ccall: null function pointer"); + if (symarg.gcroot != NULL) { // static_eval(ctx, args[1]) could not be interpreted to a function pointer + const char *errmsg = invalid_symbol_err_msg(/*ccall=*/true); + jl_cgval_t arg1 = emit_expr(ctx, args[1]); + emit_type_error(ctx, arg1, literal_pointer_val(ctx, (jl_value_t *)jl_pointer_type), errmsg); + } else { + emit_error(ctx, "ccall: null function pointer"); + } JL_GC_POP(); return jl_cgval_t(); } diff --git a/src/cgutils.cpp b/src/cgutils.cpp index c0f4d3a7da794..9b611b0f7b6b5 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1352,13 +1352,21 @@ static Value *emit_typeof(jl_codectx_t &ctx, Value *v, bool maybenull) } -static void emit_type_error(jl_codectx_t &ctx, const jl_cgval_t &x, Value *type, const std::string &msg) +static void just_emit_type_error(jl_codectx_t &ctx, const jl_cgval_t &x, Value *type, const std::string &msg) { Value *msg_val = stringConstPtr(ctx.emission_context, ctx.builder, msg); ctx.builder.CreateCall(prepare_call(jltypeerror_func), { msg_val, maybe_decay_untracked(ctx, type), mark_callee_rooted(ctx, boxed(ctx, x))}); } +static void emit_type_error(jl_codectx_t &ctx, const jl_cgval_t &x, Value *type, const std::string &msg) +{ + just_emit_type_error(ctx, x, type, msg); + ctx.builder.CreateUnreachable(); + BasicBlock *cont = BasicBlock::Create(ctx.builder.getContext(), "after_type_error", ctx.f); + ctx.builder.SetInsertPoint(cont); +} + // Should agree with `emit_isa` below static bool _can_optimize_isa(jl_value_t *type, int &counter) { @@ -1441,9 +1449,6 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, if (known_isa) { if (!*known_isa && msg) { emit_type_error(ctx, x, literal_pointer_val(ctx, type), *msg); - ctx.builder.CreateUnreachable(); - BasicBlock *failBB = BasicBlock::Create(ctx.builder.getContext(), "fail", ctx.f); - ctx.builder.SetInsertPoint(failBB); } return std::make_pair(ConstantInt::get(getInt1Ty(ctx.builder.getContext()), *known_isa), true); } @@ -1581,7 +1586,7 @@ static void emit_typecheck(jl_codectx_t &ctx, const jl_cgval_t &x, jl_value_t *t ctx.builder.CreateCondBr(istype, passBB, failBB); ctx.builder.SetInsertPoint(failBB); - emit_type_error(ctx, x, literal_pointer_val(ctx, type), msg); + just_emit_type_error(ctx, x, literal_pointer_val(ctx, type), msg); ctx.builder.CreateUnreachable(); ctx.f->getBasicBlockList().push_back(passBB); @@ -3464,7 +3469,7 @@ static void emit_cpointercheck(jl_codectx_t &ctx, const jl_cgval_t &x, const std ctx.builder.CreateCondBr(istype, passBB, failBB); ctx.builder.SetInsertPoint(failBB); - emit_type_error(ctx, x, literal_pointer_val(ctx, (jl_value_t*)jl_pointer_type), msg); + just_emit_type_error(ctx, x, literal_pointer_val(ctx, (jl_value_t*)jl_pointer_type), msg); ctx.builder.CreateUnreachable(); ctx.f->getBasicBlockList().push_back(passBB); diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index 9536ed1f02fb3..99a81974a68b3 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -490,14 +490,14 @@ JL_DLLEXPORT jl_value_t *jl_cglobal(jl_value_t *v, jl_value_t *ty) char *f_lib = NULL; if (jl_is_tuple(v) && jl_nfields(v) > 1) { - jl_value_t *t1 = jl_fieldref_noalloc(v, 1); - v = jl_fieldref(v, 0); + jl_value_t *t1 = jl_fieldref(v, 1); if (jl_is_symbol(t1)) f_lib = jl_symbol_name((jl_sym_t*)t1); else if (jl_is_string(t1)) f_lib = jl_string_data(t1); else JL_TYPECHK(cglobal, symbol, t1) + v = jl_fieldref(v, 0); } char *f_name = NULL; diff --git a/test/ccall.jl b/test/ccall.jl index d88e667b55c72..eee48c5576465 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -1516,6 +1516,12 @@ end @test_throws(ErrorException("ccall return type struct fields cannot contain a reference"), @eval ccall(:fn, typeof(Ref("")), ())) +fn45187() = nothing + +@test_throws(TypeError, @eval ccall(nothing, Cvoid, ())) +@test_throws(TypeError, @eval ccall(49142, Cvoid, ())) +@test_throws(TypeError, @eval ccall((:fn, fn45187), Cvoid, ())) + # test for malformed syntax errors @test Expr(:error, "more arguments than types for ccall") == Meta.lower(@__MODULE__, :(ccall(:fn, A, (), x))) @test Expr(:error, "more arguments than types for ccall") == Meta.lower(@__MODULE__, :(ccall(:fn, A, (B,), x, y))) @@ -1910,6 +1916,12 @@ end function cglobal33413_literal_notype() return cglobal(:sin) end + function cglobal49142_nothing() + return cglobal(nothing) + end + function cglobal45187fn() + return cglobal((:fn, fn45187)) + end @test unsafe_load(cglobal33413_ptrvar()) == 1 @test unsafe_load(cglobal33413_ptrinline()) == 1 @test unsafe_load(cglobal33413_tupleliteral()) == 1 @@ -1918,6 +1930,10 @@ end @test unsafe_load(convert(Ptr{Cint}, cglobal33413_tupleliteral_notype())) == 1 @test cglobal33413_literal() != C_NULL @test cglobal33413_literal_notype() != C_NULL + @test_throws(TypeError, cglobal49142_nothing()) + @test_throws(TypeError, cglobal45187fn()) + @test_throws(TypeError, @eval cglobal(nothing)) + @test_throws(TypeError, @eval cglobal((:fn, fn45187))) end @testset "ccall_effects" begin From d586b0c9520afd30d101522b1e09708bdd31fcc6 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 29 Mar 2023 18:09:22 -0300 Subject: [PATCH 401/775] Add bit to the GC tag --- src/gc.c | 2 +- src/julia.h | 1 + src/julia_internal.h | 1 + src/staticdata.c | 7 +++++-- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/gc.c b/src/gc.c index 9d5d49fa4fc53..195ee2d98ab4f 100644 --- a/src/gc.c +++ b/src/gc.c @@ -2390,7 +2390,7 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ int update_meta = __likely(!meta_updated && !gc_verifying); int foreign_alloc = 0; // directly point at eyt_obj_in_img to encourage inlining - if (update_meta && eyt_obj_in_img(new_obj)) { + if (update_meta && o->bits.in_image) { foreign_alloc = 1; update_meta = 0; } diff --git a/src/julia.h b/src/julia.h index 2186ee346418d..64b7fc452a4da 100644 --- a/src/julia.h +++ b/src/julia.h @@ -91,6 +91,7 @@ typedef struct _jl_value_t jl_value_t; struct _jl_taggedvalue_bits { uintptr_t gc:2; + uintptr_t in_image:1; }; JL_EXTENSION struct _jl_taggedvalue_t { diff --git a/src/julia_internal.h b/src/julia_internal.h index 2e1aef50d9a55..73f8b9467fcf4 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -301,6 +301,7 @@ static inline void memmove_refs(void **dstp, void *const *srcp, size_t n) JL_NOT #define GC_MARKED 1 // reachable and young #define GC_OLD 2 // if it is reachable it will be marked as old #define GC_OLD_MARKED (GC_OLD | GC_MARKED) // reachable and old +#define GC_IN_IMAGE 4 // useful constants extern jl_methtable_t *jl_type_type_mt JL_GLOBALLY_ROOTED; diff --git a/src/staticdata.c b/src/staticdata.c index ddc27f83cc0ee..d6c92ac93a851 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1758,6 +1758,7 @@ void gc_sweep_sysimg(void) last_pos = pos; jl_taggedvalue_t *o = (jl_taggedvalue_t *)(base + pos); o->bits.gc = GC_OLD; + assert(o->bits.in_image == 1); } } } @@ -2811,7 +2812,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl *base = image_base; s.s = &sysimg; - jl_read_reloclist(&s, s.link_ids_gctags, GC_OLD); // gctags + jl_read_reloclist(&s, s.link_ids_gctags, GC_OLD | GC_IN_IMAGE); // gctags size_t sizeof_tags = ios_pos(&relocs); (void)sizeof_tags; jl_read_reloclist(&s, s.link_ids_relocs, 0); // general relocs @@ -2922,7 +2923,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl arraylist_push(&cleanup_list, (void*)obj); } if (tag) - *pfld = (uintptr_t)newobj | GC_OLD; + *pfld = (uintptr_t)newobj | GC_OLD | GC_IN_IMAGE; else *pfld = (uintptr_t)newobj; assert(!(image_base < (char*)newobj && (char*)newobj <= image_base + sizeof_sysimg + sizeof(uintptr_t))); @@ -2965,6 +2966,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl memset(o, 0xba, sizeof(jl_value_t*) + sizeof(jl_datatype_t)); else memset(o, 0xba, sizeof(jl_value_t*) + 0); // singleton + o->bits.in_image = 1; } arraylist_grow(&cleanup_list, -cleanup_list.len); // finally cache all our new types now @@ -3032,6 +3034,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl jl_value_t *t = jl_typeof(item); if (t == (jl_value_t*)jl_method_instance_type) memset(o, 0xba, sizeof(jl_value_t*) * 3); // only specTypes and sparams fields stored + o->bits.in_image = 1; } arraylist_free(&cleanup_list); for (size_t i = 0; i < s.fixup_objs.len; i++) { From cdc37ce4d5bb7bb59accc65cc642d023e54e6e57 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 29 Mar 2023 17:16:52 -0400 Subject: [PATCH 402/775] rewrite DW_TAG_compile_unit during aotcompile (#49183) The macOS linker requires these to be unique due to a bug, and we do not care about this value at all as it does nothing, so just make it something unique. However, keep the directory as ".", since it uses that to compose the DW_AT_decl_file values for its subprograms. Also try to save a little memory, since we have to leak all of these objects (per LLVMContext), and we would like to avoid that. Fix #49152 Fix #49151 Fix #49153 --- src/aotcompile.cpp | 5 +++++ src/cgutils.cpp | 33 +++++++++++++++++++++++++++++++++ src/codegen.cpp | 43 +++++++++++-------------------------------- src/jitlayers.cpp | 4 ++-- 4 files changed, 51 insertions(+), 34 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index db0d284df4254..a749e6daa7634 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1341,6 +1341,11 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o timers[i].construct.startTimer(); construct_vars(*M, partitions[i]); M->setModuleFlag(Module::Error, "julia.mv.suffix", MDString::get(M->getContext(), "_" + std::to_string(i))); + // The DICompileUnit file is not used for anything, but ld64 requires it be a unique string per object file + // or it may skip emitting debug info for that file. Here set it to ./julia#N + DIFile *topfile = DIFile::get(M->getContext(), "julia#" + std::to_string(i), "."); + for (DICompileUnit *CU : M->debug_compile_units()) + CU->replaceOperandWith(0, topfile); timers[i].construct.stopTimer(); timers[i].deletion.startTimer(); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 9b611b0f7b6b5..b83ba025e4008 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -153,6 +153,39 @@ Metadata *to_md_tree(jl_value_t *val, LLVMContext &ctxt) { // --- Debug info --- +static DICompileUnit *getOrCreateJuliaCU(Module &M, + DICompileUnit::DebugEmissionKind emissionKind, + DICompileUnit::DebugNameTableKind tableKind) +{ + // TODO: share debug objects globally in the context, instead of allocating a new one every time + // or figure out how to delete them afterwards? + // But at least share them a little bit here + auto CUs = M.debug_compile_units(); + for (DICompileUnit *CU : CUs) { + if (CU->getEmissionKind() == emissionKind && + CU->getNameTableKind() == tableKind) + return CU; + } + DIFile *topfile = DIFile::get(M.getContext(), "julia", "."); + DIBuilder dbuilder(M); + DICompileUnit *CU = + dbuilder.createCompileUnit(llvm::dwarf::DW_LANG_Julia + ,topfile // File + ,"julia" // Producer + ,true // isOptimized + ,"" // Flags + ,0 // RuntimeVersion + ,"" // SplitName + ,emissionKind // Kind + ,0 // DWOId + ,true // SplitDebugInlining + ,false // DebugInfoForProfiling + ,tableKind // NameTableKind + ); + dbuilder.finalize(); + return CU; +} + static DIType *_julia_type_to_di(jl_codegen_params_t *ctx, jl_debugcache_t &debuginfo, jl_value_t *jt, DIBuilder *dbuilder, bool isboxed) { jl_datatype_t *jdt = (jl_datatype_t*)jt; diff --git a/src/codegen.cpp b/src/codegen.cpp index 6b573051f3b97..c45d015cbd137 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7149,47 +7149,26 @@ static jl_llvm_functions_t ctx.f = f; // Step 4b. determine debug info signature and other type info for locals - DIBuilder dbuilder(*M); + DICompileUnit::DebugEmissionKind emissionKind = (DICompileUnit::DebugEmissionKind) ctx.params->debug_info_kind; + DICompileUnit::DebugNameTableKind tableKind; + if (JL_FEAT_TEST(ctx, gnu_pubnames)) + tableKind = DICompileUnit::DebugNameTableKind::GNU; + else + tableKind = DICompileUnit::DebugNameTableKind::None; + DIBuilder dbuilder(*M, true, ctx.debug_enabled ? getOrCreateJuliaCU(*M, emissionKind, tableKind) : NULL); DIFile *topfile = NULL; DISubprogram *SP = NULL; DebugLoc noDbg, topdebugloc; if (ctx.debug_enabled) { - DICompileUnit::DebugEmissionKind emissionKind = (DICompileUnit::DebugEmissionKind) ctx.params->debug_info_kind; - DICompileUnit::DebugNameTableKind tableKind; - - if (JL_FEAT_TEST(ctx, gnu_pubnames)) { - tableKind = DICompileUnit::DebugNameTableKind::GNU; - } - else { - tableKind = DICompileUnit::DebugNameTableKind::None; - } topfile = dbuilder.createFile(ctx.file, "."); - DICompileUnit *CU = - dbuilder.createCompileUnit(llvm::dwarf::DW_LANG_Julia - ,topfile // File - ,"julia" // Producer - ,true // isOptimized - ,"" // Flags - ,0 // RuntimeVersion - ,"" // SplitName - ,emissionKind // Kind - ,0 // DWOId - ,true // SplitDebugInlining - ,false // DebugInfoForProfiling - ,tableKind // NameTableKind - ); - DISubroutineType *subrty; - if (jl_options.debug_level <= 1) { + if (jl_options.debug_level <= 1) subrty = debuginfo.jl_di_func_null_sig; - } - else if (!specsig) { + else if (!specsig) subrty = debuginfo.jl_di_func_sig; - } - else { + else subrty = get_specsig_di(ctx, debuginfo, jlrettype, lam->specTypes, dbuilder); - } - SP = dbuilder.createFunction(CU + SP = dbuilder.createFunction(nullptr ,dbgFuncName // Name ,f->getName() // LinkageName ,topfile // File diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 79e2ef1436704..c7e202b98efab 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -1694,8 +1694,8 @@ void jl_merge_module(orc::ThreadSafeModule &destTSM, orc::ThreadSafeModule srcTS NamedMDNode *sNMD = src.getNamedMetadata("llvm.dbg.cu"); if (sNMD) { NamedMDNode *dNMD = dest.getOrInsertNamedMetadata("llvm.dbg.cu"); - for (NamedMDNode::op_iterator I = sNMD->op_begin(), E = sNMD->op_end(); I != E; ++I) { - dNMD->addOperand(*I); + for (MDNode *I : sNMD->operands()) { + dNMD->addOperand(I); } } }); From 7d8322d0d6c92389c55db28f6e1acfd3f381cac4 Mon Sep 17 00:00:00 2001 From: Ian Date: Sun, 26 Mar 2023 19:14:09 -0400 Subject: [PATCH 403/775] build checkbounds default last to make them the first to try --- pkgimage.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgimage.mk b/pkgimage.mk index 462767b07d550..b407db2b1309c 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -27,13 +27,13 @@ define pkgimg_builder $1_SRCS := $$(shell find $$(build_datarootdir)/julia/stdlib/$$(VERSDIR)/$1/src -name \*.jl) \ $$(wildcard $$(build_prefix)/manifest/$$(VERSDIR)/$1) $$(BUILDDIR)/stdlib/$1.release.image: $$($1_SRCS) $$(addsuffix .release.image,$$(addprefix $$(BUILDDIR)/stdlib/,$2)) - @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.compilecache(Base.identify_package("$1"))') @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes -e 'Base.compilecache(Base.identify_package("$1"))') + @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.compilecache(Base.identify_package("$1"))') touch $$@ cache-release-$1: $$(BUILDDIR)/stdlib/$1.release.image $$(BUILDDIR)/stdlib/$1.debug.image: $$($1_SRCS) $$(addsuffix .debug.image,$$(addprefix $$(BUILDDIR)/stdlib/,$2)) - @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.compilecache(Base.identify_package("$1"))') @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes -e 'Base.compilecache(Base.identify_package("$1"))') + @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.compilecache(Base.identify_package("$1"))') cache-debug-$1: $$(BUILDDIR)/stdlib/$1.debug.image .SECONDARY: $$(BUILDDIR)/stdlib/$1.release.image $$(BUILDDIR)/stdlib/$1.debug.image endef From 0b15e7e8db03568cc8f78a48a7a4af72af311a03 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Mon, 27 Mar 2023 15:23:11 -0400 Subject: [PATCH 404/775] add missing dependency on sysimage --- pkgimage.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgimage.mk b/pkgimage.mk index b407db2b1309c..d3a0238dd035c 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -26,12 +26,12 @@ all-debug: $(addprefix cache-debug-, $(STDLIBS)) define pkgimg_builder $1_SRCS := $$(shell find $$(build_datarootdir)/julia/stdlib/$$(VERSDIR)/$1/src -name \*.jl) \ $$(wildcard $$(build_prefix)/manifest/$$(VERSDIR)/$1) -$$(BUILDDIR)/stdlib/$1.release.image: $$($1_SRCS) $$(addsuffix .release.image,$$(addprefix $$(BUILDDIR)/stdlib/,$2)) +$$(BUILDDIR)/stdlib/$1.release.image: $$($1_SRCS) $$(addsuffix .release.image,$$(addprefix $$(BUILDDIR)/stdlib/,$2)) $(build_private_libdir)/sys.$(SHLIB_EXT) @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes -e 'Base.compilecache(Base.identify_package("$1"))') @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.compilecache(Base.identify_package("$1"))') touch $$@ cache-release-$1: $$(BUILDDIR)/stdlib/$1.release.image -$$(BUILDDIR)/stdlib/$1.debug.image: $$($1_SRCS) $$(addsuffix .debug.image,$$(addprefix $$(BUILDDIR)/stdlib/,$2)) +$$(BUILDDIR)/stdlib/$1.debug.image: $$($1_SRCS) $$(addsuffix .debug.image,$$(addprefix $$(BUILDDIR)/stdlib/,$2)) $(build_private_libdir)/sys-debug.$(SHLIB_EXT) @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes -e 'Base.compilecache(Base.identify_package("$1"))') @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.compilecache(Base.identify_package("$1"))') cache-debug-$1: $$(BUILDDIR)/stdlib/$1.debug.image From 7ca912f6a90072cb92b5061d4b6253c345ddc3c0 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Wed, 29 Mar 2023 20:51:11 -0400 Subject: [PATCH 405/775] Remove left-over code from special handling of bindings (#49180) --- src/codegen.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index c45d015cbd137..8340b1267ce6e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -964,15 +964,6 @@ static const auto jl_write_barrier_func = new JuliaFunction{ AttributeSet(), {Attributes(C, {Attribute::ReadOnly})}); }, }; -static const auto jl_write_barrier_binding_func = new JuliaFunction{ - "julia.write_barrier_binding", - [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), - {JuliaType::get_prjlvalue_ty(C)}, true); }, - [](LLVMContext &C) { return AttributeList::get(C, - Attributes(C, {Attribute::NoUnwind, Attribute::NoRecurse, Attribute::InaccessibleMemOnly}), - AttributeSet(), - {Attributes(C, {Attribute::ReadOnly})}); }, -}; static const auto jlisa_func = new JuliaFunction{ XSTR(jl_isa), [](LLVMContext &C) { @@ -8728,7 +8719,6 @@ static void init_jit_functions(void) add_named_global(jl_loopinfo_marker_func, (void*)NULL); add_named_global(jl_typeof_func, (void*)NULL); add_named_global(jl_write_barrier_func, (void*)NULL); - add_named_global(jl_write_barrier_binding_func, (void*)NULL); add_named_global(jldlsym_func, &jl_load_and_lookup); add_named_global("jl_adopt_thread", &jl_adopt_thread); add_named_global(jlgetcfunctiontrampoline_func, &jl_get_cfunction_trampoline); From 9169c1946170693a661c4239c8ac701286666aec Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 30 Mar 2023 10:57:19 +0900 Subject: [PATCH 406/775] fix printed repr of `:(using A: (..) [as xxx])` (#49178) We already have a special case for the `.` symbol, so it would be reasonable to have another special case for `(..)`. --- base/show.jl | 10 ++++++++-- test/show.jl | 5 ++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/base/show.jl b/base/show.jl index 3a17a5e4197ad..4e56a9837c4c3 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1842,10 +1842,16 @@ function show_import_path(io::IO, ex, quote_level) end elseif ex.head === :(.) for i = 1:length(ex.args) - if ex.args[i] === :(.) + sym = ex.args[i]::Symbol + if sym === :(.) print(io, '.') else - show_sym(io, ex.args[i]::Symbol, allow_macroname=(i==length(ex.args))) + if sym === :(..) + # special case for https://github.com/JuliaLang/julia/issues/49168 + print(io, "(..)") + else + show_sym(io, sym, allow_macroname=(i==length(ex.args))) + end i < length(ex.args) && print(io, '.') end end diff --git a/test/show.jl b/test/show.jl index b78816c077f60..651391bf9e729 100644 --- a/test/show.jl +++ b/test/show.jl @@ -268,7 +268,6 @@ end @test repr(Expr(:import, :Foo)) == ":(\$(Expr(:import, :Foo)))" @test repr(Expr(:import, Expr(:(.), ))) == ":(\$(Expr(:import, :(\$(Expr(:.))))))" - @test repr(Expr(:using, Expr(:(.), :A))) == ":(using A)" @test repr(Expr(:using, Expr(:(.), :A), Expr(:(.), :B))) == ":(using A, B)" @@ -286,6 +285,10 @@ end @test repr(Expr(:import, Expr(:(.), :A, :B), Expr(:(.), :C, :D))) == ":(import A.B, C.D)" +# https://github.com/JuliaLang/julia/issues/49168 +@test repr(:(using A: (..))) == ":(using A: (..))" +@test repr(:(using A: (..) as twodots)) == ":(using A: (..) as twodots)" + # range syntax @test_repr "1:2" @test_repr "3:4:5" From 329f92c3fab91bc3816bac6ca3b92400c066c5bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20W=C3=BCrfel?= Date: Thu, 30 Mar 2023 08:38:56 +0200 Subject: [PATCH 407/775] Handle `ProcessChain` in open(f, cmd, ..) (#49166) --- base/process.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/process.jl b/base/process.jl index 55df523c1f7d2..ed51a30ae3ced 100644 --- a/base/process.jl +++ b/base/process.jl @@ -413,7 +413,7 @@ process failed, or if the process attempts to print anything to stdout. """ function open(f::Function, cmds::AbstractCmd, args...; kwargs...) P = open(cmds, args...; kwargs...) - function waitkill(P::Process) + function waitkill(P::Union{Process,ProcessChain}) close(P) # 0.1 seconds after we hope it dies (from closing stdio), # we kill the process with SIGTERM (15) From e5c2c51c083a33b4357178cb0009bc1e831c004e Mon Sep 17 00:00:00 2001 From: Loong Date: Thu, 30 Mar 2023 14:39:48 +0800 Subject: [PATCH 408/775] fix(REPL): using/import statements should on top-level, #49041 (#49098) --- stdlib/REPL/src/REPL.jl | 14 +++++++++++++- stdlib/REPL/test/repl.jl | 5 +++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 5149f1f30df8b..5f7ab768e3015 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1407,12 +1407,24 @@ function repl_eval_counter(hp) end function out_transform(@nospecialize(x), n::Ref{Int}) - return quote + return Expr(:toplevel, get_usings!([], x)..., quote let __temp_val_a72df459 = $x $capture_result($n, __temp_val_a72df459) __temp_val_a72df459 end + end) +end + +function get_usings!(usings, ex) + # get all `using` and `import` statements which are at the top level + for (i, arg) in enumerate(ex.args) + if Base.isexpr(arg, :toplevel) + get_usings!(usings, arg) + elseif Base.isexpr(arg, [:using, :import]) + push!(usings, popat!(ex.args, i)) + end end + return usings end function capture_result(n::Ref{Int}, @nospecialize(x)) diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 9e6ab515daba3..c71bdc86d965f 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -1647,6 +1647,11 @@ fake_repl() do stdin_write, stdout_read, repl s = sendrepl2("x_47878 = range(-1; stop = 1)\n", "-1:1") @test contains(s, "Out[11]: -1:1") + # Test for https://github.com/JuliaLang/julia/issues/49041 + s = sendrepl2("using Test; @test true", "In [14]") + @test !contains(s, "ERROR") + @test contains(s, "Test Passed") + write(stdin_write, '\x04') Base.wait(repltask) end From 20920007ef3320bd26dbd701e3a259a791acf27e Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 30 Mar 2023 14:57:12 +0800 Subject: [PATCH 409/775] fix `obviously_disjoint` for Union Types (#49177) --- src/subtype.c | 6 ++++++ test/subtype.jl | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/src/subtype.c b/src/subtype.c index e4cfa9bac0af0..69d0892403c43 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -393,6 +393,12 @@ static int obviously_disjoint(jl_value_t *a, jl_value_t *b, int specificity) return 1; if (jl_is_unionall(a)) a = jl_unwrap_unionall(a); if (jl_is_unionall(b)) b = jl_unwrap_unionall(b); + if (jl_is_uniontype(a)) + return obviously_disjoint(((jl_uniontype_t *)a)->a, b, specificity) && + obviously_disjoint(((jl_uniontype_t *)a)->b, b, specificity); + if (jl_is_uniontype(b)) + return obviously_disjoint(a, ((jl_uniontype_t *)b)->a, specificity) && + obviously_disjoint(a, ((jl_uniontype_t *)b)->b, specificity); if (jl_is_datatype(a) && jl_is_datatype(b)) { jl_datatype_t *ad = (jl_datatype_t*)a, *bd = (jl_datatype_t*)b; if (ad->name != bd->name) { diff --git a/test/subtype.jl b/test/subtype.jl index 00007cdfbb01b..40ebda9ec9a73 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2474,3 +2474,7 @@ let a = [TypeVar(:V, Union{}, Function) for i in 1:32] T = foldr((v, d) -> UnionAll(v, d), b; init = foldl((i, j) -> F49127{i, j}, b)) @test S <: T end + +# requires assertions enabled (to test union-split in `obviously_disjoint`) +@test !<:(Tuple{Type{Int}, Int}, Tuple{Type{Union{Int, T}}, T} where T<:Union{Int8,Int16}) +@test <:(Tuple{Type{Int}, Int}, Tuple{Type{Union{Int, T}}, T} where T<:Union{Int8,Int}) From da1ce65fa7dcb8e6470aa98479bc703652d7c863 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 30 Mar 2023 04:31:25 -0500 Subject: [PATCH 410/775] Add iterator for method specializations (#49116) There have been longstanding reasons to want an API for extracting all extant specializations of a method, but this is even more true after #49071. Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Co-authored-by: Jameson Nash --- base/reflection.jl | 28 ++++++++++++++++++++++++++++ doc/src/manual/performance-tips.md | 2 +- test/precompile.jl | 25 ++++++++++--------------- test/reflection.jl | 7 +++++++ test/worlds.jl | 17 +++-------------- 5 files changed, 49 insertions(+), 30 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index 5ac74e1c75bab..ac8d2752a4719 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1110,6 +1110,34 @@ function visit(f, d::Core.TypeMapEntry) end nothing end +struct MethodSpecializations + specializations::Union{Nothing, Core.MethodInstance, Core.SimpleVector} +end +""" + specializations(m::Method) → itr + +Return an iterator `itr` of all compiler-generated specializations of `m`. +""" +specializations(m::Method) = MethodSpecializations(isdefined(m, :specializations) ? m.specializations : nothing) +function iterate(specs::MethodSpecializations) + s = specs.specializations + s === nothing && return nothing + isa(s, Core.MethodInstance) && return (s, nothing) + return iterate(specs, 0) +end +iterate(specs::MethodSpecializations, ::Nothing) = nothing +function iterate(specs::MethodSpecializations, i::Int) + s = specs.specializations::Core.SimpleVector + n = length(s) + i >= n && return nothing + item = nothing + while i < n && item === nothing + item = s[i+=1] + end + item === nothing && return nothing + return (item, i) +end +length(specs::MethodSpecializations) = count(Returns(true), specs) function length(mt::Core.MethodTable) n = 0 diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index 1eee23e163a77..ffb84333e8e78 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -584,7 +584,7 @@ h_vararg(x::Vararg{Any, N}) where {N} = tuple(x...) Note that [`@code_typed`](@ref) and friends will always show you specialized code, even if Julia would not normally specialize that method call. You need to check the [method internals](@ref ast-lowered-method) if you want to see whether specializations are generated -when argument types are changed, i.e., if `(@which f(...)).specializations` contains specializations +when argument types are changed, i.e., if `Base.specializations(@which f(...))` contains specializations for the argument in question. ## Break functions into multiple definitions diff --git a/test/precompile.jl b/test/precompile.jl index 2b5405a06c88d..de15171c8138c 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -665,10 +665,8 @@ precompile_test_harness("code caching") do dir # size(::Vector) has an inferred specialization for Vector{X} msize = which(size, (Vector{<:Any},)) hasspec = false - msizespecs = msize.specializations::Core.SimpleVector - for i = 1:length(msizespecs) - mi = msizespecs[i] - if isa(mi, Core.MethodInstance) && mi.specTypes == Tuple{typeof(size),Vector{Cacheb8321416e8a3e2f1.X}} + for mi in Base.specializations(msize) + if mi.specTypes == Tuple{typeof(size),Vector{Cacheb8321416e8a3e2f1.X}} if isdefined(mi, :cache) && isa(mi.cache, Core.CodeInstance) && mi.cache.max_world == typemax(UInt) && mi.cache.inferred !== nothing hasspec = true break @@ -786,9 +784,7 @@ precompile_test_harness("code caching") do dir MB = getfield(@__MODULE__, RootB) M = getfield(MA, RootModule) m = which(M.f, (Any,)) - mspecs = m.specializations - mspecs isa Core.SimpleVector || (mspecs = Core.svec(mspecs)) - for mi in mspecs + for mi in Base.specializations(m) mi === nothing && continue mi = mi::Core.MethodInstance if mi.specTypes.parameters[2] === Int8 @@ -925,8 +921,7 @@ precompile_test_harness("code caching") do dir # Reporting test (ensure SnoopCompile works) @test all(i -> isassigned(invalidations, i), eachindex(invalidations)) m = only(methods(MB.call_nbits)) - for mi in m.specializations::Core.SimpleVector - mi === nothing && continue + for mi in Base.specializations(m) hv = hasvalid(mi, world) @test mi.specTypes.parameters[end] === Integer ? !hv : hv end @@ -1088,13 +1083,13 @@ precompile_test_harness("invoke") do dir end m = get_method_for_type(M.h, Real) - @test m.specializations === Core.svec() + @test isempty(Base.specializations(m)) m = get_method_for_type(M.hnc, Real) - @test m.specializations === Core.svec() + @test isempty(Base.specializations(m)) m = only(methods(M.callq)) - @test m.specializations === Core.svec() || nvalid(m.specializations::Core.MethodInstance) == 0 + @test isempty(Base.specializations(m)) || nvalid(m.specializations::Core.MethodInstance) == 0 m = only(methods(M.callqnc)) - @test m.specializations === Core.svec() || nvalid(m.specializations::Core.MethodInstance) == 0 + @test isempty(Base.specializations(m)) || nvalid(m.specializations::Core.MethodInstance) == 0 m = only(methods(M.callqi)) @test (m.specializations::Core.MethodInstance).specTypes == Tuple{typeof(M.callqi), Int} m = only(methods(M.callqnci)) @@ -1112,7 +1107,7 @@ precompile_test_harness("invoke") do dir m_any, m_int = sort(collect(methods(invokeme)); by=m->(m.file,m.line)) @test precompile(invokeme, (Int,), m_any) @test (m_any.specializations::Core.MethodInstance).specTypes === Tuple{typeof(invokeme), Int} - @test m_int.specializations === Core.svec() + @test isempty(Base.specializations(m_int)) end # test --compiled-modules=no command line option @@ -1581,7 +1576,7 @@ precompile_test_harness("issue #46296") do load_path """ module CodeInstancePrecompile - mi = first(methods(identity)).specializations[1] + mi = first(Base.specializations(first(methods(identity)))) ci = Core.CodeInstance(mi, Any, nothing, nothing, zero(Int32), typemin(UInt), typemax(UInt), zero(UInt32), zero(UInt32), nothing, 0x00) diff --git a/test/reflection.jl b/test/reflection.jl index 95965bf1725a7..da6e71d687c92 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -1039,3 +1039,10 @@ ambig_effects_test(a, b) = 1 end @test Base._methods_by_ftype(Tuple{}, -1, Base.get_world_counter()) == Any[] + +@testset "specializations" begin + f(x) = 1 + f(1) + f("hello") + @test length(Base.specializations(only(methods(f)))) == 2 +end diff --git a/test/worlds.jl b/test/worlds.jl index ff6b0197e8051..b5a8f1c5449ac 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -233,21 +233,10 @@ function method_instance(f, types=Base.default_tt(f)) m = which(f, types) inst = nothing tt = Base.signature_type(f, types) - specs = m.specializations - if isa(specs, Core.SimpleVector) - for i = 1:length(specs) - mi = specs[i] - if mi isa Core.MethodInstance - if mi.specTypes <: tt && tt <: mi.specTypes - inst = mi - break - end - end - end - else - mi = specs::Core.MethodInstance - if mi.specTypes === tt + for mi in Base.specializations(m) + if mi.specTypes <: tt && tt <: mi.specTypes inst = mi + break end end return inst From ceafd6cad9a66e290aece75a62ba7733e03ba58d Mon Sep 17 00:00:00 2001 From: Ranlajetech <56589757+Ranlajetech@users.noreply.github.com> Date: Thu, 30 Mar 2023 19:09:43 +0800 Subject: [PATCH 411/775] fixed 2 typo in doc (#49192) --- doc/src/manual/command-line-interface.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/manual/command-line-interface.md b/doc/src/manual/command-line-interface.md index 54c56a354c7a3..cd2dfe1fb4525 100644 --- a/doc/src/manual/command-line-interface.md +++ b/doc/src/manual/command-line-interface.md @@ -106,8 +106,8 @@ The following is a complete list of command-line switches available when launchi |`-e`, `--eval ` |Evaluate ``| |`-E`, `--print ` |Evaluate `` and display the result| |`-L`, `--load ` |Load `` immediately on all processors| -|`-t`, `--threads {N\|auto`} |Enable N threads; `auto` tries to infer a useful default number of threads to use but the exact behavior might change in the future. Currently, `auto` uses the number of CPUs assigned to this julia process based on the OS-specific affinity assignment interface, if supported (Linux and Windows). If this is not supported (macOS) or process affinity is not configured, it uses the number of CPU threads.| -|`-p`, `--procs {N\|auto`} |Integer value N launches N additional local worker processes; `auto` launches as many workers as the number of local CPU threads (logical cores)| +|`-t`, `--threads {N\|auto}` |Enable N threads; `auto` tries to infer a useful default number of threads to use but the exact behavior might change in the future. Currently, `auto` uses the number of CPUs assigned to this julia process based on the OS-specific affinity assignment interface, if supported (Linux and Windows). If this is not supported (macOS) or process affinity is not configured, it uses the number of CPU threads.| +|`-p`, `--procs {N\|auto}` |Integer value N launches N additional local worker processes; `auto` launches as many workers as the number of local CPU threads (logical cores)| |`--machine-file ` |Run processes on hosts listed in ``| |`-i` |Interactive mode; REPL runs and `isinteractive()` is true| |`-q`, `--quiet` |Quiet startup: no banner, suppress REPL warnings| From fe1e1c4bb41a7c36422327f7e3798edb88fe43ca Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Thu, 30 Mar 2023 14:36:03 -0400 Subject: [PATCH 412/775] Update Allocs.jl (#49169) For most applications the sample rate of 1/1000 is way too low. This will require manually setting a rate for production use, but IMO it's a lot better to make the default be taylored towards interactive/beginner use rather than deployment. --- stdlib/Profile/src/Allocs.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/Profile/src/Allocs.jl b/stdlib/Profile/src/Allocs.jl index 1a52c1ec782de..2be0e2c8a1b55 100644 --- a/stdlib/Profile/src/Allocs.jl +++ b/stdlib/Profile/src/Allocs.jl @@ -30,7 +30,7 @@ struct RawResults end """ - Profile.Allocs.@profile [sample_rate=0.0001] expr + Profile.Allocs.@profile [sample_rate=0.1] expr Profile allocations that happen during `expr`, returning both the result and and AllocResults struct. @@ -67,7 +67,7 @@ macro profile(opts, ex) _prof_expr(ex, opts) end macro profile(ex) - _prof_expr(ex, :(sample_rate=0.0001)) + _prof_expr(ex, :(sample_rate=0.1)) end function _prof_expr(expr, opts) From a43b1ad406c17693e18d63ff46011aaf9d35547c Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 31 Mar 2023 20:05:20 +0900 Subject: [PATCH 413/775] show: support toplevel `InferenceResult` (#49202) --- base/show.jl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/base/show.jl b/base/show.jl index 4e56a9837c4c3..36f7df54d0008 100644 --- a/base/show.jl +++ b/base/show.jl @@ -2664,13 +2664,18 @@ function show(io::IO, src::CodeInfo; debuginfo::Symbol=:source) end function show(io::IO, inferred::Core.Compiler.InferenceResult) - tt = inferred.linfo.specTypes.parameters[2:end] + mi = inferred.linfo + tt = mi.specTypes.parameters[2:end] tts = join(["::$(t)" for t in tt], ", ") rettype = inferred.result if isa(rettype, Core.Compiler.InferenceState) rettype = rettype.bestguess end - print(io, "$(inferred.linfo.def.name)($(tts)) => $(rettype)") + if isa(mi.def, Method) + print(io, mi.def.name, "(", tts, " => ", rettype, ")") + else + print(io, "Toplevel MethodInstance thunk from ", mi.def, " => ", rettype) + end end function show(io::IO, ::Core.Compiler.NativeInterpreter) From 9001ea0c08180e5350d825b357a0b3d204350118 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 31 Mar 2023 09:23:25 -0400 Subject: [PATCH 414/775] allow more statements in generate_precompile.jl (#49200) --- contrib/generate_precompile.jl | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index e6acefd53753a..862820a944e60 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -479,16 +479,6 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe occursin("Main.", statement) && continue Base.in!(statement, statements) && continue # println(statement) - # XXX: skip some that are broken. these are caused by issue #39902 - occursin("Tuple{Artifacts.var\"#@artifact_str\", LineNumberNode, Module, Any, Any}", statement) && continue - occursin("Tuple{Base.Cartesian.var\"#@ncall\", LineNumberNode, Module, Int64, Any, Vararg{Any}}", statement) && continue - occursin("Tuple{Base.Cartesian.var\"#@ncall\", LineNumberNode, Module, Int32, Any, Vararg{Any}}", statement) && continue - occursin("Tuple{Base.Cartesian.var\"#@nloops\", LineNumberNode, Module, Any, Any, Any, Vararg{Any}}", statement) && continue - occursin("Tuple{Core.var\"#@doc\", LineNumberNode, Module, Vararg{Any}}", statement) && continue - # XXX: this is strange, as this isn't the correct representation of this - occursin("typeof(Core.IntrinsicFunction)", statement) && continue - # XXX: this is strange, as this method should not be getting compiled - occursin(", Core.Compiler.AbstractInterpreter, ", statement) && continue try ps = Meta.parse(statement) if !isexpr(ps, :call) @@ -499,15 +489,6 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe end popfirst!(ps.args) # precompile(...) ps.head = :tuple - l = ps.args[end] - if (isexpr(l, :tuple) || isexpr(l, :curly)) && length(l.args) > 0 # Tuple{...} or (...) - # XXX: precompile doesn't currently handle overloaded Vararg arguments very well. - # Replacing N with a large number works around it. - l = l.args[end] - if isexpr(l, :curly) && length(l.args) == 2 && l.args[1] === :Vararg # Vararg{T} - push!(l.args, 100) # form Vararg{T, 100} instead - end - end # println(ps) ps = Core.eval(PrecompileStagingArea, ps) precompile(ps...) From 450c1faf4823c71390809e251582f57516ce210b Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Fri, 31 Mar 2023 10:30:08 -0300 Subject: [PATCH 415/775] Fix interval for many pointers --- src/gc.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/gc.c b/src/gc.c index 195ee2d98ab4f..d316b797b8db4 100644 --- a/src/gc.c +++ b/src/gc.c @@ -3059,12 +3059,14 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) // update heuristics only if this GC was automatically triggered if (collection == JL_GC_AUTO) { - if (not_freed_enough) { - gc_num.interval = gc_num.interval * 2; - } if (large_frontier) { sweep_full = 1; + gc_num.interval = last_long_collect_interval; } + if (not_freed_enough || large_frontier) { + gc_num.interval = gc_num.interval * 2; + } + size_t maxmem = 0; #ifdef _P64 // on a big memory machine, increase max_collect_interval to totalmem / nthreads / 2 @@ -3097,6 +3099,7 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) // on the first collection after sweep_full, and the current scan perm_scanned_bytes = 0; promoted_bytes = 0; + last_long_collect_interval = gc_num.interval; } scanned_bytes = 0; // 6. start sweeping @@ -3166,9 +3169,20 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) live_bytes += -gc_num.freed + gc_num.since_sweep; if (collection == JL_GC_AUTO) { + //If we aren't freeing enough or are seeing lots and lots of pointers let it increase faster + if(!not_freed_enough || large_frontier) { + int64_t tot = 2 * (live_bytes + gc_num.since_sweep) / 3; + if (gc_num.interval > tot) { + gc_num.interval = tot; + last_long_collect_interval = tot; + } // If the current interval is larger than half the live data decrease the interval - int64_t half = live_bytes/2; - if (gc_num.interval > half) gc_num.interval = half; + } else { + int64_t half = (live_bytes / 2); + if (gc_num.interval > half) + gc_num.interval = half; + } + // But never go below default if (gc_num.interval < default_collect_interval) gc_num.interval = default_collect_interval; } @@ -3176,12 +3190,13 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) if (gc_num.interval + live_bytes > max_total_memory) { if (live_bytes < max_total_memory) { gc_num.interval = max_total_memory - live_bytes; + last_long_collect_interval = max_total_memory - live_bytes; } else { // We can't stay under our goal so let's go back to // the minimum interval and hope things get better gc_num.interval = default_collect_interval; - } + } } gc_time_summary(sweep_full, t_start, gc_end_time, gc_num.freed, From 77de8db9d2bb16d3532d53233f241d2a68aceeac Mon Sep 17 00:00:00 2001 From: Joseph Wilson Date: Sat, 1 Apr 2023 03:45:39 +1300 Subject: [PATCH 416/775] Fix typo in Base.binomial overflow message (#49204) --- base/intfuncs.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 4ecb2b36be63b..1b007700f4331 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -1111,7 +1111,7 @@ Base.@assume_effects :terminates_locally function binomial(n::T, k::T) where T<: while rr <= k xt = div(widemul(x, nn), rr) x = xt % T - x == xt || throw(OverflowError(LazyString("binomial(", n0, ", ", k0, " overflows"))) + x == xt || throw(OverflowError(LazyString("binomial(", n0, ", ", k0, ") overflows"))) rr += one(T) nn += one(T) end From 55dcfca6b06c37f18452b586f9bffda5709abb75 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sat, 1 Apr 2023 04:31:46 +0900 Subject: [PATCH 417/775] complete `ismutationfree` fixup (#49209) `ismutationfree(Vector{Any})` is fixed in #48868, but there are other array types that are instantiated within jltypes.c and thus annotated as `ismutationfree` wrongly. This commit fixes it up by covering all the builtin array types. --- src/jltypes.c | 4 ++++ test/compiler/effects.jl | 4 ---- test/reflection.jl | 9 +++++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/jltypes.c b/src/jltypes.c index fd2f1eb17f8f9..7ce7b33504fb4 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2892,6 +2892,10 @@ void jl_init_types(void) JL_GC_DISABLED // Array's mutable data is hidden, so we need to override it ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_array_type))->ismutationfree = 0; ((jl_datatype_t*)jl_array_any_type)->ismutationfree = 0; + ((jl_datatype_t*)jl_array_symbol_type)->ismutationfree = 0; + ((jl_datatype_t*)jl_array_uint8_type)->ismutationfree = 0; + ((jl_datatype_t*)jl_array_int32_type)->ismutationfree = 0; + ((jl_datatype_t*)jl_array_uint64_type)->ismutationfree = 0; // override the preferred layout for a couple types jl_lineinfonode_type->name->mayinlinealloc = 0; // FIXME: assumed to be a pointer by codegen diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index 53c558cc95106..e4dcf6e9537d9 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -775,7 +775,6 @@ end |> Core.Compiler.is_foldable_nothrow end @test Core.Compiler.is_foldable(Base.infer_effects(ImmutRef, Tuple{Any})) -@test Base.ismutationfree(Type{Union{}}) @test Core.Compiler.is_foldable_nothrow(Base.infer_effects(typejoin, ())) # nothrow-ness of subtyping operations @@ -815,6 +814,3 @@ actually_recursive1(x) = actually_recursive2(x) actually_recursive2(x) = (x <= 0) ? 1 : actually_recursive1(x - 1) actually_recursive3(x) = actually_recursive2(x) @test !Core.Compiler.is_terminates(Base.infer_effects(actually_recursive3, (Int,))) - -# https://github.com/JuliaLang/julia/issues/48856 -@test Base.ismutationfree(Vector{Any}) == false diff --git a/test/reflection.jl b/test/reflection.jl index da6e71d687c92..ec8ff09e4ad04 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -1046,3 +1046,12 @@ end f("hello") @test length(Base.specializations(only(methods(f)))) == 2 end + +# https://github.com/JuliaLang/julia/issues/48856 +@test !Base.ismutationfree(Vector{Any}) +@test !Base.ismutationfree(Vector{Symbol}) +@test !Base.ismutationfree(Vector{UInt8}) +@test !Base.ismutationfree(Vector{Int32}) +@test !Base.ismutationfree(Vector{UInt64}) + +@test Base.ismutationfree(Type{Union{}}) From dfc2cb515def17e5f6239e708d828210e4293bdf Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Fri, 31 Mar 2023 16:58:54 -0400 Subject: [PATCH 418/775] base/versions_git.sh fix when GNU date is present on BSD systems (#49184) Even on BSD systems, if `date --version` succeeds and prints `GNU coreutils`, call it with GNU options. --- base/version_git.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/base/version_git.sh b/base/version_git.sh index e7a0e6e834b77..76092e9800594 100644 --- a/base/version_git.sh +++ b/base/version_git.sh @@ -3,7 +3,7 @@ # This file collects git info and create a julia file with the GIT_VERSION_INFO struct -echo "# This file was autogenerated in base/version_git.sh" +echo "# This file was autogenerated by base/version_git.sh" echo "struct GitVersionInfo" echo " commit::String" echo " commit_short::String" @@ -60,10 +60,14 @@ else build_number=$(git rev-list --count HEAD "^$verchanged") fi -date_string=$git_time case $(uname) in Darwin | FreeBSD) - date_string="$(date -jr $git_time -u '+%Y-%m-%d %H:%M %Z')" + if (date --version 2>/dev/null | grep -q 'GNU coreutils') + then # GNU date installed and earlier on PATH than BSD date + date_string="$(date --date="@$git_time" -u '+%Y-%m-%d %H:%M %Z')" + else # otherwise assume BSD date + date_string="$(date -jr $git_time -u '+%Y-%m-%d %H:%M %Z')" + fi ;; MINGW*) git_time=$(git log -1 --pretty=format:%ci) From 8327e859b51a926d499babe71dafa23f5bbb0840 Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Fri, 31 Mar 2023 21:27:37 +0000 Subject: [PATCH 419/775] Derive T_size from DataLayout (#49188) --- src/aotcompile.cpp | 8 +- src/ccall.cpp | 14 +- src/cgutils.cpp | 11 +- src/codegen.cpp | 400 ++++++++++++++++++---------------- src/intrinsics.cpp | 6 +- src/llvm-codegen-shared.h | 47 ++-- src/llvm-late-gc-lowering.cpp | 8 +- src/llvm-multiversioning.cpp | 86 ++++---- src/llvm-pass-helpers.cpp | 117 +++++----- src/llvm-pass-helpers.h | 2 +- src/llvm-ptls.cpp | 4 +- 11 files changed, 362 insertions(+), 341 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index a749e6daa7634..63d941949343e 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -258,8 +258,6 @@ static void jl_ci_cache_lookup(const jl_cgparams_t &cgparams, jl_method_instance *ci_out = codeinst; } -void replaceUsesWithLoad(Function &F, function_ref should_replace, MDNode *tbaa_const); - // takes the running content that has collected in the shadow module and dump it to disk // this builds the object file portion of the sysimage files for fast startup, and can // also be used be extern consumers like GPUCompiler.jl to obtain a module containing @@ -1503,11 +1501,7 @@ void jl_dump_native_impl(void *native_code, dataM->setTargetTriple(SourceTM->getTargetTriple().str()); dataM->setDataLayout(jl_create_datalayout(*SourceTM)); - Type *T_size; - if (sizeof(size_t) == 8) - T_size = Type::getInt64Ty(Context); - else - T_size = Type::getInt32Ty(Context); + Type *T_size = dataM->getDataLayout().getIntPtrType(Context); Type *T_psize = T_size->getPointerTo(); bool imaging_mode = imaging_default() || jl_options.outputo; diff --git a/src/ccall.cpp b/src/ccall.cpp index 8d054bb5faa41..d6023d429420d 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1577,7 +1577,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) assert(!isVa && !llvmcall && nccallargs == 0); JL_GC_POP(); ctx.builder.CreateCall(prepare_call(gcroot_flush_func)); - emit_gc_safepoint(ctx.builder, get_current_ptls(ctx), ctx.tbaa().tbaa_const); + emit_gc_safepoint(ctx.builder, ctx.types().T_size, get_current_ptls(ctx), ctx.tbaa().tbaa_const); return ghostValue(ctx, jl_nothing_type); } else if (is_libjulia_func("jl_get_ptls_states")) { @@ -1682,7 +1682,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) ctx.builder.CreateLoad( ctx.types().T_size, ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_size, - get_current_signal_page_from_ptls(ctx.builder, get_current_ptls(ctx), ctx.tbaa().tbaa_const), -1), + get_current_signal_page_from_ptls(ctx.builder, ctx.types().T_size, get_current_ptls(ctx), ctx.tbaa().tbaa_const), -1), true); ctx.builder.CreateBr(contBB); ctx.f->getBasicBlockList().push_back(contBB); @@ -1699,8 +1699,8 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) len = ConstantInt::get(ctx.types().T_size, jl_svec_len(svecv.constant)); } else { - auto ptr = emit_bitcast(ctx, boxed(ctx, svecv), getSizePtrTy(ctx.builder.getContext())); - len = ctx.builder.CreateAlignedLoad(ctx.types().T_size, ptr, Align(sizeof(size_t))); + auto ptr = emit_bitcast(ctx, boxed(ctx, svecv), ctx.types().T_size->getPointerTo()); + len = ctx.builder.CreateAlignedLoad(ctx.types().T_size, ptr, ctx.types().alignof_ptr); // Only mark with TBAA if we are sure about the type. // This could otherwise be in a dead branch if (svecv.typ == (jl_value_t*)jl_simplevector_type) { @@ -1868,9 +1868,9 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) if (val.typ == (jl_value_t*)jl_symbol_type) { JL_GC_POP(); const int hash_offset = offsetof(jl_sym_t, hash); - Value *ph1 = emit_bitcast(ctx, decay_derived(ctx, boxed(ctx, val)), getSizePtrTy(ctx.builder.getContext())); - Value *ph2 = ctx.builder.CreateInBoundsGEP(ctx.types().T_size, ph1, ConstantInt::get(ctx.types().T_size, hash_offset / sizeof(size_t))); - LoadInst *hashval = ctx.builder.CreateAlignedLoad(ctx.types().T_size, ph2, Align(sizeof(size_t))); + Value *ph1 = emit_bitcast(ctx, decay_derived(ctx, boxed(ctx, val)), ctx.types().T_size->getPointerTo()); + Value *ph2 = ctx.builder.CreateInBoundsGEP(ctx.types().T_size, ph1, ConstantInt::get(ctx.types().T_size, hash_offset / ctx.types().sizeof_ptr)); + LoadInst *hashval = ctx.builder.CreateAlignedLoad(ctx.types().T_size, ph2, ctx.types().alignof_ptr); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); ai.decorateInst(hashval); return mark_or_box_ccall_result(ctx, hashval, retboxed, rt, unionall, static_rt); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index b83ba025e4008..792f66c27bb45 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -554,7 +554,7 @@ static Value *maybe_bitcast(jl_codectx_t &ctx, Value *V, Type *to) { static Value *julia_binding_pvalue(jl_codectx_t &ctx, Value *bv) { bv = emit_bitcast(ctx, bv, ctx.types().T_pprjlvalue); - Value *offset = ConstantInt::get(ctx.types().T_size, offsetof(jl_binding_t, value) / sizeof(size_t)); + Value *offset = ConstantInt::get(ctx.types().T_size, offsetof(jl_binding_t, value) / ctx.types().sizeof_ptr); return ctx.builder.CreateInBoundsGEP(ctx.types().T_prjlvalue, bv, offset); } @@ -1124,7 +1124,7 @@ static Value *emit_datatype_types(jl_codectx_t &ctx, Value *dt) static Value *emit_datatype_nfields(jl_codectx_t &ctx, Value *dt) { - Value *type_svec = emit_bitcast(ctx, emit_datatype_types(ctx, dt), getSizePtrTy(ctx.builder.getContext())); + Value *type_svec = emit_bitcast(ctx, emit_datatype_types(ctx, dt), ctx.types().T_size->getPointerTo()); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); return ai.decorateInst(ctx.builder.CreateAlignedLoad(ctx.types().T_size, type_svec, Align(sizeof(void*)))); } @@ -2715,7 +2715,7 @@ static Value *emit_arraylen_prim(jl_codectx_t &ctx, const jl_cgval_t &tinfo) Value *addr = ctx.builder.CreateStructGEP(ctx.types().T_jlarray, emit_bitcast(ctx, decay_derived(ctx, t), ctx.types().T_pjlarray), 1); //index (not offset) of length field in ctx.types().T_pjlarray - LoadInst *len = ctx.builder.CreateAlignedLoad(ctx.types().T_size, addr, Align(sizeof(size_t))); + LoadInst *len = ctx.builder.CreateAlignedLoad(ctx.types().T_size, addr, ctx.types().alignof_ptr); len->setOrdering(AtomicOrdering::NotAtomic); MDBuilder MDB(ctx.builder.getContext()); auto rng = MDB.createRange(Constant::getNullValue(ctx.types().T_size), ConstantInt::get(ctx.types().T_size, arraytype_maxsize(tinfo.typ))); @@ -2923,7 +2923,7 @@ static Value *emit_array_nd_index( // CreateAlloca is OK here since we are on an error branch Value *tmp = ctx.builder.CreateAlloca(ctx.types().T_size, ConstantInt::get(ctx.types().T_size, nidxs)); for (size_t k = 0; k < nidxs; k++) { - ctx.builder.CreateAlignedStore(idxs[k], ctx.builder.CreateInBoundsGEP(ctx.types().T_size, tmp, ConstantInt::get(ctx.types().T_size, k)), Align(sizeof(size_t))); + ctx.builder.CreateAlignedStore(idxs[k], ctx.builder.CreateInBoundsGEP(ctx.types().T_size, tmp, ConstantInt::get(ctx.types().T_size, k)), ctx.types().alignof_ptr); } ctx.builder.CreateCall(prepare_call(jlboundserrorv_func), { mark_callee_rooted(ctx, a), tmp, ConstantInt::get(ctx.types().T_size, nidxs) }); @@ -3040,7 +3040,8 @@ static jl_value_t *static_constant_instance(const llvm::DataLayout &DL, Constant return obj; } -static Value *call_with_attrs(jl_codectx_t &ctx, JuliaFunction *intr, Value *v) +template +static Value *call_with_attrs(jl_codectx_t &ctx, JuliaFunction *intr, Value *v) { Function *F = prepare_call(intr); CallInst *Call = ctx.builder.CreateCall(F, v); diff --git a/src/codegen.cpp b/src/codegen.cpp index 8340b1267ce6e..68fafba7a5a06 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -156,13 +156,6 @@ auto getFloatPtrTy(LLVMContext &ctxt) { auto getDoublePtrTy(LLVMContext &ctxt) { return Type::getDoublePtrTy(ctxt); } -auto getSizePtrTy(LLVMContext &ctxt) { - if (sizeof(size_t) > sizeof(uint32_t)) { - return getInt64PtrTy(ctxt); - } else { - return getInt32PtrTy(ctxt); - } -} typedef Instruction TerminatorInst; @@ -254,6 +247,8 @@ struct jl_typecache_t { IntegerType *T_sigatomic; Type *T_ppint8; + unsigned sizeof_ptr; + Align alignof_ptr; bool initialized; @@ -271,6 +266,9 @@ struct jl_typecache_t { T_ppint8 = PointerType::get(getInt8PtrTy(context), 0); T_sigatomic = Type::getIntNTy(context, sizeof(sig_atomic_t) * 8); T_size = DL.getIntPtrType(context); + sizeof_ptr = DL.getPointerSize(); + // use pointer abi alignment for intptr_t + alignof_ptr = DL.getPointerABIAlignment(0); T_jlvalue = JuliaType::get_jlvalue_ty(context); T_pjlvalue = PointerType::get(T_jlvalue, 0); T_prjlvalue = PointerType::get(T_jlvalue, AddressSpace::Tracked); @@ -481,14 +479,15 @@ struct JuliaVariable { public: StringLiteral name; bool isconst; - Type *(*_type)(LLVMContext &C); + Type *(*_type)(Type *T_size); JuliaVariable(const JuliaVariable&) = delete; JuliaVariable(const JuliaVariable&&) = delete; GlobalVariable *realize(Module *m) { if (GlobalValue *V = m->getNamedValue(name)) return cast(V); - return new GlobalVariable(*m, _type(m->getContext()), + auto T_size = m->getDataLayout().getIntPtrType(m->getContext()); + return new GlobalVariable(*m, _type(T_size), isconst, GlobalVariable::ExternalLinkage, NULL, name); } @@ -499,10 +498,31 @@ static inline void add_named_global(JuliaVariable *name, void *addr) add_named_global(name->name, addr); } + +typedef FunctionType *(*TypeFnContextOnly)(LLVMContext &C); +typedef FunctionType *(*TypeFnContextAndSizeT)(LLVMContext &C, Type *T_size); +typedef FunctionType *(*TypeFnContextAndTriple)(LLVMContext &C, const Triple &triple); + +FunctionType *invoke_type(TypeFnContextOnly f, Module &M) +{ + return f(M.getContext()); +} + +FunctionType *invoke_type(TypeFnContextAndSizeT f, Module &M) +{ + return f(M.getContext(), M.getDataLayout().getIntPtrType(M.getContext())); +} + +FunctionType *invoke_type(TypeFnContextAndTriple f, Module &M) +{ + return f(M.getContext(), Triple(M.getTargetTriple())); +} + +template struct JuliaFunction { public: llvm::StringLiteral name; - llvm::FunctionType *(*_type)(llvm::LLVMContext &C); + TypeFn_t _type; llvm::AttributeList (*_attrs)(llvm::LLVMContext &C); JuliaFunction(const JuliaFunction&) = delete; @@ -510,7 +530,7 @@ struct JuliaFunction { llvm::Function *realize(llvm::Module *m) { if (llvm::GlobalValue *V = m->getNamedValue(name)) return llvm::cast(V); - llvm::Function *F = llvm::Function::Create(_type(m->getContext()), + llvm::Function *F = llvm::Function::Create(invoke_type(_type, *m), llvm::Function::ExternalLinkage, name, m); if (_attrs) @@ -519,8 +539,8 @@ struct JuliaFunction { } }; -template -static inline void add_named_global(JuliaFunction *name, T *addr) +template +static inline void add_named_global(JuliaFunction *name, T *addr) { // cast through integer to avoid c++ pedantic warning about casting between // data and code pointers @@ -606,55 +626,55 @@ static AttributeList get_attrs_zext(LLVMContext &C) static const auto jlRTLD_DEFAULT_var = new JuliaVariable{ XSTR(jl_RTLD_DEFAULT_handle), true, - [](LLVMContext &C) { return static_cast(getInt8PtrTy(C)); }, + [](Type *T_size) -> Type * { return getInt8PtrTy(T_size->getContext()); }, }; static const auto jlexe_var = new JuliaVariable{ XSTR(jl_exe_handle), true, - [](LLVMContext &C) { return static_cast(getInt8PtrTy(C)); }, + [](Type *T_size) -> Type * { return getInt8PtrTy(T_size->getContext()); }, }; static const auto jldll_var = new JuliaVariable{ XSTR(jl_libjulia_handle), true, - [](LLVMContext &C) { return static_cast(getInt8PtrTy(C)); }, + [](Type *T_size) -> Type * { return getInt8PtrTy(T_size->getContext()); }, }; static const auto jldlli_var = new JuliaVariable{ XSTR(jl_libjulia_internal_handle), true, - [](LLVMContext &C) { return static_cast(getInt8PtrTy(C)); }, + [](Type *T_size) -> Type * { return getInt8PtrTy(T_size->getContext()); }, }; static const auto jlstack_chk_guard_var = new JuliaVariable{ XSTR(__stack_chk_guard), true, - get_pjlvalue, + [](Type *T_size) -> Type * { return get_pjlvalue(T_size->getContext()); }, }; static const auto jlgetworld_global = new JuliaVariable{ XSTR(jl_world_counter), false, - [](LLVMContext &C) { return (Type*)getSizeTy(C); }, + [](Type *T_size) -> Type * { return T_size; }, }; static const auto jlboxed_int8_cache = new JuliaVariable{ XSTR(jl_boxed_int8_cache), true, - [](LLVMContext &C) { return (Type*)ArrayType::get(get_pjlvalue(C), 256); }, + [](Type *T_size) -> Type * { return ArrayType::get(get_pjlvalue(T_size->getContext()), 256); }, }; static const auto jlboxed_uint8_cache = new JuliaVariable{ XSTR(jl_boxed_uint8_cache), true, - [](LLVMContext &C) { return (Type*)ArrayType::get(get_pjlvalue(C), 256); }, + [](Type *T_size) -> Type * { return ArrayType::get(get_pjlvalue(T_size->getContext()), 256); }, }; -static const auto jlpgcstack_func = new JuliaFunction{ +static const auto jlpgcstack_func = new JuliaFunction<>{ "julia.get_pgcstack", [](LLVMContext &C) { return FunctionType::get(PointerType::get(JuliaType::get_ppjlvalue_ty(C), 0), false); }, nullptr, }; -static const auto jladoptthread_func = new JuliaFunction{ +static const auto jladoptthread_func = new JuliaFunction<>{ "julia.get_pgcstack_or_new", jlpgcstack_func->_type, jlpgcstack_func->_attrs, @@ -664,12 +684,12 @@ static const auto jladoptthread_func = new JuliaFunction{ // important functions // Symbols are not gc-tracked, but we'll treat them as callee rooted anyway, // because they may come from a gc-rooted location -static const auto jlnew_func = new JuliaFunction{ +static const auto jlnew_func = new JuliaFunction<>{ XSTR(jl_new_structv), get_func_sig, get_func_attrs, }; -static const auto jlsplatnew_func = new JuliaFunction{ +static const auto jlsplatnew_func = new JuliaFunction<>{ XSTR(jl_new_structt), [](LLVMContext &C) { auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); @@ -678,62 +698,62 @@ static const auto jlsplatnew_func = new JuliaFunction{ }, get_attrs_basic, }; -static const auto jlthrow_func = new JuliaFunction{ +static const auto jlthrow_func = new JuliaFunction<>{ XSTR(jl_throw), [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), {PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::CalleeRooted)}, false); }, get_attrs_noreturn, }; -static const auto jlerror_func = new JuliaFunction{ +static const auto jlerror_func = new JuliaFunction<>{ XSTR(jl_error), [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), {getInt8PtrTy(C)}, false); }, get_attrs_noreturn, }; -static const auto jlatomicerror_func = new JuliaFunction{ +static const auto jlatomicerror_func = new JuliaFunction<>{ XSTR(jl_atomic_error), [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), {getInt8PtrTy(C)}, false); }, get_attrs_noreturn, }; -static const auto jltypeerror_func = new JuliaFunction{ +static const auto jltypeerror_func = new JuliaFunction<>{ XSTR(jl_type_error), [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), {getInt8PtrTy(C), JuliaType::get_prjlvalue_ty(C), PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::CalleeRooted)}, false); }, get_attrs_noreturn, }; -static const auto jlundefvarerror_func = new JuliaFunction{ +static const auto jlundefvarerror_func = new JuliaFunction<>{ XSTR(jl_undefined_var_error), [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), {PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::CalleeRooted)}, false); }, get_attrs_noreturn, }; -static const auto jlboundserrorv_func = new JuliaFunction{ +static const auto jlboundserrorv_func = new JuliaFunction{ XSTR(jl_bounds_error_ints), - [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), - {PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::CalleeRooted), getSizePtrTy(C), getSizeTy(C)}, false); }, + [](LLVMContext &C, Type *T_size) { return FunctionType::get(getVoidTy(C), + {PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::CalleeRooted), T_size->getPointerTo(), T_size}, false); }, get_attrs_noreturn, }; -static const auto jlboundserror_func = new JuliaFunction{ +static const auto jlboundserror_func = new JuliaFunction{ XSTR(jl_bounds_error_int), - [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), - {PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::CalleeRooted), getSizeTy(C)}, false); }, + [](LLVMContext &C, Type *T_size) { return FunctionType::get(getVoidTy(C), + {PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::CalleeRooted), T_size}, false); }, get_attrs_noreturn, }; -static const auto jlvboundserror_func = new JuliaFunction{ +static const auto jlvboundserror_func = new JuliaFunction{ XSTR(jl_bounds_error_tuple_int), - [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), - {JuliaType::get_pprjlvalue_ty(C), getSizeTy(C), getSizeTy(C)}, false); }, + [](LLVMContext &C, Type *T_size) { return FunctionType::get(getVoidTy(C), + {JuliaType::get_pprjlvalue_ty(C), T_size, T_size}, false); }, get_attrs_noreturn, }; -static const auto jluboundserror_func = new JuliaFunction{ +static const auto jluboundserror_func = new JuliaFunction{ XSTR(jl_bounds_error_unboxed_int), - [](LLVMContext &C) { + [](LLVMContext &C, Type *T_size) { return FunctionType::get(getVoidTy(C), - {PointerType::get(getInt8Ty(C), AddressSpace::Derived), JuliaType::get_pjlvalue_ty(C), getSizeTy(C)}, false); }, + {PointerType::get(getInt8Ty(C), AddressSpace::Derived), JuliaType::get_pjlvalue_ty(C), T_size}, false); }, get_attrs_noreturn, }; -static const auto jlcheckassign_func = new JuliaFunction{ +static const auto jlcheckassign_func = new JuliaFunction<>{ XSTR(jl_checked_assignment), [](LLVMContext &C) { auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); @@ -741,7 +761,7 @@ static const auto jlcheckassign_func = new JuliaFunction{ {T_pjlvalue, T_pjlvalue, T_pjlvalue, PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::CalleeRooted)}, false); }, nullptr, }; -static const auto jldeclareconst_func = new JuliaFunction{ +static const auto jldeclareconst_func = new JuliaFunction<>{ XSTR(jl_declare_constant), [](LLVMContext &C) { auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); @@ -749,7 +769,7 @@ static const auto jldeclareconst_func = new JuliaFunction{ {T_pjlvalue, T_pjlvalue, T_pjlvalue}, false); }, nullptr, }; -static const auto jlgetbindingorerror_func = new JuliaFunction{ +static const auto jlgetbindingorerror_func = new JuliaFunction<>{ XSTR(jl_get_binding_or_error), [](LLVMContext &C) { auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); @@ -758,7 +778,7 @@ static const auto jlgetbindingorerror_func = new JuliaFunction{ }, nullptr, }; -static const auto jlgetbindingwrorerror_func = new JuliaFunction{ +static const auto jlgetbindingwrorerror_func = new JuliaFunction<>{ XSTR(jl_get_binding_wr), [](LLVMContext &C) { auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); @@ -767,7 +787,7 @@ static const auto jlgetbindingwrorerror_func = new JuliaFunction{ }, nullptr, }; -static const auto jlboundp_func = new JuliaFunction{ +static const auto jlboundp_func = new JuliaFunction<>{ XSTR(jl_boundp), [](LLVMContext &C) { auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); @@ -776,7 +796,7 @@ static const auto jlboundp_func = new JuliaFunction{ }, nullptr, }; -static const auto jltopeval_func = new JuliaFunction{ +static const auto jltopeval_func = new JuliaFunction<>{ XSTR(jl_toplevel_eval), [](LLVMContext &C) { auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); @@ -788,7 +808,7 @@ static const auto jltopeval_func = new JuliaFunction{ Attributes(C, {Attribute::NonNull}), None); }, }; -static const auto jlcopyast_func = new JuliaFunction{ +static const auto jlcopyast_func = new JuliaFunction<>{ XSTR(jl_copy_ast), [](LLVMContext &C) { auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); @@ -800,21 +820,12 @@ static const auto jlcopyast_func = new JuliaFunction{ Attributes(C, {Attribute::NonNull}), None); }, }; -//static const auto jlnsvec_func = new JuliaFunction{ -// XSTR(jl_svec), -// [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, -// {getSizeTy(C)}, true); }, -// [](LLVMContext &C) { return AttributeList::get(C, -// AttributeSet(), -// Attributes(C, {Attribute::NonNull}), -// None); }, -//}; -static const auto jlapplygeneric_func = new JuliaFunction{ +static const auto jlapplygeneric_func = new JuliaFunction<>{ XSTR(jl_apply_generic), get_func_sig, get_func_attrs, }; -static const auto jlinvoke_func = new JuliaFunction{ +static const auto jlinvoke_func = new JuliaFunction<>{ XSTR(jl_invoke), get_func2_sig, [](LLVMContext &C) { return AttributeList::get(C, @@ -823,7 +834,7 @@ static const auto jlinvoke_func = new JuliaFunction{ {AttributeSet(), Attributes(C, {Attribute::ReadOnly, Attribute::NoCapture})}); }, }; -static const auto jlmethod_func = new JuliaFunction{ +static const auto jlmethod_func = new JuliaFunction<>{ XSTR(jl_method_def), [](LLVMContext &C) { auto T_jlvalue = JuliaType::get_jlvalue_ty(C); @@ -834,7 +845,7 @@ static const auto jlmethod_func = new JuliaFunction{ }, nullptr, }; -static const auto jlgenericfunction_func = new JuliaFunction{ +static const auto jlgenericfunction_func = new JuliaFunction<>{ XSTR(jl_generic_function_def), [](LLVMContext &C) { auto T_jlvalue = JuliaType::get_jlvalue_ty(C); @@ -845,7 +856,7 @@ static const auto jlgenericfunction_func = new JuliaFunction{ }, nullptr, }; -static const auto jllockvalue_func = new JuliaFunction{ +static const auto jllockvalue_func = new JuliaFunction<>{ XSTR(jl_lock_value), [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), {PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::CalleeRooted)}, false); }, @@ -854,7 +865,7 @@ static const auto jllockvalue_func = new JuliaFunction{ AttributeSet(), {Attributes(C, {Attribute::NoCapture})}); }, }; -static const auto jlunlockvalue_func = new JuliaFunction{ +static const auto jlunlockvalue_func = new JuliaFunction<>{ XSTR(jl_unlock_value), [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), {PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::CalleeRooted)}, false); }, @@ -863,35 +874,35 @@ static const auto jlunlockvalue_func = new JuliaFunction{ AttributeSet(), {Attributes(C, {Attribute::NoCapture})}); }, }; -static const auto jlenter_func = new JuliaFunction{ +static const auto jlenter_func = new JuliaFunction<>{ XSTR(jl_enter_handler), [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), {getInt8PtrTy(C)}, false); }, nullptr, }; -static const auto jl_current_exception_func = new JuliaFunction{ +static const auto jl_current_exception_func = new JuliaFunction<>{ XSTR(jl_current_exception), [](LLVMContext &C) { return FunctionType::get(JuliaType::get_prjlvalue_ty(C), false); }, nullptr, }; -static const auto jlleave_func = new JuliaFunction{ +static const auto jlleave_func = new JuliaFunction<>{ XSTR(jl_pop_handler), [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), {getInt32Ty(C)}, false); }, nullptr, }; -static const auto jl_restore_excstack_func = new JuliaFunction{ +static const auto jl_restore_excstack_func = new JuliaFunction{ XSTR(jl_restore_excstack), - [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), - {getSizeTy(C)}, false); }, + [](LLVMContext &C, Type *T_size) { return FunctionType::get(getVoidTy(C), + {T_size}, false); }, nullptr, }; -static const auto jl_excstack_state_func = new JuliaFunction{ +static const auto jl_excstack_state_func = new JuliaFunction{ XSTR(jl_excstack_state), - [](LLVMContext &C) { return FunctionType::get(getSizeTy(C), false); }, + [](LLVMContext &C, Type *T_size) { return FunctionType::get(T_size, false); }, nullptr, }; -static const auto jlegalx_func = new JuliaFunction{ +static const auto jlegalx_func = new JuliaFunction<>{ XSTR(jl_egal__unboxed), [](LLVMContext &C) { Type *T = PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::Derived); @@ -901,14 +912,14 @@ static const auto jlegalx_func = new JuliaFunction{ AttributeSet(), None); }, }; -static const auto jl_alloc_obj_func = new JuliaFunction{ +static const auto jl_alloc_obj_func = new JuliaFunction{ "julia.gc_alloc_obj", - [](LLVMContext &C) { + [](LLVMContext &C, Type *T_size) { auto T_jlvalue = JuliaType::get_jlvalue_ty(C); auto T_prjlvalue = PointerType::get(T_jlvalue, AddressSpace::Tracked); auto T_ppjlvalue = PointerType::get(PointerType::get(T_jlvalue, 0), 0); return FunctionType::get(T_prjlvalue, - {T_ppjlvalue, getSizeTy(C), T_prjlvalue}, false); + {T_ppjlvalue, T_size, T_prjlvalue}, false); }, [](LLVMContext &C) { return AttributeList::get(C, AttributeSet::get(C, makeArrayRef({Attribute::getWithAllocSizeArgs(C, 1, None)})), // returns %1 bytes @@ -920,7 +931,7 @@ static const auto jl_alloc_obj_func = new JuliaFunction{ }), None); }, }; -static const auto jl_newbits_func = new JuliaFunction{ +static const auto jl_newbits_func = new JuliaFunction<>{ XSTR(jl_new_bits), [](LLVMContext &C) { auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); @@ -935,7 +946,7 @@ static const auto jl_newbits_func = new JuliaFunction{ // `julia.typeof` does read memory, but it is effectively readnone before we lower // the allocation function. This is OK as long as we lower `julia.typeof` no later than // `julia.gc_alloc_obj`. -static const auto jl_typeof_func = new JuliaFunction{ +static const auto jl_typeof_func = new JuliaFunction<>{ "julia.typeof", [](LLVMContext &C) { auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); @@ -947,7 +958,7 @@ static const auto jl_typeof_func = new JuliaFunction{ Attributes(C, {Attribute::NonNull}), None); }, }; -static const auto jl_loopinfo_marker_func = new JuliaFunction{ +static const auto jl_loopinfo_marker_func = new JuliaFunction<>{ "julia.loopinfo_marker", [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), false); }, [](LLVMContext &C) { return AttributeList::get(C, @@ -955,7 +966,7 @@ static const auto jl_loopinfo_marker_func = new JuliaFunction{ AttributeSet(), None); }, }; -static const auto jl_write_barrier_func = new JuliaFunction{ +static const auto jl_write_barrier_func = new JuliaFunction<>{ "julia.write_barrier", [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), {JuliaType::get_prjlvalue_ty(C)}, true); }, @@ -964,7 +975,8 @@ static const auto jl_write_barrier_func = new JuliaFunction{ AttributeSet(), {Attributes(C, {Attribute::ReadOnly})}); }, }; -static const auto jlisa_func = new JuliaFunction{ + +static const auto jlisa_func = new JuliaFunction<>{ XSTR(jl_isa), [](LLVMContext &C) { auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); @@ -974,7 +986,7 @@ static const auto jlisa_func = new JuliaFunction{ nullptr, }; -static const auto jlsubtype_func = new JuliaFunction{ +static const auto jlsubtype_func = new JuliaFunction<>{ XSTR(jl_subtype), [](LLVMContext &C) { auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); @@ -983,7 +995,7 @@ static const auto jlsubtype_func = new JuliaFunction{ }, nullptr, }; -static const auto jlapplytype_func = new JuliaFunction{ +static const auto jlapplytype_func = new JuliaFunction<>{ XSTR(jl_instantiate_type_in_env), [](LLVMContext &C) { auto T_jlvalue = JuliaType::get_jlvalue_ty(C); @@ -1001,48 +1013,49 @@ static const auto jlapplytype_func = new JuliaFunction{ None); }, }; -static const auto jl_object_id__func = new JuliaFunction{ +static const auto jl_object_id__func = new JuliaFunction{ XSTR(jl_object_id_), - [](LLVMContext &C) { return FunctionType::get(getSizeTy(C), + [](LLVMContext &C, Type *T_size) { return FunctionType::get(T_size, {JuliaType::get_prjlvalue_ty(C), PointerType::get(getInt8Ty(C), AddressSpace::Derived)}, false); }, nullptr, }; -static const auto setjmp_func = new JuliaFunction{ +static const auto setjmp_func = new JuliaFunction{ jl_setjmp_name, - [](LLVMContext &C) { return FunctionType::get(getInt32Ty(C), - {getInt8PtrTy(C), -#ifndef _OS_WINDOWS_ - getInt32Ty(C), -#endif - }, false); }, + [](LLVMContext &C, const Triple &T) { + if (T.isOSWindows()) + return FunctionType::get(getInt32Ty(C), + {getInt8PtrTy(C)}, false); + return FunctionType::get(getInt32Ty(C), + {getInt8PtrTy(C), getInt32Ty(C)}, false); + }, [](LLVMContext &C) { return AttributeList::get(C, Attributes(C, {Attribute::ReturnsTwice}), AttributeSet(), None); }, }; -static const auto memcmp_func = new JuliaFunction{ +static const auto memcmp_func = new JuliaFunction{ XSTR(memcmp), - [](LLVMContext &C) { return FunctionType::get(getInt32Ty(C), - {getInt8PtrTy(C), getInt8PtrTy(C), getSizeTy(C)}, false); }, + [](LLVMContext &C, Type *T_size) { return FunctionType::get(getInt32Ty(C), + {getInt8PtrTy(C), getInt8PtrTy(C), T_size}, false); }, [](LLVMContext &C) { return AttributeList::get(C, Attributes(C, {Attribute::ReadOnly, Attribute::NoUnwind, Attribute::ArgMemOnly}), AttributeSet(), None); }, // TODO: inferLibFuncAttributes(*memcmp_func, TLI); }; -static const auto jldlsym_func = new JuliaFunction{ +static const auto jldlsym_func = new JuliaFunction<>{ XSTR(jl_load_and_lookup), [](LLVMContext &C) { return FunctionType::get(JuliaType::get_pvoidfunc_ty(C), {getInt8PtrTy(C), getInt8PtrTy(C), PointerType::get(getInt8PtrTy(C), 0)}, false); }, nullptr, }; -static const auto jllazydlsym_func = new JuliaFunction{ +static const auto jllazydlsym_func = new JuliaFunction<>{ XSTR(jl_lazy_load_and_lookup), [](LLVMContext &C) { return FunctionType::get(JuliaType::get_pvoidfunc_ty(C), {JuliaType::get_prjlvalue_ty(C), getInt8PtrTy(C)}, false); }, nullptr, }; -static const auto jltypeassert_func = new JuliaFunction{ +static const auto jltypeassert_func = new JuliaFunction<>{ XSTR(jl_typeassert), [](LLVMContext &C) { auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); @@ -1051,31 +1064,31 @@ static const auto jltypeassert_func = new JuliaFunction{ }, nullptr, }; -static const auto jlgetnthfieldchecked_func = new JuliaFunction{ +static const auto jlgetnthfieldchecked_func = new JuliaFunction{ XSTR(jl_get_nth_field_checked), - [](LLVMContext &C) { + [](LLVMContext &C, Type *T_size) { auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); return FunctionType::get(T_prjlvalue, - {T_prjlvalue, getSizeTy(C)}, false); + {T_prjlvalue, T_size}, false); }, [](LLVMContext &C) { return AttributeList::get(C, AttributeSet(), Attributes(C, {Attribute::NonNull}), None); }, }; -static const auto jlfieldisdefinedchecked_func = new JuliaFunction{ +static const auto jlfieldisdefinedchecked_func = new JuliaFunction{ XSTR(jl_field_isdefined_checked), - [](LLVMContext &C) { + [](LLVMContext &C, Type *T_size) { auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); return FunctionType::get(getInt32Ty(C), - {T_prjlvalue, getSizeTy(C)}, false); + {T_prjlvalue, T_size}, false); }, [](LLVMContext &C) { return AttributeList::get(C, AttributeSet(), Attributes(C, {}), None); }, }; -static const auto jlgetcfunctiontrampoline_func = new JuliaFunction{ +static const auto jlgetcfunctiontrampoline_func = new JuliaFunction<>{ XSTR(jl_get_cfunction_trampoline), [](LLVMContext &C) { auto T_jlvalue = JuliaType::get_jlvalue_ty(C); @@ -1099,18 +1112,18 @@ static const auto jlgetcfunctiontrampoline_func = new JuliaFunction{ Attributes(C, {Attribute::NonNull}), None); }, }; -static const auto diff_gc_total_bytes_func = new JuliaFunction{ +static const auto diff_gc_total_bytes_func = new JuliaFunction<>{ XSTR(jl_gc_diff_total_bytes), [](LLVMContext &C) { return FunctionType::get(getInt64Ty(C), false); }, nullptr, }; -static const auto sync_gc_total_bytes_func = new JuliaFunction{ +static const auto sync_gc_total_bytes_func = new JuliaFunction<>{ XSTR(jl_gc_sync_total_bytes), [](LLVMContext &C) { return FunctionType::get(getInt64Ty(C), {getInt64Ty(C)}, false); }, nullptr, }; -static const auto jlarray_data_owner_func = new JuliaFunction{ +static const auto jlarray_data_owner_func = new JuliaFunction<>{ XSTR(jl_array_data_owner), [](LLVMContext &C) { auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); @@ -1123,7 +1136,7 @@ static const auto jlarray_data_owner_func = new JuliaFunction{ None); }, }; #define BOX_FUNC(ct,at,attrs) \ -static const auto box_##ct##_func = new JuliaFunction{ \ +static const auto box_##ct##_func = new JuliaFunction<>{ \ XSTR(jl_box_##ct), \ [](LLVMContext &C) { return FunctionType::get(JuliaType::get_prjlvalue_ty(C),\ {at}, false); }, \ @@ -1138,27 +1151,36 @@ BOX_FUNC(uint64, getInt64Ty(C), get_attrs_zext); BOX_FUNC(char, getCharTy(C), get_attrs_zext); BOX_FUNC(float32, getFloatTy(C), get_attrs_basic); BOX_FUNC(float64, getDoubleTy(C), get_attrs_basic); -BOX_FUNC(ssavalue, getSizeTy(C), get_attrs_basic); #undef BOX_FUNC +static const auto box_ssavalue_func = new JuliaFunction{ + XSTR(jl_box_ssavalue), + [](LLVMContext &C, Type *T_size) { + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); + return FunctionType::get(T_prjlvalue, + {T_size}, false); + }, + get_attrs_basic, +}; + // placeholder functions -static const auto gcroot_flush_func = new JuliaFunction{ +static const auto gcroot_flush_func = new JuliaFunction<>{ "julia.gcroot_flush", [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), false); }, nullptr, }; -static const auto gc_preserve_begin_func = new JuliaFunction{ +static const auto gc_preserve_begin_func = new JuliaFunction<>{ "llvm.julia.gc_preserve_begin", [](LLVMContext &C) { return FunctionType::get(Type::getTokenTy(C), true); }, nullptr, }; -static const auto gc_preserve_end_func = new JuliaFunction { +static const auto gc_preserve_end_func = new JuliaFunction<> { "llvm.julia.gc_preserve_end", [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), {Type::getTokenTy(C)}, false); }, nullptr, }; -static const auto except_enter_func = new JuliaFunction{ +static const auto except_enter_func = new JuliaFunction<>{ "julia.except_enter", [](LLVMContext &C) { return FunctionType::get(getInt32Ty(C), false); }, [](LLVMContext &C) { return AttributeList::get(C, @@ -1166,7 +1188,7 @@ static const auto except_enter_func = new JuliaFunction{ AttributeSet(), None); }, }; -static const auto pointer_from_objref_func = new JuliaFunction{ +static const auto pointer_from_objref_func = new JuliaFunction<>{ "julia.pointer_from_objref", [](LLVMContext &C) { return FunctionType::get(JuliaType::get_pjlvalue_ty(C), {PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::Derived)}, false); }, @@ -1187,7 +1209,7 @@ static const auto pointer_from_objref_func = new JuliaFunction{ // with all the spelled out args appropriately moved into the argument stack buffer. // By representing it this way rather than allocating the stack buffer earlier, we // allow LLVM to make more aggressive optimizations on the call arguments. -static const auto julia_call = new JuliaFunction{ +static const auto julia_call = new JuliaFunction<>{ "julia.call", [](LLVMContext &C) { auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); @@ -1200,7 +1222,7 @@ static const auto julia_call = new JuliaFunction{ // julia.call2 is like julia.call, except that %arg1 gets passed as a register // argument at the end of the argument list. -static const auto julia_call2 = new JuliaFunction{ +static const auto julia_call2 = new JuliaFunction<>{ "julia.call2", [](LLVMContext &C) { auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); @@ -1212,49 +1234,49 @@ static const auto julia_call2 = new JuliaFunction{ get_attrs_basic, }; -static const auto jltuple_func = new JuliaFunction{XSTR(jl_f_tuple), get_func_sig, get_func_attrs}; +static const auto jltuple_func = new JuliaFunction<>{XSTR(jl_f_tuple), get_func_sig, get_func_attrs}; static const auto &builtin_func_map() { - static std::map builtins = { - { jl_f_is_addr, new JuliaFunction{XSTR(jl_f_is), get_func_sig, get_func_attrs} }, - { jl_f_typeof_addr, new JuliaFunction{XSTR(jl_f_typeof), get_func_sig, get_func_attrs} }, - { jl_f_sizeof_addr, new JuliaFunction{XSTR(jl_f_sizeof), get_func_sig, get_func_attrs} }, - { jl_f_issubtype_addr, new JuliaFunction{XSTR(jl_f_issubtype), get_func_sig, get_func_attrs} }, - { jl_f_isa_addr, new JuliaFunction{XSTR(jl_f_isa), get_func_sig, get_func_attrs} }, - { jl_f_typeassert_addr, new JuliaFunction{XSTR(jl_f_typeassert), get_func_sig, get_func_attrs} }, - { jl_f_ifelse_addr, new JuliaFunction{XSTR(jl_f_ifelse), get_func_sig, get_func_attrs} }, - { jl_f__apply_iterate_addr, new JuliaFunction{XSTR(jl_f__apply_iterate), get_func_sig, get_func_attrs} }, - { jl_f__apply_pure_addr, new JuliaFunction{XSTR(jl_f__apply_pure), get_func_sig, get_func_attrs} }, - { jl_f__call_latest_addr, new JuliaFunction{XSTR(jl_f__call_latest), get_func_sig, get_func_attrs} }, - { jl_f__call_in_world_addr, new JuliaFunction{XSTR(jl_f__call_in_world), get_func_sig, get_func_attrs} }, - { jl_f__call_in_world_total_addr, new JuliaFunction{XSTR(jl_f__call_in_world_total), get_func_sig, get_func_attrs} }, - { jl_f_throw_addr, new JuliaFunction{XSTR(jl_f_throw), get_func_sig, get_func_attrs} }, + static std::map*> builtins = { + { jl_f_is_addr, new JuliaFunction<>{XSTR(jl_f_is), get_func_sig, get_func_attrs} }, + { jl_f_typeof_addr, new JuliaFunction<>{XSTR(jl_f_typeof), get_func_sig, get_func_attrs} }, + { jl_f_sizeof_addr, new JuliaFunction<>{XSTR(jl_f_sizeof), get_func_sig, get_func_attrs} }, + { jl_f_issubtype_addr, new JuliaFunction<>{XSTR(jl_f_issubtype), get_func_sig, get_func_attrs} }, + { jl_f_isa_addr, new JuliaFunction<>{XSTR(jl_f_isa), get_func_sig, get_func_attrs} }, + { jl_f_typeassert_addr, new JuliaFunction<>{XSTR(jl_f_typeassert), get_func_sig, get_func_attrs} }, + { jl_f_ifelse_addr, new JuliaFunction<>{XSTR(jl_f_ifelse), get_func_sig, get_func_attrs} }, + { jl_f__apply_iterate_addr, new JuliaFunction<>{XSTR(jl_f__apply_iterate), get_func_sig, get_func_attrs} }, + { jl_f__apply_pure_addr, new JuliaFunction<>{XSTR(jl_f__apply_pure), get_func_sig, get_func_attrs} }, + { jl_f__call_latest_addr, new JuliaFunction<>{XSTR(jl_f__call_latest), get_func_sig, get_func_attrs} }, + { jl_f__call_in_world_addr, new JuliaFunction<>{XSTR(jl_f__call_in_world), get_func_sig, get_func_attrs} }, + { jl_f__call_in_world_total_addr, new JuliaFunction<>{XSTR(jl_f__call_in_world_total), get_func_sig, get_func_attrs} }, + { jl_f_throw_addr, new JuliaFunction<>{XSTR(jl_f_throw), get_func_sig, get_func_attrs} }, { jl_f_tuple_addr, jltuple_func }, - { jl_f_svec_addr, new JuliaFunction{XSTR(jl_f_svec), get_func_sig, get_func_attrs} }, - { jl_f_applicable_addr, new JuliaFunction{XSTR(jl_f_applicable), get_func_sig, get_func_attrs} }, - { jl_f_invoke_addr, new JuliaFunction{XSTR(jl_f_invoke), get_func_sig, get_func_attrs} }, - { jl_f_isdefined_addr, new JuliaFunction{XSTR(jl_f_isdefined), get_func_sig, get_func_attrs} }, - { jl_f_getfield_addr, new JuliaFunction{XSTR(jl_f_getfield), get_func_sig, get_func_attrs} }, - { jl_f_setfield_addr, new JuliaFunction{XSTR(jl_f_setfield), get_func_sig, get_func_attrs} }, - { jl_f_swapfield_addr, new JuliaFunction{XSTR(jl_f_swapfield), get_func_sig, get_func_attrs} }, - { jl_f_modifyfield_addr, new JuliaFunction{XSTR(jl_f_modifyfield), get_func_sig, get_func_attrs} }, - { jl_f_fieldtype_addr, new JuliaFunction{XSTR(jl_f_fieldtype), get_func_sig, get_func_attrs} }, - { jl_f_nfields_addr, new JuliaFunction{XSTR(jl_f_nfields), get_func_sig, get_func_attrs} }, - { jl_f__expr_addr, new JuliaFunction{XSTR(jl_f__expr), get_func_sig, get_func_attrs} }, - { jl_f__typevar_addr, new JuliaFunction{XSTR(jl_f__typevar), get_func_sig, get_func_attrs} }, - { jl_f_arrayref_addr, new JuliaFunction{XSTR(jl_f_arrayref), get_func_sig, get_func_attrs} }, - { jl_f_const_arrayref_addr, new JuliaFunction{XSTR(jl_f_const_arrayref), get_func_sig, get_func_attrs} }, - { jl_f_arrayset_addr, new JuliaFunction{XSTR(jl_f_arrayset), get_func_sig, get_func_attrs} }, - { jl_f_arraysize_addr, new JuliaFunction{XSTR(jl_f_arraysize), get_func_sig, get_func_attrs} }, - { jl_f_apply_type_addr, new JuliaFunction{XSTR(jl_f_apply_type), get_func_sig, get_func_attrs} }, - { jl_f_donotdelete_addr, new JuliaFunction{XSTR(jl_f_donotdelete), get_donotdelete_sig, get_donotdelete_func_attrs} }, - { jl_f_compilerbarrier_addr, new JuliaFunction{XSTR(jl_f_compilerbarrier), get_func_sig, get_func_attrs} }, - { jl_f_finalizer_addr, new JuliaFunction{XSTR(jl_f_finalizer), get_func_sig, get_func_attrs} }, - { jl_f__svec_ref_addr, new JuliaFunction{XSTR(jl_f__svec_ref), get_func_sig, get_func_attrs} } + { jl_f_svec_addr, new JuliaFunction<>{XSTR(jl_f_svec), get_func_sig, get_func_attrs} }, + { jl_f_applicable_addr, new JuliaFunction<>{XSTR(jl_f_applicable), get_func_sig, get_func_attrs} }, + { jl_f_invoke_addr, new JuliaFunction<>{XSTR(jl_f_invoke), get_func_sig, get_func_attrs} }, + { jl_f_isdefined_addr, new JuliaFunction<>{XSTR(jl_f_isdefined), get_func_sig, get_func_attrs} }, + { jl_f_getfield_addr, new JuliaFunction<>{XSTR(jl_f_getfield), get_func_sig, get_func_attrs} }, + { jl_f_setfield_addr, new JuliaFunction<>{XSTR(jl_f_setfield), get_func_sig, get_func_attrs} }, + { jl_f_swapfield_addr, new JuliaFunction<>{XSTR(jl_f_swapfield), get_func_sig, get_func_attrs} }, + { jl_f_modifyfield_addr, new JuliaFunction<>{XSTR(jl_f_modifyfield), get_func_sig, get_func_attrs} }, + { jl_f_fieldtype_addr, new JuliaFunction<>{XSTR(jl_f_fieldtype), get_func_sig, get_func_attrs} }, + { jl_f_nfields_addr, new JuliaFunction<>{XSTR(jl_f_nfields), get_func_sig, get_func_attrs} }, + { jl_f__expr_addr, new JuliaFunction<>{XSTR(jl_f__expr), get_func_sig, get_func_attrs} }, + { jl_f__typevar_addr, new JuliaFunction<>{XSTR(jl_f__typevar), get_func_sig, get_func_attrs} }, + { jl_f_arrayref_addr, new JuliaFunction<>{XSTR(jl_f_arrayref), get_func_sig, get_func_attrs} }, + { jl_f_const_arrayref_addr, new JuliaFunction<>{XSTR(jl_f_const_arrayref), get_func_sig, get_func_attrs} }, + { jl_f_arrayset_addr, new JuliaFunction<>{XSTR(jl_f_arrayset), get_func_sig, get_func_attrs} }, + { jl_f_arraysize_addr, new JuliaFunction<>{XSTR(jl_f_arraysize), get_func_sig, get_func_attrs} }, + { jl_f_apply_type_addr, new JuliaFunction<>{XSTR(jl_f_apply_type), get_func_sig, get_func_attrs} }, + { jl_f_donotdelete_addr, new JuliaFunction<>{XSTR(jl_f_donotdelete), get_donotdelete_sig, get_donotdelete_func_attrs} }, + { jl_f_compilerbarrier_addr, new JuliaFunction<>{XSTR(jl_f_compilerbarrier), get_func_sig, get_func_attrs} }, + { jl_f_finalizer_addr, new JuliaFunction<>{XSTR(jl_f_finalizer), get_func_sig, get_func_attrs} }, + { jl_f__svec_ref_addr, new JuliaFunction<>{XSTR(jl_f__svec_ref), get_func_sig, get_func_attrs} } }; return builtins; } -static const auto jl_new_opaque_closure_jlcall_func = new JuliaFunction{XSTR(jl_new_opaque_closure_jlcall), get_func_sig, get_func_attrs}; +static const auto jl_new_opaque_closure_jlcall_func = new JuliaFunction<>{XSTR(jl_new_opaque_closure_jlcall), get_func_sig, get_func_attrs}; static _Atomic(int) globalUniqueGeneratedNames{1}; @@ -1703,9 +1725,9 @@ static Value *get_current_ptls(jl_codectx_t &ctx); static Value *get_last_age_field(jl_codectx_t &ctx); static void CreateTrap(IRBuilder<> &irbuilder, bool create_new_block = true); static CallInst *emit_jlcall(jl_codectx_t &ctx, FunctionCallee theFptr, Value *theF, - const jl_cgval_t *args, size_t nargs, JuliaFunction *trampoline); -static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction *theFptr, Value *theF, - const jl_cgval_t *args, size_t nargs, JuliaFunction *trampoline); + const jl_cgval_t *args, size_t nargs, JuliaFunction<> *trampoline); +static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction<> *theFptr, Value *theF, + const jl_cgval_t *args, size_t nargs, JuliaFunction<> *trampoline); static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2, Value *nullcheck1 = nullptr, Value *nullcheck2 = nullptr); static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t nargs, const jl_cgval_t *argv, bool is_promotable=false); @@ -1719,7 +1741,8 @@ static GlobalVariable *prepare_global_in(Module *M, JuliaVariable *G) return G->realize(M); } -static Function *prepare_call_in(Module *M, JuliaFunction *G) +template +static Function *prepare_call_in(Module *M, JuliaFunction *G) { return G->realize(M); } @@ -3847,9 +3870,9 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, return true; } // String and SimpleVector's length fields have the same layout - auto ptr = emit_bitcast(ctx, boxed(ctx, obj), getSizePtrTy(ctx.builder.getContext())); + auto ptr = emit_bitcast(ctx, boxed(ctx, obj), ctx.types().T_size->getPointerTo()); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); - Value *len = ai.decorateInst(ctx.builder.CreateAlignedLoad(ctx.types().T_size, ptr, Align(sizeof(size_t)))); + Value *len = ai.decorateInst(ctx.builder.CreateAlignedLoad(ctx.types().T_size, ptr, ctx.types().alignof_ptr)); MDBuilder MDB(ctx.builder.getContext()); if (sty == jl_simplevector_type) { auto rng = MDB.createRange( @@ -3981,7 +4004,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, // emit this using the same type as emit_getfield_knownidx // so that LLVM may be able to load-load forward them and fold the result jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); - fldv = ai.decorateInst(ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, addr, Align(sizeof(size_t)))); + fldv = ai.decorateInst(ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, addr, ctx.types().alignof_ptr)); cast(fldv)->setOrdering(order <= jl_memory_order_notatomic ? AtomicOrdering::Unordered : get_llvm_atomic_order(order)); } else { @@ -4039,7 +4062,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, // Returns ctx.types().T_prjlvalue static CallInst *emit_jlcall(jl_codectx_t &ctx, FunctionCallee theFptr, Value *theF, - const jl_cgval_t *argv, size_t nargs, JuliaFunction *trampoline) + const jl_cgval_t *argv, size_t nargs, JuliaFunction<> *trampoline) { ++EmittedJLCalls; Function *TheTrampoline = prepare_call(trampoline); @@ -4059,8 +4082,8 @@ static CallInst *emit_jlcall(jl_codectx_t &ctx, FunctionCallee theFptr, Value *t } // Returns ctx.types().T_prjlvalue -static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction *theFptr, Value *theF, - const jl_cgval_t *argv, size_t nargs, JuliaFunction *trampoline) +static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction<> *theFptr, Value *theF, + const jl_cgval_t *argv, size_t nargs, JuliaFunction<> *trampoline) { return emit_jlcall(ctx, prepare_call(theFptr), theF, argv, nargs, trampoline); } @@ -5469,7 +5492,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ if (F) { jl_cgval_t jlcall_ptr = mark_julia_type(ctx, F, false, jl_voidpointer_type); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); - Instruction *I = ctx.builder.CreateAlignedLoad(ctx.types().T_size, get_last_age_field(ctx), Align(sizeof(size_t))); + Instruction *I = ctx.builder.CreateAlignedLoad(ctx.types().T_size, get_last_age_field(ctx), ctx.types().alignof_ptr); jl_cgval_t world_age = mark_julia_type(ctx, ai.decorateInst(I), false, jl_long_type); jl_cgval_t fptr; if (specF) @@ -5610,13 +5633,13 @@ static void allocate_gc_frame(jl_codectx_t &ctx, BasicBlock *b0, bool or_new=fal static Value *get_current_task(jl_codectx_t &ctx) { - return get_current_task_from_pgcstack(ctx.builder, ctx.pgcstack); + return get_current_task_from_pgcstack(ctx.builder, ctx.types().T_size, ctx.pgcstack); } // Get PTLS through current task. static Value *get_current_ptls(jl_codectx_t &ctx) { - return get_current_ptls_from_task(ctx.builder, get_current_task(ctx), ctx.tbaa().tbaa_gcframe); + return get_current_ptls_from_task(ctx.builder, ctx.types().T_size, get_current_task(ctx), ctx.tbaa().tbaa_gcframe); } // Get the address of the world age of the current task @@ -5625,8 +5648,8 @@ static Value *get_last_age_field(jl_codectx_t &ctx) Value *ct = get_current_task(ctx); return ctx.builder.CreateInBoundsGEP( ctx.types().T_size, - ctx.builder.CreateBitCast(ct, getSizePtrTy(ctx.builder.getContext())), - ConstantInt::get(ctx.types().T_size, offsetof(jl_task_t, world_age) / sizeof(size_t)), + ctx.builder.CreateBitCast(ct, ctx.types().T_size->getPointerTo()), + ConstantInt::get(ctx.types().T_size, offsetof(jl_task_t, world_age) / ctx.types().sizeof_ptr), "world_age"); } @@ -5931,10 +5954,10 @@ static Function* gen_cfun_wrapper( Value *world_age_field = get_last_age_field(ctx); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); Value *last_age = ai.decorateInst( - ctx.builder.CreateAlignedLoad(ctx.types().T_size, world_age_field, Align(sizeof(size_t)))); + ctx.builder.CreateAlignedLoad(ctx.types().T_size, world_age_field, ctx.types().alignof_ptr)); Value *world_v = ctx.builder.CreateAlignedLoad(ctx.types().T_size, - prepare_global_in(jl_Module, jlgetworld_global), Align(sizeof(size_t))); + prepare_global_in(jl_Module, jlgetworld_global), ctx.types().alignof_ptr); cast(world_v)->setOrdering(AtomicOrdering::Acquire); Value *age_ok = NULL; @@ -5943,9 +5966,9 @@ static Function* gen_cfun_wrapper( ctx.types().T_size, ctx.builder.CreateConstInBoundsGEP1_32( ctx.types().T_size, - emit_bitcast(ctx, literal_pointer_val(ctx, (jl_value_t*)codeinst), getSizePtrTy(ctx.builder.getContext())), - offsetof(jl_code_instance_t, max_world) / sizeof(size_t)), - Align(sizeof(size_t))); + emit_bitcast(ctx, literal_pointer_val(ctx, (jl_value_t*)codeinst), ctx.types().T_size->getPointerTo()), + offsetof(jl_code_instance_t, max_world) / ctx.types().sizeof_ptr), + ctx.types().alignof_ptr); age_ok = ctx.builder.CreateICmpUGE(lam_max, world_v); } ctx.builder.CreateStore(world_v, world_age_field); @@ -6484,7 +6507,7 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con assert(jl_datatype_size(output_type) == sizeof(void*) * 4); Value *strct = emit_allocobj(ctx, jl_datatype_size(output_type), literal_pointer_val(ctx, (jl_value_t*)output_type)); - Value *derived_strct = emit_bitcast(ctx, decay_derived(ctx, strct), getSizePtrTy(ctx.builder.getContext())); + Value *derived_strct = emit_bitcast(ctx, decay_derived(ctx, strct), ctx.types().T_size->getPointerTo()); MDNode *tbaa = best_tbaa(ctx.tbaa(), output_type); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); ai.decorateInst(ctx.builder.CreateStore(F, derived_strct)); @@ -7252,7 +7275,7 @@ static jl_llvm_functions_t if (toplevel || ctx.is_opaque_closure) { jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); last_age = ai.decorateInst(ctx.builder.CreateAlignedLoad( - ctx.types().T_size, world_age_field, Align(sizeof(size_t)))); + ctx.types().T_size, world_age_field, ctx.types().alignof_ptr)); } // step 7. allocate local variables slots @@ -7521,8 +7544,8 @@ static jl_llvm_functions_t ConstantInt::get(ctx.types().T_size, offsetof(jl_opaque_closure_t, world))); jl_cgval_t closure_world = typed_load(ctx, worldaddr, NULL, (jl_value_t*)jl_long_type, - theArg.tbaa, nullptr, false, AtomicOrdering::NotAtomic, false, sizeof(size_t)); - emit_unbox_store(ctx, closure_world, world_age_field, ctx.tbaa().tbaa_gcframe, sizeof(size_t)); + theArg.tbaa, nullptr, false, AtomicOrdering::NotAtomic, false, ctx.types().alignof_ptr.value()); + emit_unbox_store(ctx, closure_world, world_age_field, ctx.tbaa().tbaa_gcframe, ctx.types().alignof_ptr.value()); // Load closure env Value *envaddr = ctx.builder.CreateInBoundsGEP( @@ -7738,7 +7761,7 @@ static jl_llvm_functions_t // step 11a. Emit the entry safepoint if (JL_FEAT_TEST(ctx, safepoint_on_entry)) - emit_gc_safepoint(ctx.builder, get_current_ptls(ctx), ctx.tbaa().tbaa_const); + emit_gc_safepoint(ctx.builder, ctx.types().T_size, get_current_ptls(ctx), ctx.tbaa().tbaa_const); // step 11b. Do codegen in control flow order std::vector workstack; @@ -8066,9 +8089,9 @@ static jl_llvm_functions_t if (!jl_is_method(ctx.linfo->def.method) && !ctx.is_opaque_closure) { // TODO: inference is invalid if this has any effect (which it often does) LoadInst *world = ctx.builder.CreateAlignedLoad(ctx.types().T_size, - prepare_global_in(jl_Module, jlgetworld_global), Align(sizeof(size_t))); + prepare_global_in(jl_Module, jlgetworld_global), ctx.types().alignof_ptr); world->setOrdering(AtomicOrdering::Acquire); - ctx.builder.CreateAlignedStore(world, world_age_field, Align(sizeof(size_t))); + ctx.builder.CreateAlignedStore(world, world_age_field, ctx.types().alignof_ptr); } emit_stmtpos(ctx, stmt, cursor); mallocVisitStmt(debuginfoloc, nullptr); @@ -8667,12 +8690,15 @@ static void init_jit_functions(void) add_named_global(jldll_var, &jl_libjulia_handle); add_named_global(jldlli_var, &jl_libjulia_internal_handle); #endif - global_jlvalue_to_llvm(new JuliaVariable{"jl_true", true, get_pjlvalue}, &jl_true); - global_jlvalue_to_llvm(new JuliaVariable{"jl_false", true, get_pjlvalue}, &jl_false); - global_jlvalue_to_llvm(new JuliaVariable{"jl_emptysvec", true, get_pjlvalue}, (jl_value_t**)&jl_emptysvec); - global_jlvalue_to_llvm(new JuliaVariable{"jl_emptytuple", true, get_pjlvalue}, &jl_emptytuple); - global_jlvalue_to_llvm(new JuliaVariable{"jl_diverror_exception", true, get_pjlvalue}, &jl_diverror_exception); - global_jlvalue_to_llvm(new JuliaVariable{"jl_undefref_exception", true, get_pjlvalue}, &jl_undefref_exception); + auto size2pjlvalue = [](Type *T_size) -> Type * { + return get_pjlvalue(T_size->getContext()); + }; + global_jlvalue_to_llvm(new JuliaVariable{"jl_true", true, size2pjlvalue}, &jl_true); + global_jlvalue_to_llvm(new JuliaVariable{"jl_false", true, size2pjlvalue}, &jl_false); + global_jlvalue_to_llvm(new JuliaVariable{"jl_emptysvec", true, size2pjlvalue}, (jl_value_t**)&jl_emptysvec); + global_jlvalue_to_llvm(new JuliaVariable{"jl_emptytuple", true, size2pjlvalue}, &jl_emptytuple); + global_jlvalue_to_llvm(new JuliaVariable{"jl_diverror_exception", true, size2pjlvalue}, &jl_diverror_exception); + global_jlvalue_to_llvm(new JuliaVariable{"jl_undefref_exception", true, size2pjlvalue}, &jl_undefref_exception); add_named_global(jlgetworld_global, &jl_world_counter); add_named_global("__stack_chk_fail", &__stack_chk_fail); add_named_global(jlpgcstack_func, (void*)NULL); diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 8aec4a4990e6f..e829eea7f625a 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -45,10 +45,10 @@ FunctionType *get_intr_args5(LLVMContext &C) { return FunctionType::get(JuliaTyp const auto &runtime_func() { static struct runtime_funcs_t { - std::array runtime_func; + std::array *, num_intrinsics> runtime_func; runtime_funcs_t() : runtime_func{ -#define ADD_I(name, nargs) new JuliaFunction{XSTR(jl_##name), get_intr_args##nargs, nullptr}, +#define ADD_I(name, nargs) new JuliaFunction<>{XSTR(jl_##name), get_intr_args##nargs, nullptr}, #define ADD_HIDDEN ADD_I #define ALIAS(alias, base) nullptr, INTRINSICS @@ -734,7 +734,7 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, jl_cgval_t *argv) Value *thePtr; if (ety == (jl_value_t*)jl_any_type) { // unsafe_store to Ptr{Any} is allowed to implicitly drop GC roots. - thePtr = emit_unbox(ctx, getSizePtrTy(ctx.builder.getContext()), e, e.typ); + thePtr = emit_unbox(ctx, ctx.types().T_size->getPointerTo(), e, e.typ); Instruction *store = ctx.builder.CreateAlignedStore( ctx.builder.CreatePtrToInt(emit_pointer_from_objref(ctx, boxed(ctx, x)), ctx.types().T_size), ctx.builder.CreateInBoundsGEP(ctx.types().T_size, thePtr, im1), Align(align_nb)); diff --git a/src/llvm-codegen-shared.h b/src/llvm-codegen-shared.h index e013bfc962c11..1d4414d6aaaa8 100644 --- a/src/llvm-codegen-shared.h +++ b/src/llvm-codegen-shared.h @@ -22,17 +22,6 @@ enum AddressSpace { LastSpecial = Loaded, }; -// Do not use (only for migration purposes) -// Prefer DataLayout::getIntPtrType with addrspace argument to support cross-compilation -static inline auto getSizeTy(llvm::LLVMContext &ctxt) { - //return M.getDataLayout().getIntPtrType(M.getContext()); - if (sizeof(size_t) > sizeof(uint32_t)) { - return llvm::Type::getInt64Ty(ctxt); - } else { - return llvm::Type::getInt32Ty(ctxt); - } -} - namespace JuliaType { static inline llvm::StructType* get_jlvalue_ty(llvm::LLVMContext &C) { return llvm::StructType::get(C); @@ -181,7 +170,7 @@ static inline llvm::Value *emit_bitcast_with_builder(llvm::IRBuilder<> &builder, } // Get PTLS through current task. -static inline llvm::Value *get_current_task_from_pgcstack(llvm::IRBuilder<> &builder, llvm::Value *pgcstack) +static inline llvm::Value *get_current_task_from_pgcstack(llvm::IRBuilder<> &builder, llvm::Type *T_size, llvm::Value *pgcstack) { using namespace llvm; auto T_ppjlvalue = JuliaType::get_ppjlvalue_ty(builder.getContext()); @@ -189,17 +178,16 @@ static inline llvm::Value *get_current_task_from_pgcstack(llvm::IRBuilder<> &bui const int pgcstack_offset = offsetof(jl_task_t, gcstack); return builder.CreateInBoundsGEP( T_pjlvalue, emit_bitcast_with_builder(builder, pgcstack, T_ppjlvalue), - ConstantInt::get(getSizeTy(builder.getContext()), -(pgcstack_offset / sizeof(void *))), + ConstantInt::get(T_size, -(pgcstack_offset / sizeof(void *))), "current_task"); } // Get PTLS through current task. -static inline llvm::Value *get_current_ptls_from_task(llvm::IRBuilder<> &builder, llvm::Value *current_task, llvm::MDNode *tbaa) +static inline llvm::Value *get_current_ptls_from_task(llvm::IRBuilder<> &builder, llvm::Type *T_size, llvm::Value *current_task, llvm::MDNode *tbaa) { using namespace llvm; auto T_ppjlvalue = JuliaType::get_ppjlvalue_ty(builder.getContext()); auto T_pjlvalue = JuliaType::get_pjlvalue_ty(builder.getContext()); - auto T_size = getSizeTy(builder.getContext()); const int ptls_offset = offsetof(jl_task_t, ptls); llvm::Value *pptls = builder.CreateInBoundsGEP( T_pjlvalue, current_task, @@ -213,11 +201,10 @@ static inline llvm::Value *get_current_ptls_from_task(llvm::IRBuilder<> &builder } // Get signal page through current task. -static inline llvm::Value *get_current_signal_page_from_ptls(llvm::IRBuilder<> &builder, llvm::Value *ptls, llvm::MDNode *tbaa) +static inline llvm::Value *get_current_signal_page_from_ptls(llvm::IRBuilder<> &builder, llvm::Type *T_size, llvm::Value *ptls, llvm::MDNode *tbaa) { using namespace llvm; // return builder.CreateCall(prepare_call(reuse_signal_page_func)); - auto T_size = getSizeTy(builder.getContext()); auto T_psize = T_size->getPointerTo(); auto T_ppsize = T_psize->getPointerTo(); int nthfield = offsetof(jl_tls_states_t, safepoint) / sizeof(void *); @@ -236,22 +223,20 @@ static inline void emit_signal_fence(llvm::IRBuilder<> &builder) builder.CreateFence(AtomicOrdering::SequentiallyConsistent, SyncScope::SingleThread); } -static inline void emit_gc_safepoint(llvm::IRBuilder<> &builder, llvm::Value *ptls, llvm::MDNode *tbaa, bool final = false) +static inline void emit_gc_safepoint(llvm::IRBuilder<> &builder, llvm::Type *T_size, llvm::Value *ptls, llvm::MDNode *tbaa, bool final = false) { using namespace llvm; - llvm::Value *signal_page = get_current_signal_page_from_ptls(builder, ptls, tbaa); + llvm::Value *signal_page = get_current_signal_page_from_ptls(builder, T_size, ptls, tbaa); emit_signal_fence(builder); Module *M = builder.GetInsertBlock()->getModule(); LLVMContext &C = builder.getContext(); // inline jlsafepoint_func->realize(M) if (final) { - auto T_size = getSizeTy(builder.getContext()); builder.CreateLoad(T_size, signal_page, true); } else { Function *F = M->getFunction("julia.safepoint"); if (!F) { - auto T_size = getSizeTy(builder.getContext()); auto T_psize = T_size->getPointerTo(); FunctionType *FT = FunctionType::get(Type::getVoidTy(C), {T_psize}, false); F = Function::Create(FT, Function::ExternalLinkage, "julia.safepoint", M); @@ -262,7 +247,7 @@ static inline void emit_gc_safepoint(llvm::IRBuilder<> &builder, llvm::Value *pt emit_signal_fence(builder); } -static inline llvm::Value *emit_gc_state_set(llvm::IRBuilder<> &builder, llvm::Value *ptls, llvm::Value *state, llvm::Value *old_state, bool final) +static inline llvm::Value *emit_gc_state_set(llvm::IRBuilder<> &builder, llvm::Type *T_size, llvm::Value *ptls, llvm::Value *state, llvm::Value *old_state, bool final) { using namespace llvm; Type *T_int8 = state->getType(); @@ -288,38 +273,38 @@ static inline llvm::Value *emit_gc_state_set(llvm::IRBuilder<> &builder, llvm::V passBB, exitBB); builder.SetInsertPoint(passBB); MDNode *tbaa = get_tbaa_const(builder.getContext()); - emit_gc_safepoint(builder, ptls, tbaa, final); + emit_gc_safepoint(builder, T_size, ptls, tbaa, final); builder.CreateBr(exitBB); builder.SetInsertPoint(exitBB); return old_state; } -static inline llvm::Value *emit_gc_unsafe_enter(llvm::IRBuilder<> &builder, llvm::Value *ptls, bool final) +static inline llvm::Value *emit_gc_unsafe_enter(llvm::IRBuilder<> &builder, llvm::Type *T_size, llvm::Value *ptls, bool final) { using namespace llvm; Value *state = builder.getInt8(0); - return emit_gc_state_set(builder, ptls, state, nullptr, final); + return emit_gc_state_set(builder, T_size, ptls, state, nullptr, final); } -static inline llvm::Value *emit_gc_unsafe_leave(llvm::IRBuilder<> &builder, llvm::Value *ptls, llvm::Value *state, bool final) +static inline llvm::Value *emit_gc_unsafe_leave(llvm::IRBuilder<> &builder, llvm::Type *T_size, llvm::Value *ptls, llvm::Value *state, bool final) { using namespace llvm; Value *old_state = builder.getInt8(0); - return emit_gc_state_set(builder, ptls, state, old_state, final); + return emit_gc_state_set(builder, T_size, ptls, state, old_state, final); } -static inline llvm::Value *emit_gc_safe_enter(llvm::IRBuilder<> &builder, llvm::Value *ptls, bool final) +static inline llvm::Value *emit_gc_safe_enter(llvm::IRBuilder<> &builder, llvm::Type *T_size, llvm::Value *ptls, bool final) { using namespace llvm; Value *state = builder.getInt8(JL_GC_STATE_SAFE); - return emit_gc_state_set(builder, ptls, state, nullptr, final); + return emit_gc_state_set(builder, T_size, ptls, state, nullptr, final); } -static inline llvm::Value *emit_gc_safe_leave(llvm::IRBuilder<> &builder, llvm::Value *ptls, llvm::Value *state, bool final) +static inline llvm::Value *emit_gc_safe_leave(llvm::IRBuilder<> &builder, llvm::Type *T_size, llvm::Value *ptls, llvm::Value *state, bool final) { using namespace llvm; Value *old_state = builder.getInt8(JL_GC_STATE_SAFE); - return emit_gc_state_set(builder, ptls, state, old_state, final); + return emit_gc_state_set(builder, T_size, ptls, state, old_state, final); } // Compatibility shims for LLVM attribute APIs that were renamed in LLVM 14. diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index a67b76c1ba918..a8bab71ce91b5 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -2218,7 +2218,8 @@ Value *LateLowerGCFrame::EmitTagPtr(IRBuilder<> &builder, Type *T, Type *T_size, Value *LateLowerGCFrame::EmitLoadTag(IRBuilder<> &builder, Type *T_size, Value *V) { auto addr = EmitTagPtr(builder, T_size, T_size, V); - LoadInst *load = builder.CreateAlignedLoad(T_size, addr, Align(sizeof(size_t))); + auto &M = *builder.GetInsertBlock()->getModule(); + LoadInst *load = builder.CreateAlignedLoad(T_size, addr, M.getDataLayout().getPointerABIAlignment(0)); load->setOrdering(AtomicOrdering::Unordered); load->setMetadata(LLVMContext::MD_tbaa, tbaa_tag); MDBuilder MDB(load->getContext()); @@ -2344,7 +2345,7 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S, bool *CFGModified) { // Create a call to the `julia.gc_alloc_bytes` intrinsic, which is like // `julia.gc_alloc_obj` except it doesn't set the tag. auto allocBytesIntrinsic = getOrDeclare(jl_intrinsics::GCAllocBytes); - auto ptlsLoad = get_current_ptls_from_task(builder, CI->getArgOperand(0), tbaa_gcframe); + auto ptlsLoad = get_current_ptls_from_task(builder, T_size, CI->getArgOperand(0), tbaa_gcframe); auto ptls = builder.CreateBitCast(ptlsLoad, Type::getInt8PtrTy(builder.getContext())); auto newI = builder.CreateCall( allocBytesIntrinsic, @@ -2396,8 +2397,9 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S, bool *CFGModified) { } } // Set the tag. + auto &M = *builder.GetInsertBlock()->getModule(); StoreInst *store = builder.CreateAlignedStore( - tag, EmitTagPtr(builder, tag_type, T_size, newI), Align(sizeof(size_t))); + tag, EmitTagPtr(builder, tag_type, T_size, newI), M.getDataLayout().getPointerABIAlignment(0)); store->setOrdering(AtomicOrdering::Unordered); store->setMetadata(LLVMContext::MD_tbaa, tbaa_tag); diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 1041fdb540028..21a090724802a 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -48,8 +48,6 @@ using namespace llvm; extern Optional always_have_fma(Function&, const Triple &TT); -void replaceUsesWithLoad(Function &F, function_ref should_replace, MDNode *tbaa_const); - namespace { constexpr uint32_t clone_mask = JL_TARGET_CLONE_LOOP | JL_TARGET_CLONE_SIMD | JL_TARGET_CLONE_MATH | JL_TARGET_CLONE_CPU | JL_TARGET_CLONE_FLOAT16; @@ -385,6 +383,7 @@ struct CloneCtx { std::vector fvars; std::vector gvars; Module &M; + Type *T_size; Triple TT; // Map from original function to one based index in `fvars` @@ -441,6 +440,7 @@ CloneCtx::CloneCtx(Module &M, bool allow_bad_fvars) fvars(consume_gv(M, "jl_fvars", allow_bad_fvars)), gvars(consume_gv(M, "jl_gvars", false)), M(M), + T_size(M.getDataLayout().getIntPtrType(M.getContext())), TT(M.getTargetTriple()), allow_bad_fvars(allow_bad_fvars) { @@ -723,9 +723,9 @@ void CloneCtx::fix_gv_uses() assert(info.use->getOperandNo() == 0); assert(!val->isConstant()); auto fid = get_func_id(orig_f); - auto addr = ConstantExpr::getPtrToInt(val, getSizeTy(val->getContext())); + auto addr = ConstantExpr::getPtrToInt(val, T_size); if (info.offset) - addr = ConstantExpr::getAdd(addr, ConstantInt::get(getSizeTy(val->getContext()), info.offset)); + addr = ConstantExpr::getAdd(addr, ConstantInt::get(T_size, info.offset)); gv_relocs.emplace_back(addr, fid); val->setInitializer(rewrite_gv_init(stack)); } @@ -763,7 +763,7 @@ std::pair CloneCtx::get_reloc_slot(Function *F) const } template -static Value *rewrite_inst_use(const Stack& stack, Value *replace, Instruction *insert_before) +static Value *rewrite_inst_use(const Stack& stack, Type *T_size, Value *replace, Instruction *insert_before) { SmallVector args; uint32_t nlevel = stack.size(); @@ -800,7 +800,7 @@ static Value *rewrite_inst_use(const Stack& stack, Value *replace, Instruction * } else if (isa(val)) { replace = InsertElementInst::Create(ConstantVector::get(args), replace, - ConstantInt::get(getSizeTy(insert_before->getContext()), idx), "", + ConstantInt::get(T_size, idx), "", insert_before); } else { @@ -812,6 +812,31 @@ static Value *rewrite_inst_use(const Stack& stack, Value *replace, Instruction * return replace; } +template +static void replaceUsesWithLoad(Function &F, Type *T_size, I2GV should_replace, MDNode *tbaa_const) { + bool changed; + do { + changed = false; + for (auto uses = ConstantUses(&F, *F.getParent()); !uses.done(); uses.next()) { + auto info = uses.get_info(); + auto use_i = info.val; + GlobalVariable *slot = should_replace(*use_i); + if (!slot) + continue; + Instruction *insert_before = use_i; + if (auto phi = dyn_cast(use_i)) + insert_before = phi->getIncomingBlock(*info.use)->getTerminator(); + Instruction *ptr = new LoadInst(F.getType(), slot, "", false, insert_before); + ptr->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const); + ptr->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(ptr->getContext(), None)); + use_i->setOperand(info.use->getOperandNo(), + rewrite_inst_use(uses.get_stack(), T_size, ptr, + insert_before)); + changed = true; + } + } while (changed); +} + void CloneCtx::fix_inst_uses() { uint32_t nfuncs = orig_funcs.size(); @@ -822,7 +847,7 @@ void CloneCtx::fix_inst_uses() continue; auto F = grp.base_func(orig_f); auto grpidx = std::to_string(grp.idx); - replaceUsesWithLoad(*F, [&](Instruction &I) -> GlobalVariable * { + replaceUsesWithLoad(*F, T_size, [&](Instruction &I) -> GlobalVariable * { uint32_t id; GlobalVariable *slot; auto use_f = I.getFunction(); @@ -841,19 +866,18 @@ void CloneCtx::finalize_orig_features() { } } -static Constant *get_ptrdiff32(Constant *ptr, Constant *base) +static Constant *get_ptrdiff32(Type *T_size, Constant *ptr, Constant *base) { if (ptr->getType()->isPointerTy()) - ptr = ConstantExpr::getPtrToInt(ptr, getSizeTy(ptr->getContext())); + ptr = ConstantExpr::getPtrToInt(ptr, T_size); auto ptrdiff = ConstantExpr::getSub(ptr, base); return sizeof(void*) == 8 ? ConstantExpr::getTrunc(ptrdiff, Type::getInt32Ty(ptr->getContext())) : ptrdiff; } template -static Constant *emit_offset_table(Module &M, const std::vector &vars, StringRef name, StringRef suffix) +static Constant *emit_offset_table(Module &M, Type *T_size, const std::vector &vars, StringRef name, StringRef suffix) { auto T_int32 = Type::getInt32Ty(M.getContext()); - auto T_size = getSizeTy(M.getContext()); uint32_t nvars = vars.size(); Constant *base = nullptr; if (nvars > 0) { @@ -873,7 +897,7 @@ static Constant *emit_offset_table(Module &M, const std::vector &vars, Strin if (nvars > 0) { offsets[1] = ConstantInt::get(T_int32, 0); for (uint32_t i = 1; i < nvars; i++) - offsets[i + 1] = get_ptrdiff32(vars[i], vbase); + offsets[i + 1] = get_ptrdiff32(T_size, vars[i], vbase); } ArrayType *vars_type = ArrayType::get(T_int32, nvars + 1); auto gv = new GlobalVariable(M, vars_type, true, @@ -898,8 +922,8 @@ void CloneCtx::emit_metadata() } // Store back the information about exported functions. - auto fbase = emit_offset_table(M, fvars, "jl_fvar", suffix); - auto gbase = emit_offset_table(M, gvars, "jl_gvar", suffix); + auto fbase = emit_offset_table(M, T_size, fvars, "jl_fvar", suffix); + auto gbase = emit_offset_table(M, T_size, gvars, "jl_gvar", suffix); M.getGlobalVariable("jl_fvar_idxs")->setName("jl_fvar_idxs" + suffix); M.getGlobalVariable("jl_gvar_idxs")->setName("jl_gvar_idxs" + suffix); @@ -927,13 +951,13 @@ void CloneCtx::emit_metadata() gv_reloc_idx++) { shared_relocs.insert(id); values.push_back(id_v); - values.push_back(get_ptrdiff32(gv_relocs[gv_reloc_idx].first, gbase)); + values.push_back(get_ptrdiff32(T_size, gv_relocs[gv_reloc_idx].first, gbase)); } auto it = const_relocs.find(id); if (it != const_relocs.end()) { shared_relocs.insert(id); values.push_back(id_v); - values.push_back(get_ptrdiff32(it->second, gbase)); + values.push_back(get_ptrdiff32(T_size, it->second, gbase)); } } values[0] = ConstantInt::get(T_int32, values.size() / 2); @@ -963,7 +987,7 @@ void CloneCtx::emit_metadata() idxs.push_back(j); } if (i != 0) { - offsets.push_back(get_ptrdiff32(grp->base_func(fvars[j]), fbase)); + offsets.push_back(get_ptrdiff32(T_size, grp->base_func(fvars[j]), fbase)); } } } @@ -977,12 +1001,12 @@ void CloneCtx::emit_metadata() count++; idxs.push_back(jl_sysimg_tag_mask | j); auto f = map_get(*tgt->vmap, base_f, base_f); - offsets.push_back(get_ptrdiff32(cast(f), fbase)); + offsets.push_back(get_ptrdiff32(T_size, cast(f), fbase)); } else if (auto f = map_get(*tgt->vmap, base_f)) { count++; idxs.push_back(j); - offsets.push_back(get_ptrdiff32(cast(f), fbase)); + offsets.push_back(get_ptrdiff32(T_size, cast(f), fbase)); } } } @@ -1106,30 +1130,6 @@ void multiversioning_preannotate(Module &M) M.addModuleFlag(Module::ModFlagBehavior::Error, "julia.mv.enable", 1); } -void replaceUsesWithLoad(Function &F, function_ref should_replace, MDNode *tbaa_const) { - bool changed; - do { - changed = false; - for (auto uses = ConstantUses(&F, *F.getParent()); !uses.done(); uses.next()) { - auto info = uses.get_info(); - auto use_i = info.val; - GlobalVariable *slot = should_replace(*use_i); - if (!slot) - continue; - Instruction *insert_before = use_i; - if (auto phi = dyn_cast(use_i)) - insert_before = phi->getIncomingBlock(*info.use)->getTerminator(); - Instruction *ptr = new LoadInst(F.getType(), slot, "", false, insert_before); - ptr->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const); - ptr->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(ptr->getContext(), None)); - use_i->setOperand(info.use->getOperandNo(), - rewrite_inst_use(uses.get_stack(), ptr, - insert_before)); - changed = true; - } - } while (changed); -} - PreservedAnalyses MultiVersioning::run(Module &M, ModuleAnalysisManager &AM) { if (runMultiVersioning(M, external_use)) { diff --git a/src/llvm-pass-helpers.cpp b/src/llvm-pass-helpers.cpp index e69a7d32bda9b..b006f191937f5 100644 --- a/src/llvm-pass-helpers.cpp +++ b/src/llvm-pass-helpers.cpp @@ -101,7 +101,8 @@ llvm::Function *JuliaPassContext::getOrDeclare( else { // Otherwise, we'll declare it and add it to the module. // Declare the function. - auto func = desc.declare(*this); + auto T_size = module->getDataLayout().getIntPtrType(module->getContext()); + auto func = desc.declare(T_size); // Add it to the function list. module->getFunctionList().push_back(func); // Return the newly created function. @@ -121,7 +122,7 @@ namespace jl_intrinsics { // Annotates a function with attributes suitable for GC allocation // functions. Specifically, the return value is marked noalias and nonnull. // The allocation size is set to the first argument. - static Function *addGCAllocAttributes(Function *target, LLVMContext &context) + static Function *addGCAllocAttributes(Function *target) { addRetAttr(target, Attribute::NoAlias); addRetAttr(target, Attribute::NonNull); @@ -130,11 +131,13 @@ namespace jl_intrinsics { const IntrinsicDescription getGCFrameSlot( GET_GC_FRAME_SLOT_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_pprjlvalue = JuliaType::get_pprjlvalue_ty(ctx); return Function::Create( FunctionType::get( - PointerType::get(context.T_prjlvalue, 0), - {PointerType::get(context.T_prjlvalue, 0), Type::getInt32Ty(context.getLLVMContext())}, + T_pprjlvalue, + {T_pprjlvalue, Type::getInt32Ty(ctx)}, false), Function::ExternalLinkage, GET_GC_FRAME_SLOT_NAME); @@ -142,26 +145,27 @@ namespace jl_intrinsics { const IntrinsicDescription GCAllocBytes( GC_ALLOC_BYTES_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx); auto intrinsic = Function::Create( FunctionType::get( - context.T_prjlvalue, - { Type::getInt8PtrTy(context.getLLVMContext()), - sizeof(size_t) == sizeof(uint32_t) ? - Type::getInt32Ty(context.getLLVMContext()) : - Type::getInt64Ty(context.getLLVMContext()) }, + T_prjlvalue, + { Type::getInt8PtrTy(ctx), T_size }, false), Function::ExternalLinkage, GC_ALLOC_BYTES_NAME); - intrinsic->addFnAttr(Attribute::getWithAllocSizeArgs(context.getLLVMContext(), 1, None)); - return addGCAllocAttributes(intrinsic, context.getLLVMContext()); + intrinsic->addFnAttr(Attribute::getWithAllocSizeArgs(ctx, 1, None)); + return addGCAllocAttributes(intrinsic); }); const IntrinsicDescription newGCFrame( NEW_GC_FRAME_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_pprjlvalue = JuliaType::get_pprjlvalue_ty(ctx); auto intrinsic = Function::Create( - FunctionType::get(PointerType::get(context.T_prjlvalue, 0), {Type::getInt32Ty(context.getLLVMContext())}, false), + FunctionType::get(T_pprjlvalue, {Type::getInt32Ty(ctx)}, false), Function::ExternalLinkage, NEW_GC_FRAME_NAME); addRetAttr(intrinsic, Attribute::NoAlias); @@ -172,11 +176,13 @@ namespace jl_intrinsics { const IntrinsicDescription pushGCFrame( PUSH_GC_FRAME_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_pprjlvalue = JuliaType::get_pprjlvalue_ty(ctx); return Function::Create( FunctionType::get( - Type::getVoidTy(context.getLLVMContext()), - {PointerType::get(context.T_prjlvalue, 0), Type::getInt32Ty(context.getLLVMContext())}, + Type::getVoidTy(ctx), + {T_pprjlvalue, Type::getInt32Ty(ctx)}, false), Function::ExternalLinkage, PUSH_GC_FRAME_NAME); @@ -184,11 +190,13 @@ namespace jl_intrinsics { const IntrinsicDescription popGCFrame( POP_GC_FRAME_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_pprjlvalue = JuliaType::get_pprjlvalue_ty(ctx); return Function::Create( FunctionType::get( - Type::getVoidTy(context.getLLVMContext()), - {PointerType::get(context.T_prjlvalue, 0)}, + Type::getVoidTy(ctx), + {T_pprjlvalue}, false), Function::ExternalLinkage, POP_GC_FRAME_NAME); @@ -196,11 +204,13 @@ namespace jl_intrinsics { const IntrinsicDescription queueGCRoot( QUEUE_GC_ROOT_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx); auto intrinsic = Function::Create( FunctionType::get( - Type::getVoidTy(context.getLLVMContext()), - { context.T_prjlvalue }, + Type::getVoidTy(ctx), + { T_prjlvalue }, false), Function::ExternalLinkage, QUEUE_GC_ROOT_NAME); @@ -210,12 +220,12 @@ namespace jl_intrinsics { const IntrinsicDescription safepoint( SAFEPOINT_NAME, - [](const JuliaPassContext &context) { - auto T_size = getSizeTy(context.getLLVMContext()); + [](Type *T_size) { + auto &ctx = T_size->getContext(); auto T_psize = T_size->getPointerTo(); auto intrinsic = Function::Create( FunctionType::get( - Type::getVoidTy(context.getLLVMContext()), + Type::getVoidTy(ctx), {T_psize}, false), Function::ExternalLinkage, @@ -235,42 +245,45 @@ namespace jl_well_known { const WellKnownFunctionDescription GCBigAlloc( GC_BIG_ALLOC_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx); auto bigAllocFunc = Function::Create( FunctionType::get( - context.T_prjlvalue, - { Type::getInt8PtrTy(context.getLLVMContext()), - sizeof(size_t) == sizeof(uint32_t) ? - Type::getInt32Ty(context.getLLVMContext()) : - Type::getInt64Ty(context.getLLVMContext()) }, + T_prjlvalue, + { Type::getInt8PtrTy(ctx), T_size }, false), Function::ExternalLinkage, GC_BIG_ALLOC_NAME); - bigAllocFunc->addFnAttr(Attribute::getWithAllocSizeArgs(context.getLLVMContext(), 1, None)); - return addGCAllocAttributes(bigAllocFunc, context.getLLVMContext()); + bigAllocFunc->addFnAttr(Attribute::getWithAllocSizeArgs(ctx, 1, None)); + return addGCAllocAttributes(bigAllocFunc); }); const WellKnownFunctionDescription GCPoolAlloc( GC_POOL_ALLOC_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx); auto poolAllocFunc = Function::Create( FunctionType::get( - context.T_prjlvalue, - { Type::getInt8PtrTy(context.getLLVMContext()), Type::getInt32Ty(context.getLLVMContext()), Type::getInt32Ty(context.getLLVMContext()) }, + T_prjlvalue, + { Type::getInt8PtrTy(ctx), Type::getInt32Ty(ctx), Type::getInt32Ty(ctx) }, false), Function::ExternalLinkage, GC_POOL_ALLOC_NAME); - poolAllocFunc->addFnAttr(Attribute::getWithAllocSizeArgs(context.getLLVMContext(), 2, None)); - return addGCAllocAttributes(poolAllocFunc, context.getLLVMContext()); + poolAllocFunc->addFnAttr(Attribute::getWithAllocSizeArgs(ctx, 2, None)); + return addGCAllocAttributes(poolAllocFunc); }); const WellKnownFunctionDescription GCQueueRoot( GC_QUEUE_ROOT_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx); auto func = Function::Create( FunctionType::get( - Type::getVoidTy(context.getLLVMContext()), - { context.T_prjlvalue }, + Type::getVoidTy(ctx), + { T_prjlvalue }, false), Function::ExternalLinkage, GC_QUEUE_ROOT_NAME); @@ -280,19 +293,19 @@ namespace jl_well_known { const WellKnownFunctionDescription GCAllocTyped( GC_ALLOC_TYPED_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx); auto allocTypedFunc = Function::Create( FunctionType::get( - context.T_prjlvalue, - { Type::getInt8PtrTy(context.getLLVMContext()), - sizeof(size_t) == sizeof(uint32_t) ? - Type::getInt32Ty(context.getLLVMContext()) : - Type::getInt64Ty(context.getLLVMContext()), - Type::getInt8PtrTy(context.getLLVMContext()) }, + T_prjlvalue, + { Type::getInt8PtrTy(ctx), + T_size, + Type::getInt8PtrTy(ctx) }, false), Function::ExternalLinkage, GC_ALLOC_TYPED_NAME); - allocTypedFunc->addFnAttr(Attribute::getWithAllocSizeArgs(context.getLLVMContext(), 1, None)); - return addGCAllocAttributes(allocTypedFunc, context.getLLVMContext()); + allocTypedFunc->addFnAttr(Attribute::getWithAllocSizeArgs(ctx, 1, None)); + return addGCAllocAttributes(allocTypedFunc); }); } diff --git a/src/llvm-pass-helpers.h b/src/llvm-pass-helpers.h index 3388e6d485181..727f463dc50ef 100644 --- a/src/llvm-pass-helpers.h +++ b/src/llvm-pass-helpers.h @@ -20,7 +20,7 @@ namespace jl_intrinsics { // intrinsics and declare new intrinsics if necessary. struct IntrinsicDescription final { // The type of function that declares an intrinsic. - typedef llvm::Function *(*DeclarationFunction)(const JuliaPassContext&) JL_NOTSAFEPOINT; + typedef llvm::Function *(*DeclarationFunction)(llvm::Type *T_size) JL_NOTSAFEPOINT; // Creates an intrinsic description with a particular // name and declaration function. diff --git a/src/llvm-ptls.cpp b/src/llvm-ptls.cpp index 3c73b23dddac6..8174832b3cebf 100644 --- a/src/llvm-ptls.cpp +++ b/src/llvm-ptls.cpp @@ -183,7 +183,7 @@ void LowerPTLS::fix_pgcstack_use(CallInst *pgcstack, Function *pgcstack_getter, IRBuilder<> builder(fastTerm->getParent()); fastTerm->removeFromParent(); MDNode *tbaa = tbaa_gcframe; - Value *prior = emit_gc_unsafe_enter(builder, get_current_ptls_from_task(builder, get_current_task_from_pgcstack(builder, pgcstack), tbaa), true); + Value *prior = emit_gc_unsafe_enter(builder, T_size, get_current_ptls_from_task(builder, T_size, get_current_task_from_pgcstack(builder, T_size, pgcstack), tbaa), true); builder.Insert(fastTerm); phi->addIncoming(pgcstack, fastTerm->getParent()); // emit pre-return cleanup @@ -195,7 +195,7 @@ void LowerPTLS::fix_pgcstack_use(CallInst *pgcstack, Function *pgcstack_getter, for (auto &BB : *pgcstack->getParent()->getParent()) { if (isa(BB.getTerminator())) { IRBuilder<> builder(BB.getTerminator()); - emit_gc_unsafe_leave(builder, get_current_ptls_from_task(builder, get_current_task_from_pgcstack(builder, phi), tbaa), last_gc_state, true); + emit_gc_unsafe_leave(builder, T_size, get_current_ptls_from_task(builder, T_size, get_current_task_from_pgcstack(builder, T_size, phi), tbaa), last_gc_state, true); } } } From 7618e64f9c0ac77ee6e5c6cf3e3251526ccf8ff6 Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Fri, 31 Mar 2023 19:22:14 -0400 Subject: [PATCH 420/775] Tasks: don't advance task RNG on task spawn (#49110) --- base/sysimg.jl | 1 + src/gc.c | 6 +- src/jltypes.c | 6 +- src/julia.h | 4 +- src/task.c | 201 ++++++++++++++++++-- stdlib/Random/src/Xoshiro.jl | 27 +-- stdlib/Random/test/runtests.jl | 47 +++++ test/llvmpasses/alloc-opt-gcframe.jl | 2 +- test/llvmpasses/late-lower-gc-addrspaces.ll | 4 +- test/llvmpasses/late-lower-gc.ll | 4 +- 10 files changed, 259 insertions(+), 43 deletions(-) diff --git a/base/sysimg.jl b/base/sysimg.jl index 7d3826eb13bdc..6f7219afda550 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -27,6 +27,7 @@ let task.rngState1 = 0x7431eaead385992c task.rngState2 = 0x503e1d32781c2608 task.rngState3 = 0x3a77f7189200c20b + task.rngState4 = 0x5502376d099035ae # Stdlibs sorted in dependency, then alphabetical, order by contrib/print_sorted_stdlibs.jl # Run with the `--exclude-jlls` option to filter out all JLL packages diff --git a/src/gc.c b/src/gc.c index d316b797b8db4..abe9169137b55 100644 --- a/src/gc.c +++ b/src/gc.c @@ -501,9 +501,9 @@ static void jl_gc_run_finalizers_in_list(jl_task_t *ct, arraylist_t *list) JL_NO ct->sticky = sticky; } -static uint64_t finalizer_rngState[4]; +static uint64_t finalizer_rngState[JL_RNG_SIZE]; -void jl_rng_split(uint64_t to[4], uint64_t from[4]) JL_NOTSAFEPOINT; +void jl_rng_split(uint64_t dst[JL_RNG_SIZE], uint64_t src[JL_RNG_SIZE]) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_gc_init_finalizer_rng_state(void) { @@ -532,7 +532,7 @@ static void run_finalizers(jl_task_t *ct) jl_atomic_store_relaxed(&jl_gc_have_pending_finalizers, 0); arraylist_new(&to_finalize, 0); - uint64_t save_rngState[4]; + uint64_t save_rngState[JL_RNG_SIZE]; memcpy(&save_rngState[0], &ct->rngState[0], sizeof(save_rngState)); jl_rng_split(ct->rngState, finalizer_rngState); diff --git a/src/jltypes.c b/src/jltypes.c index 7ce7b33504fb4..4a451e9b70e80 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2769,7 +2769,7 @@ void jl_init_types(void) JL_GC_DISABLED NULL, jl_any_type, jl_emptysvec, - jl_perm_symsvec(15, + jl_perm_symsvec(16, "next", "queue", "storage", @@ -2781,11 +2781,12 @@ void jl_init_types(void) JL_GC_DISABLED "rngState1", "rngState2", "rngState3", + "rngState4", "_state", "sticky", "_isexception", "priority"), - jl_svec(15, + jl_svec(16, jl_any_type, jl_any_type, jl_any_type, @@ -2797,6 +2798,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_uint64_type, jl_uint64_type, jl_uint64_type, + jl_uint64_type, jl_uint8_type, jl_bool_type, jl_bool_type, diff --git a/src/julia.h b/src/julia.h index 64b7fc452a4da..fc8a4b8daa524 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1911,6 +1911,8 @@ typedef struct _jl_handler_t { size_t world_age; } jl_handler_t; +#define JL_RNG_SIZE 5 // xoshiro 4 + splitmix 1 + typedef struct _jl_task_t { JL_DATA_TYPE jl_value_t *next; // invasive linked list for scheduler @@ -1922,7 +1924,7 @@ typedef struct _jl_task_t { jl_function_t *start; // 4 byte padding on 32-bit systems // uint32_t padding0; - uint64_t rngState[4]; + uint64_t rngState[JL_RNG_SIZE]; _Atomic(uint8_t) _state; uint8_t sticky; // record whether this Task can be migrated to a new thread _Atomic(uint8_t) _isexception; // set if `result` is an exception to throw or that we exited with diff --git a/src/task.c b/src/task.c index 7102cdec35d08..580ad444d8cad 100644 --- a/src/task.c +++ b/src/task.c @@ -866,28 +866,187 @@ uint64_t jl_genrandom(uint64_t rngState[4]) JL_NOTSAFEPOINT return res; } -void jl_rng_split(uint64_t to[4], uint64_t from[4]) JL_NOTSAFEPOINT +/* +The jl_rng_split function forks a task's RNG state in a way that is essentially +guaranteed to avoid collisions between the RNG streams of all tasks. The main +RNG is the xoshiro256++ RNG whose state is stored in rngState[0..3]. There is +also a small internal RNG used for task forking stored in rngState[4]. This +state is used to iterate a LCG (linear congruential generator), which is then +put through four different variations of the strongest PCG output function, +referred to as PCG-RXS-M-XS-64 [1]. This output function is invertible: it maps +a 64-bit state to 64-bit output; which is one of the reasons it's not +recommended for general purpose RNGs unless space is at a premium, but in our +usage invertibility is actually a benefit, as is explained below. + +The goal of jl_rng_split is to perturb the state of each child task's RNG in +such a way each that for an entire tree of tasks spawned starting with a given +state in a root task, no two tasks have the same RNG state. Moreover, we want to +do this in a way that is deterministic and repeatable based on (1) the root +task's seed, (2) how many random numbers are generated, and (3) the task tree +structure. The RNG state of a parent task is allowed to affect the initial RNG +state of a child task, but the mere fact that a child was spawned should not +alter the RNG output of the parent. This second requirement rules out using the +main RNG to seed children -- some separate state must be maintained and changed +upon forking a child task while leaving the main RNG state unchanged. + +The basic approach is that used by the DotMix [2] and SplitMix [3] RNG systems: +each task is uniquely identified by a sequence of "pedigree" numbers, indicating +where in the task tree it was spawned. This vector of pedigree coordinates is +then reduced to a single value by computing a dot product with a common vector +of random weights. The DotMix paper provides a proof that this dot product hash +value (referred to as a "compression function") is collision resistant in the +sense the the pairwise collision probability of two distinct tasks is 1/N where +N is the number of possible weight values. Both DotMix and SplitMix use a prime +value of N because the proof requires that the difference between two distinct +pedigree coordinates must be invertible, which is guaranteed by N being prime. +We take a different approach: we instead limit pedigree coordinates to being +binary instead -- when a task spawns a child, both tasks share the same pedigree +prefix, with the parent appending a zero and the child appending a one. This way +a binary pedigree vector uniquely identifies each task. Moreover, since the +coordinates are binary, the difference between coordinates is always one which +is its own inverse regardless of whether N is prime or not. This allows us to +compute the dot product modulo 2^64 using native machine arithmetic, which is +considerably more efficient and simpler to implement than arithmetic in a prime +modulus. It also means that when accumulating the dot product incrementally, as +described in SplitMix, we don't need to multiply weights by anything, we simply +add the random weight for the current task tree depth to the parent's dot +product to derive the child's dot product. + +We use the LCG in rngState[4] to derive generate pseudorandom weights for the +dot product. Each time a child is forked, we update the LCG in both parent and +child tasks. In the parent, that's all we have to do -- the main RNG state +remains unchanged (recall that spawning a child should *not* affect subsequence +RNG draws in the parent). The next time the parent forks a child, the dot +product weight used will be different, corresponding to being a level deeper in +the binary task tree. In the child, we use the LCG state to generate four +pseudorandom 64-bit weights (more below) and add each weight to one of the +xoshiro256 state registers, rngState[0..3]. If we assume the main RNG remains +unused in all tasks, then each register rngState[0..3] accumulates a different +Dot/SplitMix dot product hash as additional child tasks are spawned. Each one is +collision resistant with a pairwise collision chance of only 1/2^64. Assuming +that the four pseudorandom 64-bit weight streams are sufficiently independent, +the pairwise collision probability for distinct tasks is 1/2^256. If we somehow +managed to spawn a trillion tasks, the probability of a collision would be on +the order of 1/10^54. Practically impossible. Put another way, this is the same +as the probability of two SHA256 hash values accidentally colliding, which we +generally consider so unlikely as not to be worth worrying about. + +What about the random "junk" that's in the xoshiro256 state registers from +normal use of the RNG? For a tree of tasks spawned with no intervening samples +taken from the main RNG, all tasks start with the same junk which doesn't affect +the chance of collision. The Dot/SplitMix papers even suggest adding a random +base value to the dot product, so we can consider whatever happens to be in the +xoshiro256 registers to be that. What if the main RNG gets used between task +forks? In that case, the initial state registers will be different. The DotMix +collision resistance proof doesn't apply without modification, but we can +generalize the setup by adding a different base constant to each compression +function and observe that we still have a 1/N chance of the weight value +matching that exact difference. This proves collision resistance even between +tasks whose dot product hashes are computed with arbitrary offsets. We can +conclude that this scheme provides collision resistance even in the face of +different starting states of the main RNG. Does this seem too good to be true? +Perhaps another way of thinking about it will help. Suppose we seeded each task +completely randomly. Then there would also be a 1/2^256 chance of collision, +just as the DotMix proof gives. Essentially what the proof is telling us is that +if the weights are chosen uniformly and uncorrelated with the rest of the +compression function, then the dot product construction is a good enough way to +pseudorandomly seed each task. From that perspective, it's easier to believe +that adding an arbitrary constant to each seed doesn't worsen its randomness. + +This leaves us with the question of how to generate four pseudorandom weights to +add to the rngState[0..3] registers at each depth of the task tree. The scheme +used here is that a single 64-bit LCG state is iterated in both parent and child +at each task fork, and four different variations of the PCG-RXS-M-XS-64 output +function are applied to that state to generate four different pseudorandom +weights. Another obvious way to generate four weights would be to iterate the +LCG four times per task split. There are two main reasons we've chosen to use +four output variants instead: + +1. Advancing four times per fork reduces the set of possible weights that each + register can be perturbed by from 2^64 to 2^60. Since collision resistance is + proportional to the number of possible weight values, that would reduce + collision resistance. + +2. It's easier to compute four PCG output variants in parallel. Iterating the + LCG is inherently sequential. Each PCG variant can be computed independently + from the LCG state. All four can even be computed at once with SIMD vector + instructions, but the compiler doesn't currently choose to do that. + +A key question is whether the approach of using four variations of PCG-RXS-M-XS +is sufficiently random both within and between streams to provide the collision +resistance we expect. We obviously can't test that with 256 bits, but we have +tested it with a reduced state analogue using four PCG-RXS-M-XS-8 output +variations applied to a common 8-bit LCG. Test results do indicate sufficient +independence: a single register has collisions at 2^5 while four registers only +start having collisions at 2^20, which is actually better scaling of collision +resistance than we expect in theory. In theory, with one byte of resistance we +have a 50% chance of some collision at 20, which matches, but four bytes gives a +50% chance of collision at 2^17 and our (reduced size analogue) construction is +still collision free at 2^19. This may be due to the next observation, which guarantees collision avoidance for certain shapes of task trees as a result of using an +invertible RNG to generate weights. + +In the specific case where a parent task spawns a sequence of child tasks with +no intervening usage of its main RNG, the parent and child tasks are actually +_guaranteed_ to have different RNG states. This is true because the four PCG +streams each produce every possible 2^64 bit output exactly once in the full +2^64 period of the LCG generator. This is considered a weakness of PCG-RXS-M-XS +when used as a general purpose RNG, but is quite beneficial in this application. +Since each of up to 2^64 children will be perturbed by different weights, they +cannot have hash collisions. What about parent colliding with child? That can +only happen if all four main RNG registers are perturbed by exactly zero. This +seems unlikely, but could it occur? Consider this part of each output function: + + p ^= p >> ((p >> 59) + 5); + p *= m[i]; + p ^= p >> 43 + +It's easy to check that this maps zero to zero. An unchanged parent RNG can only +happen if all four `p` values are zero at the end of this, which implies that +they were all zero at the beginning. However, that is impossible since the four +`p` values differ from `x` by different additive constants, so they cannot all +be zero. Stated more generally, this non-collision property: assuming the main +RNG isn't used between task forks, sibling and parent tasks cannot have RNG +collisions. If the task tree structure is more deeply nested or if there are +intervening uses of the main RNG, we're back to relying on "merely" 256 bits of +collision resistance, but it's nice to know that in what is likely the most +common case, RNG collisions are actually impossible. This fact may also explain +better-than-theoretical collision resistance observed in our experiment with a +reduced size analogue of our hashing system. + +[1]: https://www.pcg-random.org/pdf/hmc-cs-2014-0905.pdf + +[2]: http://supertech.csail.mit.edu/papers/dprng.pdf + +[3]: https://gee.cs.oswego.edu/dl/papers/oopsla14.pdf +*/ +void jl_rng_split(uint64_t dst[JL_RNG_SIZE], uint64_t src[JL_RNG_SIZE]) JL_NOTSAFEPOINT { - /* TODO: consider a less ad-hoc construction - Ideally we could just use the output of the random stream to seed the initial - state of the child. Out of an overabundance of caution we multiply with - effectively random coefficients, to break possible self-interactions. - - It is not the goal to mix bits -- we work under the assumption that the - source is well-seeded, and its output looks effectively random. - However, xoshiro has never been studied in the mode where we seed the - initial state with the output of another xoshiro instance. - - Constants have nothing up their sleeve: - 0x02011ce34bce797f == hash(UInt(1))|0x01 - 0x5a94851fb48a6e05 == hash(UInt(2))|0x01 - 0x3688cf5d48899fa7 == hash(UInt(3))|0x01 - 0x867b4bb4c42e5661 == hash(UInt(4))|0x01 - */ - to[0] = 0x02011ce34bce797f * jl_genrandom(from); - to[1] = 0x5a94851fb48a6e05 * jl_genrandom(from); - to[2] = 0x3688cf5d48899fa7 * jl_genrandom(from); - to[3] = 0x867b4bb4c42e5661 * jl_genrandom(from); + // load and advance the internal LCG state + uint64_t x = src[4]; + src[4] = dst[4] = x * 0xd1342543de82ef95 + 1; + // high spectrum multiplier from https://arxiv.org/abs/2001.05304 + + static const uint64_t a[4] = { + 0xe5f8fa077b92a8a8, // random additive offsets... + 0x7a0cd918958c124d, + 0x86222f7d388588d4, + 0xd30cbd35f2b64f52 + }; + static const uint64_t m[4] = { + 0xaef17502108ef2d9, // standard PCG multiplier + 0xf34026eeb86766af, // random odd multipliers... + 0x38fd70ad58dd9fbb, + 0x6677f9b93ab0c04d + }; + + // PCG-RXS-M-XS output with four variants + for (int i = 0; i < 4; i++) { + uint64_t p = x + a[i]; + p ^= p >> ((p >> 59) + 5); + p *= m[i]; + p ^= p >> 43; + dst[i] = src[i] + p; // SplitMix dot product + } } JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion_future, size_t ssize) diff --git a/stdlib/Random/src/Xoshiro.jl b/stdlib/Random/src/Xoshiro.jl index 5b8aa4644d140..3be276ad23754 100644 --- a/stdlib/Random/src/Xoshiro.jl +++ b/stdlib/Random/src/Xoshiro.jl @@ -113,12 +113,17 @@ struct TaskLocalRNG <: AbstractRNG end TaskLocalRNG(::Nothing) = TaskLocalRNG() rng_native_52(::TaskLocalRNG) = UInt64 -function setstate!(x::TaskLocalRNG, s0::UInt64, s1::UInt64, s2::UInt64, s3::UInt64) +function setstate!( + x::TaskLocalRNG, + s0::UInt64, s1::UInt64, s2::UInt64, s3::UInt64, # xoshiro256 state + s4::UInt64 = 1s0 + 3s1 + 5s2 + 7s3, # internal splitmix state +) t = current_task() t.rngState0 = s0 t.rngState1 = s1 t.rngState2 = s2 t.rngState3 = s3 + t.rngState4 = s4 x end @@ -128,11 +133,11 @@ end tmp = s0 + s3 res = ((tmp << 23) | (tmp >> 41)) + s0 t = s1 << 17 - s2 = xor(s2, s0) - s3 = xor(s3, s1) - s1 = xor(s1, s2) - s0 = xor(s0, s3) - s2 = xor(s2, t) + s2 ⊻= s0 + s3 ⊻= s1 + s1 ⊻= s2 + s0 ⊻= s3 + s2 ⊻= t s3 = s3 << 45 | s3 >> 19 task.rngState0, task.rngState1, task.rngState2, task.rngState3 = s0, s1, s2, s3 res @@ -159,7 +164,7 @@ seed!(rng::Union{TaskLocalRNG, Xoshiro}, seed::Integer) = seed!(rng, make_seed(s @inline function rand(rng::Union{TaskLocalRNG, Xoshiro}, ::SamplerType{UInt128}) first = rand(rng, UInt64) second = rand(rng,UInt64) - second + UInt128(first)<<64 + second + UInt128(first) << 64 end @inline rand(rng::Union{TaskLocalRNG, Xoshiro}, ::SamplerType{Int128}) = rand(rng, UInt128) % Int128 @@ -178,14 +183,14 @@ end function copy!(dst::TaskLocalRNG, src::Xoshiro) t = current_task() - t.rngState0, t.rngState1, t.rngState2, t.rngState3 = src.s0, src.s1, src.s2, src.s3 - dst + setstate!(dst, src.s0, src.s1, src.s2, src.s3) + return dst end function copy!(dst::Xoshiro, src::TaskLocalRNG) t = current_task() - dst.s0, dst.s1, dst.s2, dst.s3 = t.rngState0, t.rngState1, t.rngState2, t.rngState3 - dst + setstate!(dst, t.rngState0, t.rngState1, t.rngState2, t.rngState3) + return dst end function ==(a::Xoshiro, b::TaskLocalRNG) diff --git a/stdlib/Random/test/runtests.jl b/stdlib/Random/test/runtests.jl index 4e8d3b4ffb39a..3f570d862b743 100644 --- a/stdlib/Random/test/runtests.jl +++ b/stdlib/Random/test/runtests.jl @@ -1018,3 +1018,50 @@ guardseed() do @test f42752(true) === val end end + +@testset "TaskLocalRNG: stream collision smoke test" begin + # spawn a trinary tree of tasks: + # - spawn three recursive child tasks in each + # - generate a random UInt64 in each before, after and between + # - collect and count all the generated random values + # these should all be distinct across all tasks + function gen(d) + r = rand(UInt64) + vals = [r] + if d ≥ 0 + append!(vals, gent(d - 1)) + isodd(r) && append!(vals, gent(d - 1)) + push!(vals, rand(UInt64)) + iseven(r) && append!(vals, gent(d - 1)) + end + push!(vals, rand(UInt64)) + end + gent(d) = fetch(@async gen(d)) + seeds = rand(RandomDevice(), UInt64, 5) + for seed in seeds + Random.seed!(seed) + vals = gen(6) + @test allunique(vals) + end +end + +@testset "TaskLocalRNG: child doesn't affect parent" begin + seeds = rand(RandomDevice(), UInt64, 5) + for seed in seeds + Random.seed!(seed) + x = rand(UInt64) + y = rand(UInt64) + n = 3 + for i = 1:n + Random.seed!(seed) + @sync for j = 0:i + @async rand(UInt64) + end + @test x == rand(UInt64) + @sync for j = 0:(n-i) + @async rand(UInt64) + end + @test y == rand(UInt64) + end + end +end diff --git a/test/llvmpasses/alloc-opt-gcframe.jl b/test/llvmpasses/alloc-opt-gcframe.jl index c5a5b2be86614..e7ddf12d79bc7 100644 --- a/test/llvmpasses/alloc-opt-gcframe.jl +++ b/test/llvmpasses/alloc-opt-gcframe.jl @@ -14,7 +14,7 @@ target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" # CHECK-LABEL: @return_obj # CHECK-NOT: @julia.gc_alloc_obj # CHECK: %current_task = getelementptr inbounds {}*, {}** %gcstack, i64 -12 -# CHECK-NEXT: [[ptls_field:%.*]] = getelementptr inbounds {}*, {}** %current_task, i64 15 +# CHECK-NEXT: [[ptls_field:%.*]] = getelementptr inbounds {}*, {}** %current_task, i64 16 # CHECK-NEXT: [[ptls_load:%.*]] = load {}*, {}** [[ptls_field]], align 8, !tbaa !0 # CHECK-NEXT: [[ppjl_ptls:%.*]] = bitcast {}* [[ptls_load]] to {}** # CHECK-NEXT: [[ptls_i8:%.*]] = bitcast {}** [[ppjl_ptls]] to i8* diff --git a/test/llvmpasses/late-lower-gc-addrspaces.ll b/test/llvmpasses/late-lower-gc-addrspaces.ll index 7497febf1e846..8bdcbdeacf8f3 100644 --- a/test/llvmpasses/late-lower-gc-addrspaces.ll +++ b/test/llvmpasses/late-lower-gc-addrspaces.ll @@ -45,7 +45,7 @@ top: %0 = bitcast {}*** %pgcstack to {}** %current_task = getelementptr inbounds {}*, {}** %0, i64 -12 ; CHECK: %current_task = getelementptr inbounds {}*, {}** %0, i64 -12 -; CHECK-NEXT: [[ptls_field:%.*]] = getelementptr inbounds {}*, {}** %current_task, i64 15 +; CHECK-NEXT: [[ptls_field:%.*]] = getelementptr inbounds {}*, {}** %current_task, i64 16 ; CHECK-NEXT: [[ptls_load:%.*]] = load {}*, {}** [[ptls_field]], align 8, !tbaa !0 ; CHECK-NEXT: [[ppjl_ptls:%.*]] = bitcast {}* [[ptls_load]] to {}** ; CHECK-NEXT: [[ptls_i8:%.*]] = bitcast {}** [[ppjl_ptls]] to i8* @@ -70,7 +70,7 @@ top: %0 = bitcast {}*** %pgcstack to {}** %current_task = getelementptr inbounds {}*, {}** %0, i64 -12 ; CHECK: %current_task = getelementptr inbounds {}*, {}** %0, i64 -12 -; CHECK-NEXT: [[ptls_field:%.*]] = getelementptr inbounds {}*, {}** %current_task, i64 15 +; CHECK-NEXT: [[ptls_field:%.*]] = getelementptr inbounds {}*, {}** %current_task, i64 16 ; CHECK-NEXT: [[ptls_load:%.*]] = load {}*, {}** [[ptls_field]], align 8, !tbaa !0 ; CHECK-NEXT: [[ppjl_ptls:%.*]] = bitcast {}* [[ptls_load]] to {}** ; CHECK-NEXT: [[ptls_i8:%.*]] = bitcast {}** [[ppjl_ptls]] to i8* diff --git a/test/llvmpasses/late-lower-gc.ll b/test/llvmpasses/late-lower-gc.ll index 65a67c78d7810..04adfe72ff0b6 100644 --- a/test/llvmpasses/late-lower-gc.ll +++ b/test/llvmpasses/late-lower-gc.ll @@ -42,7 +42,7 @@ top: %0 = bitcast {}*** %pgcstack to {}** %current_task = getelementptr inbounds {}*, {}** %0, i64 -12 ; CHECK: %current_task = getelementptr inbounds {}*, {}** %0, i64 -12 -; CHECK-NEXT: [[ptls_field:%.*]] = getelementptr inbounds {}*, {}** %current_task, i64 15 +; CHECK-NEXT: [[ptls_field:%.*]] = getelementptr inbounds {}*, {}** %current_task, i64 16 ; CHECK-NEXT: [[ptls_load:%.*]] = load {}*, {}** [[ptls_field]], align 8, !tbaa !0 ; CHECK-NEXT: [[ppjl_ptls:%.*]] = bitcast {}* [[ptls_load]] to {}** ; CHECK-NEXT: [[ptls_i8:%.*]] = bitcast {}** [[ppjl_ptls]] to i8* @@ -67,7 +67,7 @@ top: %0 = bitcast {}*** %pgcstack to {}** %current_task = getelementptr inbounds {}*, {}** %0, i64 -12 ; CHECK: %current_task = getelementptr inbounds {}*, {}** %0, i64 -12 -; CHECK-NEXT: [[ptls_field:%.*]] = getelementptr inbounds {}*, {}** %current_task, i64 15 +; CHECK-NEXT: [[ptls_field:%.*]] = getelementptr inbounds {}*, {}** %current_task, i64 16 ; CHECK-NEXT: [[ptls_load:%.*]] = load {}*, {}** [[ptls_field]], align 8, !tbaa !0 ; CHECK-NEXT: [[ppjl_ptls:%.*]] = bitcast {}* [[ptls_load]] to {}** ; CHECK-NEXT: [[ptls_i8:%.*]] = bitcast {}** [[ppjl_ptls]] to i8* From 1eee6ef7c830751255ca99d2fe66fe2897a60bcf Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Fri, 31 Mar 2023 23:25:19 -0400 Subject: [PATCH 421/775] Make :open ccall use variadic cconv (#49212) --- base/file.jl | 2 +- test/file.jl | 2 +- test/testhelpers/FakePTYs.jl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/base/file.jl b/base/file.jl index ba50a9deb3335..866e82b6e39c2 100644 --- a/base/file.jl +++ b/base/file.jl @@ -105,7 +105,7 @@ if Sys.iswindows() end else function cd(f::Function, dir::AbstractString) - fd = ccall(:open, Int32, (Cstring, Int32), :., 0) + fd = ccall(:open, Int32, (Cstring, Int32, UInt32...), :., 0) systemerror(:open, fd == -1) try cd(dir) diff --git a/test/file.jl b/test/file.jl index 7ca49fe3a065b..8544ae980af6b 100644 --- a/test/file.jl +++ b/test/file.jl @@ -1253,7 +1253,7 @@ let f = open(file, "w") if Sys.iswindows() f = RawFD(ccall(:_open, Cint, (Cstring, Cint), file, Base.Filesystem.JL_O_RDONLY)) else - f = RawFD(ccall(:open, Cint, (Cstring, Cint), file, Base.Filesystem.JL_O_RDONLY)) + f = RawFD(ccall(:open, Cint, (Cstring, Cint, UInt32...), file, Base.Filesystem.JL_O_RDONLY)) end test_LibcFILE(Libc.FILE(f, Libc.modestr(true, false))) end diff --git a/test/testhelpers/FakePTYs.jl b/test/testhelpers/FakePTYs.jl index 17dd270cd2424..c592699440ee0 100644 --- a/test/testhelpers/FakePTYs.jl +++ b/test/testhelpers/FakePTYs.jl @@ -39,7 +39,7 @@ function open_fake_pty() rc = ccall(:unlockpt, Cint, (Cint,), fdm) rc != 0 && error("unlockpt") - fds = ccall(:open, Cint, (Ptr{UInt8}, Cint), + fds = ccall(:open, Cint, (Ptr{UInt8}, Cint, UInt32...), ccall(:ptsname, Ptr{UInt8}, (Cint,), fdm), O_RDWR | O_NOCTTY) pts = RawFD(fds) From e022ab22b9c8be582da37b1441ce91aa6dc172cf Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki Date: Thu, 2 Mar 2023 21:10:52 +0900 Subject: [PATCH 422/775] effects: avoid `UndefRefError` error check when analyzing `arrayset` This helps us prove `:nothrow`-ness of `arrayset` when bounds checking is turned off manually. --- base/compiler/tfuncs.jl | 14 ++++++++------ test/compiler/effects.jl | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index b23d1b17efd42..27caf3f0bea1d 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1999,14 +1999,16 @@ function array_type_undefable(@nospecialize(arytype)) end end -function array_builtin_common_nothrow(argtypes::Vector{Any}, first_idx_idx::Int) +function array_builtin_common_nothrow(argtypes::Vector{Any}, first_idx_idx::Int, isarrayref::Bool) length(argtypes) >= 4 || return false boundscheck = argtypes[1] arytype = argtypes[2] array_builtin_common_typecheck(boundscheck, arytype, argtypes, first_idx_idx) || return false - # If we could potentially throw undef ref errors, bail out now. - arytype = widenconst(arytype) - array_type_undefable(arytype) && return false + if isarrayref + # If we could potentially throw undef ref errors, bail out now. + arytype = widenconst(arytype) + array_type_undefable(arytype) && return false + end # If we have @inbounds (first argument is false), we're allowed to assume # we don't throw bounds errors. if isa(boundscheck, Const) @@ -2042,11 +2044,11 @@ end @nospecs function _builtin_nothrow(𝕃::AbstractLattice, f, argtypes::Vector{Any}, rt) ⊑ = Core.Compiler.:⊑(𝕃) if f === arrayset - array_builtin_common_nothrow(argtypes, 4) || return false + array_builtin_common_nothrow(argtypes, 4, #=isarrayref=#false) || return false # Additionally check element type compatibility return arrayset_typecheck(argtypes[2], argtypes[3]) elseif f === arrayref || f === const_arrayref - return array_builtin_common_nothrow(argtypes, 3) + return array_builtin_common_nothrow(argtypes, 3, #=isarrayref=#true) elseif f === Core._expr length(argtypes) >= 1 || return false return argtypes[1] ⊑ Symbol diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index e4dcf6e9537d9..25dc7a77fd762 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -455,9 +455,19 @@ let effects = Base.infer_effects(f_setfield_nothrow, ()) end # nothrow for arrayset -@test Base.infer_effects((Vector{Int},Int)) do a, i - a[i] = 0 # may throw +@test Base.infer_effects((Vector{Int},Int,Int)) do a, v, i + Base.arrayset(true, a, v, i) end |> !Core.Compiler.is_nothrow +@test Base.infer_effects((Vector{Int},Int,Int)) do a, v, i + a[i] = v # may throw +end |> !Core.Compiler.is_nothrow +# when bounds checking is turned off, it should be safe +@test Base.infer_effects((Vector{Int},Int,Int)) do a, v, i + Base.arrayset(false, a, v, i) +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Vector{Number},Number,Int)) do a, v, i + Base.arrayset(false, a, v, i) +end |> Core.Compiler.is_nothrow # even if 2-arg `getfield` may throw, it should be still `:consistent` @test Core.Compiler.is_consistent(Base.infer_effects(getfield, (NTuple{5, Float64}, Int))) From 8b86d910947c43592077dac4e000b87d4ebe53c0 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki Date: Thu, 2 Mar 2023 21:16:14 +0900 Subject: [PATCH 423/775] effects: analyze bounds checking of `getfield` properly This allows us to prove `:nothrow`-ness of `getfield` when bounds checking is turned off manually. It still taints `:nothrow` when a name of invalid type is given. --- base/compiler/tfuncs.jl | 45 +++++++++++++++++++++------------------- test/compiler/effects.jl | 19 ++++++++++++++--- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 27caf3f0bea1d..209a10c9954ad 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -937,7 +937,7 @@ function getfield_boundscheck((; fargs, argtypes)::ArgInfo) # Symbol return :unknown end -function getfield_nothrow(arginfo::ArgInfo, boundscheck::Symbol=getfield_boundscheck(arginfo)) +function getfield_nothrow(𝕃::AbstractLattice, arginfo::ArgInfo, boundscheck::Symbol=getfield_boundscheck(arginfo)) (;argtypes) = arginfo boundscheck === :unknown && return false ordering = Const(:not_atomic) @@ -958,17 +958,19 @@ function getfield_nothrow(arginfo::ArgInfo, boundscheck::Symbol=getfield_boundsc if ordering !== :not_atomic # TODO: this is assuming not atomic return false end - return getfield_nothrow(argtypes[2], argtypes[3], !(boundscheck === :off)) + return getfield_nothrow(𝕃, argtypes[2], argtypes[3], !(boundscheck === :off)) else return false end end -@nospecs function getfield_nothrow(s00, name, boundscheck::Bool) +@nospecs function getfield_nothrow(𝕃::AbstractLattice, s00, name, boundscheck::Bool) # If we don't have boundscheck off and don't know the field, don't even bother if boundscheck isa(name, Const) || return false end + ⊑ = Core.Compiler.:⊑(𝕃) + # If we have s00 being a const, we can potentially refine our type-based analysis above if isa(s00, Const) || isconstType(s00) if !isa(s00, Const) @@ -984,31 +986,32 @@ end end return isdefined(sv, nval) end - if !boundscheck && !isa(sv, Module) - # If bounds checking is disabled and all fields are assigned, - # we may assume that we don't throw - for i = 1:fieldcount(typeof(sv)) - isdefined(sv, i) || return false - end - return true + boundscheck && return false + # If bounds checking is disabled and all fields are assigned, + # we may assume that we don't throw + isa(sv, Module) && return false + name ⊑ Int || name ⊑ Symbol || return false + for i = 1:fieldcount(typeof(sv)) + isdefined(sv, i) || return false end - return false + return true end s0 = widenconst(s00) s = unwrap_unionall(s0) if isa(s, Union) - return getfield_nothrow(rewrap_unionall(s.a, s00), name, boundscheck) && - getfield_nothrow(rewrap_unionall(s.b, s00), name, boundscheck) + return getfield_nothrow(𝕃, rewrap_unionall(s.a, s00), name, boundscheck) && + getfield_nothrow(𝕃, rewrap_unionall(s.b, s00), name, boundscheck) elseif isType(s) && isTypeDataType(s.parameters[1]) s = s0 = DataType end if isa(s, DataType) # Can't say anything about abstract types isabstracttype(s) && return false - # If all fields are always initialized, and bounds check is disabled, we can assume - # we don't throw + # If all fields are always initialized, and bounds check is disabled, + # we can assume we don't throw if !boundscheck && s.name.n_uninitialized == 0 + name ⊑ Int || name ⊑ Symbol || return false return true end # Else we need to know what the field is @@ -2012,7 +2015,7 @@ function array_builtin_common_nothrow(argtypes::Vector{Any}, first_idx_idx::Int, # If we have @inbounds (first argument is false), we're allowed to assume # we don't throw bounds errors. if isa(boundscheck, Const) - !(boundscheck.val::Bool) && return true + boundscheck.val::Bool || return true end # Else we can't really say anything here # TODO: In the future we may be able to track the shapes of arrays though @@ -2067,7 +2070,7 @@ end elseif f === invoke return false elseif f === getfield - return getfield_nothrow(ArgInfo(nothing, Any[Const(f), argtypes...])) + return getfield_nothrow(𝕃, ArgInfo(nothing, Any[Const(f), argtypes...])) elseif f === setfield! if na == 3 return setfield!_nothrow(𝕃, argtypes[1], argtypes[2], argtypes[3]) @@ -2224,7 +2227,7 @@ function isdefined_effects(𝕃::AbstractLattice, argtypes::Vector{Any}) return Effects(EFFECTS_TOTAL; consistent, nothrow) end -function getfield_effects(arginfo::ArgInfo, @nospecialize(rt)) +function getfield_effects(𝕃::AbstractLattice, arginfo::ArgInfo, @nospecialize(rt)) (;argtypes) = arginfo # consistent if the argtype is immutable length(argtypes) < 3 && return EFFECTS_THROWS @@ -2240,9 +2243,9 @@ function getfield_effects(arginfo::ArgInfo, @nospecialize(rt)) if !(length(argtypes) ≥ 3 && getfield_notundefined(obj, argtypes[3])) consistent = ALWAYS_FALSE end - nothrow = getfield_nothrow(arginfo, :on) + bcheck = getfield_boundscheck(arginfo) + nothrow = getfield_nothrow(𝕃, arginfo, bcheck) if !nothrow - bcheck = getfield_boundscheck(arginfo) if !(bcheck === :on || bcheck === :boundscheck) # If we cannot independently prove inboundsness, taint consistency. # The inbounds-ness assertion requires dynamic reachability, while @@ -2293,7 +2296,7 @@ function builtin_effects(𝕃::AbstractLattice, @nospecialize(f::Builtin), argin @assert !contains_is(_SPECIAL_BUILTINS, f) if f === getfield - return getfield_effects(arginfo, rt) + return getfield_effects(𝕃, arginfo, rt) end argtypes = arginfo.argtypes[2:end] diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index 25dc7a77fd762..a6576664f5edf 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -487,14 +487,27 @@ end |> Core.Compiler.is_consistent end |> Core.Compiler.is_effect_free # `getfield_effects` handles access to union object nicely -@test Core.Compiler.is_consistent(Core.Compiler.getfield_effects(Core.Compiler.ArgInfo(nothing, Any[Core.Const(getfield), Some{String}, Core.Const(:value)]), String)) -@test Core.Compiler.is_consistent(Core.Compiler.getfield_effects(Core.Compiler.ArgInfo(nothing, Any[Core.Const(getfield), Some{Symbol}, Core.Const(:value)]), Symbol)) -@test Core.Compiler.is_consistent(Core.Compiler.getfield_effects(Core.Compiler.ArgInfo(nothing, Any[Core.Const(getfield), Union{Some{Symbol},Some{String}}, Core.Const(:value)]), Union{Symbol,String})) +let 𝕃 = Core.Compiler.fallback_lattice + @test Core.Compiler.is_consistent(Core.Compiler.getfield_effects(𝕃, Core.Compiler.ArgInfo(nothing, Any[Core.Const(getfield), Some{String}, Core.Const(:value)]), String)) + @test Core.Compiler.is_consistent(Core.Compiler.getfield_effects(𝕃, Core.Compiler.ArgInfo(nothing, Any[Core.Const(getfield), Some{Symbol}, Core.Const(:value)]), Symbol)) + @test Core.Compiler.is_consistent(Core.Compiler.getfield_effects(𝕃, Core.Compiler.ArgInfo(nothing, Any[Core.Const(getfield), Union{Some{Symbol},Some{String}}, Core.Const(:value)]), Union{Symbol,String})) +end @test Base.infer_effects((Bool,)) do c obj = c ? Some{String}("foo") : Some{Symbol}(:bar) return getfield(obj, :value) end |> Core.Compiler.is_consistent +# getfield is nothrow when bounds checking is turned off +@test Base.infer_effects((Tuple{Int,Int},Int)) do t, i + getfield(t, i, false) +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Tuple{Int,Int},Symbol)) do t, i + getfield(t, i, false) +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Tuple{Int,Int},String)) do t, i + getfield(t, i, false) # invalid name type +end |> !Core.Compiler.is_nothrow + @test Core.Compiler.is_consistent(Base.infer_effects(setindex!, (Base.RefValue{Int}, Int))) # :inaccessiblememonly effect From 121dca64c1df5278050eff1b2dbd757762e8ffc4 Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Sat, 1 Apr 2023 16:21:45 -0400 Subject: [PATCH 424/775] NEWS: add news for task-local RNG split change (#49217) --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 028c388d754aa..33fd3549284d5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,6 +7,7 @@ New language features Language changes ---------------- +* When a task forks a child, the parent task's task-local RNG (random number generator) is no longer affected. The seeding of child based on the parent task also takes a more disciplined approach to collision resistance, using a design based on the SplitMix and DotMix splittable RNG schemes ([#49110]). Compiler/Runtime improvements ----------------------------- From b8057f3419ae50cac41c1b8fb8bcce89ca4d6794 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Sat, 1 Apr 2023 16:39:10 -0400 Subject: [PATCH 425/775] Don't hardcode LLVM version number (#49051) --- base/Makefile | 4 ++++ base/binaryplatforms.jl | 2 +- stdlib/libLLVM_jll/src/libLLVM_jll.jl | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/base/Makefile b/base/Makefile index 0ea0359c8cc8e..493302af78b02 100644 --- a/base/Makefile +++ b/base/Makefile @@ -3,6 +3,9 @@ BUILDDIR := . JULIAHOME := $(abspath $(SRCDIR)/..) include $(JULIAHOME)/Make.inc +# import LLVM_SHARED_LIB_NAME +include $(JULIAHOME)/deps/llvm-ver.make + TAGGED_RELEASE_BANNER := "" all: $(addprefix $(BUILDDIR)/,pcre_h.jl errno_h.jl build_h.jl.phony features_h.jl file_constants.jl uv_constants.jl version_git.jl.phony) @@ -57,6 +60,7 @@ else @echo "const USE_GPL_LIBS = false" >> $@ endif @echo "const libllvm_version_string = \"$$($(LLVM_CONFIG_HOST) --version)\"" >> $@ + @echo "const libllvm_name = \"$(LLVM_SHARED_LIB_NAME)\"" >> $@ @echo "const VERSION_STRING = \"$(JULIA_VERSION)\"" >> $@ @echo "const TAGGED_RELEASE_BANNER = \"$(TAGGED_RELEASE_BANNER)\"" >> $@ ifeq ($(OS),WINNT) diff --git a/base/binaryplatforms.jl b/base/binaryplatforms.jl index fb9feba41c636..39348891c83a6 100644 --- a/base/binaryplatforms.jl +++ b/base/binaryplatforms.jl @@ -904,7 +904,7 @@ function detect_cxxstring_abi() end function open_libllvm(f::Function) - for lib_name in ("libLLVM-14jl", "libLLVM", "LLVM", "libLLVMSupport") + for lib_name in (Base.libllvm_name, "libLLVM", "LLVM", "libLLVMSupport") hdl = Libdl.dlopen_e(lib_name) if hdl != C_NULL try diff --git a/stdlib/libLLVM_jll/src/libLLVM_jll.jl b/stdlib/libLLVM_jll/src/libLLVM_jll.jl index 331600eab6523..bd92890acb7c3 100644 --- a/stdlib/libLLVM_jll/src/libLLVM_jll.jl +++ b/stdlib/libLLVM_jll/src/libLLVM_jll.jl @@ -19,11 +19,11 @@ libLLVM_handle = C_NULL libLLVM_path = "" if Sys.iswindows() - const libLLVM = "libLLVM-14jl.dll" + const libLLVM = "$(Base.libllvm_name).dll" elseif Sys.isapple() const libLLVM = "@rpath/libLLVM.dylib" else - const libLLVM = "libLLVM-14jl.so" + const libLLVM = "$(Base.libllvm_name).so" end function __init__() From a20a3d09ec47a4a39bbafd6241a66dc75c21d41a Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sun, 2 Apr 2023 14:27:09 +0900 Subject: [PATCH 426/775] effects: power-up effects analysis for array operations (#47154) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mostly by making use of newly added `:inaccessiblememonly` effect property. Now we can fold simple vector operations like: ```julia julia> function simple_vec_ops(T, op!, op, xs...) a = T[] op!(a, xs...) return op(a) end; simple_vec_ops (generic function with 1 method) julia> for T = Any[Int,Any], op! = Any[push!,pushfirst!], op = Any[length,size], xs = Any[(Int,), (Int,Int,)] let effects = Base.infer_effects(simple_vec_ops, (Type{T},typeof(op!),typeof(op),xs...)) @test Core.Compiler.is_foldable(effects) end end julia> code_typed() do simple_vec_ops(Any, push!, length, Any,nothing,Core.Const(1)) end 1-element Vector{Any}: CodeInfo( 1 ─ return 3 ) => Int64 ``` --- base/array.jl | 113 ++++++++++++++----- base/compiler/tfuncs.jl | 86 +++++++++++--- base/essentials.jl | 33 ++++++ base/tuple.jl | 2 + test/compiler/EscapeAnalysis/local.jl | 8 +- test/compiler/effects.jl | 154 ++++++++++++++++++++++---- test/precompile.jl | 9 +- 7 files changed, 331 insertions(+), 74 deletions(-) diff --git a/base/array.jl b/base/array.jl index 84399b9a43480..1cfa55b52c999 100644 --- a/base/array.jl +++ b/base/array.jl @@ -122,8 +122,50 @@ const DenseVecOrMat{T} = Union{DenseVector{T}, DenseMatrix{T}} using Core: arraysize, arrayset, const_arrayref +""" + @_safeindex + +This internal macro converts: +- `getindex(xs::Tuple, )` -> `__inbounds_getindex(args...)` +- `setindex!(xs::Vector, args...)` -> `__inbounds_setindex!(xs, args...)` +to tell the compiler that indexing operations within the applied expression are always +inbounds and do not need to taint `:consistent` and `:nothrow`. +""" +macro _safeindex(ex) + return esc(_safeindex(__module__, ex)) +end +function _safeindex(__module__, ex) + isa(ex, Expr) || return ex + if ex.head === :(=) + lhs = arrayref(true, ex.args, 1) + if isa(lhs, Expr) && lhs.head === :ref # xs[i] = x + rhs = arrayref(true, ex.args, 2) + xs = arrayref(true, lhs.args, 1) + args = Vector{Any}(undef, length(lhs.args)-1) + for i = 2:length(lhs.args) + arrayset(true, args, _safeindex(__module__, arrayref(true, lhs.args, i)), i-1) + end + return Expr(:call, GlobalRef(__module__, :__inbounds_setindex!), xs, _safeindex(__module__, rhs), args...) + end + elseif ex.head === :ref # xs[i] + return Expr(:call, GlobalRef(__module__, :__inbounds_getindex), ex.args...) + end + args = Vector{Any}(undef, length(ex.args)) + for i = 1:length(ex.args) + arrayset(true, args, _safeindex(__module__, arrayref(true, ex.args, i)), i) + end + return Expr(ex.head, args...) +end + vect() = Vector{Any}() -vect(X::T...) where {T} = T[ X[i] for i = 1:length(X) ] +function vect(X::T...) where T + @_terminates_locally_meta + vec = Vector{T}(undef, length(X)) + @_safeindex for i = 1:length(X) + vec[i] = X[i] + end + return vec +end """ vect(X...) @@ -321,7 +363,7 @@ end function _copyto_impl!(dest::Array, doffs::Integer, src::Array, soffs::Integer, n::Integer) n == 0 && return dest - n > 0 || _throw_argerror() + n > 0 || _throw_argerror("Number of elements to copy must be nonnegative.") @boundscheck checkbounds(dest, doffs:doffs+n-1) @boundscheck checkbounds(src, soffs:soffs+n-1) unsafe_copyto!(dest, doffs, src, soffs, n) @@ -331,10 +373,7 @@ end # Outlining this because otherwise a catastrophic inference slowdown # occurs, see discussion in #27874. # It is also mitigated by using a constant string. -function _throw_argerror() - @noinline - throw(ArgumentError("Number of elements to copy must be nonnegative.")) -end +_throw_argerror(s) = (@noinline; throw(ArgumentError(s))) copyto!(dest::Array, src::Array) = copyto!(dest, 1, src, 1, length(src)) @@ -397,9 +436,11 @@ julia> getindex(Int8, 1, 2, 3) ``` """ function getindex(::Type{T}, vals...) where T + @inline + @_effect_free_terminates_locally_meta a = Vector{T}(undef, length(vals)) if vals isa NTuple - @inbounds for i in 1:length(vals) + @_safeindex for i in 1:length(vals) a[i] = vals[i] end else @@ -412,9 +453,21 @@ function getindex(::Type{T}, vals...) where T return a end +# safe version +function getindex(::Type{T}, vals::T...) where T + @inline + @_effect_free_terminates_locally_meta + a = Vector{T}(undef, length(vals)) + @_safeindex for i in 1:length(vals) + a[i] = vals[i] + end + return a +end + function getindex(::Type{Any}, @nospecialize vals...) + @_effect_free_terminates_locally_meta a = Vector{Any}(undef, length(vals)) - @inbounds for i = 1:length(vals) + @_safeindex for i = 1:length(vals) a[i] = vals[i] end return a @@ -966,10 +1019,16 @@ Dict{String, Int64} with 2 entries: """ function setindex! end -@eval setindex!(A::Array{T}, x, i1::Int) where {T} = arrayset($(Expr(:boundscheck)), A, convert(T,x)::T, i1) +@eval setindex!(A::Array{T}, x, i1::Int) where {T} = + arrayset($(Expr(:boundscheck)), A, convert(T,x)::T, i1) @eval setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} = (@inline; arrayset($(Expr(:boundscheck)), A, convert(T,x)::T, i1, i2, I...)) +__inbounds_setindex!(A::Array{T}, x, i1::Int) where {T} = + arrayset(false, A, convert(T,x)::T, i1) +__inbounds_setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} = + (@inline; arrayset(false, A, convert(T,x)::T, i1, i2, I...)) + # This is redundant with the abstract fallbacks but needed and helpful for bootstrap function setindex!(A::Array, X::AbstractArray, I::AbstractVector{Int}) @_propagate_inbounds_meta @@ -1055,26 +1114,27 @@ See also [`pushfirst!`](@ref). """ function push! end -function push!(a::Array{T,1}, item) where T +function push!(a::Vector{T}, item) where T # convert first so we don't grow the array if the assignment won't work itemT = convert(T, item) _growend!(a, 1) - @inbounds a[end] = itemT + @_safeindex a[length(a)] = itemT return a end # specialize and optimize the single argument case function push!(a::Vector{Any}, @nospecialize x) _growend!(a, 1) - arrayset(true, a, x, length(a)) + @_safeindex a[length(a)] = x return a end function push!(a::Vector{Any}, @nospecialize x...) + @_terminates_locally_meta na = length(a) nx = length(x) _growend!(a, nx) - for i = 1:nx - arrayset(true, a, x[i], na+i) + @_safeindex for i = 1:nx + a[na+i] = x[i] end return a end @@ -1129,10 +1189,11 @@ push!(a::AbstractVector, iter...) = append!(a, iter) append!(a::AbstractVector, iter...) = foldl(append!, iter, init=a) function _append!(a, ::Union{HasLength,HasShape}, iter) + @_terminates_locally_meta n = length(a) i = lastindex(a) resize!(a, n+Int(length(iter))::Int) - @inbounds for (i, item) in zip(i+1:lastindex(a), iter) + @_safeindex for (i, item) in zip(i+1:lastindex(a), iter) a[i] = item end a @@ -1194,12 +1255,13 @@ pushfirst!(a::Vector, iter...) = prepend!(a, iter) prepend!(a::AbstractVector, iter...) = foldr((v, a) -> prepend!(a, v), iter, init=a) function _prepend!(a, ::Union{HasLength,HasShape}, iter) + @_terminates_locally_meta require_one_based_indexing(a) n = length(iter) _growbeg!(a, n) i = 0 for item in iter - @inbounds a[i += 1] = item + @_safeindex a[i += 1] = item end a end @@ -1249,7 +1311,7 @@ function resize!(a::Vector, nl::Integer) _growend!(a, nl-l) elseif nl != l if nl < 0 - throw(ArgumentError("new length must be ≥ 0")) + _throw_argerror("new length must be ≥ 0") end _deleteend!(a, l-nl) end @@ -1329,7 +1391,7 @@ julia> pop!(Dict(1=>2)) """ function pop!(a::Vector) if isempty(a) - throw(ArgumentError("array must be non-empty")) + _throw_argerror("array must be non-empty") end item = a[end] _deleteend!(a, 1) @@ -1403,24 +1465,25 @@ julia> pushfirst!([1, 2, 3, 4], 5, 6) 4 ``` """ -function pushfirst!(a::Array{T,1}, item) where T +function pushfirst!(a::Vector{T}, item) where T item = convert(T, item) _growbeg!(a, 1) - a[1] = item + @_safeindex a[1] = item return a end # specialize and optimize the single argument case function pushfirst!(a::Vector{Any}, @nospecialize x) _growbeg!(a, 1) - a[1] = x + @_safeindex a[1] = x return a end function pushfirst!(a::Vector{Any}, @nospecialize x...) + @_terminates_locally_meta na = length(a) nx = length(x) _growbeg!(a, nx) - for i = 1:nx + @_safeindex for i = 1:nx a[i] = x[i] end return a @@ -1460,7 +1523,7 @@ julia> A """ function popfirst!(a::Vector) if isempty(a) - throw(ArgumentError("array must be non-empty")) + _throw_argerror("array must be non-empty") end item = a[1] _deletebeg!(a, 1) @@ -1600,7 +1663,7 @@ function _deleteat!(a::Vector, inds, dltd=Nowhere()) (i,s) = y if !(q <= i <= n) if i < q - throw(ArgumentError("indices must be unique and sorted")) + _throw_argerror("indices must be unique and sorted") else throw(BoundsError()) end @@ -1856,7 +1919,7 @@ for (f,_f) in ((:reverse,:_reverse), (:reverse!,:_reverse!)) $_f(A::AbstractVector, ::Colon) = $f(A, firstindex(A), lastindex(A)) $_f(A::AbstractVector, dim::Tuple{Integer}) = $_f(A, first(dim)) function $_f(A::AbstractVector, dim::Integer) - dim == 1 || throw(ArgumentError("invalid dimension $dim ≠ 1")) + dim == 1 || _throw_argerror(LazyString("invalid dimension ", dim, " ≠ 1")) return $_f(A, :) end end diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 209a10c9954ad..ede0f4545503f 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -2134,14 +2134,12 @@ end end # known to be always effect-free (in particular nothrow) -const _PURE_BUILTINS = Any[tuple, svec, ===, typeof, nfields, applicable] - -# known to be effect-free (but not necessarily nothrow) -const _EFFECT_FREE_BUILTINS = [ - fieldtype, apply_type, isa, UnionAll, - getfield, arrayref, const_arrayref, isdefined, Core.sizeof, - Core.ifelse, Core._typevar, (<:), - typeassert, throw, arraysize, getglobal, compilerbarrier +const _PURE_BUILTINS = Any[ + tuple, + svec, + ===, + typeof, + nfields, ] const _CONSISTENT_BUILTINS = Any[ @@ -2159,14 +2157,34 @@ const _CONSISTENT_BUILTINS = Any[ (<:), typeassert, throw, - setfield! + setfield!, +] + +# known to be effect-free (but not necessarily nothrow) +const _EFFECT_FREE_BUILTINS = [ + fieldtype, + apply_type, + isa, + UnionAll, + getfield, + arrayref, + arraysize, + const_arrayref, + isdefined, + Core.sizeof, + Core.ifelse, + Core._typevar, + (<:), + typeassert, + throw, + getglobal, + compilerbarrier, ] const _INACCESSIBLEMEM_BUILTINS = Any[ (<:), (===), apply_type, - arraysize, Core.ifelse, Core.sizeof, svec, @@ -2185,6 +2203,7 @@ const _INACCESSIBLEMEM_BUILTINS = Any[ const _ARGMEM_BUILTINS = Any[ arrayref, arrayset, + arraysize, modifyfield!, replacefield!, setfield!, @@ -2193,7 +2212,7 @@ const _ARGMEM_BUILTINS = Any[ const _INCONSISTENT_INTRINSICS = Any[ Intrinsics.pointerref, # this one is volatile - Intrinsics.arraylen, # this one is volatile + Intrinsics.sqrt_llvm_fast, # this one may differ at runtime (by a few ulps) Intrinsics.have_fma, # this one depends on the runtime environment Intrinsics.cglobal, # cglobal lookup answer changes at runtime # ... and list fastmath intrinsics: @@ -2207,7 +2226,7 @@ const _INCONSISTENT_INTRINSICS = Any[ Intrinsics.ne_float_fast, Intrinsics.neg_float_fast, Intrinsics.sqrt_llvm_fast, - Intrinsics.sub_float_fast + Intrinsics.sub_float_fast, # TODO needs to revive #31193 to mark this as inconsistent to be accurate # while preserving the currently optimizations for many math operations # Intrinsics.muladd_float, # this is not interprocedurally consistent @@ -2309,8 +2328,15 @@ function builtin_effects(𝕃::AbstractLattice, @nospecialize(f::Builtin), argin effect_free = get_binding_type_effect_free(argtypes[1], argtypes[2]) ? ALWAYS_TRUE : ALWAYS_FALSE return Effects(EFFECTS_TOTAL; effect_free) else - consistent = contains_is(_CONSISTENT_BUILTINS, f) ? ALWAYS_TRUE : - (f === Core._typevar) ? CONSISTENT_IF_NOTRETURNED : ALWAYS_FALSE + if contains_is(_CONSISTENT_BUILTINS, f) + consistent = ALWAYS_TRUE + elseif f === arrayref || f === arrayset || f === arraysize + consistent = CONSISTENT_IF_INACCESSIBLEMEMONLY + elseif f === Core._typevar + consistent = CONSISTENT_IF_NOTRETURNED + else + consistent = ALWAYS_FALSE + end if f === setfield! || f === arrayset effect_free = EFFECT_FREE_IF_INACCESSIBLEMEMONLY elseif contains_is(_EFFECT_FREE_BUILTINS, f) || contains_is(_PURE_BUILTINS, f) @@ -2498,11 +2524,21 @@ function intrinsic_effects(f::IntrinsicFunction, argtypes::Vector{Any}) return Effects() end - consistent = contains_is(_INCONSISTENT_INTRINSICS, f) ? ALWAYS_FALSE : ALWAYS_TRUE + if contains_is(_INCONSISTENT_INTRINSICS, f) + consistent = ALWAYS_FALSE + elseif f === arraylen + consistent = CONSISTENT_IF_INACCESSIBLEMEMONLY + else + consistent = ALWAYS_TRUE + end effect_free = !(f === Intrinsics.pointerset) ? ALWAYS_TRUE : ALWAYS_FALSE nothrow = (isempty(argtypes) || !isvarargtype(argtypes[end])) && intrinsic_nothrow(f, argtypes) - - return Effects(EFFECTS_TOTAL; consistent, effect_free, nothrow) + if f === arraylen + inaccessiblememonly = INACCESSIBLEMEM_OR_ARGMEMONLY + else + inaccessiblememonly = ALWAYS_TRUE + end + return Effects(EFFECTS_TOTAL; consistent, effect_free, nothrow, inaccessiblememonly) end # TODO: this function is a very buggy and poor model of the return_type function @@ -2767,9 +2803,25 @@ function foreigncall_effects(@specialize(abstract_eval), e::Expr) return new_array_effects(abstract_eval, args) end end + if is_array_resize(name) + return array_resize_effects() + end return EFFECTS_UNKNOWN end +function is_array_resize(name::Symbol) + return name === :jl_array_grow_beg || name === :jl_array_grow_end || + name === :jl_array_del_beg || name === :jl_array_del_end || + name === :jl_array_grow_at || name === :jl_array_del_at +end + +function array_resize_effects() + return Effects(EFFECTS_TOTAL; + effect_free = EFFECT_FREE_IF_INACCESSIBLEMEMONLY, + nothrow = false, + inaccessiblememonly = INACCESSIBLEMEM_OR_ARGMEMONLY) +end + function alloc_array_ndims(name::Symbol) if name === :jl_alloc_array_1d return 1 diff --git a/base/essentials.jl b/base/essentials.jl index fc79f88f5c0b8..59e4a3fe1162e 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -220,6 +220,39 @@ macro _foldable_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#true)) end +# can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping) +macro _nothrow_meta() + return _is_internal(__module__) && Expr(:meta, Expr(:purity, + #=:consistent=#false, + #=:effect_free=#false, + #=:nothrow=#true, + #=:terminates_globally=#false, + #=:terminates_locally=#false, + #=:notaskstate=#false, + #=:inaccessiblememonly=#false)) +end +# can be used in place of `@assume_effects :terminates_locally` (supposed to be used for bootstrapping) +macro _terminates_locally_meta() + return _is_internal(__module__) && Expr(:meta, Expr(:purity, + #=:consistent=#false, + #=:effect_free=#false, + #=:nothrow=#false, + #=:terminates_globally=#false, + #=:terminates_locally=#true, + #=:notaskstate=#false, + #=:inaccessiblememonly=#false)) +end +# can be used in place of `@assume_effects :effect_free :terminates_locally` (supposed to be used for bootstrapping) +macro _effect_free_terminates_locally_meta() + return _is_internal(__module__) && Expr(:meta, Expr(:purity, + #=:consistent=#false, + #=:effect_free=#true, + #=:nothrow=#false, + #=:terminates_globally=#false, + #=:terminates_locally=#true, + #=:notaskstate=#false, + #=:inaccessiblememonly=#false)) +end # another version of inlining that propagates an inbounds context macro _propagate_inbounds_meta() diff --git a/base/tuple.jl b/base/tuple.jl index 134010268c7fe..b8ef63517a49f 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -30,6 +30,8 @@ size(@nospecialize(t::Tuple), d::Integer) = (d == 1) ? length(t) : throw(Argumen axes(@nospecialize t::Tuple) = (OneTo(length(t)),) @eval getindex(@nospecialize(t::Tuple), i::Int) = getfield(t, i, $(Expr(:boundscheck))) @eval getindex(@nospecialize(t::Tuple), i::Integer) = getfield(t, convert(Int, i), $(Expr(:boundscheck))) +__inbounds_getindex(@nospecialize(t::Tuple), i::Int) = getfield(t, i, false) +__inbounds_getindex(@nospecialize(t::Tuple), i::Integer) = getfield(t, convert(Int, i), false) getindex(t::Tuple, r::AbstractArray{<:Any,1}) = (eltype(t)[t[ri] for ri in r]...,) getindex(t::Tuple, b::AbstractArray{Bool,1}) = length(b) == length(t) ? getindex(t, findall(b)) : throw(BoundsError(t, b)) getindex(t::Tuple, c::Colon) = t diff --git a/test/compiler/EscapeAnalysis/local.jl b/test/compiler/EscapeAnalysis/local.jl index e5d8f1bf2c940..dd324c3619dc7 100644 --- a/test/compiler/EscapeAnalysis/local.jl +++ b/test/compiler/EscapeAnalysis/local.jl @@ -1997,9 +1997,9 @@ let result = code_escapes((Int,String,)) do n,s i = only(findall(isarrayalloc, result.ir.stmts.inst)) r = only(findall(isreturn, result.ir.stmts.inst)) @test has_return_escape(result.state[SSAValue(i)], r) - Base.JLOptions().check_bounds ≠ 0 && @test has_thrown_escape(result.state[SSAValue(i)]) + @test !has_thrown_escape(result.state[SSAValue(i)]) @test has_return_escape(result.state[Argument(3)], r) # s - Base.JLOptions().check_bounds ≠ 0 && @test has_thrown_escape(result.state[Argument(3)]) # s + @test !has_thrown_escape(result.state[Argument(3)]) # s end let result = code_escapes((Int,String,)) do n,s xs = String[] @@ -2011,9 +2011,9 @@ let result = code_escapes((Int,String,)) do n,s i = only(findall(isarrayalloc, result.ir.stmts.inst)) r = only(findall(isreturn, result.ir.stmts.inst)) @test has_return_escape(result.state[SSAValue(i)], r) # xs - @test has_thrown_escape(result.state[SSAValue(i)]) # xs + @test !has_thrown_escape(result.state[SSAValue(i)]) # xs @test has_return_escape(result.state[Argument(3)], r) # s - @test has_thrown_escape(result.state[Argument(3)]) # s + @test !has_thrown_escape(result.state[Argument(3)]) # s end let result = code_escapes((String,String,String)) do s, t, u xs = String[] diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index a6576664f5edf..eb3df4ba9272e 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -12,9 +12,6 @@ end nothing end -# Test that arraysize has proper effect modeling -@test fully_eliminated(M->(size(M, 2); nothing), (Matrix{Float64},)) - # Test that effect modeling for return_type doesn't incorrectly pick # up the effects of the function being analyzed f_throws() = error() @@ -454,21 +451,6 @@ let effects = Base.infer_effects(f_setfield_nothrow, ()) @test Core.Compiler.is_nothrow(effects) end -# nothrow for arrayset -@test Base.infer_effects((Vector{Int},Int,Int)) do a, v, i - Base.arrayset(true, a, v, i) -end |> !Core.Compiler.is_nothrow -@test Base.infer_effects((Vector{Int},Int,Int)) do a, v, i - a[i] = v # may throw -end |> !Core.Compiler.is_nothrow -# when bounds checking is turned off, it should be safe -@test Base.infer_effects((Vector{Int},Int,Int)) do a, v, i - Base.arrayset(false, a, v, i) -end |> Core.Compiler.is_nothrow -@test Base.infer_effects((Vector{Number},Number,Int)) do a, v, i - Base.arrayset(false, a, v, i) -end |> Core.Compiler.is_nothrow - # even if 2-arg `getfield` may throw, it should be still `:consistent` @test Core.Compiler.is_consistent(Base.infer_effects(getfield, (NTuple{5, Float64}, Int))) @@ -700,12 +682,14 @@ end end @test !Core.Compiler.is_removable_if_unused(Base.infer_effects(unremovable_if_unused3!)) -@testset "effects analysis on array ops" begin +# array ops +# ========= -@testset "effects analysis on array construction" begin +# allocation +# ---------- +# low-level constructor @noinline construct_array(@nospecialize(T), args...) = Array{T}(undef, args...) - # should eliminate safe but dead allocations let good_dims = @static Int === Int64 ? (1:10) : (1:8) Ns = @static Int === Int64 ? (1:10) : (1:8) @@ -720,7 +704,6 @@ let good_dims = @static Int === Int64 ? (1:10) : (1:8) end end end - # should analyze throwness correctly let bad_dims = [-1, typemax(Int)] for dim in bad_dims, N in 1:10 @@ -736,9 +719,132 @@ let bad_dims = [-1, typemax(Int)] end end -end # @testset "effects analysis on array construction" begin +# high-level interfaces +# getindex +for safesig = Any[ + (Type{Int},) + (Type{Int}, Int) + (Type{Int}, Int, Int) + (Type{Number},) + (Type{Number}, Number) + (Type{Number}, Int) + (Type{Any},) + (Type{Any}, Any,) + (Type{Any}, Any, Any) + ] + let effects = Base.infer_effects(getindex, safesig) + @test Core.Compiler.is_consistent_if_notreturned(effects) + @test Core.Compiler.is_removable_if_unused(effects) + end +end +for unsafesig = Any[ + (Type{Int}, String) + (Type{Int}, Any) + (Type{Number}, AbstractString) + (Type{Number}, Any) + ] + let effects = Base.infer_effects(getindex, unsafesig) + @test !Core.Compiler.is_nothrow(effects) + end +end +# vect +for safesig = Any[ + () + (Int,) + (Int, Int) + ] + let effects = Base.infer_effects(Base.vect, safesig) + @test Core.Compiler.is_consistent_if_notreturned(effects) + @test Core.Compiler.is_removable_if_unused(effects) + end +end + +# arrayref +# -------- + +let effects = Base.infer_effects(Base.arrayref, (Vector{Any},Int)) + @test Core.Compiler.is_consistent_if_inaccessiblememonly(effects) + @test Core.Compiler.is_effect_free(effects) + @test !Core.Compiler.is_nothrow(effects) + @test Core.Compiler.is_terminates(effects) +end + +# arrayset +# -------- + +let effects = Base.infer_effects(Base.arrayset, (Vector{Any},Any,Int)) + @test Core.Compiler.is_consistent_if_inaccessiblememonly(effects) + @test Core.Compiler.is_effect_free_if_inaccessiblememonly(effects) + @test !Core.Compiler.is_nothrow(effects) + @test Core.Compiler.is_terminates(effects) +end +# nothrow for arrayset +@test Base.infer_effects((Vector{Int},Int,Int)) do a, v, i + Base.arrayset(true, a, v, i) +end |> !Core.Compiler.is_nothrow +@test Base.infer_effects((Vector{Int},Int,Int)) do a, v, i + a[i] = v # may throw +end |> !Core.Compiler.is_nothrow +# when bounds checking is turned off, it should be safe +@test Base.infer_effects((Vector{Int},Int,Int)) do a, v, i + Base.arrayset(false, a, v, i) +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Vector{Number},Number,Int)) do a, v, i + Base.arrayset(false, a, v, i) +end |> Core.Compiler.is_nothrow + +# arraysize +# --------- + +let effects = Base.infer_effects(Base.arraysize, (Array,Int)) + @test Core.Compiler.is_consistent_if_inaccessiblememonly(effects) + @test Core.Compiler.is_effect_free(effects) + @test !Core.Compiler.is_nothrow(effects) + @test Core.Compiler.is_terminates(effects) +end +# Test that arraysize has proper effect modeling +@test fully_eliminated(M->(size(M, 2); nothing), (Matrix{Float64},)) + +# arraylen +# -------- + +let effects = Base.infer_effects(Base.arraylen, (Vector{Any},)) + @test Core.Compiler.is_consistent_if_inaccessiblememonly(effects) + @test Core.Compiler.is_effect_free(effects) + @test Core.Compiler.is_nothrow(effects) + @test Core.Compiler.is_terminates(effects) +end + +# resize +# ------ + +for op = Any[ + Base._growbeg!, + Base._growend!, + Base._deletebeg!, + Base._deleteend!, + ] + let effects = Base.infer_effects(op, (Vector, Int)) + @test Core.Compiler.is_effect_free_if_inaccessiblememonly(effects) + @test Core.Compiler.is_terminates(effects) + @test !Core.Compiler.is_nothrow(effects) + end +end + +# end to end +# ---------- -end # @testset "effects analysis on array ops" begin +function simple_vec_ops(T, op!, op, xs...) + a = T[] + op!(a, xs...) + return op(a) +end +for T = Any[Int,Any], op! = Any[push!,pushfirst!], op = Any[length,size], + xs = Any[(Int,), (Int,Int,)] + let effects = Base.infer_effects(simple_vec_ops, (Type{T},typeof(op!),typeof(op),xs...)) + @test Core.Compiler.is_foldable(effects) + end +end # Test that builtin_effects handles vararg correctly @test !Core.Compiler.is_nothrow(Core.Compiler.builtin_effects(Core.Compiler.fallback_lattice, Core.isdefined, diff --git a/test/precompile.jl b/test/precompile.jl index de15171c8138c..50dfe329d3db4 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -662,12 +662,13 @@ precompile_test_harness("code caching") do dir @test all(i -> root_provenance(m, i) == Mid, 1:length(m.roots)) end # Check that we can cache external CodeInstances: - # size(::Vector) has an inferred specialization for Vector{X} - msize = which(size, (Vector{<:Any},)) + # length(::Vector) has an inferred specialization for `Vector{X}` + msize = which(length, (Vector{<:Any},)) hasspec = false for mi in Base.specializations(msize) - if mi.specTypes == Tuple{typeof(size),Vector{Cacheb8321416e8a3e2f1.X}} - if isdefined(mi, :cache) && isa(mi.cache, Core.CodeInstance) && mi.cache.max_world == typemax(UInt) && mi.cache.inferred !== nothing + if mi.specTypes == Tuple{typeof(length),Vector{Cacheb8321416e8a3e2f1.X}} + if (isdefined(mi, :cache) && isa(mi.cache, Core.CodeInstance) && + mi.cache.max_world == typemax(UInt) && mi.cache.inferred !== nothing) hasspec = true break end From a61222069f3e5e8211d0d3439fd6fb6543139af8 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sun, 2 Apr 2023 15:28:53 +0900 Subject: [PATCH 427/775] effects: add docstrings to the type-based-effect-analysis queries (#49222) --- base/compiler/typeinfer.jl | 2 +- base/compiler/typeutils.jl | 40 +++++++++++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 38859f62cbbeb..03b074bbec318 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -447,7 +447,7 @@ function adjust_effects(sv::InferenceState) end ipo_effects = Effects(ipo_effects; inaccessiblememonly=ALWAYS_TRUE) end - if is_consistent_if_notreturned(ipo_effects) && is_consistent_argtype(rt) + if is_consistent_if_notreturned(ipo_effects) && is_identity_free_argtype(rt) # in a case when the :consistent-cy here is only tainted by mutable allocations # (indicated by `CONSISTENT_IF_NOTRETURNED`), we may be able to refine it if the return # type guarantees that the allocations are never returned diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index 293ef5797888b..5caf0d5de80fd 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -340,13 +340,29 @@ function unwraptv_lb(@nospecialize t) end const unwraptv = unwraptv_ub -# this query is specially written for `adjust_effects` and returns true if a value of this type -# never involves inconsistency of mutable objects that are allocated somewhere within a call graph -is_consistent_argtype(@nospecialize ty) = - is_consistent_type(widenconst(ignorelimited(ty))) -is_consistent_type(@nospecialize ty) = isidentityfree(ty) +""" + is_identity_free_argtype(argtype) -> Bool + +Return `true` if the `argtype` object is identity free in the sense that this type or any +reachable through its fields has non-content-based identity (see `Base.isidentityfree`). +This query is specifically designed for `adjust_effects`, enabling it to refine the +`:consistent` effect property tainted by mutable allocation(s) within the analyzed call +graph when the return value type is `is_identity_free_argtype`, ensuring that the allocated +mutable objects are never returned. +""" +is_identity_free_argtype(@nospecialize ty) = is_identity_free_type(widenconst(ignorelimited(ty))) +is_identity_free_type(@nospecialize ty) = isidentityfree(ty) + +""" + is_immutable_argtype(argtype) -> Bool -is_immutable_argtype(@nospecialize ty) = is_immutable_type(widenconst(ignorelimited(ty))) +Return `true` if the `argtype` object is known to be immutable. +This query is specifically designed for `getfield_effects` and `isdefined_effects`, allowing +them to prove `:consistent`-cy of `getfield` / `isdefined` calls when applied to immutable +objects. Otherwise, we need to additionally prove that the non-immutable object is not a +global object to prove the `:consistent`-cy. +""" +is_immutable_argtype(@nospecialize argtype) = is_immutable_type(widenconst(ignorelimited(argtype))) is_immutable_type(@nospecialize ty) = _is_immutable_type(unwrap_unionall(ty)) function _is_immutable_type(@nospecialize ty) if isa(ty, Union) @@ -355,6 +371,16 @@ function _is_immutable_type(@nospecialize ty) return !isabstracttype(ty) && !ismutabletype(ty) end -is_mutation_free_argtype(@nospecialize argtype) = +""" + is_mutation_free_argtype(argtype) -> Bool + +Return `true` if `argtype` object is mutation free in the sense that no mutable memory +is reachable from this type (either in the type itself) or through any fields +(see `Base.ismutationfree`). +This query is specifically written for analyzing the `:inaccessiblememonly` effect property +and is supposed to improve the analysis accuracy by not tainting the `:inaccessiblememonly` +property when there is access to mutation-free global object. +""" +is_mutation_free_argtype(@nospecialize(argtype)) = is_mutation_free_type(widenconst(ignorelimited(argtype))) is_mutation_free_type(@nospecialize ty) = ismutationfree(ty) From ff9435c6d2faeb97080930dbf69d3519305f6364 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Sun, 2 Apr 2023 17:03:24 -0700 Subject: [PATCH 428/775] Delete codesign.sh (#48545) --- Makefile | 12 ------------ contrib/codesign.sh | 38 -------------------------------------- contrib/prepare_release.sh | 6 ------ 3 files changed, 56 deletions(-) delete mode 100755 contrib/codesign.sh diff --git a/Makefile b/Makefile index c6c1717732056..60aca9a4e8d22 100644 --- a/Makefile +++ b/Makefile @@ -438,14 +438,6 @@ ifeq ($(JULIA_BUILD_MODE),release) else ifeq ($(JULIA_BUILD_MODE),debug) $(call stringreplace,$(DESTDIR)$(shlibdir)/libjulia-debug.$(JL_MAJOR_MINOR_SHLIB_EXT),$(LOADER_DEBUG_BUILD_DEP_LIBS)$$,$(LOADER_DEBUG_INSTALL_DEP_LIBS)) endif -ifeq ($(OS),Darwin) - # Codesign the libjulia we just modified -ifeq ($(JULIA_BUILD_MODE),release) - $(JULIAHOME)/contrib/codesign.sh "$(MACOS_CODESIGN_IDENTITY)" "$(DESTDIR)$(shlibdir)/libjulia.$(JL_MAJOR_MINOR_SHLIB_EXT)" -else ifeq ($(JULIA_BUILD_MODE),debug) - $(JULIAHOME)/contrib/codesign.sh "$(MACOS_CODESIGN_IDENTITY)" "$(DESTDIR)$(shlibdir)/libjulia-debug.$(JL_MAJOR_MINOR_SHLIB_EXT)" -endif -endif endif ifeq ($(OS),FreeBSD) @@ -501,10 +493,6 @@ ifeq ($(OS), Linux) endif ifeq ($(OS), WINNT) cd $(BUILDROOT)/julia-$(JULIA_COMMIT)/bin && rm -f llvm* llc.exe lli.exe opt.exe LTO.dll bugpoint.exe macho-dump.exe -endif -ifeq ($(OS),Darwin) - # If we're on macOS, and we have a codesigning identity, then codesign the binary-dist tarball! - $(JULIAHOME)/contrib/codesign.sh "$(MACOS_CODESIGN_IDENTITY)" "$(BUILDROOT)/julia-$(JULIA_COMMIT)" endif cd $(BUILDROOT) && $(TAR) zcvf $(JULIA_BINARYDIST_FILENAME).tar.gz julia-$(JULIA_COMMIT) diff --git a/contrib/codesign.sh b/contrib/codesign.sh deleted file mode 100755 index 03866c4bb1ac1..0000000000000 --- a/contrib/codesign.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# Codesign binary files for macOS. - -usage() { - echo "Usage: ${0} MACOS_CODESIGN_IDENTITY FILE-OR-DIRECTORY" - exit 0 -} - -# Default codesign identity to `-` if not provided -if [ -z "${1}" ]; then - MACOS_CODESIGN_IDENTITY="-" - ENTITLEMENTS="" -else - MACOS_CODESIGN_IDENTITY="${1}" - ENTITLEMENTS="--entitlements $(dirname "${0}")/mac/app/Entitlements.plist" -fi - -if [ "${#}" -eq 2 ]; then - if [ -f "${2}" ]; then - # Codesign only the given file - MACHO_FILES="${2}" - elif [ -d "${2}" ]; then - # Find all files in the given directory - MACHO_FILES=$(find "${2}" -type f -perm -0111 | cut -d: -f1) - else - usage - fi -else - usage -fi - -echo "Codesigning with identity ${MACOS_CODESIGN_IDENTITY}" -for f in ${MACHO_FILES}; do - echo "Codesigning ${f}..." - codesign -s "${MACOS_CODESIGN_IDENTITY}" --option=runtime ${ENTITLEMENTS} -vvv --timestamp --deep --force "${f}" -done diff --git a/contrib/prepare_release.sh b/contrib/prepare_release.sh index 7d4e55e3a402e..2772e44a858f1 100755 --- a/contrib/prepare_release.sh +++ b/contrib/prepare_release.sh @@ -56,12 +56,6 @@ curl -L -o julia-$version-win32.exe \ $julianightlies/winnt/x86/$majmin/julia-$majminpatch-$shashort-win32.exe cp julia-$version-win32.exe julia-$majmin-latest-win32.exe -if [ -e codesign.sh ]; then - # code signing needs to run on windows, script is not checked in since it - # hard-codes a few things. TODO: see if signtool.exe can run in wine - ./codesign.sh -fi - shasum -a 256 julia-$version* | grep -v -e sha256 -e md5 -e asc > julia-$version.sha256 md5sum julia-$version* | grep -v -e sha256 -e md5 -e asc > julia-$version.md5 From 46813d3316d9ab9f927d7d12f327114826c1bc43 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Mon, 3 Apr 2023 13:56:52 +0900 Subject: [PATCH 429/775] optimize `Core.Compiler` a bit (#49227) --- base/compiler/abstractinterpretation.jl | 18 ++++++++++++------ base/compiler/ssair/ir.jl | 6 +++--- base/compiler/stmtinfo.jl | 10 +++++----- base/compiler/tfuncs.jl | 8 ++++---- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 4980f051d1459..4d10507b834f3 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2023,7 +2023,7 @@ end function abstract_call_opaque_closure(interp::AbstractInterpreter, closure::PartialOpaque, arginfo::ArgInfo, si::StmtInfo, sv::InferenceState, check::Bool=true) sig = argtypes_to_type(arginfo.argtypes) - result = abstract_call_method(interp, closure.source, sig, Core.svec(), false, si, sv) + result = abstract_call_method(interp, closure.source::Method, sig, Core.svec(), false, si, sv) (; rt, edge, effects) = result tt = closure.typ sigT = (unwrap_unionall(tt)::DataType).parameters[1] @@ -2276,7 +2276,9 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp ismutable = ismutabletype(ut) fcount = datatype_fieldcount(ut) nargs = length(e.args) - 1 - if fcount === nothing || (fcount > nargs && any(i::Int -> !is_undefref_fieldtype(fieldtype(t, i)), (nargs+1):fcount)) + if (fcount === nothing || (fcount > nargs && (let t = t + any(i::Int -> !is_undefref_fieldtype(fieldtype(t, i)), (nargs+1):fcount) + end))) # allocation with undefined field leads to undefined behavior and should taint `:consistent`-cy consistent = ALWAYS_FALSE elseif ismutable @@ -2335,12 +2337,16 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp if length(e.args) == 2 && isconcretedispatch(t) && !ismutabletype(t) at = abstract_eval_value(interp, e.args[2], vtypes, sv) n = fieldcount(t) - if isa(at, Const) && isa(at.val, Tuple) && n == length(at.val::Tuple) && - let t = t, at = at; all(i::Int->getfield(at.val::Tuple, i) isa fieldtype(t, i), 1:n); end + if (isa(at, Const) && isa(at.val, Tuple) && n == length(at.val::Tuple) && + (let t = t, at = at + all(i::Int->getfield(at.val::Tuple, i) isa fieldtype(t, i), 1:n) + end)) nothrow = isexact t = Const(ccall(:jl_new_structt, Any, (Any, Any), t, at.val)) - elseif isa(at, PartialStruct) && at ⊑ᵢ Tuple && n == length(at.fields::Vector{Any}) && - let t = t, at = at; all(i::Int->(at.fields::Vector{Any})[i] ⊑ᵢ fieldtype(t, i), 1:n); end + elseif (isa(at, PartialStruct) && at ⊑ᵢ Tuple && n == length(at.fields::Vector{Any}) && + (let t = t, at = at, ⊑ᵢ = ⊑ᵢ + all(i::Int->(at.fields::Vector{Any})[i] ⊑ᵢ fieldtype(t, i), 1:n) + end)) nothrow = isexact t = PartialStruct(t, at.fields::Vector{Any}) end diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index c0146c1dd2d23..c5415add51cc5 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -687,7 +687,7 @@ end function getindex(compact::IncrementalCompact, ssa::OldSSAValue) id = ssa.id if id < compact.idx - new_idx = compact.ssa_rename[id] + new_idx = compact.ssa_rename[id]::Int return compact.result[new_idx] elseif id <= length(compact.ir.stmts) return compact.ir.stmts[id] @@ -721,7 +721,7 @@ end function block_for_inst(compact::IncrementalCompact, idx::OldSSAValue) id = idx.id if id < compact.idx # if ssa within result - id = compact.ssa_rename[id] + id = compact.ssa_rename[id]::Int return block_for_inst(compact, SSAValue(id)) else return block_for_inst(compact.ir.cfg, id) @@ -962,7 +962,7 @@ end function setindex!(compact::IncrementalCompact, @nospecialize(v), idx::OldSSAValue) id = idx.id if id < compact.idx - new_idx = compact.ssa_rename[id] + new_idx = compact.ssa_rename[id]::Int (compact.result[new_idx][:inst] === v) && return compact kill_current_uses!(compact, compact.result[new_idx][:inst]) compact.result[new_idx][:inst] = v diff --git a/base/compiler/stmtinfo.jl b/base/compiler/stmtinfo.jl index 23f8c3aba908e..9f55d56181838 100644 --- a/base/compiler/stmtinfo.jl +++ b/base/compiler/stmtinfo.jl @@ -56,11 +56,13 @@ nsplit_impl(info::UnionSplitInfo) = length(info.matches) getsplit_impl(info::UnionSplitInfo, idx::Int) = getsplit_impl(info.matches[idx], 1) getresult_impl(::UnionSplitInfo, ::Int) = nothing -struct ConstPropResult +abstract type ConstResult end + +struct ConstPropResult <: ConstResult result::InferenceResult end -struct ConcreteResult +struct ConcreteResult <: ConstResult mi::MethodInstance effects::Effects result @@ -68,14 +70,12 @@ struct ConcreteResult ConcreteResult(mi::MethodInstance, effects::Effects, @nospecialize val) = new(mi, effects, val) end -struct SemiConcreteResult +struct SemiConcreteResult <: ConstResult mi::MethodInstance ir::IRCode effects::Effects end -const ConstResult = Union{ConstPropResult, ConcreteResult, SemiConcreteResult} - """ info::ConstCallInfo <: CallInfo diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index ede0f4545503f..89ae4a1c26df6 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1793,12 +1793,12 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K, if i == largs && ub === Any push!(tparams, Vararg) elseif isT - push!(tparams, rewrap_unionall(unw.parameters[1], ai)) + push!(tparams, rewrap_unionall((unw::DataType).parameters[1], ai)) else push!(tparams, Any) end elseif isT - push!(tparams, unw.parameters[1]) + push!(tparams, (unw::DataType).parameters[1]) while isa(ai, UnionAll) push!(outervars, ai.var) ai = ai.body @@ -2633,7 +2633,7 @@ function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, rt = Const(true) # has applicable matches for i in 1:napplicable match = applicable[i]::MethodMatch - edge = specialize_method(match) + edge = specialize_method(match)::MethodInstance add_backedge!(sv, edge) end @@ -2681,7 +2681,7 @@ function _hasmethod_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, sv add_mt_backedge!(sv, mt, types) # this should actually be an invoke-type backedge else rt = Const(true) - edge = specialize_method(match) + edge = specialize_method(match)::MethodInstance add_invoke_backedge!(sv, types, edge) end return CallMeta(rt, EFFECTS_TOTAL, NoCallInfo()) From 10e20012f13924da120eb2503e2a1e08f337abe0 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki Date: Tue, 7 Mar 2023 00:02:58 +0900 Subject: [PATCH 430/775] inference: refactoring to allow irinterp to perform `:call` inference This commit implements a significant refactoring of the inference routines, which is necessary to enable `:call` inference in irinterp. While this commit does not yet enable `:call` inference, a subsequent small commit will do so. This is because external `AbstractInterpreter`s first need to adjust their code for this refactoring, and in the event of a regression detected by the recursive `:call` inference, we will be able to simply revert the small commit. Additionally, this commit improves the robustness of irinterp by allowing it to handle invoke calls, which currently result in a crash. TODOs: - [x] implement a simple recursion detection mechanism for `IRInterpretationState` - [x] add proper invalidation support - [x] allow constant inference from semi-concrete interpretation - [x] propagate callinfo and allow double inlining --- base/compiler/abstractinterpretation.jl | 387 ++++++++++--------- base/compiler/compiler.jl | 1 - base/compiler/inferenceresult.jl | 8 +- base/compiler/inferencestate.jl | 472 ++++++++++++++++++------ base/compiler/optimize.jl | 18 +- base/compiler/ssair/irinterp.jl | 316 +++++----------- base/compiler/ssair/passes.jl | 15 - base/compiler/tfuncs.jl | 12 +- base/compiler/typeinfer.jl | 60 +-- base/compiler/types.jl | 24 +- test/compiler/datastructures.jl | 2 +- test/compiler/inference.jl | 61 +++ 12 files changed, 799 insertions(+), 577 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 4d10507b834f3..e110482ff9ac5 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -13,44 +13,26 @@ const _REF_NAME = Ref.body.name # See if the inference result of the current statement's result value might affect # the final answer for the method (aside from optimization potential and exceptions). # To do that, we need to check both for slot assignment and SSA usage. -call_result_unused(frame::InferenceState, currpc::Int) = - isexpr(frame.src.code[currpc], :call) && isempty(frame.ssavalue_uses[currpc]) +call_result_unused(sv::InferenceState, currpc::Int) = + isexpr(sv.src.code[currpc], :call) && isempty(sv.ssavalue_uses[currpc]) call_result_unused(si::StmtInfo) = !si.used -function get_max_methods(mod::Module, interp::AbstractInterpreter) - max_methods = ccall(:jl_get_module_max_methods, Cint, (Any,), mod) % Int - max_methods < 0 ? InferenceParams(interp).max_methods : max_methods +function get_max_methods(sv::AbsIntState, interp::AbstractInterpreter) + max_methods = ccall(:jl_get_module_max_methods, Cint, (Any,), frame_module(sv)) % Int + return max_methods < 0 ? InferenceParams(interp).max_methods : max_methods end -function get_max_methods(@nospecialize(f), mod::Module, interp::AbstractInterpreter) +function get_max_methods(@nospecialize(f), sv::AbsIntState, interp::AbstractInterpreter) if f !== nothing fmm = typeof(f).name.max_methods fmm !== UInt8(0) && return Int(fmm) end - return get_max_methods(mod, interp) -end - -function should_infer_this_call(interp::AbstractInterpreter, sv::InferenceState) - if InferenceParams(interp).unoptimize_throw_blocks - # Disable inference of calls in throw blocks, since we're unlikely to - # need their types. There is one exception however: If up until now, the - # function has not seen any side effects, we would like to make sure there - # aren't any in the throw block either to enable other optimizations. - if is_stmt_throw_block(get_curr_ssaflag(sv)) - should_infer_for_effects(sv) || return false - end - end - return true -end - -function should_infer_for_effects(sv::InferenceState) - effects = sv.ipo_effects - return is_terminates(effects) && is_effect_free(effects) + return get_max_methods(sv, interp) end function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), arginfo::ArgInfo, si::StmtInfo, @nospecialize(atype), - sv::InferenceState, max_methods::Int) + sv::AbsIntState, max_methods::Int) ⊑ₚ = ⊑(ipo_lattice(interp)) if !should_infer_this_call(interp, sv) add_remark!(interp, sv, "Skipped call in throw block") @@ -178,7 +160,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), @assert !(this_conditional isa Conditional || this_rt isa MustAlias) "invalid lattice element returned from inter-procedural context" seen += 1 rettype = tmerge(𝕃ₚ, rettype, this_rt) - if has_conditional(𝕃ₚ) && this_conditional !== Bottom && is_lattice_bool(𝕃ₚ, rettype) && fargs !== nothing + if has_conditional(𝕃ₚ, sv) && this_conditional !== Bottom && is_lattice_bool(𝕃ₚ, rettype) && fargs !== nothing if conditionals === nothing conditionals = Any[Bottom for _ in 1:length(argtypes)], Any[Bottom for _ in 1:length(argtypes)] @@ -214,7 +196,9 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), # Also considering inferring the compilation signature for this method, so # it is available to the compiler in case it ends up needing it. - if infer_compilation_signature(interp) && 1 == seen == napplicable && rettype !== Any && rettype !== Union{} && !is_removable_if_unused(all_effects) + if (isa(sv, InferenceState) && infer_compilation_signature(interp) && + (1 == seen == napplicable) && rettype !== Any && rettype !== Bottom && + !is_removable_if_unused(all_effects)) match = applicable[1]::MethodMatch method = match.method sig = match.spec_types @@ -238,10 +222,16 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), rettype = Any end add_call_backedges!(interp, rettype, all_effects, edges, matches, atype, sv) - if !isempty(sv.pclimitations) # remove self, if present - delete!(sv.pclimitations, sv) - for caller in sv.callers_in_cycle - delete!(sv.pclimitations, caller) + if isa(sv, InferenceState) + # TODO (#48913) implement a proper recursion handling for irinterp: + # This works just because currently the `:terminate` condition guarantees that + # irinterp doesn't fail into unresolved cycles, but it's not a good solution. + # We should revisit this once we have a better story for handling cycles in irinterp. + if !isempty(sv.pclimitations) # remove self, if present + delete!(sv.pclimitations, sv) + for caller in callers_in_cycle(sv) + delete!(sv.pclimitations, caller) + end end end return CallMeta(rettype, all_effects, info) @@ -349,7 +339,7 @@ function find_matching_methods(𝕃::AbstractLattice, end """ - from_interprocedural!(𝕃ₚ::AbstractLattice, rt, sv::InferenceState, arginfo::ArgInfo, maybecondinfo) -> newrt + from_interprocedural!(𝕃ₚ::AbstractLattice, rt, sv::AbsIntState, arginfo::ArgInfo, maybecondinfo) -> newrt Converts inter-procedural return type `rt` into a local lattice element `newrt`, that is appropriate in the context of current local analysis frame `sv`, especially: @@ -368,7 +358,7 @@ In such cases `maybecondinfo` should be either of: When we deal with multiple `MethodMatch`es, it's better to precompute `maybecondinfo` by `tmerge`ing argument signature type of each method call. """ -function from_interprocedural!(𝕃ₚ::AbstractLattice, @nospecialize(rt), sv::InferenceState, arginfo::ArgInfo, @nospecialize(maybecondinfo)) +function from_interprocedural!(𝕃ₚ::AbstractLattice, @nospecialize(rt), sv::AbsIntState, arginfo::ArgInfo, @nospecialize(maybecondinfo)) rt = collect_limitations!(rt, sv) if isa(rt, InterMustAlias) rt = from_intermustalias(rt, arginfo) @@ -407,11 +397,13 @@ function from_intermustalias(rt::InterMustAlias, arginfo::ArgInfo) return widenmustalias(rt) end -function from_interconditional(𝕃ₚ::AbstractLattice, @nospecialize(typ), - sv::InferenceState, (; fargs, argtypes)::ArgInfo, @nospecialize(maybecondinfo)) - 𝕃 = widenlattice(𝕃ₚ) - has_conditional(𝕃ₚ) || return widenconditional(typ) +function from_interconditional(𝕃ₚ::AbstractLattice, + typ, sv::AbsIntState, arginfo::ArgInfo, maybecondinfo) + @nospecialize typ maybecondinfo + has_conditional(𝕃ₚ, sv) || return widenconditional(typ) + (; fargs, argtypes) = arginfo fargs === nothing && return widenconditional(typ) + 𝕃 = widenlattice(𝕃ₚ) slot = 0 alias = nothing thentype = elsetype = Any @@ -505,7 +497,7 @@ end function add_call_backedges!(interp::AbstractInterpreter, @nospecialize(rettype), all_effects::Effects, edges::Vector{MethodInstance}, matches::Union{MethodMatches,UnionSplitMethodMatches}, @nospecialize(atype), - sv::InferenceState) + sv::AbsIntState) # don't bother to add backedges when both type and effects information are already # maximized to the top since a new method couldn't refine or widen them anyway if rettype === Any @@ -515,7 +507,8 @@ function add_call_backedges!(interp::AbstractInterpreter, @nospecialize(rettype) all_effects = Effects(all_effects; nonoverlayed=false) end if (# ignore the `:noinbounds` property if `:consistent`-cy is tainted already - sv.ipo_effects.consistent === ALWAYS_FALSE || all_effects.consistent === ALWAYS_FALSE || + (sv isa InferenceState && sv.ipo_effects.consistent === ALWAYS_FALSE) || + all_effects.consistent === ALWAYS_FALSE || # or this `:noinbounds` doesn't taint it !stmt_taints_inbounds_consistency(sv)) all_effects = Effects(all_effects; noinbounds=false) @@ -541,7 +534,9 @@ const RECURSION_UNUSED_MSG = "Bounded recursion detected with unused result. Ann const RECURSION_MSG = "Bounded recursion detected. Call was widened to force convergence." const RECURSION_MSG_HARDLIMIT = "Bounded recursion detected under hardlimit. Call was widened to force convergence." -function abstract_call_method(interp::AbstractInterpreter, method::Method, @nospecialize(sig), sparams::SimpleVector, hardlimit::Bool, si::StmtInfo, sv::InferenceState) +function abstract_call_method(interp::AbstractInterpreter, + method::Method, @nospecialize(sig), sparams::SimpleVector, + hardlimit::Bool, si::StmtInfo, sv::AbsIntState) if method.name === :depwarn && isdefined(Main, :Base) && method.module === Main.Base add_remark!(interp, sv, "Refusing to infer into `depwarn`") return MethodCallResult(Any, false, false, nothing, Effects()) @@ -554,9 +549,10 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp edgecycle = edgelimited = false topmost = nothing - for infstate in InfStackUnwind(sv) - if method === infstate.linfo.def - if infstate.linfo.specTypes::Type == sig::Type + for sv′ in AbsIntStackUnwind(sv) + infmi = frame_instance(sv′) + if method === infmi.def + if infmi.specTypes::Type == sig::Type # avoid widening when detecting self-recursion # TODO: merge call cycle and return right away if call_result_unused(si) @@ -572,8 +568,8 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp break end topmost === nothing || continue - if edge_matches_sv(interp, infstate, method, sig, sparams, hardlimit, sv) - topmost = infstate + if edge_matches_sv(interp, sv′, method, sig, sparams, hardlimit, sv) + topmost = sv′ edgecycle = true end end @@ -585,11 +581,12 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp msig = unwrap_unionall(method.sig)::DataType spec_len = length(msig.parameters) + 1 ls = length(sigtuple.parameters) + mi = frame_instance(sv) - if method === sv.linfo.def + if method === mi.def # Under direct self-recursion, permit much greater use of reducers. # here we assume that complexity(specTypes) :>= complexity(sig) - comparison = sv.linfo.specTypes + comparison = mi.specTypes l_comparison = length((unwrap_unionall(comparison)::DataType).parameters) spec_len = max(spec_len, l_comparison) else @@ -603,7 +600,7 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp end # see if the type is actually too big (relative to the caller), and limit it if required - newsig = limit_type_size(sig, comparison, hardlimit ? comparison : sv.linfo.specTypes, InferenceParams(interp).tuple_complexity_limit_depth, spec_len) + newsig = limit_type_size(sig, comparison, hardlimit ? comparison : mi.specTypes, InferenceParams(interp).tuple_complexity_limit_depth, spec_len) if newsig !== sig # continue inference, but note that we've limited parameter complexity @@ -618,9 +615,16 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp return MethodCallResult(Any, true, true, nothing, Effects()) end add_remark!(interp, sv, washardlimit ? RECURSION_MSG_HARDLIMIT : RECURSION_MSG) - topmost = topmost::InferenceState - parentframe = topmost.parent - poison_callstack(sv, parentframe === nothing ? topmost : parentframe) + # TODO (#48913) implement a proper recursion handling for irinterp: + # This works just because currently the `:terminate` condition guarantees that + # irinterp doesn't fail into unresolved cycles, but it's not a good solution. + # We should revisit this once we have a better story for handling cycles in irinterp. + if isa(topmost, InferenceState) + parentframe = frame_parent(topmost) + if isa(sv, InferenceState) && isa(parentframe, InferenceState) + poison_callstack!(sv, parentframe === nothing ? topmost : parentframe) + end + end sig = newsig sparams = svec() edgelimited = true @@ -680,7 +684,9 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp return MethodCallResult(rt, edgecycle, edgelimited, edge, effects) end -function edge_matches_sv(interp::AbstractInterpreter, frame::InferenceState, method::Method, @nospecialize(sig), sparams::SimpleVector, hardlimit::Bool, sv::InferenceState) +function edge_matches_sv(interp::AbstractInterpreter, frame::AbsIntState, + method::Method, @nospecialize(sig), sparams::SimpleVector, + hardlimit::Bool, sv::AbsIntState) # The `method_for_inference_heuristics` will expand the given method's generator if # necessary in order to retrieve this field from the generated `CodeInfo`, if it exists. # The other `CodeInfo`s we inspect will already have this field inflated, so we just @@ -688,12 +694,12 @@ function edge_matches_sv(interp::AbstractInterpreter, frame::InferenceState, met world = get_world_counter(interp) callee_method2 = method_for_inference_heuristics(method, sig, sparams, world) # Union{Method, Nothing} - inf_method2 = frame.src.method_for_inference_limit_heuristics # limit only if user token match + inf_method2 = method_for_inference_limit_heuristics(frame) # limit only if user token match inf_method2 isa Method || (inf_method2 = nothing) if callee_method2 !== inf_method2 return false end - if !hardlimit || InferenceParams(sv.interp).ignore_recursion_hardlimit + if !hardlimit || InferenceParams(interp).ignore_recursion_hardlimit # if this is a soft limit, # also inspect the parent of this edge, # to see if they are the same Method as sv @@ -702,11 +708,10 @@ function edge_matches_sv(interp::AbstractInterpreter, frame::InferenceState, met # check in the cycle list first # all items in here are mutual parents of all others - if !any(p::InferenceState->matches_sv(p, sv), frame.callers_in_cycle) - let parent = frame.parent + if !any(p::AbsIntState->matches_sv(p, sv), callers_in_cycle(frame)) + let parent = frame_parent(frame) parent !== nothing || return false - parent = parent::InferenceState - (parent.cached || parent.parent !== nothing) || return false + (is_cached(parent) || frame_parent(parent) !== nothing) || return false matches_sv(parent, sv) || return false end end @@ -714,7 +719,7 @@ function edge_matches_sv(interp::AbstractInterpreter, frame::InferenceState, met # If the method defines a recursion relation, give it a chance # to tell us that this recursion is actually ok. if isdefined(method, :recursion_relation) - if Core._apply_pure(method.recursion_relation, Any[method, callee_method2, sig, frame.linfo.specTypes]) + if Core._apply_pure(method.recursion_relation, Any[method, callee_method2, sig, frame_instance(frame).specTypes]) return false end end @@ -739,35 +744,35 @@ function method_for_inference_heuristics(method::Method, @nospecialize(sig), spa return nothing end -function matches_sv(parent::InferenceState, sv::InferenceState) - sv_method2 = sv.src.method_for_inference_limit_heuristics # limit only if user token match +function matches_sv(parent::AbsIntState, sv::AbsIntState) + sv_method2 = method_for_inference_limit_heuristics(sv) # limit only if user token match sv_method2 isa Method || (sv_method2 = nothing) - parent_method2 = parent.src.method_for_inference_limit_heuristics # limit only if user token match + parent_method2 = method_for_inference_limit_heuristics(parent) # limit only if user token match parent_method2 isa Method || (parent_method2 = nothing) - return parent.linfo.def === sv.linfo.def && sv_method2 === parent_method2 + return frame_instance(parent).def === frame_instance(sv).def && sv_method2 === parent_method2 end -function is_edge_recursed(edge::MethodInstance, sv::InferenceState) - return any(InfStackUnwind(sv)) do infstate - return edge === infstate.linfo +function is_edge_recursed(edge::MethodInstance, caller::AbsIntState) + return any(AbsIntStackUnwind(caller)) do sv::AbsIntState + return edge === frame_instance(sv) end end -function is_method_recursed(method::Method, sv::InferenceState) - return any(InfStackUnwind(sv)) do infstate - return method === infstate.linfo.def +function is_method_recursed(method::Method, caller::AbsIntState) + return any(AbsIntStackUnwind(caller)) do sv::AbsIntState + return method === frame_instance(sv).def end end -function is_constprop_edge_recursed(edge::MethodInstance, sv::InferenceState) - return any(InfStackUnwind(sv)) do infstate - return edge === infstate.linfo && any(infstate.result.overridden_by_const) +function is_constprop_edge_recursed(edge::MethodInstance, caller::AbsIntState) + return any(AbsIntStackUnwind(caller)) do sv::AbsIntState + return edge === frame_instance(sv) && is_constproped(sv) end end -function is_constprop_method_recursed(method::Method, sv::InferenceState) - return any(InfStackUnwind(sv)) do infstate - return method === infstate.linfo.def && any(infstate.result.overridden_by_const) +function is_constprop_method_recursed(method::Method, caller::AbsIntState) + return any(AbsIntStackUnwind(caller)) do sv::AbsIntState + return method === frame_instance(sv).def && is_constproped(sv) end end @@ -792,7 +797,7 @@ end # - false: eligible for semi-concrete evaluation # - nothing: not eligible for either of it function concrete_eval_eligible(interp::AbstractInterpreter, - @nospecialize(f), result::MethodCallResult, arginfo::ArgInfo, sv::InferenceState) + @nospecialize(f), result::MethodCallResult, arginfo::ArgInfo, sv::AbsIntState) # disable all concrete-evaluation if this function call is tainted by some overlayed # method since currently there is no direct way to execute overlayed methods if inbounds_option() === :off @@ -842,7 +847,7 @@ end function concrete_eval_call(interp::AbstractInterpreter, @nospecialize(f), result::MethodCallResult, arginfo::ArgInfo, si::StmtInfo, - sv::InferenceState, invokecall::Union{Nothing,InvokeCall}=nothing) + sv::AbsIntState, invokecall::Union{Nothing,InvokeCall}=nothing) eligible = concrete_eval_eligible(interp, f, result, arginfo, sv) eligible === nothing && return false if eligible @@ -869,7 +874,7 @@ end any_conditional(argtypes::Vector{Any}) = any(@nospecialize(x)->isa(x, Conditional), argtypes) any_conditional(arginfo::ArgInfo) = any_conditional(arginfo.argtypes) -function const_prop_enabled(interp::AbstractInterpreter, sv::InferenceState, match::MethodMatch) +function const_prop_enabled(interp::AbstractInterpreter, sv::AbsIntState, match::MethodMatch) if !InferenceParams(interp).ipo_constant_propagation add_remark!(interp, sv, "[constprop] Disabled by parameter") return false @@ -893,7 +898,7 @@ struct ConstCallResults new(rt, const_result, effects, edge) end -# TODO MustAlias forwarding +# TODO implement MustAlias forwarding struct ConditionalArgtypes <: ForwardableArgtypes arginfo::ArgInfo @@ -958,9 +963,23 @@ function matching_cache_argtypes(𝕃::AbstractLattice, linfo::MethodInstance, a return pick_const_args!(𝕃, cache_argtypes, overridden_by_const, given_argtypes) end +# check if there is a cycle and duplicated inference of `mi` +function is_constprop_recursed(result::MethodCallResult, mi::MethodInstance, sv::AbsIntState) + result.edgecycle || return false + if result.edgelimited + return is_constprop_method_recursed(mi.def::Method, sv) + else + # if the type complexity limiting didn't decide to limit the call signature (as + # indicated by `result.edgelimited === false`), we can relax the cycle detection + # by comparing `MethodInstance`s and allow inference to propagate different + # constant elements if the recursion is finite over the lattice + return is_constprop_edge_recursed(mi, sv) + end +end + function abstract_call_method_with_const_args(interp::AbstractInterpreter, result::MethodCallResult, @nospecialize(f), arginfo::ArgInfo, si::StmtInfo, match::MethodMatch, - sv::InferenceState, invokecall::Union{Nothing,InvokeCall}=nothing) + sv::AbsIntState, invokecall::Union{Nothing,InvokeCall}=nothing) if !const_prop_enabled(interp, sv, match) return nothing end @@ -974,19 +993,28 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, isa(res, ConstCallResults) && return res mi = maybe_get_const_prop_profitable(interp, result, f, arginfo, si, match, sv) mi === nothing && return nothing + if is_constprop_recursed(result, mi, sv) + add_remark!(interp, sv, "[constprop] Edge cycle encountered") + return nothing + end # try semi-concrete evaluation if res::Bool && !any_conditional(arginfo) - mi_cache = WorldView(code_cache(interp), sv.world) + world = frame_world(sv) + mi_cache = WorldView(code_cache(interp), world) code = get(mi_cache, mi, nothing) if code !== nothing - ir = codeinst_to_ir(interp, code) - if isa(ir, IRCode) - irinterp = switch_to_irinterp(interp) - irsv = IRInterpretationState(irinterp, ir, mi, sv.world, arginfo.argtypes) - rt, nothrow = ir_abstract_constant_propagation(irinterp, irsv) - @assert !(rt isa Conditional || rt isa MustAlias) "invalid lattice element returned from IR interpretation" - if !isa(rt, Type) || typeintersect(rt, Bool) === Union{} - new_effects = Effects(result.effects; nothrow=nothrow) + irsv = IRInterpretationState(interp, code, mi, arginfo.argtypes, world) + if irsv !== nothing + irsv.parent = sv + rt, nothrow = ir_abstract_constant_propagation(interp, irsv) + @assert !(rt isa Conditional || rt isa MustAlias) "invalid lattice element returned from irinterp" + if !(isa(rt, Type) && hasintersect(rt, Bool)) + ir = irsv.ir + # TODO (#48913) enable double inlining pass when there are any calls + # that are newly resovled by irinterp + # state = InliningState(interp) + # ir = ssa_inlining_pass!(irsv.ir, state, propagate_inbounds(irsv)) + new_effects = Effects(result.effects; nothrow) return ConstCallResults(rt, SemiConcreteResult(mi, ir, new_effects), new_effects, mi) end end @@ -997,18 +1025,8 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, 𝕃ᵢ = typeinf_lattice(interp) inf_result = cache_lookup(𝕃ᵢ, mi, arginfo.argtypes, inf_cache) if inf_result === nothing - # if there might be a cycle, check to make sure we don't end up - # calling ourselves here. - if result.edgecycle && (result.edgelimited ? - is_constprop_method_recursed(match.method, sv) : - # if the type complexity limiting didn't decide to limit the call signature (`result.edgelimited = false`) - # we can relax the cycle detection by comparing `MethodInstance`s and allow inference to - # propagate different constant elements if the recursion is finite over the lattice - is_constprop_edge_recursed(mi, sv)) - add_remark!(interp, sv, "[constprop] Edge cycle encountered") - return nothing - end - argtypes = has_conditional(𝕃ᵢ) ? ConditionalArgtypes(arginfo, sv) : SimpleArgtypes(arginfo.argtypes) + # fresh constant prop' + argtypes = has_conditional(𝕃ᵢ, sv) ? ConditionalArgtypes(arginfo, sv) : SimpleArgtypes(arginfo.argtypes) inf_result = InferenceResult(mi, argtypes, typeinf_lattice(interp)) if !any(inf_result.overridden_by_const) add_remark!(interp, sv, "[constprop] Could not handle constant info in matching_cache_argtypes") @@ -1026,6 +1044,7 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, end @assert inf_result.result !== nothing else + # found the cache for this constant prop' if inf_result.result === nothing add_remark!(interp, sv, "[constprop] Found cached constant inference in a cycle") return nothing @@ -1038,7 +1057,7 @@ end # (hopefully without doing too much work), returns `MethodInstance`, or nothing otherwise function maybe_get_const_prop_profitable(interp::AbstractInterpreter, result::MethodCallResult, @nospecialize(f), arginfo::ArgInfo, si::StmtInfo, - match::MethodMatch, sv::InferenceState) + match::MethodMatch, sv::AbsIntState) method = match.method force = force_const_prop(interp, f, method) force || const_prop_entry_heuristic(interp, result, si, sv) || return nothing @@ -1050,8 +1069,7 @@ function maybe_get_const_prop_profitable(interp::AbstractInterpreter, return nothing end all_overridden = is_all_overridden(interp, arginfo, sv) - if !force && !const_prop_function_heuristic(interp, f, arginfo, nargs, all_overridden, - is_nothrow(sv.ipo_effects), sv) + if !force && !const_prop_function_heuristic(interp, f, arginfo, nargs, all_overridden, sv) add_remark!(interp, sv, "[constprop] Disabled by function heuristic") return nothing end @@ -1069,7 +1087,7 @@ function maybe_get_const_prop_profitable(interp::AbstractInterpreter, return mi end -function const_prop_entry_heuristic(interp::AbstractInterpreter, result::MethodCallResult, si::StmtInfo, sv::InferenceState) +function const_prop_entry_heuristic(interp::AbstractInterpreter, result::MethodCallResult, si::StmtInfo, sv::AbsIntState) if call_result_unused(si) && result.edgecycle add_remark!(interp, sv, "[constprop] Disabled by entry heuristic (edgecycle with unused result)") return false @@ -1108,12 +1126,12 @@ end # determines heuristically whether if constant propagation can be worthwhile # by checking if any of given `argtypes` is "interesting" enough to be propagated -function const_prop_argument_heuristic(interp::AbstractInterpreter, arginfo::ArgInfo, sv::InferenceState) +function const_prop_argument_heuristic(interp::AbstractInterpreter, arginfo::ArgInfo, sv::AbsIntState) 𝕃ᵢ = typeinf_lattice(interp) argtypes = arginfo.argtypes for i in 1:length(argtypes) a = argtypes[i] - if has_conditional(𝕃ᵢ) && isa(a, Conditional) && arginfo.fargs !== nothing + if has_conditional(𝕃ᵢ, sv) && isa(a, Conditional) && arginfo.fargs !== nothing is_const_prop_profitable_conditional(a, arginfo.fargs, sv) && return true else a = widenslotwrapper(a) @@ -1146,11 +1164,11 @@ function find_constrained_arg(cnd::Conditional, fargs::Vector{Any}, sv::Inferenc end # checks if all argtypes has additional information other than what `Type` can provide -function is_all_overridden(interp::AbstractInterpreter, (; fargs, argtypes)::ArgInfo, sv::InferenceState) +function is_all_overridden(interp::AbstractInterpreter, (; fargs, argtypes)::ArgInfo, sv::AbsIntState) 𝕃ᵢ = typeinf_lattice(interp) for i in 1:length(argtypes) a = argtypes[i] - if has_conditional(𝕃ᵢ) && isa(a, Conditional) && fargs !== nothing + if has_conditional(𝕃ᵢ, sv) && isa(a, Conditional) && fargs !== nothing is_const_prop_profitable_conditional(a, fargs, sv) || return false else is_forwardable_argtype(𝕃ᵢ, widenslotwrapper(a)) || return false @@ -1166,8 +1184,8 @@ function force_const_prop(interp::AbstractInterpreter, @nospecialize(f), method: istopfunction(f, :setproperty!) end -function const_prop_function_heuristic(interp::AbstractInterpreter, @nospecialize(f), arginfo::ArgInfo, - nargs::Int, all_overridden::Bool, still_nothrow::Bool, _::InferenceState) +function const_prop_function_heuristic(interp::AbstractInterpreter, @nospecialize(f), + arginfo::ArgInfo, nargs::Int, all_overridden::Bool, sv::AbsIntState) argtypes = arginfo.argtypes if nargs > 1 𝕃ᵢ = typeinf_lattice(interp) @@ -1177,6 +1195,7 @@ function const_prop_function_heuristic(interp::AbstractInterpreter, @nospecializ if arrty isa Type && arrty <: AbstractArray && !issingletontype(arrty) # For static arrays, allow the constprop if we could possibly # deduce nothrow as a result. + still_nothrow = isa(sv, InferenceState) ? is_nothrow(sv.ipo_effects) : false if !still_nothrow || ismutabletype(arrty) return false end @@ -1214,7 +1233,7 @@ end # where we would spend a lot of time, but are probably unlikely to get an improved # result anyway. function const_prop_methodinstance_heuristic(interp::AbstractInterpreter, - mi::MethodInstance, arginfo::ArgInfo, sv::InferenceState) + mi::MethodInstance, arginfo::ArgInfo, sv::AbsIntState) method = mi.def::Method if method.is_for_opaque_closure # Not inlining an opaque closure can be very expensive, so be generous @@ -1258,7 +1277,6 @@ end # This is only for use with `Conditional`. # In general, usage of this is wrong. -ssa_def_slot(@nospecialize(arg), sv::IRCode) = nothing function ssa_def_slot(@nospecialize(arg), sv::InferenceState) code = sv.src.code init = sv.currpc @@ -1322,7 +1340,7 @@ AbstractIterationResult(cti::Vector{Any}, info::MaybeAbstractIterationInfo) = # Union of Tuples of the same length is converted to Tuple of Unions. # returns an array of types function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft), @nospecialize(typ), - sv::Union{InferenceState, IRCode}) + sv::AbsIntState) if isa(typ, PartialStruct) widet = typ.typ if isa(widet, DataType) && widet.name === Tuple.name @@ -1392,7 +1410,7 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) end # simulate iteration protocol on container type up to fixpoint -function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @nospecialize(itertype), sv::Union{InferenceState, IRCode}) +function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @nospecialize(itertype), sv::AbsIntState) if isa(itft, Const) iteratef = itft.val else @@ -1481,8 +1499,7 @@ end # do apply(af, fargs...), where af is a function value function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, si::StmtInfo, - sv::Union{InferenceState, IRCode}, - max_methods::Int = get_max_methods(sv.mod, interp)) + sv::AbsIntState, max_methods::Int=get_max_methods(sv, interp)) itft = argtype_by_index(argtypes, 2) aft = argtype_by_index(argtypes, 3) (itft === Bottom || aft === Bottom) && return CallMeta(Bottom, EFFECTS_THROWS, NoCallInfo()) @@ -1664,12 +1681,12 @@ end end function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs, argtypes)::ArgInfo, - sv::Union{InferenceState, IRCode}, max_methods::Int) + sv::AbsIntState, max_methods::Int) @nospecialize f la = length(argtypes) 𝕃ᵢ = typeinf_lattice(interp) ⊑ᵢ = ⊑(𝕃ᵢ) - if has_conditional(𝕃ᵢ) && f === Core.ifelse && fargs isa Vector{Any} && la == 4 + if has_conditional(𝕃ᵢ, sv) && f === Core.ifelse && fargs isa Vector{Any} && la == 4 cnd = argtypes[2] if isa(cnd, Conditional) newcnd = widenconditional(cnd) @@ -1708,7 +1725,7 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs end end end - elseif has_conditional(𝕃ᵢ) && (rt === Bool || (isa(rt, Const) && isa(rt.val, Bool))) && isa(fargs, Vector{Any}) + elseif has_conditional(𝕃ᵢ, sv) && (rt === Bool || (isa(rt, Const) && isa(rt.val, Bool))) && isa(fargs, Vector{Any}) # perform very limited back-propagation of type information for `is` and `isa` if f === isa a = ssa_def_slot(fargs[2], sv) @@ -1852,7 +1869,7 @@ function abstract_call_unionall(interp::AbstractInterpreter, argtypes::Vector{An return CallMeta(Any, EFFECTS_UNKNOWN, NoCallInfo()) end -function abstract_invoke(interp::AbstractInterpreter, (; fargs, argtypes)::ArgInfo, si::StmtInfo, sv::InferenceState) +function abstract_invoke(interp::AbstractInterpreter, (; fargs, argtypes)::ArgInfo, si::StmtInfo, sv::AbsIntState) ft′ = argtype_by_index(argtypes, 2) ft = widenconst(ft′) ft === Bottom && return CallMeta(Bottom, EFFECTS_THROWS, NoCallInfo()) @@ -1915,7 +1932,7 @@ function invoke_rewrite(xs::Vector{Any}) return newxs end -function abstract_finalizer(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::InferenceState) +function abstract_finalizer(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::AbsIntState) if length(argtypes) == 3 finalizer_argvec = Any[argtypes[2], argtypes[3]] call = abstract_call(interp, ArgInfo(nothing, finalizer_argvec), StmtInfo(false), sv, 1) @@ -1926,8 +1943,8 @@ end # call where the function is known exactly function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), - arginfo::ArgInfo, si::StmtInfo, sv::Union{InferenceState, IRCode}, - max_methods::Int = isa(sv, InferenceState) ? get_max_methods(f, sv.mod, interp) : 0) + arginfo::ArgInfo, si::StmtInfo, sv::AbsIntState, + max_methods::Int = get_max_methods(f, sv, interp)) (; fargs, argtypes) = arginfo la = length(argtypes) @@ -2066,7 +2083,7 @@ end # call where the function is any lattice element function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo, si::StmtInfo, - sv::Union{InferenceState, IRCode}, max_methods::Union{Int, Nothing} = isa(sv, IRCode) ? 0 : nothing) + sv::AbsIntState, max_methods::Union{Int, Nothing} = nothing) argtypes = arginfo.argtypes ft = widenslotwrapper(argtypes[1]) f = singleton_type(ft) @@ -2089,10 +2106,10 @@ function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo, si::StmtIn return CallMeta(Any, Effects(), NoCallInfo()) end # non-constant function, but the number of arguments is known and the `f` is not a builtin or intrinsic - max_methods = max_methods === nothing ? get_max_methods(sv.mod, interp) : max_methods + max_methods = max_methods === nothing ? get_max_methods(sv, interp) : max_methods return abstract_call_gf_by_type(interp, nothing, arginfo, si, argtypes_to_type(argtypes), sv, max_methods) end - max_methods = max_methods === nothing ? get_max_methods(f, sv.mod, interp) : max_methods + max_methods = max_methods === nothing ? get_max_methods(f, sv, interp) : max_methods return abstract_call_known(interp, f, arginfo, si, sv, max_methods) end @@ -2132,10 +2149,10 @@ function sp_type_rewrap(@nospecialize(T), linfo::MethodInstance, isreturn::Bool) return unwraptv(T) end -function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::VarTable, sv::InferenceState) +function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable,Nothing}, sv::AbsIntState) f = abstract_eval_value(interp, e.args[2], vtypes, sv) # rt = sp_type_rewrap(e.args[3], sv.linfo, true) - at = Any[ sp_type_rewrap(argt, sv.linfo, false) for argt in e.args[4]::SimpleVector ] + at = Any[ sp_type_rewrap(argt, frame_instance(sv), false) for argt in e.args[4]::SimpleVector ] pushfirst!(at, f) # this may be the wrong world for the call, # but some of the result is likely to be valid anyways @@ -2144,7 +2161,7 @@ function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::V nothing end -function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable, Nothing}, sv::Union{InferenceState, IRCode}) +function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable,Nothing}, sv::AbsIntState) rt = Any head = e.head if head === :static_parameter @@ -2186,23 +2203,27 @@ function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, vtypes:: return rt end -function abstract_eval_special_value(interp::AbstractInterpreter, @nospecialize(e), vtypes::Union{VarTable, Nothing}, sv::Union{InferenceState, IRCode}) +function abstract_eval_special_value(interp::AbstractInterpreter, @nospecialize(e), vtypes::Union{VarTable,Nothing}, sv::AbsIntState) if isa(e, QuoteNode) return Const(e.value) elseif isa(e, SSAValue) return abstract_eval_ssavalue(e, sv) elseif isa(e, SlotNumber) - vtyp = vtypes[slot_id(e)] - if vtyp.undef - merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; nothrow=false)) + if vtypes !== nothing + vtyp = vtypes[slot_id(e)] + if vtyp.undef + merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; nothrow=false)) + end + return vtyp.typ end - return vtyp.typ + merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; nothrow=false)) + return Any elseif isa(e, Argument) - if !isa(vtypes, Nothing) + if vtypes !== nothing return vtypes[slot_id(e)].typ else - @assert isa(sv, IRCode) - return sv.argtypes[e.n] + @assert isa(sv, IRInterpretationState) + return sv.ir.argtypes[e.n] # TODO frame_argtypes(sv)[e.n] and remove the assertion end elseif isa(e, GlobalRef) return abstract_eval_globalref(interp, e, sv) @@ -2211,7 +2232,7 @@ function abstract_eval_special_value(interp::AbstractInterpreter, @nospecialize( return Const(e) end -function abstract_eval_value(interp::AbstractInterpreter, @nospecialize(e), vtypes::Union{VarTable, Nothing}, sv::Union{InferenceState, IRCode}) +function abstract_eval_value(interp::AbstractInterpreter, @nospecialize(e), vtypes::Union{VarTable,Nothing}, sv::AbsIntState) if isa(e, Expr) return abstract_eval_value_expr(interp, e, vtypes, sv) else @@ -2220,7 +2241,7 @@ function abstract_eval_value(interp::AbstractInterpreter, @nospecialize(e), vtyp end end -function collect_argtypes(interp::AbstractInterpreter, ea::Vector{Any}, vtypes::Union{VarTable, Nothing}, sv::Union{InferenceState, IRCode}) +function collect_argtypes(interp::AbstractInterpreter, ea::Vector{Any}, vtypes::Union{VarTable,Nothing}, sv::AbsIntState) n = length(ea) argtypes = Vector{Any}(undef, n) @inbounds for i = 1:n @@ -2239,33 +2260,39 @@ struct RTEffects RTEffects(@nospecialize(rt), effects::Effects) = new(rt, effects) end -function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable, Nothing}, - sv::Union{InferenceState, IRCode}, mi::Union{MethodInstance, Nothing})::RTEffects +function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo, sv::InferenceState) + si = StmtInfo(!call_result_unused(sv, sv.currpc)) + (; rt, effects, info) = abstract_call(interp, arginfo, si, sv) + sv.stmt_info[sv.currpc] = info + # mark this call statement as DCE-elgible + # TODO better to do this in a single pass based on the `info` object at the end of abstractinterpret? + if is_removable_if_unused(effects) + add_curr_ssaflag!(sv, IR_FLAG_EFFECT_FREE) + else + sub_curr_ssaflag!(sv, IR_FLAG_EFFECT_FREE) + end + return RTEffects(rt, effects) +end + +function abstract_eval_call(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable,Nothing}, + sv::AbsIntState) + ea = e.args + argtypes = collect_argtypes(interp, ea, vtypes, sv) + if argtypes === nothing + return RTEffects(Bottom, Effects()) + end + arginfo = ArgInfo(ea, argtypes) + return abstract_call(interp, arginfo, sv) +end + +function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable,Nothing}, + sv::AbsIntState) effects = EFFECTS_UNKNOWN ehead = e.head 𝕃ᵢ = typeinf_lattice(interp) ⊑ᵢ = ⊑(𝕃ᵢ) if ehead === :call - ea = e.args - argtypes = collect_argtypes(interp, ea, vtypes, sv) - if argtypes === nothing - rt = Bottom - effects = Effects() - else - arginfo = ArgInfo(ea, argtypes) - si = StmtInfo(isa(sv, IRCode) ? true : !call_result_unused(sv, sv.currpc)) - (; rt, effects, info) = abstract_call(interp, arginfo, si, sv) - if isa(sv, InferenceState) - sv.stmt_info[sv.currpc] = info - # mark this call statement as DCE-elgible - # TODO better to do this in a single pass based on the `info` object at the end of abstractinterpret? - if is_removable_if_unused(effects) - add_curr_ssaflag!(sv, IR_FLAG_EFFECT_FREE) - else - sub_curr_ssaflag!(sv, IR_FLAG_EFFECT_FREE) - end - end - end + (; rt, effects) = abstract_eval_call(interp, e, vtypes, sv) t = rt elseif ehead === :new t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv)) @@ -2365,9 +2392,9 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp if argtypes === nothing t = Bottom else - mi′ = isa(sv, InferenceState) ? sv.linfo : mi - t = _opaque_closure_tfunc(𝕃ᵢ, argtypes[1], argtypes[2], argtypes[3], - argtypes[4], argtypes[5:end], mi′) + mi = frame_instance(sv) + t = opaque_closure_tfunc(𝕃ᵢ, argtypes[1], argtypes[2], argtypes[3], + argtypes[4], argtypes[5:end], mi) if isa(t, PartialOpaque) && isa(sv, InferenceState) && !call_result_unused(sv, sv.currpc) # Infer this now so that the specialization is available to # optimization. @@ -2380,7 +2407,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp end end elseif ehead === :foreigncall - (;rt, effects) = abstract_eval_foreigncall(interp, e, vtypes, sv, mi) + (; rt, effects) = abstract_eval_foreigncall(interp, e, vtypes, sv) t = rt if isa(sv, InferenceState) # mark this call statement as DCE-elgible @@ -2411,7 +2438,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp sym = e.args[1] t = Bool effects = EFFECTS_TOTAL - if isa(sym, SlotNumber) + if isa(sym, SlotNumber) && vtypes !== nothing vtyp = vtypes[slot_id(sym)] if vtyp.typ === Bottom t = Const(false) # never assigned previously @@ -2419,7 +2446,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp t = Const(true) # definitely assigned previously end elseif isa(sym, Symbol) - if isdefined(sv.mod, sym) + if isdefined(frame_module(sv), sym) t = Const(true) elseif InferenceParams(interp).assume_bindings_static t = Const(false) @@ -2465,10 +2492,10 @@ function refine_partial_type(@nospecialize t) return t end -function abstract_eval_foreigncall(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable, Nothing}, sv::Union{InferenceState, IRCode}, mi::Union{MethodInstance, Nothing}=nothing) +function abstract_eval_foreigncall(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable,Nothing}, sv::AbsIntState) abstract_eval_value(interp, e.args[1], vtypes, sv) - mi′ = isa(sv, InferenceState) ? sv.linfo : mi - t = sp_type_rewrap(e.args[2], mi′, true) + mi = frame_instance(sv) + t = sp_type_rewrap(e.args[2], mi, true) for i = 3:length(e.args) if abstract_eval_value(interp, e.args[i], vtypes, sv) === Bottom return RTEffects(Bottom, EFFECTS_THROWS) @@ -2493,7 +2520,7 @@ function abstract_eval_foreigncall(interp::AbstractInterpreter, e::Expr, vtypes: return RTEffects(t, effects) end -function abstract_eval_phi(interp::AbstractInterpreter, phi::PhiNode, vtypes::Union{VarTable, Nothing}, sv::Union{InferenceState, IRCode}) +function abstract_eval_phi(interp::AbstractInterpreter, phi::PhiNode, vtypes::Union{VarTable,Nothing}, sv::AbsIntState) rt = Union{} for i in 1:length(phi.values) isassigned(phi.values, i) || continue @@ -2503,8 +2530,8 @@ function abstract_eval_phi(interp::AbstractInterpreter, phi::PhiNode, vtypes::Un return rt end -function stmt_taints_inbounds_consistency(sv::InferenceState) - sv.src.propagate_inbounds && return true +function stmt_taints_inbounds_consistency(sv::AbsIntState) + propagate_inbounds(sv) && return true return (get_curr_ssaflag(sv) & IR_FLAG_INBOUNDS) != 0 end @@ -2515,9 +2542,9 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), end return abstract_eval_special_value(interp, e, vtypes, sv) end - (;rt, effects) = abstract_eval_statement_expr(interp, e, vtypes, sv, nothing) + (; rt, effects) = abstract_eval_statement_expr(interp, e, vtypes, sv) if !effects.noinbounds - if !sv.src.propagate_inbounds + if !propagate_inbounds(sv) # The callee read our inbounds flag, but unless we propagate inbounds, # we ourselves don't read our parent's inbounds. effects = Effects(effects; noinbounds=true) @@ -2555,7 +2582,7 @@ function abstract_eval_globalref(g::GlobalRef) end abstract_eval_global(M::Module, s::Symbol) = abstract_eval_globalref(GlobalRef(M, s)) -function abstract_eval_globalref(interp::AbstractInterpreter, g::GlobalRef, frame::Union{InferenceState, IRCode}) +function abstract_eval_globalref(interp::AbstractInterpreter, g::GlobalRef, sv::AbsIntState) rt = abstract_eval_globalref(g) consistent = inaccessiblememonly = ALWAYS_FALSE nothrow = false @@ -2573,7 +2600,7 @@ function abstract_eval_globalref(interp::AbstractInterpreter, g::GlobalRef, fram consistent = inaccessiblememonly = ALWAYS_TRUE rt = Union{} end - merge_effects!(interp, frame, Effects(EFFECTS_TOTAL; consistent, nothrow, inaccessiblememonly)) + merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; consistent, nothrow, inaccessiblememonly)) return rt end @@ -2586,7 +2613,7 @@ function handle_global_assignment!(interp::AbstractInterpreter, frame::Inference end abstract_eval_ssavalue(s::SSAValue, sv::InferenceState) = abstract_eval_ssavalue(s, sv.ssavaluetypes) -abstract_eval_ssavalue(s::SSAValue, src::CodeInfo) = abstract_eval_ssavalue(s, src.ssavaluetypes::Vector{Any}) + function abstract_eval_ssavalue(s::SSAValue, ssavaluetypes::Vector{Any}) typ = ssavaluetypes[s.id] if typ === NOT_FOUND diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 9229f54f143f2..0a1b852b052f9 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -152,7 +152,6 @@ include("compiler/ssair/domtree.jl") include("compiler/ssair/ir.jl") include("compiler/abstractlattice.jl") - include("compiler/inferenceresult.jl") include("compiler/inferencestate.jl") diff --git a/base/compiler/inferenceresult.jl b/base/compiler/inferenceresult.jl index c079553fca06a..790af05eebada 100644 --- a/base/compiler/inferenceresult.jl +++ b/base/compiler/inferenceresult.jl @@ -86,7 +86,7 @@ function va_process_argtypes(@nospecialize(va_handler!), 𝕃::AbstractLattice, nargs = Int(def.nargs) if isva || isvarargtype(given_argtypes[end]) isva_given_argtypes = Vector{Any}(undef, nargs) - for i = 1:(nargs - isva) + for i = 1:(nargs-isva) isva_given_argtypes[i] = argtype_by_index(given_argtypes, i) end if isva @@ -110,10 +110,8 @@ function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(spe isva = !toplevel && method.isva linfo_argtypes = Any[(unwrap_unionall(specTypes)::DataType).parameters...] nargs::Int = toplevel ? 0 : method.nargs - if !withfirst - # For opaque closure, the closure environment is processed elsewhere - nargs -= 1 - end + # For opaque closure, the closure environment is processed elsewhere + withfirst || (nargs -= 1) cache_argtypes = Vector{Any}(undef, nargs) # First, if we're dealing with a varargs method, then we set the last element of `args` # to the appropriate `Tuple` type or `PartialStruct` instance. diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 48d25243417fe..0e0409f755a0b 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -1,14 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -""" - const VarTable = Vector{VarState} - -The extended lattice that maps local variables to inferred type represented as `AbstractLattice`. -Each index corresponds to the `id` of `SlotNumber` which identifies each local variable. -Note that `InferenceState` will maintain multiple `VarTable`s at each SSA statement -to enable flow-sensitive analysis. -""" -const VarTable = Vector{VarState} +# data structures +# =============== mutable struct BitSetBoundedMinPrioritySet <: AbstractSet{Int} elems::BitSet @@ -78,6 +71,130 @@ function append!(bsbmp::BitSetBoundedMinPrioritySet, itr) end end +mutable struct TwoPhaseVectorView <: AbstractVector{Int} + const data::Vector{Int} + count::Int + const range::UnitRange{Int} +end +size(tpvv::TwoPhaseVectorView) = (tpvv.count,) +function getindex(tpvv::TwoPhaseVectorView, i::Int) + checkbounds(tpvv, i) + @inbounds tpvv.data[first(tpvv.range) + i - 1] +end +function push!(tpvv::TwoPhaseVectorView, v::Int) + tpvv.count += 1 + tpvv.data[first(tpvv.range) + tpvv.count - 1] = v + return nothing +end + +""" + mutable struct TwoPhaseDefUseMap + +This struct is intended as a memory- and GC-pressure-efficient mechanism +for incrementally computing def-use maps. The idea is that the def-use map +is constructed into two passes over the IR. In the first, we simply count the +the number of uses, computing the number of uses for each def as well as the +total number of uses. In the second pass, we actually fill in the def-use +information. + +The idea is that either of these two phases can be combined with other useful +work that needs to scan the instruction stream anyway, while avoiding the +significant allocation pressure of e.g. allocating an array for every SSA value +or attempting to dynamically move things around as new uses are discovered. + +The def-use map is presented as a vector of vectors. For every def, indexing +into the map will return a vector of uses. +""" +mutable struct TwoPhaseDefUseMap <: AbstractVector{TwoPhaseVectorView} + ssa_uses::Vector{Int} + data::Vector{Int} + complete::Bool +end + +function complete!(tpdum::TwoPhaseDefUseMap) + cumsum = 0 + for i = 1:length(tpdum.ssa_uses) + this_val = cumsum + 1 + cumsum += tpdum.ssa_uses[i] + tpdum.ssa_uses[i] = this_val + end + resize!(tpdum.data, cumsum) + fill!(tpdum.data, 0) + tpdum.complete = true +end + +function TwoPhaseDefUseMap(nssas::Int) + ssa_uses = zeros(Int, nssas) + data = Int[] + complete = false + return TwoPhaseDefUseMap(ssa_uses, data, complete) +end + +function count!(tpdum::TwoPhaseDefUseMap, arg::SSAValue) + @assert !tpdum.complete + tpdum.ssa_uses[arg.id] += 1 +end + +function kill_def_use!(tpdum::TwoPhaseDefUseMap, def::Int, use::Int) + if !tpdum.complete + tpdum.ssa_uses[def] -= 1 + else + range = tpdum.ssa_uses[def]:(def == length(tpdum.ssa_uses) ? length(tpdum.data) : (tpdum.ssa_uses[def + 1] - 1)) + # TODO: Sorted + useidx = findfirst(idx->tpdum.data[idx] == use, range) + @assert useidx !== nothing + idx = range[useidx] + while idx < lastindex(range) + ndata = tpdum.data[idx+1] + ndata == 0 && break + tpdum.data[idx] = ndata + end + tpdum.data[idx + 1] = 0 + end +end +kill_def_use!(tpdum::TwoPhaseDefUseMap, def::SSAValue, use::Int) = + kill_def_use!(tpdum, def.id, use) + +function getindex(tpdum::TwoPhaseDefUseMap, idx::Int) + @assert tpdum.complete + range = tpdum.ssa_uses[idx]:(idx == length(tpdum.ssa_uses) ? length(tpdum.data) : (tpdum.ssa_uses[idx + 1] - 1)) + # TODO: Make logarithmic + nelems = 0 + for i in range + tpdum.data[i] == 0 && break + nelems += 1 + end + return TwoPhaseVectorView(tpdum.data, nelems, range) +end + +mutable struct LazyGenericDomtree{IsPostDom} + ir::IRCode + domtree::GenericDomTree{IsPostDom} + LazyGenericDomtree{IsPostDom}(ir::IRCode) where {IsPostDom} = new{IsPostDom}(ir) +end +function get!(x::LazyGenericDomtree{IsPostDom}) where {IsPostDom} + isdefined(x, :domtree) && return x.domtree + return @timeit "domtree 2" x.domtree = IsPostDom ? + construct_postdomtree(x.ir.cfg.blocks) : + construct_domtree(x.ir.cfg.blocks) +end + +const LazyDomtree = LazyGenericDomtree{false} +const LazyPostDomtree = LazyGenericDomtree{true} + +# InferenceState +# ============== + +""" + const VarTable = Vector{VarState} + +The extended lattice that maps local variables to inferred type represented as `AbstractLattice`. +Each index corresponds to the `id` of `SlotNumber` which identifies each local variable. +Note that `InferenceState` will maintain multiple `VarTable`s at each SSA statement +to enable flow-sensitive analysis. +""" +const VarTable = Vector{VarState} + mutable struct InferenceState #= information about this method instance =# linfo::MethodInstance @@ -87,6 +204,7 @@ mutable struct InferenceState slottypes::Vector{Any} src::CodeInfo cfg::CFG + method_info::MethodInfo #= intermediate states for local abstract interpretation =# currbb::Int @@ -106,7 +224,7 @@ mutable struct InferenceState cycle_backedges::Vector{Tuple{InferenceState, Int}} # call-graph backedges connecting from callee to caller callers_in_cycle::Vector{InferenceState} dont_work_on_me::Bool - parent::Union{Nothing, InferenceState} + parent # ::Union{Nothing,AbsIntState} #= results =# result::InferenceResult # remember where to put the result @@ -135,6 +253,7 @@ mutable struct InferenceState sptypes = sptypes_from_meth_instance(linfo) code = src.code::Vector{Any} cfg = compute_basic_blocks(code) + method_info = MethodInfo(src) currbb = currpc = 1 ip = BitSet(1) # TODO BitSetBoundedMinPrioritySet(1) @@ -183,7 +302,7 @@ mutable struct InferenceState cache !== :no && push!(get_inference_cache(interp), result) return new( - linfo, world, mod, sptypes, slottypes, src, cfg, + linfo, world, mod, sptypes, slottypes, src, cfg, method_info, currbb, currpc, ip, handler_at, ssavalue_uses, bb_vartables, ssavaluetypes, stmt_edges, stmt_info, pclimitations, limitations, cycle_backedges, callers_in_cycle, dont_work_on_me, parent, result, valid_worlds, bestguess, ipo_effects, @@ -195,43 +314,6 @@ end is_inferred(sv::InferenceState) = is_inferred(sv.result) is_inferred(result::InferenceResult) = result.result !== nothing -function merge_effects!(::AbstractInterpreter, caller::InferenceState, effects::Effects) - caller.ipo_effects = merge_effects(caller.ipo_effects, effects) -end - -merge_effects!(interp::AbstractInterpreter, caller::InferenceState, callee::InferenceState) = - merge_effects!(interp, caller, Effects(callee)) -merge_effects!(interp::AbstractInterpreter, caller::IRCode, effects::Effects) = nothing - -is_effect_overridden(sv::InferenceState, effect::Symbol) = is_effect_overridden(sv.linfo, effect) -function is_effect_overridden(linfo::MethodInstance, effect::Symbol) - def = linfo.def - return isa(def, Method) && is_effect_overridden(def, effect) -end -is_effect_overridden(method::Method, effect::Symbol) = is_effect_overridden(decode_effects_override(method.purity), effect) -is_effect_overridden(override::EffectsOverride, effect::Symbol) = getfield(override, effect) - -add_remark!(::AbstractInterpreter, sv::Union{InferenceState, IRCode}, remark) = return - -struct InferenceLoopState - sig - rt - effects::Effects - function InferenceLoopState(@nospecialize(sig), @nospecialize(rt), effects::Effects) - new(sig, rt, effects) - end -end - -function bail_out_toplevel_call(::AbstractInterpreter, state::InferenceLoopState, sv::Union{InferenceState, IRCode}) - return isa(sv, InferenceState) && sv.restrict_abstract_call_sites && !isdispatchtuple(state.sig) -end -function bail_out_call(::AbstractInterpreter, state::InferenceLoopState, sv::Union{InferenceState, IRCode}) - return state.rt === Any && !is_foldable(state.effects) -end -function bail_out_apply(::AbstractInterpreter, state::InferenceLoopState, sv::Union{InferenceState, IRCode}) - return state.rt === Any -end - was_reached(sv::InferenceState, pc::Int) = sv.ssavaluetypes[pc] !== NOT_FOUND function compute_trycatch(code::Vector{Any}, ip::BitSet) @@ -341,29 +423,6 @@ function should_insert_coverage(mod::Module, src::CodeInfo) return false end -""" - Iterate through all callers of the given InferenceState in the abstract - interpretation stack (including the given InferenceState itself), vising - children before their parents (i.e. ascending the tree from the given - InferenceState). Note that cycles may be visited in any order. -""" -struct InfStackUnwind - inf::InferenceState -end -iterate(unw::InfStackUnwind) = (unw.inf, (unw.inf, 0)) -function iterate(unw::InfStackUnwind, (infstate, cyclei)::Tuple{InferenceState, Int}) - # iterate through the cycle before walking to the parent - if cyclei < length(infstate.callers_in_cycle) - cyclei += 1 - infstate = infstate.callers_in_cycle[cyclei] - else - cyclei = 0 - infstate = infstate.parent - end - infstate === nothing && return nothing - (infstate::InferenceState, (infstate, cyclei)) -end - function InferenceState(result::InferenceResult, cache::Symbol, interp::AbstractInterpreter) # prepare an InferenceState object for inferring lambda world = get_world_counter(interp) @@ -511,14 +570,7 @@ function sptypes_from_meth_instance(linfo::MethodInstance) return sptypes end -_topmod(sv::InferenceState) = _topmod(sv.mod) - -# work towards converging the valid age range for sv -function update_valid_age!(sv::InferenceState, valid_worlds::WorldRange) - valid_worlds = sv.valid_worlds = intersect(valid_worlds, sv.valid_worlds) - @assert(sv.world in valid_worlds, "invalid age range update") - return valid_worlds -end +_topmod(sv::InferenceState) = _topmod(frame_module(sv)) function record_ssa_assign!(𝕃ᵢ::AbstractLattice, ssa_id::Int, @nospecialize(new), frame::InferenceState) ssavaluetypes = frame.ssavaluetypes @@ -552,44 +604,16 @@ function add_cycle_backedge!(caller::InferenceState, frame::InferenceState, curr return frame end -# temporarily accumulate our edges to later add as backedges in the callee -function add_backedge!(caller::InferenceState, mi::MethodInstance) - edges = get_stmt_edges!(caller) - if edges !== nothing - push!(edges, mi) - end - return nothing -end - -function add_invoke_backedge!(caller::InferenceState, @nospecialize(invokesig::Type), mi::MethodInstance) - edges = get_stmt_edges!(caller) - if edges !== nothing - push!(edges, invokesig, mi) - end - return nothing -end - -# used to temporarily accumulate our no method errors to later add as backedges in the callee method table -function add_mt_backedge!(caller::InferenceState, mt::MethodTable, @nospecialize(typ)) - edges = get_stmt_edges!(caller) - if edges !== nothing - push!(edges, mt, typ) - end - return nothing -end - -function get_stmt_edges!(caller::InferenceState) - if !isa(caller.linfo.def, Method) - return nothing # don't add backedges to toplevel exprs - end - edges = caller.stmt_edges[caller.currpc] +function get_stmt_edges!(caller::InferenceState, currpc::Int=caller.currpc) + stmt_edges = caller.stmt_edges + edges = stmt_edges[currpc] if edges === nothing - edges = caller.stmt_edges[caller.currpc] = [] + edges = stmt_edges[currpc] = [] end return edges end -function empty_backedges!(frame::InferenceState, currpc::Int = frame.currpc) +function empty_backedges!(frame::InferenceState, currpc::Int=frame.currpc) edges = frame.stmt_edges[currpc] edges === nothing || empty!(edges) return nothing @@ -608,10 +632,6 @@ function print_callstack(sv::InferenceState) end end -get_curr_ssaflag(sv::InferenceState) = sv.src.ssaflags[sv.currpc] -add_curr_ssaflag!(sv::InferenceState, flag::UInt8) = sv.src.ssaflags[sv.currpc] |= flag -sub_curr_ssaflag!(sv::InferenceState, flag::UInt8) = sv.src.ssaflags[sv.currpc] &= ~flag - function narguments(sv::InferenceState, include_va::Bool=true) def = sv.linfo.def nargs = length(sv.result.argtypes) @@ -620,3 +640,223 @@ function narguments(sv::InferenceState, include_va::Bool=true) end return nargs end + +# IRInterpretationState +# ===================== + +# TODO add `result::InferenceResult` and put the irinterp result into the inference cache? +mutable struct IRInterpretationState + const method_info::MethodInfo + const ir::IRCode + const mi::MethodInstance + const world::UInt + curridx::Int + const argtypes_refined::Vector{Bool} + const sptypes::Vector{VarState} + const tpdum::TwoPhaseDefUseMap + const ssa_refined::BitSet + const lazydomtree::LazyDomtree + valid_worlds::WorldRange + const edges::Vector{Any} + parent # ::Union{Nothing,AbsIntState} + + function IRInterpretationState(interp::AbstractInterpreter, + method_info::MethodInfo, ir::IRCode, mi::MethodInstance, argtypes::Vector{Any}, + world::UInt, min_world::UInt, max_world::UInt) + curridx = 1 + given_argtypes = Vector{Any}(undef, length(argtypes)) + for i = 1:length(given_argtypes) + given_argtypes[i] = widenslotwrapper(argtypes[i]) + end + given_argtypes = va_process_argtypes(optimizer_lattice(interp), given_argtypes, mi) + argtypes_refined = Bool[!⊑(optimizer_lattice(interp), ir.argtypes[i], given_argtypes[i]) + for i = 1:length(given_argtypes)] + empty!(ir.argtypes) + append!(ir.argtypes, given_argtypes) + tpdum = TwoPhaseDefUseMap(length(ir.stmts)) + ssa_refined = BitSet() + lazydomtree = LazyDomtree(ir) + valid_worlds = WorldRange(min_world, max_world == typemax(UInt) ? get_world_counter() : max_world) + edges = Any[] + parent = nothing + return new(method_info, ir, mi, world, curridx, argtypes_refined, ir.sptypes, tpdum, + ssa_refined, lazydomtree, valid_worlds, edges, parent) + end +end + +function IRInterpretationState(interp::AbstractInterpreter, + code::CodeInstance, mi::MethodInstance, argtypes::Vector{Any}, world::UInt) + @assert code.def === mi + src = @atomic :monotonic code.inferred + if isa(src, Vector{UInt8}) + src = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), mi.def, C_NULL, src)::CodeInfo + else + isa(src, CodeInfo) || return nothing + end + method_info = MethodInfo(src) + ir = inflate_ir(src, mi) + return IRInterpretationState(interp, method_info, ir, mi, argtypes, world, + src.min_world, src.max_world) +end + +# AbsIntState +# =========== + +const AbsIntState = Union{InferenceState,IRInterpretationState} + +frame_instance(sv::InferenceState) = sv.linfo +frame_instance(sv::IRInterpretationState) = sv.mi + +function frame_module(sv::AbsIntState) + mi = frame_instance(sv) + def = mi.def + isa(def, Module) && return def + return def.module +end + +frame_parent(sv::InferenceState) = sv.parent::Union{Nothing,AbsIntState} +frame_parent(sv::IRInterpretationState) = sv.parent::Union{Nothing,AbsIntState} + +is_constproped(sv::InferenceState) = any(sv.result.overridden_by_const) +is_constproped(::IRInterpretationState) = true + +is_cached(sv::InferenceState) = sv.cached +is_cached(::IRInterpretationState) = false + +method_info(sv::InferenceState) = sv.method_info +method_info(sv::IRInterpretationState) = sv.method_info + +propagate_inbounds(sv::AbsIntState) = method_info(sv).propagate_inbounds +method_for_inference_limit_heuristics(sv::AbsIntState) = method_info(sv).method_for_inference_limit_heuristics + +frame_world(sv::InferenceState) = sv.world +frame_world(sv::IRInterpretationState) = sv.world + +callers_in_cycle(sv::InferenceState) = sv.callers_in_cycle +callers_in_cycle(sv::IRInterpretationState) = () + +is_effect_overridden(sv::AbsIntState, effect::Symbol) = is_effect_overridden(frame_instance(sv), effect) +function is_effect_overridden(linfo::MethodInstance, effect::Symbol) + def = linfo.def + return isa(def, Method) && is_effect_overridden(def, effect) +end +is_effect_overridden(method::Method, effect::Symbol) = is_effect_overridden(decode_effects_override(method.purity), effect) +is_effect_overridden(override::EffectsOverride, effect::Symbol) = getfield(override, effect) + +has_conditional(𝕃::AbstractLattice, ::InferenceState) = has_conditional(𝕃) +has_conditional(::AbstractLattice, ::IRInterpretationState) = false + +# work towards converging the valid age range for sv +function update_valid_age!(sv::AbsIntState, valid_worlds::WorldRange) + valid_worlds = sv.valid_worlds = intersect(valid_worlds, sv.valid_worlds) + @assert sv.world in valid_worlds "invalid age range update" + return valid_worlds +end + +""" + AbsIntStackUnwind(sv::AbsIntState) + +Iterate through all callers of the given `AbsIntState` in the abstract interpretation stack +(including the given `AbsIntState` itself), visiting children before their parents (i.e. +ascending the tree from the given `AbsIntState`). +Note that cycles may be visited in any order. +""" +struct AbsIntStackUnwind + sv::AbsIntState +end +iterate(unw::AbsIntStackUnwind) = (unw.sv, (unw.sv, 0)) +function iterate(unw::AbsIntStackUnwind, (sv, cyclei)::Tuple{AbsIntState, Int}) + # iterate through the cycle before walking to the parent + if cyclei < length(callers_in_cycle(sv)) + cyclei += 1 + parent = callers_in_cycle(sv)[cyclei] + else + cyclei = 0 + parent = frame_parent(sv) + end + parent === nothing && return nothing + return (parent, (parent, cyclei)) +end + +# temporarily accumulate our edges to later add as backedges in the callee +function add_backedge!(caller::InferenceState, mi::MethodInstance) + isa(caller.linfo.def, Method) || return nothing # don't add backedges to toplevel method instance + return push!(get_stmt_edges!(caller), mi) +end +function add_backedge!(irsv::IRInterpretationState, mi::MethodInstance) + return push!(irsv.edges, mi) +end + +function add_invoke_backedge!(caller::InferenceState, @nospecialize(invokesig::Type), mi::MethodInstance) + isa(caller.linfo.def, Method) || return nothing # don't add backedges to toplevel method instance + return push!(get_stmt_edges!(caller), invokesig, mi) +end +function add_invoke_backedge!(irsv::IRInterpretationState, @nospecialize(invokesig::Type), mi::MethodInstance) + return push!(irsv.edges, invokesig, mi) +end + +# used to temporarily accumulate our no method errors to later add as backedges in the callee method table +function add_mt_backedge!(caller::InferenceState, mt::MethodTable, @nospecialize(typ)) + isa(caller.linfo.def, Method) || return nothing # don't add backedges to toplevel method instance + return push!(get_stmt_edges!(caller), mt, typ) +end +function add_mt_backedge!(irsv::IRInterpretationState, mt::MethodTable, @nospecialize(typ)) + return push!(irsv.edges, mt, typ) +end + +get_curr_ssaflag(sv::InferenceState) = sv.src.ssaflags[sv.currpc] +get_curr_ssaflag(sv::IRInterpretationState) = sv.ir.stmts[sv.curridx][:flag] + +add_curr_ssaflag!(sv::InferenceState, flag::UInt8) = sv.src.ssaflags[sv.currpc] |= flag +add_curr_ssaflag!(sv::IRInterpretationState, flag::UInt8) = sv.ir.stmts[sv.curridx][:flag] |= flag + +sub_curr_ssaflag!(sv::InferenceState, flag::UInt8) = sv.src.ssaflags[sv.currpc] &= ~flag +sub_curr_ssaflag!(sv::IRInterpretationState, flag::UInt8) = sv.ir.stmts[sv.curridx][:flag] &= ~flag + +merge_effects!(::AbstractInterpreter, caller::InferenceState, effects::Effects) = + caller.ipo_effects = merge_effects(caller.ipo_effects, effects) +merge_effects!(::AbstractInterpreter, ::IRInterpretationState, ::Effects) = return + +struct InferenceLoopState + sig + rt + effects::Effects + function InferenceLoopState(@nospecialize(sig), @nospecialize(rt), effects::Effects) + new(sig, rt, effects) + end +end + +bail_out_toplevel_call(::AbstractInterpreter, state::InferenceLoopState, sv::InferenceState) = + sv.restrict_abstract_call_sites && !isdispatchtuple(state.sig) +bail_out_toplevel_call(::AbstractInterpreter, ::InferenceLoopState, ::IRInterpretationState) = false + +bail_out_call(::AbstractInterpreter, state::InferenceLoopState, ::InferenceState) = + state.rt === Any && !is_foldable(state.effects) +bail_out_call(::AbstractInterpreter, state::InferenceLoopState, ::IRInterpretationState) = + state.rt === Any && !is_foldable(state.effects) + +bail_out_apply(::AbstractInterpreter, state::InferenceLoopState, ::InferenceState) = + state.rt === Any +bail_out_apply(::AbstractInterpreter, state::InferenceLoopState, ::IRInterpretationState) = + state.rt === Any + +function should_infer_this_call(interp::AbstractInterpreter, sv::InferenceState) + if InferenceParams(interp).unoptimize_throw_blocks + # Disable inference of calls in throw blocks, since we're unlikely to + # need their types. There is one exception however: If up until now, the + # function has not seen any side effects, we would like to make sure there + # aren't any in the throw block either to enable other optimizations. + if is_stmt_throw_block(get_curr_ssaflag(sv)) + should_infer_for_effects(sv) || return false + end + end + return true +end +function should_infer_for_effects(sv::InferenceState) + effects = sv.ipo_effects + return is_terminates(effects) && is_effect_free(effects) +end +should_infer_this_call(::AbstractInterpreter, ::IRInterpretationState) = true + +add_remark!(::AbstractInterpreter, ::InferenceState, remark) = return +add_remark!(::AbstractInterpreter, ::IRInterpretationState, remark) = return diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 1b5f45fac3e2e..84817b1bf6531 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -125,9 +125,9 @@ struct InliningState{Interp<:AbstractInterpreter} world::UInt interp::Interp end -function InliningState(frame::InferenceState, interp::AbstractInterpreter) - et = EdgeTracker(frame.stmt_edges[1]::Vector{Any}, frame.valid_worlds) - return InliningState(et, frame.world, interp) +function InliningState(sv::InferenceState, interp::AbstractInterpreter) + et = EdgeTracker(sv.stmt_edges[1]::Vector{Any}, sv.valid_worlds) + return InliningState(et, sv.world, interp) end function InliningState(interp::AbstractInterpreter) return InliningState(nothing, get_world_counter(interp), interp) @@ -150,12 +150,12 @@ mutable struct OptimizationState{Interp<:AbstractInterpreter} cfg::Union{Nothing,CFG} insert_coverage::Bool end -function OptimizationState(frame::InferenceState, interp::AbstractInterpreter, +function OptimizationState(sv::InferenceState, interp::AbstractInterpreter, recompute_cfg::Bool=true) - inlining = InliningState(frame, interp) - cfg = recompute_cfg ? nothing : frame.cfg - return OptimizationState(frame.linfo, frame.src, nothing, frame.stmt_info, frame.mod, - frame.sptypes, frame.slottypes, inlining, cfg, frame.insert_coverage) + inlining = InliningState(sv, interp) + cfg = recompute_cfg ? nothing : sv.cfg + return OptimizationState(sv.linfo, sv.src, nothing, sv.stmt_info, sv.mod, + sv.sptypes, sv.slottypes, inlining, cfg, sv.insert_coverage) end function OptimizationState(linfo::MethodInstance, src::CodeInfo, interp::AbstractInterpreter) # prepare src for running optimization passes if it isn't already @@ -389,9 +389,9 @@ function argextype( return Const(x) end end +abstract_eval_ssavalue(s::SSAValue, src::CodeInfo) = abstract_eval_ssavalue(s, src.ssavaluetypes::Vector{Any}) abstract_eval_ssavalue(s::SSAValue, src::Union{IRCode,IncrementalCompact}) = types(src)[s] - """ finish(interp::AbstractInterpreter, opt::OptimizationState, ir::IRCode, caller::InferenceResult) diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index 49c2f54278545..d171cceb842e9 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -1,189 +1,73 @@ -mutable struct TwoPhaseVectorView <: AbstractVector{Int} - const data::Vector{Int} - count::Int - const range::UnitRange{Int} -end -size(tpvv::TwoPhaseVectorView) = (tpvv.count,) -function getindex(tpvv::TwoPhaseVectorView, i::Int) - checkbounds(tpvv, i) - @inbounds tpvv.data[first(tpvv.range) + i - 1] -end -function push!(tpvv::TwoPhaseVectorView, v::Int) - tpvv.count += 1 - tpvv.data[first(tpvv.range) + tpvv.count - 1] = v - return nothing -end - -""" - mutable struct TwoPhaseDefUseMap - -This struct is intended as a memory- and GC-pressure-efficient mechanism -for incrementally computing def-use maps. The idea is that the def-use map -is constructed into two passes over the IR. In the first, we simply count the -the number of uses, computing the number of uses for each def as well as the -total number of uses. In the second pass, we actually fill in the def-use -information. - -The idea is that either of these two phases can be combined with other useful -work that needs to scan the instruction stream anyway, while avoiding the -significant allocation pressure of e.g. allocating an array for every SSA value -or attempting to dynamically move things around as new uses are discovered. - -The def-use map is presented as a vector of vectors. For every def, indexing -into the map will return a vector of uses. -""" -mutable struct TwoPhaseDefUseMap <: AbstractVector{TwoPhaseVectorView} - ssa_uses::Vector{Int} - data::Vector{Int} - complete::Bool -end - -function complete!(tpdum::TwoPhaseDefUseMap) - cumsum = 0 - for i = 1:length(tpdum.ssa_uses) - this_val = cumsum + 1 - cumsum += tpdum.ssa_uses[i] - tpdum.ssa_uses[i] = this_val - end - resize!(tpdum.data, cumsum) - fill!(tpdum.data, 0) - tpdum.complete = true -end - -function TwoPhaseDefUseMap(nssas::Int) - ssa_uses = zeros(Int, nssas) - data = Int[] - complete = false - return TwoPhaseDefUseMap(ssa_uses, data, complete) -end - -function count!(tpdum::TwoPhaseDefUseMap, arg::SSAValue) - @assert !tpdum.complete - tpdum.ssa_uses[arg.id] += 1 -end - -function kill_def_use!(tpdum::TwoPhaseDefUseMap, def::Int, use::Int) - if !tpdum.complete - tpdum.ssa_uses[def] -= 1 - else - range = tpdum.ssa_uses[def]:(def == length(tpdum.ssa_uses) ? length(tpdum.data) : (tpdum.ssa_uses[def + 1] - 1)) - # TODO: Sorted - useidx = findfirst(idx->tpdum.data[idx] == use, range) - @assert useidx !== nothing - idx = range[useidx] - while idx < lastindex(range) - ndata = tpdum.data[idx+1] - ndata == 0 && break - tpdum.data[idx] = ndata - end - tpdum.data[idx + 1] = 0 - end -end -kill_def_use!(tpdum::TwoPhaseDefUseMap, def::SSAValue, use::Int) = - kill_def_use!(tpdum, def.id, use) - -function getindex(tpdum::TwoPhaseDefUseMap, idx::Int) - @assert tpdum.complete - range = tpdum.ssa_uses[idx]:(idx == length(tpdum.ssa_uses) ? length(tpdum.data) : (tpdum.ssa_uses[idx + 1] - 1)) - # TODO: Make logarithmic - nelems = 0 - for i in range - tpdum.data[i] == 0 && break - nelems += 1 - end - return TwoPhaseVectorView(tpdum.data, nelems, range) -end - -struct IRInterpretationState - ir::IRCode - mi::MethodInstance - world::UInt - argtypes_refined::Vector{Bool} - tpdum::TwoPhaseDefUseMap - ssa_refined::BitSet - lazydomtree::LazyDomtree - function IRInterpretationState(interp::AbstractInterpreter, - ir::IRCode, mi::MethodInstance, world::UInt, argtypes::Vector{Any}) - argtypes = va_process_argtypes(optimizer_lattice(interp), argtypes, mi) - for i = 1:length(argtypes) - argtypes[i] = widenslotwrapper(argtypes[i]) - end - argtypes_refined = Bool[!⊑(optimizer_lattice(interp), ir.argtypes[i], argtypes[i]) for i = 1:length(argtypes)] - empty!(ir.argtypes) - append!(ir.argtypes, argtypes) - tpdum = TwoPhaseDefUseMap(length(ir.stmts)) - ssa_refined = BitSet() - lazydomtree = LazyDomtree(ir) - return new(ir, mi, world, argtypes_refined, tpdum, ssa_refined, lazydomtree) - end -end - -function codeinst_to_ir(interp::AbstractInterpreter, code::CodeInstance) - src = @atomic :monotonic code.inferred - mi = code.def - if isa(src, Vector{UInt8}) - src = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), mi.def, C_NULL, src)::CodeInfo - else - isa(src, CodeInfo) || return nothing - end - return inflate_ir(src, mi) -end +# This file is a part of Julia. License is MIT: https://julialang.org/license +# TODO (#48913) remove this overload to enable interprocedural call inference from irinterp function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), - arginfo::ArgInfo, si::StmtInfo, @nospecialize(atype), - sv::IRCode, max_methods::Int) + arginfo::ArgInfo, si::StmtInfo, @nospecialize(atype), + sv::IRInterpretationState, max_methods::Int) return CallMeta(Any, Effects(), NoCallInfo()) end -function collect_limitations!(@nospecialize(typ), ::IRCode) - @assert !isa(typ, LimitedAccuracy) "semi-concrete eval on recursive call graph" +function collect_limitations!(@nospecialize(typ), ::IRInterpretationState) + @assert !isa(typ, LimitedAccuracy) "irinterp is unable to handle heavy recursion" return typ end function concrete_eval_invoke(interp::AbstractInterpreter, inst::Expr, mi::MethodInstance, irsv::IRInterpretationState) - mi_cache = WorldView(code_cache(interp), irsv.world) + world = frame_world(irsv) + mi_cache = WorldView(code_cache(interp), world) code = get(mi_cache, mi, nothing) - if code === nothing - return Pair{Any, Bool}(nothing, false) - end - argtypes = collect_argtypes(interp, inst.args[2:end], nothing, irsv.ir) - argtypes === nothing && return Pair{Any, Bool}(Union{}, false) + code === nothing && return Pair{Any,Bool}(nothing, false) + argtypes = collect_argtypes(interp, inst.args[2:end], nothing, irsv) + argtypes === nothing && return Pair{Any,Bool}(Bottom, false) effects = decode_effects(code.ipo_purity_bits) if is_foldable(effects) && is_all_const_arg(argtypes, #=start=#1) args = collect_const_args(argtypes, #=start=#1) - world = get_world_counter(interp) - value = try - Core._call_in_world_total(world, args...) - catch - return Pair{Any, Bool}(Union{}, false) + value = let world = get_world_counter(interp) + try + Core._call_in_world_total(world, args...) + catch + return Pair{Any,Bool}(Bottom, false) + end end - return Pair{Any, Bool}(Const(value), true) + return Pair{Any,Bool}(Const(value), true) else - ir′ = codeinst_to_ir(interp, code) - if ir′ !== nothing - irsv′ = IRInterpretationState(interp, ir′, mi, irsv.world, argtypes) - return _ir_abstract_constant_propagation(interp, irsv′) + if is_constprop_edge_recursed(mi, irsv) + return Pair{Any,Bool}(nothing, is_nothrow(effects)) + end + newirsv = IRInterpretationState(interp, code, mi, argtypes, world) + if newirsv !== nothing + newirsv.parent = irsv + return _ir_abstract_constant_propagation(interp, newirsv) end + return Pair{Any,Bool}(nothing, is_nothrow(effects)) end - return Pair{Any, Bool}(nothing, is_nothrow(effects)) end +abstract_eval_ssavalue(s::SSAValue, sv::IRInterpretationState) = abstract_eval_ssavalue(s, sv.ir) + function abstract_eval_phi_stmt(interp::AbstractInterpreter, phi::PhiNode, ::Int, irsv::IRInterpretationState) - return abstract_eval_phi(interp, phi, nothing, irsv.ir) + return abstract_eval_phi(interp, phi, nothing, irsv) end function propagate_control_effects!(interp::AbstractInterpreter, idx::Int, stmt::GotoIfNot, - irsv::IRInterpretationState, reprocess::Union{Nothing, BitSet, BitSetBoundedMinPrioritySet}) + irsv::IRInterpretationState, extra_reprocess::Union{Nothing,BitSet,BitSetBoundedMinPrioritySet}) # Nothing to do for most abstract interpreters, but if the abstract # interpreter has control-dependent lattice effects, it can override # this method. return false end -function reprocess_instruction!(interp::AbstractInterpreter, - idx::Int, bb::Union{Int, Nothing}, @nospecialize(inst), @nospecialize(typ), - irsv::IRInterpretationState, reprocess::Union{Nothing, BitSet, BitSetBoundedMinPrioritySet}) +function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo, irsv::IRInterpretationState) + si = StmtInfo(true) # TODO better job here? + (; rt, effects, info) = abstract_call(interp, arginfo, si, irsv) + irsv.ir.stmts[irsv.curridx][:info] = info + return RTEffects(rt, effects) +end + +function reprocess_instruction!(interp::AbstractInterpreter, idx::Int, bb::Union{Int,Nothing}, + @nospecialize(inst), @nospecialize(typ), irsv::IRInterpretationState, + extra_reprocess::Union{Nothing,BitSet,BitSetBoundedMinPrioritySet}) ir = irsv.ir if isa(inst, GotoIfNot) cond = inst.cond @@ -192,22 +76,22 @@ function reprocess_instruction!(interp::AbstractInterpreter, function update_phi!(from::Int, to::Int) if length(ir.cfg.blocks[to].preds) == 0 # Kill the entire block - for idx in ir.cfg.blocks[to].stmts - ir.stmts[idx][:inst] = nothing - ir.stmts[idx][:type] = Union{} - ir.stmts[idx][:flag] = IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW + for bidx = ir.cfg.blocks[to].stmts + ir.stmts[bidx][:inst] = nothing + ir.stmts[bidx][:type] = Bottom + ir.stmts[bidx][:flag] = IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW end return end - for idx in ir.cfg.blocks[to].stmts - stmt = ir.stmts[idx][:inst] - isa(stmt, Nothing) && continue # allowed between `PhiNode`s - isa(stmt, PhiNode) || break - for (i, edge) in enumerate(stmt.edges) + for sidx = ir.cfg.blocks[to].stmts + sinst = ir.stmts[sidx][:inst] + isa(sinst, Nothing) && continue # allowed between `PhiNode`s + isa(sinst, PhiNode) || break + for (eidx, edge) in enumerate(sinst.edges) if edge == from - deleteat!(stmt.edges, i) - deleteat!(stmt.values, i) - push!(irsv.ssa_refined, idx) + deleteat!(sinst.edges, eidx) + deleteat!(sinst.values, eidx) + push!(irsv.ssa_refined, sidx) break end end @@ -230,27 +114,24 @@ function reprocess_instruction!(interp::AbstractInterpreter, end return true end - return propagate_control_effects!(interp, idx, inst, irsv, reprocess) + return propagate_control_effects!(interp, idx, inst, irsv, extra_reprocess) end rt = nothing if isa(inst, Expr) head = inst.head if head === :call || head === :foreigncall || head === :new || head === :splatnew - (; rt, effects) = abstract_eval_statement_expr(interp, inst, nothing, ir, irsv.mi) + (; rt, effects) = abstract_eval_statement_expr(interp, inst, nothing, irsv) ir.stmts[idx][:flag] |= flags_for_effects(effects) if is_foldable(effects) && isa(rt, Const) && is_inlineable_constant(rt.val) ir.stmts[idx][:inst] = quoted(rt.val) end elseif head === :invoke - mi′ = inst.args[1]::MethodInstance - if mi′ !== irsv.mi # prevent infinite loop - rt, nothrow = concrete_eval_invoke(interp, inst, mi′, irsv) - if nothrow - ir.stmts[idx][:flag] |= IR_FLAG_NOTHROW - if isa(rt, Const) && is_inlineable_constant(rt.val) - ir.stmts[idx][:inst] = quoted(rt.val) - end + rt, nothrow = concrete_eval_invoke(interp, inst, inst.args[1]::MethodInstance, irsv) + if nothrow + ir.stmts[idx][:flag] |= IR_FLAG_NOTHROW + if isa(rt, Const) && is_inlineable_constant(rt.val) + ir.stmts[idx][:inst] = quoted(rt.val) end end elseif head === :throw_undef_if_not || # TODO: Terminate interpretation early if known false? @@ -258,7 +139,6 @@ function reprocess_instruction!(interp::AbstractInterpreter, head === :gc_preserve_end return false else - ccall(:jl_, Cvoid, (Any,), inst) error("reprocess_instruction!: unhandled expression found") end elseif isa(inst, PhiNode) @@ -273,8 +153,7 @@ function reprocess_instruction!(interp::AbstractInterpreter, elseif isa(inst, GlobalRef) # GlobalRef is not refinable else - ccall(:jl_, Cvoid, (Any,), inst) - error() + error("reprocess_instruction!: unhandled instruction found") end if rt !== nothing && !⊑(optimizer_lattice(interp), typ, rt) ir.stmts[idx][:type] = rt @@ -283,10 +162,9 @@ function reprocess_instruction!(interp::AbstractInterpreter, return false end -# Process the terminator and add the successor to `ip`. Returns whether a backedge was seen. -function process_terminator!(ir::IRCode, idx::Int, bb::Int, - all_rets::Vector{Int}, ip::BitSetBoundedMinPrioritySet) - inst = ir.stmts[idx][:inst] +# Process the terminator and add the successor to `bb_ip`. Returns whether a backedge was seen. +function process_terminator!(ir::IRCode, @nospecialize(inst), idx::Int, bb::Int, + all_rets::Vector{Int}, bb_ip::BitSetBoundedMinPrioritySet) if isa(inst, ReturnNode) if isdefined(inst, :val) push!(all_rets, idx) @@ -294,43 +172,44 @@ function process_terminator!(ir::IRCode, idx::Int, bb::Int, return false elseif isa(inst, GotoNode) backedge = inst.label <= bb - !backedge && push!(ip, inst.label) + backedge || push!(bb_ip, inst.label) return backedge elseif isa(inst, GotoIfNot) backedge = inst.dest <= bb - !backedge && push!(ip, inst.dest) - push!(ip, bb + 1) + backedge || push!(bb_ip, inst.dest) + push!(bb_ip, bb+1) return backedge elseif isexpr(inst, :enter) dest = inst.args[1]::Int @assert dest > bb - push!(ip, dest) - push!(ip, bb + 1) + push!(bb_ip, dest) + push!(bb_ip, bb+1) return false else - push!(ip, bb + 1) + push!(bb_ip, bb+1) return false end end -default_reprocess(interp::AbstractInterpreter, irsv::IRInterpretationState) = nothing +default_reprocess(::AbstractInterpreter, ::IRInterpretationState) = nothing function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IRInterpretationState; extra_reprocess::Union{Nothing,BitSet} = default_reprocess(interp, irsv)) (; ir, tpdum, ssa_refined) = irsv bbs = ir.cfg.blocks - ip = BitSetBoundedMinPrioritySet(length(bbs)) - push!(ip, 1) + bb_ip = BitSetBoundedMinPrioritySet(length(bbs)) + push!(bb_ip, 1) all_rets = Int[] # Fast path: Scan both use counts and refinement in one single pass of # of the instructions. In the absence of backedges, this will # converge. - while !isempty(ip) - bb = popfirst!(ip) + while !isempty(bb_ip) + bb = popfirst!(bb_ip) stmts = bbs[bb].stmts lstmt = last(stmts) for idx = stmts + irsv.curridx = idx inst = ir.stmts[idx][:inst] typ = ir.stmts[idx][:type] any_refined = false @@ -357,11 +236,7 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR idx, bb, inst, typ, irsv, extra_reprocess) push!(ssa_refined, idx) end - if idx == lstmt - if process_terminator!(ir, idx, bb, all_rets, ip) - @goto residual_scan - end - end + idx == lstmt && process_terminator!(ir, inst, idx, bb, all_rets, bb_ip) && @goto residual_scan if typ === Bottom && !isa(inst, PhiNode) break end @@ -377,11 +252,12 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR end # Slow Path Phase 1.A: Complete use scanning - while !isempty(ip) - bb = popfirst!(ip) + while !isempty(bb_ip) + bb = popfirst!(bb_ip) stmts = bbs[bb].stmts lstmt = last(stmts) for idx = stmts + irsv.curridx = idx inst = ir.stmts[idx][:inst] for ur in userefs(inst) val = ur[] @@ -393,18 +269,19 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR count!(tpdum, val) end end - idx == lstmt && process_terminator!(ir, idx, bb, all_rets, ip) + idx == lstmt && process_terminator!(ir, inst, idx, bb, all_rets, bb_ip) end end # Slow Path Phase 1.B: Assemble def-use map complete!(tpdum) - push!(ip, 1) - while !isempty(ip) - bb = popfirst!(ip) + push!(bb_ip, 1) + while !isempty(bb_ip) + bb = popfirst!(bb_ip) stmts = bbs[bb].stmts lstmt = last(stmts) for idx = stmts + irsv.curridx = idx inst = ir.stmts[idx][:inst] for ur in userefs(inst) val = ur[] @@ -412,7 +289,7 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR push!(tpdum[val.id], idx) end end - idx == lstmt && process_terminator!(ir, idx, bb, all_rets, ip) + idx == lstmt && process_terminator!(ir, inst, idx, bb, all_rets, bb_ip) end end @@ -424,6 +301,7 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR end while !isempty(stmt_ip) idx = popfirst!(stmt_ip) + irsv.curridx = idx inst = ir.stmts[idx][:inst] typ = ir.stmts[idx][:type] if reprocess_instruction!(interp, @@ -434,7 +312,7 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR end begin @label compute_rt - ultimate_rt = Union{} + ultimate_rt = Bottom for idx in all_rets bb = block_for_inst(ir.cfg, idx) if bb != 1 && length(ir.cfg.blocks[bb].preds) == 0 @@ -448,26 +326,32 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR end nothrow = true - for i = 1:length(ir.stmts) - if (ir.stmts[i][:flag] & IR_FLAG_NOTHROW) == 0 + for idx = 1:length(ir.stmts) + if (ir.stmts[idx][:flag] & IR_FLAG_NOTHROW) == 0 nothrow = false break end end - return Pair{Any, Bool}(maybe_singleton_const(ultimate_rt), nothrow) + if last(irsv.valid_worlds) >= get_world_counter() + # if we aren't cached, we don't need this edge + # but our caller might, so let's just make it anyways + store_backedges(frame_instance(irsv), irsv.edges) + end + + return Pair{Any,Bool}(maybe_singleton_const(ultimate_rt), nothrow) end function ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IRInterpretationState) + irinterp = switch_to_irinterp(interp) if __measure_typeinf__[] inf_frame = Timings.InferenceFrameInfo(irsv.mi, irsv.world, VarState[], Any[], length(irsv.ir.argtypes)) Timings.enter_new_timer(inf_frame) - v = _ir_abstract_constant_propagation(interp, irsv) + ret = _ir_abstract_constant_propagation(irinterp, irsv) append!(inf_frame.slottypes, irsv.ir.argtypes) Timings.exit_current_timer(inf_frame) - return v + return ret else - T = _ir_abstract_constant_propagation(interp, irsv) - return T + return _ir_abstract_constant_propagation(irinterp, irsv) end end diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 7a9c877b6c2f3..a2508992bf290 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -604,21 +604,6 @@ function is_old(compact, @nospecialize(old_node_ssa)) !already_inserted(compact, old_node_ssa) end -mutable struct LazyGenericDomtree{IsPostDom} - ir::IRCode - domtree::GenericDomTree{IsPostDom} - LazyGenericDomtree{IsPostDom}(ir::IRCode) where {IsPostDom} = new{IsPostDom}(ir) -end -function get!(x::LazyGenericDomtree{IsPostDom}) where {IsPostDom} - isdefined(x, :domtree) && return x.domtree - return @timeit "domtree 2" x.domtree = IsPostDom ? - construct_postdomtree(x.ir.cfg.blocks) : - construct_domtree(x.ir.cfg.blocks) -end - -const LazyDomtree = LazyGenericDomtree{false} -const LazyPostDomtree = LazyGenericDomtree{true} - function perform_lifting!(compact::IncrementalCompact, visited_phinodes::Vector{AnySSAValue}, @nospecialize(cache_key), lifting_cache::IdDict{Pair{AnySSAValue, Any}, AnySSAValue}, diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 89ae4a1c26df6..84794a27ec034 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1365,7 +1365,7 @@ end PT = Const(Pair) return instanceof_tfunc(apply_type_tfunc(𝕃, PT, T, T))[1] end -function abstract_modifyfield!(interp::AbstractInterpreter, argtypes::Vector{Any}, si::StmtInfo, sv::InferenceState) +function abstract_modifyfield!(interp::AbstractInterpreter, argtypes::Vector{Any}, si::StmtInfo, sv::AbsIntState) nargs = length(argtypes) if !isempty(argtypes) && isvarargtype(argtypes[nargs]) nargs - 1 <= 6 || return CallMeta(Bottom, EFFECTS_THROWS, NoCallInfo()) @@ -1973,7 +1973,7 @@ function array_elmtype(@nospecialize ary) return Any end -@nospecs function _opaque_closure_tfunc(𝕃::AbstractLattice, arg, lb, ub, source, env::Vector{Any}, linfo::MethodInstance) +@nospecs function opaque_closure_tfunc(𝕃::AbstractLattice, arg, lb, ub, source, env::Vector{Any}, linfo::MethodInstance) argt, argt_exact = instanceof_tfunc(arg) lbt, lb_exact = instanceof_tfunc(lb) if !lb_exact @@ -2363,7 +2363,7 @@ function builtin_nothrow(𝕃::AbstractLattice, @nospecialize(f), argtypes::Vect end function builtin_tfunction(interp::AbstractInterpreter, @nospecialize(f), argtypes::Vector{Any}, - sv::Union{InferenceState,IRCode,Nothing}) + sv::Union{AbsIntState, Nothing}) 𝕃ᵢ = typeinf_lattice(interp) if f === tuple return tuple_tfunc(𝕃ᵢ, argtypes) @@ -2544,7 +2544,7 @@ end # TODO: this function is a very buggy and poor model of the return_type function # since abstract_call_gf_by_type is a very inaccurate model of _method and of typeinf_type, # while this assumes that it is an absolutely precise and accurate and exact model of both -function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, si::StmtInfo, sv::Union{InferenceState, IRCode}) +function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, si::StmtInfo, sv::AbsIntState) if length(argtypes) == 3 tt = widenslotwrapper(argtypes[3]) if isa(tt, Const) || (isType(tt) && !has_free_typevars(tt)) @@ -2603,7 +2603,7 @@ end # a simplified model of abstract_call_gf_by_type for applicable function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, - sv::InferenceState, max_methods::Int) + sv::AbsIntState, max_methods::Int) length(argtypes) < 2 && return CallMeta(Union{}, EFFECTS_UNKNOWN, NoCallInfo()) isvarargtype(argtypes[2]) && return CallMeta(Bool, EFFECTS_UNKNOWN, NoCallInfo()) argtypes = argtypes[2:end] @@ -2649,7 +2649,7 @@ end add_tfunc(applicable, 1, INT_INF, @nospecs((𝕃::AbstractLattice, f, args...)->Bool), 40) # a simplified model of abstract_invoke for Core._hasmethod -function _hasmethod_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::InferenceState) +function _hasmethod_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::AbsIntState) if length(argtypes) == 3 && !isvarargtype(argtypes[3]) ft′ = argtype_by_index(argtypes, 2) ft = widenconst(ft′) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 03b074bbec318..1eec73d0435bd 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -206,6 +206,7 @@ const __measure_typeinf__ = fill(false) # Wrapper around _typeinf that optionally records the exclusive time for each invocation. function typeinf(interp::AbstractInterpreter, frame::InferenceState) + interp = switch_from_irinterp(interp) if __measure_typeinf__[] Timings.enter_new_timer(frame) v = _typeinf(interp, frame) @@ -564,23 +565,22 @@ function finish(me::InferenceState, interp::AbstractInterpreter) end # record the backedges -function store_backedges(frame::InferenceResult, edges::Vector{Any}) - toplevel = !isa(frame.linfo.def, Method) - if !toplevel - store_backedges(frame.linfo, edges) - end - nothing +function store_backedges(caller::InferenceResult, edges::Vector{Any}) + isa(caller.linfo.def, Method) || return nothing # don't add backedges to toplevel method instance + return store_backedges(caller.linfo, edges) end -function store_backedges(frame::MethodInstance, edges::Vector{Any}) - for (; sig, caller) in BackedgeIterator(edges) - if isa(caller, MethodInstance) - ccall(:jl_method_instance_add_backedge, Cvoid, (Any, Any, Any), caller, sig, frame) +function store_backedges(caller::MethodInstance, edges::Vector{Any}) + for itr in BackedgeIterator(edges) + callee = itr.caller + if isa(callee, MethodInstance) + ccall(:jl_method_instance_add_backedge, Cvoid, (Any, Any, Any), callee, itr.sig, caller) else - typeassert(caller, MethodTable) - ccall(:jl_method_table_add_backedge, Cvoid, (Any, Any, Any), caller, sig, frame) + typeassert(callee, MethodTable) + ccall(:jl_method_table_add_backedge, Cvoid, (Any, Any, Any), callee, itr.sig, caller) end end + return nothing end function record_slot_assign!(sv::InferenceState) @@ -784,15 +784,20 @@ function merge_call_chain!(interp::AbstractInterpreter, parent::InferenceState, union_caller_cycle!(ancestor, child) child = parent child === ancestor && break - parent = child.parent::InferenceState + parent = frame_parent(child) + while !isa(parent, InferenceState) + # XXX we may miss some edges here? + parent = frame_parent(parent::IRInterpretationState) + end + parent = parent::InferenceState end end function is_same_frame(interp::AbstractInterpreter, mi::MethodInstance, frame::InferenceState) - return mi === frame.linfo + return mi === frame_instance(frame) end -function poison_callstack(infstate::InferenceState, topmost::InferenceState) +function poison_callstack!(infstate::InferenceState, topmost::InferenceState) push!(infstate.pclimitations, topmost) nothing end @@ -804,33 +809,38 @@ end # frame's `callers_in_cycle` field and adding the appropriate backedges. Finally, # we return `mi`'s pre-existing frame. If no cycles are found, `nothing` is # returned instead. -function resolve_call_cycle!(interp::AbstractInterpreter, mi::MethodInstance, parent::InferenceState) +function resolve_call_cycle!(interp::AbstractInterpreter, mi::MethodInstance, parent::AbsIntState) + # TODO (#48913) implement a proper recursion handling for irinterp: + # This works just because currently the `:terminate` condition guarantees that + # irinterp doesn't fail into unresolved cycles, but it's not a good solution. + # We should revisit this once we have a better story for handling cycles in irinterp. + isa(parent, InferenceState) || return false frame = parent uncached = false while isa(frame, InferenceState) - uncached |= !frame.cached # ensure we never add an uncached frame to a cycle + uncached |= !is_cached(frame) # ensure we never add an uncached frame to a cycle if is_same_frame(interp, mi, frame) if uncached # our attempt to speculate into a constant call lead to an undesired self-cycle # that cannot be converged: poison our call-stack (up to the discovered duplicate frame) # with the limited flag and abort (set return type to Any) now - poison_callstack(parent, frame) + poison_callstack!(parent, frame) return true end merge_call_chain!(interp, parent, frame, frame) return frame end - for caller in frame.callers_in_cycle + for caller in callers_in_cycle(frame) if is_same_frame(interp, mi, caller) if uncached - poison_callstack(parent, frame) + poison_callstack!(parent, frame) return true end merge_call_chain!(interp, parent, frame, caller) return caller end end - frame = frame.parent + frame = frame_parent(frame) end return false end @@ -851,7 +861,7 @@ struct EdgeCallResult end # compute (and cache) an inferred AST and return the current best estimate of the result type -function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, caller::InferenceState) +function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, caller::AbsIntState) mi = specialize_method(method, atype, sparams)::MethodInstance code = get(code_cache(interp), mi, nothing) if code isa CodeInstance # return existing rettype if the code is already inferred @@ -890,9 +900,9 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize add_remark!(interp, caller, "Inference is disabled for the target module") return EdgeCallResult(Any, nothing, Effects()) end - if !caller.cached && caller.parent === nothing + if !is_cached(caller) && frame_parent(caller) === nothing # this caller exists to return to the user - # (if we asked resolve_call_cyle, it might instead detect that there is a cycle that it can't merge) + # (if we asked resolve_call_cycle!, it might instead detect that there is a cycle that it can't merge) frame = false else frame = resolve_call_cycle!(interp, mi, caller) @@ -908,7 +918,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize unlock_mi_inference(interp, mi) return EdgeCallResult(Any, nothing, Effects()) end - if caller.cached || caller.parent !== nothing # don't involve uncached functions in cycle resolution + if is_cached(caller) || frame_parent(caller) !== nothing # don't involve uncached functions in cycle resolution frame.parent = caller end typeinf(interp, frame) diff --git a/base/compiler/types.jl b/base/compiler/types.jl index 91d585cdd76ff..c987e03df5261 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -32,6 +32,14 @@ struct StmtInfo used::Bool end +struct MethodInfo + propagate_inbounds::Bool + method_for_inference_limit_heuristics::Union{Nothing,Method} +end +MethodInfo(src::CodeInfo) = MethodInfo( + src.propagate_inbounds, + src.method_for_inference_limit_heuristics::Union{Nothing,Method}) + """ v::VarState @@ -465,13 +473,23 @@ optimizer_lattice(interp::NativeInterpreter) = OptimizerLattice(SimpleInferenceL """ switch_to_irinterp(interp::AbstractInterpreter) -> irinterp::AbstractInterpreter -Optionally convert `interp` to new `irinterp::AbstractInterpreter` to perform semi-concrete -interpretation. `NativeInterpreter` uses this interface to switch its lattice to -`optimizer_lattice` during semi-concrete interpretation on `IRCode`. +This interface allows `ir_abstract_constant_propagation` to convert `interp` to a new +`irinterp::AbstractInterpreter` to perform semi-concrete interpretation. +`NativeInterpreter` uses this interface to switch its lattice to `optimizer_lattice` during +semi-concrete interpretation on `IRCode`. """ switch_to_irinterp(interp::AbstractInterpreter) = interp switch_to_irinterp(interp::NativeInterpreter) = NativeInterpreter(interp; irinterp=true) +""" + switch_from_irinterp(irinterp::AbstractInterpreter) -> interp::AbstractInterpreter + +The inverse operation of `switch_to_irinterp`, allowing `typeinf` to convert `irinterp` back +to a new `interp::AbstractInterpreter` to perform ordinary abstract interpretation. +""" +switch_from_irinterp(irinterp::AbstractInterpreter) = irinterp +switch_from_irinterp(irinterp::NativeInterpreter) = NativeInterpreter(irinterp; irinterp=false) + abstract type CallInfo end @nospecialize diff --git a/test/compiler/datastructures.jl b/test/compiler/datastructures.jl index a25a884373ab4..8dbaee61503d0 100644 --- a/test/compiler/datastructures.jl +++ b/test/compiler/datastructures.jl @@ -7,7 +7,7 @@ using Test table = Core.Compiler.method_table(interp) sig = Tuple{typeof(*), Any, Any} result1 = Core.Compiler.findall(sig, table; limit=-1) - result2 = Core.Compiler.findall(sig, table; limit=Core.Compiler.get_max_methods(*, @__MODULE__, interp)) + result2 = Core.Compiler.findall(sig, table; limit=Core.Compiler.InferenceParams().max_methods) @test result1 !== nothing && !Core.Compiler.isempty(result1.matches) @test result2 === nothing end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 12fedf2792a61..1634345f70459 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -4762,3 +4762,64 @@ fhasmethod(::Integer, ::Int32) = 3 @test only(Base.return_types(()) do; Val(hasmethod(tuple, Tuple{Vararg{Int}})); end) === Val{true} @test only(Base.return_types(()) do; Val(hasmethod(sin, Tuple{Int, Vararg{Int}})); end) == Val{false} @test only(Base.return_types(()) do; Val(hasmethod(sin, Tuple{Int, Int, Vararg{Int}})); end) === Val{false} + +# TODO (#48913) enable interprocedural call inference from irinterp +# # interprocedural call inference from irinterp +# @noinline Base.@assume_effects :total issue48679_unknown_any(x) = Base.inferencebarrier(x) + +# @noinline _issue48679(y::Union{Nothing,T}) where {T} = T::Type +# Base.@constprop :aggressive function issue48679(x, b) +# if b +# x = issue48679_unknown_any(x) +# end +# return _issue48679(x) +# end +# @test Base.return_types((Float64,)) do x +# issue48679(x, false) +# end |> only == Type{Float64} + +# Base.@constprop :aggressive @noinline _issue48679_const(b, y::Union{Nothing,T}) where {T} = b ? nothing : T::Type +# Base.@constprop :aggressive function issue48679_const(x, b) +# if b +# x = issue48679_unknown_any(x) +# end +# return _issue48679_const(b, x) +# end +# @test Base.return_types((Float64,)) do x +# issue48679_const(x, false) +# end |> only == Type{Float64} + +# `invoke` call in irinterp +@noinline _irinterp_invoke(x::Any) = :any +@noinline _irinterp_invoke(x::T) where T = T +Base.@constprop :aggressive Base.@assume_effects :foldable function irinterp_invoke(x::T, b) where T + return @invoke _irinterp_invoke(x::(b ? T : Any)) +end +@test Base.return_types((Int,)) do x + irinterp_invoke(x, true) +end |> only == Type{Int} + +# recursion detection for semi-concrete interpretation +# avoid direct infinite loop via `concrete_eval_invoke` +Base.@assume_effects :foldable function recur_irinterp1(x, y) + if rand(Bool) + return x, y + end + return recur_irinterp1(x+1, y) +end +@test Base.return_types((Symbol,)) do y + recur_irinterp1(0, y) +end |> only === Tuple{Int,Symbol} +@test last(recur_irinterp1(0, :y)) === :y +# avoid indirect infinite loop via `concrete_eval_invoke` +Base.@assume_effects :foldable function recur_irinterp2(x, y) + if rand(Bool) + return x, y + end + return _recur_irinterp2(x+1, y) +end +Base.@assume_effects :foldable _recur_irinterp2(x, y) = @noinline recur_irinterp2(x, y) +@test Base.return_types((Symbol,)) do y + recur_irinterp2(0, y) +end |> only === Tuple{Int,Symbol} +@test last(recur_irinterp2(0, :y)) === :y From 98988d8bfbc769c3d20736e0c2e20717b11cd4c9 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 4 Apr 2023 05:29:54 +0900 Subject: [PATCH 431/775] REPLCompletions: replace `get_type` by the proper inference (#49206) This PR generalizes the idea from #49199 and uses inference to analyze the types of REPL expression. This approach offers several advantages over the current `get_[value|type]`-based implementation: - The need for various special cases is eliminated, as lowering normalizes expressions, and inference handles all language features. - Constant propagation allows us to obtain accurate completions for complex expressions safely (see #36437). Analysis on arbitrary REPL expressions can be done by the following steps: - Lower a given expression - Form a top-level `MethodInstance` from the lowered expression - Run inference on the top-level `MethodInstance` This PR implements `REPLInterpreter`, a custom `AbstractInterpreter` that: - aggressively resolve global bindings to enable reasonable completions for lines like `Mod.a.|` (where `|` is the cursor position) - aggressively concrete evaluates `:inconsistent` calls to provide reasonable completions for cases like `Ref(Some(42))[].|` - does not optimize the inferred code, as `REPLInterpreter` is only used to obtain the type or constant information of given expressions Aggressive binding resolution presents challenges for `REPLInterpreter`'s cache validation (since #40399 hasn't been resolved yet). To avoid cache validation issue, `REPLInterpreter` only allows aggressive binding resolution for top-level frame representing REPL input code (`repl_frame`) and for child `getproperty` frames that are constant propagated from the `repl_frame`. This works, since 1.) these frames are never cached, and 2.) their results are only observed by the non-cached `repl_frame` `REPLInterpreter` also aggressively concrete evaluate `:inconsistent` calls within `repl_frame`, allowing it to get get accurate type information about complex expressions that otherwise can not be constant folded, in a safe way, i.e. it still doesn't evaluate effectful expressions like `pop!(xs)`. Similarly to the aggressive binding resolution, aggressive concrete evaluation doesn't present any cache validation issues because `repl_frame` is never cached. Also note that the code cache for `REPLInterpreter` is separated from the native code cache, ensuring that code caches produced by `REPLInterpreter`, where bindings are aggressively resolved and the code is not really optimized, do not affect the native code execution. A hack has also been added to avoid serializing `CodeInstances`s produced by `REPLInterpreter` during precompilation to workaround #48453. closes #36437 replaces #49199 --- stdlib/REPL/src/REPLCompletions.jl | 291 +++++++++++++++++----------- stdlib/REPL/test/replcompletions.jl | 91 ++++++--- 2 files changed, 243 insertions(+), 139 deletions(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 34ce7ad9928fb..03332e8905d3d 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -4,6 +4,8 @@ module REPLCompletions export completions, shell_completions, bslash_completions, completion_text +using Core: CodeInfo, MethodInstance, CodeInstance, Const +const CC = Core.Compiler using Base.Meta using Base: propertynames, something @@ -151,21 +153,21 @@ function complete_symbol(sym::String, @nospecialize(ffunc), context_module::Modu ex = Meta.parse(lookup_name, raise=false, depwarn=false) - b, found = get_value(ex, context_module) - if found - val = b - if isa(b, Module) - mod = b + res = repl_eval_ex(ex, context_module) + res === nothing && return Completion[] + if res isa Const + val = res.val + if isa(val, Module) + mod = val lookup_module = true else lookup_module = false - t = typeof(b) + t = typeof(val) end - else # If the value is not found using get_value, the expression contain an advanced expression + else lookup_module = false - t, found = get_type(ex, context_module) + t = CC.widenconst(res) end - found || return Completion[] end suggestions = Completion[] @@ -404,133 +406,182 @@ function find_start_brace(s::AbstractString; c_start='(', c_end=')') return (startind:lastindex(s), method_name_end) end -# Returns the value in a expression if sym is defined in current namespace fn. -# This method is used to iterate to the value of a expression like: -# :(REPL.REPLCompletions.whitespace_chars) a `dump` of this expression -# will show it consist of Expr, QuoteNode's and Symbol's which all needs to -# be handled differently to iterate down to get the value of whitespace_chars. -function get_value(sym::Expr, fn) - if sym.head === :quote || sym.head === :inert - return sym.args[1], true - end - sym.head !== :. && return (nothing, false) - for ex in sym.args - ex, found = get_value(ex, fn)::Tuple{Any, Bool} - !found && return (nothing, false) - fn, found = get_value(ex, fn)::Tuple{Any, Bool} - !found && return (nothing, false) - end - return (fn, true) +struct REPLInterpreterCache + dict::IdDict{MethodInstance,CodeInstance} end -get_value(sym::Symbol, fn) = isdefined(fn, sym) ? (getfield(fn, sym), true) : (nothing, false) -get_value(sym::QuoteNode, fn) = (sym.value, true) -get_value(sym::GlobalRef, fn) = get_value(sym.name, sym.mod) -get_value(sym, fn) = (sym, true) - -# Return the type of a getfield call expression -function get_type_getfield(ex::Expr, fn::Module) - length(ex.args) == 3 || return Any, false # should never happen, but just for safety - fld, found = get_value(ex.args[3], fn) - fld isa Symbol || return Any, false - obj = ex.args[2] - objt, found = get_type(obj, fn) - found || return Any, false - objt isa DataType || return Any, false - hasfield(objt, fld) || return Any, false - return fieldtype(objt, fld), true +REPLInterpreterCache() = REPLInterpreterCache(IdDict{MethodInstance,CodeInstance}()) +const REPL_INTERPRETER_CACHE = REPLInterpreterCache() + +function get_code_cache() + # XXX Avoid storing analysis results into the cache that persists across precompilation, + # as [sys|pkg]image currently doesn't support serializing externally created `CodeInstance`. + # Otherwise, `CodeInstance`s created by `REPLInterpreter``, that are much less optimized + # that those produced by `NativeInterpreter`, will leak into the native code cache, + # potentially causing runtime slowdown. + # (see https://github.com/JuliaLang/julia/issues/48453). + if (@ccall jl_generating_output()::Cint) == 1 + return REPLInterpreterCache() + else + return REPL_INTERPRETER_CACHE + end end -# Determines the return type with the Compiler of a function call using the type information of the arguments. -function get_type_call(expr::Expr, fn::Module) - f_name = expr.args[1] - f, found = get_type(f_name, fn) - found || return (Any, false) # If the function f is not found return Any. - args = Any[] - for i in 2:length(expr.args) # Find the type of the function arguments - typ, found = get_type(expr.args[i], fn) - found ? push!(args, typ) : push!(args, Any) +struct REPLInterpreter <: CC.AbstractInterpreter + repl_frame::CC.InferenceResult + world::UInt + inf_params::CC.InferenceParams + opt_params::CC.OptimizationParams + inf_cache::Vector{CC.InferenceResult} + code_cache::REPLInterpreterCache + function REPLInterpreter(repl_frame::CC.InferenceResult; + world::UInt = Base.get_world_counter(), + inf_params::CC.InferenceParams = CC.InferenceParams(), + opt_params::CC.OptimizationParams = CC.OptimizationParams(), + inf_cache::Vector{CC.InferenceResult} = CC.InferenceResult[], + code_cache::REPLInterpreterCache = get_code_cache()) + return new(repl_frame, world, inf_params, opt_params, inf_cache, code_cache) end - world = Base.get_world_counter() - return_type = Core.Compiler.return_type(Tuple{f, args...}, world) - return (return_type, true) end - -# Returns the return type. example: get_type(:(Base.strip("", ' ')), Main) returns (SubString{String}, true) -function try_get_type(sym::Expr, fn::Module) - val, found = get_value(sym, fn) - found && return Core.Typeof(val), found - if sym.head === :call - # getfield call is special cased as the evaluation of getfield provides good type information, - # is inexpensive and it is also performed in the complete_symbol function. - a1 = sym.args[1] - if a1 === :getfield || a1 === GlobalRef(Core, :getfield) - return get_type_getfield(sym, fn) +CC.InferenceParams(interp::REPLInterpreter) = interp.inf_params +CC.OptimizationParams(interp::REPLInterpreter) = interp.opt_params +CC.get_world_counter(interp::REPLInterpreter) = interp.world +CC.get_inference_cache(interp::REPLInterpreter) = interp.inf_cache +CC.code_cache(interp::REPLInterpreter) = CC.WorldView(interp.code_cache, CC.WorldRange(interp.world)) +CC.get(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance, default) = get(wvc.cache.dict, mi, default) +CC.getindex(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance) = getindex(wvc.cache.dict, mi) +CC.haskey(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance) = haskey(wvc.cache.dict, mi) +CC.setindex!(wvc::CC.WorldView{REPLInterpreterCache}, ci::CodeInstance, mi::MethodInstance) = setindex!(wvc.cache.dict, ci, mi) + +# REPLInterpreter is only used for type analysis, so it should disable optimization entirely +CC.may_optimize(::REPLInterpreter) = false + +# REPLInterpreter analyzes a top-level frame, so better to not bail out from it +CC.bail_out_toplevel_call(::REPLInterpreter, ::CC.InferenceLoopState, ::CC.InferenceState) = false + +# `REPLInterpreter` aggressively resolves global bindings to enable reasonable completions +# for lines like `Mod.a.|` (where `|` is the cursor position). +# Aggressive binding resolution poses challenges for the inference cache validation +# (until https://github.com/JuliaLang/julia/issues/40399 is implemented). +# To avoid the cache validation issues, `REPLInterpreter` only allows aggressive binding +# resolution for top-level frame representing REPL input code (`repl_frame`) and for child +# `getproperty` frames that are constant propagated from the `repl_frame`. This works, since +# a.) these frames are never cached, and +# b.) their results are only observed by the non-cached `repl_frame`. +# +# `REPLInterpreter` also aggressively concrete evaluate `:inconsistent` calls within +# `repl_frame` to provide reasonable completions for lines like `Ref(Some(42))[].|`. +# Aggressive concrete evaluation allows us to get accurate type information about complex +# expressions that otherwise can not be constant folded, in a safe way, i.e. it still +# doesn't evaluate effectful expressions like `pop!(xs)`. +# Similarly to the aggressive binding resolution, aggressive concrete evaluation doesn't +# present any cache validation issues because `repl_frame` is never cached. + +is_repl_frame(interp::REPLInterpreter, sv::CC.InferenceState) = interp.repl_frame === sv.result + +# aggressive global binding resolution within `repl_frame` +function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef, + sv::CC.InferenceState) + if is_repl_frame(interp, sv) + if CC.isdefined_globalref(g) + return Const(ccall(:jl_get_globalref_value, Any, (Any,), g)) end - return get_type_call(sym, fn) - elseif sym.head === :thunk - thk = sym.args[1] - rt = ccall(:jl_infer_thunk, Any, (Any, Any), thk::Core.CodeInfo, fn) - rt !== Any && return (rt, true) - elseif sym.head === :ref - # some simple cases of `expand` - return try_get_type(Expr(:call, GlobalRef(Base, :getindex), sym.args...), fn) - elseif sym.head === :. && sym.args[2] isa QuoteNode # second check catches broadcasting - return try_get_type(Expr(:call, GlobalRef(Core, :getfield), sym.args...), fn) - elseif sym.head === :toplevel || sym.head === :block - isempty(sym.args) && return (nothing, true) - return try_get_type(sym.args[end], fn) - elseif sym.head === :escape || sym.head === :var"hygienic-scope" - return try_get_type(sym.args[1], fn) + return Union{} end - return (Any, false) + return @invoke CC.abstract_eval_globalref(interp::CC.AbstractInterpreter, g::GlobalRef, + sv::CC.InferenceState) end -try_get_type(other, fn::Module) = get_type(other, fn) +function is_repl_frame_getproperty(interp::REPLInterpreter, sv::CC.InferenceState) + def = sv.linfo.def + def isa Method || return false + def.name === :getproperty || return false + sv.cached && return false + return is_repl_frame(interp, sv.parent) +end -function get_type(sym::Expr, fn::Module) - # try to analyze nests of calls. if this fails, try using the expanded form. - val, found = try_get_type(sym, fn) - found && return val, found - # https://github.com/JuliaLang/julia/issues/27184 - if isexpr(sym, :macrocall) - _, found = get_type(first(sym.args), fn) - found || return Any, false - end - newsym = try - macroexpand(fn, sym; recursive=false) - catch e - # user code failed in macroexpand (ignore it) - return Any, false - end - val, found = try_get_type(newsym, fn) - if !found - newsym = try - Meta.lower(fn, sym) - catch e - # user code failed in lowering (ignore it) - return Any, false +# aggressive global binding resolution for `getproperty(::Module, ::Symbol)` calls within `repl_frame` +function CC.builtin_tfunction(interp::REPLInterpreter, @nospecialize(f), + argtypes::Vector{Any}, sv::CC.InferenceState) + if f === Core.getglobal && is_repl_frame_getproperty(interp, sv) + if length(argtypes) == 2 + a1, a2 = argtypes + if isa(a1, Const) && isa(a2, Const) + a1val, a2val = a1.val, a2.val + if isa(a1val, Module) && isa(a2val, Symbol) + g = GlobalRef(a1val, a2val) + if CC.isdefined_globalref(g) + return Const(ccall(:jl_get_globalref_value, Any, (Any,), g)) + end + return Union{} + end + end end - val, found = try_get_type(newsym, fn) end - return val, found + return @invoke CC.builtin_tfunction(interp::CC.AbstractInterpreter, f::Any, + argtypes::Vector{Any}, sv::CC.InferenceState) end -function get_type(sym, fn::Module) - val, found = get_value(sym, fn) - return found ? Core.Typeof(val) : Any, found +# aggressive concrete evaluation for `:inconsistent` frames within `repl_frame` +function CC.concrete_eval_eligible(interp::REPLInterpreter, @nospecialize(f), + result::CC.MethodCallResult, arginfo::CC.ArgInfo, + sv::CC.InferenceState) + if is_repl_frame(interp, sv) + neweffects = CC.Effects(result.effects; consistent=CC.ALWAYS_TRUE) + result = CC.MethodCallResult(result.rt, result.edgecycle, result.edgelimited, + result.edge, neweffects) + end +return @invoke CC.concrete_eval_eligible(interp::CC.AbstractInterpreter, f::Any, + result::CC.MethodCallResult, arginfo::CC.ArgInfo, + sv::CC.InferenceState) +end + +function resolve_toplevel_symbols!(mod::Module, src::Core.CodeInfo) + newsrc = copy(src) + @ccall jl_resolve_globals_in_ir( + #=jl_array_t *stmts=# newsrc.code::Any, + #=jl_module_t *m=# mod::Any, + #=jl_svec_t *sparam_vals=# Core.svec()::Any, + #=int binding_effects=# 0::Int)::Cvoid + return newsrc end -function get_type(T, found::Bool, default_any::Bool) - return found ? T : - default_any ? Any : throw(ArgumentError("argument not found")) +# lower `ex` and run type inference on the resulting top-level expression +function repl_eval_ex(@nospecialize(ex), context_module::Module) + lwr = try + Meta.lower(context_module, ex) + catch # macro expansion failed, etc. + return nothing + end + if lwr isa Symbol + return isdefined(context_module, lwr) ? Const(getfield(context_module, lwr)) : nothing + end + lwr isa Expr || return Const(lwr) # `ex` is literal + isexpr(lwr, :thunk) || return nothing # lowered to `Expr(:error, ...)` or similar + src = lwr.args[1]::Core.CodeInfo + + # construct top-level `MethodInstance` + mi = ccall(:jl_new_method_instance_uninit, Ref{Core.MethodInstance}, ()); + mi.specTypes = Tuple{} + + mi.def = context_module + src = resolve_toplevel_symbols!(context_module, src) + @atomic mi.uninferred = src + + result = CC.InferenceResult(mi) + interp = REPLInterpreter(result) + frame = CC.InferenceState(result, src, #=cache=#:no, interp)::CC.InferenceState + + CC.typeinf(interp, frame) + + return frame.result.result end # Method completion on function call expression that look like :(max(1)) MAX_METHOD_COMPLETIONS::Int = 40 function _complete_methods(ex_org::Expr, context_module::Module, shift::Bool) - funct, found = get_type(ex_org.args[1], context_module)::Tuple{Any,Bool} - !found && return 2, funct, [], Set{Symbol}() - + funct = repl_eval_ex(ex_org.args[1], context_module) + funct === nothing && return 2, nothing, [], Set{Symbol}() + funct = CC.widenconst(funct) args_ex, kwargs_ex, kwargs_flag = complete_methods_args(ex_org, context_module, true, true) return kwargs_flag, funct, args_ex, kwargs_ex end @@ -635,7 +686,14 @@ function detect_args_kwargs(funargs::Vector{Any}, context_module::Module, defaul # argument types push!(args_ex, Any) else - push!(args_ex, get_type(get_type(ex, context_module)..., default_any)) + argt = repl_eval_ex(ex, context_module) + if argt !== nothing + push!(args_ex, CC.widenconst(argt)) + elseif default_any + push!(args_ex, Any) + else + throw(ArgumentError("argument not found")) + end end end end @@ -709,7 +767,6 @@ function close_path_completion(str, startpos, r, paths, pos) return lastindex(str) <= pos || str[nextind(str, pos)] != '"' end - function bslash_completions(string::String, pos::Int) slashpos = something(findprev(isequal('\\'), string, pos), 0) if (something(findprev(in(bslash_separators), string, pos), 0) < slashpos && diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 547e5c5659d3f..2ce2471b21c4d 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -121,7 +121,7 @@ let ex = quote const tuple = (1, 2) - test_y_array=[CompletionFoo.Test_y(rand()) for i in 1:10] + test_y_array=[(@__MODULE__).Test_y(rand()) for i in 1:10] test_dict = Dict("abc"=>1, "abcd"=>10, :bar=>2, :bar2=>9, Base=>3, occursin=>4, `ls`=>5, 66=>7, 67=>8, ("q",3)=>11, "α"=>12, :α=>13) @@ -132,7 +132,7 @@ let ex = quote macro testcmd_cmd(s) end macro tϵsτcmδ_cmd(s) end - end + end # module CompletionFoo test_repl_comp_dict = CompletionFoo.test_dict test_repl_comp_customdict = CompletionFoo.test_customdict test_dict_ℂ = Dict(1=>2) @@ -548,22 +548,17 @@ let s = "convert(" @test length(c2) > REPL.REPLCompletions.MAX_METHOD_COMPLETIONS end -########## Test where the current inference logic fails ######## -# Fails due to inference fails to determine a concrete type for arg 1 -# But it returns AbstractArray{T,N} and hence is able to remove test5(x::Float64) from the suggestions -let s = "CompletionFoo.test5(AbstractArray[[]][1]," +let s = "CompletionFoo.test5(AbstractArray[Bool[]][1]," c, r, res = test_complete(s) @test !res - @test length(c) == 2 + @test length(c) == 1 end -# equivalent to above but due to the time macro the completion fails to find the concrete type -let s = "CompletionFoo.test3(@time([1, 2] + CompletionFoo.varfloat)," +let s = "CompletionFoo.test3(@time([1, 2] .+ CompletionFoo.varfloat)," c, r, res = test_complete(s) @test !res @test length(c) == 2 end -################################################################# # method completions with kwargs let s = "CompletionFoo.kwtest( " @@ -780,7 +775,7 @@ end let s = "CompletionFoo.test10(\"a\", Union{Signed,Bool,String}[3][1], " c, r, res = test_complete(s) @test !res - @test length(c) == 4 + @test length(c) == 2 @test all(startswith("test10("), c) @test allunique(c) @test !any(str->occursin("test10(a::Integer, b::Integer, c)", str), c) @@ -790,7 +785,7 @@ end let s = "CompletionFoo.test11(Integer[false][1], Integer[14][1], " c, r, res = test_complete(s) @test !res - @test length(c) == 4 + @test length(c) == 3 @test all(startswith("test11("), c) @test allunique(c) end @@ -798,10 +793,10 @@ end let s = "CompletionFoo.test11(Integer[-7][1], Integer[0x6][1], 6," c, r, res = test_complete(s) @test !res - @test length(c) == 3 + @test length(c) == 2 @test any(str->occursin("test11(a::Integer, b, c)", str), c) @test any(str->occursin("test11(u, v::Integer, w)", str), c) - @test any(str->occursin("test11(x::$Int, y::$Int, z)", str), c) + @test !any(str->occursin("test11(x::$Int, y::$Int, z)", str), c) end let s = "CompletionFoo.test11(3, 4," @@ -1606,11 +1601,16 @@ let s = ":(function foo(::Int) end).args[1].args[2]." @test c == Any[] end -let s = "log(log.(x)," +let s = "log(log.(varfloat)," c, r = test_complete_foo(s) @test !isempty(c) end +let s = "log(log.(noexist)," + c, r = test_complete_foo(s) + @test isempty(c) +end + let s = "Base.return_types(getin" c, r = test_complete_foo(s) @test "getindex" in c @@ -1698,8 +1698,7 @@ end @testset "https://github.com/JuliaLang/julia/issues/40247" begin # getfield type completion can work for complicated expression - let - m = Module() + let m = Module() @eval m begin struct Rs rs::Vector{Regex} @@ -1716,8 +1715,7 @@ end @test length(c) == fieldcount(Regex) end - let - m = Module() + let m = Module() @eval m begin struct R r::Regex @@ -1739,10 +1737,8 @@ end end end - @testset "https://github.com/JuliaLang/julia/issues/47593" begin - let - m = Module() + let m = Module() @eval m begin struct TEST_47594 var"("::Int @@ -1754,3 +1750,54 @@ end @test c == Any["var\"(\""] end end + +# https://github.com/JuliaLang/julia/issues/36437 +struct Issue36437{T} + v::T +end +Base.propertynames(::Issue36437) = (:a, :b, :c) +function Base.getproperty(v::Issue36437, s::Symbol) + if s === :a + return 1 + elseif s === :b + return 2 + elseif s === :c + return getfield(v, :v) + else + throw(ArgumentError(lazy"`(v::Issue36437).$s` is not supported")) + end +end + +let s = "Issue36437(42)." + c, r, res = test_complete_context(s, @__MODULE__) + @test res + for n in ("a", "b", "c") + @test n in c + end +end + +let s = "Some(Issue36437(42)).value." + c, r, res = test_complete_context(s, @__MODULE__) + @test res + for n in ("a", "b", "c") + @test n in c + end +end + +# aggressive concrete evaluation on mutable allocation in `repl_frame` +let s = "Ref(Issue36437(42))[]." + c, r, res = test_complete_context(s, @__MODULE__) + @test res + for n in ("a", "b", "c") + @test n in c + end + @test "v" ∉ c +end + +const global_xs = [Some(42)] +let s = "pop!(global_xs)." + c, r, res = test_complete_context(s, @__MODULE__) + @test res + @test "value" in c +end +@test length(global_xs) == 1 # the completion above shouldn't evaluate `pop!` call From b258051f65a1d90084376839e0974901f9afbdd1 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 4 Apr 2023 06:42:50 +0200 Subject: [PATCH 432/775] move out Profile from the sysimage (#49132) --- base/Base.jl | 34 ++++++++++++++++++++++++++ base/sysimg.jl | 1 - contrib/generate_precompile.jl | 14 ----------- pkgimage.mk | 3 +-- stdlib/Profile/src/Profile.jl | 42 ++------------------------------ stdlib/Profile/src/precompile.jl | 6 +++++ test/precompile.jl | 2 +- 7 files changed, 44 insertions(+), 58 deletions(-) create mode 100644 stdlib/Profile/src/precompile.jl diff --git a/base/Base.jl b/base/Base.jl index 5e3e64a104ee6..5dd029e1660da 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -529,6 +529,31 @@ for match = _methods(+, (Int, Int), -1, get_world_counter()) end if is_primary_base_module + +# Profiling helper +# triggers printing the report and (optionally) saving a heap snapshot after a SIGINFO/SIGUSR1 profile request +# Needs to be in Base because Profile is no longer loaded on boot +const PROFILE_PRINT_COND = Ref{Base.AsyncCondition}() +function profile_printing_listener() + profile = nothing + try + while true + wait(PROFILE_PRINT_COND[]) + profile = @something(profile, require(Base, :Profile)) + invokelatest(profile.peek_report[]) + if Base.get_bool_env("JULIA_PROFILE_PEEK_HEAP_SNAPSHOT", false) === true + println(stderr, "Saving heap snapshot...") + fname = invokelatest(profile.take_heap_snapshot) + println(stderr, "Heap snapshot saved to `$(fname)`") + end + end + catch ex + if !isa(ex, InterruptException) + @error "Profile printing listener crashed" exception=ex,catch_backtrace() + end + end +end + function __init__() # Base library init reinit_stdio() @@ -541,6 +566,15 @@ function __init__() if haskey(ENV, "JULIA_MAX_NUM_PRECOMPILE_FILES") MAX_NUM_PRECOMPILE_FILES[] = parse(Int, ENV["JULIA_MAX_NUM_PRECOMPILE_FILES"]) end + # Profiling helper + @static if !Sys.iswindows() + # triggering a profile via signals is not implemented on windows + cond = Base.AsyncCondition() + Base.uv_unref(cond.handle) + PROFILE_PRINT_COND[] = cond + ccall(:jl_set_peek_cond, Cvoid, (Ptr{Cvoid},), PROFILE_PRINT_COND[].handle) + errormonitor(Threads.@spawn(profile_printing_listener())) + end nothing end diff --git a/base/sysimg.jl b/base/sysimg.jl index 6f7219afda550..d64b463e9a957 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -60,7 +60,6 @@ let :Future, :InteractiveUtils, :LibGit2, - :Profile, :UUIDs, # 3-depth packages diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 862820a944e60..a7d3bfafdd849 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -233,20 +233,6 @@ if Test !== nothing """ end -Profile = get(Base.loaded_modules, - Base.PkgId(Base.UUID("9abbd945-dff8-562f-b5e8-e1ebf5ef1b79"), "Profile"), - nothing) -if Profile !== nothing - repl_script = Profile.precompile_script * repl_script # do larger workloads first for better parallelization - hardcoded_precompile_statements *= """ - precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, Int, UInt}) - precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, Int, UnitRange{UInt}}) - precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, UnitRange{Int}, UInt}) - precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, UnitRange{Int}, UnitRange{UInt}}) - precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, Vector{Int}, Vector{UInt}}) - """ -end - const JULIA_PROMPT = "julia> " const PKG_PROMPT = "pkg> " const SHELL_PROMPT = "shell> " diff --git a/pkgimage.mk b/pkgimage.mk index d3a0238dd035c..8d4d522f224d3 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -65,6 +65,7 @@ $(eval $(call sysimg_builder,SHA,)) $(eval $(call sysimg_builder,Serialization,)) $(eval $(call sysimg_builder,Sockets,)) $(eval $(call sysimg_builder,Unicode,)) +$(eval $(call pkgimg_builder,Profile,)) # 1-depth packages $(eval $(call pkgimg_builder,GMP_jll,Artifacts Libdl)) @@ -96,10 +97,8 @@ $(eval $(call sysimg_builder,Distributed,Random Serialization Sockets)) $(eval $(call sysimg_builder,Future,Random)) $(eval $(call sysimg_builder,InteractiveUtils,Markdown)) $(eval $(call sysimg_builder,LibGit2,NetworkOptions Printf SHA Base64)) -$(eval $(call sysimg_builder,Profile,Printf)) $(eval $(call sysimg_builder,UUIDs,Random SHA)) - # 3-depth packages # LibGit2_jll $(eval $(call pkgimg_builder,LibCURL_jll,LibSSH2_jll nghttp2_jll MbedTLS_jll Zlib_jll Artifacts Libdl)) diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index 518dc54c7f757..4bce0c4fecd88 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -31,26 +31,6 @@ macro profile(ex) end end -# triggers printing the report and (optionally) saving a heap snapshot after a SIGINFO/SIGUSR1 profile request -const PROFILE_PRINT_COND = Ref{Base.AsyncCondition}() -function profile_printing_listener() - try - while true - wait(PROFILE_PRINT_COND[]) - peek_report[]() - if Base.get_bool_env("JULIA_PROFILE_PEEK_HEAP_SNAPSHOT", false) === true - println(stderr, "Saving heap snapshot...") - fname = take_heap_snapshot() - println(stderr, "Heap snapshot saved to `$(fname)`") - end - end - catch ex - if !isa(ex, InterruptException) - @error "Profile printing listener crashed" exception=ex,catch_backtrace() - end - end -end - # An internal function called to show the report after an information request (SIGINFO or SIGUSR1). function _peek_report() iob = IOBuffer() @@ -74,12 +54,7 @@ Set the duration in seconds of the profile "peek" that is triggered via `SIGINFO """ set_peek_duration(t::Float64) = ccall(:jl_set_profile_peek_duration, Cvoid, (Float64,), t) -precompile_script = """ -import Profile -Profile.@profile while Profile.len_data() < 1000; rand(10,10) * rand(10,10); end -Profile.peek_report[]() -Profile.clear() -""" + #### #### User-level functions @@ -150,20 +125,6 @@ function check_init() end end -function __init__() - # Note: The profile buffer is no longer initialized during __init__ because Profile is in the sysimage, - # thus __init__ is called every startup. The buffer is lazily initialized the first time `@profile` is - # used, if not manually initialized before that. - @static if !Sys.iswindows() - # triggering a profile via signals is not implemented on windows - cond = Base.AsyncCondition() - Base.uv_unref(cond.handle) - PROFILE_PRINT_COND[] = cond - ccall(:jl_set_peek_cond, Cvoid, (Ptr{Cvoid},), PROFILE_PRINT_COND[].handle) - errormonitor(Threads.@spawn(profile_printing_listener())) - end -end - """ clear() @@ -1267,5 +1228,6 @@ end include("Allocs.jl") +include("precompile.jl") end # module diff --git a/stdlib/Profile/src/precompile.jl b/stdlib/Profile/src/precompile.jl new file mode 100644 index 0000000000000..7817cdd2fba79 --- /dev/null +++ b/stdlib/Profile/src/precompile.jl @@ -0,0 +1,6 @@ +precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, Int, UInt}) +precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, Int, UnitRange{UInt}}) +precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, UnitRange{Int}, UInt}) +precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, UnitRange{Int}, UnitRange{UInt}}) +precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, Vector{Int}, Vector{UInt}}) +precompile(Tuple{typeof(Profile._peek_report)}) diff --git a/test/precompile.jl b/test/precompile.jl index 50dfe329d3db4..9ca3d26864346 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -398,7 +398,7 @@ precompile_test_harness(false) do dir :Distributed, :Downloads, :FileWatching, :Future, :InteractiveUtils, :libblastrampoline_jll, :LazyArtifacts, :LibCURL, :LibCURL_jll, :LibGit2, :Libdl, :LinearAlgebra, :Logging, :Markdown, :Mmap, :MozillaCACerts_jll, :NetworkOptions, :OpenBLAS_jll, :Pkg, :Printf, - :Profile, :p7zip_jll, :REPL, :Random, :SHA, :Serialization, :SharedArrays, :Sockets, + :p7zip_jll, :REPL, :Random, :SHA, :Serialization, :SharedArrays, :Sockets, :TOML, :Tar, :Test, :UUIDs, :Unicode, :nghttp2_jll] ), From 3f0f8d970beddd14494f7316a05f24e2be2489f6 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 4 Apr 2023 17:19:54 +0900 Subject: [PATCH 433/775] effects: improve effect analysis for `isdefined` call (#49226) When applied to non-immutable object, `isdefined_effects` should taint `:consistent`-cy by `CONSISTENT_IF_INACCESSIBLEMEMONLY` instead of `ALWAYS_FALSE`. This commit also fixes up its `inaccessiblememonly` modeling. --- base/compiler/tfuncs.jl | 61 +++++++++++++++++++++++++++++----------- test/compiler/effects.jl | 49 ++++++++++++++++++++++++++++---- 2 files changed, 89 insertions(+), 21 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 84794a27ec034..4dfb29aca602b 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -345,8 +345,17 @@ end end add_tfunc(===, 2, 2, egal_tfunc, 1) +function isdefined_nothrow(𝕃::AbstractLattice, argtypes::Vector{Any}) + if length(argtypes) ≠ 2 + # TODO prove nothrow when ordering is specified + return false + end + return isdefined_nothrow(𝕃, argtypes[1], argtypes[2]) +end @nospecs function isdefined_nothrow(𝕃::AbstractLattice, x, name) ⊑ = Core.Compiler.:⊑(𝕃) + isvarargtype(x) && return false + isvarargtype(name) && return false if hasintersect(widenconst(x), Module) return name ⊑ Symbol else @@ -951,17 +960,13 @@ function getfield_nothrow(𝕃::AbstractLattice, arginfo::ArgInfo, boundscheck:: elseif length(argtypes) != 3 return false end - isvarargtype(ordering) && return false - widenconst(ordering) === Symbol || return false - if isa(ordering, Const) - ordering = ordering.val::Symbol - if ordering !== :not_atomic # TODO: this is assuming not atomic - return false - end - return getfield_nothrow(𝕃, argtypes[2], argtypes[3], !(boundscheck === :off)) - else + isa(ordering, Const) || return false + ordering = ordering.val + isa(ordering, Symbol) || return false + if ordering !== :not_atomic # TODO: this is assuming not atomic return false end + return getfield_nothrow(𝕃, argtypes[2], argtypes[3], !(boundscheck === :off)) end @nospecs function getfield_nothrow(𝕃::AbstractLattice, s00, name, boundscheck::Bool) # If we don't have boundscheck off and don't know the field, don't even bother @@ -2092,8 +2097,7 @@ end elseif f === UnionAll return na == 2 && (argtypes[1] ⊑ TypeVar && argtypes[2] ⊑ Type) elseif f === isdefined - na == 2 || return false - return isdefined_nothrow(𝕃, argtypes[1], argtypes[2]) + return isdefined_nothrow(𝕃, argtypes) elseif f === Core.sizeof na == 1 || return false return sizeof_nothrow(argtypes[1]) @@ -2239,11 +2243,36 @@ const _SPECIAL_BUILTINS = Any[ function isdefined_effects(𝕃::AbstractLattice, argtypes::Vector{Any}) # consistent if the first arg is immutable na = length(argtypes) - na == 0 && return EFFECTS_THROWS - obj = argtypes[1] - consistent = is_immutable_argtype(unwrapva(obj)) ? ALWAYS_TRUE : ALWAYS_FALSE - nothrow = !isvarargtype(argtypes[end]) && na == 2 && isdefined_nothrow(𝕃, obj, argtypes[2]) - return Effects(EFFECTS_TOTAL; consistent, nothrow) + 2 ≤ na ≤ 3 || return EFFECTS_THROWS + obj, sym = argtypes + wobj = unwrapva(obj) + consistent = CONSISTENT_IF_INACCESSIBLEMEMONLY + if is_immutable_argtype(wobj) + consistent = ALWAYS_TRUE + else + # Bindings/fields are not allowed to transition from defined to undefined, so even + # if the object is not immutable, we can prove `:consistent`-cy if it is defined: + if isa(wobj, Const) && isa(sym, Const) + objval = wobj.val + symval = sym.val + if isa(objval, Module) + if isa(symval, Symbol) && isdefined(objval, symval) + consistent = ALWAYS_TRUE + end + elseif (isa(symval, Symbol) || isa(symval, Int)) && isdefined(objval, symval) + consistent = ALWAYS_TRUE + end + end + end + nothrow = isdefined_nothrow(𝕃, argtypes) + if hasintersect(widenconst(wobj), Module) + inaccessiblememonly = ALWAYS_FALSE + elseif is_mutation_free_argtype(wobj) + inaccessiblememonly = ALWAYS_TRUE + else + inaccessiblememonly = INACCESSIBLEMEM_OR_ARGMEMONLY + end + return Effects(EFFECTS_TOTAL; consistent, nothrow, inaccessiblememonly) end function getfield_effects(𝕃::AbstractLattice, arginfo::ArgInfo, @nospecialize(rt)) diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index eb3df4ba9272e..ee1d5dc569702 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -564,7 +564,7 @@ end |> !Core.Compiler.is_inaccessiblememonly end |> !Core.Compiler.is_inaccessiblememonly # the `:inaccessiblememonly` helper effect allows us to prove `:consistent`-cy of frames -# including `getfield` accessing to local mutable object +# including `getfield` / `isdefined` accessing to local mutable object mutable struct SafeRef{T} x::T @@ -593,13 +593,11 @@ const consistent_global = Some(:foo) @test Base.infer_effects() do consistent_global.value end |> Core.Compiler.is_consistent - const inconsistent_global = SafeRef(:foo) @test Base.infer_effects() do inconsistent_global[] end |> !Core.Compiler.is_consistent - -global inconsistent_condition_ref = Ref{Bool}(false) +const inconsistent_condition_ref = Ref{Bool}(false) @test Base.infer_effects() do if inconsistent_condition_ref[] return 0 @@ -918,7 +916,6 @@ gotoifnot_throw_check_48583(x) = x ? x : 0 @test !Core.Compiler.is_nothrow(Base.infer_effects(gotoifnot_throw_check_48583, (Any,))) @test Core.Compiler.is_nothrow(Base.infer_effects(gotoifnot_throw_check_48583, (Bool,))) - # unknown :static_parameter should taint :nothrow # https://github.com/JuliaLang/julia/issues/46771 unknown_sparam_throw(::Union{Nothing, Type{T}}) where T = (T; nothing) @@ -943,3 +940,45 @@ actually_recursive1(x) = actually_recursive2(x) actually_recursive2(x) = (x <= 0) ? 1 : actually_recursive1(x - 1) actually_recursive3(x) = actually_recursive2(x) @test !Core.Compiler.is_terminates(Base.infer_effects(actually_recursive3, (Int,))) + +# `isdefined` effects +struct MaybeSome{T} + value::T + MaybeSome(x::T) where T = new{T}(x) + MaybeSome{T}(x::T) where T = new{T}(x) + MaybeSome{T}() where T = new{T}() +end +const undefined_ref = Ref{String}() +const defined_ref = Ref{String}("julia") +const undefined_some = MaybeSome{String}() +const defined_some = MaybeSome{String}("julia") +let effects = Base.infer_effects() do + isdefined(undefined_ref, :x) + end + @test !Core.Compiler.is_consistent(effects) + @test Core.Compiler.is_nothrow(effects) +end +let effects = Base.infer_effects() do + isdefined(defined_ref, :x) + end + @test Core.Compiler.is_consistent(effects) + @test Core.Compiler.is_nothrow(effects) +end +let effects = Base.infer_effects() do + isdefined(undefined_some, :value) + end + @test Core.Compiler.is_consistent(effects) + @test Core.Compiler.is_nothrow(effects) +end +let effects = Base.infer_effects() do + isdefined(defined_some, :value) + end + @test Core.Compiler.is_consistent(effects) + @test Core.Compiler.is_nothrow(effects) +end +# high-level interface test +isassigned_effects(s) = isassigned(Ref(s)) +@test Core.Compiler.is_consistent(Base.infer_effects(isassigned_effects, (Symbol,))) +@test fully_eliminated(; retval=true) do + isassigned_effects(:foo) +end From 50f50a7e90a4cc429c632e41219d013b96316dc8 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 4 Apr 2023 08:52:20 -0300 Subject: [PATCH 434/775] Make mark_obj32 like the 16 and 8 bit function by (#49240) pushing the parent into the remset --- src/gc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gc.c b/src/gc.c index abe9169137b55..158a03be017fa 100644 --- a/src/gc.c +++ b/src/gc.c @@ -2070,6 +2070,7 @@ STATIC_INLINE jl_value_t *gc_mark_obj32(jl_ptls_t ptls, char *obj32_parent, uint gc_heap_snapshot_record_object_edge((jl_value_t*)obj32_parent, slot); } } + gc_mark_push_remset(ptls, (jl_value_t *)obj32_parent, nptr); return new_obj; } From 822d9b832bf63c65681dcae0a576939d3bbedbfc Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 4 Apr 2023 09:28:28 -0400 Subject: [PATCH 435/775] Do not create sigwait() thread when JL_OPTIONS_HANDLE_SIGNALS_OFF (#48957) We already disable part of the signal-handling machinery, but it seems that this piece was missed for an unknown reason. This is important to disable so that, e.g. `sigwait()` can be received reliably by the main application (which may even be another copy of Julia). --- src/init.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/init.c b/src/init.c index 0651d3b274f24..5990bd24aaabd 100644 --- a/src/init.c +++ b/src/init.c @@ -754,7 +754,9 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) jl_init_uv(); init_stdio(); restore_fp_env(); - restore_signals(); + if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON) + restore_signals(); + jl_init_intrinsic_properties(); jl_prep_sanitizers(); From 93df7e28c12a171b0a579f5e7517429def7c1d3b Mon Sep 17 00:00:00 2001 From: Benjamin Lorenz Date: Tue, 4 Apr 2023 16:32:51 +0200 Subject: [PATCH 436/775] (re-)allow include_dependency(directory) (#49244) same for symlinks, adjust docs accordingly and clarify that it refers to the mtime --- base/loading.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 4c5a7df320dbe..a20f0e67ee1e5 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1543,11 +1543,11 @@ end """ include_dependency(path::AbstractString) -In a module, declare that the file specified by `path` (relative or absolute) is a -dependency for precompilation; that is, the module will need to be recompiled if this file -changes. +In a module, declare that the file, directory, or symbolic link specified by `path` +(relative or absolute) is a dependency for precompilation; that is, the module will need +to be recompiled if the modification time of `path` changes. -This is only needed if your module depends on a file that is not used via [`include`](@ref). It has +This is only needed if your module depends on a path that is not used via [`include`](@ref). It has no effect outside of compilation. """ function include_dependency(path::AbstractString) @@ -2822,7 +2822,7 @@ end end for chi in includes f, ftime_req = chi.filename, chi.mtime - if !isfile(f) + if !ispath(f) _f = fixup_stdlib_path(f) if isfile(_f) && startswith(_f, Sys.STDLIB) # mtime is changed by extraction From 96812bdedd2de3b5bba5ed9f63bcf3d867d68ca1 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 4 Apr 2023 16:34:39 -0400 Subject: [PATCH 437/775] subtype: allocate space for small environments on the stack (#49215) For comparisons of expressions with less than about 4-8 variables, allocate all of the save space on the stack instead of a temporary short-lived svec. --- src/subtype.c | 304 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 199 insertions(+), 105 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 69d0892403c43..a6a2a7094c511 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -154,77 +154,141 @@ static void statestack_set(jl_unionstate_t *st, int i, int val) JL_NOTSAFEPOINT memcpy(&(dst)->stack, (saved)->stack, ((saved)->used+7)/8); \ } while (0); +static int current_env_length(jl_stenv_t *e) +{ + jl_varbinding_t *v = e->vars; + int len = 0; + while (v) { + len++; + v = v->prev; + } + return len; +} + typedef struct { int8_t *buf; int rdepth; - int8_t _space[24]; + int8_t _space[24]; // == 8 * 3 + jl_gcframe_t gcframe; + jl_value_t *roots[24]; } jl_savedenv_t; -static void save_env(jl_stenv_t *e, jl_value_t **root, jl_savedenv_t *se) +static void re_save_env(jl_stenv_t *e, jl_savedenv_t *se, int root) { - jl_varbinding_t *v = e->vars; - int len=0; - while (v != NULL) { - len++; - v = v->prev; + jl_value_t **roots = NULL; + int nroots = 0; + if (root) { + if (se->gcframe.nroots == JL_GC_ENCODE_PUSHARGS(1)) { + jl_svec_t *sv = (jl_svec_t*)se->roots[0]; + assert(jl_is_svec(sv)); + roots = jl_svec_data(sv); + nroots = jl_svec_len(sv); + } + else { + roots = se->roots; + nroots = se->gcframe.nroots >> 2; + } } - if (root) - *root = (jl_value_t*)jl_alloc_svec(len * 3); - se->buf = (int8_t*)(len > 8 ? malloc_s(len * 3) : &se->_space); -#ifdef __clang_gcanalyzer__ - memset(se->buf, 0, len * 3); -#endif - int i=0, j=0; v = e->vars; + jl_varbinding_t *v = e->vars; + int i = 0, j = 0; while (v != NULL) { if (root) { - jl_svecset(*root, i++, v->lb); - jl_svecset(*root, i++, v->ub); - jl_svecset(*root, i++, (jl_value_t*)v->innervars); + roots[i++] = v->lb; + roots[i++] = v->ub; + roots[i++] = (jl_value_t*)v->innervars; } se->buf[j++] = v->occurs; se->buf[j++] = v->occurs_inv; se->buf[j++] = v->occurs_cov; v = v->prev; } + assert(i == nroots); (void)nroots; se->rdepth = e->Runions.depth; } +static void alloc_env(jl_stenv_t *e, jl_savedenv_t *se, int root) +{ + jl_task_t *ct = jl_current_task; + int len = current_env_length(e); + se->gcframe.nroots = 0; + se->gcframe.prev = NULL; + se->roots[0] = NULL; + if (len > 8) { + if (root) { + se->gcframe.nroots = JL_GC_ENCODE_PUSHARGS(1); + se->gcframe.prev = ct->gcstack; + ct->gcstack = &se->gcframe; + jl_svec_t *sv = jl_alloc_svec(len * 3); + se->roots[0] = (jl_value_t*)sv; + } + } + else { + if (root && len) { + for (int i = 0; i < len * 3; i++) + se->roots[i] = NULL; + se->gcframe.nroots = JL_GC_ENCODE_PUSHARGS(len * 3); + se->gcframe.prev = ct->gcstack; + ct->gcstack = &se->gcframe; + } + } + se->buf = (len > 8 ? (int8_t*)malloc_s(len * 3) : se->_space); +#ifdef __clang_gcanalyzer__ + memset(se->buf, 0, len * 3); +#endif +} + +static void save_env(jl_stenv_t *e, jl_savedenv_t *se, int root) +{ + alloc_env(e, se, root); + re_save_env(e, se, root); +} + static void free_env(jl_savedenv_t *se) JL_NOTSAFEPOINT { + if (se->gcframe.nroots) { + assert(jl_current_task->gcstack == &se->gcframe); + JL_GC_POP(); + } if (se->buf != se->_space) free(se->buf); se->buf = NULL; } -static void restore_env(jl_stenv_t *e, jl_value_t *root, jl_savedenv_t *se) JL_NOTSAFEPOINT +static void restore_env(jl_stenv_t *e, jl_savedenv_t *se, int root) JL_NOTSAFEPOINT { + jl_value_t **roots = NULL; + int nroots = 0; + if (root) { + if (se->gcframe.nroots == JL_GC_ENCODE_PUSHARGS(1)) { + jl_svec_t *sv = (jl_svec_t*)se->roots[0]; + assert(jl_is_svec(sv)); + roots = jl_svec_data(sv); + nroots = jl_svec_len(sv); + } + else { + roots = se->roots; + nroots = se->gcframe.nroots >> 2; + } + } jl_varbinding_t *v = e->vars; int i = 0, j = 0; while (v != NULL) { - if (root) v->lb = jl_svecref(root, i++); - if (root) v->ub = jl_svecref(root, i++); - if (root) v->innervars = (jl_array_t*)jl_svecref(root, i++); + if (root) { + v->lb = roots[i++]; + v->ub = roots[i++]; + v->innervars = (jl_array_t*)roots[i++]; + } v->occurs = se->buf[j++]; v->occurs_inv = se->buf[j++]; v->occurs_cov = se->buf[j++]; v = v->prev; } + assert(i == nroots); (void)nroots; e->Runions.depth = se->rdepth; if (e->envout && e->envidx < e->envsz) memset(&e->envout[e->envidx], 0, (e->envsz - e->envidx)*sizeof(void*)); } -static int current_env_length(jl_stenv_t *e) -{ - jl_varbinding_t *v = e->vars; - int len = 0; - while (v) { - len++; - v = v->prev; - } - return len; -} - static void clean_occurs(jl_stenv_t *e) { jl_varbinding_t *v = e->vars; @@ -1577,7 +1641,7 @@ static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) return sub; } -static int exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, jl_value_t *saved, jl_savedenv_t *se, int param) +static int exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, jl_savedenv_t *se, int param) { e->Runions.used = 0; while (1) { @@ -1591,11 +1655,11 @@ static int exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, jl_value_ // We preserve `envout` here as `subtype_unionall` needs previous assigned env values. int oldidx = e->envidx; e->envidx = e->envsz; - restore_env(e, saved, se); + restore_env(e, se, 1); e->envidx = oldidx; } else { - restore_env(e, saved, se); + restore_env(e, se, 1); return 0; } } @@ -1608,26 +1672,23 @@ static int _forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, i // ∃₁ assert(e->Runions.depth == 0); assert(e->Lunions.depth == 0); - jl_value_t *saved=NULL; jl_savedenv_t se; - JL_GC_PUSH1(&saved); - save_env(e, &saved, &se); + jl_savedenv_t se; + save_env(e, &se, 1); e->Lunions.used = 0; int sub; if (count) *count = 0; if (noRmore) *noRmore = 1; while (1) { - sub = exists_subtype(x, y, e, saved, &se, param); + sub = exists_subtype(x, y, e, &se, param); if (count) *count = (*count < 4) ? *count + 1 : 4; if (noRmore) *noRmore = *noRmore && e->Runions.more == 0; if (!sub || !next_union_state(e, 0)) break; - free_env(&se); - save_env(e, &saved, &se); + re_save_env(e, &se, 1); } free_env(&se); - JL_GC_POP(); return sub; } @@ -2449,13 +2510,11 @@ static int try_subtype_in_env(jl_value_t *a, jl_value_t *b, jl_stenv_t *e) { if (a == jl_bottom_type || b == (jl_value_t *)jl_any_type || try_subtype_by_bounds(a, b, e)) return 1; - jl_value_t *root=NULL; jl_savedenv_t se; - JL_GC_PUSH1(&root); - save_env(e, &root, &se); + jl_savedenv_t se; + save_env(e, &se, 1); int ret = subtype_in_env(a, b, e); - restore_env(e, root, &se); + restore_env(e, &se, 1); free_env(&se); - JL_GC_POP(); return ret; } @@ -2559,17 +2618,17 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int return R ? intersect(a, bb->lb, e, param) : intersect(bb->lb, a, e, param); if (!jl_is_type(a) && !jl_is_typevar(a)) return set_var_to_const(bb, a, e, R); - jl_value_t *root=NULL; jl_savedenv_t se; + jl_savedenv_t se; if (param == 2) { jl_value_t *ub = NULL; - JL_GC_PUSH2(&ub, &root); + JL_GC_PUSH1(&ub); if (!jl_has_free_typevars(a)) { - save_env(e, &root, &se); + save_env(e, &se, 1); int issub = subtype_in_env_existential(bb->lb, a, e); - restore_env(e, root, &se); + restore_env(e, &se, 1); if (issub) { issub = subtype_in_env_existential(a, bb->ub, e); - restore_env(e, root, &se); + restore_env(e, &se, 1); } free_env(&se); if (!issub) { @@ -2582,9 +2641,9 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int e->triangular++; ub = R ? intersect_aside(a, bb->ub, e, bb->depth0) : intersect_aside(bb->ub, a, e, bb->depth0); e->triangular--; - save_env(e, &root, &se); + save_env(e, &se, 1); int issub = subtype_in_env_existential(bb->lb, ub, e); - restore_env(e, root, &se); + restore_env(e, &se, 1); free_env(&se); if (!issub) { JL_GC_POP(); @@ -2942,12 +3001,12 @@ static jl_value_t *intersect_unionall_(jl_value_t *t, jl_unionall_t *u, jl_stenv static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8_t R, int param) { - jl_value_t *res=NULL, *save=NULL; + jl_value_t *res = NULL; jl_savedenv_t se; jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, 0, 0, 0, e->invdepth, NULL, e->vars }; - JL_GC_PUSH5(&res, &vb.lb, &vb.ub, &save, &vb.innervars); - save_env(e, &save, &se); + JL_GC_PUSH4(&res, &vb.lb, &vb.ub, &vb.innervars); + save_env(e, &se, 1); res = intersect_unionall_(t, u, e, R, param, &vb); if (vb.limited) { // if the environment got too big, avoid tree recursion and propagate the flag @@ -2962,7 +3021,7 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_ else if (vb.occurs_cov && !var_occurs_invariant(u->body, u->var, 0)) vb.constraintkind = 1; if (vb.constraintkind) { - restore_env(e, vb.constraintkind == 1 ? save : NULL, &se); + restore_env(e, &se, vb.constraintkind == 1 ? 1 : 0); vb.occurs = vb.occurs_cov = vb.occurs_inv = 0; res = intersect_unionall_(t, u, e, R, param, &vb); } @@ -3169,20 +3228,19 @@ static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t flip_vars(e); flip_offset(e); return jl_bottom_type; } - jl_value_t *root=NULL; jl_savedenv_t se; - JL_GC_PUSH2(&ii, &root); - save_env(e, &root, &se); + JL_GC_PUSH1(&ii); + save_env(e, &se, 1); if (!subtype_in_env_existential(x, y, e)) ii = NULL; else { - restore_env(e, root, &se); + restore_env(e, &se, 1); flip_offset(e); if (!subtype_in_env_existential(y, x, e)) ii = NULL; flip_offset(e); } - restore_env(e, root, &se); + restore_env(e, &se, 1); free_env(&se); JL_GC_POP(); return ii; @@ -3434,9 +3492,9 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa if (jl_is_unionall(x)) { if (jl_is_unionall(y)) { jl_value_t *a=NULL, *b=jl_bottom_type, *res=NULL; - JL_GC_PUSH2(&a,&b); + JL_GC_PUSH2(&a, &b); jl_savedenv_t se; - save_env(e, NULL, &se); + save_env(e, &se, 0); a = intersect_unionall(y, (jl_unionall_t*)x, e, 0, param); if (jl_is_unionall(a)) { jl_unionall_t *ua = (jl_unionall_t*)a; @@ -3444,7 +3502,7 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa jl_unionall_t *ub = (jl_unionall_t*)ua->body; if (jl_has_typevar(ub->var->ub, ua->var) || jl_has_typevar(ub->var->lb, ua->var)) { - restore_env(e, NULL, &se); // restore counts + restore_env(e, &se, 0); // restore counts b = intersect_unionall(x, (jl_unionall_t*)y, e, 1, param); } } @@ -3517,35 +3575,54 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa return jl_bottom_type; } -static int merge_env(jl_stenv_t *e, jl_value_t **root, jl_savedenv_t *se, int count) +static int merge_env(jl_stenv_t *e, jl_savedenv_t *se, int count) { - if (count == 0) { - int len = current_env_length(e); - *root = (jl_value_t*)jl_alloc_svec(len * 3); - se->buf = (int8_t*)(len > 8 ? malloc_s(len * 3) : &se->_space); - memset(se->buf, 0, len * 3); + if (count == 0) + alloc_env(e, se, 1); + jl_value_t **roots = NULL; + int nroots = 0; + if (se->gcframe.nroots == JL_GC_ENCODE_PUSHARGS(1)) { + jl_svec_t *sv = (jl_svec_t*)se->roots[0]; + assert(jl_is_svec(sv)); + roots = jl_svec_data(sv); + nroots = jl_svec_len(sv); + } + else { + roots = se->roots; + nroots = se->gcframe.nroots >> 2; } int n = 0; jl_varbinding_t *v = e->vars; - jl_value_t *b1 = NULL, *b2 = NULL; - JL_GC_PUSH2(&b1, &b2); // clang-sagc does not understand that *root is rooted already v = e->vars; while (v != NULL) { + if (count == 0) { + // need to initialize this + se->buf[n] = 0; + se->buf[n+1] = 0; + se->buf[n+2] = 0; + } if (v->occurs) { // only merge lb/ub/innervars if this var occurs. - b1 = jl_svecref(*root, n); + jl_value_t *b1, *b2; + b1 = roots[n]; + JL_GC_PROMISE_ROOTED(b1); // clang-sagc doesn't know this came from our GC frame b2 = v->lb; - jl_svecset(*root, n, b1 ? simple_meet(b1, b2, 0) : b2); - b1 = jl_svecref(*root, n+1); + JL_GC_PROMISE_ROOTED(b2); // clang-sagc doesn't know the fields of this are stack GC roots + roots[n] = b1 ? simple_meet(b1, b2, 0) : b2; + b1 = roots[n+1]; + JL_GC_PROMISE_ROOTED(b1); // clang-sagc doesn't know this came from our GC frame b2 = v->ub; - jl_svecset(*root, n+1, b1 ? simple_join(b1, b2) : b2); - b1 = jl_svecref(*root, n+2); + JL_GC_PROMISE_ROOTED(b2); // clang-sagc doesn't know the fields of this are stack GC roots + roots[n+1] = b1 ? simple_join(b1, b2) : b2; + b1 = roots[n+2]; + JL_GC_PROMISE_ROOTED(b1); // clang-sagc doesn't know this came from our GC frame b2 = (jl_value_t*)v->innervars; + JL_GC_PROMISE_ROOTED(b2); // clang-sagc doesn't know the fields of this are stack GC roots if (b2 && b1 != b2) { if (b1) jl_array_ptr_1d_append((jl_array_t*)b1, (jl_array_t*)b2); else - jl_svecset(*root, n+2, b2); + roots[n+2] = b2; } // record the meeted vars. se->buf[n] = 1; @@ -3558,33 +3635,52 @@ static int merge_env(jl_stenv_t *e, jl_value_t **root, jl_savedenv_t *se, int co n = n + 3; v = v->prev; } - JL_GC_POP(); + assert(n == nroots); (void)nroots; return count + 1; } // merge untouched vars' info. -static void final_merge_env(jl_value_t **merged, jl_savedenv_t *me, jl_value_t **saved, jl_savedenv_t *se) +static void final_merge_env(jl_stenv_t *e, jl_savedenv_t *me, jl_savedenv_t *se) { - int l = jl_svec_len(*merged); - assert(l == jl_svec_len(*saved) && l%3 == 0); - jl_value_t *b1 = NULL, *b2 = NULL; - JL_GC_PUSH2(&b1, &b2); - for (int n = 0; n < l; n = n + 3) { - if (jl_svecref(*merged, n) == NULL) - jl_svecset(*merged, n, jl_svecref(*saved, n)); - if (jl_svecref(*merged, n+1) == NULL) - jl_svecset(*merged, n+1, jl_svecref(*saved, n+1)); - b1 = jl_svecref(*merged, n+2); - b2 = jl_svecref(*saved , n+2); + jl_value_t **merged = NULL; + jl_value_t **saved = NULL; + int nroots = 0; + assert(se->gcframe.nroots == me->gcframe.nroots); + if (se->gcframe.nroots == JL_GC_ENCODE_PUSHARGS(1)) { + jl_svec_t *sv = (jl_svec_t*)se->roots[0]; + assert(jl_is_svec(sv)); + saved = jl_svec_data(sv); + nroots = jl_svec_len(sv); + sv = (jl_svec_t*)me->roots[0]; + assert(jl_is_svec(sv)); + merged = jl_svec_data(sv); + assert(nroots == jl_svec_len(sv)); + } + else { + saved = se->roots; + merged = me->roots; + nroots = se->gcframe.nroots >> 2; + } + assert(nroots == current_env_length(e) * 3); + assert(nroots % 3 == 0); + for (int n = 0; n < nroots; n = n + 3) { + if (merged[n] == NULL) + merged[n] = saved[n]; + if (merged[n+1] == NULL) + merged[n+1] = saved[n+1]; + jl_value_t *b1, *b2; + b1 = merged[n+2]; + JL_GC_PROMISE_ROOTED(b1); // clang-sagc doesn't know this came from our GC frame + b2 = saved[n+2]; + JL_GC_PROMISE_ROOTED(b2); // clang-sagc doesn't know this came from our GC frame if (b2 && b1 != b2) { if (b1) jl_array_ptr_1d_append((jl_array_t*)b1, (jl_array_t*)b2); else - jl_svecset(*merged, n+2, b2); + merged[n+2] = b2; } me->buf[n] |= se->buf[n]; } - JL_GC_POP(); } static void expand_local_env(jl_stenv_t *e, jl_value_t *res) @@ -3616,19 +3712,17 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) e->Runions.more = 0; e->Runions.used = 0; jl_value_t **is; - JL_GC_PUSHARGS(is, 4); - jl_value_t **saved = &is[2]; - jl_value_t **merged = &is[3]; + JL_GC_PUSHARGS(is, 2); jl_savedenv_t se, me; - save_env(e, saved, &se); + save_env(e, &se, 1); int niter = 0, total_iter = 0; clean_occurs(e); is[0] = intersect(x, y, e, 0); // root if (is[0] != jl_bottom_type) { expand_local_env(e, is[0]); - niter = merge_env(e, merged, &me, niter); + niter = merge_env(e, &me, niter); } - restore_env(e, *saved, &se); + restore_env(e, &se, 1); while (next_union_state(e, 1)) { if (e->emptiness_only && is[0] != jl_bottom_type) break; @@ -3639,9 +3733,9 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) is[1] = intersect(x, y, e, 0); if (is[1] != jl_bottom_type) { expand_local_env(e, is[1]); - niter = merge_env(e, merged, &me, niter); + niter = merge_env(e, &me, niter); } - restore_env(e, *saved, &se); + restore_env(e, &se, 1); if (is[0] == jl_bottom_type) is[0] = is[1]; else if (is[1] != jl_bottom_type) { @@ -3655,8 +3749,8 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) } } if (niter) { - final_merge_env(merged, &me, saved, &se); - restore_env(e, *merged, &me); + final_merge_env(e, &me, &se); + restore_env(e, &me, 1); free_env(&me); } free_env(&se); From 4134e931e556ef392063bfd47d580c3ca8f15758 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 4 Apr 2023 22:28:02 -0400 Subject: [PATCH 438/775] use Pkg.precompile during loading via Pkg hook (#49242) --- base/loading.jl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/base/loading.jl b/base/loading.jl index a20f0e67ee1e5..b82028216663b 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1758,6 +1758,9 @@ function set_pkgorigin_version_path(pkg::PkgId, path::Union{String,Nothing}) nothing end +# A hook to allow code load to use Pkg.precompile +const PKG_PRECOMPILE_HOOK = Ref{Function}() + # Returns `nothing` or the new(ish) module function _require(pkg::PkgId, env=nothing) assert_havelock(require_lock) @@ -1777,8 +1780,11 @@ function _require(pkg::PkgId, env=nothing) end set_pkgorigin_version_path(pkg, path) + pkg_precompile_attempted = false # being safe to avoid getting stuck in a Pkg.precompile loop + # attempt to load the module file via the precompile cache locations if JLOptions().use_compiled_modules != 0 + @label load_from_cache m = _require_search_from_serialized(pkg, path, UInt128(0)) if m isa Module return m @@ -1800,6 +1806,16 @@ function _require(pkg::PkgId, env=nothing) if JLOptions().use_compiled_modules != 0 if (0 == ccall(:jl_generating_output, Cint, ())) || (JLOptions().incremental != 0) + if !pkg_precompile_attempted && isassigned(PKG_PRECOMPILE_HOOK) + pkg_precompile_attempted = true + unlock(require_lock) + try + PKG_PRECOMPILE_HOOK[](pkg.name, _from_loading = true) + finally + lock(require_lock) + end + @goto load_from_cache + end # spawn off a new incremental pre-compile task for recursive `require` calls cachefile = compilecache(pkg, path) if isa(cachefile, Exception) From 80c2ec4d393a74012c9c81bf2da3c57706ad38f7 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 5 Apr 2023 08:52:45 +0200 Subject: [PATCH 439/775] move out SharedArrays from the sysimage (#49131) --- base/sysimg.jl | 1 - pkgimage.mk | 2 +- test/precompile.jl | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/base/sysimg.jl b/base/sysimg.jl index d64b463e9a957..168138531eaff 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -64,7 +64,6 @@ let # 3-depth packages :REPL, - :SharedArrays, :TOML, :Test, diff --git a/pkgimage.mk b/pkgimage.mk index 8d4d522f224d3..a18923450950f 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -103,7 +103,7 @@ $(eval $(call sysimg_builder,UUIDs,Random SHA)) # LibGit2_jll $(eval $(call pkgimg_builder,LibCURL_jll,LibSSH2_jll nghttp2_jll MbedTLS_jll Zlib_jll Artifacts Libdl)) $(eval $(call sysimg_builder,REPL,InteractiveUtils Markdown Sockets Unicode)) -$(eval $(call sysimg_builder,SharedArrays,Distributed Mmap Random Serialization)) +$(eval $(call pkgimg_builder,SharedArrays,Distributed Mmap Random Serialization)) $(eval $(call sysimg_builder,TOML,Dates)) $(eval $(call sysimg_builder,Test,Logging Random Serialization InteractiveUtils)) diff --git a/test/precompile.jl b/test/precompile.jl index 9ca3d26864346..b1b929c9e043b 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -398,7 +398,7 @@ precompile_test_harness(false) do dir :Distributed, :Downloads, :FileWatching, :Future, :InteractiveUtils, :libblastrampoline_jll, :LazyArtifacts, :LibCURL, :LibCURL_jll, :LibGit2, :Libdl, :LinearAlgebra, :Logging, :Markdown, :Mmap, :MozillaCACerts_jll, :NetworkOptions, :OpenBLAS_jll, :Pkg, :Printf, - :p7zip_jll, :REPL, :Random, :SHA, :Serialization, :SharedArrays, :Sockets, + :p7zip_jll, :REPL, :Random, :SHA, :Serialization, :Sockets, :TOML, :Tar, :Test, :UUIDs, :Unicode, :nghttp2_jll] ), From 8be6f0f0ea1c4c0fb4d021eac6c83b155a908ffc Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 5 Apr 2023 08:53:25 +0200 Subject: [PATCH 440/775] move out LazyArtifacts from the sysimage (#49130) --- base/sysimg.jl | 3 --- pkgimage.mk | 2 +- test/precompile.jl | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/base/sysimg.jl b/base/sysimg.jl index 168138531eaff..423854fba3a13 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -75,9 +75,6 @@ let # 6-depth packages :Pkg, - - # 7-depth packages - :LazyArtifacts, ] # PackageCompiler can filter out stdlibs so it can be empty maxlen = maximum(textwidth.(string.(stdlibs)); init=0) diff --git a/pkgimage.mk b/pkgimage.mk index a18923450950f..ef913628212b8 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -117,7 +117,7 @@ $(eval $(call sysimg_builder,Downloads,ArgTools FileWatching LibCURL NetworkOpti $(eval $(call sysimg_builder,Pkg,Dates LibGit2 Libdl Logging Printf Random SHA UUIDs)) # Markdown REPL # 7-depth packages -$(eval $(call sysimg_builder,LazyArtifacts,Artifacts Pkg)) +$(eval $(call pkgimg_builder,LazyArtifacts,Artifacts Pkg)) $(eval $(call pkgimg_builder,SparseArrays,Libdl LinearAlgebra Random Serialization)) # SuiteSparse_jll diff --git a/test/precompile.jl b/test/precompile.jl index b1b929c9e043b..48437fb04eca2 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -396,7 +396,7 @@ precompile_test_harness(false) do dir end for s in [:ArgTools, :Artifacts, :Base64, :CompilerSupportLibraries_jll, :CRC32c, :Dates, :Distributed, :Downloads, :FileWatching, :Future, :InteractiveUtils, :libblastrampoline_jll, - :LazyArtifacts, :LibCURL, :LibCURL_jll, :LibGit2, :Libdl, :LinearAlgebra, + :LibCURL, :LibCURL_jll, :LibGit2, :Libdl, :LinearAlgebra, :Logging, :Markdown, :Mmap, :MozillaCACerts_jll, :NetworkOptions, :OpenBLAS_jll, :Pkg, :Printf, :p7zip_jll, :REPL, :Random, :SHA, :Serialization, :Sockets, :TOML, :Tar, :Test, :UUIDs, :Unicode, From bd1a664bff55c62cd5b094da7ad371a4f75936a3 Mon Sep 17 00:00:00 2001 From: Lionel Zoubritzky Date: Wed, 5 Apr 2023 08:55:24 +0200 Subject: [PATCH 441/775] Fix LazyString building with non-ASCII preceding $ (#49248) --- base/strings/lazy.jl | 2 +- test/strings/basic.jl | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/base/strings/lazy.jl b/base/strings/lazy.jl index 3510afc9b4f11..eaaa6397d37f2 100644 --- a/base/strings/lazy.jl +++ b/base/strings/lazy.jl @@ -67,7 +67,7 @@ macro lazy_str(text) parts = Any[] lastidx = idx = 1 while (idx = findnext('$', text, idx)) !== nothing - lastidx < idx && push!(parts, text[lastidx:idx-1]) + lastidx < idx && push!(parts, text[lastidx:prevind(text, idx)]) idx += 1 expr, idx = Meta.parseatom(text, idx; filename=string(__source__.file)) push!(parts, esc(expr)) diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 5f27df006b093..e1d6e9dd60491 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -1191,6 +1191,9 @@ end end return a end |> Core.Compiler.is_foldable + let i=49248 + @test String(lazy"PR n°$i") == "PR n°49248" + end end @testset "String Effects" begin From 280f9993608956f76eac30fc85e1c6ebbca4f5e6 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 16 Mar 2023 17:06:55 +0800 Subject: [PATCH 442/775] Enable seperatable subtyping for equality check. --- src/subtype.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index a6a2a7094c511..1034473aeff89 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1569,6 +1569,10 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t { int16_t oldRmore = e->Runions.more; int sub; + int kindx = !jl_has_free_typevars(x); + int kindy = !jl_has_free_typevars(y); + if (kindx && kindy) + return jl_subtype(x, y); if (may_contain_union_decision(y, e, NULL) && pick_union_decision(e, 1) == 0) { jl_saved_unionstate_t oldRunions; push_unionstate(&oldRunions, &e->Runions); e->Lunions.used = e->Runions.used = 0; @@ -1581,6 +1585,8 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t // Once limit_slow == 1, also skip it if // 1) `forall_exists_subtype` return false // 2) the left `Union` looks big + if (limit_slow == -1) + limit_slow = kindx || kindy; if (noRmore || (limit_slow && (count > 3 || !sub))) e->Runions.more = oldRmore; } @@ -1630,8 +1636,7 @@ static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) jl_saved_unionstate_t oldLunions; push_unionstate(&oldLunions, &e->Lunions); - int limit_slow = !jl_has_free_typevars(x) || !jl_has_free_typevars(y); - int sub = local_forall_exists_subtype(x, y, e, 2, limit_slow); + int sub = local_forall_exists_subtype(x, y, e, 2, -1); if (sub) { flip_offset(e); sub = local_forall_exists_subtype(y, x, e, 0, 0); From f1de0a9ac54f669e18af716e81faa7bc79d96cac Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 9 Mar 2023 11:10:57 +0800 Subject: [PATCH 443/775] Skip the subtype check in `rename_unionall` --- src/jltypes.c | 44 +++++++++++++++++++++++++++++++------------- src/julia_internal.h | 1 + src/subtype.c | 15 ++------------- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/jltypes.c b/src/jltypes.c index 4a451e9b70e80..918ac3b8292dd 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1015,7 +1015,7 @@ struct _jl_typestack_t; typedef struct _jl_typestack_t jl_typestack_t; static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **iparams, size_t ntp, - jl_typestack_t *stack, jl_typeenv_t *env); + jl_typestack_t *stack, jl_typeenv_t *env, int check); // Build an environment mapping a TypeName's parameters to parameter values. // This is the environment needed for instantiating a type's supertype and field types. @@ -1023,7 +1023,7 @@ static jl_value_t *inst_datatype_env(jl_value_t *dt, jl_svec_t *p, jl_value_t ** jl_typestack_t *stack, jl_typeenv_t *env, int c) { if (jl_is_datatype(dt)) - return inst_datatype_inner((jl_datatype_t*)dt, p, iparams, ntp, stack, env); + return inst_datatype_inner((jl_datatype_t*)dt, p, iparams, ntp, stack, env, 1); assert(jl_is_unionall(dt)); jl_unionall_t *ua = (jl_unionall_t*)dt; jl_typeenv_t e = { ua->var, iparams[c], env }; @@ -1154,6 +1154,18 @@ JL_DLLEXPORT jl_value_t *jl_instantiate_unionall(jl_unionall_t *u, jl_value_t *p return inst_type_w_(u->body, &env, NULL, 1); } +jl_unionall_t *jl_rename_unionall(jl_unionall_t *u) +{ + jl_tvar_t *v = jl_new_typevar(u->var->name, u->var->lb, u->var->ub); + jl_value_t *t = NULL; + JL_GC_PUSH2(&v, &t); + jl_typeenv_t env = { u->var, (jl_value_t *)v, NULL }; + t = inst_type_w_(u->body, &env, NULL, 0); + t = jl_new_struct(jl_unionall_type, v, t); + JL_GC_POP(); + return (jl_unionall_t*)t; +} + jl_value_t *jl_substitute_var(jl_value_t *t, jl_tvar_t *var, jl_value_t *val) { jl_typeenv_t env = { var, val, NULL }; @@ -1503,13 +1515,13 @@ jl_value_t *normalize_unionalls(jl_value_t *t) static jl_value_t *_jl_instantiate_type_in_env(jl_value_t *ty, jl_unionall_t *env, jl_value_t **vals, jl_typeenv_t *prev, jl_typestack_t *stack); static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **iparams, size_t ntp, - jl_typestack_t *stack, jl_typeenv_t *env) + jl_typestack_t *stack, jl_typeenv_t *env, int check) { jl_typestack_t top; jl_typename_t *tn = dt->name; int istuple = (tn == jl_tuple_typename); int isnamedtuple = (tn == jl_namedtuple_typename); - if (tn != jl_type_typename) { + if (check && tn != jl_type_typename) { size_t i; for (i = 0; i < ntp; i++) iparams[i] = normalize_unionalls(iparams[i]); @@ -1566,7 +1578,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value if (stack_lkup) return stack_lkup; - if (!istuple) { + if (check && !istuple) { // check parameters against bounds in type definition check_datatype_parameters(tn, iparams, ntp); } @@ -1686,7 +1698,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value ndt->super = jl_any_type; } else if (dt->super) { - ndt->super = (jl_datatype_t*)inst_type_w_((jl_value_t*)dt->super, env, stack, 1); + ndt->super = (jl_datatype_t*)inst_type_w_((jl_value_t*)dt->super, env, stack, check); jl_gc_wb(ndt, ndt->super); } jl_svec_t *ftypes = dt->types; @@ -1734,7 +1746,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value static jl_tupletype_t *jl_apply_tuple_type_v_(jl_value_t **p, size_t np, jl_svec_t *params) { - return (jl_datatype_t*)inst_datatype_inner(jl_anytuple_type, params, p, np, NULL, NULL); + return (jl_datatype_t*)inst_datatype_inner(jl_anytuple_type, params, p, np, NULL, NULL, 1); } JL_DLLEXPORT jl_tupletype_t *jl_apply_tuple_type(jl_svec_t *params) @@ -1773,7 +1785,7 @@ jl_tupletype_t *jl_inst_arg_tuple_type(jl_value_t *arg1, jl_value_t **args, size } jl_svecset(params, i, ai); } - tt = (jl_datatype_t*)inst_datatype_inner(jl_anytuple_type, params, jl_svec_data(params), nargs, NULL, NULL); + tt = (jl_datatype_t*)inst_datatype_inner(jl_anytuple_type, params, jl_svec_data(params), nargs, NULL, NULL, 1); JL_GC_POP(); } return tt; @@ -1844,14 +1856,14 @@ static jl_value_t *inst_tuple_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_ int i; for (i = 0; i < ntp; i++) { jl_value_t *elt = jl_svecref(tp, i); - jl_value_t *pi = inst_type_w_(elt, env, stack, 0); + jl_value_t *pi = inst_type_w_(elt, env, stack, check); iparams[i] = pi; if (ip_heap) jl_gc_wb(ip_heap, pi); bound |= (pi != elt); } if (bound) - t = inst_datatype_inner(tt, ip_heap, iparams, ntp, stack, env); + t = inst_datatype_inner(tt, ip_heap, iparams, ntp, stack, env, check); JL_GC_POP(); return t; } @@ -1904,8 +1916,14 @@ static jl_value_t *inst_type_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_t JL_GC_PUSH2(&a, &b); b = inst_type_w_(u->b, env, stack, check); if (a != u->a || b != u->b) { - jl_value_t *uargs[2] = {a, b}; - t = jl_type_union(uargs, 2); + if (check) { + jl_value_t *uargs[2] = {a, b}; + t = jl_type_union(uargs, 2); + } + else { + // fast path for `jl_rename_unionall`. + t = jl_new_struct(jl_uniontype_type, a, b); + } } JL_GC_POP(); return t; @@ -1947,7 +1965,7 @@ static jl_value_t *inst_type_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_t } // if t's parameters are not bound in the environment, return it uncopied (#9378) if (bound) - t = inst_datatype_inner(tt, NULL, iparams, ntp, stack, env); + t = inst_datatype_inner(tt, NULL, iparams, ntp, stack, env, check); JL_GC_POP(); return t; } diff --git a/src/julia_internal.h b/src/julia_internal.h index 73f8b9467fcf4..4f1a0b4513d8d 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -694,6 +694,7 @@ JL_DLLEXPORT int jl_type_morespecific_no_subtype(jl_value_t *a, jl_value_t *b); jl_value_t *jl_instantiate_type_with(jl_value_t *t, jl_value_t **env, size_t n); JL_DLLEXPORT jl_value_t *jl_instantiate_type_in_env(jl_value_t *ty, jl_unionall_t *env, jl_value_t **vals); jl_value_t *jl_substitute_var(jl_value_t *t, jl_tvar_t *var, jl_value_t *val); +jl_unionall_t *jl_rename_unionall(jl_unionall_t *u); JL_DLLEXPORT jl_value_t *jl_unwrap_unionall(jl_value_t *v JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_rewrap_unionall(jl_value_t *t, jl_value_t *u); JL_DLLEXPORT jl_value_t *jl_rewrap_unionall_(jl_value_t *t, jl_value_t *u); diff --git a/src/subtype.c b/src/subtype.c index 1034473aeff89..9cbf5549ed94e 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -630,17 +630,6 @@ static jl_value_t *simple_meet(jl_value_t *a, jl_value_t *b, int overesi) return overesi ? b : jl_bottom_type; } -static jl_unionall_t *rename_unionall(jl_unionall_t *u) -{ - jl_tvar_t *v = jl_new_typevar(u->var->name, u->var->lb, u->var->ub); - jl_value_t *t = NULL; - JL_GC_PUSH2(&v, &t); - t = jl_instantiate_unionall(u, (jl_value_t*)v); - t = jl_new_struct(jl_unionall_type, v, t); - JL_GC_POP(); - return (jl_unionall_t*)t; -} - // main subtyping algorithm static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param); @@ -935,7 +924,7 @@ static jl_unionall_t *unalias_unionall(jl_unionall_t *u, jl_stenv_t *e) // outer var can only refer to inner var if bounds changed (btemp->lb != btemp->var->lb && jl_has_typevar(btemp->lb, u->var)) || (btemp->ub != btemp->var->ub && jl_has_typevar(btemp->ub, u->var))) { - u = rename_unionall(u); + u = jl_rename_unionall(u); break; } btemp = btemp->prev; @@ -2945,7 +2934,7 @@ static jl_value_t *intersect_unionall_(jl_value_t *t, jl_unionall_t *u, jl_stenv } if (btemp->var == u->var || btemp->lb == (jl_value_t*)u->var || btemp->ub == (jl_value_t*)u->var) { - u = rename_unionall(u); + u = jl_rename_unionall(u); break; } btemp = btemp->prev; From 5235bfb483a29d27724b6dbc4b9a9842390cf47a Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Wed, 22 Mar 2023 23:19:34 +0800 Subject: [PATCH 444/775] Avoid re-intersection for simple case --- src/subtype.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/subtype.c b/src/subtype.c index 9cbf5549ed94e..8f7488dcb27bc 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -3001,6 +3001,8 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_ e->invdepth, NULL, e->vars }; JL_GC_PUSH4(&res, &vb.lb, &vb.ub, &vb.innervars); save_env(e, &se, 1); + if (is_leaf_typevar(u->var) && !var_occurs_invariant(u->body, u->var, 0)) + vb.constraintkind = 1; res = intersect_unionall_(t, u, e, R, param, &vb); if (vb.limited) { // if the environment got too big, avoid tree recursion and propagate the flag @@ -3008,13 +3010,19 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_ e->vars->limited = 1; } else if (res != jl_bottom_type) { + int constraint1 = vb.constraintkind; if (vb.concrete || vb.occurs_inv>1 || (vb.occurs_inv && vb.occurs_cov)) vb.constraintkind = vb.concrete ? 1 : 2; else if (u->var->lb != jl_bottom_type) vb.constraintkind = 2; else if (vb.occurs_cov && !var_occurs_invariant(u->body, u->var, 0)) vb.constraintkind = 1; - if (vb.constraintkind) { + int reintersection = constraint1 != vb.constraintkind || vb.concrete; + if (reintersection) { + if (constraint1 == 1) { + vb.lb = vb.var->lb; + vb.ub = vb.var->ub; + } restore_env(e, &se, vb.constraintkind == 1 ? 1 : 0); vb.occurs = vb.occurs_cov = vb.occurs_inv = 0; res = intersect_unionall_(t, u, e, R, param, &vb); From 9d7cfa84e27c61a23d870e292e33741b0896c4f2 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Thu, 9 Mar 2023 17:38:33 -0500 Subject: [PATCH 445/775] Add `JL_TIMING` support for Tracy --- Make.inc | 4 ++-- deps/libtracyclient.mk | 1 + src/gc.c | 25 +++++++++++++++++++++++++ src/options.h | 2 +- src/task.c | 16 ++++++++++++++-- src/timing.h | 24 ++++++++++++++++++++++++ 6 files changed, 67 insertions(+), 5 deletions(-) diff --git a/Make.inc b/Make.inc index 1285de0dc3590..4d91cd9df9525 100644 --- a/Make.inc +++ b/Make.inc @@ -738,8 +738,8 @@ LIBITTAPI:=-littnotify endif ifeq ($(WITH_TRACY), 1) -JCXXFLAGS += -DUSE_TRACY -DTRACY_ENABLE -JCFLAGS += -DUSE_TRACY -DTRACY_ENABLE +JCXXFLAGS += -DUSE_TRACY -DTRACY_ENABLE -DTRACY_FIBERS +JCFLAGS += -DUSE_TRACY -DTRACY_ENABLE -DTRACY_FIBERS LIBTRACYCLIENT:=-lTracyClient endif diff --git a/deps/libtracyclient.mk b/deps/libtracyclient.mk index 29427889c1d6a..aee5d6e969770 100644 --- a/deps/libtracyclient.mk +++ b/deps/libtracyclient.mk @@ -11,6 +11,7 @@ LIBTRACYCLIENT_CMAKE := LIBTRACYCLIENT_CMAKE += -DBUILD_SHARED_LIBS=ON LIBTRACYCLIENT_CMAKE += -DTRACY_FIBERS=ON LIBTRACYCLIENT_CMAKE += -DTRACY_NO_BROADCAST=ON +LIBTRACYCLIENT_CMAKE += -DTRACY_NO_SYSTEM_TRACING=ON LIBTRACYCLIENT_CMAKE += -DTRACY_ONLY_LOCALHOST=ON LIBTRACYCLIENT_CMAKE += -DTRACY_NO_CODE_TRANSFER=ON LIBTRACYCLIENT_CMAKE += -DTRACY_NO_FRAME_IMAGE=ON diff --git a/src/gc.c b/src/gc.c index 158a03be017fa..fc7c89cd03b66 100644 --- a/src/gc.c +++ b/src/gc.c @@ -3243,6 +3243,21 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) jl_gc_state_set(ptls, old_state, JL_GC_STATE_WAITING); return; } +#ifdef USE_TRACY + static uint8_t first_time = 1; + if (first_time) { + first_time = 0; + TracyCFiberEnter("Main"); + } + TracyCFiberLeave; + TracyCFiberEnter("GC"); + { + int64_t tb; + jl_gc_get_total_bytes(&tb); + TracyCPlot("Heap size", ((double)tb) / (1024.0 * 1024.0)); + } +#endif +{ JL_TIMING(GC); int last_errno = errno; #ifdef _OS_WINDOWS_ @@ -3307,6 +3322,16 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) #endif errno = last_errno; } +#ifdef USE_TRACY + { + int64_t tb; + jl_gc_get_total_bytes(&tb); + TracyCPlot("Heap size", ((double)tb) / (1024.0 * 1024.0)); + } + TracyCFiberLeave; + TracyCFiberEnter("Main"); +#endif +} void gc_mark_queue_all_roots(jl_ptls_t ptls, jl_gc_markqueue_t *mq) { diff --git a/src/options.h b/src/options.h index 82b71431ecea0..12822b6ecf8b0 100644 --- a/src/options.h +++ b/src/options.h @@ -79,7 +79,7 @@ // #define OBJPROFILE // Automatic Instrumenting Profiler -//#define ENABLE_TIMINGS +#define ENABLE_TIMINGS // method dispatch profiling -------------------------------------------------- diff --git a/src/task.c b/src/task.c index 580ad444d8cad..1035d54f2ce9a 100644 --- a/src/task.c +++ b/src/task.c @@ -1211,6 +1211,9 @@ CFI_NORETURN sanitizer_finish_switch_fiber(ptls->previous_task, ct); _start_task(); } + +const char* fiber = "task"; + STATIC_OR_JS void NOINLINE JL_NORETURN _start_task(void) { CFI_NORETURN @@ -1246,8 +1249,14 @@ CFI_NORETURN ptls->defer_signal = 0; jl_sigint_safepoint(ptls); } - JL_TIMING(ROOT); - res = jl_apply(&ct->start, 1); +//#ifdef USE_TRACY + //TracyFiberEnter(fiber); +//#endif + { + // TODO: Re-enable + //JL_TIMING(ROOT); + res = jl_apply(&ct->start, 1); + } } JL_CATCH { res = jl_current_exception(); @@ -1256,6 +1265,9 @@ CFI_NORETURN } skip_pop_exception:; } +//#ifdef USE_TRACY + //TracyFiberLeave(fiber); +//#endif ct->result = res; jl_gc_wb(ct, ct->result); jl_finish_task(ct); diff --git a/src/timing.h b/src/timing.h index 70f34fa89f543..c9a1fd1fb4807 100644 --- a/src/timing.h +++ b/src/timing.h @@ -17,6 +17,9 @@ void jl_destroy_timing(void) JL_NOTSAFEPOINT; #else #include "julia_assert.h" +#ifdef USE_TRACY +#include "tracy/TracyC.h" +#endif #ifdef __cplusplus extern "C" { @@ -78,6 +81,9 @@ extern uint64_t jl_timing_data[(int)JL_TIMING_LAST]; extern const char *jl_timing_names[(int)JL_TIMING_LAST]; struct _jl_timing_block_t { // typedef in julia.h +#ifdef USE_TRACY + TracyCZoneCtx *tracy_ctx; +#endif jl_timing_block_t *prev; uint64_t total; uint64_t t0; @@ -125,6 +131,9 @@ STATIC_INLINE void _jl_timing_block_ctor(jl_timing_block_t *block, int owner) JL } STATIC_INLINE void _jl_timing_block_destroy(jl_timing_block_t *block) JL_NOTSAFEPOINT { +#ifdef USE_TRACY + TracyCZoneEnd(*(block->tracy_ctx)); +#endif uint64_t t = cycleclock(); jl_task_t *ct = jl_current_task; _jl_timing_block_stop(block, t); @@ -150,13 +159,28 @@ struct jl_timing_block_cpp_t { jl_timing_block_cpp_t& operator=(const jl_timing_block_cpp_t &) = delete; jl_timing_block_cpp_t& operator=(const jl_timing_block_cpp_t &&) = delete; }; +#ifdef USE_TRACY +#define JL_TIMING(owner) jl_timing_block_cpp_t __timing_block(JL_TIMING_ ## owner); \ + TracyCZoneN(__tracy_ctx, #owner, strcmp(#owner, "ROOT")); \ + __timing_block.block.tracy_ctx = &__tracy_ctx; +#else #define JL_TIMING(owner) jl_timing_block_cpp_t __timing_block(JL_TIMING_ ## owner) +#endif +#else +#ifdef USE_TRACY +#define JL_TIMING(owner) \ + __attribute__((cleanup(_jl_timing_block_destroy))) \ + jl_timing_block_t __timing_block; \ + _jl_timing_block_ctor(&__timing_block, JL_TIMING_ ## owner); \ + TracyCZoneN(__tracy_ctx, #owner, 1); \ + __timing_block.tracy_ctx = &__tracy_ctx; #else #define JL_TIMING(owner) \ __attribute__((cleanup(_jl_timing_block_destroy))) \ jl_timing_block_t __timing_block; \ _jl_timing_block_ctor(&__timing_block, JL_TIMING_ ## owner) #endif +#endif #endif #endif From 7a0441c41372f6f9d7242b8a75789e7720296a4d Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Thu, 23 Mar 2023 10:48:18 -0400 Subject: [PATCH 446/775] ios: Add fixed-size streams and expand inline storage This includes a few separate enhancements: 1. `growable` is added to allow for fixed-size buffers 2. `ios_t` struct layout is optimized for better packing 3. inline buffer is increased to 83 bytes from 54 This change grows the size of ios_t by 16 bytes (160 -> 172 bytes), but the internal buffer was increased by 29 bytes thanks to re-arranging the fields in the struct to be packed more efficiently. --- src/support/ios.c | 4 ++++ src/support/ios.h | 21 ++++++++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/support/ios.c b/src/support/ios.c index 4a6aeb54a4d32..2b3966eca7897 100644 --- a/src/support/ios.c +++ b/src/support/ios.c @@ -196,6 +196,9 @@ static char *_buf_realloc(ios_t *s, size_t sz) if (sz <= s->maxsize) return s->buf; + if (!s->growable) + return NULL; + if (s->ownbuf && s->buf != &s->local[0]) { // if we own the buffer we're free to resize it temp = (char*)LLT_REALLOC(s->buf, sz); @@ -892,6 +895,7 @@ static void _ios_init(ios_t *s) s->readable = 1; s->writable = 1; s->rereadable = 0; + s->growable = 1; } /* stream object initializers. we do no allocation. */ diff --git a/src/support/ios.h b/src/support/ios.h index 046edfae0556f..2547555b5585d 100644 --- a/src/support/ios.h +++ b/src/support/ios.h @@ -19,13 +19,13 @@ extern "C" { typedef enum { bm_none=1000, bm_line, bm_block, bm_mem } bufmode_t; typedef enum { bst_none, bst_rd, bst_wr } bufstate_t; -#define IOS_INLSIZE 54 +#define IOS_INLSIZE 83 #define IOS_BUFSIZE 32768 #ifdef _P64 -#define ON_P64(x) x +#define IF_P64(x,y) x #else -#define ON_P64(x) +#define IF_P64(x,y) y #endif // We allow ios_t as a cvalue in flisp, which only guarantees pointer @@ -36,10 +36,8 @@ JL_ATTRIBUTE_ALIGN_PTRSIZE(typedef struct { // in general, you can do any operation in any state. char *buf; // start of buffer - int errcode; - - ON_P64(int _pad_bm;) // put bm at same offset as type field of uv_stream_s - bufmode_t bm; // + IF_P64(int64_t userdata;, int errcode;) + bufmode_t bm; // bm must be at same offset as type field of uv_stream_s bufstate_t state; int64_t maxsize; // space allocated to buffer @@ -51,6 +49,8 @@ JL_ATTRIBUTE_ALIGN_PTRSIZE(typedef struct { size_t lineno; // current line number size_t u_colno; // current column number (in Unicode charwidths) + IF_P64(int errcode;, int64_t userdata;) + // pointer-size integer to support platforms where it might have // to be a pointer long fd; @@ -74,11 +74,14 @@ JL_ATTRIBUTE_ALIGN_PTRSIZE(typedef struct { // request durable writes (fsync) // unsigned char durable:1; - int64_t userdata; + // this declares that the buffer should not be (re-)alloc'd when + // attempting to write beyond its current maxsize. + unsigned char growable:1; + char local[IOS_INLSIZE]; } ios_t); -#undef ON_P64 +#undef IF_P64 extern void (*ios_set_io_wait_func)(int); /* low-level interface functions */ From 6bfb7fb13c3c60cbef17a87f49ea97b491afe792 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 5 Apr 2023 18:23:38 +0200 Subject: [PATCH 447/775] move out Test from the sysimage (#49134) --- base/sysimg.jl | 1 - contrib/generate_precompile.jl | 33 --------------------------- pkgimage.mk | 2 +- stdlib/Distributed/test/splitrange.jl | 2 ++ stdlib/Test/src/Test.jl | 1 + stdlib/Test/src/precompile.jl | 9 ++++++++ test/loading.jl | 11 ++++----- 7 files changed, 18 insertions(+), 41 deletions(-) create mode 100644 stdlib/Test/src/precompile.jl diff --git a/base/sysimg.jl b/base/sysimg.jl index 423854fba3a13..ca1a4e74f7417 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -65,7 +65,6 @@ let # 3-depth packages :REPL, :TOML, - :Test, # 4-depth packages :LibCURL, diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index a7d3bfafdd849..f28a0fcd3974f 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -199,39 +199,6 @@ if Libdl !== nothing """ end -Test = get(Base.loaded_modules, - Base.PkgId(Base.UUID("8dfed614-e22c-5e08-85e1-65c5234f0b40"), "Test"), - nothing) -if Test !== nothing - hardcoded_precompile_statements *= """ - precompile(Tuple{typeof(Test.do_test), Test.ExecutionResult, Any}) - precompile(Tuple{typeof(Test.testset_beginend_call), Tuple{String, Expr}, Expr, LineNumberNode}) - precompile(Tuple{Type{Test.DefaultTestSet}, String}) - precompile(Tuple{Type{Test.DefaultTestSet}, AbstractString}) - precompile(Tuple{Core.kwftype(Type{Test.DefaultTestSet}), Any, Type{Test.DefaultTestSet}, AbstractString}) - precompile(Tuple{typeof(Test.finish), Test.DefaultTestSet}) - precompile(Tuple{typeof(Test.eval_test), Expr, Expr, LineNumberNode, Bool}) - precompile(Tuple{typeof(Test._inferred), Expr, Module}) - precompile(Tuple{typeof(Test.push_testset), Test.DefaultTestSet}) - precompile(Tuple{typeof(Test.get_alignment), Test.DefaultTestSet, Int}) - precompile(Tuple{typeof(Test.get_test_result), Any, Any}) - precompile(Tuple{typeof(Test.do_test_throws), Test.ExecutionResult, Any, Any}) - precompile(Tuple{typeof(Test.print_counts), Test.DefaultTestSet, Int, Int, Int, Int, Int, Int, Int}) - precompile(Tuple{typeof(Test._check_testset), Type, Expr}) - precompile(Tuple{typeof(Test.test_expr!), Any, Any}) - precompile(Tuple{typeof(Test.test_expr!), Any, Any, Vararg{Any, 100}}) - precompile(Tuple{typeof(Test.pop_testset)}) - precompile(Tuple{typeof(Test.match_logs), Function, Tuple{Symbol, Regex}}) - precompile(Tuple{typeof(Test.match_logs), Function, Tuple{String, Regex}}) - precompile(Tuple{typeof(Base.CoreLogging.shouldlog), Test.TestLogger, Base.CoreLogging.LogLevel, Module, Symbol, Symbol}) - precompile(Tuple{typeof(Base.CoreLogging.handle_message), Test.TestLogger, Base.CoreLogging.LogLevel, String, Module, Symbol, Symbol, String, Int}) - precompile(Tuple{typeof(Test.detect_ambiguities), Any}) - precompile(Tuple{typeof(Test.collect_test_logs), Function}) - precompile(Tuple{typeof(Test.do_broken_test), Test.ExecutionResult, Any}) - precompile(Tuple{typeof(Test.record), Test.DefaultTestSet, Union{Test.Error, Test.Fail}}) - precompile(Tuple{typeof(Test.filter_errors), Test.DefaultTestSet}) - """ -end const JULIA_PROMPT = "julia> " const PKG_PROMPT = "pkg> " diff --git a/pkgimage.mk b/pkgimage.mk index ef913628212b8..554bcd5587abe 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -105,7 +105,7 @@ $(eval $(call pkgimg_builder,LibCURL_jll,LibSSH2_jll nghttp2_jll MbedTLS_jll Zli $(eval $(call sysimg_builder,REPL,InteractiveUtils Markdown Sockets Unicode)) $(eval $(call pkgimg_builder,SharedArrays,Distributed Mmap Random Serialization)) $(eval $(call sysimg_builder,TOML,Dates)) -$(eval $(call sysimg_builder,Test,Logging Random Serialization InteractiveUtils)) +$(eval $(call pkgimg_builder,Test,Logging Random Serialization InteractiveUtils)) # 4-depth packages $(eval $(call sysimg_builder,LibCURL,LibCURL_jll MozillaCACerts_jll)) diff --git a/stdlib/Distributed/test/splitrange.jl b/stdlib/Distributed/test/splitrange.jl index 9f3c9c92a3ffa..1cb12e1952b7d 100644 --- a/stdlib/Distributed/test/splitrange.jl +++ b/stdlib/Distributed/test/splitrange.jl @@ -28,6 +28,8 @@ isdefined(Main, :OffsetArrays) || @eval Main @everywhere include(joinpath($(BASE using .Main.OffsetArrays oa = OffsetArray([123, -345], (-2,)) + +@everywhere using Test @sync @distributed for i in eachindex(oa) @test i ∈ (-1, 0) end diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 7922bc4d82463..0253b5a42520c 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -2096,5 +2096,6 @@ function _check_bitarray_consistency(B::BitArray{N}) where N end include("logging.jl") +include("precompile.jl") end # module diff --git a/stdlib/Test/src/precompile.jl b/stdlib/Test/src/precompile.jl new file mode 100644 index 0000000000000..2cb2fb7f3f0c6 --- /dev/null +++ b/stdlib/Test/src/precompile.jl @@ -0,0 +1,9 @@ +redirect_stdout(devnull) do + @testset "example" begin + @test 1 == 1 + @test_throws ErrorException error() + @test_logs (:info, "Doing foo with n=2") @info "Doing foo with n=2" + @test_broken 1 == 2 + @test 1 ≈ 1.0000000000000001 + end +end diff --git a/test/loading.jl b/test/loading.jl index 5540728c70b7d..9b29697b31160 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1089,15 +1089,14 @@ end for (P, D, C, I, O) in Iterators.product(0:1, 0:2, 0:2, 0:1, 0:3) julia = joinpath(Sys.BINDIR, Base.julia_exename()) script = """ - using Test let cf = Base.CacheFlags() opts = Base.JLOptions() - @test cf.use_pkgimages == opts.use_pkgimages == $P - @test cf.debug_level == opts.debug_level == $D - @test cf.check_bounds == opts.check_bounds == $C - @test cf.inline == opts.can_inline == $I - @test cf.opt_level == opts.opt_level == $O + cf.use_pkgimages == opts.use_pkgimages == $P || error("use_pkgimages") + cf.debug_level == opts.debug_level == $D || error("debug_level") + cf.check_bounds == opts.check_bounds == $C || error("check_bounds") + cf.inline == opts.can_inline == $I || error("inline") + cf.opt_level == opts.opt_level == $O || error("opt_level") end """ cmd = `$julia $(pkgimage(P)) $(opt_level(O)) $(debug_level(D)) $(check_bounds(C)) $(inline(I)) -e $script` From 1bf65b99078777325b5997e270d729f3cf7cd6f3 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 5 Apr 2023 12:46:56 -0500 Subject: [PATCH 448/775] Log only if something was invalidated (#49264) --- src/gf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gf.c b/src/gf.c index 0b01f5e8e6ee2..187cfb07a2d1a 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2087,7 +2087,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method } jl_array_ptr_1d_push(oldmi, (jl_value_t*)mi); invalidate_external(mi, max_world); - if (_jl_debug_method_invalidation) { + if (_jl_debug_method_invalidation && invalidated) { jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)mi); loctag = jl_cstr_to_string("jl_method_table_insert"); jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); From 5aa8c1512f7f44447a56ebc706d965c4d5569848 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Thu, 23 Mar 2023 11:34:13 -0400 Subject: [PATCH 449/775] profiling: Add Task/GC/Inference/Codegen signposts for Tracy This adds a variety of metadata to send to Tracy during profiling, in particular method instance signatures, the Heap size, and a couple of other small pieces of relevant metadata. It also adds full support for Task execution, tracking these as "Fibers" on the Tracy side. This does incur some overhead until an enhancement is implemented on the Tracy-side to avoid a globally synchronous Fiber event queue, but most of the events that we're tracking now are relatively coarse so it should not be a major issue. --- src/aotcompile.cpp | 2 +- src/ast.c | 15 ++-- src/codegen.cpp | 8 +- src/gc.c | 204 +++++++++++++++++++++-------------------- src/gf.c | 14 +-- src/ircode.c | 4 +- src/jitlayers.cpp | 8 +- src/jltypes.c | 6 +- src/julia.h | 4 + src/method.c | 3 +- src/rtutils.c | 12 ++- src/safepoint.c | 4 +- src/staticdata.c | 4 +- src/task.c | 64 +++++++------ src/timing.c | 166 ++++++++++++++++++++++++++++++++-- src/timing.h | 219 ++++++++++++++++++++++++++++++++++----------- src/toplevel.c | 3 +- 17 files changed, 521 insertions(+), 219 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 63d941949343e..fc1d4074e92bb 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1448,7 +1448,7 @@ void jl_dump_native_impl(void *native_code, const char *asm_fname, const char *sysimg_data, size_t sysimg_len, ios_t *s) { - JL_TIMING(NATIVE_DUMP); + JL_TIMING(NATIVE_DUMP, NATIVE_DUMP); jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code; if (!bc_fname && !unopt_bc_fname && !obj_fname && !asm_fname) { LLVM_DEBUG(dbgs() << "No output requested, skipping native code dump?\n"); diff --git a/src/ast.c b/src/ast.c index 3f3d6176d342e..08c493c75b985 100644 --- a/src/ast.c +++ b/src/ast.c @@ -783,7 +783,8 @@ JL_DLLEXPORT jl_value_t *jl_fl_parse(const char *text, size_t text_len, jl_value_t *filename, size_t lineno, size_t offset, jl_value_t *options) { - JL_TIMING(PARSING); + JL_TIMING(PARSING, PARSING); + jl_timing_show_filename(jl_string_data(filename), JL_TIMING_CURRENT_BLOCK); if (offset > text_len) { jl_value_t *textstr = jl_pchar_to_string(text, text_len); JL_GC_PUSH1(&textstr); @@ -1000,7 +1001,7 @@ int jl_has_meta(jl_array_t *body, jl_sym_t *sym) JL_NOTSAFEPOINT static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule, jl_module_t **ctx, size_t world, int throw_load_error) { jl_task_t *ct = jl_current_task; - JL_TIMING(MACRO_INVOCATION); + JL_TIMING(MACRO_INVOCATION, MACRO_INVOCATION); size_t nargs = jl_array_len(args) + 1; JL_NARGSV("macrocall", 3); // macro name, location, and module jl_value_t **margs; @@ -1139,7 +1140,7 @@ static jl_value_t *jl_expand_macros(jl_value_t *expr, jl_module_t *inmodule, str JL_DLLEXPORT jl_value_t *jl_macroexpand(jl_value_t *expr, jl_module_t *inmodule) { - JL_TIMING(LOWERING); + JL_TIMING(LOWERING, LOWERING); JL_GC_PUSH1(&expr); expr = jl_copy_ast(expr); expr = jl_expand_macros(expr, inmodule, NULL, 0, jl_atomic_load_acquire(&jl_world_counter), 0); @@ -1150,7 +1151,7 @@ JL_DLLEXPORT jl_value_t *jl_macroexpand(jl_value_t *expr, jl_module_t *inmodule) JL_DLLEXPORT jl_value_t *jl_macroexpand1(jl_value_t *expr, jl_module_t *inmodule) { - JL_TIMING(LOWERING); + JL_TIMING(LOWERING, LOWERING); JL_GC_PUSH1(&expr); expr = jl_copy_ast(expr); expr = jl_expand_macros(expr, inmodule, NULL, 1, jl_atomic_load_acquire(&jl_world_counter), 0); @@ -1176,7 +1177,7 @@ JL_DLLEXPORT jl_value_t *jl_expand_with_loc(jl_value_t *expr, jl_module_t *inmod JL_DLLEXPORT jl_value_t *jl_expand_in_world(jl_value_t *expr, jl_module_t *inmodule, const char *file, int line, size_t world) { - JL_TIMING(LOWERING); + JL_TIMING(LOWERING, LOWERING); JL_GC_PUSH1(&expr); expr = jl_copy_ast(expr); expr = jl_expand_macros(expr, inmodule, NULL, 0, world, 1); @@ -1189,7 +1190,7 @@ JL_DLLEXPORT jl_value_t *jl_expand_in_world(jl_value_t *expr, jl_module_t *inmod JL_DLLEXPORT jl_value_t *jl_expand_with_loc_warn(jl_value_t *expr, jl_module_t *inmodule, const char *file, int line) { - JL_TIMING(LOWERING); + JL_TIMING(LOWERING, LOWERING); jl_array_t *kwargs = NULL; JL_GC_PUSH2(&expr, &kwargs); expr = jl_copy_ast(expr); @@ -1237,7 +1238,7 @@ JL_DLLEXPORT jl_value_t *jl_expand_with_loc_warn(jl_value_t *expr, jl_module_t * JL_DLLEXPORT jl_value_t *jl_expand_stmt_with_loc(jl_value_t *expr, jl_module_t *inmodule, const char *file, int line) { - JL_TIMING(LOWERING); + JL_TIMING(LOWERING, LOWERING); JL_GC_PUSH1(&expr); expr = jl_copy_ast(expr); expr = jl_expand_macros(expr, inmodule, NULL, 0, ~(size_t)0, 1); diff --git a/src/codegen.cpp b/src/codegen.cpp index 68fafba7a5a06..2d75d4ca8ebf3 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8421,7 +8421,8 @@ jl_llvm_functions_t jl_emit_code( jl_value_t *jlrettype, jl_codegen_params_t ¶ms) { - JL_TIMING(CODEGEN); + JL_TIMING(CODEGEN, CODEGEN); + jl_timing_show_func_sig((jl_value_t *)li->specTypes, JL_TIMING_CURRENT_BLOCK); // caller must hold codegen_lock jl_llvm_functions_t decls = {}; assert((params.params == &jl_default_cgparams /* fast path */ || !params.cache || @@ -8463,7 +8464,8 @@ jl_llvm_functions_t jl_emit_codeinst( jl_code_info_t *src, jl_codegen_params_t ¶ms) { - JL_TIMING(CODEGEN); + JL_TIMING(CODEGEN, CODEGEN); + jl_timing_show_method_instance(codeinst->def, JL_TIMING_CURRENT_BLOCK); JL_GC_PUSH1(&src); if (!src) { src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred); @@ -8542,7 +8544,7 @@ void jl_compile_workqueue( Module &original, jl_codegen_params_t ¶ms, CompilationPolicy policy) { - JL_TIMING(CODEGEN); + JL_TIMING(CODEGEN, CODEGEN); jl_code_info_t *src = NULL; JL_GC_PUSH1(&src); while (!params.workqueue.empty()) { diff --git a/src/gc.c b/src/gc.c index fc7c89cd03b66..a67783c741b43 100644 --- a/src/gc.c +++ b/src/gc.c @@ -311,6 +311,11 @@ NOINLINE uintptr_t gc_get_stack_ptr(void) void jl_gc_wait_for_the_world(jl_ptls_t* gc_all_tls_states, int gc_n_threads) { + JL_TIMING(GC, Stop); +#ifdef USE_TRACY + TracyCZoneCtx ctx = *(JL_TIMING_CURRENT_BLOCK->tracy_ctx); + TracyCZoneColor(ctx, 0x696969); +#endif assert(gc_n_threads); if (gc_n_threads > 1) jl_wake_libuv(); @@ -2941,79 +2946,86 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) { combine_thread_gc_counts(&gc_num); +#ifdef USE_TRACY + TracyCPlot("Heap size", live_bytes + gc_num.allocd); +#endif + jl_gc_markqueue_t *mq = &ptls->mark_queue; uint64_t gc_start_time = jl_hrtime(); int64_t last_perm_scanned_bytes = perm_scanned_bytes; - JL_PROBE_GC_MARK_BEGIN(); uint64_t start_mark_time = jl_hrtime(); - - // 1. fix GC bits of objects in the remset. - assert(gc_n_threads); - for (int t_i = 0; t_i < gc_n_threads; t_i++) { - jl_ptls_t ptls2 = gc_all_tls_states[t_i]; - if (ptls2 != NULL) - gc_premark(ptls2); - } - - assert(gc_n_threads); - for (int t_i = 0; t_i < gc_n_threads; t_i++) { - jl_ptls_t ptls2 = gc_all_tls_states[t_i]; - if (ptls2 != NULL) { - // 2.1. mark every thread local root - gc_queue_thread_local(mq, ptls2); - // 2.2. mark any managed objects in the backtrace buffer - // TODO: treat these as roots for gc_heap_snapshot_record - gc_queue_bt_buf(mq, ptls2); - // 2.3. mark every object in the `last_remsets` and `rem_binding` - gc_queue_remset(ptls, ptls2); + JL_PROBE_GC_MARK_BEGIN(); + { + JL_TIMING(GC, Mark); + + // 1. fix GC bits of objects in the remset. + assert(gc_n_threads); + for (int t_i = 0; t_i < gc_n_threads; t_i++) { + jl_ptls_t ptls2 = gc_all_tls_states[t_i]; + if (ptls2 != NULL) + gc_premark(ptls2); + } + + assert(gc_n_threads); + for (int t_i = 0; t_i < gc_n_threads; t_i++) { + jl_ptls_t ptls2 = gc_all_tls_states[t_i]; + if (ptls2 != NULL) { + // 2.1. mark every thread local root + gc_queue_thread_local(mq, ptls2); + // 2.2. mark any managed objects in the backtrace buffer + // TODO: treat these as roots for gc_heap_snapshot_record + gc_queue_bt_buf(mq, ptls2); + // 2.3. mark every object in the `last_remsets` and `rem_binding` + gc_queue_remset(ptls, ptls2); + } } - } - // 3. walk roots - gc_mark_roots(mq); - if (gc_cblist_root_scanner) { - gc_invoke_callbacks(jl_gc_cb_root_scanner_t, - gc_cblist_root_scanner, (collection)); - } - gc_mark_loop(ptls); - - // 4. check for objects to finalize - clear_weak_refs(); - // Record the length of the marked list since we need to - // mark the object moved to the marked list from the - // `finalizer_list` by `sweep_finalizer_list` - size_t orig_marked_len = finalizer_list_marked.len; - assert(gc_n_threads); - for (int i = 0; i < gc_n_threads; i++) { - jl_ptls_t ptls2 = gc_all_tls_states[i]; - if (ptls2 != NULL) - sweep_finalizer_list(&ptls2->finalizers); + // 3. walk roots + gc_mark_roots(mq); + if (gc_cblist_root_scanner) { + gc_invoke_callbacks(jl_gc_cb_root_scanner_t, + gc_cblist_root_scanner, (collection)); + } + gc_mark_loop(ptls); + + // 4. check for objects to finalize + clear_weak_refs(); + // Record the length of the marked list since we need to + // mark the object moved to the marked list from the + // `finalizer_list` by `sweep_finalizer_list` + size_t orig_marked_len = finalizer_list_marked.len; + assert(gc_n_threads); + for (int i = 0; i < gc_n_threads; i++) { + jl_ptls_t ptls2 = gc_all_tls_states[i]; + if (ptls2 != NULL) + sweep_finalizer_list(&ptls2->finalizers); + } + if (prev_sweep_full) { + sweep_finalizer_list(&finalizer_list_marked); + orig_marked_len = 0; + } + assert(gc_n_threads); + for (int i = 0; i < gc_n_threads; i++) { + jl_ptls_t ptls2 = gc_all_tls_states[i]; + if (ptls2 != NULL) + gc_mark_finlist(mq, &ptls2->finalizers, 0); + } + gc_mark_finlist(mq, &finalizer_list_marked, orig_marked_len); + // "Flush" the mark stack before flipping the reset_age bit + // so that the objects are not incorrectly reset. + gc_mark_loop(ptls); + // Conservative marking relies on age to tell allocated objects + // and freelist entries apart. + mark_reset_age = !jl_gc_conservative_gc_support_enabled(); + // Reset the age and old bit for any unmarked objects referenced by the + // `to_finalize` list. These objects are only reachable from this list + // and should not be referenced by any old objects so this won't break + // the GC invariant. + gc_mark_finlist(mq, &to_finalize, 0); + gc_mark_loop(ptls); + mark_reset_age = 0; } - if (prev_sweep_full) { - sweep_finalizer_list(&finalizer_list_marked); - orig_marked_len = 0; - } - assert(gc_n_threads); - for (int i = 0; i < gc_n_threads; i++) { - jl_ptls_t ptls2 = gc_all_tls_states[i]; - if (ptls2 != NULL) - gc_mark_finlist(mq, &ptls2->finalizers, 0); - } - gc_mark_finlist(mq, &finalizer_list_marked, orig_marked_len); - // "Flush" the mark stack before flipping the reset_age bit - // so that the objects are not incorrectly reset. - gc_mark_loop(ptls); - // Conservative marking relies on age to tell allocated objects - // and freelist entries apart. - mark_reset_age = !jl_gc_conservative_gc_support_enabled(); - // Reset the age and old bit for any unmarked objects referenced by the - // `to_finalize` list. These objects are only reachable from this list - // and should not be referenced by any old objects so this won't break - // the GC invariant. - gc_mark_finlist(mq, &to_finalize, 0); - gc_mark_loop(ptls); - mark_reset_age = 0; gc_num.since_sweep += gc_num.allocd; JL_PROBE_GC_MARK_END(scanned_bytes, perm_scanned_bytes); @@ -3081,7 +3093,6 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) } } - // If the live data outgrows the suggested max_total_memory // we keep going with minimum intervals and full gcs until // we either free some space or get an OOM error. @@ -3106,15 +3117,24 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) // 6. start sweeping uint64_t start_sweep_time = jl_hrtime(); JL_PROBE_GC_SWEEP_BEGIN(sweep_full); - sweep_weak_refs(); - sweep_stack_pools(); - gc_sweep_foreign_objs(); - gc_sweep_other(ptls, sweep_full); - gc_scrub(); - gc_verify_tags(); - gc_sweep_pool(sweep_full); - if (sweep_full) - gc_sweep_perm_alloc(); + { + JL_TIMING(GC, Sweep); +#ifdef USE_TRACY + if (sweep_full) { + TracyCZoneCtx ctx = *(JL_TIMING_CURRENT_BLOCK->tracy_ctx); + TracyCZoneColor(ctx, 0xFFA500); + } +#endif + sweep_weak_refs(); + sweep_stack_pools(); + gc_sweep_foreign_objs(); + gc_sweep_other(ptls, sweep_full); + gc_scrub(); + gc_verify_tags(); + gc_sweep_pool(sweep_full); + if (sweep_full) + gc_sweep_perm_alloc(); + } JL_PROBE_GC_SWEEP_END(); uint64_t gc_end_time = jl_hrtime(); @@ -3243,22 +3263,10 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) jl_gc_state_set(ptls, old_state, JL_GC_STATE_WAITING); return; } -#ifdef USE_TRACY - static uint8_t first_time = 1; - if (first_time) { - first_time = 0; - TracyCFiberEnter("Main"); - } - TracyCFiberLeave; - TracyCFiberEnter("GC"); - { - int64_t tb; - jl_gc_get_total_bytes(&tb); - TracyCPlot("Heap size", ((double)tb) / (1024.0 * 1024.0)); - } -#endif -{ - JL_TIMING(GC); + + JL_TIMING_SUSPEND(GC, ct); + JL_TIMING(GC, GC); + int last_errno = errno; #ifdef _OS_WINDOWS_ DWORD last_error = GetLastError(); @@ -3311,6 +3319,7 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) // Doing this on all threads is racy (it's impossible to check // or wait for finalizers on other threads without dead lock). if (!ptls->finalizers_inhibited && ptls->locks.len == 0) { + JL_TIMING(GC, Finalizers); run_finalizers(ct); } JL_PROBE_GC_FINALIZER(); @@ -3321,15 +3330,9 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) SetLastError(last_error); #endif errno = last_errno; -} + #ifdef USE_TRACY - { - int64_t tb; - jl_gc_get_total_bytes(&tb); - TracyCPlot("Heap size", ((double)tb) / (1024.0 * 1024.0)); - } - TracyCFiberLeave; - TracyCFiberEnter("Main"); + TracyCPlot("Heap size", jl_gc_live_bytes()); #endif } @@ -3432,6 +3435,9 @@ void jl_gc_init(void) if (jl_options.heap_size_hint) jl_gc_set_max_memory(jl_options.heap_size_hint); +#ifdef USE_TRACY + TracyCPlotConfig("Heap size", TracyPlotFormatMemory, /* rectilinear */ 0, /* fill */ 1, /* color */ 0); +#endif t_start = jl_hrtime(); } diff --git a/src/gf.c b/src/gf.c index 0b01f5e8e6ee2..57a81deda2d36 100644 --- a/src/gf.c +++ b/src/gf.c @@ -314,7 +314,7 @@ jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_a // if inference doesn't occur (or can't finish), returns NULL instead jl_code_info_t *jl_type_infer(jl_method_instance_t *mi, size_t world, int force) { - JL_TIMING(INFERENCE); + JL_TIMING(INFERENCE, INFERENCE); if (jl_typeinf_func == NULL) return NULL; jl_task_t *ct = jl_current_task; @@ -337,6 +337,8 @@ jl_code_info_t *jl_type_infer(jl_method_instance_t *mi, size_t world, int force) fargs[0] = (jl_value_t*)jl_typeinf_func; fargs[1] = (jl_value_t*)mi; fargs[2] = jl_box_ulong(world); + + jl_timing_show_method_instance(mi, JL_TIMING_CURRENT_BLOCK); #ifdef TRACE_INFERENCE if (mi->specTypes != (jl_value_t*)jl_emptytuple_type) { jl_printf(JL_STDERR,"inference on "); @@ -391,6 +393,7 @@ jl_code_info_t *jl_type_infer(jl_method_instance_t *mi, size_t world, int force) } JL_GC_POP(); #endif + return src; } @@ -1923,9 +1926,10 @@ static int is_replacing(jl_value_t *type, jl_method_t *m, jl_method_t *const *d, JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method, jl_tupletype_t *simpletype) { - JL_TIMING(ADD_METHOD); + JL_TIMING(ADD_METHOD, ADD_METHOD); assert(jl_is_method(method)); assert(jl_is_mtable(mt)); + jl_timing_show((jl_value_t *)method, JL_TIMING_CURRENT_BLOCK); jl_value_t *type = method->sig; jl_value_t *oldvalue = NULL; jl_array_t *oldmi = NULL; @@ -2205,7 +2209,7 @@ jl_method_instance_t *jl_method_lookup(jl_value_t **args, size_t nargs, size_t w JL_DLLEXPORT jl_value_t *jl_matching_methods(jl_tupletype_t *types, jl_value_t *mt, int lim, int include_ambiguous, size_t world, size_t *min_valid, size_t *max_valid, int *ambig) { - JL_TIMING(METHOD_MATCH); + JL_TIMING(METHOD_MATCH, METHOD_MATCH); if (ambig != NULL) *ambig = 0; jl_value_t *unw = jl_unwrap_unionall((jl_value_t*)types); @@ -2930,7 +2934,7 @@ STATIC_INLINE jl_method_instance_t *jl_lookup_generic_(jl_value_t *F, jl_value_t int64_t last_alloc; if (i == 4) { // if no method was found in the associative cache, check the full cache - JL_TIMING(METHOD_LOOKUP_FAST); + JL_TIMING(METHOD_LOOKUP_FAST, METHOD_LOOKUP_FAST); mt = jl_gf_mtable(F); jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); entry = NULL; @@ -2973,7 +2977,7 @@ STATIC_INLINE jl_method_instance_t *jl_lookup_generic_(jl_value_t *F, jl_value_t assert(tt); JL_LOCK(&mt->writelock); // cache miss case - JL_TIMING(METHOD_LOOKUP_SLOW); + JL_TIMING(METHOD_LOOKUP_SLOW, METHOD_LOOKUP_SLOW); mfunc = jl_mt_assoc_by_type(mt, tt, world); JL_UNLOCK(&mt->writelock); JL_GC_POP(); diff --git a/src/ircode.c b/src/ircode.c index f967dd1a29f51..71ee292cbc397 100644 --- a/src/ircode.c +++ b/src/ircode.c @@ -760,7 +760,7 @@ static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) { - JL_TIMING(AST_COMPRESS); + JL_TIMING(AST_COMPRESS, AST_COMPRESS); JL_LOCK(&m->writelock); // protect the roots array (Might GC) assert(jl_is_method(m)); assert(jl_is_code_info(code)); @@ -855,7 +855,7 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t { if (jl_is_code_info(data)) return (jl_code_info_t*)data; - JL_TIMING(AST_UNCOMPRESS); + JL_TIMING(AST_UNCOMPRESS, AST_UNCOMPRESS); JL_LOCK(&m->writelock); // protect the roots array (Might GC) assert(jl_is_method(m)); assert(jl_typeis(data, jl_array_uint8_type)); diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index c7e202b98efab..7c68b78c27850 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -192,6 +192,8 @@ static jl_callptr_t _jl_compile_codeinst( "invalid world for method-instance"); assert(src && jl_is_code_info(src)); + JL_TIMING(LLVM_MODULE_FINISH, LLVM_MODULE_FINISH); + jl_callptr_t fptr = NULL; // emit the code in LLVM IR form jl_codegen_params_t params(std::move(context), jl_ExecutionEngine->getDataLayout(), jl_ExecutionEngine->getTargetTriple()); // Locks the context @@ -245,10 +247,10 @@ static jl_callptr_t _jl_compile_codeinst( MaxWorkqueueSize.updateMax(emitted.size()); IndirectCodeinsts += emitted.size() - 1; } - JL_TIMING(LLVM_MODULE_FINISH); for (auto &def : emitted) { jl_code_instance_t *this_code = def.first; + jl_timing_show_func_sig(this_code->def->specTypes, JL_TIMING_CURRENT_BLOCK); jl_llvm_functions_t decls = std::get<1>(def.second); jl_callptr_t addr; bool isspecsig = false; @@ -1175,7 +1177,7 @@ namespace { } } - JL_TIMING(LLVM_OPT); + JL_TIMING(LLVM_OPT, LLVM_OPT); //Run the optimization assert(!verifyModule(M, &errs())); @@ -1410,7 +1412,7 @@ void JuliaOJIT::addGlobalMapping(StringRef Name, uint64_t Addr) void JuliaOJIT::addModule(orc::ThreadSafeModule TSM) { - JL_TIMING(LLVM_MODULE_FINISH); + JL_TIMING(LLVM_MODULE_FINISH, LLVM_MODULE_FINISH); ++ModulesAdded; orc::SymbolLookupSet NewExports; TSM.withModuleDo([&](Module &M) JL_NOTSAFEPOINT { diff --git a/src/jltypes.c b/src/jltypes.c index 4a451e9b70e80..fe857e03817b5 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -732,7 +732,7 @@ static ssize_t lookup_type_idx_linearvalue(jl_svec_t *cache, jl_value_t *key1, j static jl_value_t *lookup_type(jl_typename_t *tn JL_PROPAGATES_ROOT, jl_value_t **key, size_t n) { - JL_TIMING(TYPE_CACHE_LOOKUP); + JL_TIMING(TYPE_CACHE_LOOKUP, TYPE_CACHE_LOOKUP); if (tn == jl_type_typename) { assert(n == 1); jl_value_t *uw = jl_unwrap_unionall(key[0]); @@ -753,7 +753,7 @@ static jl_value_t *lookup_type(jl_typename_t *tn JL_PROPAGATES_ROOT, jl_value_t static jl_value_t *lookup_typevalue(jl_typename_t *tn, jl_value_t *key1, jl_value_t **key, size_t n, int leaf) { - JL_TIMING(TYPE_CACHE_LOOKUP); + JL_TIMING(TYPE_CACHE_LOOKUP, TYPE_CACHE_LOOKUP); unsigned hv = typekeyvalue_hash(tn, key1, key, n, leaf); if (hv) { jl_svec_t *cache = jl_atomic_load_relaxed(&tn->cache); @@ -874,7 +874,7 @@ static int is_cacheable(jl_datatype_t *type) void jl_cache_type_(jl_datatype_t *type) { - JL_TIMING(TYPE_CACHE_INSERT); + JL_TIMING(TYPE_CACHE_INSERT, TYPE_CACHE_INSERT); assert(is_cacheable(type)); jl_value_t **key = jl_svec_data(type->parameters); int n = jl_svec_len(type->parameters); diff --git a/src/julia.h b/src/julia.h index fc8a4b8daa524..a1e0a5a46c0ce 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1934,6 +1934,10 @@ typedef struct _jl_task_t { uint16_t priority; // hidden state: + +#ifdef USE_TRACY + const char *name; +#endif // id of owning thread - does not need to be defined until the task runs _Atomic(int16_t) tid; // threadpool id diff --git a/src/method.c b/src/method.c index ee333bebccedf..de78d9ab884ed 100644 --- a/src/method.c +++ b/src/method.c @@ -564,9 +564,10 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo, siz return (jl_code_info_t*)jl_copy_ast((jl_value_t*)uninferred); } - JL_TIMING(STAGED_FUNCTION); + JL_TIMING(STAGED_FUNCTION, STAGED_FUNCTION); jl_value_t *tt = linfo->specTypes; jl_method_t *def = linfo->def.method; + jl_timing_show_method_instance(linfo, JL_TIMING_CURRENT_BLOCK); jl_value_t *generator = def->generator; assert(generator != NULL); assert(jl_is_method(def)); diff --git a/src/rtutils.c b/src/rtutils.c index dd606f38d065c..41e45cd468039 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -1294,7 +1294,11 @@ JL_DLLEXPORT size_t jl_static_show_func_sig(JL_STREAM *s, jl_value_t *type) JL_N for (i = 1; i < tl; i++) { jl_value_t *tp = jl_tparam(type, i); if (i != tl - 1) { - n += jl_static_show_x(s, tp, depth); + if (jl_is_datatype(tp)) { + n += jl_printf(s, "%s", jl_symbol_name(((jl_datatype_t*)tp)->name->name)); + } else { + n += jl_static_show_x(s, tp, depth); + } n += jl_printf(s, ", "); } else { @@ -1302,7 +1306,11 @@ JL_DLLEXPORT size_t jl_static_show_func_sig(JL_STREAM *s, jl_value_t *type) JL_N tp = jl_unwrap_vararg(tp); if (jl_is_unionall(tp)) n += jl_printf(s, "("); - n += jl_static_show_x(s, tp, depth); + if (jl_is_datatype(tp)) { + n += jl_printf(s, "%s", jl_symbol_name(((jl_datatype_t*)tp)->name->name)); + } else { + n += jl_static_show_x(s, tp, depth); + } if (jl_is_unionall(tp)) n += jl_printf(s, ")"); n += jl_printf(s, "..."); diff --git a/src/safepoint.c b/src/safepoint.c index 1ff26d616a5d8..19eca4bf6f00d 100644 --- a/src/safepoint.c +++ b/src/safepoint.c @@ -150,8 +150,10 @@ void jl_safepoint_end_gc(void) void jl_safepoint_wait_gc(void) { + jl_task_t *ct = jl_current_task; (void)ct; + JL_TIMING_SUSPEND(GC_SAFEPOINT, ct); // The thread should have set this is already - assert(jl_atomic_load_relaxed(&jl_current_task->ptls->gc_state) != 0); + assert(jl_atomic_load_relaxed(&ct->ptls->gc_state) != 0); // Use normal volatile load in the loop for speed until GC finishes. // Then use an acquire load to make sure the GC result is visible on this thread. while (jl_atomic_load_relaxed(&jl_gc_running) || jl_atomic_load_acquire(&jl_gc_running)) { diff --git a/src/staticdata.c b/src/staticdata.c index d6c92ac93a851..5c6da3f200edb 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -2520,7 +2520,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli { jl_gc_collect(JL_GC_FULL); jl_gc_collect(JL_GC_INCREMENTAL); // sweep finalizers - JL_TIMING(SYSIMG_DUMP); + JL_TIMING(SYSIMG_DUMP, SYSIMG_DUMP); // iff emit_split // write header and src_text to one file f/s @@ -2672,7 +2672,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl jl_array_t **ext_targets, jl_array_t **edges, char **base, arraylist_t *ccallable_list, pkgcachesizes *cachesizes) JL_GC_DISABLED { - JL_TIMING(SYSIMG_LOAD); + JL_TIMING(SYSIMG_LOAD, SYSIMG_LOAD); int en = jl_gc_enable(0); ios_t sysimg, const_data, symbols, relocs, gvar_record, fptr_record; jl_serializer_state s; diff --git a/src/task.c b/src/task.c index 1035d54f2ce9a..e73b19563e336 100644 --- a/src/task.c +++ b/src/task.c @@ -646,13 +646,7 @@ JL_DLLEXPORT void jl_switch(void) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER int finalizers_inhibited = ptls->finalizers_inhibited; ptls->finalizers_inhibited = 0; -#ifdef ENABLE_TIMINGS - jl_timing_block_t *blk = ptls->timing_stack; - if (blk) - jl_timing_block_stop(blk); - ptls->timing_stack = NULL; -#endif - + jl_timing_block_t *blk = jl_timing_block_exit_task(ct, ptls); ctx_switch(ct); #ifdef MIGRATE_TASKS @@ -672,15 +666,7 @@ JL_DLLEXPORT void jl_switch(void) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER 0 != ct->ptls && 0 == ptls->finalizers_inhibited); ptls->finalizers_inhibited = finalizers_inhibited; - -#ifdef ENABLE_TIMINGS - assert(ptls->timing_stack == NULL); - ptls->timing_stack = blk; - if (blk) - jl_timing_block_start(blk); -#else - (void)ct; -#endif + jl_timing_block_enter_task(ct, ptls, blk); (void)blk; sig_atomic_t other_defer_signal = ptls->defer_signal; ptls->defer_signal = defer_signal; @@ -1082,6 +1068,30 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion t->start = start; t->result = jl_nothing; t->donenotify = completion_future; +#ifdef USE_TRACY + jl_value_t *start_type = jl_typeof(t->start); + const char *start_name = ""; + if (jl_is_datatype(start_type)) + start_name = jl_symbol_name(((jl_datatype_t *) start_type)->name->name); + + static uint16_t task_id = 1; + + // XXX: Tracy uses this as a handle internally and requires that this + // string live forever, so this allocation is intentionally leaked. + char *fiber_name; + if (start_name[0] == '#') { + jl_method_instance_t *mi = jl_method_lookup(&t->start, 1, jl_get_world_counter()); + size_t fiber_name_len = strlen(jl_symbol_name(mi->def.method->file)) + 22; // 22 characters in "Task 65535 (:0000000)\0" + fiber_name = (char *)malloc(fiber_name_len); + snprintf(fiber_name, fiber_name_len, "Task %d (%s:%d)", task_id++, jl_symbol_name(mi->def.method->file), mi->def.method->line); + } else { + size_t fiber_name_len = strlen(start_name) + 16; // 16 characters in "Task 65535 (\"\")\0" + fiber_name = (char *)malloc(fiber_name_len); + snprintf(fiber_name, fiber_name_len, "Task %d (\"%s\")", task_id++, start_name); + } + + t->name = fiber_name; +#endif jl_atomic_store_relaxed(&t->_isexception, 0); // Inherit logger state from parent task t->logstate = ct->logstate; @@ -1237,6 +1247,7 @@ CFI_NORETURN ct->started = 1; JL_PROBE_RT_START_TASK(ct); + jl_timing_block_enter_task(ct, ptls, NULL); if (jl_atomic_load_relaxed(&ct->_isexception)) { record_backtrace(ptls, 0); jl_push_excstack(&ct->excstack, ct->result, @@ -1249,14 +1260,8 @@ CFI_NORETURN ptls->defer_signal = 0; jl_sigint_safepoint(ptls); } -//#ifdef USE_TRACY - //TracyFiberEnter(fiber); -//#endif - { - // TODO: Re-enable - //JL_TIMING(ROOT); - res = jl_apply(&ct->start, 1); - } + JL_TIMING(ROOT, ROOT); + res = jl_apply(&ct->start, 1); } JL_CATCH { res = jl_current_exception(); @@ -1265,9 +1270,6 @@ CFI_NORETURN } skip_pop_exception:; } -//#ifdef USE_TRACY - //TracyFiberLeave(fiber); -//#endif ct->result = res; jl_gc_wb(ct, ct->result); jl_finish_task(ct); @@ -1678,6 +1680,12 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) ct->stkbuf = stack; ct->bufsz = ssize; } + +#ifdef USE_TRACY + char *unique_string = (char *)malloc(strlen("Root") + 1); + strcpy(unique_string, "Root"); + ct->name = unique_string; +#endif ct->started = 1; ct->next = jl_nothing; ct->queue = jl_nothing; @@ -1710,6 +1718,8 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) ct->ctx.asan_fake_stack = NULL; #endif + jl_timing_block_enter_task(ct, ptls, NULL); + #ifdef COPY_STACKS // initialize the base_ctx from which all future copy_stacks will be copies if (always_copy_stacks) { diff --git a/src/timing.c b/src/timing.c index 929a09305f993..b2a483747dc6e 100644 --- a/src/timing.c +++ b/src/timing.c @@ -18,7 +18,24 @@ extern "C" { #endif static uint64_t t0; -JL_DLLEXPORT uint64_t jl_timing_data[(int)JL_TIMING_LAST] = {0}; +#ifdef USE_TRACY +/** + * These sources often generate millions of events / minute. Although Tracy + * can generally keep up with that, those events also bloat the saved ".tracy" + * files, so we disable them by default. + **/ +JL_DLLEXPORT uint64_t jl_timing_enable_mask = 0xFFFFFFFFFFFFFFFF & + ~(1ull << JL_TIMING_ROOT) & + ~(1ull << JL_TIMING_TYPE_CACHE_LOOKUP) & + ~(1ull << JL_TIMING_METHOD_MATCH) & + ~(1ull << JL_TIMING_METHOD_LOOKUP_FAST) & + ~(1ull << JL_TIMING_AST_COMPRESS) & + ~(1ull << JL_TIMING_AST_UNCOMPRESS); +#else +JL_DLLEXPORT uint64_t jl_timing_enable_mask = 0xFFFFFFFFFFFFFFFF; +#endif + +JL_DLLEXPORT uint64_t jl_timing_counts[(int)JL_TIMING_LAST] = {0}; const char *jl_timing_names[(int)JL_TIMING_LAST] = { #define X(name) #name @@ -31,13 +48,13 @@ void jl_print_timings(void) uint64_t total_time = cycleclock() - t0; uint64_t root_time = total_time; for (int i = 0; i < JL_TIMING_LAST; i++) { - root_time -= jl_timing_data[i]; + root_time -= jl_timing_counts[i]; } - jl_timing_data[0] = root_time; + jl_timing_counts[0] = root_time; for (int i = 0; i < JL_TIMING_LAST; i++) { - if (jl_timing_data[i] != 0) + if (jl_timing_counts[i] != 0) fprintf(stderr, "%-25s : %5.2f %% %" PRIu64 "\n", jl_timing_names[i], - 100 * (((double)jl_timing_data[i]) / total_time), jl_timing_data[i]); + 100 * (((double)jl_timing_counts[i]) / total_time), jl_timing_counts[i]); } } @@ -62,20 +79,151 @@ jl_timing_block_t *jl_pop_timing_block(jl_timing_block_t *cur_block) return cur_block->prev; } -void jl_timing_block_start(jl_timing_block_t *cur_block) +void jl_timing_block_enter_task(jl_task_t *ct, jl_ptls_t ptls, jl_timing_block_t *prev_blk) +{ + if (prev_blk != NULL) { + assert(ptls->timing_stack == NULL); + + ptls->timing_stack = prev_blk; + if (prev_blk != NULL) { + _COUNTS_START(&prev_blk->counts_ctx, cycleclock()); + } + } + +#ifdef USE_TRACY + TracyCFiberEnter(ct->name); +#else + (void)ct; +#endif +} + +jl_timing_block_t *jl_timing_block_exit_task(jl_task_t *ct, jl_ptls_t ptls) +{ +#ifdef USE_TRACY + // Tracy is fairly strict about not leaving a fiber that + // hasn't been entered, which happens often when + // connecting to a running Julia session. + // + // Eventually, Tracy will support telling the server that + // which fibers are active upon connection, but until then + // work around around the problem by just entering the new + // fiber directly, which implicitly leaves any active fibers. + + //TracyCFiberLeave; +#endif + (void)ct; + + jl_timing_block_t *blk = ptls->timing_stack; + ptls->timing_stack = NULL; + + if (blk != NULL) { + _COUNTS_STOP(&blk->counts_ctx, cycleclock()); + } + return blk; +} + +static inline const char *gnu_basename(const char *path) { - _jl_timing_block_start(cur_block, cycleclock()); + char *base = strrchr(path, '/'); + return base ? base+1 : path; } -void jl_timing_block_stop(jl_timing_block_t *cur_block) +JL_DLLEXPORT void jl_timing_show(jl_value_t *v, jl_timing_block_t *cur_block) { - _jl_timing_block_stop(cur_block, cycleclock()); +#ifdef USE_TRACY + ios_t buf; + ios_mem(&buf, IOS_INLSIZE); + buf.growable = 0; // Restrict to inline buffer to avoid allocation + + jl_static_show((JL_STREAM*)&buf, v); + if (buf.size == buf.maxsize) + memset(&buf.buf[IOS_INLSIZE - 3], '.', 3); + + TracyCZoneText(*(cur_block->tracy_ctx), buf.buf, buf.size); +#endif +} + +JL_DLLEXPORT void jl_timing_show_module(jl_module_t *m, jl_timing_block_t *cur_block) +{ +#ifdef USE_TRACY + const char *module_name = jl_symbol_name(m->name); + TracyCZoneText(*(cur_block->tracy_ctx), module_name, strlen(module_name)); +#endif +} + +JL_DLLEXPORT void jl_timing_show_filename(const char *path, jl_timing_block_t *cur_block) +{ +#ifdef USE_TRACY + const char *filename = gnu_basename(path); + TracyCZoneText(*(cur_block->tracy_ctx), filename, strlen(filename)); +#endif +} + +JL_DLLEXPORT void jl_timing_show_method_instance(jl_method_instance_t *mi, jl_timing_block_t *cur_block) +{ + jl_timing_show_func_sig(mi->specTypes, cur_block); + jl_method_t *def = mi->def.method; + jl_timing_printf(cur_block, "%s:%d in %s", + gnu_basename(jl_symbol_name(def->file)), + def->line, + jl_symbol_name(def->module->name)); +} + +JL_DLLEXPORT void jl_timing_show_func_sig(jl_value_t *v, jl_timing_block_t *cur_block) +{ +#ifdef USE_TRACY + ios_t buf; + ios_mem(&buf, IOS_INLSIZE); + buf.growable = 0; // Restrict to inline buffer to avoid allocation + + jl_static_show_func_sig((JL_STREAM*)&buf, v); + if (buf.size == buf.maxsize) + memset(&buf.buf[IOS_INLSIZE - 3], '.', 3); + + TracyCZoneText(*(cur_block->tracy_ctx), buf.buf, buf.size); +#endif +} + +JL_DLLEXPORT void jl_timing_printf(jl_timing_block_t *cur_block, const char *format, ...) +{ + va_list args; + va_start(args, format); + +#ifdef USE_TRACY + ios_t buf; + ios_mem(&buf, IOS_INLSIZE); + buf.growable = 0; // Restrict to inline buffer to avoid allocation + + jl_vprintf((JL_STREAM*)&buf, format, args); + if (buf.size == buf.maxsize) + memset(&buf.buf[IOS_INLSIZE - 3], '.', 3); + + TracyCZoneText(*(cur_block->tracy_ctx), buf.buf, buf.size); +#endif + va_end(args); +} + +JL_DLLEXPORT int jl_timing_set_enable(const char *subsystem, uint8_t enabled) +{ + for (int i = 0; i < JL_TIMING_LAST; i++) { + if (strcmp(subsystem, jl_timing_names[i]) == 0) { + uint64_t subsystem_bit = (1ul << i); + if (enabled) { + jl_timing_enable_mask |= subsystem_bit; + } else { + jl_timing_enable_mask &= ~subsystem_bit; + } + return 0; + } + } + return -1; } #else void jl_init_timing(void) { } void jl_destroy_timing(void) { } +JL_DLLEXPORT int jl_timing_set_enable(const char *subsystem, uint8_t enabled) { return -1; } #endif diff --git a/src/timing.h b/src/timing.h index c9a1fd1fb4807..920440edf9da7 100644 --- a/src/timing.h +++ b/src/timing.h @@ -6,14 +6,41 @@ #ifdef __cplusplus extern "C" { #endif + void jl_init_timing(void); void jl_destroy_timing(void) JL_NOTSAFEPOINT; + +// Update the enable bit-mask to enable/disable tracing events for +// the subsystem in `jl_timing_names` matching the provided string. +// +// Returns -1 if no matching sub-system was found. +int jl_timing_set_enable(const char *subsystem, uint8_t enabled); + #ifdef __cplusplus } #endif -#ifndef ENABLE_TIMINGS -#define JL_TIMING(owner) +#ifdef __cplusplus +#define HAVE_TIMING_SUPPORT +#elif defined(_COMPILER_CLANG_) +#define HAVE_TIMING_SUPPORT +#elif defined(_COMPILER_GCC_) +#define HAVE_TIMING_SUPPORT +#endif + +#if !defined( ENABLE_TIMINGS ) || !defined( HAVE_TIMING_SUPPORT ) + +#define JL_TIMING(subsystem, event) +#define JL_TIMING_SUSPEND(subsystem, ct) +#define jl_timing_show(v, b) +#define jl_timing_show_module(m, b) +#define jl_timing_show_filename(f, b) +#define jl_timing_show_method_instance(mi, b) +#define jl_timing_show_func_sig(tt, b) +#define jl_timing_block_enter_task(ct, ptls, blk) +#define jl_timing_block_exit_task(ct, ptls) ((jl_timing_block_t *)NULL) +#define jl_pop_timing_block(blk) + #else #include "julia_assert.h" @@ -26,23 +53,28 @@ extern "C" { #endif void jl_print_timings(void); jl_timing_block_t *jl_pop_timing_block(jl_timing_block_t *cur_block); -void jl_timing_block_start(jl_timing_block_t *cur_block); -void jl_timing_block_stop(jl_timing_block_t *cur_block); +void jl_timing_block_enter_task(jl_task_t *ct, jl_ptls_t ptls, jl_timing_block_t *prev_blk); +jl_timing_block_t *jl_timing_block_exit_task(jl_task_t *ct, jl_ptls_t ptls); + +// Add the output of `jl_static_show(x)` as a text annotation to the +// profiling region corresponding to `cur_block`. +// +// If larger than IOS_INLSIZE (~80 characters), text is truncated. +void jl_timing_show(jl_value_t *v, jl_timing_block_t *cur_block); +void jl_timing_show_module(jl_module_t *m, jl_timing_block_t *cur_block); +void jl_timing_show_filename(const char *path, jl_timing_block_t *cur_block); +void jl_timing_show_method_instance(jl_method_instance_t *mi, jl_timing_block_t *cur_block); +void jl_timing_show_func_sig(jl_value_t *v, jl_timing_block_t *cur_block); +void jl_timing_printf(jl_timing_block_t *cur_block, const char *format, ...); #ifdef __cplusplus } #endif #ifdef __cplusplus -#define HAVE_TIMING_SUPPORT -#elif defined(_COMPILER_CLANG_) -#define HAVE_TIMING_SUPPORT -#elif defined(_COMPILER_GCC_) -#define HAVE_TIMING_SUPPORT -#endif - -#ifndef HAVE_TIMING_SUPPORT -#define JL_TIMING(owner) +#define JL_TIMING_CURRENT_BLOCK (&__timing_block.block) #else +#define JL_TIMING_CURRENT_BLOCK (&__timing_block) +#endif #define JL_TIMING_OWNERS \ X(ROOT), \ @@ -77,23 +109,61 @@ enum jl_timing_owners { JL_TIMING_LAST }; -extern uint64_t jl_timing_data[(int)JL_TIMING_LAST]; -extern const char *jl_timing_names[(int)JL_TIMING_LAST]; +/** + * Timing back-ends differ in terms of whether they support nested + * and asynchronous events. + **/ + +/** + * Timing Backend: Aggregated timing counts (implemented in timing.c) + **/ + +#define USE_COUNTS + +#ifdef USE_COUNTS +#define _COUNTS_CTX_MEMBER jl_timing_counts_t counts_ctx; +#define _COUNTS_CTOR(block, owner) _jl_timing_counts_ctor(block, owner) +#define _COUNTS_DESTROY(block) _jl_timing_counts_destroy(block) +#define _COUNTS_START(block, t) _jl_timing_counts_start(block, t) +#define _COUNTS_STOP(block, t) _jl_timing_counts_stop(block, t) +#else +#define _COUNTS_CTX_MEMBER +#define _COUNTS_CTOR(block, owner) +#define _COUNTS_DESTROY(block) +#define _COUNTS_START(block, t) +#define _COUNTS_STOP(block, t) +#endif + +/** + * Timing Backend: Tracy + **/ -struct _jl_timing_block_t { // typedef in julia.h #ifdef USE_TRACY - TracyCZoneCtx *tracy_ctx; +#define _TRACY_CTX_MEMBER TracyCZoneCtx *tracy_ctx; +#define _TRACY_CTOR(context, name, enable) TracyCZoneN(__tracy_ctx, name, (enable)); \ + (context) = &__tracy_ctx +#define _TRACY_DESTROY(ctx) TracyCZoneEnd(*ctx) +#else +#define _TRACY_CTX_MEMBER +#define _TRACY_CTOR(context, name, enable) +#define _TRACY_DESTROY(block) #endif - jl_timing_block_t *prev; + +/** + * Implementation: Aggregated counts back-end + **/ + +extern uint64_t jl_timing_counts[(int)JL_TIMING_LAST]; +typedef struct _jl_timing_counts_t { uint64_t total; uint64_t t0; int owner; #ifdef JL_DEBUG_BUILD uint8_t running; #endif -}; +} jl_timing_counts_t; -STATIC_INLINE void _jl_timing_block_stop(jl_timing_block_t *block, uint64_t t) JL_NOTSAFEPOINT { +STATIC_INLINE void _jl_timing_counts_stop(jl_timing_counts_t *block, uint64_t t) JL_NOTSAFEPOINT { #ifdef JL_DEBUG_BUILD assert(block->running); block->running = 0; @@ -101,7 +171,7 @@ STATIC_INLINE void _jl_timing_block_stop(jl_timing_block_t *block, uint64_t t) J block->total += t - block->t0; } -STATIC_INLINE void _jl_timing_block_start(jl_timing_block_t *block, uint64_t t) JL_NOTSAFEPOINT { +STATIC_INLINE void _jl_timing_counts_start(jl_timing_counts_t *block, uint64_t t) JL_NOTSAFEPOINT { #ifdef JL_DEBUG_BUILD assert(!block->running); block->running = 1; @@ -109,40 +179,75 @@ STATIC_INLINE void _jl_timing_block_start(jl_timing_block_t *block, uint64_t t) block->t0 = t; } -STATIC_INLINE uint64_t _jl_timing_block_init(jl_timing_block_t *block, int owner) JL_NOTSAFEPOINT { - uint64_t t = cycleclock(); +STATIC_INLINE void _jl_timing_counts_ctor(jl_timing_counts_t *block, int owner) JL_NOTSAFEPOINT { block->owner = owner; block->total = 0; #ifdef JL_DEBUG_BUILD block->running = 0; #endif - _jl_timing_block_start(block, t); - return t; } +STATIC_INLINE void _jl_timing_counts_destroy(jl_timing_counts_t *block) JL_NOTSAFEPOINT { + jl_timing_counts[block->owner] += block->total; +} + +/** + * Top-level jl_timing implementation + **/ + +extern uint64_t jl_timing_enable_mask; +extern const char *jl_timing_names[(int)JL_TIMING_LAST]; +struct _jl_timing_block_t { // typedef in julia.h + struct _jl_timing_block_t *prev; + _TRACY_CTX_MEMBER + _COUNTS_CTX_MEMBER +}; + STATIC_INLINE void _jl_timing_block_ctor(jl_timing_block_t *block, int owner) JL_NOTSAFEPOINT { - uint64_t t = _jl_timing_block_init(block, owner); + uint64_t t = cycleclock(); + _COUNTS_CTOR(&block->counts_ctx, owner); + _COUNTS_START(&block->counts_ctx, t); + jl_task_t *ct = jl_current_task; jl_timing_block_t **prevp = &ct->ptls->timing_stack; block->prev = *prevp; - if (block->prev) - _jl_timing_block_stop(block->prev, t); + if (block->prev) { + _COUNTS_STOP(&block->prev->counts_ctx, t); + } *prevp = block; } STATIC_INLINE void _jl_timing_block_destroy(jl_timing_block_t *block) JL_NOTSAFEPOINT { -#ifdef USE_TRACY - TracyCZoneEnd(*(block->tracy_ctx)); -#endif uint64_t t = cycleclock(); + + _COUNTS_STOP(&block->counts_ctx, t); + _COUNTS_DESTROY(&block->counts_ctx); + _TRACY_DESTROY(block->tracy_ctx); + jl_task_t *ct = jl_current_task; - _jl_timing_block_stop(block, t); - jl_timing_data[block->owner] += block->total; jl_timing_block_t **pcur = &ct->ptls->timing_stack; assert(*pcur == block); *pcur = block->prev; - if (block->prev) - _jl_timing_block_start(block->prev, t); + if (block->prev) { + _COUNTS_START(&block->prev->counts_ctx, t); + } +} + +typedef struct _jl_timing_suspend_t { + jl_task_t *ct; +} jl_timing_suspend_t; + +STATIC_INLINE void _jl_timing_suspend_ctor(jl_timing_suspend_t *suspend, const char *subsystem, jl_task_t *ct) JL_NOTSAFEPOINT { + suspend->ct = ct; +#ifdef USE_TRACY + TracyCFiberEnter(subsystem); +#endif +} + +STATIC_INLINE void _jl_timing_suspend_destroy(jl_timing_suspend_t *suspend) JL_NOTSAFEPOINT { +#ifdef USE_TRACY + TracyCFiberEnter(suspend->ct->name); +#endif } #ifdef __cplusplus @@ -159,30 +264,38 @@ struct jl_timing_block_cpp_t { jl_timing_block_cpp_t& operator=(const jl_timing_block_cpp_t &) = delete; jl_timing_block_cpp_t& operator=(const jl_timing_block_cpp_t &&) = delete; }; -#ifdef USE_TRACY -#define JL_TIMING(owner) jl_timing_block_cpp_t __timing_block(JL_TIMING_ ## owner); \ - TracyCZoneN(__tracy_ctx, #owner, strcmp(#owner, "ROOT")); \ - __timing_block.block.tracy_ctx = &__tracy_ctx; -#else -#define JL_TIMING(owner) jl_timing_block_cpp_t __timing_block(JL_TIMING_ ## owner) -#endif -#else -#ifdef USE_TRACY -#define JL_TIMING(owner) \ - __attribute__((cleanup(_jl_timing_block_destroy))) \ - jl_timing_block_t __timing_block; \ - _jl_timing_block_ctor(&__timing_block, JL_TIMING_ ## owner); \ - TracyCZoneN(__tracy_ctx, #owner, 1); \ - __timing_block.tracy_ctx = &__tracy_ctx; +#define JL_TIMING(subsystem, event) jl_timing_block_cpp_t __timing_block(JL_TIMING_ ## subsystem); \ + _TRACY_CTOR(__timing_block.block.tracy_ctx, #event, (jl_timing_enable_mask >> (JL_TIMING_ ## subsystem)) & 1) #else -#define JL_TIMING(owner) \ +#define JL_TIMING(subsystem, event) \ __attribute__((cleanup(_jl_timing_block_destroy))) \ jl_timing_block_t __timing_block; \ - _jl_timing_block_ctor(&__timing_block, JL_TIMING_ ## owner) -#endif + _jl_timing_block_ctor(&__timing_block, JL_TIMING_ ## subsystem); \ + _TRACY_CTOR(__timing_block.tracy_ctx, #event, (jl_timing_enable_mask >> (JL_TIMING_ ## subsystem)) & 1) #endif +#ifdef __cplusplus +struct jl_timing_suspend_cpp_t { + jl_timing_suspend_t suspend; + jl_timing_suspend_cpp_t(const char *subsystem, jl_task_t *ct) JL_NOTSAFEPOINT { + _jl_timing_suspend_ctor(&suspend, subsystem, ct); + } + ~jl_timing_suspend_cpp_t() JL_NOTSAFEPOINT { + _jl_timing_suspend_destroy(&suspend); + } + jl_timing_suspend_cpp_t(const jl_timing_block_cpp_t&) = delete; + jl_timing_suspend_cpp_t(const jl_timing_block_cpp_t&&) = delete; + jl_timing_suspend_cpp_t& operator=(const jl_timing_block_cpp_t &) = delete; + jl_timing_suspend_cpp_t& operator=(const jl_timing_block_cpp_t &&) = delete; +}; +#define JL_TIMING_SUSPEND(subsystem, ct) jl_timing_suspend_cpp_t __suspend_block(#subsystem, ct) +#else +#define JL_TIMING_SUSPEND(subsystem, ct) \ + __attribute__((cleanup(_jl_timing_suspend_destroy))) \ + jl_timing_suspend_t __timing_suspend; \ + _jl_timing_suspend_ctor(&__timing_suspend, #subsystem, ct) #endif + #endif #endif diff --git a/src/toplevel.c b/src/toplevel.c index 6f29c0a82d617..ad282a50d91ac 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -64,7 +64,8 @@ static jl_function_t *jl_module_get_initializer(jl_module_t *m JL_PROPAGATES_ROO void jl_module_run_initializer(jl_module_t *m) { - JL_TIMING(INIT_MODULE); + JL_TIMING(INIT_MODULE, INIT_MODULE); + jl_timing_show_module(m, JL_TIMING_CURRENT_BLOCK); jl_function_t *f = jl_module_get_initializer(m); if (f == NULL) return; From e9ac58403833bdf778868d6f269ddedd3e828c2e Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 28 Mar 2023 10:53:34 -0400 Subject: [PATCH 450/775] Add `WAIT_FOR_TRACY` environment variable This environmental variable instructs the Julia runtime to wait until it receives a connection from the Tracy profiler before starting. This allows us to get a complete trace, without having to accumulate a large queue of events to send over the wire later. --- src/jlapi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/jlapi.c b/src/jlapi.c index 5c6f01ab86a88..8f5e3e6cb13dc 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -15,6 +15,10 @@ #include "julia_assert.h" #include "julia_internal.h" +#ifdef USE_TRACY +#include "tracy/TracyC.h" +#endif + #ifdef __cplusplus #include extern "C" { @@ -680,6 +684,11 @@ static void rr_detach_teleport(void) { JL_DLLEXPORT int jl_repl_entrypoint(int argc, char *argv[]) { +#ifdef USE_TRACY + if (getenv("WAIT_FOR_TRACY")) + while (!TracyCIsConnected) ; // Wait for connection +#endif + // no-op on Windows, note that the caller must have already converted // from `wchar_t` to `UTF-8` already if we're running on Windows. uv_setup_args(argc, argv); From c90b266b39502debff43f608f50919332b650598 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Wed, 5 Apr 2023 06:45:21 -0400 Subject: [PATCH 451/775] deps: Add `TracyPlotConfig` patch for Tracy This functionality is currently only exposed on the master branch of Tracy, but it's quite useful so let's carry the patch to expose it until the next Tracy release. --- deps/libtracyclient.mk | 7 ++- .../patches/libTracyClient-config-plots.patch | 48 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 deps/patches/libTracyClient-config-plots.patch diff --git a/deps/libtracyclient.mk b/deps/libtracyclient.mk index aee5d6e969770..693df1e20fbfb 100644 --- a/deps/libtracyclient.mk +++ b/deps/libtracyclient.mk @@ -36,7 +36,12 @@ $(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-no-crash-handler.patch-applied: $(LIBT patch -p1 -f < $(SRCDIR)/patches/libTracyClient-no-crash-handler.patch echo 1 > $@ -$(LIBTRACYCLIENT_BUILDDIR)/build-configured: $(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-no-crash-handler.patch-applied +$(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-config-plots.patch-applied: $(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-no-crash-handler.patch-applied + cd $(LIBTRACYCLIENT_BUILDDIR) && \ + patch -p1 -f < $(SRCDIR)/patches/libTracyClient-config-plots.patch + echo 1 > $@ + +$(LIBTRACYCLIENT_BUILDDIR)/build-configured: $(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-config-plots.patch-applied mkdir -p $(dir $@) cd $(dir $@) && \ $(CMAKE) . $(CMAKE_GENERATOR_COMMAND) $(CMAKE_COMMON) $(LIBTRACYCLIENT_CMAKE) \ diff --git a/deps/patches/libTracyClient-config-plots.patch b/deps/patches/libTracyClient-config-plots.patch new file mode 100644 index 0000000000000..ff54f642d36ca --- /dev/null +++ b/deps/patches/libTracyClient-config-plots.patch @@ -0,0 +1,48 @@ +commit 7151c6afd9cc40877325c64bd19bcff7211fbd59 +Author: Bartosz Taudul +Date: Wed Mar 8 23:18:36 2023 +0100 + + Add support for configuring plots to C API. + +diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp +index 6104a7ed..38b5ea13 100644 +--- a/public/client/TracyProfiler.cpp ++++ b/public/client/TracyProfiler.cpp +@@ -4149,4 +4149,5 @@ TRACY_API void ___tracy_emit_frame_image( const void* image, uint16_t w, uint16_ + TRACY_API void ___tracy_emit_plot( const char* name, double val ) { tracy::Profiler::PlotData( name, val ); } ++TRACY_API void ___tracy_emit_plot_config( const char* name, int type, int step, int fill, uint32_t color ) { tracy::Profiler::ConfigurePlot( name, tracy::PlotFormatType(type), step, fill, color ); } + TRACY_API void ___tracy_emit_message( const char* txt, size_t size, int callstack ) { tracy::Profiler::Message( txt, size, callstack ); } + TRACY_API void ___tracy_emit_messageL( const char* txt, int callstack ) { tracy::Profiler::Message( txt, callstack ); } + TRACY_API void ___tracy_emit_messageC( const char* txt, size_t size, uint32_t color, int callstack ) { tracy::Profiler::MessageColor( txt, size, color, callstack ); } +diff --git a/public/tracy/TracyC.h b/public/tracy/TracyC.h +index bedf5e16..736b51ed 100644 +--- a/public/tracy/TracyC.h ++++ b/public/tracy/TracyC.h +@@ -11,6 +11,13 @@ + extern "C" { + #endif + ++enum TracyPlotFormatEnum ++{ ++ TracyPlotFormatNumber, ++ TracyPlotFormatMemory, ++ TracyPlotFormatPercentage, ++}; ++ + TRACY_API void ___tracy_set_thread_name( const char* name ); + + #define TracyCSetThreadName( name ) ___tracy_set_thread_name( name ); +@@ -49,4 +56,5 @@ typedef const void* TracyCZoneCtx; + #define TracyCPlot(x,y) ++#define TracyCPlotConfig(x,y,z,w,a) + #define TracyCMessage(x,y) + #define TracyCMessageL(x) + #define TracyCMessageC(x,y,z) +@@ -276,7 +284,9 @@ TRACY_API void ___tracy_emit_frame_image( const void* image, uint16_t w, uint16_ + TRACY_API void ___tracy_emit_plot( const char* name, double val ); ++TRACY_API void ___tracy_emit_plot_config( const char* name, int type, int step, int fill, uint32_t color ); + TRACY_API void ___tracy_emit_message_appinfo( const char* txt, size_t size ); + + #define TracyCPlot( name, val ) ___tracy_emit_plot( name, val ); ++#define TracyCPlotConfig( name, type, step, fill, color ) ___tracy_emit_plot_config( name, type, step, fill, color ); + #define TracyCAppInfo( txt, size ) ___tracy_emit_message_appinfo( txt, size ); From fa20904406a7dde60b59eefb416311eb2a33ab3d Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Wed, 5 Apr 2023 06:46:51 -0400 Subject: [PATCH 452/775] rtutils: Add "quiet" type-printing for `jl_static_show` This is useful for Tracy, which can receive method signature strings over the write. For performance reasons, we want to keep those strings as small as possible, typically less than 80 characters long. This change adds basic "configuration" support for `jl_static_show`, including only a "quiet" parameter for now which can be used to avoid printing DataType parameters. --- src/julia_internal.h | 3 + src/rtutils.c | 141 ++++++++++++++++++++++--------------------- src/timing.c | 3 +- 3 files changed, 77 insertions(+), 70 deletions(-) diff --git a/src/julia_internal.h b/src/julia_internal.h index 73f8b9467fcf4..80a7339fe458b 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -583,6 +583,9 @@ void jl_gc_reset_alloc_count(void); uint32_t jl_get_gs_ctr(void); void jl_set_gs_ctr(uint32_t ctr); +typedef struct _jl_static_show_config_t { uint8_t quiet; } jl_static_show_config_t; +size_t jl_static_show_func_sig_(JL_STREAM *s, jl_value_t *type, jl_static_show_config_t ctx) JL_NOTSAFEPOINT; + STATIC_INLINE jl_value_t *undefref_check(jl_datatype_t *dt, jl_value_t *v) JL_NOTSAFEPOINT { if (dt->layout->first_ptr >= 0) { diff --git a/src/rtutils.c b/src/rtutils.c index 41e45cd468039..a2ec34102f148 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -538,14 +538,23 @@ JL_DLLEXPORT jl_value_t *jl_stderr_obj(void) JL_NOTSAFEPOINT // toys for debugging --------------------------------------------------------- -static size_t jl_show_svec(JL_STREAM *out, jl_svec_t *t, const char *head, const char *opn, const char *cls) JL_NOTSAFEPOINT +struct recur_list { + struct recur_list *prev; + jl_value_t *v; +}; + +static size_t jl_static_show_x(JL_STREAM *out, jl_value_t *v, struct recur_list *depth, jl_static_show_config_t ctx) JL_NOTSAFEPOINT; +static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt, struct recur_list *depth, jl_static_show_config_t ctx) JL_NOTSAFEPOINT; +static size_t jl_static_show_next_(JL_STREAM *out, jl_value_t *v, jl_value_t *prev, struct recur_list *depth, jl_static_show_config_t ctx) JL_NOTSAFEPOINT; + +static size_t jl_show_svec(JL_STREAM *out, jl_svec_t *t, const char *head, const char *opn, const char *cls, jl_static_show_config_t ctx) JL_NOTSAFEPOINT { size_t i, n=0, len = jl_svec_len(t); n += jl_printf(out, "%s", head); n += jl_printf(out, "%s", opn); for (i = 0; i < len; i++) { jl_value_t *v = jl_svecref(t,i); - n += jl_static_show(out, v); + n += jl_static_show_x(out, v, 0, ctx); if (i != len-1) n += jl_printf(out, ", "); } @@ -553,14 +562,6 @@ static size_t jl_show_svec(JL_STREAM *out, jl_svec_t *t, const char *head, const return n; } -struct recur_list { - struct recur_list *prev; - jl_value_t *v; -}; - -static size_t jl_static_show_x(JL_STREAM *out, jl_value_t *v, struct recur_list *depth) JL_NOTSAFEPOINT; -static size_t jl_static_show_next_(JL_STREAM *out, jl_value_t *v, jl_value_t *prev, struct recur_list *depth) JL_NOTSAFEPOINT; - JL_DLLEXPORT int jl_id_start_char(uint32_t wc) JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_id_char(uint32_t wc) JL_NOTSAFEPOINT; @@ -697,7 +698,7 @@ static int jl_static_is_function_(jl_datatype_t *vt) JL_NOTSAFEPOINT { // This is necessary to make sure that this function doesn't allocate any // memory through the Julia GC static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt, - struct recur_list *depth) JL_NOTSAFEPOINT + struct recur_list *depth, jl_static_show_config_t ctx) JL_NOTSAFEPOINT { size_t n = 0; if ((uintptr_t)vt < 4096U) { @@ -705,7 +706,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt } else if ((uintptr_t)v < 4096U) { n += jl_printf(out, ""); } else if (vt == (jl_datatype_t*)jl_buff_tag) { @@ -746,17 +747,17 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_static_show_func_sig(out, li->def.method->sig); } else { - n += jl_static_show_x(out, (jl_value_t*)li->def.module, depth); + n += jl_static_show_x(out, (jl_value_t*)li->def.module, depth, ctx); n += jl_printf(out, ". -> "); - n += jl_static_show_x(out, jl_atomic_load_relaxed(&li->uninferred), depth); + n += jl_static_show_x(out, jl_atomic_load_relaxed(&li->uninferred), depth, ctx); } } else if (vt == jl_typename_type) { - n += jl_static_show_x(out, jl_unwrap_unionall(((jl_typename_t*)v)->wrapper), depth); + n += jl_static_show_x(out, jl_unwrap_unionall(((jl_typename_t*)v)->wrapper), depth, ctx); n += jl_printf(out, ".name"); } else if (vt == jl_simplevector_type) { - n += jl_show_svec(out, (jl_svec_t*)v, "svec", "(", ")"); + n += jl_show_svec(out, (jl_svec_t*)v, "svec", "(", ")", ctx); } else if (v == (jl_value_t*)jl_unionall_type) { // avoid printing `typeof(Type)` for `UnionAll`. @@ -767,10 +768,10 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, "Vararg"); if (vm->T) { n += jl_printf(out, "{"); - n += jl_static_show_x(out, vm->T, depth); + n += jl_static_show_x(out, vm->T, depth, ctx); if (vm->N) { n += jl_printf(out, ", "); - n += jl_static_show_x(out, vm->N, depth); + n += jl_static_show_x(out, vm->N, depth, ctx); } n += jl_printf(out, "}"); } @@ -797,7 +798,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt } if (taillen == tlen && taillen > 3) { n += jl_printf(out, "NTuple{%d, ", tlen); - n += jl_static_show_x(out, jl_tparam0(dv), depth); + n += jl_static_show_x(out, jl_tparam0(dv), depth, ctx); n += jl_printf(out, "}"); } else { @@ -805,22 +806,25 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt for (i = 0; i < (taillen > 3 ? tlen-taillen : tlen); i++) { if (i > 0) n += jl_printf(out, ", "); - n += jl_static_show_x(out, jl_tparam(dv, i), depth); + n += jl_static_show_x(out, jl_tparam(dv, i), depth, ctx); } if (taillen > 3) { n += jl_printf(out, ", Vararg{"); - n += jl_static_show_x(out, jl_tparam(dv, tlen-1), depth); + n += jl_static_show_x(out, jl_tparam(dv, tlen-1), depth, ctx); n += jl_printf(out, ", %d}", taillen); } n += jl_printf(out, "}"); } return n; } + if (ctx.quiet) { + return jl_printf(out, "%s", jl_symbol_name(dv->name->name)); + } if (globfunc) { n += jl_printf(out, "typeof("); } if (jl_core_module && (dv->name->module != jl_core_module || !jl_module_exports_p(jl_core_module, sym))) { - n += jl_static_show_x(out, (jl_value_t*)dv->name->module, depth); + n += jl_static_show_x(out, (jl_value_t*)dv->name->module, depth, ctx); n += jl_printf(out, "."); size_t i = 0; if (globfunc && !jl_id_start_char(u8_nextchar(sn, &i))) { @@ -841,7 +845,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, "{"); for (j = 0; j < tlen; j++) { jl_value_t *p = jl_tparam(dv,j); - n += jl_static_show_x(out, p, depth); + n += jl_static_show_x(out, p, depth, ctx); if (j != tlen-1) n += jl_printf(out, ", "); } @@ -908,22 +912,22 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, "Union{"); while (jl_is_uniontype(v)) { // tail-recurse on b to flatten the printing of the Union structure in the common case - n += jl_static_show_x(out, ((jl_uniontype_t*)v)->a, depth); + n += jl_static_show_x(out, ((jl_uniontype_t*)v)->a, depth, ctx); n += jl_printf(out, ", "); v = ((jl_uniontype_t*)v)->b; } - n += jl_static_show_x(out, v, depth); + n += jl_static_show_x(out, v, depth, ctx); n += jl_printf(out, "}"); } else if (vt == jl_unionall_type) { jl_unionall_t *ua = (jl_unionall_t*)v; - n += jl_static_show_x(out, ua->body, depth); + n += jl_static_show_x(out, ua->body, depth, ctx); n += jl_printf(out, " where "); - n += jl_static_show_x(out, (jl_value_t*)ua->var, depth->prev); + n += jl_static_show_x(out, (jl_value_t*)ua->var, depth->prev, ctx); } else if (vt == jl_typename_type) { n += jl_printf(out, "typename("); - n += jl_static_show_x(out, jl_unwrap_unionall(((jl_typename_t*)v)->wrapper), depth); + n += jl_static_show_x(out, jl_unwrap_unionall(((jl_typename_t*)v)->wrapper), depth, ctx); n += jl_printf(out, ")"); } else if (vt == jl_tvar_type) { @@ -943,7 +947,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt int ua = jl_is_unionall(lb); if (ua) n += jl_printf(out, "("); - n += jl_static_show_x(out, lb, depth); + n += jl_static_show_x(out, lb, depth, ctx); if (ua) n += jl_printf(out, ")"); n += jl_printf(out, "<:"); @@ -955,7 +959,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, "<:"); if (ua) n += jl_printf(out, "("); - n += jl_static_show_x(out, ub, depth); + n += jl_static_show_x(out, ub, depth, ctx); if (ua) n += jl_printf(out, ")"); } @@ -963,7 +967,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt else if (vt == jl_module_type) { jl_module_t *m = (jl_module_t*)v; if (m->parent != m && m->parent != jl_main_module) { - n += jl_static_show_x(out, (jl_value_t*)m->parent, depth); + n += jl_static_show_x(out, (jl_value_t*)m->parent, depth, ctx); n += jl_printf(out, "."); } n += jl_printf(out, "%s", jl_symbol_name(m->name)); @@ -984,7 +988,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt (uintptr_t)((jl_ssavalue_t*)v)->id); } else if (vt == jl_globalref_type) { - n += jl_static_show_x(out, (jl_value_t*)jl_globalref_mod(v), depth); + n += jl_static_show_x(out, (jl_value_t*)jl_globalref_mod(v), depth, ctx); char *name = jl_symbol_name(jl_globalref_name(v)); n += jl_printf(out, jl_is_identifier(name) ? ".%s" : ".:(%s)", name); } @@ -999,7 +1003,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt else { n += jl_printf(out, ":("); } - n += jl_static_show_x(out, qv, depth); + n += jl_static_show_x(out, qv, depth, ctx); if (!jl_is_symbol(qv)) { n += jl_printf(out, " end"); } @@ -1009,20 +1013,20 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt } else if (vt == jl_newvarnode_type) { n += jl_printf(out, ""); } else if (vt == jl_linenumbernode_type) { n += jl_printf(out, "#= "); - n += jl_static_show_x(out, jl_linenode_file(v), depth); + n += jl_static_show_x(out, jl_linenode_file(v), depth, ctx); n += jl_printf(out, ":%" PRIuPTR " =#", jl_linenode_line(v)); } else if (vt == jl_expr_type) { jl_expr_t *e = (jl_expr_t*)v; if (e->head == jl_assign_sym && jl_array_len(e->args) == 2) { - n += jl_static_show_x(out, jl_exprarg(e,0), depth); + n += jl_static_show_x(out, jl_exprarg(e,0), depth, ctx); n += jl_printf(out, " = "); - n += jl_static_show_x(out, jl_exprarg(e,1), depth); + n += jl_static_show_x(out, jl_exprarg(e,1), depth, ctx); } else { char sep = ' '; @@ -1030,14 +1034,14 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt size_t i, len = jl_array_len(e->args); for (i = 0; i < len; i++) { n += jl_printf(out, ",%c", sep); - n += jl_static_show_x(out, jl_exprarg(e,i), depth); + n += jl_static_show_x(out, jl_exprarg(e,i), depth, ctx); } n += jl_printf(out, ")"); } } else if (jl_array_type && jl_is_array_type(vt)) { n += jl_printf(out, "Array{"); - n += jl_static_show_x(out, (jl_value_t*)jl_tparam0(vt), depth); + n += jl_static_show_x(out, (jl_value_t*)jl_tparam0(vt), depth, ctx); n += jl_printf(out, ", ("); size_t i, ndims = jl_array_ndims(v); if (ndims == 1) @@ -1072,13 +1076,13 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt for (j = 0; j < tlen; j++) { if (av->flags.ptrarray) { jl_value_t **ptr = ((jl_value_t**)av->data) + j; - n += jl_static_show_x(out, *ptr, depth); + n += jl_static_show_x(out, *ptr, depth, ctx); } else { char *ptr = ((char*)av->data) + j * av->elsize; n += jl_static_show_x_(out, (jl_value_t*)ptr, typetagdata ? (jl_datatype_t*)jl_nth_union_component(el_type, typetagdata[j]) : (jl_datatype_t*)el_type, - depth); + depth, ctx); } if (j != tlen - 1) n += jl_printf(out, nlsep ? ",\n " : ", "); @@ -1087,16 +1091,16 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt } else if (vt == jl_loaderror_type) { n += jl_printf(out, "LoadError(at "); - n += jl_static_show_x(out, *(jl_value_t**)v, depth); + n += jl_static_show_x(out, *(jl_value_t**)v, depth, ctx); // Access the field directly to avoid allocation n += jl_printf(out, " line %" PRIdPTR, ((intptr_t*)v)[1]); n += jl_printf(out, ": "); - n += jl_static_show_x(out, ((jl_value_t**)v)[2], depth); + n += jl_static_show_x(out, ((jl_value_t**)v)[2], depth, ctx); n += jl_printf(out, ")"); } else if (vt == jl_errorexception_type) { n += jl_printf(out, "ErrorException("); - n += jl_static_show_x(out, *(jl_value_t**)v, depth); + n += jl_static_show_x(out, *(jl_value_t**)v, depth, ctx); n += jl_printf(out, ")"); } else if (jl_static_is_function_(vt) && is_globname_binding(v, (jl_datatype_t*)vt)) { @@ -1106,7 +1110,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt int globfunc = is_globfunction(v, dv, &sym); int quote = 0; if (jl_core_module && (dv->name->module != jl_core_module || !jl_module_exports_p(jl_core_module, sym))) { - n += jl_static_show_x(out, (jl_value_t*)dv->name->module, depth); + n += jl_static_show_x(out, (jl_value_t*)dv->name->module, depth, ctx); n += jl_printf(out, "."); size_t i = 0; @@ -1136,7 +1140,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, "NamedTuple"); } else if (!istuple) { - n += jl_static_show_x(out, (jl_value_t*)vt, depth); + n += jl_static_show_x(out, (jl_value_t*)vt, depth, ctx); } n += jl_printf(out, "("); size_t nb = jl_datatype_size(vt); @@ -1159,7 +1163,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt size_t offs = jl_field_offset(vt, i); char *fld_ptr = (char*)v + offs; if (jl_field_isptr(vt, i)) { - n += jl_static_show_x(out, *(jl_value_t**)fld_ptr, depth); + n += jl_static_show_x(out, *(jl_value_t**)fld_ptr, depth, ctx); } else { jl_datatype_t *ft = (jl_datatype_t*)jl_field_type_concrete(vt, i); @@ -1167,7 +1171,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt uint8_t sel = ((uint8_t*)fld_ptr)[jl_field_size(vt, i) - 1]; ft = (jl_datatype_t*)jl_nth_union_component((jl_value_t*)ft, sel); } - n += jl_static_show_x_(out, (jl_value_t*)fld_ptr, ft, depth); + n += jl_static_show_x_(out, (jl_value_t*)fld_ptr, ft, depth, ctx); } if ((istuple || isnamedtuple) && tlen == 1) n += jl_printf(out, ","); @@ -1177,26 +1181,26 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt if (vt == jl_typemap_entry_type) { n += jl_printf(out, ", next=↩︎\n "); jl_value_t *next = (jl_value_t*)jl_atomic_load_relaxed(&((jl_typemap_entry_t*)v)->next); - n += jl_static_show_next_(out, next, v, depth); + n += jl_static_show_next_(out, next, v, depth, ctx); } } n += jl_printf(out, ")"); } else { n += jl_printf(out, ""); } return n; } -static size_t jl_static_show_x(JL_STREAM *out, jl_value_t *v, struct recur_list *depth) JL_NOTSAFEPOINT +static size_t jl_static_show_x(JL_STREAM *out, jl_value_t *v, struct recur_list *depth, jl_static_show_config_t ctx) JL_NOTSAFEPOINT { // show values without calling a julia method or allocating through the GC - return jl_static_show_next_(out, v, NULL, depth); + return jl_static_show_next_(out, v, NULL, depth, ctx); } -static size_t jl_static_show_next_(JL_STREAM *out, jl_value_t *v, jl_value_t *prev, struct recur_list *depth) JL_NOTSAFEPOINT +static size_t jl_static_show_next_(JL_STREAM *out, jl_value_t *v, jl_value_t *prev, struct recur_list *depth, jl_static_show_config_t ctx) JL_NOTSAFEPOINT { // helper for showing a typemap list by following the next pointers // while being careful about avoiding any recursion due to malformed (circular) references @@ -1217,7 +1221,7 @@ static size_t jl_static_show_next_(JL_STREAM *out, jl_value_t *v, jl_value_t *pr while (m && jl_typeis(m, jl_typemap_entry_type)) { if (m == v) { return jl_printf(out, "sig, depth) + + jl_static_show_x(out, (jl_value_t*)((jl_typemap_entry_t*)m)->sig, depth, ctx) + jl_printf(out, ">"); } if (m == prev) { @@ -1248,15 +1252,22 @@ static size_t jl_static_show_next_(JL_STREAM *out, jl_value_t *v, jl_value_t *pr dist++; p = p->prev; } - return jl_static_show_x_(out, v, (jl_datatype_t*)jl_typeof(v), newdepth); + return jl_static_show_x_(out, v, (jl_datatype_t*)jl_typeof(v), newdepth, ctx); } JL_DLLEXPORT size_t jl_static_show(JL_STREAM *out, jl_value_t *v) JL_NOTSAFEPOINT { - return jl_static_show_x(out, v, 0); + jl_static_show_config_t ctx = { /* quiet */ 0 }; + return jl_static_show_x(out, v, 0, ctx); } JL_DLLEXPORT size_t jl_static_show_func_sig(JL_STREAM *s, jl_value_t *type) JL_NOTSAFEPOINT +{ + jl_static_show_config_t ctx = { /* quiet */ 0 }; + return jl_static_show_func_sig_(s, type, ctx); +} + +JL_DLLEXPORT size_t jl_static_show_func_sig_(JL_STREAM *s, jl_value_t *type, jl_static_show_config_t ctx) JL_NOTSAFEPOINT { size_t n = 0; size_t i; @@ -1286,7 +1297,7 @@ JL_DLLEXPORT size_t jl_static_show_func_sig(JL_STREAM *s, jl_value_t *type) JL_N } else { n += jl_printf(s, "(::"); - n += jl_static_show_x(s, ftype, depth); + n += jl_static_show_x(s, ftype, depth, ctx); n += jl_printf(s, ")"); } size_t tl = jl_nparams(type); @@ -1294,11 +1305,7 @@ JL_DLLEXPORT size_t jl_static_show_func_sig(JL_STREAM *s, jl_value_t *type) JL_N for (i = 1; i < tl; i++) { jl_value_t *tp = jl_tparam(type, i); if (i != tl - 1) { - if (jl_is_datatype(tp)) { - n += jl_printf(s, "%s", jl_symbol_name(((jl_datatype_t*)tp)->name->name)); - } else { - n += jl_static_show_x(s, tp, depth); - } + n += jl_static_show_x(s, tp, depth, ctx); n += jl_printf(s, ", "); } else { @@ -1306,17 +1313,13 @@ JL_DLLEXPORT size_t jl_static_show_func_sig(JL_STREAM *s, jl_value_t *type) JL_N tp = jl_unwrap_vararg(tp); if (jl_is_unionall(tp)) n += jl_printf(s, "("); - if (jl_is_datatype(tp)) { - n += jl_printf(s, "%s", jl_symbol_name(((jl_datatype_t*)tp)->name->name)); - } else { - n += jl_static_show_x(s, tp, depth); - } + n += jl_static_show_x(s, tp, depth, ctx); if (jl_is_unionall(tp)) n += jl_printf(s, ")"); n += jl_printf(s, "..."); } else { - n += jl_static_show_x(s, tp, depth); + n += jl_static_show_x(s, tp, depth, ctx); } } } @@ -1328,7 +1331,7 @@ JL_DLLEXPORT size_t jl_static_show_func_sig(JL_STREAM *s, jl_value_t *type) JL_N while (jl_is_unionall(tvars)) { if (!first) n += jl_printf(s, ", "); - n += jl_static_show_x(s, (jl_value_t*)tvars->var, first ? NULL : depth); + n += jl_static_show_x(s, (jl_value_t*)tvars->var, first ? NULL : depth, ctx); tvars = (jl_unionall_t*)tvars->body; if (!first) depth += 1; diff --git a/src/timing.c b/src/timing.c index b2a483747dc6e..c3ba3809884bf 100644 --- a/src/timing.c +++ b/src/timing.c @@ -176,7 +176,8 @@ JL_DLLEXPORT void jl_timing_show_func_sig(jl_value_t *v, jl_timing_block_t *cur_ ios_mem(&buf, IOS_INLSIZE); buf.growable = 0; // Restrict to inline buffer to avoid allocation - jl_static_show_func_sig((JL_STREAM*)&buf, v); + jl_static_show_config_t config = { /* quiet */ 1 }; + jl_static_show_func_sig_((JL_STREAM*)&buf, v, config); if (buf.size == buf.maxsize) memset(&buf.buf[IOS_INLSIZE - 3], '.', 3); From 3b68d4908867b20200a72e93bd988ae04b12c72f Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Wed, 5 Apr 2023 06:58:54 -0400 Subject: [PATCH 453/775] profiling: Add `WITH_TIMING_COUNTS` build option This replaces the `ENABLE_TIMINGS` #define in `options.h` Since we now support multiple back-ends for timing events, each back-end is expected to be enabled with a `WITH_*` Makefile flag, and then the JL_TIMING API is automatically enabled if any back-end is available. Turning on multiple back-ends at the same time is also supported. --- Make.inc | 8 ++++++++ src/init.c | 2 +- src/options.h | 3 --- src/timing.h | 12 +++++++----- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Make.inc b/Make.inc index 4d91cd9df9525..a5cf1c3592579 100644 --- a/Make.inc +++ b/Make.inc @@ -95,6 +95,9 @@ WITH_ITTAPI := 0 # Enable Tracy support WITH_TRACY := 0 +# Enable Timing Counts support +WITH_TIMING_COUNTS := 0 + # Prevent picking up $ARCH from the environment variables ARCH:= @@ -743,6 +746,11 @@ JCFLAGS += -DUSE_TRACY -DTRACY_ENABLE -DTRACY_FIBERS LIBTRACYCLIENT:=-lTracyClient endif +ifeq ($(WITH_TIMING_COUNTS), 1) +JCXXFLAGS += -DUSE_TIMING_COUNTS +JCFLAGS += -DUSE_TIMING_COUNTS +endif + # =========================================================================== # Select the cpu architecture to target, or automatically detects the user's compiler diff --git a/src/init.c b/src/init.c index 5990bd24aaabd..efa2d51110548 100644 --- a/src/init.c +++ b/src/init.c @@ -349,7 +349,7 @@ JL_DLLEXPORT void jl_atexit_hook(int exitcode) JL_NOTSAFEPOINT_ENTER // TODO: Destroy threads? jl_destroy_timing(); // cleans up the current timing_stack for noreturn -#ifdef ENABLE_TIMINGS +#ifdef USE_TIMING_COUNTS jl_print_timings(); #endif jl_teardown_codegen(); // prints stats diff --git a/src/options.h b/src/options.h index 12822b6ecf8b0..5253bcab0456f 100644 --- a/src/options.h +++ b/src/options.h @@ -78,9 +78,6 @@ // OBJPROFILE counts objects by type // #define OBJPROFILE -// Automatic Instrumenting Profiler -#define ENABLE_TIMINGS - // method dispatch profiling -------------------------------------------------- diff --git a/src/timing.h b/src/timing.h index 920440edf9da7..eccec059cf667 100644 --- a/src/timing.h +++ b/src/timing.h @@ -28,6 +28,10 @@ int jl_timing_set_enable(const char *subsystem, uint8_t enabled); #define HAVE_TIMING_SUPPORT #endif +#if defined( USE_TRACY ) || defined( USE_TIMING_COUNTS ) +#define ENABLE_TIMINGS +#endif + #if !defined( ENABLE_TIMINGS ) || !defined( HAVE_TIMING_SUPPORT ) #define JL_TIMING(subsystem, event) @@ -118,9 +122,7 @@ enum jl_timing_owners { * Timing Backend: Aggregated timing counts (implemented in timing.c) **/ -#define USE_COUNTS - -#ifdef USE_COUNTS +#ifdef USE_TIMING_COUNTS #define _COUNTS_CTX_MEMBER jl_timing_counts_t counts_ctx; #define _COUNTS_CTOR(block, owner) _jl_timing_counts_ctor(block, owner) #define _COUNTS_DESTROY(block) _jl_timing_counts_destroy(block) @@ -204,7 +206,7 @@ struct _jl_timing_block_t { // typedef in julia.h }; STATIC_INLINE void _jl_timing_block_ctor(jl_timing_block_t *block, int owner) JL_NOTSAFEPOINT { - uint64_t t = cycleclock(); + uint64_t t = cycleclock(); (void)t; _COUNTS_CTOR(&block->counts_ctx, owner); _COUNTS_START(&block->counts_ctx, t); @@ -218,7 +220,7 @@ STATIC_INLINE void _jl_timing_block_ctor(jl_timing_block_t *block, int owner) JL } STATIC_INLINE void _jl_timing_block_destroy(jl_timing_block_t *block) JL_NOTSAFEPOINT { - uint64_t t = cycleclock(); + uint64_t t = cycleclock(); (void)t; _COUNTS_STOP(&block->counts_ctx, t); _COUNTS_DESTROY(&block->counts_ctx); From 9ca700e06e4b9de4d4b292ce8eb6d3294bd1b353 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Wed, 5 Apr 2023 09:06:43 -0400 Subject: [PATCH 454/775] profiling: limit reported method instances Cap the number of method instances we report to the profiler for a single LLVM_MODULE_FINISH to 10. --- src/jitlayers.cpp | 8 +++++++- src/timing.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 7c68b78c27850..75f2b244dacd3 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -248,9 +248,12 @@ static jl_callptr_t _jl_compile_codeinst( IndirectCodeinsts += emitted.size() - 1; } + size_t i = 0; for (auto &def : emitted) { jl_code_instance_t *this_code = def.first; - jl_timing_show_func_sig(this_code->def->specTypes, JL_TIMING_CURRENT_BLOCK); + if (i < 10) + jl_timing_show_func_sig(this_code->def->specTypes, JL_TIMING_CURRENT_BLOCK); + jl_llvm_functions_t decls = std::get<1>(def.second); jl_callptr_t addr; bool isspecsig = false; @@ -292,7 +295,10 @@ static jl_callptr_t _jl_compile_codeinst( } if (this_code == codeinst) fptr = addr; + i++; } + if (i > 10) + jl_timing_printf(JL_TIMING_CURRENT_BLOCK, "... <%d methods truncated>", i - 10); uint64_t end_time = 0; if (timed) diff --git a/src/timing.h b/src/timing.h index eccec059cf667..d7dc53ad0e8be 100644 --- a/src/timing.h +++ b/src/timing.h @@ -41,6 +41,7 @@ int jl_timing_set_enable(const char *subsystem, uint8_t enabled); #define jl_timing_show_filename(f, b) #define jl_timing_show_method_instance(mi, b) #define jl_timing_show_func_sig(tt, b) +#define jl_timing_printf(s, f, ...) #define jl_timing_block_enter_task(ct, ptls, blk) #define jl_timing_block_exit_task(ct, ptls) ((jl_timing_block_t *)NULL) #define jl_pop_timing_block(blk) From def2ddacc9a9b064d06c24d12885427fb0502465 Mon Sep 17 00:00:00 2001 From: Tanmay Mohapatra Date: Thu, 6 Apr 2023 08:18:02 +0530 Subject: [PATCH 455/775] make default worker pool an AbstractWorkerPool (#49101) Changes [Distributed._default_worker_pool](https://github.com/JuliaLang/julia/blob/5f5d2040511b42ba74bd7529a0eac9cf817ad496/stdlib/Distributed/src/workerpool.jl#L242) to hold an `AbstractWorkerPool` instead of `WorkerPool`. With this, alternate implementations can be plugged in as the default pool. Helps in cases where a cluster is always meant to use a certain custom pool. Lower level calls can then work without having to pass a custom pool reference with every call. --- stdlib/Distributed/src/pmap.jl | 6 +++--- stdlib/Distributed/src/workerpool.jl | 15 +++++++++++++-- stdlib/Distributed/test/distributed_exec.jl | 13 +++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/stdlib/Distributed/src/pmap.jl b/stdlib/Distributed/src/pmap.jl index 603dfa7e031ce..f884d47fff98e 100644 --- a/stdlib/Distributed/src/pmap.jl +++ b/stdlib/Distributed/src/pmap.jl @@ -6,7 +6,7 @@ struct BatchProcessingError <: Exception end """ - pgenerate([::WorkerPool], f, c...) -> iterator + pgenerate([::AbstractWorkerPool], f, c...) -> iterator Apply `f` to each element of `c` in parallel using available workers and tasks. @@ -18,14 +18,14 @@ Note that `f` must be made available to all worker processes; see [Code Availability and Loading Packages](@ref code-availability) for details. """ -function pgenerate(p::WorkerPool, f, c) +function pgenerate(p::AbstractWorkerPool, f, c) if length(p) == 0 return AsyncGenerator(f, c; ntasks=()->nworkers(p)) end batches = batchsplit(c, min_batch_count = length(p) * 3) return Iterators.flatten(AsyncGenerator(remote(p, b -> asyncmap(f, b)), batches)) end -pgenerate(p::WorkerPool, f, c1, c...) = pgenerate(p, a->f(a...), zip(c1, c...)) +pgenerate(p::AbstractWorkerPool, f, c1, c...) = pgenerate(p, a->f(a...), zip(c1, c...)) pgenerate(f, c) = pgenerate(default_worker_pool(), f, c) pgenerate(f, c1, c...) = pgenerate(a->f(a...), zip(c1, c...)) diff --git a/stdlib/Distributed/src/workerpool.jl b/stdlib/Distributed/src/workerpool.jl index 89e52667c82c9..5dd1c07044e09 100644 --- a/stdlib/Distributed/src/workerpool.jl +++ b/stdlib/Distributed/src/workerpool.jl @@ -239,12 +239,14 @@ perform a `remote_do` on it. """ remote_do(f, pool::AbstractWorkerPool, args...; kwargs...) = remotecall_pool(remote_do, f, pool, args...; kwargs...) -const _default_worker_pool = Ref{Union{WorkerPool, Nothing}}(nothing) +const _default_worker_pool = Ref{Union{AbstractWorkerPool, Nothing}}(nothing) """ default_worker_pool() -[`WorkerPool`](@ref) containing idle [`workers`](@ref) - used by `remote(f)` and [`pmap`](@ref) (by default). +[`AbstractWorkerPool`](@ref) containing idle [`workers`](@ref) - used by `remote(f)` and [`pmap`](@ref) +(by default). Unless one is explicitly set via `default_worker_pool!(pool)`, the default worker pool is +initialized to a [`WorkerPool`](@ref). # Examples ```julia-repl @@ -267,6 +269,15 @@ function default_worker_pool() return _default_worker_pool[] end +""" + default_worker_pool!(pool::AbstractWorkerPool) + +Set a [`AbstractWorkerPool`](@ref) to be used by `remote(f)` and [`pmap`](@ref) (by default). +""" +function default_worker_pool!(pool::AbstractWorkerPool) + _default_worker_pool[] = pool +end + """ remote([p::AbstractWorkerPool], f) -> Function diff --git a/stdlib/Distributed/test/distributed_exec.jl b/stdlib/Distributed/test/distributed_exec.jl index 548ac73d2fb4c..16d1e4b100bf3 100644 --- a/stdlib/Distributed/test/distributed_exec.jl +++ b/stdlib/Distributed/test/distributed_exec.jl @@ -701,6 +701,19 @@ wp = CachingPool(workers()) clear!(wp) @test length(wp.map_obj2ref) == 0 +# default_worker_pool! tests +wp_default = Distributed.default_worker_pool() +try + wp = CachingPool(workers()) + Distributed.default_worker_pool!(wp) + @test [1:100...] == pmap(x->x, wp, 1:100) + @test !isempty(wp.map_obj2ref) + clear!(wp) + @test isempty(wp.map_obj2ref) +finally + Distributed.default_worker_pool!(wp_default) +end + # The below block of tests are usually run only on local development systems, since: # - tests which print errors # - addprocs tests are memory intensive From 152653452011f5ad5cf45da3b37e19759f62f3d0 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Wed, 5 Apr 2023 15:35:41 -0400 Subject: [PATCH 456/775] deps: Upgrade LibTracyClient to 0.9.1 --- deps/libtracyclient.mk | 12 +-- deps/libtracyclient.version | 8 +- .../libTracyClient-no-crash-handler.patch | 21 ----- deps/patches/libTracyClient-no-sampling.patch | 79 +++++++++++++++++++ ...patch => libTracyClient-plot-config.patch} | 15 +++- 5 files changed, 101 insertions(+), 34 deletions(-) delete mode 100644 deps/patches/libTracyClient-no-crash-handler.patch create mode 100644 deps/patches/libTracyClient-no-sampling.patch rename deps/patches/{libTracyClient-config-plots.patch => libTracyClient-plot-config.patch} (73%) diff --git a/deps/libtracyclient.mk b/deps/libtracyclient.mk index 693df1e20fbfb..bf84e25ccefc9 100644 --- a/deps/libtracyclient.mk +++ b/deps/libtracyclient.mk @@ -11,7 +11,7 @@ LIBTRACYCLIENT_CMAKE := LIBTRACYCLIENT_CMAKE += -DBUILD_SHARED_LIBS=ON LIBTRACYCLIENT_CMAKE += -DTRACY_FIBERS=ON LIBTRACYCLIENT_CMAKE += -DTRACY_NO_BROADCAST=ON -LIBTRACYCLIENT_CMAKE += -DTRACY_NO_SYSTEM_TRACING=ON +LIBTRACYCLIENT_CMAKE += -DTRACY_NO_SAMPLING=ON LIBTRACYCLIENT_CMAKE += -DTRACY_ONLY_LOCALHOST=ON LIBTRACYCLIENT_CMAKE += -DTRACY_NO_CODE_TRANSFER=ON LIBTRACYCLIENT_CMAKE += -DTRACY_NO_FRAME_IMAGE=ON @@ -31,17 +31,17 @@ $(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-freebsd-elfw.patch-applied: $(LIBTRACY patch -p1 -f < $(SRCDIR)/patches/libTracyClient-freebsd-elfw.patch echo 1 > $@ -$(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-no-crash-handler.patch-applied: $(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-freebsd-elfw.patch-applied +$(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-no-sampling.patch-applied: $(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-freebsd-elfw.patch-applied cd $(LIBTRACYCLIENT_BUILDDIR) && \ - patch -p1 -f < $(SRCDIR)/patches/libTracyClient-no-crash-handler.patch + patch -p1 -f < $(SRCDIR)/patches/libTracyClient-no-sampling.patch echo 1 > $@ -$(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-config-plots.patch-applied: $(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-no-crash-handler.patch-applied +$(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-plot-config.patch-applied: $(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-no-sampling.patch-applied cd $(LIBTRACYCLIENT_BUILDDIR) && \ - patch -p1 -f < $(SRCDIR)/patches/libTracyClient-config-plots.patch + patch -p1 -f < $(SRCDIR)/patches/libTracyClient-plot-config.patch echo 1 > $@ -$(LIBTRACYCLIENT_BUILDDIR)/build-configured: $(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-config-plots.patch-applied +$(LIBTRACYCLIENT_BUILDDIR)/build-configured: $(LIBTRACYCLIENT_BUILDDIR)/libTracyClient-plot-config.patch-applied mkdir -p $(dir $@) cd $(dir $@) && \ $(CMAKE) . $(CMAKE_GENERATOR_COMMAND) $(CMAKE_COMMON) $(LIBTRACYCLIENT_CMAKE) \ diff --git a/deps/libtracyclient.version b/deps/libtracyclient.version index 52bc74cedb660..3c0e0ebd56fa3 100644 --- a/deps/libtracyclient.version +++ b/deps/libtracyclient.version @@ -1,8 +1,8 @@ ## jll artifact LIBTRACYCLIENT_JLL_NAME := LibTracyClient -LIBTRACYCLIENT_JLL_VER := 0.9.0+1 +LIBTRACYCLIENT_JLL_VER := 0.9.1+0 ## source build -LIBTRACYCLIENT_VER := 0.9.0 -LIBTRACYCLIENT_BRANCH=v0.9 -LIBTRACYCLIENT_SHA1=5a1f5371b792c12aea324213e1dc738b2923ae21 +LIBTRACYCLIENT_VER := 0.9.1 +LIBTRACYCLIENT_BRANCH=v0.9.1 +LIBTRACYCLIENT_SHA1=897aec5b062664d2485f4f9a213715d2e527e0ca diff --git a/deps/patches/libTracyClient-no-crash-handler.patch b/deps/patches/libTracyClient-no-crash-handler.patch deleted file mode 100644 index 259b20fcff650..0000000000000 --- a/deps/patches/libTracyClient-no-crash-handler.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp -index ea168e4f..9287d433 100644 ---- a/public/client/TracyProfiler.cpp -+++ b/public/client/TracyProfiler.cpp -@@ -1454,7 +1454,7 @@ Profiler::~Profiler() - if( m_crashHandlerInstalled ) RemoveVectoredExceptionHandler( m_exceptionHandler ); - #endif - --#ifdef __linux__ -+#if defined __linux__ && !defined TRACY_NO_CRASH_HANDLER - if( m_crashHandlerInstalled ) - { - sigaction( TRACY_CRASH_SIGNAL, &m_prevSignal.pwr, nullptr ); -@@ -1520,7 +1520,7 @@ bool Profiler::ShouldExit() - - void Profiler::Worker() - { --#ifdef __linux__ -+#if defined __linux__ && !defined TRACY_NO_CRASH_HANDLER - s_profilerTid = syscall( SYS_gettid ); - #endif diff --git a/deps/patches/libTracyClient-no-sampling.patch b/deps/patches/libTracyClient-no-sampling.patch new file mode 100644 index 0000000000000..c4c8576099348 --- /dev/null +++ b/deps/patches/libTracyClient-no-sampling.patch @@ -0,0 +1,79 @@ +commit 6249999153a9497b32bc84e9dc95a1537a0af714 +Author: Cody Tapscott +Date: Tue Apr 4 15:20:46 2023 -0400 + + linux: respect `TRACY_NO_SAMPLING` for sys-tracing + + This compile-time flag was being ignored on Linux. This change adds + gating for software-sampled stack trace sampling following the same + pattern as other `TRACY_NO_SAMPLE_*` options. + + If `TRACY_NO_SAMPLING=1` is provided as an environment variable, + software stack sampling is also disabled. + +diff --git a/public/client/TracySysTrace.cpp b/public/client/TracySysTrace.cpp +index 4a562eaa..af0641fe 100644 +--- a/public/client/TracySysTrace.cpp ++++ b/public/client/TracySysTrace.cpp +@@ -770,6 +770,13 @@ bool SysTraceStart( int64_t& samplingPeriod ) + TracyDebug( "sched_wakeup id: %i\n", wakeupId ); + TracyDebug( "drm_vblank_event id: %i\n", vsyncId ); + ++#ifdef TRACY_NO_SAMPLING ++ const bool noSoftwareSampling = true; ++#else ++ const char* noSoftwareSamplingEnv = GetEnvVar( "TRACY_NO_SAMPLING" ); ++ const bool noSoftwareSampling = noSoftwareSamplingEnv && noSoftwareSamplingEnv[0] == '1'; ++#endif ++ + #ifdef TRACY_NO_SAMPLE_RETIREMENT + const bool noRetirement = true; + #else +@@ -839,28 +846,31 @@ bool SysTraceStart( int64_t& samplingPeriod ) + pe.clockid = CLOCK_MONOTONIC_RAW; + #endif + +- TracyDebug( "Setup software sampling\n" ); +- ProbePreciseIp( pe, currentPid ); +- for( int i=0; i Date: Fri, 7 Apr 2023 13:24:23 -0400 Subject: [PATCH 457/775] inference: cleanup some lattice operations (#49271) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The smerge function was bypassing some of the limits of tmerge, which was undesirable, though the impact is apparently negligible. Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Co-authored-by: Mosè Giordano --- base/compiler/abstractinterpretation.jl | 2 +- base/compiler/typelattice.jl | 9 +-------- base/compiler/typelimits.jl | 9 ++++++++- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index e110482ff9ac5..5aeba5ca194e9 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2991,7 +2991,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) if !isempty(frame.limitations) rt = LimitedAccuracy(rt, copy(frame.limitations)) end - if tchanged(𝕃ₚ, rt, bestguess) + if !⊑(𝕃ₚ, rt, bestguess) # new (wider) return type for frame bestguess = tmerge(𝕃ₚ, bestguess, rt) # TODO: if bestguess isa InterConditional && !interesting(bestguess); bestguess = widenconditional(bestguess); end diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index 23f39d8b44f5e..700a6d333cbc4 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -736,22 +736,15 @@ widenconst(::LimitedAccuracy) = error("unhandled LimitedAccuracy") # state management # #################### -issubstate(lattice::AbstractLattice, a::VarState, b::VarState) = - ⊑(lattice, a.typ, b.typ) && a.undef <= b.undef - function smerge(lattice::AbstractLattice, sa::Union{NotFound,VarState}, sb::Union{NotFound,VarState}) sa === sb && return sa sa === NOT_FOUND && return sb sb === NOT_FOUND && return sa - issubstate(lattice, sa, sb) && return sb - issubstate(lattice, sb, sa) && return sa return VarState(tmerge(lattice, sa.typ, sb.typ), sa.undef | sb.undef) end -@inline tchanged(lattice::AbstractLattice, @nospecialize(n), @nospecialize(o)) = - o === NOT_FOUND || (n !== NOT_FOUND && !⊑(lattice, n, o)) @inline schanged(lattice::AbstractLattice, @nospecialize(n), @nospecialize(o)) = - (n !== o) && (o === NOT_FOUND || (n !== NOT_FOUND && !issubstate(lattice, n::VarState, o::VarState))) + (n !== o) && (o === NOT_FOUND || (n !== NOT_FOUND && !(n.undef <= o.undef && ⊑(lattice, n.typ, o.typ)))) # remove any lattice elements that wrap the reassigned slot object from the vartable function invalidate_slotwrapper(vt::VarState, changeid::Int, ignore_conditional::Bool) diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 8bb87a38dd4c9..b5bbcde63e699 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -306,6 +306,7 @@ end function issimplertype(𝕃::AbstractLattice, @nospecialize(typea), @nospecialize(typeb)) typea isa MaybeUndef && (typea = typea.typ) # n.b. does not appear in inference typeb isa MaybeUndef && (typeb = typeb.typ) # n.b. does not appear in inference + @assert !isa(typea, LimitedAccuracy) && !isa(typeb, LimitedAccuracy) "LimitedAccuracy not supported by simplertype lattice" # n.b. the caller was supposed to handle these typea === typeb && return true if typea isa PartialStruct aty = widenconst(typea) @@ -327,7 +328,7 @@ function issimplertype(𝕃::AbstractLattice, @nospecialize(typea), @nospecializ end elseif typea isa Type return issimpleenoughtype(typea) - # elseif typea isa Const # fall-through good + # elseif typea isa Const # fall-through to true is good elseif typea isa Conditional # follow issubconditional query typeb isa Const && return true typeb isa Conditional || return false @@ -352,6 +353,12 @@ function issimplertype(𝕃::AbstractLattice, @nospecialize(typea), @nospecializ issimplertype(𝕃, typea.fldtyp, typeb.fldtyp) || return false elseif typea isa PartialOpaque # TODO + aty = widenconst(typea) + bty = widenconst(typeb) + if typea.source === typeb.source && typea.parent === typeb.parent && aty == bty && typea.env == typeb.env + return false + end + return false end return true end From a1013e7022bc0c56bc46cb737efee790a73b1a92 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 7 Apr 2023 13:25:31 -0400 Subject: [PATCH 458/775] more robust validation of allocation type (#49269) We generally hit the runtime in pretty specific places when allocations look funky (because they are missing a typevar bound, so inference is not too willing to deal with it). Try to throw an error in those cases before those can get allocated and cause problems later from being non-concrete objects. Fix #49203 --- src/datatype.c | 10 ++++++++-- src/intrinsics.cpp | 17 ++++++++++++----- src/runtime_intrinsics.c | 16 ++++++++-------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/datatype.c b/src/datatype.c index 91fd24495f299..894beeaa1b7e6 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -1400,6 +1400,9 @@ JL_DLLEXPORT jl_value_t *jl_new_struct(jl_datatype_t *type, ...) { jl_task_t *ct = jl_current_task; if (type->instance != NULL) return type->instance; + if (!jl_is_datatype(type) || !type->isconcretetype || type->layout == NULL) { + jl_type_error("new", (jl_value_t*)jl_datatype_type, (jl_value_t*)type); + } va_list args; size_t i, nf = jl_datatype_nfields(type); va_start(args, type); @@ -1417,7 +1420,7 @@ JL_DLLEXPORT jl_value_t *jl_new_struct(jl_datatype_t *type, ...) JL_DLLEXPORT jl_value_t *jl_new_structv(jl_datatype_t *type, jl_value_t **args, uint32_t na) { jl_task_t *ct = jl_current_task; - if (!jl_is_datatype(type) || type->layout == NULL) { + if (!jl_is_datatype(type) || !type->isconcretetype || type->layout == NULL) { jl_type_error("new", (jl_value_t*)jl_datatype_type, (jl_value_t*)type); } size_t nf = jl_datatype_nfields(type); @@ -1454,7 +1457,7 @@ JL_DLLEXPORT jl_value_t *jl_new_structt(jl_datatype_t *type, jl_value_t *tup) jl_task_t *ct = jl_current_task; if (!jl_is_tuple(tup)) jl_type_error("new", (jl_value_t*)jl_tuple_type, tup); - if (!jl_is_datatype(type) || type->layout == NULL) + if (!jl_is_datatype(type) || !type->isconcretetype || type->layout == NULL) jl_type_error("new", (jl_value_t *)jl_datatype_type, (jl_value_t *)type); size_t nargs = jl_nfields(tup); size_t nf = jl_datatype_nfields(type); @@ -1500,6 +1503,9 @@ JL_DLLEXPORT jl_value_t *jl_new_struct_uninit(jl_datatype_t *type) { jl_task_t *ct = jl_current_task; if (type->instance != NULL) return type->instance; + if (!jl_is_datatype(type) || type->layout == NULL) { + jl_type_error("new", (jl_value_t*)jl_datatype_type, (jl_value_t*)type); + } size_t size = jl_datatype_size(type); jl_value_t *jv = jl_gc_alloc(ct->ptls, size, type); if (size > 0) diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index e829eea7f625a..8f2f2273506b4 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -504,20 +504,25 @@ static jl_cgval_t generic_bitcast(jl_codectx_t &ctx, const jl_cgval_t *argv) Type *llvmt = bitstype_to_llvm((jl_value_t*)bt, ctx.builder.getContext(), true); uint32_t nb = jl_datatype_size(bt); + Value *bt_value_rt = NULL; + if (!jl_is_concrete_type((jl_value_t*)bt)) { + bt_value_rt = boxed(ctx, bt_value); + emit_concretecheck(ctx, bt_value_rt, "bitcast: target type not a leaf primitive type"); + } + // Examine the second argument // bool isboxed; Type *vxt = julia_type_to_llvm(ctx, v.typ, &isboxed); - if (!jl_is_primitivetype(v.typ) || jl_datatype_size(v.typ) != nb) { Value *typ = emit_typeof_boxed(ctx, v); if (!jl_is_primitivetype(v.typ)) { if (jl_is_datatype(v.typ) && !jl_is_abstracttype(v.typ)) { - emit_error(ctx, "bitcast: expected primitive type value for second argument"); + emit_error(ctx, "bitcast: value not a primitive type"); return jl_cgval_t(); } else { Value *isprimitive = emit_datatype_isprimitivetype(ctx, typ); - error_unless(ctx, isprimitive, "bitcast: expected primitive type value for second argument"); + error_unless(ctx, isprimitive, "bitcast: value not a primitive type"); } } if (jl_is_datatype(v.typ) && !jl_is_abstracttype(v.typ)) { @@ -570,7 +575,7 @@ static jl_cgval_t generic_bitcast(jl_codectx_t &ctx, const jl_cgval_t *argv) return mark_julia_type(ctx, vx, false, bt); } else { - Value *box = emit_allocobj(ctx, nb, boxed(ctx, bt_value)); + Value *box = emit_allocobj(ctx, nb, bt_value_rt); init_bits_value(ctx, box, vx, ctx.tbaa().tbaa_immut); return mark_julia_type(ctx, box, true, bt->name->wrapper); } @@ -624,7 +629,9 @@ static jl_cgval_t generic_cast( return mark_julia_type(ctx, ans, false, jlto); } else { - Value *box = emit_allocobj(ctx, nb, boxed(ctx, targ)); + Value *targ_rt = boxed(ctx, targ); + emit_concretecheck(ctx, targ_rt, std::string(jl_intrinsic_name(f)) + ": target type not a leaf primitive type"); + Value *box = emit_allocobj(ctx, nb, targ_rt); init_bits_value(ctx, box, ans, ctx.tbaa().tbaa_immut); return mark_julia_type(ctx, box, true, jlto->name->wrapper); } diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index 99a81974a68b3..d53eb368de495 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -717,7 +717,7 @@ SELECTOR_FUNC(intrinsic_1) #define un_iintrinsic(name, u) \ JL_DLLEXPORT jl_value_t *jl_##name(jl_value_t *a) \ { \ - return jl_iintrinsic_1(jl_typeof(a), a, #name, u##signbitbyte, jl_intrinsiclambda_ty1, name##_list); \ + return jl_iintrinsic_1(a, #name, u##signbitbyte, jl_intrinsiclambda_ty1, name##_list); \ } #define un_iintrinsic_fast(LLVMOP, OP, name, u) \ un_iintrinsic_ctype(OP, name, 8, u##int##8_t) \ @@ -743,7 +743,7 @@ SELECTOR_FUNC(intrinsic_u1) #define uu_iintrinsic(name, u) \ JL_DLLEXPORT jl_value_t *jl_##name(jl_value_t *a) \ { \ - return jl_iintrinsic_1(jl_typeof(a), a, #name, u##signbitbyte, jl_intrinsiclambda_u1, name##_list); \ + return jl_iintrinsic_1(a, #name, u##signbitbyte, jl_intrinsiclambda_u1, name##_list); \ } #define uu_iintrinsic_fast(LLVMOP, OP, name, u) \ uu_iintrinsic_ctype(OP, name, 8, u##int##8_t) \ @@ -765,14 +765,13 @@ static const select_intrinsic_u1_t name##_list = { \ uu_iintrinsic(name, u) static inline -jl_value_t *jl_iintrinsic_1(jl_value_t *ty, jl_value_t *a, const char *name, +jl_value_t *jl_iintrinsic_1(jl_value_t *a, const char *name, char (*getsign)(void*, unsigned), jl_value_t *(*lambda1)(jl_value_t*, void*, unsigned, unsigned, const void*), const void *list) { - if (!jl_is_primitivetype(jl_typeof(a))) - jl_errorf("%s: value is not a primitive type", name); + jl_value_t *ty = jl_typeof(a); if (!jl_is_primitivetype(ty)) - jl_errorf("%s: type is not a primitive type", name); + jl_errorf("%s: value is not a primitive type", name); void *pa = jl_data_ptr(a); unsigned isize = jl_datatype_size(jl_typeof(a)); unsigned isize2 = next_power_of_two(isize); @@ -833,11 +832,12 @@ JL_DLLEXPORT jl_value_t *jl_##name(jl_value_t *ty, jl_value_t *a) \ static inline jl_value_t *jl_intrinsic_cvt(jl_value_t *ty, jl_value_t *a, const char *name, intrinsic_cvt_t op) { + JL_TYPECHKS(name, datatype, ty); + if (!jl_is_concrete_type(ty) || !jl_is_primitivetype(ty)) + jl_errorf("%s: target type not a leaf primitive type", name); jl_value_t *aty = jl_typeof(a); if (!jl_is_primitivetype(aty)) jl_errorf("%s: value is not a primitive type", name); - if (!jl_is_primitivetype(ty)) - jl_errorf("%s: type is not a primitive type", name); void *pa = jl_data_ptr(a); unsigned isize = jl_datatype_size(aty); unsigned osize = jl_datatype_size(ty); From 0c240733b9144ce19013b259c3c8ca9492b32682 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 7 Apr 2023 14:26:54 -0400 Subject: [PATCH 459/775] slightly optimize has_free_typevars (#49278) Manually convert these to tail-recursive form, so the stack can be unwound directly as soon as it finds an answer in many common cases (DataType with many simple UnionAll wrappers). --- src/jltypes.c | 451 +++++++++++++++++++++++++++++--------------------- 1 file changed, 260 insertions(+), 191 deletions(-) diff --git a/src/jltypes.c b/src/jltypes.c index 918ac3b8292dd..dc58cb57bfde6 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -37,85 +37,119 @@ static int typeenv_has(jl_typeenv_t *env, jl_tvar_t *v) JL_NOTSAFEPOINT return 0; } -static int layout_uses_free_typevars(jl_value_t *v, jl_typeenv_t *env) +static int typeenv_has_ne(jl_typeenv_t *env, jl_tvar_t *v) JL_NOTSAFEPOINT { - if (jl_typeis(v, jl_tvar_type)) - return !typeenv_has(env, (jl_tvar_t*)v); - if (jl_is_uniontype(v)) - return layout_uses_free_typevars(((jl_uniontype_t*)v)->a, env) || - layout_uses_free_typevars(((jl_uniontype_t*)v)->b, env); - if (jl_is_vararg(v)) { - jl_vararg_t *vm = (jl_vararg_t*)v; - if (vm->T && layout_uses_free_typevars(vm->T, env)) - return 1; - if (vm->N && layout_uses_free_typevars(vm->N, env)) - return 1; - return 0; - } - if (jl_is_unionall(v)) { - jl_unionall_t *ua = (jl_unionall_t*)v; - jl_typeenv_t newenv = { ua->var, NULL, env }; - return layout_uses_free_typevars(ua->body, &newenv); + while (env != NULL) { + if (env->var == v) + return env->val != (jl_value_t*)v; // consider it actually not present if it is bound to itself unchanging + env = env->prev; } - if (jl_is_datatype(v)) { - jl_datatype_t *dt = (jl_datatype_t*)v; - if (dt->layout || dt->isconcretetype || !dt->name->mayinlinealloc) - return 0; - if (dt->name == jl_namedtuple_typename) - return layout_uses_free_typevars(jl_tparam0(dt), env) || layout_uses_free_typevars(jl_tparam1(dt), env); - if (dt->name == jl_tuple_typename) - // conservative, since we don't want to inline an abstract tuple, - // and we currently declare !has_fixed_layout for these, but that - // means we also won't be able to inline a tuple which is concrete - // except for the use of free type-vars - return 1; - jl_svec_t *types = jl_get_fieldtypes(dt); - size_t i, l = jl_svec_len(types); - for (i = 0; i < l; i++) { - jl_value_t *ft = jl_svecref(types, i); - if (layout_uses_free_typevars(ft, env)) { - // This might be inline-alloc, but we don't know the layout + return 0; +} + + +static int layout_uses_free_typevars(jl_value_t *v, jl_typeenv_t *env) +{ + while (1) { + if (jl_typeis(v, jl_tvar_type)) + return !typeenv_has(env, (jl_tvar_t*)v); + while (jl_is_unionall(v)) { + jl_unionall_t *ua = (jl_unionall_t*)v; + jl_typeenv_t *newenv = (jl_typeenv_t*)alloca(sizeof(jl_typeenv_t)); + newenv->var = ua->var; + newenv->val = NULL; + newenv->prev = env; + env = newenv; + v = ua->body; + } + if (jl_is_datatype(v)) { + jl_datatype_t *dt = (jl_datatype_t*)v; + if (dt->layout || dt->isconcretetype || !dt->name->mayinlinealloc) + return 0; + if (dt->name == jl_namedtuple_typename) + return layout_uses_free_typevars(jl_tparam0(dt), env) || layout_uses_free_typevars(jl_tparam1(dt), env); + if (dt->name == jl_tuple_typename) + // conservative, since we don't want to inline an abstract tuple, + // and we currently declare !has_fixed_layout for these, but that + // means we also won't be able to inline a tuple which is concrete + // except for the use of free type-vars return 1; + jl_svec_t *types = jl_get_fieldtypes(dt); + size_t i, l = jl_svec_len(types); + for (i = 0; i < l; i++) { + jl_value_t *ft = jl_svecref(types, i); + if (layout_uses_free_typevars(ft, env)) + // This might be inline-alloc, but we don't know the layout + return 1; } + return 0; + } + else if (jl_is_uniontype(v)) { + if (layout_uses_free_typevars(((jl_uniontype_t*)v)->a, env)) + return 1; + v = ((jl_uniontype_t*)v)->b; + } + else if (jl_is_vararg(v)) { + jl_vararg_t *vm = (jl_vararg_t*)v; + if (!vm->T) + return 0; + if (vm->N && layout_uses_free_typevars(vm->N, env)) + return 1; + v = vm->T; + } + else { + return 0; } } - return 0; } static int has_free_typevars(jl_value_t *v, jl_typeenv_t *env) JL_NOTSAFEPOINT { - if (jl_typeis(v, jl_tvar_type)) { - return !typeenv_has(env, (jl_tvar_t*)v); - } - if (jl_is_uniontype(v)) - return has_free_typevars(((jl_uniontype_t*)v)->a, env) || - has_free_typevars(((jl_uniontype_t*)v)->b, env); - if (jl_is_vararg(v)) { - jl_vararg_t *vm = (jl_vararg_t*)v; - if (vm->T) { - if (has_free_typevars(vm->T, env)) - return 1; - return vm->N && has_free_typevars(vm->N, env); + while (1) { + if (jl_typeis(v, jl_tvar_type)) { + return !typeenv_has(env, (jl_tvar_t*)v); } - } - if (jl_is_unionall(v)) { - jl_unionall_t *ua = (jl_unionall_t*)v; - jl_typeenv_t newenv = { ua->var, NULL, env }; - return has_free_typevars(ua->var->lb, env) || has_free_typevars(ua->var->ub, env) || - has_free_typevars(ua->body, &newenv); - } - if (jl_is_datatype(v)) { - int expect = ((jl_datatype_t*)v)->hasfreetypevars; - if (expect == 0 || env == NULL) - return expect; - size_t i; - for (i = 0; i < jl_nparams(v); i++) { - if (has_free_typevars(jl_tparam(v, i), env)) { + while (jl_is_unionall(v)) { + jl_unionall_t *ua = (jl_unionall_t*)v; + if (ua->var->lb != jl_bottom_type && has_free_typevars(ua->var->lb, env)) + return 1; + if (ua->var->ub != (jl_value_t*)jl_any_type && has_free_typevars(ua->var->ub, env)) return 1; + jl_typeenv_t *newenv = (jl_typeenv_t*)alloca(sizeof(jl_typeenv_t)); + newenv->var = ua->var; + newenv->val = NULL; + newenv->prev = env; + env = newenv; + v = ua->body; + } + if (jl_is_datatype(v)) { + int expect = ((jl_datatype_t*)v)->hasfreetypevars; + if (expect == 0 || env == NULL) + return expect; + size_t i; + for (i = 0; i < jl_nparams(v); i++) { + if (has_free_typevars(jl_tparam(v, i), env)) + return 1; } + return 0; + } + else if (jl_is_uniontype(v)) { + if (has_free_typevars(((jl_uniontype_t*)v)->a, env)) + return 1; + v = ((jl_uniontype_t*)v)->b; + } + else if (jl_is_vararg(v)) { + jl_vararg_t *vm = (jl_vararg_t*)v; + if (!vm->T) + return 0; + if (vm->N && has_free_typevars(vm->N, env)) + return 1; + v = vm->T; + } + else { + return 0; } } - return 0; } JL_DLLEXPORT int jl_has_free_typevars(jl_value_t *v) JL_NOTSAFEPOINT @@ -125,36 +159,48 @@ JL_DLLEXPORT int jl_has_free_typevars(jl_value_t *v) JL_NOTSAFEPOINT static void find_free_typevars(jl_value_t *v, jl_typeenv_t *env, jl_array_t *out) { - if (jl_typeis(v, jl_tvar_type)) { - if (!typeenv_has(env, (jl_tvar_t*)v)) - jl_array_ptr_1d_push(out, v); - } - else if (jl_is_uniontype(v)) { - find_free_typevars(((jl_uniontype_t*)v)->a, env, out); - find_free_typevars(((jl_uniontype_t*)v)->b, env, out); - } - else if (jl_is_vararg(v)) { - jl_vararg_t *vm = (jl_vararg_t *)v; - if (vm->T) { - find_free_typevars(vm->T, env, out); - if (vm->N) { + while (1) { + if (jl_typeis(v, jl_tvar_type)) { + if (!typeenv_has(env, (jl_tvar_t*)v)) + jl_array_ptr_1d_push(out, v); + return; + } + while (jl_is_unionall(v)) { + jl_unionall_t *ua = (jl_unionall_t*)v; + if (ua->var->lb != jl_bottom_type) + find_free_typevars(ua->var->lb, env, out); + if (ua->var->ub != (jl_value_t*)jl_any_type) + find_free_typevars(ua->var->ub, env, out); + jl_typeenv_t *newenv = (jl_typeenv_t*)alloca(sizeof(jl_typeenv_t)); + newenv->var = ua->var; + newenv->val = NULL; + newenv->prev = env; + env = newenv; + v = ua->body; + } + if (jl_is_datatype(v)) { + if (!((jl_datatype_t*)v)->hasfreetypevars) + return; + size_t i; + for (i = 0; i < jl_nparams(v); i++) + find_free_typevars(jl_tparam(v, i), env, out); + return; + } + else if (jl_is_uniontype(v)) { + find_free_typevars(((jl_uniontype_t*)v)->a, env, out); + v = ((jl_uniontype_t*)v)->b; + } + else if (jl_is_vararg(v)) { + jl_vararg_t *vm = (jl_vararg_t *)v; + if (!vm->T) + return; + if (vm->N) // this swap the visited order, but we don't mind it find_free_typevars(vm->N, env, out); - } + v = vm->T; } - } - else if (jl_is_unionall(v)) { - jl_unionall_t *ua = (jl_unionall_t*)v; - jl_typeenv_t newenv = { ua->var, NULL, env }; - find_free_typevars(ua->var->lb, env, out); - find_free_typevars(ua->var->ub, env, out); - find_free_typevars(ua->body, &newenv, out); - } - else if (jl_is_datatype(v)) { - if (!((jl_datatype_t*)v)->hasfreetypevars) + else { return; - size_t i; - for (i=0; i < jl_nparams(v); i++) - find_free_typevars(jl_tparam(v,i), env, out); + } } } @@ -170,41 +216,55 @@ JL_DLLEXPORT jl_array_t *jl_find_free_typevars(jl_value_t *v) // test whether a type has vars bound by the given environment static int jl_has_bound_typevars(jl_value_t *v, jl_typeenv_t *env) JL_NOTSAFEPOINT { - if (jl_typeis(v, jl_tvar_type)) - return typeenv_has(env, (jl_tvar_t*)v); - if (jl_is_uniontype(v)) - return jl_has_bound_typevars(((jl_uniontype_t*)v)->a, env) || - jl_has_bound_typevars(((jl_uniontype_t*)v)->b, env); - if (jl_is_vararg(v)) { - jl_vararg_t *vm = (jl_vararg_t *)v; - return vm->T && (jl_has_bound_typevars(vm->T, env) || - (vm->N && jl_has_bound_typevars(vm->N, env))); - } - if (jl_is_unionall(v)) { - jl_unionall_t *ua = (jl_unionall_t*)v; - if (jl_has_bound_typevars(ua->var->lb, env) || jl_has_bound_typevars(ua->var->ub, env)) - return 1; - jl_typeenv_t *te = env; - while (te != NULL) { - if (te->var == ua->var) - break; - te = te->prev; + while (1) { + if (jl_typeis(v, jl_tvar_type)) { + return typeenv_has_ne(env, (jl_tvar_t*)v); } - if (te) te->var = NULL; // temporarily remove this var from env - int ans = jl_has_bound_typevars(ua->body, env); - if (te) te->var = ua->var; - return ans; - } - if (jl_is_datatype(v)) { - if (!((jl_datatype_t*)v)->hasfreetypevars) + while (jl_is_unionall(v)) { + jl_unionall_t *ua = (jl_unionall_t*)v; + if (ua->var->lb != jl_bottom_type && jl_has_bound_typevars(ua->var->lb, env)) + return 1; + if (ua->var->ub != (jl_value_t*)jl_any_type && jl_has_bound_typevars(ua->var->ub, env)) + return 1; + // Temporarily remove this var from env if necessary + // Note that te might be bound more than once in the env, so + // we remove it by setting it to itself in a new env. + if (typeenv_has_ne(env, ua->var)) { + jl_typeenv_t *newenv = (jl_typeenv_t*)alloca(sizeof(jl_typeenv_t)); + newenv->var = ua->var; + newenv->val = (jl_value_t*)ua->var; + newenv->prev = env; + env = newenv; + } + v = ua->body; + } + if (jl_is_datatype(v)) { + if (!((jl_datatype_t*)v)->hasfreetypevars) + return 0; + size_t i; + for (i = 0; i < jl_nparams(v); i++) { + if (jl_has_bound_typevars(jl_tparam(v, i), env)) + return 1; + } return 0; - size_t i; - for (i=0; i < jl_nparams(v); i++) { - if (jl_has_bound_typevars(jl_tparam(v,i), env)) + } + else if (jl_is_uniontype(v)) { + if (jl_has_bound_typevars(((jl_uniontype_t*)v)->a, env)) + return 1; + v = ((jl_uniontype_t*)v)->b; + } + else if (jl_is_vararg(v)) { + jl_vararg_t *vm = (jl_vararg_t *)v; + if (!vm->T) + return 0; + if (vm->N && jl_has_bound_typevars(vm->N, env)) return 1; + v = vm->T; + } + else { + return 0; } } - return 0; } JL_DLLEXPORT int jl_has_typevar(jl_value_t *t, jl_tvar_t *v) JL_NOTSAFEPOINT @@ -283,26 +343,28 @@ JL_DLLEXPORT int jl_get_size(jl_value_t *val, size_t *pnt) static int count_union_components(jl_value_t **types, size_t n) { - size_t i, c=0; - for(i=0; i < n; i++) { + size_t i, c = 0; + for (i = 0; i < n; i++) { jl_value_t *e = types[i]; - if (jl_is_uniontype(e)) { + while (jl_is_uniontype(e)) { jl_uniontype_t *u = (jl_uniontype_t*)e; c += count_union_components(&u->a, 1); - c += count_union_components(&u->b, 1); - } - else { - c++; + e = u->b; } + c++; } return c; } int jl_count_union_components(jl_value_t *v) { - if (!jl_is_uniontype(v)) return 1; - jl_uniontype_t *u = (jl_uniontype_t*)v; - return jl_count_union_components(u->a) + jl_count_union_components(u->b); + size_t c = 0; + while (jl_is_uniontype(v)) { + jl_uniontype_t *u = (jl_uniontype_t*)v; + c += jl_count_union_components(u->a); + v = u->b; + } + return c + 1; } // Return the `*pi`th element of a nested type union, according to a @@ -310,16 +372,16 @@ int jl_count_union_components(jl_value_t *v) // considered an "element". `*pi` is destroyed in the process. static jl_value_t *nth_union_component(jl_value_t *v, int *pi) JL_NOTSAFEPOINT { - if (!jl_is_uniontype(v)) { - if (*pi == 0) - return v; - (*pi)--; - return NULL; + while (jl_is_uniontype(v)) { + jl_uniontype_t *u = (jl_uniontype_t*)v; + jl_value_t *a = nth_union_component(u->a, pi); + if (a) return a; + v = u->b; } - jl_uniontype_t *u = (jl_uniontype_t*)v; - jl_value_t *a = nth_union_component(u->a, pi); - if (a) return a; - return nth_union_component(u->b, pi); + if (*pi == 0) + return v; + (*pi)--; + return NULL; } jl_value_t *jl_nth_union_component(jl_value_t *v, int i) JL_NOTSAFEPOINT @@ -330,12 +392,11 @@ jl_value_t *jl_nth_union_component(jl_value_t *v, int i) JL_NOTSAFEPOINT // inverse of jl_nth_union_component int jl_find_union_component(jl_value_t *haystack, jl_value_t *needle, unsigned *nth) JL_NOTSAFEPOINT { - if (jl_is_uniontype(haystack)) { - if (jl_find_union_component(((jl_uniontype_t*)haystack)->a, needle, nth)) - return 1; - if (jl_find_union_component(((jl_uniontype_t*)haystack)->b, needle, nth)) + while (jl_is_uniontype(haystack)) { + jl_uniontype_t *u = (jl_uniontype_t*)haystack; + if (jl_find_union_component(u->a, needle, nth)) return 1; - return 0; + haystack = u->b; } if (needle == haystack) return 1; @@ -346,17 +407,15 @@ int jl_find_union_component(jl_value_t *haystack, jl_value_t *needle, unsigned * static void flatten_type_union(jl_value_t **types, size_t n, jl_value_t **out, size_t *idx) JL_NOTSAFEPOINT { size_t i; - for(i=0; i < n; i++) { + for (i = 0; i < n; i++) { jl_value_t *e = types[i]; - if (jl_is_uniontype(e)) { + while (jl_is_uniontype(e)) { jl_uniontype_t *u = (jl_uniontype_t*)e; flatten_type_union(&u->a, 1, out, idx); - flatten_type_union(&u->b, 1, out, idx); - } - else { - out[*idx] = e; - (*idx)++; + e = u->b; } + out[*idx] = e; + (*idx)++; } } @@ -1168,6 +1227,8 @@ jl_unionall_t *jl_rename_unionall(jl_unionall_t *u) jl_value_t *jl_substitute_var(jl_value_t *t, jl_tvar_t *var, jl_value_t *val) { + if (val == (jl_value_t*)var) + return t; jl_typeenv_t env = { var, val, NULL }; return inst_type_w_(t, &env, NULL, 1); } @@ -1421,45 +1482,54 @@ static jl_value_t *extract_wrapper(jl_value_t *t JL_PROPAGATES_ROOT) JL_GLOBALLY int _may_substitute_ub(jl_value_t *v, jl_tvar_t *var, int inside_inv, int *cov_count) JL_NOTSAFEPOINT { - if (v == (jl_value_t*)var) { - if (inside_inv) { - return 0; + while (1) { + if (v == (jl_value_t*)var) { + if (inside_inv) { + return 0; + } + else { + (*cov_count)++; + return *cov_count <= 1 || jl_is_concrete_type(var->ub); + } } - else { - (*cov_count)++; - return *cov_count <= 1 || jl_is_concrete_type(var->ub); + while (jl_is_unionall(v)) { + jl_unionall_t *ua = (jl_unionall_t*)v; + if (ua->var == var) + return 1; + if (ua->var->lb != jl_bottom_type && !_may_substitute_ub(ua->var->lb, var, inside_inv, cov_count)) + return 0; + if (ua->var->ub != (jl_value_t*)jl_any_type && !_may_substitute_ub(ua->var->ub, var, inside_inv, cov_count)) + return 0; + v = ua->body; } - } - else if (jl_is_uniontype(v)) { - return _may_substitute_ub(((jl_uniontype_t*)v)->a, var, inside_inv, cov_count) && - _may_substitute_ub(((jl_uniontype_t*)v)->b, var, inside_inv, cov_count); - } - else if (jl_is_unionall(v)) { - jl_unionall_t *ua = (jl_unionall_t*)v; - if (ua->var == var) + if (jl_is_datatype(v)) { + int invar = inside_inv || !jl_is_tuple_type(v); + for (size_t i = 0; i < jl_nparams(v); i++) { + if (!_may_substitute_ub(jl_tparam(v, i), var, invar, cov_count)) + return 0; + } return 1; - return _may_substitute_ub(ua->var->lb, var, inside_inv, cov_count) && - _may_substitute_ub(ua->var->ub, var, inside_inv, cov_count) && - _may_substitute_ub(ua->body, var, inside_inv, cov_count); - } - else if (jl_is_datatype(v)) { - int invar = inside_inv || !jl_is_tuple_type(v); - for (size_t i = 0; i < jl_nparams(v); i++) { - if (!_may_substitute_ub(jl_tparam(v,i), var, invar, cov_count)) + } + else if (jl_is_uniontype(v)) { + // TODO: is !inside_inv, these don't have to share the changes to cov_count + if (!_may_substitute_ub(((jl_uniontype_t*)v)->a, var, inside_inv, cov_count)) return 0; + v = ((jl_uniontype_t*)v)->b; + } + else if (jl_is_vararg(v)) { + jl_vararg_t *va = (jl_vararg_t*)v; + if (!va->T) + return 1; + if (va->N && !_may_substitute_ub(va->N, var, 1, cov_count)) + return 0; + if (!jl_is_concrete_type(var->ub)) + inside_inv = 1; // treat as invariant inside vararg, for the sake of this algorithm + v = va->T; + } + else { + return 1; } } - else if (jl_is_vararg(v)) { - jl_vararg_t *va = (jl_vararg_t*)v; - int old_count = *cov_count; - if (va->T && !_may_substitute_ub(va->T, var, inside_inv, cov_count)) - return 0; - if (*cov_count > old_count && !jl_is_concrete_type(var->ub)) - return 0; - if (va->N && !_may_substitute_ub(va->N, var, 1, cov_count)) - return 0; - } - return 1; } // Check whether `var` may be replaced with its upper bound `ub` in `v where var<:ub` @@ -1475,7 +1545,6 @@ int may_substitute_ub(jl_value_t *v, jl_tvar_t *var) JL_NOTSAFEPOINT jl_value_t *normalize_unionalls(jl_value_t *t) { - JL_GC_PUSH1(&t); if (jl_is_uniontype(t)) { jl_uniontype_t *u = (jl_uniontype_t*)t; jl_value_t *a = NULL; @@ -1491,14 +1560,14 @@ jl_value_t *normalize_unionalls(jl_value_t *t) else if (jl_is_unionall(t)) { jl_unionall_t *u = (jl_unionall_t*)t; jl_value_t *body = normalize_unionalls(u->body); + JL_GC_PUSH1(&body); if (body != u->body) { - JL_GC_PUSH1(&body); t = jl_new_struct(jl_unionall_type, u->var, body); - JL_GC_POP(); u = (jl_unionall_t*)t; } if (u->var->lb == u->var->ub || may_substitute_ub(body, u->var)) { + body = (jl_value_t*)u; JL_TRY { t = jl_instantiate_unionall(u, u->var->ub); } @@ -1507,8 +1576,8 @@ jl_value_t *normalize_unionalls(jl_value_t *t) // (may happen for bounds inconsistent with the wrapper's bounds) } } + JL_GC_POP(); } - JL_GC_POP(); return t; } @@ -1588,9 +1657,9 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value } jl_datatype_t *ndt = NULL; - jl_value_t *last = iparams[ntp - 1]; - JL_GC_PUSH3(&p, &ndt, &last); + JL_GC_PUSH2(&p, &ndt); + jl_value_t *last = iparams[ntp - 1]; if (istuple && ntp > 0 && jl_is_vararg(last)) { // normalize Tuple{..., Vararg{Int, 3}} to Tuple{..., Int, Int, Int} jl_value_t *va = jl_unwrap_unionall(last); From 02704d9b8fb3040721298fc2fe2ce2371b9c6c99 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 7 Apr 2023 14:30:13 -0400 Subject: [PATCH 460/775] normalize unions made in subtyping (#49276) We observed a case where simple_tmeet made a Union of egal things, which is undesirable. There also was no sorting of the result, as it normally done, and theoretically, simplification with an omit_bad_union to remove `S` could similar result in a Union that should be further simplified to remove redundancies. ``` Union{Union{Val{T}, S} where T<:AbstractString, Union{Val{T}, Int64} where T<:AbstractString} where S ``` (In principle, that simplification might also be possible to do during the original jl_type_union call when flattening it: see #49279) --- src/jltypes.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/subtype.c | 11 +++-------- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/jltypes.c b/src/jltypes.c index dc58cb57bfde6..fbcae4be5a542 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -582,6 +582,52 @@ JL_DLLEXPORT jl_value_t *jl_type_union(jl_value_t **ts, size_t n) return tu; } +jl_value_t *simple_union(jl_value_t *a, jl_value_t *b) +{ + size_t nt = count_union_components(&a, 1); + nt += count_union_components(&b, 1); + jl_value_t **temp; + JL_GC_PUSHARGS(temp, nt+1); + size_t count = 0; + flatten_type_union(&a, 1, temp, &count); + flatten_type_union(&b, 1, temp, &count); + assert(count == nt); + size_t i, j; + for (i = 0; i < nt; i++) { + int has_free = temp[i] != NULL && jl_has_free_typevars(temp[i]); + for (j = 0; j < nt; j++) { + if (j != i && temp[i] && temp[j]) { + if (temp[i] == jl_bottom_type || + temp[j] == (jl_value_t*)jl_any_type || + jl_egal(temp[i], temp[j]) || + (!has_free && !jl_has_free_typevars(temp[j]) && + // issue #24521: don't merge Type{T} where typeof(T) varies + !(jl_is_type_type(temp[i]) && jl_is_type_type(temp[j]) && jl_typeof(jl_tparam0(temp[i])) != jl_typeof(jl_tparam0(temp[j]))) && + jl_subtype(temp[i], temp[j]))) { + temp[i] = NULL; + } + } + } + } + isort_union(temp, nt); + jl_value_t **ptu = &temp[nt]; + *ptu = jl_bottom_type; + int k; + for (k = (int)nt-1; k >= 0; --k) { + if (temp[k] != NULL) { + if (*ptu == jl_bottom_type) + *ptu = temp[k]; + else + *ptu = jl_new_struct(jl_uniontype_type, temp[k], *ptu); + } + } + assert(*ptu != NULL); + jl_value_t *tu = *ptu; + JL_GC_POP(); + return tu; +} + + // unionall types ------------------------------------------------------------- JL_DLLEXPORT jl_value_t *jl_type_unionall(jl_tvar_t *v, jl_value_t *body) diff --git a/src/subtype.c b/src/subtype.c index 8f7488dcb27bc..9c6509f90e5a9 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -566,6 +566,7 @@ static int is_any_like(jl_value_t* x, jl_typeenv_t *env) JL_NOTSAFEPOINT return 0; } +jl_value_t *simple_union(jl_value_t *a, jl_value_t *b); // compute a least upper bound of `a` and `b` static jl_value_t *simple_join(jl_value_t *a, jl_value_t *b) { @@ -589,13 +590,7 @@ static jl_value_t *simple_join(jl_value_t *a, jl_value_t *b) return a; if (jl_is_typevar(b) && obviously_egal(a, ((jl_tvar_t*)b)->lb)) return b; - if (!jl_has_free_typevars(a) && !jl_has_free_typevars(b) && - // issue #24521: don't merge Type{T} where typeof(T) varies - !(jl_is_type_type(a) && jl_is_type_type(b) && jl_typeof(jl_tparam0(a)) != jl_typeof(jl_tparam0(b)))) { - if (jl_subtype(a, b)) return b; - if (jl_subtype(b, a)) return a; - } - return jl_new_struct(jl_uniontype_type, a, b); + return simple_union(a, b); } // Compute a greatest lower bound of `a` and `b` @@ -2759,7 +2754,7 @@ static jl_value_t *omit_bad_union(jl_value_t *u, jl_tvar_t *t) b = omit_bad_union(b, t); res = a == NULL ? b : b == NULL ? a : - jl_new_struct(jl_uniontype_type, a, b); + simple_join(a, b); JL_GC_POP(); } return res; From 1bd8482b0e1433f9f032d6ac500d5b7a2c690303 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 7 Apr 2023 15:39:20 -0400 Subject: [PATCH 461/775] subtype: reuse existing allocations when possible (#49277) When part of a type is a subtype of the other part, we can reuse the existing object to represent the result of that part. Additionally, we can sometimes defer allocating this until it is known how long it needs to be and whether it is already present in the type cache. --- src/subtype.c | 154 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 120 insertions(+), 34 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 9c6509f90e5a9..73e68ca671097 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -303,7 +303,7 @@ static void clean_occurs(jl_stenv_t *e) // type utilities // quickly test that two types are identical -static int obviously_egal(jl_value_t *a, jl_value_t *b) +static int obviously_egal(jl_value_t *a, jl_value_t *b) JL_NOTSAFEPOINT { if (a == (jl_value_t*)jl_typeofbottom_type->super) a = (jl_value_t*)jl_typeofbottom_type; // supertype(typeof(Union{})) is equal to, although distinct from, itself @@ -996,7 +996,7 @@ static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8 if (R && ans && e->envidx < e->envsz) { jl_value_t *val; if (vb.intvalued && vb.lb == (jl_value_t*)jl_any_type) - val = (jl_value_t*)jl_wrap_vararg(NULL, NULL); + val = (jl_value_t*)jl_wrap_vararg(NULL, NULL); // special token result that represents N::Int in the envout else if (!vb.occurs_inv && vb.lb != jl_bottom_type) val = is_leaf_bound(vb.lb) ? vb.lb : (jl_value_t*)jl_new_typevar(u->var->name, jl_bottom_type, vb.lb); else if (vb.lb == vb.ub) @@ -2816,8 +2816,9 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind res = jl_bottom_type; } } - else if (btemp->lb == (jl_value_t*)vb->var) + else if (btemp->lb == (jl_value_t*)vb->var) { btemp->lb = vb->lb; + } else if (btemp->depth0 == vb->depth0 && !jl_has_typevar(vb->lb, btemp->var) && !jl_has_typevar(vb->ub, btemp->var) && jl_has_typevar(btemp->ub, vb->var)) { // if our variable is T, and some outer variable has constraint S = Ref{T}, @@ -2830,8 +2831,9 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind btemp = btemp->prev; continue; } - else + else { btemp->lb = jl_new_struct(jl_unionall_type, vb->var, btemp->lb); + } assert((jl_value_t*)btemp->var != btemp->lb); } if (jl_has_typevar(btemp->ub, vb->var)) { @@ -3057,13 +3059,21 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, ssize_t if (intersect(xp1, yp1, e, param==0 ? 1 : param) == jl_bottom_type) return jl_bottom_type; jl_value_t *i2=NULL, *ii = intersect(xp1, yp1, e, 1); - if (ii == jl_bottom_type) return jl_bottom_type; - JL_GC_PUSH2(&ii, &i2); + if (ii == jl_bottom_type) + return jl_bottom_type; if (!xp2 && !yp2) { - ii = (jl_value_t*)jl_wrap_vararg(ii, NULL); - JL_GC_POP(); + if (obviously_egal(xp1, ii)) + ii = (jl_value_t*)vmx; + else if (obviously_egal(yp1, ii)) + ii = (jl_value_t*)vmy; + else { + JL_GC_PUSH1(&ii); + ii = (jl_value_t*)jl_wrap_vararg(ii, NULL); + JL_GC_POP(); + } return ii; } + JL_GC_PUSH2(&ii, &i2); assert(e->Loffset == 0); e->Loffset = offset; jl_varbinding_t *xb = NULL, *yb = NULL; @@ -3090,7 +3100,14 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, ssize_t } assert(e->Loffset == offset); e->Loffset = 0; - ii = i2 == jl_bottom_type ? (jl_value_t*)jl_bottom_type : (jl_value_t*)jl_wrap_vararg(ii, i2); + if (i2 == jl_bottom_type) + ii = (jl_value_t*)jl_bottom_type; + else if (xp2 && obviously_egal(xp1, ii) && obviously_egal(xp2, i2)) + ii = (jl_value_t*)vmx; + else if (yp2 && obviously_egal(yp1, ii) && obviously_egal(yp2, i2)) + ii = (jl_value_t*)vmy; + else + ii = (jl_value_t*)jl_wrap_vararg(ii, i2); JL_GC_POP(); return ii; } @@ -3105,27 +3122,46 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten int vvy = (ly > 0 && jl_is_vararg(jl_tparam(yd, ly-1))); if (!vvx && !vvy && lx != ly) return jl_bottom_type; - jl_svec_t *params = jl_alloc_svec(lx > ly ? lx : ly); - jl_value_t *res=NULL; - JL_GC_PUSH1(¶ms); + + size_t np = lx > ly ? lx : ly; + jl_value_t *res = NULL; + jl_svec_t *p = NULL; + jl_value_t **params; + jl_value_t **roots; + JL_GC_PUSHARGS(roots, np < 64 ? np : 1); + if (np < 64) { + params = roots; + } + else { + p = jl_alloc_svec(np); + roots[0] = (jl_value_t*)p; + params = jl_svec_data(p); + } size_t i=0, j=0; jl_value_t *xi, *yi; + int isx = 1, isy = 1; // try to reuse the object x or y as res whenever we can (e.g. when it is the supertype) instead of allocating a copy while (1) { vx = vy = 0; xi = i < lx ? jl_tparam(xd, i) : NULL; yi = j < ly ? jl_tparam(yd, j) : NULL; if (xi == NULL && yi == NULL) { - assert(i == j && i == jl_svec_len(params)); + assert(i == j && i == np); break; } if (xi && jl_is_vararg(xi)) vx = 1; if (yi && jl_is_vararg(yi)) vy = 1; if (xi == NULL || yi == NULL) { - res = jl_bottom_type; - if (vx && intersect_vararg_length(xi, ly+1-lx, e, 0)) - res = (jl_value_t*)jl_apply_tuple_type_v(jl_svec_data(params), j); - if (vy && intersect_vararg_length(yi, lx+1-ly, e, 1)) - res = (jl_value_t*)jl_apply_tuple_type_v(jl_svec_data(params), i); + if (vx && intersect_vararg_length(xi, ly+1-lx, e, 0)) { + np = j; + p = NULL; + } + else if (vy && intersect_vararg_length(yi, lx+1-ly, e, 1)) { + np = i; + p = NULL; + } + else { + res = jl_bottom_type; + } break; } jl_value_t *ii = NULL; @@ -3134,13 +3170,13 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten (jl_vararg_t*)yi, ly - lx, // xi's offset: {A^n...,Vararg{T,N}} ∩ {Vararg{S,M}} // {(A∩S)^n...,Vararg{T∩S,N}} plus N = M-n - e, param); + e, + param); else { - if (vx) - xi = jl_unwrap_vararg(xi); - if (vy) - yi = jl_unwrap_vararg(yi); - ii = intersect(xi, yi, e, param == 0 ? 1 : param); + ii = intersect(vx ? jl_unwrap_vararg(xi) : xi, + vy ? jl_unwrap_vararg(yi) : yi, + e, + param == 0 ? 1 : param); } if (ii == jl_bottom_type) { if (vx && vy) { @@ -3160,7 +3196,8 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten assert(e->Loffset == 0); if (xb) set_var_to_const(xb, jl_box_long(len-lx+1), e, 0); if (yb) set_var_to_const(yb, jl_box_long(len-ly+1), e, 1); - res = (jl_value_t*)jl_apply_tuple_type_v(jl_svec_data(params), len); + np = len; + p = NULL; } } else { @@ -3168,15 +3205,44 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten } break; } - jl_svecset(params, (i > j ? i : j), ii); + isx = isx && ii == xi; + isy = isy && ii == yi; + if (p) + jl_svecset(p, (i > j ? i : j), ii); + else + params[i > j ? i : j] = ii; if (vx && vy) break; if (i < lx-1 || !vx) i++; if (j < ly-1 || !vy) j++; } // TODO: handle Vararg with explicit integer length parameter - if (res == NULL) - res = (jl_value_t*)jl_apply_tuple_type(params); + if (res == NULL) { + assert(!p || np == jl_svec_len(p)); + isx = isx && lx == np; + isy = isy && ly == np; + if (!isx && !isy) { + // do a more careful check now for equivalence + if (lx == np) { + isx = 1; + for (i = 0; i < np; i++) + isx = isx && obviously_egal(params[i], jl_tparam(xd, i)); + } + if (!isx && ly == np) { + isy = 1; + for (i = 0; i < np; i++) + isy = isy && obviously_egal(params[i], jl_tparam(yd, i)); + } + } + if (isx) + res = (jl_value_t*)xd; + else if (isy) + res = (jl_value_t*)yd; + else if (p) + res = (jl_value_t*)jl_apply_tuple_type(p); + else + res = (jl_value_t*)jl_apply_tuple_type_v(params, np); + } JL_GC_POP(); return res; } @@ -3536,20 +3602,40 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa size_t i, np = jl_nparams(xd); jl_value_t **newparams; JL_GC_PUSHARGS(newparams, np); - for (i=0; i < np; i++) { + int isx = 1, isy = 1; // try to reuse the object x or y as res whenever we can (e.g. when it is the supertype) instead of allocating a copy + for (i = 0; i < np; i++) { jl_value_t *xi = jl_tparam(xd, i), *yi = jl_tparam(yd, i); jl_value_t *ii = intersect_invariant(xi, yi, e); if (ii == NULL) break; + isx = isx && ii == xi; + isy = isy && ii == yi; newparams[i] = ii; } jl_value_t *res = jl_bottom_type; - if (i >= np) { - JL_TRY { - res = jl_apply_type(xd->name->wrapper, newparams, np); + if (i == np) { + if (!isx && !isy) { + // do a more careful check now for equivalence + isx = 1; + for (i = 0; i < np; i++) + isx = isx && obviously_egal(newparams[i], jl_tparam(xd, i)); + if (!isx) { + isy = 1; + for (i = 0; i < np; i++) + isy = isy && obviously_egal(newparams[i], jl_tparam(yd, i)); + } } - JL_CATCH { - res = jl_bottom_type; + if (isx) + res = x; + else if (isy) + res = y; + else { + JL_TRY { + res = jl_apply_type(xd->name->wrapper, newparams, np); + } + JL_CATCH { + res = jl_bottom_type; + } } } JL_GC_POP(); From 50606b2c8d161ee25e2736443691816f9ed0b2fc Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Fri, 7 Apr 2023 15:40:01 -0400 Subject: [PATCH 462/775] deps: Upgrade OpenBLAS to v0.3.23 (#49283) --- deps/checksums/openblas | 186 ++++++++++++------------- deps/openblas.mk | 7 +- deps/openblas.version | 6 +- deps/patches/openblas-power-test.patch | 55 -------- stdlib/OpenBLAS_jll/Project.toml | 2 +- 5 files changed, 97 insertions(+), 159 deletions(-) delete mode 100644 deps/patches/openblas-power-test.patch diff --git a/deps/checksums/openblas b/deps/checksums/openblas index 96098b9fccf2c..99577b1427805 100644 --- a/deps/checksums/openblas +++ b/deps/checksums/openblas @@ -1,94 +1,92 @@ -OpenBLAS.v0.3.21+4.aarch64-apple-darwin-libgfortran5.tar.gz/md5/86311621de9f353cdc4495718544c4cc -OpenBLAS.v0.3.21+4.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/7b4b0159d120f3f822d59432091a64c8556fcc2d1c99c8d366400768bf31a463393027f8caeec2bacbb221d3c09142814bd7ffd1f8f479a8942aa177256642ae -OpenBLAS.v0.3.21+4.aarch64-linux-gnu-libgfortran3.tar.gz/md5/eff07fb30c3e35d26fa3a7d0fc837c20 -OpenBLAS.v0.3.21+4.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/70000c634af84974e96ac13481ba729c4668342cd06cb98032c1ad4d23890bafc7f9c2692e8ebae7127d88fc27553e382b80edb078f1a1ba6c13535f56535685 -OpenBLAS.v0.3.21+4.aarch64-linux-gnu-libgfortran4.tar.gz/md5/0015556833727545b248985acf9a79dd -OpenBLAS.v0.3.21+4.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/5b0007054c67629b86520298b462b07908c57a0c42871db99ce834673f7f2164e0cea0b952c4dcf2678cbf75354bcf0a8feee37266ddd41ef2c33a848666b00f -OpenBLAS.v0.3.21+4.aarch64-linux-gnu-libgfortran5.tar.gz/md5/1888c11bfc930840fe57d29ac4fb1dbd -OpenBLAS.v0.3.21+4.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/571f985d34ee403f9af09a143c695c4c626f32b363ae2439d8ca4057041fc966217c6d912089ad0c675e573253cd9327099e71f293cbe5f06be4e077da9f48ea -OpenBLAS.v0.3.21+4.aarch64-linux-musl-libgfortran3.tar.gz/md5/ec3bb6b7d998e37b52ae61439f4e4c58 -OpenBLAS.v0.3.21+4.aarch64-linux-musl-libgfortran3.tar.gz/sha512/7ad0bbfa194f7c8c30533d81a15c4229d300ed1c108af975f381a7b644c1da377b11cff60b60ee193152b91e0d29967f79d9b32d44b54d9b2230a768427ab28a -OpenBLAS.v0.3.21+4.aarch64-linux-musl-libgfortran4.tar.gz/md5/45bffcba3c88e111d903b95d503bead6 -OpenBLAS.v0.3.21+4.aarch64-linux-musl-libgfortran4.tar.gz/sha512/be76750ff245ad91d5e6fad268619ec8732ad1fc1236e02c8e4ff65ecbf5502fa1cae89b026e0414dfe8ec38d002acf79d18f4ae8a320da6dedb3760db969c04 -OpenBLAS.v0.3.21+4.aarch64-linux-musl-libgfortran5.tar.gz/md5/6932c7351aed13bfc25ba56a283595d5 -OpenBLAS.v0.3.21+4.aarch64-linux-musl-libgfortran5.tar.gz/sha512/2df1fd87da29d1ee85bc3208e3435756bfe615df49385072a2a18e15ba54fba754e94be0fdce9f7c370af5ff746b5841beda9a3f5f011b8472b2b579ca2eded5 -OpenBLAS.v0.3.21+4.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/367657c9e84b6016d3b835d98af3dd2a -OpenBLAS.v0.3.21+4.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/3a660c0096f086742e2bac219db732244f35bf527537302e8590ea6e6a369438268ebc479a67096e0ac872f5792f149c6097c64a8afb2624e09687fa4f3bf023 -OpenBLAS.v0.3.21+4.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/86309e18c22fc5fa5e437bc5b8814f28 -OpenBLAS.v0.3.21+4.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/ca51805940efadb27fcdd845551f57a936fbdfbc3caf71fb04eb70360686b75ec5eaf8957e860622d5cbfa4305edacdcfd49bbb48134cd05b96b499faa8e2fd4 -OpenBLAS.v0.3.21+4.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/993d99064baa38498d0c40829bc0899a -OpenBLAS.v0.3.21+4.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/267ebe796afa475a41bb57d6eed1397a5b434945446cd2e612024218fa93f9342bcc4fb8cee0957422aa31ee89c77fe4b07f3f573eb01b6fad0d52d859c7df6c -OpenBLAS.v0.3.21+4.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/243ee32320336ada3524545164ba1fd3 -OpenBLAS.v0.3.21+4.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/2e6b95d82004b8e411441d13e90dd39e39b57288f3502d077daf811709ca1f2eab10fed66648de7cbd0ee37bebb9eef46bd5f476e9ff942f1110b4cde337cea6 -OpenBLAS.v0.3.21+4.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/586032d64e693a46dfe7ae5f56cc6bb3 -OpenBLAS.v0.3.21+4.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/1bc53988dcb22dc82d434a151eb74eadca00ffe3822575497a10229fda967c333828137e76a4afbcc8576ac9261f492ccb4e1e70eb22da977a189b39a72bde63 -OpenBLAS.v0.3.21+4.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/a6b2acfa27a2cf042f228e3f79288161 -OpenBLAS.v0.3.21+4.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/e4b04da8a2002946ca674e191851c506723a91837232025c9e23115348df3187b041725d2406a592e48a595aa3fbe8ff9da9ae70ad8d366e4c118fdba52c9411 -OpenBLAS.v0.3.21+4.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/367657c9e84b6016d3b835d98af3dd2a -OpenBLAS.v0.3.21+4.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/3a660c0096f086742e2bac219db732244f35bf527537302e8590ea6e6a369438268ebc479a67096e0ac872f5792f149c6097c64a8afb2624e09687fa4f3bf023 -OpenBLAS.v0.3.21+4.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/86309e18c22fc5fa5e437bc5b8814f28 -OpenBLAS.v0.3.21+4.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/ca51805940efadb27fcdd845551f57a936fbdfbc3caf71fb04eb70360686b75ec5eaf8957e860622d5cbfa4305edacdcfd49bbb48134cd05b96b499faa8e2fd4 -OpenBLAS.v0.3.21+4.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/993d99064baa38498d0c40829bc0899a -OpenBLAS.v0.3.21+4.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/267ebe796afa475a41bb57d6eed1397a5b434945446cd2e612024218fa93f9342bcc4fb8cee0957422aa31ee89c77fe4b07f3f573eb01b6fad0d52d859c7df6c -OpenBLAS.v0.3.21+4.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/243ee32320336ada3524545164ba1fd3 -OpenBLAS.v0.3.21+4.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/2e6b95d82004b8e411441d13e90dd39e39b57288f3502d077daf811709ca1f2eab10fed66648de7cbd0ee37bebb9eef46bd5f476e9ff942f1110b4cde337cea6 -OpenBLAS.v0.3.21+4.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/586032d64e693a46dfe7ae5f56cc6bb3 -OpenBLAS.v0.3.21+4.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/1bc53988dcb22dc82d434a151eb74eadca00ffe3822575497a10229fda967c333828137e76a4afbcc8576ac9261f492ccb4e1e70eb22da977a189b39a72bde63 -OpenBLAS.v0.3.21+4.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/a6b2acfa27a2cf042f228e3f79288161 -OpenBLAS.v0.3.21+4.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/e4b04da8a2002946ca674e191851c506723a91837232025c9e23115348df3187b041725d2406a592e48a595aa3fbe8ff9da9ae70ad8d366e4c118fdba52c9411 -OpenBLAS.v0.3.21+4.i686-linux-gnu-libgfortran3.tar.gz/md5/e752b2e67a151ea7cfb76c9b15b687e1 -OpenBLAS.v0.3.21+4.i686-linux-gnu-libgfortran3.tar.gz/sha512/230a5fade6d4a205d932362ce40b5455229247ed279e6a0d7105d6207c28d699094c227216aad267aa053878b914043284c7dd9d1c2e4d26d0978efc9071bb48 -OpenBLAS.v0.3.21+4.i686-linux-gnu-libgfortran4.tar.gz/md5/478d429202eb287a840df5bbf191b878 -OpenBLAS.v0.3.21+4.i686-linux-gnu-libgfortran4.tar.gz/sha512/31f98104f1355c01aa86f2fb01165fdf93035319285987d93045518fc4ecc84b59a911369169fc534a4ab3b18c27fe49dc80125263d73ad7d265e6e3913d25a4 -OpenBLAS.v0.3.21+4.i686-linux-gnu-libgfortran5.tar.gz/md5/c8821f59bc938244b2bdc46278c041a5 -OpenBLAS.v0.3.21+4.i686-linux-gnu-libgfortran5.tar.gz/sha512/475413dda79b2263a19eeb657f2c31e36f2f915a702327b3331d294c8783a4f6f4f31b672edfed14cdbdbd2b4f5bf36273f70fa2dd1ec5611925f7a55971780f -OpenBLAS.v0.3.21+4.i686-linux-musl-libgfortran3.tar.gz/md5/9ba93d57e2cf8695699b881715f32f2c -OpenBLAS.v0.3.21+4.i686-linux-musl-libgfortran3.tar.gz/sha512/eb0ea7be9853ab1c220545f628cea4047deacf1fd704c889950f7aeafb58dc75e6cccd1b0c85c30ca12007cce05773382bf4a944f61aa9a0ed0f51621b45fc64 -OpenBLAS.v0.3.21+4.i686-linux-musl-libgfortran4.tar.gz/md5/a4f7aa370d7023f0b062a255a48ff81f -OpenBLAS.v0.3.21+4.i686-linux-musl-libgfortran4.tar.gz/sha512/cc8fd2dd011d4007ea5bd99d909776ca2a3700f6457a92cb43f77684b0dfa5a13f808e03db142647766c385e2bbd4db97f90701f286f5cb04276153d8f6a94fa -OpenBLAS.v0.3.21+4.i686-linux-musl-libgfortran5.tar.gz/md5/5b33f48568c3347a98a72d44786f14c7 -OpenBLAS.v0.3.21+4.i686-linux-musl-libgfortran5.tar.gz/sha512/684ac712c035b14ec1b9b1f0ebad1b813b19155852b7b2af75e5956533d8b90e09bc9652a2beb908d2a1c7e2d22fccbf5479aab10d95528aa173d603f60f6135 -OpenBLAS.v0.3.21+4.i686-w64-mingw32-libgfortran3.tar.gz/md5/7f49b3f4847564539398d50abd8c8f47 -OpenBLAS.v0.3.21+4.i686-w64-mingw32-libgfortran3.tar.gz/sha512/8d57f41a3c092f2373e5ecb91ef7e30176d72686d9b7906cf2a84ebb4d0ed0e4a9303b0814d1bcca13d9345ae424ea4bc40ede656b2084050ca76ce2e0bf1fa9 -OpenBLAS.v0.3.21+4.i686-w64-mingw32-libgfortran4.tar.gz/md5/f75f688f483d0a782365f95e8463f804 -OpenBLAS.v0.3.21+4.i686-w64-mingw32-libgfortran4.tar.gz/sha512/cbb4e0a82f3f003f390241af787ff5579e75a0b37c8174976b19760410718cacbacf40f352e6ec56ab7a88ef56357523606400ded23e0931d8d6c791196e30c5 -OpenBLAS.v0.3.21+4.i686-w64-mingw32-libgfortran5.tar.gz/md5/16e0538b1492142f35e3aefa19d95a8c -OpenBLAS.v0.3.21+4.i686-w64-mingw32-libgfortran5.tar.gz/sha512/21190512ce1c6d5267e01ccf92c111be7bc8807a6114d57b2a5ac74d9be749a82143ad269a293164bf040dc5f5e5353702d49ed48a72dbe9e996e59ac226b407 -OpenBLAS.v0.3.21+4.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/a6d6d1f7bf381edcdc01b47a08ef31f7 -OpenBLAS.v0.3.21+4.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/99bb2a5736eb6c04be445f3bde5e4e3d1010053d615c02d1de69ec65a69f08c5a1a77e146489b69112bb830702d56fa2a3111f44b30f1369e71fbd69afa1e9d2 -OpenBLAS.v0.3.21+4.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/71b32f5fa2b38cc49e08f10c6d28610a -OpenBLAS.v0.3.21+4.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/0cc9c46b299ecf81d7174f962f75562b78ab9c2df794f3dc2dc7c3c15ccafe4baf3f5d23fa3e9ae754b3a0d40f493433829efa15228bf046548d32ee5a90f020 -OpenBLAS.v0.3.21+4.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/2ede77add3d501c98d2b38aebf7f82b5 -OpenBLAS.v0.3.21+4.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/97e4588004ad8b74f72c64fafef150ef0f774b50d48a8b83e269217fccea6b63e8fd9ec9a4907b14d96fee2bd24cd1a347f2744367b2de22d5b7f5a753140892 -OpenBLAS.v0.3.21+4.x86_64-apple-darwin-libgfortran3.tar.gz/md5/c51184816f4b576e0b79b776a631b064 -OpenBLAS.v0.3.21+4.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/cc67ee691f93cf15057ee513a30ff31e440a6438d8ab9eb9a3a6bd10b44cc8ba6428c18dd454f70cf2066f2bbab99374e373b5bda9a3201b0c97239efad135e8 -OpenBLAS.v0.3.21+4.x86_64-apple-darwin-libgfortran4.tar.gz/md5/e5ff0d607488f582317554f856c825f5 -OpenBLAS.v0.3.21+4.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/466801af19cf67bf2755adbc2c895eda7adf67af94e58a21b1415175b3ff0e4913b6b4c14f0d57aa589cdea1972dc597cdd7c345a6fa881b64aa999dc55b22e9 -OpenBLAS.v0.3.21+4.x86_64-apple-darwin-libgfortran5.tar.gz/md5/f35f0f769d71f1bb603f7213a2898d2b -OpenBLAS.v0.3.21+4.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/72e80a685c21d7570d4127ee88baa1900ea64af1c91d307bfe3049a02ad5672c1330549625f10db91ca8dfa45c664cd35bf1e702b110b8317e7c4392cbcfc323 -OpenBLAS.v0.3.21+4.x86_64-linux-gnu-libgfortran3.tar.gz/md5/9c7563a8df3c1d27e4db9f8e5288fc45 -OpenBLAS.v0.3.21+4.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/c6f81ec1da096f6e27e6179235042f0558bc5e3ade27a230bafb2f121b48bc96d7d9e0c45b0e4dc8fee10257913eccabcfcaf67d515e0ba4f373f1ca4869773c -OpenBLAS.v0.3.21+4.x86_64-linux-gnu-libgfortran4.tar.gz/md5/206f6302a757a1a30ab6716b6e508741 -OpenBLAS.v0.3.21+4.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/35adbec09c4a636131a757f4b51d7b458f23e7b7118194be63a116746b2e1e7a15990bd76e8ecbbe2a6010cb6e54bca3a296f02d83b27c6394b42bdffa041653 -OpenBLAS.v0.3.21+4.x86_64-linux-gnu-libgfortran5.tar.gz/md5/9aa48579f0c50500796de228f00909d8 -OpenBLAS.v0.3.21+4.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/ef1b1578259b055a23df4e57bb4bf2c1ef94e4d0d1e63eda622db291256ef8cde8694befcdc83b507cf6c3f47db142b51e6bac614ec32bae92ae576ddd6e2f15 -OpenBLAS.v0.3.21+4.x86_64-linux-musl-libgfortran3.tar.gz/md5/ba5ac7e48691965b3d5045785d37618e -OpenBLAS.v0.3.21+4.x86_64-linux-musl-libgfortran3.tar.gz/sha512/0e37f806faa5bc5cd8e5096efbbbef458720c267ed69028e66ced3269baac53c9e0a92ab728ceab499f6aed3edbcbca6c2b1caaa6e145c386bafb57657a6c353 -OpenBLAS.v0.3.21+4.x86_64-linux-musl-libgfortran4.tar.gz/md5/e9b1ab8fcd6b62b92db6e064054a02ea -OpenBLAS.v0.3.21+4.x86_64-linux-musl-libgfortran4.tar.gz/sha512/75706214b660df35daa3865cc267087ca9aecd04b658d5b9d867d15bad901f00677766c04d7701b45d5238aaf4ed09fafff0ca07e9f501dded9479eea26a5112 -OpenBLAS.v0.3.21+4.x86_64-linux-musl-libgfortran5.tar.gz/md5/d86ecacd9904ef70fe17b762e70986e7 -OpenBLAS.v0.3.21+4.x86_64-linux-musl-libgfortran5.tar.gz/sha512/12edb53c88d7deb4585092ca9d9e3c0578d0ca0e91b388c2eddf84dc080c971dba122195b750e2aa4c3a532a7df2c9da7b56b565f3c2002282621cc63425954e -OpenBLAS.v0.3.21+4.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/494fccd04e83345dc20bd753319b8ed3 -OpenBLAS.v0.3.21+4.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/3000b9ec7b66e09597759570eb77645dec1e82a0b4f56ab9439f2fa3e55bbc2863cc61cbbe77752415fd5926010b1647fffedb8dacaa77b87f815d98bb47d86b -OpenBLAS.v0.3.21+4.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/df1133b14f2561b02f4f2e52106635ed -OpenBLAS.v0.3.21+4.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/ed2c18a9f0768412128dd2e370d85868e536cf3ca4d0d03bc626e4690ba81965c540f08d00b46208a7969bd07b9848e6ff8a14e91a4e927d2b44e9ba5b374e8c -OpenBLAS.v0.3.21+4.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/0ed230111e50d49609d631a2f205dff5 -OpenBLAS.v0.3.21+4.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/1940a500b25f1c2613d2f2ab6533309991808622082a7910985fc0573d41358c5c9311c9bb65a00d5508e7d9e4612773e97adb860cba2c2f4f3957782d695f21 -OpenBLAS.v0.3.21+4.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/f156c495d81f4c70690f7e5be02d8816 -OpenBLAS.v0.3.21+4.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/30b575c6f17b3b59a1d21feba9922a230021a99cd19126bb1c955025906ff7539ac8427b9ec82a950988fa251409b045007a1a2223f6e710b0a6f734f8c00ad5 -OpenBLAS.v0.3.21+4.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/57c919083397ddb90621148635b11bb7 -OpenBLAS.v0.3.21+4.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/7fc6dc8aaea7ed725018e0aabcf3185559ce196f75ec2f18405eaac641d3decb1aae577ace684ffd2539346dcd1444f8f17414291085878c5f80a56550e338cb -OpenBLAS.v0.3.21+4.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/1cdb4c9a5ae04c7c9127acb06a7acbc2 -OpenBLAS.v0.3.21+4.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/a479cef47e8aff321ee9835dfcd69f89db2921afd1e253103d43e0a5b9d831259b07ca99adffd76d412a296e447b58c6585ea29a5905a6266d1d853b50067203 -openblas-b89fb708caa5a5a32de8f4306c4ff132e0228e9a.tar.gz/md5/716ebe95d4b491253cdde8308b8adb83 -openblas-b89fb708caa5a5a32de8f4306c4ff132e0228e9a.tar.gz/sha512/00e7bde49525c2c28bf07b47290e00b53bff446be63f09e90c51724c6350e5ddc90f5a071ae6de057b3fbb107060e70bf16683fcefcf48ae37ba1d0758be553b +OpenBLAS.v0.3.23+0.aarch64-apple-darwin-libgfortran5.tar.gz/md5/f4ab1aa718db6ab731179199b48506ad +OpenBLAS.v0.3.23+0.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/5cd6326eab751d087b6638acc256a7c5dfc3a8a4be8949f4e2b5b8079aedc05cd8569774da19912fcbcd2dc1eac6a09d72d19bdbeded1198317992a85ccd605b +OpenBLAS.v0.3.23+0.aarch64-linux-gnu-libgfortran3.tar.gz/md5/57b8903e05998d293d28e70ee6cbc4d8 +OpenBLAS.v0.3.23+0.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/03325728191f88dcfc2bea16d818c0325b4f42019ed9c2e0533233e8e2a4da09a2c70503632fef2ab55ed12b7da39fdab470b801d34a9b6f576bda509f8a8a8d +OpenBLAS.v0.3.23+0.aarch64-linux-gnu-libgfortran4.tar.gz/md5/fe529647382de5693557363f658c71b6 +OpenBLAS.v0.3.23+0.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/77ac56f683a481477fa898d208e67c0c04c1ab8ca9dacb1e4e4ea3795fadb2604faffd1f3fd35d53eecb223c7f92de40cc8b2bdeb9c8a6a1b6a9949965cb9380 +OpenBLAS.v0.3.23+0.aarch64-linux-gnu-libgfortran5.tar.gz/md5/5aea8a00a946273a154110ca7b468214 +OpenBLAS.v0.3.23+0.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/a606933bed17e563d15ac206a4a37d38d75e9bb0bef46ef62485dcd32aa5a0e8501dab01f6887a1e60736c59177c6fbf0ec541fa521a9a8de854f44703f337c3 +OpenBLAS.v0.3.23+0.aarch64-linux-musl-libgfortran3.tar.gz/md5/d81dc2a42a8c0d87f4ee9bad98579f2a +OpenBLAS.v0.3.23+0.aarch64-linux-musl-libgfortran3.tar.gz/sha512/f2bda57546f1b9aa1f8dfe9a07b2243cadc002a9ffefbcfdde344ccc96efb07608a55bf8dbb6de34925af03f01ac5487f9fe293befa84edd9a84c01a9b7409e1 +OpenBLAS.v0.3.23+0.aarch64-linux-musl-libgfortran4.tar.gz/md5/400ba512f73a60420aa0d316bc24db48 +OpenBLAS.v0.3.23+0.aarch64-linux-musl-libgfortran4.tar.gz/sha512/927c711c3950f24e6b4c22c6dd92cd2b212e3df9241c637ff42f5b9135e7bee8f3864868aea594c6e8ba5b40f0563d63a5f8634ea3c3276bec35d480601e76e5 +OpenBLAS.v0.3.23+0.aarch64-linux-musl-libgfortran5.tar.gz/md5/6a91ea53f3aff17b602b324d025309c5 +OpenBLAS.v0.3.23+0.aarch64-linux-musl-libgfortran5.tar.gz/sha512/8ee85883fcc605c16031bafdd0f1a4f4d4a5957a4f85c2022466232f902a4cf64c284537dd2f237221f7d0c154e2b46200501891d3990e94dcf49a74a66c36de +OpenBLAS.v0.3.23+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/c653ff340dc25b19ca36309060dd6b1a +OpenBLAS.v0.3.23+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/cc77c84538bb0301eaa98ca1a32f024da6242e40e847e71f4a36ab69233590422aea41a32ee67031d8055c929f741617053416e5b9d446affa36e7233e5af48b +OpenBLAS.v0.3.23+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/18a914a1df2be07ff6b419617cb6347f +OpenBLAS.v0.3.23+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/eafab27655b0c179ad8b9b1dc818e8394d365f19cf75a0d77402951a38e204aa2fbe580037116a28e8e1254b66d15a543ccd0f438f3ae388e8bcad39f5953c64 +OpenBLAS.v0.3.23+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/4b8d18500b4bdc6f1081da6f0837340f +OpenBLAS.v0.3.23+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/6512bd03d58b7669dba7f9830d3f8654b2747ee66c7bfc05acdbca6c3d2c3750c9d1163768a3f91d56c5a87cb30705ad6f10395652fee4c9cd06cd2920db3027 +OpenBLAS.v0.3.23+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/27fd022a3b84c3a92da9d6062d8dafaf +OpenBLAS.v0.3.23+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/c0e73f2012df2453cc6231a9e7a644609ba1280c9aea63d2cbbf9594539fb26c8f9ab6976de8ec9870cab483b1fe7e3a1fc81246fa99bbd7526051e74a4733e1 +OpenBLAS.v0.3.23+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/e2b0503bf1144f4b6a65ae9f09b25828 +OpenBLAS.v0.3.23+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/204678995b9f337e4ddae793762c3a00968faa3da3433ea17578944fd56f33c381150521b6a561d6ff2022693f8d46b9d0f32f330e500036b4bfc08a7dbd8a62 +OpenBLAS.v0.3.23+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/3e733c1c668a3efaccfde643092595e5 +OpenBLAS.v0.3.23+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/4a37e5de66920f20a648118f62555755b51e6e089e7ee43d2b7b8ec0dc47e68c7705b878158ad83d152cfebf77118f789d1bf7b2ee0702334d4317f0c6a926a1 +OpenBLAS.v0.3.23+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/c653ff340dc25b19ca36309060dd6b1a +OpenBLAS.v0.3.23+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/cc77c84538bb0301eaa98ca1a32f024da6242e40e847e71f4a36ab69233590422aea41a32ee67031d8055c929f741617053416e5b9d446affa36e7233e5af48b +OpenBLAS.v0.3.23+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/18a914a1df2be07ff6b419617cb6347f +OpenBLAS.v0.3.23+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/eafab27655b0c179ad8b9b1dc818e8394d365f19cf75a0d77402951a38e204aa2fbe580037116a28e8e1254b66d15a543ccd0f438f3ae388e8bcad39f5953c64 +OpenBLAS.v0.3.23+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/4b8d18500b4bdc6f1081da6f0837340f +OpenBLAS.v0.3.23+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/6512bd03d58b7669dba7f9830d3f8654b2747ee66c7bfc05acdbca6c3d2c3750c9d1163768a3f91d56c5a87cb30705ad6f10395652fee4c9cd06cd2920db3027 +OpenBLAS.v0.3.23+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/27fd022a3b84c3a92da9d6062d8dafaf +OpenBLAS.v0.3.23+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/c0e73f2012df2453cc6231a9e7a644609ba1280c9aea63d2cbbf9594539fb26c8f9ab6976de8ec9870cab483b1fe7e3a1fc81246fa99bbd7526051e74a4733e1 +OpenBLAS.v0.3.23+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/e2b0503bf1144f4b6a65ae9f09b25828 +OpenBLAS.v0.3.23+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/204678995b9f337e4ddae793762c3a00968faa3da3433ea17578944fd56f33c381150521b6a561d6ff2022693f8d46b9d0f32f330e500036b4bfc08a7dbd8a62 +OpenBLAS.v0.3.23+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/3e733c1c668a3efaccfde643092595e5 +OpenBLAS.v0.3.23+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/4a37e5de66920f20a648118f62555755b51e6e089e7ee43d2b7b8ec0dc47e68c7705b878158ad83d152cfebf77118f789d1bf7b2ee0702334d4317f0c6a926a1 +OpenBLAS.v0.3.23+0.i686-linux-gnu-libgfortran3.tar.gz/md5/639643a12f8018e4be7bb1f9f29e57f6 +OpenBLAS.v0.3.23+0.i686-linux-gnu-libgfortran3.tar.gz/sha512/0993e1967964874a3f90610745d82369ee70fa4313445391fdcb26c4218c6badb18577c67648d2f77f359b163dafde31a3723998e0b006622effeace506b669f +OpenBLAS.v0.3.23+0.i686-linux-gnu-libgfortran4.tar.gz/md5/13ec86d62840258c425b0a5a6824a609 +OpenBLAS.v0.3.23+0.i686-linux-gnu-libgfortran4.tar.gz/sha512/0bc74dac87b8ab5ea244fa5bcd05baf2968b7041c4eb392ff808d0aae897cec4b3082ef7fecda28aea2662b6cd956a5254212740b1802a947dd3f1e5a3dfe2d2 +OpenBLAS.v0.3.23+0.i686-linux-gnu-libgfortran5.tar.gz/md5/413d4eae7b9c409204ab5fb7867dc30f +OpenBLAS.v0.3.23+0.i686-linux-gnu-libgfortran5.tar.gz/sha512/4a484d2aa239d8c1e2733cd9d16bd17549f5048d9958899a4e20039a7efcfd280bba901f3fe63b3b079fd7fae88911f7201a7649a472d47d0148ba8520f350cb +OpenBLAS.v0.3.23+0.i686-linux-musl-libgfortran3.tar.gz/md5/7f342d27a9b193b5d37e2ae4de6e4640 +OpenBLAS.v0.3.23+0.i686-linux-musl-libgfortran3.tar.gz/sha512/2927b18e176e07fe8a05d2eba24f6160680131832094bde9634f0890c1bc3b877c3293163fc65067cea402f3e75871c41b47e4a9999f273e667ac400878aa2b2 +OpenBLAS.v0.3.23+0.i686-linux-musl-libgfortran4.tar.gz/md5/523c007c319adbdde6e8cd7d3d89a9a1 +OpenBLAS.v0.3.23+0.i686-linux-musl-libgfortran4.tar.gz/sha512/ddb7a8d67c9430976ad967e21a6b8717c8a5501e8808fabf6e7b2e7298a0ca56049dcfc12214a5a19dbf7bd52d625b0b2b1bcc6b4c1d921c3ee62fd2766da891 +OpenBLAS.v0.3.23+0.i686-linux-musl-libgfortran5.tar.gz/md5/7dd91db180e59da5f866f73eaccc4d1d +OpenBLAS.v0.3.23+0.i686-linux-musl-libgfortran5.tar.gz/sha512/ff0ee65e536eae5ece7fbc00a0735349d560a142e025084d64f28891bdd3da5914e976640be354d8ad34fd3d89bfb90461eb95f2426d5e292906ed4ead1cfafc +OpenBLAS.v0.3.23+0.i686-w64-mingw32-libgfortran3.tar.gz/md5/fef43c3fed5ed7e9fdd9c7757be6b95e +OpenBLAS.v0.3.23+0.i686-w64-mingw32-libgfortran3.tar.gz/sha512/b580c1da073ed94d1a259183c5b2a6896a746c5e88c83e2df57fea801f259cb49f99b3468bbc5c1d7dc6bb84f597843bc3c383c9cab7608dbfbbb15352fb1012 +OpenBLAS.v0.3.23+0.i686-w64-mingw32-libgfortran4.tar.gz/md5/88db137baca7ce99e58ff3b13ee73644 +OpenBLAS.v0.3.23+0.i686-w64-mingw32-libgfortran4.tar.gz/sha512/1608f3ee3964df833db9a1277fb9f69e3bb1d328a27482ac419e08520a51b2cb25501cf8986b2ff617bc04881984ce73ecd2b55b0c99afb5cb28f32d24d89052 +OpenBLAS.v0.3.23+0.i686-w64-mingw32-libgfortran5.tar.gz/md5/32c1ca252dcae7d02bcd54d2b00a4409 +OpenBLAS.v0.3.23+0.i686-w64-mingw32-libgfortran5.tar.gz/sha512/401126557d3072d965327aa1440eeaf22fdfb1e5265c28dca779d81b94ababd1d487603d55e384f2bac305125c9ed3826f0bb7be99af20b0d18a674a8069ce5b +OpenBLAS.v0.3.23+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/3059083c8293106486a0f28a3564e499 +OpenBLAS.v0.3.23+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/019bb4bc71d7be14f040b36d1b44f653ee89aac680749a6a3b8b72446dffae185dd3d8172ca7ac9aac45cfe564c0fc6cf3221a6f8496b9ba10d04ab44d897b65 +OpenBLAS.v0.3.23+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/648167f83536f32921f1208d09cc8f47 +OpenBLAS.v0.3.23+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/084346b93a99671967433f4ac6548d7b828aa65c402bac7e68aee78bbf75e5cb06b22f42a7d4876fdea3e838162278ee3fcf011fa18530c8d8b0e853a4c6440c +OpenBLAS.v0.3.23+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/9796916fb0acbea2e93747dafa96d496 +OpenBLAS.v0.3.23+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/7c3643c3669fea262907bb5c0f27b492adfec910716498a0bd992d705a544b21023d77801f27c967c07be9d5b30bbd936137c8f59f61632fb16cc0e1f2efebd1 +OpenBLAS.v0.3.23+0.x86_64-apple-darwin-libgfortran3.tar.gz/md5/cbf9ad429547ebd1a473f735b6c65442 +OpenBLAS.v0.3.23+0.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/5e98ec17ee35624bf0a286a2dbe01f5ae4fa879274af70b218080c537a325a92fe76331b746e98b3ce3a0d127df2c03f522f554cb43c169a2b7b1890a9a8a81f +OpenBLAS.v0.3.23+0.x86_64-apple-darwin-libgfortran4.tar.gz/md5/28792164b6c34bc627966e338221ff34 +OpenBLAS.v0.3.23+0.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/433dcec661ff2459740c4d1e72d766549135f6f41a7ffb488502d76751fcb00c3d75aaa0e3db182441ef6b5e3b487a9df3e1b8b979da3681496f4ac6c6ce819b +OpenBLAS.v0.3.23+0.x86_64-apple-darwin-libgfortran5.tar.gz/md5/7013b806bfcd2c65582df5f224bd7d86 +OpenBLAS.v0.3.23+0.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/1078cf5583d158af5d38690acf913db378195b79b4743d977e7654c246fecb0ded4ebee96d89f54c5ec5f04af1b9858bcc0700251ccce1bf7c87926ede069b91 +OpenBLAS.v0.3.23+0.x86_64-linux-gnu-libgfortran3.tar.gz/md5/f959117d5c3fd001412c790bd478f7f6 +OpenBLAS.v0.3.23+0.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/e6fbe9fe1b7a92e10760d2b945bcc2c1c5e8399d729fbbb771764e7b72856707629123bc2d2fed2549f551776f8f0a737b0f414ffddc820a655172d933c10af9 +OpenBLAS.v0.3.23+0.x86_64-linux-gnu-libgfortran4.tar.gz/md5/af04d6bd91df5c9bcc63fe06c88a4b79 +OpenBLAS.v0.3.23+0.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/0cd4972d0a44505f9d8d3958bd20e491c986f55f5f84000ab534020dc8d39d788402355fa51bbd521c8c1bf6884d9d35c1db156bd106a98fbde80c104e8dd5a1 +OpenBLAS.v0.3.23+0.x86_64-linux-gnu-libgfortran5.tar.gz/md5/c5e6138630c5b616df1d045e1c388710 +OpenBLAS.v0.3.23+0.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/a54db7cb7e28dd792bd2c4f33945e7d99db1ee9a620bbe77a21cd7fa7f4cddc5c7744d27116951582f00223df09e7dc2258754032cebd57f61a723762743d3fb +OpenBLAS.v0.3.23+0.x86_64-linux-musl-libgfortran3.tar.gz/md5/7d407633f4f59c305896f9132c098cd2 +OpenBLAS.v0.3.23+0.x86_64-linux-musl-libgfortran3.tar.gz/sha512/8a04d46b6dc2eef87d6c4ac43bcdacf5da2b1669bb829c42f07f7f73bc0dba35a6e48f303d1e9cb951062fa2c3a4cce894406c5551c2bac7f57f02d2f92122a3 +OpenBLAS.v0.3.23+0.x86_64-linux-musl-libgfortran4.tar.gz/md5/1d6c7e0b6f3eeedb41ecfea9881d0bac +OpenBLAS.v0.3.23+0.x86_64-linux-musl-libgfortran4.tar.gz/sha512/9152b7f584ecc3f06caf0eaf0a496d9e9c16afe41a4750a9bcce0477cd3cabcdcec5c97c24fa3fba03d603148c8a3dcf7199c171abe10121aaee2f8a68b93c91 +OpenBLAS.v0.3.23+0.x86_64-linux-musl-libgfortran5.tar.gz/md5/fdd5c9e5f746403f7ba4789d8d8c47e1 +OpenBLAS.v0.3.23+0.x86_64-linux-musl-libgfortran5.tar.gz/sha512/2bd980e1e2021b32f3455fb3fdbae407fb672074ca798664c77e063ea6a7503b625eac7655c8cf25307afbfd9abaa64af52fbb3ed811ff8eb6515e3edcf26b1d +OpenBLAS.v0.3.23+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/8c69d9b7b6fbd0896f839c8979c35a81 +OpenBLAS.v0.3.23+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/d8859f485fa35b33be167dd45f1fe87696be0b12f27dd041087cfbb9df0da94bb726fb9c5f89162405de473969013e3a6a11b0520236db7f5603b25466ebf0d9 +OpenBLAS.v0.3.23+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/40724e1d694288f930a15860650f37bd +OpenBLAS.v0.3.23+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/b7bd75b57803da93d19016f5fe63bd88357aa4e728fdde026a55ab2382957f5a82254b12e701ffb19085a6d1ecc0c0b0c685efb6fa9654e7537f146087cce00a +OpenBLAS.v0.3.23+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/d78352f4e9baf1225aa135b03da9315b +OpenBLAS.v0.3.23+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/aa4d3b0972429af7376e80eab93375ea0368f2f3a31cdbacdb782ff32f7b1c708c5e2d7f1c30ba5b8a7c604a3a7c27a7601fc7f09c8dad2b6dbc54ff099fc0e2 +OpenBLAS.v0.3.23+0.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/dbf8b0592102b01de80df0767f681227 +OpenBLAS.v0.3.23+0.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/9bdf9ab9c3ff36281fa501771c4ed932e8a481ffc4cef08725b4877999bd320c99f9c756beba7143050705323bdc0bea150ab3a11e47f3f7c60f206595c37b73 +OpenBLAS.v0.3.23+0.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/feba9f9647e82992ba310650e3b8ff71 +OpenBLAS.v0.3.23+0.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/b6c98a5a57764eef4940d81461f9706f905d376d165abdbd0fafbdd5802e34523ad15e6ee75a4550555b7c969630c43438d6cce3d6e37ac95e57b58bcc9d542c +OpenBLAS.v0.3.23+0.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/732544eb61201b6dd7c27d5be376d50d +OpenBLAS.v0.3.23+0.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/7b68cceb0bdb892ae74e2744f2a9139602a03e01d937188ca9c875d606d79f555594a5ff022b64d955613b6eb0026a26003011dc17382f019882d9c4c612e8e2 diff --git a/deps/openblas.mk b/deps/openblas.mk index e7e53ad139657..e2837bc47232a 100644 --- a/deps/openblas.mk +++ b/deps/openblas.mk @@ -100,12 +100,7 @@ $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/neoverse-generic-kernels.patch-applied: $(BUILDD patch -p1 -f < $(SRCDIR)/patches/neoverse-generic-kernels.patch echo 1 > $@ -$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-power-test.patch-applied: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/neoverse-generic-kernels.patch-applied - cd $(BUILDDIR)/$(OPENBLAS_SRC_DIR) && \ - patch -p1 -f < $(SRCDIR)/patches/openblas-power-test.patch - echo 1 > $@ - -$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-configured: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-power-test.patch-applied +$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-configured: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/neoverse-generic-kernels.patch-applied echo 1 > $@ $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-compiled: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-configured diff --git a/deps/openblas.version b/deps/openblas.version index 9e433d2629071..be0506fcd5137 100644 --- a/deps/openblas.version +++ b/deps/openblas.version @@ -3,9 +3,9 @@ OPENBLAS_JLL_NAME := OpenBLAS ## source build -OPENBLAS_VER := 0.3.21 -OPENBLAS_BRANCH=v0.3.21 -OPENBLAS_SHA1=b89fb708caa5a5a32de8f4306c4ff132e0228e9a +OPENBLAS_VER := 0.3.23 +OPENBLAS_BRANCH=v0.3.23 +OPENBLAS_SHA1=394a9fbafe9010b76a2615c562204277a956eb52 # LAPACK, source-only LAPACK_VER := 3.9.0 diff --git a/deps/patches/openblas-power-test.patch b/deps/patches/openblas-power-test.patch deleted file mode 100644 index aaf5c0ba32639..0000000000000 --- a/deps/patches/openblas-power-test.patch +++ /dev/null @@ -1,55 +0,0 @@ -From d9dc015cfc78fc32f555995a89d6957ef0184ea2 Mon Sep 17 00:00:00 2001 -From: Martin Kroeker -Date: Mon, 8 Aug 2022 14:52:10 +0200 -Subject: [PATCH 1/2] Use blasint for INTERFACE64 compatibility - ---- - test/compare_sgemm_sbgemm.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/test/compare_sgemm_sbgemm.c b/test/compare_sgemm_sbgemm.c -index a2c358cfa7..d4b5914506 100644 ---- a/test/compare_sgemm_sbgemm.c -+++ b/test/compare_sgemm_sbgemm.c -@@ -76,9 +76,9 @@ float16to32 (bfloat16_bits f16) - int - main (int argc, char *argv[]) - { -- int m, n, k; -+ blasint m, n, k; - int i, j, l; -- int x; -+ blasint x; - int ret = 0; - int loop = 100; - char transA = 'N', transB = 'N'; - -From 3d338b57de1837f1e2264a1262a9ee9203f31c6c Mon Sep 17 00:00:00 2001 -From: Martin Kroeker -Date: Mon, 8 Aug 2022 17:09:45 +0200 -Subject: [PATCH 2/2] remove spurious loops - ---- - test/compare_sgemm_sbgemm.c | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/test/compare_sgemm_sbgemm.c b/test/compare_sgemm_sbgemm.c -index d4b5914506..276fecae9d 100644 ---- a/test/compare_sgemm_sbgemm.c -+++ b/test/compare_sgemm_sbgemm.c -@@ -112,7 +112,6 @@ main (int argc, char *argv[]) - &m, BB, &k, &beta, CC, &m); - for (i = 0; i < n; i++) - for (j = 0; j < m; j++) -- for (l = 0; l < k; l++) - if (fabs (CC[i * m + j] - C[i * m + j]) > 1.0) - ret++; - if (transA == 'N' && transB == 'N') -@@ -126,7 +125,6 @@ main (int argc, char *argv[]) - } - for (i = 0; i < n; i++) - for (j = 0; j < m; j++) -- for (l = 0; l < k; l++) - if (CC[i * m + j] != DD[i * m + j]) - ret++; - } diff --git a/stdlib/OpenBLAS_jll/Project.toml b/stdlib/OpenBLAS_jll/Project.toml index c117bf553bb73..6d953327003be 100644 --- a/stdlib/OpenBLAS_jll/Project.toml +++ b/stdlib/OpenBLAS_jll/Project.toml @@ -1,6 +1,6 @@ name = "OpenBLAS_jll" uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.21+4" +version = "0.3.23+0" [deps] CompilerSupportLibraries_jll = "e66e0078-7015-5450-92f7-15fbd957f2ae" From 14afdd242d196786dff01a1a8184a09ddf722584 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Fri, 7 Apr 2023 12:40:53 -0700 Subject: [PATCH 463/775] Silence `gfortran: command not found` in build logs (#49243) Looks like these snuck in after the last round we silenced these warnings. --- Make.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Make.inc b/Make.inc index 1285de0dc3590..2cd57751ed96e 100644 --- a/Make.inc +++ b/Make.inc @@ -546,8 +546,8 @@ CC_BASE := $(shell echo $(CC) | cut -d' ' -f1) CC_ARG := $(shell echo $(CC) | cut -s -d' ' -f2-) CXX_BASE := $(shell echo $(CXX) | cut -d' ' -f1) CXX_ARG := $(shell echo $(CXX) | cut -s -d' ' -f2-) -FC_BASE := $(shell echo $(FC) | cut -d' ' -f1) -FC_ARG := $(shell echo $(FC) | cut -s -d' ' -f2-) +FC_BASE := $(shell echo $(FC) 2>/dev/null | cut -d' ' -f1) +FC_ARG := $(shell echo $(FC) 2>/dev/null | cut -s -d' ' -f2-) endif JFFLAGS := -O2 $(fPIC) From 804da71a547d8211008572e0627510059d21f9ce Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Sat, 8 Apr 2023 02:25:13 +0200 Subject: [PATCH 464/775] Recompile sysimage when generate_precompile.jl is changed (#49043) --- sysimage.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysimage.mk b/sysimage.mk index b426a74454b1d..7ed61d471a153 100644 --- a/sysimage.mk +++ b/sysimage.mk @@ -74,7 +74,7 @@ $(build_private_libdir)/sys.ji: $(build_private_libdir)/corecompiler.ji $(JULIAH @mv $@.tmp $@ define sysimg_builder -$$(build_private_libdir)/sys$1-o.a $$(build_private_libdir)/sys$1-bc.a : $$(build_private_libdir)/sys$1-%.a : $$(build_private_libdir)/sys.ji +$$(build_private_libdir)/sys$1-o.a $$(build_private_libdir)/sys$1-bc.a : $$(build_private_libdir)/sys$1-%.a : $$(build_private_libdir)/sys.ji $$(JULIAHOME)/contrib/generate_precompile.jl @$$(call PRINT_JULIA, cd $$(JULIAHOME)/base && \ if ! JULIA_BINDIR=$$(call cygpath_w,$(build_bindir)) WINEPATH="$$(call cygpath_w,$$(build_bindir));$$$$WINEPATH" \ JULIA_NUM_THREADS=1 \ From 197710e89aa268a6a0b76bb72c494123a899ed25 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 8 Apr 2023 04:21:27 -0400 Subject: [PATCH 465/775] Fix debug assert when using module as tparam (#49292) This was an oversight in the implementation of #47749 and caused spurious asserts in debug mode: ``` julia> struct Foo{A, B}; end julia> Foo{Base} julia-debug: /home/keno/julia/src/builtins.c:350: type_object_id_: Assertion `!tv->name->mutabl' failed. ``` --- src/builtins.c | 2 ++ test/core.jl | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/builtins.c b/src/builtins.c index 471b06e559dc5..00fa848c7c7fc 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -347,6 +347,8 @@ static uintptr_t type_object_id_(jl_value_t *v, jl_varidx_t *env) JL_NOTSAFEPOIN } if (tv == jl_symbol_type) return ((jl_sym_t*)v)->hash; + if (tv == jl_module_type) + return ((jl_module_t*)v)->hash; assert(!tv->name->mutabl); return immut_id_(tv, v, tv->hash); } diff --git a/test/core.jl b/test/core.jl index 7b989cfd0759d..06c59dfe8cb15 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7977,3 +7977,7 @@ end f48950(::Union{Int,d}, ::Union{c,Nothing}...) where {c,d} = 1 @test f48950(1, 1, 1) == 1 + +# Module as tparam in unionall +struct ModTParamUnionAll{A, B}; end +@test isa(objectid(ModTParamUnionAll{Base}), UInt) From 1cf5091b474f46a4fc1f2d648db9be168e610399 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 8 Apr 2023 13:27:34 -0400 Subject: [PATCH 466/775] simply Union inside UnionAll inside Union during construction (#49279) Allows more opporutunities to discover (and eliminate) repeated components, as well as helping to separate the UnionAll variables into separate domains for subtyping's separability analysis. --- src/jltypes.c | 100 +++++++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/src/jltypes.c b/src/jltypes.c index fbcae4be5a542..759e90d941bed 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -341,21 +341,6 @@ JL_DLLEXPORT int jl_get_size(jl_value_t *val, size_t *pnt) // --- type union --- -static int count_union_components(jl_value_t **types, size_t n) -{ - size_t i, c = 0; - for (i = 0; i < n; i++) { - jl_value_t *e = types[i]; - while (jl_is_uniontype(e)) { - jl_uniontype_t *u = (jl_uniontype_t*)e; - c += count_union_components(&u->a, 1); - e = u->b; - } - c++; - } - return c; -} - int jl_count_union_components(jl_value_t *v) { size_t c = 0; @@ -404,21 +389,6 @@ int jl_find_union_component(jl_value_t *haystack, jl_value_t *needle, unsigned * return 0; } -static void flatten_type_union(jl_value_t **types, size_t n, jl_value_t **out, size_t *idx) JL_NOTSAFEPOINT -{ - size_t i; - for (i = 0; i < n; i++) { - jl_value_t *e = types[i]; - while (jl_is_uniontype(e)) { - jl_uniontype_t *u = (jl_uniontype_t*)e; - flatten_type_union(&u->a, 1, out, idx); - e = u->b; - } - out[*idx] = e; - (*idx)++; - } -} - STATIC_INLINE const char *datatype_module_name(jl_value_t *t) JL_NOTSAFEPOINT { if (((jl_datatype_t*)t)->name->module == NULL) @@ -515,6 +485,53 @@ static int union_sort_cmp(jl_value_t *a, jl_value_t *b) JL_NOTSAFEPOINT } } +static int count_union_components(jl_value_t **types, size_t n) +{ + size_t i, c = 0; + for (i = 0; i < n; i++) { + jl_value_t *e = types[i]; + while (jl_is_uniontype(e)) { + jl_uniontype_t *u = (jl_uniontype_t*)e; + c += count_union_components(&u->a, 1); + e = u->b; + } + if (jl_is_unionall(e) && jl_is_uniontype(jl_unwrap_unionall(e))) { + jl_uniontype_t *u = (jl_uniontype_t*)jl_unwrap_unionall(e); + c += count_union_components(&u->a, 2); + } + else { + c++; + } + } + return c; +} + +static void flatten_type_union(jl_value_t **types, size_t n, jl_value_t **out, size_t *idx) +{ + size_t i; + for (i = 0; i < n; i++) { + jl_value_t *e = types[i]; + while (jl_is_uniontype(e)) { + jl_uniontype_t *u = (jl_uniontype_t*)e; + flatten_type_union(&u->a, 1, out, idx); + e = u->b; + } + if (jl_is_unionall(e) && jl_is_uniontype(jl_unwrap_unionall(e))) { + // flatten this UnionAll into place by switching the union and unionall + jl_uniontype_t *u = (jl_uniontype_t*)jl_unwrap_unionall(e); + size_t old_idx = 0; + flatten_type_union(&u->a, 2, out, idx); + for (; old_idx < *idx; old_idx++) + out[old_idx] = jl_rewrap_unionall(out[old_idx], e); + } + else { + out[*idx] = e; + (*idx)++; + } + } +} + + static void isort_union(jl_value_t **a, size_t len) JL_NOTSAFEPOINT { size_t i, j; @@ -601,28 +618,27 @@ jl_value_t *simple_union(jl_value_t *a, jl_value_t *b) temp[j] == (jl_value_t*)jl_any_type || jl_egal(temp[i], temp[j]) || (!has_free && !jl_has_free_typevars(temp[j]) && - // issue #24521: don't merge Type{T} where typeof(T) varies - !(jl_is_type_type(temp[i]) && jl_is_type_type(temp[j]) && jl_typeof(jl_tparam0(temp[i])) != jl_typeof(jl_tparam0(temp[j]))) && - jl_subtype(temp[i], temp[j]))) { + // issue #24521: don't merge Type{T} where typeof(T) varies + !(jl_is_type_type(temp[i]) && jl_is_type_type(temp[j]) && jl_typeof(jl_tparam0(temp[i])) != jl_typeof(jl_tparam0(temp[j]))) && + jl_subtype(temp[i], temp[j]))) { temp[i] = NULL; } } } } isort_union(temp, nt); - jl_value_t **ptu = &temp[nt]; - *ptu = jl_bottom_type; - int k; - for (k = (int)nt-1; k >= 0; --k) { + temp[nt] = jl_bottom_type; + size_t k; + for (k = nt; k-- > 0; ) { if (temp[k] != NULL) { - if (*ptu == jl_bottom_type) - *ptu = temp[k]; + if (temp[nt] == jl_bottom_type) + temp[nt] = temp[k]; else - *ptu = jl_new_struct(jl_uniontype_type, temp[k], *ptu); + temp[nt] = jl_new_struct(jl_uniontype_type, temp[k], temp[nt]); } } - assert(*ptu != NULL); - jl_value_t *tu = *ptu; + assert(temp[nt] != NULL); + jl_value_t *tu = temp[nt]; JL_GC_POP(); return tu; } From 2ef3361066ae4a07c70f1a61482e0820ebfcbbf3 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 23 Mar 2023 10:23:06 -0400 Subject: [PATCH 467/775] syntax: minor cleanup of convert-for-type-decl code [NFC] --- src/julia-syntax.scm | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 99d51f2ec3432..5356faa97ae8d 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -818,12 +818,13 @@ (field-convert (lambda (fld fty val) (if (equal? fty '(core Any)) val - `(call (top convert) - ,(if (and (not selftype?) (equal? type-params params) (memq fty params) (memq fty sparams)) - fty ; the field type is a simple parameter, the usage here is of a - ; local variable (currently just handles sparam) for the bijection of params to type-params - `(call (core fieldtype) ,tn ,(+ fld 1))) - ,val))))) + (convert-for-type-decl val + ; for ty, usually use the fieldtype, not the fty expression + (if (and (not selftype?) (equal? type-params params) (memq fty params) (memq fty sparams)) + fty ; the field type is a simple parameter, the usage here is of a + ; local variable (currently just handles sparam) for the bijection of params to type-params + `(call (core fieldtype) ,tn ,(+ fld 1))) + #f))))) (cond ((> (num-non-varargs args) (length field-names)) `(call (core throw) (call (top ArgumentError) ,(string "new: too many arguments (expected " (length field-names) ")")))) @@ -3498,18 +3499,17 @@ f(x) = yt(x) (cons (car x) (map do-replace (cdr x)))))))))) -(define (convert-for-type-decl rhs t) +(define (convert-for-type-decl rhs t assert) (if (equal? t '(core Any)) rhs - (let* ((temp (if (or (atom? t) (ssavalue? t) (quoted? t)) + (let* ((left (if (or (atom? t) (ssavalue? t) (quoted? t)) #f (make-ssavalue))) - (ty (or temp t)) - (ex `(call (core typeassert) - (call (top convert) ,ty ,rhs) - ,ty))) - (if temp - `(block (= ,temp ,(renumber-assigned-ssavalues t)) ,ex) + (ty (or left t)) + (ex `(call (top convert) ,ty ,rhs)) + (ex (if assert `(call (core typeassert) ,ex ,ty) ex))) + (if left + `(block (= ,left ,(renumber-assigned-ssavalues t)) ,ex) ex)))) (define (capt-var-access var fname opaq) @@ -3525,7 +3525,7 @@ f(x) = yt(x) (ref (binding-to-globalref var)) (ty `(call (core get_binding_type) ,(cadr ref) (inert ,(caddr ref)))) (rhs (if (get globals ref #t) ;; no type declaration for constants - (convert-for-type-decl rhs1 ty) + (convert-for-type-decl rhs1 ty #t) rhs1)) (ex `(= ,var ,rhs))) (if (eq? rhs1 rhs0) @@ -3557,9 +3557,7 @@ f(x) = yt(x) (equal? rhs0 '(the_exception))) rhs0 (make-ssavalue))) - (rhs (if (equal? vt '(core Any)) - rhs1 - (convert-for-type-decl rhs1 (cl-convert vt fname lam #f #f #f interp opaq)))) + (rhs (convert-for-type-decl rhs1 (cl-convert vt fname lam #f #f #f interp opaq) #t)) (ex (cond (closed `(call (core setfield!) ,(if interp `($ ,var) @@ -4315,7 +4313,7 @@ f(x) = yt(x) x))) (define (actually-return x) (let* ((x (if rett - (compile (convert-for-type-decl (emit- x) rett) '() #t #f) + (compile (convert-for-type-decl (emit- x) rett #t) '() #t #f) x)) (x (emit- x))) (let ((pexc (pop-exc-expr catch-token-stack '()))) From 339c2c595091aaf74f5c05db821177dd933757a5 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 23 Mar 2023 10:40:25 -0400 Subject: [PATCH 468/775] remove redundant type-assert for global assignment Makes a better error message too. Saves about 0.1% (0.15MB) on the standard system image. --- src/julia-syntax.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 5356faa97ae8d..bd2b62f6fac9c 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -3525,7 +3525,7 @@ f(x) = yt(x) (ref (binding-to-globalref var)) (ty `(call (core get_binding_type) ,(cadr ref) (inert ,(caddr ref)))) (rhs (if (get globals ref #t) ;; no type declaration for constants - (convert-for-type-decl rhs1 ty #t) + (convert-for-type-decl rhs1 ty #f) rhs1)) (ex `(= ,var ,rhs))) (if (eq? rhs1 rhs0) From 139f07b778c498dfc922efc3a7ad474b9ee86793 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 27 Mar 2023 17:46:29 -0400 Subject: [PATCH 469/775] avoid calling no-op convert in lowering when already isa This necessitated removing the unreachable DCE pass, since it would skip emitting SSA assignments, and then attempt to emit a label and then use the value, which was not legal. That necessitated making sure the IR was valid even when it was dead (a couple places tried to emit literal #f into the IR, instead of (null) or skipping it). --- src/julia-syntax.scm | 63 +++++++++++++++++++++++------------------ test/compiler/inline.jl | 2 +- test/core.jl | 28 ++++++++++-------- 3 files changed, 52 insertions(+), 41 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index bd2b62f6fac9c..e6a1dee4e8d10 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -824,6 +824,7 @@ fty ; the field type is a simple parameter, the usage here is of a ; local variable (currently just handles sparam) for the bijection of params to type-params `(call (core fieldtype) ,tn ,(+ fld 1))) + #f #f))))) (cond ((> (num-non-varargs args) (length field-names)) `(call (core throw) (call (top ArgumentError) @@ -3499,25 +3500,34 @@ f(x) = yt(x) (cons (car x) (map do-replace (cdr x)))))))))) -(define (convert-for-type-decl rhs t assert) +(define (convert-for-type-decl rhs t assert lam) (if (equal? t '(core Any)) rhs - (let* ((left (if (or (atom? t) (ssavalue? t) (quoted? t)) + (let* ((new-mutable-var + (lambda () (let ((g (gensy))) + (if lam (set-car! (lam:vinfo lam) (append (car (lam:vinfo lam)) `((,g Any 10))))) + g))) + (left (if (or (atom? t) (ssavalue? t) (quoted? t)) #f (make-ssavalue))) + (temp (new-mutable-var)) ; use a slot to permit union-splitting this in inference (ty (or left t)) - (ex `(call (top convert) ,ty ,rhs)) - (ex (if assert `(call (core typeassert) ,ex ,ty) ex))) - (if left - `(block (= ,left ,(renumber-assigned-ssavalues t)) ,ex) - ex)))) + (ex `(call (top convert) ,ty ,temp)) + (ex (if assert `(call (core typeassert) ,ex ,ty) ex)) + (ex `(= ,temp ,ex)) + (ex `(if (call (core isa) ,temp ,ty) (null) ,ex)) + (t (if left (renumber-assigned-ssavalues t) t)) + (ex `((= ,temp ,rhs) ,ex ,temp)) + (ex (if left (cons `(= ,left ,t) ex) ex)) + (ex (if lam ex (cons `(local-def ,temp) ex)))) + (cons 'block ex)))) (define (capt-var-access var fname opaq) (if opaq `(call (core getfield) ,fname ,(get opaq var)) `(call (core getfield) ,fname (inert ,var)))) -(define (convert-global-assignment var rhs0 globals) +(define (convert-global-assignment var rhs0 globals lam) (let* ((rhs1 (if (or (simple-atom? rhs0) (equal? rhs0 '(the_exception))) rhs0 @@ -3525,7 +3535,7 @@ f(x) = yt(x) (ref (binding-to-globalref var)) (ty `(call (core get_binding_type) ,(cadr ref) (inert ,(caddr ref)))) (rhs (if (get globals ref #t) ;; no type declaration for constants - (convert-for-type-decl rhs1 ty #f) + (convert-for-type-decl rhs1 ty #f lam) rhs1)) (ex `(= ,var ,rhs))) (if (eq? rhs1 rhs0) @@ -3552,12 +3562,12 @@ f(x) = yt(x) (if (and (not closed) (not capt) (equal? vt '(core Any))) (if (or (local-in? var lam) (underscore-symbol? var)) `(= ,var ,rhs0) - (convert-global-assignment var rhs0 globals)) + (convert-global-assignment var rhs0 globals lam)) (let* ((rhs1 (if (or (simple-atom? rhs0) (equal? rhs0 '(the_exception))) rhs0 (make-ssavalue))) - (rhs (convert-for-type-decl rhs1 (cl-convert vt fname lam #f #f #f interp opaq) #t)) + (rhs (convert-for-type-decl rhs1 (cl-convert vt fname lam #f #f #f interp opaq) #t lam)) (ex (cond (closed `(call (core setfield!) ,(if interp `($ ,var) @@ -3572,7 +3582,7 @@ f(x) = yt(x) ,ex ,rhs1)))))) ((or (outerref? var) (globalref? var)) - (convert-global-assignment var rhs0 globals)) + (convert-global-assignment var rhs0 globals lam)) ((ssavalue? var) `(= ,var ,rhs0)) (else @@ -4258,6 +4268,7 @@ f(x) = yt(x) (handler-level 0) ;; exception handler nesting depth (catch-token-stack '())) ;; tokens identifying handler enter for current catch blocks (define (emit c) + (or c (raise "missing value in IR")) (set! code (cons c code)) c) (define (make-label) @@ -4313,7 +4324,7 @@ f(x) = yt(x) x))) (define (actually-return x) (let* ((x (if rett - (compile (convert-for-type-decl (emit- x) rett #t) '() #t #f) + (compile (convert-for-type-decl (emit- x) rett #t lam) '() #t #f) x)) (x (emit- x))) (let ((pexc (pop-exc-expr catch-token-stack '()))) @@ -4432,7 +4443,8 @@ f(x) = yt(x) (emit `(= ,lhs ,rhs)) (let ((rr (make-ssavalue))) (emit `(= ,rr ,rhs)) - (emit `(= ,lhs ,rr))))) + (emit `(= ,lhs ,rr)))) + (emit `(= ,lhs (null)))) ; in unreachable code (such as after return), still emit the assignment so that the structure of those uses is preserved #f) ;; the interpreter loop. `break-labels` keeps track of the labels to jump to ;; for all currently closing break-blocks. @@ -4551,7 +4563,7 @@ f(x) = yt(x) (emit '(meta pop_loc)) (emit `(return ,retv))) (emit '(meta pop_loc)))) - ((and value (not (simple-atom? v))) + ((and v value (not (simple-atom? v))) (let ((tmp (make-ssavalue))) (emit `(= ,tmp ,v)) (set! v tmp) @@ -4680,7 +4692,7 @@ f(x) = yt(x) (begin (mark-label els) (let ((v3 (compile (cadddr e) break-labels value tail))) ;; emit else block code (if val (emit-assignment val v3))) - (emit `(goto ,endl)))) + (if endl (emit `(goto ,endl))))) ;; emit either catch or finally block (mark-label catch) (emit `(leave 1)) @@ -4936,22 +4948,20 @@ f(x) = yt(x) (linetable '(list)) (labltable (table)) (ssavtable (table)) - (reachable #t) (current-loc 0) (current-file file) (current-line line) (locstack '()) (i 1)) (define (emit e) + (or e (raise "missing value in IR")) (if (and (null? (cdr linetable)) (not (and (pair? e) (eq? (car e) 'meta)))) (begin (set! linetable (cons (make-lineinfo name file line) linetable)) (set! current-loc 1))) - (if (or reachable - (and (pair? e) (memq (car e) '(meta inbounds gc_preserve_begin gc_preserve_end aliasscope popaliasscope inline noinline)))) - (begin (set! code (cons e code)) - (set! i (+ i 1)) - (set! locs (cons current-loc locs))))) + (set! code (cons e code)) + (set! i (+ i 1)) + (set! locs (cons current-loc locs))) (let loop ((stmts (cdr body))) (if (pair? stmts) (let ((e (car stmts))) @@ -4983,7 +4993,6 @@ f(x) = yt(x) (set! current-line (cadr l)) (set! current-file (caddr l)))) ((eq? (car e) 'label) - (set! reachable #t) (put! labltable (cadr e) i)) ((and (assignment? e) (ssavalue? (cadr e))) (let ((idx (and (ssavalue? (caddr e)) (get ssavtable (cadr (caddr e)) #f)))) @@ -4994,9 +5003,7 @@ f(x) = yt(x) (put! ssavtable (cadr (cadr e)) i) (emit (caddr e)))))) (else - (emit e) - (if (or (eq? (car e) 'goto) (eq? (car e) 'return)) - (set! reachable #f)))) + (emit e))) (loop (cdr stmts))))) (vector (reverse code) (reverse locs) (reverse linetable) ssavtable labltable))) @@ -5030,8 +5037,8 @@ f(x) = yt(x) ((or (atom? e) (quoted? e) (eq? (car e) 'global)) e) ((ssavalue? e) - (let ((idx (or (get ssavalue-table (cadr e) #f) - (error "ssavalue with no def")))) + (let ((idx (get ssavalue-table (cadr e) #f))) + (if (not idx) (begin (prn e) (prn lam) (error "ssavalue with no def"))) `(ssavalue ,idx))) ((memq (car e) '(goto enter)) (list* (car e) (get label-table (cadr e)) (cddr e))) diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 4f85527e04cb4..e32cc73c4f3c3 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -1108,7 +1108,7 @@ function f44200() x44200 end let src = code_typed1(f44200) - @test count(x -> isa(x, Core.PiNode), src.code) == 0 + @test_broken count(x -> isa(x, Core.PiNode), src.code) == 0 end # Test that peeling off one case from (::Any) doesn't introduce diff --git a/test/core.jl b/test/core.jl index 06c59dfe8cb15..a89d206182dbf 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7327,11 +7327,11 @@ struct sparse_t31649 end Base.convert(::Any, v::sparse_t31649) = copy(v.val) let spvec = sparse_t31649(zeros(Float64,5), Vector{Int64}()) - @test_throws MethodError repr(spvec) + @test_throws MethodError convert(Any, spvec) # Try manually putting the problematic method into the cache (in # the original issue compiling the showerror method caused this to happen) @test convert(Any, nothing) === nothing - @test_throws MethodError repr(spvec) + @test_throws MethodError convert(Any, spvec) end # Issue #31062 - Accidental recursion in jl_has_concrete_subtype @@ -7371,16 +7371,20 @@ end let code = code_lowered(FieldConvert)[1].code @test code[1] == Expr(:call, GlobalRef(Core, :apply_type), GlobalRef(@__MODULE__, :FieldConvert), GlobalRef(@__MODULE__, :FieldTypeA), Expr(:static_parameter, 1)) @test code[2] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(1), 1) - @test code[3] == Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(2), Core.SlotNumber(2)) - @test code[4] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(1), 2) - @test code[5] == Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(4), Core.SlotNumber(3)) - @test code[6] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(1), 4) - @test code[7] == Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(6), Core.SlotNumber(5)) - @test code[8] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(1), 5) - @test code[9] == Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(8), Core.SlotNumber(6)) - @test code[10] == Expr(:new, Core.SSAValue(1), Core.SSAValue(3), Core.SSAValue(5), Core.SlotNumber(4), Core.SSAValue(7), Core.SSAValue(9)) - @test code[11] == Core.ReturnNode(Core.SSAValue(10)) - end + @test code[7] == Expr(:(=), Core.SlotNumber(10), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(2), Core.SlotNumber(10))) + @test code[8] == Core.SlotNumber(10) + @test code[9] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(1), 2) + @test code[14] == Expr(:(=), Core.SlotNumber(9), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(9), Core.SlotNumber(9))) + @test code[15] == Core.SlotNumber(9) + @test code[16] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(1), 4) + @test code[21] == Expr(:(=), Core.SlotNumber(8), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(16), Core.SlotNumber(8))) + @test code[22] == Core.SlotNumber(8) + @test code[23] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(1), 5) + @test code[28] == Expr(:(=), Core.SlotNumber(7), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(23), Core.SlotNumber(7))) + @test code[29] == Core.SlotNumber(7) + @test code[30] == Expr(:new, Core.SSAValue(1), Core.SSAValue(8), Core.SSAValue(15), Core.SlotNumber(4), Core.SSAValue(22), Core.SSAValue(29)) + @test code[31] == Core.ReturnNode(Core.SSAValue(30)) +end # Issue #32820 function f32820(refs) From b33a7635915f838c7e038a815a0de7a7749da616 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 23 Mar 2023 13:08:21 -0400 Subject: [PATCH 470/775] avoid calling no-op convert most of the time While these are free at runtime, they incur a small compile time cost to create an enumerate the backedge to `convert(::Type{T}, ::T) where T` which is shadowed by pretty much every `convert` method ever added later, requiring careful re-checking for invalidations. This drops the number of specializations of that method from over 1000 to under 100. With the other commits, seems to save over 1 MB (1%) from the standard system image. --- base/Base.jl | 40 ++++++++++++++++++++++++++++++---------- base/array.jl | 18 +++++++++--------- base/dict.jl | 40 +++++++++++++++++++++++++++++----------- base/essentials.jl | 14 ++++++++++---- base/iddict.jl | 4 ++-- base/logging.jl | 3 ++- base/namedtuple.jl | 7 +++++-- base/pair.jl | 6 +++++- base/reducedim.jl | 4 ++-- base/tuple.jl | 6 ++++-- 10 files changed, 98 insertions(+), 44 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 5dd029e1660da..730239dea0c7d 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -35,14 +35,19 @@ getproperty(x::Tuple, f::Int) = (@inline; getfield(x, f)) setproperty!(x::Tuple, f::Int, v) = setfield!(x, f, v) # to get a decent error getproperty(x, f::Symbol) = (@inline; getfield(x, f)) -setproperty!(x, f::Symbol, v) = setfield!(x, f, convert(fieldtype(typeof(x), f), v)) +function setproperty!(x, f::Symbol, v) + ty = fieldtype(typeof(x), f) + val = v isa ty ? v : convert(ty, v) + return setfield!(x, f, val) +end dotgetproperty(x, f) = getproperty(x, f) getproperty(x::Module, f::Symbol, order::Symbol) = (@inline; getglobal(x, f, order)) function setproperty!(x::Module, f::Symbol, v, order::Symbol=:monotonic) @inline - val::Core.get_binding_type(x, f) = v + ty = Core.get_binding_type(x, f) + val = v isa ty ? v : convert(ty, v) return setglobal!(x, f, val, order) end getproperty(x::Type, f::Symbol, order::Symbol) = (@inline; getfield(x, f, order)) @@ -51,14 +56,29 @@ getproperty(x::Tuple, f::Int, order::Symbol) = (@inline; getfield(x, f, order)) setproperty!(x::Tuple, f::Int, v, order::Symbol) = setfield!(x, f, v, order) # to get a decent error getproperty(x, f::Symbol, order::Symbol) = (@inline; getfield(x, f, order)) -setproperty!(x, f::Symbol, v, order::Symbol) = (@inline; setfield!(x, f, convert(fieldtype(typeof(x), f), v), order)) +function setproperty!(x, f::Symbol, v, order::Symbol) + @inline + ty = fieldtype(typeof(x), f) + val = v isa ty ? v : convert(ty, v) + return setfield!(x, f, val, order) +end -swapproperty!(x, f::Symbol, v, order::Symbol=:not_atomic) = - (@inline; Core.swapfield!(x, f, convert(fieldtype(typeof(x), f), v), order)) -modifyproperty!(x, f::Symbol, op, v, order::Symbol=:not_atomic) = - (@inline; Core.modifyfield!(x, f, op, v, order)) -replaceproperty!(x, f::Symbol, expected, desired, success_order::Symbol=:not_atomic, fail_order::Symbol=success_order) = - (@inline; Core.replacefield!(x, f, expected, convert(fieldtype(typeof(x), f), desired), success_order, fail_order)) +function swapproperty!(x, f::Symbol, v, order::Symbol=:not_atomic) + @inline + ty = fieldtype(typeof(x), f) + val = v isa ty ? v : convert(ty, v) + return Core.swapfield!(x, f, val, order) +end +function modifyproperty!(x, f::Symbol, op, v, order::Symbol=:not_atomic) + @inline + return Core.modifyfield!(x, f, op, v, order) +end +function replaceproperty!(x, f::Symbol, expected, desired, success_order::Symbol=:not_atomic, fail_order::Symbol=success_order) + @inline + ty = fieldtype(typeof(x), f) + val = desired isa ty ? desired : convert(ty, desired) + return Core.replacefield!(x, f, expected, val, success_order, fail_order) +end convert(::Type{Any}, Core.@nospecialize x) = x convert(::Type{T}, x::T) where {T} = x @@ -149,7 +169,7 @@ include("refpointer.jl") delete_method(which(Pair{Any,Any}, (Any, Any))) @eval function (P::Type{Pair{A, B}})(@nospecialize(a), @nospecialize(b)) where {A, B} @inline - return $(Expr(:new, :P, :(convert(A, a)), :(convert(B, b)))) + return $(Expr(:new, :P, :(a isa A ? a : convert(A, a)), :(b isa B ? b : convert(B, b)))) end # The REPL stdlib hooks into Base using this Ref diff --git a/base/array.jl b/base/array.jl index 1cfa55b52c999..752ca4cc641dd 100644 --- a/base/array.jl +++ b/base/array.jl @@ -187,7 +187,7 @@ function vect(X...) return T[X...] end -size(a::Array, d::Integer) = arraysize(a, convert(Int, d)) +size(a::Array, d::Integer) = arraysize(a, d isa Int ? d : convert(Int, d)) size(a::Vector) = (arraysize(a,1),) size(a::Matrix) = (arraysize(a,1), arraysize(a,2)) size(a::Array{<:Any,N}) where {N} = (@inline; ntuple(M -> size(a, M), Val(N))::Dims) @@ -383,7 +383,7 @@ copyto!(dest::Array{T}, src::Array{T}) where {T} = copyto!(dest, 1, src, 1, leng # N.B: The generic definition in multidimensional.jl covers, this, this is just here # for bootstrapping purposes. function fill!(dest::Array{T}, x) where T - xT = convert(T, x) + xT = x isa T ? x : convert(T, x)::T for i in eachindex(dest) @inbounds dest[i] = xT end @@ -475,7 +475,7 @@ end getindex(::Type{Any}) = Vector{Any}() function fill!(a::Union{Array{UInt8}, Array{Int8}}, x::Integer) - ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), a, convert(eltype(a), x), length(a)) + ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), a, x isa eltype(a) ? x : convert(eltype(a), x), length(a)) return a end @@ -1020,9 +1020,9 @@ Dict{String, Int64} with 2 entries: function setindex! end @eval setindex!(A::Array{T}, x, i1::Int) where {T} = - arrayset($(Expr(:boundscheck)), A, convert(T,x)::T, i1) + arrayset($(Expr(:boundscheck)), A, x isa T ? x : convert(T,x)::T, i1) @eval setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} = - (@inline; arrayset($(Expr(:boundscheck)), A, convert(T,x)::T, i1, i2, I...)) + (@inline; arrayset($(Expr(:boundscheck)), A, x isa T ? x : convert(T,x)::T, i1, i2, I...)) __inbounds_setindex!(A::Array{T}, x, i1::Int) where {T} = arrayset(false, A, convert(T,x)::T, i1) @@ -1116,7 +1116,7 @@ function push! end function push!(a::Vector{T}, item) where T # convert first so we don't grow the array if the assignment won't work - itemT = convert(T, item) + itemT = item isa T ? item : convert(T, item)::T _growend!(a, 1) @_safeindex a[length(a)] = itemT return a @@ -1466,7 +1466,7 @@ julia> pushfirst!([1, 2, 3, 4], 5, 6) ``` """ function pushfirst!(a::Vector{T}, item) where T - item = convert(T, item) + item = item isa T ? item : convert(T, item)::T _growbeg!(a, 1) @_safeindex a[1] = item return a @@ -1553,7 +1553,7 @@ julia> insert!(Any[1:6;], 3, "here") """ function insert!(a::Array{T,1}, i::Integer, item) where T # Throw convert error before changing the shape of the array - _item = convert(T, item) + _item = item isa T ? item : convert(T, item)::T _growat!(a, i, 1) # _growat! already did bound check @inbounds a[i] = _item @@ -2194,7 +2194,7 @@ findfirst(p::Union{Fix2{typeof(isequal),T},Fix2{typeof(==),T}}, r::AbstractUnitR function findfirst(p::Union{Fix2{typeof(isequal),T},Fix2{typeof(==),T}}, r::StepRange{T,S}) where {T,S} isempty(r) && return nothing minimum(r) <= p.x <= maximum(r) || return nothing - d = convert(S, p.x - first(r)) + d = convert(S, p.x - first(r))::S iszero(d % step(r)) || return nothing return d ÷ step(r) + 1 end diff --git a/base/dict.jl b/base/dict.jl index 66329e9184646..359016bd3c2a8 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -359,15 +359,19 @@ ht_keyindex2!(h::Dict, key) = ht_keyindex2_shorthash!(h, key)[1] end function setindex!(h::Dict{K,V}, v0, key0) where V where K - key = convert(K, key0) - if !(isequal(key, key0)::Bool) - throw(ArgumentError("$(limitrepr(key0)) is not a valid key for type $K")) + if key0 isa K + key = key0 + else + key = convert(K, key0)::K + if !(isequal(key, key0)::Bool) + throw(ArgumentError("$(limitrepr(key0)) is not a valid key for type $K")) + end end setindex!(h, v0, key) end function setindex!(h::Dict{K,V}, v0, key::K) where V where K - v = convert(V, v0) + v = v0 isa V ? v0 : convert(V, v0)::V index, sh = ht_keyindex2_shorthash!(h, key) if index > 0 @@ -453,9 +457,13 @@ Dict{Int64, Int64} with 1 entry: get!(f::Callable, collection, key) function get!(default::Callable, h::Dict{K,V}, key0) where V where K - key = convert(K, key0) - if !isequal(key, key0) - throw(ArgumentError("$(limitrepr(key0)) is not a valid key for type $K")) + if key0 isa K + key = key0 + else + key = convert(K, key0)::K + if !isequal(key, key0) + throw(ArgumentError("$(limitrepr(key0)) is not a valid key for type $K")) + end end return get!(default, h, key) end @@ -466,7 +474,10 @@ function get!(default::Callable, h::Dict{K,V}, key::K) where V where K index > 0 && return h.vals[index] age0 = h.age - v = convert(V, default()) + v = default() + if !isa(v, V) + v = convert(V, v)::V + end if h.age != age0 index, sh = ht_keyindex2_shorthash!(h, key) end @@ -756,10 +767,17 @@ function mergewith!(combine, d1::Dict{K, V}, d2::AbstractDict) where {K, V} if i > 0 d1.vals[i] = combine(d1.vals[i], v) else - if !isequal(k, convert(K, k)) - throw(ArgumentError("$(limitrepr(k)) is not a valid key for type $K")) + if !(k isa K) + k1 = convert(K, k)::K + if !isequal(k, k1) + throw(ArgumentError("$(limitrepr(k)) is not a valid key for type $K")) + end + k = k1 + end + if !isa(v, V) + v = convert(V, v)::V end - @inbounds _setindex!(d1, convert(V, v), k, -i, sh) + @inbounds _setindex!(d1, v, k, -i, sh) end end return d1 diff --git a/base/essentials.jl b/base/essentials.jl index 59e4a3fe1162e..829341c482383 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -342,7 +342,7 @@ end @eval struct Pairs{K, V, I, A} <: AbstractDict{K, V} data::A itr::I - Pairs{K, V, I, A}(data, itr) where {K, V, I, A} = $(Expr(:new, :(Pairs{K, V, I, A}), :(convert(A, data)), :(convert(I, itr)))) + Pairs{K, V, I, A}(data, itr) where {K, V, I, A} = $(Expr(:new, :(Pairs{K, V, I, A}), :(data isa A ? data : convert(A, data)), :(itr isa I ? itr : convert(I, itr)))) Pairs{K, V}(data::A, itr::I) where {K, V, I, A} = $(Expr(:new, :(Pairs{K, V, I, A}), :data, :itr)) Pairs{K}(data::A, itr::I) where {K, I, A} = $(Expr(:new, :(Pairs{K, eltype(A), I, A}), :data, :itr)) Pairs(data::A, itr::I) where {I, A} = $(Expr(:new, :(Pairs{eltype(I), eltype(A), I, A}), :data, :itr)) @@ -459,7 +459,13 @@ function convert(::Type{T}, x::NTuple{N,Any}) where {N, T<:Tuple} if typeintersect(NTuple{N,Any}, T) === Union{} _tuple_error(T, x) end - cvt1(n) = (@inline; convert(fieldtype(T, n), getfield(x, n, #=boundscheck=#false))) + function cvt1(n) + @inline + Tn = fieldtype(T, n) + xn = getfield(x, n, #=boundscheck=#false) + xn isa Tn && return xn + return convert(Tn, xn) + end return ntuple(cvt1, Val(N))::NTuple{N,Any} end @@ -512,7 +518,7 @@ julia> oftype(y, x) 4.0 ``` """ -oftype(x, y) = convert(typeof(x), y) +oftype(x, y) = y isa typeof(x) ? y : convert(typeof(x), y)::typeof(x) unsigned(x::Int) = reinterpret(UInt, x) signed(x::UInt) = reinterpret(Int, x) @@ -533,7 +539,7 @@ Neither `convert` nor `cconvert` should take a Julia object and turn it into a ` """ function cconvert end -cconvert(T::Type, x) = convert(T, x) # do the conversion eagerly in most cases +cconvert(T::Type, x) = x isa T ? x : convert(T, x) # do the conversion eagerly in most cases cconvert(::Type{<:Ptr}, x) = x # but defer the conversion to Ptr to unsafe_convert unsafe_convert(::Type{T}, x::T) where {T} = x # unsafe_convert (like convert) defaults to assuming the convert occurred unsafe_convert(::Type{T}, x::T) where {T<:Ptr} = x # to resolve ambiguity with the next method diff --git a/base/iddict.jl b/base/iddict.jl index dc7af461b09ec..99710fbb3491e 100644 --- a/base/iddict.jl +++ b/base/iddict.jl @@ -86,7 +86,7 @@ end function setindex!(d::IdDict{K,V}, @nospecialize(val), @nospecialize(key)) where {K, V} !isa(key, K) && throw(ArgumentError("$(limitrepr(key)) is not a valid key for type $K")) if !(val isa V) # avoid a dynamic call - val = convert(V, val) + val = convert(V, val)::V end if d.ndel >= ((3*length(d.ht))>>2) rehash!(d, max((length(d.ht)%UInt)>>1, 32)) @@ -155,7 +155,7 @@ copy(d::IdDict) = typeof(d)(d) function get!(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V} val = ccall(:jl_eqtable_get, Any, (Any, Any, Any), d.ht, key, secret_table_token) if val === secret_table_token - val = isa(default, V) ? default : convert(V, default) + val = isa(default, V) ? default : convert(V, default)::V setindex!(d, val, key) return val else diff --git a/base/logging.jl b/base/logging.jl index c2f243bcabf46..c42af08d8f4ae 100644 --- a/base/logging.jl +++ b/base/logging.jl @@ -369,7 +369,8 @@ function logmsg_code(_module, file, line, level, message, exs...) return quote let level = $level - std_level = convert(LogLevel, level) + # simplify std_level code emitted, if we know it is one of our global constants + std_level = $(level isa Symbol ? :level : :(level isa LogLevel ? level : convert(LogLevel, level)::LogLevel)) if std_level >= _min_enabled_level[] group = $(log_data._group) _module = $(log_data._module) diff --git a/base/namedtuple.jl b/base/namedtuple.jl index e2a78c6ea0444..320d068205a3d 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -124,7 +124,10 @@ end function NamedTuple{names, T}(nt::NamedTuple) where {names, T <: Tuple} if @generated Expr(:new, :(NamedTuple{names, T}), - Any[ :(convert(fieldtype(T, $n), getfield(nt, $(QuoteNode(names[n]))))) for n in 1:length(names) ]...) + Any[ :(let Tn = fieldtype(T, $n), + ntn = getfield(nt, $(QuoteNode(names[n]))) + ntn isa Tn ? ntn : convert(Tn, ntn) + end) for n in 1:length(names) ]...) else NamedTuple{names, T}(map(Fix1(getfield, nt), names)) end @@ -195,7 +198,7 @@ end if nameof(@__MODULE__) === :Base Tuple(nt::NamedTuple) = (nt...,) - (::Type{T})(nt::NamedTuple) where {T <: Tuple} = convert(T, Tuple(nt)) + (::Type{T})(nt::NamedTuple) where {T <: Tuple} = (t = Tuple(nt); t isa T ? t : convert(T, t)::T) end function show(io::IO, t::NamedTuple) diff --git a/base/pair.jl b/base/pair.jl index 28a9f981080ec..f34ebb89c80da 100644 --- a/base/pair.jl +++ b/base/pair.jl @@ -60,7 +60,11 @@ last(p::Pair) = p.second convert(::Type{Pair{A,B}}, x::Pair{A,B}) where {A,B} = x function convert(::Type{Pair{A,B}}, x::Pair) where {A,B} - Pair{A,B}(convert(A, x[1]), convert(B, x[2]))::Pair{A,B} + a = getfield(x, :first) + a isa A || (a = convert(A, a)) + b = getfield(x, :second) + b isa B || (b = convert(B, b)) + return Pair{A,B}(a, b)::Pair{A,B} end promote_rule(::Type{Pair{A1,B1}}, ::Type{Pair{A2,B2}}) where {A1,B1,A2,B2} = diff --git a/base/reducedim.jl b/base/reducedim.jl index dc34b4feb1f6a..101568d60002b 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -211,8 +211,8 @@ reducedim_init(f, op::typeof(|), A::AbstractArrayOrBroadcasted, region) = reduce let BitIntFloat = Union{BitInteger, IEEEFloat} T = Union{ - [AbstractArray{t} for t in uniontypes(BitIntFloat)]..., - [AbstractArray{Complex{t}} for t in uniontypes(BitIntFloat)]...} + Any[AbstractArray{t} for t in uniontypes(BitIntFloat)]..., + Any[AbstractArray{Complex{t}} for t in uniontypes(BitIntFloat)]...} global function reducedim_init(f, op::Union{typeof(+),typeof(add_sum)}, A::T, region) z = zero(f(zero(eltype(A)))) diff --git a/base/tuple.jl b/base/tuple.jl index b8ef63517a49f..dcceaabf12e83 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -377,7 +377,7 @@ function tuple_type_tail(T::Type) end end -(::Type{T})(x::Tuple) where {T<:Tuple} = convert(T, x) # still use `convert` for tuples +(::Type{T})(x::Tuple) where {T<:Tuple} = x isa T ? x : convert(T, x) # still use `convert` for tuples Tuple(x::Ref) = tuple(getindex(x)) # faster than iterator for one element Tuple(x::Array{T,0}) where {T} = tuple(getindex(x)) @@ -395,7 +395,9 @@ function _totuple(::Type{T}, itr, s::Vararg{Any,N}) where {T,N} @inline y = iterate(itr, s...) y === nothing && _totuple_err(T) - t1 = convert(fieldtype(T, 1), y[1]) + T1 = fieldtype(T, 1) + y1 = y[1] + t1 = y1 isa T1 ? y1 : convert(T1, y1)::T1 # inference may give up in recursive calls, so annotate here to force accurate return type to be propagated rT = tuple_type_tail(T) ts = _totuple(rT, itr, y[2])::rT From ffca15a0d43591449ba149bb8e1326b6b313a850 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 23 Mar 2023 13:35:20 -0400 Subject: [PATCH 471/775] teach inference about splitting isa on the type Sometime `fieldtype` returns Union{Types}, which we can split during the if, to avoid union splitting on the convert call later when. --- base/compiler/abstractinterpretation.jl | 16 ++++++++++++++-- test/compiler/inference.jl | 10 ++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 5aeba5ca194e9..8c6fa56dba8a7 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1728,22 +1728,34 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs elseif has_conditional(𝕃ᵢ, sv) && (rt === Bool || (isa(rt, Const) && isa(rt.val, Bool))) && isa(fargs, Vector{Any}) # perform very limited back-propagation of type information for `is` and `isa` if f === isa + # try splitting value argument, based on types a = ssa_def_slot(fargs[2], sv) a2 = argtypes[2] + a3 = argtypes[3] if isa(a, SlotNumber) - cndt = isa_condition(a2, argtypes[3], InferenceParams(interp).max_union_splitting, rt) + cndt = isa_condition(a2, a3, InferenceParams(interp).max_union_splitting, rt) if cndt !== nothing return Conditional(a, cndt.thentype, cndt.elsetype) end end if isa(a2, MustAlias) if !isa(rt, Const) # skip refinement when the field is known precisely (just optimization) - cndt = isa_condition(a2, argtypes[3], InferenceParams(interp).max_union_splitting) + cndt = isa_condition(a2, a3, InferenceParams(interp).max_union_splitting) if cndt !== nothing return form_mustalias_conditional(a2, cndt.thentype, cndt.elsetype) end end end + # try splitting type argument, based on value + if isdispatchelem(widenconst(a2)) && a3 isa Union && !has_free_typevars(a3) && !isa(rt, Const) + b = ssa_def_slot(fargs[3], sv) + if isa(b, SlotNumber) + # !(x isa T) implies !(Type{a2} <: T) + # TODO: complete splitting, based on which portions of the Union a3 for which isa_tfunc returns Const(true) or Const(false) instead of Bool + elsetype = typesubtract(a3, Type{widenconst(a2)}, InferenceParams(interp).max_union_splitting) + return Conditional(b, a3, elsetype) + end + end elseif f === (===) a = ssa_def_slot(fargs[2], sv) b = ssa_def_slot(fargs[3], sv) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 1634345f70459..c8aed565e7089 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -4823,3 +4823,13 @@ Base.@assume_effects :foldable _recur_irinterp2(x, y) = @noinline recur_irinterp recur_irinterp2(0, y) end |> only === Tuple{Int,Symbol} @test last(recur_irinterp2(0, :y)) === :y + +# test Conditional Union splitting of info derived from fieldtype (e.g. in abstract setproperty! handling) +@test only(Base.return_types((Int, Pair{Int,Nothing}, Symbol)) do a, x, s + T = fieldtype(typeof(x), s) + if a isa T + throw(a) + else + return T + end +end) == Type{Nothing} From 618a0008caaee8a77f9f0f9d3241a70117bdc46d Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Sun, 9 Apr 2023 00:56:38 -0400 Subject: [PATCH 472/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=20429175914=20to=20fe2b3bdac=20(#49295)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dilum Aluthge --- .../Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/md5 | 1 - .../Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/sha512 | 1 - .../Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/md5 | 1 + .../Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/md5 create mode 100644 deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/sha512 diff --git a/deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/md5 b/deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/md5 deleted file mode 100644 index 80f94f607268b..0000000000000 --- a/deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -dff8afa625321af081b4567102e3f91f diff --git a/deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/sha512 b/deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/sha512 deleted file mode 100644 index 65ca28ff83f4a..0000000000000 --- a/deps/checksums/Pkg-429175914e8d44f6675ae82865b9d140735cb001.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -25e9c430beac4c13e5fa5d1edc2eb09ae5c17f766651b5c8cf22e8c1720a3fda4fc7f73e4d209ca1726f8a64ec6b7cefc303e671949c974d20ae21347184ed79 diff --git a/deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/md5 b/deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/md5 new file mode 100644 index 0000000000000..b698577fac206 --- /dev/null +++ b/deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/md5 @@ -0,0 +1 @@ +9cb0bc8b7fbd53ab6162afa38046a5ba diff --git a/deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/sha512 b/deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/sha512 new file mode 100644 index 0000000000000..9a693b92ba4a5 --- /dev/null +++ b/deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/sha512 @@ -0,0 +1 @@ +b2345815e6ba9593335679bb51ea6bb2d7c0d039abd2d31ea6f3c8aae56aa85ab10861b0fc5d1af1ee124f685726456b903841487524c22543608462d6a068a3 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 2440d23eda0ab..b643a8fada9ed 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 429175914e8d44f6675ae82865b9d140735cb001 +PKG_SHA1 = fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 1f5e0a29e470b11cb02f5263a4952054d45992e5 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Sun, 9 Apr 2023 17:27:53 +0200 Subject: [PATCH 473/775] move out Distributed from the sysimage (#49258) --- base/sysimg.jl | 1 - contrib/generate_precompile.jl | 22 --------------------- pkgimage.mk | 2 +- stdlib/Distributed/src/Distributed.jl | 1 + stdlib/Distributed/src/precompile.jl | 14 +++++++++++++ stdlib/Distributed/test/distributed_exec.jl | 4 ++-- test/precompile.jl | 2 +- 7 files changed, 19 insertions(+), 27 deletions(-) create mode 100644 stdlib/Distributed/src/precompile.jl diff --git a/base/sysimg.jl b/base/sysimg.jl index ca1a4e74f7417..b0eeffa5757ba 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -56,7 +56,6 @@ let # 2-depth packages :Dates, - :Distributed, :Future, :InteractiveUtils, :LibGit2, diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index f28a0fcd3974f..f756e0bfb8fee 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -129,28 +129,6 @@ if have_repl """ end -Distributed = get(Base.loaded_modules, - Base.PkgId(Base.UUID("8ba89e20-285c-5b6f-9357-94700520ee1b"), "Distributed"), - nothing) -if Distributed !== nothing - hardcoded_precompile_statements *= """ - precompile(Tuple{typeof(Distributed.remotecall),Function,Int,Module,Vararg{Any, 100}}) - precompile(Tuple{typeof(Distributed.procs)}) - precompile(Tuple{typeof(Distributed.finalize_ref), Distributed.Future}) - """ -# This is disabled because it doesn't give much benefit -# and the code in Distributed is poorly typed causing many invalidations -#= - precompile_script *= """ - using Distributed - addprocs(2) - pmap(x->iseven(x) ? 1 : 0, 1:4) - @distributed (+) for i = 1:100 Int(rand(Bool)) end - """ -=# -end - - Artifacts = get(Base.loaded_modules, Base.PkgId(Base.UUID("56f22d72-fd6d-98f1-02f0-08ddc0907c33"), "Artifacts"), nothing) diff --git a/pkgimage.mk b/pkgimage.mk index 554bcd5587abe..caf30a91c1d18 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -93,7 +93,7 @@ $(eval $(call pkgimg_builder,LibSSH2_jll,Artifacts Libdl MbedTLS_jll)) $(eval $(call pkgimg_builder,MPFR_jll,Artifacts Libdl GMP_jll)) $(eval $(call sysimg_builder,LinearAlgebra,Libdl libblastrampoline_jll OpenBLAS_jll)) $(eval $(call sysimg_builder,Dates,Printf)) -$(eval $(call sysimg_builder,Distributed,Random Serialization Sockets)) +$(eval $(call pkgimg_builder,Distributed,Random Serialization Sockets)) $(eval $(call sysimg_builder,Future,Random)) $(eval $(call sysimg_builder,InteractiveUtils,Markdown)) $(eval $(call sysimg_builder,LibGit2,NetworkOptions Printf SHA Base64)) diff --git a/stdlib/Distributed/src/Distributed.jl b/stdlib/Distributed/src/Distributed.jl index 65bb6224a7bcd..28933e32e8bb8 100644 --- a/stdlib/Distributed/src/Distributed.jl +++ b/stdlib/Distributed/src/Distributed.jl @@ -107,6 +107,7 @@ include("macros.jl") # @spawn and friends include("workerpool.jl") include("pmap.jl") include("managers.jl") # LocalManager and SSHManager +include("precompile.jl") function __init__() init_parallel() diff --git a/stdlib/Distributed/src/precompile.jl b/stdlib/Distributed/src/precompile.jl new file mode 100644 index 0000000000000..87380f627db7a --- /dev/null +++ b/stdlib/Distributed/src/precompile.jl @@ -0,0 +1,14 @@ +precompile(Tuple{typeof(Distributed.remotecall),Function,Int,Module,Vararg{Any, 100}}) +precompile(Tuple{typeof(Distributed.procs)}) +precompile(Tuple{typeof(Distributed.finalize_ref), Distributed.Future}) +# This is disabled because it doesn't give much benefit +# and the code in Distributed is poorly typed causing many invalidations +# TODO: Maybe reenable now that Distributed is not in sysimage. +#= + precompile_script *= """ + using Distributed + addprocs(2) + pmap(x->iseven(x) ? 1 : 0, 1:4) + @distributed (+) for i = 1:100 Int(rand(Bool)) end + """ +=# diff --git a/stdlib/Distributed/test/distributed_exec.jl b/stdlib/Distributed/test/distributed_exec.jl index 16d1e4b100bf3..44929a762b2c5 100644 --- a/stdlib/Distributed/test/distributed_exec.jl +++ b/stdlib/Distributed/test/distributed_exec.jl @@ -1874,7 +1874,7 @@ let julia = `$(Base.julia_cmd()) --startup-file=no`; mktempdir() do tmp using Distributed project = mktempdir() env = Dict( - "JULIA_LOAD_PATH" => LOAD_PATH[1], + "JULIA_LOAD_PATH" => string(LOAD_PATH[1], $(repr(pathsep)), "@stdlib"), "JULIA_DEPOT_PATH" => DEPOT_PATH[1], "TMPDIR" => ENV["TMPDIR"], ) @@ -1884,7 +1884,7 @@ let julia = `$(Base.julia_cmd()) --startup-file=no`; mktempdir() do tmp """ * setupcode * """ for w in workers() @test remotecall_fetch(depot_path, w) == [DEPOT_PATH[1]] - @test remotecall_fetch(load_path, w) == [LOAD_PATH[1]] + @test remotecall_fetch(load_path, w) == [LOAD_PATH[1], "@stdlib"] @test remotecall_fetch(active_project, w) == project @test remotecall_fetch(Base.active_project, w) == joinpath(project, "Project.toml") end diff --git a/test/precompile.jl b/test/precompile.jl index 48437fb04eca2..37498068fd39c 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -395,7 +395,7 @@ precompile_test_harness(false) do dir Base.PkgId(m) => Base.module_build_id(m) end for s in [:ArgTools, :Artifacts, :Base64, :CompilerSupportLibraries_jll, :CRC32c, :Dates, - :Distributed, :Downloads, :FileWatching, :Future, :InteractiveUtils, :libblastrampoline_jll, + :Downloads, :FileWatching, :Future, :InteractiveUtils, :libblastrampoline_jll, :LibCURL, :LibCURL_jll, :LibGit2, :Libdl, :LinearAlgebra, :Logging, :Markdown, :Mmap, :MozillaCACerts_jll, :NetworkOptions, :OpenBLAS_jll, :Pkg, :Printf, :p7zip_jll, :REPL, :Random, :SHA, :Serialization, :Sockets, From bf379e2a2fefa029defb5468765d6761ca18196b Mon Sep 17 00:00:00 2001 From: KristofferC Date: Wed, 5 Apr 2023 11:19:09 +0200 Subject: [PATCH 474/775] Revert "delay loading of extensions as much as possible (#48674)" This reverts commit e3043a875d432ec4bd7c073874a654b55421438f. --- base/loading.jl | 87 +++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index b82028216663b..c36990fd3a07e 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1080,6 +1080,7 @@ function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String) end function run_package_callbacks(modkey::PkgId) + run_extension_callbacks(modkey) assert_havelock(require_lock) unlock(require_lock) try @@ -1204,55 +1205,51 @@ function run_extension_callbacks(extid::ExtensionId) return succeeded end -function run_extension_callbacks() +function run_extension_callbacks(pkgid::PkgId) assert_havelock(require_lock) - loaded_triggers = collect(intersect(keys(Base.loaded_modules), keys(Base.EXT_DORMITORY))) - sort!(loaded_triggers; by=x->x.uuid) - for pkgid in loaded_triggers - # take ownership of extids that depend on this pkgid - extids = pop!(EXT_DORMITORY, pkgid, nothing) - extids === nothing && continue - for extid in extids - if extid.ntriggers > 0 - # It is possible that pkgid was loaded in an environment - # below the one of the parent. This will cause a load failure when the - # pkg ext tries to load the triggers. Therefore, check this first - # before loading the pkg ext. - pkgenv = identify_package_env(extid.id, pkgid.name) - ext_not_allowed_load = false - if pkgenv === nothing + # take ownership of extids that depend on this pkgid + extids = pop!(EXT_DORMITORY, pkgid, nothing) + extids === nothing && return + for extid in extids + if extid.ntriggers > 0 + # It is possible that pkgid was loaded in an environment + # below the one of the parent. This will cause a load failure when the + # pkg ext tries to load the triggers. Therefore, check this first + # before loading the pkg ext. + pkgenv = identify_package_env(extid.id, pkgid.name) + ext_not_allowed_load = false + if pkgenv === nothing + ext_not_allowed_load = true + else + pkg, env = pkgenv + path = locate_package(pkg, env) + if path === nothing ext_not_allowed_load = true - else - pkg, env = pkgenv - path = Base.locate_package(pkg, env) - if path === nothing - ext_not_allowed_load = true - end - end - if ext_not_allowed_load - @debug "Extension $(extid.id.name) of $(extid.parentid.name) will not be loaded \ - since $(pkgid.name) loaded in environment lower in load path" - # indicate extid is expected to fail - extid.ntriggers *= -1 - else - # indicate pkgid is loaded - extid.ntriggers -= 1 end end - if extid.ntriggers < 0 - # indicate pkgid is loaded - extid.ntriggers += 1 - succeeded = false + if ext_not_allowed_load + @debug "Extension $(extid.id.name) of $(extid.parentid.name) will not be loaded \ + since $(pkgid.name) loaded in environment lower in load path" + # indicate extid is expected to fail + extid.ntriggers *= -1 else - succeeded = true - end - 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) + # indicate pkgid is loaded + extid.ntriggers -= 1 end end + if extid.ntriggers < 0 + # indicate pkgid is loaded + extid.ntriggers += 1 + succeeded = false + else + succeeded = true + end + 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) + end end return end @@ -1276,7 +1273,7 @@ function retry_load_extensions() end prepend!(EXT_DORMITORY_FAILED, failed) end - nothing + return end """ @@ -1669,10 +1666,6 @@ function _require_prelocked(uuidkey::PkgId, env=nothing) else newm = root_module(uuidkey) end - # Load extensions when not precompiling and not in a nested package load - if JLOptions().incremental == 0 && isempty(package_locks) - run_extension_callbacks() - end return newm end From aa16ceac581d5a94e82496cc66c2600f7c31dd80 Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Sun, 9 Apr 2023 17:07:54 +0200 Subject: [PATCH 475/775] add a test that extension is available during precompilation --- .../HasDepWithExtensions.jl/src/HasDepWithExtensions.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/project/Extensions/HasDepWithExtensions.jl/src/HasDepWithExtensions.jl b/test/project/Extensions/HasDepWithExtensions.jl/src/HasDepWithExtensions.jl index d64cbc680e3a5..5c1f2d1f301aa 100644 --- a/test/project/Extensions/HasDepWithExtensions.jl/src/HasDepWithExtensions.jl +++ b/test/project/Extensions/HasDepWithExtensions.jl/src/HasDepWithExtensions.jl @@ -4,10 +4,18 @@ using HasExtensions: HasExtensions, HasExtensionsStruct using ExtDep: ExtDepStruct # Loading ExtDep makes the extension "Extension" load +const m = Base.get_extension(HasExtensions, :Extension) +m isa Module || error("extension not loaded during precompilation") + function do_something() HasExtensions.foo(HasExtensionsStruct()) == 1 || error() HasExtensions.foo(ExtDepStruct()) == 2 || error() return true end +function __init__() + m = Base.get_extension(HasExtensions, :Extension) + m isa Module || error("extension not loaded during __init__") +end + end # module From 4ccaa1b595885a219c51cb2be56a00347928795f Mon Sep 17 00:00:00 2001 From: Tanmay Mohapatra Date: Mon, 10 Apr 2023 01:06:52 +0530 Subject: [PATCH 476/775] fix new scope assignment warning in Distributed (#49303) Disambiguate scope of the `wp` variable inside `try`-`catch` in distributed tests by qualifying it and suppress the warning reported in the tests. fixes #49289 --- stdlib/Distributed/test/distributed_exec.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Distributed/test/distributed_exec.jl b/stdlib/Distributed/test/distributed_exec.jl index 44929a762b2c5..43e02c92b5a81 100644 --- a/stdlib/Distributed/test/distributed_exec.jl +++ b/stdlib/Distributed/test/distributed_exec.jl @@ -704,7 +704,7 @@ clear!(wp) # default_worker_pool! tests wp_default = Distributed.default_worker_pool() try - wp = CachingPool(workers()) + local wp = CachingPool(workers()) Distributed.default_worker_pool!(wp) @test [1:100...] == pmap(x->x, wp, 1:100) @test !isempty(wp.map_obj2ref) From 84906d93fe49ab478280f303435cc18289bf99b3 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sun, 9 Apr 2023 20:56:26 -0400 Subject: [PATCH 477/775] Move interactive check in code load Pkg.precompile (#49304) --- base/loading.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/loading.jl b/base/loading.jl index c36990fd3a07e..65dc91abf6bea 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1799,7 +1799,7 @@ function _require(pkg::PkgId, env=nothing) if JLOptions().use_compiled_modules != 0 if (0 == ccall(:jl_generating_output, Cint, ())) || (JLOptions().incremental != 0) - if !pkg_precompile_attempted && isassigned(PKG_PRECOMPILE_HOOK) + if !pkg_precompile_attempted && isinteractive() && isassigned(PKG_PRECOMPILE_HOOK) pkg_precompile_attempted = true unlock(require_lock) try From 7ddb4e5f39898e4771c9e5ddb4b00c2a11d9db6f Mon Sep 17 00:00:00 2001 From: Jean-Francois Baffier Date: Mon, 10 Apr 2023 16:59:05 +0900 Subject: [PATCH 478/775] Update parallel.md (#49308) Removes the duplicate header `Synchronization` --- doc/src/base/parallel.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/src/base/parallel.md b/doc/src/base/parallel.md index ee84f4b8b445d..c9f24429fd0e5 100644 --- a/doc/src/base/parallel.md +++ b/doc/src/base/parallel.md @@ -26,8 +26,6 @@ Base.schedule ## [Synchronization](@id lib-task-sync) -## Synchronization - ```@docs Base.errormonitor Base.@sync From cc10e8c2c931c96b29d9f305f0e51146d13638fa Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Mon, 10 Apr 2023 07:07:02 -0400 Subject: [PATCH 479/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=20fe2b3bdac=20to=20992a8c27b=20(#49307)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/md5 | 1 + .../Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/sha512 | 1 + .../Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/md5 | 1 - .../Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/md5 create mode 100644 deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/sha512 diff --git a/deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/md5 b/deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/md5 new file mode 100644 index 0000000000000..fec075ed8775c --- /dev/null +++ b/deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/md5 @@ -0,0 +1 @@ +a972d55766241fbd4b3e789286c87a5a diff --git a/deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/sha512 b/deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/sha512 new file mode 100644 index 0000000000000..ff82d32f4001b --- /dev/null +++ b/deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/sha512 @@ -0,0 +1 @@ +357a3494d5015e4541e7657f74d480efda6b503d5fa49a250f8aafda08c466bca292c630e52eb18a9d694464a00124176bdf3ae2e84db475f6b497bbda0dad3c diff --git a/deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/md5 b/deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/md5 deleted file mode 100644 index b698577fac206..0000000000000 --- a/deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -9cb0bc8b7fbd53ab6162afa38046a5ba diff --git a/deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/sha512 b/deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/sha512 deleted file mode 100644 index 9a693b92ba4a5..0000000000000 --- a/deps/checksums/Pkg-fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -b2345815e6ba9593335679bb51ea6bb2d7c0d039abd2d31ea6f3c8aae56aa85ab10861b0fc5d1af1ee124f685726456b903841487524c22543608462d6a068a3 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index b643a8fada9ed..3b4e7a877932b 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = fe2b3bdacd0fc35c201bcf7118521bc75b0c4a49 +PKG_SHA1 = 992a8c27b967f45f04de07bd84a2763644cb8a33 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 94da49226052ac98106cb1788e16b403020b210d Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 10 Apr 2023 08:08:18 -0400 Subject: [PATCH 480/775] apply_type_tfunc: add heuristic complexity limit (#49167) In #48421 we removed all limits from apply_type, but that can lead to significant expression complexity which may be hard for subtyping to deal with. Try adding a heuristic size limiting constraint. Also do a bunch of code rearrangement and slight cleanup so that this change does not make the logic look complicated here. Solves #49127 --- base/compiler/tfuncs.jl | 54 +++++++++++++++++++++++++++----------- base/compiler/typeutils.jl | 9 +++++++ test/compiler/inference.jl | 6 +++++ 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 4dfb29aca602b..48f712576d32f 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1779,31 +1779,55 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K, uncertain = true unw = unwrap_unionall(ai) isT = isType(unw) + # compute our desired upper bound value if isT - tai = ai - while isa(tai, UnionAll) - if contains_is(outervars, tai.var) - ai = rename_unionall(ai) - unw = unwrap_unionall(ai) - break + ub = rewrap_unionall(unw.parameters[1], ai) + else + ub = Any + end + if !istuple && unionall_depth(ai) > 3 + # Heuristic: if we are adding more than N unknown parameters here to the + # outer type, use the wrapper type, instead of letting it nest more + # complexity here. This is not monotonic, but seems to work out pretty well. + if isT + ub = unwrap_unionall(unw.parameters[1]) + if ub isa DataType + ub = ub.name.wrapper + unw = Type{unwrap_unionall(ub)} + ai = rewrap_unionall(unw, ub) + else + isT = false + ai = unw = ub = Any end - tai = tai.body + else + isT = false + ai = unw = ub = Any end + elseif !isT + # if we didn't have isType to compute ub directly, try to use instanceof_tfunc to refine this guess + ai_w = widenconst(ai) + ub = ai_w isa Type && ai_w <: Type ? instanceof_tfunc(ai)[1] : Any end - ai_w = widenconst(ai) - ub = ai_w isa Type && ai_w <: Type ? instanceof_tfunc(ai)[1] : Any if istuple # in the last parameter of a Tuple type, if the upper bound is Any # then this could be a Vararg type. if i == largs && ub === Any - push!(tparams, Vararg) - elseif isT - push!(tparams, rewrap_unionall((unw::DataType).parameters[1], ai)) - else - push!(tparams, Any) + ub = Vararg end + push!(tparams, ub) elseif isT - push!(tparams, (unw::DataType).parameters[1]) + tai = ai + while isa(tai, UnionAll) + # make sure vars introduced here are unique + if contains_is(outervars, tai.var) + ai = rename_unionall(ai) + unw = unwrap_unionall(ai)::DataType + # ub = rewrap_unionall(unw, ai) + break + end + tai = tai.body + end + push!(tparams, unw.parameters[1]) while isa(ai, UnionAll) push!(outervars, ai.var) ai = ai.body diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index 5caf0d5de80fd..3b4975a6cb848 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -308,6 +308,15 @@ function _unioncomplexity(@nospecialize x) end end +function unionall_depth(@nospecialize ua) # aka subtype_env_size + depth = 0 + while ua isa UnionAll + depth += 1 + ua = ua.body + end + return depth +end + # convert a Union of Tuple types to a Tuple of Unions function unswitchtupleunion(u::Union) ts = uniontypes(u) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 1634345f70459..b03d6acf45457 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2658,10 +2658,16 @@ let 𝕃 = Core.Compiler.fallback_lattice @test apply_type_tfunc(𝕃, Const(Issue47089), Const(String)) === Union{} @test apply_type_tfunc(𝕃, Const(Issue47089), Const(AbstractString)) === Union{} @test apply_type_tfunc(𝕃, Const(Issue47089), Type{Ptr}, Type{Ptr{T}} where T) === Base.rewrap_unionall(Type{Issue47089.body.body}, Issue47089) + # check complexity size limiting + @test apply_type_tfunc(𝕃, Const(Val), Type{Pair{Pair{Pair{Pair{A,B},C},D},E}} where {A,B,C,D,E}) == Type{Val{Pair{A, B}}} where {A, B} + @test apply_type_tfunc(𝕃, Const(Pair), Base.rewrap_unionall(Type{Pair.body.body},Pair), Type{Pair{Pair{Pair{Pair{A,B},C},D},E}} where {A,B,C,D,E}) == Type{Pair{Pair{A, B}, Pair{C, D}}} where {A, B, C, D} + @test apply_type_tfunc(𝕃, Const(Val), Type{Union{Int,Pair{Pair{Pair{Pair{A,B},C},D},E}}} where {A,B,C,D,E}) == Type{Val{_A}} where _A end @test only(Base.return_types(keys, (Dict{String},))) == Base.KeySet{String, T} where T<:(Dict{String}) @test only(Base.return_types((r)->similar(Array{typeof(r[])}, 1), (Base.RefValue{Array{Int}},))) == Vector{<:Array{Int}} @test only(Base.return_types((r)->similar(Array{typeof(r[])}, 1), (Base.RefValue{Array{<:Real}},))) == Vector{<:Array{<:Real}} +# test complexity limit on apply_type on a function capturing functions returning functions +@test only(Base.return_types(Base.afoldl, (typeof((m, n) -> () -> Returns(nothing)(m, n)), Function, Function, Vararg{Function}))) === Function let A = Tuple{A,B,C,D,E,F,G,H} where {A,B,C,D,E,F,G,H} B = Core.Compiler.rename_unionall(A) From d0f4327066911d04b56ae5ad2df72a74365d4be8 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Mon, 10 Apr 2023 09:57:25 -0400 Subject: [PATCH 481/775] REPLCompletions: Add completions for var"" identifiers (#49294) * REPLCompletions: Add completions for var"" identifiers Fixes #49280. Mostly just moving code around, but there's one extra place where we're pattern matching var"". I do hope that after the future parser replacement, we can do these things on the in-progress AST rather than textually. * Apply suggestions from code review --------- Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> --- stdlib/REPL/src/REPLCompletions.jl | 126 +++++++++++++++++----------- stdlib/REPL/test/replcompletions.jl | 29 +++++++ 2 files changed, 108 insertions(+), 47 deletions(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 03332e8905d3d..c5d8832e4d7d0 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -119,7 +119,8 @@ function completes_global(x, name) end function appendmacro!(syms, macros, needle, endchar) - for s in macros + for macsym in macros + s = String(macsym) if endswith(s, needle) from = nextind(s, firstindex(s)) to = prevind(s, sizeof(s)-sizeof(needle)+1) @@ -131,28 +132,21 @@ end function filtered_mod_names(ffunc::Function, mod::Module, name::AbstractString, all::Bool = false, imported::Bool = false) ssyms = names(mod, all = all, imported = imported) filter!(ffunc, ssyms) - syms = String[string(s) for s in ssyms] - macros = filter(x -> startswith(x, "@" * name), syms) + macros = filter(x -> startswith(String(x), "@" * name), ssyms) + 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", "`") - filter!(x->completes_global(x, name), syms) return [ModuleCompletion(mod, sym) for sym in syms] end # REPL Symbol Completions -function complete_symbol(sym::String, @nospecialize(ffunc), context_module::Module=Main) +function complete_symbol(@nospecialize(ex), name::String, @nospecialize(ffunc), context_module::Module=Main) mod = context_module - name = sym lookup_module = true t = Union{} val = nothing - if something(findlast(in(non_identifier_chars), sym), 0) < something(findlast(isequal('.'), sym), 0) - # Find module - lookup_name, name = rsplit(sym, ".", limit=2) - - ex = Meta.parse(lookup_name, raise=false, depwarn=false) - + if ex !== nothing res = repl_eval_ex(ex, context_module) res === nothing && return Completion[] if res isa Const @@ -898,7 +892,7 @@ function complete_keyword_argument(partial, last_idx, context_module) end suggestions = Completion[KeywordArgumentCompletion(kwarg) for kwarg in kwargs] - append!(suggestions, complete_symbol(last_word, Returns(true), context_module)) + append!(suggestions, complete_symbol(nothing, last_word, Returns(true), context_module)) return sort!(suggestions, by=completion_text), wordrange end @@ -919,6 +913,55 @@ function project_deps_get_completion_candidates(pkgstarts::String, project_file: return Completion[PackageCompletion(name) for name in loading_candidates] end +function complete_identifiers!(suggestions::Vector{Completion}, @nospecialize(ffunc::Function), context_module::Module, string::String, name::String, pos::Int, dotpos::Int, startpos::Int, comp_keywords=false) + ex = nothing + comp_keywords && append!(suggestions, complete_keyword(name)) + if dotpos > 1 && string[dotpos] == '.' + s = string[1:dotpos-1] + # First see if the whole string up to `pos` is a valid expression. If so, use it. + ex = Meta.parse(s, raise=false, depwarn=false) + if isexpr(ex, :incomplete) + s = string[startpos:pos] + # Heuristic to find the start of the expression. TODO: This would be better + # done with a proper error-recovering parser. + if 0 < startpos <= lastindex(string) && string[startpos] == '.' + i = prevind(string, startpos) + while 0 < i + c = string[i] + if c in (')', ']') + if c == ')' + c_start = '(' + c_end = ')' + elseif c == ']' + c_start = '[' + c_end = ']' + end + frange, end_of_identifier = find_start_brace(string[1:prevind(string, i)], c_start=c_start, c_end=c_end) + isempty(frange) && break # unbalanced parens + startpos = first(frange) + i = prevind(string, startpos) + elseif c in ('\'', '\"', '\`') + s = "$c$c"*string[startpos:pos] + break + else + break + end + s = string[startpos:pos] + end + end + if something(findlast(in(non_identifier_chars), s), 0) < something(findlast(isequal('.'), s), 0) + lookup_name, name = rsplit(s, ".", limit=2) + name = String(name) + + ex = Meta.parse(lookup_name, raise=false, depwarn=false) + end + isexpr(ex, :incomplete) && (ex = nothing) + end + end + append!(suggestions, complete_symbol(ex, name, ffunc, context_module)) + return sort!(unique(suggestions), by=completion_text), (dotpos+1):pos, true +end + function completions(string::String, pos::Int, context_module::Module=Main, shift::Bool=true) # First parse everything up to the current position partial = string[1:pos] @@ -962,8 +1005,25 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif length(matches)>0 && return Completion[DictCompletion(identifier, match) for match in sort!(matches)], loc::Int:pos, true end + ffunc = Returns(true) + suggestions = Completion[] + + # Check if this is a var"" string macro that should be completed like + # an identifier rather than a string. + # TODO: It would be nice for the parser to give us more information here + # so that we can lookup the macro by identity rather than pattern matching + # its invocation. + varrange = findprev("var\"", string, pos) + + if varrange !== nothing + ok, ret = bslash_completions(string, pos) + ok && return ret + startpos = first(varrange) + 4 + dotpos = something(findprev(isequal('.'), string, startpos), 0) + return complete_identifiers!(Completion[], ffunc, context_module, string, + string[startpos:pos], pos, dotpos, startpos) # otherwise... - if inc_tag in [:cmd, :string] + elseif inc_tag in [:cmd, :string] m = match(r"[\t\n\r\"`><=*?|]| (?!\\)", reverse(partial)) startpos = nextind(partial, reverseind(partial, m.offset)) r = startpos:pos @@ -1010,9 +1070,8 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif startpos += length(m.match) end - ffunc = Returns(true) - suggestions = Completion[] - comp_keywords = true + name = string[max(startpos, dotpos+1):pos] + comp_keywords = !isempty(name) && startpos > dotpos if afterusing(string, startpos) # We're right after using or import. Let's look only for packages # and modules we can reach from here @@ -1054,38 +1113,11 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif ffunc = (mod,x)->(Base.isbindingresolved(mod, x) && isdefined(mod, x) && isa(getfield(mod, x), Module)) comp_keywords = false end + startpos == 0 && (pos = -1) dotpos < startpos && (dotpos = startpos - 1) - s = string[startpos:pos] - comp_keywords && append!(suggestions, complete_keyword(s)) - # if the start of the string is a `.`, try to consume more input to get back to the beginning of the last expression - if 0 < startpos <= lastindex(string) && string[startpos] == '.' - i = prevind(string, startpos) - while 0 < i - c = string[i] - if c in (')', ']') - if c == ')' - c_start = '(' - c_end = ')' - elseif c == ']' - c_start = '[' - c_end = ']' - end - frange, end_of_identifier = find_start_brace(string[1:prevind(string, i)], c_start=c_start, c_end=c_end) - isempty(frange) && break # unbalanced parens - startpos = first(frange) - i = prevind(string, startpos) - elseif c in ('\'', '\"', '\`') - s = "$c$c"*string[startpos:pos] - break - else - break - end - s = string[startpos:pos] - end - end - append!(suggestions, complete_symbol(s, ffunc, context_module)) - return sort!(unique(suggestions), by=completion_text), (dotpos+1):pos, true + return complete_identifiers!(suggestions, ffunc, context_module, string, + name, pos, dotpos, startpos, comp_keywords) end function shell_completions(string, pos) diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 2ce2471b21c4d..b53999bc79f3d 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -132,6 +132,11 @@ let ex = quote macro testcmd_cmd(s) end macro tϵsτcmδ_cmd(s) end + var"complicated symbol with spaces" = 5 + + struct WeirdNames end + Base.propertynames(::WeirdNames) = (Symbol("oh no!"), Symbol("oh yes!")) + end # module CompletionFoo test_repl_comp_dict = CompletionFoo.test_dict test_repl_comp_customdict = CompletionFoo.test_customdict @@ -1801,3 +1806,27 @@ let s = "pop!(global_xs)." @test "value" in c end @test length(global_xs) == 1 # the completion above shouldn't evaluate `pop!` call + +# Test completion of var"" identifiers (#49280) +let s = "var\"complicated " + c, r = test_complete_foo(s) + @test c == Any["var\"complicated symbol with spaces\""] +end + +let s = "WeirdNames().var\"oh " + c, r = test_complete_foo(s) + @test c == Any["var\"oh no!\"", "var\"oh yes!\""] +end + +# Test completion of non-Expr literals +let s = "\"abc\"." + c, r = test_complete(s) + # (no completion, but shouldn't error) + @test isempty(c) +end + +let s = "`abc`.e" + c, r = test_complete(s) + # (completions for the fields of `Cmd`) + @test c == Any["env", "exec"] +end From ea72b9427926640d970b390cb32b9b5f2770838f Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Mon, 10 Apr 2023 14:15:36 +0000 Subject: [PATCH 482/775] Remove alloca from codegen (#49186) Replace with SmallVector for better readability. --- src/ccall.cpp | 12 +++++----- src/cgutils.cpp | 2 +- src/codegen.cpp | 59 +++++++++++++++++++++++----------------------- src/intrinsics.cpp | 58 ++++++++++++++++++++++----------------------- 4 files changed, 66 insertions(+), 65 deletions(-) diff --git a/src/ccall.cpp b/src/ccall.cpp index d6023d429420d..e490f4146cad2 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -839,7 +839,7 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar * type. Otherwise we pass a pointer to a jl_value_t. */ std::vector argtypes; - Value **argvals = (Value**)alloca(nargt * sizeof(Value*)); + SmallVector argvals(nargt); for (size_t i = 0; i < nargt; ++i) { jl_value_t *tti = jl_svecref(tt,i); bool toboxed; @@ -986,7 +986,7 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar Function *decl = Function::Create(decl_typ, def->getLinkage(), def->getAddressSpace(), def->getName(), jl_Module); decl->setAttributes(def->getAttributes()); - CallInst *inst = ctx.builder.CreateCall(decl, ArrayRef(&argvals[0], nargt)); + CallInst *inst = ctx.builder.CreateCall(decl, argvals); // save the module to be linked later. // we cannot do this right now, because linking mutates the destination module, @@ -1399,7 +1399,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) #define is_libjulia_func(name) _is_libjulia_func((uintptr_t)&(name), StringRef(XSTR(name))) // emit arguments - jl_cgval_t *argv = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nccallargs); + SmallVector argv(nccallargs); for (size_t i = 0; i < nccallargs; i++) { // Julia (expression) value of current parameter jl_value_t *argi = ccallarg(i); @@ -1897,7 +1897,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) jl_cgval_t retval = sig.emit_a_ccall( ctx, symarg, - argv, + argv.data(), gc_uses, static_rt); JL_GC_POP(); @@ -1919,7 +1919,7 @@ jl_cgval_t function_sig_t::emit_a_ccall( FunctionType *functype = this->functype(ctx.builder.getContext()); - Value **argvals = (Value**) alloca((nccallargs + sret) * sizeof(Value*)); + SmallVector argvals(nccallargs + sret); for (size_t ai = 0; ai < nccallargs; ai++) { // Current C function parameter jl_cgval_t &arg = argv[ai]; @@ -2099,7 +2099,7 @@ jl_cgval_t function_sig_t::emit_a_ccall( OperandBundleDef OpBundle("jl_roots", gc_uses); // the actual call CallInst *ret = ctx.builder.CreateCall(functype, llvmf, - ArrayRef(&argvals[0], nccallargs + sret), + argvals, ArrayRef(&OpBundle, gc_uses.empty() ? 0 : 1)); ((CallInst*)ret)->setAttributes(attributes); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 792f66c27bb45..a6d41b9e41214 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -2859,7 +2859,7 @@ static Value *emit_array_nd_index( endBB = BasicBlock::Create(ctx.builder.getContext(), "idxend"); } #endif - Value **idxs = (Value**)alloca(sizeof(Value*) * nidxs); + SmallVector idxs(nidxs); for (size_t k = 0; k < nidxs; k++) { idxs[k] = emit_unbox(ctx, ctx.types().T_size, argv[k], (jl_value_t*)jl_long_type); // type asserted by caller } diff --git a/src/codegen.cpp b/src/codegen.cpp index 68fafba7a5a06..49f5472d4132e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2491,7 +2491,7 @@ static void cg_bdw(jl_codectx_t &ctx, jl_sym_t *var, jl_binding_t *b) static jl_value_t *static_apply_type(jl_codectx_t &ctx, const jl_cgval_t *args, size_t nargs) { assert(nargs > 1); - jl_value_t **v = (jl_value_t**)alloca(sizeof(jl_value_t*) * nargs); + SmallVector v(nargs); for (size_t i = 0; i < nargs; i++) { if (!args[i].constant) return NULL; @@ -2503,7 +2503,7 @@ static jl_value_t *static_apply_type(jl_codectx_t &ctx, const jl_cgval_t *args, jl_current_task->world_age = 1; jl_value_t *result; JL_TRY { - result = jl_apply(v, nargs); + result = jl_apply(v.data(), nargs); } JL_CATCH { result = NULL; @@ -4101,9 +4101,9 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_ *return_roots = returninfo.return_roots; size_t nfargs = cft->getNumParams(); - Value **argvals = (Value**)alloca(nfargs * sizeof(Value*)); + SmallVector argvals(nfargs); unsigned idx = 0; - AllocaInst *result; + AllocaInst *result = nullptr; switch (returninfo.cc) { case jl_returninfo_t::Boxed: case jl_returninfo_t::Register: @@ -4180,7 +4180,7 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_ jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); callee = ai.decorateInst(ctx.builder.CreateAlignedLoad(callee->getType(), GV, Align(sizeof(void*)))); } - CallInst *call = ctx.builder.CreateCall(cft, callee, ArrayRef(&argvals[0], nfargs)); + CallInst *call = ctx.builder.CreateCall(cft, callee, argvals); call->setAttributes(returninfo.decl->getAttributes()); jl_cgval_t retval; @@ -4192,6 +4192,7 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_ retval = mark_julia_type(ctx, call, false, jlretty); break; case jl_returninfo_t::SRet: + assert(result); retval = mark_julia_slot(result, jlretty, NULL, ctx.tbaa().tbaa_stack); break; case jl_returninfo_t::Union: { @@ -4254,13 +4255,13 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt) assert(arglen >= 2); jl_cgval_t lival = emit_expr(ctx, args[0]); - jl_cgval_t *argv = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nargs); + SmallVector argv(nargs); for (size_t i = 0; i < nargs; ++i) { argv[i] = emit_expr(ctx, args[i + 1]); if (argv[i].typ == jl_bottom_type) return jl_cgval_t(); } - return emit_invoke(ctx, lival, argv, nargs, rt); + return emit_invoke(ctx, lival, argv.data(), nargs, rt); } static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, const jl_cgval_t *argv, size_t nargs, jl_value_t *rt) @@ -4375,7 +4376,7 @@ static jl_cgval_t emit_invoke_modify(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_ size_t nargs = arglen - 1; assert(arglen >= 2); jl_cgval_t lival = emit_expr(ctx, args[0]); - jl_cgval_t *argv = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nargs); + SmallVector argv(nargs); for (size_t i = 0; i < nargs; ++i) { argv[i] = emit_expr(ctx, args[i + 1]); if (argv[i].typ == jl_bottom_type) @@ -4384,7 +4385,7 @@ static jl_cgval_t emit_invoke_modify(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_ const jl_cgval_t &f = argv[0]; jl_cgval_t ret; if (f.constant && f.constant == jl_builtin_modifyfield) { - if (emit_f_opfield(ctx, &ret, jl_builtin_modifyfield, argv, nargs - 1, &lival)) + if (emit_f_opfield(ctx, &ret, jl_builtin_modifyfield, argv.data(), nargs - 1, &lival)) return ret; auto it = builtin_func_map().find(jl_f_modifyfield_addr); assert(it != builtin_func_map().end()); @@ -4394,11 +4395,11 @@ static jl_cgval_t emit_invoke_modify(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_ if (f.constant && jl_typeis(f.constant, jl_intrinsic_type)) { JL_I::intrinsic fi = (intrinsic)*(uint32_t*)jl_data_ptr(f.constant); if (fi == JL_I::atomic_pointermodify && jl_intrinsic_nargs((int)fi) == nargs - 1) - return emit_atomic_pointerop(ctx, fi, argv, nargs - 1, &lival); + return emit_atomic_pointerop(ctx, fi, argv.data(), nargs - 1, &lival); } // emit function and arguments - Value *callval = emit_jlcall(ctx, jlapplygeneric_func, nullptr, argv, nargs, julia_call); + Value *callval = emit_jlcall(ctx, jlapplygeneric_func, nullptr, argv.data(), nargs, julia_call); return mark_julia_type(ctx, callval, true, rt); } @@ -4418,8 +4419,8 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bo jl_value_t *context = ctx.params->generic_context == jl_nothing ? nullptr : ctx.params->generic_context; size_t n_generic_args = nargs + (context ? 1 : 0); - jl_cgval_t *generic_argv = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * n_generic_args); - jl_cgval_t *argv = generic_argv; + SmallVector generic_argv(n_generic_args); + jl_cgval_t *argv = generic_argv.data(); if (context) { generic_argv[0] = mark_julia_const(ctx, context); argv = &generic_argv[1]; @@ -4449,7 +4450,7 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bo } // emit function and arguments - Value *callval = emit_jlcall(ctx, jlapplygeneric_func, nullptr, generic_argv, n_generic_args, julia_call); + Value *callval = emit_jlcall(ctx, jlapplygeneric_func, nullptr, generic_argv.data(), n_generic_args, julia_call); return mark_julia_type(ctx, callval, true, rt); } @@ -5420,7 +5421,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ is_promotable = ctx.ssavalue_usecount.at(ssaidx_0based) == 1; } assert(nargs > 0); - jl_cgval_t *argv = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nargs); + SmallVector argv(nargs); for (size_t i = 0; i < nargs; ++i) { argv[i] = emit_expr(ctx, args[i]); } @@ -5429,12 +5430,12 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ jl_is_datatype(jl_tparam0(ty)) && jl_is_concrete_type(jl_tparam0(ty))) { assert(nargs <= jl_datatype_nfields(jl_tparam0(ty)) + 1); - jl_cgval_t res = emit_new_struct(ctx, jl_tparam0(ty), nargs - 1, &argv[1], is_promotable); + jl_cgval_t res = emit_new_struct(ctx, jl_tparam0(ty), nargs - 1, argv.data() + 1, is_promotable); if (is_promotable && res.promotion_point && res.promotion_ssa==-1) res.promotion_ssa = ssaidx_0based; return res; } - Value *val = emit_jlcall(ctx, jlnew_func, nullptr, argv, nargs, julia_call); + Value *val = emit_jlcall(ctx, jlnew_func, nullptr, argv.data(), nargs, julia_call); // temporarily mark as `Any`, expecting `emit_ssaval_assign` to update // it to the inferred type. return mark_julia_type(ctx, val, true, (jl_value_t*)jl_any_type); @@ -5478,12 +5479,12 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ jl_tupletype_t *env_t = NULL; JL_GC_PUSH2(&closure_t, &env_t); - jl_value_t **env_component_ts = (jl_value_t**)alloca(sizeof(jl_value_t*) * (nargs-4)); + SmallVector env_component_ts(nargs-4); for (size_t i = 0; i < nargs - 4; ++i) { env_component_ts[i] = argv[4+i].typ; } - env_t = jl_apply_tuple_type_v(env_component_ts, nargs-4); + env_t = jl_apply_tuple_type_v(env_component_ts.data(), nargs-4); // we need to know the full env type to look up the right specialization if (jl_is_concrete_type((jl_value_t*)env_t)) { jl_tupletype_t *argt_typ = (jl_tupletype_t*)argt.constant; @@ -5565,7 +5566,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ return mark_julia_const(ctx, bounds_check_enabled(ctx, jl_true) ? jl_true : jl_false); } else if (head == jl_gc_preserve_begin_sym) { - jl_cgval_t *argv = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nargs); + SmallVector argv(nargs); for (size_t i = 0; i < nargs; ++i) { argv[i] = emit_expr(ctx, args[i]); } @@ -5716,7 +5717,7 @@ static void emit_cfunc_invalidate( allocate_gc_frame(ctx, b0); Function::arg_iterator AI = gf_thunk->arg_begin(); - jl_cgval_t *myargs = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nargs); + SmallVector myargs(nargs); if (cc == jl_returninfo_t::SRet || cc == jl_returninfo_t::Union) ++AI; if (return_roots) @@ -5747,7 +5748,7 @@ static void emit_cfunc_invalidate( } } assert(AI == gf_thunk->arg_end()); - Value *gf_ret = emit_jlcall(ctx, target, nullptr, myargs, nargs, julia_call); + Value *gf_ret = emit_jlcall(ctx, target, nullptr, myargs.data(), nargs, julia_call); jl_cgval_t gf_retbox = mark_julia_type(ctx, gf_ret, true, jl_any_type); if (cc != jl_returninfo_t::Boxed) { emit_typecheck(ctx, gf_retbox, rettype, "cfunction"); @@ -5977,7 +5978,7 @@ static Function* gen_cfun_wrapper( Function::arg_iterator AI = cw->arg_begin(); Value *sretPtr = sig.sret ? &*AI++ : NULL; Value *nestPtr = nest ? &*AI++ : NULL; - jl_cgval_t *inputargs = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * (nargs + 1)); + SmallVector inputargs(nargs + 1); if (ff) { // we need to pass the function object even if (even though) it is a singleton inputargs[0] = mark_julia_const(ctx, ff); @@ -6168,7 +6169,7 @@ static Function* gen_cfun_wrapper( ctx.builder.CreateBr(b_after); ctx.builder.SetInsertPoint(b_generic); } - Value *ret = emit_jlcall(ctx, jlapplygeneric_func, NULL, inputargs, nargs + 1, julia_call); + Value *ret = emit_jlcall(ctx, jlapplygeneric_func, NULL, inputargs.data(), nargs + 1, julia_call); if (age_ok) { ctx.builder.CreateBr(b_after); ctx.builder.SetInsertPoint(b_after); @@ -6608,7 +6609,7 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret // TODO: replace this with emit_call_specfun_other? FunctionType *ftype = f.decl->getFunctionType(); size_t nfargs = ftype->getNumParams(); - Value **args = (Value**) alloca(nfargs * sizeof(Value*)); + SmallVector args(nfargs); unsigned idx = 0; AllocaInst *result = NULL; switch (f.cc) { @@ -6665,7 +6666,7 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret args[idx] = theArg; idx++; } - CallInst *call = ctx.builder.CreateCall(f.decl, ArrayRef(&args[0], nfargs)); + CallInst *call = ctx.builder.CreateCall(f.decl, args); call->setAttributes(f.decl->getAttributes()); jl_cgval_t retval; @@ -7594,7 +7595,7 @@ static jl_llvm_functions_t } else if (specsig) { ctx.nvargs = jl_nparams(lam->specTypes) - nreq; - jl_cgval_t *vargs = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * ctx.nvargs); + SmallVector vargs(ctx.nvargs); for (size_t i = nreq; i < jl_nparams(lam->specTypes); ++i) { jl_value_t *argType = jl_nth_slot_type(lam->specTypes, i); bool isboxed = deserves_argbox(argType); @@ -7602,12 +7603,12 @@ static jl_llvm_functions_t vargs[i - nreq] = get_specsig_arg(argType, llvmArgType, isboxed); } if (jl_is_concrete_type(vi.value.typ)) { - jl_cgval_t tuple = emit_new_struct(ctx, vi.value.typ, ctx.nvargs, vargs); + jl_cgval_t tuple = emit_new_struct(ctx, vi.value.typ, ctx.nvargs, vargs.data()); emit_varinfo_assign(ctx, vi, tuple); } else { restTuple = emit_jlcall(ctx, jltuple_func, Constant::getNullValue(ctx.types().T_prjlvalue), - vargs, ctx.nvargs, julia_call); + vargs.data(), ctx.nvargs, julia_call); jl_cgval_t tuple = mark_julia_type(ctx, restTuple, true, vi.value.typ); emit_varinfo_assign(ctx, vi, tuple); } diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 8f2f2273506b4..0a029efdb1b00 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -481,11 +481,11 @@ static jl_datatype_t *staticeval_bitstype(const jl_cgval_t &targ) static jl_cgval_t emit_runtime_call(jl_codectx_t &ctx, JL_I::intrinsic f, const jl_cgval_t *argv, size_t nargs) { Function *func = prepare_call(runtime_func()[f]); - Value **argvalues = (Value**)alloca(sizeof(Value*) * nargs); + SmallVector argvalues(nargs); for (size_t i = 0; i < nargs; ++i) { argvalues[i] = boxed(ctx, argv[i]); } - Value *r = ctx.builder.CreateCall(func, makeArrayRef(argvalues, nargs)); + Value *r = ctx.builder.CreateCall(func, argvalues); return mark_julia_type(ctx, r, true, (jl_value_t*)jl_any_type); } @@ -1146,7 +1146,7 @@ static jl_cgval_t emit_intrinsic(jl_codectx_t &ctx, intrinsic f, jl_value_t **ar if (f == cglobal_auto || f == cglobal) return emit_cglobal(ctx, args, nargs); - jl_cgval_t *argv = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nargs); + SmallVector argv(nargs); for (size_t i = 0; i < nargs; ++i) { jl_cgval_t arg = emit_expr(ctx, args[i + 1]); if (arg.typ == jl_bottom_type) { @@ -1166,78 +1166,78 @@ static jl_cgval_t emit_intrinsic(jl_codectx_t &ctx, intrinsic f, jl_value_t **ar const jl_cgval_t &x = argv[0]; jl_value_t *typ = jl_unwrap_unionall(x.typ); if (!jl_is_datatype(typ) || ((jl_datatype_t*)typ)->name != jl_array_typename) - return emit_runtime_call(ctx, f, argv, nargs); + return emit_runtime_call(ctx, f, argv.data(), nargs); return mark_julia_type(ctx, emit_arraylen(ctx, x), false, jl_long_type); } case pointerref: ++Emitted_pointerref; assert(nargs == 3); - return emit_pointerref(ctx, argv); + return emit_pointerref(ctx, argv.data()); case pointerset: ++Emitted_pointerset; assert(nargs == 4); - return emit_pointerset(ctx, argv); + return emit_pointerset(ctx, argv.data()); case atomic_fence: ++Emitted_atomic_fence; assert(nargs == 1); - return emit_atomicfence(ctx, argv); + return emit_atomicfence(ctx, argv.data()); case atomic_pointerref: ++Emitted_atomic_pointerref; assert(nargs == 2); - return emit_atomic_pointerref(ctx, argv); + return emit_atomic_pointerref(ctx, argv.data()); case atomic_pointerset: case atomic_pointerswap: case atomic_pointermodify: case atomic_pointerreplace: ++Emitted_atomic_pointerop; - return emit_atomic_pointerop(ctx, f, argv, nargs, nullptr); + return emit_atomic_pointerop(ctx, f, argv.data(), nargs, nullptr); case bitcast: ++Emitted_bitcast; assert(nargs == 2); - return generic_bitcast(ctx, argv); + return generic_bitcast(ctx, argv.data()); case trunc_int: ++Emitted_trunc_int; assert(nargs == 2); - return generic_cast(ctx, f, Instruction::Trunc, argv, true, true); + return generic_cast(ctx, f, Instruction::Trunc, argv.data(), true, true); case sext_int: ++Emitted_sext_int; assert(nargs == 2); - return generic_cast(ctx, f, Instruction::SExt, argv, true, true); + return generic_cast(ctx, f, Instruction::SExt, argv.data(), true, true); case zext_int: ++Emitted_zext_int; assert(nargs == 2); - return generic_cast(ctx, f, Instruction::ZExt, argv, true, true); + return generic_cast(ctx, f, Instruction::ZExt, argv.data(), true, true); case uitofp: ++Emitted_uitofp; assert(nargs == 2); - return generic_cast(ctx, f, Instruction::UIToFP, argv, false, true); + return generic_cast(ctx, f, Instruction::UIToFP, argv.data(), false, true); case sitofp: ++Emitted_sitofp; assert(nargs == 2); - return generic_cast(ctx, f, Instruction::SIToFP, argv, false, true); + return generic_cast(ctx, f, Instruction::SIToFP, argv.data(), false, true); case fptoui: ++Emitted_fptoui; assert(nargs == 2); - return generic_cast(ctx, f, Instruction::FPToUI, argv, true, false); + return generic_cast(ctx, f, Instruction::FPToUI, argv.data(), true, false); case fptosi: ++Emitted_fptosi; assert(nargs == 2); - return generic_cast(ctx, f, Instruction::FPToSI, argv, true, false); + return generic_cast(ctx, f, Instruction::FPToSI, argv.data(), true, false); case fptrunc: ++Emitted_fptrunc; assert(nargs == 2); - return generic_cast(ctx, f, Instruction::FPTrunc, argv, false, false); + return generic_cast(ctx, f, Instruction::FPTrunc, argv.data(), false, false); case fpext: ++Emitted_fpext; assert(nargs == 2); - return generic_cast(ctx, f, Instruction::FPExt, argv, false, false); + return generic_cast(ctx, f, Instruction::FPExt, argv.data(), false, false); case not_int: { ++Emitted_not_int; assert(nargs == 1); const jl_cgval_t &x = argv[0]; if (!jl_is_primitivetype(x.typ)) - return emit_runtime_call(ctx, f, argv, nargs); + return emit_runtime_call(ctx, f, argv.data(), nargs); Type *xt = INTT(bitstype_to_llvm(x.typ, ctx.builder.getContext(), true), DL); Value *from = emit_unbox(ctx, xt, x, x.typ); Value *ans = ctx.builder.CreateNot(from); @@ -1249,7 +1249,7 @@ static jl_cgval_t emit_intrinsic(jl_codectx_t &ctx, intrinsic f, jl_value_t **ar assert(nargs == 1); const jl_cgval_t &x = argv[0]; if (!x.constant || !jl_is_datatype(x.constant)) - return emit_runtime_call(ctx, f, argv, nargs); + return emit_runtime_call(ctx, f, argv.data(), nargs); jl_datatype_t *dt = (jl_datatype_t*) x.constant; // select the appropriated overloaded intrinsic @@ -1259,7 +1259,7 @@ static jl_cgval_t emit_intrinsic(jl_codectx_t &ctx, intrinsic f, jl_value_t **ar else if (dt == jl_float64_type) intr_name += "f64"; else - return emit_runtime_call(ctx, f, argv, nargs); + return emit_runtime_call(ctx, f, argv.data(), nargs); FunctionCallee intr = jl_Module->getOrInsertFunction(intr_name, getInt1Ty(ctx.builder.getContext())); auto ret = ctx.builder.CreateCall(intr); @@ -1272,14 +1272,14 @@ static jl_cgval_t emit_intrinsic(jl_codectx_t &ctx, intrinsic f, jl_value_t **ar // verify argument types if (!jl_is_primitivetype(xinfo.typ)) - return emit_runtime_call(ctx, f, argv, nargs); + return emit_runtime_call(ctx, f, argv.data(), nargs); Type *xtyp = bitstype_to_llvm(xinfo.typ, ctx.builder.getContext(), true); if (float_func()[f]) xtyp = FLOATT(xtyp); else xtyp = INTT(xtyp, DL); if (!xtyp) - return emit_runtime_call(ctx, f, argv, nargs); + return emit_runtime_call(ctx, f, argv.data(), nargs); ////Bool are required to be in the range [0,1] ////so while they are represented as i8, ////the operations need to be done in mod 1 @@ -1290,31 +1290,31 @@ static jl_cgval_t emit_intrinsic(jl_codectx_t &ctx, intrinsic f, jl_value_t **ar //if (xtyp == (jl_value_t*)jl_bool_type) // r = getInt1Ty(ctx.builder.getContext()); - Type **argt = (Type**)alloca(sizeof(Type*) * nargs); + SmallVector argt(nargs); argt[0] = xtyp; if (f == shl_int || f == lshr_int || f == ashr_int) { if (!jl_is_primitivetype(argv[1].typ)) - return emit_runtime_call(ctx, f, argv, nargs); + return emit_runtime_call(ctx, f, argv.data(), nargs); argt[1] = INTT(bitstype_to_llvm(argv[1].typ, ctx.builder.getContext(), true), DL); } else { for (size_t i = 1; i < nargs; ++i) { if (xinfo.typ != argv[i].typ) - return emit_runtime_call(ctx, f, argv, nargs); + return emit_runtime_call(ctx, f, argv.data(), nargs); argt[i] = xtyp; } } // unbox the arguments - Value **argvalues = (Value**)alloca(sizeof(Value*) * nargs); + SmallVector argvalues(nargs); for (size_t i = 0; i < nargs; ++i) { argvalues[i] = emit_unbox(ctx, argt[i], argv[i], argv[i].typ); } // call the intrinsic jl_value_t *newtyp = xinfo.typ; - Value *r = emit_untyped_intrinsic(ctx, f, argvalues, nargs, (jl_datatype_t**)&newtyp, xinfo.typ); + Value *r = emit_untyped_intrinsic(ctx, f, argvalues.data(), nargs, (jl_datatype_t**)&newtyp, xinfo.typ); // Turn Bool operations into mod 1 now, if needed if (newtyp == (jl_value_t*)jl_bool_type && !r->getType()->isIntegerTy(1)) r = ctx.builder.CreateTrunc(r, getInt1Ty(ctx.builder.getContext())); From 35e4a1f9689f4b98f301884e0683e4f07db7514b Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 10 Apr 2023 10:17:06 -0400 Subject: [PATCH 483/775] make Tuple{Union{}} unconstructable (#49111) Type intersection assumed it was equal to Union{}, so this makes it unconstructable so that holds true. This is similar to what the NamedTuple constructor does. Secondarily, this fixes an inference bug where it would create Vararg{Union{}} and then incorrectly handle that fieldtype. - Fixes #32392 - Addresses part of the concerns discussed in https://github.com/JuliaLang/julia/issues/24614#issuecomment-349679271 - Addresses part of the issues presented in https://github.com/JuliaLang/julia/issues/26175 - May allow improving jl_type_equality_is_identity (https://github.com/JuliaLang/julia/pull/49017/files#diff-882927c6e612596e22406ae0d06adcee88a9ec05e8b61ad81b48942e2cb266e9R986) - May allow improving intersection (finish_unionall can be more aggressive at computing varval for any typevars that appears in covariant position and has lb=Union{} and ub=leaf type) --- base/broadcast.jl | 16 +++-- base/compiler/abstractinterpretation.jl | 25 ++++++-- base/compiler/inferenceresult.jl | 4 +- base/compiler/ssair/inlining.jl | 2 +- base/compiler/tfuncs.jl | 10 +++ base/compiler/typeutils.jl | 1 + base/iterators.jl | 10 +-- base/promotion.jl | 12 +++- base/slicearray.jl | 3 +- src/builtins.c | 2 +- src/codegen.cpp | 16 ++--- src/gf.c | 6 +- src/intrinsics.cpp | 2 +- src/jl_exported_funcs.inc | 1 - src/jltypes.c | 50 +++++++++++---- src/julia.h | 5 +- src/method.c | 74 ++++++++++++---------- src/opaque_closure.c | 3 +- src/precompile.c | 2 +- src/precompile_utils.c | 2 +- src/runtime_intrinsics.c | 2 +- src/subtype.c | 6 +- stdlib/LinearAlgebra/src/uniformscaling.jl | 10 +-- test/compiler/codegen.jl | 7 +- test/compiler/contextual.jl | 21 +++--- test/compiler/effects.jl | 4 +- test/compiler/inference.jl | 24 +++++-- test/reflection.jl | 1 - test/subtype.jl | 21 ++++-- test/tuple.jl | 8 +++ 30 files changed, 219 insertions(+), 131 deletions(-) diff --git a/base/broadcast.jl b/base/broadcast.jl index 0478b1074c505..d86b5cd92e02f 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -732,17 +732,21 @@ broadcastable(x) = collect(x) broadcastable(::Union{AbstractDict, NamedTuple}) = throw(ArgumentError("broadcasting over dictionaries and `NamedTuple`s is reserved")) ## Computation of inferred result type, for empty and concretely inferred cases only -_broadcast_getindex_eltype(bc::Broadcasted) = Base._return_type(bc.f, eltypes(bc.args)) +_broadcast_getindex_eltype(bc::Broadcasted) = combine_eltypes(bc.f, bc.args) _broadcast_getindex_eltype(A) = eltype(A) # Tuple, Array, etc. eltypes(::Tuple{}) = Tuple{} -eltypes(t::Tuple{Any}) = Tuple{_broadcast_getindex_eltype(t[1])} -eltypes(t::Tuple{Any,Any}) = Tuple{_broadcast_getindex_eltype(t[1]), _broadcast_getindex_eltype(t[2])} -eltypes(t::Tuple) = Tuple{_broadcast_getindex_eltype(t[1]), eltypes(tail(t)).types...} +eltypes(t::Tuple{Any}) = Iterators.TupleOrBottom(_broadcast_getindex_eltype(t[1])) +eltypes(t::Tuple{Any,Any}) = Iterators.TupleOrBottom(_broadcast_getindex_eltype(t[1]), _broadcast_getindex_eltype(t[2])) +# eltypes(t::Tuple) = (TT = eltypes(tail(t)); TT === Union{} ? Union{} : Iterators.TupleOrBottom(_broadcast_getindex_eltype(t[1]), TT.parameters...)) +eltypes(t::Tuple) = Iterators.TupleOrBottom(ntuple(i -> _broadcast_getindex_eltype(t[i]), Val(length(t)))...) # Inferred eltype of result of broadcast(f, args...) -combine_eltypes(f, args::Tuple) = - promote_typejoin_union(Base._return_type(f, eltypes(args))) +function combine_eltypes(f, args::Tuple) + argT = eltypes(args) + argT === Union{} && return Union{} + return promote_typejoin_union(Base._return_type(f, argT)) +end ## Broadcasting core diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 5aeba5ca194e9..e3642ceac27c4 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -541,6 +541,8 @@ function abstract_call_method(interp::AbstractInterpreter, add_remark!(interp, sv, "Refusing to infer into `depwarn`") return MethodCallResult(Any, false, false, nothing, Effects()) end + sigtuple = unwrap_unionall(sig) + sigtuple isa DataType || return MethodCallResult(Any, false, false, nothing, Effects()) # Limit argument type tuple growth of functions: # look through the parents list to see if there's a call to the same method @@ -577,7 +579,6 @@ function abstract_call_method(interp::AbstractInterpreter, washardlimit = hardlimit if topmost !== nothing - sigtuple = unwrap_unionall(sig)::DataType msig = unwrap_unionall(method.sig)::DataType spec_len = length(msig.parameters) + 1 ls = length(sigtuple.parameters) @@ -1394,7 +1395,11 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) va = isvarargtype(last) elts = Any[ fieldtype(tti0, i) for i = 1:len ] if va - elts[len] = Vararg{elts[len]} + if elts[len] === Union{} + pop!(elts) + else + elts[len] = Vararg{elts[len]} + end end return AbstractIterationResult(elts, nothing) end @@ -1403,6 +1408,9 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) elseif tti0 === Any return AbstractIterationResult(Any[Vararg{Any}], nothing, Effects()) elseif tti0 <: Array + if eltype(tti0) === Union{} + return AbstractIterationResult(Any[], nothing) + end return AbstractIterationResult(Any[Vararg{eltype(tti0)}], nothing) else return abstract_iteration(interp, itft, typ, sv) @@ -2115,7 +2123,7 @@ end function sp_type_rewrap(@nospecialize(T), linfo::MethodInstance, isreturn::Bool) isref = false - if T === Bottom + if unwrapva(T) === Bottom return Bottom elseif isa(T, Type) if isa(T, DataType) && (T::DataType).name === _REF_NAME @@ -2152,8 +2160,13 @@ end function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable,Nothing}, sv::AbsIntState) f = abstract_eval_value(interp, e.args[2], vtypes, sv) # rt = sp_type_rewrap(e.args[3], sv.linfo, true) - at = Any[ sp_type_rewrap(argt, frame_instance(sv), false) for argt in e.args[4]::SimpleVector ] - pushfirst!(at, f) + atv = e.args[4]::SimpleVector + at = Vector{Any}(undef, length(atv) + 1) + at[1] = f + for i = 1:length(atv) + at[i + 1] = sp_type_rewrap(at[i], frame_instance(sv), false) + at[i + 1] === Bottom && return + end # this may be the wrong world for the call, # but some of the result is likely to be valid anyways # and that may help generate better codegen @@ -2370,7 +2383,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp end)) nothrow = isexact t = Const(ccall(:jl_new_structt, Any, (Any, Any), t, at.val)) - elseif (isa(at, PartialStruct) && at ⊑ᵢ Tuple && n == length(at.fields::Vector{Any}) && + elseif (isa(at, PartialStruct) && at ⊑ᵢ Tuple && n > 0 && n == length(at.fields::Vector{Any}) && !isvarargtype(at.fields[end]) && (let t = t, at = at, ⊑ᵢ = ⊑ᵢ all(i::Int->(at.fields::Vector{Any})[i] ⊑ᵢ fieldtype(t, i), 1:n) end)) diff --git a/base/compiler/inferenceresult.jl b/base/compiler/inferenceresult.jl index 790af05eebada..68fe2d9f02038 100644 --- a/base/compiler/inferenceresult.jl +++ b/base/compiler/inferenceresult.jl @@ -117,9 +117,9 @@ function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(spe # to the appropriate `Tuple` type or `PartialStruct` instance. if !toplevel && isva if specTypes::Type == Tuple + linfo_argtypes = Any[Any for i = 1:nargs] if nargs > 1 - linfo_argtypes = Any[Any for i = 1:nargs] - linfo_argtypes[end] = Vararg{Any} + linfo_argtypes[end] = Tuple end vargtype = Tuple else diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index b91652e478636..3b1cb2c46ce6e 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -1170,7 +1170,7 @@ function handle_invoke_call!(todo::Vector{Pair{Int,Any}}, if isa(result, ConstPropResult) mi = result.result.linfo validate_sparams(mi.sparam_vals) || return nothing - if argtypes_to_type(argtypes) <: mi.def.sig + if Union{} !== argtypes_to_type(argtypes) <: mi.def.sig item = resolve_todo(mi, result.result, argtypes, info, flag, state; invokesig) handle_single_case!(todo, ir, idx, stmt, item, OptimizationParams(state.interp), true) return nothing diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 48f712576d32f..cb75a8e769712 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1904,7 +1904,15 @@ add_tfunc(apply_type, 1, INT_INF, apply_type_tfunc, 10) # convert the dispatch tuple type argtype to the real (concrete) type of # the tuple of those values function tuple_tfunc(𝕃::AbstractLattice, argtypes::Vector{Any}) + isempty(argtypes) && return Const(()) argtypes = anymap(widenslotwrapper, argtypes) + if isvarargtype(argtypes[end]) && unwrapva(argtypes[end]) === Union{} + # Drop the Vararg in Tuple{...,Vararg{Union{}}} since it must be length 0. + # If there is a Vararg num also, it must be a TypeVar, and it must be + # zero, but that generally shouldn't show up here, since it implies a + # UnionAll context is missing around this. + pop!(argtypes) + end all_are_const = true for i in 1:length(argtypes) if !isa(argtypes[i], Const) @@ -1947,6 +1955,8 @@ function tuple_tfunc(𝕃::AbstractLattice, argtypes::Vector{Any}) params[i] = x elseif !isvarargtype(x) && hasintersect(x, Type) params[i] = Union{x, Type} + elseif x === Union{} + return Bottom # argtypes is malformed, but try not to crash else params[i] = x end diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index 3b4975a6cb848..98117fd7cb345 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -187,6 +187,7 @@ function typesubtract(@nospecialize(a), @nospecialize(b), max_union_splitting::I bp = b.parameters[i] (isvarargtype(ap) || isvarargtype(bp)) && return a ta[i] = typesubtract(ap, bp, min(2, max_union_splitting)) + ta[i] === Union{} && return Union{} return Tuple{ta...} end end diff --git a/base/iterators.jl b/base/iterators.jl index f2a9f23c9d094..a4d12517aabcc 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -12,7 +12,7 @@ using .Base: @inline, Pair, Pairs, AbstractDict, IndexLinear, IndexStyle, AbstractVector, Vector, SizeUnknown, HasLength, HasShape, IsInfinite, EltypeUnknown, HasEltype, OneTo, @propagate_inbounds, @isdefined, @boundscheck, @inbounds, Generator, - AbstractRange, AbstractUnitRange, UnitRange, LinearIndices, + AbstractRange, AbstractUnitRange, UnitRange, LinearIndices, TupleOrBottom, (:), |, +, -, *, !==, !, ==, !=, <=, <, >, >=, missing, any, _counttuple, eachindex, ntuple, zero, prod, reduce, in, firstindex, lastindex, tail, fieldtypes, min, max, minimum, zero, oneunit, promote, promote_shape @@ -209,7 +209,7 @@ size(e::Enumerate) = size(e.itr) end last(e::Enumerate) = (length(e.itr), e.itr[end]) -eltype(::Type{Enumerate{I}}) where {I} = Tuple{Int, eltype(I)} +eltype(::Type{Enumerate{I}}) where {I} = TupleOrBottom(Int, eltype(I)) IteratorSize(::Type{Enumerate{I}}) where {I} = IteratorSize(I) IteratorEltype(::Type{Enumerate{I}}) where {I} = IteratorEltype(I) @@ -394,7 +394,7 @@ _promote_tuple_shape((m,)::Tuple{Integer}, (n,)::Tuple{Integer}) = (min(m, n),) _promote_tuple_shape(a, b) = promote_shape(a, b) _promote_tuple_shape(a, b...) = _promote_tuple_shape(a, _promote_tuple_shape(b...)) _promote_tuple_shape(a) = a -eltype(::Type{Zip{Is}}) where {Is<:Tuple} = Tuple{map(eltype, fieldtypes(Is))...} +eltype(::Type{Zip{Is}}) where {Is<:Tuple} = TupleOrBottom(map(eltype, fieldtypes(Is))...) #eltype(::Type{Zip{Tuple{}}}) = Tuple{} #eltype(::Type{Zip{Tuple{A}}}) where {A} = Tuple{eltype(A)} #eltype(::Type{Zip{Tuple{A, B}}}) where {A, B} = Tuple{eltype(A), eltype(B)} @@ -1072,8 +1072,7 @@ end eltype(::Type{ProductIterator{I}}) where {I} = _prod_eltype(I) _prod_eltype(::Type{Tuple{}}) = Tuple{} -_prod_eltype(::Type{I}) where {I<:Tuple} = - Tuple{ntuple(n -> eltype(fieldtype(I, n)), _counttuple(I)::Int)...} +_prod_eltype(::Type{I}) where {I<:Tuple} = TupleOrBottom(ntuple(n -> eltype(fieldtype(I, n)), _counttuple(I)::Int)...) iterate(::ProductIterator{Tuple{}}) = (), true iterate(::ProductIterator{Tuple{}}, state) = nothing @@ -1442,6 +1441,7 @@ end function _approx_iter_type(itrT::Type, vstate::Type) vstate <: Union{Nothing, Tuple{Any, Any}} || return Any vstate <: Union{} && return Union{} + itrT <: Union{} && return Union{} nextvstate = Base._return_type(doiterate, Tuple{itrT, vstate}) return (nextvstate <: vstate ? vstate : Any) end diff --git a/base/promotion.jl b/base/promotion.jl index b9ab5ed7254f7..31f507d021e78 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -472,6 +472,11 @@ else _return_type(@nospecialize(f), @nospecialize(t)) = Any end +function TupleOrBottom(tt...) + any(p -> p === Union{}, tt) && return Union{} + return Tuple{tt...} +end + """ promote_op(f, argtypes...) @@ -483,7 +488,12 @@ Guess what an appropriate container eltype would be for storing results of the container eltype on the type of the actual elements. Only in the absence of any elements (for an empty result container), it may be unavoidable to call `promote_op`. """ -promote_op(f, S::Type...) = _return_type(f, Tuple{S...}) +function promote_op(f, S::Type...) + argT = TupleOrBottom(S...) + argT === Union{} && return Union{} + return _return_type(f, argT) +end + ## catch-alls to prevent infinite recursion when definitions are missing ## diff --git a/base/slicearray.jl b/base/slicearray.jl index fae353dbe7690..e5a433cdb8d2a 100644 --- a/base/slicearray.jl +++ b/base/slicearray.jl @@ -40,7 +40,8 @@ unitaxis(::AbstractArray) = Base.OneTo(1) function Slices(A::P, slicemap::SM, ax::AX) where {P,SM,AX} N = length(ax) - S = Base._return_type(view, Tuple{P, map((a,l) -> l === (:) ? Colon : eltype(a), axes(A), slicemap)...}) + argT = map((a,l) -> l === (:) ? Colon : eltype(a), axes(A), slicemap) + S = Base.promote_op(view, P, argT...) Slices{P,SM,AX,S,N}(A, slicemap, ax) end diff --git a/src/builtins.c b/src/builtins.c index 00fa848c7c7fc..8138694fdee8a 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1320,7 +1320,7 @@ JL_CALLABLE(jl_f_apply_type) jl_type_error_rt("Tuple", "parameter", (jl_value_t*)jl_type_type, pi); } } - return (jl_value_t*)jl_apply_tuple_type_v(&args[1], nargs-1); + return jl_apply_tuple_type_v(&args[1], nargs-1); } else if (args[0] == (jl_value_t*)jl_uniontype_type) { // Union{} has extra restrictions, so it needs to be checked after diff --git a/src/codegen.cpp b/src/codegen.cpp index 49f5472d4132e..b6b86ba4442e1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5153,7 +5153,7 @@ static std::pair get_oc_function(jl_codectx_t &ctx, jl_met for (size_t i = 0; i < jl_svec_len(argt_typ->parameters); ++i) { jl_svecset(sig_args, 1+i, jl_svecref(argt_typ->parameters, i)); } - sigtype = (jl_value_t*)jl_apply_tuple_type_v(jl_svec_data(sig_args), nsig); + sigtype = jl_apply_tuple_type_v(jl_svec_data(sig_args), nsig); jl_method_instance_t *mi = jl_specializations_get_linfo(closure_method, sigtype, jl_emptysvec); jl_code_instance_t *ci = (jl_code_instance_t*)jl_rettype_inferred(mi, ctx.world, ctx.world); @@ -5476,7 +5476,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ if (can_optimize) { jl_value_t *closure_t = NULL; - jl_tupletype_t *env_t = NULL; + jl_value_t *env_t = NULL; JL_GC_PUSH2(&closure_t, &env_t); SmallVector env_component_ts(nargs-4); @@ -5486,10 +5486,10 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ env_t = jl_apply_tuple_type_v(env_component_ts.data(), nargs-4); // we need to know the full env type to look up the right specialization - if (jl_is_concrete_type((jl_value_t*)env_t)) { + if (jl_is_concrete_type(env_t)) { jl_tupletype_t *argt_typ = (jl_tupletype_t*)argt.constant; Function *F, *specF; - std::tie(F, specF) = get_oc_function(ctx, (jl_method_t*)source.constant, env_t, argt_typ, ub.constant); + std::tie(F, specF) = get_oc_function(ctx, (jl_method_t*)source.constant, (jl_datatype_t*)env_t, argt_typ, ub.constant); if (F) { jl_cgval_t jlcall_ptr = mark_julia_type(ctx, F, false, jl_voidpointer_type); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); @@ -5502,7 +5502,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ fptr = mark_julia_type(ctx, (llvm::Value*)Constant::getNullValue(ctx.types().T_size), false, jl_voidpointer_type); // TODO: Inline the env at the end of the opaque closure and generate a descriptor for GC - jl_cgval_t env = emit_new_struct(ctx, (jl_value_t*)env_t, nargs-4, &argv.data()[4]); + jl_cgval_t env = emit_new_struct(ctx, env_t, nargs-4, &argv.data()[4]); jl_cgval_t closure_fields[5] = { env, @@ -6448,7 +6448,7 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con sigt = NULL; } else { - sigt = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)sigt); + sigt = jl_apply_tuple_type((jl_svec_t*)sigt); } if (sigt && !(unionall_env && jl_has_typevar_from_unionall(rt, unionall_env))) { unionall_env = NULL; @@ -6898,9 +6898,9 @@ static jl_datatype_t *compute_va_type(jl_method_instance_t *lam, size_t nreq) } jl_svecset(tupargs, i-nreq, argType); } - jl_datatype_t *typ = jl_apply_tuple_type(tupargs); + jl_value_t *typ = jl_apply_tuple_type(tupargs); JL_GC_POP(); - return typ; + return (jl_datatype_t*)typ; } diff --git a/src/gf.c b/src/gf.c index 187cfb07a2d1a..85c9766587f37 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1227,7 +1227,7 @@ static jl_method_instance_t *cache_method( intptr_t nspec = (kwmt == NULL || kwmt == jl_type_type_mt || kwmt == jl_nonfunction_mt || kwmt == jl_kwcall_mt ? definition->nargs + 1 : jl_atomic_load_relaxed(&kwmt->max_args) + 2 + 2 * (mt == jl_kwcall_mt)); jl_compilation_sig(tt, sparams, definition, nspec, &newparams); if (newparams) { - temp2 = (jl_value_t*)jl_apply_tuple_type(newparams); + temp2 = jl_apply_tuple_type(newparams); // Now there may be a problem: the widened signature is more general // than just the given arguments, so it might conflict with another // definition that does not have cache instances yet. To fix this, we @@ -1350,7 +1350,7 @@ static jl_method_instance_t *cache_method( } } if (newparams) { - simplett = jl_apply_tuple_type(newparams); + simplett = (jl_datatype_t*)jl_apply_tuple_type(newparams); temp2 = (jl_value_t*)simplett; } @@ -2513,7 +2513,7 @@ JL_DLLEXPORT jl_value_t *jl_normalize_to_compilable_sig(jl_methtable_t *mt, jl_t jl_compilation_sig(ti, env, m, nspec, &newparams); int is_compileable = ((jl_datatype_t*)ti)->isdispatchtuple; if (newparams) { - tt = jl_apply_tuple_type(newparams); + tt = (jl_datatype_t*)jl_apply_tuple_type(newparams); if (!is_compileable) { // compute new env, if used below jl_value_t *ti = jl_type_intersection_env((jl_value_t*)tt, (jl_value_t*)m->sig, &newparams); diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 0a029efdb1b00..91a06f2f10524 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -1416,7 +1416,7 @@ static Value *emit_untyped_intrinsic(jl_codectx_t &ctx, intrinsic f, Value **arg jl_value_t *params[2]; params[0] = xtyp; params[1] = (jl_value_t*)jl_bool_type; - jl_datatype_t *tuptyp = jl_apply_tuple_type_v(params, 2); + jl_datatype_t *tuptyp = (jl_datatype_t*)jl_apply_tuple_type_v(params, 2); *newtyp = tuptyp; Value *tupval; diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 863f5d5686fb7..02355d7003605 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -478,7 +478,6 @@ XX(jl_try_substrtod) \ XX(jl_try_substrtof) \ XX(jl_tty_set_mode) \ - XX(jl_tupletype_fill) \ XX(jl_typeassert) \ XX(jl_typeinf_lock_begin) \ XX(jl_typeinf_lock_end) \ diff --git a/src/jltypes.c b/src/jltypes.c index 759e90d941bed..2aa8385e744a3 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1154,7 +1154,7 @@ static jl_value_t *inst_datatype_env(jl_value_t *dt, jl_svec_t *p, jl_value_t ** jl_value_t *jl_apply_type(jl_value_t *tc, jl_value_t **params, size_t n) { if (tc == (jl_value_t*)jl_anytuple_type) - return (jl_value_t*)jl_apply_tuple_type_v(params, n); + return jl_apply_tuple_type_v(params, n); if (tc == (jl_value_t*)jl_uniontype_type) return (jl_value_t*)jl_type_union(params, n); size_t i; @@ -1243,20 +1243,20 @@ jl_datatype_t *jl_apply_cmpswap_type(jl_value_t *dt) } params[0] = dt; params[1] = (jl_value_t*)jl_bool_type; - jl_datatype_t *tuptyp = jl_apply_tuple_type_v(params, 2); + jl_datatype_t *tuptyp = (jl_datatype_t*)jl_apply_tuple_type_v(params, 2); JL_GC_PROMISE_ROOTED(tuptyp); // (JL_ALWAYS_LEAFTYPE) jl_datatype_t *rettyp = (jl_datatype_t*)jl_apply_type2((jl_value_t*)jl_namedtuple_type, names, (jl_value_t*)tuptyp); JL_GC_PROMISE_ROOTED(rettyp); // (JL_ALWAYS_LEAFTYPE) return rettyp; } -JL_DLLEXPORT jl_value_t *jl_tupletype_fill(size_t n, jl_value_t *v) +// used to expand an NTuple to a flat representation +static jl_value_t *jl_tupletype_fill(size_t n, jl_value_t *v) { - // TODO: replace with just using NTuple jl_value_t *p = NULL; JL_GC_PUSH1(&p); p = (jl_value_t*)jl_svec_fill(n, v); - p = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)p); + p = jl_apply_tuple_type((jl_svec_t*)p); JL_GC_POP(); return p; } @@ -1662,9 +1662,31 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value int cacheable = 1; if (istuple) { size_t i; - for (i = 0; cacheable && i < ntp; i++) - if (!jl_is_concrete_type(iparams[i]) && iparams[i] != jl_bottom_type) + for (i = 0; i < ntp; i++) { + jl_value_t *pi = iparams[i]; + if (jl_is_vararg(pi) && jl_unwrap_vararg(pi) == jl_bottom_type) { + jl_value_t *va1 = jl_unwrap_vararg_num(pi); + if (va1 && jl_is_long(va1)) { + ssize_t nt = jl_unbox_long(va1); + if (nt == 0) + va1 = NULL; + else + pi = jl_bottom_type; // trigger errorf below + } + // This imposes an implicit constraint that va1==0, + // so we keep the Vararg if it has a TypeVar + if (va1 == NULL) { + p = NULL; + ntp -= 1; + assert(i == ntp); + break; + } + } + if (pi == jl_bottom_type) + jl_errorf("Tuple field type cannot be Union{}"); + if (cacheable && !jl_is_concrete_type(pi)) cacheable = 0; + } } else { size_t i; @@ -1746,7 +1768,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value l = ntp - 1 + nt; for (; i < l; i++) jl_svecset(p, i, va0); - jl_value_t *ndt = (jl_value_t*)jl_apply_tuple_type(p); + jl_value_t *ndt = jl_apply_tuple_type(p); JL_GC_POP(); return ndt; } @@ -1875,17 +1897,17 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value return (jl_value_t*)ndt; } -static jl_tupletype_t *jl_apply_tuple_type_v_(jl_value_t **p, size_t np, jl_svec_t *params) +static jl_value_t *jl_apply_tuple_type_v_(jl_value_t **p, size_t np, jl_svec_t *params) { - return (jl_datatype_t*)inst_datatype_inner(jl_anytuple_type, params, p, np, NULL, NULL, 1); + return inst_datatype_inner(jl_anytuple_type, params, p, np, NULL, NULL, 1); } -JL_DLLEXPORT jl_tupletype_t *jl_apply_tuple_type(jl_svec_t *params) +JL_DLLEXPORT jl_value_t *jl_apply_tuple_type(jl_svec_t *params) { return jl_apply_tuple_type_v_(jl_svec_data(params), jl_svec_len(params), params); } -JL_DLLEXPORT jl_tupletype_t *jl_apply_tuple_type_v(jl_value_t **p, size_t np) +JL_DLLEXPORT jl_value_t *jl_apply_tuple_type_v(jl_value_t **p, size_t np) { return jl_apply_tuple_type_v_(p, np, NULL); } @@ -1971,7 +1993,7 @@ static jl_value_t *inst_tuple_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_ ssize_t nt = jl_unbox_long(N); if (nt < 0) jl_errorf("size or dimension is negative: %zd", nt); - return (jl_value_t*)jl_tupletype_fill(nt, T); + return jl_tupletype_fill(nt, T); } } jl_value_t **iparams; @@ -2442,7 +2464,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_anytuple_type->layout = NULL; jl_typeofbottom_type->super = jl_wrap_Type(jl_bottom_type); - jl_emptytuple_type = jl_apply_tuple_type(jl_emptysvec); + jl_emptytuple_type = (jl_datatype_t*)jl_apply_tuple_type(jl_emptysvec); jl_emptytuple = jl_gc_permobj(0, jl_emptytuple_type); jl_emptytuple_type->instance = jl_emptytuple; diff --git a/src/julia.h b/src/julia.h index fc8a4b8daa524..45363c8092bb1 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1452,8 +1452,8 @@ JL_DLLEXPORT jl_value_t *jl_apply_type1(jl_value_t *tc, jl_value_t *p1); JL_DLLEXPORT jl_value_t *jl_apply_type2(jl_value_t *tc, jl_value_t *p1, jl_value_t *p2); JL_DLLEXPORT jl_datatype_t *jl_apply_modify_type(jl_value_t *dt); JL_DLLEXPORT jl_datatype_t *jl_apply_cmpswap_type(jl_value_t *dt); -JL_DLLEXPORT jl_tupletype_t *jl_apply_tuple_type(jl_svec_t *params); -JL_DLLEXPORT jl_tupletype_t *jl_apply_tuple_type_v(jl_value_t **p, size_t np); +JL_DLLEXPORT jl_value_t *jl_apply_tuple_type(jl_svec_t *params); +JL_DLLEXPORT jl_value_t *jl_apply_tuple_type_v(jl_value_t **p, size_t np); JL_DLLEXPORT jl_datatype_t *jl_new_datatype(jl_sym_t *name, jl_module_t *module, jl_datatype_t *super, @@ -1487,7 +1487,6 @@ JL_DLLEXPORT jl_svec_t *jl_alloc_svec(size_t n); JL_DLLEXPORT jl_svec_t *jl_alloc_svec_uninit(size_t n); JL_DLLEXPORT jl_svec_t *jl_svec_copy(jl_svec_t *a); JL_DLLEXPORT jl_svec_t *jl_svec_fill(size_t n, jl_value_t *x); -JL_DLLEXPORT jl_value_t *jl_tupletype_fill(size_t n, jl_value_t *v); JL_DLLEXPORT jl_sym_t *jl_symbol(const char *str) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_sym_t *jl_symbol_lookup(const char *str) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_sym_t *jl_symbol_n(const char *str, size_t len) JL_NOTSAFEPOINT; diff --git a/src/method.c b/src/method.c index ee333bebccedf..8b4c87a46ecd9 100644 --- a/src/method.c +++ b/src/method.c @@ -989,7 +989,9 @@ JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata, JL_GC_PUSH3(&f, &m, &argtype); size_t i, na = jl_svec_len(atypes); - argtype = (jl_value_t*)jl_apply_tuple_type(atypes); + argtype = jl_apply_tuple_type(atypes); + if (!jl_is_datatype(argtype)) + jl_error("invalid type in method definition (Union{})"); jl_methtable_t *external_mt = mt; if (!mt) @@ -1024,49 +1026,19 @@ JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata, } } - for (i = jl_svec_len(tvars); i > 0; i--) { - jl_value_t *tv = jl_svecref(tvars, i - 1); - if (!jl_is_typevar(tv)) - jl_type_error("method signature", (jl_value_t*)jl_tvar_type, tv); - if (!jl_has_typevar(argtype, (jl_tvar_t*)tv)) // deprecate this to an error in v2 - jl_printf(JL_STDERR, - "WARNING: method definition for %s at %s:%d declares type variable %s but does not use it.\n", - jl_symbol_name(name), - jl_symbol_name(file), - line, - jl_symbol_name(((jl_tvar_t*)tv)->name)); - argtype = jl_new_struct(jl_unionall_type, tv, argtype); - } - if (jl_has_free_typevars(argtype)) { - jl_exceptionf(jl_argumenterror_type, - "method definition for %s at %s:%d has free type variables", - jl_symbol_name(name), - jl_symbol_name(file), - line); - } - - if (!jl_is_code_info(f)) { // this occurs when there is a closure being added to an out-of-scope function // the user should only do this at the toplevel // the result is that the closure variables get interpolated directly into the IR f = jl_new_code_info_from_ir((jl_expr_t*)f); } - m = jl_new_method_uninit(module); - m->external_mt = (jl_value_t*)external_mt; - if (external_mt) - jl_gc_wb(m, external_mt); - m->sig = argtype; - m->name = name; - m->isva = isva; - m->nargs = nargs; - m->file = file; - m->line = line; - jl_method_set_source(m, f); for (i = 0; i < na; i++) { jl_value_t *elt = jl_svecref(atypes, i); - if (!jl_is_type(elt) && !jl_is_typevar(elt) && !jl_is_vararg(elt)) { + int isvalid = jl_is_type(elt) || jl_is_typevar(elt) || jl_is_vararg(elt); + if (elt == jl_bottom_type || (jl_is_vararg(elt) && jl_unwrap_vararg(elt) == jl_bottom_type)) + isvalid = 0; + if (!isvalid) { jl_sym_t *argname = (jl_sym_t*)jl_array_ptr_ref(f->slotnames, i); if (argname == jl_unused_sym) jl_exceptionf(jl_argumenterror_type, @@ -1090,6 +1062,38 @@ JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata, jl_symbol_name(file), line); } + for (i = jl_svec_len(tvars); i > 0; i--) { + jl_value_t *tv = jl_svecref(tvars, i - 1); + if (!jl_is_typevar(tv)) + jl_type_error("method signature", (jl_value_t*)jl_tvar_type, tv); + if (!jl_has_typevar(argtype, (jl_tvar_t*)tv)) // deprecate this to an error in v2 + jl_printf(JL_STDERR, + "WARNING: method definition for %s at %s:%d declares type variable %s but does not use it.\n", + jl_symbol_name(name), + jl_symbol_name(file), + line, + jl_symbol_name(((jl_tvar_t*)tv)->name)); + argtype = jl_new_struct(jl_unionall_type, tv, argtype); + } + if (jl_has_free_typevars(argtype)) { + jl_exceptionf(jl_argumenterror_type, + "method definition for %s at %s:%d has free type variables", + jl_symbol_name(name), + jl_symbol_name(file), + line); + } + + m = jl_new_method_uninit(module); + m->external_mt = (jl_value_t*)external_mt; + if (external_mt) + jl_gc_wb(m, external_mt); + m->sig = argtype; + m->name = name; + m->isva = isva; + m->nargs = nargs; + m->file = file; + m->line = line; + jl_method_set_source(m, f); #ifdef RECORD_METHOD_ORDER if (jl_all_methods == NULL) diff --git a/src/opaque_closure.c b/src/opaque_closure.c index 1cc7bd23b6c1a..07a17ac3e8bec 100644 --- a/src/opaque_closure.c +++ b/src/opaque_closure.c @@ -22,6 +22,7 @@ JL_DLLEXPORT int jl_is_valid_oc_argtype(jl_tupletype_t *argt, jl_method_t *sourc return 1; } +// TODO: merge this with jl_argtype_with_function static jl_value_t *prepend_type(jl_value_t *t0, jl_tupletype_t *t) { jl_svec_t *sig_args = NULL; @@ -32,7 +33,7 @@ static jl_value_t *prepend_type(jl_value_t *t0, jl_tupletype_t *t) for (size_t i = 0; i < nsig-1; ++i) { jl_svecset(sig_args, 1+i, jl_tparam(t, i)); } - jl_value_t *sigtype = (jl_value_t*)jl_apply_tuple_type_v(jl_svec_data(sig_args), nsig); + jl_value_t *sigtype = jl_apply_tuple_type(sig_args); JL_GC_POP(); return sigtype; } diff --git a/src/precompile.c b/src/precompile.c index 75970a20237c2..4aac28ff9a790 100644 --- a/src/precompile.c +++ b/src/precompile.c @@ -99,7 +99,7 @@ JL_DLLEXPORT void jl_write_compiler_output(void) // since it's a slightly duplication of effort jl_value_t *tt = jl_is_type(f) ? (jl_value_t*)jl_wrap_Type(f) : jl_typeof(f); JL_GC_PUSH1(&tt); - tt = (jl_value_t*)jl_apply_tuple_type_v(&tt, 1); + tt = jl_apply_tuple_type_v(&tt, 1); jl_compile_hint((jl_tupletype_t*)tt); JL_GC_POP(); } diff --git a/src/precompile_utils.c b/src/precompile_utils.c index 5b0c231b04345..9e513a1cfed3a 100644 --- a/src/precompile_utils.c +++ b/src/precompile_utils.c @@ -120,7 +120,7 @@ static void _compile_all_union(jl_value_t *sig) jl_svecset(p, i, ty); } } - methsig = (jl_value_t*)jl_apply_tuple_type(p); + methsig = jl_apply_tuple_type(p); methsig = jl_rewrap_unionall(methsig, sig); _compile_all_tvar_union(methsig); } diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index d53eb368de495..0ac5b277b0657 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -1035,7 +1035,7 @@ static inline jl_value_t *jl_intrinsiclambda_checked(jl_value_t *ty, void *pa, v jl_value_t *params[2]; params[0] = ty; params[1] = (jl_value_t*)jl_bool_type; - jl_datatype_t *tuptyp = jl_apply_tuple_type_v(params, 2); + jl_datatype_t *tuptyp = (jl_datatype_t*)jl_apply_tuple_type_v(params, 2); JL_GC_PROMISE_ROOTED(tuptyp); // (JL_ALWAYS_LEAFTYPE) jl_task_t *ct = jl_current_task; jl_value_t *newv = jl_gc_alloc(ct->ptls, jl_datatype_size(tuptyp), tuptyp); diff --git a/src/subtype.c b/src/subtype.c index 73e68ca671097..f67e37ee079fc 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -3239,9 +3239,9 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten else if (isy) res = (jl_value_t*)yd; else if (p) - res = (jl_value_t*)jl_apply_tuple_type(p); + res = jl_apply_tuple_type(p); else - res = (jl_value_t*)jl_apply_tuple_type_v(params, np); + res = jl_apply_tuple_type_v(params, np); } JL_GC_POP(); return res; @@ -3959,7 +3959,7 @@ static jl_value_t *switch_union_tuple(jl_value_t *a, jl_value_t *b) ts[1] = jl_tparam(b, i); jl_svecset(vec, i, jl_type_union(ts, 2)); } - jl_value_t *ans = (jl_value_t*)jl_apply_tuple_type(vec); + jl_value_t *ans = jl_apply_tuple_type(vec); JL_GC_POP(); return ans; } diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index 2f18dc45a2909..21ae8a1bb913a 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -179,7 +179,7 @@ for (t1, t2) in ((:UnitUpperTriangular, :UpperTriangular), (:UnitLowerTriangular, :LowerTriangular)) @eval begin function (+)(UL::$t1, J::UniformScaling) - ULnew = copymutable_oftype(UL.data, Base._return_type(+, Tuple{eltype(UL), typeof(J)})) + ULnew = copymutable_oftype(UL.data, Base.promote_op(+, eltype(UL), typeof(J))) for i in axes(ULnew, 1) ULnew[i,i] = one(ULnew[i,i]) + J end @@ -193,7 +193,7 @@ end # However, to preserve type stability, we do not special-case a # UniformScaling{<:Complex} that happens to be real. function (+)(A::Hermitian, J::UniformScaling{<:Complex}) - TS = Base._return_type(+, Tuple{eltype(A), typeof(J)}) + TS = Base.promote_op(+, eltype(A), typeof(J)) B = copytri!(copymutable_oftype(parent(A), TS), A.uplo, true) for i in diagind(B) B[i] = A[i] + J @@ -202,7 +202,7 @@ function (+)(A::Hermitian, J::UniformScaling{<:Complex}) end function (-)(J::UniformScaling{<:Complex}, A::Hermitian) - TS = Base._return_type(+, Tuple{eltype(A), typeof(J)}) + TS = Base.promote_op(+, eltype(A), typeof(J)) B = copytri!(copymutable_oftype(parent(A), TS), A.uplo, true) B .= .-B for i in diagind(B) @@ -213,7 +213,7 @@ end function (+)(A::AbstractMatrix, J::UniformScaling) checksquare(A) - B = copymutable_oftype(A, Base._return_type(+, Tuple{eltype(A), typeof(J)})) + B = copymutable_oftype(A, Base.promote_op(+, eltype(A), typeof(J))) for i in intersect(axes(A,1), axes(A,2)) @inbounds B[i,i] += J end @@ -222,7 +222,7 @@ end function (-)(J::UniformScaling, A::AbstractMatrix) checksquare(A) - B = convert(AbstractMatrix{Base._return_type(+, Tuple{eltype(A), typeof(J)})}, -A) + B = convert(AbstractMatrix{Base.promote_op(+, eltype(A), typeof(J))}, -A) for i in intersect(axes(A,1), axes(A,2)) @inbounds B[i,i] += J end diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 2279856cf141c..8a3949212ea16 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -649,7 +649,7 @@ end # issue #41157 f41157(a, b) = a[1] = b[1] -@test_throws BoundsError f41157(Tuple{Int}[], Tuple{Union{}}[]) +@test_throws BoundsError f41157(Tuple{Int}[], (NTuple{N,Union{}} where N)[]) # issue #41096 struct Modulate41096{M<:Union{Function, Val{true}, Val{false}}, id} @@ -786,11 +786,6 @@ f_isa_type(@nospecialize(x)) = isa(x, Type) f47247(a::Ref{Int}, b::Nothing) = setfield!(a, :x, b) @test_throws TypeError f47247(Ref(5), nothing) -@testset "regression in generic_bitcast: should support Union{} values" begin - f(x) = Core.bitcast(UInt64, x) - @test occursin("llvm.trap", get_llvm(f, Tuple{Union{}})) -end - f48085(@nospecialize x...) = length(x) @test Core.Compiler.get_compileable_sig(which(f48085, (Vararg{Any},)), Tuple{typeof(f48085), Vararg{Int}}, Core.svec()) === nothing @test Core.Compiler.get_compileable_sig(which(f48085, (Vararg{Any},)), Tuple{typeof(f48085), Int, Vararg{Int}}, Core.svec()) === Tuple{typeof(f48085), Any, Vararg{Any}} diff --git a/test/compiler/contextual.jl b/test/compiler/contextual.jl index 9db7ae1aeaa5d..16332555a0c3a 100644 --- a/test/compiler/contextual.jl +++ b/test/compiler/contextual.jl @@ -9,7 +9,7 @@ module MiniCassette using Core.Compiler: retrieve_code_info, CodeInfo, MethodInstance, SSAValue, GotoNode, GotoIfNot, ReturnNode, SlotNumber, quoted, - signature_type + signature_type, anymap using Base: _methods_by_ftype using Base.Meta: isexpr using Test @@ -19,10 +19,11 @@ module MiniCassette struct Ctx; end # A no-op cassette-like transform - function transform_expr(expr, map_slot_number, map_ssa_value, sparams) - transform(expr) = transform_expr(expr, map_slot_number, map_ssa_value, sparams) + function transform_expr(expr, map_slot_number, map_ssa_value, sparams::Core.SimpleVector) + @nospecialize expr + transform(@nospecialize expr) = transform_expr(expr, map_slot_number, map_ssa_value, sparams) if isexpr(expr, :call) - return Expr(:call, overdub, SlotNumber(2), map(transform, expr.args)...) + return Expr(:call, overdub, SlotNumber(2), anymap(transform, expr.args)...) elseif isa(expr, GotoIfNot) return GotoIfNot(transform(expr.cond), map_ssa_value(SSAValue(expr.dest)).id) elseif isexpr(expr, :static_parameter) @@ -30,7 +31,7 @@ module MiniCassette elseif isa(expr, ReturnNode) return ReturnNode(transform(expr.val)) elseif isa(expr, Expr) - return Expr(expr.head, map(transform, expr.args)...) + return Expr(expr.head, anymap(transform, expr.args)...) elseif isa(expr, GotoNode) return GotoNode(map_ssa_value(SSAValue(expr.label)).id) elseif isa(expr, SlotNumber) @@ -42,16 +43,16 @@ module MiniCassette end end - function transform!(ci, nargs, sparams) + function transform!(ci::CodeInfo, nargs::Int, sparams::Core.SimpleVector) code = ci.code ci.slotnames = Symbol[Symbol("#self#"), :ctx, :f, :args, ci.slotnames[nargs+1:end]...] ci.slotflags = UInt8[(0x00 for i = 1:4)..., ci.slotflags[nargs+1:end]...] # Insert one SSAValue for every argument statement - prepend!(code, [Expr(:call, getfield, SlotNumber(4), i) for i = 1:nargs]) - prepend!(ci.codelocs, [0 for i = 1:nargs]) - prepend!(ci.ssaflags, [0x00 for i = 1:nargs]) + prepend!(code, Any[Expr(:call, getfield, SlotNumber(4), i) for i = 1:nargs]) + prepend!(ci.codelocs, fill(0, nargs)) + prepend!(ci.ssaflags, fill(0x00, nargs)) ci.ssavaluetypes += nargs - function map_slot_number(slot) + function map_slot_number(slot::Int) if slot == 1 # self in the original function is now `f` return SlotNumber(3) diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index ee1d5dc569702..f809192d8d1ed 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -354,9 +354,9 @@ function f_boundscheck_elim(n) # Inbounds here assumes that this is only ever called with `n==0`, but of # course the compiler has no way of knowing that, so it must not attempt # to run the `@inbounds getfield(sin, 1)` that `ntuple` generates. - ntuple(x->(@inbounds getfield(sin, x)), n) + ntuple(x->(@inbounds ()[x]), n) end -@test !Core.Compiler.is_consistent(Base.infer_effects(f_boundscheck_elim, (Int,))) +@test_broken !Core.Compiler.is_consistent(Base.infer_effects(f_boundscheck_elim, (Int,))) @test Tuple{} <: only(Base.return_types(f_boundscheck_elim, (Int,))) # Test that purity modeling doesn't accidentally introduce new world age issues diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index b03d6acf45457..c8bb10cf21337 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -682,6 +682,7 @@ end # inference of `fieldtype` mutable struct UndefField__ x::Union{} + UndefField__() = new() end f_infer_undef_field() = fieldtype(UndefField__, :x) @test Base.return_types(f_infer_undef_field, ()) == Any[Type{Union{}}] @@ -1020,7 +1021,7 @@ end g21771(T) = T f21771(::Val{U}) where {U} = Tuple{g21771(U)} @test @inferred(f21771(Val{Int}())) === Tuple{Int} -@test @inferred(f21771(Val{Union{}}())) === Tuple{Union{}} +@test_throws ErrorException @inferred(f21771(Val{Union{}}())) @test @inferred(f21771(Val{Integer}())) === Tuple{Integer} # PR #28284, check that constants propagate through calls to new @@ -4400,18 +4401,18 @@ end init = Base.ImmutableDict{Number,Number}() a = Const(init) - b = Core.PartialStruct(typeof(init), Any[Const(init), Any, ComplexF64]) + b = Core.Compiler.PartialStruct(typeof(init), Any[Const(init), Any, ComplexF64]) c = Core.Compiler.tmerge(a, b) @test ⊑(a, c) && ⊑(b, c) @test c === typeof(init) - a = Core.PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64]) + a = Core.Compiler.PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64]) c = Core.Compiler.tmerge(a, b) @test ⊑(a, c) && ⊑(b, c) @test c.fields[2] === Any # or Number @test c.fields[3] === ComplexF64 - b = Core.PartialStruct(typeof(init), Any[Const(init), ComplexF32, Union{ComplexF32,ComplexF64}]) + b = Core.Compiler.PartialStruct(typeof(init), Any[Const(init), ComplexF32, Union{ComplexF32,ComplexF64}]) c = Core.Compiler.tmerge(a, b) @test ⊑(a, c) @test ⊑(b, c) @@ -4453,13 +4454,24 @@ end Core.Compiler.return_type(+, NTuple{2, Rational}) end == Rational -# vararg-tuple comparison within `PartialStruct` +# vararg-tuple comparison within `Compiler.PartialStruct` # https://github.com/JuliaLang/julia/issues/44965 let 𝕃ᵢ = Core.Compiler.fallback_lattice - t = Core.Compiler.tuple_tfunc(𝕃ᵢ, Any[Core.Const(42), Vararg{Any}]) + t = Core.Compiler.tuple_tfunc(𝕃ᵢ, Any[Const(42), Vararg{Any}]) @test Core.Compiler.issimplertype(𝕃ᵢ, t, t) + + t = Core.Compiler.tuple_tfunc(𝕃ᵢ, Any[Const(42), Vararg{Union{}}]) + @test t === Const((42,)) + t = Core.Compiler.tuple_tfunc(𝕃ᵢ, Any[Const(42), Int, Vararg{Union{}}]) + @test t.typ === Tuple{Int, Int} + @test t.fields == Any[Const(42), Int] end +foo_empty_vararg(i...) = i[2] +bar_empty_vararg(i) = foo_empty_vararg(10, 20, 30, i...) +@test bar_empty_vararg(Union{}[]) === 20 + + # check the inference convergence with an empty vartable: # the inference state for the toplevel chunk below will have an empty vartable, # and so we may fail to terminate (or optimize) it if we don't update vartables correctly diff --git a/test/reflection.jl b/test/reflection.jl index ec8ff09e4ad04..3797cab1e5465 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -84,7 +84,6 @@ end # module ReflectionTest @test isconcretetype(DataType) @test isconcretetype(Union) @test !isconcretetype(Union{}) -@test isconcretetype(Tuple{Union{}}) @test !isconcretetype(Complex) @test !isconcretetype(Complex.body) @test !isconcretetype(AbstractArray{Int,1}) diff --git a/test/subtype.jl b/test/subtype.jl index 40ebda9ec9a73..2ec2a8d89e5e0 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -588,7 +588,7 @@ function test_old() end const easy_menagerie = - Any[Bottom, Any, Int, Int8, Integer, Real, + Any[Any, Int, Int8, Integer, Real, Array{Int,1}, AbstractArray{Int,1}, Tuple{Int,Vararg{Integer}}, Tuple{Integer,Vararg{Int}}, Tuple{}, Union{Int,Int8}, @@ -627,6 +627,10 @@ end add_variants!(easy_menagerie) add_variants!(hard_menagerie) +push!(easy_menagerie, Bottom) +push!(easy_menagerie, Ref{Bottom}) +push!(easy_menagerie, @UnionAll N NTuple{N,Bottom}) +push!(easy_menagerie, @UnionAll S<:Bottom Ref{S}) const menagerie = [easy_menagerie; hard_menagerie] @@ -673,9 +677,11 @@ function test_properties() @test isequal_type(T, S) == isequal_type(Ref{T}, Ref{S}) # covariance - @test issubTS == issub(Tuple{T}, Tuple{S}) - @test issubTS == issub(Tuple{Vararg{T}}, Tuple{Vararg{S}}) - @test issubTS == issub(Tuple{T}, Tuple{Vararg{S}}) + if T !== Bottom && S !== Bottom + @test issubTS == issub(Tuple{T}, Tuple{S}) + @test issubTS == issub(Tuple{Vararg{T}}, Tuple{Vararg{S}}) + @test issubTS == issub(Tuple{T}, Tuple{Vararg{S}}) + end # pseudo-contravariance @test issubTS == issub(¬S, ¬T) @@ -1870,8 +1876,11 @@ s26065 = Ref{Tuple{T,Ref{Union{Ref{Tuple{Ref{Union{Ref{Ref{Tuple{Ref{Tuple{Union Tuple{Type{Tuple{Vararg{V}}}, Tuple{Vararg{V}}} where V) # issue 36100 -@test NamedTuple{(:a, :b), Tuple{Missing, Union{}}} == NamedTuple{(:a, :b), Tuple{Missing, Union{}}} -@test Val{Tuple{Missing, Union{}}} === Val{Tuple{Missing, Union{}}} +@test Pair{(:a, :b), Tuple{Missing, Vararg{Union{},N}} where N} === + Pair{(:a, :b), Tuple{Missing, Vararg{Union{},N}} where N} != + Pair{(:a, :b), Tuple{Missing, Vararg{Union{}}}} === Pair{(:a, :b), Tuple{Missing}} +@test Val{Tuple{Missing, Vararg{Union{},N}} where N} === Val{Tuple{Missing, Vararg{Union{},N}} where N} != + Val{Tuple{Missing, Vararg{Union{}}}} === Val{Tuple{Missing}} # issue #36869 struct F36869{T, V} <: AbstractArray{Union{T, V}, 1} diff --git a/test/tuple.jl b/test/tuple.jl index ae764bd05481b..9238c6cd6d7a6 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -783,3 +783,11 @@ namedtup = (;a=1, b=2, c=3) # Make sure that tuple iteration is foldable @test Core.Compiler.is_foldable(Base.infer_effects(iterate, Tuple{NTuple{4, Float64}, Int})) @test Core.Compiler.is_foldable(Base.infer_effects(eltype, Tuple{Tuple})) + +# some basic equivalence handling tests for Union{} appearing in Tuple Vararg parameters +@test Tuple{} <: Tuple{Vararg{Union{}}} +@test Tuple{Int} <: Tuple{Int, Vararg{Union{}}} +@test_throws ErrorException("Tuple field type cannot be Union{}") Tuple{Int, Vararg{Union{},1}} +@test_throws ErrorException("Tuple field type cannot be Union{}") Tuple{Vararg{Union{},1}} +@test Tuple{} <: Tuple{Vararg{Union{},N}} where N +@test !(Tuple{} >: Tuple{Vararg{Union{},N}} where N) From 2a19342c614d00ebfeff302289509f6004a0ce93 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 10 Apr 2023 11:39:40 -0400 Subject: [PATCH 484/775] [REPLCompletions] this code uses `nothing` as the failure token type Need to convert the failure type from inference (Union{}) to the failure type for the new completions logic (nothing). Simultaneously update these tests to be more robust. There is no sort guaranteed order for `methods`, so the lookup needs to be the same as the completions is expected to do. Changed by 98988d8bfbc769c3d20736e0c2e20717b11cd4c9, which ended up conflicting with 35e4a1f9689f4b98f301884e0683e4f07db7514b. --- stdlib/REPL/src/REPLCompletions.jl | 4 ++- stdlib/REPL/test/replcompletions.jl | 48 ++++++++++++++++------------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index c5d8832e4d7d0..149920381dcc8 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -567,7 +567,9 @@ function repl_eval_ex(@nospecialize(ex), context_module::Module) CC.typeinf(interp, frame) - return frame.result.result + result = frame.result.result + result === Union{} && return nothing # for whatever reason, callers expect this as the Bottom and/or Top type instead + return result end # Method completion on function call expression that look like :(max(1)) diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index b53999bc79f3d..4577be19fa9ac 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -158,6 +158,9 @@ test_complete_context(s, m) = map_completion_text(@inferred(completions(s,lasti test_complete_foo(s) = test_complete_context(s, Main.CompletionFoo) test_complete_noshift(s) = map_completion_text(@inferred(completions(s, lastindex(s), Main, false))) +test_methods_list(@nospecialize(f), tt) = map(x -> string(x.method), Base._methods_by_ftype(Base.signature_type(f, tt), 10, Base.get_world_counter())) + + module M32377 end test_complete_32377(s) = map_completion_text(completions(s,lastindex(s), M32377)) @@ -423,8 +426,9 @@ end let s = "CompletionFoo.test(1, 1, " c, r, res = test_complete(s) @test !res - @test c[1] == string(first(methods(Main.CompletionFoo.test, Tuple{Int, Int}))) - @test c[2] == string(first(methods(Main.CompletionFoo.test, Tuple{}))) # corresponding to the vararg + m = test_methods_list(Main.CompletionFoo.test, Tuple{Int, Int, Vararg}) + @test c[1] == m[1] + @test c[2] == m[2] @test length(c) == 2 # In particular, this checks that test(x::Real, y::Real) is not a valid completion # since it is strictly less specific than test(x::T, y::T) where T @@ -435,7 +439,7 @@ end let s = "CompletionFoo.test(CompletionFoo.array," c, r, res = test_complete(s) @test !res - @test c[1] == string(first(methods(Main.CompletionFoo.test, Tuple{Array{Int, 1}, Any}))) + @test c[1] == first(test_methods_list(Main.CompletionFoo.test, Tuple{Array{Int, 1}, Any, Vararg})) @test length(c) == 2 @test r == 1:18 @test s[r] == "CompletionFoo.test" @@ -444,7 +448,7 @@ end let s = "CompletionFoo.test(1,1,1," c, r, res = test_complete(s) @test !res - @test c[1] == string(first(methods(Main.CompletionFoo.test, Tuple{Any, Any, Any}))) + @test c[1] == first(test_methods_list(Main.CompletionFoo.test, Tuple{Any, Any, Any, Vararg})) @test length(c) == 1 @test r == 1:18 @test s[r] == "CompletionFoo.test" @@ -468,7 +472,7 @@ end let s = "prevind(\"θ\",1," c, r, res = test_complete(s) - @test c[1] == string(first(methods(prevind, Tuple{String, Int}))) + @test c[1] == first(test_methods_list(prevind, Tuple{String, Int, Vararg})) @test r == 1:7 @test s[r] == "prevind" end @@ -477,7 +481,7 @@ for (T, arg) in [(String,"\")\""),(Char, "')'")] s = "(1, CompletionFoo.test2($arg," c, r, res = test_complete(s) @test length(c) == 1 - @test c[1] == string(first(methods(Main.CompletionFoo.test2, Tuple{T,}))) + @test c[1] == first(test_methods_list(Main.CompletionFoo.test2, Tuple{T, Vararg})) @test r == 5:23 @test s[r] == "CompletionFoo.test2" end @@ -485,19 +489,19 @@ end let s = "(1, CompletionFoo.test2(`')'`," c, r, res = test_complete(s) @test length(c) == 1 - @test c[1] == string(first(methods(Main.CompletionFoo.test2, Tuple{Cmd}))) + @test c[1] == first(test_methods_list(Main.CompletionFoo.test2, Tuple{Cmd, Vararg})) end let s = "CompletionFoo.test3([1, 2] .+ CompletionFoo.varfloat," c, r, res = test_complete(s) @test !res - @test_broken only(c) == string(first(methods(Main.CompletionFoo.test3, Tuple{Array{Float64, 1}, Float64}))) + @test_broken only(c) == first(test_methods_list(Main.CompletionFoo.test3, Tuple{Array{Float64, 1}, Float64, Vararg})) end let s = "CompletionFoo.test3([1.,2.], 1.," c, r, res = test_complete(s) @test !res - @test c[1] == string(first(methods(Main.CompletionFoo.test3, Tuple{Array{Float64, 1}, Float64}))) + @test c[1] == first(test_methods_list(Main.CompletionFoo.test3, Tuple{Array{Float64, 1}, Float64, Vararg})) @test r == 1:19 @test length(c) == 1 @test s[r] == "CompletionFoo.test3" @@ -506,7 +510,7 @@ end let s = "CompletionFoo.test4(\"e\",r\" \"," c, r, res = test_complete(s) @test !res - @test c[1] == string(first(methods(Main.CompletionFoo.test4, Tuple{String, Regex}))) + @test c[1] == first(test_methods_list(Main.CompletionFoo.test4, Tuple{String, Regex, Vararg})) @test r == 1:19 @test length(c) == 1 @test s[r] == "CompletionFoo.test4" @@ -517,7 +521,7 @@ end let s = "CompletionFoo.test5(broadcast((x,y)->x==y, push!(Base.split(\"\",' '),\"\",\"\"), \"\")," c, r, res = test_complete(s) @test !res - @test_broken only(c) == string(first(methods(Main.CompletionFoo.test5, Tuple{BitArray{1}}))) + @test_broken only(c) == first(test_methods_list(Main.CompletionFoo.test5, Tuple{BitArray{1}, Vararg})) end # test partial expression expansion @@ -525,14 +529,14 @@ let s = "CompletionFoo.test5(Bool[x==1 for x=1:4]," c, r, res = test_complete(s) @test !res @test length(c) == 1 - @test c[1] == string(first(methods(Main.CompletionFoo.test5, Tuple{Array{Bool,1}}))) + @test c[1] == first(test_methods_list(Main.CompletionFoo.test5, Tuple{Array{Bool,1}, Vararg})) end let s = "CompletionFoo.test4(CompletionFoo.test_y_array[1]()[1], CompletionFoo.test_y_array[1]()[2], " c, r, res = test_complete(s) @test !res @test length(c) == 1 - @test c[1] == string(first(methods(Main.CompletionFoo.test4, Tuple{String, String}))) + @test c[1] == first(test_methods_list(Main.CompletionFoo.test4, Tuple{String, String, Vararg})) end # Test that string escaping is handled correct @@ -1611,10 +1615,11 @@ let s = "log(log.(varfloat)," @test !isempty(c) end -let s = "log(log.(noexist)," - c, r = test_complete_foo(s) - @test isempty(c) -end +# TODO: this is a bad test +#let s = "log(log.(noexist)," +# c, r = test_complete_foo(s) +# @test isempty(c) +#end let s = "Base.return_types(getin" c, r = test_complete_foo(s) @@ -1631,9 +1636,10 @@ end let s = "test(1,1, " c, r, res = test_complete_foo(s) @test !res - @test c[1] == string(first(methods(Main.CompletionFoo.test, Tuple{Int, Int}))) - @test c[2] == string(first(methods(Main.CompletionFoo.test, Tuple{}))) # corresponding to the vararg - @test length(c) == 2 + m = test_methods_list(Main.CompletionFoo.test, Tuple{Int, Int, Vararg}) + @test length(m) == 2 == length(c) + @test c[1] == m[1] + @test c[2] == m[2] # In particular, this checks that test(x::Real, y::Real) is not a valid completion # since it is strictly less specific than test(x::T, y::T) where T @test r == 1:4 @@ -1652,7 +1658,7 @@ end let s = "prevind(\"θ\",1," c, r, res = test_complete_foo(s) - @test c[1] == string(first(methods(prevind, Tuple{String, Int}))) + @test c[1] == first(test_methods_list(prevind, Tuple{String, Int, Vararg})) @test r == 1:7 @test s[r] == "prevind" end From 844c20dd63870aa5b369b85038f0523d7d79308a Mon Sep 17 00:00:00 2001 From: Alexander Demin <60229118+sumiya11@users.noreply.github.com> Date: Mon, 10 Apr 2023 20:50:45 +0300 Subject: [PATCH 485/775] Do not use `widen` for 128-bit ints in `MultiplicativeInverses` (#47995) * Do not use widen for UInt128 --------- Co-authored-by: Oscar Smith --- base/multinverses.jl | 25 ++++++++++++++++++++++--- test/numbers.jl | 20 ++++++++++++++++---- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/base/multinverses.jl b/base/multinverses.jl index 1d2eab6e78501..21d8e53d2ff83 100644 --- a/base/multinverses.jl +++ b/base/multinverses.jl @@ -97,7 +97,6 @@ struct UnsignedMultiplicativeInverse{T<:Unsigned} <: MultiplicativeInverse{T} function UnsignedMultiplicativeInverse{T}(d::T) where T<:Unsigned d == 0 && throw(ArgumentError("cannot compute magic for d == $d")) - u2 = convert(T, 2) add = false signedmin = one(d) << (sizeof(d)*8-1) signedmax = signedmin - one(T) @@ -135,13 +134,33 @@ struct UnsignedMultiplicativeInverse{T<:Unsigned} <: MultiplicativeInverse{T} end UnsignedMultiplicativeInverse(x::Unsigned) = UnsignedMultiplicativeInverse{typeof(x)}(x) +# Returns the higher half of the product a*b +function _mul_high(a::T, b::T) where {T<:Union{Signed, Unsigned}} + ((widen(a)*b) >>> (sizeof(a)*8)) % T +end + +function _mul_high(a::UInt128, b::UInt128) + shift = sizeof(a)*4 + mask = typemax(UInt128) >> shift + a1, a2 = a >>> shift, a & mask + b1, b2 = b >>> shift, b & mask + a1b1, a1b2, a2b1, a2b2 = a1*b1, a1*b2, a2*b1, a2*b2 + carry = ((a1b2 & mask) + (a2b1 & mask) + (a2b2 >>> shift)) >>> shift + a1b1 + (a1b2 >>> shift) + (a2b1 >>> shift) + carry +end +function _mul_high(a::Int128, b::Int128) + shift = sizeof(a)*8 - 1 + t1, t2 = (a >> shift) & b % UInt128, (b >> shift) & a % UInt128 + (_mul_high(a % UInt128, b % UInt128) - t1 - t2) % Int128 +end + function div(a::T, b::SignedMultiplicativeInverse{T}) where T - x = ((widen(a)*b.multiplier) >>> (sizeof(a)*8)) % T + x = _mul_high(a, b.multiplier) x += (a*b.addmul) % T ifelse(abs(b.divisor) == 1, a*b.divisor, (signbit(x) + (x >> b.shift)) % T) end function div(a::T, b::UnsignedMultiplicativeInverse{T}) where T - x = ((widen(a)*b.multiplier) >>> (sizeof(a)*8)) % T + x = _mul_high(a, b.multiplier) x = ifelse(b.add, convert(T, convert(T, (convert(T, a - x) >>> 1)) + x), x) ifelse(b.divisor == 1, a, x >>> b.shift) end diff --git a/test/numbers.jl b/test/numbers.jl index 1505725a7f4cf..e41d88243d014 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -2488,14 +2488,26 @@ Base.abs(x::TestNumber) = TestNumber(abs(x.inner)) end end testmi(-1000:1000, -100:100) - testmi(typemax(Int)-1000:typemax(Int), -100:100) - testmi(typemin(Int)+1:typemin(Int)+1000, -100:100) @test_throws ArgumentError Base.multiplicativeinverse(0) - testmi(map(UInt32, 0:1000), map(UInt32, 1:100)) - testmi(typemax(UInt32)-UInt32(1000):typemax(UInt32), map(UInt32, 1:100)) + for T in [Int8, Int16, Int32, Int64, Int128] + testmi(map(T, typemin(T)+1:typemin(T)+100), map(T, -50:50)) + end + for T in [UInt8, UInt16, UInt32, UInt64, UInt128, Int8, Int16, Int32, Int64, Int128] + testmi(map(T, typemax(T)-50:typemax(T)), map(T, 1:50)) + testmi(filter(!iszero, rand(T, 50)), filter(!iszero, rand(T, 50))) + @test_throws ArgumentError Base.multiplicativeinverse(T(0)) + end + + # Division overflow is not handled + T = Int8 + fastd = Base.multiplicativeinverse(T(-1)) + @test_throws DivideError div(typemin(T), T(-1)) + # does not throw: + # @test_throws div(typemin(T), fastd) # test broadcasting works. @test div.(3, Base.multiplicativeinverse(3)) == 1 end + @testset "ndims/indices/size/length" begin @test ndims(1) == 0 @test ndims(Integer) == 0 From 4ced71adf75060fb1f5a6f6dc930b30ab1fb8833 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Mon, 10 Apr 2023 11:54:34 -0700 Subject: [PATCH 486/775] Bump to LBT v5.7.0 (#49281) This release of libblastrampoline contains two new features: ILP64 Accelerate support, and LAPACK_jll support. --- deps/blastrampoline.version | 6 +-- deps/checksums/blastrampoline | 64 +++++++++++------------ stdlib/LinearAlgebra/src/lbt.jl | 10 +++- stdlib/libblastrampoline_jll/Project.toml | 4 +- 4 files changed, 45 insertions(+), 39 deletions(-) diff --git a/deps/blastrampoline.version b/deps/blastrampoline.version index 2ab10915a73a1..faf2ecd0229c1 100644 --- a/deps/blastrampoline.version +++ b/deps/blastrampoline.version @@ -2,6 +2,6 @@ BLASTRAMPOLINE_JLL_NAME := libblastrampoline ## source build -BLASTRAMPOLINE_VER := 5.4.0 -BLASTRAMPOLINE_BRANCH=v5.4.0 -BLASTRAMPOLINE_SHA1=d00e6ca235bb747faae4c9f3a297016cae6959ed +BLASTRAMPOLINE_VER := 5.7.0 +BLASTRAMPOLINE_BRANCH=v5.7.0 +BLASTRAMPOLINE_SHA1=2272604bfb10b9e8a3ae5f1a4569899b99251a65 diff --git a/deps/checksums/blastrampoline b/deps/checksums/blastrampoline index 786085c82769f..38cb44236eb87 100644 --- a/deps/checksums/blastrampoline +++ b/deps/checksums/blastrampoline @@ -1,34 +1,34 @@ blastrampoline-d00e6ca235bb747faae4c9f3a297016cae6959ed.tar.gz/md5/b49ebb89b7f9a1eaf85217c4a9dac744 blastrampoline-d00e6ca235bb747faae4c9f3a297016cae6959ed.tar.gz/sha512/ac3a767fdb03cc0a9e12ae6df31229e6c5050f2b7ccaee47ef14d6bef34b37a20c2d79956b73bf74d72af1f01a3d1316931db264e1b00cb6cadd57fb842e6f2f -libblastrampoline.v5.4.0+0.aarch64-apple-darwin.tar.gz/md5/9c084085ecf2f263164ab3557db634b7 -libblastrampoline.v5.4.0+0.aarch64-apple-darwin.tar.gz/sha512/c8233325dc71582efe43a741c7e8348e853e02d77cc1296261abf12027008e1b79ec369575638c775944ae4ce9cc9d5d999e0994b2b2c7ceccd956f1c49d8f75 -libblastrampoline.v5.4.0+0.aarch64-linux-gnu.tar.gz/md5/6bdce10e27dfcd219d6bd212ade361dd -libblastrampoline.v5.4.0+0.aarch64-linux-gnu.tar.gz/sha512/003a5afbc5f92ec5da518fc33f819b6c063946f75aac347775582266138a0cbf22839e0f4f5b13909185e8a2643d51db434d0d325d2898980386d8c24acfd8e7 -libblastrampoline.v5.4.0+0.aarch64-linux-musl.tar.gz/md5/048ff56f538d56f5cc2ba72c751a1bfc -libblastrampoline.v5.4.0+0.aarch64-linux-musl.tar.gz/sha512/0fdef61ee05c77722e661c522341531eeb3882e76ae2ce1add53fea813a19b70f1cd50a75643c3324aade594dfd7f5b269f43be58e4ef3f560340f9fe95cdd11 -libblastrampoline.v5.4.0+0.armv6l-linux-gnueabihf.tar.gz/md5/332f6857be4f7840bbb03a78fe5b50d4 -libblastrampoline.v5.4.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/228a9b5fe1ef57c0ac4d3130de8bce184baac702c9df02fa4706558c23973ec8396db39d0d0125638bd330065527c6fe1c205e3a095b401c27900c21e941d1c3 -libblastrampoline.v5.4.0+0.armv6l-linux-musleabihf.tar.gz/md5/5f7008ccf0155c164bf8eec5a184be1d -libblastrampoline.v5.4.0+0.armv6l-linux-musleabihf.tar.gz/sha512/0395ea3aec6ba4f4e3ce56e152a7d3db78b937a8bee603ed84143c3f35b76453ec3650c733ffd79a3b59424f5196218b33a45939ea176e8666cf4d44593e35be -libblastrampoline.v5.4.0+0.armv7l-linux-gnueabihf.tar.gz/md5/f184171d5ce4fa9238e11478f54ad6c9 -libblastrampoline.v5.4.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/3e4406f2bb09dfa17b926a83246c45107bfd72776f3d22320985c3f2c58cdab78c674d759e74bd2725e04c7e78263acfc47879598db7181660145a88af5e11af -libblastrampoline.v5.4.0+0.armv7l-linux-musleabihf.tar.gz/md5/c6996b382b042c87f714866bb1d2ce37 -libblastrampoline.v5.4.0+0.armv7l-linux-musleabihf.tar.gz/sha512/e5c69979743f228ed61931b5e1f8130c832f925a155f04de751ae817c1865d759999bfcfd0d2646ee192de3dba0a8d25626f70be7abd83d4a07c11988c6fd57c -libblastrampoline.v5.4.0+0.i686-linux-gnu.tar.gz/md5/155937c2f2e9650654d93210e82e5b9e -libblastrampoline.v5.4.0+0.i686-linux-gnu.tar.gz/sha512/e7e33da75b5076ac7fbdf1f882cc77244b861f5265bcb4f7aec28e578ed5af00d08f40513fa17dd62d15e7e911a948047b45f32e31f062eb4ef07bee4ce02010 -libblastrampoline.v5.4.0+0.i686-linux-musl.tar.gz/md5/206d874fbc0a9590390c5476edfc877d -libblastrampoline.v5.4.0+0.i686-linux-musl.tar.gz/sha512/6f6dd3468f788d717b0ee58b189172950892a84e7379379863ea9d5b316901084fcaa325b8fe7c472d16f08552aa5ab89ccafefa30c05a362ffb44330f1ec383 -libblastrampoline.v5.4.0+0.i686-w64-mingw32.tar.gz/md5/9adc6d8cd38f9151feb13b21a28aeb7b -libblastrampoline.v5.4.0+0.i686-w64-mingw32.tar.gz/sha512/13f7a6f14b0dc7db29591d6d9bbd3e41e72b4a079105987540d3393203ed487ebce32d21569c3724df29332006fc32d915e54055f99ecc74829717ca11bcafdf -libblastrampoline.v5.4.0+0.powerpc64le-linux-gnu.tar.gz/md5/e9dfb0f5a0e564231a75b3fc8a44bc91 -libblastrampoline.v5.4.0+0.powerpc64le-linux-gnu.tar.gz/sha512/fb4c1f953728acf6db4a6a2e93bc5ed8242285cd3112ba1921432bef045b03a375813c34c0d071d19508c226669774afe640acd7d85b10de5176d864eee5f73c -libblastrampoline.v5.4.0+0.x86_64-apple-darwin.tar.gz/md5/c092da8bc56af60cbd4afe5565c471c5 -libblastrampoline.v5.4.0+0.x86_64-apple-darwin.tar.gz/sha512/3fe0aafcdc51c5f2414f889a4f0970b0e302f4d1f37b36bedd094202ae9b7ea760607ca4f80aa815ca2346f526202ef932cd7d3f43522fc4a823c3db6b41604d -libblastrampoline.v5.4.0+0.x86_64-linux-gnu.tar.gz/md5/e05d2295208649a55620681241f9a6fc -libblastrampoline.v5.4.0+0.x86_64-linux-gnu.tar.gz/sha512/2bde6e6b80eb80dd78967dcf6d946b2397b3129b7c6de6fbab2168c23293770ad3d2bbc269c403ee26ea6d752b91eee87e1c651bd7f451f62a8a2acd68196db7 -libblastrampoline.v5.4.0+0.x86_64-linux-musl.tar.gz/md5/4b374750eb2d42a55a39d28cdee70d6b -libblastrampoline.v5.4.0+0.x86_64-linux-musl.tar.gz/sha512/314d877497462d521fafc92299f1e387a03193c20050da529f3e3d02da9f55063f45883377288750d7b8cc64d8701c94db79798a7ef298a73051cd51f21104be -libblastrampoline.v5.4.0+0.x86_64-unknown-freebsd.tar.gz/md5/b5549fb2b1ed82ab95b0636a1eb7682e -libblastrampoline.v5.4.0+0.x86_64-unknown-freebsd.tar.gz/sha512/b94975cef6c1ea26e7635bc70e51a4c53ad1c4610322d0c15841ccfb7e996c8e55b5f060a5ab318d6dda4cfdb615d9c77848cb13bd71c03df8c90c6ac717ff0e -libblastrampoline.v5.4.0+0.x86_64-w64-mingw32.tar.gz/md5/00bd607714c91a2cbc5e2a2f87e6d5e1 -libblastrampoline.v5.4.0+0.x86_64-w64-mingw32.tar.gz/sha512/e75a3780f65963e6a6baf68af57d7260b57052770d6ac3608971134b449d33d02a0be6f0edd0cddae1645ccb0faf6c744ecc3ff40cf7bcfed8acbf05f756013c +libblastrampoline.v5.7.0+0.aarch64-apple-darwin.tar.gz/md5/a28837b9838fef2b3831de3278ec7949 +libblastrampoline.v5.7.0+0.aarch64-apple-darwin.tar.gz/sha512/111ac2fe5f8f8102f2f7c9e9e6aa1d1a12d2db941238c949ff8e64b30335e8b2f6ecce0d5f577879c231eb839c06e259302b709f3d34e94a97047bfa984222f6 +libblastrampoline.v5.7.0+0.aarch64-linux-gnu.tar.gz/md5/3792c0a4443c0e2ebe88bea180a3beb1 +libblastrampoline.v5.7.0+0.aarch64-linux-gnu.tar.gz/sha512/d5ce0f9bb47f80d04e13bf90e48d6ab942cf7fd79b582f1496a83a1eca0db29e01315efa52bcb1099466a9037926a7a2cf3135395ac1322544cd7150b9687d7b +libblastrampoline.v5.7.0+0.aarch64-linux-musl.tar.gz/md5/45622c514e744282f996bacc26ca231b +libblastrampoline.v5.7.0+0.aarch64-linux-musl.tar.gz/sha512/790f7cf4d5f11be246c617694a7d617435627229f52c52ee49037c3650707ac1c0d8631b713d5b32ac76f97b19b18eacc1645fd0aecc296167439bfbb0514b5c +libblastrampoline.v5.7.0+0.armv6l-linux-gnueabihf.tar.gz/md5/f945fe9ee8472db8fe27409b7c028a57 +libblastrampoline.v5.7.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/96876374813acc9ae249ef3c7140e1e0eac9259a0b5c567e11843a17a9d5db4fe759e873d0e2c1dd944e9f9104b885a09cd8977634eaa7c24e580d391f23b75f +libblastrampoline.v5.7.0+0.armv6l-linux-musleabihf.tar.gz/md5/902b8ae755be3087002969377b0332d8 +libblastrampoline.v5.7.0+0.armv6l-linux-musleabihf.tar.gz/sha512/bda692349136d1b540e00458ba9ed689e762e9acdc60d0cc59203cefcdcc59611f3883f5050bf9186019003be726f3d798138dcf5929a64a4d1fab314c84e7a4 +libblastrampoline.v5.7.0+0.armv7l-linux-gnueabihf.tar.gz/md5/95058aaed39a095b6f50b04b335b1ff6 +libblastrampoline.v5.7.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/bb963ac3d9fd57e656e33177c01ae6cd0cfbe63826f79cd3ae04c38641c5a82fe9cae29a38f1a725ca11d93dd143aeb6a5213f26ceedb622f9ebb5c156b9ceac +libblastrampoline.v5.7.0+0.armv7l-linux-musleabihf.tar.gz/md5/f66178bcb8e159e7b022c54a917cd895 +libblastrampoline.v5.7.0+0.armv7l-linux-musleabihf.tar.gz/sha512/c92b0fe3e3f486d2f559d5f34fdcf59bf4db764b28dc1146a2559f236de48057832ba1b766d69bd5ffe91f6b0d13238e6ee3eb70d4508947b3235672cba60d6f +libblastrampoline.v5.7.0+0.i686-linux-gnu.tar.gz/md5/33bb177a7928a80ef888b51ff39eb724 +libblastrampoline.v5.7.0+0.i686-linux-gnu.tar.gz/sha512/c63c1511213ca89a540db2b7e8cbfae52e6433292b59a7f652ed28f2ad01603ece480ab3c3bb8860598def5c3a994dd0132fc0bf7f070efd458e7e9bbebb1118 +libblastrampoline.v5.7.0+0.i686-linux-musl.tar.gz/md5/65472af1f7b69695470c65eea92e237d +libblastrampoline.v5.7.0+0.i686-linux-musl.tar.gz/sha512/6e16059c68e19447aa6b37854ce7200ae7c7ecd4f016c5fcd120d4ad960377bd4b86ace7470277b785b61d68a915b5476568d876ea510b50a8bb7146a33ee431 +libblastrampoline.v5.7.0+0.i686-w64-mingw32.tar.gz/md5/0a945591dda93017836cdcc87095863a +libblastrampoline.v5.7.0+0.i686-w64-mingw32.tar.gz/sha512/26376cefbe8891907a2f6ee506edc9cb95289df52eaad3ac39135eade5486879da5733019d437dcfba6c286007a29a43b2aabdcc436db893910f6b2730517f12 +libblastrampoline.v5.7.0+0.powerpc64le-linux-gnu.tar.gz/md5/2d29aff294807c0b857adee62dbce7f5 +libblastrampoline.v5.7.0+0.powerpc64le-linux-gnu.tar.gz/sha512/414cc2971bbc7e635b7d1d68aa545ff23568dd7722b8fdd990439c0c55e4dc63420afaf191633fbcf54205fc11edb01fa7d5d4a712b8951775dcdd500f135231 +libblastrampoline.v5.7.0+0.x86_64-apple-darwin.tar.gz/md5/21beb51d448bd22e4608a16b3f4fde05 +libblastrampoline.v5.7.0+0.x86_64-apple-darwin.tar.gz/sha512/620ba64d93ef416e483f813617aa313957282d8361f920b5444702fa911ff0051d1f8a8814b5fa0b082fd4dc77d96cb8b763937c786959bbc97cbb6131617152 +libblastrampoline.v5.7.0+0.x86_64-linux-gnu.tar.gz/md5/43a9eb58e79131d9a8594a42c5b15c5f +libblastrampoline.v5.7.0+0.x86_64-linux-gnu.tar.gz/sha512/5b8dddd742a7742eef14025a859251d605b34a61de3e07ff696c168a88462602c35c5b3680da072e28a8bedc89df5b5ae622be61a5b0203000f9439558d423d9 +libblastrampoline.v5.7.0+0.x86_64-linux-musl.tar.gz/md5/5790c157871d03fcb8c14063303bfcf8 +libblastrampoline.v5.7.0+0.x86_64-linux-musl.tar.gz/sha512/8f82de757b66559cdcd8127946b50c1c5b479149966d53881bdae7c7b536a7c79a1b331d04425aeb57d47fabeb2946985edab23cc94e049240e1925320f03795 +libblastrampoline.v5.7.0+0.x86_64-unknown-freebsd.tar.gz/md5/bb676e6bc64ed1be85f95443e4366eca +libblastrampoline.v5.7.0+0.x86_64-unknown-freebsd.tar.gz/sha512/77ba2fb295f2765cd64ca7d00ee92230c467e056b1441eea63470dcb3491481174956b7058950a7fc2d7dad4b245283dca907b2dea96c8fe1b0e27e4375c0be4 +libblastrampoline.v5.7.0+0.x86_64-w64-mingw32.tar.gz/md5/58ab9512505a6b3eb2f7f2f2bf9a55cf +libblastrampoline.v5.7.0+0.x86_64-w64-mingw32.tar.gz/sha512/a58a7be8ef3ea958591c1cc9d9192efa08bb2aeb0de4cbd1e3f23ea57aa2b8f6048ab4db6bce4f9724a9f81f0e0356e66b884763ead5b309cb38bab9ea475e7f diff --git a/stdlib/LinearAlgebra/src/lbt.jl b/stdlib/LinearAlgebra/src/lbt.jl index be3f880ae2093..b133741611adc 100644 --- a/stdlib/LinearAlgebra/src/lbt.jl +++ b/stdlib/LinearAlgebra/src/lbt.jl @@ -83,11 +83,17 @@ struct lbt_config_t exported_symbols::Ptr{Cstring} num_exported_symbols::UInt32 end -const LBT_BUILDFLAGS_DEEPBINDLESS = 0x01 -const LBT_BUILDFLAGS_F2C_CAPABLE = 0x02 +const LBT_BUILDFLAGS_DEEPBINDLESS = 0x01 +const LBT_BUILDFLAGS_F2C_CAPABLE = 0x02 +const LBT_BUILDFLAGS_CBLAS_DIVERGENCE = 0x04 +const LBT_BUILDFLAGS_COMPLEX_RETSTYLE = 0x08 +const LBT_BUILDFLAGS_SYMBOL_TRIMMING = 0x10 const LBT_BUILDFLAGS_MAP = Dict( LBT_BUILDFLAGS_DEEPBINDLESS => :deepbindless, LBT_BUILDFLAGS_F2C_CAPABLE => :f2c_capable, + LBT_BUILDFLAGS_CBLAS_DIVERGENCE => :cblas_divergence, + LBT_BUILDFLAGS_COMPLEX_RETSTYLE => :complex_retstyle, + LBT_BUILDFLAGS_SYMBOL_TRIMMING => :symbol_trimming, ) struct LBTConfig diff --git a/stdlib/libblastrampoline_jll/Project.toml b/stdlib/libblastrampoline_jll/Project.toml index 08ba2e266512d..42d49010019c3 100644 --- a/stdlib/libblastrampoline_jll/Project.toml +++ b/stdlib/libblastrampoline_jll/Project.toml @@ -1,13 +1,13 @@ name = "libblastrampoline_jll" uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.4.0+0" +version = "5.7.0+0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" [compat] -julia = "1.8" +julia = "1.10" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" From b96ec2cc46804cbe2792240f7f233f83db206919 Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Mon, 10 Apr 2023 22:51:49 +0200 Subject: [PATCH 487/775] Docs: `julia_cmd`: Document use of `julia_cmd()[1]` (#48425) --- base/util.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/base/util.jl b/base/util.jl index bce768c61f335..6f424f80d13b6 100644 --- a/base/util.jl +++ b/base/util.jl @@ -154,6 +154,8 @@ command line arguments that are not at their default values. Among others, `--math-mode`, `--warn-overwrite`, and `--trace-compile` are notably not propagated currently. +To get the julia command without propagated command line arguments, `julia_cmd()[1]` can be used. + !!! compat "Julia 1.1" Only the `--cpu-target`, `--sysimage`, `--depwarn`, `--compile` and `--check-bounds` flags were propagated before Julia 1.1. From 99938fc8c569cbdb0614c9ef503f2bbbc027c00b Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Tue, 21 Feb 2023 18:28:20 +0000 Subject: [PATCH 488/775] Resolve artifact ambiguities using shortest match This changes the behavior of `select_platform()` to resolve ambiguities using a two-step process. Previously, we would simply sort the resultant triplets and return the last one (so as to provide asemi- arbitrary stability, and also to prefer higher version numbers over lower version numbers in tags). However, with the proliferation of tags (and especially the new `sanitize=memory` tags) we need a way to exclude these "extra" tags when our `HostPlatform()` does not have them. This new matching algorithm excludes candidates from matching with the platform if there are other candidates with fewer total tags. This results in a "simplest match first" behavior, which better represents the intent of tags in the first place. --- base/binaryplatforms.jl | 20 ++++++++++++++------ test/binaryplatforms.jl | 17 ++++++++++++++--- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/base/binaryplatforms.jl b/base/binaryplatforms.jl index 39348891c83a6..d59b6397b1f73 100644 --- a/base/binaryplatforms.jl +++ b/base/binaryplatforms.jl @@ -1067,12 +1067,20 @@ function select_platform(download_info::Dict, platform::AbstractPlatform = HostP return nothing end - # At this point, we may have multiple possibilities. E.g. if, in the future, - # Julia can be built without a direct dependency on libgfortran, we may match - # multiple tarballs that vary only within their libgfortran ABI. To narrow it - # down, we just sort by triplet, then pick the last one. This has the effect - # of generally choosing the latest release (e.g. a `libgfortran5` tarball - # rather than a `libgfortran3` tarball) + # At this point, we may have multiple possibilities. We now engage a multi- + # stage selection algorithm, where we first choose simpler matches over more + # complex matches. We define a simpler match as one that has fewer tags + # overall. As these candidate matches have already been filtered to match + # the given platform, the only other tags that exist are ones that are in + # addition to the tags declared by the platform. Hence, selecting the + # minimum in number of tags is equivalent to selecting the closest match. + min_tag_count = minimum(length(tags(p)) for p in ps) + filter!(p -> length(tags(p)) == min_tag_count, ps) + + # Now we _still_ may continue to have multiple matches, so we now simply sort + # the candidate matches by their triplets and take the last one, so as to + # generally choose the latest release (e.g. a `libgfortran5` tarball over a + # `libgfortran3` tarball). p = last(sort(ps, by = p -> triplet(p))) return download_info[p] end diff --git a/test/binaryplatforms.jl b/test/binaryplatforms.jl index 793a9b1f06a41..54154f492168f 100644 --- a/test/binaryplatforms.jl +++ b/test/binaryplatforms.jl @@ -315,8 +315,9 @@ end P("x86_64", "linux"; libgfortran_version=v"5") => "linux8", # Ambiguity test - P("aarch64", "linux"; libgfortran_version=v"3") => "linux4", + P("aarch64", "linux"; libgfortran_version=v"3") => "linux3", P("aarch64", "linux"; libgfortran_version=v"3", libstdcxx_version=v"3.4.18") => "linux5", + P("aarch64", "linux"; libgfortran_version=v"3", libstdcxx_version=v"3.4.18", foo="bar") => "linux9", # OS test P("x86_64", "macos"; libgfortran_version=v"3") => "mac4", @@ -327,8 +328,10 @@ end @test select_platform(platforms, P("x86_64", "linux"; libgfortran_version=v"4")) == "linux7" # Ambiguity test - @test select_platform(platforms, P("aarch64", "linux")) == "linux5" - @test select_platform(platforms, P("aarch64", "linux"; libgfortran_version=v"3")) == "linux5" + @test select_platform(platforms, P("aarch64", "linux")) == "linux3" + @test select_platform(platforms, P("aarch64", "linux"; libgfortran_version=v"3")) == "linux3" + # This one may be surprising, but we still match `linux3`, and since linux3 is shorter, we choose it. + @test select_platform(platforms, P("aarch64", "linux"; libgfortran_version=v"3", libstdcxx_version=v"3.4.18")) === "linux3" @test select_platform(platforms, P("aarch64", "linux"; libgfortran_version=v"4")) === nothing @test select_platform(platforms, P("x86_64", "macos")) == "mac4" @@ -339,6 +342,14 @@ end # Sorry, Alex. ;) @test select_platform(platforms, P("x86_64", "freebsd")) === nothing + + # The new "prefer shortest matching" algorithm is meant to be used to resolve ambiguities such as the following: + platforms = Dict( + # Typical binning test + P("x86_64", "linux") => "good", + P("x86_64", "linux"; sanitize="memory") => "bad", + ) + @test select_platform(platforms, P("x86_64", "linux")) == "good" end @testset "Custom comparators" begin From d599c405f655b1f659cd6577cdb035a362f6f357 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Mon, 10 Apr 2023 19:35:16 -0400 Subject: [PATCH 489/775] irshow: Be more robust in the face of invalid :invoke Exprs (#49312) In general, printing should take all reasonable steps to show even invalid data structures, since printing is used for debugging. In this case, the :invoke printing had some type asserts and minimum arg length constraints. Check those at the top and fall back to regular `Expr` printing if they are not met. --- base/compiler/ssair/show.jl | 2 +- test/show.jl | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index f6fac22886046..44baae392fa91 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -48,7 +48,7 @@ function print_stmt(io::IO, idx::Int, @nospecialize(stmt), used::BitSet, maxleng print(io, ", ") print(io, stmt.typ) print(io, ")") - elseif isexpr(stmt, :invoke) + elseif isexpr(stmt, :invoke) && length(stmt.args) >= 2 && isa(stmt.args[1], MethodInstance) stmt = stmt::Expr # TODO: why is this here, and not in Base.show_unquoted print(io, "invoke ") diff --git a/test/show.jl b/test/show.jl index 651391bf9e729..5a9a8a28cca62 100644 --- a/test/show.jl +++ b/test/show.jl @@ -2069,6 +2069,13 @@ let src = code_typed(my_fun28173, (Int,), debuginfo=:source)[1][1] Base.IRShow.show_ir(io, ir, Base.IRShow.default_config(ir; verbose_linetable=true)) seekstart(io) @test count(contains(r"@ a{80}:\d+ within `my_fun28173"), eachline(io)) == 10 + + # Test that a bad :invoke doesn't cause an error during printing + Core.Compiler.insert_node!(ir, 1, Core.Compiler.NewInstruction(Expr(:invoke, nothing, sin), Any), false) + io = IOBuffer() + Base.IRShow.show_ir(io, ir) + seekstart(io) + @test contains(String(take!(io)), "Expr(:invoke, nothing") end # Verify that extra instructions at the end of the IR From 8fbef6e3fc7b3b8a95dc44d0a0181d967219ddd5 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 11 Apr 2023 12:27:18 +0900 Subject: [PATCH 490/775] delete unnecessary `getindex` method (#49310) xref: --- base/array.jl | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/base/array.jl b/base/array.jl index 752ca4cc641dd..60b1304f20ee0 100644 --- a/base/array.jl +++ b/base/array.jl @@ -453,17 +453,6 @@ function getindex(::Type{T}, vals...) where T return a end -# safe version -function getindex(::Type{T}, vals::T...) where T - @inline - @_effect_free_terminates_locally_meta - a = Vector{T}(undef, length(vals)) - @_safeindex for i in 1:length(vals) - a[i] = vals[i] - end - return a -end - function getindex(::Type{Any}, @nospecialize vals...) @_effect_free_terminates_locally_meta a = Vector{Any}(undef, length(vals)) From 1aa65c33f24e7a6c7a341ce4a13202bfb6ac4811 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Tue, 11 Apr 2023 11:40:40 -0400 Subject: [PATCH 491/775] when `x` is `NaN` in trig functions return `x` rather than `NaN` (#49285) * when x is NaN in trig functions return x rather than NaN * prefer `isnan(x) | isnan(y)` --------- Co-authored-by: mikmoore <95002244+mikmoore@users.noreply.github.com> --- base/special/hyperbolic.jl | 2 +- base/special/trig.jl | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/base/special/hyperbolic.jl b/base/special/hyperbolic.jl index 74f750064c7c2..333951b6f6024 100644 --- a/base/special/hyperbolic.jl +++ b/base/special/hyperbolic.jl @@ -175,7 +175,7 @@ function asinh(x::T) where T <: Union{Float32, Float64} # return sign(x)*log(2|x|+1/(|x|+sqrt(x*x+1))) # d) |x| >= 2^28 # return sign(x)*(log(x)+ln2)) - if isnan(x) || isinf(x) + if !isfinite(x) return x end absx = abs(x) diff --git a/base/special/trig.jl b/base/special/trig.jl index 6dae3ed351503..5b2a23688ca6b 100644 --- a/base/special/trig.jl +++ b/base/special/trig.jl @@ -34,7 +34,7 @@ function sin(x::T) where T<:Union{Float32, Float64} end return sin_kernel(x) elseif isnan(x) - return T(NaN) + return x elseif isinf(x) sin_domain_error(x) end @@ -103,7 +103,7 @@ function cos(x::T) where T<:Union{Float32, Float64} end return cos_kernel(x) elseif isnan(x) - return T(NaN) + return x elseif isinf(x) cos_domain_error(x) else @@ -179,7 +179,7 @@ function sincos(x::T) where T<:Union{Float32, Float64} end return sincos_kernel(x) elseif isnan(x) - return T(NaN), T(NaN) + return x, x elseif isinf(x) sincos_domain_error(x) end @@ -221,7 +221,7 @@ function tan(x::T) where T<:Union{Float32, Float64} end return tan_kernel(x) elseif isnan(x) - return T(NaN) + return x elseif isinf(x) tan_domain_error(x) end @@ -582,8 +582,8 @@ function atan(y::T, x::T) where T<:Union{Float32, Float64} # S8) ATAN2(+-INF,+INF ) is +-pi/4 ; # S9) ATAN2(+-INF,-INF ) is +-3pi/4; # S10) ATAN2(+-INF, (anything but,0,NaN, and INF)) is +-pi/2; - if isnan(x) || isnan(y) # S1 or S2 - return T(NaN) + if isnan(x) | isnan(y) # S1 or S2 + return isnan(x) ? x : y end if x == T(1.0) # then y/x = y and x > 0, see M2 @@ -1191,7 +1191,7 @@ function sind(x::Real) if isinf(x) return throw(DomainError(x, "`x` cannot be infinite.")) elseif isnan(x) - return oftype(x,NaN) + return x end rx = copysign(float(rem(x,360)),x) @@ -1222,7 +1222,7 @@ function cosd(x::Real) if isinf(x) return throw(DomainError(x, "`x` cannot be infinite.")) elseif isnan(x) - return oftype(x,NaN) + return x end rx = abs(float(rem(x,360))) From d86285dba8b0e30faab777fde09734d1748647c2 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Mon, 27 Feb 2023 14:08:12 +0530 Subject: [PATCH 492/775] fix: return true while either generating the output "or" using pkgimages --- src/jitlayers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jitlayers.h b/src/jitlayers.h index 1b62c87910a7f..cf160843710f5 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -73,7 +73,7 @@ GlobalVariable *jl_emit_RTLD_DEFAULT_var(Module *M) JL_NOTSAFEPOINT; DataLayout jl_create_datalayout(TargetMachine &TM) JL_NOTSAFEPOINT; static inline bool imaging_default() JL_NOTSAFEPOINT { - return jl_options.image_codegen || (jl_generating_output() && jl_options.use_pkgimages); + return jl_options.image_codegen || jl_generating_output() || jl_options.use_pkgimages; } struct OptimizationOptions { From 4f4842c8896c774cd1ac5adbfea51c3a04671fa3 Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Tue, 28 Feb 2023 15:26:50 +0530 Subject: [PATCH 493/775] test: pkgimages and object files with multiple cpu targets --- test/cmdlineargs.jl | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index eafcb4ebe89d0..64d06d080659d 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -726,6 +726,42 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` end end +let exename = `$(Base.julia_cmd(; cpu_target="native;native")) --startup-file=no --color=no` + # --pkgimages with multiple cpu targets + @testset let v = readchomperrors(`$exename --pkgimages=no`) + @test !v[1] + @test isempty(v[2]) + @test v[3] == "ERROR: More than one command line CPU targets specified without a `--output-` flag specified" + end + + @test readchomp(`$exename --pkgimages=yes -e ' + println("cpu_target = $(unsafe_string(Base.JLOptions().cpu_target)) and use_pkgimages = $(Base.JLOptions().use_pkgimages)")'`) == + "cpu_target = native;native and use_pkgimages = 1" +end + +# Object file with multiple cpu targets +@testset "Object file for multiple microarchitectures" begin + julia_path = joinpath(Sys.BINDIR, Base.julia_exename()) + outputo_file = tempname() + write(outputo_file, "1") + object_file = tempname() * ".o" + + # This is to test that even with `pkgimages=no`, we can create object file + # with multiple cpu-targets + # The cmd is checked for `--object-o` as soon as it is run. So, to avoid long + # testing times, intentionally don't pass `--sysimage`; when we reach the + # corresponding error, we know that `check_cmdline` has already passed + let v = readchomperrors(`$julia_path + --cpu-target='native;native' + --output-o=$object_file $outputo_file + --pkgimages=no`) + + @test v[1] == false + @test v[2] == "" + @test !contains(v[3], "More than one command line CPU targets specified") + @test v[3] == "ERROR: File \"boot.jl\" not found" + end +end # Find the path of libjulia (or libjulia-debug, as the case may be) # to use as a dummy shlib to open From 3fa1bcbd7854647f6b753b649ce750fd429a3efc Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Tue, 11 Apr 2023 15:41:56 +0000 Subject: [PATCH 494/775] Incorporate review feedback --- src/jitlayers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jitlayers.h b/src/jitlayers.h index cf160843710f5..d8c06df44176f 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -73,7 +73,7 @@ GlobalVariable *jl_emit_RTLD_DEFAULT_var(Module *M) JL_NOTSAFEPOINT; DataLayout jl_create_datalayout(TargetMachine &TM) JL_NOTSAFEPOINT; static inline bool imaging_default() JL_NOTSAFEPOINT { - return jl_options.image_codegen || jl_generating_output() || jl_options.use_pkgimages; + return jl_options.image_codegen || (jl_generating_output() && (!jl_options.incremental || jl_options.use_pkgimages)); } struct OptimizationOptions { From 5917a0115fea55197662b2037ac56aecf8ebfe92 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 21 Mar 2023 10:50:18 -0400 Subject: [PATCH 495/775] skip function when splitting typemap It is always the same value, so it just wastes a little bit of memory and time to inspect this. --- src/gf.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/gf.c b/src/gf.c index 85c9766587f37..2555d92dcf2c6 100644 --- a/src/gf.c +++ b/src/gf.c @@ -246,7 +246,7 @@ JL_DLLEXPORT jl_value_t *jl_methtable_lookup(jl_methtable_t *mt, jl_value_t *typ // jl_typemap_assoc_by_type with subtype=0), while normally jl_gf_invoke_lookup would be // expected to be used instead struct jl_typemap_assoc search = {type, world, NULL, 0, ~(size_t)0}; - jl_typemap_entry_t *sf = jl_typemap_assoc_by_type(jl_atomic_load_relaxed(&mt->defs), &search, /*offs*/0, /*subtype*/0); + jl_typemap_entry_t *sf = jl_typemap_assoc_by_type(jl_atomic_load_relaxed(&mt->defs), &search, jl_cachearg_offset(mt), /*subtype*/0); if (!sf) return jl_nothing; return sf->func.value; @@ -286,7 +286,7 @@ jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_a newentry = jl_typemap_alloc(jl_anytuple_type, NULL, jl_emptysvec, (jl_value_t*)m, 1, ~(size_t)0); - jl_typemap_insert(&mt->defs, (jl_value_t*)mt, newentry, 0); + jl_typemap_insert(&mt->defs, (jl_value_t*)mt, newentry, jl_cachearg_offset(mt)); jl_method_instance_t *mi = jl_get_specialized(m, (jl_value_t*)jl_anytuple_type, jl_emptysvec); jl_atomic_store_relaxed(&m->unspecialized, mi); @@ -1459,7 +1459,7 @@ static int get_intersect_visitor(jl_typemap_entry_t *oldentry, struct typemap_in return 1; } -static jl_value_t *get_intersect_matches(jl_typemap_t *defs, jl_typemap_entry_t *newentry, jl_typemap_entry_t **replaced) +static jl_value_t *get_intersect_matches(jl_typemap_t *defs, jl_typemap_entry_t *newentry, jl_typemap_entry_t **replaced, int8_t offs) { jl_tupletype_t *type = newentry->sig; jl_tupletype_t *ttypes = (jl_tupletype_t*)jl_unwrap_unionall((jl_value_t*)type); @@ -1476,7 +1476,7 @@ static jl_value_t *get_intersect_matches(jl_typemap_t *defs, jl_typemap_entry_t /* .ti = */ NULL, /* .env = */ jl_emptysvec, /* .issubty = */ 0}, /* .newentry = */ newentry, /* .shadowed */ NULL, /* .replaced */ NULL}; JL_GC_PUSH3(&env.match.env, &env.match.ti, &env.shadowed); - jl_typemap_intersection_visitor(defs, 0, &env.match); + jl_typemap_intersection_visitor(defs, offs, &env.match); env.match.env = NULL; env.match.ti = NULL; *replaced = env.replaced; @@ -1942,10 +1942,10 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method // add our new entry newentry = jl_typemap_alloc((jl_tupletype_t*)type, simpletype, jl_emptysvec, (jl_value_t*)method, method->primary_world, method->deleted_world); - jl_typemap_insert(&mt->defs, (jl_value_t*)mt, newentry, 0); + jl_typemap_insert(&mt->defs, (jl_value_t*)mt, newentry, jl_cachearg_offset(mt)); jl_typemap_entry_t *replaced = NULL; // then check what entries we replaced - oldvalue = get_intersect_matches(jl_atomic_load_relaxed(&mt->defs), newentry, &replaced); + oldvalue = get_intersect_matches(jl_atomic_load_relaxed(&mt->defs), newentry, &replaced, jl_cachearg_offset(mt)); int invalidated = 0; if (replaced) { oldvalue = (jl_value_t*)replaced; @@ -3220,7 +3220,7 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio static int ml_mtable_visitor(jl_methtable_t *mt, void *env) { - return jl_typemap_intersection_visitor(jl_atomic_load_relaxed(&mt->defs), 0, (struct typemap_intersection_env*)env); + return jl_typemap_intersection_visitor(jl_atomic_load_relaxed(&mt->defs), jl_cachearg_offset(mt), (struct typemap_intersection_env*)env); } // This is the collect form of calling jl_typemap_intersection_visitor @@ -3318,7 +3318,7 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, return env.t; } } - if (!jl_typemap_intersection_visitor(jl_atomic_load_relaxed(&mt->defs), 0, &env.match)) { + if (!jl_typemap_intersection_visitor(jl_atomic_load_relaxed(&mt->defs), jl_cachearg_offset(mt), &env.match)) { JL_GC_POP(); return jl_nothing; } From 2998c905072d6725213db819700459009269c073 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 21 Mar 2023 10:50:28 -0400 Subject: [PATCH 496/775] split typemap more frequently / aggressively Seems to have very little impact on size now (since we use tables much more heavily now instead), so try bumping this parameter. --- src/typemap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typemap.c b/src/typemap.c index 49fc2277bc23d..bd9fd00f06815 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -9,7 +9,7 @@ #endif #include "julia_assert.h" -#define MAX_METHLIST_COUNT 12 // this can strongly affect the sysimg size and speed! +#define MAX_METHLIST_COUNT 6 // this helps configure the sysimg size and speed. #ifdef __cplusplus extern "C" { From acd51cfd42b7a9a470c6c2bbf985ea5e8a955053 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Tue, 11 Apr 2023 21:15:29 +0000 Subject: [PATCH 497/775] Adjust tests --- test/cmdlineargs.jl | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 64d06d080659d..903f6e0663b5d 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -726,19 +726,6 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` end end -let exename = `$(Base.julia_cmd(; cpu_target="native;native")) --startup-file=no --color=no` - # --pkgimages with multiple cpu targets - @testset let v = readchomperrors(`$exename --pkgimages=no`) - @test !v[1] - @test isempty(v[2]) - @test v[3] == "ERROR: More than one command line CPU targets specified without a `--output-` flag specified" - end - - @test readchomp(`$exename --pkgimages=yes -e ' - println("cpu_target = $(unsafe_string(Base.JLOptions().cpu_target)) and use_pkgimages = $(Base.JLOptions().use_pkgimages)")'`) == - "cpu_target = native;native and use_pkgimages = 1" -end - # Object file with multiple cpu targets @testset "Object file for multiple microarchitectures" begin julia_path = joinpath(Sys.BINDIR, Base.julia_exename()) @@ -761,6 +748,14 @@ end @test !contains(v[3], "More than one command line CPU targets specified") @test v[3] == "ERROR: File \"boot.jl\" not found" end + + # This is to test that with `pkgimages=yes`, multiple CPU targets are parsed. + # We intentionally fail fast due to a lack of an `--output-o` flag. + let v = readchomperrors(`$julia_path --cpu-target='native;native' --pkgimages=yes`) + @test v[1] == false + @test v[2] == "" + @test contains(v[3], "More than one command line CPU targets specified") + end end # Find the path of libjulia (or libjulia-debug, as the case may be) From b4cc5c23308ee0d4742f5bb928a99398bdb98157 Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Wed, 12 Apr 2023 10:57:09 -0300 Subject: [PATCH 498/775] fix in maxthreadid (#49327) --- contrib/generate_precompile.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index f756e0bfb8fee..c99e6c646ec1c 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -1,7 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license if Threads.maxthreadid() != 1 - @warn "Running this file with multiple Julia threads may lead to a build error" Base.maxthreadid() + @warn "Running this file with multiple Julia threads may lead to a build error" Threads.maxthreadid() end if Base.isempty(Base.ARGS) || Base.ARGS[1] !== "0" From dcda267b93142fddf1d345a1cb838f9f5598e836 Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Wed, 12 Apr 2023 16:14:23 +0000 Subject: [PATCH 499/775] Add tests for broadcast vectorization (#49317) --- test/llvmpasses/pipeline-o2-broadcast.jl | 109 +++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 test/llvmpasses/pipeline-o2-broadcast.jl diff --git a/test/llvmpasses/pipeline-o2-broadcast.jl b/test/llvmpasses/pipeline-o2-broadcast.jl new file mode 100644 index 0000000000000..dc44293379284 --- /dev/null +++ b/test/llvmpasses/pipeline-o2-broadcast.jl @@ -0,0 +1,109 @@ +# RUN: julia --startup-file=no -O2 --check-bounds=auto %s %t -O && llvm-link -S %t/* | FileCheck %s +# RUN: julia --startup-file=no -O3 --check-bounds=auto %s %t -O && llvm-link -S %t/* | FileCheck %s + +include(joinpath("..", "testhelpers", "llvmpasses.jl")) + +# COM: Check broadcasted outer product is vectorized + +# COM: Float32 +# CHECK: @japi1_prod_v_vT +# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x float> +# CHECK: fmul <[[VSCALE]][[VEC_FACTOR]] x float> +# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x float> + +# COM: Float64 +# CHECK: @japi1_prod_v_vT +# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x double> +# CHECK: fmul <[[VSCALE]][[VEC_FACTOR]] x double> +# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x double> + +# COM: Int32 +# CHECK: @japi1_prod_v_vT +# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i32> +# CHECK: mul <[[VSCALE]][[VEC_FACTOR]] x i32> +# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x i32> + +# COM: Int64 +# CHECK: @japi1_prod_v_vT +# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i64> +# CHECK: mul <[[VSCALE]][[VEC_FACTOR]] x i64> +# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x i64> + +function prod_v_vT(R, x, y) + R .= x .* y' +end + +# COM: Check broadcasted inner product is vectorized + +# COM: Float32 +# CHECK: @japi1_prod_vT_v +# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x float> +# CHECK: fmul <[[VSCALE]][[VEC_FACTOR]] x float> +# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x float> + +# COM: Float64 +# CHECK: @japi1_prod_vT_v +# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x double> +# CHECK: fmul <[[VSCALE]][[VEC_FACTOR]] x double> +# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x double> + +# COM: Int32 +# CHECK: @japi1_prod_vT_v +# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i32> +# CHECK: mul <[[VSCALE]][[VEC_FACTOR]] x i32> +# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x i32> + +# COM: Int64 +# CHECK: @japi1_prod_vT_v +# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i64> +# CHECK: mul <[[VSCALE]][[VEC_FACTOR]] x i64> +# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x i64> + +function prod_vT_v(R, x, y) + R .= x' .* y +end + +# COM: Check broadcasted multiplications are vectorized + +# COM: Float32 +# CHECK: @japi1_prod_v_M_vT +# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x float> +# CHECK: fmul <[[VSCALE]][[VEC_FACTOR]] x float> +# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x float> + +# COM: Float64 +# CHECK: @japi1_prod_v_M_vT +# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x double> +# CHECK: fmul <[[VSCALE]][[VEC_FACTOR]] x double> +# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x double> + +# COM: Int32 +# CHECK: @japi1_prod_v_M_vT +# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i32> +# CHECK: mul <[[VSCALE]][[VEC_FACTOR]] x i32> +# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x i32> + +# COM: Int64 +# CHECK: @japi1_prod_v_M_vT +# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i64> +# CHECK: mul <[[VSCALE]][[VEC_FACTOR]] x i64> +# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x i64> + +function prod_v_M_vT(R, x, M, y) + R .= x .* M .* y' +end + +emit(prod_v_vT, Matrix{Float32}, Vector{Float32}, Vector{Float32}) +emit(prod_v_vT, Matrix{Float64}, Vector{Float64}, Vector{Float64}) +emit(prod_v_vT, Matrix{Int32}, Vector{Int32}, Vector{Int32}) +emit(prod_v_vT, Matrix{Int64}, Vector{Int64}, Vector{Int64}) + +emit(prod_vT_v, Matrix{Float32}, Vector{Float32}, Vector{Float32}) +emit(prod_vT_v, Matrix{Float64}, Vector{Float64}, Vector{Float64}) +emit(prod_vT_v, Matrix{Int32}, Vector{Int32}, Vector{Int32}) +emit(prod_vT_v, Matrix{Int64}, Vector{Int64}, Vector{Int64}) + +emit(prod_v_M_vT, Matrix{Float32}, Vector{Float32}, Matrix{Float32}, Vector{Float32}) +emit(prod_v_M_vT, Matrix{Float64}, Vector{Float64}, Matrix{Float64}, Vector{Float64}) +emit(prod_v_M_vT, Matrix{Int32}, Vector{Int32}, Matrix{Int32}, Vector{Int32}) +emit(prod_v_M_vT, Matrix{Int64}, Vector{Int64}, Matrix{Int64}, Vector{Int64}) From b554e8f1de33a8dee57c988daa476c2bcc976df3 Mon Sep 17 00:00:00 2001 From: ndinsmore <45537276+ndinsmore@users.noreply.github.com> Date: Wed, 12 Apr 2023 12:17:41 -0400 Subject: [PATCH 500/775] Add native UTF-8 Validation using fast shift based DFA (#47880) * Working Native UTF-8 Validation --------- Co-authored-by: Oscar Smith Co-authored-by: Steven G. Johnson --- base/strings/string.jl | 195 +++++++++++++++++++++++++++++++++++++- base/strings/substring.jl | 6 -- test/strings/basic.jl | 152 +++++++++++++++++++++++++++++ 3 files changed, 343 insertions(+), 10 deletions(-) diff --git a/base/strings/string.jl b/base/strings/string.jl index ac1403f01a4a1..9716d06deefdf 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -98,6 +98,7 @@ String(s::AbstractString) = print_to_string(s) @assume_effects :total String(s::Symbol) = unsafe_string(unsafe_convert(Ptr{UInt8}, s)) unsafe_wrap(::Type{Vector{UInt8}}, s::String) = ccall(:jl_string_to_array, Ref{Vector{UInt8}}, (Any,), s) +unsafe_wrap(::Type{Vector{UInt8}}, s::FastContiguousSubArray{UInt8,1,Vector{UInt8}}) = unsafe_wrap(Vector{UInt8}, pointer(s), size(s)) Vector{UInt8}(s::CodeUnits{UInt8,String}) = copyto!(Vector{UInt8}(undef, length(s)), s) Vector{UInt8}(s::String) = Vector{UInt8}(codeunits(s)) @@ -191,15 +192,201 @@ end end ## checking UTF-8 & ACSII validity ## +#= + The UTF-8 Validation is performed by a shift based DFA. + ┌───────────────────────────────────────────────────────────────────┐ + │ UTF-8 DFA State Diagram ┌──────────────2──────────────┐ │ + │ ├────────3────────┐ │ │ + │ ┌──────────┐ │ ┌─┐ ┌▼┐ │ │ + │ ASCII │ UTF-8 │ ├─5──►│9├───1────► │ │ │ + │ │ │ │ ├─┤ │ │ ┌▼┐ │ + │ │ ┌─0─┐ │ ├─6──►│8├─1,7,9──►4├──1,7,9──► │ │ + │ ┌─0─┐ │ │ │ │ │ ├─┤ │ │ │ │ │ + │ │ │ │ ┌▼───┴┐ │ ├─11─►│7├──7,9───► │ ┌───────►3├─┐ │ + │ ┌▼───┴┐ │ │ │ ▼ │ └─┘ └─┘ │ │ │ │ │ + │ │ 0 ├─────┘ │ 1 ├─► ──┤ │ ┌────► │ │ │ + │ └─────┘ │ │ │ ┌─┐ │ │ └─┘ │ │ + │ └──▲──┘ ├─10─►│5├─────7──────┘ │ │ │ + │ │ │ ├─┤ │ │ │ + │ │ └─4──►│6├─────1,9───────┘ │ │ + │ INVALID │ └─┘ │ │ + │ ┌─*─┐ └──────────────────1,7,9──────────────────┘ │ + │ ┌▼───┴┐ │ + │ │ 2 ◄─── All undefined transitions result in state 2 │ + │ └─────┘ │ + └───────────────────────────────────────────────────────────────────┘ + + Validation States + 0 -> _UTF8_DFA_ASCII is the start state and will only stay in this state if the string is only ASCII characters + If the DFA ends in this state the string is ASCII only + 1 -> _UTF8_DFA_ACCEPT is the valid complete character state of the DFA once it has encountered a UTF-8 Unicode character + 2 -> _UTF8_DFA_INVALID is only reached by invalid bytes and once in this state it will not change + as seen by all 1s in that column of table below + 3 -> One valid continuation byte needed to return to state 0 + 4,5,6 -> Two valid continuation bytes needed to return to state 0 + 7,8,9 -> Three valids continuation bytes needed to return to state 0 + + Current State + 0̲ 1̲ 2̲ 3̲ 4̲ 5̲ 6̲ 7̲ 8̲ 9̲ + 0 | 0 1 2 2 2 2 2 2 2 2 + 1 | 2 2 2 1 3 2 3 2 4 4 + 2 | 3 3 2 2 2 2 2 2 2 2 + 3 | 4 4 2 2 2 2 2 2 2 2 + 4 | 6 6 2 2 2 2 2 2 2 2 + Character 5 | 9 9 2 2 2 2 2 2 2 2 <- Next State + Class 6 | 8 8 2 2 2 2 2 2 2 2 + 7 | 2 2 2 1 3 3 2 4 4 2 + 8 | 2 2 2 2 2 2 2 2 2 2 + 9 | 2 2 2 1 3 2 3 4 4 2 + 10 | 5 5 2 2 2 2 2 2 2 2 + 11 | 7 7 2 2 2 2 2 2 2 2 + + Shifts | 0 4 10 14 18 24 8 20 12 26 + + The shifts that represent each state were derived using teh SMT solver Z3, to ensure when encoded into + the rows the correct shift was a result. + + Each character class row is encoding 10 states with shifts as defined above. By shifting the bitsof a row by + the current state then masking the result with 0x11110 give the shift for the new state + + +=# + +#State type used by UTF-8 DFA +const _UTF8DFAState = UInt32 +# Fill the table with 256 UInt64 representing the DFA transitions for all bytes +const _UTF8_DFA_TABLE = let # let block rather than function doesn't pollute base + num_classes=12 + num_states=10 + bit_per_state = 6 + + # These shifts were derived using a SMT solver + state_shifts = [0, 4, 10, 14, 18, 24, 8, 20, 12, 26] + + character_classes = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, + 11, 6, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 ] + + # These are the rows discussed in comments above + state_arrays = [ 0 1 2 2 2 2 2 2 2 2; + 2 2 2 1 3 2 3 2 4 4; + 3 3 2 2 2 2 2 2 2 2; + 4 4 2 2 2 2 2 2 2 2; + 6 6 2 2 2 2 2 2 2 2; + 9 9 2 2 2 2 2 2 2 2; + 8 8 2 2 2 2 2 2 2 2; + 2 2 2 1 3 3 2 4 4 2; + 2 2 2 2 2 2 2 2 2 2; + 2 2 2 1 3 2 3 4 4 2; + 5 5 2 2 2 2 2 2 2 2; + 7 7 2 2 2 2 2 2 2 2] + + #This converts the state_arrays into the shift encoded _UTF8DFAState + class_row = zeros(_UTF8DFAState, num_classes) + + for i = 1:num_classes + row = _UTF8DFAState(0) + for j in 1:num_states + #Calculate the shift required for the next state + to_shift = UInt8((state_shifts[state_arrays[i,j]+1]) ) + #Shift the next state into the position of the current state + row = row | (_UTF8DFAState(to_shift) << state_shifts[j]) + end + class_row[i]=row + end + + map(c->class_row[c+1],character_classes) +end + + +const _UTF8_DFA_ASCII = _UTF8DFAState(0) #This state represents the start and end of any valid string +const _UTF8_DFA_ACCEPT = _UTF8DFAState(4) #This state represents the start and end of any valid string +const _UTF8_DFA_INVALID = _UTF8DFAState(10) # If the state machine is ever in this state just stop + +# The dfa step is broken out so that it may be used in other functions. The mask was calculated to work with state shifts above +@inline _utf_dfa_step(state::_UTF8DFAState, byte::UInt8) = @inbounds (_UTF8_DFA_TABLE[byte+1] >> state) & _UTF8DFAState(0x0000001E) + +@inline function _isvalid_utf8_dfa(state::_UTF8DFAState, bytes::AbstractVector{UInt8}, first::Int = firstindex(bytes), last::Int = lastindex(bytes)) + for i = first:last + @inbounds state = _utf_dfa_step(state, bytes[i]) + end + return (state) +end + +@inline function _find_nonascii_chunk(chunk_size,cu::AbstractVector{CU}, first,last) where {CU} + n=first + while n <= last - chunk_size + _isascii(cu,n,n+chunk_size-1) || return n + n += chunk_size + end + n= last-chunk_size+1 + _isascii(cu,n,last) || return n + return nothing +end + +## -byte_string_classify(s::Union{String,Vector{UInt8},FastContiguousSubArray{UInt8,1,Vector{UInt8}}}) = - ccall(:u8_isvalid, Int32, (Ptr{UInt8}, Int), s, sizeof(s)) +# Classifcations of string # 0: neither valid ASCII nor UTF-8 # 1: valid ASCII # 2: valid UTF-8 + byte_string_classify(s::AbstractString) = byte_string_classify(codeunits(s)) + + +function byte_string_classify(bytes::AbstractVector{UInt8}) + chunk_size = 1024 + chunk_threshold = chunk_size + (chunk_size ÷ 2) + n = length(bytes) + if n > chunk_threshold + start = _find_nonascii_chunk(chunk_size,bytes,1,n) + isnothing(start) && return 1 + else + _isascii(bytes,1,n) && return 1 + start = 1 + end + return _byte_string_classify_nonascii(bytes,start,n) +end + +function _byte_string_classify_nonascii(bytes::AbstractVector{UInt8}, first::Int, last::Int) + chunk_size = 256 + + start = first + stop = min(last,first + chunk_size - 1) + state = _UTF8_DFA_ACCEPT + while start <= last + # try to process ascii chunks + while state == _UTF8_DFA_ACCEPT + _isascii(bytes,start,stop) || break + (start = start + chunk_size) <= last || break + stop = min(last,stop + chunk_size) + end + # Process non ascii chunk + state = _isvalid_utf8_dfa(state,bytes,start,stop) + state == _UTF8_DFA_INVALID && return 0 + + start = start + chunk_size + stop = min(last,stop + chunk_size) + end + return ifelse(state == _UTF8_DFA_ACCEPT,2,0) +end + +isvalid(::Type{String}, bytes::AbstractVector{UInt8}) = (@inline byte_string_classify(bytes)) ≠ 0 +isvalid(::Type{String}, s::AbstractString) = (@inline byte_string_classify(s)) ≠ 0 -isvalid(::Type{String}, s::Union{Vector{UInt8},FastContiguousSubArray{UInt8,1,Vector{UInt8}},String}) = byte_string_classify(s) ≠ 0 -isvalid(s::String) = isvalid(String, s) +@inline isvalid(s::AbstractString) = @inline isvalid(String, codeunits(s)) is_valid_continuation(c) = c & 0xc0 == 0x80 diff --git a/base/strings/substring.jl b/base/strings/substring.jl index ea132402447be..5ba08ac2f7fff 100644 --- a/base/strings/substring.jl +++ b/base/strings/substring.jl @@ -100,12 +100,6 @@ function isvalid(s::SubString, i::Integer) @inbounds return ib && isvalid(s.string, s.offset + i)::Bool end -byte_string_classify(s::SubString{String}) = - ccall(:u8_isvalid, Int32, (Ptr{UInt8}, Int), s, sizeof(s)) - -isvalid(::Type{String}, s::SubString{String}) = byte_string_classify(s) ≠ 0 -isvalid(s::SubString{String}) = isvalid(String, s) - thisind(s::SubString{String}, i::Int) = _thisind_str(s, i) nextind(s::SubString{String}, i::Int) = _nextind_str(s, i) diff --git a/test/strings/basic.jl b/test/strings/basic.jl index e1d6e9dd60491..602c38551f6d8 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -1234,3 +1234,155 @@ end end @test_throws ArgumentError Symbol("a\0a") end + +@testset "Ensure UTF-8 DFA can never leave invalid state" begin + for b = typemin(UInt8):typemax(UInt8) + @test Base._isvalid_utf8_dfa(Base._UTF8_DFA_INVALID,[b],1,1) == Base._UTF8_DFA_INVALID + end +end +@testset "Ensure UTF-8 DFA stays in ASCII State for all ASCII" begin + for b = 0x00:0x7F + @test Base._isvalid_utf8_dfa(Base._UTF8_DFA_ASCII,[b],1,1) == Base._UTF8_DFA_ASCII + end +end + +@testset "Validate UTF-8 DFA" begin + # Unicode 15 + # Table 3-7. Well-Formed UTF-8 Byte Sequences + + table_rows = [ [0x00:0x7F], + [0xC2:0xDF,0x80:0xBF], + [0xE0:0xE0,0xA0:0xBF,0x80:0xBF], + [0xE1:0xEC,0x80:0xBF,0x80:0xBF], + [0xED:0xED,0x80:0x9F,0x80:0xBF], + [0xEE:0xEF,0x80:0xBF,0x80:0xBF], + [0xF0:0xF0,0x90:0xBF,0x80:0xBF,0x80:0xBF], + [0xF1:0xF3,0x80:0xBF,0x80:0xBF,0x80:0xBF], + [0xF4:0xF4,0x80:0x8F,0x80:0xBF,0x80:0xBF]] + invalid_first_bytes = union(0xC0:0xC1,0xF5:0xFF,0x80:0xBF) + + valid_first_bytes = union(collect(first(r) for r in table_rows)...) + + + + # Prove that the first byte sets in the table & invalid cover all bytes + @test length(union(valid_first_bytes,invalid_first_bytes)) == 256 + @test length(intersect(valid_first_bytes,invalid_first_bytes)) == 0 + + #Check the ASCII range + for b = 0x00:0x7F + #Test from both UTF-8 state and ascii state + @test Base._isvalid_utf8_dfa(Base._UTF8_DFA_ACCEPT,[b],1,1) == Base._UTF8_DFA_ACCEPT + @test Base._isvalid_utf8_dfa(Base._UTF8_DFA_ASCII,[b],1,1) == Base._UTF8_DFA_ASCII + end + + #Check the remaining first bytes + for b = 0x80:0xFF + if b ∈ invalid_first_bytes + @test Base._isvalid_utf8_dfa(Base._UTF8_DFA_ACCEPT,[b],1,1) == Base._UTF8_DFA_INVALID + @test Base._isvalid_utf8_dfa(Base._UTF8_DFA_ASCII,[b],1,1) == Base._UTF8_DFA_INVALID + else + @test Base._isvalid_utf8_dfa(Base._UTF8_DFA_ACCEPT,[b],1,1) != Base._UTF8_DFA_INVALID + @test Base._isvalid_utf8_dfa(Base._UTF8_DFA_ASCII,[b],1,1) != Base._UTF8_DFA_INVALID + end + end + + # Check two byte Sequences + for table_row in [table_rows[2]] + b1 = first(table_row[1]) + state1 = Base._isvalid_utf8_dfa(Base._UTF8_DFA_ACCEPT,[b1],1,1) + state2 = Base._isvalid_utf8_dfa(Base._UTF8_DFA_ASCII,[b1],1,1) + @test state1 == state2 + #Prove that all the first bytes in a row give same state + for b1 in table_row[1] + @test state1 == Base._isvalid_utf8_dfa(Base._UTF8_DFA_ACCEPT,[b1],1,1) + @test state1 == Base._isvalid_utf8_dfa(Base._UTF8_DFA_ASCII,[b1],1,1) + end + b1 = first(table_row[1]) + #Prove that all valid second bytes return correct state + for b2 = table_row[2] + @test Base._UTF8_DFA_ACCEPT == Base._isvalid_utf8_dfa(state1,[b2],1,1) + end + for b2 = setdiff(0x00:0xFF,table_row[2]) + @test Base._UTF8_DFA_INVALID == Base._isvalid_utf8_dfa(state1,[b2],1,1) + end + end + + # Check three byte Sequences + for table_row in table_rows[3:6] + b1 = first(table_row[1]) + state1 = Base._isvalid_utf8_dfa(Base._UTF8_DFA_ACCEPT,[b1],1,1) + state2 = Base._isvalid_utf8_dfa(Base._UTF8_DFA_ASCII,[b1],1,1) + @test state1 == state2 + #Prove that all the first bytes in a row give same state + for b1 in table_row[1] + @test state1 == Base._isvalid_utf8_dfa(Base._UTF8_DFA_ACCEPT,[b1],1,1) + @test state1 == Base._isvalid_utf8_dfa(Base._UTF8_DFA_ASCII,[b1],1,1) + end + + b1 = first(table_row[1]) + b2 = first(table_row[2]) + #Prove that all valid second bytes return same state + state2 = Base._isvalid_utf8_dfa(state1,[b2],1,1) + for b2 = table_row[2] + @test state2 == Base._isvalid_utf8_dfa(state1,[b2],1,1) + end + for b2 = setdiff(0x00:0xFF,table_row[2]) + @test Base._UTF8_DFA_INVALID == Base._isvalid_utf8_dfa(state1,[b2],1,1) + end + + b2 = first(table_row[2]) + #Prove that all valid third bytes return correct state + for b3 = table_row[3] + @test Base._UTF8_DFA_ACCEPT == Base._isvalid_utf8_dfa(state2,[b3],1,1) + end + for b3 = setdiff(0x00:0xFF,table_row[3]) + @test Base._UTF8_DFA_INVALID == Base._isvalid_utf8_dfa(state2,[b3],1,1) + end + end + + # Check Four byte Sequences + for table_row in table_rows[7:9] + b1 = first(table_row[1]) + state1 = Base._isvalid_utf8_dfa(Base._UTF8_DFA_ACCEPT,[b1],1,1) + state2 = Base._isvalid_utf8_dfa(Base._UTF8_DFA_ASCII,[b1],1,1) + @test state1 == state2 + #Prove that all the first bytes in a row give same state + for b1 in table_row[1] + @test state1 == Base._isvalid_utf8_dfa(Base._UTF8_DFA_ACCEPT,[b1],1,1) + @test state1 == Base._isvalid_utf8_dfa(Base._UTF8_DFA_ASCII,[b1],1,1) + end + + b1 = first(table_row[1]) + b2 = first(table_row[2]) + #Prove that all valid second bytes return same state + state2 = Base._isvalid_utf8_dfa(state1,[b2],1,1) + for b2 = table_row[2] + @test state2 == Base._isvalid_utf8_dfa(state1,[b2],1,1) + end + for b2 = setdiff(0x00:0xFF,table_row[2]) + @test Base._UTF8_DFA_INVALID == Base._isvalid_utf8_dfa(state1,[b2],1,1) + end + + + b2 = first(table_row[2]) + b3 = first(table_row[3]) + state3 = Base._isvalid_utf8_dfa(state2,[b3],1,1) + #Prove that all valid third bytes return same state + for b3 = table_row[3] + @test state3 == Base._isvalid_utf8_dfa(state2,[b3],1,1) + end + for b3 = setdiff(0x00:0xFF,table_row[3]) + @test Base._UTF8_DFA_INVALID == Base._isvalid_utf8_dfa(state2,[b3],1,1) + end + + b3 = first(table_row[3]) + #Prove that all valid forth bytes return correct state + for b4 = table_row[4] + @test Base._UTF8_DFA_ACCEPT == Base._isvalid_utf8_dfa(state3,[b4],1,1) + end + for b4 = setdiff(0x00:0xFF,table_row[4]) + @test Base._UTF8_DFA_INVALID == Base._isvalid_utf8_dfa(state3,[b4],1,1) + end + end +end From 5a6ce6a992381f993b53bfa50d15cb5392043d9e Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Wed, 12 Apr 2023 12:03:21 -0700 Subject: [PATCH 501/775] Further relax another piece of the AbstractQ tests Judging by the other `Matrix(Q)` tests, this should be an approximate equals. Testing with Apple Accelerate shows that all errors are on the order of `1e-16`. --- stdlib/LinearAlgebra/test/abstractq.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/test/abstractq.jl b/stdlib/LinearAlgebra/test/abstractq.jl index cb28629424194..b3ef5a1952244 100644 --- a/stdlib/LinearAlgebra/test/abstractq.jl +++ b/stdlib/LinearAlgebra/test/abstractq.jl @@ -56,7 +56,8 @@ n = 5 @test Q[:,1] == Q.Q[:,1] @test Q[1,1] == Q.Q[1,1] @test Q[:] == Q.Q[:] - @test Q[:,1:3] == Q.Q[:,1:3] == Matrix(Q)[:,1:3] + @test Q[:,1:3] == Q.Q[:,1:3] + @test Q[:,1:3] ≈ Matrix(Q)[:,1:3] @test_throws BoundsError Q[0,1] @test_throws BoundsError Q[n+1,1] @test_throws BoundsError Q[1,0] From 0fad80ba4a6607a80ebf1598c60a8810f4e792cf Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Thu, 13 Apr 2023 00:34:27 +0000 Subject: [PATCH 502/775] Add optional vscale prefix to loopinfo test (#49333) --- test/llvmpasses/loopinfo.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/llvmpasses/loopinfo.jl b/test/llvmpasses/loopinfo.jl index 412bee7015c3e..c970e07f8a125 100644 --- a/test/llvmpasses/loopinfo.jl +++ b/test/llvmpasses/loopinfo.jl @@ -32,7 +32,7 @@ function simdf(X) # LOWER: fadd fast double # LOWER-NOT: call void @julia.loopinfo_marker() # LOWER: br {{.*}}, !llvm.loop [[LOOPID:![0-9]+]] -# FINAL: fadd fast <{{[0-9]+}} x double> +# FINAL: fadd fast <{{(vscale x )?}}{{[0-9]+}} x double> end acc end From 05fa0a77e04deb108b51798cf921b56c68db1b76 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Wed, 12 Apr 2023 20:43:13 -0400 Subject: [PATCH 503/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=20992a8c27b=20to=207ebf98b43=20(#49339)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 | 1 + .../Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 | 1 + .../Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/md5 | 1 - .../Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 create mode 100644 deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/sha512 diff --git a/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 b/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 new file mode 100644 index 0000000000000..7a0f31b8bec01 --- /dev/null +++ b/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 @@ -0,0 +1 @@ +2885181bffe95462f1877668ccea7057 diff --git a/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 b/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 new file mode 100644 index 0000000000000..f8231bbf2833f --- /dev/null +++ b/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 @@ -0,0 +1 @@ +5f3ded1970a6d8bfc779de54e61b1dd58fa770799aac3a031b2ea536d159bf672e9490f53ecdfbf1c175adfe93b0868a88330619506da802218a98f07e64dd94 diff --git a/deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/md5 b/deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/md5 deleted file mode 100644 index fec075ed8775c..0000000000000 --- a/deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -a972d55766241fbd4b3e789286c87a5a diff --git a/deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/sha512 b/deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/sha512 deleted file mode 100644 index ff82d32f4001b..0000000000000 --- a/deps/checksums/Pkg-992a8c27b967f45f04de07bd84a2763644cb8a33.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -357a3494d5015e4541e7657f74d480efda6b503d5fa49a250f8aafda08c466bca292c630e52eb18a9d694464a00124176bdf3ae2e84db475f6b497bbda0dad3c diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 3b4e7a877932b..4274859b10120 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 992a8c27b967f45f04de07bd84a2763644cb8a33 +PKG_SHA1 = 7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From f84cb275073f0ab55ddf7b83a0c7dc389e6364ed Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 13 Apr 2023 15:28:23 +0900 Subject: [PATCH 504/775] fix `PartialOpaque` handling within `issimplertype` (#49329) The proper handling has not been implemented here, but it should not error at least. --- base/compiler/typelimits.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index b5bbcde63e699..e7b9066d53068 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -353,6 +353,7 @@ function issimplertype(𝕃::AbstractLattice, @nospecialize(typea), @nospecializ issimplertype(𝕃, typea.fldtyp, typeb.fldtyp) || return false elseif typea isa PartialOpaque # TODO + typeb isa PartialOpaque || return false aty = widenconst(typea) bty = widenconst(typeb) if typea.source === typeb.source && typea.parent === typeb.parent && aty == bty && typea.env == typeb.env From 1f3173e867e14fe944f7bf34494cfb13f1ce58ad Mon Sep 17 00:00:00 2001 From: wldeh Date: Sun, 2 Apr 2023 23:09:18 -0700 Subject: [PATCH 505/775] fix(sysinfo): address permission errors in Base.Sys.which (#49181) Resolves a discrepancy between Julia's Base.Sys.which function and the Unix which command when handling directories without read access in the PATH. By introducing a try-catch block for isfile and isexecutable checks, the function now gracefully skips inaccessible directories, mirroring the Unix which command behavior and preventing errors from being raised in such cases. --- base/sysinfo.jl | 18 +++++++++++++++--- test/sysinfo.jl | 29 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/base/sysinfo.jl b/base/sysinfo.jl index b885d88a5f3cb..2c962088484e7 100644 --- a/base/sysinfo.jl +++ b/base/sysinfo.jl @@ -543,9 +543,21 @@ function which(program_name::String) for path_dir in path_dirs for pname in program_names program_path = joinpath(path_dir, pname) - # If we find something that matches our name and we can execute - if isfile(program_path) && isexecutable(program_path) - return program_path + try + # If we find something that matches our name and we can execute + if isfile(program_path) && isexecutable(program_path) + return program_path + end + catch e + # If we encounter a permission error, we skip this directory + # and continue to the next directory in the PATH variable. + if isa(e, Base.IOError) && e.code == Base.UV_EACCES + # Permission denied, continue searching + continue + else + # Rethrow the exception if it's not a permission error + rethrow(e) + end end end end diff --git a/test/sysinfo.jl b/test/sysinfo.jl index e423f6071c9e0..3a16dc73b4f6a 100644 --- a/test/sysinfo.jl +++ b/test/sysinfo.jl @@ -9,3 +9,32 @@ Base.Sys.loadavg() @test Base.libllvm_path() isa Symbol @test contains(String(Base.libllvm_path()), "LLVM") + +if Sys.isunix() + mktempdir() do tempdir + firstdir = joinpath(tempdir, "first") + seconddir = joinpath(tempdir, "second") + + mkpath(firstdir) + mkpath(seconddir) + + touch(joinpath(firstdir, "foo")) + touch(joinpath(seconddir, "foo")) + + chmod(joinpath(firstdir, "foo"), 0o777) + chmod(joinpath(seconddir, "foo"), 0o777) + + # zero permissions on first directory + chmod(firstdir, 0o000) + + original_path = ENV["PATH"] + ENV["PATH"] = string(firstdir, ":", seconddir, ":", original_path) + try + @test abspath(Base.Sys.which("foo")) == abspath(joinpath(seconddir, "foo")) + finally + # clean up + chmod(firstdir, 0o777) + ENV["PATH"] = original_path + end + end +end From 9ed4087f7f515f76a71164e292b013043b862053 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 13 Apr 2023 09:04:06 -0400 Subject: [PATCH 506/775] make thread 1 interactive when there is an interactive pool, so it can run the event loop (#49094) --- base/task.jl | 4 +-- base/threadingconstructs.jl | 58 +++++++++++++++++++++++++++---------- src/threading.c | 11 +++---- test/threadpool_use.jl | 6 ++-- 4 files changed, 53 insertions(+), 26 deletions(-) diff --git a/base/task.jl b/base/task.jl index ffe8e5665b041..e407cbd62bbd6 100644 --- a/base/task.jl +++ b/base/task.jl @@ -253,7 +253,7 @@ istaskfailed(t::Task) = (load_state_acquire(t) === task_state_failed) Threads.threadid(t::Task) = Int(ccall(:jl_get_task_tid, Int16, (Any,), t)+1) function Threads.threadpool(t::Task) tpid = ccall(:jl_get_task_threadpoolid, Int8, (Any,), t) - return tpid == 0 ? :default : :interactive + return Threads._tpid_to_sym(tpid) end task_result(t::Task) = t.result @@ -786,7 +786,7 @@ function enq_work(t::Task) if Threads.threadpoolsize(tp) == 1 # There's only one thread in the task's assigned thread pool; # use its work queue. - tid = (tp === :default) ? 1 : Threads.threadpoolsize(:default)+1 + tid = (tp === :interactive) ? 1 : Threads.threadpoolsize(:interactive)+1 ccall(:jl_set_task_tid, Cint, (Any, Cint), t, tid-1) push!(workqueue_for(tid), t) else diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl index e7257759b15a9..f6e7ea4480305 100644 --- a/base/threadingconstructs.jl +++ b/base/threadingconstructs.jl @@ -39,6 +39,14 @@ function _nthreads_in_pool(tpid::Int8) return Int(unsafe_load(p, tpid + 1)) end +function _tpid_to_sym(tpid::Int8) + return tpid == 0 ? :interactive : :default +end + +function _sym_to_tpid(tp::Symbol) + return tp === :interactive ? Int8(0) : Int8(1) +end + """ Threads.threadpool(tid = threadid()) -> Symbol @@ -46,7 +54,7 @@ Returns the specified thread's threadpool; either `:default` or `:interactive`. """ function threadpool(tid = threadid()) tpid = ccall(:jl_threadpoolid, Int8, (Int16,), tid-1) - return tpid == 0 ? :default : :interactive + return _tpid_to_sym(tpid) end """ @@ -67,24 +75,39 @@ See also: `BLAS.get_num_threads` and `BLAS.set_num_threads` in the [`Distributed`](@ref man-distributed) standard library. """ function threadpoolsize(pool::Symbol = :default) - if pool === :default - tpid = Int8(0) - elseif pool === :interactive - tpid = Int8(1) + if pool === :default || pool === :interactive + tpid = _sym_to_tpid(pool) else error("invalid threadpool specified") end return _nthreads_in_pool(tpid) end +""" + threadpooltids(pool::Symbol) + +Returns a vector of IDs of threads in the given pool. +""" +function threadpooltids(pool::Symbol) + ni = _nthreads_in_pool(Int8(0)) + if pool === :interactive + return collect(1:ni) + elseif pool === :default + return collect(ni+1:ni+_nthreads_in_pool(Int8(1))) + else + error("invalid threadpool specified") + end +end + function threading_run(fun, static) ccall(:jl_enter_threaded_region, Cvoid, ()) n = threadpoolsize() + tid_offset = threadpoolsize(:interactive) tasks = Vector{Task}(undef, n) for i = 1:n t = Task(() -> fun(i)) # pass in tid t.sticky = static - static && ccall(:jl_set_task_tid, Cint, (Any, Cint), t, i-1) + static && ccall(:jl_set_task_tid, Cint, (Any, Cint), t, tid_offset + i-1) tasks[i] = t schedule(t) end @@ -287,6 +310,15 @@ macro threads(args...) return _threadsfor(ex.args[1], ex.args[2], sched) end +function _spawn_set_thrpool(t::Task, tp::Symbol) + tpid = _sym_to_tpid(tp) + if _nthreads_in_pool(tpid) == 0 + tpid = _sym_to_tpid(:default) + end + ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, tpid) + nothing +end + """ Threads.@spawn [:default|:interactive] expr @@ -315,7 +347,7 @@ the variable's value in the current task. A threadpool may be specified as of Julia 1.9. """ macro spawn(args...) - tpid = Int8(0) + tp = :default na = length(args) if na == 2 ttype, ex = args @@ -325,9 +357,9 @@ macro spawn(args...) # TODO: allow unquoted symbols ttype = nothing end - if ttype === :interactive - tpid = Int8(1) - elseif ttype !== :default + if ttype === :interactive || ttype === :default + tp = ttype + else throw(ArgumentError("unsupported threadpool in @spawn: $ttype")) end elseif na == 1 @@ -344,11 +376,7 @@ macro spawn(args...) let $(letargs...) local task = Task($thunk) task.sticky = false - local tpid_actual = $tpid - if _nthreads_in_pool(tpid_actual) == 0 - tpid_actual = Int8(0) - end - ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), task, tpid_actual) + _spawn_set_thrpool(task, $(QuoteNode(tp))) if $(Expr(:islocal, var)) put!($var, task) end diff --git a/src/threading.c b/src/threading.c index db9df0bad0dde..f909f41ac5c64 100644 --- a/src/threading.c +++ b/src/threading.c @@ -600,17 +600,16 @@ void jl_init_threading(void) // specified on the command line (and so are in `jl_options`) or by the // environment variable. Set the globals `jl_n_threadpools`, `jl_n_threads` // and `jl_n_threads_per_pool`. - jl_n_threadpools = 1; + jl_n_threadpools = 2; int16_t nthreads = JULIA_NUM_THREADS; int16_t nthreadsi = 0; char *endptr, *endptri; if (jl_options.nthreads != 0) { // --threads specified - jl_n_threadpools = jl_options.nthreadpools; nthreads = jl_options.nthreads_per_pool[0]; if (nthreads < 0) nthreads = jl_effective_threads(); - if (jl_n_threadpools == 2) + if (jl_options.nthreadpools == 2) nthreadsi = jl_options.nthreads_per_pool[1]; } else if ((cp = getenv(NUM_THREADS_NAME))) { // ENV[NUM_THREADS_NAME] specified @@ -635,15 +634,13 @@ void jl_init_threading(void) if (errno != 0 || endptri == cp || nthreadsi < 0) nthreadsi = 0; } - if (nthreadsi > 0) - jl_n_threadpools++; } } jl_all_tls_states_size = nthreads + nthreadsi; jl_n_threads_per_pool = (int*)malloc_s(2 * sizeof(int)); - jl_n_threads_per_pool[0] = nthreads; - jl_n_threads_per_pool[1] = nthreadsi; + jl_n_threads_per_pool[0] = nthreadsi; + jl_n_threads_per_pool[1] = nthreads; jl_atomic_store_release(&jl_all_tls_states, (jl_ptls_t*)calloc(jl_all_tls_states_size, sizeof(jl_ptls_t))); jl_atomic_store_release(&jl_n_threads, jl_all_tls_states_size); diff --git a/test/threadpool_use.jl b/test/threadpool_use.jl index 64227c8a8110b..e5ea5f95cf4ff 100644 --- a/test/threadpool_use.jl +++ b/test/threadpool_use.jl @@ -4,8 +4,10 @@ using Test using Base.Threads @test nthreadpools() == 2 -@test threadpool() === :default -@test threadpool(2) === :interactive +@test threadpool() === :interactive +@test threadpool(2) === :default @test fetch(Threads.@spawn Threads.threadpool()) === :default @test fetch(Threads.@spawn :default Threads.threadpool()) === :default @test fetch(Threads.@spawn :interactive Threads.threadpool()) === :interactive +@test Threads.threadpooltids(:interactive) == [1] +@test Threads.threadpooltids(:default) == [2] From 95f437609edabdcd4dca1e18e39e3a60709b87ae Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 14 Mar 2023 12:30:03 -0400 Subject: [PATCH 507/775] Lookup `ccall` symbols in local internal libraries first This is similar to what we do on Windows, where handles to the exact library are required anyway. This is enough to make sure that our `dlsym` lookups are directed to the correct libjulia, even when loading a Julia run-time within Julia. The second change needed to get things working (not included in this commit) is to add symbol versioning, so that the runtime linker does not mix up symbols between the two libraries. --- src/ccall.cpp | 30 +++++++++++-------------- src/codegen.cpp | 2 -- src/dlload.c | 48 ++++++++++++++++++++-------------------- src/init.c | 8 ++----- src/julia_internal.h | 9 ++++---- src/runtime_ccall.cpp | 2 -- src/runtime_intrinsics.c | 4 +--- 7 files changed, 45 insertions(+), 58 deletions(-) diff --git a/src/ccall.cpp b/src/ccall.cpp index e490f4146cad2..1087525e1b341 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -61,7 +61,6 @@ static bool runtime_sym_gvs(jl_codectx_t &ctx, const char *f_lib, const char *f_ bool runtime_lib = false; GlobalVariable *libptrgv; jl_codegen_params_t::SymMapGV *symMap; -#ifdef _OS_WINDOWS_ if ((intptr_t)f_lib == (intptr_t)JL_EXE_LIBNAME) { libptrgv = prepare_global_in(M, jlexe_var); symMap = &ctx.emission_context.symMapExe; @@ -74,9 +73,7 @@ static bool runtime_sym_gvs(jl_codectx_t &ctx, const char *f_lib, const char *f_ libptrgv = prepare_global_in(M, jldll_var); symMap = &ctx.emission_context.symMapDll; } - else -#endif - if (f_lib == NULL) { + else if (f_lib == NULL) { libptrgv = jl_emit_RTLD_DEFAULT_var(M); symMap = &ctx.emission_context.symMapDefault; } @@ -631,16 +628,12 @@ static void interpret_symbol_arg(jl_codectx_t &ctx, native_sym_arg_t &out, jl_va std::string iname("i"); iname += f_name; if (jl_dlsym(jl_libjulia_internal_handle, iname.c_str(), &symaddr, 0)) { -#ifdef _OS_WINDOWS_ f_lib = JL_LIBJULIA_INTERNAL_DL_LIBNAME; -#endif f_name = jl_symbol_name(jl_symbol(iname.c_str())); } -#ifdef _OS_WINDOWS_ else { - f_lib = jl_dlfind_win32(f_name); + f_lib = jl_dlfind(f_name); } -#endif } } else if (jl_is_cpointer_type(jl_typeof(ptr))) { @@ -726,7 +719,8 @@ static jl_cgval_t emit_cglobal(jl_codectx_t &ctx, jl_value_t **args, size_t narg void *symaddr; void* libsym = jl_get_library_(sym.f_lib, 0); - if (!libsym || !jl_dlsym(libsym, sym.f_name, &symaddr, 0)) { + int symbol_found = jl_dlsym(libsym, sym.f_name, &symaddr, 0); + if (!libsym || !symbol_found) { // Error mode, either the library or the symbol couldn't be find during compiletime. // Fallback to a runtime symbol lookup. res = runtime_sym_lookup(ctx, cast(getInt8PtrTy(ctx.builder.getContext())), sym.f_lib, NULL, sym.f_name, ctx.f); @@ -1381,18 +1375,19 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) if ((uintptr_t)fptr == ptr) return true; if (f_lib) { -#ifdef _OS_WINDOWS_ if ((f_lib == JL_EXE_LIBNAME) || // preventing invalid pointer access (f_lib == JL_LIBJULIA_INTERNAL_DL_LIBNAME) || - (f_lib == JL_LIBJULIA_DL_LIBNAME) || - (!strcmp(f_lib, jl_crtdll_basename))) { + (f_lib == JL_LIBJULIA_DL_LIBNAME)) { + // libjulia-like + } + else +#ifdef _OS_WINDOWS_ + if (strcmp(f_lib, jl_crtdll_basename) == 0) { // libjulia-like } else - return false; -#else - return false; #endif + return false; } return f_name && f_name == name; }; @@ -2082,7 +2077,8 @@ jl_cgval_t function_sig_t::emit_a_ccall( else { void *symaddr; void *libsym = jl_get_library_(symarg.f_lib, 0); - if (!libsym || !jl_dlsym(libsym, symarg.f_name, &symaddr, 0)) { + int symbol_found = jl_dlsym(libsym, symarg.f_name, &symaddr, 0); + if (!libsym || !symbol_found) { ++DeferredCCallLookups; // either the library or the symbol could not be found, place a runtime // lookup here instead. diff --git a/src/codegen.cpp b/src/codegen.cpp index b6b86ba4442e1..0b62c481b9e41 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8686,11 +8686,9 @@ static void init_jit_functions(void) { add_named_global(jlstack_chk_guard_var, &__stack_chk_guard); add_named_global(jlRTLD_DEFAULT_var, &jl_RTLD_DEFAULT_handle); -#ifdef _OS_WINDOWS_ add_named_global(jlexe_var, &jl_exe_handle); add_named_global(jldll_var, &jl_libjulia_handle); add_named_global(jldlli_var, &jl_libjulia_internal_handle); -#endif auto size2pjlvalue = [](Type *T_size) -> Type * { return get_pjlvalue(T_size->getContext()); }; diff --git a/src/dlload.c b/src/dlload.c index 9f4e8be29952d..64365848ad6f3 100644 --- a/src/dlload.c +++ b/src/dlload.c @@ -189,6 +189,7 @@ JL_DLLEXPORT JL_NO_SANITIZE void *jl_dlopen(const char *filename, unsigned flags if (!dlopen) return NULL; void *libdl_handle = dlopen("libdl.so", RTLD_NOW | RTLD_NOLOAD); + assert(libdl_handle); dlopen = (dlopen_prototype*)dlsym(libdl_handle, "dlopen"); dlclose(libdl_handle); assert(dlopen); @@ -239,6 +240,25 @@ JL_DLLEXPORT int jl_dlclose(void *handle) JL_NOTSAFEPOINT #endif } +void *jl_find_dynamic_library_by_addr(void *symbol) { + void *handle; +#ifdef _OS_WINDOWS_ + if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCWSTR)symbol, + (HMODULE*)&handle)) { + jl_error("could not load base module"); + } +#else + Dl_info info; + if (!dladdr(symbol, &info) || !info.dli_fname) { + jl_error("could not load base module"); + } + handle = dlopen(info.dli_fname, RTLD_NOW | RTLD_NOLOAD); + dlclose(handle); // Undo ref count increment from `dlopen` +#endif + return handle; +} + JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, int throw_err) { char path[PATHBUF], relocated[PATHBUF]; @@ -255,26 +275,6 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, int n_extensions = endswith_extension(modname) ? 1 : N_EXTENSIONS; int ret; - /* - this branch returns handle of libjulia-internal - */ - if (modname == NULL) { -#ifdef _OS_WINDOWS_ - if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - (LPCWSTR)(uintptr_t)(&jl_load_dynamic_library), - (HMODULE*)&handle)) { - jl_error("could not load base module"); - } -#else - Dl_info info; - if (!dladdr((void*)(uintptr_t)&jl_load_dynamic_library, &info) || !info.dli_fname) { - jl_error("could not load base module"); - } - handle = dlopen(info.dli_fname, RTLD_NOW); -#endif - goto done; - } - abspath = jl_isabspath(modname); is_atpath = 0; @@ -421,9 +421,8 @@ JL_DLLEXPORT int jl_dlsym(void *handle, const char *symbol, void ** value, int t return symbol_found; } -#ifdef _OS_WINDOWS_ -//Look for symbols in win32 libraries -JL_DLLEXPORT const char *jl_dlfind_win32(const char *f_name) +// Look for symbols in internal libraries +JL_DLLEXPORT const char *jl_dlfind(const char *f_name) { void * dummy; if (jl_dlsym(jl_exe_handle, f_name, &dummy, 0)) @@ -432,6 +431,7 @@ JL_DLLEXPORT const char *jl_dlfind_win32(const char *f_name) return JL_LIBJULIA_INTERNAL_DL_LIBNAME; if (jl_dlsym(jl_libjulia_handle, f_name, &dummy, 0)) return JL_LIBJULIA_DL_LIBNAME; +#ifdef _OS_WINDOWS_ if (jl_dlsym(jl_kernel32_handle, f_name, &dummy, 0)) return "kernel32"; if (jl_dlsym(jl_crtdll_handle, f_name, &dummy, 0)) // Prefer crtdll over ntdll @@ -440,6 +440,7 @@ JL_DLLEXPORT const char *jl_dlfind_win32(const char *f_name) return "ntdll"; if (jl_dlsym(jl_winsock_handle, f_name, &dummy, 0)) return "ws2_32"; +#endif // additional common libraries (libc?) could be added here, but in general, // it is better to specify the library explicitly in the code. This exists // mainly to ease compatibility with linux, and for libraries that don't @@ -451,7 +452,6 @@ JL_DLLEXPORT const char *jl_dlfind_win32(const char *f_name) // which defaults to jl_libjulia_internal_handle, where we won't find it, and // will throw the appropriate error. } -#endif #ifdef __cplusplus } diff --git a/src/init.c b/src/init.c index 5990bd24aaabd..95a5a32704f2f 100644 --- a/src/init.c +++ b/src/init.c @@ -763,15 +763,11 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) void *stack_lo, *stack_hi; jl_init_stack_limits(1, &stack_lo, &stack_hi); - jl_libjulia_internal_handle = jl_load_dynamic_library(NULL, JL_RTLD_DEFAULT, 1); + jl_libjulia_internal_handle = jl_find_dynamic_library_by_addr(&jl_load_dynamic_library); + jl_libjulia_handle = jl_find_dynamic_library_by_addr(&jl_any_type); #ifdef _OS_WINDOWS_ jl_exe_handle = GetModuleHandleA(NULL); jl_RTLD_DEFAULT_handle = jl_libjulia_internal_handle; - if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - (LPCWSTR)&jl_any_type, - (HMODULE*)&jl_libjulia_handle)) { - jl_error("could not load base module"); - } jl_ntdll_handle = jl_dlopen("ntdll.dll", JL_RTLD_NOLOAD); // bypass julia's pathchecking for system dlls jl_kernel32_handle = jl_dlopen("kernel32.dll", JL_RTLD_NOLOAD); jl_crtdll_handle = jl_dlopen(jl_crtdll_name, JL_RTLD_NOLOAD); diff --git a/src/julia_internal.h b/src/julia_internal.h index 4f1a0b4513d8d..5d3f26e1eb1ba 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1254,11 +1254,11 @@ JL_DLLEXPORT uint64_t jl_rand(void) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_srand(uint64_t) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_init_rand(void); +JL_DLLEXPORT extern void *jl_exe_handle; +JL_DLLEXPORT extern void *jl_libjulia_handle; JL_DLLEXPORT extern void *jl_libjulia_internal_handle; JL_DLLEXPORT extern void *jl_RTLD_DEFAULT_handle; #if defined(_OS_WINDOWS_) -JL_DLLEXPORT extern void *jl_exe_handle; -JL_DLLEXPORT extern void *jl_libjulia_handle; JL_DLLEXPORT extern const char *jl_crtdll_basename; extern void *jl_ntdll_handle; extern void *jl_kernel32_handle; @@ -1268,6 +1268,7 @@ void win32_formatmessage(DWORD code, char *reason, int len) JL_NOTSAFEPOINT; #endif JL_DLLEXPORT void *jl_get_library_(const char *f_lib, int throw_err); +void *jl_find_dynamic_library_by_addr(void *symbol); #define jl_get_library(f_lib) jl_get_library_(f_lib, 1) JL_DLLEXPORT void *jl_load_and_lookup(const char *f_lib, const char *f_name, _Atomic(void*) *hnd); JL_DLLEXPORT void *jl_lazy_load_and_lookup(jl_value_t *lib_val, const char *f_name); @@ -1277,11 +1278,11 @@ JL_DLLEXPORT jl_value_t *jl_get_cfunction_trampoline( jl_unionall_t *env, jl_value_t **vals); -// Windows only +// Special filenames used to refer to internal julia libraries #define JL_EXE_LIBNAME ((const char*)1) #define JL_LIBJULIA_DL_LIBNAME ((const char*)2) #define JL_LIBJULIA_INTERNAL_DL_LIBNAME ((const char*)3) -JL_DLLEXPORT const char *jl_dlfind_win32(const char *name); +JL_DLLEXPORT const char *jl_dlfind(const char *name); // libuv wrappers: JL_DLLEXPORT int jl_fs_rename(const char *src_path, const char *dst_path); diff --git a/src/runtime_ccall.cpp b/src/runtime_ccall.cpp index e3543c9f62656..fa2184f555f28 100644 --- a/src/runtime_ccall.cpp +++ b/src/runtime_ccall.cpp @@ -31,14 +31,12 @@ void *jl_get_library_(const char *f_lib, int throw_err) { if (f_lib == NULL) return jl_RTLD_DEFAULT_handle; -#ifdef _OS_WINDOWS_ if (f_lib == JL_EXE_LIBNAME) return jl_exe_handle; if (f_lib == JL_LIBJULIA_INTERNAL_DL_LIBNAME) return jl_libjulia_internal_handle; if (f_lib == JL_LIBJULIA_DL_LIBNAME) return jl_libjulia_handle; -#endif JL_LOCK(&libmap_lock); // This is the only operation we do on the map, which doesn't invalidate // any references or iterators. diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index 0ac5b277b0657..9babdf89f098b 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -508,10 +508,8 @@ JL_DLLEXPORT jl_value_t *jl_cglobal(jl_value_t *v, jl_value_t *ty) else JL_TYPECHK(cglobal, symbol, v) -#ifdef _OS_WINDOWS_ if (!f_lib) - f_lib = (char*)jl_dlfind_win32(f_name); -#endif + f_lib = (char*)jl_dlfind(f_name); void *ptr; jl_dlsym(jl_get_library(f_lib), f_name, &ptr, 1); From c931884e5b6a6c1df51de1f6d3c55fe3ff9ea5ce Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 13 Apr 2023 12:56:47 -0400 Subject: [PATCH 508/775] fix another case where we might return free TypeVar (#48228) We had an environment here that looked like while computing the upper bound for J{S} where S: where S=T where T where I{T} where J{S} where S Then we started handling those, and filling in the values: First replacing S with T, which creates a `res` of `J{T}` where T where I{T} where J{S} where S Then we handled T, which is also going to set `wrap=0`, so our result for `J{T}` will not be made into `J{T} where T`. where I{T} (wrap 0) where J{S} where S Here we then had finished handling all the dependencies for J{S} where S, which resulted in an upper bound assignment of J{T} where I{T} where J{T} Next, we handle I{T}, though it is now unused, so while we will make `I{T} where T` (via innervars) here for it, this goes unuesd. And finally, we had our resulting clause: where J{T} But it is missing the `where T`, since `I` (from lhs) was discarded. Thus we need to add that back now, when handling some innervars, if we see our term got duplicated to a higher part of the bounds before reaching this handling for its placement movement. Co-authored-by: N5N3 <2642243996@qq.com> --- src/subtype.c | 119 +++++++++++++++++++++++++---------- test/compiler/inference.jl | 2 +- test/subtype.jl | 124 ++++++++++++++++++++++--------------- 3 files changed, 163 insertions(+), 82 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index f67e37ee079fc..5190d28ce3e0c 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2733,16 +2733,34 @@ static jl_value_t *omit_bad_union(jl_value_t *u, jl_tvar_t *t) if (jl_is_unionall(u)) { jl_tvar_t *var = ((jl_unionall_t *)u)->var; jl_value_t *ub = var->ub, *body = ((jl_unionall_t *)u)->body; - JL_GC_PUSH3(&ub, &body, &var); assert(var != t); - ub = omit_bad_union(ub, t); - body = omit_bad_union(body, t); - if (ub != NULL && body != NULL && !jl_has_typevar(var->lb, t)) { - if (ub != var->ub) { - var = jl_new_typevar(var->name, var->lb, ub); - body = jl_substitute_var(body, ((jl_unionall_t *)u)->var, (jl_value_t *)var); + if (!jl_has_typevar(var->lb, t)) { + JL_GC_PUSH3(&ub, &body, &var); + body = omit_bad_union(body, t); + if (!jl_has_typevar(body, var)) { + res = body; + } + else { + ub = omit_bad_union(ub, t); + if (ub == jl_bottom_type && var->lb != ub) { + res = jl_bottom_type; + } + else if (obviously_egal(var->lb, ub)) { + JL_TRY { + res = jl_substitute_var(body, var, ub); + } + JL_CATCH { + res = jl_bottom_type; + } + } + else { + if (ub != var->ub) { + var = jl_new_typevar(var->name, var->lb, ub); + body = jl_substitute_var(body, ((jl_unionall_t *)u)->var, (jl_value_t *)var); + } + res = jl_new_struct(jl_unionall_type, var, body); + } } - res = jl_new_struct(jl_unionall_type, var, body); } JL_GC_POP(); } @@ -2752,11 +2770,13 @@ static jl_value_t *omit_bad_union(jl_value_t *u, jl_tvar_t *t) JL_GC_PUSH2(&a, &b); a = omit_bad_union(a, t); b = omit_bad_union(b, t); - res = a == NULL ? b : - b == NULL ? a : - simple_join(a, b); + res = simple_join(a, b); JL_GC_POP(); } + else { + res = jl_bottom_type; + } + assert(res != NULL); return res; } @@ -2800,9 +2820,8 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind newvar = jl_new_typevar(vb->var->name, vb->lb, vb->ub); // remove/replace/rewrap free occurrences of this var in the environment - jl_varbinding_t *btemp = e->vars; jl_varbinding_t *wrap = NULL; - while (btemp != NULL) { + for (jl_varbinding_t *btemp = e->vars; btemp != NULL; btemp = btemp->prev) { if (jl_has_typevar(btemp->lb, vb->var)) { if (vb->lb == (jl_value_t*)btemp->var) { JL_GC_POP(); @@ -2819,17 +2838,12 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind else if (btemp->lb == (jl_value_t*)vb->var) { btemp->lb = vb->lb; } - else if (btemp->depth0 == vb->depth0 && !jl_has_typevar(vb->lb, btemp->var) && - !jl_has_typevar(vb->ub, btemp->var) && jl_has_typevar(btemp->ub, vb->var)) { + else if (btemp->depth0 == vb->depth0 && !jl_has_typevar(vb->lb, btemp->var) && !jl_has_typevar(vb->ub, btemp->var)) { // if our variable is T, and some outer variable has constraint S = Ref{T}, // move the `where T` outside `where S` instead of putting it here. issue #21243. - if (newvar != vb->var) { + if (newvar != vb->var) btemp->lb = jl_substitute_var(btemp->lb, vb->var, (jl_value_t*)newvar); - btemp->ub = jl_substitute_var(btemp->ub, vb->var, (jl_value_t*)newvar); - } wrap = btemp; - btemp = btemp->prev; - continue; } else { btemp->lb = jl_new_struct(jl_unionall_type, vb->var, btemp->lb); @@ -2839,7 +2853,7 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind if (jl_has_typevar(btemp->ub, vb->var)) { if (vb->ub == (jl_value_t*)btemp->var) { btemp->ub = omit_bad_union(btemp->ub, vb->var); - if (btemp->ub == NULL) { + if (btemp->ub == jl_bottom_type && btemp->ub != btemp->lb) { JL_GC_POP(); return jl_bottom_type; } @@ -2852,13 +2866,22 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind res = jl_bottom_type; } } - else if (btemp->ub == (jl_value_t*)vb->var) + else if (btemp->ub == (jl_value_t*)vb->var) { + // TODO: this loses some constraints, such as in this test, where we replace T4<:S3 (e.g. T4==S3 since T4 only appears covariantly once) with T4<:Any + // a = Tuple{Float64,T3,T4} where T4 where T3 + // b = Tuple{S2,Tuple{S3},S3} where S2 where S3 + // Tuple{Float64, T3, T4} where {S3, T3<:Tuple{S3}, T4<:S3} btemp->ub = vb->ub; + } + else if (btemp->depth0 == vb->depth0 && !jl_has_typevar(vb->lb, btemp->var) && !jl_has_typevar(vb->ub, btemp->var)) { + if (newvar != vb->var) + btemp->ub = jl_substitute_var(btemp->ub, vb->var, (jl_value_t*)newvar); + wrap = btemp; + } else btemp->ub = jl_new_struct(jl_unionall_type, vb->var, btemp->ub); assert((jl_value_t*)btemp->var != btemp->ub); } - btemp = btemp->prev; } if (wrap) { @@ -2867,9 +2890,9 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind if (wrap->innervars == NULL) wrap->innervars = jl_alloc_array_1d(jl_array_any_type, 0); jl_array_ptr_1d_push(wrap->innervars, (jl_value_t*)newvar); + // TODO: should we move all the innervars here too? } - // if `v` still occurs, re-wrap body in `UnionAll v` or eliminate the UnionAll if (jl_has_typevar(res, vb->var)) { if (varval) { @@ -2895,12 +2918,27 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind } } - if (res != jl_bottom_type && vb->innervars != NULL) { - int i; - for(i=0; i < jl_array_len(vb->innervars); i++) { + if (vb->innervars != NULL) { + for (size_t i = 0; i < jl_array_len(vb->innervars); i++) { jl_tvar_t *var = (jl_tvar_t*)jl_array_ptr_ref(vb->innervars, i); - if (jl_has_typevar(res, var)) - res = jl_type_unionall((jl_tvar_t*)var, res); + // the `btemp->prev` walk is only giving a sort of post-order guarantee (since we are + // iterating 2 trees at once), so once we set `wrap`, there might remain other branches + // of the type walk that now still may have incomplete bounds: finish those now too + jl_varbinding_t *wrap = NULL; + for (jl_varbinding_t *btemp = e->vars; btemp != NULL; btemp = btemp->prev) { + if (btemp->depth0 == vb->depth0 && (jl_has_typevar(btemp->lb, var) || jl_has_typevar(btemp->ub, var))) { + wrap = btemp; + } + } + if (wrap) { + if (wrap->innervars == NULL) + wrap->innervars = jl_alloc_array_1d(jl_array_any_type, 0); + jl_array_ptr_1d_push(wrap->innervars, (jl_value_t*)var); + } + else if (res != jl_bottom_type) { + if (jl_has_typevar(res, var)) + res = jl_type_unionall((jl_tvar_t*)var, res); + } } } @@ -2979,7 +3017,7 @@ static jl_value_t *intersect_unionall_(jl_value_t *t, jl_unionall_t *u, jl_stenv JL_GC_PUSH1(&res); vb->ub = omit_bad_union(vb->ub, u->var); JL_GC_POP(); - if (vb->ub == NULL) + if (vb->ub == jl_bottom_type && vb->ub != vb->lb) res = jl_bottom_type; } } @@ -3361,6 +3399,23 @@ static int subtype_by_bounds(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) JL_NOT return compareto_var(x, (jl_tvar_t*)y, e, -1) || compareto_var(y, (jl_tvar_t*)x, e, 1); } +static int has_typevar_via_env(jl_value_t *x, jl_tvar_t *t, jl_stenv_t *e) +{ + if (e->Loffset == 0) { + jl_varbinding_t *temp = e->vars; + while (temp != NULL) { + if (temp->var == t) + break; + if (temp->lb == temp->ub && + temp->lb == (jl_value_t *)t && + jl_has_typevar(x, temp->var)) + return 1; + temp = temp->prev; + } + } + return jl_has_typevar(x, t); +} + // `param` means we are currently looking at a parameter of a type constructor // (as opposed to being outside any type constructor, or comparing variable bounds). // this is used to record the positions where type variables occur for the @@ -3488,8 +3543,8 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa if (R) flip_offset(e); if (!ccheck) return jl_bottom_type; - if ((jl_has_typevar(xub, (jl_tvar_t*)y) || jl_has_typevar(xub, (jl_tvar_t*)x)) && - (jl_has_typevar(yub, (jl_tvar_t*)x) || jl_has_typevar(yub, (jl_tvar_t*)y))) { + if ((has_typevar_via_env(xub, (jl_tvar_t*)y, e) || has_typevar_via_env(xub, (jl_tvar_t*)x, e)) && + (has_typevar_via_env(yub, (jl_tvar_t*)x, e) || has_typevar_via_env(yub, (jl_tvar_t*)y, e))) { // TODO: This doesn't make much sense. // circular constraint. the result will be Bottom, but in the meantime // we need to avoid computing intersect(xub, yub) since it won't terminate. diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index d97f5d3a8a095..3ea296d908ef9 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -946,7 +946,7 @@ end # issue #21410 f21410(::V, ::Pair{V,E}) where {V, E} = E -@test code_typed(f21410, Tuple{Ref, Pair{Ref{T},Ref{T}} where T<:Number})[1].second == +@test only(Base.return_types(f21410, Tuple{Ref, Pair{Ref{T},Ref{T}} where T<:Number})) == Type{E} where E <: (Ref{T} where T<:Number) # issue #21369 diff --git a/test/subtype.jl b/test/subtype.jl index 2ec2a8d89e5e0..8a9981107a74b 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -910,11 +910,11 @@ function test_intersection() # both of these answers seem acceptable #@testintersect(Tuple{T,T} where T<:Union{UpperTriangular, UnitUpperTriangular}, # Tuple{AbstractArray{T,N}, AbstractArray{T,N}} where N where T, - # Union{Tuple{T,T} where T<:UpperTriangular, - # Tuple{T,T} where T<:UnitUpperTriangular}) + # Union{Tuple{T,T} where T<:UpperTriangular{T1}, + # Tuple{T,T} where T<:UnitUpperTriangular{T1}} where T) @testintersect(Tuple{T,T} where T<:Union{UpperTriangular, UnitUpperTriangular}, Tuple{AbstractArray{T,N}, AbstractArray{T,N}} where N where T, - Tuple{T,T} where T<:Union{UpperTriangular, UnitUpperTriangular}) + Tuple{T,T} where {T1, T<:Union{UpperTriangular{T1}, UnitUpperTriangular{T1}}}) @testintersect(DataType, Type, DataType) @testintersect(DataType, Type{T} where T<:Integer, Type{T} where T<:Integer) @@ -1211,12 +1211,12 @@ let a = Tuple{Float64,T3,T4} where T4 where T3, b = Tuple{S2,Tuple{S3},S3} where S2 where S3 I1 = typeintersect(a, b) I2 = typeintersect(b, a) - @test I1 <: I2 + @test_broken I1 <: I2 @test I2 <: I1 @test I1 <: a @test I2 <: a @test_broken I1 <: b - @test_broken I2 <: b + @test I2 <: b end let a = Tuple{T1,Tuple{T1}} where T1, b = Tuple{Float64,S3} where S3 @@ -1233,12 +1233,12 @@ let a = Tuple{5,T4,T5} where T4 where T5, b = Tuple{S2,S3,Tuple{S3}} where S2 where S3 I1 = typeintersect(a, b) I2 = typeintersect(b, a) - @test I1 <: I2 + @test_broken I1 <: I2 @test I2 <: I1 @test I1 <: a @test I2 <: a @test_broken I1 <: b - @test_broken I2 <: b + @test I2 <: b end let a = Tuple{T2,Tuple{T4,T2}} where T4 where T2, b = Tuple{Float64,Tuple{Tuple{S3},S3}} where S3 @@ -1248,12 +1248,12 @@ let a = Tuple{Tuple{T2,4},T6} where T2 where T6, b = Tuple{Tuple{S2,S3},Tuple{S2}} where S2 where S3 I1 = typeintersect(a, b) I2 = typeintersect(b, a) - @test I1 <: I2 + @test_broken I1 <: I2 @test I2 <: I1 @test I1 <: a @test I2 <: a @test_broken I1 <: b - @test_broken I2 <: b + @test I2 <: b end let a = Tuple{T3,Int64,Tuple{T3}} where T3, b = Tuple{S3,S3,S4} where S4 where S3 @@ -1898,27 +1898,23 @@ end # issue #38081 struct AlmostLU{T, S<:AbstractMatrix{T}} end -let X1 = Tuple{AlmostLU, Vector{T}} where T, - X2 = Tuple{AlmostLU{S, X} where X<:Matrix, Vector{S}} where S<:Union{Float32, Float64}, - I = Tuple{AlmostLU{T, S} where S<:Matrix{T}, Vector{T}} where T<:Union{Float32, Float64} - @testintersect(X1, X2, I) -end +@testintersect(Tuple{AlmostLU, Vector{T}} where T, + Tuple{AlmostLU{S, X} where X<:Matrix, Vector{S}} where S<:Union{Float32, Float64}, + Tuple{AlmostLU{T, X} where X<:Matrix{T}, Vector{T}} where T<:Union{Float32, Float64}) -let - # issue #22787 - @testintersect(Tuple{Type{Q}, Q, Ref{Q}} where Q<:Ref, - Tuple{Type{S}, Union{Ref{S}, Ref{R}}, R} where R where S, - !Union{}) +# issue #22787 +@testintersect(Tuple{Type{Q}, Q, Ref{Q}} where Q<:Ref, + Tuple{Type{S}, Union{Ref{S}, Ref{R}}, R} where R where S, + !Union{}) - t = typeintersect(Tuple{Type{T}, T, Ref{T}} where T, - Tuple{Type{S}, Ref{S}, S} where S) - @test_broken t != Union{} +t = typeintersect(Tuple{Type{T}, T, Ref{T}} where T, + Tuple{Type{S}, Ref{S}, S} where S) +@test_broken t != Union{} # optimal solution: Tuple{Type{T}, Ref{T}, Ref{T}} where T>:Ref - # issue #38279 - t = typeintersect(Tuple{<:Array{T, N}, Val{T}} where {T<:Real, N}, - Tuple{<:Array{T, N}, Val{<:AbstractString}} where {T<:Real, N}) - @test t == Tuple{<:Array{Union{}, N}, Val{Union{}}} where N -end +# issue #38279 +t = typeintersect(Tuple{<:Array{T, N}, Val{T}} where {T<:Real, N}, + Tuple{<:Array{T, N}, Val{<:AbstractString}} where {T<:Real, N}) +@test t == Tuple{<:Array{Union{}, N}, Val{Union{}}} where N # issue #36951 @testintersect(Type{T} where T>:Missing, @@ -1956,10 +1952,23 @@ end # issue #34170 let A = Tuple{Type{T} where T<:Ref, Ref, Union{T, Union{Ref{T}, T}} where T<:Ref}, B = Tuple{Type{T}, Ref{T}, Union{Int, Ref{T}, T}} where T - I = typeintersect(A,B) # this was a case where <: disagreed with === (due to a badly-normalized type) - @test I == typeintersect(A,B) - @test I == Tuple{Type{T}, Ref{T}, Ref} where T<:Ref + I = _type_intersect(B, A) + @test I == _type_intersect(B, A) == Union{Tuple{Type{T}, Ref{T}, Ref{T}} where T<:Ref, Tuple{Type{T}, Ref{T}, T} where T<:Ref} + I = typeintersect(B, A) + @test I == typeintersect(B, A) == Tuple{Type{T}, Ref{T}, Union{Ref{T}, T}} where T<:Ref + + I = _type_intersect(A, B) + @test !Base.has_free_typevars(I) + J = Tuple{Type{T1}, Ref{T1}, Ref} where {T, T1<:Union{Ref, Ref{T}}} + @test I == _type_intersect(A, B) == J + @test_broken I == Tuple{Type{T}, Ref{T}, T1} where {T<:Ref, T1<:Union{T, Ref{T}}} # a better result, == to the result with arguments switched + + I = typeintersect(A, B) + @test !Base.has_free_typevars(I) + J = Tuple{Type{T1}, Ref{T1}, Ref} where {T, T1<:Union{Ref, Ref{T}}} + @test I == typeintersect(A, B) == J + end # issue #39218 @@ -1988,20 +1997,14 @@ let A = Tuple{Type{<:Union{Number, T}}, Ref{T}} where T, end # issue #39698 -let T = Type{T} where T<:(AbstractArray{I}) where I<:(Base.IteratorsMD.CartesianIndex), - S = Type{S} where S<:(Base.IteratorsMD.CartesianIndices{A, B} where B<:Tuple{Vararg{Any, A}} where A) - I = typeintersect(T, S) - @test_broken I <: T - @test I <: S - @test_broken I == typeintersect(S, T) -end +@testintersect(Type{T} where T<:(AbstractArray{I}) where I<:(Base.IteratorsMD.CartesianIndex), + Type{S} where S<:(Base.IteratorsMD.CartesianIndices{A, B} where B<:Tuple{Vararg{Any, A}} where A), + Type{S} where {N, S<:(Base.IteratorsMD.CartesianIndices{N, B} where B<:Tuple{Vararg{Any, N}})}) # issue #39948 -let A = Tuple{Array{Pair{T, JT} where JT<:Ref{T}, 1} where T, Vector}, - I = typeintersect(A, Tuple{Vararg{Vector{T}}} where T) - @test I <: A - @test !Base.has_free_typevars(I) -end +@testintersect(Tuple{Array{Pair{T, JT} where JT<:Ref{T}, 1} where T, Vector}, + Tuple{Vararg{Vector{T}}} where T, + Tuple{Array{Pair{T, JT} where JT<:Ref{T}, 1}, Array{Pair{T, JT} where JT<:Ref{T}, 1}} where T) # issue #8915 struct D8915{T<:Union{Float32,Float64}} @@ -2226,13 +2229,10 @@ end Val{Tuple{Tuple{Any, Vararg{Any, N}}}} where {N}) let A = Pair{NTuple{N, Int}, Val{N}} where N, - Bs = (Pair{<:Tuple{Int, Vararg{Int}}, <:Val}, - Pair{Tuple{Int, Vararg{Int,N1}}, Val{N2}} where {N1,N2}) - Cerr = Pair{Tuple{Int, Vararg{Int,N}}, Val{N}} where N - for B in Bs - @testintersect(A, B, !Cerr) - @testintersect(A, B, !Union{}) - end + C = Pair{Tuple{Int, Vararg{Int,N1}}, Val{N2}} where {N1,N2}, + B = Pair{<:Tuple{Int, Vararg{Int}}, <:Val} + @testintersect A B C + @testintersect A C C end # issue #43064 @@ -2487,3 +2487,29 @@ end # requires assertions enabled (to test union-split in `obviously_disjoint`) @test !<:(Tuple{Type{Int}, Int}, Tuple{Type{Union{Int, T}}, T} where T<:Union{Int8,Int16}) @test <:(Tuple{Type{Int}, Int}, Tuple{Type{Union{Int, T}}, T} where T<:Union{Int8,Int}) + +let A = Tuple{Type{T}, T, Val{T}} where T, + B = Tuple{Type{S}, Val{S}, Val{S}} where S + @test_broken typeintersect(A, B) != Union{} + # optimal = Tuple{Type{T}, Val{T}, Val{T}} where T>:Val +end +let A = Tuple{Type{T}, T, Val{T}} where T<:Val, + B = Tuple{Type{S}, Val{S}, Val{S}} where S<:Val + @test_broken typeintersect(A, B) != Union{} + # optimal = Tuple{Type{Val}, Val{Val}, Val{Val}} +end +let A = Tuple{Type{T}, T, Val{T}} where T<:Val, + B = Tuple{Type{S}, Val{S}, Val{S}} where S<:Val{A} where A + @test typeintersect(A, B) == Union{} +end +let A = Tuple{Type{T}, T, Val{T}} where T<:Val{<:Val}, + B = Tuple{Type{S}, Val{S}, Val{S}} where S<:Val + @test_broken typeintersect(A, B) != Union{} + # optimal = Tuple{Type{Val{<:Val}}, Val{Val{<:Val}}, Val{Val{<:Val}}} +end +let T = Tuple{Union{Type{T}, Type{S}}, Union{Val{T}, Val{S}}, Union{Val{T}, S}} where T<:Val{A} where A where S<:Val, + S = Tuple{Type{T}, T, Val{T}} where T<:(Val{S} where S<:Val) + # optimal = Union{}? + @test typeintersect(T, S) == Tuple{Type{T}, T, Val{T}} where T<:(Val{S} where S<:Val) + @test typeintersect(S, T) == Tuple{Union{Type{T}, Type{T1}}, Union{Val{T1}, Val{S1}, T}, Union{S, S1}} where {T<:(Val{S} where S<:Val), S<:Val{T}, T1<:Val, S1<:Val{T1}} +end From d82a5b518fc871e7e7340517e1133d670118e4aa Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Thu, 13 Apr 2023 13:19:34 -0400 Subject: [PATCH 509/775] deps: bump libtracyclient to v0.9.1+1 This enables broadcast messages so that Tracy processes can be listed by the profiler, which is a nice quality-of-life feature. --- deps/checksums/libtracyclient | 66 +++++++++++++++++------------------ deps/libtracyclient.mk | 1 - deps/libtracyclient.version | 2 +- 3 files changed, 33 insertions(+), 36 deletions(-) diff --git a/deps/checksums/libtracyclient b/deps/checksums/libtracyclient index 506377a3f4e13..55a436b059c42 100644 --- a/deps/checksums/libtracyclient +++ b/deps/checksums/libtracyclient @@ -1,34 +1,32 @@ -LibTracyClient.v0.9.0+1.aarch64-apple-darwin.tar.gz/md5/621591ea1b72b07a0be82f87ed29a456 -LibTracyClient.v0.9.0+1.aarch64-apple-darwin.tar.gz/sha512/d053db12a0256bd60730f9b9a73ed6308c4cecb3f4a31cb979e1ecd8afbec5e3217b4a4f6355e24fc0c3bcc90dc9a83bf1be1dee467544e15ae6597d9d1a8d01 -LibTracyClient.v0.9.0+1.aarch64-linux-gnu.tar.gz/md5/7e2183c4cba6108e39c58e57ba31eb53 -LibTracyClient.v0.9.0+1.aarch64-linux-gnu.tar.gz/sha512/a912d329e065aae7a9d5b4392f6c292b68fed5cbd83b06bfddf925601f84bde4a76993864ecf3750fd216313630632157ff2f3f9e659caa71530e31aa738c72d -LibTracyClient.v0.9.0+1.aarch64-linux-musl.tar.gz/md5/a9d1b9700f9ed3c8c70480da7ebf326d -LibTracyClient.v0.9.0+1.aarch64-linux-musl.tar.gz/sha512/e9e928dda72f0b1aa9a92809f6f8b6c9d3c7e99f30d1944725e6d0eae0eeba34928e0262172f6e1ccd10f99dfb44d2e39537663a4ab72ebb3ce65f8f1b001c13 -LibTracyClient.v0.9.0+1.armv6l-linux-gnueabihf.tar.gz/md5/7c1541edbe31bfb9e43f4ec09a3aa748 -LibTracyClient.v0.9.0+1.armv6l-linux-gnueabihf.tar.gz/sha512/ab8c2502c0fa743538b8929756f283514ee4c69a6fc65555dca7b95021c36ce827ee33e8594d0447f15fa9bd1df873b1a1a75f876989813386f46a803c504c06 -LibTracyClient.v0.9.0+1.armv6l-linux-musleabihf.tar.gz/md5/2904a775192b8bb53c170f28d3588ea0 -LibTracyClient.v0.9.0+1.armv6l-linux-musleabihf.tar.gz/sha512/1b1288619a72e30a1e414295591d93e122c9c478e574e31c09f49e6ee3b665a64a883cd367566cec9ba95abb5fdcc51056d9853400f441ddd0f27a369a20bae3 -LibTracyClient.v0.9.0+1.armv7l-linux-gnueabihf.tar.gz/md5/7773f17dab1acdcb6b9e749dfb04f727 -LibTracyClient.v0.9.0+1.armv7l-linux-gnueabihf.tar.gz/sha512/49b7a433aa9945cfd20702584916ab24cf2e35a67804635c11726576763a09c5f2e578002e175d9ca3e109e29c454b4ad5db2e267ed5aeb002eff45965a74704 -LibTracyClient.v0.9.0+1.armv7l-linux-musleabihf.tar.gz/md5/9c1799102529603793bf180c2fd432ec -LibTracyClient.v0.9.0+1.armv7l-linux-musleabihf.tar.gz/sha512/ab2fcde7a59754b15d36f39e88fddbf1f198e15221680b9cd0dcb7eb43becc498d17ca1763ec576479646f0d4a1947a9b39f340db800e859751105d7d7aa5ed6 -LibTracyClient.v0.9.0+1.i686-linux-gnu.tar.gz/md5/4f64c950d319cdeeec379cef58f41a13 -LibTracyClient.v0.9.0+1.i686-linux-gnu.tar.gz/sha512/9258ec31e5023e7b503c36f1d76d4e6c0b7492abeb177ffe772a64da1d5db6f199031d22956a7702a809b1263b49aef614763816d1a18f5590d0a00b3f6243f1 -LibTracyClient.v0.9.0+1.i686-linux-musl.tar.gz/md5/d5524ddb8c8537b02b737bd7f2a68275 -LibTracyClient.v0.9.0+1.i686-linux-musl.tar.gz/sha512/f149ea48cff01f6cd9da40692eed9900d5b2ff3a3e10e27bb10b62a89034f37f9a68fc080b97d41efb95718472898c8cc6271a8934f907275cde19f44582de08 -LibTracyClient.v0.9.0+1.i686-w64-mingw32.tar.gz/md5/c415459220b24c0a67553e4691801639 -LibTracyClient.v0.9.0+1.i686-w64-mingw32.tar.gz/sha512/57c8826be9fb049fa418a72dc8fbf576b6fbf45da1662f07ed041b9e55c36e487c02c43a1e64003d76a0040f0e998201e8b0d3853960023ea440a2daadcb4f77 -LibTracyClient.v0.9.0+1.powerpc64le-linux-gnu.tar.gz/md5/5eacaa3672522f45733595182ba643fc -LibTracyClient.v0.9.0+1.powerpc64le-linux-gnu.tar.gz/sha512/4edf154a9ac126fe31879b7962af127d99e5afd19dc046275ddb26b9f455431fd6fd398373a01d6f8865b090cb87ed521a5919b8037d569c569c36c7a8a2f72f -LibTracyClient.v0.9.0+1.x86_64-apple-darwin.tar.gz/md5/bb517fdccbf51c7f0889919735889d65 -LibTracyClient.v0.9.0+1.x86_64-apple-darwin.tar.gz/sha512/a3587248776c859e60d367e91908e36fd9f7fd3e27320dfac2c7527ee120c1a2652b2da1c0c1a006d2c28c7f93992dd4a3b2565e7f2c5feec6a5661cc4cf080e -LibTracyClient.v0.9.0+1.x86_64-linux-gnu.tar.gz/md5/2dd1cbcf917c7df9df110b99f393b916 -LibTracyClient.v0.9.0+1.x86_64-linux-gnu.tar.gz/sha512/3d0dc4cd3df2528678a0305baa23d5cda97406f73a3def3ff2fd8ee2517f07051e19719faf99604fddb3aa5b20574b92b240f7898d392d9e21431f275c0a8aa8 -LibTracyClient.v0.9.0+1.x86_64-linux-musl.tar.gz/md5/d1b02aaf45f13ba34f4f1138b83f8ce7 -LibTracyClient.v0.9.0+1.x86_64-linux-musl.tar.gz/sha512/7c8a3748238d015de4be7074c1efe72b2cda9dbc23c2ab722750885cd01cd4a6ea6e37b241fc997d41ab3949154b4a5bddbfd8f3a59ca153e9b42136a154a02a -LibTracyClient.v0.9.0+1.x86_64-unknown-freebsd.tar.gz/md5/7bb6f98ab2a39a062293c95f91b959f0 -LibTracyClient.v0.9.0+1.x86_64-unknown-freebsd.tar.gz/sha512/72935612fbfb339003b9be38c64a53c6a19a58b8427485b4351f18933a2ec7a4f7bf00556996501ccd3857e8085910af72020e4507951eb8ee094287a82208ae -LibTracyClient.v0.9.0+1.x86_64-w64-mingw32.tar.gz/md5/f3b60a51e8f64ec62c07597f6c4e52f7 -LibTracyClient.v0.9.0+1.x86_64-w64-mingw32.tar.gz/sha512/3ef5916f9a441e8655569c803392987a39c3baa79ac9ac446760cc60577619a616ee1365673d5323eb1c5884a6bd9e283b4094cdcbf42eba6b409a0348643b25 -libtracyclient-5a1f5371b792c12aea324213e1dc738b2923ae21.tar.gz/md5/62791801e0ffb11a7d70c2d724a230be -libtracyclient-5a1f5371b792c12aea324213e1dc738b2923ae21.tar.gz/sha512/b0570e048eee08ba5e21e0f855b2346cad408b0b86cdf79c8bb3727bb0ab57167dc7988f4dd1ee4157b371135201b87d1491237c09a2934de65eb3b9e26fcdc2 +LibTracyClient.v0.9.1+1.aarch64-apple-darwin.tar.gz/md5/540617535443c918d42415d7e775456d +LibTracyClient.v0.9.1+1.aarch64-apple-darwin.tar.gz/sha512/5dc245327900a26f20692c76c6a3043a07ee88010b814bdded79460fd77cd587b69448b074a1afc931290ef7f445771aec71a003d6e425d42c75d2cc72bdf846 +LibTracyClient.v0.9.1+1.aarch64-linux-gnu.tar.gz/md5/d2a09ad722a1f15090dd0ae6ce9c37c7 +LibTracyClient.v0.9.1+1.aarch64-linux-gnu.tar.gz/sha512/b5e6f44bb4690226dd4176a43824193c7e1a7873cf75c2e261b6cb0a614aad172c0265b6aa89849328133de9894af94a4a38b4362ec8d706d03a0cad4fd1171a +LibTracyClient.v0.9.1+1.aarch64-linux-musl.tar.gz/md5/eccc851b7346590d2636ff585e4b1f55 +LibTracyClient.v0.9.1+1.aarch64-linux-musl.tar.gz/sha512/214dd6d7ce70ce11748091143a2e89dfc6b85c62424d971eb973b1126ee3da98d8285c2f5557c7b62523f76e513692947b5ef0f046bdf183da3ddd38554b4a97 +LibTracyClient.v0.9.1+1.armv6l-linux-gnueabihf.tar.gz/md5/200b940fb4b6c7f8cb6c621ae4bab347 +LibTracyClient.v0.9.1+1.armv6l-linux-gnueabihf.tar.gz/sha512/1213b716ed3680bb8b1a682099ef325a257e29811498a731553c4d6fc8f93831038d211720da1985f72f42c6409ea5e2aa262493557abecb6587d7db69bde737 +LibTracyClient.v0.9.1+1.armv6l-linux-musleabihf.tar.gz/md5/44dddf9ef55cd9d222a16eff2b2e14e7 +LibTracyClient.v0.9.1+1.armv6l-linux-musleabihf.tar.gz/sha512/ba887e97366e9ac9dbc43864b3d8cd8cdf2db571fb198593f2ae66790fb9cd5a12d4c29088a65bc103939ec029fa426925a0990c0a2b1441fece974b3dabce6c +LibTracyClient.v0.9.1+1.armv7l-linux-gnueabihf.tar.gz/md5/286bbb5c258fcd38224ff03a691cf474 +LibTracyClient.v0.9.1+1.armv7l-linux-gnueabihf.tar.gz/sha512/26e85ec00a794901d412bb54d1ea1cbd9c7972e2dcc6fbcad12d520088c4d6d86a8ff7398cff14e60741abb30028fcda39515b2a1ae1a225a3192e9e956a8641 +LibTracyClient.v0.9.1+1.armv7l-linux-musleabihf.tar.gz/md5/36b317542174c07c4e85fdffcf5e34c7 +LibTracyClient.v0.9.1+1.armv7l-linux-musleabihf.tar.gz/sha512/d9e28e6ebb4537ae4de75ae03bb28d170c7dc92731f6a8d6431ce2e5ee5ad717a04990a42b57898247a5059b8e0d93b5812d6ffc66c94c5571adec2ecfe0555d +LibTracyClient.v0.9.1+1.i686-linux-gnu.tar.gz/md5/1990c1f0701a3c7853fa57e54d214465 +LibTracyClient.v0.9.1+1.i686-linux-gnu.tar.gz/sha512/f899f109ad77f2b1964e94242b0a601128b063140190105fd44e43782036ef0134cdc2b6cb1faaf5b7c0742f4a168c7b7870f18d2d18b19fc94d8f269f3027d1 +LibTracyClient.v0.9.1+1.i686-linux-musl.tar.gz/md5/eeee122d2166e8a251bcee40d66daede +LibTracyClient.v0.9.1+1.i686-linux-musl.tar.gz/sha512/fcf0de849b8533065e61d5e1528cc1e882d2734c488a030728ec4915651bb2bd049536d9d76ecce3063647d0cd20e10033beb3d8de82e06c9c73e9eb41b12b03 +LibTracyClient.v0.9.1+1.i686-w64-mingw32.tar.gz/md5/0f42ad75bb75084e129b0e6fe5e86196 +LibTracyClient.v0.9.1+1.i686-w64-mingw32.tar.gz/sha512/28821145c8d3d7d8dc3e1db883478e53b4eacafa5f4deae965057ad6466c683de6bd9265a92f1d63ab587107fbb374f7167ff4be70c5fbdf09db9b57fb619b3e +LibTracyClient.v0.9.1+1.powerpc64le-linux-gnu.tar.gz/md5/1a2243ac3a4efa224da1eaace7aa9278 +LibTracyClient.v0.9.1+1.powerpc64le-linux-gnu.tar.gz/sha512/acd17f12afb523348de56f7fa84497468ec5f2f76d9622180e72bded7eb4eda4033e28ed20cb25c7c8049b086c994c10a6d97efae06a661ce7d0d65e5c8bbfd5 +LibTracyClient.v0.9.1+1.x86_64-apple-darwin.tar.gz/md5/34a75343f9aed8e6db252cc043b26b09 +LibTracyClient.v0.9.1+1.x86_64-apple-darwin.tar.gz/sha512/2eaf4e5ef1959110cecaf467bdc28e92d45862c4d83315fccafbf3ab4e498918f4d715b0303be4e563ac7ddb88c9d9cec71351183c5ec0055759a86341473232 +LibTracyClient.v0.9.1+1.x86_64-linux-gnu.tar.gz/md5/d6fcd4255ab1363412e85497770ab522 +LibTracyClient.v0.9.1+1.x86_64-linux-gnu.tar.gz/sha512/a879afb13a35d18c8ed5593cf83d48ac228c45c246fa013f5f067fa216b61d2dc5b8adfc4ced6043c331b982d050522b228a5792c707b9deff2fb65b37aa66ea +LibTracyClient.v0.9.1+1.x86_64-linux-musl.tar.gz/md5/0490919c558c5ae8c833934426e0dda4 +LibTracyClient.v0.9.1+1.x86_64-linux-musl.tar.gz/sha512/b37637e8530ad9b3cb58732bb35d9634aadaeb5a83815f602d5804d4fff1499ea49ce72300b03036ecd642d0dbd8f86d475b637673bc8d400f70e4cb4cf865eb +LibTracyClient.v0.9.1+1.x86_64-unknown-freebsd.tar.gz/md5/985a19b60f44349870c304c7a140cad8 +LibTracyClient.v0.9.1+1.x86_64-unknown-freebsd.tar.gz/sha512/514f01127dcc641ab4b66fac49c0470f6277bff37fbd82084482d3db72e1964d85655ca8aa135dbe08265d711d857ed861eba038a072f8e4fcffeefe3b11808d +LibTracyClient.v0.9.1+1.x86_64-w64-mingw32.tar.gz/md5/a777f0997a238c3f28a362e2998d92a2 +LibTracyClient.v0.9.1+1.x86_64-w64-mingw32.tar.gz/sha512/3aa49b49f696792d20265e9947b9a0dc4b888a482617513252176b0ac59db9069c965f01a8c1c253f73050eab3344d9d0b4c26a826ff9bfa92e36eb42814444d diff --git a/deps/libtracyclient.mk b/deps/libtracyclient.mk index bf84e25ccefc9..19c08f774aa0c 100644 --- a/deps/libtracyclient.mk +++ b/deps/libtracyclient.mk @@ -10,7 +10,6 @@ LIBTRACYCLIENT_SRCCACHE := $(SRCCACHE)/$(LIBTRACYCLIENT_SRC_DIR) LIBTRACYCLIENT_CMAKE := LIBTRACYCLIENT_CMAKE += -DBUILD_SHARED_LIBS=ON LIBTRACYCLIENT_CMAKE += -DTRACY_FIBERS=ON -LIBTRACYCLIENT_CMAKE += -DTRACY_NO_BROADCAST=ON LIBTRACYCLIENT_CMAKE += -DTRACY_NO_SAMPLING=ON LIBTRACYCLIENT_CMAKE += -DTRACY_ONLY_LOCALHOST=ON LIBTRACYCLIENT_CMAKE += -DTRACY_NO_CODE_TRANSFER=ON diff --git a/deps/libtracyclient.version b/deps/libtracyclient.version index 3c0e0ebd56fa3..2faa4327b7749 100644 --- a/deps/libtracyclient.version +++ b/deps/libtracyclient.version @@ -1,6 +1,6 @@ ## jll artifact LIBTRACYCLIENT_JLL_NAME := LibTracyClient -LIBTRACYCLIENT_JLL_VER := 0.9.1+0 +LIBTRACYCLIENT_JLL_VER := 0.9.1+1 ## source build LIBTRACYCLIENT_VER := 0.9.1 From 8e0cba566348e74a9451d0af23b0488afc678597 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Thu, 13 Apr 2023 13:54:23 -0400 Subject: [PATCH 510/775] timing: add envvars for subsystem enable and verbose metadata This commit adds the following environment variables: - `JULIA_TIMING_SUBSYSTEMS` which can be set to, e.g., "+INFERENCE,-GC,METHOD_MATCH" to enable INFERENCE and METHOD_MATCH and disable GC. - `JULIA_TIMING_METADATA_PRINT_LIMIT` which defaults to 10 and determines how many metadata items to add to a single timing zone before truncating This commit also includes other miscellaneous changes to incorporate review feedback. --- src/jitlayers.cpp | 4 +-- src/jlapi.c | 7 ++-- src/task.c | 2 -- src/timing.c | 81 +++++++++++++++++++++++++++++++++++++++++++---- src/timing.h | 15 +++++++++ 5 files changed, 96 insertions(+), 13 deletions(-) diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 75f2b244dacd3..2ca97289f62d9 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -251,7 +251,7 @@ static jl_callptr_t _jl_compile_codeinst( size_t i = 0; for (auto &def : emitted) { jl_code_instance_t *this_code = def.first; - if (i < 10) + if (i < jl_timing_print_limit) jl_timing_show_func_sig(this_code->def->specTypes, JL_TIMING_CURRENT_BLOCK); jl_llvm_functions_t decls = std::get<1>(def.second); @@ -297,7 +297,7 @@ static jl_callptr_t _jl_compile_codeinst( fptr = addr; i++; } - if (i > 10) + if (i > jl_timing_print_limit) jl_timing_printf(JL_TIMING_CURRENT_BLOCK, "... <%d methods truncated>", i - 10); uint64_t end_time = 0; diff --git a/src/jlapi.c b/src/jlapi.c index 8f5e3e6cb13dc..369f4d6ec3ff1 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -685,8 +685,11 @@ static void rr_detach_teleport(void) { JL_DLLEXPORT int jl_repl_entrypoint(int argc, char *argv[]) { #ifdef USE_TRACY - if (getenv("WAIT_FOR_TRACY")) - while (!TracyCIsConnected) ; // Wait for connection + // Apply e.g. JULIA_TIMING_SUBSYSTEMS="+GC,-INFERENCE" and + // JULIA_TIMING_METADATA_PRINT_LIMIT=20 + jl_timing_apply_env(); + if (getenv("JULIA_WAIT_FOR_TRACY")) + while (!TracyCIsConnected) jl_cpu_pause(); // Wait for connection #endif // no-op on Windows, note that the caller must have already converted diff --git a/src/task.c b/src/task.c index e73b19563e336..123cfaac00163 100644 --- a/src/task.c +++ b/src/task.c @@ -1222,8 +1222,6 @@ CFI_NORETURN _start_task(); } -const char* fiber = "task"; - STATIC_OR_JS void NOINLINE JL_NORETURN _start_task(void) { CFI_NORETURN diff --git a/src/timing.c b/src/timing.c index c3ba3809884bf..7337902010a3d 100644 --- a/src/timing.c +++ b/src/timing.c @@ -36,6 +36,11 @@ JL_DLLEXPORT uint64_t jl_timing_enable_mask = 0xFFFFFFFFFFFFFFFF; #endif JL_DLLEXPORT uint64_t jl_timing_counts[(int)JL_TIMING_LAST] = {0}; + +// Used to as an item limit when several strings of metadata can +// potentially be associated with a single timing zone. +JL_DLLEXPORT uint32_t jl_timing_print_limit = 10; + const char *jl_timing_names[(int)JL_TIMING_LAST] = { #define X(name) #name @@ -100,14 +105,16 @@ void jl_timing_block_enter_task(jl_task_t *ct, jl_ptls_t ptls, jl_timing_block_t jl_timing_block_t *jl_timing_block_exit_task(jl_task_t *ct, jl_ptls_t ptls) { #ifdef USE_TRACY - // Tracy is fairly strict about not leaving a fiber that - // hasn't been entered, which happens often when - // connecting to a running Julia session. + // Tracy is fairly strict about not leaving a fiber that hasn't + // been entered, which happens often when connecting to a running + // Julia session. + // + // Eventually, Tracy will support telling the server which fibers + // are active upon connection, but until then we work around the + // problem by not explicitly leaving the fiber at all. // - // Eventually, Tracy will support telling the server that - // which fibers are active upon connection, but until then - // work around around the problem by just entering the new - // fiber directly, which implicitly leaves any active fibers. + // Later when we enter the new fiber directly, that will cause the + // the active fiber to be left implicitly. //TracyCFiberLeave; #endif @@ -220,11 +227,71 @@ JL_DLLEXPORT int jl_timing_set_enable(const char *subsystem, uint8_t enabled) return -1; } +static void jl_timing_set_enable_from_env(void) +{ + const char *env = getenv("JULIA_TIMING_SUBSYSTEMS"); + if (!env) + return; + + // Copy `env`, so that we can modify it + size_t sz = strlen(env) + 1; + char *env_copy = (char *)malloc(sz); + memcpy(env_copy, env, sz); + + char *subsystem = env_copy; + char *ch = subsystem; + uint8_t enable = 1; + while (1) { + // +SUBSYSTEM means enable, -SUBSYSTEM means disable + if (*subsystem == '+' || *subsystem == '-') + enable = (*subsystem++ == '+'); + + if (*ch == ',') { + *ch++ = '\0'; + if ((*subsystem != '\0') && jl_timing_set_enable(subsystem, enable)) + fprintf(stderr, "warning: unable to configure timing for non-existent subsystem \"%s\"\n", subsystem); + + subsystem = ch; + enable = 1; + } + else if (*ch == '\0') { + if ((*subsystem != '\0') && jl_timing_set_enable(subsystem, enable)) + fprintf(stderr, "warning: unable to configure timing for non-existent subsystem \"%s\"\n", subsystem); + + break; + } + else ch++; + } + free(env_copy); +} + +static void jl_timing_set_print_limit_from_env(void) +{ + const char *const env = getenv("JULIA_TIMING_METADATA_PRINT_LIMIT"); + if (!env) + return; + + char *endp; + long value = strtol(env, &endp, 10); + if (*endp == '\0' && value >= 0 && value <= UINT32_MAX) + jl_timing_print_limit = (uint32_t)value; +} + +void jl_timing_apply_env(void) +{ + // JULIA_TIMING_SUBSYSTEMS + jl_timing_set_enable_from_env(); + + // JULIA_TIMING_METADATA_PRINT_LIMIT + jl_timing_set_print_limit_from_env(); +} + #else void jl_init_timing(void) { } void jl_destroy_timing(void) { } JL_DLLEXPORT int jl_timing_set_enable(const char *subsystem, uint8_t enabled) { return -1; } +JL_DLLEXPORT uint32_t jl_timing_print_limit = 0; #endif diff --git a/src/timing.h b/src/timing.h index d7dc53ad0e8be..d6e4c6d80ab63 100644 --- a/src/timing.h +++ b/src/timing.h @@ -16,6 +16,21 @@ void jl_destroy_timing(void) JL_NOTSAFEPOINT; // Returns -1 if no matching sub-system was found. int jl_timing_set_enable(const char *subsystem, uint8_t enabled); +// Check for environment vars "JULIA_TIMING_METADATA_PRINT_LIMIT" and +// "JULIA_TIMING_SUBSYSTEMS" and if present apply these to the metadata +// print limit and the timings enable mask, respectively. +// +// For example, to enable INFERENCE and METHOD_MATCH and disable GC: +// JULIA_TIMING_SUBSYSTEMS="+INFERENCE,-GC,+METHOD_MATCH" +// +// For example, to increase the metadata item print limit from 10 to 20: +// JULIA_TIMING_METADATA_PRINT_LIMIT=20 +void jl_timing_apply_env(void); + +// Configurable item limit, runtime code should use this to limit printing +// when adding potentially many items of metadata to a single timing zone. +extern uint32_t jl_timing_print_limit; + #ifdef __cplusplus } #endif From b987396a14345ff0e6232ab9090661e60154b20f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Thi=C3=A9baut?= Date: Thu, 13 Apr 2023 20:32:34 +0200 Subject: [PATCH 511/775] Fix doc. about linear from/to Cartesian indices (#49343) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since Julia (ordinary) arrays have 1-based indices and are in column-major order, the linear index `k` and the Cartesian index `(i,j)` of an element in a `m×n` Julia array are related by: `k = i + m*(j - 1)`. The examples of conversion between linear and Cartesian indices in the doc. use incorrect formulae although the results were correct in the specific element at `(1,3)`. This can be easily checked for other indices than `(1,3)` with a simple `2×3` array (as in the examples) built by `A = reshape(collect(1:6),2,3)`. --- base/indices.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/indices.jl b/base/indices.jl index 0584b32941132..6a28cf63316e6 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -30,7 +30,7 @@ to implement indexing (and indexed assignment) with a single `Int` index; all other indexing expressions — including multidimensional accesses — will be recomputed to the linear index. For example, if `A` were a `2×3` custom matrix with linear indexing, and we referenced `A[1, 3]`, this would be -recomputed to the equivalent linear index and call `A[5]` since `2*1 + 3 = 5`. +recomputed to the equivalent linear index and call `A[5]` since `1 + 2*(3 - 1) = 5`. See also [`IndexCartesian`](@ref). """ @@ -53,7 +53,7 @@ to implement indexing (and indexed assignment) with exactly `N` `Int` indices; all other indexing expressions — including linear indexing — will be recomputed to the equivalent Cartesian location. For example, if `A` were a `2×3` custom matrix with cartesian indexing, and we referenced `A[5]`, this would be -recomputed to the equivalent Cartesian index and call `A[1, 3]` since `5 = 2*1 + 3`. +recomputed to the equivalent Cartesian index and call `A[1, 3]` since `5 = 1 + 2*(3 - 1)`. It is significantly more expensive to compute Cartesian indices from a linear index than it is to go the other way. The former operation requires division — a very costly operation — whereas From f7554b5c9f0f580a9fcf5c7b8b9a83b678e2f48a Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Thu, 13 Apr 2023 14:34:43 -0400 Subject: [PATCH 512/775] deps: Update openblas `.tar.gz` checksum (#49311) This was an oversight from #49283. Oops! --- deps/checksums/openblas | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deps/checksums/openblas b/deps/checksums/openblas index 99577b1427805..5cd8d27baf25e 100644 --- a/deps/checksums/openblas +++ b/deps/checksums/openblas @@ -90,3 +90,5 @@ OpenBLAS.v0.3.23+0.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/feba9f9647e82992ba OpenBLAS.v0.3.23+0.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/b6c98a5a57764eef4940d81461f9706f905d376d165abdbd0fafbdd5802e34523ad15e6ee75a4550555b7c969630c43438d6cce3d6e37ac95e57b58bcc9d542c OpenBLAS.v0.3.23+0.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/732544eb61201b6dd7c27d5be376d50d OpenBLAS.v0.3.23+0.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/7b68cceb0bdb892ae74e2744f2a9139602a03e01d937188ca9c875d606d79f555594a5ff022b64d955613b6eb0026a26003011dc17382f019882d9c4c612e8e2 +openblas-394a9fbafe9010b76a2615c562204277a956eb52.tar.gz/md5/7ccaaaafc8176b87dc59d4e527ca4d9f +openblas-394a9fbafe9010b76a2615c562204277a956eb52.tar.gz/sha512/12235f0459469b483a393844c228be5ad4bc60575bbe4b3238198f2480b7b457e4b0609730ce6d99530bb82e1d16fdd2338ceed6d28c952e6fff0da7f571f863 From a1872091747b8f730395f142db57bc5fa6f3b0b8 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 13 Apr 2023 16:06:37 -0400 Subject: [PATCH 513/775] sroa_pass!: Mark statements that were updated (#49340) sroa_pass! can introduce new type information that inference did not see (e.g. when forwarding field accesses through a mutable). Currently, we just accept that in base, but there are external pass pipelines that run multiple rounds of inference and would like to refine these. Make this possible by introducing a new IR flag that sroa_pass! sets when it modifies a statement. There are currently no consumers in the Base pipeline, but the idea is that these can be passed into _ir_abstract_constant_propagation for refinement. --- base/compiler/optimize.jl | 4 ++++ base/compiler/ssair/passes.jl | 2 ++ 2 files changed, 6 insertions(+) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 84817b1bf6531..0ab1b14d4a185 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -32,6 +32,10 @@ const IR_FLAG_EFFECT_FREE = 0x01 << 4 const IR_FLAG_NOTHROW = 0x01 << 5 # This is :consistent const IR_FLAG_CONSISTENT = 0x01 << 6 +# An optimization pass has updated this statement in a way that may +# have exposed information that inference did not see. Re-running +# inference on this statement may be profitable. +const IR_FLAG_REFINED = 0x01 << 7 const TOP_TUPLE = GlobalRef(Core, :tuple) diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index a2508992bf290..2b50a6114865f 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1031,6 +1031,7 @@ function sroa_pass!(ir::IRCode, inlining::Union{Nothing,InliningState}=nothing) end compact[idx] = val === nothing ? nothing : val.val + compact[SSAValue(idx)][:flag] |= IR_FLAG_REFINED end non_dce_finish!(compact) @@ -1379,6 +1380,7 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse if use.kind === :getfield ir[SSAValue(use.idx)][:inst] = compute_value_for_use(ir, domtree, allblocks, du, phinodes, fidx, use.idx) + ir[SSAValue(use.idx)][:flag] |= IR_FLAG_REFINED elseif use.kind === :isdefined continue # already rewritten if possible elseif use.kind === :nopreserve From d6752ebebf7b417b23ab488deef8572ec8e0239b Mon Sep 17 00:00:00 2001 From: uoza Date: Fri, 14 Apr 2023 07:22:42 +0900 Subject: [PATCH 514/775] Fix type_more_complex (#49338) * fix type_more_complex for Vararg{Tuple{}} and Vararg{Tuple} * add test for type_more_complex * Update base/compiler/typelimits.jl Co-authored-by: Jameson Nash --------- Co-authored-by: Jameson Nash --- base/compiler/typelimits.jl | 3 ++- test/compiler/inference.jl | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index e7b9066d53068..8e845d6f21888 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -261,7 +261,8 @@ function type_more_complex(@nospecialize(t), @nospecialize(c), sources::SimpleVe elseif isa(c, DataType) && t.name === c.name cP = c.parameters length(cP) < length(tP) && return true - length(cP) > length(tP) && !isvarargtype(tP[end]) && depth == 1 && return false + isempty(tP) && return false + length(cP) > length(tP) && !isvarargtype(tP[end]) && depth == 1 && return false # is this line necessary? ntail = length(cP) - length(tP) # assume parameters were dropped from the tuple head # allow creating variation within a nested tuple, but only so deep if t.name === Tuple.name && tupledepth > 0 diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 3ea296d908ef9..1ee077f6ca3ef 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -90,6 +90,9 @@ end @test Core.Compiler.type_more_complex(Type{<:Union{Float32,Float64}}, Type{Union{Float32,Float64}}, Core.svec(Union{Float32,Float64}), 1, 1, 1) @test Core.Compiler.type_more_complex(Type{<:Union{Float32,Float64}}, Any, Core.svec(Union{Float32,Float64}), 1, 1, 1) +# issue #49287 +@test !Core.Compiler.type_more_complex(Tuple{Vararg{Tuple{}}}, Tuple{Vararg{Tuple}}, Core.svec(), 0, 0, 0) +@test Core.Compiler.type_more_complex(Tuple{Vararg{Tuple}}, Tuple{Vararg{Tuple{}}}, Core.svec(), 0, 0, 0) let # 40336 t = Type{Type{Int}} From fdd71c79f3442ada3e0f4e9ff3e5d629054e5a25 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Fri, 14 Apr 2023 07:28:06 +0800 Subject: [PATCH 515/775] Subtype: fix union estimation on non-type parameters. (#49345) --- src/subtype.c | 2 +- test/subtype.jl | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 5190d28ce3e0c..cea91614c8320 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1536,7 +1536,7 @@ static int may_contain_union_decision(jl_value_t *x, jl_stenv_t *e, jl_typeenv_t return 0; } if (!jl_is_typevar(x)) - return 1; + return jl_is_type(x); jl_typeenv_t *t = log; while (t != NULL) { if (x == (jl_value_t *)t->var) diff --git a/test/subtype.jl b/test/subtype.jl index 8a9981107a74b..aad1424a2d66c 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2453,8 +2453,11 @@ let A = Tuple{Type{T}, T} where T, @testintersect(A, B, C) end -let a = (isodd(i) ? Pair{Char, String} : Pair{String, String} for i in 1:2000) +let + a = (isodd(i) ? Pair{Char, String} : Pair{String, String} for i in 1:2000) @test Tuple{Type{Pair{Union{Char, String}, String}}, a...} <: Tuple{Type{Pair{K, V}}, Vararg{Pair{A, B} where B where A}} where V where K + a = (isodd(i) ? Matrix{Int} : Vector{Int} for i in 1:4000) + @test Tuple{Type{Pair{Union{Char, String}, String}}, a...,} <: Tuple{Type{Pair{K, V}}, Vararg{Array}} where V where K end #issue 48582 From 327da72f7fef99c796215a50d78becb2eb003082 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Fri, 14 Apr 2023 07:30:10 +0800 Subject: [PATCH 516/775] subtype: `simple_union` allocation optimization (#49293) --- src/jltypes.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/subtype.c | 38 ++---------------------- 2 files changed, 79 insertions(+), 39 deletions(-) diff --git a/src/jltypes.c b/src/jltypes.c index 2aa8385e744a3..30897437cc35f 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -599,10 +599,51 @@ JL_DLLEXPORT jl_value_t *jl_type_union(jl_value_t **ts, size_t n) return tu; } +// note: this is turned off as `Union` doesn't do such normalization. +// static int simple_subtype(jl_value_t *a, jl_value_t *b) +// { +// if (jl_is_kind(b) && jl_is_type_type(a) && jl_typeof(jl_tparam0(a)) == b) +// return 1; +// if (jl_is_typevar(b) && obviously_egal(a, ((jl_tvar_t*)b)->lb)) +// return 1; +// return 0; +// } + +static int simple_subtype2(jl_value_t *a, jl_value_t *b, int hasfree) +{ + int subab = 0, subba = 0; + if (jl_egal(a, b)) { + subab = subba = 1; + } + else if (a == jl_bottom_type || b == (jl_value_t*)jl_any_type) { + subab = 1; + } + else if (b == jl_bottom_type || a == (jl_value_t*)jl_any_type) { + subba = 1; + } + else if (hasfree) { + // subab = simple_subtype(a, b); + // subba = simple_subtype(b, a); + } + else if (jl_is_type_type(a) && jl_is_type_type(b) && + jl_typeof(jl_tparam0(a)) != jl_typeof(jl_tparam0(b))) { + // issue #24521: don't merge Type{T} where typeof(T) varies + } + else if (jl_typeof(a) == jl_typeof(b) && jl_types_egal(a, b)) { + subab = subba = 1; + } + else { + subab = jl_subtype(a, b); + subba = jl_subtype(b, a); + } + return subab | (subba<<1); +} + jl_value_t *simple_union(jl_value_t *a, jl_value_t *b) { - size_t nt = count_union_components(&a, 1); - nt += count_union_components(&b, 1); + size_t nta = count_union_components(&a, 1); + size_t ntb = count_union_components(&b, 1); + size_t nt = nta + ntb; jl_value_t **temp; JL_GC_PUSHARGS(temp, nt+1); size_t count = 0; @@ -610,9 +651,42 @@ jl_value_t *simple_union(jl_value_t *a, jl_value_t *b) flatten_type_union(&b, 1, temp, &count); assert(count == nt); size_t i, j; + size_t ra = nta, rb = ntb; + // first remove cross-redundancy and check if `a >: b` or `a <: b`. + for (i = 0; i < nta; i++) { + if (temp[i] == NULL) continue; + int hasfree = jl_has_free_typevars(temp[i]); + for (j = nta; j < nt; j++) { + if (temp[j] == NULL) continue; + int subs = simple_subtype2(temp[i], temp[j], hasfree || jl_has_free_typevars(temp[j])); + int subab = subs & 1, subba = subs >> 1; + if (subab) { + temp[i] = NULL; + if (!subba) ra = 0; + count--; + break; + } + else if (subba) { + temp[j] = NULL; + rb = 0; + count--; + } + } + } + if (count == ra) { + JL_GC_POP(); + return a; + } + if (count == rb) { + JL_GC_POP(); + return b; + } + // then remove self-redundancy for (i = 0; i < nt; i++) { int has_free = temp[i] != NULL && jl_has_free_typevars(temp[i]); - for (j = 0; j < nt; j++) { + size_t jmin = i < nta ? 0 : nta; + size_t jmax = i < nta ? nta : nt; + for (j = jmin; j < jmax; j++) { if (j != i && temp[i] && temp[j]) { if (temp[i] == jl_bottom_type || temp[j] == (jl_value_t*)jl_any_type || diff --git a/src/subtype.c b/src/subtype.c index cea91614c8320..518c566193b70 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -538,50 +538,16 @@ static int obviously_disjoint(jl_value_t *a, jl_value_t *b, int specificity) return 0; } -static int is_any_like(jl_value_t* x, jl_typeenv_t *env) JL_NOTSAFEPOINT -{ - if (x == (jl_value_t *)jl_any_type) - return 1; - if (jl_is_uniontype(x)) - return is_any_like(((jl_uniontype_t*)x)->a, env) || - is_any_like(((jl_uniontype_t*)x)->b, env); - if (jl_is_unionall(x)) { - jl_unionall_t *ua = (jl_unionall_t*)x; - jl_typeenv_t newenv = { ua->var, NULL, env }; - return is_any_like(ua->body, &newenv); - } - if (jl_is_typevar(x) && env != NULL) { - jl_tvar_t *v = (jl_tvar_t *)x; - if (v->lb != jl_bottom_type) - return 0; - int in_env = 0; - jl_typeenv_t *vs = env; - while (vs != NULL) { - in_env = vs->var == v; - if (in_env) break; - vs = vs->prev; - } - return in_env && is_any_like(v->ub, env); - } - return 0; -} - jl_value_t *simple_union(jl_value_t *a, jl_value_t *b); // compute a least upper bound of `a` and `b` static jl_value_t *simple_join(jl_value_t *a, jl_value_t *b) { - if (is_any_like(a, NULL) || is_any_like(b, NULL)) - return (jl_value_t *)jl_any_type; - if (a == jl_bottom_type || obviously_egal(a,b)) + if (a == jl_bottom_type || b == (jl_value_t*)jl_any_type || obviously_egal(a, b)) return b; - if (b == jl_bottom_type) + if (b == jl_bottom_type || a == (jl_value_t*)jl_any_type) return a; if (!(jl_is_type(a) || jl_is_typevar(a)) || !(jl_is_type(b) || jl_is_typevar(b))) return (jl_value_t*)jl_any_type; - if (jl_is_uniontype(a) && obviously_in_union(a, b)) - return a; - if (jl_is_uniontype(b) && obviously_in_union(b, a)) - return b; if (jl_is_kind(a) && jl_is_type_type(b) && jl_typeof(jl_tparam0(b)) == a) return a; if (jl_is_kind(b) && jl_is_type_type(a) && jl_typeof(jl_tparam0(a)) == b) From 29d19908a4036d41294b4b8de3e2a43e1f10fd67 Mon Sep 17 00:00:00 2001 From: Jae-Mo Lihm Date: Fri, 14 Apr 2023 12:30:00 +0900 Subject: [PATCH 517/775] Add syevd LAPACK Hermitian eigensolver (#49262) * Add LAPACK cheevd and zheevd wrapper * Add interface to real symmetric eigensolver syevd --- stdlib/LinearAlgebra/docs/src/index.md | 1 + stdlib/LinearAlgebra/src/lapack.jl | 123 ++++++++++++++++++++++--- stdlib/LinearAlgebra/test/lapack.jl | 7 ++ 3 files changed, 119 insertions(+), 12 deletions(-) diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index 305fa6fa2562d..00ce21ed6fcae 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -838,6 +838,7 @@ LinearAlgebra.LAPACK.hetri! LinearAlgebra.LAPACK.hetrs! LinearAlgebra.LAPACK.syev! LinearAlgebra.LAPACK.syevr! +LinearAlgebra.LAPACK.syevd! LinearAlgebra.LAPACK.sygvd! LinearAlgebra.LAPACK.bdsqr! LinearAlgebra.LAPACK.bdsdc! diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl index c498e9b51bc19..c5f820a68a6fc 100644 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ b/stdlib/LinearAlgebra/src/lapack.jl @@ -5170,9 +5170,9 @@ solution `X`. hetrs!(uplo::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat) # Symmetric (real) eigensolvers -for (syev, syevr, sygvd, elty) in - ((:dsyev_,:dsyevr_,:dsygvd_,:Float64), - (:ssyev_,:ssyevr_,:ssygvd_,:Float32)) +for (syev, syevr, syevd, sygvd, elty) in + ((:dsyev_,:dsyevr_,:dsyevd_,:dsygvd_,:Float64), + (:ssyev_,:ssyevr_,:ssyevd_,:ssygvd_,:Float32)) @eval begin # SUBROUTINE DSYEV( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, INFO ) # * .. Scalar Arguments .. @@ -5225,7 +5225,7 @@ for (syev, syevr, sygvd, elty) in end lda = stride(A,2) m = Ref{BlasInt}() - w = similar(A, $elty, n) + W = similar(A, $elty, n) ldz = n if jobz == 'N' Z = similar(A, $elty, ldz, 0) @@ -5249,7 +5249,7 @@ for (syev, syevr, sygvd, elty) in jobz, range, uplo, n, A, max(1,lda), vl, vu, il, iu, abstol, m, - w, Z, max(1,ldz), isuppz, + W, Z, max(1,ldz), isuppz, work, lwork, iwork, liwork, info, 1, 1, 1) chklapackerror(info[]) @@ -5260,11 +5260,51 @@ for (syev, syevr, sygvd, elty) in resize!(iwork, liwork) end end - w[1:m[]], Z[:,1:(jobz == 'V' ? m[] : 0)] + W[1:m[]], Z[:,1:(jobz == 'V' ? m[] : 0)] end syevr!(jobz::AbstractChar, A::AbstractMatrix{$elty}) = syevr!(jobz, 'A', 'U', A, 0.0, 0.0, 0, 0, -1.0) + # SUBROUTINE DSYEVD( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, + # $ IWORK, LIWORK, INFO ) + # * .. Scalar Arguments .. + # CHARACTER JOBZ, UPLO + # INTEGER INFO, LDA, LIWORK, LWORK, N + # * .. + # * .. Array Arguments .. + # INTEGER IWORK( * ) + # DOUBLE PRECISION A( LDA, * ), W( * ), WORK( * ) + function syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) + chkstride1(A) + n = checksquare(A) + chkuplofinite(A, uplo) + lda = stride(A,2) + m = Ref{BlasInt}() + W = similar(A, $elty, n) + work = Vector{$elty}(undef, 1) + lwork = BlasInt(-1) + iwork = Vector{BlasInt}(undef, 1) + liwork = BlasInt(-1) + info = Ref{BlasInt}() + for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] + 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), + jobz, uplo, n, A, max(1,lda), + W, work, lwork, iwork, liwork, + info, 1, 1) + chklapackerror(info[]) + if i == 1 + lwork = BlasInt(real(work[1])) + resize!(work, lwork) + liwork = iwork[1] + resize!(iwork, liwork) + end + end + jobz == 'V' ? (W, A) : W + end + # Generalized eigenproblem # SUBROUTINE DSYGVD( ITYPE, JOBZ, UPLO, N, A, LDA, B, LDB, W, WORK, # $ LWORK, IWORK, LIWORK, INFO ) @@ -5313,9 +5353,9 @@ for (syev, syevr, sygvd, elty) in end end # Hermitian eigensolvers -for (syev, syevr, sygvd, elty, relty) in - ((:zheev_,:zheevr_,:zhegvd_,:ComplexF64,:Float64), - (:cheev_,:cheevr_,:chegvd_,:ComplexF32,:Float32)) +for (syev, syevr, syevd, sygvd, elty, relty) in + ((:zheev_,:zheevr_,:zheevd_,:zhegvd_,:ComplexF64,:Float64), + (:cheev_,:cheevr_,:cheevd_,:chegvd_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE ZHEEV( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, RWORK, INFO ) # * .. Scalar Arguments .. @@ -5376,7 +5416,7 @@ for (syev, syevr, sygvd, elty, relty) in end lda = max(1,stride(A,2)) m = Ref{BlasInt}() - w = similar(A, $relty, n) + W = similar(A, $relty, n) if jobz == 'N' ldz = 1 Z = similar(A, $elty, ldz, 0) @@ -5404,7 +5444,7 @@ for (syev, syevr, sygvd, elty, relty) in jobz, range, uplo, n, A, lda, vl, vu, il, iu, abstol, m, - w, Z, ldz, isuppz, + W, Z, ldz, isuppz, work, lwork, rwork, lrwork, iwork, liwork, info, 1, 1, 1) @@ -5418,11 +5458,56 @@ for (syev, syevr, sygvd, elty, relty) in resize!(iwork, liwork) end end - w[1:m[]], Z[:,1:(jobz == 'V' ? m[] : 0)] + W[1:m[]], Z[:,1:(jobz == 'V' ? m[] : 0)] end syevr!(jobz::AbstractChar, A::AbstractMatrix{$elty}) = syevr!(jobz, 'A', 'U', A, 0.0, 0.0, 0, 0, -1.0) + # SUBROUTINE ZHEEVD( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, RWORK, + # $ LRWORK, IWORK, LIWORK, INFO ) + # * .. Scalar Arguments .. + # CHARACTER JOBZ, UPLO + # INTEGER INFO, LDA, LIWORK, LRWORK, LWORK, N + # * .. + # * .. Array Arguments .. + # INTEGER IWORK( * ) + # DOUBLE PRECISION RWORK( * ) + # COMPLEX*16 A( LDA, * ), WORK( * ) + function syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) + chkstride1(A) + chkuplofinite(A, uplo) + n = checksquare(A) + lda = max(1, stride(A,2)) + m = Ref{BlasInt}() + W = similar(A, $relty, n) + work = Vector{$elty}(undef, 1) + lwork = BlasInt(-1) + rwork = Vector{$relty}(undef, 1) + lrwork = BlasInt(-1) + iwork = Vector{BlasInt}(undef, 1) + liwork = BlasInt(-1) + info = Ref{BlasInt}() + for i = 1:2 # first call returns lwork as work[1], lrwork as rwork[1] and liwork as iwork[1] + 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), + jobz, uplo, n, A, stride(A,2), + W, work, lwork, rwork, lrwork, + iwork, liwork, info, 1, 1) + chklapackerror(info[]) + if i == 1 + lwork = BlasInt(real(work[1])) + resize!(work, lwork) + lrwork = BlasInt(rwork[1]) + resize!(rwork, lrwork) + liwork = iwork[1] + resize!(iwork, liwork) + end + end + jobz == 'V' ? (W, A) : W + end + # SUBROUTINE ZHEGVD( ITYPE, JOBZ, UPLO, N, A, LDA, B, LDB, W, WORK, # $ LWORK, RWORK, LRWORK, IWORK, LIWORK, INFO ) # * .. Scalar Arguments .. @@ -5504,6 +5589,20 @@ The eigenvalues are returned in `W` and the eigenvectors in `Z`. syevr!(jobz::AbstractChar, range::AbstractChar, uplo::AbstractChar, A::AbstractMatrix, vl::AbstractFloat, vu::AbstractFloat, il::Integer, iu::Integer, abstol::AbstractFloat) +""" + syevd!(jobz, uplo, A) + +Finds the eigenvalues (`jobz = N`) or eigenvalues and eigenvectors +(`jobz = V`) of a symmetric matrix `A`. If `uplo = U`, the upper triangle +of `A` is used. If `uplo = L`, the lower triangle of `A` is used. + +Use the divide-and-conquer method, instead of the QR iteration used by +`syev!` or multiple relatively robust representations used by `syevr!`. +See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for +a comparison of the accuracy and performatce of different methods. +""" +syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix) + """ sygvd!(itype, jobz, uplo, A, B) -> (w, A, B) diff --git a/stdlib/LinearAlgebra/test/lapack.jl b/stdlib/LinearAlgebra/test/lapack.jl index 1e9e2a2e31e65..a164de0e31815 100644 --- a/stdlib/LinearAlgebra/test/lapack.jl +++ b/stdlib/LinearAlgebra/test/lapack.jl @@ -27,6 +27,13 @@ using LinearAlgebra: BlasInt @test LAPACK.syevr!('N', 'V', 'U', copy(Asym), 0.0, 1.0, 4, 5, -1.0)[1] ≈ vals[vals .< 1.0] @test LAPACK.syevr!('N', 'I', 'U', copy(Asym), 0.0, 1.0, 4, 5, -1.0)[1] ≈ vals[4:5] @test vals ≈ LAPACK.syev!('N', 'U', copy(Asym)) + @test vals ≈ LAPACK.syevd!('N', 'U', copy(Asym)) + vals_test, Z_test = LAPACK.syev!('V', 'U', copy(Asym)) + @test vals_test ≈ vals + @test Z_test*(Diagonal(vals)*Z_test') ≈ Asym + vals_test, Z_test = LAPACK.syevd!('V', 'U', copy(Asym)) + @test vals_test ≈ vals + @test Z_test*(Diagonal(vals)*Z_test') ≈ Asym @test_throws DimensionMismatch LAPACK.sygvd!(1, 'V', 'U', copy(Asym), zeros(elty, 6, 6)) end end From 99c0dad3a3deec4a5d0a5f57661ad7bca26c5ceb Mon Sep 17 00:00:00 2001 From: Yuto Horikawa Date: Fri, 14 Apr 2023 14:55:31 +0900 Subject: [PATCH 518/775] Rename `ipython_mode` to `numbered_prompt` (#49314) Co-authored-by: Jeff Bezanson Co-authored-by: Kristoffer Carlsson --- HISTORY.md | 4 ++-- stdlib/REPL/docs/src/index.md | 9 +++++---- stdlib/REPL/src/REPL.jl | 8 ++++---- stdlib/REPL/test/repl.jl | 4 ++-- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index f98073351e2f2..e31eb04608e7b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -135,8 +135,8 @@ Standard library changes * The contextual module which is active in the REPL can be changed (it is `Main` by default), via the `REPL.activate(::Module)` function or via typing the module in the REPL and pressing the keybinding Alt-m ([#33872]). -* An "IPython mode" which mimics the behaviour of the prompts and storing the evaluated result in `Out` can be - activated with `REPL.ipython_mode!()`. See the manual for how to enable this at startup ([#46474]). +* A "numbered prompt" mode which prints numbers for each input and output and stores evaluated results in `Out` can be + activated with `REPL.numbered_prompt!()`. See the manual for how to enable this at startup ([#46474]). * Tab completion displays available keyword arguments ([#43536]) #### SuiteSparse diff --git a/stdlib/REPL/docs/src/index.md b/stdlib/REPL/docs/src/index.md index a23b8f224a6cb..8e9e19228ea3d 100644 --- a/stdlib/REPL/docs/src/index.md +++ b/stdlib/REPL/docs/src/index.md @@ -617,19 +617,20 @@ julia> REPL.activate(CustomMod) var 8 bytes Int64 ``` -## IPython mode +## Numbered prompt + +It is possible to get an interface which is similar to the IPython REPL and the Mathematica notebook with numbered input prompts and output prefixes. This is done by calling `REPL.numbered_prompt!()`. If you want to have this enabled on startup, add -It is possible to get an interface which is similar to the IPython REPL with numbered input prompts and output prefixes. This is done by calling `REPL.ipython_mode!()`. If you want to have this enabled on startup, add ```julia atreplinit() do repl if !isdefined(repl, :interface) repl.interface = REPL.setup_interface(repl) end - REPL.ipython_mode!(repl) + REPL.numbered_prompt!(repl) end ``` -to your `startup.jl` file. In `IPython` mode the variable `Out[n]` (where `n` is an integer) can be used to refer to earlier results: +to your `startup.jl` file. In numbered prompt the variable `Out[n]` (where `n` is an integer) can be used to refer to earlier results: ```julia-repl In [1]: 5 + 3 diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 5f7ab768e3015..c951f302359f2 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1396,7 +1396,7 @@ function run_frontend(repl::StreamREPL, backend::REPLBackendRef) nothing end -module IPython +module Numbered using ..REPL @@ -1468,7 +1468,7 @@ function __current_ast_transforms(backend) end -function ipython_mode!(repl::LineEditREPL=Base.active_repl, backend=nothing) +function numbered_prompt!(repl::LineEditREPL=Base.active_repl, backend=nothing) n = Ref{Int}(0) set_prompt(repl, n) set_output_prefix(repl, n) @@ -1480,7 +1480,7 @@ end Out[n] A variable referring to all previously computed values, automatically imported to the interactive prompt. -Only defined and exists while using [IPython mode](@ref IPython-mode). +Only defined and exists while using [Numbered prompt](@ref Numbered-prompt). See also [`ans`](@ref). """ @@ -1488,6 +1488,6 @@ Base.MainInclude.Out end -import .IPython.ipython_mode! +import .Numbered.numbered_prompt! end # module diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index c71bdc86d965f..8a6c6a3445e0a 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -1602,7 +1602,7 @@ fake_repl() do stdin_write, stdout_read, repl @test buffercontents(LineEdit.buffer(s)) == "1234αβ56γ" end -# Non standard output_prefix, tested via `ipython_mode!` +# Non standard output_prefix, tested via `numbered_prompt!` fake_repl() do stdin_write, stdout_read, repl repl.interface = REPL.setup_interface(repl) @@ -1611,7 +1611,7 @@ fake_repl() do stdin_write, stdout_read, repl REPL.run_repl(repl; backend) end - REPL.ipython_mode!(repl, backend) + REPL.numbered_prompt!(repl, backend) global c = Base.Event(true) function sendrepl2(cmd, txt) From ea5c9cba8c97e1d221d4b48afe1d2d965c48ee64 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Fri, 14 Apr 2023 13:34:10 +0200 Subject: [PATCH 519/775] Make `normalize` work for `Number`s (#49342) --- stdlib/LinearAlgebra/src/generic.jl | 7 ++--- stdlib/LinearAlgebra/test/generic.jl | 28 +++-------------- test/testhelpers/DualNumbers.jl | 46 ++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 29 deletions(-) create mode 100644 test/testhelpers/DualNumbers.jl diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index 0c947936dee6b..c66f59838e8ba 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -1804,21 +1804,18 @@ function normalize!(a::AbstractArray, p::Real=2) __normalize!(a, nrm) end -@inline function __normalize!(a::AbstractArray, nrm::Real) +@inline function __normalize!(a::AbstractArray, nrm) # The largest positive floating point number whose inverse is less than infinity δ = inv(prevfloat(typemax(nrm))) - if nrm ≥ δ # Safe to multiply with inverse invnrm = inv(nrm) rmul!(a, invnrm) - else # scale elements to avoid overflow εδ = eps(one(nrm))/δ rmul!(a, εδ) rmul!(a, inv(nrm*εδ)) end - - a + return a end """ diff --git a/stdlib/LinearAlgebra/test/generic.jl b/stdlib/LinearAlgebra/test/generic.jl index 108d3aec8f069..3ebaf38e84945 100644 --- a/stdlib/LinearAlgebra/test/generic.jl +++ b/stdlib/LinearAlgebra/test/generic.jl @@ -12,6 +12,8 @@ using .Main.Quaternions isdefined(Main, :OffsetArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "OffsetArrays.jl")) using .Main.OffsetArrays +isdefined(Main, :DualNumbers) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "DualNumbers.jl")) +using .Main.DualNumbers Random.seed!(123) @@ -78,30 +80,7 @@ n = 5 # should be odd end @testset "det with nonstandard Number type" begin - struct MyDual{T<:Real} <: Real - val::T - eps::T - end - Base.:+(x::MyDual, y::MyDual) = MyDual(x.val + y.val, x.eps + y.eps) - Base.:*(x::MyDual, y::MyDual) = MyDual(x.val * y.val, x.eps * y.val + y.eps * x.val) - Base.:/(x::MyDual, y::MyDual) = x.val / y.val - Base.:(==)(x::MyDual, y::MyDual) = x.val == y.val && x.eps == y.eps - Base.zero(::MyDual{T}) where {T} = MyDual(zero(T), zero(T)) - Base.zero(::Type{MyDual{T}}) where {T} = MyDual(zero(T), zero(T)) - Base.one(::MyDual{T}) where {T} = MyDual(one(T), zero(T)) - Base.one(::Type{MyDual{T}}) where {T} = MyDual(one(T), zero(T)) - # the following line is required for BigFloat, IDK why it doesn't work via - # promote_rule like for all other types - Base.promote_type(::Type{MyDual{BigFloat}}, ::Type{BigFloat}) = MyDual{BigFloat} - Base.promote_rule(::Type{MyDual{T}}, ::Type{S}) where {T,S<:Real} = - MyDual{promote_type(T, S)} - Base.promote_rule(::Type{MyDual{T}}, ::Type{MyDual{S}}) where {T,S} = - MyDual{promote_type(T, S)} - Base.convert(::Type{MyDual{T}}, x::MyDual) where {T} = - MyDual(convert(T, x.val), convert(T, x.eps)) - if elty <: Real - @test det(triu(MyDual.(A, zero(A)))) isa MyDual - end + elty <: Real && @test det(Dual.(triu(A), zero(A))) isa Dual end end @@ -390,6 +369,7 @@ end [1.0 2.0 3.0; 4.0 5.0 6.0], # 2-dim rand(1,2,3), # higher dims rand(1,2,3,4), + Dual.(randn(2,3), randn(2,3)), OffsetArray([-1,0], (-2,)) # no index 1 ) @test normalize(arr) == normalize!(copy(arr)) diff --git a/test/testhelpers/DualNumbers.jl b/test/testhelpers/DualNumbers.jl new file mode 100644 index 0000000000000..9f62e3bf0d429 --- /dev/null +++ b/test/testhelpers/DualNumbers.jl @@ -0,0 +1,46 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module DualNumbers + +export Dual + +# Dual numbers type with minimal interface +# example of a (real) number type that subtypes Number, but not Real. +# Can be used to test generic linear algebra functions. + +struct Dual{T<:Real} <: Number + val::T + eps::T +end +Base.:+(x::Dual, y::Dual) = Dual(x.val + y.val, x.eps + y.eps) +Base.:-(x::Dual, y::Dual) = Dual(x.val - y.val, x.eps - y.eps) +Base.:*(x::Dual, y::Dual) = Dual(x.val * y.val, x.eps * y.val + y.eps * x.val) +Base.:*(x::Number, y::Dual) = Dual(x*y.val, x*y.eps) +Base.:*(x::Dual, y::Number) = Dual(x.val*y, x.eps*y) +Base.:/(x::Dual, y::Dual) = Dual(x.val / y.val, (x.eps*y.val - x.val*y.eps)/(y.val*y.val)) + +Base.:(==)(x::Dual, y::Dual) = x.val == y.val && x.eps == y.eps + +Base.promote_rule(::Type{Dual{T}}, ::Type{T}) where {T} = Dual{T} +Base.promote_rule(::Type{Dual{T}}, ::Type{S}) where {T,S<:Real} = Dual{promote_type(T, S)} +Base.promote_rule(::Type{Dual{T}}, ::Type{Dual{S}}) where {T,S} = Dual{promote_type(T, S)} + +Base.convert(::Type{Dual{T}}, x::Dual{T}) where {T} = x +Base.convert(::Type{Dual{T}}, x::Dual) where {T} = Dual(convert(T, x.val), convert(T, x.eps)) +Base.convert(::Type{Dual{T}}, x::Real) where {T} = Dual(convert(T, x), zero(T)) + +Base.float(x::Dual) = Dual(float(x.val), float(x.eps)) +# the following two methods are needed for normalize (to check for potential overflow) +Base.typemax(x::Dual) = Dual(typemax(x.val), zero(x.eps)) +Base.prevfloat(x::Dual{<:AbstractFloat}) = prevfloat(x.val) + +Base.abs2(x::Dual) = x*x +Base.abs(x::Dual) = sqrt(abs2(x)) +Base.sqrt(x::Dual) = Dual(sqrt(x.val), x.eps/(2sqrt(x.val))) + +Base.isless(x::Dual, y::Dual) = x.val < y.val +Base.isless(x::Real, y::Dual) = x < y.val +Base.isinf(x::Dual) = isinf(x.val) & isfinite(x.eps) +Base.real(x::Dual) = x # since we curently only consider Dual{<:Real} + +end # module From b795ccf9c9747c908f1b3309a9efa854c8717061 Mon Sep 17 00:00:00 2001 From: Denis Barucic Date: Fri, 14 Apr 2023 18:19:49 +0200 Subject: [PATCH 520/775] LDLT: Restrict several methods to SymTridiagonal (#49344) --- stdlib/LinearAlgebra/src/ldlt.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/ldlt.jl b/stdlib/LinearAlgebra/src/ldlt.jl index 8c6bfee435186..d3d6234961c44 100644 --- a/stdlib/LinearAlgebra/src/ldlt.jl +++ b/stdlib/LinearAlgebra/src/ldlt.jl @@ -62,7 +62,7 @@ LDLt{T}(F::LDLt) where {T} = LDLt(convert(AbstractMatrix{T}, F.data)::AbstractMa Factorization{T}(F::LDLt{T}) where {T} = F Factorization{T}(F::LDLt) where {T} = LDLt{T}(F) -function getproperty(F::LDLt, d::Symbol) +function getproperty(F::LDLt{<:Any, <:SymTridiagonal}, d::Symbol) Fdata = getfield(F, :data) if d === :d return Fdata.dv @@ -211,7 +211,7 @@ function logabsdet(F::LDLt{<:Any,<:SymTridiagonal}) end # Conversion methods -function SymTridiagonal(F::LDLt) +function SymTridiagonal(F::LDLt{<:Any, <:SymTridiagonal}) e = copy(F.data.ev) d = copy(F.data.dv) e .*= d[1:end-1] From ff7b8eb00bf887f20bf57fb7e53be0070a242c07 Mon Sep 17 00:00:00 2001 From: Gabriel Wu Date: Sat, 15 Apr 2023 00:29:38 +0800 Subject: [PATCH 521/775] doc: Fix unclosed code fence in src/manual/methods.md (#49357) --- doc/src/manual/methods.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/src/manual/methods.md b/doc/src/manual/methods.md index a504f8e3511b2..23f409b22b880 100644 --- a/doc/src/manual/methods.md +++ b/doc/src/manual/methods.md @@ -1247,5 +1247,6 @@ function f2(inc) x -> x - 1 end end +``` [^Clarke61]: Arthur C. Clarke, *Profiles of the Future* (1961): Clarke's Third Law. From 4f9e1cbcb44a688846468895cb431b4a192c8491 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 15 Apr 2023 18:53:16 +0200 Subject: [PATCH 522/775] Fix open() invalid flags error reporting (#49371) Fixes #49038 --- src/support/ios.c | 4 +++- test/file.jl | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/support/ios.c b/src/support/ios.c index 4a6aeb54a4d32..efae7b076014c 100644 --- a/src/support/ios.c +++ b/src/support/ios.c @@ -935,9 +935,11 @@ ios_t *ios_file(ios_t *s, const char *fname, int rd, int wr, int create, int tru { int flags; int fd; - if (!(rd || wr)) + if (!(rd || wr)) { // must specify read and/or write + errno = EINVAL; goto open_file_err; + } flags = wr ? (rd ? O_RDWR : O_WRONLY) : O_RDONLY; if (create) flags |= O_CREAT; if (trunc) flags |= O_TRUNC; diff --git a/test/file.jl b/test/file.jl index 8544ae980af6b..1d2ac4c6f9132 100644 --- a/test/file.jl +++ b/test/file.jl @@ -598,6 +598,17 @@ close(s) # This section tests temporary file and directory creation. # ####################################################################### +@testset "invalid read/write flags" begin + @test try + open("this file is not expected to exist", read=false, write=false) + false + catch e + isa(e, SystemError) || rethrow() + @test endswith(sprint(showerror, e), "Invalid argument") + true + end +end + @testset "quoting filenames" begin @test try open("this file is not expected to exist") From 46adde9d8d749433fee62b53d0e892735f65a40c Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 15 Apr 2023 16:30:55 -0400 Subject: [PATCH 523/775] Fix incorrect inlining location for early finalization (#49370) The early inlining of finalizer inside sroa_pass! had incorrect logic that would sometimes cause it to accidentally inline the finalizer at the beginning of the basic block of the last use, rather than after the final use. This generally happened when the final use is not in basic block that is the postdominator of all previous uses. Because this optimization is relatively conservative with respect to what may happen between the various uses, all our test cases had this property, so we didn't see it. This changed in b33a7635915f838c7e038a815a0de7a7749da616, which introduced an extra basic block in `setproperty!` causing downstream breakage. Fix the logic and add a regression test with this pattern. --- base/compiler/ssair/passes.jl | 10 +++---- test/compiler/inline.jl | 50 +++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 2b50a6114865f..5fe1a86baaee1 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1152,12 +1152,10 @@ function try_resolve_finalizer!(ir::IRCode, idx::Int, finalizer_idx::Int, defuse function note_block_use!(usebb::Int, useidx::Int) new_bb_insert_block = nearest_common_dominator(get!(lazypostdomtree), bb_insert_block, usebb) - if new_bb_insert_block == bb_insert_block == usebb - if bb_insert_idx !== nothing - bb_insert_idx = max(bb_insert_idx::Int, useidx) - else - bb_insert_idx = useidx - end + if new_bb_insert_block == bb_insert_block && bb_insert_idx !== nothing + bb_insert_idx = max(bb_insert_idx::Int, useidx) + elseif new_bb_insert_block == usebb + bb_insert_idx = useidx else bb_insert_idx = nothing end diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index e32cc73c4f3c3..62ff1a1ee7b6b 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -1940,3 +1940,53 @@ let result = @test_throws MethodError issue49074(Issue49050Concrete) @test result.value.f === issue49074 @test result.value.args === (Any,) end + +# Regression for finalizer inlining with more complex control flow +global finalizer_escape::Int = 0 +mutable struct FinalizerEscapeTest + x::Int + function FinalizerEscapeTest() + this = new(0) + finalizer(this) do this + global finalizer_escape + finalizer_escape = this.x + end + return this + end +end + +function run_finalizer_escape_test1(b1, b2) + x = FinalizerEscapeTest() + x.x = 1 + if b1 + x.x = 2 + end + if b2 + Base.donotdelete(b2) + end + x.x = 3 + return nothing +end + +function run_finalizer_escape_test2(b1, b2) + x = FinalizerEscapeTest() + x.x = 1 + if b1 + x.x = 2 + end + x.x = 3 + return nothing +end + +for run_finalizer_escape_test in (run_finalizer_escape_test1, run_finalizer_escape_test2) + global finalizer_escape::Int = 0 + + let src = code_typed1(run_finalizer_escape_test, Tuple{Bool, Bool}) + @test any(x->isexpr(x, :(=)), src.code) + end + + let + run_finalizer_escape_test(true, true) + @test finalizer_escape == 3 + end +end From 9071fe64e607e7d9118d22646829e924d272344a Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Mon, 17 Apr 2023 02:06:29 -0400 Subject: [PATCH 524/775] Copy slottypes in copy(::CodeInfo) (#49378) This started as #49369 where I noticed CodeInfos with corrupted slottypes, though Shuhei pointed out that Cthulhu was actually copying the slottypes itself (but other downstreams were not), so I opened https://github.com/JuliaDebug/Cthulhu.jl/pull/429. However, on second thought, it seemed unnecessary for Cthulhu to be doing the copy of the slottypes explicitly, since it was already explicitly copying the CodeInfo. Upon further inspection, it became apparent that we simply forgot to add the `slottypes` to the CodeInfo copy method. Whoops. --- base/expr.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/base/expr.jl b/base/expr.jl index 5649303b41ef4..457eebc4ea548 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -75,6 +75,9 @@ function copy(c::CodeInfo) cnew.code = copy_exprargs(cnew.code) cnew.slotnames = copy(cnew.slotnames) cnew.slotflags = copy(cnew.slotflags) + if cnew.slottypes !== nothing + cnew.slottypes = copy(cnew.slottypes) + end cnew.codelocs = copy(cnew.codelocs) cnew.linetable = copy(cnew.linetable::Union{Vector{Any},Vector{Core.LineInfoNode}}) cnew.ssaflags = copy(cnew.ssaflags) From 3ce7a9b09c82680a351ec2b3d79e28ad63e30e63 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Mon, 17 Apr 2023 02:10:41 -0400 Subject: [PATCH 525/775] Fix TODO in IncrementalCompact's `already_inserted` (#49380) The `already_inserted` query on `IncrementalCompact` had a missing case for being called on incremental compact whose underlying IR had new nodes to be inserted. This does not happen in the base pipeline, because `already_inserted` is only used in the sroa passes, but can happen in some non-Base pipelines that run multiple rounds of sroa. --- base/compiler/ssair/passes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 5fe1a86baaee1..913b7cde6f606 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -326,7 +326,7 @@ function already_inserted(compact::IncrementalCompact, old::OldSSAValue) end id -= length(compact.ir.stmts) if id < length(compact.ir.new_nodes) - error("") + return already_inserted(compact, OldSSAValue(compact.ir.new_nodes.info[id].pos)) end id -= length(compact.ir.new_nodes) @assert id <= length(compact.pending_nodes) From 78fd05a799f5d40105253bce594163cde700ed93 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Mon, 17 Apr 2023 11:06:53 +0200 Subject: [PATCH 526/775] Minor `AbstractQ` audit (#49363) --- stdlib/LinearAlgebra/src/abstractq.jl | 16 +++++++++------- stdlib/LinearAlgebra/test/abstractq.jl | 14 +++++++++----- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/stdlib/LinearAlgebra/src/abstractq.jl b/stdlib/LinearAlgebra/src/abstractq.jl index 853286f34048f..334a9a9235972 100644 --- a/stdlib/LinearAlgebra/src/abstractq.jl +++ b/stdlib/LinearAlgebra/src/abstractq.jl @@ -22,9 +22,14 @@ promote_rule(::Type{<:AbstractMatrix{T}}, ::Type{<:AbstractQ{T}}) where {T} = (@inline; Union{AbstractMatrix{T},AbstractQ{T}}) # conversion -AbstractQ{S}(Q::AbstractQ{S}) where {S} = Q -# the following eltype promotion needs to be defined for each subtype +# the following eltype promotion should be defined for each subtype `QType` # convert(::Type{AbstractQ{T}}, Q::QType) where {T} = QType{T}(Q) +# and then care has to be taken that +# QType{T}(Q::QType{T}) where T = ... +# is implemented as a no-op + +# the following conversion method ensures functionality when the above method is not defined +# (as for HessenbergQ), but no eltype conversion is required either (say, in multiplication) convert(::Type{AbstractQ{T}}, Q::AbstractQ{T}) where {T} = Q convert(::Type{AbstractQ{T}}, adjQ::AdjointQ{T}) where {T} = adjQ convert(::Type{AbstractQ{T}}, adjQ::AdjointQ) where {T} = convert(AbstractQ{T}, adjQ.Q)' @@ -36,7 +41,6 @@ Matrix(Q::AbstractQ{T}) where {T} = Matrix{T}(Q) Array{T}(Q::AbstractQ) where {T} = Matrix{T}(Q) Array(Q::AbstractQ) = Matrix(Q) convert(::Type{T}, Q::AbstractQ) where {T<:Array} = T(Q) -convert(::Type{T}, Q::AbstractQ) where {T<:Matrix} = T(Q) # legacy @deprecate(convert(::Type{AbstractMatrix{T}}, Q::AbstractQ) where {T}, convert(LinearAlgebra.AbstractQ{T}, Q)) @@ -227,11 +231,9 @@ QRCompactWYQ{S}(factors::AbstractMatrix, T::AbstractMatrix) where {S} = @deprecate(QRCompactWYQ{S,M}(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S,M}, QRCompactWYQ{S,M,typeof(T)}(factors, T), false) -QRPackedQ{T}(Q::QRPackedQ) where {T} = QRPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(Vector{T}, Q.τ)) +QRPackedQ{T}(Q::QRPackedQ) where {T} = QRPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(AbstractVector{T}, Q.τ)) QRCompactWYQ{S}(Q::QRCompactWYQ) where {S} = QRCompactWYQ(convert(AbstractMatrix{S}, Q.factors), convert(AbstractMatrix{S}, Q.T)) -AbstractQ{S}(Q::QRPackedQ) where {S} = QRPackedQ{S}(Q) -AbstractQ{S}(Q::QRCompactWYQ) where {S} = QRCompactWYQ{S}(Q) # override generic square fallback Matrix{T}(Q::Union{QRCompactWYQ{S},QRPackedQ{S}}) where {T,S} = convert(Matrix{T}, lmul!(Q, Matrix{S}(I, size(Q, 1), min(size(Q.factors)...)))) @@ -505,7 +507,7 @@ struct LQPackedQ{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: AbstractQ{T} τ::C end -LQPackedQ{T}(Q::LQPackedQ) where {T} = LQPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(Vector{T}, Q.τ)) +LQPackedQ{T}(Q::LQPackedQ) where {T} = LQPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(AbstractVector{T}, Q.τ)) @deprecate(AbstractMatrix{T}(Q::LQPackedQ) where {T}, convert(AbstractQ{T}, Q), false) diff --git a/stdlib/LinearAlgebra/test/abstractq.jl b/stdlib/LinearAlgebra/test/abstractq.jl index b3ef5a1952244..e3f48c7b2e3fd 100644 --- a/stdlib/LinearAlgebra/test/abstractq.jl +++ b/stdlib/LinearAlgebra/test/abstractq.jl @@ -29,6 +29,9 @@ n = 5 A = rand(T, n, n) F = qr(A) Q = MyQ(F.Q) + @test ndims(Q) == 2 + T <: Real && @test transpose(Q) == adjoint(Q) + T <: Complex && @test_throws ErrorException transpose(Q) @test convert(AbstractQ{complex(T)}, Q) isa MyQ{complex(T)} @test convert(AbstractQ{complex(T)}, Q') isa AdjointQ{<:complex(T),<:MyQ{complex(T)}} @test Q*I ≈ Q.Q*I rtol=2eps(real(T)) @@ -50,14 +53,15 @@ n = 5 @test mul!(X, transQ(Q), transY(Y)) ≈ transQ(Q) * transY(Y) ≈ transQ(Q.Q) * transY(Y) @test mul!(X, transY(Y), transQ(Q)) ≈ transY(Y) * transQ(Q) ≈ transY(Y) * transQ(Q.Q) end - @test Matrix(Q) ≈ Q[:,:] ≈ copyto!(zeros(T, size(Q)), Q) ≈ Q.Q*I - @test Matrix(Q') ≈ (Q')[:,:] ≈ copyto!(zeros(T, size(Q)), Q') ≈ Q.Q'*I - @test Q[1,:] == Q.Q[1,:] - @test Q[:,1] == Q.Q[:,1] + @test convert(Matrix, Q) ≈ Matrix(Q) ≈ Q[:,:] ≈ copyto!(zeros(T, size(Q)), Q) ≈ Q.Q*I + @test convert(Matrix, Q') ≈ Matrix(Q') ≈ (Q')[:,:] ≈ copyto!(zeros(T, size(Q)), Q') ≈ Q.Q'*I + @test Q[1,:] == Q.Q[1,:] == view(Q, 1, :) + @test Q[:,1] == Q.Q[:,1] == view(Q, :, 1) @test Q[1,1] == Q.Q[1,1] @test Q[:] == Q.Q[:] - @test Q[:,1:3] == Q.Q[:,1:3] + @test Q[:,1:3] == Q.Q[:,1:3] == view(Q, :, 1:3) @test Q[:,1:3] ≈ Matrix(Q)[:,1:3] + @test Q[2:3,2:3] == view(Q, 2:3, 2:3) ≈ Matrix(Q)[2:3,2:3] @test_throws BoundsError Q[0,1] @test_throws BoundsError Q[n+1,1] @test_throws BoundsError Q[1,0] From 837e62eb8a2d3162a6cb61b635fdcdedf10def33 Mon Sep 17 00:00:00 2001 From: KronosTheLate <61620837+KronosTheLate@users.noreply.github.com> Date: Mon, 17 Apr 2023 11:26:44 +0200 Subject: [PATCH 527/775] Add example to be more clear about meaning of inputs (#49331) --- stdlib/LinearAlgebra/src/eigen.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/src/eigen.jl b/stdlib/LinearAlgebra/src/eigen.jl index 224b7d4b86245..185061b0a3a7d 100644 --- a/stdlib/LinearAlgebra/src/eigen.jl +++ b/stdlib/LinearAlgebra/src/eigen.jl @@ -182,7 +182,9 @@ end Compute the eigenvalue decomposition of `A`, returning an [`Eigen`](@ref) factorization object `F` which contains the eigenvalues in `F.values` and the eigenvectors in the columns of the -matrix `F.vectors`. (The `k`th eigenvector can be obtained from the slice `F.vectors[:, k]`.) +matrix `F.vectors`. This corresponds to solving an eigenvalue problem of the form +`Ax = λx`, where `A` is a matrix, `x` is an eigenvector, and `λ` is an eigenvalue. +(The `k`th eigenvector can be obtained from the slice `F.vectors[:, k]`.) Iterating the decomposition produces the components `F.values` and `F.vectors`. @@ -480,6 +482,8 @@ end Compute the generalized eigenvalue decomposition of `A` and `B`, returning a [`GeneralizedEigen`](@ref) factorization object `F` which contains the generalized eigenvalues in `F.values` and the generalized eigenvectors in the columns of the matrix `F.vectors`. +This corresponds to solving a generalized eigenvalue problem of the form +`Ax = λBx`, where `A, B` are matrices, `x` is an eigenvector, and `λ` is an eigenvalue. (The `k`th generalized eigenvector can be obtained from the slice `F.vectors[:, k]`.) Iterating the decomposition produces the components `F.values` and `F.vectors`. From fe2ca96aab10595999bebbb8bda7a223d5fac777 Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Mon, 17 Apr 2023 09:41:08 -0400 Subject: [PATCH 528/775] rem2pi: return argument when it is NaN (#49364) --- base/math.jl | 12 ++++++++---- test/numbers.jl | 8 ++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/base/math.jl b/base/math.jl index 3394ab1e2f436..f42ecf3d0ee7e 100644 --- a/base/math.jl +++ b/base/math.jl @@ -1337,7 +1337,8 @@ julia> rem2pi(7pi/4, RoundDown) """ function rem2pi end function rem2pi(x::Float64, ::RoundingMode{:Nearest}) - isfinite(x) || return NaN + isnan(x) && return x + isinf(x) && return NaN abs(x) < pi && return x @@ -1362,7 +1363,8 @@ function rem2pi(x::Float64, ::RoundingMode{:Nearest}) end end function rem2pi(x::Float64, ::RoundingMode{:ToZero}) - isfinite(x) || return NaN + isnan(x) && return x + isinf(x) && return NaN ax = abs(x) ax <= 2*Float64(pi,RoundDown) && return x @@ -1389,7 +1391,8 @@ function rem2pi(x::Float64, ::RoundingMode{:ToZero}) copysign(z,x) end function rem2pi(x::Float64, ::RoundingMode{:Down}) - isfinite(x) || return NaN + isnan(x) && return x + isinf(x) && return NaN if x < pi4o2_h if x >= 0 @@ -1420,7 +1423,8 @@ function rem2pi(x::Float64, ::RoundingMode{:Down}) end end function rem2pi(x::Float64, ::RoundingMode{:Up}) - isfinite(x) || return NaN + isnan(x) && return x + isinf(x) && return NaN if x > -pi4o2_h if x <= 0 diff --git a/test/numbers.jl b/test/numbers.jl index e41d88243d014..d7baecd847c8f 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -2710,11 +2710,15 @@ end @test rem2pi(T(-13), RoundUp) ≈ -13+4π end -@testset "PR #36420 $T" for T in (Float16, Float32, Float64) +@testset "PR #36420 $T" for T in (Float16, Float32, Float64, BigFloat) + nan = reinterpret(Float64, reinterpret(UInt64, NaN) | rand(UInt64)) for r in (RoundToZero, RoundNearest, RoundDown, RoundUp) - for x in (Inf, -Inf, NaN, -NaN) + for x in (Inf, -Inf, NaN, -NaN, nan) @test isnan(rem2pi(T(x), r)) @test rem2pi(T(x), r) isa T + if isnan(x) && T !== BigFloat + @test rem2pi(T(x), r) === T(x) + end end end end From 32003afef2cb610a1b7ff4983442d396d99e93f6 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 17 Apr 2023 10:58:19 -0400 Subject: [PATCH 529/775] fix a minor issue with methods filtering (#49348) In rare cases, `methods` might list many ambiguous methods, because lim=-1, but there was a disambiguating method detected that fully covered all of them. This was not fully intended behavior. An example of this is: `Base.methods(eltype, (Type{<:AbstractSet},))` --- src/gf.c | 71 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/src/gf.c b/src/gf.c index 2555d92dcf2c6..489c21f64ae93 100644 --- a/src/gf.c +++ b/src/gf.c @@ -3623,8 +3623,11 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, agid = ambig_groupid[i]; } } - // laborious test, checking for existence and coverage of m3 - if (has_ambiguity) { + } + // laborious test, checking for existence and coverage of m3 + // (has_ambiguity is overestimated for lim==-1, since we don't compute skipped matches either) + if (has_ambiguity) { + if (lim != -1) { // some method is ambiguous, but let's see if we can find another method (m3) // outside of the ambiguity group that dominates any ambiguous methods, // and means we can ignore this for has_ambiguity @@ -3690,43 +3693,43 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, break; } } - } - // If we're only returning possible matches, now filter out any method - // whose intersection is fully ambiguous with the group it is in. - if (!include_ambiguous && has_ambiguity) { - for (i = 0; i < len; i++) { - if (skip[i]) - continue; - uint32_t agid = ambig_groupid[i]; - jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, i); - jl_method_t *m = matc->method; - jl_tupletype_t *ti = matc->spec_types; - int subt = matc->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m->sig) - char ambig1 = 0; - for (j = agid; j < len && ambig_groupid[j] == agid; j++) { - if (j == i) + // If we're only returning possible matches, now filter out any method + // whose intersection is fully ambiguous with the group it is in. + if (!include_ambiguous) { + for (i = 0; i < len; i++) { + if (skip[i]) continue; - jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(env.t, j); - jl_method_t *m2 = matc2->method; - int subt2 = matc2->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) - // if their intersection contributes to the ambiguity cycle - if (subt || subt2 || !jl_has_empty_intersection((jl_value_t*)ti, m2->sig)) { - // and the contribution of m is fully ambiguous with the portion of the cycle from m2 - if (subt2 || jl_subtype((jl_value_t*)ti, m2->sig)) { - // but they aren't themselves simply ordered (here - // we don't consider that a third method might be - // disrupting that ordering and just consider them - // pairwise to keep this simple). - if (!jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig) && - !jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig)) { - ambig1 = 1; - break; + uint32_t agid = ambig_groupid[i]; + jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, i); + jl_method_t *m = matc->method; + jl_tupletype_t *ti = matc->spec_types; + int subt = matc->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m->sig) + char ambig1 = 0; + for (j = agid; j < len && ambig_groupid[j] == agid; j++) { + if (j == i) + continue; + jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(env.t, j); + jl_method_t *m2 = matc2->method; + int subt2 = matc2->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) + // if their intersection contributes to the ambiguity cycle + if (subt || subt2 || !jl_has_empty_intersection((jl_value_t*)ti, m2->sig)) { + // and the contribution of m is fully ambiguous with the portion of the cycle from m2 + if (subt2 || jl_subtype((jl_value_t*)ti, m2->sig)) { + // but they aren't themselves simply ordered (here + // we don't consider that a third method might be + // disrupting that ordering and just consider them + // pairwise to keep this simple). + if (!jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig) && + !jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig)) { + ambig1 = 1; + break; + } } } } + if (ambig1) + skip[i] = 1; } - if (ambig1) - skip[i] = 1; } } } From 6ac57e6b8b392a574ef4e9f996c2d6924d238a95 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Mon, 17 Apr 2023 12:10:52 -0400 Subject: [PATCH 530/775] profiling: delete `JULIA_WAIT_FOR_TRACY` when pre-compiling We'll eventually want to provide some kind of useful interface to turn this on for a particular pre-compilation, but for now we should just prevent causing pre-compilation to hang forever. --- base/loading.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/loading.jl b/base/loading.jl index b82028216663b..df5dde36d371c 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2109,6 +2109,7 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: $trace -`, "OPENBLAS_NUM_THREADS" => 1, + "JULIA_WAIT_FOR_TRACY" => nothing, "JULIA_NUM_THREADS" => 1), stderr = internal_stderr, stdout = internal_stdout), "w", stdout) From e08e14449fdec30d83ae2b9f0d6d1f4a9acf0b75 Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Mon, 17 Apr 2023 19:37:59 +0000 Subject: [PATCH 531/775] Bring in newpm (new pass manager) updates to master (#47038) * Workaround missing ASAN global * Add alias analysis at O2 instead of O3 * Disable runtime unrolling * Make SimpleLoopUnswitch act like LoopUnswitch * Add --time-passes support * Only add verification passes in debug mode * Hide assertion function --- src/codegen.cpp | 11 ++++++++++- src/jitlayers.cpp | 49 ++++++++++++++++++++++++++++++++++++----------- src/jitlayers.h | 17 ++++++++++++---- src/pipeline.cpp | 27 ++++++++++++++------------ 4 files changed, 76 insertions(+), 28 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index b6b86ba4442e1..fb8cefe5eb44f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8838,6 +8838,15 @@ extern "C" void jl_init_llvm(void) clopt = llvmopts.lookup("enable-tail-merge"); // NOO TOUCHIE; NO TOUCH! See #922 if (clopt->getNumOccurrences() == 0) cl::ProvidePositionalOption(clopt, "0", 1); +#ifdef JL_USE_NEW_PM + // For parity with LoopUnswitch + clopt = llvmopts.lookup("unswitch-threshold"); + if (clopt->getNumOccurrences() == 0) + cl::ProvidePositionalOption(clopt, "100", 1); + clopt = llvmopts.lookup("enable-unswitch-cost-multiplier"); + if (clopt->getNumOccurrences() == 0) + cl::ProvidePositionalOption(clopt, "false", 1); +#endif // if the patch adding this option has been applied, lower its limit to provide // better DAGCombiner performance. clopt = llvmopts.lookup("combiner-store-merge-dependence-limit"); @@ -8916,7 +8925,7 @@ extern "C" JL_DLLEXPORT void jl_init_codegen_impl(void) extern "C" JL_DLLEXPORT void jl_teardown_codegen_impl() JL_NOTSAFEPOINT { // output LLVM timings and statistics - reportAndResetTimings(); + jl_ExecutionEngine->printTimers(); PrintStatistics(); } diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index c7e202b98efab..29665d4e420b9 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -1103,6 +1103,9 @@ namespace { int optlevel; PMCreator(TargetMachine &TM, int optlevel) JL_NOTSAFEPOINT : TM(cantFail(createJTMBFromTM(TM, optlevel).createTargetMachine())), optlevel(optlevel) {} + // overload for newpm compatibility + PMCreator(TargetMachine &TM, int optlevel, std::vector> &) JL_NOTSAFEPOINT + : PMCreator(TM, optlevel) {} PMCreator(const PMCreator &other) JL_NOTSAFEPOINT : PMCreator(*other.TM, other.optlevel) {} PMCreator(PMCreator &&other) JL_NOTSAFEPOINT @@ -1128,18 +1131,23 @@ namespace { struct PMCreator { orc::JITTargetMachineBuilder JTMB; OptimizationLevel O; - PMCreator(TargetMachine &TM, int optlevel) JL_NOTSAFEPOINT - : JTMB(createJTMBFromTM(TM, optlevel)), O(getOptLevel(optlevel)) {} + std::vector> &printers; + PMCreator(TargetMachine &TM, int optlevel, std::vector> &printers) JL_NOTSAFEPOINT + : JTMB(createJTMBFromTM(TM, optlevel)), O(getOptLevel(optlevel)), printers(printers) {} auto operator()() JL_NOTSAFEPOINT { - return std::make_unique(cantFail(JTMB.createTargetMachine()), O); + auto NPM = std::make_unique(cantFail(JTMB.createTargetMachine()), O); + printers.push_back([NPM = NPM.get()]() JL_NOTSAFEPOINT { + NPM->printTimers(); + }); + return NPM; } }; #endif struct OptimizerT { - OptimizerT(TargetMachine &TM, int optlevel) JL_NOTSAFEPOINT - : optlevel(optlevel), PMs(PMCreator(TM, optlevel)) {} + OptimizerT(TargetMachine &TM, int optlevel, std::vector> &printers) JL_NOTSAFEPOINT + : optlevel(optlevel), PMs(PMCreator(TM, optlevel, printers)) {} OptimizerT(OptimizerT&) JL_NOTSAFEPOINT = delete; OptimizerT(OptimizerT&&) JL_NOTSAFEPOINT = default; @@ -1247,11 +1255,15 @@ llvm::DataLayout jl_create_datalayout(TargetMachine &TM) { return jl_data_layout; } -JuliaOJIT::PipelineT::PipelineT(orc::ObjectLayer &BaseLayer, TargetMachine &TM, int optlevel) +JuliaOJIT::PipelineT::PipelineT(orc::ObjectLayer &BaseLayer, TargetMachine &TM, int optlevel, std::vector> &PrintLLVMTimers) : CompileLayer(BaseLayer.getExecutionSession(), BaseLayer, std::make_unique(orc::irManglingOptionsFromTargetOptions(TM.Options), TM, optlevel)), OptimizeLayer(CompileLayer.getExecutionSession(), CompileLayer, - llvm::orc::IRTransformLayer::TransformFunction(OptimizerT(TM, optlevel))) {} + llvm::orc::IRTransformLayer::TransformFunction(OptimizerT(TM, optlevel, PrintLLVMTimers))) {} + +#ifdef _COMPILER_ASAN_ENABLED_ +int64_t ___asan_globals_registered; +#endif JuliaOJIT::JuliaOJIT() : TM(createTargetMachine()), @@ -1285,10 +1297,10 @@ JuliaOJIT::JuliaOJIT() #endif LockLayer(ObjectLayer), Pipelines{ - std::make_unique(LockLayer, *TM, 0), - std::make_unique(LockLayer, *TM, 1), - std::make_unique(LockLayer, *TM, 2), - std::make_unique(LockLayer, *TM, 3), + std::make_unique(LockLayer, *TM, 0, PrintLLVMTimers), + std::make_unique(LockLayer, *TM, 1, PrintLLVMTimers), + std::make_unique(LockLayer, *TM, 2, PrintLLVMTimers), + std::make_unique(LockLayer, *TM, 3, PrintLLVMTimers), }, OptSelLayer(Pipelines) { @@ -1393,6 +1405,11 @@ JuliaOJIT::JuliaOJIT() reinterpret_cast(static_cast(msan_workaround::MSanTLS::origin)), JITSymbolFlags::Exported); cantFail(GlobalJD.define(orc::absoluteSymbols(msan_crt))); #endif +#ifdef _COMPILER_ASAN_ENABLED_ + orc::SymbolMap asan_crt; + asan_crt[mangle("___asan_globals_registered")] = JITEvaluatedSymbol::fromPointer(&___asan_globals_registered, JITSymbolFlags::Exported); + cantFail(JD.define(orc::absoluteSymbols(asan_crt))); +#endif } JuliaOJIT::~JuliaOJIT() = default; @@ -1583,6 +1600,16 @@ size_t JuliaOJIT::getTotalBytes() const } #endif +void JuliaOJIT::printTimers() +{ +#ifdef JL_USE_NEW_PM + for (auto &printer : PrintLLVMTimers) { + printer(); + } +#endif + reportAndResetTimings(); +} + JuliaOJIT *jl_ExecutionEngine; // destructively move the contents of src into dest diff --git a/src/jitlayers.h b/src/jitlayers.h index d8c06df44176f..7f07034586c80 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -42,9 +42,7 @@ // and feature support (e.g. Windows, JITEventListeners for various profilers, // etc.). Thus, we currently only use JITLink where absolutely required, that is, // for Mac/aarch64. -// #define JL_FORCE_JITLINK - -#if defined(_OS_DARWIN_) && defined(_CPU_AARCH64_) || defined(JL_FORCE_JITLINK) +#if defined(_OS_DARWIN_) && defined(_CPU_AARCH64_) || defined(_COMPILER_ASAN_ENABLED_) || defined(JL_FORCE_JITLINK) # if JL_LLVM_VERSION < 130000 # pragma message("On aarch64-darwin, LLVM version >= 13 is required for JITLink; fallback suffers from occasional segfaults") # endif @@ -91,6 +89,12 @@ struct OptimizationOptions { } }; +// LLVM's new pass manager is scheduled to replace the legacy pass manager +// for middle-end IR optimizations. However, we have not qualified the new +// pass manager on our optimization pipeline yet, so this remains an optional +// define +// #define JL_USE_NEW_PM + struct NewPM { std::unique_ptr TM; StandardInstrumentations SI; @@ -103,6 +107,8 @@ struct NewPM { ~NewPM() JL_NOTSAFEPOINT; void run(Module &M) JL_NOTSAFEPOINT; + + void printTimers() JL_NOTSAFEPOINT; }; struct AnalysisManagers { @@ -420,7 +426,7 @@ class JuliaOJIT { std::unique_ptr mutex; }; struct PipelineT { - PipelineT(orc::ObjectLayer &BaseLayer, TargetMachine &TM, int optlevel); + PipelineT(orc::ObjectLayer &BaseLayer, TargetMachine &TM, int optlevel, std::vector> &PrintLLVMTimers); CompileLayerT CompileLayer; OptimizeLayerT OptimizeLayer; }; @@ -490,6 +496,7 @@ class JuliaOJIT { TargetIRAnalysis getTargetIRAnalysis() const JL_NOTSAFEPOINT; size_t getTotalBytes() const JL_NOTSAFEPOINT; + void printTimers() JL_NOTSAFEPOINT; jl_locked_stream &get_dump_emitted_mi_name_stream() JL_NOTSAFEPOINT { return dump_emitted_mi_name_stream; @@ -522,6 +529,8 @@ class JuliaOJIT { jl_locked_stream dump_compiles_stream; jl_locked_stream dump_llvm_opt_stream; + std::vector> PrintLLVMTimers; + ResourcePool> ContextPool; #ifndef JL_USE_JITLINK diff --git a/src/pipeline.cpp b/src/pipeline.cpp index ae2b1c3202f04..4403653a9d8e4 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -146,7 +146,7 @@ namespace { // Opts.Recover = CodeGenOpts.SanitizeRecover.has(Mask); // Opts.UseAfterScope = CodeGenOpts.SanitizeAddressUseAfterScope; // Opts.UseAfterReturn = CodeGenOpts.getSanitizeAddressUseAfterReturn(); - MPM.addPass(RequireAnalysisPass()); + // MPM.addPass(RequireAnalysisPass()); // MPM.addPass(ModuleAddressSanitizerPass( // Opts, UseGlobalGC, UseOdrIndicator, DestructorKind)); //Let's assume the defaults are actually fine for our purposes @@ -173,11 +173,13 @@ namespace { // } } - void addVerificationPasses(ModulePassManager &MPM, bool llvm_only) JL_NOTSAFEPOINT { +#ifdef JL_DEBUG_BUILD + static inline void addVerificationPasses(ModulePassManager &MPM, bool llvm_only) JL_NOTSAFEPOINT { if (!llvm_only) MPM.addPass(llvm::createModuleToFunctionPassAdaptor(GCInvariantVerifierPass())); MPM.addPass(VerifierPass()); } +#endif auto basicSimplifyCFGOptions() JL_NOTSAFEPOINT { return SimplifyCFGOptions() @@ -244,9 +246,9 @@ namespace { //Use for O1 and below static void buildBasicPipeline(ModulePassManager &MPM, PassBuilder *PB, OptimizationLevel O, OptimizationOptions options) JL_NOTSAFEPOINT { -// #ifdef JL_DEBUG_BUILD +#ifdef JL_DEBUG_BUILD addVerificationPasses(MPM, options.llvm_only); -// #endif +#endif invokePipelineStartCallbacks(MPM, PB, O); MPM.addPass(ConstantMergePass()); if (!options.dump_native) { @@ -320,9 +322,9 @@ static void buildBasicPipeline(ModulePassManager &MPM, PassBuilder *PB, Optimiza //Use for O2 and above static void buildFullPipeline(ModulePassManager &MPM, PassBuilder *PB, OptimizationLevel O, OptimizationOptions options) JL_NOTSAFEPOINT { -// #ifdef JL_DEBUG_BUILD +#ifdef JL_DEBUG_BUILD addVerificationPasses(MPM, options.llvm_only); -// #endif +#endif invokePipelineStartCallbacks(MPM, PB, O); MPM.addPass(ConstantMergePass()); { @@ -382,7 +384,7 @@ static void buildFullPipeline(ModulePassManager &MPM, PassBuilder *PB, Optimizat #endif LPM2.addPass(LICMPass(LICMOptions())); JULIA_PASS(LPM2.addPass(JuliaLICMPass())); - LPM2.addPass(SimpleLoopUnswitchPass()); + LPM2.addPass(SimpleLoopUnswitchPass(true, true)); LPM2.addPass(LICMPass(LICMOptions())); JULIA_PASS(LPM2.addPass(JuliaLICMPass())); //LICM needs MemorySSA now, so we must use it @@ -399,7 +401,7 @@ static void buildFullPipeline(ModulePassManager &MPM, PassBuilder *PB, Optimizat //We don't know if the loop end callbacks support MSSA FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM), /*UseMemorySSA = */false)); } - FPM.addPass(LoopUnrollPass()); + FPM.addPass(LoopUnrollPass(LoopUnrollOptions().setRuntime(false))); JULIA_PASS(FPM.addPass(AllocOptPass())); FPM.addPass(SROAPass()); FPM.addPass(InstSimplifyPass()); @@ -541,11 +543,8 @@ PIC->addClassToPassName(decltype(CREATE_PASS)::name(), NAME); // Register the AA manager first so that our version is the one used. FAM.registerPass([&] JL_NOTSAFEPOINT { AAManager AA; - // TODO: Why are we only doing this for -O3? - if (O.getSpeedupLevel() >= 3) { - AA.registerFunctionAnalysis(); - } if (O.getSpeedupLevel() >= 2) { + AA.registerFunctionAnalysis(); AA.registerFunctionAnalysis(); AA.registerFunctionAnalysis(); } @@ -603,6 +602,10 @@ void NewPM::run(Module &M) { #endif } +void NewPM::printTimers() { + SI.getTimePasses().print(); +} + OptimizationLevel getOptLevel(int optlevel) { switch (std::min(std::max(optlevel, 0), 3)) { case 0: From bbd3639fdc5b5887a47e5722df3f3c991a7152e5 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Mon, 17 Apr 2023 16:01:20 -0400 Subject: [PATCH 532/775] dlload: use RTLD_LOCAL when looking up internal libraries This prevents the library from being upgraded to the global namespace for symbol resolution. We were already making this mistake before this function was introduced, but probably did not notice it because RTLD_LOCAL is the default behavior on Linux. On the other hand, macOS does need this fix. --- src/dlload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dlload.c b/src/dlload.c index 64365848ad6f3..4f50228f63564 100644 --- a/src/dlload.c +++ b/src/dlload.c @@ -253,7 +253,7 @@ void *jl_find_dynamic_library_by_addr(void *symbol) { if (!dladdr(symbol, &info) || !info.dli_fname) { jl_error("could not load base module"); } - handle = dlopen(info.dli_fname, RTLD_NOW | RTLD_NOLOAD); + handle = dlopen(info.dli_fname, RTLD_NOW | RTLD_NOLOAD | RTLD_LOCAL); dlclose(handle); // Undo ref count increment from `dlopen` #endif return handle; From 1fff0261f5ec4db013516358a80a44629b5ce859 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Mon, 17 Apr 2023 16:31:36 -0400 Subject: [PATCH 533/775] Bump LLVM to 14.0.6-3 (#49385) --- deps/checksums/clang | 232 ++++++++-------- deps/checksums/llvm | 468 ++++++++++++++++---------------- deps/clang.version | 2 +- deps/llvm-tools.version | 4 +- deps/llvm.version | 4 +- stdlib/libLLVM_jll/Project.toml | 2 +- 6 files changed, 356 insertions(+), 356 deletions(-) diff --git a/deps/checksums/clang b/deps/checksums/clang index 6dd3cc5c84cea..5fecc08fe523e 100644 --- a/deps/checksums/clang +++ b/deps/checksums/clang @@ -1,116 +1,116 @@ -Clang.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/d7af710da0dfe4a19bd0742499226f8a -Clang.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/b69deeff9169e58986ced189f5947d7de00872ee1d5301de381fba6b71237119ff431762e2f530bc37fee5d640178e2184d0c9f9c6a9b5a5896c210a405f5cc9 -Clang.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/b559523937532f608380ea0ef077e5ed -Clang.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/20f37272327b938d1fcb77538023d88fa799c8863db9a1348cb1e8ff883a8b50474e6496f9730139a1d6ce199b4e17ddbf7e1deba448a7303bae2efffd18d889 -Clang.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/f59591c24225c687863d8878068d5d4b -Clang.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/e1970c15dcad8e8a30d4f300ef597c12f4c953cfb656582dd4d75324a2d17bbf6500de288f80c53add9e6b148b3153abb9f331305ba176deb83504e59fab5c7a -Clang.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/8ee3ba6f2f5c6cbda9515fb3528a6267 -Clang.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/cae04df5b65aaf0f8413f0dcb150e29c276c2e7d60c071f41db1dd35c8c0a0c5d36441e6aaf9c8b32079442ce79a48379b72d86a6d1d2b59af6d9a2744ecc8d6 -Clang.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/f1cc3d1a27058db2427d6c1b7e762200 -Clang.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/5d63f84dd7d119fb2f5cd6e51a16e87557a8b54d2a1f733100e4ff296af496913c84c0c3a77c9f81cb27cf5d23e3ea72ea5be0d6cdb02bb4c3d65182bbc4a84a -Clang.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/6b4203de35b0ce42c7fd5990e0a7dbbc -Clang.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/261242b7ef98203e1cb0a77c3d56b87252c4647bda5229f08d586634ebf217613e434f0010fd088ac3a08e4393fc838a1c26573eb8549bb6bb6e14a0cdfaed26 -Clang.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/c1b0ab693bf840be628469afd62131d0 -Clang.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/ddea198aa6e465ef77e542b7ce4b614e9d08fde32095fc21d85125c7312944f13ce84a9d24ad8fa22caef995c1c70965342a6bb9336297ec305795e9bc457ba4 -Clang.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/dde818c965da13154b438f9a4c7bac2a -Clang.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/d11097c997851e3d4e396abd4e4e0c1cac867a50bde661ada7e4e7fac160de30bf15aeb0e3c38651d7c4e6d8f03eb649e2447a81f2ca0a6d4114bea89692e9ee -Clang.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/0437f8c3983b6762beba43c7f089199c -Clang.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/9bf004c4f21c2215505dfbd1fbca8d886d1fad87ce897cf9e982a0b8f10112cd0d080132aa2b575b5ad1ab681f1eaf8da7ddf026bb2e42d1b1a5a3d2a253f71f -Clang.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/684b5c8b283ab5a341603350cb4c815b -Clang.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/ee0f147da7dcb6bdde1a0c1929b46b442cd01e010cfdcc6f9d3c344f684ae3018faba3d88b46486b3d76ae1f456ba7e34ae15bb4a9432c0ad61eaf03045e2730 -Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/72c1d2de0b4628685067787fe81fd9ae -Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/c18086fa874240574d4084977831e13779e44b2690e810a662e2c9361413f6cfb74bc5aa9718c5b64370f9a797df7544c211ae7089c7323024c2973b6bb016f2 -Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/bdd3524a04ca1606ceedc990c828e7c8 -Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/93d2e22962993a032b9b589bd43a635eafb374b5cf3aabf28aaecb6a6617ea6684ac754f121889610b49efbc2cf18e4d25639a6fe80f5522a9b94ba6f4caaced -Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/36958bf224b804c2effe335f8057b404 -Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/8e7720e7d650a100152219731d92e22152bb58ed9a890c0bbf75aea089bbff80efd53f8f80cfc146457e0def0e4b58bc10997d22aa285c06c3de83b5bc9212b8 -Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/f4ca31683aa1445ecf0fb2166280f8ae -Clang.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/1469ea69e864c77fa0df209d3db5c777c92baea7ed3473aff6c865e399aa3237883eda2d419bb224aac20db2cf43adf40bb0d1d3e53699968b7c5494bff40041 -Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/b6f7cd04f2442dc76b198d85bd419bf4 -Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/518aeb631b70ee0abec23bd79d9943b92ae3db61704533a6abfce75ebeccfe5a4b447a178afe9c331299e0c6d077eebda30ee67d93cdf98dacef62fe69587d46 -Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/e1bc81bf1fe6f27c60ca8c98948d26e3 -Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/8e7768abc0cc97f12580ee5f461868f55c8a4cafa274cb726c7da2fe20513f06d7d299e70b14a3aa290ca7d52d76a9a7a36271785e24379f9aaf8caf0b79cff1 -Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/443d936081749e8ff98c45391caf743c -Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/bfa78eeb3673c265a4c279547b015e9f3ea752d823e1b29e78315d5534b574d6aeaa2c25e5dfda0057b853546912ef56f8cce2a33c5ca400fc3f2fa278c1a9e0 -Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/5424303660411ca2ef318aff90688a35 -Clang.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/06a5e3b8c3dcd28c9baf41b821b05e546b2c2c3e3351437a81c5b88bbc4098bc2cf2001fbd040c6afbcae1bc1764384204c5be787e5cc719a7247857110a0366 -Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/be1f870fc8298be34187262768a35a1d -Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/34069aa66c1016e14edc075ecf7ebe7e885a7aa611b809d7251c658d6c0f833f4b04898b6000a7612524959f35536744c1445b8e03e447601f6b8050ab8e0948 -Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/c40fa2e3499d476f6840cad60deb5562 -Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/297287ff1ef0d38a15361c8c653109caaa8baeb5964c75f033e76176ef5594b5cdf8b2c380ad8f270618d1e73f2a4c67aa6d4f9c971d837f5bb12402863d3258 -Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/010417352411a80c93d9538bf7adf7b5 -Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/60cc639eda70230ca41c63621b08ce00a9460d406c9dc626cdb7d24561fdd2f93cd593cd040694c4c0d4e98bcf5f2b514001a88f19d9a69943632c6f5ad0bd24 -Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/6a3ccd40a6487d2e0b4ccff250dc412c -Clang.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/69443b2da14aee04f4d4772b65776c2e96b7e114f11ac055c38c01f56015e32c35940c0ee42eb216e405f9980178986551eaabe6f02fa2e27fddd7ae073f8830 -Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/3a4210ad734ea531f97c94ca1e8a76ed -Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/fa116c6a227c319facb466fc068df7e1802bbe21fc2267b6075f9aeb519d0306b4193381b7ae17faff2e7ab3e868e9fda80ab4dde44a47df180ef1de8df8d015 -Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/e0da7e68ed8cbbb4ffd624bda6c7aa19 -Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/394804f909bcdee79a1506462be80f071990e255b4b9075dc6e8048983a81794e7937236cbd13cf043679e69322b98282dff86f349ae762be04df130c9ae989b -Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/bd2668a1b87f51851b1d975175431277 -Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/cea19ca32b642c5d6f85b3e14b634be907af33677910c9a31e29539265bd516f8368569285f0d09b9ebe9d0175809d2a1460191dd0f3e1e2ce6bcf4ead83f857 -Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/117d0b2b77e4f62a4d4dfa4f89077b33 -Clang.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/412e0f79382717bfac414db83af70fdaeeaa57b17e66c6525405f775fca922f5c02d01241be97dc493e78da10f3bad0db287ac4cf3450abdb1d7874df8f19ba7 -Clang.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/f325dfa797153a1d97e9b81ec74f2635 -Clang.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/8535ce903c71ed7b3a55d03473a2c921d1d8bf8d9c890f5bc0897af3042f62ee19d0841809daf51254d52aaa539d991d8b7222561d950b6e61894e910760cc30 -Clang.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/bde355360a60d90a9ac1f242b5c114e0 -Clang.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/176c08334a68d9a83feb0635173a3b4b292b6b03bde7c9c4c447ba629eb74685c7259268305c71b1d57d2943de5b152639b31b15347b24a07d2ec6628a37df4c -Clang.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/f50e7f89c8652930a56161d3ca703cf4 -Clang.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/b71cd857d1db0ce0e40bed58d5bbfa3807cf5e2f3c0bb102ec5d1d384aff7d6b45e1b4e895533cbc7176c8a3a2c617e96adf57e5f7f8347681bedabe5083129a -Clang.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/732e09b8c05100125c46947a01146a5a -Clang.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/ead28e4edae3d6bc77fb3c0410d87c468c474dd938551a70ff64a40df90aa1feaa90f088be683bddc688c5e298c5d988d7bba8b54d366490c9d07543b2f459af -Clang.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/10529d4bb4a5d183709c384be98b5ac7 -Clang.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/7b4bae0f72d6e2dce6f5e65f4c0baecfd4f3fe030c2cf731e4e4efb678435ea14d73bd1f3187630beefca734f10d10d9d3bbc76996c7f5cf82440de48be19195 -Clang.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/7403f85447c8e622b01d4ed76fab8b3a -Clang.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/0127fb603efad3229e3d70e5cb857cfe91fe5cd399091e756ebd45e3f0e0159aaea6eeff2e5e8d83bb79521040093de2c6cb4ac479f60a43332581e01dbdf6bd -Clang.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/d47f8ca3f45f485705232e42ad3c1213 -Clang.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/ffe1e0756f960475b12c08b55ae3d81fef3d9fce72be73db8d2f9a6e45ab73110366e1dcb1a7814b4abdcbcf128086c09fdad86a512251ea42169f977df85f8e -Clang.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/3dc043e50aa93e140c5ce9e38c483ee5 -Clang.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/200925daba6db1f10066f64dc5d6cd763c5f2f872ce0f69b1be620637d9eaa0c4865d8a251f385face6b4fe9423f677c83a613c0be2c80dd319ffe2c88ae5ce8 -Clang.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/a5860093b12609c8fae69fc2e280b43a -Clang.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/9d0fb968d834462b98eba5081c712dc23dac43e8f91ae8fca3600fa6aa60890409bc486eacc82cbeb646bc0cced788d0d532c2c87d4a824f5d53c0c99c0c6eb4 -Clang.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/95411432c7722b132f400f8d12d47345 -Clang.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/4191a4d6341177d73a28eef09c022a4d5f235a6106f1361c780b543d5393651aaa7c8da9d8072f5b270ac5673b2af9597eb3e9a3152918785f2c859bb1ca58da -Clang.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/9b410214e77f817d100419493403b667 -Clang.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/1b1db5fc0f201328502a57b0b62b6da740d46c665bedbdc2e1dbfdcc3559a3e833afc44057d595468099a85898ea9eddd27a19c2da1bb704942f343f8c8f92dd -Clang.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/987703541a76749eb03e1f678d0cae43 -Clang.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/7811d9e3784a48f235a63aa58e6253534b3c5b88ba15a574340b5059f4aced222318b152adad83eac0c1d637e1e61d1238c4f5315f1bdc406911e21b76e77064 -Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/77f794a2630074684c3e11e5ba424ce0 -Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/45c204483758b727d074f27ba630b3fef8f44bacaba1ea4a50b46d2bd6defcc30577fec6cfecfe6eb3e6b8d6fb9bf372f714cd5cff4423970ac2358f9ab62c40 -Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/dab80f64898fe9a5ffdffeac60ab9726 -Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/147d9860913196e2873f81098fadcd356adb7b365d12f8a4951d87ee7451ef7191d43841c909406a8ef0cd11a171bc1208bb3803035fb789d4c842b96be1b44c -Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/3ba24f9af99881963370b72b8865152e -Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/7f2b8fceabaa4a4c91edacd54ec7fb365567de4b51b42b34eb89faa2de481dfa75882c4066dc081d15f6aad4ea342b4226510a7d580667ef2b77d297409b9961 -Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/71dac37bbefd7a780592ec81a87864d9 -Clang.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/b972be1373d5a3aec000902f7fa47ab241cdfdb0782dc4734c27310eb23cf457af11c794413482a43247cec744fd4283ed2bf81ea919eb825954bcae7cccd4f8 -Clang.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/f2b3ce2c6888649a1c50fbb54882bba6 -Clang.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/eddbd9db8fe7c7b6afc1ff81ad921f2fc00c81f06d57ce680071d2d115a813b0d8212b76a356cfe26d8495f88cbda3c0f42f32b17f676803706a81ed49749d51 -Clang.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/ece1f4eba3ebcbcaed2a05baa427031b -Clang.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/cc0c694e1365c1ba1521b6e971d850f83497074e5648dbc29503939f00713bb196cadf2d9bee992f7998cbd09e7805e7d3de4fec3e97e0df877c000bfae4cf1e -Clang.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/86b29b63f068c84f0882bc43c03d1f2e -Clang.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/4d6bbcd93d61e80db12218d072b25b2d467159edf2c22c72fad67b2eff7c76ac4635a7818d201734858cf1e936db4b46c7d89ac537f02546957323249f5d23c8 -Clang.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/0090fcfa2823e945b8ff3efc1e8d1a1e -Clang.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/3e665032abcc1c85bd30adc902fad22d198c7364c91906e823523d046291bcb94c7b23a364f66d68c5d1c8158e4397ebeb87c08d8d328c8b3af003fb0460f592 -Clang.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/2a66245fcbe1e9cf63615578f61d90c2 -Clang.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/d4662c7eb908a31a6e6e2d8ff7fbe2c74b6be7e7bd6a39999aa82c489ed50d7c7a11a5ec1d7046f67e6b18d2350fb51a77aeff91e3b18ee5d50acbc50be38e72 -Clang.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/b4ce7760690fe64a96cf8e6a17e70ae7 -Clang.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/4114b2c0bd895d151ab255bda0c24a7ef4e2851c746ef163fbd25cd31587e9fe51d5a770c727b7073ad17a4fdf64e10f0357a8919705be986d86b8ae1a7c3ff1 -Clang.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/35d24663842e2a0abbdb6be5ae0bc95b -Clang.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/5391d44fedb6fe296e24437d8765ed8b819c01be8822bef3bb2e5703fce4f0ebccc7a2aac2ef1ac65dbae6e54dff81be200bde799a0200baa4c06194bcb39504 -Clang.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/2b012d456b01cf6e8c6b95a624d44d5f -Clang.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/ddd340ef459c3214bdc56da19c771d95843ffcd3b7fa8e31f71af09afe7204563e892f5cf7b0a9e83d002b1215f03ba6ad323700c6a6ef7723c1be978b22a454 -Clang.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/70f4d3f4b44a71ce08913a173ecae6a3 -Clang.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/d24ab9e63f445658a8be91f2c9a884ce2567872462357648d16f3a8b9bb7d22f9d149ee26f168e5b40a362166fca875446f543334392bafec9fc34bd6952a4d6 -Clang.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/06a8977d439610b925519206cd95a426 -Clang.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/a90c5223ed234238f7a47c8681880a7dcc93454ef9d34e011a44c89c9282e4b082ae2f45c3c6ac7f4b30d11a7938b03b8be132aaa3d769f21a5d83aa6f2fa6fe -Clang.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/edaffef3a5feb62025a683296ab9f569 -Clang.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/84ceacbad23c900a1157e80534391fa127da4b3d99f280ea537a5c391196bfcbc82b9f2ebf877aa45be045afb3de8e6f43a560e2c20060340de3e4829e95fe6f -Clang.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/9dd123ced026b03f71e156c15ca6188d -Clang.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/375fd746dca3f3e7fae3d1d978f19ef84d6b17005f42094c1754fc40e4ffd0b7f775c64c47ff0ee3cfa1e1cbf75e1725cc84325b05600ffe23b1a0ea8340e3e0 -Clang.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/fcff2f4448512a0bb9b2591feda80171 -Clang.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/c7e53932ee0932aa50d10364da5bef677a708fd52f70b2bb55a97ee9e2b9e75d56d9cc4b18d7bd4567259a4e2733d2a9fe33261c35e373908390fbaf49985d40 -Clang.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/f6a4ba3f52ed4576b79e6316d4e24297 -Clang.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/a06e5985731b3f5f1597076141bc4a46c0369facbf5e7bfd9e869cd2a163a76a3c6e667c7b26730b3022b77db4603429940a5e6b1c1bcf116149a88692206c84 -Clang.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/7295e8afef38b3f6c508c0175c1f5603 -Clang.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/538021b18cc8f01cfb242a8ef4d8ec869337d7f1c697ce8ec96936c8d84020b7a2b88b954589cd839098d30a2f76f1a0c34eb2fc3c1a82e58e22e72543a5f5a5 -Clang.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/d243a90350e08a486ba39160261865e1 -Clang.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/51c63f86cdbc58b8dc6ff595ff3cb5020b98fc3b937113bcba59b8faf0cc5aea68e9aee978b325a485ce0168f0a93f6ce0cee412a75afdd2b58fe88bd8a75c22 +Clang.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/f5b5a95a89899922798e78df359813a5 +Clang.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/716a57c99b96407d1f2e65f1f419c4de788aca832fb9f92911739da122a5cb6be09e00c6e24bdbe57bddc8c6aed30e37910a38ee0acec7e4aecd6232270763b9 +Clang.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/d88f1754c6d3d95263cceb804a7cccf8 +Clang.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/7a22375ea1ee4c20d4c33c928f0c79399f382cb71e965da72e067dcd91d57cc28275532da564a05cf6ab91595996b3c3bd30f3537222b0fa651616b032f848de +Clang.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/b60623ab0dcb034fb27be4a68a034b91 +Clang.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/d93da664ba2e7123dc7a84788d417683fae769e0753af6c564be7d50713ab2a3d1b5e925467c3bfa32ccc2eaff1be2ebfed8a3fb5bf07bb2d5023a3e87eb1084 +Clang.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/f737f5a987eb274b839e3a8721104695 +Clang.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/4925d1f0e2f69a35fb4ef922c32de221125e49198cd952ec727ecbe17c3e3819b0d45a8bada12535dbb6aef57304c3b4abc8caef687559314472a2077ca04295 +Clang.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/4c675903f286691915f60445474bbf50 +Clang.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/85561aa0dcf8126bc21e90e8a08bb2402938e5139c05dd15a01ac7a67094cd1141d4414b0a3577f95ecc277dafc699cf1f000e59af3c3b0796daf0d38797081d +Clang.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/4771224587cc7378ed190c4a7fb129bf +Clang.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/54e15eef2a5aeef3992f2b43d5433e8602659b361c1e59f29b3ec11d0e75f8fbedcf023015a3452ad89fd13c825911c1a2ea7fab71df0a98cbf87af10f48934e +Clang.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/08cf0a7f776c75f6842b26acc213c2ae +Clang.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/0f9e0c1b2199b0e48751363245c0e83cf4fc4e72d78df7bfa936e39e0f96cfad9ad89c47b80632469b89f615e3368ca09e42a10e67f1bf7c3604347183c89b7f +Clang.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/a3a88eb47cbc54262a51469a993d0bde +Clang.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/65f899bb7d062623f1c6a7518b76e3ce753e1ab0d7891a736d15d9053ff7c3117bd4b0b510d1b71c3c0e5b43e86b96c70678228271f44b4ce3c642d44f32352c +Clang.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/75c230f9135708a81ef41075ff350f1e +Clang.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/ded16404f36d676d01f6dcf9668043560c40250c4080596f4f927a642d857c4fd28eb1fa7b76638873dbfdb77e86ffce4d1e5ca25b78654d8bf41dbf7891c44d +Clang.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/14cd2bccb3d1fd1f60af78fca8b0f571 +Clang.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/697550e5abd7c4723ccf2b59a342ba7ceb5e55299ec02a33269ee879de705903bb1cd4e5e0da3d0f6e186248295e8007d39256cf81764dfe9601b589361438fa +Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/401162a5dd6ee0697ba69260a74afe46 +Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/a8809c15036b8cbb9a4e50915179ec136cee497ee0a16015a4c7a7af53022e7ab9ef90785ab4e233105800396c4d7802b7aac9b4999b803feefd824a2007777b +Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/ceb2db1de7f5ca31bcb9e33155b8fd45 +Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/7d5db7f81bfe825dfbb824b4ce65fdfa61cba3040dad12346d7e231ff868d0bd597ca28b2e3aef8f728628e93f26a5ad9a95cee02839d62dee25cafb0744196e +Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/797c3a5197012afc428625517927b0bf +Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/2f36b2ef2c0a87b8ad2971f3254b435098edf35f7b1fce795fff00fe8b0e5099758a7135696e6fe60025fb61c14865f5b3c8d5c3bf84f04d6f24f6e3b5809894 +Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/811e61e240af841f71c11f397d7a5648 +Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/abe7d7387b9de4965505120f41705ac59ffcf868001f2cf0081b85ad9b750a9c906b311055e4c381960e23b6a938ddf41c8889c6dc9f481c471f0509b26ed4de +Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/d21460305a04bc2e0a7471fea7e4fac3 +Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/c0a9d771ddaa423f341d322bbb59d18060255a66a6ef8800a1b4c3c9ccecd071a7739923a1dc17bb12b44f6a3aa012fa9fd9316345f96bd45e624c1fc3a0086d +Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/c860424b11a24eb5f162f78e98365c5d +Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/66ae467bc9cf489b2a13d49f4ac947acf66b4b0c52dc71b3faa356fbe999c592c7aabb16175f70fa9f8ee12f303866c5ef9e3290dead97b481ba47ad046ce1e4 +Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/6d3d11f8704c24f405b56d69f85aed07 +Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/479e54cbee1378200bca8d875fb50381bba2d91a36dce96d2652b7e8ee2f1a602d583f13ffaccbf02cdf28030c31982072004e301e4ad43e7040f1432ebbb9f7 +Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/b7ecdd0c751640bfb45b39623f7b578a +Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/a251fe2f94bf9416d656589f53b277bde01b0662a255ca18a3d9bb62293b9db74d446c771939c2fc3d9c59f39b03fd3b4f8fbc3f60335dd6f779543e11b4aa50 +Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/09eda4af5a0d2bad3336e206e53916db +Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/ab9231c36ccbf8b5c51120cf9feadb5c0ce85edec93d5b05d734dc0b3304ea91d01516fb854572bad90b5ac02f766f214f60be25e5e8d7cc6ef43c2c2a761ce3 +Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/ad3c4f9cee5c065ba2e5f35da81d1288 +Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/100ba320f85449f0c7c7fcbf782d5e674b4a4c34eaeb861e16af09cd5eb0198103a9d6d57a99851b2f8fcc6e05169afebcc1c8af280286d0bb5cf711708a5b9b +Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/87cdb57d1d99d67a01927289d8e52882 +Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/a382fa6abc81aba8d59d9cec885c86d864209c80d1afafd85b4f20509ec7720639909fe3f8bae06a9938a697450fd6b300136fd892fabf423708df33454667da +Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/ddf46decaf6982033e329992461a3519 +Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/0037d4792c72fcc127ddfa4588bbbb7ccc5f3d378218fe3c9578cc480dd81e35ae5faf58cb037609359170be9c3a8d5cfebe72b5b91b3c3677c882471225b353 +Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/94e2b29a4b43ed82d4ae613a4405e3e9 +Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/6612fcd70d7754a56ebf1979f4c2c77075234afaea66a46cacbf583198be6d8db0620b0b44f9d1308b7d5931d6fc9474cfc8897697609ef76244ea2dd9396fe4 +Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/5caf0a59f5ceaaf22721ee1aaec17f62 +Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/1294f3ae159d91ba0fec7e90d2c043087a3ef8508185e91d3dc53e33359e7abf7a4c0a7da2cf8d39062a5ab8e62b297991bfa22816de753890c6eca2920fa900 +Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/7bb1190508b5501940c5118bc6e896f9 +Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/222fe7aecdcfdb07d594cc7b47bb9a72124118a8549b6cf20f114082dda64606c46419ef9e633440b7c9c0aae95b38c64c8c7c47a4cb9fe7c96e0689d66a960a +Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/9e5b664cbf3f71489812cf88fdacd232 +Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/a61e2e62c967eed4c987aec6d85a88fec6e8031f7bd3254ace8548ed283dcd593704941f09642b79a7609dd264e1bdf5aa7a7328a676ec6f07d83e178797a8f7 +Clang.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/3ff3989dc561b3e25de33862c6607a2e +Clang.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/6de35bb12cfe18f3f3305ff3b880e2f93fbc5a941ecdf704012f555553f6bd7412aff3c402443b394ec83dda1cb092082c08b6085045181b8832139ec61a4ec5 +Clang.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/9cf19e989e28fb226e1705aecf94e62f +Clang.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/c2bc6a4e7313a6065d827d33c88b05d297828f6a2f9d6f5a729fcd811d9990e744d92973c3f283010d798713df9858106fd25fbda7654ca843cf85443a3e4fc0 +Clang.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/cd5228524e17b0d1a6c6cf231592cc22 +Clang.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/af8dd8b19e2344d1df07ce6fb89fed3be5d137eeb43b08e8ee7024b6b2c6705355846ded42e9b5583f36b7d1ddf9a0530cd5943e3722260e28b52fd5fc10033b +Clang.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/33d3f0259e4426101964bd3708d8a35e +Clang.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/07486f4c11b8bca3c483dc60b9103927648dd74b468fc37f99f029094317cd050ac1083e82866523cd018a031293ae9f4e7986d15dccb154b9208bd90732188d +Clang.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/93c50e6d95b59145a5c7d0afe2e20c91 +Clang.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/4749856cb2739f7b0ce8d0930d4274311e5ea753b77150e732db0ffe55758f0aabcff06c97243a7b3954a1b205e975bd55bf6755e6f3d55407b698135a2b8029 +Clang.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/2574258d11a12b23ca3194a79e3f7f24 +Clang.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/c84f7054fbdf79196f19712796317131d57fb324a36d8e8cfcee2522261a127344d52364f6b2272136d5e36eb3f0c1a30de21c11aee11d4278e6ae2b5c406d72 +Clang.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/ba41df7ad23f43f742bb9b2cc68789ab +Clang.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/0e2b1be78c5cac46b66ad8b4e9853d45e85216625bdfa023de71d7871814ef1e7da58f5a9c0f8fcd5070e4fa57194243a759a1dc906cfbbfb7d332db38fa3635 +Clang.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/e7172fbe2947d01106597c4d1f32f091 +Clang.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/2ff2ba01b6ebe439acc9e4507cc81b78a00595b824d7012b0a404bc67d26bd1640187afb4c368360bb5d3566cab53b7a6380cccd0d062800313e9d30152881aa +Clang.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/9da1ed32f3207dd583ff66ec54a009be +Clang.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/3969a16fa4ad2789cfe202ae1fcf954dfbf332247bcc4b44eee725086176a86850383b07ea3454106447fcb464db29fef726885be3f47156835f737a446b0ae5 +Clang.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/1a0cef3a60b6388e3225977eb3dc2717 +Clang.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/80513e5902ad738d7157ce25e33e222fde04978e0d1a7e9dbb407adf285b6632a869334caef29a72794fcf74d2728b514c24bdd5a79e74c47a4f21a6da2346f7 +Clang.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/deb810d3c2cffefaff7df8e133680962 +Clang.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/f90106f0460cccafa6fdfe6e1babd95f002e41801f6091e27881682799fc7c71f16b8527462555fb769b94a0394ab64c97c67bb2dc309a1593088972fd661f1c +Clang.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/df41dff25d872279868ab4261bb7c919 +Clang.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/a01decdd5ceade5987e192e379fa8f44416364b7a256a70c60e8ff87a6073bac32f361e2625203d804c0110a256117ef7462528dc732a5e75e12c7ccd496fae6 +Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/771216e42312936decd5fa2ed8e43135 +Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/8d70319fc762c57c502c1b6db04795e71458aaa337e49f970025e4fc31ed1691c35fe450bc28ecd440aeab1e9f21ff399ea3dac7e9c9e54b97c1a0a5dc3c7f45 +Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/d58115e5fda3ab222493267841ab4219 +Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/468ca9fe210ce03e48a8efc50b9410ebdee2d3c73de2324c2d7242d86ab2b32168d83acd860717f463524caba5c59b32722043c1d452ae2054b3eaede73807b4 +Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/d453bcf391b68f67492b5258177faefc +Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/50d4c8b4a6d06d6e39829b0ef07e672f91e90d64278d87605dd52cc82fcd967d2823644a1501195e88e5e8c4e23a96ee2a336b1de505c8785fd2eea660f6ee14 +Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/f6241b5c64ca759fe8c6cb996b7fa011 +Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/b7bfc9e057a124ce5570e49016cf17309e77da4e24536610b652936c51d26b7cde26ea65b5727bb0f990b1aa3bca2c0be35b11ced5fef6d3e5a1a027d6f2e958 +Clang.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/c2fe21a6699a10e3e639ad3c109a18e4 +Clang.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/93a6fe1e11c35af12e8f4164ea9fc9c80cf706f424c4877b4c302b25a4c7f6ee103dc614d5d7d44586cb9948c819986e8442a7a4ab8ad0f21a4d82899d97baec +Clang.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/2fb786ca049cfe820ee26d5a51f5f84b +Clang.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/d441e0f83107ddc025a6a80cf3c6865598e381323d04173dfc2a60c985a248248799fa9ffe36f8b97c75170e23fcd563f2aae599487dc1218bd24f7d12645183 +Clang.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/b4b8134cb2dc032a19ddd7f8c1a66dcd +Clang.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/4fed4767d97b2b3c5e3ab60ce5cf8b5c0bc65029ca57c53b3bd50f5ed924101583ea156f4df31144bb85e1771ed5af72b18f8cc90b777da0e07d6631ddcf2a3d +Clang.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/796cc2877173231a6b15a3ebe5bf0356 +Clang.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/18349ad2a77786fa1566b234c03251eb5e805cf69b6fe978fbfb40ef38fcd5bdb41c7773d131fe6867651b151d11d61b14a52262022dd00462daacad936df3ff +Clang.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/a7b5f9b64abffeecd1255de96cdd718f +Clang.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/0429d2cec6f54fb7196030857325e761aecb0f76850a591bf532b6482571ae908a94c979875e178620b760d2b6faeb06fc66475b3633975905e6d20965abf604 +Clang.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/894eef7d0411e065b63f3adb2a0a2f02 +Clang.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/e28a429aadaa8a5b91849191396eb031be9515ccd4913d03dc67f13aeb3cd71f48a8ecc2e04cb6158156fec0ee132133ac1e6de013a86af5cd6a82a3bdc5f0c7 +Clang.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/dc1701be0ee2ec5c1c0206df5a5cfe09 +Clang.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/7a0fe6b9086bc3458c6f2b0797f5cfffb647ffb5afe149dba437e0fdf21bee68971bbc50ffe0d07e022647b2176364e65536a8c0fc77aade37e9ed4e7dcb82dd +Clang.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/2d6d11cac078e7e9ff76d957927fde17 +Clang.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/886e1558c7cf71112169f63c0571a98ff5b98600acf1a740e3d32efa6df5387931f876ac13aeb2095cc38e02b547dba24645ef6ecff42e4edcb1aaab506af6e7 +Clang.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/19489b3d76838ec1603462b6406c2316 +Clang.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/ac6984d4117735727185306fb60b45fcb81c4c337d56ccb78040790cbe38e7b010b206e1fe7fadd46d009089c5300e181564a30b70c3871a24ceb0a80f83b106 +Clang.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/cb2ba20005e1f6b656b7bab26b99e23a +Clang.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/8efeb29e9b4e627966e96b05c75bb842e89260c63e8a8cbc018fa728ea72aebca331a0089c5cd425597813b8861e39b898f778a937f32d915526fa108a41af7f +Clang.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/c2fd4cbaac70ce49076ae72f0d00470e +Clang.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/548bde94d5e88e7067acf4fc2658987583fb68b032f1416e41461cfdddd09ec68c38cd5529281d3ced2aea93ed57acb260c5f4597dcce057240bc12b394b873f +Clang.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/05306ecb416772494abc55ddb68c7163 +Clang.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/a49f4800efe99973b364dbf07443de0d6a3763b4535221c0fcad5c3a8076d174ae343be8afe566954c3475fbb5b54c712cb1f2c54d222715005bb94455307662 +Clang.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/4eeb4cf34bc8b95f4fdd3280d8f1f5be +Clang.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/f2ad27b4e0121992cabac4d568835a988217facf5ed6e6f095b2ee124ed2eb466b2deff53ba706ac98a3777833fb94f4a4efd692bfa775b0717af27e30d81176 +Clang.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/c39e96db6850939096c3f9d913cf0123 +Clang.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/db048395b39a55efa88656d6ba6cecd8b394b125095fc522c5438cb9f7d1dbcabc0ea5225fef7f8aea405571c2b16b5300dda23034461949da6b2e521e716c82 +Clang.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/915122da271a7fb0f486cb306a76e06a +Clang.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/2eee597113a0d94514f973f435a30a80370195388b78ba3945ac64d47baae47d4653356d7bde884a7ed13a28172b54b2bc827b6cabe09e35fe567b4770dce817 +Clang.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/49e329091e6f1d6f03576c662a4f0150 +Clang.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/dafec5d9ed6209f93a910b8162f2b77b825cc5854bbd21502f91c538b529e28207d9a0eeaf8114481638d500565f1335333f1630427d29e7e85885676d7c4174 diff --git a/deps/checksums/llvm b/deps/checksums/llvm index d96d2da078b1f..629ad74a7b09d 100644 --- a/deps/checksums/llvm +++ b/deps/checksums/llvm @@ -1,119 +1,119 @@ -LLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/8eac4da97313ba075330c1df33b7a85f -LLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/7e49321c13c854ee43e7b5ec66951a2417debc79e2874bf35bf2a15238cac622bba5967d8e833152a2967b259b4075573f28b4b3e43817d599376c2ccdcb23bd -LLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/2677a6865f3e9b53069c231594468730 -LLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/dfc917b5f38fdc96492b0c80d82931a1a625452498aa0f9ad50f106673514e922c109a1ea7a44e69473abecd527a2ba47d8aec42ab22ef5ee6cc739813ba043e -LLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/6e89784f5981feea414f9d44e4443a92 -LLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/a37ad85acf99bad4aa435ca5ada57f75117211ebe68a9eb130702d256710ee3fc7c4de6f3dd058a06dc3e2f4474b9fabeefaa8eb0f8b9317aee0459d2d686610 -LLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/3b63e849255b7d5bc6b159b8a6612c44 -LLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/bc5d58c4be42fecd9e9a093eaa56160ff552f17caee2560705efe0532ebaf9f580f01fb44fed46860e6d3811f0386326f14e71b1d267a055484ed6620c2553ba -LLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/1ee1593da5b0a86425cf742335b055b4 -LLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/31bf4ae6b3d0eb836be946f3fd4e98d9ea4596a55311a9e37896a639dbfa4f2ef2ff3d2ee716c5f915c68d271646193995d4312975194264e4103a22b6c013b4 -LLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/bd1c69390d4ff6bc47ac7f20fb2fd596 -LLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/5409aca3de4c18f97367bc6d1f7b9761de80e1e7193f981b5870f6a4315ae6a7e0f7dd37482f237996759f4d04269aaaa7ea89b912b83fe2016dcf78f94b809d -LLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/8ab3ed766d7620b7ffb0995be9126dc0 -LLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/41fc3a758fe586aa16051ef7b88212ba68746ead8446d5af247f1a337b44a60a7f8eb14891aefe2195d4ac6eb4c0b97f047c5b16a3703053986ae50fde4e4854 -LLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/481cff2fac3420b66a73f8541cecf8ff -LLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/c0255dd7d30eda1183aaebdf0ef5d54f5b99c9529a9a36a29ebbdfe801dc6ca4be6ea491584d5b608be1e999c5504211e89880ad729177a47b195de37ef439d5 -LLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/6f664b22078386c19354371eff14cd7a -LLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/9bb3a3ec6f9b6a2a8daca946de94a0d7297880cecc695d39d049347b97a7c3bbe4e126c41c34c7ca26a2ab93b13d38c8054e0fcd373e7e73975c1a94988d43a5 -LLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/51d963d135d08f43897870a300428166 -LLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/7cadccf6838b87273e6afaeebfb71cc3c90bab9d0d3211371de9a86364a3543d97247608f36d48b598c25eb605c9ffb5eacbd1f55a6af55429caec3b099b6b53 -LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/6560ffb0cb4dd783fb658f0990b963f7 -LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/2c4e3775471d5d385b34471e700c2c2d607248f10112e3b95723181521b6ad6a560843bf047aede8b3c16dab7d1a1dfc7611b55b0b610d5dd42af2099bff70fa -LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/6cb2402baf7f4039b541e42644e5ae6f -LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/4ce2285b81650b75385a476125dae6c14548f4e70989aa171609fac2d633a0d0318762cb66331824d05faef381ebef4913ba70c97d12043cc96fdb32bdfce1f9 -LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/1c3ff1c0213d092e1977dc8903604d20 -LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/0abede35823168e4d1a6af03d5cb160a731f7e3e1ee022b915f9516958171a78d2cc536786c7f2d15f5a4684bcf0e4e33a22147999b53df77040f7d3b2fff8e9 -LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/18ae412527f6550ce7ff803ecfa367dc -LLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/327b794da935648cec99acf4dc5aa729c1495ad102bd6a3bea9f579497cb10aad79d912640cb4064119cd2535b9dba20701a99a2ffd8cd6c8e48ab38613d62ea -LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/14f35dfafd979c4da4eeaebcdec3248c -LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/d33593b8aa8ce51343767c0de6e7be5ccc793089bb9fa86e13ba7a732368767b96bd5219fc890fd7c6c151a79419b0e8af5c7de1f29b79c36420c02dc988e636 -LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/432bdbb6bce6a0b0a00958b528eee64c -LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/727e8c254be12f8a8bd583100d72fe8792c9e5aab99e5e5d7fe07a5fba683792b8ac377c9929dfb9ff24da3044a8f4bf467d7d317ce85e8ec982aa53895bbe03 -LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/21b4883a4d2bbd287b85187cc93a8bd1 -LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/39b5b5668057afd60f9cb48aabf09d9fb404a4e14db5eb13ae6524802795cd5b23a2aadf7ab0f3b74c73a782ed72ed9a82c3ebd98a7c6785a4cb478fe128cc8b -LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/7c969cd27ebfd0ac55fd9965ec064999 -LLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/5bf1aab8b3f48494ae0968434ae5b1670620a7df113f9b85a781e7ed9025e4a91b8f3bcac900657c8d72254d1d98c7399998b7b14fd164d303749e6816aedf67 -LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/4c0b0d214a471e2a1f1c95368c939da7 -LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/66e840c7117f6c4561050a8719ddf91e454f5209377357fcee15bb97e69735dfc1def3571adef292a52bdb0f6c9c36b99644b86770435a453273a7ceedadbac3 -LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/208e0fc55996d91e60a9b44ee5b3de04 -LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/21d75958120a8e279b8c7f05f06338c3904cfee2d6dca4ee2f98a7b391694f147577388dd4662e5b0b5bb54707d7bd9fd5e9b8011519e6c275017d856c640053 -LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/1b4d74a43a5f592f63fba7247caf2d38 -LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/5317ded3758e3568ad957892ef051460729d35d4da716d272b88de4789696d06b4598417beb9e076730e6e02a0d20f3dcf4222638986b8869ca9fb400a3d6808 -LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/aec5398120865028826330cf2d38f590 -LLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/8e0acbe7561692f272858c6d9f918a374438f5cdef94c2522012dfe671594c83e6d63f91475c502c413df90b9cb04e5e8ef199111f179f6f1d2f840aedc51ca1 -LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/5d8977d45615cbb8fe0bd103364bb100 -LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/986952f4a445869b607ab634f9e871356f51992ac95a167b45a6d946cf7092df1babf45d409bfb86f4433167b9deec6b9e394a75c445a37740180730b14770d3 -LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/9ba14a5eac8c25a29553cf5c07a9c61e -LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/3f68b6348f05bb7bb3deac81678233b7804060a5dd9ba875e68fa4dd59a55ea8447326243c1dda24de5bbe551af31b7c94b66cbc047de55b7a21a85230fa642b -LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/02a3bf9ad33c815495be4d426281445b -LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/2197a60255b3cda2e7e0a00b81afddb552c5a65385d28013f33dc93067598d4246097a130fb18568fcfa5c70c8f7df04ebd1271bca40fbbd9c57907152d50f0f -LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/c02344cab90c0a78fe3e949c9ed6e9fd -LLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/ece70d7ca2c5027deab4ec26e376a0d193d1a9c703c26a7d9c29e03a853741c3296560511a5b561535f04ee61fe571a07638c935211e1d34068c0bc108106455 -LLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/454166455da7bcd5472961645e44b517 -LLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/64ee815667e935f088ba7d215b825390da4d58843cc878b9474479f86829f8db92c14e7058d18cbf419b140208123a2bc3e5952bbf07dd03997fb2b2044b1111 -LLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/9930627a964dba67f8245159fa97c6c7 -LLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/8bdb26d9d2d5ef3fbf64782c318846a1d2a119ab0b8a068428937c71b04cb2ec18b6a7ca367848a3e9afb7b649dfef11954dab0e27757f41a515b188f4504941 -LLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/bf54dad4ab1c66fa0494eecc55689440 -LLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/9f1839e23b86266fea0a11863cfd874c256ceec062fac843bf49d780e3431439527421b747869e1aefd6038563a93815c62a935ae323e19aa61b33f6cf4a5b64 -LLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/1a711b82a5318c5b425df78280ed274a -LLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/df956f922bd1a9e2ec11c84154dc613dc424f3dadca20e908d39b5883e2668f8dae24cc828245b420a5fb88f8553f4393229b4dbd5b0b7f468885bef365609da -LLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/50f42d954ce79ae055b1d99e5e074f17 -LLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/9c338a86f77329b456b49b6b87a25f04fb39c7e818b65fee12cc83d7d9ef6199022899745920b5a0b9450537e1a5392463295472f63dbf317fa6f35cceb8a6f6 -LLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/af0adb5edc6f5573d911b285b90a3972 -LLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/fdafad0afdc123b0adacfa3dd546004659a30a4c28ed118056ee8e69820fe1f33e6f052bfcd39ef9fe7677ac23ab87e52c98c2a248e7bfdb58a3da651ed5fc16 -LLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/78373933d33910dd268eb66d77d7b9ff -LLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/bb9933b984fd494a44662a3b545700c99d166bf7995654c8347fdbd19226c1ea699a53c6d4dd9db17458ce6d34623475c8cae97ad12d21c48aaea8652d3029f9 -LLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/aa21cb389ccf5d49514e4742e34c3a8c -LLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/29e6b732a77f1bb2871d185bd9b8c79f721124a659257085b36a19a520ea9146d4b3d9749d01cbaf06eaf259f240ee634586b9abf53b580a8c0c9aa51575e7b1 -LLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/b4ee794dd157f1f2115a169176700eb2 -LLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/e33fb35cf3107255769d8a788d59d39a7c4fc5e50ae489887b767bdb53f5e2a212eba485af8c937e847dcc96fd987176ac5a97cbd7f72aa84707404f2d8b832f -LLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/272dc8e5a14ac5ccb513327fe2fffca1 -LLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/2b05f64ec0a6aac2210cdf569a32eec727270cf667f8e83167f7e45f62dae5f4e7b9d8c6c24fee970f168e5fa82fe7d8dd5f4a277feed410fbcb754257869c26 -LLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/aa4aa3280955c48432f5e44ede0cbab4 -LLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/4f47ee3a6c0d225108993bdf5586cc5071646513c9991765efff42b35c191e207e977673a688ac33b7d6bbe6b30b394b892cab8631e7c272f01ae24a86ae1f8e -LLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/f1d3a23804ee7383bb545bce4f1d2443 -LLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/5a7fd24e8406400431a2244e4c19dfd128b050f6e6968dd3df686969c3458d6446ebe09393e64989369e38fd4dd099e98ac5f359a7401b4cf56f1d5b777dc9a9 -LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/8971e352ad29a536d48938406b019eb9 -LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/f3a32879f34731c275bbe5d157f5776db176be2424ff11443bd9325d5b3f1d6d478cc9d8f5a8399756c38da6d253d1de13089f20113c24d2dbfb0e8b80e618ae -LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/5780b46302e5388d68f7f5dc0dcd5ab5 -LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/4a02b55aaaada0946c3f6dbae8bfe0eafb1abbf5d8b231bc426925ac28e9b5353f7bd5e12e723a54b72cf1a48193c5cf22cc68bdf4e9843bb4301b9ac1effdcc -LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/08fbaf91ff5c730977f15e869f73f582 -LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/6492619b05cb4857d17a07494b4d8bed87dc2fde4f54492f7ebac734a81bb6a6d854e8f0e3c9d44b5618e7aa446eb179c05d7e5e4388d8ce3d1e3a70bf5e2260 -LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/01982b5a59b24b1e06afce35a3657ab5 -LLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/a78cb2d023b12379963a902df9eaaa6caed3832420862852731a830334177d7af38364c75ee216171ac3978474981a50153ce2f62e6e137cd8c1e403590002ec -LLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/45e76481a0816607a619cb74014ba50e -LLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/d92235a2f9f271f6b2f4d0c70b143cbb7a57fceafdef168f5c109e03aa060ca77374989f80958efe8f6750dfd38be64008c8401103d2240b6139d4ad55595610 -LLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/0b6ed0e46bfb8d11663600e755bb43f8 -LLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/5837590ee9955e670bfefc6746d896593233a4b46033a933369df70f9af86af2470986a9d0903e60e14966b3c65c4969d1e599fd08f7db3b42b985710c43a883 -LLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/82c3b519fe20a72f6a7a1540910acf1c -LLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/870978451a61ecd9d3fa294d6904e0dea1773032830b2a4e6489fc60a0a4d34e57224249f41e0292cb04647df2363dab0ab2a4f620503518c690b233c85a4d5a -LLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/86747234744c7de5d6216f852d175333 -LLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/c661e9f5a47fc8783365a3718b2e21dca72cf8bd7580885a2a7ac007eaa8921de3880da9112ec73f2f386a7af3ab9e71f183ce52840db0d0559d4e6b218cf93f -LLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/45c61ca66a89d592236803c896d1c4d3 -LLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/aebe144ec08cc61475f751e4ca869139df85960de6b8117abd0d4376de66f8383b1212d9a3c0591442feab83ac86e8ca6e4d3988be93efe69d605f3282e5fd1c -LLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/416bbafbf3c3b89f13bbf695786df058 -LLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/61fe4b7e027e2fab51d2309e4dc853beda1be305c521423376e30652c123779c8a8be927a32e857f15f74c23e8e1361dca7c28257526dc2cc5276624e4b3140e -LLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/cbb5ffd80e05099cc0897fe7e06833e3 -LLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/190394027a27b7ec49821dcb8bc9709505ac46c6d6231ab83d6229f93ada6029b81f0c7937c4a057c6d8e7e92a4485e32ee8d76b001b97d5408828a4a056240f -LLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/e9f97576575e03b500d593c06e983c1c -LLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/2598129b836060ef32680197cef40608b7e48ce87aaada571e457914628e0c7960c56cb2db8eb7e0468a865f9fe7fe74ea4099d7af3741d8314d37991dd172b2 -LLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/48e47f997aa9fa9ee29b67cecb54afb9 -LLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/1974d9fd350d2ea451eca64a3a4a51b1b84e9e4a9b6a86e41c2941addca787229ed464da25d55bebab903793af096272aa327391eabd14b1c780798fef72f440 -LLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/e1158414a12755d1edaf5f736408b851 -LLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/099c2eb3bf9e4aafbc8f5e206ec1aea48856549a7c48ba7a0cf9bcfcbedc64c8edf235906cd6a8917f673be4e7f01c6c6bc10fa1bf6eee8e3ce219f164134784 -LLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/4a34df9b4a0febb1211c0f245c0e8a2e -LLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/5f0adda9e9f7fb34f1d6451246fc2e815edc68f489b21efa6cfdb8ad5fea1ebfe147ee55dc1b7376aa23f30ea65a221af1350a2815c2ec54023cfe13e463aaab -LLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/4dbd878b1c71c502cfee553ca0817d69 -LLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/21246244ae7109f26de885965a39c88bf268476e7d2dd6139d793cefffbf84bb9a91e9bcde2656af7cff3c77c4b1e64a3dc53bc835e80e16d997103655c8fde4 -LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/1080ce8588dbed9f2035c919db69cb7c -LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/d5326a7d82f3fbc4451de483496197b7ac726bffa76c0ba19bf136752e75d150a0c4e7578dc51934eeb47371ae85e176971545525ff9af293e433f11ec12cc77 -LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/78147126c1cba62e1617b51ba5068618 -LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/6a295619bc6e314e9563f5d04a3e617a1e1e861724e9142e9df9063e57637ce881b38b4fded31356dde7f25d0e8192dc50be6aedb6c08d4b3f26aade5c020d4a -LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/3231888c0e1448c0da3c33c651ce7346 -LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/ebdd2a19e0f3a572a5546fa9adcc4353d1184ef8eb89f75ed92608b2e797211ecae59feec69ceafd79724785e4ed7b0893a8a21a1e5645404f9009684f48435f -LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/d55f62f9c35ca2132f319e6620fbb04e -LLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/4f4848759df3feb37abaebb64abf56f43e8940df281f5816cbfb16e443edff16b6ea52e10dcb780d2e4c279cb45b9f50bc05f024ee96b4751276543095d00490 +LLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/f8898ac241396be3f36b212dbdcc93df +LLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/cf6c73fd44d50eec0ca0846075e0c5dc49618d356d872fb631537f810301e2574d75b5d4df98d78b4b5307321d8eb726b16842fbb828df18b683303a9453341b +LLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/16d5077bfa39219e5e6bf77d0451e669 +LLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/220c1c778cf3a970db45cba327426000b19138a7a9fc663713c42ec1db67092ca31c04186b517ec23d30e4c013a6387f052a3602e989ed65e2bab537333b4c59 +LLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/cfa5f23bca017bab7a770af82c26d5f6 +LLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/7bd257f6f8f6a834404626453ad5a746c3cf26cc8f0bc14b95ff28fbe5a0420d73bba035f27787eb8a383858eeaec1b3911cacf203e0ae765c340d360e87e0b9 +LLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/cd5284fb1c68c6129c038fe346b85456 +LLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/38772e0801d1f4b953ec05ff76742c769ed3c1dab4ff3ac2ca16fec2ae7dd29a901d551750f7403b11cd0fb0b850b80448185446c2aa668125bb876758cc0a1b +LLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/58636f7b5939a19cb3161d509c9cf074 +LLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/41532bd7a420a09311e2ba6b6f429110ce619d1b06bbf8100d79cccd7fef40ee454a0740ac888f697b5aef11349f98a82730f793130de4675ce6baa7af885b6b +LLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/a32713673b9989f5ee9c199387f6c7cb +LLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/6943d9548707c5f27c50c54d5faaef4ee376c16230c8b15ec54f59ac2dd20df37d4896c9f005655dcff2dcdad5cccb17189d133e9c3a8ba68fb899dc50ce0ef7 +LLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/287d0c02efb240f08ff037f8661fa790 +LLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/26b04f55e400aa8e00fa7b5703348479c1f8a278279e26a87dccc316912a2649d600d93a3f31610e79d7a2ca3f98729c2b8cb54d2742a6f8622db21215310111 +LLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/facba02ef69ace92495691d777476930 +LLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/dc9a44162249ab597ed2e3d795c81562abbb7f182ed53c2ed367be35524e8c507d15b74a4654c0c74038e502e14df3041b644f008abd379e6c7fbbba70551491 +LLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/9cd2116f05c25e234fff49964ce2671d +LLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/ea219b1f76941fa9d712b6b8a6b3156c072c63cc0c31c003471ecefa3ed6578a8a72e8133dbc57536535ebf56de39afa6c5a1ec39427138fcd6f59684b557f88 +LLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/003f3221bd28b40ec6ab9aa09dbdf636 +LLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/7168a2ddf8c86b964d02b4dcddf5f0bde4fa1dedd907b23b2eb25a70de99798a97cf05518bfa1669cdd7794a17cee0d3fed1912f33ea0627e12b927613af4fab +LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/616c3f87030f3def3a82a12c0ab04805 +LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/5dd8fae42aebe56733035c53c9511913deee4372ab31db7b7aa088f2e43cb6a9cdb222042b7324b366a3a411ad88aec6b337231906081983e37da447de095374 +LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/7b1745ce99166f969675392d4339d2d8 +LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/d6fddd5805c5cbb454a93b16d95ea79ad8c1d5e8a4d4d2b181592b1a63a574392ab820e380199b1c85f83bdfc39822511dd4c274f3ee52a72fc12aeb3db437f3 +LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/08353c3b95d7db89754e5ff218ec7a63 +LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/7ad5dce0082e303b02fad9eb950cb0f39b9911f84b7504230026e49314ed784ee00aee085a367a460851d00471b2be15e284c084cd3d6f1256ffd604e5ed9153 +LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/d4bf8c670b24b0fa1546b0ae3cb18517 +LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/aead293a74dba7ed80511f8b4d84791e99d1a994d690236d4cd62d5aaadf4c047a98edc33454095b065f90439b50823af440af819322bcd0d9c3c9445cc7bc68 +LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/1fbfa0e36d3cdf63460a941dff2dadc5 +LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/e4de2b1436a6e605ff9977d900b57acdd8c69caaaff3688e7e4b584e5d813df40d03e8553fb5ac10612f87c6a37a8a713360c6ecd1891f60581ecea6d0dedee2 +LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/47ba83307c3220be4c7e9eea0c7d3738 +LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/f241f7ea49645a559b08af805389a05d9b4dfe4454df88a9cdfc625c0a02a6d240d85bcd21234431a294749edb8a301cddb67d5a292da92a7b85164656ab92fb +LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/4dae8e2e6b67c664bd55ad4fa7676a15 +LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/0ec2fab4d4631f8464bb0870149b79128c24d891f63bd6552f54dfb23010ff91685fa76824540b5feabc5c437e0b07071348ecfb595d3d60251a46b81e1684f3 +LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/64a9d279e3a97c8a30de831037296ff7 +LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/e3993f8a6196de2d823eb9fe11658c743715624376901f3608411d96bb4a474fce0ed05c2b664b1561b682ec2ca0d4f79c8e0299fe5f1e508fa098b288bee816 +LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/ee5fdf2fc8540c584fd6514c352cfc7c +LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/8c274237dd92aa45f63ab88b445486cb56a4ed243f8fc223a47894d24a0fda61a4f352013660fe7e91ea73f5c0c8f2737ccd975049ed19899598054daf110d7c +LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/3781760366096b4a7fb6993163533196 +LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/0a6710e9e24982371df4a9c7cb855e51572febd7603c5b815dff75bab71dbd56e67ceba18b62dad199fcd9d07fb52ad321db97c3ad4260d453a9a947d515d012 +LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/90bf20ef7e47aa8485c069138b0dc8ec +LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/2be47845cb039b332514cee2c2e1e5f7b139d575bdbb115eea3b92a6fbadd0a52c104b046c496afcc027cf8b7191dcbbeacf8b449796062217fa1aad5f5bb798 +LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/df0e5d4a5b0be0b24c66d9da374fcb05 +LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/3e78b4246a324af11b08a9174a19506a4e0ffa90430d22616e5382a67d860485169b0db83058b6f35aa9e074b255326aaa649451d22eb6fb928e37db990af454 +LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/13b355afda84dbc23edd6f877290af40 +LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/2e02c2defc936f6681122820dd56aa432070f1374185a807d07c1224a8baeb653df585126553f00c1aaaf642e683e3c0948075e6a67baa963f512968f611ac2d +LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/e03360fb5ba43dbce6f26eff1772d6d2 +LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/f538f9c263f63bac5d663e89c394934ac6752f3a40780718c5b6731bfb1963fe79cf906a5435147a9253e313401afe8895d026b8f2e457241087d800b81ea151 +LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/252a43d72038e373004638bb78ba0964 +LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/b9bc5f6dbeec198574cffe5554e383162b22b95532093cc102f8e06d553ef9f75ef32989ca4998656b3bdc9d4152d692116c265a2d1ff86957d2662d04494277 +LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/181d18127d7e4058e5be7c3ed7375357 +LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/5843ff12fe3ea9ad2d691100b58c2b49318389a418837e7f4b684e82f7c048b07abd3d5ba020bd8a86e84f6c1582ae8670d106f3286884f4b8737655e4df4f28 +LLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/872bdd2708fd4bf7cf76545b4f4017ac +LLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/d2e56fcbf4392588252625c7bd968d94d65b2db702560ad4ce4de7bcfb47d90f7784245cf7901511c85d7edcd025a06b6bc65bc621ef345d180542b4135ecf27 +LLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/82f3961594e04f578aab8ce1ed17b6ea +LLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/5a3f5d328e01b9db702f72ed36fd39dd900f7b1b80bb3264b69e13dc659d97a61d2ef146e4f1c78c7db1e6074e6ad39af3216babf3d16802543f7b55faf1e6f4 +LLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/d5f0d3995a9c24b776d8f60935cbbf03 +LLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/00b9cd1c66fa6ba88771f7b3b970aa0a179bcc765d81e7abaf56dd93c5b7e63ccbdf83db658af2ca6baadaba711cf261ce80ba4d8684b19382b9f4aef0248bec +LLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/7781851733ebaa6005d89002596ff1a6 +LLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/e56c43f4e23129edd880bc20c56be469f2efe317c8eda60797cac79686601f04960bc40daa0c520c10171193fae383c17f40753ce7be445007a2cb1e4f4d8d43 +LLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/83106fe5c9acb5ea59a2b7467136b987 +LLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/13157b1c20784aba583c26c2e1c064953debb6e29091d772187d287aad516ee5f6ab090c63aa5fcdd81738518910ad6be1d50961b006ee8d9ccf8158ca2cac16 +LLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/1fdc73b29ca46d9cf017d06bc340b523 +LLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/e456ad88c470740d65e175b1d8a01aa0dfcf934ca128079717a741d1368a8392e6ee434ac77bb7dac4e521427237526144c30cc3111c514e7e32927fb936d413 +LLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/1c72656bc3a71900f2a1d76374709a1c +LLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/4de14a92181aa877e9c18b3801bd2505115db155559772b5858279f4c65ca998a99b0226d80bdb9036a2d236a91d4e7d819378295f6c496b15973b74cb3422b2 +LLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/57ce3641a30b58f2f4db060846526d5a +LLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/304d0bf4352737cbe7c1e3691d4f155f35faca70b2ecb0ae6d524a6a4276406715c66283b6571d4779b58daed247a2cd12684be39069ae8ff25dcd71a1133056 +LLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/8fd5f48ed4777759c92a26b31ffa5f0d +LLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/98baa938c7b6171bfafbe210ad69701297bbb0f596ee0a86577f0dc21430263ce9a15ac4769273f49d3d5f16a6b43fe5d8cd06e3ed2551a6633b6f53025ddd0f +LLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/3f79c7cc30d70bd3f16d62d84cb19561 +LLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/6c97668ca3dda3abbaf8518b726b3bf5d7422711038db5b50cd9f1f7b9e97b146abdcebdcca7460c99be41efd3548077451ba34d8191bd680c789481df52c65b +LLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/43066a80bc08622907025da96b0a1542 +LLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/c1a45fa322229cada5ae3d5ac130280cf50544dee0332a93aa6814de3dde070ea00ff1c31f31957862466b215b9d0cadaf75aaecaa9b5e8fccda54ab9b45c527 +LLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/d53d5ca151b6521e2360ce7b4fba2890 +LLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/52fc82850f7567e138bccf8b4a6ebe24f090c762370f02e217c778a7eb154f9b0ad19130ded4a48e0234107605f4405b9851760bf70f74bd3a151be1c5a1f576 +LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/80f628fdb92587af5ad7f9a2472467fe +LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/6afdf26db0a7c01adf33d2f8c7a6507be0192b41caaee69742179610f23ca5ae1ded96288dc4859d9646f3282002ada7aff70651df29301f02e92596a39d9114 +LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/5caa2b8f1bb313844bf5579c2c0f9bfa +LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/448f2b0c03c5969d616143f359e8a2d1fe2788ab2f0085a16838c9b65f23e90e9d998be86fb9dbb29c62dab8394b638ff9ec5562e2e750897fdc6c60f53ec4ec +LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/07c9b26fe0d10234b1f1a2dc1b97b4fa +LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/d819a0f6e14ef32c8e2d3d7d5b87f1c0447eca34a31a6708c1bf99633580d962d2520703c5eb5b649a03a68c9ee02716eeda7754c337651e4b4a04653150771f +LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/520533c4f5ccdd0eda9bbd9ef200dd2e +LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/b088c0d39c920d26afc67d3be9139b51a327356d8828ed8abc2726f80e1ffc6dbf63b29dad7df5cb0ac654eeabb74e0b704f6ce4dadf97165ed9d9f2bca5130c +LLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/b00b98999dfc6da61cc551c5386d6819 +LLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/c7f59618e6f30ec38adb50fee57db6ac7456d538358d62614ee73ab1c3100ca68ecab07958d8fdfc32d30cfd621615f14a5ebab64f207caee22c51d883be780d +LLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/b8de143fbe8931ebc0518ca16bd22bad +LLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/fe8bb87001f1c3fd288264fd725b037d7b976a50e89bb423c44a3643c1dbeda49b6cf2a243ef9b03b4ef9b37c3336f369c725e46ecd9096373fb944a6e6be4bb +LLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/7979d24a368752deae6d94316c5fb75c +LLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/74701aec4fda4764bb28a545f6f0657617da7ecb0550b90b6c9397b6f8e9050a5ed23f499d2e1f413bf576d9c89611ebbc9ef4d3624e7f16937d27d2d0e92d40 +LLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/fe59d3eb8174b680d18ac6b270ac311b +LLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/97ac0a051b5ecc77e6cee41434fb20824cb300364f6fe0622de8472e650e9f6c287122c451baa1b4e58ca8870a1d61ff81550075d440738235d4f2b65393b5e4 +LLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/402fcc09446abc303b49e6b8d255fcaa +LLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/cf936a78eff2e8bc24e629c353d375ba9999db1c92c6c95f06d889d779a480b6d2bd14ac9ec2e1612f736b8d70d788130b9c757a60385af9b45eac28bdddad4c +LLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/64669d77f64106423dba4a209f5d6c52 +LLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/47b3d116278315f01749a4c27f9bddb2e758c69d861e7eb43b9621ea7d632d06d06f29d83e88ba60975c42f9c2fbcf1fc8ad8f5a8ae42f363ae5fb47a48997bd +LLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/b8afba947c590c67c42cf5b9a4fd89f1 +LLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/eb3c6b082c4e8e8df2b654664bc3981f3e911a40d4d90d7d15cbbd44c27822a76fb8e64168be4a2b2ec0fbb47e1fb5954f725be7a45a71c170220191c012db95 +LLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/b22436fca5aa043c407e4154a56e3e20 +LLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/4333ef60825ba4453ca11171b43c53d38eff42cd90d2f44dec9919c6c937a2cec4aa886fb67e9a3978a043b0b61b4cca8de027709c7b23f93196d78de939ae79 +LLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/a4ba7b2429d008fa8a33968a8b1d2e3c +LLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/897a83de22b69264ed278404b1c5c38ff64d1bd1f527d7b635ad2654dc13b480115023d5e0c0b2ecaa70f8f511b8c4716866e3b46eca96874eedb9843bf85ab8 +LLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/d4a5667eba20923db7558c8b6d590058 +LLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/c8ae3ea058aad9536f9866494c32b8fbe5306d97e23a779cf1f47907fc7d5d0e7635ea4397c0670366d5da7ffd2d281fd2bf6f7977a68b6dfcb8e923d82c8f44 +LLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/87f8c1829f04fcca61fef97044b5d04b +LLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/465d2c25af6af83e2a5e849e1144dd70de9563c128abd0064facf6edee9efa53b671e1d224da5169f146285d0f18659a11ad0dc54026c70ad06bf82ab3340c53 +LLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/21702f17ddd96c8a6d964ffc0030e99a +LLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/ac70f188ad64b782fd941a9a8da01da9fced895373827e625263427d29fef9d7ea4e7ae4db6fefc8bc68c204111bb70c9c434ed9631e4a6d57bad02f4b262f19 +LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/420adc5bd7205523332379a6ceb72f82 +LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/cd898799070339f8fea29059de5f73b396915872eb055853fb32840dfd3ba740fa5fdc2c1ff1848e261986eba9c467010ae3fb6b21d5d26046bb6780fcfd640e +LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/3b60cf9b472b37733161152ae25515ea +LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/6cd22c25746826c227d35e06f4d028855d0888ebd82cca087e73fa3abf0e189bf0bd054539668bbba07cad3d06d819273813eb40e0ec1ff7965f8df8aa741764 +LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/d7cd0abdfd4abbe963de65f2f068eb61 +LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/63e06497093d70871fdbd3f2c3b3e536643ac81b32cc38182052fe2578a0b415234678392183c50a571df258c3c213e5b657535dbbee41e5f174d5105d504697 +LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/77be17016e40bd4c5816b33691d63a6a +LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/fe9cc457bd2a24072e1767f5de441a8a22e9a113f8623d621b57f2f0890363d326b977d0fa9f7cb6e1f451f6c80127934c9fbbe734246b393953d70f85d0e3c0 LLVMLibUnwind.v12.0.1+0.aarch64-apple-darwin.tar.gz/md5/b95ad4844e649bf46db43683b55b9f4f LLVMLibUnwind.v12.0.1+0.aarch64-apple-darwin.tar.gz/sha512/15e0996aebe6db91fe58121001aa7ea4b23685ead3c26b5d89afae34b535e34b4e801a971f4854d8e1a1fbc805cece06272470622eef863e225358113a127913 LLVMLibUnwind.v12.0.1+0.aarch64-linux-gnu.tar.gz/md5/6d8783dc9b86c9884e0877f0d8ac4167 @@ -146,123 +146,123 @@ LLVMLibUnwind.v12.0.1+0.x86_64-unknown-freebsd.tar.gz/md5/54ac594b4c8e7f261034a8 LLVMLibUnwind.v12.0.1+0.x86_64-unknown-freebsd.tar.gz/sha512/a43756afd92081e6dd7244d162862fc318b41ca110a5e8be6e4ee2d8fdfd8fb0f79961ae55e48913e055779791bd1c0ecd34fd59281fb66b3c4f24a1f44128f0 LLVMLibUnwind.v12.0.1+0.x86_64-w64-mingw32.tar.gz/md5/83cf8fc2a085a73b8af4245a82b7d32f LLVMLibUnwind.v12.0.1+0.x86_64-w64-mingw32.tar.gz/sha512/297a5c7b33bd3f57878871eccb3b9879ea5549639523a1b9db356b710cafb232906a74d668315340d60ba0c5087d3400f14ab92c3704e32e062e6b546abf7df6 -libLLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/ab5a9ae6d4f42d18fa71f95b4bc86513 -libLLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/977d4f159fda3d4c18ed920c1935f32cdca743b3fc8527b90b68ed3a6375903f9b614f0bd83b9dc35b40c96ec6e6ca4e4aba6aacb3e3cd051cae5a040fa6ffb4 -libLLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/c90bdd3e26729482f29164d72efefb3a -libLLVM.v14.0.6+2.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/c86257eaa3a9975cb0d61f526ae4852c66cdfde24f43e2aa7d75787dd84a620d2dccd623a540493f688a908db96d4f4ec36699927482b2612cc49dc694ae9656 -libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/4177bb0f67f4351b3e92383011e3d5e1 -libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/6f8a7795f9097e6108777fb999396a44360edbf50c889cb181512594b4ef717f0e3e4e03a92f1481b49077562685d13128ee4542a06a1b655113042c60af834b -libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/43e6b5e9edda8754abfe44a85958f4da -libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/f0fc52ec77f2f7570c00219784c2aa2e9642339c9a00fb8c3ccf0d0545068f8d55c126897ecc1244a22efde4840d2408f6377c8a1c6ad1d9c6ba3e6a5ac63e32 -libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/7545e9392b0f10ad60f36e7db7196a80 -libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/a56058b0d3b2415c1912364bc3f725f44d517d10e0d93462e0f7887f023da20b5f99c1262b176da46cc3acce9d79869775037b2594cff86697463ceacd48e26f -libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/39d57b2e6419fe728cf091580babe118 -libLLVM.v14.0.6+2.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/b149e3d239511fac07b55c9cdd5793529fdd5275b4680d4b3c80a187e4e97991e5285956ef2299332a36a66c8e2d4be67f21927f834e8046a462e0b54002c67a -libLLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/399fc68e348d561d95c986160294cbef -libLLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/7c03de6445393a2881367b9d346ec8e8c6216c0c1b94a3f07594abd68f6a1e4ae40edec8aba984363bbf48fe29679c67174b71b4ae15feb7cfb6cdd8f0e126e9 -libLLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/c3fd98354a09dec28f2c95b98b053c99 -libLLVM.v14.0.6+2.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/9f5571f87bf5ed390d37d6212899e8d2c51fa89a5f1cbb039d2dacbd6f1c7f2789787c5197dc82ede18a1ea868f3649a24449d563ff85333a43720a508af8d07 -libLLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/20198f989a900a79c8d590d9c6938ef2 -libLLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/f14f771acbaa04986e835062172675e5afa3a76d9b1e5c977aa8a1f7cf37d0b51cfed13a0f19a618fd14f540649d42c7d9a06a3bdfa32a7308498963cbf0a5dc -libLLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/ec8469b1ecd45be0f4ec11a51c332c64 -libLLVM.v14.0.6+2.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/52ca038d86a86b91e812b755901276ef5bc9b04cac7537216bb631f6394a46066240c76edef7e3db90d75b24e528634491f523bcd6a3686013fe3406506e8740 -libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/7fc93074bd66c0f8311318a031aeaa6b -libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/9db07db632c227d109f56fdbe503d07cdbc163a3f6b225498f700145782c4c846c2aca47a82cf174ebcef5b230f58419d1084d030932c85e671d3b5f43a5c0bf -libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/a3b134c87c5a8eb24d02caf9a0d11ede -libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/fa4eb2f09f58cd3248018efce795d64f7b8e31a65fb90cb5e53fba5cc175c69e082adbaf7ba87f645508b7a7b7223d49602c8013c1d5beceaf614d66565dabd9 -libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/de80f238ab1969b16b26998d5d7b3f43 -libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/e6a626ea5731abaddf56d28e0be7080b82662471878b6e0c67dff9d10c461ab24ffbdfa1a45e91dd24277ed85d5f55126ab59139b54fcc291deeef2c5dcd72ad -libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/75be85afadc2d42833993521ee415b58 -libLLVM.v14.0.6+2.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/fd0176144abdac37c02d3e1f2c4f27297033fa331f99d26e103b87957107afcf2e6a8db6b8beeae037f528fa5c1eec29c1079c0d62d082e8a613e63223b0f888 -libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/4e20e425d15b5a8be8c1f700471b0b99 -libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/7aa390adfe0cf046173ccf2eb4e077585fec64cb82cae1569543183be276306191b2c4bf42e7eeab536ee7886f94716936568ccac82a5f37a2633d58bcdfb539 -libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/7c81a3e495428aa7ea58e247471f642b -libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/15c13bec336ec071f14640baa2ca4723212ef5a97fdae9a5b90b7a07aa1380eedfa72af27ea13fa694921be45cc799eb06622345e87eedfece2780f8b5677293 -libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/c365fa356c069d232c0fb58dd82ae2e0 -libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/afb435457c0220d3e06c6aa9361060d8fa04e1a3b0e406e1ab9b1f18c60f2e2464d08b5afd2e2251c1e38a0e4ea0470c6af05201708a51ebd55a0a37da3662fd -libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/6d2f02e0fb213a6683e6fd229cb39458 -libLLVM.v14.0.6+2.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/6dd9f7ad806f17182d1e43c1b59fb00a3f230d9747924351c199028db6beb7c1e66fba41d58fd2c24f0aaa8a94ff2e95b8d35486a6e0b5c0f2abb37eedf593fe -libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/c68f787fae3c30b11b0adbc38572b4f3 -libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/0a094431baec6f84b8fef56446ea95c1d7e1be309d6bd71c80e7ff069573d83c1595e88f8a44c9982c87b6ce5d3d97a4def8b17c79e2fa4a1c556c64695e4be7 -libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/4930ed9f72d3aa896a7c22bede0abfa7 -libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/68166a7941bd9c4121b3263ca881123958c9f589b826c0eaea2d06982566898e213aa586af44901d04bdab3c99c2bdc9e6d6d9534ac1ffe9a00eeb9ef311e056 -libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/493d597530b6352f52e06267b96faad2 -libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/bdc3ca526a84b273df908ed7deec3ecea73c66f3991e8b5d0fbf8e29b6376f6f8bc7e789615467ab2d3828d8fb76e61a22cf87fd589fa04c4910ae18944b705b -libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/256d30ff71f384240704b543fce2471c -libLLVM.v14.0.6+2.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/353c553a5991e893a329e565919da707d3dc9ab61297c27f5fae7c74bdd1cadeedeaf3601b27357cab11a6b113bfe66f2d39f31ad328b205662e1a5d07c9c5bd -libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/47bbde3f4962518804468a5d7bbe79b3 -libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/a9e4b806cfd1d45d2e43d899a9e89e74b5c30fa82e9b83274241d919a635a5f30863573b1d509b3c61a67bc53486e5c85068e2d6221aad992ecd673e51dd53b7 -libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/ac0a061b0b0502ecbdcba24727b05c26 -libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/888e440c19296f79470a46859c551e283f1cee953dc197494189e2fd7ce03f5eff07b2dd504fe8d7e0b1d522bac14f518c7803795e84dbfa33966fae965b6f90 -libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/6c163cf961ee1f95b365f1e8462841e4 -libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/e15909f72f481c242b06c7bd9fdf7d6acede33661cd383e4b33a29bbe4c1727f86255ae0c9519967e90d2606cc2446544c00eb6bc072f705fca122910cf63e16 -libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/358d52a901a9d2ec6e97b6cf3ec324a4 -libLLVM.v14.0.6+2.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/35cd3064a55ec7bf7824146bdba1905ac065e1d9e014a836b15af6ad17b23a426bb3274971ae9ce7efd8cec7845af2897feae3db8f586772e1abe9e8bcf5143f -libLLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/8ee57951124a8370b86d6c4af30300ba -libLLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/9af5f771b94092e28bf36f7f8f1d5f7c510815f5af12b16215f39e2f377f206f82f9d37de35a142f89b2092125647073f1d0ede9345122b696752063c881c82a -libLLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/95383b63e38ada6a4c36230a4ca9496c -libLLVM.v14.0.6+2.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/7b7047fa5292a6ceb7d540006cd1f07b00d6e0f5f00c7090178e867f6f62ee0c15d6029c31a3f328f99fe3aaa8a1581f1791a212f79ce42e737f9feeaf58616b -libLLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/4248ff578e33399c33c2a39c2f3d0b05 -libLLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/f856964e9ab29f4b73414041eba95637506d8c01dfd6e97a982d8e5f8845e30487b3492da776a9d35696c14a9e027beb3752e2946de6e9db11070b683ca7e6c0 -libLLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/d77c1501a19329d95545178f63697015 -libLLVM.v14.0.6+2.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/d46d163e5bb7720b2cafab0dc664c882729307d446610753e1d9269a4f524bcec1643fce603415b8c61e11589bbc0cdf4664cb58b433eec646eea6564f78b3f9 -libLLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/f89572a9c10f8913b7c009ed39f41d68 -libLLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/8924341540b7d57389c2b4274a51a1a7c7543783442a3b98add990a87f3c0a97a785a5106df68a5495b322b6eb3af3025526d77fbe04f6f2af57186588bedac0 -libLLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/da07aba109f41f6fa7e8c8d9da6b3e1d -libLLVM.v14.0.6+2.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/82aa09aa9e2f26cd84d962d02cf239e3845a151ea1a85f770b35e25c2706f269aceaee582fb98f4cc143847ae19eb75853182cc3f30e96a064c07ae126de0666 -libLLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/5579bf880fcd0b8c48dab90b839e5b04 -libLLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/3bbf718caf0e734a266b7930e7a23f65c6ee3ef164c9304c93c6b67066e78f5eef7b1cb7181b2043ed2cf58f1008c4d948330372304260b1f488b3c3a0538eb3 -libLLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/dc25070c072e28204fc8eb8d14086301 -libLLVM.v14.0.6+2.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/8eeb20ab8a173bd0e72bed1578270b4b69d1e179aa0a1b980b7ffd9a683e9a52a611828647cccaa817a7aefbcc794ea0c586613ec7f91774a7433e8bf93fe1a2 -libLLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/152a803e4a3da4b9a9f178a08ff3bf32 -libLLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/7088b49f0662e6f8cdaeb241d1ee3ef9779f7e9ae612f396121d9a7c1dcea7a0aef1c7313621055f255f17b16a50624d5ff288c8f8ce33d87bdf432d9263b246 -libLLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/482c643fbd9865a2c8f494a3888579b7 -libLLVM.v14.0.6+2.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/2e07d0169ec7d3f2cce10f3aad7936ee12e74cd8574700d561cd935751948e8a79bdaa40bc631ace2a485190bc450dae27835f6e9bb7c11a642e562c8f34439d -libLLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/a42b7bf02170b1c69767e4081e43b70d -libLLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/3e23ad9db83b341d04b869f67ed9f9ea3c090d3759175a2f76c614580100d1e08e66609324510696146e776c54dd3bb0f0b1a3cb175631cfd94c90e395da59db -libLLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/5463c05d5da79b3f2fe0b176152e97c6 -libLLVM.v14.0.6+2.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/7d0e4ac637e04c6cd13af590c7dc4a2725541c226d7acc18d0d649c5f5689eb587a4235d6b09cf884837fd8a4d1aa71d23c33fdb1c4e61abae2bed931f8afc97 -libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/f92359aa26292bda057b12cc08c78420 -libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/cff0bef9edcf77511b8a16acb40def5e8667cb36c6e6d2f554ebc38389c93f3ed2746e2cbe6e5cd70596daa9bfcd0729392484c7f6056860fdbe1045521fcc67 -libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/e49088c9b21583546e9985e5fdf66905 -libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/6a5d449519e054ae30068417840dccda2fe03f82d95ec822ee6e22bd691c57ecd7950e4483964c5baed520a911b3c68c80f1240679eeec0b2b6f396d72d71466 -libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/ef83ddeb912459a190cccd257622f28f -libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/6aa4eee7b201b954b21c88de7ee85dfb940c8c07c44cf080dcac5269ab68e39276c42064d574cd5a3f6205494406be906883da1d75d8a1d84a8379387d309986 -libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/2cd11ed664ecd61ba35a59941af4d5c7 -libLLVM.v14.0.6+2.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/7364be94ec5dcc312fd1a356f05823715af2346435e1638117d9fd33f725508402a77c93eb820be41d85155dd8ba0e81cc803c74c48ace1ae92dbb826cfaa6df -libLLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/0202223794e21f778f99dcaeece42613 -libLLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/ae5cac8c68c68d7a3acd480341e8d6678ad1ddaea40864e252c46f865d64cdd3f2032f7a765fa7cdd498f1b8a5fa8881a10500d497de50b2703a695814ff5604 -libLLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/5eefefcb73961052d706981f62f4257a -libLLVM.v14.0.6+2.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/129da5989609018a7c6960c1fb86268e35809b062efb25d52276b21e99494272bbc55ceb584c7a761e5557d6fc21788340bd50bebef60d2e4007111e6aaae237 -libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/ccc65aa88718939d370f7a2843c0a7ca -libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/6c7dc9da261ae6d0a1a12ce03bb893492a9677f289df6b1b9e40fa35cfbebb5bc31169fe5d7291f893ee74ed7d86899488ea121b0d8b1403e615f104ab7f569d -libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/f0576c099c77c09c5f27e7d3f2723d47 -libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/3a5a6e39e0f8f253de61c9fa0dee1d01cc10d3a17ed583cc2c263e743be3f83f29c5e5d59a11d64da5768159c990c61996358d26576925a7f9fedc460303b511 -libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/73a29eb63f6d834d59776c4d9138475e -libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/0545ac95a2ac4d2f23a200778a13c64c2a80eacde553f5cc38dc90c5de84b3f9d0dbfcd9e3b16cf38c047e9e154c044e0c798850affdf5f917a28d08d3fc5827 -libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/28eea26109c23a3059cd6e4250cb532b -libLLVM.v14.0.6+2.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/1f42b9a15cdb2e0e0faf33ca134a90e73b61573e951a1efb284623c42111df6b8db9871cb13765cb04290caa05f3c69e80752dbe3df5f94b917d1b424d88f923 -libLLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/188d846f846556d33b6adf48408a41c9 -libLLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/93dfe6af424dd2a2f7ea9e5894c88050d55c6e0b7d8b20ca44793dca36d584a49b3fc4ddb5183881b69e86285b8baa93a6f0cf1e3de54fcb283f6f18a421277c -libLLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/bec621675c0d62e1e5884289e3e84b69 -libLLVM.v14.0.6+2.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/f7b20e8cc0534c59f3b7cb422df545ee963736c9fcc2941ae14294bc5bbf4adbb13ca72622518ada4fb5871b67fb2c313c4532eb17046bce9b9fe8458cac4ce8 -libLLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/f204eb9d4f696cb5e7e85e539d1a2d1a -libLLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/df9697817a96e1b6bb7d69413969990cd2203aead52eaad3f576f57702d3a657e10ffd531a68b0995642f9cb3fa6961c297659351501e3a163e6cf228d4234d2 -libLLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/cba1e24a29a5e490ded6eab85383c6b1 -libLLVM.v14.0.6+2.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/63319d17c6ef3313219eca4f46dc7d879c955a7e4ce5b56896f7f4b230b657718829e3f892433818493457850a2a3573fdde2298b290932bf1d0c34923f99339 -libLLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/6355fcef3bfddb656c5ec91f755ddb0f -libLLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/4a1ddb2230cd62fcff69e17e223f5c8b0a191ebacae1bbf262c159381749522a1efafde0a57663ed659b0e53b6c1a9032a14342b239f95e8ae007a619dfade62 -libLLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/070a6bb4575b60e5a14d959ce34556d1 -libLLVM.v14.0.6+2.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/0a47fd12c936b7cf3a9a97f2127627a44c2f577e2fb5d8bcb2b96e3d2d78a602770966a37c733b1a1bf663e37a15fe1743e0d683111d7b1fdb7dfc4510027827 -libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/605116c3315105515acb70c9b3ecf9f7 -libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/3fb6762a8215f230a63d100feb882efd08e668dc47b5b4cca1c9565b0926b4920f5f671fc5db903d6fc3b6c445b00d2822d179ee999c614ae22ebff7a2d73659 -libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/8921804e0461aeeaf1e6a484c5b392a7 -libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/af9510843a9083e363e67bc1b1fed6eca4d594c34d6a9fb8f920dff5b726dbee376f33dafaf040989e83aaced066d35f3fd90b89f4c3e0e6a1f3a11a471ad8a7 -libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/2bbad5e9373fc2354b9e0878663169a9 -libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/adfa1edc0a4138d977fde832aaa6549b5ee38a1c0bb3b59dd9c05740569bd108c2b2b2de4e81ac06d367c9f834662fa5238972affee8bc638309e4470cd980f1 -libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/605ba226b4d0d82802590eadf31d50ce -libLLVM.v14.0.6+2.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/992dd8cf723b986d506743f2ea345391752893b07fc0be35129afbeb4cd01d41f32c56a99b0f6a25b51787bee9a56c60ce66fce60123e8fd3fe0fa11ba051b3d -llvm-julia-14.0.6-2.tar.gz/md5/1401091c768e6c4aef468bb3fb1fac83 -llvm-julia-14.0.6-2.tar.gz/sha512/42feedbfc5866ed1fde7e15810ba5224d46e61122d5fcbb4e4c4dfe72cb898e429bdfcdf6b0712fceefd8cc5b910857d7babfd73ce65e7f8a43cec42424a7c3d +libLLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/5ea3b06e462084efd39b06e1fbf348f1 +libLLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/4a39d9aab00935c9e8fc0d7c7176ba1c5729437eabd7dcefc20a7f4d85975046bd5a2b55db38bc39a6d095e74fc490d33e825ac4abaf57a2317063e1f0dc3ac3 +libLLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/357e1cea81e8413f00ec5179298d54c1 +libLLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/cf4f14bc57aa41feed2c35658c64d05dc9d91d05c0babdcd78da78453a4220cbbe497acee99ab137784f0bc888d24740583aabfca1ae68a124e6ee036c1455ca +libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/38b0c96663e2241b1b48eba04a7c5380 +libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/e6cb9eddadf4158268219a78f10c41bbc6ae55fb8a7869ac6650e01254cd5e1da1ccb3b63ac6132b5d2625da5f1af186708d0d3438be4e85121d14a0fb94dbcd +libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/00b7d3d04c7398fcfb0118c24c2322d2 +libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/6d16e52be1643e49dcfa1c6fc069c8e021acbe8cc43b769265b2413a1a261dfddcc5128f5168cfb3a363d4cd4766be81c5f285a5a7b77021d5c0dd512ab3c94c +libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/218949adbbee820dd95132b7ffdc4b55 +libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/ca1003466cc54935af1b0960d6d9ad83bec9c4a7814184f2a0c5e5aa062e95f825a40438fe3790c8fe39051f21d494866cba37a2879e684f15175652fa08c710 +libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/4d28237d915059ed2607d6999705604a +libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/829c4ff056b49094abd85c63bc0378845f95467a75fffce6ecb9a6b860cb3fba92f683316ca36cbead7b3abc0ce08a9b1bb87cbb37a38c97661fdfc2efaa4524 +libLLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/1da43b4df0c72775ded9a359ee4caaca +libLLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/4714bada6d77cf0a040c2853d3d0558e1f2979a31c77c51764683753444f0076b412c0c6c58646a4f3c096fd329c0095bcabaa16c8d51f8848ab385734b2a655 +libLLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/950f85ab3600d19a285e78f893f692d1 +libLLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/3e5d69f14bc6380bdb96ab32ee24091275d51f702b2e53c459350792167a4724929149f5dacf075d36f38b4bcf1bdee60db85c59df841b2174c9aee12ba99e17 +libLLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/d438de4f324509bef175a99c466c0309 +libLLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/7168eb8ca536ed40eaee9b9766cc5b3a6f7183a47cdc6e0020a80da266d024427fce8481cd7570128b9c5620a92503bd304f40e34704242f4ea0cb47399d5200 +libLLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/2eacfaab253166b936c12e1df650bd98 +libLLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/17954baa02defb2e5b5cf0e5293bac0c84a6d7a431d6e6a7c85fa0d1cf01d3bf4dd4f4ec25689d8d003606e69642be43136d2136461e6d53a0b0627a58d6cf23 +libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/f2df37b8471de9dbd843fddfc478a1ca +libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/ca26696ef29f63478bf0abf22b9965a30cf3c4f3913fb2ddf1c8fd229e7a1af46479d253966eb72e7b4e40f1fbc767477b420c77b6c3a61f45aafd769717b626 +libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/b870dcfc7c9f4703111b8a9684f03972 +libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/1e5e5252540bbbbd46df19301417936a5e8a8e4d8fb1ba18583650c5503317b915b97131aa0632b3e87d4cd07e2fff97943994bb3a3188bf8a01bd52df99fc40 +libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/ac9797450cfbc5cb3536cb8561fd620d +libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/1ef42d3fdb399605e6c6410753b663ea541b953a0ba0c721589ed8e23cbe4a45f370de41161780fd3bc0615228afe33a1bd3bbf7bdbab82e6ed16f037ad7e780 +libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/abd5fbe08c54275be7025c8a26a46c83 +libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/bb480ed858d737e1439ed64acdede4bceafdefe2156c55d5944264c8ce755b662f3becadacda327cefd7362fe3d77351be214f256e6a0d1893f0293c4e596077 +libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/0c5dda523e96162cd5d5480646d9156a +libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/eddeb472c042f4b4333de4d764fb71f9ef8f742be64856c6d301f3f46c8c2c057750790c76366dfa3c6e2ccaaeee667069993d7156089014d7cc969fe41534a0 +libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/79d9a1a3873c266b05efdde2c9d0b430 +libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/2bc60244ba85cb27f160c32ad28cc99351fe2a3124a4294320509be173af02ea8d246c52705206305a9d0530cbe92d12bfbaa9f667090e9698b03a1f2bcab04b +libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/700d1cc6027488259dea73ba9c727e5b +libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/47e3556d98216ec520b0687fb5b4a320f72e94e3cd640050b2d078a853083a48e3f002e6ffb6df55c9564dc5576e4d4080bbdfd3e052012f7deb989000a38b45 +libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/656928399ba9208225fd0341e1357d2b +libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/ee9f43c21378ab7e821f04abab248c0e48acd0c8323bcebcfef3afae6e9cd1d674f8dbd8a2f136acba76fee02ab0af081ecdce88a3390212e943877367372861 +libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/e44eb8d30b68e02b8ca6803328cb5a6d +libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/443207f8ac1c48819e2c9acbfcbdcadce31407b152fff2a5b80947c62fa2e56c1e7345481121b0b1806ec96c272574be07c402ecd2471b660f8b8b9c499fda7b +libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/67f20454d56fe1ea89f5a9c9eea77e85 +libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/94688ec1cfd9f0e3ad169e7f0ded60ebb432b8f725092a1d80f34f1f3806ac2cad353b9219cd42a0f3705483aa4442830bf68ce62482ff6c418519ff6bfe7825 +libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/30edf59dbd758609fb75251f68667251 +libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/bec840012f60f96614f46e3d297fc1b91c031ac5909298c2f7127eda98306f6655b9e3c08a55121914c932e970ba96e81d871082c333c10595c477591b981afc +libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/bdcae8feb5cccbf074d535c3c8a05cb1 +libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/09cc38ca97051fca769df60a1dc71696d9c4f55d822dfd8e5f70d83a6bd28878b5c0027c8cb97664e4667ca6f9bec8f461e4a719332ff94dd7c5f35043d7e38f +libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/a53bf9ae2af47d1fb0dfa8a693beeb7f +libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/c11656d939570ca49f7697fe0f33c5c5057122ec6293d1bfa45ed792f20cab9affa509d59b03a98a1ebee3d0d3d5941d7bc08ff4ff0ce7401ba3c2f479f650eb +libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/54f7d64625849703b352bc68dac6bd91 +libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/3bfe75c57c50e97c72085b51eb6bb0ec47d1227af5c7025d88fd50ac91b39ff9cb8ba8868a88988fe7c930e5d91e9b488e69ee38300bd51122f135ea8d55f2f7 +libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/9dfda3599c6f4c1548af05e58b061440 +libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/20c6a5d6d2238f89f85b6700dad402321aac9c7377d4c29763a5ac616ba5b3fe42d1f83df7f3e65f7cab90190a1c44e579e045da669dc8f49e873a344c68374f +libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/81e1c7d224066856ce143b23f6e4af49 +libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/883aae9e85b79dd4890bad04b0820fca1d487483c8de44e21b042abbf9bb274ec0f58c3af626c55c51178acfbc332840fb893915913419572608b356fd8bf37b +libLLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/358d9294cc14a4f6b73e1f243598b146 +libLLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/2394b05d4949e8d20eb653385833fcd1f635fce3669b7b5ddb3db4574d001c48c8b9ddf5bad9ac08206ae4341e3ddf78a5a4c9038cc22bda95cfac1e81851830 +libLLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/0fcd35d44392cfbf706b27c6d8567119 +libLLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/e7e84f56340a61c4f9a511df83a84c7ab74c6887ff505cd7824039ee31ba3a3bea88a5b40a83dd38a815f7d1373bd64d6b309b874be79f3fd66c14aef5a60778 +libLLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/812a478a131bb87e7a15639f41c02cda +libLLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/c99129eb688905766ca486d2a0c61e2227e65cfed75f3d64497768921f527b58c3f82759416cf3da0362d57f2b4bdb7b9832fd1a2d9bb2395386c39f0d472cf7 +libLLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/ca1cc07830f34f7a613f16e9e5086840 +libLLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/2e75e3499adf61cea54c42a19573bcb0f2a61b0184735b473aa9f20b5097034df6d1ad82034e0b22b7aa6670271189e7dcad29e885fb62c11b298eeb552b78ff +libLLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/27529d1cddf3c343cc5c77abf4d1c395 +libLLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/74e742157b5bb9d9ba5497851ec1d9b73e46d73d79b1706404a3e0d9e69b262d3a64380cdeeaa7b37aa5fba69a3982ab482257c8ec771afbcc398a3359b19c78 +libLLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/728421c5ed92612cb31050c18a10c8bc +libLLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/91a5f334aa3621cf7e4691c9b2cc45b3f63839944a2b245b33fc96faed57cd21fd1aefe63e0667b25d46787b8039446129ec084f68b2130862cf8fe351822a7b +libLLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/3dc106534a7ac3c02d2429d03d70dbf3 +libLLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/51b65ac973d968e4040a7a61c8b30f03732a22d42b252df0c9d92967b5a86cf93cde821ce481c11c00f44e7479a18b1a56ab5d6c4ff253ef3d7dd3eb9b0e76b3 +libLLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/a3bcb02806f93f1d4e1a5b8bc5d45c04 +libLLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/135ab615b67fc014a08fa6b83b21361b2d532858051ce1f07255b606bc42c522525b1bb14e9700140e79f7655119b91aa4eada22f9680b177e8a0548d634daf8 +libLLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/fff6d4b614004e891b0b526fd3451df1 +libLLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/855f7364e7797326fdcc1c49f08ccd12f855029e1f6684a223d3a16a827c5d78fbf8697a71a8ca083e2660f456da16c243b57f87f0458f72a07df923262079a1 +libLLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/c961b55edb5bfa399ab70e2b8c80ed81 +libLLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/2e65c7d6e836b679ba6039d10cb15cc5b0ac76deb2acf7a099632efe11c78db441b58ddf6f13d2215d576acf3a979b5c347f1ccc287cd6b3e107fba9df5e264d +libLLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/a910109a1423922f175ff33cd55a6373 +libLLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/853c4c0cf4e9c325be74c3dc35791915c08b69b37b2e80fb5db4b6a16afcbd208c08f97399e07e10be50a905b772aa11a59600dc85f42ed91abff114a5fc844c +libLLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/c27f58e90a431f2779be67d49657af70 +libLLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/95d2cf9685e24d99136ffcb0368f3bb017e0a6bc1a863aae23690d1f80df61dc72982f6647876ac266e6e9c73425a639fa2c5f94af2a80de8d20979603cbb489 +libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/56e3e6cbae412582dcb0d4669cd5c0d8 +libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/d65f0364a9799e0df9db546dd558e117049c03e08088c144dded64b3f05f03898696b3ed568ff79b07fb9c00fbd7d6cc9f7fc4987d4d6c5e85b9037c982b3aba +libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/8046cee5793583152dd168b7b766fe11 +libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/a0d0208d383d8161a6dc71da6eeeca19780e3c4d81c85bd9e35b9bd814e75ff674a0ade49f4d42e6a3dd1b5389ecbbb3e70813b3780b3fb7f0c9ab8cf11a5952 +libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/9dfe4eefe41cd5c4089116c00bef38e6 +libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/1ede0e20bfbd468158afec5d7e68e7dc2031c68ced59ae5b2c171c65f10a32863fff74eedb350e0065efd6b88f0fbaa06e5d0458e36e5fbb1bdfdec9cde9a367 +libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/2fe7f17e4a7129318d9374992664f491 +libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/a3d042ab4edb82b47dc6899ac00c08cac4736d5145584f8067255930b3a7f1fc01953921a1ee74d414a2f2833b18fe865e2903eb1c560e7cea7fb1edc0f8e4f6 +libLLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/f4c4bbf9be3fc51d22d24ad5d4e2885c +libLLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/bb77696308e111acd2cabfb8e813ef4243cb60e1099f87f00cc725e66e099b6ac59d5fd21fbb663b9b0b698cc67887a38e4283429f19e965e82c5c6de231342e +libLLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/995758957795ec54a179d95029769409 +libLLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/2e3f59adebc5ad837236d62c64885f72cda38facf73046ccf80ec74f590ed661efabc768d6121a8159d0107c66e02ddabb2323ff02eed6c50decf783cd57e5fd +libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/34e026ee1024bf613b04d264749324cd +libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/177781edf0a99c34fb43ed5d1973fe20bb5da53c31236aa0d6e3e12b73643709234d82f88cd36efe65aec53965d48f001c471b8d66ef292164f6c5a43c58651b +libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/08b3804953d94cee5845a00048951455 +libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/c09b58c74ac6bb9b161e15491c19ba3e59716a2c9b28a825668da51bd787927352dfbcd1db1337570559bb6d4dd12352c843b8f83d8dc95150ab184a270fd304 +libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/1957335c8483c2bf36b9fdbb3d833c91 +libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/092478aad9420da0acbdcd5ef44ad3fa2dceb8c87e7a5f8bd043d711ad9757ee1c5f796079373ae0a1b504f41c2c015c24eb79dbeb93fbb11c2ed364646cd719 +libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/1d013242b0a5178d18c67cdf382a7032 +libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/f5876e35b0a68e2a2761bcf2cae6efd22636f0bc6c841cd35bf3e1217fa2879f16625981cb424831232a91b2f28ed97575097d1b59a682b041c4158d208aeeda +libLLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/b9b62732f42240d3341f130e48472ebe +libLLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/e3bfc2c974c8b8857601af3aadc5bcd0a1dc6fc24ceea82d321d23714ebf12b7bcf34eae666ba6480e31aa9464fbf6ec9aa3905309b7ff23b7b25b848b640173 +libLLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/16b9ce4c2af1c10fb0ecd4cdefb509da +libLLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/a9f11ecb1fe3c477e8187d78ff47cf9cb0bef92da0aab24ef7d25e2afb0f52bde3fce626b4314dafc08f9e3a5611fbe61c3e48b7040cbe84d3a0f3aee8015cad +libLLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/048fad53865cb94f716e8503f94a37c7 +libLLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/39b73107638f8481e5e102afcab6d906ef1478965aa190bb3fe2e876c3d00d08f3fd141ef7cab69a65a77fc618bd00f26803a490a67c27cb82fc3e521487e0cc +libLLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/e67157dadfc9fec9dbc96e4511714f5d +libLLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/33a3e5a78c43ca82698bf78f06d93f59086df684063cfb4e1ed061ba7a133457b36995447ed9fb61d5b3ed268c0b8ae41e0cc8e6a78f74ae4b918e64cec3de2a +libLLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/4fd409177ea89bbb9964abec90b88157 +libLLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/8906304c26e9a5275daefb5d153cd49a8e58a77ec8f4320d1ccc2276a42ad0a063e55f574b489b7f51d8b8f4d324c7a67e4d6cb57f313147af5b605bc0cd7da0 +libLLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/5008243388dc6ba6594500fe09b9545d +libLLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/90e53ba57bc919ee4e9bbb33d1ca7b019a0ccc275b54ef4acbf547b9e9da8b2639f040b06886c8abdedf95042745291eb293a4cb2b6cf8666b21de507faf29cb +libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/fa8a3b8c679a80c1e954746609320b24 +libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/c286926ca6361153f2437ac64d50f13ae2031bc489e3a7bab17209bb381b9c1468b84afe385c51226bf9fd1631217d7cfe736e1dedceecb407289220c4907ba2 +libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/eade937b5aebf503090b4a1fd64aaceb +libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/50404c2dfda3da541ea8ec21eee404cf79f065560b7aa58980e87ff993689aa5e9f031355c7a0edf73297c65ad1734398951573c53b706f814bec70933cb9062 +libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/a04d985f3538fe441757c38d837df80f +libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/86b3d4851748650872d2c0e96cccf1fb1e49d8848cac186754c0360f1f770310046a54964fc9dc323e568c0b316a7fe7a2bc8b517c6ca973f7faf98773ac80cc +libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/1477d88e814d20e1f97dc3497e073b08 +libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/412597249d593598078c57ca4d576e69012c59d013db4c452e9d3927dba7122a1608db87956a44d36aa8563de3683f2e718e81e1debff6ca3ed79c73d0dfb8f8 +llvm-julia-14.0.6-3.tar.gz/md5/6f81ab7585990c5a4be462dcab508c1c +llvm-julia-14.0.6-3.tar.gz/sha512/75f38482042256e1e07ca5d7a949b0722865967df09fb0cb0d8f6bebd8864c08ce62afcf9af680b0cbed922a1abd90407a8bd5f73f421bde747482b0c15c6691 llvmunwind-12.0.1.tar.xz/md5/4ec327cee517fdb1f6a20e83748e2c7b llvmunwind-12.0.1.tar.xz/sha512/847b6ba03010a43f4fdbfdc49bf16d18fd18474d01584712e651b11191814bf7c1cf53475021d9ee447ed78413202b4ed97973d7bdd851d3e49f8d06f55a7af4 diff --git a/deps/clang.version b/deps/clang.version index d10ae4340ce6c..5e08026317a8b 100644 --- a/deps/clang.version +++ b/deps/clang.version @@ -1,4 +1,4 @@ ## jll artifact # Clang (paired with LLVM, only here as a JLL download) CLANG_JLL_NAME := Clang -CLANG_JLL_VER := 14.0.6+2 +CLANG_JLL_VER := 14.0.6+3 diff --git a/deps/llvm-tools.version b/deps/llvm-tools.version index 236c76ca407ab..d809e59ff4c02 100644 --- a/deps/llvm-tools.version +++ b/deps/llvm-tools.version @@ -1,5 +1,5 @@ ## jll artifact # LLVM_tools (downloads LLVM_jll to get things like `lit` and `opt`) LLVM_TOOLS_JLL_NAME := LLVM -LLVM_TOOLS_JLL_VER := 14.0.6+2 -LLVM_TOOLS_ASSERT_JLL_VER := 14.0.6+2 +LLVM_TOOLS_JLL_VER := 14.0.6+3 +LLVM_TOOLS_ASSERT_JLL_VER := 14.0.6+3 diff --git a/deps/llvm.version b/deps/llvm.version index 64ed012bc9989..a962a94a50e05 100644 --- a/deps/llvm.version +++ b/deps/llvm.version @@ -4,5 +4,5 @@ LLVM_ASSERT_JLL_VER := 14.0.5+3 ## source build LLVM_VER := 14.0.5 -LLVM_BRANCH=julia-14.0.6-2 -LLVM_SHA1=julia-14.0.6-2 +LLVM_BRANCH=julia-14.0.6-3 +LLVM_SHA1=julia-14.0.6-3 diff --git a/stdlib/libLLVM_jll/Project.toml b/stdlib/libLLVM_jll/Project.toml index 119eb8755424d..be34e53f94789 100644 --- a/stdlib/libLLVM_jll/Project.toml +++ b/stdlib/libLLVM_jll/Project.toml @@ -1,6 +1,6 @@ name = "libLLVM_jll" uuid = "8f36deef-c2a5-5394-99ed-8e07531fb29a" -version = "14.0.6+2" +version = "14.0.6+3" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" From c9407bd439fb4ee355b0d90c57d1195212751e49 Mon Sep 17 00:00:00 2001 From: Jonas August Date: Mon, 17 Apr 2023 17:10:14 -0400 Subject: [PATCH 534/775] Update style-guide.md (#49216) * Update style-guide.md I think the term "trivial anonymous function" is more memorable than `x -> f(x)`. Co-authored-by: woclass --------- Co-authored-by: woclass --- doc/src/manual/style-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/style-guide.md b/doc/src/manual/style-guide.md index cbe7e9b94eefc..d250fdd811387 100644 --- a/doc/src/manual/style-guide.md +++ b/doc/src/manual/style-guide.md @@ -378,7 +378,7 @@ You generally want to use [`isa`](@ref) and [`<:`](@ref) for testing types, not `==`. Checking types for exact equality typically only makes sense when comparing to a known concrete type (e.g. `T == Float64`), or if you *really, really* know what you're doing. -## Do not write `x->f(x)` +## Don't write a trivial anonymous function `x->f(x)` for a named function `f` Since higher-order functions are often called with anonymous functions, it is easy to conclude that this is desirable or even necessary. But any function can be passed directly, without being From e9983672a2845831a72139f9dfa2c37a1c2acc54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1ll=20Haraldsson?= Date: Mon, 17 Apr 2023 21:38:37 +0000 Subject: [PATCH 535/775] Clarify Cxx.jl. Update calling-c-and-fortran-code.md (#49379) * Update calling-c-and-fortran-code.md Drop mentioning Cxx.jl --- doc/src/manual/calling-c-and-fortran-code.md | 4 +--- doc/src/manual/embedding.md | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index d8ec8110b6d18..eab901adc2043 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -1118,9 +1118,7 @@ For more details on how to pass callbacks to C libraries, see this [blog post](h ## C++ -For direct C++ interfacing, see the [Cxx](https://github.com/Keno/Cxx.jl) package. For tools to create C++ -bindings, see the [CxxWrap](https://github.com/JuliaInterop/CxxWrap.jl) package. - +For tools to create C++ bindings, see the [CxxWrap](https://github.com/JuliaInterop/CxxWrap.jl) package. [^1]: Non-library function calls in both C and Julia can be inlined and thus may have diff --git a/doc/src/manual/embedding.md b/doc/src/manual/embedding.md index 9b8a67bb8c4c2..2b6e48c533849 100644 --- a/doc/src/manual/embedding.md +++ b/doc/src/manual/embedding.md @@ -6,7 +6,8 @@ calling Julia functions from C code. This can be used to integrate Julia code in C/C++ project, without the need to rewrite everything in C/C++. Julia has a C API to make this possible. As almost all programming languages have some way to call C functions, the Julia C API can also be used to build further language bridges (e.g. calling Julia from -Python or C#). +Python, Rust or C#). Even though Rust and C++ can use the C embedding API directly, both +have packages helping with it, for C++ [Jluna](https://github.com/Clemapfel/jluna) is useful. ## High-Level Embedding From e3ad0ddca98cbe3aef18b40c7fb3a3ab0b075a7b Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Mon, 17 Apr 2023 18:33:56 -0400 Subject: [PATCH 536/775] Update checksum for LBT 5.7 (#49396) --- deps/checksums/blastrampoline | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/checksums/blastrampoline b/deps/checksums/blastrampoline index 38cb44236eb87..d4c5879fc9403 100644 --- a/deps/checksums/blastrampoline +++ b/deps/checksums/blastrampoline @@ -1,5 +1,5 @@ -blastrampoline-d00e6ca235bb747faae4c9f3a297016cae6959ed.tar.gz/md5/b49ebb89b7f9a1eaf85217c4a9dac744 -blastrampoline-d00e6ca235bb747faae4c9f3a297016cae6959ed.tar.gz/sha512/ac3a767fdb03cc0a9e12ae6df31229e6c5050f2b7ccaee47ef14d6bef34b37a20c2d79956b73bf74d72af1f01a3d1316931db264e1b00cb6cadd57fb842e6f2f +blastrampoline-2272604bfb10b9e8a3ae5f1a4569899b99251a65.tar.gz/md5/bd7fb047c1b7d4826e24011df7e74afe +blastrampoline-2272604bfb10b9e8a3ae5f1a4569899b99251a65.tar.gz/sha512/1fc7506feaf2bcfaf898b2966843225f7190e5f36274f9cab1daa097ff9c2208ed94a685fc613653bc489d341d6b8c8b158e7a809f4d821e0a76da4308c698a5 libblastrampoline.v5.7.0+0.aarch64-apple-darwin.tar.gz/md5/a28837b9838fef2b3831de3278ec7949 libblastrampoline.v5.7.0+0.aarch64-apple-darwin.tar.gz/sha512/111ac2fe5f8f8102f2f7c9e9e6aa1d1a12d2db941238c949ff8e64b30335e8b2f6ecce0d5f577879c231eb839c06e259302b709f3d34e94a97047bfa984222f6 libblastrampoline.v5.7.0+0.aarch64-linux-gnu.tar.gz/md5/3792c0a4443c0e2ebe88bea180a3beb1 From 7c80f253c18836ab65da5e807bb0800e9867d260 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 18 Apr 2023 01:21:19 +0000 Subject: [PATCH 537/775] irverify: Catch invalid use of Goto{IfNot, Node}, ReturnNode Referencing control flow statements in value position is illegal and will cause crashes in compilation or interpretation. Add this to the verifier, so that downstream tooling that checks for valid IR can catch this before it gets handed off to the compiler. --- base/compiler/ssair/verify.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/base/compiler/ssair/verify.jl b/base/compiler/ssair/verify.jl index 66fde426347bd..c281aa4b7786f 100644 --- a/base/compiler/ssair/verify.jl +++ b/base/compiler/ssair/verify.jl @@ -43,6 +43,12 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, error("") end end + + use_inst = ir[op] + if isa(use_inst[:inst], Union{GotoIfNot, GotoNode, ReturnNode}) + @verify_error "At statement %$use_idx: Invalid use of value statement or terminator %$(op.id)" + error("") + end elseif isa(op, GlobalRef) if !isdefined(op.mod, op.name) || !isconst(op.mod, op.name) @verify_error "Unbound GlobalRef not allowed in value position" From c6ed7d7c54a501090019e2f509935f6f21bdf9af Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 18 Apr 2023 11:24:41 -0400 Subject: [PATCH 538/775] gf: add `max_varargs` field to jl_method_t (#49320) * gf: add `max_varargs` field to jl_method_t This field is currently always configured to use the existing heuristic, which is based on specializing to the max # of args appearing in other methods for the same function. This makes progress on #49172. It leaves for later: 1. Go back and change the places we manually tweak `max_args` to set `max_varargs` on the relevant method(s) instead. 2. Re-visit the original heuristic, to see if it can be better defined without "spooky action at a distance" based on other method defs. * Initialize purity bits * gf: re-factor `get_max_varargs` to separate function * Update src/gf.c * Revert "Update src/gf.c" This reverts commit a12c4f92c60ec3ca912df98864fb42ba708712c7. --------- Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Co-authored-by: Shuhei Kadowaki --- src/gf.c | 66 +++++++++++++++++++++++++++++++++++++++------------ src/jltypes.c | 6 +++-- src/julia.h | 4 +++- src/method.c | 2 ++ 4 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/gf.c b/src/gf.c index 8cb3ff54018a6..704471a29b4ad 100644 --- a/src/gf.c +++ b/src/gf.c @@ -38,6 +38,36 @@ JL_DLLEXPORT size_t jl_get_tls_world_age(void) JL_NOTSAFEPOINT return jl_current_task->world_age; } +// Compute the maximum number of times to unroll Varargs{T}, based on +// m->max_varargs (if specified) or a heuristic based on the maximum +// number of non-varargs arguments in the provided method table. +// +// If provided, `may_increase` is set to 1 if the returned value is +// heuristic-based and has a chance of increasing in the future. +static size_t get_max_varargs( + jl_method_t *m, + jl_methtable_t *kwmt, + jl_methtable_t *mt, + uint8_t *may_increase) JL_NOTSAFEPOINT +{ + size_t max_varargs = 1; + if (may_increase != NULL) + *may_increase = 0; + + if (m->max_varargs != UINT8_MAX) + max_varargs = m->max_varargs; + else if (kwmt != NULL && kwmt != jl_type_type_mt && kwmt != jl_nonfunction_mt && kwmt != jl_kwcall_mt) { + if (may_increase != NULL) + *may_increase = 1; // `max_args` can increase as new methods are inserted + + max_varargs = jl_atomic_load_relaxed(&kwmt->max_args) + 2; + if (mt == jl_kwcall_mt) + max_varargs += 2; + max_varargs -= m->nargs; + } + return max_varargs; +} + /// ----- Handling for Julia callbacks ----- /// JL_DLLEXPORT int8_t jl_is_in_pure_context(void) @@ -727,13 +757,14 @@ static void jl_compilation_sig( jl_tupletype_t *const tt, // the original tupletype of the call (or DataType from precompile) jl_svec_t *sparams, jl_method_t *definition, - intptr_t nspec, + intptr_t max_varargs, // output: jl_svec_t **const newparams JL_REQUIRE_ROOTED_SLOT) { assert(jl_is_tuple_type(tt)); jl_value_t *decl = definition->sig; size_t nargs = definition->nargs; // == jl_nparams(jl_unwrap_unionall(decl)); + size_t nspec = max_varargs + nargs; if (definition->generator) { // staged functions aren't optimized @@ -769,7 +800,8 @@ static void jl_compilation_sig( case JL_VARARG_UNBOUND: if (np < nspec && jl_is_va_tuple(tt)) // there are insufficient given parameters for jl_isa_compileable_sig now to like this type - // (there were probably fewer methods defined when we first selected this signature) + // (there were probably fewer methods defined when we first selected this signature, or + // the max varargs limit was not reached indicating the type is already fully-specialized) return; break; } @@ -922,7 +954,13 @@ static void jl_compilation_sig( // and the types we find should be bigger. if (np >= nspec && jl_va_tuple_kind((jl_datatype_t*)decl) == JL_VARARG_UNBOUND) { if (!*newparams) *newparams = tt->parameters; - type_i = jl_svecref(*newparams, nspec - 2); + if (max_varargs > 0) { + type_i = jl_svecref(*newparams, nspec - 2); + } else { + // If max varargs is zero, always specialize to (Any...) since + // there is no preceding parameter to use for `type_i` + type_i = jl_bottom_type; + } // if all subsequent arguments are subtypes of type_i, specialize // on that instead of decl. for example, if decl is // (Any...) @@ -991,18 +1029,16 @@ JL_DLLEXPORT int jl_isa_compileable_sig( // supertype of any other method signatures. so far we are conservative // and the types we find should be bigger. if (definition->isva) { - unsigned nspec_min = nargs + 1; // min number of non-vararg values before vararg - unsigned nspec_max = INT32_MAX; // max number of non-vararg values before vararg + unsigned nspec_min = nargs + 1; // min number of arg values (including tail vararg) + unsigned nspec_max = INT32_MAX; // max number of arg values (including tail vararg) jl_methtable_t *mt = jl_method_table_for(decl); jl_methtable_t *kwmt = mt == jl_kwcall_mt ? jl_kwmethod_table_for(decl) : mt; if ((jl_value_t*)mt != jl_nothing) { // try to refine estimate of min and max - if (kwmt != NULL && kwmt != jl_type_type_mt && kwmt != jl_nonfunction_mt && kwmt != jl_kwcall_mt) - // new methods may be added, increasing nspec_min later - nspec_min = jl_atomic_load_relaxed(&kwmt->max_args) + 2 + 2 * (mt == jl_kwcall_mt); - else - // nspec is always nargs+1, regardless of the other contents of these mt - nspec_max = nspec_min; + uint8_t heuristic_used = 0; + nspec_max = nspec_min = nargs + get_max_varargs(definition, kwmt, mt, &heuristic_used); + if (heuristic_used) + nspec_max = INT32_MAX; // new methods may be added, increasing nspec_min later } int isunbound = (jl_va_tuple_kind((jl_datatype_t*)decl) == JL_VARARG_UNBOUND); if (jl_is_vararg(jl_tparam(type, np - 1))) { @@ -1227,8 +1263,8 @@ static jl_method_instance_t *cache_method( int cache_with_orig = 1; jl_tupletype_t *compilationsig = tt; jl_methtable_t *kwmt = mt == jl_kwcall_mt ? jl_kwmethod_table_for(definition->sig) : mt; - intptr_t nspec = (kwmt == NULL || kwmt == jl_type_type_mt || kwmt == jl_nonfunction_mt || kwmt == jl_kwcall_mt ? definition->nargs + 1 : jl_atomic_load_relaxed(&kwmt->max_args) + 2 + 2 * (mt == jl_kwcall_mt)); - jl_compilation_sig(tt, sparams, definition, nspec, &newparams); + intptr_t max_varargs = get_max_varargs(definition, kwmt, mt, NULL); + jl_compilation_sig(tt, sparams, definition, max_varargs, &newparams); if (newparams) { temp2 = jl_apply_tuple_type(newparams); // Now there may be a problem: the widened signature is more general @@ -2513,8 +2549,8 @@ JL_DLLEXPORT jl_value_t *jl_normalize_to_compilable_sig(jl_methtable_t *mt, jl_t jl_svec_t *newparams = NULL; JL_GC_PUSH2(&tt, &newparams); jl_methtable_t *kwmt = mt == jl_kwcall_mt ? jl_kwmethod_table_for(m->sig) : mt; - intptr_t nspec = (kwmt == NULL || kwmt == jl_type_type_mt || kwmt == jl_nonfunction_mt || kwmt == jl_kwcall_mt ? m->nargs + 1 : jl_atomic_load_relaxed(&kwmt->max_args) + 2 + 2 * (mt == jl_kwcall_mt)); - jl_compilation_sig(ti, env, m, nspec, &newparams); + intptr_t max_varargs = get_max_varargs(m, kwmt, mt, NULL); + jl_compilation_sig(ti, env, m, max_varargs, &newparams); int is_compileable = ((jl_datatype_t*)ti)->isdispatchtuple; if (newparams) { tt = (jl_datatype_t*)jl_apply_tuple_type(newparams); diff --git a/src/jltypes.c b/src/jltypes.c index 2e3a38d7df3ec..482f21a14c76e 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2805,7 +2805,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_method_type = jl_new_datatype(jl_symbol("Method"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(28, + jl_perm_symsvec(29, "name", "module", "file", @@ -2833,8 +2833,9 @@ void jl_init_types(void) JL_GC_DISABLED "isva", "is_for_opaque_closure", "constprop", + "max_varargs", "purity"), - jl_svec(28, + jl_svec(29, jl_symbol_type, jl_module_type, jl_symbol_type, @@ -2862,6 +2863,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_bool_type, jl_bool_type, jl_uint8_type, + jl_uint8_type, jl_uint8_type), jl_emptysvec, 0, 1, 10); diff --git a/src/julia.h b/src/julia.h index 362a82bb93d42..216628c9aebdc 100644 --- a/src/julia.h +++ b/src/julia.h @@ -344,7 +344,9 @@ typedef struct _jl_method_t { uint8_t isva; uint8_t is_for_opaque_closure; // uint8 settings - uint8_t constprop; // 0x00 = use heuristic; 0x01 = aggressive; 0x02 = none + uint8_t constprop; // 0x00 = use heuristic; 0x01 = aggressive; 0x02 = none + uint8_t max_varargs; // 0xFF = use heuristic; otherwise, max # of args to expand + // varargs when specializing. // Override the conclusions of inter-procedural effect analysis, // forcing the conclusion to always true. diff --git a/src/method.c b/src/method.c index 1c110e94c1160..0e67ef347dbd2 100644 --- a/src/method.c +++ b/src/method.c @@ -810,6 +810,8 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t *module) m->deleted_world = ~(size_t)0; m->is_for_opaque_closure = 0; m->constprop = 0; + m->purity.bits = 0; + m->max_varargs = UINT8_MAX; JL_MUTEX_INIT(&m->writelock); return m; } From dccef3bc29bdfce46c85e3780b1f3330567d5490 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 18 Apr 2023 15:51:12 -0400 Subject: [PATCH 539/775] update Statistics.jl Removes some overly strict `@test_throws MethodError` for calls with `Union{}` or `Any`, in preparation for making those errors more precise. --- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - stdlib/Statistics.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/md5 create mode 100644 deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/sha512 delete mode 100644 deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/md5 delete mode 100644 deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/sha512 diff --git a/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/md5 b/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/md5 new file mode 100644 index 0000000000000..7e7a889eecd29 --- /dev/null +++ b/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/md5 @@ -0,0 +1 @@ +6564297a5f5971231809bf9940f68b98 diff --git a/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/sha512 b/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/sha512 new file mode 100644 index 0000000000000..bbe9b8bed6371 --- /dev/null +++ b/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/sha512 @@ -0,0 +1 @@ +22d14c82a30f3ec7af09028423cc823808abf86918d5707fd1fcf6ca20dea7871589da9b22e462d194e86fcee380f549aeb65f585048f00bf23281786b17e040 diff --git a/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/md5 b/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/md5 deleted file mode 100644 index 0e2d0534cd8c7..0000000000000 --- a/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -62d47cffac86df3c59b3de8dd218aa79 diff --git a/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/sha512 b/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/sha512 deleted file mode 100644 index 95e88c63f1a14..0000000000000 --- a/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -6354b1e84d7df1fe8d7e1444181497cac87d22d10a2a21b9f7fab748c209bd9aba64f2df6489e9441624fcf27140ccffa3f7eabaf2517f4900b2661be0c74ba5 diff --git a/stdlib/Statistics.version b/stdlib/Statistics.version index 22857e138655a..27197b12be54c 100644 --- a/stdlib/Statistics.version +++ b/stdlib/Statistics.version @@ -1,4 +1,4 @@ STATISTICS_BRANCH = master -STATISTICS_SHA1 = e9ac70b760dcf87b77affe6c068548a3325d6e2b +STATISTICS_SHA1 = a3feba2bb63f06b7f40024185e9fa5f6385e2510 STATISTICS_GIT_URL := https://github.com/JuliaStats/Statistics.jl.git STATISTICS_TAR_URL = https://api.github.com/repos/JuliaStats/Statistics.jl/tarball/$1 From 6ec0a1a484b42646554d3b67394f26fd29a7ed88 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 12 Apr 2023 17:20:17 -0400 Subject: [PATCH 540/775] morespecific: add rule for Type{Union{}} Make Type{Union{}} in method definitions always the most specific type (normally these would end up being ambiguous). This ensures we do not invalidate them, nor need to consider ambiguities that might arise from intersections with them. --- NEWS.md | 6 ++++++ base/essentials.jl | 9 ++------- src/subtype.c | 22 ++++++++++++++++++++++ test/specificity.jl | 5 +++++ 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/NEWS.md b/NEWS.md index 33fd3549284d5..931db0ad1081f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,12 @@ Language changes ---------------- * When a task forks a child, the parent task's task-local RNG (random number generator) is no longer affected. The seeding of child based on the parent task also takes a more disciplined approach to collision resistance, using a design based on the SplitMix and DotMix splittable RNG schemes ([#49110]). +* A new morespecific rule for methods resolves ambiguities containing Union{} in favor of + the method defined explicitly to handle the Union{} argument. This makes it possible to + define methods to explicitly handle Union{} without the ambiguities that commonly would + result previously. This also lets the runtime optimize certain method lookups in a way + that significantly improves load and inference times for heavily overloaded methods that + dispatch on Types (such as traits and constructors). Compiler/Runtime improvements ----------------------------- diff --git a/base/essentials.jl b/base/essentials.jl index 829341c482383..1cf3be297edb7 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -310,13 +310,8 @@ See also: [`round`](@ref), [`trunc`](@ref), [`oftype`](@ref), [`reinterpret`](@r """ function convert end -# make convert(::Type{<:Union{}}, x::T) intentionally ambiguous for all T -# so it will never get called or invalidated by loading packages -# with carefully chosen types that won't have any other convert methods defined -convert(T::Type{<:Core.IntrinsicFunction}, x) = throw(MethodError(convert, (T, x))) -convert(T::Type{<:Nothing}, x) = throw(MethodError(convert, (Nothing, x))) -convert(::Type{T}, x::T) where {T<:Core.IntrinsicFunction} = x -convert(::Type{T}, x::T) where {T<:Nothing} = x +# ensure this is never ambiguous, and therefore fast for lookup +convert(T::Type{Union{}}, x) = throw(MethodError(convert, (T, x))) convert(::Type{Type}, x::Type) = x # the ssair optimizer is strongly dependent on this method existing to avoid over-specialization # in the absence of inlining-enabled diff --git a/src/subtype.c b/src/subtype.c index 518c566193b70..2693af3c11819 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -4470,6 +4470,21 @@ static int num_occurs(jl_tvar_t *v, jl_typeenv_t *env) return 0; } +int tuple_cmp_typeofbottom(jl_datatype_t *a, jl_datatype_t *b) +{ + size_t i, la = jl_nparams(a), lb = jl_nparams(b); + for (i = 0; i < la || i < lb; i++) { + jl_value_t *pa = i < la ? jl_tparam(a, i) : NULL; + jl_value_t *pb = i < lb ? jl_tparam(b, i) : NULL; + int xa = pa == (jl_value_t*)jl_typeofbottom_type || pa == (jl_value_t*)jl_typeofbottom_type->super; + int xb = pb == (jl_value_t*)jl_typeofbottom_type || pb == (jl_value_t*)jl_typeofbottom_type->super; + if (xa != xb) + return xa - xb; + } + return 0; +} + + #define HANDLE_UNIONALL_A \ jl_unionall_t *ua = (jl_unionall_t*)a; \ jl_typeenv_t newenv = { ua->var, 0x0, env }; \ @@ -4488,6 +4503,13 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, jl_value_t *a0, jl_v return 0; if (jl_is_tuple_type(a) && jl_is_tuple_type(b)) { + // compare whether a and b have Type{Union{}} included, + // which makes them instantly the most specific, regardless of all else, + // for whichever is left most (the left-to-right behavior here ensures + // we do not need to keep track of conflicts with multiple methods). + int msp = tuple_cmp_typeofbottom((jl_datatype_t*)a, (jl_datatype_t*)b); + if (msp) + return msp > 0; // When one is JL_VARARG_BOUND and the other has fixed length, // allow the argument length to fix the tvar jl_vararg_kind_t akind = jl_va_tuple_kind((jl_datatype_t*)a); diff --git a/test/specificity.jl b/test/specificity.jl index 5808ac71fa54b..9b605444bad42 100644 --- a/test/specificity.jl +++ b/test/specificity.jl @@ -311,3 +311,8 @@ let A = Tuple{Type{SubString{S}},AbstractString} where S<:AbstractString, @test args_morespecific(B, C) @test args_morespecific(A, C) end + +@test args_morespecific(Tuple{Type{Union{}}, Any}, Tuple{Any, Type{Union{}}}) +@test args_morespecific(Tuple{typeof(Union{}), Any}, Tuple{Any, Type{Union{}}}) +@test args_morespecific(Tuple{Type{Union{}}, Type{Union{}}, Any}, Tuple{Type{Union{}}, Any, Type{Union{}}}) +@test args_morespecific(Tuple{Type{Union{}}, Type{Union{}}, Any, Type{Union{}}}, Tuple{Type{Union{}}, Any, Type{Union{}}, Type{Union{}}}) From a08e3276e64355ffc8f12b63fe3bc3781ea17462 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 12 Apr 2023 14:12:55 -0400 Subject: [PATCH 541/775] add typemap filtering option for Union{} Based on the new morespecific rule for Union{} and method definitions of the specific form `f(..., Type{Union{}}, Vararg)`. If a method definition exists with that specific form, the intersection visitor will ignore all intersections that have that as their only result, saving significant effort when working with lookups involving `Type{<:T}` (which usually ended up mostly ambiguous anyways). Fixes: https://github.com/JuliaLang/julia/issues/33780 This pattern turns out to have still to been making package loading slow. We could keep adding methods following the ambiguity pattern https://github.com/JuliaLang/julia/pull/46000 for the couple specific functions that need it (constructor, eltype, IteratorEltype, IteratorSize, and maybe a couple others) so the internals can detect those and optimize functions that have that method pair. But it seems somewhat odd, convoluted, and non-obvious behavior there. Instead, this breaks all ambiguities in which Union{} is present explicitly in favor of the method with Union{}. This means that when computing method matches, as soon as we see one method definition with Union{}, we can record that the method is the only possible match for that slot. This, in essence, permits creating a rule for dispatch that a TypeVar lower bound must be strictly a supertype of Union{}, but this creates it at the function level, instead of expecting the user to add it to every TypeVar they use to define methods. This also lets us improve the error message for these cases (generally they should error to avoid polluting the inference result), since we can be assured this method will be called, and not result in an ambiguous MethodError instead! Reverts the functional change of #46000 --- base/abstractarray.jl | 5 +- base/array.jl | 3 + base/arrayshow.jl | 2 + base/boot.jl | 20 +-- base/broadcast.jl | 6 +- base/complex.jl | 1 + base/essentials.jl | 3 +- base/float.jl | 1 + base/generator.jl | 8 +- base/indices.jl | 2 +- base/io.jl | 2 + base/iterators.jl | 2 + base/missing.jl | 2 +- base/number.jl | 4 + base/operators.jl | 1 + base/parse.jl | 2 + base/promotion.jl | 6 + base/some.jl | 1 + base/traits.jl | 4 +- src/gf.c | 19 ++- src/jltypes.c | 2 +- src/julia_internal.h | 2 + src/subtype.c | 27 +++- src/typemap.c | 319 ++++++++++++++++++++++++++++-------------- test/abstractarray.jl | 3 - test/ambiguous.jl | 6 +- test/core.jl | 6 +- test/missing.jl | 4 +- test/some.jl | 2 +- 29 files changed, 317 insertions(+), 148 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 7be3f39d16def..cb3956eb7c6d4 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -183,11 +183,13 @@ CartesianIndex{2} For arrays, this function requires at least Julia 1.2. """ keytype(a::AbstractArray) = keytype(typeof(a)) +keytype(::Type{Union{}}, slurp...) = eltype(Union{}) keytype(A::Type{<:AbstractArray}) = CartesianIndex{ndims(A)} keytype(A::Type{<:AbstractVector}) = Int valtype(a::AbstractArray) = valtype(typeof(a)) +valtype(::Type{Union{}}, slurp...) = eltype(Union{}) """ valtype(T::Type{<:AbstractArray}) @@ -232,7 +234,7 @@ UInt8 ``` """ eltype(::Type) = Any -eltype(::Type{Bottom}) = throw(ArgumentError("Union{} does not have elements")) +eltype(::Type{Bottom}, slurp...) = throw(ArgumentError("Union{} does not have elements")) eltype(x) = eltype(typeof(x)) eltype(::Type{<:AbstractArray{E}}) where {E} = @isdefined(E) ? E : Any @@ -268,6 +270,7 @@ julia> ndims(A) """ ndims(::AbstractArray{T,N}) where {T,N} = N ndims(::Type{<:AbstractArray{<:Any,N}}) where {N} = N +ndims(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements")) """ length(collection) -> Integer diff --git a/base/array.jl b/base/array.jl index 60b1304f20ee0..0a5451bac5b74 100644 --- a/base/array.jl +++ b/base/array.jl @@ -252,7 +252,10 @@ function bitsunionsize(u::Union) return sz end +# Deprecate this, as it seems to have no documented meaning and is unused here, +# but is frequently accessed in packages elsize(@nospecialize _::Type{A}) where {T,A<:Array{T}} = aligned_sizeof(T) +elsize(::Type{Union{}}, slurp...) = 0 sizeof(a::Array) = Core.sizeof(a) function isassigned(a::Array, i::Int...) diff --git a/base/arrayshow.jl b/base/arrayshow.jl index af65df3c97b9d..e600e6281bd15 100644 --- a/base/arrayshow.jl +++ b/base/arrayshow.jl @@ -540,10 +540,12 @@ end # returning Any, as this would cause incorrect printing in e.g. `Vector[Any[1]]`, # because eltype(Vector) == Any so `Any` wouldn't be printed in `Any[1]`) typeinfo_eltype(typeinfo) = nothing # element type not precisely known +typeinfo_eltype(typeinfo::Type{Union{}}, slurp...) = nothing 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) + # types that can be parsed back accurately from their un-decorated representations function typeinfo_implicit(@nospecialize(T)) if T === Float64 || T === Int || T === Char || T === String || T === Symbol || diff --git a/base/boot.jl b/base/boot.jl index ca6e6c81405e2..3a8abde4bce14 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -258,9 +258,17 @@ UnionAll(v::TypeVar, @nospecialize(t)) = ccall(:jl_type_unionall, Any, (Any, Any const Vararg = ccall(:jl_toplevel_eval_in, Any, (Any, Any), Core, _expr(:new, TypeofVararg)) -# let the compiler assume that calling Union{} as a constructor does not need -# to be considered ever (which comes up often as Type{<:T}) -Union{}(a...) = throw(MethodError(Union{}, a)) +# dispatch token indicating a kwarg (keyword sorter) call +function kwcall end +# deprecated internal functions: +kwfunc(@nospecialize(f)) = kwcall +kwftype(@nospecialize(t)) = typeof(kwcall) + +# Let the compiler assume that calling Union{} as a constructor does not need +# to be considered ever (which comes up often as Type{<:T} inference, and +# occasionally in user code from eltype). +Union{}(a...) = throw(ArgumentError("cannot construct a value of type Union{} for return result")) +kwcall(kwargs, ::Type{Union{}}, a...) = Union{}(a...) Expr(@nospecialize args...) = _expr(args...) @@ -369,12 +377,6 @@ include(m::Module, fname::String) = ccall(:jl_load_, Any, (Any, Any), m, fname) eval(m::Module, @nospecialize(e)) = ccall(:jl_toplevel_eval_in, Any, (Any, Any), m, e) -# dispatch token indicating a kwarg (keyword sorter) call -function kwcall end -# deprecated internal functions: -kwfunc(@nospecialize(f)) = kwcall -kwftype(@nospecialize(t)) = typeof(kwcall) - mutable struct Box contents::Any Box(@nospecialize(x)) = new(x) diff --git a/base/broadcast.jl b/base/broadcast.jl index d86b5cd92e02f..94413ae05c87a 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -34,6 +34,9 @@ that you may be able to leverage; see the """ abstract type BroadcastStyle end +struct Unknown <: BroadcastStyle end +BroadcastStyle(::Type{Union{}}, slurp...) = Unknown() # ambiguity resolution + """ `Broadcast.Style{C}()` defines a [`BroadcastStyle`](@ref) signaling through the type parameter `C`. You can use this as an alternative to creating custom subtypes of `BroadcastStyle`, @@ -45,9 +48,6 @@ struct Style{T} <: BroadcastStyle end BroadcastStyle(::Type{<:Tuple}) = Style{Tuple}() -struct Unknown <: BroadcastStyle end -BroadcastStyle(::Type{Union{}}) = Unknown() # ambiguity resolution - """ `Broadcast.AbstractArrayStyle{N} <: BroadcastStyle` is the abstract supertype for any style associated with an `AbstractArray` type. diff --git a/base/complex.jl b/base/complex.jl index 4ce43687aa932..a0473c90d5c17 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -120,6 +120,7 @@ Float64 real(T::Type) = typeof(real(zero(T))) real(::Type{T}) where {T<:Real} = T real(C::Type{<:Complex}) = fieldtype(C, 1) +real(::Type{Union{}}, slurp...) = Union{}(im) """ isreal(x) -> Bool diff --git a/base/essentials.jl b/base/essentials.jl index 1cf3be297edb7..e2035601f4fb5 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -311,7 +311,7 @@ See also: [`round`](@ref), [`trunc`](@ref), [`oftype`](@ref), [`reinterpret`](@r function convert end # ensure this is never ambiguous, and therefore fast for lookup -convert(T::Type{Union{}}, x) = throw(MethodError(convert, (T, x))) +convert(T::Type{Union{}}, x...) = throw(ArgumentError("cannot convert a value to Union{} for assignment")) convert(::Type{Type}, x::Type) = x # the ssair optimizer is strongly dependent on this method existing to avoid over-specialization # in the absence of inlining-enabled @@ -535,6 +535,7 @@ Neither `convert` nor `cconvert` should take a Julia object and turn it into a ` function cconvert end cconvert(T::Type, x) = x isa T ? x : convert(T, x) # do the conversion eagerly in most cases +cconvert(::Type{Union{}}, x...) = convert(Union{}, x...) cconvert(::Type{<:Ptr}, x) = x # but defer the conversion to Ptr to unsafe_convert unsafe_convert(::Type{T}, x::T) where {T} = x # unsafe_convert (like convert) defaults to assuming the convert occurred unsafe_convert(::Type{T}, x::T) where {T<:Ptr} = x # to resolve ambiguity with the next method diff --git a/base/float.jl b/base/float.jl index 4190bfa18bb2b..fad7146655ade 100644 --- a/base/float.jl +++ b/base/float.jl @@ -310,6 +310,7 @@ Float64 """ float(::Type{T}) where {T<:Number} = typeof(float(zero(T))) float(::Type{T}) where {T<:AbstractFloat} = T +float(::Type{Union{}}, slurp...) = Union{}(0.0) """ unsafe_trunc(T, x) diff --git a/base/generator.jl b/base/generator.jl index d11742fe5b72f..aa4b7f67cba95 100644 --- a/base/generator.jl +++ b/base/generator.jl @@ -92,13 +92,13 @@ Base.HasLength() """ IteratorSize(x) = IteratorSize(typeof(x)) IteratorSize(::Type) = HasLength() # HasLength is the default +IteratorSize(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements")) +IteratorSize(::Type{Any}) = SizeUnknown() IteratorSize(::Type{<:Tuple}) = HasLength() IteratorSize(::Type{<:AbstractArray{<:Any,N}}) where {N} = HasShape{N}() IteratorSize(::Type{Generator{I,F}}) where {I,F} = IteratorSize(I) -IteratorSize(::Type{Any}) = SizeUnknown() - haslength(iter) = IteratorSize(iter) isa Union{HasShape, HasLength} abstract type IteratorEltype end @@ -126,7 +126,7 @@ Base.HasEltype() """ IteratorEltype(x) = IteratorEltype(typeof(x)) IteratorEltype(::Type) = HasEltype() # HasEltype is the default +IteratorEltype(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements")) +IteratorEltype(::Type{Any}) = EltypeUnknown() IteratorEltype(::Type{Generator{I,T}}) where {I,T} = EltypeUnknown() - -IteratorEltype(::Type{Any}) = EltypeUnknown() diff --git a/base/indices.jl b/base/indices.jl index 6a28cf63316e6..a9189865048cd 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -92,7 +92,7 @@ particular, [`eachindex`](@ref) creates an iterator whose type depends on the setting of this trait. """ IndexStyle(A::AbstractArray) = IndexStyle(typeof(A)) -IndexStyle(::Type{Union{}}) = IndexLinear() +IndexStyle(::Type{Union{}}, slurp...) = IndexLinear() IndexStyle(::Type{<:AbstractArray}) = IndexCartesian() IndexStyle(::Type{<:Array}) = IndexLinear() IndexStyle(::Type{<:AbstractRange}) = IndexLinear() diff --git a/base/io.jl b/base/io.jl index 3bcae2e8d7836..9c00c57576bac 100644 --- a/base/io.jl +++ b/base/io.jl @@ -219,6 +219,8 @@ julia> read(io, String) ``` """ read(stream, t) +read(stream, ::Type{Union{}}, slurp...; kwargs...) = error("cannot read a value of type Union{}") + """ write(io::IO, x) diff --git a/base/iterators.jl b/base/iterators.jl index a4d12517aabcc..11e94d3384de8 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -1170,6 +1170,7 @@ IteratorEltype(::Type{Flatten{Tuple{}}}) = IteratorEltype(Tuple{}) _flatteneltype(I, ::HasEltype) = IteratorEltype(eltype(I)) _flatteneltype(I, et) = EltypeUnknown() +flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{Union{}}, slurp...) = HasLength() # length==0 flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:NTuple{N,Any}}) where {N} = HasLength() flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Tuple}) = SizeUnknown() flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Number}) = HasLength() @@ -1181,6 +1182,7 @@ _flatten_iteratorsize(sz, ::HasEltype, ::Type{Tuple{}}) = HasLength() IteratorSize(::Type{Flatten{I}}) where {I} = _flatten_iteratorsize(IteratorSize(I), IteratorEltype(I), I) +flatten_length(f, T::Type{Union{}}, slurp...) = 0 function flatten_length(f, T::Type{<:NTuple{N,Any}}) where {N} return N * length(f.it) end diff --git a/base/missing.jl b/base/missing.jl index e1988064aadc1..4544c2b38c460 100644 --- a/base/missing.jl +++ b/base/missing.jl @@ -41,6 +41,7 @@ nonmissingtype(::Type{T}) where {T} = typesplit(T, Missing) function nonmissingtype_checked(T::Type) R = nonmissingtype(T) R >: T && error("could not compute non-missing type") + R <: Union{} && error("cannot convert a value to missing for assignment") return R end @@ -69,7 +70,6 @@ convert(::Type{T}, x::T) where {T>:Union{Missing, Nothing}} = x convert(::Type{T}, x) where {T>:Missing} = convert(nonmissingtype_checked(T), x) convert(::Type{T}, x) where {T>:Union{Missing, Nothing}} = convert(nonmissingtype_checked(nonnothingtype_checked(T)), x) - # Comparison operators ==(::Missing, ::Missing) = missing ==(::Missing, ::Any) = missing diff --git a/base/number.jl b/base/number.jl index 31aa616b0eb55..923fc907d4038 100644 --- a/base/number.jl +++ b/base/number.jl @@ -307,6 +307,7 @@ julia> zero(rand(2,2)) """ zero(x::Number) = oftype(x,0) zero(::Type{T}) where {T<:Number} = convert(T,0) +zero(::Type{Union{}}, slurp...) = Union{}(0) """ one(x) @@ -345,6 +346,7 @@ julia> import Dates; one(Dates.Day(1)) """ one(::Type{T}) where {T<:Number} = convert(T,1) one(x::T) where {T<:Number} = one(T) +one(::Type{Union{}}, slurp...) = Union{}(1) # note that convert(T, 1) should throw an error if T is dimensionful, # so this fallback definition should be okay. @@ -368,6 +370,7 @@ julia> import Dates; oneunit(Dates.Day) """ oneunit(x::T) where {T} = T(one(x)) oneunit(::Type{T}) where {T} = T(one(T)) +oneunit(::Type{Union{}}, slurp...) = Union{}(1) """ big(T::Type) @@ -388,3 +391,4 @@ Complex{BigInt} ``` """ big(::Type{T}) where {T<:Number} = typeof(big(zero(T))) +big(::Type{Union{}}, slurp...) = Union{}(0) diff --git a/base/operators.jl b/base/operators.jl index 3b34e549ea849..5893c5944a3a0 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -888,6 +888,7 @@ julia> widen(1.5f0) """ widen(x::T) where {T} = convert(widen(T), x) widen(x::Type{T}) where {T} = throw(MethodError(widen, (T,))) +widen(x::Type{Union{}}, slurp...) = throw(MethodError(widen, (Union{},))) # function pipelining diff --git a/base/parse.jl b/base/parse.jl index 6e616004a47af..d800e54258b0d 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -36,6 +36,7 @@ julia> parse(Complex{Float64}, "3.2e-1 + 4.5im") ``` """ parse(T::Type, str; base = Int) +parse(::Type{Union{}}, slurp...; kwargs...) = error("cannot parse a value as Union{}") function parse(::Type{T}, c::AbstractChar; base::Integer = 10) where T<:Integer a::Int = (base <= 36 ? 10 : 36) @@ -251,6 +252,7 @@ function parse(::Type{T}, s::AbstractString; base::Union{Nothing,Integer} = noth convert(T, tryparse_internal(T, s, firstindex(s), lastindex(s), base===nothing ? 0 : check_valid_base(base), true)) end +tryparse(::Type{Union{}}, slurp...; kwargs...) = error("cannot parse a value as Union{}") ## string to float functions ## diff --git a/base/promotion.jl b/base/promotion.jl index 31f507d021e78..6e32bd7a42efa 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -323,6 +323,12 @@ it for new types as appropriate. function promote_rule end promote_rule(::Type, ::Type) = Bottom +# Define some methods to avoid needing to enumerate unrelated possibilities when presented +# with Type{<:T}, and return a value in general accordance with the result given by promote_type +promote_rule(::Type{Bottom}, slurp...) = Bottom +promote_rule(::Type{Bottom}, ::Type{Bottom}, slurp...) = Bottom # not strictly necessary, since the next method would match unambiguously anyways +promote_rule(::Type{Bottom}, ::Type{T}, slurp...) where {T} = T +promote_rule(::Type{T}, ::Type{Bottom}, slurp...) where {T} = T promote_result(::Type,::Type,::Type{T},::Type{S}) where {T,S} = (@inline; promote_type(T,S)) # If no promote_rule is defined, both directions give Bottom. In that diff --git a/base/some.jl b/base/some.jl index 08cb3c1648ba1..0d538cbed6c23 100644 --- a/base/some.jl +++ b/base/some.jl @@ -29,6 +29,7 @@ end function nonnothingtype_checked(T::Type) R = nonnothingtype(T) R >: T && error("could not compute non-nothing type") + R <: Union{} && error("cannot convert a value to nothing for assignment") return R end diff --git a/base/traits.jl b/base/traits.jl index 53ae14b12c61e..47ab8ddc0c7ac 100644 --- a/base/traits.jl +++ b/base/traits.jl @@ -11,7 +11,7 @@ OrderStyle(::Type{<:Real}) = Ordered() OrderStyle(::Type{<:AbstractString}) = Ordered() OrderStyle(::Type{Symbol}) = Ordered() OrderStyle(::Type{<:Any}) = Unordered() -OrderStyle(::Type{Union{}}) = Ordered() +OrderStyle(::Type{Union{}}, slurp...) = Ordered() # trait for objects that support arithmetic abstract type ArithmeticStyle end @@ -23,6 +23,7 @@ ArithmeticStyle(instance) = ArithmeticStyle(typeof(instance)) ArithmeticStyle(::Type{<:AbstractFloat}) = ArithmeticRounds() ArithmeticStyle(::Type{<:Integer}) = ArithmeticWraps() ArithmeticStyle(::Type{<:Any}) = ArithmeticUnknown() +ArithmeticStyle(::Type{Union{}}, slurp...) = ArithmeticUnknown() # trait for objects that support ranges with regular step """ @@ -58,5 +59,6 @@ ranges with an element type which is a subtype of `Integer`. abstract type RangeStepStyle end struct RangeStepRegular <: RangeStepStyle end # range with regular step struct RangeStepIrregular <: RangeStepStyle end # range with rounding error +RangeStepStyle(::Type{Union{}}, slurp...) = RangeStepIrregular() RangeStepStyle(instance) = RangeStepStyle(typeof(instance)) diff --git a/src/gf.c b/src/gf.c index 704471a29b4ad..74457283b2873 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1487,6 +1487,8 @@ static int get_intersect_visitor(jl_typemap_entry_t *oldentry, struct typemap_in // skip if no world has both active // also be careful not to try to scan something from the current dump-reload though return 1; + // don't need to consider other similar methods if this oldentry will always fully intersect with them and dominates all of them + typemap_slurp_search(oldentry, &closure->match); jl_method_t *oldmethod = oldentry->func.method; if (closure->match.issubty // e.g. jl_subtype(closure->newentry.sig, oldentry->sig) && jl_subtype(oldmethod->sig, (jl_value_t*)closure->newentry->sig)) { // e.g. jl_type_equal(closure->newentry->sig, oldentry->sig) @@ -1511,7 +1513,7 @@ static jl_value_t *get_intersect_matches(jl_typemap_t *defs, jl_typemap_entry_t else va = NULL; } - struct matches_env env = {{get_intersect_visitor, (jl_value_t*)type, va, + struct matches_env env = {{get_intersect_visitor, (jl_value_t*)type, va, /* .search_slurp = */ 0, /* .ti = */ NULL, /* .env = */ jl_emptysvec, /* .issubty = */ 0}, /* .newentry = */ newentry, /* .shadowed */ NULL, /* .replaced */ NULL}; JL_GC_PUSH3(&env.match.env, &env.match.ti, &env.shadowed); @@ -3189,6 +3191,7 @@ struct ml_matches_env { int intersections; size_t world; int lim; + int include_ambiguous; // results: jl_value_t *t; // array of method matches size_t min_valid; @@ -3244,6 +3247,9 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio return 0; closure->lim--; } + // don't need to consider other similar methods if this ml will always fully intersect with them and dominates all of them + if (!closure->include_ambiguous || closure->lim != -1) + typemap_slurp_search(ml, &closure->match); closure->matc = make_method_match((jl_tupletype_t*)closure->match.ti, closure->match.env, meth, closure->match.issubty ? FULLY_COVERS : NOT_FULLY_COVERS); @@ -3258,9 +3264,10 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio return 1; } -static int ml_mtable_visitor(jl_methtable_t *mt, void *env) +static int ml_mtable_visitor(jl_methtable_t *mt, void *closure0) { - return jl_typemap_intersection_visitor(jl_atomic_load_relaxed(&mt->defs), jl_cachearg_offset(mt), (struct typemap_intersection_env*)env); + struct typemap_intersection_env* env = (struct typemap_intersection_env*)closure0; + return jl_typemap_intersection_visitor(jl_atomic_load_relaxed(&mt->defs), jl_cachearg_offset(mt), env); } // This is the collect form of calling jl_typemap_intersection_visitor @@ -3292,9 +3299,9 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, else va = NULL; } - struct ml_matches_env env = {{ml_matches_visitor, (jl_value_t*)type, va, + struct ml_matches_env env = {{ml_matches_visitor, (jl_value_t*)type, va, /* .search_slurp = */ 0, /* .ti = */ NULL, /* .env = */ jl_emptysvec, /* .issubty = */ 0}, - intersections, world, lim, /* .t = */ jl_an_empty_vec_any, + intersections, world, lim, include_ambiguous, /* .t = */ jl_an_empty_vec_any, /* .min_valid = */ *min_valid, /* .max_valid = */ *max_valid, /* .matc = */ NULL}; struct jl_typemap_assoc search = {(jl_value_t*)type, world, jl_emptysvec, 1, ~(size_t)0}; jl_value_t *isect2 = NULL; @@ -3358,7 +3365,7 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, return env.t; } } - if (!jl_typemap_intersection_visitor(jl_atomic_load_relaxed(&mt->defs), jl_cachearg_offset(mt), &env.match)) { + if (!ml_mtable_visitor(mt, &env.match)) { JL_GC_POP(); return jl_nothing; } diff --git a/src/jltypes.c b/src/jltypes.c index 482f21a14c76e..1837b21742e28 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1601,7 +1601,7 @@ static void check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, si JL_GC_POP(); } -static jl_value_t *extract_wrapper(jl_value_t *t JL_PROPAGATES_ROOT) JL_GLOBALLY_ROOTED +jl_value_t *extract_wrapper(jl_value_t *t JL_PROPAGATES_ROOT) JL_GLOBALLY_ROOTED { t = jl_unwrap_unionall(t); if (jl_is_datatype(t)) diff --git a/src/julia_internal.h b/src/julia_internal.h index 61c8a40f7eeb3..0674806d35a5b 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1462,12 +1462,14 @@ struct typemap_intersection_env { jl_typemap_intersection_visitor_fptr const fptr; // fptr to call on a match jl_value_t *const type; // type to match jl_value_t *const va; // the tparam0 for the vararg in type, if applicable (or NULL) + size_t search_slurp; // output values jl_value_t *ti; // intersection type jl_svec_t *env; // intersection env (initialize to null to perform intersection without an environment) int issubty; // if `a <: b` is true in `intersect(a,b)` }; int jl_typemap_intersection_visitor(jl_typemap_t *a, int offs, struct typemap_intersection_env *closure); +void typemap_slurp_search(jl_typemap_entry_t *ml, struct typemap_intersection_env *closure); // -- simplevector.c -- // diff --git a/src/subtype.c b/src/subtype.c index 2693af3c11819..5b05e8197a420 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2265,20 +2265,34 @@ int jl_has_intersect_type_not_kind(jl_value_t *t) t = jl_unwrap_unionall(t); if (t == (jl_value_t*)jl_any_type) return 1; - if (jl_is_uniontype(t)) { + assert(!jl_is_vararg(t)); + if (jl_is_uniontype(t)) return jl_has_intersect_type_not_kind(((jl_uniontype_t*)t)->a) || jl_has_intersect_type_not_kind(((jl_uniontype_t*)t)->b); - } - if (jl_is_typevar(t)) { + if (jl_is_typevar(t)) return jl_has_intersect_type_not_kind(((jl_tvar_t*)t)->ub); - } - if (jl_is_datatype(t)) { + if (jl_is_datatype(t)) if (((jl_datatype_t*)t)->name == jl_type_typename) return 1; - } return 0; } +// compute if DataType<:t || Union<:t || UnionAll<:t etc. +int jl_has_intersect_kind_not_type(jl_value_t *t) +{ + t = jl_unwrap_unionall(t); + if (t == (jl_value_t*)jl_any_type || jl_is_kind(t)) + return 1; + assert(!jl_is_vararg(t)); + if (jl_is_uniontype(t)) + return jl_has_intersect_kind_not_type(((jl_uniontype_t*)t)->a) || + jl_has_intersect_kind_not_type(((jl_uniontype_t*)t)->b); + if (jl_is_typevar(t)) + return jl_has_intersect_kind_not_type(((jl_tvar_t*)t)->ub); + return 0; +} + + JL_DLLEXPORT int jl_isa(jl_value_t *x, jl_value_t *t) { if (jl_typeis(x,t) || t == (jl_value_t*)jl_any_type) @@ -4476,6 +4490,7 @@ int tuple_cmp_typeofbottom(jl_datatype_t *a, jl_datatype_t *b) for (i = 0; i < la || i < lb; i++) { jl_value_t *pa = i < la ? jl_tparam(a, i) : NULL; jl_value_t *pb = i < lb ? jl_tparam(b, i) : NULL; + assert(jl_typeofbottom_type); // for clang-sa int xa = pa == (jl_value_t*)jl_typeofbottom_type || pa == (jl_value_t*)jl_typeofbottom_type->super; int xb = pb == (jl_value_t*)jl_typeofbottom_type || pb == (jl_value_t*)jl_typeofbottom_type->super; if (xa != xb) diff --git a/src/typemap.c b/src/typemap.c index bd9fd00f06815..3351725788601 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -33,6 +33,9 @@ static jl_value_t *jl_type_extract_name(jl_value_t *t1 JL_PROPAGATES_ROOT) else if (jl_is_typevar(t1)) { return jl_type_extract_name(((jl_tvar_t*)t1)->ub); } + else if (t1 == jl_bottom_type || t1 == (jl_value_t*)jl_typeofbottom_type || t1 == (jl_value_t*)jl_typeofbottom_type->super) { + return (jl_value_t*)jl_typeofbottom_type->name; // put Union{} and typeof(Union{}) and Type{Union{}} together for convenience + } else if (jl_is_datatype(t1)) { jl_datatype_t *dt = (jl_datatype_t*)t1; if (!jl_is_kind(t1)) @@ -63,6 +66,9 @@ static int jl_type_extract_name_precise(jl_value_t *t1, int invariant) else if (jl_is_typevar(t1)) { return jl_type_extract_name_precise(((jl_tvar_t*)t1)->ub, 0); } + else if (t1 == jl_bottom_type || t1 == (jl_value_t*)jl_typeofbottom_type || t1 == (jl_value_t*)jl_typeofbottom_type->super) { + return 1; + } else if (jl_is_datatype(t1)) { jl_datatype_t *dt = (jl_datatype_t*)t1; if ((invariant || !dt->name->abstract) && !jl_is_kind(t1)) @@ -84,6 +90,18 @@ static int jl_type_extract_name_precise(jl_value_t *t1, int invariant) return 1; } +// return whether Type{Union{}} is a subtype of Type{t1} (which may have free typevars) +static int jl_parameter_includes_bottom(jl_value_t *t1) +{ + if (jl_is_typevar(t1) || t1 == jl_bottom_type) + return 1; + else if (jl_is_uniontype(t1)) { + jl_uniontype_t *u1 = (jl_uniontype_t*)t1; + return jl_parameter_includes_bottom(u1->a) && jl_parameter_includes_bottom(u1->b); + } + return 0; +} + // ----- Type Signature Subtype Testing ----- // @@ -378,8 +396,10 @@ static unsigned jl_supertype_height(jl_datatype_t *dt) } // return true if a and b might intersect in the type domain (over just their type-names) -static int tname_intersection(jl_datatype_t *a, jl_typename_t *bname, unsigned ha) +static int tname_intersection_dt(jl_datatype_t *a, jl_typename_t *bname, unsigned ha) { + if (a == jl_any_type) + return 1; jl_datatype_t *b = (jl_datatype_t*)jl_unwrap_unionall(bname->wrapper); unsigned hb = 1; while (b != jl_any_type) { @@ -395,8 +415,42 @@ static int tname_intersection(jl_datatype_t *a, jl_typename_t *bname, unsigned h return a->name == bname; } -// tparam bit 1 is ::Type{T} (vs. T) -// tparam bit 2 is typename(T) (vs. T) +static int tname_intersection(jl_value_t *a, jl_typename_t *bname, int8_t tparam) +{ + if (a == (jl_value_t*)jl_any_type) + return 1; + a = jl_unwrap_unionall(a); + assert(!jl_is_vararg(a)); + if (jl_is_uniontype(a)) + return tname_intersection(((jl_uniontype_t*)a)->a, bname, tparam) || + tname_intersection(((jl_uniontype_t*)a)->b, bname, tparam); + if (jl_is_typevar(a)) + return tname_intersection(((jl_tvar_t*)a)->ub, bname, tparam); + if (jl_is_datatype(a)) { + if (tparam) { + if (!jl_is_type_type(a)) + return 0; + a = jl_unwrap_unionall(jl_tparam0(a)); + if (!jl_is_datatype(a)) + return tname_intersection(a, bname, 0); + } + return tname_intersection_dt((jl_datatype_t*)a, bname, jl_supertype_height((jl_datatype_t*)a)); + } + return 0; +} + +static int concrete_intersects(jl_value_t *t, jl_value_t *ty, int8_t tparam) +{ + if (ty == (jl_value_t*)jl_any_type) // easy case: Any always matches + return 1; + if (tparam & 1) + return jl_isa(t, ty); // (Type{t} <: ty), where is_leaf_type(t) => isa(t, ty) + else + return t == ty || jl_subtype(t, ty); +} + +// tparam bit 0 is ::Type{T} (vs. T) +// tparam bit 1 is typename(T) (vs. T) static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, int8_t tparam, int8_t offs, struct typemap_intersection_env *closure) { @@ -404,15 +458,26 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, size_t i, l = jl_array_len(a); _Atomic(jl_typemap_t*) *data = (_Atomic(jl_typemap_t*)*)jl_array_data(a); unsigned height = 0; - jl_datatype_t *tydt = NULL; - if (jl_is_kind(ty)) - ty = (jl_value_t*)jl_any_type; + jl_datatype_t *tydt = jl_any_type; if (tparam & 2) { - tydt = (jl_datatype_t*)jl_unwrap_unionall(ty); - if (jl_is_datatype(ty)) - height = jl_supertype_height(tydt); - else + // try to extract a description of ty for intersections, but since we + jl_value_t *ttype = jl_unwrap_unionall(ty); + if (tparam & 1) + // extract T from Type{T} (if possible) + ttype = jl_is_type_type(ttype) ? jl_tparam0(ttype) : NULL; + if (ttype && jl_is_datatype(ttype)) { + tydt = (jl_datatype_t*)ttype; + } + else if (ttype) { + ttype = jl_type_extract_name(ttype); + tydt = ttype ? (jl_datatype_t*)jl_unwrap_unionall(((jl_typename_t*)ttype)->wrapper) : NULL; + } + if (tydt == jl_any_type) + ty = (jl_value_t*)jl_any_type; + else if (tydt == NULL) tydt = jl_any_type; + else + height = jl_supertype_height(tydt); } for (i = 0; i < l; i += 2) { jl_value_t *t = jl_atomic_load_relaxed(&data[i]); @@ -422,8 +487,11 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, if (tparam & 2) { jl_typemap_t *ml = jl_atomic_load_relaxed(&data[i + 1]); JL_GC_PROMISE_ROOTED(ml); - if (tydt == jl_any_type || // easy case: Any always matches - tname_intersection(tydt, (jl_typename_t*)t, height)) { + if (tydt == jl_any_type ? + tname_intersection(ty, (jl_typename_t*)t, tparam & 1) : + tname_intersection_dt(tydt, (jl_typename_t*)t, height)) { + if ((tparam & 1) && t == (jl_value_t*)jl_typeofbottom_type->name) // skip Type{Union{}} and Type{typeof(Union{})}, since the caller should have already handled those + continue; if (jl_is_array(ml)) { if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, 1, offs, closure)) goto exit; @@ -436,10 +504,7 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, } else { // `t` is a leaftype, so intersection test becomes subtype (after excluding kinds) - if (ty == (jl_value_t*)jl_any_type || // easy case: Any always matches - (tparam & 1 - ? jl_isa(t, ty) // (Type{t} <: ty), where is_leaf_type(t) => isa(t, ty) - : (t == ty || jl_subtype(t, ty)))) { + if (concrete_intersects(t, ty, tparam)) { jl_typemap_t *ml = jl_atomic_load_relaxed(&data[i + 1]); JL_GC_PROMISE_ROOTED(ml); // NOTE: ml might be NULL if we're racing with the thread that's inserting the item @@ -456,6 +521,7 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, return 0; } + // calls fptr on each jl_typemap_entry_t in cache in sort order // for which type ∩ ml->type != Union{}, until fptr return false static int jl_typemap_intersection_node_visitor(jl_typemap_entry_t *ml, struct typemap_intersection_env *closure) @@ -496,6 +562,30 @@ static int jl_typemap_intersection_node_visitor(jl_typemap_entry_t *ml, struct t return 1; } +int jl_has_intersect_type_not_kind(jl_value_t *t); +int jl_has_intersect_kind_not_type(jl_value_t *t); + +void typemap_slurp_search(jl_typemap_entry_t *ml, struct typemap_intersection_env *closure) +{ + // n.b. we could consider mt->max_args here too, so this optimization + // usually works even if the user forgets the `slurp...` argument, but + // there is discussion that parameter may be going away? (and it is + // already not accurately up-to-date for all tables currently anyways) + if (closure->search_slurp && ml->va) { + jl_value_t *sig = jl_unwrap_unionall((jl_value_t*)ml->sig); + size_t nargs = jl_nparams(sig); + if (nargs > 1 && nargs - 1 == closure->search_slurp) { + jl_vararg_t *va = (jl_vararg_t*)jl_tparam(sig, nargs - 1); + assert(jl_is_vararg((jl_value_t*)va)); + if (va->T == (jl_value_t*)jl_any_type && va->N == NULL) { + // instruct typemap it can set exclude_typeofbottom on parameter nargs + // since we found the necessary slurp argument + closure->search_slurp = 0; + } + } + } +} + int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, struct typemap_intersection_env *closure) { @@ -504,13 +594,12 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, //TODO: fast-path for leaf-type tuples? //if (ttypes->isdispatchtuple) { // register jl_typemap_intersection_visitor_fptr fptr = closure->fptr; - // struct jl_typemap_assoc search = {(jl_value_t*)closure->type, world, closure->env, 0, ~(size_t)0}; - // jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(map, search, offs, /*subtype*/1); - // if (ml) { - // closure->env = search->env; - // if (!fptr(ml, closure)) - // return 0; - // } + // struct jl_typemap_assoc search = {(jl_value_t*)closure->type, world, closure->env, 0, ~(size_t)0}; + // jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(map, search, offs, /*subtype*/1); + // if (ml) { + // closure->env = search->env; + // if (!fptr(ml, closure)) + // return 0; // } // return 1; //} @@ -532,23 +621,56 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, if (ty) { while (jl_is_typevar(ty)) ty = ((jl_tvar_t*)ty)->ub; - jl_value_t *typetype = jl_unwrap_unionall(ty); - typetype = jl_is_type_type(typetype) ? jl_tparam0(typetype) : NULL; - jl_value_t *name = typetype ? jl_type_extract_name(typetype) : NULL; // approxify the tparam until we have a valid type - if (jl_has_free_typevars(ty)) { - ty = jl_unwrap_unionall(ty); - if (jl_is_datatype(ty)) - ty = ((jl_datatype_t*)ty)->name->wrapper; - else - ty = (jl_value_t*)jl_any_type; - } + if (jl_has_free_typevars(ty)) + ty = jl_rewrap_unionall(ty, closure->type); + JL_GC_PUSH1(&ty); jl_array_t *targ = jl_atomic_load_relaxed(&cache->targ); - if (targ != (jl_array_t*)jl_an_empty_vec_any - && !(typetype && !jl_has_free_typevars(typetype) && !is_cache_leaf(typetype, 1))) { // cannot contain this, so don't bother with checking - if (name && !jl_is_typevar(typetype)) { - // semi-direct lookup of types via their names - if (jl_type_extract_name_precise(typetype, 1)) { + jl_array_t *tname = jl_atomic_load_relaxed(&cache->tname); + int maybe_type = 0; + int maybe_kind = 0; + int exclude_typeofbottom = 0; + jl_value_t *typetype = NULL; + jl_value_t *name = NULL; + // pre-check: optimized pre-intersection test to see if `ty` could intersect with any Type or Kind + if (targ != (jl_array_t*)jl_an_empty_vec_any || tname != (jl_array_t*)jl_an_empty_vec_any) { + maybe_kind = jl_has_intersect_kind_not_type(ty); + maybe_type = maybe_kind || jl_has_intersect_type_not_kind(ty); + if (maybe_type && !maybe_kind) { + typetype = jl_unwrap_unionall(ty); + typetype = jl_is_type_type(typetype) ? jl_tparam0(typetype) : NULL; + name = typetype ? jl_type_extract_name(typetype) : NULL; + exclude_typeofbottom = !(typetype ? jl_parameter_includes_bottom(typetype) : jl_subtype((jl_value_t*)jl_typeofbottom_type, ty)); + } + } + // First check for intersections with methods defined on Type{T}, where T was a concrete type + if (targ != (jl_array_t*)jl_an_empty_vec_any && maybe_type && + (!typetype || jl_has_free_typevars(typetype) || is_cache_leaf(typetype, 1))) { // otherwise cannot contain this particular kind, so don't bother with checking + if (!exclude_typeofbottom) { + // detect Type{Union{}}, Type{Type{Union{}}}, and Type{typeof(Union{}} and do those early here + // otherwise the possibility of encountering `Type{Union{}}` in this intersection may + // be forcing us to do some extra work here whenever we see a typevar, even though + // the likelihood of that value actually occurring is frequently likely to be + // zero (or result in an ambiguous match) + targ = jl_atomic_load_relaxed(&cache->targ); // may be GC'd during type-intersection + jl_value_t *ml = mtcache_hash_lookup(targ, (jl_value_t*)jl_typeofbottom_type->name); + if (ml != jl_nothing) { + size_t search_slurp = closure->search_slurp; + closure->search_slurp = offs + 1; + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { + closure->search_slurp = search_slurp; + JL_GC_POP(); + return 0; + } + if (closure->search_slurp == 0) + exclude_typeofbottom = 1; + closure->search_slurp = search_slurp; + } + } + if (name != (jl_value_t*)jl_typeofbottom_type->name) { + targ = jl_atomic_load_relaxed(&cache->targ); // may be GC'd earlier + if (exclude_typeofbottom && name && jl_type_extract_name_precise(typetype, 1)) { + // attempt semi-direct lookup of types via their names // consider the type name first jl_value_t *ml = mtcache_hash_lookup(targ, (jl_value_t*)name); if (jl_is_array(ml)) { @@ -557,33 +679,21 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, if (is_cache_leaf(typetype, 1)) { ml = mtcache_hash_lookup((jl_array_t*)ml, typetype); if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { JL_GC_POP(); return 0; } } } } else { - if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, (jl_value_t*)ty, 1, offs, closure)) return 0; + if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, 1, offs, closure)) { JL_GC_POP(); return 0; } } } else if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { JL_GC_POP(); return 0; } } } else { - // consider all of the possible subtypes - // TODO: the possibility of encountering `Type{Union{}}` in this intersection may - // be forcing us to do some extra work here whenever we see a typevar, even though - // the likelihood of that value actually occurring is frequently likely to be - // zero (or result in an ambiguous match) - if (!jl_typemap_intersection_array_visitor((jl_array_t*)targ, (jl_value_t*)ty, 3, offs, closure)) return 0; - } - } - else { - // else an array scan is required to check subtypes - // first, fast-path: optimized pre-intersection test to see if `ty` could intersect with any Type - if (typetype || !jl_has_empty_intersection((jl_value_t*)jl_type_type, ty)) { - targ = jl_atomic_load_relaxed(&cache->targ); // may be GC'd during type-intersection - if (!jl_typemap_intersection_array_visitor(targ, ty, 3, offs, closure)) return 0; + // else an array scan is required to consider all the possible subtypes + if (!jl_typemap_intersection_array_visitor(targ, exclude_typeofbottom && !maybe_kind ? ty : (jl_value_t*)jl_any_type, 3, offs, closure)) { JL_GC_POP(); return 0; } } } } @@ -596,7 +706,7 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, if (jl_is_array(ml)) ml = mtcache_hash_lookup((jl_array_t*)ml, ty); if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) { JL_GC_POP(); return 0; } } } else { @@ -605,82 +715,87 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, // direct lookup of leaf types jl_value_t *ml = mtcache_hash_lookup(cachearg1, name); if (jl_is_array(ml)) { - if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, (jl_value_t*)ty, 0, offs, closure)) return 0; + if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, 0, offs, closure)) { JL_GC_POP(); return 0; } } else { - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { JL_GC_POP(); return 0; } } } else { // else an array scan is required to check subtypes - if (!jl_typemap_intersection_array_visitor(cachearg1, ty, 2, offs, closure)) return 0; + if (!jl_typemap_intersection_array_visitor(cachearg1, ty, 2, offs, closure)) { JL_GC_POP(); return 0; } } } } - jl_array_t *tname = jl_atomic_load_relaxed(&cache->tname); - if (tname != (jl_array_t*)jl_an_empty_vec_any) { - if (name && !jl_is_typevar(typetype)) { - // semi-direct lookup of types - // TODO: the possibility of encountering `Type{Union{}}` in this intersection may + // Next check for intersections with methods defined on Type{T}, where T was not concrete (it might even have been a TypeVar), but had an extractable TypeName + if (tname != (jl_array_t*)jl_an_empty_vec_any && maybe_type) { + if (!exclude_typeofbottom || (!typetype && jl_isa((jl_value_t*)jl_typeofbottom_type, ty))) { + // detect Type{Union{}}, Type{Type{Union{}}}, and Type{typeof(Union{}} and do those early here + // otherwise the possibility of encountering `Type{Union{}}` in this intersection may // be forcing us to do some extra work here whenever we see a typevar, even though // the likelihood of that value actually occurring is frequently likely to be // zero (or result in an ambiguous match) - jl_datatype_t *super = (jl_datatype_t*)jl_unwrap_unionall(((jl_typename_t*)name)->wrapper); - if (jl_type_extract_name_precise(typetype, 1)) { - // just consider the type and its direct super types - while (1) { - tname = jl_atomic_load_relaxed(&cache->tname); // reload after callback - jl_typemap_t *ml = mtcache_hash_lookup(tname, (jl_value_t*)super->name); - if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; - } - if (super == jl_any_type) - break; - super = super->super; + tname = jl_atomic_load_relaxed(&cache->tname); // may be GC'd earlier + jl_value_t *ml = mtcache_hash_lookup(tname, (jl_value_t*)jl_typeofbottom_type->name); + if (ml != jl_nothing) { + size_t search_slurp = closure->search_slurp; + closure->search_slurp = offs + 1; + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { + closure->search_slurp = search_slurp; + JL_GC_POP(); + return 0; } + if (closure->search_slurp == 0) + exclude_typeofbottom = 1; + closure->search_slurp = search_slurp; } - else { - // consider all of the possible subtypes - if (!jl_typemap_intersection_array_visitor(tname, (jl_value_t*)super, 3, offs, closure)) return 0; + } + if (exclude_typeofbottom && name && jl_type_extract_name_precise(typetype, 1)) { + // semi-direct lookup of types + // just consider the type and its direct super types + jl_datatype_t *super = (jl_datatype_t*)jl_unwrap_unionall(((jl_typename_t*)name)->wrapper); + if (super->name == jl_typeofbottom_type->name) + super = super->super; // this was handled above + while (1) { + tname = jl_atomic_load_relaxed(&cache->tname); // reload after callback + jl_typemap_t *ml = mtcache_hash_lookup(tname, (jl_value_t*)super->name); + if (ml != jl_nothing) { + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) { JL_GC_POP(); return 0; } + } + if (super == jl_any_type) + break; + super = super->super; } } else { - // else an array scan is required to check subtypes - // first, fast-path: optimized pre-intersection test to see if `ty` could intersect with any Type - if (name || !jl_has_empty_intersection((jl_value_t*)jl_type_type, ty)) { - tname = jl_atomic_load_relaxed(&cache->tname); // may be GC'd during type-intersection - if (!jl_typemap_intersection_array_visitor(tname, (jl_value_t*)jl_any_type, 3, offs, closure)) return 0; - } + // else an array scan is required to check subtypes of typetype too + tname = jl_atomic_load_relaxed(&cache->tname); // may be GC'd earlier + if (!jl_typemap_intersection_array_visitor(tname, exclude_typeofbottom && !maybe_kind ? ty : (jl_value_t*)jl_any_type, 3, offs, closure)) { JL_GC_POP(); return 0; } } } jl_array_t *name1 = jl_atomic_load_relaxed(&cache->name1); if (name1 != (jl_array_t*)jl_an_empty_vec_any) { jl_value_t *name = jl_type_extract_name(ty); - if (name) { + if (name && jl_type_extract_name_precise(ty, 0)) { jl_datatype_t *super = (jl_datatype_t*)jl_unwrap_unionall(((jl_typename_t*)name)->wrapper); - if (jl_type_extract_name_precise(ty, 0)) { - // direct lookup of concrete types - while (1) { - name1 = jl_atomic_load_relaxed(&cache->name1); // reload after callback - jl_typemap_t *ml = mtcache_hash_lookup(name1, (jl_value_t*)super->name); - if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; - } - if (super == jl_any_type) - break; - super = super->super; + // direct lookup of concrete types + while (1) { + name1 = jl_atomic_load_relaxed(&cache->name1); // reload after callback + jl_typemap_t *ml = mtcache_hash_lookup(name1, (jl_value_t*)super->name); + if (ml != jl_nothing) { + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) { JL_GC_POP(); return 0; } } - } - else { - // consider all of the possible subtypes too - if (!jl_typemap_intersection_array_visitor(name1, (jl_value_t*)super, 2, offs, closure)) return 0; + if (super == jl_any_type) + break; + super = super->super; } } else { // else an array scan is required to check subtypes - if (!jl_typemap_intersection_array_visitor(name1, (jl_value_t*)jl_any_type, 2, offs, closure)) return 0; + if (!jl_typemap_intersection_array_visitor(name1, ty, 2, offs, closure)) { JL_GC_POP(); return 0; } } } + JL_GC_POP(); } if (!jl_typemap_intersection_node_visitor(jl_atomic_load_relaxed(&cache->linear), closure)) return 0; diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 070e5d7a7b289..c5ff97deb6777 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -520,9 +520,6 @@ function test_primitives(::Type{T}, shape, ::Type{TestAbstractArray}) where T @test convert(Matrix, Y) == Y @test convert(Matrix, view(Y, 1:2, 1:2)) == Y @test_throws MethodError convert(Matrix, X) - - # convert(::Type{Union{}}, A::AbstractMatrix) - @test_throws MethodError convert(Union{}, X) end mutable struct TestThrowNoGetindex{T} <: AbstractVector{T} end diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 67fb16d3b7458..a1b973f30a70c 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -177,12 +177,10 @@ ambs = detect_ambiguities(Ambig48312) @test good end - # some ambiguities involving Union{} type parameters are expected, but not required + # some ambiguities involving Union{} type parameters may be expected, but not required let ambig = Set(detect_ambiguities(Core; recursive=true, ambiguous_bottom=true)) - m1 = which(Core.Compiler.convert, Tuple{Type{<:Core.IntrinsicFunction}, Any}) - m2 = which(Core.Compiler.convert, Tuple{Type{<:Nothing}, Any}) - pop!(ambig, (m1, m2)) @test !isempty(ambig) + @test length(ambig) < 30 end STDLIB_DIR = Sys.STDLIB diff --git a/test/core.jl b/test/core.jl index a89d206182dbf..daec51ab5b566 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1694,7 +1694,9 @@ end # issue #3221 let x = fill(nothing, 1) - @test_throws MethodError x[1] = 1 + @test_throws ErrorException("cannot convert a value to nothing for assignment") x[1] = 1 + x = Vector{Union{}}(undef, 1) + @test_throws ArgumentError("cannot convert a value to Union{} for assignment") x[1] = 1 end # issue #3220 @@ -4916,7 +4918,7 @@ struct f47209 x::Int f47209()::Nothing = new(1) end -@test_throws MethodError f47209() +@test_throws ErrorException("cannot convert a value to nothing for assignment") f47209() # issue #12096 let a = Val{Val{TypeVar(:_, Int)}}, diff --git a/test/missing.jl b/test/missing.jl index 450b816ea3e57..f06d1aad7a6b1 100644 --- a/test/missing.jl +++ b/test/missing.jl @@ -21,8 +21,8 @@ end @test convert(Union{Nothing, Missing}, nothing) === nothing @test convert(Union{Missing, Nothing, Float64}, 1) === 1.0 - @test_throws MethodError convert(Missing, 1) - @test_throws MethodError convert(Union{Nothing, Missing}, 1) + @test_throws ErrorException("cannot convert a value to missing for assignment") convert(Missing, 1) + @test_throws ErrorException("cannot convert a value to missing for assignment") convert(Union{Nothing, Missing}, 1) @test_throws MethodError convert(Union{Int, Missing}, "a") end diff --git a/test/some.jl b/test/some.jl index 27d50ca354a49..e49fc586a3a6e 100644 --- a/test/some.jl +++ b/test/some.jl @@ -33,7 +33,7 @@ @test convert(Union{Int, Nothing}, 1) === 1 @test convert(Union{Int, Nothing}, 1.0) === 1 @test convert(Nothing, nothing) === nothing -@test_throws MethodError convert(Nothing, 1) +@test_throws ErrorException("cannot convert a value to nothing for assignment") convert(Nothing, 1) ## show() From 285c770cfc487681fe33f2e08f788505158ae5be Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 14 Apr 2023 09:41:45 -0400 Subject: [PATCH 542/775] also optimize {Type{T},T} lookup Since T cannot be Union{} here, the prior optimization would not get detected post facto, but a priori this cannot be inhabited for T=Union{}, so we can exclude it immediately. This does not happen during inference, but shows up during edge validation somewhat often. --- src/typemap.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/typemap.c b/src/typemap.c index 3351725788601..ad54bfaa9b517 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -565,6 +565,16 @@ static int jl_typemap_intersection_node_visitor(jl_typemap_entry_t *ml, struct t int jl_has_intersect_type_not_kind(jl_value_t *t); int jl_has_intersect_kind_not_type(jl_value_t *t); +// if TypeVar tv is used covariantly, it cannot be Union{} +int has_covariant_var(jl_datatype_t *ttypes, jl_tvar_t *tv) +{ + size_t i, l = jl_nparams(ttypes); + for (i = 0; i < l; i++) + if (jl_tparam(ttypes, i) == (jl_value_t*)tv) + return 1; + return 0; +} + void typemap_slurp_search(jl_typemap_entry_t *ml, struct typemap_intersection_env *closure) { // n.b. we could consider mt->max_args here too, so this optimization @@ -640,7 +650,12 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, typetype = jl_unwrap_unionall(ty); typetype = jl_is_type_type(typetype) ? jl_tparam0(typetype) : NULL; name = typetype ? jl_type_extract_name(typetype) : NULL; - exclude_typeofbottom = !(typetype ? jl_parameter_includes_bottom(typetype) : jl_subtype((jl_value_t*)jl_typeofbottom_type, ty)); + if (!typetype) + exclude_typeofbottom = !jl_subtype((jl_value_t*)jl_typeofbottom_type, ty); + else if (jl_is_typevar(typetype)) + exclude_typeofbottom = has_covariant_var((jl_datatype_t*)ttypes, (jl_tvar_t*)typetype); + else + exclude_typeofbottom = !jl_parameter_includes_bottom(typetype); } } // First check for intersections with methods defined on Type{T}, where T was a concrete type From 22ee08924748ca82b6717c59b01b2316fba8f39d Mon Sep 17 00:00:00 2001 From: KristofferC Date: Fri, 24 Mar 2023 13:42:12 +0100 Subject: [PATCH 543/775] WIP: move Pkg out of the sysimage --- base/sysimg.jl | 3 --- contrib/generate_precompile.jl | 14 +------------- pkgimage.mk | 2 +- stdlib/REPL/src/REPL.jl | 25 +++++++++++++++++++++++++ test/precompile.jl | 4 ++-- 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/base/sysimg.jl b/base/sysimg.jl index b0eeffa5757ba..3bb79b0fde842 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -70,9 +70,6 @@ let # 5-depth packages :Downloads, - - # 6-depth packages - :Pkg, ] # PackageCompiler can filter out stdlibs so it can be empty maxlen = maximum(textwidth.(string.(stdlibs)); init=0) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index c99e6c646ec1c..64ffc4df65fa3 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -147,16 +147,6 @@ if Artifacts !== nothing """ end - -Pkg = get(Base.loaded_modules, - Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg"), - nothing) - -if Pkg !== nothing - # TODO: Split Pkg precompile script into REPL and script part - repl_script = Pkg.precompile_script * repl_script # do larger workloads first for better parallelization -end - FileWatching = get(Base.loaded_modules, Base.PkgId(Base.UUID("7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"), "FileWatching"), nothing) @@ -179,7 +169,6 @@ end const JULIA_PROMPT = "julia> " -const PKG_PROMPT = "pkg> " const SHELL_PROMPT = "shell> " const HELP_PROMPT = "help?> " @@ -352,7 +341,6 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe while !eof(output_copy) strbuf *= String(readavailable(output_copy)) occursin(JULIA_PROMPT, strbuf) && break - occursin(PKG_PROMPT, strbuf) && break occursin(SHELL_PROMPT, strbuf) && break occursin(HELP_PROMPT, strbuf) && break sleep(0.1) @@ -439,7 +427,7 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe if have_repl # Seems like a reasonable number right now, adjust as needed # comment out if debugging script - n_succeeded > 1500 || @warn "Only $n_succeeded precompile statements" + n_succeeded > 650 || @warn "Only $n_succeeded precompile statements" end fetch(step1) == :ok || throw("Step 1 of collecting precompiles failed.") diff --git a/pkgimage.mk b/pkgimage.mk index caf30a91c1d18..13a27d83dfed8 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -114,7 +114,7 @@ $(eval $(call sysimg_builder,LibCURL,LibCURL_jll MozillaCACerts_jll)) $(eval $(call sysimg_builder,Downloads,ArgTools FileWatching LibCURL NetworkOptions)) # 6-depth packages -$(eval $(call sysimg_builder,Pkg,Dates LibGit2 Libdl Logging Printf Random SHA UUIDs)) # Markdown REPL +$(eval $(call pkgimg_builder,Pkg,Dates LibGit2 Libdl Logging Printf Random SHA UUIDs)) # Markdown REPL # 7-depth packages $(eval $(call pkgimg_builder,LazyArtifacts,Artifacts Pkg)) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index c951f302359f2..12225946984ef 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1096,6 +1096,31 @@ function setup_interface( edit_insert(s, '?') end end, + ']' => function (s::MIState,o...) + if isempty(s) || position(LineEdit.buffer(s)) == 0 + pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg") + if Base.locate_package(pkgid) !== nothing # Only try load Pkg if we can find it + Pkg = Base.require(Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg")) + # Pkg should have loaded its REPL mode by now, let's find it so we can transition to it. + pkg_mode = nothing + for mode in repl.interface.modes + if mode isa LineEdit.Prompt && mode.complete isa Pkg.REPLMode.PkgCompletionProvider + pkg_mode = mode + break + end + end + # TODO: Cache the `pkg_mode`? + if pkg_mode !== nothing + buf = copy(LineEdit.buffer(s)) + transition(s, pkg_mode) do + LineEdit.state(s, pkg_mode).input_buffer = buf + end + return + end + end + end + edit_insert(s, ']') + end, # Bracketed Paste Mode "\e[200~" => (s::MIState,o...)->begin diff --git a/test/precompile.jl b/test/precompile.jl index 37498068fd39c..36f33046a68ad 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -397,8 +397,8 @@ precompile_test_harness(false) do dir [:ArgTools, :Artifacts, :Base64, :CompilerSupportLibraries_jll, :CRC32c, :Dates, :Downloads, :FileWatching, :Future, :InteractiveUtils, :libblastrampoline_jll, :LibCURL, :LibCURL_jll, :LibGit2, :Libdl, :LinearAlgebra, - :Logging, :Markdown, :Mmap, :MozillaCACerts_jll, :NetworkOptions, :OpenBLAS_jll, :Pkg, :Printf, - :p7zip_jll, :REPL, :Random, :SHA, :Serialization, :Sockets, + :Logging, :Markdown, :Mmap, :MozillaCACerts_jll, :NetworkOptions, :OpenBLAS_jll, :Printf, + :REPL, :Random, :SHA, :Serialization, :Sockets, :TOML, :Tar, :Test, :UUIDs, :Unicode, :nghttp2_jll] ), From 1c6271f48a9aedabd5a7bcf3557b24768689394e Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 19 Apr 2023 10:53:50 +0200 Subject: [PATCH 544/775] Reland: Improve performance of global code by emitting fewer atomic barriers. (#47636) --- src/codegen.cpp | 49 +++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 71a704910b70b..3cde68c60ced3 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7760,11 +7760,19 @@ static jl_llvm_functions_t Instruction &prologue_end = ctx.builder.GetInsertBlock()->back(); - // step 11a. Emit the entry safepoint + // step 11a. For top-level code, load the world age + if (toplevel && !ctx.is_opaque_closure) { + LoadInst *world = ctx.builder.CreateAlignedLoad(ctx.types().T_size, + prepare_global_in(jl_Module, jlgetworld_global), ctx.types().alignof_ptr); + world->setOrdering(AtomicOrdering::Acquire); + ctx.builder.CreateAlignedStore(world, world_age_field, ctx.types().alignof_ptr); + } + + // step 11b. Emit the entry safepoint if (JL_FEAT_TEST(ctx, safepoint_on_entry)) emit_gc_safepoint(ctx.builder, ctx.types().T_size, get_current_ptls(ctx), ctx.tbaa().tbaa_const); - // step 11b. Do codegen in control flow order + // step 11c. Do codegen in control flow order std::vector workstack; std::map BB; std::map come_from_bb; @@ -8087,13 +8095,6 @@ static jl_llvm_functions_t ctx.builder.SetInsertPoint(tryblk); } else { - if (!jl_is_method(ctx.linfo->def.method) && !ctx.is_opaque_closure) { - // TODO: inference is invalid if this has any effect (which it often does) - LoadInst *world = ctx.builder.CreateAlignedLoad(ctx.types().T_size, - prepare_global_in(jl_Module, jlgetworld_global), ctx.types().alignof_ptr); - world->setOrdering(AtomicOrdering::Acquire); - ctx.builder.CreateAlignedStore(world, world_age_field, ctx.types().alignof_ptr); - } emit_stmtpos(ctx, stmt, cursor); mallocVisitStmt(debuginfoloc, nullptr); } @@ -8319,12 +8320,12 @@ static jl_llvm_functions_t } // step 12. Perform any delayed instantiations - if (ctx.debug_enabled) { - bool in_prologue = true; - for (auto &BB : *ctx.f) { - for (auto &I : BB) { - CallBase *call = dyn_cast(&I); - if (call && !I.getDebugLoc()) { + bool in_prologue = true; + for (auto &BB : *ctx.f) { + for (auto &I : BB) { + CallBase *call = dyn_cast(&I); + if (call) { + if (ctx.debug_enabled && !I.getDebugLoc()) { // LLVM Verifier: inlinable function call in a function with debug info must have a !dbg location // make sure that anything we attempt to call has some inlining info, just in case optimization messed up // (except if we know that it is an intrinsic used in our prologue, which should never have its own debug subprogram) @@ -8333,12 +8334,24 @@ static jl_llvm_functions_t I.setDebugLoc(topdebugloc); } } - if (&I == &prologue_end) - in_prologue = false; + if (toplevel && !ctx.is_opaque_closure && !in_prologue) { + // we're at toplevel; insert an atomic barrier between every instruction + // TODO: inference is invalid if this has any effect (which it often does) + LoadInst *world = new LoadInst(ctx.types().T_size, + prepare_global_in(jl_Module, jlgetworld_global), Twine(), + /*isVolatile*/false, ctx.types().alignof_ptr, /*insertBefore*/&I); + world->setOrdering(AtomicOrdering::Acquire); + StoreInst *store_world = new StoreInst(world, world_age_field, + /*isVolatile*/false, ctx.types().alignof_ptr, /*insertBefore*/&I); + (void)store_world; + } } + if (&I == &prologue_end) + in_prologue = false; } - dbuilder.finalize(); } + if (ctx.debug_enabled) + dbuilder.finalize(); if (ctx.vaSlot > 0) { // remove VA allocation if we never referenced it From 2eea5857a2bd14c7aaa5665ae901b0ddd038da5e Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Wed, 19 Apr 2023 16:49:10 +0200 Subject: [PATCH 545/775] redirect muladd for BigFloat to fma (#49401) A fused multiply-add is available in MPFR as `mpfr_fma` and `Base.fma` already uses it. Apart from being fused (more accurate), it's also more performant than the generic `muladd` and allocates one less temporary `BigFloat`. --- base/mpfr.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/base/mpfr.jl b/base/mpfr.jl index 601d17490a77c..ff85fc6155df4 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -8,7 +8,7 @@ export import .Base: *, +, -, /, <, <=, ==, >, >=, ^, ceil, cmp, convert, copysign, div, - inv, exp, exp2, exponent, factorial, floor, fma, hypot, isinteger, + inv, exp, exp2, exponent, factorial, floor, fma, muladd, hypot, isinteger, isfinite, isinf, isnan, ldexp, log, log2, log10, max, min, mod, modf, nextfloat, prevfloat, promote_rule, rem, rem2pi, round, show, float, sum, sqrt, string, print, trunc, precision, _precision, exp10, expm1, log1p, @@ -536,6 +536,8 @@ function fma(x::BigFloat, y::BigFloat, z::BigFloat) return r end +muladd(x::BigFloat, y::BigFloat, z::BigFloat) = fma(x, y, z) + # div # BigFloat function div(x::BigFloat, y::BigFloat) From b6646931b830c323fbe9598630d5716eb6066ce4 Mon Sep 17 00:00:00 2001 From: Zachary P Christensen Date: Wed, 19 Apr 2023 10:51:18 -0400 Subject: [PATCH 546/775] Guidelines for documenting usntable contributions (#49296) (#49309) Encourage documentation of contributions that may be unstable but require explicit admonition. Also add review of "Writing Documentation" to contributors checklist --- CONTRIBUTING.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 099ef6b03509b..ecf19e4ff8faf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,6 +30,8 @@ If you are already familiar with Julia itself, this blog post by Katharine Hyatt * Review discussions on the [Julia Discourse forum](https://discourse.julialang.org). +* Review [Writing Documentation](https://github.com/JuliaLang/julia/blob/master/doc/src/manual/documentation.md#writing-documentation). + * For more detailed tips, read the [submission guide](https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md#submitting-contributions) below. * Relax and have fun! @@ -181,6 +183,15 @@ At the moment, this should always be done with the following `compat` admonition This method was added in Julia 1.X. ``` +#### Documenting unstable APIs + +Some contributions are intended primarily for internal use or may be part of an API that is still in development. In addition to appropriate documentation, relevant code should be clearly annotated as unstable as follows: + + ``` + !!! danger "Unstable" + This method is unstable and subject to change without notice. + ``` + ### Contributing to core functionality or base libraries *By contributing code to Julia, you are agreeing to release it under the [MIT License](https://github.com/JuliaLang/julia/tree/master/LICENSE.md).* From ba9c45566ac0f668a674397f0d84d641e107cab4 Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Tue, 18 Apr 2023 14:46:17 +0200 Subject: [PATCH 547/775] bump Pkg to latest version --- .../Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/md5 | 1 + .../Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/sha512 | 1 + .../Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 | 1 - .../Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/md5 create mode 100644 deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 diff --git a/deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/md5 b/deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/md5 new file mode 100644 index 0000000000000..b7f96f9d33c6c --- /dev/null +++ b/deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/md5 @@ -0,0 +1 @@ +99234d899798a88e8b3ce3021dba7740 diff --git a/deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/sha512 b/deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/sha512 new file mode 100644 index 0000000000000..26b61ade7e7a1 --- /dev/null +++ b/deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/sha512 @@ -0,0 +1 @@ +64124b687d78cf386248b80724131780da09f224a2848d09e6017795fe297b95702edd507f8818ee730bb1e6fb8cea4d1dad774bbc71443ac17c17ad82d6fcf3 diff --git a/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 b/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 deleted file mode 100644 index 7a0f31b8bec01..0000000000000 --- a/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -2885181bffe95462f1877668ccea7057 diff --git a/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 b/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 deleted file mode 100644 index f8231bbf2833f..0000000000000 --- a/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -5f3ded1970a6d8bfc779de54e61b1dd58fa770799aac3a031b2ea536d159bf672e9490f53ecdfbf1c175adfe93b0868a88330619506da802218a98f07e64dd94 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 4274859b10120..a06ec5b6df3f2 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc +PKG_SHA1 = 2618945685d32b1e595f00ef809d545067ae01f1 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 7b99ca529165bda29620d65e73f8146de221ebcd Mon Sep 17 00:00:00 2001 From: oscarddssmith Date: Wed, 19 Apr 2023 12:39:07 -0400 Subject: [PATCH 548/775] typemin divided by negative 1 is an error --- test/numbers.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/numbers.jl b/test/numbers.jl index d7baecd847c8f..af7fa98b86258 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -2483,6 +2483,7 @@ Base.abs(x::TestNumber) = TestNumber(abs(x.inner)) d == 0 && continue fastd = Base.multiplicativeinverse(d) for n in numrange + d == -1 && n == typemin(typeof(n)) && continue @test div(n,d) == div(n,fastd) end end @@ -2494,7 +2495,7 @@ Base.abs(x::TestNumber) = TestNumber(abs(x.inner)) end for T in [UInt8, UInt16, UInt32, UInt64, UInt128, Int8, Int16, Int32, Int64, Int128] testmi(map(T, typemax(T)-50:typemax(T)), map(T, 1:50)) - testmi(filter(!iszero, rand(T, 50)), filter(!iszero, rand(T, 50))) + testmi(rand(T, 50), rand(T, 50)) @test_throws ArgumentError Base.multiplicativeinverse(T(0)) end From 174e138d023cbbcd64fff0e2642583b317770450 Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Wed, 19 Apr 2023 15:21:48 -0400 Subject: [PATCH 549/775] Revert "Guidelines for documenting usntable contributions (#49296) (#49309)" (#49427) This reverts commit b6646931b830c323fbe9598630d5716eb6066ce4. --- CONTRIBUTING.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ecf19e4ff8faf..099ef6b03509b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,8 +30,6 @@ If you are already familiar with Julia itself, this blog post by Katharine Hyatt * Review discussions on the [Julia Discourse forum](https://discourse.julialang.org). -* Review [Writing Documentation](https://github.com/JuliaLang/julia/blob/master/doc/src/manual/documentation.md#writing-documentation). - * For more detailed tips, read the [submission guide](https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md#submitting-contributions) below. * Relax and have fun! @@ -183,15 +181,6 @@ At the moment, this should always be done with the following `compat` admonition This method was added in Julia 1.X. ``` -#### Documenting unstable APIs - -Some contributions are intended primarily for internal use or may be part of an API that is still in development. In addition to appropriate documentation, relevant code should be clearly annotated as unstable as follows: - - ``` - !!! danger "Unstable" - This method is unstable and subject to change without notice. - ``` - ### Contributing to core functionality or base libraries *By contributing code to Julia, you are agreeing to release it under the [MIT License](https://github.com/JuliaLang/julia/tree/master/LICENSE.md).* From b864999aeb0ef365421a44bff5ec37acdcca9828 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 20 Apr 2023 03:05:35 +0200 Subject: [PATCH 550/775] enable loading Profile in listener even if it is not in project (#49429) --- base/Base.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/Base.jl b/base/Base.jl index 730239dea0c7d..a824cf8487193 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -559,7 +559,8 @@ function profile_printing_listener() try while true wait(PROFILE_PRINT_COND[]) - profile = @something(profile, require(Base, :Profile)) + profile = @something(profile, require(PkgId(UUID("9abbd945-dff8-562f-b5e8-e1ebf5ef1b79"), "Profile"))) + invokelatest(profile.peek_report[]) if Base.get_bool_env("JULIA_PROFILE_PEEK_HEAP_SNAPSHOT", false) === true println(stderr, "Saving heap snapshot...") From a6df43e23b7ede93081e9a9e96823ef8a350fb51 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 20 Apr 2023 07:24:10 +0200 Subject: [PATCH 551/775] remove Serialization from being stated as being a dependency of Random (#49431) It is not --- pkgimage.mk | 2 +- stdlib/Random/Project.toml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgimage.mk b/pkgimage.mk index 13a27d83dfed8..6f90911d4a836 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -83,7 +83,7 @@ $(eval $(call sysimg_builder,libblastrampoline_jll,Artifacts Libdl)) $(eval $(call sysimg_builder,OpenBLAS_jll,Artifacts Libdl)) $(eval $(call sysimg_builder,Markdown,Base64)) $(eval $(call sysimg_builder,Printf,Unicode)) -$(eval $(call sysimg_builder,Random,Serialization SHA)) +$(eval $(call sysimg_builder,Random,SHA)) $(eval $(call sysimg_builder,Tar,ArgTools,SHA)) $(eval $(call pkgimg_builder,DelimitedFiles,Mmap)) diff --git a/stdlib/Random/Project.toml b/stdlib/Random/Project.toml index 199dcab940c86..f32fc3e2a4f84 100644 --- a/stdlib/Random/Project.toml +++ b/stdlib/Random/Project.toml @@ -2,7 +2,6 @@ name = "Random" uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [deps] -Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b" SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce" [extras] From dcdac1e895f0ea3e70cb4584c82fed12e3bdb1f8 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 20 Apr 2023 07:36:25 +0200 Subject: [PATCH 552/775] Revert "move Pkg out of the sysimage" (#49432) --- base/sysimg.jl | 3 +++ contrib/generate_precompile.jl | 14 ++++++++++- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + pkgimage.mk | 2 +- stdlib/Pkg.version | 2 +- stdlib/REPL/src/REPL.jl | 25 ------------------- test/precompile.jl | 4 +-- 10 files changed, 22 insertions(+), 32 deletions(-) delete mode 100644 deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 create mode 100644 deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 diff --git a/base/sysimg.jl b/base/sysimg.jl index 3bb79b0fde842..b0eeffa5757ba 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -70,6 +70,9 @@ let # 5-depth packages :Downloads, + + # 6-depth packages + :Pkg, ] # PackageCompiler can filter out stdlibs so it can be empty maxlen = maximum(textwidth.(string.(stdlibs)); init=0) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 64ffc4df65fa3..c99e6c646ec1c 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -147,6 +147,16 @@ if Artifacts !== nothing """ end + +Pkg = get(Base.loaded_modules, + Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg"), + nothing) + +if Pkg !== nothing + # TODO: Split Pkg precompile script into REPL and script part + repl_script = Pkg.precompile_script * repl_script # do larger workloads first for better parallelization +end + FileWatching = get(Base.loaded_modules, Base.PkgId(Base.UUID("7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"), "FileWatching"), nothing) @@ -169,6 +179,7 @@ end const JULIA_PROMPT = "julia> " +const PKG_PROMPT = "pkg> " const SHELL_PROMPT = "shell> " const HELP_PROMPT = "help?> " @@ -341,6 +352,7 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe while !eof(output_copy) strbuf *= String(readavailable(output_copy)) occursin(JULIA_PROMPT, strbuf) && break + occursin(PKG_PROMPT, strbuf) && break occursin(SHELL_PROMPT, strbuf) && break occursin(HELP_PROMPT, strbuf) && break sleep(0.1) @@ -427,7 +439,7 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe if have_repl # Seems like a reasonable number right now, adjust as needed # comment out if debugging script - n_succeeded > 650 || @warn "Only $n_succeeded precompile statements" + n_succeeded > 1500 || @warn "Only $n_succeeded precompile statements" end fetch(step1) == :ok || throw("Step 1 of collecting precompiles failed.") diff --git a/deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/md5 b/deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/md5 deleted file mode 100644 index b7f96f9d33c6c..0000000000000 --- a/deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -99234d899798a88e8b3ce3021dba7740 diff --git a/deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/sha512 b/deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/sha512 deleted file mode 100644 index 26b61ade7e7a1..0000000000000 --- a/deps/checksums/Pkg-2618945685d32b1e595f00ef809d545067ae01f1.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -64124b687d78cf386248b80724131780da09f224a2848d09e6017795fe297b95702edd507f8818ee730bb1e6fb8cea4d1dad774bbc71443ac17c17ad82d6fcf3 diff --git a/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 b/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 new file mode 100644 index 0000000000000..7a0f31b8bec01 --- /dev/null +++ b/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 @@ -0,0 +1 @@ +2885181bffe95462f1877668ccea7057 diff --git a/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 b/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 new file mode 100644 index 0000000000000..f8231bbf2833f --- /dev/null +++ b/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 @@ -0,0 +1 @@ +5f3ded1970a6d8bfc779de54e61b1dd58fa770799aac3a031b2ea536d159bf672e9490f53ecdfbf1c175adfe93b0868a88330619506da802218a98f07e64dd94 diff --git a/pkgimage.mk b/pkgimage.mk index 6f90911d4a836..dcf9dd1303d47 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -114,7 +114,7 @@ $(eval $(call sysimg_builder,LibCURL,LibCURL_jll MozillaCACerts_jll)) $(eval $(call sysimg_builder,Downloads,ArgTools FileWatching LibCURL NetworkOptions)) # 6-depth packages -$(eval $(call pkgimg_builder,Pkg,Dates LibGit2 Libdl Logging Printf Random SHA UUIDs)) # Markdown REPL +$(eval $(call sysimg_builder,Pkg,Dates LibGit2 Libdl Logging Printf Random SHA UUIDs)) # Markdown REPL # 7-depth packages $(eval $(call pkgimg_builder,LazyArtifacts,Artifacts Pkg)) diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index a06ec5b6df3f2..4274859b10120 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 2618945685d32b1e595f00ef809d545067ae01f1 +PKG_SHA1 = 7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 12225946984ef..c951f302359f2 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1096,31 +1096,6 @@ function setup_interface( edit_insert(s, '?') end end, - ']' => function (s::MIState,o...) - if isempty(s) || position(LineEdit.buffer(s)) == 0 - pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg") - if Base.locate_package(pkgid) !== nothing # Only try load Pkg if we can find it - Pkg = Base.require(Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg")) - # Pkg should have loaded its REPL mode by now, let's find it so we can transition to it. - pkg_mode = nothing - for mode in repl.interface.modes - if mode isa LineEdit.Prompt && mode.complete isa Pkg.REPLMode.PkgCompletionProvider - pkg_mode = mode - break - end - end - # TODO: Cache the `pkg_mode`? - if pkg_mode !== nothing - buf = copy(LineEdit.buffer(s)) - transition(s, pkg_mode) do - LineEdit.state(s, pkg_mode).input_buffer = buf - end - return - end - end - end - edit_insert(s, ']') - end, # Bracketed Paste Mode "\e[200~" => (s::MIState,o...)->begin diff --git a/test/precompile.jl b/test/precompile.jl index 36f33046a68ad..37498068fd39c 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -397,8 +397,8 @@ precompile_test_harness(false) do dir [:ArgTools, :Artifacts, :Base64, :CompilerSupportLibraries_jll, :CRC32c, :Dates, :Downloads, :FileWatching, :Future, :InteractiveUtils, :libblastrampoline_jll, :LibCURL, :LibCURL_jll, :LibGit2, :Libdl, :LinearAlgebra, - :Logging, :Markdown, :Mmap, :MozillaCACerts_jll, :NetworkOptions, :OpenBLAS_jll, :Printf, - :REPL, :Random, :SHA, :Serialization, :Sockets, + :Logging, :Markdown, :Mmap, :MozillaCACerts_jll, :NetworkOptions, :OpenBLAS_jll, :Pkg, :Printf, + :p7zip_jll, :REPL, :Random, :SHA, :Serialization, :Sockets, :TOML, :Tar, :Test, :UUIDs, :Unicode, :nghttp2_jll] ), From c068048f06619c0cfa35d76739632a5cff14836d Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Thu, 20 Apr 2023 09:50:22 +0200 Subject: [PATCH 553/775] Allow conversion of `AbstractQ` to `AbstractArray` (#49424) --- stdlib/LinearAlgebra/src/abstractq.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/src/abstractq.jl b/stdlib/LinearAlgebra/src/abstractq.jl index 334a9a9235972..88610dac2e6f6 100644 --- a/stdlib/LinearAlgebra/src/abstractq.jl +++ b/stdlib/LinearAlgebra/src/abstractq.jl @@ -40,7 +40,7 @@ Matrix{T}(adjQ::AdjointQ{S}) where {T,S} = convert(Matrix{T}, lmul!(adjQ, Matrix Matrix(Q::AbstractQ{T}) where {T} = Matrix{T}(Q) Array{T}(Q::AbstractQ) where {T} = Matrix{T}(Q) Array(Q::AbstractQ) = Matrix(Q) -convert(::Type{T}, Q::AbstractQ) where {T<:Array} = T(Q) +convert(::Type{T}, Q::AbstractQ) where {T<:AbstractArray} = T(Q) # legacy @deprecate(convert(::Type{AbstractMatrix{T}}, Q::AbstractQ) where {T}, convert(LinearAlgebra.AbstractQ{T}, Q)) From bb118c99ce5b08dc1be2c88a4f9d561646b06d63 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 20 Apr 2023 16:50:22 +0200 Subject: [PATCH 554/775] prevent `display` in REPL from erroring on non standard prompts (#49383) --- stdlib/REPL/src/REPL.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index c951f302359f2..f8bb442ad6ec4 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -261,7 +261,9 @@ function display(d::REPLDisplay, mime::MIME"text/plain", x) if d.repl isa LineEditREPL mistate = d.repl.mistate mode = LineEdit.mode(mistate) - LineEdit.write_output_prefix(io, mode, get(io, :color, false)::Bool) + if mode isa LineEdit.Prompt + LineEdit.write_output_prefix(io, mode, get(io, :color, false)::Bool) + end end get(io, :color, false)::Bool && write(io, answer_color(d.repl)) if isdefined(d.repl, :options) && isdefined(d.repl.options, :iocontext) From ca5070695499661d79011e6da9cfd2b4ab9eb701 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 20 Apr 2023 11:30:38 -0400 Subject: [PATCH 555/775] fix regression in methods lookup (#49416) Certain queries were searching for Type{T} instead of T due to a mistaken tparam setting, resulting in missing methods in lookup. Fix #49408 Ref #48925 --- src/typemap.c | 2 +- test/reflection.jl | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/typemap.c b/src/typemap.c index bd9fd00f06815..e60f3d566284e 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -425,7 +425,7 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, if (tydt == jl_any_type || // easy case: Any always matches tname_intersection(tydt, (jl_typename_t*)t, height)) { if (jl_is_array(ml)) { - if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, 1, offs, closure)) + if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, tparam & ~2, offs, closure)) goto exit; } else { diff --git a/test/reflection.jl b/test/reflection.jl index 3797cab1e5465..8fdfa5be0b57c 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -1038,6 +1038,10 @@ ambig_effects_test(a, b) = 1 end @test Base._methods_by_ftype(Tuple{}, -1, Base.get_world_counter()) == Any[] +@test length(methods(Base.Broadcast.broadcasted, Tuple{Any, Any, Vararg})) > + length(methods(Base.Broadcast.broadcasted, Tuple{Base.Broadcast.BroadcastStyle, Any, Vararg})) >= + length(methods(Base.Broadcast.broadcasted, Tuple{Base.Broadcast.DefaultArrayStyle{1}, Any, Vararg})) >= + 10 @testset "specializations" begin f(x) = 1 From c237c0ad0aae0af74f782239e3ff4705c70c8941 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 20 Apr 2023 11:35:26 -0400 Subject: [PATCH 556/775] subtype: replace leaf-bound typevars if they would result in Tuple{Union{}} otherwise (#49393) This was a primary motivation for #49111. Previously, we'd see some some method specializations such as `convert(::Type{T}, ::T) where T<:Float64` (apparently from inference of some tuple convert specializations), which were not necessary to have. --- src/subtype.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 5b05e8197a420..9b85c0ceb703c 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2774,10 +2774,9 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind // given x<:T<:x, substitute x for T varval = vb->ub; } - // TODO: `vb.occurs_cov == 1` here allows substituting Tuple{<:X} => Tuple{X}, - // which is valid but changes some ambiguity errors so we don't need to do it yet. - else if ((/*vb->occurs_cov == 1 || */is_leaf_bound(vb->ub)) && - !var_occurs_invariant(u->body, u->var, 0)) { + // TODO: `vb.occurs_cov == 1`, we could also substitute Tuple{<:X} => Tuple{X}, + // but it may change some ambiguity errors so we don't need to do it yet. + else if (vb->occurs_cov && is_leaf_bound(vb->ub) && !jl_has_free_typevars(vb->ub)) { // replace T<:x with x in covariant position when possible varval = vb->ub; } From 02b7b048b239b855c48fa2867387f90cda92db44 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 20 Apr 2023 11:36:15 -0400 Subject: [PATCH 557/775] simplify Broadcast object computations (#49395) Code should normally preserve values, not the types of values. This ensures the user can define styles with metadata, and requires less type-parameter-based programming, but rather can focus on the values. --- base/broadcast.jl | 70 +++++++++++++++++++++++++++-------------------- test/broadcast.jl | 2 +- 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/base/broadcast.jl b/base/broadcast.jl index 94413ae05c87a..1e057789509ed 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -167,16 +167,28 @@ BroadcastStyle(a::AbstractArrayStyle{M}, ::DefaultArrayStyle{N}) where {M,N} = # copyto!(dest::AbstractArray, bc::Broadcasted{MyStyle}) struct Broadcasted{Style<:Union{Nothing,BroadcastStyle}, Axes, F, Args<:Tuple} <: Base.AbstractBroadcasted + style::Style f::F args::Args axes::Axes # the axes of the resulting object (may be bigger than implied by `args` if this is nested inside a larger `Broadcasted`) -end -Broadcasted(f::F, args::Args, axes=nothing) where {F, Args<:Tuple} = - Broadcasted{typeof(combine_styles(args...))}(f, args, axes) -function Broadcasted{Style}(f::F, args::Args, axes=nothing) where {Style, F, Args<:Tuple} - # using Core.Typeof rather than F preserves inferrability when f is a type - Broadcasted{Style, typeof(axes), Core.Typeof(f), Args}(f, args, axes) + Broadcasted(style::Union{Nothing,BroadcastStyle}, f::Tuple, args::Tuple) = error() # disambiguation: tuple is not callable + function Broadcasted(style::Union{Nothing,BroadcastStyle}, f::F, args::Tuple, axes=nothing) where {F} + # using Core.Typeof rather than F preserves inferrability when f is a type + return new{typeof(style), typeof(axes), Core.Typeof(f), typeof(args)}(style, f, args, axes) + end + + function Broadcasted(f::F, args::Tuple, axes=nothing) where {F} + Broadcasted(combine_styles(args...)::BroadcastStyle, f, args, axes) + end + + function Broadcasted{Style}(f::F, args, axes=nothing) where {Style, F} + return new{Style, typeof(axes), Core.Typeof(f), typeof(args)}(Style()::Style, f, args, axes) + end + + function Broadcasted{Style,Axes,F,Args}(f, args, axes) where {Style,Axes,F,Args} + return new{Style, Axes, F, Args}(Style()::Style, f, args, axes) + end end struct AndAnd end @@ -194,7 +206,7 @@ function broadcasted(::OrOr, a, bc::Broadcasted) broadcasted((a, args...) -> a || bcf.f(args...), a, bcf.args...) end -Base.convert(::Type{Broadcasted{NewStyle}}, bc::Broadcasted{Style,Axes,F,Args}) where {NewStyle,Style,Axes,F,Args} = +Base.convert(::Type{Broadcasted{NewStyle}}, bc::Broadcasted{<:Any,Axes,F,Args}) where {NewStyle,Axes,F,Args} = Broadcasted{NewStyle,Axes,F,Args}(bc.f, bc.args, bc.axes)::Broadcasted{NewStyle,Axes,F,Args} function Base.show(io::IO, bc::Broadcasted{Style}) where {Style} @@ -202,8 +214,8 @@ function Base.show(io::IO, bc::Broadcasted{Style}) where {Style} # Only show the style parameter if we have a set of axes — representing an instantiated # "outermost" Broadcasted. The styles of nested Broadcasteds represent an intermediate # computation that is not relevant for dispatch, confusing, and just extra line noise. - bc.axes isa Tuple && print(io, '{', Style, '}') - print(io, '(', bc.f, ", ", bc.args, ')') + bc.axes isa Tuple && print(io, "{", Style, "}") + print(io, "(", bc.f, ", ", bc.args, ")") nothing end @@ -231,7 +243,7 @@ BroadcastStyle(::Type{<:Broadcasted{Style}}) where {Style} = Style() BroadcastStyle(::Type{<:Broadcasted{S}}) where {S<:Union{Nothing,Unknown}} = throw(ArgumentError("Broadcasted{Unknown} wrappers do not have a style assigned")) -argtype(::Type{Broadcasted{Style,Axes,F,Args}}) where {Style,Axes,F,Args} = Args +argtype(::Type{BC}) where {BC<:Broadcasted} = fieldtype(BC, :args) argtype(bc::Broadcasted) = argtype(typeof(bc)) @inline Base.eachindex(bc::Broadcasted) = _eachindex(axes(bc)) @@ -262,7 +274,7 @@ Base.@propagate_inbounds function Base.iterate(bc::Broadcasted, s) end Base.IteratorSize(::Type{T}) where {T<:Broadcasted} = Base.HasShape{ndims(T)}() -Base.ndims(BC::Type{<:Broadcasted{<:Any,Nothing}}) = _maxndims(fieldtype(BC, 2)) +Base.ndims(BC::Type{<:Broadcasted{<:Any,Nothing}}) = _maxndims(fieldtype(BC, :args)) Base.ndims(::Type{<:Broadcasted{<:AbstractArrayStyle{N},Nothing}}) where {N<:Integer} = N _maxndims(T::Type{<:Tuple}) = reduce(max, (ntuple(n -> _ndims(fieldtype(T, n)), Base._counttuple(T)))) @@ -289,14 +301,14 @@ Custom [`BroadcastStyle`](@ref)s may override this default in cases where it is to compute and verify the resulting `axes` on-demand, leaving the `axis` field of the `Broadcasted` object empty (populated with [`nothing`](@ref)). """ -@inline function instantiate(bc::Broadcasted{Style}) where {Style} +@inline function instantiate(bc::Broadcasted) if bc.axes isa Nothing # Not done via dispatch to make it easier to extend instantiate(::Broadcasted{Style}) axes = combine_axes(bc.args...) else axes = bc.axes check_broadcast_axes(axes, bc.args...) end - return Broadcasted{Style}(bc.f, bc.args, axes) + return Broadcasted(bc.style, bc.f, bc.args, axes) end instantiate(bc::Broadcasted{<:AbstractArrayStyle{0}}) = bc # Tuples don't need axes, but when they have axes (for .= assignment), we need to check them (#33020) @@ -325,7 +337,7 @@ becomes This is an optional operation that may make custom implementation of broadcasting easier in some cases. """ -function flatten(bc::Broadcasted{Style}) where {Style} +function flatten(bc::Broadcasted) isflat(bc) && return bc # concatenate the nested arguments into {a, b, c, d} args = cat_nested(bc) @@ -341,7 +353,7 @@ function flatten(bc::Broadcasted{Style}) where {Style} newf = @inline function(args::Vararg{Any,N}) where N f(makeargs(args...)...) end - return Broadcasted{Style}(newf, args, bc.axes) + return Broadcasted(bc.style, newf, args, bc.axes) end end @@ -895,11 +907,11 @@ materialize(x) = x return materialize!(dest, instantiate(Broadcasted(identity, (x,), axes(dest)))) end -@inline function materialize!(dest, bc::Broadcasted{Style}) where {Style} +@inline function materialize!(dest, bc::Broadcasted{<:Any}) return materialize!(combine_styles(dest, bc), dest, bc) end -@inline function materialize!(::BroadcastStyle, dest, bc::Broadcasted{Style}) where {Style} - return copyto!(dest, instantiate(Broadcasted{Style}(bc.f, bc.args, axes(dest)))) +@inline function materialize!(::BroadcastStyle, dest, bc::Broadcasted{<:Any}) + return copyto!(dest, instantiate(Broadcasted(bc.style, bc.f, bc.args, axes(dest)))) end ## general `copy` methods @@ -909,7 +921,7 @@ copy(bc::Broadcasted{<:Union{Nothing,Unknown}}) = const NonleafHandlingStyles = Union{DefaultArrayStyle,ArrayConflict} -@inline function copy(bc::Broadcasted{Style}) where {Style} +@inline function copy(bc::Broadcasted) ElType = combine_eltypes(bc.f, bc.args) if Base.isconcretetype(ElType) # We can trust it and defer to the simpler `copyto!` @@ -968,7 +980,7 @@ broadcast_unalias(::Nothing, src) = src # Preprocessing a `Broadcasted` does two things: # * unaliases any arguments from `dest` # * "extrudes" the arguments where it is advantageous to pre-compute the broadcasted indices -@inline preprocess(dest, bc::Broadcasted{Style}) where {Style} = Broadcasted{Style}(bc.f, preprocess_args(dest, bc.args), bc.axes) +@inline preprocess(dest, bc::Broadcasted) = Broadcasted(bc.style, bc.f, preprocess_args(dest, bc.args), bc.axes) preprocess(dest, x) = extrude(broadcast_unalias(dest, x)) @inline preprocess_args(dest, args::Tuple) = (preprocess(dest, args[1]), preprocess_args(dest, tail(args))...) @@ -1038,11 +1050,11 @@ ischunkedbroadcast(R, args::Tuple{<:BroadcastedChunkableOp,Vararg{Any}}) = ischu ischunkedbroadcast(R, args::Tuple{}) = true # Convert compatible functions to chunkable ones. They must also be green-lighted as ChunkableOps -liftfuncs(bc::Broadcasted{Style}) where {Style} = Broadcasted{Style}(bc.f, map(liftfuncs, bc.args), bc.axes) -liftfuncs(bc::Broadcasted{Style,<:Any,typeof(sign)}) where {Style} = Broadcasted{Style}(identity, map(liftfuncs, bc.args), bc.axes) -liftfuncs(bc::Broadcasted{Style,<:Any,typeof(!)}) where {Style} = Broadcasted{Style}(~, map(liftfuncs, bc.args), bc.axes) -liftfuncs(bc::Broadcasted{Style,<:Any,typeof(*)}) where {Style} = Broadcasted{Style}(&, map(liftfuncs, bc.args), bc.axes) -liftfuncs(bc::Broadcasted{Style,<:Any,typeof(==)}) where {Style} = Broadcasted{Style}((~)∘(xor), map(liftfuncs, bc.args), bc.axes) +liftfuncs(bc::Broadcasted{<:Any,<:Any,<:Any}) = Broadcasted(bc.style, bc.f, map(liftfuncs, bc.args), bc.axes) +liftfuncs(bc::Broadcasted{<:Any,<:Any,typeof(sign)}) = Broadcasted(bc.style, identity, map(liftfuncs, bc.args), bc.axes) +liftfuncs(bc::Broadcasted{<:Any,<:Any,typeof(!)}) = Broadcasted(bc.style, ~, map(liftfuncs, bc.args), bc.axes) +liftfuncs(bc::Broadcasted{<:Any,<:Any,typeof(*)}) = Broadcasted(bc.style, &, map(liftfuncs, bc.args), bc.axes) +liftfuncs(bc::Broadcasted{<:Any,<:Any,typeof(==)}) = Broadcasted(bc.style, (~)∘(xor), map(liftfuncs, bc.args), bc.axes) liftfuncs(x) = x liftchunks(::Tuple{}) = () @@ -1315,7 +1327,7 @@ end return broadcasted((args...) -> f(args...; kwargs...), args...) end end -@inline function broadcasted(f, args...) +@inline function broadcasted(f::F, args...) where {F} args′ = map(broadcastable, args) broadcasted(combine_styles(args′...), f, args′...) end @@ -1323,18 +1335,18 @@ end # the totally generic varargs broadcasted(f, args...) method above loses Type{T}s in # mapping broadcastable across the args. These additional methods with explicit # arguments ensure we preserve Type{T}s in the first or second argument position. -@inline function broadcasted(f, arg1, args...) +@inline function broadcasted(f::F, arg1, args...) where {F} arg1′ = broadcastable(arg1) args′ = map(broadcastable, args) broadcasted(combine_styles(arg1′, args′...), f, arg1′, args′...) end -@inline function broadcasted(f, arg1, arg2, args...) +@inline function broadcasted(f::F, arg1, arg2, args...) where {F} arg1′ = broadcastable(arg1) arg2′ = broadcastable(arg2) args′ = map(broadcastable, args) broadcasted(combine_styles(arg1′, arg2′, args′...), f, arg1′, arg2′, args′...) end -@inline broadcasted(::S, f, args...) where S<:BroadcastStyle = Broadcasted{S}(f, args) +@inline broadcasted(style::BroadcastStyle, f::F, args...) where {F} = Broadcasted(style, f, args) """ BroadcastFunction{F} <: Function diff --git a/test/broadcast.jl b/test/broadcast.jl index 41ca604cb50e4..87858dd0f08fc 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -880,7 +880,7 @@ let @test Broadcast.broadcasted(+, AD1(rand(3)), AD2(rand(3))) isa Broadcast.Broadcasted{Broadcast.ArrayConflict} @test Broadcast.broadcasted(+, AD1(rand(3)), AD2(rand(3))) isa Broadcast.Broadcasted{<:Broadcast.AbstractArrayStyle{Any}} - @test @inferred(Base.IteratorSize(Broadcast.broadcasted((1,2,3),a1,zeros(3,3,3)))) === Base.HasShape{3}() + @test @inferred(Base.IteratorSize(Broadcast.broadcasted(+, (1,2,3), a1, zeros(3,3,3)))) === Base.HasShape{3}() # inference on nested bc = Base.broadcasted(+, AD1(randn(3)), AD1(randn(3))) From b27c87eb1d04f0bd001f32ffd134437007170984 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 20 Apr 2023 11:52:01 -0400 Subject: [PATCH 558/775] avoid some more invalidations that are not necessary (#49418) Even if we have a new method that is more specific than the method it is replacing, there still might exist an existing method that is more specific than both which already covers their intersection. An example of this pattern is adding Base.IteratorSize(::Type{<:NewType}) causing invalidations on Base.IteratorSize(::Type) for specializations such as Base.IteratorSize(::Type{<:AbstractString}) even though the intersection of these is fully covered already by Base.IteratorSize(::Type{Union{}}) so our new method would never be selected there. This won't detect ambiguities that already cover this intersection, but that is why we are looking to move away from that pattern towards explicit methods for detection in closer to O(n) instead of O(n^2): #49349. Similarly, for this method, we were unnecessarily dropping it from the MethodTable cache. This is not a significant latency problem (the cache is cheap to rebuild), but it is also easy to avoid in the first place. Refs #49350 --- src/gf.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/gf.c b/src/gf.c index 74457283b2873..23ce8d33c82d2 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1797,6 +1797,22 @@ static int invalidate_mt_cache(jl_typemap_entry_t *oldentry, void *closure0) break; } } + if (intersects && (jl_value_t*)oldentry->sig != mi->specTypes) { + // the entry may point to a widened MethodInstance, in which case it is worthwhile to check if the new method + // actually has any meaningful intersection with the old one + intersects = !jl_has_empty_intersection((jl_value_t*)oldentry->sig, (jl_value_t*)env->newentry->sig); + } + if (intersects && oldentry->guardsigs != jl_emptysvec) { + // similarly, if it already matches an existing guardsigs, this is already safe to keep + size_t i, l; + for (i = 0, l = jl_svec_len(oldentry->guardsigs); i < l; i++) { + // see corresponding code in jl_typemap_entry_assoc_exact + if (jl_subtype((jl_value_t*)env->newentry->sig, jl_svecref(oldentry->guardsigs, i))) { + intersects = 0; + break; + } + } + } if (intersects) { if (_jl_debug_method_invalidation) { jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)mi); @@ -1939,8 +1955,7 @@ enum morespec_options { }; // check if `type` is replacing `m` with an ambiguity here, given other methods in `d` that already match it -// precondition: type is not more specific than `m` -static int is_replacing(jl_value_t *type, jl_method_t *m, jl_method_t *const *d, size_t n, jl_value_t *isect, jl_value_t *isect2, char *morespec) +static int is_replacing(char ambig, jl_value_t *type, jl_method_t *m, jl_method_t *const *d, size_t n, jl_value_t *isect, jl_value_t *isect2, char *morespec) { size_t k; for (k = 0; k < n; k++) { @@ -1953,11 +1968,15 @@ static int is_replacing(jl_value_t *type, jl_method_t *m, jl_method_t *const *d, if (morespec[k] == (char)morespec_is) // not actually shadowing this--m2 will still be better return 0; + // if type is not more specific than m (thus now dominating it) + // then there is a new ambiguity here, // since m2 was also a previous match over isect, - // see if m was also previously dominant over all m2 - if (!jl_type_morespecific(m->sig, m2->sig)) - // m and m2 were previously ambiguous over the full intersection of mi with type, and will still be ambiguous with type + // see if m was previously dominant over all m2 + // or if this was already ambiguous before + if (ambig != morespec_is && !jl_type_morespecific(m->sig, m2->sig)) { + // m and m2 were previously ambiguous over the full intersection of mi with type, and will still be ambiguous with addition of type return 0; + } } return 1; } @@ -2098,7 +2117,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method ambig = jl_type_morespecific(type, m->sig) ? morespec_is : morespec_isnot; // replacing a method--see if this really was the selected method previously // over the intersection (not ambiguous) and the new method will be selected now (morespec_is) - int replaced_dispatch = ambig == morespec_is || is_replacing(type, m, d, n, isect, isect2, morespec); + int replaced_dispatch = is_replacing(ambig, type, m, d, n, isect, isect2, morespec); // found that this specialization dispatch got replaced by m // call invalidate_backedges(&do_nothing_with_codeinst, mi, max_world, "jl_method_table_insert"); // but ignore invoke-type edges @@ -2112,7 +2131,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method int replaced_edge; if (invokeTypes) { // n.b. normally we must have mi.specTypes <: invokeTypes <: m.sig (though it might not strictly hold), so we only need to check the other subtypes - replaced_edge = jl_subtype(invokeTypes, type) && (ambig == morespec_is || is_replacing(type, m, d, n, invokeTypes, NULL, morespec)); + replaced_edge = jl_subtype(invokeTypes, type) && is_replacing(ambig, type, m, d, n, invokeTypes, NULL, morespec); } else { replaced_edge = replaced_dispatch; @@ -2139,7 +2158,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method } if (jl_array_len(oldmi)) { // search mt->cache and leafcache and drop anything that might overlap with the new method - // TODO: keep track of just the `mi` for which shadowing was true (to avoid recomputing that here) + // this is very cheap, so we don't mind being fairly conservative at over-approximating this struct invalidate_mt_env mt_cache_env; mt_cache_env.max_world = max_world; mt_cache_env.shadowed = oldmi; From 499647e95d7c839a984fb76bc39040a41f35f032 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 20 Apr 2023 11:53:14 -0400 Subject: [PATCH 559/775] inference: fix some of the edges, being created from the wrong signature (#49404) Inference should have already made this edge from this item, so we do not want to add another wider edge. Even if this target object contains some invalid code later, inference already showed that does not affect our code-paths, so we don't need or want that wide edge. --- base/compiler/ssair/inlining.jl | 55 ++++++++++++++++----------------- base/compiler/utilities.jl | 12 +------ 2 files changed, 27 insertions(+), 40 deletions(-) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 3b1cb2c46ce6e..6a2f5b5be0c99 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -793,35 +793,38 @@ function rewrite_apply_exprargs!(todo::Vector{Pair{Int,Any}}, return new_argtypes end -function compileable_specialization(match::MethodMatch, effects::Effects, +function compileable_specialization(mi::MethodInstance, effects::Effects, et::InliningEdgeTracker, @nospecialize(info::CallInfo); compilesig_invokes::Bool=true) - if !compilesig_invokes - # If there are unknown typevars, this MethodInstance is illegal to - # :invoke, but we only check for compilesig usually, so check here to - # avoid generating bad code. - # TODO: We could also compute the correct type parameters in the runtime - # and let this go through, but that requires further changes, because - # currently the runtime assumes that a MethodInstance with the appropriate - # sparams is created. + mi_invoke = mi + if compilesig_invokes + method, atype, sparams = mi.def::Method, mi.specTypes, mi.sparam_vals + new_atype = get_compileable_sig(method, atype, sparams) + new_atype === nothing && return nothing + if atype !== new_atype + sp_ = ccall(:jl_type_intersection_with_env, Any, (Any, Any), new_atype, method.sig)::SimpleVector + if sparams === sp_[2]::SimpleVector + mi_invoke = specialize_method(method, new_atype, sparams) + mi_invoke === nothing && return nothing + end + end + else + # If this caller does not want us to optimize calls to use their + # declared compilesig, then it is also likely they would handle sparams + # incorrectly if there were any unknown typevars, so we conservatively return nothing if _any(t->isa(t, TypeVar), match.sparams) return nothing end end - mi = specialize_method(match; compilesig=compilesig_invokes) - mi === nothing && return nothing add_inlining_backedge!(et, mi) - return InvokeCase(mi, effects, info) + return InvokeCase(mi_invoke, effects, info) end -function compileable_specialization(linfo::MethodInstance, effects::Effects, +function compileable_specialization(match::MethodMatch, effects::Effects, et::InliningEdgeTracker, @nospecialize(info::CallInfo); compilesig_invokes::Bool=true) - return compileable_specialization(MethodMatch(linfo.specTypes, - linfo.sparam_vals, linfo.def::Method, false), effects, et, info; compilesig_invokes) + mi = specialize_method(match) + return compileable_specialization(mi, effects, et, info; compilesig_invokes) end -compileable_specialization(result::InferenceResult, args...; kwargs...) = (@nospecialize; - compileable_specialization(result.linfo, args...; kwargs...)) - struct CachedResult src::Any effects::Effects @@ -872,12 +875,12 @@ function resolve_todo(mi::MethodInstance, result::Union{MethodMatch,InferenceRes # the duplicated check might have been done already within `analyze_method!`, but still # we need it here too since we may come here directly using a constant-prop' result if !OptimizationParams(state.interp).inlining || is_stmt_noinline(flag) - return compileable_specialization(result, effects, et, info; + return compileable_specialization(mi, effects, et, info; compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) end src = inlining_policy(state.interp, src, info, flag, mi, argtypes) - src === nothing && return compileable_specialization(result, effects, et, info; + src === nothing && return compileable_specialization(mi, effects, et, info; compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) add_inlining_backedge!(et, mi) @@ -951,15 +954,9 @@ function analyze_method!(match::MethodMatch, argtypes::Vector{Any}, (allow_typevars && !may_have_fcalls(match.method)) || return nothing end - # See if there exists a specialization for this method signature - mi = specialize_method(match; preexisting=true) # Union{Nothing, MethodInstance} - if mi === nothing - et = InliningEdgeTracker(state.et, invokesig) - effects = info_effects(nothing, match, state) - return compileable_specialization(match, effects, et, info; - compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) - end - + # Get the specialization for this method signature + # (later we will decide what to do with it) + mi = specialize_method(match) return resolve_todo(mi, match, argtypes, info, flag, state; invokesig) end diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index bae8ef5bae242..f523d8ad8e810 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -199,20 +199,10 @@ function normalize_typevars(method::Method, @nospecialize(atype), sparams::Simpl end # get a handle to the unique specialization object representing a particular instantiation of a call -function specialize_method(method::Method, @nospecialize(atype), sparams::SimpleVector; preexisting::Bool=false, compilesig::Bool=false) +function specialize_method(method::Method, @nospecialize(atype), sparams::SimpleVector; preexisting::Bool=false) if isa(atype, UnionAll) atype, sparams = normalize_typevars(method, atype, sparams) end - if compilesig - new_atype = get_compileable_sig(method, atype, sparams) - new_atype === nothing && return nothing - if atype !== new_atype - sp_ = ccall(:jl_type_intersection_with_env, Any, (Any, Any), new_atype, method.sig)::SimpleVector - if sparams === sp_[2]::SimpleVector - atype = new_atype - end - end - end if preexisting # check cached specializations # for an existing result stored there From 4f035fa93686d9e9f3f0db145588f159f1bcd4d8 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Thu, 20 Apr 2023 12:18:53 -0400 Subject: [PATCH 560/775] Make clang happy about prototype (#49443) --- src/signals-mach.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/signals-mach.c b/src/signals-mach.c index 2b1da43b71f63..2bb26976b0d61 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -104,7 +104,7 @@ void *mach_segv_listener(void *arg) } -static void allocate_mach_handler() +static void allocate_mach_handler(void) { // ensure KEYMGR_GCC3_DW2_OBJ_LIST is initialized, as this requires malloc // and thus can deadlock when used without first initializing it. From 1f94d2e819d5a7e61f5556af70d49c7f61635b43 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Thu, 20 Apr 2023 12:50:26 -0400 Subject: [PATCH 561/775] Only add big objarray to remset once (#49315) Fixes https://github.com/JuliaLang/julia/issues/49205 --- src/gc.c | 141 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 112 insertions(+), 29 deletions(-) diff --git a/src/gc.c b/src/gc.c index a67783c741b43..e77ee90a34353 100644 --- a/src/gc.c +++ b/src/gc.c @@ -2087,24 +2087,47 @@ STATIC_INLINE void gc_mark_objarray(jl_ptls_t ptls, jl_value_t *obj_parent, jl_v jl_value_t *new_obj; // Decide whether need to chunk objary (void)jl_assume(step > 0); - size_t nobjs = (obj_end - obj_begin) / step; - if (nobjs > MAX_REFS_AT_ONCE) { - jl_gc_chunk_t c = {GC_objary_chunk, obj_parent, obj_begin + step * MAX_REFS_AT_ONCE, - obj_end, NULL, NULL, - step, nptr}; - gc_chunkqueue_push(mq, &c); - obj_end = obj_begin + step * MAX_REFS_AT_ONCE; + if ((nptr & 0x2) == 0x2) { + // pre-scan this object: most of this object should be old, so look for + // the first young object before starting this chunk + // (this also would be valid for young objects, but probably less beneficial) + for (; obj_begin < obj_end; obj_begin += step) { + new_obj = *obj_begin; + if (new_obj != NULL) { + verify_parent2("obj array", obj_parent, obj_begin, "elem(%d)", + gc_slot_to_arrayidx(obj_parent, obj_begin)); + jl_taggedvalue_t *o = jl_astaggedvalue(new_obj); + if (!gc_old(o->header)) + nptr |= 1; + if (!gc_marked(o->header)) + break; + gc_heap_snapshot_record_array_edge(obj_parent, &new_obj); + } + } + } + size_t too_big = (obj_end - obj_begin) / MAX_REFS_AT_ONCE > step; // use this order of operations to avoid idiv + jl_value_t **scan_end = obj_end; + if (too_big) { + scan_end = obj_begin + step * MAX_REFS_AT_ONCE; } - for (; obj_begin < obj_end; obj_begin += step) { + for (; obj_begin < scan_end; obj_begin += step) { new_obj = *obj_begin; if (new_obj != NULL) { verify_parent2("obj array", obj_parent, obj_begin, "elem(%d)", - gc_slot_to_arrayidx(obj_parent, obj_begin)); + gc_slot_to_arrayidx(obj_parent, obj_begin)); gc_try_claim_and_push(mq, new_obj, &nptr); gc_heap_snapshot_record_array_edge(obj_parent, &new_obj); } } - gc_mark_push_remset(ptls, obj_parent, nptr); + if (too_big) { + jl_gc_chunk_t c = {GC_objary_chunk, obj_parent, scan_end, + obj_end, NULL, NULL, + step, nptr}; + gc_chunkqueue_push(mq, &c); + } + else { + gc_mark_push_remset(ptls, obj_parent, nptr); + } } // Mark array with 8bit field descriptors @@ -2116,14 +2139,36 @@ STATIC_INLINE void gc_mark_array8(jl_ptls_t ptls, jl_value_t *ary8_parent, jl_va jl_value_t *new_obj; size_t elsize = ((jl_array_t *)ary8_parent)->elsize / sizeof(jl_value_t *); assert(elsize > 0); - // Decide whether need to chunk ary8 - size_t nrefs = (ary8_end - ary8_begin) / elsize; - if (nrefs > MAX_REFS_AT_ONCE) { - jl_gc_chunk_t c = {GC_ary8_chunk, ary8_parent, ary8_begin + elsize * MAX_REFS_AT_ONCE, - ary8_end, elem_begin, elem_end, - 0, nptr}; - gc_chunkqueue_push(mq, &c); - ary8_end = ary8_begin + elsize * MAX_REFS_AT_ONCE; + // Decide whether need to chunk objary + if ((nptr & 0x2) == 0x2) { + // pre-scan this object: most of this object should be old, so look for + // the first young object before starting this chunk + // (this also would be valid for young objects, but probably less beneficial) + for (; ary8_begin < ary8_end; ary8_begin += elsize) { + int early_end = 0; + for (uint8_t *pindex = elem_begin; pindex < elem_end; pindex++) { + new_obj = ary8_begin[*pindex]; + if (new_obj != NULL) { + verify_parent2("array", ary8_parent, &new_obj, "elem(%d)", + gc_slot_to_arrayidx(ary8_parent, ary8_begin)); + jl_taggedvalue_t *o = jl_astaggedvalue(new_obj); + if (!gc_old(o->header)) + nptr |= 1; + if (!gc_marked(o->header)){ + early_end = 1; + break; + } + gc_heap_snapshot_record_array_edge(ary8_parent, &new_obj); + } + } + if (early_end) + break; + } + } + size_t too_big = (ary8_end - ary8_begin) / MAX_REFS_AT_ONCE > elsize; // use this order of operations to avoid idiv + jl_value_t **scan_end = ary8_end; + if (too_big) { + scan_end = ary8_begin + elsize * MAX_REFS_AT_ONCE; } for (; ary8_begin < ary8_end; ary8_begin += elsize) { for (uint8_t *pindex = elem_begin; pindex < elem_end; pindex++) { @@ -2136,7 +2181,15 @@ STATIC_INLINE void gc_mark_array8(jl_ptls_t ptls, jl_value_t *ary8_parent, jl_va } } } - gc_mark_push_remset(ptls, ary8_parent, nptr); + if (too_big) { + jl_gc_chunk_t c = {GC_objary_chunk, ary8_parent, scan_end, + ary8_end, elem_begin, elem_end, + 0, nptr}; + gc_chunkqueue_push(mq, &c); + } + else { + gc_mark_push_remset(ptls, ary8_parent, nptr); + } } // Mark array with 16bit field descriptors @@ -2148,16 +2201,38 @@ STATIC_INLINE void gc_mark_array16(jl_ptls_t ptls, jl_value_t *ary16_parent, jl_ jl_value_t *new_obj; size_t elsize = ((jl_array_t *)ary16_parent)->elsize / sizeof(jl_value_t *); assert(elsize > 0); - // Decide whether need to chunk ary16 - size_t nrefs = (ary16_end - ary16_begin) / elsize; - if (nrefs > MAX_REFS_AT_ONCE) { - jl_gc_chunk_t c = {GC_ary16_chunk, ary16_parent, ary16_begin + elsize * MAX_REFS_AT_ONCE, - ary16_end, elem_begin, elem_end, - 0, nptr}; - gc_chunkqueue_push(mq, &c); - ary16_end = ary16_begin + elsize * MAX_REFS_AT_ONCE; + // Decide whether need to chunk objary + if ((nptr & 0x2) == 0x2) { + // pre-scan this object: most of this object should be old, so look for + // the first young object before starting this chunk + // (this also would be valid for young objects, but probably less beneficial) + for (; ary16_begin < ary16_end; ary16_begin += elsize) { + int early_end = 0; + for (uint16_t *pindex = elem_begin; pindex < elem_end; pindex++) { + new_obj = ary16_begin[*pindex]; + if (new_obj != NULL) { + verify_parent2("array", ary16_parent, &new_obj, "elem(%d)", + gc_slot_to_arrayidx(ary16_parent, ary16_begin)); + jl_taggedvalue_t *o = jl_astaggedvalue(new_obj); + if (!gc_old(o->header)) + nptr |= 1; + if (!gc_marked(o->header)){ + early_end = 1; + break; + } + gc_heap_snapshot_record_array_edge(ary16_parent, &new_obj); + } + } + if (early_end) + break; + } + } + size_t too_big = (ary16_end - ary16_begin) / MAX_REFS_AT_ONCE > elsize; // use this order of operations to avoid idiv + jl_value_t **scan_end = ary16_end; + if (too_big) { + scan_end = ary16_begin + elsize * MAX_REFS_AT_ONCE; } - for (; ary16_begin < ary16_end; ary16_begin += elsize) { + for (; ary16_begin < scan_end; ary16_begin += elsize) { for (uint16_t *pindex = elem_begin; pindex < elem_end; pindex++) { new_obj = ary16_begin[*pindex]; if (new_obj != NULL) { @@ -2168,7 +2243,15 @@ STATIC_INLINE void gc_mark_array16(jl_ptls_t ptls, jl_value_t *ary16_parent, jl_ } } } - gc_mark_push_remset(ptls, ary16_parent, nptr); + if (too_big) { + jl_gc_chunk_t c = {GC_objary_chunk, ary16_parent, scan_end, + ary16_end, elem_begin, elem_end, + elsize, nptr}; + gc_chunkqueue_push(mq, &c); + } + else { + gc_mark_push_remset(ptls, ary16_parent, nptr); + } } // Mark chunk of large array From f84fb5b26f87169c00e4bdc3c4db050772974023 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Thu, 20 Apr 2023 12:52:20 -0400 Subject: [PATCH 562/775] Save a couple loads/stores in sweep pages (#49263) We did a load/store on every iteration. Keep a temporary in a register instead. It's a very small difference but it's visible in vtune. --- src/gc-debug.c | 4 ++-- src/gc.c | 31 +++++++++++++++++-------------- src/gc.h | 3 ++- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/gc-debug.c b/src/gc-debug.c index a233b18d7dcfc..2350a21958815 100644 --- a/src/gc-debug.c +++ b/src/gc-debug.c @@ -563,11 +563,11 @@ JL_NO_ASAN static void gc_scrub_range(char *low, char *high) // Find the age bit char *page_begin = gc_page_data(tag) + GC_PAGE_OFFSET; int obj_id = (((char*)tag) - page_begin) / osize; - uint8_t *ages = pg->ages + obj_id / 8; + uint32_t *ages = pg->ages + obj_id / 32; // Force this to be a young object to save some memory // (especially on 32bit where it's more likely to have pointer-like // bit patterns) - *ages &= ~(1 << (obj_id % 8)); + *ages &= ~(1 << (obj_id % 32)); memset(tag, 0xff, osize); // set mark to GC_MARKED (young and marked) tag->bits.gc = GC_MARKED; diff --git a/src/gc.c b/src/gc.c index e77ee90a34353..b1e29ca149810 100644 --- a/src/gc.c +++ b/src/gc.c @@ -976,8 +976,8 @@ STATIC_INLINE void gc_setmark_pool_(jl_ptls_t ptls, jl_taggedvalue_t *o, page->has_young = 1; char *page_begin = gc_page_data(o) + GC_PAGE_OFFSET; int obj_id = (((char*)o) - page_begin) / page->osize; - uint8_t *ages = page->ages + obj_id / 8; - jl_atomic_fetch_and_relaxed((_Atomic(uint8_t)*)ages, ~(1 << (obj_id % 8))); + uint32_t *ages = page->ages + obj_id / 32; + jl_atomic_fetch_and_relaxed((_Atomic(uint32_t)*)ages, ~(1 << (obj_id % 32))); } } objprofile_count(jl_typeof(jl_valueof(o)), @@ -1406,7 +1406,7 @@ static NOINLINE jl_taggedvalue_t *add_page(jl_gc_pool_t *p) JL_NOTSAFEPOINT jl_ptls_t ptls = jl_current_task->ptls; jl_gc_pagemeta_t *pg = jl_gc_alloc_page(); pg->osize = p->osize; - pg->ages = (uint8_t*)malloc_s(GC_PAGE_SZ / 8 / p->osize + 1); + pg->ages = (uint32_t*)malloc_s(LLT_ALIGN(GC_PAGE_SZ / 8 / p->osize + 1, sizeof(uint32_t))); pg->thread_n = ptls->tid; jl_taggedvalue_t *fl = reset_page(ptls, p, pg, NULL); p->newpages = fl; @@ -1506,7 +1506,7 @@ int64_t lazy_freed_pages = 0; static jl_taggedvalue_t **sweep_page(jl_gc_pool_t *p, jl_gc_pagemeta_t *pg, jl_taggedvalue_t **pfl, int sweep_full, int osize) JL_NOTSAFEPOINT { char *data = pg->data; - uint8_t *ages = pg->ages; + uint32_t *ages = pg->ages; jl_taggedvalue_t *v = (jl_taggedvalue_t*)(data + GC_PAGE_OFFSET); char *lim = (char*)v + GC_PAGE_SZ - GC_PAGE_OFFSET - osize; size_t old_nfree = pg->nfree; @@ -1557,18 +1557,25 @@ static jl_taggedvalue_t **sweep_page(jl_gc_pool_t *p, jl_gc_pagemeta_t *pg, jl_t int16_t prev_nold = 0; int pg_nfree = 0; jl_taggedvalue_t **pfl_begin = NULL; - uint8_t msk = 1; // mask for the age bit in the current age byte + uint32_t msk = 1; // mask for the age bit in the current age byte + uint32_t age = *ages; while ((char*)v <= lim) { + if (!msk) { + msk = 1; + *ages = age; + ages++; + age = *ages; + } int bits = v->bits.gc; if (!gc_marked(bits)) { *pfl = v; pfl = &v->next; pfl_begin = pfl_begin ? pfl_begin : pfl; pg_nfree++; - *ages &= ~msk; + age &= ~msk; } else { // marked young or old - if (*ages & msk || bits == GC_OLD_MARKED) { // old enough + if (age & msk || bits == GC_OLD_MARKED) { // old enough // `!age && bits == GC_OLD_MARKED` is possible for // non-first-class objects like array buffers // (they may get promoted by jl_gc_wb_buf for example, @@ -1584,17 +1591,13 @@ static jl_taggedvalue_t **sweep_page(jl_gc_pool_t *p, jl_gc_pagemeta_t *pg, jl_t has_young = 1; } has_marked |= gc_marked(bits); - *ages |= msk; + age |= msk; freedall = 0; } v = (jl_taggedvalue_t*)((char*)v + osize); msk <<= 1; - if (!msk) { - msk = 1; - ages++; - } } - + *ages = age; assert(!freedall); pg->has_marked = has_marked; pg->has_young = has_young; @@ -4017,7 +4020,7 @@ JL_DLLEXPORT jl_value_t *jl_gc_internal_obj_base_ptr(void *p) goto valid_object; // We know now that the age bit reflects liveness status during // the last sweep and that the cell has not been reused since. - if (!(meta->ages[obj_id / 8] & (1 << (obj_id % 8)))) { + if (!(meta->ages[obj_id / 32] & (1 << (obj_id % 32)))) { return NULL; } // Not a freelist entry, therefore a valid object. diff --git a/src/gc.h b/src/gc.h index e0510d9bc3917..3961aeecada8c 100644 --- a/src/gc.h +++ b/src/gc.h @@ -9,6 +9,7 @@ #ifndef JL_GC_H #define JL_GC_H +#include #include #include #include @@ -170,7 +171,7 @@ typedef struct { uint16_t fl_end_offset; // offset of last free object in this page uint16_t thread_n; // thread id of the heap that owns this page char *data; - uint8_t *ages; + uint32_t *ages; } jl_gc_pagemeta_t; // Page layout: From 186634effbc08708ab29d3201b94d1d63d1ddd3a Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 20 Apr 2023 21:12:43 -0400 Subject: [PATCH 563/775] Fix inference of one-arg `return_type` method (#49407) * Fix inference of one-arg `return_type` method `Core.Compiler.return_type` has two methods: - return_type(f, args::Type{<:Tuple}) - return_type(args::Type{<:Tuple}) Our inference code was only catching the first one. Expand it to support both. * Update test/compiler/inference.jl --------- Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> --- base/compiler/tfuncs.jl | 114 ++++++++++++++++++++----------------- test/compiler/inference.jl | 5 ++ 2 files changed, 68 insertions(+), 51 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index cb75a8e769712..cba74a3e658ca 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -2608,60 +2608,72 @@ end # since abstract_call_gf_by_type is a very inaccurate model of _method and of typeinf_type, # while this assumes that it is an absolutely precise and accurate and exact model of both function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, si::StmtInfo, sv::AbsIntState) + UNKNOWN = CallMeta(Type, EFFECTS_THROWS, NoCallInfo()) + if !(2 <= length(argtypes) <= 3) + return UNKNOWN + end + + tt = widenslotwrapper(argtypes[end]) + if !isa(tt, Const) && !(isType(tt) && !has_free_typevars(tt)) + return UNKNOWN + end + + af_argtype = isa(tt, Const) ? tt.val : (tt::DataType).parameters[1] + if !isa(af_argtype, DataType) || !(af_argtype <: Tuple) + return UNKNOWN + end + if length(argtypes) == 3 - tt = widenslotwrapper(argtypes[3]) - if isa(tt, Const) || (isType(tt) && !has_free_typevars(tt)) - aft = widenslotwrapper(argtypes[2]) - if isa(aft, Const) || (isType(aft) && !has_free_typevars(aft)) || - (isconcretetype(aft) && !(aft <: Builtin)) - af_argtype = isa(tt, Const) ? tt.val : (tt::DataType).parameters[1] - if isa(af_argtype, DataType) && af_argtype <: Tuple - argtypes_vec = Any[aft, af_argtype.parameters...] - if contains_is(argtypes_vec, Union{}) - return CallMeta(Const(Union{}), EFFECTS_TOTAL, NoCallInfo()) - end - # - # Run the abstract_call without restricting abstract call - # sites. Otherwise, our behavior model of abstract_call - # below will be wrong. - if isa(sv, InferenceState) - old_restrict = sv.restrict_abstract_call_sites - sv.restrict_abstract_call_sites = false - call = abstract_call(interp, ArgInfo(nothing, argtypes_vec), si, sv, -1) - sv.restrict_abstract_call_sites = old_restrict - else - call = abstract_call(interp, ArgInfo(nothing, argtypes_vec), si, sv, -1) - end - info = verbose_stmt_info(interp) ? MethodResultPure(ReturnTypeCallInfo(call.info)) : MethodResultPure() - rt = widenslotwrapper(call.rt) - if isa(rt, Const) - # output was computed to be constant - return CallMeta(Const(typeof(rt.val)), EFFECTS_TOTAL, info) - end - rt = widenconst(rt) - if rt === Bottom || (isconcretetype(rt) && !iskindtype(rt)) - # output cannot be improved so it is known for certain - return CallMeta(Const(rt), EFFECTS_TOTAL, info) - elseif isa(sv, InferenceState) && !isempty(sv.pclimitations) - # conservatively express uncertainty of this result - # in two ways: both as being a subtype of this, and - # because of LimitedAccuracy causes - return CallMeta(Type{<:rt}, EFFECTS_TOTAL, info) - elseif (isa(tt, Const) || isconstType(tt)) && - (isa(aft, Const) || isconstType(aft)) - # input arguments were known for certain - # XXX: this doesn't imply we know anything about rt - return CallMeta(Const(rt), EFFECTS_TOTAL, info) - elseif isType(rt) - return CallMeta(Type{rt}, EFFECTS_TOTAL, info) - else - return CallMeta(Type{<:rt}, EFFECTS_TOTAL, info) - end - end - end + aft = widenslotwrapper(argtypes[2]) + if !isa(aft, Const) && !(isType(aft) && !has_free_typevars(aft)) && + !(isconcretetype(aft) && !(aft <: Builtin)) + return UNKNOWN end + argtypes_vec = Any[aft, af_argtype.parameters...] + else + argtypes_vec = Any[af_argtype.parameters...] + end + + if contains_is(argtypes_vec, Union{}) + return CallMeta(Const(Union{}), EFFECTS_TOTAL, NoCallInfo()) + end + + # Run the abstract_call without restricting abstract call + # sites. Otherwise, our behavior model of abstract_call + # below will be wrong. + if isa(sv, InferenceState) + old_restrict = sv.restrict_abstract_call_sites + sv.restrict_abstract_call_sites = false + call = abstract_call(interp, ArgInfo(nothing, argtypes_vec), si, sv, -1) + sv.restrict_abstract_call_sites = old_restrict + else + call = abstract_call(interp, ArgInfo(nothing, argtypes_vec), si, sv, -1) + end + info = verbose_stmt_info(interp) ? MethodResultPure(ReturnTypeCallInfo(call.info)) : MethodResultPure() + rt = widenslotwrapper(call.rt) + if isa(rt, Const) + # output was computed to be constant + return CallMeta(Const(typeof(rt.val)), EFFECTS_TOTAL, info) + end + rt = widenconst(rt) + if rt === Bottom || (isconcretetype(rt) && !iskindtype(rt)) + # output cannot be improved so it is known for certain + return CallMeta(Const(rt), EFFECTS_TOTAL, info) + elseif isa(sv, InferenceState) && !isempty(sv.pclimitations) + # conservatively express uncertainty of this result + # in two ways: both as being a subtype of this, and + # because of LimitedAccuracy causes + return CallMeta(Type{<:rt}, EFFECTS_TOTAL, info) + elseif (isa(tt, Const) || isconstType(tt)) && + (isa(aft, Const) || isconstType(aft)) + # input arguments were known for certain + # XXX: this doesn't imply we know anything about rt + return CallMeta(Const(rt), EFFECTS_TOTAL, info) + elseif isType(rt) + return CallMeta(Type{rt}, EFFECTS_TOTAL, info) + else + return CallMeta(Type{<:rt}, EFFECTS_TOTAL, info) end - return CallMeta(Type, EFFECTS_THROWS, NoCallInfo()) end # a simplified model of abstract_call_gf_by_type for applicable diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 1ee077f6ca3ef..8e23ca2760241 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -4854,3 +4854,8 @@ end |> only === Tuple{Int,Symbol} return T end end) == Type{Nothing} + +# Test that Core.Compiler.return_type inference works for the 1-arg version +@test Base.return_types() do + Core.Compiler.return_type(Tuple{typeof(+), Int, Int}) +end |> only == Type{Int} From ecc3751e52165c111411c7bd3950cefebb47a097 Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Fri, 21 Apr 2023 14:54:54 +0000 Subject: [PATCH 564/775] Add ITTAPI hooks to jl_mutex_wait (#49434) * Add ITT synchronization profiling calls * Init mutexes with name * Functions -> macros * remove env checking * error when both ITTAPI and DTrace are requested * Update Make.inc * Move timing functions to timing.h --------- Co-authored-by: Valentin Churavy Co-authored-by: Valentin Churavy --- src/Makefile | 4 ++-- src/datatype.c | 2 +- src/gc.c | 4 ++-- src/init.c | 14 +++++++++++++- src/jl_uv.c | 2 +- src/julia_locks.h | 8 ++++---- src/method.c | 2 +- src/module.c | 2 +- src/runtime_ccall.cpp | 2 +- src/staticdata.c | 2 +- src/staticdata_utils.c | 2 +- src/threading.c | 17 +++++++++++++++++ src/timing.h | 35 ++++++++++++++++++++++++++++++++++- 13 files changed, 79 insertions(+), 17 deletions(-) diff --git a/src/Makefile b/src/Makefile index 7159e4f210b59..00e3fa18044d0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -157,8 +157,8 @@ LIBJULIA_PATH_REL := libjulia endif COMMON_LIBPATHS := -L$(build_libdir) -L$(build_shlibdir) -RT_LIBS := $(WHOLE_ARCHIVE) $(LIBUV) $(WHOLE_ARCHIVE) $(LIBUTF8PROC) $(NO_WHOLE_ARCHIVE) $(LIBUNWIND) $(RT_LLVMLINK) $(OSLIBS) $(LIBTRACYCLIENT) -CG_LIBS := $(LIBUNWIND) $(CG_LLVMLINK) $(OSLIBS) $(LIBTRACYCLIENT) +RT_LIBS := $(WHOLE_ARCHIVE) $(LIBUV) $(WHOLE_ARCHIVE) $(LIBUTF8PROC) $(NO_WHOLE_ARCHIVE) $(LIBUNWIND) $(RT_LLVMLINK) $(OSLIBS) $(LIBTRACYCLIENT) $(LIBITTAPI) +CG_LIBS := $(LIBUNWIND) $(CG_LLVMLINK) $(OSLIBS) $(LIBTRACYCLIENT) $(LIBITTAPI) RT_DEBUG_LIBS := $(COMMON_LIBPATHS) $(WHOLE_ARCHIVE) $(BUILDDIR)/flisp/libflisp-debug.a $(WHOLE_ARCHIVE) $(BUILDDIR)/support/libsupport-debug.a -ljulia-debug $(RT_LIBS) CG_DEBUG_LIBS := $(COMMON_LIBPATHS) $(CG_LIBS) -ljulia-debug -ljulia-internal-debug RT_RELEASE_LIBS := $(COMMON_LIBPATHS) $(WHOLE_ARCHIVE) $(BUILDDIR)/flisp/libflisp.a $(WHOLE_ARCHIVE) $(BUILDDIR)/support/libsupport.a -ljulia $(RT_LIBS) diff --git a/src/datatype.c b/src/datatype.c index 894beeaa1b7e6..db334c7343d80 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -53,7 +53,7 @@ JL_DLLEXPORT jl_methtable_t *jl_new_method_table(jl_sym_t *name, jl_module_t *mo jl_atomic_store_relaxed(&mt->cache, jl_nothing); jl_atomic_store_relaxed(&mt->max_args, 0); mt->backedges = NULL; - JL_MUTEX_INIT(&mt->writelock); + JL_MUTEX_INIT(&mt->writelock, "methodtable->writelock"); mt->offs = 0; mt->frozen = 0; return mt; diff --git a/src/gc.c b/src/gc.c index b1e29ca149810..8653b8a05851f 100644 --- a/src/gc.c +++ b/src/gc.c @@ -3485,8 +3485,8 @@ void jl_init_thread_heap(jl_ptls_t ptls) void jl_gc_init(void) { - JL_MUTEX_INIT(&heapsnapshot_lock); - JL_MUTEX_INIT(&finalizers_lock); + JL_MUTEX_INIT(&heapsnapshot_lock, "heapsnapshot_lock"); + JL_MUTEX_INIT(&finalizers_lock, "finalizers_lock"); uv_mutex_init(&gc_cache_lock); uv_mutex_init(&gc_perm_lock); diff --git a/src/init.c b/src/init.c index efa2d51110548..b0039bfe3e311 100644 --- a/src/init.c +++ b/src/init.c @@ -705,6 +705,9 @@ static void jl_set_io_wait(int v) } extern jl_mutex_t jl_modules_mutex; +extern jl_mutex_t precomp_statement_out_lock; +extern jl_mutex_t newly_inferred_mutex; +extern jl_mutex_t global_roots_lock; static void restore_fp_env(void) { @@ -717,6 +720,15 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_ JL_DLLEXPORT int jl_default_debug_info_kind; +static void init_global_mutexes(void) { + JL_MUTEX_INIT(&jl_modules_mutex, "jl_modules_mutex"); + JL_MUTEX_INIT(&precomp_statement_out_lock, "precomp_statement_out_lock"); + JL_MUTEX_INIT(&newly_inferred_mutex, "newly_inferred_mutex"); + JL_MUTEX_INIT(&global_roots_lock, "global_roots_lock"); + JL_MUTEX_INIT(&jl_codegen_lock, "jl_codegen_lock"); + JL_MUTEX_INIT(&typecache_lock, "typecache_lock"); +} + JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) { // initialize many things, in no particular order @@ -746,7 +758,7 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) jl_safepoint_init(); jl_page_size = jl_getpagesize(); htable_new(&jl_current_modules, 0); - JL_MUTEX_INIT(&jl_modules_mutex); + init_global_mutexes(); jl_precompile_toplevel_module = NULL; ios_set_io_wait_func = jl_set_io_wait; jl_io_loop = uv_default_loop(); // this loop will internal events (spawning process etc.), diff --git a/src/jl_uv.c b/src/jl_uv.c index b34c3f51c6766..281dd798dbb36 100644 --- a/src/jl_uv.c +++ b/src/jl_uv.c @@ -112,7 +112,7 @@ void jl_init_uv(void) { uv_async_init(jl_io_loop, &signal_async, jl_signal_async_cb); uv_unref((uv_handle_t*)&signal_async); - JL_MUTEX_INIT(&jl_uv_mutex); // a file-scope initializer can be used instead + JL_MUTEX_INIT(&jl_uv_mutex, "jl_uv_mutex"); // a file-scope initializer can be used instead } _Atomic(int) jl_uv_n_waiters = 0; diff --git a/src/julia_locks.h b/src/julia_locks.h index 7db37b03f0bed..2fbaffa5e47c3 100644 --- a/src/julia_locks.h +++ b/src/julia_locks.h @@ -17,6 +17,7 @@ extern "C" { // The JL_LOCK* and JL_UNLOCK* macros are no-op for non-threading build // while the jl_mutex_* functions are always locking and unlocking the locks. +JL_DLLEXPORT void _jl_mutex_init(jl_mutex_t *lock, const char *name) JL_NOTSAFEPOINT; JL_DLLEXPORT void _jl_mutex_wait(jl_task_t *self, jl_mutex_t *lock, int safepoint); JL_DLLEXPORT void _jl_mutex_lock(jl_task_t *self, jl_mutex_t *lock); JL_DLLEXPORT int _jl_mutex_trylock_nogc(jl_task_t *self, jl_mutex_t *lock) JL_NOTSAFEPOINT; @@ -86,13 +87,12 @@ static inline void jl_mutex_unlock_nogc(jl_mutex_t *lock) JL_NOTSAFEPOINT JL_NOT _jl_mutex_unlock_nogc(lock); } -static inline void jl_mutex_init(jl_mutex_t *lock) JL_NOTSAFEPOINT +static inline void jl_mutex_init(jl_mutex_t *lock, const char *name) JL_NOTSAFEPOINT { - jl_atomic_store_relaxed(&lock->owner, (jl_task_t*)NULL); - lock->count = 0; + _jl_mutex_init(lock, name); } -#define JL_MUTEX_INIT(m) jl_mutex_init(m) +#define JL_MUTEX_INIT(m, name) jl_mutex_init(m, name) #define JL_LOCK(m) jl_mutex_lock(m) #define JL_UNLOCK(m) jl_mutex_unlock(m) #define JL_LOCK_NOGC(m) jl_mutex_lock_nogc(m) diff --git a/src/method.c b/src/method.c index 0e67ef347dbd2..30d77f70ab37c 100644 --- a/src/method.c +++ b/src/method.c @@ -812,7 +812,7 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t *module) m->constprop = 0; m->purity.bits = 0; m->max_varargs = UINT8_MAX; - JL_MUTEX_INIT(&m->writelock); + JL_MUTEX_INIT(&m->writelock, "method->writelock"); return m; } diff --git a/src/module.c b/src/module.c index 9a8285ad003f6..a232c6e9f8367 100644 --- a/src/module.c +++ b/src/module.c @@ -36,7 +36,7 @@ JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, ui m->max_methods = -1; m->hash = parent == NULL ? bitmix(name->hash, jl_module_type->hash) : bitmix(name->hash, parent->hash); - JL_MUTEX_INIT(&m->lock); + JL_MUTEX_INIT(&m->lock, "module->lock"); jl_atomic_store_relaxed(&m->bindings, jl_emptysvec); jl_atomic_store_relaxed(&m->bindingkeyset, (jl_array_t*)jl_an_empty_vec_any); arraylist_new(&m->usings, 0); diff --git a/src/runtime_ccall.cpp b/src/runtime_ccall.cpp index e3543c9f62656..6a1f11e32c929 100644 --- a/src/runtime_ccall.cpp +++ b/src/runtime_ccall.cpp @@ -362,6 +362,6 @@ JL_GCC_IGNORE_STOP void jl_init_runtime_ccall(void) { - JL_MUTEX_INIT(&libmap_lock); + JL_MUTEX_INIT(&libmap_lock, "libmap_lock"); uv_mutex_init(&trampoline_lock); } diff --git a/src/staticdata.c b/src/staticdata.c index 5c6da3f200edb..b5d3aecaeb96e 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -2127,7 +2127,7 @@ static void jl_strip_all_codeinfos(void) // --- entry points --- jl_array_t *jl_global_roots_table; -static jl_mutex_t global_roots_lock; +jl_mutex_t global_roots_lock; JL_DLLEXPORT int jl_is_globally_rooted(jl_value_t *val JL_MAYBE_UNROOTED) JL_NOTSAFEPOINT { diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index e01ba40c63aed..08870a5df70ec 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -85,7 +85,7 @@ static uint64_t jl_worklist_key(jl_array_t *worklist) JL_NOTSAFEPOINT static jl_array_t *newly_inferred JL_GLOBALLY_ROOTED /*FIXME*/; // Mutex for newly_inferred -static jl_mutex_t newly_inferred_mutex; +jl_mutex_t newly_inferred_mutex; // Register array of newly-inferred MethodInstances // This gets called as the first step of Base.include_package_for_output diff --git a/src/threading.c b/src/threading.c index f909f41ac5c64..76c21496927a8 100644 --- a/src/threading.c +++ b/src/threading.c @@ -10,6 +10,10 @@ #include "julia_internal.h" #include "julia_assert.h" +#ifdef USE_ITTAPI +#include "ittapi/ittnotify.h" +#endif + // Ref https://www.uclibc.org/docs/tls.pdf // For variant 1 JL_ELF_TLS_INIT_SIZE is the size of the thread control block (TCB) // For variant 2 JL_ELF_TLS_INIT_SIZE is 0 @@ -724,6 +728,15 @@ JL_DLLEXPORT void jl_exit_threaded_region(void) } } +// Profiling stubs + +void _jl_mutex_init(jl_mutex_t *lock, const char *name) JL_NOTSAFEPOINT +{ + jl_atomic_store_relaxed(&lock->owner, (jl_task_t*)NULL); + lock->count = 0; + jl_profile_lock_init(lock, name); +} + void _jl_mutex_wait(jl_task_t *self, jl_mutex_t *lock, int safepoint) { jl_task_t *owner = jl_atomic_load_relaxed(&lock->owner); @@ -731,9 +744,11 @@ void _jl_mutex_wait(jl_task_t *self, jl_mutex_t *lock, int safepoint) lock->count++; return; } + jl_profile_lock_start_wait(lock); while (1) { if (owner == NULL && jl_atomic_cmpswap(&lock->owner, &owner, self)) { lock->count = 1; + jl_profile_lock_acquired(lock); return; } if (safepoint) { @@ -809,6 +824,7 @@ void _jl_mutex_unlock_nogc(jl_mutex_t *lock) assert(jl_atomic_load_relaxed(&lock->owner) == jl_current_task && "Unlocking a lock in a different thread."); if (--lock->count == 0) { + jl_profile_lock_release_start(lock); jl_atomic_store_release(&lock->owner, (jl_task_t*)NULL); jl_cpu_wake(); if (jl_running_under_rr(0)) { @@ -817,6 +833,7 @@ void _jl_mutex_unlock_nogc(jl_mutex_t *lock) uv_cond_broadcast(&cond); uv_mutex_unlock(&tls_lock); } + jl_profile_lock_release_end(lock); } #endif } diff --git a/src/timing.h b/src/timing.h index d6e4c6d80ab63..de8d980c357fb 100644 --- a/src/timing.h +++ b/src/timing.h @@ -43,7 +43,7 @@ extern uint32_t jl_timing_print_limit; #define HAVE_TIMING_SUPPORT #endif -#if defined( USE_TRACY ) || defined( USE_TIMING_COUNTS ) +#if defined( USE_TRACY ) || defined( USE_ITTAPI ) || defined( USE_TIMING_COUNTS ) #define ENABLE_TIMINGS #endif @@ -61,6 +61,12 @@ extern uint32_t jl_timing_print_limit; #define jl_timing_block_exit_task(ct, ptls) ((jl_timing_block_t *)NULL) #define jl_pop_timing_block(blk) +#define jl_profile_lock_init(lock, name) +#define jl_profile_lock_start_wait(lock) +#define jl_profile_lock_acquired(lock) +#define jl_profile_lock_release_start(lock) +#define jl_profile_lock_release_end(lock) + #else #include "julia_assert.h" @@ -68,6 +74,10 @@ extern uint32_t jl_timing_print_limit; #include "tracy/TracyC.h" #endif +#ifdef USE_ITTAPI +#include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -314,6 +324,29 @@ struct jl_timing_suspend_cpp_t { _jl_timing_suspend_ctor(&__timing_suspend, #subsystem, ct) #endif +// Locking profiling +static inline void jl_profile_lock_init(jl_mutex_t *lock, const char *name) { +#ifdef USE_ITTAPI + __itt_sync_create(lock, "jl_mutex_t", name, __itt_attr_mutex); +#endif +} +static inline void jl_profile_lock_start_wait(jl_mutex_t *lock) { +#ifdef USE_ITTAPI + __itt_sync_prepare(lock); +#endif +} +static inline void jl_profile_lock_acquired(jl_mutex_t *lock) { +#ifdef USE_ITTAPI + __itt_sync_acquired(lock); +#endif +} +static inline void jl_profile_lock_release_start(jl_mutex_t *lock) { +#ifdef USE_ITTAPI + __itt_sync_releasing(lock); +#endif +} +static inline void jl_profile_lock_release_end(jl_mutex_t *lock) {} + #endif #endif From bb83df1d61fb649efd155a1bf32a2f436aecced9 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Fri, 21 Apr 2023 17:34:04 +0200 Subject: [PATCH 565/775] Make generic cholesky throw on non-psd input (#49417) --- stdlib/LinearAlgebra/src/cholesky.jl | 9 +++++---- stdlib/LinearAlgebra/test/cholesky.jl | 29 +++++++++++++++------------ 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/stdlib/LinearAlgebra/src/cholesky.jl b/stdlib/LinearAlgebra/src/cholesky.jl index 560c29cf89508..82f138db7d7b9 100644 --- a/stdlib/LinearAlgebra/src/cholesky.jl +++ b/stdlib/LinearAlgebra/src/cholesky.jl @@ -206,7 +206,7 @@ function _chol!(A::AbstractMatrix, ::Type{UpperTriangular}) A[k,k] = Akk Akk, info = _chol!(Akk, UpperTriangular) if info != 0 - return UpperTriangular(A), info + return UpperTriangular(A), convert(BlasInt, k) end A[k,k] = Akk AkkInv = inv(copy(Akk')) @@ -233,7 +233,7 @@ function _chol!(A::AbstractMatrix, ::Type{LowerTriangular}) A[k,k] = Akk Akk, info = _chol!(Akk, LowerTriangular) if info != 0 - return LowerTriangular(A), info + return LowerTriangular(A), convert(BlasInt, k) end A[k,k] = Akk AkkInv = inv(Akk) @@ -251,11 +251,12 @@ function _chol!(A::AbstractMatrix, ::Type{LowerTriangular}) end ## Numbers -function _chol!(x::Number, uplo) +function _chol!(x::Number, _) rx = real(x) + iszero(rx) && return (rx, convert(BlasInt, 1)) rxr = sqrt(abs(rx)) rval = convert(promote_type(typeof(x), typeof(rxr)), rxr) - rx == abs(x) ? (rval, convert(BlasInt, 0)) : (rval, convert(BlasInt, 1)) + return (rval, convert(BlasInt, rx != abs(x))) end ## for StridedMatrices, check that matrix is symmetric/Hermitian diff --git a/stdlib/LinearAlgebra/test/cholesky.jl b/stdlib/LinearAlgebra/test/cholesky.jl index a3008a236df7b..a795eb8d44a03 100644 --- a/stdlib/LinearAlgebra/test/cholesky.jl +++ b/stdlib/LinearAlgebra/test/cholesky.jl @@ -260,11 +260,12 @@ end end end -@testset "behavior for non-positive definite matrices" for T in (Float64, ComplexF64) +@testset "behavior for non-positive definite matrices" for T in (Float64, ComplexF64, BigFloat) A = T[1 2; 2 1] B = T[1 2; 0 1] + C = T[2 0; 0 0] # check = (true|false) - for M in (A, Hermitian(A), B) + for M in (A, Hermitian(A), B, C) @test_throws PosDefException cholesky(M) @test_throws PosDefException cholesky!(copy(M)) @test_throws PosDefException cholesky(M; check = true) @@ -272,17 +273,19 @@ end @test !LinearAlgebra.issuccess(cholesky(M; check = false)) @test !LinearAlgebra.issuccess(cholesky!(copy(M); check = false)) end - for M in (A, Hermitian(A), B) - @test_throws RankDeficientException cholesky(M, RowMaximum()) - @test_throws RankDeficientException cholesky!(copy(M), RowMaximum()) - @test_throws RankDeficientException cholesky(M, RowMaximum(); check = true) - @test_throws RankDeficientException cholesky!(copy(M), RowMaximum(); check = true) - @test !LinearAlgebra.issuccess(cholesky(M, RowMaximum(); check = false)) - @test !LinearAlgebra.issuccess(cholesky!(copy(M), RowMaximum(); check = false)) - C = cholesky(M, RowMaximum(); check = false) - @test_throws RankDeficientException chkfullrank(C) - C = cholesky!(copy(M), RowMaximum(); check = false) - @test_throws RankDeficientException chkfullrank(C) + if T !== BigFloat # generic pivoted cholesky is not implemented + for M in (A, Hermitian(A), B) + @test_throws RankDeficientException cholesky(M, RowMaximum()) + @test_throws RankDeficientException cholesky!(copy(M), RowMaximum()) + @test_throws RankDeficientException cholesky(M, RowMaximum(); check = true) + @test_throws RankDeficientException cholesky!(copy(M), RowMaximum(); check = true) + @test !LinearAlgebra.issuccess(cholesky(M, RowMaximum(); check = false)) + @test !LinearAlgebra.issuccess(cholesky!(copy(M), RowMaximum(); check = false)) + C = cholesky(M, RowMaximum(); check = false) + @test_throws RankDeficientException chkfullrank(C) + C = cholesky!(copy(M), RowMaximum(); check = false) + @test_throws RankDeficientException chkfullrank(C) + end end @test !isposdef(A) str = sprint((io, x) -> show(io, "text/plain", x), cholesky(A; check = false)) From 23a5b04a01a7429442b80931cdc36b5410e33f89 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Fri, 21 Apr 2023 15:20:31 -0500 Subject: [PATCH 566/775] Add missing entry to invalidation log (#49449) * Add missing entry to invalidation log Addresses https://github.com/timholy/SnoopCompile.jl/issues/357#issuecomment-1516228631 using the observation in the following comment. Co-authored-by: Jameson Nash * Fix indentation --------- Co-authored-by: Jameson Nash --- src/staticdata_utils.c | 20 ++++++++++---------- test/precompile.jl | 10 +++++++++- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 08870a5df70ec..a5413cb96cf16 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -1022,18 +1022,18 @@ static int jl_verify_graph_edge(size_t *maxvalids2_data, jl_array_t *edges, size if (idx != childidx) { if (max_valid < maxvalids2_data[childidx]) maxvalids2_data[childidx] = max_valid; - if (_jl_debug_method_invalidation && max_valid != ~(size_t)0) { - jl_method_instance_t *mi = (jl_method_instance_t*)jl_array_ptr_ref(edges, childidx * 2); - jl_value_t *loctag = NULL; - JL_GC_PUSH1(&loctag); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)mi); - loctag = jl_cstr_to_string("verify_methods"); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)cause); - JL_GC_POP(); - } } visited->items[childidx] = (void*)1; + if (_jl_debug_method_invalidation && max_valid != ~(size_t)0) { + jl_method_instance_t *mi = (jl_method_instance_t*)jl_array_ptr_ref(edges, childidx * 2); + jl_value_t *loctag = NULL; + JL_GC_PUSH1(&loctag); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)mi); + loctag = jl_cstr_to_string("verify_methods"); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)cause); + JL_GC_POP(); + } } return 0; } diff --git a/test/precompile.jl b/test/precompile.jl index 37498068fd39c..79e12939c615e 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -857,9 +857,13 @@ precompile_test_harness("code caching") do dir # This will be invalidated if StaleC is loaded useA() = $StaleA.stale("hello") + useA2() = useA() # force precompilation - useA() + begin + Base.Experimental.@force_compile + useA2() + end ## Reporting tests call_nbits(x::Integer) = $StaleA.nbits(x) @@ -940,6 +944,10 @@ precompile_test_harness("code caching") do dir @test invalidations[j-1] == "insert_backedges_callee" @test isa(invalidations[j-2], Type) @test isa(invalidations[j+1], Vector{Any}) # [nbits(::UInt8)] + m = only(methods(MB.useA2)) + mi = only(Base.specializations(m)) + @test !hasvalid(mi, world) + @test mi ∈ invalidations m = only(methods(MB.map_nbits)) @test !hasvalid(m.specializations::Core.MethodInstance, world+1) # insert_backedges invalidations also trigger their backedges From 81ae8a3eb379e88848930c3ed79e49a89ba913e9 Mon Sep 17 00:00:00 2001 From: Dilum Aluthge Date: Fri, 21 Apr 2023 19:09:53 -0400 Subject: [PATCH 567/775] Revert "add morespecific rule for Type{Union{}}" --- NEWS.md | 6 - base/abstractarray.jl | 5 +- base/array.jl | 3 - base/arrayshow.jl | 2 - base/boot.jl | 20 +- base/broadcast.jl | 6 +- base/complex.jl | 1 - base/essentials.jl | 10 +- base/float.jl | 1 - base/generator.jl | 8 +- base/indices.jl | 2 +- base/io.jl | 2 - base/iterators.jl | 2 - base/missing.jl | 2 +- base/number.jl | 4 - base/operators.jl | 1 - base/parse.jl | 2 - base/promotion.jl | 6 - base/some.jl | 1 - base/traits.jl | 4 +- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + src/gf.c | 19 +- src/jltypes.c | 2 +- src/julia_internal.h | 2 - src/subtype.c | 49 +-- src/typemap.c | 334 ++++++------------ stdlib/Statistics.version | 2 +- test/abstractarray.jl | 3 + test/ambiguous.jl | 6 +- test/core.jl | 6 +- test/missing.jl | 4 +- test/some.jl | 2 +- test/specificity.jl | 5 - 36 files changed, 157 insertions(+), 369 deletions(-) delete mode 100644 deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/md5 delete mode 100644 deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/sha512 create mode 100644 deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/md5 create mode 100644 deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/sha512 diff --git a/NEWS.md b/NEWS.md index 931db0ad1081f..33fd3549284d5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,12 +8,6 @@ Language changes ---------------- * When a task forks a child, the parent task's task-local RNG (random number generator) is no longer affected. The seeding of child based on the parent task also takes a more disciplined approach to collision resistance, using a design based on the SplitMix and DotMix splittable RNG schemes ([#49110]). -* A new morespecific rule for methods resolves ambiguities containing Union{} in favor of - the method defined explicitly to handle the Union{} argument. This makes it possible to - define methods to explicitly handle Union{} without the ambiguities that commonly would - result previously. This also lets the runtime optimize certain method lookups in a way - that significantly improves load and inference times for heavily overloaded methods that - dispatch on Types (such as traits and constructors). Compiler/Runtime improvements ----------------------------- diff --git a/base/abstractarray.jl b/base/abstractarray.jl index cb3956eb7c6d4..7be3f39d16def 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -183,13 +183,11 @@ CartesianIndex{2} For arrays, this function requires at least Julia 1.2. """ keytype(a::AbstractArray) = keytype(typeof(a)) -keytype(::Type{Union{}}, slurp...) = eltype(Union{}) keytype(A::Type{<:AbstractArray}) = CartesianIndex{ndims(A)} keytype(A::Type{<:AbstractVector}) = Int valtype(a::AbstractArray) = valtype(typeof(a)) -valtype(::Type{Union{}}, slurp...) = eltype(Union{}) """ valtype(T::Type{<:AbstractArray}) @@ -234,7 +232,7 @@ UInt8 ``` """ eltype(::Type) = Any -eltype(::Type{Bottom}, slurp...) = throw(ArgumentError("Union{} does not have elements")) +eltype(::Type{Bottom}) = throw(ArgumentError("Union{} does not have elements")) eltype(x) = eltype(typeof(x)) eltype(::Type{<:AbstractArray{E}}) where {E} = @isdefined(E) ? E : Any @@ -270,7 +268,6 @@ julia> ndims(A) """ ndims(::AbstractArray{T,N}) where {T,N} = N ndims(::Type{<:AbstractArray{<:Any,N}}) where {N} = N -ndims(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements")) """ length(collection) -> Integer diff --git a/base/array.jl b/base/array.jl index 0a5451bac5b74..60b1304f20ee0 100644 --- a/base/array.jl +++ b/base/array.jl @@ -252,10 +252,7 @@ function bitsunionsize(u::Union) return sz end -# Deprecate this, as it seems to have no documented meaning and is unused here, -# but is frequently accessed in packages elsize(@nospecialize _::Type{A}) where {T,A<:Array{T}} = aligned_sizeof(T) -elsize(::Type{Union{}}, slurp...) = 0 sizeof(a::Array) = Core.sizeof(a) function isassigned(a::Array, i::Int...) diff --git a/base/arrayshow.jl b/base/arrayshow.jl index e600e6281bd15..af65df3c97b9d 100644 --- a/base/arrayshow.jl +++ b/base/arrayshow.jl @@ -540,12 +540,10 @@ end # returning Any, as this would cause incorrect printing in e.g. `Vector[Any[1]]`, # because eltype(Vector) == Any so `Any` wouldn't be printed in `Any[1]`) typeinfo_eltype(typeinfo) = nothing # element type not precisely known -typeinfo_eltype(typeinfo::Type{Union{}}, slurp...) = nothing 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) - # types that can be parsed back accurately from their un-decorated representations function typeinfo_implicit(@nospecialize(T)) if T === Float64 || T === Int || T === Char || T === String || T === Symbol || diff --git a/base/boot.jl b/base/boot.jl index 3a8abde4bce14..ca6e6c81405e2 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -258,17 +258,9 @@ UnionAll(v::TypeVar, @nospecialize(t)) = ccall(:jl_type_unionall, Any, (Any, Any const Vararg = ccall(:jl_toplevel_eval_in, Any, (Any, Any), Core, _expr(:new, TypeofVararg)) -# dispatch token indicating a kwarg (keyword sorter) call -function kwcall end -# deprecated internal functions: -kwfunc(@nospecialize(f)) = kwcall -kwftype(@nospecialize(t)) = typeof(kwcall) - -# Let the compiler assume that calling Union{} as a constructor does not need -# to be considered ever (which comes up often as Type{<:T} inference, and -# occasionally in user code from eltype). -Union{}(a...) = throw(ArgumentError("cannot construct a value of type Union{} for return result")) -kwcall(kwargs, ::Type{Union{}}, a...) = Union{}(a...) +# let the compiler assume that calling Union{} as a constructor does not need +# to be considered ever (which comes up often as Type{<:T}) +Union{}(a...) = throw(MethodError(Union{}, a)) Expr(@nospecialize args...) = _expr(args...) @@ -377,6 +369,12 @@ include(m::Module, fname::String) = ccall(:jl_load_, Any, (Any, Any), m, fname) eval(m::Module, @nospecialize(e)) = ccall(:jl_toplevel_eval_in, Any, (Any, Any), m, e) +# dispatch token indicating a kwarg (keyword sorter) call +function kwcall end +# deprecated internal functions: +kwfunc(@nospecialize(f)) = kwcall +kwftype(@nospecialize(t)) = typeof(kwcall) + mutable struct Box contents::Any Box(@nospecialize(x)) = new(x) diff --git a/base/broadcast.jl b/base/broadcast.jl index 1e057789509ed..955a5652353d7 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -34,9 +34,6 @@ that you may be able to leverage; see the """ abstract type BroadcastStyle end -struct Unknown <: BroadcastStyle end -BroadcastStyle(::Type{Union{}}, slurp...) = Unknown() # ambiguity resolution - """ `Broadcast.Style{C}()` defines a [`BroadcastStyle`](@ref) signaling through the type parameter `C`. You can use this as an alternative to creating custom subtypes of `BroadcastStyle`, @@ -48,6 +45,9 @@ struct Style{T} <: BroadcastStyle end BroadcastStyle(::Type{<:Tuple}) = Style{Tuple}() +struct Unknown <: BroadcastStyle end +BroadcastStyle(::Type{Union{}}) = Unknown() # ambiguity resolution + """ `Broadcast.AbstractArrayStyle{N} <: BroadcastStyle` is the abstract supertype for any style associated with an `AbstractArray` type. diff --git a/base/complex.jl b/base/complex.jl index a0473c90d5c17..4ce43687aa932 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -120,7 +120,6 @@ Float64 real(T::Type) = typeof(real(zero(T))) real(::Type{T}) where {T<:Real} = T real(C::Type{<:Complex}) = fieldtype(C, 1) -real(::Type{Union{}}, slurp...) = Union{}(im) """ isreal(x) -> Bool diff --git a/base/essentials.jl b/base/essentials.jl index e2035601f4fb5..829341c482383 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -310,8 +310,13 @@ See also: [`round`](@ref), [`trunc`](@ref), [`oftype`](@ref), [`reinterpret`](@r """ function convert end -# ensure this is never ambiguous, and therefore fast for lookup -convert(T::Type{Union{}}, x...) = throw(ArgumentError("cannot convert a value to Union{} for assignment")) +# make convert(::Type{<:Union{}}, x::T) intentionally ambiguous for all T +# so it will never get called or invalidated by loading packages +# with carefully chosen types that won't have any other convert methods defined +convert(T::Type{<:Core.IntrinsicFunction}, x) = throw(MethodError(convert, (T, x))) +convert(T::Type{<:Nothing}, x) = throw(MethodError(convert, (Nothing, x))) +convert(::Type{T}, x::T) where {T<:Core.IntrinsicFunction} = x +convert(::Type{T}, x::T) where {T<:Nothing} = x convert(::Type{Type}, x::Type) = x # the ssair optimizer is strongly dependent on this method existing to avoid over-specialization # in the absence of inlining-enabled @@ -535,7 +540,6 @@ Neither `convert` nor `cconvert` should take a Julia object and turn it into a ` function cconvert end cconvert(T::Type, x) = x isa T ? x : convert(T, x) # do the conversion eagerly in most cases -cconvert(::Type{Union{}}, x...) = convert(Union{}, x...) cconvert(::Type{<:Ptr}, x) = x # but defer the conversion to Ptr to unsafe_convert unsafe_convert(::Type{T}, x::T) where {T} = x # unsafe_convert (like convert) defaults to assuming the convert occurred unsafe_convert(::Type{T}, x::T) where {T<:Ptr} = x # to resolve ambiguity with the next method diff --git a/base/float.jl b/base/float.jl index fad7146655ade..4190bfa18bb2b 100644 --- a/base/float.jl +++ b/base/float.jl @@ -310,7 +310,6 @@ Float64 """ float(::Type{T}) where {T<:Number} = typeof(float(zero(T))) float(::Type{T}) where {T<:AbstractFloat} = T -float(::Type{Union{}}, slurp...) = Union{}(0.0) """ unsafe_trunc(T, x) diff --git a/base/generator.jl b/base/generator.jl index aa4b7f67cba95..d11742fe5b72f 100644 --- a/base/generator.jl +++ b/base/generator.jl @@ -92,13 +92,13 @@ Base.HasLength() """ IteratorSize(x) = IteratorSize(typeof(x)) IteratorSize(::Type) = HasLength() # HasLength is the default -IteratorSize(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements")) -IteratorSize(::Type{Any}) = SizeUnknown() IteratorSize(::Type{<:Tuple}) = HasLength() IteratorSize(::Type{<:AbstractArray{<:Any,N}}) where {N} = HasShape{N}() IteratorSize(::Type{Generator{I,F}}) where {I,F} = IteratorSize(I) +IteratorSize(::Type{Any}) = SizeUnknown() + haslength(iter) = IteratorSize(iter) isa Union{HasShape, HasLength} abstract type IteratorEltype end @@ -126,7 +126,7 @@ Base.HasEltype() """ IteratorEltype(x) = IteratorEltype(typeof(x)) IteratorEltype(::Type) = HasEltype() # HasEltype is the default -IteratorEltype(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements")) -IteratorEltype(::Type{Any}) = EltypeUnknown() IteratorEltype(::Type{Generator{I,T}}) where {I,T} = EltypeUnknown() + +IteratorEltype(::Type{Any}) = EltypeUnknown() diff --git a/base/indices.jl b/base/indices.jl index a9189865048cd..6a28cf63316e6 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -92,7 +92,7 @@ particular, [`eachindex`](@ref) creates an iterator whose type depends on the setting of this trait. """ IndexStyle(A::AbstractArray) = IndexStyle(typeof(A)) -IndexStyle(::Type{Union{}}, slurp...) = IndexLinear() +IndexStyle(::Type{Union{}}) = IndexLinear() IndexStyle(::Type{<:AbstractArray}) = IndexCartesian() IndexStyle(::Type{<:Array}) = IndexLinear() IndexStyle(::Type{<:AbstractRange}) = IndexLinear() diff --git a/base/io.jl b/base/io.jl index 9c00c57576bac..3bcae2e8d7836 100644 --- a/base/io.jl +++ b/base/io.jl @@ -219,8 +219,6 @@ julia> read(io, String) ``` """ read(stream, t) -read(stream, ::Type{Union{}}, slurp...; kwargs...) = error("cannot read a value of type Union{}") - """ write(io::IO, x) diff --git a/base/iterators.jl b/base/iterators.jl index 11e94d3384de8..a4d12517aabcc 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -1170,7 +1170,6 @@ IteratorEltype(::Type{Flatten{Tuple{}}}) = IteratorEltype(Tuple{}) _flatteneltype(I, ::HasEltype) = IteratorEltype(eltype(I)) _flatteneltype(I, et) = EltypeUnknown() -flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{Union{}}, slurp...) = HasLength() # length==0 flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:NTuple{N,Any}}) where {N} = HasLength() flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Tuple}) = SizeUnknown() flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Number}) = HasLength() @@ -1182,7 +1181,6 @@ _flatten_iteratorsize(sz, ::HasEltype, ::Type{Tuple{}}) = HasLength() IteratorSize(::Type{Flatten{I}}) where {I} = _flatten_iteratorsize(IteratorSize(I), IteratorEltype(I), I) -flatten_length(f, T::Type{Union{}}, slurp...) = 0 function flatten_length(f, T::Type{<:NTuple{N,Any}}) where {N} return N * length(f.it) end diff --git a/base/missing.jl b/base/missing.jl index 4544c2b38c460..e1988064aadc1 100644 --- a/base/missing.jl +++ b/base/missing.jl @@ -41,7 +41,6 @@ nonmissingtype(::Type{T}) where {T} = typesplit(T, Missing) function nonmissingtype_checked(T::Type) R = nonmissingtype(T) R >: T && error("could not compute non-missing type") - R <: Union{} && error("cannot convert a value to missing for assignment") return R end @@ -70,6 +69,7 @@ convert(::Type{T}, x::T) where {T>:Union{Missing, Nothing}} = x convert(::Type{T}, x) where {T>:Missing} = convert(nonmissingtype_checked(T), x) convert(::Type{T}, x) where {T>:Union{Missing, Nothing}} = convert(nonmissingtype_checked(nonnothingtype_checked(T)), x) + # Comparison operators ==(::Missing, ::Missing) = missing ==(::Missing, ::Any) = missing diff --git a/base/number.jl b/base/number.jl index 923fc907d4038..31aa616b0eb55 100644 --- a/base/number.jl +++ b/base/number.jl @@ -307,7 +307,6 @@ julia> zero(rand(2,2)) """ zero(x::Number) = oftype(x,0) zero(::Type{T}) where {T<:Number} = convert(T,0) -zero(::Type{Union{}}, slurp...) = Union{}(0) """ one(x) @@ -346,7 +345,6 @@ julia> import Dates; one(Dates.Day(1)) """ one(::Type{T}) where {T<:Number} = convert(T,1) one(x::T) where {T<:Number} = one(T) -one(::Type{Union{}}, slurp...) = Union{}(1) # note that convert(T, 1) should throw an error if T is dimensionful, # so this fallback definition should be okay. @@ -370,7 +368,6 @@ julia> import Dates; oneunit(Dates.Day) """ oneunit(x::T) where {T} = T(one(x)) oneunit(::Type{T}) where {T} = T(one(T)) -oneunit(::Type{Union{}}, slurp...) = Union{}(1) """ big(T::Type) @@ -391,4 +388,3 @@ Complex{BigInt} ``` """ big(::Type{T}) where {T<:Number} = typeof(big(zero(T))) -big(::Type{Union{}}, slurp...) = Union{}(0) diff --git a/base/operators.jl b/base/operators.jl index 5893c5944a3a0..3b34e549ea849 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -888,7 +888,6 @@ julia> widen(1.5f0) """ widen(x::T) where {T} = convert(widen(T), x) widen(x::Type{T}) where {T} = throw(MethodError(widen, (T,))) -widen(x::Type{Union{}}, slurp...) = throw(MethodError(widen, (Union{},))) # function pipelining diff --git a/base/parse.jl b/base/parse.jl index d800e54258b0d..6e616004a47af 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -36,7 +36,6 @@ julia> parse(Complex{Float64}, "3.2e-1 + 4.5im") ``` """ parse(T::Type, str; base = Int) -parse(::Type{Union{}}, slurp...; kwargs...) = error("cannot parse a value as Union{}") function parse(::Type{T}, c::AbstractChar; base::Integer = 10) where T<:Integer a::Int = (base <= 36 ? 10 : 36) @@ -252,7 +251,6 @@ function parse(::Type{T}, s::AbstractString; base::Union{Nothing,Integer} = noth convert(T, tryparse_internal(T, s, firstindex(s), lastindex(s), base===nothing ? 0 : check_valid_base(base), true)) end -tryparse(::Type{Union{}}, slurp...; kwargs...) = error("cannot parse a value as Union{}") ## string to float functions ## diff --git a/base/promotion.jl b/base/promotion.jl index 6e32bd7a42efa..31f507d021e78 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -323,12 +323,6 @@ it for new types as appropriate. function promote_rule end promote_rule(::Type, ::Type) = Bottom -# Define some methods to avoid needing to enumerate unrelated possibilities when presented -# with Type{<:T}, and return a value in general accordance with the result given by promote_type -promote_rule(::Type{Bottom}, slurp...) = Bottom -promote_rule(::Type{Bottom}, ::Type{Bottom}, slurp...) = Bottom # not strictly necessary, since the next method would match unambiguously anyways -promote_rule(::Type{Bottom}, ::Type{T}, slurp...) where {T} = T -promote_rule(::Type{T}, ::Type{Bottom}, slurp...) where {T} = T promote_result(::Type,::Type,::Type{T},::Type{S}) where {T,S} = (@inline; promote_type(T,S)) # If no promote_rule is defined, both directions give Bottom. In that diff --git a/base/some.jl b/base/some.jl index 0d538cbed6c23..08cb3c1648ba1 100644 --- a/base/some.jl +++ b/base/some.jl @@ -29,7 +29,6 @@ end function nonnothingtype_checked(T::Type) R = nonnothingtype(T) R >: T && error("could not compute non-nothing type") - R <: Union{} && error("cannot convert a value to nothing for assignment") return R end diff --git a/base/traits.jl b/base/traits.jl index 47ab8ddc0c7ac..53ae14b12c61e 100644 --- a/base/traits.jl +++ b/base/traits.jl @@ -11,7 +11,7 @@ OrderStyle(::Type{<:Real}) = Ordered() OrderStyle(::Type{<:AbstractString}) = Ordered() OrderStyle(::Type{Symbol}) = Ordered() OrderStyle(::Type{<:Any}) = Unordered() -OrderStyle(::Type{Union{}}, slurp...) = Ordered() +OrderStyle(::Type{Union{}}) = Ordered() # trait for objects that support arithmetic abstract type ArithmeticStyle end @@ -23,7 +23,6 @@ ArithmeticStyle(instance) = ArithmeticStyle(typeof(instance)) ArithmeticStyle(::Type{<:AbstractFloat}) = ArithmeticRounds() ArithmeticStyle(::Type{<:Integer}) = ArithmeticWraps() ArithmeticStyle(::Type{<:Any}) = ArithmeticUnknown() -ArithmeticStyle(::Type{Union{}}, slurp...) = ArithmeticUnknown() # trait for objects that support ranges with regular step """ @@ -59,6 +58,5 @@ ranges with an element type which is a subtype of `Integer`. abstract type RangeStepStyle end struct RangeStepRegular <: RangeStepStyle end # range with regular step struct RangeStepIrregular <: RangeStepStyle end # range with rounding error -RangeStepStyle(::Type{Union{}}, slurp...) = RangeStepIrregular() RangeStepStyle(instance) = RangeStepStyle(typeof(instance)) diff --git a/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/md5 b/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/md5 deleted file mode 100644 index 7e7a889eecd29..0000000000000 --- a/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -6564297a5f5971231809bf9940f68b98 diff --git a/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/sha512 b/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/sha512 deleted file mode 100644 index bbe9b8bed6371..0000000000000 --- a/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -22d14c82a30f3ec7af09028423cc823808abf86918d5707fd1fcf6ca20dea7871589da9b22e462d194e86fcee380f549aeb65f585048f00bf23281786b17e040 diff --git a/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/md5 b/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/md5 new file mode 100644 index 0000000000000..0e2d0534cd8c7 --- /dev/null +++ b/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/md5 @@ -0,0 +1 @@ +62d47cffac86df3c59b3de8dd218aa79 diff --git a/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/sha512 b/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/sha512 new file mode 100644 index 0000000000000..95e88c63f1a14 --- /dev/null +++ b/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/sha512 @@ -0,0 +1 @@ +6354b1e84d7df1fe8d7e1444181497cac87d22d10a2a21b9f7fab748c209bd9aba64f2df6489e9441624fcf27140ccffa3f7eabaf2517f4900b2661be0c74ba5 diff --git a/src/gf.c b/src/gf.c index 23ce8d33c82d2..2c3485823202b 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1487,8 +1487,6 @@ static int get_intersect_visitor(jl_typemap_entry_t *oldentry, struct typemap_in // skip if no world has both active // also be careful not to try to scan something from the current dump-reload though return 1; - // don't need to consider other similar methods if this oldentry will always fully intersect with them and dominates all of them - typemap_slurp_search(oldentry, &closure->match); jl_method_t *oldmethod = oldentry->func.method; if (closure->match.issubty // e.g. jl_subtype(closure->newentry.sig, oldentry->sig) && jl_subtype(oldmethod->sig, (jl_value_t*)closure->newentry->sig)) { // e.g. jl_type_equal(closure->newentry->sig, oldentry->sig) @@ -1513,7 +1511,7 @@ static jl_value_t *get_intersect_matches(jl_typemap_t *defs, jl_typemap_entry_t else va = NULL; } - struct matches_env env = {{get_intersect_visitor, (jl_value_t*)type, va, /* .search_slurp = */ 0, + struct matches_env env = {{get_intersect_visitor, (jl_value_t*)type, va, /* .ti = */ NULL, /* .env = */ jl_emptysvec, /* .issubty = */ 0}, /* .newentry = */ newentry, /* .shadowed */ NULL, /* .replaced */ NULL}; JL_GC_PUSH3(&env.match.env, &env.match.ti, &env.shadowed); @@ -3210,7 +3208,6 @@ struct ml_matches_env { int intersections; size_t world; int lim; - int include_ambiguous; // results: jl_value_t *t; // array of method matches size_t min_valid; @@ -3266,9 +3263,6 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio return 0; closure->lim--; } - // don't need to consider other similar methods if this ml will always fully intersect with them and dominates all of them - if (!closure->include_ambiguous || closure->lim != -1) - typemap_slurp_search(ml, &closure->match); closure->matc = make_method_match((jl_tupletype_t*)closure->match.ti, closure->match.env, meth, closure->match.issubty ? FULLY_COVERS : NOT_FULLY_COVERS); @@ -3283,10 +3277,9 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio return 1; } -static int ml_mtable_visitor(jl_methtable_t *mt, void *closure0) +static int ml_mtable_visitor(jl_methtable_t *mt, void *env) { - struct typemap_intersection_env* env = (struct typemap_intersection_env*)closure0; - return jl_typemap_intersection_visitor(jl_atomic_load_relaxed(&mt->defs), jl_cachearg_offset(mt), env); + return jl_typemap_intersection_visitor(jl_atomic_load_relaxed(&mt->defs), jl_cachearg_offset(mt), (struct typemap_intersection_env*)env); } // This is the collect form of calling jl_typemap_intersection_visitor @@ -3318,9 +3311,9 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, else va = NULL; } - struct ml_matches_env env = {{ml_matches_visitor, (jl_value_t*)type, va, /* .search_slurp = */ 0, + struct ml_matches_env env = {{ml_matches_visitor, (jl_value_t*)type, va, /* .ti = */ NULL, /* .env = */ jl_emptysvec, /* .issubty = */ 0}, - intersections, world, lim, include_ambiguous, /* .t = */ jl_an_empty_vec_any, + intersections, world, lim, /* .t = */ jl_an_empty_vec_any, /* .min_valid = */ *min_valid, /* .max_valid = */ *max_valid, /* .matc = */ NULL}; struct jl_typemap_assoc search = {(jl_value_t*)type, world, jl_emptysvec, 1, ~(size_t)0}; jl_value_t *isect2 = NULL; @@ -3384,7 +3377,7 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, return env.t; } } - if (!ml_mtable_visitor(mt, &env.match)) { + if (!jl_typemap_intersection_visitor(jl_atomic_load_relaxed(&mt->defs), jl_cachearg_offset(mt), &env.match)) { JL_GC_POP(); return jl_nothing; } diff --git a/src/jltypes.c b/src/jltypes.c index 1837b21742e28..482f21a14c76e 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1601,7 +1601,7 @@ static void check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, si JL_GC_POP(); } -jl_value_t *extract_wrapper(jl_value_t *t JL_PROPAGATES_ROOT) JL_GLOBALLY_ROOTED +static jl_value_t *extract_wrapper(jl_value_t *t JL_PROPAGATES_ROOT) JL_GLOBALLY_ROOTED { t = jl_unwrap_unionall(t); if (jl_is_datatype(t)) diff --git a/src/julia_internal.h b/src/julia_internal.h index 0674806d35a5b..61c8a40f7eeb3 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1462,14 +1462,12 @@ struct typemap_intersection_env { jl_typemap_intersection_visitor_fptr const fptr; // fptr to call on a match jl_value_t *const type; // type to match jl_value_t *const va; // the tparam0 for the vararg in type, if applicable (or NULL) - size_t search_slurp; // output values jl_value_t *ti; // intersection type jl_svec_t *env; // intersection env (initialize to null to perform intersection without an environment) int issubty; // if `a <: b` is true in `intersect(a,b)` }; int jl_typemap_intersection_visitor(jl_typemap_t *a, int offs, struct typemap_intersection_env *closure); -void typemap_slurp_search(jl_typemap_entry_t *ml, struct typemap_intersection_env *closure); // -- simplevector.c -- // diff --git a/src/subtype.c b/src/subtype.c index 9b85c0ceb703c..20ef6139ee886 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2265,34 +2265,20 @@ int jl_has_intersect_type_not_kind(jl_value_t *t) t = jl_unwrap_unionall(t); if (t == (jl_value_t*)jl_any_type) return 1; - assert(!jl_is_vararg(t)); - if (jl_is_uniontype(t)) + if (jl_is_uniontype(t)) { return jl_has_intersect_type_not_kind(((jl_uniontype_t*)t)->a) || jl_has_intersect_type_not_kind(((jl_uniontype_t*)t)->b); - if (jl_is_typevar(t)) + } + if (jl_is_typevar(t)) { return jl_has_intersect_type_not_kind(((jl_tvar_t*)t)->ub); - if (jl_is_datatype(t)) + } + if (jl_is_datatype(t)) { if (((jl_datatype_t*)t)->name == jl_type_typename) return 1; + } return 0; } -// compute if DataType<:t || Union<:t || UnionAll<:t etc. -int jl_has_intersect_kind_not_type(jl_value_t *t) -{ - t = jl_unwrap_unionall(t); - if (t == (jl_value_t*)jl_any_type || jl_is_kind(t)) - return 1; - assert(!jl_is_vararg(t)); - if (jl_is_uniontype(t)) - return jl_has_intersect_kind_not_type(((jl_uniontype_t*)t)->a) || - jl_has_intersect_kind_not_type(((jl_uniontype_t*)t)->b); - if (jl_is_typevar(t)) - return jl_has_intersect_kind_not_type(((jl_tvar_t*)t)->ub); - return 0; -} - - JL_DLLEXPORT int jl_isa(jl_value_t *x, jl_value_t *t) { if (jl_typeis(x,t) || t == (jl_value_t*)jl_any_type) @@ -4483,22 +4469,6 @@ static int num_occurs(jl_tvar_t *v, jl_typeenv_t *env) return 0; } -int tuple_cmp_typeofbottom(jl_datatype_t *a, jl_datatype_t *b) -{ - size_t i, la = jl_nparams(a), lb = jl_nparams(b); - for (i = 0; i < la || i < lb; i++) { - jl_value_t *pa = i < la ? jl_tparam(a, i) : NULL; - jl_value_t *pb = i < lb ? jl_tparam(b, i) : NULL; - assert(jl_typeofbottom_type); // for clang-sa - int xa = pa == (jl_value_t*)jl_typeofbottom_type || pa == (jl_value_t*)jl_typeofbottom_type->super; - int xb = pb == (jl_value_t*)jl_typeofbottom_type || pb == (jl_value_t*)jl_typeofbottom_type->super; - if (xa != xb) - return xa - xb; - } - return 0; -} - - #define HANDLE_UNIONALL_A \ jl_unionall_t *ua = (jl_unionall_t*)a; \ jl_typeenv_t newenv = { ua->var, 0x0, env }; \ @@ -4517,13 +4487,6 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, jl_value_t *a0, jl_v return 0; if (jl_is_tuple_type(a) && jl_is_tuple_type(b)) { - // compare whether a and b have Type{Union{}} included, - // which makes them instantly the most specific, regardless of all else, - // for whichever is left most (the left-to-right behavior here ensures - // we do not need to keep track of conflicts with multiple methods). - int msp = tuple_cmp_typeofbottom((jl_datatype_t*)a, (jl_datatype_t*)b); - if (msp) - return msp > 0; // When one is JL_VARARG_BOUND and the other has fixed length, // allow the argument length to fix the tvar jl_vararg_kind_t akind = jl_va_tuple_kind((jl_datatype_t*)a); diff --git a/src/typemap.c b/src/typemap.c index a77fdfa4c13fd..e60f3d566284e 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -33,9 +33,6 @@ static jl_value_t *jl_type_extract_name(jl_value_t *t1 JL_PROPAGATES_ROOT) else if (jl_is_typevar(t1)) { return jl_type_extract_name(((jl_tvar_t*)t1)->ub); } - else if (t1 == jl_bottom_type || t1 == (jl_value_t*)jl_typeofbottom_type || t1 == (jl_value_t*)jl_typeofbottom_type->super) { - return (jl_value_t*)jl_typeofbottom_type->name; // put Union{} and typeof(Union{}) and Type{Union{}} together for convenience - } else if (jl_is_datatype(t1)) { jl_datatype_t *dt = (jl_datatype_t*)t1; if (!jl_is_kind(t1)) @@ -66,9 +63,6 @@ static int jl_type_extract_name_precise(jl_value_t *t1, int invariant) else if (jl_is_typevar(t1)) { return jl_type_extract_name_precise(((jl_tvar_t*)t1)->ub, 0); } - else if (t1 == jl_bottom_type || t1 == (jl_value_t*)jl_typeofbottom_type || t1 == (jl_value_t*)jl_typeofbottom_type->super) { - return 1; - } else if (jl_is_datatype(t1)) { jl_datatype_t *dt = (jl_datatype_t*)t1; if ((invariant || !dt->name->abstract) && !jl_is_kind(t1)) @@ -90,18 +84,6 @@ static int jl_type_extract_name_precise(jl_value_t *t1, int invariant) return 1; } -// return whether Type{Union{}} is a subtype of Type{t1} (which may have free typevars) -static int jl_parameter_includes_bottom(jl_value_t *t1) -{ - if (jl_is_typevar(t1) || t1 == jl_bottom_type) - return 1; - else if (jl_is_uniontype(t1)) { - jl_uniontype_t *u1 = (jl_uniontype_t*)t1; - return jl_parameter_includes_bottom(u1->a) && jl_parameter_includes_bottom(u1->b); - } - return 0; -} - // ----- Type Signature Subtype Testing ----- // @@ -396,10 +378,8 @@ static unsigned jl_supertype_height(jl_datatype_t *dt) } // return true if a and b might intersect in the type domain (over just their type-names) -static int tname_intersection_dt(jl_datatype_t *a, jl_typename_t *bname, unsigned ha) +static int tname_intersection(jl_datatype_t *a, jl_typename_t *bname, unsigned ha) { - if (a == jl_any_type) - return 1; jl_datatype_t *b = (jl_datatype_t*)jl_unwrap_unionall(bname->wrapper); unsigned hb = 1; while (b != jl_any_type) { @@ -415,42 +395,8 @@ static int tname_intersection_dt(jl_datatype_t *a, jl_typename_t *bname, unsigne return a->name == bname; } -static int tname_intersection(jl_value_t *a, jl_typename_t *bname, int8_t tparam) -{ - if (a == (jl_value_t*)jl_any_type) - return 1; - a = jl_unwrap_unionall(a); - assert(!jl_is_vararg(a)); - if (jl_is_uniontype(a)) - return tname_intersection(((jl_uniontype_t*)a)->a, bname, tparam) || - tname_intersection(((jl_uniontype_t*)a)->b, bname, tparam); - if (jl_is_typevar(a)) - return tname_intersection(((jl_tvar_t*)a)->ub, bname, tparam); - if (jl_is_datatype(a)) { - if (tparam) { - if (!jl_is_type_type(a)) - return 0; - a = jl_unwrap_unionall(jl_tparam0(a)); - if (!jl_is_datatype(a)) - return tname_intersection(a, bname, 0); - } - return tname_intersection_dt((jl_datatype_t*)a, bname, jl_supertype_height((jl_datatype_t*)a)); - } - return 0; -} - -static int concrete_intersects(jl_value_t *t, jl_value_t *ty, int8_t tparam) -{ - if (ty == (jl_value_t*)jl_any_type) // easy case: Any always matches - return 1; - if (tparam & 1) - return jl_isa(t, ty); // (Type{t} <: ty), where is_leaf_type(t) => isa(t, ty) - else - return t == ty || jl_subtype(t, ty); -} - -// tparam bit 0 is ::Type{T} (vs. T) -// tparam bit 1 is typename(T) (vs. T) +// tparam bit 1 is ::Type{T} (vs. T) +// tparam bit 2 is typename(T) (vs. T) static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, int8_t tparam, int8_t offs, struct typemap_intersection_env *closure) { @@ -458,26 +404,15 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, size_t i, l = jl_array_len(a); _Atomic(jl_typemap_t*) *data = (_Atomic(jl_typemap_t*)*)jl_array_data(a); unsigned height = 0; - jl_datatype_t *tydt = jl_any_type; + jl_datatype_t *tydt = NULL; + if (jl_is_kind(ty)) + ty = (jl_value_t*)jl_any_type; if (tparam & 2) { - // try to extract a description of ty for intersections, but since we - jl_value_t *ttype = jl_unwrap_unionall(ty); - if (tparam & 1) - // extract T from Type{T} (if possible) - ttype = jl_is_type_type(ttype) ? jl_tparam0(ttype) : NULL; - if (ttype && jl_is_datatype(ttype)) { - tydt = (jl_datatype_t*)ttype; - } - else if (ttype) { - ttype = jl_type_extract_name(ttype); - tydt = ttype ? (jl_datatype_t*)jl_unwrap_unionall(((jl_typename_t*)ttype)->wrapper) : NULL; - } - if (tydt == jl_any_type) - ty = (jl_value_t*)jl_any_type; - else if (tydt == NULL) - tydt = jl_any_type; - else + tydt = (jl_datatype_t*)jl_unwrap_unionall(ty); + if (jl_is_datatype(ty)) height = jl_supertype_height(tydt); + else + tydt = jl_any_type; } for (i = 0; i < l; i += 2) { jl_value_t *t = jl_atomic_load_relaxed(&data[i]); @@ -487,11 +422,8 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, if (tparam & 2) { jl_typemap_t *ml = jl_atomic_load_relaxed(&data[i + 1]); JL_GC_PROMISE_ROOTED(ml); - if (tydt == jl_any_type ? - tname_intersection(ty, (jl_typename_t*)t, tparam & 1) : - tname_intersection_dt(tydt, (jl_typename_t*)t, height)) { - if ((tparam & 1) && t == (jl_value_t*)jl_typeofbottom_type->name) // skip Type{Union{}} and Type{typeof(Union{})}, since the caller should have already handled those - continue; + if (tydt == jl_any_type || // easy case: Any always matches + tname_intersection(tydt, (jl_typename_t*)t, height)) { if (jl_is_array(ml)) { if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, tparam & ~2, offs, closure)) goto exit; @@ -504,7 +436,10 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, } else { // `t` is a leaftype, so intersection test becomes subtype (after excluding kinds) - if (concrete_intersects(t, ty, tparam)) { + if (ty == (jl_value_t*)jl_any_type || // easy case: Any always matches + (tparam & 1 + ? jl_isa(t, ty) // (Type{t} <: ty), where is_leaf_type(t) => isa(t, ty) + : (t == ty || jl_subtype(t, ty)))) { jl_typemap_t *ml = jl_atomic_load_relaxed(&data[i + 1]); JL_GC_PROMISE_ROOTED(ml); // NOTE: ml might be NULL if we're racing with the thread that's inserting the item @@ -521,7 +456,6 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, return 0; } - // calls fptr on each jl_typemap_entry_t in cache in sort order // for which type ∩ ml->type != Union{}, until fptr return false static int jl_typemap_intersection_node_visitor(jl_typemap_entry_t *ml, struct typemap_intersection_env *closure) @@ -562,40 +496,6 @@ static int jl_typemap_intersection_node_visitor(jl_typemap_entry_t *ml, struct t return 1; } -int jl_has_intersect_type_not_kind(jl_value_t *t); -int jl_has_intersect_kind_not_type(jl_value_t *t); - -// if TypeVar tv is used covariantly, it cannot be Union{} -int has_covariant_var(jl_datatype_t *ttypes, jl_tvar_t *tv) -{ - size_t i, l = jl_nparams(ttypes); - for (i = 0; i < l; i++) - if (jl_tparam(ttypes, i) == (jl_value_t*)tv) - return 1; - return 0; -} - -void typemap_slurp_search(jl_typemap_entry_t *ml, struct typemap_intersection_env *closure) -{ - // n.b. we could consider mt->max_args here too, so this optimization - // usually works even if the user forgets the `slurp...` argument, but - // there is discussion that parameter may be going away? (and it is - // already not accurately up-to-date for all tables currently anyways) - if (closure->search_slurp && ml->va) { - jl_value_t *sig = jl_unwrap_unionall((jl_value_t*)ml->sig); - size_t nargs = jl_nparams(sig); - if (nargs > 1 && nargs - 1 == closure->search_slurp) { - jl_vararg_t *va = (jl_vararg_t*)jl_tparam(sig, nargs - 1); - assert(jl_is_vararg((jl_value_t*)va)); - if (va->T == (jl_value_t*)jl_any_type && va->N == NULL) { - // instruct typemap it can set exclude_typeofbottom on parameter nargs - // since we found the necessary slurp argument - closure->search_slurp = 0; - } - } - } -} - int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, struct typemap_intersection_env *closure) { @@ -604,12 +504,13 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, //TODO: fast-path for leaf-type tuples? //if (ttypes->isdispatchtuple) { // register jl_typemap_intersection_visitor_fptr fptr = closure->fptr; - // struct jl_typemap_assoc search = {(jl_value_t*)closure->type, world, closure->env, 0, ~(size_t)0}; - // jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(map, search, offs, /*subtype*/1); - // if (ml) { - // closure->env = search->env; - // if (!fptr(ml, closure)) - // return 0; + // struct jl_typemap_assoc search = {(jl_value_t*)closure->type, world, closure->env, 0, ~(size_t)0}; + // jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(map, search, offs, /*subtype*/1); + // if (ml) { + // closure->env = search->env; + // if (!fptr(ml, closure)) + // return 0; + // } // } // return 1; //} @@ -631,61 +532,23 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, if (ty) { while (jl_is_typevar(ty)) ty = ((jl_tvar_t*)ty)->ub; + jl_value_t *typetype = jl_unwrap_unionall(ty); + typetype = jl_is_type_type(typetype) ? jl_tparam0(typetype) : NULL; + jl_value_t *name = typetype ? jl_type_extract_name(typetype) : NULL; // approxify the tparam until we have a valid type - if (jl_has_free_typevars(ty)) - ty = jl_rewrap_unionall(ty, closure->type); - JL_GC_PUSH1(&ty); - jl_array_t *targ = jl_atomic_load_relaxed(&cache->targ); - jl_array_t *tname = jl_atomic_load_relaxed(&cache->tname); - int maybe_type = 0; - int maybe_kind = 0; - int exclude_typeofbottom = 0; - jl_value_t *typetype = NULL; - jl_value_t *name = NULL; - // pre-check: optimized pre-intersection test to see if `ty` could intersect with any Type or Kind - if (targ != (jl_array_t*)jl_an_empty_vec_any || tname != (jl_array_t*)jl_an_empty_vec_any) { - maybe_kind = jl_has_intersect_kind_not_type(ty); - maybe_type = maybe_kind || jl_has_intersect_type_not_kind(ty); - if (maybe_type && !maybe_kind) { - typetype = jl_unwrap_unionall(ty); - typetype = jl_is_type_type(typetype) ? jl_tparam0(typetype) : NULL; - name = typetype ? jl_type_extract_name(typetype) : NULL; - if (!typetype) - exclude_typeofbottom = !jl_subtype((jl_value_t*)jl_typeofbottom_type, ty); - else if (jl_is_typevar(typetype)) - exclude_typeofbottom = has_covariant_var((jl_datatype_t*)ttypes, (jl_tvar_t*)typetype); - else - exclude_typeofbottom = !jl_parameter_includes_bottom(typetype); - } + if (jl_has_free_typevars(ty)) { + ty = jl_unwrap_unionall(ty); + if (jl_is_datatype(ty)) + ty = ((jl_datatype_t*)ty)->name->wrapper; + else + ty = (jl_value_t*)jl_any_type; } - // First check for intersections with methods defined on Type{T}, where T was a concrete type - if (targ != (jl_array_t*)jl_an_empty_vec_any && maybe_type && - (!typetype || jl_has_free_typevars(typetype) || is_cache_leaf(typetype, 1))) { // otherwise cannot contain this particular kind, so don't bother with checking - if (!exclude_typeofbottom) { - // detect Type{Union{}}, Type{Type{Union{}}}, and Type{typeof(Union{}} and do those early here - // otherwise the possibility of encountering `Type{Union{}}` in this intersection may - // be forcing us to do some extra work here whenever we see a typevar, even though - // the likelihood of that value actually occurring is frequently likely to be - // zero (or result in an ambiguous match) - targ = jl_atomic_load_relaxed(&cache->targ); // may be GC'd during type-intersection - jl_value_t *ml = mtcache_hash_lookup(targ, (jl_value_t*)jl_typeofbottom_type->name); - if (ml != jl_nothing) { - size_t search_slurp = closure->search_slurp; - closure->search_slurp = offs + 1; - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { - closure->search_slurp = search_slurp; - JL_GC_POP(); - return 0; - } - if (closure->search_slurp == 0) - exclude_typeofbottom = 1; - closure->search_slurp = search_slurp; - } - } - if (name != (jl_value_t*)jl_typeofbottom_type->name) { - targ = jl_atomic_load_relaxed(&cache->targ); // may be GC'd earlier - if (exclude_typeofbottom && name && jl_type_extract_name_precise(typetype, 1)) { - // attempt semi-direct lookup of types via their names + jl_array_t *targ = jl_atomic_load_relaxed(&cache->targ); + if (targ != (jl_array_t*)jl_an_empty_vec_any + && !(typetype && !jl_has_free_typevars(typetype) && !is_cache_leaf(typetype, 1))) { // cannot contain this, so don't bother with checking + if (name && !jl_is_typevar(typetype)) { + // semi-direct lookup of types via their names + if (jl_type_extract_name_precise(typetype, 1)) { // consider the type name first jl_value_t *ml = mtcache_hash_lookup(targ, (jl_value_t*)name); if (jl_is_array(ml)) { @@ -694,21 +557,33 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, if (is_cache_leaf(typetype, 1)) { ml = mtcache_hash_lookup((jl_array_t*)ml, typetype); if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { JL_GC_POP(); return 0; } + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; } } } else { - if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, 1, offs, closure)) { JL_GC_POP(); return 0; } + if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, (jl_value_t*)ty, 1, offs, closure)) return 0; } } else if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { JL_GC_POP(); return 0; } + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; } } else { - // else an array scan is required to consider all the possible subtypes - if (!jl_typemap_intersection_array_visitor(targ, exclude_typeofbottom && !maybe_kind ? ty : (jl_value_t*)jl_any_type, 3, offs, closure)) { JL_GC_POP(); return 0; } + // consider all of the possible subtypes + // TODO: the possibility of encountering `Type{Union{}}` in this intersection may + // be forcing us to do some extra work here whenever we see a typevar, even though + // the likelihood of that value actually occurring is frequently likely to be + // zero (or result in an ambiguous match) + if (!jl_typemap_intersection_array_visitor((jl_array_t*)targ, (jl_value_t*)ty, 3, offs, closure)) return 0; + } + } + else { + // else an array scan is required to check subtypes + // first, fast-path: optimized pre-intersection test to see if `ty` could intersect with any Type + if (typetype || !jl_has_empty_intersection((jl_value_t*)jl_type_type, ty)) { + targ = jl_atomic_load_relaxed(&cache->targ); // may be GC'd during type-intersection + if (!jl_typemap_intersection_array_visitor(targ, ty, 3, offs, closure)) return 0; } } } @@ -721,7 +596,7 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, if (jl_is_array(ml)) ml = mtcache_hash_lookup((jl_array_t*)ml, ty); if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) { JL_GC_POP(); return 0; } + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; } } else { @@ -730,87 +605,82 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, // direct lookup of leaf types jl_value_t *ml = mtcache_hash_lookup(cachearg1, name); if (jl_is_array(ml)) { - if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, 0, offs, closure)) { JL_GC_POP(); return 0; } + if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, (jl_value_t*)ty, 0, offs, closure)) return 0; } else { - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { JL_GC_POP(); return 0; } + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; } } else { // else an array scan is required to check subtypes - if (!jl_typemap_intersection_array_visitor(cachearg1, ty, 2, offs, closure)) { JL_GC_POP(); return 0; } + if (!jl_typemap_intersection_array_visitor(cachearg1, ty, 2, offs, closure)) return 0; } } } - // Next check for intersections with methods defined on Type{T}, where T was not concrete (it might even have been a TypeVar), but had an extractable TypeName - if (tname != (jl_array_t*)jl_an_empty_vec_any && maybe_type) { - if (!exclude_typeofbottom || (!typetype && jl_isa((jl_value_t*)jl_typeofbottom_type, ty))) { - // detect Type{Union{}}, Type{Type{Union{}}}, and Type{typeof(Union{}} and do those early here - // otherwise the possibility of encountering `Type{Union{}}` in this intersection may + jl_array_t *tname = jl_atomic_load_relaxed(&cache->tname); + if (tname != (jl_array_t*)jl_an_empty_vec_any) { + if (name && !jl_is_typevar(typetype)) { + // semi-direct lookup of types + // TODO: the possibility of encountering `Type{Union{}}` in this intersection may // be forcing us to do some extra work here whenever we see a typevar, even though // the likelihood of that value actually occurring is frequently likely to be // zero (or result in an ambiguous match) - tname = jl_atomic_load_relaxed(&cache->tname); // may be GC'd earlier - jl_value_t *ml = mtcache_hash_lookup(tname, (jl_value_t*)jl_typeofbottom_type->name); - if (ml != jl_nothing) { - size_t search_slurp = closure->search_slurp; - closure->search_slurp = offs + 1; - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { - closure->search_slurp = search_slurp; - JL_GC_POP(); - return 0; - } - if (closure->search_slurp == 0) - exclude_typeofbottom = 1; - closure->search_slurp = search_slurp; - } - } - if (exclude_typeofbottom && name && jl_type_extract_name_precise(typetype, 1)) { - // semi-direct lookup of types - // just consider the type and its direct super types jl_datatype_t *super = (jl_datatype_t*)jl_unwrap_unionall(((jl_typename_t*)name)->wrapper); - if (super->name == jl_typeofbottom_type->name) - super = super->super; // this was handled above - while (1) { - tname = jl_atomic_load_relaxed(&cache->tname); // reload after callback - jl_typemap_t *ml = mtcache_hash_lookup(tname, (jl_value_t*)super->name); - if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) { JL_GC_POP(); return 0; } + if (jl_type_extract_name_precise(typetype, 1)) { + // just consider the type and its direct super types + while (1) { + tname = jl_atomic_load_relaxed(&cache->tname); // reload after callback + jl_typemap_t *ml = mtcache_hash_lookup(tname, (jl_value_t*)super->name); + if (ml != jl_nothing) { + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; + } + if (super == jl_any_type) + break; + super = super->super; } - if (super == jl_any_type) - break; - super = super->super; + } + else { + // consider all of the possible subtypes + if (!jl_typemap_intersection_array_visitor(tname, (jl_value_t*)super, 3, offs, closure)) return 0; } } else { - // else an array scan is required to check subtypes of typetype too - tname = jl_atomic_load_relaxed(&cache->tname); // may be GC'd earlier - if (!jl_typemap_intersection_array_visitor(tname, exclude_typeofbottom && !maybe_kind ? ty : (jl_value_t*)jl_any_type, 3, offs, closure)) { JL_GC_POP(); return 0; } + // else an array scan is required to check subtypes + // first, fast-path: optimized pre-intersection test to see if `ty` could intersect with any Type + if (name || !jl_has_empty_intersection((jl_value_t*)jl_type_type, ty)) { + tname = jl_atomic_load_relaxed(&cache->tname); // may be GC'd during type-intersection + if (!jl_typemap_intersection_array_visitor(tname, (jl_value_t*)jl_any_type, 3, offs, closure)) return 0; + } } } jl_array_t *name1 = jl_atomic_load_relaxed(&cache->name1); if (name1 != (jl_array_t*)jl_an_empty_vec_any) { jl_value_t *name = jl_type_extract_name(ty); - if (name && jl_type_extract_name_precise(ty, 0)) { + if (name) { jl_datatype_t *super = (jl_datatype_t*)jl_unwrap_unionall(((jl_typename_t*)name)->wrapper); - // direct lookup of concrete types - while (1) { - name1 = jl_atomic_load_relaxed(&cache->name1); // reload after callback - jl_typemap_t *ml = mtcache_hash_lookup(name1, (jl_value_t*)super->name); - if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) { JL_GC_POP(); return 0; } + if (jl_type_extract_name_precise(ty, 0)) { + // direct lookup of concrete types + while (1) { + name1 = jl_atomic_load_relaxed(&cache->name1); // reload after callback + jl_typemap_t *ml = mtcache_hash_lookup(name1, (jl_value_t*)super->name); + if (ml != jl_nothing) { + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; + } + if (super == jl_any_type) + break; + super = super->super; } - if (super == jl_any_type) - break; - super = super->super; + } + else { + // consider all of the possible subtypes too + if (!jl_typemap_intersection_array_visitor(name1, (jl_value_t*)super, 2, offs, closure)) return 0; } } else { // else an array scan is required to check subtypes - if (!jl_typemap_intersection_array_visitor(name1, ty, 2, offs, closure)) { JL_GC_POP(); return 0; } + if (!jl_typemap_intersection_array_visitor(name1, (jl_value_t*)jl_any_type, 2, offs, closure)) return 0; } } - JL_GC_POP(); } if (!jl_typemap_intersection_node_visitor(jl_atomic_load_relaxed(&cache->linear), closure)) return 0; diff --git a/stdlib/Statistics.version b/stdlib/Statistics.version index 27197b12be54c..22857e138655a 100644 --- a/stdlib/Statistics.version +++ b/stdlib/Statistics.version @@ -1,4 +1,4 @@ STATISTICS_BRANCH = master -STATISTICS_SHA1 = a3feba2bb63f06b7f40024185e9fa5f6385e2510 +STATISTICS_SHA1 = e9ac70b760dcf87b77affe6c068548a3325d6e2b STATISTICS_GIT_URL := https://github.com/JuliaStats/Statistics.jl.git STATISTICS_TAR_URL = https://api.github.com/repos/JuliaStats/Statistics.jl/tarball/$1 diff --git a/test/abstractarray.jl b/test/abstractarray.jl index c5ff97deb6777..070e5d7a7b289 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -520,6 +520,9 @@ function test_primitives(::Type{T}, shape, ::Type{TestAbstractArray}) where T @test convert(Matrix, Y) == Y @test convert(Matrix, view(Y, 1:2, 1:2)) == Y @test_throws MethodError convert(Matrix, X) + + # convert(::Type{Union{}}, A::AbstractMatrix) + @test_throws MethodError convert(Union{}, X) end mutable struct TestThrowNoGetindex{T} <: AbstractVector{T} end diff --git a/test/ambiguous.jl b/test/ambiguous.jl index a1b973f30a70c..67fb16d3b7458 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -177,10 +177,12 @@ ambs = detect_ambiguities(Ambig48312) @test good end - # some ambiguities involving Union{} type parameters may be expected, but not required + # some ambiguities involving Union{} type parameters are expected, but not required let ambig = Set(detect_ambiguities(Core; recursive=true, ambiguous_bottom=true)) + m1 = which(Core.Compiler.convert, Tuple{Type{<:Core.IntrinsicFunction}, Any}) + m2 = which(Core.Compiler.convert, Tuple{Type{<:Nothing}, Any}) + pop!(ambig, (m1, m2)) @test !isempty(ambig) - @test length(ambig) < 30 end STDLIB_DIR = Sys.STDLIB diff --git a/test/core.jl b/test/core.jl index daec51ab5b566..a89d206182dbf 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1694,9 +1694,7 @@ end # issue #3221 let x = fill(nothing, 1) - @test_throws ErrorException("cannot convert a value to nothing for assignment") x[1] = 1 - x = Vector{Union{}}(undef, 1) - @test_throws ArgumentError("cannot convert a value to Union{} for assignment") x[1] = 1 + @test_throws MethodError x[1] = 1 end # issue #3220 @@ -4918,7 +4916,7 @@ struct f47209 x::Int f47209()::Nothing = new(1) end -@test_throws ErrorException("cannot convert a value to nothing for assignment") f47209() +@test_throws MethodError f47209() # issue #12096 let a = Val{Val{TypeVar(:_, Int)}}, diff --git a/test/missing.jl b/test/missing.jl index f06d1aad7a6b1..450b816ea3e57 100644 --- a/test/missing.jl +++ b/test/missing.jl @@ -21,8 +21,8 @@ end @test convert(Union{Nothing, Missing}, nothing) === nothing @test convert(Union{Missing, Nothing, Float64}, 1) === 1.0 - @test_throws ErrorException("cannot convert a value to missing for assignment") convert(Missing, 1) - @test_throws ErrorException("cannot convert a value to missing for assignment") convert(Union{Nothing, Missing}, 1) + @test_throws MethodError convert(Missing, 1) + @test_throws MethodError convert(Union{Nothing, Missing}, 1) @test_throws MethodError convert(Union{Int, Missing}, "a") end diff --git a/test/some.jl b/test/some.jl index e49fc586a3a6e..27d50ca354a49 100644 --- a/test/some.jl +++ b/test/some.jl @@ -33,7 +33,7 @@ @test convert(Union{Int, Nothing}, 1) === 1 @test convert(Union{Int, Nothing}, 1.0) === 1 @test convert(Nothing, nothing) === nothing -@test_throws ErrorException("cannot convert a value to nothing for assignment") convert(Nothing, 1) +@test_throws MethodError convert(Nothing, 1) ## show() diff --git a/test/specificity.jl b/test/specificity.jl index 9b605444bad42..5808ac71fa54b 100644 --- a/test/specificity.jl +++ b/test/specificity.jl @@ -311,8 +311,3 @@ let A = Tuple{Type{SubString{S}},AbstractString} where S<:AbstractString, @test args_morespecific(B, C) @test args_morespecific(A, C) end - -@test args_morespecific(Tuple{Type{Union{}}, Any}, Tuple{Any, Type{Union{}}}) -@test args_morespecific(Tuple{typeof(Union{}), Any}, Tuple{Any, Type{Union{}}}) -@test args_morespecific(Tuple{Type{Union{}}, Type{Union{}}, Any}, Tuple{Type{Union{}}, Any, Type{Union{}}}) -@test args_morespecific(Tuple{Type{Union{}}, Type{Union{}}, Any, Type{Union{}}}, Tuple{Type{Union{}}, Any, Type{Union{}}, Type{Union{}}}) From 9c4724b3dfa0f8a3d2cace709189780282b63348 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Fri, 21 Apr 2023 19:11:31 -0500 Subject: [PATCH 568/775] Support and use two argument at-irrational (#46054) --- base/irrationals.jl | 30 ++++++++++++++++++++---------- base/mathconstants.jl | 10 +++++----- test/numbers.jl | 14 ++++++++++++++ 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/base/irrationals.jl b/base/irrationals.jl index 72341fea71690..6513e3269a4d7 100644 --- a/base/irrationals.jl +++ b/base/irrationals.jl @@ -165,11 +165,13 @@ end round(x::Irrational, r::RoundingMode) = round(float(x), r) """ - @irrational sym val def - @irrational(sym, val, def) + @irrational sym [val] def -Define a new `Irrational` value, `sym`, with pre-computed `Float64` value `val`, -and arbitrary-precision definition in terms of `BigFloat`s given by the expression `def`. +Define a new `Irrational` value, `sym`, with arbitrary-precision definition in terms +of `BigFloat`s given by the expression `def`. + +Optionally provide a pre-computed `Float64` value `val` which must equal `Float64(def)`. +`val` will be computed automatically if omitted. An `AssertionError` is thrown when either `big(def) isa BigFloat` or `Float64(val) == Float64(def)` returns `false`. @@ -184,24 +186,30 @@ returns `false`. # Examples ```jldoctest -julia> Base.@irrational(twoπ, 6.2831853071795864769, 2*big(π)) +julia> Base.@irrational twoπ 2*big(π) julia> twoπ twoπ = 6.2831853071795... -julia> Base.@irrational sqrt2 1.4142135623730950488 √big(2) +julia> Base.@irrational sqrt2 1.4142135623730950488 √big(2) julia> sqrt2 sqrt2 = 1.4142135623730... -julia> Base.@irrational sqrt2 1.4142135623730950488 big(2) +julia> Base.@irrational sqrt2 1.4142135623730950488 big(2) ERROR: AssertionError: big($(Expr(:escape, :sqrt2))) isa BigFloat -julia> Base.@irrational sqrt2 1.41421356237309 √big(2) +julia> Base.@irrational sqrt2 1.41421356237309 √big(2) ERROR: AssertionError: Float64($(Expr(:escape, :sqrt2))) == Float64(big($(Expr(:escape, :sqrt2)))) ``` """ macro irrational(sym, val, def) + irrational(sym, val, def) +end +macro irrational(sym, def) + irrational(sym, :(big($(esc(sym)))), def) +end +function irrational(sym, val, def) esym = esc(sym) qsym = esc(Expr(:quote, sym)) bigconvert = isa(def,Symbol) ? quote @@ -221,8 +229,10 @@ macro irrational(sym, val, def) quote const $esym = Irrational{$qsym}() $bigconvert - Base.Float64(::Irrational{$qsym}) = $val - Base.Float32(::Irrational{$qsym}) = $(Float32(val)) + let v = $val, v64 = Float64(v), v32 = Float32(v) + Base.Float64(::Irrational{$qsym}) = v64 + Base.Float32(::Irrational{$qsym}) = v32 + end @assert isa(big($esym), BigFloat) @assert Float64($esym) == Float64(big($esym)) @assert Float32($esym) == Float32(big($esym)) diff --git a/base/mathconstants.jl b/base/mathconstants.jl index 3bb4bb52ad07f..4bb8c409acf00 100644 --- a/base/mathconstants.jl +++ b/base/mathconstants.jl @@ -10,11 +10,11 @@ module MathConstants export π, pi, ℯ, e, γ, eulergamma, catalan, φ, golden -Base.@irrational π 3.14159265358979323846 pi -Base.@irrational ℯ 2.71828182845904523536 exp(big(1)) -Base.@irrational γ 0.57721566490153286061 euler -Base.@irrational φ 1.61803398874989484820 (1+sqrt(big(5)))/2 -Base.@irrational catalan 0.91596559417721901505 catalan +Base.@irrational π pi +Base.@irrational ℯ exp(big(1)) +Base.@irrational γ euler +Base.@irrational φ (1+sqrt(big(5)))/2 +Base.@irrational catalan catalan # aliases """ diff --git a/test/numbers.jl b/test/numbers.jl index d7baecd847c8f..9c2c4d1d1fdb8 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -2966,6 +2966,20 @@ end end end +Base.@irrational irrational_1548_pi 4863.185427757 1548big(pi) +Base.@irrational irrational_inv_1548_pi 1/big(irrational_1548_pi) +@testset "@irrational" begin + @test irrational_1548_pi ≈ 1548big(pi) + @test Float64(irrational_1548_pi) == 1548π + @test irrational_1548_pi ≈ 1548pi + @test irrational_1548_pi != 1548pi + + @test irrational_inv_1548_pi ≈ inv(1548big(pi)) + @test Float64(irrational_inv_1548_pi) == 1/(1548π) + @test irrational_inv_1548_pi ≈ inv(1548pi) + @test irrational_inv_1548_pi != inv(1548pi) +end + @testset "modf" begin @testset "remd" begin denorm_min = nextfloat(0.0) From 357bcdd302e10c2f0e827a8dad625e42719e106c Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sat, 22 Apr 2023 14:09:10 +0900 Subject: [PATCH 569/775] annotate the signature type of the first argument of `kwcall` method (#49439) This commit annotates the signature type of the first argument of `kwcall` method as `kwargs::NamedTuple`. Previously it's annotated as `::Any` but only `NamedTuple` object is supported (and synthesized by the frontend) as the first argument to `kwcall` method. --- base/Base.jl | 4 ++-- base/errorshow.jl | 2 +- base/methodshow.jl | 2 +- base/reflection.jl | 4 ++-- src/julia-syntax.scm | 3 ++- stdlib/Serialization/src/Serialization.jl | 2 +- test/keywordargs.jl | 2 +- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index a824cf8487193..87a8d94c866a2 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -125,7 +125,7 @@ include("options.jl") # define invoke(f, T, args...; kwargs...), without kwargs wrapping # to forward to invoke -function Core.kwcall(kwargs, ::typeof(invoke), f, T, args...) +function Core.kwcall(kwargs::NamedTuple, ::typeof(invoke), f, T, args...) @inline # prepend kwargs and f to the invoked from the user T = rewrap_unionall(Tuple{Core.Typeof(kwargs), Core.Typeof(f), (unwrap_unionall(T)::DataType).parameters...}, T) @@ -136,7 +136,7 @@ setfield!(typeof(invoke).name.mt, :max_args, 3, :monotonic) # invoke, f, T, args # define applicable(f, T, args...; kwargs...), without kwargs wrapping # to forward to applicable -function Core.kwcall(kwargs, ::typeof(applicable), @nospecialize(args...)) +function Core.kwcall(kwargs::NamedTuple, ::typeof(applicable), @nospecialize(args...)) @inline return applicable(Core.kwcall, kwargs, args...) end diff --git a/base/errorshow.jl b/base/errorshow.jl index d3d2feda1f9b5..0b407f9221c28 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -912,7 +912,7 @@ function process_backtrace(t::Vector, limit::Int=typemax(Int); skipC = true) code = lkup.linfo if code isa MethodInstance def = code.def - if def isa Method && def.name !== :kwcall && def.sig <: Tuple{typeof(Core.kwcall),Any,Any,Vararg} + if def isa Method && def.name !== :kwcall && def.sig <: Tuple{typeof(Core.kwcall),NamedTuple,Any,Vararg} # hide kwcall() methods, which are probably internal keyword sorter methods # (we print the internal method instead, after demangling # the argument list, since it has the right line number info) diff --git a/base/methodshow.jl b/base/methodshow.jl index a45b89c6ccf63..ab6412a95395d 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -80,7 +80,7 @@ end function kwarg_decl(m::Method, kwtype = nothing) if m.sig !== Tuple # OpaqueClosure or Builtin kwtype = typeof(Core.kwcall) - sig = rewrap_unionall(Tuple{kwtype, Any, (unwrap_unionall(m.sig)::DataType).parameters...}, m.sig) + sig = rewrap_unionall(Tuple{kwtype, NamedTuple, (unwrap_unionall(m.sig)::DataType).parameters...}, m.sig) kwli = ccall(:jl_methtable_lookup, Any, (Any, Any, UInt), kwtype.name.mt, sig, get_world_counter()) if kwli !== nothing kwli = kwli::Method diff --git a/base/reflection.jl b/base/reflection.jl index ac8d2752a4719..fc45099e49ab3 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1741,7 +1741,7 @@ function hasmethod(@nospecialize(f), @nospecialize(t)) return Core._hasmethod(f, t isa Type ? t : to_tuple_type(t)) end -function Core.kwcall(kwargs, ::typeof(hasmethod), @nospecialize(f), @nospecialize(t)) +function Core.kwcall(kwargs::NamedTuple, ::typeof(hasmethod), @nospecialize(f), @nospecialize(t)) world = kwargs.world::UInt # make sure this is the only local, to avoid confusing kwarg_decl() return ccall(:jl_gf_invoke_lookup, Any, (Any, Any, UInt), signature_type(f, t), nothing, world) !== nothing end @@ -1752,7 +1752,7 @@ function hasmethod(f, t, kwnames::Tuple{Vararg{Symbol}}; world::UInt=get_world_c t = to_tuple_type(t) ft = Core.Typeof(f) u = unwrap_unionall(t)::DataType - tt = rewrap_unionall(Tuple{typeof(Core.kwcall), typeof(pairs((;))), ft, u.parameters...}, t) + tt = rewrap_unionall(Tuple{typeof(Core.kwcall), NamedTuple, ft, u.parameters...}, t) match = ccall(:jl_gf_invoke_lookup, Any, (Any, Any, UInt), tt, nothing, world) match === nothing && return false kws = ccall(:jl_uncompress_argnames, Array{Symbol,1}, (Any,), (match::Method).slot_syms) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index e6a1dee4e8d10..ccf566ed87885 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -507,6 +507,7 @@ positional-sparams))) sparams)) (kw (gensy)) + (kwdecl `(|::| ,kw (core NamedTuple))) (rkw (if (null? restkw) (make-ssavalue) (symbol (string (car restkw) "...")))) (restkw (map (lambda (v) `(|::| ,v (call (top pairs) (core NamedTuple)))) restkw)) (mangled (let ((und (and name (undot-name name)))) @@ -555,7 +556,7 @@ `((|::| ;; if there are optional positional args, we need to be able to reference the function name ,(if (any kwarg? pargl) (gensy) UNUSED) - (call (core kwftype) ,ftype)) ,kw ,@pargl ,@vararg) + (call (core kwftype) ,ftype)) ,kwdecl ,@pargl ,@vararg) `(block ;; propagate method metadata to keyword sorter ,@(map propagate-method-meta (filter meta? prologue)) diff --git a/stdlib/Serialization/src/Serialization.jl b/stdlib/Serialization/src/Serialization.jl index 630185ebd575a..dd901d6910abf 100644 --- a/stdlib/Serialization/src/Serialization.jl +++ b/stdlib/Serialization/src/Serialization.jl @@ -1368,7 +1368,7 @@ function deserialize_typename(s::AbstractSerializer, number) end else # old object format -- try to forward from old to new - @eval Core.kwcall(kwargs, f::$ty, args...) = $kws(kwargs, f, args...) + @eval Core.kwcall(kwargs::NamedTuple, f::$ty, args...) = $kws(kwargs, f, args...) end end end diff --git a/test/keywordargs.jl b/test/keywordargs.jl index 7211cfa85701d..0aed0544b7e2e 100644 --- a/test/keywordargs.jl +++ b/test/keywordargs.jl @@ -389,7 +389,7 @@ f41416(a...="a"; b=true) = (b, a) @test f41416(3; b=false) === (false, (3,)) Core.kwcall(i::Int) = "hi $i" -let m = first(methods(Core.kwcall, (Any,typeof(kwf1),Vararg))) +let m = first(methods(Core.kwcall, (NamedTuple,typeof(kwf1),Vararg))) @test m.name === :kwf1 @test Core.kwcall(1) == "hi 1" @test which(Core.kwcall, (Int,)).name === :kwcall From 4044096c15f03a9f6bf4b777d93048a299f29334 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sat, 22 Apr 2023 20:16:57 +0530 Subject: [PATCH 570/775] Adjoint for diagonal should be lazy (#48443) --- stdlib/LinearAlgebra/src/diagonal.jl | 3 ++- stdlib/LinearAlgebra/test/diagonal.jl | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 7c54ce4009e33..ec1bca909ce7b 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -675,7 +675,8 @@ end conj(D::Diagonal) = Diagonal(conj(D.diag)) transpose(D::Diagonal{<:Number}) = D transpose(D::Diagonal) = Diagonal(transpose.(D.diag)) -adjoint(D::Diagonal{<:Number}) = conj(D) +adjoint(D::Diagonal{<:Number}) = Diagonal(vec(adjoint(D.diag))) +adjoint(D::Diagonal{<:Number,<:Base.ReshapedArray{<:Number,1,<:Adjoint}}) = Diagonal(adjoint(parent(D.diag))) adjoint(D::Diagonal) = Diagonal(adjoint.(D.diag)) permutedims(D::Diagonal) = D permutedims(D::Diagonal, perm) = (Base.checkdims_perm(D, D, perm); D) diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index b8f3789ae2674..5f169d21ff6fb 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -381,9 +381,17 @@ Random.seed!(1) @testset "conj and transpose" begin @test transpose(D) == D - if elty <: BlasComplex + if elty <: Real + @test transpose(D) === D + @test adjoint(D) === D + elseif elty <: BlasComplex @test Array(conj(D)) ≈ conj(DM) @test adjoint(D) == conj(D) + local D2 = copy(D) + local D2adj = adjoint(D2) + D2adj[1,1] = rand(eltype(D2adj)) + @test D2[1,1] == adjoint(D2adj[1,1]) + @test D2adj' === D2 end # Translates to Ac/t_mul_B, which is specialized after issue 21286 @test(D' * vv == conj(D) * vv) From 6b79e8c7436089ad12a95fe4a17cfb58f0e0f750 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Sun, 23 Apr 2023 00:59:49 +0800 Subject: [PATCH 571/775] Subtype: Improve `simple_meet` resolution for `Union` inputs (#49376) * Improve `simple_meet` resolution. * Fix for many-to-one cases. * Test disjoint via `jl_has_empty_intersection` --- src/jltypes.c | 135 +++++++++++++++++++++++++++++++++++++++++++----- src/subtype.c | 15 ++---- test/subtype.jl | 8 +++ 3 files changed, 132 insertions(+), 26 deletions(-) diff --git a/src/jltypes.c b/src/jltypes.c index 482f21a14c76e..bf15611de4587 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -485,19 +485,19 @@ static int union_sort_cmp(jl_value_t *a, jl_value_t *b) JL_NOTSAFEPOINT } } -static int count_union_components(jl_value_t **types, size_t n) +static int count_union_components(jl_value_t **types, size_t n, int widen) { size_t i, c = 0; for (i = 0; i < n; i++) { jl_value_t *e = types[i]; while (jl_is_uniontype(e)) { jl_uniontype_t *u = (jl_uniontype_t*)e; - c += count_union_components(&u->a, 1); + c += count_union_components(&u->a, 1, widen); e = u->b; } - if (jl_is_unionall(e) && jl_is_uniontype(jl_unwrap_unionall(e))) { + if (widen && jl_is_unionall(e) && jl_is_uniontype(jl_unwrap_unionall(e))) { jl_uniontype_t *u = (jl_uniontype_t*)jl_unwrap_unionall(e); - c += count_union_components(&u->a, 2); + c += count_union_components(&u->a, 2, widen); } else { c++; @@ -506,21 +506,21 @@ static int count_union_components(jl_value_t **types, size_t n) return c; } -static void flatten_type_union(jl_value_t **types, size_t n, jl_value_t **out, size_t *idx) +static void flatten_type_union(jl_value_t **types, size_t n, jl_value_t **out, size_t *idx, int widen) { size_t i; for (i = 0; i < n; i++) { jl_value_t *e = types[i]; while (jl_is_uniontype(e)) { jl_uniontype_t *u = (jl_uniontype_t*)e; - flatten_type_union(&u->a, 1, out, idx); + flatten_type_union(&u->a, 1, out, idx, widen); e = u->b; } - if (jl_is_unionall(e) && jl_is_uniontype(jl_unwrap_unionall(e))) { + if (widen && jl_is_unionall(e) && jl_is_uniontype(jl_unwrap_unionall(e))) { // flatten this UnionAll into place by switching the union and unionall jl_uniontype_t *u = (jl_uniontype_t*)jl_unwrap_unionall(e); size_t old_idx = 0; - flatten_type_union(&u->a, 2, out, idx); + flatten_type_union(&u->a, 2, out, idx, widen); for (; old_idx < *idx; old_idx++) out[old_idx] = jl_rewrap_unionall(out[old_idx], e); } @@ -560,11 +560,11 @@ JL_DLLEXPORT jl_value_t *jl_type_union(jl_value_t **ts, size_t n) if (n == 1) return ts[0]; - size_t nt = count_union_components(ts, n); + size_t nt = count_union_components(ts, n, 1); jl_value_t **temp; JL_GC_PUSHARGS(temp, nt+1); size_t count = 0; - flatten_type_union(ts, n, temp, &count); + flatten_type_union(ts, n, temp, &count, 1); assert(count == nt); size_t j; for (i = 0; i < nt; i++) { @@ -641,14 +641,14 @@ static int simple_subtype2(jl_value_t *a, jl_value_t *b, int hasfree) jl_value_t *simple_union(jl_value_t *a, jl_value_t *b) { - size_t nta = count_union_components(&a, 1); - size_t ntb = count_union_components(&b, 1); + size_t nta = count_union_components(&a, 1, 1); + size_t ntb = count_union_components(&b, 1, 1); size_t nt = nta + ntb; jl_value_t **temp; JL_GC_PUSHARGS(temp, nt+1); size_t count = 0; - flatten_type_union(&a, 1, temp, &count); - flatten_type_union(&b, 1, temp, &count); + flatten_type_union(&a, 1, temp, &count, 1); + flatten_type_union(&b, 1, temp, &count, 1); assert(count == nt); size_t i, j; size_t ra = nta, rb = ntb; @@ -717,6 +717,113 @@ jl_value_t *simple_union(jl_value_t *a, jl_value_t *b) return tu; } +int obviously_disjoint(jl_value_t *a, jl_value_t *b, int specificity); + +static int simple_disjoint(jl_value_t *a, jl_value_t *b, int hasfree) +{ + if (jl_is_uniontype(b)) { + jl_value_t *b1 = ((jl_uniontype_t *)b)->a, *b2 = ((jl_uniontype_t *)b)->b; + JL_GC_PUSH2(&b1, &b2); + int res = simple_disjoint(a, b1, hasfree) && simple_disjoint(a, b2, hasfree); + JL_GC_POP(); + return res; + } + if (!hasfree && !jl_has_free_typevars(b)) + return jl_has_empty_intersection(a, b); + return obviously_disjoint(a, b, 0); +} + +jl_value_t *simple_intersect(jl_value_t *a, jl_value_t *b, int overesi) +{ + // Unlike `Union`, we don't unwrap `UnionAll` here to avoid possible widening. + size_t nta = count_union_components(&a, 1, 0); + size_t ntb = count_union_components(&b, 1, 0); + size_t nt = nta + ntb; + jl_value_t **temp; + JL_GC_PUSHARGS(temp, nt+1); + size_t count = 0; + flatten_type_union(&a, 1, temp, &count, 0); + flatten_type_union(&b, 1, temp, &count, 0); + assert(count == nt); + size_t i, j; + // first remove disjoint elements. + for (i = 0; i < nt; i++) { + if (simple_disjoint(temp[i], (i < nta ? b : a), jl_has_free_typevars(temp[i]))) + temp[i] = NULL; + } + // then check subtyping. + // stemp[k] == -1 : ∃i temp[k] >:ₛ temp[i] + // stemp[k] == 1 : ∃i temp[k] == temp[i] + // stemp[k] == 2 : ∃i temp[k] <:ₛ temp[i] + int8_t *stemp = (int8_t *)alloca(count); + memset(stemp, 0, count); + for (i = 0; i < nta; i++) { + if (temp[i] == NULL) continue; + int hasfree = jl_has_free_typevars(temp[i]); + for (j = nta; j < nt; j++) { + if (temp[j] == NULL) continue; + int subs = simple_subtype2(temp[i], temp[j], hasfree || jl_has_free_typevars(temp[j])); + int subab = subs & 1, subba = subs >> 1; + if (subba && !subab) { + stemp[i] = -1; + if (stemp[j] >= 0) stemp[j] = 2; + } + else if (subab && !subba) { + stemp[j] = -1; + if (stemp[i] >= 0) stemp[i] = 2; + } + else if (subs) { + if (stemp[i] == 0) stemp[i] = 1; + if (stemp[j] == 0) stemp[j] = 1; + } + } + } + int subs[2] = {1, 1}, rs[2] = {1, 1}; + for (i = 0; i < nt; i++) { + subs[i >= nta] &= (temp[i] == NULL || stemp[i] > 0); + rs[i >= nta] &= (temp[i] != NULL && stemp[i] > 0); + } + // return a(b) if a(b) <: b(a) + if (rs[0]) { + JL_GC_POP(); + return a; + } + if (rs[1]) { + JL_GC_POP(); + return b; + } + // return `Union{}` for `merge_env` if we can't prove `<:` or `>:` + if (!overesi && !subs[0] && !subs[1]) { + JL_GC_POP(); + return jl_bottom_type; + } + nt = subs[0] ? nta : subs[1] ? nt : nt; + i = subs[0] ? 0 : subs[1] ? nta : 0; + count = nt - i; + if (!subs[0] && !subs[1]) { + // prepare for over estimation + // only preserve `a` with strict <:, but preserve `b` without strict >: + for (j = 0; j < nt; j++) { + if (stemp[j] < (j < nta ? 2 : 0)) + temp[j] = NULL; + } + } + isort_union(&temp[i], count); + temp[nt] = jl_bottom_type; + size_t k; + for (k = nt; k-- > i; ) { + if (temp[k] != NULL) { + if (temp[nt] == jl_bottom_type) + temp[nt] = temp[k]; + else + temp[nt] = jl_new_struct(jl_uniontype_type, temp[k], temp[nt]); + } + } + assert(temp[nt] != NULL); + jl_value_t *tu = temp[nt]; + JL_GC_POP(); + return tu; +} // unionall types ------------------------------------------------------------- diff --git a/src/subtype.c b/src/subtype.c index 20ef6139ee886..336d697423845 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -447,7 +447,7 @@ static int obviously_in_union(jl_value_t *u, jl_value_t *x) return obviously_egal(u, x); } -static int obviously_disjoint(jl_value_t *a, jl_value_t *b, int specificity) +int obviously_disjoint(jl_value_t *a, jl_value_t *b, int specificity) { if (a == b || a == (jl_value_t*)jl_any_type || b == (jl_value_t*)jl_any_type) return 0; @@ -559,6 +559,7 @@ static jl_value_t *simple_join(jl_value_t *a, jl_value_t *b) return simple_union(a, b); } +jl_value_t *simple_intersect(jl_value_t *a, jl_value_t *b, int overesi); // Compute a greatest lower bound of `a` and `b` // For the subtype path, we need to over-estimate this by returning `b` in many cases. // But for `merge_env`, we'd better under-estimate and return a `Union{}` @@ -570,10 +571,6 @@ static jl_value_t *simple_meet(jl_value_t *a, jl_value_t *b, int overesi) return a; if (!(jl_is_type(a) || jl_is_typevar(a)) || !(jl_is_type(b) || jl_is_typevar(b))) return jl_bottom_type; - if (jl_is_uniontype(a) && obviously_in_union(a, b)) - return b; - if (jl_is_uniontype(b) && obviously_in_union(b, a)) - return a; if (jl_is_kind(a) && jl_is_type_type(b) && jl_typeof(jl_tparam0(b)) == a) return b; if (jl_is_kind(b) && jl_is_type_type(a) && jl_typeof(jl_tparam0(a)) == b) @@ -582,13 +579,7 @@ static jl_value_t *simple_meet(jl_value_t *a, jl_value_t *b, int overesi) return a; if (jl_is_typevar(b) && obviously_egal(a, ((jl_tvar_t*)b)->ub)) return b; - if (obviously_disjoint(a, b, 0)) - return jl_bottom_type; - if (!jl_has_free_typevars(a) && !jl_has_free_typevars(b)) { - if (jl_subtype(a, b)) return a; - if (jl_subtype(b, a)) return b; - } - return overesi ? b : jl_bottom_type; + return simple_intersect(a, b, overesi); } // main subtyping algorithm diff --git a/test/subtype.jl b/test/subtype.jl index aad1424a2d66c..b38588155ef64 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2491,6 +2491,14 @@ end @test !<:(Tuple{Type{Int}, Int}, Tuple{Type{Union{Int, T}}, T} where T<:Union{Int8,Int16}) @test <:(Tuple{Type{Int}, Int}, Tuple{Type{Union{Int, T}}, T} where T<:Union{Int8,Int}) +#issue #49354 (requires assertions enabled) +@test !<:(Tuple{Type{Union{Int, Val{1}}}, Int}, Tuple{Type{Union{Int, T1}}, T1} where T1<:Val) +@test !<:(Tuple{Type{Union{Int, Val{1}}}, Int}, Tuple{Type{Union{Int, T1}}, T1} where T1<:Union{Val,Pair}) +@test <:(Tuple{Type{Union{Int, Val{1}}}, Int}, Tuple{Type{Union{Int, T1}}, T1} where T1<:Union{Integer,Val}) +@test <:(Tuple{Type{Union{Int, Int8}}, Int}, Tuple{Type{Union{Int, T1}}, T1} where T1<:Integer) +@test !<:(Tuple{Type{Union{Pair{Int, Any}, Pair{Int, Int}}}, Pair{Int, Any}}, + Tuple{Type{Union{Pair{Int, Any}, T1}}, T1} where T1<:(Pair{T,T} where {T})) + let A = Tuple{Type{T}, T, Val{T}} where T, B = Tuple{Type{S}, Val{S}, Val{S}} where S @test_broken typeintersect(A, B) != Union{} From b1c0eac0d8d1acecd321cea26459ff6443e9aaa9 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Sat, 22 Apr 2023 20:37:09 +0200 Subject: [PATCH 572/775] make extension not load twice on workers (#49441) --- base/loading.jl | 5 +++ stdlib/Distributed/src/Distributed.jl | 3 ++ test/loading.jl | 47 +++++++++++++++++---------- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 3cc9215814f18..7eb928eb385bf 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1189,9 +1189,12 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, <:An end end +loading_extension::Bool = false function run_extension_callbacks(extid::ExtensionId) assert_havelock(require_lock) succeeded = try + # Used by Distributed to now load extensions in the package callback + global loading_extension = true _require_prelocked(extid.id) @debug "Extension $(extid.id.name) of $(extid.parentid.name) loaded" true @@ -1201,6 +1204,8 @@ function run_extension_callbacks(extid::ExtensionId) @error "Error during loading of extension $(extid.id.name) of $(extid.parentid.name), \ use `Base.retry_load_extensions()` to retry." exception=errs false + finally + global loading_extension = false end return succeeded end diff --git a/stdlib/Distributed/src/Distributed.jl b/stdlib/Distributed/src/Distributed.jl index 28933e32e8bb8..a7c5b1778b144 100644 --- a/stdlib/Distributed/src/Distributed.jl +++ b/stdlib/Distributed/src/Distributed.jl @@ -76,6 +76,9 @@ function _require_callback(mod::Base.PkgId) # broadcast top-level (e.g. from Main) import/using from node 1 (only) @sync for p in procs() p == 1 && continue + # Extensions are already loaded on workers by their triggers being loaded + # so no need to fire the callback upon extension being loaded on master. + Base.loading_extension && continue @async_unwrap remotecall_wait(p) do Base.require(mod) nothing diff --git a/test/loading.jl b/test/loading.jl index 9b29697b31160..13e46fc856990 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1006,34 +1006,47 @@ end try proj = joinpath(@__DIR__, "project", "Extensions", "HasDepWithExtensions.jl") - function gen_extension_cmd(compile) - ```$(Base.julia_cmd()) $compile --startup-file=no -e ' - begin - push!(empty!(DEPOT_PATH), '$(repr(depot_path))') - using HasExtensions - Base.get_extension(HasExtensions, :Extension) === nothing || error("unexpectedly got an extension") - HasExtensions.ext_loaded && error("ext_loaded set") - using HasDepWithExtensions - Base.get_extension(HasExtensions, :Extension).extvar == 1 || error("extvar in Extension not set") - HasExtensions.ext_loaded || error("ext_loaded not set") - HasExtensions.ext_folder_loaded && error("ext_folder_loaded set") - HasDepWithExtensions.do_something() || error("do_something errored") - using ExtDep2 - HasExtensions.ext_folder_loaded || error("ext_folder_loaded not set") - end - ' - ``` + function gen_extension_cmd(compile, distr=false) + load_distr = distr ? "using Distributed; addprocs(1)" : "" + ew = distr ? "@everywhere" : "" + cmd = """ + $load_distr + begin + $ew push!(empty!(DEPOT_PATH), $(repr(depot_path))) + using HasExtensions + $ew using HasExtensions + $ew Base.get_extension(HasExtensions, :Extension) === nothing || error("unexpectedly got an extension") + $ew HasExtensions.ext_loaded && error("ext_loaded set") + using HasDepWithExtensions + $ew using HasDepWithExtensions + $ew Base.get_extension(HasExtensions, :Extension).extvar == 1 || error("extvar in Extension not set") + $ew HasExtensions.ext_loaded || error("ext_loaded not set") + $ew HasExtensions.ext_folder_loaded && error("ext_folder_loaded set") + $ew HasDepWithExtensions.do_something() || error("do_something errored") + using ExtDep2 + $ew using ExtDep2 + $ew HasExtensions.ext_folder_loaded || error("ext_folder_loaded not set") + end + """ + return `$(Base.julia_cmd()) $compile --startup-file=no -e $cmd` end for compile in (`--compiled-modules=no`, ``, ``) # Once when requiring precompilation, once where it is already precompiled cmd = gen_extension_cmd(compile) cmd = addenv(cmd, "JULIA_LOAD_PATH" => proj) cmd = pipeline(cmd; stdout, stderr) + @show compile @test success(cmd) end sep = Sys.iswindows() ? ';' : ':' + cmd = gen_extension_cmd(``, true) + cmd = addenv(cmd, "JULIA_LOAD_PATH" => join([proj, "@stdlib"], sep)) + str = read(cmd, String) + @test !occursin("Error during loading of extension", str) + @test !occursin("ConcurrencyViolationError", str) + # 48351 cmd = gen_extension_cmd(``) cmd = addenv(cmd, "JULIA_LOAD_PATH" => join([mktempdir(), proj], sep)) From 319815994d87ee23df2c4e5f468f7dd4d7b2eb0c Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Sat, 22 Apr 2023 13:49:59 -0500 Subject: [PATCH 573/775] Remove unnecessary definition of `eltype(::Type{BitSet})` (#49462) --- base/bitset.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/base/bitset.jl b/base/bitset.jl index 6e18f7f472d42..d1acdb0e5176f 100644 --- a/base/bitset.jl +++ b/base/bitset.jl @@ -38,8 +38,6 @@ end @inline intoffset(s::BitSet) = s.offset << 6 -eltype(::Type{BitSet}) = Int - empty(s::BitSet, ::Type{Int}=Int) = BitSet() emptymutable(s::BitSet, ::Type{Int}=Int) = BitSet() From 5ea1a4147abe51d33ecb7d2d88fcc1285003ef69 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Sat, 22 Apr 2023 13:50:37 -0500 Subject: [PATCH 574/775] Remove unnecessary allocation in `BitSet()` (#49461) --- base/bitset.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/bitset.jl b/base/bitset.jl index d1acdb0e5176f..5ce07389c771e 100644 --- a/base/bitset.jl +++ b/base/bitset.jl @@ -15,7 +15,7 @@ mutable struct BitSet <: AbstractSet{Int} # 1st stored Int equals 64*offset offset::Int - BitSet() = new(sizehint!(zeros(UInt64, 0), 4), NO_OFFSET) + BitSet() = new(resize!(Vector{UInt64}(undef, 4), 0), NO_OFFSET) end """ From a34261f526c0f85a7cacaf2f9e4f17af4af7152d Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Sat, 22 Apr 2023 20:44:16 +0000 Subject: [PATCH 575/775] Add ITTAPI timing functions to JL_TIMING (#49448) --- src/codegen.cpp | 6 +-- src/gc.c | 8 ++-- src/jitlayers.cpp | 4 +- src/threading.c | 1 + src/timing.c | 34 +++++++++----- src/timing.h | 117 ++++++++++++++++++++++++++++++++-------------- 6 files changed, 115 insertions(+), 55 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 3cde68c60ced3..b50110a20a8fc 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8435,7 +8435,7 @@ jl_llvm_functions_t jl_emit_code( jl_value_t *jlrettype, jl_codegen_params_t ¶ms) { - JL_TIMING(CODEGEN, CODEGEN); + JL_TIMING(CODEGEN, CODEGEN_LLVM); jl_timing_show_func_sig((jl_value_t *)li->specTypes, JL_TIMING_CURRENT_BLOCK); // caller must hold codegen_lock jl_llvm_functions_t decls = {}; @@ -8478,7 +8478,7 @@ jl_llvm_functions_t jl_emit_codeinst( jl_code_info_t *src, jl_codegen_params_t ¶ms) { - JL_TIMING(CODEGEN, CODEGEN); + JL_TIMING(CODEGEN, CODEGEN_Codeinst); jl_timing_show_method_instance(codeinst->def, JL_TIMING_CURRENT_BLOCK); JL_GC_PUSH1(&src); if (!src) { @@ -8558,7 +8558,7 @@ void jl_compile_workqueue( Module &original, jl_codegen_params_t ¶ms, CompilationPolicy policy) { - JL_TIMING(CODEGEN, CODEGEN); + JL_TIMING(CODEGEN, CODEGEN_Workqueue); jl_code_info_t *src = NULL; JL_GC_PUSH1(&src); while (!params.workqueue.empty()) { diff --git a/src/gc.c b/src/gc.c index 8653b8a05851f..388cf7fa6a671 100644 --- a/src/gc.c +++ b/src/gc.c @@ -311,7 +311,7 @@ NOINLINE uintptr_t gc_get_stack_ptr(void) void jl_gc_wait_for_the_world(jl_ptls_t* gc_all_tls_states, int gc_n_threads) { - JL_TIMING(GC, Stop); + JL_TIMING(GC, GC_Stop); #ifdef USE_TRACY TracyCZoneCtx ctx = *(JL_TIMING_CURRENT_BLOCK->tracy_ctx); TracyCZoneColor(ctx, 0x696969); @@ -3043,7 +3043,7 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) uint64_t start_mark_time = jl_hrtime(); JL_PROBE_GC_MARK_BEGIN(); { - JL_TIMING(GC, Mark); + JL_TIMING(GC, GC_Mark); // 1. fix GC bits of objects in the remset. assert(gc_n_threads); @@ -3204,7 +3204,7 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) uint64_t start_sweep_time = jl_hrtime(); JL_PROBE_GC_SWEEP_BEGIN(sweep_full); { - JL_TIMING(GC, Sweep); + JL_TIMING(GC, GC_Sweep); #ifdef USE_TRACY if (sweep_full) { TracyCZoneCtx ctx = *(JL_TIMING_CURRENT_BLOCK->tracy_ctx); @@ -3405,7 +3405,7 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) // Doing this on all threads is racy (it's impossible to check // or wait for finalizers on other threads without dead lock). if (!ptls->finalizers_inhibited && ptls->locks.len == 0) { - JL_TIMING(GC, Finalizers); + JL_TIMING(GC, GC_Finalizers); run_finalizers(ct); } JL_PROBE_GC_FINALIZER(); diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 0f75789077dbc..37302e8ca2ace 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -192,7 +192,7 @@ static jl_callptr_t _jl_compile_codeinst( "invalid world for method-instance"); assert(src && jl_is_code_info(src)); - JL_TIMING(LLVM_MODULE_FINISH, LLVM_MODULE_FINISH); + JL_TIMING(CODEINST_COMPILE, CODEINST_COMPILE); jl_callptr_t fptr = NULL; // emit the code in LLVM IR form @@ -1435,7 +1435,7 @@ void JuliaOJIT::addGlobalMapping(StringRef Name, uint64_t Addr) void JuliaOJIT::addModule(orc::ThreadSafeModule TSM) { - JL_TIMING(LLVM_MODULE_FINISH, LLVM_MODULE_FINISH); + JL_TIMING(LLVM_ORC, LLVM_ORC); ++ModulesAdded; orc::SymbolLookupSet NewExports; TSM.withModuleDo([&](Module &M) JL_NOTSAFEPOINT { diff --git a/src/threading.c b/src/threading.c index 76c21496927a8..ea9ec8e16ca45 100644 --- a/src/threading.c +++ b/src/threading.c @@ -744,6 +744,7 @@ void _jl_mutex_wait(jl_task_t *self, jl_mutex_t *lock, int safepoint) lock->count++; return; } + JL_TIMING(LOCK_SPIN, LOCK_SPIN); jl_profile_lock_start_wait(lock); while (1) { if (owner == NULL && jl_atomic_cmpswap(&lock->owner, &owner, self)) { diff --git a/src/timing.c b/src/timing.c index 7337902010a3d..65c350f247b27 100644 --- a/src/timing.c +++ b/src/timing.c @@ -11,28 +11,26 @@ extern "C" { #endif #ifdef ENABLE_TIMINGS -#include "timing.h" #ifndef HAVE_TIMING_SUPPORT #error Timings are not supported on your compiler #endif static uint64_t t0; -#ifdef USE_TRACY +#if defined(USE_TRACY) || defined(USE_ITTAPI) /** * These sources often generate millions of events / minute. Although Tracy * can generally keep up with that, those events also bloat the saved ".tracy" * files, so we disable them by default. **/ -JL_DLLEXPORT uint64_t jl_timing_enable_mask = 0xFFFFFFFFFFFFFFFF & - ~(1ull << JL_TIMING_ROOT) & - ~(1ull << JL_TIMING_TYPE_CACHE_LOOKUP) & - ~(1ull << JL_TIMING_METHOD_MATCH) & - ~(1ull << JL_TIMING_METHOD_LOOKUP_FAST) & - ~(1ull << JL_TIMING_AST_COMPRESS) & - ~(1ull << JL_TIMING_AST_UNCOMPRESS); +JL_DLLEXPORT uint64_t jl_timing_enable_mask = ~((1ull << JL_TIMING_ROOT) | + (1ull << JL_TIMING_TYPE_CACHE_LOOKUP) | + (1ull << JL_TIMING_METHOD_MATCH) | + (1ull << JL_TIMING_METHOD_LOOKUP_FAST) | + (1ull << JL_TIMING_AST_COMPRESS) | + (1ull << JL_TIMING_AST_UNCOMPRESS)); #else -JL_DLLEXPORT uint64_t jl_timing_enable_mask = 0xFFFFFFFFFFFFFFFF; +JL_DLLEXPORT uint64_t jl_timing_enable_mask = ~0ull; #endif JL_DLLEXPORT uint64_t jl_timing_counts[(int)JL_TIMING_LAST] = {0}; @@ -43,11 +41,15 @@ JL_DLLEXPORT uint32_t jl_timing_print_limit = 10; const char *jl_timing_names[(int)JL_TIMING_LAST] = { -#define X(name) #name +#define X(name) #name, JL_TIMING_OWNERS #undef X }; +#ifdef USE_ITTAPI +JL_DLLEXPORT __itt_event jl_timing_ittapi_events[(int)JL_TIMING_EVENT_LAST]; +#endif + void jl_print_timings(void) { uint64_t total_time = cycleclock() - t0; @@ -66,6 +68,16 @@ void jl_print_timings(void) void jl_init_timing(void) { t0 = cycleclock(); + + _Static_assert(JL_TIMING_EVENT_LAST < sizeof(uint64_t) * CHAR_BIT, "Too many timing events!"); + _Static_assert((int)JL_TIMING_LAST <= (int)JL_TIMING_EVENT_LAST, "More owners than events!"); + + int i = 0; +#ifdef USE_ITTAPI +#define X(name) jl_timing_ittapi_events[i++] = __itt_event_create(#name, strlen(#name)); + JL_TIMING_EVENTS +#undef X +#endif } void jl_destroy_timing(void) diff --git a/src/timing.h b/src/timing.h index de8d980c357fb..ddf9b1d5201d8 100644 --- a/src/timing.h +++ b/src/timing.h @@ -3,6 +3,8 @@ #ifndef JL_TIMING_H #define JL_TIMING_H +#include "julia.h" + #ifdef __cplusplus extern "C" { #endif @@ -106,39 +108,60 @@ void jl_timing_printf(jl_timing_block_t *cur_block, const char *format, ...); #define JL_TIMING_CURRENT_BLOCK (&__timing_block) #endif -#define JL_TIMING_OWNERS \ - X(ROOT), \ - X(GC), \ - X(LOWERING), \ - X(PARSING), \ - X(INFERENCE), \ - X(CODEGEN), \ - X(METHOD_LOOKUP_SLOW), \ - X(METHOD_LOOKUP_FAST), \ - X(LLVM_OPT), \ - X(LLVM_MODULE_FINISH), \ - X(METHOD_MATCH), \ - X(TYPE_CACHE_LOOKUP), \ - X(TYPE_CACHE_INSERT), \ - X(STAGED_FUNCTION), \ - X(MACRO_INVOCATION), \ - X(AST_COMPRESS), \ - X(AST_UNCOMPRESS), \ - X(SYSIMG_LOAD), \ - X(SYSIMG_DUMP), \ - X(NATIVE_DUMP), \ - X(ADD_METHOD), \ - X(LOAD_MODULE), \ - X(SAVE_MODULE), \ - X(INIT_MODULE), +#define JL_TIMING_OWNERS \ + X(ROOT) \ + X(GC) \ + X(LOWERING) \ + X(PARSING) \ + X(INFERENCE) \ + X(CODEGEN) \ + X(METHOD_LOOKUP_SLOW) \ + X(METHOD_LOOKUP_FAST) \ + X(CODEINST_COMPILE) \ + X(LLVM_OPT) \ + X(LLVM_ORC) \ + X(METHOD_MATCH) \ + X(TYPE_CACHE_LOOKUP) \ + X(TYPE_CACHE_INSERT) \ + X(STAGED_FUNCTION) \ + X(MACRO_INVOCATION) \ + X(AST_COMPRESS) \ + X(AST_UNCOMPRESS) \ + X(SYSIMG_LOAD) \ + X(SYSIMG_DUMP) \ + X(NATIVE_DUMP) \ + X(ADD_METHOD) \ + X(LOAD_MODULE) \ + X(SAVE_MODULE) \ + X(INIT_MODULE) \ + X(LOCK_SPIN) \ + + +#define JL_TIMING_EVENTS \ + JL_TIMING_OWNERS \ + X(GC_Stop) \ + X(GC_Mark) \ + X(GC_Sweep) \ + X(GC_Finalizers) \ + X(CODEGEN_LLVM) \ + X(CODEGEN_Codeinst) \ + X(CODEGEN_Workqueue) \ + enum jl_timing_owners { -#define X(name) JL_TIMING_ ## name +#define X(name) JL_TIMING_ ## name, JL_TIMING_OWNERS #undef X JL_TIMING_LAST }; +enum jl_timing_events { +#define X(name) JL_TIMING_EVENT_ ## name, + JL_TIMING_EVENTS +#undef X + JL_TIMING_EVENT_LAST +}; + /** * Timing back-ends differ in terms of whether they support nested * and asynchronous events. @@ -177,6 +200,18 @@ enum jl_timing_owners { #define _TRACY_DESTROY(block) #endif +#ifdef USE_ITTAPI +#define _ITTAPI_CTX_MEMBER int event; +#define _ITTAPI_CTOR(block, event) block->event = event +#define _ITTAPI_START(block) if (_jl_timing_enabled(block->event)) __itt_event_start(jl_timing_ittapi_events[block->event]) +#define _ITTAPI_STOP(block) if (_jl_timing_enabled(block->event)) __itt_event_end(jl_timing_ittapi_events[block->event]) +#else +#define _ITTAPI_CTX_MEMBER +#define _ITTAPI_CTOR(block, event) +#define _ITTAPI_START(block) +#define _ITTAPI_STOP(block) +#endif + /** * Implementation: Aggregated counts back-end **/ @@ -225,16 +260,27 @@ STATIC_INLINE void _jl_timing_counts_destroy(jl_timing_counts_t *block) JL_NOTSA extern uint64_t jl_timing_enable_mask; extern const char *jl_timing_names[(int)JL_TIMING_LAST]; +#ifdef USE_ITTAPI +extern __itt_event jl_timing_ittapi_events[(int)JL_TIMING_EVENT_LAST]; +#endif + struct _jl_timing_block_t { // typedef in julia.h struct _jl_timing_block_t *prev; _TRACY_CTX_MEMBER + _ITTAPI_CTX_MEMBER _COUNTS_CTX_MEMBER }; -STATIC_INLINE void _jl_timing_block_ctor(jl_timing_block_t *block, int owner) JL_NOTSAFEPOINT { +STATIC_INLINE int _jl_timing_enabled(int event) JL_NOTSAFEPOINT { + return !!(jl_timing_enable_mask & (1 << event)); +} + +STATIC_INLINE void _jl_timing_block_ctor(jl_timing_block_t *block, int owner, int event) JL_NOTSAFEPOINT { uint64_t t = cycleclock(); (void)t; _COUNTS_CTOR(&block->counts_ctx, owner); _COUNTS_START(&block->counts_ctx, t); + _ITTAPI_CTOR(block, event); + _ITTAPI_START(block); jl_task_t *ct = jl_current_task; jl_timing_block_t **prevp = &ct->ptls->timing_stack; @@ -248,6 +294,7 @@ STATIC_INLINE void _jl_timing_block_ctor(jl_timing_block_t *block, int owner) JL STATIC_INLINE void _jl_timing_block_destroy(jl_timing_block_t *block) JL_NOTSAFEPOINT { uint64_t t = cycleclock(); (void)t; + _ITTAPI_STOP(block); _COUNTS_STOP(&block->counts_ctx, t); _COUNTS_DESTROY(&block->counts_ctx); _TRACY_DESTROY(block->tracy_ctx); @@ -281,8 +328,8 @@ STATIC_INLINE void _jl_timing_suspend_destroy(jl_timing_suspend_t *suspend) JL_N #ifdef __cplusplus struct jl_timing_block_cpp_t { jl_timing_block_t block; - jl_timing_block_cpp_t(int owner) JL_NOTSAFEPOINT { - _jl_timing_block_ctor(&block, owner); + jl_timing_block_cpp_t(int owner, int event) JL_NOTSAFEPOINT { + _jl_timing_block_ctor(&block, owner, event); } ~jl_timing_block_cpp_t() JL_NOTSAFEPOINT { _jl_timing_block_destroy(&block); @@ -292,13 +339,13 @@ struct jl_timing_block_cpp_t { jl_timing_block_cpp_t& operator=(const jl_timing_block_cpp_t &) = delete; jl_timing_block_cpp_t& operator=(const jl_timing_block_cpp_t &&) = delete; }; -#define JL_TIMING(subsystem, event) jl_timing_block_cpp_t __timing_block(JL_TIMING_ ## subsystem); \ +#define JL_TIMING(subsystem, event) jl_timing_block_cpp_t __timing_block(JL_TIMING_ ## subsystem, JL_TIMING_EVENT_ ## event); \ _TRACY_CTOR(__timing_block.block.tracy_ctx, #event, (jl_timing_enable_mask >> (JL_TIMING_ ## subsystem)) & 1) #else #define JL_TIMING(subsystem, event) \ __attribute__((cleanup(_jl_timing_block_destroy))) \ jl_timing_block_t __timing_block; \ - _jl_timing_block_ctor(&__timing_block, JL_TIMING_ ## subsystem); \ + _jl_timing_block_ctor(&__timing_block, JL_TIMING_ ## subsystem, JL_TIMING_EVENT_ ## event); \ _TRACY_CTOR(__timing_block.tracy_ctx, #event, (jl_timing_enable_mask >> (JL_TIMING_ ## subsystem)) & 1) #endif @@ -311,10 +358,10 @@ struct jl_timing_suspend_cpp_t { ~jl_timing_suspend_cpp_t() JL_NOTSAFEPOINT { _jl_timing_suspend_destroy(&suspend); } - jl_timing_suspend_cpp_t(const jl_timing_block_cpp_t&) = delete; - jl_timing_suspend_cpp_t(const jl_timing_block_cpp_t&&) = delete; - jl_timing_suspend_cpp_t& operator=(const jl_timing_block_cpp_t &) = delete; - jl_timing_suspend_cpp_t& operator=(const jl_timing_block_cpp_t &&) = delete; + jl_timing_suspend_cpp_t(const jl_timing_suspend_cpp_t &) = delete; + jl_timing_suspend_cpp_t(jl_timing_suspend_cpp_t &&) = delete; + jl_timing_suspend_cpp_t& operator=(const jl_timing_suspend_cpp_t &) = delete; + jl_timing_suspend_cpp_t& operator=(jl_timing_suspend_cpp_t &&) = delete; }; #define JL_TIMING_SUSPEND(subsystem, ct) jl_timing_suspend_cpp_t __suspend_block(#subsystem, ct) #else From 7560dea912ca1604598cda8d1744e456943b5382 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 18 Apr 2023 15:51:12 -0400 Subject: [PATCH 576/775] update Statistics.jl Removes some overly strict `@test_throws MethodError` for calls with `Union{}` or `Any`, in preparation for making those errors more precise. --- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - stdlib/Statistics.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/md5 create mode 100644 deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/sha512 delete mode 100644 deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/md5 delete mode 100644 deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/sha512 diff --git a/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/md5 b/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/md5 new file mode 100644 index 0000000000000..7e7a889eecd29 --- /dev/null +++ b/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/md5 @@ -0,0 +1 @@ +6564297a5f5971231809bf9940f68b98 diff --git a/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/sha512 b/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/sha512 new file mode 100644 index 0000000000000..bbe9b8bed6371 --- /dev/null +++ b/deps/checksums/Statistics-a3feba2bb63f06b7f40024185e9fa5f6385e2510.tar.gz/sha512 @@ -0,0 +1 @@ +22d14c82a30f3ec7af09028423cc823808abf86918d5707fd1fcf6ca20dea7871589da9b22e462d194e86fcee380f549aeb65f585048f00bf23281786b17e040 diff --git a/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/md5 b/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/md5 deleted file mode 100644 index 0e2d0534cd8c7..0000000000000 --- a/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -62d47cffac86df3c59b3de8dd218aa79 diff --git a/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/sha512 b/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/sha512 deleted file mode 100644 index 95e88c63f1a14..0000000000000 --- a/deps/checksums/Statistics-e9ac70b760dcf87b77affe6c068548a3325d6e2b.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -6354b1e84d7df1fe8d7e1444181497cac87d22d10a2a21b9f7fab748c209bd9aba64f2df6489e9441624fcf27140ccffa3f7eabaf2517f4900b2661be0c74ba5 diff --git a/stdlib/Statistics.version b/stdlib/Statistics.version index 22857e138655a..27197b12be54c 100644 --- a/stdlib/Statistics.version +++ b/stdlib/Statistics.version @@ -1,4 +1,4 @@ STATISTICS_BRANCH = master -STATISTICS_SHA1 = e9ac70b760dcf87b77affe6c068548a3325d6e2b +STATISTICS_SHA1 = a3feba2bb63f06b7f40024185e9fa5f6385e2510 STATISTICS_GIT_URL := https://github.com/JuliaStats/Statistics.jl.git STATISTICS_TAR_URL = https://api.github.com/repos/JuliaStats/Statistics.jl/tarball/$1 From 44b3d2c8018d1b8da5d9f5be5166701c49b572e2 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 12 Apr 2023 17:20:17 -0400 Subject: [PATCH 577/775] morespecific: add rule for Type{Union{}} Make Type{Union{}} in method definitions always the most specific type (normally these would end up being ambiguous). This ensures we do not invalidate them, nor need to consider ambiguities that might arise from intersections with them. --- NEWS.md | 6 ++++++ base/essentials.jl | 9 ++------- src/subtype.c | 22 ++++++++++++++++++++++ test/specificity.jl | 5 +++++ 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/NEWS.md b/NEWS.md index 33fd3549284d5..931db0ad1081f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,12 @@ Language changes ---------------- * When a task forks a child, the parent task's task-local RNG (random number generator) is no longer affected. The seeding of child based on the parent task also takes a more disciplined approach to collision resistance, using a design based on the SplitMix and DotMix splittable RNG schemes ([#49110]). +* A new morespecific rule for methods resolves ambiguities containing Union{} in favor of + the method defined explicitly to handle the Union{} argument. This makes it possible to + define methods to explicitly handle Union{} without the ambiguities that commonly would + result previously. This also lets the runtime optimize certain method lookups in a way + that significantly improves load and inference times for heavily overloaded methods that + dispatch on Types (such as traits and constructors). Compiler/Runtime improvements ----------------------------- diff --git a/base/essentials.jl b/base/essentials.jl index 829341c482383..1cf3be297edb7 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -310,13 +310,8 @@ See also: [`round`](@ref), [`trunc`](@ref), [`oftype`](@ref), [`reinterpret`](@r """ function convert end -# make convert(::Type{<:Union{}}, x::T) intentionally ambiguous for all T -# so it will never get called or invalidated by loading packages -# with carefully chosen types that won't have any other convert methods defined -convert(T::Type{<:Core.IntrinsicFunction}, x) = throw(MethodError(convert, (T, x))) -convert(T::Type{<:Nothing}, x) = throw(MethodError(convert, (Nothing, x))) -convert(::Type{T}, x::T) where {T<:Core.IntrinsicFunction} = x -convert(::Type{T}, x::T) where {T<:Nothing} = x +# ensure this is never ambiguous, and therefore fast for lookup +convert(T::Type{Union{}}, x) = throw(MethodError(convert, (T, x))) convert(::Type{Type}, x::Type) = x # the ssair optimizer is strongly dependent on this method existing to avoid over-specialization # in the absence of inlining-enabled diff --git a/src/subtype.c b/src/subtype.c index 336d697423845..c1c03763ecf10 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -4460,6 +4460,21 @@ static int num_occurs(jl_tvar_t *v, jl_typeenv_t *env) return 0; } +int tuple_cmp_typeofbottom(jl_datatype_t *a, jl_datatype_t *b) +{ + size_t i, la = jl_nparams(a), lb = jl_nparams(b); + for (i = 0; i < la || i < lb; i++) { + jl_value_t *pa = i < la ? jl_tparam(a, i) : NULL; + jl_value_t *pb = i < lb ? jl_tparam(b, i) : NULL; + int xa = pa == (jl_value_t*)jl_typeofbottom_type || pa == (jl_value_t*)jl_typeofbottom_type->super; + int xb = pb == (jl_value_t*)jl_typeofbottom_type || pb == (jl_value_t*)jl_typeofbottom_type->super; + if (xa != xb) + return xa - xb; + } + return 0; +} + + #define HANDLE_UNIONALL_A \ jl_unionall_t *ua = (jl_unionall_t*)a; \ jl_typeenv_t newenv = { ua->var, 0x0, env }; \ @@ -4478,6 +4493,13 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, jl_value_t *a0, jl_v return 0; if (jl_is_tuple_type(a) && jl_is_tuple_type(b)) { + // compare whether a and b have Type{Union{}} included, + // which makes them instantly the most specific, regardless of all else, + // for whichever is left most (the left-to-right behavior here ensures + // we do not need to keep track of conflicts with multiple methods). + int msp = tuple_cmp_typeofbottom((jl_datatype_t*)a, (jl_datatype_t*)b); + if (msp) + return msp > 0; // When one is JL_VARARG_BOUND and the other has fixed length, // allow the argument length to fix the tvar jl_vararg_kind_t akind = jl_va_tuple_kind((jl_datatype_t*)a); diff --git a/test/specificity.jl b/test/specificity.jl index 5808ac71fa54b..9b605444bad42 100644 --- a/test/specificity.jl +++ b/test/specificity.jl @@ -311,3 +311,8 @@ let A = Tuple{Type{SubString{S}},AbstractString} where S<:AbstractString, @test args_morespecific(B, C) @test args_morespecific(A, C) end + +@test args_morespecific(Tuple{Type{Union{}}, Any}, Tuple{Any, Type{Union{}}}) +@test args_morespecific(Tuple{typeof(Union{}), Any}, Tuple{Any, Type{Union{}}}) +@test args_morespecific(Tuple{Type{Union{}}, Type{Union{}}, Any}, Tuple{Type{Union{}}, Any, Type{Union{}}}) +@test args_morespecific(Tuple{Type{Union{}}, Type{Union{}}, Any, Type{Union{}}}, Tuple{Type{Union{}}, Any, Type{Union{}}, Type{Union{}}}) From 67aa46273ac47da6d6b91e732bef5ca5d3b8d7d7 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 12 Apr 2023 14:12:55 -0400 Subject: [PATCH 578/775] add typemap filtering option for Union{} Based on the new morespecific rule for Union{} and method definitions of the specific form `f(..., Type{Union{}}, Vararg)`. If a method definition exists with that specific form, the intersection visitor will ignore all intersections that have that as their only result, saving significant effort when working with lookups involving `Type{<:T}` (which usually ended up mostly ambiguous anyways). Fixes: https://github.com/JuliaLang/julia/issues/33780 This pattern turns out to have still to been making package loading slow. We could keep adding methods following the ambiguity pattern https://github.com/JuliaLang/julia/pull/46000 for the couple specific functions that need it (constructor, eltype, IteratorEltype, IteratorSize, and maybe a couple others) so the internals can detect those and optimize functions that have that method pair. But it seems somewhat odd, convoluted, and non-obvious behavior there. Instead, this breaks all ambiguities in which Union{} is present explicitly in favor of the method with Union{}. This means that when computing method matches, as soon as we see one method definition with Union{}, we can record that the method is the only possible match for that slot. This, in essence, permits creating a rule for dispatch that a TypeVar lower bound must be strictly a supertype of Union{}, but this creates it at the function level, instead of expecting the user to add it to every TypeVar they use to define methods. This also lets us improve the error message for these cases (generally they should error to avoid polluting the inference result), since we can be assured this method will be called, and not result in an ambiguous MethodError instead! Reverts the functional change of #46000 --- base/abstractarray.jl | 5 +- base/array.jl | 3 + base/arrayshow.jl | 2 + base/boot.jl | 20 +-- base/broadcast.jl | 6 +- base/complex.jl | 1 + base/essentials.jl | 3 +- base/float.jl | 1 + base/generator.jl | 8 +- base/indices.jl | 2 +- base/io.jl | 2 + base/iterators.jl | 2 + base/missing.jl | 2 +- base/number.jl | 4 + base/operators.jl | 1 + base/parse.jl | 2 + base/promotion.jl | 6 + base/some.jl | 1 + base/traits.jl | 4 +- src/gf.c | 19 ++- src/jltypes.c | 2 +- src/julia_internal.h | 2 + src/subtype.c | 27 +++- src/typemap.c | 323 ++++++++++++++++++++++++++++-------------- test/abstractarray.jl | 3 - test/ambiguous.jl | 6 +- test/core.jl | 6 +- test/missing.jl | 4 +- test/some.jl | 2 +- 29 files changed, 319 insertions(+), 150 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 7be3f39d16def..cb3956eb7c6d4 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -183,11 +183,13 @@ CartesianIndex{2} For arrays, this function requires at least Julia 1.2. """ keytype(a::AbstractArray) = keytype(typeof(a)) +keytype(::Type{Union{}}, slurp...) = eltype(Union{}) keytype(A::Type{<:AbstractArray}) = CartesianIndex{ndims(A)} keytype(A::Type{<:AbstractVector}) = Int valtype(a::AbstractArray) = valtype(typeof(a)) +valtype(::Type{Union{}}, slurp...) = eltype(Union{}) """ valtype(T::Type{<:AbstractArray}) @@ -232,7 +234,7 @@ UInt8 ``` """ eltype(::Type) = Any -eltype(::Type{Bottom}) = throw(ArgumentError("Union{} does not have elements")) +eltype(::Type{Bottom}, slurp...) = throw(ArgumentError("Union{} does not have elements")) eltype(x) = eltype(typeof(x)) eltype(::Type{<:AbstractArray{E}}) where {E} = @isdefined(E) ? E : Any @@ -268,6 +270,7 @@ julia> ndims(A) """ ndims(::AbstractArray{T,N}) where {T,N} = N ndims(::Type{<:AbstractArray{<:Any,N}}) where {N} = N +ndims(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements")) """ length(collection) -> Integer diff --git a/base/array.jl b/base/array.jl index 60b1304f20ee0..0a5451bac5b74 100644 --- a/base/array.jl +++ b/base/array.jl @@ -252,7 +252,10 @@ function bitsunionsize(u::Union) return sz end +# Deprecate this, as it seems to have no documented meaning and is unused here, +# but is frequently accessed in packages elsize(@nospecialize _::Type{A}) where {T,A<:Array{T}} = aligned_sizeof(T) +elsize(::Type{Union{}}, slurp...) = 0 sizeof(a::Array) = Core.sizeof(a) function isassigned(a::Array, i::Int...) diff --git a/base/arrayshow.jl b/base/arrayshow.jl index af65df3c97b9d..e600e6281bd15 100644 --- a/base/arrayshow.jl +++ b/base/arrayshow.jl @@ -540,10 +540,12 @@ end # returning Any, as this would cause incorrect printing in e.g. `Vector[Any[1]]`, # because eltype(Vector) == Any so `Any` wouldn't be printed in `Any[1]`) typeinfo_eltype(typeinfo) = nothing # element type not precisely known +typeinfo_eltype(typeinfo::Type{Union{}}, slurp...) = nothing 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) + # types that can be parsed back accurately from their un-decorated representations function typeinfo_implicit(@nospecialize(T)) if T === Float64 || T === Int || T === Char || T === String || T === Symbol || diff --git a/base/boot.jl b/base/boot.jl index ca6e6c81405e2..3a8abde4bce14 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -258,9 +258,17 @@ UnionAll(v::TypeVar, @nospecialize(t)) = ccall(:jl_type_unionall, Any, (Any, Any const Vararg = ccall(:jl_toplevel_eval_in, Any, (Any, Any), Core, _expr(:new, TypeofVararg)) -# let the compiler assume that calling Union{} as a constructor does not need -# to be considered ever (which comes up often as Type{<:T}) -Union{}(a...) = throw(MethodError(Union{}, a)) +# dispatch token indicating a kwarg (keyword sorter) call +function kwcall end +# deprecated internal functions: +kwfunc(@nospecialize(f)) = kwcall +kwftype(@nospecialize(t)) = typeof(kwcall) + +# Let the compiler assume that calling Union{} as a constructor does not need +# to be considered ever (which comes up often as Type{<:T} inference, and +# occasionally in user code from eltype). +Union{}(a...) = throw(ArgumentError("cannot construct a value of type Union{} for return result")) +kwcall(kwargs, ::Type{Union{}}, a...) = Union{}(a...) Expr(@nospecialize args...) = _expr(args...) @@ -369,12 +377,6 @@ include(m::Module, fname::String) = ccall(:jl_load_, Any, (Any, Any), m, fname) eval(m::Module, @nospecialize(e)) = ccall(:jl_toplevel_eval_in, Any, (Any, Any), m, e) -# dispatch token indicating a kwarg (keyword sorter) call -function kwcall end -# deprecated internal functions: -kwfunc(@nospecialize(f)) = kwcall -kwftype(@nospecialize(t)) = typeof(kwcall) - mutable struct Box contents::Any Box(@nospecialize(x)) = new(x) diff --git a/base/broadcast.jl b/base/broadcast.jl index 955a5652353d7..1e057789509ed 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -34,6 +34,9 @@ that you may be able to leverage; see the """ abstract type BroadcastStyle end +struct Unknown <: BroadcastStyle end +BroadcastStyle(::Type{Union{}}, slurp...) = Unknown() # ambiguity resolution + """ `Broadcast.Style{C}()` defines a [`BroadcastStyle`](@ref) signaling through the type parameter `C`. You can use this as an alternative to creating custom subtypes of `BroadcastStyle`, @@ -45,9 +48,6 @@ struct Style{T} <: BroadcastStyle end BroadcastStyle(::Type{<:Tuple}) = Style{Tuple}() -struct Unknown <: BroadcastStyle end -BroadcastStyle(::Type{Union{}}) = Unknown() # ambiguity resolution - """ `Broadcast.AbstractArrayStyle{N} <: BroadcastStyle` is the abstract supertype for any style associated with an `AbstractArray` type. diff --git a/base/complex.jl b/base/complex.jl index 4ce43687aa932..a0473c90d5c17 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -120,6 +120,7 @@ Float64 real(T::Type) = typeof(real(zero(T))) real(::Type{T}) where {T<:Real} = T real(C::Type{<:Complex}) = fieldtype(C, 1) +real(::Type{Union{}}, slurp...) = Union{}(im) """ isreal(x) -> Bool diff --git a/base/essentials.jl b/base/essentials.jl index 1cf3be297edb7..e2035601f4fb5 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -311,7 +311,7 @@ See also: [`round`](@ref), [`trunc`](@ref), [`oftype`](@ref), [`reinterpret`](@r function convert end # ensure this is never ambiguous, and therefore fast for lookup -convert(T::Type{Union{}}, x) = throw(MethodError(convert, (T, x))) +convert(T::Type{Union{}}, x...) = throw(ArgumentError("cannot convert a value to Union{} for assignment")) convert(::Type{Type}, x::Type) = x # the ssair optimizer is strongly dependent on this method existing to avoid over-specialization # in the absence of inlining-enabled @@ -535,6 +535,7 @@ Neither `convert` nor `cconvert` should take a Julia object and turn it into a ` function cconvert end cconvert(T::Type, x) = x isa T ? x : convert(T, x) # do the conversion eagerly in most cases +cconvert(::Type{Union{}}, x...) = convert(Union{}, x...) cconvert(::Type{<:Ptr}, x) = x # but defer the conversion to Ptr to unsafe_convert unsafe_convert(::Type{T}, x::T) where {T} = x # unsafe_convert (like convert) defaults to assuming the convert occurred unsafe_convert(::Type{T}, x::T) where {T<:Ptr} = x # to resolve ambiguity with the next method diff --git a/base/float.jl b/base/float.jl index 4190bfa18bb2b..fad7146655ade 100644 --- a/base/float.jl +++ b/base/float.jl @@ -310,6 +310,7 @@ Float64 """ float(::Type{T}) where {T<:Number} = typeof(float(zero(T))) float(::Type{T}) where {T<:AbstractFloat} = T +float(::Type{Union{}}, slurp...) = Union{}(0.0) """ unsafe_trunc(T, x) diff --git a/base/generator.jl b/base/generator.jl index d11742fe5b72f..aa4b7f67cba95 100644 --- a/base/generator.jl +++ b/base/generator.jl @@ -92,13 +92,13 @@ Base.HasLength() """ IteratorSize(x) = IteratorSize(typeof(x)) IteratorSize(::Type) = HasLength() # HasLength is the default +IteratorSize(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements")) +IteratorSize(::Type{Any}) = SizeUnknown() IteratorSize(::Type{<:Tuple}) = HasLength() IteratorSize(::Type{<:AbstractArray{<:Any,N}}) where {N} = HasShape{N}() IteratorSize(::Type{Generator{I,F}}) where {I,F} = IteratorSize(I) -IteratorSize(::Type{Any}) = SizeUnknown() - haslength(iter) = IteratorSize(iter) isa Union{HasShape, HasLength} abstract type IteratorEltype end @@ -126,7 +126,7 @@ Base.HasEltype() """ IteratorEltype(x) = IteratorEltype(typeof(x)) IteratorEltype(::Type) = HasEltype() # HasEltype is the default +IteratorEltype(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements")) +IteratorEltype(::Type{Any}) = EltypeUnknown() IteratorEltype(::Type{Generator{I,T}}) where {I,T} = EltypeUnknown() - -IteratorEltype(::Type{Any}) = EltypeUnknown() diff --git a/base/indices.jl b/base/indices.jl index 6a28cf63316e6..a9189865048cd 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -92,7 +92,7 @@ particular, [`eachindex`](@ref) creates an iterator whose type depends on the setting of this trait. """ IndexStyle(A::AbstractArray) = IndexStyle(typeof(A)) -IndexStyle(::Type{Union{}}) = IndexLinear() +IndexStyle(::Type{Union{}}, slurp...) = IndexLinear() IndexStyle(::Type{<:AbstractArray}) = IndexCartesian() IndexStyle(::Type{<:Array}) = IndexLinear() IndexStyle(::Type{<:AbstractRange}) = IndexLinear() diff --git a/base/io.jl b/base/io.jl index 3bcae2e8d7836..9c00c57576bac 100644 --- a/base/io.jl +++ b/base/io.jl @@ -219,6 +219,8 @@ julia> read(io, String) ``` """ read(stream, t) +read(stream, ::Type{Union{}}, slurp...; kwargs...) = error("cannot read a value of type Union{}") + """ write(io::IO, x) diff --git a/base/iterators.jl b/base/iterators.jl index a4d12517aabcc..11e94d3384de8 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -1170,6 +1170,7 @@ IteratorEltype(::Type{Flatten{Tuple{}}}) = IteratorEltype(Tuple{}) _flatteneltype(I, ::HasEltype) = IteratorEltype(eltype(I)) _flatteneltype(I, et) = EltypeUnknown() +flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{Union{}}, slurp...) = HasLength() # length==0 flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:NTuple{N,Any}}) where {N} = HasLength() flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Tuple}) = SizeUnknown() flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Number}) = HasLength() @@ -1181,6 +1182,7 @@ _flatten_iteratorsize(sz, ::HasEltype, ::Type{Tuple{}}) = HasLength() IteratorSize(::Type{Flatten{I}}) where {I} = _flatten_iteratorsize(IteratorSize(I), IteratorEltype(I), I) +flatten_length(f, T::Type{Union{}}, slurp...) = 0 function flatten_length(f, T::Type{<:NTuple{N,Any}}) where {N} return N * length(f.it) end diff --git a/base/missing.jl b/base/missing.jl index e1988064aadc1..4544c2b38c460 100644 --- a/base/missing.jl +++ b/base/missing.jl @@ -41,6 +41,7 @@ nonmissingtype(::Type{T}) where {T} = typesplit(T, Missing) function nonmissingtype_checked(T::Type) R = nonmissingtype(T) R >: T && error("could not compute non-missing type") + R <: Union{} && error("cannot convert a value to missing for assignment") return R end @@ -69,7 +70,6 @@ convert(::Type{T}, x::T) where {T>:Union{Missing, Nothing}} = x convert(::Type{T}, x) where {T>:Missing} = convert(nonmissingtype_checked(T), x) convert(::Type{T}, x) where {T>:Union{Missing, Nothing}} = convert(nonmissingtype_checked(nonnothingtype_checked(T)), x) - # Comparison operators ==(::Missing, ::Missing) = missing ==(::Missing, ::Any) = missing diff --git a/base/number.jl b/base/number.jl index 31aa616b0eb55..923fc907d4038 100644 --- a/base/number.jl +++ b/base/number.jl @@ -307,6 +307,7 @@ julia> zero(rand(2,2)) """ zero(x::Number) = oftype(x,0) zero(::Type{T}) where {T<:Number} = convert(T,0) +zero(::Type{Union{}}, slurp...) = Union{}(0) """ one(x) @@ -345,6 +346,7 @@ julia> import Dates; one(Dates.Day(1)) """ one(::Type{T}) where {T<:Number} = convert(T,1) one(x::T) where {T<:Number} = one(T) +one(::Type{Union{}}, slurp...) = Union{}(1) # note that convert(T, 1) should throw an error if T is dimensionful, # so this fallback definition should be okay. @@ -368,6 +370,7 @@ julia> import Dates; oneunit(Dates.Day) """ oneunit(x::T) where {T} = T(one(x)) oneunit(::Type{T}) where {T} = T(one(T)) +oneunit(::Type{Union{}}, slurp...) = Union{}(1) """ big(T::Type) @@ -388,3 +391,4 @@ Complex{BigInt} ``` """ big(::Type{T}) where {T<:Number} = typeof(big(zero(T))) +big(::Type{Union{}}, slurp...) = Union{}(0) diff --git a/base/operators.jl b/base/operators.jl index 3b34e549ea849..5893c5944a3a0 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -888,6 +888,7 @@ julia> widen(1.5f0) """ widen(x::T) where {T} = convert(widen(T), x) widen(x::Type{T}) where {T} = throw(MethodError(widen, (T,))) +widen(x::Type{Union{}}, slurp...) = throw(MethodError(widen, (Union{},))) # function pipelining diff --git a/base/parse.jl b/base/parse.jl index 6e616004a47af..d800e54258b0d 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -36,6 +36,7 @@ julia> parse(Complex{Float64}, "3.2e-1 + 4.5im") ``` """ parse(T::Type, str; base = Int) +parse(::Type{Union{}}, slurp...; kwargs...) = error("cannot parse a value as Union{}") function parse(::Type{T}, c::AbstractChar; base::Integer = 10) where T<:Integer a::Int = (base <= 36 ? 10 : 36) @@ -251,6 +252,7 @@ function parse(::Type{T}, s::AbstractString; base::Union{Nothing,Integer} = noth convert(T, tryparse_internal(T, s, firstindex(s), lastindex(s), base===nothing ? 0 : check_valid_base(base), true)) end +tryparse(::Type{Union{}}, slurp...; kwargs...) = error("cannot parse a value as Union{}") ## string to float functions ## diff --git a/base/promotion.jl b/base/promotion.jl index 31f507d021e78..6e32bd7a42efa 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -323,6 +323,12 @@ it for new types as appropriate. function promote_rule end promote_rule(::Type, ::Type) = Bottom +# Define some methods to avoid needing to enumerate unrelated possibilities when presented +# with Type{<:T}, and return a value in general accordance with the result given by promote_type +promote_rule(::Type{Bottom}, slurp...) = Bottom +promote_rule(::Type{Bottom}, ::Type{Bottom}, slurp...) = Bottom # not strictly necessary, since the next method would match unambiguously anyways +promote_rule(::Type{Bottom}, ::Type{T}, slurp...) where {T} = T +promote_rule(::Type{T}, ::Type{Bottom}, slurp...) where {T} = T promote_result(::Type,::Type,::Type{T},::Type{S}) where {T,S} = (@inline; promote_type(T,S)) # If no promote_rule is defined, both directions give Bottom. In that diff --git a/base/some.jl b/base/some.jl index 08cb3c1648ba1..0d538cbed6c23 100644 --- a/base/some.jl +++ b/base/some.jl @@ -29,6 +29,7 @@ end function nonnothingtype_checked(T::Type) R = nonnothingtype(T) R >: T && error("could not compute non-nothing type") + R <: Union{} && error("cannot convert a value to nothing for assignment") return R end diff --git a/base/traits.jl b/base/traits.jl index 53ae14b12c61e..47ab8ddc0c7ac 100644 --- a/base/traits.jl +++ b/base/traits.jl @@ -11,7 +11,7 @@ OrderStyle(::Type{<:Real}) = Ordered() OrderStyle(::Type{<:AbstractString}) = Ordered() OrderStyle(::Type{Symbol}) = Ordered() OrderStyle(::Type{<:Any}) = Unordered() -OrderStyle(::Type{Union{}}) = Ordered() +OrderStyle(::Type{Union{}}, slurp...) = Ordered() # trait for objects that support arithmetic abstract type ArithmeticStyle end @@ -23,6 +23,7 @@ ArithmeticStyle(instance) = ArithmeticStyle(typeof(instance)) ArithmeticStyle(::Type{<:AbstractFloat}) = ArithmeticRounds() ArithmeticStyle(::Type{<:Integer}) = ArithmeticWraps() ArithmeticStyle(::Type{<:Any}) = ArithmeticUnknown() +ArithmeticStyle(::Type{Union{}}, slurp...) = ArithmeticUnknown() # trait for objects that support ranges with regular step """ @@ -58,5 +59,6 @@ ranges with an element type which is a subtype of `Integer`. abstract type RangeStepStyle end struct RangeStepRegular <: RangeStepStyle end # range with regular step struct RangeStepIrregular <: RangeStepStyle end # range with rounding error +RangeStepStyle(::Type{Union{}}, slurp...) = RangeStepIrregular() RangeStepStyle(instance) = RangeStepStyle(typeof(instance)) diff --git a/src/gf.c b/src/gf.c index 2c3485823202b..23ce8d33c82d2 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1487,6 +1487,8 @@ static int get_intersect_visitor(jl_typemap_entry_t *oldentry, struct typemap_in // skip if no world has both active // also be careful not to try to scan something from the current dump-reload though return 1; + // don't need to consider other similar methods if this oldentry will always fully intersect with them and dominates all of them + typemap_slurp_search(oldentry, &closure->match); jl_method_t *oldmethod = oldentry->func.method; if (closure->match.issubty // e.g. jl_subtype(closure->newentry.sig, oldentry->sig) && jl_subtype(oldmethod->sig, (jl_value_t*)closure->newentry->sig)) { // e.g. jl_type_equal(closure->newentry->sig, oldentry->sig) @@ -1511,7 +1513,7 @@ static jl_value_t *get_intersect_matches(jl_typemap_t *defs, jl_typemap_entry_t else va = NULL; } - struct matches_env env = {{get_intersect_visitor, (jl_value_t*)type, va, + struct matches_env env = {{get_intersect_visitor, (jl_value_t*)type, va, /* .search_slurp = */ 0, /* .ti = */ NULL, /* .env = */ jl_emptysvec, /* .issubty = */ 0}, /* .newentry = */ newentry, /* .shadowed */ NULL, /* .replaced */ NULL}; JL_GC_PUSH3(&env.match.env, &env.match.ti, &env.shadowed); @@ -3208,6 +3210,7 @@ struct ml_matches_env { int intersections; size_t world; int lim; + int include_ambiguous; // results: jl_value_t *t; // array of method matches size_t min_valid; @@ -3263,6 +3266,9 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio return 0; closure->lim--; } + // don't need to consider other similar methods if this ml will always fully intersect with them and dominates all of them + if (!closure->include_ambiguous || closure->lim != -1) + typemap_slurp_search(ml, &closure->match); closure->matc = make_method_match((jl_tupletype_t*)closure->match.ti, closure->match.env, meth, closure->match.issubty ? FULLY_COVERS : NOT_FULLY_COVERS); @@ -3277,9 +3283,10 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio return 1; } -static int ml_mtable_visitor(jl_methtable_t *mt, void *env) +static int ml_mtable_visitor(jl_methtable_t *mt, void *closure0) { - return jl_typemap_intersection_visitor(jl_atomic_load_relaxed(&mt->defs), jl_cachearg_offset(mt), (struct typemap_intersection_env*)env); + struct typemap_intersection_env* env = (struct typemap_intersection_env*)closure0; + return jl_typemap_intersection_visitor(jl_atomic_load_relaxed(&mt->defs), jl_cachearg_offset(mt), env); } // This is the collect form of calling jl_typemap_intersection_visitor @@ -3311,9 +3318,9 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, else va = NULL; } - struct ml_matches_env env = {{ml_matches_visitor, (jl_value_t*)type, va, + struct ml_matches_env env = {{ml_matches_visitor, (jl_value_t*)type, va, /* .search_slurp = */ 0, /* .ti = */ NULL, /* .env = */ jl_emptysvec, /* .issubty = */ 0}, - intersections, world, lim, /* .t = */ jl_an_empty_vec_any, + intersections, world, lim, include_ambiguous, /* .t = */ jl_an_empty_vec_any, /* .min_valid = */ *min_valid, /* .max_valid = */ *max_valid, /* .matc = */ NULL}; struct jl_typemap_assoc search = {(jl_value_t*)type, world, jl_emptysvec, 1, ~(size_t)0}; jl_value_t *isect2 = NULL; @@ -3377,7 +3384,7 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, return env.t; } } - if (!jl_typemap_intersection_visitor(jl_atomic_load_relaxed(&mt->defs), jl_cachearg_offset(mt), &env.match)) { + if (!ml_mtable_visitor(mt, &env.match)) { JL_GC_POP(); return jl_nothing; } diff --git a/src/jltypes.c b/src/jltypes.c index bf15611de4587..c4bd02f8ae37d 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1708,7 +1708,7 @@ static void check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, si JL_GC_POP(); } -static jl_value_t *extract_wrapper(jl_value_t *t JL_PROPAGATES_ROOT) JL_GLOBALLY_ROOTED +jl_value_t *extract_wrapper(jl_value_t *t JL_PROPAGATES_ROOT) JL_GLOBALLY_ROOTED { t = jl_unwrap_unionall(t); if (jl_is_datatype(t)) diff --git a/src/julia_internal.h b/src/julia_internal.h index 61c8a40f7eeb3..0674806d35a5b 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1462,12 +1462,14 @@ struct typemap_intersection_env { jl_typemap_intersection_visitor_fptr const fptr; // fptr to call on a match jl_value_t *const type; // type to match jl_value_t *const va; // the tparam0 for the vararg in type, if applicable (or NULL) + size_t search_slurp; // output values jl_value_t *ti; // intersection type jl_svec_t *env; // intersection env (initialize to null to perform intersection without an environment) int issubty; // if `a <: b` is true in `intersect(a,b)` }; int jl_typemap_intersection_visitor(jl_typemap_t *a, int offs, struct typemap_intersection_env *closure); +void typemap_slurp_search(jl_typemap_entry_t *ml, struct typemap_intersection_env *closure); // -- simplevector.c -- // diff --git a/src/subtype.c b/src/subtype.c index c1c03763ecf10..a12faf1400b58 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2256,20 +2256,34 @@ int jl_has_intersect_type_not_kind(jl_value_t *t) t = jl_unwrap_unionall(t); if (t == (jl_value_t*)jl_any_type) return 1; - if (jl_is_uniontype(t)) { + assert(!jl_is_vararg(t)); + if (jl_is_uniontype(t)) return jl_has_intersect_type_not_kind(((jl_uniontype_t*)t)->a) || jl_has_intersect_type_not_kind(((jl_uniontype_t*)t)->b); - } - if (jl_is_typevar(t)) { + if (jl_is_typevar(t)) return jl_has_intersect_type_not_kind(((jl_tvar_t*)t)->ub); - } - if (jl_is_datatype(t)) { + if (jl_is_datatype(t)) if (((jl_datatype_t*)t)->name == jl_type_typename) return 1; - } return 0; } +// compute if DataType<:t || Union<:t || UnionAll<:t etc. +int jl_has_intersect_kind_not_type(jl_value_t *t) +{ + t = jl_unwrap_unionall(t); + if (t == (jl_value_t*)jl_any_type || jl_is_kind(t)) + return 1; + assert(!jl_is_vararg(t)); + if (jl_is_uniontype(t)) + return jl_has_intersect_kind_not_type(((jl_uniontype_t*)t)->a) || + jl_has_intersect_kind_not_type(((jl_uniontype_t*)t)->b); + if (jl_is_typevar(t)) + return jl_has_intersect_kind_not_type(((jl_tvar_t*)t)->ub); + return 0; +} + + JL_DLLEXPORT int jl_isa(jl_value_t *x, jl_value_t *t) { if (jl_typeis(x,t) || t == (jl_value_t*)jl_any_type) @@ -4466,6 +4480,7 @@ int tuple_cmp_typeofbottom(jl_datatype_t *a, jl_datatype_t *b) for (i = 0; i < la || i < lb; i++) { jl_value_t *pa = i < la ? jl_tparam(a, i) : NULL; jl_value_t *pb = i < lb ? jl_tparam(b, i) : NULL; + assert(jl_typeofbottom_type); // for clang-sa int xa = pa == (jl_value_t*)jl_typeofbottom_type || pa == (jl_value_t*)jl_typeofbottom_type->super; int xb = pb == (jl_value_t*)jl_typeofbottom_type || pb == (jl_value_t*)jl_typeofbottom_type->super; if (xa != xb) diff --git a/src/typemap.c b/src/typemap.c index e60f3d566284e..4b2049552067c 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -23,7 +23,7 @@ static int jl_is_any(jl_value_t *t1) return t1 == (jl_value_t*)jl_any_type; } -static jl_value_t *jl_type_extract_name(jl_value_t *t1 JL_PROPAGATES_ROOT) +static jl_value_t *jl_type_extract_name(jl_value_t *t1 JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT { if (jl_is_unionall(t1)) t1 = jl_unwrap_unionall(t1); @@ -33,6 +33,9 @@ static jl_value_t *jl_type_extract_name(jl_value_t *t1 JL_PROPAGATES_ROOT) else if (jl_is_typevar(t1)) { return jl_type_extract_name(((jl_tvar_t*)t1)->ub); } + else if (t1 == jl_bottom_type || t1 == (jl_value_t*)jl_typeofbottom_type || t1 == (jl_value_t*)jl_typeofbottom_type->super) { + return (jl_value_t*)jl_typeofbottom_type->name; // put Union{} and typeof(Union{}) and Type{Union{}} together for convenience + } else if (jl_is_datatype(t1)) { jl_datatype_t *dt = (jl_datatype_t*)t1; if (!jl_is_kind(t1)) @@ -63,6 +66,9 @@ static int jl_type_extract_name_precise(jl_value_t *t1, int invariant) else if (jl_is_typevar(t1)) { return jl_type_extract_name_precise(((jl_tvar_t*)t1)->ub, 0); } + else if (t1 == jl_bottom_type || t1 == (jl_value_t*)jl_typeofbottom_type || t1 == (jl_value_t*)jl_typeofbottom_type->super) { + return 1; + } else if (jl_is_datatype(t1)) { jl_datatype_t *dt = (jl_datatype_t*)t1; if ((invariant || !dt->name->abstract) && !jl_is_kind(t1)) @@ -84,6 +90,18 @@ static int jl_type_extract_name_precise(jl_value_t *t1, int invariant) return 1; } +// return whether Type{Union{}} is a subtype of Type{t1} (which may have free typevars) +static int jl_parameter_includes_bottom(jl_value_t *t1) +{ + if (jl_is_typevar(t1) || t1 == jl_bottom_type) + return 1; + else if (jl_is_uniontype(t1)) { + jl_uniontype_t *u1 = (jl_uniontype_t*)t1; + return jl_parameter_includes_bottom(u1->a) && jl_parameter_includes_bottom(u1->b); + } + return 0; +} + // ----- Type Signature Subtype Testing ----- // @@ -367,7 +385,7 @@ int jl_typemap_visitor(jl_typemap_t *cache, jl_typemap_visitor_fptr fptr, void * } } -static unsigned jl_supertype_height(jl_datatype_t *dt) +static unsigned jl_supertype_height(jl_datatype_t *dt) JL_NOTSAFEPOINT { unsigned height = 1; while (dt != jl_any_type) { @@ -378,8 +396,10 @@ static unsigned jl_supertype_height(jl_datatype_t *dt) } // return true if a and b might intersect in the type domain (over just their type-names) -static int tname_intersection(jl_datatype_t *a, jl_typename_t *bname, unsigned ha) +static int tname_intersection_dt(jl_datatype_t *a, jl_typename_t *bname, unsigned ha) JL_NOTSAFEPOINT { + if (a == jl_any_type) + return 1; jl_datatype_t *b = (jl_datatype_t*)jl_unwrap_unionall(bname->wrapper); unsigned hb = 1; while (b != jl_any_type) { @@ -395,8 +415,42 @@ static int tname_intersection(jl_datatype_t *a, jl_typename_t *bname, unsigned h return a->name == bname; } -// tparam bit 1 is ::Type{T} (vs. T) -// tparam bit 2 is typename(T) (vs. T) +static int tname_intersection(jl_value_t *a, jl_typename_t *bname, int8_t tparam) JL_NOTSAFEPOINT +{ + if (a == (jl_value_t*)jl_any_type) + return 1; + a = jl_unwrap_unionall(a); + assert(!jl_is_vararg(a)); + if (jl_is_uniontype(a)) + return tname_intersection(((jl_uniontype_t*)a)->a, bname, tparam) || + tname_intersection(((jl_uniontype_t*)a)->b, bname, tparam); + if (jl_is_typevar(a)) + return tname_intersection(((jl_tvar_t*)a)->ub, bname, tparam); + if (jl_is_datatype(a)) { + if (tparam) { + if (!jl_is_type_type(a)) + return 0; + a = jl_unwrap_unionall(jl_tparam0(a)); + if (!jl_is_datatype(a)) + return tname_intersection(a, bname, 0); + } + return tname_intersection_dt((jl_datatype_t*)a, bname, jl_supertype_height((jl_datatype_t*)a)); + } + return 0; +} + +static int concrete_intersects(jl_value_t *t, jl_value_t *ty, int8_t tparam) +{ + if (ty == (jl_value_t*)jl_any_type) // easy case: Any always matches + return 1; + if (tparam & 1) + return jl_isa(t, ty); // (Type{t} <: ty), where is_leaf_type(t) => isa(t, ty) + else + return t == ty || jl_subtype(t, ty); +} + +// tparam bit 0 is ::Type{T} (vs. T) +// tparam bit 1 is typename(T) (vs. T) static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, int8_t tparam, int8_t offs, struct typemap_intersection_env *closure) { @@ -404,15 +458,26 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, size_t i, l = jl_array_len(a); _Atomic(jl_typemap_t*) *data = (_Atomic(jl_typemap_t*)*)jl_array_data(a); unsigned height = 0; - jl_datatype_t *tydt = NULL; - if (jl_is_kind(ty)) - ty = (jl_value_t*)jl_any_type; + jl_datatype_t *tydt = jl_any_type; if (tparam & 2) { - tydt = (jl_datatype_t*)jl_unwrap_unionall(ty); - if (jl_is_datatype(ty)) - height = jl_supertype_height(tydt); - else + // try to extract a description of ty for intersections, but since we + jl_value_t *ttype = jl_unwrap_unionall(ty); + if (tparam & 1) + // extract T from Type{T} (if possible) + ttype = jl_is_type_type(ttype) ? jl_tparam0(ttype) : NULL; + if (ttype && jl_is_datatype(ttype)) { + tydt = (jl_datatype_t*)ttype; + } + else if (ttype) { + ttype = jl_type_extract_name(ttype); + tydt = ttype ? (jl_datatype_t*)jl_unwrap_unionall(((jl_typename_t*)ttype)->wrapper) : NULL; + } + if (tydt == jl_any_type) + ty = (jl_value_t*)jl_any_type; + else if (tydt == NULL) tydt = jl_any_type; + else + height = jl_supertype_height(tydt); } for (i = 0; i < l; i += 2) { jl_value_t *t = jl_atomic_load_relaxed(&data[i]); @@ -422,8 +487,11 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, if (tparam & 2) { jl_typemap_t *ml = jl_atomic_load_relaxed(&data[i + 1]); JL_GC_PROMISE_ROOTED(ml); - if (tydt == jl_any_type || // easy case: Any always matches - tname_intersection(tydt, (jl_typename_t*)t, height)) { + if (tydt == jl_any_type ? + tname_intersection(ty, (jl_typename_t*)t, tparam & 1) : + tname_intersection_dt(tydt, (jl_typename_t*)t, height)) { + if ((tparam & 1) && t == (jl_value_t*)jl_typeofbottom_type->name) // skip Type{Union{}} and Type{typeof(Union{})}, since the caller should have already handled those + continue; if (jl_is_array(ml)) { if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, tparam & ~2, offs, closure)) goto exit; @@ -436,10 +504,7 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, } else { // `t` is a leaftype, so intersection test becomes subtype (after excluding kinds) - if (ty == (jl_value_t*)jl_any_type || // easy case: Any always matches - (tparam & 1 - ? jl_isa(t, ty) // (Type{t} <: ty), where is_leaf_type(t) => isa(t, ty) - : (t == ty || jl_subtype(t, ty)))) { + if (concrete_intersects(t, ty, tparam)) { jl_typemap_t *ml = jl_atomic_load_relaxed(&data[i + 1]); JL_GC_PROMISE_ROOTED(ml); // NOTE: ml might be NULL if we're racing with the thread that's inserting the item @@ -456,6 +521,7 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, return 0; } + // calls fptr on each jl_typemap_entry_t in cache in sort order // for which type ∩ ml->type != Union{}, until fptr return false static int jl_typemap_intersection_node_visitor(jl_typemap_entry_t *ml, struct typemap_intersection_env *closure) @@ -496,6 +562,30 @@ static int jl_typemap_intersection_node_visitor(jl_typemap_entry_t *ml, struct t return 1; } +int jl_has_intersect_type_not_kind(jl_value_t *t); +int jl_has_intersect_kind_not_type(jl_value_t *t); + +void typemap_slurp_search(jl_typemap_entry_t *ml, struct typemap_intersection_env *closure) +{ + // n.b. we could consider mt->max_args here too, so this optimization + // usually works even if the user forgets the `slurp...` argument, but + // there is discussion that parameter may be going away? (and it is + // already not accurately up-to-date for all tables currently anyways) + if (closure->search_slurp && ml->va) { + jl_value_t *sig = jl_unwrap_unionall((jl_value_t*)ml->sig); + size_t nargs = jl_nparams(sig); + if (nargs > 1 && nargs - 1 == closure->search_slurp) { + jl_vararg_t *va = (jl_vararg_t*)jl_tparam(sig, nargs - 1); + assert(jl_is_vararg((jl_value_t*)va)); + if (va->T == (jl_value_t*)jl_any_type && va->N == NULL) { + // instruct typemap it can set exclude_typeofbottom on parameter nargs + // since we found the necessary slurp argument + closure->search_slurp = 0; + } + } + } +} + int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, struct typemap_intersection_env *closure) { @@ -504,13 +594,12 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, //TODO: fast-path for leaf-type tuples? //if (ttypes->isdispatchtuple) { // register jl_typemap_intersection_visitor_fptr fptr = closure->fptr; - // struct jl_typemap_assoc search = {(jl_value_t*)closure->type, world, closure->env, 0, ~(size_t)0}; - // jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(map, search, offs, /*subtype*/1); - // if (ml) { - // closure->env = search->env; - // if (!fptr(ml, closure)) - // return 0; - // } + // struct jl_typemap_assoc search = {(jl_value_t*)closure->type, world, closure->env, 0, ~(size_t)0}; + // jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(map, search, offs, /*subtype*/1); + // if (ml) { + // closure->env = search->env; + // if (!fptr(ml, closure)) + // return 0; // } // return 1; //} @@ -532,23 +621,56 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, if (ty) { while (jl_is_typevar(ty)) ty = ((jl_tvar_t*)ty)->ub; - jl_value_t *typetype = jl_unwrap_unionall(ty); - typetype = jl_is_type_type(typetype) ? jl_tparam0(typetype) : NULL; - jl_value_t *name = typetype ? jl_type_extract_name(typetype) : NULL; // approxify the tparam until we have a valid type - if (jl_has_free_typevars(ty)) { - ty = jl_unwrap_unionall(ty); - if (jl_is_datatype(ty)) - ty = ((jl_datatype_t*)ty)->name->wrapper; - else - ty = (jl_value_t*)jl_any_type; - } + if (jl_has_free_typevars(ty)) + ty = jl_rewrap_unionall(ty, closure->type); + JL_GC_PUSH1(&ty); jl_array_t *targ = jl_atomic_load_relaxed(&cache->targ); - if (targ != (jl_array_t*)jl_an_empty_vec_any - && !(typetype && !jl_has_free_typevars(typetype) && !is_cache_leaf(typetype, 1))) { // cannot contain this, so don't bother with checking - if (name && !jl_is_typevar(typetype)) { - // semi-direct lookup of types via their names - if (jl_type_extract_name_precise(typetype, 1)) { + jl_array_t *tname = jl_atomic_load_relaxed(&cache->tname); + int maybe_type = 0; + int maybe_kind = 0; + int exclude_typeofbottom = 0; + jl_value_t *typetype = NULL; + jl_value_t *name = NULL; + // pre-check: optimized pre-intersection test to see if `ty` could intersect with any Type or Kind + if (targ != (jl_array_t*)jl_an_empty_vec_any || tname != (jl_array_t*)jl_an_empty_vec_any) { + maybe_kind = jl_has_intersect_kind_not_type(ty); + maybe_type = maybe_kind || jl_has_intersect_type_not_kind(ty); + if (maybe_type && !maybe_kind) { + typetype = jl_unwrap_unionall(ty); + typetype = jl_is_type_type(typetype) ? jl_tparam0(typetype) : NULL; + name = typetype ? jl_type_extract_name(typetype) : NULL; + exclude_typeofbottom = !(typetype ? jl_parameter_includes_bottom(typetype) : jl_subtype((jl_value_t*)jl_typeofbottom_type, ty)); + } + } + // First check for intersections with methods defined on Type{T}, where T was a concrete type + if (targ != (jl_array_t*)jl_an_empty_vec_any && maybe_type && + (!typetype || jl_has_free_typevars(typetype) || is_cache_leaf(typetype, 1))) { // otherwise cannot contain this particular kind, so don't bother with checking + if (!exclude_typeofbottom) { + // detect Type{Union{}}, Type{Type{Union{}}}, and Type{typeof(Union{}} and do those early here + // otherwise the possibility of encountering `Type{Union{}}` in this intersection may + // be forcing us to do some extra work here whenever we see a typevar, even though + // the likelihood of that value actually occurring is frequently likely to be + // zero (or result in an ambiguous match) + targ = jl_atomic_load_relaxed(&cache->targ); // may be GC'd during type-intersection + jl_value_t *ml = mtcache_hash_lookup(targ, (jl_value_t*)jl_typeofbottom_type->name); + if (ml != jl_nothing) { + size_t search_slurp = closure->search_slurp; + closure->search_slurp = offs + 1; + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { + closure->search_slurp = search_slurp; + JL_GC_POP(); + return 0; + } + if (closure->search_slurp == 0) + exclude_typeofbottom = 1; + closure->search_slurp = search_slurp; + } + } + if (name != (jl_value_t*)jl_typeofbottom_type->name) { + targ = jl_atomic_load_relaxed(&cache->targ); // may be GC'd earlier + if (exclude_typeofbottom && name && jl_type_extract_name_precise(typetype, 1)) { + // attempt semi-direct lookup of types via their names // consider the type name first jl_value_t *ml = mtcache_hash_lookup(targ, (jl_value_t*)name); if (jl_is_array(ml)) { @@ -557,33 +679,21 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, if (is_cache_leaf(typetype, 1)) { ml = mtcache_hash_lookup((jl_array_t*)ml, typetype); if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { JL_GC_POP(); return 0; } } } } else { - if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, (jl_value_t*)ty, 1, offs, closure)) return 0; + if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, 1, offs, closure)) { JL_GC_POP(); return 0; } } } else if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { JL_GC_POP(); return 0; } } } else { - // consider all of the possible subtypes - // TODO: the possibility of encountering `Type{Union{}}` in this intersection may - // be forcing us to do some extra work here whenever we see a typevar, even though - // the likelihood of that value actually occurring is frequently likely to be - // zero (or result in an ambiguous match) - if (!jl_typemap_intersection_array_visitor((jl_array_t*)targ, (jl_value_t*)ty, 3, offs, closure)) return 0; - } - } - else { - // else an array scan is required to check subtypes - // first, fast-path: optimized pre-intersection test to see if `ty` could intersect with any Type - if (typetype || !jl_has_empty_intersection((jl_value_t*)jl_type_type, ty)) { - targ = jl_atomic_load_relaxed(&cache->targ); // may be GC'd during type-intersection - if (!jl_typemap_intersection_array_visitor(targ, ty, 3, offs, closure)) return 0; + // else an array scan is required to consider all the possible subtypes + if (!jl_typemap_intersection_array_visitor(targ, exclude_typeofbottom && !maybe_kind ? ty : (jl_value_t*)jl_any_type, 3, offs, closure)) { JL_GC_POP(); return 0; } } } } @@ -596,7 +706,7 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, if (jl_is_array(ml)) ml = mtcache_hash_lookup((jl_array_t*)ml, ty); if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) { JL_GC_POP(); return 0; } } } else { @@ -605,82 +715,87 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, // direct lookup of leaf types jl_value_t *ml = mtcache_hash_lookup(cachearg1, name); if (jl_is_array(ml)) { - if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, (jl_value_t*)ty, 0, offs, closure)) return 0; + if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, 0, offs, closure)) { JL_GC_POP(); return 0; } } else { - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { JL_GC_POP(); return 0; } } } else { // else an array scan is required to check subtypes - if (!jl_typemap_intersection_array_visitor(cachearg1, ty, 2, offs, closure)) return 0; + if (!jl_typemap_intersection_array_visitor(cachearg1, ty, 2, offs, closure)) { JL_GC_POP(); return 0; } } } } - jl_array_t *tname = jl_atomic_load_relaxed(&cache->tname); - if (tname != (jl_array_t*)jl_an_empty_vec_any) { - if (name && !jl_is_typevar(typetype)) { - // semi-direct lookup of types - // TODO: the possibility of encountering `Type{Union{}}` in this intersection may + // Next check for intersections with methods defined on Type{T}, where T was not concrete (it might even have been a TypeVar), but had an extractable TypeName + if (tname != (jl_array_t*)jl_an_empty_vec_any && maybe_type) { + if (!exclude_typeofbottom || (!typetype && jl_isa((jl_value_t*)jl_typeofbottom_type, ty))) { + // detect Type{Union{}}, Type{Type{Union{}}}, and Type{typeof(Union{}} and do those early here + // otherwise the possibility of encountering `Type{Union{}}` in this intersection may // be forcing us to do some extra work here whenever we see a typevar, even though // the likelihood of that value actually occurring is frequently likely to be // zero (or result in an ambiguous match) - jl_datatype_t *super = (jl_datatype_t*)jl_unwrap_unionall(((jl_typename_t*)name)->wrapper); - if (jl_type_extract_name_precise(typetype, 1)) { - // just consider the type and its direct super types - while (1) { - tname = jl_atomic_load_relaxed(&cache->tname); // reload after callback - jl_typemap_t *ml = mtcache_hash_lookup(tname, (jl_value_t*)super->name); - if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; - } - if (super == jl_any_type) - break; - super = super->super; + tname = jl_atomic_load_relaxed(&cache->tname); // may be GC'd earlier + jl_value_t *ml = mtcache_hash_lookup(tname, (jl_value_t*)jl_typeofbottom_type->name); + if (ml != jl_nothing) { + size_t search_slurp = closure->search_slurp; + closure->search_slurp = offs + 1; + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { + closure->search_slurp = search_slurp; + JL_GC_POP(); + return 0; } + if (closure->search_slurp == 0) + exclude_typeofbottom = 1; + closure->search_slurp = search_slurp; } - else { - // consider all of the possible subtypes - if (!jl_typemap_intersection_array_visitor(tname, (jl_value_t*)super, 3, offs, closure)) return 0; + } + if (exclude_typeofbottom && name && jl_type_extract_name_precise(typetype, 1)) { + // semi-direct lookup of types + // just consider the type and its direct super types + jl_datatype_t *super = (jl_datatype_t*)jl_unwrap_unionall(((jl_typename_t*)name)->wrapper); + if (super->name == jl_typeofbottom_type->name) + super = super->super; // this was handled above + while (1) { + tname = jl_atomic_load_relaxed(&cache->tname); // reload after callback + jl_typemap_t *ml = mtcache_hash_lookup(tname, (jl_value_t*)super->name); + if (ml != jl_nothing) { + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) { JL_GC_POP(); return 0; } + } + if (super == jl_any_type) + break; + super = super->super; } } else { - // else an array scan is required to check subtypes - // first, fast-path: optimized pre-intersection test to see if `ty` could intersect with any Type - if (name || !jl_has_empty_intersection((jl_value_t*)jl_type_type, ty)) { - tname = jl_atomic_load_relaxed(&cache->tname); // may be GC'd during type-intersection - if (!jl_typemap_intersection_array_visitor(tname, (jl_value_t*)jl_any_type, 3, offs, closure)) return 0; - } + // else an array scan is required to check subtypes of typetype too + tname = jl_atomic_load_relaxed(&cache->tname); // may be GC'd earlier + if (!jl_typemap_intersection_array_visitor(tname, exclude_typeofbottom && !maybe_kind ? ty : (jl_value_t*)jl_any_type, 3, offs, closure)) { JL_GC_POP(); return 0; } } } jl_array_t *name1 = jl_atomic_load_relaxed(&cache->name1); if (name1 != (jl_array_t*)jl_an_empty_vec_any) { jl_value_t *name = jl_type_extract_name(ty); - if (name) { + if (name && jl_type_extract_name_precise(ty, 0)) { jl_datatype_t *super = (jl_datatype_t*)jl_unwrap_unionall(((jl_typename_t*)name)->wrapper); - if (jl_type_extract_name_precise(ty, 0)) { - // direct lookup of concrete types - while (1) { - name1 = jl_atomic_load_relaxed(&cache->name1); // reload after callback - jl_typemap_t *ml = mtcache_hash_lookup(name1, (jl_value_t*)super->name); - if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; - } - if (super == jl_any_type) - break; - super = super->super; + // direct lookup of concrete types + while (1) { + name1 = jl_atomic_load_relaxed(&cache->name1); // reload after callback + jl_typemap_t *ml = mtcache_hash_lookup(name1, (jl_value_t*)super->name); + if (ml != jl_nothing) { + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) { JL_GC_POP(); return 0; } } - } - else { - // consider all of the possible subtypes too - if (!jl_typemap_intersection_array_visitor(name1, (jl_value_t*)super, 2, offs, closure)) return 0; + if (super == jl_any_type) + break; + super = super->super; } } else { // else an array scan is required to check subtypes - if (!jl_typemap_intersection_array_visitor(name1, (jl_value_t*)jl_any_type, 2, offs, closure)) return 0; + if (!jl_typemap_intersection_array_visitor(name1, ty, 2, offs, closure)) { JL_GC_POP(); return 0; } } } + JL_GC_POP(); } if (!jl_typemap_intersection_node_visitor(jl_atomic_load_relaxed(&cache->linear), closure)) return 0; diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 070e5d7a7b289..c5ff97deb6777 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -520,9 +520,6 @@ function test_primitives(::Type{T}, shape, ::Type{TestAbstractArray}) where T @test convert(Matrix, Y) == Y @test convert(Matrix, view(Y, 1:2, 1:2)) == Y @test_throws MethodError convert(Matrix, X) - - # convert(::Type{Union{}}, A::AbstractMatrix) - @test_throws MethodError convert(Union{}, X) end mutable struct TestThrowNoGetindex{T} <: AbstractVector{T} end diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 67fb16d3b7458..a1b973f30a70c 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -177,12 +177,10 @@ ambs = detect_ambiguities(Ambig48312) @test good end - # some ambiguities involving Union{} type parameters are expected, but not required + # some ambiguities involving Union{} type parameters may be expected, but not required let ambig = Set(detect_ambiguities(Core; recursive=true, ambiguous_bottom=true)) - m1 = which(Core.Compiler.convert, Tuple{Type{<:Core.IntrinsicFunction}, Any}) - m2 = which(Core.Compiler.convert, Tuple{Type{<:Nothing}, Any}) - pop!(ambig, (m1, m2)) @test !isempty(ambig) + @test length(ambig) < 30 end STDLIB_DIR = Sys.STDLIB diff --git a/test/core.jl b/test/core.jl index a89d206182dbf..daec51ab5b566 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1694,7 +1694,9 @@ end # issue #3221 let x = fill(nothing, 1) - @test_throws MethodError x[1] = 1 + @test_throws ErrorException("cannot convert a value to nothing for assignment") x[1] = 1 + x = Vector{Union{}}(undef, 1) + @test_throws ArgumentError("cannot convert a value to Union{} for assignment") x[1] = 1 end # issue #3220 @@ -4916,7 +4918,7 @@ struct f47209 x::Int f47209()::Nothing = new(1) end -@test_throws MethodError f47209() +@test_throws ErrorException("cannot convert a value to nothing for assignment") f47209() # issue #12096 let a = Val{Val{TypeVar(:_, Int)}}, diff --git a/test/missing.jl b/test/missing.jl index 450b816ea3e57..f06d1aad7a6b1 100644 --- a/test/missing.jl +++ b/test/missing.jl @@ -21,8 +21,8 @@ end @test convert(Union{Nothing, Missing}, nothing) === nothing @test convert(Union{Missing, Nothing, Float64}, 1) === 1.0 - @test_throws MethodError convert(Missing, 1) - @test_throws MethodError convert(Union{Nothing, Missing}, 1) + @test_throws ErrorException("cannot convert a value to missing for assignment") convert(Missing, 1) + @test_throws ErrorException("cannot convert a value to missing for assignment") convert(Union{Nothing, Missing}, 1) @test_throws MethodError convert(Union{Int, Missing}, "a") end diff --git a/test/some.jl b/test/some.jl index 27d50ca354a49..e49fc586a3a6e 100644 --- a/test/some.jl +++ b/test/some.jl @@ -33,7 +33,7 @@ @test convert(Union{Int, Nothing}, 1) === 1 @test convert(Union{Int, Nothing}, 1.0) === 1 @test convert(Nothing, nothing) === nothing -@test_throws MethodError convert(Nothing, 1) +@test_throws ErrorException("cannot convert a value to nothing for assignment") convert(Nothing, 1) ## show() From 7f4e129cf5a0fa853a8347edb2b52b40622182be Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 14 Apr 2023 09:41:45 -0400 Subject: [PATCH 579/775] also optimize {Type{T},T} lookup Since T cannot be Union{} here, the prior optimization would not get detected post facto, but a priori this cannot be inhabited for T=Union{}, so we can exclude it immediately. This does not happen during inference, but shows up during edge validation somewhat often. --- src/typemap.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/typemap.c b/src/typemap.c index 4b2049552067c..97ea31928251d 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -565,6 +565,16 @@ static int jl_typemap_intersection_node_visitor(jl_typemap_entry_t *ml, struct t int jl_has_intersect_type_not_kind(jl_value_t *t); int jl_has_intersect_kind_not_type(jl_value_t *t); +// if TypeVar tv is used covariantly, it cannot be Union{} +int has_covariant_var(jl_datatype_t *ttypes, jl_tvar_t *tv) +{ + size_t i, l = jl_nparams(ttypes); + for (i = 0; i < l; i++) + if (jl_tparam(ttypes, i) == (jl_value_t*)tv) + return 1; + return 0; +} + void typemap_slurp_search(jl_typemap_entry_t *ml, struct typemap_intersection_env *closure) { // n.b. we could consider mt->max_args here too, so this optimization @@ -640,7 +650,12 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, typetype = jl_unwrap_unionall(ty); typetype = jl_is_type_type(typetype) ? jl_tparam0(typetype) : NULL; name = typetype ? jl_type_extract_name(typetype) : NULL; - exclude_typeofbottom = !(typetype ? jl_parameter_includes_bottom(typetype) : jl_subtype((jl_value_t*)jl_typeofbottom_type, ty)); + if (!typetype) + exclude_typeofbottom = !jl_subtype((jl_value_t*)jl_typeofbottom_type, ty); + else if (jl_is_typevar(typetype)) + exclude_typeofbottom = has_covariant_var((jl_datatype_t*)ttypes, (jl_tvar_t*)typetype); + else + exclude_typeofbottom = !jl_parameter_includes_bottom(typetype); } } // First check for intersections with methods defined on Type{T}, where T was a concrete type From a3cda94ca1cfeef19ca4e73e115a2ce852683f1f Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Mon, 24 Apr 2023 09:40:11 -0500 Subject: [PATCH 580/775] Remove redundant definitions of `push!(::AbstractDict, elements...)` (#49464) --- base/abstractdict.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/base/abstractdict.jl b/base/abstractdict.jl index ab1ad2464cb4c..9dba5369a2a66 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -560,8 +560,6 @@ function get!(default::Callable, t::AbstractDict{K,V}, key) where K where V end push!(t::AbstractDict, p::Pair) = setindex!(t, p.second, p.first) -push!(t::AbstractDict, p::Pair, q::Pair) = push!(push!(t, p), q) -push!(t::AbstractDict, p::Pair, q::Pair, r::Pair...) = push!(push!(push!(t, p), q), r...) # AbstractDicts are convertible convert(::Type{T}, x::T) where {T<:AbstractDict} = x From fa215891aff303e57608a6786fb152ef67565a51 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Mon, 24 Apr 2023 18:01:05 +0000 Subject: [PATCH 581/775] Avoid usage of `jl_error()` in `check_cmdline()` This is the same as https://github.com/JuliaLang/julia/pull/45765 where we use `jl_error()` too early to get backtraces, but too late to fail the supposed guard `if` statement that should prevent us from trying to take a backtrace. X-ref: https://github.com/JuliaLang/julia/issues/45847 --- src/processor.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/processor.cpp b/src/processor.cpp index fec2b77102f55..0b4f9b1243446 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -822,20 +822,24 @@ static inline void check_cmdline(T &&cmdline, bool imaging) // sysimg means. Make it an error for now. if (!imaging) { if (cmdline.size() > 1) { - jl_error("More than one command line CPU targets specified " - "without a `--output-` flag specified"); + jl_safe_printf("More than one command line CPU targets specified " + "without a `--output-` flag specified"); + exit(1); } if (cmdline[0].en.flags & JL_TARGET_CLONE_ALL) { - jl_error("\"clone_all\" feature specified " - "without a `--output-` flag specified"); + jl_safe_printf("\"clone_all\" feature specified " + "without a `--output-` flag specified"); + exit(1); } if (cmdline[0].en.flags & JL_TARGET_OPTSIZE) { - jl_error("\"opt_size\" feature specified " - "without a `--output-` flag specified"); + jl_safe_printf("\"opt_size\" feature specified " + "without a `--output-` flag specified"); + exit(1); } if (cmdline[0].en.flags & JL_TARGET_MINSIZE) { - jl_error("\"min_size\" feature specified " - "without a `--output-` flag specified"); + jl_safe_printf("\"min_size\" feature specified " + "without a `--output-` flag specified"); + exit(1); } } } From 2180db160498bcf7aaaa667a869cb067d6852a97 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 24 Apr 2023 18:44:36 -0400 Subject: [PATCH 582/775] ensure sub-bitfield bits are always defined (#49471) Helps avoid UB or inconsistencies between Julia and C definitions. --- src/datatype.c | 1 + src/julia.h | 7 +++++++ src/module.c | 1 + 3 files changed, 9 insertions(+) diff --git a/src/datatype.c b/src/datatype.c index db334c7343d80..d500d762999a3 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -106,6 +106,7 @@ jl_datatype_t *jl_new_uninitialized_datatype(void) t->maybe_subtype_of_cache = 1; t->ismutationfree = 0; t->isidentityfree = 0; + t->padding = 0; t->name = NULL; t->super = NULL; t->parameters = NULL; diff --git a/src/julia.h b/src/julia.h index 216628c9aebdc..e1032f6ac3111 100644 --- a/src/julia.h +++ b/src/julia.h @@ -92,6 +92,11 @@ typedef struct _jl_value_t jl_value_t; struct _jl_taggedvalue_bits { uintptr_t gc:2; uintptr_t in_image:1; +#ifdef _P64 + uintptr_t padding:61; +#else + uintptr_t padding:29; +#endif }; JL_EXTENSION struct _jl_taggedvalue_t { @@ -555,6 +560,7 @@ typedef struct _jl_datatype_t { uint16_t isprimitivetype:1; // whether this is declared with 'primitive type' keyword (sized, no fields, and immutable) uint16_t ismutationfree:1; // whether any mutable memory is reachable through this type (in the type or via fields) uint16_t isidentityfree:1; // whether this type or any object reachable through its fields has non-content-based identity + uint16_t padding:6; } jl_datatype_t; typedef struct _jl_vararg_t { @@ -579,6 +585,7 @@ typedef struct _jl_binding_t { uint8_t imported:1; uint8_t usingfailed:1; uint8_t deprecated:2; // 0=not deprecated, 1=renamed, 2=moved to another package + uint8_t padding:2; } jl_binding_t; typedef struct { diff --git a/src/module.c b/src/module.c index a232c6e9f8367..4ac0d48a6f9e0 100644 --- a/src/module.c +++ b/src/module.c @@ -182,6 +182,7 @@ static jl_binding_t *new_binding(jl_module_t *mod, jl_sym_t *name) b->imported = 0; b->deprecated = 0; b->usingfailed = 0; + b->padding = 0; JL_GC_PUSH1(&b); b->globalref = jl_new_globalref(mod, name, b); JL_GC_POP(); From 2768ec507df2c5fba0e95b8d75e0ed06c9914614 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 24 Apr 2023 18:47:11 -0400 Subject: [PATCH 583/775] staticdata: ensure lookup is for a Type narrower than the Method signature (#49444) The type_more_complex widening might result in a wider value here, based on the needs of inference, but we only care about the dispatch lookups that could have resulted in this particular Method before, not any other wider results. --- src/staticdata_utils.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index a5413cb96cf16..ad9149bb54526 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -475,7 +475,8 @@ static void jl_collect_edges(jl_array_t *edges, jl_array_t *ext_targets, jl_arra // and compute the old methods list, ready for serialization jl_value_t *matches = NULL; jl_array_t *callee_ids = NULL; - JL_GC_PUSH2(&matches, &callee_ids); + jl_value_t *sig = NULL; + JL_GC_PUSH3(&matches, &callee_ids, &sig); for (size_t i = 0; i < l; i += 2) { jl_array_t *callees = (jl_array_t*)jl_array_ptr_ref(edges, i + 1); size_t l = jl_array_len(callees); @@ -519,14 +520,17 @@ static void jl_collect_edges(jl_array_t *edges, jl_array_t *ext_targets, jl_arra } } else { - jl_value_t *sig; - if (jl_is_method_instance(callee)) - sig = ((jl_method_instance_t*)callee)->specTypes; - else + if (jl_is_method_instance(callee)) { + jl_method_instance_t *mi = (jl_method_instance_t*)callee; + sig = jl_type_intersection(mi->def.method->sig, (jl_value_t*)mi->specTypes); + } + else { sig = callee; + } int ambig = 0; matches = jl_matching_methods((jl_tupletype_t*)sig, jl_nothing, INT32_MAX, 0, world, &min_valid, &max_valid, &ambig); + sig = NULL; if (matches == jl_nothing) { callee_ids = NULL; // invalid break; @@ -840,7 +844,8 @@ static jl_array_t *jl_verify_edges(jl_array_t *targets, size_t minworld) memset(jl_array_data(maxvalids), 0, l * sizeof(size_t)); jl_value_t *loctag = NULL; jl_value_t *matches = NULL; - JL_GC_PUSH3(&maxvalids, &matches, &loctag); + jl_value_t *sig = NULL; + JL_GC_PUSH4(&maxvalids, &matches, &sig, &loctag); for (i = 0; i < l; i++) { jl_value_t *invokesig = jl_array_ptr_ref(targets, i * 3); jl_value_t *callee = jl_array_ptr_ref(targets, i * 3 + 1); @@ -867,11 +872,13 @@ static jl_array_t *jl_verify_edges(jl_array_t *targets, size_t minworld) } } else { - jl_value_t *sig; - if (jl_is_method_instance(callee)) - sig = ((jl_method_instance_t*)callee)->specTypes; - else + if (jl_is_method_instance(callee)) { + jl_method_instance_t *mi = (jl_method_instance_t*)callee; + sig = jl_type_intersection(mi->def.method->sig, (jl_value_t*)mi->specTypes); + } + else { sig = callee; + } assert(jl_is_array(expected)); int ambig = 0; // TODO: possibly need to included ambiguities too (for the optimizer correctness)? @@ -879,6 +886,7 @@ static jl_array_t *jl_verify_edges(jl_array_t *targets, size_t minworld) matches = jl_matching_methods((jl_tupletype_t*)sig, jl_nothing, _jl_debug_method_invalidation ? INT32_MAX : jl_array_len(expected), 0, minworld, &min_valid, &max_valid, &ambig); + sig = NULL; if (matches == jl_nothing) { max_valid = 0; } From c70e0fa552d64e807ca5b82b322d610b2ba6e490 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 24 Apr 2023 18:47:36 -0400 Subject: [PATCH 584/775] remove SIGABRT from sigwait list (#49445) This already is added to the sigdie list, so on mach it was attempting to handle it in both places simultaneously. --- src/signals-unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/signals-unix.c b/src/signals-unix.c index 79300567b4bce..2858538372722 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -645,7 +645,7 @@ void jl_install_thread_signal_handler(jl_ptls_t ptls) } const static int sigwait_sigs[] = { - SIGINT, SIGTERM, SIGABRT, SIGQUIT, + SIGINT, SIGTERM, SIGQUIT, #ifdef SIGINFO SIGINFO, #else From 86b819c3f79d814fd5a9830e01144ae371f549bb Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Tue, 25 Apr 2023 02:49:15 -0400 Subject: [PATCH 585/775] improve effects of `objectid` and `getindex(::Dict)` (#49447) This commit also marks `Module` type as `identityfree`. Co-authored-by: Shuhei Kadowaki --- base/dict.jl | 3 ++- base/reflection.jl | 33 ++++++++++++++++++++++----------- src/jltypes.c | 2 ++ test/core.jl | 19 +++++++++++++++++++ test/dict.jl | 8 ++++++++ 5 files changed, 53 insertions(+), 12 deletions(-) diff --git a/base/dict.jl b/base/dict.jl index 359016bd3c2a8..55a364dc97e6a 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -258,11 +258,12 @@ function empty!(h::Dict{K,V}) where V where K end # get the index where a key is stored, or -1 if not present -function ht_keyindex(h::Dict{K,V}, key) where V where K +@assume_effects :terminates_locally function ht_keyindex(h::Dict{K,V}, key) where V where K isempty(h) && return -1 sz = length(h.keys) iter = 0 maxprobe = h.maxprobe + maxprobe < sz || throw(AssertionError()) # This error will never trigger, but is needed for terminates_locally to be valid index, sh = hashindex(key, sz) keys = h.keys diff --git a/base/reflection.jl b/base/reflection.jl index fc45099e49ab3..0156db00c9f52 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -334,17 +334,6 @@ macro locals() return Expr(:locals) end -""" - objectid(x) -> UInt - -Get a hash value for `x` based on object identity. - -If `x === y` then `objectid(x) == objectid(y)`, and usually when `x !== y`, `objectid(x) != objectid(y)`. - -See also [`hash`](@ref), [`IdDict`](@ref). -""" -objectid(@nospecialize(x)) = ccall(:jl_object_id, UInt, (Any,), x) - # concrete datatype predicates datatype_fieldtypes(x::DataType) = ccall(:jl_get_fieldtypes, Core.SimpleVector, (Any,), x) @@ -600,6 +589,28 @@ Return `true` if `x` is an instance of an [`isbitstype`](@ref) type. """ isbits(@nospecialize x) = isbitstype(typeof(x)) +""" + objectid(x) -> UInt + +Get a hash value for `x` based on object identity. + +If `x === y` then `objectid(x) == objectid(y)`, and usually when `x !== y`, `objectid(x) != objectid(y)`. + +See also [`hash`](@ref), [`IdDict`](@ref). +""" +function objectid(x) + # objectid is foldable iff it isn't a pointer. + if isidentityfree(typeof(x)) + return _foldable_objectid(x) + end + return _objectid(x) +end +function _foldable_objectid(@nospecialize(x)) + @_foldable_meta + _objectid(x) +end +_objectid(@nospecialize(x)) = ccall(:jl_object_id, UInt, (Any,), x) + """ isdispatchtuple(T) diff --git a/src/jltypes.c b/src/jltypes.c index c4bd02f8ae37d..3294f7d15fc0c 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3244,6 +3244,8 @@ void jl_init_types(void) JL_GC_DISABLED // Technically not ismutationfree, but there's a separate system to deal // with mutations for global state. jl_module_type->ismutationfree = 1; + // Module object identity is determined by its name and parent name. + jl_module_type->isidentityfree = 1; // Array's mutable data is hidden, so we need to override it ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_array_type))->ismutationfree = 0; diff --git a/test/core.jl b/test/core.jl index daec51ab5b566..efd32c9789a59 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7987,3 +7987,22 @@ f48950(::Union{Int,d}, ::Union{c,Nothing}...) where {c,d} = 1 # Module as tparam in unionall struct ModTParamUnionAll{A, B}; end @test isa(objectid(ModTParamUnionAll{Base}), UInt) + +# effects for objectid +for T in (Int, String, Symbol, Module) + @test Core.Compiler.is_foldable(Base.infer_effects(objectid, (T,))) + @test Core.Compiler.is_foldable(Base.infer_effects(hash, (T,))) + @test Core.Compiler.is_foldable(Base.infer_effects(objectid, (Some{T},))) + @test Core.Compiler.is_foldable(Base.infer_effects(hash, (Some{T},))) + @test Core.Compiler.is_foldable(Base.infer_effects(objectid, (Some{Some{T}},))) + @test Core.Compiler.is_foldable(Base.infer_effects(hash, (Some{Some{T}},))) + @test Core.Compiler.is_foldable(Base.infer_effects(objectid, (Tuple{T},))) + @test Core.Compiler.is_foldable(Base.infer_effects(hash, (Tuple{T},))) + @test Core.Compiler.is_foldable(Base.infer_effects(objectid, (Tuple{T,T},))) + @test Core.Compiler.is_foldable(Base.infer_effects(hash, (Tuple{T,T},))) +end +@test !Core.Compiler.is_consistent(Base.infer_effects(objectid, (Ref{Int},))) +@test !Core.Compiler.is_consistent(Base.infer_effects(objectid, (Tuple{Ref{Int}},))) +# objectid for datatypes is inconsistant for types that have unbound type parameters. +@test !Core.Compiler.is_consistent(Base.infer_effects(objectid, (DataType,))) +@test !Core.Compiler.is_consistent(Base.infer_effects(objectid, (Tuple{Vector{Int}},))) diff --git a/test/dict.jl b/test/dict.jl index 65f8939bc6dfc..6a47c3c6eea8b 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -1363,3 +1363,11 @@ end sizehint!(d, 10) @test length(d.slots) < 100 end + +# getindex is :effect_free and :terminates but not :consistent +for T in (Int, Float64, String, Symbol) + @test !Core.Compiler.is_consistent(Base.infer_effects(getindex, (Dict{T,Any}, T))) + @test Core.Compiler.is_effect_free(Base.infer_effects(getindex, (Dict{T,Any}, T))) + @test !Core.Compiler.is_nothrow(Base.infer_effects(getindex, (Dict{T,Any}, T))) + @test Core.Compiler.is_terminates(Base.infer_effects(getindex, (Dict{T,Any}, T))) +end From b291522977bdd8d5fdbc29e5795aee8c42b2e046 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Tue, 25 Apr 2023 18:50:50 +0800 Subject: [PATCH 586/775] Subtype: minor optimization for `simple_intersect` (#49477) 1. remove duplicated disjoint check. 2. add a fast path for all disjoint case. --- src/jltypes.c | 61 +++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/src/jltypes.c b/src/jltypes.c index 3294f7d15fc0c..622f4f3222555 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -719,20 +719,6 @@ jl_value_t *simple_union(jl_value_t *a, jl_value_t *b) int obviously_disjoint(jl_value_t *a, jl_value_t *b, int specificity); -static int simple_disjoint(jl_value_t *a, jl_value_t *b, int hasfree) -{ - if (jl_is_uniontype(b)) { - jl_value_t *b1 = ((jl_uniontype_t *)b)->a, *b2 = ((jl_uniontype_t *)b)->b; - JL_GC_PUSH2(&b1, &b2); - int res = simple_disjoint(a, b1, hasfree) && simple_disjoint(a, b2, hasfree); - JL_GC_POP(); - return res; - } - if (!hasfree && !jl_has_free_typevars(b)) - return jl_has_empty_intersection(a, b); - return obviously_disjoint(a, b, 0); -} - jl_value_t *simple_intersect(jl_value_t *a, jl_value_t *b, int overesi) { // Unlike `Union`, we don't unwrap `UnionAll` here to avoid possible widening. @@ -746,19 +732,31 @@ jl_value_t *simple_intersect(jl_value_t *a, jl_value_t *b, int overesi) flatten_type_union(&b, 1, temp, &count, 0); assert(count == nt); size_t i, j; + int8_t *stemp = (int8_t *)alloca(count); // first remove disjoint elements. + memset(stemp, 0, count); + for (i = 0; i < nta; i++) { + int hasfree = jl_has_free_typevars(temp[i]); + for (j = nta; j < nt; j++) { + if (!stemp[i] || !stemp[j]) { + int intersect = !hasfree && !jl_has_free_typevars(temp[j]); + if (!(intersect ? jl_has_empty_intersection(temp[i], temp[j]) : obviously_disjoint(temp[i], temp[j], 0))) + stemp[i] = stemp[j] = 1; + } + } + } for (i = 0; i < nt; i++) { - if (simple_disjoint(temp[i], (i < nta ? b : a), jl_has_free_typevars(temp[i]))) - temp[i] = NULL; + temp[i] = stemp[i] ? temp[i] : NULL; } // then check subtyping. // stemp[k] == -1 : ∃i temp[k] >:ₛ temp[i] // stemp[k] == 1 : ∃i temp[k] == temp[i] // stemp[k] == 2 : ∃i temp[k] <:ₛ temp[i] - int8_t *stemp = (int8_t *)alloca(count); memset(stemp, 0, count); + int all_disjoint = 1, subs[2] = {1, 1}, rs[2] = {1, 1}; for (i = 0; i < nta; i++) { if (temp[i] == NULL) continue; + all_disjoint = 0; int hasfree = jl_has_free_typevars(temp[i]); for (j = nta; j < nt; j++) { if (temp[j] == NULL) continue; @@ -778,22 +776,23 @@ jl_value_t *simple_intersect(jl_value_t *a, jl_value_t *b, int overesi) } } } - int subs[2] = {1, 1}, rs[2] = {1, 1}; - for (i = 0; i < nt; i++) { - subs[i >= nta] &= (temp[i] == NULL || stemp[i] > 0); - rs[i >= nta] &= (temp[i] != NULL && stemp[i] > 0); - } - // return a(b) if a(b) <: b(a) - if (rs[0]) { - JL_GC_POP(); - return a; - } - if (rs[1]) { - JL_GC_POP(); - return b; + if (!all_disjoint) { + for (i = 0; i < nt; i++) { + subs[i >= nta] &= (temp[i] == NULL || stemp[i] > 0); + rs[i >= nta] &= (temp[i] != NULL && stemp[i] > 0); + } + // return a(b) if a(b) <: b(a) + if (rs[0]) { + JL_GC_POP(); + return a; + } + if (rs[1]) { + JL_GC_POP(); + return b; + } } // return `Union{}` for `merge_env` if we can't prove `<:` or `>:` - if (!overesi && !subs[0] && !subs[1]) { + if (all_disjoint || (!overesi && !subs[0] && !subs[1])) { JL_GC_POP(); return jl_bottom_type; } From 3db036eed80c1c0b90dd027b5382e9e0d12df652 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Tue, 25 Apr 2023 07:17:48 -0400 Subject: [PATCH 587/775] [Inference] limit inference timing recording to `NativeInterpreter` only (#49391) The logic of `Core.Compiler.Timings` assumes that the whole recorded inference graph is constructed by the same interpreter, thus we should limit the inference timing recording to `NativeInterpreter` only. External `AbstractInterpreter` can implement its own recording logic, likely by reusing existing `Core.Compiler.Timings` utilities, in a way that does not interfere with the recording for native compilation pipeline. --------- Co-authored-by: Shuhei Kadowaki --- base/compiler/ssair/irinterp.jl | 13 ++++++++----- base/compiler/typeinfer.jl | 8 +++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index d171cceb842e9..a479bb1f99a82 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -38,7 +38,7 @@ function concrete_eval_invoke(interp::AbstractInterpreter, newirsv = IRInterpretationState(interp, code, mi, argtypes, world) if newirsv !== nothing newirsv.parent = irsv - return _ir_abstract_constant_propagation(interp, newirsv) + return ir_abstract_constant_propagation(interp, newirsv) end return Pair{Any,Bool}(nothing, is_nothrow(effects)) end @@ -194,6 +194,8 @@ end default_reprocess(::AbstractInterpreter, ::IRInterpretationState) = nothing function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IRInterpretationState; extra_reprocess::Union{Nothing,BitSet} = default_reprocess(interp, irsv)) + interp = switch_to_irinterp(interp) + (; ir, tpdum, ssa_refined) = irsv bbs = ir.cfg.blocks @@ -342,16 +344,17 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR return Pair{Any,Bool}(maybe_singleton_const(ultimate_rt), nothrow) end -function ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IRInterpretationState) - irinterp = switch_to_irinterp(interp) +function ir_abstract_constant_propagation(interp::NativeInterpreter, irsv::IRInterpretationState) if __measure_typeinf__[] inf_frame = Timings.InferenceFrameInfo(irsv.mi, irsv.world, VarState[], Any[], length(irsv.ir.argtypes)) Timings.enter_new_timer(inf_frame) - ret = _ir_abstract_constant_propagation(irinterp, irsv) + ret = _ir_abstract_constant_propagation(interp, irsv) append!(inf_frame.slottypes, irsv.ir.argtypes) Timings.exit_current_timer(inf_frame) return ret else - return _ir_abstract_constant_propagation(irinterp, irsv) + return _ir_abstract_constant_propagation(interp, irsv) end end +ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IRInterpretationState) = + _ir_abstract_constant_propagation(interp, irsv) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 1eec73d0435bd..5dd0267c52a81 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -204,9 +204,9 @@ If set to `true`, record per-method-instance timings within type inference in th __set_measure_typeinf(onoff::Bool) = __measure_typeinf__[] = onoff const __measure_typeinf__ = fill(false) -# Wrapper around _typeinf that optionally records the exclusive time for each invocation. -function typeinf(interp::AbstractInterpreter, frame::InferenceState) - interp = switch_from_irinterp(interp) +# Wrapper around `_typeinf` that optionally records the exclusive time for +# each inference performed by `NativeInterpreter`. +function typeinf(interp::NativeInterpreter, frame::InferenceState) if __measure_typeinf__[] Timings.enter_new_timer(frame) v = _typeinf(interp, frame) @@ -216,6 +216,7 @@ function typeinf(interp::AbstractInterpreter, frame::InferenceState) return _typeinf(interp, frame) end end +typeinf(interp::AbstractInterpreter, frame::InferenceState) = _typeinf(interp, frame) function finish!(interp::AbstractInterpreter, caller::InferenceResult) # If we didn't transform the src for caching, we may have to transform @@ -242,6 +243,7 @@ function finish!(interp::AbstractInterpreter, caller::InferenceResult) end function _typeinf(interp::AbstractInterpreter, frame::InferenceState) + interp = switch_from_irinterp(interp) typeinf_nocycle(interp, frame) || return false # frame is now part of a higher cycle # with no active ip's, frame is done frames = frame.callers_in_cycle From e1de57fb3fbe95770d88cca724ca27a43a9192a0 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Tue, 25 Apr 2023 09:18:00 -0700 Subject: [PATCH 588/775] [BinaryPlatforms] Change "shortest match" algorithm to "best match" My assertion in the previous attempt to fix this issue was incorrect: > We define a simpler match as one that has fewer tags overall. > As these candidate matches have already been filtered to match the > given platform, the only other tags that exist are ones that are in > addition to the tags declared by the platform. Hence, selecting the > minimum in number of tags is equivalent to selecting the closest match. This is demonstrably false, by my own test case: ``` platforms = Dict( Platform("x86_64", "linux") => "bad", Platform("x86_64", "linux"; sanitize="memory") => "good", ) select_platform(platforms, Platform("x86_64", "linux"; sanitize="memory")) == "good" ``` In this case, because there exists a candidate that is _more general_ than the provided platform type, the shortest match is no longer the best match. This PR performs a more rigorous matching that works more reliably in all cases. --- base/binaryplatforms.jl | 37 ++++++++++++++++++++++--------------- test/binaryplatforms.jl | 21 ++++++++++++++------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/base/binaryplatforms.jl b/base/binaryplatforms.jl index d59b6397b1f73..04a0073b7ff08 100644 --- a/base/binaryplatforms.jl +++ b/base/binaryplatforms.jl @@ -1068,21 +1068,28 @@ function select_platform(download_info::Dict, platform::AbstractPlatform = HostP end # At this point, we may have multiple possibilities. We now engage a multi- - # stage selection algorithm, where we first choose simpler matches over more - # complex matches. We define a simpler match as one that has fewer tags - # overall. As these candidate matches have already been filtered to match - # the given platform, the only other tags that exist are ones that are in - # addition to the tags declared by the platform. Hence, selecting the - # minimum in number of tags is equivalent to selecting the closest match. - min_tag_count = minimum(length(tags(p)) for p in ps) - filter!(p -> length(tags(p)) == min_tag_count, ps) - - # Now we _still_ may continue to have multiple matches, so we now simply sort - # the candidate matches by their triplets and take the last one, so as to - # generally choose the latest release (e.g. a `libgfortran5` tarball over a - # `libgfortran3` tarball). - p = last(sort(ps, by = p -> triplet(p))) - return download_info[p] + # stage selection algorithm, where we first sort the matches by how complete + # the match is, e.g. preferring matches where the intersection of tags is + # equal to the union of the tags: + function match_loss(a, b) + a_tags = Set(keys(tags(a))) + b_tags = Set(keys(tags(b))) + return length(union(a_tags, b_tags)) - length(intersect(a_tags, b_tags)) + end + + # We prefer these better matches, and secondarily reverse-sort by triplet so + # as to generally choose the latest release (e.g. a `libgfortran5` tarball + # over a `libgfortran3` tarball). + ps = sort(ps, lt = (a, b) -> begin + loss_a = match_loss(a, platform) + loss_b = match_loss(b, platform) + if loss_a != loss_b + return loss_a < loss_b + end + return triplet(a) > triplet(b) + end) + + return download_info[first(ps)] end # precompiles to reduce latency (see https://github.com/JuliaLang/julia/pull/43990#issuecomment-1025692379) diff --git a/test/binaryplatforms.jl b/test/binaryplatforms.jl index 54154f492168f..8de522e9c6c8b 100644 --- a/test/binaryplatforms.jl +++ b/test/binaryplatforms.jl @@ -330,8 +330,7 @@ end # Ambiguity test @test select_platform(platforms, P("aarch64", "linux")) == "linux3" @test select_platform(platforms, P("aarch64", "linux"; libgfortran_version=v"3")) == "linux3" - # This one may be surprising, but we still match `linux3`, and since linux3 is shorter, we choose it. - @test select_platform(platforms, P("aarch64", "linux"; libgfortran_version=v"3", libstdcxx_version=v"3.4.18")) === "linux3" + @test select_platform(platforms, P("aarch64", "linux"; libgfortran_version=v"3", libstdcxx_version=v"3.4.18")) === "linux5" @test select_platform(platforms, P("aarch64", "linux"; libgfortran_version=v"4")) === nothing @test select_platform(platforms, P("x86_64", "macos")) == "mac4" @@ -343,13 +342,21 @@ end # Sorry, Alex. ;) @test select_platform(platforms, P("x86_64", "freebsd")) === nothing - # The new "prefer shortest matching" algorithm is meant to be used to resolve ambiguities such as the following: + # The new "most complete match" algorithm deals with ambiguities as follows: platforms = Dict( - # Typical binning test - P("x86_64", "linux") => "good", - P("x86_64", "linux"; sanitize="memory") => "bad", + P("x86_64", "linux") => "normal", + P("x86_64", "linux"; sanitize="memory") => "sanitized", + ) + @test select_platform(platforms, P("x86_64", "linux")) == "normal" + @test select_platform(platforms, P("x86_64", "linux"; sanitize="memory")) == "sanitized" + + # Ties are broken by reverse-sorting by triplet: + platforms = Dict( + P("x86_64", "linux"; libgfortran_version=v"3") => "libgfortran3", + P("x86_64", "linux"; libgfortran_version=v"4") => "libgfortran4", ) - @test select_platform(platforms, P("x86_64", "linux")) == "good" + @test select_platform(platforms, P("x86_64", "linux")) == "libgfortran4" + @test select_platform(platforms, P("x86_64", "linux"; libgfortran_version=v"3")) == "libgfortran3" end @testset "Custom comparators" begin From bc5dd5387c0a170428b6f64653b87a3f0e360146 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Tue, 25 Apr 2023 11:57:19 -0700 Subject: [PATCH 589/775] Rearrange allocation profiling in Julia manual (#48713) As mentioned in #48070, the allocation profiler now provides better functionality than the `--track-allocation` option. This rearranges the sections in the manual to reflect this, and adds a note to that effect. It also mentions ProfileCanvas.jl, which seems to be better supported than PProf.jl. --- doc/src/manual/profile.md | 60 +++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/doc/src/manual/profile.md b/doc/src/manual/profile.md index f3438d2a80524..e5f1d6c417fa6 100644 --- a/doc/src/manual/profile.md +++ b/doc/src/manual/profile.md @@ -65,6 +65,7 @@ One "family" of visualizers is based on [FlameGraphs.jl](https://github.com/timh - [StatProfilerHTML.jl](https://github.com/tkluck/StatProfilerHTML.jl) produces HTML and presents some additional summaries, and also integrates well with Jupyter notebooks - [ProfileSVG.jl](https://github.com/timholy/ProfileSVG.jl) renders SVG - [PProf.jl](https://github.com/JuliaPerf/PProf.jl) serves a local website for inspecting graphs, flamegraphs and more +- [ProfileCanvas.jl](https://github.com/pfitzseb/ProfileCanvas.jl) is a HTML canvas based profile viewer UI, used by the [Julia VS Code extension](https://www.julia-vscode.org/), but can also generate interactive HTML files. An entirely independent approach to profile visualization is [PProf.jl](https://github.com/vchuravy/PProf.jl), which uses the external `pprof` tool. @@ -308,26 +309,6 @@ and specific lines triggering allocation can often be inferred from profiling vi collection that these lines incur. However, sometimes it is more efficient to directly measure the amount of memory allocated by each line of code. -### Line-by-Line Allocation Tracking - -To measure allocation line-by-line, start Julia with the `--track-allocation=` command-line -option, for which you can choose `none` (the default, do not measure allocation), `user` (measure -memory allocation everywhere except Julia's core code), or `all` (measure memory allocation at -each line of Julia code). Allocation gets measured for each line of compiled code. When you quit -Julia, the cumulative results are written to text files with `.mem` appended after the file name, -residing in the same directory as the source file. Each line lists the total number of bytes -allocated. The [`Coverage` package](https://github.com/JuliaCI/Coverage.jl) contains some elementary -analysis tools, for example to sort the lines in order of number of bytes allocated. - -In interpreting the results, there are a few important details. Under the `user` setting, the -first line of any function directly called from the REPL will exhibit allocation due to events -that happen in the REPL code itself. More significantly, JIT-compilation also adds to allocation -counts, because much of Julia's compiler is written in Julia (and compilation usually requires -memory allocation). The recommended procedure is to force compilation by executing all the commands -you want to analyze, then call [`Profile.clear_malloc_data()`](@ref) to reset all allocation counters. - Finally, execute the desired commands and quit Julia to trigger the generation of the `.mem` -files. - ### GC Logging While [`@time`](@ref) logs high-level stats about memory usage and garbage collection over the course @@ -337,17 +318,20 @@ and how much garbage it collects each time. This can be enabled with [`GC.enable_logging(true)`](@ref), which causes Julia to log to stderr every time a garbage collection happens. -### Allocation Profiler +### [Allocation Profiler](@id allocation-profiler) + +!!! compat "Julia 1.8" + This functionality requires at least Julia 1.8. The allocation profiler records the stack trace, type, and size of each allocation while it is running. It can be invoked with [`Profile.Allocs.@profile`](@ref). This information about the allocations is returned as an array of `Alloc` -objects, wrapped in an `AllocResults` object. The best way to visualize -these is currently with the [PProf.jl](https://github.com/JuliaPerf/PProf.jl) -package, which can visualize the call stacks which are making the most -allocations. +objects, wrapped in an `AllocResults` object. The best way to visualize these is +currently with the [PProf.jl](https://github.com/JuliaPerf/PProf.jl) and +[ProfileCanvas.jl](https://github.com/pfitzseb/ProfileCanvas.jl) packages, which +can visualize the call stacks which are making the most allocations. The allocation profiler does have significant overhead, so a `sample_rate` argument can be passed to speed it up by making it skip some allocations. @@ -364,6 +348,32 @@ Passing `sample_rate=1.0` will make it record everything (which is slow); You can read more about the missing types and the plan to improve this, here: [issue #43688](https://github.com/JuliaLang/julia/issues/43688). +#### Line-by-Line Allocation Tracking + +An alternative way to measure allocations is to start Julia with the `--track-allocation=` command-line +option, for which you can choose `none` (the default, do not measure allocation), `user` (measure +memory allocation everywhere except Julia's core code), or `all` (measure memory allocation at +each line of Julia code). Allocation gets measured for each line of compiled code. When you quit +Julia, the cumulative results are written to text files with `.mem` appended after the file name, +residing in the same directory as the source file. Each line lists the total number of bytes +allocated. The [`Coverage` package](https://github.com/JuliaCI/Coverage.jl) contains some elementary +analysis tools, for example to sort the lines in order of number of bytes allocated. + +In interpreting the results, there are a few important details. Under the `user` setting, the +first line of any function directly called from the REPL will exhibit allocation due to events +that happen in the REPL code itself. More significantly, JIT-compilation also adds to allocation +counts, because much of Julia's compiler is written in Julia (and compilation usually requires +memory allocation). The recommended procedure is to force compilation by executing all the commands +you want to analyze, then call [`Profile.clear_malloc_data()`](@ref) to reset all allocation counters. + Finally, execute the desired commands and quit Julia to trigger the generation of the `.mem` +files. + +!!! note + + `--track-allocation` changes code generation to log the allocations, and so the allocations may + be different than what happens without the option. We recommend using the + [allocation profiler](@ref allocation-profiler) instead. + ## External Profiling Currently Julia supports `Intel VTune`, `OProfile` and `perf` as external profiling tools. From b12ddca3b18341d2abca10b0858f554e36452cfb Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Wed, 26 Apr 2023 01:10:37 +0200 Subject: [PATCH 590/775] doc: manual: constructors: xref "plain data" to isbits (#49492) --- doc/src/manual/constructors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/manual/constructors.md b/doc/src/manual/constructors.md index dad96e374742e..6ec206dade335 100644 --- a/doc/src/manual/constructors.md +++ b/doc/src/manual/constructors.md @@ -244,8 +244,8 @@ ERROR: UndefRefError: access to undefined reference This avoids the need to continually check for `null` values. However, not all object fields are references. Julia considers some types to be "plain data", meaning all of their data is self-contained and does not reference other objects. The plain data types consist of primitive types (e.g. `Int`) -and immutable structs of other plain data types. The initial contents of a plain data type is -undefined: +and immutable structs of other plain data types (see also: [`isbits`](@ref), [`isbitstype`](@ref)). +The initial contents of a plain data type is undefined: ```julia-repl julia> struct HasPlain From 2fa6970a8c876ea8e22994dc23736515e8d7b88d Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Wed, 26 Apr 2023 01:14:11 -0300 Subject: [PATCH 591/775] fix in GC chunking code (#49505) --- src/gc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gc.c b/src/gc.c index 388cf7fa6a671..3c116b4cd352f 100644 --- a/src/gc.c +++ b/src/gc.c @@ -2185,7 +2185,7 @@ STATIC_INLINE void gc_mark_array8(jl_ptls_t ptls, jl_value_t *ary8_parent, jl_va } } if (too_big) { - jl_gc_chunk_t c = {GC_objary_chunk, ary8_parent, scan_end, + jl_gc_chunk_t c = {GC_ary8_chunk, ary8_parent, scan_end, ary8_end, elem_begin, elem_end, 0, nptr}; gc_chunkqueue_push(mq, &c); @@ -2247,7 +2247,7 @@ STATIC_INLINE void gc_mark_array16(jl_ptls_t ptls, jl_value_t *ary16_parent, jl_ } } if (too_big) { - jl_gc_chunk_t c = {GC_objary_chunk, ary16_parent, scan_end, + jl_gc_chunk_t c = {GC_ary16_chunk, ary16_parent, scan_end, ary16_end, elem_begin, elem_end, elsize, nptr}; gc_chunkqueue_push(mq, &c); From e6b707cfa78e5c76379431cf9d6335f1ab8760e7 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 26 Apr 2023 09:42:24 +0200 Subject: [PATCH 592/775] show the root module in tracing for __init__ as well (#49480) * show the root module in tracing for __init__ as well When a submodule is running `__init__` it can sometimes be non trivial to find out what package that module actually lives in Co-authored-by: Cody Tapscott <84105208+topolarity@users.noreply.github.com> --- src/module.c | 9 +++++++++ src/timing.c | 12 ++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/module.c b/src/module.c index 4ac0d48a6f9e0..3428c5e8b59f9 100644 --- a/src/module.c +++ b/src/module.c @@ -928,6 +928,15 @@ JL_DLLEXPORT jl_value_t *jl_module_names(jl_module_t *m, int all, int imported) JL_DLLEXPORT jl_sym_t *jl_module_name(jl_module_t *m) { return m->name; } JL_DLLEXPORT jl_module_t *jl_module_parent(jl_module_t *m) { return m->parent; } +jl_module_t *jl_module_root(jl_module_t *m) +{ + while (1) { + if (m->parent == NULL || m->parent == m) + return m; + m = m->parent; + } +} + JL_DLLEXPORT jl_uuid_t jl_module_build_id(jl_module_t *m) { return m->build_id; } JL_DLLEXPORT jl_uuid_t jl_module_uuid(jl_module_t* m) { return m->uuid; } diff --git a/src/timing.c b/src/timing.c index 65c350f247b27..1628620f5b84a 100644 --- a/src/timing.c +++ b/src/timing.c @@ -6,6 +6,8 @@ #include "options.h" #include "stdio.h" +jl_module_t *jl_module_root(jl_module_t *m); + #ifdef __cplusplus extern "C" { #endif @@ -165,8 +167,14 @@ JL_DLLEXPORT void jl_timing_show(jl_value_t *v, jl_timing_block_t *cur_block) JL_DLLEXPORT void jl_timing_show_module(jl_module_t *m, jl_timing_block_t *cur_block) { #ifdef USE_TRACY - const char *module_name = jl_symbol_name(m->name); - TracyCZoneText(*(cur_block->tracy_ctx), module_name, strlen(module_name)); + jl_module_t *root = jl_module_root(m); + if (root == m || root == jl_main_module) { + const char *module_name = jl_symbol_name(m->name); + TracyCZoneText(*(cur_block->tracy_ctx), module_name, strlen(module_name)); + } else { + + jl_timing_printf(cur_block, "%s.%s", jl_symbol_name(root->name), jl_symbol_name(m->name)); + } #endif } From 960870e3c65a3b94047252f99903b3cba544363e Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 26 Apr 2023 09:43:59 +0200 Subject: [PATCH 593/775] instrument `jl_load_dynamic_library` to the profiler (#49496) --- src/dlload.c | 10 ++++++---- src/timing.c | 6 ------ src/timing.h | 8 ++++++++ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/dlload.c b/src/dlload.c index 9f4e8be29952d..701a93786bed2 100644 --- a/src/dlload.c +++ b/src/dlload.c @@ -272,12 +272,15 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, } handle = dlopen(info.dli_fname, RTLD_NOW); #endif - goto done; + return handle; } abspath = jl_isabspath(modname); is_atpath = 0; + JL_TIMING(DL_OPEN, DL_OPEN); + jl_timing_printf(JL_TIMING_CURRENT_BLOCK, gnu_basename(modname)); + // Detect if our `modname` is something like `@rpath/libfoo.dylib` #ifdef _OS_DARWIN_ size_t nameLen = strlen(modname); @@ -334,7 +337,7 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, #endif handle = jl_dlopen(path, flags); if (handle) - goto done; + return handle; #ifdef _OS_WINDOWS_ err = GetLastError(); } @@ -354,7 +357,7 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, snprintf(path, PATHBUF, "%s%s", modname, ext); handle = jl_dlopen(path, flags); if (handle) - goto done; + return handle; #ifdef _OS_WINDOWS_ err = GetLastError(); break; // LoadLibrary already tested the rest @@ -377,7 +380,6 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, } handle = NULL; -done: return handle; } diff --git a/src/timing.c b/src/timing.c index 1628620f5b84a..006e9b5dced15 100644 --- a/src/timing.c +++ b/src/timing.c @@ -143,12 +143,6 @@ jl_timing_block_t *jl_timing_block_exit_task(jl_task_t *ct, jl_ptls_t ptls) return blk; } -static inline const char *gnu_basename(const char *path) -{ - char *base = strrchr(path, '/'); - return base ? base+1 : path; -} - JL_DLLEXPORT void jl_timing_show(jl_value_t *v, jl_timing_block_t *cur_block) { #ifdef USE_TRACY diff --git a/src/timing.h b/src/timing.h index ddf9b1d5201d8..4a30da583badc 100644 --- a/src/timing.h +++ b/src/timing.h @@ -5,6 +5,12 @@ #include "julia.h" +static inline const char *gnu_basename(const char *path) +{ + const char *base = strrchr(path, '/'); + return base ? base+1 : path; +} + #ifdef __cplusplus extern "C" { #endif @@ -135,6 +141,8 @@ void jl_timing_printf(jl_timing_block_t *cur_block, const char *format, ...); X(SAVE_MODULE) \ X(INIT_MODULE) \ X(LOCK_SPIN) \ + X(DL_OPEN) \ + #define JL_TIMING_EVENTS \ From 3f7ae8b167dd97b986ccb7617979b87360a86344 Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Wed, 26 Apr 2023 14:09:52 +0000 Subject: [PATCH 594/775] Add more profiling events (#49493) --- src/aotcompile.cpp | 3 ++- src/gf.c | 2 +- src/processor.cpp | 1 + src/stackwalk.c | 2 ++ src/staticdata.c | 3 ++- src/staticdata_utils.c | 3 +++ src/threading.c | 8 +++++++- src/timing.h | 31 +++++++++++++++++++++---------- 8 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index fc1d4074e92bb..391c5d3df46fb 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -268,6 +268,7 @@ static void jl_ci_cache_lookup(const jl_cgparams_t &cgparams, jl_method_instance extern "C" JL_DLLEXPORT void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvmmod, const jl_cgparams_t *cgparams, int _policy, int _imaging_mode, int _external_linkage, size_t _world) { + JL_TIMING(NATIVE_AOT, NATIVE_Create); ++CreateNativeCalls; CreateNativeMax.updateMax(jl_array_len(methods)); if (cgparams == NULL) @@ -1448,7 +1449,7 @@ void jl_dump_native_impl(void *native_code, const char *asm_fname, const char *sysimg_data, size_t sysimg_len, ios_t *s) { - JL_TIMING(NATIVE_DUMP, NATIVE_DUMP); + JL_TIMING(NATIVE_AOT, NATIVE_Dump); jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code; if (!bc_fname && !unopt_bc_fname && !obj_fname && !asm_fname) { LLVM_DEBUG(dbgs() << "No output requested, skipping native code dump?\n"); diff --git a/src/gf.c b/src/gf.c index 23ce8d33c82d2..b7d4dd70dc8a4 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2266,7 +2266,6 @@ jl_method_instance_t *jl_method_lookup(jl_value_t **args, size_t nargs, size_t w JL_DLLEXPORT jl_value_t *jl_matching_methods(jl_tupletype_t *types, jl_value_t *mt, int lim, int include_ambiguous, size_t world, size_t *min_valid, size_t *max_valid, int *ambig) { - JL_TIMING(METHOD_MATCH, METHOD_MATCH); if (ambig != NULL) *ambig = 0; jl_value_t *unw = jl_unwrap_unionall((jl_value_t*)types); @@ -3304,6 +3303,7 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, int intersections, size_t world, int cache_result, size_t *min_valid, size_t *max_valid, int *ambig) { + JL_TIMING(METHOD_MATCH, METHOD_MATCH); if (world > jl_atomic_load_acquire(&jl_world_counter)) return jl_nothing; // the future is not enumerable int has_ambiguity = 0; diff --git a/src/processor.cpp b/src/processor.cpp index 0b4f9b1243446..88fcb813d8248 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -629,6 +629,7 @@ static inline std::vector> &get_cmdline_targets(F &&feature_cb) template static inline jl_image_t parse_sysimg(void *hdl, F &&callback) { + JL_TIMING(LOAD_IMAGE, LOAD_Processor); jl_image_t res{}; const jl_image_pointers_t *pointers; diff --git a/src/stackwalk.c b/src/stackwalk.c index caf0705b85be7..093467750d573 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -321,6 +321,7 @@ static void decode_backtrace(jl_bt_element_t *bt_data, size_t bt_size, JL_DLLEXPORT jl_value_t *jl_get_backtrace(void) { + JL_TIMING(STACKWALK, STACKWALK_Backtrace); jl_excstack_t *s = jl_current_task->excstack; jl_bt_element_t *bt_data = NULL; size_t bt_size = 0; @@ -343,6 +344,7 @@ JL_DLLEXPORT jl_value_t *jl_get_backtrace(void) JL_DLLEXPORT jl_value_t *jl_get_excstack(jl_task_t* task, int include_bt, int max_entries) { JL_TYPECHK(current_exceptions, task, (jl_value_t*)task); + JL_TIMING(STACKWALK, STACKWALK_Excstack); jl_task_t *ct = jl_current_task; if (task != ct && jl_atomic_load_relaxed(&task->_state) == JL_TASK_STATE_RUNNABLE) { jl_error("Inspecting the exception stack of a task which might " diff --git a/src/staticdata.c b/src/staticdata.c index b5d3aecaeb96e..2f61e91c8128e 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -2672,7 +2672,6 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl jl_array_t **ext_targets, jl_array_t **edges, char **base, arraylist_t *ccallable_list, pkgcachesizes *cachesizes) JL_GC_DISABLED { - JL_TIMING(SYSIMG_LOAD, SYSIMG_LOAD); int en = jl_gc_enable(0); ios_t sysimg, const_data, symbols, relocs, gvar_record, fptr_record; jl_serializer_state s; @@ -3195,6 +3194,7 @@ static jl_value_t *jl_validate_cache_file(ios_t *f, jl_array_t *depmods, uint64_ // TODO?: refactor to make it easier to create the "package inspector" static jl_value_t *jl_restore_package_image_from_stream(ios_t *f, jl_image_t *image, jl_array_t *depmods, int completeinfo) { + JL_TIMING(LOAD_IMAGE, LOAD_Pkgimg); uint64_t checksum = 0; int64_t dataendpos = 0; int64_t datastartpos = 0; @@ -3265,6 +3265,7 @@ static jl_value_t *jl_restore_package_image_from_stream(ios_t *f, jl_image_t *im static void jl_restore_system_image_from_stream(ios_t *f, jl_image_t *image, uint32_t checksum) { + JL_TIMING(LOAD_IMAGE, LOAD_Sysimg); jl_restore_system_image_from_stream_(f, image, NULL, checksum | ((uint64_t)0xfdfcfbfa << 32), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index ad9149bb54526..ca5cf9100f5d7 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -836,6 +836,7 @@ static void jl_copy_roots(jl_array_t *method_roots_list, uint64_t key) // verify that these edges intersect with the same methods as before static jl_array_t *jl_verify_edges(jl_array_t *targets, size_t minworld) { + JL_TIMING(VERIFY_IMAGE, VERIFY_Edges); size_t i, l = jl_array_len(targets) / 3; static jl_value_t *ulong_array JL_ALWAYS_LEAFTYPE = NULL; if (ulong_array == NULL) @@ -936,6 +937,7 @@ static jl_array_t *jl_verify_edges(jl_array_t *targets, size_t minworld) // Combine all edges relevant to a method to initialize the maxvalids list static jl_array_t *jl_verify_methods(jl_array_t *edges, jl_array_t *maxvalids) { + JL_TIMING(VERIFY_IMAGE, VERIFY_Methods); jl_value_t *loctag = NULL; jl_array_t *maxvalids2 = NULL; JL_GC_PUSH2(&loctag, &maxvalids2); @@ -1049,6 +1051,7 @@ static int jl_verify_graph_edge(size_t *maxvalids2_data, jl_array_t *edges, size // Visit all entries in edges, verify if they are valid static void jl_verify_graph(jl_array_t *edges, jl_array_t *maxvalids2) { + JL_TIMING(VERIFY_IMAGE, VERIFY_Graph); arraylist_t stack, visited; arraylist_new(&stack, 0); size_t i, n = jl_array_len(edges) / 2; diff --git a/src/threading.c b/src/threading.c index ea9ec8e16ca45..6718a47f5e836 100644 --- a/src/threading.c +++ b/src/threading.c @@ -744,8 +744,14 @@ void _jl_mutex_wait(jl_task_t *self, jl_mutex_t *lock, int safepoint) lock->count++; return; } - JL_TIMING(LOCK_SPIN, LOCK_SPIN); + // Don't use JL_TIMING for instant acquires, results in large blowup of events jl_profile_lock_start_wait(lock); + if (owner == NULL && jl_atomic_cmpswap(&lock->owner, &owner, self)) { + lock->count = 1; + jl_profile_lock_acquired(lock); + return; + } + JL_TIMING(LOCK_SPIN, LOCK_SPIN); while (1) { if (owner == NULL && jl_atomic_cmpswap(&lock->owner, &owner, self)) { lock->count = 1; diff --git a/src/timing.h b/src/timing.h index 4a30da583badc..4cdd32da8b195 100644 --- a/src/timing.h +++ b/src/timing.h @@ -133,16 +133,17 @@ void jl_timing_printf(jl_timing_block_t *cur_block, const char *format, ...); X(MACRO_INVOCATION) \ X(AST_COMPRESS) \ X(AST_UNCOMPRESS) \ - X(SYSIMG_LOAD) \ X(SYSIMG_DUMP) \ - X(NATIVE_DUMP) \ + X(NATIVE_AOT) \ X(ADD_METHOD) \ X(LOAD_MODULE) \ + X(LOAD_IMAGE) \ + X(VERIFY_IMAGE) \ X(SAVE_MODULE) \ X(INIT_MODULE) \ X(LOCK_SPIN) \ - X(DL_OPEN) \ - + X(STACKWALK) \ + X(DL_OPEN) \ #define JL_TIMING_EVENTS \ @@ -154,6 +155,16 @@ void jl_timing_printf(jl_timing_block_t *cur_block, const char *format, ...); X(CODEGEN_LLVM) \ X(CODEGEN_Codeinst) \ X(CODEGEN_Workqueue) \ + X(LOAD_Sysimg) \ + X(LOAD_Pkgimg) \ + X(LOAD_Processor) \ + X(VERIFY_Edges) \ + X(VERIFY_Methods) \ + X(VERIFY_Graph) \ + X(STACKWALK_Backtrace) \ + X(STACKWALK_Excstack) \ + X(NATIVE_Dump) \ + X(NATIVE_Create) \ enum jl_timing_owners { @@ -209,13 +220,13 @@ enum jl_timing_events { #endif #ifdef USE_ITTAPI -#define _ITTAPI_CTX_MEMBER int event; -#define _ITTAPI_CTOR(block, event) block->event = event -#define _ITTAPI_START(block) if (_jl_timing_enabled(block->event)) __itt_event_start(jl_timing_ittapi_events[block->event]) -#define _ITTAPI_STOP(block) if (_jl_timing_enabled(block->event)) __itt_event_end(jl_timing_ittapi_events[block->event]) +#define _ITTAPI_CTX_MEMBER int owner; int event; +#define _ITTAPI_CTOR(block, owner, event) block->owner = owner; block->event = event +#define _ITTAPI_START(block) if (_jl_timing_enabled(block->owner)) __itt_event_start(jl_timing_ittapi_events[block->event]) +#define _ITTAPI_STOP(block) if (_jl_timing_enabled(block->owner)) __itt_event_end(jl_timing_ittapi_events[block->event]) #else #define _ITTAPI_CTX_MEMBER -#define _ITTAPI_CTOR(block, event) +#define _ITTAPI_CTOR(block, owner, event) #define _ITTAPI_START(block) #define _ITTAPI_STOP(block) #endif @@ -287,7 +298,7 @@ STATIC_INLINE void _jl_timing_block_ctor(jl_timing_block_t *block, int owner, in uint64_t t = cycleclock(); (void)t; _COUNTS_CTOR(&block->counts_ctx, owner); _COUNTS_START(&block->counts_ctx, t); - _ITTAPI_CTOR(block, event); + _ITTAPI_CTOR(block, owner, event); _ITTAPI_START(block); jl_task_t *ct = jl_current_task; From a152d116bf79f327cd4adaa8712d864db9c5602b Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Wed, 26 Apr 2023 09:55:27 -0500 Subject: [PATCH 595/775] Nospecialize close(c::Channel, excp::Exception) on excp. (#49508) * Nospecialize close(c::Channel, excp::Exception) on excp. Fixes https://github.com/JuliaLang/julia/issues/49507. Avoids dynamic dispatch when closing a Channel with an Exception, and should avoid a call into the runtime for julia compilation when attempting to report an exception. * Add test for this case. --- base/channels.jl | 3 ++- test/channels.jl | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/base/channels.jl b/base/channels.jl index aa4d913dcdadd..33365c03e5d3d 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -183,7 +183,8 @@ Close a channel. An exception (optionally given by `excp`), is thrown by: * [`put!`](@ref) on a closed channel. * [`take!`](@ref) and [`fetch`](@ref) on an empty, closed channel. """ -function close(c::Channel, excp::Exception=closed_exception()) +close(c::Channel) = close(c, closed_exception()) # nospecialize on default arg seems to confuse makedocs +function close(c::Channel, @nospecialize(excp::Exception)) lock(c) try c.excp = excp diff --git a/test/channels.jl b/test/channels.jl index dbda5cf069081..89b0e5c09d7d8 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -626,3 +626,20 @@ end @test n_avail(c) == 0 end end + +# Issue #49507: stackoverflow in type inference caused by close(::Channel, ::Exception) +@testset "close(::Channel, ::StackOverflowError)" begin + ch = let result = Channel() + foo() = try + foo() + catch e; + close(result, e) + end + + foo() # This shouldn't fail with an internal stackoverflow error in inference. + + result + end + + @test (try take!(ch) catch e; e; end) isa StackOverflowError +end From 2cd0149fc6034d8785b66a1e0e2cadf26c4d1ca3 Mon Sep 17 00:00:00 2001 From: Nicholas Bauer Date: Wed, 26 Apr 2023 12:18:28 -0400 Subject: [PATCH 596/775] Lookup metadata for inlined frames for stack traces (#41099) --- base/compiler/ssair/inlining.jl | 33 +++++--- base/compiler/ssair/passes.jl | 2 +- base/compiler/typeinfer.jl | 2 +- base/stacktraces.jl | 135 +++++++++++++++++++++++++++++--- src/debuginfo.cpp | 2 +- src/julia.h | 4 +- src/method.c | 15 ++-- test/stacktraces.jl | 5 +- 8 files changed, 162 insertions(+), 36 deletions(-) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 6a2f5b5be0c99..1c9f4454abbfe 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -302,18 +302,32 @@ function finish_cfg_inline!(state::CFGInliningState) end end +# duplicated from IRShow +normalize_method_name(m::Method) = m.name +normalize_method_name(m::MethodInstance) = (m.def::Method).name +normalize_method_name(m::Symbol) = m +normalize_method_name(m) = Symbol("") +@noinline method_name(m::LineInfoNode) = normalize_method_name(m.method) + +inline_node_is_duplicate(topline::LineInfoNode, line::LineInfoNode) = + topline.module === line.module && + method_name(topline) === method_name(line) && + topline.file === line.file && + topline.line === line.line + function ir_inline_linetable!(linetable::Vector{LineInfoNode}, inlinee_ir::IRCode, - inlinee::Method, + inlinee::MethodInstance, inlined_at::Int32) - coverage = coverage_enabled(inlinee.module) + inlinee_def = inlinee.def::Method + coverage = coverage_enabled(inlinee_def.module) linetable_offset::Int32 = length(linetable) # Append the linetable of the inlined function to our line table topline::Int32 = linetable_offset + Int32(1) coverage_by_path = JLOptions().code_coverage == 3 - push!(linetable, LineInfoNode(inlinee.module, inlinee.name, inlinee.file, inlinee.line, inlined_at)) + push!(linetable, LineInfoNode(inlinee_def.module, inlinee, inlinee_def.file, inlinee_def.line, inlined_at)) oldlinetable = inlinee_ir.linetable extra_coverage_line = zero(Int32) - for oldline in 1:length(oldlinetable) + for oldline in eachindex(oldlinetable) entry = oldlinetable[oldline] if !coverage && coverage_by_path && is_file_tracked(entry.file) # include topline coverage entry if in path-specific coverage mode, and any file falls under path @@ -323,7 +337,7 @@ function ir_inline_linetable!(linetable::Vector{LineInfoNode}, inlinee_ir::IRCod (entry.inlined_at > 0 ? entry.inlined_at + linetable_offset + (oldline == 1) : inlined_at)) if oldline == 1 # check for a duplicate on the first iteration (likely true) - if newentry === linetable[topline] + if inline_node_is_duplicate(linetable[topline], newentry) continue else linetable_offset += 1 @@ -339,9 +353,10 @@ end function ir_prepare_inlining!(insert_node!::Inserter, inline_target::Union{IRCode, IncrementalCompact}, linetable::Vector{LineInfoNode}, ir′::IRCode, sparam_vals::SimpleVector, - def::Method, inlined_at::Int32, argexprs::Vector{Any}) + mi::MethodInstance, inlined_at::Int32, argexprs::Vector{Any}) + def = mi.def::Method topline::Int32 = length(linetable) + Int32(1) - linetable_offset, extra_coverage_line = ir_inline_linetable!(linetable, ir′, def, inlined_at) + linetable_offset, extra_coverage_line = ir_inline_linetable!(linetable, ir′, mi, inlined_at) if extra_coverage_line != 0 insert_node!(NewInstruction(Expr(:code_coverage_effect), Nothing, extra_coverage_line)) end @@ -371,11 +386,10 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}}) # Ok, do the inlining here sparam_vals = item.mi.sparam_vals - def = item.mi.def::Method inlined_at = compact.result[idx][:line] ((sp_ssa, argexprs), linetable_offset) = ir_prepare_inlining!(InsertHere(compact), - compact, linetable, item.ir, sparam_vals, def, inlined_at, argexprs) + compact, linetable, item.ir, sparam_vals, item.mi, inlined_at, argexprs) if boundscheck === :default || boundscheck === :propagate if (compact.result[idx][:flag] & IR_FLAG_INBOUNDS) != 0 @@ -385,6 +399,7 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector # If the iterator already moved on to the next basic block, # temporarily re-open in again. local return_value + def = item.mi.def::Method sig = def.sig # Special case inlining that maintains the current basic block if there's only one BB in the target new_new_offset = length(compact.new_new_nodes) diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 913b7cde6f606..7bf1b70487087 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1085,7 +1085,7 @@ function try_inline_finalizer!(ir::IRCode, argexprs::Vector{Any}, idx::Int, # TODO: Should there be a special line number node for inlined finalizers? inlined_at = ir[SSAValue(idx)][:line] ((sp_ssa, argexprs), linetable_offset) = ir_prepare_inlining!(InsertBefore(ir, SSAValue(idx)), ir, - ir.linetable, src, mi.sparam_vals, mi.def, inlined_at, argexprs) + ir.linetable, src, mi.sparam_vals, mi, inlined_at, argexprs) # TODO: Use the actual inliner here rather than open coding this special purpose inliner. spvals = mi.sparam_vals diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 5dd0267c52a81..7b69b8c248c1a 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -1011,7 +1011,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) tree.slotflags = fill(IR_FLAG_NULL, nargs) tree.ssavaluetypes = 1 tree.codelocs = Int32[1] - tree.linetable = LineInfoNode[LineInfoNode(method.module, method.name, method.file, method.line, Int32(0))] + tree.linetable = LineInfoNode[LineInfoNode(method.module, mi, method.file, method.line, Int32(0))] tree.ssaflags = UInt8[0] set_inlineable!(tree, true) tree.parent = mi diff --git a/base/stacktraces.jl b/base/stacktraces.jl index 1d0f6996ec42e..273d8236c4841 100644 --- a/base/stacktraces.jl +++ b/base/stacktraces.jl @@ -52,8 +52,9 @@ struct StackFrame # this type should be kept platform-agnostic so that profiles file::Symbol "the line number in the file containing the execution context" line::Int - "the MethodInstance or CodeInfo containing the execution context (if it could be found)" - linfo::Union{MethodInstance, CodeInfo, Nothing} + "the MethodInstance or CodeInfo containing the execution context (if it could be found), \ + or Module (for macro expansions)" + linfo::Union{MethodInstance, Method, Module, CodeInfo, Nothing} "true if the code is from C" from_c::Bool "true if the code is from an inlined frame" @@ -95,6 +96,86 @@ function hash(frame::StackFrame, h::UInt) return h end +get_inlinetable(::Any) = nothing +function get_inlinetable(mi::MethodInstance) + isdefined(mi, :def) && mi.def isa Method && isdefined(mi, :cache) && isdefined(mi.cache, :inferred) && + mi.cache.inferred !== nothing || return nothing + linetable = ccall(:jl_uncompress_ir, Any, (Any, Any, Any), mi.def, mi.cache, mi.cache.inferred).linetable + return filter!(x -> x.inlined_at > 0, linetable) +end + +get_method_instance_roots(::Any) = nothing +function get_method_instance_roots(mi::Union{Method, MethodInstance}) + m = mi isa MethodInstance ? mi.def : mi + m isa Method && isdefined(m, :roots) || return nothing + return filter(x -> x isa MethodInstance, m.roots) +end + +function lookup_inline_frame_info(func::Symbol, file::Symbol, linenum::Int, inlinetable::Vector{Core.LineInfoNode}) + #REPL frames and some base files lack this prefix while others have it; should fix? + filestripped = Symbol(lstrip(string(file), ('.', '\\', '/'))) + linfo = nothing + #= + Some matching entries contain the MethodInstance directly. + Other matching entries contain only a Method or Symbol (function name); such entries + are located after the entry with the MethodInstance, so backtracking is required. + If backtracking fails, the Method or Module is stored for return, but we continue + the search in case a MethodInstance is found later. + TODO: If a backtrack has failed, do we need to backtrack again later if another Method + or Symbol match is found? Or can a limit on the subsequent backtracks be placed? + =# + for (i, line) in enumerate(inlinetable) + Base.IRShow.method_name(line) == func && line.file ∈ (file, filestripped) && line.line == linenum || continue + if line.method isa MethodInstance + linfo = line.method + break + elseif line.method isa Method || line.method isa Symbol + linfo = line.method isa Method ? line.method : line.module + # backtrack to find the matching MethodInstance, if possible + for j in (i - 1):-1:1 + nextline = inlinetable[j] + nextline.inlined_at == line.inlined_at && Base.IRShow.method_name(line) == Base.IRShow.method_name(nextline) && line.file == nextline.file || break + if nextline.method isa MethodInstance + linfo = nextline.method + break + end + end + end + end + return linfo +end + +function lookup_inline_frame_info(func::Symbol, file::Symbol, miroots::Vector{Any}) + # REPL frames and some base files lack this prefix while others have it; should fix? + filestripped = Symbol(lstrip(string(file), ('.', '\\', '/'))) + matches = filter(miroots) do x + x.def isa Method || return false + m = x.def::Method + return m.name == func && m.file ∈ (file, filestripped) + end + if length(matches) > 1 + # ambiguous, check if method is same and return that instead + all_matched = true + for m in matches + all_matched = m.def.line == matches[1].def.line && + m.def.module == matches[1].def.module + all_matched || break + end + if all_matched + return matches[1].def + end + # all else fails, return module if they match, or give up + all_matched = true + for m in matches + all_matched = m.def.module == matches[1].def.module + all_matched || break + end + return all_matched ? matches[1].def.module : nothing + elseif length(matches) == 1 + return matches[1] + end + return nothing +end """ lookup(pointer::Ptr{Cvoid}) -> Vector{StackFrame} @@ -107,11 +188,26 @@ Base.@constprop :none function lookup(pointer::Ptr{Cvoid}) infos = ccall(:jl_lookup_code_address, Any, (Ptr{Cvoid}, Cint), pointer, false)::Core.SimpleVector pointer = convert(UInt64, pointer) isempty(infos) && return [StackFrame(empty_sym, empty_sym, -1, nothing, true, false, pointer)] # this is equal to UNKNOWN + parent_linfo = infos[end][4] + inlinetable = get_inlinetable(parent_linfo) + miroots = inlinetable === nothing ? get_method_instance_roots(parent_linfo) : nothing # fallback if linetable missing res = Vector{StackFrame}(undef, length(infos)) - for i in 1:length(infos) + for i in reverse(1:length(infos)) info = infos[i]::Core.SimpleVector @assert(length(info) == 6) - res[i] = StackFrame(info[1]::Symbol, info[2]::Symbol, info[3]::Int, info[4], info[5]::Bool, info[6]::Bool, pointer) + func = info[1]::Symbol + file = info[2]::Symbol + linenum = info[3]::Int + linfo = info[4] + if i < length(infos) + if inlinetable !== nothing + linfo = lookup_inline_frame_info(func, file, linenum, inlinetable) + elseif miroots !== nothing + linfo = lookup_inline_frame_info(func, file, miroots) + end + linfo = linfo === nothing ? parentmodule(res[i + 1]) : linfo # e.g. `macro expansion` + end + res[i] = StackFrame(func, file, linenum, linfo, info[5]::Bool, info[6]::Bool, pointer) end return res end @@ -219,10 +315,17 @@ function show_spec_linfo(io::IO, frame::StackFrame) else Base.print_within_stacktrace(io, Base.demangle_function_name(string(frame.func)), bold=true) end - elseif linfo isa MethodInstance - def = linfo.def - if isa(def, Method) - sig = linfo.specTypes + elseif linfo isa CodeInfo + print(io, "top-level scope") + elseif linfo isa Module + Base.print_within_stacktrace(io, Base.demangle_function_name(string(frame.func)), bold=true) + else + def, sig = if linfo isa MethodInstance + linfo.def, linfo.specTypes + else + linfo, linfo.sig + end + if def isa Method argnames = Base.method_argnames(def) argnames = replace(argnames, :var"#unused#" => :var"") if def.nkw > 0 @@ -247,8 +350,6 @@ function show_spec_linfo(io::IO, frame::StackFrame) else Base.show_mi(io, linfo, true) end - elseif linfo isa CodeInfo - print(io, "top-level scope") end end @@ -273,10 +374,18 @@ function Base.parentmodule(frame::StackFrame) linfo = frame.linfo if linfo isa MethodInstance def = linfo.def - return def isa Module ? def : parentmodule(def::Method) + if def isa Module + return def + else + return (def::Method).module + end + elseif linfo isa Method + return linfo.module + elseif linfo isa Module + return linfo else - # The module is not always available (common reasons include inlined - # frames and frames arising from the interpreter) + # The module is not always available (common reasons include + # frames arising from the interpreter) nothing end end diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index 87bd822a9e818..69c8248c7c7d0 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -503,7 +503,7 @@ static int lookup_pointer( std::size_t semi_pos = func_name.find(';'); if (semi_pos != std::string::npos) { func_name = func_name.substr(0, semi_pos); - frame->linfo = NULL; // TODO: if (new_frames[n_frames - 1].linfo) frame->linfo = lookup(func_name in linfo)? + frame->linfo = NULL; // Looked up on Julia side } } } diff --git a/src/julia.h b/src/julia.h index e1032f6ac3111..5a90037af3460 100644 --- a/src/julia.h +++ b/src/julia.h @@ -237,7 +237,7 @@ JL_DLLEXPORT extern jl_callptr_t jl_fptr_interpret_call_addr; typedef struct _jl_line_info_node_t { struct _jl_module_t *module; - jl_value_t *method; + jl_value_t *method; // may contain a jl_symbol, jl_method_t, or jl_method_instance_t jl_sym_t *file; int32_t line; int32_t inlined_at; @@ -406,7 +406,7 @@ typedef struct _jl_code_instance_t { // inference state cache jl_value_t *rettype; // return type for fptr jl_value_t *rettype_const; // inferred constant return value, or null - _Atomic(jl_value_t *) inferred; // inferred jl_code_info_t, or jl_nothing, or null + _Atomic(jl_value_t *) inferred; // inferred jl_code_info_t (may be compressed), or jl_nothing, or null //TODO: jl_array_t *edges; // stored information about edges from this object //TODO: uint8_t absolute_max; // whether true max world is unknown diff --git a/src/method.c b/src/method.c index 30d77f70ab37c..c20132832a3de 100644 --- a/src/method.c +++ b/src/method.c @@ -494,8 +494,9 @@ jl_code_info_t *jl_new_code_info_from_ir(jl_expr_t *ir) return src; } -void jl_add_function_name_to_lineinfo(jl_code_info_t *ci, jl_value_t *name) +void jl_add_function_to_lineinfo(jl_code_info_t *ci, jl_value_t *func) { + // func may contain jl_symbol (function name), jl_method_t, or jl_method_instance_t jl_array_t *li = (jl_array_t*)ci->linetable; size_t i, n = jl_array_len(li); jl_value_t *rt = NULL, *lno = NULL, *inl = NULL; @@ -508,10 +509,10 @@ void jl_add_function_name_to_lineinfo(jl_code_info_t *ci, jl_value_t *name) lno = jl_fieldref(ln, 3); inl = jl_fieldref(ln, 4); // respect a given linetable if available - jl_value_t *ln_name = jl_fieldref_noalloc(ln, 1); - if (jl_is_symbol(ln_name) && (jl_sym_t*)ln_name == jl_symbol("none") && jl_is_int32(inl) && jl_unbox_int32(inl) == 0) - ln_name = name; - rt = jl_new_struct(jl_lineinfonode_type, mod, ln_name, file, lno, inl); + jl_value_t *ln_func = jl_fieldref_noalloc(ln, 1); + if (jl_is_symbol(ln_func) && (jl_sym_t*)ln_func == jl_symbol("none") && jl_is_int32(inl) && jl_unbox_int32(inl) == 0) + ln_func = func; + rt = jl_new_struct(jl_lineinfonode_type, mod, ln_func, file, lno, inl); jl_array_ptr_set(li, i, rt); } JL_GC_POP(); @@ -604,7 +605,7 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo, siz jl_error("The function body AST defined by this @generated function is not pure. This likely means it contains a closure, a comprehension or a generator."); } } - jl_add_function_name_to_lineinfo(func, (jl_value_t*)def->name); + jl_add_function_to_lineinfo(func, (jl_value_t*)def->name); // If this generated function has an opaque closure, cache it for // correctness of method identity @@ -682,7 +683,7 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) m->called = called; m->constprop = src->constprop; m->purity.bits = src->purity.bits; - jl_add_function_name_to_lineinfo(src, (jl_value_t*)m->name); + jl_add_function_to_lineinfo(src, (jl_value_t*)m->name); jl_array_t *copy = NULL; jl_svec_t *sparam_vars = jl_outer_unionall_vars(m->sig); diff --git a/test/stacktraces.jl b/test/stacktraces.jl index fb873c1a5cfb7..96393b124f70e 100644 --- a/test/stacktraces.jl +++ b/test/stacktraces.jl @@ -91,8 +91,9 @@ trace = (try; f(3); catch; stacktrace(catch_backtrace()); end)[1:3] can_inline = Bool(Base.JLOptions().can_inline) for (frame, func, inlined) in zip(trace, [g,h,f], (can_inline, can_inline, false)) @test frame.func === typeof(func).name.mt.name - #@test get(frame.linfo).def === which(func, (Any,)).func - #@test get(frame.linfo).specTypes === Tuple{typeof(func), Int} + @test frame.linfo.def.module === which(func, (Any,)).module + @test frame.linfo.def === which(func, (Any,)) + @test frame.linfo.specTypes === Tuple{typeof(func), Int} # line @test frame.file === Symbol(@__FILE__) @test !frame.from_c From 04cb800df011a4c81d2157fae2bcdbcb35e4343d Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 26 Apr 2023 21:32:51 +0200 Subject: [PATCH 597/775] add some very basic documentaton about supported tracing profilers (#49520) --- doc/make.jl | 1 + doc/src/devdocs/external_profilers.md | 68 ++++++++++++++++++++++++++ doc/src/devdocs/tracy.png | Bin 0 -> 1016125 bytes 3 files changed, 69 insertions(+) create mode 100644 doc/src/devdocs/external_profilers.md create mode 100644 doc/src/devdocs/tracy.png diff --git a/doc/make.jl b/doc/make.jl index 04b8af595e58f..3c69f4e6c47b5 100644 --- a/doc/make.jl +++ b/doc/make.jl @@ -159,6 +159,7 @@ DevDocs = [ "devdocs/backtraces.md", "devdocs/debuggingtips.md", "devdocs/valgrind.md", + "devdocs/external_profilers.md", "devdocs/sanitizers.md", "devdocs/probes.md", ], diff --git a/doc/src/devdocs/external_profilers.md b/doc/src/devdocs/external_profilers.md new file mode 100644 index 0000000000000..bfc0d6538f111 --- /dev/null +++ b/doc/src/devdocs/external_profilers.md @@ -0,0 +1,68 @@ +# External Profiler Support + +Julia provides explicit support for some external tracing profilers, enabling you to obtain a high-level overview of the runtime's execution behavior. + +The currently supported profilers are: +- [Tracy](https://github.com/wolfpld/tracy) +- [ITTAPI (VTune)](https://github.com/intel/ittapi) + +### Adding New Zones + +To add new zones, use the `JL_TIMING` macro. You can find numerous examples throughout the codebase by searching for `JL_TIMING`. To add a new type of zone +you add it to `JL_TIMING_OWNERS` (and possibly `JL_TIMING_EVENTS`). + +### Dynamically Enabling and Disabling Zones + +The `JULIA_TIMING_SUBSYSTEMS` environment variable allows you to enable or disable zones for a specific Julia run. For instance, setting the variable to `+GC,-INFERENCE` will enable the `GC` zones and disable the `INFERENCE` +zones. + +## Tracy Profiler + +[Tracy](https://github.com/wolfpld/tracy) is a flexible profiler that can be optionally integrated with Julia. + +A typical Tracy session might look like this: + +![Typical Tracy usage](tracy.png) + +### Building Julia with Tracy + +To enable Tracy integration, build Julia with the extra option `WITH_TRACY=1` in the `Make.user` file. + +### Installing the Tracy Profile Viewer + +The easiest way to obtain the profile viewer is by adding the `TracyProfiler_jll` package and launching the profiler with: + +```julia +run(TracyProfiler_jll.tracy()) +``` + +!!! note + On macOS, you may want to set the `TRACY_DPI_SCALE` environment variable to `1.0` if the UI elements in the profiler appear excessively large. + +To run a "headless" instance that saves the trace to disk, use `TracyProfiler_jll.capture() -o mytracefile.tracy` instead. + +For information on using the Tracy UI, refer to the Tracy manual. + +### Profiling Julia with Tracy + +A typical workflow for profiling Julia with Tracy involves starting Julia using: + +```julia +JULIA_WAIT_FOR_TRACY=1 ./julia -e '...' +``` + +The environment variable ensures that Julia waits until it has successfully connected to the Tracy profiler before continuing execution. Afterward, use the Tracy profiler UI, click `Connect`, and Julia execution should resume and profiling should start. + +### Adding metadata to zones + +The various `jl_timing_show_*` and `jl_timing_printf` functions can be used to attach a string (or strings) to a zone. For example, the trace zone for inference shows the method instance that is being inferred. + +The `TracyCZoneColor` function can be used to set the color of a certain zone. Search through the codebase to see how it is used. + +### Hosting Tracy Traces Online + +*This section is yet to be written.* + +## ITTAPI Profiler + +*This section is yet to be written.* diff --git a/doc/src/devdocs/tracy.png b/doc/src/devdocs/tracy.png new file mode 100644 index 0000000000000000000000000000000000000000..a0371be9db63e9053a57522b4efcd15a1dc68b29 GIT binary patch literal 1016125 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­3E_vn6gnyW=K+-L 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�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 z zo1O1_PE!zXD5T1LyGWOpUj8-pow{pxw>_d-N;ip}BYMfHwlnNje53hQMKO?Q#g%(2 z8z!jCh^EJ6YgP%}%$c1r*e=|Z2f5Nm!|9(rO!{uZZMSq=4jgKFpwj1Mlv zns?#{lV!F>jLpdtL?i>G+ve+Bk&%BT+ii_-X5(MF(7nbWyOPo)kLP!_NesS!U12?& zibl;-Z{JA<5xerC^mWR@jsczB7pxLN6R*B=mUbRrIkpz%5{b&2-ah-)U_#`Hw|P1#%CFa+M0$I7&6!24~C7 ziHAb+R>_1sq?~uB_^gO{96sFxqCv|{>|UTNIHIXqxHzt-DHd3~Y$e0*S|V}GW7gHY z6z#flUWeZaG$Ny6Z+yyF=X;at;c>CjNpX&|Tt`v$I%Jb&B0$pilc3j)=|l*SRGU{= zjI)8M^i{+zIHLnLIHtGK2kCy`RPn4q$_1!AZEq_f!bhLt2}?9o$An|)G57Yd=&6;n zV$f590jiLXNy7D3m@leB3GdaFrrBw+A}ZaFb7<}6>5li1d$_$C>W`PlPsn6?yH+{l zhd-Mf+6`=7<1jI$N+c4h3HqMmW&Na{AZk_9-tk&sYP7%oX*n5Yak`C!3H|dhAJ$v`KCEXC)aqMLmACG2@wE9WfV%oWBzP^LqudSC z!#X{tCGuFpPx`TL#qZ{Mulnjlb;NYjD_Z@F%FOVfA`^(_*rkejT-m;6WC@B^1?eC$ zVKqJG4S&3V*>GkIG2hAkR2!q*63`Vs_jYMy4rE-Q5zwqULt6cu#&7 zKqMB={s~-_9$Lz@bWC?eru|GCZ@Rlh9bNIfiU?&(DNP|@r@tKXnJ8PNR5;!#qaVqg zvu`YYo55zS!Lihjfm2yb%5|Kc(ad1Y91n661Z_VNFzZBd8Hcm~0{){zTRX@5GZmrz z$d=yFGAvU%cjK;z$iAvms(#xo$WK7haF%;GCjR6n=^X!~&&ezkY_E)zh-5;I&+K3OonfWrM^H0od!z6K4t{s<+9 zAQDR6@s+u^w6^fGY}J3>d-&O<jANiTKfebyu>QhD+ z3eOWEbJW`}eNc^X{(gss1=pR4q)tT@V(X_bXmOy)jnAJ2kOwbm@P}yXrB+FX@` z+Ad##FJRwc6#-wc!YPY?fc;bIQ`w_OC&^tr!*U_766h0cMjsoba=xD&yv;ou)Gff; zU3i5Qf>dHZ@pS&u0yk09Kxb!S4|H0mPu5DOTS{O3{VwXYgM(In*?s;W3!%`wje3kmNW#ya5 z2Y`pX)AcyQUu?bE+vrb17_{@1w@8+nEEVC(*Rt<$lu#hv)XR-J%@L&1I(e{>Bh9y1TneLZnl=yF6awg1jEebhXD}8fwJDD!4z*XGtsZjYbJ4gHcP~<|RBV z0!o7_z{>|@v58sInK7O%CS}eLjQfS73ArgWANR}5@&7Iib{=&DddJUa+ocBg7V1f& zPN(74aOoA5oA0mZ!-xbjSUqnq%d=JwAH&B)kJ-83pi)=JOI8~Uy&2E5r$XZk2MoM1 z4Uw_395YZyX3bUYbiQl3J!WTu2%BJMBZ zN%M1W5s!uiS{?!&(w~pEO-N#^D9(N0UT^a=zjf`@smgeKtQ-7Q`c_HKrQ@jx)zG)-EHiiLaRy3P1BK(Hi^&W-Qm}~eAL4mu6P!||ydRxeRz_=}I(>W`GyosKPuq2PPICCrwzNy*DFivPy>1av9*us7 z@kx!feNh^*PD^}@WV;Wj?h;fp$mM9O7JB>tJ( zvY?Z_$zQERWu-OmJ=M4_J~)uV&uk2L(A^h7LS*)o?_>S1RG?PvJ48-rrcd@Qe7D-} z?K-}tNgG@S*P@BUpzQ~(@TmE7!#Gt&gx2zcG^yXCSGFJIb1!5EDzDF!JYMoTcz8%* z1IHu1_Q!KWKJX_DH&tt0GI<~@u~=`Okq{5jy0|R5)`{t!10t+<>vgZ*N?Ah#A5+yWQ)(aG=f$sXAGw9hC~7& zhNc{?yY8&9GFW45 zf!?{z!4Yfi0%i3nFfpd(i@-lhP(gn!B;;P5WErB=p6JBmdCzU~aNlg^jiRS^()R6q zpMPRC)~8>f#OJm<`~yn{8iI4ny-S5sbW7UJpG&m$=&dlt)>X+?WRS<+D{*vZFmwuL zaOGI@KtuSv$QK{rAFM`4Xgb;RsX0nNXy8|=h+O53C?>7PQTxtz=)h|9?qOz@z zhYhx%W>2i_|LolKoB~ecB+(TlZA{eZeH~1s9HY80`J(>7uF7Dl#a(8}jY9%0K9z)X zSB7gu$D?F3Np5!6D$VVfx9^~rHUJP7rE-si3~liYAf(5Yx*ZO&pm}B6XHx|Py*OOm ztCyVuGHRVl=u%&y0cSa$l?^!R%4+)r&$Fx;YKf!2LI6awjykO zpvKSf(s=tzi8drP0zs6L!^deh>^y{kOL%((*c#M==a*OeNXss|E@j#;p*i@yRLR+1 zxONp?@I%e7ESj3~{v>XpINROHqpY2+FSuPBgl0jcd{t?^`<1Zw*OcRYc9e@0B)Ke6 zlW{~vI_E|2i(Ni)ugWr+%KqM~Cb^{8!g-+_U-i^}Lw|^^w55$kC znZ-A0OC)YvKW`UM;0Um_1!Go8LcI+t^Rtn{5y6{Y*8Y{Yi+nt-%g=yg{U}_)L?d7( zD-vj8=f5vb^z+-9E~YcybNzfnfxgfkMu3CaT^xv|OGVfzkP-*xj(I>0oRiRYa<$qE zD04;C?3!O&i?@Q|T)O>NS@8yKF^#qN0lhD@TQH4pO{HjiMxv%$q9{gIVU)`73n9ro zpZa+QSmaZ+ZcJ*IzgX?;c03+cM4{yQI90%@a<*G7+M+aQ53S})mI9g}=8UN+AI_8x9r9aFh7q3Hn>vpEU|Is`S82dA=g1jq z-Rt>&iIW>08tohP%}swit(@vU*AA1@J&-wE*25hq!ASS#E9TNJ|2zBLZ9r?2Mv%j- zI-kclv4eH^8+U|^X{|0;{f4L5ByvH{^bOZ*v?rp2p-Ba@WAGrTLP}cd`kT;C)(ZHP z%yj+Y;~<5@j@@U_vhzrG+c~@Qj*)<>#c2uAWgi&kjWYZdJZq_=N`5%CUbC2gk4#^QN^lh zO00mFE*1;)JM5g%R-k5A>)8(>-8e+3wBLIlqh6_EX0bQS@5+KSWYIB_-^9mm(XsP8 zZ=Lkj^;qrdIeLs1H=@{dQe@P02G$O$v)$A6+7|M8l=tvYu_?zre0I1R9iFPsT^S?1 z?`#rue{y^QEMz|E=NLERl3n@}7#Yd6qkWmbnrxRNq7t3 zfpgl5)1VZ6CV|}WOch*u3{sIh-KIAj5{^x0qM6Tu0>7~vPzUQ5%S>EZiH8w;M6pHi zJyJ&GJ3OpKkDI}}cq~1OXVJJ@4S0RGxnwgn=B||MDBD;1ch{U!MDx0@Sy@7FhPaUu z_km}c=z2?<=-PK#JpeNFCGR7i4YDgdd389N@$GuW7eG!&KyNccGW`0*&-xi6x^_v9 zVCP<Fr#Ao!90W z96tfy$GT-TccowBoqVGyj^}H>kHA_0SiPVj7987}9t!jT_8y#@EaCV(tt`7!Ds=24ng& zbi}{17qUE)6H#Ntnvw4m@B?C*;Wsp*gOA@}@H8Xgcn4t3f6tqAX<0l-=Wvvd$!k5{`@VkKe|89@iHAm=%ozwi=WKJitj~qLO>~_%u_i#oR{x2m4#ZozqtCb9 z9wvYk261kQ;P*O^FScRhb6uC`>t-WSXt&(%HXh>Ug9a#uzH!~`jnO=pX)&GXAaVh1 zJqBDfC#(hD9oTJCNPLs%HbaZ%&q5HlHV2QGJET+(SBF_jQYJ}F2V#>5=J((W2 zYr2C;S92xF@`nT)UK~+Eo+6Yr#EQ8J518n{Q!XF|2i>I3N+z>zJtEUiDhH(&qtaN| zmuDsp%nWioctrG0evWnQj1Fg)Subv}7oh`Orjq~~b={Elq^A%ypousIE*~T^4R%3C z*YNs{FoNYg0q5grhw9+0sQXfpcOu_?Y2U{eby4tt6T9Z2z5sHk`M|@w?=ncv5D2M$ z?s?;WlC17F`r`)bh|vC zFOAu1oe>*9UC-znbqEw9#|q@A023HfxIH6A1LwDF-GUCo$veBfQAE@-jfYB+UP6{( zHUVf4esYQ${-M*{*|G`8`s0-ZhRu^9IBHT&+nZVRE{Hi4=K^jrXbKm zA%>K<_Y^$yxNB;v^_Xv3cG_YC&~#YPgKOFQL$)QTY7{#73xS#}5iH(sUq z&wAk#!=U@|zy4LfPg=X z?>VLeHm1&|y25d6b#jWW>|T@5=MygS`pHI;)T5s^tU@tX#W(t6)3{oZ^Vkg)@?vr$ z0+4ra)Q5?$l&2Z$rQGi^C^`L8j~%G$B%9wCNQKVrP7l`BBavFit6`i(kUgkZo6Y{< z@&sy=C>oJdTc+qRNuQyxSb~n4tXe8%w74YYfXcha2&lB}Qet0uJ#O(i?S4MY_IvK; z8zx9y0J6=Cc+HMw;z4uH`chUdrd zIBFX-JU+7IyG{;?=RluaBxyC&DdsAaIUs*K)sR5%(!ZSWirg{`%VD? zvzd}tt3qMxUg{vbpz%|lO1S6-N!FfnZ<EzIx{Fr$z~ zgwdsNp%?26A=Pw=ri4N1FHVEZxAGeV{1DPL$a=MUCD(##iCVcI_weYlmziRStjiRI zQtc18?*@ciQ(>T?Z0!`tf|*cf89@Rd0n@cbQHICiGxPTg{o?IlR#XTyu+Vi~*aF*x zgyIu|tvF4mRbpSNDyf!gWrzPN7cr(4#rym43+ViYOtkP_e)t4;a!B97kd&!GfyybD`K z5g_At5Ra7nX0=)23Vf~HN%eqRTR6u%H#v<^tnShu;-Kb-uf7SASFZk73xGj@lm+W# z;p>dTP36GLelxrBJhE1&OZwai*MRa55BI)TEB%`TtR zBjZxqM(^jp%7GleSVH4>1j_6(N{d^+MF-ibC@*}x9N`usuEL^$3wCY*3Q^l~(i0Su zX!;wm?jDL6wa4v_kM+2DjQA{OO1`;V?|3RcM8))Alk?$k2L^%Gg-h@e3<}tRj71Bh zC;tW<%j_Z#u)bckBR^zXE=UO~gQwGk#|j4}jK2ASXY$VsA8Nh?1uswV^+*p>9WOOHYM+hEl>WYGR!Flrz0)R+ z_7oyjDVQWj(Wnj{wVjQ&dy>!<8m9~lg4Y^G^=u{?W!u2sX!73PcG~B0C#yH)$bDBy zu;DE{M!o{_@hp5nE8)(PKtJE9>6P^w{&h3v^VZlcovPW!gNC?)@D602PV|@C=gnvH zTX)@+FG|r*iryY=Z`UlvF>A}r{W2VFSpBHcx`2)U@U0cF`mmyoQs0Bj^9~pae0z+A zQhz8O0W{i0_mX_4I)m3|v443#zPZv2?6WDlfm(cVmKY<4w}-VLD$4`a!c* zw$T`*O1?3^Z)Vy3f)T67^VXW#5N)$Z<9PZ0{Q0ubQRg?XJ_)CoIo!Hfda!*&>}M3& zUJm>b%7&3n>VLjo34=n-JikZfgPaMG{wdz=f@u*EVST*F$^Yv&;SkAxr#+GACz+u( zMM2K;^=3dDJ&N=k4t{UGR>;`?hC9g$*+r+WZA}PwQQQ4YNF*sHfZWZP1H<)TL4D4S z$-KVT?>iQ)tO{6T+I9kW(}~^MJyi?W!vq|EW$V?_M=p|)i1@%uP&5!SUZZY=2Td0z zl|O-D_z3(CJ`a}Jt;1U8*PbBQw9+26;^GF~Y zz8>tT1a((F_mWfGP|eCxAGjBgD4dxPsTpA{aPK%Vp6VA_ueUs7xSZcy2o}JKh(}0J zDV(MrBCl2x=+(Mx(UXNa##`7LA~JeRp)8Z2)R0(Yv)b`dJe_f)M(z z2r?l9H{vg)=?I=nkJ3hj6fWg2y9Q+kJ2Hg^!*%uGI)O&I1fP}s<{kTon$-R!C21SYT92X)^N={ysw(zU2<+kRS}IhpoXOAaq|hoMq~yBk)SEdn4Uh#O6N7$mec%YT$>WSA9`pQ4lBP zq)3~Ozp=Y`KCsAoBD=K9!!m_hTxRv&z{0-H=(gc6l2z1C+bQr;`Sz;5_yRC;PedRo z{a_WsX*jH-Yfe|58=wW%mdoMwgwxnSd6}KW??z>?-r7l>Kl1pX-q4HDdUoJ~;-e6m zv=+eaUYq8WB5alN@C0%`R=XZW4NOk+yeISKtvav0nYwH8Kova(>EiM?jy@ol3hjJ2 zCr>)&!aQj@UGKib1RZFW`~9xm^RzLV#Qzbv|Aa9;Ae zaG6+D(O`-!P4dd6GnsmsKbwf26)jjNrb^WRMHVBq5SB)nG*Ua}agF1l{G6F!uk%r8 zLP9)(E|_6c7C%9vH5ZYBW5pQGZz6s!TX%bfj9y+utQQup%=2Y)p`P93;vhwKP)}h3 zDlI-$Frwy_1<$@m{yRm$neus78sB6m6Jp4hcmx8AJ!IY01xO;g#u%*&E7XCe;M;<% z&duFLVN(J!yBoiQ8A|+p_;h0og_tC5F{BdT{t1M1DGi0_eT?K57EGnB-nVox{q{3J zG&1je;*rRa@4NsHyERvhq`|JchH|(L3J(HI=KUpw8=2kfqYwn)BPIDxTbGgaHY^Ry zi(j)M>m6{Fu0e)&^7~4G_Ea)~_OA!`4z3>sqMvY79KS^DB!%JD6Vzh@d89w*+3L)< z*Lc;CF1B^OfxQr%m-+9$(Xx2M_<4q*aGB3ZA&};b;@dbx8n}J)a$LTP+GojwOW}_cnwF3$aW|69L8}e-+xRHt&wUbbUh}9y=-pq%}frLvJ?qkua z%a@v_U3*x(%yr0}s-h0kmVbzl5aCo(D3h5_@q;`O7I12Fi2@ z9Gg1}I?beCnZU}ZjBP>%F`lW*s6mLJYj@brq@@B1?`6jN&6VJfvy-ST_$xAwpIU-V zVb+ejllk$oL0A2~QQAOKTnz1W$42eW1OmF@G$qgEBo05WPd>be5j@WX-LI9$-@o!k zZR0>1kKfKF<35N(WF0BM6}a4ydye*wI`97{j;(#YQn3i=5`*8>IniyY6izAQm@W!0 zCT<4w->9v*-2JlL?WvcU%y8#-Pg)NDbT3}>&p#r(0j;1v3(!PCFLilDCz@2=zL@R* zT=J5_-EM?PSMabvvz-=G1D;RE`YQu`80OoLlwOY|!E8={$a~=HU3!gqrC`&JVzM5K zvn2^d$iO4TLuu9G#CWK`Pjvk$w_Y^HpMHqaPYtv~&m}XF|Ib3gi#(DNW7L*%U7Vfu zDdu5kQ^hCK0>z=d!ugO5*=IacmNyCon#+tT2xtdcFCaN_REmO~51UQ`Tuz2&ApiGI!uH7| z&j)89-%KU6@WXgc2z{34$NC)k(&a)_%O%O2AJd&2j2KM+6jhOSh`pJf<3w+ z-Efp|*^cuQ$KaHBVFuftMzU!XY z>9@S4T*)QrW+qgu$P)do!2iXAG2i56%Y@uu&2w{}mU%_$?2`#pDrto^S|->(?Jcb& z>RL*?YPzs>ERCtWN|^~wZ1%^t_^1=%e*VV&GQK8{7(Ul6>XhWAB>X8l7Wz?=QnBfISdY1kQ&VY`4@W5$kQxc&j-$n|l=LZS4*1e8 znA}JI27nF5VW)sjUZ3;TK5KQSnpn+?Hy@5(P#V({7edRy%up_ zJ_(vHj2|3Ss*I4%<}Kr-KOE*@$6j;}nt{x(oc(4hz7z;XNw)9Ric4!P&5L{U&eBjq zBZ9KCDXU{iwI87RsvA5ZUx1}6?Pj)&V-lbBnjd(2yf`Le)>7F7n*snUKh zl=XZVB~v_jQ`VW3`*TFrP+`6uwo12DONiYvEuADMODF?v(gdV79ept3GnFCJhD=ausjK~Wv?3kps*SoKFOw@y% zr30Hwznsy2Debk!%oh*FEC3d#P4B8qXPCgME$UQ;7#&jz2>>5W51T3*VWWVPRX{fBcP>O^Zg(hrszoP!Q z()0!(S)c-H8|mVG9Q)l#B>R=`B2W!^KKQFfRHjx|Xr{6253#^wHyqZP8V1{0t4hV> zU$k0oiBim7WrETq-Q(^|6@IE47?V&<3`$6SlQx*XzZ_uBl_ELC1?X_|DuZVuN z!=GaaP$`_jBv9rn1LI-0fn&Uv?>|lzfA}2y7K<)ooyB8aomS`fBpd@^)X7PDbyvY7 zx6s8jww5V3>BA$_8ja4^S%0ITsh4GdTc{GtZTjh8eRoX;?YE%{7N@QOLtJ>wS|2lB zwTrlt1|htB&!B{WMyexfP-8Z$40wKOp_FTYs7@7`T$S_)+uCzAhorkfJLa6|VrWnW zMsF538>O)T7D*hj&Ny(@EBero$1eIj;hzdFiVAS)pY%ru{7uvX6yO*gDJLo7{>eap zZ?+f99nA8mSi^0+L;5bEUcOH z{hN1yF*jKO&`rFza!Fk4hG4z^Q@GoIy^ki4n+t&_XFgCVW1}A+S7eI8hvD8Ir4Mb; z^!jmnHlu`7wN3!`-57iQtOd`3!dUiPwQ1ZDYjrrB4ZQ%JT%z!nmFN2QxP^~S$Q}{* zKFjo7V%2h;1*8NSZvc8C>^T=sVHT@8tjPb*6G8gHSx!cF)bSN+OW!#6aLJFx(1rniOZ_SQ3g0o_ORU8wIu)+A|(&{+VK;4lf07Wqh z=>rtEI59$h87Cd&mE`dA%d15u0PK{s+&NfCS!q5v=}<52c&S(DtAGLG#{6uhAw0*6 zT_^Hu>BcdmuIC;!KInRX)2xA-Gp^=+ieT0hE>*tc`}}>PztCsdE?oQ;Z+%$?|^qa zMa8NGTdQjqy|go_-k;;n@YA8(^}+)1*Fz##J`H?T1(w6>4QU+4ZoRfupG!0wD<2l> zZJ~4jD~&lhL}lx}|D5mIp#h~f2=sir7K9}6m4R1)N`B=LAVz1Lwga6vCgAg8LbM?1 z4F8sNomkK>tWZt=3FF_Y4f=%%!53Cvr)=>NwviNGn&0@yUp|mD>H4co4mJAk7nc05 zFAU^d^1$*9eQA!_-G3gIoEv&mIqbW(uVGRZBWBG8;knik36&f~tbZO+s^w3qR!r2F z@ZZU9OcdM=W1@;1h_Jzd;8+I-+S|O5-hIV8C3$)R`6t_5?#ksMc6F-QaVM34fy8^D zVy6hS22_AZ=5obtR|t+OfKZjl8^Xzi0y-|k4HLZDl(5R6Gy|WhCg{hV9yU`WS~@wayj)hOmFnV3 z{*&0tzyS-3KwcD*Rt39SjH(HT#6F8gS+1tGxiJMwAe?2wJ zp6i#4hoU3#&pptG53Sp}W2w~to}qtQ-#6fXWPT$Yf-gdj0WC){+TooXkERWm_QJT|sH*8LH= zosD3p0dZ1&AZ4fg#mBlblOJWQuKZxX>NzMt3iNu0UjR8-Q2wDjO}y-+VS)uQpcG-( zST>P`F9*Rorrht&msDJ`{Wtf2nX!OGt*b%Xo$|c$%P%`QB-ixyZbEnIxQjgh3_H2u zO^uJ_2Pd+cD5s7Ki9bygd>GfNeL^X^EL5^kAaCB9d0u)W-Rq12p&$EGO-UUR@ zL8bsZeGi6J?})gH(ApodY1CTu%#pX;ShPUJHCO7FonT;a@e;kxfUsU3udRWqDW*AoUs2!W4wV|DqTolaX7VT3~{jOUz zB;6I@s3aZF$uZav??D@5+-h7;w#6DTA)2_uT(Zebyw`tfqo2~Xao7L>f#L@3ILLVd zDwd%H^a|gQZI(v%Je3f*$=>lh6pbwbobFFwjBW^h7s)Dl*suTlwm^p77BC@BNU(jx z(A&ZpzU#TG;vp+6S3(fyKexr3|J)YbnxWYA1wc76y+r##tXQ*A5=>w}K<9{68$iQ( zms7{wmQFrd8SED#wf9&ykyGRl$$j_X4zdF;Z?h>&&NZn(tvvIEX2Y{6ILlQvNr)DZ zq?#NNB2xlPhb-WgH8=tY(eG87oRCyN@erX$i)%xEg=Y)50BS>mv3<(9PFiE`)wiEZ z4K3z%8KAZJ(NxauSK4XwcXMUSiS_ks_2z*NdA%1Cs0mpYGyfn(HaLHgBAcPm{4j|U znkUJ-Yxl|~^NvCVz6g#5p?D)SZxW+}{?<}!l%SRh4z11l?@5-hA~`rTS;Cs**igV= zX}tdozFt|D?;e=D}b0N zmDN+Ss-dvw81ksrswcX4(;Moqn!k;v3H40FR zJV0(a;%wJ$c9F;iK6bLvv|n_WTgav=KFL(8UCD{VHC2fUaiuG9j z>}Hn3q^GH4cl5KXam$HPW)^+$zu*cJY`NMg;7K8$LMY{| z5d4y;#s+(7D!{T`c_5A%jAP9|7yME~Y&@G*E_f5WTVV8#FD#N3>w^MV!YKl^VLhMQ zB|6;>tt`Vau4zJf!OO*#qNP5yYy#7wXi*QIqITV6Z0A^xKOxORg&xY9#*0#q2M6c< zlM+O)-H|N)c-E5A8@a22+&>@Up5EUwo_hG$8L+qb%Or zn2t&U^wG`d3xJXfBs6c?={+nbj45zM6)%#>J>Goz-A{l44oq3)fPH}ixIcYvPv9|5 zj<<$nLN&Xj2ee7S?A`_UZm%?M5{NqENI73(#c?1ynFqeFpZt47-y8Mhrh!H$6x!(g zDeeAgbWDd9KCU*`h_Uk>-*HeyyB+-DFUp23bn&y6N;Y>F*oEG{P}%CF*wW;WliW{! zh~X=9brkPx3dYK%H>x{b14@W_sZMM3K&si@zg%X%X2|^8c3(_f?-NsHuwO70$}E_B zf6@Ax_m}p=b(sqwX!x!HnF1C2^IyD_w7=d0&8O1t2$Ev?w0oN8&n$}80^le==g95l zgR)oc)$vB4vt^?LiEwgI;+oj`Z?7%E z^2gzkZRyosc_C{WpOdA-M5U^2D4oA%%)8II4&KrPkg z13&fZLLePD!WRLCq3k|;I2aKmUq=O%0JYY)>;|&gHO(~7Q(4hO&}6v{7>d?bBUeEC zVzf`WYPIrnO#4YSS?l2@&?%OykDC7yAxs^5Z#h+Japo-(N2~U%WPCvCic!W z-364;v15MSp0K8MW-=6HlJ zZ%cfi++73Aje6M9WV&eJj^;XjwlX*HOO0~AO_3~}>9pV7HNXa3nMNGakbc>kES`_tUjJ#zs;XWN2{sq{@OKrUTCeAQwSHfFde)dD*tHA9KGA=_ z?D>E)vgZri`Llc9wJ_#lZi~u&nN^aE=Qk-75>v?(r`NcUlH(y$g35aX76(Rg-2Sxs z$!(~8L55h`+8OVpD5|ywNs|0cihynHk}OAF?~Ne{%MF`zWHbNK*tK3=mph_Ce$&LUt5qm{?C-) z?={gc@|V-l+nffkweeE5g(B*GRS^k^Qw`ET;^U{qY9IGIecIczmFL zZC_05*7Z*y!>7@7i7?RMd_KE96-j|skmf0vvpZ8tpq9rT9b2L)!}1X7|LR-EllJBd zRB}dvwz=Ri>+sc~83Ug$SM^CZz^gV7OwoeSa{ZAlB}~`@K;zW_iZ&)!_uC@`sCW>E zU`_LRQcL&qdaRRlO&qupCe4O|dCL*;q}11wMe6K9)I6VYL*wAA#x}hX5R-v)_x4b# z@S_O{9*7nIC&2oiHd|CDHlr1?4-sOXq~W0N*s`0sg=4pqgE>hcF`4JBuK&u1WwkrA1zkDpIlMN%E-o@e3lPq)(2ojK1};}@tse9T(W;mgj#5Y|IMu2%`zIxK{_Sw83EgPTf)~OeUa@h6xIyJ!EtLJJ70d^O zuTjxcSOOm|*L5d{EeCB`gvL&+p1;~zYQ`c{vr(!UOkghrM8_CZXM0q*R=XX=(Y8Wx z3l@6Zxs3kUP$UO!0}KpzpTttrRq$JTx-*Fxqy0|;B#>vcPq;0Y0yyyT(9&TBZ1VJY zh`)YsOMa=^gvHo=TJPPt(=H>hfwjCv%xjas-s8FDw)TCYwDtbYClW02cuB&afn%+D z$-&Mj?FpKz?GivhdH;vv5)faES2jn*+IRgkrX=gTNd@UlOZ5>28tUHjGU|9%J;|0H zn55Qw`=CVjo||L}uw@9;)P<1wHuhrUXX;Nu&yp_Lowp)RMwjCxDuf1yI!Z&ibg0|BYe(!6LlB z{5x@sa;4akz}qx39t80VIPY59R^Poub`uQmB?y~`)lEKhP(J4>=nhNB1JdP#SptzK zgQ%gRFI6qb+o$$-$sR5nuXhVqy~z`c^r<6sqpF3yeTq^JqF$43!;3|pfWXOM`dEjK ziKpM%9gfv&;T;%&%y6$~Jy%+&UWuFXn{4Fjdq9^Tl3p+mXM%kC;U`#Fj;fi`qK_3PY>gnt9r z_8^S9pUWTn_Bwj2z|zjZ+bI_ zko24HL$OBeY(e@5GtgCi_iISzwLPNHDEiRmOtA$PZncNf9v+uQ;efUO>WRc)XAf4} z4-r!BT_8MbTkrNU&NgBmR?emaNeLkdnngo|(gl44>;tgWN(%oB(u=5}Z9RT)Y9}I? zS$<_qGI|R@U+NN6X|4Zm0!7H-DboMJ81=zU_?QH*MyEjG$3@pebRd280G2fCZD@*o z%Ctd?XMFT()ed;=42jBm?)qmz*;bj_noGUo?M1zBt_eJV%}c&y82*r!g1}K8Ave=1 zA*sAIucF^W84S1QJ>~e%sI+RiMR!MS%G#oH8XN;pAdkn&wthpOa8S@j`yK&hSK)Xc zFN8{N8ClY$ZTih#8Xj-U_bc{j+Hqct6EF3NoS_8S>mPb(6G*?UzxF}J8Azuw1#irF zEOopl>n)c*e?2M|xPz=>Lp>&{jLX8fi-oaoe|Nye^lS;#xib$PZ3up%T5%X#{@8Ce zYZJ+vyR<*t)ZpaUIJYnQA(UTbL*ka)dUQG`MMjkptjK3YR zo~%X}BF~^gVz;y0=wdRPqE>!#x3028oVj92JFnANB(ROH%*FTc0o?Tc<>O#{FytJu zY_h5oSvNdfa%45$ygA&CuJ3r|y#grJy5_PhQHV;xk_bcTEOF1qmv66ZrA$+AShL{GVc#q?*gtw^i6Jr-(8-_e{@(JyRvC!R3H_>69R`Yd*WECidO)T%vh?u3Mh?j-2 zNIBM4(=(hIK8tC&UoQ-a8%9nFzh`gwJ2o$$`9i<0=9+p`zgWF2IHS-6(BVRm4OpB} zsrh(_Gxrv6EKHc&vUWhDI>!Bt%+wDoJU%l;uX-s4m~fz(NWlk%Pme}~%KqB*(Z%sn zcjnN1^WpP>gGGd^@5^ADbQl3GeDiTm68GVneeBHvQC7e)n#JV0y}^_D9?U9DcJdU7`#EUn%@KFwzN|=srLqjVzpYvQ;O6 z6JhzZ=={?%rxkc5Jv;8BvUp9P45yJ!Dv_H>#x9BW5t)_wQJ0+k>Zh?~#6gtIJ`_gm z>w4NeVWO_Dj6`|b1^MaKlvD|v4Z!8cGQj*t*;h|{;uGeoBp^T>#aWo%+QUt%L7}^Z zofuh^ERUC@KPm|{ml)w=lmJq?9&;avMn=7lU+6S}LOTUKWV#bWK_k;nC@h~UFr4_H zb38YU!}L)cj0&S#NkF?r!HB*cBvAgDD{KZ{7H6)@-Zue<6-ZMW=pQsO#I9nR8!0?9 za+=yq0YV2@i=ePB0ajkcZ&SgA0y%;7hN2=20-a}!FU?iPmg{l@0Mef@_+$VNK`is# zngXeug>?gLml#$?f_bo^t&RxneUrE3b2A&llsLXlT94TZ9OWp=u1WLSZ1#}VJzm=! z_7-{Fboc(BP1AAR{YXkFPq+-nFc`=i38KIE!|zTLstDv60IhuccD+O&aNYziYp`G7zGwuUXKO@ zTJ|k9H%c`Q_>fDFUOu)B8(j&x5W`}?c>X67rFY74)5v_)HCF`l$@Hr}scBL_a`LCo z2u~lypp51w+`RM?d6g>@rv*f3n!((Tzo`=m_MO=GLm=1W4ZN!>&-n)(0ToYV>!s&5 zIe5u>m0#<;&m9D~8}pS6$3!X;cYzDhyVB=XN`IK% z|L$l->p^0fykoBST>`>?$?i4&KSa0*XxAEQfM+0Fb{C-4x<;b4Q%s$5Y_7+ z5SJt*?ae{m`-;6-IHL(vD+cw)bf}8E7?65{yCkaqEX5wU_x#WYYyaY03g52J_lM_> z2RE0?bopC}R#62K*RdHjWT6@mH-Y7PuM2@wSj85iW7!K=5a?8;YvwipAc&bQNQ*XP%RD-_ zPdJ`=$uzVnhQ?0kuuOoG{M-G167avuqN78!*zytk{!WMglJSK@#QtUB`~6|z+fZyl zcbR(OPn>KUMSK%?f3Nzs_u05?zwlD%M8S-y7s?JFytR-o;VYs}OuM>oekj&!j1RO4}3yyYh4 zKleE^jnUxawv(&vG!^LLWwme^0eiG8^(#`?3=-B-y&ie}21k7Jx0*D^@<<9vbXqK& z2EB4koTgkrH;iuh;c6r|eC+Ye#nPU9%qMc6kDc(a9=LZID0!C4-m>C8R|W3+-Tgl~ z)ZwzAiqhRvQ-!MZy=1ZsB5Lt31J(0^Y6-!&V|(CwuydE+IfED&E*mK#jOkNJS=y$Z z3;eP~fyosLCG66$K9iAE+ZG0K5GI)MQOAsv#Q$*LpX0ptxE($|BGwQC(_Per%%#A2 za-dQorqXg7?xxx7LL*G+yP#f~!=_9Bd_0ZYf|Wh4Sk(WG_%Gw$5kP_xKiYAoeHd^6szYWfB{smWsSxFBqX~&0elD+s|ui&!*xEVw#OSN z?3I8*fJDjaTHFzmHI^mpEjlSU{+pJ0903E%ot}8GaC1bvm-QLq)Z7)&cZ$Wzk$OB= z@6{N~?|Dy*WivRkQ1jSUQ$$`7{e+OiC^J3#$?&eGfc-8tNpd_k+mm&~Q|6D(%~e8- z%)B$7hm*<=7Mi%79&}95E+a_q{At=$lh)Y-$BY3;ACFX1yPUeCaFXi@cg@jM2--~) z7a`76tz`@$`<#i28~Yp5HUaZi4{p?Cx2sJc`Jdgo_N&5^ZXaVG((^uCMtpepPf-Qu#90i8;Y&dM!aB+#S4^cY-MwdJ{nsrv*Hz#JZ6|DJq)`Z+80Cu1zWC9K-lewq{@9-~3dC-e8 zu(GV<9jb{&$RXi`_C`&bDLpxoF^lq7C-d7b;enPfThf3d$NFPOK5fTkcd~G6oCK+R zGFM~FHP96rF$l>aAm0?qt*woQMXlg*MUMoIxj^;b8ZSt?68IfqhGzR?BpXzx_z#;1 zl-n_B-jq#yIMo=9#Y>$W^lcfXCHjGLCBf!-M>60bHuyLS?U24(2vDk4FHjN%R>55V z7SbIoammcO>)!5{UtqC#E$(ykGKzvuGJf6frw41}SgoK)q8b^kjSYpMn$Oj94$XGm zFa#N@P+Z0h*ISuiE$oq2et3Tt1|$%3pai5aaRmq*SQYNXA@BR2WZf(^xW9@~Pzjob z=X7`w(l=*acvM%^4YWAkI^{-VPbrZWM3C3e(EA{7q*g(_MDAp^P^YPj^$Td=XT|K7 zKGEmtLV-JVephFuTWz;P7-7!kzpJu=K;|ae8B&r}y~240_TjBgnOliFXnD1qR@1-? zQb9~ylFFSKKbsmtT7~hQI!16O)c-sG;V*O>wi$5j!dGHna+gih$G7<>=%afs@G!>{ zZt`|LzQRV{wOSqQMP&U<(XexW1ypBpO4CWqfa(lIQhFkTk6b6fIu$*e$B_$Nm|xXbY*6F;ZDh`TkG44~h({WpBAIPwB zz^aGsAK+Tb>W4vA*K~3HLVt!B0@sdXIlesOXFtP{jv!8FbiYB!J9zzuJGVv}j#g(Mdyr8-6<>e=iN4fy=qY)c0yKeBp z{vc@C7a!v%)&x?Q7pEq-W;Yey<-Ft=27QMWX9YlnKCBn?b*(dbABbysp*|d>R&jY| z{p0ga?oEfxu5fTsP_Bn4(JdZ!1ij4eP&$s;<)?&WZ$FsL;a}-Be@HXBJz39@SA&~R zF4uJ!7UABIMaGnmWORV;uSk3J`-~8pMGfzL+dLI3)4BBGPhc<6@gUgi^!$}C@Cbb| zth%}XqFmbRxbJE22|aC)iV1$|T?1{sDWhs`1{4P<7U|NNaH%!17r6nASDM#Y6LPkY-lKUV`A44nRt!LKS#ejZPabS8vop2)N8y~RBGJU z<8l3%^W=KY`B+?It;;*}V;;Bq<>hb4WEIJ6njq1lCxMv~Ame%*b+0?}<)s4mTduyT z=)(PSsxl(phc?Fzv1xERTiR1?d&F>3@5W@-D|6K{Lz%a44(p0L;rD zl+yWq%QZ1ZIJEz42$xqo`$O7p4RNURy~00*D{sEIRL+H{wgrZcx2*_4^5oKBQUF;v zeV6XWF zF}WYw2I+)8UumBG&F431Q+&tBYF&I{iLUT1K|$ zjXYk7gkrOeKWANjf?~6s_pM(jVAlh|3yQQR??KtdLZ-ARYYBW=>EJixk0PZ=ke*}? z2hO*_tjCD5rg4LCrEtDIbNNo~VscC(s!*{OFrStzZu{>W)~$bg!f0e{;{ z4VH?CHsKh?3B&<9-M<1CnFMl5+_-`Mn@m- zEq;?VgI|u8sXPoN8eRbX_LJ76IAX-O+WKlV(TL$ii2v&bJ>;@4#$}O_W~(P6yJ>>g zArNOj;*2&C^&o38=fBX)=RF2Nlt==m=!1VM0OW&Y`aU&u|6;q6Aa?tSZ?tbLP9a|{ z^`e*%XPC0iA|TwZUk$mI<#J`6XMWth%0EO~IE-WRLr?gVL z50=bCJ5N48^TV?;CNyxw_ouG|JS6N_#bRndZPNq-LgZ$n(gD_sG8(G!?CSc_g##iO@Ii5j$AI7dR?#l%cxcwL9Q1Yn}1}s5dl3N)PI{ z3+uWQ_qWR@>l0rbaUlxm8rbYk_vN=B`vcG_8+`I23GQZH%iFi|wc^O_+=)ko%-Osq z4M5=SxaEe4O$6|lI->}@$j>P88PI*8g3*=wby)IHU4@spz?1ieiL=B;y5Gq!N4buv zVR%Q>LNs}}33lZVS~AnY;eoIwpydB)cdY5AbrPhKGT*RotdCee z$Ip~BWmejCxZK$VSv5R1*h>Jjk>0yOdgr4R+6hcT$D=odj|onK85vYSS^0) z)OYIh^H6c@W>%6bCzD-*!CHg@2^E9CU?IX}&5RNDoF_`%a`KyYV883yee1_A8 zP_5wm#8x2%9o#(ISlnKAAj2R2iY3K8rWWVbP!27PRP0IDHl;ndtT<&>Pe<;)I)K*9 zhq0s)Hv`L&RNe6@kr_F|(d|eVEaY#}3-bt}up1D-bFT_u1d~8{n`;?* zB=&bVaEzaTVc#Qg@EuzczV z6C>3*R)cz|TpS>zxW;qqCwpxJGw^HPV2l%_1(jT-z>q4?mY0m75?rno-T}$uh_^8} z!T!ObAXw5He>@z*V9i`K1JKsD#RN5tRx>g}{EOl*o9iI3L6jq-`CEq_B?Zrq* zK>2rdN5LY5APe?7C39b19KZ(;g3S>F62cA#0?yfPbz{u4i^G%;O!fd54K{*%N5O81 z;AdcrO%y+3nC16AL5y@}DRO=2DEI_=#*^lq4K4xt)y5J0FA=)ojB{4A$Qzr;loY(G zcJ31z;55A^h%}h}5j4v;%$Kw6QW7g4nM40Sl#xqI>>* zo^t+=asVRy(&8AP7T6OWu-CK-5XXgDFmWnH8m4_inQmsdjB5f;`0H24n|jct*<@La z|F8Dhw;2@Vj> z2@@1cIAmuhP|NS9{(Xqwo6$N#9ij9v41oddCkw~ZH3hrc`3>z)o6k9OxMO8Efwh}r zhp0T+TFa~q5X<J4l{xtcBH5%%B|PcCcPQP6S4q~|1C^p_EEP~JHcYzt7xe| zh*`_(Q524eRx_;Fl*B$8Q5f4DUa=dE0HBl~C!Nr8dL40*Q-;TZ+<0_N2rpUEpa^eRE-ik~;*V zML`*mmh8GF4$b=FM`WYPr7(^mv_OlQe@J+j#98sWplh354Ct%Gu@3>?S|D^zl&C@0 zaHj}ng`_M|Y`@NFyKkBDU3g_A0eU<4H^DE$wgm(oH1q=~yT1l=5tj&%_P~Oc5+d3T zX@SlEp;{Z&M-X0&0VL~9L3&9M-y*B;0K70Zmf~l@@Mf-)2t7R-l2o1QyfF9U1>gG2i)k?x zn25mDcK$^H&ayeONesM9oFFq5O#CSrR6zLOeOR6VI8 zp^&eJ!;m;juwM5H_zu7uF%w~hgPG0AkZ8(K_dTs1%i8ZJ7U6N^!$32VMs&Sb_q#kt z%fEaGB8Sc$$wwal-h*{f-LHI*Cmjq;Gowpx1H@-3l3hMC3o5PC+caKM#VL3)y{*TQ z3dby1^0_DnZ4>Q8)_8k{4kjG~{@a@My8=FL&?@x5ESc@3gwe*ibvxS`h$jXmzu24d zg74X;rAWcrd8%~i$OdoLUiG~baKk7^;)i|3tUAe2kOpI%qnj+jmF38S35UDX5q>Qp zigJU1eN2wcu>X>GMExhs<2FsTxBzhDf*#dI15Gm}yVVItjH-!ZP#MWnJ<>eAHe(-V zulrl7gph@Dv!q`g0@7HZlYnOi#pueBKfpUXdJ1<*JZOj(CPJ`9+j?V;hta@_9A&U8 zSke1)7DcIOJi|DRc|7}V!s0(Y18_8(X+R6q%8<>NCSy?KNf7FOqX#19YeoW0R^!Oc z2EU!6`v6JBAhXeI%BbHx43BYzOxfoGonmd9*53D7Bu#aXyB=H-PIWU~Uz1HKP3@yfehn`alpC8 zl=)ZyBpch8-m}dqpB^D^H~KRGT&#oJ^au&<1C(#wA?jZ658t}Ae&A)77{pKIt!dCO8r)AH zwoX9vdt*A{pLq4XgJ{u;7|X`;uB^L`V-}>_N3_QmE=wuQLRp~zVIkf z!hZf0aR7TGwfN|l>U!0{DHggx954qTVq`Ol%T$p_W~P@kgdHQ`1i)5u{$b4SxcWbf35qqgLnV( zH}%`0x3h1A8xqO@J)3Ul)(}RPIZ^0{#nvm>2`5XJn*hb-&;?oXlYmAgV28K-Fu7#ql)g|!;pbtCi^F7`gx99k5c*FCt1brT02UoNez}7r`9mi8m&v%zF#p)B5nw6SSbKkm*u7(pGA{6O zu@!!p_>|o&SBCy^ew*+Smu_=|P-%U5*LTT{$Uhwz@*q1>G3r$zmi9;dP^T&O=*T-z z&fS175}#p-DUUe9)AL%h>CiEd`Ox<6#%A>H)@@T)j)lA+0i6&4fa1`Kp1q-oY+0u2 zGMlZ2$G)1Z%PFoqh}g5>_A|uF zi7^`&qeqcoe!?a$zrSYL#?1hE2G$|9HQv1%YK5LxQOL+;;SVH_ESY!1I)8VKgcX47+q?V}dU7RBhg1 z+t-*4c}A+&I+hsSycQ%>0;#-O*P2+yCMg3KW2~}>0w;jlT&+Y8bUWIMGTM~s09AP8 zzDBv}s=7fkaN#@uPAyJfH`L%}^4=pq#kEDLv!zI@Uce-rLuzLcVYNuMIl(mzldR{ zk5XA=!F_p)b?nHEft7~YZqB5>-g2}3oXOG~Py>RbZG2W9W0w(c^3{Ev0tYb284qo- z%&51p_!SBaau0{omdp5`Uq&35VI&2D6vJ8qgbr_Gb^Q{UDMx3m-arpvERP?h#HciT zbZ=q{?>+P!?nhuVpyPc3UhFH+{v};coc<2QQDY*WZW;ocX~E_7%5P}ir)65seV=%X zJ|VL^KwBijA+F53c$8UEKEj-Hya3kql=Ib)N%#w27x28h)PiGZ?N=(08R@rVU#T=7 z(*`>lZ@Rd3-s}hW=iNEwG7_JW_H025c&QrYm!@p~-`jM&8ib6ggEZhVdHI{!phvJn2nb4L*WR-oqy^gseMLZT zlx;X@_3Cmwd%V?Tjw=jT$g*g1eP%Vv%YuASSF-_#*cKbD_=rLkU~{!5M8p8W>oDJ+ zD@ybyKV)R{JC(A??RittP;7ccD}JGLf^kN{rvy*@Av$Jv0l}=h`PcxU%MD?^V=n~0k&_LKDBiTNw~u&k8#I7>Gjf|?#KkJNKb`r;AjhRt;pm5a1P(SQ9( z$W3D17S18#WY3C`Dz0HoCmY+V86S`kpL z>4>BN92HJaa~_8p*azlg|4E(cE=|Bgyj4^YOwtzOKDkIykqyrD@xWQyF>~k8> z5t`dMt&aH+X}3oCwN}!Pa!tK45`(xK7uY=etbo?Y2NJY9Z^)AM*2X6d0JCPoF9agQ!C&p!0j6DxrW z+7DBGy~ZPnXHvpo6dDhhIL^ug$sQN}t_iF(la8q~xb3Z^FNkG9zq4;No~^ec5va+r zDQ!5uU;+=gOfYd4=l%TF1F3}64<3tNvmKU!&Su^n?3o%#WxAiwvAJqxIjk2Zhq$tH zGy<5tNS105*L_OE{-fwz&IMit_zdx&Uy>ixA|p=b6TFF~rNUaZH8~W_N$T?AxUl~d$Vy1wH~9Wk%~^!&Zev9!)}-uKK$TQC2ElUvW}XMJmSpS|pPi%@1BJ(e+n>Kk=SVwmCd5>-EW;!5Ch zP8aaOJ_H1)vuCY;v$?w(=ylLN-+PMhLgeL_8io+Soagt-G&{0I*e@3GlxH+6S{&H& zx*g1Zi;2aNaV;=vb}e3~OWvkXn-^$c!$q`ufpJ@RxGah}^{%2{Z~SCqh)>xj$(X)^ z(5i6ojgHV!#72F5Q_B|Q#Dp;DnlMgp4&RN;$^|*A4?_ps>I*y+4iV~F$PU%Sx-%TH z!v3y8u_d=R$M;jNQ5L@5CUCQ7m-)!Ro8ors*AH!@uPSYURa}K-K$Q?ZI=1F;AQHZH z5zgb32w0E5Y?KX1D!rgJubBtO3K$IVh;iJLs$hGr#bu4E>Ug|je9G;Dy8g`d?U5Li z=nZ=Xc{w2Hv~xOf|7#vlEwl0ioNoKMu9<4AdcTL4che0%qwmOXaLcmxEk2`k6aZOR zS~&i%U}{sMpfjAE3CV=ei~2y=9PFM9;+TYy!llk|p3Hak20`H!+L~|m z=W%qxGWIihyJ?j;Re(!GY=Fc-?`?UZa~*dC?rFIBd_7siyJc~P(<)ExpWz>}D_QXM z2^n$oDo4=$kz^pJSn`n2M_qFKdh0JUSa-o|RotWnR?K$8#j!7Rn*l)XE9agAexL9K z%qv9VOS8I@4x=VP&_=5=ItbO2`FllYpH|LzSIc5P}$trD=_5c zb!Ox*^sDo{#kqT*8fHsLNipiY(3ig7HsEf3s|r*hUsns;VAW>pz@FfBUosD3^b}Zx zDP$1~lSG;dFfWaFw} zB24KBG;7l(+z)MahPjXG3SaDS{XRgSIiaNxcNY7>VNqFs+;NsFOGk`eP0O6bfg<$|u_&cX9 z*JetrTJ##T2BLERVDsN&>;kHFuLC*;;_2c9U2*cGmg;Ws!&+`Pnbg zTex10>~oQIqEnAvD;+t(<#abw{1)7=Q9kvJe1qzZ}=2$Dkv>@7F%s{!ksu}-3USzrq`kWd91v51q zb=vce?rILP1%aIGaQ-Q-;}6CO3%)Hstap$N(w>ROE9Z^E?1wZK7GA8n>OF5lgwWoj<2Yxrx4NBT8Q50Olm|*+AR^|4h|J1pdBVNfUSyunhP? z?INVA5aShCNuLNqm~N*D(s#iWm}3D{5t*ExH`OUsVhqQj)SoFz3Z26`cekW)9&dLk zX7#U~T~#%Ccu();Tz&rxZ=5zgZVTd@CH1rn5fUb0?Dm6fUn=Vl7_gC*z2^B4Sj*=F zb3+d-HNa2iCz`sFss!H3Ebn;qf}X~%1K6T+Bxb;~;wdvb2}PaGH}1fP#;ocd^D28X zS}`(kgw~DV%M7FG{a?K}-zQ@4xd7+cA%F6C1>`;ek02E|-eK;OD4*6!GJ6VFd;yNR zp0=1qqdO(tlAf;jzTk34xNFoSY%P=*?nQo=p3z^w27goi(L@6#cPozpN`Q@gr_^MC zuo5?q?uecJ5$i@`%j0NJfe9`2c-TcUb^C(FYeN>ejN9`fJBQRwFA7!@C~dS*0OA%g zDjxAxijlJhXnAe#dp13@x2I>(Z}b*Vfs}DA++|~=rA3K`91F}x1>V#Tgfv%$UUP#b zia!A%pqdI&;_*Bg>I|trzYmrwO%VGK&Y^ufAbG`U`Ia&EEJz2mEYu8mjC!O@L0Z>shWy5 z1XsxjCI%R5irWf(e`WqYG+gNyC8RhlO(4|=URt^N)0MjrtL;*wP}jw$$1%iqN@UU6 z^iH9|q|}*u@_mjEAx99Gei7w3(UyJ{uo)!CTV@>4zYYRle zA28^U!&kS5I%?k;x*9!eNMVDR22d6C2SxK@@bTqxy4W@i{FDS-B4xmHF|(_GKA<%a z%h3IWS-U_T_3C^1;#C~yDPGDa;w_=*H>5djrWnU^)^w)w~ zad?e<10M;KtHj0L${OE)x5a=4uDW#boM1-=OqMF3x`|!y{qbeZUpFlas|l}~*L)Pm z^Rvi99J5dMXdAw(u2g}a6z=zAX zi4BZpm9EdTA{3>0*A9VENLrun{c^=x)BUaM6=C=vY3Xo0HhG(-TSClKtWf~N6RXHi zpnJI%K3+HnvB0CGq!iLE*xXk1ZuXNFnS`7z1`%b`HD0vi^nZFZCQ)F%!;;7%vklUo zU{!rWBq=!so#H=S3mr7yT%p3c-#9vp9}WIAHv)FKVc*xRV@@aBY;Y4sf1M)J7=A?p zWuuypWRgw<>_w;31W-fxq6WOO>h4lSfyiCzMrb%|*D^3Aj>}6s4!7$7E=CSs zZnM=zo9CrPHy?XiT#WZ?*7?yQ`cNkSvdhCen27qXwM+ELu#i8rA1lNPJ~lhgO8Lm} zR5mUZcA-V5luaLZxht~PnqpwoU16a*5?g7}Ze{&V zNy3M}BDY@z9+VwhuAYSWGGIHlE1HVy<4&r$j}cmGW^arw*eh}a3RtU3; z1s`@7+prkAzBY)6T2H7~fK>Q9CtuUxo`3gaY@W#iuofpvn9&!<)N8Yj*!8Cx(Vhsw zrOr9)qr1~=++vmF4?K{uAYyx5%=`Gig`$e#e9DFRroFt%`~K!hz+tSm`U%5+O{90F z*$DH}9m2EGWUsrFdRts-eyKpPcdfv4&}=h`LRa6F!5@n>EQFL*EcaErmf6WV&1NC_ zu>;IvX3+?;Fv7^)!FFJ*$yF8C*=H7Vd_DeD=l#Zdmq*I2Km$Mc=16lIQZo7D8?7w8 zJ|Xu6MTIYb4cINT%L@eUwl=C0}a0y=fPrJa;d;{AWu2lX(lD$R{E?YY)Jbh$%et$TTKf|$Z zIPAKy$L6(++UE6IkxC_F(WXJeV8JnNxHDpG+YRn06p{WS!ePBJ?#nvxITNCW+zNrh zNI+X^*LJ_s&E!&m*qN4SmON}xEobIW^2LR0jYj(>9T0^HX@d`&$Uz?fUp~pF6!a!?3mcs4+_KW+}=rW7tvq7|sdPKtN2~n4hQ#HL8xmHdj zYrmXka!Nu@CN!Zi!3@)Be_5$~{-mHQW;@SsAM2=z>t=4Je#FG80toK3-bl#4bRzrw z#3gh?_o8Vf!@e>b|P?9Hn3M2Aw91mmt|iC1Idm~3EGxN zWDE>ZFE5dq(>zg}*6at{`;D&N5@vO$c#P)E;Lum2#1Pfcf#t?H#K(@qK%Yz?mwH0H zCY(OUf}#KnP*m2Mo#ECXyZ9fI`B7mdsMK~w(8wzRs!D=u`YbALLW$DaV_@5+zM}JL zMxO>9X)hsUgZ7`;BLHI0{arQ3pj0R)kdHXZ131?-xaKX?!d^0O5CZlnmvWi_yvj^V zJKM8}l5LmnufH>t4qm^b0Cdlk>i{tAVUV@0&2cJt!Dc;c%E)$f?M=LxQ3PA}+RvL0PMNN#;gAr z59=i8al6R)^=p7Zv0AocyK(&uk$?zl0%)2dk6UJZdBBts2KJ&r%nu&; z!yr^|;QOE(RucIns1#HcpQ)=Y4fF|h%5MbShJop22h1Cq*iP1%+Hz+)r|b}|U{dg_ zmTWb24p7!#j3_O9UYmKBvt@XT@TAH`rbxS?HMEAl4RNGRI|Vk&nF2Jtq||@wGi*J5 zLmgQPCUz}&%oS5L^73*J!B=VVkKx&<{}>S7abv>sHEULw!wWvbws9e<(AQ>PJw3Yl z?QrYglQ46R(0sNr6g0z!LBGM*&+Ts~@{fPbJEa62vcm26suz$jPkFU#B;_Kq~^Pem* zw|Xb^$0#wZ`1K!kzgma_f`i%!(N2mcUvCX)z=2fj2A~2u{MBjQh)qMwiHY+p=wmAw zeDW645|?@wm@@*9h{J&e_mfxw3$E)P)3c*~jkgA3ySJ1&-#+1Q*I}$QzgYuy-kVR2 z&p&f70b)V^2M1h1w4XNC22fCx42o5&HI~{-UQc|hr`H{#mQP|t+0IK24G#Y3Nj(W{ z-M>2Z5n>@g7c(_2qc4P~+`qXjgA!?<89%qFAG|vO$6>4s$N$}4tp$b``cRXjf;>D2 z9YYEEJK|-$r`U~|aoSoUZ;=J$=|Pzu1?kxAWw>;@o)@`-xOptR{$_uFi+*G*c7koc z($z0Sd-7Z~>?NV@;$tc+IiGg&-?dH_a4cC*_zXa1^Q7ppAqjsJ_XQ9f57rq~J z3&R6fEtLzjArf!^%9YVdDdA;vO~`Xl?x}#;n|f8!P%iM41j)>h!0Chwz5$_q^a*y~iDf+8GIk5&Xd?jgRfODw3O;gA$_wCijicA! z?gQ%f#S3!ht5dvY0)1-6)(qDSTJ>E91beVKbhH~A`bK9X)vwrU9qJ4aJ=*UlSx0X# z{V?b)LK3^)Um#*bjMrvBrO-5{SR1pL6;R%fO|^ekkA+5^e5=iqq-d4oz_&}GKY6$u z#NA2y_#f`X5=g?M?+!I!05UPs6=P879e9Rolx7{&;_72xWDxxy#Y%j%pI(S(c^5)m zGuxe@2`EyJ`MohC-=}aRk;BOCOG!!nM17IfUB0clM#F*T`$*|2u&e$e{#o)nHL*OB zjlLNF*Mqv4sri|z1C;ov~G4m80J;~u^K z2^!V)GX&%c01MIA?xWrC$$?5mKUswfP_O9Yl;cbiu%+v(-7lX^d48N{FhU4KZUg-a z#*ycitC}^|#+QG}Oa@A3Z$MTQykXo_jcxr3;2?8MpeX+f#Qh(_+F}%8-!BnAzjYlS6C59V}llL`W+TXpM|>hgp$0 zGJtPW>R2B~TD>|PZ=#otgvR?jGxS+X2f6J!&$Rp)n(=Z-HB)Ug)#=@7uWxS;%oVAV zpwbDj7z*rXZ9H}>&FfpZ%Q)Me5#b6Qn7oP#{bzmW72e~xlYz-oikbmUB%~p##%(5= zEzyM10NnLh1|5Xh1~$y$OH7iikk9zgaU(X&@VbKlxrKJ;6=?7YP9~m#yvr(Pv6P&# zMG}zdv=kz&doq^}lNvvrnjbvEobR*T-)#-(RRVM})4o!I9B-Roj3fviciEWRS^=dE z8Pf#d|E;46&d5!l5Cb0DT8j_g8lbq3<)TJIh-K>xyU{D{eA^!}!p_Drq-(NV#XmK{ zd#%MS%XmWksrh5WSbzqCh0AtLzhw1iSL@NA`DWcU(9Q-bTyfjdT7pBCxL(>g$H$%Q zNVCn#{%(Wfj}x>{rG^B0ZiauQd=QwF$ZQPB5S%GDlQqVWus;AW48~VQz)q5y6xGZ$ za)j?ukj{utYSqUAx9FlF^DnOl&`Mp@*|T$G;`EDz5?*Xd;zC=lP8iUZYvDhogwaQR z?b9t(r5bf3rI6a1G3lDZPK0(HgWFYmRwu{40rQ1GKf}*fMp7d^8&L+GC&8F|tbHxt zHhJ$3-9_l^YgcuOW!N^z7Nx?xrLIhtdSZSWu|L=j7(myIj1!DX80V?zp8ays>pZ&moZ@SSV%^BB$H-JY8OaiO?M6JiLqdO0Q4I>&JH6kY(R2t zswu0cRQk1mkS7;YLlr0)l`Y0C$+25{l~dmHkRVcEcSgo-@nEY)um6mLHouOarh7Gm zq93qZr}IE#2#TuC0UM5GK~P&ZufA|dO zYTOO~lB~Rggcjp`u?60Di#NKR%8l-qVp=(gxysug$wf5lT|%UYWXMw zP7`eZ?g4!`AbNfslc_F6lE`8am}fupi*5qE{KUtvIFG)TTvuR5sT}Vf3B=sAUQ?y3;sV#?Dy@Fcm=jg1{CcR4;C`~NRPc}X zCa@LxBCGAl`GB1JOG#D)pcK=m$tI+J|X?+O1(esFQq#yw!GPZGWmN7&JAD4-EsH!*3OXR zR`Xh$Ku;)IKGt7ux?MOp$DK~6Vr!+3VC1yBlM=2RPl7lK6tiSCS_8kbpWh5l)e5tmJ8sgQ9 ztm@~rLO0&0FrQ&zCWAo?=Gn^w0(M%eZGSZJORewcBn~)yCiNHiiJ@Mbc^pvc2hQ zIk8vUjgBAzAi%o9ithFNN*!p^fDT3(TOciwTL6YaZBMd;WfMq)sJ7Ze79!&b3#v8n z#axNkeO3^$H!o0EmkM~s7hW(efaE+YNgG8>@4(yjL&QTiBBlygs(x*Q#ykRZkq>~r z5yb02pB+m9cl@mSgHdlX4b_2mmd*NarXST!wr4xy@;#wXa8d?5iXfD%)w!WwX9*K3(sF&+w&(;1p6oZsi)^Qd^-S~b(Dl2z8-+L{fo-3fM zpg`kvz3bFvsr?zaU<6v70Eg)`8X`}aF}r0CbS>75e4N1>Ld!^`29r@ zY4<-qD1SGKqezH78pSZb9r*H1L$Sfr288I85)GY#H0`wLe;;Oh4QOlC_tGSq$|SP$ zpLJ#BnU7&9`yrr?);O4qfdt(${ZR{yP-rxTY%aL2rmm3Bf2t@AUj}D9O&cCxK?dn6 zKwT<>DK`f$5Vgvr>$V8w%BOQF9WMQL!I=tsHS%FkX8|;GKzsRD zUEZvHn3*#jRRV;ep(S_13INxv020dxEFm6@v(#A7Y+|1=l+vGPTL?RS)0Q;9TXI*K z>ivWEHh*p4UL}b=N?${}=I!6`LJ~PP{SaQNO;|t?qD%EJhuzQ`&TyF5VWQ~UL)YN1 z-3i=dVq%Z?u4r&RVNf9XtD^p&Q3g5Zrl~KR?Ea3aijzrRSF{M}O%%?tBLZ&)RUBGP zq%LVN&5H}J1SY}2HSEayTALQE1@c2-mCx5bFZ38N)$5~C1b-EEV}o9-ZVXr6JxGH} zrk@uEk*u|#@6-xzQi5#*^SRoJH>Zt{fBZ%Uk@1Bn2tuv7Rx@Z@AlJ%E;&KK)T0c6T zlZ=048-}zz<%EEK^6!;yZ1GLo4m6T|%EE1<%Dz zb0wD;V*shk4R7E-_6sh-QxB0Z%;JsL-AyiNbaOoCiuNIn<8LqpZsO(uHQ`x9sgMy2 z48h(d*Z_+YS73SE)_6^P>Z1wdp|70)$8*66gRD^Nm@R9&P8`uV5sdQXuFrOvY-R|9 zsQj!_b?D4Nm9buS#Ukzlwu_J!aujiZgO5Dm*K<|?RH4N;d#Ih%hi5(sHjQz?kl$l1 zeu#dRenWv2Uq4Rd(4c?s1a=6eA&#*y3k}y4fY^!dInR>j1_1D24+q}!+;$lFu>-vy zrTP6i@JV=qFpIt}@TsB`Kpqs3k&#bOxi`cQ=@J<~Ltj@6xI~bW@)q-2qcJDhhORk* z>IhppMDn5L<0pPc3?ATzwC}&d{`&wU$3R`Cqv8dVaq|iju&J5!3xtzRa?ZOL!q#6t z)K|Z&GX zq#xh1)dnW+oxeF2{xg5j94qv^G7ADMsh4rxJe}v@9V-CSm^|?0i|p6?e<>&^i~<(C z$D(`G48A{=pESk-nKYyg9%B>KuI;OWo**m=ues-8A0CjO*( zc!Pe0W3v{X@Zo zr08f}{knq1bTAUxTnrPA>&C|)0deVfB1c&_#40yVKobD+78-ADyR0Fx@>`%Rbq|9V zh`ks=vx8P?t6foAI{G9XzVOFmV6V|*!k3_$(ofMPIu?<)YhZv^$JicPjFFO)k1K(J z_aT!JN(_Gn3gSJ;_f%YuR>*!zP|<#*lHd7o2f_d-fI0w=+1amR^{qe%7MU`@8D|$X z@>%Jiv5w@0K;&KrY*BVid@Kaobs>H09n$wYHTE%$upArsyTv~%odc#Tt@5-6y)A|b z5&1}PKxWT}p$jI{=t@>Bi1B8Ju>fg7Bll46Lf>pgZ1B)h;>;C$NTBiaU;POWG%xBD zE_?!W7zX0v;eXsI!h>S_pzVR*)grTBr&E35esRUJ_)81}nDjFUhV^{CIM|KmApQZo zS4dYgo@9hoN#0&8GcI`e1Iidl+{L>*&CkDmcc5tZkzMBP^`NN|IebAQA8^(h;80GvBc-(bt?Qp(33^3w zCtnt7qY@upn17G~7&M^#z@d3VM#xXRivd2wZgE7`H&A5cu>MTJ`P+!=RMlt37;LXc z#T(B9+2Skb6}i7r4EpQ;`RU-5fPRXe80BDBqaGy{AapTaJxn-WQ=KYZ4|`HSQKTjR zgErCV`Fxp)EEGa7Os4&x^uzNZ{rLYFe_TP0KbA1zhS5Mc96{T5ff|1#o0DF-GC&U@ zj)yy$2<&5N2rwpUP_L2+c12*ef4I{BeI=n~3>6#%1c+zU?=XOk-aED8Y$|zL@j1=Z z*q*qo)GIQHKl4I|nl&!!TAU7=1anpLN$-=WSL)yDy&HOXw=s&KwO;*r*vdb#`{A$l z*?0|-qYXVu24ons0Pwb(1vt9@B0VM4ImO(a{hI}_)?%d)!LR|&UT|#H_jyG$OTH*cU%o_W(wL|!7gkDfN=T}^{6_uZeX>)ajAOwQ zfb{0Gyj#Wk_XYarliXm^BgWr3>pOf0A0;FNRXIR?SE=&mmp*JN+4!~zdL9p6qcYf? zRs@5{lGzx=zkh`f#2flq0@c4U{_khOzz0tSzEgA$M5JJ7L{fWKG7t^?au{R=o;_Ts zzf7dXrvLXr{`21rvHrd^lx5h*TjMr)J>*Vu6ZR|8DD28-c{rnY;SOUy9vWky8$0|) zTKvs2@xio(O>Q!eTi$w#mq<7P7uB+%racu13!X^r`os5NAv5QvaA+PF;OBsW#N{sv ziZdirh)R)mno7~#(0p`f9w-LnK{r_*tW+t?RM{N9l$?eKMQ~lNVpe0WTFKX*A9PK% zo8-Shl~w>ohPi0?lLN2pmMM1a@f*JBE2~~zmby_G#2BB*Ma9;WM1FTlh?G-t+MA6o zySiMzR|FN?al92W^eSc$Ab#P|_|i!E=X3Bv=|Dq|#rr1;`*C&i4`JG9ulD=!{|1SF zVnrl5QY>s=xfnh4p|EXbNKjO&OpFA_{s<3t4XSi6I@_Jcc& z&=8c+b4EbTJGS`*aPvB&U}`c7I%#8HcxjrS$*U#J&&e}b{$ zNI(Y`d@Z=l8=f)XQ?po23$bPgcK^!)Fkes=k(nnfQ*P;+ylU$Bv@yXj(HbUB8h3FMhmrO#y}FbaZ_u>?!f@^?Prad zREp&M&)>?)0na~;+oN8mCryepYV@*h$G1xw0Dn$HV!x0HF4=s5Upw1Y+xDI=)++$3 zUKs;L_=Zw&^=Fs#gap8}ckHLIB$$c{dnNfl^Pd1gSTJ4d6b(4^ zZoJfkB4@i?a-c!M=W#wqo&4qS7$Y6KXrE?lOV;Zuw3Yh>ecf(lY0q||S=eXtAq4(r ziv~$I0=QB3S|cC>VxWtl5-I&Z$w;WM`s(*qtBrcc%LnNbk&$HwuWqd6#u=yjQ~Gs-}c~1h>cXGro_S0Z28yz>SNvD@QYP45Y!BzV6Hs|C z^GPU+o@CYvzuhqVHk@LXpoG(fCK!R9hn&3{#udTgq1FCgvj@aDQ)3)ZaIVWujnLD2 z{ifl3BfZ%$_~TweL_$tEBh%r{UqWCIF3^|8MPk?R=VzBGQ4b0$bGl<6&5e*mB=-8p z#6B8MUVuZ58@Q?c@nJV!G1YHu>HJ72cwQ#jW@Th=AgQznG-@G?F`|R5Q}j zTo0NJO>pGm05+ze^$&>9Mvw^QK2|vzH?RmV*_2O(ZW?}ZpHuUIx8Z;FMBtxD?V+!; zRh-OU{EPeBL9d-0sXjexdaJ{oF$#d#f0S$bf;;+bnpGpFNWZD>74O+ol73Wk^~bAT zn!v!P-7O$pa3Xga1H@a%d;*jQUJ*BM8ZGTDVo?q z#Y}m1f)J7-+Fb=nUitaDjATT2VBb_BO)Etu=Kl#xOPd9(8x=kK?-TbQ-F?Yhy&tbJfVU&qw?%(S&PJ*YjjHifvj!wX@tQ28ObDQ zRKtZmLo)NW_NFwKDbM>(jp@qWj+Vq)Aj5uBiF8<23%69~Tr(_WFJ*s#UR{N`<%}Ln z7TowUNAoC{@iUIC9cFPsAZL5jTMUDo1HO(%My4LCs+6Z5?)O5K*)K`^zv6n1M^_}u z#>FN#kTrwi@H+RyE}Qtke&2>-Y_wb=ZMP+ud4&*^dtXPMMSu7zxNopmt+@UMFyp`N zP1|Jy5O(2FX$Cm%g@q(pK-HH2GVWvIc08|F>`6h74?&CUQ5n@*TN% zBTO;{bWVf^gRTp}1iwnO3rNU3q#i(RV#j{BEhHo->rx!t3>5p}^3Cw6#$!*b_8%|X z9%1PI_(&sbqFBDKgBoecte5z=Bh>j!GCwIjf~<035c(6@?+G%)$RGlCFgUF>%gSxQ zuCGesbH<|-3~+0d%I8Ve2}xr>`v@Q!`Vaxe8_7i!OhnJ|14GXcqvk2#eL)PDmZ_;pG3 z*~zP&83#0+40*VE!UfK3^V-03OkGd9)vg4lq-2Pc9O$A-r-+gXmZt(xx z%>TV|kD*_j53Z>@)I=Oe0JtUc-%Y<0MXA`$y`ee}4f+()Wir+kF`;&UgEIArXkHB* zXWM&|c#qw2btJy>+q2zazJkZ)N3Q_aOC`;=>!JHPW5+QRRJEO)>?etqfG*?lEIIj!dpUJV6ji)I50-0fboQOCxfB`a)Fz$E#i$vQAjW{R# z&Ltd%=Sh)&)(QD{dVWJ6AP&?&}&4> zhgFSAdS}008K4Z9@if5 zf<%o=CVs9lW~$OkEqOzRY;jP`AC6y?X;*iAPCwe>sVpt^I`r_b(yp;=1@ph^;lLwY zyd@6{KS2_XJ>;2p+WO{AZ^kS-_+S&#fp{d_jSh`jBa6owWFSUu`+lU@m9}8i@>mq~ z<9(emb5=r9e~q&3^Bi~Gac$atcB-uBO}Yi%fq9vZ%dSU}C}n8ixAf2pyb<2H-E#e- z4a>5PNUhJT3k@!fi+4Y|;RlpS!dD5UwJsM?-cEpeZ^RZ6z~A_pHgGAhN5HGQl|9zjJqxF zi!A;)4>w9G`5iiBD|EjxyzqU-v5Igzl#lo_G6i*}OqY3YndWmbst-!sXNzOYXv5ZkN2t4IXn4W_F&tpIxCIa zz6x$qr6OD}d7pAeD#&q3H#t6&#h89*q*?x)Gk!}9 zb62<0zZ$$@T9b-@)?O5XH>|EA%-z1?Z|6Qclco1x-Y~Eig?Z|njW5YEVi1zNjn6yp zzpF49lu|g9=5|7U3{t%#7S6rqy;E^T4n2V|_zGxii7eaQGd$8_0B1mA4|Cn}nP&{} zYsPit{*DnU&&ybwiP%w(>bHCvuoI|jLX1<7!>ColLWlHhPT(@1{_%G={vVwfVu6U2 z%jani#c$U+g;dg28$J;=6)6Gql`*I?B+N?{Vcic#bEWL|PgPJ;S~@DpRe+E}D3aL)UM-xuabc{?e4lIJs@n&C%y7eUsGL3<+D+SNPG z2Od2~q;dpvD^-9Nq+kzNobAFw^Z;|2%_}F8JzTKmr^H%oa@yMl-D{5ZbaNbY5o!Ai zGQzbCL7&`b#{I*M-Ge%H`T29=;Wq7{`C}!w(26DI6uhRpI zP!ck<{sfD8fG&ObJkr^S6_9cDsFRgoMM{BY0beurGCsi}IYl3I$5|%Q4K9f9jZ)am zcCY((*{$HosRx(a+x1L$$IZlUWMmqR%g5yYUMG?KA}4SS^E~KOz0vp>j=v6^jmj9r z6N$T;L6$f}(d5tBmMA*9e(~_Dp3nZ_EbRjX!;r9smGNrdj(!L3Be@2=Fs%BXUxyyg zGFSOKEv0~mOX7Yj9&zBFsV2h`X6#eB0gAInF}23OF9#mT>xaf}c%S+9AP3t&_=0zN zhHY*+SWjnp=!#M6Gn5fs_D2mJ=Ihwo5`Mn2;~-647W;imFFzI2uXtXg29oUHrxX6pDws( ze!26j;Rh`ujQIj3?jN{&VBon=VsU6|A+Fz5yvK*N9bG})bC}s~EiM+sWz=8jgoLp2 z>5b(|?9Tggk?6J&d5>M;+H)sY!C85(b&Y`|fJ$_OEPF zjD@1ia}Odj^Gi|Is>NqU3<6JkOgN&Cr3he?gCE_NZfBUE0>%s&eC!n?f;wR=qws@W|C zgIbKSYiDYN%Egk`tKH%T+QB}g zB1^f^RuK-=cMRyE3e{@7&)D_5T8!H37tKc$8N*KMbP2>mbMHK!x+`L$v2myzy| zfVtYAKqoIada{OV4r45c?(?xbYP)ikRgW*sr zhYXXCAl3yKHHq8#l4G2~SL;yU2!bY!>U2?>css?i`E;Od*QenNm&UGBfd@(NzdN?e zLSgLFwb2Ry&nEw9Mv_~#&vMxI0)ITFFPI=ma+@O^znBV0!vr);tP8(7CniE#Jdg;YCN|f(J z(r%9K@Y%@-F6(1?Z5x6plcB7r$smYoNJ=kfyP5ZV`+YT7J!H&3>){DU@cB@NN$_cn zt|N4Rp|#%iWV+mkFAha@-i~Z*mJGXXW?{OiW@gw0Z{)xwlx&Nqlfmu(%zvPl#oBXF zNu=OhL*bxr6&*|rwucudL2UhS2oMmUFnJcncZ(*yA4*mD76_~Is?Vw*$Rio7lup*P z0p>MaE4E}3r1qhmgW7)}3Ymznjx*@$QzzLL3L*DzDM6djY)-5tZ;TM9W6)(3Z)e^T zggt3L2tr%>8Zl+nEWL(31Q~e@8fq@rs!EqXzc_}#oBaCWqU$blT-ECqXTWBykcg7p`xyY^dyU8Qo>5_~e#>S=;1XAd|8DT8RYT=xH?Iq(&pV7V#BetO z)~fF$s6fT>PSTvCccRA1$8hmRf{_L~C-wKAm+@ma+8!48y}>a!AW8@50pP$tNlp>~ z6Y?zF*bRU_?w!eI%X$7zi;;lak(Tc3*4H{uI0lw$xO}WKgx>QyH)9Lr9<+ zpvheS*1KaDXzYGb;F_ZWt3rr_lp_h(ki*5KmdL>Nu@^HX-^wEMwI;*}s4jqMnv9f-UXI#d_CW^WFi`5z- z1|F&Pr}4lKx_pHg?`pMclhY?0H*HKiPIQVfphS88wF)V;Si8q*y_H(^;Gs+PW70WS z`u0y9&2WaLa1YP7%Q6ZyY91~8e!JR22B!pjK2?Gg(-0%PrU>12GoQ>3pHPLErCH*O=^d;@*gq{uO?fu2&}?SLkJx?>(Pov|5rW7!Dy_-ZG6%aD9KBQenI2 zHhXh-wV0UjA_Tm&SsD&}&RcI1WN75^dSRPEoE_#^S>nw)$)o)J*9W}|GO-&hD~Ok1 zE2u9czO}|Dz1fcEz0Gzv&Mo(|TVfTg9ddJ}=m!^cN&j;bl;UEGoQ=B_A zum!S(Qr$wxbIQKFAj*C1C{|e=HKM>>(*g!=s}Bc%W+%d7eImY5eoOHp6mUcm$}?=G9P_!3aQV0b?bV5eY3Yx)D0OC#pSIj9!A ze-3+J`xVuTbPw%ykZqmsj3rDSs0=lNP{v?a!=vw#xYIm|58&g@4Smu01!tY!NsIpo zkWPPqmfsC#4Ulk7&Hor6|?sQZ13=xv2}3=KKxLmH_urM zdo~9}IK-tE9C(Y!wVZybIhCK*7p-g6>bo=g zR0TbiQ29amKZUVF(WOMt7Mv-UlTGut5!Hr~N(~~UY0k9BJXbLBNy5PMjLkY?xSgCC+&x(OtyN|v*!?)2Y}%M79;I4C|TnG6NjvRhP1ZRyKeW52lk9cxt?P1 z*Q6~w!BFivFw0NS?uE(k@L6w>SjuJ$xxhm800I+c3^&Xx!VRKiC$}5oDw=EDOX#y| z6_1B>HfO_J`gpf};Qt+k=@dY{d0jOz9AS0Cw~nP#nWKc?WMoJdkrs&OMEKkCVI|B1_xqfTxtL@Mo%2 zh#>`7oq}3S1hLkW5@=K>LFn}d$K?uYuE3ScMYn37nLMABi_X|+FD0~qiKc((!4qrh=Oke71{x|Ieve}(@05A~c znLZaIt?w6Ibzkb7-L$(MesBarWt}rw6)i_DeeN!PyB>s91=Vln-UfdsX>(qDArYoCNFI;A;7})^N7h_^eck3fHG;&`;deQq?asma1gaQ zK(oMlUCF}ldG>`j`Di99nJY8gL+UD1#`W04mAg_E=Q%<($WQ(r_&gV_b`;9_i<-+y zN)NiV)C7I+z8nO=0P{b64^jK9F;9;ZN)98T6P6?G*5^zs6gLTyN_S@^Zg_pqaC<&N zfYY-z0eKrb#0EfsfGhuP3crVEiru+_fdZ@}3Lc$4593X~9Yf$m!DV}P|5jLd#k>!} zK4kY^Z*leA5_z-#yNW@G2H5cbwwQ$oyn43jYfjEY=A&L`JDpt}*ANRVeEUMvN}#lU zo7G6gP(Pe0kc3@@275JC5&|R;G*h#yz64q|7Rd#{5wgkNVY@PS9ksTz8tvGxJHRKI z(RW!aTW2;v=}7S8l&5cvbV65wcducS>j4S|3(~m-iSz>{#wY|!hhB<fl(6m}Q7t8$p8pMZDQ*~`lAwkB}5J?OT7;Q-q(*^;Ee$Khc7rAa0cJ^p&? zyyusTxkkbD#um=M+G)M8_x}-K|3^Uz&fQNcAVZumiw$oEEEw{uXU__`Se4S=N%TNP zF8zZB+1jEu1;)fqouPK#*A9MO8X(RGg(}S=>psnMBd+(Gi0ug2n3CzZ|HvVKsEb|_wLcf& zDr%&;GI1sMW|U5K`A^{TgTNPGynMIyRzuj|6MpUfm*E4Rf7{GHZk}H9XEl=NmX4e_|(cO3lAhRi|W1O9Ip-SThVQ{#Fa>uDlvy&m`Gg%1e$u92| z-=X^~rzC%|w&uiMeU5Bpd1Rc6Mvn_l(hMIfa}o>QU~}F~-Iu_-@H%27 zLeibtM&->Z~=_@I6hr|)_hnT!yx1X@a-*|&Lj3L*#$UnW{~9hf>wqKlDdr7UwiU>7 za%_W%6rYnXB4Qn}^Wc!Q&w_%e+>Z%<-Hh!vK#vv`X0%xM$7bHum#-z2Rf0;CS;lLu z4yP%%y1z%qb(QUp-w?d!oIYO71oD8WzLe)85x=KOri?lem-uM+d3>uXI%zQQ9D`fsK)^ND@VxO#|=!a#^v z9x#9Pue(LBFY+?427U~Pv!`6w%W$Y)cRE=91;he^(#~*=QvgHuQVmox+SMBLtgI38 z21lkYJo`TBL4(LD%$zRXC*4x62BHSYVMZ2`TO8@1#IVq|htj?RX_^E<(B4T8@_tXA z{i0{p`=VOI5O**i7zq}%-2gN8=1;tPZT8E$O~E0_7GbhI4O?|!hvQm4^6woE&rHsL zpU^r+fClaZJ|(#Wz{DZ8`u_ww955&3Q@}i0SsVm3It{<8&Lfyp%a+mV?F?I9gc(5e zP@-%({{!a)P3x8~e@Qbwd!K(+)AuTIBk+5hqnafjp&qk#9>{93}nPzHo? zYxE#vi;PQaKt;b0c!V9ppre+qX<)*10j}Ag5{sW%SopA)D}Y5QBU2mkXCg2{h(gjk zes6Y5Xi%06wP;g>L#au5*OO{%*O8P`6hpjTfOpZTZPg?cZWJgEzVAX+jMe=efXBn` z(UGcHbZY%*f8Xx6+EGu>*b`V3@E#b~Lg#*C6TM6j6ULlv^FvD!&N^%=MSt6vXhJU6Dc+ng z;o=D7pg{h#UAKhvs*B&-g4Nf4JioC-8{(=`A<(roaqtXCWr7g7?bIq84m$d4uS9>^ zWmPC;`9g=?G;9~F8u9fEAu`b61aHbMzwtKH%B!9Yke96j>OJJWycj{mhNI06kg-z; zlAlS8G`Q6C3_7|!(`~kRCfyg#2o6vDpI(mh%lVQUmQyA*Y(8zbnf|6=Cx*&TZ=So1F|-fcaBoOC5c|uXpY0YSMWz0>wX>*|S!OkDeZfMq z1n7xNqu>1=4fe2jE*C+^^c<+w{%^;G|2g>!yM=vfVt+3d`|y+c<|1)Ca$rjdc|tol4-S)n` z5E%$p3fN$AD&k@wK2HNk0pzl{?5q)x;aOB|^!28R8PrSU^P9iQiwE&cg34xB7ffKl zV?{HW^?1h^M!?_DrqgijWk@*2|KtSZmoX7;F1lS7goG1p5js@?>!fv8IIAGz8M$

j_$h?iKxl@ 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

)(x6=*t7@LH(`bd&EMXHYll`) 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 bUfVi_E@dwBb|l*Z|5VOup2<-(yYc@3ljoX9 literal 0 HcmV?d00001 From 15663e66c82536809f46225bba14d9429936348c Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Wed, 26 Apr 2023 14:53:46 -0700 Subject: [PATCH 598/775] Install `libTracyClient` when `WITH_TRACY` is enabled. Without this, `make install` does not bundle `libTracyClient`, which is important when trying to redistribute a build of Julia that is built with Tracy support. --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 60aca9a4e8d22..046f18492bc3e 100644 --- a/Makefile +++ b/Makefile @@ -237,6 +237,9 @@ JL_PRIVATE_LIBS-$(USE_SYSTEM_CSL) += libwinpthread else JL_PRIVATE_LIBS-$(USE_SYSTEM_CSL) += libpthread endif +ifeq ($(WITH_TRACY),1) +JL_PRIVATE_LIBS-0 += libTracyClient +endif ifeq ($(OS),Darwin) From b3bbd5b7aa6e738137eec8d3a83fca3edb6c2213 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 27 Apr 2023 07:02:33 +0800 Subject: [PATCH 599/775] Intersect: fix `intersect_tuple` for inputs with bad normalization. (#49499) --- src/subtype.c | 67 +++++++++++++++++++++++++++++-------------------- test/subtype.jl | 11 ++++++++ 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index a12faf1400b58..83b8a8413394d 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -3085,13 +3085,15 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, ssize_t assert(e->Loffset == 0); e->Loffset = offset; jl_varbinding_t *xb = NULL, *yb = NULL; - if (xp2 && jl_is_typevar(xp2)) { + if (xp2) { + assert(jl_is_typevar(xp2)); xb = lookup(e, (jl_tvar_t*)xp2); if (xb) xb->intvalued = 1; if (!yp2) i2 = bound_var_below((jl_tvar_t*)xp2, xb, e, 0); } - if (yp2 && jl_is_typevar(yp2)) { + if (yp2) { + assert(jl_is_typevar(yp2)); yb = lookup(e, (jl_tvar_t*)yp2); if (yb) yb->intvalued = 1; if (!xp2) @@ -3124,14 +3126,24 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, ssize_t static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_stenv_t *e, int param) { size_t lx = jl_nparams(xd), ly = jl_nparams(yd); + size_t llx = lx, lly = ly; if (lx == 0 && ly == 0) return (jl_value_t*)yd; - int vx=0, vy=0, vvx = (lx > 0 && jl_is_vararg(jl_tparam(xd, lx-1))); - int vvy = (ly > 0 && jl_is_vararg(jl_tparam(yd, ly-1))); - if (!vvx && !vvy && lx != ly) - return jl_bottom_type; + int vx=0, vy=0; + jl_vararg_kind_t vvx = lx > 0 ? jl_vararg_kind(jl_tparam(xd, lx-1)) : JL_VARARG_NONE; + jl_vararg_kind_t vvy = ly > 0 ? jl_vararg_kind(jl_tparam(yd, ly-1)) : JL_VARARG_NONE; + if (vvx == JL_VARARG_INT) + llx += jl_unbox_long(jl_unwrap_vararg_num((jl_vararg_t *)jl_tparam(xd, lx-1))) - 1; + if (vvy == JL_VARARG_INT) + lly += jl_unbox_long(jl_unwrap_vararg_num((jl_vararg_t *)jl_tparam(yd, ly-1))) - 1; + + if ((vvx == JL_VARARG_NONE || vvx == JL_VARARG_INT) && + (vvy == JL_VARARG_NONE || vvy == JL_VARARG_INT)) { + if (llx != lly) + return jl_bottom_type; + } - size_t np = lx > ly ? lx : ly; + size_t np = llx > lly ? llx : lly; jl_value_t *res = NULL; jl_svec_t *p = NULL; jl_value_t **params; @@ -3150,20 +3162,20 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten int isx = 1, isy = 1; // try to reuse the object x or y as res whenever we can (e.g. when it is the supertype) instead of allocating a copy while (1) { vx = vy = 0; - xi = i < lx ? jl_tparam(xd, i) : NULL; - yi = j < ly ? jl_tparam(yd, j) : NULL; + xi = i < llx ? jl_tparam(xd, i < lx ? i : lx - 1) : NULL; + yi = j < lly ? jl_tparam(yd, j < ly ? j : ly - 1) : NULL; if (xi == NULL && yi == NULL) { assert(i == j && i == np); break; } - if (xi && jl_is_vararg(xi)) vx = 1; - if (yi && jl_is_vararg(yi)) vy = 1; + if (xi && jl_is_vararg(xi)) vx = vvx != JL_VARARG_INT; + if (yi && jl_is_vararg(yi)) vy = vvy != JL_VARARG_INT; if (xi == NULL || yi == NULL) { - if (vx && intersect_vararg_length(xi, ly+1-lx, e, 0)) { + if (vx && intersect_vararg_length(xi, lly+1-llx, e, 0)) { np = j; p = NULL; } - else if (vy && intersect_vararg_length(yi, lx+1-ly, e, 1)) { + else if (vy && intersect_vararg_length(yi, llx+1-lly, e, 1)) { np = i; p = NULL; } @@ -3173,16 +3185,17 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten break; } jl_value_t *ii = NULL; - if (vx && vy) + if (vx && vy) { ii = intersect_varargs((jl_vararg_t*)xi, (jl_vararg_t*)yi, - ly - lx, // xi's offset: {A^n...,Vararg{T,N}} ∩ {Vararg{S,M}} + lly - llx, // xi's offset: {A^n...,Vararg{T,N}} ∩ {Vararg{S,M}} // {(A∩S)^n...,Vararg{T∩S,N}} plus N = M-n e, param); + } else { - ii = intersect(vx ? jl_unwrap_vararg(xi) : xi, - vy ? jl_unwrap_vararg(yi) : yi, + ii = intersect(jl_is_vararg(xi) ? jl_unwrap_vararg(xi) : xi, + jl_is_vararg(yi) ? jl_unwrap_vararg(yi) : yi, e, param == 0 ? 1 : param); } @@ -3190,20 +3203,20 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten if (vx && vy) { jl_varbinding_t *xb=NULL, *yb=NULL; jl_value_t *xlen = jl_unwrap_vararg_num(xi); - if (xlen && jl_is_typevar(xlen)) - xb = lookup(e, (jl_tvar_t*)xlen); + assert(xlen == NULL || jl_is_typevar(xlen)); + if (xlen) xb = lookup(e, (jl_tvar_t*)xlen); jl_value_t *ylen = jl_unwrap_vararg_num(yi); - if (ylen && jl_is_typevar(ylen)) - yb = lookup(e, (jl_tvar_t*)ylen); + assert(ylen == NULL || jl_is_typevar(ylen)); + if (ylen) yb = lookup(e, (jl_tvar_t*)ylen); int len = i > j ? i : j; - if ((xb && jl_is_long(xb->lb) && lx-1+jl_unbox_long(xb->lb) != len) || - (yb && jl_is_long(yb->lb) && ly-1+jl_unbox_long(yb->lb) != len)) { + if ((xb && jl_is_long(xb->lb) && llx-1+jl_unbox_long(xb->lb) != len) || + (yb && jl_is_long(yb->lb) && lly-1+jl_unbox_long(yb->lb) != len)) { res = jl_bottom_type; } else { assert(e->Loffset == 0); - if (xb) set_var_to_const(xb, jl_box_long(len-lx+1), e, 0); - if (yb) set_var_to_const(yb, jl_box_long(len-ly+1), e, 1); + if (xb) set_var_to_const(xb, jl_box_long(len-llx+1), e, 0); + if (yb) set_var_to_const(yb, jl_box_long(len-lly+1), e, 1); np = len; p = NULL; } @@ -3221,8 +3234,8 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten params[i > j ? i : j] = ii; if (vx && vy) break; - if (i < lx-1 || !vx) i++; - if (j < ly-1 || !vy) j++; + if (!vx) i++; + if (!vy) j++; } // TODO: handle Vararg with explicit integer length parameter if (res == NULL) { diff --git a/test/subtype.jl b/test/subtype.jl index b38588155ef64..05ff82106bda0 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2235,6 +2235,17 @@ let A = Pair{NTuple{N, Int}, Val{N}} where N, @testintersect A C C end +# issue #49484 +let S = Tuple{Integer, U} where {II<:Array, U<:Tuple{Vararg{II, 1}}} + T = Tuple{Int, U} where {II<:Array, U<:Tuple{Vararg{II, 1}}} + @testintersect(S, Tuple{Int, U} where {U<:Tuple{Vararg{Any}}}, T) + @testintersect(S, Tuple{Int, U} where {N, U<:Tuple{Vararg{Any,N}}}, T) + @testintersect(S, Tuple{Int, U} where {U<:Tuple{Any,Vararg{Any}}}, T) + @testintersect(S, Tuple{Int, U} where {N, U<:Tuple{Any,Vararg{Any,N}}}, T) + @testintersect(S, Tuple{Int, U} where {U<:Tuple{Any,Any,Vararg{Any}}}, Union{}) + @testintersect(S, Tuple{Int, U} where {N, U<:Tuple{Any,Any,Vararg{Any,N}}}, Union{}) +end + # issue #43064 let env_tuple(@nospecialize(x), @nospecialize(y)) = (intersection_env(x, y)[2]...,) From 3b993a958900d7f3b1aa064f2bf9e917edae0f79 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 27 Apr 2023 09:00:57 +0900 Subject: [PATCH 600/775] AbstractInterpreter: account for overlay possibility in unanalyzed call (#49518) When we skip inference of a call on `throw` block, previously we only checked if the call is overlayed or not. But the call may call an overlayed method internally, thus we need to conservatively taint the `:nonoverlayed` bit when `interp` uses overlay method table. Nevertheless this will not introduce any regressions on GPUCompiler stack (like #48097), since it defines `InferenceParams(::GPUInterpreter)` overload to turn off `unoptimize_throw_blocks` --- base/compiler/abstractinterpretation.jl | 15 +-------------- test/compiler/AbstractInterpreter.jl | 1 + 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index e817e0bd927fe..451cbed407c35 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -36,23 +36,10 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), ⊑ₚ = ⊑(ipo_lattice(interp)) if !should_infer_this_call(interp, sv) add_remark!(interp, sv, "Skipped call in throw block") - nonoverlayed = false - if isoverlayed(method_table(interp)) && is_nonoverlayed(sv.ipo_effects) - # as we may want to concrete-evaluate this frame in cases when there are - # no overlayed calls, try an additional effort now to check if this call - # isn't overlayed rather than just handling it conservatively - matches = find_matching_methods(typeinf_lattice(interp), arginfo.argtypes, atype, method_table(interp), - InferenceParams(interp).max_union_splitting, max_methods) - if !isa(matches, FailedMethodMatch) - nonoverlayed = matches.nonoverlayed - end - else - nonoverlayed = true - end # At this point we are guaranteed to end up throwing on this path, # which is all that's required for :consistent-cy. Of course, we don't # know anything else about this statement. - effects = Effects(; consistent=ALWAYS_TRUE, nonoverlayed) + effects = Effects(; consistent=ALWAYS_TRUE, nonoverlayed=!isoverlayed(method_table(interp))) return CallMeta(Any, effects, NoCallInfo()) end diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index ffd7117cd99be..2a2502a003ac5 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -112,6 +112,7 @@ end |> only === Nothing @newinterp Issue48097Interp @MethodTable Issue48097MT CC.method_table(interp::Issue48097Interp) = CC.OverlayMethodTable(CC.get_world_counter(interp), Issue48097MT) +CC.InferenceParams(::Issue48097Interp) = CC.InferenceParams(; unoptimize_throw_blocks=false) @overlay Issue48097MT @noinline Core.throw_inexacterror(f::Symbol, ::Type{T}, val) where {T} = return issue48097(; kwargs...) = return 42 @test fully_eliminated(; interp=Issue48097Interp(), retval=42) do From bc2fa503a8875c24cd01b611d779ef8ce19adba2 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 27 Apr 2023 07:52:23 +0200 Subject: [PATCH 601/775] show line info + module in `ADD_METHOD` profiling (#49495) --- src/gf.c | 2 +- src/timing.c | 9 +++++++++ src/timing.h | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/gf.c b/src/gf.c index b7d4dd70dc8a4..caadb45f1b262 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1986,7 +1986,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method JL_TIMING(ADD_METHOD, ADD_METHOD); assert(jl_is_method(method)); assert(jl_is_mtable(mt)); - jl_timing_show((jl_value_t *)method, JL_TIMING_CURRENT_BLOCK); + jl_timing_show_method(method, JL_TIMING_CURRENT_BLOCK); jl_value_t *type = method->sig; jl_value_t *oldvalue = NULL; jl_array_t *oldmi = NULL; diff --git a/src/timing.c b/src/timing.c index 006e9b5dced15..d5fc9d784b78c 100644 --- a/src/timing.c +++ b/src/timing.c @@ -190,6 +190,15 @@ JL_DLLEXPORT void jl_timing_show_method_instance(jl_method_instance_t *mi, jl_ti jl_symbol_name(def->module->name)); } +JL_DLLEXPORT void jl_timing_show_method(jl_method_t *method, jl_timing_block_t *cur_block) +{ + jl_timing_show((jl_value_t *)method, cur_block); + jl_timing_printf(cur_block, "%s:%d in %s", + gnu_basename(jl_symbol_name(method->file)), + method->line, + jl_symbol_name(method->module->name)); +} + JL_DLLEXPORT void jl_timing_show_func_sig(jl_value_t *v, jl_timing_block_t *cur_block) { #ifdef USE_TRACY diff --git a/src/timing.h b/src/timing.h index 4cdd32da8b195..c1ef09d1e8b4d 100644 --- a/src/timing.h +++ b/src/timing.h @@ -63,6 +63,7 @@ extern uint32_t jl_timing_print_limit; #define jl_timing_show_module(m, b) #define jl_timing_show_filename(f, b) #define jl_timing_show_method_instance(mi, b) +#define jl_timing_show_method(mi, b) #define jl_timing_show_func_sig(tt, b) #define jl_timing_printf(s, f, ...) #define jl_timing_block_enter_task(ct, ptls, blk) @@ -102,6 +103,7 @@ void jl_timing_show(jl_value_t *v, jl_timing_block_t *cur_block); void jl_timing_show_module(jl_module_t *m, jl_timing_block_t *cur_block); void jl_timing_show_filename(const char *path, jl_timing_block_t *cur_block); void jl_timing_show_method_instance(jl_method_instance_t *mi, jl_timing_block_t *cur_block); +void jl_timing_show_method(jl_method_t *method, jl_timing_block_t *cur_block); void jl_timing_show_func_sig(jl_value_t *v, jl_timing_block_t *cur_block); void jl_timing_printf(jl_timing_block_t *cur_block, const char *format, ...); #ifdef __cplusplus From 75e979bdaca5de21ff83b7306bc9b6ae8ed8a6f6 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki Date: Thu, 27 Apr 2023 22:18:29 +0900 Subject: [PATCH 602/775] fix `compilesig_invokes=false` inlining option Follows up #49404. Tests are added. --- base/compiler/ssair/inlining.jl | 2 +- test/compiler/inline.jl | 47 ++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 1c9f4454abbfe..83b69e4c82ff3 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -826,7 +826,7 @@ function compileable_specialization(mi::MethodInstance, effects::Effects, # If this caller does not want us to optimize calls to use their # declared compilesig, then it is also likely they would handle sparams # incorrectly if there were any unknown typevars, so we conservatively return nothing - if _any(t->isa(t, TypeVar), match.sparams) + if any(@nospecialize(t)->isa(t, TypeVar), mi.sparam_vals) return nothing end end diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 62ff1a1ee7b6b..bcdd8c2875393 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -4,7 +4,8 @@ using Test using Base.Meta using Core: ReturnNode -include(normpath(@__DIR__, "irutils.jl")) +include("irutils.jl") +include("newinterp.jl") """ Helper to walk the AST and call a function on every node. @@ -1990,3 +1991,47 @@ for run_finalizer_escape_test in (run_finalizer_escape_test1, run_finalizer_esca @test finalizer_escape == 3 end end + +# `compilesig_invokes` inlining option +@newinterp NoCompileSigInvokes +Core.Compiler.OptimizationParams(::NoCompileSigInvokes) = + Core.Compiler.OptimizationParams(; compilesig_invokes=false) +@noinline no_compile_sig_invokes(@nospecialize x) = (x !== Any && !Base.has_free_typevars(x)) +# test the single dispatch candidate case +let src = code_typed1((Type,)) do x + no_compile_sig_invokes(x) + end + @test count(src.code) do @nospecialize x + isinvoke(:no_compile_sig_invokes, x) && + (x.args[1]::MethodInstance).specTypes == Tuple{typeof(no_compile_sig_invokes),Any} + end == 1 +end +let src = code_typed1((Type,); interp=NoCompileSigInvokes()) do x + no_compile_sig_invokes(x) + end + @test count(src.code) do @nospecialize x + isinvoke(:no_compile_sig_invokes, x) && + (x.args[1]::MethodInstance).specTypes == Tuple{typeof(no_compile_sig_invokes),Type} + end == 1 +end +# test the union split case +let src = code_typed1((Union{DataType,UnionAll},)) do x + no_compile_sig_invokes(x) + end + @test count(src.code) do @nospecialize x + isinvoke(:no_compile_sig_invokes, x) && + (x.args[1]::MethodInstance).specTypes == Tuple{typeof(no_compile_sig_invokes),Any} + end == 2 +end +let src = code_typed1((Union{DataType,UnionAll},); interp=NoCompileSigInvokes()) do x + no_compile_sig_invokes(x) + end + @test count(src.code) do @nospecialize x + isinvoke(:no_compile_sig_invokes, x) && + (x.args[1]::MethodInstance).specTypes == Tuple{typeof(no_compile_sig_invokes),DataType} + end == 1 + @test count(src.code) do @nospecialize x + isinvoke(:no_compile_sig_invokes, x) && + (x.args[1]::MethodInstance).specTypes == Tuple{typeof(no_compile_sig_invokes),UnionAll} + end == 1 +end From bf7bd3f0ec98bb69fa462a904d2f6ee3c2efde31 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 27 Apr 2023 16:02:10 +0200 Subject: [PATCH 603/775] remove debug print from loading test (#49534) --- test/loading.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/loading.jl b/test/loading.jl index 13e46fc856990..e019dc95dab26 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1035,7 +1035,6 @@ end cmd = gen_extension_cmd(compile) cmd = addenv(cmd, "JULIA_LOAD_PATH" => proj) cmd = pipeline(cmd; stdout, stderr) - @show compile @test success(cmd) end From 527117ea045cf6a8e76a2b33a36b078ebc43a587 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Thu, 27 Apr 2023 10:05:40 -0400 Subject: [PATCH 604/775] Fix attributes and opaque pointer usage for LLVM15 (#49528) Co-authored-by: Gabriel Baraldi Co-authored-by: Prem Chintalapudi --- src/cgutils.cpp | 4 ++-- src/codegen.cpp | 25 ++++++++++++++++--------- src/llvm-remove-addrspaces.cpp | 4 ++-- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index a6d41b9e41214..380c6f7bc0be0 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -942,10 +942,10 @@ static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const const DataLayout &DL = jl_Module->getDataLayout(); auto srcty = cast(src->getType()); //TODO unsafe nonopaque pointer - auto srcel = srcty->getPointerElementType(); + auto srcel = srcty->getNonOpaquePointerElementType(); auto dstty = cast(dst->getType()); //TODO unsafe nonopaque pointer - auto dstel = dstty->getPointerElementType(); + auto dstel = dstty->getNonOpaquePointerElementType(); while (srcel->isArrayTy() && srcel->getArrayNumElements() == 1) { src = ctx.builder.CreateConstInBoundsGEP2_32(srcel, src, 0, 0); srcel = srcel->getArrayElementType(); diff --git a/src/codegen.cpp b/src/codegen.cpp index b50110a20a8fc..329c4b452a9dc 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -921,15 +921,20 @@ static const auto jl_alloc_obj_func = new JuliaFunction{ return FunctionType::get(T_prjlvalue, {T_ppjlvalue, T_size, T_prjlvalue}, false); }, - [](LLVMContext &C) { return AttributeList::get(C, - AttributeSet::get(C, makeArrayRef({Attribute::getWithAllocSizeArgs(C, 1, None)})), // returns %1 bytes - - Attributes(C, {Attribute::NoAlias, Attribute::NonNull, + [](LLVMContext &C) { + auto FnAttrs = AttrBuilder(C); + FnAttrs.addAllocSizeAttr(1, None); // returns %1 bytes #if JL_LLVM_VERSION >= 150000 - Attribute::get(C, Attribute::AllocKind, AllocFnKind::Alloc | AllocFnKind::Uninitialized | AllocFnKind::Aligned), + FnAttrs.addAllocKindAttr(AllocFnKind::Alloc | AllocFnKind::Uninitialized | AllocFnKind::Aligned); #endif - }), - None); }, + auto RetAttrs = AttrBuilder(C); + RetAttrs.addAttribute(Attribute::NoAlias); + RetAttrs.addAttribute(Attribute::NonNull); + return AttributeList::get(C, + AttributeSet::get(C, FnAttrs), + AttributeSet::get(C, RetAttrs), + None); + }, }; static const auto jl_newbits_func = new JuliaFunction<>{ XSTR(jl_new_bits), @@ -2349,7 +2354,11 @@ static void jl_init_function(Function *F, const Triple &TT) attr.addStackAlignmentAttr(16); } if (TT.isOSWindows() && TT.getArch() == Triple::x86_64) { +#if JL_LLVM_VERSION < 150000 attr.addAttribute(Attribute::UWTable); // force NeedsWinEH +#else + attr.addUWTableAttr(llvm::UWTableKind::Default); // force NeedsWinEH +#endif } if (jl_fpo_disabled(TT)) attr.addAttribute("frame-pointer", "all"); @@ -6312,8 +6321,6 @@ static Function* gen_cfun_wrapper( } else if (!type_is_ghost(sig.lrt)) { Type *prt = sig.prt; - if (sig.sret) - prt = sig.fargt_sig[0]->getContainedType(0); // sret is a PointerType bool issigned = jl_signed_type && jl_subtype(declrt, (jl_value_t*)jl_signed_type); Value *v = emit_unbox(ctx, sig.lrt, retval, retval.typ); r = llvm_type_rewrite(ctx, v, prt, issigned); diff --git a/src/llvm-remove-addrspaces.cpp b/src/llvm-remove-addrspaces.cpp index e263467ba600c..a005d3cfaa352 100644 --- a/src/llvm-remove-addrspaces.cpp +++ b/src/llvm-remove-addrspaces.cpp @@ -51,7 +51,7 @@ class AddrspaceRemoveTypeRemapper : public ValueMapTypeRemapper { else { //Remove once opaque pointer transition is complete DstTy = PointerType::get( - remapType(Ty->getPointerElementType()), + remapType(Ty->getNonOpaquePointerElementType()), ASRemapper(Ty->getAddressSpace())); } } @@ -161,7 +161,7 @@ class AddrspaceRemoveValueMaterializer : public ValueMaterializer { auto ptrty = cast(Src->getType()->getScalarType()); //Remove once opaque pointer transition is complete if (!ptrty->isOpaque()) { - Type *SrcTy = remapType(ptrty->getPointerElementType()); + Type *SrcTy = remapType(ptrty->getNonOpaquePointerElementType()); DstV = CE->getWithOperands(Ops, Ty, false, SrcTy); } } From d54566fa73155e68782926aa60f82c78c40721e3 Mon Sep 17 00:00:00 2001 From: cormullion Date: Thu, 27 Apr 2023 16:40:58 +0100 Subject: [PATCH 605/775] add dark and light images for README.md --- README.md | 10 +- doc/src/assets/julialogoheaderimage_dark.svg | 209 ++++++++++++++++++ doc/src/assets/julialogoheaderimage_light.svg | 209 ++++++++++++++++++ 3 files changed, 424 insertions(+), 4 deletions(-) create mode 100644 doc/src/assets/julialogoheaderimage_dark.svg create mode 100644 doc/src/assets/julialogoheaderimage_light.svg diff --git a/README.md b/README.md index f822f7b1a2364..b24e2131edb4e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ -

diff --git a/doc/src/assets/julialogoheaderimage_dark.svg b/doc/src/assets/julialogoheaderimage_dark.svg new file mode 100644 index 0000000000000..04e06d2665633 --- /dev/null +++ b/doc/src/assets/julialogoheaderimage_dark.svg @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/src/assets/julialogoheaderimage_light.svg b/doc/src/assets/julialogoheaderimage_light.svg new file mode 100644 index 0000000000000..892ca1bd08701 --- /dev/null +++ b/doc/src/assets/julialogoheaderimage_light.svg @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2f0fe5872056d187ec7c1cc132f3bf93c8b16f94 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 27 Apr 2023 18:09:24 +0200 Subject: [PATCH 606/775] add a root profile zone for julia initialization (#49536) --- src/init.c | 1 + src/timing.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/init.c b/src/init.c index b0039bfe3e311..b7f3ffb644b01 100644 --- a/src/init.c +++ b/src/init.c @@ -833,6 +833,7 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_task_t *ct) { + JL_TIMING(JULIA_INIT, JULIA_INIT); jl_resolve_sysimg_location(rel); // loads sysimg if available, and conditionally sets jl_options.cpu_target if (jl_options.image_file) diff --git a/src/timing.h b/src/timing.h index c1ef09d1e8b4d..b8b1d51c603f6 100644 --- a/src/timing.h +++ b/src/timing.h @@ -146,6 +146,7 @@ void jl_timing_printf(jl_timing_block_t *cur_block, const char *format, ...); X(LOCK_SPIN) \ X(STACKWALK) \ X(DL_OPEN) \ + X(JULIA_INIT) \ #define JL_TIMING_EVENTS \ From 09a0f34831a6fff5f9358b7034b7ef9073f3053e Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 27 Apr 2023 20:44:32 +0200 Subject: [PATCH 607/775] add two more explicit precompile statements (#49537) --- contrib/generate_precompile.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index c99e6c646ec1c..68650836fd6b4 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -45,6 +45,8 @@ precompile(Tuple{typeof(push!), Vector{Function}, Function}) # miscellaneous precompile(Tuple{typeof(Base.require), Base.PkgId}) precompile(Tuple{typeof(Base.recursive_prefs_merge), Base.Dict{String, Any}}) +precompile(Tuple{typeof(Base.hashindex), Tuple{Base.PkgId, Nothing}, Int64}) +precompile(Tuple{typeof(Base.hashindex), Tuple{Base.PkgId, String}, Int64}) precompile(Tuple{typeof(isassigned), Core.SimpleVector, Int}) precompile(Tuple{typeof(getindex), Core.SimpleVector, Int}) precompile(Tuple{typeof(Base.Experimental.register_error_hint), Any, Type}) From 959902f1c6099c1b513e29103b998545c16731fc Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Thu, 27 Apr 2023 16:27:09 -0400 Subject: [PATCH 608/775] Support both Float16 ABIs depending on LLVM and platform (#49527) There are two Float16 ABIs in the wild, one for platforms that have a defing register and the original one where we used i16. LLVM 15 follows GCC and uses the new ABI on x86/ARM but not PPC. Co-authored-by: Gabriel Baraldi --- src/aotcompile.cpp | 11 +++++++-- src/codegen.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++ src/jitlayers.cpp | 2 ++ src/llvm-version.h | 10 +++++++++ 4 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 391c5d3df46fb..2a14e2a4fa0ab 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -494,6 +494,7 @@ static void reportWriterError(const ErrorInfoBase &E) jl_safe_printf("ERROR: failed to emit output file %s\n", err.c_str()); } +#if JULIA_FLOAT16_ABI == 1 static void injectCRTAlias(Module &M, StringRef name, StringRef alias, FunctionType *FT) { Function *target = M.getFunction(alias); @@ -510,7 +511,7 @@ static void injectCRTAlias(Module &M, StringRef name, StringRef alias, FunctionT auto val = builder.CreateCall(target, CallArgs); builder.CreateRet(val); } - +#endif void multiversioning_preannotate(Module &M); // See src/processor.h for documentation about this table. Corresponds to jl_image_shard_t. @@ -943,6 +944,8 @@ struct ShardTimers { } }; +void emitFloat16Wrappers(Module &M, bool external); + // Perform the actual optimization and emission of the output files static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *outputs, const std::string *names, NewArchiveMember *unopt, NewArchiveMember *opt, NewArchiveMember *obj, NewArchiveMember *asm_, @@ -1003,7 +1006,9 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out } } // no need to inject aliases if we have no functions + if (inject_aliases) { +#if JULIA_FLOAT16_ABI == 1 // We would like to emit an alias or an weakref alias to redirect these symbols // but LLVM doesn't let us emit a GlobalAlias to a declaration... // So for now we inject a definition of these functions that calls our runtime @@ -1018,8 +1023,10 @@ static void add_output_impl(Module &M, TargetMachine &SourceTM, std::string *out FunctionType::get(Type::getHalfTy(M.getContext()), { Type::getFloatTy(M.getContext()) }, false)); injectCRTAlias(M, "__truncdfhf2", "julia__truncdfhf2", FunctionType::get(Type::getHalfTy(M.getContext()), { Type::getDoubleTy(M.getContext()) }, false)); +#else + emitFloat16Wrappers(M, false); +#endif } - timers.optimize.stopTimer(); if (opt) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 329c4b452a9dc..f4b0fd518cd39 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5818,6 +5818,7 @@ static void emit_cfunc_invalidate( prepare_call_in(gf_thunk->getParent(), jlapplygeneric_func)); } +#include static Function* gen_cfun_wrapper( Module *into, jl_codegen_params_t ¶ms, const function_sig_t &sig, jl_value_t *ff, const char *aliasname, @@ -8704,6 +8705,58 @@ static JuliaVariable *julia_const_gv(jl_value_t *val) return nullptr; } +// Handle FLOAT16 ABI v2 +#if JULIA_FLOAT16_ABI == 2 +static void makeCastCall(Module &M, StringRef wrapperName, StringRef calledName, FunctionType *FTwrapper, FunctionType *FTcalled, bool external) +{ + Function *calledFun = M.getFunction(calledName); + if (!calledFun) { + calledFun = Function::Create(FTcalled, Function::ExternalLinkage, calledName, M); + } + auto linkage = external ? Function::ExternalLinkage : Function::InternalLinkage; + auto wrapperFun = Function::Create(FTwrapper, linkage, wrapperName, M); + wrapperFun->addFnAttr(Attribute::AlwaysInline); + llvm::IRBuilder<> builder(BasicBlock::Create(M.getContext(), "top", wrapperFun)); + SmallVector CallArgs; + if (wrapperFun->arg_size() != calledFun->arg_size()){ + llvm::errs() << "FATAL ERROR: Can't match wrapper to called function"; + abort(); + } + for (auto wrapperArg = wrapperFun->arg_begin(), calledArg = calledFun->arg_begin(); + wrapperArg != wrapperFun->arg_end() && calledArg != calledFun->arg_end(); ++wrapperArg, ++calledArg) + { + CallArgs.push_back(builder.CreateBitCast(wrapperArg, calledArg->getType())); + } + auto val = builder.CreateCall(calledFun, CallArgs); + auto retval = builder.CreateBitCast(val,wrapperFun->getReturnType()); + builder.CreateRet(retval); +} + +void emitFloat16Wrappers(Module &M, bool external) +{ + auto &ctx = M.getContext(); + makeCastCall(M, "__gnu_h2f_ieee", "julia__gnu_h2f_ieee", FunctionType::get(Type::getFloatTy(ctx), { Type::getHalfTy(ctx) }, false), + FunctionType::get(Type::getFloatTy(ctx), { Type::getInt16Ty(ctx) }, false), external); + makeCastCall(M, "__extendhfsf2", "julia__gnu_h2f_ieee", FunctionType::get(Type::getFloatTy(ctx), { Type::getHalfTy(ctx) }, false), + FunctionType::get(Type::getFloatTy(ctx), { Type::getInt16Ty(ctx) }, false), external); + makeCastCall(M, "__gnu_f2h_ieee", "julia__gnu_f2h_ieee", FunctionType::get(Type::getHalfTy(ctx), { Type::getFloatTy(ctx) }, false), + FunctionType::get(Type::getInt16Ty(ctx), { Type::getFloatTy(ctx) }, false), external); + makeCastCall(M, "__truncsfhf2", "julia__gnu_f2h_ieee", FunctionType::get(Type::getHalfTy(ctx), { Type::getFloatTy(ctx) }, false), + FunctionType::get(Type::getInt16Ty(ctx), { Type::getFloatTy(ctx) }, false), external); + makeCastCall(M, "__truncdfhf2", "julia__truncdfhf2", FunctionType::get(Type::getHalfTy(ctx), { Type::getDoubleTy(ctx) }, false), + FunctionType::get(Type::getInt16Ty(ctx), { Type::getDoubleTy(ctx) }, false), external); +} + +static void init_f16_funcs(void) +{ + auto ctx = jl_ExecutionEngine->acquireContext(); + auto TSM = jl_create_ts_module("F16Wrappers", ctx, imaging_default()); + auto aliasM = TSM.getModuleUnlocked(); + emitFloat16Wrappers(*aliasM, true); + jl_ExecutionEngine->addModule(std::move(TSM)); +} +#endif + static void init_jit_functions(void) { add_named_global(jlstack_chk_guard_var, &__stack_chk_guard); @@ -8942,6 +8995,9 @@ extern "C" JL_DLLEXPORT void jl_init_codegen_impl(void) jl_init_llvm(); // Now that the execution engine exists, initialize all modules init_jit_functions(); +#if JULIA_FLOAT16_ABI == 2 + init_f16_funcs(); +#endif } extern "C" JL_DLLEXPORT void jl_teardown_codegen_impl() JL_NOTSAFEPOINT diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 37302e8ca2ace..b3ec102821858 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -1383,6 +1383,7 @@ JuliaOJIT::JuliaOJIT() JD.addToLinkOrder(GlobalJD, orc::JITDylibLookupFlags::MatchExportedSymbolsOnly); +#if JULIA_FLOAT16_ABI == 1 orc::SymbolAliasMap jl_crt = { { mangle("__gnu_h2f_ieee"), { mangle("julia__gnu_h2f_ieee"), JITSymbolFlags::Exported } }, { mangle("__extendhfsf2"), { mangle("julia__gnu_h2f_ieee"), JITSymbolFlags::Exported } }, @@ -1391,6 +1392,7 @@ JuliaOJIT::JuliaOJIT() { mangle("__truncdfhf2"), { mangle("julia__truncdfhf2"), JITSymbolFlags::Exported } } }; cantFail(GlobalJD.define(orc::symbolAliases(jl_crt))); +#endif #ifdef MSAN_EMUTLS_WORKAROUND orc::SymbolMap msan_crt; diff --git a/src/llvm-version.h b/src/llvm-version.h index 4e15e787b7de8..a3f3774b6dc15 100644 --- a/src/llvm-version.h +++ b/src/llvm-version.h @@ -2,6 +2,7 @@ #include #include "julia_assert.h" +#include "platform.h" // The LLVM version used, JL_LLVM_VERSION, is represented as a 5-digit integer // of the form ABBCC, where A is the major version, B is minor, and C is patch. @@ -17,6 +18,15 @@ #define JL_LLVM_OPAQUE_POINTERS 1 #endif +// Pre GCC 12 libgcc defined the ABI for Float16->Float32 +// to take an i16. GCC 12 silently changed the ABI to now pass +// Float16 in Float32 registers. +#if JL_LLVM_VERSION < 150000 || defined(_CPU_PPC64_) || defined(_CPU_PPC_) +#define JULIA_FLOAT16_ABI 1 +#else +#define JULIA_FLOAT16_ABI 2 +#endif + #ifdef __cplusplus #if defined(__GNUC__) && (__GNUC__ >= 9) // Added in GCC 9, this warning is annoying From f11bfc6ccad3e07fde4e40493635bd832d108477 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Thu, 27 Apr 2023 16:29:25 -0400 Subject: [PATCH 609/775] Use NewPM for ASAN/MSAN (#49530) Co-authored-by: Gabriel Baraldi Co-authored-by: Prem Chintalapudi --- src/aotcompile.cpp | 4 ++++ src/cgmemmgr.cpp | 4 ++-- src/jitlayers.h | 13 +++++++++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 2a14e2a4fa0ab..b89cdf550171f 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1775,6 +1775,7 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level, PM->add(createCFGSimplificationPass(basicSimplifyCFGOptions)); } } +#if JL_LLVM_VERSION < 150000 #if defined(_COMPILER_ASAN_ENABLED_) PM->add(createAddressSanitizerFunctionPass()); #endif @@ -1783,6 +1784,7 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level, #endif #if defined(_COMPILER_TSAN_ENABLED_) PM->add(createThreadSanitizerLegacyPassPass()); +#endif #endif return; } @@ -1934,6 +1936,7 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level, } PM->add(createCombineMulAddPass()); PM->add(createDivRemPairsPass()); +#if JL_LLVM_VERSION < 150000 #if defined(_COMPILER_ASAN_ENABLED_) PM->add(createAddressSanitizerFunctionPass()); #endif @@ -1943,6 +1946,7 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level, #if defined(_COMPILER_TSAN_ENABLED_) PM->add(createThreadSanitizerLegacyPassPass()); #endif +#endif } // An LLVM module pass that just runs all julia passes in order. Useful for diff --git a/src/cgmemmgr.cpp b/src/cgmemmgr.cpp index 9f4d69137c0fd..15d28ff270c55 100644 --- a/src/cgmemmgr.cpp +++ b/src/cgmemmgr.cpp @@ -860,8 +860,8 @@ uint8_t *RTDyldMemoryManagerJL::allocateCodeSection(uintptr_t Size, StringRef SectionName) { // allocating more than one code section can confuse libunwind. -#if !defined(_COMPILER_MSAN_ENABLED_) - // TODO: Figure out why msan needs this. +#if !defined(_COMPILER_MSAN_ENABLED_) && !defined(_COMPILER_ASAN_ENABLED_) + // TODO: Figure out why msan and now asan too need this. assert(!code_allocated); code_allocated = true; #endif diff --git a/src/jitlayers.h b/src/jitlayers.h index 7f07034586c80..f63f3a42842f1 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -42,7 +42,14 @@ // and feature support (e.g. Windows, JITEventListeners for various profilers, // etc.). Thus, we currently only use JITLink where absolutely required, that is, // for Mac/aarch64. -#if defined(_OS_DARWIN_) && defined(_CPU_AARCH64_) || defined(_COMPILER_ASAN_ENABLED_) || defined(JL_FORCE_JITLINK) +// #define JL_FORCE_JITLINK + +#if defined(_COMPILER_ASAN_ENABLED_) || defined(_COMPILER_MSAN_ENABLED_) || defined(_COMPILER_TSAN_ENABLED_) +# define HAS_SANITIZER +#endif +// The sanitizers don't play well with our memory manager + +#if defined(_OS_DARWIN_) && defined(_CPU_AARCH64_) || defined(JL_FORCE_JITLINK) || JL_LLVM_VERSION >= 150000 && defined(HAS_SANITIZER) # if JL_LLVM_VERSION < 130000 # pragma message("On aarch64-darwin, LLVM version >= 13 is required for JITLink; fallback suffers from occasional segfaults") # endif @@ -93,7 +100,9 @@ struct OptimizationOptions { // for middle-end IR optimizations. However, we have not qualified the new // pass manager on our optimization pipeline yet, so this remains an optional // define -// #define JL_USE_NEW_PM +#if defined(HAS_SANITIZER) && JL_LLVM_VERSION >= 150000 +#define JL_USE_NEW_PM +#endif struct NewPM { std::unique_ptr TM; From 1dcac5eb40a14a151076086f98ce61264541957f Mon Sep 17 00:00:00 2001 From: Christian Guinard Date: Fri, 28 Apr 2023 01:00:05 -0300 Subject: [PATCH 610/775] Update current stable version in README.md (#49543) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b24e2131edb4e..a95ee05b2bab2 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ and then use the command prompt to change into the resulting julia directory. By Julia. However, most users should use the [most recent stable version](https://github.com/JuliaLang/julia/releases) of Julia. You can get this version by running: - git checkout v1.8.4 + git checkout v1.8.5 To build the `julia` executable, run `make` from within the julia directory. From ced696de381bb6c2648f0fb5da3dcd432e765cd2 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 28 Apr 2023 12:32:59 +0200 Subject: [PATCH 611/775] color compilation with red in tracy during recompilation (#49524) --- src/jitlayers.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index b3ec102821858..ef4cc9ac95aa6 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -178,7 +178,8 @@ static jl_callptr_t _jl_compile_codeinst( jl_code_instance_t *codeinst, jl_code_info_t *src, size_t world, - orc::ThreadSafeContext context) + orc::ThreadSafeContext context, + bool is_recompile) { // caller must hold codegen_lock // and have disabled finalizers @@ -193,7 +194,12 @@ static jl_callptr_t _jl_compile_codeinst( assert(src && jl_is_code_info(src)); JL_TIMING(CODEINST_COMPILE, CODEINST_COMPILE); - +#ifdef USE_TRACY + if (is_recompile) { + TracyCZoneCtx ctx = *(JL_TIMING_CURRENT_BLOCK->tracy_ctx); + TracyCZoneColor(ctx, 0xFFA500); + } +#endif jl_callptr_t fptr = NULL; // emit the code in LLVM IR form jl_codegen_params_t params(std::move(context), jl_ExecutionEngine->getDataLayout(), jl_ExecutionEngine->getTargetTriple()); // Locks the context @@ -475,7 +481,7 @@ jl_code_instance_t *jl_generate_fptr_impl(jl_method_instance_t *mi JL_PROPAGATES } } ++SpecFPtrCount; - _jl_compile_codeinst(codeinst, src, world, *jl_ExecutionEngine->getContext()); + _jl_compile_codeinst(codeinst, src, world, *jl_ExecutionEngine->getContext(), is_recompile); if (jl_atomic_load_relaxed(&codeinst->invoke) == NULL) codeinst = NULL; } @@ -532,7 +538,7 @@ void jl_generate_fptr_for_unspecialized_impl(jl_code_instance_t *unspec) } assert(src && jl_is_code_info(src)); ++UnspecFPtrCount; - _jl_compile_codeinst(unspec, src, unspec->min_world, *jl_ExecutionEngine->getContext()); + _jl_compile_codeinst(unspec, src, unspec->min_world, *jl_ExecutionEngine->getContext(), 0); jl_callptr_t null = nullptr; // if we hit a codegen bug (or ran into a broken generated function or llvmcall), fall back to the interpreter as a last resort jl_atomic_cmpswap(&unspec->invoke, &null, jl_fptr_interpret_call_addr); @@ -591,7 +597,7 @@ jl_value_t *jl_dump_method_asm_impl(jl_method_instance_t *mi, size_t world, specfptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->specptr.fptr); if (src && jl_is_code_info(src)) { if (fptr == (uintptr_t)jl_fptr_const_return_addr && specfptr == 0) { - fptr = (uintptr_t)_jl_compile_codeinst(codeinst, src, world, *jl_ExecutionEngine->getContext()); + fptr = (uintptr_t)_jl_compile_codeinst(codeinst, src, world, *jl_ExecutionEngine->getContext(), 0); specfptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->specptr.fptr); } } From ee7538e368bc33f08902bec26bf0b6b37c81d428 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 28 Apr 2023 12:48:25 +0200 Subject: [PATCH 612/775] annotate global non-const variables in bundled jlls with types (#49542) --- .../src/CompilerSupportLibraries_jll.jl | 14 +++--- stdlib/GMP_jll/src/GMP_jll.jl | 10 ++-- stdlib/LLD_jll/src/LLD_jll.jl | 4 +- .../src/LLVMLibUnwind_jll.jl | 6 +-- stdlib/LibCURL_jll/src/LibCURL_jll.jl | 6 +-- stdlib/LibGit2_jll/src/LibGit2_jll.jl | 6 +-- stdlib/LibSSH2_jll/src/LibSSH2_jll.jl | 6 +-- stdlib/LibUV_jll/src/LibUV_jll.jl | 6 +-- stdlib/LibUnwind_jll/src/LibUnwind_jll.jl | 6 +-- stdlib/MPFR_jll/src/MPFR_jll.jl | 6 +-- stdlib/MbedTLS_jll/src/MbedTLS_jll.jl | 14 +++--- stdlib/OpenBLAS_jll/src/OpenBLAS_jll.jl | 6 +-- stdlib/OpenLibm_jll/src/OpenLibm_jll.jl | 6 +-- stdlib/PCRE2_jll/src/PCRE2_jll.jl | 6 +-- stdlib/SuiteSparse_jll/src/SuiteSparse_jll.jl | 50 +++++++++---------- stdlib/Zlib_jll/src/Zlib_jll.jl | 6 +-- stdlib/dSFMT_jll/src/dSFMT_jll.jl | 6 +-- stdlib/libLLVM_jll/src/libLLVM_jll.jl | 6 +-- .../src/libblastrampoline_jll.jl | 6 +-- stdlib/nghttp2_jll/src/nghttp2_jll.jl | 6 +-- stdlib/p7zip_jll/src/p7zip_jll.jl | 4 +- 21 files changed, 93 insertions(+), 93 deletions(-) diff --git a/stdlib/CompilerSupportLibraries_jll/src/CompilerSupportLibraries_jll.jl b/stdlib/CompilerSupportLibraries_jll/src/CompilerSupportLibraries_jll.jl index 097659e01b396..bd7a0571f9d5a 100644 --- a/stdlib/CompilerSupportLibraries_jll/src/CompilerSupportLibraries_jll.jl +++ b/stdlib/CompilerSupportLibraries_jll/src/CompilerSupportLibraries_jll.jl @@ -14,13 +14,13 @@ export libgfortran, libstdcxx, libgomp # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libgfortran_handle = C_NULL -libgfortran_path = "" -libstdcxx_handle = C_NULL -libstdcxx_path = "" -libgomp_handle = C_NULL -libgomp_path = "" +artifact_dir::String = "" +libgfortran_handle::Ptr{Cvoid} = C_NULL +libgfortran_path::String = "" +libstdcxx_handle::Ptr{Cvoid} = C_NULL +libstdcxx_path::String = "" +libgomp_handle::Ptr{Cvoid} = C_NULL +libgomp_path::String = "" if Sys.iswindows() if arch(HostPlatform()) == "x86_64" diff --git a/stdlib/GMP_jll/src/GMP_jll.jl b/stdlib/GMP_jll/src/GMP_jll.jl index 90daa24b150ed..fde2fc15acf90 100644 --- a/stdlib/GMP_jll/src/GMP_jll.jl +++ b/stdlib/GMP_jll/src/GMP_jll.jl @@ -13,11 +13,11 @@ export libgmp, libgmpxx # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libgmp_handle = C_NULL -libgmp_path = "" -libgmpxx_handle = C_NULL -libgmpxx_path = "" +artifact_dir::String = "" +libgmp_handle::Ptr{Cvoid} = C_NULL +libgmp_path::String = "" +libgmpxx_handle::Ptr{Cvoid} = C_NULL +libgmpxx_path::String = "" if Sys.iswindows() const libgmp = "libgmp-10.dll" diff --git a/stdlib/LLD_jll/src/LLD_jll.jl b/stdlib/LLD_jll/src/LLD_jll.jl index a59d8deb8c7b5..55ccec9cc4005 100644 --- a/stdlib/LLD_jll/src/LLD_jll.jl +++ b/stdlib/LLD_jll/src/LLD_jll.jl @@ -14,8 +14,8 @@ export lld # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -lld_path = "" +artifact_dir::String = "" +lld_path::String = "" if Sys.iswindows() const lld_exe = "lld.exe" else diff --git a/stdlib/LLVMLibUnwind_jll/src/LLVMLibUnwind_jll.jl b/stdlib/LLVMLibUnwind_jll/src/LLVMLibUnwind_jll.jl index 2196323ad35aa..5c4026291a673 100644 --- a/stdlib/LLVMLibUnwind_jll/src/LLVMLibUnwind_jll.jl +++ b/stdlib/LLVMLibUnwind_jll/src/LLVMLibUnwind_jll.jl @@ -14,9 +14,9 @@ export llvmlibunwind # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -llvmlibunwind_handle = C_NULL -llvmlibunwind_path = "" +artifact_dir::String = "" +llvmlibunwind_handle::Ptr{Cvoid} = C_NULL +llvmlibunwind_path::String = "" const llvmlibunwind = "libunwind" diff --git a/stdlib/LibCURL_jll/src/LibCURL_jll.jl b/stdlib/LibCURL_jll/src/LibCURL_jll.jl index 9d019bb784584..cd67bfac0006a 100644 --- a/stdlib/LibCURL_jll/src/LibCURL_jll.jl +++ b/stdlib/LibCURL_jll/src/LibCURL_jll.jl @@ -14,9 +14,9 @@ export libcurl # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libcurl_handle = C_NULL -libcurl_path = "" +artifact_dir::String = "" +libcurl_handle::Ptr{Cvoid} = C_NULL +libcurl_path::String = "" if Sys.iswindows() const libcurl = "libcurl-4.dll" diff --git a/stdlib/LibGit2_jll/src/LibGit2_jll.jl b/stdlib/LibGit2_jll/src/LibGit2_jll.jl index 29d12fc5343e1..f8e814f1f7c30 100644 --- a/stdlib/LibGit2_jll/src/LibGit2_jll.jl +++ b/stdlib/LibGit2_jll/src/LibGit2_jll.jl @@ -14,9 +14,9 @@ export libgit2 # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libgit2_handle = C_NULL -libgit2_path = "" +artifact_dir::String = "" +libgit2_handle::Ptr{Cvoid} = C_NULL +libgit2_path::String = "" if Sys.iswindows() const libgit2 = "libgit2.dll" diff --git a/stdlib/LibSSH2_jll/src/LibSSH2_jll.jl b/stdlib/LibSSH2_jll/src/LibSSH2_jll.jl index 66987b30d090c..a809f7a912d6b 100644 --- a/stdlib/LibSSH2_jll/src/LibSSH2_jll.jl +++ b/stdlib/LibSSH2_jll/src/LibSSH2_jll.jl @@ -14,9 +14,9 @@ export libssh2 # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libssh2_handle = C_NULL -libssh2_path = "" +artifact_dir::String = "" +libssh2_handle::Ptr{Cvoid} = C_NULL +libssh2_path::String = "" if Sys.iswindows() const libssh2 = "libssh2.dll" diff --git a/stdlib/LibUV_jll/src/LibUV_jll.jl b/stdlib/LibUV_jll/src/LibUV_jll.jl index e4897138cc6cc..f6714fae536e9 100644 --- a/stdlib/LibUV_jll/src/LibUV_jll.jl +++ b/stdlib/LibUV_jll/src/LibUV_jll.jl @@ -14,9 +14,9 @@ export libuv # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libuv_handle = C_NULL -libuv_path = "" +artifact_dir::String = "" +libuv_handle::Ptr{Cvoid} = C_NULL +libuv_path::String = "" if Sys.iswindows() const libuv = "libuv-2.dll" diff --git a/stdlib/LibUnwind_jll/src/LibUnwind_jll.jl b/stdlib/LibUnwind_jll/src/LibUnwind_jll.jl index ae79e790a999b..12abeaf598151 100644 --- a/stdlib/LibUnwind_jll/src/LibUnwind_jll.jl +++ b/stdlib/LibUnwind_jll/src/LibUnwind_jll.jl @@ -14,9 +14,9 @@ export libunwind # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libunwind_handle = C_NULL -libunwind_path = "" +artifact_dir::String = "" +libunwind_handle::Ptr{Cvoid} = C_NULL +libunwind_path::String = "" const libunwind = "libunwind.so.8" diff --git a/stdlib/MPFR_jll/src/MPFR_jll.jl b/stdlib/MPFR_jll/src/MPFR_jll.jl index 5b2dbd1e84b24..c184a9801102f 100644 --- a/stdlib/MPFR_jll/src/MPFR_jll.jl +++ b/stdlib/MPFR_jll/src/MPFR_jll.jl @@ -13,9 +13,9 @@ export libmpfr # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libmpfr_handle = C_NULL -libmpfr_path = "" +artifact_dir::String = "" +libmpfr_handle::Ptr{Cvoid} = C_NULL +libmpfr_path::String = "" if Sys.iswindows() const libmpfr = "libmpfr-6.dll" diff --git a/stdlib/MbedTLS_jll/src/MbedTLS_jll.jl b/stdlib/MbedTLS_jll/src/MbedTLS_jll.jl index 338bec9503c07..e46da42a9a638 100644 --- a/stdlib/MbedTLS_jll/src/MbedTLS_jll.jl +++ b/stdlib/MbedTLS_jll/src/MbedTLS_jll.jl @@ -14,13 +14,13 @@ export libmbedcrypto, libmbedtls, libmbedx509 # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libmbedcrypto_handle = C_NULL -libmbedcrypto_path = "" -libmbedtls_handle = C_NULL -libmbedtls_path = "" -libmbedx509_handle = C_NULL -libmbedx509_path = "" +artifact_dir::String = "" +libmbedcrypto_handle::Ptr{Cvoid} = C_NULL +libmbedcrypto_path::String = "" +libmbedtls_handle::Ptr{Cvoid} = C_NULL +libmbedtls_path::String = "" +libmbedx509_handle::Ptr{Cvoid} = C_NULL +libmbedx509_path::String = "" if Sys.iswindows() const libmbedcrypto = "libmbedcrypto.dll" diff --git a/stdlib/OpenBLAS_jll/src/OpenBLAS_jll.jl b/stdlib/OpenBLAS_jll/src/OpenBLAS_jll.jl index 2684a6b635cb4..4f1c57a7d06be 100644 --- a/stdlib/OpenBLAS_jll/src/OpenBLAS_jll.jl +++ b/stdlib/OpenBLAS_jll/src/OpenBLAS_jll.jl @@ -13,9 +13,9 @@ export libopenblas # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libopenblas_handle = C_NULL -libopenblas_path = "" +artifact_dir::String = "" +libopenblas_handle::Ptr{Cvoid} = C_NULL +libopenblas_path::String = "" if Base.USE_BLAS64 const libsuffix = "64_" diff --git a/stdlib/OpenLibm_jll/src/OpenLibm_jll.jl b/stdlib/OpenLibm_jll/src/OpenLibm_jll.jl index e3536021ad4c9..f2dee45a279cd 100644 --- a/stdlib/OpenLibm_jll/src/OpenLibm_jll.jl +++ b/stdlib/OpenLibm_jll/src/OpenLibm_jll.jl @@ -13,9 +13,9 @@ export libopenlibm # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libopenlibm_handle = C_NULL -libopenlibm_path = "" +artifact_dir::String = "" +libopenlibm_handle::Ptr{Cvoid} = C_NULL +libopenlibm_path::String = "" if Sys.iswindows() const libopenlibm = "libopenlibm.dll" diff --git a/stdlib/PCRE2_jll/src/PCRE2_jll.jl b/stdlib/PCRE2_jll/src/PCRE2_jll.jl index 81048a45998b5..e7f685820830b 100644 --- a/stdlib/PCRE2_jll/src/PCRE2_jll.jl +++ b/stdlib/PCRE2_jll/src/PCRE2_jll.jl @@ -13,9 +13,9 @@ export libpcre2_8 # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libpcre2_8_handle = C_NULL -libpcre2_8_path = "" +artifact_dir::String = "" +libpcre2_8_handle::Ptr{Cvoid} = C_NULL +libpcre2_8_path::String = "" if Sys.iswindows() const libpcre2_8 = "libpcre2-8-0.dll" diff --git a/stdlib/SuiteSparse_jll/src/SuiteSparse_jll.jl b/stdlib/SuiteSparse_jll/src/SuiteSparse_jll.jl index 2940970ceff9f..6b87d417fc2a8 100644 --- a/stdlib/SuiteSparse_jll/src/SuiteSparse_jll.jl +++ b/stdlib/SuiteSparse_jll/src/SuiteSparse_jll.jl @@ -14,31 +14,31 @@ export libamd, libbtf, libcamd, libccolamd, libcholmod, libcolamd, libklu, libld # Man I can't wait until these are automatically handled by an in-Base JLLWrappers clone. const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libamd_handle = C_NULL -libamd_path = "" -libbtf_handle = C_NULL -libbtf_path = "" -libcamd_handle = C_NULL -libcamd_path = "" -libccolamd_handle = C_NULL -libccolamd_path = "" -libcholmod_handle = C_NULL -libcholmod_path = "" -libcolamd_handle = C_NULL -libcolamd_path = "" -libklu_handle = C_NULL -libklu_path = "" -libldl_handle = C_NULL -libldl_path = "" -librbio_handle = C_NULL -librbio_path = "" -libspqr_handle = C_NULL -libspqr_path = "" -libsuitesparseconfig_handle = C_NULL -libsuitesparseconfig_path = "" -libumfpack_handle = C_NULL -libumfpack_path = "" +artifact_dir::String = "" +libamd_handle::Ptr{Cvoid} = C_NULL +libamd_path::String = "" +libbtf_handle::Ptr{Cvoid} = C_NULL +libbtf_path::String = "" +libcamd_handle::Ptr{Cvoid} = C_NULL +libcamd_path::String = "" +libccolamd_handle::Ptr{Cvoid} = C_NULL +libccolamd_path::String = "" +libcholmod_handle::Ptr{Cvoid} = C_NULL +libcholmod_path::String = "" +libcolamd_handle::Ptr{Cvoid} = C_NULL +libcolamd_path::String = "" +libklu_handle::Ptr{Cvoid} = C_NULL +libklu_path::String = "" +libldl_handle::Ptr{Cvoid} = C_NULL +libldl_path::String = "" +librbio_handle::Ptr{Cvoid} = C_NULL +librbio_path::String = "" +libspqr_handle::Ptr{Cvoid} = C_NULL +libspqr_path::String = "" +libsuitesparseconfig_handle::Ptr{Cvoid} = C_NULL +libsuitesparseconfig_path::String = "" +libumfpack_handle::Ptr{Cvoid} = C_NULL +libumfpack_path::String = "" if Sys.iswindows() const libamd = "libamd.dll" diff --git a/stdlib/Zlib_jll/src/Zlib_jll.jl b/stdlib/Zlib_jll/src/Zlib_jll.jl index c05e26c4c6993..ea381b8b0683c 100644 --- a/stdlib/Zlib_jll/src/Zlib_jll.jl +++ b/stdlib/Zlib_jll/src/Zlib_jll.jl @@ -13,9 +13,9 @@ export libz # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libz_handle = C_NULL -libz_path = "" +artifact_dir::String = "" +libz_handle::Ptr{Cvoid} = C_NULL +libz_path::String = "" if Sys.iswindows() const libz = "libz.dll" diff --git a/stdlib/dSFMT_jll/src/dSFMT_jll.jl b/stdlib/dSFMT_jll/src/dSFMT_jll.jl index f1d6d019faf59..35ada23778a94 100644 --- a/stdlib/dSFMT_jll/src/dSFMT_jll.jl +++ b/stdlib/dSFMT_jll/src/dSFMT_jll.jl @@ -14,9 +14,9 @@ export libdSFMT # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libdSFMT_handle = C_NULL -libdSFMT_path = "" +artifact_dir::String = "" +libdSFMT_handle::Ptr{Cvoid} = C_NULL +libdSFMT_path::String = "" if Sys.iswindows() const libdSFMT = "libdSFMT.dll" diff --git a/stdlib/libLLVM_jll/src/libLLVM_jll.jl b/stdlib/libLLVM_jll/src/libLLVM_jll.jl index bd92890acb7c3..3140dc3989a72 100644 --- a/stdlib/libLLVM_jll/src/libLLVM_jll.jl +++ b/stdlib/libLLVM_jll/src/libLLVM_jll.jl @@ -14,9 +14,9 @@ export libLLVM # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libLLVM_handle = C_NULL -libLLVM_path = "" +artifact_dir::String = "" +libLLVM_handle::Ptr{Cvoid} = C_NULL +libLLVM_path::String = "" if Sys.iswindows() const libLLVM = "$(Base.libllvm_name).dll" diff --git a/stdlib/libblastrampoline_jll/src/libblastrampoline_jll.jl b/stdlib/libblastrampoline_jll/src/libblastrampoline_jll.jl index c223b513382d7..49e7932a6b701 100644 --- a/stdlib/libblastrampoline_jll/src/libblastrampoline_jll.jl +++ b/stdlib/libblastrampoline_jll/src/libblastrampoline_jll.jl @@ -14,9 +14,9 @@ export libblastrampoline # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libblastrampoline_handle = C_NULL -libblastrampoline_path = "" +artifact_dir::String = "" +libblastrampoline_handle::Ptr{Cvoid} = C_NULL +libblastrampoline_path::String = "" # NOTE: keep in sync with `Base.libblas_name` and `Base.liblapack_name`. const libblastrampoline = if Sys.iswindows() diff --git a/stdlib/nghttp2_jll/src/nghttp2_jll.jl b/stdlib/nghttp2_jll/src/nghttp2_jll.jl index 09af350636943..76e8d3582c402 100644 --- a/stdlib/nghttp2_jll/src/nghttp2_jll.jl +++ b/stdlib/nghttp2_jll/src/nghttp2_jll.jl @@ -13,9 +13,9 @@ export libnghttp2 # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -libnghttp2_handle = C_NULL -libnghttp2_path = "" +artifact_dir::String = "" +libnghttp2_handle::Ptr{Cvoid} = C_NULL +libnghttp2_path::String = "" if Sys.iswindows() const libnghttp2 = "libnghttp2-14.dll" diff --git a/stdlib/p7zip_jll/src/p7zip_jll.jl b/stdlib/p7zip_jll/src/p7zip_jll.jl index eaa709735c383..01f26de936e78 100644 --- a/stdlib/p7zip_jll/src/p7zip_jll.jl +++ b/stdlib/p7zip_jll/src/p7zip_jll.jl @@ -13,8 +13,8 @@ export p7zip # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") -artifact_dir = "" -p7zip_path = "" +artifact_dir::String = "" +p7zip_path::String = "" if Sys.iswindows() const p7zip_exe = "7z.exe" else From 4289264d609f9ebac10caef3d3b31e633e687239 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 28 Apr 2023 14:45:42 +0200 Subject: [PATCH 613/775] Preserve LLVM function attributes during address space removal pass. (#49551) --- src/llvm-remove-addrspaces.cpp | 5 ++--- test/llvmpasses/remove-addrspaces.ll | 8 +++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/llvm-remove-addrspaces.cpp b/src/llvm-remove-addrspaces.cpp index a005d3cfaa352..4a3290da0ecf5 100644 --- a/src/llvm-remove-addrspaces.cpp +++ b/src/llvm-remove-addrspaces.cpp @@ -323,7 +323,7 @@ bool removeAddrspaces(Module &M, AddrspaceRemapFunction ASRemapper) Function *NF = Function::Create( NFTy, F->getLinkage(), F->getAddressSpace(), Name, &M); - // no need to copy attributes here, that's done by CloneFunctionInto + NF->copyAttributesFrom(F); VMap[F] = NF; } @@ -385,8 +385,7 @@ bool removeAddrspaces(Module &M, AddrspaceRemapFunction ASRemapper) &TypeRemapper, &Materializer); - // CloneFunctionInto unconditionally copies the attributes from F to NF, - // without considering e.g. the byval attribute type. + // Update function attributes that contain types AttributeList Attrs = F->getAttributes(); LLVMContext &C = F->getContext(); for (unsigned i = 0; i < Attrs.getNumAttrSets(); ++i) { diff --git a/test/llvmpasses/remove-addrspaces.ll b/test/llvmpasses/remove-addrspaces.ll index 77a8a5e815057..26406d2ca7d98 100644 --- a/test/llvmpasses/remove-addrspaces.ll +++ b/test/llvmpasses/remove-addrspaces.ll @@ -47,7 +47,7 @@ top: %list = type { i64, %list* } ; COM: There's nothing to remove in this function; but remove-addrspaces shouldn't crash. -define i64 @sum.linked.list() #0 { +define i64 @sum.linked.list() { ; CHECK-LABEL: @sum.linked.list top: %a = alloca %list @@ -109,3 +109,9 @@ define void @byval_type([1 x {} addrspace(10)*] addrspace(11)* byval([1 x {} add ; CHECK: define void @byval_type([1 x {}*]* byval([1 x {}*]) %0) ret void } + + +; COM: check that other function attributes are preserved +declare void @convergent_function() #0 +attributes #0 = { convergent } +; CHECK: attributes #0 = { convergent } From cc7fec740e4349d4036b2771692ad80ef38b975a Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 28 Apr 2023 15:01:07 +0200 Subject: [PATCH 614/775] add the package name to the `LOAD_Pkgimage` zone (#49538) --- base/loading.jl | 4 ++-- src/julia.h | 2 +- src/staticdata.c | 15 ++++++++------- src/timing.c | 1 - test/precompile.jl | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 7eb928eb385bf..83737dd7d1fd1 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1008,10 +1008,10 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No if ocachepath !== nothing @debug "Loading object cache file $ocachepath for $pkg" - sv = ccall(:jl_restore_package_image_from_file, Any, (Cstring, Any, Cint), ocachepath, depmods, false) + sv = ccall(:jl_restore_package_image_from_file, Any, (Cstring, Any, Cint, Cstring), ocachepath, depmods, false, pkg.name) else @debug "Loading cache file $path for $pkg" - sv = ccall(:jl_restore_incremental, Any, (Cstring, Any, Cint), path, depmods, false) + sv = ccall(:jl_restore_incremental, Any, (Cstring, Any, Cint, Cstring), path, depmods, false, pkg.name) end if isa(sv, Exception) return sv diff --git a/src/julia.h b/src/julia.h index 5a90037af3460..c20af4384a858 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1780,7 +1780,7 @@ JL_DLLEXPORT void jl_set_sysimg_so(void *handle); JL_DLLEXPORT void jl_create_system_image(void **, jl_array_t *worklist, bool_t emit_split, ios_t **s, ios_t **z, jl_array_t **udeps, int64_t *srctextpos); JL_DLLEXPORT void jl_restore_system_image(const char *fname); JL_DLLEXPORT void jl_restore_system_image_data(const char *buf, size_t len); -JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *depmods, int complete); +JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *depmods, int complete, const char *pkgimage); JL_DLLEXPORT void jl_set_newly_inferred(jl_value_t *newly_inferred); JL_DLLEXPORT void jl_push_newly_inferred(jl_value_t *ci); diff --git a/src/staticdata.c b/src/staticdata.c index 2f61e91c8128e..33667a05578d4 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -3192,9 +3192,10 @@ static jl_value_t *jl_validate_cache_file(ios_t *f, jl_array_t *depmods, uint64_ } // TODO?: refactor to make it easier to create the "package inspector" -static jl_value_t *jl_restore_package_image_from_stream(ios_t *f, jl_image_t *image, jl_array_t *depmods, int completeinfo) +static jl_value_t *jl_restore_package_image_from_stream(ios_t *f, jl_image_t *image, jl_array_t *depmods, int completeinfo, const char *pkgname) { JL_TIMING(LOAD_IMAGE, LOAD_Pkgimg); + jl_timing_printf(JL_TIMING_CURRENT_BLOCK, pkgname); uint64_t checksum = 0; int64_t dataendpos = 0; int64_t datastartpos = 0; @@ -3269,16 +3270,16 @@ static void jl_restore_system_image_from_stream(ios_t *f, jl_image_t *image, uin jl_restore_system_image_from_stream_(f, image, NULL, checksum | ((uint64_t)0xfdfcfbfa << 32), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } -JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(const char *buf, jl_image_t *image, size_t sz, jl_array_t *depmods, int completeinfo) +JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(const char *buf, jl_image_t *image, size_t sz, jl_array_t *depmods, int completeinfo, const char *pkgname) { ios_t f; ios_static_buffer(&f, (char*)buf, sz); - jl_value_t *ret = jl_restore_package_image_from_stream(&f, image, depmods, completeinfo); + jl_value_t *ret = jl_restore_package_image_from_stream(&f, image, depmods, completeinfo, pkgname); ios_close(&f); return ret; } -JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *depmods, int completeinfo) +JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *depmods, int completeinfo, const char *pkgname) { ios_t f; if (ios_file(&f, fname, 1, 0, 0, 0) == NULL) { @@ -3286,7 +3287,7 @@ JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *d "Cache file \"%s\" not found.\n", fname); } jl_image_t pkgimage = {}; - jl_value_t *ret = jl_restore_package_image_from_stream(&f, &pkgimage, depmods, completeinfo); + jl_value_t *ret = jl_restore_package_image_from_stream(&f, &pkgimage, depmods, completeinfo, pkgname); ios_close(&f); return ret; } @@ -3336,7 +3337,7 @@ JL_DLLEXPORT void jl_restore_system_image_data(const char *buf, size_t len) JL_SIGATOMIC_END(); } -JL_DLLEXPORT jl_value_t *jl_restore_package_image_from_file(const char *fname, jl_array_t *depmods, int completeinfo) +JL_DLLEXPORT jl_value_t *jl_restore_package_image_from_file(const char *fname, jl_array_t *depmods, int completeinfo, const char *pkgname) { void *pkgimg_handle = jl_dlopen(fname, JL_RTLD_LAZY); if (!pkgimg_handle) { @@ -3357,7 +3358,7 @@ JL_DLLEXPORT jl_value_t *jl_restore_package_image_from_file(const char *fname, j jl_image_t pkgimage = jl_init_processor_pkgimg(pkgimg_handle); - jl_value_t* mod = jl_restore_incremental_from_buf(pkgimg_data, &pkgimage, *plen, depmods, completeinfo); + jl_value_t* mod = jl_restore_incremental_from_buf(pkgimg_data, &pkgimage, *plen, depmods, completeinfo, pkgname); return mod; } diff --git a/src/timing.c b/src/timing.c index d5fc9d784b78c..26cbccf6f86b2 100644 --- a/src/timing.c +++ b/src/timing.c @@ -166,7 +166,6 @@ JL_DLLEXPORT void jl_timing_show_module(jl_module_t *m, jl_timing_block_t *cur_b const char *module_name = jl_symbol_name(m->name); TracyCZoneText(*(cur_block->tracy_ctx), module_name, strlen(module_name)); } else { - jl_timing_printf(cur_block, "%s.%s", jl_symbol_name(root->name), jl_symbol_name(m->name)); } #endif diff --git a/test/precompile.jl b/test/precompile.jl index 79e12939c615e..606ee1087e51e 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -1680,9 +1680,9 @@ precompile_test_harness("PkgCacheInspector") do load_path end if ocachefile !== nothing - sv = ccall(:jl_restore_package_image_from_file, Any, (Cstring, Any, Cint), ocachefile, depmods, true) + sv = ccall(:jl_restore_package_image_from_file, Any, (Cstring, Any, Cint, Cstring), ocachefile, depmods, true, "PCI") else - sv = ccall(:jl_restore_incremental, Any, (Cstring, Any, Cint), cachefile, depmods, true) + sv = ccall(:jl_restore_incremental, Any, (Cstring, Any, Cint, Cstring), cachefile, depmods, true, "PCI") end modules, init_order, external_methods, new_specializations, new_method_roots, external_targets, edges = sv From 4158640e0ece2c81135ac27a592f8defc6dc5b1b Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Fri, 28 Apr 2023 19:09:05 +0200 Subject: [PATCH 615/775] Document Float64 return type of Base.time (#49519) --- base/libc.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/libc.jl b/base/libc.jl index 5b508e00bf3e0..82286fbf01af6 100644 --- a/base/libc.jl +++ b/base/libc.jl @@ -235,14 +235,14 @@ end # system date in seconds """ - time(t::TmStruct) + time(t::TmStruct) -> Float64 Converts a `TmStruct` struct to a number of seconds since the epoch. """ time(tm::TmStruct) = Float64(ccall(:mktime, Int, (Ref{TmStruct},), tm)) """ - time() + time() -> Float64 Get the system time in seconds since the epoch, with fairly high (typically, microsecond) resolution. """ From abeecee71c202f06c454963890e41c1f73522a3e Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Fri, 28 Apr 2023 17:59:33 -0300 Subject: [PATCH 616/775] Implement parallel marking (#48600) Using a work-stealing queue after Chase and Lev, optimized for weak memory models by Le et al. Default number of GC threads is half the number of compute threads. Co-authored-by: Gabriel Baraldi Co-authored-by: Valentin Churavy --- NEWS.md | 4 + base/options.jl | 1 + base/threadingconstructs.jl | 7 + doc/man/julia.1 | 5 + doc/src/base/multi-threading.md | 1 + doc/src/manual/command-line-interface.md | 1 + doc/src/manual/environment-variables.md | 8 + doc/src/manual/multi-threading.md | 9 + src/Makefile | 2 +- src/gc-debug.c | 64 ++-- src/gc.c | 381 +++++++++++++++++------ src/gc.h | 37 ++- src/init.c | 1 + src/jl_exported_data.inc | 1 + src/jloptions.c | 11 + src/jloptions.h | 1 + src/julia.h | 1 + src/julia_threads.h | 10 +- src/options.h | 3 + src/partr.c | 33 +- src/threading.c | 42 ++- src/threading.h | 1 + src/work-stealing-queue.h | 102 ++++++ stdlib/Distributed/src/cluster.jl | 5 +- test/choosetests.jl | 2 +- test/cmdlineargs.jl | 18 ++ test/gc.jl | 18 ++ test/gc/binarytree.jl | 53 ++++ test/gc/linkedlist.jl | 21 ++ test/gc/objarray.jl | 35 +++ 30 files changed, 722 insertions(+), 156 deletions(-) create mode 100644 src/work-stealing-queue.h create mode 100644 test/gc.jl create mode 100644 test/gc/binarytree.jl create mode 100644 test/gc/linkedlist.jl create mode 100644 test/gc/objarray.jl diff --git a/NEWS.md b/NEWS.md index 931db0ad1081f..bf7ae28c236ce 100644 --- a/NEWS.md +++ b/NEWS.md @@ -17,11 +17,15 @@ Language changes Compiler/Runtime improvements ----------------------------- + * The `@pure` macro is now deprecated. Use `Base.@assume_effects :foldable` instead ([#48682]). +* The mark phase of the Garbage Collector is now multi-threaded ([#48600]). Command-line option changes --------------------------- +* New option `--gcthreads` to set how many threads will be used by the Garbage Collector ([#48600]). + The default is set to `N/2` where `N` is the amount of worker threads (`--threads`) used by Julia. Multi-threading changes ----------------------- diff --git a/base/options.jl b/base/options.jl index dda0e8b377076..23a3dbc802b5f 100644 --- a/base/options.jl +++ b/base/options.jl @@ -11,6 +11,7 @@ struct JLOptions cpu_target::Ptr{UInt8} nthreadpools::Int16 nthreads::Int16 + ngcthreads::Int16 nthreads_per_pool::Ptr{Int16} nprocs::Int32 machine_file::Ptr{UInt8} diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl index f6e7ea4480305..5a491a04139db 100644 --- a/base/threadingconstructs.jl +++ b/base/threadingconstructs.jl @@ -99,6 +99,13 @@ function threadpooltids(pool::Symbol) end end +""" + Threads.ngcthreads() -> Int + +Returns the number of GC threads currently configured. +""" +ngcthreads() = Int(unsafe_load(cglobal(:jl_n_gcthreads, Cint))) + 1 + function threading_run(fun, static) ccall(:jl_enter_threaded_region, Cvoid, ()) n = threadpoolsize() diff --git a/doc/man/julia.1 b/doc/man/julia.1 index 383c588c58dae..fa9f641b1e76f 100644 --- a/doc/man/julia.1 +++ b/doc/man/julia.1 @@ -118,6 +118,11 @@ supported (Linux and Windows). If this is not supported (macOS) or process affinity is not configured, it uses the number of CPU threads. +.TP +--gcthreads +Enable n GC threads; If unspecified is set to half of the +compute worker threads. + .TP -p, --procs {N|auto} Integer value N launches N additional local worker processes `auto` launches as many workers diff --git a/doc/src/base/multi-threading.md b/doc/src/base/multi-threading.md index 4932aef4cc938..fb75b21479707 100644 --- a/doc/src/base/multi-threading.md +++ b/doc/src/base/multi-threading.md @@ -10,6 +10,7 @@ Base.Threads.nthreads Base.Threads.threadpool Base.Threads.nthreadpools Base.Threads.threadpoolsize +Base.Threads.ngcthreads ``` See also [Multi-Threading](@ref man-multithreading). diff --git a/doc/src/manual/command-line-interface.md b/doc/src/manual/command-line-interface.md index cd2dfe1fb4525..781a77a33dadb 100644 --- a/doc/src/manual/command-line-interface.md +++ b/doc/src/manual/command-line-interface.md @@ -107,6 +107,7 @@ The following is a complete list of command-line switches available when launchi |`-E`, `--print ` |Evaluate `` and display the result| |`-L`, `--load ` |Load `` immediately on all processors| |`-t`, `--threads {N\|auto}` |Enable N threads; `auto` tries to infer a useful default number of threads to use but the exact behavior might change in the future. Currently, `auto` uses the number of CPUs assigned to this julia process based on the OS-specific affinity assignment interface, if supported (Linux and Windows). If this is not supported (macOS) or process affinity is not configured, it uses the number of CPU threads.| +| `--gcthreads {N}` |Enable N GC threads; If unspecified is set to half of the compute worker threads.| |`-p`, `--procs {N\|auto}` |Integer value N launches N additional local worker processes; `auto` launches as many workers as the number of local CPU threads (logical cores)| |`--machine-file ` |Run processes on hosts listed in ``| |`-i` |Interactive mode; REPL runs and `isinteractive()` is true| diff --git a/doc/src/manual/environment-variables.md b/doc/src/manual/environment-variables.md index a5f4efc28e965..ac5a6fad6cc08 100644 --- a/doc/src/manual/environment-variables.md +++ b/doc/src/manual/environment-variables.md @@ -316,6 +316,14 @@ then spinning threads never sleep. Otherwise, `$JULIA_THREAD_SLEEP_THRESHOLD` is interpreted as an unsigned 64-bit integer (`uint64_t`) and gives, in nanoseconds, the amount of time after which spinning threads should sleep. +### [`JULIA_NUM_GC_THREADS`](@id env-gc-threads) + +Sets the number of threads used by Garbage Collection. If unspecified is set to +half of the number of worker threads. + +!!! compat "Julia 1.10" + The environment variable was added in 1.10 + ### [`JULIA_IMAGE_THREADS`](@id env-image-threads) An unsigned 32-bit integer that sets the number of threads used by image diff --git a/doc/src/manual/multi-threading.md b/doc/src/manual/multi-threading.md index 7c48581bd4bea..be64390e473f2 100644 --- a/doc/src/manual/multi-threading.md +++ b/doc/src/manual/multi-threading.md @@ -72,6 +72,15 @@ julia> Threads.threadid() three processes have 2 threads enabled. For more fine grained control over worker threads use [`addprocs`](@ref) and pass `-t`/`--threads` as `exeflags`. +### Multiple GC Threads + +The Garbage Collector (GC) can use multiple threads. The amount used is either half the number +of compute worker threads or configured by either the `--gcthreads` command line argument or by using the +[`JULIA_NUM_GC_THREADS`](@ref env-gc-threads) environment variable. + +!!! compat "Julia 1.10" + The `--gcthreads` command line argument requires at least Julia 1.10. + ## [Threadpools](@id man-threadpools) When a program's threads are busy with many tasks to run, tasks may experience diff --git a/src/Makefile b/src/Makefile index 00e3fa18044d0..bba361eaadeaa 100644 --- a/src/Makefile +++ b/src/Makefile @@ -99,7 +99,7 @@ ifeq ($(USE_SYSTEM_LIBUV),0) UV_HEADERS += uv.h UV_HEADERS += uv/*.h endif -PUBLIC_HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,julia.h julia_assert.h julia_threads.h julia_fasttls.h julia_locks.h julia_atomics.h jloptions.h) +PUBLIC_HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,work-stealing-queue.h julia.h julia_assert.h julia_threads.h julia_fasttls.h julia_locks.h julia_atomics.h jloptions.h) ifeq ($(OS),WINNT) PUBLIC_HEADERS += $(addprefix $(SRCDIR)/,win32_ucontext.h) endif diff --git a/src/gc-debug.c b/src/gc-debug.c index 2350a21958815..ca0cf82c7d581 100644 --- a/src/gc-debug.c +++ b/src/gc-debug.c @@ -198,12 +198,21 @@ static void restore(void) static void gc_verify_track(jl_ptls_t ptls) { + // `gc_verify_track` is limited to single-threaded GC + if (jl_n_gcthreads != 0) + return; do { jl_gc_markqueue_t mq; - mq.current = mq.start = ptls->mark_queue.start; - mq.end = ptls->mark_queue.end; - mq.current_chunk = mq.chunk_start = ptls->mark_queue.chunk_start; - mq.chunk_end = ptls->mark_queue.chunk_end; + jl_gc_markqueue_t *mq2 = &ptls->mark_queue; + ws_queue_t *cq = &mq.chunk_queue; + ws_queue_t *q = &mq.ptr_queue; + jl_atomic_store_relaxed(&cq->top, 0); + jl_atomic_store_relaxed(&cq->bottom, 0); + jl_atomic_store_relaxed(&cq->array, jl_atomic_load_relaxed(&mq2->chunk_queue.array)); + jl_atomic_store_relaxed(&q->top, 0); + jl_atomic_store_relaxed(&q->bottom, 0); + jl_atomic_store_relaxed(&q->array, jl_atomic_load_relaxed(&mq2->ptr_queue.array)); + arraylist_new(&mq.reclaim_set, 32); arraylist_push(&lostval_parents_done, lostval); jl_safe_printf("Now looking for %p =======\n", lostval); clear_mark(GC_CLEAN); @@ -214,7 +223,7 @@ static void gc_verify_track(jl_ptls_t ptls) gc_mark_finlist(&mq, &ptls2->finalizers, 0); } gc_mark_finlist(&mq, &finalizer_list_marked, 0); - gc_mark_loop_(ptls, &mq); + gc_mark_loop_serial_(ptls, &mq); if (lostval_parents.len == 0) { jl_safe_printf("Could not find the missing link. We missed a toplevel root. This is odd.\n"); break; @@ -248,11 +257,22 @@ static void gc_verify_track(jl_ptls_t ptls) void gc_verify(jl_ptls_t ptls) { + // `gc_verify` is limited to single-threaded GC + if (jl_n_gcthreads != 0) { + jl_safe_printf("Warn. GC verify disabled in multi-threaded GC\n"); + return; + } jl_gc_markqueue_t mq; - mq.current = mq.start = ptls->mark_queue.start; - mq.end = ptls->mark_queue.end; - mq.current_chunk = mq.chunk_start = ptls->mark_queue.chunk_start; - mq.chunk_end = ptls->mark_queue.chunk_end; + jl_gc_markqueue_t *mq2 = &ptls->mark_queue; + ws_queue_t *cq = &mq.chunk_queue; + ws_queue_t *q = &mq.ptr_queue; + jl_atomic_store_relaxed(&cq->top, 0); + jl_atomic_store_relaxed(&cq->bottom, 0); + jl_atomic_store_relaxed(&cq->array, jl_atomic_load_relaxed(&mq2->chunk_queue.array)); + jl_atomic_store_relaxed(&q->top, 0); + jl_atomic_store_relaxed(&q->bottom, 0); + jl_atomic_store_relaxed(&q->array, jl_atomic_load_relaxed(&mq2->ptr_queue.array)); + arraylist_new(&mq.reclaim_set, 32); lostval = NULL; lostval_parents.len = 0; lostval_parents_done.len = 0; @@ -265,7 +285,7 @@ void gc_verify(jl_ptls_t ptls) gc_mark_finlist(&mq, &ptls2->finalizers, 0); } gc_mark_finlist(&mq, &finalizer_list_marked, 0); - gc_mark_loop_(ptls, &mq); + gc_mark_loop_serial_(ptls, &mq); int clean_len = bits_save[GC_CLEAN].len; for(int i = 0; i < clean_len + bits_save[GC_OLD].len; i++) { jl_taggedvalue_t *v = (jl_taggedvalue_t*)bits_save[i >= clean_len ? GC_OLD : GC_CLEAN].items[i >= clean_len ? i - clean_len : i]; @@ -1268,30 +1288,6 @@ int gc_slot_to_arrayidx(void *obj, void *_slot) JL_NOTSAFEPOINT return (slot - start) / elsize; } -// Print a backtrace from the `mq->start` of the mark queue up to `mq->current` -// `offset` will be added to `mq->current` for convenience in the debugger. -NOINLINE void gc_mark_loop_unwind(jl_ptls_t ptls, jl_gc_markqueue_t *mq, int offset) -{ - jl_jmp_buf *old_buf = jl_get_safe_restore(); - jl_jmp_buf buf; - jl_set_safe_restore(&buf); - if (jl_setjmp(buf, 0) != 0) { - jl_safe_printf("\n!!! ERROR when unwinding gc mark loop -- ABORTING !!!\n"); - jl_set_safe_restore(old_buf); - return; - } - jl_value_t **start = mq->start; - jl_value_t **end = mq->current + offset; - for (; start < end; start++) { - jl_value_t *obj = *start; - jl_taggedvalue_t *o = jl_astaggedvalue(obj); - jl_safe_printf("Queued object: %p :: (tag: %zu) (bits: %zu)\n", obj, - (uintptr_t)o->header, ((uintptr_t)o->header & 3)); - jl_((void*)(jl_datatype_t *)(o->header & ~(uintptr_t)0xf)); - } - jl_set_safe_restore(old_buf); -} - static int gc_logging_enabled = 0; JL_DLLEXPORT void jl_enable_gc_logging(int enable) { diff --git a/src/gc.c b/src/gc.c index 3c116b4cd352f..4987af5f296dc 100644 --- a/src/gc.c +++ b/src/gc.c @@ -11,6 +11,18 @@ extern "C" { #endif +// `tid` of mutator thread that triggered GC +_Atomic(int) gc_master_tid; +// `tid` of first GC thread +int gc_first_tid; + +// Mutex/cond used to synchronize sleep/wakeup of GC threads +uv_mutex_t gc_threads_lock; +uv_cond_t gc_threads_cond; + +// Number of threads currently running the GC mark-loop +_Atomic(int) gc_n_threads_marking; + // Linked list of callback functions typedef void (*jl_gc_cb_func_t)(void); @@ -1889,7 +1901,6 @@ JL_NORETURN NOINLINE void gc_assert_datatype_fail(jl_ptls_t ptls, jl_datatype_t jl_gc_debug_print_status(); jl_(vt); jl_gc_debug_critical_error(); - gc_mark_loop_unwind(ptls, mq, 0); abort(); } @@ -1912,65 +1923,53 @@ STATIC_INLINE void gc_mark_push_remset(jl_ptls_t ptls, jl_value_t *obj, } } -// Double the mark queue -static NOINLINE void gc_markqueue_resize(jl_gc_markqueue_t *mq) JL_NOTSAFEPOINT -{ - jl_value_t **old_start = mq->start; - size_t old_queue_size = (mq->end - mq->start); - size_t offset = (mq->current - old_start); - mq->start = (jl_value_t **)realloc_s(old_start, 2 * old_queue_size * sizeof(jl_value_t *)); - mq->current = (mq->start + offset); - mq->end = (mq->start + 2 * old_queue_size); -} - // Push a work item to the queue -STATIC_INLINE void gc_markqueue_push(jl_gc_markqueue_t *mq, jl_value_t *obj) JL_NOTSAFEPOINT +STATIC_INLINE void gc_ptr_queue_push(jl_gc_markqueue_t *mq, jl_value_t *obj) JL_NOTSAFEPOINT { - if (__unlikely(mq->current == mq->end)) - gc_markqueue_resize(mq); - *mq->current = obj; - mq->current++; + ws_array_t *old_a = ws_queue_push(&mq->ptr_queue, &obj, sizeof(jl_value_t*)); + // Put `old_a` in `reclaim_set` to be freed after the mark phase + if (__unlikely(old_a != NULL)) + arraylist_push(&mq->reclaim_set, old_a); } // Pop from the mark queue -STATIC_INLINE jl_value_t *gc_markqueue_pop(jl_gc_markqueue_t *mq) +STATIC_INLINE jl_value_t *gc_ptr_queue_pop(jl_gc_markqueue_t *mq) JL_NOTSAFEPOINT { - jl_value_t *obj = NULL; - if (mq->current != mq->start) { - mq->current--; - obj = *mq->current; - } - return obj; + jl_value_t *v = NULL; + ws_queue_pop(&mq->ptr_queue, &v, sizeof(jl_value_t*)); + return v; } -// Double the chunk queue -static NOINLINE void gc_chunkqueue_resize(jl_gc_markqueue_t *mq) JL_NOTSAFEPOINT +// Steal from `mq2` +STATIC_INLINE jl_value_t *gc_ptr_queue_steal_from(jl_gc_markqueue_t *mq2) JL_NOTSAFEPOINT { - jl_gc_chunk_t *old_start = mq->chunk_start; - size_t old_queue_size = (mq->chunk_end - mq->chunk_start); - size_t offset = (mq->current_chunk - old_start); - mq->chunk_start = (jl_gc_chunk_t *)realloc_s(old_start, 2 * old_queue_size * sizeof(jl_gc_chunk_t)); - mq->current_chunk = (mq->chunk_start + offset); - mq->chunk_end = (mq->chunk_start + 2 * old_queue_size); + jl_value_t *v = NULL; + ws_queue_steal_from(&mq2->ptr_queue, &v, sizeof(jl_value_t*)); + return v; } // Push chunk `*c` into chunk queue STATIC_INLINE void gc_chunkqueue_push(jl_gc_markqueue_t *mq, jl_gc_chunk_t *c) JL_NOTSAFEPOINT { - if (__unlikely(mq->current_chunk == mq->chunk_end)) - gc_chunkqueue_resize(mq); - *mq->current_chunk = *c; - mq->current_chunk++; + ws_array_t *old_a = ws_queue_push(&mq->chunk_queue, c, sizeof(jl_gc_chunk_t)); + // Put `old_a` in `reclaim_set` to be freed after the mark phase + if (__unlikely(old_a != NULL)) + arraylist_push(&mq->reclaim_set, old_a); } // Pop chunk from chunk queue STATIC_INLINE jl_gc_chunk_t gc_chunkqueue_pop(jl_gc_markqueue_t *mq) JL_NOTSAFEPOINT { jl_gc_chunk_t c = {.cid = GC_empty_chunk}; - if (mq->current_chunk != mq->chunk_start) { - mq->current_chunk--; - c = *mq->current_chunk; - } + ws_queue_pop(&mq->chunk_queue, &c, sizeof(jl_gc_chunk_t)); + return c; +} + +// Steal chunk from `mq2` +STATIC_INLINE jl_gc_chunk_t gc_chunkqueue_steal_from(jl_gc_markqueue_t *mq2) JL_NOTSAFEPOINT +{ + jl_gc_chunk_t c = {.cid = GC_empty_chunk}; + ws_queue_steal_from(&mq2->chunk_queue, &c, sizeof(jl_gc_chunk_t)); return c; } @@ -1985,7 +1984,7 @@ STATIC_INLINE void gc_try_claim_and_push(jl_gc_markqueue_t *mq, void *_obj, if (!gc_old(o->header) && nptr) *nptr |= 1; if (gc_try_setmark_tag(o, GC_MARKED)) - gc_markqueue_push(mq, obj); + gc_ptr_queue_push(mq, obj); } // Mark object with 8bit field descriptors @@ -2108,10 +2107,22 @@ STATIC_INLINE void gc_mark_objarray(jl_ptls_t ptls, jl_value_t *obj_parent, jl_v } } } - size_t too_big = (obj_end - obj_begin) / MAX_REFS_AT_ONCE > step; // use this order of operations to avoid idiv + size_t too_big = (obj_end - obj_begin) / GC_CHUNK_BATCH_SIZE > step; // use this order of operations to avoid idiv jl_value_t **scan_end = obj_end; + int pushed_chunk = 0; if (too_big) { - scan_end = obj_begin + step * MAX_REFS_AT_ONCE; + scan_end = obj_begin + step * GC_CHUNK_BATCH_SIZE; + // case 1: array owner is young, so we won't need to scan through all its elements + // to know that we will never need to push it to the remset. it's fine + // to create a chunk with "incorrect" `nptr` and push it to the chunk-queue + // ASAP in order to expose as much parallelism as possible + // case 2: lowest two bits of `nptr` are already set to 0x3, so won't change after + // scanning the array elements + if ((nptr & 0x2) != 0x2 || (nptr & 0x3) == 0x3) { + jl_gc_chunk_t c = {GC_objary_chunk, obj_parent, scan_end, obj_end, NULL, NULL, step, nptr}; + gc_chunkqueue_push(mq, &c); + pushed_chunk = 1; + } } for (; obj_begin < scan_end; obj_begin += step) { new_obj = *obj_begin; @@ -2123,10 +2134,10 @@ STATIC_INLINE void gc_mark_objarray(jl_ptls_t ptls, jl_value_t *obj_parent, jl_v } } if (too_big) { - jl_gc_chunk_t c = {GC_objary_chunk, obj_parent, scan_end, - obj_end, NULL, NULL, - step, nptr}; - gc_chunkqueue_push(mq, &c); + if (!pushed_chunk) { + jl_gc_chunk_t c = {GC_objary_chunk, obj_parent, scan_end, obj_end, NULL, NULL, step, nptr}; + gc_chunkqueue_push(mq, &c); + } } else { gc_mark_push_remset(ptls, obj_parent, nptr); @@ -2168,10 +2179,22 @@ STATIC_INLINE void gc_mark_array8(jl_ptls_t ptls, jl_value_t *ary8_parent, jl_va break; } } - size_t too_big = (ary8_end - ary8_begin) / MAX_REFS_AT_ONCE > elsize; // use this order of operations to avoid idiv + size_t too_big = (ary8_end - ary8_begin) / GC_CHUNK_BATCH_SIZE > elsize; // use this order of operations to avoid idiv jl_value_t **scan_end = ary8_end; + int pushed_chunk = 0; if (too_big) { - scan_end = ary8_begin + elsize * MAX_REFS_AT_ONCE; + scan_end = ary8_begin + elsize * GC_CHUNK_BATCH_SIZE; + // case 1: array owner is young, so we won't need to scan through all its elements + // to know that we will never need to push it to the remset. it's fine + // to create a chunk with "incorrect" `nptr` and push it to the chunk-queue + // ASAP in order to expose as much parallelism as possible + // case 2: lowest two bits of `nptr` are already set to 0x3, so won't change after + // scanning the array elements + if ((nptr & 0x2) != 0x2 || (nptr & 0x3) == 0x3) { + jl_gc_chunk_t c = {GC_ary8_chunk, ary8_parent, scan_end, ary8_end, elem_begin, elem_end, 0, nptr}; + gc_chunkqueue_push(mq, &c); + pushed_chunk = 1; + } } for (; ary8_begin < ary8_end; ary8_begin += elsize) { for (uint8_t *pindex = elem_begin; pindex < elem_end; pindex++) { @@ -2185,10 +2208,10 @@ STATIC_INLINE void gc_mark_array8(jl_ptls_t ptls, jl_value_t *ary8_parent, jl_va } } if (too_big) { - jl_gc_chunk_t c = {GC_ary8_chunk, ary8_parent, scan_end, - ary8_end, elem_begin, elem_end, - 0, nptr}; - gc_chunkqueue_push(mq, &c); + if (!pushed_chunk) { + jl_gc_chunk_t c = {GC_ary8_chunk, ary8_parent, scan_end, ary8_end, elem_begin, elem_end, 0, nptr}; + gc_chunkqueue_push(mq, &c); + } } else { gc_mark_push_remset(ptls, ary8_parent, nptr); @@ -2230,10 +2253,22 @@ STATIC_INLINE void gc_mark_array16(jl_ptls_t ptls, jl_value_t *ary16_parent, jl_ break; } } - size_t too_big = (ary16_end - ary16_begin) / MAX_REFS_AT_ONCE > elsize; // use this order of operations to avoid idiv + size_t too_big = (ary16_end - ary16_begin) / GC_CHUNK_BATCH_SIZE > elsize; // use this order of operations to avoid idiv jl_value_t **scan_end = ary16_end; + int pushed_chunk = 0; if (too_big) { - scan_end = ary16_begin + elsize * MAX_REFS_AT_ONCE; + scan_end = ary16_begin + elsize * GC_CHUNK_BATCH_SIZE; + // case 1: array owner is young, so we won't need to scan through all its elements + // to know that we will never need to push it to the remset. it's fine + // to create a chunk with "incorrect" `nptr` and push it to the chunk-queue + // ASAP in order to expose as much parallelism as possible + // case 2: lowest two bits of `nptr` are already set to 0x3, so won't change after + // scanning the array elements + if ((nptr & 0x2) != 0x2 || (nptr & 0x3) == 0x3) { + jl_gc_chunk_t c = {GC_ary16_chunk, ary16_parent, scan_end, ary16_end, elem_begin, elem_end, elsize, nptr}; + gc_chunkqueue_push(mq, &c); + pushed_chunk = 1; + } } for (; ary16_begin < scan_end; ary16_begin += elsize) { for (uint16_t *pindex = elem_begin; pindex < elem_end; pindex++) { @@ -2247,10 +2282,10 @@ STATIC_INLINE void gc_mark_array16(jl_ptls_t ptls, jl_value_t *ary16_parent, jl_ } } if (too_big) { - jl_gc_chunk_t c = {GC_ary16_chunk, ary16_parent, scan_end, - ary16_end, elem_begin, elem_end, - elsize, nptr}; - gc_chunkqueue_push(mq, &c); + if (!pushed_chunk) { + jl_gc_chunk_t c = {GC_ary16_chunk, ary16_parent, scan_end, ary16_end, elem_begin, elem_end, elsize, nptr}; + gc_chunkqueue_push(mq, &c); + } } else { gc_mark_push_remset(ptls, ary16_parent, nptr); @@ -2418,10 +2453,10 @@ void gc_mark_finlist_(jl_gc_markqueue_t *mq, jl_value_t **fl_begin, jl_value_t * jl_value_t *new_obj; // Decide whether need to chunk finlist size_t nrefs = (fl_end - fl_begin); - if (nrefs > MAX_REFS_AT_ONCE) { - jl_gc_chunk_t c = {GC_finlist_chunk, NULL, fl_begin + MAX_REFS_AT_ONCE, fl_end, 0, 0, 0, 0}; + if (nrefs > GC_CHUNK_BATCH_SIZE) { + jl_gc_chunk_t c = {GC_finlist_chunk, NULL, fl_begin + GC_CHUNK_BATCH_SIZE, fl_end, 0, 0, 0, 0}; gc_chunkqueue_push(mq, &c); - fl_end = fl_begin + MAX_REFS_AT_ONCE; + fl_end = fl_begin + GC_CHUNK_BATCH_SIZE; } for (; fl_begin < fl_end; fl_begin++) { new_obj = *fl_begin; @@ -2453,7 +2488,7 @@ JL_DLLEXPORT int jl_gc_mark_queue_obj(jl_ptls_t ptls, jl_value_t *obj) { int may_claim = gc_try_setmark_tag(jl_astaggedvalue(obj), GC_MARKED); if (may_claim) - gc_markqueue_push(&ptls->mark_queue, obj); + gc_ptr_queue_push(&ptls->mark_queue, obj); return may_claim; } @@ -2673,7 +2708,7 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ if (!meta_updated) goto mark_obj; else - gc_markqueue_push(mq, new_obj); + gc_ptr_queue_push(mq, new_obj); } } else if (vt == jl_string_type) { @@ -2710,7 +2745,7 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ if (!meta_updated) goto mark_obj; else - gc_markqueue_push(mq, new_obj); + gc_ptr_queue_push(mq, new_obj); } } else if (layout->fielddesc_type == 1) { @@ -2723,7 +2758,7 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ if (!meta_updated) goto mark_obj; else - gc_markqueue_push(mq, new_obj); + gc_ptr_queue_push(mq, new_obj); } } else if (layout->fielddesc_type == 2) { @@ -2738,7 +2773,7 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ if (!meta_updated) goto mark_obj; else - gc_markqueue_push(mq, new_obj); + gc_ptr_queue_push(mq, new_obj); } } else { @@ -2754,13 +2789,12 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ } // Used in gc-debug -void gc_mark_loop_(jl_ptls_t ptls, jl_gc_markqueue_t *mq) +void gc_mark_loop_serial_(jl_ptls_t ptls, jl_gc_markqueue_t *mq) { while (1) { - void *new_obj = (void *)gc_markqueue_pop(&ptls->mark_queue); + void *new_obj = (void *)gc_ptr_queue_pop(&ptls->mark_queue); // No more objects to mark if (__unlikely(new_obj == NULL)) { - // TODO: work-stealing comes here... return; } gc_mark_outrefs(ptls, mq, new_obj, 0); @@ -2775,21 +2809,172 @@ void gc_drain_own_chunkqueue(jl_ptls_t ptls, jl_gc_markqueue_t *mq) c = gc_chunkqueue_pop(mq); if (c.cid != GC_empty_chunk) { gc_mark_chunk(ptls, mq, &c); - gc_mark_loop_(ptls, mq); + gc_mark_loop_serial_(ptls, mq); } } while (c.cid != GC_empty_chunk); } -// Main mark loop. Single stack (allocated on the heap) of `jl_value_t *` +// Main mark loop. Stack (allocated on the heap) of `jl_value_t *` // is used to keep track of processed items. Maintaning this stack (instead of // native one) avoids stack overflow when marking deep objects and // makes it easier to implement parallel marking via work-stealing -JL_EXTENSION NOINLINE void gc_mark_loop(jl_ptls_t ptls) +JL_EXTENSION NOINLINE void gc_mark_loop_serial(jl_ptls_t ptls) { - gc_mark_loop_(ptls, &ptls->mark_queue); + gc_mark_loop_serial_(ptls, &ptls->mark_queue); gc_drain_own_chunkqueue(ptls, &ptls->mark_queue); } +void gc_mark_and_steal(jl_ptls_t ptls) +{ + jl_gc_markqueue_t *mq = &ptls->mark_queue; + jl_gc_markqueue_t *mq_master = NULL; + int master_tid = jl_atomic_load(&gc_master_tid); + if (master_tid != -1) + mq_master = &gc_all_tls_states[master_tid]->mark_queue; + void *new_obj; + jl_gc_chunk_t c; + pop : { + new_obj = gc_ptr_queue_pop(mq); + if (new_obj != NULL) { + goto mark; + } + c = gc_chunkqueue_pop(mq); + if (c.cid != GC_empty_chunk) { + gc_mark_chunk(ptls, mq, &c); + goto pop; + } + goto steal; + } + mark : { + gc_mark_outrefs(ptls, mq, new_obj, 0); + goto pop; + } + // Note that for the stealing heuristics, we try to + // steal chunks much more agressively than pointers, + // since we know chunks will likely expand into a lot + // of work for the mark loop + steal : { + // Try to steal chunk from random GC thread + for (int i = 0; i < 4 * jl_n_gcthreads; i++) { + uint32_t v = gc_first_tid + cong(UINT64_MAX, UINT64_MAX, &ptls->rngseed) % jl_n_gcthreads; + jl_gc_markqueue_t *mq2 = &gc_all_tls_states[v]->mark_queue; + c = gc_chunkqueue_steal_from(mq2); + if (c.cid != GC_empty_chunk) { + gc_mark_chunk(ptls, mq, &c); + goto pop; + } + } + // Sequentially walk GC threads to try to steal chunk + for (int i = gc_first_tid; i < gc_first_tid + jl_n_gcthreads; i++) { + jl_gc_markqueue_t *mq2 = &gc_all_tls_states[i]->mark_queue; + c = gc_chunkqueue_steal_from(mq2); + if (c.cid != GC_empty_chunk) { + gc_mark_chunk(ptls, mq, &c); + goto pop; + } + } + // Try to steal chunk from master thread + if (mq_master != NULL) { + c = gc_chunkqueue_steal_from(mq_master); + if (c.cid != GC_empty_chunk) { + gc_mark_chunk(ptls, mq, &c); + goto pop; + } + } + // Try to steal pointer from random GC thread + for (int i = 0; i < 4 * jl_n_gcthreads; i++) { + uint32_t v = gc_first_tid + cong(UINT64_MAX, UINT64_MAX, &ptls->rngseed) % jl_n_gcthreads; + jl_gc_markqueue_t *mq2 = &gc_all_tls_states[v]->mark_queue; + new_obj = gc_ptr_queue_steal_from(mq2); + if (new_obj != NULL) + goto mark; + } + // Sequentially walk GC threads to try to steal pointer + for (int i = gc_first_tid; i < gc_first_tid + jl_n_gcthreads; i++) { + jl_gc_markqueue_t *mq2 = &gc_all_tls_states[i]->mark_queue; + new_obj = gc_ptr_queue_steal_from(mq2); + if (new_obj != NULL) + goto mark; + } + // Try to steal pointer from master thread + if (mq_master != NULL) { + new_obj = gc_ptr_queue_steal_from(mq_master); + if (new_obj != NULL) + goto mark; + } + } +} + +#define GC_BACKOFF_MIN 4 +#define GC_BACKOFF_MAX 12 + +void gc_mark_backoff(int *i) +{ + if (*i < GC_BACKOFF_MAX) { + (*i)++; + } + for (int j = 0; j < (1 << *i); j++) { + jl_cpu_pause(); + } +} + +void gc_mark_loop_parallel(jl_ptls_t ptls, int master) +{ + int backoff = GC_BACKOFF_MIN; + if (master) { + jl_atomic_store(&gc_master_tid, ptls->tid); + // Wake threads up and try to do some work + uv_mutex_lock(&gc_threads_lock); + jl_atomic_fetch_add(&gc_n_threads_marking, 1); + uv_cond_broadcast(&gc_threads_cond); + uv_mutex_unlock(&gc_threads_lock); + gc_mark_and_steal(ptls); + jl_atomic_fetch_add(&gc_n_threads_marking, -1); + } + while (jl_atomic_load(&gc_n_threads_marking) > 0) { + // Try to become a thief while other threads are marking + jl_atomic_fetch_add(&gc_n_threads_marking, 1); + if (jl_atomic_load(&gc_master_tid) != -1) { + gc_mark_and_steal(ptls); + } + jl_atomic_fetch_add(&gc_n_threads_marking, -1); + // Failed to steal + gc_mark_backoff(&backoff); + } +} + +void gc_mark_loop(jl_ptls_t ptls) +{ + if (jl_n_gcthreads == 0 || gc_heap_snapshot_enabled) { + gc_mark_loop_serial(ptls); + } + else { + gc_mark_loop_parallel(ptls, 1); + } +} + +void gc_mark_loop_barrier(void) +{ + jl_atomic_store(&gc_master_tid, -1); + while (jl_atomic_load(&gc_n_threads_marking) != 0) { + jl_cpu_pause(); + } +} + +void gc_mark_clean_reclaim_sets(void) +{ + // Clean up `reclaim-sets` and reset `top/bottom` of queues + for (int i = 0; i < gc_n_threads; i++) { + jl_ptls_t ptls2 = gc_all_tls_states[i]; + arraylist_t *reclaim_set2 = &ptls2->mark_queue.reclaim_set; + ws_array_t *a = NULL; + while ((a = (ws_array_t *)arraylist_pop(reclaim_set2)) != NULL) { + free(a->buffer); + free(a); + } + } +} + static void gc_premark(jl_ptls_t ptls2) { arraylist_t *remset = ptls2->heap.remset; @@ -3054,16 +3239,23 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) } assert(gc_n_threads); + int single_threaded = (jl_n_gcthreads == 0 || gc_heap_snapshot_enabled); for (int t_i = 0; t_i < gc_n_threads; t_i++) { jl_ptls_t ptls2 = gc_all_tls_states[t_i]; + jl_gc_markqueue_t *mq2 = mq; + jl_ptls_t ptls_gc_thread = NULL; + if (!single_threaded) { + ptls_gc_thread = gc_all_tls_states[gc_first_tid + t_i % jl_n_gcthreads]; + mq2 = &ptls_gc_thread->mark_queue; + } if (ptls2 != NULL) { // 2.1. mark every thread local root - gc_queue_thread_local(mq, ptls2); + gc_queue_thread_local(mq2, ptls2); // 2.2. mark any managed objects in the backtrace buffer // TODO: treat these as roots for gc_heap_snapshot_record - gc_queue_bt_buf(mq, ptls2); + gc_queue_bt_buf(mq2, ptls2); // 2.3. mark every object in the `last_remsets` and `rem_binding` - gc_queue_remset(ptls, ptls2); + gc_queue_remset(single_threaded ? ptls : ptls_gc_thread, ptls2); } } @@ -3074,6 +3266,8 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) gc_cblist_root_scanner, (collection)); } gc_mark_loop(ptls); + gc_mark_loop_barrier(); + gc_mark_clean_reclaim_sets(); // 4. check for objects to finalize clear_weak_refs(); @@ -3100,7 +3294,7 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) gc_mark_finlist(mq, &finalizer_list_marked, orig_marked_len); // "Flush" the mark stack before flipping the reset_age bit // so that the objects are not incorrectly reset. - gc_mark_loop(ptls); + gc_mark_loop_serial(ptls); // Conservative marking relies on age to tell allocated objects // and freelist entries apart. mark_reset_age = !jl_gc_conservative_gc_support_enabled(); @@ -3109,7 +3303,7 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) // and should not be referenced by any old objects so this won't break // the GC invariant. gc_mark_finlist(mq, &to_finalize, 0); - gc_mark_loop(ptls); + gc_mark_loop_serial(ptls); mark_reset_age = 0; } @@ -3169,7 +3363,7 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) size_t maxmem = 0; #ifdef _P64 // on a big memory machine, increase max_collect_interval to totalmem / nthreads / 2 - maxmem = total_mem / gc_n_threads / 2; + maxmem = total_mem / (gc_n_threads - jl_n_gcthreads) / 2; #endif if (maxmem < max_collect_interval) maxmem = max_collect_interval; @@ -3277,14 +3471,15 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) if (collection == JL_GC_AUTO) { //If we aren't freeing enough or are seeing lots and lots of pointers let it increase faster - if(!not_freed_enough || large_frontier) { + if (!not_freed_enough || large_frontier) { int64_t tot = 2 * (live_bytes + gc_num.since_sweep) / 3; if (gc_num.interval > tot) { gc_num.interval = tot; last_long_collect_interval = tot; } // If the current interval is larger than half the live data decrease the interval - } else { + } + else { int64_t half = (live_bytes / 2); if (gc_num.interval > half) gc_num.interval = half; @@ -3427,7 +3622,7 @@ void gc_mark_queue_all_roots(jl_ptls_t ptls, jl_gc_markqueue_t *mq) assert(gc_n_threads); for (size_t i = 0; i < gc_n_threads; i++) { jl_ptls_t ptls2 = gc_all_tls_states[i]; - if (ptls2) + if (ptls2 != NULL) gc_queue_thread_local(mq, ptls2); } gc_mark_roots(mq); @@ -3468,14 +3663,18 @@ void jl_init_thread_heap(jl_ptls_t ptls) gc_cache->nbig_obj = 0; // Initialize GC mark-queue - size_t init_size = (1 << 18); jl_gc_markqueue_t *mq = &ptls->mark_queue; - mq->start = (jl_value_t **)malloc_s(init_size * sizeof(jl_value_t *)); - mq->current = mq->start; - mq->end = mq->start + init_size; - size_t cq_init_size = (1 << 14); - mq->current_chunk = mq->chunk_start = (jl_gc_chunk_t *)malloc_s(cq_init_size * sizeof(jl_gc_chunk_t)); - mq->chunk_end = mq->chunk_start + cq_init_size; + ws_queue_t *cq = &mq->chunk_queue; + ws_array_t *wsa = create_ws_array(GC_CHUNK_QUEUE_INIT_SIZE, sizeof(jl_gc_chunk_t)); + jl_atomic_store_relaxed(&cq->top, 0); + jl_atomic_store_relaxed(&cq->bottom, 0); + jl_atomic_store_relaxed(&cq->array, wsa); + ws_queue_t *q = &mq->ptr_queue; + ws_array_t *wsa2 = create_ws_array(GC_PTR_QUEUE_INIT_SIZE, sizeof(jl_value_t *)); + jl_atomic_store_relaxed(&q->top, 0); + jl_atomic_store_relaxed(&q->bottom, 0); + jl_atomic_store_relaxed(&q->array, wsa2); + arraylist_new(&mq->reclaim_set, 32); memset(&ptls->gc_num, 0, sizeof(ptls->gc_num)); jl_atomic_store_relaxed(&ptls->gc_num.allocd, -(int64_t)gc_num.interval); @@ -3489,6 +3688,8 @@ void jl_gc_init(void) JL_MUTEX_INIT(&finalizers_lock, "finalizers_lock"); uv_mutex_init(&gc_cache_lock); uv_mutex_init(&gc_perm_lock); + uv_mutex_init(&gc_threads_lock); + uv_cond_init(&gc_threads_cond); jl_gc_init_page(); jl_gc_debug_init(); diff --git a/src/gc.h b/src/gc.h index 3961aeecada8c..236d9067f4a6c 100644 --- a/src/gc.h +++ b/src/gc.h @@ -84,26 +84,33 @@ typedef struct { uint64_t total_mark_time; } jl_gc_num_t; +// Array chunks (work items representing suffixes of +// large arrays of pointers left to be marked) + typedef enum { - GC_empty_chunk, - GC_objary_chunk, - GC_ary8_chunk, - GC_ary16_chunk, - GC_finlist_chunk, + GC_empty_chunk = 0, // for sentinel representing no items left in chunk queue + GC_objary_chunk, // for chunk of object array + GC_ary8_chunk, // for chunk of array with 8 bit field descriptors + GC_ary16_chunk, // for chunk of array with 16 bit field descriptors + GC_finlist_chunk, // for chunk of finalizer list } gc_chunk_id_t; typedef struct _jl_gc_chunk_t { gc_chunk_id_t cid; - struct _jl_value_t *parent; - struct _jl_value_t **begin; - struct _jl_value_t **end; - void *elem_begin; - void *elem_end; - uint32_t step; - uintptr_t nptr; + struct _jl_value_t *parent; // array owner + struct _jl_value_t **begin; // pointer to first element that needs scanning + struct _jl_value_t **end; // pointer to last element that needs scanning + void *elem_begin; // used to scan pointers within objects when marking `ary8` or `ary16` + void *elem_end; // used to scan pointers within objects when marking `ary8` or `ary16` + uint32_t step; // step-size used when marking objarray + uintptr_t nptr; // (`nptr` & 0x1) if array has young element and (`nptr` & 0x2) if array owner is old } jl_gc_chunk_t; -#define MAX_REFS_AT_ONCE (1 << 16) +#define GC_CHUNK_BATCH_SIZE (1 << 16) // maximum number of references that can be processed + // without creating a chunk + +#define GC_PTR_QUEUE_INIT_SIZE (1 << 18) // initial size of queue of `jl_value_t *` +#define GC_CHUNK_QUEUE_INIT_SIZE (1 << 14) // initial size of chunk-queue // layout for big (>2k) objects @@ -377,8 +384,8 @@ void gc_mark_finlist_(jl_gc_markqueue_t *mq, jl_value_t **fl_begin, jl_value_t **fl_end) JL_NOTSAFEPOINT; void gc_mark_finlist(jl_gc_markqueue_t *mq, arraylist_t *list, size_t start) JL_NOTSAFEPOINT; -void gc_mark_loop_(jl_ptls_t ptls, jl_gc_markqueue_t *mq); -void gc_mark_loop(jl_ptls_t ptls); +void gc_mark_loop_serial_(jl_ptls_t ptls, jl_gc_markqueue_t *mq); +void gc_mark_loop_serial(jl_ptls_t ptls); void sweep_stack_pools(void); void jl_gc_debug_init(void); diff --git a/src/init.c b/src/init.c index b7f3ffb644b01..31c6c6d28a4e3 100644 --- a/src/init.c +++ b/src/init.c @@ -868,6 +868,7 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_ if (jl_base_module == NULL) { // nthreads > 1 requires code in Base jl_atomic_store_relaxed(&jl_n_threads, 1); + jl_n_gcthreads = 0; } jl_start_threads(); diff --git a/src/jl_exported_data.inc b/src/jl_exported_data.inc index 52f6cb11d8c0f..51e73ad22105d 100644 --- a/src/jl_exported_data.inc +++ b/src/jl_exported_data.inc @@ -131,6 +131,7 @@ #define JL_EXPORTED_DATA_SYMBOLS(XX) \ XX(jl_n_threadpools, int) \ XX(jl_n_threads, _Atomic(int)) \ + XX(jl_n_gcthreads, int) \ XX(jl_options, jl_options_t) \ // end of file diff --git a/src/jloptions.c b/src/jloptions.c index 7f41aeefd1195..4c0b59f811643 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -40,6 +40,7 @@ JL_DLLEXPORT void jl_init_options(void) NULL, // cpu_target ("native", "core2", etc...) 0, // nthreadpools 0, // nthreads + 0, // ngcthreads NULL, // nthreads_per_pool 0, // nprocs NULL, // machine_file @@ -128,6 +129,7 @@ static const char opts[] = " interface if supported (Linux and Windows) or to the number of CPU\n" " threads if not supported (MacOS) or if process affinity is not\n" " configured, and sets M to 1.\n" + " --gcthreads=N Use N threads for GC, set to half of the number of compute threads if unspecified.\n" " -p, --procs {N|auto} Integer value N launches N additional local worker processes\n" " \"auto\" launches as many workers as the number of local CPU threads (logical cores)\n" " --machine-file Run processes on hosts listed in \n\n" @@ -251,6 +253,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) opt_strip_metadata, opt_strip_ir, opt_heap_size_hint, + opt_gc_threads, }; static const char* const shortopts = "+vhqH:e:E:L:J:C:it:p:O:g:"; static const struct option longopts[] = { @@ -275,6 +278,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) { "cpu-target", required_argument, 0, 'C' }, { "procs", required_argument, 0, 'p' }, { "threads", required_argument, 0, 't' }, + { "gcthreads", required_argument, 0, opt_gc_threads }, { "machine-file", required_argument, 0, opt_machine_file }, { "project", optional_argument, 0, opt_project }, { "color", required_argument, 0, opt_color }, @@ -815,6 +819,13 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) if (jl_options.heap_size_hint == 0) jl_errorf("julia: invalid argument to --heap-size-hint without memory size specified"); + break; + case opt_gc_threads: + errno = 0; + long ngcthreads = strtol(optarg, &endptr, 10); + if (errno != 0 || optarg == endptr || *endptr != 0 || ngcthreads < 1 || ngcthreads >= INT16_MAX) + jl_errorf("julia: --gcthreads=; n must be an integer >= 1"); + jl_options.ngcthreads = (int16_t)ngcthreads; break; default: jl_errorf("julia: unhandled option -- %c\n" diff --git a/src/jloptions.h b/src/jloptions.h index d0aba777027e7..c44a8cfe05770 100644 --- a/src/jloptions.h +++ b/src/jloptions.h @@ -15,6 +15,7 @@ typedef struct { const char *cpu_target; int8_t nthreadpools; int16_t nthreads; + int16_t ngcthreads; const int16_t *nthreads_per_pool; int32_t nprocs; const char *machine_file; diff --git a/src/julia.h b/src/julia.h index c20af4384a858..ab8959cb3c6c8 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1689,6 +1689,7 @@ JL_DLLEXPORT jl_sym_t *jl_get_ARCH(void) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_get_libllvm(void) JL_NOTSAFEPOINT; extern JL_DLLIMPORT int jl_n_threadpools; extern JL_DLLIMPORT _Atomic(int) jl_n_threads; +extern JL_DLLIMPORT int jl_n_gcthreads; extern JL_DLLIMPORT int *jl_n_threads_per_pool; // environment entries diff --git a/src/julia_threads.h b/src/julia_threads.h index 6439caa0aa2ee..29f152172d2ab 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -4,6 +4,7 @@ #ifndef JL_THREADS_H #define JL_THREADS_H +#include "work-stealing-queue.h" #include "julia_atomics.h" #ifndef _OS_WINDOWS_ #include "pthread.h" @@ -171,12 +172,9 @@ typedef struct { } jl_thread_heap_t; typedef struct { - struct _jl_gc_chunk_t *chunk_start; - struct _jl_gc_chunk_t *current_chunk; - struct _jl_gc_chunk_t *chunk_end; - struct _jl_value_t **start; - struct _jl_value_t **current; - struct _jl_value_t **end; + ws_queue_t chunk_queue; + ws_queue_t ptr_queue; + arraylist_t reclaim_set; } jl_gc_markqueue_t; typedef struct { diff --git a/src/options.h b/src/options.h index 5253bcab0456f..b535d5ad4566f 100644 --- a/src/options.h +++ b/src/options.h @@ -131,6 +131,9 @@ // threadpools specification #define THREADPOOLS_NAME "JULIA_THREADPOOLS" +// GC threads +#define NUM_GC_THREADS_NAME "JULIA_NUM_GC_THREADS" + // affinitization behavior #define MACHINE_EXCLUSIVE_NAME "JULIA_EXCLUSIVE" #define DEFAULT_MACHINE_EXCLUSIVE 0 diff --git a/src/partr.c b/src/partr.c index b51f5eee8089f..5c7a09ed0bd9a 100644 --- a/src/partr.c +++ b/src/partr.c @@ -108,7 +108,37 @@ void jl_init_threadinginfra(void) void JL_NORETURN jl_finish_task(jl_task_t *t); -// thread function: used by all except the main thread +extern uv_mutex_t gc_threads_lock; +extern uv_cond_t gc_threads_cond; +extern _Atomic(int) gc_n_threads_marking; +extern void gc_mark_loop_parallel(jl_ptls_t ptls, int master); + +// gc thread function +void jl_gc_threadfun(void *arg) +{ + jl_threadarg_t *targ = (jl_threadarg_t*)arg; + + // initialize this thread (set tid and create heap) + jl_ptls_t ptls = jl_init_threadtls(targ->tid); + + // wait for all threads + jl_gc_state_set(ptls, JL_GC_STATE_WAITING, 0); + uv_barrier_wait(targ->barrier); + + // free the thread argument here + free(targ); + + while (1) { + uv_mutex_lock(&gc_threads_lock); + while (jl_atomic_load(&gc_n_threads_marking) == 0) { + uv_cond_wait(&gc_threads_cond, &gc_threads_lock); + } + uv_mutex_unlock(&gc_threads_lock); + gc_mark_loop_parallel(ptls, 0); + } +} + +// thread function: used by all mutator threads except the main thread void jl_threadfun(void *arg) { jl_threadarg_t *targ = (jl_threadarg_t*)arg; @@ -448,7 +478,6 @@ JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q, break; } uv_cond_wait(&ptls->wake_signal, &ptls->sleep_lock); - // TODO: help with gc work here, if applicable } assert(jl_atomic_load_relaxed(&ptls->sleep_check_state) == not_sleeping); uv_mutex_unlock(&ptls->sleep_lock); diff --git a/src/threading.c b/src/threading.c index 6718a47f5e836..2653bb8abd629 100644 --- a/src/threading.c +++ b/src/threading.c @@ -589,6 +589,8 @@ static void jl_check_tls(void) JL_DLLEXPORT const int jl_tls_elf_support = 0; #endif +extern int gc_first_tid; + // interface to Julia; sets up to make the runtime thread-safe void jl_init_threading(void) { @@ -641,13 +643,32 @@ void jl_init_threading(void) } } - jl_all_tls_states_size = nthreads + nthreadsi; + int16_t ngcthreads = jl_options.ngcthreads - 1; + if (ngcthreads == -1 && + (cp = getenv(NUM_GC_THREADS_NAME))) { // ENV[NUM_GC_THREADS_NAME] specified + + ngcthreads = (uint64_t)strtol(cp, NULL, 10) - 1; + } + if (ngcthreads == -1) { + // if `--gcthreads` was not specified, set the number of GC threads + // to half of compute threads + if (nthreads <= 1) { + ngcthreads = 0; + } + else { + ngcthreads = (nthreads / 2) - 1; + } + } + + jl_all_tls_states_size = nthreads + nthreadsi + ngcthreads; jl_n_threads_per_pool = (int*)malloc_s(2 * sizeof(int)); jl_n_threads_per_pool[0] = nthreadsi; jl_n_threads_per_pool[1] = nthreads; jl_atomic_store_release(&jl_all_tls_states, (jl_ptls_t*)calloc(jl_all_tls_states_size, sizeof(jl_ptls_t))); jl_atomic_store_release(&jl_n_threads, jl_all_tls_states_size); + jl_n_gcthreads = ngcthreads; + gc_first_tid = nthreads; } static uv_barrier_t thread_init_done; @@ -655,6 +676,7 @@ static uv_barrier_t thread_init_done; void jl_start_threads(void) { int nthreads = jl_atomic_load_relaxed(&jl_n_threads); + int ngcthreads = jl_n_gcthreads; int cpumasksize = uv_cpumask_size(); char *cp; int i, exclusive; @@ -687,15 +709,23 @@ void jl_start_threads(void) // create threads uv_barrier_init(&thread_init_done, nthreads); + // GC/System threads need to be after the worker threads. + int nworker_threads = nthreads - ngcthreads; + for (i = 1; i < nthreads; ++i) { jl_threadarg_t *t = (jl_threadarg_t *)malloc_s(sizeof(jl_threadarg_t)); // ownership will be passed to the thread t->tid = i; t->barrier = &thread_init_done; - uv_thread_create(&uvtid, jl_threadfun, t); - if (exclusive) { - mask[i] = 1; - uv_thread_setaffinity(&uvtid, mask, NULL, cpumasksize); - mask[i] = 0; + if (i < nworker_threads) { + uv_thread_create(&uvtid, jl_threadfun, t); + if (exclusive) { + mask[i] = 1; + uv_thread_setaffinity(&uvtid, mask, NULL, cpumasksize); + mask[i] = 0; + } + } + else { + uv_thread_create(&uvtid, jl_gc_threadfun, t); } uv_thread_detach(&uvtid); } diff --git a/src/threading.h b/src/threading.h index 4df6815124eb9..40792a2889e44 100644 --- a/src/threading.h +++ b/src/threading.h @@ -25,6 +25,7 @@ jl_ptls_t jl_init_threadtls(int16_t tid) JL_NOTSAFEPOINT; // provided by a threading infrastructure void jl_init_threadinginfra(void); +void jl_gc_threadfun(void *arg); void jl_threadfun(void *arg); #ifdef __cplusplus diff --git a/src/work-stealing-queue.h b/src/work-stealing-queue.h new file mode 100644 index 0000000000000..38429e02886e9 --- /dev/null +++ b/src/work-stealing-queue.h @@ -0,0 +1,102 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#ifndef WORK_STEALING_QUEUE_H +#define WORK_STEALING_QUEUE_H + +#include "julia_atomics.h" +#include "assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// ======= +// Chase and Lev's work-stealing queue, optimized for +// weak memory models by Le et al. +// +// * Chase D., Lev Y. Dynamic Circular Work-Stealing queue +// * Le N. M. et al. Correct and Efficient Work-Stealing for +// Weak Memory Models +// ======= + +typedef struct { + char *buffer; + int32_t capacity; + int32_t mask; +} ws_array_t; + +static inline ws_array_t *create_ws_array(size_t capacity, int32_t eltsz) JL_NOTSAFEPOINT +{ + ws_array_t *a = (ws_array_t *)malloc_s(sizeof(ws_array_t)); + a->buffer = (char *)malloc_s(capacity * eltsz); + a->capacity = capacity; + a->mask = capacity - 1; + return a; +} + +typedef struct { + _Atomic(int64_t) top; + _Atomic(int64_t) bottom; + _Atomic(ws_array_t *) array; +} ws_queue_t; + +static inline ws_array_t *ws_queue_push(ws_queue_t *q, void *elt, int32_t eltsz) JL_NOTSAFEPOINT +{ + int64_t b = jl_atomic_load_relaxed(&q->bottom); + int64_t t = jl_atomic_load_acquire(&q->top); + ws_array_t *ary = jl_atomic_load_relaxed(&q->array); + ws_array_t *old_ary = NULL; + if (__unlikely(b - t > ary->capacity - 1)) { + ws_array_t *new_ary = create_ws_array(2 * ary->capacity, eltsz); + for (int i = 0; i < ary->capacity; i++) { + memcpy(new_ary->buffer + ((t + i) & new_ary->mask) * eltsz, ary->buffer + ((t + i) & ary->mask) * eltsz, eltsz); + } + jl_atomic_store_release(&q->array, new_ary); + old_ary = ary; + ary = new_ary; + } + memcpy(ary->buffer + (b & ary->mask) * eltsz, elt, eltsz); + jl_fence_release(); + jl_atomic_store_relaxed(&q->bottom, b + 1); + return old_ary; +} + +static inline void ws_queue_pop(ws_queue_t *q, void *dest, int32_t eltsz) JL_NOTSAFEPOINT +{ + int64_t b = jl_atomic_load_relaxed(&q->bottom) - 1; + ws_array_t *ary = jl_atomic_load_relaxed(&q->array); + jl_atomic_store_relaxed(&q->bottom, b); + jl_fence(); + int64_t t = jl_atomic_load_relaxed(&q->top); + if (__likely(t <= b)) { + memcpy(dest, ary->buffer + (b & ary->mask) * eltsz, eltsz); + if (t == b) { + if (!jl_atomic_cmpswap(&q->top, &t, t + 1)) + memset(dest, 0, eltsz); + jl_atomic_store_relaxed(&q->bottom, b + 1); + } + } + else { + memset(dest, 0, eltsz); + jl_atomic_store_relaxed(&q->bottom, b + 1); + } +} + +static inline void ws_queue_steal_from(ws_queue_t *q, void *dest, int32_t eltsz) JL_NOTSAFEPOINT +{ + int64_t t = jl_atomic_load_acquire(&q->top); + jl_fence(); + int64_t b = jl_atomic_load_acquire(&q->bottom); + if (t < b) { + ws_array_t *ary = jl_atomic_load_relaxed(&q->array); + memcpy(dest, ary->buffer + (t & ary->mask) * eltsz, eltsz); + if (!jl_atomic_cmpswap(&q->top, &t, t + 1)) + memset(dest, 0, eltsz); + } +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/stdlib/Distributed/src/cluster.jl b/stdlib/Distributed/src/cluster.jl index d2cbe55e63270..3fd3d63108297 100644 --- a/stdlib/Distributed/src/cluster.jl +++ b/stdlib/Distributed/src/cluster.jl @@ -1331,7 +1331,10 @@ function process_opts(opts) end # Propagate --threads to workers - exeflags = opts.nthreads > 0 ? `--threads=$(opts.nthreads)` : `` + threads = opts.nthreads > 0 ? `--threads=$(opts.nthreads)` : `` + gcthreads = opts.ngcthreads > 0 ? `--gcthreads=$(opts.ngcthreads)` : `` + + exeflags = `$threads $gcthreads` # add processors if opts.nprocs > 0 diff --git a/test/choosetests.jl b/test/choosetests.jl index 627771206b727..18af88ea191e9 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -21,7 +21,7 @@ const TESTNAMES = [ "combinatorics", "sysinfo", "env", "rounding", "ranges", "mod2pi", "euler", "show", "client", "errorshow", "sets", "goto", "llvmcall", "llvmcall2", "ryu", - "some", "meta", "stacktraces", "docs", + "some", "meta", "stacktraces", "docs", "gc", "misc", "threads", "stress", "binaryplatforms", "atexit", "enums", "cmdlineargs", "int", "interpreter", "checked", "bitset", "floatfuncs", "precompile", diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 903f6e0663b5d..389b195d97935 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -341,6 +341,24 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` @test p.exitcode == 1 && p.termsignal == 0 end + # --gcthreads + code = "print(Threads.ngcthreads())" + cpu_threads = ccall(:jl_effective_threads, Int32, ()) + @test (cpu_threads == 1 ? "1" : string(div(cpu_threads, 2))) == + read(`$exename --threads auto -e $code`, String) == + read(`$exename --threads=auto -e $code`, String) == + read(`$exename -tauto -e $code`, String) == + read(`$exename -t auto -e $code`, String) + for nt in (nothing, "1") + withenv("JULIA_NUM_GC_THREADS" => nt) do + @test read(`$exename --gcthreads=2 -e $code`, String) == "2" + end + end + + withenv("JULIA_NUM_GC_THREADS" => 2) do + @test read(`$exename -e $code`, String) == "2" + end + # --machine-file # this does not check that machine file works, # only that the filename gets correctly passed to the option struct diff --git a/test/gc.jl b/test/gc.jl new file mode 100644 index 0000000000000..9cc9d753dfc09 --- /dev/null +++ b/test/gc.jl @@ -0,0 +1,18 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +using Test + +function run_gctest(file) + let cmd = `$(Base.julia_cmd()) --depwarn=error --rr-detach --startup-file=no $file` + for test_nthreads in (1, 2, 4) + new_env = copy(ENV) + new_env["JULIA_NUM_THREADS"] = string(test_nthreads) + new_env["JULIA_NUM_GC_THREADS"] = string(test_nthreads) + @time run(pipeline(setenv(cmd, new_env), stdout = stdout, stderr = stderr)) + end + end +end + +@time run_gctest("gc/binarytree.jl") +@time run_gctest("gc/linkedlist.jl") +@time run_gctest("gc/objarray.jl") diff --git a/test/gc/binarytree.jl b/test/gc/binarytree.jl new file mode 100644 index 0000000000000..3089e2d2ce869 --- /dev/null +++ b/test/gc/binarytree.jl @@ -0,0 +1,53 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module BinaryTreeMutable + +# Adopted from +# https://benchmarksgame-team.pages.debian.net/benchmarksgame/description/binarytrees.html#binarytrees + +using Base.Threads +using Printf + +mutable struct Node + l::Union{Nothing, Node} + r::Union{Nothing, Node} +end + +function make(n::Int) + return n === 0 ? Node(nothing, nothing) : Node(make(n-1), make(n-1)) +end + +function check(node::Node) + return 1 + (node.l === nothing ? 0 : check(node.l) + check(node.r)) +end + +function binary_trees(io, n::Int) + @printf io "stretch tree of depth %jd\t check: %jd\n" n+1 check(make(n+1)) + + long_tree = make(n) + minDepth = 4 + resultSize = div((n - minDepth), 2) + 1 + results = Vector{String}(undef, resultSize) + Threads.@threads for depth in minDepth:2:n + c = 0 + niter = 1 << (n - depth + minDepth) + for _ in 1:niter + c += check(make(depth)) + end + index = div((depth - minDepth),2) + 1 + results[index] = @sprintf "%jd\t trees of depth %jd\t check: %jd\n" niter depth c + end + + for i in results + write(io, i) + end + + @printf io "long lived tree of depth %jd\t check: %jd\n" n check(long_tree) +end + +end #module + +using .BinaryTreeMutable + +BinaryTreeMutable.binary_trees(devnull, 20) +GC.gc() diff --git a/test/gc/linkedlist.jl b/test/gc/linkedlist.jl new file mode 100644 index 0000000000000..c447a9680326d --- /dev/null +++ b/test/gc/linkedlist.jl @@ -0,0 +1,21 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +mutable struct ListNode + key::Int64 + next::ListNode + ListNode() = new() + ListNode(x)= new(x) + ListNode(x,y) = new(x,y); +end + +function list(n=128) + start::ListNode = ListNode(1) + current::ListNode = start + for i = 2:(n*1024^2) + current = ListNode(i,current) + end + return current.key +end + +_ = list() +GC.gc() diff --git a/test/gc/objarray.jl b/test/gc/objarray.jl new file mode 100644 index 0000000000000..4b4cb67c42eac --- /dev/null +++ b/test/gc/objarray.jl @@ -0,0 +1,35 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +using Random: seed! +seed!(1) + +abstract type Cell end + +struct CellA<:Cell + a::Ref{Int} +end + +struct CellB<:Cell + b::String +end + +function fillcells!(mc::Array{Cell}) + for ind in eachindex(mc) + mc[ind] = ifelse(rand() > 0.5, CellA(ind), CellB(string(ind))) + end + return mc +end + +function work(size) + mcells = Array{Cell}(undef, size, size) + fillcells!(mcells) +end + +function run(maxsize) + Threads.@threads for i in 1:maxsize + work(i*500) + end +end + +run(4) +GC.gc() From 645f7af27a011d304c7f41f67e3eead00e6fc462 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 28 Apr 2023 19:47:39 -0400 Subject: [PATCH 617/775] Fix starting range in REPLCompletions (#49547) The new completion for `var"` fields (#49294) failed when the `var"` was at the end of the completion query, e.g. in `WeirdNames().var"`. This is because we have the following behavior: ``` julia> findprev(==('x'), "x", 1) 1 julia> findprev(==('x'), "x", 2) ``` REPLCompletions attempt to find `.` starting after the `var"`, which in this case is at the end of the string. Of course, the index was probably off by one anyway, because we didn't want to match `var".`, but nevertheless, I find this behavior surprising (ref also [1]). For now, fix this by starting the search before the `var"`, but we may want to revisit the `findprev` behavior also. [1] https://github.com/JuliaLang/julia/pull/35742/files#r420945975 --- stdlib/REPL/src/REPLCompletions.jl | 2 +- stdlib/REPL/test/replcompletions.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 149920381dcc8..e09e3b2aa9e6b 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -1021,7 +1021,7 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif ok, ret = bslash_completions(string, pos) ok && return ret startpos = first(varrange) + 4 - dotpos = something(findprev(isequal('.'), string, startpos), 0) + dotpos = something(findprev(isequal('.'), string, first(varrange)-1), 0) return complete_identifiers!(Completion[], ffunc, context_module, string, string[startpos:pos], pos, dotpos, startpos) # otherwise... diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 4577be19fa9ac..036cda53aa2a2 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -1819,7 +1819,7 @@ let s = "var\"complicated " @test c == Any["var\"complicated symbol with spaces\""] end -let s = "WeirdNames().var\"oh " +for s in ("WeirdNames().var\"oh ", "WeirdNames().var\"") c, r = test_complete_foo(s) @test c == Any["var\"oh no!\"", "var\"oh yes!\""] end From 219dc107dabe24a3a99b2e9a5b33b777437cc27b Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Fri, 28 Apr 2023 20:21:33 -0500 Subject: [PATCH 618/775] Clarify Vararg/UnionAll deprecation warning (#49558) Fixes #49553 --- src/jltypes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jltypes.c b/src/jltypes.c index 622f4f3222555..792588ed231b4 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -831,8 +831,8 @@ JL_DLLEXPORT jl_value_t *jl_type_unionall(jl_tvar_t *v, jl_value_t *body) if (jl_is_vararg(body)) { if (jl_options.depwarn) { if (jl_options.depwarn == JL_OPTIONS_DEPWARN_ERROR) - jl_error("Wrapping `Vararg` directly in UnionAll is deprecated (wrap the tuple instead)."); - jl_printf(JL_STDERR, "WARNING: Wrapping `Vararg` directly in UnionAll is deprecated (wrap the tuple instead).\n"); + jl_error("Wrapping `Vararg` directly in UnionAll is deprecated (wrap the tuple instead).\nYou may need to write `f(x::Vararg{T})` rather than `f(x::Vararg{<:T})` or `f(x::Vararg{T}) where T` instead of `f(x::Vararg{T} where T)`."); + jl_printf(JL_STDERR, "WARNING: Wrapping `Vararg` directly in UnionAll is deprecated (wrap the tuple instead).\nYou may need to write `f(x::Vararg{T})` rather than `f(x::Vararg{<:T})` or `f(x::Vararg{T}) where T` instead of `f(x::Vararg{T} where T)`.\n"); } jl_vararg_t *vm = (jl_vararg_t*)body; int T_has_tv = vm->T && jl_has_typevar(vm->T, v); From faced27dac38a55880ba87180958076a218b4ff2 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Sat, 29 Apr 2023 19:14:18 -0400 Subject: [PATCH 619/775] Export offsets necessary for external codegen (#49548) --- src/init.c | 4 ++++ src/jl_exported_data.inc | 2 ++ src/julia.h | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/init.c b/src/init.c index 31c6c6d28a4e3..596ed5d30ee4e 100644 --- a/src/init.c +++ b/src/init.c @@ -771,6 +771,10 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) jl_init_intrinsic_properties(); + // Important offset for external codegen. + jl_task_gcstack_offset = offsetof(jl_task_t, gcstack); + jl_task_ptls_offset = offsetof(jl_task_t, ptls); + jl_prep_sanitizers(); void *stack_lo, *stack_hi; jl_init_stack_limits(1, &stack_lo, &stack_hi); diff --git a/src/jl_exported_data.inc b/src/jl_exported_data.inc index 51e73ad22105d..092a48be81930 100644 --- a/src/jl_exported_data.inc +++ b/src/jl_exported_data.inc @@ -133,5 +133,7 @@ XX(jl_n_threads, _Atomic(int)) \ XX(jl_n_gcthreads, int) \ XX(jl_options, jl_options_t) \ + XX(jl_task_gcstack_offset, int) \ + XX(jl_task_ptls_offset, int) \ // end of file diff --git a/src/julia.h b/src/julia.h index ab8959cb3c6c8..c7f4e9f1334e9 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1992,6 +1992,9 @@ JL_DLLEXPORT void JL_NORETURN jl_no_exc_handler(jl_value_t *e, jl_task_t *ct); JL_DLLEXPORT JL_CONST_FUNC jl_gcframe_t **(jl_get_pgcstack)(void) JL_GLOBALLY_ROOTED JL_NOTSAFEPOINT; #define jl_current_task (container_of(jl_get_pgcstack(), jl_task_t, gcstack)) +extern JL_DLLIMPORT int jl_task_gcstack_offset; +extern JL_DLLIMPORT int jl_task_ptls_offset; + #include "julia_locks.h" // requires jl_task_t definition JL_DLLEXPORT void jl_enter_handler(jl_handler_t *eh); From b4d153025172fd04cdfc64e00951b2d1b6b7794c Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Sat, 29 Apr 2023 20:21:26 -0300 Subject: [PATCH 620/775] Fix dyld lock not getting unlocked on invalid threads. (#49446) --- src/signals-mach.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/signals-mach.c b/src/signals-mach.c index 2bb26976b0d61..13f5f6923cec3 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -614,12 +614,12 @@ void *mach_profile_listener(void *arg) if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) _dyld_atfork_prepare(); // briefly acquire the dlsym lock host_thread_state_t state; - if (!jl_thread_suspend_and_get_state2(i, &state)) - continue; + int valid_thread = jl_thread_suspend_and_get_state2(i, &state); unw_context_t *uc = (unw_context_t*)&state; if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) _dyld_atfork_parent(); // quickly release the dlsym lock - + if (!valid_thread) + continue; if (running) { #ifdef LLVMLIBUNWIND /* From c83dff9cd40e5a5c87dc241a10cefc3ddc02c326 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 1 May 2023 10:27:06 -0400 Subject: [PATCH 621/775] add `__init__()` calls to `time_imports` macro (#49529) --- NEWS.md | 1 + base/loading.jl | 36 +++++++++++++++++++++++++++++++++- contrib/generate_precompile.jl | 1 + doc/src/devdocs/init.md | 2 +- src/jl_exported_funcs.inc | 2 +- src/module.c | 20 ++++++++----------- 6 files changed, 47 insertions(+), 15 deletions(-) diff --git a/NEWS.md b/NEWS.md index bf7ae28c236ce..c87f45265ea86 100644 --- a/NEWS.md +++ b/NEWS.md @@ -122,6 +122,7 @@ Standard library changes #### InteractiveUtils * `code_native` and `@code_native` now default to intel syntax instead of AT&T. + * `@time_imports` now shows the timing of any module `__init__()`s that are run ([#49529]) Deprecated or removed --------------------- diff --git a/base/loading.jl b/base/loading.jl index 83737dd7d1fd1..5f4f3a73af749 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1071,7 +1071,9 @@ function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String) if !isempty(inits) unlock(require_lock) # temporarily _unlock_ during these callbacks try - ccall(:jl_init_restored_modules, Cvoid, (Any,), inits) + for (i, mod) in pairs(inits) + run_module_init(mod, i) + end finally lock(require_lock) end @@ -1079,6 +1081,38 @@ function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String) return restored end +function run_module_init(mod::Module, i::Int=1) + # `i` informs ordering for the `@time_imports` report formatting + if TIMING_IMPORTS[] == 0 + ccall(:jl_init_restored_module, Cvoid, (Any,), mod) + else + if isdefined(mod, :__init__) + connector = i > 1 ? "├" : "┌" + printstyled(" $connector ", color = :light_black) + + elapsedtime = time_ns() + cumulative_compile_timing(true) + compile_elapsedtimes = cumulative_compile_time_ns() + + ccall(:jl_init_restored_module, Cvoid, (Any,), mod) + + elapsedtime = (time_ns() - elapsedtime) / 1e6 + cumulative_compile_timing(false); + comp_time, recomp_time = (cumulative_compile_time_ns() .- compile_elapsedtimes) ./ 1e6 + + print(round(elapsedtime, digits=1), " ms $mod.__init__() ") + if comp_time > 0 + printstyled(Ryu.writefixed(Float64(100 * comp_time / elapsedtime), 2), "% compilation time", color = Base.info_color()) + end + if recomp_time > 0 + perc = Float64(100 * recomp_time / comp_time) + printstyled(" (", perc < 1 ? "<1" : Ryu.writefixed(perc, 0), "% recompilation)", color = Base.warn_color()) + end + println() + end + end +end + function run_package_callbacks(modkey::PkgId) run_extension_callbacks(modkey) assert_havelock(require_lock) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 68650836fd6b4..c7ec9d0988996 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -56,6 +56,7 @@ precompile(Base.CoreLogging.current_logger_for_env, (Base.CoreLogging.LogLevel, precompile(Base.CoreLogging.current_logger_for_env, (Base.CoreLogging.LogLevel, Symbol, Module)) precompile(Base.CoreLogging.env_override_minlevel, (Symbol, Module)) precompile(Base.StackTraces.lookup, (Ptr{Nothing},)) +precompile(Tuple{typeof(Base.run_module_init), Module, Int}) """ for T in (Float16, Float32, Float64), IO in (IOBuffer, IOContext{IOBuffer}, Base.TTY, IOContext{Base.TTY}) diff --git a/doc/src/devdocs/init.md b/doc/src/devdocs/init.md index 981a19b13fcf3..1e0e1173f8695 100644 --- a/doc/src/devdocs/init.md +++ b/doc/src/devdocs/init.md @@ -118,7 +118,7 @@ Other signals (`SIGINFO, SIGBUS, SIGILL, SIGTERM, SIGABRT, SIGQUIT, SIGSYS` and hooked up to [`sigdie_handler()`](https://github.com/JuliaLang/julia/blob/master/src/signals-unix.c) which prints a backtrace. -[`jl_init_restored_modules()`](https://github.com/JuliaLang/julia/blob/master/src/staticdata.c) calls +[`jl_init_restored_module()`](https://github.com/JuliaLang/julia/blob/master/src/staticdata.c) calls [`jl_module_run_initializer()`](https://github.com/JuliaLang/julia/blob/master/src/module.c) for each deserialized module to run the `__init__()` function. diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 02355d7003605..e82d43de7937c 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -258,7 +258,7 @@ XX(jl_infer_thunk) \ XX(jl_init) \ XX(jl_init_options) \ - XX(jl_init_restored_modules) \ + XX(jl_init_restored_module) \ XX(jl_init_with_image) \ XX(jl_init_with_image__threading) \ XX(jl_init__threading) \ diff --git a/src/module.c b/src/module.c index 3428c5e8b59f9..d504cf7738767 100644 --- a/src/module.c +++ b/src/module.c @@ -972,19 +972,15 @@ JL_DLLEXPORT void jl_clear_implicit_imports(jl_module_t *m) JL_UNLOCK(&m->lock); } -JL_DLLEXPORT void jl_init_restored_modules(jl_array_t *init_order) +JL_DLLEXPORT void jl_init_restored_module(jl_value_t *mod) { - int i, l = jl_array_len(init_order); - for (i = 0; i < l; i++) { - jl_value_t *mod = jl_array_ptr_ref(init_order, i); - if (!jl_generating_output() || jl_options.incremental) { - jl_module_run_initializer((jl_module_t*)mod); - } - else { - if (jl_module_init_order == NULL) - jl_module_init_order = jl_alloc_vec_any(0); - jl_array_ptr_1d_push(jl_module_init_order, mod); - } + if (!jl_generating_output() || jl_options.incremental) { + jl_module_run_initializer((jl_module_t*)mod); + } + else { + if (jl_module_init_order == NULL) + jl_module_init_order = jl_alloc_vec_any(0); + jl_array_ptr_1d_push(jl_module_init_order, mod); } } From 0b0ae1a3eaaf04cf6b0c4eee9fc6a00061f99080 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Mon, 1 May 2023 21:07:11 -0400 Subject: [PATCH 622/775] Ensure that new GC tests actually succeed (#49579) --- test/gc.jl | 17 ++++++++++++----- test/gc/binarytree.jl | 3 ++- test/gc/chunks.jl | 17 +++++++++++++++++ test/gc/linkedlist.jl | 6 ++++-- test/gc/objarray.jl | 5 +++-- test/runtests.jl | 1 + 6 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 test/gc/chunks.jl diff --git a/test/gc.jl b/test/gc.jl index 9cc9d753dfc09..ecf71fe51f6ad 100644 --- a/test/gc.jl +++ b/test/gc.jl @@ -4,15 +4,22 @@ using Test function run_gctest(file) let cmd = `$(Base.julia_cmd()) --depwarn=error --rr-detach --startup-file=no $file` - for test_nthreads in (1, 2, 4) + @testset for test_nthreads in (1, 2, 4) new_env = copy(ENV) new_env["JULIA_NUM_THREADS"] = string(test_nthreads) new_env["JULIA_NUM_GC_THREADS"] = string(test_nthreads) - @time run(pipeline(setenv(cmd, new_env), stdout = stdout, stderr = stderr)) + @test success(run(pipeline(setenv(cmd, new_env), stdout = stdout, stderr = stderr))) end end end -@time run_gctest("gc/binarytree.jl") -@time run_gctest("gc/linkedlist.jl") -@time run_gctest("gc/objarray.jl") +# !!! note: +# Since we run our tests on 32bit OS as well we confine ourselves +# to parameters that allocate about 512MB of objects. Max RSS is lower +# than that. +@testset "GC threads" begin + run_gctest("gc/binarytree.jl") + run_gctest("gc/linkedlist.jl") + run_gctest("gc/objarray.jl") + run_gctest("gc/chunks.jl") +end diff --git a/test/gc/binarytree.jl b/test/gc/binarytree.jl index 3089e2d2ce869..896f47fa4c9c7 100644 --- a/test/gc/binarytree.jl +++ b/test/gc/binarytree.jl @@ -49,5 +49,6 @@ end #module using .BinaryTreeMutable -BinaryTreeMutable.binary_trees(devnull, 20) +# Memory usage is 466MB +BinaryTreeMutable.binary_trees(devnull, 16) GC.gc() diff --git a/test/gc/chunks.jl b/test/gc/chunks.jl new file mode 100644 index 0000000000000..08af59ecbf973 --- /dev/null +++ b/test/gc/chunks.jl @@ -0,0 +1,17 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# MWE from https://github.com/JuliaLang/julia/issues/49501 +N = 1_000_000 # or larger +T = BigFloat + +struct Q{T} + a::T + b::T +end + +# Memoy use is ~512MB +let + A = [Q(rand(T), rand(T)) for _ in 1:N] +end + +GC.gc() diff --git a/test/gc/linkedlist.jl b/test/gc/linkedlist.jl index c447a9680326d..669e5f8ec21d9 100644 --- a/test/gc/linkedlist.jl +++ b/test/gc/linkedlist.jl @@ -8,14 +8,16 @@ mutable struct ListNode ListNode(x,y) = new(x,y); end -function list(n=128) +function list(N=16*1024^2) start::ListNode = ListNode(1) current::ListNode = start - for i = 2:(n*1024^2) + for i = 2:N current = ListNode(i,current) end return current.key end +# Memory use is 512 MB _ = list() + GC.gc() diff --git a/test/gc/objarray.jl b/test/gc/objarray.jl index 4b4cb67c42eac..805ee8dadf750 100644 --- a/test/gc/objarray.jl +++ b/test/gc/objarray.jl @@ -27,9 +27,10 @@ end function run(maxsize) Threads.@threads for i in 1:maxsize - work(i*500) + work(i*375) end end -run(4) +# Memory usage 581 MB +@time run(4) GC.gc() diff --git a/test/runtests.jl b/test/runtests.jl index 91f3a67490315..16f60ddcf6764 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -75,6 +75,7 @@ move_to_node1("precompile") move_to_node1("SharedArrays") move_to_node1("threads") move_to_node1("Distributed") +move_to_node1("gc") # Ensure things like consuming all kernel pipe memory doesn't interfere with other tests move_to_node1("stress") From 70ebadb9fbf0df77ca6e8ccd7dc4e0b606ce658f Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Mon, 1 May 2023 21:48:05 -0400 Subject: [PATCH 623/775] Specialized codegen for opaque closure calls (#49337) * WIP codegen for opaque closure calls * Clean up returninfo change * Specialized codegen for opaque closure calls Benchmark: ``` using Base.Experimental: @opaque f() = @opaque (x::Float64)->x+1.0 vec = [f() for i = 1:10_000]; g((x,f),) = f(Float64(x)) ``` Before: ``` julia> @time mapreduce(g, +, enumerate(vec)) 0.001928 seconds (30.00 k allocations: 781.297 KiB) 5.0015e7 ``` After: ``` julia> @time mapreduce(g, +, enumerate(vec)) 0.000085 seconds (3 allocations: 48 bytes) 5.0015e7 ``` * Apply suggestions from code review Co-authored-by: Jameson Nash * Apply suggestions from code review Co-authored-by: Jameson Nash * Address code review * Add tests for bad rt bounds --------- Co-authored-by: Jeff Bezanson Co-authored-by: Jameson Nash --- base/compiler/abstractinterpretation.jl | 2 +- src/builtins.c | 5 + src/codegen-stubs.c | 2 + src/codegen.cpp | 433 ++++++++++++++++-------- src/gf.c | 26 +- src/interpreter.c | 2 +- src/jitlayers.cpp | 17 +- src/jitlayers.h | 3 +- src/jl_exported_funcs.inc | 1 + src/jltypes.c | 2 + src/julia.h | 2 + src/julia_internal.h | 11 +- src/method.c | 1 + src/opaque_closure.c | 114 ++++--- src/staticdata.c | 3 +- test/opaque_closure.jl | 24 +- 16 files changed, 435 insertions(+), 213 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 451cbed407c35..b094ec460d491 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2085,7 +2085,7 @@ function most_general_argtypes(closure::PartialOpaque) if !isa(argt, DataType) || argt.name !== typename(Tuple) argt = Tuple end - return most_general_argtypes(closure.source, argt, #=withfirst=#false) + return Any[argt.parameters...] end # call where the function is any lattice element diff --git a/src/builtins.c b/src/builtins.c index 8138694fdee8a..d423e6c934780 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1920,6 +1920,11 @@ void jl_init_intrinsic_functions(void) JL_GC_DISABLED (jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_opaque_closure_type), "OpaqueClosure", jl_f_opaque_closure_call); + // Save a reference to the just created OpaqueClosure method, so we can provide special + // codegen for it later. + jl_opaque_closure_method = (jl_method_t*)jl_methtable_lookup(jl_opaque_closure_typename->mt, + (jl_value_t*)jl_anytuple_type, 1); + #define ADD_I(name, nargs) add_intrinsic(inm, #name, name); #define ADD_HIDDEN(name, nargs) #define ALIAS ADD_I diff --git a/src/codegen-stubs.c b/src/codegen-stubs.c index 43327816fb371..1c52f969a11f7 100644 --- a/src/codegen-stubs.c +++ b/src/codegen-stubs.c @@ -48,6 +48,8 @@ JL_DLLEXPORT void jl_generate_fptr_for_unspecialized_fallback(jl_code_instance_t jl_atomic_store_release(&unspec->invoke, &jl_fptr_interpret_call); } +JL_DLLEXPORT void jl_generate_fptr_for_oc_wrapper_fallback(jl_code_instance_t *unspec) UNAVAILABLE + JL_DLLEXPORT uint32_t jl_get_LLVM_VERSION_fallback(void) { return 0; diff --git a/src/codegen.cpp b/src/codegen.cpp index 7e7d91120a52e..16149325eb3e0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1718,7 +1718,7 @@ jl_aliasinfo_t jl_aliasinfo_t::fromTBAA(jl_codectx_t &ctx, MDNode *tbaa) { } static Type *julia_type_to_llvm(jl_codectx_t &ctx, jl_value_t *jt, bool *isboxed = NULL); -static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, StringRef name, jl_value_t *sig, jl_value_t *jlrettype, bool is_opaque_closure); +static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value *fval, StringRef name, jl_value_t *sig, jl_value_t *jlrettype, bool is_opaque_closure); static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval = -1); static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t *s, jl_binding_t **pbnd, bool assign); @@ -2385,57 +2385,62 @@ static void jl_init_function(Function *F, const Triple &TT) #endif } -static std::pair uses_specsig(jl_method_instance_t *lam, jl_value_t *rettype, bool prefer_specsig) +static bool uses_specsig(jl_value_t *sig, bool needsparams, bool va, jl_value_t *rettype, bool prefer_specsig) { - int va = lam->def.method->isva; - jl_value_t *sig = lam->specTypes; - bool needsparams = false; - if (jl_is_method(lam->def.method)) { - if ((size_t)jl_subtype_env_size(lam->def.method->sig) != jl_svec_len(lam->sparam_vals)) - needsparams = true; - for (size_t i = 0; i < jl_svec_len(lam->sparam_vals); ++i) { - if (jl_is_typevar(jl_svecref(lam->sparam_vals, i))) - needsparams = true; - } - } if (needsparams) - return std::make_pair(false, true); + return false; if (sig == (jl_value_t*)jl_anytuple_type) - return std::make_pair(false, false); + return false; if (!jl_is_datatype(sig)) - return std::make_pair(false, false); + return false; if (jl_nparams(sig) == 0) - return std::make_pair(false, false); + return false; if (va) { if (jl_is_vararg(jl_tparam(sig, jl_nparams(sig) - 1))) - return std::make_pair(false, false); + return false; } // not invalid, consider if specialized signature is worthwhile if (prefer_specsig) - return std::make_pair(true, false); + return true; if (!deserves_retbox(rettype) && !jl_is_datatype_singleton((jl_datatype_t*)rettype) && rettype != (jl_value_t*)jl_bool_type) - return std::make_pair(true, false); + return true; if (jl_is_uniontype(rettype)) { bool allunbox; size_t nbytes, align, min_align; union_alloca_type((jl_uniontype_t*)rettype, allunbox, nbytes, align, min_align); if (nbytes > 0) - return std::make_pair(true, false); // some elements of the union could be returned unboxed avoiding allocation + return true; // some elements of the union could be returned unboxed avoiding allocation } if (jl_nparams(sig) <= 3) // few parameters == more efficient to pass directly - return std::make_pair(true, false); + return true; bool allSingleton = true; for (size_t i = 0; i < jl_nparams(sig); i++) { jl_value_t *sigt = jl_tparam(sig, i); bool issing = jl_is_datatype(sigt) && jl_is_datatype_singleton((jl_datatype_t*)sigt); allSingleton &= issing; if (!deserves_argbox(sigt) && !issing) { - return std::make_pair(true, false); + return true; } } if (allSingleton) - return std::make_pair(true, false); - return std::make_pair(false, false); // jlcall sig won't require any box allocations + return true; + return false; // jlcall sig won't require any box allocations +} + +static std::pair uses_specsig(jl_method_instance_t *lam, jl_value_t *rettype, bool prefer_specsig) +{ + int va = lam->def.method->isva; + jl_value_t *sig = lam->specTypes; + bool needsparams = false; + if (jl_is_method(lam->def.method)) { + if ((size_t)jl_subtype_env_size(lam->def.method->sig) != jl_svec_len(lam->sparam_vals)) + needsparams = true; + for (size_t i = 0; i < jl_svec_len(lam->sparam_vals); ++i) { + if (jl_is_typevar(jl_svecref(lam->sparam_vals, i))) + needsparams = true; + } + } + return std::make_pair(uses_specsig(sig, needsparams, va, rettype, prefer_specsig), needsparams); } @@ -4097,15 +4102,13 @@ static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction<> *theFptr, Value return emit_jlcall(ctx, prepare_call(theFptr), theF, argv, nargs, trampoline); } - -static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_t *mi, jl_value_t *jlretty, StringRef specFunctionObject, jl_code_instance_t *fromexternal, +static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_closure, jl_value_t *specTypes, jl_value_t *jlretty, llvm::Value *callee, StringRef specFunctionObject, jl_code_instance_t *fromexternal, const jl_cgval_t *argv, size_t nargs, jl_returninfo_t::CallingConv *cc, unsigned *return_roots, jl_value_t *inferred_retty) { ++EmittedSpecfunCalls; // emit specialized call site - bool is_opaque_closure = jl_is_method(mi->def.value) && mi->def.method->is_for_opaque_closure; - jl_returninfo_t returninfo = get_specsig_function(ctx, jl_Module, specFunctionObject, mi->specTypes, jlretty, is_opaque_closure); - FunctionType *cft = returninfo.decl->getFunctionType(); + jl_returninfo_t returninfo = get_specsig_function(ctx, jl_Module, callee, specFunctionObject, specTypes, jlretty, is_opaque_closure); + FunctionType *cft = returninfo.decl.getFunctionType(); *cc = returninfo.cc; *return_roots = returninfo.return_roots; @@ -4119,7 +4122,7 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_ case jl_returninfo_t::Ghosts: break; case jl_returninfo_t::SRet: - result = emit_static_alloca(ctx, getAttributeAtIndex(returninfo.decl->getAttributes(), 1, Attribute::StructRet).getValueAsType()); + result = emit_static_alloca(ctx, getAttributeAtIndex(returninfo.attrs, 1, Attribute::StructRet).getValueAsType()); assert(cast(result->getType())->hasSameElementTypeAs(cast(cft->getParamType(0)))); argvals[idx] = result; idx++; @@ -4140,57 +4143,66 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_ } for (size_t i = 0; i < nargs; i++) { - jl_value_t *jt = (is_opaque_closure && i == 0) ? (jl_value_t*)jl_any_type : - jl_nth_slot_type(mi->specTypes, i); - if (is_uniquerep_Type(jt)) - continue; - bool isboxed = deserves_argbox(jt); - Type *et = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, jt); - if (type_is_ghost(et)) - continue; - assert(idx < nfargs); - Type *at = cft->getParamType(idx); + jl_value_t *jt = jl_nth_slot_type(specTypes, i); jl_cgval_t arg = argv[i]; - if (isboxed) { - assert(at == ctx.types().T_prjlvalue && et == ctx.types().T_prjlvalue); - argvals[idx] = boxed(ctx, arg); - } - else if (et->isAggregateType()) { + if (is_opaque_closure && i == 0) { + Type *at = cft->getParamType(idx); + // Special optimization for opaque closures: We know that specsig opaque + // closures don't look at their type tag (they are fairly quickly discarded + // for their environments). Therefore, we can just pass these as a pointer, + // rather than a boxed value. arg = value_to_pointer(ctx, arg); - // can lazy load on demand, no copy needed - assert(at == PointerType::get(et, AddressSpace::Derived)); argvals[idx] = decay_derived(ctx, maybe_bitcast(ctx, data_pointer(ctx, arg), at)); - } - else { - assert(at == et); - Value *val = emit_unbox(ctx, et, arg, jt); - if (!val) { - // There was a type mismatch of some sort - exit early - CreateTrap(ctx.builder); - return jl_cgval_t(); + } else if (is_uniquerep_Type(jt)) { + continue; + } else { + bool isboxed = deserves_argbox(jt); + Type *et = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, jt); + if (type_is_ghost(et)) + continue; + assert(idx < nfargs); + Type *at = cft->getParamType(idx); + if (isboxed) { + assert(at == ctx.types().T_prjlvalue && et == ctx.types().T_prjlvalue); + argvals[idx] = boxed(ctx, arg); + } + else if (et->isAggregateType()) { + arg = value_to_pointer(ctx, arg); + // can lazy load on demand, no copy needed + assert(at == PointerType::get(et, AddressSpace::Derived)); + argvals[idx] = decay_derived(ctx, maybe_bitcast(ctx, data_pointer(ctx, arg), at)); + } + else { + assert(at == et); + Value *val = emit_unbox(ctx, et, arg, jt); + if (!val) { + // There was a type mismatch of some sort - exit early + CreateTrap(ctx.builder); + return jl_cgval_t(); + } + argvals[idx] = val; } - argvals[idx] = val; } idx++; } assert(idx == nfargs); - Value *callee = returninfo.decl; + Value *TheCallee = returninfo.decl.getCallee(); if (fromexternal) { std::string namep("p"); - namep += returninfo.decl->getName(); + namep += cast(returninfo.decl.getCallee())->getName(); GlobalVariable *GV = cast_or_null(jl_Module->getNamedValue(namep)); if (GV == nullptr) { - GV = new GlobalVariable(*jl_Module, callee->getType(), false, + GV = new GlobalVariable(*jl_Module, TheCallee->getType(), false, GlobalVariable::ExternalLinkage, - Constant::getNullValue(callee->getType()), + Constant::getNullValue(TheCallee->getType()), namep); ctx.external_calls[std::make_tuple(fromexternal, true)] = GV; } jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); - callee = ai.decorateInst(ctx.builder.CreateAlignedLoad(callee->getType(), GV, Align(sizeof(void*)))); + TheCallee = ai.decorateInst(ctx.builder.CreateAlignedLoad(TheCallee->getType(), GV, Align(sizeof(void*)))); } - CallInst *call = ctx.builder.CreateCall(cft, callee, argvals); - call->setAttributes(returninfo.decl->getAttributes()); + CallInst *call = ctx.builder.CreateCall(cft, TheCallee, argvals); + call->setAttributes(returninfo.attrs); jl_cgval_t retval; switch (returninfo.cc) { @@ -4229,6 +4241,14 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_ return update_julia_type(ctx, retval, inferred_retty); } +static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_t *mi, jl_value_t *jlretty, StringRef specFunctionObject, jl_code_instance_t *fromexternal, + const jl_cgval_t *argv, size_t nargs, jl_returninfo_t::CallingConv *cc, unsigned *return_roots, jl_value_t *inferred_retty) +{ + bool is_opaque_closure = jl_is_method(mi->def.value) && mi->def.method->is_for_opaque_closure; + return emit_call_specfun_other(ctx, is_opaque_closure, mi->specTypes, jlretty, NULL, + specFunctionObject, fromexternal, argv, nargs, cc, return_roots, inferred_retty); +} + static jl_cgval_t emit_call_specfun_boxed(jl_codectx_t &ctx, jl_value_t *jlretty, StringRef specFunctionObject, jl_code_instance_t *fromexternal, const jl_cgval_t *argv, size_t nargs, jl_value_t *inferred_retty) { @@ -4412,6 +4432,33 @@ static jl_cgval_t emit_invoke_modify(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_ return mark_julia_type(ctx, callval, true, rt); } +static jl_cgval_t emit_specsig_oc_call(jl_codectx_t &ctx, jl_value_t *oc_type, jl_value_t *sigtype, jl_cgval_t *argv, size_t nargs) +{ + jl_datatype_t *oc_argt = (jl_datatype_t *)jl_tparam0(oc_type); + jl_value_t *oc_rett = jl_tparam1(oc_type); + jl_svec_t *types = jl_get_fieldtypes((jl_datatype_t*)oc_argt); + size_t ntypes = jl_svec_len(types); + for (size_t i = 0; i < nargs-1; ++i) { + jl_value_t *typ = i >= ntypes ? jl_svecref(types, ntypes-1) : jl_svecref(types, i); + if (jl_is_vararg(typ)) + typ = jl_unwrap_vararg(typ); + emit_typecheck(ctx, argv[i+1], typ, "typeassert"); + argv[i+1] = update_julia_type(ctx, argv[i+1], typ); + } + jl_returninfo_t::CallingConv cc = jl_returninfo_t::CallingConv::Boxed; + unsigned return_roots = 0; + + // Load specptr + jl_cgval_t &theArg = argv[0]; + jl_cgval_t closure_specptr = emit_getfield_knownidx(ctx, theArg, 4, (jl_datatype_t*)oc_type, jl_memory_order_notatomic); + Value *specptr = emit_unbox(ctx, ctx.types().T_size, closure_specptr, (jl_value_t*)jl_long_type); + JL_GC_PUSH1(&sigtype); + jl_cgval_t r = emit_call_specfun_other(ctx, true, sigtype, oc_rett, specptr, "", NULL, argv, nargs, + &cc, &return_roots, oc_rett); + JL_GC_POP(); + return r; +} + static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bool is_promotable) { ++EmittedCalls; @@ -4458,6 +4505,21 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bo } } + // handle calling an OpaqueClosure + if (jl_is_concrete_type(f.typ) && jl_subtype(f.typ, (jl_value_t*)jl_opaque_closure_type)) { + jl_value_t *oc_argt = jl_tparam0(f.typ); + jl_value_t *oc_rett = jl_tparam1(f.typ); + if (jl_is_datatype(oc_argt) && jl_tupletype_length_compat(oc_argt, nargs-1)) { + jl_value_t *sigtype = jl_argtype_with_function_type((jl_value_t*)f.typ, (jl_value_t*)oc_argt); + if (uses_specsig(sigtype, false, true, oc_rett, true)) { + JL_GC_PUSH1(&sigtype); + jl_cgval_t r = emit_specsig_oc_call(ctx, f.typ, sigtype, argv, nargs); + JL_GC_POP(); + return r; + } + } + } + // emit function and arguments Value *callval = emit_jlcall(ctx, jlapplygeneric_func, nullptr, generic_argv.data(), n_generic_args, julia_call); return mark_julia_type(ctx, callval, true, rt); @@ -5210,9 +5272,9 @@ static std::pair get_oc_function(jl_codectx_t &ctx, jl_met //emission context holds context lock so can get module specF = closure_m.getModuleUnlocked()->getFunction(closure_decls.specFunctionObject); if (specF) { - jl_returninfo_t returninfo = get_specsig_function(ctx, jl_Module, + jl_returninfo_t returninfo = get_specsig_function(ctx, jl_Module, NULL, closure_decls.specFunctionObject, sigtype, rettype, true); - specF = returninfo.decl; + specF = cast(returninfo.decl.getCallee()); } } ctx.oc_modules.push_back(std::move(closure_m)); @@ -5474,7 +5536,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ if (source.constant == NULL) { // For now, we require non-constant source to be handled by using // eval. This should probably be a verifier error and an abort here. - emit_error(ctx, "(internal error) invalid IR: opaque closure source be constant"); + emit_error(ctx, "(internal error) invalid IR: opaque closure source must be constant"); return jl_cgval_t(); } bool can_optimize = argt.constant != NULL && lb.constant != NULL && ub.constant != NULL && @@ -5710,7 +5772,7 @@ static Type *get_unionbytes_type(LLVMContext &C, unsigned unionbytes) { static void emit_cfunc_invalidate( Function *gf_thunk, jl_returninfo_t::CallingConv cc, unsigned return_roots, - jl_value_t *calltype, jl_value_t *rettype, + jl_value_t *calltype, jl_value_t *rettype, bool is_for_opaque_closure, size_t nargs, jl_codegen_params_t ¶ms, Function *target) @@ -5733,8 +5795,18 @@ static void emit_cfunc_invalidate( ++AI; for (size_t i = 0; i < nargs; i++) { jl_value_t *jt = jl_nth_slot_type(calltype, i); - bool isboxed = deserves_argbox(jt); - Type *et = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, jt); + bool isboxed = false; + Type *et; + if (i == 0 && is_for_opaque_closure) { + et = PointerType::get(ctx.types().T_jlvalue, AddressSpace::Derived); + } + else if (deserves_argbox(jt)) { + et = ctx.types().T_prjlvalue; + isboxed = true; + } + else { + et = julia_type_to_llvm(ctx, jt); + } if (is_uniquerep_Type(jt)) { myargs[i] = mark_julia_const(ctx, jl_tparam0(jt)); } @@ -5746,7 +5818,7 @@ static void emit_cfunc_invalidate( Value *arg_v = &*AI; ++AI; Type *at = arg_v->getType(); - if (!isboxed && et->isAggregateType()) { + if ((i == 0 && is_for_opaque_closure) || (!isboxed && et->isAggregateType())) { myargs[i] = mark_julia_slot(arg_v, jt, NULL, ctx.tbaa().tbaa_const); } else { @@ -5810,11 +5882,11 @@ static void emit_cfunc_invalidate( static void emit_cfunc_invalidate( Function *gf_thunk, jl_returninfo_t::CallingConv cc, unsigned return_roots, - jl_value_t *calltype, jl_value_t *rettype, + jl_value_t *calltype, jl_value_t *rettype, bool is_for_opaque_closure, size_t nargs, jl_codegen_params_t ¶ms) { - emit_cfunc_invalidate(gf_thunk, cc, return_roots, calltype, rettype, nargs, params, + emit_cfunc_invalidate(gf_thunk, cc, return_roots, calltype, rettype, is_for_opaque_closure, nargs, params, prepare_call_in(gf_thunk->getParent(), jlapplygeneric_func)); } @@ -6195,8 +6267,8 @@ static Function* gen_cfun_wrapper( assert(calltype == 3); // emit a specsig call StringRef protoname = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)callptr, codeinst); - jl_returninfo_t returninfo = get_specsig_function(ctx, M, protoname, lam->specTypes, astrt, is_opaque_closure); - FunctionType *cft = returninfo.decl->getFunctionType(); + jl_returninfo_t returninfo = get_specsig_function(ctx, M, NULL, protoname, lam->specTypes, astrt, is_opaque_closure); + FunctionType *cft = returninfo.decl.getFunctionType(); jlfunc_sret = (returninfo.cc == jl_returninfo_t::SRet); // TODO: Can use use emit_call_specfun_other here? @@ -6209,7 +6281,7 @@ static Function* gen_cfun_wrapper( } else { if (jlfunc_sret) { - result = emit_static_alloca(ctx, getAttributeAtIndex(returninfo.decl->getAttributes(), 1, Attribute::StructRet).getValueAsType()); + result = emit_static_alloca(ctx, getAttributeAtIndex(returninfo.attrs, 1, Attribute::StructRet).getValueAsType()); assert(cast(result->getType())->hasSameElementTypeAs(cast(cft->getParamType(0)))); } else { result = emit_static_alloca(ctx, get_unionbytes_type(ctx.builder.getContext(), returninfo.union_bytes)); @@ -6253,25 +6325,25 @@ static Function* gen_cfun_wrapper( // add to argument list args.push_back(arg); } - Value *theFptr = returninfo.decl; + Value *theFptr = returninfo.decl.getCallee(); assert(theFptr); if (age_ok) { funcName += "_gfthunk"; - Function *gf_thunk = Function::Create(returninfo.decl->getFunctionType(), + Function *gf_thunk = Function::Create(returninfo.decl.getFunctionType(), GlobalVariable::InternalLinkage, funcName, M); jl_init_function(gf_thunk, ctx.emission_context.TargetTriple); - gf_thunk->setAttributes(AttributeList::get(M->getContext(), {returninfo.decl->getAttributes(), gf_thunk->getAttributes()})); + gf_thunk->setAttributes(AttributeList::get(M->getContext(), {returninfo.attrs, gf_thunk->getAttributes()})); // build a specsig -> jl_apply_generic converter thunk // this builds a method that calls jl_apply_generic (as a closure over a singleton function pointer), // but which has the signature of a specsig - emit_cfunc_invalidate(gf_thunk, returninfo.cc, returninfo.return_roots, lam->specTypes, codeinst->rettype, nargs + 1, ctx.emission_context); + emit_cfunc_invalidate(gf_thunk, returninfo.cc, returninfo.return_roots, lam->specTypes, codeinst->rettype, is_opaque_closure, nargs + 1, ctx.emission_context); theFptr = ctx.builder.CreateSelect(age_ok, theFptr, gf_thunk); } - assert(cast(theFptr->getType())->isOpaqueOrPointeeTypeMatches(returninfo.decl->getFunctionType())); + assert(cast(theFptr->getType())->isOpaqueOrPointeeTypeMatches(returninfo.decl.getFunctionType())); CallInst *call = ctx.builder.CreateCall( - cast(returninfo.decl->getFunctionType()), + returninfo.decl.getFunctionType(), theFptr, ArrayRef(args)); - call->setAttributes(returninfo.decl->getAttributes()); + call->setAttributes(returninfo.attrs); switch (returninfo.cc) { case jl_returninfo_t::Boxed: retval = mark_julia_type(ctx, call, true, astrt); @@ -6615,7 +6687,7 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret allocate_gc_frame(ctx, b0); // TODO: replace this with emit_call_specfun_other? - FunctionType *ftype = f.decl->getFunctionType(); + FunctionType *ftype = const_cast(f.decl).getFunctionType(); size_t nfargs = ftype->getNumParams(); SmallVector args(nfargs); unsigned idx = 0; @@ -6626,8 +6698,8 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret case jl_returninfo_t::Ghosts: break; case jl_returninfo_t::SRet: - assert(cast(ftype->getParamType(0))->isOpaqueOrPointeeTypeMatches(getAttributeAtIndex(f.decl->getAttributes(), 1, Attribute::StructRet).getValueAsType())); - result = ctx.builder.CreateAlloca(getAttributeAtIndex(f.decl->getAttributes(), 1, Attribute::StructRet).getValueAsType()); + assert(cast(ftype->getParamType(0))->isOpaqueOrPointeeTypeMatches(getAttributeAtIndex(f.attrs, 1, Attribute::StructRet).getValueAsType())); + result = ctx.builder.CreateAlloca(getAttributeAtIndex(f.attrs, 1, Attribute::StructRet).getValueAsType()); args[idx] = result; idx++; break; @@ -6655,7 +6727,12 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret continue; Value *theArg; if (i == 0) { - theArg = funcArg; + // This function adapts from generic jlcall to OC specsig. Generic jlcall pointers + // come in as ::Tracked, but specsig expected ::Derived. + if (is_opaque_closure) + theArg = decay_derived(ctx, funcArg); + else + theArg = funcArg; } else { Value *argPtr = ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_prjlvalue, argArray, i - 1); @@ -6675,7 +6752,7 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret idx++; } CallInst *call = ctx.builder.CreateCall(f.decl, args); - call->setAttributes(f.decl->getAttributes()); + call->setAttributes(f.attrs); jl_cgval_t retval; if (retarg != -1) { @@ -6718,7 +6795,7 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret return w; } -static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, StringRef name, jl_value_t *sig, jl_value_t *jlrettype, bool is_opaque_closure) +static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value *fval, StringRef name, jl_value_t *sig, jl_value_t *jlrettype, bool is_opaque_closure) { jl_returninfo_t props = {}; SmallVector fsig; @@ -6817,13 +6894,18 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, String for (size_t i = 0; i < jl_nparams(sig); i++) { jl_value_t *jt = jl_tparam(sig, i); + bool isboxed = false; + Type *ty = NULL; if (i == 0 && is_opaque_closure) { - jt = (jl_value_t*)jl_any_type; + ty = PointerType::get(ctx.types().T_jlvalue, AddressSpace::Derived); + isboxed = true; // true-ish anyway - we might not have the type tag + } + else { + if (is_uniquerep_Type(jt)) + continue; + isboxed = deserves_argbox(jt); + ty = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, jt); } - if (is_uniquerep_Type(jt)) - continue; - bool isboxed = deserves_argbox(jt); - Type *ty = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, jt); if (type_is_ghost(ty)) continue; #if JL_LLVM_VERSION >= 140000 @@ -6855,17 +6937,29 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, String else if (rt == ctx.types().T_prjlvalue) RetAttrs = RetAttrs.addAttribute(ctx.builder.getContext(), Attribute::NonNull); AttributeList attributes = AttributeList::get(ctx.builder.getContext(), FnAttrs, RetAttrs, attrs); + FunctionType *ftype = FunctionType::get(rt, fsig, false); - Function *f = M ? cast_or_null(M->getNamedValue(name)) : NULL; - if (f == NULL) { - f = Function::Create(ftype, GlobalVariable::ExternalLinkage, name, M); - jl_init_function(f, ctx.emission_context.TargetTriple); - f->setAttributes(AttributeList::get(f->getContext(), {attributes, f->getAttributes()})); + if (fval == NULL) { + Function *f = M ? cast_or_null(M->getNamedValue(name)) : NULL; + if (f == NULL) { + f = Function::Create(ftype, GlobalVariable::ExternalLinkage, name, M); + jl_init_function(f, ctx.emission_context.TargetTriple); + f->setAttributes(AttributeList::get(f->getContext(), {attributes, f->getAttributes()})); + } + else { + assert(f->getFunctionType() == ftype); + } + fval = f; } else { - assert(f->getFunctionType() == ftype); + if (fval->getType()->isIntegerTy()) + fval = emit_inttoptr(ctx, fval, ftype->getPointerTo()); + else + fval = emit_bitcast(ctx, fval, ftype->getPointerTo()); } - props.decl = f; + + props.decl = FunctionCallee(ftype, fval); + props.attrs = attributes; return props; } @@ -6911,6 +7005,24 @@ static jl_datatype_t *compute_va_type(jl_method_instance_t *lam, size_t nreq) return (jl_datatype_t*)typ; } +static std::string get_function_name(bool specsig, bool needsparams, const char *unadorned_name, const Triple &TargetTriple) +{ + std::string _funcName; + raw_string_ostream funcName(_funcName); + // try to avoid conflicts in the global symbol table + if (specsig) + funcName << "julia_"; // api 5 + else if (needsparams) + funcName << "japi3_"; + else + funcName << "japi1_"; + if (TargetTriple.isOSLinux()) { + if (unadorned_name[0] == '@') + unadorned_name++; + } + funcName << unadorned_name << "_" << jl_atomic_fetch_add(&globalUniqueGeneratedNames, 1); + return funcName.str(); +} // Compile to LLVM IR, using a specialized signature if applicable. static jl_llvm_functions_t @@ -7057,22 +7169,8 @@ static jl_llvm_functions_t if (!specsig) ctx.nReqArgs--; // function not part of argArray in jlcall - std::string _funcName; - raw_string_ostream funcName(_funcName); - // try to avoid conflicts in the global symbol table - if (specsig) - funcName << "julia_"; // api 5 - else if (needsparams) - funcName << "japi3_"; - else - funcName << "japi1_"; - const char* unadorned_name = ctx.name; - if (ctx.emission_context.TargetTriple.isOSLinux()) { - if (unadorned_name[0] == '@') - unadorned_name++; - } - funcName << unadorned_name << "_" << jl_atomic_fetch_add(&globalUniqueGeneratedNames, 1); - declarations.specFunctionObject = funcName.str(); + std::string _funcName = get_function_name(specsig, needsparams, ctx.name, ctx.emission_context.TargetTriple); + declarations.specFunctionObject = _funcName; // allocate Function declarations and wrapper objects //Safe because params holds ctx lock @@ -7083,8 +7181,8 @@ static jl_llvm_functions_t Function *f = NULL; bool has_sret = false; if (specsig) { // assumes !va and !needsparams - returninfo = get_specsig_function(ctx, M, declarations.specFunctionObject, lam->specTypes, jlrettype, ctx.is_opaque_closure); - f = returninfo.decl; + returninfo = get_specsig_function(ctx, M, NULL, declarations.specFunctionObject, lam->specTypes, jlrettype, ctx.is_opaque_closure); + f = cast(returninfo.decl.getCallee()); has_sret = (returninfo.cc == jl_returninfo_t::SRet || returninfo.cc == jl_returninfo_t::Union); jl_init_function(f, ctx.emission_context.TargetTriple); @@ -7113,7 +7211,7 @@ static jl_llvm_functions_t }(); std::string wrapName; - raw_string_ostream(wrapName) << "jfptr_" << unadorned_name << "_" << jl_atomic_fetch_add(&globalUniqueGeneratedNames, 1); + raw_string_ostream(wrapName) << "jfptr_" << ctx.name << "_" << jl_atomic_fetch_add(&globalUniqueGeneratedNames, 1); declarations.functionObject = wrapName; (void)gen_invoke_wrapper(lam, jlrettype, returninfo, retarg, declarations.functionObject, M, ctx.emission_context); // TODO: add attributes: maybe_mark_argument_dereferenceable(Arg, argType) @@ -7496,10 +7594,17 @@ static jl_llvm_functions_t } for (i = 0; i < nreq; i++) { jl_sym_t *s = slot_symbol(ctx, i); - jl_value_t *argType = (i == 0 && ctx.is_opaque_closure) ? (jl_value_t*)jl_any_type : - jl_nth_slot_type(lam->specTypes, i); + jl_value_t *argType = jl_nth_slot_type(lam->specTypes, i); bool isboxed = deserves_argbox(argType); - Type *llvmArgType = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, argType); + Type *llvmArgType = NULL; + if (i == 0 && ctx.is_opaque_closure) { + isboxed = true; + llvmArgType = PointerType::get(ctx.types().T_jlvalue, AddressSpace::Derived); + argType = (jl_value_t*)jl_any_type; + } + else { + llvmArgType = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, argType); + } if (s == jl_unused_sym) { if (specsig && !type_is_ghost(llvmArgType) && !is_uniquerep_Type(argType)) ++AI; @@ -7513,14 +7618,36 @@ static jl_llvm_functions_t ++AI; } else { - if (specsig) { + // If this is an opaque closure, implicitly load the env and switch + // the world age. + if (i == 0 && ctx.is_opaque_closure) { + // Load closure world + Value *oc_this = decay_derived(ctx, &*AI++); + Value *argaddr = emit_bitcast(ctx, oc_this, getInt8PtrTy(ctx.builder.getContext())); + Value *worldaddr = ctx.builder.CreateInBoundsGEP( + getInt8Ty(ctx.builder.getContext()), argaddr, + ConstantInt::get(ctx.types().T_size, offsetof(jl_opaque_closure_t, world))); + + jl_cgval_t closure_world = typed_load(ctx, worldaddr, NULL, (jl_value_t*)jl_long_type, + nullptr, nullptr, false, AtomicOrdering::NotAtomic, false, ctx.types().alignof_ptr.value()); + emit_unbox_store(ctx, closure_world, world_age_field, ctx.tbaa().tbaa_gcframe, ctx.types().alignof_ptr.value()); + + // Load closure env + Value *envaddr = ctx.builder.CreateInBoundsGEP( + getInt8Ty(ctx.builder.getContext()), argaddr, + ConstantInt::get(ctx.types().T_size, offsetof(jl_opaque_closure_t, captures))); + + jl_cgval_t closure_env = typed_load(ctx, envaddr, NULL, (jl_value_t*)jl_any_type, + nullptr, nullptr, true, AtomicOrdering::NotAtomic, false, sizeof(void*)); + theArg = update_julia_type(ctx, closure_env, vi.value.typ); + } + else if (specsig) { theArg = get_specsig_arg(argType, llvmArgType, isboxed); } else { if (i == 0) { // first (function) arg is separate in jlcall - theArg = mark_julia_type(ctx, fArg, true, ctx.is_opaque_closure ? - argType : vi.value.typ); + theArg = mark_julia_type(ctx, fArg, true, vi.value.typ); } else { Value *argPtr = ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_prjlvalue, argArray, i - 1); @@ -7543,28 +7670,6 @@ static jl_llvm_functions_t } } - // If this is an opaque closure, implicitly load the env and switch - // the world age. - if (i == 0 && ctx.is_opaque_closure) { - // Load closure world - Value *argaddr = emit_bitcast(ctx, data_pointer(ctx, theArg), getInt8PtrTy(ctx.builder.getContext())); - Value *worldaddr = ctx.builder.CreateInBoundsGEP( - getInt8Ty(ctx.builder.getContext()), argaddr, - ConstantInt::get(ctx.types().T_size, offsetof(jl_opaque_closure_t, world))); - - jl_cgval_t closure_world = typed_load(ctx, worldaddr, NULL, (jl_value_t*)jl_long_type, - theArg.tbaa, nullptr, false, AtomicOrdering::NotAtomic, false, ctx.types().alignof_ptr.value()); - emit_unbox_store(ctx, closure_world, world_age_field, ctx.tbaa().tbaa_gcframe, ctx.types().alignof_ptr.value()); - - // Load closure env - Value *envaddr = ctx.builder.CreateInBoundsGEP( - getInt8Ty(ctx.builder.getContext()), argaddr, - ConstantInt::get(ctx.types().T_size, offsetof(jl_opaque_closure_t, captures))); - - jl_cgval_t closure_env = typed_load(ctx, envaddr, NULL, (jl_value_t*)jl_any_type, - theArg.tbaa, nullptr, true, AtomicOrdering::NotAtomic, false, sizeof(void*)); - theArg = convert_julia_type(ctx, closure_env, vi.value.typ); - } if (vi.boxroot == NULL) { assert(vi.value.V == NULL && "unexpected variable slot created for argument"); @@ -7953,6 +8058,11 @@ static jl_llvm_functions_t // this is basically a copy of emit_assignment, // but where the assignment slot is the retval jl_cgval_t retvalinfo = emit_expr(ctx, retexpr); + + if (ctx.is_opaque_closure) { + emit_typecheck(ctx, retvalinfo, jlrettype, "OpaqueClosure"); + } + retvalinfo = convert_julia_type(ctx, retvalinfo, jlrettype); if (retvalinfo.typ == jl_bottom_type) { CreateTrap(ctx.builder, false); @@ -8480,6 +8590,25 @@ jl_llvm_functions_t jl_emit_code( return decls; } +static jl_llvm_functions_t jl_emit_oc_wrapper(orc::ThreadSafeModule &m, jl_codegen_params_t ¶ms, jl_method_instance_t *mi, jl_value_t *rettype) +{ + Module *M = m.getModuleUnlocked(); + jl_codectx_t ctx(M->getContext(), params); + ctx.name = M->getModuleIdentifier().data(); + std::string funcName = get_function_name(true, false, ctx.name, ctx.emission_context.TargetTriple); + jl_llvm_functions_t declarations; + declarations.functionObject = "jl_f_opaque_closure_call"; + if (uses_specsig(mi->specTypes, false, true, rettype, true)) { + jl_returninfo_t returninfo = get_specsig_function(ctx, M, NULL, funcName, mi->specTypes, rettype, 1); + Function *gf_thunk = cast(returninfo.decl.getCallee()); + jl_init_function(gf_thunk, ctx.emission_context.TargetTriple); + size_t nrealargs = jl_nparams(mi->specTypes); + emit_cfunc_invalidate(gf_thunk, returninfo.cc, returninfo.return_roots, mi->specTypes, rettype, true, nrealargs, ctx.emission_context); + declarations.specFunctionObject = funcName; + } + return declarations; +} + jl_llvm_functions_t jl_emit_codeinst( orc::ThreadSafeModule &m, jl_code_instance_t *codeinst, @@ -8492,6 +8621,12 @@ jl_llvm_functions_t jl_emit_codeinst( if (!src) { src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred); jl_method_t *def = codeinst->def->def.method; + // Check if this is the generic method for opaque closure wrappers - + // if so, generate the specsig -> invoke converter. + if (def == jl_opaque_closure_method) { + JL_GC_POP(); + return jl_emit_oc_wrapper(m, params, codeinst->def, codeinst->rettype); + } if (src && (jl_value_t*)src != jl_nothing && jl_is_method(def)) src = jl_uncompress_ir(def, codeinst, (jl_array_t*)src); if (!src || !jl_is_code_info(src)) { @@ -8661,7 +8796,7 @@ void jl_compile_workqueue( jl_init_function(protodecl, params.TargetTriple); size_t nrealargs = jl_nparams(codeinst->def->specTypes); // number of actual arguments being passed // TODO: maybe this can be cached in codeinst->specfptr? - emit_cfunc_invalidate(protodecl, proto_cc, proto_return_roots, codeinst->def->specTypes, codeinst->rettype, nrealargs, params, preal); + emit_cfunc_invalidate(protodecl, proto_cc, proto_return_roots, codeinst->def->specTypes, codeinst->rettype, false, nrealargs, params, preal); preal_decl = ""; // no need to fixup the name } else { diff --git a/src/gf.c b/src/gf.c index caadb45f1b262..49e6f3b9d4162 100644 --- a/src/gf.c +++ b/src/gf.c @@ -134,7 +134,7 @@ static int speccache_eq(size_t idx, const void *ty, jl_svec_t *data, uint_t hv) // get or create the MethodInstance for a specialization static jl_method_instance_t *jl_specializations_get_linfo_(jl_method_t *m JL_PROPAGATES_ROOT, jl_value_t *type, jl_svec_t *sparams, jl_method_instance_t *mi_insert) { - if (m->sig == (jl_value_t*)jl_anytuple_type && jl_atomic_load_relaxed(&m->unspecialized) != NULL) + if (m->sig == (jl_value_t*)jl_anytuple_type && jl_atomic_load_relaxed(&m->unspecialized) != NULL && m != jl_opaque_closure_method) return jl_atomic_load_relaxed(&m->unspecialized); // handle builtin methods jl_value_t *ut = jl_is_unionall(type) ? jl_unwrap_unionall(type) : type; JL_TYPECHK(specializations, datatype, ut); @@ -2546,6 +2546,8 @@ JL_DLLEXPORT jl_callptr_t jl_fptr_const_return_addr = &jl_fptr_const_return; JL_DLLEXPORT jl_callptr_t jl_fptr_sparam_addr = &jl_fptr_sparam; +JL_DLLEXPORT jl_callptr_t jl_f_opaque_closure_call_addr = (jl_callptr_t)&jl_f_opaque_closure_call; + // Return the index of the invoke api, if known JL_DLLEXPORT int32_t jl_invoke_api(jl_code_instance_t *codeinst) { @@ -2813,18 +2815,20 @@ JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types) } // add type of `f` to front of argument tuple type -static jl_value_t *jl_argtype_with_function(jl_function_t *f, jl_value_t *types0) +jl_value_t *jl_argtype_with_function(jl_value_t *f, jl_value_t *types0) +{ + return jl_argtype_with_function_type(jl_is_type(f) ? (jl_value_t*)jl_wrap_Type(f) : jl_typeof(f), types0); +} + +jl_value_t *jl_argtype_with_function_type(jl_value_t *ft JL_MAYBE_UNROOTED, jl_value_t *types0) { jl_value_t *types = jl_unwrap_unionall(types0); size_t l = jl_nparams(types); - jl_value_t *tt = (jl_value_t*)jl_alloc_svec(1+l); - size_t i; - JL_GC_PUSH1(&tt); - if (jl_is_type(f)) - jl_svecset(tt, 0, jl_wrap_Type(f)); - else - jl_svecset(tt, 0, jl_typeof(f)); - for(i=0; i < l; i++) + jl_value_t *tt = NULL; + JL_GC_PUSH2(&tt, &ft); + tt = (jl_value_t*)jl_alloc_svec(1+l); + jl_svecset(tt, 0, ft); + for (size_t i = 0; i < l; i++) jl_svecset(tt, i+1, jl_tparam(types,i)); tt = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)tt); tt = jl_rewrap_unionall_(tt, types0); @@ -3118,7 +3122,7 @@ jl_value_t *jl_gf_invoke(jl_value_t *types0, jl_value_t *gf, jl_value_t **args, size_t world = jl_current_task->world_age; jl_value_t *types = NULL; JL_GC_PUSH1(&types); - types = jl_argtype_with_function(gf, types0); + types = jl_argtype_with_function((jl_value_t*)gf, types0); jl_method_t *method = (jl_method_t*)jl_gf_invoke_lookup(types, jl_nothing, world); JL_GC_PROMISE_ROOTED(method); diff --git a/src/interpreter.c b/src/interpreter.c index 713887f234898..7a699223d746e 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -736,8 +736,8 @@ jl_value_t *jl_interpret_opaque_closure(jl_opaque_closure_t *oc, jl_value_t **ar jl_value_t *r = eval_body(code->code, s, 0, 0); locals[0] = r; // GC root JL_GC_PROMISE_ROOTED(r); - jl_typeassert(r, jl_tparam1(jl_typeof(oc))); ct->world_age = last_age; + jl_typeassert(r, jl_tparam1(jl_typeof(oc))); JL_GC_POP(); return r; } diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index ef4cc9ac95aa6..9dad5cf509f0e 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -191,7 +191,6 @@ static jl_callptr_t _jl_compile_codeinst( assert(jl_is_code_instance(codeinst)); assert(codeinst->min_world <= world && (codeinst->max_world >= world || codeinst->max_world == 0) && "invalid world for method-instance"); - assert(src && jl_is_code_info(src)); JL_TIMING(CODEINST_COMPILE, CODEINST_COMPILE); #ifdef USE_TRACY @@ -269,6 +268,9 @@ static jl_callptr_t _jl_compile_codeinst( else if (decls.functionObject == "jl_fptr_sparam") { addr = jl_fptr_sparam_addr; } + else if (decls.functionObject == "jl_f_opaque_closure_call") { + addr = jl_f_opaque_closure_call_addr; + } else { addr = (jl_callptr_t)getAddressForFunction(decls.functionObject); isspecsig = true; @@ -503,6 +505,19 @@ jl_code_instance_t *jl_generate_fptr_impl(jl_method_instance_t *mi JL_PROPAGATES return codeinst; } +extern "C" JL_DLLEXPORT +void jl_generate_fptr_for_oc_wrapper_impl(jl_code_instance_t *oc_wrap) +{ + if (jl_atomic_load_relaxed(&oc_wrap->invoke) != NULL) { + return; + } + JL_LOCK(&jl_codegen_lock); + if (jl_atomic_load_relaxed(&oc_wrap->invoke) == NULL) { + _jl_compile_codeinst(oc_wrap, NULL, 1, *jl_ExecutionEngine->getContext(), 0); + } + JL_UNLOCK(&jl_codegen_lock); // Might GC +} + extern "C" JL_DLLEXPORT void jl_generate_fptr_for_unspecialized_impl(jl_code_instance_t *unspec) { diff --git a/src/jitlayers.h b/src/jitlayers.h index f63f3a42842f1..bbbcbe73f1e54 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -178,7 +178,8 @@ typedef struct _jl_llvm_functions_t { } jl_llvm_functions_t; struct jl_returninfo_t { - llvm::Function *decl; + llvm::FunctionCallee decl; + llvm::AttributeList attrs; enum CallingConv { Boxed = 0, Register, diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index e82d43de7937c..c09f2aff4cb88 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -546,6 +546,7 @@ YY(jl_register_fptrs) \ YY(jl_generate_fptr) \ YY(jl_generate_fptr_for_unspecialized) \ + YY(jl_generate_fptr_for_oc_wrapper) \ YY(jl_compile_extern_c) \ YY(jl_teardown_codegen) \ YY(jl_jit_total_bytes) \ diff --git a/src/jltypes.c b/src/jltypes.c index 792588ed231b4..902e1e557f7e0 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3182,6 +3182,8 @@ void jl_init_types(void) JL_GC_DISABLED tv = jl_svec2(tvar("A"), tvar("R")); jl_opaque_closure_type = (jl_unionall_t*)jl_new_datatype(jl_symbol("OpaqueClosure"), core, jl_function_type, tv, + // N.B.: OpaqueClosure call code relies on specptr being field 5. + // Update that code if you change this. jl_perm_symsvec(5, "captures", "world", "source", "invoke", "specptr"), jl_svec(5, jl_any_type, jl_long_type, jl_any_type, pointer_void, pointer_void), jl_emptysvec, 0, 0, 5)->name->wrapper; diff --git a/src/julia.h b/src/julia.h index c7f4e9f1334e9..0a542dd8b6bcb 100644 --- a/src/julia.h +++ b/src/julia.h @@ -235,6 +235,8 @@ typedef jl_value_t *(*jl_fptr_sparam_t)(jl_value_t*, jl_value_t**, uint32_t, jl_ extern jl_call_t jl_fptr_interpret_call; JL_DLLEXPORT extern jl_callptr_t jl_fptr_interpret_call_addr; +JL_DLLEXPORT extern jl_callptr_t jl_f_opaque_closure_call_addr; + typedef struct _jl_line_info_node_t { struct _jl_module_t *module; jl_value_t *method; // may contain a jl_symbol, jl_method_t, or jl_method_instance_t diff --git a/src/julia_internal.h b/src/julia_internal.h index c62621142e508..6c217789cd80b 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -307,6 +307,7 @@ static inline void memmove_refs(void **dstp, void *const *srcp, size_t n) JL_NOT extern jl_methtable_t *jl_type_type_mt JL_GLOBALLY_ROOTED; extern jl_methtable_t *jl_nonfunction_mt JL_GLOBALLY_ROOTED; extern jl_methtable_t *jl_kwcall_mt JL_GLOBALLY_ROOTED; +extern JL_DLLEXPORT jl_method_t *jl_opaque_closure_method JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT _Atomic(size_t) jl_world_counter; typedef void (*tracer_cb)(jl_value_t *tracee); @@ -615,8 +616,10 @@ typedef union { JL_DLLEXPORT jl_code_info_t *jl_type_infer(jl_method_instance_t *li, size_t world, int force); JL_DLLEXPORT jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *meth JL_PROPAGATES_ROOT, size_t world); +JL_DLLEXPORT void *jl_compile_oc_wrapper(jl_code_instance_t *ci); jl_code_instance_t *jl_generate_fptr(jl_method_instance_t *mi JL_PROPAGATES_ROOT, size_t world); void jl_generate_fptr_for_unspecialized(jl_code_instance_t *unspec); +void jl_generate_fptr_for_oc_wrapper(jl_code_instance_t *unspec); JL_DLLEXPORT jl_code_instance_t *jl_get_method_inferred( jl_method_instance_t *mi JL_PROPAGATES_ROOT, jl_value_t *rettype, size_t min_world, size_t max_world); @@ -996,7 +999,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types, jl_method_instance_t *jl_get_specialized(jl_method_t *m, jl_value_t *types, jl_svec_t *sp); JL_DLLEXPORT jl_value_t *jl_rettype_inferred(jl_method_instance_t *li JL_PROPAGATES_ROOT, size_t min_world, size_t max_world); JL_DLLEXPORT jl_code_instance_t *jl_method_compiled(jl_method_instance_t *mi JL_PROPAGATES_ROOT, size_t world); -JL_DLLEXPORT jl_value_t *jl_methtable_lookup(jl_methtable_t *mt, jl_value_t *type, size_t world); +JL_DLLEXPORT jl_value_t *jl_methtable_lookup(jl_methtable_t *mt JL_PROPAGATES_ROOT, jl_value_t *type, size_t world); JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo( jl_method_t *m JL_PROPAGATES_ROOT, jl_value_t *type, jl_svec_t *sparams); jl_method_instance_t *jl_specializations_get_or_insert(jl_method_instance_t *mi_ins); @@ -1478,6 +1481,12 @@ void typemap_slurp_search(jl_typemap_entry_t *ml, struct typemap_intersection_en JL_DLLEXPORT size_t (jl_svec_len)(jl_svec_t *t) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_svec_ref(jl_svec_t *t JL_PROPAGATES_ROOT, ssize_t i); +// check whether the specified number of arguments is compatible with the +// specified number of parameters of the tuple type +JL_DLLEXPORT int jl_tupletype_length_compat(jl_value_t *v, size_t nargs) JL_NOTSAFEPOINT; + +JL_DLLEXPORT jl_value_t *jl_argtype_with_function(jl_value_t *f, jl_value_t *types0); +JL_DLLEXPORT jl_value_t *jl_argtype_with_function_type(jl_value_t *ft JL_MAYBE_UNROOTED, jl_value_t *types0); JL_DLLEXPORT unsigned jl_special_vector_alignment(size_t nfields, jl_value_t *field_type); diff --git a/src/method.c b/src/method.c index c20132832a3de..bed94319953c0 100644 --- a/src/method.c +++ b/src/method.c @@ -18,6 +18,7 @@ extern "C" { extern jl_value_t *jl_builtin_getfield; extern jl_value_t *jl_builtin_tuple; jl_methtable_t *jl_kwcall_mt; +jl_method_t *jl_opaque_closure_method; jl_method_t *jl_make_opaque_closure_method(jl_module_t *module, jl_value_t *name, int nargs, jl_value_t *functionloc, jl_code_info_t *ci, int isva); diff --git a/src/opaque_closure.c b/src/opaque_closure.c index 07a17ac3e8bec..d73beff0f8587 100644 --- a/src/opaque_closure.c +++ b/src/opaque_closure.c @@ -8,6 +8,11 @@ jl_value_t *jl_fptr_const_opaque_closure(jl_opaque_closure_t *oc, jl_value_t **a return oc->captures; } +jl_value_t *jl_fptr_const_opaque_closure_typeerror(jl_opaque_closure_t *oc, jl_value_t **args, size_t nargs) +{ + jl_type_error("OpaqueClosure", jl_tparam1(jl_typeof(oc)), oc->captures); +} + // determine whether `argt` is a valid argument type tuple for the given opaque closure method JL_DLLEXPORT int jl_is_valid_oc_argtype(jl_tupletype_t *argt, jl_method_t *source) { @@ -22,22 +27,6 @@ JL_DLLEXPORT int jl_is_valid_oc_argtype(jl_tupletype_t *argt, jl_method_t *sourc return 1; } -// TODO: merge this with jl_argtype_with_function -static jl_value_t *prepend_type(jl_value_t *t0, jl_tupletype_t *t) -{ - jl_svec_t *sig_args = NULL; - JL_GC_PUSH1(&sig_args); - size_t nsig = 1 + jl_svec_len(t->parameters); - sig_args = jl_alloc_svec_uninit(nsig); - jl_svecset(sig_args, 0, t0); - for (size_t i = 0; i < nsig-1; ++i) { - jl_svecset(sig_args, 1+i, jl_tparam(t, i)); - } - jl_value_t *sigtype = jl_apply_tuple_type(sig_args); - JL_GC_POP(); - return sigtype; -} - static jl_opaque_closure_t *new_opaque_closure(jl_tupletype_t *argt, jl_value_t *rt_lb, jl_value_t *rt_ub, jl_value_t *source_, jl_value_t *captures, int do_compile) { @@ -57,44 +46,78 @@ static jl_opaque_closure_t *new_opaque_closure(jl_tupletype_t *argt, jl_value_t if (jl_nparams(argt) + 1 - jl_is_va_tuple(argt) < source->nargs - source->isva) jl_error("Argument type tuple has too few required arguments for method"); jl_value_t *sigtype = NULL; - JL_GC_PUSH1(&sigtype); - sigtype = prepend_type(jl_typeof(captures), argt); - - jl_value_t *oc_type JL_ALWAYS_LEAFTYPE; - oc_type = jl_apply_type2((jl_value_t*)jl_opaque_closure_type, (jl_value_t*)argt, rt_ub); - JL_GC_PROMISE_ROOTED(oc_type); + jl_value_t *selected_rt = rt_ub; + JL_GC_PUSH2(&sigtype, &selected_rt); + sigtype = jl_argtype_with_function(captures, (jl_value_t*)argt); jl_method_instance_t *mi = jl_specializations_get_linfo(source, sigtype, jl_emptysvec); - size_t world = jl_current_task->world_age; + jl_task_t *ct = jl_current_task; + size_t world = ct->world_age; jl_code_instance_t *ci = NULL; - if (do_compile) + if (do_compile) { ci = jl_compile_method_internal(mi, world); + } - jl_task_t *ct = jl_current_task; - jl_opaque_closure_t *oc = (jl_opaque_closure_t*)jl_gc_alloc(ct->ptls, sizeof(jl_opaque_closure_t), oc_type); - JL_GC_POP(); - oc->source = source; - oc->captures = captures; - oc->specptr = NULL; - if (!ci) { - oc->invoke = (jl_fptr_args_t)jl_interpret_opaque_closure; - } else { - jl_callptr_t invoke = jl_atomic_load_acquire(&ci->invoke); - if (invoke == jl_fptr_interpret_call) { - oc->invoke = (jl_fptr_args_t)jl_interpret_opaque_closure; + jl_fptr_args_t invoke = (jl_fptr_args_t)jl_interpret_opaque_closure; + void *specptr = NULL; + + if (ci) { + invoke = (jl_fptr_args_t)jl_atomic_load_relaxed(&ci->invoke); + specptr = jl_atomic_load_relaxed(&ci->specptr.fptr); + + selected_rt = ci->rettype; + // If we're not allowed to generate a specsig with this, rt, fall + // back to the invoke wrapper. We could instead generate a specsig->specsig + // wrapper, but lets leave that for later. + if (!jl_subtype(rt_lb, selected_rt)) { + // TODO: It would be better to try to get a specialization with the + // correct rt check here (or we could codegen a wrapper). + specptr = NULL; invoke = (jl_fptr_args_t)jl_interpret_opaque_closure; + jl_value_t *ts[2] = {rt_lb, (jl_value_t*)ci->rettype}; + selected_rt = jl_type_union(ts, 2); } - else if (invoke == jl_fptr_args) { - oc->invoke = jl_atomic_load_relaxed(&ci->specptr.fptr1); + if (!jl_subtype(ci->rettype, rt_ub)) { + // TODO: It would be better to try to get a specialization with the + // correct rt check here (or we could codegen a wrapper). + specptr = NULL; invoke = (jl_fptr_args_t)jl_interpret_opaque_closure; + selected_rt = jl_type_intersection(rt_ub, selected_rt); + } + + if (invoke == (jl_fptr_args_t) jl_fptr_interpret_call) { + invoke = (jl_fptr_args_t)jl_interpret_opaque_closure; } - else if (invoke == jl_fptr_const_return) { - oc->invoke = (jl_fptr_args_t)jl_fptr_const_opaque_closure; - oc->captures = ci->rettype_const; + else if (invoke == (jl_fptr_args_t)jl_fptr_args && specptr) { + invoke = (jl_fptr_args_t)specptr; } - else { - oc->invoke = (jl_fptr_args_t) invoke; + else if (invoke == (jl_fptr_args_t)jl_fptr_const_return) { + invoke = jl_isa(ci->rettype_const, selected_rt) ? + (jl_fptr_args_t)jl_fptr_const_opaque_closure : + (jl_fptr_args_t)jl_fptr_const_opaque_closure_typeerror; + captures = ci->rettype_const; } } + + jl_value_t *oc_type JL_ALWAYS_LEAFTYPE = jl_apply_type2((jl_value_t*)jl_opaque_closure_type, (jl_value_t*)argt, selected_rt); + JL_GC_PROMISE_ROOTED(oc_type); + + if (!specptr) { + sigtype = jl_argtype_with_function_type((jl_value_t*)oc_type, (jl_value_t*)argt); + jl_method_instance_t *mi_generic = jl_specializations_get_linfo(jl_opaque_closure_method, sigtype, jl_emptysvec); + + // OC wrapper methods are not world dependent + ci = jl_get_method_inferred(mi_generic, selected_rt, 1, ~(size_t)0); + if (!jl_atomic_load_acquire(&ci->invoke)) + jl_generate_fptr_for_oc_wrapper(ci); + specptr = jl_atomic_load_relaxed(&ci->specptr.fptr); + } + jl_opaque_closure_t *oc = (jl_opaque_closure_t*)jl_gc_alloc(ct->ptls, sizeof(jl_opaque_closure_t), oc_type); + oc->source = source; + oc->captures = captures; oc->world = world; + oc->invoke = invoke; + oc->specptr = specptr; + + JL_GC_POP(); return oc; } @@ -132,7 +155,7 @@ JL_DLLEXPORT jl_opaque_closure_t *jl_new_opaque_closure_from_code_info(jl_tuplet root = (jl_value_t*)meth; meth->primary_world = jl_current_task->world_age; - sigtype = prepend_type(jl_typeof(env), argt); + sigtype = jl_argtype_with_function(env, (jl_value_t*)argt); jl_method_instance_t *mi = jl_specializations_get_linfo((jl_method_t*)root, sigtype, jl_emptysvec); inst = jl_new_codeinst(mi, rt_ub, NULL, (jl_value_t*)ci, 0, meth->primary_world, -1, 0, 0, jl_nothing, 0); @@ -151,10 +174,9 @@ JL_CALLABLE(jl_new_opaque_closure_jlcall) args[1], args[2], args[3], &args[4], nargs-4, 1); } - // check whether the specified number of arguments is compatible with the // specified number of parameters of the tuple type -STATIC_INLINE int jl_tupletype_length_compat(jl_value_t *v, size_t nargs) JL_NOTSAFEPOINT +int jl_tupletype_length_compat(jl_value_t *v, size_t nargs) { v = jl_unwrap_unionall(v); assert(jl_is_tuple_type(v)); diff --git a/src/staticdata.c b/src/staticdata.c index 33667a05578d4..29bcc78d09986 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -98,7 +98,7 @@ extern "C" { // TODO: put WeakRefs on the weak_refs list during deserialization // TODO: handle finalizers -#define NUM_TAGS 157 +#define NUM_TAGS 158 // An array of references that need to be restored from the sysimg // This is a manually constructed dual of the gvars array, which would be produced by codegen for Julia code, for C. @@ -241,6 +241,7 @@ jl_value_t **const*const get_tags(void) { INSERT_TAG(jl_nonfunction_mt); INSERT_TAG(jl_kwcall_mt); INSERT_TAG(jl_kwcall_func); + INSERT_TAG(jl_opaque_closure_method); // some Core.Builtin Functions that we want to be able to reference: INSERT_TAG(jl_builtin_throw); diff --git a/test/opaque_closure.jl b/test/opaque_closure.jl index 6bd86f7f3a4d5..e6490f5e9d345 100644 --- a/test/opaque_closure.jl +++ b/test/opaque_closure.jl @@ -4,6 +4,7 @@ using Core: OpaqueClosure using Base.Experimental: @opaque const_int() = 1 +const_int_barrier() = Base.inferencebarrier(1)::typeof(1) const lno = LineNumberNode(1, :none) @@ -177,7 +178,7 @@ mk_va_opaque() = @opaque (x...)->x @test mk_va_opaque()(1,2) == (1,2) # OpaqueClosure show method -@test repr(@opaque x->1) == "(::Any)::Any->◌" +@test repr(@opaque x->Base.inferencebarrier(1)) == "(::Any)::Any->◌" # Opaque closure in CodeInfo returned from generated functions let ci = @code_lowered const_int() @@ -275,3 +276,24 @@ let src = code_typed((Int,Int)) do x, y... @test_throws MethodError oc(1,2,3) end end + +# Check for correct handling in case of broken return type. +eval_oc_dyn(oc) = Base.inferencebarrier(oc)() +eval_oc_spec(oc) = oc() +for f in (const_int, const_int_barrier) + ci = code_lowered(f, Tuple{})[1] + for compiled in (true, false) + oc_expr = Expr(:new_opaque_closure, Tuple{}, Union{}, Float64, + Expr(:opaque_closure_method, nothing, 0, false, lno, ci)) + oc_mismatch = let ci = code_lowered(f, Tuple{})[1] + if compiled + eval(:((()->$oc_expr)())) + else + eval(oc_expr) + end + end + @test isa(oc_mismatch, OpaqueClosure{Tuple{}, Union{}}) + @test_throws TypeError eval_oc_dyn(oc_mismatch) + @test_throws TypeError eval_oc_spec(oc_mismatch) + end +end From 1a973c7a7ac7c4a91b39c8fd67944132f6d488f5 Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Tue, 2 May 2023 00:46:47 -0300 Subject: [PATCH 624/775] NFC: some cleanup in gc.c (#49577) --- src/gc.c | 151 ++++--------------------------------------- src/gc.h | 4 ++ src/init.c | 4 ++ src/julia_internal.h | 2 + src/partr.c | 5 -- src/staticdata.c | 119 +++++++++++++++++++++++++++++++++- 6 files changed, 140 insertions(+), 145 deletions(-) diff --git a/src/gc.c b/src/gc.c index 4987af5f296dc..1cbcff5b50ab8 100644 --- a/src/gc.c +++ b/src/gc.c @@ -11,18 +11,16 @@ extern "C" { #endif +// Number of threads currently running the GC mark-loop +_Atomic(int) gc_n_threads_marking; // `tid` of mutator thread that triggered GC _Atomic(int) gc_master_tid; // `tid` of first GC thread int gc_first_tid; - // Mutex/cond used to synchronize sleep/wakeup of GC threads uv_mutex_t gc_threads_lock; uv_cond_t gc_threads_cond; -// Number of threads currently running the GC mark-loop -_Atomic(int) gc_n_threads_marking; - // Linked list of callback functions typedef void (*jl_gc_cb_func_t)(void); @@ -142,8 +140,8 @@ static _Atomic(int) support_conservative_marking = 0; * Note about GC synchronization: * * When entering `jl_gc_collect()`, `jl_gc_running` is atomically changed from - * `0` to `1` to make sure that only one thread can be running the GC. Other - * threads that enters `jl_gc_collect()` at the same time (or later calling + * `0` to `1` to make sure that only one thread can be running `_jl_gc_collect`. Other + * mutator threads that enters `jl_gc_collect()` at the same time (or later calling * from unmanaged code) will wait in `jl_gc_collect()` until the GC is finished. * * Before starting the mark phase the GC thread calls `jl_safepoint_start_gc()` @@ -153,7 +151,7 @@ static _Atomic(int) support_conservative_marking = 0; * GC (`gc_state != 0`). It also acquires the `finalizers` lock so that no * other thread will access them when the GC is running. * - * During the mark and sweep phase of the GC, the threads that are not running + * During the mark and sweep phase of the GC, the mutator threads that are not running * the GC should either be running unmanaged code (or code section that does * not have a GC critical region mainly including storing to the stack or * another object) or paused at a safepoint and wait for the GC to finish. @@ -185,13 +183,6 @@ pagetable_t memory_map; // List of marked big objects. Not per-thread. Accessed only by master thread. bigval_t *big_objects_marked = NULL; -// Eytzinger tree of images. Used for very fast jl_object_in_image queries during gc -// See https://algorithmica.org/en/eytzinger -static arraylist_t eytzinger_image_tree; -static arraylist_t eytzinger_idxs; -static uintptr_t gc_img_min; -static uintptr_t gc_img_max; - // -- Finalization -- // `ptls->finalizers` and `finalizer_list_marked` might have tagged pointers. // If an object pointer has the lowest bit set, the next pointer is an unboxed c function pointer. @@ -202,117 +193,6 @@ arraylist_t finalizer_list_marked; arraylist_t to_finalize; JL_DLLEXPORT _Atomic(int) jl_gc_have_pending_finalizers = 0; -static int ptr_cmp(const void *l, const void *r) -{ - uintptr_t left = *(const uintptr_t*)l; - uintptr_t right = *(const uintptr_t*)r; - // jl_safe_printf("cmp %p %p\n", (void*)left, (void*)right); - return (left > right) - (left < right); -} - -// Build an eytzinger tree from a sorted array -static int eytzinger(uintptr_t *src, uintptr_t *dest, size_t i, size_t k, size_t n) -{ - if (k <= n) { - i = eytzinger(src, dest, i, 2 * k, n); - dest[k-1] = src[i]; - i++; - i = eytzinger(src, dest, i, 2 * k + 1, n); - } - return i; -} - -static size_t eyt_obj_idx(jl_value_t *obj) JL_NOTSAFEPOINT -{ - size_t n = eytzinger_image_tree.len - 1; - if (n == 0) - return n; - assert(n % 2 == 0 && "Eytzinger tree not even length!"); - uintptr_t cmp = (uintptr_t) obj; - if (cmp <= gc_img_min || cmp > gc_img_max) - return n; - uintptr_t *tree = (uintptr_t*)eytzinger_image_tree.items; - size_t k = 1; - // note that k preserves the history of how we got to the current node - while (k <= n) { - int greater = (cmp > tree[k - 1]); - k <<= 1; - k |= greater; - } - // Free to assume k is nonzero, since we start with k = 1 - // and cmp > gc_img_min - // This shift does a fast revert of the path until we get - // to a node that evaluated less than cmp. - k >>= (__builtin_ctzll(k) + 1); - assert(k != 0); - assert(k <= n && "Eytzinger tree index out of bounds!"); - assert(tree[k - 1] < cmp && "Failed to find lower bound for object!"); - return k - 1; -} - -//used in staticdata.c after we add an image -void rebuild_image_blob_tree(void) -{ - size_t inc = 1 + jl_linkage_blobs.len - eytzinger_image_tree.len; - assert(eytzinger_idxs.len == eytzinger_image_tree.len); - assert(eytzinger_idxs.max == eytzinger_image_tree.max); - arraylist_grow(&eytzinger_idxs, inc); - arraylist_grow(&eytzinger_image_tree, inc); - eytzinger_idxs.items[eytzinger_idxs.len - 1] = (void*)jl_linkage_blobs.len; - eytzinger_image_tree.items[eytzinger_image_tree.len - 1] = (void*)1; // outside image - for (size_t i = 0; i < jl_linkage_blobs.len; i++) { - assert((uintptr_t) jl_linkage_blobs.items[i] % 4 == 0 && "Linkage blob not 4-byte aligned!"); - // We abuse the pointer here a little so that a couple of properties are true: - // 1. a start and an end are never the same value. This simplifies the binary search. - // 2. ends are always after starts. This also simplifies the binary search. - // We assume that there exist no 0-size blobs, but that's a safe assumption - // since it means nothing could be there anyways - uintptr_t val = (uintptr_t) jl_linkage_blobs.items[i]; - eytzinger_idxs.items[i] = (void*)(val + (i & 1)); - } - qsort(eytzinger_idxs.items, eytzinger_idxs.len - 1, sizeof(void*), ptr_cmp); - gc_img_min = (uintptr_t) eytzinger_idxs.items[0]; - gc_img_max = (uintptr_t) eytzinger_idxs.items[eytzinger_idxs.len - 2] + 1; - eytzinger((uintptr_t*)eytzinger_idxs.items, (uintptr_t*)eytzinger_image_tree.items, 0, 1, eytzinger_idxs.len - 1); - // Reuse the scratch memory to store the indices - // Still O(nlogn) because binary search - for (size_t i = 0; i < jl_linkage_blobs.len; i ++) { - uintptr_t val = (uintptr_t) jl_linkage_blobs.items[i]; - // This is the same computation as in the prior for loop - uintptr_t eyt_val = val + (i & 1); - size_t eyt_idx = eyt_obj_idx((jl_value_t*)(eyt_val + 1)); assert(eyt_idx < eytzinger_idxs.len - 1); - assert(eytzinger_image_tree.items[eyt_idx] == (void*)eyt_val && "Eytzinger tree failed to find object!"); - if (i & 1) - eytzinger_idxs.items[eyt_idx] = (void*)n_linkage_blobs(); - else - eytzinger_idxs.items[eyt_idx] = (void*)(i / 2); - } -} - -static int eyt_obj_in_img(jl_value_t *obj) JL_NOTSAFEPOINT -{ - assert((uintptr_t) obj % 4 == 0 && "Object not 4-byte aligned!"); - int idx = eyt_obj_idx(obj); - // Now we use a tiny trick: tree[idx] & 1 is whether or not tree[idx] is a - // start (0) or an end (1) of a blob. If it's a start, then the object is - // in the image, otherwise it is not. - int in_image = ((uintptr_t)eytzinger_image_tree.items[idx] & 1) == 0; - return in_image; -} - -size_t external_blob_index(jl_value_t *v) JL_NOTSAFEPOINT -{ - assert((uintptr_t) v % 4 == 0 && "Object not 4-byte aligned!"); - int eyt_idx = eyt_obj_idx(v); - // We fill the invalid slots with the length, so we can just return that - size_t idx = (size_t) eytzinger_idxs.items[eyt_idx]; - return idx; -} - -uint8_t jl_object_in_image(jl_value_t *obj) JL_NOTSAFEPOINT -{ - return eyt_obj_in_img(obj); -} NOINLINE uintptr_t gc_get_stack_ptr(void) { @@ -346,9 +226,6 @@ void jl_gc_wait_for_the_world(jl_ptls_t* gc_all_tls_states, int gc_n_threads) } } - -void jl_gc_wait_for_the_world(jl_ptls_t* gc_all_tls_states, int gc_n_threads); - // malloc wrappers, aligned allocation #if defined(_OS_WINDOWS_) @@ -3242,20 +3119,20 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) int single_threaded = (jl_n_gcthreads == 0 || gc_heap_snapshot_enabled); for (int t_i = 0; t_i < gc_n_threads; t_i++) { jl_ptls_t ptls2 = gc_all_tls_states[t_i]; - jl_gc_markqueue_t *mq2 = mq; - jl_ptls_t ptls_gc_thread = NULL; + jl_ptls_t ptls_dest = ptls; + jl_gc_markqueue_t *mq_dest = mq; if (!single_threaded) { - ptls_gc_thread = gc_all_tls_states[gc_first_tid + t_i % jl_n_gcthreads]; - mq2 = &ptls_gc_thread->mark_queue; + ptls_dest = gc_all_tls_states[gc_first_tid + t_i % jl_n_gcthreads]; + mq_dest = &ptls_dest->mark_queue; } if (ptls2 != NULL) { // 2.1. mark every thread local root - gc_queue_thread_local(mq2, ptls2); + gc_queue_thread_local(mq_dest, ptls2); // 2.2. mark any managed objects in the backtrace buffer // TODO: treat these as roots for gc_heap_snapshot_record - gc_queue_bt_buf(mq2, ptls2); + gc_queue_bt_buf(mq_dest, ptls2); // 2.3. mark every object in the `last_remsets` and `rem_binding` - gc_queue_remset(single_threaded ? ptls : ptls_gc_thread, ptls2); + gc_queue_remset(ptls_dest, ptls2); } } @@ -3696,10 +3573,6 @@ void jl_gc_init(void) arraylist_new(&finalizer_list_marked, 0); arraylist_new(&to_finalize, 0); - arraylist_new(&eytzinger_image_tree, 0); - arraylist_new(&eytzinger_idxs, 0); - arraylist_push(&eytzinger_idxs, (void*)0); - arraylist_push(&eytzinger_image_tree, (void*)1); // outside image gc_num.interval = default_collect_interval; last_long_collect_interval = default_collect_interval; diff --git a/src/gc.h b/src/gc.h index 236d9067f4a6c..3d5328c525e98 100644 --- a/src/gc.h +++ b/src/gc.h @@ -379,6 +379,9 @@ STATIC_INLINE void gc_big_object_link(bigval_t *hdr, bigval_t **list) JL_NOTSAFE *list = hdr; } +extern uv_mutex_t gc_threads_lock; +extern uv_cond_t gc_threads_cond; +extern _Atomic(int) gc_n_threads_marking; void gc_mark_queue_all_roots(jl_ptls_t ptls, jl_gc_markqueue_t *mq); void gc_mark_finlist_(jl_gc_markqueue_t *mq, jl_value_t **fl_begin, jl_value_t **fl_end) JL_NOTSAFEPOINT; @@ -386,6 +389,7 @@ void gc_mark_finlist(jl_gc_markqueue_t *mq, arraylist_t *list, size_t start) JL_NOTSAFEPOINT; void gc_mark_loop_serial_(jl_ptls_t ptls, jl_gc_markqueue_t *mq); void gc_mark_loop_serial(jl_ptls_t ptls); +void gc_mark_loop_parallel(jl_ptls_t ptls, int master); void sweep_stack_pools(void); void jl_gc_debug_init(void); diff --git a/src/init.c b/src/init.c index f88033bc263c8..36e83fdd9c24d 100644 --- a/src/init.c +++ b/src/init.c @@ -818,6 +818,10 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) arraylist_new(&jl_linkage_blobs, 0); arraylist_new(&jl_image_relocs, 0); + arraylist_new(&eytzinger_image_tree, 0); + arraylist_new(&eytzinger_idxs, 0); + arraylist_push(&eytzinger_idxs, (void*)0); + arraylist_push(&eytzinger_image_tree, (void*)1); // outside image jl_ptls_t ptls = jl_init_threadtls(0); #pragma GCC diagnostic push diff --git a/src/julia_internal.h b/src/julia_internal.h index 6c217789cd80b..9167dca652056 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -317,6 +317,8 @@ void print_func_loc(JL_STREAM *s, jl_method_t *m); extern jl_array_t *_jl_debug_method_invalidation JL_GLOBALLY_ROOTED; JL_DLLEXPORT extern arraylist_t jl_linkage_blobs; // external linkage: sysimg/pkgimages JL_DLLEXPORT extern arraylist_t jl_image_relocs; // external linkage: sysimg/pkgimages +extern arraylist_t eytzinger_image_tree; +extern arraylist_t eytzinger_idxs; extern JL_DLLEXPORT size_t jl_page_size; extern jl_function_t *jl_typeinf_func JL_GLOBALLY_ROOTED; diff --git a/src/partr.c b/src/partr.c index 5c7a09ed0bd9a..bed02cf80c219 100644 --- a/src/partr.c +++ b/src/partr.c @@ -108,11 +108,6 @@ void jl_init_threadinginfra(void) void JL_NORETURN jl_finish_task(jl_task_t *t); -extern uv_mutex_t gc_threads_lock; -extern uv_cond_t gc_threads_cond; -extern _Atomic(int) gc_n_threads_marking; -extern void gc_mark_loop_parallel(jl_ptls_t ptls, int master); - // gc thread function void jl_gc_threadfun(void *arg) { diff --git a/src/staticdata.c b/src/staticdata.c index 29bcc78d09986..353382f11a067 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -305,10 +305,127 @@ static arraylist_t object_worklist; // used to mimic recursion by jl_serialize_ // Permanent list of void* (begin, end+1) pairs of system/package images we've loaded previously // together with their module build_ids (used for external linkage) // jl_linkage_blobs.items[2i:2i+1] correspond to build_ids[i] (0-offset indexing) -// TODO: Keep this sorted so that we can use binary-search arraylist_t jl_linkage_blobs; arraylist_t jl_image_relocs; +// Eytzinger tree of images. Used for very fast jl_object_in_image queries +// See https://algorithmica.org/en/eytzinger +arraylist_t eytzinger_image_tree; +arraylist_t eytzinger_idxs; +static uintptr_t img_min; +static uintptr_t img_max; + +static int ptr_cmp(const void *l, const void *r) +{ + uintptr_t left = *(const uintptr_t*)l; + uintptr_t right = *(const uintptr_t*)r; + return (left > right) - (left < right); +} + +// Build an eytzinger tree from a sorted array +static int eytzinger(uintptr_t *src, uintptr_t *dest, size_t i, size_t k, size_t n) +{ + if (k <= n) { + i = eytzinger(src, dest, i, 2 * k, n); + dest[k-1] = src[i]; + i++; + i = eytzinger(src, dest, i, 2 * k + 1, n); + } + return i; +} + +static size_t eyt_obj_idx(jl_value_t *obj) JL_NOTSAFEPOINT +{ + size_t n = eytzinger_image_tree.len - 1; + if (n == 0) + return n; + assert(n % 2 == 0 && "Eytzinger tree not even length!"); + uintptr_t cmp = (uintptr_t) obj; + if (cmp <= img_min || cmp > img_max) + return n; + uintptr_t *tree = (uintptr_t*)eytzinger_image_tree.items; + size_t k = 1; + // note that k preserves the history of how we got to the current node + while (k <= n) { + int greater = (cmp > tree[k - 1]); + k <<= 1; + k |= greater; + } + // Free to assume k is nonzero, since we start with k = 1 + // and cmp > gc_img_min + // This shift does a fast revert of the path until we get + // to a node that evaluated less than cmp. + k >>= (__builtin_ctzll(k) + 1); + assert(k != 0); + assert(k <= n && "Eytzinger tree index out of bounds!"); + assert(tree[k - 1] < cmp && "Failed to find lower bound for object!"); + return k - 1; +} + +//used in staticdata.c after we add an image +void rebuild_image_blob_tree(void) +{ + size_t inc = 1 + jl_linkage_blobs.len - eytzinger_image_tree.len; + assert(eytzinger_idxs.len == eytzinger_image_tree.len); + assert(eytzinger_idxs.max == eytzinger_image_tree.max); + arraylist_grow(&eytzinger_idxs, inc); + arraylist_grow(&eytzinger_image_tree, inc); + eytzinger_idxs.items[eytzinger_idxs.len - 1] = (void*)jl_linkage_blobs.len; + eytzinger_image_tree.items[eytzinger_image_tree.len - 1] = (void*)1; // outside image + for (size_t i = 0; i < jl_linkage_blobs.len; i++) { + assert((uintptr_t) jl_linkage_blobs.items[i] % 4 == 0 && "Linkage blob not 4-byte aligned!"); + // We abuse the pointer here a little so that a couple of properties are true: + // 1. a start and an end are never the same value. This simplifies the binary search. + // 2. ends are always after starts. This also simplifies the binary search. + // We assume that there exist no 0-size blobs, but that's a safe assumption + // since it means nothing could be there anyways + uintptr_t val = (uintptr_t) jl_linkage_blobs.items[i]; + eytzinger_idxs.items[i] = (void*)(val + (i & 1)); + } + qsort(eytzinger_idxs.items, eytzinger_idxs.len - 1, sizeof(void*), ptr_cmp); + img_min = (uintptr_t) eytzinger_idxs.items[0]; + img_max = (uintptr_t) eytzinger_idxs.items[eytzinger_idxs.len - 2] + 1; + eytzinger((uintptr_t*)eytzinger_idxs.items, (uintptr_t*)eytzinger_image_tree.items, 0, 1, eytzinger_idxs.len - 1); + // Reuse the scratch memory to store the indices + // Still O(nlogn) because binary search + for (size_t i = 0; i < jl_linkage_blobs.len; i ++) { + uintptr_t val = (uintptr_t) jl_linkage_blobs.items[i]; + // This is the same computation as in the prior for loop + uintptr_t eyt_val = val + (i & 1); + size_t eyt_idx = eyt_obj_idx((jl_value_t*)(eyt_val + 1)); assert(eyt_idx < eytzinger_idxs.len - 1); + assert(eytzinger_image_tree.items[eyt_idx] == (void*)eyt_val && "Eytzinger tree failed to find object!"); + if (i & 1) + eytzinger_idxs.items[eyt_idx] = (void*)n_linkage_blobs(); + else + eytzinger_idxs.items[eyt_idx] = (void*)(i / 2); + } +} + +static int eyt_obj_in_img(jl_value_t *obj) JL_NOTSAFEPOINT +{ + assert((uintptr_t) obj % 4 == 0 && "Object not 4-byte aligned!"); + int idx = eyt_obj_idx(obj); + // Now we use a tiny trick: tree[idx] & 1 is whether or not tree[idx] is a + // start (0) or an end (1) of a blob. If it's a start, then the object is + // in the image, otherwise it is not. + int in_image = ((uintptr_t)eytzinger_image_tree.items[idx] & 1) == 0; + return in_image; +} + +size_t external_blob_index(jl_value_t *v) JL_NOTSAFEPOINT +{ + assert((uintptr_t) v % 4 == 0 && "Object not 4-byte aligned!"); + int eyt_idx = eyt_obj_idx(v); + // We fill the invalid slots with the length, so we can just return that + size_t idx = (size_t) eytzinger_idxs.items[eyt_idx]; + return idx; +} + +uint8_t jl_object_in_image(jl_value_t *obj) JL_NOTSAFEPOINT +{ + return eyt_obj_in_img(obj); +} + // hash of definitions for predefined function pointers static htable_t fptr_to_id; void *native_functions; // opaque jl_native_code_desc_t blob used for fetching data from LLVM From f77ad0a93e283cf104fbc8c38380bfe062dfd765 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Tue, 2 May 2023 14:16:57 +0200 Subject: [PATCH 625/775] Ensure LLVM function attributes are preserved by always calling CloneFunctionInto. --- src/llvm-remove-addrspaces.cpp | 6 ++---- test/llvmpasses/remove-addrspaces.ll | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/llvm-remove-addrspaces.cpp b/src/llvm-remove-addrspaces.cpp index 4a3290da0ecf5..50c3be1b83444 100644 --- a/src/llvm-remove-addrspaces.cpp +++ b/src/llvm-remove-addrspaces.cpp @@ -323,7 +323,7 @@ bool removeAddrspaces(Module &M, AddrspaceRemapFunction ASRemapper) Function *NF = Function::Create( NFTy, F->getLinkage(), F->getAddressSpace(), Name, &M); - NF->copyAttributesFrom(F); + // no need to copy attributes here, that's done by CloneFunctionInto VMap[F] = NF; } @@ -356,11 +356,9 @@ bool removeAddrspaces(Module &M, AddrspaceRemapFunction ASRemapper) // Similarly, copy over and rewrite function bodies for (Function *F : Functions) { - if (F->isDeclaration()) - continue; - Function *NF = cast(VMap[F]); LLVM_DEBUG(dbgs() << "Processing function " << NF->getName() << "\n"); + // we also need this to run for declarations, or attributes won't be copied Function::arg_iterator DestI = NF->arg_begin(); for (Function::const_arg_iterator I = F->arg_begin(); I != F->arg_end(); diff --git a/test/llvmpasses/remove-addrspaces.ll b/test/llvmpasses/remove-addrspaces.ll index 26406d2ca7d98..a748b3843bacb 100644 --- a/test/llvmpasses/remove-addrspaces.ll +++ b/test/llvmpasses/remove-addrspaces.ll @@ -111,7 +111,7 @@ define void @byval_type([1 x {} addrspace(10)*] addrspace(11)* byval([1 x {} add } -; COM: check that other function attributes are preserved +; COM: check that function attributes are preserved on declarations too declare void @convergent_function() #0 attributes #0 = { convergent } ; CHECK: attributes #0 = { convergent } From b815bb160932da5300a8db71cae8d8970faaa8d5 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Tue, 2 May 2023 14:19:42 +0200 Subject: [PATCH 626/775] Don't explicitly remap the personality function. LLVM does this already. --- src/llvm-remove-addrspaces.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/llvm-remove-addrspaces.cpp b/src/llvm-remove-addrspaces.cpp index 50c3be1b83444..32bd4e563ac92 100644 --- a/src/llvm-remove-addrspaces.cpp +++ b/src/llvm-remove-addrspaces.cpp @@ -408,9 +408,6 @@ bool removeAddrspaces(Module &M, AddrspaceRemapFunction ASRemapper) } NF->setAttributes(Attrs); - if (F->hasPersonalityFn()) - NF->setPersonalityFn(MapValue(F->getPersonalityFn(), VMap)); - copyComdat(NF, F); RemoveNoopAddrSpaceCasts(NF); From 82f76e8a63275e74e5e1aa028884da80d6056a42 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 2 May 2023 11:40:47 -0400 Subject: [PATCH 627/775] ml-matches: skip intersection by world-bounds (#49590) Should help speed up package loading some, by avoid complicated self-intersections, which would then be filtered out anyways from the returned results. It is not a frequent pattern, but it was observed in occasional cases, and is such an easy, obvious improvement. --- src/gf.c | 45 +++++++++++++++++++---------------------- src/julia_internal.h | 2 ++ src/typemap.c | 48 ++++++++++++++++++++------------------------ 3 files changed, 45 insertions(+), 50 deletions(-) diff --git a/src/gf.c b/src/gf.c index 49e6f3b9d4162..baaafab3814f2 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1481,12 +1481,9 @@ struct matches_env { static int get_intersect_visitor(jl_typemap_entry_t *oldentry, struct typemap_intersection_env *closure0) { struct matches_env *closure = container_of(closure0, struct matches_env, match); - if (oldentry == closure->newentry) - return 1; - if (oldentry->max_world < ~(size_t)0 || oldentry->min_world == closure->newentry->min_world) - // skip if no world has both active - // also be careful not to try to scan something from the current dump-reload though - return 1; + assert(oldentry != closure->newentry && "entry already added"); + assert(oldentry->min_world <= closure->newentry->min_world && "old method cannot be newer than new method"); + assert(oldentry->max_world == ~(size_t)0 && "method cannot be added at the same time as method deleted"); // don't need to consider other similar methods if this oldentry will always fully intersect with them and dominates all of them typemap_slurp_search(oldentry, &closure->match); jl_method_t *oldmethod = oldentry->func.method; @@ -1500,7 +1497,7 @@ static int get_intersect_visitor(jl_typemap_entry_t *oldentry, struct typemap_in return 1; } -static jl_value_t *get_intersect_matches(jl_typemap_t *defs, jl_typemap_entry_t *newentry, jl_typemap_entry_t **replaced, int8_t offs) +static jl_value_t *get_intersect_matches(jl_typemap_t *defs, jl_typemap_entry_t *newentry, jl_typemap_entry_t **replaced, int8_t offs, size_t world) { jl_tupletype_t *type = newentry->sig; jl_tupletype_t *ttypes = (jl_tupletype_t*)jl_unwrap_unionall((jl_value_t*)type); @@ -1513,7 +1510,9 @@ static jl_value_t *get_intersect_matches(jl_typemap_t *defs, jl_typemap_entry_t else va = NULL; } + // search for all intersecting methods active in the previous world, to determine the changes needed to be made for the next world struct matches_env env = {{get_intersect_visitor, (jl_value_t*)type, va, /* .search_slurp = */ 0, + /* .min_valid = */ world, /* .max_valid = */ world, /* .ti = */ NULL, /* .env = */ jl_emptysvec, /* .issubty = */ 0}, /* .newentry = */ newentry, /* .shadowed */ NULL, /* .replaced */ NULL}; JL_GC_PUSH3(&env.match.env, &env.match.ti, &env.shadowed); @@ -2006,7 +2005,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method jl_typemap_insert(&mt->defs, (jl_value_t*)mt, newentry, jl_cachearg_offset(mt)); jl_typemap_entry_t *replaced = NULL; // then check what entries we replaced - oldvalue = get_intersect_matches(jl_atomic_load_relaxed(&mt->defs), newentry, &replaced, jl_cachearg_offset(mt)); + oldvalue = get_intersect_matches(jl_atomic_load_relaxed(&mt->defs), newentry, &replaced, jl_cachearg_offset(mt), max_world); int invalidated = 0; if (replaced) { oldvalue = (jl_value_t*)replaced; @@ -3216,9 +3215,6 @@ struct ml_matches_env { int include_ambiguous; // results: jl_value_t *t; // array of method matches - size_t min_valid; - size_t max_valid; - // temporary: jl_method_match_t *matc; // current working method match }; @@ -3246,22 +3242,22 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio return 1; if (closure->world < ml->min_world) { // ignore method table entries that are part of a later world - if (closure->max_valid >= ml->min_world) - closure->max_valid = ml->min_world - 1; + if (closure->match.max_valid >= ml->min_world) + closure->match.max_valid = ml->min_world - 1; return 1; } else if (closure->world > ml->max_world) { // ignore method table entries that have been replaced in the current world - if (closure->min_valid <= ml->max_world) - closure->min_valid = ml->max_world + 1; + if (closure->match.min_valid <= ml->max_world) + closure->match.min_valid = ml->max_world + 1; return 1; } else { - // intersect the env valid range with method's valid range - if (closure->min_valid < ml->min_world) - closure->min_valid = ml->min_world; - if (closure->max_valid > ml->max_world) - closure->max_valid = ml->max_world; + // intersect the env valid range with method's inclusive valid range + if (closure->match.min_valid < ml->min_world) + closure->match.min_valid = ml->min_world; + if (closure->match.max_valid > ml->max_world) + closure->match.max_valid = ml->max_world; } jl_method_t *meth = ml->func.method; if (closure->lim >= 0 && jl_is_dispatch_tupletype(meth->sig)) { @@ -3323,9 +3319,10 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, va = NULL; } struct ml_matches_env env = {{ml_matches_visitor, (jl_value_t*)type, va, /* .search_slurp = */ 0, + /* .min_valid = */ *min_valid, /* .max_valid = */ *max_valid, /* .ti = */ NULL, /* .env = */ jl_emptysvec, /* .issubty = */ 0}, intersections, world, lim, include_ambiguous, /* .t = */ jl_an_empty_vec_any, - /* .min_valid = */ *min_valid, /* .max_valid = */ *max_valid, /* .matc = */ NULL}; + /* .matc = */ NULL}; struct jl_typemap_assoc search = {(jl_value_t*)type, world, jl_emptysvec, 1, ~(size_t)0}; jl_value_t *isect2 = NULL; JL_GC_PUSH6(&env.t, &env.matc, &env.match.env, &search.env, &env.match.ti, &isect2); @@ -3400,8 +3397,8 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, return jl_nothing; } } - *min_valid = env.min_valid; - *max_valid = env.max_valid; + *min_valid = env.match.min_valid; + *max_valid = env.match.max_valid; // done with many of these values now env.match.ti = NULL; env.matc = NULL; env.match.env = NULL; search.env = NULL; size_t i, j, len = jl_array_len(env.t); @@ -3823,7 +3820,7 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_method_t *meth = env.matc->method; jl_svec_t *tpenv = env.matc->sparams; JL_LOCK(&mt->writelock); - cache_method(mt, &mt->cache, (jl_value_t*)mt, (jl_tupletype_t*)unw, meth, world, env.min_valid, env.max_valid, tpenv); + cache_method(mt, &mt->cache, (jl_value_t*)mt, (jl_tupletype_t*)unw, meth, world, env.match.min_valid, env.match.max_valid, tpenv); JL_UNLOCK(&mt->writelock); } } diff --git a/src/julia_internal.h b/src/julia_internal.h index 9167dca652056..c6c9f3f8e21df 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1470,6 +1470,8 @@ struct typemap_intersection_env { jl_value_t *const va; // the tparam0 for the vararg in type, if applicable (or NULL) size_t search_slurp; // output values + size_t min_valid; + size_t max_valid; jl_value_t *ti; // intersection type jl_svec_t *env; // intersection env (initialize to null to perform intersection without an environment) int issubty; // if `a <: b` is true in `intersect(a,b)` diff --git a/src/typemap.c b/src/typemap.c index 97ea31928251d..1bdbe52a974dd 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -530,34 +530,26 @@ static int jl_typemap_intersection_node_visitor(jl_typemap_entry_t *ml, struct t // mark this `register` because (for branch prediction) // that can be absolutely critical for speed register jl_typemap_intersection_visitor_fptr fptr = closure->fptr; - while (ml != (void*)jl_nothing) { - if (closure->type == (jl_value_t*)ml->sig) { - // fast-path for the intersection of a type with itself - if (closure->env) - closure->env = jl_outer_unionall_vars((jl_value_t*)ml->sig); - closure->ti = closure->type; - closure->issubty = 1; - if (!fptr(ml, closure)) - return 0; + for (; ml != (void*)jl_nothing; ml = jl_atomic_load_relaxed(&ml->next)) { + if (closure->max_valid < ml->min_world) + continue; + if (closure->min_valid > ml->max_world) + continue; + jl_svec_t **penv = NULL; + if (closure->env) { + closure->env = jl_emptysvec; + penv = &closure->env; } - else { - jl_svec_t **penv = NULL; - if (closure->env) { - closure->env = jl_emptysvec; - penv = &closure->env; - } - closure->ti = jl_type_intersection_env_s(closure->type, (jl_value_t*)ml->sig, penv, &closure->issubty); - if (closure->ti != (jl_value_t*)jl_bottom_type) { - // In some corner cases type intersection is conservative and returns something - // for intersect(A, B) even though A is a dispatch tuple and !(A <: B). - // For dispatch purposes in such a case we know there's no match. This check - // fixes issue #30394. - if (closure->issubty || !jl_is_dispatch_tupletype(closure->type)) - if (!fptr(ml, closure)) - return 0; - } + closure->ti = jl_type_intersection_env_s(closure->type, (jl_value_t*)ml->sig, penv, &closure->issubty); + if (closure->ti != (jl_value_t*)jl_bottom_type) { + // In some corner cases type intersection is conservative and returns something + // for intersect(A, B) even though A is a dispatch tuple and !(A <: B). + // For dispatch purposes in such a case we know there's no match. This check + // fixes issue #30394. + if (closure->issubty || !jl_is_dispatch_tupletype(closure->type)) + if (!fptr(ml, closure)) + return 0; } - ml = jl_atomic_load_relaxed(&ml->next); } return 1; } @@ -844,6 +836,10 @@ static jl_typemap_entry_t *jl_typemap_entry_assoc_by_type( size_t n = jl_nparams(unw); int typesisva = n == 0 ? 0 : jl_is_vararg(jl_tparam(unw, n-1)); for (; ml != (void*)jl_nothing; ml = jl_atomic_load_relaxed(&ml->next)) { + if (search->max_valid < ml->min_world) + continue; + if (search->min_valid > ml->max_world) + continue; size_t lensig = jl_nparams(jl_unwrap_unionall((jl_value_t*)ml->sig)); if (lensig == n || (ml->va && lensig <= n+1)) { int resetenv = 0, ismatch = 1; From 6052e445dc5e2687e3c262fa5ba4b84a23dc4ed4 Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Tue, 2 May 2023 18:00:04 +0000 Subject: [PATCH 628/775] Improve llvmpasses test quality (#49594) --- .../{alloc-opt-pass.jl => alloc-opt-pass.ll} | 125 ++++++++---------- test/llvmpasses/alloc-opt-unsized.ll | 2 + test/llvmpasses/cpu-features.ll | 2 + test/llvmpasses/final-lower-gc-addrspaces.ll | 2 + test/llvmpasses/final-lower-gc.ll | 2 + test/llvmpasses/gcroots.ll | 2 + test/llvmpasses/julia-licm-fail.ll | 96 ++++++++++++++ test/llvmpasses/julia-licm-missed.ll | 109 +++++++++++++++ test/llvmpasses/julia-licm.ll | 24 ++++ test/llvmpasses/late-lower-gc-addrspaces.ll | 2 + test/llvmpasses/late-lower-gc.ll | 2 + test/llvmpasses/lower-handlers-addrspaces.ll | 2 + test/llvmpasses/lower-handlers.ll | 2 + test/llvmpasses/muladd.ll | 33 +++++ .../multiversioning-annotate-only.ll | 2 + test/llvmpasses/multiversioning-clone-only.ll | 2 + test/llvmpasses/pipeline-o0.jl | 2 + test/llvmpasses/pipeline-o2-allocs.jl | 10 ++ test/llvmpasses/pipeline-o2-broadcast.jl | 2 + test/llvmpasses/pipeline-o2.jl | 2 + .../propagate-addrspace-non-zero.ll | 2 + test/llvmpasses/propagate-addrspace.ll | 2 + test/llvmpasses/refinements.ll | 2 + test/llvmpasses/remove-addrspaces.ll | 2 + test/llvmpasses/returnstwicegc.ll | 2 + test/llvmpasses/simdloop.ll | 2 + 26 files changed, 367 insertions(+), 70 deletions(-) rename test/llvmpasses/{alloc-opt-pass.jl => alloc-opt-pass.ll} (55%) create mode 100644 test/llvmpasses/julia-licm-fail.ll create mode 100644 test/llvmpasses/julia-licm-missed.ll diff --git a/test/llvmpasses/alloc-opt-pass.jl b/test/llvmpasses/alloc-opt-pass.ll similarity index 55% rename from test/llvmpasses/alloc-opt-pass.jl rename to test/llvmpasses/alloc-opt-pass.ll index 7ea9b6eff3ecb..4ce152669246f 100644 --- a/test/llvmpasses/alloc-opt-pass.jl +++ b/test/llvmpasses/alloc-opt-pass.ll @@ -1,29 +1,24 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license +; This file is a part of Julia. License is MIT: https://julialang.org/license -# RUN: julia --startup-file=no %s | opt -enable-new-pm=0 -load libjulia-codegen%shlibext -AllocOpt -S - | FileCheck %s -# RUN: julia --startup-file=no %s | opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(AllocOpt)' -S - | FileCheck %s +; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -AllocOpt -S %s | FileCheck %s +; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(AllocOpt)' -S %s | FileCheck %s -isz = sizeof(UInt) == 8 ? "i64" : "i32" - -println(""" @tag = external addrspace(10) global {} -""") -# Test that the gc_preserve intrinsics are deleted directly. +; Test that the gc_preserve intrinsics are deleted directly. -# CHECK-LABEL: @preserve_branches -# CHECK: call {}*** @julia.ptls_states() -# CHECK: L1: -# CHECK-NOT: @llvm.julia.gc_preserve_begin -# CHECK-NEXT: @external_function() -# CHECK-NEXT: br i1 %b2, label %L2, label %L3 +; CHECK-LABEL: @preserve_branches +; CHECK: call {}*** @julia.ptls_states() +; CHECK: L1: +; CHECK-NOT: @llvm.julia.gc_preserve_begin +; CHECK-NEXT: @external_function() +; CHECK-NEXT: br i1 %b2, label %L2, label %L3 -# CHECK: L2: -# CHECK: @external_function() -# CHECK-NEXT: br label %L3 +; CHECK: L2: +; CHECK: @external_function() +; CHECK-NEXT: br label %L3 -# CHECK: L3: -println(""" +; CHECK: L3: define void @preserve_branches(i8* %fptr, i1 %b, i1 %b2) { %pgcstack = call {}*** @julia.get_pgcstack() %ptls = call {}*** @julia.ptls_states() @@ -31,7 +26,7 @@ define void @preserve_branches(i8* %fptr, i1 %b, i1 %b2) { br i1 %b, label %L1, label %L3 L1: - %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, $isz 8, {} addrspace(10)* @tag) + %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag) %tok = call token (...) @llvm.julia.gc_preserve_begin({} addrspace(10)* nonnull %v) call void @external_function() br i1 %b2, label %L2, label %L3 @@ -43,22 +38,20 @@ L2: L3: ret void } -""") -# CHECK-LABEL: }{{$}} - -# CHECK-LABEL: @preserve_branches2 -# CHECK: call {}*** @julia.ptls_states() -# CHECK: L1: -# CHECK-NEXT: @llvm.julia.gc_preserve_begin{{.*}}{} addrspace(10)* %v2 -# CHECK-NEXT: @external_function() -# CHECK-NEXT: br i1 %b2, label %L2, label %L3 - -# CHECK: L2: -# CHECK: @external_function() -# CHECK-NEXT: br label %L3 - -# CHECK: L3: -println(""" +; CHECK-LABEL: }{{$}} + +; CHECK-LABEL: @preserve_branches2 +; CHECK: call {}*** @julia.ptls_states() +; CHECK: L1: +; CHECK-NEXT: @llvm.julia.gc_preserve_begin{{.*}}{} addrspace(10)* %v2 +; CHECK-NEXT: @external_function() +; CHECK-NEXT: br i1 %b2, label %L2, label %L3 + +; CHECK: L2: +; CHECK: @external_function() +; CHECK-NEXT: br label %L3 + +; CHECK: L3: define void @preserve_branches2(i8* %fptr, i1 %b, i1 %b2) { %pgcstack = call {}*** @julia.get_pgcstack() %ptls = call {}*** @julia.ptls_states() @@ -67,7 +60,7 @@ define void @preserve_branches2(i8* %fptr, i1 %b, i1 %b2) { br i1 %b, label %L1, label %L3 L1: - %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, $isz 8, {} addrspace(10)* @tag) + %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag) %tok = call token (...) @llvm.julia.gc_preserve_begin({} addrspace(10)* %v, {} addrspace(10)* nonnull %v2) call void @external_function() br i1 %b2, label %L2, label %L3 @@ -79,57 +72,50 @@ L2: L3: ret void } -""") -# CHECK-LABEL: }{{$}} - -# CHECK-LABEL: @legal_int_types -# CHECK: alloca [12 x i8] -# CHECK-NOT: alloca i96 -# CHECK: ret void -println(""" +; CHECK-LABEL: }{{$}} + +; CHECK-LABEL: @legal_int_types +; CHECK: alloca [12 x i8] +; CHECK-NOT: alloca i96 +; CHECK: ret void define void @legal_int_types() { %pgcstack = call {}*** @julia.get_pgcstack() %ptls = call {}*** @julia.ptls_states() %ptls_i8 = bitcast {}*** %ptls to i8* - %var1 = call {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, $isz 12, {} addrspace(10)* @tag) + %var1 = call {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 12, {} addrspace(10)* @tag) %var2 = addrspacecast {} addrspace(10)* %var1 to {} addrspace(11)* %var3 = call {}* @julia.pointer_from_objref({} addrspace(11)* %var2) ret void } -""") -# CHECK-LABEL: }{{$}} - +; CHECK-LABEL: }{{$}} -println(""" declare void @external_function() declare {} addrspace(10)* @external_function2() declare {}*** @julia.ptls_states() declare {}*** @julia.get_pgcstack() -declare noalias {} addrspace(10)* @julia.gc_alloc_obj(i8*, $isz, {} addrspace(10)*) +declare noalias {} addrspace(10)* @julia.gc_alloc_obj(i8*, i64, {} addrspace(10)*) declare {}* @julia.pointer_from_objref({} addrspace(11)*) declare void @llvm.memcpy.p11i8.p0i8.i64(i8 addrspace(11)* nocapture writeonly, i8* nocapture readonly, i64, i32, i1) declare token @llvm.julia.gc_preserve_begin(...) declare void @llvm.julia.gc_preserve_end(token) -""") - -# CHECK-LABEL: @memref_collision -# CHECK: call {}*** @julia.ptls_states() -# CHECK-NOT: store {} -# CHECK: store i -# CHECK-NOT: store {} -# CHECK: L1: -# CHECK: load {} -# CHECK: L2: -# CHECK: load i -println(""" -define void @memref_collision($isz %x) { + +; CHECK-LABEL: @memref_collision +; CHECK: call {}*** @julia.ptls_states() +; CHECK-NOT: store {} +; CHECK: store i +; CHECK-NOT: store {} +; CHECK: L1: +; CHECK: load {} +; CHECK: L2: +; CHECK: load i +define void @memref_collision(i64 %x) { %pgcstack = call {}*** @julia.get_pgcstack() %ptls = call {}*** @julia.ptls_states() %ptls_i8 = bitcast {}*** %ptls to i8* - %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, $isz 8, {} addrspace(10)* @tag) - %v_p = bitcast {} addrspace(10)* %v to $isz addrspace(10)* - store $isz %x, $isz addrspace(10)* %v_p + %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag) + %v_p = bitcast {} addrspace(10)* %v to i64 addrspace(10)* + store i64 %x, i64 addrspace(10)* %v_p br i1 0, label %L1, label %L2 L1: @@ -138,9 +124,8 @@ L1: ret void L2: - %v2 = bitcast {} addrspace(10)* %v to $isz addrspace(10)* + %v2 = bitcast {} addrspace(10)* %v to i64 addrspace(10)* %v2_x = load i64, i64 addrspace(10)* %v2 ret void } -""") -# CHECK-LABEL: }{{$}} +; CHECK-LABEL: }{{$}} diff --git a/test/llvmpasses/alloc-opt-unsized.ll b/test/llvmpasses/alloc-opt-unsized.ll index f7ea31fde6b05..8a21091ce558c 100644 --- a/test/llvmpasses/alloc-opt-unsized.ll +++ b/test/llvmpasses/alloc-opt-unsized.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -AllocOpt -S %s | FileCheck %s source_filename = "text" diff --git a/test/llvmpasses/cpu-features.ll b/test/llvmpasses/cpu-features.ll index ccb8cc69f0f66..1a04db5749b39 100644 --- a/test/llvmpasses/cpu-features.ll +++ b/test/llvmpasses/cpu-features.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -CPUFeatures -simplifycfg -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='CPUFeatures,simplifycfg' -S %s | FileCheck %s diff --git a/test/llvmpasses/final-lower-gc-addrspaces.ll b/test/llvmpasses/final-lower-gc-addrspaces.ll index 54fc19566ff32..61e9e33875078 100644 --- a/test/llvmpasses/final-lower-gc-addrspaces.ll +++ b/test/llvmpasses/final-lower-gc-addrspaces.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -FinalLowerGC -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='FinalLowerGC' -S %s | FileCheck %s diff --git a/test/llvmpasses/final-lower-gc.ll b/test/llvmpasses/final-lower-gc.ll index 7fc2d1b04ca2d..6f1be3d240ae4 100644 --- a/test/llvmpasses/final-lower-gc.ll +++ b/test/llvmpasses/final-lower-gc.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -FinalLowerGC -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='FinalLowerGC' -S %s | FileCheck %s diff --git a/test/llvmpasses/gcroots.ll b/test/llvmpasses/gcroots.ll index 84f120712734b..eefd847bf68fa 100644 --- a/test/llvmpasses/gcroots.ll +++ b/test/llvmpasses/gcroots.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -LateLowerGCFrame -FinalLowerGC -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame),FinalLowerGC' -S %s | FileCheck %s diff --git a/test/llvmpasses/julia-licm-fail.ll b/test/llvmpasses/julia-licm-fail.ll new file mode 100644 index 0000000000000..250ad620b05e6 --- /dev/null +++ b/test/llvmpasses/julia-licm-fail.ll @@ -0,0 +1,96 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + +; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -JuliaLICM -S %s | FileCheck %s +; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='JuliaLICM' -S %s | FileCheck %s + +; COM: This file contains functions that should not trigger allocations to be hoisted out of loops + +@tag = external addrspace(10) global {}, align 16 + +; COM: Tests that an escape in a loop prevents hoisting of the allocation +; CHECK-LABEL: @julia_escape_alloc +define void @julia_escape_alloc(i1 %ret) { +top: + %pgcstack = call {}*** @julia.get_pgcstack() + %current_task = bitcast {}*** %pgcstack to {}** +; CHECK: br label %preheader + br label %preheader +; CHECK: preheader: +preheader: +; CHECK-NOT: julia.gc_alloc_obj +; CHECK-NEXT: br label %loop + br label %loop +; CHECK: loop: +loop: +; CHECK-NEXT: %alloc = call noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}** nonnull %current_task, i64 8, {} addrspace(10)* @tag) + %alloc = call noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}** nonnull %current_task, i64 8, {} addrspace(10)* @tag) +; CHECK-NEXT: %ignore = call {} addrspace(10)* @escape({} addrspace(10)* %alloc) + %ignore = call {} addrspace(10)* @escape({} addrspace(10)* %alloc) + br i1 %ret, label %return, label %loop +return: + ret void +} + +; COM: Tests that addrescape in a loop prevents hoisting of the allocation +; CHECK-LABEL: @julia_addrescape_alloc +define void @julia_addrescape_alloc(i1 %ret) { +top: + %pgcstack = call {}*** @julia.get_pgcstack() + %current_task = bitcast {}*** %pgcstack to {}** +; CHECK: br label %preheader + br label %preheader +; CHECK: preheader: +preheader: +; CHECK-NOT: julia.gc_alloc_obj +; CHECK-NEXT: br label %loop + br label %loop +; CHECK: loop: +loop: +; CHECK-NEXT: %alloc = call noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}** nonnull %current_task, i64 8, {} addrspace(10)* @tag) + %alloc = call noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}** nonnull %current_task, i64 8, {} addrspace(10)* @tag) +; CHECK-NEXT: %cast = addrspacecast {} addrspace(10)* %alloc to {} addrspace(11)* + %cast = addrspacecast {} addrspace(10)* %alloc to {} addrspace(11)* +; CHECK-NEXT: %ptr = call nonnull {}* @julia.pointer_from_objref({} addrspace(11)* %cast) + %ptr = call nonnull {}* @julia.pointer_from_objref({} addrspace(11)* %cast) + br i1 %ret, label %return, label %loop +return: + ret void +} + +declare void @julia.write_barrier({}*, ...) + +declare {}*** @julia.get_pgcstack() + +; Function Attrs: allocsize(1) +declare noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}**, i64, {} addrspace(10)*) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2 + +; Function Attrs: inaccessiblemem_or_argmemonly +declare void @ijl_gc_queue_root({} addrspace(10)*) #3 + +; Function Attrs: allocsize(1) +declare noalias nonnull {} addrspace(10)* @ijl_gc_pool_alloc(i8*, i32, i32) #1 + +; Function Attrs: allocsize(1) +declare noalias nonnull {} addrspace(10)* @ijl_gc_big_alloc(i8*, i64) #1 + +; COM: escape to make it easy to find +declare nonnull {} addrspace(10)* @escape({} addrspace(10)*) + +; COM: addrescape function +declare nonnull {}* @julia.pointer_from_objref({} addrspace(11)*) + +attributes #0 = { "probe-stack"="inline-asm" } +attributes #1 = { allocsize(1) } +attributes #2 = { argmemonly nofree nosync nounwind willreturn } +attributes #3 = { inaccessiblemem_or_argmemonly } + +!llvm.module.flags = !{!0, !1} + +!0 = !{i32 2, !"Dwarf Version", i32 4} +!1 = !{i32 2, !"Debug Info Version", i32 3} diff --git a/test/llvmpasses/julia-licm-missed.ll b/test/llvmpasses/julia-licm-missed.ll new file mode 100644 index 0000000000000..977b8e2a787f9 --- /dev/null +++ b/test/llvmpasses/julia-licm-missed.ll @@ -0,0 +1,109 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + +; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -JuliaLICM -S %s | FileCheck %s +; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='JuliaLICM' -S %s | FileCheck %s + +; COM: This file contains functions that currently do not trigger allocations to be hoisted out of loops +; COM: i.e. they are missed optimizations +; COM: Better optimization could potentially enable allocations to be hoisted out of these loops + +@tag = external addrspace(10) global {}, align 16 + +; COM: Currently we don't hoist allocations that have references stored into them out of loops +; COM: This is because we need to insert write barriers for the stores when the storee does not +; COM: dominate the allocation after it has been moved out of the loop +; CHECK-LABEL: @julia_refstore +define void @julia_refstore({} addrspace(10)* %obj, i1 %ret) { +top: + %pgcstack = call {}*** @julia.get_pgcstack() + %current_task = bitcast {}*** %pgcstack to {}** +; CHECK: br label %preheader + br label %preheader +; CHECK: preheader: +preheader: +; CHECK-NOT: julia.gc_alloc_obj +; CHECK-NEXT: br label %loop + br label %loop +; CHECK: loop: +loop: +; CHECK-NEXT: %alloc = call noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}** nonnull %current_task, i64 8, {} addrspace(10)* @tag) + %alloc = call noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}** nonnull %current_task, i64 8, {} addrspace(10)* @tag) +; CHECK-NEXT: %derived = addrspacecast {} addrspace(10)* %alloc to {} addrspace(11)* + %derived = addrspacecast {} addrspace(10)* %alloc to {} addrspace(11)* +; CHECK-NEXT: %ptr = bitcast {} addrspace(11)* %derived to {} addrspace(10)* addrspace(11)* + %ptr = bitcast {} addrspace(11)* %derived to {} addrspace(10)* addrspace(11)* +; CHECK-NEXT: store {} addrspace(10)* %obj, {} addrspace(10)* addrspace(11)* %ptr, align 8 + store {} addrspace(10)* %obj, {} addrspace(10)* addrspace(11)* %ptr, align 8 + br i1 %ret, label %return, label %loop +return: + ret void +} + +; COM: Currently our LLVM-level escape analysis doesn't handle phi nodes at all +; COM: so this allocation is counted as 'escaping' despite the fact that it's +; COM: clearly dead +; CHECK-LABEL: @julia_phi +define void @julia_phi({} addrspace(10)* %obj, i1 %ret) { +top: + %pgcstack = call {}*** @julia.get_pgcstack() + %current_task = bitcast {}*** %pgcstack to {}** +; CHECK: br label %preheader + br label %preheader +; CHECK: preheader: +preheader: +; CHECK-NOT: julia.gc_alloc_obj +; CHECK-NEXT: br label %loop + br label %loop +; CHECK: loop: +loop: +; CHECK-NEXT: %alloc = call noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}** nonnull %current_task, i64 8, {} addrspace(10)* @tag) + %alloc = call noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}** nonnull %current_task, i64 8, {} addrspace(10)* @tag) + br label %other +; CHECK: other: +other: +; CHECK-NEXT: %phi = phi {} addrspace(10)* [ %alloc, %loop ] + %phi = phi {} addrspace(10)* [ %alloc, %loop ] + br i1 %ret, label %return, label %loop +return: + ret void +} + + + +declare void @julia.write_barrier({}*, ...) + +declare {}*** @julia.get_pgcstack() + +; Function Attrs: allocsize(1) +declare noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}**, i64, {} addrspace(10)*) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2 + +; Function Attrs: inaccessiblemem_or_argmemonly +declare void @ijl_gc_queue_root({} addrspace(10)*) #3 + +; Function Attrs: allocsize(1) +declare noalias nonnull {} addrspace(10)* @ijl_gc_pool_alloc(i8*, i32, i32) #1 + +; Function Attrs: allocsize(1) +declare noalias nonnull {} addrspace(10)* @ijl_gc_big_alloc(i8*, i64) #1 + +; COM: escape to make it easy to find +declare nonnull {} addrspace(10)* @escape({} addrspace(10)*) + +; COM: addrescape function +declare nonnull {}* @julia.pointer_from_objref({} addrspace(11)*) + +attributes #0 = { "probe-stack"="inline-asm" } +attributes #1 = { allocsize(1) } +attributes #2 = { argmemonly nofree nosync nounwind willreturn } +attributes #3 = { inaccessiblemem_or_argmemonly } + +!llvm.module.flags = !{!0, !1} + +!0 = !{i32 2, !"Dwarf Version", i32 4} +!1 = !{i32 2, !"Debug Info Version", i32 3} diff --git a/test/llvmpasses/julia-licm.ll b/test/llvmpasses/julia-licm.ll index 0c7cf9a640ef7..dbef009204586 100644 --- a/test/llvmpasses/julia-licm.ll +++ b/test/llvmpasses/julia-licm.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -JuliaLICM -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='JuliaLICM' -S %s | FileCheck %s @@ -7,6 +9,8 @@ declare void @julia.write_barrier({}*, ...) declare {}*** @julia.get_pgcstack() +; COM: check basic allocation hoisting functionality +; CHECK-LABEL: @julia_allocation_hoist define nonnull {} addrspace(10)* @julia_allocation_hoist(i64 signext %0) #0 { top: %1 = call {}*** @julia.get_pgcstack() @@ -40,6 +44,26 @@ L22: ; preds = %L4, %L22 br i1 %.not, label %L3.loopexit, label %L22 } +; COM: check that we hoist the allocation out of the loop despite returning the allocation +; CHECK-LABEL: @julia_hoist_returned +define nonnull {} addrspace(10)* @julia_hoist_returned(i64 signext %n, i1 zeroext %ret) { +top: + %pgcstack = call {}*** @julia.get_pgcstack() + %current_task = bitcast {}*** %pgcstack to {}** +; CHECK: br label %preheader + br label %preheader +; CHECK: preheader: +preheader: +; CHECK-NEXT: %alloc = call noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}** nonnull %current_task, i64 8, {} addrspace(10)* @tag) +; CHECK-NEXT: br label %loop + br label %loop +loop: + %alloc = call noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}** nonnull %current_task, i64 8, {} addrspace(10)* @tag) + br i1 %ret, label %return, label %loop +return: + ret {} addrspace(10)* %alloc +} + ; Function Attrs: allocsize(1) declare noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}**, i64, {} addrspace(10)*) #1 diff --git a/test/llvmpasses/late-lower-gc-addrspaces.ll b/test/llvmpasses/late-lower-gc-addrspaces.ll index 8bdcbdeacf8f3..84a6da9f2554d 100644 --- a/test/llvmpasses/late-lower-gc-addrspaces.ll +++ b/test/llvmpasses/late-lower-gc-addrspaces.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -LateLowerGCFrame -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame)' -S %s | FileCheck %s diff --git a/test/llvmpasses/late-lower-gc.ll b/test/llvmpasses/late-lower-gc.ll index 04adfe72ff0b6..98c472771aaf9 100644 --- a/test/llvmpasses/late-lower-gc.ll +++ b/test/llvmpasses/late-lower-gc.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -LateLowerGCFrame -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame)' -S %s | FileCheck %s diff --git a/test/llvmpasses/lower-handlers-addrspaces.ll b/test/llvmpasses/lower-handlers-addrspaces.ll index 9770684574034..fcc4dc0114c21 100644 --- a/test/llvmpasses/lower-handlers-addrspaces.ll +++ b/test/llvmpasses/lower-handlers-addrspaces.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -LowerExcHandlers -print-before-all -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LowerExcHandlers)' -S %s | FileCheck %s diff --git a/test/llvmpasses/lower-handlers.ll b/test/llvmpasses/lower-handlers.ll index 01bc1ae728f15..c3d51f2e94c30 100644 --- a/test/llvmpasses/lower-handlers.ll +++ b/test/llvmpasses/lower-handlers.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -LowerExcHandlers -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LowerExcHandlers)' -S %s | FileCheck %s diff --git a/test/llvmpasses/muladd.ll b/test/llvmpasses/muladd.ll index 2eddb62cef3ec..f93940db392af 100644 --- a/test/llvmpasses/muladd.ll +++ b/test/llvmpasses/muladd.ll @@ -1,7 +1,10 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -CombineMulAdd -S %s | FileCheck %s ; RUN: opt -enable-new-pm=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}} @@ -11,6 +14,7 @@ top: ret double %v2 } +; CHECK-LABEL: @fast_mulsub1 define double @fast_mulsub1(double %a, double %b, double %c) { top: ; CHECK: {{contract|fmuladd}} @@ -20,6 +24,7 @@ top: 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}} @@ -28,3 +33,31 @@ top: ; 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/multiversioning-annotate-only.ll b/test/llvmpasses/multiversioning-annotate-only.ll index 38af146c078f5..ababb4fc74b8a 100644 --- a/test/llvmpasses/multiversioning-annotate-only.ll +++ b/test/llvmpasses/multiversioning-annotate-only.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -JuliaMultiVersioning -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='JuliaMultiVersioning' -S %s | FileCheck %s diff --git a/test/llvmpasses/multiversioning-clone-only.ll b/test/llvmpasses/multiversioning-clone-only.ll index a5c327548d702..897652700c335 100644 --- a/test/llvmpasses/multiversioning-clone-only.ll +++ b/test/llvmpasses/multiversioning-clone-only.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -JuliaMultiVersioning -S %s | FileCheck %s --allow-unused-prefixes=false ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='JuliaMultiVersioning' -S %s | FileCheck %s --allow-unused-prefixes=false diff --git a/test/llvmpasses/pipeline-o0.jl b/test/llvmpasses/pipeline-o0.jl index ff9cd0aace704..1b5d1df3c9f36 100644 --- a/test/llvmpasses/pipeline-o0.jl +++ b/test/llvmpasses/pipeline-o0.jl @@ -1,3 +1,5 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + # RUN: julia --startup-file=no -O0 --check-bounds=yes %s %t -O && llvm-link -S %t/* | FileCheck %s # RUN: julia --startup-file=no -O1 --check-bounds=yes %s %t -O && llvm-link -S %t/* | FileCheck %s # RUN: julia --startup-file=no -O2 --check-bounds=yes %s %t -O && llvm-link -S %t/* | FileCheck %s diff --git a/test/llvmpasses/pipeline-o2-allocs.jl b/test/llvmpasses/pipeline-o2-allocs.jl index e7be976919344..86e1ded3f11e5 100644 --- a/test/llvmpasses/pipeline-o2-allocs.jl +++ b/test/llvmpasses/pipeline-o2-allocs.jl @@ -1,8 +1,12 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + # RUN: julia --startup-file=no -O2 --check-bounds=yes %s %t -O && llvm-link -S %t/* | FileCheck %s # RUN: julia --startup-file=no -O3 --check-bounds=yes %s %t -O && llvm-link -S %t/* | FileCheck %s include(joinpath("..", "testhelpers", "llvmpasses.jl")) +# COM: This tests that simplifycfg is still hoisting allocations in different basic blocks +# COM: into the parent basic block, and deduplicating them in the process # CHECK-LABEL: @julia_split # CHECK: alloc # CHECK-NOT: alloc @@ -15,6 +19,8 @@ function split(maybe) end end +# COM: This tests that irrespective of the condition outside the loop +# COM: allocations inside the loop are hoisted and the loop is deleted # CHECK-LABEL: @julia_loop_alloc # CHECK: phi # CHECK-NOT: phi @@ -27,6 +33,8 @@ function loop_alloc(N) ref end +# COM: This tests that even with the allocation LLVM will recognize +# COM: that the loop is meaningless and delete it # CHECK-LABEL: @julia_loop_const # CHECK-NOT: br function loop_const() @@ -37,6 +45,8 @@ function loop_const() ref end +# COM: This tests that the GC.@preserve macro is being ignored since ref +# COM: is not used anywhere else # CHECK-LABEL: @julia_nopreserve # CHECK-NOT: alloc # CHECK-NOT: julia.gc_preserve_begin diff --git a/test/llvmpasses/pipeline-o2-broadcast.jl b/test/llvmpasses/pipeline-o2-broadcast.jl index dc44293379284..47c2a989737e7 100644 --- a/test/llvmpasses/pipeline-o2-broadcast.jl +++ b/test/llvmpasses/pipeline-o2-broadcast.jl @@ -1,3 +1,5 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + # RUN: julia --startup-file=no -O2 --check-bounds=auto %s %t -O && llvm-link -S %t/* | FileCheck %s # RUN: julia --startup-file=no -O3 --check-bounds=auto %s %t -O && llvm-link -S %t/* | FileCheck %s diff --git a/test/llvmpasses/pipeline-o2.jl b/test/llvmpasses/pipeline-o2.jl index 85f5035a3249d..2996a44de62b3 100644 --- a/test/llvmpasses/pipeline-o2.jl +++ b/test/llvmpasses/pipeline-o2.jl @@ -1,3 +1,5 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + # RUN: julia --startup-file=no -O2 --check-bounds=yes %s %t -O && llvm-link -S %t/* | FileCheck %s --check-prefixes=ALL # RUN: julia --startup-file=no -O3 --check-bounds=yes %s %t -O && llvm-link -S %t/* | FileCheck %s --check-prefixes=ALL diff --git a/test/llvmpasses/propagate-addrspace-non-zero.ll b/test/llvmpasses/propagate-addrspace-non-zero.ll index b896850935e37..c1ba2069102ac 100644 --- a/test/llvmpasses/propagate-addrspace-non-zero.ll +++ b/test/llvmpasses/propagate-addrspace-non-zero.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -PropagateJuliaAddrspaces -dce -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='PropagateJuliaAddrspaces,dce' -S %s | FileCheck %s diff --git a/test/llvmpasses/propagate-addrspace.ll b/test/llvmpasses/propagate-addrspace.ll index 84ad33310ab3f..92bf68578477f 100644 --- a/test/llvmpasses/propagate-addrspace.ll +++ b/test/llvmpasses/propagate-addrspace.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -PropagateJuliaAddrspaces -dce -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='PropagateJuliaAddrspaces,dce' -S %s | FileCheck %s diff --git a/test/llvmpasses/refinements.ll b/test/llvmpasses/refinements.ll index 6c92bab06e357..3600fb76804ab 100644 --- a/test/llvmpasses/refinements.ll +++ b/test/llvmpasses/refinements.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -LateLowerGCFrame -FinalLowerGC -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame),FinalLowerGC' -S %s | FileCheck %s diff --git a/test/llvmpasses/remove-addrspaces.ll b/test/llvmpasses/remove-addrspaces.ll index a748b3843bacb..4710f9bd6c4d6 100644 --- a/test/llvmpasses/remove-addrspaces.ll +++ b/test/llvmpasses/remove-addrspaces.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -RemoveJuliaAddrspaces -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='RemoveJuliaAddrspaces' -S %s | FileCheck %s diff --git a/test/llvmpasses/returnstwicegc.ll b/test/llvmpasses/returnstwicegc.ll index 17791d630d61a..404330ac3f7e1 100644 --- a/test/llvmpasses/returnstwicegc.ll +++ b/test/llvmpasses/returnstwicegc.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -LateLowerGCFrame -FinalLowerGC -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame),FinalLowerGC' -S %s | FileCheck %s diff --git a/test/llvmpasses/simdloop.ll b/test/llvmpasses/simdloop.ll index 894d3a1428a5c..142250212984e 100644 --- a/test/llvmpasses/simdloop.ll +++ b/test/llvmpasses/simdloop.ll @@ -1,3 +1,5 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + ; RUN: opt -enable-new-pm=0 -load libjulia-codegen%shlibext -LowerSIMDLoop -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='LowerSIMDLoop' -S %s | FileCheck %s From 04190546bc4d79cb3c311bdc25b9e63da90dde57 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 2 May 2023 15:00:51 -0400 Subject: [PATCH 629/775] Allow toplevel-ish MethodInstances in va_process_argtypes (#49592) Usually toplevel methods don't go this way, but we'd like to broaden the use of "toplevel" MethodInstances slightly to other random chunks of IR we'd like to infer (as discussed in #49384), so give something sensible here if we do come this way. --- base/compiler/inferenceresult.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/base/compiler/inferenceresult.jl b/base/compiler/inferenceresult.jl index 68fe2d9f02038..3a96b21d7c40a 100644 --- a/base/compiler/inferenceresult.jl +++ b/base/compiler/inferenceresult.jl @@ -78,12 +78,12 @@ function is_argtype_match(𝕃::AbstractLattice, return !overridden_by_const end -va_process_argtypes(𝕃::AbstractLattice, given_argtypes::Vector{Any}, linfo::MethodInstance) = - va_process_argtypes(Returns(nothing), 𝕃, given_argtypes, linfo) -function va_process_argtypes(@nospecialize(va_handler!), 𝕃::AbstractLattice, given_argtypes::Vector{Any}, linfo::MethodInstance) - def = linfo.def::Method - isva = def.isva - nargs = Int(def.nargs) +va_process_argtypes(𝕃::AbstractLattice, given_argtypes::Vector{Any}, mi::MethodInstance) = + va_process_argtypes(Returns(nothing), 𝕃, given_argtypes, mi) +function va_process_argtypes(@nospecialize(va_handler!), 𝕃::AbstractLattice, given_argtypes::Vector{Any}, mi::MethodInstance) + def = mi.def + isva = isa(def, Method) ? def.isva : false + nargs = isa(def, Method) ? Int(def.nargs) : length(mi.specTypes.parameters) if isva || isvarargtype(given_argtypes[end]) isva_given_argtypes = Vector{Any}(undef, nargs) for i = 1:(nargs-isva) @@ -100,7 +100,7 @@ function va_process_argtypes(@nospecialize(va_handler!), 𝕃::AbstractLattice, end return isva_given_argtypes end - @assert length(given_argtypes) == nargs "invalid `given_argtypes` for `linfo`" + @assert length(given_argtypes) == nargs "invalid `given_argtypes` for `mi`" return given_argtypes end From e9d678e7e02a48263996f7b7a367b7435af2d504 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 2 May 2023 16:25:53 -0400 Subject: [PATCH 630/775] fix #48870, normalize U+210F to U+0127 (#49559) --- NEWS.md | 1 + src/flisp/julia_charmap.h | 1 + test/syntax.jl | 2 ++ 3 files changed, 4 insertions(+) diff --git a/NEWS.md b/NEWS.md index c87f45265ea86..eca0564770ab1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -14,6 +14,7 @@ Language changes result previously. This also lets the runtime optimize certain method lookups in a way that significantly improves load and inference times for heavily overloaded methods that dispatch on Types (such as traits and constructors). +* The "h bar" `ℏ` (`\hslash` U+210F) character is now treated as equivalent to `ħ` (`\hbar` U+0127). Compiler/Runtime improvements ----------------------------- diff --git a/src/flisp/julia_charmap.h b/src/flisp/julia_charmap.h index 3c54eaf98f484..8471d1e3b3b91 100644 --- a/src/flisp/julia_charmap.h +++ b/src/flisp/julia_charmap.h @@ -10,4 +10,5 @@ static const uint32_t charmap[][2] = { { 0x00B7, 0x22C5 }, // middot char -> dot operator (#25098) { 0x0387, 0x22C5 }, // Greek interpunct -> dot operator (#25098) { 0x2212, 0x002D }, // minus -> hyphen-minus (#26193) + { 0x210F, 0x0127 }, // hbar -> small letter h with stroke (#48870) }; diff --git a/test/syntax.jl b/test/syntax.jl index fac3479315c03..7778e1ffd60b1 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -839,6 +839,8 @@ let ε=1, μ=2, x=3, î=4, ⋅=5, (-)=6 @test Meta.parse("100.0f\u22122") === Meta.parse("100.0f-2") @test Meta.parse("0x100p\u22128") === Meta.parse("0x100P\u22128") === Meta.parse("0x100p-8") @test (−) == (-) == 6 + # hbar ℏ to ħ - (#48870) + @test :ℏ === :ħ end # issue #8925 From 404bb1f84960a17141a8fc554b06769b872f39d4 Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Wed, 3 May 2023 04:58:02 +0000 Subject: [PATCH 631/775] Mark up optimization passes with remarks and debug prints (#49588) Add pass remarks and LLVM_DEBUG statements to alloc-opt, muladd, julia-licm, and simdloop passes --- src/llvm-alloc-helpers.cpp | 113 ++++++++++++++++++++++++++++--------- src/llvm-alloc-helpers.h | 8 +++ src/llvm-alloc-opt.cpp | 73 ++++++++++++++++++++---- src/llvm-julia-licm.cpp | 105 +++++++++++++++++++++++++++------- src/llvm-muladd.cpp | 29 ++++++++-- src/llvm-simdloop.cpp | 36 +++++++++++- 6 files changed, 298 insertions(+), 66 deletions(-) diff --git a/src/llvm-alloc-helpers.cpp b/src/llvm-alloc-helpers.cpp index 6ba0c73c14785..d24c08b4b4930 100644 --- a/src/llvm-alloc-helpers.cpp +++ b/src/llvm-alloc-helpers.cpp @@ -7,6 +7,8 @@ #include +#define DEBUG_TYPE "escape-analysis" + using namespace llvm; using namespace jl_alloc; @@ -110,40 +112,58 @@ bool AllocUseInfo::addMemOp(Instruction *inst, unsigned opno, uint32_t offset, return true; } -JL_USED_FUNC void AllocUseInfo::dump() +JL_USED_FUNC void AllocUseInfo::dump(llvm::raw_ostream &OS) { - jl_safe_printf("escaped: %d\n", escaped); - jl_safe_printf("addrescaped: %d\n", addrescaped); - jl_safe_printf("returned: %d\n", returned); - jl_safe_printf("haserror: %d\n", haserror); - jl_safe_printf("hasload: %d\n", hasload); - jl_safe_printf("haspreserve: %d\n", haspreserve); - jl_safe_printf("hasunknownmem: %d\n", hasunknownmem); - jl_safe_printf("hastypeof: %d\n", hastypeof); - jl_safe_printf("refload: %d\n", refload); - jl_safe_printf("refstore: %d\n", refstore); - jl_safe_printf("Uses: %d\n", (unsigned)uses.size()); + OS << "AllocUseInfo:\n"; + OS << "escaped: " << escaped << '\n'; + OS << "addrescaped: " << addrescaped << '\n'; + OS << "returned: " << returned << '\n'; + OS << "haserror: " << haserror << '\n'; + OS << "hasload: " << hasload << '\n'; + OS << "haspreserve: " << haspreserve << '\n'; + OS << "hasunknownmem: " << hasunknownmem << '\n'; + OS << "hastypeof: " << hastypeof << '\n'; + OS << "refload: " << refload << '\n'; + OS << "refstore: " << refstore << '\n'; + OS << "Uses: " << uses.size() << '\n'; for (auto inst: uses) - llvm_dump(inst); + inst->print(OS); if (!preserves.empty()) { - jl_safe_printf("Preserves: %d\n", (unsigned)preserves.size()); - for (auto inst: preserves) { - llvm_dump(inst); - } + OS << "Preserves: " << preserves.size() << '\n'; + for (auto inst: preserves) + inst->print(OS); } - if (!memops.empty()) { - jl_safe_printf("Memops: %d\n", (unsigned)memops.size()); - for (auto &field: memops) { - jl_safe_printf(" Field %d @ %d\n", field.second.size, field.first); - jl_safe_printf(" Accesses:\n"); - for (auto memop: field.second.accesses) { - jl_safe_printf(" "); - llvm_dump(memop.inst); - } + OS << "MemOps: " << memops.size() << '\n'; + for (auto &field: memops) { + OS << " offset: " << field.first << '\n'; + OS << " size: " << field.second.size << '\n'; + OS << " hasobjref: " << field.second.hasobjref << '\n'; + OS << " hasload: " << field.second.hasload << '\n'; + OS << " hasaggr: " << field.second.hasaggr << '\n'; + OS << " accesses: " << field.second.accesses.size() << '\n'; + for (auto &memop: field.second.accesses) { + OS << " "; + memop.inst->print(OS); + OS << '\n'; + OS << " " << (memop.isaggr ? "aggr" : "scalar") << '\n'; + OS << " " << (memop.isobjref ? "objref" : "bits") << '\n'; + OS << " " << memop.offset << '\n'; + OS << " " << memop.size << '\n'; } } } +JL_USED_FUNC void AllocUseInfo::dump() +{ + dump(dbgs()); +} + +#ifndef __clang_gcanalyzer__ +#define REMARK(remark) if (options.ORE) options.ORE->emit(remark) +#else +#define REMARK(remark) +#endif + void jl_alloc::runEscapeAnalysis(llvm::Instruction *I, EscapeAnalysisRequiredArgs required, EscapeAnalysisOptionalArgs options) { required.use_info.reset(); if (I->use_empty()) @@ -161,9 +181,11 @@ void jl_alloc::runEscapeAnalysis(llvm::Instruction *I, EscapeAnalysisRequiredArg }; auto check_inst = [&] (Instruction *inst, Use *use) { + LLVM_DEBUG(dbgs() << "Checking: " << *inst << "\n"); if (isa(inst)) { required.use_info.hasload = true; if (cur.offset == UINT32_MAX) { + LLVM_DEBUG(dbgs() << "Load inst has unknown offset\n"); auto elty = inst->getType(); required.use_info.has_unknown_objref |= hasObjref(elty); required.use_info.has_unknown_objrefaggr |= hasObjref(elty) && !isa(elty); @@ -186,13 +208,16 @@ void jl_alloc::runEscapeAnalysis(llvm::Instruction *I, EscapeAnalysisRequiredArg !isa(call->getArgOperand(2)) || !isa(call->getArgOperand(1)) || (cast(call->getArgOperand(2))->getLimitedValue() >= - UINT32_MAX - cur.offset)) + UINT32_MAX - cur.offset)) { + LLVM_DEBUG(dbgs() << "Memset inst has unknown offset\n"); required.use_info.hasunknownmem = true; + } return true; } if (id == Intrinsic::lifetime_start || id == Intrinsic::lifetime_end || isa(II)) return true; + LLVM_DEBUG(dbgs() << "Unknown intrinsic, marking addrescape\n"); required.use_info.addrescaped = true; return true; } @@ -220,23 +245,38 @@ void jl_alloc::runEscapeAnalysis(llvm::Instruction *I, EscapeAnalysisRequiredArg if (!call->isBundleOperand(opno) || call->getOperandBundleForOperand(opno).getTagName() != "jl_roots") { if (isa(call->getParent()->getTerminator())) { + LLVM_DEBUG(dbgs() << "Detected use of allocation in block terminating with unreachable, likely error function\n"); required.use_info.haserror = true; return true; } + LLVM_DEBUG(dbgs() << "Unknown call, marking escape\n"); + REMARK([&]() { + return OptimizationRemarkMissed(DEBUG_TYPE, "UnknownCall", + inst) + << "Unknown call, marking escape (" << ore::NV("Call", inst) << ")"; + }); required.use_info.escaped = true; return false; } + LLVM_DEBUG(dbgs() << "Call is in jl_roots bundle, marking haspreserve\n"); required.use_info.haspreserve = true; return true; } if (auto store = dyn_cast(inst)) { // Only store value count if (use->getOperandNo() != StoreInst::getPointerOperandIndex()) { + LLVM_DEBUG(dbgs() << "Object address is stored somewhere, marking escape\n"); + REMARK([&]() { + return OptimizationRemarkMissed(DEBUG_TYPE, "StoreObjAddr", + inst) + << "Object address is stored somewhere, marking escape (" << ore::NV("Store", inst) << ")"; + }); required.use_info.escaped = true; return false; } auto storev = store->getValueOperand(); if (cur.offset == UINT32_MAX) { + LLVM_DEBUG(dbgs() << "Store inst has unknown offset\n"); auto elty = storev->getType(); required.use_info.has_unknown_objref |= hasObjref(elty); required.use_info.has_unknown_objrefaggr |= hasObjref(elty) && !isa(elty); @@ -250,6 +290,12 @@ void jl_alloc::runEscapeAnalysis(llvm::Instruction *I, EscapeAnalysisRequiredArg if (isa(inst) || isa(inst)) { // Only store value count if (use->getOperandNo() != isa(inst) ? AtomicCmpXchgInst::getPointerOperandIndex() : AtomicRMWInst::getPointerOperandIndex()) { + LLVM_DEBUG(dbgs() << "Object address is cmpxchg/rmw-ed somewhere, marking escape\n"); + REMARK([&]() { + return OptimizationRemarkMissed(DEBUG_TYPE, "StoreObjAddr", + inst) + << "Object address is cmpxchg/rmw-ed somewhere, marking escape (" << ore::NV("Store", inst) << ")"; + }); required.use_info.escaped = true; return false; } @@ -257,8 +303,10 @@ void jl_alloc::runEscapeAnalysis(llvm::Instruction *I, EscapeAnalysisRequiredArg auto storev = isa(inst) ? cast(inst)->getNewValOperand() : cast(inst)->getValOperand(); if (cur.offset == UINT32_MAX || !required.use_info.addMemOp(inst, use->getOperandNo(), cur.offset, storev->getType(), - true, required.DL)) + true, required.DL)) { + LLVM_DEBUG(dbgs() << "Atomic inst has unknown offset\n"); required.use_info.hasunknownmem = true; + } required.use_info.refload = true; return true; } @@ -272,10 +320,12 @@ void jl_alloc::runEscapeAnalysis(llvm::Instruction *I, EscapeAnalysisRequiredArg APInt apoffset(sizeof(void*) * 8, cur.offset, true); if (!gep->accumulateConstantOffset(required.DL, apoffset) || apoffset.isNegative()) { next_offset = UINT32_MAX; + LLVM_DEBUG(dbgs() << "GEP inst has unknown offset\n"); } else { next_offset = apoffset.getLimitedValue(); if (next_offset > UINT32_MAX) { + LLVM_DEBUG(dbgs() << "GEP inst exceeeds 32-bit offset\n"); next_offset = UINT32_MAX; } } @@ -285,9 +335,16 @@ void jl_alloc::runEscapeAnalysis(llvm::Instruction *I, EscapeAnalysisRequiredArg return true; } if (isa(inst)) { + LLVM_DEBUG(dbgs() << "Allocation is returned\n"); required.use_info.returned = true; return true; } + LLVM_DEBUG(dbgs() << "Unknown instruction, marking escape\n"); + REMARK([&]() { + return OptimizationRemarkMissed(DEBUG_TYPE, "UnknownInst", + inst) + << "Unknown instruction, marking escape (" << ore::NV("Inst", inst) << ")"; + }); required.use_info.escaped = true; return false; }; diff --git a/src/llvm-alloc-helpers.h b/src/llvm-alloc-helpers.h index 38a0b2ba181ce..3bd80704a0888 100644 --- a/src/llvm-alloc-helpers.h +++ b/src/llvm-alloc-helpers.h @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -110,6 +111,7 @@ namespace jl_alloc { preserves.clear(); memops.clear(); } + void dump(llvm::raw_ostream &OS); void dump(); bool addMemOp(llvm::Instruction *inst, unsigned opno, uint32_t offset, llvm::Type *elty, bool isstore, const llvm::DataLayout &DL); @@ -136,6 +138,7 @@ namespace jl_alloc { //will not be considered. Defaults to nullptr, which means all uses of the allocation //are considered const llvm::SmallPtrSetImpl *valid_set; + llvm::OptimizationRemarkEmitter *ORE = nullptr; EscapeAnalysisOptionalArgs() = default; @@ -143,6 +146,11 @@ namespace jl_alloc { this->valid_set = valid_set; return *this; } + + EscapeAnalysisOptionalArgs &with_optimization_remark_emitter(decltype(ORE) ORE) { + this->ORE = ORE; + return *this; + } }; void runEscapeAnalysis(llvm::Instruction *I, EscapeAnalysisRequiredArgs required, EscapeAnalysisOptionalArgs options=EscapeAnalysisOptionalArgs()); diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index b044e2351f512..1a524cbe8d419 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,7 @@ #include #include -#define DEBUG_TYPE "alloc_opt" +#define DEBUG_TYPE "alloc-opt" #include "julia_assert.h" using namespace llvm; @@ -98,6 +99,11 @@ static void removeGCPreserve(CallInst *call, Instruction *val) * * Handle jl_box* */ +#ifndef __clang_gcanalyzer__ +#define REMARK(remark) ORE.emit(remark) +#else +#define REMARK(remark) (void) 0; +#endif struct AllocOpt : public JuliaPassContext { const DataLayout *DL; @@ -112,6 +118,7 @@ struct AllocOpt : public JuliaPassContext { struct Optimizer { Optimizer(Function &F, AllocOpt &pass, function_ref GetDT) : F(F), + ORE(&F), pass(pass), GetDT(std::move(GetDT)) {} @@ -139,6 +146,7 @@ struct Optimizer { void optimizeTag(CallInst *orig_inst); Function &F; + OptimizationRemarkEmitter ORE; AllocOpt &pass; DominatorTree *_DT = nullptr; function_ref GetDT; @@ -215,17 +223,29 @@ void Optimizer::optimizeAll() size_t sz = item.second; checkInst(orig); if (use_info.escaped) { + REMARK([&]() { + return OptimizationRemarkMissed(DEBUG_TYPE, "Escaped", orig) + << "GC allocation escaped " << ore::NV("GC Allocation", orig); + }); if (use_info.hastypeof) optimizeTag(orig); continue; } if (use_info.haserror || use_info.returned) { + REMARK([&]() { + return OptimizationRemarkMissed(DEBUG_TYPE, "Escaped", orig) + << "GC allocation has error or was returned " << ore::NV("GC Allocation", orig); + }); if (use_info.hastypeof) optimizeTag(orig); continue; } if (!use_info.addrescaped && !use_info.hasload && (!use_info.haspreserve || !use_info.refstore)) { + REMARK([&]() { + return OptimizationRemark(DEBUG_TYPE, "Dead Allocation", orig) + << "GC allocation removed " << ore::NV("GC Allocation", orig); + }); // No one took the address, no one reads anything and there's no meaningful // preserve of fields (either no preserve/ccall or no object reference fields) // We can just delete all the uses. @@ -246,16 +266,28 @@ void Optimizer::optimizeAll() } } } - if (!use_info.hasunknownmem && !use_info.addrescaped && !has_refaggr) { - // No one actually care about the memory layout of this object, split it. - splitOnStack(orig); - continue; - } if (has_refaggr) { + REMARK([&]() { + return OptimizationRemarkMissed(DEBUG_TYPE, "Escaped", orig) + << "GC allocation has unusual object reference, unable to move to stack " << ore::NV("GC Allocation", orig); + }); if (use_info.hastypeof) optimizeTag(orig); continue; } + if (!use_info.hasunknownmem && !use_info.addrescaped) { + REMARK([&](){ + return OptimizationRemark(DEBUG_TYPE, "Stack Split Allocation", orig) + << "GC allocation split on stack " << ore::NV("GC Allocation", orig); + }); + // No one actually care about the memory layout of this object, split it. + splitOnStack(orig); + continue; + } + REMARK([&](){ + return OptimizationRemark(DEBUG_TYPE, "Stack Move Allocation", orig) + << "GC allocation moved to stack " << ore::NV("GC Allocation", orig); + }); // The object has no fields with mix reference access moveToStack(orig, sz, has_ref); } @@ -324,8 +356,15 @@ ssize_t Optimizer::getGCAllocSize(Instruction *I) void Optimizer::checkInst(Instruction *I) { + LLVM_DEBUG(dbgs() << "Running escape analysis on " << *I << "\n"); jl_alloc::EscapeAnalysisRequiredArgs required{use_info, check_stack, pass, *pass.DL}; - jl_alloc::runEscapeAnalysis(I, required); + jl_alloc::runEscapeAnalysis(I, required, jl_alloc::EscapeAnalysisOptionalArgs().with_optimization_remark_emitter(&ORE)); + REMARK([&](){ + std::string suse_info; + llvm::raw_string_ostream osuse_info(suse_info); + use_info.dump(osuse_info); + return OptimizationRemarkAnalysis(DEBUG_TYPE, "EscapeAnalysis", I) << "escape analysis for " << ore::NV("GC Allocation", I) << "\n" << ore::NV("UseInfo", osuse_info.str()); + }); } void Optimizer::insertLifetimeEnd(Value *ptr, Constant *sz, Instruction *insert) @@ -615,8 +654,10 @@ void Optimizer::moveToStack(CallInst *orig_inst, size_t sz, bool has_ref) } return false; }; - if (simple_replace(orig_inst, new_inst)) + if (simple_replace(orig_inst, new_inst)) { + LLVM_DEBUG(dbgs() << "Simple replace of allocation was successful in stack move\n"); return; + } assert(replace_stack.empty()); ReplaceUses::Frame cur{orig_inst, new_inst}; auto finish_cur = [&] () { @@ -731,8 +772,10 @@ void Optimizer::removeAlloc(CallInst *orig_inst) } return false; }; - if (simple_remove(orig_inst)) + if (simple_remove(orig_inst)) { + LLVM_DEBUG(dbgs() << "Simple remove of allocation was successful in removeAlloc\n"); return; + } assert(replace_stack.empty()); ReplaceUses::Frame cur{orig_inst, nullptr}; auto finish_cur = [&] () { @@ -818,6 +861,10 @@ void Optimizer::optimizeTag(CallInst *orig_inst) auto callee = call->getCalledOperand(); if (pass.typeof_func == callee) { ++RemovedTypeofs; + REMARK([&](){ + return OptimizationRemark(DEBUG_TYPE, "typeof", call) + << "removed typeof call for GC allocation " << ore::NV("Alloc", orig_inst); + }); call->replaceAllUsesWith(tag); // Push to the removed instructions to trigger `finalize` to // return the correct result. @@ -894,8 +941,10 @@ void Optimizer::splitOnStack(CallInst *orig_inst) } return false; }; - if (simple_replace(orig_inst)) + if (simple_replace(orig_inst)) { + LLVM_DEBUG(dbgs() << "Simple replace of allocation was successful in stack split\n"); return; + } assert(replace_stack.empty()); ReplaceUses::Frame cur{orig_inst, uint32_t(0)}; auto finish_cur = [&] () { @@ -1175,8 +1224,10 @@ bool AllocOpt::doInitialization(Module &M) bool AllocOpt::runOnFunction(Function &F, function_ref GetDT) { - if (!alloc_obj_func) + if (!alloc_obj_func) { + LLVM_DEBUG(dbgs() << "AllocOpt: no alloc_obj function found, skipping pass\n"); return false; + } Optimizer optimizer(F, *this, std::move(GetDT)); optimizer.initialize(); optimizer.optimizeAll(); diff --git a/src/llvm-julia-licm.cpp b/src/llvm-julia-licm.cpp index 553d091ef4c6f..84d5d614a7293 100644 --- a/src/llvm-julia-licm.cpp +++ b/src/llvm-julia-licm.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,12 @@ STATISTIC(HoistedAllocation, "Number of allocations hoisted out of a loop"); * loop context as well but it is inside a loop where they matter the most. */ +#ifndef __clang_gcanalyzer__ +#define REMARK(remark) ORE.emit(remark) +#else +#define REMARK(remark) (void) 0; +#endif + namespace { //Stolen and modified from LICM.cpp @@ -142,7 +149,7 @@ struct JuliaLICM : public JuliaPassContext { GetMSSA(GetMSSA), GetSE(GetSE) {} - bool runOnLoop(Loop *L) + bool runOnLoop(Loop *L, OptimizationRemarkEmitter &ORE) { // Get the preheader block to move instructions into, // required to run this pass. @@ -157,8 +164,10 @@ struct JuliaLICM : public JuliaPassContext { // `gc_preserve_end_func` must be from `gc_preserve_begin_func`. // We also hoist write barriers here, so we don't exit if write_barrier_func exists if (!gc_preserve_begin_func && !write_barrier_func && - !alloc_obj_func) + !alloc_obj_func) { + LLVM_DEBUG(dbgs() << "No gc_preserve_begin_func or write_barrier_func or alloc_obj_func found, skipping JuliaLICM\n"); return false; + } auto LI = &GetLI(); auto DT = &GetDT(); auto MSSA = GetMSSA(); @@ -214,6 +223,11 @@ struct JuliaLICM : public JuliaPassContext { continue; ++HoistedPreserveBegin; moveInstructionBefore(*call, *preheader->getTerminator(), MSSAU, SE); + LLVM_DEBUG(dbgs() << "Hoisted gc_preserve_begin: " << *call << "\n"); + REMARK([&](){ + return OptimizationRemark(DEBUG_TYPE, "Hoisted", call) + << "hoisting preserve begin " << ore::NV("PreserveBegin", call); + }); changed = true; } else if (callee == gc_preserve_end_func) { @@ -229,10 +243,20 @@ struct JuliaLICM : public JuliaPassContext { } ++SunkPreserveEnd; moveInstructionBefore(*call, *exit_pts[0], MSSAU, SE); + LLVM_DEBUG(dbgs() << "Sunk gc_preserve_end: " << *call << "\n"); + REMARK([&](){ + return OptimizationRemark(DEBUG_TYPE, "Sunk", call) + << "sinking preserve end " << ore::NV("PreserveEnd", call); + }); for (unsigned i = 1; i < exit_pts.size(); i++) { // Clone exit auto CI = CallInst::Create(call, {}, exit_pts[i]); createNewInstruction(CI, call, MSSAU); + LLVM_DEBUG(dbgs() << "Cloned and sunk gc_preserve_end: " << *CI << "\n"); + REMARK([&](){ + return OptimizationRemark(DEBUG_TYPE, "Sunk", call) + << "cloning and sinking preserve end" << ore::NV("PreserveEnd", call); + }); } } else if (callee == write_barrier_func) { @@ -242,41 +266,80 @@ struct JuliaLICM : public JuliaPassContext { changed, preheader->getTerminator(), MSSAU, SE)) { valid = false; + LLVM_DEBUG(dbgs() << "Failed to hoist write barrier argument: " << *call->getArgOperand(i) << "\n"); break; } } - if (valid) { - ++HoistedWriteBarrier; - moveInstructionBefore(*call, *preheader->getTerminator(), MSSAU, SE); - changed = true; + if (!valid) { + LLVM_DEBUG(dbgs() << "Failed to hoist write barrier: " << *call << "\n"); + continue; } + ++HoistedWriteBarrier; + moveInstructionBefore(*call, *preheader->getTerminator(), MSSAU, SE); + changed = true; + REMARK([&](){ + return OptimizationRemark(DEBUG_TYPE, "Hoist", call) + << "hoisting write barrier " << ore::NV("GC Write Barrier", call); + }); } else if (callee == alloc_obj_func) { - jl_alloc::AllocUseInfo use_info; - jl_alloc::CheckInst::Stack check_stack; - jl_alloc::EscapeAnalysisRequiredArgs required{use_info, check_stack, *this, DL}; - jl_alloc::runEscapeAnalysis(call, required, jl_alloc::EscapeAnalysisOptionalArgs().with_valid_set(&L->getBlocksSet())); - if (use_info.escaped || use_info.addrescaped) { - continue; - } bool valid = true; for (std::size_t i = 0; i < call->arg_size(); i++) { if (!makeLoopInvariant(L, call->getArgOperand(i), changed, preheader->getTerminator(), MSSAU, SE)) { valid = false; + LLVM_DEBUG(dbgs() << "Failed to hoist alloc_obj argument: " << *call->getArgOperand(i) << "\n"); break; } } + if (!valid) { + LLVM_DEBUG(dbgs() << "Failed to hoist alloc_obj: " << *call << "\n"); + continue; + } + LLVM_DEBUG(dbgs() << "Running escape analysis for " << *call << "\n"); + jl_alloc::AllocUseInfo use_info; + jl_alloc::CheckInst::Stack check_stack; + jl_alloc::EscapeAnalysisRequiredArgs required{use_info, check_stack, *this, DL}; + jl_alloc::runEscapeAnalysis(call, required, jl_alloc::EscapeAnalysisOptionalArgs().with_valid_set(&L->getBlocksSet()).with_optimization_remark_emitter(&ORE)); + REMARK([&](){ + std::string suse_info; + llvm::raw_string_ostream osuse_info(suse_info); + use_info.dump(osuse_info); + return OptimizationRemarkAnalysis(DEBUG_TYPE, "EscapeAnalysis", call) << "escape analysis for " << ore::NV("GC Allocation", call) << "\n" << ore::NV("UseInfo", osuse_info.str()); + }); + if (use_info.escaped) { + REMARK([&](){ + return OptimizationRemarkMissed(DEBUG_TYPE, "Escape", call) + << "not hoisting gc allocation " << ore::NV("GC Allocation", call) + << " because it may escape"; + }); + continue; + } + if (use_info.addrescaped) { + REMARK([&](){ + return OptimizationRemarkMissed(DEBUG_TYPE, "Escape", call) + << "not hoisting gc allocation " << ore::NV("GC Allocation", call) + << " because its address may escape"; + }); + continue; + } if (use_info.refstore) { // We need to add write barriers to any stores // that may start crossing generations + REMARK([&](){ + return OptimizationRemarkMissed(DEBUG_TYPE, "Escape", call) + << "not hoisting gc allocation " << ore::NV("GC Allocation", call) + << " because it may have an object stored to it"; + }); continue; } - if (valid) { - ++HoistedAllocation; - moveInstructionBefore(*call, *preheader->getTerminator(), MSSAU, SE); - changed = true; - } + REMARK([&](){ + return OptimizationRemark(DEBUG_TYPE, "Hoist", call) + << "hoisting gc allocation " << ore::NV("GC Allocation", call); + }); + ++HoistedAllocation; + moveInstructionBefore(*call, *preheader->getTerminator(), MSSAU, SE); + changed = true; } } } @@ -291,6 +354,7 @@ struct JuliaLICM : public JuliaPassContext { }; bool JuliaLICMPassLegacy::runOnLoop(Loop *L, LPPassManager &LPM) { + OptimizationRemarkEmitter ORE(L->getHeader()->getParent()); auto GetDT = [this]() -> DominatorTree & { return getAnalysis().getDomTree(); }; @@ -304,7 +368,7 @@ bool JuliaLICMPassLegacy::runOnLoop(Loop *L, LPPassManager &LPM) { return nullptr; }; auto juliaLICM = JuliaLICM(GetDT, GetLI, GetMSSA, GetSE); - return juliaLICM.runOnLoop(L); + return juliaLICM.runOnLoop(L, ORE); } char JuliaLICMPassLegacy::ID = 0; @@ -316,6 +380,7 @@ static RegisterPass PreservedAnalyses JuliaLICMPass::run(Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &U) { + OptimizationRemarkEmitter ORE(L.getHeader()->getParent()); auto GetDT = [&AR]() -> DominatorTree & { return AR.DT; }; @@ -329,7 +394,7 @@ PreservedAnalyses JuliaLICMPass::run(Loop &L, LoopAnalysisManager &AM, return &AR.SE; }; auto juliaLICM = JuliaLICM(GetDT, GetLI, GetMSSA, GetSE); - if (juliaLICM.runOnLoop(&L)) { + if (juliaLICM.runOnLoop(&L, ORE)) { auto preserved = getLoopPassPreservedAnalyses(); preserved.preserveSet(); preserved.preserve(); diff --git a/src/llvm-muladd.cpp b/src/llvm-muladd.cpp index b66ea33e57384..1f45075dd25c8 100644 --- a/src/llvm-muladd.cpp +++ b/src/llvm-muladd.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -23,12 +24,18 @@ #include "julia.h" #include "julia_assert.h" -#define DEBUG_TYPE "combine_muladd" +#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 * ``` @@ -41,16 +48,27 @@ STATISTIC(TotalContracted, "Total number of multiplies marked for FMA"); */ // Return true if we changed the mulOp -static bool checkCombine(Value *maybeMul) JL_NOTSAFEPOINT +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()) + 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); @@ -61,6 +79,7 @@ static bool checkCombine(Value *maybeMul) JL_NOTSAFEPOINT 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();) { @@ -70,13 +89,13 @@ static bool combineMulAdd(Function &F) JL_NOTSAFEPOINT case Instruction::FAdd: { if (!I.isFast()) continue; - modified |= checkCombine(I.getOperand(0)) || checkCombine(I.getOperand(1)); + modified |= checkCombine(I.getOperand(0), ORE) || checkCombine(I.getOperand(1), ORE); break; } case Instruction::FSub: { if (!I.isFast()) continue; - modified |= checkCombine(I.getOperand(0)) || checkCombine(I.getOperand(1)); + modified |= checkCombine(I.getOperand(0), ORE) || checkCombine(I.getOperand(1), ORE); break; } default: diff --git a/src/llvm-simdloop.cpp b/src/llvm-simdloop.cpp index 2f0375e39e1a3..3c94b226ad7b8 100644 --- a/src/llvm-simdloop.cpp +++ b/src/llvm-simdloop.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -42,6 +43,11 @@ STATISTIC(MaxChainLength, "Max length of reduction chain"); STATISTIC(AddChains, "Addition reduction chains"); STATISTIC(MulChains, "Multiply reduction chains"); +#ifndef __clang_gcanalyzer__ +#define REMARK(remark) ORE.emit(remark) +#else +#define REMARK(remark) (void) 0; +#endif namespace { static unsigned getReduceOpcode(Instruction *J, Instruction *operand) JL_NOTSAFEPOINT @@ -67,7 +73,7 @@ static unsigned getReduceOpcode(Instruction *J, Instruction *operand) JL_NOTSAFE /// If Phi is part of a reduction cycle of FAdd, FSub, FMul or FDiv, /// mark the ops as permitting reassociation/commuting. /// As of LLVM 4.0, FDiv is not handled by the loop vectorizer -static void enableUnsafeAlgebraIfReduction(PHINode *Phi, Loop *L) JL_NOTSAFEPOINT +static void enableUnsafeAlgebraIfReduction(PHINode *Phi, Loop *L, OptimizationRemarkEmitter &ORE) JL_NOTSAFEPOINT { typedef SmallVector chainVector; chainVector chain; @@ -81,6 +87,10 @@ static void enableUnsafeAlgebraIfReduction(PHINode *Phi, Loop *L) JL_NOTSAFEPOIN if (L->contains(U)) { if (J) { LLVM_DEBUG(dbgs() << "LSL: not a reduction var because op has two internal uses: " << *I << "\n"); + REMARK([&]() { + return OptimizationRemarkMissed(DEBUG_TYPE, "NotReductionVar", U) + << "not a reduction variable because operation has two internal uses"; + }); return; } J = U; @@ -88,6 +98,10 @@ static void enableUnsafeAlgebraIfReduction(PHINode *Phi, Loop *L) JL_NOTSAFEPOIN } if (!J) { LLVM_DEBUG(dbgs() << "LSL: chain prematurely terminated at " << *I << "\n"); + REMARK([&]() { + return OptimizationRemarkMissed(DEBUG_TYPE, "ChainPrematurelyTerminated", I) + << "chain prematurely terminated at " << ore::NV("Instruction", I); + }); return; } if (J == Phi) { @@ -98,6 +112,10 @@ static void enableUnsafeAlgebraIfReduction(PHINode *Phi, Loop *L) JL_NOTSAFEPOIN // Check that arithmetic op matches prior arithmetic ops in the chain. if (getReduceOpcode(J, I) != opcode) { LLVM_DEBUG(dbgs() << "LSL: chain broke at " << *J << " because of wrong opcode\n"); + REMARK([&](){ + return OptimizationRemarkMissed(DEBUG_TYPE, "ChainBroke", J) + << "chain broke at " << ore::NV("Instruction", J) << " because of wrong opcode"; + }); return; } } @@ -106,6 +124,10 @@ static void enableUnsafeAlgebraIfReduction(PHINode *Phi, Loop *L) JL_NOTSAFEPOIN opcode = getReduceOpcode(J, I); if (!opcode) { LLVM_DEBUG(dbgs() << "LSL: first arithmetic op in chain is uninteresting" << *J << "\n"); + REMARK([&]() { + return OptimizationRemarkMissed(DEBUG_TYPE, "FirstArithmeticOpInChainIsUninteresting", J) + << "first arithmetic op in chain is uninteresting"; + }); return; } } @@ -123,6 +145,10 @@ static void enableUnsafeAlgebraIfReduction(PHINode *Phi, Loop *L) JL_NOTSAFEPOIN int length = 0; for (chainVector::const_iterator K=chain.begin(); K!=chain.end(); ++K) { LLVM_DEBUG(dbgs() << "LSL: marking " << **K << "\n"); + REMARK([&]() { + return OptimizationRemark(DEBUG_TYPE, "MarkedUnsafeAlgebra", *K) + << "marked unsafe algebra on " << ore::NV("Instruction", *K); + }); (*K)->setFast(true); ++length; } @@ -139,6 +165,7 @@ static bool markLoopInfo(Module &M, Function *marker, function_ref(U); ToDelete.push_back(I); + OptimizationRemarkEmitter ORE(I->getParent()->getParent()); LoopInfo &LI = GetLI(*I->getParent()->getParent()); Loop *L = LI.getLoopFor(I->getParent()); I->removeFromParent(); @@ -182,6 +209,11 @@ static bool markLoopInfo(Module &M, Function *marker, function_refgetLoopID(); if (n) { // Loop already has a LoopID so copy over Metadata @@ -220,7 +252,7 @@ static bool markLoopInfo(Module &M, Function *marker, function_refbegin(), E = Lh->end(); I != E; ++I) { if (PHINode *Phi = dyn_cast(I)) - enableUnsafeAlgebraIfReduction(Phi, L); + enableUnsafeAlgebraIfReduction(Phi, L, ORE); else break; } From b66f63c5add314bc345e2a7e1ad775d52ad58065 Mon Sep 17 00:00:00 2001 From: Aravindh Krishnamoorthy Date: Wed, 3 May 2023 09:19:12 +0200 Subject: [PATCH 632/775] Ensure length(ipiv)==n before calling LAPACK.getrs! to avoid segfaults (#49602) --- stdlib/LinearAlgebra/src/lapack.jl | 3 +++ stdlib/LinearAlgebra/test/lapack.jl | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl index c5f820a68a6fc..066a858cacb30 100644 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ b/stdlib/LinearAlgebra/src/lapack.jl @@ -1010,6 +1010,9 @@ for (gels, gesv, getrs, getri, elty) in if n != size(B, 1) throw(DimensionMismatch("B has leading dimension $(size(B,1)), but needs $n")) end + if n != length(ipiv) + throw(DimensionMismatch("ipiv has length $(length(ipiv)), but needs to be $n")) + end nrhs = size(B, 2) info = Ref{BlasInt}() ccall((@blasfunc($getrs), libblastrampoline), Cvoid, diff --git a/stdlib/LinearAlgebra/test/lapack.jl b/stdlib/LinearAlgebra/test/lapack.jl index a164de0e31815..2c5d92541af93 100644 --- a/stdlib/LinearAlgebra/test/lapack.jl +++ b/stdlib/LinearAlgebra/test/lapack.jl @@ -720,4 +720,13 @@ a = zeros(2,0), zeros(0) @test LinearAlgebra.LAPACK.geqrf!(a...) === a @test LinearAlgebra.LAPACK.gerqf!(a...) === a +# Issue #49489: https://github.com/JuliaLang/julia/issues/49489 +# Dimension mismatch between A and ipiv causes segfaults +@testset "issue #49489" begin + A = randn(23,23) + b = randn(23) + ipiv = collect(1:20) + @test_throws DimensionMismatch LinearAlgebra.LAPACK.getrs!('N', A, ipiv, b) +end + end # module TestLAPACK From 5039d8aceb4802cb40548b69fcb600bb5ac59985 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 3 May 2023 09:21:58 +0200 Subject: [PATCH 633/775] shield `Base.require` from invalidations when loading Symbolics.jl (#49604) --- base/loading.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 5f4f3a73af749..cf23fdb887f67 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -747,10 +747,10 @@ function explicit_project_deps_get(project_file::String, name::String)::Union{No return nothing end -function is_v1_format_manifest(raw_manifest::Dict) +function is_v1_format_manifest(raw_manifest::Dict{String}) if haskey(raw_manifest, "manifest_format") mf = raw_manifest["manifest_format"] - if mf isa Dict && haskey(mf, "uuid") + if mf isa Dict{String} && haskey(mf, "uuid") # the off-chance where an old format manifest has a dep called "manifest_format" return true end From 43c92029a592e3ebc6b04b839c04c1c85f7d629f Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 3 May 2023 09:22:33 +0200 Subject: [PATCH 634/775] minimal changes to avoid Artifacts to get invalidated by some common definitions in ecosystem (#49603) --- base/binaryplatforms.jl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/base/binaryplatforms.jl b/base/binaryplatforms.jl index 04a0073b7ff08..a4935d060b74a 100644 --- a/base/binaryplatforms.jl +++ b/base/binaryplatforms.jl @@ -1016,19 +1016,19 @@ function platforms_match(a::AbstractPlatform, b::AbstractPlatform) # Throw an error if `a` and `b` have both set non-default comparison strategies for `k` # and they're not the same strategy. - if a_comp != compare_default && b_comp != compare_default && a_comp != b_comp + if a_comp !== compare_default && b_comp !== compare_default && a_comp !== b_comp throw(ArgumentError("Cannot compare Platform objects with two different non-default comparison strategies for the same key \"$(k)\"")) end # Select the custom comparator, if we have one. comparator = a_comp - if b_comp != compare_default + if b_comp !== compare_default comparator = b_comp end # Call the comparator, passing in which objects requested this comparison (one, the other, or both) # For some comparators this doesn't matter, but for non-symmetrical comparisons, it does. - if !(comparator(ak, bk, a_comp == comparator, b_comp == comparator)::Bool) + if !(comparator(ak, bk, a_comp === comparator, b_comp === comparator)::Bool) return false end end @@ -1089,7 +1089,8 @@ function select_platform(download_info::Dict, platform::AbstractPlatform = HostP return triplet(a) > triplet(b) end) - return download_info[first(ps)] + # @invokelatest here to not get invalidated by new defs of `==(::Function, ::Function)` + return @invokelatest getindex(download_info, first(ps)) end # precompiles to reduce latency (see https://github.com/JuliaLang/julia/pull/43990#issuecomment-1025692379) From 5304baa45a9a686f122525f0cdea7c604a39aa76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Belmant?= Date: Wed, 3 May 2023 10:57:59 +0200 Subject: [PATCH 635/775] Extend stacktrace for failed tests not directly under testset (#49451) --- NEWS.md | 1 + stdlib/Test/src/Test.jl | 73 ++++++++++++++++++----- stdlib/Test/test/runtests.jl | 109 +++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 14 deletions(-) diff --git a/NEWS.md b/NEWS.md index eca0564770ab1..daf97fff71e8f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -107,6 +107,7 @@ Standard library changes * The `@test_broken` macro (or `@test` with `broken=true`) now complains if the test expression returns a non-boolean value in the same way as a non-broken test. ([#47804]) +* When a call to `@test` fails or errors inside a function, a larger stacktrace is now printed such that the location of the test within a `@testset` can be retrieved ([#49451]) #### Dates diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 0253b5a42520c..48b37ef8047fe 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -47,23 +47,56 @@ const FAIL_FAST = Ref{Bool}(false) # Backtrace utility functions function ip_has_file_and_func(ip, file, funcs) - return any(fr -> (string(fr.file) == file && fr.func in funcs), StackTraces.lookup(ip)) + return any(fr -> (in_file(fr, file) && fr.func in funcs), StackTraces.lookup(ip)) end +in_file(frame, file) = string(frame.file) == file -function scrub_backtrace(bt) +function test_location(bt, file_ts, file_t) + if (isnothing(file_ts) || isnothing(file_t)) + return macrocall_location(bt, something(file_ts, @__FILE__)) + else + return test_callsite(bt, file_ts, file_t) + end +end + +function test_callsite(bt, file_ts, file_t) + # We avoid duplicate calls to `StackTraces.lookup`, as it is an expensive call. + # For that, we retrieve locations from lower to higher stack elements + # and only traverse parts of the backtrace which we haven't traversed before. + # The order will always be -> `@test` -> `@testset`. + internal = macrocall_location(bt, @__FILE__)::Int + test = internal - 1 + findfirst(ip -> any(frame -> in_file(frame, file_t), StackTraces.lookup(ip)), @view bt[internal:end])::Int + testset = test - 1 + macrocall_location(@view(bt[test:end]), file_ts)::Int + + # If stacktrace locations differ, include frames until the `@testset` appears. + test != testset && return testset + # `@test` and `@testset` occurred at the same stacktrace location. + # This may happen if `@test` occurred directly in scope of the testset, + # or if `@test` occurred in a function that has been inlined in the testset. + frames = StackTraces.lookup(bt[testset]) + outer_frame = findfirst(frame -> in_file(frame, file_ts) && frame.func == Symbol("macro expansion"), frames)::Int + # The `@test` call occurred directly in scope of a `@testset`. + # The __source__ from `@test` will be printed in the test message upon failure. + # There is no need to include more frames, but always include at least the internal macrocall location in the stacktrace. + in_file(frames[outer_frame], file_t) && return internal + # The `@test` call was inlined, so we still need to include the callsite. + return testset +end + +macrocall_location(bt, file) = findfirst(ip -> ip_has_file_and_func(ip, file, (Symbol("macro expansion"),)), bt) + +function scrub_backtrace(bt, file_ts, file_t) do_test_ind = findfirst(ip -> ip_has_file_and_func(ip, @__FILE__, (:do_test, :do_test_throws)), bt) if do_test_ind !== nothing && length(bt) > do_test_ind bt = bt[do_test_ind + 1:end] end - name_ind = findfirst(ip -> ip_has_file_and_func(ip, @__FILE__, (Symbol("macro expansion"),)), bt) - if name_ind !== nothing && length(bt) != 0 - bt = bt[1:name_ind] - end + stop_at = test_location(bt, file_ts, file_t) + !isnothing(stop_at) && !isempty(bt) && return bt[1:stop_at] return bt end -function scrub_exc_stack(stack) - return Any[ (x[1], scrub_backtrace(x[2]::Vector{Union{Ptr{Nothing},Base.InterpreterIP}})) for x in stack ] +function scrub_exc_stack(stack, file_ts, file_t) + return Any[ (x[1], scrub_backtrace(x[2]::Vector{Union{Ptr{Nothing},Base.InterpreterIP}}, file_ts, file_t)) for x in stack ] end # define most of the test infrastructure without type specialization @@ -185,7 +218,7 @@ struct Error <: Result function Error(test_type::Symbol, orig_expr, value, bt, source::LineNumberNode) if test_type === :test_error - bt = scrub_exc_stack(bt) + bt = scrub_exc_stack(bt, nothing, extract_file(source)) end if test_type === :test_error || test_type === :nontest_error bt_str = try # try the latest world for this, since we might have eval'd new code for show @@ -1013,8 +1046,9 @@ mutable struct DefaultTestSet <: AbstractTestSet time_start::Float64 time_end::Union{Float64,Nothing} failfast::Bool + file::Union{String,Nothing} end -function DefaultTestSet(desc::AbstractString; verbose::Bool = false, showtiming::Bool = true, failfast::Union{Nothing,Bool} = nothing) +function DefaultTestSet(desc::AbstractString; verbose::Bool = false, showtiming::Bool = true, failfast::Union{Nothing,Bool} = nothing, source = nothing) if isnothing(failfast) # pass failfast state into child testsets parent_ts = get_testset() @@ -1024,8 +1058,11 @@ function DefaultTestSet(desc::AbstractString; verbose::Bool = false, showtiming: failfast = false end end - return DefaultTestSet(String(desc)::String, [], 0, false, verbose, showtiming, time(), nothing, failfast) + return DefaultTestSet(String(desc)::String, [], 0, false, verbose, showtiming, time(), nothing, failfast, extract_file(source)) end +extract_file(source::LineNumberNode) = extract_file(source.file) +extract_file(file::Symbol) = string(file) +extract_file(::Nothing) = nothing struct FailFastError <: Exception end @@ -1043,7 +1080,7 @@ function record(ts::DefaultTestSet, t::Union{Fail, Error}; print_result::Bool=TE if !(t isa Error) || t.test_type !== :test_interrupted print(t) if !isa(t, Error) # if not gets printed in the show method - Base.show_backtrace(stdout, scrub_backtrace(backtrace())) + Base.show_backtrace(stdout, scrub_backtrace(backtrace(), ts.file, extract_file(t.source))) end println() end @@ -1489,7 +1526,11 @@ function testset_beginend_call(args, tests, source) ex = quote _check_testset($testsettype, $(QuoteNode(testsettype.args[1]))) local ret - local ts = $(testsettype)($desc; $options...) + local ts = if ($testsettype === $DefaultTestSet) && $(isa(source, LineNumberNode)) + $(testsettype)($desc; source=$(QuoteNode(source.file)), $options...) + else + $(testsettype)($desc; $options...) + end push_testset(ts) # we reproduce the logic of guardseed, but this function # cannot be used as it changes slightly the semantic of @testset, @@ -1585,7 +1626,11 @@ function testset_forloop(args, testloop, source) copy!(RNG, tmprng) end - ts = $(testsettype)($desc; $options...) + ts = if ($testsettype === $DefaultTestSet) && $(isa(source, LineNumberNode)) + $(testsettype)($desc; source=$(QuoteNode(source.file)), $options...) + else + $(testsettype)($desc; $options...) + end push_testset(ts) first_iteration = false try diff --git a/stdlib/Test/test/runtests.jl b/stdlib/Test/test/runtests.jl index ac643e0ccfca2..0388e2107e098 100644 --- a/stdlib/Test/test/runtests.jl +++ b/stdlib/Test/test/runtests.jl @@ -722,6 +722,115 @@ end rm(f; force=true) end +@testset "provide informative location in backtrace for test failures" begin + win2unix(filename) = replace(filename, "\\" => '/') + utils = win2unix(tempname()) + write(utils, + """ + function test_properties2(value) + @test isodd(value) + end + """) + + included = win2unix(tempname()) + write(included, + """ + @testset "Other tests" begin + @test 1 + 1 == 3 + test_properties2(2) + end + test_properties2(8) + + # Test calls to `@test` and `@testset` with no file/lineno information (__source__ == nothing). + eval(Expr(:macrocall, Symbol("@test"), nothing, :false)) + eval(Expr(:macrocall, Symbol("@testset"), nothing, "Testset without source", quote + @test false + @test error("failed") + end)) + """) + + runtests = win2unix(tempname()) + write(runtests, + """ + using Test + + include("$utils") + + function test_properties(value) + @test isodd(value) + end + + @testset "Tests" begin + test_properties(8) + @noinline test_properties(8) + test_properties2(8) + + include("$included") + end + """) + msg = read(pipeline(ignorestatus(`$(Base.julia_cmd()) --startup-file=no --color=no $runtests`), stderr=devnull), String) + msg = win2unix(msg) + regex = r"((?:Tests|Other tests|Testset without source): Test Failed (?:.|\n)*?)\n\nStacktrace:(?:.|\n)*?(?=\n(?:Tests|Other tests))" + failures = map(eachmatch(regex, msg)) do m + m = match(r"(Tests|Other tests|Testset without source): .*? at (.*?)\n Expression: (.*)(?:.|\n)*\n+Stacktrace:\n((?:.|\n)*)", m.match) + (; testset = m[1], source = m[2], ex = m[3], stacktrace = m[4]) + end + @test length(failures) == 8 # 8 failed tests + @test count(contains("Error During Test"), split(msg, '\n')) == 1 # 1 error + test_properties_macro_source = runtests * ":6" + test_properties2_macro_source = utils * ":2" + + fail = failures[1]; lines = split(fail.stacktrace, '\n') + @test length(lines)/2 ≤ 6 + @test fail.testset == "Tests" && fail.source == test_properties_macro_source && fail.ex == "isodd(value)" + @test count(contains(runtests * ":10"), lines) == 2 # @testset + test + + fail = failures[2]; lines = split(fail.stacktrace, '\n') + @test length(lines)/2 ≤ 6 + @test fail.testset == "Tests" && fail.source == test_properties_macro_source && fail.ex == "isodd(value)" + @test count(contains(runtests * ":10"), lines) == 1 # @testset + @test count(contains(runtests * ":11"), lines) == 1 # test + + fail = failures[3]; lines = split(fail.stacktrace, '\n') + @test length(lines)/2 ≤ 6 + @test fail.testset == "Tests" && fail.source == test_properties2_macro_source && fail.ex == "isodd(value)" + @test count(contains(runtests * ":10"), lines) == 1 # @testset + @test count(contains(runtests * ":12"), lines) == 1 # test + + fail = failures[4]; lines = split(fail.stacktrace, '\n') + @test length(lines)/2 ≤ 5 + @test fail.testset == "Other tests" && fail.source == included * ":2" && fail.ex == "1 + 1 == 3" + @test count(contains(included * ":2"), lines) == 2 # @testset + test + @test count(contains(runtests * ":10"), lines) == 0 # @testset (stop at the innermost testset) + + fail = failures[5]; lines = split(fail.stacktrace, '\n') + @test length(lines)/2 ≤ 6 + @test fail.testset == "Other tests" && fail.source == test_properties2_macro_source && fail.ex == "isodd(value)" + @test count(contains(included * ":2"), lines) == 1 # @testset + @test count(contains(included * ":3"), lines) == 1 # test + @test count(contains(runtests * ":10"), lines) == 0 # @testset (stop at the innermost testset) + + fail = failures[6]; lines = split(fail.stacktrace, '\n') + @test length(lines)/2 ≤ 8 + @test fail.testset == "Tests" && fail.source == test_properties2_macro_source && fail.ex == "isodd(value)" + @test count(contains(runtests * ":10"), lines) == 1 # @testset + @test count(contains(runtests * ":14"), lines) == 1 # include + @test count(contains(included * ":5"), lines) == 1 # test + + fail = failures[7]; lines = split(fail.stacktrace, '\n') + @test length(lines)/2 ≤ 9 + @test fail.testset == "Tests" && fail.source == "none:0" && fail.ex == "false" + @test count(contains(runtests * ":10"), lines) == 1 # @testset + @test count(contains(runtests * ":14"), lines) == 1 # include + @test count(contains(included * ":8"), lines) == 1 # test + + fail = failures[8]; lines = split(fail.stacktrace, '\n') + @test length(lines)/2 ≤ 5 + @test fail.testset == "Testset without source" && fail.source == included * ":10" && fail.ex == "false" + @test count(contains(included * ":10"), lines) == 2 # @testset + test + @test count(contains(runtests * ":10"), lines) == 0 # @testset (stop at the innermost testset) +end + let io = IOBuffer() exc = Test.TestSetException(1,2,3,4,Vector{Union{Test.Error, Test.Fail}}()) Base.showerror(io, exc, backtrace()) From 0fa8f4f57cce92a892ee6e51e917473df7624871 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Wed, 3 May 2023 11:05:13 -0400 Subject: [PATCH 636/775] Restore `modname == NULL` behavior for `jl_load_dynamic_library` (#49611) Within the Julia runtime, this usage has been superseded by the new `jl_find_library_by_addr`, but this is still used downstream. In particular, CBinding.jl ends up using this branch to get a handle to libjulia-internal (perhaps a bit accidentally, since the comments in CBinding.jl suggest it intends to get a handle to the exe) This restores the behavior to avoid downstream breakage. --- src/dlload.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/dlload.c b/src/dlload.c index 84aed01039c5c..b05b0a787c458 100644 --- a/src/dlload.c +++ b/src/dlload.c @@ -275,6 +275,10 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, int n_extensions = endswith_extension(modname) ? 1 : N_EXTENSIONS; int ret; + // modname == NULL is a sentinel value requesting the handle of libjulia-internal + if (modname == NULL) + return jl_find_dynamic_library_by_addr(&jl_load_dynamic_library); + abspath = jl_isabspath(modname); is_atpath = 0; From 252883c931d0bcfd5abb7a8d177a668d72516c70 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 3 May 2023 19:12:36 +0200 Subject: [PATCH 637/775] correct comment about type of `method` in a `LineInfoNode` (#49608) --- base/boot.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/boot.jl b/base/boot.jl index 3a8abde4bce14..b9b54a8051fb0 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -109,7 +109,7 @@ #struct LineInfoNode # module::Module -# method::Symbol +# method::Any (Union{Symbol, Method, MethodInstance}) # file::Symbol # line::Int32 # inlined_at::Int32 From a35db92b56a6a5a0757c397a304f704a14ef4cb8 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 3 May 2023 15:41:20 -0300 Subject: [PATCH 638/775] Use isb for normal cpu pause on aarch64 (#49481) --- base/locks-mt.jl | 2 +- src/ccall.cpp | 11 ++++++++--- src/jlapi.c | 5 +++++ src/julia_threads.h | 7 ++++++- src/threading.c | 2 +- 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/base/locks-mt.jl b/base/locks-mt.jl index bfa3ac1b8352e..5d355b9ed200c 100644 --- a/base/locks-mt.jl +++ b/base/locks-mt.jl @@ -43,7 +43,7 @@ function lock(l::SpinLock) if @inline trylock(l) return end - ccall(:jl_cpu_pause, Cvoid, ()) + ccall(:jl_cpu_suspend, Cvoid, ()) # Temporary solution before we have gc transition support in codegen. ccall(:jl_gc_safepoint, Cvoid, ()) end diff --git a/src/ccall.cpp b/src/ccall.cpp index 1087525e1b341..b67726eee8be1 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1515,7 +1515,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) JL_GC_POP(); return mark_or_box_ccall_result(ctx, retval, retboxed, rt, unionall, static_rt); } - else if (is_libjulia_func(jl_cpu_pause)) { + else if (is_libjulia_func(jl_cpu_pause)||is_libjulia_func(jl_cpu_suspend)) { ++CCALL_STAT(jl_cpu_pause); // Keep in sync with the julia_threads.h version assert(lrt == getVoidTy(ctx.builder.getContext())); @@ -1534,9 +1534,14 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) && ctx.emission_context.TargetTriple.getSubArch() != Triple::SubArchType::NoSubArch // ARMv7 and above is < armv6 && ctx.emission_context.TargetTriple.getSubArch() < Triple::SubArchType::ARMSubArch_v6)) { - auto wfeinst = InlineAsm::get(FunctionType::get(getVoidTy(ctx.builder.getContext()), false), "wfe", + InlineAsm* wait_inst; + if (is_libjulia_func(jl_cpu_pause)) + wait_inst = InlineAsm::get(FunctionType::get(getVoidTy(ctx.builder.getContext()), false), "isb", "~{memory}", true); - ctx.builder.CreateCall(wfeinst); + else + wait_inst = InlineAsm::get(FunctionType::get(getVoidTy(ctx.builder.getContext()), false), "wfe", + "~{memory}", true); + ctx.builder.CreateCall(wait_inst); JL_GC_POP(); return ghostValue(ctx, jl_nothing_type); } else { diff --git a/src/jlapi.c b/src/jlapi.c index 369f4d6ec3ff1..001253fed71a8 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -477,6 +477,11 @@ JL_DLLEXPORT void (jl_cpu_pause)(void) jl_cpu_pause(); } +JL_DLLEXPORT void (jl_cpu_suspend)(void) +{ + jl_cpu_suspend(); +} + JL_DLLEXPORT void (jl_cpu_wake)(void) { jl_cpu_wake(); diff --git a/src/julia_threads.h b/src/julia_threads.h index 29f152172d2ab..07c722253c7f5 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -290,23 +290,28 @@ JL_DLLEXPORT void *jl_get_ptls_states(void); // Update codegen version in `ccall.cpp` after changing either `pause` or `wake` #ifdef __MIC__ # define jl_cpu_pause() _mm_delay_64(100) +# define jl_cpu_suspend() _mm_delay_64(100) # define jl_cpu_wake() ((void)0) # define JL_CPU_WAKE_NOOP 1 #elif defined(_CPU_X86_64_) || defined(_CPU_X86_) /* !__MIC__ */ # define jl_cpu_pause() _mm_pause() +# define jl_cpu_suspend() _mm_pause() # define jl_cpu_wake() ((void)0) # define JL_CPU_WAKE_NOOP 1 #elif defined(_CPU_AARCH64_) || (defined(_CPU_ARM_) && __ARM_ARCH >= 7) -# define jl_cpu_pause() __asm__ volatile ("wfe" ::: "memory") +# define jl_cpu_pause() __asm__ volatile ("isb" ::: "memory") +# define jl_cpu_suspend() __asm__ volatile ("wfe" ::: "memory") # define jl_cpu_wake() __asm__ volatile ("sev" ::: "memory") # define JL_CPU_WAKE_NOOP 0 #else # define jl_cpu_pause() ((void)0) +# define jl_cpu_suspend() ((void)0) # define jl_cpu_wake() ((void)0) # define JL_CPU_WAKE_NOOP 1 #endif JL_DLLEXPORT void (jl_cpu_pause)(void); +JL_DLLEXPORT void (jl_cpu_suspend)(void); JL_DLLEXPORT void (jl_cpu_wake)(void); #ifdef __clang_gcanalyzer__ diff --git a/src/threading.c b/src/threading.c index 2653bb8abd629..83d2e942e960f 100644 --- a/src/threading.c +++ b/src/threading.c @@ -798,7 +798,7 @@ void _jl_mutex_wait(jl_task_t *self, jl_mutex_t *lock, int safepoint) uv_cond_wait(&cond, &tls_lock); uv_mutex_unlock(&tls_lock); } - jl_cpu_pause(); + jl_cpu_suspend(); owner = jl_atomic_load_relaxed(&lock->owner); } } From 4bb3a5585f4ac635b303412b84ac0225fd580173 Mon Sep 17 00:00:00 2001 From: Steve Kelly Date: Wed, 3 May 2023 15:22:13 -0400 Subject: [PATCH 639/775] elide precompilation in Profile.jl (#49613) This saves about 2ms on my system by checking the precompilation state. This also reorganizes the remaining precompilation calls in the module. --- stdlib/Profile/src/Allocs.jl | 5 ----- stdlib/Profile/src/precompile.jl | 17 +++++++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/stdlib/Profile/src/Allocs.jl b/stdlib/Profile/src/Allocs.jl index 2be0e2c8a1b55..e45f4dca9607f 100644 --- a/stdlib/Profile/src/Allocs.jl +++ b/stdlib/Profile/src/Allocs.jl @@ -216,9 +216,4 @@ function stacktrace_memoized( return stack end -# Precompile once for the package cache. -@assert precompile(start, ()) -@assert precompile(stop, ()) -@assert precompile(fetch, ()) - end diff --git a/stdlib/Profile/src/precompile.jl b/stdlib/Profile/src/precompile.jl index 7817cdd2fba79..2d947429861a9 100644 --- a/stdlib/Profile/src/precompile.jl +++ b/stdlib/Profile/src/precompile.jl @@ -1,6 +1,11 @@ -precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, Int, UInt}) -precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, Int, UnitRange{UInt}}) -precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, UnitRange{Int}, UInt}) -precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, UnitRange{Int}, UnitRange{UInt}}) -precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, Vector{Int}, Vector{UInt}}) -precompile(Tuple{typeof(Profile._peek_report)}) +if ccall(:jl_generating_output, Cint, ()) == 1 + precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, Int, UInt}) + precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, Int, UnitRange{UInt}}) + precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, UnitRange{Int}, UInt}) + precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, UnitRange{Int}, UnitRange{UInt}}) + precompile(Tuple{typeof(Profile.tree!), Profile.StackFrameTree{UInt64}, Vector{UInt64}, Dict{UInt64, Vector{Base.StackTraces.StackFrame}}, Bool, Symbol, Vector{Int}, Vector{UInt}}) + precompile(Tuple{typeof(Profile._peek_report)}) + precompile(Tuple{typeof(Profile.Allocs.start)}) + precompile(Tuple{typeof(Profile.Allocs.stop)}) + precompile(Tuple{typeof(Profile.Allocs.fetch)}) +end From 827d34a6d6bbb9ee31ee1752ed540e95ae3f5a78 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 3 May 2023 23:43:42 +0200 Subject: [PATCH 640/775] make `IRShow.method_name` inferrable (#49607) --- base/compiler/ssair/inlining.jl | 15 +++++++++++---- base/compiler/ssair/show.jl | 15 +++++++++++---- base/stacktraces.jl | 4 ++-- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 83b69e4c82ff3..746a64b3a0996 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -303,10 +303,17 @@ function finish_cfg_inline!(state::CFGInliningState) end # duplicated from IRShow -normalize_method_name(m::Method) = m.name -normalize_method_name(m::MethodInstance) = (m.def::Method).name -normalize_method_name(m::Symbol) = m -normalize_method_name(m) = Symbol("") +function normalize_method_name(m) + if m isa Method + return m.name + elseif m isa MethodInstance + return (m.def::Method).name + elseif m isa Symbol + return m + else + return Symbol("") + end +end @noinline method_name(m::LineInfoNode) = normalize_method_name(m.method) inline_node_is_duplicate(topline::LineInfoNode, line::LineInfoNode) = diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index 44baae392fa91..b420eb32b1205 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -171,10 +171,17 @@ function default_expr_type_printer(io::IO; @nospecialize(type), used::Bool, show return nothing end -normalize_method_name(m::Method) = m.name -normalize_method_name(m::MethodInstance) = (m.def::Method).name -normalize_method_name(m::Symbol) = m -normalize_method_name(m) = Symbol("") +function normalize_method_name(m) + if m isa Method + return m.name + elseif m isa MethodInstance + return (m.def::Method).name + elseif m isa Symbol + return m + else + return Symbol("") + end +end @noinline method_name(m::LineInfoNode) = normalize_method_name(m.method) # converts the linetable for line numbers diff --git a/base/stacktraces.jl b/base/stacktraces.jl index 273d8236c4841..ee6a2762d7818 100644 --- a/base/stacktraces.jl +++ b/base/stacktraces.jl @@ -125,7 +125,7 @@ function lookup_inline_frame_info(func::Symbol, file::Symbol, linenum::Int, inli or Symbol match is found? Or can a limit on the subsequent backtracks be placed? =# for (i, line) in enumerate(inlinetable) - Base.IRShow.method_name(line) == func && line.file ∈ (file, filestripped) && line.line == linenum || continue + Base.IRShow.method_name(line) === func && line.file ∈ (file, filestripped) && line.line == linenum || continue if line.method isa MethodInstance linfo = line.method break @@ -134,7 +134,7 @@ function lookup_inline_frame_info(func::Symbol, file::Symbol, linenum::Int, inli # backtrack to find the matching MethodInstance, if possible for j in (i - 1):-1:1 nextline = inlinetable[j] - nextline.inlined_at == line.inlined_at && Base.IRShow.method_name(line) == Base.IRShow.method_name(nextline) && line.file == nextline.file || break + nextline.inlined_at == line.inlined_at && Base.IRShow.method_name(line) === Base.IRShow.method_name(nextline) && line.file === nextline.file || break if nextline.method isa MethodInstance linfo = nextline.method break From dfc74c3f573b8a3d9da54aac93b22fcd37c9f3be Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Thu, 4 May 2023 05:26:38 -0400 Subject: [PATCH 641/775] Revise `jl_timing` prints for `dlopen` (#49560) By special request, this changes our `dlopen` logic to include the actually-opened filepath. For convenience, the base filename is listed first. This change also fixes a bug in `gnu_basename` where Windows filepaths were not being scanned for '\\' correctly. --- src/dlload.c | 10 +++++++--- src/timing.c | 7 +++++++ src/timing.h | 9 ++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/dlload.c b/src/dlload.c index b05b0a787c458..6141802df421c 100644 --- a/src/dlload.c +++ b/src/dlload.c @@ -283,7 +283,7 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, is_atpath = 0; JL_TIMING(DL_OPEN, DL_OPEN); - jl_timing_printf(JL_TIMING_CURRENT_BLOCK, gnu_basename(modname)); + jl_timing_puts(JL_TIMING_CURRENT_BLOCK, modname); // Detect if our `modname` is something like `@rpath/libfoo.dylib` #ifdef _OS_DARWIN_ @@ -340,8 +340,10 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, if (i == 0) { // LoadLibrary already tested the extensions, we just need to check the `stat` result #endif handle = jl_dlopen(path, flags); - if (handle) + if (handle) { + jl_timing_puts(JL_TIMING_CURRENT_BLOCK, jl_pathname_for_handle(handle)); return handle; + } #ifdef _OS_WINDOWS_ err = GetLastError(); } @@ -360,8 +362,10 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, path[0] = '\0'; snprintf(path, PATHBUF, "%s%s", modname, ext); handle = jl_dlopen(path, flags); - if (handle) + if (handle) { + jl_timing_puts(JL_TIMING_CURRENT_BLOCK, jl_pathname_for_handle(handle)); return handle; + } #ifdef _OS_WINDOWS_ err = GetLastError(); break; // LoadLibrary already tested the rest diff --git a/src/timing.c b/src/timing.c index 26cbccf6f86b2..c62b14b2c16c8 100644 --- a/src/timing.c +++ b/src/timing.c @@ -233,6 +233,13 @@ JL_DLLEXPORT void jl_timing_printf(jl_timing_block_t *cur_block, const char *for va_end(args); } +JL_DLLEXPORT void jl_timing_puts(jl_timing_block_t *cur_block, const char *str) +{ +#ifdef USE_TRACY + TracyCZoneText(*(cur_block->tracy_ctx), str, strlen(str)); +#endif +} + JL_DLLEXPORT int jl_timing_set_enable(const char *subsystem, uint8_t enabled) { for (int i = 0; i < JL_TIMING_LAST; i++) { diff --git a/src/timing.h b/src/timing.h index b8b1d51c603f6..24c120b388c01 100644 --- a/src/timing.h +++ b/src/timing.h @@ -8,6 +8,11 @@ static inline const char *gnu_basename(const char *path) { const char *base = strrchr(path, '/'); +#ifdef _WIN32 + const char *backslash = strrchr(path, '\\'); + if (backslash > base) + base = backslash; +#endif return base ? base+1 : path; } @@ -65,7 +70,8 @@ extern uint32_t jl_timing_print_limit; #define jl_timing_show_method_instance(mi, b) #define jl_timing_show_method(mi, b) #define jl_timing_show_func_sig(tt, b) -#define jl_timing_printf(s, f, ...) +#define jl_timing_printf(b, f, ...) +#define jl_timing_puts(b, s) #define jl_timing_block_enter_task(ct, ptls, blk) #define jl_timing_block_exit_task(ct, ptls) ((jl_timing_block_t *)NULL) #define jl_pop_timing_block(blk) @@ -106,6 +112,7 @@ void jl_timing_show_method_instance(jl_method_instance_t *mi, jl_timing_block_t void jl_timing_show_method(jl_method_t *method, jl_timing_block_t *cur_block); void jl_timing_show_func_sig(jl_value_t *v, jl_timing_block_t *cur_block); void jl_timing_printf(jl_timing_block_t *cur_block, const char *format, ...); +void jl_timing_puts(jl_timing_block_t *cur_block, const char *str); #ifdef __cplusplus } #endif From 4e70da29a6c6027cfed441364f9925690be6f750 Mon Sep 17 00:00:00 2001 From: woclass Date: Thu, 4 May 2023 17:42:05 +0800 Subject: [PATCH 642/775] Update HISTORY.md for `DelimitedFiles` (#49582) xref: #48671 --- HISTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index e31eb04608e7b..09143b63263ec 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -177,7 +177,7 @@ Standard library changes #### DelimitedFiles -* DelimitedFiles has been moved out as a separate package. It now has to be explicitly installed to be used. +* DelimitedFiles has been moved out as a separate package. Deprecated or removed --------------------- From 425389ef8c89d26b9b90efa10bf5458b7636fc3e Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 4 May 2023 06:08:03 -0400 Subject: [PATCH 643/775] Various precompile fixes (#49585) --- contrib/generate_precompile.jl | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index c7ec9d0988996..e8901a7b462ea 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -45,6 +45,7 @@ precompile(Tuple{typeof(push!), Vector{Function}, Function}) # miscellaneous precompile(Tuple{typeof(Base.require), Base.PkgId}) precompile(Tuple{typeof(Base.recursive_prefs_merge), Base.Dict{String, Any}}) +precompile(Tuple{typeof(Base.recursive_prefs_merge), Base.Dict{String, Any}, Base.Dict{String, Any}, Vararg{Base.Dict{String, Any}}}) precompile(Tuple{typeof(Base.hashindex), Tuple{Base.PkgId, Nothing}, Int64}) precompile(Tuple{typeof(Base.hashindex), Tuple{Base.PkgId, String}, Int64}) precompile(Tuple{typeof(isassigned), Core.SimpleVector, Int}) @@ -70,7 +71,8 @@ print("") printstyled("a", "b") display([1]) display([1 2; 3 4]) -@time 1+1 +foo(x) = 1 +@time @eval foo(1) ; pwd $CTRL_C $CTRL_R$CTRL_C @@ -180,6 +182,14 @@ if Libdl !== nothing """ end +InteractiveUtils = get(Base.loaded_modules, + Base.PkgId(Base.UUID("b77e0a4c-d291-57a0-90e8-8db25a27a240"), "InteractiveUtils"), + nothing) +if InteractiveUtils !== nothing + repl_script *= """ + @time_imports using Random + """ +end const JULIA_PROMPT = "julia> " const PKG_PROMPT = "pkg> " @@ -425,8 +435,11 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe ps.head = :tuple # println(ps) ps = Core.eval(PrecompileStagingArea, ps) - precompile(ps...) - n_succeeded += 1 + if precompile(ps...) + n_succeeded += 1 + else + @warn "Failed to precompile expression" form=statement _module=nothing _file=nothing _line=0 + end failed = length(statements) - n_succeeded yield() # Make clock spinning print_state("step3" => string("R$n_succeeded", failed > 0 ? " ($failed failed)" : "")) From e4622b462baad6d5e4113c6e31028fb001195dc1 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Thu, 4 May 2023 05:12:11 -0500 Subject: [PATCH 644/775] Remove unused definition of `_shorthash7` for wrong size UInt (#49498) --- base/dict.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/base/dict.jl b/base/dict.jl index 55a364dc97e6a..8a78c1fa8da45 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -147,8 +147,7 @@ end empty(a::AbstractDict, ::Type{K}, ::Type{V}) where {K, V} = Dict{K, V}() # Gets 7 most significant bits from the hash (hsh), first bit is 1 -_shorthash7(hsh::UInt32) = (hsh >> UInt(25))%UInt8 | 0x80 -_shorthash7(hsh::UInt64) = (hsh >> UInt(57))%UInt8 | 0x80 +_shorthash7(hsh::UInt) = (hsh >> (8sizeof(UInt)-7))%UInt8 | 0x80 # hashindex (key, sz) - computes optimal position and shorthash7 # idx - optimal position in the hash table From 59e47b41319a66c07e90c8d24d852dcf223b180b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20de=20Frutos?= Date: Thu, 4 May 2023 12:15:15 +0200 Subject: [PATCH 645/775] adding docu to sum! explain target should not alias with the source (#49478) --- base/reducedim.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/base/reducedim.jl b/base/reducedim.jl index 101568d60002b..c1c58ccdfefed 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -525,6 +525,8 @@ sum(f, A::AbstractArray; dims) sum!(r, A) Sum elements of `A` over the singleton dimensions of `r`, and write results to `r`. +Note that since the sum! function is intended to operate without making any allocations, +the target should not alias with the source. # Examples ```jldoctest From 2a1fa43b7e19304b1145d227cc9a5eb9d0bab6dc Mon Sep 17 00:00:00 2001 From: Lionel Zoubritzky Date: Thu, 4 May 2023 12:15:27 +0200 Subject: [PATCH 646/775] Fix method show with kwarg and optional arguments (#49475) --- base/methodshow.jl | 7 ++++--- test/show.jl | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/base/methodshow.jl b/base/methodshow.jl index ab6412a95395d..0eb99dc88303f 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -194,10 +194,11 @@ function functionloc(@nospecialize(f)) end function sym_to_string(sym) - s = String(sym) - if s === :var"..." + if sym === :var"..." return "..." - elseif endswith(s, "...") + end + s = String(sym) + if endswith(s, "...") return string(sprint(show_sym, Symbol(s[1:end-3])), "...") else return sprint(show_sym, sym) diff --git a/test/show.jl b/test/show.jl index 5a9a8a28cca62..76772c649a838 100644 --- a/test/show.jl +++ b/test/show.jl @@ -791,6 +791,14 @@ let ms = methods(S45879) @test sprint(show, Base.MethodList(Method[], typeof(S45879).name.mt)) isa String end +function f49475(a=12.0; b) end +let ms = methods(f49475) + @test length(ms) == 2 + repr1 = sprint(show, "text/plain", ms[1]) + repr2 = sprint(show, "text/plain", ms[2]) + @test occursin("f49475(; ...)", repr1) || occursin("f49475(; ...)", repr2) +end + if isempty(Base.GIT_VERSION_INFO.commit) @test occursin("https://github.com/JuliaLang/julia/tree/v$VERSION/base/special/trig.jl#L", Base.url(which(sin, (Float64,)))) else From 37e5aedb99af37b7ba47a5ca3b7c856829f09ff0 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 4 May 2023 12:16:32 +0200 Subject: [PATCH 647/775] doc: manual: command-line interface: fix cross-reference error (#49459) --- doc/src/manual/command-line-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/command-line-interface.md b/doc/src/manual/command-line-interface.md index 781a77a33dadb..8164299f01250 100644 --- a/doc/src/manual/command-line-interface.md +++ b/doc/src/manual/command-line-interface.md @@ -69,7 +69,7 @@ Note that although you should have a `~/.julia` directory once you've run Julia first time, you may need to create the `~/.julia/config` folder and the `~/.julia/config/startup.jl` file if you use it. -To have startup code run only in [The Julia REPL] (and not when `julia` is *e.g.* run +To have startup code run only in [The Julia REPL](@ref) (and not when `julia` is *e.g.* run on a script), use [`atreplinit`](@ref) in `startup.jl`: ```julia From e6616fb1db132754276f494edf3c9594137e8a49 Mon Sep 17 00:00:00 2001 From: Paul Berg Date: Thu, 4 May 2023 12:18:42 +0200 Subject: [PATCH 648/775] Markdown: Reset underline style when wrapping text. (#49454) --- .../src/render/terminal/formatting.jl | 17 ++++++++++ stdlib/Markdown/src/render/terminal/render.jl | 11 ++++--- stdlib/Markdown/test/runtests.jl | 32 +++++++++++++++++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/stdlib/Markdown/src/render/terminal/formatting.jl b/stdlib/Markdown/src/render/terminal/formatting.jl index 1d9e9a5523184..a031de4d9ad82 100644 --- a/stdlib/Markdown/src/render/terminal/formatting.jl +++ b/stdlib/Markdown/src/render/terminal/formatting.jl @@ -17,6 +17,23 @@ function wrapped_line(io::IO, s::AbstractString, width, i) word_length == 0 && continue if isempty(lines) || i + word_length + 1 > width i = word_length + if length(lines) > 0 + last_line = lines[end] + maybe_underline = findlast(Base.text_colors[:underline], last_line) + if !isnothing(maybe_underline) + # disable underline style at end of line if not already disabled. + maybe_disable_underline = max( + last(something(findlast(Base.disable_text_style[:underline], last_line), -1)), + last(something(findlast(Base.text_colors[:normal], last_line), -1)), + ) + + if maybe_disable_underline < 0 || maybe_disable_underline < last(maybe_underline) + + lines[end] = last_line * Base.disable_text_style[:underline] + word = Base.text_colors[:underline] * word + end + end + end push!(lines, word) else i += word_length + 1 diff --git a/stdlib/Markdown/src/render/terminal/render.jl b/stdlib/Markdown/src/render/terminal/render.jl index a7421b13660a0..20b1ef6d041fc 100644 --- a/stdlib/Markdown/src/render/terminal/render.jl +++ b/stdlib/Markdown/src/render/terminal/render.jl @@ -81,14 +81,15 @@ end function _term_header(io::IO, md, char, columns) text = terminline_string(io, md.text) with_output_color(:bold, io) do io - print(io, ' '^margin) + pre = ' '^margin + print(io, pre) line_no, lastline_width = print_wrapped(io, text, - width=columns - 4margin; pre=" ") - line_width = min(1 + lastline_width, columns) + width=columns - 4margin; pre) + line_width = min(lastline_width, columns) if line_no > 1 - line_width = max(line_width, div(columns, 3)) + line_width = max(line_width, div(columns, 3)+length(pre)) end - header_width = max(0, line_width-margin) + header_width = max(0, line_width-length(pre)) char != ' ' && header_width > 0 && print(io, '\n', ' '^(margin), char^header_width) end end diff --git a/stdlib/Markdown/test/runtests.jl b/stdlib/Markdown/test/runtests.jl index 52bcf07ad8942..1b2255a104ef0 100644 --- a/stdlib/Markdown/test/runtests.jl +++ b/stdlib/Markdown/test/runtests.jl @@ -1160,6 +1160,38 @@ let buf = IOBuffer() @test String(take!(buf)) == " \e[4memph\e[24m" end +let word = "Markdown" # disable underline when wrapping lines + buf = IOBuffer() + ctx = IOContext(buf, :color => true, :displaysize => (displaysize(buf)[1], length(word))) + long_italic_text = Markdown.parse('_' * join(fill(word, 10), ' ') * '_') + show(ctx, MIME("text/plain"), long_italic_text) + lines = split(String(take!(buf)), '\n') + @test endswith(lines[begin], Base.disable_text_style[:underline]) + @test startswith(lines[begin+1], ' '^Markdown.margin * Base.text_colors[:underline]) +end + +let word = "Markdown" # pre is of size Markdown.margin when wrapping title + buf = IOBuffer() + ctx = IOContext(buf, :color => true, :displaysize => (displaysize(buf)[1], length(word))) + long_title = Markdown.parse("# " * join(fill(word, 3))) + show(ctx, MIME("text/plain"), long_title) + lines = split(String(take!(buf)), '\n') + @test all(startswith(Base.text_colors[:bold] * ' '^Markdown.margin), lines) +end + +struct Struct49454 end +Base.show(io::IO, ::Struct49454) = + print(io, Base.text_colors[:underline], "Struct 49454()", Base.text_colors[:normal]) + +let buf = IOBuffer() + ctx = IOContext(buf, :color => true, :displaysize => (displaysize(buf)[1], 10)) + show(stdout, MIME("text/plain"), md""" + text without $(Struct49454()) underline. + """) + lines = split(String(take!(buf)), '\n') + @test !occursin(Base.text_colors[:underline], lines[end]) +end + # table rendering with term #25213 t = """ a | b From acb2d2d381c2254ddcfeafcd706e719c45cb619b Mon Sep 17 00:00:00 2001 From: pbouffard Date: Thu, 4 May 2023 03:19:33 -0700 Subject: [PATCH 649/775] Add missing documentation (#49377) --- stdlib/REPL/docs/src/index.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/stdlib/REPL/docs/src/index.md b/stdlib/REPL/docs/src/index.md index 8e9e19228ea3d..1ea87c3a4109f 100644 --- a/stdlib/REPL/docs/src/index.md +++ b/stdlib/REPL/docs/src/index.md @@ -43,14 +43,14 @@ julia> ans "12" ``` -In Julia mode, the REPL supports something called *prompt pasting*. This activates when pasting -text that starts with `julia> ` into the REPL. In that case, only expressions starting with -`julia> ` are parsed, others are removed. This makes it possible to paste a chunk of code -that has been copied from a REPL session without having to scrub away prompts and outputs. This -feature is enabled by default but can be disabled or enabled at will with `REPL.enable_promptpaste(::Bool)`. -If it is enabled, you can try it out by pasting the code block above this paragraph straight into -the REPL. This feature does not work on the standard Windows command prompt due to its limitation -at detecting when a paste occurs. +In Julia mode, the REPL supports something called *prompt pasting*. This activates when pasting text +that starts with `julia> ` into the REPL. In that case, only expressions starting with `julia> ` (as +well as the other REPL mode prompts: `shell> `, `help?> `, `pkg>` ) are parsed, but others are +removed. This makes it possible to paste a chunk of text that has been copied from a REPL session +without having to scrub away prompts and outputs. This feature is enabled by default but can be +disabled or enabled at will with `REPL.enable_promptpaste(::Bool)`. If it is enabled, you can try it +out by pasting the code block above this paragraph straight into the REPL. This feature does not +work on the standard Windows command prompt due to its limitation at detecting when a paste occurs. Objects are printed at the REPL using the [`show`](@ref) function with a specific [`IOContext`](@ref). In particular, the `:limit` attribute is set to `true`. From 9a6af18c2b31440261aa01d4bd72aaf858652663 Mon Sep 17 00:00:00 2001 From: Gabriel Wu Date: Thu, 4 May 2023 18:20:09 +0800 Subject: [PATCH 650/775] Add fence to avoid JL_CALLABLE being parsed as MarkdownAST.Emph (#49360) --- doc/src/devdocs/ast.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index 76b0cc97df5bf..9fd03ad9a667a 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -657,7 +657,7 @@ for important details on how to modify these fields safely. The ABI to use when calling `fptr`. Some significant ones include: * 0 - Not compiled yet - * 1 - JL_CALLABLE `jl_value_t *(*)(jl_function_t *f, jl_value_t *args[nargs], uint32_t nargs)` + * 1 - `JL_CALLABLE` `jl_value_t *(*)(jl_function_t *f, jl_value_t *args[nargs], uint32_t nargs)` * 2 - Constant (value stored in `rettype_const`) * 3 - With Static-parameters forwarded `jl_value_t *(*)(jl_svec_t *sparams, jl_function_t *f, jl_value_t *args[nargs], uint32_t nargs)` * 4 - Run in interpreter `jl_value_t *(*)(jl_method_instance_t *meth, jl_function_t *f, jl_value_t *args[nargs], uint32_t nargs)` From 2e80c0d2645f48a4b3a8884b342339c302844ede Mon Sep 17 00:00:00 2001 From: Zentrik Date: Thu, 4 May 2023 11:26:06 +0100 Subject: [PATCH 651/775] Inline StepRange construction (#49270) This can improve performance when iterating over a range with a step as currently steprange_last is not inlined. About a 12ns improvement I don't think this should slow anything down as at worst you're making a call which is what you'd have to do without inlining. Also, it should be unlikely that branch is taken. This should allow for better automatic inlining. --- base/multidimensional.jl | 2 +- base/range.jl | 3 ++- test/ranges.jl | 25 +++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 70b7354c18bd2..ce1b6c39adb43 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -526,7 +526,7 @@ module IteratorsMD dimrev = ntuple(i -> sum(==(i), dims; init = 0) == 1, Val(length(indices))) length(dims) == sum(dimrev) || throw(ArgumentError(Base.LazyString("invalid dimensions ", dims, " in reverse"))) length(dims) == length(indices) && return Base._reverse(iter, :) - indices′ = map((i, f) -> f ? reverse(i) : i, indices, dimrev) + indices′ = map((i, f) -> f ? (@noinline reverse(i)) : i, indices, dimrev) return CartesianIndices(indices′) end diff --git a/base/range.jl b/base/range.jl index ea29516f64d24..80693a8e94a0c 100644 --- a/base/range.jl +++ b/base/range.jl @@ -350,7 +350,8 @@ function steprange_last(start, step, stop)::typeof(stop) # Compute remainder as a nonnegative number: if absdiff isa Signed && absdiff < zero(absdiff) # unlikely, but handle the signed overflow case with unsigned rem - remain = convert(typeof(absdiff), unsigned(absdiff) % absstep) + overflow_case(absdiff, absstep) = (@noinline; convert(typeof(absdiff), unsigned(absdiff) % absstep)) + remain = overflow_case(absdiff, absstep) else remain = convert(typeof(absdiff), absdiff % absstep) end diff --git a/test/ranges.jl b/test/ranges.jl index bbdd303adb3c5..04c2cdcb47ac4 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -1,6 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license using Base.Checked: checked_length +using InteractiveUtils: code_llvm @testset "range construction" begin @test_throws ArgumentError range(start=1, step=1, stop=2, length=10) @@ -2400,3 +2401,27 @@ end @test test_firstindex(StepRange{Union{Int64,Int128},Int}(Int64(1), 1, Int128(1))) @test test_firstindex(StepRange{Union{Int64,Int128},Int}(Int64(1), 1, Int128(0))) end + +@testset "Inline StepRange Construction #49270" begin + x = rand(Float32, 80) + a = rand(round(Int, length(x) / 2):length(x), 10^6) + + function test(x, a) + c = zero(Float32) + + @inbounds for j in a + for i in 1:8:j + c += x[i] + end + end + + return c + end + + llvm_ir(f, args) = sprint((io, args...) -> code_llvm(io, args...; debuginfo=:none), f, Base.typesof(args...)) + + ir = llvm_ir(test, (x, a)) + @test !occursin("steprange_last", ir) + @test !occursin("_colon", ir) + @test !occursin("StepRange", ir) +end From a632def2514432b1e91f586f14adbf9211a7d6e2 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 4 May 2023 06:26:58 -0400 Subject: [PATCH 652/775] speed up variable lookups in lowering to help #48990 (#49213) Replace some more linear lookups with hash tables. --- src/julia-parser.scm | 2 +- src/julia-syntax.scm | 95 +++++++++++++++++++++++--------------------- 2 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/julia-parser.scm b/src/julia-parser.scm index 02d1c333404a6..16180129881fd 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -49,7 +49,7 @@ ((not (length> l 8)) (eval `(lambda (x) (not (not (,(if (every symbol? l) 'memq 'memv) x (quote ,l))))))) - ((and (every symbol? l) (not (length> l 20))) + ((and (not (length> l 20)) (every symbol? l)) (eval `(lambda (x) (not (not (memq x (quote ,l))))))) (else diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index ccf566ed87885..cac8c7b5228b9 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -3256,8 +3256,9 @@ ((and (pair? e) (eq? (car e) 'with-static-parameters)) (free-vars- (cadr e) tab)) ((or (atom? e) (quoted? e)) tab) ((eq? (car e) 'lambda) - (let ((bound (lambda-all-vars e))) - (for-each (lambda (v) (if (not (memq v bound)) (put! tab v #t))) + (let ((bound (table))) + (for-each (lambda (b) (put! bound b #t)) (lambda-all-vars e)) + (for-each (lambda (v) (if (not (has? bound v)) (put! tab v #t))) (free-vars (lam:body e)))) tab) (else @@ -3475,13 +3476,13 @@ f(x) = yt(x) (define (convert-lambda lam fname interp capt-sp opaq) (let ((body (add-box-inits-to-body - lam (cl-convert (cadddr lam) fname lam (table) (table) #f interp opaq)))) + lam (cl-convert (cadddr lam) fname lam (table) (table) #f interp opaq (table) (vinfo-to-table (car (lam:vinfo lam))))))) `(lambda ,(lam:args lam) (,(clear-capture-bits (car (lam:vinfo lam))) () ,(caddr (lam:vinfo lam)) ,(delete-duplicates (append (lam:sp lam) capt-sp))) - ,body))) + ,body))) ;; renumber ssavalues assigned in an expr, allowing it to be repeated (define (renumber-assigned-ssavalues e) @@ -3550,10 +3551,10 @@ f(x) = yt(x) ;; declared types. ;; when doing this, the original value needs to be preserved, to ;; ensure the expression `a=b` always returns exactly `b`. -(define (convert-assignment var rhs0 fname lam interp opaq globals) +(define (convert-assignment var rhs0 fname lam interp opaq globals locals) (cond ((symbol? var) - (let* ((vi (assq var (car (lam:vinfo lam)))) + (let* ((vi (get locals var #f)) (cv (assq var (cadr (lam:vinfo lam)))) (vt (or (and vi (vinfo:type vi)) (and cv (vinfo:type cv)) @@ -3568,7 +3569,7 @@ f(x) = yt(x) (equal? rhs0 '(the_exception))) rhs0 (make-ssavalue))) - (rhs (convert-for-type-decl rhs1 (cl-convert vt fname lam #f #f #f interp opaq) #t lam)) + (rhs (convert-for-type-decl rhs1 (cl-convert vt fname lam #f #f #f interp opaq (table) locals) #t lam)) (ex (cond (closed `(call (core setfield!) ,(if interp `($ ,var) @@ -3687,8 +3688,9 @@ f(x) = yt(x) const atomic null true false ssavalue isdefined toplevel module lambda error gc_preserve_begin gc_preserve_end import using export inline noinline))) -(define (local-in? s lam) - (or (assq s (car (lam:vinfo lam))) +(define (local-in? s lam (tab #f)) + (or (and tab (has? tab s)) + (assq s (car (lam:vinfo lam))) (assq s (cadr (lam:vinfo lam))))) ;; Try to identify never-undef variables, and then clear the `captured` flag for single-assigned, @@ -3843,17 +3845,17 @@ f(x) = yt(x) (define (toplevel-preserving? e) (and (pair? e) (memq (car e) '(if elseif block trycatch tryfinally trycatchelse)))) -(define (map-cl-convert exprs fname lam namemap defined toplevel interp opaq (globals (table))) +(define (map-cl-convert exprs fname lam namemap defined toplevel interp opaq (globals (table)) (locals (table))) (if toplevel (map (lambda (x) (let ((tl (lift-toplevel (cl-convert x fname lam namemap defined (and toplevel (toplevel-preserving? x)) - interp opaq globals)))) + interp opaq globals locals)))) (if (null? (cdr tl)) (car tl) `(block ,@(cdr tl) ,(car tl))))) exprs) - (map (lambda (x) (cl-convert x fname lam namemap defined #f interp opaq globals)) exprs))) + (map (lambda (x) (cl-convert x fname lam namemap defined #f interp opaq globals locals)) exprs))) (define (prepare-lambda! lam) ;; mark all non-arguments as assigned, since locals that are never assigned @@ -3862,11 +3864,11 @@ f(x) = yt(x) (list-tail (car (lam:vinfo lam)) (length (lam:args lam)))) (lambda-optimize-vars! lam)) -(define (cl-convert e fname lam namemap defined toplevel interp opaq (globals (table))) +(define (cl-convert e fname lam namemap defined toplevel interp opaq (globals (table)) (locals (table))) (if (and (not lam) (not (and (pair? e) (memq (car e) '(lambda method macro opaque_closure))))) (if (atom? e) e - (cons (car e) (map-cl-convert (cdr e) fname lam namemap defined toplevel interp opaq globals))) + (cons (car e) (map-cl-convert (cdr e) fname lam namemap defined toplevel interp opaq globals locals))) (cond ((symbol? e) (define (new-undef-var name) @@ -3885,12 +3887,12 @@ f(x) = yt(x) (val (if (equal? typ '(core Any)) val `(call (core typeassert) ,val - ,(cl-convert typ fname lam namemap defined toplevel interp opaq globals))))) + ,(cl-convert typ fname lam namemap defined toplevel interp opaq globals locals))))) `(block ,@(if (eq? box access) '() `((= ,access ,box))) ,undefcheck ,val))) - (let ((vi (assq e (car (lam:vinfo lam)))) + (let ((vi (get locals e #f)) (cv (assq e (cadr (lam:vinfo lam))))) (cond ((eq? e fname) e) ((memq e (lam:sp lam)) e) @@ -3917,15 +3919,15 @@ f(x) = yt(x) e) ((=) (let ((var (cadr e)) - (rhs (cl-convert (caddr e) fname lam namemap defined toplevel interp opaq globals))) - (convert-assignment var rhs fname lam interp opaq globals))) + (rhs (cl-convert (caddr e) fname lam namemap defined toplevel interp opaq globals locals))) + (convert-assignment var rhs fname lam interp opaq globals locals))) ((local-def) ;; make new Box for local declaration of defined variable - (let ((vi (assq (cadr e) (car (lam:vinfo lam))))) + (let ((vi (get locals (cadr e) #f))) (if (and vi (vinfo:asgn vi) (vinfo:capt vi)) `(= ,(cadr e) (call (core Box))) '(null)))) ((local) ;; convert local declarations to newvar statements - (let ((vi (assq (cadr e) (car (lam:vinfo lam))))) + (let ((vi (get locals (cadr e) #f))) (if (and vi (vinfo:asgn vi) (vinfo:capt vi)) `(= ,(cadr e) (call (core Box))) (if (vinfo:never-undef vi) @@ -3936,12 +3938,12 @@ f(x) = yt(x) e) ((atomic) e) ((const-if-global) - (if (local-in? (cadr e) lam) + (if (local-in? (cadr e) lam locals) '(null) `(const ,(cadr e)))) ((isdefined) ;; convert isdefined expr to function for closure converted variables (let* ((sym (cadr e)) - (vi (and (symbol? sym) (assq sym (car (lam:vinfo lam))))) + (vi (and (symbol? sym) (get locals sym #f))) (cv (and (symbol? sym) (assq sym (cadr (lam:vinfo lam)))))) (cond ((eq? sym fname) e) ((memq sym (lam:sp lam)) e) @@ -3981,13 +3983,13 @@ f(x) = yt(x) (lam2 (if short #f (cadddr e))) (vis (if short '(() () ()) (lam:vinfo lam2))) (cvs (map car (cadr vis))) - (local? (lambda (s) (and lam (symbol? s) (local-in? s lam)))) + (local? (lambda (s) (and lam (symbol? s) (local-in? s lam locals)))) (local (and (not (outerref? (cadr e))) (local? name))) (sig (and (not short) (caddr e))) (sp-inits (if (or short (not (eq? (car sig) 'block))) '() (map-cl-convert (butlast (cdr sig)) - fname lam namemap defined toplevel interp opaq globals))) + fname lam namemap defined toplevel interp opaq globals locals))) (sig (and sig (if (eq? (car sig) 'block) (last sig) sig)))) @@ -4014,10 +4016,11 @@ f(x) = yt(x) ;; anonymous functions with keyword args generate global ;; functions that refer to the type of a local function (rename-sig-types sig namemap) - fname lam namemap defined toplevel interp opaq globals) + fname lam namemap defined toplevel interp opaq globals locals) ,(let ((body (add-box-inits-to-body lam2 - (cl-convert (cadddr lam2) 'anon lam2 (table) (table) #f interp opaq)))) + (cl-convert (cadddr lam2) 'anon lam2 (table) (table) #f interp opaq (table) + (vinfo-to-table (car (lam:vinfo lam2))))))) `(lambda ,(cadr lam2) (,(clear-capture-bits (car vis)) ,@(cdr vis)) @@ -4028,7 +4031,7 @@ f(x) = yt(x) (newlam (compact-and-renumber (linearize (car exprs)) 'none 0))) `(toplevel-butfirst (block ,@sp-inits - (method ,name ,(cl-convert sig fname lam namemap defined toplevel interp opaq globals) + (method ,name ,(cl-convert sig fname lam namemap defined toplevel interp opaq globals locals) ,(julia-bq-macro newlam))) ,@top-stmts)))) @@ -4131,7 +4134,7 @@ f(x) = yt(x) (append (map (lambda (gs tvar) (make-assignment gs `(call (core TypeVar) ',tvar (core Any)))) closure-param-syms closure-param-names) - `((method #f ,(cl-convert arg-defs fname lam namemap defined toplevel interp opaq globals) + `((method #f ,(cl-convert arg-defs fname lam namemap defined toplevel interp opaq globals locals) ,(convert-lambda lam2 (if iskw (caddr (lam:args lam2)) @@ -4170,7 +4173,7 @@ f(x) = yt(x) (begin (put! defined name #t) `(toplevel-butfirst - ,(convert-assignment name mk-closure fname lam interp opaq globals) + ,(convert-assignment name mk-closure fname lam interp opaq globals locals) ,@typedef ,@(map (lambda (v) `(moved-local ,v)) moved-vars) ,@sp-inits @@ -4178,42 +4181,43 @@ f(x) = yt(x) ((lambda) ;; happens inside (thunk ...) and generated function bodies (for-each (lambda (vi) (vinfo:set-asgn! vi #t)) (list-tail (car (lam:vinfo e)) (length (lam:args e)))) + (lambda-optimize-vars! e) (let ((body (map-cl-convert (cdr (lam:body e)) 'anon - (lambda-optimize-vars! e) + e (table) (table) (null? (cadr e)) ;; only toplevel thunks have 0 args - interp opaq globals))) + interp opaq globals (vinfo-to-table (car (lam:vinfo e)))))) `(lambda ,(cadr e) (,(clear-capture-bits (car (lam:vinfo e))) () ,@(cddr (lam:vinfo e))) (block ,@body)))) ;; remaining `::` expressions are type assertions ((|::|) - (cl-convert `(call (core typeassert) ,@(cdr e)) fname lam namemap defined toplevel interp opaq globals)) + (cl-convert `(call (core typeassert) ,@(cdr e)) fname lam namemap defined toplevel interp opaq globals locals)) ;; remaining `decl` expressions are only type assertions if the ;; argument is global or a non-symbol. ((decl) (cond ((and (symbol? (cadr e)) - (local-in? (cadr e) lam)) + (local-in? (cadr e) lam locals)) '(null)) (else (cl-convert - (let ((ref (binding-to-globalref (cadr e)))) - (if ref - (begin - (put! globals ref #t) - `(block - (toplevel-only set_binding_type! ,(cadr e)) - (call (core set_binding_type!) ,(cadr ref) (inert ,(caddr ref)) ,(caddr e)))) - `(call (core typeassert) ,@(cdr e)))) - fname lam namemap defined toplevel interp opaq globals)))) + (let ((ref (binding-to-globalref (cadr e)))) + (if ref + (begin + (put! globals ref #t) + `(block + (toplevel-only set_binding_type! ,(cadr e)) + (call (core set_binding_type!) ,(cadr ref) (inert ,(caddr ref)) ,(caddr e)))) + `(call (core typeassert) ,@(cdr e)))) + fname lam namemap defined toplevel interp opaq globals locals)))) ;; `with-static-parameters` expressions can be removed now; used only by analyze-vars ((with-static-parameters) - (cl-convert (cadr e) fname lam namemap defined toplevel interp opaq globals)) + (cl-convert (cadr e) fname lam namemap defined toplevel interp opaq globals locals)) (else (cons (car e) - (map-cl-convert (cdr e) fname lam namemap defined toplevel interp opaq globals)))))))) + (map-cl-convert (cdr e) fname lam namemap defined toplevel interp opaq globals locals)))))))) (define (closure-convert e) (cl-convert e #f #f (table) (table) #f #f #f)) @@ -4257,6 +4261,7 @@ f(x) = yt(x) (current-loc #f) (rett #f) (global-const-error #f) + (vinfo-table (vinfo-to-table (car (lam:vinfo lam)))) (arg-map #f) ;; map arguments to new names if they are assigned (label-counter 0) ;; counter for generating label addresses (label-map (table)) ;; maps label names to generated addresses @@ -4734,7 +4739,7 @@ f(x) = yt(x) ;; avoid duplicate newvar nodes (if (and (not (and (pair? code) (equal? (car code) e))) ;; exclude deleted vars - (assq (cadr e) (car (lam:vinfo lam)))) + (has? vinfo-table (cadr e))) (emit e) #f)) ((global) ; keep global declarations as statements From b03e8ab9c7bd3e001add519571858fa04d6a249b Mon Sep 17 00:00:00 2001 From: Lilith Hafner Date: Fri, 17 Feb 2023 14:02:14 -0600 Subject: [PATCH 653/775] replace tabs with spaces and fix indentation --- HISTORY.md | 24 ++++++++--------- base/arrayshow.jl | 2 +- base/compiler/typeinfer.jl | 2 +- doc/src/devdocs/probes.md | 16 +++++------ src/jitlayers.cpp | 46 ++++++++++++++++---------------- src/julia_atomics.h | 4 +-- src/signals-unix.c | 6 ++--- stdlib/Markdown/test/runtests.jl | 2 +- stdlib/Test/docs/src/index.md | 20 +++++++------- test/testhelpers/FillArrays.jl | 4 +-- 10 files changed, 63 insertions(+), 63 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 09143b63263ec..935b203ffaa97 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -5564,18 +5564,18 @@ Deprecated or removed * several syntax whitespace insensitivities have been deprecated ([#11891]). ```julia - # function call - f (x) - - # getindex - x [17] - rand(2) [1] - - # function definition - f (x) = x^2 - function foo (x) - x^2 - end + # function call + f (x) + + # getindex + x [17] + rand(2) [1] + + # function definition + f (x) = x^2 + function foo (x) + x^2 + end ``` * indexing with `Real`s that are not subtypes of `Integer` (`Rational`, `AbstractFloat`, etc.) has been deprecated ([#10458]). diff --git a/base/arrayshow.jl b/base/arrayshow.jl index e600e6281bd15..a05a8d4dac51c 100644 --- a/base/arrayshow.jl +++ b/base/arrayshow.jl @@ -202,7 +202,7 @@ function _print_matrix(io, @nospecialize(X::AbstractVecOrMat), pre, sep, post, h if n > maxpossiblecols colsA = [colsA[(0:maxpossiblecols-1) .+ firstindex(colsA)]; colsA[(end-maxpossiblecols+1):end]] else - colsA = [colsA;] + colsA = [colsA;] end A = alignment(io, X, rowsA, colsA, screenwidth, screenwidth, sepsize, ncols) # Nine-slicing is accomplished using print_matrix_row repeatedly diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 7b69b8c248c1a..180c10d8340ad 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -332,7 +332,7 @@ function CodeInstance(interp::AbstractInterpreter, result::InferenceResult, widenconst(result_type), rettype_const, inferred_result, const_flags, first(valid_worlds), last(valid_worlds), # TODO: Actually do something with non-IPO effects - encode_effects(result.ipo_effects), encode_effects(result.ipo_effects), result.argescapes, + encode_effects(result.ipo_effects), encode_effects(result.ipo_effects), result.argescapes, relocatability) end diff --git a/doc/src/devdocs/probes.md b/doc/src/devdocs/probes.md index 5cfd9f6a762f8..d15723e945462 100644 --- a/doc/src/devdocs/probes.md +++ b/doc/src/devdocs/probes.md @@ -27,28 +27,28 @@ to enable USDT probes. > readelf -n usr/lib/libjulia-internal.so.1 Displaying notes found in: .note.gnu.build-id - Owner Data size Description - GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) + Owner Data size Description + GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) Build ID: 57161002f35548772a87418d2385c284ceb3ead8 Displaying notes found in: .note.stapsdt - Owner Data size Description - stapsdt 0x00000029 NT_STAPSDT (SystemTap probe descriptors) + Owner Data size Description + stapsdt 0x00000029 NT_STAPSDT (SystemTap probe descriptors) Provider: julia Name: gc__begin Location: 0x000000000013213e, Base: 0x00000000002bb4da, Semaphore: 0x0000000000346cac Arguments: - stapsdt 0x00000032 NT_STAPSDT (SystemTap probe descriptors) + stapsdt 0x00000032 NT_STAPSDT (SystemTap probe descriptors) Provider: julia Name: gc__stop_the_world Location: 0x0000000000132144, Base: 0x00000000002bb4da, Semaphore: 0x0000000000346cae Arguments: - stapsdt 0x00000027 NT_STAPSDT (SystemTap probe descriptors) + stapsdt 0x00000027 NT_STAPSDT (SystemTap probe descriptors) Provider: julia Name: gc__end Location: 0x000000000013214a, Base: 0x00000000002bb4da, Semaphore: 0x0000000000346cb0 Arguments: - stapsdt 0x0000002d NT_STAPSDT (SystemTap probe descriptors) + stapsdt 0x0000002d NT_STAPSDT (SystemTap probe descriptors) Provider: julia Name: gc__finalizer Location: 0x0000000000132150, Base: 0x00000000002bb4da, Semaphore: 0x0000000000346cb2 @@ -308,7 +308,7 @@ An example probe in the bpftrace format looks like: ``` usdt:usr/lib/libjulia-internal.so:julia:gc__begin { - @start[pid] = nsecs; + @start[pid] = nsecs; } ``` diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 9dad5cf509f0e..c6aef2d35839c 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -94,33 +94,33 @@ extern "C" { enum class MSanTLS { - param = 1, // __msan_param_tls - param_origin, //__msan_param_origin_tls - retval, // __msan_retval_tls - retval_origin, //__msan_retval_origin_tls - va_arg, // __msan_va_arg_tls - va_arg_origin, // __msan_va_arg_origin_tls - va_arg_overflow_size, // __msan_va_arg_overflow_size_tls - origin, //__msan_origin_tls + param = 1, // __msan_param_tls + param_origin, //__msan_param_origin_tls + retval, // __msan_retval_tls + retval_origin, //__msan_retval_origin_tls + va_arg, // __msan_va_arg_tls + va_arg_origin, // __msan_va_arg_origin_tls + va_arg_overflow_size, // __msan_va_arg_overflow_size_tls + origin, //__msan_origin_tls }; static void *getTLSAddress(void *control) { - auto tlsIndex = static_cast(reinterpret_cast(control)); - switch(tlsIndex) - { - case MSanTLS::param: return reinterpret_cast(&__msan_param_tls); - case MSanTLS::param_origin: return reinterpret_cast(&__msan_param_origin_tls); - case MSanTLS::retval: return reinterpret_cast(&__msan_retval_tls); - case MSanTLS::retval_origin: return reinterpret_cast(&__msan_retval_origin_tls); - case MSanTLS::va_arg: return reinterpret_cast(&__msan_va_arg_tls); - case MSanTLS::va_arg_origin: return reinterpret_cast(&__msan_va_arg_origin_tls); - case MSanTLS::va_arg_overflow_size: return reinterpret_cast(&__msan_va_arg_overflow_size_tls); - case MSanTLS::origin: return reinterpret_cast(&__msan_origin_tls); - default: - assert(false && "BAD MSAN TLS INDEX"); - return nullptr; - } + auto tlsIndex = static_cast(reinterpret_cast(control)); + switch(tlsIndex) + { + case MSanTLS::param: return reinterpret_cast(&__msan_param_tls); + case MSanTLS::param_origin: return reinterpret_cast(&__msan_param_origin_tls); + case MSanTLS::retval: return reinterpret_cast(&__msan_retval_tls); + case MSanTLS::retval_origin: return reinterpret_cast(&__msan_retval_origin_tls); + case MSanTLS::va_arg: return reinterpret_cast(&__msan_va_arg_tls); + case MSanTLS::va_arg_origin: return reinterpret_cast(&__msan_va_arg_origin_tls); + case MSanTLS::va_arg_overflow_size: return reinterpret_cast(&__msan_va_arg_overflow_size_tls); + case MSanTLS::origin: return reinterpret_cast(&__msan_origin_tls); + default: + assert(false && "BAD MSAN TLS INDEX"); + return nullptr; + } } } #endif diff --git a/src/julia_atomics.h b/src/julia_atomics.h index 959491f1ac048..c4488f774c987 100644 --- a/src/julia_atomics.h +++ b/src/julia_atomics.h @@ -79,8 +79,8 @@ enum jl_memory_order { * `mfence`. GCC 11 did switch to this representation. See #48123 */ #if defined(_CPU_X86_64_) && \ - ((defined(__GNUC__) && __GNUC__ < 11) || \ - (defined(__clang__))) + ((defined(__GNUC__) && __GNUC__ < 11) || \ + (defined(__clang__))) #define jl_fence() __asm__ volatile("lock orq $0 , (%rsp)") #else #define jl_fence() atomic_thread_fence(memory_order_seq_cst) diff --git a/src/signals-unix.c b/src/signals-unix.c index 2858538372722..4c21d25d3622c 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -299,8 +299,8 @@ int is_write_fault(void *context) { } #elif defined(_OS_LINUX_) && defined(_CPU_AARCH64_) struct linux_aarch64_ctx_header { - uint32_t magic; - uint32_t size; + uint32_t magic; + uint32_t size; }; const uint32_t linux_esr_magic = 0x45535201; @@ -767,7 +767,7 @@ static void *signal_listener(void *arg) profile = (sig == SIGUSR1); #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L if (profile && !(info.si_code == SI_TIMER && - info.si_value.sival_ptr == &timerprof)) + info.si_value.sival_ptr == &timerprof)) profile = 0; #endif #endif diff --git a/stdlib/Markdown/test/runtests.jl b/stdlib/Markdown/test/runtests.jl index 1b2255a104ef0..19d821a0254d7 100644 --- a/stdlib/Markdown/test/runtests.jl +++ b/stdlib/Markdown/test/runtests.jl @@ -1149,7 +1149,7 @@ end # issue 20225, check this can print @test typeof(sprint(Markdown.term, Markdown.parse(" "))) == String -# different output depending on whether color is requested: +# issue 20225, check this can print +# different output depending on whether color is requested: +# issue 20225, check this can print let buf = IOBuffer() @test typeof(sprint(Markdown.term, Markdown.parse(" "))) == String show(buf, "text/plain", md"*emph*") diff --git a/stdlib/Test/docs/src/index.md b/stdlib/Test/docs/src/index.md index c865891daf7c8..1c9a55480d2c9 100644 --- a/stdlib/Test/docs/src/index.md +++ b/stdlib/Test/docs/src/index.md @@ -405,13 +405,13 @@ using Test @testset "Example tests" begin - @testset "Math tests" begin - include("math_tests.jl") - end + @testset "Math tests" begin + include("math_tests.jl") + end - @testset "Greeting tests" begin - include("greeting_tests.jl") - end + @testset "Greeting tests" begin + include("greeting_tests.jl") + end end ``` @@ -426,16 +426,16 @@ Using our knowledge of `Test.jl`, here are some example tests we could add to `m ```julia @testset "Testset 1" begin - @test 2 == simple_add(1, 1) - @test 3.5 == simple_add(1, 2.5) + @test 2 == simple_add(1, 1) + @test 3.5 == simple_add(1, 2.5) @test_throws MethodError simple_add(1, "A") @test_throws MethodError simple_add(1, 2, 3) end @testset "Testset 2" begin - @test 1.0 == type_multiply(1.0, 1.0) + @test 1.0 == type_multiply(1.0, 1.0) @test isa(type_multiply(2.0, 2.0), Float64) - @test_throws MethodError type_multiply(1, 2.5) + @test_throws MethodError type_multiply(1, 2.5) end ``` diff --git a/test/testhelpers/FillArrays.jl b/test/testhelpers/FillArrays.jl index c0f91b2d0fa1d..1f36a77bf8c12 100644 --- a/test/testhelpers/FillArrays.jl +++ b/test/testhelpers/FillArrays.jl @@ -12,8 +12,8 @@ Base.size(F::Fill) = F.size @inline getindex_value(F::Fill) = F.value @inline function Base.getindex(F::Fill{<:Any,N}, i::Vararg{Int,N}) where {N} - @boundscheck checkbounds(F, i...) - getindex_value(F) + @boundscheck checkbounds(F, i...) + getindex_value(F) end @inline function Base.setindex!(F::Fill, v, k::Integer) From 36f682697689335b98d47d28e3a3c0bf4edab085 Mon Sep 17 00:00:00 2001 From: Lilith Hafner Date: Fri, 17 Feb 2023 14:01:55 -0600 Subject: [PATCH 654/775] Prohibit tabs in whitespace check --- contrib/check-whitespace.jl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/contrib/check-whitespace.jl b/contrib/check-whitespace.jl index a000370026eae..d5473ab4c7c62 100755 --- a/contrib/check-whitespace.jl +++ b/contrib/check-whitespace.jl @@ -18,6 +18,16 @@ const patterns = split(""" *Makefile """) +allow_tabs(path) = + path == "Make.inc" || + endswith(path, "Makefile") || + endswith(path, ".make") || + endswith(path, ".mk") || + startswith(path, joinpath("src", "support")) || + startswith(path, joinpath("src", "flisp")) || + endswith(path, joinpath("test", "syntax.jl")) || + endswith(path, joinpath("test", "triplequote.jl")) + const errors = Set{Tuple{String,Int,String}}() for path in eachline(`git ls-files -- $patterns`) @@ -32,6 +42,8 @@ for path in eachline(`git ls-files -- $patterns`) lineno += 1 contains(line, '\r') && file_err("non-UNIX line endings") contains(line, '\ua0') && line_err("non-breaking space") + allow_tabs(path) || + contains(line, '\t') && line_err("tab") endswith(line, '\n') || line_err("no trailing newline") line = chomp(line) endswith(line, r"\s") && line_err("trailing whitespace") From b0db5009704d8ed03447c4da4b71448eb6d9f708 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Thu, 4 May 2023 06:37:52 -0400 Subject: [PATCH 655/775] Hash values are not stable across different Julia invokations (#48408) --- base/hashing.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/base/hashing.jl b/base/hashing.jl index d47d88eedabd6..1aadd8b7e46a9 100644 --- a/base/hashing.jl +++ b/base/hashing.jl @@ -15,6 +15,8 @@ Typically, any type that implements `hash` should also implement its own [`==`]( (operator `-`) should also implement [`widen`](@ref), which is required to hash values inside heterogeneous arrays. +The hash value may change when a new Julia process is started. + ```jldoctest julia> a = hash(10) 0x95ea2955abd45275 From ce2c3ae73a45124c853401dce3839b61e94802c0 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 4 May 2023 12:45:22 +0200 Subject: [PATCH 656/775] Base: TwicePrecision: improve constructors (#49616) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correctness is improved for constructing `TwicePrecision{T}` from `TwicePrecision{S}`. Previously a call like `TwicePrecision{Float64}(::TwicePrecision{Float32})` would leave `hi` and `lo` unnormalized; while a call like `TwicePrecision{Float32}(::TwicePrecision{Float64})` would additionally introduce unnecessary truncation. Accuracy is improved for constructing `TwicePrecision` from types like `BigFloat` or `Rational`. Previously a call like `TwicePrecision{Float64}(::BigFloat)` would unconditionally leave `lo` as zero. Example code that tests the improvements (all Boolean values should evaluate to `true`): ```julia const TwicePrecision = Base.TwicePrecision const canonicalize2 = Base.canonicalize2 function twiceprecision_roundtrip_is_not_lossy( ::Type{S}, x::T, ) where {S<:Number, T<:Union{Number,TwicePrecision}} tw = TwicePrecision{S}(x) x == T(tw) end function twiceprecision_is_normalized(tw::Tw) where {Tw<:TwicePrecision} (hi, lo) = (tw.hi, tw.lo) normalized = Tw(canonicalize2(hi, lo)...) (abs(lo) ≤ abs(hi)) & (tw == normalized) end rand_twiceprecision(::Type{T}) where {T<:Number} = TwicePrecision{T}(rand(widen(T))) rand_twiceprecision_is_ok(::Type{T}) where {T<:Number} = !iszero(rand_twiceprecision(T).lo) setprecision(BigFloat, 70) # The mantissa needs to be just a bit larger than for `Float64` rand_twiceprecision_is_ok(Float32) rand_twiceprecision_is_ok(Float32) rand_twiceprecision_is_ok(Float32) rand_twiceprecision_is_ok(Float64) rand_twiceprecision_is_ok(Float64) rand_twiceprecision_is_ok(Float64) twiceprecision_roundtrip_is_not_lossy(Float64, rand(BigFloat)) twiceprecision_roundtrip_is_not_lossy(Float64, rand(BigFloat)) twiceprecision_roundtrip_is_not_lossy(Float64, rand(BigFloat)) twiceprecision_roundtrip_is_not_lossy(Float64, rand_twiceprecision(Float32)) twiceprecision_roundtrip_is_not_lossy(Float64, rand_twiceprecision(Float32)) twiceprecision_roundtrip_is_not_lossy(Float64, rand_twiceprecision(Float32)) twiceprecision_is_normalized(TwicePrecision{Float32}(rand_twiceprecision(Float64))) twiceprecision_is_normalized(TwicePrecision{Float32}(rand_twiceprecision(Float64))) twiceprecision_is_normalized(TwicePrecision{Float32}(rand_twiceprecision(Float64))) twiceprecision_is_normalized(TwicePrecision{Float64}(rand_twiceprecision(Float32))) twiceprecision_is_normalized(TwicePrecision{Float64}(rand_twiceprecision(Float32))) twiceprecision_is_normalized(TwicePrecision{Float64}(rand_twiceprecision(Float32))) ``` Updates #49589 --- base/twiceprecision.jl | 11 ++++------- test/ranges.jl | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index b6d702d9242b8..8d5650aace366 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -200,16 +200,14 @@ end TwicePrecision{T}(x::T) where {T} = TwicePrecision{T}(x, zero(T)) +TwicePrecision{T}(x::TwicePrecision{T}) where {T} = x + function TwicePrecision{T}(x) where {T} - xT = convert(T, x) + xT = T(x) Δx = x - xT TwicePrecision{T}(xT, T(Δx)) end -function TwicePrecision{T}(x::TwicePrecision) where {T} - TwicePrecision{T}(x.hi, x.lo) -end - TwicePrecision{T}(i::Integer) where {T<:AbstractFloat} = TwicePrecision{T}(canonicalize2(splitprec(T, i)...)...) @@ -264,8 +262,7 @@ promote_rule(::Type{TwicePrecision{R}}, ::Type{TwicePrecision{S}}) where {R,S} = promote_rule(::Type{TwicePrecision{R}}, ::Type{S}) where {R,S<:Number} = TwicePrecision{promote_type(R,S)} -(::Type{T})(x::TwicePrecision) where {T<:Number} = T(x.hi + x.lo)::T -TwicePrecision{T}(x::Number) where {T} = TwicePrecision{T}(T(x), zero(T)) +(::Type{T})(x::TwicePrecision) where {T<:Number} = (T(x.hi) + T(x.lo))::T convert(::Type{TwicePrecision{T}}, x::TwicePrecision{T}) where {T} = x convert(::Type{TwicePrecision{T}}, x::TwicePrecision) where {T} = diff --git a/test/ranges.jl b/test/ranges.jl index 04c2cdcb47ac4..2f99f4f0c906f 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -255,6 +255,45 @@ end @test x.hi/2 === PhysQuantity{1}(2.0) @test_throws ErrorException("Int is incommensurate with PhysQuantity") x/2 @test zero(typeof(x)) === Base.TwicePrecision(PhysQuantity{1}(0.0)) + + function twiceprecision_roundtrip_is_not_lossy( + ::Type{S}, + x::T, + ) where {S<:Number, T<:Union{Number,Base.TwicePrecision}} + tw = Base.TwicePrecision{S}(x) + @test x == T(tw) + end + + function twiceprecision_is_normalized(tw::Tw) where {Tw<:Base.TwicePrecision} + (hi, lo) = (tw.hi, tw.lo) + normalized = Tw(Base.canonicalize2(hi, lo)...) + @test (abs(lo) ≤ abs(hi)) & (tw == normalized) + end + + rand_twiceprecision(::Type{T}) where {T<:Number} = Base.TwicePrecision{T}(rand(widen(T))) + + rand_twiceprecision_is_ok(::Type{T}) where {T<:Number} = @test !iszero(rand_twiceprecision(T).lo) + + # For this test the `BigFloat` mantissa needs to be just a bit + # larger than the `Float64` mantissa + setprecision(BigFloat, 70) do + n = 10 + @testset "rand twiceprecision is ok" for T ∈ (Float32, Float64), i ∈ 1:n + rand_twiceprecision_is_ok(T) + end + @testset "twiceprecision roundtrip is not lossy 1" for i ∈ 1:n + twiceprecision_roundtrip_is_not_lossy(Float64, rand(BigFloat)) + end + @testset "twiceprecision roundtrip is not lossy 2" for i ∈ 1:n + twiceprecision_roundtrip_is_not_lossy(Float64, rand_twiceprecision(Float32)) + end + @testset "twiceprecision normalization 1: Float64 to Float32" for i ∈ 1:n + twiceprecision_is_normalized(Base.TwicePrecision{Float32}(rand_twiceprecision(Float64))) + end + @testset "twiceprecision normalization 2: Float32 to Float64" for i ∈ 1:n + twiceprecision_is_normalized(Base.TwicePrecision{Float64}(rand_twiceprecision(Float32))) + end + end end @testset "ranges" begin @test size(10:1:0) == (0,) From c90aa7705a7edf09af74ed1372d59fc27508f546 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 4 May 2023 12:48:48 +0200 Subject: [PATCH 657/775] Base: twiceprecision: optimize mul12 (#49568) It is well known and obvious that the algorithm behind `Base.mul12` (sometimes known as "Fast2Mult") doesn't require a "Fast2Sum" (known in the Julia codebase as `Base.canonicalize2`) call at the end, so remove it. This follows from the fact that IEEE-754 floating-point multiplication is required to be well-rounded, so Fast2Sum can't change the result. Reference, for example, the beginning of https://doi.org/10.1145/3121432 by Joldes, Muller, Popescu. Furthermore, `Base.Math.two_mul` already exists, so use it as a kernel function. This required adding a general fallback method for `Base.Math.two_mul`, required, for example, for `BigFloat`. Also removed the `iszero` check that is now obviously not necessary. --- base/math.jl | 5 +++++ base/twiceprecision.jl | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/base/math.jl b/base/math.jl index f42ecf3d0ee7e..71bd4949498b5 100644 --- a/base/math.jl +++ b/base/math.jl @@ -46,6 +46,11 @@ end # non-type specific math functions +function two_mul(x::T, y::T) where {T<:Number} + xy = x*y + xy, fma(x, y, -xy) +end + @assume_effects :consistent @inline function two_mul(x::Float64, y::Float64) if Core.Intrinsics.have_fma(Float64) xy = x*y diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index 8d5650aace366..736261e09792a 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -112,8 +112,8 @@ julia> Float64(hi) + Float64(lo) ``` """ function mul12(x::T, y::T) where {T<:AbstractFloat} - h = x * y - ifelse(iszero(h) | !isfinite(h), (h, h), canonicalize2(h, fma(x, y, -h))) + (h, l) = Base.Math.two_mul(x, y) + ifelse(!isfinite(h), (h, h), (h, l)) end mul12(x::T, y::T) where {T} = (p = x * y; (p, zero(p))) mul12(x, y) = mul12(promote(x, y)...) From 705bfd32d7102d1040053be7f133770b748f56fc Mon Sep 17 00:00:00 2001 From: kamesy <58350382+kamesy@users.noreply.github.com> Date: Thu, 4 May 2023 03:55:39 -0700 Subject: [PATCH 658/775] Remove unneeded allocations in `ldiv!(::QRPivoted, ...)` (#49282) --- stdlib/LinearAlgebra/src/qr.jl | 103 +++++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/stdlib/LinearAlgebra/src/qr.jl b/stdlib/LinearAlgebra/src/qr.jl index d2420fb78edef..4e3baa7ca91c4 100644 --- a/stdlib/LinearAlgebra/src/qr.jl +++ b/stdlib/LinearAlgebra/src/qr.jl @@ -531,45 +531,90 @@ end # Julia implementation similar to xgelsy function ldiv!(A::QRPivoted{T,<:StridedMatrix}, B::AbstractMatrix{T}, rcond::Real) where {T<:BlasFloat} - mA, nA = size(A.factors) - nr = min(mA,nA) - nrhs = size(B, 2) - if nr == 0 - return B, 0 + require_one_based_indexing(B) + m, n = size(A) + + if m > size(B, 1) || n > size(B, 1) + throw(DimensionMismatch("B has leading dimension $(size(B, 1)) but needs at least $(max(m, n))")) end - ar = abs(A.factors[1]) - if ar == 0 - B[1:nA, :] .= 0 + + if length(A.factors) == 0 || length(B) == 0 return B, 0 end - rnk = 1 - xmin = T[1] - xmax = T[1] - tmin = tmax = ar - while rnk < nr - tmin, smin, cmin = LAPACK.laic1!(2, xmin, tmin, view(A.factors, 1:rnk, rnk + 1), A.factors[rnk + 1, rnk + 1]) - tmax, smax, cmax = LAPACK.laic1!(1, xmax, tmax, view(A.factors, 1:rnk, rnk + 1), A.factors[rnk + 1, rnk + 1]) - tmax*rcond > tmin && break - push!(xmin, cmin) - push!(xmax, cmax) - for i = 1:rnk - xmin[i] *= smin - xmax[i] *= smax + + @inbounds begin + smin = smax = abs(A.factors[1]) + + if smax == 0 + return fill!(B, 0), 0 + end + + mn = min(m, n) + + # allocate temporary work space + tmp = Vector{T}(undef, 2mn) + wmin = view(tmp, 1:mn) + wmax = view(tmp, mn+1:2mn) + + rnk = 1 + wmin[1] = 1 + wmax[1] = 1 + + while rnk < mn + i = rnk + 1 + + smin, s1, c1 = LAPACK.laic1!(2, view(wmin, 1:rnk), smin, view(A.factors, 1:rnk, i), A.factors[i,i]) + smax, s2, c2 = LAPACK.laic1!(1, view(wmax, 1:rnk), smax, view(A.factors, 1:rnk, i), A.factors[i,i]) + + if smax*rcond > smin + break + end + + for j in 1:rnk + wmin[j] *= s1 + wmax[j] *= s2 + end + wmin[i] = c1 + wmax[i] = c2 + + rnk += 1 + end + + if rnk < n + C, τ = LAPACK.tzrzf!(A.factors[1:rnk, :]) + work = vec(C) + else + C, τ = A.factors, A.τ + work = resize!(tmp, n) + end + + lmul!(adjoint(A.Q), view(B, 1:m, :)) + ldiv!(UpperTriangular(view(C, 1:rnk, 1:rnk)), view(B, 1:rnk, :)) + + if rnk < n + B[rnk+1:n,:] .= zero(T) + LAPACK.ormrz!('L', T <: Complex ? 'C' : 'T', C, τ, view(B, 1:n, :)) + end + + for j in axes(B, 2) + for i in 1:n + work[A.p[i]] = B[i,j] + end + for i in 1:n + B[i,j] = work[i] + end end - rnk += 1 end - C, τ = LAPACK.tzrzf!(A.factors[1:rnk, :]) - lmul!(A.Q', view(B, 1:mA, :)) - ldiv!(UpperTriangular(view(C, :, 1:rnk)), view(B, 1:rnk, :)) - B[rnk+1:end,:] .= zero(T) - LAPACK.ormrz!('L', eltype(B)<:Complex ? 'C' : 'T', C, τ, view(B, 1:nA, :)) - B[A.p,:] = B[1:nA,:] + return B, rnk end + ldiv!(A::QRPivoted{T,<:StridedMatrix}, B::AbstractVector{T}) where {T<:BlasFloat} = vec(ldiv!(A, reshape(B, length(B), 1))) ldiv!(A::QRPivoted{T,<:StridedMatrix}, B::AbstractMatrix{T}) where {T<:BlasFloat} = - ldiv!(A, B, min(size(A)...)*eps(real(float(one(eltype(B))))))[1] + ldiv!(A, B, min(size(A)...)*eps(real(T)))[1] + + function _wide_qr_ldiv!(A::QR{T}, B::AbstractMatrix{T}) where T m, n = size(A) minmn = min(m,n) From daea3c3e4f826e2eeeca98dedb6e9fee2d2739da Mon Sep 17 00:00:00 2001 From: Ed Hagen Date: Thu, 4 May 2023 03:57:49 -0700 Subject: [PATCH 659/775] character literals are delimited with single quotes (#48998) Immediately inform the reader that character literals are delimited with single quotes, not double quotes. --- doc/src/manual/strings.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index 72d1eda644720..fca4fc75d9e0f 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -48,7 +48,7 @@ to a numeric value representing a [Unicode code point](https://en.wikipedia.org/wiki/Code_point). (Julia packages may define other subtypes of `AbstractChar`, e.g. to optimize operations for other [text encodings](https://en.wikipedia.org/wiki/Character_encoding).) Here is how `Char` values are -input and shown: +input and shown (note that character literals are delimited with single quotes, not double quotes): ```jldoctest julia> c = 'x' @@ -156,7 +156,7 @@ julia> 'A' + 1 ## String Basics -String literals are delimited by double quotes or triple double quotes: +String literals are delimited by double quotes or triple double quotes (not single quotes): ```jldoctest helloworldstring julia> str = "Hello, world.\n" From 0e8c0ea26a093c0887a6274036e364493a094a30 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Sun, 23 Apr 2023 09:28:24 -0400 Subject: [PATCH 660/775] Base.Threads: copy `__source__` LineInfo into closure for @spawn/@async This is useful for profilers such as Tracy which need to give a distinguishing name to different tasks. Another useful thing would be to accept an optional `name` parameter in these macros, but that's left for future work. --- base/expr.jl | 20 ++++++++++++++++++++ base/task.jl | 11 ++++++----- base/threadingconstructs.jl | 2 +- src/task.c | 5 +++-- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/base/expr.jl b/base/expr.jl index 457eebc4ea548..c37f1a6482162 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -922,6 +922,26 @@ function remove_linenums!(src::CodeInfo) return src end +replace_linenums!(ex, ln::LineNumberNode) = ex +function replace_linenums!(ex::Expr, ln::LineNumberNode) + if ex.head === :block || ex.head === :quote + # replace line number expressions from metadata (not argument literal or inert) position + map!(ex.args, ex.args) do @nospecialize(x) + isa(x, Expr) && x.head === :line && length(x.args) == 1 && return Expr(:line, ln.line) + isa(x, Expr) && x.head === :line && length(x.args) == 2 && return Expr(:line, ln.line, ln.file) + isa(x, LineNumberNode) && return ln + return x + end + end + # preserve any linenums inside `esc(...)` guards + if ex.head !== :escape + for subex in ex.args + subex isa Expr && replace_linenums!(subex, ln) + end + end + return ex +end + macro generated() return Expr(:generated) end diff --git a/base/task.jl b/base/task.jl index e407cbd62bbd6..876a8cf16ed4c 100644 --- a/base/task.jl +++ b/base/task.jl @@ -131,7 +131,8 @@ true ``` """ macro task(ex) - :(Task(()->$(esc(ex)))) + thunk = Base.replace_linenums!(:(()->$(esc(ex))), __source__) + :(Task($thunk)) end """ @@ -503,15 +504,15 @@ isolating the asynchronous code from changes to the variable's value in the curr Interpolating values via `\$` is available as of Julia 1.4. """ macro async(expr) - do_async_macro(expr) + do_async_macro(expr, __source__) end # generate the code for @async, possibly wrapping the task in something before # pushing it to the wait queue. -function do_async_macro(expr; wrap=identity) +function do_async_macro(expr, linenums; wrap=identity) letargs = Base._lift_one_interp!(expr) - thunk = esc(:(()->($expr))) + thunk = Base.replace_linenums!(:(()->($(esc(expr)))), linenums) var = esc(sync_varname) quote let $(letargs...) @@ -551,7 +552,7 @@ fetch(t::UnwrapTaskFailedException) = unwrap_task_failed(fetch, t) # macro for running async code that doesn't throw wrapped exceptions macro async_unwrap(expr) - do_async_macro(expr, wrap=task->:(Base.UnwrapTaskFailedException($task))) + do_async_macro(expr, __source__, wrap=task->:(Base.UnwrapTaskFailedException($task))) end """ diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl index 5a491a04139db..d150fd3ea1af4 100644 --- a/base/threadingconstructs.jl +++ b/base/threadingconstructs.jl @@ -377,7 +377,7 @@ macro spawn(args...) letargs = Base._lift_one_interp!(ex) - thunk = esc(:(()->($ex))) + thunk = Base.replace_linenums!(:(()->($(esc(ex)))), __source__) var = esc(Base.sync_varname) quote let $(letargs...) diff --git a/src/task.c b/src/task.c index 123cfaac00163..5e59f1d345c55 100644 --- a/src/task.c +++ b/src/task.c @@ -1081,9 +1081,10 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion char *fiber_name; if (start_name[0] == '#') { jl_method_instance_t *mi = jl_method_lookup(&t->start, 1, jl_get_world_counter()); - size_t fiber_name_len = strlen(jl_symbol_name(mi->def.method->file)) + 22; // 22 characters in "Task 65535 (:0000000)\0" + const char *filename = basename(jl_symbol_name(mi->def.method->file)); + size_t fiber_name_len = strlen(filename) + 22; // 22 characters in "Task 65535 (:0000000)\0" fiber_name = (char *)malloc(fiber_name_len); - snprintf(fiber_name, fiber_name_len, "Task %d (%s:%d)", task_id++, jl_symbol_name(mi->def.method->file), mi->def.method->line); + snprintf(fiber_name, fiber_name_len, "Task %d (%s:%d)", task_id++, filename, mi->def.method->line); } else { size_t fiber_name_len = strlen(start_name) + 16; // 16 characters in "Task 65535 (\"\")\0" fiber_name = (char *)malloc(fiber_name_len); From 99e194f3984623a60267d3349dc2b3dda98a8c30 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Sun, 23 Apr 2023 23:57:29 -0400 Subject: [PATCH 661/775] refactor: add `jl_timing_init_task` This provides a generic entrypoint to initialize any task-specific handles or metadata for profiling back-ends. This change also adds the Module name to the Task name. --- src/task.c | 26 +------------------------- src/timing.c | 36 ++++++++++++++++++++++++++++++++++++ src/timing.h | 4 ++++ 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/task.c b/src/task.c index 5e59f1d345c55..9678cf2f3fe4e 100644 --- a/src/task.c +++ b/src/task.c @@ -1068,31 +1068,6 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion t->start = start; t->result = jl_nothing; t->donenotify = completion_future; -#ifdef USE_TRACY - jl_value_t *start_type = jl_typeof(t->start); - const char *start_name = ""; - if (jl_is_datatype(start_type)) - start_name = jl_symbol_name(((jl_datatype_t *) start_type)->name->name); - - static uint16_t task_id = 1; - - // XXX: Tracy uses this as a handle internally and requires that this - // string live forever, so this allocation is intentionally leaked. - char *fiber_name; - if (start_name[0] == '#') { - jl_method_instance_t *mi = jl_method_lookup(&t->start, 1, jl_get_world_counter()); - const char *filename = basename(jl_symbol_name(mi->def.method->file)); - size_t fiber_name_len = strlen(filename) + 22; // 22 characters in "Task 65535 (:0000000)\0" - fiber_name = (char *)malloc(fiber_name_len); - snprintf(fiber_name, fiber_name_len, "Task %d (%s:%d)", task_id++, filename, mi->def.method->line); - } else { - size_t fiber_name_len = strlen(start_name) + 16; // 16 characters in "Task 65535 (\"\")\0" - fiber_name = (char *)malloc(fiber_name_len); - snprintf(fiber_name, fiber_name_len, "Task %d (\"%s\")", task_id++, start_name); - } - - t->name = fiber_name; -#endif jl_atomic_store_relaxed(&t->_isexception, 0); // Inherit logger state from parent task t->logstate = ct->logstate; @@ -1110,6 +1085,7 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion t->ptls = NULL; t->world_age = ct->world_age; t->reentrant_timing = 0; + jl_timing_init_task(t); #ifdef COPY_STACKS if (!t->copy_stack) { diff --git a/src/timing.c b/src/timing.c index c62b14b2c16c8..f0b48feb41d30 100644 --- a/src/timing.c +++ b/src/timing.c @@ -240,6 +240,42 @@ JL_DLLEXPORT void jl_timing_puts(jl_timing_block_t *cur_block, const char *str) #endif } +void jl_timing_init_task(jl_task_t *t) +{ +#ifdef USE_TRACY + jl_value_t *start_type = jl_typeof(t->start); + const char *start_name = ""; + if (jl_is_datatype(start_type)) + start_name = jl_symbol_name(((jl_datatype_t *) start_type)->name->name); + + static uint16_t task_id = 1; + + // XXX: Tracy uses this as a handle internally and requires that this + // string live forever, so this allocation is intentionally leaked. + char *fiber_name; + if (start_name[0] == '#') { + jl_method_instance_t *mi = jl_method_lookup(&t->start, 1, jl_get_world_counter()); + const char *filename = gnu_basename(jl_symbol_name(mi->def.method->file)); + const char *module_name = jl_symbol_name(mi->def.method->module->name); + + // 26 characters in "Task 65535 (:0000000 in )\0" + size_t fiber_name_len = strlen(filename) + strlen(module_name) + 26; + fiber_name = (char *)malloc(fiber_name_len); + snprintf(fiber_name, fiber_name_len, "Task %d (%s:%d in %s)", + task_id++, filename, mi->def.method->line, module_name); + } else { + + // 16 characters in "Task 65535 (\"\")\0" + size_t fiber_name_len = strlen(start_name) + 16; + fiber_name = (char *)malloc(fiber_name_len); + snprintf(fiber_name, fiber_name_len, "Task %d (\"%s\")", + task_id++, start_name); + } + + t->name = fiber_name; +#endif +} + JL_DLLEXPORT int jl_timing_set_enable(const char *subsystem, uint8_t enabled) { for (int i = 0; i < JL_TIMING_LAST; i++) { diff --git a/src/timing.h b/src/timing.h index 24c120b388c01..5d4e95c62bb67 100644 --- a/src/timing.h +++ b/src/timing.h @@ -64,6 +64,7 @@ extern uint32_t jl_timing_print_limit; #define JL_TIMING(subsystem, event) #define JL_TIMING_SUSPEND(subsystem, ct) + #define jl_timing_show(v, b) #define jl_timing_show_module(m, b) #define jl_timing_show_filename(f, b) @@ -72,6 +73,7 @@ extern uint32_t jl_timing_print_limit; #define jl_timing_show_func_sig(tt, b) #define jl_timing_printf(b, f, ...) #define jl_timing_puts(b, s) +#define jl_timing_init_task(t) #define jl_timing_block_enter_task(ct, ptls, blk) #define jl_timing_block_exit_task(ct, ptls) ((jl_timing_block_t *)NULL) #define jl_pop_timing_block(blk) @@ -98,6 +100,8 @@ extern "C" { #endif void jl_print_timings(void); jl_timing_block_t *jl_pop_timing_block(jl_timing_block_t *cur_block); + +void jl_timing_init_task(jl_task_t *t); void jl_timing_block_enter_task(jl_task_t *ct, jl_ptls_t ptls, jl_timing_block_t *prev_blk); jl_timing_block_t *jl_timing_block_exit_task(jl_task_t *ct, jl_ptls_t ptls); From bb3177205a5abc2f29978611eace3f287b1618dd Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 4 May 2023 07:20:31 -0400 Subject: [PATCH 662/775] make `map` on tuples accept different lengths (#49336) fix #49299, fix #42216 --- base/tuple.jl | 10 ++++++++-- test/tuple.jl | 13 +++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/base/tuple.jl b/base/tuple.jl index dcceaabf12e83..59fe2c1e531e1 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -313,6 +313,8 @@ function map(f, t::Any32) end # 2 argument function map(f, t::Tuple{}, s::Tuple{}) = () +map(f, t::Tuple, s::Tuple{}) = () +map(f, t::Tuple{}, s::Tuple) = () map(f, t::Tuple{Any,}, s::Tuple{Any,}) = (@inline; (f(t[1],s[1]),)) map(f, t::Tuple{Any,Any}, s::Tuple{Any,Any}) = (@inline; (f(t[1],s[1]), f(t[2],s[2]))) function map(f, t::Tuple, s::Tuple) @@ -320,7 +322,7 @@ function map(f, t::Tuple, s::Tuple) (f(t[1],s[1]), map(f, tail(t), tail(s))...) end function map(f, t::Any32, s::Any32) - n = length(t) + n = min(length(t), length(s)) A = Vector{Any}(undef, n) for i = 1:n A[i] = f(t[i], s[i]) @@ -331,12 +333,16 @@ end heads(ts::Tuple...) = map(t -> t[1], ts) tails(ts::Tuple...) = map(tail, ts) map(f, ::Tuple{}...) = () +anyempty(x::Tuple{}, xs...) = true +anyempty(x::Tuple, xs...) = anyempty(xs...) +anyempty() = false function map(f, t1::Tuple, t2::Tuple, ts::Tuple...) @inline + anyempty(t1, t2, ts...) && return () (f(heads(t1, t2, ts...)...), map(f, tails(t1, t2, ts...)...)...) end function map(f, t1::Any32, t2::Any32, ts::Any32...) - n = length(t1) + n = min(length(t1), length(t2), minimum(length, ts)) A = Vector{Any}(undef, n) for i = 1:n A[i] = f(t1[i], t2[i], map(t -> t[i], ts)...) diff --git a/test/tuple.jl b/test/tuple.jl index 9238c6cd6d7a6..71770b6a553c2 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -265,8 +265,10 @@ end @test map(foo, (1,2,3,4), (1,2,3,4)) === (2,4,6,8) @test map(foo, longtuple, longtuple) === ntuple(i->2i,20) @test map(foo, vlongtuple, vlongtuple) === ntuple(i->2i,33) - @test_throws BoundsError map(foo, (), (1,)) - @test_throws BoundsError map(foo, (1,), ()) + @test map(foo, longtuple, vlongtuple) === ntuple(i->2i,20) + @test map(foo, vlongtuple, longtuple) === ntuple(i->2i,20) + @test map(foo, (), (1,)) === () + @test map(foo, (1,), ()) === () end @testset "n arguments" begin @@ -276,8 +278,11 @@ end @test map(foo, (1,2,3,4), (1,2,3,4), (1,2,3,4)) === (3,6,9,12) @test map(foo, longtuple, longtuple, longtuple) === ntuple(i->3i,20) @test map(foo, vlongtuple, vlongtuple, vlongtuple) === ntuple(i->3i,33) - @test_throws BoundsError map(foo, (), (1,), (1,)) - @test_throws BoundsError map(foo, (1,), (1,), ()) + @test map(foo, vlongtuple, longtuple, longtuple) === ntuple(i->3i,20) + @test map(foo, longtuple, vlongtuple, longtuple) === ntuple(i->3i,20) + @test map(foo, longtuple, vlongtuple, vlongtuple) === ntuple(i->3i,20) + @test map(foo, (), (1,), (1,)) === () + @test map(foo, (1,), (1,), ()) === () end end From 724c93dd882f50d6b6c02940de8686d8b478c7c7 Mon Sep 17 00:00:00 2001 From: Taksh Dhabalia <77263611+TakshDhabalia@users.noreply.github.com> Date: Thu, 4 May 2023 17:03:58 +0530 Subject: [PATCH 663/775] Objects.md modified to include outdation warning (#49013) --- doc/src/devdocs/object.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/src/devdocs/object.md b/doc/src/devdocs/object.md index cf377c052bf15..caba6c3f12190 100644 --- a/doc/src/devdocs/object.md +++ b/doc/src/devdocs/object.md @@ -189,6 +189,8 @@ then tagged with its type: jl_value_t *jl_gc_allocobj(size_t nbytes); void jl_set_typeof(jl_value_t *v, jl_datatype_t *type); ``` +!!! note "Out of date Warning" + The documentation and usage for the function `jl_gc_allocobj` may be out of date Note that all objects are allocated in multiples of 4 bytes and aligned to the platform pointer size. Memory is allocated from a pool for smaller objects, or directly with `malloc()` for large From ebc67760d1cc5f908607ed0548bd032f39ae1b02 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Thu, 4 May 2023 07:36:05 -0400 Subject: [PATCH 664/775] (re-factor) gc: Remove redundant `since_sweep` field (#49195) This field is at all times 0 or identical to allocd, so this change removes it in preference of `gc_num.allocd`. Hopefully this makes the GC metrics a bit easier to interpret for newcomers. --- base/timing.jl | 1 - src/gc.c | 16 +++++++--------- src/gc.h | 1 - 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/base/timing.jl b/base/timing.jl index 0afe65227d4a4..3e1f3a3451149 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -12,7 +12,6 @@ struct GC_Num freecall ::Int64 total_time ::Int64 total_allocd ::Int64 # GC internal - since_sweep ::Int64 # GC internal collect ::Csize_t # GC internal pause ::Cint full_sweep ::Cint diff --git a/src/gc.c b/src/gc.c index 1cbcff5b50ab8..eea983b72ab70 100644 --- a/src/gc.c +++ b/src/gc.c @@ -3184,7 +3184,6 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) mark_reset_age = 0; } - gc_num.since_sweep += gc_num.allocd; JL_PROBE_GC_MARK_END(scanned_bytes, perm_scanned_bytes); gc_settime_premark_end(); gc_time_mark_pause(gc_start_time, scanned_bytes, perm_scanned_bytes); @@ -3192,14 +3191,14 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) uint64_t mark_time = end_mark_time - start_mark_time; gc_num.mark_time = mark_time; gc_num.total_mark_time += mark_time; - int64_t actual_allocd = gc_num.since_sweep; + int64_t allocd = gc_num.allocd; gc_settime_postmark_end(); // marking is over // Flush everything in mark cache gc_sync_all_caches_nolock(ptls); - int64_t live_sz_ub = live_bytes + actual_allocd; + int64_t live_sz_ub = live_bytes + allocd; int64_t live_sz_est = scanned_bytes + perm_scanned_bytes; int64_t estimate_freed = live_sz_ub - live_sz_est; @@ -3209,11 +3208,11 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) gc_stats_big_obj(); objprofile_printall(); objprofile_reset(); - gc_num.total_allocd += gc_num.since_sweep; + gc_num.total_allocd += gc_num.allocd; if (!prev_sweep_full) promoted_bytes += perm_scanned_bytes - last_perm_scanned_bytes; // 5. next collection decision - int not_freed_enough = (collection == JL_GC_AUTO) && estimate_freed < (7*(actual_allocd/10)); + int not_freed_enough = (collection == JL_GC_AUTO) && estimate_freed < (7*(allocd/10)); int nptr = 0; assert(gc_n_threads); for (int i = 0; i < gc_n_threads; i++) { @@ -3334,7 +3333,7 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) _report_gc_finished(pause, gc_num.freed, sweep_full, recollect); gc_final_pause_end(gc_start_time, gc_end_time); - gc_time_sweep_pause(gc_end_time, actual_allocd, live_bytes, + gc_time_sweep_pause(gc_end_time, allocd, live_bytes, estimate_freed, sweep_full); gc_num.full_sweep += sweep_full; uint64_t max_memory = last_live_bytes + gc_num.allocd; @@ -3342,9 +3341,8 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) gc_num.max_memory = max_memory; } - gc_num.allocd = 0; last_live_bytes = live_bytes; - live_bytes += -gc_num.freed + gc_num.since_sweep; + live_bytes += -gc_num.freed + gc_num.allocd; if (collection == JL_GC_AUTO) { //If we aren't freeing enough or are seeing lots and lots of pointers let it increase faster @@ -3386,7 +3384,7 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) prev_sweep_full = sweep_full; gc_num.pause += !recollect; gc_num.total_time += pause; - gc_num.since_sweep = 0; + gc_num.allocd = 0; gc_num.freed = 0; if (pause > gc_num.max_pause) { gc_num.max_pause = pause; diff --git a/src/gc.h b/src/gc.h index 3d5328c525e98..eb20dd0ac36f6 100644 --- a/src/gc.h +++ b/src/gc.h @@ -69,7 +69,6 @@ typedef struct { uint64_t freecall; uint64_t total_time; uint64_t total_allocd; - uint64_t since_sweep; size_t interval; int pause; int full_sweep; From 1d361c0a2d362526339fa6b9271e13edd35c8c88 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Thu, 4 May 2023 06:37:28 -0500 Subject: [PATCH 665/775] Remove redundant and unused _extrema in sorting (#48641) Found via coveralls line-by-line coverage. --- base/sort.jl | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/base/sort.jl b/base/sort.jl index 1041c7e4288b3..0e84657fc481e 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -815,7 +815,6 @@ function _sort!(v::AbstractVector, a::ComputeExtrema, o::Ordering, kw) lt(o, vi, mn) && (mn = vi) lt(o, mx, vi) && (mx = vi) end - mn, mx lt(o, mn, mx) || return scratch # all same @@ -1171,15 +1170,6 @@ end maybe_unsigned(x::Integer) = x # this is necessary to avoid calling unsigned on BigInt maybe_unsigned(x::BitSigned) = unsigned(x) -function _extrema(v::AbstractVector, lo::Integer, hi::Integer, o::Ordering) - mn = mx = v[lo] - @inbounds for i in (lo+1):hi - vi = v[i] - lt(o, vi, mn) && (mn = vi) - lt(o, mx, vi) && (mx = vi) - end - mn, mx -end function _issorted(v::AbstractVector, lo::Integer, hi::Integer, o::Ordering) @boundscheck checkbounds(v, lo:hi) @inbounds for i in (lo+1):hi From 5e7d7c3eeb78773f62bdf17267fb59f0d221be9a Mon Sep 17 00:00:00 2001 From: Jakob Nybo Nissen Date: Thu, 4 May 2023 15:15:34 +0200 Subject: [PATCH 666/775] Remove last references to gc_num.since_sweep (#49628) Commit ebc6776 removed the since_sweep field, but left two references to it, which makes Julia fail to build. This commit removes those references. --- src/gc-debug.c | 2 +- src/gc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gc-debug.c b/src/gc-debug.c index ca0cf82c7d581..eeb170f0299a1 100644 --- a/src/gc-debug.c +++ b/src/gc-debug.c @@ -996,7 +996,7 @@ void gc_time_sweep_pause(uint64_t gc_end_t, int64_t actual_allocd, "(%.2f ms in post_mark) %s | next in %" PRId64 " kB\n", jl_ns2ms(sweep_pause), live_bytes / 1024, gc_num.freed / 1024, estimate_freed / 1024, - gc_num.freed - estimate_freed, pct, gc_num.since_sweep / 1024, + gc_num.freed - estimate_freed, pct, gc_num.allocd / 1024, jl_ns2ms(gc_postmark_end - gc_premark_end), sweep_full ? "full" : "quick", -gc_num.allocd / 1024); } diff --git a/src/gc.c b/src/gc.c index eea983b72ab70..60b110826ee80 100644 --- a/src/gc.c +++ b/src/gc.c @@ -3347,7 +3347,7 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) if (collection == JL_GC_AUTO) { //If we aren't freeing enough or are seeing lots and lots of pointers let it increase faster if (!not_freed_enough || large_frontier) { - int64_t tot = 2 * (live_bytes + gc_num.since_sweep) / 3; + int64_t tot = 2 * (live_bytes + gc_num.allocd) / 3; if (gc_num.interval > tot) { gc_num.interval = tot; last_long_collect_interval = tot; From e95416f156c54697a593a1278f082de8352b6b83 Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Mon, 1 May 2023 15:29:37 -0400 Subject: [PATCH 667/775] Close channel task doesn't need to be sticky --- base/channels.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/channels.jl b/base/channels.jl index 33365c03e5d3d..1b5b427f92671 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -253,6 +253,7 @@ Stacktrace: """ function bind(c::Channel, task::Task) T = Task(() -> close_chnl_on_taskdone(task, c)) + T.sticky = false _wait2(task, T) return c end From 9dbfc0505bf947aab6e64dccaafbd2b6aaedf5d5 Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Wed, 3 May 2023 13:16:21 -0400 Subject: [PATCH 668/775] Set task tid before scheduling task in `_wait2` --- base/task.jl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/base/task.jl b/base/task.jl index e407cbd62bbd6..564f18d3b9d29 100644 --- a/base/task.jl +++ b/base/task.jl @@ -317,22 +317,22 @@ end # have `waiter` wait for `t` function _wait2(t::Task, waiter::Task) if !istaskdone(t) + # since _wait2 is similar to schedule, we should observe the sticky + # bit, even if we don't call `schedule` with early-return below + if waiter.sticky && Threads.threadid(waiter) == 0 && !GC.in_finalizer() + # Issue #41324 + # t.sticky && tid == 0 is a task that needs to be co-scheduled with + # the parent task. If the parent (current_task) is not sticky we must + # set it to be sticky. + # XXX: Ideally we would be able to unset this + current_task().sticky = true + tid = Threads.threadid() + ccall(:jl_set_task_tid, Cint, (Any, Cint), waiter, tid-1) + end lock(t.donenotify) if !istaskdone(t) push!(t.donenotify.waitq, waiter) unlock(t.donenotify) - # since _wait2 is similar to schedule, we should observe the sticky - # bit, even if we aren't calling `schedule` due to this early-return - if waiter.sticky && Threads.threadid(waiter) == 0 && !GC.in_finalizer() - # Issue #41324 - # t.sticky && tid == 0 is a task that needs to be co-scheduled with - # the parent task. If the parent (current_task) is not sticky we must - # set it to be sticky. - # XXX: Ideally we would be able to unset this - current_task().sticky = true - tid = Threads.threadid() - ccall(:jl_set_task_tid, Cint, (Any, Cint), waiter, tid-1) - end return nothing else unlock(t.donenotify) From 5032a1af20ad3b5bf9928eb5f9a1486b580402bc Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Thu, 4 May 2023 21:43:33 +0000 Subject: [PATCH 669/775] Initialize hoisted object allocations (#49584) --- src/llvm-julia-licm.cpp | 18 +++++++++++++++++- test/llvmpasses/julia-licm.ll | 8 ++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/llvm-julia-licm.cpp b/src/llvm-julia-licm.cpp index 84d5d614a7293..7bc8d91b525f3 100644 --- a/src/llvm-julia-licm.cpp +++ b/src/llvm-julia-licm.cpp @@ -134,7 +134,6 @@ struct JuliaLICMPassLegacy : public LoopPass { getLoopAnalysisUsage(AU); } }; - struct JuliaLICM : public JuliaPassContext { function_ref GetDT; function_ref GetLI; @@ -339,6 +338,19 @@ struct JuliaLICM : public JuliaPassContext { }); ++HoistedAllocation; moveInstructionBefore(*call, *preheader->getTerminator(), MSSAU, SE); + IRBuilder<> builder(preheader->getTerminator()); + builder.SetCurrentDebugLocation(call->getDebugLoc()); + auto obj_i8 = builder.CreateBitCast(call, Type::getInt8PtrTy(call->getContext(), call->getType()->getPointerAddressSpace())); + // Note that this alignment is assuming the GC allocates at least pointer-aligned memory + auto align = Align(DL.getPointerSize(0)); + auto clear_obj = builder.CreateMemSet(obj_i8, ConstantInt::get(Type::getInt8Ty(call->getContext()), 0), call->getArgOperand(1), align); + if (MSSAU.getMemorySSA()) { + auto alloc_mdef = MSSAU.getMemorySSA()->getMemoryAccess(call); + assert(isa(alloc_mdef) && "Expected alloc to be associated with a memory def!"); + auto clear_mdef = MSSAU.createMemoryAccessAfter(clear_obj, nullptr, alloc_mdef); + assert(isa(clear_mdef) && "Expected memset to be associated with a memory def!"); + (void) clear_mdef; + } changed = true; } } @@ -395,6 +407,10 @@ PreservedAnalyses JuliaLICMPass::run(Loop &L, LoopAnalysisManager &AM, }; auto juliaLICM = JuliaLICM(GetDT, GetLI, GetMSSA, GetSE); if (juliaLICM.runOnLoop(&L, ORE)) { +#ifdef JL_DEBUG_BUILD + if (AR.MSSA) + AR.MSSA->verifyMemorySSA(); +#endif auto preserved = getLoopPassPreservedAnalyses(); preserved.preserveSet(); preserved.preserve(); diff --git a/test/llvmpasses/julia-licm.ll b/test/llvmpasses/julia-licm.ll index dbef009204586..6fc6f85de7c26 100644 --- a/test/llvmpasses/julia-licm.ll +++ b/test/llvmpasses/julia-licm.ll @@ -29,13 +29,15 @@ L4: ; preds = %top %current_task112 = getelementptr inbounds {}**, {}*** %1, i64 -12 %current_task1 = bitcast {}*** %current_task112 to {}** ; CHECK: %3 = call noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}** nonnull %current_task1, i64 8, {} addrspace(10)* @tag) + ; CHECK-NEXT: %4 = bitcast {} addrspace(10)* %3 to i8 addrspace(10)* + ; CHECK-NEXT: call void @llvm.memset.p10i8.i64(i8 addrspace(10)* align {{[0-9]+}} %4, i8 0, i64 8, i1 false) ; CHECK-NEXT: br label %L22 br label %L22 L22: ; preds = %L4, %L22 %value_phi5 = phi i64 [ 1, %L4 ], [ %5, %L22 ] - ; CHECK: %value_phi5 = phi i64 [ 1, %L4 ], [ %5, %L22 ] - ; CHECK-NEXT %4 = bitcast {} addrspace(10)* %3 to i64 addrspace(10)* + ; CHECK: %value_phi5 = phi i64 [ 1, %L4 ], [ %6, %L22 ] + ; CHECK-NEXT %5 = bitcast {} addrspace(10)* %3 to i64 addrspace(10)* %3 = call noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}** nonnull %current_task1, i64 8, {} addrspace(10)* @tag) #1 %4 = bitcast {} addrspace(10)* %3 to i64 addrspace(10)* store i64 %value_phi5, i64 addrspace(10)* %4, align 8, !tbaa !2 @@ -55,6 +57,8 @@ top: ; CHECK: preheader: preheader: ; CHECK-NEXT: %alloc = call noalias nonnull {} addrspace(10)* @julia.gc_alloc_obj({}** nonnull %current_task, i64 8, {} addrspace(10)* @tag) +; CHECK-NEXT: [[casted:%.*]] = bitcast {} addrspace(10)* %alloc to i8 addrspace(10)* +; CHECK-NEXT: call void @llvm.memset.p10i8.i64(i8 addrspace(10)* align {{[0-9]+}} [[casted]], i8 0, i64 8, i1 false) ; CHECK-NEXT: br label %loop br label %loop loop: From c0e12cd6d94776fc8ad72a98c0f9b23b88c9f254 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 5 May 2023 07:49:25 +0900 Subject: [PATCH 670/775] inference: enable `:call` inference in irinterp (#49191) * inference: enable `:call` inference in irinterp Built on top of #48913, this commit enables `:call` inference in irinterp. In a case when some regression is detected, we can simply revert this commit rather than reverting the whole refactoring from #48913. * fix irinterp lattice Now `LimitedAccuracy` can appear in irinterp, so we should include `InferenceLattice` for `[typeinf|ipo]_lattice` for irinterp. --- base/compiler/ssair/irinterp.jl | 13 ++------- base/compiler/types.jl | 8 ++++-- test/compiler/inference.jl | 49 ++++++++++++++++----------------- 3 files changed, 33 insertions(+), 37 deletions(-) diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index a479bb1f99a82..d58d18b188757 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -1,12 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -# TODO (#48913) remove this overload to enable interprocedural call inference from irinterp -function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), - arginfo::ArgInfo, si::StmtInfo, @nospecialize(atype), - sv::IRInterpretationState, max_methods::Int) - return CallMeta(Any, Effects(), NoCallInfo()) -end - function collect_limitations!(@nospecialize(typ), ::IRInterpretationState) @assert !isa(typ, LimitedAccuracy) "irinterp is unable to handle heavy recursion" return typ @@ -147,7 +140,7 @@ function reprocess_instruction!(interp::AbstractInterpreter, idx::Int, bb::Union # Handled at the very end return false elseif isa(inst, PiNode) - rt = tmeet(optimizer_lattice(interp), argextype(inst.val, ir), widenconst(inst.typ)) + rt = tmeet(typeinf_lattice(interp), argextype(inst.val, ir), widenconst(inst.typ)) elseif inst === nothing return false elseif isa(inst, GlobalRef) @@ -155,7 +148,7 @@ function reprocess_instruction!(interp::AbstractInterpreter, idx::Int, bb::Union else error("reprocess_instruction!: unhandled instruction found") end - if rt !== nothing && !⊑(optimizer_lattice(interp), typ, rt) + if rt !== nothing && !⊑(typeinf_lattice(interp), typ, rt) ir.stmts[idx][:type] = rt return true end @@ -323,7 +316,7 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR end inst = ir.stmts[idx][:inst]::ReturnNode rt = argextype(inst.val, ir) - ultimate_rt = tmerge(optimizer_lattice(interp), ultimate_rt, rt) + ultimate_rt = tmerge(typeinf_lattice(interp), ultimate_rt, rt) end end diff --git a/base/compiler/types.jl b/base/compiler/types.jl index c987e03df5261..4a4f27c9c27c2 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -466,8 +466,12 @@ typeinf_lattice(::AbstractInterpreter) = InferenceLattice(BaseInferenceLattice.i ipo_lattice(::AbstractInterpreter) = InferenceLattice(IPOResultLattice.instance) optimizer_lattice(::AbstractInterpreter) = OptimizerLattice(SimpleInferenceLattice.instance) -typeinf_lattice(interp::NativeInterpreter) = interp.irinterp ? optimizer_lattice(interp) : InferenceLattice(BaseInferenceLattice.instance) -ipo_lattice(interp::NativeInterpreter) = interp.irinterp ? optimizer_lattice(interp) : InferenceLattice(IPOResultLattice.instance) +typeinf_lattice(interp::NativeInterpreter) = interp.irinterp ? + OptimizerLattice(InferenceLattice(SimpleInferenceLattice.instance)) : + InferenceLattice(BaseInferenceLattice.instance) +ipo_lattice(interp::NativeInterpreter) = interp.irinterp ? + InferenceLattice(SimpleInferenceLattice.instance) : + InferenceLattice(IPOResultLattice.instance) optimizer_lattice(interp::NativeInterpreter) = OptimizerLattice(SimpleInferenceLattice.instance) """ diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 8e23ca2760241..d25acd4d5a6ad 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -4784,31 +4784,30 @@ fhasmethod(::Integer, ::Int32) = 3 @test only(Base.return_types(()) do; Val(hasmethod(sin, Tuple{Int, Vararg{Int}})); end) == Val{false} @test only(Base.return_types(()) do; Val(hasmethod(sin, Tuple{Int, Int, Vararg{Int}})); end) === Val{false} -# TODO (#48913) enable interprocedural call inference from irinterp -# # interprocedural call inference from irinterp -# @noinline Base.@assume_effects :total issue48679_unknown_any(x) = Base.inferencebarrier(x) - -# @noinline _issue48679(y::Union{Nothing,T}) where {T} = T::Type -# Base.@constprop :aggressive function issue48679(x, b) -# if b -# x = issue48679_unknown_any(x) -# end -# return _issue48679(x) -# end -# @test Base.return_types((Float64,)) do x -# issue48679(x, false) -# end |> only == Type{Float64} - -# Base.@constprop :aggressive @noinline _issue48679_const(b, y::Union{Nothing,T}) where {T} = b ? nothing : T::Type -# Base.@constprop :aggressive function issue48679_const(x, b) -# if b -# x = issue48679_unknown_any(x) -# end -# return _issue48679_const(b, x) -# end -# @test Base.return_types((Float64,)) do x -# issue48679_const(x, false) -# end |> only == Type{Float64} +# interprocedural call inference from irinterp +@noinline Base.@assume_effects :total issue48679_unknown_any(x) = Base.inferencebarrier(x) + +@noinline _issue48679(y::Union{Nothing,T}) where {T} = T::Type +Base.@constprop :aggressive function issue48679(x, b) + if b + x = issue48679_unknown_any(x) + end + return _issue48679(x) +end +@test Base.return_types((Float64,)) do x + issue48679(x, false) +end |> only == Type{Float64} + +Base.@constprop :aggressive @noinline _issue48679_const(b, y::Union{Nothing,T}) where {T} = b ? nothing : T::Type +Base.@constprop :aggressive function issue48679_const(x, b) + if b + x = issue48679_unknown_any(x) + end + return _issue48679_const(b, x) +end +@test Base.return_types((Float64,)) do x + issue48679_const(x, false) +end |> only == Type{Float64} # `invoke` call in irinterp @noinline _irinterp_invoke(x::Any) = :any From ad939df098a58f38c6a2dc9aec5976f098a5e5e5 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 5 May 2023 02:13:55 +0200 Subject: [PATCH 671/775] =?UTF-8?q?Add=20U+297A=20(`=E2=A5=BA`)=20and=20U+?= =?UTF-8?q?2977=20(`=E2=A5=B7`)=20to=20binary=20operators=20(recreated)=20?= =?UTF-8?q?(#49623)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NEWS.md | 3 +++ src/julia-parser.scm | 2 +- stdlib/REPL/src/latex_symbols.jl | 4 +++- test/syntax.jl | 8 ++++++++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index daf97fff71e8f..3514ad955c948 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,9 @@ Julia v1.10 Release Notes New language features --------------------- +* `⥺` (U+297A, `\leftarrowsubset`) and `⥷` (U+2977, `\leftarrowless`) + may now be used as binary operators with arrow precedence. ([#45962]) + Language changes ---------------- diff --git a/src/julia-parser.scm b/src/julia-parser.scm index 16180129881fd..210ba8f0ae07b 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -10,7 +10,7 @@ ;; comma - higher than assignment outside parentheses, lower when inside (define prec-pair (add-dots '(=>))) (define prec-conditional '(?)) -(define prec-arrow (add-dots '(← → ↔ ↚ ↛ ↞ ↠ ↢ ↣ ↦ ↤ ↮ ⇎ ⇍ ⇏ ⇐ ⇒ ⇔ ⇴ ⇶ ⇷ ⇸ ⇹ ⇺ ⇻ ⇼ ⇽ ⇾ ⇿ ⟵ ⟶ ⟷ ⟹ ⟺ ⟻ ⟼ ⟽ ⟾ ⟿ ⤀ ⤁ ⤂ ⤃ ⤄ ⤅ ⤆ ⤇ ⤌ ⤍ ⤎ ⤏ ⤐ ⤑ ⤔ ⤕ ⤖ ⤗ ⤘ ⤝ ⤞ ⤟ ⤠ ⥄ ⥅ ⥆ ⥇ ⥈ ⥊ ⥋ ⥎ ⥐ ⥒ ⥓ ⥖ ⥗ ⥚ ⥛ ⥞ ⥟ ⥢ ⥤ ⥦ ⥧ ⥨ ⥩ ⥪ ⥫ ⥬ ⥭ ⥰ ⧴ ⬱ ⬰ ⬲ ⬳ ⬴ ⬵ ⬶ ⬷ ⬸ ⬹ ⬺ ⬻ ⬼ ⬽ ⬾ ⬿ ⭀ ⭁ ⭂ ⭃ ⭄ ⭇ ⭈ ⭉ ⭊ ⭋ ⭌ ← → ⇜ ⇝ ↜ ↝ ↩ ↪ ↫ ↬ ↼ ↽ ⇀ ⇁ ⇄ ⇆ ⇇ ⇉ ⇋ ⇌ ⇚ ⇛ ⇠ ⇢ ↷ ↶ ↺ ↻ --> <-- <-->))) +(define prec-arrow (add-dots '(← → ↔ ↚ ↛ ↞ ↠ ↢ ↣ ↦ ↤ ↮ ⇎ ⇍ ⇏ ⇐ ⇒ ⇔ ⇴ ⇶ ⇷ ⇸ ⇹ ⇺ ⇻ ⇼ ⇽ ⇾ ⇿ ⟵ ⟶ ⟷ ⟹ ⟺ ⟻ ⟼ ⟽ ⟾ ⟿ ⤀ ⤁ ⤂ ⤃ ⤄ ⤅ ⤆ ⤇ ⤌ ⤍ ⤎ ⤏ ⤐ ⤑ ⤔ ⤕ ⤖ ⤗ ⤘ ⤝ ⤞ ⤟ ⤠ ⥄ ⥅ ⥆ ⥇ ⥈ ⥊ ⥋ ⥎ ⥐ ⥒ ⥓ ⥖ ⥗ ⥚ ⥛ ⥞ ⥟ ⥢ ⥤ ⥦ ⥧ ⥨ ⥩ ⥪ ⥫ ⥬ ⥭ ⥰ ⧴ ⬱ ⬰ ⬲ ⬳ ⬴ ⬵ ⬶ ⬷ ⬸ ⬹ ⬺ ⬻ ⬼ ⬽ ⬾ ⬿ ⭀ ⭁ ⭂ ⭃ ⥷ ⭄ ⥺ ⭇ ⭈ ⭉ ⭊ ⭋ ⭌ ← → ⇜ ⇝ ↜ ↝ ↩ ↪ ↫ ↬ ↼ ↽ ⇀ ⇁ ⇄ ⇆ ⇇ ⇉ ⇋ ⇌ ⇚ ⇛ ⇠ ⇢ ↷ ↶ ↺ ↻ --> <-- <-->))) (define prec-lazy-or (add-dots '(|\|\||))) (define prec-lazy-and (add-dots '(&&))) (define prec-comparison diff --git a/stdlib/REPL/src/latex_symbols.jl b/stdlib/REPL/src/latex_symbols.jl index a184bcbc8e910..3c2be918d6bd2 100644 --- a/stdlib/REPL/src/latex_symbols.jl +++ b/stdlib/REPL/src/latex_symbols.jl @@ -1570,7 +1570,9 @@ const latex_symbols = Dict( "\\bsimilarleftarrow" => "\u2b41", # reverse tilde operator above leftwards arrow "\\leftarrowbackapprox" => "\u2b42", # leftwards arrow above reverse almost equal to "\\rightarrowgtr" => "\u2b43", # rightwards arrow through greater-than - "\\rightarrowsupset" => "\u2b44", # rightwards arrow through subset + "\\leftarrowless" => "\u2977", # leftwards arrow through less-than + "\\rightarrowsupset" => "\u2b44", # rightwards arrow through superset + "\\leftarrowsubset" => "\u297a", # leftwards arrow through subset "\\LLeftarrow" => "\u2b45", # leftwards quadruple arrow "\\RRightarrow" => "\u2b46", # rightwards quadruple arrow "\\bsimilarrightarrow" => "\u2b47", # reverse tilde operator above rightwards arrow diff --git a/test/syntax.jl b/test/syntax.jl index 7778e1ffd60b1..8bba5f9205613 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -2228,6 +2228,14 @@ end @test Meta.parse("a ⫫ b") == Expr(:call, :⫫, :a, :b) end +# issue 45962 +@testset "binary ⭄, ⥺, ⭃, and ⥷" begin + @test Meta.parse("a ⭄ b") == Expr(:call, :⭄, :a, :b) + @test Meta.parse("a ⥺ b") == Expr(:call, :⥺, :a, :b) + @test Meta.parse("a ⭃ b") == Expr(:call, :⭃, :a, :b) + @test Meta.parse("a ⥷ b") == Expr(:call, :⥷, :a, :b) +end + # only allow certain characters after interpolated vars (#25231) @test Meta.parse("\"\$x෴ \"",raise=false) == Expr(:error, "interpolated variable \$x ends with invalid character \"෴\"; use \"\$(x)\" instead.") @test Base.incomplete_tag(Meta.parse("\"\$foo", raise=false)) === :string From ee958432e043d82975ddc02fe670f56d49b25c57 Mon Sep 17 00:00:00 2001 From: Samuel Badr Date: Fri, 5 May 2023 02:16:10 +0200 Subject: [PATCH 672/775] Allow showing of StepRangLen{T} with generic T (#49516) --- base/range.jl | 2 +- test/ranges.jl | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/base/range.jl b/base/range.jl index 80693a8e94a0c..fedc0f4acc310 100644 --- a/base/range.jl +++ b/base/range.jl @@ -1101,7 +1101,7 @@ show(io::IO, r::AbstractRange) = print(io, repr(first(r)), ':', repr(step(r)), ' show(io::IO, r::UnitRange) = print(io, repr(first(r)), ':', repr(last(r))) show(io::IO, r::OneTo) = print(io, "Base.OneTo(", r.stop, ")") function show(io::IO, r::StepRangeLen) - if step(r) != 0 + if !iszero(step(r)) print(io, repr(first(r)), ':', repr(step(r)), ':', repr(last(r))) else # ugly temporary printing, to avoid 0:0:0 etc. diff --git a/test/ranges.jl b/test/ranges.jl index 2f99f4f0c906f..2a7a4d3170d67 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -2441,6 +2441,19 @@ end @test test_firstindex(StepRange{Union{Int64,Int128},Int}(Int64(1), 1, Int128(0))) end +@testset "PR 49516" begin + struct PR49516 <: Signed + n::Int + end + PR49516(f::PR49516) = f + Base.:*(x::Integer, f::PR49516) = PR49516(*(x, f.n)) + Base.:+(f1::PR49516, f2::PR49516) = PR49516(+(f1.n, f2.n)) + Base.show(io::IO, f::PR49516) = print(io, "PR49516(", f.n, ")") + + srl = StepRangeLen(PR49516(1), PR49516(2), 10) + @test sprint(show, srl) == "PR49516(1):PR49516(2):PR49516(19)" +end + @testset "Inline StepRange Construction #49270" begin x = rand(Float32, 80) a = rand(round(Int, length(x) / 2):length(x), 10^6) From ef359e3fb9c8e930c75cbb8ba3f2ffcfd37311d3 Mon Sep 17 00:00:00 2001 From: Benjamin Lorenz Date: Fri, 5 May 2023 08:32:19 +0200 Subject: [PATCH 673/775] don't add cross-compile prefix for CC twice (#49575) * don't add cross-compile prefix twice make reexports changed environment variables to subprocesses Co-authored-by: Jameson Nash --- Make.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Make.inc b/Make.inc index 44addb2ee4a59..07547f1d1aca5 100644 --- a/Make.inc +++ b/Make.inc @@ -458,10 +458,12 @@ endif # Compiler specific stuff +ifeq (default,$(origin CC)) CC := $(CROSS_COMPILE)$(CC) # attempt to add cross-compiler prefix, if the user # is not overriding the default, to form target-triple-cc (which # may not exist), and use that to decide what compiler the user # is using for the target build (or default to gcc) +endif CC_VERSION_STRING = $(shell $(CC) --version 2>/dev/null) ifneq (,$(findstring clang,$(CC_VERSION_STRING))) USECLANG := 1 From 08945aaaee8ea0763b4667bd2749051072906dbb Mon Sep 17 00:00:00 2001 From: James Wrigley Date: Fri, 5 May 2023 08:32:53 +0200 Subject: [PATCH 674/775] Prettify printing of a small number of completions (#49284) Previously show_completions() tended to maximize the number of columns used, and if there were few enough completions to only use one or two lines the text would be a little difficult to read. Now it tries to minimize the number of columns, and there's a minimum number of completions needed before it'll start using multiple columns. Co-authored-by: Kristoffer Carlsson --- stdlib/REPL/src/LineEdit.jl | 12 ++++++++++-- stdlib/REPL/test/lineedit.jl | 30 ++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index f6e8da6fa71d9..ff67e849fcc5a 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -323,6 +323,11 @@ function common_prefix(completions::Vector{String}) end end +# This is the maximum number of completions that will be displayed in a single +# column, anything above that and multiple columns will be used. Note that this +# does not restrict column length when multiple columns are used. +const MULTICOLUMN_THRESHOLD = 5 + # Show available completions function show_completions(s::PromptState, completions::Vector{String}) # skip any lines of input after the cursor @@ -331,9 +336,12 @@ function show_completions(s::PromptState, completions::Vector{String}) if any(Base.Fix1(occursin, '\n'), completions) foreach(Base.Fix1(println, terminal(s)), completions) else - colmax = 2 + maximum(length, completions; init=1) # n.b. length >= textwidth - num_cols = max(div(width(terminal(s)), colmax), 1) n = length(completions) + colmax = 2 + maximum(length, completions; init=1) # n.b. length >= textwidth + + num_cols = min(cld(n, MULTICOLUMN_THRESHOLD), + max(div(width(terminal(s)), colmax), 1)) + entries_per_col = cld(n, num_cols) idx = 0 for _ in 1:entries_per_col diff --git a/stdlib/REPL/test/lineedit.jl b/stdlib/REPL/test/lineedit.jl index e43b9fdb1c3e1..cf87e811508a0 100644 --- a/stdlib/REPL/test/lineedit.jl +++ b/stdlib/REPL/test/lineedit.jl @@ -917,16 +917,26 @@ end @test get_last_word("a[]") == "a[]" end -@testset "issue #45836" begin +@testset "show_completions" begin term = FakeTerminal(IOBuffer(), IOBuffer(), IOBuffer()) - promptstate = REPL.LineEdit.init_state(term, REPL.LineEdit.mode(new_state())) + + function getcompletion(completions) + promptstate = REPL.LineEdit.init_state(term, REPL.LineEdit.mode(new_state())) + REPL.LineEdit.show_completions(promptstate, completions) + return String(take!(term.out_stream)) + end + + # When the number of completions is less than + # LineEdit.MULTICOLUMN_THRESHOLD, they should be in a single column. strings = ["abcdef", "123456", "ijklmn"] - REPL.LineEdit.show_completions(promptstate, strings) - completion = String(take!(term.out_stream)) - @test completion == "\033[0B\n\rabcdef\r\033[8C123456\r\033[16Cijklmn\n" - strings2 = ["abcdef", "123456\nijklmn"] - promptstate = REPL.LineEdit.init_state(term, REPL.LineEdit.mode(new_state())) - REPL.LineEdit.show_completions(promptstate, strings2) - completion2 = String(take!(term.out_stream)) - @test completion2 == "\033[0B\nabcdef\n123456\nijklmn\n" + @assert length(strings) < LineEdit.MULTICOLUMN_THRESHOLD + @test getcompletion(strings) == "\033[0B\n\rabcdef\n\r123456\n\rijklmn\n" + + # But with more than the threshold there should be multiple columns + strings2 = repeat(["foo"], LineEdit.MULTICOLUMN_THRESHOLD + 1) + @test getcompletion(strings2) == "\033[0B\n\rfoo\r\e[5Cfoo\n\rfoo\r\e[5Cfoo\n\rfoo\r\e[5Cfoo\n" + + # Check that newlines in completions are handled correctly (issue #45836) + strings3 = ["abcdef", "123456\nijklmn"] + @test getcompletion(strings3) == "\033[0B\nabcdef\n123456\nijklmn\n" end From dffb9f080c1c79f05fcb17dd3e98e607726da4f8 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 5 May 2023 15:33:20 +0900 Subject: [PATCH 675/775] optimizer: allow inlining of constant `::TypeName` object (#49225) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to inline `Ref.body.name` and such. This doesn't seem to have much impact on sysimage size: > master ``` ➜ ls -la ./usr/lib/julia total 906640 drwxr-xr-x 7 aviatesk staff 224 Apr 2 20:51 . drwxr-xr-x 276 aviatesk staff 8832 Apr 1 11:22 .. -rw-r--r-- 1 aviatesk staff 18344432 Apr 2 20:49 corecompiler.ji -rw-r--r-- 1 aviatesk staff 192771632 Apr 2 20:51 sys-o.a -rwxr-xr-x 1 aviatesk staff 161034648 Apr 2 20:51 sys.dylib drwxr-xr-x 3 aviatesk staff 96 Apr 1 11:26 sys.dylib.dSYM -rw-r--r-- 1 aviatesk staff 92039864 Apr 2 20:50 sys.ji ``` > this commit ``` ➜ ls -la ./usr/lib/julia total 906912 drwxr-xr-x 7 aviatesk staff 224 Apr 2 20:52 . drwxr-xr-x 276 aviatesk staff 8832 Apr 2 20:48 .. -rw-r--r-- 1 aviatesk staff 18329400 Apr 2 20:49 corecompiler.ji -rw-r--r-- 1 aviatesk staff 192790232 Apr 2 20:52 sys-o.a -rwxr-xr-x 1 aviatesk staff 161051240 Apr 2 20:52 sys.dylib drwxr-xr-x 3 aviatesk staff 96 Mar 30 13:18 sys.dylib.dSYM -rw-r--r-- 1 aviatesk staff 92160128 Apr 2 20:50 sys.ji ``` --- base/compiler/abstractinterpretation.jl | 12 +----------- base/compiler/utilities.jl | 2 +- test/compiler/inline.jl | 5 +++++ 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index b094ec460d491..6e671b032dbeb 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1,15 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -############# -# constants # -############# - -const _REF_NAME = Ref.body.name - -######### -# logic # -######### - # See if the inference result of the current statement's result value might affect # the final answer for the method (aside from optimization potential and exceptions). # To do that, we need to check both for slot assignment and SSA usage. @@ -2125,7 +2115,7 @@ function sp_type_rewrap(@nospecialize(T), linfo::MethodInstance, isreturn::Bool) if unwrapva(T) === Bottom return Bottom elseif isa(T, Type) - if isa(T, DataType) && (T::DataType).name === _REF_NAME + if isa(T, DataType) && (T::DataType).name === Ref.body.name isref = true T = T.parameters[1] if isreturn && T === Any diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index f523d8ad8e810..80d3feaf363be 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -84,7 +84,7 @@ end const MAX_INLINE_CONST_SIZE = 256 function count_const_size(@nospecialize(x), count_self::Bool = true) - (x isa Type || x isa Symbol) && return 0 + (x isa Type || x isa Core.TypeName || x isa Symbol) && return 0 ismutable(x) && return MAX_INLINE_CONST_SIZE + 1 isbits(x) && return Core.sizeof(x) dt = typeof(x) diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index bcdd8c2875393..e41b0cb2dca6f 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -1942,6 +1942,11 @@ let result = @test_throws MethodError issue49074(Issue49050Concrete) @test result.value.args === (Any,) end +# inlining of `TypeName` +@test fully_eliminated() do + Ref.body.name +end + # Regression for finalizer inlining with more complex control flow global finalizer_escape::Int = 0 mutable struct FinalizerEscapeTest From 16e10a1e00914943e75fcc30e7ad6212bba2efdc Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Fri, 5 May 2023 02:33:40 -0400 Subject: [PATCH 676/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?LibCURL=20stdlib=20from=20fd8af64=20to=20a65b64f=20(#49091)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dilum Aluthge Co-authored-by: Kristoffer Carlsson --- deps/checksums/curl | 4 ++-- stdlib/LibCURL.version | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deps/checksums/curl b/deps/checksums/curl index 0156cdc1588eb..85974ba0bc8a0 100644 --- a/deps/checksums/curl +++ b/deps/checksums/curl @@ -1,5 +1,5 @@ -LibCURL-fd8af649b38ae20c3ff7f5dca53753512ca00376.tar.gz/md5/f082283e6a35fcba5b63c9a6219d8003 -LibCURL-fd8af649b38ae20c3ff7f5dca53753512ca00376.tar.gz/sha512/3bea5fa3fb6d29651daa923ae6bcb8eeb356ab9f2a1f3e005a6b746b617b0cf609aed4cadda4181783959840873c04b18e34e45ab973549169d19775a05ea01e +LibCURL-a65b64f6eabc932f63c2c0a4a5fb5d75f3e688d0.tar.gz/md5/e8c53aa3fb963c80921787d5d565eb2c +LibCURL-a65b64f6eabc932f63c2c0a4a5fb5d75f3e688d0.tar.gz/sha512/8e442ea834299df9c02acb87226c121395ad8e550025ac5ee1103df09c6ff43817e9e48dd1bcbc92c80331ef3ddff531962430269115179acbec2bab2de5b011 LibCURL.v8.0.1+0.aarch64-apple-darwin.tar.gz/md5/f697b4391608c2916ef159187e0d0b29 LibCURL.v8.0.1+0.aarch64-apple-darwin.tar.gz/sha512/41da87eed77ffac391a60a4af7fdc707f117affebe54960eaf43e3077440ce17d95fbe0f47de41bb1456e222e7a126d687fa0beb26cf98713b3472e9b3ba9e57 LibCURL.v8.0.1+0.aarch64-linux-gnu.tar.gz/md5/9d3e7e7601ac21a587bbb4289e149225 diff --git a/stdlib/LibCURL.version b/stdlib/LibCURL.version index 715ca76a40cdf..216ab4e7aca22 100644 --- a/stdlib/LibCURL.version +++ b/stdlib/LibCURL.version @@ -1,4 +1,4 @@ LIBCURL_BRANCH = master -LIBCURL_SHA1 = fd8af649b38ae20c3ff7f5dca53753512ca00376 +LIBCURL_SHA1 = a65b64f6eabc932f63c2c0a4a5fb5d75f3e688d0 LIBCURL_GIT_URL := https://github.com/JuliaWeb/LibCURL.jl.git LIBCURL_TAR_URL = https://api.github.com/repos/JuliaWeb/LibCURL.jl/tarball/$1 From 6b8ebb3e5b0d8056e3c2c25dd46d0cd82e7362ff Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 5 May 2023 02:33:58 -0400 Subject: [PATCH 677/775] annotate type of Task.queue field (#48420) This is apparently a common source of false-positives in JET. Co-authored-by: Kristoffer Carlsson --- base/condition.jl | 2 +- base/stream.jl | 4 ++-- base/task.jl | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/base/condition.jl b/base/condition.jl index 9d4f382064a2f..20481c98ee805 100644 --- a/base/condition.jl +++ b/base/condition.jl @@ -129,7 +129,7 @@ function wait(c::GenericCondition; first::Bool=false) try return wait() catch - ct.queue === nothing || list_deletefirst!(ct.queue, ct) + ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) rethrow() finally relockall(c.lock, token) diff --git a/base/stream.jl b/base/stream.jl index 8e247fc074422..0b6c9a93777f6 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -457,7 +457,7 @@ function closewrite(s::LibuvStream) # try-finally unwinds the sigatomic level, so need to repeat sigatomic_end sigatomic_end() iolock_begin() - ct.queue === nothing || list_deletefirst!(ct.queue, ct) + ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) if uv_req_data(req) != C_NULL # req is still alive, # so make sure we won't get spurious notifications later @@ -1050,7 +1050,7 @@ function uv_write(s::LibuvStream, p::Ptr{UInt8}, n::UInt) # try-finally unwinds the sigatomic level, so need to repeat sigatomic_end sigatomic_end() iolock_begin() - ct.queue === nothing || list_deletefirst!(ct.queue, ct) + ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) if uv_req_data(uvw) != C_NULL # uvw is still alive, # so make sure we won't get spurious notifications later diff --git a/base/task.jl b/base/task.jl index 876a8cf16ed4c..ae39a20164436 100644 --- a/base/task.jl +++ b/base/task.jl @@ -840,7 +840,7 @@ function schedule(t::Task, @nospecialize(arg); error=false) # schedule a task to be (re)started with the given value or exception t._state === task_state_runnable || Base.error("schedule: Task not runnable") if error - t.queue === nothing || Base.list_deletefirst!(t.queue, t) + t.queue === nothing || Base.list_deletefirst!(t.queue::IntrusiveLinkedList{Task}, t) setfield!(t, :result, arg) setfield!(t, :_isexception, true) else @@ -864,7 +864,7 @@ function yield() try wait() catch - ct.queue === nothing || list_deletefirst!(ct.queue, ct) + ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) rethrow() end end From 68cdf291fb80d0b1067455cf9b8985c25194382a Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Fri, 5 May 2023 02:34:13 -0400 Subject: [PATCH 678/775] Fix infinite loop on macOS with TIMING enabled (#49632) `jl_pathname_for_handle` calls `jl_load_dynamic_library` internally on macOS, so naively calling it for our TIMING instrumentation causes an infinite loop. This is a simple fix that disables instrumentation when we are RTLD_NOLOAD'ing the library, which probably makes the trace information more meaningful as a set of _opened_ libraries anyway. --- src/dlload.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/dlload.c b/src/dlload.c index 6141802df421c..3fb5a08ba2438 100644 --- a/src/dlload.c +++ b/src/dlload.c @@ -283,7 +283,8 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, is_atpath = 0; JL_TIMING(DL_OPEN, DL_OPEN); - jl_timing_puts(JL_TIMING_CURRENT_BLOCK, modname); + if (!(flags & JL_RTLD_NOLOAD)) + jl_timing_puts(JL_TIMING_CURRENT_BLOCK, modname); // Detect if our `modname` is something like `@rpath/libfoo.dylib` #ifdef _OS_DARWIN_ @@ -340,10 +341,10 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, if (i == 0) { // LoadLibrary already tested the extensions, we just need to check the `stat` result #endif handle = jl_dlopen(path, flags); - if (handle) { + if (handle && !(flags & JL_RTLD_NOLOAD)) jl_timing_puts(JL_TIMING_CURRENT_BLOCK, jl_pathname_for_handle(handle)); + if (handle) return handle; - } #ifdef _OS_WINDOWS_ err = GetLastError(); } @@ -362,10 +363,10 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, path[0] = '\0'; snprintf(path, PATHBUF, "%s%s", modname, ext); handle = jl_dlopen(path, flags); - if (handle) { + if (handle && !(flags & JL_RTLD_NOLOAD)) jl_timing_puts(JL_TIMING_CURRENT_BLOCK, jl_pathname_for_handle(handle)); + if (handle) return handle; - } #ifdef _OS_WINDOWS_ err = GetLastError(); break; // LoadLibrary already tested the rest From 1729d40115d45aa4156b0e250342300f4612b9e0 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 5 May 2023 08:39:27 +0200 Subject: [PATCH 679/775] add another entry to the git ignore blame file (#49638) Co-authored-by: Dilum Aluthge --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 3c555f006d082..3af8ba86153a1 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -5,3 +5,5 @@ e66bfa5dd32f93e76068c00ad882c1fc839c5af8 # whitespace: replace non-breaking space => space 100a741e7ab38c91d48cc929bb001afc8e09261f +# whitespace: replace tabs => space +b03e8ab9c7bd3e001add519571858fa04d6a249b From e70354da556fbf7b08c73110380abc0c34216c1e Mon Sep 17 00:00:00 2001 From: Aravindh Krishnamoorthy Date: Fri, 5 May 2023 08:40:01 +0200 Subject: [PATCH 680/775] Fix for #48911: Bugfix for the generic left-divide (ldiv!) algorithm with pivoted QR decomposition and wide matrices (#49570) * Fix for #48911 1. Fix output permutation when A.P is not the identity matrix. 2. Fix the application of the elementary transformation (correct complex conjugation) to the solution as per LAPACK zlarzb https://netlib.org/lapack/explore-html/d5/ddd/zlarzb_8f_source.html 3. Align the final permutation with LAPACK's ZGELSY at https://netlib.org/lapack/explore-html/d8/d6e/zgelsy_8f_source.html 4. Fix a complex conjugate bug in the original solution for #48911, align with LAPACK zlarzb, https://netlib.org/lapack/explore-html/d5/ddd/zlarzb_8f_source.html 5. Improve permutation performance * Integration fix for #48911: Permutation is taken care by ldiv!(QRPivoted, ...) * Tests for #48911: LinearAlgebra/test/qr.jl * Issue #48911: Include original test case mentioned in the issue. --------- Co-authored-by: Daniel Karrasch --- stdlib/LinearAlgebra/src/qr.jl | 9 ++++----- stdlib/LinearAlgebra/test/qr.jl | 30 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/stdlib/LinearAlgebra/src/qr.jl b/stdlib/LinearAlgebra/src/qr.jl index 4e3baa7ca91c4..43d04ac5fa415 100644 --- a/stdlib/LinearAlgebra/src/qr.jl +++ b/stdlib/LinearAlgebra/src/qr.jl @@ -614,7 +614,6 @@ ldiv!(A::QRPivoted{T,<:StridedMatrix}, B::AbstractVector{T}) where {T<:BlasFloat ldiv!(A::QRPivoted{T,<:StridedMatrix}, B::AbstractMatrix{T}) where {T<:BlasFloat} = ldiv!(A, B, min(size(A)...)*eps(real(T)))[1] - function _wide_qr_ldiv!(A::QR{T}, B::AbstractMatrix{T}) where T m, n = size(A) minmn = min(m,n) @@ -646,14 +645,14 @@ function _wide_qr_ldiv!(A::QR{T}, B::AbstractMatrix{T}) where T B[m + 1:mB,1:nB] .= zero(T) for j = 1:nB for k = 1:m - vBj = B[k,j] + vBj = B[k,j]' for i = m + 1:n - vBj += B[i,j]*R[k,i]' + vBj += B[i,j]'*R[k,i]' end vBj *= τ[k] - B[k,j] -= vBj + B[k,j] -= vBj' for i = m + 1:n - B[i,j] -= R[k,i]*vBj + B[i,j] -= R[k,i]'*vBj' end end end diff --git a/stdlib/LinearAlgebra/test/qr.jl b/stdlib/LinearAlgebra/test/qr.jl index cb15d94d08dcb..6e2e9a7b20603 100644 --- a/stdlib/LinearAlgebra/test/qr.jl +++ b/stdlib/LinearAlgebra/test/qr.jl @@ -474,4 +474,34 @@ end @test MyIdentity{Float64}()[1,:] == [1.0, 0.0] end +@testset "issue #48911" begin + # testcase in the original issue + # test ldiv!(::QRPivoted, ::AbstractVector) + A = Complex{BigFloat}[1+im 1-im] + b = Complex{BigFloat}[3+im] + x = A\b + AF = Complex{Float64}[1+im 1-im] + bf = Complex{Float64}[3+im] + xf = AF\bf + @test x ≈ xf + + # test ldiv!(::QRPivoted, ::AbstractVector) + A = Complex{BigFloat}[1+im 2-2im 3+3im; 4-4im 5+5im 6-6im] + b = Complex{BigFloat}[1+im; 0] + x = A\b + AF = Complex{Float64}[1+im 2-2im 3+3im; 4-4im 5+5im 6-6im] + bf = Complex{Float64}[1+im; 0] + xf = AF\bf + @test x ≈ xf + + # test ldiv!(::QRPivoted, ::AbstractMatrix) + C = Complex{BigFloat}[1+im 2-2im 3+3im; 4-4im 5+5im 6-6im] + D = Complex{BigFloat}[1+im 1-im; 0 0] + x = C\D + CF = Complex{Float64}[1+im 2-2im 3+3im; 4-4im 5+5im 6-6im] + DF = Complex{Float64}[1+im 1-im; 0 0] + xf = CF\DF + @test x ≈ xf +end + end # module TestQR From 9c6cfc682a674a1e3cfcb5861bcc31332bf186a7 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 5 May 2023 08:46:02 +0200 Subject: [PATCH 681/775] silence compiler warning when not building with ITTAPI (#49630) --- src/timing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timing.c b/src/timing.c index f0b48feb41d30..2e0dba7c025bc 100644 --- a/src/timing.c +++ b/src/timing.c @@ -74,7 +74,7 @@ void jl_init_timing(void) _Static_assert(JL_TIMING_EVENT_LAST < sizeof(uint64_t) * CHAR_BIT, "Too many timing events!"); _Static_assert((int)JL_TIMING_LAST <= (int)JL_TIMING_EVENT_LAST, "More owners than events!"); - int i = 0; + int i __attribute__((unused)) = 0; #ifdef USE_ITTAPI #define X(name) jl_timing_ittapi_events[i++] = __itt_event_create(#name, strlen(#name)); JL_TIMING_EVENTS From 0c83a231bfc70ed82854a01592670425f749bd2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Belmant?= Date: Fri, 5 May 2023 08:47:57 +0200 Subject: [PATCH 682/775] Don't assume macro expansion info is present in stacktrace (#49633) --- stdlib/Test/src/Test.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 48b37ef8047fe..811477e7d767c 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -64,9 +64,9 @@ function test_callsite(bt, file_ts, file_t) # For that, we retrieve locations from lower to higher stack elements # and only traverse parts of the backtrace which we haven't traversed before. # The order will always be -> `@test` -> `@testset`. - internal = macrocall_location(bt, @__FILE__)::Int + internal = @something(macrocall_location(bt, @__FILE__), return nothing) test = internal - 1 + findfirst(ip -> any(frame -> in_file(frame, file_t), StackTraces.lookup(ip)), @view bt[internal:end])::Int - testset = test - 1 + macrocall_location(@view(bt[test:end]), file_ts)::Int + testset = test - 1 + @something(macrocall_location(@view(bt[test:end]), file_ts), return nothing) # If stacktrace locations differ, include frames until the `@testset` appears. test != testset && return testset @@ -74,7 +74,8 @@ function test_callsite(bt, file_ts, file_t) # This may happen if `@test` occurred directly in scope of the testset, # or if `@test` occurred in a function that has been inlined in the testset. frames = StackTraces.lookup(bt[testset]) - outer_frame = findfirst(frame -> in_file(frame, file_ts) && frame.func == Symbol("macro expansion"), frames)::Int + outer_frame = findfirst(frame -> in_file(frame, file_ts) && frame.func == Symbol("macro expansion"), frames) + isnothing(outer_frame) && return nothing # The `@test` call occurred directly in scope of a `@testset`. # The __source__ from `@test` will be printed in the test message upon failure. # There is no need to include more frames, but always include at least the internal macrocall location in the stacktrace. From 6adea08b1bed85f4a119d307a0f08fd5af4ba2c9 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Fri, 5 May 2023 16:49:44 +0200 Subject: [PATCH 683/775] Merge code that handles `Adjoint` and `Transpose` (#49521) Co-authored-by: Takafumi Arakaki --- stdlib/LinearAlgebra/src/adjtrans.jl | 35 +++++ stdlib/LinearAlgebra/src/bidiag.jl | 21 +-- stdlib/LinearAlgebra/src/diagonal.jl | 8 +- stdlib/LinearAlgebra/src/matmul.jl | 171 ++++++++----------------- stdlib/LinearAlgebra/src/triangular.jl | 148 ++++++++++----------- stdlib/LinearAlgebra/test/matmul.jl | 4 +- 6 files changed, 167 insertions(+), 220 deletions(-) diff --git a/stdlib/LinearAlgebra/src/adjtrans.jl b/stdlib/LinearAlgebra/src/adjtrans.jl index 6003339735fcf..f6d8b33eb7639 100644 --- a/stdlib/LinearAlgebra/src/adjtrans.jl +++ b/stdlib/LinearAlgebra/src/adjtrans.jl @@ -64,6 +64,41 @@ end Adjoint(A) = Adjoint{Base.promote_op(adjoint,eltype(A)),typeof(A)}(A) Transpose(A) = Transpose{Base.promote_op(transpose,eltype(A)),typeof(A)}(A) +""" + adj_or_trans(::AbstractArray) -> adjoint|transpose|identity + adj_or_trans(::Type{<:AbstractArray}) -> adjoint|transpose|identity + +Return [`adjoint`](@ref) from an `Adjoint` type or object and +[`transpose`](@ref) from a `Transpose` type or object. Otherwise, +return [`identity`](@ref). Note that `Adjoint` and `Transpose` have +to be the outer-most wrapper object for a non-`identity` function to be +returned. +""" +adj_or_trans(::T) where {T<:AbstractArray} = adj_or_trans(T) +adj_or_trans(::Type{<:AbstractArray}) = identity +adj_or_trans(::Type{<:Adjoint}) = adjoint +adj_or_trans(::Type{<:Transpose}) = transpose + +""" + inplace_adj_or_trans(::AbstractArray) -> adjoint!|transpose!|copyto! + inplace_adj_or_trans(::Type{<:AbstractArray}) -> adjoint!|transpose!|copyto! + +Return [`adjoint!`](@ref) from an `Adjoint` type or object and +[`transpose!`](@ref) from a `Transpose` type or object. Otherwise, +return [`copyto!`](@ref). Note that `Adjoint` and `Transpose` have +to be the outer-most wrapper object for a non-`identity` function to be +returned. +""" +inplace_adj_or_trans(::T) where {T <: AbstractArray} = inplace_adj_or_trans(T) +inplace_adj_or_trans(::Type{<:AbstractArray}) = copyto! +inplace_adj_or_trans(::Type{<:Adjoint}) = adjoint! +inplace_adj_or_trans(::Type{<:Transpose}) = transpose! + +adj_or_trans_char(::T) where {T<:AbstractArray} = adj_or_trans_char(T) +adj_or_trans_char(::Type{<:AbstractArray}) = 'N' +adj_or_trans_char(::Type{<:Adjoint}) = 'C' +adj_or_trans_char(::Type{<:Transpose}) = 'T' + Base.dataids(A::Union{Adjoint, Transpose}) = Base.dataids(A.parent) Base.unaliascopy(A::Union{Adjoint,Transpose}) = typeof(A)(Base.unaliascopy(A.parent)) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 90f5c03f7fcfb..d136bf351b134 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -743,17 +743,13 @@ function ldiv!(c::AbstractVecOrMat, A::Bidiagonal, b::AbstractVecOrMat) end return c end -ldiv!(A::Transpose{<:Any,<:Bidiagonal}, b::AbstractVecOrMat) = @inline ldiv!(b, A, b) -ldiv!(A::Adjoint{<:Any,<:Bidiagonal}, b::AbstractVecOrMat) = @inline ldiv!(b, A, b) -ldiv!(c::AbstractVecOrMat, A::Transpose{<:Any,<:Bidiagonal}, b::AbstractVecOrMat) = - (_rdiv!(transpose(c), transpose(b), transpose(A)); return c) -ldiv!(c::AbstractVecOrMat, A::Adjoint{<:Any,<:Bidiagonal}, b::AbstractVecOrMat) = - (_rdiv!(adjoint(c), adjoint(b), adjoint(A)); return c) +ldiv!(A::AdjOrTrans{<:Any,<:Bidiagonal}, b::AbstractVecOrMat) = @inline ldiv!(b, A, b) +ldiv!(c::AbstractVecOrMat, A::AdjOrTrans{<:Any,<:Bidiagonal}, b::AbstractVecOrMat) = + (t = adj_or_trans(A); _rdiv!(t(c), t(b), t(A)); return c) ### Generic promotion methods and fallbacks \(A::Bidiagonal, B::AbstractVecOrMat) = ldiv!(_initarray(\, eltype(A), eltype(B), B), A, B) -\(tA::Transpose{<:Any,<:Bidiagonal}, B::AbstractVecOrMat) = copy(tA) \ B -\(adjA::Adjoint{<:Any,<:Bidiagonal}, B::AbstractVecOrMat) = copy(adjA) \ B +\(xA::AdjOrTrans{<:Any,<:Bidiagonal}, B::AbstractVecOrMat) = copy(xA) \ B ### Triangular specializations function \(B::Bidiagonal, U::UpperTriangular) @@ -837,12 +833,9 @@ function _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::Bidiagonal) C end rdiv!(A::AbstractMatrix, B::Bidiagonal) = @inline _rdiv!(A, A, B) -rdiv!(A::AbstractMatrix, B::Adjoint{<:Any,<:Bidiagonal}) = @inline _rdiv!(A, A, B) -rdiv!(A::AbstractMatrix, B::Transpose{<:Any,<:Bidiagonal}) = @inline _rdiv!(A, A, B) -_rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::Adjoint{<:Any,<:Bidiagonal}) = - (ldiv!(adjoint(C), adjoint(B), adjoint(A)); return C) -_rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::Transpose{<:Any,<:Bidiagonal}) = - (ldiv!(transpose(C), transpose(B), transpose(A)); return C) +rdiv!(A::AbstractMatrix, B::AdjOrTrans{<:Any,<:Bidiagonal}) = @inline _rdiv!(A, A, B) +_rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::AdjOrTrans{<:Any,<:Bidiagonal}) = + (t = adj_or_trans(B); ldiv!(t(C), t(B), t(A)); return C) /(A::AbstractMatrix, B::Bidiagonal) = _rdiv!(_initarray(/, eltype(A), eltype(B), A), A, B) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index ec1bca909ce7b..466501e72cd4f 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -396,16 +396,12 @@ end _muldiag!(out, D, V, alpha, beta) @inline mul!(out::AbstractMatrix, D::Diagonal, B::AbstractMatrix, alpha::Number, beta::Number) = _muldiag!(out, D, B, alpha, beta) -@inline mul!(out::AbstractMatrix, D::Diagonal, B::Adjoint{<:Any,<:AbstractVecOrMat}, - alpha::Number, beta::Number) = _muldiag!(out, D, B, alpha, beta) -@inline mul!(out::AbstractMatrix, D::Diagonal, B::Transpose{<:Any,<:AbstractVecOrMat}, +@inline mul!(out::AbstractMatrix, D::Diagonal, B::AdjOrTrans{<:Any,<:AbstractVecOrMat}, alpha::Number, beta::Number) = _muldiag!(out, D, B, alpha, beta) @inline mul!(out::AbstractMatrix, A::AbstractMatrix, D::Diagonal, alpha::Number, beta::Number) = _muldiag!(out, A, D, alpha, beta) -@inline mul!(out::AbstractMatrix, A::Adjoint{<:Any,<:AbstractVecOrMat}, D::Diagonal, - alpha::Number, beta::Number) = _muldiag!(out, A, D, alpha, beta) -@inline mul!(out::AbstractMatrix, A::Transpose{<:Any,<:AbstractVecOrMat}, D::Diagonal, +@inline mul!(out::AbstractMatrix, A::AdjOrTrans{<:Any,<:AbstractVecOrMat}, D::Diagonal, alpha::Number, beta::Number) = _muldiag!(out, A, D, alpha, beta) @inline mul!(C::Diagonal, Da::Diagonal, Db::Diagonal, alpha::Number, beta::Number) = _muldiag!(C, Da, Db, alpha, beta) diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 6d00b950525e6..8b71db6dc328d 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -1,11 +1,16 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +# matmul.jl: Everything to do with dense matrix multiplication + # Matrix-matrix multiplication -AdjOrTransStridedMat{T} = Union{Adjoint{T, <:StridedMatrix}, Transpose{T, <:StridedMatrix}} -StridedMaybeAdjOrTransMat{T} = Union{StridedMatrix{T}, Adjoint{T, <:StridedMatrix}, Transpose{T, <:StridedMatrix}} +AdjOrTransStridedMat{T} = Union{Adjoint{<:Any, <:StridedMatrix{T}}, Transpose{<:Any, <:StridedMatrix{T}}} +StridedMaybeAdjOrTransMat{T} = Union{StridedMatrix{T}, Adjoint{<:Any, <:StridedMatrix{T}}, Transpose{<:Any, <:StridedMatrix{T}}} +StridedMaybeAdjOrTransVecOrMat{T} = Union{StridedVecOrMat{T}, AdjOrTrans{<:Any, <:StridedVecOrMat{T}}} -# matmul.jl: Everything to do with dense matrix multiplication +_parent(A) = A +_parent(A::Adjoint) = parent(A) +_parent(A::Transpose) = parent(A) matprod(x, y) = x*y + x*y @@ -46,14 +51,14 @@ function *(transx::Transpose{<:Any,<:StridedVector{T}}, y::StridedVector{T}) whe end # Matrix-vector multiplication -function (*)(A::StridedMatrix{T}, x::StridedVector{S}) where {T<:BlasFloat,S<:Real} +function (*)(A::StridedMaybeAdjOrTransMat{T}, x::StridedVector{S}) where {T<:BlasFloat,S<:Real} TS = promote_op(matprod, T, S) y = isconcretetype(TS) ? convert(AbstractVector{TS}, x) : x mul!(similar(x, TS, size(A,1)), A, y) end function (*)(A::AbstractMatrix{T}, x::AbstractVector{S}) where {T,S} TS = promote_op(matprod, T, S) - mul!(similar(x,TS,axes(A,1)),A,x) + mul!(similar(x, TS, axes(A,1)), A, x) end # these will throw a DimensionMismatch unless B has 1 row (or 1 col for transposed case): @@ -61,68 +66,33 @@ end (*)(a::AbstractVector, adjB::AdjointAbsMat) = reshape(a, length(a), 1) * adjB (*)(a::AbstractVector, B::AbstractMatrix) = reshape(a, length(a), 1) * B -@inline mul!(y::StridedVector{T}, A::StridedVecOrMat{T}, x::StridedVector{T}, - alpha::Number, beta::Number) where {T<:BlasFloat} = - gemv!(y, 'N', A, x, alpha, beta) - +@inline mul!(y::AbstractVector, A::AbstractVecOrMat, x::AbstractVector, + alpha::Number, beta::Number) = + generic_matvecmul!(y, adj_or_trans_char(A), _parent(A), x, MulAddMul(alpha, beta)) +# BLAS cases +@inline mul!(y::StridedVector{T}, A::StridedMaybeAdjOrTransVecOrMat{T}, x::StridedVector{T}, + alpha::Number, beta::Number) where {T<:BlasFloat} = + gemv!(y, adj_or_trans_char(A), _parent(A), x, alpha, beta) +# catch the real adjoint case and rewrap to transpose +@inline mul!(y::StridedVector{T}, adjA::Adjoint{<:Any,<:StridedVecOrMat{T}}, x::StridedVector{T}, + alpha::Number, beta::Number) where {T<:BlasReal} = + mul!(y, transpose(adjA.parent), x, alpha, beta) # Complex matrix times real vector. # Reinterpret the matrix as a real matrix and do real matvec computation. @inline mul!(y::StridedVector{Complex{T}}, A::StridedVecOrMat{Complex{T}}, x::StridedVector{T}, alpha::Number, beta::Number) where {T<:BlasReal} = gemv!(y, 'N', A, x, alpha, beta) - # Real matrix times complex vector. # Multiply the matrix with the real and imaginary parts separately @inline mul!(y::StridedVector{Complex{T}}, A::StridedMaybeAdjOrTransMat{T}, x::StridedVector{Complex{T}}, alpha::Number, beta::Number) where {T<:BlasReal} = - gemv!(y, A isa StridedArray ? 'N' : 'T', A isa StridedArray ? A : parent(A), x, alpha, beta) - -@inline mul!(y::AbstractVector, A::AbstractVecOrMat, x::AbstractVector, - alpha::Number, beta::Number) = - generic_matvecmul!(y, 'N', A, x, MulAddMul(alpha, beta)) - -function *(tA::Transpose{<:Any,<:StridedMatrix{T}}, x::StridedVector{S}) where {T<:BlasFloat,S} - TS = promote_op(matprod, T, S) - mul!(similar(x, TS, size(tA, 1)), tA, convert(AbstractVector{TS}, x)) -end -function *(tA::Transpose{<:Any,<:AbstractMatrix{T}}, x::AbstractVector{S}) where {T,S} - TS = promote_op(matprod, T, S) - mul!(similar(x, TS, size(tA, 1)), tA, x) -end -@inline mul!(y::StridedVector{T}, tA::Transpose{<:Any,<:StridedVecOrMat{T}}, x::StridedVector{T}, - alpha::Number, beta::Number) where {T<:BlasFloat} = - gemv!(y, 'T', tA.parent, x, alpha, beta) -@inline mul!(y::AbstractVector, tA::Transpose{<:Any,<:AbstractVecOrMat}, x::AbstractVector, - alpha::Number, beta::Number) = - generic_matvecmul!(y, 'T', tA.parent, x, MulAddMul(alpha, beta)) - -function *(adjA::Adjoint{<:Any,<:StridedMatrix{T}}, x::StridedVector{S}) where {T<:BlasFloat,S} - TS = promote_op(matprod, T, S) - mul!(similar(x, TS, size(adjA, 1)), adjA, convert(AbstractVector{TS}, x)) -end -function *(adjA::Adjoint{<:Any,<:AbstractMatrix{T}}, x::AbstractVector{S}) where {T,S} - TS = promote_op(matprod, T, S) - mul!(similar(x, TS, size(adjA, 1)), adjA, x) -end - -@inline mul!(y::StridedVector{T}, adjA::Adjoint{<:Any,<:StridedVecOrMat{T}}, x::StridedVector{T}, - alpha::Number, beta::Number) where {T<:BlasReal} = - mul!(y, transpose(adjA.parent), x, alpha, beta) -@inline mul!(y::StridedVector{T}, adjA::Adjoint{<:Any,<:StridedVecOrMat{T}}, x::StridedVector{T}, - alpha::Number, beta::Number) where {T<:BlasComplex} = - gemv!(y, 'C', adjA.parent, x, alpha, beta) -@inline mul!(y::AbstractVector, adjA::Adjoint{<:Any,<:AbstractVecOrMat}, x::AbstractVector, - alpha::Number, beta::Number) = - generic_matvecmul!(y, 'C', adjA.parent, x, MulAddMul(alpha, beta)) + gemv!(y, A isa StridedArray ? 'N' : 'T', _parent(A), x, alpha, beta) # Vector-Matrix multiplication (*)(x::AdjointAbsVec, A::AbstractMatrix) = (A'*x')' (*)(x::TransposeAbsVec, A::AbstractMatrix) = transpose(transpose(A)*transpose(x)) -_parent(A) = A -_parent(A::Adjoint) = parent(A) -_parent(A::Transpose) = parent(A) - +# Matrix-matrix multiplication """ *(A::AbstractMatrix, B::AbstractMatrix) @@ -156,10 +126,6 @@ function (*)(A::StridedMaybeAdjOrTransMat{<:BlasComplex}, B::StridedMaybeAdjOrTr wrapperop(B)(convert(AbstractArray{TS}, _parent(B)))) end -@inline function mul!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, - alpha::Number, beta::Number) where {T<:BlasFloat} - return gemm_wrapper!(C, 'N', 'N', A, B, MulAddMul(alpha, beta)) -end # Complex Matrix times real matrix: We use that it is generally faster to reinterpret the # first matrix as a real matrix and carry out real matrix matrix multiply function (*)(A::StridedMatrix{<:BlasComplex}, B::StridedMaybeAdjOrTransMat{<:BlasReal}) @@ -301,7 +267,14 @@ julia> C """ @inline mul!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat, alpha::Number, beta::Number) = - generic_matmatmul!(C, 'N', 'N', A, B, MulAddMul(alpha, beta)) + generic_matmatmul!( + C, + adj_or_trans_char(A), + adj_or_trans_char(B), + _parent(A), + _parent(B), + MulAddMul(alpha, beta) + ) """ rmul!(A, B) @@ -369,6 +342,12 @@ julia> lmul!(F.Q, B) """ lmul!(A, B) +# generic case +@inline mul!(C::StridedMatrix{T}, A::StridedMaybeAdjOrTransVecOrMat{T}, B::StridedMaybeAdjOrTransVecOrMat{T}, + alpha::Number, beta::Number) where {T<:BlasFloat} = + gemm_wrapper!(C, adj_or_trans_char(A), adj_or_trans_char(B), _parent(A), _parent(B), MulAddMul(alpha, beta)) + +# AtB & ABt (including B === A) @inline function mul!(C::StridedMatrix{T}, tA::Transpose{<:Any,<:StridedVecOrMat{T}}, B::StridedVecOrMat{T}, alpha::Number, beta::Number) where {T<:BlasFloat} A = tA.parent @@ -378,10 +357,6 @@ lmul!(A, B) return gemm_wrapper!(C, 'T', 'N', A, B, MulAddMul(alpha, beta)) end end -@inline mul!(C::AbstractMatrix, tA::Transpose{<:Any,<:AbstractVecOrMat}, B::AbstractVecOrMat, - alpha::Number, beta::Number) = - generic_matmatmul!(C, 'T', 'N', tA.parent, B, MulAddMul(alpha, beta)) - @inline function mul!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, tB::Transpose{<:Any,<:StridedVecOrMat{T}}, alpha::Number, beta::Number) where {T<:BlasFloat} B = tB.parent @@ -391,39 +366,15 @@ end return gemm_wrapper!(C, 'N', 'T', A, B, MulAddMul(alpha, beta)) end end -# Complex matrix times (transposed) real matrix. Reinterpret the first matrix to real for efficiency. -@inline mul!(C::StridedMatrix{Complex{T}}, A::StridedVecOrMat{Complex{T}}, B::StridedVecOrMat{T}, - alpha::Number, beta::Number) where {T<:BlasReal} = - gemm_wrapper!(C, 'N', 'N', A, B, MulAddMul(alpha, beta)) -@inline mul!(C::StridedMatrix{Complex{T}}, A::StridedVecOrMat{Complex{T}}, tB::Transpose{<:Any,<:StridedVecOrMat{T}}, - alpha::Number, beta::Number) where {T<:BlasReal} = - gemm_wrapper!(C, 'N', 'T', A, parent(tB), MulAddMul(alpha, beta)) - -# collapsing the following two defs with C::AbstractVecOrMat yields ambiguities -@inline mul!(C::AbstractVector, A::AbstractVecOrMat, tB::Transpose{<:Any,<:AbstractVecOrMat}, - alpha::Number, beta::Number) = - generic_matmatmul!(C, 'N', 'T', A, tB.parent, MulAddMul(alpha, beta)) -@inline mul!(C::AbstractMatrix, A::AbstractVecOrMat, tB::Transpose{<:Any,<:AbstractVecOrMat}, - alpha::Number, beta::Number) = - generic_matmatmul!(C, 'N', 'T', A, tB.parent, MulAddMul(alpha, beta)) - -@inline mul!(C::StridedMatrix{T}, tA::Transpose{<:Any,<:StridedVecOrMat{T}}, tB::Transpose{<:Any,<:StridedVecOrMat{T}}, - alpha::Number, beta::Number) where {T<:BlasFloat} = - gemm_wrapper!(C, 'T', 'T', tA.parent, tB.parent, MulAddMul(alpha, beta)) -@inline mul!(C::AbstractMatrix, tA::Transpose{<:Any,<:AbstractVecOrMat}, tB::Transpose{<:Any,<:AbstractVecOrMat}, - alpha::Number, beta::Number) = - generic_matmatmul!(C, 'T', 'T', tA.parent, tB.parent, MulAddMul(alpha, beta)) - -@inline mul!(C::StridedMatrix{T}, tA::Transpose{<:Any,<:StridedVecOrMat{T}}, adjB::Adjoint{<:Any,<:StridedVecOrMat{T}}, - alpha::Number, beta::Number) where {T<:BlasFloat} = - gemm_wrapper!(C, 'T', 'C', tA.parent, adjB.parent, MulAddMul(alpha, beta)) -@inline mul!(C::AbstractMatrix, tA::Transpose{<:Any,<:AbstractVecOrMat}, tB::Adjoint{<:Any,<:AbstractVecOrMat}, - alpha::Number, beta::Number) = - generic_matmatmul!(C, 'T', 'C', tA.parent, tB.parent, MulAddMul(alpha, beta)) - +# real adjoint cases, also needed for disambiguation +@inline mul!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, adjB::Adjoint{<:Any,<:StridedVecOrMat{T}}, + alpha::Number, beta::Number) where {T<:BlasReal} = + mul!(C, A, transpose(adjB.parent), alpha, beta) @inline mul!(C::StridedMatrix{T}, adjA::Adjoint{<:Any,<:StridedVecOrMat{T}}, B::StridedVecOrMat{T}, - alpha::Real, beta::Real) where {T<:BlasReal} = + alpha::Real, beta::Real) where {T<:BlasReal} = mul!(C, transpose(adjA.parent), B, alpha, beta) + +# AcB & ABc (including B === A) @inline function mul!(C::StridedMatrix{T}, adjA::Adjoint{<:Any,<:StridedVecOrMat{T}}, B::StridedVecOrMat{T}, alpha::Number, beta::Number) where {T<:BlasComplex} A = adjA.parent @@ -433,13 +384,6 @@ end return gemm_wrapper!(C, 'C', 'N', A, B, MulAddMul(alpha, beta)) end end -@inline mul!(C::AbstractMatrix, adjA::Adjoint{<:Any,<:AbstractVecOrMat}, B::AbstractVecOrMat, - alpha::Number, beta::Number) = - generic_matmatmul!(C, 'C', 'N', adjA.parent, B, MulAddMul(alpha, beta)) - -@inline mul!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, adjB::Adjoint{<:Any,<:StridedVecOrMat{<:BlasReal}}, - alpha::Number, beta::Number) where {T<:BlasFloat} = - mul!(C, A, transpose(adjB.parent), alpha, beta) @inline function mul!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, adjB::Adjoint{<:Any,<:StridedVecOrMat{T}}, alpha::Number, beta::Number) where {T<:BlasComplex} B = adjB.parent @@ -449,23 +393,16 @@ end return gemm_wrapper!(C, 'N', 'C', A, B, MulAddMul(alpha, beta)) end end -@inline mul!(C::AbstractMatrix, A::AbstractVecOrMat, adjB::Adjoint{<:Any,<:AbstractVecOrMat}, - alpha::Number, beta::Number) = - generic_matmatmul!(C, 'N', 'C', A, adjB.parent, MulAddMul(alpha, beta)) - -@inline mul!(C::StridedMatrix{T}, adjA::Adjoint{<:Any,<:StridedVecOrMat{T}}, adjB::Adjoint{<:Any,<:StridedVecOrMat{T}}, - alpha::Number, beta::Number) where {T<:BlasFloat} = - gemm_wrapper!(C, 'C', 'C', adjA.parent, adjB.parent, MulAddMul(alpha, beta)) -@inline mul!(C::AbstractMatrix, adjA::Adjoint{<:Any,<:AbstractVecOrMat}, adjB::Adjoint{<:Any,<:AbstractVecOrMat}, - alpha::Number, beta::Number) = - generic_matmatmul!(C, 'C', 'C', adjA.parent, adjB.parent, MulAddMul(alpha, beta)) - -@inline mul!(C::StridedMatrix{T}, adjA::Adjoint{<:Any,<:StridedVecOrMat{T}}, tB::Transpose{<:Any,<:StridedVecOrMat{T}}, - alpha::Number, beta::Number) where {T<:BlasFloat} = - gemm_wrapper!(C, 'C', 'T', adjA.parent, tB.parent, MulAddMul(alpha, beta)) -@inline mul!(C::AbstractMatrix, adjA::Adjoint{<:Any,<:AbstractVecOrMat}, tB::Transpose{<:Any,<:AbstractVecOrMat}, - alpha::Number, beta::Number) = - generic_matmatmul!(C, 'C', 'T', adjA.parent, tB.parent, MulAddMul(alpha, beta)) + +# Complex matrix times (transposed) real matrix. Reinterpret the first matrix to real for efficiency. +@inline mul!(C::StridedMatrix{Complex{T}}, A::StridedMaybeAdjOrTransVecOrMat{Complex{T}}, B::StridedMaybeAdjOrTransVecOrMat{T}, + alpha::Number, beta::Number) where {T<:BlasReal} = + gemm_wrapper!(C, adj_or_trans_char(A), adj_or_trans_char(B), _parent(A), _parent(B), MulAddMul(alpha, beta)) +# catch the real adjoint case and interpret it as a transpose +@inline mul!(C::StridedMatrix{Complex{T}}, A::StridedVecOrMat{Complex{T}}, adjB::Adjoint{<:Any,<:StridedVecOrMat{T}}, + alpha::Number, beta::Number) where {T<:BlasReal} = + mul!(C, A, transpose(adjB.parent), alpha, beta) + # Supporting functions for matrix multiplication diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 248fc048612c8..557516d84811b 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -670,23 +670,9 @@ fillstored!(A::UnitUpperTriangular, x) = (fillband!(A.data, x, 1, size(A,2)-1); lmul!(A::Tridiagonal, B::AbstractTriangular) = A*full!(B) # is this necessary? -mul!(C::AbstractVector, A::AbstractTriangular, transB::Transpose{<:Any,<:AbstractVecOrMat}) = - (B = transB.parent; lmul!(A, transpose!(C, B))) -mul!(C::AbstractMatrix, A::AbstractTriangular, transB::Transpose{<:Any,<:AbstractVecOrMat}) = - (B = transB.parent; lmul!(A, transpose!(C, B))) -mul!(C::AbstractMatrix, A::AbstractTriangular, adjB::Adjoint{<:Any,<:AbstractVecOrMat}) = - (B = adjB.parent; lmul!(A, adjoint!(C, B))) -mul!(C::AbstractVecOrMat, A::AbstractTriangular, adjB::Adjoint{<:Any,<:AbstractVecOrMat}) = - (B = adjB.parent; lmul!(A, adjoint!(C, B))) - -# The three methods are necessary to avoid ambiguities with definitions in matmul.jl -mul!(C::AbstractVector , A::AbstractTriangular, B::AbstractVector) = lmul!(A, copyto!(C, B)) -mul!(C::AbstractMatrix , A::AbstractTriangular, B::AbstractVecOrMat) = lmul!(A, copyto!(C, B)) -mul!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = lmul!(A, copyto!(C, B)) - -@inline mul!(C::AbstractMatrix, A::AbstractTriangular, B::Adjoint{<:Any,<:AbstractVecOrMat}, alpha::Number, beta::Number) = - mul!(C, A, copy(B), alpha, beta) -@inline mul!(C::AbstractMatrix, A::AbstractTriangular, B::Transpose{<:Any,<:AbstractVecOrMat}, alpha::Number, beta::Number) = +mul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractVecOrMat) = + lmul!(A, inplace_adj_or_trans(B)(C, _parent(B))) +@inline mul!(C::AbstractMatrix, A::AbstractTriangular, B::AdjOrTrans{<:Any,<:AbstractVecOrMat}, alpha::Number, beta::Number) = mul!(C, A, copy(B), alpha, beta) # preserve triangular structure in in-place multiplication @@ -1116,78 +1102,78 @@ end # in the following transpose and conjugate transpose naive substitution variants, # accumulating in z rather than b[j,k] significantly improves performance as of Dec 2015 -for (t, tfun) in ((:Adjoint, :adjoint), (:Transpose, :transpose)) - @eval begin - function ldiv!(xA::UpperTriangular{<:Any,<:$t}, b::AbstractVector) - require_one_based_indexing(xA, b) - A = parent(parent(xA)) - n = size(A, 1) - if !(n == length(b)) - throw(DimensionMismatch("first dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) - end - @inbounds for j in n:-1:1 - z = b[j] - for i in n:-1:j+1 - z -= $tfun(A[i,j]) * b[i] - end - iszero(A[j,j]) && throw(SingularException(j)) - b[j] = $tfun(A[j,j]) \ z - end - return b +function ldiv!(xA::UpperTriangular{<:Any,<:AdjOrTrans}, b::AbstractVector) + require_one_based_indexing(xA, b) + tfun = adj_or_trans(parent(xA)) + A = parent(parent(xA)) + n = size(A, 1) + if !(n == length(b)) + throw(DimensionMismatch("first dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) + end + @inbounds for j in n:-1:1 + z = b[j] + for i in n:-1:j+1 + z -= tfun(A[i,j]) * b[i] end + iszero(A[j,j]) && throw(SingularException(j)) + b[j] = tfun(A[j,j]) \ z + end + return b +end - function ldiv!(xA::UnitUpperTriangular{<:Any,<:$t}, b::AbstractVector) - require_one_based_indexing(xA, b) - A = parent(parent(xA)) - n = size(A, 1) - if !(n == length(b)) - throw(DimensionMismatch("first dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) - end - @inbounds for j in n:-1:1 - z = b[j] - for i in n:-1:j+1 - z -= $tfun(A[i,j]) * b[i] - end - b[j] = z - end - return b +function ldiv!(xA::UnitUpperTriangular{<:Any,<:AdjOrTrans}, b::AbstractVector) + require_one_based_indexing(xA, b) + tfun = adj_or_trans(parent(xA)) + A = parent(parent(xA)) + n = size(A, 1) + if !(n == length(b)) + throw(DimensionMismatch("first dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) + end + @inbounds for j in n:-1:1 + z = b[j] + for i in n:-1:j+1 + z -= tfun(A[i,j]) * b[i] end + b[j] = z + end + return b +end - function ldiv!(xA::LowerTriangular{<:Any,<:$t}, b::AbstractVector) - require_one_based_indexing(xA, b) - A = parent(parent(xA)) - n = size(A, 1) - if !(n == length(b)) - throw(DimensionMismatch("first dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) - end - @inbounds for j in 1:n - z = b[j] - for i in 1:j-1 - z -= $tfun(A[i,j]) * b[i] - end - iszero(A[j,j]) && throw(SingularException(j)) - b[j] = $tfun(A[j,j]) \ z - end - return b +function ldiv!(xA::LowerTriangular{<:Any,<:AdjOrTrans}, b::AbstractVector) + require_one_based_indexing(xA, b) + tfun = adj_or_trans(parent(xA)) + A = parent(parent(xA)) + n = size(A, 1) + if !(n == length(b)) + throw(DimensionMismatch("first dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) + end + @inbounds for j in 1:n + z = b[j] + for i in 1:j-1 + z -= tfun(A[i,j]) * b[i] end + iszero(A[j,j]) && throw(SingularException(j)) + b[j] = tfun(A[j,j]) \ z + end + return b +end - function ldiv!(xA::UnitLowerTriangular{<:Any,<:$t}, b::AbstractVector) - require_one_based_indexing(xA, b) - A = parent(parent(xA)) - n = size(A, 1) - if !(n == length(b)) - throw(DimensionMismatch("first dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) - end - @inbounds for j in 1:n - z = b[j] - for i in 1:j-1 - z -= $tfun(A[i,j]) * b[i] - end - b[j] = z - end - return b +function ldiv!(xA::UnitLowerTriangular{<:Any,<:AdjOrTrans}, b::AbstractVector) + require_one_based_indexing(xA, b) + tfun = adj_or_trans(parent(xA)) + A = parent(parent(xA)) + n = size(A, 1) + if !(n == length(b)) + throw(DimensionMismatch("first dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) + end + @inbounds for j in 1:n + z = b[j] + for i in 1:j-1 + z -= tfun(A[i,j]) * b[i] end + b[j] = z end + return b end function rdiv!(A::AbstractMatrix, B::UpperTriangular) diff --git a/stdlib/LinearAlgebra/test/matmul.jl b/stdlib/LinearAlgebra/test/matmul.jl index 0150c4c2efdc8..2d99856a2667b 100644 --- a/stdlib/LinearAlgebra/test/matmul.jl +++ b/stdlib/LinearAlgebra/test/matmul.jl @@ -655,10 +655,10 @@ Transpose(x::RootInt) = x @testset "#14293" begin a = [RootInt(3)] - C = [0] + C = [0;;] mul!(C, a, transpose(a)) @test C[1] == 9 - C = [1] + C = [1;;] mul!(C, a, transpose(a), 2, 3) @test C[1] == 21 a = [RootInt(2), RootInt(10)] From 6bcdd00b400bde8521119cede7e5f2dff3744adb Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 5 May 2023 18:22:03 +0200 Subject: [PATCH 684/775] Fix type comparison printing in `MethodError`s from convert. (#49645) This patch removes the erroneous `{}` for types without parameters when printing `MethodError`s from convert. Fixes e.g. the following (note `Float64{}`): ```julia julia> struct A{B, C} end julia> convert(A{Float64,1}, A{Float64,2}()) ERROR: MethodError: Cannot `convert` an object of type A{Float64{},2} to an object of type A{Float64{},1} ``` --- base/errorshow.jl | 1 + test/errorshow.jl | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/base/errorshow.jl b/base/errorshow.jl index 0b407f9221c28..03650920aae57 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -181,6 +181,7 @@ function print_with_compare(io::IO, @nospecialize(a::DataType), @nospecialize(b: if a.name === b.name Base.show_type_name(io, a.name) n = length(a.parameters) + n > 0 || return print(io, '{') for i = 1:n if i > length(b.parameters) diff --git a/test/errorshow.jl b/test/errorshow.jl index 78e9a30241c9f..cb19061da8806 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -987,3 +987,12 @@ bt_str = sprint(Base.show_backtrace, bt) @test occursin("g_collapse_pos_kw(x::Float64, y::Float64; z::Float64)", bt_str) @test !occursin("g_collapse_pos_kw(x::Float64, y::Float64)", bt_str) @test !occursin("g_collapse_pos_kw(x::Float64)", bt_str) + +# Test Base.print_with_compare in convert MethodErrors +struct TypeCompareError{A,B} end +let e = try convert(TypeCompareError{Float64,1}, TypeCompareError{Float64,2}()); catch e e end + str = sprint(Base.showerror, e) + @test occursin("TypeCompareError{Float64,2}", str) + @test occursin("TypeCompareError{Float64,1}", str) + @test !occursin("TypeCompareError{Float64{},2}", str) # No {...} for types without params +end From 9201414b3d8a2ed2419e463a73e4cd67cf1f1733 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 5 May 2023 13:36:15 -0400 Subject: [PATCH 685/775] fix whitespace in test/ranges.jl (#49648) --- test/ranges.jl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/ranges.jl b/test/ranges.jl index 2a7a4d3170d67..ec69c57fc0a8f 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -2442,16 +2442,16 @@ end end @testset "PR 49516" begin - struct PR49516 <: Signed - n::Int - end - PR49516(f::PR49516) = f - Base.:*(x::Integer, f::PR49516) = PR49516(*(x, f.n)) - Base.:+(f1::PR49516, f2::PR49516) = PR49516(+(f1.n, f2.n)) - Base.show(io::IO, f::PR49516) = print(io, "PR49516(", f.n, ")") - - srl = StepRangeLen(PR49516(1), PR49516(2), 10) - @test sprint(show, srl) == "PR49516(1):PR49516(2):PR49516(19)" + struct PR49516 <: Signed + n::Int + end + PR49516(f::PR49516) = f + Base.:*(x::Integer, f::PR49516) = PR49516(*(x, f.n)) + Base.:+(f1::PR49516, f2::PR49516) = PR49516(+(f1.n, f2.n)) + Base.show(io::IO, f::PR49516) = print(io, "PR49516(", f.n, ")") + + srl = StepRangeLen(PR49516(1), PR49516(2), 10) + @test sprint(show, srl) == "PR49516(1):PR49516(2):PR49516(19)" end @testset "Inline StepRange Construction #49270" begin From f4d9416cecc7df7928d2d10f06381e04d4675575 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 5 May 2023 13:47:19 -0400 Subject: [PATCH 686/775] `widenconst` before type check in SROA (#49642) * `widenconst` before type check in SROA These days, mutable structs can be `PartialStruct` if one of the fields is annotated as `const`, so we need to widenconst before this check. * Update test/compiler/irpasses.jl Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> --------- Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> --- base/compiler/ssair/passes.jl | 1 + test/compiler/irpasses.jl | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 7bf1b70487087..4bfb5f3fcde56 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1265,6 +1265,7 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse typ = unwrap_unionall(ir.stmts[newidx][:type]) # Could still end up here if we tried to setfield! on an immutable, which would # error at runtime, but is not illegal to have in the IR. + typ = widenconst(typ) ismutabletype(typ) || continue typ = typ::DataType # First check for any finalizer calls diff --git a/test/compiler/irpasses.jl b/test/compiler/irpasses.jl index 43371b51f6a64..c704a8cf1c434 100644 --- a/test/compiler/irpasses.jl +++ b/test/compiler/irpasses.jl @@ -1230,3 +1230,18 @@ let src = code_typed1(named_tuple_elim, Tuple{Symbol, Tuple}) count(iscall((src, Core._svec_ref)), src.code) == 0 && count(iscall(x->!isa(argextype(x, src).val, Core.Builtin)), src.code) == 0 end + +# Test that sroa works if the struct type is a PartialStruct +mutable struct OneConstField + const a::Int + b::Int +end + +@eval function one_const_field_partial() + # Use explicit :new here to avoid inlining messing with the type + strct = $(Expr(:new, OneConstField, 1, 2)) + strct.b = 4 + strct.b = 5 + return strct.b +end +@test fully_eliminated(one_const_field_partial; retval=5) From b8c347aca8daf0826861450e8d857e01a9aeba01 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 5 May 2023 13:50:43 -0400 Subject: [PATCH 687/775] Thread lattice through tuple_tail_elem (#49643) --- base/compiler/abstractinterpretation.jl | 4 ++-- base/compiler/typelimits.jl | 2 +- base/compiler/typeutils.jl | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 6e671b032dbeb..0e5a99eedf2ee 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1539,7 +1539,7 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, si:: # This is vararg, we're not gonna be able to do any inlining, # drop the info info = nothing - tail = tuple_tail_elem(unwrapva(ct[end]), cti) + tail = tuple_tail_elem(typeinf_lattice(interp), unwrapva(ct[end]), cti) push!(ctypes´, push!(ct[1:(end - 1)], tail)) else push!(ctypes´, append!(ct[:], cti)) @@ -1562,7 +1562,7 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, si:: for i = 1:lct-1 cti = ct[i] if isvarargtype(cti) - ct[i] = tuple_tail_elem(unwrapva(cti), ct[(i+1):lct]) + ct[i] = tuple_tail_elem(typeinf_lattice(interp), unwrapva(cti), ct[(i+1):lct]) resize!(ct, i) break end diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 8e845d6f21888..191820951fae1 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -138,7 +138,7 @@ function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVec Q = Any[ tP[i] for i in 1:np ] if ltP > np # combine tp[np:end] into tP[np] using Vararg - Q[np] = tuple_tail_elem(Bottom, Any[ tP[i] for i in np:ltP ]) + Q[np] = tuple_tail_elem(fallback_lattice, Bottom, Any[ tP[i] for i in np:ltP ]) end for i = 1:np # now apply limit element-wise to Q diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index 98117fd7cb345..cff10b02ceafc 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -213,11 +213,11 @@ end _typename(union::UnionAll) = _typename(union.body) _typename(a::DataType) = Const(a.name) -function tuple_tail_elem(@nospecialize(init), ct::Vector{Any}) +function tuple_tail_elem(𝕃::AbstractLattice, @nospecialize(init), ct::Vector{Any}) t = init for x in ct # FIXME: this is broken: it violates subtyping relations and creates invalid types with free typevars - t = tmerge(t, unwraptv(unwrapva(x))) + t = tmerge(𝕃, t, unwraptv(unwrapva(x))) end return Vararg{widenconst(t)} end From 9b7d88b49bb9d414b6b0859b86c78d62daae62c0 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 5 May 2023 15:45:47 -0400 Subject: [PATCH 688/775] subtype: more conservative intersection of triangular variables (#49591) When a variable is used triangularly, we need to be careful not to propagate the lower bound implied by the other side to the upper bound implied by the invariant usage of the value--that is only legal when we are intersecting a variable that is used diagonally. Fix #49578 --- src/subtype.c | 28 +++++++++++------ test/subtype.jl | 80 ++++++++++++++++++++++++++++--------------------- 2 files changed, 65 insertions(+), 43 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 83b8a8413394d..2ff5f0f75d09d 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -73,7 +73,7 @@ typedef struct jl_varbinding_t { // let ub = var.ub ∩ type // 0 - var.ub <: type ? var : ub // 1 - var.ub = ub; return var - // 2 - either (var.ub = ub; return var), or return ub + // 2 - var.lb = lb; return ub int8_t constraintkind; int8_t intvalued; // intvalued: must be integer-valued; i.e. occurs as N in Vararg{_,N} int8_t limited; @@ -2646,14 +2646,24 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int return ub; } assert(bb->constraintkind == 2); - if (!jl_is_typevar(a)) { - if (ub == a && bb->lb != jl_bottom_type) - return ub; - else if (jl_egal(bb->ub, bb->lb)) - return ub; - set_bound(&bb->ub, ub, b, e); - } - return (jl_value_t*)b; + if (ub == a && bb->lb != jl_bottom_type) + return ub; + if (jl_egal(bb->ub, bb->lb)) + return ub; + if (is_leaf_bound(ub)) + set_bound(&bb->lb, ub, b, e); + // TODO: can we improve this bound by pushing a new variable into the environment + // and adding that to the lower bound of our variable? + //jl_value_t *ntv = NULL; + //JL_GC_PUSH2(&ntv, &ub); + //if (bb->innervars == NULL) + // bb->innervars = jl_alloc_array_1d(jl_array_any_type, 0); + //ntv = (jl_value_t*)jl_new_typevar(b->name, bb->lb, ub); + //jl_array_ptr_1d_push(bb->innervars, ntv); + //jl_value_t *lb = simple_join(b->lb, ntv); + //JL_GC_POP(); + //bb->lb = lb; + return ub; } // test whether `var` occurs inside constructors. `want_inv` tests only inside diff --git a/test/subtype.jl b/test/subtype.jl index 05ff82106bda0..4a3e55c039e94 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -759,8 +759,11 @@ function test_intersection() @testintersect((@UnionAll T Tuple{T, AbstractArray{T}}), Tuple{Int, Array{Number,1}}, Tuple{Int, Array{Number,1}}) + # TODO: improve this result + #@testintersect((@UnionAll S Tuple{S,Vector{S}}), (@UnionAll T<:Real Tuple{T,AbstractVector{T}}), + # (@UnionAll S<:Real Tuple{S,Vector{S}})) @testintersect((@UnionAll S Tuple{S,Vector{S}}), (@UnionAll T<:Real Tuple{T,AbstractVector{T}}), - (@UnionAll S<:Real Tuple{S,Vector{S}})) + (@UnionAll S<:Real Tuple{Real,Vector{S}})) # typevar corresponding to a type it will end up being neither greater than nor # less than @@ -819,9 +822,9 @@ function test_intersection() Tuple{Tuple{Vararg{Integer}}, Tuple{Integer,Integer}}, Tuple{Tuple{Integer,Integer}, Tuple{Integer,Integer}}) - #@test isequal_type(typeintersect((@UnionAll N Tuple{NTuple{N,Any},Array{Int,N}}), - # Tuple{Tuple{Int,Vararg{Int}},Array}), - # Tuple{Tuple{Int,Vararg{Int}},Array{Int,N}}) + @test isequal_type(typeintersect((@UnionAll N Tuple{NTuple{N,Any},Array{Int,N}}), + Tuple{Tuple{Int,Vararg{Int}},Array}), + @UnionAll N Tuple{Tuple{Int,Vararg{Int}},Array{Int,N}}) @testintersect((@UnionAll N Tuple{NTuple{N,Any},Array{Int,N}}), Tuple{Tuple{Int,Vararg{Int}},Array{Int,2}}, @@ -930,9 +933,10 @@ function test_intersection() # since this T is inside the invariant ctor Type{}, we allow T == Any here @testintersect((Type{Tuple{Vararg{T}}} where T), Type{Tuple}, Type{Tuple}) + # TODO: improve this @testintersect(Tuple{Type{S}, Tuple{Any, Vararg{Any}}} where S<:Tuple{Any, Vararg{Any}}, Tuple{Type{T}, T} where T, - Tuple{Type{S},S} where S<:Tuple{Any,Vararg{Any}}) + Tuple{Type{S}, Tuple{Any, Vararg{Any}}} where S<:Tuple{Any, Vararg{Any}}) # part of issue #20450 @testintersect(Tuple{Array{Ref{T}, 1}, Array{Pair{M, V}, 1}} where V where T where M, @@ -1079,8 +1083,7 @@ function test_intersection_properties() I2 = _type_intersect(S,T) @test isequal_type(I, I2) if i > length(easy_menagerie) || j > length(easy_menagerie) - # TODO: these cases give a conservative answer - @test issub(I, T) || issub(I, S) + # @test issub(I, T) || issub(I, S) else @test issub(I, T) && issub(I, S) end @@ -1601,7 +1604,7 @@ end Tuple{Type{A29955{T,TV,TM}}, TM} where {T,TV<:AbstractVector{T},TM<:M29955{T,TV}}, Tuple{Type{A29955{Float64,Array{Float64,1},TM}}, - TM} where TM<:M29955{Float64,Array{Float64,1}}) + M29955{Float64,Vector{Float64}}} where TM<:M29955{Float64,Array{Float64,1}}) let M = M29955{T,Vector{Float64}} where T @test M == (M29955{T,Vector{Float64}} where T) @test M{Float64} == M29955{Float64,Vector{Float64}} @@ -1619,9 +1622,9 @@ end Tuple{LT,R,I} where LT<:Union{I, R} where R<:Rational{I} where I<:Integer, Tuple{LT,Rational{Int},Int} where LT<:Union{Rational{Int},Int}) -#@testintersect(Tuple{Any,Tuple{Int},Int}, -# Tuple{LT,R,I} where LT<:Union{I, R} where R<:Tuple{I} where I<:Integer, -# Tuple{LT,Tuple{Int},Int} where LT<:Union{Tuple{Int},Int}) +@testintersect(Tuple{Any,Tuple{Int},Int}, + Tuple{LT,R,I} where LT<:Union{I, R} where R<:Tuple{I} where I<:Integer, + Tuple{LT,Tuple{Int},Int} where LT<:Union{Tuple{Int},Int}) # fails due to this: let U = Tuple{Union{LT, LT1},Union{R, R1},Int} where LT1<:R1 where R1<:Tuple{Int} where LT<:Int where R<:Tuple{Int}, U2 = Union{Tuple{LT,R,Int} where LT<:Int where R<:Tuple{Int}, Tuple{LT,R,Int} where LT<:R where R<:Tuple{Int}}, @@ -1638,9 +1641,10 @@ end # issue #31082 and #30741 @test typeintersect(Tuple{T, Ref{T}, T} where T, Tuple{Ref{S}, S, S} where S) != Union{} +# TODO: improve this bound @testintersect(Tuple{Pair{B,C},Union{C,Pair{B,C}},Union{B,Real}} where {B,C}, Tuple{Pair{B,C},C,C} where {B,C}, - Tuple{Pair{B,C},C,C} where C<:Union{Real, B} where B) + Tuple{Pair{B,C}, Union{Pair{B,C},C},Union{Real,B}} where {B,C}) f31082(::Pair{B, C}, ::Union{C, Pair{B, C}}, ::Union{B, Real}) where {B, C} = 0 f31082(::Pair{B, C}, ::C, ::C) where {B, C} = 1 @test f31082(""=>1, 2, 3) == 1 @@ -1904,12 +1908,14 @@ end # issue #22787 @testintersect(Tuple{Type{Q}, Q, Ref{Q}} where Q<:Ref, - Tuple{Type{S}, Union{Ref{S}, Ref{R}}, R} where R where S, - !Union{}) + Tuple{Type{S}, Union{Ref{S}, Ref{R}}, R} where R where S, + Tuple{Type{Q}, Union{Ref{Q}, Ref{R}}, Ref{Q}} where {Q<:Ref, R}) # likely suboptimal -t = typeintersect(Tuple{Type{T}, T, Ref{T}} where T, +let t = typeintersect(Tuple{Type{T}, T, Ref{T}} where T, Tuple{Type{S}, Ref{S}, S} where S) -@test_broken t != Union{} # optimal solution: Tuple{Type{T}, Ref{T}, Ref{T}} where T>:Ref + @test_broken t == Tuple{Type{T}, Ref{T}, Ref{T}} where T>:Ref + @test t == Tuple{Type{T}, Ref{T}, Ref{T}} where T +end # issue #38279 t = typeintersect(Tuple{<:Array{T, N}, Val{T}} where {T<:Real, N}, @@ -1954,9 +1960,11 @@ let A = Tuple{Type{T} where T<:Ref, Ref, Union{T, Union{Ref{T}, T}} where T<:Ref B = Tuple{Type{T}, Ref{T}, Union{Int, Ref{T}, T}} where T # this was a case where <: disagreed with === (due to a badly-normalized type) I = _type_intersect(B, A) - @test I == _type_intersect(B, A) == Union{Tuple{Type{T}, Ref{T}, Ref{T}} where T<:Ref, Tuple{Type{T}, Ref{T}, T} where T<:Ref} + @test_broken I == Union{Tuple{Type{T}, Ref{T}, Ref{T}} where T<:Ref, Tuple{Type{T}, Ref{T}, T} where T<:Ref} + @test I == _type_intersect(B, A) == Tuple{Type{T}, Ref{T}, Ref} where T<:Ref I = typeintersect(B, A) - @test I == typeintersect(B, A) == Tuple{Type{T}, Ref{T}, Union{Ref{T}, T}} where T<:Ref + @test_broken I == Tuple{Type{T}, Ref{T}, Union{Ref{T}, T}} where T<:Ref + @test I == typeintersect(B, A) <: Tuple{Type{T}, Ref{T}, Ref} where T<:Ref I = _type_intersect(A, B) @test !Base.has_free_typevars(I) @@ -2026,8 +2034,8 @@ let A = Tuple{Ref{T}, Vararg{T}} where T, I = typeintersect(A, B) Ts = (Tuple{Ref{Int}, Int, Int}, Tuple{Ref{Ref{Int}}, Ref{Int}, Ref{Int}}) @test I != Union{} - @test I <: A - @test_broken I <: B + @test_broken I <: A + @test I <: B for T in Ts if T <: A && T <: B @test T <: I @@ -2035,8 +2043,8 @@ let A = Tuple{Ref{T}, Vararg{T}} where T, end J = typeintersect(A, C) @test J != Union{} - @test J <: A - @test_broken J <: C + @test_broken J <: A + @test J <: C for T in Ts if T <: A && T <: C @test T <: J @@ -2045,9 +2053,13 @@ let A = Tuple{Ref{T}, Vararg{T}} where T, end let A = Tuple{Dict{I,T}, I, T} where T where I, - B = Tuple{AbstractDict{I,T}, T, I} where T where I - # TODO: we should probably have I == T here - @test typeintersect(A, B) == Tuple{Dict{I,T}, I, T} where {I, T} + B = Tuple{AbstractDict{I,T}, T, I} where T where I, + I = typeintersect(A, B) + # TODO: we should probably have something approaching I == T here, + # though note something more complex is needed since the intersection must also include types such as; + # Tuple{Dict{Integer,Any}, Integer, Int} + @test_broken I <: A && I <: B + @test I == typeintersect(B, A) == Tuple{Dict{I, T}, Any, Any} where {I, T} end let A = Tuple{UnionAll, Vector{Any}}, @@ -2378,7 +2390,7 @@ let S = Tuple{Type{T1}, T1, Val{T1}} where T1<:(Val{S1} where S1<:Val), @test I1 !== Union{} && I2 !== Union{} @test_broken I1 <: S @test_broken I2 <: T - @test I2 <: S + @test_broken I2 <: S @test_broken I2 <: T end @@ -2512,13 +2524,13 @@ end let A = Tuple{Type{T}, T, Val{T}} where T, B = Tuple{Type{S}, Val{S}, Val{S}} where S - @test_broken typeintersect(A, B) != Union{} - # optimal = Tuple{Type{T}, Val{T}, Val{T}} where T>:Val + @test_broken typeintersect(A, B) == Tuple{Type{T}, Val{T}, Val{T}} where T>:Val + @test typeintersect(A, B) <: Tuple{Type{T}, Val{T}, Val{T}} where T end let A = Tuple{Type{T}, T, Val{T}} where T<:Val, B = Tuple{Type{S}, Val{S}, Val{S}} where S<:Val - @test_broken typeintersect(A, B) != Union{} - # optimal = Tuple{Type{Val}, Val{Val}, Val{Val}} + @test_broken typeintersect(A, B) == Tuple{Type{Val}, Val{Val}, Val{Val}} + @test typeintersect(A, B) <: Tuple{Type{T}, Val{T}, Val{T}} where T<:Val end let A = Tuple{Type{T}, T, Val{T}} where T<:Val, B = Tuple{Type{S}, Val{S}, Val{S}} where S<:Val{A} where A @@ -2526,12 +2538,12 @@ let A = Tuple{Type{T}, T, Val{T}} where T<:Val, end let A = Tuple{Type{T}, T, Val{T}} where T<:Val{<:Val}, B = Tuple{Type{S}, Val{S}, Val{S}} where S<:Val - @test_broken typeintersect(A, B) != Union{} - # optimal = Tuple{Type{Val{<:Val}}, Val{Val{<:Val}}, Val{Val{<:Val}}} + @test_broken typeintersect(A, B) == Tuple{Type{Val{<:Val}}, Val{Val{<:Val}}, Val{Val{<:Val}}} + @test typeintersect(A, B) <: Tuple{Type{T}, Val{T}, Val{T}} where T<:(Val{<:Val}) end let T = Tuple{Union{Type{T}, Type{S}}, Union{Val{T}, Val{S}}, Union{Val{T}, S}} where T<:Val{A} where A where S<:Val, S = Tuple{Type{T}, T, Val{T}} where T<:(Val{S} where S<:Val) # optimal = Union{}? - @test typeintersect(T, S) == Tuple{Type{T}, T, Val{T}} where T<:(Val{S} where S<:Val) - @test typeintersect(S, T) == Tuple{Union{Type{T}, Type{T1}}, Union{Val{T1}, Val{S1}, T}, Union{S, S1}} where {T<:(Val{S} where S<:Val), S<:Val{T}, T1<:Val, S1<:Val{T1}} + @test typeintersect(T, S) == Tuple{Type{A}, Union{Val{A}, Val{S} where S<:Union{Val, A}, Val{x} where x<:Val, Val{x} where x<:Union{Val, A}}, Val{A}} where A<:(Val{S} where S<:Val) + @test typeintersect(S, T) == Tuple{Type{T}, Union{Val{T}, Val{S}}, Val{T}} where {T<:Val, S<:(Union{Val{A}, Val} where A)} end From 6296f57c756887719ee09d242f39f72479b267aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Belmant?= Date: Sat, 6 May 2023 05:31:16 +0200 Subject: [PATCH 689/775] Test: improve robustness of stacktrace scrubbing (#49646) --- 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 811477e7d767c..392b736c09837 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -65,7 +65,7 @@ function test_callsite(bt, file_ts, file_t) # and only traverse parts of the backtrace which we haven't traversed before. # The order will always be -> `@test` -> `@testset`. internal = @something(macrocall_location(bt, @__FILE__), return nothing) - test = internal - 1 + findfirst(ip -> any(frame -> in_file(frame, file_t), StackTraces.lookup(ip)), @view bt[internal:end])::Int + test = internal - 1 + @something(findfirst(ip -> any(frame -> in_file(frame, file_t), StackTraces.lookup(ip)), @view bt[internal:end]), return nothing) testset = test - 1 + @something(macrocall_location(@view(bt[test:end]), file_ts), return nothing) # If stacktrace locations differ, include frames until the `@testset` appears. From 7ea902ced64d5cb23eb4f18e25118b059fe0739b Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 5 May 2023 23:31:57 -0400 Subject: [PATCH 690/775] Extend NamedTuple special case to PartialStruct (#49641) A little later in the `precise_container_type` function, we have a special case for extracting the iteration type from a `NamedTuple`, treating it the same as `Tuple`. However, as a result of this special case we were accidentally discarding any constant information that may have been in a `PartialStruct`. If we didn't have this special case, `abstract_iteration` would have taken care of it properly. To fix this, simply add the same special case to the `PartialStruct` check at the top of the function. Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> --- base/compiler/abstractinterpretation.jl | 10 +++++++--- test/compiler/inference.jl | 13 +++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 0e5a99eedf2ee..a62f76017c2dc 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1321,14 +1321,18 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) sv::AbsIntState) if isa(typ, PartialStruct) widet = typ.typ - if isa(widet, DataType) && widet.name === Tuple.name - return AbstractIterationResult(typ.fields, nothing) + if isa(widet, DataType) + if widet.name === Tuple.name + return AbstractIterationResult(typ.fields, nothing) + elseif widet.name === _NAMEDTUPLE_NAME + return AbstractIterationResult(typ.fields, nothing) + end end end if isa(typ, Const) val = typ.val - if isa(val, SimpleVector) || isa(val, Tuple) + if isa(val, SimpleVector) || isa(val, Tuple) || isa(val, NamedTuple) return AbstractIterationResult(Any[ Const(val[i]) for i in 1:length(val) ], nothing) # avoid making a tuple Generator here! end end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index d25acd4d5a6ad..1b137d1d8f661 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -4858,3 +4858,16 @@ end) == Type{Nothing} @test Base.return_types() do Core.Compiler.return_type(Tuple{typeof(+), Int, Int}) end |> only == Type{Int} + +# Test that NamedTuple abstract iteration works for PartialStruct/Const +function nt_splat_const() + nt = (; x=1, y=2) + Val{tuple(nt...)[2]}() +end +@test @inferred(nt_splat_const()) == Val{2}() + +function nt_splat_partial(x::Int) + nt = (; x, y=2) + Val{tuple(nt...)[2]}() +end +@test @inferred(nt_splat_partial(42)) == Val{2}() From 67dd21c8a7b2c67bf82b1cd183b5785f8d46be72 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 6 May 2023 00:27:06 -0400 Subject: [PATCH 691/775] codegen: move union optimizations into emit_exactly_isa where emit_f_is expects them (#49655) --- src/cgutils.cpp | 61 ++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 380c6f7bc0be0..dc20167e14f95 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1434,6 +1434,35 @@ static bool can_optimize_isa_union(jl_uniontype_t *type) static Value *emit_exactly_isa(jl_codectx_t &ctx, const jl_cgval_t &arg, jl_value_t *dt) { assert(jl_is_concrete_type(dt)); + if (arg.TIndex) { + unsigned tindex = get_box_tindex((jl_datatype_t*)dt, arg.typ); + if (tindex > 0) { + // optimize more when we know that this is a split union-type where tindex = 0 is invalid + Value *xtindex = ctx.builder.CreateAnd(arg.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x7f)); + return ctx.builder.CreateICmpEQ(xtindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), tindex)); + } + else if (arg.Vboxed) { + // test for (arg.TIndex == 0x80 && typeof(arg.V) == type) + Value *isboxed = ctx.builder.CreateICmpEQ(arg.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)); + BasicBlock *currBB = ctx.builder.GetInsertBlock(); + BasicBlock *isaBB = BasicBlock::Create(ctx.builder.getContext(), "isa", ctx.f); + BasicBlock *postBB = BasicBlock::Create(ctx.builder.getContext(), "post_isa", ctx.f); + ctx.builder.CreateCondBr(isboxed, isaBB, postBB); + ctx.builder.SetInsertPoint(isaBB); + Value *istype_boxed = ctx.builder.CreateICmpEQ(emit_typeof(ctx, arg.Vboxed, false), + track_pjlvalue(ctx, literal_pointer_val(ctx, dt))); + ctx.builder.CreateBr(postBB); + isaBB = ctx.builder.GetInsertBlock(); // could have changed + ctx.builder.SetInsertPoint(postBB); + PHINode *istype = ctx.builder.CreatePHI(getInt1Ty(ctx.builder.getContext()), 2); + istype->addIncoming(ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0), currBB); + istype->addIncoming(istype_boxed, isaBB); + return istype; + } else { + // handle the case where we know that `arg` is unboxed (but of unknown type), but that concrete type `type` cannot be unboxed + return ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0); + } + } return ctx.builder.CreateICmpEQ( emit_typeof_boxed(ctx, arg), track_pjlvalue(ctx, literal_pointer_val(ctx, dt))); @@ -1521,38 +1550,8 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 0)), false); } // tests for isa concretetype can be handled with pointer comparisons - if (jl_is_concrete_type(intersected_type)) { - if (x.TIndex) { - unsigned tindex = get_box_tindex((jl_datatype_t*)intersected_type, x.typ); - if (tindex > 0) { - // optimize more when we know that this is a split union-type where tindex = 0 is invalid - Value *xtindex = ctx.builder.CreateAnd(x.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x7f)); - return std::make_pair(ctx.builder.CreateICmpEQ(xtindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), tindex)), false); - } - else if (x.Vboxed) { - // test for (x.TIndex == 0x80 && typeof(x.V) == type) - Value *isboxed = ctx.builder.CreateICmpEQ(x.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)); - BasicBlock *currBB = ctx.builder.GetInsertBlock(); - BasicBlock *isaBB = BasicBlock::Create(ctx.builder.getContext(), "isa", ctx.f); - BasicBlock *postBB = BasicBlock::Create(ctx.builder.getContext(), "post_isa", ctx.f); - ctx.builder.CreateCondBr(isboxed, isaBB, postBB); - ctx.builder.SetInsertPoint(isaBB); - Value *istype_boxed = ctx.builder.CreateICmpEQ(emit_typeof(ctx, x.Vboxed, false), - track_pjlvalue(ctx, literal_pointer_val(ctx, intersected_type))); - ctx.builder.CreateBr(postBB); - isaBB = ctx.builder.GetInsertBlock(); // could have changed - ctx.builder.SetInsertPoint(postBB); - PHINode *istype = ctx.builder.CreatePHI(getInt1Ty(ctx.builder.getContext()), 2); - istype->addIncoming(ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0), currBB); - istype->addIncoming(istype_boxed, isaBB); - return std::make_pair(istype, false); - } else { - // handle the case where we know that `x` is unboxed (but of unknown type), but that concrete type `type` cannot be unboxed - return std::make_pair(ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0), false); - } - } + if (jl_is_concrete_type(intersected_type)) return std::make_pair(emit_exactly_isa(ctx, x, intersected_type), false); - } jl_datatype_t *dt = (jl_datatype_t*)jl_unwrap_unionall(intersected_type); if (jl_is_datatype(dt) && !dt->name->abstract && jl_subtype(dt->name->wrapper, type)) { // intersection is a supertype of all instances of its constructor, From 9bb0731e95bef3ce89bafde570e7d35be9e6088f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 6 May 2023 00:27:35 -0400 Subject: [PATCH 692/775] fix some SCC cycle mistakes (#49654) --- src/gf.c | 2 +- src/staticdata_utils.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/gf.c b/src/gf.c index baaafab3814f2..00a51f582a597 100644 --- a/src/gf.c +++ b/src/gf.c @@ -3737,7 +3737,7 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, // now look for a third method m3 outside of this ambiguity group that fully resolves this intersection size_t k; for (k = agid; k > 0; k--) { - jl_method_match_t *matc3 = (jl_method_match_t*)jl_array_ptr_ref(env.t, k); + jl_method_match_t *matc3 = (jl_method_match_t*)jl_array_ptr_ref(env.t, k - 1); jl_method_t *m3 = matc3->method; if ((jl_subtype(ti, m3->sig) || (isect2 && jl_subtype(isect2, m3->sig))) && jl_type_morespecific((jl_value_t*)m3->sig, (jl_value_t*)m->sig) diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index ca5cf9100f5d7..03dc3a82acd93 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -178,7 +178,7 @@ static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited, int depth = stack->len; *bp = (void*)((char*)HT_NOTFOUND + 4 + depth); // preliminarily mark as in-progress size_t i = 0, n = jl_array_len(mi->backedges); - int cycle = 0; + int cycle = depth; while (i < n) { jl_method_instance_t *be; i = get_next_edge(mi->backedges, i, NULL, &be); @@ -194,7 +194,7 @@ static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited, assert(cycle); } } - if (!found && cycle && cycle != depth) + if (!found && cycle != depth) return cycle + 3; // If we are the top of the current cycle, now mark all other parts of // our cycle with what we found. @@ -1002,6 +1002,7 @@ static int jl_verify_graph_edge(size_t *maxvalids2_data, jl_array_t *edges, size assert(jl_typeis((jl_value_t*)callee_ids, jl_array_int32_type)); int32_t *idxs = (int32_t*)jl_array_data(callee_ids); size_t i, n = jl_array_len(callee_ids); + cycle = depth; for (i = idxs[0] + 1; i < n; i++) { int32_t childidx = idxs[i]; int child_cycle = jl_verify_graph_edge(maxvalids2_data, edges, childidx, visited, stack); @@ -1020,7 +1021,7 @@ static int jl_verify_graph_edge(size_t *maxvalids2_data, jl_array_t *edges, size } } size_t max_valid = maxvalids2_data[idx]; - if (max_valid != 0 && cycle && cycle != depth) + if (max_valid != 0 && cycle != depth) return cycle; // If we are the top of the current cycle, now mark all other parts of // our cycle with what we found. From 633d1aea3a83bc74edff3a5d6963685775aa4004 Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Fri, 5 May 2023 21:31:06 -0700 Subject: [PATCH 693/775] Define a method for `hash(::Type, ::UInt)` (#49636) Currently, `hash(::Type, ::UInt)` uses `objectid`, which can have some odd behavior for types: in particular, subsequent identical type-valued variable definitions can have `objectid`s which differ from the first such definition. This has some bizarre downstream effects when e.g. using types as the values of a `Set` or the keys of a `Dict`. See issue 49620 for examples. There is an internal `type_hash` C function used for caching types but isn't exposed to Julia, as Jameson pointed out in the linked issue. This commit exposes it as `jl_type_hash` which is then used via `ccall` to define a method `hash(::Type, ::UInt)`. This method then fixes #49620. Note, however, that this does not affect the differing `objectid`s for otherwise identical types. --- base/hashing.jl | 1 + src/jltypes.c | 8 ++++++++ src/julia.h | 1 + test/hashing.jl | 7 +++++++ 4 files changed, 17 insertions(+) diff --git a/base/hashing.jl b/base/hashing.jl index 1aadd8b7e46a9..5dbae09123bd6 100644 --- a/base/hashing.jl +++ b/base/hashing.jl @@ -29,6 +29,7 @@ See also: [`objectid`](@ref), [`Dict`](@ref), [`Set`](@ref). """ hash(x::Any) = hash(x, zero(UInt)) hash(w::WeakRef, h::UInt) = hash(w.value, h) +hash(T::Type, h::UInt) = hash_uint(3h - ccall(:jl_type_hash, UInt, (Any,), T)) ## hashing general objects ## diff --git a/src/jltypes.c b/src/jltypes.c index 902e1e557f7e0..85255f9247439 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1575,6 +1575,14 @@ static unsigned type_hash(jl_value_t *kj, int *failed) JL_NOTSAFEPOINT } } +JL_DLLEXPORT uintptr_t jl_type_hash(jl_value_t *v) JL_NOTSAFEPOINT +{ + // NOTE: The value of `failed` is purposefully ignored here. The parameter is relevant + // for other parts of the internal algorithm but not for exposing to the Julia side. + int failed = 0; + return type_hash(v, &failed); +} + static unsigned typekey_hash(jl_typename_t *tn, jl_value_t **key, size_t n, int nofail) JL_NOTSAFEPOINT { if (tn == jl_type_typename && key[0] == jl_bottom_type) diff --git a/src/julia.h b/src/julia.h index 0a542dd8b6bcb..89fea54fc428f 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1403,6 +1403,7 @@ JL_DLLEXPORT int jl_egal__bits(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_v JL_DLLEXPORT int jl_egal__special(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, jl_datatype_t *dt) JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_egal__unboxed(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, jl_datatype_t *dt) JL_NOTSAFEPOINT; JL_DLLEXPORT uintptr_t jl_object_id(jl_value_t *v) JL_NOTSAFEPOINT; +JL_DLLEXPORT uintptr_t jl_type_hash(jl_value_t *v) JL_NOTSAFEPOINT; STATIC_INLINE int jl_egal__unboxed_(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, jl_datatype_t *dt) JL_NOTSAFEPOINT { diff --git a/test/hashing.jl b/test/hashing.jl index b672c3de817c6..943109924f280 100644 --- a/test/hashing.jl +++ b/test/hashing.jl @@ -295,3 +295,10 @@ if Sys.WORD_SIZE >= 64 objectid(s) end end + +# Issue #49620 +let t1 = Tuple{AbstractVector,AbstractVector{<:Integer},UnitRange{<:Integer}}, + t2 = Tuple{AbstractVector,AbstractVector{<:Integer},UnitRange{<:Integer}} + @test hash(t1) == hash(t2) + @test length(Set{Type}([t1, t2])) == 1 +end From e26b63cf41cff80783621ed193e794be5978be20 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 5 May 2023 14:35:48 -0400 Subject: [PATCH 694/775] convert some typeof tags to small integers The value here is two-fold. One, we speed up sysimg load times slightly, since we can entirely skip relocations for these values and avoid faulting in some writable pages. This should let us (later) move more content into the ConstData section, such as String objects. Secondly, some type-manipulation native code becomes simpler, since it is based on small consecutive constants instead of requiring extra pointer loads to check for pointer identity. This is similar to the existing small-union optimization, but for type tags, and only for specific ones. This makes use of the fact that the zero page (about 4096 byes) cannot be allocated in the usual C memory model, which lets us define up to 255 of these special types, after taking into account the 4 gc bits. As a current implementation limitation for performance, these cannot be parametric types. Note there are probably more places in Base that can be updated to use `jl_typetagof` instead of `jl_typeof`, where we know if it is a type or tag. For example, this optimize most of builtins.c file, except for the `jl_object_id` function. --- base/boot.jl | 19 +- cli/jl_exports.h | 4 + src/aotcompile.cpp | 18 +- src/array.c | 8 +- src/ast.c | 22 +-- src/builtins.c | 117 +++++++---- src/ccall.cpp | 14 +- src/cgutils.cpp | 212 ++++++++++++-------- src/codegen.cpp | 84 +++++--- src/datatype.c | 125 ++++++------ src/gc-debug.c | 8 +- src/gc.c | 360 ++++++++++++++++++---------------- src/gf.c | 2 +- src/init.c | 76 +------ src/interpreter.c | 6 +- src/intrinsics.cpp | 9 +- src/ircode.c | 35 ++-- src/jl_exported_funcs.inc | 2 +- src/jltypes.c | 144 ++++++++++++-- src/julia.expmap | 4 +- src/julia.h | 194 ++++++++++++------ src/julia_internal.h | 11 +- src/llvm-late-gc-lowering.cpp | 4 +- src/method.c | 12 +- src/module.c | 1 + src/partr.c | 2 +- src/processor.cpp | 2 + src/processor.h | 5 +- src/rtutils.c | 8 +- src/simplevector.c | 4 + src/stackwalk.c | 2 +- src/staticdata.c | 56 +++--- src/staticdata_utils.c | 4 +- src/subtype.c | 4 +- src/symbol.c | 4 +- src/task.c | 2 + src/toplevel.c | 4 +- test/ccall.jl | 2 +- 38 files changed, 937 insertions(+), 653 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index b9b54a8051fb0..9d44b98d46881 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -245,7 +245,6 @@ ccall(:jl_toplevel_eval_in, Any, (Any, Any), (f::typeof(Typeof))(x) = ($(_expr(:meta,:nospecialize,:x)); isa(x,Type) ? Type{x} : typeof(x)) end) - macro nospecialize(x) _expr(:meta, :nospecialize, x) end @@ -256,7 +255,15 @@ TypeVar(n::Symbol, @nospecialize(lb), @nospecialize(ub)) = _typevar(n, lb, ub) UnionAll(v::TypeVar, @nospecialize(t)) = ccall(:jl_type_unionall, Any, (Any, Any), v, t) -const Vararg = ccall(:jl_toplevel_eval_in, Any, (Any, Any), Core, _expr(:new, TypeofVararg)) +# simple convert for use by constructors of types in Core +# note that there is no actual conversion defined here, +# so the methods and ccall's in Core aren't permitted to use convert +convert(::Type{Any}, @nospecialize(x)) = x +convert(::Type{T}, x::T) where {T} = x +cconvert(::Type{T}, x) where {T} = convert(T, x) +unsafe_convert(::Type{T}, x::T) where {T} = x + +const Vararg = ccall(:jl_wrap_vararg, Any, (Int, Int), 0, 0) # dispatch token indicating a kwarg (keyword sorter) call function kwcall end @@ -448,14 +455,6 @@ function _Task(@nospecialize(f), reserved_stack::Int, completion_future) return ccall(:jl_new_task, Ref{Task}, (Any, Any, Int), f, completion_future, reserved_stack) end -# simple convert for use by constructors of types in Core -# note that there is no actual conversion defined here, -# so the methods and ccall's in Core aren't permitted to use convert -convert(::Type{Any}, @nospecialize(x)) = x -convert(::Type{T}, x::T) where {T} = x -cconvert(::Type{T}, x) where {T} = convert(T, x) -unsafe_convert(::Type{T}, x::T) where {T} = x - _is_internal(__module__) = __module__ === Core # can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping) macro _foldable_meta() diff --git a/cli/jl_exports.h b/cli/jl_exports.h index e9be7c6f2f819..d28958c097edb 100644 --- a/cli/jl_exports.h +++ b/cli/jl_exports.h @@ -16,6 +16,10 @@ JL_EXPORTED_DATA_POINTERS(XX) JL_EXPORTED_DATA_SYMBOLS(XX) #undef XX +// define a copy of exported data +#define jl_max_tags 64 +JL_DLLEXPORT void *small_typeof[(jl_max_tags << 4) / sizeof(void*)]; // 16-bit aligned, like the GC + // Declare list of exported functions (sans type) #define XX(name) JL_DLLEXPORT void name(void); typedef void (anonfunc)(void); diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index b89cdf550171f..0c2aa4feb678d 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1575,6 +1575,14 @@ void jl_dump_native_impl(void *native_code, GlobalVariable::ExternalLinkage, jlRTLD_DEFAULT_var, "jl_RTLD_DEFAULT_handle_pointer"), TheTriple); + + // let the compiler know we are going to internalize a copy of this, + // if it has a current usage with ExternalLinkage + auto small_typeof_copy = dataM->getGlobalVariable("small_typeof"); + if (small_typeof_copy) { + small_typeof_copy->setVisibility(GlobalValue::HiddenVisibility); + small_typeof_copy->setDSOLocal(true); + } } // Reserve space for the output files and names @@ -1651,13 +1659,21 @@ void jl_dump_native_impl(void *native_code, auto shards = emit_shard_table(*sysimageM, T_size, T_psize, threads); auto ptls = emit_ptls_table(*sysimageM, T_size, T_psize); auto header = emit_image_header(*sysimageM, threads, nfvars, ngvars); - auto AT = ArrayType::get(T_psize, 4); + auto AT = ArrayType::get(T_size, sizeof(small_typeof) / sizeof(void*)); + auto small_typeof_copy = new GlobalVariable(*sysimageM, AT, false, + GlobalVariable::ExternalLinkage, + Constant::getNullValue(AT), + "small_typeof"); + small_typeof_copy->setVisibility(GlobalValue::HiddenVisibility); + small_typeof_copy->setDSOLocal(true); + AT = ArrayType::get(T_psize, 5); auto pointers = new GlobalVariable(*sysimageM, AT, false, GlobalVariable::ExternalLinkage, ConstantArray::get(AT, { ConstantExpr::getBitCast(header, T_psize), ConstantExpr::getBitCast(shards, T_psize), ConstantExpr::getBitCast(ptls, T_psize), + ConstantExpr::getBitCast(small_typeof_copy, T_psize), ConstantExpr::getBitCast(target_ids, T_psize) }), "jl_image_pointers"); diff --git a/src/array.c b/src/array.c index 0b582296774b5..ae730bed7c4e8 100644 --- a/src/array.c +++ b/src/array.c @@ -509,7 +509,7 @@ JL_DLLEXPORT jl_value_t *jl_alloc_string(size_t len) jl_throw(jl_memory_exception); s = jl_gc_big_alloc_noinline(ptls, allocsz); } - jl_set_typeof(s, jl_string_type); + jl_set_typetagof(s, jl_string_tag, 0); maybe_record_alloc_to_profile(s, len, jl_string_type); *(size_t*)s = len; jl_string_data(s)[len] = 0; @@ -1255,7 +1255,7 @@ JL_DLLEXPORT void jl_array_ptr_copy(jl_array_t *dest, void **dest_p, JL_DLLEXPORT void jl_array_ptr_1d_push(jl_array_t *a, jl_value_t *item) { - assert(jl_typeis(a, jl_array_any_type)); + assert(jl_typetagis(a, jl_array_any_type)); jl_array_grow_end(a, 1); size_t n = jl_array_nrows(a); jl_array_ptr_set(a, n - 1, item); @@ -1263,8 +1263,8 @@ JL_DLLEXPORT void jl_array_ptr_1d_push(jl_array_t *a, jl_value_t *item) JL_DLLEXPORT void jl_array_ptr_1d_append(jl_array_t *a, jl_array_t *a2) { - assert(jl_typeis(a, jl_array_any_type)); - assert(jl_typeis(a2, jl_array_any_type)); + assert(jl_typetagis(a, jl_array_any_type)); + assert(jl_typetagis(a2, jl_array_any_type)); size_t i; size_t n = jl_array_nrows(a); size_t n2 = jl_array_nrows(a2); diff --git a/src/ast.c b/src/ast.c index 08c493c75b985..1574f920e06e9 100644 --- a/src/ast.c +++ b/src/ast.c @@ -700,11 +700,11 @@ static value_t julia_to_scm_noalloc(fl_context_t *fl_ctx, jl_value_t *v, int che if (julia_to_scm_noalloc1(fl_ctx, v, &retval)) return retval; assert(!jl_is_expr(v) && - !jl_typeis(v, jl_linenumbernode_type) && - !jl_typeis(v, jl_gotonode_type) && - !jl_typeis(v, jl_quotenode_type) && - !jl_typeis(v, jl_newvarnode_type) && - !jl_typeis(v, jl_globalref_type)); + !jl_typetagis(v, jl_linenumbernode_type) && + !jl_typetagis(v, jl_gotonode_type) && + !jl_typetagis(v, jl_quotenode_type) && + !jl_typetagis(v, jl_newvarnode_type) && + !jl_typetagis(v, jl_globalref_type)); return julia_to_scm_noalloc2(fl_ctx, v, check_valid); } @@ -745,7 +745,7 @@ static value_t julia_to_scm_(fl_context_t *fl_ctx, jl_value_t *v, int check_vali // GC Note: jl_fieldref(v, 0) allocates for GotoNode // but we don't need a GC root here because julia_to_list2_noalloc // shouldn't allocate in this case. - if (jl_typeis(v, jl_linenumbernode_type)) { + if (jl_typetagis(v, jl_linenumbernode_type)) { jl_value_t *file = jl_fieldref_noalloc(v,1); jl_value_t *line = jl_fieldref(v,0); value_t args = julia_to_list2_noalloc(fl_ctx, line, file, check_valid); @@ -755,13 +755,13 @@ static value_t julia_to_scm_(fl_context_t *fl_ctx, jl_value_t *v, int check_vali fl_free_gc_handles(fl_ctx, 1); return scmv; } - if (jl_typeis(v, jl_gotonode_type)) + if (jl_typetagis(v, jl_gotonode_type)) return julia_to_list2_noalloc(fl_ctx, (jl_value_t*)jl_goto_sym, jl_fieldref(v,0), check_valid); - if (jl_typeis(v, jl_quotenode_type)) + if (jl_typetagis(v, jl_quotenode_type)) return julia_to_list2(fl_ctx, (jl_value_t*)jl_inert_sym, jl_fieldref_noalloc(v,0), 0); - if (jl_typeis(v, jl_newvarnode_type)) + if (jl_typetagis(v, jl_newvarnode_type)) return julia_to_list2_noalloc(fl_ctx, (jl_value_t*)jl_newvar_sym, jl_fieldref(v,0), check_valid); - if (jl_typeis(v, jl_globalref_type)) { + if (jl_typetagis(v, jl_globalref_type)) { jl_module_t *m = jl_globalref_mod(v); jl_sym_t *sym = jl_globalref_name(v); if (m == jl_core_module) @@ -1011,7 +1011,7 @@ static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule // __source__ argument jl_value_t *lno = jl_array_ptr_ref(args, 1); margs[1] = lno; - if (!jl_typeis(lno, jl_linenumbernode_type)) { + if (!jl_typetagis(lno, jl_linenumbernode_type)) { margs[1] = jl_new_struct(jl_linenumbernode_type, jl_box_long(0), jl_nothing); } margs[2] = (jl_value_t*)inmodule; diff --git a/src/builtins.c b/src/builtins.c index d423e6c934780..799db9afcf685 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -35,8 +35,8 @@ extern "C" { static int bits_equal(const void *a, const void *b, int sz) JL_NOTSAFEPOINT { switch (sz) { - case 1: return *(int8_t*)a == *(int8_t*)b; - // Let compiler constant folds the following. + case 1: return *(uint8_t*)a == *(uint8_t*)b; + // Let compiler constant folds the following, though we may not know alignment of them case 2: return memcmp(a, b, 2) == 0; case 4: return memcmp(a, b, 4) == 0; case 8: return memcmp(a, b, 8) == 0; @@ -147,10 +147,10 @@ static int egal_types(const jl_value_t *a, const jl_value_t *b, jl_typeenv_t *en { if (a == b) return 1; - jl_datatype_t *dt = (jl_datatype_t*)jl_typeof(a); - if (dt != (jl_datatype_t*)jl_typeof(b)) + uintptr_t dtag = jl_typetagof(a); + if (dtag != jl_typetagof(b)) return 0; - if (dt == jl_datatype_type) { + if (dtag == jl_datatype_tag << 4) { jl_datatype_t *dta = (jl_datatype_t*)a; jl_datatype_t *dtb = (jl_datatype_t*)b; if (dta->name != dtb->name) @@ -164,7 +164,7 @@ static int egal_types(const jl_value_t *a, const jl_value_t *b, jl_typeenv_t *en } return 1; } - if (dt == jl_tvar_type) { + if (dtag == jl_tvar_tag << 4) { jl_typeenv_t *pe = env; while (pe != NULL) { if (pe->var == (jl_tvar_t*)a) @@ -173,7 +173,7 @@ static int egal_types(const jl_value_t *a, const jl_value_t *b, jl_typeenv_t *en } return 0; } - if (dt == jl_unionall_type) { + if (dtag == jl_unionall_tag << 4) { jl_unionall_t *ua = (jl_unionall_t*)a; jl_unionall_t *ub = (jl_unionall_t*)b; if (tvar_names && ua->var->name != ub->var->name) @@ -183,11 +183,11 @@ static int egal_types(const jl_value_t *a, const jl_value_t *b, jl_typeenv_t *en jl_typeenv_t e = { ua->var, (jl_value_t*)ub->var, env }; return egal_types(ua->body, ub->body, &e, tvar_names); } - if (dt == jl_uniontype_type) { + if (dtag == jl_uniontype_tag << 4) { return egal_types(((jl_uniontype_t*)a)->a, ((jl_uniontype_t*)b)->a, env, tvar_names) && egal_types(((jl_uniontype_t*)a)->b, ((jl_uniontype_t*)b)->b, env, tvar_names); } - if (dt == jl_vararg_type) { + if (dtag == jl_vararg_tag << 4) { jl_vararg_t *vma = (jl_vararg_t*)a; jl_vararg_t *vmb = (jl_vararg_t*)b; jl_value_t *vmaT = vma->T ? vma->T : (jl_value_t*)jl_any_type; @@ -198,10 +198,8 @@ static int egal_types(const jl_value_t *a, const jl_value_t *b, jl_typeenv_t *en return egal_types(vma->N, vmb->N, env, tvar_names); return !vma->N && !vmb->N; } - if (dt == jl_symbol_type || dt == jl_module_type) - return 0; - assert(!dt->name->mutabl); - return jl_egal__bits(a, b, dt); + assert(dtag == jl_symbol_tag << 4 || dtag == jl_module_tag << 4 || !((jl_datatype_t*)jl_typeof(a))->name->mutabl); + return jl_egal__bitstag(a, b, dtag); } JL_DLLEXPORT int jl_types_egal(jl_value_t *a, jl_value_t *b) @@ -215,36 +213,72 @@ JL_DLLEXPORT int (jl_egal)(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value return jl_egal(a, b); } -JL_DLLEXPORT int jl_egal__unboxed(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, jl_datatype_t *dt) JL_NOTSAFEPOINT +JL_DLLEXPORT int jl_egal__unboxed(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, uintptr_t dtag) JL_NOTSAFEPOINT { // warning: a,b may NOT have been gc-rooted by the caller - return jl_egal__unboxed_(a, b, dt); -} - -int jl_egal__special(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, jl_datatype_t *dt) JL_NOTSAFEPOINT -{ - if (dt == jl_simplevector_type) - return compare_svec((jl_svec_t*)a, (jl_svec_t*)b); - if (dt == jl_datatype_type) { - jl_datatype_t *dta = (jl_datatype_t*)a; - jl_datatype_t *dtb = (jl_datatype_t*)b; - if (dta->name != dtb->name) - return 0; - if (dta->name != jl_tuple_typename && (dta->isconcretetype || dtb->isconcretetype)) - return 0; - return compare_svec(dta->parameters, dtb->parameters); - } - if (dt == jl_string_type) { - size_t l = jl_string_len(a); - if (jl_string_len(b) != l) + return jl_egal__unboxed_(a, b, dtag); +} + +JL_DLLEXPORT int jl_egal__bitstag(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, uintptr_t dtag) JL_NOTSAFEPOINT +{ + if (dtag < jl_max_tags << 4) { + switch ((enum jlsmall_typeof_tags)(dtag >> 4)) { + case jl_int8_tag: + case jl_uint8_tag: + return *(uint8_t*)a == *(uint8_t*)b; + case jl_int16_tag: + case jl_uint16_tag: + return *(uint16_t*)a == *(uint16_t*)b; + case jl_int32_tag: + case jl_uint32_tag: + case jl_char_tag: + return *(uint32_t*)a == *(uint32_t*)b; + case jl_int64_tag: + case jl_uint64_tag: + return *(uint64_t*)a == *(uint64_t*)b; + case jl_unionall_tag: + return egal_types(a, b, NULL, 1); + case jl_uniontype_tag: + return compare_fields(a, b, jl_uniontype_type); + case jl_vararg_tag: + return compare_fields(a, b, jl_vararg_type); + case jl_task_tag: + case jl_tvar_tag: + case jl_symbol_tag: + case jl_module_tag: + case jl_bool_tag: return 0; - return !memcmp(jl_string_data(a), jl_string_data(b), l); + case jl_simplevector_tag: + return compare_svec((jl_svec_t*)a, (jl_svec_t*)b); + case jl_string_tag: { + size_t l = jl_string_len(a); + if (jl_string_len(b) != l) + return 0; + return !memcmp(jl_string_data(a), jl_string_data(b), l); + } + case jl_datatype_tag: { + jl_datatype_t *dta = (jl_datatype_t*)a; + jl_datatype_t *dtb = (jl_datatype_t*)b; + if (dta->name != dtb->name) + return 0; + if (dta->name != jl_tuple_typename && (dta->isconcretetype || dtb->isconcretetype)) + return 0; + return compare_svec(dta->parameters, dtb->parameters); + } +#ifndef NDEBUG + default: +#endif + case jl_max_tags: + case jl_null_tag: + case jl_typeofbottom_tag: + case jl_tags_count: + abort(); + } } - assert(0 && "unreachable"); - return 0; + return jl_egal__bits(a, b, (jl_datatype_t*)dtag); } -int jl_egal__bits(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, jl_datatype_t *dt) JL_NOTSAFEPOINT +inline int jl_egal__bits(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, jl_datatype_t *dt) JL_NOTSAFEPOINT { size_t sz = jl_datatype_size(dt); if (sz == 0) @@ -252,8 +286,6 @@ int jl_egal__bits(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_ size_t nf = jl_datatype_nfields(dt); if (nf == 0 || !dt->layout->haspadding) return bits_equal(a, b, sz); - if (dt == jl_unionall_type) - return egal_types(a, b, NULL, 1); return compare_fields(a, b, dt); } @@ -1417,6 +1449,7 @@ JL_DLLEXPORT jl_tvar_t *jl_new_typevar(jl_sym_t *name, jl_value_t *lb, jl_value_ jl_type_error_rt("TypeVar", "upper bound", (jl_value_t *)jl_type_type, ub); jl_task_t *ct = jl_current_task; jl_tvar_t *tv = (jl_tvar_t *)jl_gc_alloc(ct->ptls, sizeof(jl_tvar_t), jl_tvar_type); + jl_set_typetagof(tv, jl_tvar_tag, 0); tv->name = name; tv->lb = lb; tv->ub = ub; @@ -1650,7 +1683,7 @@ static int equiv_field_types(jl_value_t *old, jl_value_t *ft) if (!jl_has_free_typevars(tb) || !jl_egal(ta, tb)) return 0; } - else if (jl_has_free_typevars(tb) || jl_typeof(ta) != jl_typeof(tb) || + else if (jl_has_free_typevars(tb) || jl_typetagof(ta) != jl_typetagof(tb) || !jl_types_equal(ta, tb)) { return 0; } @@ -1763,7 +1796,7 @@ static int equiv_type(jl_value_t *ta, jl_value_t *tb) if (!jl_is_datatype(dta)) return 0; jl_datatype_t *dtb = (jl_datatype_t*)jl_unwrap_unionall(tb); - if (!(jl_typeof(dta) == jl_typeof(dtb) && + if (!(jl_typetagof(dta) == jl_typetagof(dtb) && dta->name->name == dtb->name->name && dta->name->abstract == dtb->name->abstract && dta->name->mutabl == dtb->name->mutabl && @@ -1893,7 +1926,7 @@ static void add_intrinsic_properties(enum intrinsic f, unsigned nargs, void (*pf static void add_intrinsic(jl_module_t *inm, const char *name, enum intrinsic f) JL_GC_DISABLED { - jl_value_t *i = jl_permbox32(jl_intrinsic_type, (int32_t)f); + jl_value_t *i = jl_permbox32(jl_intrinsic_type, 0, (int32_t)f); jl_sym_t *sym = jl_symbol(name); jl_set_const(inm, sym, i); jl_module_export(inm, sym); diff --git a/src/ccall.cpp b/src/ccall.cpp index b67726eee8be1..6b2143579317f 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -803,7 +803,7 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar } ir = jl_fieldref(ir, 0); - if (!jl_is_string(ir) && !jl_typeis(ir, jl_array_uint8_type)) { + if (!jl_is_string(ir) && !jl_typetagis(ir, jl_array_uint8_type)) { emit_error(ctx, "Module IR passed to llvmcall must be a string or an array of bytes"); JL_GC_POP(); return jl_cgval_t(); @@ -1882,7 +1882,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) if (!val.isghost && !val.ispointer()) val = value_to_pointer(ctx, val); Value *args[] = { - emit_typeof_boxed(ctx, val), + emit_typeof(ctx, val), val.isghost ? ConstantPointerNull::get(T_pint8_derived) : ctx.builder.CreateBitCast( decay_derived(ctx, data_pointer(ctx, val)), @@ -1984,9 +1984,7 @@ jl_cgval_t function_sig_t::emit_a_ccall( // XXX: result needs to be zero'd and given a GC root here // and has incorrect write barriers. // instead this code path should behave like `unsafe_load` - assert(jl_datatype_size(rt) > 0 && "sret shouldn't be a singleton instance"); - result = emit_allocobj(ctx, jl_datatype_size(rt), - literal_pointer_val(ctx, (jl_value_t*)rt)); + result = emit_allocobj(ctx, (jl_datatype_t*)rt); sretty = ctx.types().T_jlvalue; sretboxed = true; gc_uses.push_back(result); @@ -2148,15 +2146,13 @@ jl_cgval_t function_sig_t::emit_a_ccall( else if (jlretboxed && !retboxed) { assert(jl_is_datatype(rt)); if (static_rt) { - Value *runtime_bt = literal_pointer_val(ctx, rt); - size_t rtsz = jl_datatype_size(rt); - assert(rtsz > 0); - Value *strct = emit_allocobj(ctx, rtsz, runtime_bt); + Value *strct = emit_allocobj(ctx, (jl_datatype_t*)rt); MDNode *tbaa = jl_is_mutable(rt) ? ctx.tbaa().tbaa_mutab : ctx.tbaa().tbaa_immut; int boxalign = julia_alignment(rt); // copy the data from the return value to the new struct const DataLayout &DL = ctx.builder.GetInsertBlock()->getModule()->getDataLayout(); auto resultTy = result->getType(); + size_t rtsz = jl_datatype_size(rt); if (DL.getTypeStoreSize(resultTy) > rtsz) { // ARM and AArch64 can use a LLVM type larger than the julia type. // When this happens, cast through memory. diff --git a/src/cgutils.cpp b/src/cgutils.cpp index dc20167e14f95..29e3cf152625f 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -323,7 +323,7 @@ static Value *get_gc_root_for(const jl_cgval_t &x) static inline Constant *literal_static_pointer_val(const void *p, Type *T); -static Value *julia_pgv(jl_codectx_t &ctx, const char *cname, void *addr) +static Constant *julia_pgv(jl_codectx_t &ctx, const char *cname, void *addr) { // emit a GlobalVariable for a jl_value_t named "cname" // store the name given so we can reuse it (facilitating merging later) @@ -355,7 +355,7 @@ static Value *julia_pgv(jl_codectx_t &ctx, const char *cname, void *addr) return gv; } -static Value *julia_pgv(jl_codectx_t &ctx, const char *prefix, jl_sym_t *name, jl_module_t *mod, void *addr) +static Constant *julia_pgv(jl_codectx_t &ctx, const char *prefix, jl_sym_t *name, jl_module_t *mod, void *addr) { // emit a GlobalVariable for a jl_value_t, using the prefix, name, and module to // to create a readable name of the form prefixModA.ModB.name# @@ -384,7 +384,7 @@ static Value *julia_pgv(jl_codectx_t &ctx, const char *prefix, jl_sym_t *name, j } static JuliaVariable *julia_const_gv(jl_value_t *val); -static Value *literal_pointer_val_slot(jl_codectx_t &ctx, jl_value_t *p) +static Constant *literal_pointer_val_slot(jl_codectx_t &ctx, jl_value_t *p) { // emit a pointer to a jl_value_t* which will allow it to be valid across reloading code // also, try to give it a nice name for gdb, for easy identification @@ -404,6 +404,12 @@ static Value *literal_pointer_val_slot(jl_codectx_t &ctx, jl_value_t *p) } if (jl_is_datatype(p)) { jl_datatype_t *addr = (jl_datatype_t*)p; + if (addr->smalltag) { + // some common builtin datatypes have a special pool for accessing them by smalltag id + Constant *tag = ConstantInt::get(getInt32Ty(ctx.builder.getContext()), addr->smalltag << 4); + Constant *smallp = ConstantExpr::getInBoundsGetElementPtr(getInt8Ty(ctx.builder.getContext()), prepare_global_in(jl_Module, jlsmall_typeof_var), tag); + return ConstantExpr::getBitCast(smallp, ctx.types().T_ppjlvalue); + } // DataTypes are prefixed with a + return julia_pgv(ctx, "+", addr->name->name, addr->name->module, p); } @@ -883,7 +889,8 @@ static bool is_uniontype_allunboxed(jl_value_t *typ) return for_each_uniontype_small([&](unsigned, jl_datatype_t*) {}, typ, counter); } -static Value *emit_typeof_boxed(jl_codectx_t &ctx, const jl_cgval_t &p, bool maybenull=false); +static Value *emit_typeof(jl_codectx_t &ctx, Value *v, bool maybenull, bool justtag, bool notag=false); +static Value *emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p, bool maybenull=false, bool justtag=false); static unsigned get_box_tindex(jl_datatype_t *jt, jl_value_t *ut) { @@ -1033,49 +1040,84 @@ static LoadInst *emit_nthptr_recast(jl_codectx_t &ctx, Value *v, Value *idx, MDN return load; } -static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &v, bool is_promotable=false); - -static Value *emit_typeof(jl_codectx_t &ctx, Value *v, bool maybenull); +static Value *emit_tagfrom(jl_codectx_t &ctx, jl_datatype_t *dt) +{ + if (dt->smalltag) + return ConstantInt::get(ctx.types().T_size, dt->smalltag << 4); + return ctx.builder.CreatePtrToInt(literal_pointer_val(ctx, (jl_value_t*)dt), ctx.types().T_size); +} -static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p, bool maybenull) +// Returns justtag ? ctx.types.T_size : ctx.types().T_prjlvalue +static Value *emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p, bool maybenull, bool justtag) { // given p, compute its type + jl_datatype_t *dt = NULL; if (p.constant) - return mark_julia_const(ctx, jl_typeof(p.constant)); - if (p.isboxed && !jl_is_concrete_type(p.typ)) { - if (jl_is_type_type(p.typ)) { - jl_value_t *tp = jl_tparam0(p.typ); - if (!jl_is_type(tp) || jl_is_concrete_type(tp)) { - // convert 1::Type{1} ==> typeof(1) ==> Int - return mark_julia_const(ctx, jl_typeof(tp)); - } + dt = (jl_datatype_t*)jl_typeof(p.constant); + else if (jl_is_concrete_type(p.typ)) + dt = (jl_datatype_t*)p.typ; + if (dt) { + if (justtag) + return emit_tagfrom(ctx, dt); + return track_pjlvalue(ctx, literal_pointer_val(ctx, (jl_value_t*)dt)); + } + auto notag = [justtag] (jl_value_t *typ) { + // compute if the tag is always a type (not a builtin tag) + // based on having no intersection with one of the special types + // this doesn't matter if the user just wants the tag value + if (justtag) + return false; + jl_value_t *uw = jl_unwrap_unionall(typ); + if (jl_is_datatype(uw)) { // quick path to catch common cases + jl_datatype_t *dt = (jl_datatype_t*)uw; + assert(!dt->smalltag); + if (!dt->name->abstract) + return true; + if (dt == jl_any_type) + return false; } - return mark_julia_type(ctx, emit_typeof(ctx, p.V, maybenull), true, jl_datatype_type); - } + if (jl_has_intersect_type_not_kind(typ)) + return false; + for (size_t i = 0; i < jl_tags_count; i++) { + jl_datatype_t *dt = small_typeof[(i << 4) / sizeof(*small_typeof)]; + if (dt && !jl_has_empty_intersection((jl_value_t*)dt, typ)) + return false; + } + return true; + }; + if (p.isboxed) + return emit_typeof(ctx, p.V, maybenull, justtag, notag(p.typ)); if (p.TIndex) { Value *tindex = ctx.builder.CreateAnd(p.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x7f)); bool allunboxed = is_uniontype_allunboxed(p.typ); - Value *datatype_or_p = ctx.emission_context.imaging ? Constant::getNullValue(ctx.types().T_ppjlvalue) : Constant::getNullValue(ctx.types().T_prjlvalue); + Type *expr_type = justtag ? ctx.types().T_size : ctx.emission_context.imaging ? ctx.types().T_pjlvalue : ctx.types().T_prjlvalue; + Value *datatype_or_p = Constant::getNullValue(ctx.emission_context.imaging ? expr_type->getPointerTo() : expr_type); unsigned counter = 0; for_each_uniontype_small( [&](unsigned idx, jl_datatype_t *jt) { Value *cmp = ctx.builder.CreateICmpEQ(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), idx)); - Value *ptr; - if (ctx.emission_context.imaging) { - ptr = literal_pointer_val_slot(ctx, (jl_value_t*)jt); - } - else { - ptr = track_pjlvalue(ctx, literal_pointer_val(ctx, (jl_value_t*)jt)); + Constant *ptr; + if (justtag && jt->smalltag) { + ptr = ConstantInt::get(expr_type, jt->smalltag << 4); + if (ctx.emission_context.imaging) + ptr = get_pointer_to_constant(ctx.emission_context, ptr, "_j_tag", *jl_Module); } + else if (ctx.emission_context.imaging) + ptr = ConstantExpr::getBitCast(literal_pointer_val_slot(ctx, (jl_value_t*)jt), datatype_or_p->getType()); + else if (justtag) + ptr = ConstantInt::get(expr_type, (uintptr_t)jt); + else + ptr = ConstantExpr::getAddrSpaceCast(literal_static_pointer_val((jl_value_t*)jt, ctx.types().T_pjlvalue), expr_type); datatype_or_p = ctx.builder.CreateSelect(cmp, ptr, datatype_or_p); }, p.typ, counter); auto emit_unboxty = [&] () -> Value* { jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); - if (ctx.emission_context.imaging) - return track_pjlvalue( - ctx, ai.decorateInst(ctx.builder.CreateAlignedLoad(ctx.types().T_pjlvalue, datatype_or_p, Align(sizeof(void*))))); + if (ctx.emission_context.imaging) { + Value *datatype = ai.decorateInst(ctx.builder.CreateAlignedLoad(expr_type, datatype_or_p, Align(sizeof(void*)))); + return justtag ? datatype : track_pjlvalue(ctx, datatype); + } return datatype_or_p; }; Value *res; @@ -1086,7 +1128,7 @@ static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p, bool maybe BasicBlock *mergeBB = BasicBlock::Create(ctx.builder.getContext(), "merge", ctx.f); ctx.builder.CreateCondBr(isnull, boxBB, unboxBB); ctx.builder.SetInsertPoint(boxBB); - auto boxTy = emit_typeof(ctx, p.Vboxed, maybenull); + auto boxTy = emit_typeof(ctx, p.Vboxed, maybenull, justtag, notag(p.typ)); ctx.builder.CreateBr(mergeBB); boxBB = ctx.builder.GetInsertBlock(); // could have changed ctx.builder.SetInsertPoint(unboxBB); @@ -1094,7 +1136,7 @@ static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p, bool maybe ctx.builder.CreateBr(mergeBB); unboxBB = ctx.builder.GetInsertBlock(); // could have changed ctx.builder.SetInsertPoint(mergeBB); - auto phi = ctx.builder.CreatePHI(ctx.types().T_prjlvalue, 2); + auto phi = ctx.builder.CreatePHI(boxTy->getType(), 2); phi->addIncoming(boxTy, boxBB); phi->addIncoming(unboxTy, unboxBB); res = phi; @@ -1102,15 +1144,9 @@ static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p, bool maybe else { res = emit_unboxty(); } - return mark_julia_type(ctx, res, true, jl_datatype_type); + return res; } - return mark_julia_const(ctx, p.typ); -} - -// Returns ctx.types().T_prjlvalue -static Value *emit_typeof_boxed(jl_codectx_t &ctx, const jl_cgval_t &p, bool maybenull) -{ - return boxed(ctx, emit_typeof(ctx, p, maybenull)); + assert(0 && "what is this struct"); abort(); } static Value *emit_datatype_types(jl_codectx_t &ctx, Value *dt) @@ -1164,7 +1200,7 @@ static Value *emit_sizeof(jl_codectx_t &ctx, const jl_cgval_t &p) ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)); ctx.builder.CreateCondBr(isboxed, dynloadBB, postBB); ctx.builder.SetInsertPoint(dynloadBB); - Value *datatype = emit_typeof(p.V); + Value *datatype = emit_typeof(ctx, p.V, false, false); Value *dyn_size = emit_datatype_size(ctx, datatype); ctx.builder.CreateBr(postBB); dynloadBB = ctx.builder.GetInsertBlock(); // could have changed @@ -1184,7 +1220,7 @@ static Value *emit_sizeof(jl_codectx_t &ctx, const jl_cgval_t &p) return ConstantInt::get(getInt32Ty(ctx.builder.getContext()), jl_datatype_size(p.typ)); } else { - Value *datatype = emit_typeof_boxed(ctx, p); + Value *datatype = emit_typeof(ctx, p, false, false); Value *dyn_size = emit_datatype_size(ctx, datatype); return dyn_size; } @@ -1369,21 +1405,38 @@ static Value *emit_nullcheck_guard2(jl_codectx_t &ctx, Value *nullcheck1, // Returns typeof(v), or null if v is a null pointer at run time and maybenull is true. // This is used when the value might have come from an undefined value (a PhiNode), -// yet we try to read its type to compute a union index when moving the value (a PiNode). +// yet jl_max_tags try to read its type to compute a union index when moving the value (a PiNode). // Returns a ctx.types().T_prjlvalue typed Value -static Value *emit_typeof(jl_codectx_t &ctx, Value *v, bool maybenull) +static Value *emit_typeof(jl_codectx_t &ctx, Value *v, bool maybenull, bool justtag, bool notag) { ++EmittedTypeof; assert(v != NULL && !isa(v) && "expected a conditionally boxed value"); + Value *nonnull = maybenull ? null_pointer_cmp(ctx, v) : ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 1); Function *typeof = prepare_call(jl_typeof_func); - if (maybenull) - return emit_guarded_test(ctx, null_pointer_cmp(ctx, v), Constant::getNullValue(typeof->getReturnType()), [&] { - // e.g. emit_typeof(ctx, v) - return ctx.builder.CreateCall(typeof, {v}); + return emit_guarded_test(ctx, nonnull, Constant::getNullValue(justtag ? ctx.types().T_size : typeof->getReturnType()), [&] { + // e.g. emit_typeof(ctx, v) + Value *typetag = ctx.builder.CreateCall(typeof, {v}); + if (notag) + return typetag; + Value *tag = ctx.builder.CreatePtrToInt(emit_pointer_from_objref(ctx, typetag), ctx.types().T_size); + if (justtag) + return tag; + auto issmall = ctx.builder.CreateICmpULT(tag, ConstantInt::get(tag->getType(), (uintptr_t)jl_max_tags << 4)); + return emit_guarded_test(ctx, issmall, typetag, [&] { + // we lied a bit: this wasn't really an object (though it was valid for GC rooting) + // and we need to use it as an index to get the real object now + Module *M = jl_Module; + Value *smallp = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), prepare_global_in(M, jlsmall_typeof_var), tag); + smallp = ctx.builder.CreateBitCast(smallp, typetag->getType()->getPointerTo(0)); + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); + auto small = ctx.builder.CreateAlignedLoad(typetag->getType(), smallp, M->getDataLayout().getPointerABIAlignment(0)); + small->setMetadata(LLVMContext::MD_nonnull, MDNode::get(M->getContext(), None)); + return ai.decorateInst(small); }); - return ctx.builder.CreateCall(typeof, {v}); + }); } +static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &v, bool is_promotable=false); static void just_emit_type_error(jl_codectx_t &ctx, const jl_cgval_t &x, Value *type, const std::string &msg) { @@ -1431,11 +1484,11 @@ static bool can_optimize_isa_union(jl_uniontype_t *type) } // a simple case of emit_isa that is obvious not to include a safe-point -static Value *emit_exactly_isa(jl_codectx_t &ctx, const jl_cgval_t &arg, jl_value_t *dt) +static Value *emit_exactly_isa(jl_codectx_t &ctx, const jl_cgval_t &arg, jl_datatype_t *dt) { - assert(jl_is_concrete_type(dt)); + assert(jl_is_concrete_type((jl_value_t*)dt)); if (arg.TIndex) { - unsigned tindex = get_box_tindex((jl_datatype_t*)dt, arg.typ); + unsigned tindex = get_box_tindex(dt, arg.typ); if (tindex > 0) { // optimize more when we know that this is a split union-type where tindex = 0 is invalid Value *xtindex = ctx.builder.CreateAnd(arg.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x7f)); @@ -1449,8 +1502,7 @@ static Value *emit_exactly_isa(jl_codectx_t &ctx, const jl_cgval_t &arg, jl_valu BasicBlock *postBB = BasicBlock::Create(ctx.builder.getContext(), "post_isa", ctx.f); ctx.builder.CreateCondBr(isboxed, isaBB, postBB); ctx.builder.SetInsertPoint(isaBB); - Value *istype_boxed = ctx.builder.CreateICmpEQ(emit_typeof(ctx, arg.Vboxed, false), - track_pjlvalue(ctx, literal_pointer_val(ctx, dt))); + Value *istype_boxed = ctx.builder.CreateICmpEQ(emit_typeof(ctx, arg.Vboxed, false, true), emit_tagfrom(ctx, dt)); ctx.builder.CreateBr(postBB); isaBB = ctx.builder.GetInsertBlock(); // could have changed ctx.builder.SetInsertPoint(postBB); @@ -1463,9 +1515,7 @@ static Value *emit_exactly_isa(jl_codectx_t &ctx, const jl_cgval_t &arg, jl_valu return ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0); } } - return ctx.builder.CreateICmpEQ( - emit_typeof_boxed(ctx, arg), - track_pjlvalue(ctx, literal_pointer_val(ctx, dt))); + return ctx.builder.CreateICmpEQ(emit_typeof(ctx, arg, false, true), emit_tagfrom(ctx, dt)); } static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, @@ -1524,17 +1574,17 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, if (intersected_type == (jl_value_t*)jl_type_type) { // Inline jl_is_kind(jl_typeof(x)) // N.B. We do the comparison with untracked pointers, because that gives - // LLVM more optimization opportunities. That means it is poosible for + // LLVM more optimization opportunities. That means it is possible for // `typ` to get GC'ed, but we don't actually care, because we don't ever // dereference it. - Value *typ = emit_pointer_from_objref(ctx, emit_typeof_boxed(ctx, x)); + Value *typ = emit_typeof(ctx, x, false, true); auto val = ctx.builder.CreateOr( ctx.builder.CreateOr( - ctx.builder.CreateICmpEQ(typ, literal_pointer_val(ctx, (jl_value_t*)jl_uniontype_type)), - ctx.builder.CreateICmpEQ(typ, literal_pointer_val(ctx, (jl_value_t*)jl_datatype_type))), + ctx.builder.CreateICmpEQ(typ, emit_tagfrom(ctx, jl_uniontype_type)), + ctx.builder.CreateICmpEQ(typ, emit_tagfrom(ctx, jl_datatype_type))), ctx.builder.CreateOr( - ctx.builder.CreateICmpEQ(typ, literal_pointer_val(ctx, (jl_value_t*)jl_unionall_type)), - ctx.builder.CreateICmpEQ(typ, literal_pointer_val(ctx, (jl_value_t*)jl_typeofbottom_type)))); + ctx.builder.CreateICmpEQ(typ, emit_tagfrom(ctx, jl_unionall_type)), + ctx.builder.CreateICmpEQ(typ, emit_tagfrom(ctx, jl_typeofbottom_type)))); return std::make_pair(val, false); } // intersection with Type needs to be handled specially @@ -1550,15 +1600,16 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 0)), false); } // tests for isa concretetype can be handled with pointer comparisons - if (jl_is_concrete_type(intersected_type)) - return std::make_pair(emit_exactly_isa(ctx, x, intersected_type), false); + if (jl_is_concrete_type(intersected_type)) { + return std::make_pair(emit_exactly_isa(ctx, x, (jl_datatype_t*)intersected_type), false); + } jl_datatype_t *dt = (jl_datatype_t*)jl_unwrap_unionall(intersected_type); if (jl_is_datatype(dt) && !dt->name->abstract && jl_subtype(dt->name->wrapper, type)) { // intersection is a supertype of all instances of its constructor, // so the isa test reduces to a comparison of the typename by pointer return std::make_pair( ctx.builder.CreateICmpEQ( - emit_datatype_name(ctx, emit_typeof_boxed(ctx, x)), + emit_datatype_name(ctx, emit_typeof(ctx, x, false, false)), literal_pointer_val(ctx, (jl_value_t*)dt->name)), false); } @@ -1587,7 +1638,7 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, // everything else can be handled via subtype tests return std::make_pair(ctx.builder.CreateICmpNE( ctx.builder.CreateCall(prepare_call(jlsubtype_func), - { emit_typeof_boxed(ctx, x), + { emit_typeof(ctx, x, false, false), track_pjlvalue(ctx, literal_pointer_val(ctx, type)) }), ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 0)), false); } @@ -2938,7 +2989,7 @@ static Value *emit_array_nd_index( // --- boxing --- -static Value *emit_allocobj(jl_codectx_t &ctx, size_t static_size, Value *jt); +static Value *emit_allocobj(jl_codectx_t &ctx, jl_datatype_t *jt); static void init_bits_value(jl_codectx_t &ctx, Value *newv, Value *v, MDNode *tbaa, unsigned alignment = sizeof(void*)) // min alignment in julia's gc is pointer-aligned @@ -3129,14 +3180,14 @@ static Value *_boxed_special(jl_codectx_t &ctx, const jl_cgval_t &vinfo, Type *t return box; } -static Value *compute_box_tindex(jl_codectx_t &ctx, Value *datatype, jl_value_t *supertype, jl_value_t *ut) +static Value *compute_box_tindex(jl_codectx_t &ctx, Value *datatype_tag, jl_value_t *supertype, jl_value_t *ut) { Value *tindex = ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0); unsigned counter = 0; for_each_uniontype_small( [&](unsigned idx, jl_datatype_t *jt) { if (jl_subtype((jl_value_t*)jt, supertype)) { - Value *cmp = ctx.builder.CreateICmpEQ(track_pjlvalue(ctx, literal_pointer_val(ctx, (jl_value_t*)jt)), datatype); + Value *cmp = ctx.builder.CreateICmpEQ(emit_tagfrom(ctx, jt), datatype_tag); tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), idx), tindex); } }, @@ -3154,7 +3205,7 @@ static Value *compute_tindex_unboxed(jl_codectx_t &ctx, const jl_cgval_t &val, j return ConstantInt::get(getInt8Ty(ctx.builder.getContext()), get_box_tindex((jl_datatype_t*)jl_typeof(val.constant), typ)); if (val.TIndex) return ctx.builder.CreateAnd(val.TIndex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x7f)); - Value *typof = emit_typeof_boxed(ctx, val, maybenull); + Value *typof = emit_typeof(ctx, val, maybenull, true); return compute_box_tindex(ctx, typof, val.typ, typ); } @@ -3247,7 +3298,7 @@ static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallB jl_cgval_t vinfo_r = jl_cgval_t(vinfo, (jl_value_t*)jt, NULL); box = _boxed_special(ctx, vinfo_r, t); if (!box) { - box = emit_allocobj(ctx, jl_datatype_size(jt), literal_pointer_val(ctx, (jl_value_t*)jt)); + box = emit_allocobj(ctx, jt); init_bits_cgval(ctx, box, vinfo_r, jl_is_mutable(jt) ? ctx.tbaa().tbaa_mutab : ctx.tbaa().tbaa_immut); } } @@ -3373,7 +3424,7 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo, bool is_promotab if (do_promote && is_promotable) { auto IP = ctx.builder.saveIP(); ctx.builder.SetInsertPoint(vinfo.promotion_point); - box = emit_allocobj(ctx, jl_datatype_size(jt), literal_pointer_val(ctx, (jl_value_t*)jt)); + box = emit_allocobj(ctx, (jl_datatype_t*)jt); Value *decayed = decay_derived(ctx, box); AllocaInst *originalAlloca = cast(vinfo.V); decayed = maybe_bitcast(ctx, decayed, PointerType::getWithSamePointeeType(originalAlloca->getType(), AddressSpace::Derived)); @@ -3385,7 +3436,7 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo, bool is_promotab originalAlloca->eraseFromParent(); ctx.builder.restoreIP(IP); } else { - box = emit_allocobj(ctx, jl_datatype_size(jt), literal_pointer_val(ctx, (jl_value_t*)jt)); + box = emit_allocobj(ctx, (jl_datatype_t*)jt); init_bits_cgval(ctx, box, vinfo, jl_is_mutable(jt) ? ctx.tbaa().tbaa_mutab : ctx.tbaa().tbaa_immut); } } @@ -3475,7 +3526,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con else { assert(src.isboxed && "expected boxed value for sizeof/alignment computation"); auto f = [&] { - Value *datatype = emit_typeof_boxed(ctx, src); + Value *datatype = emit_typeof(ctx, src, false, false); Value *copy_bytes = emit_datatype_size(ctx, datatype); emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dst), src, copy_bytes, /*TODO: min-align*/1, isVolatile); return nullptr; @@ -3491,8 +3542,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con static void emit_cpointercheck(jl_codectx_t &ctx, const jl_cgval_t &x, const std::string &msg) { ++EmittedCPointerChecks; - Value *t = emit_typeof_boxed(ctx, x); - emit_typecheck(ctx, mark_julia_type(ctx, t, true, jl_any_type), (jl_value_t*)jl_datatype_type, msg); + Value *t = emit_typeof(ctx, x, false, false); Value *istype = ctx.builder.CreateICmpEQ(emit_datatype_name(ctx, t), @@ -3519,12 +3569,15 @@ static Value *emit_allocobj(jl_codectx_t &ctx, size_t static_size, Value *jt) auto call = ctx.builder.CreateCall(F, {current_task, ConstantInt::get(ctx.types().T_size, static_size), maybe_decay_untracked(ctx, jt)}); call->setAttributes(F->getAttributes()); if (static_size > 0) - { - call->addRetAttr(Attribute::getWithDereferenceableBytes(ctx.builder.getContext(),static_size)); - } + call->addRetAttr(Attribute::getWithDereferenceableBytes(ctx.builder.getContext(), static_size)); return call; } +static Value *emit_allocobj(jl_codectx_t &ctx, jl_datatype_t *jt) +{ + return emit_allocobj(ctx, jl_datatype_size(jt), ctx.builder.CreateIntToPtr(emit_tagfrom(ctx, jt), ctx.types().T_pjlvalue)); +} + // allocation for unknown object from an untracked pointer static Value *emit_new_bits(jl_codectx_t &ctx, Value *jt, Value *pval) { @@ -3902,8 +3955,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg return ret; } } - Value *strct = emit_allocobj(ctx, jl_datatype_size(sty), - literal_pointer_val(ctx, (jl_value_t*)ty)); + Value *strct = emit_allocobj(ctx, sty); jl_cgval_t strctinfo = mark_julia_type(ctx, strct, true, ty); strct = decay_derived(ctx, strct); undef_derived_strct(ctx, strct, sty, strctinfo.tbaa); diff --git a/src/codegen.cpp b/src/codegen.cpp index 16149325eb3e0..da69678dee6ff 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -643,6 +643,11 @@ static const auto jldlli_var = new JuliaVariable{ true, [](Type *T_size) -> Type * { return getInt8PtrTy(T_size->getContext()); }, }; +static const auto jlsmall_typeof_var = new JuliaVariable{ + XSTR(small_typeof), + true, + [](Type *T_size) -> Type * { return getInt8Ty(T_size->getContext()); }, +}; static const auto jlstack_chk_guard_var = new JuliaVariable{ XSTR(__stack_chk_guard), @@ -902,11 +907,11 @@ static const auto jl_excstack_state_func = new JuliaFunction{ +static const auto jlegalx_func = new JuliaFunction{ XSTR(jl_egal__unboxed), - [](LLVMContext &C) { + [](LLVMContext &C, Type *T_size) { Type *T = PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::Derived); - return FunctionType::get(getInt32Ty(C), {T, T, JuliaType::get_prjlvalue_ty(C)}, false); }, + return FunctionType::get(getInt32Ty(C), {T, T, T_size}, false); }, [](LLVMContext &C) { return AttributeList::get(C, Attributes(C, {Attribute::ReadOnly, Attribute::NoUnwind, Attribute::ArgMemOnly}), AttributeSet(), @@ -2146,7 +2151,7 @@ static jl_cgval_t convert_julia_type_union(jl_codectx_t &ctx, const jl_cgval_t & if (!union_isaBB) { union_isaBB = BasicBlock::Create(ctx.builder.getContext(), "union_isa", ctx.f); ctx.builder.SetInsertPoint(union_isaBB); - union_box_dt = emit_typeof(ctx, v.Vboxed, skip != NULL); + union_box_dt = emit_typeof(ctx, v.Vboxed, skip != NULL, true); post_union_isaBB = ctx.builder.GetInsertBlock(); } }; @@ -2164,7 +2169,7 @@ static jl_cgval_t convert_julia_type_union(jl_codectx_t &ctx, const jl_cgval_t & if (old_idx == 0) { // didn't handle this item before, select its new union index maybe_setup_union_isa(); - Value *cmp = ctx.builder.CreateICmpEQ(track_pjlvalue(ctx, literal_pointer_val(ctx, (jl_value_t*)jt)), union_box_dt); + Value *cmp = ctx.builder.CreateICmpEQ(emit_tagfrom(ctx, jt), union_box_dt); union_box_tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80 | idx), union_box_tindex); } }, @@ -2881,8 +2886,8 @@ static Value *emit_box_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const } Value *neq = ctx.builder.CreateICmpNE(varg1, varg2); return emit_guarded_test(ctx, neq, true, [&] { - Value *dtarg = emit_typeof_boxed(ctx, arg1); - Value *dt_eq = ctx.builder.CreateICmpEQ(dtarg, emit_typeof_boxed(ctx, arg2)); + Value *dtarg = emit_typeof(ctx, arg1, false, true); + Value *dt_eq = ctx.builder.CreateICmpEQ(dtarg, emit_typeof(ctx, arg2, false, true)); return emit_guarded_test(ctx, dt_eq, false, [&] { return ctx.builder.CreateTrunc(ctx.builder.CreateCall(prepare_call(jlegalx_func), {varg1, varg2, dtarg}), getInt1Ty(ctx.builder.getContext())); @@ -3067,11 +3072,11 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva // since it is normalized to `::Type{Union{}}` instead... if (arg1.TIndex) return emit_nullcheck_guard(ctx, nullcheck1, [&] { - return emit_exactly_isa(ctx, arg1, rt2); // rt2 is a singleton type + return emit_exactly_isa(ctx, arg1, (jl_datatype_t*)rt2); // rt2 is a singleton type }); if (arg2.TIndex) return emit_nullcheck_guard(ctx, nullcheck2, [&] { - return emit_exactly_isa(ctx, arg2, rt1); // rt1 is a singleton type + return emit_exactly_isa(ctx, arg2, (jl_datatype_t*)rt1); // rt1 is a singleton type }); if (!(arg1.isboxed || arg1.constant) || !(arg2.isboxed || arg2.constant)) // not TIndex && not boxed implies it is an unboxed value of a different type from this singleton @@ -3094,8 +3099,8 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva bool justbits2 = jl_is_concrete_immutable(rt2); if (justbits1 || justbits2) { // whether this type is unique'd by value return emit_nullcheck_guard2(ctx, nullcheck1, nullcheck2, [&] () -> Value* { - jl_value_t *typ = justbits1 ? rt1 : rt2; - if (typ == (jl_value_t*)jl_bool_type) { // aka jl_pointer_egal + jl_datatype_t *typ = (jl_datatype_t*)(justbits1 ? rt1 : rt2); + if (typ == jl_bool_type) { // aka jl_pointer_egal // some optimizations for bool, since pointer comparison may be better if ((arg1.isboxed || arg1.constant) && (arg2.isboxed || arg2.constant)) { // aka have-fast-pointer Value *varg1 = arg1.constant ? literal_pointer_val(ctx, arg1.constant) : maybe_bitcast(ctx, arg1.Vboxed, ctx.types().T_pjlvalue); @@ -3105,14 +3110,14 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva } if (rt1 == rt2) return emit_bits_compare(ctx, arg1, arg2); - Value *same_type = emit_exactly_isa(ctx, (typ == rt2 ? arg1 : arg2), typ); + Value *same_type = emit_exactly_isa(ctx, (justbits1 ? arg2 : arg1), typ); BasicBlock *currBB = ctx.builder.GetInsertBlock(); BasicBlock *isaBB = BasicBlock::Create(ctx.builder.getContext(), "is", ctx.f); BasicBlock *postBB = BasicBlock::Create(ctx.builder.getContext(), "post_is", ctx.f); ctx.builder.CreateCondBr(same_type, isaBB, postBB); ctx.builder.SetInsertPoint(isaBB); - Value *bitcmp = emit_bits_compare(ctx, jl_cgval_t(arg1, typ, NULL), - jl_cgval_t(arg2, typ, NULL)); + Value *bitcmp = emit_bits_compare(ctx, jl_cgval_t(arg1, (jl_value_t*)typ, NULL), + jl_cgval_t(arg2, (jl_value_t*)typ, NULL)); isaBB = ctx.builder.GetInsertBlock(); // might have changed ctx.builder.CreateBr(postBB); ctx.builder.SetInsertPoint(postBB); @@ -3307,7 +3312,13 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } else if (f == jl_builtin_typeof && nargs == 1) { - *ret = emit_typeof(ctx, argv[1], false); + const jl_cgval_t &p = argv[1]; + if (p.constant) + *ret = mark_julia_const(ctx, jl_typeof(p.constant)); + else if (jl_is_concrete_type(p.typ)) + *ret = mark_julia_const(ctx, p.typ); + else + *ret = mark_julia_type(ctx, emit_typeof(ctx, p, false, false), true, jl_datatype_type); return true; } @@ -3743,9 +3754,10 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, // the extra code for getting the length of the tuple if (!bounds_check_enabled(ctx, boundscheck)) { vidx = ctx.builder.CreateSub(vidx, ConstantInt::get(ctx.types().T_size, 1)); - } else { + } + else { vidx = emit_bounds_check(ctx, obj, (jl_value_t*)obj.typ, vidx, - emit_datatype_nfields(ctx, emit_typeof_boxed(ctx, obj)), + emit_datatype_nfields(ctx, emit_typeof(ctx, obj, false, false)), jl_true); } bool isboxed = !jl_datatype_isinlinealloc((jl_datatype_t*)jt, 0); @@ -3838,7 +3850,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (nf != -1) sz = ConstantInt::get(ctx.types().T_size, nf); else - sz = emit_datatype_nfields(ctx, emit_typeof_boxed(ctx, obj)); + sz = emit_datatype_nfields(ctx, emit_typeof(ctx, obj, false, false)); *ret = mark_julia_type(ctx, sz, false, jl_long_type); return true; } @@ -4144,6 +4156,7 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos for (size_t i = 0; i < nargs; i++) { jl_value_t *jt = jl_nth_slot_type(specTypes, i); + // n.b.: specTypes is required to be a datatype by construction for specsig jl_cgval_t arg = argv[i]; if (is_opaque_closure && i == 0) { Type *at = cft->getParamType(idx); @@ -4153,7 +4166,8 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos // rather than a boxed value. arg = value_to_pointer(ctx, arg); argvals[idx] = decay_derived(ctx, maybe_bitcast(ctx, data_pointer(ctx, arg), at)); - } else if (is_uniquerep_Type(jt)) { + } + else if (is_uniquerep_Type(jt)) { continue; } else { bool isboxed = deserves_argbox(jt); @@ -4421,7 +4435,7 @@ static jl_cgval_t emit_invoke_modify(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_ Value *oldnew = emit_jlcall(ctx, it->second, Constant::getNullValue(ctx.types().T_prjlvalue), &argv[1], nargs - 1, julia_call); return mark_julia_type(ctx, oldnew, true, rt); } - if (f.constant && jl_typeis(f.constant, jl_intrinsic_type)) { + if (f.constant && jl_typetagis(f.constant, jl_intrinsic_type)) { JL_I::intrinsic fi = (intrinsic)*(uint32_t*)jl_data_ptr(f.constant); if (fi == JL_I::atomic_pointermodify && jl_intrinsic_nargs((int)fi) == nargs - 1) return emit_atomic_pointerop(ctx, fi, argv.data(), nargs - 1, &lival); @@ -4467,7 +4481,7 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bo assert(nargs >= 1); jl_cgval_t f = emit_expr(ctx, args[0]); - if (f.constant && jl_typeis(f.constant, jl_intrinsic_type)) { + if (f.constant && jl_typetagis(f.constant, jl_intrinsic_type)) { JL_I::intrinsic fi = (intrinsic)*(uint32_t*)jl_data_ptr(f.constant); return emit_intrinsic(ctx, fi, args, nargs - 1); } @@ -4631,8 +4645,7 @@ static jl_cgval_t emit_sparam(jl_codectx_t &ctx, size_t i) i + sizeof(jl_svec_t) / sizeof(jl_value_t*)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); Value *sp = ai.decorateInst(ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, bp, Align(sizeof(void*)))); - Value *isnull = ctx.builder.CreateICmpNE(emit_typeof(ctx, sp, false), - track_pjlvalue(ctx, literal_pointer_val(ctx, (jl_value_t*)jl_tvar_type))); + Value *isnull = ctx.builder.CreateICmpNE(emit_typeof(ctx, sp, false, true), emit_tagfrom(ctx, jl_tvar_type)); jl_unionall_t *sparam = (jl_unionall_t*)ctx.linfo->def.method->sig; for (size_t j = 0; j < i; j++) { sparam = (jl_unionall_t*)sparam->body; @@ -4687,8 +4700,7 @@ static jl_cgval_t emit_isdefined(jl_codectx_t &ctx, jl_value_t *sym) i + sizeof(jl_svec_t) / sizeof(jl_value_t*)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); Value *sp = ai.decorateInst(ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, bp, Align(sizeof(void*)))); - isnull = ctx.builder.CreateICmpNE(emit_typeof(ctx, sp, false), - track_pjlvalue(ctx, literal_pointer_val(ctx, (jl_value_t*)jl_tvar_type))); + isnull = ctx.builder.CreateICmpNE(emit_typeof(ctx, sp, false, true), emit_tagfrom(ctx, jl_tvar_type)); } else { jl_module_t *modu; @@ -5795,6 +5807,7 @@ static void emit_cfunc_invalidate( ++AI; for (size_t i = 0; i < nargs; i++) { jl_value_t *jt = jl_nth_slot_type(calltype, i); + // n.b. specTypes is required to be a datatype by construction for specsig bool isboxed = false; Type *et; if (i == 0 && is_for_opaque_closure) { @@ -5865,7 +5878,7 @@ static void emit_cfunc_invalidate( case jl_returninfo_t::Union: { Type *retty = gf_thunk->getReturnType(); Value *gf_retval = UndefValue::get(retty); - Value *tindex = compute_box_tindex(ctx, emit_typeof_boxed(ctx, gf_retbox), (jl_value_t*)jl_any_type, rettype); + Value *tindex = compute_box_tindex(ctx, emit_typeof(ctx, gf_retbox, false, true), (jl_value_t*)jl_any_type, rettype); tindex = ctx.builder.CreateOr(tindex, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)); gf_retval = ctx.builder.CreateInsertValue(gf_retval, gf_ret, 0); gf_retval = ctx.builder.CreateInsertValue(gf_retval, tindex, 1); @@ -6139,7 +6152,7 @@ static Function* gen_cfun_wrapper( BasicBlock *unboxedBB = BasicBlock::Create(ctx.builder.getContext(), "maybe-unboxed", cw); BasicBlock *isanyBB = BasicBlock::Create(ctx.builder.getContext(), "any", cw); BasicBlock *afterBB = BasicBlock::Create(ctx.builder.getContext(), "after", cw); - Value *isrtboxed = ctx.builder.CreateIsNull(val); + Value *isrtboxed = ctx.builder.CreateIsNull(val); // XXX: this is the wrong condition and should be inspecting runtime_dt intead ctx.builder.CreateCondBr(isrtboxed, boxedBB, loadBB); ctx.builder.SetInsertPoint(boxedBB); Value *p1 = ctx.builder.CreateBitCast(val, ctx.types().T_pjlvalue); @@ -6300,6 +6313,7 @@ static Function* gen_cfun_wrapper( Value *arg; jl_value_t *spect = (i == 0 && is_opaque_closure) ? (jl_value_t*)jl_any_type : jl_nth_slot_type(lam->specTypes, i); + // n.b. specTypes is required to be a datatype by construction for specsig bool isboxed = deserves_argbox(spect); Type *T = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, spect); if (is_uniquerep_Type(spect)) { @@ -6586,8 +6600,7 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con outboxed = (output_type != (jl_value_t*)jl_voidpointer_type); if (outboxed) { assert(jl_datatype_size(output_type) == sizeof(void*) * 4); - Value *strct = emit_allocobj(ctx, jl_datatype_size(output_type), - literal_pointer_val(ctx, (jl_value_t*)output_type)); + Value *strct = emit_allocobj(ctx, (jl_datatype_t*)output_type); Value *derived_strct = emit_bitcast(ctx, decay_derived(ctx, strct), ctx.types().T_size->getPointerTo()); MDNode *tbaa = best_tbaa(ctx.tbaa(), output_type); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); @@ -6721,6 +6734,7 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret for (size_t i = 0; i < jl_nparams(lam->specTypes) && idx < nfargs; ++i) { jl_value_t *ty = ((i == 0) && is_opaque_closure) ? (jl_value_t*)jl_any_type : jl_nth_slot_type(lam->specTypes, i); + // n.b. specTypes is required to be a datatype by construction for specsig bool isboxed = deserves_argbox(ty); Type *lty = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, ty); if (type_is_ghost(lty) || is_uniquerep_Type(ty)) @@ -6992,6 +7006,7 @@ static jl_datatype_t *compute_va_type(jl_method_instance_t *lam, size_t nreq) JL_GC_PUSH1(&tupargs); for (size_t i = nreq; i < jl_nparams(lam->specTypes); ++i) { jl_value_t *argType = jl_nth_slot_type(lam->specTypes, i); + // n.b. specTypes is required to be a datatype by construction for specsig if (is_uniquerep_Type(argType)) argType = jl_typeof(jl_tparam0(argType)); else if (jl_has_intersect_type_not_kind(argType)) { @@ -7130,6 +7145,8 @@ static jl_llvm_functions_t if (argname == jl_unused_sym) continue; jl_value_t *ty = jl_nth_slot_type(lam->specTypes, i); + // TODO: jl_nth_slot_type should call jl_rewrap_unionall + // specTypes is required to be a datatype by construction for specsig, but maybe not otherwise // OpaqueClosure implicitly loads the env if (i == 0 && ctx.is_opaque_closure) { if (jl_is_array(src->slottypes)) { @@ -7595,6 +7612,8 @@ static jl_llvm_functions_t for (i = 0; i < nreq; i++) { jl_sym_t *s = slot_symbol(ctx, i); jl_value_t *argType = jl_nth_slot_type(lam->specTypes, i); + // TODO: jl_nth_slot_type should call jl_rewrap_unionall? + // specTypes is required to be a datatype by construction for specsig, but maybe not otherwise bool isboxed = deserves_argbox(argType); Type *llvmArgType = NULL; if (i == 0 && ctx.is_opaque_closure) { @@ -7711,6 +7730,7 @@ static jl_llvm_functions_t SmallVector vargs(ctx.nvargs); for (size_t i = nreq; i < jl_nparams(lam->specTypes); ++i) { jl_value_t *argType = jl_nth_slot_type(lam->specTypes, i); + // n.b. specTypes is required to be a datatype by construction for specsig bool isboxed = deserves_argbox(argType); Type *llvmArgType = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, argType); vargs[i - nreq] = get_specsig_arg(argType, llvmArgType, isboxed); @@ -7785,7 +7805,7 @@ static jl_llvm_functions_t // LineInfoNode(mod::Module, method::Any, file::Symbol, line::Int32, inlined_at::Int32) jl_value_t *locinfo = jl_array_ptr_ref(src->linetable, i); DebugLineTable &info = linetable[i + 1]; - assert(jl_typeis(locinfo, jl_lineinfonode_type)); + assert(jl_typetagis(locinfo, jl_lineinfonode_type)); jl_module_t *module = (jl_module_t*)jl_fieldref_noalloc(locinfo, 0); jl_value_t *method = jl_fieldref_noalloc(locinfo, 1); jl_sym_t *filesym = (jl_sym_t*)jl_fieldref_noalloc(locinfo, 2); @@ -8669,7 +8689,7 @@ jl_llvm_functions_t jl_emit_codeinst( if (inferred != (jl_value_t*)src) { if (jl_is_method(def)) { src = (jl_code_info_t*)jl_compress_ir(def, src); - assert(jl_typeis(src, jl_array_uint8_type)); + assert(jl_typetagis(src, jl_array_uint8_type)); codeinst->relocatability = ((uint8_t*)jl_array_data(src))[jl_array_len(src)-1]; } jl_atomic_store_release(&codeinst->inferred, (jl_value_t*)src); @@ -8894,6 +8914,7 @@ static void init_f16_funcs(void) static void init_jit_functions(void) { + add_named_global(jlsmall_typeof_var, &small_typeof); add_named_global(jlstack_chk_guard_var, &__stack_chk_guard); add_named_global(jlRTLD_DEFAULT_var, &jl_RTLD_DEFAULT_handle); add_named_global(jlexe_var, &jl_exe_handle); @@ -8904,6 +8925,7 @@ static void init_jit_functions(void) }; global_jlvalue_to_llvm(new JuliaVariable{"jl_true", true, size2pjlvalue}, &jl_true); global_jlvalue_to_llvm(new JuliaVariable{"jl_false", true, size2pjlvalue}, &jl_false); + global_jlvalue_to_llvm(new JuliaVariable{"jl_nothing", true, size2pjlvalue}, &jl_nothing); global_jlvalue_to_llvm(new JuliaVariable{"jl_emptysvec", true, size2pjlvalue}, (jl_value_t**)&jl_emptysvec); global_jlvalue_to_llvm(new JuliaVariable{"jl_emptytuple", true, size2pjlvalue}, &jl_emptytuple); global_jlvalue_to_llvm(new JuliaVariable{"jl_diverror_exception", true, size2pjlvalue}, &jl_diverror_exception); diff --git a/src/datatype.c b/src/datatype.c index d500d762999a3..95c3b11c9abdc 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -96,6 +96,7 @@ jl_datatype_t *jl_new_uninitialized_datatype(void) { jl_task_t *ct = jl_current_task; jl_datatype_t *t = (jl_datatype_t*)jl_gc_alloc(ct->ptls, sizeof(jl_datatype_t), jl_datatype_type); + jl_set_typetagof(t, jl_datatype_tag, 0); t->hash = 0; t->hasfreetypevars = 0; t->isdispatchtuple = 0; @@ -106,7 +107,7 @@ jl_datatype_t *jl_new_uninitialized_datatype(void) t->maybe_subtype_of_cache = 1; t->ismutationfree = 0; t->isidentityfree = 0; - t->padding = 0; + t->smalltag = 0; t->name = NULL; t->super = NULL; t->parameters = NULL; @@ -964,6 +965,7 @@ JL_DLLEXPORT jl_value_t *jl_new_bits(jl_value_t *dt, const void *data) if (bt == jl_uint16_type) return jl_box_uint16(*(uint16_t*)data); if (bt == jl_char_type) return jl_box_char(*(uint32_t*)data); + assert(!bt->smalltag); jl_task_t *ct = jl_current_task; jl_value_t *v = jl_gc_alloc(ct->ptls, nb, bt); memcpy(jl_assume_aligned(v, sizeof(void*)), data, nb); @@ -989,6 +991,7 @@ JL_DLLEXPORT jl_value_t *jl_atomic_new_bits(jl_value_t *dt, const char *data) if (bt == jl_uint16_type) return jl_box_uint16(jl_atomic_load((_Atomic(uint16_t)*)data)); if (bt == jl_char_type) return jl_box_char(jl_atomic_load((_Atomic(uint32_t)*)data)); + assert(!bt->smalltag); jl_task_t *ct = jl_current_task; jl_value_t *v = jl_gc_alloc(ct->ptls, nb, bt); // data is aligned to the power of two, @@ -1056,6 +1059,7 @@ JL_DLLEXPORT jl_value_t *jl_atomic_swap_bits(jl_value_t *dt, char *dst, const jl if (bt == jl_uint16_type) return jl_box_uint16(jl_atomic_exchange((_Atomic(uint16_t)*)dst, *(uint16_t*)src)); if (bt == jl_char_type) return jl_box_char(jl_atomic_exchange((_Atomic(uint32_t)*)dst, *(uint32_t*)src)); + assert(!bt->smalltag); jl_task_t *ct = jl_current_task; jl_value_t *v = jl_gc_alloc(ct->ptls, jl_datatype_size(bt), bt); if (nb == 1) @@ -1224,30 +1228,29 @@ JL_DLLEXPORT jl_value_t *jl_atomic_cmpswap_bits(jl_datatype_t *dt, jl_datatype_t } // used by boot.jl -JL_DLLEXPORT jl_value_t *jl_typemax_uint(jl_value_t *bt) +JL_DLLEXPORT jl_value_t *jl_typemax_uint(jl_datatype_t *bt) { uint64_t data = 0xffffffffffffffffULL; jl_task_t *ct = jl_current_task; jl_value_t *v = jl_gc_alloc(ct->ptls, sizeof(size_t), bt); + if (bt->smalltag) + jl_set_typetagof(v, bt->smalltag, 0); memcpy(v, &data, sizeof(size_t)); return v; } -#define PERMBOXN_FUNC(nb,nw) \ - jl_value_t *jl_permbox##nb(jl_datatype_t *t, int##nb##_t x) \ +#define PERMBOXN_FUNC(nb) \ + jl_value_t *jl_permbox##nb(jl_datatype_t *t, uintptr_t tag, uint##nb##_t x) \ { /* n.b. t must be a concrete isbits datatype of the right size */ \ - jl_value_t *v = jl_gc_permobj(nw * sizeof(void*), t); \ - *(int##nb##_t*)jl_data_ptr(v) = x; \ + jl_value_t *v = jl_gc_permobj(LLT_ALIGN(nb, sizeof(void*)), t); \ + if (tag) jl_set_typetagof(v, tag, GC_OLD_MARKED); \ + *(uint##nb##_t*)jl_data_ptr(v) = x; \ return v; \ } -PERMBOXN_FUNC(8, 1) -PERMBOXN_FUNC(16, 1) -PERMBOXN_FUNC(32, 1) -#ifdef _P64 -PERMBOXN_FUNC(64, 1) -#else -PERMBOXN_FUNC(64, 2) -#endif +PERMBOXN_FUNC(8) +PERMBOXN_FUNC(16) +PERMBOXN_FUNC(32) +PERMBOXN_FUNC(64) #define UNBOX_FUNC(j_type,c_type) \ JL_DLLEXPORT c_type jl_unbox_##j_type(jl_value_t *v) \ @@ -1270,27 +1273,27 @@ UNBOX_FUNC(float64, double) UNBOX_FUNC(voidpointer, void*) UNBOX_FUNC(uint8pointer, uint8_t*) -#define BOX_FUNC(typ,c_type,pfx,nw) \ +#define BOX_FUNC(typ,c_type,pfx) \ JL_DLLEXPORT jl_value_t *pfx##_##typ(c_type x) \ { \ jl_task_t *ct = jl_current_task; \ - jl_value_t *v = jl_gc_alloc(ct->ptls, nw * sizeof(void*), \ + jl_value_t *v = jl_gc_alloc(ct->ptls, LLT_ALIGN(sizeof(x), sizeof(void*)), \ jl_##typ##_type); \ *(c_type*)jl_data_ptr(v) = x; \ return v; \ } -BOX_FUNC(float32, float, jl_box, 1) -BOX_FUNC(voidpointer, void*, jl_box, 1) -BOX_FUNC(uint8pointer, uint8_t*, jl_box, 1) -#ifdef _P64 -BOX_FUNC(float64, double, jl_box, 1) -#else -BOX_FUNC(float64, double, jl_box, 2) -#endif +BOX_FUNC(float32, float, jl_box) +BOX_FUNC(float64, double, jl_box) +BOX_FUNC(voidpointer, void*, jl_box) +BOX_FUNC(uint8pointer, uint8_t*, jl_box) #define NBOX_C 1024 -#define SIBOX_FUNC(typ,c_type,nw)\ +// some shims to support UIBOX_FUNC definition +#define jl_ssavalue_tag (((uintptr_t)jl_ssavalue_type) >> 4) +#define jl_slotnumber_tag (((uintptr_t)jl_slotnumber_type) >> 4) + +#define SIBOX_FUNC(typ,c_type) \ static jl_value_t *boxed_##typ##_cache[NBOX_C]; \ JL_DLLEXPORT jl_value_t *jl_box_##typ(c_type x) \ { \ @@ -1298,36 +1301,33 @@ BOX_FUNC(float64, double, jl_box, 2) c_type idx = x+NBOX_C/2; \ if ((u##c_type)idx < (u##c_type)NBOX_C) \ return boxed_##typ##_cache[idx]; \ - jl_value_t *v = jl_gc_alloc(ct->ptls, nw * sizeof(void*), \ + jl_value_t *v = jl_gc_alloc(ct->ptls, LLT_ALIGN(sizeof(x), sizeof(void*)), \ jl_##typ##_type); \ + jl_set_typetagof(v, jl_##typ##_tag, 0); \ *(c_type*)jl_data_ptr(v) = x; \ return v; \ } -#define UIBOX_FUNC(typ,c_type,nw) \ +#define UIBOX_FUNC(typ,c_type) \ static jl_value_t *boxed_##typ##_cache[NBOX_C]; \ JL_DLLEXPORT jl_value_t *jl_box_##typ(c_type x) \ { \ jl_task_t *ct = jl_current_task; \ if (x < NBOX_C) \ return boxed_##typ##_cache[x]; \ - jl_value_t *v = jl_gc_alloc(ct->ptls, nw * sizeof(void*), \ + jl_value_t *v = jl_gc_alloc(ct->ptls, LLT_ALIGN(sizeof(x), sizeof(void*)), \ jl_##typ##_type); \ + jl_set_typetagof(v, jl_##typ##_tag, 0); \ *(c_type*)jl_data_ptr(v) = x; \ return v; \ } -SIBOX_FUNC(int16, int16_t, 1) -SIBOX_FUNC(int32, int32_t, 1) -UIBOX_FUNC(uint16, uint16_t, 1) -UIBOX_FUNC(uint32, uint32_t, 1) -UIBOX_FUNC(ssavalue, size_t, 1) -UIBOX_FUNC(slotnumber, size_t, 1) -#ifdef _P64 -SIBOX_FUNC(int64, int64_t, 1) -UIBOX_FUNC(uint64, uint64_t, 1) -#else -SIBOX_FUNC(int64, int64_t, 2) -UIBOX_FUNC(uint64, uint64_t, 2) -#endif +SIBOX_FUNC(int16, int16_t) +SIBOX_FUNC(int32, int32_t) +UIBOX_FUNC(uint16, uint16_t) +UIBOX_FUNC(uint32, uint32_t) +UIBOX_FUNC(ssavalue, size_t) +UIBOX_FUNC(slotnumber, size_t) +SIBOX_FUNC(int64, int64_t) +UIBOX_FUNC(uint64, uint64_t) static jl_value_t *boxed_char_cache[128]; JL_DLLEXPORT jl_value_t *jl_box_char(uint32_t x) @@ -1337,6 +1337,7 @@ JL_DLLEXPORT jl_value_t *jl_box_char(uint32_t x) if (u < 128) return boxed_char_cache[(uint8_t)u]; jl_value_t *v = jl_gc_alloc(ct->ptls, sizeof(void*), jl_char_type); + jl_set_typetagof(v, jl_char_tag, 0); *(uint32_t*)jl_data_ptr(v) = x; return v; } @@ -1356,35 +1357,35 @@ void jl_init_int32_int64_cache(void) { int64_t i; for(i=0; i < NBOX_C; i++) { - boxed_int32_cache[i] = jl_permbox32(jl_int32_type, i-NBOX_C/2); - boxed_int64_cache[i] = jl_permbox64(jl_int64_type, i-NBOX_C/2); + boxed_int32_cache[i] = jl_permbox32(jl_int32_type, jl_int32_tag, i-NBOX_C/2); + boxed_int64_cache[i] = jl_permbox64(jl_int64_type, jl_int64_tag, i-NBOX_C/2); #ifdef _P64 - boxed_ssavalue_cache[i] = jl_permbox64(jl_ssavalue_type, i); - boxed_slotnumber_cache[i] = jl_permbox64(jl_slotnumber_type, i); + boxed_ssavalue_cache[i] = jl_permbox64(jl_ssavalue_type, 0, i); + boxed_slotnumber_cache[i] = jl_permbox64(jl_slotnumber_type, 0, i); #else - boxed_ssavalue_cache[i] = jl_permbox32(jl_ssavalue_type, i); - boxed_slotnumber_cache[i] = jl_permbox32(jl_slotnumber_type, i); + boxed_ssavalue_cache[i] = jl_permbox32(jl_ssavalue_type, 0, i); + boxed_slotnumber_cache[i] = jl_permbox32(jl_slotnumber_type, 0, i); #endif } for(i=0; i < 256; i++) { - jl_boxed_uint8_cache[i] = jl_permbox8(jl_uint8_type, i); + jl_boxed_uint8_cache[i] = jl_permbox8(jl_uint8_type, jl_uint8_tag, i); } } void jl_init_box_caches(void) { - int64_t i; - for(i=0; i < 128; i++) { - boxed_char_cache[i] = jl_permbox32(jl_char_type, i << 24); + uint32_t i; + for (i = 0; i < 128; i++) { + boxed_char_cache[i] = jl_permbox32(jl_char_type, jl_char_tag, i << 24); } - for(i=0; i < 256; i++) { - jl_boxed_int8_cache[i] = jl_permbox8(jl_int8_type, i); + for (i = 0; i < 256; i++) { + jl_boxed_int8_cache[i] = jl_permbox8(jl_int8_type, jl_int8_tag, i); } - for(i=0; i < NBOX_C; i++) { - boxed_int16_cache[i] = jl_permbox16(jl_int16_type, i-NBOX_C/2); - boxed_uint16_cache[i] = jl_permbox16(jl_uint16_type, i); - boxed_uint32_cache[i] = jl_permbox32(jl_uint32_type, i); - boxed_uint64_cache[i] = jl_permbox64(jl_uint64_type, i); + for (i = 0; i < NBOX_C; i++) { + boxed_int16_cache[i] = jl_permbox16(jl_int16_type, jl_int16_tag, i-NBOX_C/2); + boxed_uint16_cache[i] = jl_permbox16(jl_uint16_type, jl_uint16_tag, i); + boxed_uint32_cache[i] = jl_permbox32(jl_uint32_type, jl_uint32_tag, i); + boxed_uint64_cache[i] = jl_permbox64(jl_uint64_type, jl_uint64_tag, i); } } @@ -1408,6 +1409,8 @@ JL_DLLEXPORT jl_value_t *jl_new_struct(jl_datatype_t *type, ...) size_t i, nf = jl_datatype_nfields(type); va_start(args, type); jl_value_t *jv = jl_gc_alloc(ct->ptls, jl_datatype_size(type), type); + if (type->smalltag) // TODO: move to callers? + jl_set_typetagof(jv, type->smalltag, 0); if (nf > 0 && jl_field_offset(type, 0) != 0) { memset(jv, 0, jl_field_offset(type, 0)); } @@ -1435,6 +1438,8 @@ JL_DLLEXPORT jl_value_t *jl_new_structv(jl_datatype_t *type, jl_value_t **args, if (type->instance != NULL) return type->instance; jl_value_t *jv = jl_gc_alloc(ct->ptls, jl_datatype_size(type), type); + if (type->smalltag) // TODO: do we need this? + jl_set_typetagof(jv, type->smalltag, 0); if (jl_datatype_nfields(type) > 0) { if (jl_field_offset(type, 0) != 0) { memset(jl_data_ptr(jv), 0, jl_field_offset(type, 0)); @@ -1476,6 +1481,8 @@ JL_DLLEXPORT jl_value_t *jl_new_structt(jl_datatype_t *type, jl_value_t *tup) } size_t size = jl_datatype_size(type); jl_value_t *jv = jl_gc_alloc(ct->ptls, size, type); + if (type->smalltag) // TODO: do we need this? + jl_set_typetagof(jv, type->smalltag, 0); if (nf == 0) return jv; jl_value_t *fi = NULL; @@ -1509,6 +1516,8 @@ JL_DLLEXPORT jl_value_t *jl_new_struct_uninit(jl_datatype_t *type) } size_t size = jl_datatype_size(type); jl_value_t *jv = jl_gc_alloc(ct->ptls, size, type); + if (type->smalltag) // TODO: do we need this? + jl_set_typetagof(jv, type->smalltag, 0); if (size > 0) memset(jl_data_ptr(jv), 0, size); return jv; diff --git a/src/gc-debug.c b/src/gc-debug.c index eeb170f0299a1..a5b779c8161b1 100644 --- a/src/gc-debug.c +++ b/src/gc-debug.c @@ -369,10 +369,10 @@ static void gc_verify_tags_page(jl_gc_pagemeta_t *pg) if (!in_freelist) { jl_value_t *dt = jl_typeof(jl_valueof(v)); if (dt != (jl_value_t*)jl_buff_tag && - // the following are used by the deserializer to invalidate objects - v->header != 0x10 && v->header != 0x20 && - v->header != 0x30 && v->header != 0x40 && - v->header != 0x50 && v->header != 0x60) { + // the following may be use (by the deserializer) to invalidate objects + v->header != 0xf10 && v->header != 0xf20 && + v->header != 0xf30 && v->header != 0xf40 && + v->header != 0xf50 && v->header != 0xf60) { assert(jl_typeof(dt) == (jl_value_t*)jl_datatype_type); } } diff --git a/src/gc.c b/src/gc.c index 60b110826ee80..f421ce4363c67 100644 --- a/src/gc.c +++ b/src/gc.c @@ -579,7 +579,7 @@ JL_DLLEXPORT void jl_gc_add_quiescent(jl_ptls_t ptls, void **v, void *f) JL_NOTS JL_DLLEXPORT void jl_gc_add_finalizer_th(jl_ptls_t ptls, jl_value_t *v, jl_function_t *f) JL_NOTSAFEPOINT { - if (__unlikely(jl_typeis(f, jl_voidpointer_type))) { + if (__unlikely(jl_typetagis(f, jl_voidpointer_type))) { jl_gc_add_ptr_finalizer(ptls, v, jl_unbox_voidpointer(f)); } else { @@ -2232,6 +2232,8 @@ STATIC_INLINE void gc_mark_stack(jl_ptls_t ptls, jl_gcframe_t *s, uint32_t nroot if (nroots & 1) { void **slot = (void **)gc_read_stack(&rts[i], offset, lb, ub); new_obj = (jl_value_t *)gc_read_stack(slot, offset, lb, ub); + if (new_obj == NULL) + continue; } else { new_obj = (jl_value_t *)gc_read_stack(&rts[i], offset, lb, ub); @@ -2243,11 +2245,13 @@ STATIC_INLINE void gc_mark_stack(jl_ptls_t ptls, jl_gcframe_t *s, uint32_t nroot } if (gc_ptr_tag(new_obj, 2)) continue; + // conservatively check for the presence of any smalltag type, instead of just NULL + // in the very unlikely event that codegen decides to root the result of julia.typeof + if (new_obj < (jl_value_t*)((uintptr_t)jl_max_tags << 4)) + continue; } - if (new_obj != NULL) { - gc_try_claim_and_push(mq, new_obj, NULL); - gc_heap_snapshot_record_frame_to_object_edge(s, new_obj); - } + gc_try_claim_and_push(mq, new_obj, NULL); + gc_heap_snapshot_record_frame_to_object_edge(s, new_obj); } jl_gcframe_t *sprev = (jl_gcframe_t *)gc_read_stack(&s->prev, offset, lb, ub); if (sprev == NULL) @@ -2389,7 +2393,7 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ jl_raise_debugger(); #endif jl_taggedvalue_t *o = jl_astaggedvalue(new_obj); - jl_datatype_t *vt = (jl_datatype_t *)(o->header & ~(uintptr_t)0xf); + uintptr_t vtag = o->header & ~(uintptr_t)0xf; uint8_t bits = (gc_old(o->header) && !mark_reset_age) ? GC_OLD_MARKED : GC_MARKED; int update_meta = __likely(!meta_updated && !gc_verifying); int foreign_alloc = 0; @@ -2399,23 +2403,140 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ update_meta = 0; } // Symbols are always marked - assert(vt != jl_symbol_type); - if (vt == jl_simplevector_type) { - size_t l = jl_svec_len(new_obj); - jl_value_t **data = jl_svec_data(new_obj); - size_t dtsz = l * sizeof(void *) + sizeof(jl_svec_t); - if (update_meta) - gc_setmark(ptls, o, bits, dtsz); - else if (foreign_alloc) - objprofile_count(vt, bits == GC_OLD_MARKED, dtsz); - jl_value_t *objary_parent = new_obj; - jl_value_t **objary_begin = data; - jl_value_t **objary_end = data + l; - uint32_t step = 1; - uintptr_t nptr = (l << 2) | (bits & GC_OLD); - gc_mark_objarray(ptls, objary_parent, objary_begin, objary_end, step, nptr); + assert(vtag != (uintptr_t)jl_symbol_type && vtag != jl_symbol_tag << 4); + if (vtag == (jl_datatype_tag << 4) || + vtag == (jl_unionall_tag << 4) || + vtag == (jl_uniontype_tag << 4) || + vtag == (jl_tvar_tag << 4) || + vtag == (jl_vararg_tag << 4)) { + // these objects have pointers in them, but no other special handling + // so we want these to fall through to the end + vtag = (uintptr_t)small_typeof[vtag / sizeof(*small_typeof)]; + } + else if (vtag < jl_max_tags << 4) { + // these objects either have specialing handling + if (vtag == jl_simplevector_tag << 4) { + size_t l = jl_svec_len(new_obj); + jl_value_t **data = jl_svec_data(new_obj); + size_t dtsz = l * sizeof(void *) + sizeof(jl_svec_t); + if (update_meta) + gc_setmark(ptls, o, bits, dtsz); + else if (foreign_alloc) + objprofile_count(jl_simplevector_type, bits == GC_OLD_MARKED, dtsz); + jl_value_t *objary_parent = new_obj; + jl_value_t **objary_begin = data; + jl_value_t **objary_end = data + l; + uint32_t step = 1; + uintptr_t nptr = (l << 2) | (bits & GC_OLD); + gc_mark_objarray(ptls, objary_parent, objary_begin, objary_end, step, nptr); + } + else if (vtag == jl_module_tag << 4) { + if (update_meta) + gc_setmark(ptls, o, bits, sizeof(jl_module_t)); + else if (foreign_alloc) + objprofile_count(jl_module_type, bits == GC_OLD_MARKED, sizeof(jl_module_t)); + jl_module_t *mb_parent = (jl_module_t *)new_obj; + jl_svec_t *bindings = jl_atomic_load_relaxed(&mb_parent->bindings); + jl_binding_t **table = (jl_binding_t**)jl_svec_data(bindings); + size_t bsize = jl_svec_len(bindings); + uintptr_t nptr = ((bsize + mb_parent->usings.len + 1) << 2) | (bits & GC_OLD); + jl_binding_t **mb_begin = table + 1; + jl_binding_t **mb_end = table + bsize; + gc_mark_module_binding(ptls, mb_parent, mb_begin, mb_end, nptr, bits); + } + else if (vtag == jl_task_tag << 4) { + if (update_meta) + gc_setmark(ptls, o, bits, sizeof(jl_task_t)); + else if (foreign_alloc) + objprofile_count(jl_task_type, bits == GC_OLD_MARKED, sizeof(jl_task_t)); + jl_task_t *ta = (jl_task_t *)new_obj; + gc_scrub_record_task(ta); + if (gc_cblist_task_scanner) { + int16_t tid = jl_atomic_load_relaxed(&ta->tid); + gc_invoke_callbacks(jl_gc_cb_task_scanner_t, gc_cblist_task_scanner, + (ta, tid != -1 && ta == gc_all_tls_states[tid]->root_task)); + } + #ifdef COPY_STACKS + void *stkbuf = ta->stkbuf; + if (stkbuf && ta->copy_stack) { + gc_setmark_buf_(ptls, stkbuf, bits, ta->bufsz); + // For gc_heap_snapshot_record: + // TODO: attribute size of stack + // TODO: edge to stack data + // TODO: synthetic node for stack data (how big is it?) + } + #endif + jl_gcframe_t *s = ta->gcstack; + size_t nroots; + uintptr_t offset = 0; + uintptr_t lb = 0; + uintptr_t ub = (uintptr_t)-1; + #ifdef COPY_STACKS + if (stkbuf && ta->copy_stack && !ta->ptls) { + int16_t tid = jl_atomic_load_relaxed(&ta->tid); + assert(tid >= 0); + jl_ptls_t ptls2 = gc_all_tls_states[tid]; + ub = (uintptr_t)ptls2->stackbase; + lb = ub - ta->copy_stack; + offset = (uintptr_t)stkbuf - lb; + } + #endif + if (s != NULL) { + nroots = gc_read_stack(&s->nroots, offset, lb, ub); + gc_heap_snapshot_record_task_to_frame_edge(ta, s); + assert(nroots <= UINT32_MAX); + gc_mark_stack(ptls, s, (uint32_t)nroots, offset, lb, ub); + } + if (ta->excstack) { + jl_excstack_t *excstack = ta->excstack; + gc_heap_snapshot_record_task_to_frame_edge(ta, excstack); + size_t itr = ta->excstack->top; + gc_setmark_buf_(ptls, excstack, bits, + sizeof(jl_excstack_t) + + sizeof(uintptr_t) * excstack->reserved_size); + gc_mark_excstack(ptls, excstack, itr); + } + const jl_datatype_layout_t *layout = jl_task_type->layout; + assert(layout->fielddesc_type == 0); + assert(layout->nfields > 0); + uint32_t npointers = layout->npointers; + char *obj8_parent = (char *)ta; + uint8_t *obj8_begin = (uint8_t *)jl_dt_layout_ptrs(layout); + uint8_t *obj8_end = obj8_begin + npointers; + // assume tasks always reference young objects: set lowest bit + uintptr_t nptr = (npointers << 2) | 1 | bits; + new_obj = gc_mark_obj8(ptls, obj8_parent, obj8_begin, obj8_end, nptr); + if (new_obj != NULL) { + if (!meta_updated) + goto mark_obj; + else + gc_ptr_queue_push(mq, new_obj); + } + } + else if (vtag == jl_string_tag << 4) { + size_t dtsz = jl_string_len(new_obj) + sizeof(size_t) + 1; + if (update_meta) + gc_setmark(ptls, o, bits, dtsz); + else if (foreign_alloc) + objprofile_count(jl_string_type, bits == GC_OLD_MARKED, dtsz); + } + else { + jl_datatype_t *vt = small_typeof[vtag / sizeof(*small_typeof)]; + size_t dtsz = jl_datatype_size(vt); + if (update_meta) + gc_setmark(ptls, o, bits, dtsz); + else if (foreign_alloc) + objprofile_count(vt, bits == GC_OLD_MARKED, dtsz); + } + return; + } + else { + jl_datatype_t *vt = (jl_datatype_t *)vtag; + if (__unlikely(!jl_is_datatype(vt) || vt->smalltag)) + gc_assert_datatype_fail(ptls, vt, mq); } - else if (vt->name == jl_array_typename) { + jl_datatype_t *vt = (jl_datatype_t *)vtag; + if (vt->name == jl_array_typename) { jl_array_t *a = (jl_array_t *)new_obj; jl_array_flags_t flags = a->flags; if (update_meta) { @@ -2504,82 +2625,27 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ assert(0 && "unimplemented"); } } + return; } - else if (vt == jl_module_type) { - if (update_meta) - gc_setmark(ptls, o, bits, sizeof(jl_module_t)); - else if (foreign_alloc) - objprofile_count(vt, bits == GC_OLD_MARKED, sizeof(jl_module_t)); - jl_module_t *mb_parent = (jl_module_t *)new_obj; - jl_svec_t *bindings = jl_atomic_load_relaxed(&mb_parent->bindings); - jl_binding_t **table = (jl_binding_t**)jl_svec_data(bindings); - size_t bsize = jl_svec_len(bindings); - uintptr_t nptr = ((bsize + mb_parent->usings.len + 1) << 2) | (bits & GC_OLD); - jl_binding_t **mb_begin = table + 1; - jl_binding_t **mb_end = table + bsize; - gc_mark_module_binding(ptls, mb_parent, mb_begin, mb_end, nptr, bits); - } - else if (vt == jl_task_type) { - if (update_meta) - gc_setmark(ptls, o, bits, sizeof(jl_task_t)); - else if (foreign_alloc) - objprofile_count(vt, bits == GC_OLD_MARKED, sizeof(jl_task_t)); - jl_task_t *ta = (jl_task_t *)new_obj; - gc_scrub_record_task(ta); - if (gc_cblist_task_scanner) { - int16_t tid = jl_atomic_load_relaxed(&ta->tid); - gc_invoke_callbacks(jl_gc_cb_task_scanner_t, gc_cblist_task_scanner, - (ta, tid != -1 && ta == gc_all_tls_states[tid]->root_task)); - } - #ifdef COPY_STACKS - void *stkbuf = ta->stkbuf; - if (stkbuf && ta->copy_stack) { - gc_setmark_buf_(ptls, stkbuf, bits, ta->bufsz); - // For gc_heap_snapshot_record: - // TODO: attribute size of stack - // TODO: edge to stack data - // TODO: synthetic node for stack data (how big is it?) - } - #endif - jl_gcframe_t *s = ta->gcstack; - size_t nroots; - uintptr_t offset = 0; - uintptr_t lb = 0; - uintptr_t ub = (uintptr_t)-1; - #ifdef COPY_STACKS - if (stkbuf && ta->copy_stack && !ta->ptls) { - int16_t tid = jl_atomic_load_relaxed(&ta->tid); - assert(tid >= 0); - jl_ptls_t ptls2 = gc_all_tls_states[tid]; - ub = (uintptr_t)ptls2->stackbase; - lb = ub - ta->copy_stack; - offset = (uintptr_t)stkbuf - lb; - } - #endif - if (s != NULL) { - nroots = gc_read_stack(&s->nroots, offset, lb, ub); - gc_heap_snapshot_record_task_to_frame_edge(ta, s); - assert(nroots <= UINT32_MAX); - gc_mark_stack(ptls, s, (uint32_t)nroots, offset, lb, ub); - } - if (ta->excstack) { - jl_excstack_t *excstack = ta->excstack; - gc_heap_snapshot_record_task_to_frame_edge(ta, excstack); - size_t itr = ta->excstack->top; - gc_setmark_buf_(ptls, excstack, bits, - sizeof(jl_excstack_t) + - sizeof(uintptr_t) * excstack->reserved_size); - gc_mark_excstack(ptls, excstack, itr); - } - const jl_datatype_layout_t *layout = jl_task_type->layout; - assert(layout->fielddesc_type == 0); - assert(layout->nfields > 0); - uint32_t npointers = layout->npointers; - char *obj8_parent = (char *)ta; + size_t dtsz = jl_datatype_size(vt); + if (update_meta) + gc_setmark(ptls, o, bits, dtsz); + else if (foreign_alloc) + objprofile_count(vt, bits == GC_OLD_MARKED, dtsz); + if (vt == jl_weakref_type) + return; + const jl_datatype_layout_t *layout = vt->layout; + uint32_t npointers = layout->npointers; + if (npointers == 0) + return; + uintptr_t nptr = (npointers << 2 | (bits & GC_OLD)); + assert((layout->nfields > 0 || layout->fielddesc_type == 3) && + "opaque types should have been handled specially"); + if (layout->fielddesc_type == 0) { + char *obj8_parent = (char *)new_obj; uint8_t *obj8_begin = (uint8_t *)jl_dt_layout_ptrs(layout); uint8_t *obj8_end = obj8_begin + npointers; - // assume tasks always reference young objects: set lowest bit - uintptr_t nptr = (npointers << 2) | 1 | bits; + assert(obj8_begin < obj8_end); new_obj = gc_mark_obj8(ptls, obj8_parent, obj8_begin, obj8_end, nptr); if (new_obj != NULL) { if (!meta_updated) @@ -2588,80 +2654,42 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ gc_ptr_queue_push(mq, new_obj); } } - else if (vt == jl_string_type) { - size_t dtsz = jl_string_len(new_obj) + sizeof(size_t) + 1; - if (update_meta) - gc_setmark(ptls, o, bits, dtsz); - else if (foreign_alloc) - objprofile_count(vt, bits == GC_OLD_MARKED, dtsz); - } - else { - if (__unlikely(!jl_is_datatype(vt))) - gc_assert_datatype_fail(ptls, vt, mq); - size_t dtsz = jl_datatype_size(vt); - if (update_meta) - gc_setmark(ptls, o, bits, dtsz); - else if (foreign_alloc) - objprofile_count(vt, bits == GC_OLD_MARKED, dtsz); - if (vt == jl_weakref_type) - return; - const jl_datatype_layout_t *layout = vt->layout; - uint32_t npointers = layout->npointers; - if (npointers == 0) - return; - uintptr_t nptr = (npointers << 2 | (bits & GC_OLD)); - assert((layout->nfields > 0 || layout->fielddesc_type == 3) && - "opaque types should have been handled specially"); - if (layout->fielddesc_type == 0) { - char *obj8_parent = (char *)new_obj; - uint8_t *obj8_begin = (uint8_t *)jl_dt_layout_ptrs(layout); - uint8_t *obj8_end = obj8_begin + npointers; - assert(obj8_begin < obj8_end); - new_obj = gc_mark_obj8(ptls, obj8_parent, obj8_begin, obj8_end, nptr); - if (new_obj != NULL) { - if (!meta_updated) - goto mark_obj; - else - gc_ptr_queue_push(mq, new_obj); - } - } - else if (layout->fielddesc_type == 1) { - char *obj16_parent = (char *)new_obj; - uint16_t *obj16_begin = (uint16_t *)jl_dt_layout_ptrs(layout); - uint16_t *obj16_end = obj16_begin + npointers; - assert(obj16_begin < obj16_end); - new_obj = gc_mark_obj16(ptls, obj16_parent, obj16_begin, obj16_end, nptr); - if (new_obj != NULL) { - if (!meta_updated) - goto mark_obj; - else - gc_ptr_queue_push(mq, new_obj); - } - } - else if (layout->fielddesc_type == 2) { - // This is very uncommon - // Do not do store to load forwarding to save some code size - char *obj32_parent = (char *)new_obj; - uint32_t *obj32_begin = (uint32_t *)jl_dt_layout_ptrs(layout); - uint32_t *obj32_end = obj32_begin + npointers; - assert(obj32_begin < obj32_end); - new_obj = gc_mark_obj32(ptls, obj32_parent, obj32_begin, obj32_end, nptr); - if (new_obj != NULL) { - if (!meta_updated) - goto mark_obj; - else - gc_ptr_queue_push(mq, new_obj); - } + else if (layout->fielddesc_type == 1) { + char *obj16_parent = (char *)new_obj; + uint16_t *obj16_begin = (uint16_t *)jl_dt_layout_ptrs(layout); + uint16_t *obj16_end = obj16_begin + npointers; + assert(obj16_begin < obj16_end); + new_obj = gc_mark_obj16(ptls, obj16_parent, obj16_begin, obj16_end, nptr); + if (new_obj != NULL) { + if (!meta_updated) + goto mark_obj; + else + gc_ptr_queue_push(mq, new_obj); } - else { - assert(layout->fielddesc_type == 3); - jl_fielddescdyn_t *desc = (jl_fielddescdyn_t *)jl_dt_layout_fields(layout); - int old = jl_astaggedvalue(new_obj)->bits.gc & 2; - uintptr_t young = desc->markfunc(ptls, new_obj); - if (old && young) - gc_mark_push_remset(ptls, new_obj, young * 4 + 3); + } + else if (layout->fielddesc_type == 2) { + // This is very uncommon + // Do not do store to load forwarding to save some code size + char *obj32_parent = (char *)new_obj; + uint32_t *obj32_begin = (uint32_t *)jl_dt_layout_ptrs(layout); + uint32_t *obj32_end = obj32_begin + npointers; + assert(obj32_begin < obj32_end); + new_obj = gc_mark_obj32(ptls, obj32_parent, obj32_begin, obj32_end, nptr); + if (new_obj != NULL) { + if (!meta_updated) + goto mark_obj; + else + gc_ptr_queue_push(mq, new_obj); } } + else { + assert(layout->fielddesc_type == 3); + jl_fielddescdyn_t *desc = (jl_fielddescdyn_t *)jl_dt_layout_fields(layout); + int old = jl_astaggedvalue(new_obj)->bits.gc & 2; + uintptr_t young = desc->markfunc(ptls, new_obj); + if (old && young) + gc_mark_push_remset(ptls, new_obj, young * 4 + 3); + } } } diff --git a/src/gf.c b/src/gf.c index 00a51f582a597..5df3e18ff8db7 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2998,7 +2998,7 @@ STATIC_INLINE jl_method_instance_t *jl_lookup_generic_(jl_value_t *F, jl_value_t jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); entry = NULL; if (leafcache != (jl_array_t*)jl_an_empty_vec_any && - jl_typeis(jl_atomic_load_relaxed(&mt->cache), jl_typemap_level_type)) { + jl_typetagis(jl_atomic_load_relaxed(&mt->cache), jl_typemap_level_type)) { // hashing args is expensive, but looking at mt->cache is probably even more expensive tt = lookup_arg_type_tuple(F, args, nargs); if (tt != NULL) diff --git a/src/init.c b/src/init.c index 36e83fdd9c24d..02769e03c668e 100644 --- a/src/init.c +++ b/src/init.c @@ -381,7 +381,7 @@ JL_DLLEXPORT void jl_postoutput_hook(void) return; } -static void post_boot_hooks(void); +void post_boot_hooks(void); JL_DLLEXPORT void *jl_libjulia_internal_handle; JL_DLLEXPORT void *jl_libjulia_handle; @@ -894,80 +894,6 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_ jl_install_sigint_handler(); } -static jl_value_t *core(const char *name) -{ - return jl_get_global(jl_core_module, jl_symbol(name)); -} - -// fetch references to things defined in boot.jl -static void post_boot_hooks(void) -{ - jl_char_type = (jl_datatype_t*)core("Char"); - jl_int8_type = (jl_datatype_t*)core("Int8"); - jl_int16_type = (jl_datatype_t*)core("Int16"); - jl_float16_type = (jl_datatype_t*)core("Float16"); - jl_float32_type = (jl_datatype_t*)core("Float32"); - jl_float64_type = (jl_datatype_t*)core("Float64"); - jl_floatingpoint_type = (jl_datatype_t*)core("AbstractFloat"); - jl_number_type = (jl_datatype_t*)core("Number"); - jl_signed_type = (jl_datatype_t*)core("Signed"); - jl_datatype_t *jl_unsigned_type = (jl_datatype_t*)core("Unsigned"); - jl_datatype_t *jl_integer_type = (jl_datatype_t*)core("Integer"); - - jl_bool_type->super = jl_integer_type; - jl_uint8_type->super = jl_unsigned_type; - jl_uint16_type->super = jl_unsigned_type; - jl_uint32_type->super = jl_unsigned_type; - jl_uint64_type->super = jl_unsigned_type; - jl_int32_type->super = jl_signed_type; - jl_int64_type->super = jl_signed_type; - - jl_errorexception_type = (jl_datatype_t*)core("ErrorException"); - jl_stackovf_exception = jl_new_struct_uninit((jl_datatype_t*)core("StackOverflowError")); - jl_diverror_exception = jl_new_struct_uninit((jl_datatype_t*)core("DivideError")); - jl_undefref_exception = jl_new_struct_uninit((jl_datatype_t*)core("UndefRefError")); - jl_undefvarerror_type = (jl_datatype_t*)core("UndefVarError"); - jl_atomicerror_type = (jl_datatype_t*)core("ConcurrencyViolationError"); - jl_interrupt_exception = jl_new_struct_uninit((jl_datatype_t*)core("InterruptException")); - jl_boundserror_type = (jl_datatype_t*)core("BoundsError"); - jl_memory_exception = jl_new_struct_uninit((jl_datatype_t*)core("OutOfMemoryError")); - jl_readonlymemory_exception = jl_new_struct_uninit((jl_datatype_t*)core("ReadOnlyMemoryError")); - jl_typeerror_type = (jl_datatype_t*)core("TypeError"); - jl_argumenterror_type = (jl_datatype_t*)core("ArgumentError"); - jl_methoderror_type = (jl_datatype_t*)core("MethodError"); - jl_loaderror_type = (jl_datatype_t*)core("LoadError"); - jl_initerror_type = (jl_datatype_t*)core("InitError"); - jl_pair_type = core("Pair"); - jl_kwcall_func = core("kwcall"); - jl_kwcall_mt = ((jl_datatype_t*)jl_typeof(jl_kwcall_func))->name->mt; - jl_atomic_store_relaxed(&jl_kwcall_mt->max_args, 0); - - jl_weakref_type = (jl_datatype_t*)core("WeakRef"); - jl_vecelement_typename = ((jl_datatype_t*)jl_unwrap_unionall(core("VecElement")))->name; - - jl_init_box_caches(); - - // set module field of primitive types - jl_svec_t *bindings = jl_atomic_load_relaxed(&jl_core_module->bindings); - jl_value_t **table = jl_svec_data(bindings); - for (size_t i = 0; i < jl_svec_len(bindings); i++) { - if (table[i] != jl_nothing) { - jl_binding_t *b = (jl_binding_t*)table[i]; - jl_value_t *v = jl_atomic_load_relaxed(&b->value); - if (v) { - if (jl_is_unionall(v)) - v = jl_unwrap_unionall(v); - if (jl_is_datatype(v)) { - jl_datatype_t *tt = (jl_datatype_t*)v; - tt->name->module = jl_core_module; - if (tt->name->mt) - tt->name->mt->module = jl_core_module; - } - } - } - } -} - #ifdef __cplusplus } #endif diff --git a/src/interpreter.c b/src/interpreter.c index 7a699223d746e..c962abaa3a31a 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -102,7 +102,7 @@ static jl_value_t *eval_methoddef(jl_expr_t *ex, interpreter_state *s) fname = eval_value(args[0], s); jl_methtable_t *mt = NULL; - if (jl_typeis(fname, jl_methtable_type)) { + if (jl_typetagis(fname, jl_methtable_type)) { mt = (jl_methtable_t*)fname; } atypes = eval_value(args[1], s); @@ -663,7 +663,7 @@ jl_value_t *NOINLINE jl_fptr_interpret_call(jl_value_t *f, jl_value_t **args, ui size_t world = ct->world_age; jl_code_info_t *src = jl_code_for_interpreter(mi, world); jl_array_t *stmts = src->code; - assert(jl_typeis(stmts, jl_array_any_type)); + assert(jl_typetagis(stmts, jl_array_any_type)); unsigned nroots = jl_source_nslots(src) + jl_source_nssavalues(src) + 2; jl_value_t **locals = NULL; JL_GC_PUSHFRAME(s, locals, nroots); @@ -748,7 +748,7 @@ jl_value_t *NOINLINE jl_interpret_toplevel_thunk(jl_module_t *m, jl_code_info_t unsigned nroots = jl_source_nslots(src) + jl_source_nssavalues(src); JL_GC_PUSHFRAME(s, s->locals, nroots); jl_array_t *stmts = src->code; - assert(jl_typeis(stmts, jl_array_any_type)); + assert(jl_typetagis(stmts, jl_array_any_type)); s->src = src; s->module = m; s->sparam_vals = jl_emptysvec; diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 91a06f2f10524..9fd0561971d02 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -514,7 +514,7 @@ static jl_cgval_t generic_bitcast(jl_codectx_t &ctx, const jl_cgval_t *argv) bool isboxed; Type *vxt = julia_type_to_llvm(ctx, v.typ, &isboxed); if (!jl_is_primitivetype(v.typ) || jl_datatype_size(v.typ) != nb) { - Value *typ = emit_typeof_boxed(ctx, v); + Value *typ = emit_typeof(ctx, v, false, false); if (!jl_is_primitivetype(v.typ)) { if (jl_is_datatype(v.typ) && !jl_is_abstracttype(v.typ)) { emit_error(ctx, "bitcast: value not a primitive type"); @@ -678,8 +678,7 @@ static jl_cgval_t emit_pointerref(jl_codectx_t &ctx, jl_cgval_t *argv) else if (!jl_isbits(ety)) { assert(jl_is_datatype(ety)); uint64_t size = jl_datatype_size(ety); - Value *strct = emit_allocobj(ctx, size, - literal_pointer_val(ctx, ety)); + Value *strct = emit_allocobj(ctx, (jl_datatype_t*)ety); im1 = ctx.builder.CreateMul(im1, ConstantInt::get(ctx.types().T_size, LLT_ALIGN(size, jl_datatype_align(ety)))); Value *thePtr = emit_unbox(ctx, getInt8PtrTy(ctx.builder.getContext()), e, e.typ); @@ -823,9 +822,7 @@ static jl_cgval_t emit_atomic_pointerref(jl_codectx_t &ctx, jl_cgval_t *argv) if (!jl_isbits(ety)) { assert(jl_is_datatype(ety)); - uint64_t size = jl_datatype_size(ety); - Value *strct = emit_allocobj(ctx, size, - literal_pointer_val(ctx, ety)); + Value *strct = emit_allocobj(ctx, (jl_datatype_t*)ety); Value *thePtr = emit_unbox(ctx, getInt8PtrTy(ctx.builder.getContext()), e, e.typ); Type *loadT = Type::getIntNTy(ctx.builder.getContext(), nb * 8); thePtr = emit_bitcast(ctx, thePtr, loadT->getPointerTo()); diff --git a/src/ircode.c b/src/ircode.c index 71ee292cbc397..e04ef3fa6d96c 100644 --- a/src/ircode.c +++ b/src/ircode.c @@ -147,7 +147,7 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) else if (v == (jl_value_t*)jl_base_module) { write_uint8(s->s, TAG_BASE); } - else if (jl_typeis(v, jl_string_type) && jl_string_len(v) == 0) { + else if (jl_typetagis(v, jl_string_tag << 4) && jl_string_len(v) == 0) { jl_encode_value(s, jl_an_empty_string); } else if (v == (jl_value_t*)s->method->module) { @@ -197,7 +197,7 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) write_uint8(s->s, TAG_LONG_SSAVALUE); write_uint16(s->s, ((jl_ssavalue_t*)v)->id); } - else if (jl_typeis(v, jl_slotnumber_type) && jl_slot_number(v) <= UINT16_MAX && jl_slot_number(v) >= 0) { + else if (jl_typetagis(v, jl_slotnumber_type) && jl_slot_number(v) <= UINT16_MAX && jl_slot_number(v) >= 0) { write_uint8(s->s, TAG_SLOTNUMBER); write_uint16(s->s, jl_slot_number(v)); } @@ -299,7 +299,7 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) else jl_encode_value(s, inner); } - else if (jl_typeis(v, jl_int64_type)) { + else if (jl_typetagis(v, jl_int64_tag << 4)) { void *data = jl_data_ptr(v); if (*(int64_t*)data >= INT16_MIN && *(int64_t*)data <= INT16_MAX) { write_uint8(s->s, TAG_SHORTER_INT64); @@ -314,14 +314,14 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) write_uint64(s->s, *(int64_t*)data); } } - else if (jl_typeis(v, jl_int32_type)) { + else if (jl_typetagis(v, jl_int32_tag << 4)) { jl_encode_int32(s, *(int32_t*)jl_data_ptr(v)); } - else if (jl_typeis(v, jl_uint8_type)) { + else if (jl_typetagis(v, jl_uint8_tag << 4)) { write_uint8(s->s, TAG_UINT8); write_int8(s->s, *(int8_t*)jl_data_ptr(v)); } - else if (jl_typeis(v, jl_lineinfonode_type)) { + else if (jl_typetagis(v, jl_lineinfonode_type)) { write_uint8(s->s, TAG_LINEINFO); for (i = 0; i < jl_datatype_nfields(jl_lineinfonode_type); i++) jl_encode_value(s, jl_get_nth_field(v, i)); @@ -330,7 +330,7 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) write_uint8(s->s, TAG_SINGLETON); jl_encode_value(s, jl_typeof(v)); } - else if (as_literal && jl_typeis(v, jl_string_type)) { + else if (as_literal && jl_typetagis(v, jl_string_tag << 4)) { write_uint8(s->s, TAG_STRING); write_int32(s->s, jl_string_len(v)); ios_write(s->s, jl_string_data(v), jl_string_len(v)); @@ -610,9 +610,12 @@ static jl_value_t *jl_decode_value_any(jl_ircode_state *s, uint8_t tag) JL_GC_DI { int32_t sz = (tag == TAG_SHORT_GENERAL ? read_uint8(s->s) : read_int32(s->s)); jl_value_t *v = jl_gc_alloc(s->ptls, sz, NULL); - jl_set_typeof(v, (void*)(intptr_t)0x50); + jl_set_typeof(v, (void*)(intptr_t)0xf50); jl_datatype_t *dt = (jl_datatype_t*)jl_decode_value(s); - jl_set_typeof(v, dt); + if (dt->smalltag) + jl_set_typetagof(v, dt->smalltag, 0); + else + jl_set_typeof(v, dt); char *data = (char*)jl_data_ptr(v); size_t i, np = dt->layout->npointers; char *start = data; @@ -858,7 +861,7 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t JL_TIMING(AST_UNCOMPRESS, AST_UNCOMPRESS); JL_LOCK(&m->writelock); // protect the roots array (Might GC) assert(jl_is_method(m)); - assert(jl_typeis(data, jl_array_uint8_type)); + assert(jl_typetagis(data, jl_array_uint8_type)); size_t i; ios_t src; ios_mem(&src, 0); @@ -940,7 +943,7 @@ JL_DLLEXPORT uint8_t jl_ir_flag_inferred(jl_array_t *data) { if (jl_is_code_info(data)) return ((jl_code_info_t*)data)->inferred; - assert(jl_typeis(data, jl_array_uint8_type)); + assert(jl_typetagis(data, jl_array_uint8_type)); jl_code_info_flags_t flags; flags.packed = ((uint8_t*)data->data)[0]; return flags.bits.inferred; @@ -950,7 +953,7 @@ JL_DLLEXPORT uint8_t jl_ir_flag_inlining(jl_array_t *data) { if (jl_is_code_info(data)) return ((jl_code_info_t*)data)->inlining; - assert(jl_typeis(data, jl_array_uint8_type)); + assert(jl_typetagis(data, jl_array_uint8_type)); jl_code_info_flags_t flags; flags.packed = ((uint8_t*)data->data)[0]; return flags.bits.inlining; @@ -960,7 +963,7 @@ JL_DLLEXPORT uint8_t jl_ir_flag_has_fcall(jl_array_t *data) { if (jl_is_code_info(data)) return ((jl_code_info_t*)data)->has_fcall; - assert(jl_typeis(data, jl_array_uint8_type)); + assert(jl_typetagis(data, jl_array_uint8_type)); jl_code_info_flags_t flags; flags.packed = ((uint8_t*)data->data)[0]; return flags.bits.has_fcall; @@ -970,7 +973,7 @@ JL_DLLEXPORT uint16_t jl_ir_inlining_cost(jl_array_t *data) { if (jl_is_code_info(data)) return ((jl_code_info_t*)data)->inlining_cost; - assert(jl_typeis(data, jl_array_uint8_type)); + assert(jl_typetagis(data, jl_array_uint8_type)); uint16_t res = jl_load_unaligned_i16((char*)data->data + 2); return res; } @@ -1008,7 +1011,7 @@ JL_DLLEXPORT ssize_t jl_ir_nslots(jl_array_t *data) return jl_array_len(func->slotnames); } else { - assert(jl_typeis(data, jl_array_uint8_type)); + assert(jl_typetagis(data, jl_array_uint8_type)); int nslots = jl_load_unaligned_i32((char*)data->data + 2 + sizeof(uint16_t)); return nslots; } @@ -1019,7 +1022,7 @@ JL_DLLEXPORT uint8_t jl_ir_slotflag(jl_array_t *data, size_t i) assert(i < jl_ir_nslots(data)); if (jl_is_code_info(data)) return ((uint8_t*)((jl_code_info_t*)data)->slotflags->data)[i]; - assert(jl_typeis(data, jl_array_uint8_type)); + assert(jl_typetagis(data, jl_array_uint8_type)); return ((uint8_t*)data->data)[2 + sizeof(uint16_t) + sizeof(int32_t) + i]; } diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index c09f2aff4cb88..cce87f53368f4 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -120,7 +120,7 @@ XX(jl_check_pkgimage_clones) \ XX(jl_egal) \ XX(jl_egal__bits) \ - XX(jl_egal__special) \ + XX(jl_egal__bitstag) \ XX(jl_eh_restore_state) \ XX(jl_enter_handler) \ XX(jl_enter_threaded_region) \ diff --git a/src/jltypes.c b/src/jltypes.c index 85255f9247439..0549ee2c07e53 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -20,6 +20,7 @@ extern "C" { #endif _Atomic(jl_value_t*) cmpswap_names JL_GLOBALLY_ROOTED; +jl_datatype_t *small_typeof[(jl_max_tags << 4) / sizeof(*small_typeof)]; // 16-bit aligned, like the GC // compute empirical max-probe for a given size #define max_probe(size) ((size) <= 1024 ? 16 : (size) >> 6) @@ -51,7 +52,7 @@ static int typeenv_has_ne(jl_typeenv_t *env, jl_tvar_t *v) JL_NOTSAFEPOINT static int layout_uses_free_typevars(jl_value_t *v, jl_typeenv_t *env) { while (1) { - if (jl_typeis(v, jl_tvar_type)) + if (jl_is_typevar(v)) return !typeenv_has(env, (jl_tvar_t*)v); while (jl_is_unionall(v)) { jl_unionall_t *ua = (jl_unionall_t*)v; @@ -106,7 +107,7 @@ static int layout_uses_free_typevars(jl_value_t *v, jl_typeenv_t *env) static int has_free_typevars(jl_value_t *v, jl_typeenv_t *env) JL_NOTSAFEPOINT { while (1) { - if (jl_typeis(v, jl_tvar_type)) { + if (jl_is_typevar(v)) { return !typeenv_has(env, (jl_tvar_t*)v); } while (jl_is_unionall(v)) { @@ -160,7 +161,7 @@ JL_DLLEXPORT int jl_has_free_typevars(jl_value_t *v) JL_NOTSAFEPOINT static void find_free_typevars(jl_value_t *v, jl_typeenv_t *env, jl_array_t *out) { while (1) { - if (jl_typeis(v, jl_tvar_type)) { + if (jl_is_typevar(v)) { if (!typeenv_has(env, (jl_tvar_t*)v)) jl_array_ptr_1d_push(out, v); return; @@ -217,7 +218,7 @@ JL_DLLEXPORT jl_array_t *jl_find_free_typevars(jl_value_t *v) static int jl_has_bound_typevars(jl_value_t *v, jl_typeenv_t *env) JL_NOTSAFEPOINT { while (1) { - if (jl_typeis(v, jl_tvar_type)) { + if (jl_is_typevar(v)) { return typeenv_has_ne(env, (jl_tvar_t*)v); } while (jl_is_unionall(v)) { @@ -2353,7 +2354,7 @@ jl_datatype_t *jl_wrap_Type(jl_value_t *t) return (jl_datatype_t*)jl_instantiate_unionall(jl_type_type, t); } -jl_vararg_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n) +JL_DLLEXPORT jl_vararg_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n) { if (n) { if (jl_is_typevar(n) || jl_is_uniontype(jl_unwrap_unionall(n))) { @@ -2380,6 +2381,7 @@ jl_vararg_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n) } jl_task_t *ct = jl_current_task; jl_vararg_t *vm = (jl_vararg_t *)jl_gc_alloc(ct->ptls, sizeof(jl_vararg_t), jl_vararg_type); + jl_set_typetagof(vm, jl_vararg_tag, 0); vm->T = t; vm->N = n; return vm; @@ -2469,19 +2471,36 @@ static jl_tvar_t *tvar(const char *name) (jl_value_t*)jl_any_type); } +void export_small_typeof(void) +{ + void *copy; +#ifdef _OS_WINDOWS_ + jl_dlsym(jl_libjulia_handle, "small_typeof", ©, 1); +#else + jl_dlsym(jl_libjulia_internal_handle, "small_typeof", ©, 1); +#endif + memcpy(copy, &small_typeof, sizeof(small_typeof)); +} + +#define XX(name) \ + small_typeof[(jl_##name##_tag << 4) / sizeof(*small_typeof)] = jl_##name##_type; \ + jl_##name##_type->smalltag = jl_##name##_tag; void jl_init_types(void) JL_GC_DISABLED { jl_module_t *core = NULL; // will need to be assigned later // create base objects jl_datatype_type = jl_new_uninitialized_datatype(); - jl_set_typeof(jl_datatype_type, jl_datatype_type); + XX(datatype); jl_typename_type = jl_new_uninitialized_datatype(); jl_symbol_type = jl_new_uninitialized_datatype(); + XX(symbol); jl_simplevector_type = jl_new_uninitialized_datatype(); + XX(simplevector); jl_methtable_type = jl_new_uninitialized_datatype(); jl_emptysvec = (jl_svec_t*)jl_gc_permobj(sizeof(void*), jl_simplevector_type); + jl_set_typetagof(jl_emptysvec, jl_simplevector_tag, GC_OLD_MARKED); jl_svec_set_len_unsafe(jl_emptysvec, 0); jl_any_type = (jl_datatype_t*)jl_new_abstracttype((jl_value_t*)jl_symbol("Any"), core, NULL, jl_emptysvec); @@ -2604,18 +2623,22 @@ void jl_init_types(void) JL_GC_DISABLED jl_perm_symsvec(3, "name", "lb", "ub"), jl_svec(3, jl_symbol_type, jl_any_type, jl_any_type), jl_emptysvec, 0, 1, 3); + XX(tvar); const static uint32_t tvar_constfields[1] = { 0x00000007 }; // all fields are constant, even though TypeVar itself has identity jl_tvar_type->name->constfields = tvar_constfields; jl_typeofbottom_type = jl_new_datatype(jl_symbol("TypeofBottom"), core, type_type, jl_emptysvec, - jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 0, 0); - jl_bottom_type = jl_new_struct(jl_typeofbottom_type); + jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 0, 0); + XX(typeofbottom); + jl_bottom_type = jl_gc_permobj(0, jl_typeofbottom_type); + jl_set_typetagof(jl_bottom_type, jl_typeofbottom_tag, GC_OLD_MARKED); jl_typeofbottom_type->instance = jl_bottom_type; jl_unionall_type = jl_new_datatype(jl_symbol("UnionAll"), core, type_type, jl_emptysvec, jl_perm_symsvec(2, "var", "body"), jl_svec(2, jl_tvar_type, jl_any_type), jl_emptysvec, 0, 0, 2); + XX(unionall); // It seems like we probably usually end up needing the box for kinds (often used in an Any context), so force it to exist jl_unionall_type->name->mayinlinealloc = 0; @@ -2623,6 +2646,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_perm_symsvec(2, "a", "b"), jl_svec(2, jl_any_type, jl_any_type), jl_emptysvec, 0, 0, 2); + XX(uniontype); // It seems like we probably usually end up needing the box for kinds (often used in an Any context), so force it to exist jl_uniontype_type->name->mayinlinealloc = 0; @@ -2638,6 +2662,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_perm_symsvec(2, "T", "N"), jl_svec(2, jl_any_type, jl_any_type), jl_emptysvec, 0, 0, 0); + XX(vararg); // It seems like we probably usually end up needing the box for kinds (often used in an Any context), so force it to exist jl_vararg_type->name->mayinlinealloc = 0; @@ -2659,16 +2684,22 @@ void jl_init_types(void) JL_GC_DISABLED // non-primitive definitions follow jl_int32_type = jl_new_primitivetype((jl_value_t*)jl_symbol("Int32"), core, jl_any_type, jl_emptysvec, 32); + XX(int32); jl_int64_type = jl_new_primitivetype((jl_value_t*)jl_symbol("Int64"), core, jl_any_type, jl_emptysvec, 64); + XX(int64); jl_uint32_type = jl_new_primitivetype((jl_value_t*)jl_symbol("UInt32"), core, jl_any_type, jl_emptysvec, 32); + XX(uint32); jl_uint64_type = jl_new_primitivetype((jl_value_t*)jl_symbol("UInt64"), core, jl_any_type, jl_emptysvec, 64); + XX(uint64); jl_uint8_type = jl_new_primitivetype((jl_value_t*)jl_symbol("UInt8"), core, jl_any_type, jl_emptysvec, 8); + XX(uint8); jl_uint16_type = jl_new_primitivetype((jl_value_t*)jl_symbol("UInt16"), core, jl_any_type, jl_emptysvec, 16); + XX(uint16); jl_ssavalue_type = jl_new_datatype(jl_symbol("SSAValue"), core, jl_any_type, jl_emptysvec, jl_perm_symsvec(1, "id"), @@ -2690,12 +2721,14 @@ void jl_init_types(void) JL_GC_DISABLED jl_bool_type = NULL; jl_bool_type = jl_new_primitivetype((jl_value_t*)jl_symbol("Bool"), core, jl_any_type, jl_emptysvec, 8); - jl_false = jl_permbox8(jl_bool_type, 0); - jl_true = jl_permbox8(jl_bool_type, 1); + XX(bool); + jl_false = jl_permbox8(jl_bool_type, jl_bool_tag, 0); + jl_true = jl_permbox8(jl_bool_type, jl_bool_tag, 1); jl_abstractstring_type = jl_new_abstracttype((jl_value_t*)jl_symbol("AbstractString"), core, jl_any_type, jl_emptysvec); jl_string_type = jl_new_datatype(jl_symbol("String"), core, jl_abstractstring_type, jl_emptysvec, jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 1, 0); + XX(string); jl_string_type->instance = NULL; jl_compute_field_offsets(jl_string_type); jl_an_empty_string = jl_pchar_to_string("\0", 1); @@ -2796,6 +2829,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_module_type = jl_new_datatype(jl_symbol("Module"), core, jl_any_type, jl_emptysvec, jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 1, 0); + XX(module); jl_module_type->instance = NULL; jl_compute_field_offsets(jl_module_type); @@ -3166,11 +3200,9 @@ void jl_init_types(void) JL_GC_DISABLED jl_uint16_type), jl_emptysvec, 0, 1, 6); + XX(task); jl_value_t *listt = jl_new_struct(jl_uniontype_type, jl_task_type, jl_nothing_type); jl_svecset(jl_task_type->types, 0, listt); - jl_astaggedvalue(jl_current_task)->header = (uintptr_t)jl_task_type | jl_astaggedvalue(jl_current_task)->header; - - jl_value_t *pointer_void = jl_apply_type1((jl_value_t*)jl_pointer_type, (jl_value_t*)jl_nothing_type); jl_binding_type = jl_new_datatype(jl_symbol("Binding"), core, jl_any_type, jl_emptysvec, @@ -3188,6 +3220,8 @@ void jl_init_types(void) JL_GC_DISABLED jl_svec(3, jl_module_type, jl_symbol_type, jl_binding_type), jl_emptysvec, 0, 0, 3); + jl_value_t *pointer_void = jl_apply_type1((jl_value_t*)jl_pointer_type, (jl_value_t*)jl_nothing_type); + jl_voidpointer_type = (jl_datatype_t*)pointer_void; tv = jl_svec2(tvar("A"), tvar("R")); jl_opaque_closure_type = (jl_unionall_t*)jl_new_datatype(jl_symbol("OpaqueClosure"), core, jl_function_type, tv, // N.B.: OpaqueClosure call code relies on specptr being field 5. @@ -3204,7 +3238,6 @@ void jl_init_types(void) JL_GC_DISABLED jl_emptysvec, 0, 0, 4); // complete builtin type metadata - jl_voidpointer_type = (jl_datatype_t*)pointer_void; jl_uint8pointer_type = (jl_datatype_t*)jl_apply_type1((jl_value_t*)jl_pointer_type, (jl_value_t*)jl_uint8_type); jl_svecset(jl_datatype_type->types, 5, jl_voidpointer_type); jl_svecset(jl_datatype_type->types, 6, jl_int32_type); @@ -3266,7 +3299,90 @@ void jl_init_types(void) JL_GC_DISABLED // override the preferred layout for a couple types jl_lineinfonode_type->name->mayinlinealloc = 0; // FIXME: assumed to be a pointer by codegen + export_small_typeof(); +} + +static jl_value_t *core(const char *name) +{ + return jl_get_global(jl_core_module, jl_symbol(name)); +} + +// fetch references to things defined in boot.jl +void post_boot_hooks(void) +{ + jl_char_type = (jl_datatype_t*)core("Char"); + XX(char); + jl_int8_type = (jl_datatype_t*)core("Int8"); + XX(int8); + jl_int16_type = (jl_datatype_t*)core("Int16"); + XX(int16); + jl_float16_type = (jl_datatype_t*)core("Float16"); + //XX(float16); + jl_float32_type = (jl_datatype_t*)core("Float32"); + //XX(float32); + jl_float64_type = (jl_datatype_t*)core("Float64"); + //XX(float64); + jl_floatingpoint_type = (jl_datatype_t*)core("AbstractFloat"); + jl_number_type = (jl_datatype_t*)core("Number"); + jl_signed_type = (jl_datatype_t*)core("Signed"); + jl_datatype_t *jl_unsigned_type = (jl_datatype_t*)core("Unsigned"); + jl_datatype_t *jl_integer_type = (jl_datatype_t*)core("Integer"); + + jl_bool_type->super = jl_integer_type; + jl_uint8_type->super = jl_unsigned_type; + jl_uint16_type->super = jl_unsigned_type; + jl_uint32_type->super = jl_unsigned_type; + jl_uint64_type->super = jl_unsigned_type; + jl_int32_type->super = jl_signed_type; + jl_int64_type->super = jl_signed_type; + + jl_errorexception_type = (jl_datatype_t*)core("ErrorException"); + jl_stackovf_exception = jl_new_struct_uninit((jl_datatype_t*)core("StackOverflowError")); + jl_diverror_exception = jl_new_struct_uninit((jl_datatype_t*)core("DivideError")); + jl_undefref_exception = jl_new_struct_uninit((jl_datatype_t*)core("UndefRefError")); + jl_undefvarerror_type = (jl_datatype_t*)core("UndefVarError"); + jl_atomicerror_type = (jl_datatype_t*)core("ConcurrencyViolationError"); + jl_interrupt_exception = jl_new_struct_uninit((jl_datatype_t*)core("InterruptException")); + jl_boundserror_type = (jl_datatype_t*)core("BoundsError"); + jl_memory_exception = jl_new_struct_uninit((jl_datatype_t*)core("OutOfMemoryError")); + jl_readonlymemory_exception = jl_new_struct_uninit((jl_datatype_t*)core("ReadOnlyMemoryError")); + jl_typeerror_type = (jl_datatype_t*)core("TypeError"); + jl_argumenterror_type = (jl_datatype_t*)core("ArgumentError"); + jl_methoderror_type = (jl_datatype_t*)core("MethodError"); + jl_loaderror_type = (jl_datatype_t*)core("LoadError"); + jl_initerror_type = (jl_datatype_t*)core("InitError"); + jl_pair_type = core("Pair"); + jl_kwcall_func = core("kwcall"); + jl_kwcall_mt = ((jl_datatype_t*)jl_typeof(jl_kwcall_func))->name->mt; + jl_atomic_store_relaxed(&jl_kwcall_mt->max_args, 0); + + jl_weakref_type = (jl_datatype_t*)core("WeakRef"); + jl_vecelement_typename = ((jl_datatype_t*)jl_unwrap_unionall(core("VecElement")))->name; + + jl_init_box_caches(); + + // set module field of primitive types + jl_svec_t *bindings = jl_atomic_load_relaxed(&jl_core_module->bindings); + jl_value_t **table = jl_svec_data(bindings); + for (size_t i = 0; i < jl_svec_len(bindings); i++) { + if (table[i] != jl_nothing) { + jl_binding_t *b = (jl_binding_t*)table[i]; + jl_value_t *v = jl_atomic_load_relaxed(&b->value); + if (v) { + if (jl_is_unionall(v)) + v = jl_unwrap_unionall(v); + if (jl_is_datatype(v)) { + jl_datatype_t *tt = (jl_datatype_t*)v; + tt->name->module = jl_core_module; + if (tt->name->mt) + tt->name->mt->module = jl_core_module; + } + } + } + } + export_small_typeof(); } +#undef XX #ifdef __cplusplus } diff --git a/src/julia.expmap b/src/julia.expmap index 7df813498182b..94b955e95981f 100644 --- a/src/julia.expmap +++ b/src/julia.expmap @@ -7,6 +7,7 @@ ios_*; arraylist_grow; small_arraylist_grow; + small_typeof; jl_*; ijl_*; _jl_mutex_*; @@ -18,10 +19,7 @@ memhash32; memhash32_seed; memhash_seed; - restore_arg_area_loc; restore_signals; - rl_clear_input; - save_arg_area_loc; u8_*; uv_*; add_library_mapping; diff --git a/src/julia.h b/src/julia.h index 89fea54fc428f..6ecb845f77afe 100644 --- a/src/julia.h +++ b/src/julia.h @@ -92,10 +92,11 @@ typedef struct _jl_value_t jl_value_t; struct _jl_taggedvalue_bits { uintptr_t gc:2; uintptr_t in_image:1; + uintptr_t unused:1; #ifdef _P64 - uintptr_t padding:61; + uintptr_t tag:60; #else - uintptr_t padding:29; + uintptr_t tag:28; #endif }; @@ -109,6 +110,7 @@ JL_EXTENSION struct _jl_taggedvalue_t { // jl_value_t value; }; +static inline jl_value_t *jl_to_typeof(uintptr_t t) JL_GLOBALLY_ROOTED JL_NOTSAFEPOINT; #ifdef __clang_gcanalyzer__ JL_DLLEXPORT jl_taggedvalue_t *_jl_astaggedvalue(jl_value_t *v JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; #define jl_astaggedvalue(v) _jl_astaggedvalue((jl_value_t*)(v)) @@ -119,10 +121,10 @@ JL_DLLEXPORT jl_value_t *_jl_typeof(jl_value_t *v JL_PROPAGATES_ROOT) JL_NOTSAFE #else #define jl_astaggedvalue(v) \ ((jl_taggedvalue_t*)((char*)(v) - sizeof(jl_taggedvalue_t))) -#define jl_valueof(v) \ +#define jl_valueof(v) \ ((jl_value_t*)((char*)(v) + sizeof(jl_taggedvalue_t))) #define jl_typeof(v) \ - ((jl_value_t*)(jl_astaggedvalue(v)->header & ~(uintptr_t)15)) + jl_to_typeof(jl_typetagof(v)) #endif static inline void jl_set_typeof(void *v, void *t) JL_NOTSAFEPOINT { @@ -130,7 +132,11 @@ static inline void jl_set_typeof(void *v, void *t) JL_NOTSAFEPOINT jl_taggedvalue_t *tag = jl_astaggedvalue(v); jl_atomic_store_relaxed((_Atomic(jl_value_t*)*)&tag->type, (jl_value_t*)t); } +#define jl_typetagof(v) \ + ((jl_astaggedvalue(v)->header) & ~(uintptr_t)15) #define jl_typeis(v,t) (jl_typeof(v)==(jl_value_t*)(t)) +#define jl_typetagis(v,t) (jl_typetagof(v)==(uintptr_t)(t)) +#define jl_set_typetagof(v,t,gc) (jl_set_typeof((v), (void*)(((uintptr_t)(t) << 4) | (gc)))) // Symbols are interned strings (hash-consed) stored as an invasive binary tree. // The string data is nul-terminated and hangs off the end of the struct. @@ -562,7 +568,7 @@ typedef struct _jl_datatype_t { uint16_t isprimitivetype:1; // whether this is declared with 'primitive type' keyword (sized, no fields, and immutable) uint16_t ismutationfree:1; // whether any mutable memory is reachable through this type (in the type or via fields) uint16_t isidentityfree:1; // whether this type or any object reachable through its fields has non-content-based identity - uint16_t padding:6; + uint16_t smalltag:6; // whether this type has a small-tag optimization } jl_datatype_t; typedef struct _jl_vararg_t { @@ -694,6 +700,59 @@ typedef struct { // constants and type objects ------------------------------------------------- +#define JL_SMALL_TYPEOF(XX) \ + /* kinds */ \ + XX(typeofbottom) \ + XX(datatype) \ + XX(unionall) \ + XX(uniontype) \ + /* type parameter objects */ \ + XX(vararg) \ + XX(tvar) \ + XX(symbol) \ + XX(module) \ + /* special GC objects */ \ + XX(simplevector) \ + XX(string) \ + XX(task) \ + /* bits types with special allocators */ \ + XX(bool) \ + XX(char) \ + /*XX(float16)*/ \ + /*XX(float32)*/ \ + /*XX(float64)*/ \ + XX(int16) \ + XX(int32) \ + XX(int64) \ + XX(int8) \ + XX(uint16) \ + XX(uint32) \ + XX(uint64) \ + XX(uint8) \ + /* AST objects */ \ + /* XX(argument) */ \ + /* XX(newvarnode) */ \ + /* XX(slotnumber) */ \ + /* XX(ssavalue) */ \ + /* end of JL_SMALL_TYPEOF */ +enum jlsmall_typeof_tags { + jl_null_tag = 0, +#define XX(name) jl_##name##_tag, + JL_SMALL_TYPEOF(XX) +#undef XX + jl_tags_count, + jl_bitstags_first = jl_char_tag, // n.b. bool is not considered a bitstype, since it can be compared by pointer + jl_max_tags = 64 +}; +extern jl_datatype_t *small_typeof[(jl_max_tags << 4) / sizeof(jl_datatype_t*)]; +static inline jl_value_t *jl_to_typeof(uintptr_t t) +{ + if (t < (jl_max_tags << 4)) + return (jl_value_t*)small_typeof[t / sizeof(*small_typeof)]; + return (jl_value_t*)t; +} + + // kinds extern JL_DLLIMPORT jl_datatype_t *jl_typeofbottom_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_datatype_type JL_GLOBALLY_ROOTED; @@ -989,7 +1048,7 @@ STATIC_INLINE jl_value_t *jl_svecset( #else STATIC_INLINE jl_value_t *jl_svecref(void *t JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT { - assert(jl_typeis(t,jl_simplevector_type)); + assert(jl_typetagis(t,jl_simplevector_tag << 4)); assert(i < jl_svec_len(t)); // while svec is supposedly immutable, in practice we sometimes publish it first // and set the values lazily @@ -999,7 +1058,7 @@ STATIC_INLINE jl_value_t *jl_svecset( void *t JL_ROOTING_ARGUMENT JL_PROPAGATES_ROOT, size_t i, void *x JL_ROOTED_ARGUMENT) JL_NOTSAFEPOINT { - assert(jl_typeis(t,jl_simplevector_type)); + assert(jl_typetagis(t,jl_simplevector_tag << 4)); assert(i < jl_svec_len(t)); // while svec is supposedly immutable, in practice we sometimes publish it // first and set the values lazily. Those users occasionally might need to @@ -1055,13 +1114,13 @@ STATIC_INLINE jl_value_t *jl_array_ptr_set( STATIC_INLINE uint8_t jl_array_uint8_ref(void *a, size_t i) JL_NOTSAFEPOINT { assert(i < jl_array_len(a)); - assert(jl_typeis(a, jl_array_uint8_type)); + assert(jl_typetagis(a, jl_array_uint8_type)); return ((uint8_t*)(jl_array_data(a)))[i]; } STATIC_INLINE void jl_array_uint8_set(void *a, size_t i, uint8_t x) JL_NOTSAFEPOINT { assert(i < jl_array_len(a)); - assert(jl_typeis(a, jl_array_uint8_type)); + assert(jl_typetagis(a, jl_array_uint8_type)); ((uint8_t*)(jl_array_data(a)))[i] = x; } @@ -1230,56 +1289,57 @@ static inline int jl_is_layout_opaque(const jl_datatype_layout_t *l) JL_NOTSAFEP #define jl_is_nothing(v) (((jl_value_t*)(v)) == ((jl_value_t*)jl_nothing)) #define jl_is_tuple(v) (((jl_datatype_t*)jl_typeof(v))->name == jl_tuple_typename) #define jl_is_namedtuple(v) (((jl_datatype_t*)jl_typeof(v))->name == jl_namedtuple_typename) -#define jl_is_svec(v) jl_typeis(v,jl_simplevector_type) +#define jl_is_svec(v) jl_typetagis(v,jl_simplevector_tag<<4) #define jl_is_simplevector(v) jl_is_svec(v) -#define jl_is_datatype(v) jl_typeis(v,jl_datatype_type) +#define jl_is_datatype(v) jl_typetagis(v,jl_datatype_tag<<4) #define jl_is_mutable(t) (((jl_datatype_t*)t)->name->mutabl) #define jl_is_mutable_datatype(t) (jl_is_datatype(t) && (((jl_datatype_t*)t)->name->mutabl)) #define jl_is_immutable(t) (!((jl_datatype_t*)t)->name->mutabl) #define jl_is_immutable_datatype(t) (jl_is_datatype(t) && (!((jl_datatype_t*)t)->name->mutabl)) -#define jl_is_uniontype(v) jl_typeis(v,jl_uniontype_type) -#define jl_is_typevar(v) jl_typeis(v,jl_tvar_type) -#define jl_is_unionall(v) jl_typeis(v,jl_unionall_type) -#define jl_is_typename(v) jl_typeis(v,jl_typename_type) -#define jl_is_int8(v) jl_typeis(v,jl_int8_type) -#define jl_is_int16(v) jl_typeis(v,jl_int16_type) -#define jl_is_int32(v) jl_typeis(v,jl_int32_type) -#define jl_is_int64(v) jl_typeis(v,jl_int64_type) -#define jl_is_uint8(v) jl_typeis(v,jl_uint8_type) -#define jl_is_uint16(v) jl_typeis(v,jl_uint16_type) -#define jl_is_uint32(v) jl_typeis(v,jl_uint32_type) -#define jl_is_uint64(v) jl_typeis(v,jl_uint64_type) -#define jl_is_bool(v) jl_typeis(v,jl_bool_type) -#define jl_is_symbol(v) jl_typeis(v,jl_symbol_type) -#define jl_is_ssavalue(v) jl_typeis(v,jl_ssavalue_type) -#define jl_is_slotnumber(v) jl_typeis(v,jl_slotnumber_type) -#define jl_is_expr(v) jl_typeis(v,jl_expr_type) -#define jl_is_binding(v) jl_typeis(v,jl_binding_type) -#define jl_is_globalref(v) jl_typeis(v,jl_globalref_type) -#define jl_is_gotonode(v) jl_typeis(v,jl_gotonode_type) -#define jl_is_gotoifnot(v) jl_typeis(v,jl_gotoifnot_type) -#define jl_is_returnnode(v) jl_typeis(v,jl_returnnode_type) -#define jl_is_argument(v) jl_typeis(v,jl_argument_type) -#define jl_is_pinode(v) jl_typeis(v,jl_pinode_type) -#define jl_is_phinode(v) jl_typeis(v,jl_phinode_type) -#define jl_is_phicnode(v) jl_typeis(v,jl_phicnode_type) -#define jl_is_upsilonnode(v) jl_typeis(v,jl_upsilonnode_type) -#define jl_is_quotenode(v) jl_typeis(v,jl_quotenode_type) -#define jl_is_newvarnode(v) jl_typeis(v,jl_newvarnode_type) -#define jl_is_linenode(v) jl_typeis(v,jl_linenumbernode_type) -#define jl_is_method_instance(v) jl_typeis(v,jl_method_instance_type) -#define jl_is_code_instance(v) jl_typeis(v,jl_code_instance_type) -#define jl_is_code_info(v) jl_typeis(v,jl_code_info_type) -#define jl_is_method(v) jl_typeis(v,jl_method_type) -#define jl_is_module(v) jl_typeis(v,jl_module_type) -#define jl_is_mtable(v) jl_typeis(v,jl_methtable_type) -#define jl_is_task(v) jl_typeis(v,jl_task_type) -#define jl_is_string(v) jl_typeis(v,jl_string_type) +#define jl_is_uniontype(v) jl_typetagis(v,jl_uniontype_tag<<4) +#define jl_is_typevar(v) jl_typetagis(v,jl_tvar_tag<<4) +#define jl_is_unionall(v) jl_typetagis(v,jl_unionall_tag<<4) +#define jl_is_vararg(v) jl_typetagis(v,jl_vararg_tag<<4) +#define jl_is_typename(v) jl_typetagis(v,jl_typename_type) +#define jl_is_int8(v) jl_typetagis(v,jl_int8_tag<<4) +#define jl_is_int16(v) jl_typetagis(v,jl_int16_tag<<4) +#define jl_is_int32(v) jl_typetagis(v,jl_int32_tag<<4) +#define jl_is_int64(v) jl_typetagis(v,jl_int64_tag<<4) +#define jl_is_uint8(v) jl_typetagis(v,jl_uint8_tag<<4) +#define jl_is_uint16(v) jl_typetagis(v,jl_uint16_tag<<4) +#define jl_is_uint32(v) jl_typetagis(v,jl_uint32_tag<<4) +#define jl_is_uint64(v) jl_typetagis(v,jl_uint64_tag<<4) +#define jl_is_bool(v) jl_typetagis(v,jl_bool_tag<<4) +#define jl_is_symbol(v) jl_typetagis(v,jl_symbol_tag<<4) +#define jl_is_ssavalue(v) jl_typetagis(v,jl_ssavalue_type) +#define jl_is_slotnumber(v) jl_typetagis(v,jl_slotnumber_type) +#define jl_is_expr(v) jl_typetagis(v,jl_expr_type) +#define jl_is_binding(v) jl_typetagis(v,jl_binding_type) +#define jl_is_globalref(v) jl_typetagis(v,jl_globalref_type) +#define jl_is_gotonode(v) jl_typetagis(v,jl_gotonode_type) +#define jl_is_gotoifnot(v) jl_typetagis(v,jl_gotoifnot_type) +#define jl_is_returnnode(v) jl_typetagis(v,jl_returnnode_type) +#define jl_is_argument(v) jl_typetagis(v,jl_argument_type) +#define jl_is_pinode(v) jl_typetagis(v,jl_pinode_type) +#define jl_is_phinode(v) jl_typetagis(v,jl_phinode_type) +#define jl_is_phicnode(v) jl_typetagis(v,jl_phicnode_type) +#define jl_is_upsilonnode(v) jl_typetagis(v,jl_upsilonnode_type) +#define jl_is_quotenode(v) jl_typetagis(v,jl_quotenode_type) +#define jl_is_newvarnode(v) jl_typetagis(v,jl_newvarnode_type) +#define jl_is_linenode(v) jl_typetagis(v,jl_linenumbernode_type) +#define jl_is_method_instance(v) jl_typetagis(v,jl_method_instance_type) +#define jl_is_code_instance(v) jl_typetagis(v,jl_code_instance_type) +#define jl_is_code_info(v) jl_typetagis(v,jl_code_info_type) +#define jl_is_method(v) jl_typetagis(v,jl_method_type) +#define jl_is_module(v) jl_typetagis(v,jl_module_tag<<4) +#define jl_is_mtable(v) jl_typetagis(v,jl_methtable_type) +#define jl_is_task(v) jl_typetagis(v,jl_task_tag<<4) +#define jl_is_string(v) jl_typetagis(v,jl_string_tag<<4) #define jl_is_cpointer(v) jl_is_cpointer_type(jl_typeof(v)) #define jl_is_pointer(v) jl_is_cpointer_type(jl_typeof(v)) -#define jl_is_uint8pointer(v)jl_typeis(v,jl_uint8pointer_type) +#define jl_is_uint8pointer(v)jl_typetagis(v,jl_uint8pointer_type) #define jl_is_llvmpointer(v) (((jl_datatype_t*)jl_typeof(v))->name == jl_llvmpointer_typename) -#define jl_is_intrinsic(v) jl_typeis(v,jl_intrinsic_type) +#define jl_is_intrinsic(v) jl_typetagis(v,jl_intrinsic_type) #define jl_array_isbitsunion(a) (!(((jl_array_t*)(a))->flags.ptrarray) && jl_is_uniontype(jl_tparam0(jl_typeof(a)))) JL_DLLEXPORT int jl_subtype(jl_value_t *a, jl_value_t *b); @@ -1290,9 +1350,16 @@ STATIC_INLINE int jl_is_kind(jl_value_t *v) JL_NOTSAFEPOINT v==(jl_value_t*)jl_unionall_type || v==(jl_value_t*)jl_typeofbottom_type); } +STATIC_INLINE int jl_is_kindtag(uintptr_t t) JL_NOTSAFEPOINT +{ + t >>= 4; + return (t==(uintptr_t)jl_uniontype_tag || t==(uintptr_t)jl_datatype_tag || + t==(uintptr_t)jl_unionall_tag || t==(uintptr_t)jl_typeofbottom_tag); +} + STATIC_INLINE int jl_is_type(jl_value_t *v) JL_NOTSAFEPOINT { - return jl_is_kind(jl_typeof(v)); + return jl_is_kindtag(jl_typetagof(v)); } STATIC_INLINE int jl_is_primitivetype(void *v) JL_NOTSAFEPOINT @@ -1400,29 +1467,30 @@ STATIC_INLINE int jl_is_array_zeroinit(jl_array_t *a) JL_NOTSAFEPOINT // object identity JL_DLLEXPORT int jl_egal(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED) JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_egal__bits(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, jl_datatype_t *dt) JL_NOTSAFEPOINT; -JL_DLLEXPORT int jl_egal__special(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, jl_datatype_t *dt) JL_NOTSAFEPOINT; -JL_DLLEXPORT int jl_egal__unboxed(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, jl_datatype_t *dt) JL_NOTSAFEPOINT; +JL_DLLEXPORT int jl_egal__bitstag(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, uintptr_t dtag) JL_NOTSAFEPOINT; +JL_DLLEXPORT int jl_egal__unboxed(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, uintptr_t dtag) JL_NOTSAFEPOINT; JL_DLLEXPORT uintptr_t jl_object_id(jl_value_t *v) JL_NOTSAFEPOINT; JL_DLLEXPORT uintptr_t jl_type_hash(jl_value_t *v) JL_NOTSAFEPOINT; -STATIC_INLINE int jl_egal__unboxed_(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, jl_datatype_t *dt) JL_NOTSAFEPOINT +STATIC_INLINE int jl_egal__unboxed_(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, uintptr_t dtag) JL_NOTSAFEPOINT { - if (dt->name->mutabl) { - if (dt == jl_simplevector_type || dt == jl_string_type || dt == jl_datatype_type) - return jl_egal__special(a, b, dt); - return 0; + if (dtag < jl_max_tags << 4) { + if (dtag == jl_symbol_tag << 4 || dtag == jl_bool_tag << 4) + return 0; } - return jl_egal__bits(a, b, dt); + else if (((jl_datatype_t*)dtag)->name->mutabl) + return 0; + return jl_egal__bitstag(a, b, dtag); } STATIC_INLINE int jl_egal_(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED) JL_NOTSAFEPOINT { if (a == b) return 1; - jl_datatype_t *dt = (jl_datatype_t*)jl_typeof(a); - if (dt != (jl_datatype_t*)jl_typeof(b)) + uintptr_t dtag = jl_typetagof(a); + if (dtag != jl_typetagof(b)) return 0; - return jl_egal__unboxed_(a, b, dt); + return jl_egal__unboxed_(a, b, dtag); } #define jl_egal(a, b) jl_egal_((a), (b)) diff --git a/src/julia_internal.h b/src/julia_internal.h index c6c9f3f8e21df..1ce04cccf020f 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -512,10 +512,8 @@ STATIC_INLINE jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT o->header = tag | GC_OLD_MARKED; return jl_valueof(o); } -jl_value_t *jl_permbox8(jl_datatype_t *t, int8_t x); -jl_value_t *jl_permbox16(jl_datatype_t *t, int16_t x); -jl_value_t *jl_permbox32(jl_datatype_t *t, int32_t x); -jl_value_t *jl_permbox64(jl_datatype_t *t, int64_t x); +jl_value_t *jl_permbox8(jl_datatype_t *t, uintptr_t tag, uint8_t x); +jl_value_t *jl_permbox32(jl_datatype_t *t, uintptr_t tag, uint32_t x); jl_svec_t *jl_perm_symsvec(size_t n, ...); // this sizeof(__VA_ARGS__) trick can't be computed until C11, but that only matters to Clang in some situations @@ -798,11 +796,6 @@ typedef enum { JL_VARARG_UNBOUND = 3 } jl_vararg_kind_t; -STATIC_INLINE int jl_is_vararg(jl_value_t *v) JL_NOTSAFEPOINT -{ - return jl_typeof(v) == (jl_value_t*)jl_vararg_type; -} - STATIC_INLINE jl_value_t *jl_unwrap_vararg(jl_vararg_t *v JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT { assert(jl_is_vararg((jl_value_t*)v)); diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index a8bab71ce91b5..a836ff1361768 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -2224,10 +2224,10 @@ Value *LateLowerGCFrame::EmitLoadTag(IRBuilder<> &builder, Type *T_size, Value * load->setMetadata(LLVMContext::MD_tbaa, tbaa_tag); MDBuilder MDB(load->getContext()); auto *NullInt = ConstantInt::get(T_size, 0); - // We can be sure that the tag is larger than page size. + // We can be sure that the tag is at least 16 (1<<4) // Hopefully this is enough to convince LLVM that the value is still not NULL // after masking off the tag bits - auto *NonNullInt = ConstantExpr::getAdd(NullInt, ConstantInt::get(T_size, 4096)); + auto *NonNullInt = ConstantExpr::getAdd(NullInt, ConstantInt::get(T_size, 16)); load->setMetadata(LLVMContext::MD_range, MDB.createRange(NonNullInt, NullInt)); return load; } diff --git a/src/method.c b/src/method.c index bed94319953c0..1b6795902d837 100644 --- a/src/method.c +++ b/src/method.c @@ -130,7 +130,7 @@ static jl_value_t *resolve_globals(jl_value_t *expr, jl_module_t *module, jl_sve rt = jl_interpret_toplevel_expr_in(module, rt, NULL, sparam_vals); } JL_CATCH { - if (jl_typeis(jl_current_exception(), jl_errorexception_type)) + if (jl_typetagis(jl_current_exception(), jl_errorexception_type)) jl_error("could not evaluate cfunction return type (it might depend on a local variable)"); else jl_rethrow(); @@ -142,7 +142,7 @@ static jl_value_t *resolve_globals(jl_value_t *expr, jl_module_t *module, jl_sve at = jl_interpret_toplevel_expr_in(module, at, NULL, sparam_vals); } JL_CATCH { - if (jl_typeis(jl_current_exception(), jl_errorexception_type)) + if (jl_typetagis(jl_current_exception(), jl_errorexception_type)) jl_error("could not evaluate cfunction argument type (it might depend on a local variable)"); else jl_rethrow(); @@ -163,7 +163,7 @@ static jl_value_t *resolve_globals(jl_value_t *expr, jl_module_t *module, jl_sve rt = jl_interpret_toplevel_expr_in(module, rt, NULL, sparam_vals); } JL_CATCH { - if (jl_typeis(jl_current_exception(), jl_errorexception_type)) + if (jl_typetagis(jl_current_exception(), jl_errorexception_type)) jl_error("could not evaluate ccall return type (it might depend on a local variable)"); else jl_rethrow(); @@ -175,7 +175,7 @@ static jl_value_t *resolve_globals(jl_value_t *expr, jl_module_t *module, jl_sve at = jl_interpret_toplevel_expr_in(module, at, NULL, sparam_vals); } JL_CATCH { - if (jl_typeis(jl_current_exception(), jl_errorexception_type)) + if (jl_typetagis(jl_current_exception(), jl_errorexception_type)) jl_error("could not evaluate ccall argument type (it might depend on a local variable)"); else jl_rethrow(); @@ -504,7 +504,7 @@ void jl_add_function_to_lineinfo(jl_code_info_t *ci, jl_value_t *func) JL_GC_PUSH3(&rt, &lno, &inl); for (i = 0; i < n; i++) { jl_value_t *ln = jl_array_ptr_ref(li, i); - assert(jl_typeis(ln, jl_lineinfonode_type)); + assert(jl_typetagis(ln, jl_lineinfonode_type)); jl_value_t *mod = jl_fieldref_noalloc(ln, 0); jl_value_t *file = jl_fieldref_noalloc(ln, 2); lno = jl_fieldref(ln, 3); @@ -689,7 +689,7 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) jl_array_t *copy = NULL; jl_svec_t *sparam_vars = jl_outer_unionall_vars(m->sig); JL_GC_PUSH3(©, &sparam_vars, &src); - assert(jl_typeis(src->code, jl_array_any_type)); + assert(jl_typetagis(src->code, jl_array_any_type)); jl_array_t *stmts = (jl_array_t*)src->code; size_t i, n = jl_array_len(stmts); copy = jl_alloc_vec_any(n); diff --git a/src/module.c b/src/module.c index d504cf7738767..04d3970f9b460 100644 --- a/src/module.c +++ b/src/module.c @@ -17,6 +17,7 @@ JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, ui const jl_uuid_t uuid_zero = {0, 0}; jl_module_t *m = (jl_module_t*)jl_gc_alloc(ct->ptls, sizeof(jl_module_t), jl_module_type); + jl_set_typetagof(m, jl_module_tag, 0); assert(jl_is_symbol(name)); m->name = name; m->parent = parent; diff --git a/src/partr.c b/src/partr.c index bed02cf80c219..403f911b1284f 100644 --- a/src/partr.c +++ b/src/partr.c @@ -293,7 +293,7 @@ static jl_task_t *get_next_task(jl_value_t *trypoptask, jl_value_t *q) { jl_gc_safepoint(); jl_task_t *task = (jl_task_t*)jl_apply_generic(trypoptask, &q, 1); - if (jl_typeis(task, jl_task_type)) { + if (jl_is_task(task)) { int self = jl_atomic_load_relaxed(&jl_current_task->tid); jl_set_task_tid(task, self); return task; diff --git a/src/processor.cpp b/src/processor.cpp index 88fcb813d8248..24a434af91ad3 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -812,6 +812,8 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) *tls_offset_idx = (uintptr_t)(jl_tls_offset == -1 ? 0 : jl_tls_offset); } + res.small_typeof = pointers->small_typeof; + return res; } diff --git a/src/processor.h b/src/processor.h index d2280068fb67d..3e83bbb2247d6 100644 --- a/src/processor.h +++ b/src/processor.h @@ -88,6 +88,7 @@ typedef struct { const int32_t *gvars_offsets; uint32_t ngvars; jl_image_fptrs_t fptrs; + void **small_typeof; } jl_image_t; // The header for each image @@ -194,8 +195,10 @@ typedef struct { const jl_image_header_t *header; // The shard table, contains per-shard data const jl_image_shard_t *shards; // points to header->nshards length array - // The TLS data + // The TLS data pointer const jl_image_ptls_t *ptls; + // A copy of small_typeof[] + void **small_typeof; // serialized target data // This contains the number of targets diff --git a/src/rtutils.c b/src/rtutils.c index a2ec34102f148..afea3ac2c2388 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -129,6 +129,8 @@ JL_DLLEXPORT void JL_NORETURN jl_type_error(const char *fname, JL_DLLEXPORT void JL_NORETURN jl_undefined_var_error(jl_sym_t *var) { + if (!jl_undefvarerror_type) + jl_errorf("UndefVarError(%s)", jl_symbol_name(var)); jl_throw(jl_new_struct(jl_undefvarerror_type, var)); } @@ -1215,10 +1217,10 @@ static size_t jl_static_show_next_(JL_STREAM *out, jl_value_t *v, jl_value_t *pr *newdepth = &this_item, *p = depth; while (p) { - if (jl_typeis(v, jl_typemap_entry_type) && newdepth == &this_item) { + if (jl_typetagis(v, jl_typemap_entry_type) && newdepth == &this_item) { jl_value_t *m = p->v; unsigned nid = 1; - while (m && jl_typeis(m, jl_typemap_entry_type)) { + while (m && jl_typetagis(m, jl_typemap_entry_type)) { if (m == v) { return jl_printf(out, "sig, depth, ctx) + @@ -1234,7 +1236,7 @@ static size_t jl_static_show_next_(JL_STREAM *out, jl_value_t *v, jl_value_t *pr jl_value_t *m2 = p->v; if (m2 == mnext) break; - while (m2 && jl_typeis(m2, jl_typemap_entry_type)) { + while (m2 && jl_typetagis(m2, jl_typemap_entry_type)) { jl_value_t *mnext2 = (jl_value_t*)jl_atomic_load_relaxed(&((jl_typemap_entry_t*)m2)->next); if (mnext2 == mnext) { if (m2 != m) diff --git a/src/simplevector.c b/src/simplevector.c index cb65646e00936..65217715ae55f 100644 --- a/src/simplevector.c +++ b/src/simplevector.c @@ -23,6 +23,7 @@ jl_svec_t *(jl_perm_symsvec)(size_t n, ...) { if (n == 0) return jl_emptysvec; jl_svec_t *jv = (jl_svec_t*)jl_gc_permobj((n + 1) * sizeof(void*), jl_simplevector_type); + jl_set_typetagof(jv, jl_simplevector_tag, jl_astaggedvalue(jv)->bits.gc); jl_svec_set_len_unsafe(jv, n); va_list args; va_start(args, n); @@ -37,6 +38,7 @@ JL_DLLEXPORT jl_svec_t *jl_svec1(void *a) jl_task_t *ct = jl_current_task; jl_svec_t *v = (jl_svec_t*)jl_gc_alloc(ct->ptls, sizeof(void*) * 2, jl_simplevector_type); + jl_set_typetagof(v, jl_simplevector_tag, 0); jl_svec_set_len_unsafe(v, 1); jl_svec_data(v)[0] = (jl_value_t*)a; return v; @@ -47,6 +49,7 @@ JL_DLLEXPORT jl_svec_t *jl_svec2(void *a, void *b) jl_task_t *ct = jl_current_task; jl_svec_t *v = (jl_svec_t*)jl_gc_alloc(ct->ptls, sizeof(void*) * 3, jl_simplevector_type); + jl_set_typetagof(v, jl_simplevector_tag, 0); jl_svec_set_len_unsafe(v, 2); jl_svec_data(v)[0] = (jl_value_t*)a; jl_svec_data(v)[1] = (jl_value_t*)b; @@ -59,6 +62,7 @@ JL_DLLEXPORT jl_svec_t *jl_alloc_svec_uninit(size_t n) if (n == 0) return jl_emptysvec; jl_svec_t *jv = (jl_svec_t*)jl_gc_alloc(ct->ptls, (n + 1) * sizeof(void*), jl_simplevector_type); + jl_set_typetagof(jv, jl_simplevector_tag, 0); jl_svec_set_len_unsafe(jv, n); return jv; } diff --git a/src/stackwalk.c b/src/stackwalk.c index 093467750d573..18bf4b2126938 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -673,7 +673,7 @@ void jl_print_bt_entry_codeloc(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT while (debuginfoloc != 0) { jl_line_info_node_t *locinfo = (jl_line_info_node_t*) jl_array_ptr_ref(src->linetable, debuginfoloc - 1); - assert(jl_typeis(locinfo, jl_lineinfonode_type)); + assert(jl_typetagis(locinfo, jl_lineinfonode_type)); const char *func_name = "Unknown"; jl_value_t *method = locinfo->method; if (jl_is_method_instance(method)) diff --git a/src/staticdata.c b/src/staticdata.c index 353382f11a067..1afb360aced1d 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -598,20 +598,20 @@ static int jl_needs_serialization(jl_serializer_state *s, jl_value_t *v) JL_NOTS if (v == NULL || jl_is_symbol(v) || v == jl_nothing) { return 0; } - else if (jl_typeis(v, jl_int64_type)) { + else if (jl_typetagis(v, jl_int64_tag << 4)) { int64_t i64 = *(int64_t*)v + NBOX_C / 2; if ((uint64_t)i64 < NBOX_C) return 0; } - else if (jl_typeis(v, jl_int32_type)) { + else if (jl_typetagis(v, jl_int32_tag << 4)) { int32_t i32 = *(int32_t*)v + NBOX_C / 2; if ((uint32_t)i32 < NBOX_C) return 0; } - else if (jl_typeis(v, jl_uint8_type)) { + else if (jl_typetagis(v, jl_uint8_tag << 4)) { return 0; } - else if (jl_typeis(v, jl_task_type)) { + else if (jl_typetagis(v, jl_task_tag << 4)) { return 0; } @@ -838,7 +838,7 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ } } } - else if (jl_typeis(v, jl_module_type)) { + else if (jl_typetagis(v, jl_module_tag << 4)) { jl_queue_module_for_serialization(s, (jl_module_t*)v); } else if (layout->nfields > 0) { @@ -1024,17 +1024,17 @@ static uintptr_t _backref_id(jl_serializer_state *s, jl_value_t *v, jl_array_t * else if (v == jl_nothing) { return ((uintptr_t)TagRef << RELOC_TAG_OFFSET) + 1; } - else if (jl_typeis(v, jl_int64_type)) { + else if (jl_typetagis(v, jl_int64_tag << 4)) { int64_t i64 = *(int64_t*)v + NBOX_C / 2; if ((uint64_t)i64 < NBOX_C) return ((uintptr_t)TagRef << RELOC_TAG_OFFSET) + i64 + 2; } - else if (jl_typeis(v, jl_int32_type)) { + else if (jl_typetagis(v, jl_int32_tag << 4)) { int32_t i32 = *(int32_t*)v + NBOX_C / 2; if ((uint32_t)i32 < NBOX_C) return ((uintptr_t)TagRef << RELOC_TAG_OFFSET) + i32 + 2 + NBOX_C; } - else if (jl_typeis(v, jl_uint8_type)) { + else if (jl_typetagis(v, jl_uint8_tag << 4)) { uint8_t u8 = *(uint8_t*)v; return ((uintptr_t)TagRef << RELOC_TAG_OFFSET) + u8 + 2 + NBOX_C + NBOX_C; } @@ -1214,7 +1214,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED else if (s->incremental && needs_recaching(v)) { arraylist_push(jl_is_datatype(v) ? &s->fixup_types : &s->fixup_objs, (void*)reloc_offset); } - else if (s->incremental && jl_typeis(v, jl_binding_type)) { + else if (s->incremental && jl_typetagis(v, jl_binding_type)) { jl_binding_t *b = (jl_binding_t*)v; if (b->globalref == NULL || jl_object_in_image((jl_value_t*)b->globalref->mod)) jl_error("Binding cannot be serialized"); // no way (currently) to recover its identity @@ -1323,10 +1323,10 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED } } } - else if (jl_typeis(v, jl_module_type)) { + else if (jl_typetagis(v, jl_module_tag << 4)) { jl_write_module(s, item, (jl_module_t*)v); } - else if (jl_typeis(v, jl_task_type)) { + else if (jl_typetagis(v, jl_task_tag << 4)) { jl_error("Task cannot be serialized"); } else if (jl_is_svec(v)) { @@ -1350,7 +1350,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED assert(t->layout->npointers == 0); ios_write(s->s, (char*)v, jl_datatype_size(t)); } - else if (jl_bigint_type && jl_typeis(v, jl_bigint_type)) { + else if (jl_bigint_type && jl_typetagis(v, jl_bigint_type)) { // foreign types require special handling jl_value_t *sizefield = jl_get_nth_field(v, 1); int32_t sz = jl_unbox_int32(sizefield); @@ -1408,7 +1408,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED // A few objects need additional handling beyond the generic serialization above - if (s->incremental && jl_typeis(v, jl_typemap_entry_type)) { + if (s->incremental && jl_typetagis(v, jl_typemap_entry_type)) { jl_typemap_entry_t *newentry = (jl_typemap_entry_t*)&s->s->buf[reloc_offset]; if (newentry->max_world == ~(size_t)0) { if (newentry->min_world > 1) { @@ -1731,7 +1731,7 @@ static inline uintptr_t get_item_for_reloc(jl_serializer_state *s, uintptr_t bas #else size_t depsidx = 0; #endif - assert(depsidx < jl_array_len(s->buildid_depmods_idxs)); + assert(s->buildid_depmods_idxs && depsidx < jl_array_len(s->buildid_depmods_idxs)); size_t i = ((uint32_t*)jl_array_data(s->buildid_depmods_idxs))[depsidx]; assert(2*i < jl_linkage_blobs.len); return (uintptr_t)jl_linkage_blobs.items[2*i] + offset*sizeof(void*); @@ -1832,6 +1832,8 @@ static void jl_read_reloclist(jl_serializer_state *s, jl_array_t *link_ids, uint uintptr_t *pv = (uintptr_t *)(base + pos); uintptr_t v = *pv; v = get_item_for_reloc(s, base, size, v, link_ids, &link_index); + if (bits && v && ((jl_datatype_t*)v)->smalltag) + v = (uintptr_t)((jl_datatype_t*)v)->smalltag << 4; // TODO: should we have a representation that supports sweep without a relocation step? *pv = v | bits; } assert(!link_ids || link_index == jl_array_len(link_ids)); @@ -1944,6 +1946,9 @@ static void jl_update_all_fptrs(jl_serializer_state *s, jl_image_t *image) image->fptrs.base = NULL; if (fvars.base == NULL) return; + + memcpy(image->small_typeof, &small_typeof, sizeof(small_typeof)); + int img_fvars_max = s->fptr_record->size / sizeof(void*); size_t i; uintptr_t base = (uintptr_t)&s->s->buf[0]; @@ -2782,6 +2787,7 @@ JL_DLLEXPORT void jl_set_sysimg_so(void *handle) #endif extern void rebuild_image_blob_tree(void); +extern void export_small_typeof(void); static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl_array_t *depmods, uint64_t checksum, /* outputs */ jl_array_t **restored, jl_array_t **init_order, @@ -2857,9 +2863,13 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl jl_value_t **tag = tags[i]; *tag = jl_read_value(&s); } +#define XX(name) \ + small_typeof[(jl_##name##_tag << 4) / sizeof(*small_typeof)] = jl_##name##_type; + JL_SMALL_TYPEOF(XX) +#undef XX + export_small_typeof(); jl_global_roots_table = (jl_array_t*)jl_read_value(&s); // set typeof extra-special values now that we have the type set by tags above - jl_astaggedvalue(jl_current_task)->header = (uintptr_t)jl_task_type | jl_astaggedvalue(jl_current_task)->header; jl_astaggedvalue(jl_nothing)->header = (uintptr_t)jl_nothing_type | jl_astaggedvalue(jl_nothing)->header; s.ptls->root_task->tls = jl_read_value(&s); jl_gc_wb(s.ptls->root_task, s.ptls->root_task->tls); @@ -2995,9 +3005,9 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl continue; } } - jl_value_t *otyp = jl_typeof(obj); // the original type of the object that was written here + uintptr_t otyp = jl_typetagof(obj); // the original type of the object that was written here assert(image_base < (char*)obj && (char*)obj <= image_base + sizeof_sysimg + sizeof(uintptr_t)); - if (otyp == (jl_value_t*)jl_datatype_type) { + if (otyp == jl_datatype_tag << 4) { jl_datatype_t *dt = (jl_datatype_t*)obj[0], *newdt; if (jl_is_datatype(dt)) { newdt = dt; // already done @@ -3044,7 +3054,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl else *pfld = (uintptr_t)newobj; assert(!(image_base < (char*)newobj && (char*)newobj <= image_base + sizeof_sysimg + sizeof(uintptr_t))); - assert(jl_typeis(obj, otyp)); + assert(jl_typetagis(obj, otyp)); } // A few fields (reached via super) might be self-recursive. This is rare, but handle them now. // They cannot be instances though, since the type must fully exist before the singleton field can be allocated @@ -3121,8 +3131,8 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl pfld = (uintptr_t*)(image_base + item); obj = *(jl_value_t***)pfld; } - jl_value_t *otyp = jl_typeof(obj); // the original type of the object that was written here - if (otyp == (jl_value_t*)jl_method_instance_type) { + uintptr_t otyp = jl_typetagof(obj); // the original type of the object that was written here + if (otyp == (uintptr_t)jl_method_instance_type) { assert(image_base < (char*)obj && (char*)obj <= image_base + sizeof_sysimg + sizeof(uintptr_t)); jl_value_t *m = obj[0]; if (jl_is_method_instance(m)) { @@ -3141,7 +3151,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl } *pfld = (uintptr_t)newobj; assert(!(image_base < (char*)newobj && (char*)newobj <= image_base + sizeof_sysimg + sizeof(uintptr_t))); - assert(jl_typeis(obj, otyp)); + assert(jl_typetagis(obj, otyp)); } arraylist_free(&s.uniquing_types); arraylist_free(&s.uniquing_objs); @@ -3157,7 +3167,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl for (size_t i = 0; i < s.fixup_objs.len; i++) { uintptr_t item = (uintptr_t)s.fixup_objs.items[i]; jl_value_t *obj = (jl_value_t*)(image_base + item); - if (jl_typeis(obj, jl_typemap_entry_type)) { + if (jl_typetagis(obj, jl_typemap_entry_type)) { jl_typemap_entry_t *entry = (jl_typemap_entry_t*)obj; entry->min_world = world; } @@ -3198,7 +3208,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl // rehash IdDict //assert(((jl_datatype_t*)(jl_typeof(obj)))->name == jl_idtable_typename); jl_array_t **a = (jl_array_t**)obj; - assert(jl_typeis(*a, jl_array_any_type)); + assert(jl_typetagis(*a, jl_array_any_type)); *a = jl_idtable_rehash(*a, jl_array_len(*a)); jl_gc_wb(obj, *a); } diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 03dc3a82acd93..bf1a830b608de 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -949,7 +949,7 @@ static jl_array_t *jl_verify_methods(jl_array_t *edges, jl_array_t *maxvalids) jl_method_instance_t *caller = (jl_method_instance_t*)jl_array_ptr_ref(edges, 2 * i); assert(jl_is_method_instance(caller) && jl_is_method(caller->def.method)); jl_array_t *callee_ids = (jl_array_t*)jl_array_ptr_ref(edges, 2 * i + 1); - assert(jl_typeis((jl_value_t*)callee_ids, jl_array_int32_type)); + assert(jl_typetagis((jl_value_t*)callee_ids, jl_array_int32_type)); if (callee_ids == NULL) { // serializing the edges had failed maxvalids2_data[i] = 0; @@ -999,7 +999,7 @@ static int jl_verify_graph_edge(size_t *maxvalids2_data, jl_array_t *edges, size size_t depth = stack->len; visited->items[idx] = (void*)(1 + depth); jl_array_t *callee_ids = (jl_array_t*)jl_array_ptr_ref(edges, idx * 2 + 1); - assert(jl_typeis((jl_value_t*)callee_ids, jl_array_int32_type)); + assert(jl_typetagis((jl_value_t*)callee_ids, jl_array_int32_type)); int32_t *idxs = (int32_t*)jl_array_data(callee_ids); size_t i, n = jl_array_len(callee_ids); cycle = depth; diff --git a/src/subtype.c b/src/subtype.c index 2ff5f0f75d09d..ce44402dfbc59 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2286,7 +2286,9 @@ int jl_has_intersect_kind_not_type(jl_value_t *t) JL_DLLEXPORT int jl_isa(jl_value_t *x, jl_value_t *t) { - if (jl_typeis(x,t) || t == (jl_value_t*)jl_any_type) + if (t == (jl_value_t*)jl_any_type || jl_typetagis(x,t)) + return 1; + if (jl_typetagof(x) < (jl_max_tags << 4) && jl_is_datatype(t) && jl_typetagis(x,((jl_datatype_t*)t)->smalltag << 4)) return 1; if (jl_is_type(x)) { if (t == (jl_value_t*)jl_type_type) diff --git a/src/symbol.c b/src/symbol.c index 14606c82b9778..c9c0c0e533924 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -35,12 +35,10 @@ static jl_sym_t *mk_symbol(const char *str, size_t len) JL_NOTSAFEPOINT { jl_sym_t *sym; size_t nb = symbol_nbytes(len); - assert(jl_symbol_type && "not initialized"); - jl_taggedvalue_t *tag = (jl_taggedvalue_t*)jl_gc_perm_alloc_nolock(nb, 0, sizeof(void*), 0); sym = (jl_sym_t*)jl_valueof(tag); // set to old marked so that we won't look at it in the GC or write barrier. - tag->header = ((uintptr_t)jl_symbol_type) | GC_OLD_MARKED; + jl_set_typetagof(sym, jl_symbol_tag, GC_OLD_MARKED); jl_atomic_store_relaxed(&sym->left, NULL); jl_atomic_store_relaxed(&sym->right, NULL); sym->hash = hash_symbol(str, len); diff --git a/src/task.c b/src/task.c index 9678cf2f3fe4e..3b2cf69237ba6 100644 --- a/src/task.c +++ b/src/task.c @@ -1039,6 +1039,7 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion { jl_task_t *ct = jl_current_task; jl_task_t *t = (jl_task_t*)jl_gc_alloc(ct->ptls, sizeof(jl_task_t), jl_task_type); + jl_set_typetagof(t, jl_task_tag, 0); JL_PROBE_RT_NEW_TASK(ct, t); t->copy_stack = 0; if (ssize == 0) { @@ -1636,6 +1637,7 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) if (jl_nothing == NULL) // make a placeholder jl_nothing = jl_gc_permobj(0, jl_nothing_type); jl_task_t *ct = (jl_task_t*)jl_gc_alloc(ptls, sizeof(jl_task_t), jl_task_type); + jl_set_typetagof(ct, jl_task_tag, 0); memset(ct, 0, sizeof(jl_task_t)); void *stack = stack_lo; size_t ssize = (char*)stack_hi - (char*)stack_lo; diff --git a/src/toplevel.c b/src/toplevel.c index ad282a50d91ac..200d0ad220231 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -413,7 +413,7 @@ static void expr_attributes(jl_value_t *v, int *has_ccall, int *has_defs, int *h int jl_code_requires_compiler(jl_code_info_t *src, int include_force_compile) { jl_array_t *body = src->code; - assert(jl_typeis(body, jl_array_any_type)); + assert(jl_typetagis(body, jl_array_any_type)); size_t i; int has_ccall = 0, has_defs = 0, has_opaque = 0; if (include_force_compile && jl_has_meta(body, jl_force_compile_sym)) @@ -875,7 +875,7 @@ jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_value_t *e, int int has_ccall = 0, has_defs = 0, has_loops = 0, has_opaque = 0, forced_compile = 0; assert(head == jl_thunk_sym); thk = (jl_code_info_t*)jl_exprarg(ex, 0); - if (!jl_is_code_info(thk) || !jl_typeis(thk->code, jl_array_any_type)) { + if (!jl_is_code_info(thk) || !jl_typetagis(thk->code, jl_array_any_type)) { jl_eval_errorf(m, "malformed \"thunk\" statement"); } body_attributes((jl_array_t*)thk->code, &has_ccall, &has_defs, &has_loops, &has_opaque, &forced_compile); diff --git a/test/ccall.jl b/test/ccall.jl index eee48c5576465..0266dabd6332b 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -802,7 +802,7 @@ if cfunction_closure verbose && println("Testing cfunction closures: ") # helper Type for testing that constructors work -# with cfucntion and that object identity is preserved +# with cfunction and that object identity is preserved mutable struct IdentityTestKV{K, V} (T::Type{<:IdentityTestKV})(S) = (@test T === S; T) end From 160d2615db7f899b95d520f1232c3dc9d66e7d84 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 21 Apr 2023 09:20:32 -0400 Subject: [PATCH 695/775] move simple (isbits) objects into the constdata section --- src/jltypes.c | 2 +- src/staticdata.c | 216 ++++++++++++++++++++++++++++------------------- 2 files changed, 129 insertions(+), 89 deletions(-) diff --git a/src/jltypes.c b/src/jltypes.c index 0549ee2c07e53..24809bc534819 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2730,7 +2730,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 1, 0); XX(string); jl_string_type->instance = NULL; - jl_compute_field_offsets(jl_string_type); + jl_compute_field_offsets(jl_string_type); // re-compute now that we assigned jl_string_type jl_an_empty_string = jl_pchar_to_string("\0", 1); *(size_t*)jl_an_empty_string = 0; diff --git a/src/staticdata.c b/src/staticdata.c index 1afb360aced1d..3494aa93a8725 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -453,7 +453,7 @@ static const jl_fptr_args_t id_to_fptrs[] = { typedef struct { ios_t *s; // the main stream - ios_t *const_data; // codegen-invisible internal data (e.g., datatype layouts, list-like typename fields, foreign types, internal arrays) + ios_t *const_data; // GC-invisible internal data (e.g., datatype layouts, list-like typename fields, foreign types, internal arrays) ios_t *symbols; // names (char*) of symbols (some may be referenced by pointer in generated code) ios_t *relocs; // for (de)serializing relocs_list and gctags_list ios_t *gvar_record; // serialized array mapping gvid => spos @@ -1090,6 +1090,7 @@ static void write_gctaggedfield(jl_serializer_state *s, jl_datatype_t *ref) JL_N write_pointer(s->s); } + // Special handling from `jl_write_values` for modules static void jl_write_module(jl_serializer_state *s, uintptr_t item, jl_module_t *m) JL_GC_DISABLED { @@ -1187,41 +1188,57 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED assert(!(s->incremental && jl_object_in_image(v))); jl_datatype_t *t = (jl_datatype_t*)jl_typeof(v); assert((t->instance == NULL || t->instance == v) && "detected singleton construction corruption"); + ios_t *f = s->s; + if (t->smalltag) { + if (t->layout->npointers == 0 || t == jl_string_type) { + if (jl_datatype_nfields(t) == 0 || t->name->mutabl == 0 || t == jl_string_type) { + f = s->const_data; + } + } + } + // realign stream to expected gc alignment (16 bytes) - uintptr_t skip_header_pos = ios_pos(s->s) + sizeof(jl_taggedvalue_t); - write_padding(s->s, LLT_ALIGN(skip_header_pos, 16) - skip_header_pos); + uintptr_t skip_header_pos = ios_pos(f) + sizeof(jl_taggedvalue_t); + write_padding(f, LLT_ALIGN(skip_header_pos, 16) - skip_header_pos); // write header if (s->incremental && jl_needs_serialization(s, (jl_value_t*)t) && needs_uniquing((jl_value_t*)t)) - arraylist_push(&s->uniquing_types, (void*)(uintptr_t)(ios_pos(s->s)|1)); - write_gctaggedfield(s, t); - size_t reloc_offset = ios_pos(s->s); + arraylist_push(&s->uniquing_types, (void*)(uintptr_t)(ios_pos(f)|1)); + if (f == s->const_data) + write_uint(s->const_data, ((uintptr_t)t->smalltag << 4) | GC_OLD_MARKED); + else + write_gctaggedfield(s, t); + size_t reloc_offset = ios_pos(f); assert(item < layout_table.len && layout_table.items[item] == NULL); - layout_table.items[item] = (void*)reloc_offset; // store the inverse mapping of `serialization_order` (`id` => object-as-streampos) - - if (s->incremental && needs_uniquing(v)) { - if (jl_is_method_instance(v)) { - jl_method_instance_t *mi = (jl_method_instance_t*)v; - write_pointerfield(s, mi->def.value); - write_pointerfield(s, mi->specTypes); - write_pointerfield(s, (jl_value_t*)mi->sparam_vals); - continue; + layout_table.items[item] = (void*)(reloc_offset | (f == s->const_data)); // store the inverse mapping of `serialization_order` (`id` => object-as-streampos) + + if (s->incremental) { + if (needs_uniquing(v)) { + if (jl_is_method_instance(v)) { + assert(f == s->s); + jl_method_instance_t *mi = (jl_method_instance_t*)v; + write_pointerfield(s, mi->def.value); + write_pointerfield(s, mi->specTypes); + write_pointerfield(s, (jl_value_t*)mi->sparam_vals); + continue; + } + else if (!jl_is_datatype(v)) { + assert(jl_is_datatype_singleton(t) && "unreachable"); + } } - else if (!jl_is_datatype(v)) { - assert(jl_is_datatype_singleton(t) && "unreachable"); + else if (needs_recaching(v)) { + arraylist_push(jl_is_datatype(v) ? &s->fixup_types : &s->fixup_objs, (void*)reloc_offset); + } + else if (jl_typetagis(v, jl_binding_type)) { + jl_binding_t *b = (jl_binding_t*)v; + if (b->globalref == NULL || jl_object_in_image((jl_value_t*)b->globalref->mod)) + jl_error("Binding cannot be serialized"); // no way (currently) to recover its identity } - } - else if (s->incremental && needs_recaching(v)) { - arraylist_push(jl_is_datatype(v) ? &s->fixup_types : &s->fixup_objs, (void*)reloc_offset); - } - else if (s->incremental && jl_typetagis(v, jl_binding_type)) { - jl_binding_t *b = (jl_binding_t*)v; - if (b->globalref == NULL || jl_object_in_image((jl_value_t*)b->globalref->mod)) - jl_error("Binding cannot be serialized"); // no way (currently) to recover its identity } // write data if (jl_is_array(v)) { + assert(f == s->s); // Internal data for types in julia.h with `jl_array_t` field(s) #define JL_ARRAY_ALIGN(jl_value, nbytes) LLT_ALIGN(jl_value, nbytes) jl_array_t *ar = (jl_array_t*)v; @@ -1237,12 +1254,12 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED int ndimwords = jl_array_ndimwords(ar->flags.ndims); size_t headersize = sizeof(jl_array_t) + ndimwords*sizeof(size_t); // copy header - ios_write(s->s, (char*)v, headersize); + ios_write(f, (char*)v, headersize); size_t alignment_amt = JL_SMALL_BYTE_ALIGNMENT; if (tot >= ARRAY_CACHE_ALIGN_THRESHOLD) alignment_amt = JL_CACHE_BYTE_ALIGNMENT; // make some header modifications in-place - jl_array_t *newa = (jl_array_t*)&s->s->buf[reloc_offset]; + jl_array_t *newa = (jl_array_t*)&f->buf[reloc_offset]; if (newa->flags.ndims == 1) newa->maxsize = alen; newa->offset = 0; @@ -1284,17 +1301,17 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED } else { // Pointer eltypes are encoded in the mutable data section - size_t data = LLT_ALIGN(ios_pos(s->s), alignment_amt); - size_t padding_amt = data - ios_pos(s->s); + size_t data = LLT_ALIGN(ios_pos(f), alignment_amt); + size_t padding_amt = data - ios_pos(f); headersize += padding_amt; newa->data = (void*)headersize; // relocation offset arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_array_t, data))); // relocation location arraylist_push(&s->relocs_list, (void*)(((uintptr_t)DataRef << RELOC_TAG_OFFSET) + item)); // relocation target - write_padding(s->s, padding_amt); + write_padding(f, padding_amt); if (ar->flags.hasptr) { // copy all of the data first const char *data = (const char*)jl_array_data(ar); - ios_write(s->s, data, datasize); + ios_write(f, data, datasize); // the rewrite all of the embedded pointers to null+relocation uint16_t elsz = ar->elsize; size_t j, np = ((jl_datatype_t*)et)->layout->npointers; @@ -1309,7 +1326,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED arraylist_push(&s->relocs_list, (void*)backref_id(s, fld, s->link_ids_relocs)); // relocation target record_uniquing(s, fld, fld_pos); } - memset(&s->s->buf[fld_pos], 0, sizeof(fld)); // relocation offset (none) + memset(&f->buf[fld_pos], 0, sizeof(fld)); // relocation offset (none) } } } @@ -1323,14 +1340,16 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED } } } - else if (jl_typetagis(v, jl_module_tag << 4)) { + else if (jl_typeis(v, jl_module_type)) { + assert(f == s->s); jl_write_module(s, item, (jl_module_t*)v); } else if (jl_typetagis(v, jl_task_tag << 4)) { jl_error("Task cannot be serialized"); } else if (jl_is_svec(v)) { - ios_write(s->s, (char*)v, sizeof(void*)); + assert(f == s->s); + ios_write(f, (char*)v, sizeof(void*)); size_t ii, l = jl_svec_len(v); assert(l > 0 || (jl_svec_t*)v == jl_emptysvec); for (ii = 0; ii < l; ii++) { @@ -1338,8 +1357,8 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED } } else if (jl_is_string(v)) { - ios_write(s->s, (char*)v, sizeof(void*) + jl_string_len(v)); - write_uint8(s->s, '\0'); // null-terminated strings for easier C-compatibility + ios_write(f, (char*)v, sizeof(void*) + jl_string_len(v)); + write_uint8(f, '\0'); // null-terminated strings for easier C-compatibility } else if (jl_is_foreign_type(t) == 1) { jl_error("Cannot serialize instances of foreign datatypes"); @@ -1348,16 +1367,17 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED // The object has no fields, so we just snapshot its byte representation assert(!t->layout->npointers); assert(t->layout->npointers == 0); - ios_write(s->s, (char*)v, jl_datatype_size(t)); + ios_write(f, (char*)v, jl_datatype_size(t)); } else if (jl_bigint_type && jl_typetagis(v, jl_bigint_type)) { // foreign types require special handling + assert(f == s->s); jl_value_t *sizefield = jl_get_nth_field(v, 1); int32_t sz = jl_unbox_int32(sizefield); int32_t nw = (sz == 0 ? 1 : (sz < 0 ? -sz : sz)); size_t nb = nw * gmp_limb_size; - ios_write(s->s, (char*)&nw, sizeof(int32_t)); - ios_write(s->s, (char*)&sz, sizeof(int32_t)); + ios_write(f, (char*)&nw, sizeof(int32_t)); + ios_write(f, (char*)&sz, sizeof(int32_t)); uintptr_t data = LLT_ALIGN(ios_pos(s->const_data), 8); write_padding(s->const_data, data - ios_pos(s->const_data)); data /= sizeof(void*); @@ -1366,7 +1386,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED arraylist_push(&s->relocs_list, (void*)(((uintptr_t)ConstDataRef << RELOC_TAG_OFFSET) + data)); // relocation target void *pdata = jl_unbox_voidpointer(jl_get_nth_field(v, 2)); ios_write(s->const_data, (char*)pdata, nb); - write_pointer(s->s); + write_pointer(f); } else { // Generic object::DataType serialization by field @@ -1376,16 +1396,16 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED for (i = 0; i < nf; i++) { size_t offset = jl_field_offset(t, i); const char *slot = data + offset; - write_padding(s->s, offset - tot); + write_padding(f, offset - tot); tot = offset; size_t fsz = jl_field_size(t, i); if (t->name->mutabl && jl_is_cpointer_type(jl_field_type(t, i)) && *(intptr_t*)slot != -1) { // reset Ptr fields to C_NULL (but keep MAP_FAILED / INVALID_HANDLE) assert(!jl_field_isptr(t, i)); - write_pointer(s->s); + write_pointer(f); } else if (fsz > 0) { - ios_write(s->s, slot, fsz); + ios_write(f, slot, fsz); } tot += fsz; } @@ -1403,12 +1423,13 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED arraylist_push(&s->relocs_list, (void*)backref_id(s, fld, s->link_ids_relocs)); // relocation target record_uniquing(s, fld, fld_pos); } - memset(&s->s->buf[fld_pos], 0, sizeof(fld)); // relocation offset (none) + memset(&f->buf[fld_pos], 0, sizeof(fld)); // relocation offset (none) } // A few objects need additional handling beyond the generic serialization above if (s->incremental && jl_typetagis(v, jl_typemap_entry_type)) { + assert(f == s->s); jl_typemap_entry_t *newentry = (jl_typemap_entry_t*)&s->s->buf[reloc_offset]; if (newentry->max_world == ~(size_t)0) { if (newentry->min_world > 1) { @@ -1423,9 +1444,10 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED } } else if (jl_is_method(v)) { - write_padding(s->s, sizeof(jl_method_t) - tot); // hidden fields + assert(f == s->s); + write_padding(f, sizeof(jl_method_t) - tot); // hidden fields jl_method_t *m = (jl_method_t*)v; - jl_method_t *newm = (jl_method_t*)&s->s->buf[reloc_offset]; + jl_method_t *newm = (jl_method_t*)&f->buf[reloc_offset]; if (s->incremental) { if (newm->deleted_world != ~(size_t)0) newm->deleted_world = 1; @@ -1439,13 +1461,16 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED arraylist_push(&s->ccallable_list, (void*)reloc_offset); } else if (jl_is_method_instance(v)) { - jl_method_instance_t *newmi = (jl_method_instance_t*)&s->s->buf[reloc_offset]; + assert(f == s->s); + jl_method_instance_t *newmi = (jl_method_instance_t*)&f->buf[reloc_offset]; jl_atomic_store_relaxed(&newmi->precompiled, 0); } else if (jl_is_code_instance(v)) { + assert(f == s->s); // Handle the native-code pointers + assert(f == s->s); jl_code_instance_t *m = (jl_code_instance_t*)v; - jl_code_instance_t *newm = (jl_code_instance_t*)&s->s->buf[reloc_offset]; + jl_code_instance_t *newm = (jl_code_instance_t*)&f->buf[reloc_offset]; if (s->incremental) { arraylist_push(&s->fixup_objs, (void*)reloc_offset); @@ -1525,8 +1550,9 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED } } else if (jl_is_datatype(v)) { + assert(f == s->s); jl_datatype_t *dt = (jl_datatype_t*)v; - jl_datatype_t *newdt = (jl_datatype_t*)&s->s->buf[reloc_offset]; + jl_datatype_t *newdt = (jl_datatype_t*)&f->buf[reloc_offset]; if (dt->layout != NULL) { size_t nf = dt->layout->nfields; @@ -1557,8 +1583,9 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED } } else if (jl_is_typename(v)) { + assert(f == s->s); jl_typename_t *tn = (jl_typename_t*)v; - jl_typename_t *newtn = (jl_typename_t*)&s->s->buf[reloc_offset]; + jl_typename_t *newtn = (jl_typename_t*)&f->buf[reloc_offset]; if (tn->atomicfields != NULL) { size_t nb = (jl_svec_len(tn->names) + 31) / 32 * sizeof(uint32_t); uintptr_t layout = LLT_ALIGN(ios_pos(s->const_data), sizeof(void*)); @@ -1581,6 +1608,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED } } else if (jl_is_globalref(v)) { + assert(f == s->s); jl_globalref_t *gr = (jl_globalref_t*)v; if (s->incremental && jl_object_in_image((jl_value_t*)gr->mod)) { // will need to populate the binding field later @@ -1588,11 +1616,12 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED } } else if (((jl_datatype_t*)(jl_typeof(v)))->name == jl_idtable_typename) { + assert(f == s->s); // will need to rehash this, later (after types are fully constructed) arraylist_push(&s->fixup_objs, (void*)reloc_offset); } else { - write_padding(s->s, jl_datatype_size(t) - tot); + write_padding(f, jl_datatype_size(t) - tot); } } } @@ -1628,6 +1657,14 @@ static uintptr_t get_reloc_for_item(uintptr_t reloc_item, size_t reloc_offset) assert(reloc_item < layout_table.len); uintptr_t reloc_base = (uintptr_t)layout_table.items[reloc_item]; assert(reloc_base != 0 && "layout offset missing for relocation item"); + if (reloc_base & 1) { + // convert to a ConstDataRef + tag = ConstDataRef; + reloc_base &= ~(uintptr_t)1; + assert(LLT_ALIGN(reloc_base, sizeof(void*)) == reloc_base); + reloc_base /= sizeof(void*); + assert(reloc_offset == 0); + } // write reloc_offset into s->s at pos return ((uintptr_t)tag << RELOC_TAG_OFFSET) + reloc_base + reloc_offset; } @@ -1668,16 +1705,18 @@ static uintptr_t get_reloc_for_item(uintptr_t reloc_item, size_t reloc_offset) } // Compute target location at deserialization -static inline uintptr_t get_item_for_reloc(jl_serializer_state *s, uintptr_t base, size_t size, uintptr_t reloc_id, jl_array_t *link_ids, int *link_index) JL_NOTSAFEPOINT +static inline uintptr_t get_item_for_reloc(jl_serializer_state *s, uintptr_t base, uintptr_t reloc_id, jl_array_t *link_ids, int *link_index) JL_NOTSAFEPOINT { enum RefTags tag = (enum RefTags)(reloc_id >> RELOC_TAG_OFFSET); size_t offset = (reloc_id & (((uintptr_t)1 << RELOC_TAG_OFFSET) - 1)); switch (tag) { case DataRef: - assert(offset <= size); - return base + offset; + assert(offset <= s->s->size); + return (uintptr_t)base + offset; case ConstDataRef: - return (uintptr_t)s->const_data->buf + (offset * sizeof(void*)); + offset *= sizeof(void*); + assert(offset <= s->const_data->size); + return (uintptr_t)s->const_data->buf + offset; case SymbolRef: assert(offset < deser_sym.len && deser_sym.items[offset] && "corrupt relocation item id"); return (uintptr_t)deser_sym.items[offset]; @@ -1752,16 +1791,23 @@ static inline uintptr_t get_item_for_reloc(jl_serializer_state *s, uintptr_t bas } -static void jl_write_offsetlist(ios_t *s, char *base, size_t size, arraylist_t *list) +static void jl_finish_relocs(char *base, size_t size, arraylist_t *list) { for (size_t i = 0; i < list->len; i += 2) { - size_t last_pos = i ? (size_t)list->items[i - 2] : 0; size_t pos = (size_t)list->items[i]; size_t item = (size_t)list->items[i + 1]; // item is tagref-encoded uintptr_t *pv = (uintptr_t*)(base + pos); assert(pos < size && pos != 0); *pv = get_reloc_for_item(item, *pv); + } +} +static void jl_write_offsetlist(ios_t *s, size_t size, arraylist_t *list) +{ + for (size_t i = 0; i < list->len; i += 2) { + size_t last_pos = i ? (size_t)list->items[i - 2] : 0; + size_t pos = (size_t)list->items[i]; + assert(pos < size && pos != 0); // write pos as compressed difference. size_t pos_diff = pos - last_pos; while (pos_diff) { @@ -1790,23 +1836,9 @@ static void jl_write_arraylist(ios_t *s, arraylist_t *list) ios_write(s, (const char*)list->items, list->len * sizeof(void*)); } -static void jl_write_relocations(jl_serializer_state *s) -{ - char *base = &s->s->buf[0]; - jl_write_offsetlist(s->relocs, base, s->s->size, &s->gctags_list); - jl_write_offsetlist(s->relocs, base, s->s->size, &s->relocs_list); - if (s->incremental) { - jl_write_arraylist(s->relocs, &s->uniquing_types); - jl_write_arraylist(s->relocs, &s->uniquing_objs); - jl_write_arraylist(s->relocs, &s->fixup_types); - } - jl_write_arraylist(s->relocs, &s->fixup_objs); -} - static void jl_read_reloclist(jl_serializer_state *s, jl_array_t *link_ids, uint8_t bits) { uintptr_t base = (uintptr_t)s->s->buf; - size_t size = s->s->size; uintptr_t last_pos = 0; uint8_t *current = (uint8_t *)(s->relocs->buf + s->relocs->bpos); int link_index = 0; @@ -1831,7 +1863,7 @@ static void jl_read_reloclist(jl_serializer_state *s, jl_array_t *link_ids, uint last_pos = pos; uintptr_t *pv = (uintptr_t *)(base + pos); uintptr_t v = *pv; - v = get_item_for_reloc(s, base, size, v, link_ids, &link_index); + v = get_item_for_reloc(s, base, v, link_ids, &link_index); if (bits && v && ((jl_datatype_t*)v)->smalltag) v = (uintptr_t)((jl_datatype_t*)v)->smalltag << 4; // TODO: should we have a representation that supports sweep without a relocation step? *pv = v | bits; @@ -1900,13 +1932,12 @@ static void _jl_write_value(jl_serializer_state *s, jl_value_t *v) static jl_value_t *jl_read_value(jl_serializer_state *s) { - uintptr_t base = (uintptr_t)&s->s->buf[0]; - size_t size = s->s->size; + uintptr_t base = (uintptr_t)s->s->buf; uintptr_t offset = *(reloc_t*)(base + (uintptr_t)s->s->bpos); s->s->bpos += sizeof(reloc_t); if (offset == 0) return NULL; - return (jl_value_t*)get_item_for_reloc(s, base, size, offset, NULL, NULL); + return (jl_value_t*)get_item_for_reloc(s, base, offset, NULL, NULL); } // The next two, `jl_read_offset` and `jl_delayed_reloc`, are essentially a split version @@ -1929,10 +1960,9 @@ static jl_value_t *jl_delayed_reloc(jl_serializer_state *s, uintptr_t offset) JL { if (!offset) return NULL; - uintptr_t base = (uintptr_t)&s->s->buf[0]; - size_t size = s->s->size; + uintptr_t base = (uintptr_t)s->s->buf; int link_index = 0; - jl_value_t *ret = (jl_value_t*)get_item_for_reloc(s, base, size, offset, s->link_ids_relocs, &link_index); + jl_value_t *ret = (jl_value_t*)get_item_for_reloc(s, base, offset, s->link_ids_relocs, &link_index); assert(!s->link_ids_relocs || link_index < jl_array_len(s->link_ids_relocs)); return ret; } @@ -2020,10 +2050,9 @@ static void jl_update_all_gvars(jl_serializer_state *s, jl_image_t *image, uint3 { if (image->gvars_base == NULL) return; + uintptr_t base = (uintptr_t)s->s->buf; size_t i = 0; size_t l = s->gvar_record->size / sizeof(reloc_t); - uintptr_t base = (uintptr_t)&s->s->buf[0]; - size_t size = s->s->size; reloc_t *gvars = (reloc_t*)&s->gvar_record->buf[0]; int gvar_link_index = 0; int external_fns_link_index = 0; @@ -2032,10 +2061,10 @@ static void jl_update_all_gvars(jl_serializer_state *s, jl_image_t *image, uint3 uintptr_t offset = gvars[i]; uintptr_t v = 0; if (i < external_fns_begin) { - v = get_item_for_reloc(s, base, size, offset, s->link_ids_gvars, &gvar_link_index); + v = get_item_for_reloc(s, base, offset, s->link_ids_gvars, &gvar_link_index); } else { - v = get_item_for_reloc(s, base, size, offset, s->link_ids_external_fnvars, &external_fns_link_index); + v = get_item_for_reloc(s, base, offset, s->link_ids_external_fnvars, &external_fns_link_index); } uintptr_t *gv = sysimg_gvars(image->gvars_base, image->gvars_offsets, i); *gv = v; @@ -2371,7 +2400,7 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, ios_mem(&relocs, 0); ios_mem(&gvar_record, 0); ios_mem(&fptr_record, 0); - jl_serializer_state s; + jl_serializer_state s = {0}; s.incremental = !(worklist == NULL); s.s = &sysimg; s.const_data = &const_data; @@ -2487,7 +2516,6 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, write_padding(&sysimg, sizeof(uintptr_t)); jl_write_values(&s); external_fns_begin = write_gvars(&s, &gvars, &external_fns); - jl_write_relocations(&s); } // This ensures that we can use the low bit of addresses for @@ -2516,9 +2544,12 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, // step 3: combine all of the sections into one file assert(ios_pos(f) % JL_CACHE_BYTE_ALIGNMENT == 0); + ssize_t sysimg_offset = ios_pos(f); write_uint(f, sysimg.size - sizeof(uintptr_t)); ios_seek(&sysimg, sizeof(uintptr_t)); ios_copyall(f, &sysimg); + size_t sysimg_size = s.s->size; + assert(ios_pos(f) - sysimg_offset == sysimg_size); ios_close(&sysimg); write_uint(f, const_data.size); @@ -2534,6 +2565,18 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, ios_copyall(f, &symbols); ios_close(&symbols); + // Prepare and write the relocations sections, now that the rest of the image is laid out + char *base = &f->buf[0]; + jl_finish_relocs(base + sysimg_offset, sysimg_size, &s.gctags_list); + jl_finish_relocs(base + sysimg_offset, sysimg_size, &s.relocs_list); + jl_write_offsetlist(s.relocs, sysimg_size, &s.gctags_list); + jl_write_offsetlist(s.relocs, sysimg_size, &s.relocs_list); + if (s.incremental) { + jl_write_arraylist(s.relocs, &s.uniquing_types); + jl_write_arraylist(s.relocs, &s.uniquing_objs); + jl_write_arraylist(s.relocs, &s.fixup_types); + } + jl_write_arraylist(s.relocs, &s.fixup_objs); write_uint(f, relocs.size); write_padding(f, LLT_ALIGN(ios_pos(f), 8) - ios_pos(f)); ios_seek(&relocs, 0); @@ -2798,7 +2841,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl { int en = jl_gc_enable(0); ios_t sysimg, const_data, symbols, relocs, gvar_record, fptr_record; - jl_serializer_state s; + jl_serializer_state s = {0}; s.incremental = restored != NULL; // jl_linkage_blobs.len > 0; s.image = image; s.s = NULL; @@ -2808,9 +2851,6 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl s.gvar_record = &gvar_record; s.fptr_record = &fptr_record; s.ptls = jl_current_task->ptls; - arraylist_new(&s.relocs_list, 0); - arraylist_new(&s.gctags_list, 0); - s.link_ids_relocs = s.link_ids_gctags = s.link_ids_gvars = s.link_ids_external_fnvars = NULL; jl_value_t **const*const tags = get_tags(); htable_t new_dt_objs; htable_new(&new_dt_objs, 0); From 4be81cdb15077ea06b5af8d876526e25c81ead82 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 27 Jan 2023 14:17:47 -0500 Subject: [PATCH 696/775] change CodeInfo compression from Array to String String is simpler and smaller (since we do not yet have a Buffer{UInt8} type, which might be almost as simple) making it easier to manipulate. Combined with the previous changes, this lets us eliminate many more relocations from the image (previously every Array->data), which is quite a few megabytes of array header objects simply gone. MASTER: sysimg size breakdown: sys data: 79207516 isbits data: 58603452 symbols: 463610 tags list: 1078555 reloc list: 4491653 gvar list: 118848 fptr list: 214000 .text 11109176 .data 144184704 BEFORE (moving existing Strings): sysimg size breakdown: sys data: 75146604 isbits data: 62679532 symbols: 463411 tags list: 1015525 reloc list: 4481370 gvar list: 118688 fptr list: 213952 .text 11129192 .data 144126152 AFTER (making CodeInfo into Strings): sys data: 71348124 isbits data: 63789244 symbols: 463411 tags list: 933984 reloc list: 4398377 gvar list: 118688 fptr list: 213904 .text 11132968 .data 141272824 --- base/compiler/inferencestate.jl | 2 +- base/compiler/optimize.jl | 2 +- base/compiler/ssair/inlining.jl | 6 ++-- base/compiler/typeinfer.jl | 16 +++++++---- base/compiler/utilities.jl | 2 +- base/reflection.jl | 4 +-- src/aotcompile.cpp | 15 +++++----- src/codegen.cpp | 12 ++++---- src/gf.c | 2 +- src/interpreter.c | 4 +-- src/ircode.c | 50 +++++++++++++++++---------------- src/jitlayers.cpp | 6 ++-- src/julia.h | 16 +++++------ src/precompile_utils.c | 4 +-- src/staticdata.c | 29 ++++++++++--------- test/compiler/contextual.jl | 4 +-- test/compiler/inline.jl | 8 +++--- test/compiler/ssair.jl | 11 ++++---- 18 files changed, 103 insertions(+), 90 deletions(-) diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 0e0409f755a0b..35469b9f29524 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -688,7 +688,7 @@ function IRInterpretationState(interp::AbstractInterpreter, code::CodeInstance, mi::MethodInstance, argtypes::Vector{Any}, world::UInt) @assert code.def === mi src = @atomic :monotonic code.inferred - if isa(src, Vector{UInt8}) + if isa(src, String) src = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), mi.def, C_NULL, src)::CodeInfo else isa(src, CodeInfo) || return nothing diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 0ab1b14d4a185..71eeb15d53eb0 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -43,7 +43,7 @@ const TOP_TUPLE = GlobalRef(Core, :tuple) const InlineCostType = UInt16 const MAX_INLINE_COST = typemax(InlineCostType) const MIN_INLINE_COST = InlineCostType(10) -const MaybeCompressed = Union{CodeInfo, Vector{UInt8}} +const MaybeCompressed = Union{CodeInfo, String} is_inlineable(@nospecialize src::MaybeCompressed) = ccall(:jl_ir_inlining_cost, InlineCostType, (Any,), src) != MAX_INLINE_COST diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 746a64b3a0996..3c444894dd4b6 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -944,7 +944,7 @@ end function may_have_fcalls(m::Method) isdefined(m, :source) || return true src = m.source - isa(src, CodeInfo) || isa(src, Vector{UInt8}) || return true + isa(src, MaybeCompressed) || return true return ccall(:jl_ir_flag_has_fcall, Bool, (Any,), src) end @@ -982,8 +982,8 @@ function analyze_method!(match::MethodMatch, argtypes::Vector{Any}, return resolve_todo(mi, match, argtypes, info, flag, state; invokesig) end -function retrieve_ir_for_inlining(mi::MethodInstance, src::Array{UInt8, 1}) - src = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), mi.def, C_NULL, src::Vector{UInt8})::CodeInfo +function retrieve_ir_for_inlining(mi::MethodInstance, src::String) + src = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), mi.def, C_NULL, src)::CodeInfo return inflate_ir!(src, mi) end retrieve_ir_for_inlining(mi::MethodInstance, src::CodeInfo) = inflate_ir(src, mi) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 180c10d8340ad..7d983ec5420db 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -325,9 +325,15 @@ function CodeInstance(interp::AbstractInterpreter, result::InferenceResult, const_flags = 0x00 end end - relocatability = isa(inferred_result, Vector{UInt8}) ? inferred_result[end] : - inferred_result === nothing ? UInt8(1) : UInt8(0) - # relocatability = isa(inferred_result, Vector{UInt8}) ? inferred_result[end] : UInt8(0) + relocatability = 0x0 + if isa(inferred_result, String) + t = @_gc_preserve_begin inferred_result + relocatability = unsafe_load(unsafe_convert(Ptr{UInt8}, inferred_result), Core.sizeof(inferred_result)) + @_gc_preserve_end t + elseif inferred_result === nothing + relocatability = 0x1 + end + # relocatability = isa(inferred_result, String) ? inferred_result[end] : UInt8(0) return CodeInstance(result.linfo, widenconst(result_type), rettype_const, inferred_result, const_flags, first(valid_worlds), last(valid_worlds), @@ -352,7 +358,7 @@ function maybe_compress_codeinfo(interp::AbstractInterpreter, linfo::MethodInsta nslots = length(ci.slotflags) resize!(ci.slottypes::Vector{Any}, nslots) resize!(ci.slotnames, nslots) - return ccall(:jl_compress_ir, Vector{UInt8}, (Any, Any), def, ci) + return ccall(:jl_compress_ir, String, (Any, Any), def, ci) else return ci end @@ -1031,7 +1037,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) inf.rettype = code.rettype end return inf - elseif isa(inf, Vector{UInt8}) + elseif isa(inf, String) ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) inf = _uncompressed_ir(code, inf) return inf diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 80d3feaf363be..836c370b98bd4 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -137,7 +137,7 @@ function retrieve_code_info(linfo::MethodInstance, world::UInt) if src === nothing # can happen in images built with --strip-ir return nothing - elseif isa(src, Array{UInt8,1}) + elseif isa(src, String) c = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), m, C_NULL, src) else c = copy(src::CodeInfo) diff --git a/base/reflection.jl b/base/reflection.jl index 0156db00c9f52..a3e98e11a5f04 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1163,8 +1163,8 @@ uncompressed_ir(m::Method) = isdefined(m, :source) ? _uncompressed_ir(m, m.sourc isdefined(m, :generator) ? error("Method is @generated; try `code_lowered` instead.") : error("Code for this Method is not available.") _uncompressed_ir(m::Method, s::CodeInfo) = copy(s) -_uncompressed_ir(m::Method, s::Array{UInt8,1}) = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), m, C_NULL, s)::CodeInfo -_uncompressed_ir(ci::Core.CodeInstance, s::Array{UInt8,1}) = ccall(:jl_uncompress_ir, Any, (Any, Any, Any), ci.def.def::Method, ci, s)::CodeInfo +_uncompressed_ir(m::Method, s::String) = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), m, C_NULL, s)::CodeInfo +_uncompressed_ir(ci::Core.CodeInstance, s::String) = ccall(:jl_uncompress_ir, Any, (Any, Any, Any), ci.def.def::Method, ci, s)::CodeInfo # for backwards compat const uncompressed_ast = uncompressed_ir const _uncompressed_ast = _uncompressed_ir diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 0c2aa4feb678d..01f023d5bef5e 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -238,7 +238,7 @@ static void jl_ci_cache_lookup(const jl_cgparams_t &cgparams, jl_method_instance if ((jl_value_t*)*src_out == jl_nothing) *src_out = NULL; if (*src_out && jl_is_method(def)) - *src_out = jl_uncompress_ir(def, codeinst, (jl_array_t*)*src_out); + *src_out = jl_uncompress_ir(def, codeinst, (jl_value_t*)*src_out); } if (*src_out == NULL || !jl_is_code_info(*src_out)) { if (cgparams.lookup != jl_rettype_inferred) { @@ -2028,17 +2028,18 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, siz jl_value_t *jlrettype = (jl_value_t*)jl_any_type; jl_code_info_t *src = NULL; JL_GC_PUSH2(&src, &jlrettype); - if (jl_is_method(mi->def.method) && mi->def.method->source != NULL && jl_ir_flag_inferred((jl_array_t*)mi->def.method->source)) { + if (jl_is_method(mi->def.method) && mi->def.method->source != NULL && mi->def.method->source != jl_nothing && jl_ir_flag_inferred(mi->def.method->source)) { src = (jl_code_info_t*)mi->def.method->source; if (src && !jl_is_code_info(src)) - src = jl_uncompress_ir(mi->def.method, NULL, (jl_array_t*)src); - } else { + src = jl_uncompress_ir(mi->def.method, NULL, (jl_value_t*)src); + } + else { jl_value_t *ci = jl_rettype_inferred(mi, world, world); if (ci != jl_nothing) { jl_code_instance_t *codeinst = (jl_code_instance_t*)ci; src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred); if ((jl_value_t*)src != jl_nothing && !jl_is_code_info(src) && jl_is_method(mi->def.method)) - src = jl_uncompress_ir(mi->def.method, codeinst, (jl_array_t*)src); + src = jl_uncompress_ir(mi->def.method, codeinst, (jl_value_t*)src); jlrettype = codeinst->rettype; } if (!src || (jl_value_t*)src == jl_nothing) { @@ -2047,8 +2048,8 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, siz jlrettype = src->rettype; else if (jl_is_method(mi->def.method)) { src = mi->def.method->generator ? jl_code_for_staged(mi, world) : (jl_code_info_t*)mi->def.method->source; - if (src && !jl_is_code_info(src) && jl_is_method(mi->def.method)) - src = jl_uncompress_ir(mi->def.method, NULL, (jl_array_t*)src); + if (src && (jl_value_t*)src != jl_nothing && !jl_is_code_info(src) && jl_is_method(mi->def.method)) + src = jl_uncompress_ir(mi->def.method, NULL, (jl_value_t*)src); } // TODO: use mi->uninferred } diff --git a/src/codegen.cpp b/src/codegen.cpp index da69678dee6ff..a9d2cb0c60333 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5252,7 +5252,7 @@ static std::pair get_oc_function(jl_codectx_t &ctx, jl_met } ++EmittedOpaqueClosureFunctions; - ir = jl_uncompress_ir(closure_method, ci, (jl_array_t*)inferred); + ir = jl_uncompress_ir(closure_method, ci, (jl_value_t*)inferred); // TODO: Emit this inline and outline it late using LLVM's coroutine support. orc::ThreadSafeModule closure_m = jl_create_ts_module( @@ -8648,7 +8648,7 @@ jl_llvm_functions_t jl_emit_codeinst( return jl_emit_oc_wrapper(m, params, codeinst->def, codeinst->rettype); } if (src && (jl_value_t*)src != jl_nothing && jl_is_method(def)) - src = jl_uncompress_ir(def, codeinst, (jl_array_t*)src); + src = jl_uncompress_ir(def, codeinst, (jl_value_t*)src); if (!src || !jl_is_code_info(src)) { JL_GC_POP(); m = orc::ThreadSafeModule(); @@ -8677,7 +8677,7 @@ jl_llvm_functions_t jl_emit_codeinst( } if (params.world) {// don't alter `inferred` when the code is not directly being used - auto inferred = jl_atomic_load_relaxed(&codeinst->inferred); + jl_value_t *inferred = jl_atomic_load_relaxed(&codeinst->inferred); // don't change inferred state if (inferred) { jl_method_t *def = codeinst->def->def.method; @@ -8689,8 +8689,8 @@ jl_llvm_functions_t jl_emit_codeinst( if (inferred != (jl_value_t*)src) { if (jl_is_method(def)) { src = (jl_code_info_t*)jl_compress_ir(def, src); - assert(jl_typetagis(src, jl_array_uint8_type)); - codeinst->relocatability = ((uint8_t*)jl_array_data(src))[jl_array_len(src)-1]; + assert(jl_is_string(src)); + codeinst->relocatability = jl_string_data(src)[jl_string_len(src)-1]; } jl_atomic_store_release(&codeinst->inferred, (jl_value_t*)src); jl_gc_wb(codeinst, src); @@ -8701,7 +8701,7 @@ jl_llvm_functions_t jl_emit_codeinst( inferred != jl_nothing && // don't delete inlineable code, unless it is constant (jl_atomic_load_relaxed(&codeinst->invoke) == jl_fptr_const_return_addr || - (jl_ir_inlining_cost((jl_array_t*)inferred) == UINT16_MAX)) && + (jl_ir_inlining_cost(inferred) == UINT16_MAX)) && // don't delete code when generating a precompile file !(params.imaging || jl_options.incremental)) { // if not inlineable, code won't be needed again diff --git a/src/gf.c b/src/gf.c index 5df3e18ff8db7..b8bb21c36b2a7 100644 --- a/src/gf.c +++ b/src/gf.c @@ -443,7 +443,7 @@ JL_DLLEXPORT jl_value_t *jl_rettype_inferred(jl_method_instance_t *mi, size_t mi while (codeinst) { if (codeinst->min_world <= min_world && max_world <= codeinst->max_world) { jl_value_t *code = jl_atomic_load_relaxed(&codeinst->inferred); - if (code && (code == jl_nothing || jl_ir_flag_inferred((jl_array_t*)code))) + if (code && (code == jl_nothing || jl_ir_flag_inferred(code))) return (jl_value_t*)codeinst; } codeinst = jl_atomic_load_relaxed(&codeinst->next); diff --git a/src/interpreter.c b/src/interpreter.c index c962abaa3a31a..f5ec0ce6858c8 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -641,7 +641,7 @@ jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *mi, size_t world) } if (src && (jl_value_t*)src != jl_nothing) { JL_GC_PUSH1(&src); - src = jl_uncompress_ir(mi->def.method, NULL, (jl_array_t*)src); + src = jl_uncompress_ir(mi->def.method, NULL, (jl_value_t*)src); jl_atomic_store_release(&mi->uninferred, (jl_value_t*)src); jl_gc_wb(mi, src); JL_GC_POP(); @@ -703,7 +703,7 @@ JL_DLLEXPORT jl_callptr_t jl_fptr_interpret_call_addr = &jl_fptr_interpret_call; jl_value_t *jl_interpret_opaque_closure(jl_opaque_closure_t *oc, jl_value_t **args, size_t nargs) { jl_method_t *source = oc->source; - jl_code_info_t *code = jl_uncompress_ir(source, NULL, (jl_array_t*)source->source); + jl_code_info_t *code = jl_uncompress_ir(source, NULL, (jl_value_t*)source->source); interpreter_state *s; unsigned nroots = jl_source_nslots(code) + jl_source_nssavalues(code) + 2; jl_task_t *ct = jl_current_task; diff --git a/src/ircode.c b/src/ircode.c index e04ef3fa6d96c..4121d6691aa5b 100644 --- a/src/ircode.c +++ b/src/ircode.c @@ -761,7 +761,9 @@ static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED // --- entry points --- -JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) +typedef jl_value_t jl_string_t; // for local expressibility + +JL_DLLEXPORT jl_string_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) { JL_TIMING(AST_COMPRESS, AST_COMPRESS); JL_LOCK(&m->writelock); // protect the roots array (Might GC) @@ -841,7 +843,7 @@ JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) write_uint8(s.s, s.relocatability); ios_flush(s.s); - jl_array_t *v = jl_take_buffer(&dest); + jl_string_t *v = jl_pchar_to_string(s.s->buf, s.s->size); ios_close(s.s); if (jl_array_len(m->roots) == 0) { m->roots = NULL; @@ -854,19 +856,19 @@ JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) return v; } -JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t *metadata, jl_array_t *data) +JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t *metadata, jl_string_t *data) { if (jl_is_code_info(data)) return (jl_code_info_t*)data; JL_TIMING(AST_UNCOMPRESS, AST_UNCOMPRESS); JL_LOCK(&m->writelock); // protect the roots array (Might GC) assert(jl_is_method(m)); - assert(jl_typetagis(data, jl_array_uint8_type)); + assert(jl_is_string(data)); size_t i; ios_t src; ios_mem(&src, 0); - ios_setbuf(&src, (char*)data->data, jl_array_len(data), 0); - src.size = jl_array_len(data); + ios_setbuf(&src, (char*)jl_string_data(data), jl_string_len(data), 0); + src.size = jl_string_len(data); int en = jl_gc_enable(0); // Might GC jl_ircode_state s = { &src, @@ -939,42 +941,42 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t return code; } -JL_DLLEXPORT uint8_t jl_ir_flag_inferred(jl_array_t *data) +JL_DLLEXPORT uint8_t jl_ir_flag_inferred(jl_string_t *data) { if (jl_is_code_info(data)) return ((jl_code_info_t*)data)->inferred; - assert(jl_typetagis(data, jl_array_uint8_type)); + assert(jl_is_string(data)); jl_code_info_flags_t flags; - flags.packed = ((uint8_t*)data->data)[0]; + flags.packed = jl_string_data(data)[0]; return flags.bits.inferred; } -JL_DLLEXPORT uint8_t jl_ir_flag_inlining(jl_array_t *data) +JL_DLLEXPORT uint8_t jl_ir_flag_inlining(jl_string_t *data) { if (jl_is_code_info(data)) return ((jl_code_info_t*)data)->inlining; - assert(jl_typetagis(data, jl_array_uint8_type)); + assert(jl_is_string(data)); jl_code_info_flags_t flags; - flags.packed = ((uint8_t*)data->data)[0]; + flags.packed = jl_string_data(data)[0]; return flags.bits.inlining; } -JL_DLLEXPORT uint8_t jl_ir_flag_has_fcall(jl_array_t *data) +JL_DLLEXPORT uint8_t jl_ir_flag_has_fcall(jl_string_t *data) { if (jl_is_code_info(data)) return ((jl_code_info_t*)data)->has_fcall; - assert(jl_typetagis(data, jl_array_uint8_type)); + assert(jl_is_string(data)); jl_code_info_flags_t flags; - flags.packed = ((uint8_t*)data->data)[0]; + flags.packed = jl_string_data(data)[0]; return flags.bits.has_fcall; } -JL_DLLEXPORT uint16_t jl_ir_inlining_cost(jl_array_t *data) +JL_DLLEXPORT uint16_t jl_ir_inlining_cost(jl_string_t *data) { if (jl_is_code_info(data)) return ((jl_code_info_t*)data)->inlining_cost; - assert(jl_typetagis(data, jl_array_uint8_type)); - uint16_t res = jl_load_unaligned_i16((char*)data->data + 2); + assert(jl_is_string(data)); + uint16_t res = jl_load_unaligned_i16(jl_string_data(data) + 2); return res; } @@ -1004,26 +1006,26 @@ JL_DLLEXPORT jl_value_t *jl_compress_argnames(jl_array_t *syms) return str; } -JL_DLLEXPORT ssize_t jl_ir_nslots(jl_array_t *data) +JL_DLLEXPORT ssize_t jl_ir_nslots(jl_value_t *data) { if (jl_is_code_info(data)) { jl_code_info_t *func = (jl_code_info_t*)data; return jl_array_len(func->slotnames); } else { - assert(jl_typetagis(data, jl_array_uint8_type)); - int nslots = jl_load_unaligned_i32((char*)data->data + 2 + sizeof(uint16_t)); + assert(jl_is_string(data)); + int nslots = jl_load_unaligned_i32(jl_string_data(data) + 2 + sizeof(uint16_t)); return nslots; } } -JL_DLLEXPORT uint8_t jl_ir_slotflag(jl_array_t *data, size_t i) +JL_DLLEXPORT uint8_t jl_ir_slotflag(jl_string_t *data, size_t i) { assert(i < jl_ir_nslots(data)); if (jl_is_code_info(data)) return ((uint8_t*)((jl_code_info_t*)data)->slotflags->data)[i]; - assert(jl_typetagis(data, jl_array_uint8_type)); - return ((uint8_t*)data->data)[2 + sizeof(uint16_t) + sizeof(int32_t) + i]; + assert(jl_is_string(data)); + return jl_string_data(data)[2 + sizeof(uint16_t) + sizeof(int32_t) + i]; } JL_DLLEXPORT jl_array_t *jl_uncompress_argnames(jl_value_t *syms) diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index c6aef2d35839c..643f0468457ae 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -455,7 +455,7 @@ jl_code_instance_t *jl_generate_fptr_impl(jl_method_instance_t *mi JL_PROPAGATES if ((jl_value_t*)src == jl_nothing) src = NULL; else if (jl_is_method(mi->def.method)) - src = jl_uncompress_ir(mi->def.method, codeinst, (jl_array_t*)src); + src = jl_uncompress_ir(mi->def.method, codeinst, (jl_value_t*)src); } else { // identify whether this is an invalidated method that is being recompiled @@ -546,7 +546,7 @@ void jl_generate_fptr_for_unspecialized_impl(jl_code_instance_t *unspec) src = jl_code_for_staged(unspec->def, ~(size_t)0); } if (src && (jl_value_t*)src != jl_nothing) - src = jl_uncompress_ir(def, NULL, (jl_array_t*)src); + src = jl_uncompress_ir(def, NULL, (jl_value_t*)src); } else { src = (jl_code_info_t*)jl_atomic_load_relaxed(&unspec->def->uninferred); @@ -606,7 +606,7 @@ jl_value_t *jl_dump_method_asm_impl(jl_method_instance_t *mi, size_t world, src = def->generator ? jl_code_for_staged(mi, world) : (jl_code_info_t*)def->source; } if (src && (jl_value_t*)src != jl_nothing) - src = jl_uncompress_ir(mi->def.method, codeinst, (jl_array_t*)src); + src = jl_uncompress_ir(mi->def.method, codeinst, (jl_value_t*)src); } fptr = (uintptr_t)jl_atomic_load_acquire(&codeinst->invoke); specfptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->specptr.fptr); diff --git a/src/julia.h b/src/julia.h index 6ecb845f77afe..8774889e71e07 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1918,14 +1918,14 @@ JL_DLLEXPORT void jl_register_newmeth_tracer(void (*callback)(jl_method_t *trace JL_DLLEXPORT jl_value_t *jl_copy_ast(jl_value_t *expr JL_MAYBE_UNROOTED); // IR representation -JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code); -JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t *metadata, jl_array_t *data); -JL_DLLEXPORT uint8_t jl_ir_flag_inferred(jl_array_t *data) JL_NOTSAFEPOINT; -JL_DLLEXPORT uint8_t jl_ir_flag_inlining(jl_array_t *data) JL_NOTSAFEPOINT; -JL_DLLEXPORT uint8_t jl_ir_flag_has_fcall(jl_array_t *data) JL_NOTSAFEPOINT; -JL_DLLEXPORT uint16_t jl_ir_inlining_cost(jl_array_t *data) JL_NOTSAFEPOINT; -JL_DLLEXPORT ssize_t jl_ir_nslots(jl_array_t *data) JL_NOTSAFEPOINT; -JL_DLLEXPORT uint8_t jl_ir_slotflag(jl_array_t *data, size_t i) JL_NOTSAFEPOINT; +JL_DLLEXPORT jl_value_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code); +JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t *metadata, jl_value_t *data); +JL_DLLEXPORT uint8_t jl_ir_flag_inferred(jl_value_t *data) JL_NOTSAFEPOINT; +JL_DLLEXPORT uint8_t jl_ir_flag_inlining(jl_value_t *data) JL_NOTSAFEPOINT; +JL_DLLEXPORT uint8_t jl_ir_flag_has_fcall(jl_value_t *data) JL_NOTSAFEPOINT; +JL_DLLEXPORT uint16_t jl_ir_inlining_cost(jl_value_t *data) JL_NOTSAFEPOINT; +JL_DLLEXPORT ssize_t jl_ir_nslots(jl_value_t *data) JL_NOTSAFEPOINT; +JL_DLLEXPORT uint8_t jl_ir_slotflag(jl_value_t *data, size_t i) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_compress_argnames(jl_array_t *syms); JL_DLLEXPORT jl_array_t *jl_uncompress_argnames(jl_value_t *syms); JL_DLLEXPORT jl_value_t *jl_uncompress_argname_n(jl_value_t *syms, size_t i); diff --git a/src/precompile_utils.c b/src/precompile_utils.c index 9e513a1cfed3a..055ec4b3330f1 100644 --- a/src/precompile_utils.c +++ b/src/precompile_utils.c @@ -186,8 +186,8 @@ static int precompile_enq_specialization_(jl_method_instance_t *mi, void *closur jl_value_t *inferred = jl_atomic_load_relaxed(&codeinst->inferred); if (inferred && inferred != jl_nothing && - jl_ir_flag_inferred((jl_array_t*)inferred) && - (jl_ir_inlining_cost((jl_array_t*)inferred) == UINT16_MAX)) { + jl_ir_flag_inferred(inferred) && + (jl_ir_inlining_cost(inferred) == UINT16_MAX)) { do_compile = 1; } else if (jl_atomic_load_relaxed(&codeinst->invoke) != NULL || jl_atomic_load_relaxed(&codeinst->precompile)) { diff --git a/src/staticdata.c b/src/staticdata.c index 3494aa93a8725..1728e0642551b 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -2162,7 +2162,7 @@ static jl_value_t *strip_codeinfo_meta(jl_method_t *m, jl_value_t *ci_, int orig int compressed = 0; if (!jl_is_code_info(ci_)) { compressed = 1; - ci = jl_uncompress_ir(m, NULL, (jl_array_t*)ci_); + ci = jl_uncompress_ir(m, NULL, (jl_value_t*)ci_); } else { ci = (jl_code_info_t*)ci_; @@ -2521,6 +2521,7 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, // This ensures that we can use the low bit of addresses for // identifying end pointers in gc's eytzinger search. write_padding(&sysimg, 4 - (sysimg.size % 4)); + write_padding(&const_data, 4 - (const_data.size % 4)); if (sysimg.size > ((uintptr_t)1 << RELOC_TAG_OFFSET)) { jl_printf( @@ -2858,9 +2859,9 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl // step 1: read section map assert(ios_pos(f) == 0 && f->bm == bm_mem); - size_t sizeof_sysimg = read_uint(f); - ios_static_buffer(&sysimg, f->buf, sizeof_sysimg + sizeof(uintptr_t)); - ios_skip(f, sizeof_sysimg); + size_t sizeof_sysdata = read_uint(f); + ios_static_buffer(&sysimg, f->buf, sizeof_sysdata + sizeof(uintptr_t)); + ios_skip(f, sizeof_sysdata); size_t sizeof_constdata = read_uint(f); // realign stream to max-alignment for data @@ -2868,6 +2869,8 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl ios_static_buffer(&const_data, f->buf + f->bpos, sizeof_constdata); ios_skip(f, sizeof_constdata); + size_t sizeof_sysimg = f->bpos; + size_t sizeof_symbols = read_uint(f); ios_seek(f, LLT_ALIGN(ios_pos(f), 8)); ios_static_buffer(&symbols, f->buf + f->bpos, sizeof_symbols); @@ -3046,7 +3049,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl } } uintptr_t otyp = jl_typetagof(obj); // the original type of the object that was written here - assert(image_base < (char*)obj && (char*)obj <= image_base + sizeof_sysimg + sizeof(uintptr_t)); + assert(image_base < (char*)obj && (char*)obj <= image_base + sizeof_sysimg); if (otyp == jl_datatype_tag << 4) { jl_datatype_t *dt = (jl_datatype_t*)obj[0], *newdt; if (jl_is_datatype(dt)) { @@ -3083,7 +3086,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl newobj = (jl_value_t*)newdt; } else { - assert(!(image_base < (char*)otyp && (char*)otyp <= image_base + sizeof_sysimg + sizeof(uintptr_t))); + assert(!(image_base < (char*)otyp && (char*)otyp <= image_base + sizeof_sysimg)); assert(jl_is_datatype_singleton((jl_datatype_t*)otyp) && "unreachable"); newobj = ((jl_datatype_t*)otyp)->instance; assert(newobj != jl_nothing); @@ -3093,7 +3096,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl *pfld = (uintptr_t)newobj | GC_OLD | GC_IN_IMAGE; else *pfld = (uintptr_t)newobj; - assert(!(image_base < (char*)newobj && (char*)newobj <= image_base + sizeof_sysimg + sizeof(uintptr_t))); + assert(!(image_base < (char*)newobj && (char*)newobj <= image_base + sizeof_sysimg)); assert(jl_typetagis(obj, otyp)); } // A few fields (reached via super) might be self-recursive. This is rare, but handle them now. @@ -3106,7 +3109,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl assert(jl_is_datatype(dt)); jl_value_t *newobj = (jl_value_t*)dt; *pfld = (uintptr_t)newobj; - assert(!(image_base < (char*)newobj && (char*)newobj <= image_base + sizeof_sysimg + sizeof(uintptr_t))); + assert(!(image_base < (char*)newobj && (char*)newobj <= image_base + sizeof_sysimg)); } arraylist_free(&delay_list); // now that all the fields of dt are assigned and unique, copy them into @@ -3173,7 +3176,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl } uintptr_t otyp = jl_typetagof(obj); // the original type of the object that was written here if (otyp == (uintptr_t)jl_method_instance_type) { - assert(image_base < (char*)obj && (char*)obj <= image_base + sizeof_sysimg + sizeof(uintptr_t)); + assert(image_base < (char*)obj && (char*)obj <= image_base + sizeof_sysimg); jl_value_t *m = obj[0]; if (jl_is_method_instance(m)) { newobj = m; // already done @@ -3190,7 +3193,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl abort(); // should be unreachable } *pfld = (uintptr_t)newobj; - assert(!(image_base < (char*)newobj && (char*)newobj <= image_base + sizeof_sysimg + sizeof(uintptr_t))); + assert(!(image_base < (char*)newobj && (char*)newobj <= image_base + sizeof_sysimg)); assert(jl_typetagis(obj, otyp)); } arraylist_free(&s.uniquing_types); @@ -3288,7 +3291,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl " reloc list: %8u\n" " gvar list: %8u\n" " fptr list: %8u\n", - (unsigned)sizeof_sysimg, + (unsigned)sizeof_sysdata, (unsigned)sizeof_constdata, (unsigned)sizeof_symbols, (unsigned)sizeof_tags, @@ -3297,7 +3300,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl (unsigned)sizeof_fptr_record); } if (cachesizes) { - cachesizes->sysdata = sizeof_sysimg; + cachesizes->sysdata = sizeof_sysdata; cachesizes->isbitsdata = sizeof_constdata; cachesizes->symboldata = sizeof_symbols; cachesizes->tagslist = sizeof_tags; @@ -3325,7 +3328,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl // Prepare for later external linkage against the sysimg // Also sets up images for protection against garbage collection arraylist_push(&jl_linkage_blobs, (void*)image_base); - arraylist_push(&jl_linkage_blobs, (void*)(image_base + sizeof_sysimg + sizeof(uintptr_t))); + arraylist_push(&jl_linkage_blobs, (void*)(image_base + sizeof_sysimg)); arraylist_push(&jl_image_relocs, (void*)relocs_base); rebuild_image_blob_tree(); diff --git a/test/compiler/contextual.jl b/test/compiler/contextual.jl index 16332555a0c3a..0e8fe27591a5e 100644 --- a/test/compiler/contextual.jl +++ b/test/compiler/contextual.jl @@ -192,12 +192,12 @@ try Baz = Base.require(Main, :Baz) @test length(Bar.mt) == 1 finally + filter!((≠)(load_path), LOAD_PATH) + filter!((≠)(depot_path), DEPOT_PATH) rm(load_path, recursive=true, force=true) try rm(depot_path, force=true, recursive=true) catch err @show err end - filter!((≠)(load_path), LOAD_PATH) - filter!((≠)(depot_path), DEPOT_PATH) end diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index e41b0cb2dca6f..7920212537608 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -684,9 +684,9 @@ begin end # https://github.com/JuliaLang/julia/issues/42246 -@test mktempdir() do dir +mktempdir() do dir cd(dir) do - code = quote + code = """ issue42246() = @noinline IOBuffer("a") let ci, rt = only(code_typed(issue42246)) @@ -699,9 +699,9 @@ end exit(1) end end - end |> string + """ cmd = `$(Base.julia_cmd()) --code-coverage=tmp.info -e $code` - success(pipeline(Cmd(cmd); stdout=stdout, stderr=stderr)) + @test success(pipeline(cmd; stdout, stderr)) end end diff --git a/test/compiler/ssair.jl b/test/compiler/ssair.jl index 38862c123f160..43f17d4ad69f2 100644 --- a/test/compiler/ssair.jl +++ b/test/compiler/ssair.jl @@ -321,8 +321,8 @@ end f_if_typecheck() = (if nothing; end; unsafe_load(Ptr{Int}(0))) @test_throws TypeError f_if_typecheck() -@test let # https://github.com/JuliaLang/julia/issues/42258 - code = quote +let # https://github.com/JuliaLang/julia/issues/42258 + code = """ function foo() a = @noinline rand(rand(0:10)) if isempty(a) @@ -335,10 +335,11 @@ f_if_typecheck() = (if nothing; end; unsafe_load(Ptr{Int}(0))) code_typed(foo; optimize=true) code_typed(Core.Compiler.setindex!, (Core.Compiler.UseRef,Core.Compiler.NewSSAValue); optimize=true) - end |> string + """ cmd = `$(Base.julia_cmd()) -g 2 -e $code` - stderr = IOBuffer() - success(pipeline(Cmd(cmd); stdout=stdout, stderr=stderr)) && isempty(String(take!(stderr))) + stderr = Base.BufferStream() + @test success(pipeline(Cmd(cmd); stdout, stderr)) + @test readchomp(stderr) == "" end @testset "code_ircode" begin From 83225c510a887b1c4096a02694c9df6b3f28500b Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Sat, 6 May 2023 07:35:04 -0400 Subject: [PATCH 697/775] Remove at_time from GC test (#49662) --- test/gc/objarray.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/gc/objarray.jl b/test/gc/objarray.jl index 805ee8dadf750..d36fcedef71a4 100644 --- a/test/gc/objarray.jl +++ b/test/gc/objarray.jl @@ -32,5 +32,5 @@ function run(maxsize) end # Memory usage 581 MB -@time run(4) +run(4) GC.gc() From b69a417e74c0562d80b3aedd0205fde86bc7a636 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Sat, 6 May 2023 16:08:50 +0200 Subject: [PATCH 698/775] Reduce `mul!` methods related to "banded" matrices (#49666) --- stdlib/LinearAlgebra/src/bidiag.jl | 15 +++---- stdlib/LinearAlgebra/src/diagonal.jl | 60 +++++++++++----------------- 2 files changed, 28 insertions(+), 47 deletions(-) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index d136bf351b134..4609ff9fbd12f 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -405,18 +405,13 @@ function ==(A::Bidiagonal, B::Bidiagonal) end end +const BandedMatrix = Union{Bidiagonal,Diagonal,Tridiagonal,SymTridiagonal} # or BiDiTriSym const BiTriSym = Union{Bidiagonal,Tridiagonal,SymTridiagonal} const BiTri = Union{Bidiagonal,Tridiagonal} -@inline mul!(C::AbstractVector, A::BiTriSym, B::AbstractVector, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) -@inline mul!(C::AbstractMatrix, A::BiTriSym, B::AbstractMatrix, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) -@inline mul!(C::AbstractMatrix, A::BiTriSym, B::Transpose{<:Any,<:AbstractVecOrMat}, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) -@inline mul!(C::AbstractMatrix, A::BiTriSym, B::Adjoint{<:Any,<:AbstractVecOrMat}, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) -@inline mul!(C::AbstractMatrix, A::BiTriSym, B::Diagonal, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) -@inline mul!(C::AbstractMatrix, A::AbstractMatrix, B::BiTriSym, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) -@inline mul!(C::AbstractMatrix, A::Adjoint{<:Any,<:AbstractVecOrMat}, B::BiTriSym, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) -@inline mul!(C::AbstractMatrix, A::Transpose{<:Any,<:AbstractVecOrMat}, B::BiTriSym, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) -@inline mul!(C::AbstractMatrix, A::BiTriSym, B::BiTriSym, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) -@inline mul!(C::AbstractMatrix, A::Diagonal, B::BiTriSym, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) +@inline mul!(C::AbstractVector, A::BandedMatrix, B::AbstractVector, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) +@inline mul!(C::AbstractMatrix, A::BandedMatrix, B::AbstractMatrix, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) +@inline mul!(C::AbstractMatrix, A::AbstractMatrix, B::BandedMatrix, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) +@inline mul!(C::AbstractMatrix, A::BandedMatrix, B::BandedMatrix, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) function check_A_mul_B!_sizes(C, A, B) mA, nA = size(A) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 466501e72cd4f..a58c8e95829ed 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -295,13 +295,13 @@ function (*)(D::Diagonal, A::AdjOrTransAbsMat) lmul!(D, Ac) end -@inline function __muldiag!(out, D::Diagonal, B, alpha, beta) - require_one_based_indexing(B) - require_one_based_indexing(out) +function __muldiag!(out, D::Diagonal, B, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} + require_one_based_indexing(out, B) + alpha, beta = _add.alpha, _add.beta if iszero(alpha) _rmul_or_fill!(out, beta) else - if iszero(beta) + if bis0 @inbounds for j in axes(B, 2) @simd for i in axes(B, 1) out[i,j] = D.diag[i] * B[i,j] * alpha @@ -317,13 +317,13 @@ end end return out end -@inline function __muldiag!(out, A, D::Diagonal, alpha, beta) - require_one_based_indexing(A) - require_one_based_indexing(out) +function __muldiag!(out, A, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} + require_one_based_indexing(out, A) + alpha, beta = _add.alpha, _add.beta if iszero(alpha) _rmul_or_fill!(out, beta) else - if iszero(beta) + if bis0 @inbounds for j in axes(A, 2) dja = D.diag[j] * alpha @simd for i in axes(A, 1) @@ -341,13 +341,14 @@ end end return out end -@inline function __muldiag!(out::Diagonal, D1::Diagonal, D2::Diagonal, alpha, beta) +function __muldiag!(out::Diagonal, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} d1 = D1.diag d2 = D2.diag + alpha, beta = _add.alpha, _add.beta if iszero(alpha) _rmul_or_fill!(out.diag, beta) else - if iszero(beta) + if bis0 @inbounds @simd for i in eachindex(out.diag) out.diag[i] = d1[i] * d2[i] * alpha end @@ -359,8 +360,9 @@ end end return out end -@inline function __muldiag!(out, D1::Diagonal, D2::Diagonal, alpha, beta) +function __muldiag!(out, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} require_one_based_indexing(out) + alpha, beta = _add.alpha, _add.beta mA = size(D1, 1) d1 = D1.diag d2 = D2.diag @@ -373,9 +375,9 @@ end return out end -@inline function _muldiag!(out, A, B, alpha, beta) +function _mul!(out, A, B, _add) _muldiag_size_check(out, A, B) - __muldiag!(out, A, B, alpha, beta) + __muldiag!(out, A, B, _add) return out end @@ -391,24 +393,6 @@ function (*)(Da::Diagonal, Db::Diagonal, Dc::Diagonal) return Diagonal(Da.diag .* Db.diag .* Dc.diag) end -# Get ambiguous method if try to unify AbstractVector/AbstractMatrix here using AbstractVecOrMat -@inline mul!(out::AbstractVector, D::Diagonal, V::AbstractVector, alpha::Number, beta::Number) = - _muldiag!(out, D, V, alpha, beta) -@inline mul!(out::AbstractMatrix, D::Diagonal, B::AbstractMatrix, alpha::Number, beta::Number) = - _muldiag!(out, D, B, alpha, beta) -@inline mul!(out::AbstractMatrix, D::Diagonal, B::AdjOrTrans{<:Any,<:AbstractVecOrMat}, - alpha::Number, beta::Number) = _muldiag!(out, D, B, alpha, beta) - -@inline mul!(out::AbstractMatrix, A::AbstractMatrix, D::Diagonal, alpha::Number, beta::Number) = - _muldiag!(out, A, D, alpha, beta) -@inline mul!(out::AbstractMatrix, A::AdjOrTrans{<:Any,<:AbstractVecOrMat}, D::Diagonal, - alpha::Number, beta::Number) = _muldiag!(out, A, D, alpha, beta) -@inline mul!(C::Diagonal, Da::Diagonal, Db::Diagonal, alpha::Number, beta::Number) = - _muldiag!(C, Da, Db, alpha, beta) - -mul!(C::AbstractMatrix, Da::Diagonal, Db::Diagonal, alpha::Number, beta::Number) = - _muldiag!(C, Da, Db, alpha, beta) - /(A::AbstractVecOrMat, D::Diagonal) = _rdiv!(similar(A, _init_eltype(/, eltype(A), eltype(D))), A, D) /(A::HermOrSym, D::Diagonal) = _rdiv!(similar(A, _init_eltype(/, eltype(A), eltype(D)), size(A)), A, D) rdiv!(A::AbstractVecOrMat, D::Diagonal) = @inline _rdiv!(A, A, D) @@ -576,19 +560,21 @@ for Tri in (:UpperTriangular, :LowerTriangular) # 3-arg mul!: invoke 5-arg mul! rather than lmul! @eval mul!(C::$Tri, A::Union{$Tri,$UTri}, D::Diagonal) = mul!(C, A, D, true, false) # 5-arg mul! - @eval @inline mul!(C::$Tri, D::Diagonal, A::$Tri, α::Number, β::Number) = $Tri(mul!(C.data, D, A.data, α, β)) - @eval @inline function mul!(C::$Tri, D::Diagonal, A::$UTri, α::Number, β::Number) + @eval _mul!(C::$Tri, D::Diagonal, A::$Tri, _add) = $Tri(mul!(C.data, D, A.data, _add.alpha, _add.beta)) + @eval function _mul!(C::$Tri, D::Diagonal, A::$UTri, _add) + α, β = _add.alpha, _add.beta iszero(α) && return _rmul_or_fill!(C, β) diag′ = iszero(β) ? nothing : diag(C) data = mul!(C.data, D, A.data, α, β) - $Tri(_setdiag!(data, MulAddMul(α, β), D.diag, diag′)) + $Tri(_setdiag!(data, _add, D.diag, diag′)) end - @eval @inline mul!(C::$Tri, A::$Tri, D::Diagonal, α::Number, β::Number) = $Tri(mul!(C.data, A.data, D, α, β)) - @eval @inline function mul!(C::$Tri, A::$UTri, D::Diagonal, α::Number, β::Number) + @eval _mul!(C::$Tri, A::$Tri, D::Diagonal, _add) = $Tri(mul!(C.data, A.data, D, _add.alpha, _add.beta)) + @eval function _mul!(C::$Tri, A::$UTri, D::Diagonal, _add) + α, β = _add.alpha, _add.beta iszero(α) && return _rmul_or_fill!(C, β) diag′ = iszero(β) ? nothing : diag(C) data = mul!(C.data, A.data, D, α, β) - $Tri(_setdiag!(data, MulAddMul(α, β), D.diag, diag′)) + $Tri(_setdiag!(data, _add, D.diag, diag′)) end end From bdb3ded0a264c320c928c16e218590d467156e8c Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Sun, 7 May 2023 22:11:54 +0200 Subject: [PATCH 699/775] add reference to online tracy profile viewer (#49626) * Add docs for TRACY_CALLSTACKS and Tracy Web Viewer * add reference to online tracy profile viewer * Add `WITH_TRACY_CALLSTACKS` build option This change enables rudimentary call-stack support in Tracy. By default, call stack sampling is turned on for all zones along with random periodic sampling by the system. In the future, we'll probably want to take a more fine-grained approach to judiciously collect stack traces only where there are important, but for now gives users a flag to play with to uncover more information. --------- Co-authored-by: Cody Tapscott --- Make.inc | 6 ++++++ deps/libtracyclient.mk | 7 ++++++- doc/src/devdocs/external_profilers.md | 25 +++++++++++++++++++++---- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/Make.inc b/Make.inc index 07547f1d1aca5..4d564f057a3da 100644 --- a/Make.inc +++ b/Make.inc @@ -94,6 +94,7 @@ WITH_ITTAPI := 0 # Enable Tracy support WITH_TRACY := 0 +WITH_TRACY_CALLSTACKS := 0 # Enable Timing Counts support WITH_TIMING_COUNTS := 0 @@ -747,6 +748,11 @@ JCXXFLAGS += -DUSE_TRACY -DTRACY_ENABLE -DTRACY_FIBERS JCFLAGS += -DUSE_TRACY -DTRACY_ENABLE -DTRACY_FIBERS LIBTRACYCLIENT:=-lTracyClient endif +ifeq ($(WITH_TRACY_CALLSTACKS), 1) +JCXXFLAGS += -DTRACY_CALLSTACK=32 +JCFLAGS += -DTRACY_CALLSTACK=32 +LIBTRACYCLIENT:=-lTracyClient +endif ifeq ($(WITH_TIMING_COUNTS), 1) JCXXFLAGS += -DUSE_TIMING_COUNTS diff --git a/deps/libtracyclient.mk b/deps/libtracyclient.mk index 19c08f774aa0c..e4116b8f2acb4 100644 --- a/deps/libtracyclient.mk +++ b/deps/libtracyclient.mk @@ -10,13 +10,18 @@ LIBTRACYCLIENT_SRCCACHE := $(SRCCACHE)/$(LIBTRACYCLIENT_SRC_DIR) LIBTRACYCLIENT_CMAKE := LIBTRACYCLIENT_CMAKE += -DBUILD_SHARED_LIBS=ON LIBTRACYCLIENT_CMAKE += -DTRACY_FIBERS=ON -LIBTRACYCLIENT_CMAKE += -DTRACY_NO_SAMPLING=ON LIBTRACYCLIENT_CMAKE += -DTRACY_ONLY_LOCALHOST=ON LIBTRACYCLIENT_CMAKE += -DTRACY_NO_CODE_TRANSFER=ON LIBTRACYCLIENT_CMAKE += -DTRACY_NO_FRAME_IMAGE=ON LIBTRACYCLIENT_CMAKE += -DTRACY_NO_CRASH_HANDLER=ON LIBTRACYCLIENT_CMAKE += -DTRACY_ON_DEMAND=ON +ifeq ($(WITH_TRACY_CALLSTACKS),1) +LIBTRACYCLIENT_CMAKE += -DTRACY_CALLSTACK=32 +else +LIBTRACYCLIENT_CMAKE += -DTRACY_NO_SAMPLING=ON +endif + $(LIBTRACYCLIENT_BUILDDIR)/cmake-patch-applied: $(LIBTRACYCLIENT_BUILDDIR)/source-extracted ifneq ($(OS),WINNT) echo "target_compile_definitions(TracyClient PUBLIC __STDC_FORMAT_MACROS)" >> $(LIBTRACYCLIENT_BUILDDIR)/CMakeLists.txt diff --git a/doc/src/devdocs/external_profilers.md b/doc/src/devdocs/external_profilers.md index bfc0d6538f111..239854a7d6800 100644 --- a/doc/src/devdocs/external_profilers.md +++ b/doc/src/devdocs/external_profilers.md @@ -4,7 +4,7 @@ Julia provides explicit support for some external tracing profilers, enabling yo The currently supported profilers are: - [Tracy](https://github.com/wolfpld/tracy) -- [ITTAPI (VTune)](https://github.com/intel/ittapi) +- [Intel VTune (ITTAPI)](https://github.com/intel/ittapi) ### Adding New Zones @@ -59,10 +59,27 @@ The various `jl_timing_show_*` and `jl_timing_printf` functions can be used to a The `TracyCZoneColor` function can be used to set the color of a certain zone. Search through the codebase to see how it is used. -### Hosting Tracy Traces Online +### Viewing Tracy files in your browser -*This section is yet to be written.* +Visit https://topolarity.github.io/trace-viewer/ for an (experimental) web viewer for Tracy traces. + +You can open a local `.tracy` file or provide a URL from the web (e.g. a file in a Github repo). If you load a trace file from the web, you can also share the page URL directly with others, enabling them to view the same trace. + +### Enabling stack trace samples + +To enable call stack sampling in Tracy, build Julia with these options in your `Make.user` file: +``` +WITH_TRACY := 1 +WITH_TRACY_CALLSTACKS := 1 +USE_BINARYBUILDER_LIBTRACYCLIENT := 0 +``` + +You may also need to run `make -C deps clean-libtracyclient` to force a re-build of Tracy. + +This feature has a significant impact on trace size and profiling overhead, so it is recommended to leave call stack sampling off when possible, especially if you intend to share your trace files online. + +Note that the Julia JIT runtime does not yet have integration for Tracy's symbolification, so Julia functions will typically be unknown in these stack traces. -## ITTAPI Profiler +## Intel VTune (ITTAPI) Profiler *This section is yet to be written.* From 09adbe3a7c204dc79497791a4aa81fc6ae9b6748 Mon Sep 17 00:00:00 2001 From: Sukera <11753998+Seelengrab@users.noreply.github.com> Date: Sun, 7 May 2023 22:52:02 +0200 Subject: [PATCH 700/775] Remove mention of TwicePrecision from the docs (#49665) --- base/range.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/base/range.jl b/base/range.jl index fedc0f4acc310..f7dc35703a196 100644 --- a/base/range.jl +++ b/base/range.jl @@ -478,8 +478,6 @@ value `r[1]`, but alternatively you can supply it as the value of `r[offset]` for some other index `1 <= offset <= len`. The syntax `a:b` or `a:b:c`, where any of `a`, `b`, or `c` are floating-point numbers, creates a `StepRangeLen`. -In conjunction with `TwicePrecision` this can be used to implement ranges that -are free of roundoff error. !!! compat "Julia 1.7" The 4th type parameter `L` requires at least Julia 1.7. From ed12565a631fba0f83bd56f9c3f823cdbfcb7668 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Thu, 27 Apr 2023 05:12:52 -0400 Subject: [PATCH 701/775] Count invalidations --- src/gf.c | 1 + src/timing.c | 17 +++++++++++++++++ src/timing.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/src/gf.c b/src/gf.c index 00a51f582a597..5e5afbdaf7460 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1642,6 +1642,7 @@ static void do_nothing_with_codeinst(jl_code_instance_t *ci) {} // recursively invalidate cached methods that had an edge to a replaced method static void invalidate_method_instance(void (*f)(jl_code_instance_t*), jl_method_instance_t *replaced, size_t max_world, int depth) { + jl_timing_counter_inc(JL_TIMING_COUNTER_Invalidations, 1); if (_jl_debug_method_invalidation) { jl_value_t *boxeddepth = NULL; JL_GC_PUSH1(&boxeddepth); diff --git a/src/timing.c b/src/timing.c index 2e0dba7c025bc..a52bd6f8396af 100644 --- a/src/timing.c +++ b/src/timing.c @@ -48,6 +48,8 @@ const char *jl_timing_names[(int)JL_TIMING_LAST] = #undef X }; +JL_DLLEXPORT jl_timing_counter_t jl_timing_counters[JL_TIMING_COUNTER_LAST]; + #ifdef USE_ITTAPI JL_DLLEXPORT __itt_event jl_timing_ittapi_events[(int)JL_TIMING_EVENT_LAST]; #endif @@ -60,11 +62,22 @@ void jl_print_timings(void) root_time -= jl_timing_counts[i]; } jl_timing_counts[0] = root_time; + fprintf(stderr, "\nJULIA TIMINGS\n"); for (int i = 0; i < JL_TIMING_LAST; i++) { if (jl_timing_counts[i] != 0) fprintf(stderr, "%-25s : %5.2f %% %" PRIu64 "\n", jl_timing_names[i], 100 * (((double)jl_timing_counts[i]) / total_time), jl_timing_counts[i]); } + + fprintf(stderr, "\nJULIA COUNTERS\n"); + uint64_t val = 0; +#ifdef USE_TIMING_COUNTS +#define X(name) val = jl_atomic_load_relaxed(&jl_timing_counters[(int)JL_TIMING_COUNTER_##name].basic_counter); \ + if (val != 0) \ + fprintf(stderr, "%-25s : %" PRIu64 "\n", #name, val); + JL_TIMING_COUNTERS +#undef X +#endif } void jl_init_timing(void) @@ -78,6 +91,10 @@ void jl_init_timing(void) #ifdef USE_ITTAPI #define X(name) jl_timing_ittapi_events[i++] = __itt_event_create(#name, strlen(#name)); JL_TIMING_EVENTS +#undef X + i = 0; +#define X(name) jl_timing_counters[i++].ittapi_counter = __itt_counter_create(#name, "julia.runtime"); + JL_TIMING_COUNTERS #undef X #endif } diff --git a/src/timing.h b/src/timing.h index 5d4e95c62bb67..88c26c512f62d 100644 --- a/src/timing.h +++ b/src/timing.h @@ -78,6 +78,9 @@ extern uint32_t jl_timing_print_limit; #define jl_timing_block_exit_task(ct, ptls) ((jl_timing_block_t *)NULL) #define jl_pop_timing_block(blk) +#define jl_timing_counter_inc(counter, value) +#define jl_timing_counter_dec(counter, value) + #define jl_profile_lock_init(lock, name) #define jl_profile_lock_start_wait(lock) #define jl_profile_lock_acquired(lock) @@ -181,6 +184,10 @@ void jl_timing_puts(jl_timing_block_t *cur_block, const char *str); X(NATIVE_Create) \ +#define JL_TIMING_COUNTERS \ + X(Invalidations) \ + + enum jl_timing_owners { #define X(name) JL_TIMING_ ## name, JL_TIMING_OWNERS @@ -195,6 +202,13 @@ enum jl_timing_events { JL_TIMING_EVENT_LAST }; +enum jl_timing_counter_types { +#define X(name) JL_TIMING_COUNTER_ ## name, + JL_TIMING_COUNTERS +#undef X + JL_TIMING_COUNTER_LAST +}; + /** * Timing back-ends differ in terms of whether they support nested * and asynchronous events. @@ -404,6 +418,44 @@ struct jl_timing_suspend_cpp_t { _jl_timing_suspend_ctor(&__timing_suspend, #subsystem, ct) #endif +// Counting +#ifdef USE_ITTAPI +#define _ITTAPI_COUNTER_MEMBER __itt_counter ittapi_counter; +#else +#define _ITTAPI_COUNTER_MEMBER +#endif + +#ifdef USE_TIMING_COUNTS +#define _COUNTS_MEMBER _Atomic(uint64_t) basic_counter; +#else +#define _COUNTS_MEMBER +#endif + +typedef struct { + _ITTAPI_COUNTER_MEMBER + _COUNTS_MEMBER +} jl_timing_counter_t; + +JL_DLLEXPORT extern jl_timing_counter_t jl_timing_counters[JL_TIMING_COUNTER_LAST]; + +static inline void jl_timing_counter_inc(int counter, uint64_t val) JL_NOTSAFEPOINT { +#ifdef USE_ITTAPI + __itt_counter_inc_delta(jl_timing_counters[counter].ittapi_counter, val); +#endif +#ifdef USE_TIMING_COUNTS + jl_atomic_fetch_add_relaxed(&jl_timing_counters[counter].basic_counter, val); +#endif +} + +static inline void jl_timing_counter_dec(int counter, uint64_t val) JL_NOTSAFEPOINT { +#ifdef USE_ITTAPI + __itt_counter_dec_delta(jl_timing_counters[counter].ittapi_counter, val); +#endif +#ifdef USE_TIMING_COUNTS + jl_atomic_fetch_add_relaxed(&jl_timing_counters[counter].basic_counter, -(int64_t)val); +#endif +} + // Locking profiling static inline void jl_profile_lock_init(jl_mutex_t *lock, const char *name) { #ifdef USE_ITTAPI From 0ee05711702a8fbbb4a1454a2659f63fab9ae080 Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Wed, 3 May 2023 15:58:41 +0200 Subject: [PATCH 702/775] add tracy support for inc/dec plots --- src/timing.c | 7 +++++++ src/timing.h | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/timing.c b/src/timing.c index a52bd6f8396af..ff1c9f7082542 100644 --- a/src/timing.c +++ b/src/timing.c @@ -97,6 +97,13 @@ void jl_init_timing(void) JL_TIMING_COUNTERS #undef X #endif +#ifdef USE_TRACY + i = 0; +#define X(counter_name) jl_timing_counters[i].tracy_counter = (jl_tracy_counter_t){0, #counter_name}; \ + TracyCPlotConfig(jl_timing_counters[i++].tracy_counter.name, TracyPlotFormatNumber, /* rectilinear */ 1, /* fill */ 1, /* color */ 0); + JL_TIMING_COUNTERS +#undef X +#endif } void jl_destroy_timing(void) diff --git a/src/timing.h b/src/timing.h index 88c26c512f62d..6f28b201673d9 100644 --- a/src/timing.h +++ b/src/timing.h @@ -16,6 +16,13 @@ static inline const char *gnu_basename(const char *path) return base ? base+1 : path; } +#ifdef USE_TRACY +typedef struct { + _Atomic(int64_t) val; + char* name; +} jl_tracy_counter_t; +#endif + #ifdef __cplusplus extern "C" { #endif @@ -425,6 +432,12 @@ struct jl_timing_suspend_cpp_t { #define _ITTAPI_COUNTER_MEMBER #endif +#ifdef USE_TRACY +# define _TRACY_COUNTER_MEMBER jl_tracy_counter_t tracy_counter; +# else +# define _TRACY_COUNTER_MEMBER +#endif + #ifdef USE_TIMING_COUNTS #define _COUNTS_MEMBER _Atomic(uint64_t) basic_counter; #else @@ -433,6 +446,7 @@ struct jl_timing_suspend_cpp_t { typedef struct { _ITTAPI_COUNTER_MEMBER + _TRACY_COUNTER_MEMBER _COUNTS_MEMBER } jl_timing_counter_t; @@ -442,6 +456,11 @@ static inline void jl_timing_counter_inc(int counter, uint64_t val) JL_NOTSAFEPO #ifdef USE_ITTAPI __itt_counter_inc_delta(jl_timing_counters[counter].ittapi_counter, val); #endif +#ifdef USE_TRACY + jl_tracy_counter_t *tracy_counter = &jl_timing_counters[counter].tracy_counter; + uint64_t oldval = jl_atomic_fetch_add_relaxed(&tracy_counter->val, val); + TracyCPlotI(tracy_counter->name, oldval + val); +#endif #ifdef USE_TIMING_COUNTS jl_atomic_fetch_add_relaxed(&jl_timing_counters[counter].basic_counter, val); #endif @@ -451,6 +470,11 @@ static inline void jl_timing_counter_dec(int counter, uint64_t val) JL_NOTSAFEPO #ifdef USE_ITTAPI __itt_counter_dec_delta(jl_timing_counters[counter].ittapi_counter, val); #endif +#ifdef USE_TRACY + jl_tracy_counter_t *tracy_counter = &jl_timing_counters[counter].tracy_counter; + uint64_t oldval = jl_atomic_fetch_add_relaxed(&tracy_counter->val, -val); + TracyCPlotI(tracy_counter->name, oldval - val); +#endif #ifdef USE_TIMING_COUNTS jl_atomic_fetch_add_relaxed(&jl_timing_counters[counter].basic_counter, -(int64_t)val); #endif From 1d089b79990abba7801f6e51d00c1ed4ce66e855 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Wed, 3 May 2023 23:42:33 -0400 Subject: [PATCH 703/775] Add heap size stat --- src/gc.c | 3 +++ src/timing.h | 1 + 2 files changed, 4 insertions(+) diff --git a/src/gc.c b/src/gc.c index 60b110826ee80..fd0648e722f8e 100644 --- a/src/gc.c +++ b/src/gc.c @@ -1193,6 +1193,7 @@ void jl_gc_reset_alloc_count(void) JL_NOTSAFEPOINT { combine_thread_gc_counts(&gc_num); live_bytes += (gc_num.deferred_alloc + gc_num.allocd); + jl_timing_counter_inc(JL_TIMING_COUNTER_HeapSize, gc_num.deferred_alloc + gc_num.allocd); gc_num.allocd = 0; gc_num.deferred_alloc = 0; reset_thread_gc_counts(); @@ -3343,6 +3344,7 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) last_live_bytes = live_bytes; live_bytes += -gc_num.freed + gc_num.allocd; + jl_timing_counter_inc(JL_TIMING_COUNTER_HeapSize, -gc_num.freed + gc_num.allocd); if (collection == JL_GC_AUTO) { //If we aren't freeing enough or are seeing lots and lots of pointers let it increase faster @@ -3782,6 +3784,7 @@ static void *gc_managed_realloc_(jl_ptls_t ptls, void *d, size_t sz, size_t olds if (jl_astaggedvalue(owner)->bits.gc == GC_OLD_MARKED) { ptls->gc_cache.perm_scanned_bytes += allocsz - oldsz; live_bytes += allocsz - oldsz; + jl_timing_counter_inc(JL_TIMING_COUNTER_HeapSize, allocsz - oldsz); } else if (allocsz < oldsz) jl_atomic_store_relaxed(&ptls->gc_num.freed, diff --git a/src/timing.h b/src/timing.h index 6f28b201673d9..defae4fa2dc25 100644 --- a/src/timing.h +++ b/src/timing.h @@ -193,6 +193,7 @@ void jl_timing_puts(jl_timing_block_t *cur_block, const char *str); #define JL_TIMING_COUNTERS \ X(Invalidations) \ + X(HeapSize) \ enum jl_timing_owners { From 7a3cbd011c9b8a8117bf1cbdd0677e421147c55c Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Wed, 3 May 2023 23:50:20 -0400 Subject: [PATCH 704/775] Alter USE_TIMING_COUNTS implementation --- src/timing.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/timing.c b/src/timing.c index ff1c9f7082542..a43944c1c1179 100644 --- a/src/timing.c +++ b/src/timing.c @@ -56,6 +56,7 @@ JL_DLLEXPORT __itt_event jl_timing_ittapi_events[(int)JL_TIMING_EVENT_LAST]; void jl_print_timings(void) { +#ifdef USE_TIMING_COUNTS uint64_t total_time = cycleclock() - t0; uint64_t root_time = total_time; for (int i = 0; i < JL_TIMING_LAST; i++) { @@ -70,11 +71,12 @@ void jl_print_timings(void) } fprintf(stderr, "\nJULIA COUNTERS\n"); - uint64_t val = 0; -#ifdef USE_TIMING_COUNTS -#define X(name) val = jl_atomic_load_relaxed(&jl_timing_counters[(int)JL_TIMING_COUNTER_##name].basic_counter); \ - if (val != 0) \ - fprintf(stderr, "%-25s : %" PRIu64 "\n", #name, val); +#define X(name) do { \ + int64_t val = (int64_t) jl_atomic_load_relaxed(&jl_timing_counters[(int)JL_TIMING_COUNTER_##name].basic_counter); \ + if (val != 0) \ + fprintf(stderr, "%-25s : %" PRIi64 "\n", #name, val); \ + } while (0); + JL_TIMING_COUNTERS #undef X #endif From 0e8adb7f51d2819b14dfb0ffe3efe32662a5d40c Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Sun, 7 May 2023 22:25:51 -0400 Subject: [PATCH 705/775] Move heap plot config to timing.c, remove old heap plot code --- src/gc.c | 11 ----------- src/timing.c | 3 +++ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/gc.c b/src/gc.c index fd0648e722f8e..64c33b7923048 100644 --- a/src/gc.c +++ b/src/gc.c @@ -3095,10 +3095,6 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) { combine_thread_gc_counts(&gc_num); -#ifdef USE_TRACY - TracyCPlot("Heap size", live_bytes + gc_num.allocd); -#endif - jl_gc_markqueue_t *mq = &ptls->mark_queue; uint64_t gc_start_time = jl_hrtime(); @@ -3488,10 +3484,6 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) SetLastError(last_error); #endif errno = last_errno; - -#ifdef USE_TRACY - TracyCPlot("Heap size", jl_gc_live_bytes()); -#endif } void gc_mark_queue_all_roots(jl_ptls_t ptls, jl_gc_markqueue_t *mq) @@ -3595,9 +3587,6 @@ void jl_gc_init(void) if (jl_options.heap_size_hint) jl_gc_set_max_memory(jl_options.heap_size_hint); -#ifdef USE_TRACY - TracyCPlotConfig("Heap size", TracyPlotFormatMemory, /* rectilinear */ 0, /* fill */ 1, /* color */ 0); -#endif t_start = jl_hrtime(); } diff --git a/src/timing.c b/src/timing.c index a43944c1c1179..8a6b3fdf94afe 100644 --- a/src/timing.c +++ b/src/timing.c @@ -105,6 +105,9 @@ void jl_init_timing(void) TracyCPlotConfig(jl_timing_counters[i++].tracy_counter.name, TracyPlotFormatNumber, /* rectilinear */ 1, /* fill */ 1, /* color */ 0); JL_TIMING_COUNTERS #undef X + // We reference these by enum indexing and then asking for the name, since that allows the compiler + // to catch name mismatches. + TracyCPlotConfig(jl_timing_counters[JL_TIMING_COUNTER_HeapSize].tracy_counter.name, TracyPlotFormatMemory, /* rectilinear */ 0, /* fill */ 1, /* color */ 0); #endif } From 4e782bf91a798991db32f4ef633622ae97a095ff Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Mon, 8 May 2023 00:21:01 -0700 Subject: [PATCH 706/775] Bump LBT to v5.8.0 (#49658) Includes: https://github.com/JuliaLinearAlgebra/libblastrampoline/commit/81316155d4838392e8462a92bcac3eebe9acd0c7 --- deps/blastrampoline.version | 6 +- deps/checksums/blastrampoline | 68 +++++++++++------------ stdlib/libblastrampoline_jll/Project.toml | 2 +- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/deps/blastrampoline.version b/deps/blastrampoline.version index faf2ecd0229c1..616300377e3e6 100644 --- a/deps/blastrampoline.version +++ b/deps/blastrampoline.version @@ -2,6 +2,6 @@ BLASTRAMPOLINE_JLL_NAME := libblastrampoline ## source build -BLASTRAMPOLINE_VER := 5.7.0 -BLASTRAMPOLINE_BRANCH=v5.7.0 -BLASTRAMPOLINE_SHA1=2272604bfb10b9e8a3ae5f1a4569899b99251a65 +BLASTRAMPOLINE_VER := 5.8.0 +BLASTRAMPOLINE_BRANCH=v5.8.0 +BLASTRAMPOLINE_SHA1=81316155d4838392e8462a92bcac3eebe9acd0c7 diff --git a/deps/checksums/blastrampoline b/deps/checksums/blastrampoline index d4c5879fc9403..011b0f6e4704d 100644 --- a/deps/checksums/blastrampoline +++ b/deps/checksums/blastrampoline @@ -1,34 +1,34 @@ -blastrampoline-2272604bfb10b9e8a3ae5f1a4569899b99251a65.tar.gz/md5/bd7fb047c1b7d4826e24011df7e74afe -blastrampoline-2272604bfb10b9e8a3ae5f1a4569899b99251a65.tar.gz/sha512/1fc7506feaf2bcfaf898b2966843225f7190e5f36274f9cab1daa097ff9c2208ed94a685fc613653bc489d341d6b8c8b158e7a809f4d821e0a76da4308c698a5 -libblastrampoline.v5.7.0+0.aarch64-apple-darwin.tar.gz/md5/a28837b9838fef2b3831de3278ec7949 -libblastrampoline.v5.7.0+0.aarch64-apple-darwin.tar.gz/sha512/111ac2fe5f8f8102f2f7c9e9e6aa1d1a12d2db941238c949ff8e64b30335e8b2f6ecce0d5f577879c231eb839c06e259302b709f3d34e94a97047bfa984222f6 -libblastrampoline.v5.7.0+0.aarch64-linux-gnu.tar.gz/md5/3792c0a4443c0e2ebe88bea180a3beb1 -libblastrampoline.v5.7.0+0.aarch64-linux-gnu.tar.gz/sha512/d5ce0f9bb47f80d04e13bf90e48d6ab942cf7fd79b582f1496a83a1eca0db29e01315efa52bcb1099466a9037926a7a2cf3135395ac1322544cd7150b9687d7b -libblastrampoline.v5.7.0+0.aarch64-linux-musl.tar.gz/md5/45622c514e744282f996bacc26ca231b -libblastrampoline.v5.7.0+0.aarch64-linux-musl.tar.gz/sha512/790f7cf4d5f11be246c617694a7d617435627229f52c52ee49037c3650707ac1c0d8631b713d5b32ac76f97b19b18eacc1645fd0aecc296167439bfbb0514b5c -libblastrampoline.v5.7.0+0.armv6l-linux-gnueabihf.tar.gz/md5/f945fe9ee8472db8fe27409b7c028a57 -libblastrampoline.v5.7.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/96876374813acc9ae249ef3c7140e1e0eac9259a0b5c567e11843a17a9d5db4fe759e873d0e2c1dd944e9f9104b885a09cd8977634eaa7c24e580d391f23b75f -libblastrampoline.v5.7.0+0.armv6l-linux-musleabihf.tar.gz/md5/902b8ae755be3087002969377b0332d8 -libblastrampoline.v5.7.0+0.armv6l-linux-musleabihf.tar.gz/sha512/bda692349136d1b540e00458ba9ed689e762e9acdc60d0cc59203cefcdcc59611f3883f5050bf9186019003be726f3d798138dcf5929a64a4d1fab314c84e7a4 -libblastrampoline.v5.7.0+0.armv7l-linux-gnueabihf.tar.gz/md5/95058aaed39a095b6f50b04b335b1ff6 -libblastrampoline.v5.7.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/bb963ac3d9fd57e656e33177c01ae6cd0cfbe63826f79cd3ae04c38641c5a82fe9cae29a38f1a725ca11d93dd143aeb6a5213f26ceedb622f9ebb5c156b9ceac -libblastrampoline.v5.7.0+0.armv7l-linux-musleabihf.tar.gz/md5/f66178bcb8e159e7b022c54a917cd895 -libblastrampoline.v5.7.0+0.armv7l-linux-musleabihf.tar.gz/sha512/c92b0fe3e3f486d2f559d5f34fdcf59bf4db764b28dc1146a2559f236de48057832ba1b766d69bd5ffe91f6b0d13238e6ee3eb70d4508947b3235672cba60d6f -libblastrampoline.v5.7.0+0.i686-linux-gnu.tar.gz/md5/33bb177a7928a80ef888b51ff39eb724 -libblastrampoline.v5.7.0+0.i686-linux-gnu.tar.gz/sha512/c63c1511213ca89a540db2b7e8cbfae52e6433292b59a7f652ed28f2ad01603ece480ab3c3bb8860598def5c3a994dd0132fc0bf7f070efd458e7e9bbebb1118 -libblastrampoline.v5.7.0+0.i686-linux-musl.tar.gz/md5/65472af1f7b69695470c65eea92e237d -libblastrampoline.v5.7.0+0.i686-linux-musl.tar.gz/sha512/6e16059c68e19447aa6b37854ce7200ae7c7ecd4f016c5fcd120d4ad960377bd4b86ace7470277b785b61d68a915b5476568d876ea510b50a8bb7146a33ee431 -libblastrampoline.v5.7.0+0.i686-w64-mingw32.tar.gz/md5/0a945591dda93017836cdcc87095863a -libblastrampoline.v5.7.0+0.i686-w64-mingw32.tar.gz/sha512/26376cefbe8891907a2f6ee506edc9cb95289df52eaad3ac39135eade5486879da5733019d437dcfba6c286007a29a43b2aabdcc436db893910f6b2730517f12 -libblastrampoline.v5.7.0+0.powerpc64le-linux-gnu.tar.gz/md5/2d29aff294807c0b857adee62dbce7f5 -libblastrampoline.v5.7.0+0.powerpc64le-linux-gnu.tar.gz/sha512/414cc2971bbc7e635b7d1d68aa545ff23568dd7722b8fdd990439c0c55e4dc63420afaf191633fbcf54205fc11edb01fa7d5d4a712b8951775dcdd500f135231 -libblastrampoline.v5.7.0+0.x86_64-apple-darwin.tar.gz/md5/21beb51d448bd22e4608a16b3f4fde05 -libblastrampoline.v5.7.0+0.x86_64-apple-darwin.tar.gz/sha512/620ba64d93ef416e483f813617aa313957282d8361f920b5444702fa911ff0051d1f8a8814b5fa0b082fd4dc77d96cb8b763937c786959bbc97cbb6131617152 -libblastrampoline.v5.7.0+0.x86_64-linux-gnu.tar.gz/md5/43a9eb58e79131d9a8594a42c5b15c5f -libblastrampoline.v5.7.0+0.x86_64-linux-gnu.tar.gz/sha512/5b8dddd742a7742eef14025a859251d605b34a61de3e07ff696c168a88462602c35c5b3680da072e28a8bedc89df5b5ae622be61a5b0203000f9439558d423d9 -libblastrampoline.v5.7.0+0.x86_64-linux-musl.tar.gz/md5/5790c157871d03fcb8c14063303bfcf8 -libblastrampoline.v5.7.0+0.x86_64-linux-musl.tar.gz/sha512/8f82de757b66559cdcd8127946b50c1c5b479149966d53881bdae7c7b536a7c79a1b331d04425aeb57d47fabeb2946985edab23cc94e049240e1925320f03795 -libblastrampoline.v5.7.0+0.x86_64-unknown-freebsd.tar.gz/md5/bb676e6bc64ed1be85f95443e4366eca -libblastrampoline.v5.7.0+0.x86_64-unknown-freebsd.tar.gz/sha512/77ba2fb295f2765cd64ca7d00ee92230c467e056b1441eea63470dcb3491481174956b7058950a7fc2d7dad4b245283dca907b2dea96c8fe1b0e27e4375c0be4 -libblastrampoline.v5.7.0+0.x86_64-w64-mingw32.tar.gz/md5/58ab9512505a6b3eb2f7f2f2bf9a55cf -libblastrampoline.v5.7.0+0.x86_64-w64-mingw32.tar.gz/sha512/a58a7be8ef3ea958591c1cc9d9192efa08bb2aeb0de4cbd1e3f23ea57aa2b8f6048ab4db6bce4f9724a9f81f0e0356e66b884763ead5b309cb38bab9ea475e7f +blastrampoline-81316155d4838392e8462a92bcac3eebe9acd0c7.tar.gz/md5/0478361eac783b99002b1ad985182f05 +blastrampoline-81316155d4838392e8462a92bcac3eebe9acd0c7.tar.gz/sha512/2489ce5770a9861889a2d07e61440ba4f233a92efd4a3544747f83320e0e7a229a8fe01553d99f5f1d98713316f2506daf0adb7d024a46e32b3de1bb2966d637 +libblastrampoline.v5.8.0+0.aarch64-apple-darwin.tar.gz/md5/a28837b9838fef2b3831de3278ec7949 +libblastrampoline.v5.8.0+0.aarch64-apple-darwin.tar.gz/sha512/111ac2fe5f8f8102f2f7c9e9e6aa1d1a12d2db941238c949ff8e64b30335e8b2f6ecce0d5f577879c231eb839c06e259302b709f3d34e94a97047bfa984222f6 +libblastrampoline.v5.8.0+0.aarch64-linux-gnu.tar.gz/md5/9e781a026e03118df81347fb90f10d45 +libblastrampoline.v5.8.0+0.aarch64-linux-gnu.tar.gz/sha512/89469f32a666efd46437351a8fb16758c35e5aecc563d202b480c10ddf9fa5350a5a321076b79b0a1a07ec2cea0b73aa5c28979cc382a198fa96cca0b5899d25 +libblastrampoline.v5.8.0+0.aarch64-linux-musl.tar.gz/md5/b7acda2fdd157bbb183d0dd33643beef +libblastrampoline.v5.8.0+0.aarch64-linux-musl.tar.gz/sha512/cf4125a47334fe2ec0d5a4b11624b12e1366ec031500218f680ad5a53152b9d752c0c02a0b92d0e07f3eb21f2f8f58d0c587438a4869a72197bbd5e91531369d +libblastrampoline.v5.8.0+0.armv6l-linux-gnueabihf.tar.gz/md5/eafabd99fb1287d495acb8efb8091fde +libblastrampoline.v5.8.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/63ff4e6bc400fa8ee713a1c5ae4af0a8e152d49860c6f5e94a17e426ad9f780d41cc0f84d33c75ea5347af1a53f07fc012798d603b6a94ea39f37cfd651a0719 +libblastrampoline.v5.8.0+0.armv6l-linux-musleabihf.tar.gz/md5/9788f74b375ef6b84c16c080f2be5bdd +libblastrampoline.v5.8.0+0.armv6l-linux-musleabihf.tar.gz/sha512/f00ebf794927404e2294a2fbb759b1e3e57836c7f683525fac0b2ac570da2c75904e43f154cf76fce310a624f9b35fbd40e6c7757882bb6f30db790f4221a543 +libblastrampoline.v5.8.0+0.armv7l-linux-gnueabihf.tar.gz/md5/4492bace63d8274d68ecdaa735e47e99 +libblastrampoline.v5.8.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/8868283e6c5224b80145fdfd17f13f713053ba94e49c170f38f0cbf9f794185d7dec9c107ce65dc76121d3ac5b21d2f3857f619d8279bede86a906230ff59a71 +libblastrampoline.v5.8.0+0.armv7l-linux-musleabihf.tar.gz/md5/d66b6ed1d4e5f6a130f36791063e651d +libblastrampoline.v5.8.0+0.armv7l-linux-musleabihf.tar.gz/sha512/414ad07574a6e9aa670bbfea13eaea11da13129c9ccb4193cad708014c31493ff10ff427558b90cb16040fa64c8a325c2e375e3310c39fb37bb3e7fdb6a72a5f +libblastrampoline.v5.8.0+0.i686-linux-gnu.tar.gz/md5/595199a3a01174cfa4d9ce3407bf30dc +libblastrampoline.v5.8.0+0.i686-linux-gnu.tar.gz/sha512/02c3b0c3c0a411d5090a081f3bbbe38aaae40eaa5fe63d0690e0582e233cd9ce76483922557d4f65dc457e29a4e84d86ee5af20a60b082aec7bec4ca8607c1ca +libblastrampoline.v5.8.0+0.i686-linux-musl.tar.gz/md5/5832d0044842cb84f4e1e1b0a04b8205 +libblastrampoline.v5.8.0+0.i686-linux-musl.tar.gz/sha512/d28954d0feef6a33fa0bfeb59acb68821222d36a4e353eaf41936ee2c9aace719c2d0f0b0f080eafe2baecc67a29de4cacc0446aac776bbb615c4426d35c9c8f +libblastrampoline.v5.8.0+0.i686-w64-mingw32.tar.gz/md5/89c07640b6c7ed719199b0cd0a570961 +libblastrampoline.v5.8.0+0.i686-w64-mingw32.tar.gz/sha512/71241e83501ed473af0bf60a3223075e22a48788fdcf0ad5b2932861c89ec0741c61bf6a04c8a26e68b2f39d360b6009a79ea2502b5cccf28249738e7796be89 +libblastrampoline.v5.8.0+0.powerpc64le-linux-gnu.tar.gz/md5/5f76f5c6a88c0caaa6419ba212f8cb94 +libblastrampoline.v5.8.0+0.powerpc64le-linux-gnu.tar.gz/sha512/785071e682075b2cebd992394e66169f4ee2db3a8e23affb88dc05d9abf55f49d597b2a7400a13c83ad106ad825b5ee666b01f8625e51aec267132573273991e +libblastrampoline.v5.8.0+0.x86_64-apple-darwin.tar.gz/md5/21beb51d448bd22e4608a16b3f4fde05 +libblastrampoline.v5.8.0+0.x86_64-apple-darwin.tar.gz/sha512/620ba64d93ef416e483f813617aa313957282d8361f920b5444702fa911ff0051d1f8a8814b5fa0b082fd4dc77d96cb8b763937c786959bbc97cbb6131617152 +libblastrampoline.v5.8.0+0.x86_64-linux-gnu.tar.gz/md5/14c1045ba4d400f490ddea5343a46f04 +libblastrampoline.v5.8.0+0.x86_64-linux-gnu.tar.gz/sha512/0fdae83f4df93b28951521cf426736367f568c1e76fb68eea42b045cc9a288b6836abb3206a6d61e4f88adcf198553e911c45231aecb0f552e06de28eb3bec54 +libblastrampoline.v5.8.0+0.x86_64-linux-musl.tar.gz/md5/59b110676fcb2fcfdcf670a5d435d555 +libblastrampoline.v5.8.0+0.x86_64-linux-musl.tar.gz/sha512/57a5022e9fabc0637a29f3c32f6180cb4f6a90282191232e299df6cea5265b535e4a0af4fde15c8fe80e5a59edea0fae96dd3a510f5720ecd78e85a2a9ffbfe0 +libblastrampoline.v5.8.0+0.x86_64-unknown-freebsd.tar.gz/md5/cb1c14b4f8754561c5eaf8502582f09a +libblastrampoline.v5.8.0+0.x86_64-unknown-freebsd.tar.gz/sha512/d3b19a2a9b3dc674119590d920a2e99705de823e7d01a210485b31f8b1ce59253c4a70f2d8fb967f7fa05efb6ac376d94e79ffc6848607a366b2f0caa58b4208 +libblastrampoline.v5.8.0+0.x86_64-w64-mingw32.tar.gz/md5/34fdc53745245887f968f420b2f02ed9 +libblastrampoline.v5.8.0+0.x86_64-w64-mingw32.tar.gz/sha512/bbf478736b7bd57b340ccd5b6744d526a7a95fc524d30fdf9af6e9d79285641be26fae5f9e5302d71a5be76b05c379e969a829e259d8100ba9c6ce202b632b3d diff --git a/stdlib/libblastrampoline_jll/Project.toml b/stdlib/libblastrampoline_jll/Project.toml index 42d49010019c3..4699baa7dad23 100644 --- a/stdlib/libblastrampoline_jll/Project.toml +++ b/stdlib/libblastrampoline_jll/Project.toml @@ -1,6 +1,6 @@ name = "libblastrampoline_jll" uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.7.0+0" +version = "5.8.0+0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" From 1ae70ec4c9360894eb8113f6e7a527601ceb87b7 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 8 May 2023 08:56:15 -0400 Subject: [PATCH 707/775] test: fix TypeCompareError conflict (#49663) We have a slightly over-eager test in error.jl that requires all types ending in Error to subtype Exception, so this can cause a sporadic failure without this update. --- test/errorshow.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/errorshow.jl b/test/errorshow.jl index cb19061da8806..b56710850e3f3 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -989,9 +989,9 @@ bt_str = sprint(Base.show_backtrace, bt) @test !occursin("g_collapse_pos_kw(x::Float64)", bt_str) # Test Base.print_with_compare in convert MethodErrors -struct TypeCompareError{A,B} end -let e = try convert(TypeCompareError{Float64,1}, TypeCompareError{Float64,2}()); catch e e end - str = sprint(Base.showerror, e) +struct TypeCompareError{A,B} <: Exception end +let e = @test_throws MethodError convert(TypeCompareError{Float64,1}, TypeCompareError{Float64,2}()) + str = sprint(Base.showerror, e.value) @test occursin("TypeCompareError{Float64,2}", str) @test occursin("TypeCompareError{Float64,1}", str) @test !occursin("TypeCompareError{Float64{},2}", str) # No {...} for types without params From 43079cf89c3be58c1ce78e2ecb79efa0d4f877c7 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Mon, 8 May 2023 09:00:30 -0400 Subject: [PATCH 708/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=207ebf98b43=20to=20c8249204b=20(#49678)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 | 1 - .../Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 | 1 - .../Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/md5 | 1 + .../Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/md5 create mode 100644 deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/sha512 diff --git a/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 b/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 deleted file mode 100644 index 7a0f31b8bec01..0000000000000 --- a/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -2885181bffe95462f1877668ccea7057 diff --git a/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 b/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 deleted file mode 100644 index f8231bbf2833f..0000000000000 --- a/deps/checksums/Pkg-7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -5f3ded1970a6d8bfc779de54e61b1dd58fa770799aac3a031b2ea536d159bf672e9490f53ecdfbf1c175adfe93b0868a88330619506da802218a98f07e64dd94 diff --git a/deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/md5 b/deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/md5 new file mode 100644 index 0000000000000..f78f869de8d51 --- /dev/null +++ b/deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/md5 @@ -0,0 +1 @@ +ee53fd79ba3c85d78dcfe73ef02a3416 diff --git a/deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/sha512 b/deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/sha512 new file mode 100644 index 0000000000000..9edc82b7bf891 --- /dev/null +++ b/deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/sha512 @@ -0,0 +1 @@ +1d07131b9689c39c1a3a8ffbe0926d145957dd7c29aa2740ee4d55c267d49fc9ee7014140e0cb8bdd0373fe0f35516fd8a9aabea0bcd42ee7847548df56fe086 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 4274859b10120..e06c326459313 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 7ebf98b4365e3b68a3d0784f7470baa7b2c4b6bc +PKG_SHA1 = c8249204b52708c916ff9a4752b04386c28de46b PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 2cad2a771b1bf0592ffd3c725a0913299630b3bf Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 8 May 2023 10:22:51 -0400 Subject: [PATCH 709/775] avoid allocating a GC frame on the alt stack (#49650) This might be confusing for GC marking, which will not expect to see it running on the altstack. And the link to prev might get corrupted if there is any sort of error that happens during the processing of the signal that causes us to start handling another signal. --- src/task.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/task.c b/src/task.c index 9678cf2f3fe4e..54cd84b321e8c 100644 --- a/src/task.c +++ b/src/task.c @@ -712,11 +712,10 @@ JL_DLLEXPORT JL_NORETURN void jl_no_exc_handler(jl_value_t *e, jl_task_t *ct) #define pop_timings_stack() /* Nothing */ #endif -#define throw_internal_body() \ +#define throw_internal_body(altstack) \ assert(!jl_get_safe_restore()); \ jl_ptls_t ptls = ct->ptls; \ ptls->io_wait = 0; \ - JL_GC_PUSH1(&exception); \ jl_gc_unsafe_enter(ptls); \ if (exception) { \ /* The temporary ptls->bt_data is rooted by special purpose code in the\ @@ -729,6 +728,7 @@ JL_DLLEXPORT JL_NORETURN void jl_no_exc_handler(jl_value_t *e, jl_task_t *ct) assert(ct->excstack && ct->excstack->top); \ jl_handler_t *eh = ct->eh; \ if (eh != NULL) { \ + if (altstack) ptls->sig_exception = NULL; \ pop_timings_stack() \ asan_unpoison_task_stack(ct, &eh->eh_ctx); \ jl_longjmp(eh->eh_ctx, 1); \ @@ -741,23 +741,21 @@ JL_DLLEXPORT JL_NORETURN void jl_no_exc_handler(jl_value_t *e, jl_task_t *ct) static void JL_NORETURN throw_internal(jl_task_t *ct, jl_value_t *exception JL_MAYBE_UNROOTED) { CFI_NORETURN - throw_internal_body() + JL_GC_PUSH1(&exception); + throw_internal_body(0); jl_unreachable(); } -#ifdef _COMPILER_ASAN_ENABLED_ /* On the signal stack, we don't want to create any asan frames, but we do on the normal, stack, so we split this function in two, depending on which context - we're calling it in */ -JL_NO_ASAN static void JL_NORETURN throw_internal_altstack(jl_task_t *ct, jl_value_t *exception JL_MAYBE_UNROOTED) + we're calling it in. This also lets us avoid making a GC frame on the altstack, + which might end up getting corrupted if we recur here through another signal. */ +JL_NO_ASAN static void JL_NORETURN throw_internal_altstack(jl_task_t *ct, jl_value_t *exception) { CFI_NORETURN - throw_internal_body() + throw_internal_body(1); jl_unreachable(); } -#else -#define throw_internal_altstack throw_internal -#endif // record backtrace and raise an error JL_DLLEXPORT void jl_throw(jl_value_t *e JL_MAYBE_UNROOTED) @@ -799,7 +797,7 @@ CFI_NORETURN } jl_ptls_t ptls = ct->ptls; jl_value_t *e = ptls->sig_exception; - ptls->sig_exception = NULL; + JL_GC_PROMISE_ROOTED(e); throw_internal_altstack(ct, e); } From 0a696a3842750fcedca8832bc0aabe9096c7658f Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Mon, 8 May 2023 12:14:16 -0500 Subject: [PATCH 710/775] Remove unnecessary `Base.` prefix (#49668) --- base/twiceprecision.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index 736261e09792a..d91a04371230c 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -112,7 +112,7 @@ julia> Float64(hi) + Float64(lo) ``` """ function mul12(x::T, y::T) where {T<:AbstractFloat} - (h, l) = Base.Math.two_mul(x, y) + (h, l) = Math.two_mul(x, y) ifelse(!isfinite(h), (h, h), (h, l)) end mul12(x::T, y::T) where {T} = (p = x * y; (p, zero(p))) From a2d9c008d4fce4fe48b795b79291d0a905b70691 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Mon, 8 May 2023 18:50:38 -0400 Subject: [PATCH 711/775] Some touchups to Task docs (#49682) --- base/lock.jl | 4 ++-- base/task.jl | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/base/lock.jl b/base/lock.jl index 1321b0c0f48c7..1663a765111bb 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -481,8 +481,8 @@ end """ reset(::Event) -Reset an Event back into an un-set state. Then any future calls to `wait` will -block until `notify` is called again. +Reset an [`Event`](@ref) back into an un-set state. Then any future calls to `wait` will +block until [`notify`](@ref) is called again. """ function reset(e::Event) @atomic e.set = false # full barrier diff --git a/base/task.jl b/base/task.jl index e905323f38882..4fbb51fde3e8e 100644 --- a/base/task.jl +++ b/base/task.jl @@ -70,7 +70,7 @@ end """ TaskFailedException -This exception is thrown by a `wait(t)` call when task `t` fails. +This exception is thrown by a [`wait(t)`](@ref) call when task `t` fails. `TaskFailedException` wraps the failed task `t`. """ struct TaskFailedException <: Exception @@ -362,8 +362,8 @@ fetch(@nospecialize x) = x """ fetch(t::Task) -Wait for a Task to finish, then return its result value. -If the task fails with an exception, a `TaskFailedException` (which wraps the failed task) +Wait for a [`Task`](@ref) to finish, then return its result value. +If the task fails with an exception, a [`TaskFailedException`](@ref) (which wraps the failed task) is thrown. """ function fetch(t::Task) From 33a2a9de990d6b38f934cbae8ff1d526101b3c60 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Mon, 8 May 2023 18:53:48 -0400 Subject: [PATCH 712/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=20c8249204b=20to=2094f668cee=20(#49687)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/md5 | 1 + .../Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/sha512 | 1 + .../Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/md5 | 1 - .../Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/md5 create mode 100644 deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/sha512 diff --git a/deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/md5 b/deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/md5 new file mode 100644 index 0000000000000..88f078bc9ff07 --- /dev/null +++ b/deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/md5 @@ -0,0 +1 @@ +3a917f288ca5688dad17f49e9743a131 diff --git a/deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/sha512 b/deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/sha512 new file mode 100644 index 0000000000000..f39662c8596be --- /dev/null +++ b/deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/sha512 @@ -0,0 +1 @@ +a7e69c67f63720cd27ce4f3824a6602d562b4a1a4c0f1238a47932635896ba8dd67bbc24fd2e748ef722df3172cc3c95fe41cb9dbca049cb811c1d18eadd89ff diff --git a/deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/md5 b/deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/md5 deleted file mode 100644 index f78f869de8d51..0000000000000 --- a/deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -ee53fd79ba3c85d78dcfe73ef02a3416 diff --git a/deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/sha512 b/deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/sha512 deleted file mode 100644 index 9edc82b7bf891..0000000000000 --- a/deps/checksums/Pkg-c8249204b52708c916ff9a4752b04386c28de46b.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -1d07131b9689c39c1a3a8ffbe0926d145957dd7c29aa2740ee4d55c267d49fc9ee7014140e0cb8bdd0373fe0f35516fd8a9aabea0bcd42ee7847548df56fe086 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index e06c326459313..99058cf8bf317 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = c8249204b52708c916ff9a4752b04386c28de46b +PKG_SHA1 = 94f668ceef108ab82f5532f2a86749e4b932e010 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 493a16a8212f784736dd47cda2a177e04916c924 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Mon, 8 May 2023 23:59:56 -0500 Subject: [PATCH 713/775] Micro-optimization in WeakKeyDict constructor (#49690) --- base/weakkeydict.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/weakkeydict.jl b/base/weakkeydict.jl index 7cfc65ab0c66b..328f368c80b71 100644 --- a/base/weakkeydict.jl +++ b/base/weakkeydict.jl @@ -23,7 +23,7 @@ mutable struct WeakKeyDict{K,V} <: AbstractDict{K,V} # Constructors mirror Dict's function WeakKeyDict{K,V}() where V where K - t = new(Dict{Any,V}(), ReentrantLock(), identity, 0) + t = new(Dict{WeakRef,V}(), ReentrantLock(), identity, 0) t.finalizer = k -> t.dirty = true return t end From 7ef78e45b3aa7fb523919fe6c6dd3182b88d5b6c Mon Sep 17 00:00:00 2001 From: Steve Kelly Date: Tue, 9 May 2023 02:39:44 -0400 Subject: [PATCH 714/775] Add some more Effects system docs (#49652) * add docs for `Base.infer_effects` * add `show` decoder in `Effects` docs Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> --- base/compiler/effects.jl | 31 +++++++++++++++++++++++++++++++ base/reflection.jl | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index ace47df9153ef..ec64b7601bc76 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -54,6 +54,37 @@ analyzed method (see the implementation of `merge_effects!`). Each effect proper initialized with `ALWAYS_TRUE`/`true` and then transitioned towards `ALWAYS_FALSE`/`false`. Note that within the current flow-insensitive analysis design, effects detected by local analysis on each statement usually taint the global conclusion conservatively. + +## Key for `show` output of Effects: + +The output represents the state of different effect properties in the following order: + +1. `consistent` (`c`): + - `+c` (green): `ALWAYS_TRUE` + - `-c` (red): `ALWAYS_FALSE` + - `?c` (yellow): `CONSISTENT_IF_NOTRETURNED` and/or `CONSISTENT_IF_INACCESSIBLEMEMONLY` +2. `effect_free` (`e`): + - `+e` (green): `ALWAYS_TRUE` + - `-e` (red): `ALWAYS_FALSE` + - `?e` (yellow): `EFFECT_FREE_IF_INACCESSIBLEMEMONLY` +3. `nothrow` (`n`): + - `+n` (green): `true` + - `-n` (red): `false` +4. `terminates` (`t`): + - `+t` (green): `true` + - `-t` (red): `false` +5. `notaskstate` (`s`): + - `+s` (green): `true` + - `-s` (red): `false` +6. `inaccessiblememonly` (`m`): + - `+m` (green): `ALWAYS_TRUE` + - `-m` (red): `ALWAYS_FALSE` + - `?m` (yellow): `INACCESSIBLEMEM_OR_ARGMEMONLY` +7. `noinbounds` (`i`): + - `+i` (green): `true` + - `-i` (red): `false` + +Additionally, if the `nonoverlayed` property is false, a red prime symbol (′) is displayed after the tuple. """ struct Effects consistent::UInt8 diff --git a/base/reflection.jl b/base/reflection.jl index a3e98e11a5f04..197742318bb4b 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1525,6 +1525,42 @@ function return_types(@nospecialize(f), @nospecialize(types=default_tt(f)); return rts end +""" + infer_effects(f, types=default_tt(f); world=get_world_counter(), interp=Core.Compiler.NativeInterpreter(world)) + +Compute the `Effects` of a function `f` with argument types `types`. The `Effects` represents the computational effects of the function call, such as whether it is free of side effects, guaranteed not to throw an exception, guaranteed to terminate, etc. The `world` and `interp` arguments specify the world counter and the native interpreter to use for the analysis. + +# Arguments +- `f`: The function to analyze. +- `types` (optional): The argument types of the function. Defaults to the default tuple type of `f`. +- `world` (optional): The world counter to use for the analysis. Defaults to the current world counter. +- `interp` (optional): The native interpreter to use for the analysis. Defaults to a new `Core.Compiler.NativeInterpreter` with the specified `world`. + +# Returns +- `effects::Effects`: The computed effects of the function call. + +# Example + +```julia +julia> function foo(x) + y = x * 2 + return y + end; + +julia> effects = Base.infer_effects(foo, (Int,)) +(+c,+e,+n,+t,+s,+m,+i) +``` + +This function will return an `Effects` object with information about the computational effects of the function `foo` when called with an `Int` argument. See the documentation for `Effects` for more information on the various effect properties. + +!!! warning + The `infer_effects` function should not be used from generated functions; + doing so will result in an error. + +# See Also +- [`Core.Compiler.Effects`](@ref): A type representing the computational effects of a method call. +- [`Base.@assume_effects`](@ref): A macro for making assumptions about the effects of a method. +""" function infer_effects(@nospecialize(f), @nospecialize(types=default_tt(f)); world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) From 89b47cf7768f6c172be83f1678b58d459b140531 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 9 May 2023 13:09:15 -0400 Subject: [PATCH 715/775] Tweak `donotdelete` effects (#49691) I don't think there's any reason not to mark this intrinsic as `:consistent`. The only real property we need from it at the julia level is that the optimizer does not delete it, which `!:effect_free` will take care of. --- base/compiler/tfuncs.jl | 4 +++- test/core.jl | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index cba74a3e658ca..f894d4ab3f4a5 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -2196,6 +2196,7 @@ const _CONSISTENT_BUILTINS = Any[ typeassert, throw, setfield!, + donotdelete ] # known to be effect-free (but not necessarily nothrow) @@ -2235,7 +2236,8 @@ const _INACCESSIBLEMEM_BUILTINS = Any[ typeassert, typeof, compilerbarrier, - Core._typevar + Core._typevar, + donotdelete ] const _ARGMEM_BUILTINS = Any[ diff --git a/test/core.jl b/test/core.jl index efd32c9789a59..f71baa843d25f 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8006,3 +8006,7 @@ end # objectid for datatypes is inconsistant for types that have unbound type parameters. @test !Core.Compiler.is_consistent(Base.infer_effects(objectid, (DataType,))) @test !Core.Compiler.is_consistent(Base.infer_effects(objectid, (Tuple{Vector{Int}},))) + +# donotdelete should not taint consistency of the containing function +f_donotdete(x) = (Core.Compiler.donotdelete(x); 1) +@test Core.Compiler.is_consistent(Base.infer_effects(f_donotdete, (Tuple{Float64},))) From 61f608248b73c9a2721e484724e59ed07fb94989 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 9 May 2023 13:10:11 -0400 Subject: [PATCH 716/775] Explicitly note that :consistent includes no-ub (#49693) * Explicitly note that :consistent includes no-ub We may split these effects in the future, but currently :consistent-cy requires the absence for undefined behavior for the function. There were a few questions about this, so explicitly document this in the help text. * Update base/expr.jl Co-authored-by: Sukera <11753998+Seelengrab@users.noreply.github.com> --------- Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Co-authored-by: Sukera <11753998+Seelengrab@users.noreply.github.com> --- base/expr.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/base/expr.jl b/base/expr.jl index c37f1a6482162..e45684f95a34f 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -513,6 +513,13 @@ The `:consistent` setting asserts that for egal (`===`) inputs: even for the same world age (e.g. because one ran in the interpreter, while the other was optimized). +!!! note + The `:consistent`-cy assertion currrently includes the assertion that the function + will not execute any undefined behavior (for any input). Note that undefined behavior + may technically cause the function to violate other effect assertions (such as + `:nothrow` or `:effect_free`) as well, but we do not model this, and all effects + except `:consistent` assume the absence of undefined behavior. + !!! note If `:consistent` functions terminate by throwing an exception, that exception itself is not required to meet the egality requirement specified above. From e7425d53251f3f097e9481f02eafe8a2e14d4f70 Mon Sep 17 00:00:00 2001 From: Ben Baumgold <4933671+baumgold@users.noreply.github.com> Date: Tue, 9 May 2023 15:07:02 -0400 Subject: [PATCH 717/775] TimeType subtraction using promote (#49700) --- stdlib/Dates/src/arithmetic.jl | 1 + stdlib/Dates/test/arithmetic.jl | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/stdlib/Dates/src/arithmetic.jl b/stdlib/Dates/src/arithmetic.jl index 6537f4e1caa82..a847f749d0154 100644 --- a/stdlib/Dates/src/arithmetic.jl +++ b/stdlib/Dates/src/arithmetic.jl @@ -7,6 +7,7 @@ # TimeType arithmetic (+)(x::TimeType) = x (-)(x::T, y::T) where {T<:TimeType} = x.instant - y.instant +(-)(x::TimeType, y::TimeType) = -(promote(x, y)...) # Date-Time arithmetic """ diff --git a/stdlib/Dates/test/arithmetic.jl b/stdlib/Dates/test/arithmetic.jl index 485fea5624066..2e684815a3c86 100644 --- a/stdlib/Dates/test/arithmetic.jl +++ b/stdlib/Dates/test/arithmetic.jl @@ -10,6 +10,13 @@ using Dates b = Dates.Time(11, 59, 59) @test Dates.CompoundPeriod(a - b) == Dates.Hour(12) end + +@testset "TimeType arithmetic" begin + a = Date(2023, 5, 1) + b = DateTime(2023, 5, 2) + @test b - a == Day(1) +end + @testset "Wrapping arithmetic for Months" begin # This ends up being trickier than expected because # the user might do 2014-01-01 + Month(-14) From d10d90c35a8e94f96934ffaa4bbcb6d05a50b890 Mon Sep 17 00:00:00 2001 From: Jakob Nybo Nissen Date: Tue, 9 May 2023 22:30:54 +0200 Subject: [PATCH 718/775] Do not reallocate on sizehint to same size (#49703) The sizehint! code currently do not reallocate when sizehinting to a smaller size if 1/8th or fewer elements will be freed. However, the check rounds down, so an array of size e.g. 3 resized to size 3 will "free" 1/8th of the elements, namely zero elements, needlessly resizing. This commit changes the check to only reallocate if MORE than 1/8th of elements will be freed. --- src/array.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array.c b/src/array.c index ae730bed7c4e8..5226c729d32e7 100644 --- a/src/array.c +++ b/src/array.c @@ -1161,7 +1161,7 @@ JL_DLLEXPORT void jl_array_sizehint(jl_array_t *a, size_t sz) if (sz <= a->maxsize) { size_t dec = a->maxsize - sz; //if we don't save at least an eighth of maxsize then its not worth it to shrink - if (dec < a->maxsize / 8) return; + if (dec <= a->maxsize / 8) return; jl_array_shrink(a, dec); } else { From b9b8b38ec09b4e91b46fa833c609e0cf2b9eba45 Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Tue, 9 May 2023 14:47:27 -0600 Subject: [PATCH 719/775] Move test of stackoverflow with close(::Channel) to stack_overflow.jl (#49702) Per @vtjnash's comment here: https://github.com/JuliaLang/julia/pull/49508/files/bca5ac79d04fb2a95f3b9a7b7448fe5b478f950b#r1186161259 > the handling of this error is quite bad and sometimes ends up breaking > the process (as I found out today trying to debug something completely > unrelated) This is a Tests-only PR --- test/channels.jl | 17 ----------------- test/stack_overflow.jl | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/test/channels.jl b/test/channels.jl index 89b0e5c09d7d8..dbda5cf069081 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -626,20 +626,3 @@ end @test n_avail(c) == 0 end end - -# Issue #49507: stackoverflow in type inference caused by close(::Channel, ::Exception) -@testset "close(::Channel, ::StackOverflowError)" begin - ch = let result = Channel() - foo() = try - foo() - catch e; - close(result, e) - end - - foo() # This shouldn't fail with an internal stackoverflow error in inference. - - result - end - - @test (try take!(ch) catch e; e; end) isa StackOverflowError -end diff --git a/test/stack_overflow.jl b/test/stack_overflow.jl index 9f4bae6f3f5b3..297186c8a4d3a 100644 --- a/test/stack_overflow.jl +++ b/test/stack_overflow.jl @@ -17,3 +17,20 @@ let exename = Base.julia_cmd() @show readchomperrors(`$exename -e "f() = f(); f()"`) @show readchomperrors(`$exename -e "f() = f(); fetch(@async f())"`) end + +# Issue #49507: stackoverflow in type inference caused by close(::Channel, ::Exception) +@testset "close(::Channel, ::StackOverflowError)" begin + ch = let result = Channel() + foo() = try + foo() + catch e; + close(result, e) + end + + foo() # This shouldn't fail with an internal stackoverflow error in inference. + + result + end + + @test (try take!(ch) catch e; e; end) isa StackOverflowError +end From e204e200df8f57e516920c056b6ec2eb37d34079 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 9 May 2023 22:57:28 -0400 Subject: [PATCH 720/775] irinterp: Consider cfg information from discovered errors (#49692) If we infer a call to `Union{}`, we can terminate further abstract interpretation. However, this of course also means that we can make use of that information to refine the types of any phis that may have originated from the basic block containing the call that was refined to `Union{}`. Co-authored-by: Shuhei Kadowaki --- base/compiler/ssair/irinterp.jl | 106 +++++++++++++++++++++----------- test/compiler/inference.jl | 33 ++++++++++ 2 files changed, 103 insertions(+), 36 deletions(-) diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index d58d18b188757..ad6077fa48859 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -58,6 +58,49 @@ function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo, irsv::IRIn return RTEffects(rt, effects) end +function update_phi!(irsv::IRInterpretationState, from::Int, to::Int) + ir = irsv.ir + if length(ir.cfg.blocks[to].preds) == 0 + # Kill the entire block + for bidx = ir.cfg.blocks[to].stmts + ir.stmts[bidx][:inst] = nothing + ir.stmts[bidx][:type] = Bottom + ir.stmts[bidx][:flag] = IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW + end + return + end + for sidx = ir.cfg.blocks[to].stmts + sinst = ir.stmts[sidx][:inst] + isa(sinst, Nothing) && continue # allowed between `PhiNode`s + isa(sinst, PhiNode) || break + for (eidx, edge) in enumerate(sinst.edges) + if edge == from + deleteat!(sinst.edges, eidx) + deleteat!(sinst.values, eidx) + push!(irsv.ssa_refined, sidx) + break + end + end + end +end +update_phi!(irsv::IRInterpretationState) = (from::Int, to::Int)->update_phi!(irsv, from, to) + +function kill_terminator_edges!(irsv::IRInterpretationState, term_idx::Int, bb::Int=block_for_inst(irsv.ir, term_idx)) + ir = irsv.ir + inst = ir[SSAValue(term_idx)][:inst] + if isa(inst, GotoIfNot) + kill_edge!(ir, bb, inst.dest, update_phi!(irsv)) + kill_edge!(ir, bb, bb+1, update_phi!(irsv)) + elseif isa(inst, GotoNode) + kill_edge!(ir, bb, inst.label, update_phi!(irsv)) + elseif isa(inst, ReturnNode) + # Nothing to do + else + @assert !isexpr(inst, :enter) + kill_edge!(ir, bb, bb+1, update_phi!(irsv)) + end +end + function reprocess_instruction!(interp::AbstractInterpreter, idx::Int, bb::Union{Int,Nothing}, @nospecialize(inst), @nospecialize(typ), irsv::IRInterpretationState, extra_reprocess::Union{Nothing,BitSet,BitSetBoundedMinPrioritySet}) @@ -66,30 +109,6 @@ function reprocess_instruction!(interp::AbstractInterpreter, idx::Int, bb::Union cond = inst.cond condval = maybe_extract_const_bool(argextype(cond, ir)) if condval isa Bool - function update_phi!(from::Int, to::Int) - if length(ir.cfg.blocks[to].preds) == 0 - # Kill the entire block - for bidx = ir.cfg.blocks[to].stmts - ir.stmts[bidx][:inst] = nothing - ir.stmts[bidx][:type] = Bottom - ir.stmts[bidx][:flag] = IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW - end - return - end - for sidx = ir.cfg.blocks[to].stmts - sinst = ir.stmts[sidx][:inst] - isa(sinst, Nothing) && continue # allowed between `PhiNode`s - isa(sinst, PhiNode) || break - for (eidx, edge) in enumerate(sinst.edges) - if edge == from - deleteat!(sinst.edges, eidx) - deleteat!(sinst.values, eidx) - push!(irsv.ssa_refined, sidx) - break - end - end - end - end if isa(cond, SSAValue) kill_def_use!(irsv.tpdum, cond, idx) end @@ -100,10 +119,10 @@ function reprocess_instruction!(interp::AbstractInterpreter, idx::Int, bb::Union if condval ir.stmts[idx][:inst] = nothing ir.stmts[idx][:type] = Any - kill_edge!(ir, bb, inst.dest, update_phi!) + kill_edge!(ir, bb, inst.dest, update_phi!(irsv)) else ir.stmts[idx][:inst] = GotoNode(inst.dest) - kill_edge!(ir, bb, bb+1, update_phi!) + kill_edge!(ir, bb, bb+1, update_phi!(irsv)) end return true end @@ -123,9 +142,6 @@ function reprocess_instruction!(interp::AbstractInterpreter, idx::Int, bb::Union rt, nothrow = concrete_eval_invoke(interp, inst, inst.args[1]::MethodInstance, irsv) if nothrow ir.stmts[idx][:flag] |= IR_FLAG_NOTHROW - if isa(rt, Const) && is_inlineable_constant(rt.val) - ir.stmts[idx][:inst] = quoted(rt.val) - end end elseif head === :throw_undef_if_not || # TODO: Terminate interpretation early if known false? head === :gc_preserve_begin || @@ -148,9 +164,17 @@ function reprocess_instruction!(interp::AbstractInterpreter, idx::Int, bb::Union else error("reprocess_instruction!: unhandled instruction found") end - if rt !== nothing && !⊑(typeinf_lattice(interp), typ, rt) - ir.stmts[idx][:type] = rt - return true + if rt !== nothing + if isa(rt, Const) + ir.stmts[idx][:type] = rt + if is_inlineable_constant(rt.val) + ir.stmts[idx][:inst] = quoted(rt.val) + end + return true + elseif !⊑(typeinf_lattice(interp), typ, rt) + ir.stmts[idx][:type] = rt + return true + end end return false end @@ -227,12 +251,22 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR any_refined = true delete!(ssa_refined, idx) end - if any_refined && reprocess_instruction!(interp, - idx, bb, inst, typ, irsv, extra_reprocess) - push!(ssa_refined, idx) + did_reprocess = false + if any_refined + did_reprocess = reprocess_instruction!(interp, + idx, bb, inst, typ, irsv, extra_reprocess) + if did_reprocess + push!(ssa_refined, idx) + inst = ir.stmts[idx][:inst] + typ = ir.stmts[idx][:type] + end + end + if idx == lstmt + process_terminator!(ir, inst, idx, bb, all_rets, bb_ip) && @goto residual_scan + (isa(inst, GotoNode) || isa(inst, GotoIfNot) || isa(inst, ReturnNode) || isexpr(inst, :enter)) && continue end - idx == lstmt && process_terminator!(ir, inst, idx, bb, all_rets, bb_ip) && @goto residual_scan if typ === Bottom && !isa(inst, PhiNode) + kill_terminator_edges!(irsv, lstmt, bb) break end end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 1b137d1d8f661..5987e10401bc8 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -4871,3 +4871,36 @@ function nt_splat_partial(x::Int) Val{tuple(nt...)[2]}() end @test @inferred(nt_splat_partial(42)) == Val{2}() + +# Test that irinterp refines based on discovered errors +Base.@assume_effects :foldable Base.@constprop :aggressive function kill_error_edge(b1, b2, xs, x) + y = b1 ? "julia" : xs[] + if b2 + a = length(y) + else + a = sin(y) + end + a + x +end + +Base.@assume_effects :foldable Base.@constprop :aggressive function kill_error_edge(b1, b2, xs, ys, x) + y = b1 ? xs[] : ys[] + if b2 + a = length(y) + else + a = sin(y) + end + a + x +end + +let src = code_typed1((Bool,Base.RefValue{Any},Int,)) do b2, xs, x + kill_error_edge(true, b2, xs, x) + end + @test count(@nospecialize(x)->isa(x, Core.PhiNode), src.code) == 0 +end + +let src = code_typed1((Bool,Base.RefValue{String}, Base.RefValue{Any},Int,)) do b2, xs, ys, x + kill_error_edge(true, b2, xs, ys, x) + end + @test count(@nospecialize(x)->isa(x, Core.PhiNode), src.code) == 0 +end From 921f1b9d5e9389756826898d6907c0a2829efa51 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Wed, 10 May 2023 09:58:04 -0400 Subject: [PATCH 721/775] Fix remarks emissions from simdloop pass Co-authored-by: Valentin Churavy --- src/llvm-simdloop.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/llvm-simdloop.cpp b/src/llvm-simdloop.cpp index 3c94b226ad7b8..233f61c9fea6b 100644 --- a/src/llvm-simdloop.cpp +++ b/src/llvm-simdloop.cpp @@ -165,12 +165,14 @@ static bool markLoopInfo(Module &M, Function *marker, function_ref(U); ToDelete.push_back(I); - OptimizationRemarkEmitter ORE(I->getParent()->getParent()); - LoopInfo &LI = GetLI(*I->getParent()->getParent()); - Loop *L = LI.getLoopFor(I->getParent()); - I->removeFromParent(); - if (!L) + BasicBlock *B = I->getParent(); + OptimizationRemarkEmitter ORE(B->getParent()); + LoopInfo &LI = GetLI(*B->getParent()); + Loop *L = LI.getLoopFor(B); + if (!L) { + I->removeFromParent(); continue; + } LLVM_DEBUG(dbgs() << "LSL: loopinfo marker found\n"); bool simd = false; @@ -209,8 +211,8 @@ static bool markLoopInfo(Module &M, Function *marker, function_refgetDebugLoc(), B) << "Loop marked for SIMD vectorization with flags { \"simd\": " << (simd ? "true" : "false") << ", \"ivdep\": " << (ivdep ? "true" : "false") << " }"; }); @@ -258,6 +260,8 @@ static bool markLoopInfo(Module &M, Function *marker, function_refremoveFromParent(); + Changed = true; } From 286b371d680efe4350f85769c3f5ba9c1faee726 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Tue, 9 May 2023 21:51:00 -0400 Subject: [PATCH 722/775] Update MemorySSA correctly when sinking gc_preserve_end --- src/llvm-julia-licm.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/llvm-julia-licm.cpp b/src/llvm-julia-licm.cpp index 7bc8d91b525f3..6c996b313ce38 100644 --- a/src/llvm-julia-licm.cpp +++ b/src/llvm-julia-licm.cpp @@ -58,13 +58,13 @@ static void eraseInstruction(Instruction &I, //Stolen and modified from LICM.cpp static void moveInstructionBefore(Instruction &I, Instruction &Dest, MemorySSAUpdater &MSSAU, - ScalarEvolution *SE) { + ScalarEvolution *SE, + MemorySSA::InsertionPlace Place = MemorySSA::BeforeTerminator) { I.moveBefore(&Dest); if (MSSAU.getMemorySSA()) if (MemoryUseOrDef *OldMemAcc = cast_or_null( MSSAU.getMemorySSA()->getMemoryAccess(&I))) - MSSAU.moveToPlace(OldMemAcc, Dest.getParent(), - MemorySSA::BeforeTerminator); + MSSAU.moveToPlace(OldMemAcc, Dest.getParent(), Place); if (SE) SE->forgetValue(&I); } @@ -241,7 +241,7 @@ struct JuliaLICM : public JuliaPassContext { continue; } ++SunkPreserveEnd; - moveInstructionBefore(*call, *exit_pts[0], MSSAU, SE); + moveInstructionBefore(*call, *exit_pts[0], MSSAU, SE, MemorySSA::Beginning); LLVM_DEBUG(dbgs() << "Sunk gc_preserve_end: " << *call << "\n"); REMARK([&](){ return OptimizationRemark(DEBUG_TYPE, "Sunk", call) From b2273d39542fe803f7d9da03ef57af7e815db68c Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Sun, 30 Apr 2023 20:19:08 -0400 Subject: [PATCH 723/775] Do not yet mandate opaque pointers for LLVM 15 --- src/codegen.cpp | 11 +++++++++++ src/jitlayers.cpp | 3 --- src/llvm-version.h | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index a9d2cb0c60333..2e3f7eb2bf7bb 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -9083,6 +9083,17 @@ extern "C" void jl_init_llvm(void) if (clopt && clopt->getNumOccurrences() == 0) cl::ProvidePositionalOption(clopt, "4", 1); +#if JL_LLVM_VERSION >= 150000 + clopt = llvmopts.lookup("opaque-pointers"); + if (clopt && clopt->getNumOccurrences() == 0) { +#ifdef JL_LLVM_OPAQUE_POINTERS + cl::ProvidePositionalOption(clopt, "true", 1); +#else + cl::ProvidePositionalOption(clopt, "false", 1); +#endif + } +#endif + jl_ExecutionEngine = new JuliaOJIT(); bool jl_using_gdb_jitevents = false; diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 643f0468457ae..ef7e98bb7852a 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -1306,9 +1306,6 @@ JuliaOJIT::JuliaOJIT() JD(ES.createBareJITDylib("JuliaOJIT")), ContextPool([](){ auto ctx = std::make_unique(); -#ifdef JL_LLVM_OPAQUE_POINTERS - ctx->setOpaquePointers(true); -#endif return orc::ThreadSafeContext(std::move(ctx)); }), #ifdef JL_USE_JITLINK diff --git a/src/llvm-version.h b/src/llvm-version.h index a3f3774b6dc15..819ec1c88976b 100644 --- a/src/llvm-version.h +++ b/src/llvm-version.h @@ -14,7 +14,7 @@ #error Only LLVM versions >= 12.0.0 are supported by Julia #endif -#if JL_LLVM_VERSION >= 150000 +#if JL_LLVM_VERSION >= 160000 #define JL_LLVM_OPAQUE_POINTERS 1 #endif From 190f84180883eb498cb7b7ed27e10af9a6c62863 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Wed, 26 Apr 2023 20:48:45 -0400 Subject: [PATCH 724/775] Upgrade Julia to LLVM 15.0.7+5 Co-authored-by: Gabriel Baraldi --- Make.inc | 2 +- deps/checksums/clang | 224 ++++++++-------- deps/checksums/lld | 224 ++++++++-------- deps/checksums/llvm | 452 +++++++++++++++----------------- deps/clang.version | 2 +- deps/lld.version | 2 +- deps/llvm-tools.version | 4 +- deps/llvm.version | 9 +- stdlib/LLD_jll/Project.toml | 4 +- stdlib/libLLVM_jll/Project.toml | 2 +- 10 files changed, 446 insertions(+), 479 deletions(-) diff --git a/Make.inc b/Make.inc index 4d564f057a3da..35b0657de5aa2 100644 --- a/Make.inc +++ b/Make.inc @@ -480,7 +480,7 @@ FC := $(CROSS_COMPILE)gfortran ifeq ($(OS), Darwin) APPLE_ARCH := $(shell uname -m) ifneq ($(APPLE_ARCH),arm64) -MACOSX_VERSION_MIN := 10.10 +MACOSX_VERSION_MIN := 10.14 else MACOSX_VERSION_MIN := 11.0 endif diff --git a/deps/checksums/clang b/deps/checksums/clang index 5fecc08fe523e..c16dd849e6fc5 100644 --- a/deps/checksums/clang +++ b/deps/checksums/clang @@ -1,116 +1,108 @@ -Clang.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/f5b5a95a89899922798e78df359813a5 -Clang.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/716a57c99b96407d1f2e65f1f419c4de788aca832fb9f92911739da122a5cb6be09e00c6e24bdbe57bddc8c6aed30e37910a38ee0acec7e4aecd6232270763b9 -Clang.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/d88f1754c6d3d95263cceb804a7cccf8 -Clang.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/7a22375ea1ee4c20d4c33c928f0c79399f382cb71e965da72e067dcd91d57cc28275532da564a05cf6ab91595996b3c3bd30f3537222b0fa651616b032f848de -Clang.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/b60623ab0dcb034fb27be4a68a034b91 -Clang.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/d93da664ba2e7123dc7a84788d417683fae769e0753af6c564be7d50713ab2a3d1b5e925467c3bfa32ccc2eaff1be2ebfed8a3fb5bf07bb2d5023a3e87eb1084 -Clang.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/f737f5a987eb274b839e3a8721104695 -Clang.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/4925d1f0e2f69a35fb4ef922c32de221125e49198cd952ec727ecbe17c3e3819b0d45a8bada12535dbb6aef57304c3b4abc8caef687559314472a2077ca04295 -Clang.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/4c675903f286691915f60445474bbf50 -Clang.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/85561aa0dcf8126bc21e90e8a08bb2402938e5139c05dd15a01ac7a67094cd1141d4414b0a3577f95ecc277dafc699cf1f000e59af3c3b0796daf0d38797081d -Clang.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/4771224587cc7378ed190c4a7fb129bf -Clang.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/54e15eef2a5aeef3992f2b43d5433e8602659b361c1e59f29b3ec11d0e75f8fbedcf023015a3452ad89fd13c825911c1a2ea7fab71df0a98cbf87af10f48934e -Clang.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/08cf0a7f776c75f6842b26acc213c2ae -Clang.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/0f9e0c1b2199b0e48751363245c0e83cf4fc4e72d78df7bfa936e39e0f96cfad9ad89c47b80632469b89f615e3368ca09e42a10e67f1bf7c3604347183c89b7f -Clang.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/a3a88eb47cbc54262a51469a993d0bde -Clang.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/65f899bb7d062623f1c6a7518b76e3ce753e1ab0d7891a736d15d9053ff7c3117bd4b0b510d1b71c3c0e5b43e86b96c70678228271f44b4ce3c642d44f32352c -Clang.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/75c230f9135708a81ef41075ff350f1e -Clang.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/ded16404f36d676d01f6dcf9668043560c40250c4080596f4f927a642d857c4fd28eb1fa7b76638873dbfdb77e86ffce4d1e5ca25b78654d8bf41dbf7891c44d -Clang.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/14cd2bccb3d1fd1f60af78fca8b0f571 -Clang.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/697550e5abd7c4723ccf2b59a342ba7ceb5e55299ec02a33269ee879de705903bb1cd4e5e0da3d0f6e186248295e8007d39256cf81764dfe9601b589361438fa -Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/401162a5dd6ee0697ba69260a74afe46 -Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/a8809c15036b8cbb9a4e50915179ec136cee497ee0a16015a4c7a7af53022e7ab9ef90785ab4e233105800396c4d7802b7aac9b4999b803feefd824a2007777b -Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/ceb2db1de7f5ca31bcb9e33155b8fd45 -Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/7d5db7f81bfe825dfbb824b4ce65fdfa61cba3040dad12346d7e231ff868d0bd597ca28b2e3aef8f728628e93f26a5ad9a95cee02839d62dee25cafb0744196e -Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/797c3a5197012afc428625517927b0bf -Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/2f36b2ef2c0a87b8ad2971f3254b435098edf35f7b1fce795fff00fe8b0e5099758a7135696e6fe60025fb61c14865f5b3c8d5c3bf84f04d6f24f6e3b5809894 -Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/811e61e240af841f71c11f397d7a5648 -Clang.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/abe7d7387b9de4965505120f41705ac59ffcf868001f2cf0081b85ad9b750a9c906b311055e4c381960e23b6a938ddf41c8889c6dc9f481c471f0509b26ed4de -Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/d21460305a04bc2e0a7471fea7e4fac3 -Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/c0a9d771ddaa423f341d322bbb59d18060255a66a6ef8800a1b4c3c9ccecd071a7739923a1dc17bb12b44f6a3aa012fa9fd9316345f96bd45e624c1fc3a0086d -Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/c860424b11a24eb5f162f78e98365c5d -Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/66ae467bc9cf489b2a13d49f4ac947acf66b4b0c52dc71b3faa356fbe999c592c7aabb16175f70fa9f8ee12f303866c5ef9e3290dead97b481ba47ad046ce1e4 -Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/6d3d11f8704c24f405b56d69f85aed07 -Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/479e54cbee1378200bca8d875fb50381bba2d91a36dce96d2652b7e8ee2f1a602d583f13ffaccbf02cdf28030c31982072004e301e4ad43e7040f1432ebbb9f7 -Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/b7ecdd0c751640bfb45b39623f7b578a -Clang.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/a251fe2f94bf9416d656589f53b277bde01b0662a255ca18a3d9bb62293b9db74d446c771939c2fc3d9c59f39b03fd3b4f8fbc3f60335dd6f779543e11b4aa50 -Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/09eda4af5a0d2bad3336e206e53916db -Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/ab9231c36ccbf8b5c51120cf9feadb5c0ce85edec93d5b05d734dc0b3304ea91d01516fb854572bad90b5ac02f766f214f60be25e5e8d7cc6ef43c2c2a761ce3 -Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/ad3c4f9cee5c065ba2e5f35da81d1288 -Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/100ba320f85449f0c7c7fcbf782d5e674b4a4c34eaeb861e16af09cd5eb0198103a9d6d57a99851b2f8fcc6e05169afebcc1c8af280286d0bb5cf711708a5b9b -Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/87cdb57d1d99d67a01927289d8e52882 -Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/a382fa6abc81aba8d59d9cec885c86d864209c80d1afafd85b4f20509ec7720639909fe3f8bae06a9938a697450fd6b300136fd892fabf423708df33454667da -Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/ddf46decaf6982033e329992461a3519 -Clang.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/0037d4792c72fcc127ddfa4588bbbb7ccc5f3d378218fe3c9578cc480dd81e35ae5faf58cb037609359170be9c3a8d5cfebe72b5b91b3c3677c882471225b353 -Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/94e2b29a4b43ed82d4ae613a4405e3e9 -Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/6612fcd70d7754a56ebf1979f4c2c77075234afaea66a46cacbf583198be6d8db0620b0b44f9d1308b7d5931d6fc9474cfc8897697609ef76244ea2dd9396fe4 -Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/5caf0a59f5ceaaf22721ee1aaec17f62 -Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/1294f3ae159d91ba0fec7e90d2c043087a3ef8508185e91d3dc53e33359e7abf7a4c0a7da2cf8d39062a5ab8e62b297991bfa22816de753890c6eca2920fa900 -Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/7bb1190508b5501940c5118bc6e896f9 -Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/222fe7aecdcfdb07d594cc7b47bb9a72124118a8549b6cf20f114082dda64606c46419ef9e633440b7c9c0aae95b38c64c8c7c47a4cb9fe7c96e0689d66a960a -Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/9e5b664cbf3f71489812cf88fdacd232 -Clang.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/a61e2e62c967eed4c987aec6d85a88fec6e8031f7bd3254ace8548ed283dcd593704941f09642b79a7609dd264e1bdf5aa7a7328a676ec6f07d83e178797a8f7 -Clang.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/3ff3989dc561b3e25de33862c6607a2e -Clang.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/6de35bb12cfe18f3f3305ff3b880e2f93fbc5a941ecdf704012f555553f6bd7412aff3c402443b394ec83dda1cb092082c08b6085045181b8832139ec61a4ec5 -Clang.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/9cf19e989e28fb226e1705aecf94e62f -Clang.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/c2bc6a4e7313a6065d827d33c88b05d297828f6a2f9d6f5a729fcd811d9990e744d92973c3f283010d798713df9858106fd25fbda7654ca843cf85443a3e4fc0 -Clang.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/cd5228524e17b0d1a6c6cf231592cc22 -Clang.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/af8dd8b19e2344d1df07ce6fb89fed3be5d137eeb43b08e8ee7024b6b2c6705355846ded42e9b5583f36b7d1ddf9a0530cd5943e3722260e28b52fd5fc10033b -Clang.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/33d3f0259e4426101964bd3708d8a35e -Clang.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/07486f4c11b8bca3c483dc60b9103927648dd74b468fc37f99f029094317cd050ac1083e82866523cd018a031293ae9f4e7986d15dccb154b9208bd90732188d -Clang.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/93c50e6d95b59145a5c7d0afe2e20c91 -Clang.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/4749856cb2739f7b0ce8d0930d4274311e5ea753b77150e732db0ffe55758f0aabcff06c97243a7b3954a1b205e975bd55bf6755e6f3d55407b698135a2b8029 -Clang.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/2574258d11a12b23ca3194a79e3f7f24 -Clang.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/c84f7054fbdf79196f19712796317131d57fb324a36d8e8cfcee2522261a127344d52364f6b2272136d5e36eb3f0c1a30de21c11aee11d4278e6ae2b5c406d72 -Clang.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/ba41df7ad23f43f742bb9b2cc68789ab -Clang.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/0e2b1be78c5cac46b66ad8b4e9853d45e85216625bdfa023de71d7871814ef1e7da58f5a9c0f8fcd5070e4fa57194243a759a1dc906cfbbfb7d332db38fa3635 -Clang.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/e7172fbe2947d01106597c4d1f32f091 -Clang.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/2ff2ba01b6ebe439acc9e4507cc81b78a00595b824d7012b0a404bc67d26bd1640187afb4c368360bb5d3566cab53b7a6380cccd0d062800313e9d30152881aa -Clang.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/9da1ed32f3207dd583ff66ec54a009be -Clang.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/3969a16fa4ad2789cfe202ae1fcf954dfbf332247bcc4b44eee725086176a86850383b07ea3454106447fcb464db29fef726885be3f47156835f737a446b0ae5 -Clang.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/1a0cef3a60b6388e3225977eb3dc2717 -Clang.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/80513e5902ad738d7157ce25e33e222fde04978e0d1a7e9dbb407adf285b6632a869334caef29a72794fcf74d2728b514c24bdd5a79e74c47a4f21a6da2346f7 -Clang.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/deb810d3c2cffefaff7df8e133680962 -Clang.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/f90106f0460cccafa6fdfe6e1babd95f002e41801f6091e27881682799fc7c71f16b8527462555fb769b94a0394ab64c97c67bb2dc309a1593088972fd661f1c -Clang.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/df41dff25d872279868ab4261bb7c919 -Clang.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/a01decdd5ceade5987e192e379fa8f44416364b7a256a70c60e8ff87a6073bac32f361e2625203d804c0110a256117ef7462528dc732a5e75e12c7ccd496fae6 -Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/771216e42312936decd5fa2ed8e43135 -Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/8d70319fc762c57c502c1b6db04795e71458aaa337e49f970025e4fc31ed1691c35fe450bc28ecd440aeab1e9f21ff399ea3dac7e9c9e54b97c1a0a5dc3c7f45 -Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/d58115e5fda3ab222493267841ab4219 -Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/468ca9fe210ce03e48a8efc50b9410ebdee2d3c73de2324c2d7242d86ab2b32168d83acd860717f463524caba5c59b32722043c1d452ae2054b3eaede73807b4 -Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/d453bcf391b68f67492b5258177faefc -Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/50d4c8b4a6d06d6e39829b0ef07e672f91e90d64278d87605dd52cc82fcd967d2823644a1501195e88e5e8c4e23a96ee2a336b1de505c8785fd2eea660f6ee14 -Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/f6241b5c64ca759fe8c6cb996b7fa011 -Clang.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/b7bfc9e057a124ce5570e49016cf17309e77da4e24536610b652936c51d26b7cde26ea65b5727bb0f990b1aa3bca2c0be35b11ced5fef6d3e5a1a027d6f2e958 -Clang.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/c2fe21a6699a10e3e639ad3c109a18e4 -Clang.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/93a6fe1e11c35af12e8f4164ea9fc9c80cf706f424c4877b4c302b25a4c7f6ee103dc614d5d7d44586cb9948c819986e8442a7a4ab8ad0f21a4d82899d97baec -Clang.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/2fb786ca049cfe820ee26d5a51f5f84b -Clang.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/d441e0f83107ddc025a6a80cf3c6865598e381323d04173dfc2a60c985a248248799fa9ffe36f8b97c75170e23fcd563f2aae599487dc1218bd24f7d12645183 -Clang.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/b4b8134cb2dc032a19ddd7f8c1a66dcd -Clang.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/4fed4767d97b2b3c5e3ab60ce5cf8b5c0bc65029ca57c53b3bd50f5ed924101583ea156f4df31144bb85e1771ed5af72b18f8cc90b777da0e07d6631ddcf2a3d -Clang.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/796cc2877173231a6b15a3ebe5bf0356 -Clang.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/18349ad2a77786fa1566b234c03251eb5e805cf69b6fe978fbfb40ef38fcd5bdb41c7773d131fe6867651b151d11d61b14a52262022dd00462daacad936df3ff -Clang.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/a7b5f9b64abffeecd1255de96cdd718f -Clang.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/0429d2cec6f54fb7196030857325e761aecb0f76850a591bf532b6482571ae908a94c979875e178620b760d2b6faeb06fc66475b3633975905e6d20965abf604 -Clang.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/894eef7d0411e065b63f3adb2a0a2f02 -Clang.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/e28a429aadaa8a5b91849191396eb031be9515ccd4913d03dc67f13aeb3cd71f48a8ecc2e04cb6158156fec0ee132133ac1e6de013a86af5cd6a82a3bdc5f0c7 -Clang.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/dc1701be0ee2ec5c1c0206df5a5cfe09 -Clang.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/7a0fe6b9086bc3458c6f2b0797f5cfffb647ffb5afe149dba437e0fdf21bee68971bbc50ffe0d07e022647b2176364e65536a8c0fc77aade37e9ed4e7dcb82dd -Clang.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/2d6d11cac078e7e9ff76d957927fde17 -Clang.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/886e1558c7cf71112169f63c0571a98ff5b98600acf1a740e3d32efa6df5387931f876ac13aeb2095cc38e02b547dba24645ef6ecff42e4edcb1aaab506af6e7 -Clang.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/19489b3d76838ec1603462b6406c2316 -Clang.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/ac6984d4117735727185306fb60b45fcb81c4c337d56ccb78040790cbe38e7b010b206e1fe7fadd46d009089c5300e181564a30b70c3871a24ceb0a80f83b106 -Clang.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/cb2ba20005e1f6b656b7bab26b99e23a -Clang.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/8efeb29e9b4e627966e96b05c75bb842e89260c63e8a8cbc018fa728ea72aebca331a0089c5cd425597813b8861e39b898f778a937f32d915526fa108a41af7f -Clang.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/c2fd4cbaac70ce49076ae72f0d00470e -Clang.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/548bde94d5e88e7067acf4fc2658987583fb68b032f1416e41461cfdddd09ec68c38cd5529281d3ced2aea93ed57acb260c5f4597dcce057240bc12b394b873f -Clang.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/05306ecb416772494abc55ddb68c7163 -Clang.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/a49f4800efe99973b364dbf07443de0d6a3763b4535221c0fcad5c3a8076d174ae343be8afe566954c3475fbb5b54c712cb1f2c54d222715005bb94455307662 -Clang.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/4eeb4cf34bc8b95f4fdd3280d8f1f5be -Clang.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/f2ad27b4e0121992cabac4d568835a988217facf5ed6e6f095b2ee124ed2eb466b2deff53ba706ac98a3777833fb94f4a4efd692bfa775b0717af27e30d81176 -Clang.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/c39e96db6850939096c3f9d913cf0123 -Clang.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/db048395b39a55efa88656d6ba6cecd8b394b125095fc522c5438cb9f7d1dbcabc0ea5225fef7f8aea405571c2b16b5300dda23034461949da6b2e521e716c82 -Clang.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/915122da271a7fb0f486cb306a76e06a -Clang.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/2eee597113a0d94514f973f435a30a80370195388b78ba3945ac64d47baae47d4653356d7bde884a7ed13a28172b54b2bc827b6cabe09e35fe567b4770dce817 -Clang.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/49e329091e6f1d6f03576c662a4f0150 -Clang.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/dafec5d9ed6209f93a910b8162f2b77b825cc5854bbd21502f91c538b529e28207d9a0eeaf8114481638d500565f1335333f1630427d29e7e85885676d7c4174 +Clang.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.asserts.tar.gz/md5/5dce383804bd3d404b8a1936c12ba457 +Clang.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.asserts.tar.gz/sha512/5661a1cb2044ded03566c9316978595d692667fbc4e951feca658f9986a8557196557b05ccddf1b00b818aac0893696c3bbbf63a35dc9ed7df146b4488529f6a +Clang.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.tar.gz/md5/549cbc6fa28ebee446e99701aded16e8 +Clang.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.tar.gz/sha512/55eea0b514aa2e43ad2f373ad25ea4fad5219ff1cd8d5b639914c218a0a454ae9b27b8d022ae73771d8ec89fa329f5bfde538817653cc59e569b600148d56842 +Clang.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/ac3cd40e47702f306bc42d6be5826029 +Clang.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/73b217caa53702bc6fbbb3286241b7a20c111358cb9436283e9f7f9fec90436d5b54cb4c332afb7e447867a40ba46c9e3b93464acefbca7c0bb6191001525cbf +Clang.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/b1a656501493c15b98442bde584a34d7 +Clang.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/f424254cc887301d4d5b04fa71e2c7da6e4d561725d5b06278925e05be1c62a74769f19c37b431c2e2d73e7e5129acff07ac54a0b7fd381821aece27f260c116 +Clang.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/969170b1a791e89a0094154f34023e86 +Clang.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/d6ae356c9b1b80cbc5cea4eb8632b77ab3ce0d060b103cec4a5f1c73feaaf60688c2253034b2a6e132273fe04c803de93f415cbe2ef40cf1d6f6a30dcfa03af3 +Clang.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/22d599b774af41dcaa54481cc6325b1c +Clang.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/b0f257d45f1a920f46b18049b762b5a3cefdf8683c4dce46f48ce2993e6a622dbdfaaa6cc9a9cda8a7f047094a6f804091d1ba6c83e26cefc38fbd1ca5c0a536 +Clang.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/md5/f2f5064217c14700f0f933b704fff233 +Clang.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/sha512/2284978d8cfe22aa49b1f3b161c75cb0c9d43f84674ba58a1335edf818b91c6ea1684a9c3580f2e1918fdc050a624c698a4e87dc163e9076b9d6c0023c989d7a +Clang.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.tar.gz/md5/eafd72ec24ec81d42cb044e4e4d638dc +Clang.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.tar.gz/sha512/bbfc6c9179fc43a1db0ad82fc8c1fcc8ec8ce94d5c32b38cd1f88490dedc67953283995c0dd4db7262a9206431135cf2671c6ecc6580da65ba8ff4ec0323ab64 +Clang.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/md5/0432eb21283647995e35bd0d486148ab +Clang.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/sha512/561beaf45770c06b35bc1626e93a0cd89874026a8afa22017b40eb1e6ba306b05305619d42a4a2145c576b1dcc77ade80cd0bf0e0237761f3517f4db402f9b74 +Clang.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.tar.gz/md5/653b9b87f2573818d66992f969f7811e +Clang.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.tar.gz/sha512/517df570b40b51a4f4cbcecbdaacdf0b592fce66ec328139d95eaf8b63c89a1adb41a9cfe4982f5bc032fb29a6b967dc1b16b0eced98cd78756ced36ff2257d8 +Clang.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/4b1a5cf46925575bbc6765f3336e1cc8 +Clang.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/7afb23aa5ce823b1f2371e038faf311e8e21c3843cc50a0b1473038cd746fcdc77dede67130631bfaee778c3d42ac1eaa23ec664a82f43e2ad406962f3019479 +Clang.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/md5/5a6200aef0e6660bb156ecf3e53cc3c8 +Clang.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/sha512/0dc564fe753fbccfa03ac94e19828ea5ba2b8b74e7adbe7f501ac8b11d1ed8fd85a65572dcdf957018bfa1be3a6babadb1ec3937966347fe49fb38596a4b1728 +Clang.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/ad693e5cf8f2583c3311a39c095b0bf8 +Clang.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/b4e1120c960bd69f2643f185607bb2139095fa7a2f943fffec65ccad9422f2bd801131185cbeea1b75298c64cbf109fe28bae54c1b9917fe1ce8b2248d623668 +Clang.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/md5/c04cd594e25324c42d97739d72e772e1 +Clang.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/sha512/5aeeedbc3f0f8327f7760abe3eb6fda368353a7b429e31ff47a7bf42d612d070cc86f0e97031ca0c2fa9f9f448757d59b2652d89bb05b27fd380f2116a5beb6b +Clang.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/d706ad9062539a37df1e5cedc084086a +Clang.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/4862bbe0c71fe0e8cfddade0f881637ae5f58263208e1154f2284884ddf4ad43d76d98bde57904829f2218db21e4fb6ac038e231b682455fa22deeabe65f1336 +Clang.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/md5/6cc35754a4378902f9f126139bf299a5 +Clang.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/sha512/4256e9c3f58dfc896d56eeccd7495601ec585e208857de14f91e2d95295a4d03009149f49254be40b27affd5a2250323c6d0744e1ddfbd5fb924fdedc8a993d6 +Clang.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/128bb901686224fb6d32c9689c03cc21 +Clang.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/b7048ff3d8a3b3e3cddc49b2cd9fbda8ad308fe10e932e8d90000e76c12059547342a1132149254628077d0efc36b34479688b3e9f32e7364301e85a18304cf8 +Clang.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/md5/d860412ac46bdeef203a578f0bfc5b05 +Clang.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/sha512/497fa51af138b3c645d5017165aea6d33410262d2ce69e322b259b34fbdcf52a131541dbac66fae8b9a9027b70771199f9a76869721bf18760065ca7cb3b5364 +Clang.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/6fb13f1cc2aec210298c3045f8a7fd94 +Clang.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/085c94f43fb46ecc8cadfed5c5d91978c9ddb9d647eea6e82ff0a548eec53dbddc77721faaa8c43ab5b0674f83fef7aa3b34ba0dc273feabdbb8cb95bf5534ee +Clang.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/md5/63d765b268e792df2aa92f3689de23de +Clang.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/sha512/32b2397bb4b627f0ad9b00838e30c965feca902e417117d0884244a2be6a50e0d4d40e55a27a87616e33819967455f90ae0a4319c2eefefd49b82e9041835444 +Clang.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/c00e93211a1e470f1b00a53e776a9e3c +Clang.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/6621b3ab12302657ef2441482e8bc6335535964fda472ab8378221e4a9cc0813968589f457e1af66141821cdedbf8eff3080c20105eec810742e5539fc329fcf +Clang.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/md5/254fdeddad203954ec0531875cecec8c +Clang.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/sha512/84a19469231a9204a553abc58073e423173ff828445634501a61837c0e249ed003f9051fcf1da4eb16201f80d755e7bb4b7513536c749eb1e7ea78c7ded59945 +Clang.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/0475a3e401b59e1a34dcbd9d9b980823 +Clang.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/e0c9e1e18cc4f7106effaeb04e0e6f41fe8ad872d67d3d0da928ce36d1bce6be3d5231d149b2d404b3a4b99900b50d280ac6f7dd8965d30c4dcd3913590144a6 +Clang.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/md5/08c9e802640582af0b79bc04702c9771 +Clang.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/sha512/d4f413bbb5d5c3ae01cea2b87ef4e46816023fcf4373f00fca13f2edc6278eac651718feea3f8c7d04d3ef82360417dd93b6c7163d54ecd79a3811a0ed588054 +Clang.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/e7c253db924ea5cb5098be57029e009f +Clang.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/373884c492e5862aaff27f5782ba44e202e581e4faeb2cffe14bd696a590c0bc72459fccf3342aadbf189282af0c43efe3db113caa47c27c3ea556f0b3313e7e +Clang.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/md5/9c1867e316ac258d9199b389ea053d2d +Clang.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/sha512/9537f285d2a06b8c86ff21aab9daad1ba7e71bcfac55d780c693da8cc250707011ee22ed021e387422543b1e2abbc34de1a7fe49175a27a9c11e43b00549f1be +Clang.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/f9a13a80efacf45f49d6d7591d2cc3ea +Clang.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/c7edc55c4f76ae086080ba639d83793738884b9385618c52b30f5c3fadb0ed2a31bbe95ab80c5eee8504ec6301d73fc7318a8c0f877ba8b5f51170de51179d9a +Clang.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/c9911680ea55b36c4b9f59cfda2a8e33 +Clang.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/9c3722bd402627a4f51b4c98c8712a85031aa79380fe38be0db9df13a5cfabe428fcc7d5d5cf804ac4387d738cad1796bb3f341ebdcf4726ea7f699c6de586e9 +Clang.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/db82d62c163f69038364189a60b18d09 +Clang.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/5dc415426bd99dc2d7b5fc4fe3f2bb1aabc8961fc2b03a2bc14562f330b273c4d1942d7ea5f05b38c76ee753b440cc4f92015a25f9de7980aa3b1d52f7d0f2bb +Clang.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/67b7194b31f68db8ffcf5ec250948740 +Clang.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/a032c2ae911b6318ab23950ac74dc95f2c8bf815196be62e410b20cd2e271c4154f916388d119ca91c77e07853ba2c56bd5e75a4ce6742d2a7bbd9d3e61853ea +Clang.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/md5/50b4fa021c1c9b6bdb29eae63ea22103 +Clang.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/sha512/40b377df590521e5291c3f2f9daa8d60863c03253b07d0e537288324819a909ab3466b710b10b1a92ccd6f3566702c515d808f03e6d9fe9d01617b9a836bb63f +Clang.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.tar.gz/md5/d2da27ebc23793c107cb03e176f02d6e +Clang.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.tar.gz/sha512/3ed297cfd3c1ec03cbff10d7b54f9f4a374a9cf8c699287f179ebd5fa000dd525fdbed3c31b59a8ae32ef1c56115c3a84640d776f01c8a92bfae979c332043f5 +Clang.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/md5/aefacc80a5f704aa7498b35dfc2441e6 +Clang.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/sha512/76c7fd64fc4323ca442fb0aa30b236355b26328f897ea8cf3e3be029246574d150a9790ae1c45b289e4fc3050fdacc20b6d57b588a707f6d0750e6da91815edf +Clang.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.tar.gz/md5/59048d333a8a261d079673828c174d96 +Clang.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.tar.gz/sha512/bcd0c3c5e04cea24383fc2472f6190e48f8738fb7fa625ad700d1997f8aa81c9b6909af0fc38a2287b80756fbfd01300f3388c19c8df791d78ed913d8d59dee1 +Clang.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/bb4007dc5b0c0d545f457bdf35e868ee +Clang.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/2f686bdd0bbcc62aaf9e20d3804c83291ad7c41a0a174516d7a83dee7f969f7d50f19f70c0f35901a3eaa8d54fe83204d832a901586feb9eb8e141631c411b3b +Clang.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/08f088ab3498a4f7645393f43098583d +Clang.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/faf62bba3a282f218ea569d3064d6c0cefde9232d055fc3a08c994fe424f2b60dd9bbf1655f6ca101da701e3d05bd813695d6a66628ec2b6b4d11b89f773f0e4 +Clang.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/bb8f05da1e35ab358a96265f68b37f57 +Clang.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/30e3789ccca1fdc5eecaeb25345c30bc4f752cd41b8725c5279654d9b3f500d6e8693c6d1dda8b3167fcce15443682994d66922a17986419eb48bb09970f02e0 +Clang.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/ea9fdfb7c8d1a9c973ea953d4e057f0d +Clang.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/5e5d9298a12e65a7e4d401a0e404eb172c96e70fa906096f549e7eda5dbfb294189e4f3526246f28f71ba3bcf35d1bf790f05522150c5877bf8f186d8c503795 +Clang.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.asserts.tar.gz/md5/053334d0c5aabaccc81f22c1a371c9a6 +Clang.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.asserts.tar.gz/sha512/aa8daa99a4b52985d80e57d175b6fc4489058ed84f06fb2fd67710a873d5333ee77b64ed0620df099ed5617792fb3eab23d9cedf3ab3c79f4eb6f04ad1fd9588 +Clang.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.tar.gz/md5/b80918f03dcdfc5b5f1e8afa90dd4e88 +Clang.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.tar.gz/sha512/c0397541e06878535b41ba7479b603699d78f1ea3345d9a1146a0e7d17f42078e8365dc71a117981b2d2b25f35a40aeb707ff9ee8a2145303f3cb6567e82bd54 +Clang.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/78b9e190d5cb7e6fb172814eda2996f7 +Clang.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/2c9a764ba2427faa8e67285205dd1b8c211665046c9a4a19aea02de46d02a6d4287467bacd1260b7996b2b85d3e571e750d92f02c21b180abe37709ee9da78c1 +Clang.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/ba6dcd205dbd7c0301855f2a892c6467 +Clang.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/9a98c10943a8abfbe92b151f184370d21a10ce72afb22f131bd0522672c65875868357f60650122e1a2cc91254adceaf8044de4533aea08c4df400ded8c01669 +Clang.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/ce62f8e67b89c612eea35f4ba0e09d45 +Clang.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/9c3afaf0dd338abed0631b81d5f6c197b5dff6aae637996f5bc2f85f2f7dbf64a7a4bdc07dee9ab72abada5be576bb0466550280a9ee9093946a469a2b6af648 +Clang.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/543ebeb138123ce190e74cf0ad17d43f +Clang.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/aff131b5d0ed372557e3195e15701543ec32db05d5fc18117c4aee789a5cb967706d28b2dc53588bc7566f3a4498fd9e2293518ff28387466464ee07c10e9fff +Clang.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/md5/58617f16466bcb1b56b204dde697cd89 +Clang.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/sha512/bdc0c52168beabc0552ee941246b1d4506fec50913030965b374f4cedd67d6fd2b5746f04505aa5bbd4e6d61c5f684dd22c3b207e364578fd8538aef8efe0b14 +Clang.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.tar.gz/md5/aa6f0d9a455f5f0109433b9cfaa8f009 +Clang.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.tar.gz/sha512/b267bd6291fc5830ffee075af00fed9a37177141b0cdcaa8ffd602e6a8bfc58e191408c3a6a12c0fb3ea7a5d825adf1ef99122399e8246e0312b4cd056d49a2f +Clang.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/md5/ee2d7c4dc5c95e46c6d46c4fff112e9a +Clang.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/sha512/cd11acb2dccd2ac45a53fc48ee6a58299b5e54e80a5b9747c680e9b068381bf87cd388ee75cb0a51ccb1162ee8af03acd4c3f730a5f5a3ed5f443dd24ee91cde +Clang.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.tar.gz/md5/a5c16a8832f5c28346912f610932ecb4 +Clang.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.tar.gz/sha512/91b244ccd569597fe42ec45e5a62f6de0ab2c4da048b8b3ed191bbdde0a8ba5a710054d9f40c31a405a6c494a25c7546748870d1170d76e2d3b22dbb0c618e87 +Clang.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.asserts.tar.gz/md5/2d789f91744aebb0deed9b91202c1abf +Clang.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.asserts.tar.gz/sha512/fb991942325fcbfa1ad4903db43e81fcfeda5d007ee664d96a0e0d2ee5f04b5767d6ad5d37e0273f5af626efbf1c6fde84d54536b74cb17433d29b6772bcf7bc +Clang.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.tar.gz/md5/ab8fae829b5822e9123fc3d763d327e1 +Clang.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.tar.gz/sha512/1b24b03f6a81fba7400bdaa57899e9cdffd6da7e476832870460a12ab6188662c15a3cadd80ccd7dc0790834aa76ba0df098b400c87fd067eaa9f9fec0b053be +Clang.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/md5/d5638f87a6ac840d571a3973e89316cf +Clang.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/sha512/0f07e9e8dd75691ee73ab0e78a29047596a543c5886a137a7503c916ee6792cf7d6a7f279dbd864a2ad36d36aac422555d408381e3781ec004bcde5525abeb68 +Clang.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.tar.gz/md5/e777625c3c7efe2dcb029e74ac7d1ba7 +Clang.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.tar.gz/sha512/25e0a48a4d8a2ad7f5f5adb7c30429655ff496e6b5a224fc5707f092233239d4c3f4cc17432de12815e546bb595caf2a70b18ff208a53b9f0236accbd83acda3 +Clang.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/md5/22e03dc887f6e425f98cd66e0859ab2f +Clang.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/sha512/ef20886b841ba8b749ffb0c5780a9dc25d5f563ef726b1026ee77607e0572c45b8eb3470e252f882e2c4c23a2159d88ee83d31aae5081c6e4f4c37a61a7875c1 +Clang.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.tar.gz/md5/5d8f1390ff66b6b357768b1994a43d1c +Clang.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.tar.gz/sha512/5fd2fc0cf888d95c38531d236564109b284f20faed222d1feeab2beae68662073c9c59baee310e2bd67908f267416cded7b75f73e28969e2a16d2fcea0b03854 diff --git a/deps/checksums/lld b/deps/checksums/lld index 25a89e959d297..1b238fdbd1a96 100644 --- a/deps/checksums/lld +++ b/deps/checksums/lld @@ -1,116 +1,108 @@ -LLD.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/abbfdf9218efbe8fdd4664e07f1e6f00 -LLD.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/cd43f50368d708c345fc7250cbdfd236f25c9bb0f0082d5309e0d194aa4c5daf808aafa9074ce85ee11474ffee16fd5015830cd4e672281404b10f403e3bd288 -LLD.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/eeb0349137a82c86ca14b6e2d27318a2 -LLD.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/5971d1b5b4d6928261ffa2b8d4e1ed849057090a6b7d6582e5c69c0cf089fa18b3f3e8cc6de4f3733a5e4fcfaefa2fcde440a1d20df4c2f82b745dfdfbdb0644 -LLD.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/ec6f983f4ae41f7f10155acdafba1d7d -LLD.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/3fe71a15d14ccaca75cbde3b6bdfe914f537ef0a691bb9fbf98f3c09d0097b90dd9da9aabdeaa78aec216983975e822331c93e66e83516e02e315c74b062e408 -LLD.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/c71d04c38d1e814cf947998485833410 -LLD.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/5644ff89d652b2bb786cedec65238a964c744da2182107412c0d8ec71e323b1c55ee4b62999ec2382bb6e645c6de53cc3ab1ecc57f03a16b0e6b1debfb004836 -LLD.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/9d9f8cb68b812a65220bc9d2ffea0944 -LLD.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/8677f1a1d3960e484c888bffa9c19c52d3598fc332eb379d8a8f0ba8adbd7feb44e11625450da8993bb3b2566528b5264e90142ec5dcf58443e19ca14f3d6133 -LLD.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/6e90c88e855a18670d62bd6241c78564 -LLD.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/a4607a96c48295e92d324a92c5db2179a23a3e9d0181bdae26cbc7ca60db3decf3bd718af7b194f31fe9a6c0b494103b6d96d15b7c4b0f37087b37212b1497c2 -LLD.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/a19391c4dc753a30b340ba2c9084cd82 -LLD.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/ba06f94fe7b9a291764d9533124c4c9f36c29120c13c26f2fa0a1f8a304b3b20ee8784bd9d58ba7fd4d04c04a05bc2dbd230d9819fb9959a0f947b9eadc5011a -LLD.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/edd05eca7bb95da8ba3ce0f6013bb2a1 -LLD.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/ec14a7cb3be014c775a1d99564e3bf42ff3e6397435107a52958a0e3871b217d1b3e12289d794b6c15c6215998af7df366bdf6392b1d9bedcca832f881c2b07c -LLD.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/c966609ec3fe4bb9212986bc18448c7b -LLD.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/f0f6fe833e9a230e109d70dcaff968ad39a0e28f321c4f50fa4a022d0a9850aa8760daeb7467a9b4b27534f58e58add6f503fb1ac9e945c4e312777ea61225e9 -LLD.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/671db578f79cea4b1a72cd1901a07f45 -LLD.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/5bd28897d65149d19d5d7d8b096c6cd7b243a4c86dcea2648a6b7b2e846a8b757aba745f97cabc9f63b19a084204b7a7f58be2c208b18b12e9dceef9e68834dc -LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/602961c40a7194bd2afeafaef2023773 -LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/f32d73c06b4ea4444ce0e392bc272d639c53f77960247c0a8fed73f9569b6b99f4b2596b69f0333b657605198b02685ec1f3295f4ee5c6c703e7949a1a8e155e -LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/a6bdf7874e8825131077911c66d3cea2 -LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/aeb1a246b2edae8dc72b8e1f959e445ad41bb5de58fdab61f09c84d89061291ba0d16a93e6810911a4e20a7b1cef7b95fc4f0849dd298ea4586f060d594dc0ee -LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/71874632d597130e09ac021cedcafe3e -LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/811dcd2c42cceed51b0778d65bf7f0a471983f8261ccc36454acc8e819015d712ad88b9b327e8a56b7ae9cd8e2cc9df4aa0cfebe8402b9757f23d245b1140af3 -LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/83b5ca3bbc2d0cd18297dbb53fb167c8 -LLD.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/2c669fb67712cdf34f8217a517ecc6b755ff57e7f80f239865c65962a000dcba0c8619593d4bfde966354bdd11e16ebf1f0cabc6353fe98a26c6c55188be7d4e -LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/c9e8b4975f75ceafc114fa92fc7b7ee9 -LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/f5bb1f0299eb0ef03d527fab752a5d3660182bf49155ec8811d71b8c04cbdd7da3d9aa7673f81be3ad380ac4e46c98402805a0fbd14c2fffd6109d0d5091d7eb -LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/0e4e04c4c2bcfd91c5204e68bff5204a -LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/8ba4e5765f5b6c5da33b2e38fa9a33cbd9d94d03652431ec8555f07d851b732abc1958e0ecc9f572c3575adb8f5938170a96224b1b7f62c6f14a60c19388f376 -LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/003d935795c66643b40320a4ae5db113 -LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/f74cf1548a70256dc5094c52a31e68c6e66ecac1842d59bdb195f692574dc12d1a2c8f85d0010796297b8053d391c60310afd283893d80cc8d262ba12707c2eb -LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/25316c13c11368dd17dd58684bf58ce8 -LLD.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/d19eb96dad3f389a6a10e27cfdabbbf768c799202c0b8d463e1d6c06befd5725024b4c1debe1b6f18503fff1eff56f96f673ce8f176d560db24cf68b7555d179 -LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/1c5c481d3f47329d2b3d8d618b9ebb0e -LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/4cedf9e17bc537486c1b1f19ff5b72ebd5c831c7672aaee425e2ca69cfef0cf64bda1e1f2a9035dd0350b7a9f493c8113fd0b79f749fa6628487bfffb6a625ae -LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/1f5d02129718e929698b564c287c96b1 -LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/8a0233ea231fd3a6a3de5b2af6dace416de90527ba0b38fa3a67ccf73493254f530555597dc17d093b4893c32d4f698f724b933cba7af1be90ced62320589064 -LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/6d329f8f0c9395cef1fb49bac38f6d84 -LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/225537c9795ecb5544f8eb3332ecbe95773b6dcc98ecdae3d7c41b58ef9723ea910fa02434296b7372bb6db0f90d55f92c205890d3055c0899e707a2ec759bf0 -LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/1697711f610424891b474bde4ebe0597 -LLD.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/99a22ad6ce172679b9df50e6b76286bcf400fac5029f363877fd1c56d04c3c9ae6494e5ec6d62cef2aff6a3bb4195e598fd6403d70d55f6f313f56aa7e49718b -LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/7c72c26830b45fee5158a0319efc8fd5 -LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/838b7b333abb297c95441deb35e132b44a9d96bb4e5aca05ee63b67b642eaea23cc7fef37ade7265cc698d74881e84c680eb6e1c3a491e0227ca1c8767e8ff17 -LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/9c7b5c02360c3311e093e9326fa29e87 -LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/d11c5e60b1a7b1e8ee1cb99dc22080f5fc618389a8fa9877cad898f92718a6e61d0a53fc05039a7a975db433cb410f72a64e3d057669755dc127d02c4a640e0d -LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/b25f1433044cbe45b9ad733bca0f6922 -LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/960e4a5c8f699cceb4d3a57b993e98c97315ab877f88f744089e657a9dc65e68cea91a2bbbf999a88ec7da9c5ea312d3c154f4c14c4d9b92b003c93a008819e7 -LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/6f18f7e9b48d84bdb8a4d557c0c286e5 -LLD.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/fbc3124fa86776bc60fc20f15d4470c1165da4e97bc6376b2613bc4f2b283d411f191e4cfe7f8e12d189e5031d7ebd4763760afeec7968b9a3501ec090d51778 -LLD.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/fde2b3a5fd283cb5c04c42299b25ac54 -LLD.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/854ec57f923c920570242abc300eb4f06417b77dcb4891747fee9fe19d8d67a88ab19c70a31ad4ebee7a3ba3276ec5085f461e85654f095e8f10c72ab7488393 -LLD.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/061820b5ca0b3524af981a0f17adcfae -LLD.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/64e40be1fcbdd57e900d424f3d9080b228b0f3a7770a0f4f81a6ebeb1623ed97606cb4a4588fa6ac516b8dc16e128c0d1638e48d3ca20aac61e2c929d0d42793 -LLD.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/51bbd26ec8a4466c5d188302f74574f2 -LLD.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/c600217aa0d9d475ae5a75d3c4dde10abaf4874b24e600f1714f4f221b74e1f20776795ffe7c134e06bd23ea7a91f8c60c2e8e82822e38fd720a890f6b6ec8e4 -LLD.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/2d79c8f3c0bf435937fb1ab631ed907e -LLD.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/21233bad163596c4e80fdcb5faa2e8aeaa4d86f0646f50ade9a6f395cda826e1b6af004fced6dc5acd0950e83e8f39f84453c2c98c658f8f1c93165dda11ceee -LLD.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/8e41116e2d61292fcef755476f3b02f7 -LLD.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/36b8944a39ff35804894bf64f5c9a40b03a240c43d36c09238efd2d27056d2e69a18913ea23d4b5344347c4eeb51d993e80d2e581b54fd95eb14dae25bdbd82e -LLD.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/c723d619bde9504d10726b05f0cf3c5e -LLD.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/a167654c2b1d6a81d13dd32787b5cdf9fc23ffdb3bda3db597b3a795b578e17a0730b555f7ac013816f7e9e083f49e660f6f93138ba7fcd181ef69cdd378979b -LLD.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/2bbd5a6e70cb97138a52ace47dd9d059 -LLD.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/fbffb8ee87bf18d9a6ffe6c1fff4b67c2e6345faa2de3238bf87643d368d7a7adba3bb8c01c179783e47eff370f5e8f094bba60b6073a47023277961a4b11d91 -LLD.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/1bd7e097fd221a1cb209345f3b753029 -LLD.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/855bdb1c6a586f86a194cc4590b12ae9c6fb0d666966f59d16d6ae5b9c04af435a8ac95863a6f2453fdaf752769e926dc47c61a54dd398d935d15427b17dedd1 -LLD.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/9fd3429f3eedaffab75a1f1eb5387726 -LLD.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/52cee403e0463fe4052cca0695390baea91a4a05ec05245cc12b9970384e8d1a9b58e963526076da7f387c61e6bcd63066a17221460fa85bf56e3d10fe86465f -LLD.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/118ddb25eb2da940e0a24464f1dd7825 -LLD.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/9a302c9f588d4e1b80c224fe3239b6b584f2393150a6f5ee457ee88e5bce59f3c87415bee51848d5c7b742126a5404a0f1fccf812394d1d6d1c432e3bc13535e -LLD.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/e8918ebffdb6abc39156d35b5f2f9e48 -LLD.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/387085aa9c01163da2634186ca0b498d79d8a44c14e683e8fee540ee32f1ff9594a66fa3cb3c49cd49d06a9f327c37e3ad36cedfb8a862a43f278480a97da3e5 -LLD.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/caad7e36b4bd734878ffa6ac630da181 -LLD.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/67f94119d100df7c1236414b398f5bc39d9e891d32abf0b5bfc03f605841ec39a5c4a2bdb9093a8032baba6677b92378c9b5372fc7d3b0d02d7064752c24a0da -LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/ab1a611cdfdd94d862b49a8d74e4fdee -LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/ce5a3183c53f8c47c3e9f9e9a8a78653ebf475aa31bffa91e1c695fc526323835cf65a1013cdb800eac3b6e5288128846f0d7862c771c3e0f94d022ed3e98c81 -LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/b48f84396ccc0227194416432b068967 -LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/cbef6f8ab00e279980fa7de6633508bb473f292daedfc822adbc9dc1965c6887cda3eedab9444d50aeec259f92640d45c9aee099ca188be03848f524e92fd1b6 -LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/3ece870b36d697253e8f32ce7467f081 -LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/c5477fa66990b374b3cd27afc64f343042841f05f89aecde646835aee5db8e1d1e6a4c64049ed662c5bebc13ebc6730b3aa40e3cf4484d8877544ac66d651bdb -LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/07514a3db1b06f13c3965140ce7e7fda -LLD.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/2072f7626285d2b4dda2d7d6727287bb0b2048a5f5a6c46c45f3981faeb7405618d7d2375037104f5d57756f275058c5c8c561110b6adf0d5e674f5499339551 -LLD.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/d4ef5ec6287d636011640f6da7ad4882 -LLD.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/2875f3ede77081dc5fbbca3df7da171df6bca3bd339a5f74fbf1acf016203293fc46263c119981b5364e7156e92aa76a89641656132c3c1e52af9d5b62d32ccb -LLD.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/58f8723778b33cb8087cd17bb384cbc3 -LLD.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/38a3eda402f855bf8db33a84564b1dd4788b04b0048ab5070d58dd5e693c4855a97ce3052be6c775c5b8e663d57bfd30049f044e456368cc2ef69c71d463fbfb -LLD.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/c117ee72749bc5dba3802af434860679 -LLD.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/914521adb94db85b408665c3552de2dffa1247c7d7036c9df1716e0cc6401281d90378416b9af16efe04ff975f5e0cc05c9616d7c8da36e1566bee9e38347207 -LLD.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/1c7eebbd69b1ef2525be0fdd26abcf59 -LLD.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/b0cb3a271e7b4590692e37f830479d7065467bfbcaf1abf04c9148d6107def27c10a3f18ce3d6014513c8577484021d6c1f59bdf8acc87f1897e00869bb20cc7 -LLD.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/0019ccaa2c44c163e2fe7367b15f37ac -LLD.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/212bb2b4db2b9b557cb47e57f83e9d8835ac6a608226ff9ccddda217dd41c8b6932fd990073f51565dab6c893145fb6ce3d19317396bf9631778f04c8cc32014 -LLD.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/e9b0c8a37889346dd9514dd218a21bb2 -LLD.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/2ec2d3948c2a206fb46539a96b689646433f6d35142d1094a35642a55a42710121c50de0faa068dea83e124843582495c10fb7aa183573b6a9db784e8472e12f -LLD.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/3a53529de043f7cda9af0c8edbd2fe7b -LLD.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/36be33f081cefcaaaa632b731762e6d2d7b36046f2c8d47aa694a55be8ff14c2214efd99573c67d441268df906974329f0b0b39987d39d226765070f6bbd6b14 -LLD.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/31514c0017e92afd8ca35c9d8baff6f0 -LLD.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/b9d1322d6816075c18d2a2eb0c270cc66f311a45a777bd8079ab36a585942593e0b8a951282cdaae7484d6177ebf64695683814dd0a808ffc5006fa99853935c -LLD.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/e8180f2bc4193503b4234f47bad81da6 -LLD.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/b73aebc7f36657b2e99b6b1b12846a2a5cd2a630fef836658b96a7f71711b40897a9c56593859b45820c122f166441ebbc0139153de52535c66ebe432638c113 -LLD.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/5cade62c0c5966c58170fc4cc5edebe7 -LLD.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/f3aac09eb5cfda3300498eeffea5d31e413d65067394ffc1ba8b3416b8db05ef7a810d2d3f75e7a47ba8e15b0be7eb7da933f67b820150daeccb14ac78de12da -LLD.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/a31b9c5ed38ac28ad93cade4f3f3620e -LLD.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/9bc26bed1d8bf0cf3854c06397cac893d6bdc02e7c7f9736b1fa873e6afda0c4bfab3c724508382d4304137f8f7d7a77adeaead91844380254c8a35a072b3c68 -LLD.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/13da89b15a554d17f279143442ed6c2a -LLD.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/9e74f62580141f1cb09cfe21af016bc5b56128e03782a9727fb82f7f7bd802c0373750648ba4d43e6ded39180106ef3df190486029544c4a8f1d57518c45e118 -LLD.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/11806c7a732d195e84fef72d659a9055 -LLD.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/17c0c69d51b088b773d34016f66723a913d6db5ae82f5c3ae1f2b3429dd916b565a8b119d0c032f09f77105393052ab6206312789428cd52f1c397956d0a6d11 -LLD.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/1e8a08e4837a889cfca48bd7504f20e6 -LLD.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/30bdcb49693f23c439cecad3b7a66b6a6f7533d85cfb43344894c67de11809f4065784db95b968b013ab73dba0ded761e1925299e2606282c9fa1c9799e00978 -LLD.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/cc35b93f1dd6917e4975a743ec103c1b -LLD.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/9abbe63e95b358384a5188ce945a69671484ac68f07a6327ac1610555114b699f2c1b9844f31bdc83c297fb565c8270ffa030588bcdeb52576314ab1686cf07a -LLD.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/3f2c4935fc699fe54cbc1ce6071dea46 -LLD.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/f47ef724f2cecfc1c64b588d8bc6367427e3c59cfc34af6fe4a02e6d9b267814227938386313368702d69bd0e5d973d8ed517dee5b76f484087592b7fde57bea +LLD.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.asserts.tar.gz/md5/0edc0983135da9e37b18fa3fe6d56237 +LLD.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.asserts.tar.gz/sha512/2adbb4eb76e72be28951c96140070b6d16c5144f689631d51b56365549a5d38535c1dbb5e351a6bdac4648ba52da02297591874193b1c16e7078060c99d23f04 +LLD.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.tar.gz/md5/59b06fca083f1a5e9bf9517ae4f6a4d6 +LLD.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.tar.gz/sha512/7f1dc641df9288dfcd887239b86e7fe2871220b9d7f877b24b3197ab73d2176c4533decbea427b09e8f70ddc6c7570d31f5682eaed7215193e95f323769276a8 +LLD.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/c97e607a661b9ff571eba4238ec649dd +LLD.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/7c7add8a0fac379b580a19a02966adca4932bd4573ba0111262544c0d935fc121c5aadaeadc97f9564331202b08c7366ceb170bb2b318db3425c157772d283ea +LLD.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/d55ebbd25b97a4e4628fad1e04782056 +LLD.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/681729b4d10d8f66b0cdb89ca4500ee8a417561cc886608d06af0809d946bdf7cf5c6bda2b6d5d577bae3a15dc347568a3d7d7428568f86ca61327041026fbd2 +LLD.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/78b06e5a351e6eab372ae29d393ffdcf +LLD.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/37a8b5fa3491ec8ae74da88e81a0c229d38166acbb46ff3f5a819034c40fa59ca2ebf4c0ed58e615baf7bf7da789ba86114738252501cfbd842be95cc2104dd4 +LLD.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/7ba5b76c83d746a3c62354bf753db697 +LLD.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/1fa403c8923487e2d6a8e8c1d86c2ea955ed32bcde2328cb1167a315cdcf704af896505e9c44b750ffca9e3ae66e805f60831136eb79fe1c6d58eaf81a78b1a4 +LLD.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/md5/f052208026a0fd5120ea838843b244ac +LLD.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/sha512/fd9ff2d5836300bcf76e4aeefb1e57860b3203fab0c32e668dce3e636dc362876d0fba1f2c23bf55a342ac17294c73e839a8eaf065d64d4397582dc212b8b9f4 +LLD.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.tar.gz/md5/4d1077835df0f592a168c140ffe6299e +LLD.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.tar.gz/sha512/8dfd44113b817f607bc38ac1b4ffb192be340c826b9bc8f9d41e92e0f0333d8fc4227f93aaed16a4b9e94a5ec8b79628f2d3a73fb644684a595921f36ccfbeb8 +LLD.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/md5/0f31939f4ff00c572eb392b6e70aab38 +LLD.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/sha512/581441087ad4869cfdba13808b2d6adaf929ea1b38ce96c357f276d77c3e63439f8edbb822c8f41770cb61fc08837d7eed2466d187683bc44f2cb3c553e2e60e +LLD.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.tar.gz/md5/ca767173044b5a19a86c6a890dda3b05 +LLD.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.tar.gz/sha512/0577785079039b534fd736ea7a51d9b5176693d81e0bcda4fccd760d7c1218042999b6a38b973a903c0ef68e57dfb3b86e9e2f9e307dbaf603997a853f34eed3 +LLD.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/89bb950f17a5b792a6e60ef98450a6b4 +LLD.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/54bb68159743cd14ac0fce7f218a66ff6bf29e626df8dbdbd6e8581699d9b1d357a3c10d86c6822bde7299c14728bc55480f91cefd041d1de61cc179ed347b9a +LLD.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/md5/735e4dda5f8cc06934f6bda59eab21d6 +LLD.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/sha512/a9b91beed959804b9e121fee786f28808a7670fc5d2728688cca1c7e0fe56e82e47d95712e38fdfc42e02030896843c4b3df9928eb34c2aca9ac02262427c76c +LLD.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/30a95179bef252aaca41984daa54c680 +LLD.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/0302db3c04396a30d1f6ab8d8d585bbe3a9e70342f068747ddb875b024c173bb9bb34518da7e76a10d3a325dfd741118f36f67fb83251bdb8a9901c4799ad79f +LLD.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/md5/4386c746c5d9b1408dbe7df04bc6a08d +LLD.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/sha512/d71c6ebf5d3eb42368ab336cf8520afcd05470308ea117fe95797171e5c573948412ce777f62cbd45ee99ffa59cc769c276a60393a22fecffbeaf8b77b50ea35 +LLD.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/49287977de61b100979355e458c8970c +LLD.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/85ed3b2c7d2478a307a393a2003e694fc3097cc6812143abb3cbdd73a7d36bcb6f06a7d341ea639b9849f714c2d8f418a8b96035ed1c19a3957b42d005c0427a +LLD.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/md5/80a97341c9537b8a58c7df23f86d5cf4 +LLD.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/sha512/5774b246ae820de4230a1f4f65bd683145dad5cbc4d326fd75649e06e773c74c2cffd48108a79ee0cc93175786450b6d50f7ac532e6f68961c18fe6119ef94f5 +LLD.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/6f84d6858aecdfd95726a37c9b6a0e0f +LLD.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/2cdac9a810c777ec6d85093926292c75e4287f83b7224246f6fa248e3874a2078c46377cd5ccb0f36a5e25b139691f1111d705079e89ea4215c9bc8659414094 +LLD.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/md5/d40f0956cc36aa7846630755a672a91c +LLD.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/sha512/01368311a0ecfbe3f23514115f0bce7ce816c878815d937f3fa067b9daab07da0c02f520a96ad793212e5056bfb6294dd0129dae75f274dfeb48191e504c5322 +LLD.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/689120b8091b9da8cc9528c96f5c5df2 +LLD.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/ab78810af7d77116a4973b5825d5090133218cf08d5d77be14f83e028821e83493a112adf71094cc208f74cf4deabda63d7fff98866cc0304793aec9b27b7222 +LLD.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/md5/5627ccf1677c48b7ef8ac9e5faac1d20 +LLD.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/sha512/454d2636cd72974c79c2d907e56e3c69c30c3fff78b199591c9ebe4f14d04c40c4bd7331f8dc2c957c37e214da8d28ef3a47ed8d3dd4ca9d480d52bab3429b39 +LLD.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/8f50e5f684c41845308c123f8e45a0d5 +LLD.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/21baf8a00fa65473ff6cf7ef2974ef88cd5b0eadd06ff85598de10d09425074297bcff3472ef001047a5440065a2de2fc6b1eefe3a32c7c1b3e3261165dc063c +LLD.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/md5/c2e0a5f58e38a9acf2c3914177ceb827 +LLD.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/sha512/2a1653d171a2ff08bde55c53973e62955fe9d9629388ae014a645d3199d8f4bcf0fb923d06812ccd62e224032b261c8ebed56ebebed750acbc87671203d7aee5 +LLD.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/fa3959aa413a2b707d8831edd2bd7867 +LLD.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/8b74fef916a72c2f4933c21d3344410c7e03e64265a44dd62cf2ef2ac0feeafeb2b443eafa5dad3d3d0028be96b9424ff67b16391f1b3a2185826de68921adab +LLD.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/md5/b0751bf7eba4f7f7a28dc22993eac9cc +LLD.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/sha512/7510f7349b06365e9cd260229e7b8c84da26bac072c5fe9a4e59484d82a0753d4ecf1066ffe41343f881a682590dc9ee4ef4a49cd83dba45c21b8d76dfb80f67 +LLD.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/5abfe9e960bab4c8a44f41aaccaf936b +LLD.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/efda0e0a35e2774af2f2df53f89d61f146a5730086d40865d448b009c833934b23ea4b296c3dc3f2039527b72ef40493fdee6f7c630484f64cec2d1aebf4a4c1 +LLD.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/md5/bfe87378e965050b1b20e993c8b13a53 +LLD.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/sha512/ef2fd5e81f349673417bffd68c4122a87c09caed3f6f8f0235bc70b75deca7363cad68276aa708fb9ad8f7edd249d49f78d9f5fe7b226b62e8604c7bd3d4b9cc +LLD.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/4ee16f57d7dc060007250e17ffd55817 +LLD.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/27fd3a21bac676feb2c2c2363c027cf12988c70d889174e52c6bc1fcb4a93241f4bae85d5750ceba5fa971611700a9d15e3e02803cc14382cf6a1ab2918b719c +LLD.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/06699da5617371442b0539203152405d +LLD.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/83ba6300d5669b52c1913440598a2577106ea73e0b83549a5b3b0f081a94b6b8ca9fc05687d2be4b60c2d6a524bafd43b839082f0eee58b4685758061b229fde +LLD.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/a051688aa3a6383b4be4faa4f4aee985 +LLD.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/2059c6ac6579c4720e7167cd547b679a9c1a27a2c68174ed543be935ee23122234b3f2a4555de0abab3a982aba73d1751db336f3e28005ce8e4659d61f9269aa +LLD.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/600baa66310cf348ef3b4351ada014f4 +LLD.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/52b4718993d8abdca8ab701e86022367655d7927dabb8f3a8e41e43dbc90a9af78caf8abd37907a79b0f05017b6f0ef72314a187dab5bdac8ef7996e74c96e2d +LLD.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/md5/4bc599fc07e9c7c717355802c1538a6b +LLD.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/sha512/4521e40cf6cca31cc9ec8ad974c6eb922632d8ad0d5008c951e23b7ec193a71dba5f3bc2dadcfe47e2ca29395646293c6559bd88ac286c5d31d5c4521756177d +LLD.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.tar.gz/md5/462b9c453405768c2d93535fc83308b8 +LLD.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.tar.gz/sha512/39dee4d4a0073a8dc4ea63d43bc9a357bcf8e26e3c5c17f1441fa72145f5a4ff6a53e0aae6de687b8fcbace40207ba06e61cb8452c9bfff7882ab48e9f9f5ff0 +LLD.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/md5/8b12a4f5db80b925785f42a97e6489f0 +LLD.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/sha512/797d12888668712658fce85ff842d812a255fa4633bf4e78b21488867518a1fc2de746885e2fca1055595ae476670790239a714797f2322ca04027afbf27330f +LLD.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.tar.gz/md5/acb8716cf94f654078c7dce4a140f71c +LLD.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.tar.gz/sha512/cf64ae04ae3e55575d5781ad30212b1c0ec734f81b42e3c26da8766bde7c47b6a9512515997afd15f9eeef2ee326c7aa589ee1b557c45b4ef955a8afc72fd759 +LLD.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/331d844c447f564171345009764321a1 +LLD.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/978349a74fc5498408a5318c87ec6d25c01268b9d21fb85e6bb601243ad0d33be8501b181d1f9ab7663433a740912f5bcb7160caf1011b1a2c84fdd51e0fce78 +LLD.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/8595a49c49e851973fffae7c4062911d +LLD.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/f707e514843a206b53f380c7bd8d4d8203cc62219344c1234416462dc1cb3d3f8a7452ddfd0f07178d43dfb193b4402a018cc465dc76b43b687fd20fa1ea5222 +LLD.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/5b4463e81c156dabe3d182c42eb647e1 +LLD.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/995db577d4a78d62cfcfca3f1fafb333ff26548b41d8aa8d763e4705dcdfe8005e2f68873faba4040599a6d15821a523261d0451d75fdf6e1c5224e8e777a71e +LLD.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/d2f9f08cc952c0639f7ef1073c8630d6 +LLD.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/b1cab7b813fe0f7c26c55261e8561295cbdf1e812db3844b87605fb527d09855f2bef4a40ddb0a7cd354c7cbb626293d4d4012f33acc242f9af4abe1dbbbeeb7 +LLD.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.asserts.tar.gz/md5/e82e3b67a073cfa6b019bf5604eabf2a +LLD.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.asserts.tar.gz/sha512/9bb18adf78afa9dfa0054e6511f5750a9e2fa9138aeb1bd83f7a51d37d031e2f3c151463ea8f682dc7130cb98fafae0b84c60d3befe27f9d0d3dc3334ef82420 +LLD.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.tar.gz/md5/56da3cbe81ddff089ccf6b6392a9396c +LLD.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.tar.gz/sha512/2af483a1761022dcad414fa7cec7fb5c6fd54be28185e49539f4824cb0b6acdc1cfa5c78de31268dbdc444201936c5a6d2e04f39ef6f0b9fb184985ba4e3daa2 +LLD.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/15cbf5eaf89c7b834ee19629387515a5 +LLD.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/75ce7c398bdfd57af2c09dfc946b024d5a72e90575ed92f28e015e620ca89e421dfc9a391f4a78277c3e06c38dd696d572c5601a2b1866e521dbc2fc5a60da56 +LLD.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/b895da29b6082cdff6f0324179352fdf +LLD.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/e89a97dfd6c345158e3e12cdf97d33c22f849e5438401cf5a3670c0d1cf0252ca03e4c52475a42c3e6c2b2d689c2f53fc5cb7c925a23167ac51fa1a5e01e3d7f +LLD.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/7edda2d8c2eaadec2d262ded2456934a +LLD.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/0b1d60840d638c0b0269b901a3f5198e18e244da338aef2fb49b474b3601d44a2b4dec13e258909985e363ef8a8749838b01dd195e05a266ca36e6d9f274ef17 +LLD.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/e26138e3491a053ea9a998dd00ad728b +LLD.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/1215861fa52b1ee21196bbce0e99912b25f887f5734e0c2628ac78c1af5fdf57c4d7cf099cddcd7031a26c60cf141aeea66a0147428008cb485c207e90801835 +LLD.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/md5/a1e786ac775517b8b483bbe3f6571d37 +LLD.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/sha512/3937f156fc2fb8eecb13444c71f380753c16b08f29124228808c91ea4258ee2195219c4a9b601d4468cc24bd584403c16175518a620bd94a7dadff868b3771d7 +LLD.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.tar.gz/md5/976d840de14ef6ee2c0a538197fe8f10 +LLD.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.tar.gz/sha512/7f58f975dc3d69f502537aca79509bbc3c4f5da2ff8ddb1c7e27180a6bb2123713eb42da61cfabd7a48a31fc464fd74554b34935dfdb3ec095d14ff443f514f3 +LLD.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/md5/ab0295ba327cfa6b9a252b0e7a4b50a5 +LLD.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/sha512/7c750916d4157ba0a37cd1277a0f8faf32123dfc626ea76f848a7c567fd889a7801f8402a307c190ab34fc21b156f2a23967abc9972fc103e5847a200ffc7305 +LLD.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.tar.gz/md5/6827f38ed653f33953ff7ae510a517d5 +LLD.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.tar.gz/sha512/f01c655f6433ec6808b62872b8fb4c5a2d8e187643c11f0b4f5c06e2302e462353b516f431c1e26ee60b579c0f8c8c6385f018db3011c619745a39f9ef263436 +LLD.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.asserts.tar.gz/md5/385cd2715d29de3e85a3ac10bcbc88d8 +LLD.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.asserts.tar.gz/sha512/5c90e8e583176ed9dd563f794073bb344283284a10e303834b6c5a9b71369f50dfbcbac61400ff70f34f3065279c848dc29086309ad38774e50eca3fdd5f9799 +LLD.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.tar.gz/md5/241978345735e3b57a88918693c0c0db +LLD.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.tar.gz/sha512/916c6a4540ce9a2b2574d92c3aed42171f9e49f776ab97d3e5be84df832d463b7e542529c3ae81e4d6a31d5789d55b96f9559f48c0e4c8be36d70e3ff6f4292f +LLD.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/md5/a4f16e809240c1837b90d28930e3f711 +LLD.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/sha512/983201793e0f9e6416bcea23b4a70a5a1a36fbdd72bed2cc60ec267eee441aa3d9c850b4aa3da6a232f3de451089754138ecd5411e5431f632e48c1993513ef9 +LLD.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.tar.gz/md5/70f47c2be55741f754ffe89e4749dafa +LLD.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.tar.gz/sha512/f2dcf4f6ce888801e8a14875909f78b46d8ed853a7063a185356c7f21e42e15323d847d9a9d4b020481a7fcec9539d979e4c7f2b083ac1c1bf75a275a200562b +LLD.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/md5/becf7c6cc39a98cb722899c94b32ca34 +LLD.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/sha512/84818621307779e27cc149afbf958653049e47a62ca44ff78552878114c2fb0f7c40cc83722394ee8d880a6ddfdec79012235a6ed20bbfd1e5d9e83ed0a0199b +LLD.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.tar.gz/md5/0117c05f8dabf41c4628532d59cccd3b +LLD.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.tar.gz/sha512/b276dff2c54fdb6403a461ecf5435978e2cf9c9273934edcf3a31e7f640ecccf37de672f6b0b3f296ddb6a7059b0d95ca6c5bf62d62ca545cc62a69ebb84b8ce diff --git a/deps/checksums/llvm b/deps/checksums/llvm index 629ad74a7b09d..6380397ffb84f 100644 --- a/deps/checksums/llvm +++ b/deps/checksums/llvm @@ -1,119 +1,111 @@ -LLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/f8898ac241396be3f36b212dbdcc93df -LLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/cf6c73fd44d50eec0ca0846075e0c5dc49618d356d872fb631537f810301e2574d75b5d4df98d78b4b5307321d8eb726b16842fbb828df18b683303a9453341b -LLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/16d5077bfa39219e5e6bf77d0451e669 -LLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/220c1c778cf3a970db45cba327426000b19138a7a9fc663713c42ec1db67092ca31c04186b517ec23d30e4c013a6387f052a3602e989ed65e2bab537333b4c59 -LLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/cfa5f23bca017bab7a770af82c26d5f6 -LLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/7bd257f6f8f6a834404626453ad5a746c3cf26cc8f0bc14b95ff28fbe5a0420d73bba035f27787eb8a383858eeaec1b3911cacf203e0ae765c340d360e87e0b9 -LLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/cd5284fb1c68c6129c038fe346b85456 -LLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/38772e0801d1f4b953ec05ff76742c769ed3c1dab4ff3ac2ca16fec2ae7dd29a901d551750f7403b11cd0fb0b850b80448185446c2aa668125bb876758cc0a1b -LLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/58636f7b5939a19cb3161d509c9cf074 -LLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/41532bd7a420a09311e2ba6b6f429110ce619d1b06bbf8100d79cccd7fef40ee454a0740ac888f697b5aef11349f98a82730f793130de4675ce6baa7af885b6b -LLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/a32713673b9989f5ee9c199387f6c7cb -LLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/6943d9548707c5f27c50c54d5faaef4ee376c16230c8b15ec54f59ac2dd20df37d4896c9f005655dcff2dcdad5cccb17189d133e9c3a8ba68fb899dc50ce0ef7 -LLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/287d0c02efb240f08ff037f8661fa790 -LLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/26b04f55e400aa8e00fa7b5703348479c1f8a278279e26a87dccc316912a2649d600d93a3f31610e79d7a2ca3f98729c2b8cb54d2742a6f8622db21215310111 -LLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/facba02ef69ace92495691d777476930 -LLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/dc9a44162249ab597ed2e3d795c81562abbb7f182ed53c2ed367be35524e8c507d15b74a4654c0c74038e502e14df3041b644f008abd379e6c7fbbba70551491 -LLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/9cd2116f05c25e234fff49964ce2671d -LLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/ea219b1f76941fa9d712b6b8a6b3156c072c63cc0c31c003471ecefa3ed6578a8a72e8133dbc57536535ebf56de39afa6c5a1ec39427138fcd6f59684b557f88 -LLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/003f3221bd28b40ec6ab9aa09dbdf636 -LLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/7168a2ddf8c86b964d02b4dcddf5f0bde4fa1dedd907b23b2eb25a70de99798a97cf05518bfa1669cdd7794a17cee0d3fed1912f33ea0627e12b927613af4fab -LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/616c3f87030f3def3a82a12c0ab04805 -LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/5dd8fae42aebe56733035c53c9511913deee4372ab31db7b7aa088f2e43cb6a9cdb222042b7324b366a3a411ad88aec6b337231906081983e37da447de095374 -LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/7b1745ce99166f969675392d4339d2d8 -LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/d6fddd5805c5cbb454a93b16d95ea79ad8c1d5e8a4d4d2b181592b1a63a574392ab820e380199b1c85f83bdfc39822511dd4c274f3ee52a72fc12aeb3db437f3 -LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/08353c3b95d7db89754e5ff218ec7a63 -LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/7ad5dce0082e303b02fad9eb950cb0f39b9911f84b7504230026e49314ed784ee00aee085a367a460851d00471b2be15e284c084cd3d6f1256ffd604e5ed9153 -LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/d4bf8c670b24b0fa1546b0ae3cb18517 -LLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/aead293a74dba7ed80511f8b4d84791e99d1a994d690236d4cd62d5aaadf4c047a98edc33454095b065f90439b50823af440af819322bcd0d9c3c9445cc7bc68 -LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/1fbfa0e36d3cdf63460a941dff2dadc5 -LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/e4de2b1436a6e605ff9977d900b57acdd8c69caaaff3688e7e4b584e5d813df40d03e8553fb5ac10612f87c6a37a8a713360c6ecd1891f60581ecea6d0dedee2 -LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/47ba83307c3220be4c7e9eea0c7d3738 -LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/f241f7ea49645a559b08af805389a05d9b4dfe4454df88a9cdfc625c0a02a6d240d85bcd21234431a294749edb8a301cddb67d5a292da92a7b85164656ab92fb -LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/4dae8e2e6b67c664bd55ad4fa7676a15 -LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/0ec2fab4d4631f8464bb0870149b79128c24d891f63bd6552f54dfb23010ff91685fa76824540b5feabc5c437e0b07071348ecfb595d3d60251a46b81e1684f3 -LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/64a9d279e3a97c8a30de831037296ff7 -LLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/e3993f8a6196de2d823eb9fe11658c743715624376901f3608411d96bb4a474fce0ed05c2b664b1561b682ec2ca0d4f79c8e0299fe5f1e508fa098b288bee816 -LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/ee5fdf2fc8540c584fd6514c352cfc7c -LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/8c274237dd92aa45f63ab88b445486cb56a4ed243f8fc223a47894d24a0fda61a4f352013660fe7e91ea73f5c0c8f2737ccd975049ed19899598054daf110d7c -LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/3781760366096b4a7fb6993163533196 -LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/0a6710e9e24982371df4a9c7cb855e51572febd7603c5b815dff75bab71dbd56e67ceba18b62dad199fcd9d07fb52ad321db97c3ad4260d453a9a947d515d012 -LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/90bf20ef7e47aa8485c069138b0dc8ec -LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/2be47845cb039b332514cee2c2e1e5f7b139d575bdbb115eea3b92a6fbadd0a52c104b046c496afcc027cf8b7191dcbbeacf8b449796062217fa1aad5f5bb798 -LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/df0e5d4a5b0be0b24c66d9da374fcb05 -LLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/3e78b4246a324af11b08a9174a19506a4e0ffa90430d22616e5382a67d860485169b0db83058b6f35aa9e074b255326aaa649451d22eb6fb928e37db990af454 -LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/13b355afda84dbc23edd6f877290af40 -LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/2e02c2defc936f6681122820dd56aa432070f1374185a807d07c1224a8baeb653df585126553f00c1aaaf642e683e3c0948075e6a67baa963f512968f611ac2d -LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/e03360fb5ba43dbce6f26eff1772d6d2 -LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/f538f9c263f63bac5d663e89c394934ac6752f3a40780718c5b6731bfb1963fe79cf906a5435147a9253e313401afe8895d026b8f2e457241087d800b81ea151 -LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/252a43d72038e373004638bb78ba0964 -LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/b9bc5f6dbeec198574cffe5554e383162b22b95532093cc102f8e06d553ef9f75ef32989ca4998656b3bdc9d4152d692116c265a2d1ff86957d2662d04494277 -LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/181d18127d7e4058e5be7c3ed7375357 -LLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/5843ff12fe3ea9ad2d691100b58c2b49318389a418837e7f4b684e82f7c048b07abd3d5ba020bd8a86e84f6c1582ae8670d106f3286884f4b8737655e4df4f28 -LLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/872bdd2708fd4bf7cf76545b4f4017ac -LLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/d2e56fcbf4392588252625c7bd968d94d65b2db702560ad4ce4de7bcfb47d90f7784245cf7901511c85d7edcd025a06b6bc65bc621ef345d180542b4135ecf27 -LLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/82f3961594e04f578aab8ce1ed17b6ea -LLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/5a3f5d328e01b9db702f72ed36fd39dd900f7b1b80bb3264b69e13dc659d97a61d2ef146e4f1c78c7db1e6074e6ad39af3216babf3d16802543f7b55faf1e6f4 -LLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/d5f0d3995a9c24b776d8f60935cbbf03 -LLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/00b9cd1c66fa6ba88771f7b3b970aa0a179bcc765d81e7abaf56dd93c5b7e63ccbdf83db658af2ca6baadaba711cf261ce80ba4d8684b19382b9f4aef0248bec -LLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/7781851733ebaa6005d89002596ff1a6 -LLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/e56c43f4e23129edd880bc20c56be469f2efe317c8eda60797cac79686601f04960bc40daa0c520c10171193fae383c17f40753ce7be445007a2cb1e4f4d8d43 -LLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/83106fe5c9acb5ea59a2b7467136b987 -LLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/13157b1c20784aba583c26c2e1c064953debb6e29091d772187d287aad516ee5f6ab090c63aa5fcdd81738518910ad6be1d50961b006ee8d9ccf8158ca2cac16 -LLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/1fdc73b29ca46d9cf017d06bc340b523 -LLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/e456ad88c470740d65e175b1d8a01aa0dfcf934ca128079717a741d1368a8392e6ee434ac77bb7dac4e521427237526144c30cc3111c514e7e32927fb936d413 -LLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/1c72656bc3a71900f2a1d76374709a1c -LLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/4de14a92181aa877e9c18b3801bd2505115db155559772b5858279f4c65ca998a99b0226d80bdb9036a2d236a91d4e7d819378295f6c496b15973b74cb3422b2 -LLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/57ce3641a30b58f2f4db060846526d5a -LLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/304d0bf4352737cbe7c1e3691d4f155f35faca70b2ecb0ae6d524a6a4276406715c66283b6571d4779b58daed247a2cd12684be39069ae8ff25dcd71a1133056 -LLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/8fd5f48ed4777759c92a26b31ffa5f0d -LLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/98baa938c7b6171bfafbe210ad69701297bbb0f596ee0a86577f0dc21430263ce9a15ac4769273f49d3d5f16a6b43fe5d8cd06e3ed2551a6633b6f53025ddd0f -LLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/3f79c7cc30d70bd3f16d62d84cb19561 -LLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/6c97668ca3dda3abbaf8518b726b3bf5d7422711038db5b50cd9f1f7b9e97b146abdcebdcca7460c99be41efd3548077451ba34d8191bd680c789481df52c65b -LLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/43066a80bc08622907025da96b0a1542 -LLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/c1a45fa322229cada5ae3d5ac130280cf50544dee0332a93aa6814de3dde070ea00ff1c31f31957862466b215b9d0cadaf75aaecaa9b5e8fccda54ab9b45c527 -LLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/d53d5ca151b6521e2360ce7b4fba2890 -LLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/52fc82850f7567e138bccf8b4a6ebe24f090c762370f02e217c778a7eb154f9b0ad19130ded4a48e0234107605f4405b9851760bf70f74bd3a151be1c5a1f576 -LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/80f628fdb92587af5ad7f9a2472467fe -LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/6afdf26db0a7c01adf33d2f8c7a6507be0192b41caaee69742179610f23ca5ae1ded96288dc4859d9646f3282002ada7aff70651df29301f02e92596a39d9114 -LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/5caa2b8f1bb313844bf5579c2c0f9bfa -LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/448f2b0c03c5969d616143f359e8a2d1fe2788ab2f0085a16838c9b65f23e90e9d998be86fb9dbb29c62dab8394b638ff9ec5562e2e750897fdc6c60f53ec4ec -LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/07c9b26fe0d10234b1f1a2dc1b97b4fa -LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/d819a0f6e14ef32c8e2d3d7d5b87f1c0447eca34a31a6708c1bf99633580d962d2520703c5eb5b649a03a68c9ee02716eeda7754c337651e4b4a04653150771f -LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/520533c4f5ccdd0eda9bbd9ef200dd2e -LLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/b088c0d39c920d26afc67d3be9139b51a327356d8828ed8abc2726f80e1ffc6dbf63b29dad7df5cb0ac654eeabb74e0b704f6ce4dadf97165ed9d9f2bca5130c -LLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/b00b98999dfc6da61cc551c5386d6819 -LLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/c7f59618e6f30ec38adb50fee57db6ac7456d538358d62614ee73ab1c3100ca68ecab07958d8fdfc32d30cfd621615f14a5ebab64f207caee22c51d883be780d -LLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/b8de143fbe8931ebc0518ca16bd22bad -LLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/fe8bb87001f1c3fd288264fd725b037d7b976a50e89bb423c44a3643c1dbeda49b6cf2a243ef9b03b4ef9b37c3336f369c725e46ecd9096373fb944a6e6be4bb -LLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/7979d24a368752deae6d94316c5fb75c -LLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/74701aec4fda4764bb28a545f6f0657617da7ecb0550b90b6c9397b6f8e9050a5ed23f499d2e1f413bf576d9c89611ebbc9ef4d3624e7f16937d27d2d0e92d40 -LLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/fe59d3eb8174b680d18ac6b270ac311b -LLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/97ac0a051b5ecc77e6cee41434fb20824cb300364f6fe0622de8472e650e9f6c287122c451baa1b4e58ca8870a1d61ff81550075d440738235d4f2b65393b5e4 -LLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/402fcc09446abc303b49e6b8d255fcaa -LLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/cf936a78eff2e8bc24e629c353d375ba9999db1c92c6c95f06d889d779a480b6d2bd14ac9ec2e1612f736b8d70d788130b9c757a60385af9b45eac28bdddad4c -LLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/64669d77f64106423dba4a209f5d6c52 -LLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/47b3d116278315f01749a4c27f9bddb2e758c69d861e7eb43b9621ea7d632d06d06f29d83e88ba60975c42f9c2fbcf1fc8ad8f5a8ae42f363ae5fb47a48997bd -LLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/b8afba947c590c67c42cf5b9a4fd89f1 -LLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/eb3c6b082c4e8e8df2b654664bc3981f3e911a40d4d90d7d15cbbd44c27822a76fb8e64168be4a2b2ec0fbb47e1fb5954f725be7a45a71c170220191c012db95 -LLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/b22436fca5aa043c407e4154a56e3e20 -LLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/4333ef60825ba4453ca11171b43c53d38eff42cd90d2f44dec9919c6c937a2cec4aa886fb67e9a3978a043b0b61b4cca8de027709c7b23f93196d78de939ae79 -LLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/a4ba7b2429d008fa8a33968a8b1d2e3c -LLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/897a83de22b69264ed278404b1c5c38ff64d1bd1f527d7b635ad2654dc13b480115023d5e0c0b2ecaa70f8f511b8c4716866e3b46eca96874eedb9843bf85ab8 -LLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/d4a5667eba20923db7558c8b6d590058 -LLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/c8ae3ea058aad9536f9866494c32b8fbe5306d97e23a779cf1f47907fc7d5d0e7635ea4397c0670366d5da7ffd2d281fd2bf6f7977a68b6dfcb8e923d82c8f44 -LLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/87f8c1829f04fcca61fef97044b5d04b -LLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/465d2c25af6af83e2a5e849e1144dd70de9563c128abd0064facf6edee9efa53b671e1d224da5169f146285d0f18659a11ad0dc54026c70ad06bf82ab3340c53 -LLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/21702f17ddd96c8a6d964ffc0030e99a -LLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/ac70f188ad64b782fd941a9a8da01da9fced895373827e625263427d29fef9d7ea4e7ae4db6fefc8bc68c204111bb70c9c434ed9631e4a6d57bad02f4b262f19 -LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/420adc5bd7205523332379a6ceb72f82 -LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/cd898799070339f8fea29059de5f73b396915872eb055853fb32840dfd3ba740fa5fdc2c1ff1848e261986eba9c467010ae3fb6b21d5d26046bb6780fcfd640e -LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/3b60cf9b472b37733161152ae25515ea -LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/6cd22c25746826c227d35e06f4d028855d0888ebd82cca087e73fa3abf0e189bf0bd054539668bbba07cad3d06d819273813eb40e0ec1ff7965f8df8aa741764 -LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/d7cd0abdfd4abbe963de65f2f068eb61 -LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/63e06497093d70871fdbd3f2c3b3e536643ac81b32cc38182052fe2578a0b415234678392183c50a571df258c3c213e5b657535dbbee41e5f174d5105d504697 -LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/77be17016e40bd4c5816b33691d63a6a -LLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/fe9cc457bd2a24072e1767f5de441a8a22e9a113f8623d621b57f2f0890363d326b977d0fa9f7cb6e1f451f6c80127934c9fbbe734246b393953d70f85d0e3c0 +LLVM.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.asserts.tar.gz/md5/f18fa63ec97c79f3773af2bba51f69c6 +LLVM.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.asserts.tar.gz/sha512/4ee1c3e746177296fbe976976c58b6ca09dec22943ac1e63008aeed94f46619e4e60d8278566e74f4912fa9d3aa21c8b03ae2bee360db54c7dcdfa2381469148 +LLVM.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.tar.gz/md5/f482e543971546cd59d946cc33d79d5a +LLVM.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.tar.gz/sha512/d026b746f419e9bcc04daea60b1e66e26d4132e7a551b0f14c95ea95dc9a0f4e645110d8cd5b91b92bce7775ababb715747a2e4a09c0920787e2f25ef1bfbf19 +LLVM.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/5d12f50225285b180274cc89c21e7c44 +LLVM.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/3947f0d909444716a29c26a0645288e0f02ab19e6fa6ac0104c5ffc9659f01337198a5914beca2ccea7c98c9aeb12fc537891d440766054c0b9d3bbc40e24165 +LLVM.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/e555476d3324996897cb0845ca22312b +LLVM.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/a809d8c455d6f72c2bfc2517ab375d6ce329880ae33c5c1bf575dfd599d6132e38df35fac4300a0e72726ca33ae1db69ae67f5fb03d5c617eb34f7ad20f09b8d +LLVM.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/6432ac27166a0ebb550c7b000c27e2da +LLVM.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/be6440412d46252292e6d907f04193ed3f438b06419d0fb8b067a7cd89e5cd2dd9143af4605de9a2a697ec2745efbdaf6021d065346041fec3b86051de42a26b +LLVM.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/0bfd05e6bd23c92b73751a86826b288e +LLVM.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/68c08b2624bd0d38c7cfaa8b61b7e1ed70c7a106dda814f146a3f5796cbd42f476ef19f726d3ce368d89e624c7a3fa7f07829c171d79581f3cf565dba28c27de +LLVM.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/md5/53a9db6445352b44717f7e0f81d896b2 +LLVM.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/sha512/ae34208c128f1d4468d8a25b060bd1904f36a73dd0029606394061843f90aa26f9c3071e8281e76dbc10fcfd103f04602fde370a0cb04d435fe2f7a230989cb2 +LLVM.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.tar.gz/md5/f7320272ec2f3cc86a742a8ce3b4cec2 +LLVM.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.tar.gz/sha512/612f03f49b04fce2a21e3e0242c3ae591ccdf6398e31aaa63956c40fb805d4a060da8acd6e5ca1d1c0a7b1f994105ad74b1acf78490e31a149368c8a9c96c026 +LLVM.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/md5/db7b7a03c047a6aa7b599cafbf6023c0 +LLVM.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/sha512/05474495e73c56a8bf8a2459e705198a6c6e32df5b83ab153f1080a763d2f7d79dbe014592e12f0f3063b30bb0641dcfbf4f161ed988c777c8955ce9bdb89cbe +LLVM.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.tar.gz/md5/88255189a80045bb410da1eee3c277e2 +LLVM.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.tar.gz/sha512/b944ed004867d6bcf48dbc089d6ee5904318d6a2ab3a7dac3c802cb7646d4df21950a6e4bcd5bc57bbea872f99f39ef9e174dde8dfa4f5518f23a1fa0e8cf959 +LLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/a25160098b55d2ec00cde15d088343f9 +LLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/2e84a0b52a4852a69155aa4cdf33366b863caba7ced42db573e401a64c0fd2acd1d27446a3ad0ff94740a5fc4c579e745802bc32f925bb505177afdc64fb85eb +LLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/md5/10b225be9d25681a36fbffdb5f3e315f +LLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/sha512/6c38d87c8aa321fa08ff9880bb27cedda1806bf6aece891f08f757e6276dd37e450a899c4fca587bb693f683f9ad0d85f388e7c4ec4a76c96e73f0f26ff6766a +LLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/320b77cc43b91549ae0e6b538ff53f7b +LLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/6b297c643530c06be5ef1d8dc2fd47abbfaa3a7862ba42ee9e4cff1361e54aa7ce77d4d9d7f5d2db38a3c780cd38a472eba1308e1f50aba74d3de3bf188fe91a +LLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/md5/c3e0fe843bfcbe0c03a563bd40a16f0d +LLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/sha512/b62c3d8867594e34b1eb0c16f1db609c4b43146deceeabc23d4ee9af2046d8b2ae1a8566e2613a69691646d1991017f0a7d37ba8636a395d471f8f385a478479 +LLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/be03ae93d0825f335411a4039905052a +LLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/9e0159681e8ecfe477d3099314ccf2986eb2a8325cee274b6ab35e04ee9e89ea61356e5082d9adab6c41b8be98d0171e41642afca283ec59ed91267e66223c6e +LLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/md5/9e244718d094dd6b2cdc50be77a284af +LLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/sha512/705668d6b44bc754fff8f28246d8359773f29888c1f6ead6a5f1e10386c88572de27d4d47b8a1bb160211c07fcde2667833615c31ae445d1929229d981e36e3c +LLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/12162558c4c89913f0486f3a4c969c8f +LLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/dc6a48cdc9a04b3f0938784d5d40d0b453bf438881895c78a0cad9ebd83090cd9f1d12fc00df6538d053b2943a590a3217a8309aa0912fb3615d728280979276 +LLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/md5/e5012844af1fd76d6cf92ff0921a9f24 +LLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/sha512/436ace73907097593bd060ff5674db2e36f7a6e4081033b078554b76244ba0d2caea30dd94a49fb62c96f2a1c3e1f190de440bd2bb9242c1206f4794b65b30a8 +LLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/9ee929acc7c52d18a7c42808761ae233 +LLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/12f07258d295245f2b53414d0df0144c547c60b090354b5548f50bb704a82e1623e55ad353eec233407f1840a50d423d1404fc3e7b87f2386863189e7f886813 +LLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/md5/c94a2e1f4bc031a7c663111babb0f8fd +LLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/sha512/4c82406d8df72312798d95ac0d038b38eb332b4f4f8a586bca7103bdbf7759365daccb6f3bdef9a9c74a06d04a12e96c01ac9fd03aa38f3c586a7ef3c7ec7e8c +LLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/e038b8feabb2e60b866756a8dc7a5947 +LLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/c3e03bff11db87c7f131dbf7163b414cac91556795e4c5c340bec52409c39f7e91c26cb34a6339c10610d0906f57a209d36f6cfd458b26d24ffca9a43d259f5a +LLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/md5/b3bf4ff216946ad38ac6be230e0865e6 +LLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/sha512/0daba831dda378b2add9607fdc0d32046c0390a0a63758a6cdd9c0b90f660559cad0e71c5ee0b1c4264f3427e523a8c615bb87ebdfb63a65b983acfcb8df43e1 +LLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/33a3c56ab597e6f2c2863842f0103e53 +LLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/fb15d07a66b1f56b73625ead591f90b57a843cd9cb140e5158159a5f7c9249437678c61d0e19a11a65a536776dad37abd6be34ee0ec5dec7c0736079a0fcc7e6 +LLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/md5/7488ef289e45e6c44753a42dc51aad7c +LLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/sha512/6ecd62f0756a1941c8df92605a7edf9fc2e70957f39ae407e5b1b49977301ac6e82d55bcb856668135c27f1a75d156f3dfe7a27c47c6a3594c2c9f032af8ef19 +LLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/5a286dd05b936c0a3ab61722531ef5ee +LLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/80016717959246708eec8588fd6bb5cb4894bf05c2d78cd1641e31cb43f38c0fda866283dabf1d999c77d030b70b89363e2346bd9b9310a2999623e47b2e4e7f +LLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/md5/b62420d31c65fd8720427900b72f9aa4 +LLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/sha512/f63f62a667f6f2c6ea76db2b142d58cad3165a426fd420348f0831d447a9eacfda5ec9c006e05f60c1f2804e8b25e87369e754a0bace28257035a63a1ea23a76 +LLVM.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/ea922c8edae65c855e40f6ff924c35d7 +LLVM.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/d83a3737058da3c2427c061cac83ad910c43368e47bd1f9ff86c21ef0b40669946b128bd1063a8fcb081563ecf606d70a783a0747ec951c3443077b3ec8e93f8 +LLVM.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/7a20fc23311317b85127fa033cb69059 +LLVM.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/36d51f84dcb3c76556b6ee677a4f0fde1610df30a7030d1799fe9681c27e04faf1ecb4b5731db9a58060879076c037e3e5bab65faecc527296b439743bdd7d86 +LLVM.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/bf6859a7e73fb51bf91e2c7ce5b879e9 +LLVM.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/39aa6b1e2923aa572458cba58a328bf6ac0efd5f11974e04343d65cbb56fc5804066f7cedb1e9c58252313f94ee0487d6855a1714adebb3b71fd6c783a01018b +LLVM.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/10c32deaee824ed7a19dca9055a138ae +LLVM.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/b9b14c9ddc2b0b07c07a53bbd3b711737d1a7d71626d3c34812bc3862145865205e5da07b052e119aeaf54fb97968b27e86450d768312623a7a87c6b8179d872 +LLVM.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/md5/caa574701f180bf4dc323ecb441fa53d +LLVM.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/sha512/1c97d2311111f4411c3eedc6f1338a8c899932e7fc3490a03c0c9b2bc4c9a52d5797c50339ec7105d60edca951fc57c6f11bc7198c8e1c96334147d2b2dc670c +LLVM.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.tar.gz/md5/f46c39e2f848fb5fbc9f1eed7fa695af +LLVM.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.tar.gz/sha512/ed5bfd8057b2d6d543c4a11f0c1c6502dc7aafd07d0c5a96ca2b1d0c5194093f20f995ee38a4a25cc0291b31c682c6dcee460f9fb657b90be5afd43258ce4c43 +LLVM.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/md5/06533f3ac22a8a9be2501b6708821806 +LLVM.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/sha512/5284308b46ab1d8ed896e0425fae4288f87a640707c8cd5f298520cb19cea8d6311b0e6d21d5ed016f6d87f47b93d92d371abfe9bf1810b357972b7c9b437811 +LLVM.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.tar.gz/md5/f75c2acc329a9ee041ff2c81aa93b4ed +LLVM.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.tar.gz/sha512/6ec83776bac9e2cf2cbf3f890412a940c9507ba06eb50b6a05148c9c336775168cd5b6ec4aa1aa148703e6724c414830e54c3ae075e4d3649280ada705ce9816 +LLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/7e2ea1a3e9c61976b446cbceadf33193 +LLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/b21830528362115476cec7f32b11f3c1541a5779027c824882fdc00b248ea0f0ca8d08ebd86b938c10fc80a7f7930d86e2cd4482fdce33822613128eb250884c +LLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/d77b1c5ec7cb8bd02ccd24347e2e620a +LLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/84ddacf1c222bb5d895939ba4aab80dc6b5c5c596a36fcc2869a87d639d006a156750f04d268b6c10b47d286cf3bb5e8c20804174fc93881383f2512833ad7cc +LLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/26f634aff16b5c8cff48b0183f3f8ddd +LLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/cc3619c9c8adf322bb334a6b2c9de1ad088a17f117bcb9aae5b51a4f7613a50391c3478b7f892e9dcdb802067de69b098ba7d61edc9979b8f960028af0fa172b +LLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/891a1f113e7f3f8dfa56f5f28e1c8176 +LLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/9b6a4a26c8f83764f892f7caf5f09a5453ab6e89c742ae4cb1e831a0711104d131d8fe0d9a8cbdd384b2d881edb3d9026af804f47f5f79d62da1d51dad4ec0e0 +LLVM.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.asserts.tar.gz/md5/7dbc009fb3ef6ba400baaafa733afb54 +LLVM.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.asserts.tar.gz/sha512/c279c4be6a5e131b567625173b33e1f51a56c53eb0740895c1afc8b6824a00d4331df76bae9960c2143f7bfc2a9758dcbc7898fb49ef4aab56df6bba7030d636 +LLVM.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.tar.gz/md5/007fdc357a995d68a01fb45d52a92da9 +LLVM.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.tar.gz/sha512/2bf2752f654db140822f4ed74494bcdddb85f4040ae24a753ed9c77efa29d2f50397719fa20de031325823004a66ddc1c00c9624887289c8020d6627ffd21f5a +LLVM.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/fb17aeedc48fb6a24f0aa2d078ceb2f3 +LLVM.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/bd622d2472f85ac5b0cb255a929413ae3b30ee06ec7204148072dc1f9da7bf451b07960f4905a66d2673db9926797e4bc33b262cff656e7bf4cbcfd132b49868 +LLVM.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/eceea244f8fdaf61c6addac8b8f57319 +LLVM.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/44ab4a30ff65685a121dc54c2de55de441fad95b02f54cb359ad44fb298adbf48fd7651ce871fecb40b08d95e1ca701ad4c857f975a37a5e5a42280dab6fc670 +LLVM.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/b09f19c4940f6fa12ea8b5076501e297 +LLVM.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/a52da2ace1f0f2ce0090a582a267fcba526c86a88be3d8e55020ea07e00a1cbb0323f8b8b0205c9417982774fcc05d667b8330f7676dd40c869f374130dcc50c +LLVM.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/a365e7fd610b6f6ad2dda2d94a141b4b +LLVM.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/5242fa37a93dfd99720f9c4966b4f9ac164987cb8de136c01b3474860c6229538e73db7727a6c7c832f651ce7ccb97dba0082cd66da2fe812dbc8ecd44fe2cf8 +LLVM.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/md5/6645a6254d82bf854e50e47d671b192e +LLVM.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/sha512/d330eb15c34e13cad0eeb046e2e27f10eaefcf1d6cb68bc4d55668b55e3c00cfa07bccfb4292647a737ffc69bdf4070cf5a8bb1cb7f6319a1caf0faddde7aafe +LLVM.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.tar.gz/md5/4073ae0cc33b7f803644a272cd0730d2 +LLVM.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.tar.gz/sha512/2ea897e3ed3688e2ae45918db51c5a1273afabf46d01a6a27739ac951803645861c549fd438b48dcda05294a4d87b6c39778d42e916301277a0bfc1d9ce53979 +LLVM.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/md5/e223954ddf9e11830cbab24e4ed435c9 +LLVM.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/sha512/fb88bfc543ccae5cc9ef737e81757a8f7f61d1a2816501d569456fa62bd8ce30ae57b837ed32dd6d2a7c55fdc26c2c1b1a9965968f784eb3c01680f25ee5bd8e +LLVM.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.tar.gz/md5/356d2f3008be6e04843a278d182834ff +LLVM.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.tar.gz/sha512/ae5b30925cce41593a34cf2e76b606e978c352f2bc915d8869b01600c8a81547ad392fc900766db2ade06355c2d95aa473bd51dd3d45f6bf20289d9cdfbb126a +LLVM.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.asserts.tar.gz/md5/c31804464c51d1967e73f491948e2763 +LLVM.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.asserts.tar.gz/sha512/84ab795067bbe71390f15b2d700ff9e0c4fc124c3d111bdd141643242cf6dd7d3317a92d9c97ef5129ef089cfa3d703abc2b12c6a9d2287c90a9ad58a4de8478 +LLVM.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.tar.gz/md5/9f205efa80dbc9d43560830c668659b9 +LLVM.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.tar.gz/sha512/54548970bc7b3988142c1a5c2be36f877c4d2cbdb3a58dba71acd7bb32b20cab2ab12c82619abeb6b3bde9a95fb66942e08104df0fb0f59d2ead7eda957b783d +LLVM.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/md5/ab175b04b9c8dc73f2c06c06bd9d6915 +LLVM.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/sha512/c28bb2033ce2fe182f6a5a29e34a6ce4cdd22e994245f7122c4efb39cedd491c9d4343d8ba2aa8062eac156ad36d9f54605e6832feadce3c6e9f66e9ed7c760f +LLVM.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.tar.gz/md5/7e4dedc77bdcd6853d613d8b0e3e9af0 +LLVM.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.tar.gz/sha512/e09c451cf018548bb388f9a0b419496a6c6540cdf1e204be391391b3a5645c2198562c2f995c3ae30f775c786e9e59e8b93c0fbb5d00fc9ebf1529dbca9c568a +LLVM.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/md5/0835b50b6cd53b4d1fd894f27b3e072a +LLVM.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/sha512/8d228561f66feaaa96cf0af71421032f6c241e8a8ce3b8352771072d7bdd972e1b6270e15b0a4f5f4b76764cbd65ec371626cabe8607294041679fe9b6bac5f4 +LLVM.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.tar.gz/md5/bb61fbd156bb0a70184f6f425ba770a5 +LLVM.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.tar.gz/sha512/ec310cab20f39facaa6c0b3a8badded0e4ffbd7bbc1fea6b3e67717046bfe6932a94cf562d3e35dba5052d5cfe62c540c6a38477452e535da52e650fe5dd4d6c LLVMLibUnwind.v12.0.1+0.aarch64-apple-darwin.tar.gz/md5/b95ad4844e649bf46db43683b55b9f4f LLVMLibUnwind.v12.0.1+0.aarch64-apple-darwin.tar.gz/sha512/15e0996aebe6db91fe58121001aa7ea4b23685ead3c26b5d89afae34b535e34b4e801a971f4854d8e1a1fbc805cece06272470622eef863e225358113a127913 LLVMLibUnwind.v12.0.1+0.aarch64-linux-gnu.tar.gz/md5/6d8783dc9b86c9884e0877f0d8ac4167 @@ -146,123 +138,115 @@ LLVMLibUnwind.v12.0.1+0.x86_64-unknown-freebsd.tar.gz/md5/54ac594b4c8e7f261034a8 LLVMLibUnwind.v12.0.1+0.x86_64-unknown-freebsd.tar.gz/sha512/a43756afd92081e6dd7244d162862fc318b41ca110a5e8be6e4ee2d8fdfd8fb0f79961ae55e48913e055779791bd1c0ecd34fd59281fb66b3c4f24a1f44128f0 LLVMLibUnwind.v12.0.1+0.x86_64-w64-mingw32.tar.gz/md5/83cf8fc2a085a73b8af4245a82b7d32f LLVMLibUnwind.v12.0.1+0.x86_64-w64-mingw32.tar.gz/sha512/297a5c7b33bd3f57878871eccb3b9879ea5549639523a1b9db356b710cafb232906a74d668315340d60ba0c5087d3400f14ab92c3704e32e062e6b546abf7df6 -libLLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/5ea3b06e462084efd39b06e1fbf348f1 -libLLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/4a39d9aab00935c9e8fc0d7c7176ba1c5729437eabd7dcefc20a7f4d85975046bd5a2b55db38bc39a6d095e74fc490d33e825ac4abaf57a2317063e1f0dc3ac3 -libLLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/md5/357e1cea81e8413f00ec5179298d54c1 -libLLVM.v14.0.6+3.aarch64-apple-darwin-llvm_version+14.tar.gz/sha512/cf4f14bc57aa41feed2c35658c64d05dc9d91d05c0babdcd78da78453a4220cbbe497acee99ab137784f0bc888d24740583aabfca1ae68a124e6ee036c1455ca -libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/38b0c96663e2241b1b48eba04a7c5380 -libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/e6cb9eddadf4158268219a78f10c41bbc6ae55fb8a7869ac6650e01254cd5e1da1ccb3b63ac6132b5d2625da5f1af186708d0d3438be4e85121d14a0fb94dbcd -libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/00b7d3d04c7398fcfb0118c24c2322d2 -libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/6d16e52be1643e49dcfa1c6fc069c8e021acbe8cc43b769265b2413a1a261dfddcc5128f5168cfb3a363d4cd4766be81c5f285a5a7b77021d5c0dd512ab3c94c -libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/218949adbbee820dd95132b7ffdc4b55 -libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/ca1003466cc54935af1b0960d6d9ad83bec9c4a7814184f2a0c5e5aa062e95f825a40438fe3790c8fe39051f21d494866cba37a2879e684f15175652fa08c710 -libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/4d28237d915059ed2607d6999705604a -libLLVM.v14.0.6+3.aarch64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/829c4ff056b49094abd85c63bc0378845f95467a75fffce6ecb9a6b860cb3fba92f683316ca36cbead7b3abc0ce08a9b1bb87cbb37a38c97661fdfc2efaa4524 -libLLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/1da43b4df0c72775ded9a359ee4caaca -libLLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/4714bada6d77cf0a040c2853d3d0558e1f2979a31c77c51764683753444f0076b412c0c6c58646a4f3c096fd329c0095bcabaa16c8d51f8848ab385734b2a655 -libLLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/950f85ab3600d19a285e78f893f692d1 -libLLVM.v14.0.6+3.aarch64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/3e5d69f14bc6380bdb96ab32ee24091275d51f702b2e53c459350792167a4724929149f5dacf075d36f38b4bcf1bdee60db85c59df841b2174c9aee12ba99e17 -libLLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/d438de4f324509bef175a99c466c0309 -libLLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/7168eb8ca536ed40eaee9b9766cc5b3a6f7183a47cdc6e0020a80da266d024427fce8481cd7570128b9c5620a92503bd304f40e34704242f4ea0cb47399d5200 -libLLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/2eacfaab253166b936c12e1df650bd98 -libLLVM.v14.0.6+3.aarch64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/17954baa02defb2e5b5cf0e5293bac0c84a6d7a431d6e6a7c85fa0d1cf01d3bf4dd4f4ec25689d8d003606e69642be43136d2136461e6d53a0b0627a58d6cf23 -libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/f2df37b8471de9dbd843fddfc478a1ca -libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/ca26696ef29f63478bf0abf22b9965a30cf3c4f3913fb2ddf1c8fd229e7a1af46479d253966eb72e7b4e40f1fbc767477b420c77b6c3a61f45aafd769717b626 -libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/b870dcfc7c9f4703111b8a9684f03972 -libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/1e5e5252540bbbbd46df19301417936a5e8a8e4d8fb1ba18583650c5503317b915b97131aa0632b3e87d4cd07e2fff97943994bb3a3188bf8a01bd52df99fc40 -libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/ac9797450cfbc5cb3536cb8561fd620d -libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/1ef42d3fdb399605e6c6410753b663ea541b953a0ba0c721589ed8e23cbe4a45f370de41161780fd3bc0615228afe33a1bd3bbf7bdbab82e6ed16f037ad7e780 -libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/abd5fbe08c54275be7025c8a26a46c83 -libLLVM.v14.0.6+3.armv6l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/bb480ed858d737e1439ed64acdede4bceafdefe2156c55d5944264c8ce755b662f3becadacda327cefd7362fe3d77351be214f256e6a0d1893f0293c4e596077 -libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/0c5dda523e96162cd5d5480646d9156a -libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/eddeb472c042f4b4333de4d764fb71f9ef8f742be64856c6d301f3f46c8c2c057750790c76366dfa3c6e2ccaaeee667069993d7156089014d7cc969fe41534a0 -libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/79d9a1a3873c266b05efdde2c9d0b430 -libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/2bc60244ba85cb27f160c32ad28cc99351fe2a3124a4294320509be173af02ea8d246c52705206305a9d0530cbe92d12bfbaa9f667090e9698b03a1f2bcab04b -libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/700d1cc6027488259dea73ba9c727e5b -libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/47e3556d98216ec520b0687fb5b4a320f72e94e3cd640050b2d078a853083a48e3f002e6ffb6df55c9564dc5576e4d4080bbdfd3e052012f7deb989000a38b45 -libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/656928399ba9208225fd0341e1357d2b -libLLVM.v14.0.6+3.armv6l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/ee9f43c21378ab7e821f04abab248c0e48acd0c8323bcebcfef3afae6e9cd1d674f8dbd8a2f136acba76fee02ab0af081ecdce88a3390212e943877367372861 -libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/e44eb8d30b68e02b8ca6803328cb5a6d -libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/443207f8ac1c48819e2c9acbfcbdcadce31407b152fff2a5b80947c62fa2e56c1e7345481121b0b1806ec96c272574be07c402ecd2471b660f8b8b9c499fda7b -libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/md5/67f20454d56fe1ea89f5a9c9eea77e85 -libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx03-llvm_version+14.tar.gz/sha512/94688ec1cfd9f0e3ad169e7f0ded60ebb432b8f725092a1d80f34f1f3806ac2cad353b9219cd42a0f3705483aa4442830bf68ce62482ff6c418519ff6bfe7825 -libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/30edf59dbd758609fb75251f68667251 -libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/bec840012f60f96614f46e3d297fc1b91c031ac5909298c2f7127eda98306f6655b9e3c08a55121914c932e970ba96e81d871082c333c10595c477591b981afc -libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/md5/bdcae8feb5cccbf074d535c3c8a05cb1 -libLLVM.v14.0.6+3.armv7l-linux-gnueabihf-cxx11-llvm_version+14.tar.gz/sha512/09cc38ca97051fca769df60a1dc71696d9c4f55d822dfd8e5f70d83a6bd28878b5c0027c8cb97664e4667ca6f9bec8f461e4a719332ff94dd7c5f35043d7e38f -libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/md5/a53bf9ae2af47d1fb0dfa8a693beeb7f -libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.asserts.tar.gz/sha512/c11656d939570ca49f7697fe0f33c5c5057122ec6293d1bfa45ed792f20cab9affa509d59b03a98a1ebee3d0d3d5941d7bc08ff4ff0ce7401ba3c2f479f650eb -libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/md5/54f7d64625849703b352bc68dac6bd91 -libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx03-llvm_version+14.tar.gz/sha512/3bfe75c57c50e97c72085b51eb6bb0ec47d1227af5c7025d88fd50ac91b39ff9cb8ba8868a88988fe7c930e5d91e9b488e69ee38300bd51122f135ea8d55f2f7 -libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/md5/9dfda3599c6f4c1548af05e58b061440 -libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.asserts.tar.gz/sha512/20c6a5d6d2238f89f85b6700dad402321aac9c7377d4c29763a5ac616ba5b3fe42d1f83df7f3e65f7cab90190a1c44e579e045da669dc8f49e873a344c68374f -libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/md5/81e1c7d224066856ce143b23f6e4af49 -libLLVM.v14.0.6+3.armv7l-linux-musleabihf-cxx11-llvm_version+14.tar.gz/sha512/883aae9e85b79dd4890bad04b0820fca1d487483c8de44e21b042abbf9bb274ec0f58c3af626c55c51178acfbc332840fb893915913419572608b356fd8bf37b -libLLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/358d9294cc14a4f6b73e1f243598b146 -libLLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/2394b05d4949e8d20eb653385833fcd1f635fce3669b7b5ddb3db4574d001c48c8b9ddf5bad9ac08206ae4341e3ddf78a5a4c9038cc22bda95cfac1e81851830 -libLLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/0fcd35d44392cfbf706b27c6d8567119 -libLLVM.v14.0.6+3.i686-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/e7e84f56340a61c4f9a511df83a84c7ab74c6887ff505cd7824039ee31ba3a3bea88a5b40a83dd38a815f7d1373bd64d6b309b874be79f3fd66c14aef5a60778 -libLLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/812a478a131bb87e7a15639f41c02cda -libLLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/c99129eb688905766ca486d2a0c61e2227e65cfed75f3d64497768921f527b58c3f82759416cf3da0362d57f2b4bdb7b9832fd1a2d9bb2395386c39f0d472cf7 -libLLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/ca1cc07830f34f7a613f16e9e5086840 -libLLVM.v14.0.6+3.i686-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/2e75e3499adf61cea54c42a19573bcb0f2a61b0184735b473aa9f20b5097034df6d1ad82034e0b22b7aa6670271189e7dcad29e885fb62c11b298eeb552b78ff -libLLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/27529d1cddf3c343cc5c77abf4d1c395 -libLLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/74e742157b5bb9d9ba5497851ec1d9b73e46d73d79b1706404a3e0d9e69b262d3a64380cdeeaa7b37aa5fba69a3982ab482257c8ec771afbcc398a3359b19c78 -libLLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/md5/728421c5ed92612cb31050c18a10c8bc -libLLVM.v14.0.6+3.i686-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/91a5f334aa3621cf7e4691c9b2cc45b3f63839944a2b245b33fc96faed57cd21fd1aefe63e0667b25d46787b8039446129ec084f68b2130862cf8fe351822a7b -libLLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/3dc106534a7ac3c02d2429d03d70dbf3 -libLLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/51b65ac973d968e4040a7a61c8b30f03732a22d42b252df0c9d92967b5a86cf93cde821ce481c11c00f44e7479a18b1a56ab5d6c4ff253ef3d7dd3eb9b0e76b3 -libLLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/md5/a3bcb02806f93f1d4e1a5b8bc5d45c04 -libLLVM.v14.0.6+3.i686-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/135ab615b67fc014a08fa6b83b21361b2d532858051ce1f07255b606bc42c522525b1bb14e9700140e79f7655119b91aa4eada22f9680b177e8a0548d634daf8 -libLLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/fff6d4b614004e891b0b526fd3451df1 -libLLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/855f7364e7797326fdcc1c49f08ccd12f855029e1f6684a223d3a16a827c5d78fbf8697a71a8ca083e2660f456da16c243b57f87f0458f72a07df923262079a1 -libLLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/c961b55edb5bfa399ab70e2b8c80ed81 -libLLVM.v14.0.6+3.i686-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/2e65c7d6e836b679ba6039d10cb15cc5b0ac76deb2acf7a099632efe11c78db441b58ddf6f13d2215d576acf3a979b5c347f1ccc287cd6b3e107fba9df5e264d -libLLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/a910109a1423922f175ff33cd55a6373 -libLLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/853c4c0cf4e9c325be74c3dc35791915c08b69b37b2e80fb5db4b6a16afcbd208c08f97399e07e10be50a905b772aa11a59600dc85f42ed91abff114a5fc844c -libLLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/c27f58e90a431f2779be67d49657af70 -libLLVM.v14.0.6+3.i686-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/95d2cf9685e24d99136ffcb0368f3bb017e0a6bc1a863aae23690d1f80df61dc72982f6647876ac266e6e9c73425a639fa2c5f94af2a80de8d20979603cbb489 -libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/56e3e6cbae412582dcb0d4669cd5c0d8 -libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/d65f0364a9799e0df9db546dd558e117049c03e08088c144dded64b3f05f03898696b3ed568ff79b07fb9c00fbd7d6cc9f7fc4987d4d6c5e85b9037c982b3aba -libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/8046cee5793583152dd168b7b766fe11 -libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/a0d0208d383d8161a6dc71da6eeeca19780e3c4d81c85bd9e35b9bd814e75ff674a0ade49f4d42e6a3dd1b5389ecbbb3e70813b3780b3fb7f0c9ab8cf11a5952 -libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/9dfe4eefe41cd5c4089116c00bef38e6 -libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/1ede0e20bfbd468158afec5d7e68e7dc2031c68ced59ae5b2c171c65f10a32863fff74eedb350e0065efd6b88f0fbaa06e5d0458e36e5fbb1bdfdec9cde9a367 -libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/2fe7f17e4a7129318d9374992664f491 -libLLVM.v14.0.6+3.powerpc64le-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/a3d042ab4edb82b47dc6899ac00c08cac4736d5145584f8067255930b3a7f1fc01953921a1ee74d414a2f2833b18fe865e2903eb1c560e7cea7fb1edc0f8e4f6 -libLLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/md5/f4c4bbf9be3fc51d22d24ad5d4e2885c -libLLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.asserts.tar.gz/sha512/bb77696308e111acd2cabfb8e813ef4243cb60e1099f87f00cc725e66e099b6ac59d5fd21fbb663b9b0b698cc67887a38e4283429f19e965e82c5c6de231342e -libLLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/md5/995758957795ec54a179d95029769409 -libLLVM.v14.0.6+3.x86_64-apple-darwin-llvm_version+14.tar.gz/sha512/2e3f59adebc5ad837236d62c64885f72cda38facf73046ccf80ec74f590ed661efabc768d6121a8159d0107c66e02ddabb2323ff02eed6c50decf783cd57e5fd -libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/md5/34e026ee1024bf613b04d264749324cd -libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.asserts.tar.gz/sha512/177781edf0a99c34fb43ed5d1973fe20bb5da53c31236aa0d6e3e12b73643709234d82f88cd36efe65aec53965d48f001c471b8d66ef292164f6c5a43c58651b -libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/md5/08b3804953d94cee5845a00048951455 -libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx03-llvm_version+14.tar.gz/sha512/c09b58c74ac6bb9b161e15491c19ba3e59716a2c9b28a825668da51bd787927352dfbcd1db1337570559bb6d4dd12352c843b8f83d8dc95150ab184a270fd304 -libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/md5/1957335c8483c2bf36b9fdbb3d833c91 -libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.asserts.tar.gz/sha512/092478aad9420da0acbdcd5ef44ad3fa2dceb8c87e7a5f8bd043d711ad9757ee1c5f796079373ae0a1b504f41c2c015c24eb79dbeb93fbb11c2ed364646cd719 -libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/md5/1d013242b0a5178d18c67cdf382a7032 -libLLVM.v14.0.6+3.x86_64-linux-gnu-cxx11-llvm_version+14.tar.gz/sha512/f5876e35b0a68e2a2761bcf2cae6efd22636f0bc6c841cd35bf3e1217fa2879f16625981cb424831232a91b2f28ed97575097d1b59a682b041c4158d208aeeda -libLLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/md5/b9b62732f42240d3341f130e48472ebe -libLLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.asserts.tar.gz/sha512/e3bfc2c974c8b8857601af3aadc5bcd0a1dc6fc24ceea82d321d23714ebf12b7bcf34eae666ba6480e31aa9464fbf6ec9aa3905309b7ff23b7b25b848b640173 -libLLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/md5/16b9ce4c2af1c10fb0ecd4cdefb509da -libLLVM.v14.0.6+3.x86_64-linux-musl-cxx03-llvm_version+14.tar.gz/sha512/a9f11ecb1fe3c477e8187d78ff47cf9cb0bef92da0aab24ef7d25e2afb0f52bde3fce626b4314dafc08f9e3a5611fbe61c3e48b7040cbe84d3a0f3aee8015cad -libLLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/md5/048fad53865cb94f716e8503f94a37c7 -libLLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.asserts.tar.gz/sha512/39b73107638f8481e5e102afcab6d906ef1478965aa190bb3fe2e876c3d00d08f3fd141ef7cab69a65a77fc618bd00f26803a490a67c27cb82fc3e521487e0cc -libLLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/md5/e67157dadfc9fec9dbc96e4511714f5d -libLLVM.v14.0.6+3.x86_64-linux-musl-cxx11-llvm_version+14.tar.gz/sha512/33a3e5a78c43ca82698bf78f06d93f59086df684063cfb4e1ed061ba7a133457b36995447ed9fb61d5b3ed268c0b8ae41e0cc8e6a78f74ae4b918e64cec3de2a -libLLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/md5/4fd409177ea89bbb9964abec90b88157 -libLLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.asserts.tar.gz/sha512/8906304c26e9a5275daefb5d153cd49a8e58a77ec8f4320d1ccc2276a42ad0a063e55f574b489b7f51d8b8f4d324c7a67e4d6cb57f313147af5b605bc0cd7da0 -libLLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/md5/5008243388dc6ba6594500fe09b9545d -libLLVM.v14.0.6+3.x86_64-unknown-freebsd-llvm_version+14.tar.gz/sha512/90e53ba57bc919ee4e9bbb33d1ca7b019a0ccc275b54ef4acbf547b9e9da8b2639f040b06886c8abdedf95042745291eb293a4cb2b6cf8666b21de507faf29cb -libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/md5/fa8a3b8c679a80c1e954746609320b24 -libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.asserts.tar.gz/sha512/c286926ca6361153f2437ac64d50f13ae2031bc489e3a7bab17209bb381b9c1468b84afe385c51226bf9fd1631217d7cfe736e1dedceecb407289220c4907ba2 -libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/md5/eade937b5aebf503090b4a1fd64aaceb -libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx03-llvm_version+14.tar.gz/sha512/50404c2dfda3da541ea8ec21eee404cf79f065560b7aa58980e87ff993689aa5e9f031355c7a0edf73297c65ad1734398951573c53b706f814bec70933cb9062 -libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/md5/a04d985f3538fe441757c38d837df80f -libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.asserts.tar.gz/sha512/86b3d4851748650872d2c0e96cccf1fb1e49d8848cac186754c0360f1f770310046a54964fc9dc323e568c0b316a7fe7a2bc8b517c6ca973f7faf98773ac80cc -libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/md5/1477d88e814d20e1f97dc3497e073b08 -libLLVM.v14.0.6+3.x86_64-w64-mingw32-cxx11-llvm_version+14.tar.gz/sha512/412597249d593598078c57ca4d576e69012c59d013db4c452e9d3927dba7122a1608db87956a44d36aa8563de3683f2e718e81e1debff6ca3ed79c73d0dfb8f8 -llvm-julia-14.0.6-3.tar.gz/md5/6f81ab7585990c5a4be462dcab508c1c -llvm-julia-14.0.6-3.tar.gz/sha512/75f38482042256e1e07ca5d7a949b0722865967df09fb0cb0d8f6bebd8864c08ce62afcf9af680b0cbed922a1abd90407a8bd5f73f421bde747482b0c15c6691 +libLLVM.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.asserts.tar.gz/md5/c1bfb47e9a53cc612fe98505788e1838 +libLLVM.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.asserts.tar.gz/sha512/f16c9f1faa3e959d486fbb109add976c2a2018597a0b053ac3168abad074ff9c2b23874f8969f0a71c6551c8092082938bcc35ad846913a0a9965dd27d6dc876 +libLLVM.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.tar.gz/md5/cbe0859ffa50e2de82b8fe86c2540f6f +libLLVM.v15.0.7+5.aarch64-apple-darwin-llvm_version+15.tar.gz/sha512/e864e7d62eb3b62066fe14210c43b79dfab704f04381ba29fcfc2a2e2b839e8db2ad3f61bb257b64cb6a546cc45e95195089e8b734425d9d4afa3168211f6762 +libLLVM.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/352f8869f53096a566b387b885a74918 +libLLVM.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/67dc69f8b327791ab77d4082208653ca74ce2cc750d9cba833cadf4d0f311dba73dbc951d0ce088a66b06321f7addda34bd5705a6c38d4d901b5813b2d1bd37b +libLLVM.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/220916b081fea2190e372df195daf13f +libLLVM.v15.0.7+5.aarch64-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/28bc05009335d61bfec33f24c89e67412f13760de72ea9acf7a12b2abf6d89cc3f3067fddb4ce598031b054b33efcf6773b4057d5adad830ab15c88fdbe56955 +libLLVM.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/2774e9f2922e087d06e0976076d3ecf3 +libLLVM.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/2aacbce77120fa9d24fd4026220e610b70c08b36175dee70f718f4d023b0ced9f8ae9dd2d58e35b61db7ca77ae337ed6f2da6a0de70296b4160a3f8e99ecdf67 +libLLVM.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/63801b5fa51c2e75abd4b46f4ab1046c +libLLVM.v15.0.7+5.aarch64-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/eec9642a9c000d1aa3d298382a5b7c66caa81714665c7a405b416818f2e7a0cf1bedb81bc2a650452424391fe57061c33c2559abfc55bbac9b58e19d82131d5d +libLLVM.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/md5/b3b3975a9a00b0292b9ba4b7fdf5e757 +libLLVM.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/sha512/c886fff05f76053682a906dd94c6674f072206f37781b1025ec8a977eb952e0aeefcf20d76a3411e54782a6425667ee3a373f0a48d5a486fd4f37c02b0ecef78 +libLLVM.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.tar.gz/md5/88cf748f1a8086f949cb6217fcdd40b7 +libLLVM.v15.0.7+5.aarch64-linux-musl-cxx03-llvm_version+15.tar.gz/sha512/4e3d3cef71062b002406afb923f3d16508206662c3835242bf522cc7c881ea236695cee6add1b1f85a0b2708510dab2b59eafe004e67ee1d87a5970602a9d942 +libLLVM.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/md5/dae6e06bf26505fff786d0187cc5f90c +libLLVM.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/sha512/ed76e52223f84dd8c1ad7190341b167928493c2c617968aa17266c274527d18348865d9289cb82dd1c0d12240220750ac31e6c1354ddd9bc5ec2e226f360ba87 +libLLVM.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.tar.gz/md5/1bdae6507ca26b09a81c3b5b89f17908 +libLLVM.v15.0.7+5.aarch64-linux-musl-cxx11-llvm_version+15.tar.gz/sha512/b2704c0ef478467eb0fa21c7b436d6efc9602e8723bcf194dfcf6b3ac33d316b79de66c0c1c291e92f45f5bb09b6ab579a45782fa1ba3c03192177aaad6c29e1 +libLLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/8906c5b197baec7fc795256b92ca0a75 +libLLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/b79ec5ef4e59b0016784d31e51a94c9b292d19c36d66eadcfb3be6579244048b2650206836b4e017a63d84d8a0c72cd487f22ea08fd92f5b5ab4cb46d218e1a0 +libLLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/md5/bd81f6f43b54364bef1e6486c17e3dea +libLLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/sha512/892e4478e672fed55d63bfbf20a959b488e1cfafa332e2f1743cb519594526b5e0f2167b67636714dec6f43c76dcc0eb0bb2775eb43e4d898e63a0d1e78e9c94 +libLLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/6437ac1aa63c9b83c72238f4b0eaca00 +libLLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/f5e2bdb0af587e5cd55a5a2f16bf551c0e0fbadd2d9232fd5d3b2b38cdfaa80920d25903af5d79cb52a45a703a5bc07e550ca07163628cd1a79d3b3dda0d05d1 +libLLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/md5/5616fc6e683ab133ed751d60164ca894 +libLLVM.v15.0.7+5.armv6l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/sha512/40944ea809c3f4000038b7b26e6297a5ce9d2710995c57b4e0751e74dcbeed9c00b1d89d0c75bf0f0d9094fd4811f5c5ca0cc5b83f54cbe20c1b2db85de44d72 +libLLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/dcdb815f425a6ec2aca7f29f601a73b5 +libLLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/3619419dbc81807db63e5c7bd7b237a6355ec60d2aada9bf26c1d38f10b4cb87a3cb3fc9a81e7f695ed7a195d2c3c214cd9bf96d3ccca68422898be323797fb1 +libLLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/md5/ab2250406d3a69d68755b77b79b61f53 +libLLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/sha512/f5eaf02c7d19689a9cff2410269daccc00a075abde9287b025de3aff1d5b539b43001d1f2120f88c4c149af27eaf0caedb2942ae029550cc822e6af103b32960 +libLLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/77576af5b13b2916dae4e7e24760afec +libLLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/1b3708202ccebd47aecca5a7c6396799ef14c4235b0904d23d6b6b4fdd91fb6b13a1627f65211ee0283a15d96b8a68cfc962d7aa2ddf75c08f2670a767c6cfa8 +libLLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/md5/81277b7fde4cf08293f8ca956417fe05 +libLLVM.v15.0.7+5.armv6l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/sha512/72caccf9933e1790bdb0b6f6dc1ec5da6a84a5fc06336e29f2928142f3182261afd39477be913427d65655c40ddbda5ec5042c360bc49383e88c871db19b018b +libLLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/d326fe9ccfbbf179571fdcd684bb7b80 +libLLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/a34550dcbb416f79648a5c4306775f1aca041c4e8e3b269e94f960ec0925d5b7cca0ed1934b2b63b9f4437d304d658adc6c0d3e0169c629d50d7c0b5051dbb04 +libLLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/md5/5ced197907e87c470e5cc1ab08a7eedf +libLLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx03-llvm_version+15.tar.gz/sha512/b57810b718bbfb1ec48415ec3e727388bb647fa3768ee191d81fbb16248edbde9332086d445ff57ad53e9d62fb9c8fb1f8be176649350f5eb57c744404c63cb9 +libLLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/4d5133f794e0b53d563ccfc10ca42e98 +libLLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/9fc7bbb8dee022304c4aedb930318db04345987bb7ec9b78c3d488a5616680738ca2b9a9087f60b7d6cc68650234295d18c6cee4a45d1956d2240791993fe45a +libLLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/md5/e5c8eae08bd2defe76e0985687d6f057 +libLLVM.v15.0.7+5.armv7l-linux-gnueabihf-cxx11-llvm_version+15.tar.gz/sha512/d632971cd93131b90d5a26fdcd8a262f2042a2dd59a09c82a8523558f2b292f9a3f285b0a6276f0e6b255f34d855736c0bfb9f426488c5929f2abf6c0b921b73 +libLLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/md5/f0fb4b9b0257e0ead2e5aeafebb64214 +libLLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.asserts.tar.gz/sha512/1993c7d6ceb7efd93f2eb21379c046073b7d9d2460d6eab5aca26cae94bcbe07658780a2f6382a052e4d64813614078b5e582a933a0bc9a5d64d8388df98ce69 +libLLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/md5/e236983a6c801d33ead6f60140cf1ddd +libLLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx03-llvm_version+15.tar.gz/sha512/c6b44cd0d9139e0b1d47c8b17e9035099a6b360f873a2fc5c6e84c1c97dd455510f4f4262c746b47910703158fe0ceba0d19b8e6a61117d9723346f4c3e89004 +libLLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/md5/c3ad2f3774b9b7651078fa3b2dfbe7ff +libLLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.asserts.tar.gz/sha512/009561d4fecd65e35960843670048b79e70495c2cfc80a7c80614f253bea7ca46d8278d338bdf7719229fa7eb9f02299bf8bc39ace683b862ad005cfebcca0e7 +libLLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/md5/6f8d226436a2822eb7e0f25d1073925c +libLLVM.v15.0.7+5.armv7l-linux-musleabihf-cxx11-llvm_version+15.tar.gz/sha512/b63a32b1eb4a8af210f6a9511bcc4c90ad39091a6b2c50431253f4fe5e1ab304b68f79e71fe55e173449ebc96a3395dd1ee55a9a8cdd289950b609a5bec8e722 +libLLVM.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/a618c88b200fa25434e969a168b93a15 +libLLVM.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/940d6b61162bdd2d9ab5c264c9ba71789638fec646e62b9204e9304c8244d10c8a5ea3603c84bff76c5471e7f3184a21e4d1760bfe05deec80c8126a7207db04 +libLLVM.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/8a4e4c69ff51c941244d0765947dfaba +libLLVM.v15.0.7+5.i686-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/287e59ff6e8e81e1650796da7a01be568b9ef024eef0505eaa34cdaf4cfd8d798596e9396e48fafa39acab5e70c3a41129917e8ec7d625f9acb896bc4e9e7b5e +libLLVM.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/3f131f5c0e11db8de1e0268708ff17c4 +libLLVM.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/04d1371a970694c234880ccd826f6a75615793867a3ba1fdce683a844cac3c9d33a58d34163bf2141624dde71f3af0e3582effbfce679ad2134894f86ac3ce98 +libLLVM.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/8636def624785ea4b99d12c0d65bc0c3 +libLLVM.v15.0.7+5.i686-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/b8ae5cc249664d32a8dbc26a2bf180a782f51ba69126d099bb239ee94afdca7b8492a7458971cc91aef0ca55a1ca38d3bf3c8716234ded81319a2ad5ac082732 +libLLVM.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/md5/bedb9f6540966fc382de1a4544ce8c9c +libLLVM.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/sha512/527ad792c220e491fcfb46de81b9b15cf4f6a1d50cfe4435296e0f94ae4d8e53165b6f634f85e95a8c7369a1e7b3788d1683fa77b843f56dfb1264313f80dae1 +libLLVM.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.tar.gz/md5/62051888207139e47c9a0694cf4de5c6 +libLLVM.v15.0.7+5.i686-w64-mingw32-cxx03-llvm_version+15.tar.gz/sha512/034e4e272d09ae8f573d3a7e591f93dc551651c7a32e2b8923fcd7fcf36be5bb491530f4673cb9bf39a54c1527cc3e3ecab64c79e3fd7075209fd81f32f7f4f9 +libLLVM.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/md5/8543a076a97e6c72e7c514021ca5f121 +libLLVM.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/sha512/fc11ac25945adee135ebc523fe3908bcd5c5a7aa4c2a405e3dba61e0fb59502e5aef3cf4982502da7f7ee1974bcee8354ac675e0e0182f9319ea20c299813a1f +libLLVM.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.tar.gz/md5/6247a9f59f87a2b923aacdc0a7c128ca +libLLVM.v15.0.7+5.i686-w64-mingw32-cxx11-llvm_version+15.tar.gz/sha512/f13adadc653d2f8442c8ee4ecca9563d6cad5f958abf2893d8a3eda331d9ed8c33cd4a52bb721be811dec66b3b5566f038bfebbcfea620bf0094c305cd3aef0f +libLLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/873155e60b133d597cf8c40169c5745e +libLLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/a000e1fe4698d5c19bf85b048bcf76cdffea191ee281b44ffbd83230de5dd93c9efb564a51da082df070f2358d6dce423bf0d6023836217c5b34d563844d977e +libLLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/17467f361317ad56762b7e455d869724 +libLLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/62a8d601c8db178cbdaa57a23a26cd65a8f3855be40ba2966b445afc9ee223db2ed6c2fc344ea98ff129d8917c14f34ed93158633521780d52763fc4a4f2a799 +libLLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/2c094ecef656dc6c9317038b0c5a47cc +libLLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/af5972750be3de00df275a0f03c9c8a3b487a040f9bd29356457bc18661ffba9b3aa909849b24ae1c518fd2975a9b687c33353ba927f8713796a6c8eefa6e509 +libLLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/c10497e3f473e80e309d4c6102fc194d +libLLVM.v15.0.7+5.powerpc64le-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/2349230301cbebe8c7a7d7054bb4e60d991e1798dbb8bc6b8cf73350738e7058a9eb3c1067ce7d3ece1780e360080d00dd4777359742aff924d2db5c463f2a8b +libLLVM.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.asserts.tar.gz/md5/15c99e56a9e8ed664deb2d6aedeb7ea0 +libLLVM.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.asserts.tar.gz/sha512/c7d3d6d33f0fc0cad0394c02662bed2dd7d5389a6aa21027d7ebee124c3c9f5910316c44bd4485f1d45c6bb9fe12775c697a176602809bb52c8d3cfadf4f2737 +libLLVM.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.tar.gz/md5/b8d748a34a381d085c962549612a212f +libLLVM.v15.0.7+5.x86_64-apple-darwin-llvm_version+15.tar.gz/sha512/02afa1db42ff68a3ea0443ab539a7c613e5acb6170f7849cce1d36969ddad36e7546427bc55cd289df46a5fd8e83477b70941b8fd9aba0717dd861c84473da49 +libLLVM.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/md5/12f825c1c1586a8f7c9ce56e243b6bbf +libLLVM.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.asserts.tar.gz/sha512/f6c9cb33f129f1ff95800c0c88152d27e6de3fd78e01b29d75a80df9fdd8d95de70003dee0df3868215009cf434006223b488c64d6eb240f1e18799f529e980d +libLLVM.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.tar.gz/md5/19d05d46cd97714abd23b668693afe4e +libLLVM.v15.0.7+5.x86_64-linux-gnu-cxx03-llvm_version+15.tar.gz/sha512/deb786016835fb34967e474235b1ca9c2e9f0258c88394979c41654fc4487ef83ac622f1e049aed5d83da8738b8f1079b3dbc67ca788f6c68b432d7007b850e7 +libLLVM.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/md5/0fee1aea27ac30304228af1f398dbf14 +libLLVM.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.asserts.tar.gz/sha512/e14eb6fad8ef734efd5dae610cc1906901b389c7557853e7fad27c4cbf6c06614996bdd5840ee3640b9fcd8a870ea058c212bc978b6b869f4594cd8b06b42ca7 +libLLVM.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.tar.gz/md5/dc14c7faeadb0c42f4e9cffcae8c7684 +libLLVM.v15.0.7+5.x86_64-linux-gnu-cxx11-llvm_version+15.tar.gz/sha512/10ef07d1e1fe3bcf8bc52da169156ad10de7b3bd54f16bf1d694bd243bc4c86b4244643f1a71fec94b024ffa2e605141eec9b10e6e65dce5d96aee2b454fdb6a +libLLVM.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/md5/ee90487acb75a33b77f24fdb075402f9 +libLLVM.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.asserts.tar.gz/sha512/6bc8605021dbb23aa71636318468a1f81f8dbf7308d637f551132700634fea208d24608c4afb28a9609a7a866302597f684d204f718fd8cae10a616abc1b7b0a +libLLVM.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.tar.gz/md5/2c96c511ef55496a1044f63d4fdb096e +libLLVM.v15.0.7+5.x86_64-linux-musl-cxx03-llvm_version+15.tar.gz/sha512/564202d6cd321b8b058124c4623bfa7d7310a5020140f194bfecd44a25490ff9590e661bbb838b1af4f7e40fc15f88363a1510d8f7a2138f8ccc52ad76700506 +libLLVM.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/md5/555ea3150d5eeeec54b1d463380447cf +libLLVM.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.asserts.tar.gz/sha512/9da05a39e8d4d9cffffe85bc2717e105a47137682ede9cbbd2f216065ebdbb6624b68a2e120a1b87247838276cd8a501c83aec63c91673229bde8d207f651f4c +libLLVM.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.tar.gz/md5/a1f6daa0703ddcbc87b8f9d17c9ad54a +libLLVM.v15.0.7+5.x86_64-linux-musl-cxx11-llvm_version+15.tar.gz/sha512/e803ba34861b600b350bc99484adb619bd75a82162633e8d80f1456a908d42d95842f194a6752fa43e683c26356592fb94b64f7823b64edc922aca154d970288 +libLLVM.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.asserts.tar.gz/md5/364b73f29c1df14d8b942183cb113dd2 +libLLVM.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.asserts.tar.gz/sha512/c4966e3607314acbace4b31dc095b81770ac3414ac1bddb43084443191b92b2b96f6702177dec76b70be12f7a3af4797c9692cf872ea7eaf60569dc7fdd92ee4 +libLLVM.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.tar.gz/md5/d4aea085c08951e0facaa553b3c22a91 +libLLVM.v15.0.7+5.x86_64-unknown-freebsd-llvm_version+15.tar.gz/sha512/cc5cc36d50a342b5692144905ae52676fe9ff19054245152e3fff02276250604009881325cb5ef063f274b51cb2b45dcc88db0a929f6244d81cad1f241bd0c64 +libLLVM.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/md5/5cdf36e1300bbc9b032bebe5cba7bd6a +libLLVM.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.asserts.tar.gz/sha512/c732ba652aaf7a5f6aa8cd2f39088d83b78d2fe3121c4e2415bdc935b0a3ccdff7f028d3ef50f0b5f7bccff54f1fb5acbf970fc28301510d09b3f3847556c613 +libLLVM.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.tar.gz/md5/c5b335f634ec9e663a7c5d54dfeb1967 +libLLVM.v15.0.7+5.x86_64-w64-mingw32-cxx03-llvm_version+15.tar.gz/sha512/51c7b1ceb0e235d9d7db9727eb7744cbd8b2e51e189c58bfa6d3b65bc4b6e7a8224e8b7b57eeeefce01c7f65a4df48da97a975dec61fb000d83d23d15737728d +libLLVM.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/md5/822be345af871cd1d5e595b2a83bedf3 +libLLVM.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.asserts.tar.gz/sha512/fda0ff71c7a26e783436da214acc22842fe73df1f9d1d526955f4acd0794c3afa8722df8e4c9671b11948fd96e4c079fe525c9bf3e38b5119a79793a22baf16c +libLLVM.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.tar.gz/md5/1201b56c0dea9d1fd2a5ceb4d62f78a9 +libLLVM.v15.0.7+5.x86_64-w64-mingw32-cxx11-llvm_version+15.tar.gz/sha512/550c041f495a2d2439e6c4abcd4db6da06702d32046f6574f6a595fceea467ebf896635bc70d9c3e41c99b42404c87d98e3cd76a34b0f959d21284e3e4f15941 +llvm-julia-15.0.7-5.tar.gz/md5/1ffb5b00586262196d24dcc7baa4a4c0 +llvm-julia-15.0.7-5.tar.gz/sha512/5b5c93b4359cee649974bbdb5c3c191cff5ce5c3862e7cce00e2e35dd0627bf50e0aee454e67ea0fadd21c36065b7c1cae6e77abdd512fab70b71899d369cfac llvmunwind-12.0.1.tar.xz/md5/4ec327cee517fdb1f6a20e83748e2c7b llvmunwind-12.0.1.tar.xz/sha512/847b6ba03010a43f4fdbfdc49bf16d18fd18474d01584712e651b11191814bf7c1cf53475021d9ee447ed78413202b4ed97973d7bdd851d3e49f8d06f55a7af4 diff --git a/deps/clang.version b/deps/clang.version index 5e08026317a8b..d291dc8e8f8d8 100644 --- a/deps/clang.version +++ b/deps/clang.version @@ -1,4 +1,4 @@ ## jll artifact # Clang (paired with LLVM, only here as a JLL download) CLANG_JLL_NAME := Clang -CLANG_JLL_VER := 14.0.6+3 +CLANG_JLL_VER := 15.0.7+5 diff --git a/deps/lld.version b/deps/lld.version index dbb446d9e99be..d4b2a664d980c 100644 --- a/deps/lld.version +++ b/deps/lld.version @@ -1,3 +1,3 @@ ## jll artifact LLD_JLL_NAME := LLD -LLD_JLL_VER := 14.0.6+3 +LLD_JLL_VER := 15.0.7+5 diff --git a/deps/llvm-tools.version b/deps/llvm-tools.version index d809e59ff4c02..f2ecd0b33e989 100644 --- a/deps/llvm-tools.version +++ b/deps/llvm-tools.version @@ -1,5 +1,5 @@ ## jll artifact # LLVM_tools (downloads LLVM_jll to get things like `lit` and `opt`) LLVM_TOOLS_JLL_NAME := LLVM -LLVM_TOOLS_JLL_VER := 14.0.6+3 -LLVM_TOOLS_ASSERT_JLL_VER := 14.0.6+3 +LLVM_TOOLS_JLL_VER := 15.0.7+5 +LLVM_TOOLS_ASSERT_JLL_VER := 15.0.7+5 diff --git a/deps/llvm.version b/deps/llvm.version index a962a94a50e05..e35db3bd6aed2 100644 --- a/deps/llvm.version +++ b/deps/llvm.version @@ -1,8 +1,7 @@ ## jll artifact LLVM_JLL_NAME := libLLVM -LLVM_ASSERT_JLL_VER := 14.0.5+3 - +LLVM_ASSERT_JLL_VER := 15.0.7+5 ## source build -LLVM_VER := 14.0.5 -LLVM_BRANCH=julia-14.0.6-3 -LLVM_SHA1=julia-14.0.6-3 +LLVM_VER := 15.0.7 +LLVM_BRANCH=julia-15.0.7-5 +LLVM_SHA1=julia-15.0.7-5 diff --git a/stdlib/LLD_jll/Project.toml b/stdlib/LLD_jll/Project.toml index 52d4b67de3765..90d867ca0f7da 100644 --- a/stdlib/LLD_jll/Project.toml +++ b/stdlib/LLD_jll/Project.toml @@ -1,6 +1,6 @@ name = "LLD_jll" uuid = "d55e3150-da41-5e91-b323-ecfd1eec6109" -version = "14.0.6+3" +version = "15.0.7+5" [deps] Zlib_jll = "83775a58-1f1d-513f-b197-d71354ab007a" @@ -10,7 +10,7 @@ Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" [compat] julia = "1.9" -libLLVM_jll = "14.0.6" +libLLVM_jll = "15.0.7" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/stdlib/libLLVM_jll/Project.toml b/stdlib/libLLVM_jll/Project.toml index be34e53f94789..87519e5a824b0 100644 --- a/stdlib/libLLVM_jll/Project.toml +++ b/stdlib/libLLVM_jll/Project.toml @@ -1,6 +1,6 @@ name = "libLLVM_jll" uuid = "8f36deef-c2a5-5394-99ed-8e07531fb29a" -version = "14.0.6+3" +version = "15.0.7+5" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" From 9e3da19aaf9bfbf01c3967c9a5077218399ba093 Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Thu, 4 May 2023 02:47:22 +0000 Subject: [PATCH 725/775] Activate NewPM support Co-authored-by: Valentin Churavy --- src/codegen.cpp | 4 +++- src/jitlayers.h | 6 ++---- src/pipeline.cpp | 7 ++++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 2e3f7eb2bf7bb..07e7b15afc165 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -9169,7 +9169,9 @@ extern "C" JL_DLLEXPORT void jl_init_codegen_impl(void) extern "C" JL_DLLEXPORT void jl_teardown_codegen_impl() JL_NOTSAFEPOINT { // output LLVM timings and statistics - jl_ExecutionEngine->printTimers(); + // Guard against exits before we have initialized the ExecutionEngine + if (jl_ExecutionEngine) + jl_ExecutionEngine->printTimers(); PrintStatistics(); } diff --git a/src/jitlayers.h b/src/jitlayers.h index bbbcbe73f1e54..4c6921cd42dab 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -97,10 +97,8 @@ struct OptimizationOptions { }; // LLVM's new pass manager is scheduled to replace the legacy pass manager -// for middle-end IR optimizations. However, we have not qualified the new -// pass manager on our optimization pipeline yet, so this remains an optional -// define -#if defined(HAS_SANITIZER) && JL_LLVM_VERSION >= 150000 +// for middle-end IR optimizations. +#if JL_LLVM_VERSION >= 150000 #define JL_USE_NEW_PM #endif diff --git a/src/pipeline.cpp b/src/pipeline.cpp index 4403653a9d8e4..7e61171d288e6 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -361,7 +361,8 @@ static void buildFullPipeline(ModulePassManager &MPM, PassBuilder *PB, Optimizat { FunctionPassManager FPM; FPM.addPass(SROAPass()); - FPM.addPass(InstSimplifyPass()); + // SROA can duplicate PHI nodes which can block LowerSIMD + FPM.addPass(InstCombinePass()); FPM.addPass(JumpThreadingPass()); FPM.addPass(CorrelatedValuePropagationPass()); FPM.addPass(ReassociatePass()); @@ -384,7 +385,7 @@ static void buildFullPipeline(ModulePassManager &MPM, PassBuilder *PB, Optimizat #endif LPM2.addPass(LICMPass(LICMOptions())); JULIA_PASS(LPM2.addPass(JuliaLICMPass())); - LPM2.addPass(SimpleLoopUnswitchPass(true, true)); + LPM2.addPass(SimpleLoopUnswitchPass(false, true)); LPM2.addPass(LICMPass(LICMOptions())); JULIA_PASS(LPM2.addPass(JuliaLICMPass())); //LICM needs MemorySSA now, so we must use it @@ -397,11 +398,11 @@ static void buildFullPipeline(ModulePassManager &MPM, PassBuilder *PB, Optimizat LPM.addPass(LoopIdiomRecognizePass()); LPM.addPass(IndVarSimplifyPass()); LPM.addPass(LoopDeletionPass()); + LPM.addPass(LoopFullUnrollPass()); invokeLoopOptimizerEndCallbacks(LPM, PB, O); //We don't know if the loop end callbacks support MSSA FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM), /*UseMemorySSA = */false)); } - FPM.addPass(LoopUnrollPass(LoopUnrollOptions().setRuntime(false))); JULIA_PASS(FPM.addPass(AllocOptPass())); FPM.addPass(SROAPass()); FPM.addPass(InstSimplifyPass()); From 2ddbb5abb93045eeb4513e223c86e9c25fa774a4 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Wed, 26 Apr 2023 20:49:16 -0400 Subject: [PATCH 726/775] Fix tests and static analyzer for LLVM 15 Co-authored-by: Gabriel Baraldi Co-authored-by: Prem Chintalapudi --- src/llvm-alloc-opt.cpp | 3 ++ src/llvm-late-gc-lowering.cpp | 1 + src/llvm-lower-handlers.cpp | 1 + src/llvm-multiversioning.cpp | 3 ++ src/llvm-ptls.cpp | 2 + test/clangsa/MissingRoots.c | 3 ++ test/cmdlineargs.jl | 10 ++-- test/llvmpasses/pipeline-o2-broadcast.jl | 68 ++++++++++++++---------- test/llvmpasses/pipeline-o2.jl | 6 +-- 9 files changed, 62 insertions(+), 35 deletions(-) diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index 1a524cbe8d419..bb6de67f347ff 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -1138,9 +1138,12 @@ void Optimizer::splitOnStack(CallInst *orig_inst) ref->setOrdering(AtomicOrdering::NotAtomic); operands.push_back(ref); } +#ifndef __clang_analyzer__ + // FIXME: SA finds "Called C++ object pointer is null" inside the LLVM code. auto new_call = builder.CreateCall(pass.gc_preserve_begin_func, operands); new_call->takeName(call); call->replaceAllUsesWith(new_call); +#endif call->eraseFromParent(); return; } diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index a836ff1361768..ac70685e7431b 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -1262,6 +1262,7 @@ static bool isLoadFromConstGV(LoadInst *LI, bool &task_local, PhiSet *seen) // We only emit single slot GV in codegen // but LLVM global merging can change the pointer operands to GEPs/bitcasts auto load_base = LI->getPointerOperand()->stripInBoundsOffsets(); + assert(load_base); // Static analyzer auto gv = dyn_cast(load_base); if (isTBAA(LI->getMetadata(LLVMContext::MD_tbaa), {"jtbaa_immut", "jtbaa_const", "jtbaa_datatype"})) { diff --git a/src/llvm-lower-handlers.cpp b/src/llvm-lower-handlers.cpp index 919128769019b..39a36bfc3ba76 100644 --- a/src/llvm-lower-handlers.cpp +++ b/src/llvm-lower-handlers.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 21a090724802a..cdba03047a4b7 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -14,11 +14,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -779,6 +781,7 @@ static Value *rewrite_inst_use(const Stack& stack, Type *T_size, Value *replace, replace = inst; continue; } + assert(val); unsigned nargs = val->getNumOperands(); args.resize(nargs); for (unsigned j = 0; j < nargs; j++) { diff --git a/src/llvm-ptls.cpp b/src/llvm-ptls.cpp index 8174832b3cebf..a628710916327 100644 --- a/src/llvm-ptls.cpp +++ b/src/llvm-ptls.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -161,6 +162,7 @@ void LowerPTLS::fix_pgcstack_use(CallInst *pgcstack, Function *pgcstack_getter, SmallVector Weights{9, 1}; TerminatorInst *fastTerm; TerminatorInst *slowTerm; + assert(pgcstack->getType()); // Static analyzer auto cmp = new ICmpInst(phi, CmpInst::ICMP_NE, pgcstack, Constant::getNullValue(pgcstack->getType())); SplitBlockAndInsertIfThenElse(cmp, phi, &fastTerm, &slowTerm, MDB.createBranchWeights(Weights)); diff --git a/test/clangsa/MissingRoots.c b/test/clangsa/MissingRoots.c index f0b32c54bc7b8..0ff5e633622ce 100644 --- a/test/clangsa/MissingRoots.c +++ b/test/clangsa/MissingRoots.c @@ -352,6 +352,9 @@ void assoc_exact_broken(jl_value_t **args, size_t n, int8_t offs, size_t world) } */ +// declare +jl_typemap_level_t *jl_new_typemap_level(void); + void assoc_exact_ok(jl_value_t *args1, jl_value_t **args, size_t n, int8_t offs, size_t world) { jl_typemap_level_t *cache = jl_new_typemap_level(); JL_GC_PUSH1(&cache); diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 389b195d97935..1d04926ef23af 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -188,10 +188,12 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` @test contains(v[2], r"enable-tail-merge + = 1") @test isempty(v[3]) end - @testset let v = readchomperrors(setenv(`$exename -e 0`, "JULIA_LLVM_ARGS" => "-print-options -enable-tail-merge=1 -enable-tail-merge=1", "HOME" => homedir())) - @test !v[1] - @test isempty(v[2]) - @test v[3] == "julia: for the --enable-tail-merge option: may only occur zero or one times!" + if Base.libllvm_version < v"15" #LLVM over 15 doesn't care for multiple options + @testset let v = readchomperrors(setenv(`$exename -e 0`, "JULIA_LLVM_ARGS" => "-print-options -enable-tail-merge=1 -enable-tail-merge=1", "HOME" => homedir())) + @test !v[1] + @test isempty(v[2]) + @test v[3] == "julia: for the --enable-tail-merge option: may only occur zero or one times!" + end end end diff --git a/test/llvmpasses/pipeline-o2-broadcast.jl b/test/llvmpasses/pipeline-o2-broadcast.jl index 47c2a989737e7..584e8855f0f8c 100644 --- a/test/llvmpasses/pipeline-o2-broadcast.jl +++ b/test/llvmpasses/pipeline-o2-broadcast.jl @@ -9,26 +9,30 @@ include(joinpath("..", "testhelpers", "llvmpasses.jl")) # COM: Float32 # CHECK: @japi1_prod_v_vT -# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x float> -# CHECK: fmul <[[VSCALE]][[VEC_FACTOR]] x float> +# COM: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x float> +# COM: fmul <[[VSCALE]][[VEC_FACTOR]] x float> +# CHECK: fmul <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x float> # CHECK: store <[[VSCALE]][[VEC_FACTOR]] x float> # COM: Float64 # CHECK: @japi1_prod_v_vT -# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x double> -# CHECK: fmul <[[VSCALE]][[VEC_FACTOR]] x double> +# COM: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x double> +# COM: fmul <[[VSCALE]][[VEC_FACTOR]] x double> +# CHECK: fmul <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x double> # CHECK: store <[[VSCALE]][[VEC_FACTOR]] x double> # COM: Int32 # CHECK: @japi1_prod_v_vT -# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i32> -# CHECK: mul <[[VSCALE]][[VEC_FACTOR]] x i32> +# COM: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i32> +# COM: mul <[[VSCALE]][[VEC_FACTOR]] x i32> +# CHECK: mul <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i32> # CHECK: store <[[VSCALE]][[VEC_FACTOR]] x i32> # COM: Int64 # CHECK: @japi1_prod_v_vT -# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i64> -# CHECK: mul <[[VSCALE]][[VEC_FACTOR]] x i64> +# COM: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i64> +# COM: mul <[[VSCALE]][[VEC_FACTOR]] x i64> +# CHECK: mul <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i64> # CHECK: store <[[VSCALE]][[VEC_FACTOR]] x i64> function prod_v_vT(R, x, y) @@ -39,26 +43,30 @@ end # COM: Float32 # CHECK: @japi1_prod_vT_v -# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x float> -# CHECK: fmul <[[VSCALE]][[VEC_FACTOR]] x float> +# COM: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x float> +# COM: fmul <[[VSCALE]][[VEC_FACTOR]] x float> +# CHECK: fmul <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x float> # CHECK: store <[[VSCALE]][[VEC_FACTOR]] x float> # COM: Float64 # CHECK: @japi1_prod_vT_v -# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x double> -# CHECK: fmul <[[VSCALE]][[VEC_FACTOR]] x double> +# COM: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x double> +# COM: fmul <[[VSCALE]][[VEC_FACTOR]] x double> +# CHECK: fmul <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x double> # CHECK: store <[[VSCALE]][[VEC_FACTOR]] x double> # COM: Int32 # CHECK: @japi1_prod_vT_v -# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i32> -# CHECK: mul <[[VSCALE]][[VEC_FACTOR]] x i32> +# COM: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i32> +# COM: mul <[[VSCALE]][[VEC_FACTOR]] x i32> +# CHECK: mul <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i32> # CHECK: store <[[VSCALE]][[VEC_FACTOR]] x i32> # COM: Int64 # CHECK: @japi1_prod_vT_v -# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i64> -# CHECK: mul <[[VSCALE]][[VEC_FACTOR]] x i64> +# COM: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i64> +# COM: mul <[[VSCALE]][[VEC_FACTOR]] x i64> +# CHECK: mul <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i64> # CHECK: store <[[VSCALE]][[VEC_FACTOR]] x i64> function prod_vT_v(R, x, y) @@ -69,27 +77,31 @@ end # COM: Float32 # CHECK: @japi1_prod_v_M_vT -# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x float> -# CHECK: fmul <[[VSCALE]][[VEC_FACTOR]] x float> -# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x float> +# COM: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x float> +# COM: fmul <[[VSCALE]][[VEC_FACTOR]] x float> +# XFAIL-CHECK: fmul <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x float> +# XFAIL-CHECK: store <[[VSCALE]][[VEC_FACTOR]] x float> # COM: Float64 # CHECK: @japi1_prod_v_M_vT -# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x double> -# CHECK: fmul <[[VSCALE]][[VEC_FACTOR]] x double> -# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x double> +# COM: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x double> +# COM: fmul <[[VSCALE]][[VEC_FACTOR]] x double> +# XFAIL-CHECK: fmul <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x double> +# XFAIL-CHECK: store <[[VSCALE]][[VEC_FACTOR]] x double> # COM: Int32 # CHECK: @japi1_prod_v_M_vT -# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i32> -# CHECK: mul <[[VSCALE]][[VEC_FACTOR]] x i32> -# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x i32> +# COM: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i32> +# COM: mul <[[VSCALE]][[VEC_FACTOR]] x i32> +# XFAIL-CHECK: mul <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i32> +# XFAIL-CHECK: store <[[VSCALE]][[VEC_FACTOR]] x i32> # COM: Int64 # CHECK: @japi1_prod_v_M_vT -# CHECK: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i64> -# CHECK: mul <[[VSCALE]][[VEC_FACTOR]] x i64> -# CHECK: store <[[VSCALE]][[VEC_FACTOR]] x i64> +# COM: load <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i64> +# COM: mul <[[VSCALE]][[VEC_FACTOR]] x i64> +# XFAIL-CHECK: mul <[[VSCALE:(vscale x )?]][[VEC_FACTOR:[0-9]+]] x i64> +# XFAIL-CHECK: store <[[VSCALE]][[VEC_FACTOR]] x i64> function prod_v_M_vT(R, x, M, y) R .= x .* M .* y' diff --git a/test/llvmpasses/pipeline-o2.jl b/test/llvmpasses/pipeline-o2.jl index 2996a44de62b3..fcb2161de7614 100644 --- a/test/llvmpasses/pipeline-o2.jl +++ b/test/llvmpasses/pipeline-o2.jl @@ -78,21 +78,21 @@ end # COM: memset checks # COM: INT64 -# ALL-LABEL: define nonnull {} addrspace(10)* @julia_zeros +# ALL: define {{.*}} @julia_zeros # ALL-NOT: bounds_error # COM: memset is not used with bounds checks on (too late in the pipeline) # BC_OFF: llvm.memset # BC_AUTO: llvm.memset # COM: INT32 -# ALL-LABEL: define nonnull {} addrspace(10)* @julia_zeros +# ALL: define {{.*}} @julia_zeros # ALL-NOT: bounds_error # COM: memset is not used with bounds checks on (too late in the pipeline) # BC_OFF: llvm.memset # BC_AUTO: llvm.memset # COM: INT16 -# ALL-LABEL: define nonnull {} addrspace(10)* @julia_zeros +# ALL: define {{.*}} @julia_zeros # ALL-NOT: bounds_error # COM: memset is not used with bounds checks on (too late in the pipeline) # BC_OFF: llvm.memset From 77c13ad59364189386114b546a7482dbe2edf233 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Wed, 10 May 2023 10:51:16 -0400 Subject: [PATCH 727/775] Reenable NonTrivial Loop Unswitch --- src/codegen.cpp | 3 --- src/pipeline.cpp | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 07e7b15afc165..ae306d3d1cdb5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -9073,9 +9073,6 @@ extern "C" void jl_init_llvm(void) clopt = llvmopts.lookup("unswitch-threshold"); if (clopt->getNumOccurrences() == 0) cl::ProvidePositionalOption(clopt, "100", 1); - clopt = llvmopts.lookup("enable-unswitch-cost-multiplier"); - if (clopt->getNumOccurrences() == 0) - cl::ProvidePositionalOption(clopt, "false", 1); #endif // if the patch adding this option has been applied, lower its limit to provide // better DAGCombiner performance. diff --git a/src/pipeline.cpp b/src/pipeline.cpp index 7e61171d288e6..6e6a9a3c37d02 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -385,7 +385,7 @@ static void buildFullPipeline(ModulePassManager &MPM, PassBuilder *PB, Optimizat #endif LPM2.addPass(LICMPass(LICMOptions())); JULIA_PASS(LPM2.addPass(JuliaLICMPass())); - LPM2.addPass(SimpleLoopUnswitchPass(false, true)); + LPM2.addPass(SimpleLoopUnswitchPass(/*NonTrivial*/true, true)); LPM2.addPass(LICMPass(LICMOptions())); JULIA_PASS(LPM2.addPass(JuliaLICMPass())); //LICM needs MemorySSA now, so we must use it From 24a5dc48a46840fc1504f6448a146ecf4b419d9d Mon Sep 17 00:00:00 2001 From: Dheepak Krishnamurthy Date: Wed, 10 May 2023 11:43:22 -0400 Subject: [PATCH 728/775] Add `@eval using REPL` to the `atreplinit` do block in REPL documentation. (#49717) --- stdlib/REPL/docs/src/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/REPL/docs/src/index.md b/stdlib/REPL/docs/src/index.md index 1ea87c3a4109f..3ce0d7fa848b1 100644 --- a/stdlib/REPL/docs/src/index.md +++ b/stdlib/REPL/docs/src/index.md @@ -623,6 +623,7 @@ It is possible to get an interface which is similar to the IPython REPL and the ```julia atreplinit() do repl + @eval import REPL if !isdefined(repl, :interface) repl.interface = REPL.setup_interface(repl) end From 056112e5c25d43f70eb8040c73155862cc2cedb6 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 10 May 2023 12:54:47 -0400 Subject: [PATCH 729/775] make better use of visibility attributes (#49600) This pragma enables compilers to generate more optimal code than the identical command line flag, for better performance, by moving objects out of the GOT into direct references and eliminating the unnecessary PLT jump. Note that setting dllimport similarly enables more performance optimizations, at the cost of making duplicate symbols for functions so that they no longer have unique addresses (similar to the side-effect of setting -Bsymbolic-functions on ELF). --- base/boot.jl | 2 - base/reflection.jl | 2 +- cli/Makefile | 8 +- cli/loader.h | 14 ++-- src/Makefile | 12 ++- src/aotcompile.cpp | 70 ++++++++++------- src/ast.c | 4 +- src/builtins.c | 1 + src/cgutils.cpp | 2 +- src/codegen.cpp | 12 +-- src/coverage.cpp | 4 +- src/debuginfo.cpp | 6 +- src/disasm.cpp | 12 +-- src/flisp/Makefile | 2 +- src/gf.c | 9 ++- src/interpreter.c | 2 +- src/intrinsics.cpp | 4 +- src/jitlayers.cpp | 27 ++++--- src/jl_exported_funcs.inc | 2 - src/jltypes.c | 2 +- src/julia.h | 33 ++++---- src/julia_internal.h | 121 ++++++++++++++++------------- src/julia_locks.h | 2 - src/julia_threads.h | 6 +- src/llvm-alloc-opt.cpp | 3 +- src/llvm-cpufeatures.cpp | 3 +- src/llvm-demote-float16.cpp | 3 +- src/llvm-final-gc-lowering.cpp | 3 +- src/llvm-gc-invariant-verifier.cpp | 3 +- src/llvm-julia-licm.cpp | 3 +- src/llvm-late-gc-lowering.cpp | 3 +- src/llvm-lower-handlers.cpp | 3 +- src/llvm-muladd.cpp | 3 +- src/llvm-multiversioning.cpp | 24 ++++-- src/llvm-propagate-addrspaces.cpp | 3 +- src/llvm-ptls.cpp | 19 +++-- src/llvm-remove-addrspaces.cpp | 3 +- src/llvm-remove-ni.cpp | 3 +- src/llvm-simdloop.cpp | 5 +- src/llvmcalltest.cpp | 4 - src/method.c | 6 -- src/pipeline.cpp | 4 +- src/rtutils.c | 2 +- src/subtype.c | 5 +- src/support/Makefile | 2 +- src/support/dtypes.h | 18 +++-- src/timing.h | 4 +- 47 files changed, 274 insertions(+), 214 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index 9d44b98d46881..43ced22c043d5 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -263,8 +263,6 @@ convert(::Type{T}, x::T) where {T} = x cconvert(::Type{T}, x) where {T} = convert(T, x) unsafe_convert(::Type{T}, x::T) where {T} = x -const Vararg = ccall(:jl_wrap_vararg, Any, (Int, Int), 0, 0) - # dispatch token indicating a kwarg (keyword sorter) call function kwcall end # deprecated internal functions: diff --git a/base/reflection.jl b/base/reflection.jl index 197742318bb4b..504ccf9dbea6e 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1200,7 +1200,7 @@ struct CodegenParams prefer_specsig::Bool=false, gnu_pubnames=true, debug_info_kind::Cint = default_debug_info_kind(), safepoint_on_entry::Bool=true, - lookup::Ptr{Cvoid}=cglobal(:jl_rettype_inferred), + lookup::Ptr{Cvoid}=unsafe_load(cglobal(:jl_rettype_inferred_addr, Ptr{Cvoid})), generic_context = nothing) return new( Cint(track_allocations), Cint(code_coverage), diff --git a/cli/Makefile b/cli/Makefile index 5c2de8f2ae6d0..c2e2bcd568a07 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -55,9 +55,9 @@ all: release debug release debug : % : julia-% libjulia-% $(BUILDDIR)/loader_lib.o : $(SRCDIR)/loader_lib.c $(HEADERS) $(JULIAHOME)/VERSION - @$(call PRINT_CC, $(CC) -DLIBRARY_EXPORTS $(SHIPFLAGS) $(LOADER_CFLAGS) -c $< -o $@) + @$(call PRINT_CC, $(CC) -DJL_LIBRARY_EXPORTS $(SHIPFLAGS) $(LOADER_CFLAGS) -c $< -o $@) $(BUILDDIR)/loader_lib.dbg.obj : $(SRCDIR)/loader_lib.c $(HEADERS) $(JULIAHOME)/VERSION - @$(call PRINT_CC, $(CC) -DLIBRARY_EXPORTS $(DEBUGFLAGS) $(LOADER_CFLAGS) -c $< -o $@) + @$(call PRINT_CC, $(CC) -DJL_LIBRARY_EXPORTS $(DEBUGFLAGS) $(LOADER_CFLAGS) -c $< -o $@) $(BUILDDIR)/loader_exe.o : $(SRCDIR)/loader_exe.c $(HEADERS) $(JULIAHOME)/VERSION @$(call PRINT_CC, $(CC) $(SHIPFLAGS) $(LOADER_CFLAGS) -c $< -o $@) $(BUILDDIR)/loader_exe.dbg.obj : $(SRCDIR)/loader_exe.c $(HEADERS) $(JULIAHOME)/VERSION @@ -110,7 +110,7 @@ STRIP_EXPORTED_FUNCS := $(shell $(CPP_STDOUT) -I$(JULIAHOME)/src $(SRCDIR)/list_ endif $(build_shlibdir)/libjulia.$(JL_MAJOR_MINOR_SHLIB_EXT): $(LIB_OBJS) $(SRCDIR)/list_strip_symbols.h | $(build_shlibdir) $(build_libdir) - @$(call PRINT_LINK, $(CC) $(call IMPLIB_FLAGS,$@.tmp) $(LOADER_CFLAGS) -DLIBRARY_EXPORTS -shared $(SHIPFLAGS) $(LIB_OBJS) -o $@ \ + @$(call PRINT_LINK, $(CC) $(call IMPLIB_FLAGS,$@.tmp) $(LOADER_CFLAGS) -shared $(SHIPFLAGS) $(LIB_OBJS) -o $@ \ $(JLIBLDFLAGS) $(LOADER_LDFLAGS) $(call SONAME_FLAGS,libjulia.$(JL_MAJOR_SHLIB_EXT))) @$(INSTALL_NAME_CMD)libjulia.$(JL_MAJOR_SHLIB_EXT) $@ @$(DSYMUTIL) $@ @@ -121,7 +121,7 @@ ifeq ($(OS), WINNT) endif $(build_shlibdir)/libjulia-debug.$(JL_MAJOR_MINOR_SHLIB_EXT): $(LIB_DOBJS) $(SRCDIR)/list_strip_symbols.h | $(build_shlibdir) $(build_libdir) - @$(call PRINT_LINK, $(CC) $(call IMPLIB_FLAGS,$@.tmp) $(LOADER_CFLAGS) -DLIBRARY_EXPORTS -shared $(DEBUGFLAGS) $(LIB_DOBJS) -o $@ \ + @$(call PRINT_LINK, $(CC) $(call IMPLIB_FLAGS,$@.tmp) $(LOADER_CFLAGS) -shared $(DEBUGFLAGS) $(LIB_DOBJS) -o $@ \ $(JLIBLDFLAGS) $(LOADER_LDFLAGS) $(call SONAME_FLAGS,libjulia-debug.$(JL_MAJOR_SHLIB_EXT))) @$(INSTALL_NAME_CMD)libjulia-debug.$(JL_MAJOR_SHLIB_EXT) $@ @$(DSYMUTIL) $@ diff --git a/cli/loader.h b/cli/loader.h index d68b07da00050..b778976cee495 100644 --- a/cli/loader.h +++ b/cli/loader.h @@ -53,20 +53,18 @@ // Borrow definition from `support/dtypes.h` #ifdef _OS_WINDOWS_ -# ifdef LIBRARY_EXPORTS +# ifdef JL_LIBRARY_EXPORTS # define JL_DLLEXPORT __declspec(dllexport) -# else -# define JL_DLLEXPORT __declspec(dllimport) # endif +# define JL_DLLIMPORT __declspec(dllimport) #define JL_HIDDEN #else -# if defined(LIBRARY_EXPORTS) && defined(_OS_LINUX_) -# define JL_DLLEXPORT __attribute__ ((visibility("protected"))) -# else -# define JL_DLLEXPORT __attribute__ ((visibility("default"))) -# endif +# define JL_DLLIMPORT __attribute__ ((visibility("default"))) #define JL_HIDDEN __attribute__ ((visibility("hidden"))) #endif +#ifndef JL_DLLEXPORT +# define JL_DLLEXPORT JL_DLLIMPORT +#endif /* * DEP_LIBS is our list of dependent libraries that must be loaded before `libjulia`. * Note that order matters, as each entry will be opened in-order. We define here a diff --git a/src/Makefile b/src/Makefile index bba361eaadeaa..382e904818838 100644 --- a/src/Makefile +++ b/src/Makefile @@ -170,8 +170,8 @@ DOBJS := $(SRCS:%=$(BUILDDIR)/%.dbg.obj) CODEGEN_OBJS := $(CODEGEN_SRCS:%=$(BUILDDIR)/%.o) CODEGEN_DOBJS := $(CODEGEN_SRCS:%=$(BUILDDIR)/%.dbg.obj) -DEBUGFLAGS += $(FLAGS) -DLIBRARY_EXPORTS -SHIPFLAGS += $(FLAGS) -DLIBRARY_EXPORTS +SHIPFLAGS += $(FLAGS) +DEBUGFLAGS += $(FLAGS) # if not absolute, then relative to the directory of the julia executable SHIPFLAGS += "-DJL_SYSTEM_IMAGE_PATH=\"$(build_private_libdir_rel)/sys.$(SHLIB_EXT)\"" @@ -417,6 +417,12 @@ libjulia-codegen-release: $(build_shlibdir)/libjulia-codegen.$(JL_MAJOR_MINOR_SH libjulia-codegen-debug: $(build_shlibdir)/libjulia-codegen-debug.$(JL_MAJOR_MINOR_SHLIB_EXT) libjulia-codegen-debug libjulia-codegen-release: $(PUBLIC_HEADER_TARGETS) +# set the exports for the source files based on where they are getting linked +$(OBJS): SHIPFLAGS += -DJL_LIBRARY_EXPORTS_INTERNAL +$(DOBJS): DEBUGFLAGS += -DJL_LIBRARY_EXPORTS_INTERNAL +$(CODEGEN_OBJS): SHIPFLAGS += -DJL_LIBRARY_EXPORTS_CODEGEN +$(CODEGEN_DOBJS): DEBUGFLAGS += -DJL_LIBRARY_EXPORTS_CODEGEN + clean: -rm -fr $(build_shlibdir)/libjulia-internal* $(build_shlibdir)/libjulia-codegen* $(build_shlibdir)/libccalltest* $(build_shlibdir)/libllvmcalltest* -rm -f $(BUILDDIR)/julia_flisp.boot $(BUILDDIR)/julia_flisp.boot.inc $(BUILDDIR)/jl_internal_funcs.inc @@ -503,6 +509,8 @@ clang-tidy-%: $(SRCDIR)/%.cpp $(build_shlibdir)/libImplicitAtomicsPlugin.$(SHLIB -load $(build_shlibdir)/libImplicitAtomicsPlugin.$(SHLIB_EXT) --checks='-clang-analyzer-*$(COMMA)-clang-diagnostic-*$(COMMA)concurrency-implicit-atomics' --warnings-as-errors='*' \ -- $(CLANGSA_FLAGS) $(CLANGSA_CXXFLAGS) $(LLVM_CXXFLAGS) $(JCPPFLAGS) $(JCXXFLAGS) $(JL_CXXFLAGS) $(DEBUGFLAGS) -fcolor-diagnostics --system-header-prefix=llvm -Wno-deprecated-declarations -fno-caret-diagnostics -x c++) +# set the exports for the source files based on where they are getting linked +clang-sa-% clang-sagc-% clang-tidy-%: DEBUGFLAGS += -DJL_LIBRARY_EXPORTS # Add C files as a target of `analyzesrc` and `analyzegc` and `tidysrc` tidysrc: $(addprefix clang-tidy-,$(filter-out $(basename $(SKIP_IMPLICIT_ATOMICS)),$(CODEGEN_SRCS) $(SRCS))) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 01f023d5bef5e..4fc9ad4bdf596 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -96,7 +96,7 @@ typedef struct { std::vector jl_external_to_llvm; } jl_native_code_desc_t; -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN void jl_get_function_id_impl(void *native_code, jl_code_instance_t *codeinst, int32_t *func_idx, int32_t *specfunc_idx) { @@ -110,7 +110,7 @@ void jl_get_function_id_impl(void *native_code, jl_code_instance_t *codeinst, } } -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN void jl_get_llvm_gvs_impl(void *native_code, arraylist_t *gvs) { // map a memory location (jl_value_t or jl_binding_t) to a GlobalVariable @@ -119,7 +119,7 @@ void jl_get_llvm_gvs_impl(void *native_code, arraylist_t *gvs) memcpy(gvs->items, data->jl_value_to_llvm.data(), gvs->len * sizeof(void*)); } -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN void jl_get_llvm_external_fns_impl(void *native_code, arraylist_t *external_fns) { jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code; @@ -128,7 +128,7 @@ void jl_get_llvm_external_fns_impl(void *native_code, arraylist_t *external_fns) external_fns->len * sizeof(jl_code_instance_t*)); } -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN LLVMOrcThreadSafeModuleRef jl_get_llvm_module_impl(void *native_code) { jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code; @@ -138,7 +138,7 @@ LLVMOrcThreadSafeModuleRef jl_get_llvm_module_impl(void *native_code) return NULL; } -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN GlobalValue* jl_get_llvm_function_impl(void *native_code, uint32_t idx) { jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code; @@ -160,10 +160,12 @@ static void emit_offset_table(Module &mod, const std::vector &vars addrs[i] = ConstantExpr::getBitCast(var, T_psize); } ArrayType *vars_type = ArrayType::get(T_psize, nvars); - new GlobalVariable(mod, vars_type, true, + auto GV = new GlobalVariable(mod, vars_type, true, GlobalVariable::ExternalLinkage, ConstantArray::get(vars_type, addrs), name); + GV->setVisibility(GlobalValue::HiddenVisibility); + GV->setDSOLocal(true); } static bool is_safe_char(unsigned char c) @@ -241,7 +243,7 @@ static void jl_ci_cache_lookup(const jl_cgparams_t &cgparams, jl_method_instance *src_out = jl_uncompress_ir(def, codeinst, (jl_value_t*)*src_out); } if (*src_out == NULL || !jl_is_code_info(*src_out)) { - if (cgparams.lookup != jl_rettype_inferred) { + if (cgparams.lookup != jl_rettype_inferred_addr) { jl_error("Refusing to automatically run type inference with custom cache lookup."); } else { @@ -265,7 +267,7 @@ static void jl_ci_cache_lookup(const jl_cgparams_t &cgparams, jl_method_instance // The `policy` flag switches between the default mode `0` and the extern mode `1` used by GPUCompiler. // `_imaging_mode` controls if raw pointers can be embedded (e.g. the code will be loaded into the same session). // `_external_linkage` create linkages between pkgimages. -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvmmod, const jl_cgparams_t *cgparams, int _policy, int _imaging_mode, int _external_linkage, size_t _world) { JL_TIMING(NATIVE_AOT, NATIVE_Create); @@ -432,6 +434,7 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm G->setInitializer(ConstantPointerNull::get(cast(G->getValueType()))); G->setLinkage(GlobalValue::ExternalLinkage); G->setVisibility(GlobalValue::HiddenVisibility); + G->setDSOLocal(true); data->jl_sysimg_gvars.push_back(G); } CreateNativeGlobals += gvars.size(); @@ -456,6 +459,7 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm if (!G.isDeclaration()) { G.setLinkage(GlobalValue::ExternalLinkage); G.setVisibility(GlobalValue::HiddenVisibility); + G.setDSOLocal(true); makeSafeName(G); if (TT.isOSWindows() && TT.getArch() == Triple::x86_64) { // Add unwind exception personalities to functions to handle async exceptions @@ -523,6 +527,7 @@ static GlobalVariable *emit_shard_table(Module &M, Type *T_size, Type *T_psize, auto gv = new GlobalVariable(M, T_size, constant, GlobalValue::ExternalLinkage, nullptr, name + suffix); gv->setVisibility(GlobalValue::HiddenVisibility); + gv->setDSOLocal(true); return gv; }; auto table = tables.data() + i * sizeof(jl_image_shard_t) / sizeof(void *); @@ -540,6 +545,7 @@ static GlobalVariable *emit_shard_table(Module &M, Type *T_size, Type *T_psize, auto tables_gv = new GlobalVariable(M, tables_arr->getType(), false, GlobalValue::ExternalLinkage, tables_arr, "jl_shard_tables"); tables_gv->setVisibility(GlobalValue::HiddenVisibility); + tables_gv->setDSOLocal(true); return tables_gv; } @@ -550,12 +556,15 @@ static GlobalVariable *emit_ptls_table(Module &M, Type *T_size, Type *T_psize) { new GlobalVariable(M, T_size, false, GlobalValue::ExternalLinkage, Constant::getNullValue(T_size), "jl_pgcstack_key_slot"), new GlobalVariable(M, T_size, false, GlobalValue::ExternalLinkage, Constant::getNullValue(T_size), "jl_tls_offset"), }; - for (auto &gv : ptls_table) + for (auto &gv : ptls_table) { cast(gv)->setVisibility(GlobalValue::HiddenVisibility); + cast(gv)->setDSOLocal(true); + } auto ptls_table_arr = ConstantArray::get(ArrayType::get(T_psize, ptls_table.size()), ptls_table); auto ptls_table_gv = new GlobalVariable(M, ptls_table_arr->getType(), false, GlobalValue::ExternalLinkage, ptls_table_arr, "jl_ptls_table"); ptls_table_gv->setVisibility(GlobalValue::HiddenVisibility); + ptls_table_gv->setDSOLocal(true); return ptls_table_gv; } @@ -1101,6 +1110,8 @@ static void materializePreserved(Module &M, Partition &partition) { if (!Preserve.contains(&F)) { F.deleteBody(); F.setLinkage(GlobalValue::ExternalLinkage); + F.setVisibility(GlobalValue::HiddenVisibility); + F.setDSOLocal(true); } } } @@ -1109,6 +1120,8 @@ static void materializePreserved(Module &M, Partition &partition) { if (!Preserve.contains(&GV)) { GV.setInitializer(nullptr); GV.setLinkage(GlobalValue::ExternalLinkage); + GV.setVisibility(GlobalValue::HiddenVisibility); + GV.setDSOLocal(true); } } } @@ -1130,7 +1143,8 @@ static void materializePreserved(Module &M, Partition &partition) { GA.setAliasee(F); DeletedAliases.push_back({ &GA, F }); - } else { + } + else { auto GV = new GlobalVariable(M, GA.getValueType(), false, GlobalValue::ExternalLinkage, Constant::getNullValue(GA.getValueType())); DeletedAliases.push_back({ &GA, GV }); } @@ -1197,26 +1211,24 @@ static void construct_vars(Module &M, Partition &partition) { GlobalVariable::ExternalLinkage, fidxs, "jl_fvar_idxs"); fidxs_var->setVisibility(GlobalValue::HiddenVisibility); + fidxs_var->setDSOLocal(true); auto gidxs = ConstantDataArray::get(M.getContext(), gvar_idxs); auto gidxs_var = new GlobalVariable(M, gidxs->getType(), true, GlobalVariable::ExternalLinkage, gidxs, "jl_gvar_idxs"); gidxs_var->setVisibility(GlobalValue::HiddenVisibility); + gidxs_var->setDSOLocal(true); } // Materialization will leave many unused declarations, which multiversioning would otherwise clone. // This function removes them to avoid unnecessary cloning of declarations. -static void dropUnusedDeclarations(Module &M) { - SmallVector unused; +// The GlobalDCEPass is much better at this, but we only care about removing unused +// declarations, not actually about seeing if code is dead (codegen knows it is live, by construction). +static void dropUnusedGlobals(Module &M) { + std::vector unused; for (auto &G : M.global_values()) { - if (G.isDeclaration()) { - if (G.use_empty()) { - unused.push_back(&G); - } else { - G.setDSOLocal(false); // These are never going to be seen in the same module again - G.setVisibility(GlobalValue::DefaultVisibility); - } - } + if (G.isDeclaration() && G.use_empty()) + unused.push_back(&G); } for (auto &G : unused) G->eraseFromParent(); @@ -1355,7 +1367,7 @@ static void add_output(Module &M, TargetMachine &TM, std::vector &o timers[i].construct.stopTimer(); timers[i].deletion.startTimer(); - dropUnusedDeclarations(*M); + dropUnusedGlobals(*M); timers[i].deletion.stopTimer(); add_output_impl(*M, TM, outputs_start + i * outcount, names_start + i * outcount, @@ -1450,7 +1462,7 @@ static unsigned compute_image_thread_count(const ModuleInfo &info) { // takes the running content that has collected in the shadow module and dump it to disk // this builds the object file portion of the sysimage files for fast startup -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN void jl_dump_native_impl(void *native_code, const char *bc_fname, const char *unopt_bc_fname, const char *obj_fname, const char *asm_fname, @@ -1556,6 +1568,7 @@ void jl_dump_native_impl(void *native_code, GlobalVariable::ExternalLinkage, gidxs, "jl_gvar_idxs"); gidxs_var->setVisibility(GlobalValue::HiddenVisibility); + gidxs_var->setDSOLocal(true); idxs.clear(); idxs.resize(data->jl_sysimg_fvars.size()); std::iota(idxs.begin(), idxs.end(), 0); @@ -1564,6 +1577,7 @@ void jl_dump_native_impl(void *native_code, GlobalVariable::ExternalLinkage, fidxs, "jl_fvar_idxs"); fidxs_var->setVisibility(GlobalValue::HiddenVisibility); + fidxs_var->setDSOLocal(true); dataM->addModuleFlag(Module::Error, "julia.mv.suffix", MDString::get(Context, "_0")); // reflect the address of the jl_RTLD_DEFAULT_handle variable @@ -2004,7 +2018,7 @@ static RegisterPass> XS("juliaO0-sysimg", "Runs the entire static RegisterPass> YS("julia-sysimg", "Runs the entire julia pipeline (at -O2/sysimg mode)", false, false); static RegisterPass> ZS("juliaO3-sysimg", "Runs the entire julia pipeline (at -O3/sysimg mode)", false, false); -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN void jl_add_optimization_passes_impl(LLVMPassManagerRef PM, int opt_level, int lower_intrinsics) { addOptimizationPasses(unwrap(PM), opt_level, lower_intrinsics); } @@ -2014,7 +2028,7 @@ void jl_add_optimization_passes_impl(LLVMPassManagerRef PM, int opt_level, int l // for use in reflection from Julia. // this is paired with jl_dump_function_ir, jl_dump_function_asm, jl_dump_method_asm in particular ways: // misuse will leak memory or cause read-after-free -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, size_t world, char getwrapper, char optimize, const jl_cgparams_t params) { if (jl_is_method(mi->def.method) && mi->def.method->source == NULL && @@ -2027,20 +2041,22 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, siz // get the source code for this function jl_value_t *jlrettype = (jl_value_t*)jl_any_type; jl_code_info_t *src = NULL; - JL_GC_PUSH2(&src, &jlrettype); + jl_code_instance_t *codeinst = NULL; + JL_GC_PUSH3(&src, &jlrettype, &codeinst); if (jl_is_method(mi->def.method) && mi->def.method->source != NULL && mi->def.method->source != jl_nothing && jl_ir_flag_inferred(mi->def.method->source)) { src = (jl_code_info_t*)mi->def.method->source; if (src && !jl_is_code_info(src)) src = jl_uncompress_ir(mi->def.method, NULL, (jl_value_t*)src); } else { - jl_value_t *ci = jl_rettype_inferred(mi, world, world); + jl_value_t *ci = jl_rettype_inferred_addr(mi, world, world); if (ci != jl_nothing) { - jl_code_instance_t *codeinst = (jl_code_instance_t*)ci; + codeinst = (jl_code_instance_t*)ci; src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred); if ((jl_value_t*)src != jl_nothing && !jl_is_code_info(src) && jl_is_method(mi->def.method)) src = jl_uncompress_ir(mi->def.method, codeinst, (jl_value_t*)src); jlrettype = codeinst->rettype; + codeinst = NULL; // not needed outside of this branch } if (!src || (jl_value_t*)src == jl_nothing) { src = jl_type_infer(mi, world, 0); diff --git a/src/ast.c b/src/ast.c index 1574f920e06e9..97bbc6e8227ba 100644 --- a/src/ast.c +++ b/src/ast.c @@ -1259,8 +1259,8 @@ JL_DLLEXPORT jl_value_t *jl_expand_stmt(jl_value_t *expr, jl_module_t *inmodule) // Internal C entry point to parser // `text` is passed as a pointer to allow raw non-String buffers to be used // without copying. -JL_DLLEXPORT jl_value_t *jl_parse(const char *text, size_t text_len, jl_value_t *filename, - size_t lineno, size_t offset, jl_value_t *options) +jl_value_t *jl_parse(const char *text, size_t text_len, jl_value_t *filename, + size_t lineno, size_t offset, jl_value_t *options) { jl_value_t *core_parse = NULL; if (jl_core_module) { diff --git a/src/builtins.c b/src/builtins.c index 799db9afcf685..a6c904c851c95 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -2060,6 +2060,7 @@ void jl_init_primitives(void) JL_GC_DISABLED add_builtin("Tuple", (jl_value_t*)jl_anytuple_type); add_builtin("TypeofVararg", (jl_value_t*)jl_vararg_type); add_builtin("SimpleVector", (jl_value_t*)jl_simplevector_type); + add_builtin("Vararg", (jl_value_t*)jl_wrap_vararg(NULL, NULL)); add_builtin("Module", (jl_value_t*)jl_module_type); add_builtin("MethodTable", (jl_value_t*)jl_methtable_type); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 29e3cf152625f..46c7407e95d98 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -635,7 +635,7 @@ static Type *julia_type_to_llvm(jl_codectx_t &ctx, jl_value_t *jt, bool *isboxed return _julia_type_to_llvm(&ctx.emission_context, ctx.builder.getContext(), jt, isboxed); } -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN Type *jl_type_to_llvm_impl(jl_value_t *jt, LLVMContextRef ctxt, bool *isboxed) { return _julia_type_to_llvm(NULL, *unwrap(ctxt), jt, isboxed); diff --git a/src/codegen.cpp b/src/codegen.cpp index a9d2cb0c60333..05863e69341ec 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -193,7 +193,7 @@ STATISTIC(GeneratedCCallables, "Number of C-callable functions generated"); STATISTIC(GeneratedInvokeWrappers, "Number of invoke wrappers generated"); STATISTIC(EmittedFunctions, "Number of functions emitted"); -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN void jl_dump_emitted_mi_name_impl(void *s) { **jl_ExecutionEngine->get_dump_emitted_mi_name_stream() = (ios_t*)s; @@ -1300,7 +1300,7 @@ extern "C" { #endif (int) DICompileUnit::DebugEmissionKind::FullDebug, 1, - jl_rettype_inferred, NULL }; + jl_rettype_inferred_addr, NULL }; } @@ -5239,7 +5239,7 @@ static std::pair get_oc_function(jl_codectx_t &ctx, jl_met sigtype = jl_apply_tuple_type_v(jl_svec_data(sig_args), nsig); jl_method_instance_t *mi = jl_specializations_get_linfo(closure_method, sigtype, jl_emptysvec); - jl_code_instance_t *ci = (jl_code_instance_t*)jl_rettype_inferred(mi, ctx.world, ctx.world); + jl_code_instance_t *ci = (jl_code_instance_t*)jl_rettype_inferred_addr(mi, ctx.world, ctx.world); if (ci == NULL || (jl_value_t*)ci == jl_nothing) { JL_GC_POP(); @@ -9145,7 +9145,7 @@ extern "C" void jl_init_llvm(void) cl::PrintOptionValues(); } -extern "C" JL_DLLEXPORT void jl_init_codegen_impl(void) +extern "C" JL_DLLEXPORT_CODEGEN void jl_init_codegen_impl(void) { jl_init_llvm(); // Now that the execution engine exists, initialize all modules @@ -9155,7 +9155,7 @@ extern "C" JL_DLLEXPORT void jl_init_codegen_impl(void) #endif } -extern "C" JL_DLLEXPORT void jl_teardown_codegen_impl() JL_NOTSAFEPOINT +extern "C" JL_DLLEXPORT_CODEGEN void jl_teardown_codegen_impl() JL_NOTSAFEPOINT { // output LLVM timings and statistics jl_ExecutionEngine->printTimers(); @@ -9230,7 +9230,7 @@ extern void jl_write_bitcode_module(void *M, char *fname) { #include -extern "C" JL_DLLEXPORT jl_value_t *jl_get_libllvm_impl(void) JL_NOTSAFEPOINT +extern "C" JL_DLLEXPORT_CODEGEN jl_value_t *jl_get_libllvm_impl(void) JL_NOTSAFEPOINT { #if defined(_OS_WINDOWS_) HMODULE mod; diff --git a/src/coverage.cpp b/src/coverage.cpp index 2be064726b1fe..95924f326524b 100644 --- a/src/coverage.cpp +++ b/src/coverage.cpp @@ -192,7 +192,7 @@ static void write_lcov_data(logdata_t &logData, const std::string &outfile) outf.close(); } -extern "C" JL_DLLEXPORT void jl_write_coverage_data(const char *output) +extern "C" void jl_write_coverage_data(const char *output) { if (output) { StringRef output_pattern(output); @@ -206,7 +206,7 @@ extern "C" JL_DLLEXPORT void jl_write_coverage_data(const char *output) } } -extern "C" JL_DLLEXPORT void jl_write_malloc_log(void) +extern "C" void jl_write_malloc_log(void) { std::string stm; raw_string_ostream(stm) << "." << uv_os_getpid() << ".mem"; diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index 69c8248c7c7d0..35e41fe657045 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -687,7 +687,7 @@ openDebugInfo(StringRef debuginfopath, const debug_link_info &info) JL_NOTSAFEPO std::move(error_splitobj.get()), std::move(SplitFile.get())); } -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN void jl_register_fptrs_impl(uint64_t image_base, const jl_image_fptrs_t *fptrs, jl_method_instance_t **linfos, size_t n) { @@ -1217,7 +1217,7 @@ int jl_DI_for_fptr(uint64_t fptr, uint64_t *symsize, int64_t *slide, } // Set *name and *filename to either NULL or malloc'd string -extern "C" JL_DLLEXPORT int jl_getFunctionInfo_impl(jl_frame_t **frames_out, size_t pointer, int skipC, int noInline) JL_NOTSAFEPOINT +extern "C" JL_DLLEXPORT_CODEGEN int jl_getFunctionInfo_impl(jl_frame_t **frames_out, size_t pointer, int skipC, int noInline) JL_NOTSAFEPOINT { // This function is not allowed to reference any TLS variables if noInline // since it can be called from an unmanaged thread on OSX. @@ -1607,7 +1607,7 @@ void deregister_eh_frames(uint8_t *Addr, size_t Size) #endif -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN uint64_t jl_getUnwindInfo_impl(uint64_t dwAddr) { // Might be called from unmanaged thread diff --git a/src/disasm.cpp b/src/disasm.cpp index e693fe7427570..8e0516d5e5431 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -489,7 +489,7 @@ void jl_strip_llvm_addrspaces(Module *m) JL_NOTSAFEPOINT // print an llvm IR acquired from jl_get_llvmf // warning: this takes ownership of, and destroys, dump->TSM -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN jl_value_t *jl_dump_function_ir_impl(jl_llvmf_dump_t *dump, char strip_ir_metadata, char dump_module, const char *debuginfo) { std::string code; @@ -578,7 +578,7 @@ static uint64_t compute_obj_symsize(object::SectionRef Section, uint64_t offset) } // print a native disassembly for the function starting at fptr -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN jl_value_t *jl_dump_fptr_asm_impl(uint64_t fptr, char raw_mc, const char* asm_variant, const char *debuginfo, char binary) { assert(fptr != 0); @@ -1212,7 +1212,7 @@ class LineNumberPrinterHandler : public AsmPrinterHandler { }; // get a native assembly for llvm::Function -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN jl_value_t *jl_dump_function_asm_impl(jl_llvmf_dump_t* dump, char raw_mc, const char* asm_variant, const char *debuginfo, char binary) { // precise printing via IR assembler @@ -1286,7 +1286,7 @@ jl_value_t *jl_dump_function_asm_impl(jl_llvmf_dump_t* dump, char raw_mc, const return jl_pchar_to_string(ObjBufferSV.data(), ObjBufferSV.size()); } -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN LLVMDisasmContextRef jl_LLVMCreateDisasm_impl( const char *TripleName, void *DisInfo, int TagType, LLVMOpInfoCallback GetOpInfo, LLVMSymbolLookupCallback SymbolLookUp) @@ -1294,8 +1294,8 @@ LLVMDisasmContextRef jl_LLVMCreateDisasm_impl( return LLVMCreateDisasm(TripleName, DisInfo, TagType, GetOpInfo, SymbolLookUp); } -extern "C" JL_DLLEXPORT -JL_DLLEXPORT size_t jl_LLVMDisasmInstruction_impl( +extern "C" JL_DLLEXPORT_CODEGEN +size_t jl_LLVMDisasmInstruction_impl( LLVMDisasmContextRef DC, uint8_t *Bytes, uint64_t BytesSize, uint64_t PC, char *OutString, size_t OutStringSize) { diff --git a/src/flisp/Makefile b/src/flisp/Makefile index 7a363b0ec13d7..c2bf30300b041 100644 --- a/src/flisp/Makefile +++ b/src/flisp/Makefile @@ -49,7 +49,7 @@ endif FLAGS := -I$(LLTSRCDIR) $(JCFLAGS) $(HFILEDIRS:%=-I%) \ -I$(LIBUV_INC) -I$(UTF8PROC_INC) -I$(build_includedir) $(LIBDIRS:%=-L%) \ - -DLIBRARY_EXPORTS -DUTF8PROC_EXPORTS + -DJL_LIBRARY_EXPORTS_INTERNAL -DUTF8PROC_EXPORTS ifneq ($(OS), emscripten) FLAGS += -DUSE_COMPUTED_GOTO endif diff --git a/src/gf.c b/src/gf.c index b8bb21c36b2a7..ff48b5224a455 100644 --- a/src/gf.c +++ b/src/gf.c @@ -450,6 +450,7 @@ JL_DLLEXPORT jl_value_t *jl_rettype_inferred(jl_method_instance_t *mi, size_t mi } return (jl_value_t*)jl_nothing; } +JL_DLLEXPORT jl_value_t *(*const jl_rettype_inferred_addr)(jl_method_instance_t *mi, size_t min_world, size_t max_world) JL_NOTSAFEPOINT = jl_rettype_inferred; JL_DLLEXPORT jl_code_instance_t *jl_get_method_inferred( @@ -2539,13 +2540,13 @@ jl_value_t *jl_fptr_sparam(jl_value_t *f, jl_value_t **args, uint32_t nargs, jl_ return invoke(f, args, nargs, sparams); } -JL_DLLEXPORT jl_callptr_t jl_fptr_args_addr = &jl_fptr_args; +JL_DLLEXPORT const jl_callptr_t jl_fptr_args_addr = &jl_fptr_args; -JL_DLLEXPORT jl_callptr_t jl_fptr_const_return_addr = &jl_fptr_const_return; +JL_DLLEXPORT const jl_callptr_t jl_fptr_const_return_addr = &jl_fptr_const_return; -JL_DLLEXPORT jl_callptr_t jl_fptr_sparam_addr = &jl_fptr_sparam; +JL_DLLEXPORT const jl_callptr_t jl_fptr_sparam_addr = &jl_fptr_sparam; -JL_DLLEXPORT jl_callptr_t jl_f_opaque_closure_call_addr = (jl_callptr_t)&jl_f_opaque_closure_call; +JL_DLLEXPORT const jl_callptr_t jl_f_opaque_closure_call_addr = (jl_callptr_t)&jl_f_opaque_closure_call; // Return the index of the invoke api, if known JL_DLLEXPORT int32_t jl_invoke_api(jl_code_instance_t *codeinst) diff --git a/src/interpreter.c b/src/interpreter.c index f5ec0ce6858c8..c08496f72ce04 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -698,7 +698,7 @@ jl_value_t *NOINLINE jl_fptr_interpret_call(jl_value_t *f, jl_value_t **args, ui return r; } -JL_DLLEXPORT jl_callptr_t jl_fptr_interpret_call_addr = &jl_fptr_interpret_call; +JL_DLLEXPORT const jl_callptr_t jl_fptr_interpret_call_addr = &jl_fptr_interpret_call; jl_value_t *jl_interpret_opaque_closure(jl_opaque_closure_t *oc, jl_value_t **args, size_t nargs) { diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 9fd0561971d02..7bef27f477534 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -108,8 +108,8 @@ const auto &float_func() { return float_funcs.float_func; } -extern "C" -JL_DLLEXPORT uint32_t jl_get_LLVM_VERSION_impl(void) +extern "C" JL_DLLEXPORT_CODEGEN +uint32_t jl_get_LLVM_VERSION_impl(void) { return 10000 * LLVM_VERSION_MAJOR + 100 * LLVM_VERSION_MINOR #ifdef LLVM_VERSION_PATCH diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 643f0468457ae..6b913743f1aa4 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -126,12 +126,12 @@ static void *getTLSAddress(void *control) #endif // Snooping on which functions are being compiled, and how long it takes -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN void jl_dump_compiles_impl(void *s) { **jl_ExecutionEngine->get_dump_compiles_stream() = (ios_t*)s; } -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN void jl_dump_llvm_opt_impl(void *s) { **jl_ExecutionEngine->get_dump_llvm_opt_stream() = (ios_t*)s; @@ -159,6 +159,7 @@ void jl_link_global(GlobalVariable *GV, void *addr) JL_NOTSAFEPOINT else { GV->setConstant(true); GV->setLinkage(GlobalValue::PrivateLinkage); + GV->setVisibility(GlobalValue::DefaultVisibility); GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); } } @@ -329,7 +330,7 @@ static jl_callptr_t _jl_compile_codeinst( const char *jl_generate_ccallable(LLVMOrcThreadSafeModuleRef llvmmod, void *sysimg_handle, jl_value_t *declrt, jl_value_t *sigt, jl_codegen_params_t ¶ms); // compile a C-callable alias -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN int jl_compile_extern_c_impl(LLVMOrcThreadSafeModuleRef llvmmod, void *p, void *sysimg, jl_value_t *declrt, jl_value_t *sigt) { auto ct = jl_current_task; @@ -389,7 +390,7 @@ int jl_compile_extern_c_impl(LLVMOrcThreadSafeModuleRef llvmmod, void *p, void * } // declare a C-callable entry point; called during code loading from the toplevel -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN void jl_extern_c_impl(jl_value_t *declrt, jl_tupletype_t *sigt) { // validate arguments. try to do as many checks as possible here to avoid @@ -432,7 +433,7 @@ void jl_extern_c_impl(jl_value_t *declrt, jl_tupletype_t *sigt) } // this compiles li and emits fptr -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN jl_code_instance_t *jl_generate_fptr_impl(jl_method_instance_t *mi JL_PROPAGATES_ROOT, size_t world) { auto ct = jl_current_task; @@ -446,10 +447,12 @@ jl_code_instance_t *jl_generate_fptr_impl(jl_method_instance_t *mi JL_PROPAGATES compiler_start_time = jl_hrtime(); // if we don't have any decls already, try to generate it now jl_code_info_t *src = NULL; - JL_GC_PUSH1(&src); + jl_code_instance_t *codeinst = NULL; + JL_GC_PUSH2(&src, &codeinst); JL_LOCK(&jl_codegen_lock); // also disables finalizers, to prevent any unexpected recursion - jl_value_t *ci = jl_rettype_inferred(mi, world, world); - jl_code_instance_t *codeinst = (ci == jl_nothing ? NULL : (jl_code_instance_t*)ci); + jl_value_t *ci = jl_rettype_inferred_addr(mi, world, world); + if (ci != jl_nothing) + codeinst = (jl_code_instance_t*)ci; if (codeinst) { src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred); if ((jl_value_t*)src == jl_nothing) @@ -505,7 +508,7 @@ jl_code_instance_t *jl_generate_fptr_impl(jl_method_instance_t *mi JL_PROPAGATES return codeinst; } -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN void jl_generate_fptr_for_oc_wrapper_impl(jl_code_instance_t *oc_wrap) { if (jl_atomic_load_relaxed(&oc_wrap->invoke) != NULL) { @@ -518,7 +521,7 @@ void jl_generate_fptr_for_oc_wrapper_impl(jl_code_instance_t *oc_wrap) JL_UNLOCK(&jl_codegen_lock); // Might GC } -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN void jl_generate_fptr_for_unspecialized_impl(jl_code_instance_t *unspec) { if (jl_atomic_load_relaxed(&unspec->invoke) != NULL) { @@ -571,7 +574,7 @@ void jl_generate_fptr_for_unspecialized_impl(jl_code_instance_t *unspec) // get a native disassembly for a compiled method -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN jl_value_t *jl_dump_method_asm_impl(jl_method_instance_t *mi, size_t world, char raw_mc, char getwrapper, const char* asm_variant, const char *debuginfo, char binary) { @@ -1921,7 +1924,7 @@ void add_named_global(StringRef name, void *addr) jl_ExecutionEngine->addGlobalMapping(name, (uint64_t)(uintptr_t)addr); } -extern "C" JL_DLLEXPORT +extern "C" JL_DLLEXPORT_CODEGEN size_t jl_jit_total_bytes_impl(void) { return jl_ExecutionEngine->getTotalBytes(); diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index cce87f53368f4..f79537d419b90 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -21,7 +21,6 @@ XX(jl_apply_type1) \ XX(jl_apply_type2) \ XX(jl_argument_datatype) \ - XX(jl_argument_method_table) \ XX(jl_arraylen) \ XX(jl_arrayref) \ XX(jl_arrayset) \ @@ -409,7 +408,6 @@ XX(jl_restore_system_image_data) \ XX(jl_rethrow) \ XX(jl_rethrow_other) \ - XX(jl_rettype_inferred) \ XX(jl_running_on_valgrind) \ XX(jl_safe_printf) \ XX(jl_SC_CLK_TCK) \ diff --git a/src/jltypes.c b/src/jltypes.c index 24809bc534819..1a30df637a706 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2354,7 +2354,7 @@ jl_datatype_t *jl_wrap_Type(jl_value_t *t) return (jl_datatype_t*)jl_instantiate_unionall(jl_type_type, t); } -JL_DLLEXPORT jl_vararg_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n) +jl_vararg_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n) { if (n) { if (jl_is_typevar(n) || jl_is_uniontype(jl_unwrap_unionall(n))) { diff --git a/src/julia.h b/src/julia.h index 8774889e71e07..286bef615c92d 100644 --- a/src/julia.h +++ b/src/julia.h @@ -3,7 +3,10 @@ #ifndef JULIA_H #define JULIA_H -#ifdef LIBRARY_EXPORTS +#if defined(JL_LIBRARY_EXPORTS_INTERNAL) || defined(JL_LIBRARY_EXPORTS_CODEGEN) +#define JL_LIBRARY_EXPORTS +#endif +#ifdef JL_LIBRARY_EXPORTS // Generated file, needs to be searched in include paths so that the builddir // retains priority #include @@ -70,7 +73,7 @@ typedef struct _jl_taggedvalue_t jl_taggedvalue_t; typedef struct _jl_tls_states_t *jl_ptls_t; -#ifdef LIBRARY_EXPORTS +#ifdef JL_LIBRARY_EXPORTS #include "uv.h" #endif #include "julia_atomics.h" @@ -228,20 +231,20 @@ typedef jl_call_t *jl_callptr_t; // "speccall" calling convention signatures. // This describes some of the special ABI used by compiled julia functions. extern jl_call_t jl_fptr_args; -JL_DLLEXPORT extern jl_callptr_t jl_fptr_args_addr; +JL_DLLEXPORT extern const jl_callptr_t jl_fptr_args_addr; typedef jl_value_t *(*jl_fptr_args_t)(jl_value_t*, jl_value_t**, uint32_t); extern jl_call_t jl_fptr_const_return; -JL_DLLEXPORT extern jl_callptr_t jl_fptr_const_return_addr; +JL_DLLEXPORT extern const jl_callptr_t jl_fptr_const_return_addr; extern jl_call_t jl_fptr_sparam; -JL_DLLEXPORT extern jl_callptr_t jl_fptr_sparam_addr; +JL_DLLEXPORT extern const jl_callptr_t jl_fptr_sparam_addr; typedef jl_value_t *(*jl_fptr_sparam_t)(jl_value_t*, jl_value_t**, uint32_t, jl_svec_t*); extern jl_call_t jl_fptr_interpret_call; -JL_DLLEXPORT extern jl_callptr_t jl_fptr_interpret_call_addr; +JL_DLLEXPORT extern const jl_callptr_t jl_fptr_interpret_call_addr; -JL_DLLEXPORT extern jl_callptr_t jl_f_opaque_closure_call_addr; +JL_DLLEXPORT extern const jl_callptr_t jl_f_opaque_closure_call_addr; typedef struct _jl_line_info_node_t { struct _jl_module_t *module; @@ -1691,10 +1694,10 @@ JL_DLLEXPORT size_t jl_array_size(jl_value_t *a, int d); JL_DLLEXPORT const char *jl_string_ptr(jl_value_t *s); // modules and global variables -extern JL_DLLEXPORT jl_module_t *jl_main_module JL_GLOBALLY_ROOTED; -extern JL_DLLEXPORT jl_module_t *jl_core_module JL_GLOBALLY_ROOTED; -extern JL_DLLEXPORT jl_module_t *jl_base_module JL_GLOBALLY_ROOTED; -extern JL_DLLEXPORT jl_module_t *jl_top_module JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_module_t *jl_main_module JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_module_t *jl_core_module JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_module_t *jl_base_module JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_module_t *jl_top_module JL_GLOBALLY_ROOTED; JL_DLLEXPORT jl_module_t *jl_new_module(jl_sym_t *name, jl_module_t *parent); JL_DLLEXPORT void jl_set_module_nospecialize(jl_module_t *self, int on); JL_DLLEXPORT void jl_set_module_optlevel(jl_module_t *self, int lvl); @@ -1757,7 +1760,7 @@ JL_DLLEXPORT long jl_getallocationgranularity(void) JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_is_debugbuild(void) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_sym_t *jl_get_UNAME(void) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_sym_t *jl_get_ARCH(void) JL_NOTSAFEPOINT; -JL_DLLEXPORT jl_value_t *jl_get_libllvm(void) JL_NOTSAFEPOINT; +JL_DLLIMPORT jl_value_t *jl_get_libllvm(void) JL_NOTSAFEPOINT; extern JL_DLLIMPORT int jl_n_threadpools; extern JL_DLLIMPORT _Atomic(int) jl_n_threads; extern JL_DLLIMPORT int jl_n_gcthreads; @@ -1831,7 +1834,7 @@ typedef enum { //JL_IMAGE_LIBJULIA = 2, } JL_IMAGE_SEARCH; -JL_DLLEXPORT const char *jl_get_libdir(void); +JL_DLLIMPORT const char *jl_get_libdir(void); JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel); JL_DLLEXPORT void jl_init(void); JL_DLLEXPORT void jl_init_with_image(const char *julia_bindir, @@ -2086,7 +2089,7 @@ void (jl_longjmp)(jmp_buf _Buf, int _Value); JL_DLLEXPORT int (ijl_setjmp)(jmp_buf _Buf); void (ijl_longjmp)(jmp_buf _Buf, int _Value); #endif -#ifdef LIBRARY_EXPORTS +#ifdef JL_LIBRARY_EXPORTS #define jl_setjmp_f ijl_setjmp #define jl_setjmp_name "ijl_setjmp" #define jl_setjmp(a,b) ijl_setjmp(a) @@ -2114,7 +2117,7 @@ void (ijl_longjmp)(jmp_buf _Buf, int _Value); #define jl_setjmp(a,b) sigsetjmp(a,b) #if defined(_COMPILER_ASAN_ENABLED_) && __GLIBC__ // Bypass the ASAN longjmp wrapper - we're unpoisoning the stack ourselves. -extern int __attribute__ ((nothrow)) (__libc_siglongjmp)(jl_jmp_buf buf, int val); +JL_DLLIMPORT int __attribute__ ((nothrow)) (__libc_siglongjmp)(jl_jmp_buf buf, int val); #define jl_longjmp(a,b) __libc_siglongjmp(a,b) #else #define jl_longjmp(a,b) siglongjmp(a,b) diff --git a/src/julia_internal.h b/src/julia_internal.h index 1ce04cccf020f..49f0b19ec4209 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -4,6 +4,7 @@ #define JL_INTERNAL_H #include "options.h" +#include "julia_assert.h" #include "julia_locks.h" #include "julia_threads.h" #include "support/utils.h" @@ -24,6 +25,9 @@ #include #endif +// pragma visibility is more useful than -fvisibility +#pragma GCC visibility push(hidden) + #ifdef __cplusplus extern "C" { #endif @@ -47,9 +51,9 @@ static inline uintptr_t jmpbuf_sp(jl_jmp_buf *buf) #else #error Need to implement jmpbuf_sp for this architecture #endif -void __sanitizer_start_switch_fiber(void**, const void*, size_t); -void __sanitizer_finish_switch_fiber(void*, const void**, size_t*); -extern void __asan_unpoison_stack_memory(uintptr_t addr, size_t size); +JL_DLLIMPORT void __sanitizer_start_switch_fiber(void**, const void*, size_t); +JL_DLLIMPORT void __sanitizer_finish_switch_fiber(void*, const void**, size_t*); +JL_DLLIMPORT void __asan_unpoison_stack_memory(uintptr_t addr, size_t size); static inline void asan_unpoison_task_stack(jl_task_t *ct, jl_jmp_buf *buf) { if (!ct) @@ -69,9 +73,9 @@ static inline void asan_unpoison_task_stack(jl_task_t *ct, jl_jmp_buf *buf) JL_N static inline void asan_unpoison_stack_memory(uintptr_t addr, size_t size) JL_NOTSAFEPOINT {} #endif #ifdef _COMPILER_MSAN_ENABLED_ -void __msan_unpoison(const volatile void *a, size_t size) JL_NOTSAFEPOINT; -void __msan_allocated_memory(const volatile void *a, size_t size) JL_NOTSAFEPOINT; -void __msan_unpoison_string(const volatile char *a) JL_NOTSAFEPOINT; +JL_DLLIMPORT void __msan_unpoison(const volatile void *a, size_t size) JL_NOTSAFEPOINT; +JL_DLLIMPORT void __msan_allocated_memory(const volatile void *a, size_t size) JL_NOTSAFEPOINT; +JL_DLLIMPORT void __msan_unpoison_string(const volatile char *a) JL_NOTSAFEPOINT; static inline void msan_allocated_memory(const volatile void *a, size_t size) JL_NOTSAFEPOINT { __msan_allocated_memory(a, size); } @@ -87,10 +91,10 @@ static inline void msan_allocated_memory(const volatile void *a, size_t size) JL static inline void msan_unpoison_string(const volatile char *a) JL_NOTSAFEPOINT {} #endif #ifdef _COMPILER_TSAN_ENABLED_ -void *__tsan_create_fiber(unsigned flags); -void *__tsan_get_current_fiber(void); -void __tsan_destroy_fiber(void *fiber); -void __tsan_switch_to_fiber(void *fiber, unsigned flags); +JL_DLLIMPORT void *__tsan_create_fiber(unsigned flags); +JL_DLLIMPORT void *__tsan_get_current_fiber(void); +JL_DLLIMPORT void __tsan_destroy_fiber(void *fiber); +JL_DLLIMPORT void __tsan_switch_to_fiber(void *fiber, unsigned flags); #endif #ifdef __cplusplus } @@ -304,8 +308,8 @@ static inline void memmove_refs(void **dstp, void *const *srcp, size_t n) JL_NOT #define GC_IN_IMAGE 4 // useful constants -extern jl_methtable_t *jl_type_type_mt JL_GLOBALLY_ROOTED; -extern jl_methtable_t *jl_nonfunction_mt JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_methtable_t *jl_type_type_mt JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_methtable_t *jl_nonfunction_mt JL_GLOBALLY_ROOTED; extern jl_methtable_t *jl_kwcall_mt JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_method_t *jl_opaque_closure_method JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT _Atomic(size_t) jl_world_counter; @@ -616,10 +620,6 @@ typedef union { JL_DLLEXPORT jl_code_info_t *jl_type_infer(jl_method_instance_t *li, size_t world, int force); JL_DLLEXPORT jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *meth JL_PROPAGATES_ROOT, size_t world); -JL_DLLEXPORT void *jl_compile_oc_wrapper(jl_code_instance_t *ci); -jl_code_instance_t *jl_generate_fptr(jl_method_instance_t *mi JL_PROPAGATES_ROOT, size_t world); -void jl_generate_fptr_for_unspecialized(jl_code_instance_t *unspec); -void jl_generate_fptr_for_oc_wrapper(jl_code_instance_t *unspec); JL_DLLEXPORT jl_code_instance_t *jl_get_method_inferred( jl_method_instance_t *mi JL_PROPAGATES_ROOT, jl_value_t *rettype, size_t min_world, size_t max_world); @@ -632,8 +632,8 @@ jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *lam JL_PROPAGATES_ int jl_code_requires_compiler(jl_code_info_t *src, int include_force_compile); jl_code_info_t *jl_new_code_info_from_ir(jl_expr_t *ast); JL_DLLEXPORT jl_code_info_t *jl_new_code_info_uninit(void); -void jl_resolve_globals_in_ir(jl_array_t *stmts, jl_module_t *m, jl_svec_t *sparam_vals, - int binding_effects); +JL_DLLEXPORT void jl_resolve_globals_in_ir(jl_array_t *stmts, jl_module_t *m, jl_svec_t *sparam_vals, + int binding_effects); int get_next_edge(jl_array_t *list, int i, jl_value_t** invokesig, jl_method_instance_t **caller) JL_NOTSAFEPOINT; int set_next_edge(jl_array_t *list, int i, jl_value_t *invokesig, jl_method_instance_t *caller); @@ -667,7 +667,7 @@ void jl_install_thread_signal_handler(jl_ptls_t ptls); JL_DLLEXPORT jl_fptr_args_t jl_get_builtin_fptr(jl_value_t *b); extern uv_loop_t *jl_io_loop; -void jl_uv_flush(uv_stream_t *stream); +JL_DLLEXPORT void jl_uv_flush(uv_stream_t *stream); typedef struct jl_typeenv_t { jl_tvar_t *var; @@ -762,7 +762,6 @@ jl_methtable_t *jl_kwmethod_table_for( jl_value_t *argtypes JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_methtable_t *jl_method_get_table( jl_method_t *method JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; -jl_methtable_t *jl_argument_method_table(jl_value_t *argt JL_PROPAGATES_ROOT); JL_DLLEXPORT int jl_pointer_egal(jl_value_t *t); JL_DLLEXPORT jl_value_t *jl_nth_slot_type(jl_value_t *sig JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT; @@ -770,7 +769,7 @@ void jl_compute_field_offsets(jl_datatype_t *st); jl_array_t *jl_new_array_for_deserialization(jl_value_t *atype, uint32_t ndims, size_t *dims, int isunboxed, int hasptr, int isunion, int elsz); void jl_module_run_initializer(jl_module_t *m); -jl_binding_t *jl_get_module_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc); +JL_DLLEXPORT jl_binding_t *jl_get_module_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc); JL_DLLEXPORT void jl_binding_deprecation_warning(jl_module_t *m, jl_sym_t *sym, jl_binding_t *b); extern jl_array_t *jl_module_init_order JL_GLOBALLY_ROOTED; extern htable_t jl_current_modules JL_GLOBALLY_ROOTED; @@ -778,7 +777,6 @@ extern JL_DLLEXPORT jl_module_t *jl_precompile_toplevel_module JL_GLOBALLY_ROOTE extern jl_array_t *jl_global_roots_table JL_GLOBALLY_ROOTED; JL_DLLEXPORT int jl_is_globally_rooted(jl_value_t *val JL_MAYBE_UNROOTED) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_as_global_root(jl_value_t *val JL_MAYBE_UNROOTED); -int jl_compile_extern_c(LLVMOrcThreadSafeModuleRef llvmmod, void *params, void *sysimg, jl_value_t *declrt, jl_value_t *sigt); jl_opaque_closure_t *jl_new_opaque_closure(jl_tupletype_t *argt, jl_value_t *rt_lb, jl_value_t *rt_ub, jl_value_t *source, jl_value_t **env, size_t nenv, int do_compile); @@ -856,7 +854,6 @@ void jl_init_flisp(void); void jl_init_common_symbols(void); void jl_init_primitives(void) JL_GC_DISABLED; void jl_init_llvm(void); -void jl_init_codegen(void); void jl_init_runtime_ccall(void); void jl_init_intrinsic_functions(void); void jl_init_intrinsic_properties(void); @@ -870,15 +867,12 @@ void jl_init_thread_heap(jl_ptls_t ptls) JL_NOTSAFEPOINT; void jl_init_int32_int64_cache(void); JL_DLLEXPORT void jl_init_options(void); -void jl_teardown_codegen(void) JL_NOTSAFEPOINT; - void jl_set_base_ctx(char *__stk); extern JL_DLLEXPORT ssize_t jl_tls_offset; extern JL_DLLEXPORT const int jl_tls_elf_support; void jl_init_threading(void); void jl_start_threads(void); -int jl_effective_threads(void); // Whether the GC is running extern char *jl_safepoint_pages; @@ -961,27 +955,6 @@ size_t external_blob_index(jl_value_t *v) JL_NOTSAFEPOINT; uint8_t jl_object_in_image(jl_value_t* v) JL_NOTSAFEPOINT; -typedef struct { - LLVMOrcThreadSafeModuleRef TSM; - LLVMValueRef F; -} jl_llvmf_dump_t; - -JL_DLLEXPORT jl_value_t *jl_dump_method_asm(jl_method_instance_t *linfo, size_t world, - char raw_mc, char getwrapper, const char* asm_variant, const char *debuginfo, char binary); -JL_DLLEXPORT void jl_get_llvmf_defn(jl_llvmf_dump_t* dump, jl_method_instance_t *linfo, size_t world, char getwrapper, char optimize, const jl_cgparams_t params); -JL_DLLEXPORT jl_value_t *jl_dump_fptr_asm(uint64_t fptr, char raw_mc, const char* asm_variant, const char *debuginfo, char binary); -JL_DLLEXPORT jl_value_t *jl_dump_function_ir(jl_llvmf_dump_t *dump, char strip_ir_metadata, char dump_module, const char *debuginfo); -JL_DLLEXPORT jl_value_t *jl_dump_function_asm(jl_llvmf_dump_t *dump, char raw_mc, const char* asm_variant, const char *debuginfo, char binary); - -void *jl_create_native(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvmmod, const jl_cgparams_t *cgparams, int policy, int imaging_mode, int cache, size_t world); -void jl_dump_native(void *native_code, - const char *bc_fname, const char *unopt_bc_fname, const char *obj_fname, const char *asm_fname, - const char *sysimg_data, size_t sysimg_len, ios_t *s); -void jl_get_llvm_gvs(void *native_code, arraylist_t *gvs); -void jl_get_llvm_external_fns(void *native_code, arraylist_t *gvs); -JL_DLLEXPORT void jl_get_function_id(void *native_code, jl_code_instance_t *ncode, - int32_t *func_idx, int32_t *specfunc_idx); - // the first argument to jl_idtable_rehash is used to return a value // make sure it is rooted if it is used after the function returns JL_DLLEXPORT jl_array_t *jl_idtable_rehash(jl_array_t *a, size_t newsz); @@ -993,7 +966,7 @@ JL_DLLEXPORT jl_methtable_t *jl_new_method_table(jl_sym_t *name, jl_module_t *mo JL_DLLEXPORT jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types, size_t world, size_t *min_valid, size_t *max_valid, int mt_cache); jl_method_instance_t *jl_get_specialized(jl_method_t *m, jl_value_t *types, jl_svec_t *sp); JL_DLLEXPORT jl_value_t *jl_rettype_inferred(jl_method_instance_t *li JL_PROPAGATES_ROOT, size_t min_world, size_t max_world); -JL_DLLEXPORT jl_code_instance_t *jl_method_compiled(jl_method_instance_t *mi JL_PROPAGATES_ROOT, size_t world); +JL_DLLEXPORT jl_code_instance_t *jl_method_compiled(jl_method_instance_t *mi JL_PROPAGATES_ROOT, size_t world) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_methtable_lookup(jl_methtable_t *mt JL_PROPAGATES_ROOT, jl_value_t *type, size_t world); JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo( jl_method_t *m JL_PROPAGATES_ROOT, jl_value_t *type, jl_svec_t *sparams); @@ -1002,14 +975,15 @@ JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, JL_DLLEXPORT void jl_method_table_add_backedge(jl_methtable_t *mt, jl_value_t *typ, jl_value_t *caller); JL_DLLEXPORT void jl_mi_cache_insert(jl_method_instance_t *mi JL_ROOTING_ARGUMENT, jl_code_instance_t *ci JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED); +JL_DLLEXPORT extern jl_value_t *(*const jl_rettype_inferred_addr)(jl_method_instance_t *mi JL_PROPAGATES_ROOT, size_t min_world, size_t max_world) JL_NOTSAFEPOINT; uint32_t jl_module_next_counter(jl_module_t *m) JL_NOTSAFEPOINT; jl_tupletype_t *arg_type_tuple(jl_value_t *arg1, jl_value_t **args, size_t nargs); JL_DLLEXPORT int jl_has_meta(jl_array_t *body, jl_sym_t *sym) JL_NOTSAFEPOINT; -jl_value_t *jl_parse(const char *text, size_t text_len, jl_value_t *filename, - size_t lineno, size_t offset, jl_value_t *options); +JL_DLLEXPORT jl_value_t *jl_parse(const char *text, size_t text_len, jl_value_t *filename, + size_t lineno, size_t offset, jl_value_t *options); //-------------------------------------------------- // Backtraces @@ -1126,8 +1100,6 @@ typedef struct { int inlined; } jl_frame_t; -// Might be called from unmanaged thread -uint64_t jl_getUnwindInfo(uint64_t dwBase); #ifdef _OS_WINDOWS_ #include JL_DLLEXPORT EXCEPTION_DISPOSITION NTAPI __julia_personality( @@ -1146,7 +1118,9 @@ extern JL_DLLEXPORT uv_mutex_t jl_in_stackwalk; #elif !defined(JL_DISABLE_LIBUNWIND) // This gives unwind only local unwinding options ==> faster code # define UNW_LOCAL_ONLY +#pragma GCC visibility push(default) # include +#pragma GCC visibility pop typedef unw_context_t bt_context_t; typedef unw_cursor_t bt_cursor_t; # if (!defined(SYSTEM_LIBUNWIND) || UNW_VERSION_MAJOR > 1 || \ @@ -1171,7 +1145,6 @@ size_t rec_backtrace_ctx_dwarf(jl_bt_element_t *bt_data, size_t maxsize, bt_cont JL_DLLEXPORT jl_value_t *jl_get_backtrace(void); void jl_critical_error(int sig, int si_code, bt_context_t *context, jl_task_t *ct); JL_DLLEXPORT void jl_raise_debugger(void) JL_NOTSAFEPOINT; -int jl_getFunctionInfo(jl_frame_t **frames, uintptr_t pointer, int skipC, int noInline) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_gdblookup(void* ip) JL_NOTSAFEPOINT; void jl_print_native_codeloc(uintptr_t ip) JL_NOTSAFEPOINT; void jl_print_bt_entry_codeloc(jl_bt_element_t *bt_data) JL_NOTSAFEPOINT; @@ -1259,6 +1232,7 @@ JL_DLLEXPORT extern void *jl_exe_handle; JL_DLLEXPORT extern void *jl_libjulia_handle; JL_DLLEXPORT extern void *jl_libjulia_internal_handle; JL_DLLEXPORT extern void *jl_RTLD_DEFAULT_handle; + #if defined(_OS_WINDOWS_) JL_DLLEXPORT extern const char *jl_crtdll_basename; extern void *jl_ntdll_handle; @@ -1612,8 +1586,6 @@ JL_DLLEXPORT enum jl_memory_order jl_get_atomic_order_checked(jl_sym_t *order, c struct _jl_image_fptrs_t; -void jl_register_fptrs(uint64_t image_base, const struct _jl_image_fptrs_t *fptrs, - jl_method_instance_t **linfos, size_t n); void jl_write_coverage_data(const char*); void jl_write_malloc_log(void); @@ -1670,10 +1642,49 @@ JL_DLLEXPORT uint16_t julia__truncdfhf2(double param) JL_NOTSAFEPOINT; JL_DLLEXPORT uint32_t jl_crc32c(uint32_t crc, const char *buf, size_t len); +// -- exports from codegen -- // + +JL_DLLIMPORT jl_code_instance_t *jl_generate_fptr(jl_method_instance_t *mi JL_PROPAGATES_ROOT, size_t world); +JL_DLLIMPORT void jl_generate_fptr_for_unspecialized(jl_code_instance_t *unspec); +JL_DLLIMPORT void jl_generate_fptr_for_oc_wrapper(jl_code_instance_t *unspec); +JL_DLLIMPORT int jl_compile_extern_c(LLVMOrcThreadSafeModuleRef llvmmod, void *params, void *sysimg, jl_value_t *declrt, jl_value_t *sigt); + +typedef struct { + LLVMOrcThreadSafeModuleRef TSM; + LLVMValueRef F; +} jl_llvmf_dump_t; + +JL_DLLIMPORT jl_value_t *jl_dump_method_asm(jl_method_instance_t *linfo, size_t world, + char raw_mc, char getwrapper, const char* asm_variant, const char *debuginfo, char binary); +JL_DLLIMPORT void jl_get_llvmf_defn(jl_llvmf_dump_t* dump, jl_method_instance_t *linfo, size_t world, char getwrapper, char optimize, const jl_cgparams_t params); +JL_DLLIMPORT jl_value_t *jl_dump_fptr_asm(uint64_t fptr, char raw_mc, const char* asm_variant, const char *debuginfo, char binary); +JL_DLLIMPORT jl_value_t *jl_dump_function_ir(jl_llvmf_dump_t *dump, char strip_ir_metadata, char dump_module, const char *debuginfo); +JL_DLLIMPORT jl_value_t *jl_dump_function_asm(jl_llvmf_dump_t *dump, char raw_mc, const char* asm_variant, const char *debuginfo, char binary); + +JL_DLLIMPORT void *jl_create_native(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvmmod, const jl_cgparams_t *cgparams, int policy, int imaging_mode, int cache, size_t world); +JL_DLLIMPORT void jl_dump_native(void *native_code, + const char *bc_fname, const char *unopt_bc_fname, const char *obj_fname, const char *asm_fname, + const char *sysimg_data, size_t sysimg_len, ios_t *s); +JL_DLLIMPORT void jl_get_llvm_gvs(void *native_code, arraylist_t *gvs); +JL_DLLIMPORT void jl_get_llvm_external_fns(void *native_code, arraylist_t *gvs); +JL_DLLIMPORT void jl_get_function_id(void *native_code, jl_code_instance_t *ncode, + int32_t *func_idx, int32_t *specfunc_idx); +JL_DLLIMPORT void jl_register_fptrs(uint64_t image_base, const struct _jl_image_fptrs_t *fptrs, + jl_method_instance_t **linfos, size_t n); + +JL_DLLIMPORT void jl_init_codegen(void); +JL_DLLIMPORT void jl_teardown_codegen(void) JL_NOTSAFEPOINT; +JL_DLLIMPORT int jl_getFunctionInfo(jl_frame_t **frames, uintptr_t pointer, int skipC, int noInline) JL_NOTSAFEPOINT; +// n.b. this might be called from unmanaged thread: +JL_DLLIMPORT uint64_t jl_getUnwindInfo(uint64_t dwBase); + #ifdef __cplusplus } #endif +#pragma GCC visibility pop + + #ifdef USE_DTRACE // Generated file, needs to be searched in include paths so that the builddir // retains priority diff --git a/src/julia_locks.h b/src/julia_locks.h index 2fbaffa5e47c3..47e258f69aab2 100644 --- a/src/julia_locks.h +++ b/src/julia_locks.h @@ -3,8 +3,6 @@ #ifndef JL_LOCKS_H #define JL_LOCKS_H -#include "julia_assert.h" - #ifdef __cplusplus extern "C" { #endif diff --git a/src/julia_threads.h b/src/julia_threads.h index 07c722253c7f5..c8242d6d6eb0f 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -80,6 +80,7 @@ typedef struct { void *stacktop; } _jl_ucontext_t; #endif +#pragma GCC visibility push(default) #if defined(JL_HAVE_UNW_CONTEXT) #define UNW_LOCAL_ONLY #include @@ -89,6 +90,7 @@ typedef unw_context_t _jl_ucontext_t; #include typedef ucontext_t _jl_ucontext_t; #endif +#pragma GCC visibility pop #endif typedef struct { @@ -276,13 +278,13 @@ typedef struct _jl_tls_states_t { ) // some hidden state (usually just because we don't have the type's size declaration) -#ifdef LIBRARY_EXPORTS +#ifdef JL_LIBRARY_EXPORTS uv_mutex_t sleep_lock; uv_cond_t wake_signal; #endif } jl_tls_states_t; -#ifndef LIBRARY_EXPORTS +#ifndef JL_LIBRARY_EXPORTS // deprecated (only for external consumers) JL_DLLEXPORT void *jl_get_ptls_states(void); #endif diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index 1a524cbe8d419..85befbe82cf53 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -1286,7 +1286,8 @@ PreservedAnalyses AllocOptPass::run(Function &F, FunctionAnalysisManager &AM) { } } -extern "C" JL_DLLEXPORT void LLVMExtraAddAllocOptPass_impl(LLVMPassManagerRef PM) +extern "C" JL_DLLEXPORT_CODEGEN +void LLVMExtraAddAllocOptPass_impl(LLVMPassManagerRef PM) { unwrap(PM)->add(createAllocOptPass()); } diff --git a/src/llvm-cpufeatures.cpp b/src/llvm-cpufeatures.cpp index b9bff66092d75..45637a4c567f6 100644 --- a/src/llvm-cpufeatures.cpp +++ b/src/llvm-cpufeatures.cpp @@ -150,7 +150,8 @@ Pass *createCPUFeaturesPass() return new CPUFeaturesLegacy(); } -extern "C" JL_DLLEXPORT void LLVMExtraAddCPUFeaturesPass_impl(LLVMPassManagerRef PM) +extern "C" JL_DLLEXPORT_CODEGEN +void LLVMExtraAddCPUFeaturesPass_impl(LLVMPassManagerRef PM) { unwrap(PM)->add(createCPUFeaturesPass()); } diff --git a/src/llvm-demote-float16.cpp b/src/llvm-demote-float16.cpp index e27d3f19b3f21..6ff7feaa550c8 100644 --- a/src/llvm-demote-float16.cpp +++ b/src/llvm-demote-float16.cpp @@ -220,7 +220,8 @@ Pass *createDemoteFloat16Pass() return new DemoteFloat16Legacy(); } -extern "C" JL_DLLEXPORT void LLVMExtraAddDemoteFloat16Pass_impl(LLVMPassManagerRef PM) +extern "C" JL_DLLEXPORT_CODEGEN +void LLVMExtraAddDemoteFloat16Pass_impl(LLVMPassManagerRef PM) { unwrap(PM)->add(createDemoteFloat16Pass()); } diff --git a/src/llvm-final-gc-lowering.cpp b/src/llvm-final-gc-lowering.cpp index fc9f76ef27fa7..ac7d67cddd6f3 100644 --- a/src/llvm-final-gc-lowering.cpp +++ b/src/llvm-final-gc-lowering.cpp @@ -451,7 +451,8 @@ Pass *createFinalLowerGCPass() return new FinalLowerGCLegacy(); } -extern "C" JL_DLLEXPORT void LLVMExtraAddFinalLowerGCPass_impl(LLVMPassManagerRef PM) +extern "C" JL_DLLEXPORT_CODEGEN +void LLVMExtraAddFinalLowerGCPass_impl(LLVMPassManagerRef PM) { unwrap(PM)->add(createFinalLowerGCPass()); } diff --git a/src/llvm-gc-invariant-verifier.cpp b/src/llvm-gc-invariant-verifier.cpp index af9a1862089e4..26288dc09379d 100644 --- a/src/llvm-gc-invariant-verifier.cpp +++ b/src/llvm-gc-invariant-verifier.cpp @@ -222,7 +222,8 @@ Pass *createGCInvariantVerifierPass(bool Strong) { return new GCInvariantVerifierLegacy(Strong); } -extern "C" JL_DLLEXPORT void LLVMExtraAddGCInvariantVerifierPass_impl(LLVMPassManagerRef PM, LLVMBool Strong) +extern "C" JL_DLLEXPORT_CODEGEN +void LLVMExtraAddGCInvariantVerifierPass_impl(LLVMPassManagerRef PM, LLVMBool Strong) { unwrap(PM)->add(createGCInvariantVerifierPass(Strong)); } diff --git a/src/llvm-julia-licm.cpp b/src/llvm-julia-licm.cpp index 7bc8d91b525f3..a8682a49fba53 100644 --- a/src/llvm-julia-licm.cpp +++ b/src/llvm-julia-licm.cpp @@ -424,7 +424,8 @@ Pass *createJuliaLICMPass() return new JuliaLICMPassLegacy(); } -extern "C" JL_DLLEXPORT void LLVMExtraJuliaLICMPass_impl(LLVMPassManagerRef PM) +extern "C" JL_DLLEXPORT_CODEGEN +void LLVMExtraJuliaLICMPass_impl(LLVMPassManagerRef PM) { unwrap(PM)->add(createJuliaLICMPass()); } diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index a836ff1361768..cb466b7cb1c93 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -2792,7 +2792,8 @@ Pass *createLateLowerGCFramePass() { return new LateLowerGCFrameLegacy(); } -extern "C" JL_DLLEXPORT void LLVMExtraAddLateLowerGCFramePass_impl(LLVMPassManagerRef PM) +extern "C" JL_DLLEXPORT_CODEGEN +void LLVMExtraAddLateLowerGCFramePass_impl(LLVMPassManagerRef PM) { unwrap(PM)->add(createLateLowerGCFramePass()); } diff --git a/src/llvm-lower-handlers.cpp b/src/llvm-lower-handlers.cpp index 919128769019b..4f9706e04252d 100644 --- a/src/llvm-lower-handlers.cpp +++ b/src/llvm-lower-handlers.cpp @@ -271,7 +271,8 @@ Pass *createLowerExcHandlersPass() return new LowerExcHandlersLegacy(); } -extern "C" JL_DLLEXPORT void LLVMExtraAddLowerExcHandlersPass_impl(LLVMPassManagerRef PM) +extern "C" JL_DLLEXPORT_CODEGEN +void LLVMExtraAddLowerExcHandlersPass_impl(LLVMPassManagerRef PM) { unwrap(PM)->add(createLowerExcHandlersPass()); } diff --git a/src/llvm-muladd.cpp b/src/llvm-muladd.cpp index 1f45075dd25c8..efe0acb36f1fc 100644 --- a/src/llvm-muladd.cpp +++ b/src/llvm-muladd.cpp @@ -139,7 +139,8 @@ Pass *createCombineMulAddPass() return new CombineMulAddLegacy(); } -extern "C" JL_DLLEXPORT void LLVMExtraAddCombineMulAddPass_impl(LLVMPassManagerRef PM) +extern "C" JL_DLLEXPORT_CODEGEN +void LLVMExtraAddCombineMulAddPass_impl(LLVMPassManagerRef PM) { unwrap(PM)->add(createCombineMulAddPass()); } diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 21a090724802a..5baccbf4045b9 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -495,14 +495,16 @@ void CloneCtx::prepare_slots() for (auto &F : orig_funcs) { if (F->hasFnAttribute("julia.mv.reloc")) { assert(F->hasFnAttribute("julia.mv.clones")); + GlobalVariable *GV = new GlobalVariable(M, F->getType(), false, GlobalValue::ExternalLinkage, nullptr, F->getName() + ".reloc_slot"); + GV->setVisibility(GlobalValue::HiddenVisibility); + GV->setDSOLocal(true); if (F->isDeclaration()) { - auto GV = new GlobalVariable(M, F->getType(), false, GlobalValue::ExternalLinkage, nullptr, F->getName() + ".reloc_slot"); extern_relocs[F] = GV; - } else { + } + else { auto id = get_func_id(F); - auto GV = new GlobalVariable(M, F->getType(), false, GlobalValue::ExternalLinkage, Constant::getNullValue(F->getType()), F->getName() + ".reloc_slot"); - GV->setVisibility(GlobalValue::HiddenVisibility); const_relocs[id] = GV; + GV->setInitializer(Constant::getNullValue(F->getType())); } } } @@ -525,6 +527,7 @@ void CloneCtx::clone_decls() auto new_F = Function::Create(F->getFunctionType(), F->getLinkage(), F->getName() + suffixes[i], &M); new_F->copyAttributesFrom(F); new_F->setVisibility(F->getVisibility()); + new_F->setDSOLocal(true); auto base_func = F; if (specs[i].flags & JL_TARGET_CLONE_ALL) base_func = static_cast(linearized[specs[i].base])->base_func(F); @@ -668,6 +671,7 @@ void CloneCtx::rewrite_alias(GlobalAlias *alias, Function *F) trampoline->copyAttributesFrom(F); trampoline->takeName(alias); trampoline->setVisibility(alias->getVisibility()); + trampoline->setDSOLocal(alias->isDSOLocal()); // drop multiversioning attributes, add alias attribute for testing purposes trampoline->removeFnAttr("julia.mv.reloc"); trampoline->removeFnAttr("julia.mv.clones"); @@ -754,7 +758,8 @@ std::pair CloneCtx::get_reloc_slot(Function *F) const auto extern_decl = extern_relocs.find(F); assert(extern_decl != extern_relocs.end() && "Missing extern relocation slot!"); return {(uint32_t)-1, extern_decl->second}; - } else { + } + else { auto id = get_func_id(F); auto slot = const_relocs.find(id); assert(slot != const_relocs.end() && "Missing relocation slot!"); @@ -886,9 +891,11 @@ static Constant *emit_offset_table(Module &M, Type *T_size, const std::vectorsetVisibility(GlobalValue::HiddenVisibility); + ga->setDSOLocal(true); } else { auto gv = new GlobalVariable(M, T_size, true, GlobalValue::ExternalLinkage, Constant::getNullValue(T_size), name + "_base" + suffix); gv->setVisibility(GlobalValue::HiddenVisibility); + gv->setDSOLocal(true); base = gv; } auto vbase = ConstantExpr::getPtrToInt(base, T_size); @@ -905,6 +912,7 @@ static Constant *emit_offset_table(Module &M, Type *T_size, const std::vectorsetVisibility(GlobalValue::HiddenVisibility); + gv->setDSOLocal(true); return vbase; } @@ -966,6 +974,7 @@ void CloneCtx::emit_metadata() ConstantArray::get(vars_type, values), "jl_clone_slots" + suffix); gv->setVisibility(GlobalValue::HiddenVisibility); + gv->setDSOLocal(true); } // Generate `jl_dispatch_fvars_idxs` and `jl_dispatch_fvars_offsets` @@ -1017,12 +1026,14 @@ void CloneCtx::emit_metadata() GlobalVariable::ExternalLinkage, idxval, "jl_clone_idxs" + suffix); gv1->setVisibility(GlobalValue::HiddenVisibility); + gv1->setDSOLocal(true); ArrayType *offsets_type = ArrayType::get(Type::getInt32Ty(M.getContext()), offsets.size()); auto gv2 = new GlobalVariable(M, offsets_type, true, GlobalVariable::ExternalLinkage, ConstantArray::get(offsets_type, offsets), "jl_clone_offsets" + suffix); gv2->setVisibility(GlobalValue::HiddenVisibility); + gv2->setDSOLocal(true); } } @@ -1145,7 +1156,8 @@ Pass *createMultiVersioningPass(bool allow_bad_fvars) return new MultiVersioningLegacy(allow_bad_fvars); } -extern "C" JL_DLLEXPORT void LLVMExtraAddMultiVersioningPass_impl(LLVMPassManagerRef PM) +extern "C" JL_DLLEXPORT_CODEGEN +void LLVMExtraAddMultiVersioningPass_impl(LLVMPassManagerRef PM) { unwrap(PM)->add(createMultiVersioningPass(false)); } diff --git a/src/llvm-propagate-addrspaces.cpp b/src/llvm-propagate-addrspaces.cpp index 91bec48bca861..2158109cea120 100644 --- a/src/llvm-propagate-addrspaces.cpp +++ b/src/llvm-propagate-addrspaces.cpp @@ -330,7 +330,8 @@ PreservedAnalyses PropagateJuliaAddrspacesPass::run(Function &F, FunctionAnalysi } } -extern "C" JL_DLLEXPORT void LLVMExtraAddPropagateJuliaAddrspaces_impl(LLVMPassManagerRef PM) +extern "C" JL_DLLEXPORT_CODEGEN +void LLVMExtraAddPropagateJuliaAddrspaces_impl(LLVMPassManagerRef PM) { unwrap(PM)->add(createPropagateJuliaAddrspaces()); } diff --git a/src/llvm-ptls.cpp b/src/llvm-ptls.cpp index 8174832b3cebf..d4032129e76dc 100644 --- a/src/llvm-ptls.cpp +++ b/src/llvm-ptls.cpp @@ -57,7 +57,7 @@ struct LowerPTLS { void set_pgcstack_attrs(CallInst *pgcstack) const; Instruction *emit_pgcstack_tp(Value *offset, Instruction *insertBefore) const; template T *add_comdat(T *G) const; - GlobalVariable *create_aliased_global(Type *T, StringRef name) const; + GlobalVariable *create_hidden_global(Type *T, StringRef name) const; void fix_pgcstack_use(CallInst *pgcstack, Function *pgcstack_getter, bool or_new, bool *CFGModified); }; @@ -125,14 +125,12 @@ Instruction *LowerPTLS::emit_pgcstack_tp(Value *offset, Instruction *insertBefor return new LoadInst(T_pppjlvalue, tls, "pgcstack", false, insertBefore); } -GlobalVariable *LowerPTLS::create_aliased_global(Type *T, StringRef name) const +GlobalVariable *LowerPTLS::create_hidden_global(Type *T, StringRef name) const { - // Create a static global variable and points a global alias to it so that - // the address is visible externally but LLVM can still assume that the - // address of this variable doesn't need dynamic relocation - // (can be accessed with a single PC-rel load). auto GV = new GlobalVariable(*M, T, false, GlobalVariable::ExternalLinkage, nullptr, name); + GV->setVisibility(GlobalValue::HiddenVisibility); + GV->setDSOLocal(true); return GV; } @@ -304,9 +302,9 @@ bool LowerPTLS::run(bool *CFGModified) T_pgcstack_getter = FT_pgcstack_getter->getPointerTo(); T_pppjlvalue = cast(FT_pgcstack_getter->getReturnType()); if (imaging_mode) { - pgcstack_func_slot = create_aliased_global(T_pgcstack_getter, "jl_pgcstack_func_slot"); - pgcstack_key_slot = create_aliased_global(T_size, "jl_pgcstack_key_slot"); // >= sizeof(jl_pgcstack_key_t) - pgcstack_offset = create_aliased_global(T_size, "jl_tls_offset"); + pgcstack_func_slot = create_hidden_global(T_pgcstack_getter, "jl_pgcstack_func_slot"); + pgcstack_key_slot = create_hidden_global(T_size, "jl_pgcstack_key_slot"); // >= sizeof(jl_pgcstack_key_t) + pgcstack_offset = create_hidden_global(T_size, "jl_tls_offset"); } need_init = false; } @@ -372,7 +370,8 @@ Pass *createLowerPTLSPass(bool imaging_mode) return new LowerPTLSLegacy(imaging_mode); } -extern "C" JL_DLLEXPORT void LLVMExtraAddLowerPTLSPass_impl(LLVMPassManagerRef PM, LLVMBool imaging_mode) +extern "C" JL_DLLEXPORT_CODEGEN +void LLVMExtraAddLowerPTLSPass_impl(LLVMPassManagerRef PM, LLVMBool imaging_mode) { unwrap(PM)->add(createLowerPTLSPass(imaging_mode)); } diff --git a/src/llvm-remove-addrspaces.cpp b/src/llvm-remove-addrspaces.cpp index 32bd4e563ac92..650c457ad4a7c 100644 --- a/src/llvm-remove-addrspaces.cpp +++ b/src/llvm-remove-addrspaces.cpp @@ -531,7 +531,8 @@ PreservedAnalyses RemoveJuliaAddrspacesPass::run(Module &M, ModuleAnalysisManage return RemoveAddrspacesPass(removeJuliaAddrspaces).run(M, AM); } -extern "C" JL_DLLEXPORT void LLVMExtraAddRemoveJuliaAddrspacesPass_impl(LLVMPassManagerRef PM) +extern "C" JL_DLLEXPORT_CODEGEN +void LLVMExtraAddRemoveJuliaAddrspacesPass_impl(LLVMPassManagerRef PM) { unwrap(PM)->add(createRemoveJuliaAddrspacesPass()); } diff --git a/src/llvm-remove-ni.cpp b/src/llvm-remove-ni.cpp index d9e3357524a9a..b767074202eb2 100644 --- a/src/llvm-remove-ni.cpp +++ b/src/llvm-remove-ni.cpp @@ -68,7 +68,8 @@ Pass *createRemoveNIPass() return new RemoveNILegacy(); } -extern "C" JL_DLLEXPORT void LLVMExtraAddRemoveNIPass_impl(LLVMPassManagerRef PM) +extern "C" JL_DLLEXPORT_CODEGEN +void LLVMExtraAddRemoveNIPass_impl(LLVMPassManagerRef PM) { unwrap(PM)->add(createRemoveNIPass()); } diff --git a/src/llvm-simdloop.cpp b/src/llvm-simdloop.cpp index 3c94b226ad7b8..fd94ed989347c 100644 --- a/src/llvm-simdloop.cpp +++ b/src/llvm-simdloop.cpp @@ -343,12 +343,13 @@ static RegisterPass X("LowerSIMDLoop", "LowerSIMDLoop Pass" false /* Only looks at CFG */, false /* Analysis Pass */); -JL_DLLEXPORT Pass *createLowerSimdLoopPass() +Pass *createLowerSimdLoopPass() { return new LowerSIMDLoopLegacy(); } -extern "C" JL_DLLEXPORT void LLVMExtraAddLowerSimdLoopPass_impl(LLVMPassManagerRef PM) +extern "C" JL_DLLEXPORT_CODEGEN +void LLVMExtraAddLowerSimdLoopPass_impl(LLVMPassManagerRef PM) { unwrap(PM)->add(createLowerSimdLoopPass()); } diff --git a/src/llvmcalltest.cpp b/src/llvmcalltest.cpp index 352c4695f2f20..93c442445d79a 100644 --- a/src/llvmcalltest.cpp +++ b/src/llvmcalltest.cpp @@ -17,11 +17,7 @@ using namespace llvm; #ifdef _OS_WINDOWS_ # define DLLEXPORT __declspec(dllexport) #else -# if defined(_OS_LINUX_) -# define DLLEXPORT __attribute__ ((visibility("protected"))) -# else # define DLLEXPORT __attribute__ ((visibility("default"))) -# endif #endif extern "C" { diff --git a/src/method.c b/src/method.c index 1b6795902d837..c207149032fb9 100644 --- a/src/method.c +++ b/src/method.c @@ -964,12 +964,6 @@ JL_DLLEXPORT jl_methtable_t *jl_method_get_table(jl_method_t *method JL_PROPAGAT return method->external_mt ? (jl_methtable_t*)method->external_mt : jl_method_table_for(method->sig); } -// get the MethodTable implied by a single given type, or `nothing` -JL_DLLEXPORT jl_methtable_t *jl_argument_method_table(jl_value_t *argt JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT -{ - return nth_methtable(argt, 0); -} - jl_array_t *jl_all_methods JL_GLOBALLY_ROOTED; JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata, diff --git a/src/pipeline.cpp b/src/pipeline.cpp index 4403653a9d8e4..4e014d32a9241 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -720,7 +720,7 @@ void registerCallbacks(PassBuilder &PB) JL_NOTSAFEPOINT { }); } -extern "C" JL_DLLEXPORT ::llvm::PassPluginLibraryInfo -llvmGetPassPluginInfo() JL_NOTSAFEPOINT { +extern "C" JL_DLLEXPORT_CODEGEN +::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() JL_NOTSAFEPOINT { return {LLVM_PLUGIN_API_VERSION, "Julia", "1", registerCallbacks}; } diff --git a/src/rtutils.c b/src/rtutils.c index afea3ac2c2388..01ea11014a6db 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -1269,7 +1269,7 @@ JL_DLLEXPORT size_t jl_static_show_func_sig(JL_STREAM *s, jl_value_t *type) JL_N return jl_static_show_func_sig_(s, type, ctx); } -JL_DLLEXPORT size_t jl_static_show_func_sig_(JL_STREAM *s, jl_value_t *type, jl_static_show_config_t ctx) JL_NOTSAFEPOINT +size_t jl_static_show_func_sig_(JL_STREAM *s, jl_value_t *type, jl_static_show_config_t ctx) JL_NOTSAFEPOINT { size_t n = 0; size_t i; diff --git a/src/subtype.c b/src/subtype.c index ce44402dfbc59..fd9bd3e8be00f 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1646,11 +1646,12 @@ static int forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, in static void init_stenv(jl_stenv_t *e, jl_value_t **env, int envsz) { e->vars = NULL; - assert(env != NULL || envsz == 0); e->envsz = envsz; e->envout = env; - if (envsz) + if (envsz) { + assert(env != NULL); memset(env, 0, envsz*sizeof(void*)); + } e->envidx = 0; e->invdepth = 0; e->ignore_free = 0; diff --git a/src/support/Makefile b/src/support/Makefile index a884aa5fd47e0..1ee98a4eabdee 100644 --- a/src/support/Makefile +++ b/src/support/Makefile @@ -24,7 +24,7 @@ HEADERS := $(wildcard *.h) $(LIBUV_INC)/uv.h OBJS := $(SRCS:%=$(BUILDDIR)/%.o) DOBJS := $(SRCS:%=$(BUILDDIR)/%.dbg.obj) -FLAGS := $(HFILEDIRS:%=-I%) -I$(LIBUV_INC) -I$(UTF8PROC_INC) -DLIBRARY_EXPORTS -DUTF8PROC_EXPORTS +FLAGS := $(HFILEDIRS:%=-I%) -I$(LIBUV_INC) -I$(UTF8PROC_INC) -DJL_LIBRARY_EXPORTS_INTERNAL -DUTF8PROC_EXPORTS FLAGS += -Wall -Wno-strict-aliasing -fvisibility=hidden -Wpointer-arith -Wundef JCFLAGS += -Wold-style-definition -Wstrict-prototypes -Wc++-compat diff --git a/src/support/dtypes.h b/src/support/dtypes.h index 891c091413084..a30fe85ccc0d0 100644 --- a/src/support/dtypes.h +++ b/src/support/dtypes.h @@ -72,16 +72,24 @@ typedef intptr_t ssize_t; #ifdef _OS_WINDOWS_ #define STDCALL __stdcall -# ifdef LIBRARY_EXPORTS +# ifdef JL_LIBRARY_EXPORTS_INTERNAL # define JL_DLLEXPORT __declspec(dllexport) -# else -# define JL_DLLEXPORT __declspec(dllimport) # endif +# ifdef JL_LIBRARY_EXPORTS_CODEGEN +# define JL_DLLEXPORT_CODEGEN __declspec(dllexport) +# endif +#define JL_HIDDEN #define JL_DLLIMPORT __declspec(dllimport) #else #define STDCALL -# define JL_DLLEXPORT __attribute__ ((visibility("default"))) -#define JL_DLLIMPORT +#define JL_DLLIMPORT __attribute__ ((visibility("default"))) +#define JL_HIDDEN __attribute__ ((visibility("hidden"))) +#endif +#ifndef JL_DLLEXPORT +# define JL_DLLEXPORT JL_DLLIMPORT +#endif +#ifndef JL_DLLEXPORT_CODEGEN +# define JL_DLLEXPORT_CODEGEN JL_DLLIMPORT #endif #ifdef _OS_LINUX_ diff --git a/src/timing.h b/src/timing.h index 5d4e95c62bb67..8ea2791461fd5 100644 --- a/src/timing.h +++ b/src/timing.h @@ -27,7 +27,7 @@ void jl_destroy_timing(void) JL_NOTSAFEPOINT; // the subsystem in `jl_timing_names` matching the provided string. // // Returns -1 if no matching sub-system was found. -int jl_timing_set_enable(const char *subsystem, uint8_t enabled); +JL_DLLEXPORT int jl_timing_set_enable(const char *subsystem, uint8_t enabled); // Check for environment vars "JULIA_TIMING_METADATA_PRINT_LIMIT" and // "JULIA_TIMING_SUBSYSTEMS" and if present apply these to the metadata @@ -42,7 +42,7 @@ void jl_timing_apply_env(void); // Configurable item limit, runtime code should use this to limit printing // when adding potentially many items of metadata to a single timing zone. -extern uint32_t jl_timing_print_limit; +extern JL_DLLEXPORT uint32_t jl_timing_print_limit; #ifdef __cplusplus } From 7757e460af08d03a6de086b7d7db46ef6f617428 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Wed, 10 May 2023 18:05:42 -0400 Subject: [PATCH 730/775] Excise support for LLVM 13 (#49722) --- src/aotcompile.cpp | 4 --- src/ccall.cpp | 10 ------ src/cgutils.cpp | 18 ----------- src/codegen.cpp | 59 +--------------------------------- src/disasm.cpp | 10 ------ src/jitlayers.cpp | 52 +++--------------------------- src/jitlayers.h | 3 -- src/llvm-codegen-shared.h | 52 ------------------------------ src/llvm-multiversioning.cpp | 4 --- src/llvm-remove-addrspaces.cpp | 17 ---------- src/llvm-version.h | 4 +-- 11 files changed, 8 insertions(+), 225 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 4fc9ad4bdf596..cf6378b4f926b 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -9,11 +9,7 @@ #include #include #include -#if JL_LLVM_VERSION >= 140000 #include -#else -#include -#endif #include // analysis passes diff --git a/src/ccall.cpp b/src/ccall.cpp index 6b2143579317f..90f7417c03524 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -958,10 +958,8 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar // copy module properties that should always match Mod->setTargetTriple(jl_Module->getTargetTriple()); Mod->setDataLayout(jl_Module->getDataLayout()); -#if JL_LLVM_VERSION >= 130000 Mod->setStackProtectorGuard(jl_Module->getStackProtectorGuard()); Mod->setOverrideStackAlignment(jl_Module->getOverrideStackAlignment()); -#endif // verify the definition Function *def = Mod->getFunction(ir_name); @@ -1097,11 +1095,7 @@ std::string generate_func_sig(const char *fname) abi->use_sret(jl_voidpointer_type, LLVMCtx); } else if (abi->use_sret((jl_datatype_t*)rt, LLVMCtx)) { -#if JL_LLVM_VERSION >= 140000 AttrBuilder retattrs(LLVMCtx); -#else - AttrBuilder retattrs; -#endif if (!ctx->TargetTriple.isOSWindows()) { // llvm used to use the old mingw ABI, skipping this marking works around that difference retattrs.addStructRetAttr(lrt); @@ -1120,11 +1114,7 @@ std::string generate_func_sig(const char *fname) } for (size_t i = 0; i < nccallargs; ++i) { -#if JL_LLVM_VERSION >= 140000 AttrBuilder ab(LLVMCtx); -#else - AttrBuilder ab; -#endif jl_value_t *tti = jl_svecref(at, i); Type *t = NULL; bool isboxed; diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 46c7407e95d98..9e42a6b246e9b 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1768,12 +1768,8 @@ std::vector first_ptr(Type *T) num_elements = AT->getNumElements(); else { VectorType *VT = cast(T); -#if JL_LLVM_VERSION >= 120000 ElementCount EC = VT->getElementCount(); num_elements = EC.getKnownMinValue(); -#else - num_elements = VT->getNumElements(); -#endif } if (num_elements == 0) return {}; @@ -2015,12 +2011,7 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, assert(Order != AtomicOrdering::NotAtomic && r); // we can't handle isboxed here as a workaround for really bad LLVM // design issue: plain Xchg only works with integers -#if JL_LLVM_VERSION >= 130000 auto *store = ctx.builder.CreateAtomicRMW(AtomicRMWInst::Xchg, ptr, r, Align(alignment), Order); -#else - auto *store = ctx.builder.CreateAtomicRMW(AtomicRMWInst::Xchg, ptr, r, Order); - store->setAlignment(Align(alignment)); -#endif jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); ai.noalias = MDNode::concatenate(aliasscope, ai.noalias); ai.decorateInst(store); @@ -2170,12 +2161,7 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, FailOrder = AtomicOrdering::Monotonic; else if (FailOrder == AtomicOrdering::Unordered) FailOrder = AtomicOrdering::Monotonic; -#if JL_LLVM_VERSION >= 130000 auto *store = ctx.builder.CreateAtomicCmpXchg(ptr, Compare, r, Align(alignment), Order, FailOrder); -#else - auto *store = ctx.builder.CreateAtomicCmpXchg(ptr, Compare, r, Order, FailOrder); - store->setAlignment(Align(alignment)); -#endif jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); ai.noalias = MDNode::concatenate(aliasscope, ai.noalias); ai.decorateInst(store); @@ -3052,12 +3038,8 @@ static jl_value_t *static_constant_instance(const llvm::DataLayout &DL, Constant if (const auto *CC = dyn_cast(constant)) nargs = CC->getNumOperands(); else if (const auto *CAZ = dyn_cast(constant)) { -#if JL_LLVM_VERSION >= 130000 // SVE: Elsewhere we use `getMinKownValue` nargs = CAZ->getElementCount().getFixedValue(); -#else - nargs = CAZ->getNumElements(); -#endif } else if (const auto *CDS = dyn_cast(constant)) nargs = CDS->getNumElements(); diff --git a/src/codegen.cpp b/src/codegen.cpp index 10ee78ef6e8e1..a5d54f16ed2e6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -20,11 +20,7 @@ // target machine computation #include -#if JL_LLVM_VERSION >= 140000 #include -#else -#include -#endif #include #include #include @@ -2338,7 +2334,7 @@ std::unique_ptr jl_create_llvm_module(StringRef name, LLVMContext &conte m->setOverrideStackAlignment(16); } -#if defined(JL_DEBUG_BUILD) && JL_LLVM_VERSION >= 130000 +#if defined(JL_DEBUG_BUILD) m->setStackProtectorGuard("global"); #endif return m; @@ -2347,11 +2343,7 @@ std::unique_ptr jl_create_llvm_module(StringRef name, LLVMContext &conte static void jl_init_function(Function *F, const Triple &TT) { // set any attributes that *must* be set on all functions -#if JL_LLVM_VERSION >= 140000 AttrBuilder attr(F->getContext()); -#else - AttrBuilder attr; -#endif if (TT.isOSWindows() && TT.getArch() == Triple::x86) { // tell Win32 to assume the stack is always 16-byte aligned, // and to ensure that it is 16-byte aligned for out-going calls, @@ -2383,11 +2375,7 @@ static void jl_init_function(Function *F, const Triple &TT) #if defined(_COMPILER_MSAN_ENABLED_) attr.addAttribute(Attribute::SanitizeMemory); #endif -#if JL_LLVM_VERSION >= 140000 F->addFnAttrs(attr); -#else - F->addAttributes(AttributeList::FunctionIndex, attr); -#endif } static bool uses_specsig(jl_value_t *sig, bool needsparams, bool va, jl_value_t *rettype, bool prefer_specsig) @@ -5978,13 +5966,8 @@ static Function* gen_cfun_wrapper( // we are adding the extra nest parameter after sret arg. std::vector> newAttributes; newAttributes.reserve(attributes.getNumAttrSets() + 1); -#if JL_LLVM_VERSION >= 140000 auto it = *attributes.indexes().begin(); const auto it_end = *attributes.indexes().end(); -#else - auto it = attributes.index_begin(); - const auto it_end = attributes.index_end(); -#endif // Skip past FunctionIndex if (it == AttributeList::AttrIndex::FunctionIndex) { @@ -5999,11 +5982,7 @@ static Function* gen_cfun_wrapper( } // Add the new nest attribute -#if JL_LLVM_VERSION >= 140000 AttrBuilder attrBuilder(M->getContext()); -#else - AttrBuilder attrBuilder; -#endif attrBuilder.addAttribute(Attribute::Nest); newAttributes.emplace_back(it, AttributeSet::get(M->getContext(), attrBuilder)); @@ -6868,11 +6847,7 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value SmallVector attrs; // function declaration attributes if (props.cc == jl_returninfo_t::SRet) { assert(srt); -#if JL_LLVM_VERSION >= 140000 AttrBuilder param(ctx.builder.getContext()); -#else - AttrBuilder param; -#endif param.addStructRetAttr(srt); param.addAttribute(Attribute::NoAlias); param.addAttribute(Attribute::NoCapture); @@ -6881,11 +6856,7 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value assert(fsig.size() == 1); } if (props.cc == jl_returninfo_t::Union) { -#if JL_LLVM_VERSION >= 140000 AttrBuilder param(ctx.builder.getContext()); -#else - AttrBuilder param; -#endif param.addAttribute(Attribute::NoAlias); param.addAttribute(Attribute::NoCapture); param.addAttribute(Attribute::NoUndef); @@ -6894,11 +6865,7 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value } if (props.return_roots) { -#if JL_LLVM_VERSION >= 140000 AttrBuilder param(ctx.builder.getContext()); -#else - AttrBuilder param; -#endif param.addAttribute(Attribute::NoAlias); param.addAttribute(Attribute::NoCapture); param.addAttribute(Attribute::NoUndef); @@ -6922,11 +6889,7 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value } if (type_is_ghost(ty)) continue; -#if JL_LLVM_VERSION >= 140000 AttrBuilder param(ctx.builder.getContext()); -#else - AttrBuilder param; -#endif if (ty->isAggregateType()) { // aggregate types are passed by pointer param.addAttribute(Attribute::NoCapture); param.addAttribute(Attribute::ReadOnly); @@ -7245,16 +7208,8 @@ static jl_llvm_functions_t declarations.functionObject = needsparams ? "jl_fptr_sparam" : "jl_fptr_args"; } -#if JL_LLVM_VERSION >= 140000 AttrBuilder FnAttrs(ctx.builder.getContext(), f->getAttributes().getFnAttrs()); -#else - AttrBuilder FnAttrs(f->getAttributes().getFnAttributes()); -#endif -#if JL_LLVM_VERSION >= 140000 AttrBuilder RetAttrs(ctx.builder.getContext(), f->getAttributes().getRetAttrs()); -#else - AttrBuilder RetAttrs(f->getAttributes().getRetAttributes()); -#endif if (jlrettype == (jl_value_t*)jl_bottom_type) FnAttrs.addAttribute(Attribute::NoReturn); @@ -7547,11 +7502,7 @@ static jl_llvm_functions_t } Argument *Arg = &*AI; ++AI; -#if JL_LLVM_VERSION >= 140000 AttrBuilder param(ctx.builder.getContext(), f->getAttributes().getParamAttrs(Arg->getArgNo())); -#else - AttrBuilder param(f->getAttributes().getParamAttributes(Arg->getArgNo())); -#endif jl_cgval_t theArg; if (llvmArgType->isAggregateType()) { maybe_mark_argument_dereferenceable(param, argType); @@ -7571,11 +7522,7 @@ static jl_llvm_functions_t if (has_sret) { Argument *Arg = &*AI; ++AI; -#if JL_LLVM_VERSION >= 140000 AttrBuilder param(ctx.builder.getContext(), f->getAttributes().getParamAttrs(Arg->getArgNo())); -#else - AttrBuilder param(f->getAttributes().getParamAttributes(Arg->getArgNo())); -#endif if (returninfo.cc == jl_returninfo_t::Union) { param.addAttribute(Attribute::NonNull); // The `dereferenceable` below does not imply `nonnull` for non addrspace(0) pointers. @@ -7597,11 +7544,7 @@ static jl_llvm_functions_t if (returninfo.return_roots) { Argument *Arg = &*AI; ++AI; -#if JL_LLVM_VERSION >= 140000 AttrBuilder param(ctx.builder.getContext(), f->getAttributes().getParamAttrs(Arg->getArgNo())); -#else - AttrBuilder param(f->getAttributes().getParamAttributes(Arg->getArgNo())); -#endif param.addAttribute(Attribute::NonNull); // The `dereferenceable` below does not imply `nonnull` for non addrspace(0) pointers. size_t size = returninfo.return_roots * sizeof(jl_value_t*); diff --git a/src/disasm.cpp b/src/disasm.cpp index 8e0516d5e5431..96595d4381987 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -92,11 +92,7 @@ #include #include #include -#if JL_LLVM_VERSION >= 140000 #include -#else -#include -#endif #include #include @@ -883,16 +879,10 @@ static void jl_dump_asm_internal( TheTarget->createMCSubtargetInfo(TheTriple.str(), cpu, features)); assert(STI && "Unable to create subtarget info!"); -#if JL_LLVM_VERSION >= 130000 MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); std::unique_ptr MOFI( TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false, /*LargeCodeModel=*/ false)); Ctx.setObjectFileInfo(MOFI.get()); -#else - std::unique_ptr MOFI(new MCObjectFileInfo()); - MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &SrcMgr); - MOFI->InitMCObjectFileInfo(TheTriple, /* PIC */ false, Ctx); -#endif std::unique_ptr DisAsm(TheTarget->createMCDisassembler(*STI, Ctx)); if (!DisAsm) { diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index c16f9cfbb8121..30dbb0b939f02 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -12,9 +12,7 @@ #include #include #include -#if JL_LLVM_VERSION >= 130000 #include -#endif #include #include #include @@ -26,11 +24,7 @@ // target machine computation #include -#if JL_LLVM_VERSION >= 140000 #include -#else -#include -#endif #include #include #include @@ -44,9 +38,7 @@ using namespace llvm; #include "processor.h" #ifdef JL_USE_JITLINK -# if JL_LLVM_VERSION >= 140000 -# include -# endif +# include # include # include # if JL_LLVM_VERSION >= 150000 @@ -820,11 +812,7 @@ class JLDebuginfoPlugin : public ObjectLinkingLayer::Plugin { auto SecName = Sec.getName(); #endif // https://github.com/llvm/llvm-project/commit/118e953b18ff07d00b8f822dfbf2991e41d6d791 -#if JL_LLVM_VERSION >= 140000 Info.SectionLoadAddresses[SecName] = jitlink::SectionRange(Sec).getStart().getValue(); -#else - Info.SectionLoadAddresses[SecName] = jitlink::SectionRange(Sec).getStart(); -#endif } return Error::success(); }); @@ -866,9 +854,7 @@ class JLMemoryUsagePlugin : public ObjectLinkingLayer::Plugin { // TODO: Port our memory management optimisations to JITLink instead of using the // default InProcessMemoryManager. std::unique_ptr createJITLinkMemoryManager() { -#if JL_LLVM_VERSION < 140000 - return std::make_unique(); -#elif JL_LLVM_VERSION < 150000 +#if JL_LLVM_VERSION < 150000 return cantFail(jitlink::InProcessMemoryManager::Create()); #else return cantFail(orc::MapperJITLinkMemoryManager::CreateWithMapper()); @@ -878,17 +864,11 @@ std::unique_ptr createJITLinkMemoryManager() { # ifdef LLVM_SHLIB -# if JL_LLVM_VERSION >= 140000 -# define EHFRAME_RANGE(name) orc::ExecutorAddrRange name -# define UNPACK_EHFRAME_RANGE(name) \ +# define EHFRAME_RANGE(name) orc::ExecutorAddrRange name +# define UNPACK_EHFRAME_RANGE(name) \ name.Start.toPtr(), \ static_cast(name.size()) -# else -# define EHFRAME_RANGE(name) JITTargetAddress name##Addr, size_t name##Size -# define UNPACK_EHFRAME_RANGE(name) \ - jitTargetAddressToPointer(name##Addr), \ - name##Size -# endif + class JLEHFrameRegistrar final : public jitlink::EHFrameRegistrar { public: @@ -1022,19 +1002,6 @@ namespace { TheTriple.setObjectFormat(Triple::ELF); } //options.PrintMachineCode = true; //Print machine code produced during JIT compiling -#if JL_LLVM_VERSION < 130000 - if (TheTriple.isOSWindows() && TheTriple.getArch() == Triple::x86) { - // tell Win32 to assume the stack is always 16-byte aligned, - // and to ensure that it is 16-byte aligned for out-going calls, - // to ensure compatibility with GCC codes - // In LLVM 13 and onwards this has turned into a module option - options.StackAlignmentOverride = 16; - } -#endif -#if defined(JL_DEBUG_BUILD) && JL_LLVM_VERSION < 130000 - // LLVM defaults to tls stack guard, which causes issues with Julia's tls implementation - options.StackProtectorGuard = StackProtectorGuards::Global; -#endif #if defined(MSAN_EMUTLS_WORKAROUND) options.EmulatedTLS = true; options.ExplicitEmulatedTLS = true; @@ -1300,11 +1267,7 @@ int64_t ___asan_globals_registered; JuliaOJIT::JuliaOJIT() : TM(createTargetMachine()), DL(jl_create_datalayout(*TM)), -#if JL_LLVM_VERSION >= 130000 ES(cantFail(orc::SelfExecutorProcessControl::Create())), -#else - ES(), -#endif GlobalJD(ES.createBareJITDylib("JuliaGlobals")), JD(ES.createBareJITDylib("JuliaOJIT")), ContextPool([](){ @@ -1568,10 +1531,6 @@ StringRef JuliaOJIT::getFunctionAtAddress(uint64_t Addr, jl_code_instance_t *cod #ifdef JL_USE_JITLINK -# if JL_LLVM_VERSION < 140000 -# pragma message("JIT debugging (GDB integration) not available on LLVM < 14.0 (for JITLink)") -void JuliaOJIT::enableJITDebuggingSupport() {} -# else extern "C" orc::shared::CWrapperFunctionResult llvm_orc_registerJITLoaderGDBAllocAction(const char *Data, size_t Size); @@ -1585,7 +1544,6 @@ void JuliaOJIT::enableJITDebuggingSupport() const auto Addr = ExecutorAddr::fromPtr(&llvm_orc_registerJITLoaderGDBAllocAction); ObjectLayer.addPlugin(std::make_unique(Addr)); } -# endif #else void JuliaOJIT::enableJITDebuggingSupport() { diff --git a/src/jitlayers.h b/src/jitlayers.h index 4c6921cd42dab..c056a6b3418a3 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -50,9 +50,6 @@ // The sanitizers don't play well with our memory manager #if defined(_OS_DARWIN_) && defined(_CPU_AARCH64_) || defined(JL_FORCE_JITLINK) || JL_LLVM_VERSION >= 150000 && defined(HAS_SANITIZER) -# if JL_LLVM_VERSION < 130000 -# pragma message("On aarch64-darwin, LLVM version >= 13 is required for JITLink; fallback suffers from occasional segfaults") -# endif # define JL_USE_JITLINK #endif diff --git a/src/llvm-codegen-shared.h b/src/llvm-codegen-shared.h index 1d4414d6aaaa8..0ab140b42b8b7 100644 --- a/src/llvm-codegen-shared.h +++ b/src/llvm-codegen-shared.h @@ -316,125 +316,73 @@ using namespace llvm; inline void addFnAttr(CallInst *Target, Attribute::AttrKind Attr) { -#if JL_LLVM_VERSION >= 140000 Target->addFnAttr(Attr); -#else - Target->addAttribute(AttributeList::FunctionIndex, Attr); -#endif } template inline void addRetAttr(T *Target, A Attr) { -#if JL_LLVM_VERSION >= 140000 Target->addRetAttr(Attr); -#else - Target->addAttribute(AttributeList::ReturnIndex, Attr); -#endif } inline void addAttributeAtIndex(Function *F, unsigned Index, Attribute Attr) { -#if JL_LLVM_VERSION >= 140000 F->addAttributeAtIndex(Index, Attr); -#else - F->addAttribute(Index, Attr); -#endif } inline AttributeSet getFnAttrs(const AttributeList &Attrs) { -#if JL_LLVM_VERSION >= 140000 return Attrs.getFnAttrs(); -#else - return Attrs.getFnAttributes(); -#endif } inline AttributeSet getRetAttrs(const AttributeList &Attrs) { -#if JL_LLVM_VERSION >= 140000 return Attrs.getRetAttrs(); -#else - return Attrs.getRetAttributes(); -#endif } inline bool hasFnAttr(const AttributeList &L, Attribute::AttrKind Kind) { -#if JL_LLVM_VERSION >= 140000 return L.hasFnAttr(Kind); -#else - return L.hasAttribute(AttributeList::FunctionIndex, Kind); -#endif } inline AttributeList addAttributeAtIndex(const AttributeList &L, LLVMContext &C, unsigned Index, Attribute::AttrKind Kind) { -#if JL_LLVM_VERSION >= 140000 return L.addAttributeAtIndex(C, Index, Kind); -#else - return L.addAttribute(C, Index, Kind); -#endif } inline AttributeList addAttributeAtIndex(const AttributeList &L, LLVMContext &C, unsigned Index, Attribute Attr) { -#if JL_LLVM_VERSION >= 140000 return L.addAttributeAtIndex(C, Index, Attr); -#else - return L.addAttribute(C, Index, Attr); -#endif } inline AttributeList addAttributesAtIndex(const AttributeList &L, LLVMContext &C, unsigned Index, const AttrBuilder &Builder) { -#if JL_LLVM_VERSION >= 140000 return L.addAttributesAtIndex(C, Index, Builder); -#else - return L.addAttributes(C, Index, Builder); -#endif } inline AttributeList addFnAttribute(const AttributeList &L, LLVMContext &C, Attribute::AttrKind Kind) { -#if JL_LLVM_VERSION >= 140000 return L.addFnAttribute(C, Kind); -#else - return L.addAttribute(C, AttributeList::FunctionIndex, Kind); -#endif } inline AttributeList addRetAttribute(const AttributeList &L, LLVMContext &C, Attribute::AttrKind Kind) { -#if JL_LLVM_VERSION >= 140000 return L.addRetAttribute(C, Kind); -#else - return L.addAttribute(C, AttributeList::ReturnIndex, Kind); -#endif } inline bool hasAttributesAtIndex(const AttributeList &L, unsigned Index) { -#if JL_LLVM_VERSION >= 140000 return L.hasAttributesAtIndex(Index); -#else - return L.hasAttributes(Index); -#endif } inline Attribute getAttributeAtIndex(const AttributeList &L, unsigned Index, Attribute::AttrKind Kind) { -#if JL_LLVM_VERSION >= 140000 return L.getAttributeAtIndex(Index, Kind); -#else - return L.getAttribute(Index, Kind); -#endif } // Iterate through uses of a particular type. diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index da381c17be5e1..814b13554358c 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -546,12 +546,8 @@ static void clone_function(Function *F, Function *new_f, ValueToValueMapTy &vmap vmap[&*J] = &*DestI++; } SmallVector Returns; -#if JL_LLVM_VERSION >= 130000 // We are cloning into the same module CloneFunctionInto(new_f, F, vmap, CloneFunctionChangeType::GlobalChanges, Returns); -#else - CloneFunctionInto(new_f, F, vmap, true, Returns); -#endif } static void add_features(Function *F, TargetSpec &spec) diff --git a/src/llvm-remove-addrspaces.cpp b/src/llvm-remove-addrspaces.cpp index 650c457ad4a7c..b964c20e3353e 100644 --- a/src/llvm-remove-addrspaces.cpp +++ b/src/llvm-remove-addrspaces.cpp @@ -343,11 +343,7 @@ bool removeAddrspaces(Module &M, AddrspaceRemapFunction ASRemapper) for (auto MD : MDs) NGV->addMetadata( MD.first, -#if JL_LLVM_VERSION >= 130000 *MapMetadata(MD.second, VMap)); -#else - *MapMetadata(MD.second, VMap, RF_MoveDistinctMDs)); -#endif copyComdat(NGV, GV); @@ -372,11 +368,7 @@ bool removeAddrspaces(Module &M, AddrspaceRemapFunction ASRemapper) NF, F, VMap, -#if JL_LLVM_VERSION >= 130000 CloneFunctionChangeType::GlobalChanges, -#else - /*ModuleLevelChanges=*/true, -#endif Returns, "", nullptr, @@ -389,19 +381,10 @@ bool removeAddrspaces(Module &M, AddrspaceRemapFunction ASRemapper) for (unsigned i = 0; i < Attrs.getNumAttrSets(); ++i) { for (Attribute::AttrKind TypedAttr : {Attribute::ByVal, Attribute::StructRet, Attribute::ByRef}) { -#if JL_LLVM_VERSION >= 140000 auto Attr = Attrs.getAttributeAtIndex(i, TypedAttr); -#else - auto Attr = Attrs.getAttribute(i, TypedAttr); -#endif if (Type *Ty = Attr.getValueAsType()) { -#if JL_LLVM_VERSION >= 140000 Attrs = Attrs.replaceAttributeTypeAtIndex( C, i, TypedAttr, TypeRemapper.remapType(Ty)); -#else - Attrs = Attrs.replaceAttributeType( - C, i, TypedAttr, TypeRemapper.remapType(Ty)); -#endif break; } } diff --git a/src/llvm-version.h b/src/llvm-version.h index 819ec1c88976b..01638b8d44a6e 100644 --- a/src/llvm-version.h +++ b/src/llvm-version.h @@ -10,8 +10,8 @@ #define JL_LLVM_VERSION (LLVM_VERSION_MAJOR * 10000 + LLVM_VERSION_MINOR * 100 \ + LLVM_VERSION_PATCH) -#if JL_LLVM_VERSION < 120000 - #error Only LLVM versions >= 12.0.0 are supported by Julia +#if JL_LLVM_VERSION < 140000 + #error Only LLVM versions >= 14.0.0 are supported by Julia #endif #if JL_LLVM_VERSION >= 160000 From 0b5ec1f57eb38102235568de0a5f6a8a3ae92ace Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 10 May 2023 23:35:44 -0400 Subject: [PATCH 731/775] irinterp: Fix accidentally introduced deletion of effectful statement (#49750) I moved around some code in #49692 that broadened the replacement of statements by their const results. This is fine for how we're currently using irinterp in base, because we're requiring some fairly strong effects, but some downstream pipelines (and potentially Base in the future) want to use irinterp on code with arbitrary effects, so put in an appropriate check. --- base/compiler/abstractinterpretation.jl | 1 + base/compiler/ssair/irinterp.jl | 6 +----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index a62f76017c2dc..0f2011fd07c3c 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2544,6 +2544,7 @@ end function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState) if !isa(e, Expr) if isa(e, PhiNode) + add_curr_ssaflag!(sv, IR_FLAG_EFFECT_FREE) return abstract_eval_phi(interp, e, vtypes, sv) end return abstract_eval_special_value(interp, e, vtypes, sv) diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index ad6077fa48859..39bf9ef9fe385 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -128,16 +128,12 @@ function reprocess_instruction!(interp::AbstractInterpreter, idx::Int, bb::Union end return propagate_control_effects!(interp, idx, inst, irsv, extra_reprocess) end - rt = nothing if isa(inst, Expr) head = inst.head if head === :call || head === :foreigncall || head === :new || head === :splatnew (; rt, effects) = abstract_eval_statement_expr(interp, inst, nothing, irsv) ir.stmts[idx][:flag] |= flags_for_effects(effects) - if is_foldable(effects) && isa(rt, Const) && is_inlineable_constant(rt.val) - ir.stmts[idx][:inst] = quoted(rt.val) - end elseif head === :invoke rt, nothrow = concrete_eval_invoke(interp, inst, inst.args[1]::MethodInstance, irsv) if nothrow @@ -167,7 +163,7 @@ function reprocess_instruction!(interp::AbstractInterpreter, idx::Int, bb::Union if rt !== nothing if isa(rt, Const) ir.stmts[idx][:type] = rt - if is_inlineable_constant(rt.val) + if is_inlineable_constant(rt.val) && (ir.stmts[idx][:flag] & IR_FLAG_EFFECT_FREE) != 0 ir.stmts[idx][:inst] = quoted(rt.val) end return true From 6618d4416d23c71c676cd697a99df6ef63d0e2ae Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 11 May 2023 17:40:56 +0900 Subject: [PATCH 732/775] minor follow up on #49692 (#49752) --- base/compiler/ssair/irinterp.jl | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index 39bf9ef9fe385..4aed080e57bb4 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -247,15 +247,11 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR any_refined = true delete!(ssa_refined, idx) end - did_reprocess = false - if any_refined - did_reprocess = reprocess_instruction!(interp, + if any_refined && reprocess_instruction!(interp, idx, bb, inst, typ, irsv, extra_reprocess) - if did_reprocess - push!(ssa_refined, idx) - inst = ir.stmts[idx][:inst] - typ = ir.stmts[idx][:type] - end + push!(ssa_refined, idx) + inst = ir.stmts[idx][:inst] + typ = ir.stmts[idx][:type] end if idx == lstmt process_terminator!(ir, inst, idx, bb, all_rets, bb_ip) && @goto residual_scan From e3e5eaa035cb94d3816a1167136377c15a5565a7 Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Thu, 11 May 2023 12:26:38 -0400 Subject: [PATCH 733/775] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20?= =?UTF-8?q?Pkg=20stdlib=20from=2094f668cee=20to=20daf02a458=20(#49764)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dilum Aluthge --- .../Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/md5 | 1 - .../Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/sha512 | 1 - .../Pkg-daf02a458ae6daa402a5dd6683c40d6910325c4e.tar.gz/md5 | 1 + .../Pkg-daf02a458ae6daa402a5dd6683c40d6910325c4e.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-daf02a458ae6daa402a5dd6683c40d6910325c4e.tar.gz/md5 create mode 100644 deps/checksums/Pkg-daf02a458ae6daa402a5dd6683c40d6910325c4e.tar.gz/sha512 diff --git a/deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/md5 b/deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/md5 deleted file mode 100644 index 88f078bc9ff07..0000000000000 --- a/deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -3a917f288ca5688dad17f49e9743a131 diff --git a/deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/sha512 b/deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/sha512 deleted file mode 100644 index f39662c8596be..0000000000000 --- a/deps/checksums/Pkg-94f668ceef108ab82f5532f2a86749e4b932e010.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -a7e69c67f63720cd27ce4f3824a6602d562b4a1a4c0f1238a47932635896ba8dd67bbc24fd2e748ef722df3172cc3c95fe41cb9dbca049cb811c1d18eadd89ff diff --git a/deps/checksums/Pkg-daf02a458ae6daa402a5dd6683c40d6910325c4e.tar.gz/md5 b/deps/checksums/Pkg-daf02a458ae6daa402a5dd6683c40d6910325c4e.tar.gz/md5 new file mode 100644 index 0000000000000..08f5ccda57979 --- /dev/null +++ b/deps/checksums/Pkg-daf02a458ae6daa402a5dd6683c40d6910325c4e.tar.gz/md5 @@ -0,0 +1 @@ +c135dc6ed97656fe956d9ee5cf3cbc55 diff --git a/deps/checksums/Pkg-daf02a458ae6daa402a5dd6683c40d6910325c4e.tar.gz/sha512 b/deps/checksums/Pkg-daf02a458ae6daa402a5dd6683c40d6910325c4e.tar.gz/sha512 new file mode 100644 index 0000000000000..957075f0f281a --- /dev/null +++ b/deps/checksums/Pkg-daf02a458ae6daa402a5dd6683c40d6910325c4e.tar.gz/sha512 @@ -0,0 +1 @@ +2ae67fd4c5e1bf83df5df836fcd69afc0fb8454723043d32de9c7bc29feedf390adb76efda52e79937ea801ff21b5f4ea875469136424e2889904130b247b52a diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 99058cf8bf317..7b5006f2141ff 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 94f668ceef108ab82f5532f2a86749e4b932e010 +PKG_SHA1 = daf02a458ae6daa402a5dd6683c40d6910325c4e PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From e4633e0c5b024e1f3561aef073519162b439a80f Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Thu, 11 May 2023 11:04:35 -0600 Subject: [PATCH 734/775] add note about references in `Out` (#49729) --- stdlib/REPL/docs/src/index.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stdlib/REPL/docs/src/index.md b/stdlib/REPL/docs/src/index.md index 3ce0d7fa848b1..ce594d55863bc 100644 --- a/stdlib/REPL/docs/src/index.md +++ b/stdlib/REPL/docs/src/index.md @@ -646,6 +646,13 @@ Out[3]: Dict{Int64, Any} with 2 entries: 1 => 8 ``` +!!! note + Since all outputs from previous REPL evaluations are saved in the `Out` variable, one should be careful if they are returning many + large in-memory objects like arrays, since they will be protected from garbage collection so long as a reference to them remains in + `Out`. If you need to remove references to objects in `Out`, you can clear the entire history it stores with `empty!(Out)`, or clear + an individual entry with `Out[n] = nothing`. + + ## TerminalMenus TerminalMenus is a submodule of the Julia REPL and enables small, low-profile interactive menus in the terminal. From c714e2ec2567fde62d108f631134a9b7a6a7c813 Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Thu, 11 May 2023 17:09:54 +0000 Subject: [PATCH 735/775] Add JITLink ELF debugger support (#47037) --- src/jitlayers.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 30dbb0b939f02..759828cef3321 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -1536,13 +1538,15 @@ llvm_orc_registerJITLoaderGDBAllocAction(const char *Data, size_t Size); void JuliaOJIT::enableJITDebuggingSupport() { - // We do not use GDBJITDebugInfoRegistrationPlugin::Create, as the runtime name - // lookup is unnecessarily involved/fragile for our in-process JIT use case - // (with the llvm_orc_registerJITLoaderGDBAllocAction symbol being in either - // libjulia-codegen or yet another shared library for LLVM depending on the build - // flags, etc.). - const auto Addr = ExecutorAddr::fromPtr(&llvm_orc_registerJITLoaderGDBAllocAction); - ObjectLayer.addPlugin(std::make_unique(Addr)); + orc::SymbolMap GDBFunctions; + GDBFunctions[mangle("llvm_orc_registerJITLoaderGDBAllocAction")] = JITEvaluatedSymbol::fromPointer(&llvm_orc_registerJITLoaderGDBAllocAction, JITSymbolFlags::Exported | JITSymbolFlags::Callable); + GDBFunctions[mangle("llvm_orc_registerJITLoaderGDBWrapper")] = JITEvaluatedSymbol::fromPointer(&llvm_orc_registerJITLoaderGDBWrapper, JITSymbolFlags::Exported | JITSymbolFlags::Callable); + cantFail(JD.define(orc::absoluteSymbols(GDBFunctions))); + if (TM->getTargetTriple().isOSBinFormatMachO()) + ObjectLayer.addPlugin(cantFail(orc::GDBJITDebugInfoRegistrationPlugin::Create(ES, JD, TM->getTargetTriple()))); + else if (TM->getTargetTriple().isOSBinFormatELF()) + //EPCDebugObjectRegistrar doesn't take a JITDylib, so we have to directly provide the call address + ObjectLayer.addPlugin(std::make_unique(ES, std::make_unique(ES, orc::ExecutorAddr::fromPtr(&llvm_orc_registerJITLoaderGDBWrapper)))); } #else void JuliaOJIT::enableJITDebuggingSupport() From 528949f0c46354fe4b4355c86e4e5d95c95bdfa8 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 11 May 2023 13:29:07 -0400 Subject: [PATCH 736/775] macOS: avoid deadlock inside dyld4 deadlock workaround (#49740) Extend the fix for #43578 (2939272af2ef3fe9d8921f7ed0a6500e31a550c9) to cover the deadlock bug present internally in dyld4 inside the function we use to avoid the previous deadlock issue. Fix #49733 --- src/signals-mach.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/signals-mach.c b/src/signals-mach.c index 13f5f6923cec3..073ab2ebc33a6 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -36,6 +36,9 @@ extern int _keymgr_set_lockmode_processwide_ptr(unsigned int key, unsigned int m extern void _dyld_atfork_prepare(void) __attribute__((weak_import)); extern void _dyld_atfork_parent(void) __attribute__((weak_import)); //extern void _dyld_fork_child(void) __attribute__((weak_import)); +extern void _dyld_dlopen_atfork_prepare(void) __attribute__((weak_import)); +extern void _dyld_dlopen_atfork_parent(void) __attribute__((weak_import)); +//extern void _dyld_dlopen_atfork_child(void) __attribute__((weak_import)); static void attach_exception_port(thread_port_t thread, int segv_only); @@ -564,7 +567,12 @@ static int jl_lock_profile_mach(int dlsymlock) // workaround for old keymgr bugs void *unused = NULL; int keymgr_locked = _keymgr_get_and_lock_processwide_ptr_2(KEYMGR_GCC3_DW2_OBJ_LIST, &unused) == 0; - // workaround for new dlsym4 bugs (API and bugs introduced in macOS 12.1) + // workaround for new dlsym4 bugs in the workaround for dlsym bugs: _dyld_atfork_prepare + // acquires its locks in the wrong order, but fortunately we happen to able to guard it + // with this call to force it to prevent that TSAN violation from causing a deadlock + if (dlsymlock && _dyld_dlopen_atfork_prepare != NULL && _dyld_dlopen_atfork_parent != NULL) + _dyld_dlopen_atfork_prepare(); + // workaround for new dlsym4 bugs (API and bugs introduced circa macOS 12.1) if (dlsymlock && _dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) _dyld_atfork_prepare(); return keymgr_locked; @@ -572,8 +580,10 @@ static int jl_lock_profile_mach(int dlsymlock) static void jl_unlock_profile_mach(int dlsymlock, int keymgr_locked) { - if (dlsymlock && _dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) \ - _dyld_atfork_parent(); \ + if (dlsymlock && _dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) + _dyld_atfork_parent(); + if (dlsymlock && _dyld_dlopen_atfork_prepare != NULL && _dyld_dlopen_atfork_parent != NULL) + _dyld_dlopen_atfork_parent(); if (keymgr_locked) _keymgr_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST); jl_unlock_profile(); @@ -611,6 +621,8 @@ void *mach_profile_listener(void *arg) break; } + if (_dyld_dlopen_atfork_prepare != NULL && _dyld_dlopen_atfork_parent != NULL) + _dyld_dlopen_atfork_prepare(); if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) _dyld_atfork_prepare(); // briefly acquire the dlsym lock host_thread_state_t state; @@ -618,6 +630,8 @@ void *mach_profile_listener(void *arg) unw_context_t *uc = (unw_context_t*)&state; if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) _dyld_atfork_parent(); // quickly release the dlsym lock + if (_dyld_dlopen_atfork_prepare != NULL && _dyld_dlopen_atfork_parent != NULL) + _dyld_dlopen_atfork_parent(); if (!valid_thread) continue; if (running) { From 65c3b4130144fbc51e8d65ed2d7430e8d7899ac6 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Thu, 11 May 2023 13:22:19 -0400 Subject: [PATCH 737/775] Initialize `last_alloc` The uninit usage analyzer appears to be thrown off by the `__attribute__((cleanup(*)))` used by the `JL_TIMING` macro, so work around it by explicitly initializing `last_alloc`. --- src/gf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gf.c b/src/gf.c index ff48b5224a455..6d55e479babfe 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2991,7 +2991,7 @@ STATIC_INLINE jl_method_instance_t *jl_lookup_generic_(jl_value_t *F, jl_value_t #undef LOOP_BODY i = 4; jl_tupletype_t *tt = NULL; - int64_t last_alloc; + int64_t last_alloc = 0; if (i == 4) { // if no method was found in the associative cache, check the full cache JL_TIMING(METHOD_LOOKUP_FAST, METHOD_LOOKUP_FAST); From e642cb925c4cfa49467af196496c914b7d652971 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Thu, 11 May 2023 13:46:29 -0400 Subject: [PATCH 738/775] Enable `TRACY_TIMER_FALLBACK` for libTracyClient This fallback is most likely to kick in on VM's that may not have support for the rdtsc instruction. The loss in timer fidelity can be pretty severe (8 ns -> 15.6 ms) but we pack lots of other metadata into our traces, so it can still be useful to run with the fallback (and forcefully crashing the application as Tracy does now is just not the Julian way to communicate a hard error anyway) --- deps/libtracyclient.mk | 1 + deps/libtracyclient.version | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/deps/libtracyclient.mk b/deps/libtracyclient.mk index e4116b8f2acb4..92d6bee4caea6 100644 --- a/deps/libtracyclient.mk +++ b/deps/libtracyclient.mk @@ -15,6 +15,7 @@ LIBTRACYCLIENT_CMAKE += -DTRACY_NO_CODE_TRANSFER=ON LIBTRACYCLIENT_CMAKE += -DTRACY_NO_FRAME_IMAGE=ON LIBTRACYCLIENT_CMAKE += -DTRACY_NO_CRASH_HANDLER=ON LIBTRACYCLIENT_CMAKE += -DTRACY_ON_DEMAND=ON +LIBTRACYCLIENT_CMAKE += -DTRACY_TIMER_FALLBACK=ON ifeq ($(WITH_TRACY_CALLSTACKS),1) LIBTRACYCLIENT_CMAKE += -DTRACY_CALLSTACK=32 diff --git a/deps/libtracyclient.version b/deps/libtracyclient.version index 2faa4327b7749..0baf8504261f1 100644 --- a/deps/libtracyclient.version +++ b/deps/libtracyclient.version @@ -1,6 +1,6 @@ ## jll artifact LIBTRACYCLIENT_JLL_NAME := LibTracyClient -LIBTRACYCLIENT_JLL_VER := 0.9.1+1 +LIBTRACYCLIENT_JLL_VER := 0.9.1+2 ## source build LIBTRACYCLIENT_VER := 0.9.1 From a09f426621c46b0b8dfe82ce0089429e3d7913eb Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Thu, 11 May 2023 13:48:00 -0400 Subject: [PATCH 739/775] Fix visibility of `timing.h` exports --- src/timing.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/timing.h b/src/timing.h index 8ea2791461fd5..2e5147608fb22 100644 --- a/src/timing.h +++ b/src/timing.h @@ -109,14 +109,14 @@ jl_timing_block_t *jl_timing_block_exit_task(jl_task_t *ct, jl_ptls_t ptls); // profiling region corresponding to `cur_block`. // // If larger than IOS_INLSIZE (~80 characters), text is truncated. -void jl_timing_show(jl_value_t *v, jl_timing_block_t *cur_block); -void jl_timing_show_module(jl_module_t *m, jl_timing_block_t *cur_block); -void jl_timing_show_filename(const char *path, jl_timing_block_t *cur_block); -void jl_timing_show_method_instance(jl_method_instance_t *mi, jl_timing_block_t *cur_block); -void jl_timing_show_method(jl_method_t *method, jl_timing_block_t *cur_block); -void jl_timing_show_func_sig(jl_value_t *v, jl_timing_block_t *cur_block); -void jl_timing_printf(jl_timing_block_t *cur_block, const char *format, ...); -void jl_timing_puts(jl_timing_block_t *cur_block, const char *str); +JL_DLLEXPORT void jl_timing_show(jl_value_t *v, jl_timing_block_t *cur_block); +JL_DLLEXPORT void jl_timing_show_module(jl_module_t *m, jl_timing_block_t *cur_block); +JL_DLLEXPORT void jl_timing_show_filename(const char *path, jl_timing_block_t *cur_block); +JL_DLLEXPORT void jl_timing_show_method_instance(jl_method_instance_t *mi, jl_timing_block_t *cur_block); +JL_DLLEXPORT void jl_timing_show_method(jl_method_t *method, jl_timing_block_t *cur_block); +JL_DLLEXPORT void jl_timing_show_func_sig(jl_value_t *v, jl_timing_block_t *cur_block); +JL_DLLEXPORT void jl_timing_printf(jl_timing_block_t *cur_block, const char *format, ...); +JL_DLLEXPORT void jl_timing_puts(jl_timing_block_t *cur_block, const char *str); #ifdef __cplusplus } #endif @@ -249,7 +249,7 @@ enum jl_timing_events { * Implementation: Aggregated counts back-end **/ -extern uint64_t jl_timing_counts[(int)JL_TIMING_LAST]; +extern JL_DLLEXPORT uint64_t jl_timing_counts[(int)JL_TIMING_LAST]; typedef struct _jl_timing_counts_t { uint64_t total; uint64_t t0; @@ -291,10 +291,10 @@ STATIC_INLINE void _jl_timing_counts_destroy(jl_timing_counts_t *block) JL_NOTSA * Top-level jl_timing implementation **/ -extern uint64_t jl_timing_enable_mask; +extern JL_DLLEXPORT uint64_t jl_timing_enable_mask; extern const char *jl_timing_names[(int)JL_TIMING_LAST]; #ifdef USE_ITTAPI -extern __itt_event jl_timing_ittapi_events[(int)JL_TIMING_EVENT_LAST]; +extern JL_DLLEXPORT __itt_event jl_timing_ittapi_events[(int)JL_TIMING_EVENT_LAST]; #endif struct _jl_timing_block_t { // typedef in julia.h From b21f1002271a2068f23283f5652ae95bcb8c38b4 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Thu, 11 May 2023 21:06:25 +0200 Subject: [PATCH 740/775] Make `*Triangular` handle units (#43972) --- stdlib/LinearAlgebra/src/bidiag.jl | 102 +-- stdlib/LinearAlgebra/src/dense.jl | 12 +- stdlib/LinearAlgebra/src/diagonal.jl | 25 +- stdlib/LinearAlgebra/src/hessenberg.jl | 24 +- stdlib/LinearAlgebra/src/matmul.jl | 5 +- stdlib/LinearAlgebra/src/special.jl | 8 + stdlib/LinearAlgebra/src/triangular.jl | 929 +++++++++++++----------- stdlib/LinearAlgebra/test/bidiag.jl | 27 +- stdlib/LinearAlgebra/test/hessenberg.jl | 21 +- stdlib/LinearAlgebra/test/testgroups | 4 +- stdlib/LinearAlgebra/test/triangular.jl | 53 +- test/testhelpers/Furlongs.jl | 13 +- 12 files changed, 638 insertions(+), 585 deletions(-) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 4609ff9fbd12f..dd3783d67b0cf 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -409,6 +409,7 @@ const BandedMatrix = Union{Bidiagonal,Diagonal,Tridiagonal,SymTridiagonal} # or const BiTriSym = Union{Bidiagonal,Tridiagonal,SymTridiagonal} const BiTri = Union{Bidiagonal,Tridiagonal} @inline mul!(C::AbstractVector, A::BandedMatrix, B::AbstractVector, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) +@inline mul!(C::AbstractMatrix, A::BandedMatrix, B::AbstractVector, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) @inline mul!(C::AbstractMatrix, A::BandedMatrix, B::AbstractMatrix, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) @inline mul!(C::AbstractMatrix, A::AbstractMatrix, B::BandedMatrix, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) @inline mul!(C::AbstractMatrix, A::BandedMatrix, B::BandedMatrix, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) @@ -747,39 +748,27 @@ ldiv!(c::AbstractVecOrMat, A::AdjOrTrans{<:Any,<:Bidiagonal}, b::AbstractVecOrMa \(xA::AdjOrTrans{<:Any,<:Bidiagonal}, B::AbstractVecOrMat) = copy(xA) \ B ### Triangular specializations -function \(B::Bidiagonal, U::UpperTriangular) - A = ldiv!(_initarray(\, eltype(B), eltype(U), U), B, U) - return B.uplo == 'U' ? UpperTriangular(A) : A -end -function \(B::Bidiagonal, U::UnitUpperTriangular) - A = ldiv!(_initarray(\, eltype(B), eltype(U), U), B, U) - return B.uplo == 'U' ? UpperTriangular(A) : A -end -function \(B::Bidiagonal, L::LowerTriangular) - A = ldiv!(_initarray(\, eltype(B), eltype(L), L), B, L) - return B.uplo == 'L' ? LowerTriangular(A) : A +for tri in (:UpperTriangular, :UnitUpperTriangular) + @eval function \(B::Bidiagonal, U::$tri) + A = ldiv!(_initarray(\, eltype(B), eltype(U), U), B, U) + return B.uplo == 'U' ? UpperTriangular(A) : A + end + @eval function \(U::$tri, B::Bidiagonal) + A = ldiv!(_initarray(\, eltype(U), eltype(B), U), U, B) + return B.uplo == 'U' ? UpperTriangular(A) : A + end end -function \(B::Bidiagonal, L::UnitLowerTriangular) - A = ldiv!(_initarray(\, eltype(B), eltype(L), L), B, L) - return B.uplo == 'L' ? LowerTriangular(A) : A +for tri in (:LowerTriangular, :UnitLowerTriangular) + @eval function \(B::Bidiagonal, L::$tri) + A = ldiv!(_initarray(\, eltype(B), eltype(L), L), B, L) + return B.uplo == 'L' ? LowerTriangular(A) : A + end + @eval function \(L::$tri, B::Bidiagonal) + A = ldiv!(_initarray(\, eltype(L), eltype(B), L), L, B) + return B.uplo == 'L' ? LowerTriangular(A) : A + end end -function \(U::UpperTriangular, B::Bidiagonal) - A = ldiv!(U, copy_similar(B, _init_eltype(\, eltype(U), eltype(B)))) - return B.uplo == 'U' ? UpperTriangular(A) : A -end -function \(U::UnitUpperTriangular, B::Bidiagonal) - A = ldiv!(U, copy_similar(B, _init_eltype(\, eltype(U), eltype(B)))) - return B.uplo == 'U' ? UpperTriangular(A) : A -end -function \(L::LowerTriangular, B::Bidiagonal) - A = ldiv!(L, copy_similar(B, _init_eltype(\, eltype(L), eltype(B)))) - return B.uplo == 'L' ? LowerTriangular(A) : A -end -function \(L::UnitLowerTriangular, B::Bidiagonal) - A = ldiv!(L, copy_similar(B, _init_eltype(\, eltype(L), eltype(B)))) - return B.uplo == 'L' ? LowerTriangular(A) : A -end ### Diagonal specialization function \(B::Bidiagonal, D::Diagonal) A = ldiv!(_initarray(\, eltype(B), eltype(D), D), B, D) @@ -835,38 +824,27 @@ _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::AdjOrTrans{<:Any,<:Bidiagonal}) /(A::AbstractMatrix, B::Bidiagonal) = _rdiv!(_initarray(/, eltype(A), eltype(B), A), A, B) ### Triangular specializations -function /(U::UpperTriangular, B::Bidiagonal) - A = _rdiv!(_initarray(/, eltype(U), eltype(B), U), U, B) - return B.uplo == 'U' ? UpperTriangular(A) : A -end -function /(U::UnitUpperTriangular, B::Bidiagonal) - A = _rdiv!(_initarray(/, eltype(U), eltype(B), U), U, B) - return B.uplo == 'U' ? UpperTriangular(A) : A -end -function /(L::LowerTriangular, B::Bidiagonal) - A = _rdiv!(_initarray(/, eltype(L), eltype(B), L), L, B) - return B.uplo == 'L' ? LowerTriangular(A) : A -end -function /(L::UnitLowerTriangular, B::Bidiagonal) - A = _rdiv!(_initarray(/, eltype(L), eltype(B), L), L, B) - return B.uplo == 'L' ? LowerTriangular(A) : A -end -function /(B::Bidiagonal, U::UpperTriangular) - A = rdiv!(copy_similar(B, _init_eltype(/, eltype(B), eltype(U))), U) - return B.uplo == 'U' ? UpperTriangular(A) : A -end -function /(B::Bidiagonal, U::UnitUpperTriangular) - A = rdiv!(copy_similar(B, _init_eltype(/, eltype(B), eltype(U))), U) - return B.uplo == 'U' ? UpperTriangular(A) : A -end -function /(B::Bidiagonal, L::LowerTriangular) - A = rdiv!(copy_similar(B, _init_eltype(/, eltype(B), eltype(L))), L) - return B.uplo == 'L' ? LowerTriangular(A) : A +for tri in (:UpperTriangular, :UnitUpperTriangular) + @eval function /(U::$tri, B::Bidiagonal) + A = _rdiv!(_initarray(/, eltype(U), eltype(B), U), U, B) + return B.uplo == 'U' ? UpperTriangular(A) : A + end + @eval function /(B::Bidiagonal, U::$tri) + A = _rdiv!(_initarray(/, eltype(B), eltype(U), U), B, U) + return B.uplo == 'U' ? UpperTriangular(A) : A + end end -function /(B::Bidiagonal, L::UnitLowerTriangular) - A = rdiv!(copy_similar(B, _init_eltype(/, eltype(B), eltype(L))), L) - return B.uplo == 'L' ? LowerTriangular(A) : A +for tri in (:LowerTriangular, :UnitLowerTriangular) + @eval function /(L::$tri, B::Bidiagonal) + A = _rdiv!(_initarray(/, eltype(L), eltype(B), L), L, B) + return B.uplo == 'L' ? LowerTriangular(A) : A + end + @eval function /(B::Bidiagonal, L::$tri) + A = _rdiv!(_initarray(/, eltype(B), eltype(L), L), B, L) + return B.uplo == 'L' ? LowerTriangular(A) : A + end end + ### Diagonal specialization function /(D::Diagonal, B::Bidiagonal) A = _rdiv!(_initarray(/, eltype(D), eltype(B), D), D, B) @@ -886,8 +864,8 @@ end factorize(A::Bidiagonal) = A function inv(B::Bidiagonal{T}) where T n = size(B, 1) - dest = zeros(typeof(oneunit(T)\one(T)), (n, n)) - ldiv!(dest, B, Diagonal{typeof(one(T)\one(T))}(I, n)) + dest = zeros(typeof(inv(oneunit(T))), (n, n)) + ldiv!(dest, B, Diagonal{typeof(one(T)/one(T))}(I, n)) return B.uplo == 'U' ? UpperTriangular(dest) : LowerTriangular(dest) end diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index 7f5e44382f5c5..56c5954cc28fe 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -907,14 +907,12 @@ sqrt(A::TransposeAbsMat) = transpose(sqrt(parent(A))) function inv(A::StridedMatrix{T}) where T checksquare(A) - S = typeof((oneunit(T)*zero(T) + oneunit(T)*zero(T))/oneunit(T)) - AA = convert(AbstractArray{S}, A) - if istriu(AA) - Ai = triu!(parent(inv(UpperTriangular(AA)))) - elseif istril(AA) - Ai = tril!(parent(inv(LowerTriangular(AA)))) + if istriu(A) + Ai = triu!(parent(inv(UpperTriangular(A)))) + elseif istril(A) + Ai = tril!(parent(inv(LowerTriangular(A)))) else - Ai = inv!(lu(AA)) + Ai = inv!(lu(A)) Ai = convert(typeof(parent(Ai)), Ai) end return Ai diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index a58c8e95829ed..b9fa98a9b12b3 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -375,12 +375,23 @@ function __muldiag!(out, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) return out end -function _mul!(out, A, B, _add) +function _mul_diag!(out, A, B, _add) _muldiag_size_check(out, A, B) __muldiag!(out, A, B, _add) return out end +_mul!(out::AbstractVecOrMat, D::Diagonal, V::AbstractVector, _add) = + _mul_diag!(out, D, V, _add) +_mul!(out::AbstractMatrix, D::Diagonal, B::AbstractMatrix, _add) = + _mul_diag!(out, D, B, _add) +_mul!(out::AbstractMatrix, A::AbstractMatrix, D::Diagonal, _add) = + _mul_diag!(out, A, D, _add) +_mul!(C::Diagonal, Da::Diagonal, Db::Diagonal, _add) = + _mul_diag!(C, Da, Db, _add) +_mul!(C::AbstractMatrix, Da::Diagonal, Db::Diagonal, _add) = + _mul_diag!(C, Da, Db, _add) + function (*)(Da::Diagonal, A::AbstractMatrix, Db::Diagonal) _muldiag_size_check(Da, A) _muldiag_size_check(A, Db) @@ -395,6 +406,7 @@ end /(A::AbstractVecOrMat, D::Diagonal) = _rdiv!(similar(A, _init_eltype(/, eltype(A), eltype(D))), A, D) /(A::HermOrSym, D::Diagonal) = _rdiv!(similar(A, _init_eltype(/, eltype(A), eltype(D)), size(A)), A, D) + rdiv!(A::AbstractVecOrMat, D::Diagonal) = @inline _rdiv!(A, A, D) # avoid copy when possible via internal 3-arg backend function _rdiv!(B::AbstractVecOrMat, A::AbstractVecOrMat, D::Diagonal) @@ -557,22 +569,21 @@ for Tri in (:UpperTriangular, :LowerTriangular) # 3-arg ldiv! @eval ldiv!(C::$Tri, D::Diagonal, A::$Tri) = $Tri(ldiv!(C.data, D, A.data)) @eval ldiv!(C::$Tri, D::Diagonal, A::$UTri) = $Tri(_setdiag!(ldiv!(C.data, D, A.data), inv, D.diag)) - # 3-arg mul!: invoke 5-arg mul! rather than lmul! - @eval mul!(C::$Tri, A::Union{$Tri,$UTri}, D::Diagonal) = mul!(C, A, D, true, false) + # 3-arg mul! is disambiguated in special.jl # 5-arg mul! @eval _mul!(C::$Tri, D::Diagonal, A::$Tri, _add) = $Tri(mul!(C.data, D, A.data, _add.alpha, _add.beta)) - @eval function _mul!(C::$Tri, D::Diagonal, A::$UTri, _add) + @eval function _mul!(C::$Tri, D::Diagonal, A::$UTri, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} α, β = _add.alpha, _add.beta iszero(α) && return _rmul_or_fill!(C, β) - diag′ = iszero(β) ? nothing : diag(C) + diag′ = bis0 ? nothing : diag(C) data = mul!(C.data, D, A.data, α, β) $Tri(_setdiag!(data, _add, D.diag, diag′)) end @eval _mul!(C::$Tri, A::$Tri, D::Diagonal, _add) = $Tri(mul!(C.data, A.data, D, _add.alpha, _add.beta)) - @eval function _mul!(C::$Tri, A::$UTri, D::Diagonal, _add) + @eval function _mul!(C::$Tri, A::$UTri, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} α, β = _add.alpha, _add.beta iszero(α) && return _rmul_or_fill!(C, β) - diag′ = iszero(β) ? nothing : diag(C) + diag′ = bis0 ? nothing : diag(C) data = mul!(C.data, A.data, D, α, β) $Tri(_setdiag!(data, _add, D.diag, diag′)) end diff --git a/stdlib/LinearAlgebra/src/hessenberg.jl b/stdlib/LinearAlgebra/src/hessenberg.jl index ef7570731197a..75b3e121f9086 100644 --- a/stdlib/LinearAlgebra/src/hessenberg.jl +++ b/stdlib/LinearAlgebra/src/hessenberg.jl @@ -129,41 +129,29 @@ for T = (:Number, :UniformScaling, :Diagonal) end function *(H::UpperHessenberg, U::UpperOrUnitUpperTriangular) - T = typeof(oneunit(eltype(H))*oneunit(eltype(U))) - HH = copy_similar(H, T) - rmul!(HH, U) + HH = _mulmattri!(_initarray(*, eltype(H), eltype(U), H), H, U) UpperHessenberg(HH) end function *(U::UpperOrUnitUpperTriangular, H::UpperHessenberg) - T = typeof(oneunit(eltype(H))*oneunit(eltype(U))) - HH = copy_similar(H, T) - lmul!(U, HH) + HH = _multrimat!(_initarray(*, eltype(U), eltype(H), H), U, H) UpperHessenberg(HH) end function /(H::UpperHessenberg, U::UpperTriangular) - T = typeof(oneunit(eltype(H))/oneunit(eltype(U))) - HH = copy_similar(H, T) - rdiv!(HH, U) + HH = _rdiv!(_initarray(/, eltype(H), eltype(U), H), H, U) UpperHessenberg(HH) end function /(H::UpperHessenberg, U::UnitUpperTriangular) - T = typeof(oneunit(eltype(H))/oneunit(eltype(U))) - HH = copy_similar(H, T) - rdiv!(HH, U) + HH = _rdiv!(_initarray(/, eltype(H), eltype(U), H), H, U) UpperHessenberg(HH) end function \(U::UpperTriangular, H::UpperHessenberg) - T = typeof(oneunit(eltype(U))\oneunit(eltype(H))) - HH = copy_similar(H, T) - ldiv!(U, HH) + HH = ldiv!(_initarray(\, eltype(U), eltype(H), H), U, H) UpperHessenberg(HH) end function \(U::UnitUpperTriangular, H::UpperHessenberg) - T = typeof(oneunit(eltype(U))\oneunit(eltype(H))) - HH = copy_similar(H, T) - ldiv!(U, HH) + HH = ldiv!(_initarray(\, eltype(U), eltype(H), H), U, H) UpperHessenberg(HH) end diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 8b71db6dc328d..7a7f615e6f3e3 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -265,15 +265,14 @@ julia> C 730.0 740.0 ``` """ -@inline mul!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat, - alpha::Number, beta::Number) = +@inline mul!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat, α::Number, β::Number) = generic_matmatmul!( C, adj_or_trans_char(A), adj_or_trans_char(B), _parent(A), _parent(B), - MulAddMul(alpha, beta) + MulAddMul(α, β) ) """ diff --git a/stdlib/LinearAlgebra/src/special.jl b/stdlib/LinearAlgebra/src/special.jl index 06227c782002a..1744a2301f48a 100644 --- a/stdlib/LinearAlgebra/src/special.jl +++ b/stdlib/LinearAlgebra/src/special.jl @@ -107,6 +107,14 @@ for op in (:+, :-) end end +# disambiguation between triangular and banded matrices, banded ones "dominate" +mul!(C::AbstractMatrix, A::AbstractTriangular, B::BandedMatrix) = _mul!(C, A, B, MulAddMul()) +mul!(C::AbstractMatrix, A::BandedMatrix, B::AbstractTriangular) = _mul!(C, A, B, MulAddMul()) +mul!(C::AbstractMatrix, A::AbstractTriangular, B::BandedMatrix, alpha::Number, beta::Number) = + _mul!(C, A, B, MulAddMul(alpha, beta)) +mul!(C::AbstractMatrix, A::BandedMatrix, B::AbstractTriangular, alpha::Number, beta::Number) = + _mul!(C, A, B, MulAddMul(alpha, beta)) + function *(H::UpperHessenberg, B::Bidiagonal) T = promote_op(matprod, eltype(H), eltype(B)) A = mul!(similar(H, T, size(H)), H, B) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 557516d84811b..1e4ba4119393d 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -176,7 +176,7 @@ function Matrix{T}(A::UnitLowerTriangular) where T copyto!(B, A.data) tril!(B) for i = 1:size(B,1) - B[i,i] = 1 + B[i,i] = oneunit(T) end B end @@ -191,7 +191,7 @@ function Matrix{T}(A::UnitUpperTriangular) where T copyto!(B, A.data) triu!(B) for i = 1:size(B,1) - B[i,i] = 1 + B[i,i] = oneunit(T) end B end @@ -205,7 +205,7 @@ function full!(A::UnitLowerTriangular) B = A.data tril!(B) for i = 1:size(A,1) - B[i,i] = 1 + B[i,i] = oneunit(eltype(B)) end B end @@ -218,7 +218,7 @@ function full!(A::UnitUpperTriangular) B = A.data triu!(B) for i = 1:size(A,1) - B[i,i] = 1 + B[i,i] = oneunit(eltype(B)) end B end @@ -234,7 +234,7 @@ getindex(A::UpperTriangular, i::Integer, j::Integer) = function setindex!(A::UpperTriangular, x, i::Integer, j::Integer) if i > j - x == 0 || throw(ArgumentError("cannot set index in the lower triangular part " * + iszero(x) || throw(ArgumentError("cannot set index in the lower triangular part " * "($i, $j) of an UpperTriangular matrix to a nonzero value ($x)")) else A.data[i,j] = x @@ -244,10 +244,10 @@ end function setindex!(A::UnitUpperTriangular, x, i::Integer, j::Integer) if i > j - x == 0 || throw(ArgumentError("cannot set index in the lower triangular part " * + iszero(x) || throw(ArgumentError("cannot set index in the lower triangular part " * "($i, $j) of a UnitUpperTriangular matrix to a nonzero value ($x)")) elseif i == j - x == 1 || throw(ArgumentError("cannot set index on the diagonal ($i, $j) " * + x == oneunit(x) || throw(ArgumentError("cannot set index on the diagonal ($i, $j) " * "of a UnitUpperTriangular matrix to a non-unit value ($x)")) else A.data[i,j] = x @@ -257,7 +257,7 @@ end function setindex!(A::LowerTriangular, x, i::Integer, j::Integer) if i < j - x == 0 || throw(ArgumentError("cannot set index in the upper triangular part " * + iszero(x) || throw(ArgumentError("cannot set index in the upper triangular part " * "($i, $j) of a LowerTriangular matrix to a nonzero value ($x)")) else A.data[i,j] = x @@ -267,10 +267,10 @@ end function setindex!(A::UnitLowerTriangular, x, i::Integer, j::Integer) if i < j - x == 0 || throw(ArgumentError("cannot set index in the upper triangular part " * + iszero(x) || throw(ArgumentError("cannot set index in the upper triangular part " * "($i, $j) of a UnitLowerTriangular matrix to a nonzero value ($x)")) elseif i == j - x == 1 || throw(ArgumentError("cannot set index on the diagonal ($i, $j) " * + x == oneunit(x) || throw(ArgumentError("cannot set index on the diagonal ($i, $j) " * "of a UnitLowerTriangular matrix to a non-unit value ($x)")) else A.data[i,j] = x @@ -302,23 +302,23 @@ istril(A::Transpose, k::Integer=0) = istriu(A.parent, -k) istriu(A::Adjoint, k::Integer=0) = istril(A.parent, -k) istriu(A::Transpose, k::Integer=0) = istril(A.parent, -k) -function tril!(A::UpperTriangular, k::Integer=0) +function tril!(A::UpperTriangular{T}, k::Integer=0) where {T} n = size(A,1) if k < 0 - fill!(A.data,0) + fill!(A.data, zero(T)) return A elseif k == 0 for j in 1:n, i in 1:j-1 - A.data[i,j] = 0 + A.data[i,j] = zero(T) end return A else return UpperTriangular(tril!(A.data,k)) end end -triu!(A::UpperTriangular, k::Integer=0) = UpperTriangular(triu!(A.data,k)) +triu!(A::UpperTriangular, k::Integer=0) = UpperTriangular(triu!(A.data, k)) -function tril!(A::UnitUpperTriangular{T}, k::Integer=0) where T +function tril!(A::UnitUpperTriangular{T}, k::Integer=0) where {T} n = size(A,1) if k < 0 fill!(A.data, zero(T)) @@ -341,25 +341,25 @@ function triu!(A::UnitUpperTriangular, k::Integer=0) for i in diagind(A) A.data[i] = oneunit(eltype(A)) end - return triu!(UpperTriangular(A.data),k) + return triu!(UpperTriangular(A.data), k) end -function triu!(A::LowerTriangular, k::Integer=0) +function triu!(A::LowerTriangular{T}, k::Integer=0) where {T} n = size(A,1) if k > 0 - fill!(A.data,0) + fill!(A.data, zero(T)) return A elseif k == 0 for j in 1:n, i in j+1:n - A.data[i,j] = 0 + A.data[i,j] = zero(T) end return A else - return LowerTriangular(triu!(A.data,k)) + return LowerTriangular(triu!(A.data, k)) end end -tril!(A::LowerTriangular, k::Integer=0) = LowerTriangular(tril!(A.data,k)) +tril!(A::LowerTriangular, k::Integer=0) = LowerTriangular(tril!(A.data, k)) function triu!(A::UnitLowerTriangular{T}, k::Integer=0) where T n = size(A,1) @@ -376,7 +376,7 @@ function triu!(A::UnitLowerTriangular{T}, k::Integer=0) where T for i in diagind(A) A.data[i] = oneunit(T) end - return LowerTriangular(triu!(A.data,k)) + return LowerTriangular(triu!(A.data, k)) end end @@ -384,7 +384,7 @@ function tril!(A::UnitLowerTriangular, k::Integer=0) for i in diagind(A) A.data[i] = oneunit(eltype(A)) end - return tril!(LowerTriangular(A.data),k) + return tril!(LowerTriangular(A.data), k) end adjoint(A::LowerTriangular) = UpperTriangular(adjoint(A.data)) @@ -406,9 +406,9 @@ adjoint!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U' , true, true adjoint!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U' , true, true)) diag(A::LowerTriangular) = diag(A.data) -diag(A::UnitLowerTriangular) = fill(one(eltype(A)), size(A,1)) +diag(A::UnitLowerTriangular) = fill(oneunit(eltype(A)), size(A,1)) diag(A::UpperTriangular) = diag(A.data) -diag(A::UnitUpperTriangular) = fill(one(eltype(A)), size(A,1)) +diag(A::UnitUpperTriangular) = fill(oneunit(eltype(A)), size(A,1)) # Unary operations -(A::LowerTriangular) = LowerTriangular(-A.data) @@ -416,14 +416,14 @@ diag(A::UnitUpperTriangular) = fill(one(eltype(A)), size(A,1)) function -(A::UnitLowerTriangular) Anew = -A.data for i = 1:size(A, 1) - Anew[i, i] = -1 + Anew[i, i] = -A[i, i] end LowerTriangular(Anew) end function -(A::UnitUpperTriangular) Anew = -A.data for i = 1:size(A, 1) - Anew[i, i] = -1 + Anew[i, i] = -A[i, i] end UpperTriangular(Anew) end @@ -434,7 +434,7 @@ tr(A::UpperTriangular) = tr(A.data) tr(A::UnitUpperTriangular) = size(A, 1) * oneunit(eltype(A)) # copy and scale -function copyto!(A::T, B::T) where T<:Union{UpperTriangular,UnitUpperTriangular} +function copyto!(A::T, B::T) where {T<:Union{UpperTriangular,UnitUpperTriangular}} n = size(B,1) for j = 1:n for i = 1:(isa(B, UnitUpperTriangular) ? j-1 : j) @@ -443,7 +443,7 @@ function copyto!(A::T, B::T) where T<:Union{UpperTriangular,UnitUpperTriangular} end return A end -function copyto!(A::T, B::T) where T<:Union{LowerTriangular,UnitLowerTriangular} +function copyto!(A::T, B::T) where {T<:Union{LowerTriangular,UnitLowerTriangular}} n = size(B,1) for j = 1:n for i = (isa(B, UnitLowerTriangular) ? j+1 : j):n @@ -453,106 +453,100 @@ function copyto!(A::T, B::T) where T<:Union{LowerTriangular,UnitLowerTriangular} return A end -# Define `mul!` for (Unit){Upper,Lower}Triangular matrices times a -# number. -for (Trig, UnitTrig) in Any[(UpperTriangular, UnitUpperTriangular), - (LowerTriangular, UnitLowerTriangular)] - for (TB, TC) in Any[(Trig, Number), - (Number, Trig), - (UnitTrig, Number), - (Number, UnitTrig)] - @eval @inline mul!(A::$Trig, B::$TB, C::$TC, alpha::Number, beta::Number) = - _mul!(A, B, C, MulAddMul(alpha, beta)) - end -end +# Define `mul!` for (Unit){Upper,Lower}Triangular matrices times a number. +# be permissive here and require compatibility later in _triscale! +@inline mul!(A::UpperOrLowerTriangular, B::UpperOrLowerTriangular, C::Number, alpha::Number, beta::Number) = + _triscale!(A, B, C, MulAddMul(alpha, beta)) +@inline mul!(A::UpperOrLowerTriangular, B::Number, C::UpperOrLowerTriangular, alpha::Number, beta::Number) = + _triscale!(A, B, C, MulAddMul(alpha, beta)) -@inline function _mul!(A::UpperTriangular, B::UpperTriangular, c::Number, _add::MulAddMul) +function _triscale!(A::UpperTriangular, B::UpperTriangular, c::Number, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) for j = 1:n for i = 1:j - @inbounds _modify!(_add, B[i,j] * c, A, (i,j)) + @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) end end return A end -@inline function _mul!(A::UpperTriangular, c::Number, B::UpperTriangular, _add::MulAddMul) +function _triscale!(A::UpperTriangular, c::Number, B::UpperTriangular, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) for j = 1:n for i = 1:j - @inbounds _modify!(_add, c * B[i,j], A, (i,j)) + @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) end end return A end -@inline function _mul!(A::UpperTriangular, B::UnitUpperTriangular, c::Number, _add::MulAddMul) +function _triscale!(A::UpperOrUnitUpperTriangular, B::UnitUpperTriangular, c::Number, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) for j = 1:n @inbounds _modify!(_add, c, A, (j,j)) for i = 1:(j - 1) - @inbounds _modify!(_add, B[i,j] * c, A, (i,j)) + @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) end end return A end -@inline function _mul!(A::UpperTriangular, c::Number, B::UnitUpperTriangular, _add::MulAddMul) +function _triscale!(A::UpperOrUnitUpperTriangular, c::Number, B::UnitUpperTriangular, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) for j = 1:n @inbounds _modify!(_add, c, A, (j,j)) for i = 1:(j - 1) - @inbounds _modify!(_add, c * B[i,j], A, (i,j)) + @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) end end return A end -@inline function _mul!(A::LowerTriangular, B::LowerTriangular, c::Number, _add::MulAddMul) +function _triscale!(A::LowerTriangular, B::LowerTriangular, c::Number, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) for j = 1:n for i = j:n - @inbounds _modify!(_add, B[i,j] * c, A, (i,j)) + @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) end end return A end -@inline function _mul!(A::LowerTriangular, c::Number, B::LowerTriangular, _add::MulAddMul) +function _triscale!(A::LowerTriangular, c::Number, B::LowerTriangular, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) for j = 1:n for i = j:n - @inbounds _modify!(_add, c * B[i,j], A, (i,j)) + @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) end end return A end -@inline function _mul!(A::LowerTriangular, B::UnitLowerTriangular, c::Number, _add::MulAddMul) +function _triscale!(A::LowerOrUnitLowerTriangular, B::UnitLowerTriangular, c::Number, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) for j = 1:n @inbounds _modify!(_add, c, A, (j,j)) for i = (j + 1):n - @inbounds _modify!(_add, B[i,j] * c, A, (i,j)) + @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) end end return A end -@inline function _mul!(A::LowerTriangular, c::Number, B::UnitLowerTriangular, _add::MulAddMul) +function _triscale!(A::LowerOrUnitLowerTriangular, c::Number, B::UnitLowerTriangular, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) for j = 1:n @inbounds _modify!(_add, c, A, (j,j)) for i = (j + 1):n - @inbounds _modify!(_add, c * B[i,j], A, (i,j)) + @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) end end return A end -rmul!(A::Union{UpperTriangular,LowerTriangular}, c::Number) = mul!(A, A, c) -lmul!(c::Number, A::Union{UpperTriangular,LowerTriangular}) = mul!(A, c, A) +rmul!(A::UpperOrLowerTriangular, c::Number) = @inline _triscale!(A, A, c, MulAddMul()) +lmul!(c::Number, A::UpperOrLowerTriangular) = @inline _triscale!(A, c, A, MulAddMul()) function dot(x::AbstractVector, A::UpperTriangular, y::AbstractVector) require_one_based_indexing(x, y) @@ -668,12 +662,39 @@ fillstored!(A::UnitUpperTriangular, x) = (fillband!(A.data, x, 1, size(A,2)-1); # BlasFloat routines # ###################### -lmul!(A::Tridiagonal, B::AbstractTriangular) = A*full!(B) # is this necessary? +lmul!(A::Tridiagonal, B::AbstractTriangular) = A*full!(B) +mul!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVector) = _multrimat!(C, A, B) +mul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractMatrix) = _multrimat!(C, A, B) +mul!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractTriangular) = _mulmattri!(C, A, B) +mul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractTriangular) = _multrimat!(C, A, B) -mul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractVecOrMat) = +for TC in (:AbstractVector, :AbstractMatrix) + @eval @inline function mul!(C::$TC, A::AbstractTriangular, B::AbstractVector, alpha::Number, beta::Number) + if isone(alpha) && iszero(beta) + return mul!(C, A, B) + else + return generic_matvecmul!(C, 'N', A, B, MulAddMul(alpha, beta)) + end + end +end +for (TA, TB) in ((:AbstractTriangular, :AbstractMatrix), + (:AbstractMatrix, :AbstractTriangular), + (:AbstractTriangular, :AbstractTriangular) + ) + @eval @inline function mul!(C::AbstractMatrix, A::$TA, B::$TB, alpha::Number, beta::Number) + if isone(alpha) && iszero(beta) + return mul!(C, A, B) + else + return generic_matmatmul!(C, 'N', 'N', A, B, MulAddMul(alpha, beta)) + end + end +end + + +# generic fallback for AbstractTriangular matrices outside of the four subtypes provided here +_multrimat!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = lmul!(A, inplace_adj_or_trans(B)(C, _parent(B))) -@inline mul!(C::AbstractMatrix, A::AbstractTriangular, B::AdjOrTrans{<:Any,<:AbstractVecOrMat}, alpha::Number, beta::Number) = - mul!(C, A, copy(B), alpha, beta) +_mulmattri!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractTriangular) = rmul!(copyto!(C, A), B) # preserve triangular structure in in-place multiplication for (cty, aty, bty) in ((:UpperTriangular, :UpperTriangular, :UpperTriangular), @@ -684,18 +705,10 @@ for (cty, aty, bty) in ((:UpperTriangular, :UpperTriangular, :UpperTriangular), (:LowerTriangular, :LowerTriangular, :UnitLowerTriangular), (:LowerTriangular, :UnitLowerTriangular, :LowerTriangular), (:UnitLowerTriangular, :UnitLowerTriangular, :UnitLowerTriangular)) - @eval function mul!(C::$cty, A::$aty, B::$bty) - lmul!(A, copyto!(parent(C), B)) + @eval function _multrimat!(C::$cty, A::$aty, B::$bty) + _multrimat!(parent(C), A, B) return C end - - @eval @inline function mul!(C::$cty, A::$aty, B::$bty, alpha::Number, beta::Number) - if isone(alpha) && iszero(beta) - return mul!(C, A, B) - else - return generic_matmatmul!(C, 'N', 'N', A, B, MulAddMul(alpha, beta)) - end - end end # direct multiplication/division @@ -791,16 +804,46 @@ for (t, uploc, isunitc) in ((:LowerTriangular, 'U', 'N'), end end -function inv(A::LowerTriangular{T}) where T - S = typeof((zero(T)*one(T) + zero(T))/one(T)) - LowerTriangular(ldiv!(convert(AbstractArray{S}, A), Matrix{S}(I, size(A, 1), size(A, 1)))) -end -function inv(A::UpperTriangular{T}) where T - S = typeof((zero(T)*one(T) + zero(T))/one(T)) - UpperTriangular(ldiv!(convert(AbstractArray{S}, A), Matrix{S}(I, size(A, 1), size(A, 1)))) +# redirect back to BLAS +for t in (:UpperTriangular, :UnitUpperTriangular, :LowerTriangular, :UnitLowerTriangular) + @eval _multrimat!(C::StridedVecOrMat{T}, A::$t{T,<:StridedMatrix}, B::AbstractVecOrMat{T}) where {T<:BlasFloat} = + lmul!(A, copyto!(C, B)) + @eval _multrimat!(C::StridedVecOrMat{T}, A::$t{<:Any,<:Adjoint{T,<:StridedMatrix}}, B::AbstractVecOrMat{T}) where {T<:BlasFloat} = + lmul!(A, copyto!(C, B)) + @eval _multrimat!(C::StridedVecOrMat{T}, A::$t{<:Any,<:Transpose{T,<:StridedMatrix}}, B::AbstractVecOrMat{T}) where {T<:BlasFloat} = + lmul!(A, copyto!(C, B)) + @eval _mulmattri!(C::StridedMatrix{T}, A::AbstractMatrix{T}, B::$t{T,<:StridedMatrix}) where {T<:BlasFloat} = + rmul!(copyto!(C, A), B) + @eval _mulmattri!(C::StridedMatrix{T}, A::AbstractMatrix{T}, B::$t{<:Any,<:Adjoint{T,<:StridedMatrix}}) where {T<:BlasFloat} = + rmul!(copyto!(C, A), B) + @eval _mulmattri!(C::StridedMatrix{T}, A::AbstractMatrix{T}, B::$t{<:Any,<:Transpose{T,<:StridedMatrix}}) where {T<:BlasFloat} = + rmul!(copyto!(C, A), B) + + @eval ldiv!(C::StridedVecOrMat{T}, A::$t{T,<:StridedMatrix}, B::AbstractVecOrMat{T}) where {T<:BlasFloat} = + ldiv!(A, copyto!(C, B)) + @eval ldiv!(C::StridedVecOrMat{T}, A::$t{<:Any,<:Adjoint{T,<:StridedMatrix}}, B::AbstractVecOrMat{T}) where {T<:BlasFloat} = + ldiv!(A, copyto!(C, B)) + @eval ldiv!(C::StridedVecOrMat{T}, A::$t{<:Any,<:Transpose{T,<:StridedMatrix}}, B::AbstractVecOrMat{T}) where {T<:BlasFloat} = + ldiv!(A, copyto!(C, B)) + @eval _rdiv!(C::StridedMatrix{T}, A::AbstractMatrix{T}, B::$t{T,<:StridedMatrix}) where {T<:BlasFloat} = + rdiv!(copyto!(C, A), B) + @eval _rdiv!(C::StridedMatrix{T}, A::AbstractMatrix{T}, B::$t{<:Any,<:Adjoint{T,<:StridedMatrix}}) where {T<:BlasFloat} = + rdiv!(copyto!(C, A), B) + @eval _rdiv!(C::StridedMatrix{T}, A::AbstractMatrix{T}, B::$t{<:Any,<:Transpose{T,<:StridedMatrix}}) where {T<:BlasFloat} = + rdiv!(copyto!(C, A), B) +end + +for t in (:LowerTriangular, :UnitLowerTriangular, :UpperTriangular, :UnitUpperTriangular) + @eval function inv(A::$t{T}) where {T} + S = typeof(inv(oneunit(T))) + if S <: BlasFloat || S === T # i.e. A is unitless + $t(ldiv!(convert(AbstractArray{S}, A), Matrix{S}(I, size(A)))) + else + J = (one(T)*I)(size(A, 1)) + $t(ldiv!(similar(A, S, size(A)), A, J)) + end + end end -inv(A::UnitUpperTriangular{T}) where {T} = UnitUpperTriangular(ldiv!(A, Matrix{T}(I, size(A, 1), size(A, 1)))) -inv(A::UnitLowerTriangular{T}) where {T} = UnitLowerTriangular(ldiv!(A, Matrix{T}(I, size(A, 1), size(A, 1)))) errorbounds(A::AbstractTriangular{T,<:AbstractMatrix}, X::AbstractVecOrMat{T}, B::AbstractVecOrMat{T}) where {T<:Union{BigFloat,Complex{BigFloat}}} = error("not implemented yet! Please submit a pull request.") @@ -878,147 +921,193 @@ for (t, unitt) in ((UpperTriangular, UnitUpperTriangular), end $t(B) end + + lmul!(A::$t, B::AbstractVecOrMat) = @inline _multrimat!(B, A, B) + lmul!(A::$unitt, B::AbstractVecOrMat) = @inline _multrimat!(B, A, B) + + rmul!(A::AbstractMatrix, B::$t) = @inline _mulmattri!(A, A, B) + rmul!(A::AbstractMatrix, B::$unitt) = @inline _mulmattri!(A, A, B) end end ## Generic triangular multiplication -function lmul!(A::UpperTriangular, B::AbstractVecOrMat) - require_one_based_indexing(A, B) +function _multrimat!(C::AbstractVecOrMat, A::UpperTriangular, B::AbstractVecOrMat) + require_one_based_indexing(C, A, B) m, n = size(B, 1), size(B, 2) - if m != size(A, 1) + N = size(A, 1) + if m != N throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m")) end - @inbounds for j = 1:n - for i = 1:m - Bij = A.data[i,i]*B[i,j] - for k = i + 1:m - Bij += A.data[i,k]*B[k,j] + mc, nc = size(C, 1), size(C, 2) + if mc != N || nc != n + throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($N,$n)")) + end + @inbounds for j in 1:n + for i in 1:m + Cij = A.data[i,i] * B[i,j] + for k in i + 1:m + Cij += A.data[i,k] * B[k,j] end - B[i,j] = Bij + C[i,j] = Cij end end - B + return C end -function lmul!(A::UnitUpperTriangular, B::AbstractVecOrMat) - require_one_based_indexing(A, B) +function _multrimat!(C::AbstractVecOrMat, A::UnitUpperTriangular, B::AbstractVecOrMat) + require_one_based_indexing(C, A, B) m, n = size(B, 1), size(B, 2) - if m != size(A, 1) + N = size(A, 1) + if m != N throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m")) end - @inbounds for j = 1:n - for i = 1:m - Bij = B[i,j] - for k = i + 1:m - Bij += A.data[i,k]*B[k,j] + + mc, nc = size(C, 1), size(C, 2) + if mc != N || nc != n + throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($N,$n)")) + end + @inbounds for j in 1:n + for i in 1:m + Cij = oneunit(eltype(A)) * B[i,j] + for k in i + 1:m + Cij += A.data[i,k] * B[k,j] end - B[i,j] = Bij + C[i,j] = Cij end end - B + return C end -function lmul!(A::LowerTriangular, B::AbstractVecOrMat) - require_one_based_indexing(A, B) +function _multrimat!(C::AbstractVecOrMat, A::LowerTriangular, B::AbstractVecOrMat) + require_one_based_indexing(C, A, B) m, n = size(B, 1), size(B, 2) - if m != size(A, 1) + N = size(A, 1) + if m != N throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m")) end - @inbounds for j = 1:n - for i = m:-1:1 - Bij = A.data[i,i]*B[i,j] - for k = 1:i - 1 - Bij += A.data[i,k]*B[k,j] + mc, nc = size(C, 1), size(C, 2) + if mc != N || nc != n + throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($N,$n)")) + end + @inbounds for j in 1:n + for i in m:-1:1 + Cij = A.data[i,i] * B[i,j] + for k in 1:i - 1 + Cij += A.data[i,k] * B[k,j] end - B[i,j] = Bij + C[i,j] = Cij end end - B + return C end -function lmul!(A::UnitLowerTriangular, B::AbstractVecOrMat) - require_one_based_indexing(A, B) +function _multrimat!(C::AbstractVecOrMat, A::UnitLowerTriangular, B::AbstractVecOrMat) + require_one_based_indexing(C, A, B) m, n = size(B, 1), size(B, 2) - if m != size(A, 1) + N = size(A, 1) + if m != N throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m")) end - @inbounds for j = 1:n - for i = m:-1:1 - Bij = B[i,j] - for k = 1:i - 1 - Bij += A.data[i,k]*B[k,j] + mc, nc = size(C, 1), size(C, 2) + if mc != N || nc != n + throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($N,$n)")) + end + @inbounds for j in 1:n + for i in m:-1:1 + Cij = oneunit(eltype(A)) * B[i,j] + for k in 1:i - 1 + Cij += A.data[i,k] * B[k,j] end - B[i,j] = Bij + C[i,j] = Cij end end - B + return C end -function rmul!(A::AbstractMatrix, B::UpperTriangular) - require_one_based_indexing(A, B) - m, n = size(A) - if size(B, 1) != n - throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))")) +function _mulmattri!(C::AbstractMatrix, A::AbstractMatrix, B::UpperTriangular) + require_one_based_indexing(C, A, B) + m, n = size(A, 1), size(A, 2) + N = size(B, 1) + if n != N + throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $N")) + end + mc, nc = size(C, 1), size(C, 2) + if mc != m || nc != N + throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($m,$N)")) end - @inbounds for i = 1:m - for j = n:-1:1 - Aij = A[i,j]*B.data[j,j] - for k = 1:j - 1 - Aij += A[i,k]*B.data[k,j] + @inbounds for i in 1:m + for j in n:-1:1 + Cij = A[i,j] * B.data[j,j] + for k in 1:j - 1 + Cij += A[i,k] * B.data[k,j] end - A[i,j] = Aij + C[i,j] = Cij end end - A + return C end -function rmul!(A::AbstractMatrix, B::UnitUpperTriangular) - require_one_based_indexing(A, B) - m, n = size(A) - if size(B, 1) != n - throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))")) - end - @inbounds for i = 1:m - for j = n:-1:1 - Aij = A[i,j] - for k = 1:j - 1 - Aij += A[i,k]*B.data[k,j] +function _mulmattri!(C::AbstractMatrix, A::AbstractMatrix, B::UnitUpperTriangular) + require_one_based_indexing(C, A, B) + m, n = size(A, 1), size(A, 2) + N = size(B, 1) + if n != N + throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $N")) + end + mc, nc = size(C, 1), size(C, 2) + if mc != m || nc != N + throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($m,$N)")) + end + @inbounds for i in 1:m + for j in n:-1:1 + Cij = A[i,j] * oneunit(eltype(B)) + for k in 1:j - 1 + Cij += A[i,k] * B.data[k,j] end - A[i,j] = Aij + C[i,j] = Cij end end - A + return C end - -function rmul!(A::AbstractMatrix, B::LowerTriangular) - require_one_based_indexing(A, B) - m, n = size(A) - if size(B, 1) != n - throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))")) - end - @inbounds for i = 1:m - for j = 1:n - Aij = A[i,j]*B.data[j,j] - for k = j + 1:n - Aij += A[i,k]*B.data[k,j] +function _mulmattri!(C::AbstractMatrix, A::AbstractMatrix, B::LowerTriangular) + require_one_based_indexing(C, A, B) + m, n = size(A, 1), size(A, 2) + N = size(B, 1) + if n != N + throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $N")) + end + mc, nc = size(C, 1), size(C, 2) + if mc != m || nc != N + throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($m,$N)")) + end + @inbounds for i in 1:m + for j in 1:n + Cij = A[i,j] * B.data[j,j] + for k in j + 1:n + Cij += A[i,k] * B.data[k,j] end - A[i,j] = Aij + C[i,j] = Cij end end - A + return C end -function rmul!(A::AbstractMatrix, B::UnitLowerTriangular) - require_one_based_indexing(A, B) - m, n = size(A) - if size(B, 1) != n - throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))")) - end - @inbounds for i = 1:m - for j = 1:n - Aij = A[i,j] - for k = j + 1:n - Aij += A[i,k]*B.data[k,j] +function _mulmattri!(C::AbstractMatrix, A::AbstractMatrix, B::UnitLowerTriangular) + require_one_based_indexing(C, A, B) + m, n = size(A, 1), size(A, 2) + N = size(B, 1) + if n != N + throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $N")) + end + mc, nc = size(C, 1), size(C, 2) + if mc != m || nc != N + throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($m,$N)")) + end + @inbounds for i in 1:m + for j in 1:n + Cij = A[i,j] * oneunit(eltype(B)) + for k in j + 1:n + Cij += A[i,k] * B.data[k,j] end - A[i,j] = Aij + C[i,j] = Cij end end - A + return C end #Generic solver using naive substitution @@ -1029,222 +1118,237 @@ end # does not significantly impact performance as of Dec 2015 # replacing repeated references to A.data[j,j] with [Ajj = A.data[j,j] and references to Ajj] # does not significantly impact performance as of Dec 2015 -function ldiv!(A::UpperTriangular, b::AbstractVector) - require_one_based_indexing(A, b) - n = size(A, 2) - if !(n == length(b)) - throw(DimensionMismatch("second dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) +ldiv!(A::AbstractTriangular, b::AbstractVecOrMat) = @inline ldiv!(b, A, b) +function ldiv!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractMatrix) + require_one_based_indexing(C, A, B) + nA, mA = size(A) + n = size(B, 1) + if nA != n + throw(DimensionMismatch("second dimension of left hand side A, $mA, and first dimension of right hand side B, $n, must be equal")) end - @inbounds for j in n:-1:1 - iszero(A.data[j,j]) && throw(SingularException(j)) - bj = b[j] = A.data[j,j] \ b[j] - for i in j-1:-1:1 # counterintuitively 1:j-1 performs slightly better - b[i] -= A.data[i,j] * bj + if size(C) != size(B) + throw(DimensionMismatch("size of output, $(size(C)), does not match size of right hand side, $(size(B))")) + end + @inbounds for (c, b) in zip(eachcol(C), eachcol(B)) + ldiv!(c, A, b) + end + C +end +@inline function ldiv!(c::AbstractVector, A::AbstractTriangular, b::AbstractVector) + @boundscheck begin + require_one_based_indexing(c, A, b) + n = size(A, 2) + if !(n == length(b)) + throw(DimensionMismatch("second dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) + end + if !(n == length(c)) + throw(DimensionMismatch("length of output c, $(length(c)), does not match length of right hand side b, $(length(b))")) end end - return b + return _ldiv!(c, A, b) end -function ldiv!(A::UnitUpperTriangular, b::AbstractVector) - require_one_based_indexing(A, b) + +_uconvert_copyto!(c, b, oA) = (c .= Ref(oA) .\ b) +_uconvert_copyto!(c::AbstractArray{T}, b::AbstractArray{T}, _) where {T} = copyto!(c, b) + +@inline _ustrip(a) = oneunit(a) \ a +@inline _ustrip(a::Union{AbstractFloat,Integer,Complex,Rational}) = a + +# all of the following _ldiv! methods are "unsafe" in that they assume one-based indexing +# and compatible sizes +function _ldiv!(c::AbstractVector, A::UpperTriangular, b::AbstractVector) n = size(A, 2) - if !(n == length(b)) - throw(DimensionMismatch("second dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) - end + c !== b && _uconvert_copyto!(c, b, oneunit(eltype(A))) @inbounds for j in n:-1:1 - bj = b[j] - for i in j-1:-1:1 # counterintuitively 1:j-1 performs slightly better - b[i] -= A.data[i,j] * bj + ajj = A.data[j,j] + iszero(ajj) && throw(SingularException(j)) + cj = c[j] = _ustrip(ajj) \ c[j] + for i in j-1:-1:1 + c[i] -= _ustrip(A.data[i,j]) * cj end end - return b + return c end -function ldiv!(A::LowerTriangular, b::AbstractVector) - require_one_based_indexing(A, b) +function _ldiv!(c::AbstractVector, A::UnitUpperTriangular, b::AbstractVector) n = size(A, 2) - if !(n == length(b)) - throw(DimensionMismatch("second dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) + c !== b && _uconvert_copyto!(c, b, oneunit(eltype(A))) + @inbounds for j in n:-1:1 + cj = c[j] + for i in 1:j-1 + c[i] -= _ustrip(A.data[i,j]) * cj + end end + return c +end +function _ldiv!(c::AbstractVector, A::LowerTriangular, b::AbstractVector) + n = size(A, 2) + c !== b && _uconvert_copyto!(c, b, oneunit(eltype(A))) @inbounds for j in 1:n - iszero(A.data[j,j]) && throw(SingularException(j)) - bj = b[j] = A.data[j,j] \ b[j] + ajj = A.data[j,j] + iszero(ajj) && throw(SingularException(j)) + cj = c[j] = _ustrip(ajj) \ c[j] for i in j+1:n - b[i] -= A.data[i,j] * bj + c[i] -= _ustrip(A.data[i,j]) * cj end end - return b + return c end -function ldiv!(A::UnitLowerTriangular, b::AbstractVector) - require_one_based_indexing(A, b) +function _ldiv!(c::AbstractVector, A::UnitLowerTriangular, b::AbstractVector) n = size(A, 2) - if !(n == length(b)) - throw(DimensionMismatch("second dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) - end + c !== b && _uconvert_copyto!(c, b, oneunit(eltype(A))) @inbounds for j in 1:n - bj = b[j] + cj = c[j] for i in j+1:n - b[i] -= A.data[i,j] * bj + c[i] -= _ustrip(A.data[i,j]) * cj end end - return b -end -function ldiv!(A::AbstractTriangular, B::AbstractMatrix) - require_one_based_indexing(A, B) - nA, mA = size(A) - n = size(B, 1) - if nA != n - throw(DimensionMismatch("second dimension of left hand side A, $mA, and first dimension of right hand side B, $n, must be equal")) - end - for b in eachcol(B) - ldiv!(A, b) - end - B + return c end + # in the following transpose and conjugate transpose naive substitution variants, # accumulating in z rather than b[j,k] significantly improves performance as of Dec 2015 -function ldiv!(xA::UpperTriangular{<:Any,<:AdjOrTrans}, b::AbstractVector) - require_one_based_indexing(xA, b) +function _ldiv!(c::AbstractVector, xA::UpperTriangular{<:Any,<:AdjOrTrans}, b::AbstractVector) tfun = adj_or_trans(parent(xA)) A = parent(parent(xA)) - n = size(A, 1) - if !(n == length(b)) - throw(DimensionMismatch("first dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) - end + n = size(A, 2) @inbounds for j in n:-1:1 - z = b[j] - for i in n:-1:j+1 - z -= tfun(A[i,j]) * b[i] + ajj = A[j,j] + iszero(ajj) && throw(SingularException(j)) + bj = b[j] + for i in j+1:n + bj -= tfun(A[i,j]) * c[i] end - iszero(A[j,j]) && throw(SingularException(j)) - b[j] = tfun(A[j,j]) \ z + c[j] = tfun(ajj) \ bj end - return b + return c end - -function ldiv!(xA::UnitUpperTriangular{<:Any,<:AdjOrTrans}, b::AbstractVector) - require_one_based_indexing(xA, b) +function _ldiv!(c::AbstractVector, xA::UnitUpperTriangular{<:Any,<:AdjOrTrans}, b::AbstractVector) tfun = adj_or_trans(parent(xA)) A = parent(parent(xA)) - n = size(A, 1) - if !(n == length(b)) - throw(DimensionMismatch("first dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) - end + oA = oneunit(eltype(A)) + n = size(A, 2) @inbounds for j in n:-1:1 - z = b[j] - for i in n:-1:j+1 - z -= tfun(A[i,j]) * b[i] + bj = b[j] + for i in j+1:n + bj -= tfun(A[i,j]) * c[i] end - b[j] = z + c[j] = oA \ bj end - return b + return c end - -function ldiv!(xA::LowerTriangular{<:Any,<:AdjOrTrans}, b::AbstractVector) - require_one_based_indexing(xA, b) +function _ldiv!(c::AbstractVector, xA::LowerTriangular{<:Any,<:AdjOrTrans}, b::AbstractVector) tfun = adj_or_trans(parent(xA)) A = parent(parent(xA)) - n = size(A, 1) - if !(n == length(b)) - throw(DimensionMismatch("first dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) - end + n = size(A, 2) @inbounds for j in 1:n - z = b[j] + ajj = A[j,j] + iszero(ajj) && throw(SingularException(j)) + bj = b[j] for i in 1:j-1 - z -= tfun(A[i,j]) * b[i] + bj -= tfun(A[i,j]) * c[i] end - iszero(A[j,j]) && throw(SingularException(j)) - b[j] = tfun(A[j,j]) \ z + c[j] = tfun(ajj) \ bj end - return b + return c end - -function ldiv!(xA::UnitLowerTriangular{<:Any,<:AdjOrTrans}, b::AbstractVector) - require_one_based_indexing(xA, b) +function _ldiv!(c::AbstractVector, xA::UnitLowerTriangular{<:Any,<:AdjOrTrans}, b::AbstractVector) tfun = adj_or_trans(parent(xA)) A = parent(parent(xA)) - n = size(A, 1) - if !(n == length(b)) - throw(DimensionMismatch("first dimension of left hand side A, $n, and length of right hand side b, $(length(b)), must be equal")) - end + oA = oneunit(eltype(A)) + n = size(A, 2) @inbounds for j in 1:n - z = b[j] + bj = b[j] for i in 1:j-1 - z -= tfun(A[i,j]) * b[i] + bj -= tfun(A[i,j]) * c[i] end - b[j] = z + c[j] = oA \ bj end - return b + return c end -function rdiv!(A::AbstractMatrix, B::UpperTriangular) - require_one_based_indexing(A, B) +rdiv!(A::AbstractMatrix, B::AbstractTriangular) = @inline _rdiv!(A, A, B) +function _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::UpperTriangular) + require_one_based_indexing(C, A, B) m, n = size(A) if size(B, 1) != n throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))")) end - @inbounds for i = 1:m - for j = 1:n + if size(C) != size(A) + throw(DimensionMismatch("size of output, $(size(C)), does not match size of left hand side, $(size(A))")) + end + @inbounds for i in 1:m + for j in 1:n Aij = A[i,j] - for k = 1:j - 1 - Aij -= A[i,k]*B.data[k,j] + for k in 1:j - 1 + Aij -= C[i,k]*B.data[k,j] end iszero(B.data[j,j]) && throw(SingularException(j)) - A[i,j] = Aij/B.data[j,j] + C[i,j] = Aij / B.data[j,j] end end - A + C end -function rdiv!(A::AbstractMatrix, B::UnitUpperTriangular) - require_one_based_indexing(B) +function _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::UnitUpperTriangular) + require_one_based_indexing(C, A, B) m, n = size(A) if size(B, 1) != n throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))")) end - @inbounds for i = 1:m - for j = 1:n + if size(C) != size(A) + throw(DimensionMismatch("size of output, $(size(C)), does not match size of left hand side, $(size(A))")) + end + @inbounds for i in 1:m + for j in 1:n Aij = A[i,j] - for k = 1:j - 1 - Aij -= A[i,k]*B.data[k,j] + for k in 1:j - 1 + Aij -= C[i,k]*B.data[k,j] end - A[i,j] = Aij + C[i,j] = Aij / oneunit(eltype(B)) end end - A + C end -function rdiv!(A::AbstractMatrix, B::LowerTriangular) - require_one_based_indexing(A, B) +function _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::LowerTriangular) + require_one_based_indexing(C, A, B) m, n = size(A) if size(B, 1) != n throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))")) end - @inbounds for i = 1:m - for j = n:-1:1 + if size(C) != size(A) + throw(DimensionMismatch("size of output, $(size(C)), does not match size of left hand side, $(size(A))")) + end + @inbounds for i in 1:m + for j in n:-1:1 Aij = A[i,j] - for k = j + 1:n - Aij -= A[i,k]*B.data[k,j] + for k in j + 1:n + Aij -= C[i,k]*B.data[k,j] end iszero(B.data[j,j]) && throw(SingularException(j)) - A[i,j] = Aij/B.data[j,j] + C[i,j] = Aij / B.data[j,j] end end - A + C end -function rdiv!(A::AbstractMatrix, B::UnitLowerTriangular) - require_one_based_indexing(A, B) +function _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::UnitLowerTriangular) + require_one_based_indexing(C, A, B) m, n = size(A) if size(B, 1) != n throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))")) end - @inbounds for i = 1:m - for j = n:-1:1 + if size(C) != size(A) + throw(DimensionMismatch("size of output, $(size(C)), does not match size of left hand side, $(size(A))")) + end + @inbounds for i in 1:m + for j in n:-1:1 Aij = A[i,j] - for k = j + 1:n - Aij -= A[i,k]*B.data[k,j] + for k in j + 1:n + Aij -= C[i,k]*B.data[k,j] end - A[i,j] = Aij + C[i,j] = Aij / oneunit(eltype(B)) end end - A + C end lmul!(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(lmul!(A, triu!(B.data))) @@ -1274,175 +1378,126 @@ rmul!(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(rmul!(tril!( ## the element type doesn't have to be stable under division whereas that is ## necessary in the general triangular solve problem. -## Some Triangular-Triangular cases. We might want to write tailored methods -## for these cases, but I'm not sure it is worth it. - -for (f, f2!) in ((:*, :lmul!), (:\, :ldiv!)) - @eval begin - function ($f)(A::LowerTriangular, B::LowerTriangular) - TAB = typeof(($f)(zero(eltype(A)), zero(eltype(B))) + - ($f)(zero(eltype(A)), zero(eltype(B)))) - BB = copy_similar(B, TAB) - return LowerTriangular($f2!(convert(AbstractMatrix{TAB}, A), BB)) - end - - function $(f)(A::UnitLowerTriangular, B::LowerTriangular) - TAB = typeof((*)(zero(eltype(A)), zero(eltype(B))) + - (*)(zero(eltype(A)), zero(eltype(B)))) - BB = copy_similar(B, TAB) - return LowerTriangular($f2!(convert(AbstractMatrix{TAB}, A), BB)) - end - - function $(f)(A::LowerTriangular, B::UnitLowerTriangular) - TAB = typeof(($f)(zero(eltype(A)), zero(eltype(B))) + - ($f)(zero(eltype(A)), zero(eltype(B)))) - BB = copy_similar(B, TAB) - return LowerTriangular($f2!(convert(AbstractMatrix{TAB}, A), BB)) - end - - function $(f)(A::UnitLowerTriangular, B::UnitLowerTriangular) - TAB = typeof((*)(zero(eltype(A)), zero(eltype(B))) + - (*)(zero(eltype(A)), zero(eltype(B)))) - BB = copy_similar(B, TAB) - return UnitLowerTriangular($f2!(convert(AbstractMatrix{TAB}, A), BB)) - end - - function ($f)(A::UpperTriangular, B::UpperTriangular) - TAB = typeof(($f)(zero(eltype(A)), zero(eltype(B))) + - ($f)(zero(eltype(A)), zero(eltype(B)))) - BB = copy_similar(B, TAB) - return UpperTriangular($f2!(convert(AbstractMatrix{TAB}, A), BB)) - end - - function ($f)(A::UnitUpperTriangular, B::UpperTriangular) - TAB = typeof((*)(zero(eltype(A)), zero(eltype(B))) + - (*)(zero(eltype(A)), zero(eltype(B)))) - BB = copy_similar(B, TAB) - return UpperTriangular($f2!(convert(AbstractMatrix{TAB}, A), BB)) - end - - function ($f)(A::UpperTriangular, B::UnitUpperTriangular) - TAB = typeof(($f)(zero(eltype(A)), zero(eltype(B))) + - ($f)(zero(eltype(A)), zero(eltype(B)))) - BB = copy_similar(B, TAB) - return UpperTriangular($f2!(convert(AbstractMatrix{TAB}, A), BB)) - end - - function ($f)(A::UnitUpperTriangular, B::UnitUpperTriangular) - TAB = typeof((*)(zero(eltype(A)), zero(eltype(B))) + - (*)(zero(eltype(A)), zero(eltype(B)))) - BB = copy_similar(B, TAB) - return UnitUpperTriangular($f2!(convert(AbstractMatrix{TAB}, A), BB)) - end - end -end - -function (/)(A::LowerTriangular, B::LowerTriangular) - TAB = typeof((/)(zero(eltype(A)), one(eltype(B))) + - (/)(zero(eltype(A)), one(eltype(B)))) - AA = copy_similar(A, TAB) - return LowerTriangular(rdiv!(AA, convert(AbstractMatrix{TAB}, B))) -end -function (/)(A::UnitLowerTriangular, B::LowerTriangular) - TAB = typeof((/)(zero(eltype(A)), one(eltype(B))) + - (/)(zero(eltype(A)), one(eltype(B)))) - AA = copy_similar(A, TAB) - return LowerTriangular(rdiv!(AA, convert(AbstractMatrix{TAB}, B))) -end -function (/)(A::LowerTriangular, B::UnitLowerTriangular) - TAB = typeof((/)(zero(eltype(A)), one(eltype(B))) + - (/)(zero(eltype(A)), one(eltype(B)))) - AA = copy_similar(A, TAB) - return LowerTriangular(rdiv!(AA, convert(AbstractMatrix{TAB}, B))) -end -function (/)(A::UnitLowerTriangular, B::UnitLowerTriangular) - TAB = typeof((*)(zero(eltype(A)), zero(eltype(B))) + - (*)(zero(eltype(A)), zero(eltype(B)))) - AA = copy_similar(A, TAB) - return UnitLowerTriangular(rdiv!(AA, convert(AbstractMatrix{TAB}, B))) -end -function (/)(A::UpperTriangular, B::UpperTriangular) - TAB = typeof((/)(zero(eltype(A)), one(eltype(B))) + - (/)(zero(eltype(A)), one(eltype(B)))) - AA = copy_similar(A, TAB) - return UpperTriangular(rdiv!(AA, convert(AbstractMatrix{TAB}, B))) -end -function (/)(A::UnitUpperTriangular, B::UpperTriangular) - TAB = typeof((/)(zero(eltype(A)), one(eltype(B))) + - (/)(zero(eltype(A)), one(eltype(B)))) - AA = copy_similar(A, TAB) - return UpperTriangular(rdiv!(AA, convert(AbstractMatrix{TAB}, B))) -end -function (/)(A::UpperTriangular, B::UnitUpperTriangular) - TAB = typeof((/)(zero(eltype(A)), one(eltype(B))) + - (/)(zero(eltype(A)), one(eltype(B)))) - AA = copy_similar(A, TAB) - return UpperTriangular(rdiv!(AA, convert(AbstractMatrix{TAB}, B))) -end -function (/)(A::UnitUpperTriangular, B::UnitUpperTriangular) - TAB = typeof((*)(zero(eltype(A)), zero(eltype(B))) + - (*)(zero(eltype(A)), zero(eltype(B)))) - AA = copy_similar(A, TAB) - return UnitUpperTriangular(rdiv!(AA, convert(AbstractMatrix{TAB}, B))) -end - -_inner_type_promotion(A,B) = promote_type(eltype(A), eltype(B), typeof(zero(eltype(A))*zero(eltype(B)) + zero(eltype(A))*zero(eltype(B)))) +_inner_type_promotion(op, ::Type{TA}, ::Type{TB}) where {TA<:Integer,TB<:Integer} = + _init_eltype(*, TA, TB) +_inner_type_promotion(op, ::Type{TA}, ::Type{TB}) where {TA,TB} = + _init_eltype(op, TA, TB) ## The general promotion methods function *(A::AbstractTriangular, B::AbstractTriangular) - TAB = _inner_type_promotion(A,B) - BB = copy_similar(B, TAB) - lmul!(convert(AbstractArray{TAB}, A), BB) + TAB = _init_eltype(*, eltype(A), eltype(B)) + if TAB <: BlasFloat + lmul!(convert(AbstractArray{TAB}, A), copy_similar(B, TAB)) + else + mul!(similar(B, TAB, size(B)), A, B) + end end for mat in (:AbstractVector, :AbstractMatrix) ### Multiplication with triangle to the left and hence rhs cannot be transposed. @eval function *(A::AbstractTriangular, B::$mat) require_one_based_indexing(B) - TAB = _inner_type_promotion(A,B) - BB = copy_similar(B, TAB) - lmul!(convert(AbstractArray{TAB}, A), BB) + TAB = _init_eltype(*, eltype(A), eltype(B)) + if TAB <: BlasFloat + lmul!(convert(AbstractArray{TAB}, A), copy_similar(B, TAB)) + else + mul!(similar(B, TAB, size(B)), A, B) + end end ### Left division with triangle to the left hence rhs cannot be transposed. No quotients. @eval function \(A::Union{UnitUpperTriangular,UnitLowerTriangular}, B::$mat) require_one_based_indexing(B) - TAB = _inner_type_promotion(A,B) - BB = copy_similar(B, TAB) - ldiv!(convert(AbstractArray{TAB}, A), BB) + TAB = _inner_type_promotion(\, eltype(A), eltype(B)) + if TAB <: BlasFloat + ldiv!(convert(AbstractArray{TAB}, A), copy_similar(B, TAB)) + else + ldiv!(similar(B, TAB, size(B)), A, B) + end end ### Left division with triangle to the left hence rhs cannot be transposed. Quotients. @eval function \(A::Union{UpperTriangular,LowerTriangular}, B::$mat) require_one_based_indexing(B) - TAB = typeof((zero(eltype(A))*zero(eltype(B)) + zero(eltype(A))*zero(eltype(B)))/one(eltype(A))) - BB = copy_similar(B, TAB) - ldiv!(convert(AbstractArray{TAB}, A), BB) + TAB = _init_eltype(\, eltype(A), eltype(B)) + if TAB <: BlasFloat + ldiv!(convert(AbstractArray{TAB}, A), copy_similar(B, TAB)) + else + ldiv!(similar(B, TAB, size(B)), A, B) + end end ### Right division with triangle to the right hence lhs cannot be transposed. No quotients. @eval function /(A::$mat, B::Union{UnitUpperTriangular, UnitLowerTriangular}) require_one_based_indexing(A) - TAB = _inner_type_promotion(A,B) - AA = copy_similar(A, TAB) - rdiv!(AA, convert(AbstractArray{TAB}, B)) + TAB = _inner_type_promotion(/, eltype(A), eltype(B)) + if TAB <: BlasFloat + rdiv!(copy_similar(A, TAB), convert(AbstractArray{TAB}, B)) + else + _rdiv!(similar(A, TAB, size(A)), A, B) + end end ### Right division with triangle to the right hence lhs cannot be transposed. Quotients. @eval function /(A::$mat, B::Union{UpperTriangular,LowerTriangular}) require_one_based_indexing(A) - TAB = typeof((zero(eltype(A))*zero(eltype(B)) + zero(eltype(A))*zero(eltype(B)))/one(eltype(A))) - AA = copy_similar(A, TAB) - rdiv!(AA, convert(AbstractArray{TAB}, B)) + TAB = _init_eltype(/, eltype(A), eltype(B)) + if TAB <: BlasFloat + rdiv!(copy_similar(A, TAB), convert(AbstractArray{TAB}, B)) + else + _rdiv!(similar(A, TAB, size(A)), A, B) + end end end ### Multiplication with triangle to the right and hence lhs cannot be transposed. # Only for AbstractMatrix, hence outside the above loop. function *(A::AbstractMatrix, B::AbstractTriangular) require_one_based_indexing(A) - TAB = _inner_type_promotion(A,B) - AA = copy_similar(A, TAB) - rmul!(AA, convert(AbstractArray{TAB}, B)) + TAB = _init_eltype(*, eltype(A), eltype(B)) + if TAB <: BlasFloat + rmul!(copy_similar(A, TAB), convert(AbstractArray{TAB}, B)) + else + mul!(similar(A, TAB, size(A)), A, B) + end end # ambiguity resolution with definitions in matmul.jl *(v::AdjointAbsVec, A::AbstractTriangular) = adjoint(adjoint(A) * v.parent) *(v::TransposeAbsVec, A::AbstractTriangular) = transpose(transpose(A) * v.parent) +## Some Triangular-Triangular cases. We might want to write tailored methods +## for these cases, but I'm not sure it is worth it. +for f in (:*, :\) + @eval begin + ($f)(A::LowerTriangular, B::LowerTriangular) = + LowerTriangular(@invoke $f(A::LowerTriangular, B::AbstractMatrix)) + ($f)(A::LowerTriangular, B::UnitLowerTriangular) = + LowerTriangular(@invoke $f(A::LowerTriangular, B::AbstractMatrix)) + ($f)(A::UnitLowerTriangular, B::LowerTriangular) = + LowerTriangular(@invoke $f(A::UnitLowerTriangular, B::AbstractMatrix)) + ($f)(A::UnitLowerTriangular, B::UnitLowerTriangular) = + UnitLowerTriangular(@invoke $f(A::UnitLowerTriangular, B::AbstractMatrix)) + ($f)(A::UpperTriangular, B::UpperTriangular) = + UpperTriangular(@invoke $f(A::UpperTriangular, B::AbstractMatrix)) + ($f)(A::UpperTriangular, B::UnitUpperTriangular) = + UpperTriangular(@invoke $f(A::UpperTriangular, B::AbstractMatrix)) + ($f)(A::UnitUpperTriangular, B::UpperTriangular) = + UpperTriangular(@invoke $f(A::UnitUpperTriangular, B::AbstractMatrix)) + ($f)(A::UnitUpperTriangular, B::UnitUpperTriangular) = + UnitUpperTriangular(@invoke $f(A::UnitUpperTriangular, B::AbstractMatrix)) + end +end +(/)(A::LowerTriangular, B::LowerTriangular) = + LowerTriangular(@invoke /(A::AbstractMatrix, B::LowerTriangular)) +(/)(A::LowerTriangular, B::UnitLowerTriangular) = + LowerTriangular(@invoke /(A::AbstractMatrix, B::UnitLowerTriangular)) +(/)(A::UnitLowerTriangular, B::LowerTriangular) = + LowerTriangular(@invoke /(A::AbstractMatrix, B::LowerTriangular)) +(/)(A::UnitLowerTriangular, B::UnitLowerTriangular) = + UnitLowerTriangular(@invoke /(A::AbstractMatrix, B::UnitLowerTriangular)) +(/)(A::UpperTriangular, B::UpperTriangular) = + UpperTriangular(@invoke /(A::AbstractMatrix, B::UpperTriangular)) +(/)(A::UpperTriangular, B::UnitUpperTriangular) = + UpperTriangular(@invoke /(A::AbstractMatrix, B::UnitUpperTriangular)) +(/)(A::UnitUpperTriangular, B::UpperTriangular) = + UpperTriangular(@invoke /(A::AbstractMatrix, B::UpperTriangular)) +(/)(A::UnitUpperTriangular, B::UnitUpperTriangular) = + UnitUpperTriangular(@invoke /(A::AbstractMatrix, B::UnitUpperTriangular)) + # Complex matrix power for upper triangular factor, see: # Higham and Lin, "A Schur-Padé algorithm for fractional powers of a Matrix", # SIAM J. Matrix Anal. & Appl., 32 (3), (2011) 1056–1078. @@ -2059,7 +2114,7 @@ function sqrt(A::UnitUpperTriangular{T}) where T n = checksquare(B) t = typeof(sqrt(zero(T))) R = Matrix{t}(I, n, n) - tt = typeof(zero(t)*zero(t)) + tt = typeof(oneunit(t)*oneunit(t)) half = inv(R[1,1]+R[1,1]) # for general, algebraic cases. PR#20214 @inbounds for j = 1:n for i = j-1:-1:1 @@ -2067,7 +2122,7 @@ function sqrt(A::UnitUpperTriangular{T}) where T @simd for k = i+1:j-1 r -= R[i,k]*R[k,j] end - r==0 || (R[i,j] = half*r) + iszero(r) || (R[i,j] = half*r) end end return UnitUpperTriangular(R) diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index b16bf9c62f0c0..89f2b21a6a973 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -329,36 +329,35 @@ Random.seed!(1) @test norm(x-tx,Inf) <= 4*condT*max(eps()*norm(tx,Inf), eps(promty)*norm(x,Inf)) end @testset "Specialized multiplication/division" begin + getval(x) = x + getval(x::Furlong) = x.val function _bidiagdivmultest(T, x, typemul=T.uplo == 'U' ? UpperTriangular : Matrix, typediv=T.uplo == 'U' ? UpperTriangular : Matrix, typediv2=T.uplo == 'U' ? UpperTriangular : Matrix) TM = Matrix(T) - @test (T*x)::typemul ≈ TM*x #broken=eltype(x) <: Furlong - @test (x*T)::typemul ≈ x*TM #broken=eltype(x) <: Furlong - @test (x\T)::typediv ≈ x\TM #broken=eltype(T) <: Furlong - @test (T/x)::typediv ≈ TM/x #broken=eltype(T) <: Furlong + @test map(getval, (T*x)::typemul) ≈ map(getval, TM*x) + @test map(getval, (x*T)::typemul) ≈ map(getval, x*TM) + @test map(getval, (x\T)::typediv) ≈ map(getval, x\TM) + @test map(getval, (T/x)::typediv) ≈ map(getval, TM/x) if !isa(x, Number) - @test (T\x)::typediv2 ≈ TM\x #broken=eltype(x) <: Furlong - @test (x/T)::typediv2 ≈ x/TM #broken=eltype(x) <: Furlong + @test map(getval, Array((T\x)::typediv2)) ≈ map(getval, Array(TM\x)) + @test map(getval, Array((x/T)::typediv2)) ≈ map(getval, Array(x/TM)) end return nothing end - A = randn(n,n) - d = randn(n) - dl = randn(n-1) - t = T - for t in (T, #=Furlong.(T)=#), (A, d, dl) in ((A, d, dl), #=(Furlong.(A), Furlong.(d), Furlong.(dl))=#) + A = Matrix(T) + for t in (T, Furlong.(T)), (A, dv, ev) in ((A, dv, ev), (Furlong.(A), Furlong.(dv), Furlong.(ev))) _bidiagdivmultest(t, 5, Bidiagonal, Bidiagonal) _bidiagdivmultest(t, 5I, Bidiagonal, Bidiagonal, t.uplo == 'U' ? UpperTriangular : LowerTriangular) - _bidiagdivmultest(t, Diagonal(d), Bidiagonal, Bidiagonal, t.uplo == 'U' ? UpperTriangular : LowerTriangular) + _bidiagdivmultest(t, Diagonal(dv), Bidiagonal, Bidiagonal, t.uplo == 'U' ? UpperTriangular : LowerTriangular) _bidiagdivmultest(t, UpperTriangular(A)) _bidiagdivmultest(t, UnitUpperTriangular(A)) _bidiagdivmultest(t, LowerTriangular(A), t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix) _bidiagdivmultest(t, UnitLowerTriangular(A), t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix) - _bidiagdivmultest(t, Bidiagonal(d, dl, :U), Matrix, Matrix, Matrix) - _bidiagdivmultest(t, Bidiagonal(d, dl, :L), Matrix, Matrix, Matrix) + _bidiagdivmultest(t, Bidiagonal(dv, ev, :U), Matrix, Matrix, Matrix) + _bidiagdivmultest(t, Bidiagonal(dv, ev, :L), Matrix, Matrix, Matrix) end end end diff --git a/stdlib/LinearAlgebra/test/hessenberg.jl b/stdlib/LinearAlgebra/test/hessenberg.jl index a9436b5ba8bdd..91e4e1b1b3df0 100644 --- a/stdlib/LinearAlgebra/test/hessenberg.jl +++ b/stdlib/LinearAlgebra/test/hessenberg.jl @@ -97,10 +97,10 @@ let n = 10 @testset "Multiplication/division" begin for x = (5, 5I, Diagonal(d), Bidiagonal(d,dl,:U), UpperTriangular(A), UnitUpperTriangular(A)) - @test (H*x)::UpperHessenberg == Array(H)*x - @test (x*H)::UpperHessenberg == x*Array(H) - @test H/x == Array(H)/x broken = eltype(H) <: Furlong && x isa UpperTriangular - @test x\H == x\Array(H) broken = eltype(H) <: Furlong && x isa UpperTriangular + @test (H*x)::UpperHessenberg ≈ Array(H)*x + @test (x*H)::UpperHessenberg ≈ x*Array(H) + @test H/x ≈ Array(H)/x# broken = eltype(H) <: Furlong && x isa UpperTriangular + @test x\H ≈ x\Array(H)# broken = eltype(H) <: Furlong && x isa UpperTriangular @test H/x isa UpperHessenberg @test x\H isa UpperHessenberg end @@ -113,13 +113,12 @@ let n = 10 H = UpperHessenberg(Furlong.(Areal)) for A in (A, Furlong.(A)) @testset "Multiplication/division Furlong" begin - for x = (5, 5I, Diagonal(d), Bidiagonal(d,dl,:U)) - @test (H*x)::UpperHessenberg == Array(H)*x - @test (x*H)::UpperHessenberg == x*Array(H) - @test H/x == Array(H)/x broken = eltype(H) <: Furlong && x isa UpperTriangular - @test x\H == x\Array(H) broken = eltype(H) <: Furlong && x isa UpperTriangular - @test H/x isa UpperHessenberg - @test x\H isa UpperHessenberg + for x = (5, 5I, Diagonal(d), Bidiagonal(d,dl,:U), + UpperTriangular(A), UnitUpperTriangular(A)) + @test map(x -> x.val, (H*x)::UpperHessenberg) ≈ map(x -> x.val, Array(H)*x) + @test map(x -> x.val, (x*H)::UpperHessenberg) ≈ map(x -> x.val, x*Array(H)) + @test map(x -> x.val, (H/x)::UpperHessenberg) ≈ map(x -> x.val, Array(H)/x) + @test map(x -> x.val, (x\H)::UpperHessenberg) ≈ map(x -> x.val, x\Array(H)) end x = Bidiagonal(d, dl, :L) @test H*x == Array(H)*x diff --git a/stdlib/LinearAlgebra/test/testgroups b/stdlib/LinearAlgebra/test/testgroups index 8258573969cbb..e281203bf3fa3 100644 --- a/stdlib/LinearAlgebra/test/testgroups +++ b/stdlib/LinearAlgebra/test/testgroups @@ -1,11 +1,11 @@ -addmul triangular +addmul +bidiag matmul dense symmetric diagonal special -bidiag qr cholesky blas diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index 48441e439708f..78fc2d5e0e74c 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -261,11 +261,7 @@ for elty1 in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFlo for eltyb in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}) b1 = convert(Vector{eltyb}, (elty1 <: Complex ? real(A1) : A1)*fill(1., n)) b2 = convert(Vector{eltyb}, (elty1 <: Complex ? real(A1) : A1)*randn(n)) - if elty1 in (BigFloat, Complex{BigFloat}) || eltyb in (BigFloat, Complex{BigFloat}) - @test dot(b1, A1, b2) ≈ dot(A1'b1, b2) atol=sqrt(max(eps(real(float(one(elty1)))),eps(real(float(one(eltyb))))))*n*n - else - @test dot(b1, A1, b2) ≈ dot(A1'b1, b2) atol=sqrt(max(eps(real(float(one(elty1)))),eps(real(float(one(eltyb))))))*n*n - end + @test dot(b1, A1, b2) ≈ dot(A1'b1, b2) atol=sqrt(max(eps(real(float(one(elty1)))),eps(real(float(one(eltyb))))))*n*n end # Binary operations @@ -361,21 +357,29 @@ for elty1 in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFlo if t1 === UnitUpperTriangular && t2 === UnitUpperTriangular @test A1*A2 isa UnitUpperTriangular @test A1/A2 isa UnitUpperTriangular + elty1 == Int && elty2 == Int && @test eltype(A1/A2) == Int @test A1\A2 isa UnitUpperTriangular + elty1 == Int && elty2 == Int && @test eltype(A1\A2) == Int else @test A1*A2 isa UpperTriangular @test A1/A2 isa UpperTriangular + elty1 == Int && elty2 == Int && t2 === UnitUpperTriangular && @test eltype(A1/A2) == Int @test A1\A2 isa UpperTriangular + elty1 == Int && elty2 == Int && t1 === UnitUpperTriangular && @test eltype(A1\A2) == Int end elseif uplo1 === :L && uplo2 === :L if t1 === UnitLowerTriangular && t2 === UnitLowerTriangular @test A1*A2 isa UnitLowerTriangular @test A1/A2 isa UnitLowerTriangular + elty1 == Int && elty2 == Int && @test eltype(A1/A2) == Int @test A1\A2 isa UnitLowerTriangular + elty1 == Int && elty2 == Int && @test eltype(A1\A2) == Int else @test A1*A2 isa LowerTriangular @test A1/A2 isa LowerTriangular + elty1 == Int && elty2 == Int && t2 === UnitLowerTriangular && @test eltype(A1/A2) == Int @test A1\A2 isa LowerTriangular + elty1 == Int && elty2 == Int && t1 === UnitLowerTriangular && @test eltype(A1\A2) == Int end end offsizeA = Matrix{Float64}(I, n+1, n+1) @@ -412,17 +416,15 @@ for elty1 in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFlo debug && println("elty1: $elty1, A1: $t1, B: $eltyB") - if !(eltyB in (BigFloat, Complex{BigFloat})) # rand does not support BigFloat and Complex{BigFloat} as of Dec 2015 - Tri = Tridiagonal(rand(eltyB,n-1),rand(eltyB,n),rand(eltyB,n-1)) - @test lmul!(Tri,copy(A1)) ≈ Tri*Matrix(A1) - Tri = Tridiagonal(rand(eltyB,n-1),rand(eltyB,n),rand(eltyB,n-1)) - C = Matrix{promote_type(elty1,eltyB)}(undef, n, n) - mul!(C, Tri, copy(A1)) - @test C ≈ Tri*Matrix(A1) - Tri = Tridiagonal(rand(eltyB,n-1),rand(eltyB,n),rand(eltyB,n-1)) - mul!(C, copy(A1), Tri) - @test C ≈ Matrix(A1)*Tri - end + Tri = Tridiagonal(rand(eltyB,n-1),rand(eltyB,n),rand(eltyB,n-1)) + @test lmul!(Tri,copy(A1)) ≈ Tri*Matrix(A1) + Tri = Tridiagonal(rand(eltyB,n-1),rand(eltyB,n),rand(eltyB,n-1)) + C = Matrix{promote_type(elty1,eltyB)}(undef, n, n) + mul!(C, Tri, copy(A1)) + @test C ≈ Tri*Matrix(A1) + Tri = Tridiagonal(rand(eltyB,n-1),rand(eltyB,n),rand(eltyB,n-1)) + mul!(C, copy(A1), Tri) + @test C ≈ Matrix(A1)*Tri # Triangular-dense Matrix/vector multiplication @test A1*B[:,1] ≈ Matrix(A1)*B[:,1] @@ -699,8 +701,23 @@ isdefined(Main, :Furlongs) || @eval Main include(joinpath($(BASE_TEST_PATH), "te using .Main.Furlongs LinearAlgebra.sylvester(a::Furlong,b::Furlong,c::Furlong) = -c / (a + b) -let A = UpperTriangular([Furlong(1) Furlong(4); Furlong(0) Furlong(1)]) - @test sqrt(A) == Furlong{1//2}.(UpperTriangular([1 2; 0 1])) +@testset "dimensional correctness" begin + A = UpperTriangular([Furlong(1) Furlong(4); Furlong(0) Furlong(1)]) + @test sqrt(A)::UpperTriangular == Furlong{1//2}.(UpperTriangular([1 2; 0 1])) + @test inv(A)::UpperTriangular == Furlong{-1}.(UpperTriangular([1 -4; 0 1])) + B = UnitUpperTriangular([Furlong(1) Furlong(4); Furlong(0) Furlong(1)]) + @test sqrt(B)::UnitUpperTriangular == Furlong{1//2}.(UpperTriangular([1 2; 0 1])) + @test inv(B)::UnitUpperTriangular == Furlong{-1}.(UpperTriangular([1 -4; 0 1])) + b = [Furlong(5), Furlong(8)] + @test (A \ b)::Vector{<:Furlong{0}} == (B \ b)::Vector{<:Furlong{0}} == Furlong{0}.([-27, 8]) + C = LowerTriangular([Furlong(1) Furlong(0); Furlong(4) Furlong(1)]) + @test sqrt(C)::LowerTriangular == Furlong{1//2}.(LowerTriangular([1 0; 2 1])) + @test inv(C)::LowerTriangular == Furlong{-1}.(LowerTriangular([1 0; -4 1])) + D = UnitLowerTriangular([Furlong(1) Furlong(0); Furlong(4) Furlong(1)]) + @test sqrt(D)::UnitLowerTriangular == Furlong{1//2}.(UnitLowerTriangular([1 0; 2 1])) + @test inv(D)::UnitLowerTriangular == Furlong{-1}.(UnitLowerTriangular([1 0; -4 1])) + b = [Furlong(5), Furlong(8)] + @test (C \ b)::Vector{<:Furlong{0}} == (D \ b)::Vector{<:Furlong{0}} == Furlong{0}.([5, -12]) end isdefined(Main, :ImmutableArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "ImmutableArrays.jl")) diff --git a/test/testhelpers/Furlongs.jl b/test/testhelpers/Furlongs.jl index 15950a9f9ca4b..17970f0b0572e 100644 --- a/test/testhelpers/Furlongs.jl +++ b/test/testhelpers/Furlongs.jl @@ -21,9 +21,10 @@ Furlong{p}(v::Number) where {p} = Furlong{p,typeof(v)}(v) Furlong{p}(x::Furlong{q}) where {p,q} = (typeassert(x, Furlong{p}); Furlong{p,typeof(x.val)}(x.val)) Furlong{p,T}(x::Furlong{q}) where {T,p,q} = (typeassert(x, Furlong{p}); Furlong{p,T}(T(x.val))) -Base.promote_type(::Type{Furlong{p,T}}, ::Type{Furlong{p,S}}) where {p,T,S} = +Base.promote_rule(::Type{Furlong{p,T}}, ::Type{Furlong{p,S}}) where {p,T,S} = Furlong{p,promote_type(T,S)} - +Base.promote_rule(::Type{Furlong{0,T}}, ::Type{S}) where {T,S<:Union{Real,Complex}} = + Furlong{0,promote_type(T,S)} # only Furlong{0} forms a ring and isa Number Base.convert(::Type{T}, y::Number) where {T<:Furlong{0}} = T(y)::T Base.convert(::Type{Furlong}, y::Number) = Furlong{0}(y) @@ -35,11 +36,11 @@ Base.convert(::Type{Furlong}, y::Furlong) = y Base.convert(::Type{Furlong{<:Any,T}}, y::Furlong{p}) where {p,T<:Number} = Furlong{p,T}(y) Base.convert(::Type{T}, y::Furlong) where {T<:Furlong} = T(y)::T -Base.one(x::Furlong{p,T}) where {p,T} = one(T) +Base.one(::Furlong{p,T}) where {p,T} = one(T) Base.one(::Type{Furlong{p,T}}) where {p,T} = one(T) -Base.oneunit(x::Furlong{p,T}) where {p,T} = Furlong{p,T}(one(T)) -Base.oneunit(x::Type{Furlong{p,T}}) where {p,T} = Furlong{p,T}(one(T)) -Base.zero(x::Furlong{p,T}) where {p,T} = Furlong{p,T}(zero(T)) +Base.oneunit(::Furlong{p,T}) where {p,T} = Furlong{p,T}(one(T)) +Base.oneunit(::Type{Furlong{p,T}}) where {p,T} = Furlong{p,T}(one(T)) +Base.zero(::Furlong{p,T}) where {p,T} = Furlong{p,T}(zero(T)) Base.zero(::Type{Furlong{p,T}}) where {p,T} = Furlong{p,T}(zero(T)) Base.iszero(x::Furlong) = iszero(x.val) Base.float(x::Furlong{p}) where {p} = Furlong{p}(float(x.val)) From 6a2e50dee302f4e9405d82db2fc0374b420948a1 Mon Sep 17 00:00:00 2001 From: Steve Kelly Date: Thu, 11 May 2023 22:35:20 -0400 Subject: [PATCH 741/775] add docs for `Base.return_types` (#49744) Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> --- base/reflection.jl | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/base/reflection.jl b/base/reflection.jl index 504ccf9dbea6e..83f3967b98dbe 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1500,6 +1500,39 @@ function code_ircode_by_type( return asts end + +""" + Base.return_types(f::Function, types::DataType=default_tt(f); + world::UInt=get_world_counter(), interp::NativeInterpreter=Core.Compiler.NativeInterpreter(world)) + +Return a list of possible return types for a given function `f` and argument types `types`. +The list corresponds to the results of type inference on all the possible method match +candidates for `f` and `types` (see also [`methods(f, types)`](@ref methods). + +# Example + +```julia +julia> Base.return_types(sum, Tuple{Vector{Int}}) +1-element Vector{Any}: + Int64 + +julia> methods(sum, (Union{Vector{Int},UnitRange{Int}},)) +# 2 methods for generic function "sum" from Base: + [1] sum(r::AbstractRange{<:Real}) + @ range.jl:1396 + [2] sum(a::AbstractArray; dims, kw...) + @ reducedim.jl:996 + +julia> Base.return_types(sum, (Union{Vector{Int},UnitRange{Int}},)) +2-element Vector{Any}: + Int64 # the result of inference on sum(r::AbstractRange{<:Real}) + Int64 # the result of inference on sum(a::AbstractArray; dims, kw...) +``` + +!!! warning + The `return_types` function should not be used from generated functions; + doing so will result in an error. +""" function return_types(@nospecialize(f), @nospecialize(types=default_tt(f)); world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) From 1d58f24716454fd02ef7975889a8fe31ffbeed81 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 12 May 2023 14:55:49 +0900 Subject: [PATCH 742/775] adopt `Core.Compiler.get_max_methods` changes from #46810 (#49781) Since this part of refactoring is generally useful and I would like to utilize it for other PRs that may be merged before #46810. --- base/compiler/abstractinterpretation.jl | 23 ++++-------------- base/compiler/inferencestate.jl | 31 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 0f2011fd07c3c..097eb7a5d098e 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -7,19 +7,6 @@ call_result_unused(sv::InferenceState, currpc::Int) = isexpr(sv.src.code[currpc], :call) && isempty(sv.ssavalue_uses[currpc]) call_result_unused(si::StmtInfo) = !si.used -function get_max_methods(sv::AbsIntState, interp::AbstractInterpreter) - max_methods = ccall(:jl_get_module_max_methods, Cint, (Any,), frame_module(sv)) % Int - return max_methods < 0 ? InferenceParams(interp).max_methods : max_methods -end - -function get_max_methods(@nospecialize(f), sv::AbsIntState, interp::AbstractInterpreter) - if f !== nothing - fmm = typeof(f).name.max_methods - fmm !== UInt8(0) && return Int(fmm) - end - return get_max_methods(sv, interp) -end - function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), arginfo::ArgInfo, si::StmtInfo, @nospecialize(atype), sv::AbsIntState, max_methods::Int) @@ -1488,7 +1475,7 @@ end # do apply(af, fargs...), where af is a function value function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, si::StmtInfo, - sv::AbsIntState, max_methods::Int=get_max_methods(sv, interp)) + sv::AbsIntState, max_methods::Int=get_max_methods(interp, sv)) itft = argtype_by_index(argtypes, 2) aft = argtype_by_index(argtypes, 3) (itft === Bottom || aft === Bottom) && return CallMeta(Bottom, EFFECTS_THROWS, NoCallInfo()) @@ -1936,7 +1923,7 @@ end function abstract_finalizer(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::AbsIntState) if length(argtypes) == 3 finalizer_argvec = Any[argtypes[2], argtypes[3]] - call = abstract_call(interp, ArgInfo(nothing, finalizer_argvec), StmtInfo(false), sv, 1) + call = abstract_call(interp, ArgInfo(nothing, finalizer_argvec), StmtInfo(false), sv, #=max_methods=#1) return CallMeta(Nothing, Effects(), FinalizerInfo(call.info, call.effects)) end return CallMeta(Nothing, Effects(), NoCallInfo()) @@ -1945,7 +1932,7 @@ end # call where the function is known exactly function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), arginfo::ArgInfo, si::StmtInfo, sv::AbsIntState, - max_methods::Int = get_max_methods(f, sv, interp)) + max_methods::Int = get_max_methods(interp, f, sv)) (; fargs, argtypes) = arginfo la = length(argtypes) @@ -2107,10 +2094,10 @@ function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo, si::StmtIn return CallMeta(Any, Effects(), NoCallInfo()) end # non-constant function, but the number of arguments is known and the `f` is not a builtin or intrinsic - max_methods = max_methods === nothing ? get_max_methods(sv, interp) : max_methods + max_methods = max_methods === nothing ? get_max_methods(interp, sv) : max_methods return abstract_call_gf_by_type(interp, nothing, arginfo, si, argtypes_to_type(argtypes), sv, max_methods) end - max_methods = max_methods === nothing ? get_max_methods(f, sv, interp) : max_methods + max_methods = max_methods === nothing ? get_max_methods(interp, f, sv) : max_methods return abstract_call_known(interp, f, arginfo, si, sv, max_methods) end diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 35469b9f29524..97a7ed66ab9b5 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -860,3 +860,34 @@ should_infer_this_call(::AbstractInterpreter, ::IRInterpretationState) = true add_remark!(::AbstractInterpreter, ::InferenceState, remark) = return add_remark!(::AbstractInterpreter, ::IRInterpretationState, remark) = return + +function get_max_methods(interp::AbstractInterpreter, @nospecialize(f), sv::AbsIntState) + fmax = get_max_methods_for_func(f) + fmax !== nothing && return fmax + return get_max_methods(interp, sv) +end +function get_max_methods(interp::AbstractInterpreter, @nospecialize(f)) + fmax = get_max_methods_for_func(f) + fmax !== nothing && return fmax + return get_max_methods(interp) +end +function get_max_methods(interp::AbstractInterpreter, sv::AbsIntState) + mmax = get_max_methods_for_module(sv) + mmax !== nothing && return mmax + return get_max_methods(interp) +end +get_max_methods(interp::AbstractInterpreter) = InferenceParams(interp).max_methods + +function get_max_methods_for_func(@nospecialize(f)) + if f !== nothing + fmm = typeof(f).name.max_methods + fmm !== UInt8(0) && return Int(fmm) + end + return nothing +end +get_max_methods_for_module(sv::AbsIntState) = get_max_methods_for_module(frame_module(sv)) +function get_max_methods_for_module(mod::Module) + max_methods = ccall(:jl_get_module_max_methods, Cint, (Any,), mod) % Int + max_methods < 0 && return nothing + return max_methods +end From 9002d1665cd849858fc3150361f5b33aacfaf688 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 12 May 2023 15:35:06 +0900 Subject: [PATCH 743/775] abstractarray: fix `append!(::AbstractVector, ...)` interface (#49754) JuliaLang/julia#47154 mistakenly added `@_safeindex` macro on the `_append!(a::AbstractVector, ::Union{HasLength,HasShape}, iter)` method, although `@_safeindex` is only valid for builtin vectors i.e. `Vector`. This commit adds `isa` check so that `@_safeindex` is only applied to builtin vectors. The `isa` check should be removed at compile time, so it should not affect the runtime performance. closes #49748 --- base/array.jl | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/base/array.jl b/base/array.jl index 0a5451bac5b74..68e3e38992731 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1167,6 +1167,8 @@ See [`sizehint!`](@ref) for notes about the performance model. See also [`vcat`](@ref) for vectors, [`union!`](@ref) for sets, and [`prepend!`](@ref) and [`pushfirst!`](@ref) for the opposite order. """ +function append! end + function append!(a::Vector, items::AbstractVector) itemindices = eachindex(items) n = length(itemindices) @@ -1180,18 +1182,21 @@ push!(a::AbstractVector, iter...) = append!(a, iter) append!(a::AbstractVector, iter...) = foldl(append!, iter, init=a) -function _append!(a, ::Union{HasLength,HasShape}, iter) +function _append!(a::AbstractVector, ::Union{HasLength,HasShape}, iter) @_terminates_locally_meta n = length(a) i = lastindex(a) resize!(a, n+Int(length(iter))::Int) - @_safeindex for (i, item) in zip(i+1:lastindex(a), iter) - a[i] = item + for (i, item) in zip(i+1:lastindex(a), iter) + if isa(a, Vector) # give better effects for builtin vectors + @_safeindex a[i] = item + else + a[i] = item + end end a end - -function _append!(a, ::IteratorSize, iter) +function _append!(a::AbstractVector, ::IteratorSize, iter) for item in iter push!(a, item) end @@ -1246,7 +1251,7 @@ pushfirst!(a::Vector, iter...) = prepend!(a, iter) prepend!(a::AbstractVector, iter...) = foldr((v, a) -> prepend!(a, v), iter, init=a) -function _prepend!(a, ::Union{HasLength,HasShape}, iter) +function _prepend!(a::Vector, ::Union{HasLength,HasShape}, iter) @_terminates_locally_meta require_one_based_indexing(a) n = length(iter) @@ -1257,7 +1262,7 @@ function _prepend!(a, ::Union{HasLength,HasShape}, iter) end a end -function _prepend!(a, ::IteratorSize, iter) +function _prepend!(a::Vector, ::IteratorSize, iter) n = 0 for item in iter n += 1 From 021015d42032be262522024b086327920f3638c5 Mon Sep 17 00:00:00 2001 From: Lasse Peters Date: Fri, 12 May 2023 10:13:47 +0200 Subject: [PATCH 744/775] Remove 1.9 package extension news item from NEWS.md (#49786) This feature was already shipped with 1.9 so it probably shouldn't be mentioned a second time in the 1.10 NEWS.md. --- NEWS.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index 3514ad955c948..ab4f1e9025555 100644 --- a/NEWS.md +++ b/NEWS.md @@ -61,10 +61,6 @@ Standard library changes #### Package Manager -* "Package Extensions": support for loading a piece of code based on other - packages being loaded in the Julia session. - This has similar applications as the Requires.jl package but also - supports precompilation and setting compatibility. * `Pkg.precompile` now accepts `timing` as a keyword argument which displays per package timing information for precompilation (e.g. `Pkg.precompile(timing=true)`) #### LinearAlgebra From 67331978b0d68f1142cd58f76e042208ee64e461 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 12 May 2023 11:13:17 +0200 Subject: [PATCH 745/775] Artifacts: pull out a recursive function from a closure to a stand alone function (#49755) --- stdlib/Artifacts/src/Artifacts.jl | 37 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/stdlib/Artifacts/src/Artifacts.jl b/stdlib/Artifacts/src/Artifacts.jl index a9554c95f3151..47812fb993428 100644 --- a/stdlib/Artifacts/src/Artifacts.jl +++ b/stdlib/Artifacts/src/Artifacts.jl @@ -56,6 +56,23 @@ function artifacts_dirs(args...) end end +# Recursive function, let's not make this a closure because it then has to +# be boxed. +function parse_mapping(mapping::String, name::String, override_file::String) + if !isabspath(mapping) && !isempty(mapping) + mapping = tryparse(Base.SHA1, mapping) + if mapping === nothing + @error("Invalid override in '$(override_file)': entry '$(name)' must map to an absolute path or SHA1 hash!") + end + end + return mapping +end +function parse_mapping(mapping::Dict, name::String, override_file::String) + return Dict(k => parse_mapping(v, name, override_file) for (k, v) in mapping) +end +# Fallthrough for invalid Overrides.toml files +parse_mapping(mapping, name::String, override_file::String) = nothing + """ ARTIFACT_OVERRIDES @@ -103,24 +120,9 @@ function load_overrides(;force::Bool = false)::Dict{Symbol, Any} # Load the toml file depot_override_dict = parse_toml(override_file) - function parse_mapping(mapping::String, name::String) - if !isabspath(mapping) && !isempty(mapping) - mapping = tryparse(Base.SHA1, mapping) - if mapping === nothing - @error("Invalid override in '$(override_file)': entry '$(name)' must map to an absolute path or SHA1 hash!") - end - end - return mapping - end - function parse_mapping(mapping::Dict, name::String) - return Dict(k => parse_mapping(v, name) for (k, v) in mapping) - end - # Fallthrough for invalid Overrides.toml files - parse_mapping(mapping, name::String) = nothing - for (k, mapping) in depot_override_dict # First, parse the mapping. Is it an absolute path, a valid SHA1-hash, or neither? - mapping = parse_mapping(mapping, k) + mapping = parse_mapping(mapping, k, override_file) if mapping === nothing @error("Invalid override in '$(override_file)': failed to parse entry `$(k)`") continue @@ -743,5 +745,8 @@ artifact_slash_lookup(name::AbstractString, artifact_dict::Dict, artifacts_toml: precompile(load_artifacts_toml, (String,)) precompile(NamedTuple{(:pkg_uuid,)}, (Tuple{Base.UUID},)) precompile(Core.kwfunc(load_artifacts_toml), (NamedTuple{(:pkg_uuid,), Tuple{Base.UUID}}, typeof(load_artifacts_toml), String)) +precompile(parse_mapping, (String, String, String)) +precompile(parse_mapping, (Dict{String, Any}, String, String)) + end # module Artifacts From ae6484d46839067d26143eba32ae6bcb1873766b Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Fri, 12 May 2023 07:59:29 -0400 Subject: [PATCH 746/775] Update toolchain requirements and LLVM build docs (#49742) --- doc/src/devdocs/build/build.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/src/devdocs/build/build.md b/doc/src/devdocs/build/build.md index 6d5d4a54c8d64..ad3871c2e70f0 100644 --- a/doc/src/devdocs/build/build.md +++ b/doc/src/devdocs/build/build.md @@ -144,7 +144,7 @@ Notes for various architectures: Building Julia requires that the following software be installed: - **[GNU make]** — building dependencies. -- **[gcc & g++][gcc]** (>= 5.1) or **[Clang][clang]** (>= 3.5, >= 6.0 for Apple Clang) — compiling and linking C, C++. +- **[gcc & g++][gcc]** (>= 7.1) or **[Clang][clang]** (>= 5.0, >= 9.3 for Apple Clang) — compiling and linking C, C++. - **[libatomic][gcc]** — provided by **[gcc]** and needed to support atomic operations. - **[python]** (>=2.7) — needed to build LLVM. - **[gfortran]** — compiling and linking Fortran libraries. @@ -169,7 +169,7 @@ repository) and then compiled from source the first time you run `make`. The specific version numbers of these libraries that Julia uses are listed in [`deps/$(libname).version`](https://github.com/JuliaLang/julia/blob/master/deps/): -- **[LLVM]** (14.0 + [patches](https://github.com/JuliaLang/llvm-project)) — compiler infrastructure (see [note below](#llvm)). +- **[LLVM]** (15.0 + [patches](https://github.com/JuliaLang/llvm-project/tree/julia-release/15.x)) — compiler infrastructure (see [note below](#llvm)). - **[FemtoLisp]** — packaged with Julia source, and used to implement the compiler front-end. - **[libuv]** (custom fork) — portable, high-performance event-based I/O library. - **[OpenLibm]** — portable libm library containing elementary math functions. @@ -238,7 +238,7 @@ The most complicated dependency is LLVM, for which we require additional patches For packaging Julia with LLVM, we recommend either: - bundling a Julia-only LLVM library inside the Julia package, or - adding the patches to the LLVM package of the distribution. - * A complete list of patches is available in `deps/llvm.mk`, and the patches themselves are in `deps/patches/`. + * A complete list of patches is available in on [Github](https://github.com/JuliaLang/llvm-project) see the `julia-release/15.x` branch. * The only Julia-specific patch is the lib renaming (`llvm-symver-jlprefix.patch`), which should _not_ be applied to a system LLVM. * The remaining patches are all upstream bug fixes, and have been contributed into upstream LLVM. From 2f6941ff7772f32b86130c04952f534e45a887e4 Mon Sep 17 00:00:00 2001 From: Christian Guinard Date: Fri, 12 May 2023 08:59:41 -0300 Subject: [PATCH 747/775] Update stable version in README.md to 1.9.0 (#49767) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a95ee05b2bab2..26fbb21a8a6a7 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ and then use the command prompt to change into the resulting julia directory. By Julia. However, most users should use the [most recent stable version](https://github.com/JuliaLang/julia/releases) of Julia. You can get this version by running: - git checkout v1.8.5 + git checkout v1.9.0 To build the `julia` executable, run `make` from within the julia directory. From 46b8a3587e7e61319f11b866e41b6ad043fa9a56 Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Fri, 12 May 2023 09:00:21 -0300 Subject: [PATCH 748/775] remove duplicate gc_try_claim_and_push (#49780) --- src/gc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gc.c b/src/gc.c index f421ce4363c67..4925ed91ea179 100644 --- a/src/gc.c +++ b/src/gc.c @@ -1909,7 +1909,6 @@ STATIC_INLINE jl_value_t *gc_mark_obj16(jl_ptls_t ptls, char *obj16_parent, uint if (new_obj != NULL) { verify_parent2("object", obj16_parent, slot, "field(%d)", gc_slot_to_fieldidx(obj16_parent, slot, (jl_datatype_t*)jl_typeof(obj16_parent))); - gc_try_claim_and_push(mq, new_obj, &nptr); if (obj16_begin + 1 != obj16_end) { gc_try_claim_and_push(mq, new_obj, &nptr); } From 7bd3977163572fa71366d58fe467f7c72e8b12e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1ll=20Haraldsson?= Date: Fri, 12 May 2023 12:00:44 +0000 Subject: [PATCH 749/775] Update NEWS.md for grammar (#49759) [skip ci] --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index ab4f1e9025555..5c42c469e4051 100644 --- a/NEWS.md +++ b/NEWS.md @@ -11,7 +11,7 @@ Language changes ---------------- * When a task forks a child, the parent task's task-local RNG (random number generator) is no longer affected. The seeding of child based on the parent task also takes a more disciplined approach to collision resistance, using a design based on the SplitMix and DotMix splittable RNG schemes ([#49110]). -* A new morespecific rule for methods resolves ambiguities containing Union{} in favor of +* A new more-specific rule for methods resolves ambiguities containing Union{} in favor of the method defined explicitly to handle the Union{} argument. This makes it possible to define methods to explicitly handle Union{} without the ambiguities that commonly would result previously. This also lets the runtime optimize certain method lookups in a way From e365e5709a2856cd9876ab3ba05b5aabebfd938f Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Thu, 11 May 2023 21:45:15 -0400 Subject: [PATCH 750/775] Add LibTracyClient checksums --- deps/checksums/libtracyclient | 66 ++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/deps/checksums/libtracyclient b/deps/checksums/libtracyclient index 55a436b059c42..19b7b26c5461e 100644 --- a/deps/checksums/libtracyclient +++ b/deps/checksums/libtracyclient @@ -1,32 +1,34 @@ -LibTracyClient.v0.9.1+1.aarch64-apple-darwin.tar.gz/md5/540617535443c918d42415d7e775456d -LibTracyClient.v0.9.1+1.aarch64-apple-darwin.tar.gz/sha512/5dc245327900a26f20692c76c6a3043a07ee88010b814bdded79460fd77cd587b69448b074a1afc931290ef7f445771aec71a003d6e425d42c75d2cc72bdf846 -LibTracyClient.v0.9.1+1.aarch64-linux-gnu.tar.gz/md5/d2a09ad722a1f15090dd0ae6ce9c37c7 -LibTracyClient.v0.9.1+1.aarch64-linux-gnu.tar.gz/sha512/b5e6f44bb4690226dd4176a43824193c7e1a7873cf75c2e261b6cb0a614aad172c0265b6aa89849328133de9894af94a4a38b4362ec8d706d03a0cad4fd1171a -LibTracyClient.v0.9.1+1.aarch64-linux-musl.tar.gz/md5/eccc851b7346590d2636ff585e4b1f55 -LibTracyClient.v0.9.1+1.aarch64-linux-musl.tar.gz/sha512/214dd6d7ce70ce11748091143a2e89dfc6b85c62424d971eb973b1126ee3da98d8285c2f5557c7b62523f76e513692947b5ef0f046bdf183da3ddd38554b4a97 -LibTracyClient.v0.9.1+1.armv6l-linux-gnueabihf.tar.gz/md5/200b940fb4b6c7f8cb6c621ae4bab347 -LibTracyClient.v0.9.1+1.armv6l-linux-gnueabihf.tar.gz/sha512/1213b716ed3680bb8b1a682099ef325a257e29811498a731553c4d6fc8f93831038d211720da1985f72f42c6409ea5e2aa262493557abecb6587d7db69bde737 -LibTracyClient.v0.9.1+1.armv6l-linux-musleabihf.tar.gz/md5/44dddf9ef55cd9d222a16eff2b2e14e7 -LibTracyClient.v0.9.1+1.armv6l-linux-musleabihf.tar.gz/sha512/ba887e97366e9ac9dbc43864b3d8cd8cdf2db571fb198593f2ae66790fb9cd5a12d4c29088a65bc103939ec029fa426925a0990c0a2b1441fece974b3dabce6c -LibTracyClient.v0.9.1+1.armv7l-linux-gnueabihf.tar.gz/md5/286bbb5c258fcd38224ff03a691cf474 -LibTracyClient.v0.9.1+1.armv7l-linux-gnueabihf.tar.gz/sha512/26e85ec00a794901d412bb54d1ea1cbd9c7972e2dcc6fbcad12d520088c4d6d86a8ff7398cff14e60741abb30028fcda39515b2a1ae1a225a3192e9e956a8641 -LibTracyClient.v0.9.1+1.armv7l-linux-musleabihf.tar.gz/md5/36b317542174c07c4e85fdffcf5e34c7 -LibTracyClient.v0.9.1+1.armv7l-linux-musleabihf.tar.gz/sha512/d9e28e6ebb4537ae4de75ae03bb28d170c7dc92731f6a8d6431ce2e5ee5ad717a04990a42b57898247a5059b8e0d93b5812d6ffc66c94c5571adec2ecfe0555d -LibTracyClient.v0.9.1+1.i686-linux-gnu.tar.gz/md5/1990c1f0701a3c7853fa57e54d214465 -LibTracyClient.v0.9.1+1.i686-linux-gnu.tar.gz/sha512/f899f109ad77f2b1964e94242b0a601128b063140190105fd44e43782036ef0134cdc2b6cb1faaf5b7c0742f4a168c7b7870f18d2d18b19fc94d8f269f3027d1 -LibTracyClient.v0.9.1+1.i686-linux-musl.tar.gz/md5/eeee122d2166e8a251bcee40d66daede -LibTracyClient.v0.9.1+1.i686-linux-musl.tar.gz/sha512/fcf0de849b8533065e61d5e1528cc1e882d2734c488a030728ec4915651bb2bd049536d9d76ecce3063647d0cd20e10033beb3d8de82e06c9c73e9eb41b12b03 -LibTracyClient.v0.9.1+1.i686-w64-mingw32.tar.gz/md5/0f42ad75bb75084e129b0e6fe5e86196 -LibTracyClient.v0.9.1+1.i686-w64-mingw32.tar.gz/sha512/28821145c8d3d7d8dc3e1db883478e53b4eacafa5f4deae965057ad6466c683de6bd9265a92f1d63ab587107fbb374f7167ff4be70c5fbdf09db9b57fb619b3e -LibTracyClient.v0.9.1+1.powerpc64le-linux-gnu.tar.gz/md5/1a2243ac3a4efa224da1eaace7aa9278 -LibTracyClient.v0.9.1+1.powerpc64le-linux-gnu.tar.gz/sha512/acd17f12afb523348de56f7fa84497468ec5f2f76d9622180e72bded7eb4eda4033e28ed20cb25c7c8049b086c994c10a6d97efae06a661ce7d0d65e5c8bbfd5 -LibTracyClient.v0.9.1+1.x86_64-apple-darwin.tar.gz/md5/34a75343f9aed8e6db252cc043b26b09 -LibTracyClient.v0.9.1+1.x86_64-apple-darwin.tar.gz/sha512/2eaf4e5ef1959110cecaf467bdc28e92d45862c4d83315fccafbf3ab4e498918f4d715b0303be4e563ac7ddb88c9d9cec71351183c5ec0055759a86341473232 -LibTracyClient.v0.9.1+1.x86_64-linux-gnu.tar.gz/md5/d6fcd4255ab1363412e85497770ab522 -LibTracyClient.v0.9.1+1.x86_64-linux-gnu.tar.gz/sha512/a879afb13a35d18c8ed5593cf83d48ac228c45c246fa013f5f067fa216b61d2dc5b8adfc4ced6043c331b982d050522b228a5792c707b9deff2fb65b37aa66ea -LibTracyClient.v0.9.1+1.x86_64-linux-musl.tar.gz/md5/0490919c558c5ae8c833934426e0dda4 -LibTracyClient.v0.9.1+1.x86_64-linux-musl.tar.gz/sha512/b37637e8530ad9b3cb58732bb35d9634aadaeb5a83815f602d5804d4fff1499ea49ce72300b03036ecd642d0dbd8f86d475b637673bc8d400f70e4cb4cf865eb -LibTracyClient.v0.9.1+1.x86_64-unknown-freebsd.tar.gz/md5/985a19b60f44349870c304c7a140cad8 -LibTracyClient.v0.9.1+1.x86_64-unknown-freebsd.tar.gz/sha512/514f01127dcc641ab4b66fac49c0470f6277bff37fbd82084482d3db72e1964d85655ca8aa135dbe08265d711d857ed861eba038a072f8e4fcffeefe3b11808d -LibTracyClient.v0.9.1+1.x86_64-w64-mingw32.tar.gz/md5/a777f0997a238c3f28a362e2998d92a2 -LibTracyClient.v0.9.1+1.x86_64-w64-mingw32.tar.gz/sha512/3aa49b49f696792d20265e9947b9a0dc4b888a482617513252176b0ac59db9069c965f01a8c1c253f73050eab3344d9d0b4c26a826ff9bfa92e36eb42814444d +LibTracyClient.v0.9.1+2.aarch64-apple-darwin.tar.gz/md5/08881ffc565e099903e2e972a7f7c002 +LibTracyClient.v0.9.1+2.aarch64-apple-darwin.tar.gz/sha512/a9dcc7f9ed7565a769dd1080513eec7439cd7b03d68d48f570ac3f396769ef0a7f9b07446045ce6536b7e67860096eb150670256c311c0a77ac1a271dc4b4422 +LibTracyClient.v0.9.1+2.aarch64-linux-gnu.tar.gz/md5/d6a8dbc7cf871f772f848a5e515e6502 +LibTracyClient.v0.9.1+2.aarch64-linux-gnu.tar.gz/sha512/cb9b3065f581a956d318d71a94216ca0e57599262a12a25bc2e6fa0234505fed5a9cad9c2eb7ad30d7ffe9c4ee3d26d9f645887d3f7180d69d3bf1d0745b4f22 +LibTracyClient.v0.9.1+2.aarch64-linux-musl.tar.gz/md5/0d74193e3571fbd80eb7d9e884b47e53 +LibTracyClient.v0.9.1+2.aarch64-linux-musl.tar.gz/sha512/18821911a96129486cb12726018b33fde1da345228623b7f326b92ccfcbbbb2349d79a35e6fa7cb4b6cf9283a860e8ac44c40d6b54a4dc1ea4373b869491b6d6 +LibTracyClient.v0.9.1+2.armv6l-linux-gnueabihf.tar.gz/md5/6111f3b3c696d9d07139e137c2ec1d08 +LibTracyClient.v0.9.1+2.armv6l-linux-gnueabihf.tar.gz/sha512/135139c221cb2d4d6000bd1a3771bd095e93487c7c649ebdf760ff5cb03f6ae003c33c2a36a52bbdf70e4c349195f78a97bc963336a36f33fcdeee33e4fc1eb7 +LibTracyClient.v0.9.1+2.armv6l-linux-musleabihf.tar.gz/md5/5b3154cc849b04bb3523f04fa4481b83 +LibTracyClient.v0.9.1+2.armv6l-linux-musleabihf.tar.gz/sha512/7f62a546c7cdbe3bb6a0a446980371ff340d5f530907a2434eba2a14bbfede8c740a763b0c68a252d7a3e357d9d933bcc6313919cd9bfa385715bc833be56cce +LibTracyClient.v0.9.1+2.armv7l-linux-gnueabihf.tar.gz/md5/f6952d495c5b699226260e065cf2703c +LibTracyClient.v0.9.1+2.armv7l-linux-gnueabihf.tar.gz/sha512/5fdad7f8ce3a03ce05adb3deb6bc8347aefcc8a7fe0a30e0f7684fe233eb8520aca138e0b8a6cc5555a1f2316a6e36bca32cb5de37f2aac5c5deddfaeb0f8570 +LibTracyClient.v0.9.1+2.armv7l-linux-musleabihf.tar.gz/md5/84924c2e32b39ed580b553a968e97360 +LibTracyClient.v0.9.1+2.armv7l-linux-musleabihf.tar.gz/sha512/2b81834b91472eb9897abefbe77e931782e8c14eaf7193f22fce82024610906b6e96122610edfab29a9c844581cc4ee9124e330af9eacd97fb8759c1de421472 +LibTracyClient.v0.9.1+2.i686-linux-gnu.tar.gz/md5/9f243a9d10cd928d45436f634d020c27 +LibTracyClient.v0.9.1+2.i686-linux-gnu.tar.gz/sha512/c9512030d83f32942c7fefd598bfa597ce758f39d11bc9551fbf565a418a3000d23f899f1e9411cddebb3642efef8cccfa3cf3f629bcc11fcf50585e1a80549e +LibTracyClient.v0.9.1+2.i686-linux-musl.tar.gz/md5/4aebc58f4c8101640d9e450338a4e12a +LibTracyClient.v0.9.1+2.i686-linux-musl.tar.gz/sha512/2085b7c0658bb39dce9a9b511c209a348916ed8e50ed0d51eb22f7eac167b890a87d357e433e12eaf7034c15842c8d2893a0c128443c4f25fa90fd5ca83e256d +LibTracyClient.v0.9.1+2.i686-w64-mingw32.tar.gz/md5/dc6f911f5cdd2789ef9f13a1a9882243 +LibTracyClient.v0.9.1+2.i686-w64-mingw32.tar.gz/sha512/57894c759db949dc669e23b7d5e015942630328a3dc754185a0f6bae95a66f0c3e65e365317bae95f3a216f4dcab681203e64dc8c9a0b5478cc9e27c9dab2e56 +LibTracyClient.v0.9.1+2.powerpc64le-linux-gnu.tar.gz/md5/a7429f900f7f0a14fa355186d99a24e1 +LibTracyClient.v0.9.1+2.powerpc64le-linux-gnu.tar.gz/sha512/e37ff8e8de9b74367b9f0d6fe49d983900529caf9c2c55d5ace305d5896c2de6589380247dc85017d959901864d4a163fe110e6d860340d949c6ea4dec50f47c +LibTracyClient.v0.9.1+2.x86_64-apple-darwin.tar.gz/md5/b037ea1027e6466d5dd9c0fb41f65ded +LibTracyClient.v0.9.1+2.x86_64-apple-darwin.tar.gz/sha512/81e2d00bd8eaa1cbcbd5c0ee4552028ccedffcc072beea3dc08ac3181677da93406e8dfc581a78434175fa5bb861df06848dd3012f8adbbb6dc72efcbb5094a0 +LibTracyClient.v0.9.1+2.x86_64-linux-gnu.tar.gz/md5/cfbe122083aeeea6bd7ddc4591b1cb53 +LibTracyClient.v0.9.1+2.x86_64-linux-gnu.tar.gz/sha512/e0418a0b50d64990d6f1b80dfe65e2360817211e1225c4d8d9fc9c871a95bbb62c2601c617adf1d55305518f5ba1dd05baee82f6934d0011269fab21b89336b9 +LibTracyClient.v0.9.1+2.x86_64-linux-musl.tar.gz/md5/f152ba78f2461fec711144ae66380c34 +LibTracyClient.v0.9.1+2.x86_64-linux-musl.tar.gz/sha512/f59f837d2beb4df4d3d65352a8c46261bb5a92ae88a62e2d1bfb7293184e02be982fbefe20736456719055e718a26003984224d0d74a0a6244dcc59e0d350556 +LibTracyClient.v0.9.1+2.x86_64-unknown-freebsd.tar.gz/md5/83c7b3d9438dd04d25573a386bc5c3df +LibTracyClient.v0.9.1+2.x86_64-unknown-freebsd.tar.gz/sha512/f22d0d4f4171067bd1f56bb63dba801e262d0ed4809538dae907296d1a12817954ad759cdc9e61f710fff5802fb7371d8283d6df52c9e8faf6b43c713c23e371 +LibTracyClient.v0.9.1+2.x86_64-w64-mingw32.tar.gz/md5/83f3db14b65b8e9942c754bcdb430060 +LibTracyClient.v0.9.1+2.x86_64-w64-mingw32.tar.gz/sha512/8acdd1d407ae927925f33eb75891684d6687e3577d5f8ac77e738daedc8145462b1f044e31edd9e2db4507673a0abebcea19e171833042cbbe5a135b0c0435cb +libtracyclient-897aec5b062664d2485f4f9a213715d2e527e0ca.tar.gz/md5/51986311723ba88ac305ad2c1e3e86c6 +libtracyclient-897aec5b062664d2485f4f9a213715d2e527e0ca.tar.gz/sha512/f92c5bd71fd3e933f03e3535c0668a9afddc7ea19531aaee11b22bde09c57cc8a555f7f17f489d4221645fb6d73ecf9299d5bb11949d7529987beec3e7d91763 From c6fc12c9eee6aa4fb1f584840e5a0f988009d82f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 12 May 2023 10:02:12 -0400 Subject: [PATCH 751/775] fix build failure with dyld4 deadlock workaround (#49776) Accidentally missed in #49740 Fixes #49773 --- src/mach_dyld_atfork.tbd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mach_dyld_atfork.tbd b/src/mach_dyld_atfork.tbd index 9a5d18099dbcf..c2cda4417ec38 100644 --- a/src/mach_dyld_atfork.tbd +++ b/src/mach_dyld_atfork.tbd @@ -21,5 +21,6 @@ install-name: '/usr/lib/libSystem.B.dylib' exports: - targets: [ arm64-macos, arm64e-macos, x86_64-macos, x86_64-maccatalyst, arm64-maccatalyst, arm64e-maccatalyst ] - symbols: [ __dyld_atfork_parent, __dyld_atfork_prepare ] + symbols: [ __dyld_atfork_parent, __dyld_atfork_prepare, + __dyld_dlopen_atfork_parent, __dyld_dlopen_atfork_prepare ] ... From d55314c05e35259e185863e063d1bacc50de3e4f Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 12 May 2023 20:54:17 +0200 Subject: [PATCH 752/775] allow loading extensions when a trigger is loaded from below the parent's load path (#49701) also allow loading extensions of the active project --- base/loading.jl | 91 +++++++++++++------ test/loading.jl | 18 +++- .../Extensions/EnvWithDeps/Manifest.toml | 21 +++++ .../Extensions/EnvWithDeps/Project.toml | 4 + 4 files changed, 103 insertions(+), 31 deletions(-) create mode 100644 test/project/Extensions/EnvWithDeps/Manifest.toml create mode 100644 test/project/Extensions/EnvWithDeps/Project.toml diff --git a/base/loading.jl b/base/loading.jl index cf23fdb887f67..36a92855d2265 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -413,7 +413,9 @@ function locate_package_env(pkg::PkgId, stopenv::Union{String, Nothing}=nothing) @goto done end end - stopenv == env && @goto done + if !(loading_extension || precompiling_extension) + stopenv == env && @goto done + end end else for env in load_path() @@ -428,7 +430,9 @@ function locate_package_env(pkg::PkgId, stopenv::Union{String, Nothing}=nothing) path = entry_path(path, pkg.name) @goto done end - stopenv == env && break + if !(loading_extension || precompiling_extension) + stopenv == env && break + end end # Allow loading of stdlibs if the name/uuid are given # e.g. if they have been explicitly added to the project/manifest @@ -619,6 +623,24 @@ function manifest_deps_get(env::String, where::PkgId, name::String)::Union{Nothi pkg_uuid = explicit_project_deps_get(project_file, name) return PkgId(pkg_uuid, name) end + d = parsed_toml(project_file) + exts = get(d, "extensions", nothing)::Union{Dict{String, Any}, Nothing} + if exts !== nothing + # Check if `where` is an extension of the project + if where.name in keys(exts) && where.uuid == uuid5(proj.uuid, where.name) + # Extensions can load weak deps... + weakdeps = get(d, "weakdeps", nothing)::Union{Dict{String, Any}, Nothing} + if weakdeps !== nothing + wuuid = get(weakdeps, name, nothing)::Union{String, Nothing} + if wuuid !== nothing + return PkgId(UUID(wuuid), name) + end + end + # ... and they can load same deps as the project itself + mby_uuid = explicit_project_deps_get(project_file, name) + mby_uuid === nothing || return PkgId(mby_uuid, name) + end + end # look for manifest file and `where` stanza return explicit_manifest_deps_get(project_file, where, name) elseif project_file @@ -636,6 +658,8 @@ function manifest_uuid_path(env::String, pkg::PkgId)::Union{Nothing,String,Missi # if `pkg` matches the project, return the project itself return project_file_path(project_file) end + mby_ext = project_file_ext_path(project_file, pkg.name) + mby_ext === nothing || return mby_ext # look for manifest file and `where` stanza return explicit_manifest_uuid_path(project_file, pkg) elseif project_file @@ -645,6 +669,25 @@ function manifest_uuid_path(env::String, pkg::PkgId)::Union{Nothing,String,Missi return nothing end + +function find_ext_path(project_path::String, extname::String) + extfiledir = joinpath(project_path, "ext", extname, extname * ".jl") + isfile(extfiledir) && return extfiledir + return joinpath(project_path, "ext", extname * ".jl") +end + +function project_file_ext_path(project_file::String, name::String) + d = parsed_toml(project_file) + p = project_file_path(project_file) + exts = get(d, "extensions", nothing)::Union{Dict{String, Any}, Nothing} + if exts !== nothing + if name in keys(exts) + return find_ext_path(p, name) + end + end + return nothing +end + # find project file's top-level UUID entry (or nothing) function project_file_name_uuid(project_file::String, name::String)::PkgId d = parsed_toml(project_file) @@ -876,9 +919,7 @@ function explicit_manifest_uuid_path(project_file::String, pkg::PkgId)::Union{No error("failed to find source of parent package: \"$name\"") end p = normpath(dirname(parent_path), "..") - extfiledir = joinpath(p, "ext", pkg.name, pkg.name * ".jl") - isfile(extfiledir) && return extfiledir - return joinpath(p, "ext", pkg.name * ".jl") + return find_ext_path(p, pkg.name) end end end @@ -1160,6 +1201,18 @@ end function insert_extension_triggers(env::String, pkg::PkgId)::Union{Nothing,Missing} project_file = env_project_file(env) if project_file isa String + # Look in project for extensions to insert + proj_pkg = project_file_name_uuid(project_file, pkg.name) + if pkg == proj_pkg + d_proj = parsed_toml(project_file) + weakdeps = get(d_proj, "weakdeps", nothing)::Union{Nothing, Vector{String}, Dict{String,Any}} + extensions = get(d_proj, "extensions", nothing)::Union{Nothing, Dict{String, Any}} + extensions === nothing && return + weakdeps === nothing && return + return _insert_extension_triggers(pkg, extensions, weakdeps) + end + + # Now look in manifest manifest_file = project_file_manifest_path(project_file) manifest_file === nothing && return d = get_deps(parsed_toml(manifest_file)) @@ -1224,6 +1277,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, <:An end loading_extension::Bool = false +precompiling_extension::Bool = false function run_extension_callbacks(extid::ExtensionId) assert_havelock(require_lock) succeeded = try @@ -1251,30 +1305,8 @@ function run_extension_callbacks(pkgid::PkgId) extids === nothing && return for extid in extids if extid.ntriggers > 0 - # It is possible that pkgid was loaded in an environment - # below the one of the parent. This will cause a load failure when the - # pkg ext tries to load the triggers. Therefore, check this first - # before loading the pkg ext. - pkgenv = identify_package_env(extid.id, pkgid.name) - ext_not_allowed_load = false - if pkgenv === nothing - ext_not_allowed_load = true - else - pkg, env = pkgenv - path = locate_package(pkg, env) - if path === nothing - ext_not_allowed_load = true - end - end - if ext_not_allowed_load - @debug "Extension $(extid.id.name) of $(extid.parentid.name) will not be loaded \ - since $(pkgid.name) loaded in environment lower in load path" - # indicate extid is expected to fail - extid.ntriggers *= -1 - else - # indicate pkgid is loaded - extid.ntriggers -= 1 - end + # indicate pkgid is loaded + extid.ntriggers -= 1 end if extid.ntriggers < 0 # indicate pkgid is loaded @@ -2148,6 +2180,7 @@ 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.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)), $deps, $(repr(source_path(nothing)))) """) diff --git a/test/loading.jl b/test/loading.jl index e019dc95dab26..ea544c2635dbc 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1056,8 +1056,6 @@ end envs = [joinpath(@__DIR__, "project", "Extensions", "EnvWithHasExtensionsv2"), joinpath(@__DIR__, "project", "Extensions", "EnvWithHasExtensions")] cmd = addenv(```$(Base.julia_cmd()) --startup-file=no -e ' begin - - push!(empty!(DEPOT_PATH), '$(repr(depot_path))') using HasExtensions using ExtDep @@ -1067,6 +1065,22 @@ end ' ```, "JULIA_LOAD_PATH" => join(envs, sep)) @test success(cmd) + + test_ext_proj = """ + begin + using HasExtensions + using ExtDep + Base.get_extension(HasExtensions, :Extension) isa Module || error("expected extension to load") + using ExtDep2 + Base.get_extension(HasExtensions, :ExtensionFolder) isa Module || error("expected extension to load") + end + """ + for compile in (`--compiled-modules=no`, ``) + cmd_proj_ext = `$(Base.julia_cmd()) $compile --startup-file=no -e $test_ext_proj` + proj = joinpath(@__DIR__, "project", "Extensions") + cmd_proj_ext = addenv(cmd_proj_ext, "JULIA_LOAD_PATH" => join([joinpath(proj, "HasExtensions.jl"), joinpath(proj, "EnvWithDeps")], sep)) + run(cmd_proj_ext) + end finally try rm(depot_path, force=true, recursive=true) diff --git a/test/project/Extensions/EnvWithDeps/Manifest.toml b/test/project/Extensions/EnvWithDeps/Manifest.toml new file mode 100644 index 0000000000000..85ff259f0a4d5 --- /dev/null +++ b/test/project/Extensions/EnvWithDeps/Manifest.toml @@ -0,0 +1,21 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.9.0-rc3" +manifest_format = "2.0" +project_hash = "ec25ff8df3a5e2212a173c3de2c7d716cc47cd36" + +[[deps.ExtDep]] +deps = ["SomePackage"] +path = "../ExtDep.jl" +uuid = "fa069be4-f60b-4d4c-8b95-f8008775090c" +version = "0.1.0" + +[[deps.ExtDep2]] +path = "../ExtDep2" +uuid = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" +version = "0.1.0" + +[[deps.SomePackage]] +path = "../SomePackage" +uuid = "678608ae-7bb3-42c7-98b1-82102067a3d8" +version = "0.1.0" diff --git a/test/project/Extensions/EnvWithDeps/Project.toml b/test/project/Extensions/EnvWithDeps/Project.toml new file mode 100644 index 0000000000000..cf020b56fc2e8 --- /dev/null +++ b/test/project/Extensions/EnvWithDeps/Project.toml @@ -0,0 +1,4 @@ +[deps] +ExtDep = "fa069be4-f60b-4d4c-8b95-f8008775090c" +ExtDep2 = "55982ee5-2ad5-4c40-8cfe-5e9e1b01500d" +SomePackage = "678608ae-7bb3-42c7-98b1-82102067a3d8" From 6a5f51bdd9a25d80c9e0caa1f17fccdc1ee5ac47 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 4 May 2023 17:21:29 -0400 Subject: [PATCH 753/775] reorder ml-matches to avoid catastrophic performance case This ordering of the algorithm abandons the elegant insertion in favor of using another copy of Tarjan's SCC code. This enables us to abort the algorithm in O(k*n) time, instead of always running full O(n*n) time, where k is `min(lim,n)`. For example, to sort 1338 methods: Before: julia> @time Base._methods_by_ftype(Tuple{typeof(Core.kwcall), NamedTuple, Any, Vararg{Any}}, 3, Base.get_world_counter()); 0.136609 seconds (22.74 k allocations: 1.104 MiB) julia> @time Base._methods_by_ftype(Tuple{typeof(Core.kwcall), NamedTuple, Any, Vararg{Any}}, -1, Base.get_world_counter()); 0.046280 seconds (9.95 k allocations: 497.453 KiB) julia> @time Base._methods_by_ftype(Tuple{typeof(Core.kwcall), NamedTuple, Any, Vararg{Any}}, 30000, Base.get_world_counter()); 0.132588 seconds (22.73 k allocations: 1.103 MiB) julia> @time Base._methods_by_ftype(Tuple{typeof(Core.kwcall), NamedTuple, Any, Vararg{Any}}, 30000, Base.get_world_counter()); 0.135912 seconds (22.73 k allocations: 1.103 MiB) After: julia> @time Base._methods_by_ftype(Tuple{typeof(Core.kwcall), NamedTuple, Any, Vararg{Any}}, 3, Base.get_world_counter()); 0.001040 seconds (1.47 k allocations: 88.375 KiB) julia> @time Base._methods_by_ftype(Tuple{typeof(Core.kwcall), NamedTuple, Any, Vararg{Any}}, -1, Base.get_world_counter()); 0.039167 seconds (8.24 k allocations: 423.984 KiB) julia> @time Base._methods_by_ftype(Tuple{typeof(Core.kwcall), NamedTuple, Any, Vararg{Any}}, 30000, Base.get_world_counter()); 0.081354 seconds (8.26 k allocations: 424.734 KiB) julia> @time Base._methods_by_ftype(Tuple{typeof(Core.kwcall), NamedTuple, Any, Vararg{Any}}, 30000, Base.get_world_counter()); 0.080849 seconds (8.26 k allocations: 424.734 KiB) And makes inference faster in rare cases (this particular example came up because the expression below occurs appears in `@test` macroexpansion), both before loading loading more packages, such as OmniPackage, and afterwards, where the cost is almost unchanged afterwards, versus increasing about 50x. julia> f() = x(args...; kwargs...); @time @code_typed optimize=false f(); 0.143523 seconds (23.25 k allocations: 1.128 MiB, 99.96% compilation time) # before 0.001172 seconds (1.86 k allocations: 108.656 KiB, 97.71% compilation time) # after --- src/gf.c | 571 ++++++++++++--------------- test/ambiguous.jl | 11 + test/compiler/AbstractInterpreter.jl | 2 +- test/reflection.jl | 2 +- 4 files changed, 268 insertions(+), 318 deletions(-) diff --git a/src/gf.c b/src/gf.c index 6d55e479babfe..b8fc3d79b8b0b 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1922,7 +1922,7 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho JL_UNLOCK(&mt->writelock); } -static int jl_type_intersection2(jl_value_t *t1, jl_value_t *t2, jl_value_t **isect, jl_value_t **isect2) +static int jl_type_intersection2(jl_value_t *t1, jl_value_t *t2, jl_value_t **isect JL_REQUIRE_ROOTED_SLOT, jl_value_t **isect2 JL_REQUIRE_ROOTED_SLOT) { *isect2 = NULL; int is_subty = 0; @@ -3289,6 +3289,214 @@ static int ml_mtable_visitor(jl_methtable_t *mt, void *closure0) return jl_typemap_intersection_visitor(jl_atomic_load_relaxed(&mt->defs), jl_cachearg_offset(mt), env); } + +// Visit the candidate methods, starting from edges[idx], to determine if their sort ordering +// Implements Tarjan's SCC (strongly connected components) algorithm, simplified to remove the count variable +// TODO: convert this function into an iterative call, rather than recursive +static int sort_mlmatches(jl_array_t *t, size_t idx, arraylist_t *visited, arraylist_t *stack, arraylist_t *result, int lim, int include_ambiguous, int *has_ambiguity, int *found_minmax) +{ + size_t cycle = (size_t)visited->items[idx]; + if (cycle != 0) + return cycle - 1; // depth remaining + arraylist_push(stack, (void*)idx); + size_t depth = stack->len; + visited->items[idx] = (void*)(1 + depth); + jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(t, idx); + jl_method_t *m = matc->method; + cycle = depth; + for (size_t childidx = 0; childidx < jl_array_len(t); childidx++) { + if (childidx == idx || (size_t)visited->items[childidx] == 1) + continue; + jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(t, childidx); + jl_method_t *m2 = matc2->method; + int subt2 = matc2->fully_covers != NOT_FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) + if (!subt2 && jl_has_empty_intersection(m2->sig, m->sig)) + continue; + if (jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig)) + continue; + // m2 is better or ambiguous + int child_cycle = sort_mlmatches(t, childidx, visited, stack, result, lim, include_ambiguous, has_ambiguity, found_minmax); + if (child_cycle == -1) + return -1; + if (child_cycle && child_cycle < cycle) { + // record the cycle will resolve at depth "cycle" + cycle = child_cycle; + } + } + if (cycle != depth) + return cycle; + // If we are the top of the current cycle, we have the next set of mostspecific methods. + // Decide if we need to append those the current results. + int ncycle = 0; + for (size_t i = depth - 1; i < stack->len; i++) { + size_t childidx = (size_t)stack->items[i]; + jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(t, childidx); + if ((size_t)visited->items[childidx] == 1) { + assert(matc->fully_covers != NOT_FULLY_COVERS); + continue; + } + assert(visited->items[childidx] == (void*)(2 + i)); + // always remove fully_covers matches after the first minmax ambiguity group is handled + if (matc->fully_covers != NOT_FULLY_COVERS) { + if (*found_minmax) + visited->items[childidx] = (void*)1; + // but still need to count minmax itself, if this is the first time we are here + if (*found_minmax == 1) + ncycle += 1; + continue; + } + else if (lim != -1) { + // when limited, don't include this match if it was covered by an earlier one (and isn't perhaps ambiguous with something) + jl_value_t *ti = (jl_value_t*)matc->spec_types; + for (size_t i = 0; i < result->len; i++) { + size_t idx2 = (size_t)result->items[i]; + jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(t, idx2); + jl_method_t *m2 = matc2->method; + if (jl_subtype((jl_value_t*)ti, m2->sig)) { + visited->items[childidx] = (void*)1; + break; + } + } + } + if ((size_t)visited->items[childidx] != 1) + ncycle += 1; + } + if (ncycle > 1 && !*has_ambiguity) { + if (lim == -1) { + *has_ambiguity = 1; + } + else { + // laborious test, checking for existence and coverage of m3 + // (has_ambiguity is overestimated for lim==-1, since we don't compute skipped matches either) + // some method is ambiguous, but let's see if we can find another method (m3) + // outside of the ambiguity group that dominates any ambiguous methods, + // and means we can ignore this for has_ambiguity + jl_value_t *ti = NULL; + jl_value_t *isect2 = NULL; + JL_GC_PUSH2(&ti, &isect2); + for (size_t i = depth - 1; i < stack->len; i++) { + size_t childidx = (size_t)stack->items[i]; + if ((size_t)visited->items[childidx] == 1) + continue; + jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(t, childidx); + jl_method_t *m = matc->method; + int subt = matc->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m->sig) + for (size_t j = depth - 1; j < stack->len; j++) { + if (i == j) + continue; + size_t idx2 = (size_t)stack->items[j]; + assert(childidx != idx2); + // n.b. even if we skipped them earlier, they still might + // contribute to the ambiguities (due to lock of transitivity of + // morespecific over subtyping) + // TODO: we could improve this result by checking if the removal of some + // edge earlier means that this subgraph is now well-ordered and then be + // allowed to ignore these vertexes entirely here + jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(t, idx2); + jl_method_t *m2 = matc2->method; + int subt2 = matc2->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) + // if they aren't themselves simply ordered + if (jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig) || + jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig)) + continue; + if (subt) { + ti = (jl_value_t*)matc2->spec_types; + isect2 = NULL; + } + else if (subt2) { + ti = (jl_value_t*)matc->spec_types; + isect2 = NULL; + } + else { + jl_type_intersection2((jl_value_t*)matc->spec_types, (jl_value_t*)matc2->spec_types, &ti, &isect2); + } + // and their intersection contributes to the ambiguity cycle + if (ti != jl_bottom_type) { + // now look for a third method m3 outside of this ambiguity group that fully resolves this intersection + size_t k; + for (k = 0; k < result->len; k++) { + size_t idx3 = (size_t)result->items[k]; + jl_method_match_t *matc3 = (jl_method_match_t*)jl_array_ptr_ref(t, idx3); + jl_method_t *m3 = matc3->method; + if ((jl_subtype(ti, m3->sig) || (isect2 && jl_subtype(isect2, m3->sig))) + && jl_type_morespecific((jl_value_t*)m3->sig, (jl_value_t*)m->sig) + && jl_type_morespecific((jl_value_t*)m3->sig, (jl_value_t*)m2->sig)) { + //if (jl_subtype(matc->spec_types, ti) || jl_subtype(matc->spec_types, matc3->m3->sig)) + // // check if it covered not only this intersection, but all intersections with matc + // // if so, we do not need to check all of them separately + // j = len; + break; + } + } + if (k == result->len) { + *has_ambiguity = 1; + } + isect2 = NULL; + } + ti = NULL; + if (*has_ambiguity) + break; + } + if (*has_ambiguity) + break; + } + JL_GC_POP(); + } + } + // If we're only returning possible matches, now filter out this method + // if its intersection is fully ambiguous in this SCC group. + if (!include_ambiguous) { + for (size_t i = depth - 1; i < stack->len; i++) { + size_t childidx = (size_t)stack->items[i]; + if ((size_t)visited->items[childidx] == 1) + continue; + jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(t, childidx); + jl_method_t *m = matc->method; + jl_value_t *ti = (jl_value_t*)matc->spec_types; + for (size_t j = depth - 1; j < stack->len; j++) { + if (i == j) + continue; + size_t idx2 = (size_t)stack->items[j]; + jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(t, idx2); + jl_method_t *m2 = matc2->method; + int subt2 = matc2->fully_covers != NOT_FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) + // if their intersection contributes to the ambiguity cycle + // and the contribution of m is fully ambiguous with the portion of the cycle from m2 + if (subt2 || jl_subtype((jl_value_t*)ti, m2->sig)) { + // but they aren't themselves simply ordered (here + // we don't consider that a third method might be + // disrupting that ordering and just consider them + // pairwise to keep this simple). + if (!jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig) && + !jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig)) { + assert(lim != -1 || *has_ambiguity); + visited->items[childidx] = (void*)1; + break; + } + } + } + } + } + while (stack->len >= depth) { + size_t childidx = (size_t)arraylist_pop(stack); + // always remove fully_covers matches after the first minmax ambiguity group is handled + jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(t, childidx); + if (matc->fully_covers != NOT_FULLY_COVERS) + *found_minmax = 2; + if ((size_t)visited->items[childidx] != 1) { + assert(visited->items[childidx] == (void*)(2 + stack->len)); + visited->items[childidx] = (void*)1; + if (lim == -1 || result->len < lim) + arraylist_push(result, (void*)childidx); + else + return -1; + } + } + return 0; +} + + + // This is the collect form of calling jl_typemap_intersection_visitor // with optimizations to skip fully shadowed methods. // @@ -3487,330 +3695,61 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, len = 1; } } + if (minmax && lim == 0) { + // protect some later algorithms from underflow + JL_GC_POP(); + return jl_nothing; + } } if (len > 1) { - // need to partially domsort the graph now into a list - // (this is an insertion sort attempt) - // if we have a minmax method, we ignore anything less specific - // we'll clean that up next - for (i = 1; i < len; i++) { - env.matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, i); - jl_method_t *m = env.matc->method; - int subt = env.matc->fully_covers != NOT_FULLY_COVERS; - if ((minmax != NULL || (minmax_ambig && !include_ambiguous)) && subt) { - continue; // already the biggest (skip will filter others) - } - for (j = 0; j < i; j++) { - jl_method_match_t *matc2 = (jl_method_match_t *)jl_array_ptr_ref(env.t, i - j - 1); - jl_method_t *m2 = matc2->method; - int subt2 = matc2->fully_covers != NOT_FULLY_COVERS; - if (!subt2 && subt) - break; - if (subt == subt2) { - if (lim != -1) { - if (subt || !jl_has_empty_intersection(m->sig, m2->sig)) - if (!jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig)) - break; - } - else { - // if unlimited, use approximate sorting, with the only - // main downside being that it may be overly- - // conservative at reporting existence of ambiguities - if (jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig)) - break; - } - } - jl_array_ptr_set(env.t, i - j, matc2); - } - jl_array_ptr_set(env.t, i - j, env.matc); - } - char *skip = (char*)alloca(len); - memset(skip, 0, len); + arraylist_t stack, visited, result; + arraylist_new(&result, lim != -1 && lim < len ? lim : len); + arraylist_new(&stack, 0); + arraylist_new(&visited, len); + arraylist_grow(&visited, len); + memset(visited.items, 0, len * sizeof(size_t)); // if we had a minmax method (any subtypes), now may now be able to - // quickly cleanup some of our sort result + // quickly cleanup some of methods + int found_minmax = 0; if (minmax != NULL || (minmax_ambig && !include_ambiguous)) { - for (i = 0; i < len; i++) { - jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, i); - if (minmax != matc && matc->fully_covers != NOT_FULLY_COVERS) { - skip[i] = 1; - } - } - } - if (include_ambiguous && lim == -1 && ambig == NULL && !minmax_ambig) { - // in this case, we don't actually need to compute the ambiguity - // information at all as the user doesn't need us to filter them - // out or report them + found_minmax = 1; } - else { - // now that the results are (mostly) sorted, assign group numbers to each ambiguity - // by computing the specificity-ambiguity matrix covering this query - uint32_t *ambig_groupid = (uint32_t*)alloca(len * sizeof(uint32_t)); - for (i = 0; i < len; i++) - ambig_groupid[i] = i; - // as we go, keep a rough count of how many methods are disjoint, which - // gives us a lower bound on how many methods we will be returning - // and lets us stop early if we reach our limit - int ndisjoint = minmax ? 1 : 0; - for (i = 0; i < len; i++) { - jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, i); - if (skip[i]) { - // if there was a minmax method, we can just pretend the rest are all in the same group: - // they're all together but unsorted in the list, since we'll drop them all later anyways - assert(matc->fully_covers != NOT_FULLY_COVERS); - if (ambig_groupid[len - 1] > i) - ambig_groupid[len - 1] = i; // ambiguity covering range [i:len) - break; - } - jl_method_t *m = matc->method; - int subt = matc->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m->sig) - int rsubt = jl_egal((jl_value_t*)matc->spec_types, m->sig); - int disjoint = 1; - for (j = len; j > i; j--) { - if (ambig_groupid[j - 1] < i) { - disjoint = 0; - break; - } - jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(env.t, j - 1); - // can't use skip[j - 1] here, since we still need to make sure the minmax dominates - jl_method_t *m2 = matc2->method; - int subt2 = matc2->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) - int rsubt2 = jl_egal((jl_value_t*)matc2->spec_types, m2->sig); - jl_value_t *ti; - if (!subt && !subt2 && rsubt && rsubt2 && lim == -1 && ambig == NULL) - // these would only be filtered out of the list as - // ambiguous if they are also type-equal, as we - // aren't skipping matches and the user doesn't - // care if we report any ambiguities - continue; - if (jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig)) - continue; - if (subt) { - ti = (jl_value_t*)matc2->spec_types; - isect2 = NULL; - } - else if (subt2) { - ti = (jl_value_t*)matc->spec_types; - isect2 = NULL; - } - else { - jl_type_intersection2((jl_value_t*)matc->spec_types, (jl_value_t*)matc2->spec_types, &env.match.ti, &isect2); - ti = env.match.ti; - } - if (ti != jl_bottom_type) { - disjoint = 0; - ambig_groupid[j - 1] = i; // ambiguity covering range [i:j) - isect2 = NULL; - break; - } - isect2 = NULL; - } - if (disjoint && lim >= 0) { - ndisjoint += 1; - if (ndisjoint > lim) { - JL_GC_POP(); - return jl_nothing; - } - } - } - // then we'll merge those numbers to assign each item in the group the same number - // (similar to Kosaraju's SCC algorithm?) - uint32_t groupid = 0; - uint32_t grouphi = 0; - for (i = 0; i < len; i++) { - j = len - i - 1; - uint32_t agid = ambig_groupid[j]; - if (agid != j) { // thus agid < j - if (grouphi == 0) { - groupid = agid; - grouphi = j; - } - else if (agid < groupid) { - groupid = agid; - } - } - if (grouphi && j == groupid) { - do { - ambig_groupid[grouphi--] = groupid; - } while (grouphi > j); - ambig_groupid[j] = groupid; - groupid = 0; - grouphi = 0; - } - } - // always remove matches after the first subtype, now that we've sorted the list for ambiguities - for (i = 0; i < len; i++) { - jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, i); - if (matc->fully_covers == FULLY_COVERS) { // jl_subtype((jl_value_t*)type, (jl_value_t*)m->sig) - uint32_t agid = ambig_groupid[i]; - while (i < len && agid == ambig_groupid[i]) - i++; // keep ambiguous ones - for (; i < len; i++) - skip[i] = 1; // drop the rest - } - } - // when limited, skip matches that are covered by earlier ones (and aren't perhaps ambiguous with them) - if (lim != -1) { - for (i = 0; i < len; i++) { - if (skip[i]) - continue; - jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, i); - jl_method_t *m = matc->method; - jl_tupletype_t *ti = matc->spec_types; - if (matc->fully_covers == FULLY_COVERS) - break; // remaining matches are ambiguous or already skipped - for (j = 0; j < i; j++) { - jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(env.t, j); - jl_method_t *m2 = matc2->method; - if (jl_subtype((jl_value_t*)ti, m2->sig)) { - if (ambig_groupid[i] != ambig_groupid[j]) { - skip[i] = 1; - break; - } - else if (!include_ambiguous) { - if (!jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig)) { - skip[i] = 1; - break; - } - } - } - } - } - } - // Compute whether anything could be ambiguous by seeing if any two - // remaining methods in the result are in the same ambiguity group. - assert(len > 0); - if (!has_ambiguity) { - // quick test - uint32_t agid = ambig_groupid[0]; - for (i = 1; i < len; i++) { - if (!skip[i]) { - if (agid == ambig_groupid[i]) { - has_ambiguity = 1; - break; - } - agid = ambig_groupid[i]; - } - } + for (i = 0; i < len; i++) { + assert(visited.items[i] == (void*)0 || visited.items[i] == (void*)1); + jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, i); + if (matc->fully_covers != NOT_FULLY_COVERS && found_minmax) { + // this was already handled above and below, so we won't learn anything new + // by visiting it and it might be a bit costly + continue; } - // laborious test, checking for existence and coverage of m3 - // (has_ambiguity is overestimated for lim==-1, since we don't compute skipped matches either) - if (has_ambiguity) { - if (lim != -1) { - // some method is ambiguous, but let's see if we can find another method (m3) - // outside of the ambiguity group that dominates any ambiguous methods, - // and means we can ignore this for has_ambiguity - has_ambiguity = 0; - for (i = 0; i < len; i++) { - if (skip[i]) - continue; - uint32_t agid = ambig_groupid[i]; - jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, i); - jl_method_t *m = matc->method; - int subt = matc->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m->sig) - for (j = agid; j < len && ambig_groupid[j] == agid; j++) { - // n.b. even if we skipped them earlier, they still might - // contribute to the ambiguities (due to lock of transitivity of - // morespecific over subtyping) - if (j == i) - continue; - jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(env.t, j); - jl_method_t *m2 = matc2->method; - int subt2 = matc2->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) - // if they aren't themselves simply ordered - if (jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig) || - jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig)) - continue; - jl_value_t *ti; - if (subt) { - ti = (jl_value_t*)matc2->spec_types; - isect2 = NULL; - } - else if (subt2) { - ti = (jl_value_t*)matc->spec_types; - isect2 = NULL; - } - else { - jl_type_intersection2((jl_value_t*)matc->spec_types, (jl_value_t*)matc2->spec_types, &env.match.ti, &isect2); - ti = env.match.ti; - } - // and their intersection contributes to the ambiguity cycle - if (ti != jl_bottom_type) { - // now look for a third method m3 outside of this ambiguity group that fully resolves this intersection - size_t k; - for (k = agid; k > 0; k--) { - jl_method_match_t *matc3 = (jl_method_match_t*)jl_array_ptr_ref(env.t, k - 1); - jl_method_t *m3 = matc3->method; - if ((jl_subtype(ti, m3->sig) || (isect2 && jl_subtype(isect2, m3->sig))) - && jl_type_morespecific((jl_value_t*)m3->sig, (jl_value_t*)m->sig) - && jl_type_morespecific((jl_value_t*)m3->sig, (jl_value_t*)m2->sig)) { - //if (jl_subtype(matc->spec_types, ti) || jl_subtype(matc->spec_types, matc3->m3->sig)) - // // check if it covered not only this intersection, but all intersections with matc - // // if so, we do not need to check all of them separately - // j = len; - break; - } - } - if (k == 0) - has_ambiguity = 1; - isect2 = NULL; - } - if (has_ambiguity) - break; - } - if (has_ambiguity) - break; - } - } - // If we're only returning possible matches, now filter out any method - // whose intersection is fully ambiguous with the group it is in. - if (!include_ambiguous) { - for (i = 0; i < len; i++) { - if (skip[i]) - continue; - uint32_t agid = ambig_groupid[i]; - jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, i); - jl_method_t *m = matc->method; - jl_tupletype_t *ti = matc->spec_types; - int subt = matc->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m->sig) - char ambig1 = 0; - for (j = agid; j < len && ambig_groupid[j] == agid; j++) { - if (j == i) - continue; - jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(env.t, j); - jl_method_t *m2 = matc2->method; - int subt2 = matc2->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) - // if their intersection contributes to the ambiguity cycle - if (subt || subt2 || !jl_has_empty_intersection((jl_value_t*)ti, m2->sig)) { - // and the contribution of m is fully ambiguous with the portion of the cycle from m2 - if (subt2 || jl_subtype((jl_value_t*)ti, m2->sig)) { - // but they aren't themselves simply ordered (here - // we don't consider that a third method might be - // disrupting that ordering and just consider them - // pairwise to keep this simple). - if (!jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig) && - !jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig)) { - ambig1 = 1; - break; - } - } - } - } - if (ambig1) - skip[i] = 1; - } - } + int child_cycle = sort_mlmatches((jl_array_t*)env.t, i, &visited, &stack, &result, lim == -1 || minmax == NULL ? lim : lim - 1, include_ambiguous, &has_ambiguity, &found_minmax); + if (child_cycle == -1) { + arraylist_free(&visited); + arraylist_free(&stack); + arraylist_free(&result); + JL_GC_POP(); + return jl_nothing; } - } - // cleanup array to remove skipped entries - for (i = 0, j = 0; i < len; i++) { + assert(child_cycle == 0); (void)child_cycle; + assert(stack.len == 0); + assert(visited.items[i] == (void*)1); + } + arraylist_free(&visited); + arraylist_free(&stack); + for (j = 0; j < result.len; j++) { + i = (size_t)result.items[j]; jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, i); - if (!skip[i]) { - jl_array_ptr_set(env.t, j++, matc); - // remove our sentinel entry markers - if (matc->fully_covers == SENTINEL) - matc->fully_covers = NOT_FULLY_COVERS; - } + // remove our sentinel entry markers + if (matc->fully_covers == SENTINEL) + matc->fully_covers = NOT_FULLY_COVERS; + result.items[j] = (void*)matc; + } + if (minmax) { + arraylist_push(&result, minmax); + j++; } + memcpy(jl_array_data(env.t), result.items, j * sizeof(jl_method_match_t*)); + arraylist_free(&result); if (j != len) jl_array_del_end((jl_array_t*)env.t, len - j); len = j; diff --git a/test/ambiguous.jl b/test/ambiguous.jl index a1b973f30a70c..5056fc626e84a 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -378,6 +378,17 @@ let ambig = Ref{Int32}(0) @test ambig[] == 1 end +fnoambig(::Int,::Int) = 1 +fnoambig(::Int,::Any) = 2 +fnoambig(::Any,::Int) = 3 +fnoambig(::Any,::Any) = 4 +let has_ambig = Ref(Int32(0)) + ms = Base._methods_by_ftype(Tuple{typeof(fnoambig), Any, Any}, nothing, 4, Base.get_world_counter(), false, Ref(typemin(UInt)), Ref(typemax(UInt)), has_ambig) + @test ms isa Vector + @test length(ms) == 4 + @test has_ambig[] == 0 +end + # issue #11407 f11407(::Dict{K,V}, ::Dict{Any,V}) where {K,V} = 1 f11407(::Dict{K,V}, ::Dict{K,Any}) where {K,V} = 2 diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index 2a2502a003ac5..0e94d42fa8866 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -43,8 +43,8 @@ end |> !Core.Compiler.is_nonoverlayed end |> !Core.Compiler.is_nonoverlayed # account for overlay possibility in unanalyzed matching method -callstrange(::Nothing) = Core.compilerbarrier(:type, nothing) # trigger inference bail out callstrange(::Float64) = strangesin(x) +callstrange(::Nothing) = Core.compilerbarrier(:type, nothing) # trigger inference bail out callstrange_entry(x) = callstrange(x) # needs to be defined here because of world age let interp = MTOverlayInterp(Set{Any}()) matches = Core.Compiler.findall(Tuple{typeof(callstrange),Any}, Core.Compiler.method_table(interp)).matches diff --git a/test/reflection.jl b/test/reflection.jl index 8fdfa5be0b57c..0ae8cb3f9d393 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -547,7 +547,7 @@ let end # code_typed_by_type -@test Base.code_typed_by_type(Tuple{Type{<:Val}})[1][2] == Val +@test Base.code_typed_by_type(Tuple{Type{<:Val}})[2][2] == Val @test Base.code_typed_by_type(Tuple{typeof(sin), Float64})[1][2] === Float64 # New reflection methods in 0.6 From ac1cb1c7b6a3787ac1c752039ab5cbdd1dca052a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 9 May 2023 10:35:21 -0400 Subject: [PATCH 754/775] optimize reordering of ml-matches to avoid unnecessary computations This now chooses the optimal SCC set based on the size of lim, which ensures we can assume this algorithm is now << O(n^2) in all reasonable cases, even though the algorithm we are using is O(n + e), where e may require up to n^2 work to compute in the worst case, but should require only about n*min(lim, log(n)) work in the expected average case. This also further pre-optimizes quick work (checking for existing coverage) and delays unnecessary work (computing for *ambig return). --- doc/src/manual/methods.md | 4 +- src/gf.c | 449 +++++++++++++++++++--------- stdlib/REPL/test/replcompletions.jl | 2 +- test/errorshow.jl | 11 +- 4 files changed, 313 insertions(+), 153 deletions(-) diff --git a/doc/src/manual/methods.md b/doc/src/manual/methods.md index 23f409b22b880..8ca00aa1cfe76 100644 --- a/doc/src/manual/methods.md +++ b/doc/src/manual/methods.md @@ -322,10 +322,10 @@ julia> g(2.0, 3.0) ERROR: MethodError: g(::Float64, ::Float64) is ambiguous. Candidates: - g(x::Float64, y) - @ Main none:1 g(x, y::Float64) @ Main none:1 + g(x::Float64, y) + @ Main none:1 Possible fix, define g(::Float64, ::Float64) diff --git a/src/gf.c b/src/gf.c index b8fc3d79b8b0b..22bd0add82664 100644 --- a/src/gf.c +++ b/src/gf.c @@ -3290,207 +3290,270 @@ static int ml_mtable_visitor(jl_methtable_t *mt, void *closure0) } -// Visit the candidate methods, starting from edges[idx], to determine if their sort ordering +// Visit the candidate methods, starting from t[idx], to determine a possible valid sort ordering, +// where every morespecific method appears before any method which it has a common +// intersection with but is not partly ambiguous with (ambiguity is transitive, particularly +// if lim==-1, although morespecific is not transitive). // Implements Tarjan's SCC (strongly connected components) algorithm, simplified to remove the count variable +// Inputs: +// * `t`: the array of vertexes (method matches) +// * `idx`: the next vertex to add to the output +// * `visited`: the state of the algorithm for each vertex in `t`: either 1 if we visited it already or 1+depth if we are visiting it now +// * `stack`: the state of the algorithm for the current vertex (up to length equal to `t`): the list of all vertexes currently in the depth-first path or in the current SCC +// * `result`: the output of the algorithm, a sorted list of vertexes (up to length `lim`) +// * `allambig`: a list of all vertexes with an ambiguity (up to length equal to `t`), discovered while running the rest of the algorithm +// * `lim`: either -1 for unlimited matches, or the maximum length for `result` before returning failure (return -1). +// If specified as -1, this will return extra matches that would have been elided from the list because they were already covered by an earlier match. +// This gives a sort of maximal set of matching methods (up to the first minmax method). +// If specified as -1, the sorting will also include all "weak" edges (every ambiguous pair) which will create much larger ambiguity cycles, +// resulting in a less accurate sort order and much less accurate `*has_ambiguity` result. +// * `include_ambiguous`: whether to filter out fully ambiguous matches from `result` +// * `*has_ambiguity`: whether the algorithm does not need to compute if there is an unresolved ambiguity +// * `*found_minmax`: whether there is a minmax method already found, so future fully_covers matches should be ignored +// Outputs: +// * `*has_ambiguity`: whether the caller should check if there remains an unresolved ambiguity (in `allambig`) +// Returns: +// * -1: too many matches for lim, other outputs are undefined +// * 0: the child(ren) have been added to the output +// * 1+: the children are part of this SCC (up to this depth) // TODO: convert this function into an iterative call, rather than recursive -static int sort_mlmatches(jl_array_t *t, size_t idx, arraylist_t *visited, arraylist_t *stack, arraylist_t *result, int lim, int include_ambiguous, int *has_ambiguity, int *found_minmax) +static int sort_mlmatches(jl_array_t *t, size_t idx, arraylist_t *visited, arraylist_t *stack, arraylist_t *result, arraylist_t *allambig, int lim, int include_ambiguous, int *has_ambiguity, int *found_minmax) { size_t cycle = (size_t)visited->items[idx]; if (cycle != 0) return cycle - 1; // depth remaining + jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(t, idx); + jl_method_t *m = matc->method; + jl_value_t *ti = (jl_value_t*)matc->spec_types; + int subt = matc->fully_covers != NOT_FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m->sig) + // first check if this new method is actually already fully covered by an + // existing match and we can just ignore this entry quickly + size_t result_len = 0; + if (subt) { + if (*found_minmax == 2) + visited->items[idx] = (void*)1; + } + else if (lim != -1) { + for (; result_len < result->len; result_len++) { + size_t idx2 = (size_t)result->items[result_len]; + jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(t, idx2); + jl_method_t *m2 = matc2->method; + if (jl_subtype(ti, m2->sig)) { + if (include_ambiguous) { + if (!jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig)) + continue; + } + visited->items[idx] = (void*)1; + break; + } + } + } + if ((size_t)visited->items[idx] == 1) + return 0; arraylist_push(stack, (void*)idx); size_t depth = stack->len; visited->items[idx] = (void*)(1 + depth); - jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(t, idx); - jl_method_t *m = matc->method; cycle = depth; + int addambig = 0; + int mayexclude = 0; + // First visit all "strong" edges where the child is definitely better. + // This likely won't hit any cycles, but might (because morespecific is not transitive). + // Along the way, record if we hit any ambiguities-we may need to track those later. for (size_t childidx = 0; childidx < jl_array_len(t); childidx++) { - if (childidx == idx || (size_t)visited->items[childidx] == 1) + if (childidx == idx) continue; + int child_cycle = (size_t)visited->items[childidx]; + if (child_cycle == 1) + continue; // already handled + if (child_cycle != 0 && child_cycle - 1 >= cycle) + continue; // already part of this cycle jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(t, childidx); jl_method_t *m2 = matc2->method; int subt2 = matc2->fully_covers != NOT_FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) + // TODO: we could change this to jl_has_empty_intersection(ti, (jl_value_t*)matc2->spec_types); + // since we only care about sorting of the intersections the user asked us about if (!subt2 && jl_has_empty_intersection(m2->sig, m->sig)) continue; - if (jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig)) + int msp = jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig); + int msp2 = !msp && jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig); + if (!msp) { + if (subt || !include_ambiguous || (lim != -1 && msp2)) { + if (subt2 || jl_subtype((jl_value_t*)ti, m2->sig)) { + // this may be filtered out as fully intersected, if applicable later + mayexclude = 1; + } + } + if (!msp2) { + addambig = 1; // record there is a least one previously-undetected ambiguity that may need to be investigated later (between m and m2) + } + } + if (lim == -1 ? msp : !msp2) // include only strong or also weak edges, depending on whether the result size is limited continue; - // m2 is better or ambiguous - int child_cycle = sort_mlmatches(t, childidx, visited, stack, result, lim, include_ambiguous, has_ambiguity, found_minmax); + // m2 is (lim!=-1 ? better : not-worse), so attempt to visit it first + // if limited, then we want to visit only better edges, because that results in finding k best matches quickest + // if not limited, then we want to visit all edges, since that results in finding the largest SCC cycles, which requires doing the fewest intersections + child_cycle = sort_mlmatches(t, childidx, visited, stack, result, allambig, lim, include_ambiguous, has_ambiguity, found_minmax); if (child_cycle == -1) return -1; if (child_cycle && child_cycle < cycle) { // record the cycle will resolve at depth "cycle" cycle = child_cycle; } + if (stack->len == depth) { + // if this child resolved without hitting a cycle, then there is + // some probability that this method is already fully covered now + // (same check as before), and we can delete this vertex now without + // anyone noticing (too much) + if (subt) { + if (*found_minmax == 2) + visited->items[idx] = (void*)1; + } + else if (lim != -1) { + for (; result_len < result->len; result_len++) { + size_t idx2 = (size_t)result->items[result_len]; + jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(t, idx2); + jl_method_t *m2 = matc2->method; + if (jl_subtype(ti, m2->sig)) { + if (include_ambiguous) { + if (!jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig)) + continue; + } + visited->items[idx] = (void*)1; + break; + } + } + } + if ((size_t)visited->items[idx] == 1) { + assert(cycle == depth); + size_t childidx = (size_t)arraylist_pop(stack); + assert(childidx == idx); (void)childidx; + assert(!subt || *found_minmax == 2); + return 0; + } + } } + if (matc->fully_covers == NOT_FULLY_COVERS && addambig) + arraylist_push(allambig, (void*)idx); if (cycle != depth) return cycle; - // If we are the top of the current cycle, we have the next set of mostspecific methods. - // Decide if we need to append those the current results. - int ncycle = 0; - for (size_t i = depth - 1; i < stack->len; i++) { - size_t childidx = (size_t)stack->items[i]; - jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(t, childidx); - if ((size_t)visited->items[childidx] == 1) { - assert(matc->fully_covers != NOT_FULLY_COVERS); - continue; + result_len = result->len; + if (stack->len == depth) { + // Found one "best" method to add right now. But we might exclude it if + // we determined earlier that we had that option. + if (mayexclude) { + if (!subt || *found_minmax == 2) + visited->items[idx] = (void*)1; } - assert(visited->items[childidx] == (void*)(2 + i)); - // always remove fully_covers matches after the first minmax ambiguity group is handled - if (matc->fully_covers != NOT_FULLY_COVERS) { - if (*found_minmax) - visited->items[childidx] = (void*)1; - // but still need to count minmax itself, if this is the first time we are here - if (*found_minmax == 1) - ncycle += 1; - continue; - } - else if (lim != -1) { - // when limited, don't include this match if it was covered by an earlier one (and isn't perhaps ambiguous with something) + } + else { + // We have a set of ambiguous methods. Record that. + // This is greatly over-approximated for lim==-1 + *has_ambiguity = 1; + // If we followed weak edges above, then this also fully closed the ambiguity cycle + if (lim == -1) + addambig = 0; + // If we're only returning possible matches, now filter out this method + // if its intersection is fully ambiguous in this SCC group. + // This is a repeat of the "first check", now that we have completed the cycle analysis + for (size_t i = depth - 1; i < stack->len; i++) { + size_t childidx = (size_t)stack->items[i]; + jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(t, childidx); jl_value_t *ti = (jl_value_t*)matc->spec_types; - for (size_t i = 0; i < result->len; i++) { - size_t idx2 = (size_t)result->items[i]; - jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(t, idx2); - jl_method_t *m2 = matc2->method; - if (jl_subtype((jl_value_t*)ti, m2->sig)) { + int subt = matc->fully_covers != NOT_FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m->sig) + if ((size_t)visited->items[childidx] == 1) { + assert(subt); + continue; + } + assert(visited->items[childidx] == (void*)(2 + i)); + // if we only followed strong edges before above + // check also if this set has an unresolved ambiguity missing from it + if (lim != -1 && !addambig) { + for (size_t j = 0; j < allambig->len; j++) { + if ((size_t)allambig->items[j] == childidx) { + addambig = 1; + break; + } + } + } + // always remove fully_covers matches after the first minmax ambiguity group is handled + if (subt) { + if (*found_minmax) visited->items[childidx] = (void*)1; - break; + continue; + } + else if (lim != -1) { + // when limited, don't include this match if it was covered by an earlier one + for (size_t result_len = 0; result_len < result->len; result_len++) { + size_t idx2 = (size_t)result->items[result_len]; + jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(t, idx2); + jl_method_t *m2 = matc2->method; + if (jl_subtype(ti, m2->sig)) { + if (include_ambiguous) { + if (!jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig)) + continue; + } + visited->items[childidx] = (void*)1; + break; + } } } } - if ((size_t)visited->items[childidx] != 1) - ncycle += 1; - } - if (ncycle > 1 && !*has_ambiguity) { - if (lim == -1) { - *has_ambiguity = 1; - } - else { - // laborious test, checking for existence and coverage of m3 - // (has_ambiguity is overestimated for lim==-1, since we don't compute skipped matches either) - // some method is ambiguous, but let's see if we can find another method (m3) - // outside of the ambiguity group that dominates any ambiguous methods, - // and means we can ignore this for has_ambiguity - jl_value_t *ti = NULL; - jl_value_t *isect2 = NULL; - JL_GC_PUSH2(&ti, &isect2); + if (!include_ambiguous && lim == -1) { for (size_t i = depth - 1; i < stack->len; i++) { size_t childidx = (size_t)stack->items[i]; if ((size_t)visited->items[childidx] == 1) continue; jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(t, childidx); jl_method_t *m = matc->method; - int subt = matc->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m->sig) + jl_value_t *ti = (jl_value_t*)matc->spec_types; for (size_t j = depth - 1; j < stack->len; j++) { if (i == j) continue; size_t idx2 = (size_t)stack->items[j]; - assert(childidx != idx2); - // n.b. even if we skipped them earlier, they still might - // contribute to the ambiguities (due to lock of transitivity of - // morespecific over subtyping) - // TODO: we could improve this result by checking if the removal of some - // edge earlier means that this subgraph is now well-ordered and then be - // allowed to ignore these vertexes entirely here jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(t, idx2); jl_method_t *m2 = matc2->method; - int subt2 = matc2->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) - // if they aren't themselves simply ordered - if (jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig) || - jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig)) - continue; - if (subt) { - ti = (jl_value_t*)matc2->spec_types; - isect2 = NULL; - } - else if (subt2) { - ti = (jl_value_t*)matc->spec_types; - isect2 = NULL; - } - else { - jl_type_intersection2((jl_value_t*)matc->spec_types, (jl_value_t*)matc2->spec_types, &ti, &isect2); - } - // and their intersection contributes to the ambiguity cycle - if (ti != jl_bottom_type) { - // now look for a third method m3 outside of this ambiguity group that fully resolves this intersection - size_t k; - for (k = 0; k < result->len; k++) { - size_t idx3 = (size_t)result->items[k]; - jl_method_match_t *matc3 = (jl_method_match_t*)jl_array_ptr_ref(t, idx3); - jl_method_t *m3 = matc3->method; - if ((jl_subtype(ti, m3->sig) || (isect2 && jl_subtype(isect2, m3->sig))) - && jl_type_morespecific((jl_value_t*)m3->sig, (jl_value_t*)m->sig) - && jl_type_morespecific((jl_value_t*)m3->sig, (jl_value_t*)m2->sig)) { - //if (jl_subtype(matc->spec_types, ti) || jl_subtype(matc->spec_types, matc3->m3->sig)) - // // check if it covered not only this intersection, but all intersections with matc - // // if so, we do not need to check all of them separately - // j = len; - break; - } - } - if (k == result->len) { - *has_ambiguity = 1; + int subt2 = matc2->fully_covers != NOT_FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) + // if their intersection contributes to the ambiguity cycle + // and the contribution of m is fully ambiguous with the portion of the cycle from m2 + if (subt2 || jl_subtype((jl_value_t*)ti, m2->sig)) { + // but they aren't themselves simply ordered (here + // we don't consider that a third method might be + // disrupting that ordering and just consider them + // pairwise to keep this simple). + if (!jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig) && + !jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig)) { + visited->items[childidx] = (void*)-1; + break; } - isect2 = NULL; } - ti = NULL; - if (*has_ambiguity) - break; } - if (*has_ambiguity) - break; } - JL_GC_POP(); } } - // If we're only returning possible matches, now filter out this method - // if its intersection is fully ambiguous in this SCC group. - if (!include_ambiguous) { - for (size_t i = depth - 1; i < stack->len; i++) { - size_t childidx = (size_t)stack->items[i]; - if ((size_t)visited->items[childidx] == 1) - continue; - jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(t, childidx); - jl_method_t *m = matc->method; - jl_value_t *ti = (jl_value_t*)matc->spec_types; - for (size_t j = depth - 1; j < stack->len; j++) { - if (i == j) - continue; - size_t idx2 = (size_t)stack->items[j]; - jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(t, idx2); - jl_method_t *m2 = matc2->method; - int subt2 = matc2->fully_covers != NOT_FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) - // if their intersection contributes to the ambiguity cycle - // and the contribution of m is fully ambiguous with the portion of the cycle from m2 - if (subt2 || jl_subtype((jl_value_t*)ti, m2->sig)) { - // but they aren't themselves simply ordered (here - // we don't consider that a third method might be - // disrupting that ordering and just consider them - // pairwise to keep this simple). - if (!jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig) && - !jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig)) { - assert(lim != -1 || *has_ambiguity); - visited->items[childidx] = (void*)1; - break; - } - } - } + // copy this cycle into the results + for (size_t i = depth - 1; i < stack->len; i++) { + size_t childidx = (size_t)stack->items[i]; + if ((size_t)visited->items[childidx] == 1) + continue; + if ((size_t)visited->items[childidx] != -1) { + assert(visited->items[childidx] == (void*)(2 + i)); + visited->items[childidx] = (void*)-1; + if (lim == -1 || result->len < lim) + arraylist_push(result, (void*)childidx); + else + return -1; } } + // now finally cleanup the stack while (stack->len >= depth) { size_t childidx = (size_t)arraylist_pop(stack); // always remove fully_covers matches after the first minmax ambiguity group is handled - jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(t, childidx); - if (matc->fully_covers != NOT_FULLY_COVERS) + //jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(t, childidx); + if (matc->fully_covers != NOT_FULLY_COVERS && !addambig) *found_minmax = 2; - if ((size_t)visited->items[childidx] != 1) { - assert(visited->items[childidx] == (void*)(2 + stack->len)); - visited->items[childidx] = (void*)1; - if (lim == -1 || result->len < lim) - arraylist_push(result, (void*)childidx); - else - return -1; - } + if (visited->items[childidx] != (void*)-1) + continue; + visited->items[childidx] = (void*)1; } return 0; } @@ -3702,18 +3765,22 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, } } if (len > 1) { - arraylist_t stack, visited, result; + arraylist_t stack, visited, result, allambig; arraylist_new(&result, lim != -1 && lim < len ? lim : len); arraylist_new(&stack, 0); arraylist_new(&visited, len); + arraylist_new(&allambig, len); arraylist_grow(&visited, len); memset(visited.items, 0, len * sizeof(size_t)); // if we had a minmax method (any subtypes), now may now be able to // quickly cleanup some of methods int found_minmax = 0; - if (minmax != NULL || (minmax_ambig && !include_ambiguous)) { + if (minmax != NULL) + found_minmax = 2; + else if (minmax_ambig && !include_ambiguous) found_minmax = 1; - } + if (ambig == NULL) // if we don't care about the result, set it now so we won't bother attempting to compute it accurately later + has_ambiguity = 1; for (i = 0; i < len; i++) { assert(visited.items[i] == (void*)0 || visited.items[i] == (void*)1); jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, i); @@ -3722,8 +3789,9 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, // by visiting it and it might be a bit costly continue; } - int child_cycle = sort_mlmatches((jl_array_t*)env.t, i, &visited, &stack, &result, lim == -1 || minmax == NULL ? lim : lim - 1, include_ambiguous, &has_ambiguity, &found_minmax); + int child_cycle = sort_mlmatches((jl_array_t*)env.t, i, &visited, &stack, &result, &allambig, lim == -1 || minmax == NULL ? lim : lim - 1, include_ambiguous, &has_ambiguity, &found_minmax); if (child_cycle == -1) { + arraylist_free(&allambig); arraylist_free(&visited); arraylist_free(&stack); arraylist_free(&result); @@ -3734,6 +3802,91 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, assert(stack.len == 0); assert(visited.items[i] == (void*)1); } + // now compute whether there were ambiguities left in this cycle + if (has_ambiguity == 0 && allambig.len > 0) { + if (lim == -1) { + // lim is over-approximated, so has_ambiguities is too + has_ambiguity = 1; + } + else { + // go back and find the additional ambiguous methods and temporary add them to the stack + // (potentially duplicating them from lower on the stack to here) + jl_value_t *ti = NULL; + jl_value_t *isect2 = NULL; + JL_GC_PUSH2(&ti, &isect2); + for (size_t i = 0; i < allambig.len; i++) { + size_t idx = (size_t)allambig.items[i]; + jl_method_match_t *matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, idx); + jl_method_t *m = matc->method; + int subt = matc->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m->sig) + for (size_t idx2 = 0; idx2 < jl_array_len(env.t); idx2++) { + if (idx2 == idx) + continue; + // laborious test, checking for existence and coverage of another method (m3) + // outside of the ambiguity group that dominates any ambiguous methods, + // and means we can ignore this for has_ambiguity + // (has_ambiguity is overestimated for lim==-1, since we don't compute skipped matches either) + // n.b. even if we skipped them earlier, they still might + // contribute to the ambiguities (due to lock of transitivity of + // morespecific over subtyping) + // TODO: we could improve this result by checking if the removal of some + // edge earlier means that this subgraph is now well-ordered and then be + // allowed to ignore these vertexes entirely here + jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(env.t, idx2); + jl_method_t *m2 = matc2->method; + int subt2 = matc2->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) + if (subt) { + ti = (jl_value_t*)matc2->spec_types; + isect2 = NULL; + } + else if (subt2) { + ti = (jl_value_t*)matc->spec_types; + isect2 = NULL; + } + else { + jl_type_intersection2((jl_value_t*)matc->spec_types, (jl_value_t*)matc2->spec_types, &ti, &isect2); + } + // if their intersection contributes to the ambiguity cycle + if (ti == jl_bottom_type) + continue; + // and they aren't themselves simply ordered + if (jl_type_morespecific((jl_value_t*)m->sig, (jl_value_t*)m2->sig) || + jl_type_morespecific((jl_value_t*)m2->sig, (jl_value_t*)m->sig)) + continue; + // now look for a third method m3 that dominated these and that fully covered this intersection already + size_t k; + for (k = 0; k < result.len; k++) { + size_t idx3 = (size_t)result.items[k]; + if (idx3 == idx || idx3 == idx2) { + has_ambiguity = 1; + break; + } + jl_method_match_t *matc3 = (jl_method_match_t*)jl_array_ptr_ref(env.t, idx3); + jl_method_t *m3 = matc3->method; + if ((jl_subtype(ti, m3->sig) || (isect2 && jl_subtype(isect2, m3->sig))) + && jl_type_morespecific((jl_value_t*)m3->sig, (jl_value_t*)m->sig) + && jl_type_morespecific((jl_value_t*)m3->sig, (jl_value_t*)m2->sig)) { + //if (jl_subtype(matc->spec_types, ti) || jl_subtype(matc->spec_types, matc3->m3->sig)) + // // check if it covered not only this intersection, but all intersections with matc + // // if so, we do not need to check all of them separately + // j = len; + break; + } + } + if (k == result.len) + has_ambiguity = 1; + isect2 = NULL; + ti = NULL; + if (has_ambiguity) + break; + } + if (has_ambiguity) + break; + } + JL_GC_POP(); + } + } + arraylist_free(&allambig); arraylist_free(&visited); arraylist_free(&stack); for (j = 0; j < result.len; j++) { diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 036cda53aa2a2..b0d1ff4b5237a 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -811,7 +811,7 @@ end let s = "CompletionFoo.test11(3, 4," c, r, res = test_complete(s) @test !res - @test length(c) == 4 + @test length(c) == 2 @test any(str->occursin("test11(x::$Int, y::$Int, z)", str), c) @test any(str->occursin("test11(::Any, ::Any, s::String)", str), c) end diff --git a/test/errorshow.jl b/test/errorshow.jl index b56710850e3f3..94722b803865f 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -92,8 +92,15 @@ method_c2(x::Int32, y::Float64) = true method_c2(x::Int32, y::Int32, z::Int32) = true method_c2(x::T, y::T, z::T) where {T<:Real} = true -Base.show_method_candidates(buf, Base.MethodError(method_c2,(1., 1., 2))) -@test occursin( "\n\nClosest candidates are:\n method_c2(!Matched::Int32, ::Float64, ::Any...)$cmod$cfile$(c2line+2)\n method_c2(::T, ::T, !Matched::T) where T<:Real$cmod$cfile$(c2line+5)\n method_c2(!Matched::Int32, ::Any...)$cmod$cfile$(c2line+1)\n ...\n", String(take!(buf))) +let s + Base.show_method_candidates(buf, Base.MethodError(method_c2, (1., 1., 2))) + s = String(take!(buf)) + @test occursin("\n\nClosest candidates are:\n ", s) + @test occursin("\n method_c2(!Matched::Int32, ::Float64, ::Any...)$cmod$cfile$(c2line+2)\n ", s) + @test occursin("\n method_c2(::T, ::T, !Matched::T) where T<:Real$cmod$cfile$(c2line+5)\n ", s) + @test occursin("\n method_c2(!Matched::Int32, ::Any...)$cmod$cfile$(c2line+1)\n ", s) + @test occursin("\n ...\n", s) +end c3line = @__LINE__() + 1 method_c3(x::Float64, y::Float64) = true From 0a05a5b05d382bdf38da7fdd0c069dff8bdc072c Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sat, 13 May 2023 23:18:09 +0900 Subject: [PATCH 755/775] improve type inference of `Base.aligned_sizeof` (#49801) This commit includes a bit of refactoring of `Base.aligned_sizeof` to make it more inference-friendly, especially in cases like `Base.aligned_sizeof(::Union{DataType,Union})`. In particular, it eliminates the chance of inference accounting for a method error of `datatype_alignment(::Union)` in the second branch. xref: --- base/reflection.jl | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index 83f3967b98dbe..97f1ed14c6729 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -374,15 +374,18 @@ LLT_ALIGN(x, sz) = (x + sz - 1) & -sz # amount of total space taken by T when stored in a container function aligned_sizeof(@nospecialize T::Type) @_foldable_meta - if isbitsunion(T) - _, sz, al = uniontype_layout(T) - return LLT_ALIGN(sz, al) + if isa(T, Union) + if allocatedinline(T) + # NOTE this check is equivalent to `isbitsunion(T)`, we can improve type + # inference in the second branch with the outer `isa(T, Union)` check + _, sz, al = uniontype_layout(T) + return LLT_ALIGN(sz, al) + end elseif allocatedinline(T) al = datatype_alignment(T) return LLT_ALIGN(Core.sizeof(T), al) - else - return Core.sizeof(Ptr{Cvoid}) end + return Core.sizeof(Ptr{Cvoid}) end gc_alignment(sz::Integer) = Int(ccall(:jl_alignment, Cint, (Csize_t,), sz)) From e10dbd0524a96bf14b667c81494bda7a3b1a901f Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Sat, 13 May 2023 11:26:05 -0400 Subject: [PATCH 756/775] Address reviews --- src/gc.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/gc.c b/src/gc.c index 64c33b7923048..b769ef40cc917 100644 --- a/src/gc.c +++ b/src/gc.c @@ -1189,11 +1189,16 @@ static void reset_thread_gc_counts(void) JL_NOTSAFEPOINT } } +static int64_t inc_live_bytes(int64_t inc) JL_NOTSAFEPOINT +{ + jl_timing_counter_inc(JL_TIMING_COUNTER_HeapSize, inc); + return live_bytes += inc; +} + void jl_gc_reset_alloc_count(void) JL_NOTSAFEPOINT { combine_thread_gc_counts(&gc_num); - live_bytes += (gc_num.deferred_alloc + gc_num.allocd); - jl_timing_counter_inc(JL_TIMING_COUNTER_HeapSize, gc_num.deferred_alloc + gc_num.allocd); + inc_live_bytes(gc_num.deferred_alloc + gc_num.allocd); gc_num.allocd = 0; gc_num.deferred_alloc = 0; reset_thread_gc_counts(); @@ -3095,6 +3100,10 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) { combine_thread_gc_counts(&gc_num); + // We separate the update of the graph from the update of live_bytes here + // so that the sweep shows a downward trend in memory usage. + jl_timing_counter_inc(JL_TIMING_COUNTER_HeapSize, gc_num.allocd); + jl_gc_markqueue_t *mq = &ptls->mark_queue; uint64_t gc_start_time = jl_hrtime(); @@ -3339,8 +3348,10 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) } last_live_bytes = live_bytes; + // Can't call inc_live_bytes here because we already added allocd + // to the graph earlier live_bytes += -gc_num.freed + gc_num.allocd; - jl_timing_counter_inc(JL_TIMING_COUNTER_HeapSize, -gc_num.freed + gc_num.allocd); + jl_timing_counter_dec(JL_TIMING_COUNTER_HeapSize, gc_num.freed); if (collection == JL_GC_AUTO) { //If we aren't freeing enough or are seeing lots and lots of pointers let it increase faster @@ -3772,8 +3783,7 @@ static void *gc_managed_realloc_(jl_ptls_t ptls, void *d, size_t sz, size_t olds if (jl_astaggedvalue(owner)->bits.gc == GC_OLD_MARKED) { ptls->gc_cache.perm_scanned_bytes += allocsz - oldsz; - live_bytes += allocsz - oldsz; - jl_timing_counter_inc(JL_TIMING_COUNTER_HeapSize, allocsz - oldsz); + inc_live_bytes(allocsz - oldsz); } else if (allocsz < oldsz) jl_atomic_store_relaxed(&ptls->gc_num.freed, From 60273a520a4fd10493565b38bedb9d04492480ee Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Sat, 13 May 2023 11:44:52 -0400 Subject: [PATCH 757/775] Add JIT memory counters --- src/cgmemmgr.cpp | 4 ++++ src/jitlayers.cpp | 17 +++++++++++++++++ src/timing.c | 3 +++ src/timing.h | 3 +++ 4 files changed, 27 insertions(+) diff --git a/src/cgmemmgr.cpp b/src/cgmemmgr.cpp index 15d28ff270c55..b627224e027a9 100644 --- a/src/cgmemmgr.cpp +++ b/src/cgmemmgr.cpp @@ -866,6 +866,8 @@ uint8_t *RTDyldMemoryManagerJL::allocateCodeSection(uintptr_t Size, code_allocated = true; #endif total_allocated += Size; + jl_timing_counter_inc(JL_TIMING_COUNTER_JITSize, Size); + jl_timing_counter_inc(JL_TIMING_COUNTER_JITCodeSize, Size); if (exe_alloc) return (uint8_t*)exe_alloc->alloc(Size, Alignment); return SectionMemoryManager::allocateCodeSection(Size, Alignment, SectionID, @@ -879,6 +881,8 @@ uint8_t *RTDyldMemoryManagerJL::allocateDataSection(uintptr_t Size, bool isReadOnly) { total_allocated += Size; + jl_timing_counter_inc(JL_TIMING_COUNTER_JITSize, Size); + jl_timing_counter_inc(JL_TIMING_COUNTER_JITDataSize, Size); if (!isReadOnly) return (uint8_t*)rw_alloc.alloc(Size, Alignment); if (ro_alloc) diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index c6aef2d35839c..2dbdaf296cab1 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -851,10 +851,27 @@ class JLMemoryUsagePlugin : public ObjectLinkingLayer::Plugin { jitlink::PassConfiguration &Config) override { Config.PostAllocationPasses.push_back([this](jitlink::LinkGraph &G) { size_t graph_size = 0; + size_t code_size = 0; + size_t data_size = 0; for (auto block : G.blocks()) { graph_size += block->getSize(); } + for (auto §ion : G.sections()) { + size_t secsize = 0; + for (auto block : section.blocks()) { + secsize += block->getSize(); + } + if ((section.getMemProt() & orc::MemProt::Exec) == orc::MemProt::None) { + data_size += secsize; + } else { + code_size += secsize; + } + graph_size += secsize; + } this->total_size.fetch_add(graph_size, std::memory_order_relaxed); + jl_timing_counter_inc(JL_TIMING_COUNTER_JITSize, graph_size); + jl_timing_counter_inc(JL_TIMING_COUNTER_JITCodeSize, code_size); + jl_timing_counter_inc(JL_TIMING_COUNTER_JITDataSize, data_size); return Error::success(); }); } diff --git a/src/timing.c b/src/timing.c index 8a6b3fdf94afe..db893e5b9d193 100644 --- a/src/timing.c +++ b/src/timing.c @@ -108,6 +108,9 @@ void jl_init_timing(void) // We reference these by enum indexing and then asking for the name, since that allows the compiler // to catch name mismatches. TracyCPlotConfig(jl_timing_counters[JL_TIMING_COUNTER_HeapSize].tracy_counter.name, TracyPlotFormatMemory, /* rectilinear */ 0, /* fill */ 1, /* color */ 0); + TracyCPlotConfig(jl_timing_counters[JL_TIMING_COUNTER_JITSize].tracy_counter.name, TracyPlotFormatMemory, /* rectilinear */ 0, /* fill */ 1, /* color */ 0); + TracyCPlotConfig(jl_timing_counters[JL_TIMING_COUNTER_JITCodeSize].tracy_counter.name, TracyPlotFormatMemory, /* rectilinear */ 0, /* fill */ 1, /* color */ 0); + TracyCPlotConfig(jl_timing_counters[JL_TIMING_COUNTER_JITDataSize].tracy_counter.name, TracyPlotFormatMemory, /* rectilinear */ 0, /* fill */ 1, /* color */ 0); #endif } diff --git a/src/timing.h b/src/timing.h index defae4fa2dc25..1c9d56a65e38d 100644 --- a/src/timing.h +++ b/src/timing.h @@ -194,6 +194,9 @@ void jl_timing_puts(jl_timing_block_t *cur_block, const char *str); #define JL_TIMING_COUNTERS \ X(Invalidations) \ X(HeapSize) \ + X(JITSize) \ + X(JITCodeSize) \ + X(JITDataSize) \ enum jl_timing_owners { From 6fbb9b7c6f0ee35ab9f8865ce78730b5959d618d Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Sat, 13 May 2023 11:50:56 -0400 Subject: [PATCH 758/775] Track loaded image size --- src/staticdata.c | 1 + src/timing.c | 1 + src/timing.h | 1 + 3 files changed, 3 insertions(+) diff --git a/src/staticdata.c b/src/staticdata.c index 353382f11a067..96c3c3d147856 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -3277,6 +3277,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl arraylist_push(&jl_linkage_blobs, (void*)image_base); arraylist_push(&jl_linkage_blobs, (void*)(image_base + sizeof_sysimg + sizeof(uintptr_t))); arraylist_push(&jl_image_relocs, (void*)relocs_base); + jl_timing_counter_inc(JL_TIMING_COUNTER_ImageSize, sizeof_sysimg + sizeof(uintptr_t)); rebuild_image_blob_tree(); // jl_printf(JL_STDOUT, "%ld blobs to link against\n", jl_linkage_blobs.len >> 1); diff --git a/src/timing.c b/src/timing.c index db893e5b9d193..3290e68ee9169 100644 --- a/src/timing.c +++ b/src/timing.c @@ -111,6 +111,7 @@ void jl_init_timing(void) TracyCPlotConfig(jl_timing_counters[JL_TIMING_COUNTER_JITSize].tracy_counter.name, TracyPlotFormatMemory, /* rectilinear */ 0, /* fill */ 1, /* color */ 0); TracyCPlotConfig(jl_timing_counters[JL_TIMING_COUNTER_JITCodeSize].tracy_counter.name, TracyPlotFormatMemory, /* rectilinear */ 0, /* fill */ 1, /* color */ 0); TracyCPlotConfig(jl_timing_counters[JL_TIMING_COUNTER_JITDataSize].tracy_counter.name, TracyPlotFormatMemory, /* rectilinear */ 0, /* fill */ 1, /* color */ 0); + TracyCPlotConfig(jl_timing_counters[JL_TIMING_COUNTER_ImageSize].tracy_counter.name, TracyPlotFormatMemory, /* rectilinear */ 0, /* fill */ 1, /* color */ 0); #endif } diff --git a/src/timing.h b/src/timing.h index 1c9d56a65e38d..13ac3c5a95de5 100644 --- a/src/timing.h +++ b/src/timing.h @@ -197,6 +197,7 @@ void jl_timing_puts(jl_timing_block_t *cur_block, const char *str); X(JITSize) \ X(JITCodeSize) \ X(JITDataSize) \ + X(ImageSize) \ enum jl_timing_owners { From ee86c0681d2f2271f2ccd12d32115de0c3441e51 Mon Sep 17 00:00:00 2001 From: Prem Chintalapudi Date: Sat, 13 May 2023 11:56:18 -0400 Subject: [PATCH 759/775] orc::MemProt -> jitlink::MemProt --- src/jitlayers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 2dbdaf296cab1..c8ea8d9a18448 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -861,7 +861,7 @@ class JLMemoryUsagePlugin : public ObjectLinkingLayer::Plugin { for (auto block : section.blocks()) { secsize += block->getSize(); } - if ((section.getMemProt() & orc::MemProt::Exec) == orc::MemProt::None) { + if ((section.getMemProt() & jitlink::MemProt::Exec) == jitlink::MemProt::None) { data_size += secsize; } else { code_size += secsize; From 7e1431f371f2dd5a41fc030152d5a92e4237f44b Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 13 May 2023 17:27:41 -0400 Subject: [PATCH 760/775] irinterp: Don't introduce invalid CFGs (#49797) This is yet another followup to #49692 and #49750. With the introduced change, we kill the CFG edge from the basic block with the discovered error to its successors. However, we have an invariant in the verifier that the CFG should always match the IR. Turns out this is for good reason, as we assume in a number of places (including, ironically in the irinterp) that a GotoNode/GotoIfNot terminator means that the BB has the corresponding number of successors in the IR. Fix all this by killing the rest of the basic block when we discover that it is unreachable and if possible introducing an unreachable node at the end. However, of course if the erroring statement is the fallthrough terminator itself, there is no space for an unreachable node. We fix this by tweaking the verification to allow this case, as its really no worse than the other problems with fall-through terminators (#41476), but of course it would be good to address that as part of a more general IR refactor. --- base/compiler/ssair/irinterp.jl | 15 ++++++++++----- base/compiler/ssair/verify.jl | 10 ++++++++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index 4aed080e57bb4..ba2587173c089 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -253,14 +253,19 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR inst = ir.stmts[idx][:inst] typ = ir.stmts[idx][:type] end - if idx == lstmt - process_terminator!(ir, inst, idx, bb, all_rets, bb_ip) && @goto residual_scan - (isa(inst, GotoNode) || isa(inst, GotoIfNot) || isa(inst, ReturnNode) || isexpr(inst, :enter)) && continue - end - if typ === Bottom && !isa(inst, PhiNode) + if typ === Bottom && !(isa(inst, PhiNode) || isa(inst, GotoNode) || isa(inst, GotoIfNot) || isa(inst, ReturnNode) || isexpr(inst, :enter)) kill_terminator_edges!(irsv, lstmt, bb) + if idx != lstmt + for idx2 in (idx+1:lstmt-1) + ir[SSAValue(idx2)] = nothing + end + ir[SSAValue(lstmt)][:inst] = ReturnNode() + end break end + if idx == lstmt + process_terminator!(ir, inst, idx, bb, all_rets, bb_ip) && @goto residual_scan + end end end @goto compute_rt diff --git a/base/compiler/ssair/verify.jl b/base/compiler/ssair/verify.jl index c281aa4b7786f..baa0e6e2235a6 100644 --- a/base/compiler/ssair/verify.jl +++ b/base/compiler/ssair/verify.jl @@ -174,8 +174,14 @@ function verify_ir(ir::IRCode, print::Bool=true, end isa(stmt, PhiNode) || break end - @verify_error "Block $idx successors ($(block.succs)), does not match fall-through terminator ($terminator)" - error("") + if isempty(block.succs) && ir.stmts[idx][:type] == Union{} + # Allow fallthrough terminators that are known to error to + # be removed from the CFG. Ideally we'd add an unreachable + # here, but that isn't always possible. + else + @verify_error "Block $idx successors ($(block.succs)), does not match fall-through terminator ($terminator)" + error("") + end end end end From 344f1f5a9cd5a1b55ab6f7a06aafb075cb66bade Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Sun, 14 May 2023 12:56:11 -0500 Subject: [PATCH 761/775] Fixups for the `reinterpret` docstring (#49807) --- base/essentials.jl | 6 +++--- base/reinterpretarray.jl | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/base/essentials.jl b/base/essentials.jl index e2035601f4fb5..06e2c3ea2ec87 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -542,11 +542,11 @@ unsafe_convert(::Type{T}, x::T) where {T<:Ptr} = x # to resolve ambiguity with unsafe_convert(::Type{P}, x::Ptr) where {P<:Ptr} = convert(P, x) """ - reinterpret(type, A) + reinterpret(type, x) -Change the type-interpretation of the binary data in the primitive type `A` +Change the type-interpretation of the binary data in the primitive value `x` to that of the primitive type `type`. -The size of `type` has to be the same as that of the type of `A`. +The size of `type` has to be the same as that of the type of `x`. For example, `reinterpret(Float32, UInt32(7))` interprets the 4 bytes corresponding to `UInt32(7)` as a [`Float32`](@ref). diff --git a/base/reinterpretarray.jl b/base/reinterpretarray.jl index ebcb743729160..2fc246f86fa96 100644 --- a/base/reinterpretarray.jl +++ b/base/reinterpretarray.jl @@ -26,7 +26,7 @@ struct ReinterpretArray{T,N,S,A<:AbstractArray{S},IsReshaped} <: AbstractArray{T global reinterpret - """ + @doc """ reinterpret(T::DataType, A::AbstractArray) Construct a view of the array with the same binary data as the given @@ -38,13 +38,13 @@ struct ReinterpretArray{T,N,S,A<:AbstractArray{S},IsReshaped} <: AbstractArray{T ```jldoctest julia> reinterpret(Float32, UInt32[1 2 3 4 5]) 1×5 reinterpret(Float32, ::Matrix{UInt32}): - 1.0f-45 3.0f-45 4.0f-45 6.0f-45 7.0f-45 + 1.0f-45 3.0f-45 4.0f-45 6.0f-45 7.0f-45 julia> reinterpret(Complex{Int}, 1:6) 3-element reinterpret(Complex{$Int}, ::UnitRange{$Int}): - 1 + 2im - 3 + 4im - 5 + 6im + 1 + 2im + 3 + 4im + 5 + 6im ``` """ function reinterpret(::Type{T}, a::A) where {T,N,S,A<:AbstractArray{S, N}} From e4924c51de668387c407a54f328e349ae882ac30 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Mon, 15 May 2023 09:58:29 +0200 Subject: [PATCH 762/775] add devdocs how to profile package precompilation with tracy (#49784) --- base/Base.jl | 2 ++ base/loading.jl | 1 - doc/src/devdocs/external_profilers.md | 13 +++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/base/Base.jl b/base/Base.jl index 87a8d94c866a2..06df2edb276fd 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -596,6 +596,8 @@ function __init__() ccall(:jl_set_peek_cond, Cvoid, (Ptr{Cvoid},), PROFILE_PRINT_COND[].handle) errormonitor(Threads.@spawn(profile_printing_listener())) end + # Prevent spawned Julia process from getting stuck waiting on Tracy to connect. + delete!(ENV, "JULIA_WAIT_FOR_TRACY") nothing end diff --git a/base/loading.jl b/base/loading.jl index 36a92855d2265..9cc2f13752dfb 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2173,7 +2173,6 @@ function create_expr_cache(pkg::PkgId, input::String, output::String, output_o:: $trace -`, "OPENBLAS_NUM_THREADS" => 1, - "JULIA_WAIT_FOR_TRACY" => nothing, "JULIA_NUM_THREADS" => 1), stderr = internal_stderr, stdout = internal_stdout), "w", stdout) diff --git a/doc/src/devdocs/external_profilers.md b/doc/src/devdocs/external_profilers.md index 239854a7d6800..956d66508fc89 100644 --- a/doc/src/devdocs/external_profilers.md +++ b/doc/src/devdocs/external_profilers.md @@ -53,6 +53,19 @@ JULIA_WAIT_FOR_TRACY=1 ./julia -e '...' The environment variable ensures that Julia waits until it has successfully connected to the Tracy profiler before continuing execution. Afterward, use the Tracy profiler UI, click `Connect`, and Julia execution should resume and profiling should start. +### Profiling package precompilation with Tracy + +To profile a package precompilation process it is easiest to explicitly call into `Base.compilecache` with the package you want to precompile: + +```julia +pkg = Base.identify_package("SparseArrays") +withenv("JULIA_WAIT_FOR_TRACY" => 1, "TRACY_PORT" => 9001) do + Base.compilecache(pkg) +end +``` + +Here, we use a custom port for tracy which makes it easier to find the correct client in the Tracy UI to connect to. + ### Adding metadata to zones The various `jl_timing_show_*` and `jl_timing_printf` functions can be used to attach a string (or strings) to a zone. For example, the trace zone for inference shows the method instance that is being inferred. From be33e665f9e5f32a6dcf7af4e4a4646a0da5d9a8 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Mon, 15 May 2023 16:04:28 +0200 Subject: [PATCH 763/775] Core.Compiler: remove unused variable `phi_ssas` (#49816) --- base/compiler/ssair/slot2ssa.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index 22e4be12a72cf..757fa1b98bedc 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -639,7 +639,6 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, phi_slots = Vector{Int}[Int[] for _ = 1:length(ir.cfg.blocks)] new_phi_nodes = Vector{NewPhiNode}[NewPhiNode[] for _ = 1:length(cfg.blocks)] - phi_ssas = SSAValue[] new_phic_nodes = IdDict{Int, Vector{NewPhiCNode}}() for (; leave_block) in catch_entry_blocks new_phic_nodes[leave_block] = NewPhiCNode[] From f7b0cf2e300f592ce34e986031c72126fd234e38 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Mon, 15 May 2023 10:05:37 -0400 Subject: [PATCH 764/775] fix cross-reference link in variables.md (#49779) --- doc/src/manual/variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/variables.md b/doc/src/manual/variables.md index 4d057a1ab4883..6c22719c1ce86 100644 --- a/doc/src/manual/variables.md +++ b/doc/src/manual/variables.md @@ -216,7 +216,7 @@ a [`mutable struct`](@ref). When you call a [function](@ref man-functions) in Julia, it behaves as if you *assigned* the argument values to new variable names corresponding to the function arguments, as discussed -in [Argument-Passing Behavior](@ref man-functions). (By [convention](@ref man-punctuation), +in [Argument-Passing Behavior](@ref man-argument-passing). (By [convention](@ref man-punctuation), functions that mutate one or more of their arguments have names ending with `!`.) From 9dd3090234268cdb942d481b5af720d1efb89a1a Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Mon, 15 May 2023 08:09:02 -0600 Subject: [PATCH 765/775] Fix thread safety in `atexit(f)`: Lock access to atexit_hooks (#49774) - atexit(f) mutates global shared state. - atexit(f) can be called anytime by any thread. - Accesses & mutations to global shared state must be locked if they can be accessed from multiple threads. Add unit test for thread safety of adding many atexit functions in parallel --- base/initdefs.jl | 3 ++- test/threads_exec.jl | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/base/initdefs.jl b/base/initdefs.jl index 89ebecaefbdc4..002984b83dd97 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -353,6 +353,7 @@ end const atexit_hooks = Callable[ () -> Filesystem.temp_cleanup_purge(force=true) ] +const _atexit_hooks_lock = ReentrantLock() """ atexit(f) @@ -374,7 +375,7 @@ calls `exit(n)`, then Julia will exit with the exit code corresponding to the last called exit hook that calls `exit(n)`. (Because exit hooks are called in LIFO order, "last called" is equivalent to "first registered".) """ -atexit(f::Function) = (pushfirst!(atexit_hooks, f); nothing) +atexit(f::Function) = Base.@lock _atexit_hooks_lock (pushfirst!(atexit_hooks, f); nothing) function _atexit(exitcode::Cint) while !isempty(atexit_hooks) diff --git a/test/threads_exec.jl b/test/threads_exec.jl index e8a81f7fc2683..9c7c524febeff 100644 --- a/test/threads_exec.jl +++ b/test/threads_exec.jl @@ -1070,4 +1070,24 @@ end end end +# issue #49746, thread safety in `atexit(f)` +@testset "atexit thread safety" begin + f = () -> nothing + before_len = length(Base.atexit_hooks) + @sync begin + for _ in 1:1_000_000 + Threads.@spawn begin + atexit(f) + end + end + end + @test length(Base.atexit_hooks) == before_len + 1_000_000 + @test all(hook -> hook === f, Base.atexit_hooks[1 : 1_000_000]) + + # cleanup + Base.@lock Base._atexit_hooks_lock begin + deleteat!(Base.atexit_hooks, 1:1_000_000) + end +end + end # main testset From 15d7bd872adc705aac7af15443514cb77063a0ab Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Mon, 15 May 2023 16:42:34 +0200 Subject: [PATCH 766/775] Simplify `mul!` dispatch (#49806) --- stdlib/LinearAlgebra/src/adjtrans.jl | 1 + stdlib/LinearAlgebra/src/matmul.jl | 117 +++++++++------------------ 2 files changed, 41 insertions(+), 77 deletions(-) diff --git a/stdlib/LinearAlgebra/src/adjtrans.jl b/stdlib/LinearAlgebra/src/adjtrans.jl index f6d8b33eb7639..2f5c5508e0ee3 100644 --- a/stdlib/LinearAlgebra/src/adjtrans.jl +++ b/stdlib/LinearAlgebra/src/adjtrans.jl @@ -97,6 +97,7 @@ inplace_adj_or_trans(::Type{<:Transpose}) = transpose! adj_or_trans_char(::T) where {T<:AbstractArray} = adj_or_trans_char(T) adj_or_trans_char(::Type{<:AbstractArray}) = 'N' adj_or_trans_char(::Type{<:Adjoint}) = 'C' +adj_or_trans_char(::Type{<:Adjoint{<:Real}}) = 'T' adj_or_trans_char(::Type{<:Transpose}) = 'T' Base.dataids(A::Union{Adjoint, Transpose}) = Base.dataids(A.parent) diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 7a7f615e6f3e3..170aacee6682f 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -70,23 +70,22 @@ end alpha::Number, beta::Number) = generic_matvecmul!(y, adj_or_trans_char(A), _parent(A), x, MulAddMul(alpha, beta)) # BLAS cases -@inline mul!(y::StridedVector{T}, A::StridedMaybeAdjOrTransVecOrMat{T}, x::StridedVector{T}, - alpha::Number, beta::Number) where {T<:BlasFloat} = - gemv!(y, adj_or_trans_char(A), _parent(A), x, alpha, beta) -# catch the real adjoint case and rewrap to transpose -@inline mul!(y::StridedVector{T}, adjA::Adjoint{<:Any,<:StridedVecOrMat{T}}, x::StridedVector{T}, - alpha::Number, beta::Number) where {T<:BlasReal} = - mul!(y, transpose(adjA.parent), x, alpha, beta) +# equal eltypes +@inline generic_matvecmul!(y::StridedVector{T}, tA, A::StridedVecOrMat{T}, x::StridedVector{T}, + _add::MulAddMul=MulAddMul()) where {T<:BlasFloat} = + gemv!(y, tA, _parent(A), x, _add.alpha, _add.beta) +# Real (possibly transposed) matrix times complex vector. +# Multiply the matrix with the real and imaginary parts separately +@inline generic_matvecmul!(y::StridedVector{Complex{T}}, tA, A::StridedVecOrMat{T}, x::StridedVector{Complex{T}}, + _add::MulAddMul=MulAddMul()) where {T<:BlasReal} = + gemv!(y, tA, _parent(A), x, _add.alpha, _add.beta) # Complex matrix times real vector. # Reinterpret the matrix as a real matrix and do real matvec computation. -@inline mul!(y::StridedVector{Complex{T}}, A::StridedVecOrMat{Complex{T}}, x::StridedVector{T}, - alpha::Number, beta::Number) where {T<:BlasReal} = - gemv!(y, 'N', A, x, alpha, beta) -# Real matrix times complex vector. -# Multiply the matrix with the real and imaginary parts separately -@inline mul!(y::StridedVector{Complex{T}}, A::StridedMaybeAdjOrTransMat{T}, x::StridedVector{Complex{T}}, - alpha::Number, beta::Number) where {T<:BlasReal} = - gemv!(y, A isa StridedArray ? 'N' : 'T', _parent(A), x, alpha, beta) +# works only in cooperation with BLAS when A is untransposed (tA == 'N') +# but that check is included in gemv! anyway +@inline generic_matvecmul!(y::StridedVector{Complex{T}}, tA, A::StridedVecOrMat{Complex{T}}, x::StridedVector{T}, + _add::MulAddMul=MulAddMul()) where {T<:BlasReal} = + gemv!(y, tA, _parent(A), x, _add.alpha, _add.beta) # Vector-Matrix multiplication (*)(x::AdjointAbsVec, A::AbstractMatrix) = (A'*x')' @@ -341,66 +340,26 @@ julia> lmul!(F.Q, B) """ lmul!(A, B) -# generic case -@inline mul!(C::StridedMatrix{T}, A::StridedMaybeAdjOrTransVecOrMat{T}, B::StridedMaybeAdjOrTransVecOrMat{T}, - alpha::Number, beta::Number) where {T<:BlasFloat} = - gemm_wrapper!(C, adj_or_trans_char(A), adj_or_trans_char(B), _parent(A), _parent(B), MulAddMul(alpha, beta)) - -# AtB & ABt (including B === A) -@inline function mul!(C::StridedMatrix{T}, tA::Transpose{<:Any,<:StridedVecOrMat{T}}, B::StridedVecOrMat{T}, - alpha::Number, beta::Number) where {T<:BlasFloat} - A = tA.parent - if A === B - return syrk_wrapper!(C, 'T', A, MulAddMul(alpha, beta)) - else - return gemm_wrapper!(C, 'T', 'N', A, B, MulAddMul(alpha, beta)) - end -end -@inline function mul!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, tB::Transpose{<:Any,<:StridedVecOrMat{T}}, - alpha::Number, beta::Number) where {T<:BlasFloat} - B = tB.parent - if A === B - return syrk_wrapper!(C, 'N', A, MulAddMul(alpha, beta)) - else - return gemm_wrapper!(C, 'N', 'T', A, B, MulAddMul(alpha, beta)) - end -end -# real adjoint cases, also needed for disambiguation -@inline mul!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, adjB::Adjoint{<:Any,<:StridedVecOrMat{T}}, - alpha::Number, beta::Number) where {T<:BlasReal} = - mul!(C, A, transpose(adjB.parent), alpha, beta) -@inline mul!(C::StridedMatrix{T}, adjA::Adjoint{<:Any,<:StridedVecOrMat{T}}, B::StridedVecOrMat{T}, - alpha::Real, beta::Real) where {T<:BlasReal} = - mul!(C, transpose(adjA.parent), B, alpha, beta) - -# AcB & ABc (including B === A) -@inline function mul!(C::StridedMatrix{T}, adjA::Adjoint{<:Any,<:StridedVecOrMat{T}}, B::StridedVecOrMat{T}, - alpha::Number, beta::Number) where {T<:BlasComplex} - A = adjA.parent - if A === B - return herk_wrapper!(C, 'C', A, MulAddMul(alpha, beta)) +@inline function generic_matmatmul!(C::StridedMatrix{T}, tA, tB, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, + _add::MulAddMul=MulAddMul()) where {T<:BlasFloat} + if tA == 'T' && tB == 'N' && A === B + return syrk_wrapper!(C, 'T', A, _add) + elseif tA == 'N' && tB == 'T' && A === B + return syrk_wrapper!(C, 'N', A, _add) + elseif tA == 'C' && tB == 'N' && A === B + return herk_wrapper!(C, 'C', A, _add) + elseif tA == 'N' && tB == 'C' && A === B + return herk_wrapper!(C, 'N', A, _add) else - return gemm_wrapper!(C, 'C', 'N', A, B, MulAddMul(alpha, beta)) - end -end -@inline function mul!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, adjB::Adjoint{<:Any,<:StridedVecOrMat{T}}, - alpha::Number, beta::Number) where {T<:BlasComplex} - B = adjB.parent - if A === B - return herk_wrapper!(C, 'N', A, MulAddMul(alpha, beta)) - else - return gemm_wrapper!(C, 'N', 'C', A, B, MulAddMul(alpha, beta)) + return gemm_wrapper!(C, tA, tB, A, B, _add) end end # Complex matrix times (transposed) real matrix. Reinterpret the first matrix to real for efficiency. -@inline mul!(C::StridedMatrix{Complex{T}}, A::StridedMaybeAdjOrTransVecOrMat{Complex{T}}, B::StridedMaybeAdjOrTransVecOrMat{T}, - alpha::Number, beta::Number) where {T<:BlasReal} = - gemm_wrapper!(C, adj_or_trans_char(A), adj_or_trans_char(B), _parent(A), _parent(B), MulAddMul(alpha, beta)) -# catch the real adjoint case and interpret it as a transpose -@inline mul!(C::StridedMatrix{Complex{T}}, A::StridedVecOrMat{Complex{T}}, adjB::Adjoint{<:Any,<:StridedVecOrMat{T}}, - alpha::Number, beta::Number) where {T<:BlasReal} = - mul!(C, A, transpose(adjB.parent), alpha, beta) +@inline function generic_matmatmul!(C::StridedVecOrMat{Complex{T}}, tA, tB, A::StridedVecOrMat{Complex{T}}, B::StridedVecOrMat{T}, + _add::MulAddMul=MulAddMul()) where {T<:BlasReal} + gemm_wrapper!(C, tA, tB, A, B, _add) +end # Supporting functions for matrix multiplication @@ -438,7 +397,7 @@ function gemv!(y::StridedVector{T}, tA::AbstractChar, A::StridedVecOrMat{T}, x:: !iszero(stride(x, 1)) # We only check input's stride here. return BLAS.gemv!(tA, alpha, A, x, beta, y) else - return generic_matvecmul!(y, tA, A, x, MulAddMul(α, β)) + return _generic_matvecmul!(y, tA, A, x, MulAddMul(α, β)) end end @@ -459,7 +418,7 @@ function gemv!(y::StridedVector{Complex{T}}, tA::AbstractChar, A::StridedVecOrMa BLAS.gemv!(tA, alpha, reinterpret(T, A), x, beta, reinterpret(T, y)) return y else - return generic_matvecmul!(y, tA, A, x, MulAddMul(α, β)) + return _generic_matvecmul!(y, tA, A, x, MulAddMul(α, β)) end end @@ -482,7 +441,7 @@ function gemv!(y::StridedVector{Complex{T}}, tA::AbstractChar, A::StridedVecOrMa BLAS.gemv!(tA, alpha, A, xfl[2, :], beta, yfl[2, :]) return y else - return generic_matvecmul!(y, tA, A, x, MulAddMul(α, β)) + return _generic_matvecmul!(y, tA, A, x, MulAddMul(α, β)) end end @@ -609,7 +568,7 @@ function gemm_wrapper!(C::StridedVecOrMat{T}, tA::AbstractChar, tB::AbstractChar stride(C, 2) >= size(C, 1)) return BLAS.gemm!(tA, tB, alpha, A, B, beta, C) end - generic_matmatmul!(C, tA, tB, A, B, _add) + _generic_matmatmul!(C, tA, tB, A, B, _add) end function gemm_wrapper!(C::StridedVecOrMat{Complex{T}}, tA::AbstractChar, tB::AbstractChar, @@ -652,7 +611,7 @@ function gemm_wrapper!(C::StridedVecOrMat{Complex{T}}, tA::AbstractChar, tB::Abs BLAS.gemm!(tA, tB, alpha, reinterpret(T, A), B, beta, reinterpret(T, C)) return C end - generic_matmatmul!(C, tA, tB, A, B, _add) + _generic_matmatmul!(C, tA, tB, A, B, _add) end # blas.jl defines matmul for floats; other integer and mixed precision @@ -686,8 +645,12 @@ end # NOTE: the generic version is also called as fallback for # strides != 1 cases -function generic_matvecmul!(C::AbstractVector{R}, tA, A::AbstractVecOrMat, B::AbstractVector, - _add::MulAddMul = MulAddMul()) where R +generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, + _add::MulAddMul = MulAddMul()) = + _generic_matvecmul!(C, tA, A, B, _add) + +function _generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, + _add::MulAddMul = MulAddMul()) require_one_based_indexing(C, A, B) mB = length(B) mA, nA = lapack_size(tA, A) From 1f161b49532a1d98247a7ccb2d303dee6b5fe246 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Mon, 15 May 2023 16:44:03 +0200 Subject: [PATCH 767/775] only time inference if any work is actually done (#49817) --- src/gf.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gf.c b/src/gf.c index 22bd0add82664..6fcaf6ce9a341 100644 --- a/src/gf.c +++ b/src/gf.c @@ -344,7 +344,6 @@ jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_a // if inference doesn't occur (or can't finish), returns NULL instead jl_code_info_t *jl_type_infer(jl_method_instance_t *mi, size_t world, int force) { - JL_TIMING(INFERENCE, INFERENCE); if (jl_typeinf_func == NULL) return NULL; jl_task_t *ct = jl_current_task; @@ -361,7 +360,7 @@ jl_code_info_t *jl_type_infer(jl_method_instance_t *mi, size_t world, int force) #ifdef ENABLE_INFERENCE if (mi->inInference && !force) return NULL; - + JL_TIMING(INFERENCE, INFERENCE); jl_value_t **fargs; JL_GC_PUSHARGS(fargs, 3); fargs[0] = (jl_value_t*)jl_typeinf_func; From 76fbd61717ea64acae49f5c79080769edf98bde1 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 15 May 2023 14:19:26 -0400 Subject: [PATCH 768/775] jitlayers: move the local dylibs ahead of the global one --- src/jitlayers.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 759828cef3321..bef04b8aaa5f8 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -1469,8 +1469,8 @@ void JuliaOJIT::addModule(orc::ThreadSafeModule TSM) JL_JITSymbol JuliaOJIT::findSymbol(StringRef Name, bool ExportedSymbolsOnly) { - orc::JITDylib* SearchOrders[2] = {&GlobalJD, &JD}; - ArrayRef SearchOrder = makeArrayRef(&SearchOrders[ExportedSymbolsOnly ? 0 : 1], ExportedSymbolsOnly ? 2 : 1); + orc::JITDylib* SearchOrders[2] = {&JD, &GlobalJD}; + ArrayRef SearchOrder = makeArrayRef(&SearchOrders[0], ExportedSymbolsOnly ? 2 : 1); auto Sym = ES.lookup(SearchOrder, Name); if (Sym) return *Sym; From b9806d6e63ad1910455cba6a0edb1e000a8b3560 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Mon, 15 May 2023 16:47:14 -0400 Subject: [PATCH 769/775] irinterp: Don't try to rekill fall-through terminators (#49815) If a fall-through terminator was already Bottom, we should not attempt to rekill the successor edge, because it was already deleted. Yet another fix in the #49692, #49750, #49797 series, which is turning out to be quite a rabit hole. Also fix a typo in the verifer tweak where we were looking at the BB idx rather than the terminator idx. --- base/compiler/ssair/irinterp.jl | 6 +++++- base/compiler/ssair/verify.jl | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index ba2587173c089..8d75ad3948ee2 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -247,13 +247,17 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR any_refined = true delete!(ssa_refined, idx) end + is_terminator_or_phi = isa(inst, PhiNode) || isa(inst, GotoNode) || isa(inst, GotoIfNot) || isa(inst, ReturnNode) || isexpr(inst, :enter) + if typ === Bottom && (idx != lstmt || !is_terminator_or_phi) + continue + end if any_refined && reprocess_instruction!(interp, idx, bb, inst, typ, irsv, extra_reprocess) push!(ssa_refined, idx) inst = ir.stmts[idx][:inst] typ = ir.stmts[idx][:type] end - if typ === Bottom && !(isa(inst, PhiNode) || isa(inst, GotoNode) || isa(inst, GotoIfNot) || isa(inst, ReturnNode) || isexpr(inst, :enter)) + if typ === Bottom && !is_terminator_or_phi kill_terminator_edges!(irsv, lstmt, bb) if idx != lstmt for idx2 in (idx+1:lstmt-1) diff --git a/base/compiler/ssair/verify.jl b/base/compiler/ssair/verify.jl index baa0e6e2235a6..bf06d6bb3e523 100644 --- a/base/compiler/ssair/verify.jl +++ b/base/compiler/ssair/verify.jl @@ -174,12 +174,14 @@ function verify_ir(ir::IRCode, print::Bool=true, end isa(stmt, PhiNode) || break end - if isempty(block.succs) && ir.stmts[idx][:type] == Union{} + termidx = last(block.stmts) + stmttyp = ir.stmts[termidx][:type] + if isempty(block.succs) && stmttyp == Union{} # Allow fallthrough terminators that are known to error to # be removed from the CFG. Ideally we'd add an unreachable # here, but that isn't always possible. else - @verify_error "Block $idx successors ($(block.succs)), does not match fall-through terminator ($terminator)" + @verify_error "Block $idx successors ($(block.succs)), does not match fall-through terminator %$termidx ($terminator)::$stmttyp" error("") end end From ed8580ad3ae1d1ee46b84ec7bbe69ac9b37befca Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Wed, 17 May 2023 01:03:35 +0000 Subject: [PATCH 770/775] WIP --- src/gc-common.c | 6 +++--- src/gc.c | 25 +++++++++++++++++++++++++ src/gc.h | 2 +- src/mmtk-gc.c | 10 ++++++++-- src/partr.c | 25 ------------------------- 5 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/gc-common.c b/src/gc-common.c index 0c6138231e24f..8fd368f9e0875 100644 --- a/src/gc-common.c +++ b/src/gc-common.c @@ -46,7 +46,7 @@ memsize_t max_total_memory = (memsize_t) 2 * 1024 * 1024 * 1024; // finalizers // --- -static uint64_t finalizer_rngState[JL_RNG_SIZE]; +uint64_t finalizer_rngState[JL_RNG_SIZE]; void jl_rng_split(uint64_t dst[JL_RNG_SIZE], uint64_t src[JL_RNG_SIZE]) JL_NOTSAFEPOINT; @@ -259,7 +259,7 @@ static int64_t inc_live_bytes(int64_t inc) JL_NOTSAFEPOINT void jl_gc_reset_alloc_count(void) JL_NOTSAFEPOINT { combine_thread_gc_counts(&gc_num); - live_bytes += (gc_num.deferred_alloc + gc_num.allocd); + inc_live_bytes(gc_num.deferred_alloc + gc_num.allocd); gc_num.allocd = 0; gc_num.deferred_alloc = 0; reset_thread_gc_counts(); @@ -501,7 +501,7 @@ void *gc_managed_realloc_(jl_ptls_t ptls, void *d, size_t sz, size_t oldsz, // TODO: not needed? gc_cache.*? if (jl_astaggedvalue(owner)->bits.gc == GC_OLD_MARKED) { ptls->gc_cache.perm_scanned_bytes += allocsz - oldsz; - live_bytes += allocsz - oldsz; + inc_live_bytes(allocsz - oldsz); } else if (allocsz < oldsz) jl_atomic_store_relaxed(&ptls->gc_num.freed, diff --git a/src/gc.c b/src/gc.c index 4a87980ae3924..d6d3955bdb68f 100644 --- a/src/gc.c +++ b/src/gc.c @@ -3677,6 +3677,31 @@ JL_DLLEXPORT jl_value_t *jl_gc_internal_obj_base_ptr(void *p) return NULL; } +// gc thread function +void jl_gc_threadfun(void *arg) +{ + jl_threadarg_t *targ = (jl_threadarg_t*)arg; + + // initialize this thread (set tid and create heap) + jl_ptls_t ptls = jl_init_threadtls(targ->tid); + + // wait for all threads + jl_gc_state_set(ptls, JL_GC_STATE_WAITING, 0); + uv_barrier_wait(targ->barrier); + + // free the thread argument here + free(targ); + + while (1) { + uv_mutex_lock(&gc_threads_lock); + while (jl_atomic_load(&gc_n_threads_marking) == 0) { + uv_cond_wait(&gc_threads_cond, &gc_threads_lock); + } + uv_mutex_unlock(&gc_threads_lock); + gc_mark_loop_parallel(ptls, 0); + } +} + // added for MMTk integration void enable_collection(void) { diff --git a/src/gc.h b/src/gc.h index a340a1ec0b545..3def80327ceda 100644 --- a/src/gc.h +++ b/src/gc.h @@ -42,7 +42,7 @@ extern void jl_finalize_th(jl_task_t *ct, jl_value_t *o); extern jl_weakref_t *jl_gc_new_weakref_th(jl_ptls_t ptls, jl_value_t *value); extern jl_value_t *jl_gc_big_alloc_inner(jl_ptls_t ptls, size_t sz); extern jl_value_t *jl_gc_pool_alloc_inner(jl_ptls_t ptls, int pool_offset, int osize); -extern void jl_rng_split(uint64_t to[4], uint64_t from[4]); +extern void jl_rng_split(uint64_t to[JL_RNG_SIZE], uint64_t from[JL_RNG_SIZE]); extern void gc_premark(jl_ptls_t ptls2); extern void *gc_managed_realloc_(jl_ptls_t ptls, void *d, size_t sz, size_t oldsz, int isaligned, jl_value_t *owner, int8_t can_collect); diff --git a/src/mmtk-gc.c b/src/mmtk-gc.c index b354d287baa14..08d0bed7b4304 100644 --- a/src/mmtk-gc.c +++ b/src/mmtk-gc.c @@ -229,7 +229,7 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) jl_atomic_fetch_add((_Atomic(uint64_t)*)&gc_num.deferred_alloc, localbytes); return; } - handle_user_collection_request(ptls); + handle_user_collection_request(ptls, collection); } // Per-thread initialization @@ -275,7 +275,7 @@ void jl_gc_init(void) if (jl_options.heap_size_hint) jl_gc_set_max_memory(jl_options.heap_size_hint); - JL_MUTEX_INIT(&heapsnapshot_lock); + JL_MUTEX_INIT(&heapsnapshot_lock, "heapsnapshot_lock"); uv_mutex_init(&gc_perm_lock); gc_num.interval = default_collect_interval; @@ -480,6 +480,12 @@ void objprofile_reset(void) { } +// gc thread function +void jl_gc_threadfun(void *arg) +{ + unreachable(); +} + JL_DLLEXPORT void jl_gc_array_ptr_copy(jl_array_t *dest, void **dest_p, jl_array_t *src, void **src_p, ssize_t n) JL_NOTSAFEPOINT { jl_ptls_t ptls = jl_current_task->ptls; diff --git a/src/partr.c b/src/partr.c index 403f911b1284f..2c729add629e2 100644 --- a/src/partr.c +++ b/src/partr.c @@ -108,31 +108,6 @@ void jl_init_threadinginfra(void) void JL_NORETURN jl_finish_task(jl_task_t *t); -// gc thread function -void jl_gc_threadfun(void *arg) -{ - jl_threadarg_t *targ = (jl_threadarg_t*)arg; - - // initialize this thread (set tid and create heap) - jl_ptls_t ptls = jl_init_threadtls(targ->tid); - - // wait for all threads - jl_gc_state_set(ptls, JL_GC_STATE_WAITING, 0); - uv_barrier_wait(targ->barrier); - - // free the thread argument here - free(targ); - - while (1) { - uv_mutex_lock(&gc_threads_lock); - while (jl_atomic_load(&gc_n_threads_marking) == 0) { - uv_cond_wait(&gc_threads_cond, &gc_threads_lock); - } - uv_mutex_unlock(&gc_threads_lock); - gc_mark_loop_parallel(ptls, 0); - } -} - // thread function: used by all mutator threads except the main thread void jl_threadfun(void *arg) { From ec37ebe24b43973746e4730572c00365dd4edf5e Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 18 May 2023 04:45:30 +0000 Subject: [PATCH 771/775] Minor fix --- src/gc-common.c | 2 ++ src/gc.c | 2 -- src/llvm-late-gc-lowering.cpp | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gc-common.c b/src/gc-common.c index 8fd368f9e0875..cfb83c08a7a6b 100644 --- a/src/gc-common.c +++ b/src/gc-common.c @@ -6,6 +6,8 @@ jl_gc_num_t gc_num = {0}; size_t last_long_collect_interval; int gc_n_threads; jl_ptls_t* gc_all_tls_states; +// `tid` of first GC thread +int gc_first_tid; int64_t live_bytes = 0; diff --git a/src/gc.c b/src/gc.c index d6d3955bdb68f..932bb1d97c6db 100644 --- a/src/gc.c +++ b/src/gc.c @@ -17,8 +17,6 @@ extern "C" { _Atomic(int) gc_n_threads_marking; // `tid` of mutator thread that triggered GC _Atomic(int) gc_master_tid; -// `tid` of first GC thread -int gc_first_tid; // Mutex/cond used to synchronize sleep/wakeup of GC threads uv_mutex_t gc_threads_lock; uv_cond_t gc_threads_cond; diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index d812146027eba..4877565c61495 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -2513,6 +2513,7 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S, bool *CFGModified) { } IRBuilder<> builder(CI); builder.SetCurrentDebugLocation(CI->getDebugLoc()); +#ifndef MMTK_GC auto parBits = builder.CreateAnd(EmitLoadTag(builder, T_size, parent), 3); auto parOldMarked = builder.CreateICmpEQ(parBits, ConstantInt::get(T_size, 3)); auto mayTrigTerm = SplitBlockAndInsertIfThen(parOldMarked, CI, false); From 34930e502fac00cac2698ecf14564ff764b03527 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 18 May 2023 05:58:48 +0000 Subject: [PATCH 772/775] Use JL_DLLIMPORT for MMTk functions. Update uses of MMTk functions with prefix mmtk_ --- src/init.c | 2 +- src/julia.h | 4 +-- src/julia_internal.h | 13 +++---- src/llvm-late-gc-lowering.cpp | 2 +- src/llvm-pass-helpers.cpp | 64 ++++++++++++++++++++++------------- src/mmtk-gc.c | 38 +++++++++++++-------- src/symbol.c | 2 +- 7 files changed, 76 insertions(+), 49 deletions(-) diff --git a/src/init.c b/src/init.c index a076b9d0fbed5..9c18a60eb8b06 100644 --- a/src/init.c +++ b/src/init.c @@ -825,7 +825,7 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) jl_ptls_t ptls = jl_init_threadtls(0); #ifdef MMTK_GC - initialize_collection((void *)ptls); + mmtk_initialize_collection((void *)ptls); #endif #pragma GCC diagnostic push #if defined(_COMPILER_GCC_) && __GNUC__ >= 12 diff --git a/src/julia.h b/src/julia.h index 5f692d1f4de2d..7950eca3e0f1d 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2383,7 +2383,7 @@ extern JL_DLLEXPORT int jl_default_debug_info_kind; extern void mmtk_object_reference_write_post(void* mutator, const void* parent, const void* ptr); extern void mmtk_object_reference_write_slow(void* mutator, const void* parent, const void* ptr); extern const uint8_t MMTK_NEEDS_WRITE_BARRIER; -extern const uint8_t OBJECT_BARRIER; +extern const uint8_t MMTK_OBJECT_BARRIER; extern const void* MMTK_SIDE_LOG_BIT_BASE_ADDRESS; // Directly call into MMTk for write barrier (debugging only) @@ -2397,7 +2397,7 @@ STATIC_INLINE void mmtk_gc_wb_full(const void *parent, const void *ptr) JL_NOTSA // Inlined fastpath STATIC_INLINE void mmtk_gc_wb_fast(const void *parent, const void *ptr) JL_NOTSAFEPOINT { - if (MMTK_NEEDS_WRITE_BARRIER == OBJECT_BARRIER) { + if (MMTK_NEEDS_WRITE_BARRIER == MMTK_OBJECT_BARRIER) { intptr_t addr = (intptr_t) (void*) parent; uint8_t* meta_addr = (uint8_t*) (MMTK_SIDE_LOG_BIT_BASE_ADDRESS) + (addr >> 6); intptr_t shift = (addr >> 3) & 0b111; diff --git a/src/julia_internal.h b/src/julia_internal.h index d89de5753c380..5e5b0ebb76e41 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -333,15 +333,16 @@ extern jl_array_t *jl_all_methods JL_GLOBALLY_ROOTED; JL_DLLEXPORT extern int jl_lineno; JL_DLLEXPORT extern const char *jl_filename; -void enable_collection(void); -void disable_collection(void); +extern void enable_collection(void); +extern void disable_collection(void); jl_value_t *jl_gc_pool_alloc_noinline(jl_ptls_t ptls, int pool_offset, int osize); jl_value_t *jl_gc_big_alloc_noinline(jl_ptls_t ptls, size_t allocsz); #ifdef MMTK_GC -JL_DLLEXPORT jl_value_t *jl_mmtk_gc_alloc_default(jl_ptls_t ptls, int pool_offset, int osize, void* ty); -JL_DLLEXPORT jl_value_t *jl_mmtk_gc_alloc_big(jl_ptls_t ptls, size_t allocsz); -extern void post_alloc(void* mutator, void* obj, size_t bytes, int allocator); +JL_DLLIMPORT jl_value_t *jl_mmtk_gc_alloc_default(jl_ptls_t ptls, int pool_offset, int osize, void* ty); +JL_DLLIMPORT jl_value_t *jl_mmtk_gc_alloc_big(jl_ptls_t ptls, size_t allocsz); +JL_DLLIMPORT extern void mmtk_post_alloc(void* mutator, void* obj, size_t bytes, int allocator); +JL_DLLIMPORT extern void mmtk_initialize_collection(void* tls); #endif // MMTK_GC JL_DLLEXPORT int jl_gc_classify_pools(size_t sz, int *osize) JL_NOTSAFEPOINT; extern uv_mutex_t gc_perm_lock; @@ -549,7 +550,7 @@ STATIC_INLINE jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT o->header = tag | GC_OLD_MARKED; #ifdef MMTK_GC jl_ptls_t ptls = jl_current_task->ptls; - post_alloc(ptls->mmtk_mutator_ptr, jl_valueof(o), allocsz, 1); + mmtk_post_alloc(ptls->mmtk_mutator_ptr, jl_valueof(o), allocsz, 1); #endif return jl_valueof(o); } diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index 4877565c61495..2bf340be13b62 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -2543,7 +2543,7 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S, bool *CFGModified) { // But for other MMTk plans, we need to be careful. const bool INLINE_WRITE_BARRIER = true; if (CI->getCalledOperand() == write_barrier_func) { - if (MMTK_NEEDS_WRITE_BARRIER == OBJECT_BARRIER) { + if (MMTK_NEEDS_WRITE_BARRIER == MMTK_OBJECT_BARRIER) { if (INLINE_WRITE_BARRIER) { auto i8_ty = Type::getInt8Ty(F.getContext()); auto intptr_ty = T_size; diff --git a/src/llvm-pass-helpers.cpp b/src/llvm-pass-helpers.cpp index 1aa7346516f62..df3ffa5e27486 100644 --- a/src/llvm-pass-helpers.cpp +++ b/src/llvm-pass-helpers.cpp @@ -243,11 +243,13 @@ namespace jl_intrinsics { #ifdef MMTK_GC const IntrinsicDescription writeBarrier1( WRITE_BARRIER_1_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx); auto intrinsic = Function::Create( FunctionType::get( - Type::getVoidTy(context.getLLVMContext()), - { context.T_prjlvalue }, + Type::getVoidTy(ctx), + { T_prjlvalue }, false), Function::ExternalLinkage, WRITE_BARRIER_1_NAME); @@ -256,11 +258,13 @@ namespace jl_intrinsics { }); const IntrinsicDescription writeBarrier2( WRITE_BARRIER_2_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx); auto intrinsic = Function::Create( FunctionType::get( - Type::getVoidTy(context.getLLVMContext()), - { context.T_prjlvalue, context.T_prjlvalue }, + Type::getVoidTy(ctx), + { T_prjlvalue, T_prjlvalue }, false), Function::ExternalLinkage, WRITE_BARRIER_2_NAME); @@ -269,11 +273,13 @@ namespace jl_intrinsics { }); const IntrinsicDescription writeBarrier1Slow( WRITE_BARRIER_1_SLOW_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx); auto intrinsic = Function::Create( FunctionType::get( - Type::getVoidTy(context.getLLVMContext()), - { context.T_prjlvalue }, + Type::getVoidTy(ctx), + { T_prjlvalue }, false), Function::ExternalLinkage, WRITE_BARRIER_1_SLOW_NAME); @@ -282,11 +288,13 @@ namespace jl_intrinsics { }); const IntrinsicDescription writeBarrier2Slow( WRITE_BARRIER_2_SLOW_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx); auto intrinsic = Function::Create( FunctionType::get( - Type::getVoidTy(context.getLLVMContext()), - { context.T_prjlvalue, context.T_prjlvalue }, + Type::getVoidTy(ctx), + { T_prjlvalue, T_prjlvalue }, false), Function::ExternalLinkage, WRITE_BARRIER_2_SLOW_NAME); @@ -379,11 +387,13 @@ namespace jl_well_known { #ifdef MMTK_GC const WellKnownFunctionDescription GCWriteBarrier1( GC_WB_1_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx); auto func = Function::Create( FunctionType::get( - Type::getVoidTy(context.getLLVMContext()), - { context.T_prjlvalue }, + Type::getVoidTy(ctx), + { T_prjlvalue }, false), Function::ExternalLinkage, GC_WB_1_NAME); @@ -393,11 +403,13 @@ namespace jl_well_known { const WellKnownFunctionDescription GCWriteBarrier2( GC_WB_2_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx); auto func = Function::Create( FunctionType::get( - Type::getVoidTy(context.getLLVMContext()), - { context.T_prjlvalue, context.T_prjlvalue }, + Type::getVoidTy(ctx), + { T_prjlvalue, T_prjlvalue }, false), Function::ExternalLinkage, GC_WB_2_NAME); @@ -407,11 +419,13 @@ namespace jl_well_known { const WellKnownFunctionDescription GCWriteBarrier1Slow( GC_WB_1_SLOW_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx); auto func = Function::Create( FunctionType::get( - Type::getVoidTy(context.getLLVMContext()), - { context.T_prjlvalue }, + Type::getVoidTy(ctx), + { T_prjlvalue }, false), Function::ExternalLinkage, GC_WB_1_SLOW_NAME); @@ -421,11 +435,13 @@ namespace jl_well_known { const WellKnownFunctionDescription GCWriteBarrier2Slow( GC_WB_2_SLOW_NAME, - [](const JuliaPassContext &context) { + [](Type *T_size) { + auto &ctx = T_size->getContext(); + auto T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx); auto func = Function::Create( FunctionType::get( - Type::getVoidTy(context.getLLVMContext()), - { context.T_prjlvalue, context.T_prjlvalue }, + Type::getVoidTy(ctx), + { T_prjlvalue, T_prjlvalue }, false), Function::ExternalLinkage, GC_WB_2_SLOW_NAME); diff --git a/src/mmtk-gc.c b/src/mmtk-gc.c index b3646cb16dacf..6d232919a55f8 100644 --- a/src/mmtk-gc.c +++ b/src/mmtk-gc.c @@ -72,7 +72,7 @@ JL_DLLEXPORT void jl_gc_run_pending_finalizers(jl_task_t *ct) JL_DLLEXPORT void jl_gc_add_ptr_finalizer(jl_ptls_t ptls, jl_value_t *v, void *f) JL_NOTSAFEPOINT { - register_finalizer(v, f, 1); + mmtk_register_finalizer(v, f, 1); } // schedule f(v) to call at the next quiescent interval (aka after the next safepoint/region on all threads) @@ -87,13 +87,13 @@ JL_DLLEXPORT void jl_gc_add_finalizer_th(jl_ptls_t ptls, jl_value_t *v, jl_funct jl_gc_add_ptr_finalizer(ptls, v, jl_unbox_voidpointer(f)); } else { - register_finalizer(v, f, 0); + mmtk_register_finalizer(v, f, 0); } } JL_DLLEXPORT void jl_finalize_th(jl_task_t *ct, jl_value_t *o) { - run_finalizers_for_obj(o); + mmtk_run_finalizers_for_obj(o); } void jl_gc_run_all_finalizers(jl_task_t *ct) @@ -103,7 +103,7 @@ void jl_gc_run_all_finalizers(jl_task_t *ct) void jl_gc_add_finalizer_(jl_ptls_t ptls, void *v, void *f) JL_NOTSAFEPOINT { - register_finalizer(v, f, 0); + mmtk_register_finalizer(v, f, 0); } @@ -192,13 +192,13 @@ void jl_gc_free_array(jl_array_t *a) JL_NOTSAFEPOINT JL_DLLEXPORT void jl_gc_queue_root(const jl_value_t *ptr) { - unreachable(); + mmtk_unreachable(); } // TODO: exported, but not MMTk-specific? JL_DLLEXPORT void jl_gc_queue_multiroot(const jl_value_t *parent, const jl_value_t *ptr) JL_NOTSAFEPOINT { - unreachable(); + mmtk_unreachable(); } @@ -207,13 +207,13 @@ JL_DLLEXPORT void jl_gc_queue_multiroot(const jl_value_t *parent, const jl_value JL_DLLEXPORT int jl_gc_mark_queue_obj(jl_ptls_t ptls, jl_value_t *obj) { - unreachable(); + mmtk_unreachable(); return 0; } JL_DLLEXPORT void jl_gc_mark_queue_objarray(jl_ptls_t ptls, jl_value_t *parent, jl_value_t **objs, size_t nobjs) { - unreachable(); + mmtk_unreachable(); } @@ -231,7 +231,7 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) jl_atomic_fetch_add((_Atomic(uint64_t)*)&gc_num.deferred_alloc, localbytes); return; } - handle_user_collection_request(ptls, collection); + mmtk_handle_user_collection_request(ptls, collection); } // Per-thread initialization @@ -266,7 +266,7 @@ void jl_init_thread_heap(jl_ptls_t ptls) memset(&ptls->gc_num, 0, sizeof(ptls->gc_num)); jl_atomic_store_relaxed(&ptls->gc_num.allocd, -(int64_t)gc_num.interval); - MMTk_Mutator mmtk_mutator = bind_mutator((void *)ptls, ptls->tid); + MMTk_Mutator mmtk_mutator = mmtk_bind_mutator((void *)ptls, ptls->tid); ptls->mmtk_mutator_ptr = ((MMTkMutatorContext*)mmtk_mutator); } @@ -337,9 +337,9 @@ void jl_gc_init(void) // if only max size is specified initialize MMTk with a fixed size heap if (max_size_def != NULL || (max_size_gb != NULL && (min_size_def == NULL && min_size_gb == NULL))) { - gc_init(0, max_heap_size, &mmtk_upcalls, (sizeof(jl_taggedvalue_t))); + mmtk_gc_init(0, max_heap_size, &mmtk_upcalls, (sizeof(jl_taggedvalue_t))); } else { - gc_init(min_heap_size, max_heap_size, &mmtk_upcalls, (sizeof(jl_taggedvalue_t))); + mmtk_gc_init(min_heap_size, max_heap_size, &mmtk_upcalls, (sizeof(jl_taggedvalue_t))); } } @@ -485,7 +485,17 @@ void objprofile_reset(void) // gc thread function void jl_gc_threadfun(void *arg) { - unreachable(); + mmtk_unreachable(); +} + +// added for MMTk integration +void enable_collection(void) +{ + mmtk_enable_collection(); +} +void disable_collection(void) +{ + mmtk_disable_collection(); } JL_DLLEXPORT void jl_gc_array_ptr_copy(jl_array_t *dest, void **dest_p, jl_array_t *src, void **src_p, ssize_t n) JL_NOTSAFEPOINT @@ -522,7 +532,7 @@ JL_DLLEXPORT void jl_gc_wb2_slow(const void *parent, const void* ptr) JL_NOTSAFE void *jl_gc_perm_alloc_nolock(size_t sz, int zero, unsigned align, unsigned offset) { jl_ptls_t ptls = jl_current_task->ptls; - void* addr = alloc(ptls->mmtk_mutator_ptr, sz, align, offset, 1); + void* addr = mmtk_alloc(ptls->mmtk_mutator_ptr, sz, align, offset, 1); return addr; } diff --git a/src/symbol.c b/src/symbol.c index 00de9872e8255..dcfa0b6086846 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -41,7 +41,7 @@ static jl_sym_t *mk_symbol(const char *str, size_t len) JL_NOTSAFEPOINT jl_set_typetagof(sym, jl_symbol_tag, GC_OLD_MARKED); #ifdef MMTK_GC jl_ptls_t ptls = jl_current_task->ptls; - post_alloc(ptls->mmtk_mutator_ptr, jl_valueof(tag), nb, 1); + mmtk_post_alloc(ptls->mmtk_mutator_ptr, jl_valueof(tag), nb, 1); #endif jl_atomic_store_relaxed(&sym->left, NULL); jl_atomic_store_relaxed(&sym->right, NULL); From 1af2dd00b700032ed5757a8e29b855732e989c81 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 18 May 2023 22:17:48 +0000 Subject: [PATCH 773/775] Pass n_gcthreads to mmtk_gc_init. Avoid spawning GC threads in Julia. --- src/mmtk-gc.c | 4 ++-- src/threading.c | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/mmtk-gc.c b/src/mmtk-gc.c index 6d232919a55f8..10635cc11a07a 100644 --- a/src/mmtk-gc.c +++ b/src/mmtk-gc.c @@ -337,9 +337,9 @@ void jl_gc_init(void) // if only max size is specified initialize MMTk with a fixed size heap if (max_size_def != NULL || (max_size_gb != NULL && (min_size_def == NULL && min_size_gb == NULL))) { - mmtk_gc_init(0, max_heap_size, &mmtk_upcalls, (sizeof(jl_taggedvalue_t))); + mmtk_gc_init(0, max_heap_size, jl_options.ngcthreads, &mmtk_upcalls, (sizeof(jl_taggedvalue_t))); } else { - mmtk_gc_init(min_heap_size, max_heap_size, &mmtk_upcalls, (sizeof(jl_taggedvalue_t))); + mmtk_gc_init(min_heap_size, max_heap_size, jl_options.ngcthreads, &mmtk_upcalls, (sizeof(jl_taggedvalue_t))); } } diff --git a/src/threading.c b/src/threading.c index 52d69805d0b79..4f24ce1aad704 100644 --- a/src/threading.c +++ b/src/threading.c @@ -663,6 +663,11 @@ void jl_init_threading(void) ngcthreads = (nthreads / 2) - 1; } } +#ifdef MMTK_GC + // MMTk gets the number of GC threads from jl_options.ngcthreads. So we just set ngcthreads to 0 here + // to avoid spawning any GC threads in Julia. + ngcthreads = 0; +#endif jl_all_tls_states_size = nthreads + nthreadsi + ngcthreads; jl_n_threads_per_pool = (int*)malloc_s(2 * sizeof(int)); From da45bf76f3431ae0662e44d581a91bee7cae987d Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Fri, 19 May 2023 05:49:34 +0000 Subject: [PATCH 774/775] Set ngcthreads=0 in jl_start_threads --- src/mmtk-gc.c | 3 +++ src/threading.c | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/mmtk-gc.c b/src/mmtk-gc.c index 10635cc11a07a..8b4d1f2c22397 100644 --- a/src/mmtk-gc.c +++ b/src/mmtk-gc.c @@ -335,6 +335,9 @@ void jl_gc_init(void) max_heap_size = uv_get_free_memory() * 70 / 100; } + // If the two values are the same, we can use either. Otherwise, we need to be careful. + assert(jl_n_gcthreads == jl_options.ngcthreads); + // if only max size is specified initialize MMTk with a fixed size heap if (max_size_def != NULL || (max_size_gb != NULL && (min_size_def == NULL && min_size_gb == NULL))) { mmtk_gc_init(0, max_heap_size, jl_options.ngcthreads, &mmtk_upcalls, (sizeof(jl_taggedvalue_t))); diff --git a/src/threading.c b/src/threading.c index 4f24ce1aad704..51bdd6e8107da 100644 --- a/src/threading.c +++ b/src/threading.c @@ -663,11 +663,6 @@ void jl_init_threading(void) ngcthreads = (nthreads / 2) - 1; } } -#ifdef MMTK_GC - // MMTk gets the number of GC threads from jl_options.ngcthreads. So we just set ngcthreads to 0 here - // to avoid spawning any GC threads in Julia. - ngcthreads = 0; -#endif jl_all_tls_states_size = nthreads + nthreadsi + ngcthreads; jl_n_threads_per_pool = (int*)malloc_s(2 * sizeof(int)); @@ -686,6 +681,11 @@ void jl_start_threads(void) { int nthreads = jl_atomic_load_relaxed(&jl_n_threads); int ngcthreads = jl_n_gcthreads; +#ifdef MMTK_GC + // MMTk gets the number of GC threads from jl_options.ngcthreads, and spawn its GC threads. + // So we just set ngcthreads to 0 here to avoid spawning any GC threads in Julia. + ngcthreads = 0; +#endif int cpumasksize = uv_cpumask_size(); char *cp; int i, exclusive; From 8194356082ec76655dc4fb14a909e9b721730b79 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 25 May 2023 23:48:23 +0000 Subject: [PATCH 775/775] Fix stock Julia build --- src/gc-debug.c | 203 ++++++++++++++----------------------------------- src/gc.c | 138 ++++++++++++++++++++++++++++----- src/gc.h | 49 +++++------- src/mmtk-gc.c | 2 + 4 files changed, 201 insertions(+), 191 deletions(-) diff --git a/src/gc-debug.c b/src/gc-debug.c index fc3da5b2ba282..df2e3487506fa 100644 --- a/src/gc-debug.c +++ b/src/gc-debug.c @@ -647,91 +647,6 @@ void jl_gc_debug_print_status(void) } #endif -#ifdef OBJPROFILE -static htable_t obj_counts[3]; -static htable_t obj_sizes[3]; -void objprofile_count(void *ty, int old, int sz) -{ - if (gc_verifying) return; - if ((intptr_t)ty <= 0x10) { - ty = (void*)jl_buff_tag; - } - else if (ty != (void*)jl_buff_tag && ty != jl_malloc_tag && - jl_typeof(ty) == (jl_value_t*)jl_datatype_type && - ((jl_datatype_t*)ty)->instance) { - ty = jl_singleton_tag; - } - void **bp = ptrhash_bp(&obj_counts[old], ty); - if (*bp == HT_NOTFOUND) - *bp = (void*)2; - else - (*((intptr_t*)bp))++; - bp = ptrhash_bp(&obj_sizes[old], ty); - if (*bp == HT_NOTFOUND) - *bp = (void*)(intptr_t)(1 + sz); - else - *((intptr_t*)bp) += sz; -} - -void objprofile_reset(void) -{ - for (int g = 0; g < 3; g++) { - htable_reset(&obj_counts[g], 0); - htable_reset(&obj_sizes[g], 0); - } -} - -static void objprofile_print(htable_t nums, htable_t sizes) -{ - for(int i=0; i < nums.size; i+=2) { - if (nums.table[i+1] != HT_NOTFOUND) { - void *ty = nums.table[i]; - int num = (intptr_t)nums.table[i + 1] - 1; - size_t sz = (uintptr_t)ptrhash_get(&sizes, ty) - 1; - static const int ptr_hex_width = 2 * sizeof(void*); - if (sz > 2e9) { - jl_safe_printf(" %6d : %*.1f GB of (%*p) ", - num, 6, ((double)sz) / 1024 / 1024 / 1024, - ptr_hex_width, ty); - } - else if (sz > 2e6) { - jl_safe_printf(" %6d : %*.1f MB of (%*p) ", - num, 6, ((double)sz) / 1024 / 1024, - ptr_hex_width, ty); - } - else if (sz > 2e3) { - jl_safe_printf(" %6d : %*.1f kB of (%*p) ", - num, 6, ((double)sz) / 1024, - ptr_hex_width, ty); - } - else { - jl_safe_printf(" %6d : %*d B of (%*p) ", - num, 6, (int)sz, ptr_hex_width, ty); - } - if (ty == (void*)jl_buff_tag) - jl_safe_printf("#"); - else if (ty == jl_malloc_tag) - jl_safe_printf("#"); - else if (ty == jl_singleton_tag) - jl_safe_printf("#"); - else - jl_static_show(JL_STDERR, (jl_value_t*)ty); - jl_safe_printf("\n"); - } - } -} - -void objprofile_printall(void) -{ - jl_safe_printf("Transient mark :\n"); - objprofile_print(obj_counts[0], obj_sizes[0]); - jl_safe_printf("Perm mark :\n"); - objprofile_print(obj_counts[1], obj_sizes[1]); - jl_safe_printf("Remset :\n"); - objprofile_print(obj_counts[2], obj_sizes[2]); -} -#endif - #if defined(GC_TIME) || defined(GC_FINAL_STATS) STATIC_INLINE double jl_ns2ms(int64_t t) { @@ -1257,68 +1172,68 @@ void gc_count_pool(void) // `offset` will be added to `mq->current` for convenience in the debugger. NOINLINE void gc_mark_loop_unwind(jl_ptls_t ptls, jl_gc_markqueue_t *mq, int offset) { - jl_jmp_buf *old_buf = jl_get_safe_restore(); - jl_jmp_buf buf; - jl_set_safe_restore(&buf); - if (jl_setjmp(buf, 0) != 0) { - jl_safe_printf("\n!!! ERROR when unwinding gc mark loop -- ABORTING !!!\n"); - jl_set_safe_restore(old_buf); - return; - } - jl_value_t **start = mq->start; - jl_value_t **end = mq->current + offset; - for (; start < end; start++) { - jl_value_t *obj = *start; - jl_taggedvalue_t *o = jl_astaggedvalue(obj); - jl_safe_printf("Queued object: %p :: (tag: %zu) (bits: %zu)\n", obj, - (uintptr_t)o->header, ((uintptr_t)o->header & 3)); - jl_((void*)(jl_datatype_t *)(o->header & ~(uintptr_t)0xf)); - } - jl_set_safe_restore(old_buf); -} - -int gc_slot_to_fieldidx(void *obj, void *slot, jl_datatype_t *vt) JL_NOTSAFEPOINT -{ - int nf = (int)jl_datatype_nfields(vt); - for (int i = 1; i < nf; i++) { - if (slot < (void*)((char*)obj + jl_field_offset(vt, i))) - return i - 1; - } - return nf - 1; -} - -int gc_slot_to_arrayidx(void *obj, void *_slot) JL_NOTSAFEPOINT -{ - char *slot = (char*)_slot; - jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(obj); - char *start = NULL; - size_t len = 0; - size_t elsize = sizeof(void*); - if (vt == jl_module_type) { - jl_module_t *m = (jl_module_t*)obj; - start = (char*)m->usings.items; - len = m->usings.len; - } - else if (vt == jl_simplevector_type) { - start = (char*)jl_svec_data(obj); - len = jl_svec_len(obj); - } - else if (vt->name == jl_array_typename) { - jl_array_t *a = (jl_array_t*)obj; - start = (char*)a->data; - len = jl_array_len(a); - elsize = a->elsize; - } - if (slot < start || slot >= start + elsize * len) - return -1; - return (slot - start) / elsize; -} + // jl_jmp_buf *old_buf = jl_get_safe_restore(); + // jl_jmp_buf buf; + // jl_set_safe_restore(&buf); + // if (jl_setjmp(buf, 0) != 0) { + // jl_safe_printf("\n!!! ERROR when unwinding gc mark loop -- ABORTING !!!\n"); + // jl_set_safe_restore(old_buf); + // return; + // } + // jl_value_t **start = mq->start; + // jl_value_t **end = mq->current + offset; + // for (; start < end; start++) { + // jl_value_t *obj = *start; + // jl_taggedvalue_t *o = jl_astaggedvalue(obj); + // jl_safe_printf("Queued object: %p :: (tag: %zu) (bits: %zu)\n", obj, + // (uintptr_t)o->header, ((uintptr_t)o->header & 3)); + // jl_((void*)(jl_datatype_t *)(o->header & ~(uintptr_t)0xf)); + // } + // jl_set_safe_restore(old_buf); +} + +// int gc_slot_to_fieldidx(void *obj, void *slot, jl_datatype_t *vt) JL_NOTSAFEPOINT +// { +// int nf = (int)jl_datatype_nfields(vt); +// for (int i = 1; i < nf; i++) { +// if (slot < (void*)((char*)obj + jl_field_offset(vt, i))) +// return i - 1; +// } +// return nf - 1; +// } + +// int gc_slot_to_arrayidx(void *obj, void *_slot) JL_NOTSAFEPOINT +// { +// char *slot = (char*)_slot; +// jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(obj); +// char *start = NULL; +// size_t len = 0; +// size_t elsize = sizeof(void*); +// if (vt == jl_module_type) { +// jl_module_t *m = (jl_module_t*)obj; +// start = (char*)m->usings.items; +// len = m->usings.len; +// } +// else if (vt == jl_simplevector_type) { +// start = (char*)jl_svec_data(obj); +// len = jl_svec_len(obj); +// } +// else if (vt->name == jl_array_typename) { +// jl_array_t *a = (jl_array_t*)obj; +// start = (char*)a->data; +// len = jl_array_len(a); +// elsize = a->elsize; +// } +// if (slot < start || slot >= start + elsize * len) +// return -1; +// return (slot - start) / elsize; +// } static int gc_logging_enabled = 0; -JL_DLLEXPORT void jl_enable_gc_logging(int enable) { - gc_logging_enabled = enable; -} +// JL_DLLEXPORT void jl_enable_gc_logging(int enable) { +// gc_logging_enabled = enable; +// } void _report_gc_finished(uint64_t pause, uint64_t freed, int full, int recollect) JL_NOTSAFEPOINT { if (!gc_logging_enabled) { diff --git a/src/gc.c b/src/gc.c index 932bb1d97c6db..ce80597a937f1 100644 --- a/src/gc.c +++ b/src/gc.c @@ -376,10 +376,6 @@ void jl_gc_wait_for_the_world(jl_ptls_t* gc_all_tls_states, int gc_n_threads) } } -<<<<<<< HEAD - -======= ->>>>>>> upstream/master // malloc wrappers, aligned allocation #if defined(_OS_WINDOWS_) @@ -2648,6 +2644,8 @@ JL_EXTENSION NOINLINE void gc_mark_loop_serial(jl_ptls_t ptls) gc_drain_own_chunkqueue(ptls, &ptls->mark_queue); } +extern int gc_first_tid; + void gc_mark_and_steal(jl_ptls_t ptls) { jl_gc_markqueue_t *mq = &ptls->mark_queue; @@ -2799,24 +2797,109 @@ void gc_mark_clean_reclaim_sets(void) } } -static void gc_premark(jl_ptls_t ptls2) +// void gc_premark(jl_ptls_t ptls2) +// { +// arraylist_t *remset = ptls2->heap.remset; +// ptls2->heap.remset = ptls2->heap.last_remset; +// ptls2->heap.last_remset = remset; +// ptls2->heap.remset->len = 0; +// ptls2->heap.remset_nptr = 0; +// // avoid counting remembered objects +// // in `perm_scanned_bytes` +// size_t len = remset->len; +// void **items = remset->items; +// for (size_t i = 0; i < len; i++) { +// jl_value_t *item = (jl_value_t *)items[i]; +// objprofile_count(jl_typeof(item), 2, 0); +// jl_astaggedvalue(item)->bits.gc = GC_OLD_MARKED; +// } +// } + +#ifdef OBJPROFILE +static htable_t obj_counts[3]; +static htable_t obj_sizes[3]; +void objprofile_count(void *ty, int old, int sz) +{ + if (gc_verifying) return; + if ((intptr_t)ty <= 0x10) { + ty = (void*)jl_buff_tag; + } + else if (ty != (void*)jl_buff_tag && ty != jl_malloc_tag && + jl_typeof(ty) == (jl_value_t*)jl_datatype_type && + ((jl_datatype_t*)ty)->instance) { + ty = jl_singleton_tag; + } + void **bp = ptrhash_bp(&obj_counts[old], ty); + if (*bp == HT_NOTFOUND) + *bp = (void*)2; + else + (*((intptr_t*)bp))++; + bp = ptrhash_bp(&obj_sizes[old], ty); + if (*bp == HT_NOTFOUND) + *bp = (void*)(intptr_t)(1 + sz); + else + *((intptr_t*)bp) += sz; +} + +void objprofile_reset(void) { - arraylist_t *remset = ptls2->heap.remset; - ptls2->heap.remset = ptls2->heap.last_remset; - ptls2->heap.last_remset = remset; - ptls2->heap.remset->len = 0; - ptls2->heap.remset_nptr = 0; - // avoid counting remembered objects - // in `perm_scanned_bytes` - size_t len = remset->len; - void **items = remset->items; - for (size_t i = 0; i < len; i++) { - jl_value_t *item = (jl_value_t *)items[i]; - objprofile_count(jl_typeof(item), 2, 0); - jl_astaggedvalue(item)->bits.gc = GC_OLD_MARKED; + for (int g = 0; g < 3; g++) { + htable_reset(&obj_counts[g], 0); + htable_reset(&obj_sizes[g], 0); + } +} + +static void objprofile_print(htable_t nums, htable_t sizes) +{ + for(int i=0; i < nums.size; i+=2) { + if (nums.table[i+1] != HT_NOTFOUND) { + void *ty = nums.table[i]; + int num = (intptr_t)nums.table[i + 1] - 1; + size_t sz = (uintptr_t)ptrhash_get(&sizes, ty) - 1; + static const int ptr_hex_width = 2 * sizeof(void*); + if (sz > 2e9) { + jl_safe_printf(" %6d : %*.1f GB of (%*p) ", + num, 6, ((double)sz) / 1024 / 1024 / 1024, + ptr_hex_width, ty); + } + else if (sz > 2e6) { + jl_safe_printf(" %6d : %*.1f MB of (%*p) ", + num, 6, ((double)sz) / 1024 / 1024, + ptr_hex_width, ty); + } + else if (sz > 2e3) { + jl_safe_printf(" %6d : %*.1f kB of (%*p) ", + num, 6, ((double)sz) / 1024, + ptr_hex_width, ty); + } + else { + jl_safe_printf(" %6d : %*d B of (%*p) ", + num, 6, (int)sz, ptr_hex_width, ty); + } + if (ty == (void*)jl_buff_tag) + jl_safe_printf("#"); + else if (ty == jl_malloc_tag) + jl_safe_printf("#"); + else if (ty == jl_singleton_tag) + jl_safe_printf("#"); + else + jl_static_show(JL_STDERR, (jl_value_t*)ty); + jl_safe_printf("\n"); + } } } +void objprofile_printall(void) +{ + jl_safe_printf("Transient mark :\n"); + objprofile_print(obj_counts[0], obj_sizes[0]); + jl_safe_printf("Perm mark :\n"); + objprofile_print(obj_counts[1], obj_sizes[1]); + jl_safe_printf("Remset :\n"); + objprofile_print(obj_counts[2], obj_sizes[2]); +} +#endif + static void gc_queue_thread_local(jl_gc_markqueue_t *mq, jl_ptls_t ptls2) { jl_task_t *task; @@ -2955,6 +3038,9 @@ static void sweep_finalizer_list(arraylist_t *list) size_t jl_maxrss(void); +extern void objprofile_printall(void); +extern void objprofile_reset(void); + // Only one thread should be running in this function static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) { @@ -3708,6 +3794,22 @@ void disable_collection(void) { } +JL_DLLEXPORT void jl_gc_wb1_noinline(const void *parent) JL_NOTSAFEPOINT +{ +} + +JL_DLLEXPORT void jl_gc_wb2_noinline(const void *parent, const void *ptr) JL_NOTSAFEPOINT +{ +} + +JL_DLLEXPORT void jl_gc_wb1_slow(const void *parent) JL_NOTSAFEPOINT +{ +} + +JL_DLLEXPORT void jl_gc_wb2_slow(const void *parent, const void* ptr) JL_NOTSAFEPOINT +{ +} + #ifdef __cplusplus } #endif diff --git a/src/gc.h b/src/gc.h index 3def80327ceda..701c2c769e1b4 100644 --- a/src/gc.h +++ b/src/gc.h @@ -47,7 +47,24 @@ extern void gc_premark(jl_ptls_t ptls2); extern void *gc_managed_realloc_(jl_ptls_t ptls, void *d, size_t sz, size_t oldsz, int isaligned, jl_value_t *owner, int8_t can_collect); extern size_t jl_array_nbytes(jl_array_t *a); -extern void objprofile_count(void *ty, int old, int sz); + +#ifdef OBJPROFILE +void objprofile_count(void *ty, int old, int sz) JL_NOTSAFEPOINT; +void objprofile_printall(void); +void objprofile_reset(void); +#else +static inline void objprofile_count(void *ty, int old, int sz) JL_NOTSAFEPOINT +{ +} + +static inline void objprofile_printall(void) +{ +} + +static inline void objprofile_reset(void) +{ +} +#endif #define malloc_cache_align(sz) jl_malloc_aligned(sz, JL_CACHE_BYTE_ALIGNMENT) #define realloc_cache_align(p, sz, oldsz) jl_realloc_aligned(p, sz, oldsz, JL_CACHE_BYTE_ALIGNMENT) @@ -70,7 +87,7 @@ extern uint64_t finalizer_rngState[]; extern int gc_n_threads; extern jl_ptls_t* gc_all_tls_states; -// keep in sync with the Julia type of the same name in base/timing.jl +// This struct must be kept in sync with the Julia type of the same name in base/timing.jl typedef struct { int64_t allocd; int64_t deferred_alloc; @@ -82,7 +99,6 @@ typedef struct { uint64_t freecall; uint64_t total_time; uint64_t total_allocd; - uint64_t since_sweep; size_t interval; int pause; int full_sweep; @@ -90,6 +106,7 @@ typedef struct { uint64_t max_memory; uint64_t time_to_safepoint; uint64_t max_time_to_safepoint; + uint64_t total_time_to_safepoint; uint64_t sweep_time; uint64_t mark_time; uint64_t total_sweep_time; @@ -217,32 +234,6 @@ typedef struct { jl_alloc_num_t print; } jl_gc_debug_env_t; -// This struct must be kept in sync with the Julia type of the same name in base/timing.jl -typedef struct { - int64_t allocd; - int64_t deferred_alloc; - int64_t freed; - uint64_t malloc; - uint64_t realloc; - uint64_t poolalloc; - uint64_t bigalloc; - uint64_t freecall; - uint64_t total_time; - uint64_t total_allocd; - size_t interval; - int pause; - int full_sweep; - uint64_t max_pause; - uint64_t max_memory; - uint64_t time_to_safepoint; - uint64_t max_time_to_safepoint; - uint64_t total_time_to_safepoint; - uint64_t sweep_time; - uint64_t mark_time; - uint64_t total_sweep_time; - uint64_t total_mark_time; -} jl_gc_num_t; - // Array chunks (work items representing suffixes of // large arrays of pointers left to be marked) diff --git a/src/mmtk-gc.c b/src/mmtk-gc.c index 8b4d1f2c22397..5e868ef11c1d2 100644 --- a/src/mmtk-gc.c +++ b/src/mmtk-gc.c @@ -473,6 +473,7 @@ void jl_print_gc_stats(JL_STREAM *s) { } +#ifdef OBJPROFILE void objprofile_count(void *ty, int old, int sz) JL_NOTSAFEPOINT { } @@ -484,6 +485,7 @@ void objprofile_printall(void) void objprofile_reset(void) { } +#endif // gc thread function void jl_gc_threadfun(void *arg)

n40-nh8dd3*l*H{{ ziG-j}6#k9aka32v>pq%&JIlz4Q&pL~xsl!nKAB_vup^HQo3Vm-ZTWYyweLiH>&aCEvPkFOFoppgWu??`a7-YPu&oww|B#6C4E^JlYYN_(4!bB~7KJB|i^ z#shWY5h~vlkvRA#0P;Nr`x{>)`6ij(!`-z^)vdv&XO#E}YGvzX$~kPsR&M6q^UQ7X z>NqncN?sRtU0sd`Gj|dOMs*kkgm2r{*r=b`-+BW=$k(b(`q%Hk{PmvGVmIDxcl=|x zOBTCRsre9nIVaN&3ro(zH*FXdQ{*@O-`3H#-PXEx#W8K9eQF}!nEU;9QD$B>;Tf*| zj-&r;cG>6k3+ z{|q0X761dN`qN+?ktQ7-(jmf7v+Pc~1?_w|{)IEv2{%xTbby(>hFq+>#;yuS26>&0 zA@nB>VH=WFS^9_gSG2w=f4G?`^qx6DH3wv4HHyDvV|gT7huh|cw}U}evoi)>2@wnc zK2y+LCD?jACW!aOG8|-6aHWVNLPYxe`+a4EsT^m&Pw{IDe%K*Z@wiFF5xPhyo`WyP zQT*_Zw0(-^`?{_}OF6Vy_TgE?hAg?S6OnJAyn~aVrHcf5tAIZAfANlS-u~qs^EiE0(pU%I5vs;7BEmoizV>YTEM|PvOksjJn`5 z(C+u)*um97sP&;MkknDzHZKty^fe??Es`CTfl>5Ssf zEb7U_r4~7#+XF^aoeBerxrWKu*3yf;$7-mcpGF+2YkvKyX9Sk zD!j6s0rMLLXV!7c12K`hp)LvsVYPF@U@(_dHL1chSI6UvKGF zJCXnAKV)PJBq$jfk9AG;VCpie6<*$Zesvv!9RgM9*t{1VtR|0l8tyCx^Z0{(j(_Iy zo#>Frq;EyCpUca?n~ijb8@Bi=+yjaIqc-33X?l^MR6b3`hhK3_Nquhjb7n3ZqZ%%E zeSn0|^B2?fpWXbwdL%?FsEEg0S!pRq;WvC#C79vYT6Y&EoQ7fzRxfK|fGl3V5)cHxnOp&kG`CD9CewV)-vNDxK}%xg{&HEp zqWRRs-!OJDyP@8w-K~}16azF~|FB=-W&&Vt$0^Ur4SyRY=(Qh`1xjN>Y9c@(L8Geu zP^_52+wJj;b3392-&(Lw^9j#?rX3p5mGnik?K|S|W4;;T9u0=YnAm~5kfvtaSpb#` z8ldaLWZ8vNttX||l6iqhK(7y&DP3g>uxswy!w2Godc{zUe}1E_JSC(4kD3ydue`;z z(P6l*1}xQ>t2IbdU_56@=9QR*Uj{n1YsXWBTsUcUWzrBfCOytnnhK3FUZzj=CZa3R z_z8U@B1A72OWS4jEo}-F?|TY~!+FOj`=$3-uy;{`)vH&|hs>Vlo8!ob8|@Ab#y()2 z&E$MAB$X5Np!Expktyd-(K751Y34_8?c!IEmB^2ZC~5?$zdigwfK=kN4keDr-r27_ z6w%Ud0b@z@trjWHP&!Y@kRG``V`#noVrJ%TAU%-EJ_RJFU;CTaaw9h<@yp6bQJT?j z(Q?*8vy9wBATKChE5M3-y)Yw1ZyQD37>e4iNGSn&$mS~c{+CsZc{2?s3#foP%=Pe- zpIPn6(1R)WZ`i2)!=@wSbYXkA9FzlYy-a=&Z0sE)cs9-Qg}uR)3G2J7x2-2iqN3VU zRi<3_F~;RY2T=(?AM_)u*tzH^pfdb?1lXoWsWZC2Iffs-o{R13qI|udNVk0a``JmL zJB$g{+{yjzrE)UK)orL(AH(3nG?{afqwBIkE7>C(Ec8jIl?Pqb41pXWa|I z&0KdP@0kYQw4r6^g3sL`5I<_}VJ#{$A6?kR)3ajGMZd^hjrxrJ9yN-gG(gB8}8y2exgg#z8~t zCt?Bl`}XIp8-cYEf=WFBnt>T#9F@iK^t#jl@pGG}MR zBq49N)NYA{duWKW4SR5(zdoq-Y3XtH`p7BX)IhT(LRFzQOPSPWBr^=@uil>Ab>5y- zzae#Z;wi#-Eu9h6aqY0WVxdIxHtf;?`<$QINEYwEOiF$KTAtu&q44s7Dj?e*FiYc9 zh&Zd97$KNkg%W&iJBE%zodWwG!4AM*ZNuPHSI=uDV0l!uBWe#AM*#4Hw^hcMqG;Jz zRP>p`M#Yv5*aGE;oU3(HD-1#)w$5x8!;IAMse1&ROQHrh$DJdV7gAc4*N~{cg(1QEQbXA9z_iV28o3Eq*-wLiZ*gK0Rkbt_WDjdP0&>Gz~p5eDZ z7w&UbFjY;(Ts0!ZV#4^c%MyUf-r~@Fl3KAec>A6a)H?x)4L}SiPK%BU<@xGjqeL^E z%d)T`dc#s|t@eF)>!}ObyuST^jlm<=(V8i-slmEfkwF3BKO6>whnE3#|>nRhV&P4gSb2*tOiG0Oo)lL zWQ9|joxtgBH!jJIv^aN|bCg151GpBreDBqpD@X^8t7Ba)*M;|U9I+>qk~-EwqeYEcRWPRkl>8&F>oh><%Tcn==CWQd6a9>I_RPIRj-Hq5gT?P zDc!HwHVn?kJ33rhZl zz}#N-W9i}k_P)daUA_4bN_U_26g|r!>s%urCWDLO=CQJzQBr zmJn?$wlsS2K^DWZF1rFq;IVx7ZQT(pGxmSpeEhr2i10Eo_*(KYCDAQ~_p!fFu@o7k zGvpqu${M^{z+qBT-Xu4F)8`>%XLv}4^ZdV<7yU_}{B=E(0`kC4E};_Ob-%#60Qru2 zI`#Ha^jI%}^E1R9^J=9%D1WKazfXrk8s9|o2_oP%YC^?=-@AInN4+>#S{Fk=!sU3N zug@HlK&nDPM+^}w8nV9f6|glkW3+!xuqt$rp6bXbrfd2zWYY%$5ZnUqNp+}Ai(Om7 z<1GcxH8+#>x5DRZC)RT{#e#vM_|yDA1^tSF*mk~7qSnEoLhUzTqRQH!r!jztC|Cr9 zIE*L)kbSlO-k5ypLpyb2;G!6zL4*e>75~{Ng*7la*uwV@#&|@BtSz-P-4=5tUv6rF zxqv+>6ramLMa16_lUQA_g!so1JMu-Kn@3+P0{2;s6;`+-PLT%kR91fe{460?xD+XG zjy@1*w*c5lXkc1Oi1Mx^faU6FAm zkSs!6oWmwj%sEJx%L;=mc>PQHgPR@fY1q-79Pn^%Sk$n%@^TNKb0R2L8HoF} zC5k`{hqzW>&q4RixsCQB_6Q7O5H%~>swwoqgQ7x2#0+p{$^@%J%(b2V;1Coxj6MkO zzNEGx;|z2lS)kGx2@WJyLjWVy!TEKbaoAGXR%5k%!Hmz%YbZ!Az zCa2pMuQ=6T5c5a(*DGJ?w+Y4;6|6sdj$#Kh_cHbrq~*yS%n&10N6vlDs^wa)P)*x` z7!kUY{U&p7SUn#4v0pB-5F_%4IpF7tx&aWPsq(xC+8ET{3hXVsFA`xH5^}Ru3BKJ+ ztk3-NDxCn^6-n~-drxS`alg;=F9lG92aBQZ4_iApm8Ay?sieo6N}&~l;qbd}1D_e& zH=4z6XQH%qnMwcaOOE(g+UNgU0qKVfS6=e>GTpW^`m{+AeK`u%y4eZ?{b%YDM zb6ejTl!rB%Iz%ul4#U~ntBhX(4hyXLE)zVyL_uH+Z;xUp)%LwRLC7C>?vs)a&XqDT zL4gW{$(-Y-EJZ9EmU9(fR8zl2eun?*DRaUD{*RyW9wMZlFdW75BiH}PP5*0Dw}uDh zS~zXyi%n<8KZi^!hZFE!lz!+F>5<2z+!qNtG>u=2r$V+g-Reok5nq~5?jWd-lWXa< zn-Zro-SU*t!-7~(?dO8#>nkc%p|}FwNF-?@L>8ARx7@Jlo2$Jxo^wK74qn>yUBAbc z$V%odl0Ez&xT9RM`21i-`>sxKn>=v!XGsJew^aIdkzT1dA`D-zph6Ta|z5;$S_x2yK}J zMWPx~N@T=W29?Te`t(N}m%T27TDKXU85t@{$|3|}cX6P~h@{%AO@CApIw1F0Hhbsb zL*BoCQ(Xv*C$E;|x1H8|4oU#!&H8X**7|p)7u$#H@lr;h(c6Fb@Q&8h?QlUmKJYO` z0rtb;0;e@~KHG8*d^j{klX(o%mPsdp5BLQ$gWkS(j;pufqaE>>*IV}iVUUb@$@ zI<$A3>GCd;X;fUn7Hh=2%2f0G(c*}4C9ffqTd;xJl9rOYJ+P?>)(zz_5$W->D4iXh zvTU46wz7_nrIVZ6<%)fK2I*5#x|^(&Ga|1q0z-?NnC3!R8yBuwzRQP8^OwF3<~`Ak zoa)gFlc3WXEvTG+1~3B3K<370yU;)kf+KC18l+nt`d*MNk)2F_x*sCTk)~!e`UVn> z4*H*dQ?SRnUiO(g>EzTvsEC;do4f(7k8S;NUR!)E&+q&j{&;89cFfAzi5(}EZk35y zGQYyrAWDip=-?M>BW$RoVY=F)g^w8Da?IdHlkj*94gEP38-$L76Uk60wlL;h#T)ak zKYlg)fGTaT!Fk)&a`8usg7FYsBo+k{z4D`t2By z6IM;WZI71ehDqa$M>qij-zs!pRnO*>qh3l~X)`mNtjs}|haRp0eg!0DS5(eqPe2g0 zg$={*_*bbBdWDp}RE}R&k1h&3|Nh%Mz5;g=4~p40neZ(Qz!rJBv_!W+{U)maIPunS zFYm445K-Df{bBYW4-e@kFM~AhYVo;i%ov=lO$H6L*UQZZgFn&~7)l*H!*4HMrt`GI z8MNNb*mCp6@q4@{JQjFTtXe3^C8TL|b)4gPbu8k3^M+9Hic|obNeBBG%>K`B+U%pe zb2@o7;>i}l6w}@(mm?7j*KX|v3Iu~l5EBKnykhd%?`B~K^Ox@rUG}H_LE|B*DnqIJ z@1MP>F!)6XJe$K+0G4h+ocR!sF1ZMZU|%{3rq``-ASK;OS%Pp+`v-#uEcg+UhQ2nsED2>1*UkEis=pngpbt4OW(Vw69Ww)6LzSlr(NPVJhAd^VT`WRRF zFqRiq>3*VT;QeQ>naby%zp}d@xWkU_t!%ukQ|Z;M`#ukmJltRM=X$q9jLg@rMEEfCioQ)3*8( za6`&5aF|Nf=zt|v&r#nz{yX%1LVu=z|F(;;HO#55Qm&+dc`&Aef~~QfWhDGaO$a%c z4m`2d=d@N6@B9<#yh-1j!GlTegeNRAnRNR0W>O>qEEdgqV8aQf7NQ-fP%8{OAQnnj zEf@U>VJ37Vt{;f8-mm;58!b@ql1^rM8aZ8lvi4Kx@s4R){xCs3N24roG*{WQFNpz7 zCsy1TNKTKhPOyl>W}Do&2Gh6*UK`d8@#gftNyipP6sLHKe-=k{45~g+;LSx z68m#oC?GIb6U-kUI~r5`b4o>j76Uriwpx08GP)!3(?*BIz}a=SIM>StYA5Re=#|1W z_$#rAp5OiMcX1Ne7cFZC2kCuh zK>ivFaE@WXQz0qmMUqW=Oh?H3;R$eOBOsu`@Ani2fgIvMz-JMuGU+BW8%S*Ueu(j- zcs6WTA#i2oUh>5E;TrcWU@4O6hQm^MeuvN}h5JOwwL z!`8?C{)EQ>;fz$$1SZo)J4^Rsi_UvSWjgB0Ar61S7Bg>(K4ABPPOk%aZ3)-3T09kM z>}TMB?R-#h4FuA{+su@Q$|O*CbcTKIPAMS-e;b(87yVgn{=DzbW-E<}>{WS-2KWKV z1CQraD$j9`aB90fNcrjx;;Wn|Nt!Q{08axE9kUxT{~-5Q$VPrKSDAV z1ku|j_^pBw$nk!~`sm#;YTk!h!}Pa?NtyH&wLiG$IO$1l7_}u8|3=F%9Zz}8viCXEh)!Dd; zFJp;K`nBwv=8waYB{?AX^+@Jm`XyQWtzF>b(2s#C{9FcsJfUSavhY@=_jo9l2k(d*3<*IdU?aB-Bz;{*; z2vf>z_{yQ;;Q^rHOPtRkV9VhjSl8e1{eTJ<_G+HW^KAz)=EG#gGJ43H6i0H(^fK(& zD7`rlGyq4$3!5`PTz02_djzijcdYnCd3T*1#E=pv%vWQa=7Y3X*V}KKl^($wA)>b@ z&ua-6aLHXMjETB=JkQ?TCKOis+?<7iZT@JvF#)G7B>=Qy=wBZGOxjaG={EhV8wi11|N5-(^RxOTWh_Xw$0=VLLBVIc>Hvew)vgHBbkuu-H#;^#W?bs#awc_8 zVQ$(%++xfG60%oUV0-X$toNR3U^`*mQDfztEgTTY_?gnV6Ffdwci>OXwwXb-RJSMc zn|W~7@KH~#o9eErd+Sj&a=7S~bDJy6{XFGGcOKO>OT-_5DeJivdf@INy-?c(re2Il zO};gOX=y4LFi;DP5jyN&2ALixA|eOwS%>wNl`PFM&9g5tiJ*DUZ)~;r;yp}~S_^9u za0TmusuXRudVA$ZY;tT@b+L+smRgQY;5W~?_e{>uPL=+BDS{jFEorvTShOe`r65n!*`sQe%Is0jwN?$7N>ENSL~-Dvf3NS!On9X|x6$Rh56cLXBj z!N$NV*yEkP&2lTn&TDEiMlBZHn~VDw25r7}6Ct_50+jGS-fyybpGY`xJCb(r5^Epc z++Wh!4rg}RP?}8GQ^r!{5Ox!BJ3iUIzq@wEcAyd=(pZ{1TX?BQ!qf@S{;M!kj?AZfm<1Nam_=k}E~ams16`7&$_5%}XQh30HFS4nQ$ z-kX-f1m?0)Pi+buG8H|ky}Z+Cq~6a=L8CVdTK^wwZy8ku*R_pGmw+?^f|PVg zcZrmAcL>tmEl7i;gmg$ZY`V7~-Q6K6CEfTfyr1VC_j|rG#`$x8GWY{~?X~8d*SrGv zy6f+Hiz@l=%p54Xi}aVSnj9p2 zY|0{9XBv3hoCVwtTPhuLO*QI@b;0w2Q2l|&b>CdG#T`bw9j(WUl39oB8>AO~9B8@v zFGsve=k(}u6Dfn?^chQ8XUZ{#oIsB%uoS0#YBPsG_+h;F`|EFCsULO{?(Z5fG!uL zmGjcW3w-^!Sf8=TSI&?MY2KUl6zc~?9Bb=-hT;PGlvXC1l&ll@8ue63OL01^vs7A6 z$<)2OG>B^=lbIDyECte!kJFRG#6c?fhdYL`6flV7SSXN-BVkw}q1{o6WhqF&WxSHW z%?a?nsFs}}<}Me%KB3lvNb~^h4HA&5iC;E<8k5_ra1N~pI#B!|_souh%nPyOVM2%q zeiUr$ldS5ox6=)PJcJ1*njnzitnFMefaUxEjN9A#}xl=9ek;C!L#*RAgbU@zPoFmymb8%aEJ9TV!>kLoA%=m|B*68suoM|l? zgY;BhCsOUNCD4GjAX3Xm0FZsbsVn@vjTF_)g?DXE&-; zB6%C5KQ82--*mltY>HF0Ret^FN&kCgeSppD#5RRw&_@~^@Pfzd^thPDySly5QIOK& zH=uyF{L${xHzJjwoGAkiX1%L2XTWFK+TW`Gi|{JIrkZTZ0tJEbpNPg+7mA?c%gYcZ z9oN%iK9}N}O|Bc|I&0o!ZaWk*7PVsJW}Quz6hG}fcayKSN6VqkUlxWzUnvgE3DDWN ztY?D{ZBBE%M#X*)u;9!=HScfy%|%(DP<>F*3_*(Nz9qNcLmFasCOHfPKPcf6Xpa1| zaSIB<=phb2eYv_q!MR{b@+LBB5#cjwKV;@Lk~G9)#yfT~Ye`VOaT!1e}9M0_D{JqXbC(C9bab6{OZ&+nsT8{waCWzO!`XiE8 zC}sceQ+f0ER2ct%Iu$H%D(4p$Zwc~klsYCzfeK)_XbgS~#JY?!Sq381{Hb2Wq&5e& zk*`VVUd$`}61XR#OGam``IPNxnPx}BYf%lpt_S&pMC0G2;e_v4376L2YW4utCRVd~ ztJ@JXE>lWo?uXvh0sO0SknRPW|L48}^KAE&y2;&#CTnlzh`~AH_Bkp7x9IIf*^sHl z#Z`y@O762tq%?jvM8IdZHIZYbKhJ*pVso}kN5L6RAW#o8K3UrC^mL$?*~Dxx85Iuk zYyu5?H@POzW9tO+W1y!82^iJ%ZCZ|I;gbk?hhFNb8Fa72v5qWT31F+n!+|pZQ4^o> zss5P71srQs!)tw$V(U#}5nf>jKFPs64(pZ3b@ppHm0+gCUEyL6|Mu&E?%)fv9-n5t zEklgg0Wg@$*&IF=StJwowN|yg{`XCAdp6o;-b5*kwbl5kdV~C*wx=rWEWbM)}wZO2i4>Pn^DktX7l$w zFkWQ;1g@ALf#apsCqj5rs(E4|@4_+O8)L@763oudTb@WqeqwA#7^F z-mmQ3O{M9fwfw~Nt!#wF_ z)-d+g*OF-Lt=QzscVCTT`z!FaVyWL&t4pSq{3^N9dHiieqm_Vi-aCw0%E44tBa%qM z<4@A4M{OX6jr4643EWq9GY_{>lM!%Kx=T5^>AiBCdVbCo;{a4+hm`Xo1vTP0Gg+mm zs62c|jQKByM?a&zI;u@&4No>Ko)KyiGtIjM zfH$ftsNrYNh}e3U^eBaat?RMpFI8kJ={R@@vx}N%qR`Lo2qcg}27-IgI?i_ez5@e; z=XCC>dp~I4d;rmJQkd&iWX+mzh=V>c~th}0;9H|mQ} zqh0>%S5h%C2)SVQA+h#L6vA_fyxige_X+X-2~eZgn6r)O-i*!3Z;&>5UFs{=TDstO zjXM?i{SoqR*IB3E{-+fG?aWsh9}xkwJPW$?OS$AE#;@E|aw&`0*v%tAD2WUG-aTKv zv`Q85`1!sJ#s;OEaNb_o`hQMF1bJ2zxLLb)#AS(fh$Hv8PeZC7AdYdONraE`5JuS z;i0KmkP7sKfoRGrBgKEpB#oIA1=E?Ai-^^brl||>SvtToWq41^q!yWI& zaQalar^SF>(@_yrKx1kK+fQ~oG_}!c@k`OJn;FWfst$-6!=YDa9GVH>svz?v^e<24 zZ&XX(9&`l+70kP18KqJWa|gXJFG?SFe*@Il*i!5l>7*Gx>y9{FHx zU0-iLVX07UxuS4zWCu5Qs~HekLY&Eg;AGPs8-#v0+M{ z(pT30*Qd@S20ryD#dsdEm!2rSKE$nexeRU=mpc?k8Q!)38qoJMrf!Q`Tghwd2^L#O zo?adD2*wDJYz%PB$g&WSKYk|%)5d)rlK-4$@Z7L zlH0lLmXR;rN>t3x%b8KnA3ju#RT_QLa;^tVUGsT}Kl!_!D804YqmU~1$JD|7UrtL+ zoTIf?NaBQ6{DAx(AeZV|o!45f#}93CDRIetQ!5nZ$oNlN9XT+WCCUW(;wn||eL)Ju zmu3LtV*>CZ7D9|@(->32+&HhyppvXLsIqw{+I*5OVYWug)utuc=40zSmL7D#Ffu*d z1Of;xm}Sc94kfH3a|d5|9E@k{Y%s5GZn9Gs$kvh!eh^M6wf*a5?!bF|Qp%-PIEF4o zlO=OnaOuXyf^`vII?$6zB?}<5a3ZX&Uh)tgbx*LmKl~uu$h3KRRScS)$X3Ry?g&TCcmWw#5N$Cw6z zri)i6{?WT$|K~xGRPg7(yoLHBiJ4)PMfyP}3I5oaB>2krKOzG%{YXsN8mX((?YV}v-Ek}1oec6KkBdl`NBI**i@zm8AQTi{FU9v# zO8Oj_D-%=;Uk;O!63H)RcgwH_=C%=K?4^rRa3^k4ROS8TcV zUYDe>WSP$UzV(2u1=7{1dAzuwH}_bZ z(?6CM)`&s_QI^0dLec>@rU#$5-e$r2*%KP6m}lCxsK%F5R3Cv^Jl(V@k|}xWzix%D z%wt149{C_EP=|JUvO$O8>w)@7BkD;~*oi8CEYFd2FI|Jf7Bbj-i@Zow_~)SuHH9}B z6;9+VRF(V}puh*9$pSbD3=|klnwOVlA>1($;WP(_6qTfLrT<__K{6bH1NFAw8E|tN z9V5tCNKJ-fNc9E$;0vPdYEq`YN&976&3fX0k0qW1Io?pfh5QVaP=lNSr&Q6EHUozg zaG3%nqoFTN`*E$;A}}6~d+c_iF8Ff=rjq>O+9gGmF>%8sx)`)M$Xa}?_L0N7OsQT| zvdw|MElVo)3Et8#GyC64fZ7j-b~2ncMsO;ZdO=`4TR9w)n0xMk*u&DcgXypy14m zx?lZ<2@DAlpN9xBPUpP`!F3`GDP&{i*8i?nypPptfc^VZ<7QSCyJhT{R%aKfeZh$P{vIW1nf3oPX(6C9@WZqrq)YtQp{GfK8YLe{CyfHb{fPO= zWeGA8Tex9bJ)?8`ICgE8uC5P##FaCRB6^g35ug z5>diRVzx;axTBGihTlp>O5pDR(*$_XM}<;(=6*{f*M8)JG|3%=9rDO#N6f1-X7Ss< zB-9&z=$o!_4j&A$oJ0<6e@N-P-Gb*o-{Uq>!bZ@pGX7gPzAu~{?avLwa9Aiuh_>Im zK6#gJxlc3`d&BdIQcG%Yv61)-oeBnsPYjtpT&|Yd>+rufKd@&5)oILF@14TdKoVx# zgXgo25rZdjGzu6Xq(xLomBrc4>&vIVw+olI7%Cw1{p){TM-uQl=DxnVy%qoSup$P0 z)~-@oowhNW4D>(ev|u?C$!D~Dyd0N@aLq)vm-tQp+B$Tl*C-bdiU`O8ItO=jzm9Gy z*#r|9$FUp)5qxsF?)qZp6S{xqkH3d#h65%OK7O0lGjMzrQenk0wa#)K7$y4W>3*QW zZfJ26)NS`hEQ78%``*Om{Bu}u_+d1j-f&w^k`SQee*!-EP=7eD+}0?z>?h%K+2Wn{ zJEOh@_>E#g^f7{7^0PH&U~DUBx1TS1{+5mH#8A1xo~<{LiJv*;94aySB$_Nl)c!YV zgYP|2fvs#ZEk31oPIGbDej8JmB`tuNN_+>>mPw%a2%|Ou|b5vB$g=g~de?2#z2SH~jO^ z+7N1klE&izefE8kq898mM!)s(Tiwn(-ap({{Qi#l3Eg7nfYG20kpLbat`)&He+`5i z!2r!rN4I1iC@%((XBpVQ|GO_@1W=HJ@R#e28}rRSu_7n~>)nj01adb`AUt!h#JxXj8HOvuo** zGTPw=q)KK*!2jn6jYV5Y|M{NmmD4c$kf?V2DDcJQU>*)Y3lSMZ)!9mT zp7?2!;Wa^_(})X;lkpN7-G^f0@SGZ}j7R9GMij5|AGX4$2Tf`MJFd zLO$l+>wZJ#BC)5E^Gw_xF+g5m;}>4&i;>ct6>ni4h60j#l?|oWyLWL^bVQRy9q-xB zbi|HGKsW@)Eq@$?hJK-D6+I|v*n-(MoW&^jTp(bA2yDj32uHL@C)iL|jSTi1?TU6l zrkEs4s3@q}9v9|UAib5;j`F8C`-gX?N1>i-RG0za)BS)M4)Vs{V15i6_4;ATAy7*w z$o_Cx8G_x~vXSfpduZta>%5kUn={#G5+pY2>P6F<~8mX86ye>!Nk9C2+ zm<1}Zt?Zh|dUU=Q+Noa6QL#iS;QkTr@L-HS%G62}>_$H+=k?o9f}PWo@4@I}uW&33 z7(TKmU4m+prI4vGr`^QmACAZHeWPze6E{n+d2Hx&pu zcJ_K{HbNh70XmEq(tWMr$<`1OfCWETeK_52Pi?8Cf*t?iM|N4p<`e{;$5?FI$Uc9u zlO_9u`6=4>c8d)N0$%tSz*!~SQ9B=H)WqWULeHOZJ)Oq^7X&zWXOKzJO0-Wv=CFTo z=qcRV9`f0{>J)kEG|}iK6!fPG4NYkf|A@a3%x)=f8L>9CsbIDH5!n|U=7p^^S+)?0 z;PHhl%)Ag&K0=40t^KWN7ozS<9O#PNA0Wkn2c~-6Bd~muImDhbxwn5F?iJ6RYZf`e zy$N8&I#6M}ZoEMY=MQ!Rn(vepl9RY>v^6UY>CjGSU$_2zxIIFHd`yj;$nm<)jrLp> zwDuwkVB8tbXni7~^_AzdMj>XF8R|K@IWVh2hFDCDmr6yzb)Wd{PTb}^t7eOs$7f3E z5#zQ=C{Zg3N(^-(#E2z~3e<#*%>*4RfF-Tj!~BnlHhqm=ZKJUfP%%Ycf z(vg0*Hm@IS+leP~R;;I(A(b_NG1bB|MVsU8@Epbs(wqn7KW$v7G>z*eyvfeT(X(V5hCYg9Lp8u{hr-jC-er>(cCp^siUr5vc?DNy% z-y~mRXwGvye`$(JhK%02R(akyYYg~M2Re#5Z^(niKjoPq03$8W(PlR?(n=Z!{YJ;` zwcUw=qbHUZ)h2!6)5{lCoAsDNQqclOkDG9&@)LDf9-C#zc*=Sbg9c37)w(P*lH3hw z8W4lfu*y$B2N5OK1Q!EnM7m7g2uOS(P~iMoWxYmHZ$6eon_mJ~^I=GY;f)n%UgWHr zSQ7{w?$j_i|Lm-vAe;Vj&kLAGY;;<^Y5lhTQlO~gYzu9lv0QI0t z6Yz*EgY)#D4k6*a8w=OxeaV0fW}IN1`YySu#HxKX|I5!G%RW7L>Jo8TV?3@*^bUla zb-X&Bb;Tj~KHCvos(P#*0%kctLW=hwkFa*MVVZ<|Y7VXQIOWpa0P5TK?R3?g2s%)n zn&EP(fkl_q@D|#koi>^!y_G`8$itJS8=;y6%+>+Awcth`kZWp$CI?amf;R^Uo(HIv z5%ngXWC;I3sw!lG6EtSwV$OisfqW_fO=bS`k3`?Sim#`j(* z1n?TFS1WWIPabFV7+xoQZvq1(K#!Vu>r-)&%3%@o9^YaW7-}{2I@BvI_3`QLj&EK+ ze;2_4Ds|_GqQOe!4ptTsYJFU1o-BDoDC>!Pu##8`OVl^%b{Z-^g>*h9or9IC_h@Jb zy&HBWG})gdN=+; zY9|8~*W5nqUB|zFMz0W0RqE7du-dOaYhw;Zza{-JoYrsbPQ*Z|fX885=Xox(GnQMU zByhdX2YYDl=9K5Q<&=bv0iMp!J$6nTyp5zYRmlbEEXVFf`f*f!FU9lk7>9Es zw#b_@P`<<5Y7{QJm_$UsxW>IQlnH{Hgv8PwP1oYAL#MHlvHv~k{WF&QCi`1?vdlH6 z=HoM?qghbD-mNOl3L>}q4P+E@h2R;HuJ&I6j8NMl4d1ZVlGE_lq?UJ~-O7mJ;bN$d z4dMLJLcOr}&F=Q^?aX-l@p2v@FPbc3!njB#dsmz227oZbfy5;NMd|fs1HaGq@j4); z(RtyR8`D^$SQQbelqChfXWfnZTg|J%+-jgU`AkCu_%|xJGec8y8hHgYgOo|4B^q{b z_TYKvJk@5(Ul@&K=r!&3ivDd#DGc|r+EFVWTgOkfS=z^^4u^{tZxdhT=)iyL-?7N7 zV?A{?B9+@8Hz_R%^x!CZ7e4@rMTiIMhqsn*b)av;H$;&b^^nTxKR7SPk14Gb+MfkJ zUZMz8G8^4$>$zezetZ`&k6=i!6Fe;d4}9lzxN8oYDE5p6ceSGg^B3uFz!{$il5$z@ zmrj8Y=BIPYi+!foU@uRsb^-26DC}cx`GkZio~@&y`;qI#&E+5HmhMPqJ8IY%vOEYu zbw5twqG&7-^1UNzKXu)kMwu?JwdJgkNhoIIqG0dM zZbz&IrQCC&`oMJ%ACH_5vQfN{PT9GIGe^^1-&9SjW7v(Q0&LYbyCnmpXrIx$qD8M_w~ z3h%Q`2VUZf<1?st*r(tRAR>`%cBgwT(}v?SMMVf|I9+vg(C^#MKGSm5ecLg0U|Y00 z?0KjxY&V=1)I!*#kh#1w_nyRz0op?NflWb=abudR)s7V4xlr8$2355`x3EL&>ty#& zwB~n=rM|Gj{ORrq5wLk2SQfZPLT|NYS2xoBH7WnoZ*{zdr5N-=k_q&f4)S=jH=2!O z?E?~I+E>9IxE=0lg%ln3XwE4{LzUY?Ia)*74?x(iK@+WB1yXBMFji0j)OynaX8hWr zuk1KwaNfg87N*Ce+27}iNwhX=+*BUx1(VL;BS$kMYiF_uO^2XRx3ToFqf$meA$g{3 zA*7+-gOy+`MR9D|Q_3BDd5u|Myb$keahFjfm$fPsw-b;{325aVYu)bX{_YN*>A`Eshb4m3Uh?elF#}W75VhD>V7u z*V|=1ws~`2Vy^aHSYbN0J1+nsqFRCmj1Si20gGm_o5GI5wzdOlybRhlbRF$#$3JOA zN?4+mR2@-hIx!rc4|V7MtYe+P*#eS(Jm3Qz;o=^Q^B(OfzNwlI0b_EIx*2KiRIzak zaEHjcS#0x(Uy4&|qbFe#wl`#X@!A>%=hb?jdXV6 z_#{hOuSveokqQOtQBHMm8d#D2J>-*1ndhA2i=A3E@Sr54YbApK(l7J@Yw?6!mXiVP zBVXC4K2((!*XUwb1Zy3C#`zd(B1c(siyBn}oeJTEHhL@P z+UQ&X>#_pM%df_{mlk{HAgo2s_iadEn)sf&&RN{JU?RI2Xsmhk zEDXE&V&u2co2leIok(yo1}m&n2TXts{}en!ou|(sWWpVoz$~jqaGaBxgn$wIYPVo* zw!U$I83`CNAR}0RzS5FrHURkn0TI_fI)R#w!c-Er=09w;v3uJO`$--~q?kOz;dld@ zG3J&QtjZLwB!xQl4t!(%Oaj*U!zbF!Eqa7)i>%!P?^~YWT;u6VT?AOhRkw%N5PJ^ zqaUkjs*Kf>f9#VVNIaO8S?P9-x zq)bZP<^PiHk*-V>Y;Gsti-|e4)(XTZw9kh!RBKBx^Y?Kw+NfsERIJ!HkGmOH8Fs!a zR}m5M#Wx4wP)cK*I}68Idg@%Omm3jE|ACEob>RAT5=uohya4aJ>-cAwuKertdu4N?n9L8=T=QmUgGB)!qI*YGu=E?pD zR-7wy3gzq4AR--!cJ`6y#>=HZOx%K~f?vfJyQ-9X?#|{&IHB8G?mkyYJlo-|^gLIN zps=vx2>L>M)MF}+I5Cr9~ta`F<9jJ9k(h#iQIpigt;nQJzM9v z>A4@jxhk$Owy**)2(ThkWz^2+F$v4Wcj0+Y+3B6ig>@)V^v83vpC9ep7he!^>95ir1@rV5q5R&ZAjN~PvPTaq7IreDttCw0*pWz=D#U{gW;KbZt~!X5Co`e=7v z+O zVHKs}yt<2m^Opn0$5fHePjNCcxoo(aYKt&FA^D-j( zmx>P<-Ox{`8~TartMQ@Vr{z#~OHoeEH@z$Lm)!A$%8XyJz4`g$e6%|L_)Wgdh-xzU zF1*!Ud%;G|XDDKj%=SK|OyY|@7zSFlAD;#d<{EQrmy7*&bR2%vJnWItji2!}MgrIQ zrHiqqY}FrP1$ebE8>8b}f0Q=^4p2zE7}lptuM6vt)9sNQo;|$gH{bgBuytZNUzPDQ zw0hGW*eq~Wt$y+~=&i=lTFd(U#uP9?C1sgY-HpHWOCKs(tVh2I#3Wlbs|uA#5^~x^ z?FfU?3(z$?ZR6L;1ZCoivE9xJ#jUGq0=GmMRonn?SVvE;tjJWy@ z_Q5nCrJqT}M?#7&Sn<4y!lukdpbiv1K}OZ_H+Eosmo<*&7ZRxlrIUS?s5|#I4?Tfc ziN2m!qpmK{0n&=gVD+xgl0xeu=U}7n{m%3rkN%#5yM6%th4beWRCT)2vKfC19i>I^ z1jIgzXJi9?NJ>!LBvMY(W*<(+C0)Wm8iFvNSy5Y*uHV>_P}FbxZy9xqW?eIjA4bll zb4;;aVx9#t@{F-BrEt^tCovjNmoLR+jyR2q_ZouVW zj0ENyOwC2qZnc(_4BDTG>!B-OW@rPiae^3&<{YtGi50)@G|Eq(@2GHr!n98V8VkFD z#B^(>^RBR;cMFNHO=ap(WzjacZ019!XFd=zURlbH7b@XEiqH9laXc^f0}@$gPAz^- z+}&Tk!#`jG>o#D38eKa!c!){%fFqPUUvpIk=vz%|mNVsOes{;*+tP_0)^lH2qi>Rd zCM94*=r%AY0Fs>Rahk~LxIri%4FAUj3*`PRo9Fp)3ZKHlo^*bpn;0X~e-$N8Azn)t zJMk!W5)YZ4l?=7tlpCo5l(;pDkU>_)!)iP$M@?;XFHRow`Zmy7|8!03=d z*qS-{;Ms1x%Xf23zLb3I`uJ`=l>uM+dmvTLGOxk^;|l(PBX3QV8!2na9RB%2yLb4f zy)ws(jT4P_V(OoYgxgK}1oUUujxv!Jn6a3tP`X!AYhsr*XkRoj?@fkP(n?Lc12X%V zQS>~3i<$7eN3+PXWRq;o1^Eg6toDck=p*76>TE=<>X^`bYWZdEZmt{%nU(vC1gUSC zDgJgG`-MVozyYpUZO>!uyoc;kB;rUGU`JUI(h@4?Rvfgs3@^QrKwswF1j8J#_&mdlLL^?7J84SYFsGII~09jofYUenFM)9xC z$M~YE2tVpVfzBh224f(B0R!Y7{j3BQwIKi@hQj^gv{Ik8ffY0&Mc*3V@W_)<8+AF* zXlZ{WwNDinDS?nf??79Z_Mh)L%!L8pm!}sJ$AtDAVe&fXZ*u{l+O?^RFYsFMR0AIY z7}O=Y{_CxnNJ2&tYlU_#5v$8DMrN3@Uo9Q;3%_~2#B><(k3ent9Q|mlLX)+Nh7$Ye{+PHfweR-e9Y{=y$E(+L zMq3(nbucPVRD617!N6{+7yib9qf-k1TFTyxJ|>q#&BtOTjz=bl{qcd2cZ?`r3{uig zbw!YYNb~+2fock~foVYRBII-Ci7260rF|~_RO&wJEBp5Pt&^!dv)4r=S8IV) zkjIxpkaqn!oZV{LTQ>$Pc8h!EOtR{?!Q1Tw#V`zf=Cqm022rgP;FE>V@{2uuBY>q} zuVosV#+91FxtBx8YDGm3;d+<&Av0tsioKK6Au4`RcNM1JP}zP0#+$y^BZk=Ir@siV@D;QWF%vq3@pQ-c>m> z5yZunYDE*A7oPQ@vNx)8>zPQ44PG2HSB;M7wkzae>1AFl)^GJo!Ct2GUsfept8kC| z-E)3g;$hKm`N(AQD^Cf0{c&yHGUfwf8Crd|J`eAf0BpG}SnPcM<#d^9hgijbWIBNV z9o*lI(no}OX4Q8&I-Hf|)qgH8#o$C-?0>{_|0?Z(gBJR@2hE1xkEDVM@cV0_99l+r zaQp@AOiN}Ck2u<3lYo8N(AQE~uwLF*%rNv{YX_k|{`J1ggz(GOBy z3UQ!_=zM)GK`F=hO+KBd+w+GQHb&$hRJ{K8(+HKQl$oF<=+cM1z_^L8%>{l+m6`#i zOS@Xk%Ija7-RyokSt(JuYjX#i7%_94k%lNS7w!y@KsscmB!z7015~b; z+k9}ALYcyFlXN{|&*|sSO2cC~&tZzAQ>g;`<=S ziVLprtEOB>O*EVY(8e>eO-(y=2F#i3b!)m8Yku{ecY2XrhnKf$0TrM|WHk%w(e`=q z_s+gsP`zCu!Ib=EaUe9YWz}@Fb8e9mm3Q>x@?}`XP{cJL4+q%d4iHfnxcR6&G1ZGH z<%(5PskVQ^JhtomZLbJ8hFNz;KmjYA&@ ze$)3?lMBNb{i@Sh@-t*I?-+jqrI^uXi3l!lbj9|_n|+h7sZfyG7Q#fbJ@}P)Fsvz+ z)9Ukk=^}^DVK&H4t$6lnvY1nk^nbko6ub^~TE!+B7LM8#*1RqO;C~gRv|w%&uT98j zHvA5E%C|fTzPa|*%X5hr~ofO>dK zfGBM3o~Y_-Udc%Rou9tdSAYY&NiLwK{5O&Gzc+gI*&`q>jxzgPI?%%vwg!7q4Jsfz zcQ{*xCv;cYT#klCEko&64T05b9;5fvo|0ymvyf=s&4;(o@0q&9XClgFnAQ7La!8}K z4~$5dnl8y&!%c)O3D}rITjWx?M0%oZo>~Cc@dMob;}LghRX zOv^Fp#{#R+Y-|wdff!l+s2W9w-+yt&qT9fO%c75J{@Qh}8lj1o_BfJPR)&KOWR(#_ zH5+#uE85xuhxw;q^!on5 ztEC7_r)FaJz*HSVZ^81<1&x5$aq1Y`Q;83pS_;Q2$)TlYi5b}Dv`X9yrja*U3q6#a z7ZV%KjiI4Km3n>Rh8-KDV}Ece7e+`2XqmU8mdh|0CHDk`nwU%3rlQ%SE)c^P*G&__ z36BqZB+Bfft3I*u%X`W*xSVyAcDi#c!;B3to9Nn)Hda9Qi1b zsAYVQA=c5Zd|O^)vjmd5B90dPe#0Vbn@Aoy1FI&KNsXLt1a>odB+nJU#{`4TNOy($ z#5(M)p=36^fdv+2dsEIo;0|y7{P}(QZrA-2V(nf%Ye6|unpYmvNbG(77)lIr z4z{uFyeEr!Y!l~u^(X9^0jnl*f)$pjObGur=X%q;qP+x{<5UIdxvuNX%W*7Ys6 zQAT=4DZE=AYc%4d#mmG+g5`EUGMZ(KPOwpPn7T$>Qog?34rJ0{MBAn{23&SpZ4P0~ zUzRw^(3XWA=(jgvp(tY5!PYBnNl2d|%=nPw!Grjcxi+8MpO$OJBqku_a3|Q!`P-_D z6t`2pX@o~URG`TX%t!tV4>jxGexmNov5R$prH^H)fdgcp`$@ihx^-{G+t1oNzfAf3 zW@?C};&UP6x;C2w8Xu+OAGRut*%Xvw707@4VpqD2wo63RSqnPPl$X(p@fV@Vg_%k1 z;E}Y@C=-^k+HLlCBObG9sZs37nD|FB>L)LJF-3DUldvtD{J>;Dt(h&o*yu!=@s*z0 zhY0NyKb!T4D|0$r0IX9$n zmdr*n-ZH3#I5XjE<2;q9MK23GER>&yzR3fltW|Gmd2rsJ&}sC z9P#Asy}h29kffygM$6-_h78t`#G84qdkME&p8oI4p4!bLgItIi_GdScFZmZFf;4>z z>?j<->@&y~p2bRm_#(mmXs*T{;#iB?y@-{lbU$j!Ea)UP_-_3@wi$!fG}U1KHb*y5 zq@vOwso-B z_`>(W16Nzts$2bzS^udKns2_tc^iWfDN`lT`IvuuDU&Rjc+~$T%zb+XaUAuOh^zDy zMXQ^bF?LoY5ohp#j6*NQpB4|NQOh~_yP=kBxmW$S04N*8a1A)lTv&17HN82{y9hDwA>nyHUp-A zP`>%>abhyzBN_Uok|Y!x2Ha1kpqk4@e&0L02K!RFC{Fn|1RI|stL=mTnM_6g>URD= zn_H8Sj&d#wGBN|*v3;ay=%MJ&z0SOf{DMO2E4Xj();{4Va@!mCx9q9NKF4Qxf&5iQ z)t_hQVd_OH*t~-6aoPFR&EAn>+C{8fr`P$f;CSyJ_#9_}9$%8D5g6q#;E^O7)GMhe zwKwm<{?KFHzq}Q+z9;lPHoB5vFbQ`3U8C2W2$+NTB%=m{(}?eaJa`>9I!>|HlZ1Rc zzXP=~q&pN+A*U1Q5&OFrhWxiaR;AgfL1PmInq-aHpBK{bWgT?Kkr@r4&}4!wiH4Ng zSME6|Jk_P5%ou%q!Skh?dkw5CVyxzUtwr>ob_-euX@#^jk1ymqr{DBCBB7j}aaokKT)k@qdhoAmaEvpR4j+ECNR3^{v zfG@AQcxZV~9-)u3z=v!M2YMZebY*Z2FV^UAG*>9WAHQpYrsIO$b~-B0*S(f#ks zAIK4y$`#9X&KbAnv<;ZZbjpRDOx1Qr3rgl`=mk`-(_Au(&d!8h6t(N~ZAHbJ<^WTI z-SAhcB%AUnLm2BeT4zK<YyHl)q3`s3%yKc``v4-Ou8Zar-pQiMa^;+bVkcp$2W&AxfFvc2(&tN zB+f_6XiMelW_$i7W5E4O$lQSsN?nLbkT3#`fR+;J9Fv- zdYvS8L4}n*So*Lc3x^+*MSeR7YLT+dRc72u;+qRz&KhIY00$aqY z%<0fFeaq9{FLdnM0R|%>#p+DaKHnAbVfGruW`5+}&z`hQPs=_^cLkW9&jFs<|B#>l zTLnWNG2s_79m7&Uc88Oku$lpQcM&9=oswgE+I%Flhwy+)G0Nl-P1@?7*RuREhu4Ty zD=xRs9|+u5MJ^n`W-eGSk-Il%X*m71Q2-E>Od??Bp%N0wlDl^gx44WdKCUw=W!eZ2 zz{Ful3jef_R5B_pc(O<(S1r!vQPE|?MsX0q%7Tr}`x1Ux2lnFWO-bKtN6`kClC~ff zSPMCU_wva}!hWK-x2lF~BazRKN&`~3FdB1nIC`~7wT4tuYrS9KZ56pwNm0pCP;phc z)FeHZOS4^UKn!B(*19`6Y9ATXTh!G%4y(N>emzeV1uc+CAToz01ahj5P|I#*tEJr1 z)0IG53hS~t$MoJjpLo(}n`^TM?LD;39oMg`q;Rv?$cdl-5jSAUyz2L7zZHW7Yile} z!_d%Yc+7cy?(zDlPz?V5U10q@rrhs9z0O(!8JRZAifv^2#&ROp$7&|CvD$LNLGAWU zP)MD3-K5H<#Pm)teY#*O<#|2l{nhbL@xAF6FO_n-D!ndUd0c)q*iODS?H6zJ4jLu; zCFj~xMnJ1k{scEb)o5?7=7WT$!^PzYg7ZO)gpJD}N4a{kWIIk&+n=xb3@bq$w<3TV zmB~p86xzr;_o>U-eS)uS0Q=+n5j#O<=pE~g8HD~W$^*?Ye)gfazRzV*)Ds> z|@X2JFbbeus0W#?)WnB{b zCdn)YqL3>M7)B;_#Q>{cM@;$xGnO8@vHAUA{ToF)FOOdoT`_DWJVo)Onyb}vN?9lm zewp9F0j059E!DA|-)`C2G#YP*jW!SsiJ%>S7le>a zg_thrl|NSuyu;9smdI8?p41hFq8K;O(;EOU4euW_HbW^1F5i1ax3TSAnk8!?`6Em1 ze=<-w)GbC_fd_G+Nac$`wG zmrZ1C0yC$l$E63O#eUdbV~<7vY2UoJm1CnM)RHA}p6b*eZguWD{Cu0cmuNj&X%sI7 z73c?k@#MhIN}=3uBL93WeTaXC$yvWS4EPj@Ux0I!>zEe(6NrK%rl?5s(g;z`cZ@0- z3_I>W(pVL&j%DrEBTp!k)PQm0YN_0S5$Nb#3ksY$-oOD`$F@p6W z1Op+LA98Qb#6qg=XE8jzHYkUyT~a}xS+=I?ZuxU-XrYpmK}Eyx2wwaqK1s<&+zk1Y z#D=A~ovHNh?sYaYIdn6%aZk1Cn}TFHoS;!gN+|}$^5=3GOskqzA4fR3>2Y(XU?8tQ zQu0cv(N-x@_vuY$a{>c~;Q1yr<*vs0Uc&6Lzx(DHT#@*Qc(7CD)Vx1BFNzPG69=-X zsukU^vb=H555r5@)k4nKCldHXd(=|4aD6^68*%S$k5+g?ladH73KmX+zES15S9#=o%MLY7t@UF0QSmQ2osC#X#2082uIn?!I@g5}1KU@OJ@{*Nyh; zY`jRLLP&cc6`G^%_vf9b=sur1;Bw&pydOI1*zCuX9I&6U2|tq0M#2ldY2#2AorabC ze1qkCZ5k=J3BfM}Zk0Rtok$2OH1;s>O}O(o^v4<;x{MJg+Wcjj^;=&tT5}`?qZSD1 z#lFfb%Q%9tnmpV+kA-)p_q+KP6lUU)0DrGNPzwG!5pv^hDQH_Tdg`edNHTZ(gg8{$ zehQN&?1#T#p36lXJzIa-mgi*+4HTX|x%dde9J~?@-;p8@l-dCh@q#mY1SPSk#Y|Nu}%=*RI zNGdm(wo;+>*m`izKUG39-Wt&8H@A~BSi{Cy0Dn?jlMZ8TZH=}#V;HBZZVPNdqIHv0 z8ha_isI$Z9LvEY{zXI7Z5yzMe4p4X%gl%H-*?ndY${w1w$I7OBV8Dv26L5ap$bydE z8iA9I22pYsF1FcuYLtuxOfnM<$1EX{6 zJ*nXJaMw7;0qkNUZP{jyodC_JIm>hqkL03er0wH~@KH#>0r3tgB+`}$DBr#dsaa9V zdf|TU5;X}+C|$|qcl%+zJdbns0DvFutJP0u=DRj9XNUK(7m#irH?-42R@tO-sIN5& zA^QhhZFXYQD(|`sNcTLkV6)0Msj&AbA)`2*q`BXUDx}wi<^jPEk=wQfhb!eaJ-ZUX z^$zECKqVue!e1{)_y zKdjFfU#MjI>z3>tXLICw)ttBGIZhWUp#>GIRI+^eh~P~1t;UpcjF!n6!2Sl0PJC~b zQ1KbNn8DwpsK~z2y0p{c?STHsIOor72DIVtRy5FJv$33O`@xfpp;hC7Q*E6!LdwAj zeCsKi`LvgF4`pemrGBg_>H^F9{{rrO`bH(u#*^lXI$p; zKibrtVTRb)yeH!M4L_e60h!S8?2@gBh>-ZWCscq>t3X~41jWBrB&lZP@p~ZAy>CGk z6Jr+9)C`=BL7YwHvKhAZqm$1T^BAS*gPTwIKct;yRF>V=wv~{Q5Co(fq(Mr$yFrkY z?k?%>?(POb=|(_0rMpAAq?>Qyv!8cA@7{mDG2UN};g6g9zOJ?AnrqH;9w(S{_P&gI zQiS)9`4vO6aO+QXGHrqz5r#B8I7>noiw+l?Vks83okeufY@tQIS&BOe$67OE6)u9? z3fNF5a&#CbndC2lJ+jyEr09D}yhMM$mX;Qv&BJ#)q|YI2W^W(-0Q(V45qOHl3ctK|#W9A! zBK&D7PHBX2!7tHxp;3zbwXs6Ekh=AdM% ztCoAZu%^0b$Ydafj8o=ndef+iMN)+H8Blk!XhnjSXaFd+|2~Zw6OSQnQd9*0Up_AN273biZ%}EI*phR(5O-ab7LDj~5^^fWAw0 zBX+xiWGX9dW5YrBA;S*ea!clqg@&pt-0!jk?6x78d<&q?<^TQqrHR5b4%t@W34*-v zQi|zf#mwbfN=;OV7*0*sB@-aiAzUR=9bK-qsL~n5=PpsNf%gIJ^iw-vc~x-5+2Xs0 zI_c?;fEfssHQCVZTa7tP61*Vc2XF>y!!I{eoxs6E8T3#amK*F4ZXH4qyDBc`SHx1{ z-selUe*7{lX5Q;Vb?&%x=?%3gnJ_qx?(n5O+TfEKgP_S!CU2-G$ppKLmhpH2+GQ%x zfre+&zdWC=dL}jfbZE14&n<$PQQufDiH>LFg6Od4t zwGTfpXI6edRuHiJVg@8oDZS{9NcZyT3EPj(wpnj99FIf$Nd^v0$4k3uG=Jwqk56W0 zC~IY`k+n&M@RUzIslSI0|K9v7vri(t!?(q-r)iS2XnxM1NfOBK8_*8K2Hp#%Mb9PTB-wLb3SnC>*Sw};Ch4gMkW5^u58K~CeqFHI`iN?Xg#sn z?@nMm5ldhNVzGerneZi*v~yM}IRap_MWCVMjC2jk2)UU&5 z#PCWsKmlj$VwiUr2yn==z)tJ!+0IOe=e8)Sm&XPT?Es*SANw|HDETHNi`G{j;?aGy zHj%?BBA%n{{sTs9b@~M5#!rEAC^%H>rl zwlH4RubaH`+C1UaQq{^_hPm)ge=ubBj;n5mT}GD^^+OTR=;Y2=c}~+S_w=EncI6*e zk2=T*K-L@pzAvDaerxG}LPoHnmgf(PijOw!m$g3Vh$-Di#9B*OkoND5<$sT^5B&Mm zOcVKSGcu@Xg4ZgLVt<`J)YW`g z9{OXVc-(r~_h&41C$QcDNY?2O)~P(2WP|fhliuxPgHo$t5-b$s!_l4yrD6eBXwDAlxj1N$`W~Q0b||zub%h9v+`FoK;OtAsf{AxR zRi1nv=-_Q*eqWcVSQf)dznhIdG`hqFPl>k#xXr2D@JRuRx?Mey9Wy!R-C@a$$X=j- zofWUF$J76nBR_j>G#yi1Ll;)k~zM$k>G=KaVdO;2e*8 zrh0?`Z`xy`3i8Tlp!vSkjQ*iS8Im>>%DMsgcy?e!8yhgIz6L(Z3~H3K0T^(|Z^(t; z+gwvX`L($Uw(`AO}GqX9>@X9d*equa-yn_Y{5`=#^9hUvqd|E&9aX|=J_OmfU(b0Q)m8{I1^ zXr_9%n#AN{0F*oV{riXb32b8R25Y=uaJ6a)JxnD^L}}}0KYCkOFmtyjZ-XcBadTk8M%Id)#$t|l3J)20s>T?;heDef;y&L${=jUXW4%Taux z@=1RZG17C>17(d4Bi^nZCZG&w){rh_EkA3V+PQWF17-34PeX&+FwfO*+~RP<*O@Km za80L5!-u1q?{GwqV)UayJ2v<2npyycPofYiR7s9p8i0bH2(Y-k=#C_agmycd2Z~o_ zP3o)l?)!e_V<24#OgO&zl6O@FU|bA#n{KsYlA*9gt^B`KTHF{fBtKTMaOc9Zy4l1t zfRK1HQfYwl@HycfObNg~x3^L(oZ(y=!NPeW7L#Ht6m%TT634P~d25UM=uJ4!aaF@&m*B=Ufmj4+`Sc~gDfqO`7E`}&h? zlIZWgr>O4Du8!5uY8m{(XSL|6zMp>+5U% z*Xce_lX~b z_vfT9k_YUXUqk8;m5JYw;KmKCVPu^CLC&C#A!s6#?GyUzr!GkHy|`Z-P`5>cNPvB- zvjiqSN2nHgB>8{+&wx-9VCN3gX)uyAs79MDHPbfvX;v8~e>13mqlm?ZQLX)h*!X!P zjVn6fCpM}gL|M|rSSs5t@9GAh!{jgOIimd$6JR#YtC@m8@*kHHfOmeH>wX7-?9E)x zA--A}?JMyl&(=C4j}-E(w7&aL5EZU;CzEW49C%5d#&rI5sUf_1LLZ$~B&PX$Q9idF z7CBV7+@FoGOpQ#D5StqXSdEu1M13DHQV#=Zr8+-^j7wmQ47_qR`^gL z-4%8`R<{}4)3?X$Gk>sMP7y{ckp=kf-A#kU-(LbiZJu=FpH$I*e0F=Yf zW?X+KAH;8)iZ1GXSCB^GV>27~vjC(j`$g-wuC_U`e_@Q<6yd;IP*f-1{Us4aNW^Tp z0MYzi(W!ZaX16BX1-}2D{=BAW_*Wl5!2dVB)L9@@4C_B1JqiS4+8;cHQt$!BX;3!m zmO-!5Gt|NpZ@)gpHg$?0;l4Y~PS}9OzT~UE6%qQxVkg7;v%Ud2a+y7!pj*_;rPDwF4o~QsAy6-d}I4P5O!EO+GRjC;jC^F_Ai}=hfx%ma)U7 z3Uhq>X;nBTJ^m9&UGwqDoz3}vZ`42Gb}Bcu$G{)pJ+agx#Z>{sEoSOE8oD0jd)k0xa-j(x{9=|BNIdLpeTOb8nFH>8FmN)alu93nwS1 z{jcQ{Qh#lp9DjgU%zeI5% zxBkg-*#dj|x=g>XnaE^pW4A<+2UKt2S63s`V-o%bT4%_DEy~>*p6B=X>I0t`L9w-2C>KzK0Ce;9+npT^G1d(C@7Qv`;Bz}!Bggr zfPx&1L7(7Xk4hKq3H~nqmD1rA#J_(A5BL!~cG0;4ZLxqNx7YV|y|EGmx`?k7d8JQ* zE`y>v2+;t{3BnI1F-8jB+!{fMQe>O=O8ETeEe9yP$w8d1=B$K#h3ifM@(2HFll#91 zb-mNRii-c$g@w${W?NbMo;pP$2K!gAMuD&-1Qu|C~&0DN~WOb+3YxF}A?XIIw-5MbC41ymNCZqDF9lK>q!V?u#iATFK9-PK67 zi5Vy^27m?E-W5v3m^-E*@%I<~>&bcj3G>fD-|YX)H3pGkM()L{&Dm#^DZY8KM+m^w zDFBdCIzYRekY=9z+KAE-q6o-d)IIud%D1{dh*xN=RG3?*0P?WnS|y)c14yf05#ocY!sn@bJKR&#dfd$ zoB7z`H@!VLN^p4ShA|ugLwC7i1+1z&01 zH@HAWV#!+L^FO{4Avmch{)#MV|L1ia%n0;1byobrsQz5uTGesiK-~ z%cQQ&HqhdmSK22M`4T*VI!*y;;0uuqxUpm@e|?cQ1xB7$fI$Uoq9DZoMn!mU|H(~6 zv6k3$K2_>30gjYyVUGL7K4nWF*Jsld;Ggj4xjWH;&3Cy_8y)kH-QA}86qadXVj@ti z37$+AdgfwnV?JNzO<8-Wf-6mhTGB@_h-bXqg7%tP85h)Yf*}e;%Ry&4>74##*VwqA2JYY(UZI_cQ}02D_p zx#@2sg`E5b7!iX!`lY3%kz7%F5K^f02a~W`T>&H-c|>>dl}d#!R<|ZJAcsT}E&0K< z3TkWPRm;?&%wO7d*ck1D_k!|HzZVwJGJFtml;`90L2WZju`mPoGnl%v26O6y%?Y&g z6)fLlDI(i5xH%Df4E_Tq;6(K&*XcJN;Eevn?&ju3WTD3E^!hWhz|ry;0_rcl|K-sE z%$G)^{qFBEEiydUyC696}tHL zpWF{BO1Se`{~cvfMo2&=#7$)@1?$8L-ZXcUyM_{niQ~uU^DuibbWx$4ba{WtTiF{L z)!GghSYF-(G&b;r(>c}QSWNIwilHO3M|Qy^HZTvhJ-Mg&I@Cay+IT~k&G`g&;0gWc zgM{a!4baMS=A$S43Qwb(A+VCiHmGsjo)5^OP+HAS-UfJZPj$d8bM(!px&mz_3qyZk z3rZl6Wt{we5~2c{2%-+|uL3{kh>7%f1n)LPUl%*9+RC{`zm0~9sa|Y$UVq%zgh_fW zJPr!Vd@-bbq5|2EFM!->zgg1oUTUDOHJBLs#DlM7@{BIO4={gavIO+AY`tA9< zCblNds?{0JFPU4GhOQhRS{KL1R}ZKrTimG~_GjcD!aafijq>Rxt+Mm!re15w$1Txa0e>sf({g1b`aNGOQFa#_DC|!Ip zXOnRQxmjX_FCSF#mBhyjKTrXo@V#uYI%Zjk+17({;U&HtV3-A@ zmD>d0SV(N)ApUKij=}|{KZwaiLq^6=_zflMlJ#2@Qkh%cQb!4o4{f+jD=9zfDbOl@ zN@EU-)9(!|lN*~PS9NZg7A6?J{ zt>G9QEj(GS!&?L-9Yw&37qzcELvd;=W3TX&`?<6OuU=zlam6d-Qs+b z0TaBRCySyx1_7J{?G4VBV8bh@zhBbxKlZcjD9_i0nCLnsw}H)Z|A73LA_<&iO)`lD zQquAV$J2cclfNErH{qliu%^iP*IB0;`xHzHqUR17GA z0I%jcB45^0v;-IAym-a7A&9lNvG&Og5s9LRe6VoeAd%jSgiCVY_MF!cn6s(eSY-V z418VQCCWciYc|qEzs4)j0q0op)HltJNH3D(G-eYYm+M~-9QD)GK^|cG=p(iAT>%n4 z*9uE%dLZzgRFan`>-}&d9P@~oDUU`bkq3l~c~7J%(iV2Rf-d5vY@SAky|2JWq7=Zk zzFizG?=fk@5k5V+zwGS({Iehn6^1rc51cd5PT?`>N^DD*!Yd(D5hkD|#Y3Vhq5zF4CZcYpZ&nU`N&HBc4+{=a)^nZbR_n00L_W8%6dGw5Od*tbJk0WMgG)~}hx18Z#9DL9`4rz| z;V{$Pyp}Hh;ID^^c`DY5+v}~&qOun3H7Q=X1!l%!%h`d%vLaySaLSA5eBEom$19V3 zHuP}cGc!>ZT@7pDJd)xFjHD;4O&}@ye#%+|YUgH)2zgOXr^&gweNrU;3rz9?x4 z3vDO?#J?f{nkWHg5kvSmaC(lXTg){DLNXyS4FhZuu75h*0dI0|YyLmcC>fQ55%9@u zzi6jRP&o_T)O#u3yYA<39M5x{Z)M(2)|e%(lY$xoh`eBr5L&*95`L`j0!4&>Y?&1g zLPEWkTkpmVN?53;Rqnltsig-@5~owD$m>7|`tj=Y=FD07`}tJUE`WTAlc?%M2AMBE z1olS}`2x3Petq4~NlY3HLC$F8VPvtTkZC5nsiFk#9}Z?JB0XzOCxw6lC;BMUQHbrt+Yi9z-zi&RX8V3+v(rBmq3BWU;Mc?v@ zn{^%`COz5rTFW>JOc=jhJ%fko1N7YJIpv}-ET(ss9f6*?QJ234Y+Ai18XaKbdE8&r zfE&p2{kv)eU%QR`z%TV695SYpp?ST=57jwT75s5A&&AQ-Uv57%nKQWORm z=5>K`Kbb*l&6a}x`K%m?G*)Mgw&KI?yOIbRwK5r8?kvD^uut2dOooNHMDPY9prhEn zf_km3W;BPDo)@zylclPXYU@sd(#h{e?s<-pX%&I1L!dBw zhbCRI(Y(`qJbz z#DXN%EjWUEn|oJ$tnX@3$@L)u7*^#b<1MuekMxU9UJ^;gFcU=q1jpR4Nw-QE{yq0m zJ}kqZed;7g_*#$0>bj2!{BBs>=gwA;Jq$brgy;AKW(P5+gad5lx6PlLLZLXp zpvo=XXMaepcOo&cLT{OpWK_3b_WdHkg~}UaX4U) z<$U0A*qwj_6GpgJ{J)a_8S}v-vR;1m=lv%=y?^zFjF2LA5i%Jx(az?9$1i%4bw%qi zmr&_RFhi?=LYR%%B~pF;DFvy|;6q;wS-zlhVi0qACbM6eG9WXuOyWn@`a!DVaegL0 zSa(o5TzF;AK*eox-XwC`G7jdMmeDsufZNI0%s#_b=fe0lVD#}i01TVM!yS8#&H9V; zy)wH=Fz6S?ue+FFywq|$*^ZI)OppiXgIz@HM9)cik98;GY7>2NbOkk>f z!|=MZd9ew(2%!IpL8erk!QG@5C*%VMVH3&+jz{q86age=rK1YW%YQH$<`7$0663Q` zZ#*6t!Zge#+yp31Ngb&to!oBZW+1^^OH|PO7euGS?P_^1G5H> z2mWXX(xOzeDdLM@;LqwvEw3r0m{`L{y|}5D=N-t)w+rfs1vE<=VUfe&@aa4{+!?2s z1Vnp1^la@PVoKmzQ@a_!eiu*8I9NARW0ty{XU%C+Yc}maFGKT#%Lx#3Bo^y!d@~Eg zq7|N|fqVBG>Iu3up!@CVG#ibvn?SAhi_7^O$ndyL!P=$M8Ic7in0&rWdcYMa0{Rt> z&FX$j9q2mAe|I_0uBq+5TUrDbLA!f|HYf$L`Q4**@F{M6M+fd;c88>_>smsPmtH=H zA$gR0u%#=^#NDQu$E**um%FxInNQ6%5uCho8~$Sn<#l6En~COB!7 z>5eO@3_Qh+2Yjcvtn%LHHmEmsa=CGB4XKyr?!C5YHOoQ-9OxoT zsFeDnjsk*`FwxQTTW(D2 zsZ>U)L|o%G>Xs8mAmq1H{C5``s7<;E^2)8(`xXm2TzDU@xCx><1oE1gP{$7!8ag5w z9XksWM*;O*mHSeIz+ug=fz?ruquc55G9$>DaMc~m&N0)M?%$*;zZX zdgyinAOZzM>_ej!*?YLn59^bR)t| zh(k+qxr!H3q{U$1vpwMhw(7$b%S!bET3Z3dlv4I9WV0_SOwcy?LdB_UcnP0M{cL2} z7Cc-{VSaq0*0i*GI&>zS*LRDXyBSvP2?#dObw9nYNm}WLAcmu%@Q#8BYb2?;^q!;9*9{AE%=$`L{UDB z80c$mF~@vr%uSnA`5sz~kS&rsAsm*Swm7pP7o=@cFwZsN%+yrF=;*rji;&-|cz66r zOJ#eUjHJ~(C@@J(VzXQjBR3V03G)OMh0Z{FA_`cSCt_**IVOd(EJ8RBhOkkulv74KIc5Q3`j1!5yZuH%HVLou**HhG4p+VaE)b7C~TLt z+#2SY4}2{=n|V6Ba3!DijnHXy`5pq}y-x!xXl-^tT_ zcjj1uy${&`h?x^C$BdfDV)VG6)8($~KY^hp9$ z9%oj}5Py7{f{x~`Qq}PtxjCIB>tQgg9?kWIa?nxBqakyy$|zDY8+c$y8uBme@fyeC zFs^ZO^xbg|75*Gw8zuST^m}bLm?Gl?m_b`(5PQr|ylKeuCUE%~&R)TD9O$o}wgzMJ za#IwrGlp^CFzFwpQ&GYvMsr2E3LsTYokl)Kij8bWbdlu_Wb*RT`V19xl}p_L)Z(mt zk~v1aGex>Us{};_LH4z-$5_rs;mNP+nL+S)nn)1Dw3;=`aT4%1Xd7CBlHQ9S82M zPS&O;rJD{0yl%$hSa$?mj#w&dy0OKHidc~I$h*1PufB1-W>c@x@$l5qBI6k<$dKs? zWDf{GC@dX0zWX+S^ETo)|0kkNXtjY=oK}GDEu}o_4)>e@{-M1tW8(O^f(Q!nR4gAZ z_8Xk4+}1iX)BOyp#1L>g zFL<_sef(ao)_&*6Z3MpG*=%LXG{w@?#*{vr@EK)uvIi zXDYmH-i4ryVeM9m^AhWXrKYJzY*tBrgGaJtsfm@QG3<`j2`QcT78wQ z$jOlyQ4>-+^t7%AF6^IAv1Nc+obf>}l6p8NkV*_OVm5Z3UUXzKkA7mt8d03<3W?YK z>M4@X+F-XO4=R$jv$FJx1rVwbZ_OGV%UED?N;w@ZPvg+>Ow9#u&l*$_7QyHZcL2f^ zWXTcjk~nU6Xc$fZ+}$VVU&1wDnu}Z=c>5{2-|V2oQ-8FSoV)wleDo%gLVV4oeDeF? zimUbk9L%SR0`i|65XyUtG{V&9fleKPk9;uV>Tj*1K_pM1aVXvJ;3OtSR-L?xs}`No ze~<&NgT~<}A*#v+9-nU)0l0i@_25X1Xi{oypEH3*-cnD2vice*gS`xX;GP}p93{w0 z`@<>9{#BcZCyHeN4a%9ccTLFqS8A*rpMT;^F)she_%^QEeSH++`iN}oCLk$LaRfwQIqe5~IOUtaQe>vEi< z_we&M!kkMQKlluWQkL%}I%pKC3@tuv4ysQ4@VzaTy1cCZp6s?p@z06?NYURsW#!=y z*^PBo^=5HIiUsD(9>d@0y6*CG=+hW^;OE@CU#coKRwo-Dn?92rTN?ao-EOh}PY%lZ{+ru~a_B(xfCA zl5RF2C^Xuu62FqsuLR;I&nU{MxeBQo74aT!&AV=h@w;ou9eva1Q_TJi*Fd;1=;LyJ z%I1k@wKLXLova*KgE*_uh4kzh-!pL`e&FP^h9LCX9sq8JnN`4EnfGea_;HsW1@6Bs zx6kof+$4gWf0#4Cy`}ZUydN(d=EHfwbE>i&%gfYc7Fn=Dm8(~dL?3KVPYOg-Gyf70 zk`y?kV~*Ovj~1M4h)Au|UW3R-X9ae&?eehWiANk83hsJnv%nxiUP1?_<>jHfiXPhF z+xE1aQU1MGgR;JPmG(Oj4*M$mzJeTXxgtX*vNVBe4YpXRhJLMBfC+sEj~OtvfK4;7 z^u0+8VUCX6l7xV0^SS@A-9yCn$P%!z>4w?1-aZJWZ&_F7Jtj|AbZWVvt7CmSSH(TLJL zCxp%p`&Ur0mi!8kszclFj6A}Sm}(8nu$agzF1Xh1s&3`-g59zV8UzEsNxcTVj91ZP zNH!NBX3K+(oCj$dnk)n(YrL{MvI;krwPhMl2X1&1o(_h@rxV4kDC~VYO5SL;w}q{( z@15A)Z-mAxzNp7}d8oNt)u2)u_myZinU?zI6ROjy*M=Zxf~rN_6182vz~?1LD`?-^ zK`;)^Q24`K`lq^vb3w0^wfwtvmona}kl^$iPN!p8Xe2}$84$>Rk|8vsHGRfgsd)lL zjHs3R8vR1;-3V;Gr&}5VnQ2O1(e{Wk47tv_4#?~EYAnXZbPi1x=*6u&x$G}Mu}_=) z7-YHPO9^->W>zh_))mI1?<&g4oRQK=*n?2uzMfFzwvVa!2J&cn zMq~qD_=){lyOB_QoKqDawc}@uXhI#JPB}inIfIo8fZ|_lf9(p{svl08M>6?z;gqBt zSSwJ1gSYgYkb~m_PVRD_0mTO+2#>RNOw=y@R_~hjKlZI3Iz)G{p$05@XY)!9u6ejK z!ydbKn;DJ%aK_?x-naAE$soV+W0fdx@mEO!+zz{OJrH_~rkjJ#T*#e23g&7`8vG_Z znXNjxguJlERy#a5>W8_f12OK6ZsH>(NpD7*F30n(tx%o-8~&_Qcbp~`HMoc1SS1mC zXhX2zcH!zP)Caaz=8fg0)51$0w}>o;gJPE(G>=Hth}&Vn3YH%m%2n<@fs9FOM{HBF zTD6=X-0OWd`*9q~RX(1H%W|JHo9LTM+x>>+IS+;20V+(NA%E$#_v!0SYI@$A?T3q* zdE-}P&arDi*4S*xsM~4n5Y0^8$Z54le4`5LzO&UxQ+e2|ZfVRs`a0pk@0A`7>a?OU z9%eR_P;hV0oJBybD0S#JnEp;-6?`zP!Y9nu9esTy4nRLD9r_=s@=^J=PM_z0JDB$Qt}b3m?!0`AbYj?Z8!sDiC4RD?xhGAlnP-rSu2ZO zZ20c1gQ)`4k;&uNy0I@)0)VNb?2!w;ix$DABly<5JLz-1sg^3^w4a)p&B%OeKN#x- zNASpY9u4iDnG7#up5aADcc~~62CV_l4|3g3`LWW9$-)av>LjrFLE~9^b2(HF>)W`a z@fW*sfka18T$!-aC}=4Tqms))LF*sR;2zrS@`^=*83bMHBM42vC2}4I%|nf9)#Lza z&F7rPIovL1DDeldWw9MXM9lT=xJk*WOhzuHH`R)PGuHG9q!Les(!N+;T@@KChO zM&n?)^<(HtGlU>6Mj+r(gc($p(p9Nu%n-D30!bu4l^(`YNt@yMuYp99DwG2c9J51P zcpcmIug%KynKH@)F0H$w13Gm}2h9i^$Qq-v{0JyWwg5boP?MYeC^#?BIs*npkPd^P z4C?Ml7@PM9mjxivmxRNL7xARh!?8|%L+cis?KN{+14P(7ThQnr>hH_dL@zgzgxpI7 zk>a2?RYx(~ZC@R?h>%Y%XcF3F8z3!rbp#@hEd8=I8f$A3jch3me|ewbni=2)NMU&o zg?Hq=QOVm6r7FqNw8YJ~4^1+9 zn}b?Pvz5Q%ZXZ0$4fs8}*%y{QMN2TX-27QM40@xy(f7f8g_QflH3c&{$MC)Va?3EP z_gCoFBZmA&-$4c>oD1{=@3OHq&NAgKr=PITmq7nG?dA^mj$5m;Q^^~-1)8B4af_#5 zWEP+-LR!#OP4;nJ-HJQjlx2&d?O?u@OJcbK-&ILEgDZl?`25%1KmVLRi0aO z*jIE-Mc`5z4$CZ&|F6FP@sAcVGWPl?sgYm%G*$v^sd+ zgAzwD&V@}WLwQc4Uf{axri?P8gQxjlfuC@(+Js3>il8rc^>WEIkcSlMb|s0S&TLv9 z5&MiNQ^5IG{KJ{KkL0iByA!irf6Pz%Q}0!B0hVtK2}0hh zL`v`F`c1)=yfaf#W7p;@(&~QuiIp9JCH;+1`;-fVMqiA9389KN?;(&ZpCsk6I*0b{ z`@!io)9LelN(=DmAcPAgSo^!w@>k~$^fg5OR9x8qR9t+e2Z2`yj@dgH8xW<*YyG;O zfBWu=d>X(|-Ke9}o7e$yRjv~u-O(MsH~OL-$>_DT)T!`bhvDQ=YuUP>jf(!!T#YXi z;)05F4;kt`(CVQLS=f~z;!IV*sa|d=d)}tDMu6Tpz3EC@U47Wd&xZl}^@)YE|(uw9F87HA3HTT$T#05Z%OQFBw}$)obzQwBmy!e)$#{&vvy=N+8Jx< z%m?|UZTxo|OdOPV7%*(=dd^4RBTaJ8KDCTY1=U*<{?`7Dvw@p?T1%Ic`TckIz;3e9 zCqf@hnWeL?K3Ci+zb5xvR`H;f6k_ zf|X8A?NtqFfjd(VI(Zn-*O4{Mgfw%zKt2>?Z{7YT{;1**6H7Ji=oJ!9GU2foDU^}O zC*RhejzcNv@Md=J7eXqYw-C*^d36*lmZ~bwzC;g_BHRGI1R6Qdhn*Xq zb$}Y5{4knxxdHwRIoikLmph9_Yy1{pPgDWlmYuUnM|FLVUsOccau^#oe-`p zO8ELzb`-J-_Wi@sTFA>r0gg-Gq>nYG($HU3tPPLW{1Ha0fM_ zN(s}F^P*@LVpds`Sl}xR`j$fWuFOAS|AqM6p`(xg+3~Opo(Vf(QFM|162%uETBmX= zJ6`k_^aPqrsYD*6hlg(e+3BC@N0ftC*INK-8ZPu@ro&9OfQ;!~G;SVW?)YZsDx3Qa z>6cJf_Y5Q$A6$Jw*XO@qebehXbaSBC{J|7;HzG62y7~@eJJ+>au2rmk$Q#SqW=D1Kp)QaqR`yyZVxgAyhJnQ?~ALrx&k4@m_5G z;2jrh*Kz$LpBP z%@`!i(Vm-R4_R7@A2 zYTF<41`YDc;TyHoF1=<*Hrl94vvj`)@7-~_%j3P{_0kn`Sf4*a;%{Q|yH}V9bBQ+zfqOQJNKfs~Uln^RHo z^5#?OFK*DbEB?Xi8SDX$;l$_Ot@YRQD>bFPj%{AkX@!-?IaspB&u@c*N;%$}%?_k< z`grQuI)%$H>Ubk=NY(??TIH_(1OFp~{7mh^(oVa8Kk*x_d#QcYmsNFtJH6(yL}u@0snca~){k zRla@oKEdvJCex^fk+SXJEC8?%NAmIDu{I2Ox=;Rt}2xTNsZ&t2zl!X6AUMkU!?q*Z4P&d;X>ok+g5fa<<4q$ne&POrHC^HjxomVYbUJ)Vwl?%q$fmJw8NYMxgpUi0A&b`} zJ>`cPM^Pj)&sCgE-?UwTgO;8NP}rI9T*a_yT5H>_wnNiE&XM(Ij_6mJP0Nf?$wda! za1cFZ{8+3^kp#RV@6PQ~KGUgI#@#&J%A1QnGh+id+Yxc9@9h2#=@c2;Vufdy$4usF zzJ4%iFvt&`Somx};+8KMn%))n6@ltRkYr^zr}a~A`|afbeXg^wd|_Y-Ikoos@Ba8V zW4l!g#l;XQU0LxE8!#xm+7JEPA+NnnxO%#e0KyQxI%AFdhXAh_389y^^pJ6aL=W3$ zXtn3#JsUZ#*9eRSHjp!}WzA^F#B30ay*$Q=RUx7n%+-ZN-atXYE6*^pheiMtvwV!Q z9)M>}Lmu390^|8B4r|-ntwl%4x?!8mbs@>oNl#jwvAo$ZxZtIJ1X~hT^z7{8k!*J< zs^>)Tss7g3n2wc+ON9QlUxY(9IoN0!JqFTo{Q4)(xUE_2pkE@Ohn$)Avqo_+n`)fs z_fJW@@!XTt`-PxrEbWJmQP1uAxe`uvA5$9-Tgb6m-m#skUCjdK(80eRgzP{o1ZGBj zt}7TcvI_jjO3-8MJEGh;$@_sk+#ElhdEwZ)*JJq|=7i1|&22;uRxyuoUN`N<2~Us_ zC@??M{d`FJ)^m>cHLm^c8B}cHXyUdb!MWyFHR)Z?-%CAHQMh}yrKqvFkT>s4Fw8%(;i#1?d6 zCnTQ+)TMLx+lx`2ZKx-5^M!$Xb~cOKk6qP7I;6EiS6pes9ATq6H zmfeN9F(}7ga&!Q|EHcX&Dg`P+2K%N|Ki4DK$PvPGu_#KN-=r^IF20$*$Hzdjev4|? zW&2B_leo4iFmbu!Rk_*`M*;8obdE6f81oHxU0{oQroGw;-z~5S;kr}>Zocx5m!88r z=FOjcXOGr2!-IMSEgIOqx}I-1nx_o^4QHF^v2hI8mX4M^ z!zQc?nO(XX+2%=q!?1bcG|Oj!k?3Pdm{aPu`@_XYm4Rh&r*n>WpRLUQxdIx=`EVi> zyBpMi+=#E8eX~7EQk}#a&+}zmgZ#Y{(t$T9YbXJ6kus=YyleJZ3yUoQjfYA#&niF0 zAKiTtQl^Z3Cz}jd9i(uEEY_7D)BZyjfpQ4@h9bp=sKn;gPZ>+uv0f)S|j+ z$KsTy4ID}$Dv?NJMGN1OjdrddPguu-O!%rHD?CA)i&|)7Y{kHX(B%%E060Lm{PB8c zfii0BQ_E07FCq@BGVjCtlyzofxlqbSYa796$Y}Eivdn6=k@MRbU155PAVcgSX%s={?drqxG^=3eS!FbwKm* zlHKb-MkrL_gRTw7p8Pe$chvzjd7zKlX4nJNd$SsZ)$@pWHw}XCJ071ye#(b*rmvVS z`=T^(3c-!9M`8zjzuZ_tLt9LL^`AGlMI3jtU<@7sE2Q>7mWtm#=a9KivO9~3pGIY% z^wL;L>#UABI$SQUR4Bv3b8jYvS-5Dkiw6K?VoPt@zVpBx4c)zL$4W;z4Kwcj&I$1u z^Zr$c2$rEiHs#PFRv-Mxl-96ybmLNPSfTH%A;m@jHnY~(KP|^ z=0#7vT!Sj9tus6us&FCH)J|7%SEH}{Y;|Lt1Pxx6-0CeIzsy7W8g=OA50?kHHgA(Cq<(Aixq3~N z@IK5)!ytP^`ukEKmmIWx{}#1$Pq^t!1#{=1%Js@GOlR;0-g9pGzLcQp9AC`y#ZWX* zmS`i!7`hYbn*dDzrFh${n9oVwy^5sq?j(;>;v8ja zXy{xIq`FJO3PRrQq*2!Zcp$W#lTWATfG3Z}!M_2;h>Wy3Ggo65#T>S*fd})^^OUK8 zsFRBXd)3*>aqx0V9a1TiXZYB=0H|9B{C#ZW07dTPJ11!4?r6{MgA!o%C5Iogf&1Zv zO6jW~oR7bIj!Hn0U)k09`@`Az``uLRPK9ROFd%laAP!(h`tX`2DHW~^1ZC%4cHE%P zlV9QsD>Ilw1m?yIM))IA_Q78)llQ!d9k7!o_5xKoY2L z0?p@f?=sj(Qf)ur7=;<_pK3b+{4w5dEghQdEN6=;MhXK59l--z|DPdcU7?A@bYcdT z3grPvqwG@}+He)(vC1+z>`1BF=Ajh~QlX%Z?}rSz<5&I(s%q_u6&r7g99-yz)<4Ov z3+r9+9e!=8Bc%V~qHS-_TqkWj#l{gP*&UfFG;CVwxYW#^BvjHd`Rke5ZJ2qLC zNvB<kaB~gWi$QA$vhL=kC-S+-TMnWXVw;;a`Ll}vtijS!AI*?S;y8}(n zSHJsh)@DC8{~DMsXRTtlGepJu8p%{oUZ6^}xue(>^m52RC41n`R=bFfI;K z#>o^ya*#IZlZj6>YuMJYh@iec&(gNd@J9k4dy0E!b=ZmFF!IE(9c<}9ueWgFZq^LH z{Lq?bjv@Nc5sq5YP)^;5&a`>!nzoU1s9~`Q_4(%Un8zD9rIi8fh31@Ke378)a|5dd zM+GnQps*;XRF0{lf)hh@^(PGy>HI^aYqMClL(=!@0Ge7jyPxH%k>u`|K=+>MfN%t9 z3INkRESX*NgRuy;+V;^|^%gZR4`nNqFqlxo@dl^Lw1k(FzYCq2b>N_mG;Z!Nl+l&3 zG3iAq2HH`$nsD*i`jNl=sx$oEynDGN$k}@;n|=&W|2H~$)XkWRbH{{Acd?=;5iRAn zPDPSW-L9N}LW@lOtJAZB777cFmi|52+osHOy}G6F)q@Kok;;xdao!$ctXytm2#mw< zPA<=R&*(=JYL@u{KF`OE*kM9y;`~9|h#z-D2s_)2)wk(<8nQow8YUeT2W&vTHTIHq zo_Pm=9Zz1=+`3|kX^e+0a7o6!6_$8Q)pXT3pzRiP(s9RFodujbgNNGI8_;#}+h>Q6 zZNg-NoJE-1PnUisA@?hQ#_do24};$HS}lYAA%ut`CCcp8JB?_k45B|YpznJ-kPEfn z;lVmF2m+$GvVzAcRe05$4Tol9HHps-d?w#chaMV5#7EFC&oOc8mF$@W}wDg?LqtcJV@W8 zmCJGRpliJQW5}opTAuYaXg2+ndicSX3xso}MCdxhnY=wI(SX`56bLyKe*`3i(VNVX zWBi%v)mE+jDic{t>h4w*5m40VgyX*2EMCkfdo?6)dBFUF;27OZ(g1>w`Qbt&dpy?l zX^Sfr>0mLP=I;Ay1yJtg7_o0tl>2?@<2C6bit0VJ%rwj;+h4NuIl3Dyd5A1(K9cWN zGuPo&Iy22DfJ{zDLXFI=7@XVR zu*`#);=uj9mE;F7Hz|V~75eZHnkinL< zPMOwZ%B}O~yB{2exz*sY7MUFgY2xdxlb~ zGih`VdwZn|dv?B6t)wxTD(0Kh8b(VVSjh1ur%o~{+-=cc-?L?&T=GjKXQ{? zX9#&l2ccG?$p6>o;pF0d#nD3W=T+Ie&itsv4cqG(6ZFc*s9~;Brn@9zxgWh@VzZ6R zdtUUR4h>6|kiF&o>eRCnN{VgSNC<%el3*&d~WnjVPAE9(+sP}r9 zafA?*e+XL!%{jWgWy{p>!9j;|kO^Z%guC79h(3vxgkhH}#K7-%W)>i$-1u>6+}WAS z?Ro-3$?_mG8_TmQ5oy7|PZ{T1a2LtdCIhiDK!_OV$74AiSwm&J?(PkB;ZyW3D}C?$ z#}C7V*g8tDLdG#mx*BLujZIpR_gXSC1utIbv2Iod9Z~kYVjV$~&$?JN6ngCN83x^( zJX(DdJP92Qh&YvlXaIVk2#tzH>7}${42kIB`yW=_l!qw`R(0bu6d zu{wA2X_f1FCJw?4#*AufG&rc?Kb7FsOhMr3{)VRBhSpHA=c>Q^?|8=L}xbbJ`Q0@D@v4c^~h9Yue4tR z9RLq%<^y3ftH#mj_}^9+Ao<7rYcg8HGSstshe5~ofYq{C>tkd3vl;0vwh zwTJ8kGsTE^p;Q;NX(ofPb1w6F4AaXUHc#xP9GY-phrO~M)KKZ?-h>h=l;sKL zxIIQhg4nQ<*cqFD3ki5vg8lB(`5urUD^7P#Y~Ml{?5eF0foJ0KVYj>jX{>Mpw>jy+ zCSEb+b&Hg?Tvj_K%|~9aKj`Q?ZEf-U)TgJ$_YOA|uCuytI0^x&7S-_^nwUOOx*6uk zdOq~(V!9L&*g`aI8xHnjmKrxdDMI@-GL?(ti7VX?KbObA0Om=jSMN=5^zhq)mq0FMc~Knx08s)|$ogL9$&uk%C;)1-c@cP2G zAODIZn5m&Z2_O30d?GKOD-ydB@_V{)Ldj=xKa6lw?!_09;)BxRY@kQk-pbF|sD*p3 z`E|M(L_-!)iX%?cWx`{p=;gldX>r2#UUE*$IQn+h;&sdYqvH#Frlu{<2075&2}WL# zu&-IJWH@FX4QY*s&WB-`zm^jMPdIHlr8T$H@U@vjeOl1Hyv*uan-CgKkr#nw6d>fU zpWpsA&7caqsrDG~ef0PGAwI8k{?9J**xue3)CK_LM3g3A-HR4~7rk90Gq2=5@28|A zBle%F;jjNn1naMD%h44pn_L|q6;d@E%RrLWGx$FmHZ+_H{r_m#aMzz%m!+P)Y^jRd zDGX}-sJg=0fBVgrM;Y`_0KgzCj5hFf2pGQm;0x3L#_2V~SZ&z2ij1cVEF`uK&IPA1 z#Cv~(0^B?4qRRHo??<~hUv@Q*2;WC5z?_wV&Fq6=I-4T@)~3tl@w&=Ob>1LGmX^r~ z$iCcUgj{(~Sb!2PjN%JhTbb@$OvzyI374PZ5F*f%)u5aCrrKlwep&&VoG}cuz9p{? zIx&&#u~OjzrkgwUq&-LHGlv#HL6SU)gewPV3mk*vIT@e|u^oxb1l!m!7SuDtZJTJC zFY3iRDsHFqSxc5~H{q{|`PebVL~!b$8KRL=(gwIjR63fp zY9FZ4J#PSIKy%O^zk@)g!`m?3k9BjXxiG#Ri~>2j+8np2%4x;kKjO6el4%MBn?r9U z4u%705-G1kh}y?8xzH?=MkkFOTZ3IOKDD<7v88X15^P~qU7+U3$lpyS;FUODwGNnd z4cYLsx;uMd!yc4qaujt?D|!V-t|xn?I_c?xS7wM_3ApqM9W+zcb3aB2&zNi>K8fGu zejBVNPExc1gHS}ottvS7Q{rqSobbTl6EmC&N}fYhhDvMz!9>tZljNlJY5ypq7j z8JVut;0zNX1z2wCBgr}GvRq~aJn1P?==0YrVK&>`wMM<%RMF*J^4hpFthSGmbecp~ zwmIj^X9ZXnE48ZWU^ktFi1i{9=0&Ozylp<<`=mG2gSTKw*!(cWRP+)_OR=}phuqB9 z7Zj@5AP1LkhQ4+AZY_VpsSTBFgf!~#+WI2|kc`o$>zzQf2$fY01z2;Mn4Tlet|1L} ztSYX{Vw|{DWjYOFUo-Bu*Aw*V4JYug$OLUrzt}VD6DIKJ7;8Id-!a%OjL zO07w#Qj?JPU7*bOJR<-p($($~wiCn;9 zzW())x5JBbd%W?YcR9?sS8(qjMzQ@9Epg}5Dmu>hv1zvVDh7}DQrifjC(oM@h!s{p=8pX;`D)~K#U79=`dgIaY{3(tyQIP8qR2o z4K7{sJb!iCeX#Y0tUPPmIcm8pc%tvEjFUxEb>ZIG*$clTpYpp|=~(XMDy}^2qKAet z73dADIItiifNMrMP&%4kpMogonmFxf8W}Sjy<{#rAjs{+q5iFoh@<&`whCm*2h|*M zwLRUdE&=*yq75i?Y?z-?05g1yPMJ}AJ}Lj|WXoJus%xp`nj*sru@G}jNMC_PnKHG> zgs=E(BcR@5Zy6z_NO4(2PPo38e-qe?hrpQl{>hOAXdxAuj+FKER_=#MG}Kc-MZX-h zkxS-jC0A|5@HrJ7*_!bne|_kWZc0MA7Cp~B7ac>tF%qKD?^PQ03iX`%VIoGw2O8{_ z?(~Q16s{Wh5Ti}mNg0v_+1j=VMsl;+9!giD-@BEWcVv{KBoM}%D5k7}|; zC2%fLAM}k;!8pUB`Hssb)pReF^`$>&<}1uuA5bmWR^BHK)E2WT_QF$Q6N~OzvH$LV zN&>PknLos|1ONN-X%{ePF_S5?3egT5KVq_+Io}@jC`7E;N zV}fJ$!ru9xw!<;(q3uxR8;8e{vtR$q8p-4!oW(Fbs$jjP=~VGLS6-mI{@zGTNsptLn(*fd^fld41RoNfJId-$!bBk(&ApTef1a8Gk^lHz zlzJ4EWFclj@cYk^kH6M{u=TOWIc9r9Ga|>wY6N^+67%Eq#RN@gSMy3YT1?Vt zn|!A^knXt`gGnrn%6K}aj5#|NR2XC{t`$5?k*<>;8tJ`HGQC^D4I))o!z{(`YzXM? zV&d!3E2Cd%*YKWVP9ku7H@j2Fhj;SY_dH!fV)2WEien1q@_qIYqgA|PO`!;6 zaNt)UC;Jj_5t+0MJW2V$-8d4c9IC!kBeMkHggE#bI#uSQnQY`{pqa}X7QE3QfCj~U zc~Cla1N35|MmHtf|43g6hi6~jdM+)%T$VVSLC$wIesXUI=uXj51244 zE*%2TJb_0o7oK4E-}f7-=UD&$yt)5)tsgzK2_8jPK35BJjnArj#+ukAV0_U{pdYmr z8t#$ZXwcbc#W)g8agUZ;>vapd+ciXLQ9LMu-AI?#K>J!#udP7JTcJ2rsIov?Kxbi) zGnRt#>pwh&aDeFKuXR{QZ5y`_mJnWa-4L8=a@MDN2bdkPa((k02`gl0URbaS%?GMB zowC>I6agbJ7MRbP;bF01uA}CnXEK`(IYf#uYA}oEM&j-(MHPnaTJb%>%v?6Obqj>X zxFK@X0^LE+tfrL9B*!+%ury?XK6rTvX!~cIe|>u|J3J?rS!XIekHLnyTapl6#m#ggmdi@>luw`H9Xnf2l2#%2Yfz{b!Dq@>p#K|GZeN$dUQ z*?vR~$o2x*%%Q|>5bmE?=TUFSC@0hu|8U1YZ#|x(!2wRd(s#(N!$}-d`JV~C`oWN@ z0kkGct2)ferugIF4>>Vd&GJTZGX}#xwxYmPAVz+9!Rild{%R>Y=NP@@l69Fpk)4NQ zp8X1S)z*{J`emZYNVEcMZt=^jKIz*wI#?%~f%`t?h9rIBW*)-R-U26${2Gyl=yICH7*GsI5`LyXi zByVaqmdLXR=VGmD^<1BG)bn!Q*gr#0q7%v+PfpSf=^_^R#I&_BY>6Xk`rkOi$x_%0 z#)(^g0Kt*SF*QRS?T%D(FFHbBZR^E|Lpt1!CqV1+7#5p{1rwme6}nUJ@i6L<%RF1B zxYdr+!G$@1US)3ZtLLRc*lVFk{*Kz9zn6$Cnv-;>R%7-!Bark%JdiA`KL=#J=xs>S z1Rui&9vukUEf#@V^%n3fy7=FUQ=;LVaRs7*W9HH|Wp%lstxPiHvoRMH?$nq~tw~sC zl@Hr1vq6jBRq*Z&5e$y?bn@D4{evpRe5tL7FNga2UW_wp`f)jnbL-PNI^ES9rXg#; zvB&yZ5LaV1W+)+1Lbfxi2!mQburdm*!$6u;zE(9ir@}xs2HCtK?C^V!m9LxadP{9| zme;x6XvN#Os_nJ@nB1+=hckP`X2Gohm`P%ntV=3&sX5wLZr=QS+k&L*EgtA4{Gil4eZr-(~fiC~vo+1f8N$}(LhvG6XguQE7 z-0}Zu+e-BI*lQPOdX593MiwIe9E)HO%g26mwB4dFvi;ETe7}jjK(8g8D#mn92G`JY z@0Et=7wn3v&4@4BA<=j-{>ddDq?pCD(~${&F#+-d+Id*|N<4##kHT)k`W<=hiyVAe z{mU!xlkedhGPSx$wBSB+@OiU>#h&Yju407@mjG^fFLDqu1Bm%s z9{&mE)-RGF?cuG*tikY%+K&t@XT6&y82{mm*NUdCg2u@$_9=(w;kCBBcZ2^`f)01C zURp7h>LZyXO)@p}lq7p`V0}v~hVGTtrxnn@lIc9~NTBNAG@1LM8zhpTNBXhy9JjJL6K)4->*@Lp77?lfo8p&`R_SAc2el>a zK22p~!jW-Sb{N0Hm?k2{AYUI!iICg%Diyk1gI}et;#K#R-^m^UgdOz$ALEzv`dX(aj_Ve;gBIr#>V~Ue5!UU6_qV49!e5x8nma73xcYW0ej7aXtoVGsuj}}tFua;( z@ygQsN_Usk<^2c?nBJfJxzOvoeitScN&XYtJr}lLzOX)HA4-f=iUq@B(Y&p+Z<7de zZIoL3%M1E1>doKA;2$1PtmiES{|ef)raE|)B$re*26g`I!~kW%^IQ(C%k&Z+{8t-c zzRf2YG6T$M`Bv+0jM+9salG;lZ7XwO&#Vl|j%V`B>@XdA6@_o5XDRSuJ;XP5#6%%) zz+IbaLN_P)r!(QAf@x^PQ2lae8|C;sW5+2RHk~pTTijxUpV@r6A&SU}7iPT#TJ3?;ez!1acPJ4gJ>%EwZQ#dV)0L2V2 z!l@Y>zuSO`1iMQ2X$F3t6Pd^xBIGbbZZ|yW3SN~7fe6bT`*8PwDsm0owYr;6UP1f< z^e(~`5JybqsgpQ>P=<;V&H|Lh?IUK;&rpbYN!UJ+uat~2YLunc1gnPa6oTjrK9tsN z|DRxDTfqyUSj5^FdQhvFUq25KOY5JkT^$*rVQ<6)n-cpGJUM>a?(kF?05M~#}V=JWA^jUnL18{hTNKDQ0&B|FR z;Z^E>-L#t!b5Dg1YqSERbL5-5n{$X%N9@7bUP{tW(7!OG!!a3*m&XI%1cf6-=WlD6 zCi@b+rFdwwA|{Z;O+%?c4cA(V^bVi-$JVBR6a1(3*)nJwh?z8@Rny^Ec|kIS2mK{5 zIev^L2Zo(Be9C`ai`yz#K*t2amaq=_y3$+6!gG;rVHb}@WN2Sh2gEHO1=;q`TA_z`+Up9e@F z9+}2+3V=&I=PnJdsU*^`Hqo^>H+L=hQ^q!8;d*QCect4?JIP;x3N9@)nkU|)UGQRb zi%qc%@0Qe5TRomcKH;Zk!z8==aX(v25(&A~lBMG00K^h`1@g*b<1v_5KOxLPCQ>V)+!=SslulsQRRsdLtfM$TvshYL^(!*2Kh6-job)#Pmbk|> zgptf+`RTxvQzKk%AN|DsCxVHMY;@8m-s%GiVY};%utWmLO{fIvIrkP_^i8uO^4~3Q zgk%gJs3Kn~(#*V`4*&_GS%?@OAsbA*j-0?}=2&0GKhn-h|GRY|>lSZEYOd~J9UkkU z1J;p2rb01<|4MtTz2v}kk=+an`epUvkdQN_vzZb@$$GIl7;07aiOt(s)N3)d?vixn zQWgR;4d0Mg&>sO|NwPa+cm)}425YSNMytaU68If@loCf%gKVyQ{eL_BE}rH zGubHhhVjUot~kcHFRsoXC?>HFf7G#hYWf*7td-Xco+w`~=}&eTKI|#h3fp2j z-zmWzbn7_9978H)Z?=}e)UOkbw+oBWbfd*zr+`|pxA$?kR9JG+jJ|iuOrsquK0k8mdL*fofR3}iiMxQ)%{_GH&hujp0!Nd z-1>Pf$Ot@^Y|T1g$-YZH8Du%vh@pGkXV?0$K`sCUeq z6m_I$%DxYn95OkUT>JNealbdquPraoN%<1{7k=@8?^&?CvLlw|3Z56#!OT}ie+od; zniIzX@4v`X|1(oPBmu*ei315MiD!oC=oL%|=ug-%e}j-v{~)CU7+nGn+e0r}dziZy z)sR*Qp9)Mb%7kuD#1n@2(h}+NwwBV%LI_xl4VSt@plAu|6TsS1#pk%GRAL3blv1Jl zo0r@^I6GOvf%JyyGD-w2=Qp+Upjj@R`P5wBPv%1;#~)u&h&Z46Hr6s^_9q(4i3Xrd z@R3dU!d}fvgk|~H)--1#w~>2Ygx9RJguEv}TIm4IAL)sBBMw{y!N?aCGGC8xPbQ?b zJsg?Jz$uw_x(6=h=M~vV8HnFm>wE(>FJ{f4WiB=fN!G8Q z0V!Vh zUZ3x?)N?gHB53eEgQQ&BGHy(9X(!C)l_l#U=?4s$<>)XRl8$Kn+w>cQR(*i3j|n=o zMnz=9TpCg@<+Ho$JGeTvlnZ#9%*il=CdceoV@w16%}_49cL3}db`{D8$oXlNq_eIB zUtmD=<{XaA_M^KVX-)A#Hq0u6c5XuR!l)H_bssV-+=tIPQF5(qlnR_OFR)mDSp8n3 zG2h;}TU#$?7WAYKz2!V6=P>yi^Cfe=Qss?y_(D@1F3NX`b03%TFPK;SyrD!JLn+HN zeOn$kVLQc7DMU01l{>XCFA!H+dVb0CPu@2=6`v}WYdknqpQ?KSZGkqr!O`eI;z&X! zHufSHl+r7Kd7&?N-E^4S<8)gs{W^zqb;0d?fv0eZcngBM7GgcOPx|8pz-Mfj4?l9` zFdKa{+(3La{(v9gK`{VHAfjKVO_-5T9CeziHeJ%o7NO2Tt1N6I8L!jniApa>;CkxJl)=5GwA?X$BSPPI|uHklJYnSg#8szD;@Oj_81 zN1{kSpa^-iuMmUBq91NxT>>m?*{vFdK-j~GB^9QS@+>eTDe1k@FOdU4GXec3N>{qE z_M}e&OAll%g%xZ$h4Zb634HV6f$Q*NqT1Zk{RP2MDp?P)p9(GRa1f1K0PWNQbeazv z^DJQGc0O0Jn?*X^5BBGnYnuzLt@6FIrt5R^96x#nr+Cz*r=gNzUWo~3yT9d01>J00 zw?h9cOP$YLu}$v+Fj7}wLF8SgsO<#*(bJSQFs1y*V}Z=3I1~9Fpy2;Dl-)eo$xisO zNwC4K4W0>2R9q4wKCKDank+0X(y9*s78LZyADijKFbW}`(}3^uS|9fI@y9?Yud{vF z5p1nrysN~qnZjdxl7Wiib~t}?p>~gG0*q2}$hnsd4GkxOxSReJ4@poY_fU?j7U>8R z=`F;?iN1#@L-bm5?g#1SnyDZ=9+3>RAuFMd_&+QF_Himzszn3=@4M!XLM5i>IXYlS z{+T(565Sg%a|A{Xk-hmDUhPi)4>q*O_)3huA3osr5M@5ynyXa_S4UbyVkSc)toZ?qfiu z7Ck;?e*4A+aSW=By6DF?MKv5%_9b`))86V_%Wf!jNDbIW{qnvt*i}qrF1lXd5&;D%$%ntshA^@G4qIlMl3A z2YiWR%V1gdz!r48DJXc6>@3&|qkI0f(dCG0q|!cUVE1rALm%B}NDpYo>Gt$kc8qn( z!bo`DDGD6XV0UNNANXBPN`~N9;Co`d6?olL&JCFkRsaahab@*^O<0*HB(7`V@Dw~mP|b4O5U2X&y-;$H-8oi-0C}zRBD~2%RVp8 z8Fqh*Sr2Atmhy$?85{FH^6(x`B~l1^{P}CmaccsT*0-J~A-o_F=Y3?JMqrN_|E2V= zU!Xub|5ZsUq`qBlw@g*^CPI(Xw6KJg+j}#MrUaYR=+=JKD^L89r6H~D_qHDs2iXf9 za8f`|bSc>DQzH?K6J^qimMdyYP`jYNC75EPrIL=Xc|HRo4_8k46W_gALW(BGZ~)Ep z7)}<&EO8z#T~27X*n?KSf`4@dN$W;BHYHgZq4cPc7V5(*p&;bZxeBMFG? zPsxPrg*OP9w*^4@Ew?t;;*MnPYh!9nt4(8N=&Pv$%oJ^Ly^F=P9q5VI| z2ym(X>G$ogdyJ3<{N_#FU1ajM5bC-#r?ug-r-I{m40$`!Q{PGjPp(IKGsm8Sdq zz(9jcTm=7C9I=#!3Hg_lNRZGE${1u(kwSERmb_Q;0l^ zex2^y{nGn(_)=?rv$N~IGR*f z?~yq+06skIqk`bC?znUAUqL+)C%KF8<@EB9>*K+ZDR8&NckvaKYk!ylHdDa8l=;6T zEAVL#GpRnLn)N(EL`h53sJGLK%h5E5YgPUFHkL_7n)3DlO6p`k$L8cfAXjOPVXhbL zawGmIzpFPa#aQMLOm2#RjJQ`>6NLsmJNt+)Ojo^v(C8eBKPZnAwfruBIqj%N|Ay+O z`v$jBV5uSu#1>|PN>SG2eMF2_pDWqK^laRR?W^;p1h=OI8Dt1zWoR@u9v#xg^1Khf z{PN_-inq!Q4o=)EAl_4p%XIkU18vHO37^Xg&{mIXfW_v%{`{mgZV$y(YrO^ib(cYs z&!qdGsqA1^eQ!Ytbg~K=5pUalq%!#J~Vc_4lL1^9g3LI&$>{rMhYn?>6LulWK{tWsFrb}J3 z>JP*Fq2LvwnkdniNxY&I%vgOf^ch7e`IL2?AY8sUhF*70Z#-8fxE}laKzwOeavXFd z<2w-Z;t^!~3q*+YIr(&EBA`NaA%c>AP zZ(cRGII54t3TGKRY}*;d36sE(vvrURwDwCMjOoq0I1q9@yQ)!TKdijHmi>Uo*d6Tu zrRGR8GU$zfxX7%X9U}yEr4_y{s>lG?b*x?ttV$AJwXjN{C^}EEP?hSSUeoI;L%EJw zPh)2Iwp8IjkRn%L@TZK(U*hcw%L9_!dq;srkq}t~_V#!U6Q&RH?TB@pvtz+v4Vb0OD7(KIzO#^Ihl;-j2I0{Xcr1F!DBEB% zPSLy)ph0n99r=B$%@AG zhowd z;@N5fP;Kur9hLjr^NdD^^=z|&YP8I+1b^AeWK@94c2qGgv4-F;;)h5fu})$A(ODYp zw+y$DJGjCls7W!%U4Y0W-F9Eehi1w%X=2uy{7LU^=uiqH9WK z^aOnMiQIt;wiK5@VMIni2aE3s`db-ktRN1AwU#iBf=)*2hb>h+$Q7t^n(<^%^{~Ip zE--1q3QveW6?e%*7tc8#6nNV%I@~ODoYM6wrZXkIRbc!TL;vYUFqSvC(S1YTiS4SM z&l$>UKKkh^EICOa)EQa&I>=cqJ`*ASEFrT<1=Yy+ON~9QQrAnphsVV^MGGERJt!CR zRpjFs!`>zxW8ZSTnAw>rFS4A$nGSfb^d(B-ZwW4)iQJd%4Ognk5dYsUGDNT%OdpBc zJn?by1*qwx+-zT_yh-=S$LQ5-q1UvA1KbeCH(iwhk7`nkhlmQJ1yl7FE)U8Y?HHtV zTR$%-uW=SC7d_n?FQN&?DNf;GF5;j+`*tQE+t-~TGA^{Ds7os$^Zk~#OFNF82s@J7 z1_%*Xso};0#5F8|nqt$TA)%_t;>=yKdFkUoto%nvqOR_@OIb;M-c`Yh3`KEK2u$T! zFx`JnBVNGE_NIU4@ZW555noAg{-OM^`2GQ$tFE@VcI$2o$&*b4b~rz&{~l;KBK|^V zl6_IdK`OF~Q#}y`XRwe+_)l4SWe}$+tGzi9H!jw~av6BBi ze*X0Ee8WR7P~nzC!Sh|#;m?YZ-xhTI6(BzFSM};I9`;o!!0t!ku3}}@GL7kC)mvJH zqHJrkepoC)&BjNXJ=g91S57N^P~NH3gwgFD+#acW0Jko*U!|+o08rw%wv@si&am_N zStRqfaz|!laqQxH;cmAb*ry{)Z{nziLiYE)eqcIltvO#%e<-~D{UV@7nB;@ZD6go< z?1f-ri)UwxU{iuvN8`jtGCs!&Vs>M@U-$~McALdLU@!Bu891oSH3N>z*(ia3FLNE( zU}MMZ)y7Wx@7oj_c0el5za3xmrzNvhWU5gE+@jpuryZR4})^_l5dkmd? z-Z4R0AJlS0zuD}!V`;gV-%a#uPd9E$ce5oo;A526e|K>?Sy){_s{Z4dIuhtN6#}Rp z{JosCQ_kSV4)I$c+&w#zj+c(hvC=@IWO+xW@$UXw(I?Z{0mkceItdiMh}t?zJI zxFApdeF5mJXMSazC{_)4DTri^9Z4=F+fx23fYsosRYK7F+&>SY|a!e(a z;_7x>O$a%{VKTDWw)-vtG;2s`fj#Oe*W|H_rMFM~(9)S{U}BwdKh;#V>5PbE+yx4u zkDb-3RDDi%_9VFWXn?33Znxe>Onj87xr>3hYE8H&o7mf}Lq#r)kYzat+6=c94HS@2 z0x6ODCpYi%>}|{S7B=mArE;7#7fBxcvYA-06*!Qv^>1lTNeNC0ltS+BO#hawfB#6N zrZ5uvq|>E=g=`9Tc=U}~lr$^lgRD;O=gI&i(S>HHcXx9+1(uo`2ppR9&i!ox5Th1n zyIK|xH*0{QW^~$q0}hr;8Fwu_wK^M502+M*7|UCBKd)K`GkM;w^0O(XkX=W}{kn5K zYaS;_Xon*t$!s7SEW!EBYJPp#iTUz8Y~o?g4)g$lv+ExL#W`4Wy6Q>A{;CVD4uXB6Sm->LpWu$=qh2Nn%a zlK*U`cAX70+6L?*(l9+tAT`m7W7g$!+8U>QfawF?H*$nY?&tma{PBEm)t2M8-dy&E zyYkh*RbQa*t=n-K%c4InS)37HZnHql$amEYBzKR@?~C)Rj9uk)>#<4!QmCl)<{0gX zHSy&#~P$u;h-(R;qF8aLJ$OwoAu@SC~z~-WMy2G<3P}yz`&Ga|#4fjMPP6D0^ zS}ubQUW~K7x}{=7bmBsgrG$;ak}O%fmjDvM)Ls-dUa{CaIPIQj%^mGY1ofQ}nh#xT z>@Bq@1B`ijTEX*aa>K<0eJ_Q*?KefR1l7)f2$eKb1Nlf0rK`T=bsaZbMa$hTGE+Qt z_EbK4p-H$9pzsTobEOL(ddRt{mljeMn~^f1rU72h?Up`rfxsT4c8hNfk^>jyFaMZx-aZ)I;p|R?@5ss0XEP$qyadnkZ(Ro-;5#wUikVA( zCE&BW>e1yy-yW$`s>=dG*H5~?w&rTE)aU#$4)%zH1n$oKdj#(9_>+So%Iz|efwQ2; zqx;)O)TMRU#>mT)B+qN1V(@!pIcyAzd+xQJcrKI$pkEIZ<9B{A1ioH7+tu5vmIB15 zc4oXF!kff9r#Y_oRx8uVN~&z;)br*vfffPfk zDmvhwT3&>Z8XRE;I4|kF zLv(Z_e>0_k^3s;~1X1=rapzWS5Ch?E`_1P;t+)rBH zOfrTX{wRNrnPAA!;%k-@%LuO(nRKcmMX5p)s~C!@e2Gt1j-N>*mKERgHD zoQ%iT5+IXy^)4>|gQU|fXv5H5`OZ}M2tktB{N5%p1@nEj6Et58me+*Ed6ny(aY237 zrfbYUQN3R`ZdTf$5a<9I_V0b3HvoO+UFrRnTkUxIlKX+tR8DWqbG$twiPu=t%b>;Z zeYVc_J4loqXgh5Kf)6pktBvy(3rI{PbVC{+<^S^wVZPe5&SLS#ej}jyFKrHNyDGt> zz-cJ*tjT_rd>9jo5cu>mG$_^fs2Yq41ZpEX!i3csIG_{RmrgBYo|=2tr%t9i-m`B& zI1f;k(cvzJs4X^!AM3QZ#SL-z()gfjC%!3rm!n+bc9OrpSM!RV^}gwG5>f6VFc4Rs zF`j5W(B%+Gcn{?h?gq;|C~g5QAz%VN$!+z%+8OVUI-aikqpf{r(MDN@EsQNh?Dp`b zo?%7ocU|gAN{uA3Hc?XFbDQ`j2E1E#cmshZv}L3r)ji2I-F+lHnCqY`-Nm$2?WLdn zlBcs8)vo-yyUeitOGQ?80s@oM5y@xaEdy6|*X41O;S1-A>{(*|sKFqGjeo2czTL#Q0f?ZjV=K#ktiytPi%saCw>&qdxj`(E%m75aL36rW5zo#b3%r@*3Sy5 zZ4NrS!e{hhmx?bO(uJkf%>2KIi99617D(PFvQW`xg1=>ADIvc=cm_f#myiqCGHj96 z5Kw)1--y)C@dgUe;i#^Y;dWzjQPy13vdKe0<~n9lscx^x2S4wNLvlesDxOSGA<$%+ zBg}5l$rCGTw=sXCk-@1(B#{V5m$s7d4L#{0DX$1_8%$uCFmZcJ#p?8ZsN1>U8)w$c zTS6|0ok1d98VgpY-^L6cyTq(h`ZI7`EnmD;Es#k5)cVn(GXQ-jsB4OsMz@$G$u_bD z>i{^sDSud@m^}inSA?#qVsFz|=D_<-wJgNM6zl^pOdtK>4c*g|Z9EFQEW=X$w&0rA zxkduuiqicxtKug!IY>i&v&N|OE|t$Y{Vv95Wp?hn8eVq;Y8k}$M^`cSjzyqO<+oG2%aqrJ5H@$ilDkPiyF^&Br)#*q?K+uLndgLQ!o2NS&A%`~2ZiU`g z7XW$UQ~5T|ooTQa9E{VHES@PdZdoJwXNwU6de>ss$rn(uPU>x@Z3__LBYA%ZuO$(w z0h)iJvuGLk;H^SE8yVF>tK#kui7A_wgr~N?x!TbzTiv{lXTdaE_P9OO=$qfbYPtAv z?lt%;$b)`xX)`TJMDNO#9{fi)r@O!3H(&2xGcjX0KbpI}2CtxyUx88<`_y`u1Yrgl zBpgy5pQr&6qX1W#x9t4NzB=Is5%-fT_Mj7?RwXr*Rv|U9#>~c`v=iw11*kY5x%jYPZkx;afuq`g32iCwLiy`jL&ucCUK95@lBF=+#6RHm%eNtrzxUDZLL|_ zuXsh}?`y&q@J0J1q7v@i%0Dm7-#01~D+mtG0z&XaNi7!D=LL1isWfP{m_nRpL>khAN5c5Iigga+ z6D#}20Prn{n5*cJuJHs5#gL3MnQ{^Q(}39}0nN8y-25Y_6z*5QIL(5PV!)fx^}ZUq zAPO@WZ_;XJee*kiggO;te1h-KS9CG6;|a_6aLdJRr|#VV>Dhg@=WXui+B&RJ5!)x7 zJc5xSK+8;=xP9`xQuODf#7{`l>lj{7!-QXT*O>6+(-F;BuuQx{y_V!$87p_`H3}i$ zM<^Pt9>El@&(5oK?5))^M03F?uC}BQB^N94ev%K11BltWBltdB0sFsZnWwdzpkbHh z_^^6la49&-Tt@XlR8ar!eg<5d)E*M)2!KXtRb#_ReoHh}6;?K3ruoKmPw^rpBjgo_ z9=>J>B1gKd0YsspEcwRvpW#Sm?Aj=bY_z!r`S_rei;y7?NUUkSqV* zn{?3WIn)D?-rA`;CAPudcDi!i=&al$wZ^Y?Cv^ZpaF`pRCTsId(Yni=I^r3ZNCY}K zp;RAn;?D}uOORZ#*2zM#1|m!%xS-qp#zBL!UVFx@?&M&(v(I6}K4g>T%`VSF9zzBQ zj{1%}oN{}kW`pTgB7T6p%VxuF#^9UR1K*zahr9`kr5{9IYo@-p!gi0a%Jma{JYMaQ z?}Z1>0kEVgmMJpvZ*dj0icKc@=L5cnc7*EAhPec{Zt>lv1g-6FW9n zlI=ljQ!}N(W9WE=777wf@uf!DAIqB%1U6exU0Z2$5*9+i#FmT7cG06_NU9vdv-Y)F zYWT%swq!_%D-54Q;isPLvl5m3vWu;JHo4tL22Y`HbGE+4moO{m4p5yv7osYQI;%Bn zm;N`h1S!tI)AD3)?5?)6y3n z*7S;+j|ebBw=+Wils#K5c;-24GmkmISbCPzCi_FG1_{%wR<=XN(ct z7&(^Jtyo?kliP03UYaMgurSqIO}M%_ZW6=;+Xzp9ye3EuO+w&*REZwb+#OXTZp37w7<3q^W2QYjdKUDKE#q3Jnl#HgBi}1R{7h6rhftrr z5P_DxAFJPb7sYAL&Np8n?YKAF&R9%&0M>N6v8$xf^TIT<0OV)iQyvod^qQ+)B-N!$ zQ#(kYw2zvx1W_AK++7+jwOVt|gp}nSkH$`={afX9;lWvq3718xZ~d<|UGD_yo~6=G zz3(Mhhu^@W){6>RT2^i7Jvgq8(QDGj0fvJnbPblSW}s+{3Ld)W=-ONkB&S~)O=%Vl z4|CwCGw8&BTq=Kmy)JAVPvfwaPpf_CfJ9g?TPZOX_*In$&zcWAlV_k;B*Xt)~d*j$T!jWH)aEp@RCv7TV1Gv`Hi#Je{)4^_0F1Uc2uv9Hlmf}bDjKrewSAE(xGh7!)nlCPpl zw8nZ)8AyU&cd^(sukkQ}JS)Qn3~IFKMAJCZ#8mJo0{gl)lUQwS1_`kHj=%NrD)Wy$ z#pOoDd|f;ohHjbVcaqV7=7Gt*xn*igb%9XP-| zTl`mwa`E8ULPR+6Z$S!|;tt+#X}?qLN|T|1#36SkVATaBF4xelP1(7mSWhz7P7wb= zb4ZC96Q1{NEiKMFDX#FY;FwW9BguCaf6|Qosb7@}q5@#0>`rDI;tWUHMQ~B%yKe&8 zA8B_I-lc@9C%^NRmfV5!sb`uKa6KL;Hj<6E*&MZXxo`I`sH@c|cBryws^cPBSL2i)H%ZFM39VWWF@9z(4r` z5lEI9T5mtUSV}R6fL6TdN+;$$!PA@;m&^4Tnd07o)RG6?e7|Kl5|t%h7OaL?#_p)>ckIxl9KVjz(C6w3-2a zQ)I0Zvp;qWe=Jk<6$Tm(0VK|s^Yi_!ZeQ-2KqoKtCF`_&q&aLGX!g{(v$~p$C$oV%gk+sZ0Nq z8#u`f?;ojWiipw~%zskMv~6_U!ufw({e@c;-226iDj^^p(%ncmBHc&{C=E)3bO;PF zbc2*gm$Y<)bax{$beDj1!@GIDzxSN$oPWUUa+uls-s@iLvn<0%lr~n_JE}cym^>K1 z4wl>VVjlESLpS33eWof*`t7U*uK^ju`qyn&DDMCF%TD-5G(lD10y_EqK$)xNK7ZZ* z-z=z3JYIB_?7zGDs3TCsUTeesDK)_clHY5WRV-O3ylxEeg8LaVc)Ow+Tnw4wQ+)@}fHBH^{vzk+=- zkDeZqr8uzBKBG)1)jnGo-SaFAvL9PWxg6AUNXeGOMKq&D0=GPYE2JbYOB+ zM$&_#P4=+!h9c429Ki)=S3B8dU!5D;V>T>#KhoTcXyzYHQt;pxnQug9m@<^~v_AX} zsxfXk9rxhEnD64O#Kh_+BZuALZ3W+JXdC)8eQ~r6qD2xlX-%QS5r5(mG8RbT+q5X zijc-%GZ*m@mGIOM@eh_wtFY+z@^?J`f_NzLK5!lztzAY?l5q56foI!J4(d@ez8iU)mT69XbMVx!1%yg}>xE*o9# zTI=|}j8-309aw_{ ztq`#RBqc2%?AUTBA9VLpVB(MoI@231*;T(8leir?gQ+y_PMTA4wPnD?O#%};D0e)wL^Taw#8 zNHkqeSM!@y7ysAO)S1a^myehvYjrri-=@c2_J2x)@;#!`GtW0L(og(NRalQpD;7;t zQGg7iQ~l-75BXxsDxC(`rE4*ve{KHOwS*=npeHwQ%nX5rv)%X23;-8rx6pld!7^<6 zA>0A`qi|K>^xp{EUv2s+z$2Pa(Ak2_NJsCc6ATDCt+r+8`x*0cq&ErYM`P(VJkbtI zqP}e{7UM4`-pA{EUd%BB$chf)fr;T@FIsyUKh)juJ7?L6+W{(-zkaeoC}g~&51Id( z<;T9jfW_fLH_L0Dnn(aw4?Ec5*Ds^^X7a3f09S!NJ0{ey2 zE(}yc4r5fm^mwP_PelU7OJKYRT8Q0n0!7rS4UXDH#Q{yHIxQhmKQwuIz#Gaeq)qj= zYFASq90`-rlDdj8je*;$sjok6VgiZf$oJSMk&@F;A~VBcx#gq15W{@XnfZm4^iZR+ ziKc)xmc=6I{p)fX748l{J%J4VC`pu>`Sp<`21Fl4uHYhvzzP8yNMvzKel~npZ5wco zz?>khm?{wI<-9wQ-e>P?_d}%5s;pTW=1gHcp?AI)uT4|E6gBA){ZHiKN?ov8ZJw|1 zav2lfuKD~|NvqcJO3qQ(EA4pFm%dsuZNooXMJPZTIp;hVRy=IIP>2lB)k6(HdELY@BdCVS+ymLT7t&<^`F+J4btI> z<_WS%P?ks7X8W(yVwf$?zIG zvD)I99}$+<0pAISXQmMMMA3Wwdm@qqc?e`;v9zlR3@G8?JDJf2Ko>R z1B%TSc!cKG-RO(1(Cm)s+*o?0cOGGjZmFRD^=?-m*ttTH7rytu1LTc0m#j}8j=^|i$|7H;+^9>pOtZv3faJ?5n}C++ z9;_z&$Ptw0O#?SX%cj8bC==(XRsClPPS31G_E1YBazVAk$Zq(ulJ^&W5bP#Fkr=+{qj$$^3-C+$o823ft|j{2k` z*aA#Mh4tsh8$+?$Iq!Z*{he!x#V1zT{WQNcD2RvJC1e?fL1GBZ;2gZVg`?jWc90zK zk{!=TEXRH#+l09rr7sA*g`ChPWipmV{WtF``vB<{L9d+}e*Wk2SGHCSwJ#!ZyUrkSv$sg7a;YAiS;7DBnT7UNC~8wM9q8m0mXjEVxMsu2;yFPE z8rmox1SMRx%#R%6isV}7r^wWs{QFj$9(7-5LoMB#`?y90q;JP0F+&(K?nG}qf*2gZ zJ)VOYV|+ttc&RIf7-(XFKC>Hp(+6oo(Yq8N6mnVwG}>utHRj>p`kyd%xutRuweKYX!~b z{pO=#cNID+1?NRh1L|G0c|ssDLy~}uD(|mMaAR;73h)ZFK7iF**{7Zt0nADPR_hLH z46%@JGJ0m`l*E=)Bf5+9>w>LNhluY@_V;TU4Wti--xlvFY0s%)@E<_$Zz0OAt^kZifQ?YB73h_J^^-8q@lk9|eVTNQE zh$`b)ZZ_(Yf=CKp!4L|B!A^{?EhE130j}E6*DYPdONJg{37lqI!@42j{w?_b#VJli z#|iC(*Arr^XZS3}LP~yR*&Z!eT3cdFlZth;3E>fM1GTJU%wz8H^tHlp^5x0W?Hjef zel?|FYdMj)hA>)n)Y?jL(9k+yYMo2i`>TcayQ>Y%nc7y;j9RNSzfuP~mbJ`=ixy`w zaz>!J`t-)>|6Ngzl zPz@@*)O4*~l&GD7i{IA^4~{j^$Djl1OpBV9&bn|hN%F$()eh1NXKMY9-C}7&(I=xZ zD}F@qa%*|}y<*%#>!46aVYB+j%}tTD>RujiZ-iN^y}?Ja-W8m99E4ENrx&i!0QBkHyt>FA~B$~lIaV^$!-NJL_t z%fXjqA(A4apil~HMl|BH3PO$*Nw2e@OUGlFRTGYS!EK4=(b8vf!x1F^;(B{f5Jw=b zu*m$Nl&bgljWg{pdHlY3N!s7XQL`7@I7aQk={*jy<+RmZaGWYdUO!C-dw8z2wrNp| z|6s$&@>MbbG~4jh5B5)fNG&&Q6GG(a9z==z>z&Qo_i}N~ z0gMw@tl5;`GVae$b=Nw^hs7Ta_GGt5^ygSOly5W&-VaRa;VC~W)2Z<#XlQ-7LFMu~ zSLV>K3ddto3+WE`-t0e;rSKBV>Gl2l$7+tI{4aMg-~RAM%;@dqqm=XE!te)$Op5F1 zAll##Qs%yI+-7$jTN6OPQf|7xuu_Xy0b`qZ6gk__UPf?%wLj3x+uZN2m`Gh4T`pD0 zbS+c9E!$=3lX1~(U6&A;5uLuqgX5&GWP~MlIqi^so0f%YXCl zZ)5kA+MOo1dK{G~p*pNk)nb~k2OAk0qwNjrSg)@~(H-%% z>j}&BemU*6g}jvV$!3iwb{z|QkzMo5gUs7;=UB{EC?4l1yI1N}7~0#P;p{q$^w9*q}1j7pd|y&H0ZnboYnrm5O@ zq&jc&y?18(^~<~pdzL4O*NS|&;?PJ*1AvujPwNLIPwv|Q@rl7rl5dxQEN)M<1*1P2{a*52Y1VD*JXYE?`-)BeBN&3Ogio*`ZV7krL0kiB^|Z+O1H?XL+Da1RS_g zh(Z;C;2-nCH3jWBj+Uz_pg&bT-}VchV`WGGax})~ zq>~4^n9%V=f^F^b?v#ZZ_W}(CwS=wpuF{8}@wg)*5y|PT1)jUdX3dvBdFr>mN@`e_ zV%1J-CN0f9s0~1}=uM&H=mZO(Yy>`Z(0ABHVc(1f{&YF6mpIxMS2#EIz%0MpzO~+Axr$oDO zv&@ExsbN^#vMu009$sP0;NVGVwu$qzgsk{|NKo2EnyDAT#k zv8`l?I6JiV!&Uvj_U?bvSiS+_rliQ}>!_I)tX){{ZhRsT*Jw4&S}SyVso0T`owisu z3kB6Ubmk`$TtM7TtP$HO@_?5Lsb{L zAAp%q=jtSHLvhQ3EQiLnY{TQ0bf68UGEwM_JqLhn$#`;oYPp2*r3Q4$Ja3@@k-^N# z?$H6-Yt50};T~n>HO$axTR@nn0RfvqVq|jfyDcWOYJ#DfEbW!+?Z>E|`s_1tvgOO& zQ%15co+u8`0Cto`@@GVaOlKL z9i?R-SxGz0=u5v51#4cTIPW(38*Z*mkQH@Ysvb(=r?BbBH=dMk!>~F8mZ;38gS8sl zMCyQ`kBRWyj3Hd2pU3Fz{$7is3@f5hpU8Rw(Zb^RDKDU?Gx>>7V9X|A=8gI_<8t;m1TDbn`VV z(jdEX0n_NW{z|S<8A~H!WSat@Qho3J+td?2{KbM2)<|#;Z9braE4mXb@#czBo7Vj( zqP+Qa(qO^pFTHJOg6iBm#lid3M>ivB-s2Cu9nrLz(kOYHSxN+6@wAN{aV@re zK5YPTCw4y$aeJ=(t`?tLX(H3xhganuj0zHJ1bS%1{8~RJcXqmGiiIp3Hx* zh~MGvR6d`SXdbA0ee%l93lsV!hR@?*iC2+EIhiL71G81-bU&u)5#2`KK4#%@9eFn+ zo@F9x+ztpUvr}wK4KWHM;#t*OQ-!!|Ah56jvELd>N_vJoNCp4mESqzj^N@f7vf)4- zU%Pez$2~;Se|D7c3FBw;RIZ^>F3rI;K*=Q*=r^)y2Mx%sH&hQ&nS2q%^d1Fakykh{l@zp4Ij407W%dKT%gf8MohoNs>{*I8wE2Z#FJeRXucwSY1KDs#cC#PGox)sQ5A1Iu;S3do}EP(iY zk1`u+95=%Tq`Md^sb}gVZ{Wt+E;S7xl0N?BM%`T0Om&=iim!bMxy^FiRs+q^?zM!! z@5jom$H=-*Hc_=h;2A?-jTGkD z0zHkVvX%@sZ2;)`?bHv`aK84STU`Yaf4Uhku2g|i@`e>_t{gkG+r8kri{2D8{K}I| zJ|>=>tc8#M6Z6M?JX!{9l+CmD)WiA9H?dA=t|DX4o(+2|CG)J8|1-!9t6t4kYp@=#Gm=??RVh~VtQvKP?DcdI zdWxkXLy6BDbsj#;Fu9|d1Fs-7ZK%}|Ea&yc{H18_UHv1O0HU4uSw^p3MQ>=V&!UDWyR~^ganaj-iAes{qIw1p~Ku* zq|n;~%@H=fS<(A5RPqmg|EjF#a0KV3*brXIX8(A(cNrw)h}-s!?dPLok!sEH|3*)+?+8@vxEeCa3i7G|6@qYc$(cSiJw6V z3DnH0K9!wZ?V=t`d^ZV%mvTnx|8!Id7MN1zLcuUQYaRsx^azuM|8CCQre^o^)nlSV z)lu&&Lk?UVA9hkcC9ubrGcNn(+-Jymj{Ei+QavzRdiTa*QI!3@3Fxs5-j&~s6vG`T zLN;d!yWJY)Vt!U*Y4S@|6%C!X`96Z{gM){ad?-=R|6TUx@MN$kux7mHHh?CFDw$!~KL*TbPC(X-*kw(fm8tl58do$VQdwruNZC-&X9Q%9oFh1KHT z2x)oHo9pk4)Q#2nnh30^Z;%!tpqq+QpwACJ5DZ;w9_K2+--#^y^cF#jz~sI`fo z&oj6esn`(oQPbZ*rKXOxr=y0;TZJN7VJ6CU>NmOhV(@&+|LV7;7|`Q>1OUJS&?Z?5 zF4sfr8~lSFn~)-%8hXpk2BsOXJMyFD>U!(6WkSyqh;Vb%!(?{T1kf=-$PT>MDI88) z-?2_ohS<~n%U{H1Igg=H4_nE5yE|e`pM0*wrF|)|fwZrtbs7YZ55fp5Lh+2MVJ*yv zp`T+y9S|XaCYNGn24-dapPXGbL=xHZhYOHGD6Q!l;zX|}eDc`pX9HHv(xzJIzv1W+ z1&Dm{Uk+*FF=WJrNYWNl<6aC*kIicGoxzvb5Go}3=O!%?Yc=kg*)lfI4>av9s%~nL zaqg=vaTR-6G_Jp@Kb~FJzuH`vG2lPwX6>CE?c4+DU#UX_V@h7m$@=-{&vg!dyLeF&9z=;kFPHg6 zu0k$_RApD}Kj(ie`sP_uMsgSZ5YMXXyC0>!jN%>tdQo!uBJ7TA>dSAqEX;rmP{4Eo z&5xMeWe@RhBRVJQg4U0ud7P9{Z*ig-K5jI5^?o}j{_tWQv7|FSk|$pKx3gvP>G1Xp zxH;o?=lZGdcfB_akoGLIBp{?mx`S^RzuO31Z#=3l4tC>LjIoWet=hL`Lr#Norp$O^ zk_P`hbvM6h?Y^vbJ-W4Qx>k74Vqx7$+@9u&zh?eKPCIduQ6u%|#S18urj;Xo5gH&# z^z{H1p50OJdbFfZHGX3mM}=)^d)Kv_o7Ne@irfWg39Gz_Oau3qmsy;plpZbR&9;v)6X!Bi)UAvFdM!^5lRA9H*Wa=wF;DUUXyHYjg>ftwxx{ z-Pg=PgXdbDLHP^pKGscq2)^xomB8mVN)3NYg!+dhO(XI($X=m&2s*J=X0KlxKoZJZ z$=$3_pSmu4DG|3XO)<#V&T(TK+CTSBJ;r&eyBZ~y=p9GE$9?K0In1Gwy_aL5wBg|EQ-YODN)>%tKX_QBo=|rts)<3flkNpF`z=~kRDuY4 zwnzCOApLc3e!FB)CfyDls2UYf0^T4?(z=u8(-EXKUnFu9IPDUhKN2A(l;N2f9dM`M zEhxWb3r{Q=-J%XznJEHSnZf$^)u!oIP%`W|AAhWM?-vp-UpNi9$A|r)s(K!+IB(M& zy*dPK9$3$3uU1fxO&23|0^lJLn#dR;7ua@%R&UKoyE7cV`9HEsN1dO>|6Nd#8>bVa z^XE3ulVCf(KBvTJqBox5fHjWqNp~t_|Cx~!GyDTMr;VM~DZ`z2#*%w$4NgcDht{Qw zrwtissk2#bnSi-ix#isUmgGQcQtnVrbTTR_wWllu(jzAwn;47-%HG6D*gjfx8mC^h zK6+5IsA64$#89Wsh{)YPZ>l43%{1C;F?RV;>1Bvu{__=Wxjy;?#O#opV2YsAes}p7 z2!;sBeGV%d{R|XT6MeKLgr%iVFsl688HYqL8T$53iT-|BcFuAe3`h;faO~AHhNTVw zt4iHKi$VUM6FrF$8iq`2yhAzjZb?ckU2GbD4r^qGBRhh^>AjYO8yIr@tx#XR{y@!j zRi{`#Uv5Z7!NM7fp27t9l38yKCv%dFc|ClmTYv>JeIFM?%y3xZ4KR(i5k9@@xL#P^ zyo9yOL=q3Izq!U}jg|A^eOhm-J=Ss*6v73YfL7{YHx;17c?}TSyoa^EJRle%UqfGPl%=-#+#=czZKDA6+p~by9{9Tj?@H7}6y)VRLX1NrolH1K)kV;+E*u zyM!hENDMyKU+-bnh8EqC)BdlV+8$tP7m)n10*568Lc`O))A7rJM39^n_oRQy55m^X zQA`O_oe2)YFSW>o01MeM@x3I2YwC*z7q{ttSJcdZFSwT_q2*Zcb(WJy*1xc=9<&k$ zf3UutQ580P1x=fV_tK^+CUC^+Xz%wV>b5$^kW#cTT4?b?#icuLmbqotC`PDL@3P5j z$2=-N+0T8D*S3?sc8i&j_c#u#h^wbFkl8$}#ZZXz8B0V~&)D&<$oJ{QqIBLI)_o>D z^1u6~xrf#C>&brp!8I#G71x;>Majutp2+72ObN_s*YX*E2cL9MM50hMtp15VM>G_d zXC9;$oUa8o2#lXuL?en{7o^6Mc)F(H1(D-Af~ zi02!w_{%Nd$UMqj!Gb%K_@e4*+Pl^E+F4O&C+7y@SUL~UBV;nJ{ndAmq0~1C%Tiw+ zM~hf=j+fl`y-mo9F-IxDb}!G%6vDb)mgsLgSE(&@Is{B)j;L{}0KqAj42R_CMvB*# zxB|+#%uMP20rH6a#MuMxs2%nTo%P(O*aIR4xAD>W!|hw767#Gm&}GP3g1bS}O>m#% zy4-ql8xaCT5uDn`oqVNalEpLps&9<0KDfQ(j`s>Sz%0RO4#O7+48fS zAM0ah>{Ik`!Pb&=SjBpO8;^n{I#9pms*DgU`prWeXq1F)7cu!1{#FD^n)~1-B;*?BB&7VKc(Sma9gWAtp>* zGE+GvJlK%U6Uz$Y{dbeTjkv2Gm*0eq$Txa?2>mU}w^iBg7OoPIyg(n-niAy{dkM2% zJ%^>2XPWY^#w!5&p>CChh+Ttou(D1m#ie{ey}3uIsq5JpCD-*CFufJkijAl#cV#%VSj>WouMo0hfZHYFaLxpx9 zi7y3kcG~kaE9{Yi0~bKo92Xx@lUK}dyg^OZ=MwjL=4CQk2IH|7;Iq>aB3JS6L!r7) z<7=9)be-4yGC5(;S$tkp)+s)SsFF^fcwDkzFPgfQP z>&8kklF)ss*WQ#6z&Olipc}_90w8K80MT9shlcxa^{8>hH>=G~Ugb?ctoMyB!a72) z+t&G6@Qu)dCDGq!3!C)SQR%S$RPAxuEK4A{G`NK{whUW6o2=ai&bv; zs0L2_E&;o)kc^x<%0iEoHo)f-5>uZ7umNqWevhMORu1xi$;+Sp$k~i)Fy>=IiYxbO zJQt@``R^lLep=FblT$=3lXOgi?Oh!+SMu*kthb|bb)Fhk2xa#kf}w)wc#(4Ql%7pYZ1+sI79IW zkcXzHAdWlwH(#nRtHXVrN&)(HspY7rZd7NU5)+wUlVhv3tNLHzfy~2aTwM-kR3%Gy zhEN1T56Rmh5apI^@}UJ~V#3h8#WR^l5G1>=UP>AGz??qy-WMIj&t(G661eowP}h3` z?SyzOC;X5buLTxgG2`PVy>{l-W}XSbUOqL$5%Le2Dpbdu#zU%H26f_ZFs?C)b98Fa zJSD4ByibIoX+|&98#z!>jGpnm%!+&&@K#tH!(rfV4Hia9cfZ^J#<3G8@5Ol@GIoAU zT*|N8%OfhT&rY@Q%3(jfmi`22IRPZ}Yk zm~Z5^1{n>CT+OcAoRSjAOm>lye?_F88Bb|~ZJ?S)&a0Wtb!mgf3!{Lfi=?hmWT4h7 zG@U(NT#|9732d0gy4>zL*&L zOf-TF_Oi9f7pIZB;BgedTW-?I-_mFJzIuVUlL+E>S{f-#=Q`ng2l;0ut8*uMo`Hv&V! z$2PJ_>(K~(79gQd?>D!CMTcT$|M{K~r>jY>kJ=O=#%Zo{Su3lvYV-w0Lcgm^t0TtC zpZdeeAN^oQru8v2z1Y6he9J8tI-e$zFa15cTUWT-CUEr{9J?W96f8ZRSI^5n+)vW{JhbD5 z`}J*k59j^vt+d!5!&YQi@QgCgf!FP2RtV&QbE*dm*q@Lmt)}nVyYvzX{*b?LKmWsj z&4I~BzCTr%^~%4DuAS{~;Fj+zPRymuH_tIrR3?0zo;x1zn{-UeH8!dnrALhJ%*e4_ zkqEgF+I-3-gp{ zO{DKE1^FWkUtV++f0F2U0yZ>K=P$5o5!RkZ@-126r)cg1?x|Y7vQkdYZBp?I8`m+# zeAiex`@gf;u(7lP@8M*d%xgFd;w^hIY~|$Gj`N-I&RM}{r8OA43b;=0IH`)D!&A}( z!@81>@Kfgybv0T$ktLF^MlA5!o_dtbJz*Ed=?%mtxrYo4xiya5FFZr|c4LWgUM_yF zb=*{&ApM;b0zGiUCDobv%r@KCf2Wi5AStk`qB0WW=CdL2buS<@NI1KCgZpi4=rUI| z&7wHQTT$qMf{|%pAzu`bX{cG5vbxkm;~!>}gvSzIt({0>#9niS&FrO#V8T2a3hnY% z2iRH-_AlgUnSd$IU=o#^>`woabB>PPAZb<)OdD_)g%FdL9dZ&>w1`G zg4;>MAtjL0{qTRsh0%Y7tk6Qs(&~$^;Y%X^=XH$`D;ZVM5A;f% z#MrG=sTq8Q!(|sRl~e=R8;pi(yXDB(a)sSe*D*IeI3|e9WGUrTXT{lO{j?MOr6UA9D?lXwKWv#ZlimWyJ+V;10iUVaw`AgY-7 zuy)7p2B&R-V8oYrIW7caUA9tBM+$-nl;zgyUgsxTwm!)pa)fW;4_b|%euh9QrEHJO z^aC5>*A^k>ZQoh>IDcuD;nj{&%v84HY4@;zE+ctI0C>9lJGYV-uC87FCXN?Sw?28S z--xcAg*LY+&I&4NUWbdo{entS7{~j)f&2B4WSqT3i-z!lOoedM?d`V2pVgfoEiONa z7Qe$)FdG?|sD9(&#GHsE7xv}JUIamKnK*F8`SGTMR`%rKdJMub6)?=AkOvCL4|Rr` zT4-=PQZDWG<8Y@B1i$!O-s%s!Z&C{oHNZ9>=c(+1RYhy41YpiB;S0p_jT5kv z^7Q5K92eKY(=68IAVFfOKZQyXN_#-4)S+nIS8kSHE=e$#99P@iwyQ@}cJmP&8E{Yj zIN1=8_KKlDZ$#w#nx{-=hn!7|W4-u06!!3wLv)sBZ~8zSD&lpMlbJLRn2DSX@FeD) z!QK@3$|5_{B?Q{$azwSN^pm`kMrH(}Jegk)Zk{o4+rZpl-{1DkJUQN!N{JUl{jp3o z%433|V~jTZiS1IfeJFu17o}4wdlNqwJl6J~nL;0LthiH9e~g~DPGqE~#=ZXdW&HP7 z*;+aHA%0uE-W)M`<*{CW4{&&p^>8UyJsluaO=%ROn{*YG9Ur!^5iqfM(**gSrVqK! z|IhRxN3eVXKFcbZ8(?g@cWtA>PI4IR9>pXR6w~p^@30D~8Z1k=VK8k53hmQ0LjC;I z@4qMXjiak>aPBl!QHyqw!&FEjL5UEYB)fyc-?Y0MOAqJ(N@Y@fR>F zhP}Fr*KiQ)gXdH^`iyP2_}eFM4^-dAX>5G`izt=&398*CkU_=j0x-x8Z9=U630ne*vWEcBl zI$f@lzdKnj!k!AbLH>mafE$?ok-X^|un}4jQQ@X3{Mc#SR$WTXe(7t{Oh0c^lsPtb$l!SZy#rz7Xvj({*?*tQGe zObcZxfxY)z8(Iuc(TW}v^!#!l&swp*Z%a)KJ}OPHcY*7FfU*A90&xHhrQVv0Hz94V zE8=jV4Kn09>AG!#ICa5eI&+`l&R}L810*4i_4I|HrQq9L2@mq>_ono$5P3N#;-MMx zrPibuW~;wS)&*5wH}_Fx?#XVHf8cD875gK4nOKN*jQadz@46<0$+8ps{Z|3fI5}ak zJfeA(|4|48&jY{Rx&pH&DCPKgb|e~2&;iyQ{ZhXtauuqyHRZq-?lJ^eIRjCcQS2$V zP2iB*4cxn9HoH7{d*bu_PgCM&z87?2)^AgsbXxl-YE#KZFR>c zUS4l!s9jy6cVZC!W3rsw@96r?XOoO{-^UvV_(qCn`LA0RF7j)H-7*0;RhljowjpR}&F8{c))KRiR!3v1K*$=zrzfL-Oda zI1~_1KTOQ!GWR3RS`c#6kEsllU!l`&DbX4&VMAGYaV_tY^+}QfzxosPJs(?xH?JEv zPx!=?eU{I#y&}vf@nyQ0MDY|qDx_|~6{%fjz+$nio0cmqI`I4-D_ z9YHZ#W&!}n;`-tX`+v6EUU~^MbA4Wur(cK;U`@quL%|{8OfYu=Z;KY!8{p zz0TwhUG3ZObxuJ5UE+tZ$WJ>%hUw~hs~P3#N{fU*;8#fQVYRXJI%ejIyVVGB1@W~R zdny#dg}$fXG^VO_qv-Tu0;5s7`08Sqtjnt{jlKZHJ77CNWr>jNB>iw+s#9~K5n_~R zHj&dWb&(qA2RkiYtY>>@aOC|btAqS-t@82fiAlfINsheqiHUFP+Z}(Fnhi4Hrpbyl zYe}l9&P4DQh)QHlo|oYG_-Gq$)KbD-xOZive}_VS`U^ugpRfjZcqa#oxQKcyaBcOH z4eV^scDctoj_5^V=5iI@>Y#n1e%gi~Z^Vt37ph|QN2Fr6kMw0cBxwtjG-#Ie!+E*o z=UFe}yf78KT%6JIJnpXZ1R9_LR~ZgR7VWafHS33hXsv2Su>fC<4u_nyv4KCO`oZ`X z@CkJtvYz%T+<0I2^kb9O#(Q#rDKNq=L(U;`h z4BZDfo9y!td~s11EBdfRf`E7Wj0h~)_EkIV!+qlZfZZu6sn}tOTS1M}mhpy@{qvXU zZ~p=!tAP+%sm+v_5Ie%Af7I3V%y4YLGw-|j<32ujKyV&jG)2~{7sLW$W897|*YSp_ zSN^Hs-AOaKiYP#+_=lCoCK&iG6UP6Qk@a^mwRaXj{bsQ`Mxto3Ax!KHTK!iCdxiJ{ zb?0I#ITCs<8=2N~j?pL^wW)u1#j=JqoT&I5^>t zQ)R4P@Y9M3{dVBqsv7(;6(H($^Wk{b*h{flHYG7QAPsEN{lI~ZVs!~5=U~K*i?zR7 zna~i3S~{TPg#&J61jfT3bDO9!jX!lqPC13{m1k?Wbs#DQ%2GB5OyP7+&)A4mdA^VQ}u_sldEz}2l2L-LE z66ae~H0th%*E2Of;KKqd-2w&qX<5mt=b-mI-S1{IF>7UeYe^o}%9Z;`~>xj=pI z61sOOC8)AR|Mz>Cu=b?jCKZ?T=dY^)=B7L*(Mg)y?ZM2N8d|TV_`dwb zLbud}36mu@bD^A+x6I)(iKgXyuaA1oCv^hu_C*u1<_KzVo_Tw+a4b`9-|0R+uBjR_ zsS1PJxt?M6Lnfhw*q_xt{2(xg&m~40pT7GtkjzVvgx1eGk}AxmoGN5Z+Ax~0&9#k3 zG+|Cx1#BpH%Ty{AA4|vN@P-W(794~*7*6p*!^ObeO?7S9DHG;?PK^8}Kb7CE0`v>l zUoAf_46(q+2p$1>8wMcDDxr>hm!r&qgqVfI3$|>fp}25OIaakU zp$(hPAXd1p>INro>}L0`n1cxyzMqW9mCs#)=SESC%qTKAf&J68jLPOs*3S6)a|NTN z0@Y4{VG<_5QC>nL6ZBJ}-#s;|GXrlK9P#ratn#M_p7#L^y`b#1QQ`QD!lW4S99qEe zQ_CH|ngirDZ?LPNZVtZhD8^g!sTHzli~&F7vMwPai~uQ%U5Bdv4Seos9KZq}&Z`43 zS2-;``YLtfF0kM;<@0X^ziINc8~;aOHXkRpXN{qeWgFSv=r@*k>X0XjFJaPcHMNla zjoO!}-G)H25P*`M*w@GZcz>#Hpj_@~G_B5zS-Ln4&kl;XIp4MVTF0|jxyHVCK3+qd zMl0Yi)GL8C1Md4oPB+8C_mauGJ%`F7Z?noT>!D`X7L)BYX-zntfXoyf+L3g#(tfdl zXxvyIlSpJ>%NZLGIGFHAUu7m5unCX(&ql1F(=rsIHe#_cuCl;V(CO_0O0jDhOnHjn zvD%AM|1Rxz*A+#%Kb|bvctx+KJ_u56#j|J!Rfqq<(+K#InAYWry$t$PdjymW7Q9@) z`W|(F>gVo_{&l%3WA2F){Wb?Tw*bp)CiNch)eTQyjJW>00qoc8JGu+v#aa~+)hTtH z=i~%kLT|Mxw?R;)hn62}0W2yFUzaVSHomc-1;6VcP#`kCTJ!J|Fs~9xVf7CB6B^Ch zEf?qWKz5xVPWT~rX|-d&XlLjkovkE8qs&y_k~X+4(@M$RiN;#}!9Nuhu#rqb?);U= z_sXyPN|7rQcZahTqNd*{pFHg3(Fo~=2oqpbaSsAwX%C|nA26gm{*93HhqA&<1?`sjq(PC!@Y&Df`()nM@V|3T@6)amt1rC?&y4^3 ziHa9Y1xmExJkJk*=;1 zNb0ituH%QUdM#xkuiaAeWGbJHP*S^K&y34s)8|^-#V=e|wPC|HAV!;B)0qUg*_)aJ z+fx~5QCzmmEkz(@sFWyQmO_iWt|ZrLN{9jmrxS^Xw9oON1JE~8N1)1mfE^T1=aPeE zkD%+JylHImkYpK1vzE@#x6g3EqepVu$53vG-;xhZ4&_wxkmNILHT1Ki7TubIIq2*4 zj?|_u^OKaO84**izwpmEC;{kbyy|ZO4q!=L{7#3V#&iv=!3lKnwbs|_ET0Y+5I|P%GNyk= zsQCGlomwK+(xgcl`ihgCG`By_JPe$%ae3waYsbA57?=N+4fn!ky|3y%M zHowJOC0S%#W%$8-ohs4rB*Uu;k9;Ry29$RPv7kwcw&+z<5W?}ihCLx}Fqe3Fsa^w1 z3JQk2*kZi|;0r7&764@no5+r_j7g)+Ae0A2&T%?0y7}B%LFIP><*cDWr2o8as;<$} zztzqz=CUTj9|(JQfb2IqdJqEnhu8mqx|X4IHu?V#dgw{seIhRg`sgB5<0{n|HeUf{ zoxLC_I24}~Gbap438z1Me4gKTosM4SDs6o<$*XQCrRm#AnLR8a5o^` zfW9{8d|YX&AhkVmAh9$U843LTafRu`iqli>1s{v}Y|ll(tz>NO?+TtPy%iKOdkpfAt1?{#X?{k>?F)2q z`+lDH{btr$GwUyBbOyYx^E~!rk8S^L3QYq`9zh5Z0CZ6=26a>zM%6ZE3OsYR71IW%z*e*5J&}2R&1&~++G;!Cl&EJLOUgGeWlAB; zVFWfM?B4;9LyI{aJ#D<>IXdgBz2lUBj!?1+rfPtNA|C%w2>t`GHMVxXE7$sQVG0(S zDUcGt%-Z0=0D>Rx6Arjsa0U_GCp%ECaXm$$S)DKCt&X*R;J=u?nB%I1(9V{M&r0Hb zGAwnzk~%PL-v_G_o1Nn%R7n3YB^t%34_@^k$LT zQzpH`kL$qFw-S#!#q=)Ue>4z1tYhdRV(ZZFl~+98YjB!Z$7=2tVHl0ms8wO(WZ(PD zA7q5`h#Cmzz-r!^Q@)3#B84;t>&RkxWa-q?l9aInqyY$UQ*w zufg?jzD}Sqc)O1xUH4%kIO{Xi96P1_N3m$VkbRf;%T9=XGkWfbl;k2 z)y%xr{o7N5P}%(Fl>G|=`_G{hXu*J-3&<|<$A+m=<*NEA5Ld=5LZ%A*#Q}tyH*D8q zYwgc0V*`Y);hq56pJ&46+;l$HD+y_!3wooY^TiiLQ@WJ7Nmy{R(wVc+u1YT8xxFU@ z=Pe3__5^ItNdQA-373mWlyI=?z4RN4)uQ71W;-sES_gI&7aw0|v;iRH*79c@KD^yma<+Vz4!x&KigV%d>uh3EKb<1{|2;f$ZRwy+5BUiw1j|UNM5Ke$Uh!{ zvwY}*ri?kxq>w)$&B1!UVyrqdb?IzfeVort`O-)mdpti(JqZ*`4eAo<@s@a4t3H_%JH%i1*%%MJo2J7p*ij z+}TnAj=6$%drTW{l5S6y7i}4^=QO44!?OqMw-ad# z1>aC9$8Nb_(FNRIpJd#-PHnNj4>%M+J%cu`(EO|#kTcG8gTdf<<9zHK0l4f85OcC% z^SYl+6JKP1RfwAcuobEMQeoR2QnkDRxM%y;jaCtjN<4fjVC z2%R0&2mElH4YT#_X{ZpZe{B7n7;dty9NEJEp@+?4p$FxlB-Zf9kC2l~@Soqg5~q6- ziT9E*1p85gvFpc>Nc2Me>`4W{DocF9-JLeVovNhw&tkpbybRdccQntnT%J~z1H-m` z|KAHtGhKwEiU6)e0sgJa3g|TAsA)xBh0c>K8O3mZ6XNc2c&E$BhMjIPSK;$aBr|SE z@!9@?Utv5AS9EI{(y@`EeDVri3!vG1Ht^?Y~*KoBja5?zb>G7@dIuHPM9SrCP0D8Imdn6z=(lVK79u`)QlkS$$q@S58QP_VT4n5{F&y{XRZRsm^{J z*LQ-g&AuqL`_q0%k4|c~FtRiS1R)QAwi(ZRd=x^#HWM58z;&(GR)=VLsaf+a8N;@8 z7RoOeK6c$_SGOoK(99r{UwB|VJ*)UF z4yQeFN2p?P7{=4$ixfa>|9RA)lTEgzeTJgy$zU+h(UA)pGSIzmK)7ItziHpf{9{^5 z9~f+`^>NIp4vb1*_*Fv?0=hT> zfH}C%(7}G<^+FX>fbjZAL`~zg5mWKB@e-pW7SwX4crQyfMXG->qnX3Tw%=n_l(#@A znPCgs;1y%gBnoCuiAdxI0Sf2x$+M5-y(pmW2TFVcCT!#}z*(4xo0@)D(yei#SxtpU zV*~W9H0VTBT4q6Tu>a1U*X|FCw7Hbo>)J|B!eUjTUN4UITkiA@OoD$#ko2Ec{+nMX zytKO>mYzRTlQPiXsp2tdw(Pn5t-TDxx<>L^YTqleMlbQ&PJF;?IbXpPdLQ+6(EX{A zfBBL4JIV0@07K~XyodG%&mQZ<`Cjn-0QIKB>tVjW1WpH*EA5(Uo1d{{J1gzDgp1h- z0(PryFLAw$Q?!Fd3Y3t?1iqimPR6vPtFjvwIm{F z>~uO$MMy}874|}W03gxc;uul3SSOPPM}yR1ql&0oEt1s7v`A+d_lci~rXr*b%!7O@ zj9?_{_A+edNt4czE!KnNIb-w=!6h(~_VePl5J5~4x)nn|j7*vbOAemiZtps^R}C{) zt5rZq+0kp)b};bVYLwphj^#I3^_BfS=V4&o zO_nAI8GtR@{c?X7A79Rw$l>Cxx3{PQo2+M%Kbr7BBuVD~kAsf;F1GYK1U;@l+9!c+ zGLQ`3{&xG;88AVCwkz^=L97I+{;1`P9@}$LhpexP9lV%RJ4*H6u@H4|$2tVe?kG5JDpC z11CmLv%T5=j;a+Cl>#l;5M+UmvT|dsUU#|X%S_IC?+P%(`EI*eQ`M?rZ|u8*#C*-U zoG%m8!P0TV7o;^*e<}P&i@Ec9FfU9EqS%!+~2cJz;GTwtp+18XEG={w;A&?&E4RU@0r01ts4|9&$h9-P|H~83YTyw zg0wscTE2|$@b_C@bHTX)6iYnusjP3rjF0UD+8K)9_^GER8)G4)W&6|iuzSu&po@-@ zVIlqPk?TG9HFni&SG(k*MN}78*3X_HOtDlYhyeP*v-*P$ZrhJz*>s`Zr!6Y6++}v@vnX!);VEAn9Y{x< zITL=PwkxOy9YUQkK1%#1v3h&+oMs$}FX5*%4-`7dsrDMaqSr~g&T;|GmTcHBzCO$5 zvCbb;OIIWqwpmAI<&ryQ!Ts(Ai+CHs%q$s87hS>Ji0|bbEo{<+ETU(H`s#0|+&=`T zp01W$t?CaIbyqY#k5nR?hD0(KfnKsjIL@b%5&Js(gUPJ+<9{en^yZ zt+j9meGLv#kbNzb-1!BONRVzi7Lw6weLx+9`5dvvFrz7%!9%42yCcbrBOhU9!hdTU3nkD?7_4c*PeVWZiBUo&zEwmv=8p}m zacl5J8AtztBioi;e@)EHL#u5>345h)k9A`~D=%;H7Sby48E>!FpkRUkK?zMvZ~1V` zoEdeXEp!o{w47KMmbK$Yklzlz2Mhs&UxE#WwZtsYE` z5`=Rn7YoCIETak|b1;RL41FgXRsWP8wI7_~#=+-uZ{_}kDnbYIw?-32QGOfs-_P*B z%C#DG)DbDBbx5yg+OuL|L{Ey>z%2UDT%nd~*$^wBpaE zHDkhNw|LhyVSaTWc)S;Rk^LPI(GEogVr{&3zkvc09v*^u>$7MUW?X}Ofjw`=->B%OmzrELSP~k|W(id(=igi`X}YW*e@e^T!_k@l$AhbeY!5x7b<{p zXG#dFalj&~qB{69J8pDe$=gMuv_=lSh5-q9MsKf{pTdaJfSLv$j7s);N=`FT{AmFt zThoO+uO^Za^g`0@_K}_o4Z>o71FM+&o*d%AC!HYQQq`|)lNRfcn=_vU!J}pf*t`(= zpAQWM4f@F7Mi3nwIDM1H`14oZm{o2wZPHtJO*SGL%_bpt3_ursV)K`D3{ZfUXYV%Y z75YS@ur%7rT&$z8iJ>G~VEgyYXgTou0Y*j`wNk*?R_kN0qhom#nsKE5Tk<1Ovd0Hj zh8j6SLk#S-v15uU%4)Sq(tq_~7H1}S?Z zb5@##nBIfk)QVUptgeMpOCMAw}mcc>IBr5w(@pz!_8w)Mm=}XT$nqQ+Ibo zZ%KXSqygUQqg*y*!sB9vG12N<)`gdo7P>XELNZL?4QLJizrKl=kbp#sI%|W0E*6>f zCIFr6ZNScO&x7+i1vTH-w@M}V)FHSF;(>uK925Ux0icluHRKu6|NKF*{hm7ZV?b6P zr+7=3Ohx|v8k|v7(BBuT2J!D3M0lrx(FJ;+BxQR49i(Bul_}R7B4@}PrU23ZV+bDa z1|?$H*;w{q&nwToh@4Hws1mrRS3x(^S?)vVqeq%nkN3t|u@KN8ArMAhd1jN9#R2TqRwy5cn9R#&Hov!oATIcsT43dg}-|h}@$_6m%^)ra& z;9?0<=ZY{H;O}ZF9G*5m^zR4%&BD$!rzl{cpi|LUoreSnb))YIts*wo=7RUvQYe+6 z+?7t8eFRCQqa}#RD%W}=Up^j+CJHUoJca^;iV`-b?_|a!udW$I1-O4gJijk|1RZH` z`3=BY>LYPhA1y*^{>T3q8bCuF2r+I5|FW>a&`I{DwcE2L1m0SwEQHNq7cq5k#RfIt zvvB&q@w7DbISM{7$f8mF+n@dWiH-w*V4d9vfALX*>)?jWU+m)EDh*4gxKjS;v(uZp zNDJ_wJF719U9Zh0*w3O-dS&(AOg}XeNF}pV4yiY6z=b~3cm^=X0)anmAZ3hnVEY{_ z<$+^JSfI0Xe&RarnOa;r#gSS-(^G}Q%}w{0&i{DxTs~h@mrW0MHH$#&j4DU}ZwIU8 zAO!qJn-@)g^fmvlh*Av&zIO-CUuAu|0)oKE$6GOd=rp5m5mV}9GRD4H2=#2ul)Ni< z!SVDhvPp0K^BU>>9~G;ITRWw(Hhbr(^IhS1d2+A0Go(NBYZOSE>~(Ot)*&=arSIw~ zj+7};9ZqvkZTwF;MHtQgL=WY!x50UH!2SqeOD_N^HpJ=Y~id1ge zSH{EWti?Yc>Zgd`4>|(CJ1U6un`WSvQ~JL6g{hr-ZWt$u(U?mjE-zIy>PG6__E@j_ z+L2tn4?f~grH%d+1LX{OU=!iG9HYt&7VT&Ndi7p6v(9iz#u}fZ}5lUwr zLI`N23#ElU+?BlGKWd3zeMZg^91*GvjnvElQ?t<;D><3suP?)a@mBb=kF0$%Drm(? zf?=x!0AFihvwg!Cg0Vvn`JwLU=lAWh!_+Ypk5?Fw)|m|h?zaoTf{o6_-tzS!P!gem zb~=j9_9LOw#oi%B^7Rh;RG+lY_4T@RK$ELxWOX{&K-U2~Jgv^7ou` z&V(G;+s@5)g0$#_jrIrm;Bv&i_@D-yZeDc&IbJ6Kw|MS;P+#bK%(33gPdA%IIG5yn zzSsKJ(wZ=+RRuPNl9Vz($7m2sB`SXfjgs4D*R`wV=@V8I+@qRxq|qvK!)H|*XwD3C zH8J$1IA#BRntxEhOMh@LvepO?PLRTH{r)|G3sTSSTJw1o3L5{$p67%UWHDcKo8~Jw z6q?o={p6h||uZZ(!LN;~7@Y z?QCzC^xzQS z!Za-20DVFO9+_u8S*z>QgBCD0E9sl|ZN?8NQoABU&1ZbM{3$S&Y_P;jFUKxtXMU>Q z0h!RLc#H>0W4Z5<>{G~Bp>JlA%fr2HGDDW7L(lAxrXpQvhK2t5&dWKZK7b#?vh`f*aZao=0iw}|IfimUpM>ql5F;4l&~pwgQzH#JJc z0umFov2*Q5;IBeJA)kx?+>jIr!Ir`ba|^Lq_;iTLP{fQh_ue+K=h{7NMU#=aC4WPl;Z#6RC;cqK| z@o5(UhMxWPmEsjIK#``G${LJu)g=3SQ7V)6ESKqXPK8t(4%3WXI>-_eN@mZNZ=+4- zKkzy$L6CNq+EJp2esgeeK(4E)SNnld{mE@jWTO*aM#CH0lhXr*iwzpGRo#;_@FiU9 z>9P$O^ZfWY_qc9-d4JK-eA40Gm`#2DXQ@TCn9B9eu4EZtL}NdlrBXgcq1goKeWro6 zO!e)q-{-Gur4%Zi6jM?cJV_$TnE=1^8Uo~BBj$ww;8XjQ^wBP_{C~_X7mVS0VvnRE zYbIF9@$nB>$VKUoIs6x(x~b?p=?-IrnCVM|0~PlYEJgC&d!P*-ZYw7|pxlMt4wW=N zvKS9Le$>-hGE8wT1O$M_+ZLKwXWqR_LCO;pr%El(pN;Km5 ziK3Ir85|o!D(Mu(QP~J{thLhMbiv>XOY;(4VGS4v{{Rn(SO8jYlou62@+TGCzkdb( z;QIld87*H6aScAoOE}T`U`PUDK2)JY_!mxG2F44t0VX<24_C-vfOSzaht*n5zDA?Y z7Zl){t;wGu3xGAiXB*Qv^2YP_pt^Do5D}}GKjkpbf*zHga z=yoULN(D-7wrrECf1+6ZGed3l(rMhN-1gb%r=vWu8lC@iZ}{6}=67jdGB;;Jv{zoI zG!JGz-&DEk+r}E&aLSoN+C)iM%vGt%>tfDxGani)JVwIT_N} z@Sb*Jq~tHVhZ12<jAn)8EmJ>RGxFH#p`tbHu(yk_HL#sF)17$BCUgq03%m7Tc)qXdwG2$pX3K0e^B{stjSzO=OT@H_B));4zn0a>y6aW4_>NQ#dJ8iIc%MC8SC&$X@M zx)IrpVSlumJ}|pfulX*L6dasj${w%}OdqLg<-1QeWfkV)@q44rxe!Hoj|_qX;CodwZaP=-G91Bu#zz3t)uPWua6XcNvUe42DicFT)@$B3^ zj@=;-LS~<{o>zZ_5^@Ik8xHv^Zch6xXPOlXNKN{q2M_5~HSCW%(WPrdk-(*`xZW+T z?TJUY-qDg7V*(|9?-S{>(JdS!%5vx?yS_JJ64f`h8Q8w%cWHN)Z zVW}mSR>4ywk)^KJut>JW^wQPnnjH->8crchWeJD;oK3@-6_Id0APg2`O9MnPhE*h+xA(YZz;<;Qp#V*OPRMpK=#W ziQ{sEuA0CUHG0_*Wu3pw*LV-CNszs#w*-{le8A#I@NR4MdRmVbVY|h1(1Flcxtl7O zei2gnZS^bRHv2f$Yif6J=2pM!cDsFA&NV$uERfIRFUu7#s}b*b47YHv{rir#z(Zaw z5Or_`%hmWh;&18?gb7CtBx~w7k`?ud#SsxAIa`P4zdnCx~d#-xW@YbEEO1f zg(@3B^L&HwRXiJ9kJ1p3r~FS200)^ygsOABMB47gUT2-9+u?NfW;y;T4uc- zwLl6vI@qHyoZDn*^{Yrqlys#5M4Y z)e*7Iz2|Djv)Btk#Uq4r7azbu*E$C*ifM>?4ECqY2@sK0-og><_Pj}*Mf`Bd9)43t z&#u?(oD8bSSeC2jkw(_IFbhpTd=sn zDdw!^8XE6C_w=3Epsw^+HMvBM1yocu`|>yRGyGTQz(tlj&;DqsSLl#Ny)N9hQ9F^j zXY6XpnbtKlSrH_=R@Kr*j4w;ktgu)we<}PvnER@fb2UoCD>;zB>rQhK^$_rfziQUQQv2lnKuutH1&#|sV z$Qhhjn~kL>qGJB7+F77P>x+c(i?nQ4=C;Z)-W;~KR{!&~hc^&EpIr4&AfBK<$Kw_R zh;^ayJ;$1dh`KH=$v(=M@-PZ3X{RX9uEFqctVUeTcSoO1_IsfOh615mBC`L!&obUW zsBS-n(`A1An?L=VGYP>#V4exXDT^th_~S#4SrEZlydk5u4%J!pPBZFyh{z?~N-GB7 zRJOPv=cEF}vX0}`lyH@v*$Q^|#dio{)4xZNBW*^cSj=WVX#jY#Tk@a?E~ou3miq$6 zS^?NGVzPz#xSWpiF9Gx%ct*knYSl1tlo!MapkRm`xEmBu%-8K^F%!X<26E|ar{mU7 z*SL=&esx}4FM^#zc^|`ojozm#-LQHiILbaP+tTU`%S3-rex0TKhVV0EmS3 z0$APk-eE!ipM-`D&`<>rB-HfdhA24#j50(<%fE zq-MTN^C3pSs;ILoaK7kX?~+smP8})m)<`*=c?mCC64Y9v5 z)&cIRuS7YGEVFtC0E0ZNV&U>MmjcfDIyr?Cqaa_Ax&-LTS&U~B`n1;~W2b>}G~up$ z>kU?OciJT{ZWQ8Lw!t7rKIo+sVuGEB6F0dZy_Y7FCgA}G;u32K%%=GmCrGZqgcF$} z3>Byg1@z>mJs_F`KEL>zD8PZ2l9A_y*j`6N1W7?Y1N*D|vOEUipkFzg(AG;;%|itT zWXNM_DAQ>e4-#(wCL9q+&k2XE9yv4W|4KMokic6xOF;DK$#+f&N-rmU^xlYo1)>0p zwj6Vi#XITXuI!CwV1%Ff)(pN(=5z>QUg1xe4M6X1MCt5$$P}YFFamj`&Y9O1-w0fJ z^nhktE7LA;^j+j<>fP-LZx_-l*h-5V!11RMAnDq131jWk+5{Tl5@0P@5|oYcT#W%K zTh}H)lQYD9;MfYmLLdJEw#dT=wddkq+QK5W3kNV%PB!M1=X;S!_P_K*_##?IDUrV2q zmM6;eb*G|pp?v0^c5DmXiZ;yO(qOxJ^vx$4)&w}3PCtrceVUIemjP8LgR!l1`~z60 z+SM0n_FWYmoVTtMxv>Usr{4q{>^_qcKG1>jx^-ESwZ}Fhf8?f~Ab8swvy!*i) zl{eR5Q1yFkme12LFKud_f;?Ced1s7jYdqoEOf&vmCdx_I_Gli5{t7gJ^be;)mC{Kr zxmp|jl!C9))HN3f(m-wadYM=8;jysP{v*C{0QgdG?5bo}1x${YHUVG(5+7 zMTCtk!IsaQrngrttHpv5gF4sDXDmQ3AVrL=pJ~?Zp2{RSV|94HIK^Ds`+)x%L-7mb=s zFqE!uQ^XvFf4n9@C>nA-j?k3_Gy9YxzHU2Gf;PFucgr9(yP}pX`AHCKU zhf24Hc;X+j8wIKK&$PrMl8&MaZ=&rwJ6-uz;0sC7jZkSgJoJqibCd}vHKn3q^N*d} z1d?D6bPV1l!RDlArlRu4Q*%CY-NUoh;D< zLI0_~$@LCB?bwxpu|kg7ST?1Rf5sD2A~=<>rLDLS|)#5%fUzFuib z%mSU0Y~%gunZ=)q2A4*R+r_)}(1(1t2i3!xu0XJTk$2RL*RCO=v-FC#K>+JEnE|G< z5$KAsD?RuRx25{Fkgz*^q7pMYe-+6LQL8c$d-hfRFv-<_XXcKIq?4?Uw<0zW$Y>ah z;0sz@kiFtdO>KUsRC5xubG-uSXMFKR3jG%c^i%Vq174ScM*QBdveZ9}BX4kMZ&as9 z1aX==D|R#h*Dd5YIQM`i?S1*X%c7dPXW-q@j5x~)Ho9EG17Q(8a@-H>2M`g%rp5{i zWh}wMm;qbA(icb4zpX3S=l8YS3vQ9vPDi7Nf<9j(B%NaM;5*?K zN&RPP-b(2MqF_f5(047HWbUuK9&ffu>;UAOL8&J%gdn{!}ookadYa4;Q2)BUlv3H=c$7Zfb|S5~0N5X}+>q#z}x5A`i%s0koy+MPF+th+a2AfNJE+?pRh% zW40rL^VR4dNb~ACny{0VR@&oRlqNNiLk}d~iw_|zmz@}z;lec`iO&lJM%*Vs3W{sn zP#)J2oLkBdx7>V+ZF6Pmzd$oGoBcryc-kOriO&`}tR(@`A*?Hr6KY9*ta#ihZdsLe z*zMXbXpJ|W)-Ug5?BRyGG=8R3dhFV-z(sXg0N%4>?HmcAsi^LH3$$XtI=Jw?A(%u> zM-;sf<3Kq)>U*XSBaKer5)OtvraSbs8m1AEqb^irX15p=^dj3(!4A(`lH_*N2P0`I zr~OS9i>h<-mg`871cD-jZe|%Vyjdm-HBI0`CZm>}$P#`uzE5w(PF>{dO%i=i113EM z)^BtFDSc^t2!!v58OC4G+T7aib)+pD6K&_Zwz#JlMK;&Cg6(j}-&?e3q=stMhg({p zv(JFK(`5H)^z`&XgHH{&V(VbpQW}9dzMk0)o0rQK5lHvx(oY;^9OmjYD8|{?LqaogUk#1* zgC87-EA|=Lo1}D#UA`o%_9om?>6;H${!R-C>Q)DdTd+h*y|hBDUYC1Q+TBRo;L0sX z*ot;0U_6cMgS|>S&mb3%kt?58#eI%TESEjF;JP~AsGj}#8i1B%3_&DPq+b6M1LvU4`04iI z!&$4(c#d?wN_n=i<;~l_6)Ej=3|nr|hQse@p!;DYx;6}lcE;!8*RF%Kbt>Wqg&13c z!>&`r{52E@@i@Dld`EG7;h<7l6K=JU8dYwx;H^-N^^m)ahHpPwX2N z1MphqZ_ZSWI@%N8*(ioxmoB+J)R_`3i@mq08@0E*!u3{DtXpcD)5|=aa9p4Me-B5Z zk2+`7Qb{lph2u9`1I|~Actd5c$Il}`?A zu&150C{*LoXf0X*w>F{FzV1gOFC)Y2kaqLgvVb+aauRtf`~>$ILm$s%@VksxTW)~o zmw!WwD(EH!!#S&)>5#e}C9>O~mu5q0yM6{WY5)?X6WY{ynsYF8m!fi@2d4v#;_8pV zi+%U~?hrRGF}WkL>%JC{$FZLz4MdQ9z}pdG=O4M-FKNy#UY`WT23Lfj`%oJlQBswfZY?;2Qqd&MGR&r3uzG{BwG-$ ziCTX6D;y3MpCYd`Z&cBlX*3#A`=Y6b+!UX*2+=xXzzo+bHdV9E>zc@D=f^7H7O)`p zg@b#_iM+T5vmh$eqj;ydGU3$K_U5!!S?7ycL%hh#!mA0+BbU_M^WB_*_$4m1*JqM- zws8t+Ys{0A`_&Tx<)zwEi;T7ZtHt~Uig){FWDO~)t;*clQ5T2=0uVB(-HX8n**%0{ z4FE-S9otz?uC9jI0bJX1`t1yJ6!w?a*KB6$pOUl1Ufh$8Et% zziF_QWl4d*b~O5L`}hQX{%XCz@Tx$aw|NKYO;BRB<#IP(D7%dTx`)XRPHHj?P%=LH zJJQo5=1EFzq!Yi>Nlt1=TER4m1#Ji^&3@!~rS$Mv4DouF>B(JJfu5t5WMCV94uT6( zT?!LG%k%1X4ckIiVuc?rXYg3RN4=OT;Bq`2BX6Y8Yw8ld4P?>5ZDLG2=goEaSnPb& zbRPWqQ+DKZWL0ZrmDuJ`(xRnO^N)sI-?^3 zv%&Zj;WZ6^$5mlw0CXTqLwd!*wy9BNrZQP&4$EOQ9gHytDE*(v%+&IsU>}e3f8lbz zA3s5Oliuxx%##iz6p~V#6M2ypJjcAMrphAAgX4wa87?=cnNg1r3V=0=#OQbmec}Fe zRiRX@sQ)tUg>F2U;{#E|BQLN4P4sJryZuIhHf9y>?)u?iAI@(Og{sgrXMOi>*-fo| zpQwxO7Sqiq)=)^W_@xIYvWM2Y2WD~bO3Q$%=xS^xX-1T^j_2fLAGur<<*ZGGHcaJt z3EZaL3zcANb;U7;rm?1n_mNE{iF?0DyIm~y+Y~#R@oXb(c+V?iQrm=}qg&sfy1zFs zgq6=opfraCiarys-Q^n+pEBkISlN~vn8y~@8;^6y`u~mzz`)-J-h0tZCiPw0pd*{< zMO(m~&T2Lj1n)Xs{E!t*qb?dv5OT_fC6qZ=xj+7%n-`s#RN}f-5@rfsi<+7=n>_Ld z-+g~L5e6?Bh)1IT#-qY&-6w=hgpVXQC*8*9+&ukv`B%%lKahuYaV-_;yr3L_)<@vr z_iIxE=fhe<(YS#liA&(Qgrt&jr@T3=*o!=u)x$AL)`jzwEZ6DG zuUMAzDmtZ7DQjWi4P!saQTKrK=@HlZ9O@bXm~>%NPx;Z#_^LMedw_Z?k`y#;S#D5d z+??^kUGPue+FNo6uEZ)eb*2htly#u2?DFF+P$(D*4O)Q0nYsq~P&B2IMWxXw17UYc zW@C@4Rp?1&h%4Ue=`$l>2-2?a!9WX+P%(X%Q-AE# z_(rb*$do?^<8c1GTL#~AK4@wNb$wzNGc{aUc!jc)oyKG%3I}W^zT^<{({b|e9nA>q_KQxt9*U)Z6td1~* zRWTQ=CYcPUiCqhWx9xO}^%hBizjsgxAX&*}7c#_u73DmN*@tp`zt@Ad_WxX_b(pwU zJnvqxI3GRky?ayx%Tvtch(>kebYT)9K7?MCMqdMHG*xYPP>g~t6FU_RbZieRUJk*b z3)7PE9dUU@@_9_iT8qd}jo%+{V#J8NP0_ortvX1DC-P{b+Cjr0hrbKPMt`2N9>($UC$2(3-*a^`gW(C)p@@g7JYaM|eUn74e!ii0m+{~hbHh7NXcnWwux zJ-X^mU1D-TFb6j2C(r3*1joYGdXGIp6Dk$_KA3md6vH5$DNw9s@>BJBHBn+dKSTi} zX(mTYizc&W!X6?q$DpnL4fZ)mb-})FrRgx}k50H9=GtrPWZW|=e7MK|#i2JC@fi%J zg14Z${TJ-Gax)`-lB;QY*Y#zpI}OCbOH9pInvw-?eyC#}G#)vhX;tFjFr#Z8so=3( zBAlv&K)+;jwEXJLkZqjU&wV-LwU?8^CE0aNX__G-3AVV`*?J9Aa5}QB>~0^|_P%Xt z;#iKRzpQYa@s}>T9V4aza9>YrTDS!jIOw{alF{B@bsRhT{vWUv)wh#U)N66w)?zH>aD1YsFEdnNAlvZw4p@%D2HRkSqOi|BB93#?x3Z zmW_$S=-kuT@DDtw?3I8$kpo|@o|ut;(eN?1f<4C$^O zZn|j#Z1dO4L-6R?W7TCec&eSam_VR3V8mG;id=iN=&Za5fb4rd7>}WF!@67uzEu@= z`?!Y=crL$ri^A*J6xobkBSHJ8Jl!p02o2ca7)^zjW*1Pyu$aCRTqhYWjYituXZ?DF zhGbT#a>jXNs#-LCOCgw<@=u6CPC`qWATh$#0Tr!S6dzxH>|AmN(f>2R;LpSK?T{sfUW{o?59M;rVM4B}z=SF@?J z7$r^DfOi(3YR+O_fqr`pBMO|}=(4{kHh;fbK^18nU*uJld4gz$Fmsu;tUt52Mzfn2 z>!?;XLV~e9lBqU;%&xchzL#G45bB1vC7G7j)3@tVS_@+?3M7v?j?x6Gk@Ycm=NX@n z$Fa{?mkQf&C2mfmRBX_gs&%LVUu4{N#kq0#A_JJ3N07!^FIcvltpQg}{|2icQ=Y8X zlM`#ujDY<6+yT*({kiuoswqHjVNSu$6`agZ(5IjC1$-#-l}phiEO;GH*%C;gq&O01 zncgS25)-p<@>`1~uy7~DL)nj@08a!S((Hvy#LKU>r(so5b7ms1PX@sJ##rO@wUF=3 z0=YtdA%*hq-*wx`&V#*?Fv)R2CCzjrZu+(!eV=tVk>!$LS?o@d1#E~}sgZoyrdar2ST~?$@W=s5aeCp{_anTjVE; zJkaCj>jFKkZycAA42Lb;(SyhoO?JQ5#ePAP)G}{UF^#D}FYG4YG7xQ(Ea{5I&_J0g z2&OJrVI1DLtp;{yPOLOwX4K8ecg zE(fdCW2?Um{uus(>NK_%1f!E0E4s z^GdD=Ve8`7V35HHQ9`zc+zedMt70-98Uo8)Q-o|fP_>G6G=^*$xs*hGmPlj^85lz< zd#!tUf8@SQ#(4EGo46*3e8I|#w5M^DB7oUnYh&13H)oR>RfHLAQ$|;!1*Gg|o!dZA zu~5x>+OsaCWjgGNCXr{c)DS>bu4cdkokqSVw)e&Ki5<%NUAi+X6)3WiVd~jv|qC?-5BPiz?r^ zl6NggC9=fbjZ2SnZ@qwm;S$$vHx>GbwJRm!#~fL(2qA$1gjzD7qek{hG-0x&nPVF> zHX>lh9RJ{5b|#>k1hCTqq2s!%kK0)^WqNqO4p0iNG)f2k28E)1tj-(gfz>!3NOhe?Qq;ABDmnyZfIu#5K-~fx0ELrg z7kh7+R(_IjjU9W+H@iW_&IZ!~iJp=d>=SGZu2g@2vDIEnJe^PL6zHz;L-nu+!T)J! z=aQ|rxAKxd>n;`aV`Hoa$~%3V5Ll#pess|`e0QEbiM=uSO`3}1wHqEb>I52Hy5^>h zuVoj`6n4>V5G2mbm&lc-_6s8NY@0zZ`@qm7TB@E}D=tGznTb;QH;OBl3F@jHvE=Dh zegFTV?JT3J-r9Drgp{<>jetmZEgA%*LAtv;q>%>clt#Lxr5h<}kQOAQ8|ixIa_{}b z8Sfb9%Q>GMgRwV@wbp;mdC&X4uHQxY#uv(jE-K9Q2p*`^PnFMv#4ZTVGp_&05vMz_ zib=WZ{v~hQ#(S%9L;CKQ4Wrk<0`(iArR+g1GfsPbXDiTU39y-Iis}S>&D-A_RVm3K z9wGrx*#|mw+I=$GR7{=^O<3VZ_COMbFEfy8AI4o!Hq=T3`tqEIk`Jxp$EfYHRh$H{7~7ccFuKfJVsa(nGe%cPWc>B*K` zavvK039=E=a6O0!Ag>X9*nBPy$WAe#A!POv1ZYp` zko2`d7Xav|ezWZi)Q?ZFlkDwWr98= zpr5?^#0tm5ET8H1+b8emu)%utA!MV>o>?M=@0~!qL1k~b4)e)``c?NccOg$4`+7%p zgk<&slS0DEcoL2od-n|1gqBZz41qs2^>5f|%4a-!5cnKD5J=XX>< zDWEPn-CVoIzI=?^+V_#eO7(ifIiMcn6c7IDBOV5#>5!QUQ|r6nbA>!f-HW!9N`tKU zFV(iXrV)(U!mZ!5*4Tw(JQ%d@`{---BnTrt8uL z*v<|9NSQq2C%Z(U%HU865+z=-S3sNuIzl<*wHeWnRd(;L8j+yETb*jck7P36OE@eR zwS)SSxy@HbgW{+q{mITd6S%oC9|ES@=~$chc&CbyfY5_T z8LyAWVg3apT00(+kKAgnTC&gr!xED0Exp=jB3iubpm56#lC6`F+(=Ct=~f`O+G-=8CD(pEnkIBX^t2vAV7) z-6TWMNpl(%IEWY5E*dv=SAASWF*c04uNb%MRPFOR5*>GbUvx&@81+pe<~?xzk>DPJ zHf-U5yj9k0CmbM2wEieWi1f?yu*9f3(lcg~*<;dfC<2rgp@5xddl$o850vGm6@Sdt!Ljn-mJ5a>9O+upU?q@|& z^b6%|T57kqosT;EsDoW)Q>SR^Djf`)X>asvKt>c#A7|*eJ*98I_u#fLxUJ}e zlM9(SskTbz)dn)H&^?|+IYc!BCa-6H981_wd)pp-;9|M8^AJj-DVMZb&=FBW2J+f^ z`Kj*#GoveifrlZDPb3DnZJj7|15#4}c}CN=9?_z&MMR2}i}I!Yz4qp?)=+?ZUP#-M zf(quwkTMeATVZM>BSr4?o))kr8~xb->^-o+7^VDR-%sd{N`!X@wpzAi*GRYQ2J=Kv z#ebpL04;A}@8hpC+BS3y3FB{uMEx?n%rHhu9{hZn8O=r(z5H<<%5gAPq7Op^$Rlo~ z;$JVMZgx9a=dIM@1G5F?t)<`3_b2kuJ=xC}DqC5DeFhX5xWB->CmYAE}i-x}TtaEvfd1SfCh08Ml@#4q3<%WPuIsi~l<2qb# z>;g)|eC4Y{zNlR^^QJNPRd#_P4)ayAjaXvd+Km98-j^anY&qmJV1N=L^a%`5Bu6nn z5nJD0#5S3Q=6oErpGyIeHjXIU07PV%Qd(liz353=om= z`BUVxU8h-g|7q@+a^^yo)S3w-yi@V{Z%KDuJ;=Z7zJ5r2>$2NxuV76;CGW#kzH;A1 znER4uDA#Ze{e?&j3v(7LZ?brxyI2H=>SygPgd}H(`9nwjquma{`>kqc!-#OJ!en4J zY1}6BINh*GiPx&{@k(D!lxma(9YxfA`JIcQ1jto!1$m3EqH~=o^ti`kpy!PiW zABmXoUYok5roaYMy-7H?80370Pf({Yms0J5MXl~`QFdzpjdv7*tp|f+POGpw!IzI7 z;gcK|rBr^Xlv{?aDZ}6bLKHGX5o~}O)ZagrtD5uEChw9_DQQFqo;)}#U zY9oiP37f5m3a1`Xq0fR4SOc65IrDp6;zKCq{O~mG3OmKSc4vf=&7Dm={nWEs-MYwa z9IyQ|RIcacttu2_vWFeq?iDyJ{ z0LIj;$8c-IP*H4el|lMb#D~i-f;W4UW%lqZRi;Nj-sMUW>Iy``PJD1pt##a)l5%;t z<2R4^$+{ML2q2=v+cBa(N(ZNzXe1{U_ahYKr7oWQwgJ;yfR$Z6guZ5FZq_TDJf3@J zHHJ<#3{5vq=HWbl8vT`Jqi1#%Aao#K;66S^5RdrmbJtsj`B%mx^Oulb$Po>-9MP~5 zwmE9An-5^!w9shPnoGs~;4gdZu-x`T_w;#QK-HINXB3;3*FhL=B&duDu3EriNI~1w zW~RCbJH<4``NSz(@S){BdfE<0EtmnsZnP}p3y^QXU*L#q9=ue!IVT>VSXWyl1(y!k zvRTA?b6iS^s9$Y>MmDe`_}~t+vIYg)i1~ zZVtvEk{vjeqTML0Bh1zs_|dur+%GTZGLalv1$>R@OwtE-jm6HQv?z*#zln52{4R}IUbl*Y7M`#QcOl3 zkL8%k@x@%g1k_8AxTn{xJ%6e2US4wbZ4c-w4G<83snoZ1>E>ukflp%{z%OQ_`Ex)n zAk^1f@$^M&bNhJCvNLN)bAPUzU&1h@;d`$8^>j`Ni^O_M(vRm5&4y$9TKmTg?h-T6 zpi%6OmQG2v=?{Mt^KPyPJSSp`M|-u6jC{ z?{iJKU9yyx=|Zbu4s1C?k`vVY2VpPYk*t=xul1fJa_;Cv$=spt9Ta1(HX&Kc5C5D3 zpr!FaYTaTYPf-^~_>qAI@)Ink*-DWi6dayiHzcW{-SOOyo-8ev-%lVH zi`Y4I*?r$5t{7KQP$by1srwUEJ^|oA=QzHF)q|O|EKb%%? zFsc0~_0Y6%fKA=gPh$?$)1GjTrNEMWy@#;>ELA92_6rIBftd}}YyVW1>S=4g$OZuL z_OPx7Z5>5Ech+)_*d(l6tMMFE3+->CxuzBK6%kWPxm4dqX(jwIN@?EZa863cKtzrsXZ_Oy_N@bnzHJ6HP7VTm;gp;_zupV zExyiquH4w_f`01!S;VbKd7(~bXDuNHJuvr6Td7HG@vX`n!){~&vx-5fCYf2(Hh z@A~2U@Cc~$4p}fuY=7+Nt}*|%FzS=bbGLw%`(nS+;&buNVBdCYOnwe|8jPT1rdI?U zt%Ntkwn%%^pxRK4bf@9cy7mgaR$p*l%D$3!dGk;tapbT@jjd7s_@?z_5G9Y}&@*~7 z)eUA8=xrL&xy;M`{E~OhB)K~XWyN~c2@wc}K3hzxianvcL239j?G9{X$Xa((_r1%>$-!bLF2s&>x-IXu0&-a*zPo^}xyp!!MbK!mVH`Bp zpJpd)WSY z{#2BH#-z@&ZZ;41pseZMl#bLQx66mwfj! zyq!6_AGBjpl#Pf%3s`<$1rk>Hx+P-9uLZ$WgFM5Wa>Ii4eFhGR?-}aGowVc?UKk2= zA|}r57g1Zlqa6~EZ00rNzm2Vk#%BhqxLu2nSyhaFwS>*T_inRepA+N(AmOlS(thn8 z5PpX}(^}k&jZkSmMbvM=G|YVbv{8l~5sr6*P3@`E98gb2mCpm(dTj421_+v@Q21Dh z%C3e1Dk4b$f}rf07;Z7n;OkEo74XBS!oea7^a^B!@>G2NdRAmzycew(1r)O1l#|~h zuzhHB{6+B#Z*+2Ym?Qe&M>8F!)yLFu8~2JZT&ReDRU(^aDx<{Si*ixNiE1Sg2gZdAnCaPbqxTrWRlX!#2cGRO zQWYiw_k>FVAQdq#a2}{z-csFnu3tZVPri+U*uPBRTr`@m&D!S&5kpnMQ^iXU>;H$&O?Ekmpi z6_H~~aNP|AgA&33itI4#!YWqUvIU(c=dfL^N`3OUNa@`#7^nW8@DvR1jEYG-_oS4T z&Pv9^*|$-1yC)7kE>~WYUS}{Hlz9N*K}%*g>>61!}RUd+ZzV6Ay$J(t*ilG+|<^dJUD|5*Tp8f#+!F>+k48%MNG5 zg0ehv#w&ghQa1E8T(`{G*Q6_~#2TNq@@jse{$6!+lmvM`u)Mux-Q6BBG(Di3p{f_l zokn$_aggCedhz5`%_F(4*Z6rj6*rRWUh-y`pzm?y=vf$hGOFKzRw?RBBe%vshTDz^ zu+lAjaKb#Hx4`d#FE;AHE{sZgz?wVpNtgj$b|bYiDLK@=?FA4&D4hYEzI%!+%XE!g z#!iyd&z}0Lo3gc(I!ttuTS$iao#2vWR9Nx;bPostkv9LTjL~3(f#^Km4s;WFHbqb3 zNlZs-r;Ex5*Z`oA#s*2cwX)~&ClDsC@0*Mt*eKipMxCrx@PxKTyCF*H>p5Ku;E-!$-~qN$m13r&#oGwU$~b1 zv4x3~E=aXM)bmX?)fydLfOVYXU{vTj7@n{OmSn6Y&<6%>BXlx`H55#OPWby7nAL1& z4jsv^X@K7}@Rphy4u<$NWtY=@O#f_%ZM2Gd9fyHH&V3579esOuJKX&>q8iJ=!=gLkh6rc*icaYO+j+$n~5FnR8DG^G45~h|6VB_*)<-OMUz%3W0J6aB5 zKkh0!)(!CnX(HbN%wq)FS06%CJ>ayqx0nGVYaR=(r4C^sp>#oCT3xn=rC*JHOVCBl_6IV^Oh;@DWg15O^oI-XTK8IM+Wvg(e{3ek;x{>)S8-GDZohzPi z2ZX8i)9ONyduJH7#DJxan#Unp%y|V5(X-vSSoVY&)GHI(vKqI?RB6t3D;#iWQE;9C8OEV}-mj<=gM;e{>5AC=$dJY# z!^td(zucF-T^cT#jZm!8W(lW&igBRqj2(1e?AxWss*z=5)UEdCCQl#Leh#V7yYog& zNXC_Xuv=sT+bWaA3x6{xXM-arWERI6bh~nMdd9{Q==3Z=f@nB<-q$Ce_|fNUt7DQ{ z*7F=tH>~7|=7eF}xjDWlDhkuWQIa{f)#$>-zFSSI?`LxOHmbLM*j$kbTFpc@FEp%* zj&Mmwl?z%z5PkUPAn2TvmBrM58s~mO&?XIRB%uuOGrdZ&XWYQe_+GGmf+v zJuvJ6vrY*nf)CZ97l+ zkdt`w5Oa-r?6`qz!~7akdl}g<9Q=1+Imjb%?-Boy!sCQyW2ma~kN94Vr$-bjzUPGT zW4!KuYjph^Uy^cAi6UEL6qDWk38CezIvXD)i{1I&wrvh`Ev|4lCVPzVT2RJhDuezA zvT1qk>BkD{V0|OE)t+(Q)SH?3WUk0fWw#;@&qmnmhRO*^EJRb#?aW1ug`@|xql);R zeg8VOQr53Ki&(5xpUo$4oq4#_stk0dok~X8g5Vow{VmZXnsFP1B(6(9*ZdCmYWFvj zZc7J&&d<#r_!AQKfmfi30#sZp7nFc6_(i3ba)YPX?h|xn(j5Hw^3fA0Mf<&UrF&ly z^|UlV0(P+jV)e8$JHJmJh$DUZF`I98&ZJyGWMN+1H{T2-y94JjmSDuI0>ti90M8Lx zd(0L)q(h)}8Z)5%JBUmpi{Dezk3FiYIRmI^iCI@khyNVH)A_y+emlcl%LZ{e%WeE& zM?WWC8$Zph_?AE`iZGg)CyIJe48&xW(=C9AUkY`g_GA43_9}i+)7*>rluk|F5Bn9I zHi*M!-%C`E#>NUMIcygbY7?SiXyY~vn%GrHc* z)3qKTds@kn`5Q6KzgKh>cmzo;ejVh2Bm(Y~N|M#bA2CiLKM{ARK_5F?;Qo?r{$W=x zWL>TVfn-bCSp8%17HD`VlAa?G<9w5L>XL=pd!4sd>^}Da8{3$s1U63mP*s{a?-{*6Z5JhAA z?&szr2WLf6@83vS+5y);fcO%dBVRt2B7ZQpG*ruTX?UT2YI(-4;eJc>;i8?hI&qBD|*xhK!K0u2+AHQ5G!26ZO<8+EnkB&x@S>oqGcNf9^?(F0Di{(CxQKbB`&ff3GLp_jZV+}&nh4Nf=&f8e? zFOuapbaoI~qN!d!z(K{QLO6GI#ic;{8Hb-1Ym2RHI>qkZ`$0Rf^1KJ~u~@@5kjAaOSOVEyauhS6m817^DQMLOy{wu;vGkghvOjN-Lt)UJwmx zw0VgqGU*9N1)r%6QDw^xZrx&^iu^gI2UnRQsBz#aLAK!b@4ME**NZUT=wnA+;<-==Z%e|59lX&MZ?1ol8`EdqWiVrzy>P5y* zUW|S&<0Za*{YGG{`bi|DEwuYJTjdJac$YK%6ZMMD*ay4Zh$FNkj=gn0zEn&N!*gT$ zNUx#4U}niSgLttP9q*QDAcMP1ZH@^Np2}w~zT%0#n84XtE+m*4nEdq5Wxt{RVoU%Ns5^&g2_y*|n&I|&za^ObY=KwO5 zk^~nk$QR_&yIV{q5%P^~2yj?`@z~1lQ%@s3Kg^~&0-~+aae@H3?q$2>HrOkBkXA+a zymlicLwC8JjW7C$j4q%yq8lIFtc~Qlb_Kv{mVvobHIMrV7(7z|?!En9-b6siKtN~; zU~hN>L+?KU8K5MZD9G(f5T0Cq~C*lpO%+2&sdnKh^GPTY1pf2sc24iMh<%v%vZX(>=(YYdYyOxF}`uh zCT&Lfeub{2$a|<5UyA9VnedH9KxFBf2Ii?kFF2CrfDtNq7#aA@KOSbA2(fQGCU*EU zQUN#-08&#F568JEc7a8_rW~EcJOv&DdE=}m$O9f*uWkU%kRmy%`L}>+$o}mwLV6bY zK3H$oxcQA9kF1@k_xH@o2qCR@?YAzpvZWI1LR!yV|xWDpf=I{e=40YC+I>H+Gryn=;a`^NKOZ_rYPM(Jcc?G6aW#S8G-xlFg) zUO$93bq582Tbd`bs!#UxBx>+B1zuxvTxARV2ARtH-_>#bJ#A(D;Axdeq*d-z!j6{% z?(qpRg)jru8KC)|TGDM789MbBgJ3;|@r$@}b^-2~K{%9JmYBibXwp(r__Jo)AEpFG z>rm02nD^D9 zHCHE|;~s>!#nj3jjzx=J?=Q77SzRwSIPhD9ol=J+&?;xL-+!l?T>7{rw1oGqMIRZ* zNG{?6poGlvd$;hz*j=u{;!wrw11!00mM!Cbo2ydv$3hFg!CBaE01)>DV0wOV#y__4 zjz*SX-(;f&7#6&kfAzYZsT@)`&UuJA_i@AKnm9EogK_bWd=zd!O&&14%1B8k>GcK#$;c1+Uj7#xQ=V zM(^Z0_iraqFjhXz<8yP~qalG^@*aR_}4 z7lA!|`7#T*8p0hxluf4Fs>RvuDU*{4zYZH$~C5J@DBwyDEmdlf;2r$j=esj_n-Z!30{B(agK=U<5jxrZ)>$<8m9Jw3$;f{4JvlyLs64BBtTefi)4H*dQ)!2*1cZ^5R_ z;UqRF3MqfS{j*ar`H_u`v>v5wGDjf3R{t0X2pUDhKaMtqOOLv8Cf2OC&276}C7k|w zGa)^SMeAPc^K8lv^btcNxZ*B8Tg`kf0bcDLt_&~X`G+O=MYM84ry= zM)vIqx5qkFaEtK%$Z(TbCl9Wp%{$Yj!hJY5PtLMNq- zDnGe)m)$6tq*`gwkjeH~N##ONaqxA$WU{gje)5(t#0Afs8$Nrv=&?!~2N&s*#mwh$ zutV~t%n+^QzyAbO_Uuao+2;p8WLY(hKbrVoAE1&ogD1KnI{V$ z#GK*?rj}k81M1ZXtIa--G zR_#C=#2|a+&FbISi2wdaM5Kdz{3$JYNNiB@CdSKAkB30&b;J@610W+(8`gXt ziK^hJ5g+Q_?t@23Dgq>oYAJsK@1_ODt@lER6y%FW(Wo!tAvKV}zYm)^aM*N!-yyaV zvwXHY0|D3UV~FAw8POFH4xM{PWj}NFuaD!wTjs>h9z=H0plb z)46?jdHC15)3c$&dU}k>7acoyGlYGrhh+ALvzUZa8VxTuhTlOCpXnwSgz0oQUEgF%z%0WUP&)n43*3EFiS@RRnh$^KGK!G_H60L@O0~?BZ+&rLvUc_vjI|Qu(z1gls-P$P7#l3}J}vxzgx+oamt4orwmO$Trex zO)^Y`4I+c-@vJY@9~dyH;ajQ z9OG2K(C%y(l3qxn8KavU2Dyt7xv}xeo+NYGpnBN@d}#Nm|E*E<0LQrOFnYtD6a&~CdR#?7uebGsk)HezW9?|S=4W`K=Y5rmK_ zfD|qTI@JbA5yVeYKS56~x2h(*DAbM?#Q2Rcn-0Ar+5Y*&=t(kjeODiyJ&YUNgzfNG zCy^o1bo=@$Fe#1I)+QjLQ)3h*U>Z#hr@rvP2(4X2KqZjD5y?~rCiUF=nj|m_OH~UC zM!QSc=-}@!=~v}VnCibHda>3O6dr>#KRN}B3j61M@Jl?&EU%k)fu6M$;oFZ<@3Yeb zp#qe`L#*C_m$+Tfh%#^s*JI0Hzg6`m9Xgw)#jCsJA>Sa+Y=o+r5Wy6ZuKhnbRe$5E z5MhL&4x1+#l8-Qhl5roYqxttDu%NF;lxftI09X(B1A)uRaiB6~ra+dBHN}3qG%wZ7 z70?pqnTrn3v^}+3++yo=wr+%c>fE372x|nRKcvg;0GdG$77Hb6n%ee^-J{sI+P^Z` zEm{a5mx8()Th&iQDW)7iM#}(-iEh}#uEW`m3VQh?-b^M#T`>BySTl?TeWQrKEUSN} zB+$j!KFw}0{mB$QN$@0sm<8R!AhzA0dt zOVK3b%u$M!MW58E#8P}+{0dG$^f34ZaOERCdHN;OjTG944$;y-!A*sagi94#U!^Mt zFqK$~TGd6p&UBvntc+%t-7gEzxI<~l}-FCFvh7Vi(FaP#0aH^9+ZGgEkppIU0V0oyDOj!!Tj>QL%0H%-J7$FP!X3=Wjp)} z){+<&ioi55ar2JM@BL4Q1MT!ijh?w$3k1^lp^=ePFgExX$uLNOQ>P=F!5hACFqAU+ zJ%+sEh*uaO#4OP6{dXn{NjK-(yrtjtY2}tR+Xth_wjgq`po=AeR@OL|VU;HXY~bd> z56=?dLXO@Y%ax$Hj;dfzpuYU};d$wV<{Q)rbC=;b=ir26n-5_YgULHp9L8|Ul^|Hw za1lJH!fQ?22mY5}+mNl1jLiY1qGMUg0Z7!|0h*fI$oID>&tJW9{5%js0!CL~*DTg9 zx^83TvaSDaOIC!?f(7xY*(_>gzg=Y?_CSu zr|quo=BY9bX%54lmj|0WQ^mbFuVtlp+P*I%h&_rWfD_x}G6vxt{AW{a)-ZT?=;^?! zEct*Asr}1I|2JyvzfWoZ*U(d%H}(+)G7HnAN+P((XDp+7(c03f{wFcE6EM0i`PKI)KO&ljQO+thK)_Rys8$xfQD9^S;hI zu99ekklcF0dbQoyaxcS&rvQn#xNkCTKry4WVV76MQqgZUJ)PIh_OnwmSpu*K-NB5U zfY`>#u_7d--Q-NJx9dx*ywshR9Yanu_eQoU+d<{UEJX=CdGxr++QImWlq^g!(DhGM>!omk!!Tbpz^Cu; z@Dp@7lViee_7}L=8c}9UtCK3KZ#;r)BMz%4p}LD0Tv#FwrAL~Sm-C+KK zxJk!Qu86sE%Twbj9}^DB*~9zHpEv$h%%LRn!cm}{R$A7)M`jtt!a0s`giDXOQV2?6 z+r;4cT#`GVJgf|u!fA+x*cMM+J`7)&jHCzkAxSjtJJ*E{~Q%NwsDGYdJ3 zpfi4hc^5>&C7X3n&JY8OaK!|)p~wVED+RJ?lBsWu7?iA#p z!=IJ;*9_;i=06a?|J%w$2l>+p*>eYgu9Bx=%7FQ`AZ|K*UqHGr%uE1wT$IgVSkJ9= zQysE1R}*bH_6aj%l~Ii51>1@L8K7WG#`m%c;5YfU?*h@gm(H~Li;e7-!*;=h-4HAC zjmPkRdNuFk#eK{9w`Z%m^LTb?+R3gcQ}msx-yI@wXMliN?3KMhwNxI*btRK;sz2fF zFrtN={hy43O`!;zCdxf11l%Y^sX%(Y^=E(!h^>uSH(f>KI~g$MiuY!n!|Uaz8WWg1Rl7<&^W z+ZDE^hPpuY@O`!_KBePPmtgM`kZW1MUO0*87*+oI2HINW*aZh}SnV#qRSFnQnS7^= zhGYxf2YhJ(kr$@~T_Dj_9zYS7Sferu|143pW>;dsewV*?owko}7_TSdK0?6UEI9H& z=#5%&-L&uK_+qOY=exkW?^OF#o>XqDOhw;2_q~&N zSE~@2iF4dWnshG?mcw{?0VUB8ABx;e3jw~rl_hYADvn=3tsP5d>gPno$k>krfvVzPWKmjMh@QJORU$) z;f~ARXR|W<+mkc{)g_*zO0tT+YK~l~0158sVQfJPK%JXU$ik{1gp-?+Uzibh} zp6O`;Qx$d4OY#)xesVJgea2e!6%*Y4*JsQYygk`^GdQL=UKS?#Oo~@V!Sp5CBO)a| z{M(eTM__5~E|xz|O~)fl1%+2RuX^~x<>NHwP3B37qLd*JnN&Lda{QhvVJ2Zf74zq> zmqL21@mv1QL_*Vx*Kh}*h?<^=y$w>j;8oPL_3? zxmUY_PA#v0hwT9NpQ5fKZRX!!o}mbsf`v(G`hDb3E*L6N$j;dTyMOHT4+cLP`;Z>V zOh_X@@vwsMyEbpOm9uwmXSC;S-iFf{24Oc!Jk@MRLSY$Y62tmG-@{*_0rMCbhPvru z)usdD^9R+%HD=MpY-Uk?{D`Z_>n+aPtj8jTO`gid>jQBJu^-+gXw};l3s&6a-;=X7 z0&AYYxaHvK=akF8FaERSV4C9EAKFfaA&AIsHJys4yG~W^UO#obCNUSW53J*>G)@EJ zP$LaAFF_c*uXhne`8iUEk1gt@B$FS%6<;TLq;7!r3h>EbUkxDrwSxcER`~P0iIF@e z-}q`k1o)|P9I~muU%BkmUUSm7gBiUln35n%YsQ8C{mK3Q1B+*(q|N^82hd0o0E?#p zi$MW1;nH8|mJ)D_?^&XQiNp`a$%6V5JHfMEU`-iPllPy2C4c>Y{vE^te+E;DXwz`w zU#uHdrpw&>opWU%13i~ImMiH7;Lri5`T=wr5U(AN4)u26ZdCR8= zeQCvTrme2<#eiT&MKQn$1p_a|P#I*}|Hw-D`=y1P|1e7=q(DD~^rbY(-z)FmYvVsZ zBmKzt5m#jnn0mrwshBaYgu@eI+H+zDvE7^mN}PXQh2RB+$)7LbzaI*+*uP#y1?*n` z4_>7U%N-zOzGKTn%E5`34F(F&oNWOntjKb@h3V5J$V*q;oi0axDJKFim-s4Cv&Aic z5Qrx{_Nx22pzjDJz{JCp zOEjD$I^R+%lxs4mWQ&-%++HejfXXNu5!{_}2b0P_0gPnXMB(TsP`i_Qn2nTr-?)~7 zzn~OkBs6v}dtdx0Q7$9_Eu!DQM@E!=Z>yH?h6coeJdy{U#sJfFZ}xBs%rjd2mtAIb zN-V#pYSTc7cuweCc2%``S}C}XfS*4DG;tyZ0jT05W&?<)w5jub5U2x@q#*FJbC|B$ z?cZOKP@E{Z*G5DSBC#+DC?rAt-eKafpeI#A9<9rNVYUeXW?N&8qRn539s10q=3_Tj z1I%`a4SSw705ewiw15bhV)5_|nwLx8OK0AKiSb*qU?}^wpTQ5)Eh0#Mrw1}sClznH zb92~N1VqK9KwSwfiV4Z>^=`VpOAbxG$X_3bZ(dtn1za}EpB-&tnzzH?f|sYg9Vod$md{Eo7pr6jZzSlFi`Bcjw9hz2fsPM7qxSuA_l zvJ)g_7!N3cfR)kjAeHF368=H!g>HVB2yZrE@|J?}h3p-m!m5;O!td|j-vJ|NXsGjo zcNQMw>*!3Oz}(lCfI7Yde&zYAYC&yKP^=GN+sX*h`#-7thi{e$2-MV@AS@wK>OF6K zj(B)6D0PeVd^GT|SkSkxN<&7Jebx~CK;(4kP)bb^xTIr|JJs7lD@h@u@SL+W((cz(h|AVE8jHfexU7|5COn!W!xW_{54U+DQhClYo?i z4&!Ur{u4AW_161qEg7ga)4U;GKIS*HkyyY-!Fh#<@a*=XM1MTiB?_iWITI zfSLQ;<9troa#d`G0|Ho{HWPUl3hE#RbjN@`URwgk4~Q9xUPq?Z;IxEc`yP$i0y%>Z z#cbWm1>iLx$&9ciQF~wiE^*q-EEBf{IzN(qJkyUr?DK7#;vfiRGg2|2Ex5Po;Kxm+ zYRlK~&G_O)?}J)!LCgiJ=Fy%8usvb<+0uPPf9y}SVbUKHHnG)d0dAQw^NS?!d|Yv} zf0}+QxcrZWuqyFqTbW<9=xJfMn3%+4u~E-%1*u$-(M&-cvQkhOFxYFzJTPO#AGQ)B z6aN0epHslk9$F;YlhbY^!W@UZuU)w8=1q0PYLtj)zt3tdeP2^zzI>`C{MbTaJKtbC z-)UR3DJiM<5vI66fP9sLuoxU>Ox!_!vj+I-0h&e3(M|iPXU4b2%}l4m#=1ovQ=j%9 z%JHK^JnvRhHqU%@Yj>$e;aTKGxXs5Nv6zoV%jCsgz__Gy+I#^?zUk^s&MdL5OU`dh zx5ie)5K9N&(nkY9oerb}2VrNQw?1>f>GXTnEHV?|~Nt>>|T1NBr0p*oI#!dJdT zwOnq;iCF5cl$p*egWvNtP#A_vhIdQG?lvv)#Ruf%)_(wNDn?^LQ7q|Zsxz8T`v>MJ z#@tB8jo5r`MVR0{9_97#D|Nv0Q}w#PQlkI?WM~!x;n!zY%E|j)v@FF6*$LcE8|=l` ztu(2eHX~O*zSxADFE)_e1mq-_I`%hwXE{O?6Pu!mufPwA-O&ldGJ%t6&~30+rPt=3 zVP*rD5G(-A`~W1%l{uNy%Ercos#6Q?zV)-Uw3;{lyQk?qr=dqjuWE%nBxC^5O85+G z!^j}+-OV{edKM5=D5qJCSh!0M(J2@BBcd{p$z?+1HrjEu2J0&tQmBA%U73~cBK6Vu zG0-zn(3iorYH4gQ9DjHZvjv(fU!#b%zJjnh4dGh-PUct!Ei{lh>PRiT#`he%Mh9q` zM`*(Wk@N}3s{GI_xsk?mK76e)BlYuqgo>nb%H!rW7b3Je}8KoBPy!|^8dX*B! zazapGy5v>-f28jsKbC-dn>C7w?vjrYih@{l!O1uk_i$RY^W!1;iE_mJS={D_RMiG!&9S$XgepNquIuS{{6u zC}4Fl#XqdqEWZn(gJ_a(boSUUwPcHVq|$qwdFXY#VD1kGT^4E>H4jF>@$%f;KHtAd zY4yU2FOVfvXmHK3{qe-r7ewOV{Ag-5Y3HO1`4Pa2#0?XMMTG@eX+NET*h6N$Wev?D zI7IC^kJAPY02yF_TFljrnhBi1bRc4L=S~ni2Ok*va23qwxlar z1BF9SbnVF%n(L=(VD%r#v72KI__UC3v>tV#` zeE}lvyMsAkHeBLgxU1-Q5}O3`=x}4;Y!0P#76gI|;J3gQFxS8yKQQr$I>g?A0$P`G zVvfKr{YuC>(DU~edC-kShZr|e*HAKx;$l<&lp!9*nf%{R#4{LOiciGm9{52LotX%R zB$Dm}%6T~C*`OUTqlX!$)F!kh6RppBCZj{<@KE8~Gc1kXGXRU*GfX)1jA!&2!x=Oy zRspdG9d0Vm_qIqv1^GV`OP|4f#!&DUP}?dpL-zYACe&Krslntol2~#fM0slsP{SV~ z?|L9e-p;>yIqgsq*?jpdY@ZuPiv*qU#;zzl2HCJ?`Q@Os{s9OE7gVaW`6oAPlxZ#s ztcA_zzSy7_E97Hl`OIs{fSQ&R74vxrMk8ZfKfSbg2%62_C>7E^h`KRmb!rFxgM-yk zLG+W;%1Xo489fQaL`8nyMkS5ImhzIFoivD$)^WX` z7RWOwDJV*KXz>6qQx_=l!q-sHnPdwfHiM`K_TrDgpN|pidME(mXC-N$po5R^)^D=2 z{W0v5Sfp`PM%lSHG9aq5`;Di<)4<|&zIUH~p25T-Rm`uk{quB9L@;2bG8;ULZ>bFf zuc6ohDT__b{EdqhmytkW#rpW^I#;=LmOwy}&cf>vz@Utar`0akquT^=?9YHYZ}T1C z>N?0>eeZj0s2T8N`0@)^Ps>!Xa(FOm+T_J160PL@NRLJl?CM>|#V*)Y#WW%P5;pT~ zs|AtLo{VAc54Wf2g^jNJZ?1iGDV4jJF#K4f-+Pb~B^>B_9aAYWYY(jJUdkuFsw+}0 z=k!&>G@ULhJ}*Lfj*R^^{G(}pYrga*+Z7J_aqDm#JTkkKRM_Z4JN)_6BJ6)X1`ry= zpYjAW>8&2tHQHZritcX3-x zxT%gcJ8$Q-{5l127CUjET#z!^uPl07oNnMnR+Pw~B@Bk@N}vLp?Tjup(~MRPbw5I9 zE6?C-7l&(Q$N_cD{7dyPWHFAHKU;5Xaart^h;E#uL;~gkiib*)zjGX74B+9Rx2(~3 zl)6%mfz{tZ@QJ$jXS2}*kqS9PrRBv&&gY%|{eSYG&)FT~xE&0ud><4?79>c|o&m%8 z>ZYo_i^P!CVgr84GV$QC#c2d#CdTQ{_IHPwVq~?yP-i^MRMA=mU5L5iK*1B15mvyh+`|B`C z?17l{P?~@@45scO_hpd^ic4(Dq0M4L5@PoaQKmFoM+1+;syW3G|IbaOmC=5pDp9x{ zk$>$c$d9uy#?aWkKlB^^yp$UPNsY<ko1qWr@GAPeZnJ9q4mbjPxcIUPB;)goH< zjJnQQ_+{hrcMiLsTQK#b64DJ)(k&g*`B2i``)0l0e)szJkM+}I9Zu(S%`3(j=gaABwin_{O7PI! zPX@7`#)l!qRXdYR@Zl?3=B?;h_~ z6t3&BwJ$({C=vRKgn)n!WGrYp&24g~yqIx+Cf*ybPZh9JO`Zp)xIu<3$kcG$l)&L( zAWxBdlg$O04+ zDE^XBx6`!IAVTqLr#2l~*a2zWu4m!4nq9tZswTVbVn!$&!c)@0)Sn3&M_UU$6%xZ=Zu8|&x0qU-`KTPyvl*8kJypkcumNk4rnf=xvDZw;XV zxa^nWLKinsh!x@5q@W?6^1?^c2oO+GJrdRv7clwQteYF^0sXx_%!8!EADPo~O6p`Q z*~j|_gSvkiI0^Q31`UaUAmbjl6`yyt&|=aOY-H#%hReW~(qoqW(8o-lC-FcER1sVH zu(*5PA9n)~w52!4;erR`i=sMe8D;EX$~!q#BC z`G%Flbz@f*!z!?c9WU3Rw?VsW>NW;(8oBBfR|i)k@{xlE5cQ=iK`4@+{>SxpE4Ec0 zPPGP5%x+r6N-k6@9p3w8TAY7s*@v41ysy7b?+d{`f4S4WrX|S+{D43SyF~5Xv|#-+ zKi=deOf%?GLVF@?WD*Xc?qr7J1K;P@fQ%QV5hpdtVxUxswW|pQVFnX!#^wAU+SW~D zq@2jYJm63dF@X?tny)GohvuV30>h`I_CG+);_8`2qi9IBSigOD1{pyE;~2#0D+4}9 zBo=6W)+>SZD&Lb|=;E$HnP4yYUwz{;`E%z$9C7jWBr`?5*4RU_$W!9P@lnOr#4HVRW91HuIV zU05v-CRb1Z`-$|7>`X*g2iN!x1zf00#u zP(&_Xw>Q4koc7W#k)0WHZ@cMEWF$7?RfbY>S=gRKlX7-C;5echLoaTL@l!DjOI z5E-@tGWzSw1>(?1$B!x{IKPYi-!vHY3B=N=(mfAWVJfwqq_}Z`!K~jHBn|<*7@)wD zjC)<_QwPerbOjz8QB!+-M*Ev5`BRF6nlxMSSqvkev&3mr%CO&QacuRe*-rz=1Tysa z-s6K;X$F`VlmT5u>$AJrCLbx}H6f42{c%@=#5mU?8(l~MoD2F#Xw7-laaish%d>49M*no6?atD}*wV!jj$mE-S}HS3>^GKp#I9x0$l&6HIq z5~i^N4y@vtRvHkYP6PBaw8Tw(w=%=823{LCcwHUFeS!!gW0SC%p`cHfNHTwNSZqZ1 zPDnk4;lvBMq@n&Ul+guUx-4x-lyFoaPgvn6+s*rq#SRB(rqgJETq~6Z?Az&S$Vg+HE|XpxMZN8kNcu-eP4!Q2TW9w0 zzJ5Ibj;9fnyoptbn7x~f&N-3AzdBxPpSXVb;DZ*j?{S7R$@QIvcG!;&=+(B4k#Rxh zBNicV8)au#*DfDhaUeBGZr3}9jL>8d+*crHuUPAEK^zi~KbS%0;E)G;jVZI0_0Etx z$&MLDAOK=?J&FbyBEAo|5ga!2e;$H@w({H{;SX=A=Wi2GnPHp2|FG^xum>Rj%#k^y z?=O<`lztW-Y_0bo<+`7Uup%T&=JDadjMeMqZSUzWO8iQe2B#Iw2?8CjWpkuNucD={ zfvOgC1RC4CEw95R0~~lm-E@vj=v(7T*$?byN17)5 zAZvz#Do#O&`PCZw%G2Xj443QQB8}!52c0nn)IHxtkzbPndKwxmnt7JAKpIcI>`qRI zpvt%IfHb2*T6qd8y6B6sBtYsCHqaW^)Rec?Dpbg(7!t4P0dPcVnhqxi=+Z6yhi20e zc+0a-9&&qgRpexJmN;Q%ktXP`^M>8EVDlQ+_xWjOz@p<*mHD_bDaxm15LC9S6&q3( zXwc$&cPaNM|97c*Xp2)On3KozEp_Sg)^Mtpz70kveHccoh{aQv2bd3$M~u4PX!dm8 zf}GbakH?K5T;0_wL})GB#p@v&ErtZBjs(G{MW)=mS+47seo!+_aa3{=i(!;}G3%?O zLbzFN4HAFe@HjUX>9wN^vZj@|Ureiu$5V}n1MpulYF092TPZ$tfc!0Q#->NOVqP%s zh~3Vl@WRgYzERfIFHRE4Xj$FX{ag3!+5Nd9H+`g&G}i;3xS#<~>vv4TUy^tDo1{dz z_j^LoyR?6oyq%Vt{xRJ%SA`ix^ykRpo$b6`T1KN)GgZfz$9mV+%_d5SxMYRVSzVhv z1^)Eq6Qu#mE|hYWE6VwW&Qp3fAm49yR<*JdIz zVDh#gbH|Z$)TWM4yvgKIi%WqEY6X!l^URO_ui%^tk+=*Ei4Nlx9zW2zEe$|V&~jW^ zz8M=C5e^6*Llvb!hie#z)`PyQ|5B1+erfEeY1#uMaMP$^irmJYdvDyK=j#u;U%hm0 z`C zMlf}^+~1r6_cfGy)LT_|((XfHxApK8^&Am#rQ@dn>Lurnzsd6~_cbIC7_H_P%z2Ve zjJKB$!_l$h2vj>P4RNd{bazI11GY9kgB&3PR%Knw7MSrFxlsaFVS8U z{$-hsO3?^Nel&n!ZpXCP>UAY`Ojh*(Qh{xZi;HM4;12&Btw9^@t@z?ol95?kbv1@m z#aO4qp%Q&_S@vuZMjkEYqS*mk!+?I()H&YXTl2FW{9gxuFhxDWoJq89lwhOJxo@s| z@pr?%PJTW1CId;R68o{yf$wQW$ku+DN=&g3aD31=iY`BEwyAa+2o8*e1O+O_9ZGFsA7uGgLeYE57Z)@@TbSk8xorJLHS8w`qz_tl|J z$PGuSGI%wIohR3=Dq5+0A6zK3_!AIl4YR~Ln~lEh6E<}Qn+-p#5e9U%Y^?YRQuLfs zct>6RU27y$;{DdmW>)oW$D{1?w1$2rgoDq%Bgx_fsr@_+T!6fd zlgGeet{8AHpug)572{0F?#p>1H$}ct|0WTes<2jsB|&w1IyBNxc7ow^o@gIrV3PrCe{AHn4BnD zP#Enm`GU?_<9@!*iZ_<8RoTgWJeTl^*}&2MNZ0Hl@a=oDluZ}NFs)Lv)~Z7e^9$SY zb-6wBHW3-pee;k7-64l-nc*%}W_ah1i5ct`r3ODW4VMEE@)(Xh?L*QP4G*cl-wO}N z?;4VpF%*yKGk^JQrZUBTCvUwgNr(;g->vR3^2-CE$(parHym2-_PFU}5Ik}$_@0*a zo)2S(w-$@pz0eQf^d!~ejbjte)r)ip!nyMVK6z-iSc}N)@pnHy+!3%v)tO(luv&|Prgt0Hfzl3 zOO=B(_TnR~7;nanNqPXlZ3H|5wcfsaxc+dkxI=S0Tr@Fxs;p!tdzdbGe1d-4$F#Pi z>#>D>w;mQr{Npa$_met^NVJG~5m%S0lQU=B9r@~XkISQOx5 z{Z(I8n>E;colyo39Xd-cutGf9`>9FVZoZ9^O-zKtC5FEV_2%1wvEsCfoprl|&qwxXkT4%Yh_9 zC}5J!)t@XVZ8F_Ge8=lvGX0_GoG2^mH zZ%Ws41rM}?{!$*(7O&(CtJhupZ?|{3SlJATdKpMO3uF?5IWH>o+S8<|NoGhGHA4M! z<5OiU0c$lSA*2dHB?pCIPBMIiz>rl|R(R z1=xnNDsGQ(@{Y|Po5}uW#juavaH-AleQ3i0r|FFg^2igv+g)5DKSs@3A?Jhn4h*q5 zt_JrL8T7YlG1!BiSC5}L#1HhLr2kZJ^@sma$j?pcG2nlTey0Tc0WX3&3nVC~nnfd&bf6lb54>qk8U`TwW zI0JHX{%nkA1JX;#j9*PK zPL$+?(mCpn+=K@w2D$T{6OK>BpZbo}q@>i!-K>v$T2#U?g3O7I>%ZT-9W9HVA3oq| zvF=KwXG9S|P@+OBG~3nY%V+$wd7oF;>fJ5E0kF)p3WCCiM~K?B<}j%t4E?8VU?sh< zGQO~}zv*vC1O=QeVYuS@9N;M;o{uk%#C0Dw zi72UdzFi(YWwzLOveShWn_c`mf@e1$i{yph5wo4EoY}Y*6{*HNsikt00G+V@wUJ7N zF6PO+RRt0Q#GU+(=xtgyZ~PnrpUEIs5`06NBIt|0R>nkH4-Htz1)5JLzajD4=zA*n zG4^EaTGg@&PFK`-i&uK2fROq)Ls(u$iZ6ka>qLWPx?=ql|G!o?49^Q7P2}}>PVwP0 z1cAa|k5mwY+1qgJeJKPo{t|kFE^OC%EthBhmChJ_(P+|VDkQhKo9=?(2Z!#Eo-ejb z)`0*qwJU3?NhnvXfkz!2gq?SCibb^#1OdXV>B@gBVh3gTOt<2VMBT(nfTCdBPzPI= z;%+tI%Dmd+hd~0X06z=(?!&XCXBmy&iZDdClF!7yrYK2 zZqIV*CtlS4ObWzru5#s*9F!B(QuCFu-|c|yqVud;B|19(&8om{QQ>E9)3W%B{qk0K z)~hu}h57Sok9+Z73(|fmv6hRC+CaEH}V1zr?+2>NKT(C zOp?nck}h4T9sK6NtF1rwRNMwm0*^wz!EEFZu>4Qun4a3|IRi;m%-jm;Natnfa0!yQ z3@LI{yIFLdMqNH2dy=d5y;V|t*w3!{Z+m=Mg!O2yzrvX&pNH%4oNr{WND zfSbQkuy-DDCZ$T^V!=){EX8c$BQ?LiH^181i*@N!)pRpSY}1EQGFleJzIO=`J>d8FnD>IQ5zj37cr9FE^RYA zK94sep3ZL36Fx?A<-cPM`L?K0W6IzAdPP64?Z+n#yF;(jWy_5xK{5x0RMvRTHp3da zQo>HW5!DyCXqA!LAhI`ACBh7wF2T_q4)m6CGuVEcKOZ(?SlF{!(vFvZjnf!rp{LCp z^+>c5b31D4D?hJo`te=rn!0$a+>Q*gLIAIgf22nf|H|jaL=fX(ubW~tM)2@^)Wm5M zwau(!zA!`)bT!&&XQrcB9i~>mt%OIfY9LPe4&X_}dW(fH+e$KrCmTHr-hD2_+?so+ z_BfNL+AvTAT576@8ne-l0^uhy;T*OZA28m!@_xf~zQ1OHV_I9owt(w)Zi92GAvx~I z5yc^FO?3MLJSb9t5g&czb!s=1q|>so4RU7J2p{3CVAS=f(f}q5pOt(vi1!;h`t<7> zdX_4gVX!DN=-x9GLOT{8VU&)e$WItgaQmV^7T!@3D;k2`UB1-pHX)=RAB#Kbb=~oj z1zCph<3<2oHuY~h7ABXysdE97&8r6{r5EErd0gI}aIOK@RjvI`jl0lMtC`ZFxKTB? zE{&cMTGF3uwn~F5-F%xbUm+qheDPsjglBU3e+S2o;Lon#@bEp9yZN4S|Jpr!$%#XC zTLmD`9Oaqv-=^iYEn}xvmac?h>d-ULfhJg%u#9Z=Apc1@cR(2%F|~kTNHofD zrR|MU0gZe(&Zke%1y6>C^)+D*dDjk1 zP7pLD8`@t-bfN2@?ZTi|$okgQm1+A9QKMtnoG}-`0gNB{nY1{8(=ym+O*RQP;wxBd zVgw;#o75=`(*k{9)<^{D=matI3J}BE{?X|SWID^G3?-^)ON$)oe922Q4#?#RgRU8> zY=Ac{^n=#q4?zllCB%5S<&*ryt~8;c0KMJvJsAZBeo2;jE@Tqua#-6A?=yJaW=M+) zFDR=VLyBdgBWlbGuU(A{kW9x97n??G;M4iX%g=wy@G}1>0jwHUj*opjKvX55NhP8$ z%1Xd-yG+SuJTc&i*R#KehckYH($OgaR3nqk38ABd*) zWRo@gyR0n)V*`}XiK}zQCytzquz^U1Hm_euM+Z71&E%8klnhhHABjOQI~7doG^>n& z$V)6!3h)d&kLoCb*K_b#D#XHA^D%S-Z!gVyfYdKN#(498q$iFU^XyfRp)1Ta9)5Vb zgd5aQ%a=Fj=2IoJpGVZz+mgJMf2iPbM1)++JJ%3Wyhrv&;>=+1VqYr4VbmV^{!U;mPu!+6L(p7sZmtiqB zz;;lo65_RosZ+4>H@Qd4uJyyd-0N%n7s@}H-TKg611ws-Ta}eard-T6K0uVGPcFNhOFVRDK=WA#m4EeEEB;6QUt6$T{ zAFi~gGu(+cpB+v**{v-6&Rn6g!qhu$KAX|XG%r1+-tICl?^K12z(OE@-%9xx2A{vK z1uZ2N7!(EChX7nHcO|o>bPR5E-Y%fzdYa7$8uWBKa76r`X`BKemoRYp^qe~^j9bd6 zed;`uZH1&1m0b1u*r(5J8GpN?p1yt7^ga`~4acvhFLigqmi!-ytY$x~1L+``R@q4H z9y~`At6MDiSY@vckK&y-3i&*AEA@$}C-CV=s30$ndV9rq1tKS)BTLL!eLen86_;E; zFTPl**r!ZI#0A)RWhh_LV1EX(STBcZz3Ibrk}cB|nGd^l)Ol-+qrirURr zW;IudkUC7*+W_v96E`HLYDU}gU@8wq*2r?5=Wsy`dBMy=>&IS;#08`V8uali$7Y-P z@2sc917Pb8Wc*0p8iv4Tba(&lnd!AsLn$M6A<}@(SK;N+1vecIS!6+}IZCn>;lI7V z4$-CXjG*=omw2rqSn)QVAy;)kZQ|{9b1J@AJbmZjl1cf2W3PX9{hVJhDc%7{XDq07 znRh><6sf((a=b}F`(;Sw8fU#sFcROMq8~WLMkT!Fx7?D-r$|3}`Mmvqq@m^?=DAwN zIAbpM0b5heVSXdx;Z>~!UN<5&da*ya@6t9LS#`73UCRFF1oS-pvO(?O_Z0l|891;r z%v#Kcc5yAL^;&0!<+WdZbJdsgCM1m1O`i7;5xU}tfU7uH$%fcD(ro#X*oMC~-vG*K z;>@QgNqmVcElN#)ikSvG;@@xyO`!S-g?}a^5 zego?T(awN2YW~&P2fkoobIcLAX5d|W-~C!l5lzBi*L;&*)Xdy*6%0Bw@98K^0 zz)cxHfI9z*VXqSv-kohX=EMoWX;MO=&s66c3tb!;P$y6z%KA!2z&g0O@Iia0!>{BLF z+00;zUr!s5VTLLLs1oG*gy4)2XpeC~=l<(^0MKBz^d}!#vL~MLwxTVQB~Sc`8d{gukW) z!rk8YZw@5JdS|0*JJ{p31XI+Lf2ZHZX*6|VSqN<*HsW)mQ5MRjub%f?@{Y}7lX>EF zw}7-&L>kWyyw(VSW7xV8CWj*HY^1B8K0tNofN^^0Z?V|_9e>qJ(^57i;;xB0;Z3|} zJ(156oNu7+3w3emuDp%$swR*6D5u+fvfx5i%J*VA|NIULrr_z1->YLAtxA%?HLu{> z*4IdN#ejykQ1jGBOb&PrwJYl@G)*%JKmaU5A4i;qe%9Gl!isQry!Og)iAORwI}+R2 zcGz#D8Tmqy>ps|U32Noj`=fWUo^8>~=QojPo;+>d41nitt|z%K5q_azCjc~X4-z<> zh=co^8na7HPP`YwmiV{h%LMG^@U*GoZ=+-K1-YqsJOn)%WaJ4{GCX=Vcq z@KCotzkfkfyQb5}iE;=so@(3FGC8#53_f`7L>Tg8;8U+2+Z;~q%5k(0aqbXL72_~y zNcc4U^+wt=xfb{?n;JK}iw2udV-0%}(#2y|qDII?ckZQ-=}3`>O^4OHpJV!f2hE^X zignQd3`ht(oFQe72<2DNEz?7Hw?q2FSQ_Qk#!bS)Y((4J;5*&Hn z{760KKcRk4KG3Ny7TuT{b1|$D-@8)N`4I?gdk8mmkV5fgOej-T=&Aj zties^f4CbhCD0Vy}CIfu<1Q1us z|6OTU*F7D_T_hlz(C(K;J;+ajuqfmT#l2n0DOT>YB0g!VFP&dn03f`2zZzzmP7*j@ z#TENt6LON71)CJ$29stSYhT(URO)gzCF*g@@ z8}J7wD>7j}5(-$t;#7)IPsq_0Bw~mxt{?+kifM+8ANKHW&bFO*c)c)U_)q*4N-@fW zFao%BrP;f=egbH%Ymg^iMWjLu69lW?I&7_qK`QDaIQ_OX;MB6oje?e2sR2*D zFDlqThAYML1VS&p?{X<`?LJk1{+OEJYgKPgaGI@R08AK*31v(!MXC|3Y$jGcF%ADT z*0flK-}qn}{GNz71jCv{tSw=}h-_38G=lU>4utg7;qMs!Vf=)h^v4E(Wz^%V!6a%0 zn)br}Yk*W3mJ zVMMqa7%LeP##K&b1ZxcCCGC~54aaBuZHU$LXeeFNCTFg~) zKbQ)Vc8(n}1iw_}Kj)97zPBY3@E(gsfKz%}$A)^Y%`bKquC&IhjLeig7bR@=iiILn z$^%pzXUc4#m3`c$b3slDm5pnEF=(J491;1NSKkvmYG_lW=D1&ZG=v{RRz{z{^ z6|b4BGp`~_QAGpJ34Doj zQmAJ!(NU*@Sx`d5(;`SfRtdh7)^vh$P+2kC6qh*$Y@FL zb%UQmCXVPSNIW)oL*YQLHU^nRaJ_Kzardj$vB@~~vguu6A&2zzUTHMzV%UXw%nybh zI61fEgoX@cf|WIyNmGver#F1p>bGBKzY{V{b^~zd=JDb*TbZOC^`P0PuF&5r4EfBL zHj7-zQExC3zQP>-IU`S#Es;>Vw7BU4+!(}X7X;tO*^tE9Yvv6jlwJl3)2EwlKMHcM zSRMy0i&M*bAp%cM=NlyOZ>O3t_@fN`Ya{>Lq{33SNgKvxf_uP)Uw8gj@XW8Us+t=y9Ylf z9C8mlnJoJsU^PYHr(^^dYjfF{jfF*kx4f6j(y@O^W1 z`;r?Sn&YopaM%)P^WGL5yt9>$eee>M_k1H~*@5J$;w3sP+6RRxXV(%Njo=ZAggFjK z7pu~KXlDt^|4$qH%k8I?f`69dpx>u1FNnGXVv^G(D?4MtOrEbQKFwO7a(+RUj91?|}7NLY-d3torZr!Wr`m`RSeo;Zu9^%UyQ_ zUi6>h9lHMs?JRPTqt=?-9TEhrIoN*ix*fe{cEQ#)v~^B+v%mlH z?0X7lkM`jgm>R+_>-<4@S~RhslTU!O#GIZ9wev1dHHr4V$6am-!7!>>fjBu}B25MZ*~U@Z13aB3iX>~1ioSM6;jv{5xW|dT zf?=ptM!R2CKN_BqJ%5~XSrq-1E|A@pK;kGNX@~xCVLF|YfXx)j#<55^ozb*XGfse^ zoL=>ZECu5NBt*NZ+Mbu=l={dY>m1~jvq}H(zQK_9;q@M+lFyJa+6*V4(5jD=KMIWe zK%|o>e9OW+c(DpJkjiP4-G6G#2wUXS*fTjCRW`kRZoOqG=2%chDOu1tf#7jye~&A9 z@N-tmhB%j9COcb$12)CeMf$RTBetA_tp%kU+0Y9 z3213lh%Fg0pv0ZF`LM_`P81--uP{Hfnhe3}PIj}E!if-wr+LD09@wpvZkC!cIWdt#N64_~8 z8r+s}!ghagAvw(bLjB%0Cz2!^uq9I9zqe2JQqH#Pg^Hq|eg5L7Z9UCf>Lc=ii2}VN z8uCcNYU%}H8+lfqKKVBIO$U`F0_YTWDV8M!KRg$lUsxzSJQrL?CgN7C*dC+tSeQ84 z`sOb%fGMv;nE2>we>NL-?7*GcGzjs#cAS^D?BK?#z5*_T#y=`T!=15?f;AKJ$F1b- z`@!Fwa$kv7d*<4FP)l5VF8}xyO`2*&#u|LVQ%{v7SV|P#SI>mQc}eRKqr?5C<#x!w zA5NKZF43C3W52>#h3~&-?PsYcK>W=6n_6#zJmLF@YC-S;K-ii1-jzXBxwNH?-=ak_+a%yuM$z2l9z<(|&L@=|R6U!vW7wRa0B-iwp;!$a=1j~Cmf-BcxF z^wJojEuRMR55Xl#!)2qSLV&tDSMKGjQDbk3ZazM^4m3<1rGrP{zO8NcqN7Iu^9_-% z_D`TYsl9j$U|Sd`r`w|nm?tF~Yv0Vr@7YhDoLPTLe}}^UZQ!iIW7!Cl(tO16;bKPP2lRs1Xd%%(*lP=HRb>&wY-Qa;gI4JD}Frv z*Wn_?c1KI4DzrVdk5NGwSYk-WtzCD(Bu%T<!?s!V2?`KcWrWg zJWj!^9FoxHrl|KlsS6gNDa`sg-@5wpm5+T}u>O!Us|&pPv$X&4x^t6wMm47ebsX!f z1PJgoAdVNlh8YAH199dsczls+V~?(_`Q~QU6yq*mt@2pAKhTL`i@A~pyp zGb`uD1pt-uvE7zXumS`g>fk8^cX@#=)9Z#tduV}sQ9t|@MYeUFGR=#W#eK`O;7i`f z6%32tQw&;dy$F=s{r&YQcLIii4=QngR!^ZXohh1F zkPeuNdu-qyn!vH8+LG>Gx6L~&F&4nb<Fz3l1hCi<`^-%)8$*pyi{hk%L)?Oi5-jlBlwnln*HlxF*EJ6g?xrG_RR*IGftsL|b|9|P(=xQ(>k=Q$LliJKII7hE-OaI-MuLC=p zG-5i46aT?v2*;^OXuTZ0S+;*D#IE&%k#@$XgD{rWB%J4=5stq6cKs)P$a4En6apw_ z#Yhx9p0&9H`@@WlavVG5BwpsRmzf+kEj~(J3)d=K|GGV6K{*ftRSg@*Ag~2Iii7%N z?G7-L3dr~z;Y@UD40621F@OA%y7~U*Gpd&dKD1pz(=9r7D0{8YjmyM!8_(&sK8hkj z6Q4p^0rl^JB@wFtkaBbyF-*01SSd+c3{eO!c6qS@7w!pgq_0j)@yB%YeI}K2IN#kU za<4QVpkvL9&z2IK)=*rbV;RNL_pJ1NczbfWpcA_3&^3_$d^&ni7(Od_;4%5Q6j}rC z&bk{Tg2SjCsY3SQlx%LXi4A$^9Rjl7V^j7N-VE1GN1zW#4!C)|zp0T9n<|i@P(8_+ z6Ex^FFSI{}P00F|kfTQvznfN^(iB0AXbu)O+s2TQbm0icaba{|2m$ zJq$4OU$bpT>u;hM;Z=Mhv>XsyPOHB7-d3tlH*^iM%cv{|S(|u0Cs58^f*P~2Sy0ak zEhgLK5xXsRT0ab1;pdY}n=VeAJ%F^TxI{v;xcKH%&&kIxO}@suvB;#RA+{jeXApBmdL1yzBe@)arpsNd$-Tml7i=>P=ou)7w<`(KyPIvGnT; z%2I*X>a)PZ1&_*>P`MbeLU_+1{Q1Sc7fIX3Lg0!X+KJGQ&wn(zq$9g;E)TRqt*ZzN zQOYHvc*v9s7-~~&<#nCmzcH?3{ZmkaK(H%r@#cjo<)&uc53Vvwu!hZSW5OO#$$1L( z=odYhM`u?f9Ka`Gt``ueLMImV?M^BIj}0u1YY~q4iHNsH(&e&kLaf|O?2oPONoV^n z!tWE}AlVZT6ln_NFORjq%YDDZ0LYm_4>Tc&${0&tVOZUf8z~rh4n{ z-D(Tu1a{8MO10vPZE-u1r`r`=CtjffUKnIpfCZO0)r9D<0Ho3IQA;)jTqd{GOk@=f zV(l;xj|gmfaTk@qKFn*#bj3>K!0!n$E(jZo?#+IMRX!BA96n6;2S;*vU*ITl@jR@+ zS6k5%&Ht?2=nrlXNwuu?I2|0G}1d}jlkR$;5$*>{YZ zIm~`_IzB+KH4nlyt-WLF_9??t3M;q;bwwRb*r<^ zoGZI*U8CdS+p3bvWxlSVM%M!>4*v52>ej{p6D$Sh^&caJ(~jm{MQ6XrJ{HKrRLAIk zmaBeyuzbnxBK5>+GattrIL^Ks*-_#6zgm;5=ZR@#?iYx3|4(ZIF2_`L!44T6nss#e zY47Y4y=>g=WdDeG5F-Aaut0F>iNK9(cpWq0jY4EOGO{`!@4l+H55^&ITW(ROS1&IV z??O|$OJQ2}9Fnk2<#mgNN1~UYv&{~7*svB8vGKfhO6h*s$%~OL{zfDC7R0f!e&_jk zG~&AyMzs&(%dM!s2L?kbSFX0iv?@BL7;96yO5s2jEm5Ix06e2Dhz>D7SA;CHq6&EO?_)tJxDm@pZ^5xWXQLSPnPUQR%rilhcU>{t=r2Bd6%@R2SZ zo@Yd)+kgx61hj|C>So@Pe8M-=(?E=DponuoZZTn8&;yh%pJ*!z|GTGJY~_40tLkpI zFPz2~R85N|zg7vqP;KHx_Nh(e=-8We+b!*ABinl65}#52e1#{~PyGlb_pJ8!NHxjX z-hda)nYoH2YbGK#T^P{yr!u3gH#)ZkCOT+vYl5i5@@Ml*aXt-GZb0cH7|6&nGRP-J zabnrbboVxJH%5p~c_~NmSDM|DLzVsBl*^g^Cg_Av37-a7$Pgq(2*SJ1Z%-z*_}s~_ zmsH%cke1slBn5Y}0e_irft*znyNOnnA?3Wc0Xg^9K>T!NMUI?$*7i_x3zBZnE*%-#cS#O#3(UQ>(07Fp{ibPrJyFE?&!o0gTf5;Zo*Y6`z zKjZRCsqL*M0j~@*b)>wn;qlst0^Oh3f|xfGj!d576o_@c4(gLr;aH5+3?Z*_P8E>d zlF_dTzr#X|{CL#nb;WqGMK#IE#u-23_IFOkc)*60=Eg$UG+p`h3eCv9tVHQMReb(v zCa)+I^I2`oy{GL(D9yDa;b=mMXIY zDzyzT8O#PhiZrTNqt>#2M8p1gJ|`3cw*9-Y=W9Y38N_2DjUlkBkErB{e%^*y#AAV2wiBy9%!L(}B2CaZ47S6(L{(~5(BlE{K8Ki?07SuSx? zqluDNxu>g-_Ib*yD|p16m3tmljK_kAb1bv+*S8$Q|Ja!Sr{JKyc`h}-@>k!K00znW z+I}LKgn+w-a*~Z>`WjWhJw~p`Gp>EH>1@K3RqNVz>1W&kfBFr+x`PmlA&?>d-HKom zz%=Tenf_s%D)rLJXGz-|f2jQFnTWjo(L6lx7HCW&$ewS|%0kp(n zq@unMGqo^}EVS)fZ2h`ccZ_zX1NC%H7ovveQWB#i6R-O5WhS2oL&@oz#AQPNj*JvD zw)gwd=KewK-WvvAP=9z#G<}%$GR%r%-WhBu9u9cip*4j^{f?DF*vI)6k$_|ekK1i4 z(V+BNQ~(KwK~_eZlmMCTLkd&CZRkuB`I}FaLn+faexV-Av&q<{S#hXkm}h)8a1!05 zz;DkWI}7$MC02u2lFDP{gC>^@sz0V=alF=G7>xDugZ2pX6Fg$?kW%uJsZ^puJ=BKi z7m}tg&63pdP@_kG_-b#BfOf+>J?QE?5i`y!1XP?1Ah{p>AxJ_qRlf9DE_K3sHo1@zPA`QdgK+Dg&*0JDZk3~5ut{gJ+O%V1V+uhPqD)sum#lur%*a=$? z5Jcw~%92)f@}(SD)wRBa(E=|7J2DCsVmI6>l>(7u_qXQY$ElIVu+P*!0yYRh2}|jF zy+*B@-J{|C=xvs$a-F>Y4cs26zE?@vU|l*z@(RPg++_f&_B$UvdEXn8H)R*^bG%1L z<0EyD^QtvryMCpmAI|jqkU|E)dV`k!r?ZEoO1XC8fa#m;00n&$6tUj5!#R-r_p&N> zBt6e!a?_Uj$gy>DSeqYeRV&O6Vl+R+V!J3+$aT&MgZ*`5T#V=RsJfV(r9wzPFwgnC^iT#GFGjrSxXIjp8(H8f2$kVEM!)$DmKZpqqDtF^*A!=!>0VL zn-AM&HFMefLl)})4D(wdy(v1Xa%~kuKdTLeTP5Jw+@C7`5YNC#fCCHlHNhYu0PabK z%4+CLzM(q|vnE?HPa>SD{jbrEp4X%f`ONLb1wqtlS;O*{bdkeG@7S-CpF=d+@5@b7 z&uR%wPm1myB}q&MRciA{;pAxezY%Bh>r#Ini24H4)fnI6_hj47(%1U?D`L^V4sIeO zQqSHXBk~VGgshkF00L#RJ)R!(tZj|Qb75-ZTLL6g#jkztqK|(=@Yo9C5Hhb8gOf4& zjy1q?m28*);7p5>f>Ji+s}ymwhdQ_#%Tw9DC)(BXs+Fmoi0iIh13@HmnC32j5T%Zv@gOvI_4SAQzXUTQ7CivgmquHUT-A} zi%l*3ZVmyYKugXwa1@j%lURyCg4akrPu}{9^iV#X)!{jTSS;rKNnsX(&OKfMNg>*i8^#Z=K+Y!+6Pn(ET616GNt637nBW}$a&f#U8ZzxBFX zK*0A&dR{xpGccFgW<{6FO>g7iUQvalX48iUAl$h`IO{Oo@9~Ah0;5oUN z^?7zNW_DIho-8U58wR0P&e&H@&gLJY2y~;WswS#wz;xjeD~Il zxuli(#QkBd#RdYBB!*VhTlAJ3;Br*X7itzV?KzU9KRN(rK@Z0M_->$1lshw?W`Mc5 z+0KDF8t;u7ueGR)wRi*ek3qd4S`9qH_P^9X+qd~myKt0(gT{#b2=D-;f;h7utJG&f z+}HP;RX1~AH>T9#ax$ntu1h`@PMiJGkO~{B?2%B9>aUOgh_tZ4y_-NG<~d%!c(jg^ z!y77&PO$5TPy5qjdeVPQx9QrH&;Qy4{PPuFy%7b;Ns&<#Ud{GjIcz**spMcC zHXzsz7xO`%jo-`dIg^Q>*0Sd~L{CZl_DYmM(4=Ad$9gF?-0{4YQO5eW( zsFwu(v|aM7O>gh6+G$f;Sir_0a5L%7bAJ44RIFjI4AQ zD+_=&Uz$2Atr|wUOg=L>1JdHbCS|k3n%#W2{rcINPhMlHWs!Fpk^eZ65Gqq_@i^n& zF{qHs|ESm}mtLQ{lB^!qzhOS{Aue$Ebn`#`y^T-Ky;|kqS5nM(6n|tJQtOn% zD5IBaU*)+;V%U5;hv{Kz4Zt%m3hlT7>}P9nL5z2{7Grb{YTcO zv`5{^fRp-j|KF#&47z`s=1he8;8QFextiE`;sHFFFqZt7t!!W3my?%&VbIF2l}cO8O8rK z<6>FtU3^N&=D3+|vnyta+UqyRgmxHg{YiZdZ`sWyob)Zib{b+-)~%BeYGd`IzX8gM z_lK3juaA3E7TtkTHC*|E9>`)DthOoDAf(^9^t{EhP7Y6AV3LSMVXD%A!VVftg>SXQ zf1Lpn5gK~cm&t-`V1o*;wb0AE+>=!)SFb?0h}Xph(wtl#%^bPjify&!1&0l)@r>m6 zahbyz00P~l=&;;EQ!OyADzWCQ(#=OL)zXH(<4FwkPN|mB6l}vt(m6G)1_CD!_XiE* zvJ<1R&s^w+Q&Tp3{;=_+zt-l6}N!y-80ASqdFWD#Jm*pfLWTJd`A7!7`gMmor+x!6%Dc zYcZ+4*lK01`+rz_tFS7&ux(dDLZnl=yHSvCq*DnAkp_iHhe&s~beD8@NlJG}cXvs{ z8tD7|-}|n$v3C9~b~xrS=Xjnm#vRvvUQ!A8;)I}z86a!XZ{t7sj1ASu{`%q}K#JCg z*Q=;wHN14AO$}`2ow+L}j{ygDzIJkc9B#gM{hy}GUnNp|(p3Q16sFTzK6R{u0t|41 znPl*Xv<@?$bug3Xpw^zQ*}5y|1L)|Haa^=oX$)X@obQz01UgN^Kay zLK~FC=ahuKZ=j(~)YK{qs3j~ovSp+QT4&FQ9S@2dk6?@^QU_}GTPR^nPrYbHziv+y z#C9feb5sgn&Q0;0!Ltp`UW-5#>ORF#3c`T|0%v9)Y=U&j^USnZWM0ejyztNFS@^znt|kAgz-ajce+36GrAOat)Rb8bX zumMe_gVVq8*lZX;an!!2kqWMbuka>4lG>C}uVC-4t$G6v!SBYm7L@WyVt9G8(&Ql{ z4zO3@-m{?f8USX=Oe%5)SD~O)Z@$u+RhhjCIO0rb&O?k7g4grHhR4tV!ADQw2FuFL zVT%<4^e9Td=}N;mp!b)#-tpYngkPt1JmaYSrEc7rEfbs4btU5;e(oE`@9Yauh}wR- z3*TDFFy?(IzdV1z?!WP@d5j-B3--KtYitwg-YAAydwjcY(pK;8y2_NJ%brH?ngDpa zt|9w*!6B2Lw?SS1)X~;da&D&%0i)x?eC_fx(bYXQ`-p>g?-&Grf8-_8`5eKe%-ISlif5WtlN|j%vx*cQ+)P#KZw|^GZg7*oJjxw{v zLyl46vfJ^_>TIp)l?)3dB|{U0lOy@#$19VHBkTsX{@MBj8Dvmpa=G`pT>z-EC{8!o za=(dGFv&uUepmvdeaP?pn_Pyn$w{7*ud&h*Xs1++)~Vm;SjwdL{(7^99FKb!(w3>~+dj+pRRRpa9PV`LQN{u4>E#mRO8=UB z|JSZ9Nq;GO;CI-&y-y!{xz^`W*2Sdx}gf^D|ka_&Pu<;f@yoTLLv|9Apk?>FHN{ zQo8|8g14;Usi5?o&@aB1DfO$5uP#Zz1@3)P5tYF0k)Rl}V?1@-XI|BP^lf=y!bbmC zmn*Ir9ww~MZPqs1eT6sUlM?4QYU*&}iSPpF+u}!IE&w(Kz?=-9O3Sfl0L_u(^07Vk z_ooE+FKC-+kA*jEinC{C5>LOu!ckbG-zK7;9)VKpp23%qoU;lio0lv}t=`mAnG8T z0V~px%|RmiwEL%eeYaN}`#T=rF{L8UvQa=NDazfL$KuV={%ojHzM5*T-E+?a#<_d$ zh~I2Bz@IQSGJ37t9|&Qr(I7J(BMlT%dHz^p1E6ofzCpSI28=_m6{auef7k$Z3IMY9 z-O)4pvgq)IMF*PqzEKY@Q7@yfx>Z(>6_H0MtvlB8*& zQ0(@~2xq_$CS4>_*d^RA023RHd7FT)`Puc|C+WGU3p91fU@Om|<;wA7i)V-h_&2)x zjko7=Glgd+kve-?S_6M@k425R>dr&s2`#AJr6)}3Ieaw;T>gG|TNM(J3LZ9HKqa1ZUObVP=?@AyWV4KxM?a;+*!fSY z{+DO@NU75K!y+OIfQJGh2uvxXBQh{~?GZ`e2MXv79~QM~)>&Z-Dn9D{GXJA?)I}cj zmGB%&$~tM1Auu}7*3sP3eH$B5fv*4>i2mJH(V0ICfB~+d30nr6f{{Mi$5v0Lmvq%h z{oP`-Q4v-I5gmIeTUoU6yFSx8jHRvH0ZtSW!Rb<^Jh%OOi@-cl{82ZR%$^}Ull@g`)xp{z6=ZublYk%lWHSKI%{0FBG z1QH7>KQF!9W2D@qzP0Jyjpw=j!u{N@82;yIY6FyvWhdZDw>_&0oy+$zY|-J2PUzlq zwt>r6PVU%eBelVnDqs~@*}O$Dw)qUmU>oAxVtHyZZT#lVmPHOfjL)_{ex23q2mKI6 zoF8-Q0Mi)t8#nxcw|@hdsu4jPI#${IRS zPp&-Crt@WP=d&PY)m_CE-`8m-PfvmuG>*MkXZW^Oe^gaBui0=EuG#FPQ)Yr5ycuMA zXkDbHzd&$-NEhYwlcF)O=u&^}YfUn!7PZZZ7Boz91a1J>Y{0Xyxs^OzXljrRmkuX| znnIFgrr>8{8;bg;3kiyXz0UeL3yEKZTuh0u!qSu}ljC2gT=6L$hUVH8-%(RQ{%Epo z40MVb{YFbV9KmcKxz^gsA<+U^W9oN`+MA0*m6os7?HfpQ7o3GL?4+*Sg#h3c>`_m& zAoI0C_}MpoDB1*({R3cA!T#Ql0xPrkTUA8K@e@4vCzSI!a=QTI;JlvA(?tPWcy^@- zfbzKL+{JO(kp%JWwN^d*H$a09~Oy)Y(Nx(&-#@Q*|v$o83fcAch|_JVRuLOX$Mi% zaz;aUacr4li_NLiKU0gFr{Wr}&!-}OmtNlMKS#hQvL#)&+xs=?G0X&}*R-p&DIR8= zPn?+!Ju017yO0;ee5_rs*4?%*R*5)!im3ikuRex7zW@JW50Fy8>uMa~gVZLn%Rs*G zo4HtL;5#v(AyNp|&A^SK{FRM3R+cFdlBPXW^V}vMIrY^i)tTcsPa1E~CwW6J{mIF= zS;t&~AOf8X5wlSd6PV#)<;{I2E1#8B{xMyGykTpB6$Ypr=~SHCGy>TV=CjqK(3dLE z$-%S!l+^9r6mO5+X_8XnaPboq#3ZW8Jw?z}DP@y+kXa10n!fnpu>T$wJ7we~gSyqO z6T&UKl%tIMzJ2D+itce5Xo&R&Ap`D3}_)4GYw`iElb1|{} zbw}{)=-ET$w(B=o3tcBMF#l z>zhicbcW2N7Bu$qj1cQrxXwMc7gp&TbI;Q{g;J)9X8l_VNI$rHFAqLlnLo}MWKb=X z(D(U##(zC#p`)1U;Bd}(ClmZQNorR3@nfg>Mn74j?$?KVm}}S6tmXwyKZlD8tm35w9^h@Y^J(& z8A&*r@Kc!rNG1jsfoMhg22HvmbT-swa`K&UYvniOcI(C~46?uxuRad@Fr-@rRK|Ph z9YL3tf~ql$Fp^m@jWlmtVUru?Gr5rFVudl+x((;K4DJhhnd{8wDDjsUe~%IdP(BTr z)*OXcNIh~TY|OSaJrTgye4dhM$)&^9TQbf6Ip1U=A7(;MMD=pM;%EGK>UK|W9Q^k} zOASzl>kST&F^3JxiB=Tqc}ceull8<+sieyV;a3D;FqBGKkA|VmfEw%UXrR4jNR|%u z3dEN2CZkzYZi^!=FHe!Ibru@0XhXzgRRK)BC|*ZHf)>$;MqC&z9YC_lAn73wOJxjm`&2KsznkeSIo4)zB}M!slcf zWkIvR7wkF%YG~bA+-K)BvX`R3Vovx=70E#JlsXIm(dT{FX~GE!JF#~Dvi!6%d-rw# zoQ%vaHJM4A8owwwUt3MsBRBHSMK11^>AxAfSbe2@UD#ohPMV-caWPk?l-0$j{r0=! zi17bPHf_Fh+NJ-yd+@&@$pYykaT9Z4jP?IT+)NQ!jd^H+P@^@(CS&IY&^mymzhp(x z1M4qlh_n{{=x3IjWvt@5JQ|i>c!%)ky1l=hY%#pF8>%tu zY=hpQU1CgTdPf`k2t7epbZLI#NdTYeYfas+CQ4=j+?K3LVTY*u6xlvcmlH){aam}| zBywLoeMteBytIwN1a7q!F{A_Nk0s`F$f<^EC#idT+@IcmOT}6L6IXLgK}yW0QrU)sDwn zqK2=F-0Unw({x+Ovk0|)>WCL zy9NvYNt#_@RS9A~KN%umgDf_;LwmIVa8)F-b)qRzKu}HGTOifXYSiYqBxR)RL(4s&%-jEdlQQYYpT_eaw00Vr z;|78Wd_q}7{QrSA{{c0ZJF>M3LiwEaWa4&AC%Xl$IuAwARqsQ|$%qpRqWu^=M#P*; zln^!jC~^UmxM3tu)v^H&-s%wtPgo)pbLZ&uq`$EB^EL=^x<8*TIKHdCRE(>AFF4w9 z^v^`ms^~k%cW@uN?V7#M=_}~NMM$3@d>8oeL?~oc=^LGj$G~`-_j#jJ#fqaTbm|@0 z;?(T=GVFPYas34)>2>9IPzrCs>ifN>#Z&~VUjTrP{B;UlxU}$Iz1LVH%`;)k1Q-R<}=<`Bj z#E>De;g69_H(Zw+S85jIFi?b#{8eX@>W=KZ%~yo_LjKnCf)hGi`97w@C6qLwSJTkbgU#8?jAM!d>2*}q>^sc?LmBrqa zV1k^mY)#Ec8GIge&?7>fv67f%#@C^SK;7Gqr)KFq0IZK7Y@P`r|Lqa z#ZwBQgmq#*&)#*fEgPyCU7yHL-Yyq6C_0{GWvWy=7}Oh60&0Y87ir34egE!mvACjj?HdQtbY zkAb9h?nxtkI0>{{tLpb8MwTb>ZW+fV$}-vk5bQHhpS;U?HyTvbJ=#D&kUWroR*aFQ)M`kIff< zgrk=JhaG&nv^`G$_Cm;cbL+*dQG;>uaQ$Yk@k}U*&r&$z#ejdp5ge@KPxX3E7MtmRvPHObE+Y}b9Dlr9t~VW{aYSfh0L0qQ%c&L6=#55 zw-Od3URRDAh`8h&@AidF4x92XCY|?ItR>$aHczG05K=Cx*pvL-33IFKF{sl|JeAl- z3ZSpZ!aF+h>yu?q`dDrvH0I>>MzfBA3ygEmEHM$=2%BNAtWx!7r{iTm{_f^8T@(^r zf8EG491z{Qhud+0(FStCWKwoBwiLivQ1S%!2r+1I_Jo1;-|9;*{uc}HaVLEKa}Id~)L`nHj^*G20T>#Wg$}w} zi8g|y>R6UMjWVTz41ww(ph^RF#uIK9)6r-?vbfjN3@e?nI!7`oseJcu<{Hk0Zo}}t z)Ic7xqfc%<^8>~HJ|V8hCse!pW}W$-LNQZ<$& zwuu>Nd^FXG#B|U>@HF`HN*NP0wex7TY7v<5v(ZsW1Tb>tld-^n7<$Vc=Ub2*{-pcR z6G?>$aPk!Ns)dZ~M*XtZl~SExD|H^sv+3R6*6H;``uOHs1A-pz{)`NS|2 z={_W9)9YY8p87DAJZfbp`fc|6a>Ex+FdzqX5a#IEucF3}1uP}?ew9XuEt!#CUmVi- zKEIk?j-Lnupbi|+dd>b-%l5wUT$QHGcqo-{T!lQYfW6!JUH;ogaySP67f`03ETjU= zY&(3cMY4*b*`DA7UEo&CSukzn2HqY?Jssd-9Vp<^yk!5>rW*V@+g^R363uh{Z1x45 z%4lqC4STKz!~i1DkK_xcnb9hY5v*_`=fM7k6@L+`dD4#7 zf6LKJ(~sz}E(!S*T)mVIcBx1Ma;p`!_3RI!pi_+w$ZjVxTJ#wI6Ck)g-c?6a5xwdE zJ_n#afVfBC1Nz%#Z0Iszi2>0kIn|X^ns81`Tzh{?0U5y&h5g7nseaqGQ)wDdS-nWClESB9YPGd3-?}}PDfUvUTo)*1UM`>g>^^7*AjlWK^s|8OocR0D;H|k z_?8siMrG9qVVsgWv^+AaChWF`zagewH@e(kO=Z|@76XjGN*15`+;Ukz1wvXBO&AUP z4kNR31bMoz!7wla4+6ADalkK^kxhE7gMLal{1*2=V%!z#KbK$hk`tH~h0CEw{^!~F zFFtw!Txz%E+y3YO-NClYvrj|wcYbs$I+%zTv@YD4C!vLd5dN8ZLQ>?Hg2t890D5s; zz!t;=IM$`{wzj~44{k)Q9Yv4*J2PX8 z`39PZRO;z=@J5Q>ZVILbfZhVealzAR-x~CJp>p7Cl+z9L(0Hs%&JAu!AlJrvln$al zScC`sLY1O|uwnib5j|D9mk0#&<$(Yk>+kPx_APtJu#TT>BV8ESy`}bZQ0c#F1^q@2 zCwU|L^Bt338D?NuqI2BKP~wk8r|b{Dcd6$Ru>C{uL-c*-iI7SvEK+8<%pPfjN+sJ6 zSZ9+dmLnT4$qj*jLxP}3-0h&qwKq}V_p5n6Clr3Y9WbXV@;n6}_#|OlEj9RR-bj5x zqGUsj2kkEpTAfDkyip>5XV$7G6D=gJmy=|R>|x+H;%L`b8b{TXwA}j=*}xCkLRGY_ z_r5~|WgOfvVtzexl3U03B-f4;RsY#7K0hk{3M{l}fwnuXaP;vc2Z6iCrT3$iheB0e zZ^plKnjd(NRvumA^Y0K~IUcP%Hf9jD7J!up%B-%x#-E58Dj!`+(d(J^)d^MSuG+4m^Bikwqocx|! zHKt=I5x}aK9N8WWu$!W|3KIh?75ZXO@MlS}YGjn50cL~8M?%IN4h7oFq?3gjbbv&< zhjy1C&LHLi{gmjylqGF%3Z(ty2m50S8>avQQtq@^XJd>hwa3&9e6#)iLl=5CL8Mq1 zjk@FgEM-XCBN`^Bc(1Odh+?DilqM#~vx@meo9{K0I(%sGc zfUJUp-NkPFbo{|oN#%2KA>cfK<+0}Nj&eYc zo`lfhY{wjLSwZ4&=e<(7=iiD>sigaZl)~_!r*Fu|tFsHW>Ov=GPojtd1~_5f z2t3^O0X@u<>)*54;}yoZ5ccou!mkd0c-$5Kn0iUM-G>H~exO=zK&I)C$?I^C4Kx7d zeIsPDw9aFis-;9@77d(Fd2#!x-=5^Z^CDPivfUa!EX=uJVW?S8f3yVfLKP5sWa)wv zJg`8gcI6e+0fa_2PMWk*IQ`&kd(1jtw^h^R${H1YlnI?touI!CnP9T4U>PR|f5CjY z&Bt_mG{$-^L!3g1;wKss85RChqek z2jJ`W5kTx^iICAtY_~^WIvijq>3T!20Ql#}NcNA1I@AZqIlwkL8V=PpA)vr6I&M?^ zjRO|&K6zZ`$3K$w2#mUlVXdTOvXIupcU?4f|Dblw9a^0z7A`POWz?Bb$z%|-s>BrB)iRe4P zk_8tFuR)8bpK4*`mePJu>@Z7P_=28Xzi3%k=U$f7>h{PRl9S1`o5QJc-ZkjG>r1;Y z7xOhu$3{QLN6=2uAX;dGr~N|LCw)V5%J%V%V#S|oC4pUT6vk;|_g3ityu**LvJ?T- zuAh-erC5LyqDT3+^uKR+coN#M@Wa}8;tbCJJ|0EbJ@jD*0cWk3s_2sa2wN_Ran3aM zc4Mw#MAZSU<#x^BGuU>}MHO$3+Ap@wr#@6Ot%Dipbg(u^!boCFm>_l%7xjIPNgf?= zx_FIwYDw%}kuK_2R&c&jepX;p^f5B3+0d8^Z^!X-(?Pl`bD&2+#(s!lIoLVIF z;k1bFd!U!{4SS9_JDwxgIg0;$yu$Xd33V9KvY!bYP@PvP=Gpe;CG}EF`+$HE-Ju$A zcrsB1Z^aM{EGfPouRKjPYIQl9`oI!i3x{kr0*J`m1LEcNt>-xiL%@AHuAT#-cHEYR431ejE=^w zRR$bnkKKZjF4h;@O0&w%mwJp~JgVbo3=`I2z5TwhAIhD)BFznu+_;=HpY#%pH(U@2 z2RKoSj%0MkW4-pyf15|0)oe6?Kdc~V9y2Xv%7R5tScGxE8MdWi&fvqVUq4|@Kp6fNxn?D4NnvLmHCF|eU+J)>?BAK*-~T-q8QNF zP$`wer7rfSK`%6Hcs#G-D;9YK&}PAj&(r_wQV*|0FLgDc7LzG{`e+vk%x*HZ#QTOr z;ww~k64^&vl%Owos%@6{=&o;u^li5`nQV8)t*AD|5j9_BKEB36dVf~$(~wEie^!t9 zarO8aCnCwi6(3hmQIe+E8myk0n^*(q-_;|Sf7pBBIRpBU2nWi-*5$tScDrmn7Lj70 zxS%{Yx$>lgigu1koi$8*@^`fnSuNb%LBqs~k!@$-;djlN*{_4}JU}{t0#em@;42^v zY2kkxsqJ=mE_-kzJl?l4aN`ToT})c%#!GOBD23qL#U0f|yWTtb4LOZj)O0ioaSAh@ zsUcV5s26KFQL_!=p8Go=)J18J4_2pC&$Fl>;n&W1gv|+)Hc)8vS&5<9xT~7bWFw!TjH;?4Fmtc=~uWFDMl;BAp^bMt$+ zb$>TH2^YzIBInQh8h`*rbfzGdoc``VpCo~>#rq-H{ar<%&~xQwUjr~sGoE};MigCYtDA3d%kd+w595R^P)aS&oMwheD1(7S?{Ij%6vQF^3-YM7 zgB&DWX2pwxWRrn}ch>ozr>)Nhc}WyKplcL?rx7@Io6aX2_NdRCsr#7Hfv>Gh3Q$v4 zXwDsgJE=q^5Q{FZdn)3TiA!AR=66k*=Yl17ihxVt_wBpuU6v$Hs`a7NupWo1JpG^Y z21O*yDS(SGCbuWD4F)(057c^pA{U$15a{W!CO#g}vyej>psvSa1if~K^+!au_E5D! z9UdDzQlSA`43UXLbp7%xTxpuWh?Sgzj?_bNVaL~St`aA2~RBd6MUL1|m? zpR-!x&sohRbL&-!x9krOb@i zK!9QoY}7| z(<2@~1Ry_JCs2$+<#9M5OqZn{Bzsieb$eLrUR^ACp~~D9H=u+DOKN>fa3S>CdRMB; zyeHlLjVbYaUi_+F=d)2jgc~4>w{IZZcv^v|x8O(Ox>DtKbz;~Cs|An+jaG_!UiC|0 z(VMJV?IQtLT0POS7>ve$4cYr<;7hwn9rnr`WMNyti8_{63&zUIu+dxL6Hf)Cw}N5= zJh>EsOI>fuDi!0mdMUz6HUC~AN}$muNkANlT$Aqyqay6{?~Bm02d&nf8Nb!)y#C_v zs?p@{dSg!GrjQb9sAQ)3ce$77AD3GV(UIw&_5YjDz>0xe5(DislQg?)j`KmjY-Hw4L`2v`6AShuJ6ACGTEZ)Cu`XV=&=+)!(@QloIzMcde^`_qS z@7o&=UAxW9icA6i;%rRB{>SGjn!q?vtY*%dA&e-8rjSSwXeWxa z8v=p128%e46lPF;=ZsO|b+6V3`wN4@-r!fNW`ZxF5vOZ_WINMnU`8?<=sopIa8rk? z#<5zj>3%g8bkjjwfG@Etv4$EJS@H8sD8z^Xpxy z`6uRLGInu*fG8x2W>N`6ZQH<%J6A#QuA9rli{jx6Fc|g*CUki0n*}8(={il>`PDqYq zPywI1l|3g$q({(d%Z2<1u){U&`$iH&drhLa+*i&C#E^PlQBS2y?)IH8{5=cRH z!H~H6G2k-$T?zAgC8$jJlkVm`+6#Ks;7m}VRtdqcqBS1WvOQiA*BP!5?MPtd++^`J zIG@3*{P7?U%&4_sza?c&yzC%mlR2)L<$R+kq_|x@TVIZ+PJv$83lb!N8jLtS%my`& z;qC|$Kf)=b3vQA^PYZJ(fAzRNZwSKuQn_~Fr`fT(1(s9YxJz0s*Qe4cc#Z=0Z> z5`R7V_~rIESKa-P5A_l!7aWnix^nrdMT63NDXmNL$ulFMv_w@TIKmk2q1(|Y6U&Dw zghyEJ3u2Yw%rF63m{96w&=D8O0OPhW1-`kP!aH6sjR!x!Z*+umdANHPB}+{Mhw`<{ zna~9Oyf>1HpkQh!k`nkNWUTkKRLR}{lfYn4T!Y2%%O2ban=(#yk@kWYG}lZC=nw)f z#F|cNyg>mgt8cLqSwjT7T z$C&nZaVk0&nPQB&zT73~yP=BS_ukSRgOrhP_?TtV39>;%tH{&^pEfvPeoEvp>hA~& zpXuhp+HnLox>IN(q!fg5CJ-3TQd}kV;R2PHj zoZR@kZQ69Un&0K96#=4}yI8cmaf%b&Z*L7CNC0tzTMppsm_9EC4FZL&cV&Y}Up$%j zBKku1>lA6STX*UM&hL#W5d;yHoUh_Q;&!TlajwsGVLnyNALqx$XhL-R^SzMom%H;C zU?xz#@D;DY>p}t(Tv9UpUu+}U_%SyDgCR$@%-vg3U!_J3&2r`tjCig*Z7M-?bw(HR z{%MC;o`MM4d29E(gqCF%wL?z&ix#_+)X}3Y*JkWdCZ_@1Xu`+$?hqkwU{z>MP*0io zsr%0@v#ocB%LMz4`7T9G5i5N&g`h)rKVpQv!2aKP)Zg6_@i$G#2LV<+?VF%DznIXz z*pi4|WIRiFg$~%1ZnxvksjX1n1FREOFfwdN3ikqeWDks%zOAu1cSr7gLJpT9%X)=E zA|M7PaH32KqkZYMK`YK_{UIuvMnNZjuSBhc2NQ>n`9$_!qhdqZ=UEI$S6R06a*4S{ z7K+LaD|PTp^0ga^!%z08DOhRi0R`%XnDg~Ejp^AIOQT`kG}}3_UCS+G6m-4C>+Fm{ z&c_+4wNkmhJ!Q*a1j34sbc5Tleo^%wwVtfP1mUt!Qf+Vx%Z|}PBYL@y3A;7SRGQC9 zVaZPwX|`K@nsSQL#gNsW{<3^Djp(BRfVTqJ{zs%LIsJv*!^v(e7hT?Qep1=;$$V@3 zfOdxN+H&g^_43{cW7aBdmsIty ze&7bqbOi?s1@INsy<7?hb52(cWn@t@qa!6P2g{IfCyT_Wz0n4j%>s>zZ{dr;@tC>y z7~B)+vH(g$?6~B1)`97@rs+CYJCb2rX|t)I!^b9%b?^&0c&#@k5r^qowd=XiLdQi@ z)ZwJ%sS`uWa|bYD+k5dzm3r^C+htD!%QWFdkTU~eBI*1jU?yPi*at<<0~y-dOq)mu z^wVeMZJOHD`}4F}Z9P#f>=TFkJOrqtDW*1P{x?FI6-<5dK^1wWs?`~;A zMKoEg?e{a8`beZ;40d!O7znHHZfb+2=3SjXd^zdKUBBW51>>`sPb^O6D=xr?3Tr{X zAsGthAf~MGvEp8Dh}1xdkNX5F_iiEo-6v4R4KtZ6+}=YrcXQDx*l(WoBRXJHSP!;LQdLo#)jB)@BfD#d5ownBU^lfiD$OH>Uxck4o!ZqxTONOsS@!iMa z%&RYd>M4O}4y@m8AMPAxJp%)hNL+u>T5w2&-g@geE(c;HlO<>%+E+{5l8df`AuTl2 zoZ@K6BsjU^C!Q>vmMwueyi(^^oW)X40yj?M=rw-`U42I8Ayjw+qI+8^sHLd|2zzQHUr7A-Xp}vc}$!jhEKZs7t!d zExtrgx@0@N*kWc$F;S+k7LSn1x-3J|>7OM*c?8ic3{BSk_6wM;3+gJrQtjZAMbVGM zgOaACQMi9m!kd&85KD(H4%>jD_imuTNkJ#%w}hwu-wWnjb$crdumtFs?6R~BV7+GA z`X3fRC9>M-0&;HC)yaA;VB#T}8tsf9iwsuGw31VXH>3vw5%<`yB7-H!A(*#kk7r>S zqGLv%iY#4K=x;Z0t+A*HdPd;?l5|{M6twR^O)M0{(9X|7&W35f!D(}Mp@}BSasg{} z7K}y8XG*wG2Nk-jvFZ^wm*BGKkR5oFeAyBJYC!9^WL54s{hSR<&5GjeZde)TQY^`TRPN$20qDH@oG&9eS*vc$?Y|+V_&Bt6a)PA0p{>2I zDPKb=H^af_5MG{7Oe-s;rG29BKh4+Y6`sA^L>}pF$>&qaQ{{j;wR#6eT_y8!K3Ud^emrkrn))y z<>@q0GiS&f6)A~iH z*yXL**w1Vh&=Auk%ItO}RCB&F=3^_i)LAZP$oiVk)#_AJhdtn!QW4}& zwaA;M8~r^ent|8xzx6Oq`s2rL`HCs&^Gt@8+gpNm1wQfUHe1Y_rRHSIyLidI_ms67 zlY7yjjaJHQzelSOxVKS#N4{DIBpDyYGZI!%`hWg#gO8i*pv-+$yDfVA8_d@y(CAoT zPhb`h?SWwll_TVs4IsW0(3t-nt0F5$7MIj%m%#6gQh(e<(8+{9cfO1duCA(T=_>hR zn1<$^iCy_txMyzx%z*3Nk%h-cP-9qkyuDNk?J3V0us77hAi7XoZWFHy;W}-KmY^<3 z&xh`0K>G+S7o8aR^i#>$?$9rGJ7l|o#IunDlH7AUo0i(nUm@mRvD%BiUg#-_01AV4 zD7igmjMZt%7iLwo$~~VU&dYYwiA!>$byM~^Q*$Bd-Eb8KNO-gZks(RyvIU^UZ-@g&GR9S2Ee!D&VOI^?UzQS z2{9lDDnwpkp1K1knv~tahdSduU|sLANM(I|=zO~<6t(ewy54EiOS9E<%xEl}tb`ri zg788EQ7I2b^?p%Q^)_wL8&XzR7>QbSYvKobw9Ak_T+oKXw!N6YyL2$|!$RL{z(#Bv z*!^`?E+Os5lHhDPYWhd%;w6x!{X1iVSDSAAIT>< zas*CnOAVEJoz`w+w*m>Ji!n@^)rRM-jVI2{mdjiM=K-Bf*hq=rUuqdun~q8CKtSm< zwywfh;q=NLfc=SGb8j{Mn{!5}_cO$8*5{`>;Aa3KRc2hU#(Y++09ZOkGtwVh9L$27 zY`OE>{#rB0)*LdF1&Tk^R!AV&AKu^^A$$Z~s=<6;t6tP&c-oG#L|0U!d=Z~OzTaC) z!9{%wZvaUtjLYf&oQWv-XN0YH+$p=@lr1fj{wmipAa*SJ7KUw=!4a)uw6KKjmKnPUy@xVY#5W(x(fn!5`2TeB6=5TZp(jxXyuUYrT6>T+=!08oS0+dqlLnmc&n9Z4Il}oCzH&XN zi*@{Q%Y40G*~1JB}z&+?+ZU_|?2*!BuaBu))_-^QnvD5hsIN%cQZXfy8t zwyB~Rr#iPo>#X_E7Z{{vhJ#7sW^nA#aAQJmg@1I&0`K{_x5~0}D9Nv_JOf@Ot`puP z6W_kvUue<{9r!W*hQx(*$0d#sFt3x);Dm|Ae$dZ=TD(O;_rs`633|6@iY=~F)vc+$ zOH9Pl2HeMArRk0D9l!5sK4i-!ezQto%zyCrgMWgiGE5yVx#;SQfK0fhWLb$Kp>N24 zeKM(u1TrIq2;v|tt0Lg2sG-F|z*uOw)BJcoA{Y;oNOJ+pPIPh%t@c*&q*r+4P?Efg ztCFb#Fi{T7){$<8*}S7G_wJf`Uho&16}_&Y`VoJts`@(u1v8$M)ZN`f_l=HdBi+T# zVXV^CiB&Z7yqw5J8^;rG3~w+;U2eT?pn7IXCAI}5QpcyiNy@@b6kAV7v=N&}DNvQ8!fq6U9DhKAYs0og62Z z#|#!mz-3}N6K|y&HQfYA2?d%3lo36k*H^hnTG$XQOTO_sG>4lW5$KD&zB4R+=0n%} zU8ACS$)dNyLO2?jI|sj#{++A?fA8YczZ~u*`&71JZx4=Ftw5EZUZ*iSuB-u9A?fvC zl#2>FF@eZcSFd9bRv-JZLgGS4k7Fwc+P6FCN<^lS`-^&-{tqHUZ+WlSK&M zu``5%%>Uu`)ZSIChGyQw1f`ApZBAP74oTYk0Q=Qz=n>*{%^HF7bD9m18YvYw2n}f& z1O^p!9m2+}U5w?2Q^A##xmhe;1slKo@is^Hg^pO2I$DJ*ss>qyjo$90_H9ad=(`@C z;fuvLv8Axqq|La;D5JGj{7$i896RbQ86F$U+}FwKc$d>A9+P3rLiPv2t7e~BqdDAK z7W58Kj;cmtmn@6>iQOwCC8RVIO3+4yDh`!-!q79UCtWW*x-E zks``!YxF*1+7)#ny#dH2lEZjtu4+;=g;W8L!b88B#f8*Gs{rhJ0de~SQPkFp;dHNC zW3TZ?e1Wf7w+FcN(Q3sXX!f;Bbzij5 z#8UDOVacC-*fpI4(1ybP*Smo8C>DaU=rFnTn;SAfV6s>=@UeZtxU-kJ{Pl~V=zY%@ z6=J-sIy;ncn^l^6IwE{weMn$2Bms%2~D8>pWESn%lKsZ!Pi+y|4E0cI^nD{RLPk1%#48^iEwgC6Db$a>3V@=C)#Bh?ZU#w-+- zU&HC`j-EMw6l&FRd-F9A76y2MjvaYoyGDS(8U~K8)KlG|M1pLtb|I$I_<9s40vB_n z)KRkGot}7Dd!yO#;6RH+v)MZWfsJ`;gb0=DhN8CP-1{rwGu zy2=$D;@O3U$a?GueC6Ylxab`ealGg|<+y~kPhcN7Wsf|vfz6dH&arHD@%wY4{h2D2 z6t=j%P6L5%Rnlf~F1W{hvA^A7`#Zhw4@|p%*)IdKK`Yt>4 z%&WQBGkA0aj4yMCy;+~E9QjPKExIa!RSjKNZWl>Cch9cw(}tBF00blv)R5Wg_-e=^ zIi=nAjaEb4VkNgWdJNtsl-rwUPVHpyWF(%ZvB~qgn@7%YR9)H)t{54V2(TVr{nZ_!|bPDwXojKg0(oEr?^oK>aU?h z)SACO9a+b?1zn2I`nA<^+R2|O>MqFlbA?>+2G8*di!6%azt*@9!(d?GR4noTkUb;F zpu~E0hg~as^%Myw0}PJ$G;kJUfBFHzoe5qvIBoD-%m7+a8Fn|llvdopS`ejIC!8D* zkdsQ@gKPh+>q8Lla^vZXq~32#KeMuZtWEBS**D9W-sNLmv@0tqGwIx_-WSo5ExKI_ zbnuP=l|Y;$Td3s5tvv_h3!=EB~WvgMu#z7C$?<@TR|X*qUfar-@OqN*`O+|QxM^bp>n&860o;be#3u~zW_#m&cLoAwjn8q z8UDYOdjC^4^+63Pu=%D#&0|fiKiw_gE5%<);CRU zx6P-8c=551JycDiOF(0XIaoerBAi>xHpHB|dBGWA9QX}?Cs(m<$_p}NWB98jag>7l zs^bo1@F)^~;FxWKXkndz%YZQqmLR`Kr`nHp1cT)MJK7I!OO++(!)aYQM84dG za6HB=zWv|*vOdHsvn$=G-xV{71@WL7p+@%wTDau8x(Ydt{C@v@Rm#W*I7I3dU|nM# zc$5^7#Af#8Q*jvXYSP4pz5n>P*B%+PSBoBA`KZRk)HZ}%MZ<77Z^qCyej0N!!c06w zkzAlP`Iq@~`5eMJHG(_h^s|*SvB$2AeIzP(FnB%2^kb#@f9bqF(>1e7#2;mire?j2 zGE5I)WAIq08^@%$R>YDPM+#v7y#%1r_6umW6-u%v7(voLq3-z$!nnPJ!m)+Awb|m@ zG_yyZ`%`<)$}h^XCyLx6Md7I@Ko;Z9NfXEV$}=`gmD|v~jbUB(dhXUF-TGr)YSk+R z1+5)U=Uof7k-CC=}-u1}u_)l}@s*Y}hv}hiN+}@`fBWO0-p(2yR zP@`l^!gr(#i9F$w_b4Zccn*D2ISvI-&3{2~ph1F68B(jmVx5rhiP7XVDp`_mG5h1r zJx5Z##anUr;T-`YH8Y7U+hR=k1sC@hmlwDZlA=%8_9`lgcJ<17l4^Dr@7Ni2H^{6m z^SCW{GjRVLK(sX3H#BM2K@D$ikB)`7E@zE)yK348TbC9LI+ZHGfAU1Rs>*|_(_nK% zVEtJr*Co!$EyPS9swz@L_x`5+{Y@Ua^Yi{@d>Nr^1>b9yv>jF@q6GttGFWU>Jhi>0 z)=cl!hr^U06+@sy3M>m=dh4w@?JD^7O;&W+;ngO|$-yO+t(Vv%_#>Pyez>~0o%vaI zW!sMyZS^2<`-xZQO6r#8X=EepraD7WVbq#Xd)zy?C(U ztmz!KIKHt({|VdGG*?~dI(Z=90LXDiqEYS+mqYBZAV<^DQMN|%3>gtPT@RVUq`^~Z z)tva?xAQ;(cp{NkhaNcB6|MIo#f^+1^*=$i7lvDGa5CX{Ae(>J*?gYDxpdOe9_i-| zghpD!Ww1hx=b;YV^Y2!9B3+E?Hf+Xn5;Uzzp0Pv$4YeE@cDV1uz0qy9%fY=$u-t4LxxH_-b@(Rc7h6!>! z=2Kb}K2mt^$A1ZgOoNxi8HI=9M(CF<&-t8TmI}`(ucvfRdOTXoa=C9d-1a^vC1Q@6 z{E_R(ecJj@#mT=1o}}-gG8Q8u`xxa=H>Ng+XpXtACg8I^qK7cDy~5DU0lJdosgNN&EMx8_lMHSHm8(ijQ&aB_mlkB$-g%tk9vAt##x1EL$xYZOA= zrugq~oX&McdSxRSz*q&JFFf1y{W zAEs<#@OJfOiHy-YP%(KE`((WN^A=2UDm!RJ15uk61_A!CY5p}o36#N3%Gx>Eka=k%`E zl>AdV0y^PQK))vTn`ZZJ*Eoh(M${k5KyN^V@!Rz1o z=4efq)S_ymzjH~#gdRb5fI{OHrEn(1xb0B8Fr~ke&Ej%f*GpAkPZfv*hLzZwQ}@Zr zE%uh1ihk_t#DcecjmhWyI7L^#%(3Xjkes(kl?=9L%2U&78=41_*|`;0s`0T&=M9Y5 zri4zn*nMb(Rc0Gzo&0CFCkM5a&G{7h7pc1t^Gn+G%k)|H&^J0TuH<;V9irM{DSLsY z*cJDDcYw`4TberN+=zDU#WohbJSDU$3;JFi8As#YjZ)FlrRtyXFg5J7yrfx%i_Ftk zH8^5Z=6G*iMw}{hCoHR;Jx4ThX0h$gdHv6EQ{+0ghuAq23OY~Tt&Pn=#a+3`I4u#} zMqv#^qPXV^m?z}c6a%PF{brxU_c95_dN9X&iR#0g8NHhRst>{3Ja=)^vJ#|kG4~5t&wbc+sKCR0=~{@1811#zhfyBm|hb29*0HINd1@^m~;@wE=EDC3)b<@0Kajlfi9Ly5}(GWR0w z$}oNAn#jZnPm&e!*^2Jf7$YEf7&&lSk^$1F5 z$W<2@e}rKS0t=KaWfMpcfYFTk8nmOD=YF~e*rgpI~U+anh zD&}88#QWDO?x8;)LTN|ui$aSd&#{=+Yk^C*(LEf4)Zj;s@ zv9+J1@ozHrzu4m~PX>J_gWpvOYDxGN?-osVc{(GsH$bvy*4#{!+v|Ewk$om5Qj5Nt zEQkL-Zl3GQrbz#v%ro5j;snlzpQ2+XG;>}2TAWO(H1dR_uqPO947Ya|R^5?1 zU;@EZxYJB?4`CIr>})cA_RhljaTlL=ML@qrBC&lsU+rdlAwz-9{g$!JkiQj4-Opy% zqc3%S6^;+vZ%Fcba~Fb=f<1eRLmvk2+)h51p(c&3xI3S0$OGm0-ObXljq#hOu@uzk zyQ0Jzqix&sAh76XmWZNj9NLTR`2_2%gh_%jNLBw=waIKT2A#&KuhDkX`WLKj`)Z8a>O7t-Orm; zS1d*|diJw0`n{6b)&u8#Ln~QL1j7?Y#leJ4liKr+WEKhYZJ%{80dDqfaP*(MBx;1l`;Y3z;e#v?W=Lj`A_l?z|y#SGmnX4^8^> z@Tsm3#WI_UBadaie7s>Qy8SFk%>eiUq&TTTXuoU`d%>xa&BDb6dj>sOj)P@!WW1jh z`5C>NbN$r;?;L*l-BP8mICZD3wbYOw+ada&%1Vie+Pu=DRoiRFripdW8})gK3DxX8 z_QTBqqALg?iM`g5>V zy9*-QSe`==JZK=&`_0nV$WEve!N2rA`Oa-a&zRn>P6_k7Yxy;9tkzUgOC#2&@rj4? zU5$^Wk4e!yufA8eW)kOv)9Q;MaAkDHaBe+TFp^`Z4~oAwo+ z-rpQ($@dB3i5nSB05fD#*DTqh+XK?6P)l+)0(_vyPOV2fX;+io$yMTk_xljy`=thI z{rrQm-Py_$@+Y4>2Dq$K%IPbDrQ<*;^n%Yh;ew7&Lbr-a1v|aHvmx@cJpakjNEG~| ztCOV;6#I2k`v`T*@p!96i6Pb+S6knmow~Yrav4RWU5#SFPYyfUM!vLI%-fmp<18_F z-@EzAhucjOqheZVGC$#VmzLiG~NKy*3m-vv3!JtDe$#X zM>vYgmx_n#uHu{GnEZD+lzYM<`@bCsZ7;xqAn(G{`MS8P>Cg@}o&k(&0lfN<^p54( ze4$cyJmwaN8KAJySN_E7Q~<;b_t>SfL2nU^4b}(NSJE+_8YN-`VAfH0FW-Y9kk--A zDoXE2kgD-w3`MY;A6d&7$$LK~qdb6RC;7+`t7%@_&U96XBx{=VAlC@~N<`vIlE(cP zUHh2R$CPhgm+R$7D-(+wkTID6V=kS2`%{3G@svd9Oj8?NYAz&#YK&WYOFkJyX+|)! zmU4oq2oz*RfwPwj9tW_yUgq3DFh^k^h*NIb@6b_jfkhEiSs2ldZGC)yJV>m~!xZE7 zK*((~P*T$)U6ha%j3kGYZ9Q4wHX!i^v_UgMy-8;1FRqfO#3YsrD3+anknE$doA&h; z&Ly%U53`s5UP2P1eDu?5Z%+L_PeQpD^Q7h8J%u)lRC#@X)3u_-k2YGQo?M9q1=sF1 z{Dm5I0R40{21^|@xhrnC@r-Ii-pqK>h{qy6vcGdK+12_Ndr~+ zxRJ~>|C1yTt=i~YD<|i(JAZ}4;Qzi%{Uw?NTWA%ASj@iC=07EkdX^RbI?)Z#7MUg* ztYWUy*401Ry{7;7)@S)1yhHKv8tpNJAS7usr`e`dNOih!Qpje9Xxr;?{x9SIDPT1B zciN`;`>^CwTqmOL{)blZWThBC0XfUsYr7s@zgY~P}qZuGQk-B9LBa9;YvmmGJ zllp+blI|O?tI(HW$E63=4!h^+qhaAV4uQ(gg69ZYW<~l=F-XHakGi$U^nhSexFz9k zTU0Z@LqeiCZB+Z@2?H^8dz)epA=cXe7}^p<>+Kx zK^yF;8B@MXCZ9{x(t1lQD-3_>bm@a{f2>vW?9p%xtBJY2KYB$pC&&Al2w`z(VzviO zl9>SR?sqr-n}nMxmxFz|iWA!9^so!K1 zzIg1r3HMAL5X3$8r(XLRG+55*qb9IqM%O#^+m)1zc%NCQ08P(G^Sd4I8l{%Vg z)S$O`=*Gmmy0|}hKh%jH9y3GeSVyg!<#>QKdoy8xV(mxfq1jkKC8sO6c7dqF#LVx^ zxCB_-z$j~}Yx2k4u0vnJTg-?g3+eZEQfigvW}50m+oG(adu_>NXhDQI>;=9`gC5{E z&$(9`^nYi-qLMkeV4dYv04Dg2izP4V~1bR4YtDW_!g6<6V|DPzv0@mgM#{&Io2982Xs| z-Tz^V(Yy}>ULl$!+w~Fr6U@_H(fsoieuc5@3?OsO10Z!-F4||shSim)>7$jOGL8uujryrVZCluTj-BO%Nek=bz|$ zEupt@jN$VEu5Cgj=2`-P6BSbnT(054w4B@Cp8{@bGa`y|>+7+8u0M}{l&Rn;cfMxg zVz;6jn^HupMZ0rAP?ZBY9A~kksJQe)C?4{G`H1*ucJh|!c2dYx3;}eiJ?#AlO_(iN zEt-|)`Whp2!?Fskp)j90GiRA~s^FOK-;u(_DSs7Y{W-lhHO!YOB&^Vf3m&~J0{54y z#~zPs6P>e_vPG-jCtLiK{3iRYdArNm5>jPJgCeU{H~n3ePg%Pvx6^TRrxaiMDPBJq zBF)RTAbkTsg?hW#Br(+OyJV!S!89c}DoHa0ZgvebFmFPni1y47SE3VAVzDmqncp))U_tPQrg;(Dk1Lu*7^&EY`TOJ__*_ z4p6#Nt9onizE@4A_DzXxQAa%88{`<}C-*n_GSH0~s;g-AvFzZS7b!T&f=cpAkrdHNGs`hZ>BIiK zwlYH3nJqdPRb4s1ie{ z&Iif72n`5Ifx>rBx6{1O#1@3^w}#8XMY@LS0x8xdu@|xQ>P+RZ?BAzFZD2#xJYYAM z%T@U{P(iP4ZO<+Q`X>u>OPKTOD;G~&&Bk-(4~0I;zW^$?6T}|m`Y&R^3a9&xBy6EK zw7{(~w-oSPH`EOzty*wrd(Y&GQO2KTm2FakmQpAX%3*7?;4$)#mKm-GlDmuhCs_=9 zT6ZyJ(Am{m``Neh*{{6y8o9*gPUW(uL)AGySn6#(dXxH6c}WuNKFywd9b+`Keakzs z-l%l#^j!gEk}mSj(iXyyEU0;3|KfWQ4G$Dg=#PVnO8WnZ{%pzA(Z~uNg2R_q2$OH(a)$rD#wDH(u3m|-jx;9V)zA-<~X7V9I zKUiRQM?;(aaZ>XI%WzNeHA)TWED3ezCp7YvVnHMzG4ny3qTn#oCz9Apr7nI-7^B}1 zTg^~NTDI659BfnmuY}o1l-O`-Pp-jxFr~qz)gM=}G*RZIoH;y7yEH-{PDql4`fWw( zBeBI)n}*N^=jIoUN84BhdSWvUI|F!>)f&ZfuaiOT`lMi=JQQr6C+0+4MP{|;LS;Gz zvKsKeDYxx>sc%KArzg5CdEy;WwP3)g4BB^5(J;k)}Plv%3`6DK#Nzji4{ z#w6gkaUY|?aVkep5~8Nd9>gPL?tWAtt_Dn@*^(c|6?L`I2b1|E^AL!@tb1&%3EyJ{58)uw@S9^fcZ#%oTVze;=L%>s0`3 zR52aq;i*vjZZf5r$CEs4!5=|jMEv%r;HKD%q;hInjn0HK9%q~)GEeALXYRXSq&>dX zaaP+~)h}+m&SMQTN~G%w@(Jh29Ct$J6AlU~<6t z<@meNWbl6r{jZ}oX{bzE(zw^s;K7$I?+Q32hy7#!EtJli)%!O1S12tbgy;r;bx?Ag z(v?ua`OPz7XWYqhJS9KL#zbNW_WMdNN$p@(PioQp`HTr{^7C_80HvZg^e_e?21e$% z_kgIbG^Sx{Qjo(@vgfA1;bOWa`|@%Bl8UvfsMWEgVSYml8@bi)~t)O)cf%#fpbcM z(;r{q94tVoF_WGkb%i*+8iWo(VrbgaSo$*j`g2j1u1&qV953Jtk%jmrB(c1DKci&e zBskH^>kb~PgwPF|NUI!%*s%kz{*5&fMS>CuM9-UQgQ*c6sMU7QJq3VOZ!YQ7hC#hx zpa9RFZ5CD~UVV>4t^OAX7>f{T&Uy*nNz7l~ZWb#=r`xfxckw562hdT&`3{ER4)zr+ z`<`Uag>xV&jJv6L+!J+#R|~!}nNK8C3E;#yr`YR!i+R$q$2_rwtZ}LT8_en&egzRp zlw%brwu`=+_FXu$rS{n?5m{c$hNVualvTe)rf;9a{rw!|#_;SzE*Kc~E+Z9je(6-G z=4G(C?HmkD$$NZ^(0Kx*@9n$&uAlzkRrxlF70=Y2@EN*OYd|>bz!NhfOPF8l=Y$Fg z^qkDON0^n71J8C-eLPA`dTrtXFC(-9`^0z_+?S~he6x}c2NGk7`MOzxXc`B?(-hLI zc4w~3ZC)e^Y_-#{15ZpDw3^4hy{2ik1hBxbN&}4=d;lG@ytHh9Z6<{VAM<>A5vUO_ zQ^!`xC)aup0M-Zz$hmU-5!2;2Qg8dMcTUqsOBNLRamRCq%~cfCof&V@flEe;e`NuT zg=V|WK5CRcr0h2omeztKFWIAxdR{p@9a+$!>m(w1p9cs*YeblqAX2=0^<}Rg)kOv> zHwjw6wLxUAamIJMmn{tFWL!U1FydeAH{At|_CxSy+A4ss^&N^AwzsvC0H|73O*(^iEZPODlJ9_zoJ^?dH*wg{%kk}_M zw60e0?$pHnsC#qXbh?TQ>D8V~L?a>*YN-~hLQk%pfY4D_XOpW%n!IHOZQiIG_o!7K z7pywHX34zg!idR=wSq=-`jCnH1-vXF^CEvKES-4Dk)i1a(IK&hw*vntieM`U|210w z_f!k&swNi&Mdq^@vfyNYbV)e&Y~Y*yU@S2tW=SFGIl?fe8@i(`mlNRNWEA2CDBl~t zx7+8q8#4gRc!PGI>ggMQj)KoL6gRxoPq_~iMW!zK{+{n?xQ&SkE!3eFGrziLAhRW> zb^$sAGj-bG1@>QM{~N&A5nrJ5zW3zMR!OC?;5(w!dR;2N`(ba+Kj_H|YFG%+*c=w@ z8L|C{UK?sl2H`8afHNKxgh4{*e@=h|W%e;N+)}~)00Xgd$eLzO<{knCx+UfoiQAf2 zM%|6%?iy&ngI`@DLwwhVoKh>)4LVkBguTi@%uaMoIA?KNsJ3!(PP}Qr!~?*JKL49R zd1rvs2z~N6&;MSM6{HK1T;A&wRlUd#f%6S53wXroB{49lVj zB{r;ACfrAO1@m?wFe!0To&pJ;h`S_iscfg@HJNw^z(T5m{07UZqVLj7WY33gL+vC3 zw^zEu`Jb^a_C{)eGS(eVRx?ilR+*2_)vlGFIfhvFV|N(triGh+a^1~F>NCN2HQiwe zProtMod7USn5F>cM`P*Om{TJT@aBJ7c(Y|#5NE%=o7CZd=3Y2Y^a|L$Xi2`s8t$QdC;@j; zfU_~1CwO?vy?=O-8_1$lYCzGl&yGz!2%1g`5HR?{&o5D79c+6uYRaNvnnx8rbM`0)$ja~W*W_~}H+eiCc zu<=5SM1bY_BwtgD5|YM-q&hof=U*FQ_1P_&om3lC zr!HnddHNJzGXYt^8xlM6W6Ey7SZOra?mble?B!UdwA>YH65wJC$FF$!-nS8uHEfq9 zKIhF)w7J%|Q2H(jfzXz0^zB!cW3NcgG(9bRe4ap{Q$`5t;yHv4aW!RcvM@8=ryDh2 zx5#h&rUrOlcayW2r@hG;9*S3Z_Y!VsfoG2Yd@2^-RFFc}S1W6_#v1iNK%>@1K)Wo= z;w%KK)4w6B`x5IZ-JRHYF5}y^K4s%djpBhCVzk?n-}ZKJY3DZu>O0yJURhJPEPTm^oht;s3dA|& z=4H5lZWXBLzo4CGHc_q~2SOg{Pw{bLn@|{h{PFFO-`}0N(Stq%ygY=W1|oowZP>a|Eop6oZTBs1||CZRSi`p8tT4|dow6!uYM911KlTK=Nv~&b=adwML=Q z?Lpiq_&5il&gr{Sc5Ih_7Gf;Tc9^qoRj3FboRyG>KpBv5U_St>OCKq1JdZH1XcE`( zLYP5+&cjn*ILWsDtX8tuzYL&OuLB?NtO5jb?4(oZajKdvYNnA^MBdNJ8ouPxr0R1U zSY|dTY0pW8_}M#qfV0aVc#iD&By}C!oyT)z7~&jW!L;xH{@E#LS#);HbF}pM@M^d6 zm%~#g^LyYHBNpAGftKzODeM+OiE>y4&ACy&V@3|xIGjjHN!{Y;ENyF)e(7JZs0Cw2 z`8Suxu$|yU+?HD2P?FqhmphCHBXG|ov8^+BMLmS-XD(BsvuW)tn=H4&T6u$JY8l&R zu`Z?w7#15v9`C1y+Ne&yo#$(NzCWEc;`d08)gEnKaF^L^+8Vg45|)a=1LzPklP#uM z{^@umQ-~%9il&iiZChs>s099{D(5YZ$V4>m203#5gMgXUrc5;N_27 z`|(we2m4~Ru%yIHjWrG^h851iB;(I1q@o=B0BxE>MTfu7-K*|fvS&d6$f1H45kZ;( zM)rGOOhlmUF<-ubn+~~B@RV>nnA8hkdX|5!B+jPHD2Kd{)gLfIXJ>CMwA>> z+U-;&K*GMlWAF8?fk&k#>23 z-HahQ5OmEIUKoaJ0;w(*`gU(el8eb4OZ6LL@zq_Wvw^N8s0hiAq-76iAxGCSp<~mX z4>X>PEDEkQB(t{~vAs`|!T!)l zk`h&s(+tl2E?&*l*6Tz29+h?_@4i ze&W0PS&AHXM%(lT*44;RELCA5NAS)QltK{5gQ0Qfx;b?Dg~!)^0PK_ja(ctl4%E?# z1&{89=6}wOf0Q^F|G!F{&tUD^86CbTB-Jdsj9T!S$E%VAZEy~(OtfH7G%^sl`i6_D z;y<)As+P14>Z_I-b_vBz+gQEeuf}fK9cu_>KdS&mhaujKi38kz;KoS$s>vKOE5^%684cLv6{;UIProaN34alW zo(#&eysn4bUgmoC9#8Wh`-Z?#3+&fs2pq(B0+u5+Q|>TNW%JdB-fa;r4eUB8vG4DI z8!Jf7FraRV#0^;x70gSAy3wMoLB_rfR$?pKkd+(xJzImFum-A5ext+G2|7LSWN~Bp z4EpNt#(P%BrnV*7)I7eC-!dyT%-5f=lo#a~Mue)tRjr?H#Z_5NE0Ar-7D;J6e0@EUR^5S__E_A4C}D227-85-)?IL3 z1!~i%}6Hu4L3N-d?~b#2n<}y!q3nm>9N1x>)qTlfI@?A_;O7fcmww zia?IxllzUMx_76xt-w2x#K`Xg+9DJzjz*{tjsU&j*YWxBS-k<$efvP2#t4twCTc*SszYEu&>~pg*EGCo!7ZQJ0?uDk zs#(qorFWe{PSnXRH=ubK#yb{IhoeolWpC?QmcAxF1a&&REcpHNYw62)|BeITP7HmA z;8A;_QMM`eit9+u_Z^qsRIW^%LIz_Q?Bp;`QC9FKN(Qs*A{>fq)V#B*`s-}cUw$C;vGgm)h<`s_GQ z6&r`kso_ZH{=iqPAtMG3z=lJsoNQA(6{50v=!Eqy zSb}4P>A8S-YmtF?>s?KpCSGP#kWBTz7^!_wMymJ}nNGSD;bXC(8^97MlC9lH=@BI= z{RfnWsSkIW&fMTi?PR4CvorBMFW+i7oo`&@8PoS#R=hK2-5MAMscA#bAFaUX`}64O z?=Skfi?u%`IV@)YRkbM-t#v!yWih9Akr2DSomQs|D)nX#Ym@AZueU3~HC_Z=Qu&Le z2Tb4)bl9G&rjXCz_cuGVs`DG$Mx9gdDU2qt3Hba{yPXN?_-FIQxk>-Dwq`b-4wM&3 zNeN>It5TvTaea3IYv4ixI3Z7`wG?((fj;?oY^mIYTNc>Mgrqip0y?OD%0eoWdclNR zjx_54aQY6yS(x!05@`?Iu%+`jhUp*fTGX#bt~j)%`nq&{XUOT6_t`c=+>R1^8 zAl0Yj6i@g;Q&1Xs=q+P;=f3O%s}x!aS2Phpqa1*UZ}vpqkQ?0W9luTcXC?RuKy?~7 zPk%HiYs>8)n!4P}``9}#~ zYuW<|^@6Q{g%Jl}8xzyq*?FzMD}Rj|8hKDy159G#@feRU z>!Zk>S-GK$w5th3h<8~J=3gby+FoePQFaV#E$y}9?-$@#ul4LWP(6poikUx^ZH)+Ar1QkRH~w+8_8Kv zewEE1+7S}Q;~iT$SG@UkS@1de-B6T!B4%k7`(MdwW+@Hzxsk4?Lu!Mc*u3dIpzn2C zuR2*Wa}mh>E=Cs3q|Zydd&jB9#?xxt?J`jev5p#hNB*b%iUz~`+cx?~F}a-+<|FOj zBwiN-F%&e_mgCCwtST+tj=Gm2S?{C0NL_L(GxqyYlfY<{wsGpdY+L#yV}0M~cUJ^9+2@3mIB3^);#h z+jibJv4GZmv2gLI`R-gvr{xxM_)~T{t(__D?_g#eltpp;tOpG{C0_yNSs6Hd-d-MF zY*kMiVCQ6GMguoG$%My63kX_vkHSq?PzBN=&yin`uq#MCD8I3|pSn`}bF8T&|LIlB z%BoK{KrsZUZ~%ZJt6gCV45eN(dyy)UhHs)DW-L((s zqWblI-37GyYqNpi993OtKwqC&>tA(sxX;nAaJ6sWp3586hg->9Kq;E|uBr+!bZe{T zKflx$rChw`hd5(R7EXTwgE!=QOy^E?(0=#^UYlcp^pXw81|iP?ZY(@^;}280%^y0q zCu6C{3vp}1T5CGp5}-ULvXxb&Q*n>Cpy<9l{uX|EG0T3eV81bxSrCs;mz4ryL;xS3 z5_Dp&X?zvH1JeqwY=%uSqp2zB5L@IMs?B@AGDs|f6h;0E+w2Ox4}Y;vW8?ZtLrI@1 zaB=2ddl!8-j0COp4m&a0;EK}&rVm)~3yE3IlyRG_w0Cv8Lm_Y5>of2bhPW zHZZM5Dsy(%C!<0uxVA38)JhHFpnXbu{RX+F(~!528u-Ezfxdn8R90UTtk)l0YUL5J zy8u=FcW$c97hhPCZ}zMFT@tG1UnQNkE|0#R{t){FLSiDfHc}nO0PDJYgYTdTH=cj9 zCLM=H!Ne(72Ta8EsEHPBbl>1?1teX}T6~RDyD2Ak05g^uMzhDxR(!Fc!TxfNf2Oeg zaK4`zA>ev-vjmmB<*yD~z3PGJwUKnb*Ek^gz1E@Ob)o^kNT#nQr}c`+JZ{N*MAjJWy0tdw zz}+#zC7<8NW;UQaSzt%y<+HFjisMImTt~G57z?35L*9O-r3ebAD9x8@h8|F%Z!1vp zjUK$+p5RsjQeW*m7NG|w3({LjG3`TOl|S7nZ4AsOkUe8I@CCD00D5dw|A+RfUat}2 zhA{YHi;czdf!dKEVejh1!j0|ngWCJ=SpQ7Le7inD7sS2b*niz&wfrY`pN;%EX6)I)m*uV7ab4UUV!A46u+Dt4ca>4MVHP& zq&VS+K+XI8b>b8Lqx&82?*c?(u^tb1MjWPnR@lgzC8P_uj5-lA#P!K9UHocnn#o!D zTo1>0>C2FS={2!wZl4^;26{f9PS@G9Q~O4K=D?+oKQDNh%m#i^OG1`qV(n_g+HztS z|7YKx(4N#lbSf(#HksEeZPt9C(YtJ>;Lo9-c58D|clEJ)4s_SJy}8%3_&izni%#-DwX7SLmm4>s#H_qd=OHA0t`dMtZs~~)O z$TV#C{^z1e-2-4FL^0Nvfx*&>AYK4Ji1`2jY~gf1kG^fNsevgK3ztqs?i&3LILM&) z@|xeD8+|UPt0_QOd+=}VE3E)cPH@0o5?A^zBeLc}14|qr@_N7+a#tS7N z9Orm_P;tsV5HcbYm#ccV&1rdkN_V>Kf1wjzzTg5_UG#gkTn_!Q>!KqBoZIZ~01Z== z_W}9@iDiN5St!uud{w6n?2d(1o&)hma=SMaq$p5(Q^IOR+1QpjwJS45(#S)AI@w5C zBM|x(UOhKsTXqI#QE=#!1}D{NuxZLf_0UN?z}ztpSe$OUcz7Psu>y$fm+0Un4!k$t zQf*|Vl<2@vn+=GQW%X`FWez(6Q4QkkydhsCbd|sR@64m(-z$#j1L^R@aaM}U`R+67 zZN->tcZLGKKP?nWqkk~oXkZ`)H=q;r&leDO5Ct14PSy6E?*H^9gt5w`z@v88{->4% zDJumyQjPc1XC~!u1e|B65eENAp6x#YGdJQSZ+>li^`bBAjrGm*{Rd$tpF!=q49@oG zwIaAzj!a1mx^aMQ2CoLf4uPIAu}_wR!)>q6E_dkX*Ve4OOs}z| zaL+sY@grd`^u=WUKxO7%RgGHkRW&9)rVc#Ug}ib*Mgy;2gy9SU|NUkcpR@RVAh{OC zmVC6vi#ZJtP2z=`rFQeNf3aCfTFsfuDqLDDYeM-WjM{8MS}G7nJCju4;`Q@0xWBC} zi`C<~*O#T**N;p|v+j8yPoKaw0}~3d3D_8PStk%+WF|4&WDMWcjmz*k46^tzBw1At z`mhrP>K+=fO;MBjn&m3H2<3^uY*R!D2^?$*ZMib122fIoXXo!QPsVcPr9gb9(d~F2 zAJR6mN)8#SB%GhQ2DBNqOi%tNZr~kgsgNm6vHkt&LDA3qJ2TB)74qeu9?l=^8|X(| zk63&P?*XIjXV72~$AhjCo4JGXPA*n0;K0{A>1qNhzu^KWTNMud*(c90(Eu~bKkAxr z|Mjn)2{^G>)WoD|kR);~bv9II zfTB?R_UhR7yT^sXlxhRC2PH$`yT5Q#M*ogiBS2RStZc^#?C3QRb0TktBM2m(bFJRC zKRkgl7boEr4P{GK&^=!=g_Od(g$DoVxz9}Lqm&H`k1KhK3&{=uT|X*x!BH#WAZcc( zbM17lny73{-<=oCyyN2X>K!bI%8Wfb>ZS!eFI}#$ceY}yyzZz${4>^Eg&74D)vI`3 z2=q(vN=z(1u_Q3_{RhD9VZGQSGGR@n^6=*+5#0qob8$A|j)Ah?NKVgVn5I;@S+~iR>}+eib2E*nFNqb$dZsJ^V2k7< z2`3X|?*G07wmu2nXMvsHt4hsgCTfHs>*4&XxOVXy5KpkO+M?r}=)8`#gVb9?n5cn5 z5M-C`Kg>3wxPA}g=0(UST}U4r{<Sp*8kK>#vUl2AF~%ZGqF|h zrgjK|422{(Y|e`huGrT*~&a~%U1b0?CK#^T!(mZ6@O??2A5dY>hDM; z#!l^88I%8HC1Qf~W+9GM@t1Mko%u`e56`P`K)isqScOQ;Z~kJK3nX0G4(4xw&239AaZ$AqS4^E4eLbsGZDvc zRo0K!@yir9ZzA*?o$+m37e3)#*hcqH?KZU?aInh6Iu<4ce*iJBpVdAQhJpZGJ~fLyz*kkJE~Jktj7<~7*bpS=B7|>p1li0rS}53 zx=M5tsiD^y{O~cQ0v{Wnu_{u>l1s>vpGcu#5{j6oaay5ZZGY^K7%Sza|C5GjCn0>3 z{S4pDQz^s^)RfpVu{nn75E_!#qT3C~elQ2XMB6plT6%aelEB~{rkWQ8M`eH;`ZsAY zfEk)y>$xMj{;!YuCmCJ13{%OZ#gOd#iZW8y3y`H4$yd}#N2Px7OO2>~G~Bs)&6j)w z>;mMtb8|c-Bu5`ak@?yU6p!yaIo|$|%aKo2c+Ptor`0?+EnaVJh=ajmmdd1$pkbw3 z?+}{JoadGh<^_@^$N`Jclt2^YSpRk@;7Toj!8u?cGRcCyApiXuJfg{b*`#5fBATel{z=lCv3UKI@R*F48Ar&^-pT$5yIoWU~@gwYZ1~7-}pp+ z?RQ6NbE4#vCYIu{QgNZdey2P)2V$an^&e%@9&Rw~fA1RDAC2)yjD&{=75+`Wyubwe z%p0R!Q-%Th&MySS%K9Uj^M}b!dhi###MhoIN#w`uY62pn z^4U2+E2#Gwd?3}*dei7!*%wb&snz6~{Tl=pu1ZtNjD#dj*B&53(^pN8ZS(RqOLHyL z0G$yjS#f^Els~^L@)^5w8o!&LGznHwP!4%1vt;m~#XO%WtBeqp9_vSr?9go^rejOxdw?r+4K| z*KyO>RI=gVpBT5M!uKf`AfcyxrBJWFpf8T5RdYeXT{?jtH+7=hb~@ABAiDO-rI(g? z3ozq23@WQY{4t)?r|bI+8Sgn0keJ)A=E2tjKPi}h!Q-UDs#tKc)oD3-od(VgVke)5 znU;^Jcc0{Qr}O>>Obfpv%ewm!5$?GFg1u!17?N6Y`jS}^UE}WqI~MS2yP3H^g?zNA zp>r^xeWYRk&vs=I6_&paRyNBo`V(gQuaAg?1@mqQpWINr3dI+bJZlkx=||L~TpEB~a1?cJzf+0vStsx*BCqQo7 zj4W1<>SvW@#G{|`JO6yx3Ns=$dA<<&Rjzkx(}i7#erXx`hjUi8VSrE&1~vzXl2!TK z#hgriY=3z;`o&^&GxWALyfU!tjkkcGjF)7XyUoB=2kHY(Y0Gs`o$HaJa*nj1SVa~d zj(4aV64A(dqM83}$3i2c|UygpAeDK`An@_ zlLkOoyz}@9>AVVh^$y)*Lc}YKdWhFmgXnu;_rebUntRs5OA;MxgH@gQS?RF=@=6ug|^-~$Et-YU}7CIdqo|I zEH+(f@n9L@{U5g8GOWsIT^m(GQc_yFkw!|oyBnlSln{|_>28>SbcrC1bT>>uy1Toj z;S5}Be`lZgsn_L1;C;tqN2l2$vKNYbzMhJXeGYsZxNTj=ftrA$H} zkKw-W1Moj7oFCi@#%M5y!N7kRm`m*W%-jNX0bi^OU{J%Im>91_)YTQiw_PiQ0fIGi zKzlVCo<(**NPBD@{ zIJUSqk`v_Kw?(!IT#o~{s4YcTOz#EKiv~^@qOL0cQ-mD&RD^8DnSWaJzXz{YZ*Ut7ry-27ZTdUgWxIDsndaWG=kHY$?vUAKBZ{iXh}Uy*AHaRCTh5B-myKm zIorHAPd{RXdH_X7A!+c6B?>MSw?K(cO}a=mFTXcok6w3QNUnu?40UaCNj~k}%jAf` z9t+hB7m9M1yOW-o0btnB7D4Z91qF~?>ivFFHLif|#fkZe5{62^UcRGGx83%h(F+Cr z_4Ft0%^yDq#orIhFq9zly}i?)&|AJ4SG#`dRR;unAq4s&n>7AHSo3isN$mWQ6?@=f zgS!Fk1)JSc^8$rRb|(c8C5rdDLxLyqrWsEF$t<4sZp3U>un3(Eod$ZgPT%S;kAb-& zOx5UdO;Pmx8>Ha-zu7D}>Oa%><0UA-QZN6REt*fb+Q*l^6sm0Mb`i5@vQU_}I$h{j z(&wKwloM+B-;)DIjO>gYpBw-9xx;#|jKd3=?Nr^ecMpk7M4f$cv^<)$UhiqVmHYyJ zmYChLyU~gzYzn}uQkVSqnJdRY?1HdpQtSqT|2T#^+_*cDI?tMd+0Vr_1&zA?A5Kl; zm}l=r@P%NQw9EYEyKu3AktPeD-K@+ipPKDxfJKN_tqxZH9{x<^@d36E)l|xUe_BPQ z?u(Ap=8Jm;q3;9e6CJv$w~#nC#TR*+i4a2 z1y3*J-7D8&Dlad00xsv%N(PO7?7GLYETNoKvIkHkVmMx+pA`I@S{xK%Svg3+P|O-q ziFPOfeSUS-&L_ecSVcF#y@5akWy)r-IC!o-h#F6ZAYpf3`JcR){GO|rrd#!KBJh=O z(PPaC8GIp*0j0y=mKfcKkBdCcSI6WEh|UE<_g}-gFgtCfK+P#G$Pv7h{K+)My@ZMb z0R587RITjm%ox=)w+9+_A_BjC2J za#@SeY8qabVP*TGHpNbQx?S}P(6V|Uh{526!&mE3D%~fSCzsZ{A{Lum-_=*)^;4Tt zX+bP7zVbJwIW~KfN|h$pch{94U`wSiHNEf%Vtm^1LTdYxi7m3oo_Mij^?A~q!Feei zOd(s@UBQG<97>61j{qK+yTrw2&mgW90g;xbxUK*cl08_(yV_xqZLySUKjn#p2|j|K zZ>#B2614VczYneHd95MyS^Gx>)4c(*+%}m=!i$I<3xi}@Yf(^glOdhXpVwNu*PkF) zg>=PgW3XUMFlob+T*BWU0j*7EHGXA>nm9bJLVlb{zU%}Ia>h$sKKAtSA_EeJ{7E-} zozhE2R2W>cVrQMy2(alI2tf^>2xqV$RD)+b|zcmY^3%^l=0}H@u(c8h)6`N)v9R#PA)ze10<#6 zf?^f3lPCa$kKApD;KC6}Ieec4OrzVeRf;Pkf_~$ZEl*wm(GWI|MBjT5JapyZ(?}*Csjt6OLT&XtcamF%( zcB7-&nxfX@I`%x~Drqd6-uvH%Ch|>p{hPH{8B7d5tDteR*{$~;iMM#! z=~@t4fK=Wkx&B)EriXBvg(mbzzGOkqNZ>noZ732$+`q5*9>gJaF1K|+q%AhQuTS%` zK>(8hi!Rm}D0a9Kr1@QimcI+4LL}1h#}<>eKn)G{zZk=n0rwk9nlWHqBzjs|00ur6&fZQsQ+s=f0ghN_XmZu5Guzvu#6acd=dNx9k5wk%L1m_g$sE4gT34do`N_f3sw zagaH>WA+<|MTliWT;R5QIPhdvnh%Uy*}zPc5QfGnVxCc9?OtV4EL%VkitNOKy!I;F zdP5;oU=P7^Con^@XPry~h?V>*M@Q4n_7-Bw+Yrp7rpqj8FwNgygSZ#h4x(PtQ9#bc zG_$1g%`(z;%-i*VygX3aLNqqtP7LP%R{&be5R^dG^e&DVU*f^0@sL+9#+NXinP2?C z%6+n(gVZ<$0#kJvb>B`|>Z7)B<3HxgCP;o4xaE6X^vMjWz62W4?vy0yXfodqi;YZC z9OOTZt0XfxKcT6BsES2@Ue6nGTeU@b|Lx8l_dOQlpPyG6%Mn8#nvcjCDnb)6KI6sK@T_7BtD33)hgMPX}%x z6kzo{PT5e4N0ca+{=w{MT%Fu!TTZv!`>9er)GgP*4~`rD zAwOfe>5`q@kIB|<8mhF}B$s5_#dqY=>(_gEykaeP{KD(z=bP|Cm#+|_$it6^}W?6<+{l@&bb=z6!qszty(y-^c zs8r1s7Xf8$?ezGLrA%S(&<)|fE8Y+#WEywiYu3)!J7OpQTG+jyXbF48HjEN-*@;%E zjB(Z&g0Mp6div^|?@tPVl0v{=mHCihuaP$orwb(XAm3Q?o#dcn+fG>=kf~ocP92%O zPvTa7edOpRcAtJ7rqmst3|W4$K^{#Cma0}25cCVQeX`=|q5Kwb^jEFEP5jM6pc5=O zyjT*YJERL#-+oZ*HObfqeJR*T`bPZd<~bnO3X_c%-Yyn2DGA8#-hj(siOmp{R3_gA zI?B-%tq~y)g+eadQ3Se*N}K!I-+-z)4uAY59?4uOSARoAL3)GE^_Sbfzp{-x>IJ_|o#k=yX~!h%24He6 zp~mSTeu?|;o81|}K~C{MOqZ2*N|82^zKMF6bk96s)O@iWIQQ`GPX*Bib;+UCzdNx+ zIV$ICD>A~|`Iu76vW$^@wki8U|H2y+5~nvHl=>TO&F#5s@(cO3;)4@Q@PU%*A!dLQyk5Wj4X6Rd-bLBw=1iYYw;j=OM2$n`=hVM2NJQ*jF?2=ob0 zx8Aad$iMg0T=T#0DG8MBt?hh#!tCsC8^zRk&14IY>9Ql2Em|4~==Y-jTafW}3c+h5 zL2$JT&poDM{xf?GPFuc4Iq%EVap-FWy38U!*6!sPZkMB!!fJ_BPhvRBVGcuI&Eb%oA`Kr_RB+oo?MVYNFr)*d8psn z=vuBY=pw+!Q%>pD_vLv5$ZtLQE?R295^6O@L)6W@HimV!tTMy~P3DL@eJnQC_GkdM z__#saaQu^BE|&{6jSt^KR5Be824AJsT78;52KOq(%V@pwf!?i@qHG+d1hZBK(BWWU7K5R-s* ztg_;UtYnIRIa$phIezP%b~WOH-k?_Ggz*!)IN*G05YLVAx`VtW%WL?qjn+_*E=Ted z-|6u|0OnC-D1d`<)0b-Orv}2JIO-QzX*68~2Y+z&Z|Lb_PuO-?ttD;&Qt%bo^M|W7 zWG=bj+fWGYh)NFv&U5c>4}hmpWAzlsyAg40y0f{ROW&mH_x#bpJX`dvY1tkx5mf)2;S5U0QX$>{u3q8ES4ize{wS`jRgzHGv+&wBB64-1W(6TR77T>KrL=mWMlz-wMuCWIueK#z}38 zXKL2GU)FJ-K*e}Xr-mWQSKOfUy)DiZ=tr}!Oj5l+6W1YcBm27Z&KxjwFhu{`su9_q zwmuBnu91d&CQv>@S*T@+g~xJFK9nXz99x{Pm>@!>lo&`Os~CekYgaN#_kVAOF-6A@ zFOYHA>_8DVzZXy+;Ur@uGXU)wNuW+gCp41y*0sG-zah~8yFfDTkR8TD3$U3HxU1|V z?OvXXFYo`(-D~H){AgoQVmWM(`*A9W6okkcFzW9XEgIu0E+$dYaF~$X8Iw~w9~OKd zH|8%yJdYJ|6?ujN3o$EE&1>Xw+>O6Xet<*4th5-!dukq3CIPwcv5R4bQ%>S6I?PaN zqkSaK2Egv2{6NN!p*HhyGcl;K9ga(0Y;I(Qariy@I*Y3k6Ep9JH3?_a_O@>EN3sml z5h`tbc-<*>CEK|a)F{)k5ZAU&dTnsomA*q)g1bvTpGsV2wqGwn4)9=Z2?Hfz&PR&7 zVR9oh^RJ7IzMgF|8KC;5kl=PfUyrka&S7YO4!+VdH;E@BG*z^`uea}OxwO+-w_$F& zs&4E0IJOf1kmo+AhKx;!9BkaCX(<3T&O}>Afpz)$WdN}PjXKC| z)&Z&4@wj5Y=e*+h+MvM@t-3E7x}Tv+=C#bA_hwVyEO8?Z6g*>lfXK?e&?>rNU;r#n zmW~&@;ZSAuM;5|mDqjX@l?=RmHw@p8L4bBc`v0Q5ftvM3hm4mpl<~4sS{q>s8)iI= zlaITA?|=#fh|q)R`V(y#FgFIBRG&HQj>}|}JxnHb_M@pKBjQ?)D2|Y1h9f&1fYuyi z>=QmGYc};g$5FKKT~JSIi4Xi#nxPq9vn-(h0j2{i3TGF9fg+AGH&2cj?X)9a%mQj) zWrDGhhI2R;O|gWvEBmTjV(X#7R3ty_qSJRa-t4-`Q|=lXF_YSnCuLpAQXADJGCntfb4hDU^*B5H=PedSU^sp$tKjbN^m}-*^u_wg8j#M8#t<- z_*Sk-&ehL`o);2-vJOW#cvVxpwTcZ}5+^nI#*Y4oD0X~|Y^B2n_BS$4fnjcMGc(?k z0T!w7#;$tWsi7i`B6*s+;Shr+19SLhpe|vfw*Y%GRV;^3x47jF_gv-Qxl9qtLsR2= z_oi)qJ$H#k8L@)Js@kV5!L9UXy<6_r5V7wo{6u8Oi0^EywuAllFIQ z_5w=+TKO}=P6>ZY)M3G1sX2bqB zh+vZVI}Ug*9p-5I+Y9%w@t54kXnTNoiIE9UGVOY^HimsB96w)c&xg%E7#wxfcqW)^ zd_d!Mf5BtJ&bSf5H~sItG5ybZvo+MF21VggZ2ReM<2vE$0`|qx$CV&ba>wCFdGT)q zq7cgQch~xGqiEwiWuL)=!E}0B+RE(41F;I!DFn;LarIv%u>AzzX#$T3DK5K+^>g|c zL&1%Kx9kf*P8MR)MG=g6MHN?f?Bx@T@TC7`KjDawH<_SS^&+HET&QzMUF&i95y+@t zAAFOYQ+sZ|(XW98kFq!j;5+UnD)!C>qc%n^%607R7L)?W&HxC@!ldZbSoL7mW}OSV zqZtUhB`fwdqa%iT$eihpW<2oWQ1L_d;Z9zlZPYnzk(Ow^hYhnD>vl*sVnBssvVIF! zLIz0rON7SqIe_z^8_zAhW0YTSl~1lvzLj3)j0;&B2ijR%xKynH2wT`M(&(N%2Q@>i zH+=FjfmKFiV`C)7*+6S&Kda|)SVg?OhwOU<&sJ*EMfbexEl-Z~36P(PTSIK6g~joU zPH)B@F!1J^N=Ug~uGAF0mpITFy1mnWD?tiu#x@T(6WC0f?@L>niCAv_0Nmm%;>pb* zP_^v9^nA6%j{FH&%ZDFaguMbVo80|ZnZq&R{Qw7|NZ7|qgietv?DIV`tB&fA6ERVt zkRH|_Lg=h850Tfx-HFKNVECZHIo}9RPoK{U03?G>Hb}Ji$16^AE8Nm8JN)o7Js>oV zCgjY%a<&@49j++@O#1bKE5J2p_cz%Z4ygRT9Na~48&FF;K9;XO%jJk1i&vYtj z&NfApGth|Zqe(?Jg#_A#V_98*Jd$u=IB}6NYLtqZYjC-SI(}&Ba*~k+Br|0n1;B{f zwTav~pEVDZ?c#gDebYD^8F|@RW=!ewI_Zs;g_lxQ-AkvYxoQrZS;gUnzf(@;+;WD0 z&mOSO?sj?rg%=93)WqIx{;xjc|G^O}Bl1XsePD-2`56fg8_Tm})Ng@chwDHOgB#!D zvRmvEuO<)F)BZrDr}cbe4x4cD`Wu` zPxROc0c_KLkA3mqVynDWilR%Ti58#Y6 zioF@uV{Jd&bqpL)mEh*(Z{)_blo3*S?A42rykyebC{`2RB=yEG%a*Fb$9HAQo~*|y zKj&2n``w&hezp}fBR*`h7x+@B;vF3#Ia#1ECrEbs5%h6TRmQA(hvIOeC@Ic7^h|!+ zeYem9xOEuzaZu`k+<}f>jA;vAaUFY8rQ8Pri@Y5QSxIGq^nNR34Ce&B(|x-X?C&Rj zR>R$FI*6;M3b_A-Mh=hATL4N}dXO~%UNl?7Rm8S4o8lERI;mhUN*$TFaXE(KYBsHV zAN(j-%t!1-_>Z!wy8%_}tDNkAbKQXaEEokJwAQqa#@moN{?J%s=P=*vjGDWN)t)id z%@j6PB`QRfp2mWKy0@bWsGk{8pxUS_X$Ke<90(g~A-c1G_)Qbhkdr@Iu*KR)<}&e2 zhRrZ{=@f<*$X+fM8V1}4M+Q?-MEvvdV}0%~JO;lD9bo60M{@|%J%5hiSx?1@-&Lw% z@s@J4?@lc5>&dFCJc+v{z&!W^!O?0DGm&qbwiQrjjTs9KYa~E5pW!SC^h&r>Ejw| zmC$52^{^Nw?P_9wqw;v*yY#OyF%vp6gl2rvhs%FnWI_-e*ZSgmP9ZNoc3dq$fFR2b zu`i8}bYj7@*vIASSgE|(0Rz2z+i}k=-Z*O&<%`9S_hd)EpmFo+!FDyOd7(Gf`7vHq zQUym1T;cD~nPJ*~IIjb43B8MB_$&Fw8q?b#tzq^!-^qrS11JMW+ji+aeFGPmG%k}M zTS`JD`YbA){7g6hdXcsG!e@j@y z%=EhdaB?w4>AKozF-R9Fc3S`vGR#6h@h^o&{^iX2d12|uEF%w|f121b9#R6G0Qsrr zp)Fv1!URT=3rURzCjr&pm-}TU8b!#3Tdp+AO(gP)z>(i0s{~ppaHoGAP0CO#fb4*bjpB?cc!L_GVT;yRo?%OS$`mWJa>$FAiMsoYPW!^Ths+vQdm=61UDH*6D70~ME?~}@w(b+?9Y`W5JUW`TCu@j{n=f0Wj z6c7Q20bA_HbYxYfR9Pb4?5joak>nGNN1o|&MbBz=inYCt z@`FYEnz05`s>v#*;V90U*0E_`;l)A&+1_!9ZtG`)+g|W}5QQR?H#@3FMM#oGHMf>d zzrJz0%8J;=dcIHR$$vkH&Y88;uvR=DF-UVY*rkoXIsa+0c-TgKlk|)aghQVspS5yS z<@`Np_}suw#L~|LBk{O6`Lj;$Mfbqu|^?EamjfMaoo=b6H&6sbZvUJ zI#rvPc#_vBucIGc_vqm_ho`}}8N*vWCmRDZp(7Ty;S8_$4F=hO**Tx6`~RN-YM-$C z9R?U7SbCH644)YnbR0G%tP_9`i^qL`<3e6y)+Z`xVbS1ppxD{u#jmqix|I4>M|UO> zGF76F093QIZ%^udX{w3b?^QU6r1SjkxJS~RCl)km(iLjH z_!($RwD@oL&l-L7@?<5BJio(XQ`)?@-6M4UGB=5#x%bW)EuCJ^X6D;SqFE}MYg#tL05AHlK&?Q zK*S17F(tPB8m#3r^SndVXghM1{%%;`E6Cm z-Q+NFo*gBnzp*Yp>pY;qx3oQ7MfWtF*qH9myDG{Ba#ANRm_nA21f2X?cEwb#77R_sS_BYZ)h9zAt7m@F>T#`6A5-U`@tU^MKJ}Y=p7eIND*OjY&dI3agwc zbF{>?@uSJ&6S~_J%rl^_X5_M-K8%BcZK(@P;8tG7uZ!yR3jJ^3a#3Wa{|xg=dn-5X ziR?OmmIz%Bleim*qa}^l^IG2mUiuS;wZ6}^O6eFQRXWEYY;d1e-TMi~xX&l>eHZ;O z%{UAObB1~4N3qltKIi(V)i+#qot;%1CkN<4P+E;vA~TsRTpgR<)>Ia+>kZbJn_1RA zv^C!Fi>&RJEtbO%L;bx<0trc6;U##Wav@lw7(5FjuhnE{w<}ZQiKaT;NQ#Mk8*6%D z<%L=FHfO|PDhENsw{5{=m_%O?GjZ zpSE*`QT#BgFjMUsbKeM-7w;SUe^{s0h}M%T+2{ZJ0xu&28E}3P;vFhrZ@pkF)&#~u z&@zj{!nSi9i$SC4D@JYU@gglh8WCq+icJ|+d?$!$LEWV1aoaO4EkEKtX}P^Bf0`OI zCK;2_?jivIrn5027LP5iI@rv%k*Rgbwr&OjED}Q77tdb5*kp(;;NA?i2nOnt)-Wm; znx{K-rS7c~zvC{|$>~5kJj%RbjV%g&kr_~4zY|6!x#P8U1$XFZ_r~v%7&aY6@41v3 z<;S#61QUa@T?0cWM`FPzCfcxA8;(9XaRt7N*CC$(S3K4djbvV`;asch)K3D~{WDf> zS$W~d)INhH5L=nQy&WhTD6i8;GJf0e#e@{nnlQ!peH~PX?yrJIqf{;>hggR##T<#c zN)Lk{$|_uO0Reg#zr$bSo@U}&#{U+A0Pig_P1Sn;#Ve-%bn|ann3x!@(Hr;ITldz? z({zTq-fk6I+2SF6xhg2CUhd!6ma0GT#lcW#bl|0=D58RlARFM_BhYC(GpQYl$&OkALT6C>Wq28eR>j z^;X`O^#=Z08DSkO#&-Q*BbqgQ%0yT==4tY$n;b0J>m`O&!ew zw<{m=%j3+jK}o7X23s^#V#~FFli^5lqq- z^JL-zH6H9+MEquRL-)yF0)#&i(YS~w3QwlX7O-2=o_57a{y(B<|LlPi?bM}TdIZ-# z=$km+K95{H9g}Q4U6-l0^*I}bTYq^lE=#zck%a(7He(EJ*x6<0&KRvkN8n)B$(mHx zXdcQ8-zLXN)9F^mRu?o`txDy%j*9!+a*auV)V)w*0ogp_dXvQI6Q_X~{3|%TR<_D^ zVO}Nj+etH@th6h;1lb=q@14BGY^_o)ow{blw2zToWC*H9wzJ7PE3gNp3-S7?cr(Md zpk1SYbgCqY&Lkbz)A4^4XaBEvs|4eZ*N6Jh)?wjL$QG>;*W%i@CuZ}JlB3ivI(q?^ z0)Kq%wZMfsNy_wlxm~LuIBCPrPNA;KQGTJ{(;B|+P$(?oQs6|S8k7Pg1BX&Fd~*+! z5Z%~lB0LBBG}PpG^cp|udf-?4qJ(VMCE$Mb5qn)Nk=o4K%FlqdU0Glk`!q{;|1JrR z14yA=YJd}!(n#A+~p)W!8*7R&HmLE2I3h4Gsa5UwhsY2fQN%ML?)m>Gq-V7m|DVMN1 zsAj=f6dI+=zH9hOZ48fWA4eON(AFqrXtIb(v6;4Fse01CH?rC(P2Num6{hn&#s0n^ znxXG%&Xp%bS%HV!2q@HDg);yvm<{bci&?E9V!)ln421b5#y3jbZ?;7n4OZ6VYW-Z6 zwH}DcY!y=NM(_0)0LFI>FwTUHgl_@+RA@yo80@Opzh`&bFm z1b?lL3p$8Gi&S!$BQPDbQ!0S86ean}wpAsH;Otke<6h!tq7vDJ6c{s$dpj5kgungq zzka`%xb_S8X)zUK()7jPsyYEy6CH60jflK2r58B@kGn;sR_U3A_<|mgt7Sj0S*dnX zlXNBF`d02eX!04v`r+yKA_fx4h5mFez@IXo&_aepR<>f>Frb-JO+me_O+TNNHENxJO3t`PO;Z`0bG9*k8`l$L& z3iKWfBI7Yh?($b5YEN|d%?pZ!)rac;SBLrvChH$}o$|`V6h!?P^20QRqL>(fRIJ|p z9+?Fx zVWqo9yCv?f%OyUoy%DM1L`V?Onf#2-PO#i^X>eld{HgKAYQ=~q*`x3SusQ?jk;2`P zsY4W)Wpa?(Nkbsl^)4#`Nz{jPA2frsev6|C*d?p67RXbMAHT1N6RKbYF;@m&_iDGO;El?4`?#+UB;FQh8UIiuBYpGKs~S_u?CFI<}9}DAU8af z>vWt#^ipD$kv&zK(Agu_k6!EtS(`$JpbQ`e(n4O(D!dbXJ{};tEGXHU!InYtqQ?KF zCqJlt`@Ow*4^KGv+SuXu&DQK5C~pnP5^LFr&|_nZ2yS(Eb|Ah3B}WPI0ZE@RAlv-F zOB?4A5kFRS((h2X8CmxnipgW|w5R5aab|V{6pt9dP)J)@G@+As0SJt2rHmxvL7ZNm z{YYR>rm2`T6`$3WBf@s>Iz7x6io^*ylq_3?rq(=*+s-s=`m%vFW?#YVnjt5H_5QbY zIgismq75oTTc^`~caGZy;pk=pH23a^uSC=g07S`uf+F~VBjPwj2!aP*$}c7Gsic&Y z1H77nsFSxO@~=s%YUd~2i!^OI!M#7^7{M!fFke6%XuG!nGEN4f{?^4kyo;{*G*+>< zRSowk$JL<_AfZ?&el|ly0f*(vSab;eR<^-hS z_bh2A|ieG(WD4&ubvP_98L%L-BlK!bxp#zz<74(;&O1PGXV6Jlfs9B36KvHu&V z3Ie7GwM#mxq)-Undu?!62fZFnsrG>e$D45E8+!2ucQ@(vg&SJjl+f)E-|7MYI&!hX z94}c-P7ZroDK2!3P>c+Oo-0~>bPEAWiCCFfTmvD*7P}>5cJBm8{M=@k?ZP|P3|@g4 zDjET{qpTp3Y@VbL(E#Kz|4S0C!LTFTo#?-dO+i-;i2lMDum|7YXZi?X%z!ix8@$eQ zSD?KVzZvAFER-L)y9agX)XjlbWE@@m{saPUFrJSSnRZQfU7#kJ8vMYSgc)v+JDln7 zlwD5j+dAp~)Jt9o&!#A~ZBsy=L@WLtpeGV^FF0^JMt$xNAcc95zmbxdzyQ2Zxw?ob z(m^i=5VwG;&S+U_YMO^|B0N1uwQPh+)nM32pE!ek zyLi#R`py5!$990S!2W)p50AQ9%^v0|PmuIUh)w^bm236M%6!Rv@`eVH(vMwQ4F0NX z2UW%7#2NSeC?)rU{JYs>r!B=`S709@r_PbJzHtZCRiH|{KiLh>ISYrS8&_8?5-0`} zh~fO?)bSQ-+b(~#KhvE*J=p%^7TNdq`IDaJ=Cq#LdV9@v@0Ztk(?VKHgt5U$e*__C z`rwf6^@z}1r6ryx<eZn24=%f)B*deaGn!l7}3|8I+LR> zV*L;zCK`U7QgEXS=Mnnb)&c`+NG!EnPpDeTQX0FtHKSS`m>t(%GJ)in0=5&6A}Uz7 z&A&~at0A->V8;{}p=O*yrtx(~O-GXUMeZPqSnX(gSn%;q&Pzym6F7<+D?1;~Hu4u? ztt=nRhDut{s`h6ar!UTQ-w;}W2OvY|zp!tJCgk-2p>SDc-G`AcC$~qIaApc?VPR3O z%}LYX0hSY&;(m31+oHs0mvn8`rQi-LV%zF7%%ey!;~1->7*7||NF}q*l&z48pCa9O z*ff=(WiQdqk04@?^5JHWMHP*Hk8|fkJR?n}CplIC-vIhj>2oca9CwU49RY&+m4kt`jGJWzcjDNV#I7 zgy~oeMe>_ULdO`q2$+1x1)v1g)-%3w-6knqq)t1s?TIb<)*`APw}q_$nDgGU`#B~K zA3W5yS>OI;GMlvKNS*sQ&l4!~!2-q0X(Qpj zM1J~VtH;KZ+?G}w#QjJZW#JALr=4~@KGa$vNOm@A{(5(MbdbojXO;dnhzm(Dh~UGI zyf%102*QQHctNtv#%y(tl>yf$Mw4`*jScATgLZX`yADvL{J|M^x6%NU0hB1DEu`gO zj0|}|54_{kE=#rca*}|LvkYqjU$2s;lmQC zvGqG9?9Ekr8OY$L&D#ngaKLBnx#^=NMY1+S4g56zqfXenLK(@x`uZRNA&mw@rFUMZ>o&EM7 zC3UIhy?x0IXZPU)_cvQJnPmDu)m04Kol}CqYpgh?_FzJ}Y}YhZO3D<7dYy%zYK zAJ(@=IdGVC<{8wQ0D+?*cEgJ6gb-Tw4M)oZ>QL5C66yj%;~8Tki1~EhH+v+@E4<7m zw&dsp_lQeH6)0!34n>AdH|}W8CMG#@uZ$S_bi7q!>XRC&>pfLI`vOnr;_+ zw0xN|VLY6TWNlF+$muhJN|4^)2=VJ`0C`;}6uLkltdkD{39JJ_hE!+Ab}o>1Tf-nD z6FVQy8=tke9{0vjTI14)1)^5z)nUEFPX)$?9hr|L4@WRaIjb+?akvjSGT}6g{ulNI z^Y4vU7Dt2KqwRnFA}8c`_*?p9nfHz7|7?(BMqbx)X$*eS>5MNU;()C4TwfF-6cf*+ zBNs1+M~(I9;0zlsv~qtz1P+U*qMPy~^x+#h&vK%dlb4~7+>~d^cl54y8bZqD$PCKW?E?a4rS99ignDDX* z45l)mGRgu31@QA;>=eL?A1!(Y2iNJmwNhQGLT&4mCc;0M8KL-wOwMV-1l`lPsCFv z>^CtWbC=P-fwNPLvA=!V{A2AuMuAi082t+ia=cGqAHcoOD|Lr=d&doIzd@u z)9^yznPeuPZ>I3sCP|nGpjxscSe+$czZy*Bi7q!wweCQM5fN$qUxpt6>8kOo??+F; zgw2ND%l3l~!vbO!AGTcYaT&FvxvDfQ9Nn#j9ae$fj~u|+Kbk-a^`D+o+|BIb%^;)u zPk;B}zb9!KdWIk#V1K8Q`FJHKvC=N#O8j-XHOeFaiHi*#i7bQK%5*VPBqN$|lD2&G ztAki<${?3FnPSa-`OJ|TnZ3n8q8}IaiUK#j;Jd(%|MS@!EBY@YtU%nlLs3?}xARI+#1jeZM>3 z^@qe=E|elBShipu7c7EPkE!|Ii7LVun>7a2pPhSX$?dWxE&1Zi_k602p#hg=X1HTF zB&MS1`;r7cEr_WYyIiBI{QAVG6w_d{e>h;J)Ayd&7trF`e zY{Sf<1ePLZ?q^Ae;8Jvap70Li^!yO0-!YzQRfxM<#-3G_4VyCdzi)Dz*h+5<4zcR1 zD5?$o(h2s#;~XFfXUD{l#RsmT^TnhU)@ki>lb0+%c@JKhPuB{Xd@%xkbqRoiTG=1Q z0@|Om2&LOma8=IDcFxj+KBK?j|6u5c+;CN-DfTK{MIl;6=QwP`_-VDEAMRr0F`2vT z8d&~xuF-a9MuA|3Ket(cACwonnyMOB8oWLN8zse2lR{@`j(GGN%A76J0!97as@))u zNc}F*_e>lyk#zix*0x=pxtSBxph{7-sx-y~3XGA;n6OiBZVaWR;#PaFcddeM77?>z zZZl;;P$mHLhKuu;VM}0h=@2$16`R5X9yai#ufF6%WHs*>r>MFgrbXk%`zOpT;{7H? z;_5$mVX_7m|9sUz^v860)Cs&Fri`<**HVp7hegs@J(`7H9FODS>-Q!GtoFzn3j-BJ zrdMRkUq=lYB(0yIplUrv6eFp0Hp)%%(eJ!jfhws9Z@F#X51?2=9-q%pq3aO%6zVa; z7_q$!`>1RA?@So=*PSyG_cf{qff4OvhacZU;nsVQyb3>I)qql3{xUdbN- zd*A7FtNa1PhMVmEece=YprnzV(&7R1boGC<1dWeI6Pj0`XMYyf+0p+zyoweE`mHQ0pvY}Eg5e2`-$$0yK<2pIxqv6P#bWM|3( zj+zw@ijnsKcYcBDc+*O`{1s{m7mwX8>GvYygJ%(TvQP#Em(8qj=b`C5g0M#pD6>{0@yTqDy=*SuhXU#&?^&?%z?U{W>lwHyJYmMMGT( zq^QeFmzuv)Mvoo|i}rcFG>f(UgM;bB*1L!Tb2-G<^M!ujD%fMw7Uxe;Rc_$t9L`k6 z^}M%$ZvvV`!YF+X4^#<5>nafv@S9y08}D|O1U$@{i~DR<$k)alrk0p5th3e)PHDWv z)&{LKfy%txM>fB79lL@tbFKw`YL~|Yy@0>rjKgX3El$?yJ~s?-k+QRga>L}b0H76r z@K+?O0*b>g&Hkf@$p)bV=+6x2IYWfXeceB1LQ(Ey;^@RT)YwfYdVg2Lnm*W8a9mQl zlx$>PjYYwt@4RADqQG3IeD{}-^P>uNS2T>U{VYLzir2o~$+FqbXcv_`PiJ%{711-N zSS66-a(Cq2WcBUmJDJ@44bp9(cntyR7tlZRF9rFZa01c+4IN!Ut5GT#zH?*TuZxYm zCO|REe*$8H;TKm7enhia$%^`ji~>;mT6Ggaybzn#YXcq;<42})An}Yss$1^eLZVha z0ggt1AS>S}^E4KJ)(Pl4`r-z(T+|8G!&aYhS9peP&sM9774NT8qhcs~MtyR)Qd8lL z2YQWJ%Rfzt5LOAwz|Kuu#Dur^-Vw$e6{o)>IE@gN;`=oDm5a^%Rn3S1YA>K36G^Wr z9U|&MFFadNSU6+Pu1hU2WIJ~{3}0mW?&?z9ajtCL2*DzFDK==$g#vSabpO2EO;6yI zrg_q^5d$(@N4kI^E^t|60%cE*%t+Cl7f3XK#0wVF7F-~8SbqXj%9RrcXxR~KQ!{81 zYS@!aps>2^9uxNn#{eHki`|T81@k!$ABdfcl<)(l5_j4I9G+O3x~TR@Tnt8F{2>2x zv>Zezgf%R9%YOi|r7Gjw*c@!BBPDqEwQQxwC%nP!LOOJhab-deq(l21%PO7z*{W4Y zz0hMXZaGj(1TMxD;J>x}uv!msSq%|yjIMV%5haPglL3Nl#eB=Df}M`SPycIx+{Xm; z&g?DPJg5VOkIdorjK`=%;lk?q!}sELSf*#QGM5l6vAAnZH(y!k+@-hjjJ2)Ki#VyOuPHu5Wd3 z6%A!KJ15<8Vm2x$HrpIzX4}xQ8_)&LF@mZoX8}lM=y5i9xsMlCg^3x^go5EQwyp#W z_E>loFPn`TVr2<;r|`RmJKK*H zzjj(D0A{mQ;5B=7@g|SgjS;$fNo zM65OY^=m{Ypo$m~{M(l_?Dxp*Gu#`~an;Xc;buaDi=M?85MYgwW7Led(>jxoB%pKE zu}X!Tw^GI$)gro(ME> z|8v$KKePQOg|ND1$OSH|&m@Nb7h7i?R#n$-eF-U*76ha_q`SLIN4g zxwPzzg#FOnw~mMV?jDqP`NUaVb{|#`LKohLyg2|{`JcMQgR)=jqVeq;l<6$S@F z^rHcmmkh=-h$_o$!dp z^Sv+zuI$J6Ad8!7Lp@sNjo0G+dB38!>TRTI+3?PZsgq67ucsYx`W+_2?%E5B81#)4 z^rFI#onv?$HPTkl4aYJpbmKp>qL1u|cA0GV-*xoi=Z{=!Y)|M4@#_>C9XetYnR zkI>zP9|3U)6TK^`;2v2F0Pt|N*$?0L^swi>CzNn~KauAf@VcJhE1v9eGAR!4OlNmom1xzWD*TExz^bH_#XgTMOpXyQI=MOMugwNF z5hY@HTCY5=4k*|goeeg&UwaA%Rtx__~JBh7zzs>1-Nkze9x-J)uJ2SNpv zc8%R#ywZRM_04}&q@8$BtBDyY>`7q=#Ba42jW8LQO}-hi5`|}h_f{BUc;Zu|Ki4;^|7US&H&qF9epRKx!<;3Kuljj2&t*zS#@yB3KLdM}cQ?F&05v`(E zf%J0utCHuS{#z$16O*RB5C~HB-7*MkGVp^ri?pLqorC;ZE2%-Uwg&nwy=H z($D4Wa|kWHKkkF2s01iFHot?#CLvGH=29`4JyCH#%oFtBscU~d?&1`8&nMdA~38x{9kcht&gb^El#Abn?Al_i@*}*zk+L-VNRsxh(88h0a3@g{(tdYWB zEY>@l$TqhzUeBVj!YcN8nqG2~TI=5zTTH#swyF~Ah`hS`d;|Tzyu2RI4n2_`TpGP+WHHAXkUE6@`LVt`w+a z5#v*~ilFCk+nZheB%Sc=y{)cce`#%F+K4iED``%|nqglD} zb2Uc9>07>O+Q2aU5^L}J>?X#g%Lz=?kp#1K19O2HVd2|5QaV4 zRuP_FCLlyAC$lj*f~2&8qHCJ_&S2SWEHe$4z{GLkc0TXh7p045r)e3zXkWQjB8-m# zP!v(xnQ>Ca;TE;(?bhqO`nQd&;U9Hcq85Ef^ni>3;ev_Fb}?tLiQ1fGb;?Z|!q+5ZuOT_6HepkJROVbJs-mD5-0#t;!+ARkuIQ^z3(%5+ZdcWE9^nY} zJFEmSEB8(D;&CA~{`%~G=I%#Df%Xx%-1`ZUv8K;F^_Q-9tRqwb?~u+0dc$!cCt;b| zO^!km@v|*5Wvw5Qf0inYS1ASPZTy#z{}2tT6Z9cnx@rg9z``)~=g(PZ(3O^(T22-S zRmMbhynZ%ox>}ZUyX=s>I=|m!h&e9&i{m;%fhmr^KPpqEs{`=GFyD@zIyY_kM4P`m z>PAFBGitnGsT`aQ>_k&;MU+_I`IxVOl-M^oV zyHc_d)HOsajGK7hQ40D+sDo(P`Nu68E@SuB_#NjkE|_4R3dLTGtHXaBQtA#(hZwpc zquKzz&9LFed2?};;hI;ld!arn<&h z-o41=>jJu|nKE_5=R!6wl@Gi55CMG$hDk(ySIP%9zC2$Ng3vlK(O~XcepQ9-rTH%Z z9*7MG`Jx3MJV^>4EEw$CJUaSJU2Dqo4SOdATVjnNB0F$V*Xe<(l%L4!C z*@=0!TI^TWoRng}1b<6CdwWRUeLW>8;_!jcy`B>NX-%E%t}sGi#jAJWIFT|N#+es2 z@@QO(=N8KY)Bi$#p%xKAC{50bDDc;sH}&SmxSnjnxVD0^8;w{9`f23(X-9GIwO(?# zp3hyV3y$8oS4q|1!T6(H81PD0aA!(T?6XNoLLtEHOsU?ia3NEw#`5=jNM1r|!S73g z4hP}Xei|Ci*T($-_1dS2t6!);ug1rN1f_TfC&&@d`^2hqY1Xw6PL7mba26bXu=xoc zQ!~X#R(^G53o%q}px;ZQ!Nx8*pZnRY9~`#S-OwG!QB3GCcrF zZ|Xo7qi!C`pgg=QQU(DkViL+W?Whn@MQK}BA!@~2S&B*(!68*i2wSt6hAz#aLMCr` zfVf1Wqj6rpOjhJE$wBJIBGPBiD4Rfe0n6__V(Ly3Iu>PSFbh zIH9Fdd@0p4RVuj80Ge;GvX0Ci@H4H;1-h=Q{=uz?TCZtNMOe^!IA|0=k$9Q9RY^4M zNqOzC8L(ke#!oqOAt31K^l77^Bh6=D(P6y8hKhV7>BE4;wuPn z!M2uVG?K0Zn)fS=)wGYqcf|SLas=P9&uK%wU%=zi#0($m#B64tQb?xG3SYB(eV=al zpGqt?EYY7*tluDD=Yf5T=ubI4wVxiXzEJS(BoutxMR3#G#nvKyHJ5+<1tXMw>v`HJc`(f>Pa&${Znxvbw;&jt#^Kn58O(m{7=P z-GsL=bu7hpuBb!nDDGq7Wnx>Sn@!_RIo@uMEQC(Slt@oY^_(Q_oIPc?C5$8 zg`8ED(~rQPR3~iE>aRGc{#IW<@fluduyQ>D;WcHikyJrrb#Yc zD*dc#L5^z>_DNqDC|$+QoE~qD(*|e}W&ZGYEm6u|u33Z8dOuqg;MpM)e_imtktd1x zE>buY0@WAGx_G5BBd%qnE;OAyy5Is_K}HP9VE4AcO@sidnzwz8S&6U&T z30ElJ3^fAVAmy^>35Q+UJXXHKMNK<8t=Q!I)+~(Pyida*> z_^+jpSqk`DEPm6wDgAae)Gd;45%E{ygU5`|($3RPe_xHR5K%F~&T2}a$-{yj z)Ve#r=IVvPzzH_)YB`>77wg5)sasLO`C(J&03B#bt2zh@X?MQ6c2=276aSe(`e8>q z?pK57mC8ho5dmK0`0Guxf4#DhqPF*{x~0dir@xr;03Bxjm;89<&+iJlF*#GUyk}dS z#QO_Eub^NiZEGG9%jpnf24;&on+07MQO#h~%HFW&!}2VXLNQkyJ8BhK+v_#W-q+h> z1wCK02OYp!6bE>nJ{{X$KHZc8F#*&>K=DQPy_a+5?MHo)qeVw1q%r}2r3m1Y7L^q{)|>WJ@kf?mspWJS?t@lV+5) z(_ou5@+1We|V)6MuJFA}m3_rqJug1w+@T zU!=^R;mdOhgS4;oC%rNTYGhFu%4fe{l;wCNWWbQ4DtyG8`a5sU^uz;YEEUAhtKDV+ zhn45%aj9JFb!Tt*ZS1qD^l|>FUKJL!Fjr!)({`$FEyYk3eu#8s%G2ornbRf2hfI-J zG)pJYklP}T1i>+JJF~-vV=tretLajnKCceCq(;f~S+n2&=e6r)YCZ8>9Sn|Fu_bOBLg2an8AY(y+w zvw2!fa}UUVS!~kSPE=V>;v!5h+AH2QX@E4p;299}L!GIU>Mt_lIAy1`F}#<$#|I@$ zq|YUpI61I~PswO*{Da^A#~*72U9i3h2AoXq_h_cEC6qbiy@)f4~_ z^Yrh@5MVwvsti>o>LoeZau$Nqf7r6zWw*}v0_5pU#O=RGpClD+Ze)g=)Tq6tu zn$8rDsJL%kwxw^fP@VSBGU6@5hoct+ntT0 zGsfyS0KQ|&$>MR6WuK1h%ku?;s4rT7Zn3M_NH=P@*A42Elq~Q!wEy~68K3VdtMnKD z#l)!7OoQ@#h}^E9_e&4s@kJNnL7b(owdyC+0|(|1i0$<&`ijvk8Me7!z<)aWd$!83 zj@t;P)b_v3&_B4K4|)e&3({c@`UwSlC>HheP)q?@rT~Xl@4GS}0CZ4ZQ;Gfi6T+9| zIaGJu9Z_ni{}5OHZg$|qeDO~nhvlGDqlb7v$;KBc!D3-$%iXy+qREK=+@?ocJdQXh zDI4sn2fzo6lDbe~C^Hf;shcl(_SEsS^R8_yqmDa^+o9Rol#&qVDY=~>s0A9H?K~qh z>XSF#JSQh}e-;-#VEuC|PrV#4e4lT=j?BQq3|~0bsCr3BC7&`h2>kOZ;1YkKc_MhR z2R&~~_sD2vUO%BAmL{hw3wyz38+LiM6_BUhFjNrri!`gt=_u({`f6`Y9bNJaTMImZ z_u&Bi13i)Vr2&~{I~iamTH*GbUsY&55eIf8Xl^g7`uylOA%4(`j2>{yPssphs6dcA zRgnK)!{oF3hk+#!f}mKY0}#D(fN7jLgKobP3ZD9e6afzy4-OAKP;{i=v|a4-HZ;Va zxN^6fb}>OgU>R5FFiB*pGXpzdtf^v;1Uv%v5{0mWg|>})K9=w)#tirW_VfrqZ<)u6 z-;LW>P<$jWGW;tW_Cq8D$rlefpC74v|5sy(2p&8{2O=dV>U}rQK)%ef9mh@uf7x>8 zDz={2Ke#^l25zIicN(Q{YmHe_D{KzpMS*gr2l#lfqXpAC<5FC=#t2zKh*+m};Wr6M zNeq}FF;h5uH1QXFh^%PR0s#nKqC2M9ZfzI)H-5hso66_fJpSZjT(qC}AcywaKt%B2 zR~bNYZw)9jp#0J(PK$2G7XFseCzy^Z;P47Nh&bunJl_|&p!FEFV35U#3UhTYkVa3? zUqPWO`rC7$M9EU8!rYXlIvcxSy2RJTOte1=`g@o#E-YB;V6CQdKljwVnLA!A+tV0FFMVsM`+HF- zlu}LuC8bHw;pa#5CEgDs8X2`C#hys8KhrqU#lKa2pW{F8^t;<2KL88!EK)#nFUSg! zXmX(Rk|$b7qy_N@Tz5(8?IT$N*T3?oeQZsqhyfN58-Q6)4fS0Oaf)@g&j54af0kGe z=u~^&4tHU|@u@ycpgpy=fiB$rKgmEo=wL^7pHuiBQK&HZ>5yz_SkP?+SK=-r0l1ff zDQDF?clTAU|4xM~kSzhSFn)RvSvlB~4v^IE=>UKIIrB{FtGB4J%m$)zhD5M01#j7~ zB0=nL?`kf^IUp9KR204`^2eOLKHCjRX%Ni-z{k6Q+o@U;D^?30U-eO-Xpyz3S~a@_ z|3siNLC_;>1hS{j$`t)h0YcK|d@HaAQyM~v_Y3Gmo&$*OAUsn{m`b56B_LbK4f(4> z+4VeQjwBF!6%FH_&<^2a(kD0nm8$VE?ByXI^AEVIIgg$vp!4nx5G67_S0`ICl{Goh z7E0x}X##a&SU;6hR3&flfk6o*69sZHU1PPvfo|Q#0yOv(!0oZ*2H{VZ+gb*Vzj3r@ z^Ez9Sec&WvUH+pMSOdYa1(=$lE5vxj$8eL$*2zkOR|O?+xwL^AZUJ^x#I{_uIIZ#h zs{d0%4|aM!SZ#rBN1!SX+@CHD2Mk6udpgh-U}%51=c@sj2rAt*U4Ye>0DIDq#kWz? zjrk#+uE`k$giF37FQfGtgpD)VEm|)Dr~zKO3he#X+>Y+?F$ngDC{`rUg++FzKr-iK z$o|5`(f)^LyOUQM)wlpFemP~=)$!EI*l>17YrSt&8fMz)lN(4asX%ZCd)Vn&0nwkm z4B~2);-;}ihP2Vzy`i@>#s4#;f_y#TnN7pxfQQ*W^pmXI-_iCP5t@>5ryO4P^nW=5 zE5KP%(8_aKHAt8n8m02SBJt~%%#6n`miA=Ye~Jt6958h?Anw9H@dx_dK}WX1q}p0f zXlT7dh}td!X4590oS|;EAXE5q+YS0QO=P3{846aVu(m(kQI*A%IWdPoi4!{#<1tIhYJLif=LmXPx1C~9Gcb~2;Zo|Glb~mOBMjJv2W`sk%&A9rRuog zwOig>1H*XP>Yp*AnznsLrEd@VD~x1cXY`2y240>*Heq1*1{huBU(77Sm4f?z4hv6T z2}%gCv7FRcg9om*Q?#l@hUy}hBKS+q&JM;XS_H*f-&gy0@Ujp^N5n2cV?4}t8KRe? z^wGf`_T_E+<(k}D;v3y9edbUP0FmFZimP!awe`N)pIy7VxH)#*CvIT{is6gD@gOfR z-VS7|w^wA5GJF&k3iXCZh1Y{bS;g=GT)3}lbzS(>?(59|bCu2sfiMcIiu_x4@{l&o>yUXR^M)}aFF~+xPhQeO5`<4!E-xA$3mcIDGLjw z75Ul4zH@oBUNCtJzdUqb4eBDg@Z6Z46)5NK zxVCJf)8VbkOOwib$a)b9p_EN9R{sn#t!wDs^+jB;gzk(BjmP^hd%FuJ#nCQ6h-4wW zih)7MTY$@T>=h$DAqn0>Q0NVb{?)E$;?k@@0Tu%bn!WGsT$3XgID6=jMQ?1>uHv=1 z9g)S`P6)}TFe~bS%eXA?jz?vDZ|TNf1ze(4bcCTWpvUPON+rsA5U8P7r2*)OYCgwJ z_WnDZ+D&@B_TSsAqM_Jyfh2aOqiKoKBM;1Y?)_0V(^;GJr>T-#6pHyiJUH<1Fe1X} z=-_1w>4gw`W-!WPG5QH?zoIA#tiLY_2@-_@yz!)v#kZCO?LXi4=ey%@U|Bciw9j4@ zD!fD*a7^dknOISgZo3KB{BmsfjtRq1_e&Yypbe&&{`O>|yarf(UU8U?{PLA$5-;%8 zj~DhP$yu0_B95dunLD_~P-*@9yO2N}k)dekWiDQ*12PK}CXF2apT7YKHWV2q^;Ai| zfz|}$KYy`E=Svv8lhk#xH;?_yC55vV%&RgFHxzaRJm)0EM*a*+39(3NB6t`q_O>{0p8WHBkzi$>z^n(GQpjehg+KuH^^L`p zsoG#1rRqh2OgSKsVPLvF+}|WM+n38TvzHHg_wzSB7@K3 z=pT3aKbH(s2(}MxR62o^7FeQ(b4i$H^0}& z6s*+)TP?)_2%#>mo z2v1nDss#~4PTZ20tET>4a63lTy^~rdZ==`%#6U!o>^XE?lenXUPw=bs$zbTeZv*`V ztD@i&tcq%^J%|4i3_ihXH)onrl6&;#JhGtOpPTVGy10h~DG=Vzpj-D1xGZnLYNkxB zn4t`cdatv+&P$s?Al=H5QWn1$bc$JOv=;*qUfs6iq3|7WkiLzQf<=N=L`OHw!b<3g z{`YssTw?tB2=(A2yeW|8Nz6B=O{`Kr`WSocBmmUbSHS^0)42jWhpU9wwYUFH7_86< zBUZfxTk5|C6nF%UN6cWu-eJP$y@Y|-=C}HPNf0Q#V1^-}oWP%z3rxYKOeFgborwO< zNB_Q%kdo`)evJ_4!323f<`c(sJ{#_ICZG0VeN~qVBKi9{t5rJg-Nk{Qm50nimhA6@ z5i21~bw?ilxq#|F1HoU|lLUGyK`UO*{lL#*C%+ z_bvYSiy-uFULWGrzJ*?lW>3PzYD>66m10ypQ$g}(7lpf4`$FEUVFTy$iV~Zf9$DKz zmm@|P1F65jXECCW`PaCJk%s=%+sG*{Xfu&axp@03!DJoi4;DWuxh{ z<;33H*prRj7~geVDc^vuz53O$fV5avJ)IV7A){VW3}{gor3#j0~wa6U(QSH_FuBQih|m91sHAFDLa+v#f2RWzQWfwSl>zmteE6k_ zT9JIJ`cMMxG-&>YdcGG2djXNrY@uMBV4bdecgukW zaHA`CJp3lJzafL6G^u#)!Z-3oWCXy_K7UEsyFALC;aXt`zorAO-Xb~`iVr7{-ERuc zH|Hy$X{B~5m|Y5}2K;)iYt(5^EN4)^sK05?8hl@9(X&$4Dk(JKl=1bL0LcZIMb$v> zLjyFL)bZF%^UU^$&MsD5!Tgy#4GJdHfG{=mbr)k zXI~gJ5@VRaS!{y6TJphx0gOS}E51#+WX8;$r5l#(Z?<{Io1?|Q7n||{9{cqx5R?Ld zzOPpa2}`6A&59ayvDf&2j9!{yCP`Rot=Lb>Fu>#7CIiM+>P z>r@Oos+fla^`4%66*HLzx=Xd;q*r08MZi2;`Xwm*YwNHV%4L3skjq{PNLS`ExO9E_ zpvVZ1MZMGZu1Pk-tboIQ?SbJWmTso4%wc1P&TMRPr>f=mnq3rN)O~Foe>`Q4L#L)T zQ}SAhP|#BuOdH?2(c0ev4}HO8kwURX^}Y7p=@d)!&4deP=@yRBewF~SI{4_tTQxDV zv1_(pu3Q`J*WEd67d4$ve??CqDCCVPQS1nS4_s!m5LE^Q;-~xS37e{HM;UuZ>m4Pa zHdl^}buZ=EWp?+aNw*E*ltJkXZ*x6bsuY8Kx#kq>*G(7e7n)XsKN}5fjTmE@|DI_6 z8Nq&mAO0-f5Leb%tE6avQ;e@wLUlfx)N=lPYr)1OqBopDn?4jH7|!eZTdkAgbC2i< z9P&W`nqa};srIKv@B1sWotZa!>}R-GSot6wsvK~{4BhqgZqFC&%>8S^1rzoDJ}Ypr z_aOPgzbsE8yc~d1Ytp{&eXqefY?l`9JCr$^L#=y{egfPRN@0+z84!LN^8Uo?>&MTQ zGrBq*-U^kPHSE6{Tgf{OEiQhySxj*7U<90(0n9>~&;3;}awL~sGtwi)Tep4!n=Orf zl4<9{;r-pzr*G1DZEm#_XMa(rMPX@s-}RzC`3+!W`d_M0pZGtMcAZ~s+1aheg)j^t z;G^2(FusfEyKitlErKfiNq+jJ6lbEJuwx$6BV9r<1H@`p#Y)*?;wA51Zi6;QY6{{S z>tef?hgvctAw%fvy}>oTZxvcI)Y%cm&)n4)1wrm-mRnv2KEGQ?OLa3i0@@A0S?t~% zprIR7XY#cdxEyxEeGgTh3ETN)2V_bN^NYV^Gy!u9gBD}UwcEeud5Hb{xl`X~oFE=2 z`=&iUK!2_EVtINxdbapk8qcB(fNaMoMHqp~S>9O2Rlo_d|1xO6OPFL<0qfFg6dWRW zJe2{cU?WoOQqWJxE}73&kSv_b)M0zEX=;XJJ^{ay5p&!IV9;lvkdzq2mmuU&Li~Gr z!Mpw1Z+wJ2vRTtoVR|@(razNSX7ak@EGY&K0K5r23l+3t4 zv)tx!O1ZESMc7huu}rWrmCa zpgsx|v+rLib`2$0=YWZ_7b|FPNxvT!vZJ#Q!+yn(@f z=;F2?c>H%*KFp{_q%@dMC_=FiBQv&?bq#!rpFg@$62@z<$%>Y zW@!jgwkkkuI(58r8JN{Spb^VhrbDEIT!X-A@vSv1Y81NnNQyl_F@SS&1|7I_^BFFy zDhcFg`FPV@sDmw#X>U8Z6l#-d&!TG1iO6qh%tH|%=9%%jFd>5-wQcaap^A>{0eFKG z`Wd(T$LZ{-guI4bnLYA+dIwBQiv?SVs!5$75FY8GKy~^Pv#U zxn4u-#t?|6THhM$OKf>Y-BUHsE@x`*A57Yr)h_5&{QX;Oc*Gme5Wpy05gtZy=BH;% z0E@LjEy7wBn;AaFDPwayGW)xYp&7NKHMG>U=T1_;f8^M&j~M^u19675{@Ft&uw%m3!=~+WZ+M+${3U)+Lryv>)7zL zMdj&>!wP(Mb0m-5nR34UY1j6;1xDK@rauEuMXYnXpo5FC2EJ1B3}s&`-Z#h4{zoT~ zNK0DFm z<7WU4Bv+n&5lkR^!#Q|$d$dFE){{G#R6pboE5`_OLv z-FBms?s~a*3>yCvlH%s;B9FzQN6th?Pq5wtG|jSG=%~o`f(Absa|XAKEAvHI`^8)h zv2>GhBq_p5Ff;kLRtVquPlb__=aX+5d7bece0D28?$8S!cu>j${jElYq~$#IHs6}n zOyyg`4 zgfA}1SDfW_4$%k&XArE?vn*9$L(u?tiZRk=?2!BPsQ>15$6VZvT8d|oMZc0DUL~is zCNZ^_HHGJ14YiC(8S2?xlf}EFq{++pZeTkK2~{w)d~HwW;Fc>Bk8^4}GG;+}P<4}g(;H@Srna~pd1}l} zLAGahk?{$Z^&J4T{jN&8A*27}b@Pl}FzQmsNOBYd1Heb*6I8VJ?w-9D`g}>n>wfx% z^)B6iMC7wc1OEZ+!FR`yb{N(P$76Pkb2Imqq!VTm^xy8EhaI*jO+rq0(yS`A>R8K* z=YzVuD%(T6?*VJx2{Z5x{@VL6(f9Y3(7hXWo9lAu`WZ;&$oIZt*1Moe4Q?Uk`m}ZQ zN8BG-ku2j?u<;zW`TOH~NZgsHTz&SlI)fIgMbuca`sXN{)e(yEX2SD+VTd@bwoAMIud^l9|k{G2D&*+e>U>!)n-N4^}YscP8R1KcKJ(wx%y%oFnI({P%E;WFus+SmjNLcTb|(GzXseE4TtYSLoP}Pu_fxY8286Lx;Q{f z7ln$iMj9pf`1bCCuK=qJ7MIDhxaTi;){-(o?v6AkCXX0%3d2xQ^!8MEsj<{B%&;$QC z%2zk4Q#SaBuek3T4~w;%ST$Z##eB*e-n-jnXDlIi-o}*EPi42b?YR5s8eu;!@+C5q zws)L8fj*O0Z&mmyOsdiTyj{qn!+owTSG8--#hlYT`bC6A>;8PxbV3x-UF=Al2FIC^3kJNMf-L>(Jb2n2pW)?-5Xg&e zLheIyyWhX&Y8vJnS=fKlDnOxwkjHFIfQW78tbCg3Ce-C;yi#ug$C}O;Ani#Bkw+$b z!RgeQJASa#f|!1~@#GY3#}I%m3Rtwf+u=FN00$BiX4J%;z(Clct0+pD`^Wb}Ii@I- z_1X1)fthE-?j=uCpHz(yaF6s+B578=qr~S*-e4(u?KMW{)72Ns+jdv|{TQUQsje*h z#X~KO5wD~-LF3U_wVYQE7zuJ~%E+Ai$1mfko@NPph4uY>(!iD7F%JSTI)3P2MuKC= zKG1H>Ih_Gp@*5x!^h1357zI1W4kdZgu+5z#R2k1obiO-3BM{q9db*|hDH_4~hn=Y{ z7W}%qlAMgN15s*QfLj0u^D<-1?>^3|J}YQ0R})aEZmaYuh`z2D0}v5s(-@me5XLePthNgn^e>i8)RG4LdcQz6baK4;8fK;Q^xx5y!K z$VyPhqEMHvE-&k_eL%jHw;V}3^R~@j4pet@nMZBiMKM?hc%VtVgcqRg zg-);GKM=N6Egfrkn?a_hf#Th`Ro?1X-*H(%obrBuu?aCg{HS8v49*Q0fy1w<`El)^ zi^JK_caASyVc`x8kXZTZ!(u+Yw$uwRqd^>?A|WOxJdFp(A&I*$JO|cp8GO%XKiQc5 zrXJ508opz?#;Y#|YGZpJ2apgWcP4RaR^o9w)yyRRFr6#rIu?$8!e1t?2oEC%B?|!q zqt}j5j(mr|Jf;g<HIoL9_6?!8kMwza0WpONkMB-Bxbek;>J35Jil* zeW#irpW;GFIqT1_!S8v=jDb{KDi*v(H+Vf1Pi5^(^5G*KqF`?zc?8!&!>;g-Y(6(P z(t~mjA-~(Bv(RBTd2NtJaCWs7uZ8@af-)wtes{&k_xiv`ufYD`piC-Sv7pB##%|SX z9t2;_|BQLtcCp+t@A0+cS|B_G9DX~`3?kH`7jp3L&St|SKQH#DIYmV>$5StiR|m2x z=Fe9@XQ?(Fb+15rR!b=D`u+uh7z-L*FBs~ZHOOiKM_HO5^>OU>fH@0kX30kr1~m4_ z?c3jgEQS$tMS>2@13#>KwY-g&IWiu z6*S(rmrTZSp3h4Q746GA-b#vcWwa7|2K5UpK{>wR;zG92N>-MR!jRYhUQGXf^%h!1 z_>?8~LnH)3`e*`Of+iFK0-vZcm)Jo~V$9&05*_5N!DYP=pEDiBj~nb-@)IXF_*+Q7 zmw@h}F+PHmL5)Qf9O5}0O(BF&Aqxf>en=9RN%!y&HtyYMn)6j!tg>IP@1KLph{r4S zqx#y%IBS3hwNo=t1dVt&-)jVNbfLuv>{$oL=5$UkDPy{E2?V3|BE`E5T#O%hJ@MeL zsZ)75;4O6jixtw1G0{<>MND#HYI8Z=XkK!#KKpOVL#DnTMwq80W(

XUVO$zFYl7lCO(U3;pv=i+pXD*wC78!mPsG)ob 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