From dfd7bb4f9fefaa1e156127b7aa7b9b2487e649b4 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 17 Feb 2022 12:46:29 -0500 Subject: [PATCH 01/26] Allow more time for profile print when Sockets and Threads watchdogs request info (#44218) (cherry picked from commit 105c77ae68512765b5f769ff3ef05794b8c853dc) --- stdlib/Sockets/test/runtests.jl | 2 +- test/threads_exec.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/Sockets/test/runtests.jl b/stdlib/Sockets/test/runtests.jl index 6b8b1be6e055f..a27bb89408f1d 100644 --- a/stdlib/Sockets/test/runtests.jl +++ b/stdlib/Sockets/test/runtests.jl @@ -16,7 +16,7 @@ function killjob(d) end if @isdefined(SIGINFO) ccall(:uv_kill, Cint, (Cint, Cint), getpid(), SIGINFO) - sleep(1) + sleep(5) # Allow time for profile to collect and print before killing end ccall(:uv_kill, Cint, (Cint, Cint), getpid(), Base.SIGTERM) nothing diff --git a/test/threads_exec.jl b/test/threads_exec.jl index 1b146f48e8c57..63e3be1b88cb7 100644 --- a/test/threads_exec.jl +++ b/test/threads_exec.jl @@ -16,7 +16,7 @@ function killjob(d) end if @isdefined(SIGINFO) ccall(:uv_kill, Cint, (Cint, Cint), getpid(), SIGINFO) - sleep(1) + sleep(5) # Allow time for profile to collect and print before killing end ccall(:uv_kill, Cint, (Cint, Cint), getpid(), Base.SIGTERM) nothing From ed41f7c398fa7a80642658e6b947904ece07059e Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 17 Feb 2022 12:52:29 -0500 Subject: [PATCH 02/26] Profile: Minor fixes. Signal handling fix. (#44199) (cherry picked from commit 072c041f8fe19a3c94dc4a249de975c64a004d62) --- src/signal-handling.c | 11 ++++++----- src/signals-unix.c | 20 +++++++++++++++----- stdlib/Profile/Project.toml | 3 ++- stdlib/Profile/src/Profile.jl | 5 +++-- stdlib/Profile/test/runtests.jl | 2 -- 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/signal-handling.c b/src/signal-handling.c index 142f03b6c899d..d9c53b0211eae 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -132,22 +132,23 @@ static size_t jl_safe_read_mem(const volatile char *ptr, char *out, size_t len) static double profile_autostop_time = -1.0; static double profile_peek_duration = 1.0; // seconds -double jl_get_profile_peek_duration(void) { +double jl_get_profile_peek_duration(void) +{ return profile_peek_duration; } -void jl_set_profile_peek_duration(double t) { +void jl_set_profile_peek_duration(double t) +{ profile_peek_duration = t; - return; } uintptr_t profile_show_peek_cond_loc; JL_DLLEXPORT void jl_set_peek_cond(uintptr_t cond) { profile_show_peek_cond_loc = cond; - return; } -static void jl_check_profile_autostop(void) { +static void jl_check_profile_autostop(void) +{ if ((profile_autostop_time != -1.0) && (jl_hrtime() > profile_autostop_time)) { profile_autostop_time = -1.0; jl_profile_stop_timer(); diff --git a/src/signals-unix.c b/src/signals-unix.c index b2e3ef2952029..8a5822d451335 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -539,9 +539,13 @@ JL_DLLEXPORT int jl_profile_start_timer(void) JL_DLLEXPORT void jl_profile_stop_timer(void) { - if (running) + if (running) { timer_delete(timerprof); - running = 0; + // Because SIGUSR1 is multipurpose, care must be taken for running = 0 to be set after the timer has fully stopped. + // There may be a pending signal emitted from the timer so wait a few timer cycles + sleep_ms((nsecprof / GIGA) * 1000 * 3); + running = 0; + } } #elif defined(HAVE_ITIMER) @@ -556,18 +560,24 @@ JL_DLLEXPORT int jl_profile_start_timer(void) timerprof.it_interval.tv_usec = 0; timerprof.it_value.tv_sec = nsecprof / GIGA; timerprof.it_value.tv_usec = ((nsecprof % GIGA) + 999) / 1000; - if (setitimer(ITIMER_PROF, &timerprof, NULL) == -1) - return -3; + // 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) { - running = 0; memset(&timerprof, 0, sizeof(timerprof)); setitimer(ITIMER_PROF, &timerprof, NULL); + // Because SIGUSR1 is multipurpose, care must be taken for running = 0 to be set after the timer has fully stopped. + // There may be a pending signal emitted from the timer so wait a few timer cycles + sleep_ms((nsecprof / GIGA) * 1000 * 3); + running = 0; } } diff --git a/stdlib/Profile/Project.toml b/stdlib/Profile/Project.toml index 1d13dad22233a..334d475832b6d 100644 --- a/stdlib/Profile/Project.toml +++ b/stdlib/Profile/Project.toml @@ -5,9 +5,10 @@ uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" [extras] +Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Logging", "Serialization", "Test"] +test = ["Base64", "Logging", "Serialization", "Test"] diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index 50e03d2c79a5a..b11dfb488c373 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -54,7 +54,7 @@ function _peek_report() iob = IOBuffer() ioc = IOContext(IOContext(iob, stdout), :displaysize=>displaysize(stdout)) print(ioc, groupby = [:thread, :task]) - Base.print(stdout, String(resize!(iob.data, iob.size))) + Base.print(stdout, String(take!(iob))) end # This is a ref so that it can be overridden by other profile info consumers. const peek_report = Ref{Function}(_peek_report) @@ -73,7 +73,8 @@ 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 = """ -Profile.@profile sleep(0.5) +import Profile +Profile.@profile while Profile.len_data() < 1000; rand(10,10) * rand(10,10); end Profile.peek_report[]() Profile.clear() """ diff --git a/stdlib/Profile/test/runtests.jl b/stdlib/Profile/test/runtests.jl index 50917b9797c7d..ab87d497fe93e 100644 --- a/stdlib/Profile/test/runtests.jl +++ b/stdlib/Profile/test/runtests.jl @@ -226,8 +226,6 @@ if Sys.isbsd() || Sys.islinux() end end end -else - @warn "Skipping \"SIGINFO/SIGUSR1 profile triggering\" test as it is not supported on this platform" end @testset "FlameGraphs" begin From 54386c78ce91875800aab5ef0f02e8d8db093f03 Mon Sep 17 00:00:00 2001 From: Thomas Christensen Date: Thu, 17 Feb 2022 16:31:22 -0500 Subject: [PATCH 03/26] make `cat(As..., dims=Val((1,2,...))` work (#44211) (cherry picked from commit 1ad2396f05fa63a71e5842c814791cd7c7715100) --- base/abstractarray.jl | 10 ++++++---- test/abstractarray.jl | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 50b83dff86e6b..9c3cb23865dff 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1712,13 +1712,15 @@ end _cs(d, a, b) = (a == b ? a : throw(DimensionMismatch( "mismatch in dimension $d (expected $a got $b)"))) -function dims2cat(::Val{n}) where {n} - n <= 0 && throw(ArgumentError("cat dimension must be a positive integer, but got $n")) - ntuple(i -> (i == n), Val(n)) +function dims2cat(::Val{dims}) where dims + if any(≤(0), dims) + throw(ArgumentError("All cat dimensions must be positive integers, but got $dims")) + end + ntuple(in(dims), maximum(dims)) end function dims2cat(dims) - if any(dims .<= 0) + if any(≤(0), dims) throw(ArgumentError("All cat dimensions must be positive integers, but got $dims")) end ntuple(in(dims), maximum(dims)) diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 060f1ffa8b8cb..d650cf67ebf11 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -732,6 +732,7 @@ function test_cat(::Type{TestAbstractArray}) @test @inferred(cat(As...; dims=Val(3))) == zeros(2, 2, 2) cat3v(As) = cat(As...; dims=Val(3)) @test @inferred(cat3v(As)) == zeros(2, 2, 2) + @test @inferred(cat(As...; dims=Val((1,2)))) == zeros(4, 4) end function test_ind2sub(::Type{TestAbstractArray}) From a9d1ad20a4a1be7997e70114a674c083cbb7965c Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Fri, 18 Feb 2022 09:47:33 +0100 Subject: [PATCH 04/26] Reduce number of `getindex(::Type, ...)` methods (#44127) Previously, there were special cases for `T[]`, `T[a]`, `T[a,b]` and `T[a,b,c]`. Together with the general case for more elements, that meant five methods to consider in cases like `T[x...]` where the length of `x` was not known at compile time. That was beyond the inference limit and such a call would be inferred as `Any`. So this change gets rid of all the special cases. The loop-based general case worked well if all arguments were of the same type, but otherwise suffered from type-instability inside the loop. Without the special cases for low element count this would be hit more often, so for the non-homogeneous case, the loop is replaced with a call to `afoldl` that basically unrolls the loop for up to 32 elements. (cherry picked from commit b8e5d7e7e2e039911e9c6f39e7e5c301fcfc8cb6) --- base/array.jl | 17 ++++++++++------- test/arrayops.jl | 4 +++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/base/array.jl b/base/array.jl index cf5bbc05e412a..b8ad7e137f25e 100644 --- a/base/array.jl +++ b/base/array.jl @@ -402,17 +402,20 @@ julia> getindex(Int8, 1, 2, 3) """ function getindex(::Type{T}, vals...) where T a = Vector{T}(undef, length(vals)) - @inbounds for i = 1:length(vals) - a[i] = vals[i] + if vals isa NTuple + @inbounds for i in 1:length(vals) + a[i] = vals[i] + end + else + # use afoldl to avoid type instability inside loop + afoldl(1, vals...) do i, v + @inbounds a[i] = v + return i + 1 + end end return a end -getindex(::Type{T}) where {T} = (@inline; Vector{T}()) -getindex(::Type{T}, x) where {T} = (@inline; a = Vector{T}(undef, 1); @inbounds a[1] = x; a) -getindex(::Type{T}, x, y) where {T} = (@inline; a = Vector{T}(undef, 2); @inbounds (a[1] = x; a[2] = y); a) -getindex(::Type{T}, x, y, z) where {T} = (@inline; a = Vector{T}(undef, 3); @inbounds (a[1] = x; a[2] = y; a[3] = z); a) - function getindex(::Type{Any}, @nospecialize vals...) a = Vector{Any}(undef, length(vals)) @inbounds for i = 1:length(vals) diff --git a/test/arrayops.jl b/test/arrayops.jl index 84b0e7d259f45..b2badb66ce93d 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -2318,10 +2318,12 @@ let A = zeros(Int, 2, 2), B = zeros(Float64, 2, 2) f40() = Float64[A A] f41() = [A B] f42() = Int[A B] + f43() = Int[A...] + f44() = Float64[A..., B...] for f in [f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, f21, f22, f23, f24, f25, f26, f27, f28, f29, f30, - f31, f32, f33, f34, f35, f36, f37, f38, f39, f40, f41, f42] + f31, f32, f33, f34, f35, f36, f37, f38, f39, f40, f41, f42, f43, f44] @test isconcretetype(Base.return_types(f, ())[1]) end end From 5f0d55196debf25b77ef4770bbf71e7d0a6d335a Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 18 Feb 2022 23:05:27 +0900 Subject: [PATCH 05/26] `AbstractInterpreter`: make it easier to overload pure/concrete-eval (#44224) fix #44174. (cherry picked from commit daa084971c57fa0999588fe3973c78fb2c8f88b0) --- base/compiler/abstractinterpretation.jl | 144 +++++++++++++----------- base/compiler/methodtable.jl | 11 +- 2 files changed, 88 insertions(+), 67 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 6a9837547834b..4fdaa6257686c 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -65,13 +65,8 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), const_results = Union{InferenceResult,Nothing,ConstResult}[] multiple_matches = napplicable > 1 - if f !== nothing && napplicable == 1 && is_method_pure(applicable[1]::MethodMatch) - val = pure_eval_call(f, argtypes) - if val !== nothing - # TODO: add some sort of edge(s) - return CallMeta(val, MethodResultPure(info)) - end - end + val = pure_eval_call(interp, f, applicable, arginfo, sv) + val !== nothing && return CallMeta(val, MethodResultPure(info)) # TODO: add some sort of edge(s) fargs = arginfo.fargs for i in 1:napplicable @@ -619,27 +614,85 @@ struct MethodCallResult end end +function pure_eval_eligible(interp::AbstractInterpreter, + @nospecialize(f), applicable::Vector{Any}, arginfo::ArgInfo, sv::InferenceState) + return !isoverlayed(method_table(interp, sv)) && + f !== nothing && + length(applicable) == 1 && + is_method_pure(applicable[1]::MethodMatch) && + is_all_const_arg(arginfo) +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, sv::InferenceState) + pure_eval_eligible(interp, f, applicable, arginfo, sv) || return nothing + return _pure_eval_call(f, arginfo) +end +function _pure_eval_call(@nospecialize(f), arginfo::ArgInfo) + args = collect_const_args(arginfo) + try + value = Core._apply_pure(f, args) + return Const(value) + catch + return nothing + end +end + +function concrete_eval_eligible(interp::AbstractInterpreter, + @nospecialize(f), result::MethodCallResult, arginfo::ArgInfo, sv::InferenceState) + return !isoverlayed(method_table(interp, sv)) && + f !== nothing && + result.edge !== nothing && + is_total_or_error(result.edge_effects) && + is_all_const_arg(arginfo) +end + function is_all_const_arg((; argtypes)::ArgInfo) - for a in argtypes - if !isa(a, Const) && !isconstType(a) && !issingletontype(a) - return false - end + for i = 2:length(argtypes) + a = widenconditional(argtypes[i]) + isa(a, Const) || isconstType(a) || issingletontype(a) || return false end return true end -function concrete_eval_const_proven_total_or_error(interp::AbstractInterpreter, - @nospecialize(f), (; argtypes)::ArgInfo, _::InferenceState) - args = Any[ (a = widenconditional(argtypes[i]); - isa(a, Const) ? a.val : - isconstType(a) ? (a::DataType).parameters[1] : - (a::DataType).instance) for i in 2:length(argtypes) ] +function collect_const_args((; argtypes)::ArgInfo) + return Any[ let a = widenconditional(argtypes[i]) + isa(a, Const) ? a.val : + isconstType(a) ? (a::DataType).parameters[1] : + (a::DataType).instance + end for i in 2:length(argtypes) ] +end + +function concrete_eval_call(interp::AbstractInterpreter, + @nospecialize(f), result::MethodCallResult, arginfo::ArgInfo, sv::InferenceState) + concrete_eval_eligible(interp, f, result, arginfo, sv) || return nothing + args = collect_const_args(arginfo) try value = Core._call_in_world_total(get_world_counter(interp), f, args...) - return Const(value) - catch e - return nothing + if is_inlineable_constant(value) || call_result_unused(sv) + # If the constant is not inlineable, still do the const-prop, since the + # code that led to the creation of the Const may be inlineable in the same + # circumstance and may be optimizable. + return ConstCallResults(Const(value), ConstResult(result.edge, value), EFFECTS_TOTAL) + end + catch + # The evaulation threw. By :consistent-cy, we're guaranteed this would have happened at runtime + return ConstCallResults(Union{}, ConstResult(result.edge), result.edge_effects) end + return nothing end function const_prop_enabled(interp::AbstractInterpreter, sv::InferenceState, match::MethodMatch) @@ -671,19 +724,10 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, resul if !const_prop_enabled(interp, sv, match) return nothing end - if f !== nothing && result.edge !== nothing && is_total_or_error(result.edge_effects) && is_all_const_arg(arginfo) - rt = concrete_eval_const_proven_total_or_error(interp, f, arginfo, sv) + val = concrete_eval_call(interp, f, result, arginfo, sv) + if val !== nothing add_backedge!(result.edge, sv) - if rt === nothing - # The evaulation threw. By :consistent-cy, we're guaranteed this would have happened at runtime - return ConstCallResults(Union{}, ConstResult(result.edge), result.edge_effects) - end - if is_inlineable_constant(rt.val) || call_result_unused(sv) - # If the constant is not inlineable, still do the const-prop, since the - # code that led to the creation of the Const may be inlineable in the same - # circumstance and may be optimizable. - return ConstCallResults(rt, ConstResult(result.edge, rt.val), EFFECTS_TOTAL) - end + return val end mi = maybe_get_const_prop_profitable(interp, result, f, arginfo, match, sv) mi === nothing && return nothing @@ -1218,36 +1262,6 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, sv:: return CallMeta(res, retinfo) 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(@nospecialize(f), argtypes::Vector{Any}) - for i = 2:length(argtypes) - a = widenconditional(argtypes[i]) - if !(isa(a, Const) || isconstType(a)) - return nothing - end - end - args = Any[ (a = widenconditional(argtypes[i]); - isa(a, Const) ? a.val : (a::DataType).parameters[1]) for i in 2:length(argtypes) ] - try - value = Core._apply_pure(f, args) - return Const(value) - catch - return nothing - end -end - function argtype_by_index(argtypes::Vector{Any}, i::Int) n = length(argtypes) na = argtypes[n] @@ -1586,8 +1600,10 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), elseif max_methods > 1 && istopfunction(f, :copyto!) max_methods = 1 elseif la == 3 && istopfunction(f, :typejoin) - val = pure_eval_call(f, argtypes) - return CallMeta(val === nothing ? Type : val, MethodResultPure()) + if is_all_const_arg(arginfo) + val = _pure_eval_call(f, arginfo) + return CallMeta(val === nothing ? Type : val, MethodResultPure()) + end end atype = argtypes_to_type(argtypes) return abstract_call_gf_by_type(interp, f, arginfo, atype, sv, max_methods) diff --git a/base/compiler/methodtable.jl b/base/compiler/methodtable.jl index 93020ae6a2639..70beb259cb6a5 100644 --- a/base/compiler/methodtable.jl +++ b/base/compiler/methodtable.jl @@ -84,9 +84,9 @@ function findall(@nospecialize(sig::Type), table::OverlayMethodTable; limit::Int _min_val[] = typemin(UInt) _max_val[] = typemax(UInt) ms = _methods_by_ftype(sig, nothing, limit, table.world, false, _min_val, _max_val, _ambig) - end - if ms === false - return missing + if ms === false + return missing + end end return MethodLookupResult(ms::Vector{Any}, WorldRange(_min_val[], _max_val[]), _ambig[] != 0) end @@ -123,3 +123,8 @@ end # This query is not cached findsup(@nospecialize(sig::Type), table::CachedMethodTable) = findsup(sig, table.table) + +isoverlayed(::MethodTableView) = error("unsatisfied MethodTableView interface") +isoverlayed(::InternalMethodTable) = false +isoverlayed(::OverlayMethodTable) = true +isoverlayed(mt::CachedMethodTable) = isoverlayed(mt.table) From 681e31877e042f9381a0e2ef373fa9e65ecacfc0 Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Fri, 18 Feb 2022 13:03:35 -0500 Subject: [PATCH 06/26] set type of statement when processing `GlobalRef`s (#44200) Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Co-authored-by: Jameson Nash (cherry picked from commit 39e849a9efaf8a2a759c3c33d9155df0a27ef04e) --- base/compiler/ssair/ir.jl | 6 +++++- test/compiler/inline.jl | 12 ++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index a86e125fcb307..039d557b2e9e8 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -1000,7 +1000,11 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr elseif isa(stmt, GotoNode) && compact.cfg_transforms_enabled result[result_idx][:inst] = GotoNode(compact.bb_rename_succ[stmt.label]) result_idx += 1 - elseif isa(stmt, GlobalRef) || isa(stmt, GotoNode) + elseif isa(stmt, GlobalRef) + result[result_idx][:inst] = stmt + result[result_idx][:type] = argextype(stmt, compact) + result_idx += 1 + elseif isa(stmt, GotoNode) result[result_idx][:inst] = stmt result_idx += 1 elseif isa(stmt, GotoIfNot) && compact.cfg_transforms_enabled diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 9347acc83f13f..aa5297066b67b 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -1069,3 +1069,15 @@ end @test fully_eliminated() do issue41694(2) end + +global x44200::Int = 0 +function f44200() + global x = 0 + while x < 10 + x += 1 + end + x +end +let src = code_typed1(f44200) + @test count(x -> isa(x, Core.PiNode), src.code) == 0 +end From 9e634bcbe516c06dd66999735983aa395e00e5ab Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Fri, 18 Feb 2022 13:04:07 -0500 Subject: [PATCH 07/26] fix nothrow check for `get_binding_type` (#44229) This got missed in #43671. (cherry picked from commit 6c51d9e87173a6a32dc24f2c0aeac24e12cd93f4) --- base/compiler/tfuncs.jl | 3 ++- test/compiler/inference.jl | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index ec56b826d1491..54b89c9a04f43 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1705,7 +1705,8 @@ function _builtin_nothrow(@nospecialize(f), argtypes::Array{Any,1}, @nospecializ end return false elseif f === Core.get_binding_type - return length(argtypes) == 2 + length(argtypes) == 2 || return false + return argtypes[1] ⊑ Module && argtypes[2] ⊑ Symbol elseif f === donotdelete return true end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index d4fbe0dbfbf6b..61058f9589f52 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -4028,3 +4028,5 @@ function f_boundscheck_elim(n) ntuple(x->(@inbounds getfield(sin, x)), n) end @test Tuple{} <: code_typed(f_boundscheck_elim, Tuple{Int})[1][2] + +@test !Core.Compiler.builtin_nothrow(Core.get_binding_type, Any[Rational{Int}, Core.Const(:foo)], Any) From bd58e0ce99a5be9d5f72f0aaaf6d192a6ce1c3bf Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Fri, 18 Feb 2022 16:25:34 -0500 Subject: [PATCH 08/26] Fix build warning in gc-alloc-profiler.cpp: cast to (size_t) (#44180) `std::vector::size()` returns a `size_t`, so we need to cast the int `jl_n_threads` to a `size_t` for the comparison. (cherry picked from commit 15dcd5bb9c3458d52eb70dc59d685af399b54189) --- src/gc-alloc-profiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gc-alloc-profiler.cpp b/src/gc-alloc-profiler.cpp index a5af9031cd0e3..88177e0ea20ef 100644 --- a/src/gc-alloc-profiler.cpp +++ b/src/gc-alloc-profiler.cpp @@ -71,7 +71,7 @@ extern "C" { // Needed since these functions doesn't take any arguments. JL_DLLEXPORT void jl_start_alloc_profile(double sample_rate) { // We only need to do this once, the first time this is called. - while (g_alloc_profile.per_thread_profiles.size() < jl_n_threads) { + while (g_alloc_profile.per_thread_profiles.size() < (size_t)jl_n_threads) { g_alloc_profile.per_thread_profiles.push_back(jl_per_thread_alloc_profile_t{}); } From a70cfd12dd4049b2f5483695355fda0a64153d9d Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 18 Feb 2022 16:32:57 -0500 Subject: [PATCH 09/26] fix bug in `addenv` for environment entries with embedded `=` (#44212) Co-authored-by: Jameson Nash Co-authored-by: Fredrik Ekre (cherry picked from commit f5d9b86cfd19e39ceedf6056ef72cc2071c7478c) --- base/cmd.jl | 13 +++++++++++-- test/spawn.jl | 6 ++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/base/cmd.jl b/base/cmd.jl index 5094dea908440..ab639a2b185c8 100644 --- a/base/cmd.jl +++ b/base/cmd.jl @@ -262,6 +262,15 @@ setenv(cmd::Cmd, env::Pair{<:AbstractString}...; dir=cmd.dir) = setenv(cmd, env; dir=dir) setenv(cmd::Cmd; dir=cmd.dir) = Cmd(cmd; dir=dir) +# split environment entry string into before and after first `=` (key and value) +function splitenv(e::String) + i = findnext('=', e, 2) + if i === nothing + throw(ArgumentError("malformed environment entry")) + end + e[1:prevind(e, i)], e[nextind(e, i):end] +end + """ addenv(command::Cmd, env...; inherit::Bool = true) @@ -282,7 +291,7 @@ function addenv(cmd::Cmd, env::Dict; inherit::Bool = true) merge!(new_env, ENV) end else - for (k, v) in eachsplit.(cmd.env, "=") + for (k, v) in splitenv.(cmd.env) new_env[string(k)::String] = string(v)::String end end @@ -301,7 +310,7 @@ function addenv(cmd::Cmd, pairs::Pair{<:AbstractString}...; inherit::Bool = true end function addenv(cmd::Cmd, env::Vector{<:AbstractString}; inherit::Bool = true) - return addenv(cmd, Dict(k => v for (k, v) in eachsplit.(env, "=")); inherit) + return addenv(cmd, Dict(k => v for (k, v) in splitenv.(env)); inherit) end """ diff --git a/test/spawn.jl b/test/spawn.jl index 92232ba5d70f6..a8a2af40643ff 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -826,6 +826,12 @@ end dir = joinpath(pwd(), "dir") cmd = addenv(setenv(`julia`; dir=dir), Dict()) @test cmd.dir == dir + + @test addenv(``, ["a=b=c"], inherit=false).env == ["a=b=c"] + cmd = addenv(``, "a"=>"b=c", inherit=false) + @test cmd.env == ["a=b=c"] + cmd = addenv(cmd, "b"=>"b") + @test issetequal(cmd.env, ["b=b", "a=b=c"]) end @testset "setenv with dir (with tests for #42131)" begin From 18ea404705d945884fd61a61be0fe3e238dc8a71 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sun, 20 Feb 2022 11:05:33 +0900 Subject: [PATCH 10/26] fix EscapeAnalysis.jl documentation (#44257) --- doc/make.jl | 1 + doc/src/devdocs/EscapeAnalysis.md | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/make.jl b/doc/make.jl index bb7ef83048178..f814ba43382e4 100644 --- a/doc/make.jl +++ b/doc/make.jl @@ -292,6 +292,7 @@ else analytics = "UA-28835595-6", collapselevel = 1, sidebar_sitename = false, + ansicolor = true, ) end diff --git a/doc/src/devdocs/EscapeAnalysis.md b/doc/src/devdocs/EscapeAnalysis.md index c4a5f14faa8ec..983a6782ccc79 100644 --- a/doc/src/devdocs/EscapeAnalysis.md +++ b/doc/src/devdocs/EscapeAnalysis.md @@ -1,3 +1,5 @@ +# `EscapeAnalysis` + `Core.Compiler.EscapeAnalysis` is a compiler utility module that aims to analyze escape information of [Julia's SSA-form IR](@ref Julia-SSA-form-IR) a.k.a. `IRCode`. @@ -18,8 +20,7 @@ This escape analysis aims to: You can give a try to the escape analysis by loading the `EAUtils.jl` utility script that define the convenience entries `code_escapes` and `@code_escapes` for testing and debugging purposes: ```@repl EAUtils -include(normpath(Sys.BINDIR::String, "..", "share", "julia", "test", "testhelpers", "EAUtils.jl")) -using EAUtils +include(normpath(Sys.BINDIR, "..", "share", "julia", "test", "compiler", "EscapeAnalysis", "EAUtils.jl")); using .EAUtils mutable struct SafeRef{T} x::T From e84a9dec81e94dc280dbf5120c68c7d0d67f7303 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sun, 20 Feb 2022 13:16:59 +0900 Subject: [PATCH 11/26] effects: make `:terminates_globally` functional on recursive functions (#44210) As with loop, it's hard to prove termination of recursive call automatically. But with this commit we can manually specify termination at least. ```julia Base.@assume_effects :terminates_globally function recur_termination1(x) x == 1 && return 1 1 < x < 20 || throw("bad") return x * recur_termination1(x-1) end @test fully_eliminated() do recur_termination1(12) end Base.@assume_effects :terminates_globally function recur_termination2(x) x == 1 && return 1 1 < x < 20 || throw("bad") return _recur_termination2(x) end _recur_termination2(x) = x * recur_termination2(x-1) @test fully_eliminated() do recur_termination2(12) end ``` --- base/compiler/abstractinterpretation.jl | 35 ++++++++++++++++--------- test/compiler/inline.jl | 18 +++++++++++++ 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 4fdaa6257686c..04fa67fa5d304 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -97,7 +97,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), const_result = abstract_call_method_with_const_args(interp, result, f, this_arginfo, match, sv) effects = result.edge_effects if const_result !== nothing - (;rt, effects, const_result) = const_result + (; rt, effects, const_result) = const_result end tristate_merge!(sv, effects) push!(const_results, const_result) @@ -589,15 +589,27 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp if edge === nothing edgecycle = edgelimited = true end - if edgecycle + if is_effect_overrided(sv, :terminates_globally) + # this frame is known to terminate + edge_effects = Effects(edge_effects, terminates=ALWAYS_TRUE) + elseif is_effect_overrided(method, :terminates_globally) + # this edge is known to terminate + edge_effects = Effects(edge_effects, terminates=ALWAYS_TRUE) + elseif edgecycle # Some sort of recursion was detected. Even if we did not limit types, - # we cannot guarantee that the call will terminate. - edge_effects = tristate_merge(edge_effects, - Effects(EFFECTS_TOTAL, terminates=TRISTATE_UNKNOWN)) + # we cannot guarantee that the call will terminate + edge_effects = Effects(edge_effects, terminates=TRISTATE_UNKNOWN) end return MethodCallResult(rt, edgecycle, edgelimited, edge, edge_effects) end +is_effect_overrided(sv::InferenceState, effect::Symbol) = is_effect_overrided(sv.linfo, effect) +function is_effect_overrided(linfo::MethodInstance, effect::Symbol) + def = linfo.def + return isa(def, Method) && is_effect_overrided(def, effect) +end +is_effect_overrided(method::Method, effect::Symbol) = getfield(decode_effects_override(method.purity), effect) + # keeps result and context information of abstract method call, will be used by succeeding constant-propagation struct MethodCallResult rt @@ -2067,14 +2079,13 @@ end function handle_control_backedge!(frame::InferenceState, from::Int, to::Int) if from > to - def = frame.linfo.def - if isa(def, Method) - effects = decode_effects_override(def.purity) - if effects.terminates_globally || effects.terminates_locally - return nothing - end + if is_effect_overrided(frame, :terminates_globally) + # this frame is known to terminate + elseif is_effect_overrided(frame, :terminates_locally) + # this backedge is known to terminate + else + tristate_merge!(frame, Effects(EFFECTS_TOTAL, terminates=TRISTATE_UNKNOWN)) end - tristate_merge!(frame, Effects(EFFECTS_TOTAL, terminates=TRISTATE_UNKNOWN)) end return nothing end diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index aa5297066b67b..3259c752d9aa0 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -1070,6 +1070,24 @@ end issue41694(2) end +Base.@assume_effects :terminates_globally function recur_termination1(x) + x == 1 && return 1 + 1 < x < 20 || throw("bad") + return x * recur_termination1(x-1) +end +@test fully_eliminated() do + recur_termination1(12) +end +Base.@assume_effects :terminates_globally function recur_termination21(x) + x == 1 && return 1 + 1 < x < 20 || throw("bad") + return recur_termination22(x) +end +recur_termination22(x) = x * recur_termination21(x-1) +@test fully_eliminated() do + recur_termination21(12) + recur_termination22(12) +end + global x44200::Int = 0 function f44200() global x = 0 From e9a82f6476851914ebc38945951ca025cdce0baf Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Mon, 21 Feb 2022 15:15:52 +0900 Subject: [PATCH 12/26] inference: override return type inference by const-prop more carefully (#44282) A return type derived by const-prop' inference can be wider than that of non const-prop' inference in rare cases e.g. when there are cycles but cached result is still accurate. This commit checks if the const-prop'ed result is really more accurate than non-const result. fix https://github.com/Ferrite-FEM/Tensors.jl/issues/178 --- base/compiler/abstractinterpretation.jl | 40 +++++++++++++++++-------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 04fa67fa5d304..39cabe33ff043 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -94,10 +94,13 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), end this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i] this_arginfo = ArgInfo(fargs, this_argtypes) - const_result = abstract_call_method_with_const_args(interp, result, f, this_arginfo, match, sv) + const_call_result = abstract_call_method_with_const_args(interp, result, f, this_arginfo, match, sv) effects = result.edge_effects - if const_result !== nothing - (; rt, effects, const_result) = const_result + const_result = nothing + if const_call_result !== nothing + if const_call_result.rt ⊑ rt + (; rt, effects, const_result) = const_call_result + end end tristate_merge!(sv, effects) push!(const_results, const_result) @@ -133,11 +136,17 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), # this is in preparation for inlining, or improving the return result this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i] this_arginfo = ArgInfo(fargs, this_argtypes) - const_result = abstract_call_method_with_const_args(interp, result, f, this_arginfo, match, sv) + const_call_result = abstract_call_method_with_const_args(interp, result, f, this_arginfo, match, sv) effects = result.edge_effects - if const_result !== nothing - this_rt = const_result.rt - (; effects, const_result) = const_result + const_result = nothing + if const_call_result !== nothing + this_const_rt = const_call_result.rt + # return type of const-prop' inference can be wider than that of non const-prop' inference + # e.g. in cases when there are cycles but cached result is still accurate + if this_const_rt ⊑ this_rt + this_rt = this_const_rt + (; effects, const_result) = const_call_result + end end tristate_merge!(sv, effects) push!(const_results, const_result) @@ -1483,9 +1492,12 @@ function abstract_invoke(interp::AbstractInterpreter, (; fargs, argtypes)::ArgIn # t, a = ti.parameters[i], argtypes′[i] # argtypes′[i] = t ⊑ a ? t : a # end - const_result = abstract_call_method_with_const_args(interp, result, singleton_type(ft′), arginfo, match, sv) - if const_result !== nothing - (;rt, const_result) = const_result + const_call_result = abstract_call_method_with_const_args(interp, result, singleton_type(ft′), arginfo, match, sv) + const_result = nothing + if const_call_result !== nothing + if const_call_result.rt ⊑ rt + (; rt, const_result) = const_call_result + end end return CallMeta(from_interprocedural!(rt, sv, arginfo, sig), InvokeCallInfo(match, const_result)) end @@ -1630,10 +1642,12 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter, closure::Part match = MethodMatch(sig, Core.svec(), closure.source, sig <: rewrap_unionall(sigT, tt)) const_result = nothing if !result.edgecycle - const_result = abstract_call_method_with_const_args(interp, result, nothing, + const_call_result = abstract_call_method_with_const_args(interp, result, nothing, arginfo, match, sv) - if const_result !== nothing - (;rt, const_result) = const_result + if const_call_result !== nothing + if const_call_result.rt ⊑ rt + (; rt, const_result) = const_call_result + end end end info = OpaqueClosureCallInfo(match, const_result) From 64a9bb904599b6404753300d1b8e7436bba2a77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Sun, 20 Feb 2022 16:17:43 +0000 Subject: [PATCH 13/26] [CPUID] Add ISA entries for A64FX and M1 (#44194) * [CPUID] Rework how current ISA is determined * [CPUID] Add ISA entry for A64FX * [CPUID] Add ISA entry for Apple Silicon M1 * [CPUID] Simplify collection of full set of features for architecture * [CPUID] Remove AES from A64FX ISA, not all chips appear to have it (cherry picked from commit f45b6adb5e8cd7fc59401d28183bc92c6df849f5) --- base/Base.jl | 6 +++--- base/binaryplatforms.jl | 3 ++- base/cpuid.jl | 28 +++++++++++++++++++++------- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index f39b227d6f663..9ad0cdf7a6661 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -276,9 +276,6 @@ include("weakkeydict.jl") include("env.jl") -# BinaryPlatforms, used by Artifacts -include("binaryplatforms.jl") - # functions defined in Random function rand end function randn end @@ -336,6 +333,9 @@ using .Order include("sort.jl") using .Sort +# BinaryPlatforms, used by Artifacts. Needs `Sort`. +include("binaryplatforms.jl") + # Fast math include("fastmath.jl") using .FastMath diff --git a/base/binaryplatforms.jl b/base/binaryplatforms.jl index efc58dc6c6b7a..61e1af796999d 100644 --- a/base/binaryplatforms.jl +++ b/base/binaryplatforms.jl @@ -608,7 +608,8 @@ const arch_march_isa_mapping = let "armv8_0" => get_set("aarch64", "armv8.0-a"), "armv8_1" => get_set("aarch64", "armv8.1-a"), "armv8_2_crypto" => get_set("aarch64", "armv8.2-a+crypto"), - "armv8_4_crypto_sve" => get_set("aarch64", "armv8.4-a+crypto+sve"), + "a64fx" => get_set("aarch64", "a64fx"), + "apple_m1" => get_set("aarch64", "apple_m1"), ], "powerpc64le" => [ "power8" => get_set("powerpc64le", "power8"), diff --git a/base/cpuid.jl b/base/cpuid.jl index b1fb82cf86dae..48930d8064ba9 100644 --- a/base/cpuid.jl +++ b/base/cpuid.jl @@ -56,9 +56,10 @@ const ISAs_by_family = Dict( "aarch64" => [ # Implicit in all sets, because always required: fp, asimd "armv8.0-a" => ISA(Set{UInt32}()), - "armv8.1-a" => ISA(Set((JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm))), - "armv8.2-a+crypto" => ISA(Set((JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm, JL_AArch64_aes, JL_AArch64_sha2))), - "armv8.4-a+crypto+sve" => ISA(Set((JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm, JL_AArch64_fp16fml, JL_AArch64_aes, JL_AArch64_sha2, JL_AArch64_dotprod, JL_AArch64_sve))), + "armv8.1-a" => ISA(Set((JL_AArch64_v8_1a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm))), + "armv8.2-a+crypto" => ISA(Set((JL_AArch64_v8_2a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm, JL_AArch64_aes, JL_AArch64_sha2))), + "a64fx" => ISA(Set((JL_AArch64_v8_2a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm, JL_AArch64_sha2, JL_AArch64_ccpp, JL_AArch64_complxnum, JL_AArch64_fullfp16, JL_AArch64_sve))), + "apple_m1" => ISA(Set((JL_AArch64_v8_5a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm, JL_AArch64_aes, JL_AArch64_sha2, JL_AArch64_sha3, JL_AArch64_ccpp, JL_AArch64_complxnum, JL_AArch64_fp16fml, JL_AArch64_fullfp16, JL_AArch64_dotprod, JL_AArch64_rcpc, JL_AArch64_altnzcv))), ], "powerpc64le" => [ # We have no way to test powerpc64le features yet, so we're only going to declare the lowest ISA: @@ -88,14 +89,27 @@ function normalize_arch(arch::String) return arch end +let + # Collect all relevant features for the current architecture, if any. + FEATURES = UInt32[] + arch = normalize_arch(String(Sys.ARCH)) + if arch in keys(ISAs_by_family) + for isa in ISAs_by_family[arch] + unique!(append!(FEATURES, last(isa).features)) + end + end + + # Use `@eval` to inline the list of features. + @eval function cpu_isa() + return ISA(Set{UInt32}(feat for feat in $(FEATURES) if test_cpu_feature(feat))) + end +end + """ cpu_isa() Return the [`ISA`](@ref) (instruction set architecture) of the current CPU. """ -function cpu_isa() - all_features = last(last(get(ISAs_by_family, normalize_arch(String(Sys.ARCH)), "" => [ISA(Set{UInt32}())]))).features - return ISA(Set{UInt32}(feat for feat in all_features if test_cpu_feature(feat))) -end +cpu_isa end # module CPUID From 2e2f214faa35e50501bfb53b8732897a1567e774 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sat, 19 Feb 2022 07:31:44 -0800 Subject: [PATCH 14/26] Fix missing `jl_effective_threads` declaration (#44251) (cherry picked from commit f852c88fe4e1d9ace9f90c0ddd7d0b33cb5f8565) --- src/jloptions.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jloptions.c b/src/jloptions.c index 1ff4da7c5c10b..b85e8f7bfe386 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -4,6 +4,7 @@ #include #include "julia.h" +#include "julia_internal.h" #include #include From f1cb21965546146e24ee2b7c07c637897db5d8ce Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sat, 19 Feb 2022 12:45:05 -0500 Subject: [PATCH 15/26] fix #44239, regression in keyword args in getindex (#44246) (cherry picked from commit 4061e8f898c91d58a117b48a8e9f47260211b962) --- src/julia-syntax.scm | 16 ++++++++++++---- test/syntax.jl | 11 +++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index caeca92f75803..52bcb307ebe19 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -120,6 +120,10 @@ ;; inside ref only replace within the first argument (list* 'ref (replace-beginend (cadr ex) a n tuples last) (cddr ex))) + ;; TODO: this probably should not be allowed since keyword args aren't + ;; positional, but in this context we have just used their positions anyway + ((eq? (car ex) 'kw) + (list 'kw (cadr ex) (replace-beginend (caddr ex) a n tuples last))) (else (cons (car ex) (map (lambda (x) (replace-beginend x a n tuples last)) @@ -142,16 +146,20 @@ (idx (if (vararg? idx0) (cadr idx0) idx0)) (last (null? (cdr lst))) (replaced (replace-beginend idx a n tuples last)) - (idx (if (or (not has-va?) (simple-atom? replaced)) replaced (make-ssavalue)))) + (val (if (kwarg? replaced) (caddr replaced) replaced)) + (idx (if (or (not has-va?) (simple-atom? val)) + val (make-ssavalue)))) (loop (cdr lst) (+ n 1) - (if (eq? idx replaced) + (if (eq? idx val) stmts - (cons `(= ,idx ,replaced) + (cons `(= ,idx ,val) stmts)) (if (vararg? idx0) (cons idx tuples) tuples) (cons (if (vararg? idx0) `(... ,idx) - idx) + (if (eq? val replaced) + idx + (list 'kw (cadr replaced) idx))) ret))))))) ;; GF method does not need to keep decl expressions on lambda args diff --git a/test/syntax.jl b/test/syntax.jl index 69d3e8c7fe591..ff65cb235d92c 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -1212,6 +1212,17 @@ let a = [], b = [4,3,2,1] @test a == [1,2] end +# issue #44239 +struct KWGetindex end +Base.getindex(::KWGetindex, args...; kws...) = (args, NamedTuple(kws)) +let A = KWGetindex(), a = [], b = [4,3,2,1] + f() = (push!(a, 1); 2) + g() = (push!(a, 2); ()) + @test A[f(), g()..., k = f()] === ((2,), (k = 2,)) + @test a == [1, 2, 1] + @test A[var"end"=1] === ((), (var"end" = 1,)) +end + @testset "raw_str macro" begin @test raw"$" == "\$" @test raw"\n" == "\\n" From 24fc8e27b6c35e1ff42177a02fe590974815aacd Mon Sep 17 00:00:00 2001 From: Seth Axen Date: Sat, 19 Feb 2022 18:46:55 +0100 Subject: [PATCH 16/26] Fix dot(::Adjoint, ::Adjoint) for numbers that don't commute under multiplication (#44219) Co-authored-by: Fredrik Ekre (cherry picked from commit 928f63ca85d4d8065e6709671f6f8735aa3ec756) --- stdlib/LinearAlgebra/src/generic.jl | 4 +++- stdlib/LinearAlgebra/test/generic.jl | 13 +++++++------ test/testhelpers/Quaternions.jl | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index 5565b28ebabe7..676e965652e8f 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -886,7 +886,9 @@ function dot(x::AbstractArray, y::AbstractArray) s end -dot(x::Adjoint, y::Adjoint) = conj(dot(parent(x), parent(y))) +function dot(x::Adjoint{<:Union{Real,Complex}}, y::Adjoint{<:Union{Real,Complex}}) + return conj(dot(parent(x), parent(y))) +end dot(x::Transpose, y::Transpose) = dot(parent(x), parent(y)) """ diff --git a/stdlib/LinearAlgebra/test/generic.jl b/stdlib/LinearAlgebra/test/generic.jl index 26534a2cdf0cd..b56edf9439fe0 100644 --- a/stdlib/LinearAlgebra/test/generic.jl +++ b/stdlib/LinearAlgebra/test/generic.jl @@ -497,20 +497,21 @@ end end @testset "adjtrans dot" begin - for t in (transpose, adjoint) - x, y = t(rand(ComplexF64, 10)), t(rand(ComplexF64, 10)) + for t in (transpose, adjoint), T in (ComplexF64, Quaternion{Float64}) + x, y = t(rand(T, 10)), t(rand(T, 10)) X, Y = copy(x), copy(y) @test dot(x, y) ≈ dot(X, Y) - x, y = t([rand(ComplexF64, 2, 2) for _ in 1:5]), t([rand(ComplexF64, 2, 2) for _ in 1:5]) + x, y = t([rand(T, 2, 2) for _ in 1:5]), t([rand(T, 2, 2) for _ in 1:5]) X, Y = copy(x), copy(y) @test dot(x, y) ≈ dot(X, Y) - x, y = t(rand(ComplexF64, 10, 5)), t(rand(ComplexF64, 10, 5)) + x, y = t(rand(T, 10, 5)), t(rand(T, 10, 5)) X, Y = copy(x), copy(y) @test dot(x, y) ≈ dot(X, Y) - x = t([rand(ComplexF64, 2, 2) for _ in 1:5, _ in 1:5]) - y = t([rand(ComplexF64, 2, 2) for _ in 1:5, _ in 1:5]) + x = t([rand(T, 2, 2) for _ in 1:5, _ in 1:5]) + y = t([rand(T, 2, 2) for _ in 1:5, _ in 1:5]) X, Y = copy(x), copy(y) @test dot(x, y) ≈ dot(X, Y) + x, y = t([rand(T, 2, 2) for _ in 1:5]), t([rand(T, 2, 2) for _ in 1:5]) end end diff --git a/test/testhelpers/Quaternions.jl b/test/testhelpers/Quaternions.jl index a3967c1aacc43..1eddad322ec40 100644 --- a/test/testhelpers/Quaternions.jl +++ b/test/testhelpers/Quaternions.jl @@ -1,6 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license module Quaternions +using Random export Quaternion @@ -36,4 +37,17 @@ Base.:(*)(q::Quaternion, b::Bool) = b * q # remove method ambiguity Base.:(/)(q::Quaternion, w::Quaternion) = q * conj(w) * (1.0 / abs2(w)) Base.:(\)(q::Quaternion, w::Quaternion) = conj(q) * w * (1.0 / abs2(q)) +# adapted from https://github.com/JuliaGeometry/Quaternions.jl/pull/42 +function Base.rand(rng::AbstractRNG, ::Random.SamplerType{Quaternion{T}}) where {T<:Real} + return Quaternion{T}(rand(rng, T), rand(rng, T), rand(rng, T), rand(rng, T)) +end +function Base.randn(rng::AbstractRNG, ::Type{Quaternion{T}}) where {T<:AbstractFloat} + return Quaternion{T}( + randn(rng, T) / 2, + randn(rng, T) / 2, + randn(rng, T) / 2, + randn(rng, T) / 2, + ) +end + end From bb6221c51d3652cff2aeadd56d39a967b5e9534c Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Sat, 19 Feb 2022 09:47:41 -0800 Subject: [PATCH 17/26] Ensure that `open(::Function, ::Cmd)` waits for termination (#44078) On Windows, we observed occasional issues where an error within the function callback to the `open(::Function, ::Cmd)` method would cause problems due to assuming that the opened process had finished by the time the `open()` call was finished. In most cases this was true, however on Windows, it was found that we need to explicitly `wait()` upon the process object to ensure that all file handles held by the subprocess were properly closed by the time `open()` is finished. Co-authored-by: Dilum Aluthge (cherry picked from commit 623ceb7834de47538eddeadfa84a8bf2d9741248) --- base/process.jl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/base/process.jl b/base/process.jl index 16e66b0bd9884..6aac514191681 100644 --- a/base/process.jl +++ b/base/process.jl @@ -402,16 +402,25 @@ 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) + close(P) + # 0.1 seconds after we hope it dies (from closing stdio), + # we kill the process with SIGTERM (15) + local t = Timer(0.1) do t + process_running(P) && kill(P) + end + wait(P) + close(t) + end ret = try f(P) catch - kill(P) - close(P) + waitkill(P) rethrow() end close(P.in) if !eof(P.out) - close(P.out) + waitkill(P) throw(_UVError("open(do)", UV_EPIPE)) end success(P) || pipeline_error(P) From 69407edfdb487f2e6f3e29c5b907d4dd6296d6be Mon Sep 17 00:00:00 2001 From: Julian Samaroo Date: Sat, 19 Feb 2022 22:52:38 -0600 Subject: [PATCH 18/26] Profile.Allocs: jl_raw_alloc_t.task is untyped pointer (#44269) (cherry picked from commit 2714b92b5904c3ae74615ba33f5c738a79bb8969) --- src/gc-alloc-profiler.cpp | 4 ++-- stdlib/Profile/src/Allocs.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gc-alloc-profiler.cpp b/src/gc-alloc-profiler.cpp index 88177e0ea20ef..e7ab1a6fdf1f5 100644 --- a/src/gc-alloc-profiler.cpp +++ b/src/gc-alloc-profiler.cpp @@ -20,7 +20,7 @@ struct jl_raw_alloc_t { jl_datatype_t *type_address; jl_raw_backtrace_t backtrace; size_t size; - jl_task_t *task; + void *task; uint64_t timestamp; }; @@ -135,7 +135,7 @@ void _maybe_record_alloc_to_profile(jl_value_t *val, size_t size, jl_datatype_t type, get_raw_backtrace(), size, - jl_current_task, + (void *)jl_current_task, cycleclock() }); } diff --git a/stdlib/Profile/src/Allocs.jl b/stdlib/Profile/src/Allocs.jl index aa689936d4598..26dd90a821e01 100644 --- a/stdlib/Profile/src/Allocs.jl +++ b/stdlib/Profile/src/Allocs.jl @@ -123,7 +123,7 @@ struct Alloc type::Any stacktrace::StackTrace size::Int - task::Ptr{Cvoid} + task::Ptr{Cvoid} # N.B. unrooted, may not be valid timestamp::UInt64 end From 15977a0d2fb14aa79ec043a1cf63818e0d52ec0b Mon Sep 17 00:00:00 2001 From: Dilum Aluthge Date: Sun, 20 Feb 2022 04:20:18 -0500 Subject: [PATCH 19/26] In the `sysimage.mk` Makefile, set the `JULIA_NUM_THREADS=1` environment variable when running the `generate_precompile.jl` script (#44281) (cherry picked from commit e454858a69b73d2582402761d8ebde050be217d8) --- contrib/generate_precompile.jl | 4 ++++ sysimage.mk | 1 + 2 files changed, 5 insertions(+) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index e6cf280812685..a10d195229cab 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -1,5 +1,9 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +if Threads.nthreads() != 1 + @warn "Running this file with multiple Julia threads may lead to a build error" Threads.nthreads() +end + if Base.isempty(Base.ARGS) || Base.ARGS[1] !== "0" Sys.__init_build() # Prevent this from being put into the Main namespace diff --git a/sysimage.mk b/sysimage.mk index de5c3e22f253a..1586eb7dbc16d 100644 --- a/sysimage.mk +++ b/sysimage.mk @@ -77,6 +77,7 @@ 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 @$$(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 \ $$(call spawn, $3) $2 -C "$$(JULIA_CPU_TARGET)" --output-$$* $$(call cygpath_w,$$@).tmp $$(JULIA_SYSIMG_BUILD_FLAGS) \ --startup-file=no --warn-overwrite=yes --sysimage $$(call cygpath_w,$$<) $$(call cygpath_w,$$(JULIAHOME)/contrib/generate_precompile.jl) $(JULIA_PRECOMPILE); then \ echo '*** This error is usually fixed by running `make clean`. If the error persists$$(COMMA) try `make cleanall`. ***'; \ From 8d2f60438da4317f4cbf3c39c47cbcc27ee4251c Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sun, 20 Feb 2022 09:40:11 -0500 Subject: [PATCH 20/26] Profile: Use grace period to avoid trailing signals from timer (#44268) (cherry picked from commit e723d374b78be36223d42a739b7cc9eb7fc147ff) --- src/signals-unix.c | 20 +++++++++++++------- stdlib/Profile/test/runtests.jl | 9 ++++++--- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/signals-unix.c b/src/signals-unix.c index 8a5822d451335..b4eb57a5501cb 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -501,6 +501,16 @@ void usr2_handler(int sig, siginfo_t *info, void *ctx) errno = errno_save; } +// Because SIGUSR1 is dual-purpose, and the timer can have trailing signals after being deleted, +// a 2-second grace period is imposed to ignore any trailing timer-created signals so they don't get +// confused for user triggers +uint64_t last_timer_delete_time = 0; + +int timer_graceperiod_elapsed(void) +{ + return jl_hrtime() > (last_timer_delete_time + 2e9); +} + #if defined(HAVE_TIMER) // Linux-style #include @@ -541,9 +551,7 @@ JL_DLLEXPORT void jl_profile_stop_timer(void) { if (running) { timer_delete(timerprof); - // Because SIGUSR1 is multipurpose, care must be taken for running = 0 to be set after the timer has fully stopped. - // There may be a pending signal emitted from the timer so wait a few timer cycles - sleep_ms((nsecprof / GIGA) * 1000 * 3); + last_timer_delete_time = jl_hrtime(); running = 0; } } @@ -574,9 +582,7 @@ JL_DLLEXPORT void jl_profile_stop_timer(void) if (running) { memset(&timerprof, 0, sizeof(timerprof)); setitimer(ITIMER_PROF, &timerprof, NULL); - // Because SIGUSR1 is multipurpose, care must be taken for running = 0 to be set after the timer has fully stopped. - // There may be a pending signal emitted from the timer so wait a few timer cycles - sleep_ms((nsecprof / GIGA) * 1000 * 3); + last_timer_delete_time = jl_hrtime(); running = 0; } } @@ -786,7 +792,7 @@ static void *signal_listener(void *arg) } #else if (sig == SIGUSR1) { - if (running != 1) + if (running != 1 && timer_graceperiod_elapsed()) trigger_profile_peek(); doexit = 0; } diff --git a/stdlib/Profile/test/runtests.jl b/stdlib/Profile/test/runtests.jl index ab87d497fe93e..c6f78cbed0522 100644 --- a/stdlib/Profile/test/runtests.jl +++ b/stdlib/Profile/test/runtests.jl @@ -200,9 +200,12 @@ if Sys.isbsd() || Sys.islinux() """ iob = Base.BufferStream() p = run(pipeline(`$cmd -e $script`, stderr = devnull, stdout = iob), wait = false) - t = Timer(60) do t # should be done in under 10 seconds + t = Timer(120) do t + # should be under 10 seconds, so give it 2 minutes then report failure + println("KILLING BY PROFILE TEST WATCHDOG\n") + kill(p, Base.SIGTERM) + sleep(10) kill(p, Base.SIGKILL) - sleep(5) close(iob) end try @@ -210,7 +213,7 @@ if Sys.isbsd() || Sys.islinux() @assert occursin("started", s) @assert process_running(p) for _ in 1:2 - sleep(2) + sleep(2.5) if Sys.isbsd() kill(p, 29) # SIGINFO elseif Sys.islinux() From 554d2ccf1c244dde41a86ca191d626a55ac0323b Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 19 Feb 2022 20:31:41 +0000 Subject: [PATCH 21/26] debuginfo: Fix build on 32-bit ARM This slipped through in 955d4271, as we aren't building for 32 bit ARM during CI right now. GitHub: Fixes #44254. (cherry picked from commit c9ac2eadfb2c80ddd2cff30c5eaec0f4e8695167) --- src/debuginfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index 42d67bd6f89c7..53105feb81bfe 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -227,7 +227,7 @@ class JITObjectRegistry continue; } } - uint64_t loadaddr = L.getSectionLoadAddress(section); + uint64_t loadaddr = getLoadAddress(section.getName().get()); size_t seclen = section.getSize(); if (istext) { arm_text_addr = loadaddr; From d9849beda7d63af6407c6d300d60db846f3553b1 Mon Sep 17 00:00:00 2001 From: Aditya Puranik <7466364+Ellipse0934@users.noreply.github.com> Date: Mon, 21 Feb 2022 15:17:52 +0530 Subject: [PATCH 22/26] Fix aliasing bug in copy!(x, x) for x::AbstractSet and x::AbstractDict, fixes #41268 (#44265) (cherry picked from commit 0b48b91c9881823d860eaf0a605072b7f20a4cbb) --- base/abstractdict.jl | 5 ++++- base/abstractset.jl | 5 ++++- test/dict.jl | 2 ++ test/sets.jl | 3 +++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/base/abstractdict.jl b/base/abstractdict.jl index a9c04dac22b7b..daab71c3cc671 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -189,7 +189,10 @@ empty(a::AbstractDict) = empty(a, keytype(a), valtype(a)) empty(a::AbstractDict, ::Type{V}) where {V} = empty(a, keytype(a), V) # Note: this is the form which makes sense for `Vector`. copy(a::AbstractDict) = merge!(empty(a), a) -copy!(dst::AbstractDict, src::AbstractDict) = merge!(empty!(dst), src) +function copy!(dst::AbstractDict, src::AbstractDict) + dst === src && return dst + merge!(empty!(dst), src) +end """ merge!(d::AbstractDict, others::AbstractDict...) diff --git a/base/abstractset.jl b/base/abstractset.jl index bec4a84b19d15..561e18c15697c 100644 --- a/base/abstractset.jl +++ b/base/abstractset.jl @@ -3,7 +3,10 @@ eltype(::Type{<:AbstractSet{T}}) where {T} = @isdefined(T) ? T : Any sizehint!(s::AbstractSet, n) = nothing -copy!(dst::AbstractSet, src::AbstractSet) = union!(empty!(dst), src) +function copy!(dst::AbstractSet, src::AbstractSet) + dst === src && return dst + union!(empty!(dst), src) +end ## set operations (union, intersection, symmetric difference) diff --git a/test/dict.jl b/test/dict.jl index cbbb475c993fd..ad15764a09d16 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -1179,6 +1179,8 @@ end @test s === copy!(s, Base.ImmutableDict(a[])) == Dict(a[]) end end + s2 = copy(s) + @test copy!(s, s) == s2 end @testset "map!(f, values(dict))" begin diff --git a/test/sets.jl b/test/sets.jl index 43ba433e780d7..1999dbf3d020f 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -151,6 +151,9 @@ end @test s === copy!(s, BitSet(a)) == S(a) end end + s = Set([1, 2]) + s2 = copy(s) + @test copy!(s, s) == s2 end @testset "sizehint, empty" begin From 11939610c3578c507b3beeaac7b9bd3fb009b9a1 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 21 Feb 2022 10:37:54 -0500 Subject: [PATCH 23/26] Fix bad precompile statements via an output lock (#44252) (cherry picked from commit c5bc69ceba10a90df68dd13a47eb888e1a309faf) --- src/gf.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/gf.c b/src/gf.c index 01d03fe77394f..25b29c63a0782 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1961,6 +1961,8 @@ jl_code_instance_t *jl_method_compiled(jl_method_instance_t *mi, size_t world) return NULL; } +jl_mutex_t precomp_statement_out_lock; + static void record_precompile_statement(jl_method_instance_t *mi) { static ios_t f_precompile; @@ -1971,6 +1973,8 @@ static void record_precompile_statement(jl_method_instance_t *mi) if (!jl_is_method(def)) return; + if (jl_n_threads > 1) + JL_LOCK(&precomp_statement_out_lock); if (s_precompile == NULL) { const char *t = jl_options.trace_compile; if (!strncmp(t, "stderr", 6)) { @@ -1989,6 +1993,8 @@ static void record_precompile_statement(jl_method_instance_t *mi) if (s_precompile != JL_STDERR) ios_flush(&f_precompile); } + if (jl_n_threads > 1) + JL_UNLOCK(&precomp_statement_out_lock); } jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t world) From a6f4b5ecefc76f170d2ce7036d5c786e6b12af9e Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Tue, 22 Feb 2022 15:33:25 -0500 Subject: [PATCH 24/26] Fix thread-safety violation in Allocations Profiler: Create a new per-thread backtrace buffer in ptls (#44116) * Fix thread-safety violation in Allocations Profiler: Re-use the shared `ptls->bt_data` buffer from the thread-local storage for the buffer, to ensure that each thread has a separate buffer. This buffer is shared with the exception throwing mechanism, but is safe to share since julia exception throwing never interleaves with allocations profiling. * Approach two: Create a separate per-thread allocations backtrace buffer. * Update src/gc-alloc-profiler.cpp Co-authored-by: Jameson Nash * Update src/gc-alloc-profiler.cpp Co-authored-by: Jameson Nash * fix type error (#44235) * Update src/gc-alloc-profiler.cpp Co-authored-by: Jameson Nash Co-authored-by: Pete Vilter <7341+vilterp@users.noreply.github.com> (cherry picked from commit 4e57966504edb485838b3458929ad6498f197709) --- src/gc-alloc-profiler.cpp | 19 ++++++++++++++----- src/julia_threads.h | 2 ++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/gc-alloc-profiler.cpp b/src/gc-alloc-profiler.cpp index e7ab1a6fdf1f5..818d6e803c9df 100644 --- a/src/gc-alloc-profiler.cpp +++ b/src/gc-alloc-profiler.cpp @@ -49,15 +49,24 @@ jl_combined_results g_combined_results; // Will live forever. // === stack stuff === jl_raw_backtrace_t get_raw_backtrace() JL_NOTSAFEPOINT { - // A single large buffer to record backtraces onto - static jl_bt_element_t static_bt_data[JL_MAX_BT_SIZE]; + // We first record the backtrace onto a MAX-sized buffer, so that we don't have to + // allocate the buffer until we know the size. To ensure thread-safety, we use a + // per-thread backtrace buffer. + jl_ptls_t ptls = jl_current_task->ptls; + jl_bt_element_t *shared_bt_data_buffer = ptls->profiling_bt_buffer; + if (shared_bt_data_buffer == NULL) { + size_t size = sizeof(jl_bt_element_t) * (JL_MAX_BT_SIZE + 1); + shared_bt_data_buffer = (jl_bt_element_t*) malloc_s(size); + ptls->profiling_bt_buffer = shared_bt_data_buffer; + } - size_t bt_size = rec_backtrace(static_bt_data, JL_MAX_BT_SIZE, 2); + size_t bt_size = rec_backtrace(shared_bt_data_buffer, JL_MAX_BT_SIZE, 2); // Then we copy only the needed bytes out of the buffer into our profile. size_t bt_bytes = bt_size * sizeof(jl_bt_element_t); - jl_bt_element_t *bt_data = (jl_bt_element_t*) malloc(bt_bytes); - memcpy(bt_data, static_bt_data, bt_bytes); + jl_bt_element_t *bt_data = (jl_bt_element_t*) malloc_s(bt_bytes); + memcpy(bt_data, shared_bt_data_buffer, bt_bytes); + return jl_raw_backtrace_t{ bt_data, diff --git a/src/julia_threads.h b/src/julia_threads.h index 371eb51250115..22acf3aec8587 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -246,6 +246,8 @@ typedef struct _jl_tls_states_t { // Temporary backtrace buffer. Scanned for gc roots when bt_size > 0. struct _jl_bt_element_t *bt_data; // JL_MAX_BT_SIZE + 1 elements long size_t bt_size; // Size for backtrace in transit in bt_data + // Temporary backtrace buffer used only for allocations profiler. + struct _jl_bt_element_t *profiling_bt_buffer; // Atomically set by the sender, reset by the handler. volatile _Atomic(sig_atomic_t) signal_request; // TODO: no actual reason for this to be _Atomic // Allow the sigint to be raised asynchronously From e23618dc347071b4a62ce4d4b4e1b4a8f2d53281 Mon Sep 17 00:00:00 2001 From: Felix Kastner Date: Wed, 23 Feb 2022 13:32:17 +0100 Subject: [PATCH 25/26] fix `BLAS.spr!` docstring (#44303) (cherry picked from commit 333a3a37f815b0053dcdbc7439a7fb67af16388d) --- stdlib/LinearAlgebra/src/blas.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/src/blas.jl b/stdlib/LinearAlgebra/src/blas.jl index e8d44c1ae1533..9b5506e993ea8 100644 --- a/stdlib/LinearAlgebra/src/blas.jl +++ b/stdlib/LinearAlgebra/src/blas.jl @@ -1216,7 +1216,7 @@ end """ spr!(uplo, α, x, AP) -Update matrix `A` as `α*A*x*x'`, where `A` is a symmetric matrix provided +Update matrix `A` as `A+α*x*x'`, where `A` is a symmetric matrix provided in packed format `AP` and `x` is a vector. With `uplo = 'U'`, the array AP must contain the upper triangular part of the From a274328df6e695f08dd238350cb10980a85c10c4 Mon Sep 17 00:00:00 2001 From: gbaraldi Date: Wed, 23 Feb 2022 09:38:45 -0300 Subject: [PATCH 26/26] Fix M1 tail call issue when building (#44279) (cherry picked from commit 629eb8662a193e5247bdbdba53916041572c6a24) --- src/ccall.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ccall.cpp b/src/ccall.cpp index 332c057afa5c4..1fc6e5d4d3ece 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -233,10 +233,11 @@ 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_)) + +#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