From cfb43c4ee9db8dc90a9c30c574c6ae0518106b35 Mon Sep 17 00:00:00 2001 From: CY Han Date: Fri, 1 Nov 2024 22:20:10 +0800 Subject: [PATCH 01/68] docs: remove `dirname.c` from THIRDPARTY file (#56413) - `dirname.c` was removed by https://github.com/JuliaLang/julia/commit/c2cec7ad57102e4fbb733b8fb79d617a9524f0ae (cherry picked from commit 85dc2c72110e44f68f483c2b7b2e4dc68c73a143) --- THIRDPARTY.md | 1 - 1 file changed, 1 deletion(-) diff --git a/THIRDPARTY.md b/THIRDPARTY.md index 51950d9e2c6a1..07b9f69f26e3e 100644 --- a/THIRDPARTY.md +++ b/THIRDPARTY.md @@ -6,7 +6,6 @@ for exceptions. - [crc32c.c](https://stackoverflow.com/questions/17645167/implementing-sse-4-2s-crc32c-in-software) (CRC-32c checksum code by Mark Adler) [[ZLib](https://opensource.org/licenses/Zlib)]. - [LDC](https://github.com/ldc-developers/ldc/blob/master/LICENSE) (for ccall/cfunction ABI definitions) [BSD-3]. The portion of code that Julia uses from LDC is [BSD-3] licensed. - [LLVM](https://releases.llvm.org/3.9.0/LICENSE.TXT) (for parts of src/disasm.cpp) [UIUC] -- [MINGW](https://sourceforge.net/p/mingw/mingw-org-wsl/ci/legacy/tree/mingwrt/mingwex/dirname.c) (for dirname implementation on Windows) [MIT] - [NetBSD](https://www.netbsd.org/about/redistribution.html) (for setjmp, longjmp, and strptime implementations on Windows) [BSD-3] - [Python](https://docs.python.org/3/license.html) (for strtod implementation on Windows) [PSF] - [FEMTOLISP](https://github.com/JeffBezanson/femtolisp) [BSD-3] From 3e597e43f53fd15ba98147b3b6e55ef367b245d5 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Tue, 18 Feb 2025 13:28:45 +0100 Subject: [PATCH 02/68] Only strip invariant.load from special pointers (#57386) Other backends (in this case NVPTX) require that `invariant.load` metadata is maintained to generate non-coherent loads. Currently, we unconditionally strip that metadata from all loads, since our other uses of it may have become invalid. x-ref: https://github.com/llvm/llvm-project/pull/112834 https://github.com/JuliaGPU/CUDA.jl/issues/2531 --------- Co-authored-by: Gabriel Baraldi (cherry picked from commit 29da86bb983066dd076439c2c7bc5e28dbd611bb) --- src/llvm-late-gc-lowering.cpp | 6 ++++-- test/llvmpasses/late-lower-gc.ll | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index 63a71a9e180ac..77a55915bf363 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -2305,8 +2305,10 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S, bool *CFGModified) { // strip all constant alias information, as it might depend on the gc having // preserved a gc root, which stops being true after this pass (#32215) // similar to RewriteStatepointsForGC::stripNonValidData, but less aggressive - if (I->getMetadata(LLVMContext::MD_invariant_load)) - I->setMetadata(LLVMContext::MD_invariant_load, NULL); + if (auto *LI = dyn_cast(I)){ + if (isSpecialPtr(LI->getPointerOperand()->getType()) && LI->getMetadata(LLVMContext::MD_invariant_load)) + LI->setMetadata(LLVMContext::MD_invariant_load, NULL); + } if (MDNode *TBAA = I->getMetadata(LLVMContext::MD_tbaa)) { if (TBAA->getNumOperands() == 4 && isTBAA(TBAA, {"jtbaa_const"})) { MDNode *MutableTBAA = createMutableTBAAAccessTag(TBAA); diff --git a/test/llvmpasses/late-lower-gc.ll b/test/llvmpasses/late-lower-gc.ll index 36e581993c176..ecd09d968afff 100644 --- a/test/llvmpasses/late-lower-gc.ll +++ b/test/llvmpasses/late-lower-gc.ll @@ -127,6 +127,20 @@ top: ret void } +; Confirm that `invariant.load` on other loads survive +define void @gc_keep_invariant(float addrspace(1)* %0) { +top: +; CHECK-LABEL: @gc_keep_invariant + %pgcstack = call {}*** @julia.get_pgcstack() + %1 = bitcast {}*** %pgcstack to {}** + %current_task = getelementptr inbounds {}*, {}** %1, i64 -12 + +; CHECK: %current_task = getelementptr inbounds ptr, ptr %1, i64 -12 + %2 = load float, ptr addrspace(1) %0, align 4, !invariant.load !1 +; CHECK-NEXT: %2 = load float, ptr addrspace(1) %0, align 4, !invariant.load + ret void +} + define i32 @callee_root({} addrspace(10)* %v0, {} addrspace(10)* %v1) { top: ; CHECK-LABEL: @callee_root From 5dc4316534fd6519136b2a846ea40c164147acb2 Mon Sep 17 00:00:00 2001 From: Denis Barucic Date: Thu, 27 Feb 2025 20:49:06 +0100 Subject: [PATCH 03/68] Docs: `circshift!(::AbstractVector, ::Integer)` (#57539) Closes #46016 (cherry picked from commit b0323ab00fe756c5a30aeed80186e6e547417421) --- base/abstractarray.jl | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index d993c7247dbc0..52d3335a37426 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3536,7 +3536,31 @@ function _keepat!(a::AbstractVector, m::AbstractVector{Bool}) deleteat!(a, j:lastindex(a)) end -## 1-d circshift ## +""" + circshift!(a::AbstractVector, shift::Integer) + +Circularly shift, or rotate, the data in vector `a` by `shift` positions. + +# Examples + +```jldoctest +julia> circshift!([1, 2, 3, 4, 5], 2) +5-element Vector{Int64}: + 4 + 5 + 1 + 2 + 3 + +julia> circshift!([1, 2, 3, 4, 5], -2) +5-element Vector{Int64}: + 3 + 4 + 5 + 1 + 2 +``` +""" function circshift!(a::AbstractVector, shift::Integer) n = length(a) n == 0 && return From 230c7b67938a85f182d1b89bde8e4eb0fca4bbbc Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Mon, 3 Mar 2025 09:41:36 +0100 Subject: [PATCH 04/68] strings: type assert in the generic `nextind`, `prevind` methods (#57608) The type assertions are valid according to the doc strings of these functions in the case of `AbstractString`. Should prevent some invalidation on loading user code. Fixes #57605 (cherry picked from commit 6c9c336d2acc71ed740b0cc470c0b76bbbda136c) --- base/strings/basic.jl | 12 ++++++------ test/strings/basic.jl | 5 +++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index 2609edeaaaa18..72088e0e06153 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -498,11 +498,11 @@ prevind(s::AbstractString, i::Int) = prevind(s, i, 1) function prevind(s::AbstractString, i::Int, n::Int) n < 0 && throw(ArgumentError("n cannot be negative: $n")) - z = ncodeunits(s) + 1 + z = ncodeunits(s)::Int + 1 @boundscheck 0 < i ≤ z || throw(BoundsError(s, i)) - n == 0 && return thisind(s, i) == i ? i : string_index_err(s, i) + n == 0 && return thisind(s, i)::Int == i ? i : string_index_err(s, i) while n > 0 && 1 < i - @inbounds n -= isvalid(s, i -= 1) + @inbounds n -= isvalid(s, i -= 1)::Bool end return i - n end @@ -557,11 +557,11 @@ nextind(s::AbstractString, i::Int) = nextind(s, i, 1) function nextind(s::AbstractString, i::Int, n::Int) n < 0 && throw(ArgumentError("n cannot be negative: $n")) - z = ncodeunits(s) + z = ncodeunits(s)::Int @boundscheck 0 ≤ i ≤ z || throw(BoundsError(s, i)) - n == 0 && return thisind(s, i) == i ? i : string_index_err(s, i) + n == 0 && return thisind(s, i)::Int == i ? i : string_index_err(s, i) while n > 0 && i < z - @inbounds n -= isvalid(s, i += 1) + @inbounds n -= isvalid(s, i += 1)::Bool end return i + n end diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 13f2f5197187a..6298744855b9e 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -872,6 +872,11 @@ end end end end + + @testset "return type infers to `Int`" begin + @test all(==(Int64), Base.return_types(prevind, Tuple{AbstractString, Vararg})) + @test all(==(Int64), Base.return_types(nextind, Tuple{AbstractString, Vararg})) + end end @testset "first and last" begin From 79f78d4b546150d1504a003fd5174928150166b8 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 6 Mar 2025 21:25:41 +0800 Subject: [PATCH 05/68] Intersect: try normal+reverse+existential subtyping during intersection (#57476) Typevars are all existential (in the sense of variable lb/ub) during intersection. This fix is somehow costly as we have to perform 3 times check to prove a false result. But a single existential <: seems too dangerous as it cause many circular env in the past. fix #57429 fix #41561 (cherry picked from commit beb928bfec532c8f038e113df2960c7b9609edb6) --- src/subtype.c | 127 ++++++++++++++++++++++++++++++++++++------------ test/subtype.jl | 33 ++++++++----- 2 files changed, 118 insertions(+), 42 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 5b326237e0dd2..db64d3b31d382 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2616,31 +2616,22 @@ 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) { - jl_varbinding_t *v = e->vars; - int len = 0; if (x == jl_bottom_type || y == (jl_value_t*)jl_any_type) return 1; - while (v != NULL) { - len++; - v = v->prev; - } - int8_t *rs = (int8_t*)malloc_s(len); + int8_t *rs = (int8_t*)alloca(current_env_length(e)); + jl_varbinding_t *v = e->vars; int n = 0; - v = e->vars; - while (n < len) { - assert(v != NULL); + while (v != NULL) { rs[n++] = v->right; v->right = 1; v = v->prev; } int issub = subtype_in_env(x, y, e); n = 0; v = e->vars; - while (n < len) { - assert(v != NULL); + while (v != NULL) { v->right = rs[n++]; v = v->prev; } - free(rs); return issub; } @@ -2688,6 +2679,8 @@ static int check_unsat_bound(jl_value_t *t, jl_tvar_t *v, jl_stenv_t *e) JL_NOTS } +static int intersect_var_ccheck_in_env(jl_value_t *xlb, jl_value_t *xub, jl_value_t *ylb, jl_value_t *yub, jl_stenv_t *e, int flip); + static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int8_t R, int param) { jl_varbinding_t *bb = lookup(e, b); @@ -2699,20 +2692,14 @@ 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_savedenv_t se; if (param == 2) { jl_value_t *ub = NULL; JL_GC_PUSH1(&ub); if (!jl_has_free_typevars(a)) { - save_env(e, &se, 1); - int issub = subtype_in_env_existential(bb->lb, a, e); - restore_env(e, &se, 1); - if (issub) { - issub = subtype_in_env_existential(a, bb->ub, e); - restore_env(e, &se, 1); - } - free_env(&se); - if (!issub) { + if (R) flip_offset(e); + int ccheck = intersect_var_ccheck_in_env(bb->lb, bb->ub, a, a, e, !R); + if (R) flip_offset(e); + if (!ccheck) { JL_GC_POP(); return jl_bottom_type; } @@ -2722,6 +2709,7 @@ 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--; + jl_savedenv_t se; save_env(e, &se, 1); int issub = subtype_in_env_existential(bb->lb, ub, e); restore_env(e, &se, 1); @@ -3654,6 +3642,89 @@ 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 intersect_var_ccheck_in_env(jl_value_t *xlb, jl_value_t *xub, jl_value_t *ylb, jl_value_t *yub, jl_stenv_t *e, int flip) +{ + int easy_check1 = xlb == jl_bottom_type || + yub == (jl_value_t *)jl_any_type || + (e->Loffset == 0 && obviously_in_union(yub, xlb)); + int easy_check2 = ylb == jl_bottom_type || + xub == (jl_value_t *)jl_any_type || + (e->Loffset == 0 && obviously_in_union(xub, ylb)); + int nofree1 = 0, nofree2 = 0; + if (!easy_check1) { + nofree1 = !jl_has_free_typevars(xlb) && !jl_has_free_typevars(yub); + if (nofree1 && e->Loffset == 0) { + easy_check1 = jl_subtype(xlb, yub); + if (!easy_check1) + return 0; + } + } + if (!easy_check2) { + nofree2 = !jl_has_free_typevars(ylb) && !jl_has_free_typevars(xub); + if (nofree2 && e->Loffset == 0) { + easy_check2 = jl_subtype(ylb, xub); + if (!easy_check2) + return 0; + } + } + if (easy_check1 && easy_check2) + return 1; + int ccheck = 0; + if ((easy_check1 || nofree1) && (easy_check2 || nofree2)) { + jl_varbinding_t *vars = e->vars; + e->vars = NULL; + ccheck = easy_check1 || subtype_in_env(xlb, yub, e); + if (ccheck && !easy_check2) { + flip_offset(e); + ccheck = subtype_in_env(ylb, xub, e); + flip_offset(e); + } + e->vars = vars; + return ccheck; + } + jl_savedenv_t se; + save_env(e, &se, 1); + // first try normal flip. + if (flip) flip_vars(e); + ccheck = easy_check1 || subtype_in_env(xlb, yub, e); + if (ccheck && !easy_check2) { + flip_offset(e); + ccheck = subtype_in_env(ylb, xub, e); + flip_offset(e); + } + if (flip) flip_vars(e); + if (!ccheck) { + // then try reverse flip. + restore_env(e, &se, 1); + if (!flip) flip_vars(e); + ccheck = easy_check1 || subtype_in_env(xlb, yub, e); + if (ccheck && !easy_check2) { + flip_offset(e); + ccheck = subtype_in_env(ylb, xub, e); + flip_offset(e); + } + if (!flip) flip_vars(e); + } + if (!ccheck) { + // then try existential. + restore_env(e, &se, 1); + if (easy_check1) + ccheck = 1; + else { + ccheck = subtype_in_env_existential(xlb, yub, e); + restore_env(e, &se, 1); + } + if (ccheck && !easy_check2) { + flip_offset(e); + ccheck = subtype_in_env_existential(ylb, xub, e); + flip_offset(e); + restore_env(e, &se, 1); + } + } + free_env(&se); + return ccheck; +} + static int has_typevar_via_env(jl_value_t *x, jl_tvar_t *t, jl_stenv_t *e) { if (e->Loffset == 0) { @@ -3786,14 +3857,8 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa ccheck = 1; } else { - if (R) flip_vars(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); + // try many subtype check to avoid false `Union{}` + ccheck = intersect_var_ccheck_in_env(xlb, xub, ylb, yub, e, R); } if (R) flip_offset(e); if (!ccheck) diff --git a/test/subtype.jl b/test/subtype.jl index 9d1ce866c7a6b..d371d18732d9b 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -1689,9 +1689,7 @@ CovType{T} = Union{AbstractArray{T,2}, # issue #31703 @testintersect(Pair{<:Any, Ref{Tuple{Ref{Ref{Tuple{Int}}},Ref{Float64}}}}, Pair{T, S} where S<:(Ref{A} where A<:(Tuple{C,Ref{T}} where C<:(Ref{D} where D<:(Ref{E} where E<:Tuple{FF}) where FF<:B)) where B) where T, - Pair{T, Ref{Tuple{Ref{Ref{Tuple{Int}}},Ref{Float64}}}} where T) -# TODO: should be able to get this result -# Pair{Float64, Ref{Tuple{Ref{Ref{Tuple{Int}}},Ref{Float64}}}} + Pair{Float64, Ref{Tuple{Ref{Ref{Tuple{Int}}},Ref{Float64}}}}) module I31703 using Test, LinearAlgebra @@ -1743,8 +1741,7 @@ end Tuple{Type{SA{2, L}}, Type{SA{2, L}}} where L) @testintersect(Tuple{Type{SA{2, L}}, Type{SA{2, 16}}} where L, Tuple{Type{<:SA{N, L}}, Type{<:SA{N, L}}} where {N,L}, - # TODO: this could be narrower - Tuple{Type{SA{2, L}}, Type{SA{2, 16}}} where L) + Tuple{Type{SA{2, 16}}, Type{SA{2, 16}}}) # issue #31993 @testintersect(Tuple{Type{<:AbstractVector{T}}, Int} where T, @@ -1849,9 +1846,9 @@ c32703(::Type{<:Str{C}}, str::Str{C}) where {C<:CSE} = str Tuple{Type{<:Str{C}}, Str{C}} where {C<:CSE}, Union{}) @test c32703(UTF16Str, ASCIIStr()) == 42 -@test_broken typeintersect(Tuple{Vector{Vector{Float32}},Matrix,Matrix}, - Tuple{Vector{V},Matrix{Int},Matrix{S}} where {S, V<:AbstractVector{S}}) == - Tuple{Array{Array{Float32,1},1},Array{Int,2},Array{Float32,2}} +@testintersect(Tuple{Vector{Vector{Float32}},Matrix,Matrix}, + Tuple{Vector{V},Matrix{Int},Matrix{S}} where {S, V<:AbstractVector{S}}, + Tuple{Array{Array{Float32,1},1},Array{Int,2},Array{Float32,2}}) @testintersect(Tuple{Pair{Int, DataType}, Any}, Tuple{Pair{A, B} where B<:Type, Int} where A, @@ -2447,6 +2444,11 @@ end abstract type P47654{A} end @test Wrapper47654{P47654, Vector{Union{P47654,Nothing}}} <: Wrapper47654 +#issue 41561 +@testintersect(Tuple{Vector{VT}, Vector{VT}} where {N1, VT<:AbstractVector{N1}}, + Tuple{Vector{VN} where {N, VN<:AbstractVector{N}}, Vector{Vector{Float64}}}, + Tuple{Vector{Vector{Float64}}, Vector{Vector{Float64}}}) + @testset "known subtype/intersect issue" begin #issue 45874 let S = Pair{Val{P}, AbstractVector{<:Union{P,<:AbstractMatrix{P}}}} where P, @@ -2454,9 +2456,6 @@ abstract type P47654{A} end @test S <: T end - #issue 41561 - @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 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}}} @@ -2695,3 +2694,15 @@ end Val{Tuple{T,R,S}} where {T,R<:Vector{T},S<:Vector{R}}, Val{Tuple{Int, Vector{Int}, T}} where T<:Vector{Vector{Int}}, ) + +#issue 57429 +@testintersect( + Pair{<:Any, <:Tuple{Int}}, + Pair{N, S} where {N, NTuple{N,Int}<:S<:NTuple{M,Int} where {M}}, + !Union{} +) +@testintersect( + Pair{N, T} where {N,NTuple{N,Int}<:T<:NTuple{N,Int}}, + Pair{N, T} where {N,NTuple{N,Int}<:T<:Tuple{Int,Vararg{Int}}}, + !Union{} +) From e4d6a37a93ae2e8359372de09765a8548d30ecc8 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Fri, 3 Jan 2025 14:20:00 -0300 Subject: [PATCH 06/68] Make sure we don't promise alignments that are larger than the heap alignment to LLVM (#56938) Fixes https://github.com/JuliaLang/julia/issues/56937 --------- Co-authored-by: Oscar Smith (cherry picked from commit 1e2758e753cf1b1f710492cdb558550b9982266f) --- src/codegen.cpp | 2 ++ src/datatype.c | 2 ++ test/compiler/codegen.jl | 6 ++++++ 3 files changed, 10 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index 1968050c3bd34..20a6240a926d0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7715,6 +7715,8 @@ static jl_llvm_functions_t Type *RT = Arg->getParamStructRetType(); TypeSize sz = DL.getTypeAllocSize(RT); Align al = DL.getPrefTypeAlign(RT); + if (al > MAX_ALIGN) + al = Align(MAX_ALIGN); param.addAttribute(Attribute::NonNull); // The `dereferenceable` below does not imply `nonnull` for non addrspace(0) pointers. param.addDereferenceableAttr(sz); diff --git a/src/datatype.c b/src/datatype.c index 905959fb80e0a..3e8de1d59f33b 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -648,6 +648,8 @@ void jl_compute_field_offsets(jl_datatype_t *st) if (al > alignm) alignm = al; } + if (alignm > MAX_ALIGN) + alignm = MAX_ALIGN; // We cannot guarantee alignments over 16 bytes because that's what our heap is aligned as if (LLT_ALIGN(sz, alignm) > sz) { haspadding = 1; sz = LLT_ALIGN(sz, alignm); diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 00aae0e748bc9..21cf2e8fca5db 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -873,3 +873,9 @@ end # Core.getptls() special handling @test !occursin("call ptr @jlplt", get_llvm(Core.getptls, Tuple{})) #It should lower to a direct load of the ptls and not a ccall + + +struct Vec56937 x::NTuple{8, VecElement{Int}} end + +x56937 = Ref(Vec56937(ntuple(_->VecElement(1),8))) +@test x56937[].x[1] == VecElement{Int}(1) # shouldn't crash From c8c5d3975f227e211264e14a2425a41f54df9e61 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 11 Mar 2025 15:42:43 -0400 Subject: [PATCH 07/68] fix alignment computation for nested objects (#57722) The alignment of a nested object (in C layouts) is not affected by the alignment of its parent container when computing a field offset (as if it will be allocated at address 0). This can be strongly counter-intuitive (as it implies it should add padding where it does not seem to provide value to the user), but this is required to match the C standard. It also permits users to write custom allocators which happen to provide alignment in excess of that which codegen may assume is guaranteed, and get the behavioral characteristics they intended to specify (without resorting to computing explicit padding). Addresses https://github.com/JuliaLang/julia/issues/57713#issuecomment-2714111074 (Cherry-picked from ec3c02af5cd6e63631c7998792e41c679e1c5364 in v1.11 backports branch) --- doc/src/manual/calling-c-and-fortran-code.md | 24 ++++++++++------ src/cgutils.cpp | 5 ++-- src/datatype.c | 29 +++++++++++++++----- test/core.jl | 7 +++++ 4 files changed, 47 insertions(+), 18 deletions(-) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index 7b889589c592d..63e0178e2bb84 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -543,15 +543,14 @@ is not valid, since the type layout of `T` is not known statically. ### SIMD Values -Note: This feature is currently implemented on 64-bit x86 and AArch64 platforms only. - If a C/C++ routine has an argument or return value that is a native SIMD type, the corresponding Julia type is a homogeneous tuple of `VecElement` that naturally maps to the SIMD type. Specifically: -> * The tuple must be the same size as the SIMD type. For example, a tuple representing an `__m128` -> on x86 must have a size of 16 bytes. -> * The element type of the tuple must be an instance of `VecElement{T}` where `T` is a primitive type that -> is 1, 2, 4 or 8 bytes. +> * The tuple must be the same size and elements as the SIMD type. For example, a tuple +> representing an `__m128` on x86 must have a size of 16 bytes and Float32 elements. +> * The element type of the tuple must be an instance of `VecElement{T}` where `T` is a +> primitive type with a power-of-two number of bytes (e.g. 1, 2, 4, 8, 16, etc) such as +> Int8 or Float64. For instance, consider this C routine that uses AVX intrinsics: @@ -624,6 +623,10 @@ For translating a C argument list to Julia: * `T`, where `T` is a Julia leaf type * argument value will be copied (passed by value) + * `vector T` (or `__attribute__ vector_size`, or a typedef such as `__m128`) + + * `NTuple{N, VecElement{T}}`, where `T` is a primitive Julia type of the correct size + and N is the number of elements in the vector (equal to `vector_size / sizeof T`). * `void*` * depends on how this parameter is used, first translate this to the intended pointer type, then @@ -670,13 +673,16 @@ For translating a C return type to Julia: * `T`, where `T` is one of the primitive types: `char`, `int`, `long`, `short`, `float`, `double`, `complex`, `enum` or any of their `typedef` equivalents - * `T`, where `T` is an equivalent Julia Bits Type (per the table above) - * if `T` is an `enum`, the argument type should be equivalent to `Cint` or `Cuint` + * same as C argument list * argument value will be copied (returned by-value) * `struct T` (including typedef to a struct) - * `T`, where `T` is a Julia Leaf Type + * same as C argument list * argument value will be copied (returned by-value) + + * `vector T` + + * same as C argument list * `void*` * depends on how this parameter is used, first translate this to the intended pointer type, then diff --git a/src/cgutils.cpp b/src/cgutils.cpp index bb96805bbb72b..d560ad47ff7aa 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -3301,7 +3301,7 @@ static void union_alloca_type(jl_uniontype_t *ut, [&](unsigned idx, jl_datatype_t *jt) { if (!jl_is_datatype_singleton(jt)) { size_t nb1 = jl_datatype_size(jt); - size_t align1 = jl_datatype_align(jt); + size_t align1 = julia_alignment((jl_value_t*)jt); if (nb1 > nbytes) nbytes = nb1; if (align1 > align) @@ -3850,9 +3850,10 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg // whether we should perform the initialization with the struct as a IR value // or instead initialize the stack buffer with stores + // although we do the former if it is a vector or could be a vector element auto tracked = CountTrackedPointers(lt); bool init_as_value = false; - if (lt->isVectorTy() || jl_is_vecelement_type(ty)) { // maybe also check the size ? + if (lt->isVectorTy() || jl_special_vector_alignment(1, ty) != 0) { init_as_value = true; } else if (tracked.count) { diff --git a/src/datatype.c b/src/datatype.c index 3e8de1d59f33b..b823f933b1376 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -291,9 +291,10 @@ static jl_datatype_layout_t *jl_get_layout(uint32_t sz, } // Determine if homogeneous tuple with fields of type t will have -// a special alignment beyond normal Julia rules. +// a special alignment and vector-ABI beyond normal rules for aggregates. // Return special alignment if one exists, 0 if normal alignment rules hold. // A non-zero result *must* match the LLVM rules for a vector type . +// Matching the compiler's `__attribute__ vector_size` behavior. // For sake of Ahead-Of-Time (AOT) compilation, this routine has to work // without LLVM being available. unsigned jl_special_vector_alignment(size_t nfields, jl_value_t *t) @@ -308,8 +309,12 @@ unsigned jl_special_vector_alignment(size_t nfields, jl_value_t *t) // motivating use case comes up for Julia, we reject pointers. return 0; size_t elsz = jl_datatype_size(ty); - if (elsz != 1 && elsz != 2 && elsz != 4 && elsz != 8) - // Only handle power-of-two-sized elements (for now) + if (next_power_of_two(elsz) != elsz) + // Only handle power-of-two-sized elements (for now), since other + // lengths may be packed into very complicated arrangements (llvm pads + // extra bits on most platforms when computing alignment but not when + // computing type size, but adds no extra bytes for each element, so + // their effect on offsets are never what you may naturally expect). return 0; size_t size = nfields * elsz; // Use natural alignment for this vector: this matches LLVM and clang. @@ -601,9 +606,9 @@ void jl_compute_field_offsets(jl_datatype_t *st) } else { fsz = sizeof(void*); - if (fsz > MAX_ALIGN) - fsz = MAX_ALIGN; al = fsz; + if (al > MAX_ALIGN) + al = MAX_ALIGN; desc[i].isptr = 1; zeroinit = 1; npointers++; @@ -648,8 +653,6 @@ void jl_compute_field_offsets(jl_datatype_t *st) if (al > alignm) alignm = al; } - if (alignm > MAX_ALIGN) - alignm = MAX_ALIGN; // We cannot guarantee alignments over 16 bytes because that's what our heap is aligned as if (LLT_ALIGN(sz, alignm) > sz) { haspadding = 1; sz = LLT_ALIGN(sz, alignm); @@ -825,6 +828,18 @@ JL_DLLEXPORT jl_datatype_t *jl_new_primitivetype(jl_value_t *name, jl_module_t * jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 0, 0); uint32_t nbytes = (nbits + 7) / 8; uint32_t alignm = next_power_of_two(nbytes); +# if defined(_CPU_X86_) && !defined(_OS_WINDOWS_) + // datalayout strings are often weird: on 64-bit they usually follow fairly simple rules, + // but on x86 32 bit platforms, sometimes 5 to 8 byte types are + // 32-bit aligned even though the MAX_ALIGN (for types 9+ bytes) is 16 + // (except for f80 which is align 4 on Mingw, Linux, and BSDs--but align 16 on MSVC and Darwin) + // https://llvm.org/doxygen/ARMTargetMachine_8cpp.html#adb29b487708f0dc2a940345b68649270 + // https://llvm.org/doxygen/AArch64TargetMachine_8cpp.html#a003a58caf135efbf7273c5ed84e700d7 + // https://llvm.org/doxygen/X86TargetMachine_8cpp.html#aefdbcd6131ef195da070cef7fdaf0532 + // 32-bit alignment is weird + if (alignm == 8) + alignm = 4; +# endif if (alignm > MAX_ALIGN) alignm = MAX_ALIGN; // memoize isprimitivetype, since it is much easier than checking diff --git a/test/core.jl b/test/core.jl index 1938e0801a100..4193267b1abed 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5665,6 +5665,13 @@ let ni128 = sizeof(FP128test) ÷ sizeof(Int), @test reinterpret(UInt128, arr[2].fp) == expected end +# make sure VecElement Tuple has the C alignment and ABI for supported types +primitive type Int24 24 end +@test Base.datatype_alignment(NTuple{10,VecElement{Int16}}) == 32 +@test Base.datatype_alignment(NTuple{10,VecElement{Int24}}) == 4 +@test Base.datatype_alignment(NTuple{10,VecElement{Int64}}) == 128 +@test Base.datatype_alignment(NTuple{10,VecElement{Int128}}) == 256 + # issue #21516 struct T21516 x::Vector{Float64} From 9a73b3a0c4a165923de97588e188743700bf1969 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 18 Aug 2023 12:09:16 -0400 Subject: [PATCH 08/68] fix missing methods in ml_matches results (#50962) This was resulting in it being too aggressive at filtering out "duplicate" results, resulting in possible inference mistakes or missing guardsig entries. Fixes: https://github.com/JuliaLang/julia/pull/50722#issuecomment-1658995284 (cherry picked from commit 762801cb537657f169c993edbef61751e3a51f7f) --- src/gf.c | 4 ++-- test/ambiguous.jl | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/gf.c b/src/gf.c index ac661782e43be..1f87a3035076e 100644 --- a/src/gf.c +++ b/src/gf.c @@ -3376,7 +3376,7 @@ static int sort_mlmatches(jl_array_t *t, size_t idx, arraylist_t *visited, array 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) + int subt2 = matc2->fully_covers == 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)) @@ -3521,7 +3521,7 @@ static int sort_mlmatches(jl_array_t *t, size_t idx, arraylist_t *visited, array 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) + 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 // 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)) { diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 496da6ac37fc6..27d4655a04497 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -288,6 +288,30 @@ for f in (Ambig8.f, Ambig8.g) @test f(Int8(0)) == 4 @test_throws MethodError f(0) @test_throws MethodError f(pi) + let ambig = Ref{Int32}(0) + ms = Base._methods_by_ftype(Tuple{typeof(f), Union{Int,AbstractIrrational}}, nothing, 10, Base.get_world_counter(), false, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + @test ms isa Vector + @test length(ms) == 2 + @test ambig[] == 1 + end + let ambig = Ref{Int32}(0) + ms = Base._methods_by_ftype(Tuple{typeof(f), Union{Int,AbstractIrrational}}, nothing, -1, Base.get_world_counter(), false, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + @test ms isa Vector + @test length(ms) == 2 + @test ambig[] == 1 + end + let ambig = Ref{Int32}(0) + ms = Base._methods_by_ftype(Tuple{typeof(f), Union{Int,AbstractIrrational}}, nothing, 10, Base.get_world_counter(), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + @test ms isa Vector + @test length(ms) == 3 + @test ambig[] == 1 + end + let ambig = Ref{Int32}(0) + ms = Base._methods_by_ftype(Tuple{typeof(f), Union{Int,AbstractIrrational}}, nothing, -1, Base.get_world_counter(), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + @test ms isa Vector + @test length(ms) == 3 + @test ambig[] == 1 + end end module Ambig9 From 49c575368601e8357919ef51dda846e2560e895c Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 25 Mar 2025 08:51:21 -0400 Subject: [PATCH 09/68] Don't free regex objects in exit-time finalizer calls (#57834) Fixes https://github.com/JuliaLang/julia/issues/57817 on 1.11 --- base/regex.jl | 7 ++++++- test/regex.jl | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/base/regex.jl b/base/regex.jl index c8d66265e0784..be0e8a56087e1 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -39,7 +39,12 @@ mutable struct Regex <: AbstractPattern end re = compile(new(pattern, compile_options, match_options, C_NULL)) finalizer(re) do re - re.regex == C_NULL || PCRE.free_re(re.regex) + # don't free during exit because tasks may still be running and + # using it. Issue #57817 + during_exit = Base._atexit_hooks_finished + if re.regex != C_NULL && !during_exit + PCRE.free_re(re.regex) + end end re end diff --git a/test/regex.jl b/test/regex.jl index e5f1428527512..ca411b26bbacc 100644 --- a/test/regex.jl +++ b/test/regex.jl @@ -245,3 +245,11 @@ end @test match(re, "ababc").match === SubString("ababc", 3:5) end end + +@testset "#57817: Don't free Regex during exit finalizer calls" begin + # this shouldn't segfault + cmd = `$(Base.julia_cmd()) -t2 --startup-file=no -e 're = Regex(""); Threads.@spawn match(re, "", 1, UInt32(0))'` + for i in 1:10 + @test success(pipeline(cmd, stderr=stderr)) + end +end From 3559623b7017910587cd5a89578ee45c77ba80ae Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 27 Mar 2025 18:47:52 -0400 Subject: [PATCH 10/68] fixup "Don't free regex objects in exit-time finalizer calls (#57834)" --- base/regex.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/base/regex.jl b/base/regex.jl index be0e8a56087e1..e8af2f41804d4 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -40,8 +40,9 @@ mutable struct Regex <: AbstractPattern re = compile(new(pattern, compile_options, match_options, C_NULL)) finalizer(re) do re # don't free during exit because tasks may still be running and - # using it. Issue #57817 - during_exit = Base._atexit_hooks_finished + # using it. Issue #57817. During sysimage creation _atexit_hooks_finished + # is not defined but threads aren't running so just always run + during_exit = @isdefined(_atexit_hooks_finished) && _atexit_hooks_finished if re.regex != C_NULL && !during_exit PCRE.free_re(re.regex) end From 6d8d5e319c1f9dfce9ae7ab93a4e99d51755364f Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Fri, 28 Mar 2025 09:24:18 -0300 Subject: [PATCH 11/68] [backports-1.10] Add passes to `-O1` pipeline to reduce allocations in `reinterpret` (#57731) --- src/pipeline.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pipeline.cpp b/src/pipeline.cpp index 8a9410aa98247..dd81c1d9989c3 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -458,6 +458,13 @@ static void buildScalarOptimizerPipeline(FunctionPassManager &FPM, PassBuilder * FPM.addPass(IRCEPass()); FPM.addPass(InstCombinePass()); FPM.addPass(JumpThreadingPass()); + } else if (O.getSpeedupLevel() >= 1) { + JULIA_PASS(FPM.addPass(AllocOptPass())); + FPM.addPass(SROAPass()); + FPM.addPass(MemCpyOptPass()); + FPM.addPass(SCCPPass()); + FPM.addPass(InstCombinePass()); + FPM.addPass(ADCEPass()); } if (O.getSpeedupLevel() >= 3) { FPM.addPass(GVNPass()); From 5170fe7f6b112883e116bcbdd3253f21aacb5565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Sat, 15 Mar 2025 00:56:03 +0000 Subject: [PATCH 12/68] [docs] Clarify that Float16 is supported natively when possible (#57725) Co-authored-by: Jishnu Bhattacharya (cherry picked from commit 3e57a8ac3adbe28e2a9a67e2fed2a1be24375ca5) --- doc/src/manual/integers-and-floating-point-numbers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/manual/integers-and-floating-point-numbers.md b/doc/src/manual/integers-and-floating-point-numbers.md index 173ca7847616e..2175f5f21a36c 100644 --- a/doc/src/manual/integers-and-floating-point-numbers.md +++ b/doc/src/manual/integers-and-floating-point-numbers.md @@ -335,8 +335,8 @@ julia> typeof(x) Float64 ``` -Half-precision floating-point numbers are also supported ([`Float16`](@ref)), but they are -implemented in software and use [`Float32`](@ref) for calculations. +Half-precision floating-point numbers are also supported ([`Float16`](@ref)) on all platforms, with native instructions used on hardware which supports this number format. Otherwise, operations are implemented in software, and use [`Float32`](@ref) for intermediate calculations. +As an internal implementation detail, this is achieved under the hood by using LLVM's [`half`](https://llvm.org/docs/LangRef.html#half-precision-floating-point-intrinsics) type, which behaves similarly to what the GCC [`-fexcess-precision=16`](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fexcess-precision) flag does for C/C++ code. ```jldoctest julia> sizeof(Float16(4.)) From b6d3b7210beb4891e7b4049b2c1f7596db83ac63 Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Mon, 17 Mar 2025 06:05:16 +0100 Subject: [PATCH 13/68] fix special function `::Real` fallback stack overflow (#57790) Fixes #57789 (cherry picked from commit 6817691ecbba3e2a687348c085af1c3d76f020fe) --- base/math.jl | 2 +- test/math.jl | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/base/math.jl b/base/math.jl index 0f917dce7c99c..0e2b7fb7c6397 100644 --- a/base/math.jl +++ b/base/math.jl @@ -1574,7 +1574,7 @@ for f in (:sin, :cos, :tan, :asin, :atan, :acos, :exponent, :sqrt, :cbrt, :sinpi, :cospi, :sincospi, :tanpi) @eval function ($f)(x::Real) xf = float(x) - x === xf && throw(MethodError($f, (x,))) + xf isa typeof(x) && throw(MethodError($f, (x,))) return ($f)(xf) end @eval $(f)(::Missing) = missing diff --git a/test/math.jl b/test/math.jl index 8bc981993f5c8..bd00f3417ecd3 100644 --- a/test/math.jl +++ b/test/math.jl @@ -1455,6 +1455,14 @@ end @test 8.758520413376658e-5^70.55863059215994 == 5.052076767078296e-287 end +@testset "special function `::Real` fallback shouldn't recur without bound, issue #57789" begin + mutable struct Issue57789 <: Real end + Base.float(::Issue57789) = Issue57789() + for f ∈ (sin, sinpi, log, exp) + @test_throws MethodError f(Issue57789()) + end +end + # Test that sqrt behaves correctly and doesn't exhibit fp80 double rounding. # This happened on old glibc versions. # Test case from https://sourceware.org/bugzilla/show_bug.cgi?id=14032. From cc1d01192cb49d44e4df92932979211ce260daa7 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 17 Mar 2025 16:53:43 -0400 Subject: [PATCH 14/68] stackwalk: fix heuristic termination (#57801) When getting stacktraces on non-X86 platforms, the first frame may not have been set up yet, incorrectly triggering this bad-frame detection logic. This should fix the issue of async unwind failing after only getting 2 frames, if the first frame happens to land in the function header. This is not normally an issue on X86 or non-signals, but also causes no expected issues to be the same logic there too. Fix #52334 After (on arm64-apple-darwin24.3.0): ``` julia> f(1) Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable. ERROR: StackOverflowError: Stacktrace: [1] f(x::Int64) @ Main ./REPL[3]:1 [2] g(x::Int64) @ Main ./REPL[4]:1 --- the above 2 lines are repeated 39990 more times --- [79983] f(x::Int64) @ Main ./REPL[3]:1 ``` n.b. This will not fix and is not related to any issues where profiling gets only a single stack frame during profiling of syscalls on Apple AArch64. This fix is specific to the bug where it gets exactly 2 frames. (cherry picked from commit f82917a8ada5a3f4e4ca85408e078a6389c9e94a) --- src/stackwalk.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/stackwalk.c b/src/stackwalk.c index 913e233418796..d552008fe9ab0 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -97,9 +97,13 @@ static int jl_unw_stepn(bt_cursor_t *cursor, jl_bt_element_t *bt_data, size_t *b } uintptr_t oldsp = thesp; have_more_frames = jl_unw_step(cursor, from_signal_handler, &return_ip, &thesp); - if (oldsp >= thesp && !jl_running_under_rr(0)) { - // The stack pointer is clearly bad, as it must grow downwards. + if ((n < 2 ? oldsp > thesp : oldsp >= thesp) && !jl_running_under_rr(0)) { + // The stack pointer is clearly bad, as it must grow downwards, // But sometimes the external unwinder doesn't check that. + // Except for n==0 when there is no oldsp and n==1 on all platforms but i686/x86_64. + // (on x86, the platform first pushes the new stack frame, then does the + // call, on almost all other platforms, the platform first does the call, + // then the user pushes the link register to the frame). have_more_frames = 0; } if (return_ip == 0) { @@ -131,11 +135,11 @@ static int jl_unw_stepn(bt_cursor_t *cursor, jl_bt_element_t *bt_data, size_t *b // * The way that libunwind handles it in `unw_get_proc_name`: // https://lists.nongnu.org/archive/html/libunwind-devel/2014-06/msg00025.html uintptr_t call_ip = return_ip; + #if defined(_CPU_ARM_) // ARM instruction pointer encoding uses the low bit as a flag for // thumb mode, which must be cleared before further use. (Note not // needed for ARM AArch64.) See // https://github.com/libunwind/libunwind/pull/131 - #ifdef _CPU_ARM_ call_ip &= ~(uintptr_t)0x1; #endif // Now there's two main cases to adjust for: From f890c91cf7441a6ef3e0afa37b01e2ae61461e2d Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Thu, 20 Mar 2025 22:02:43 +0100 Subject: [PATCH 15/68] restrict dispatch of some custrom string macros to `String` (#57781) Restricts the dispatch for these macros so that only `String` (literal) arguments are accepted: * `int128_str` * `uint128_str` * `big_str` * `ip_str` from the Sockets stdlib * `dateformat_str` from the Dates stdlib Some of these changes make the code in the sysimage less vulnerable to invalidation. (cherry picked from commit f9365a5ecd336388c0d4f3c987c1c8fde95b4da3) --- base/int.jl | 6 +++--- stdlib/Dates/src/io.jl | 2 +- stdlib/Sockets/src/IPAddr.jl | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/base/int.jl b/base/int.jl index 4b2f542bba788..27272b17c5b9f 100644 --- a/base/int.jl +++ b/base/int.jl @@ -711,7 +711,7 @@ ERROR: LoadError: ArgumentError: invalid base 10 digit '.' in "123456789123.4" [...] ``` """ -macro int128_str(s) +macro int128_str(s::String) return parse(Int128, s) end @@ -731,7 +731,7 @@ ERROR: LoadError: ArgumentError: invalid base 10 digit '-' in "-123456789123" [...] ``` """ -macro uint128_str(s) +macro uint128_str(s::String) return parse(UInt128, s) end @@ -755,7 +755,7 @@ ERROR: ArgumentError: invalid number format _ for BigInt or BigFloat [...] ``` """ -macro big_str(s) +macro big_str(s::String) message = "invalid number format $s for BigInt or BigFloat" throw_error = :(throw(ArgumentError($message))) if '_' in s diff --git a/stdlib/Dates/src/io.jl b/stdlib/Dates/src/io.jl index 257e86064c2fb..0771d0f46c69f 100644 --- a/stdlib/Dates/src/io.jl +++ b/stdlib/Dates/src/io.jl @@ -460,7 +460,7 @@ but creates the DateFormat object once during macro expansion. See [`DateFormat`](@ref) for details about format specifiers. """ -macro dateformat_str(str) +macro dateformat_str(str::String) DateFormat(str) end diff --git a/stdlib/Sockets/src/IPAddr.jl b/stdlib/Sockets/src/IPAddr.jl index 04710e400fe87..153720aca2f1c 100644 --- a/stdlib/Sockets/src/IPAddr.jl +++ b/stdlib/Sockets/src/IPAddr.jl @@ -265,7 +265,7 @@ julia> @ip_str "2001:db8:0:0:0:0:2:1" ip"2001:db8::2:1" ``` """ -macro ip_str(str) +macro ip_str(str::String) return parse(IPAddr, str) end From da7036ad5c56514ccf749202e4d421119fe802b5 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Sat, 22 Mar 2025 21:55:11 -0400 Subject: [PATCH 16/68] fix `mod` for mixes of `Signed` and `Unsigned` (#57853) Previously this was just overfowing producing wrong answers (both for the sign convention and just the wrong modulo class) fixes https://github.com/JuliaLang/julia/issues/57851 (cherry picked from commit 056891701e8ec629834def549f565cf80c46ccf9) --- base/int.jl | 10 ++++++++-- test/int.jl | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/base/int.jl b/base/int.jl index 27272b17c5b9f..5e43761eeee13 100644 --- a/base/int.jl +++ b/base/int.jl @@ -286,8 +286,14 @@ function mod(x::T, y::T) where T<:Integer y == -1 && return T(0) # avoid potential overflow in fld return x - fld(x, y) * y end -mod(x::BitSigned, y::Unsigned) = rem(y + unsigned(rem(x, y)), y) -mod(x::Unsigned, y::Signed) = rem(y + signed(rem(x, y)), y) +function mod(x::BitSigned, y::Unsigned) + remval = rem(x, y) # correct iff remval>=0 + return unsigned(remval + (remval0 so correct iff y>0 or remval==0 + return remval + (!iszero(remval) && y Date: Tue, 25 Mar 2025 13:56:37 +0100 Subject: [PATCH 17/68] `Base`: `macro b_str`: restrict argument to `String` (#57863) Should make the sysimage less vulnerable to invalidation. As far as I understand this change can't break any code, because: * the macro definition requires `AbstractString` * the way Julia macros work, the argument is either a literal or an `Expr` * `String` is the only `AbstractString` with literals (cherry picked from commit 87f4d3a344e7efe49fc41870f5e3e3901fca8b00) --- base/strings/io.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/strings/io.jl b/base/strings/io.jl index 987a64798d3da..2bb19c7e249c7 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -557,7 +557,7 @@ julia> v[2] 0x32 ``` """ -macro b_str(s) +macro b_str(s::String) v = codeunits(unescape_string(s)) QuoteNode(v) end From 026535755c51090301b476d2a7836b684d694563 Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Tue, 25 Mar 2025 13:56:52 +0100 Subject: [PATCH 18/68] `Base`: `macro cmd`: restrict argument to `String` (#57862) Should make the sysimage less vulnerable to invalidation. As far as I understand this change can't break any code, because: * the macro definition requires `AbstractString` * the way Julia macros work, the argument is either a literal or an `Expr` * `String` is the only `AbstractString` with literals (cherry picked from commit 7acb2a6aa4299b38cddb52117b21dcdbd1c8268d) --- base/cmd.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/cmd.jl b/base/cmd.jl index 475a62a82d4d7..d2aa279aaca40 100644 --- a/base/cmd.jl +++ b/base/cmd.jl @@ -497,7 +497,7 @@ julia> run(cm) Process(`echo 1`, ProcessExited(0)) ``` """ -macro cmd(str) +macro cmd(str::String) cmd_ex = shell_parse(str, special=shell_special, filename=String(__source__.file))[1] return :(cmd_gen($(esc(cmd_ex)))) end From 274e2b1c1503fa90c8366b102a66b9584bc85f3b Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Fri, 28 Mar 2025 12:26:13 +0100 Subject: [PATCH 19/68] `Random`: `show` method for `MersenneTwister`: invalidation resistance (#57913) Avoid using or constructing the vector with nonconcrete element type. Should make the sysimage more resistant to method invalidation. (cherry picked from commit 8e03cb11c8460b14ccb1553cb11343c3f39a6774) --- stdlib/Random/src/RNGs.jl | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/stdlib/Random/src/RNGs.jl b/stdlib/Random/src/RNGs.jl index 292ae00d33628..e944aaae5b205 100644 --- a/stdlib/Random/src/RNGs.jl +++ b/stdlib/Random/src/RNGs.jl @@ -151,21 +151,26 @@ function show(io::IO, rng::MersenneTwister) end print(io, MersenneTwister, "(", seed_str, ", (") # state - adv = Integer[rng.adv_jump, rng.adv] + sep = ", " + show(io, rng.adv_jump) + print(io, sep) + show(io, rng.adv) if rng.adv_vals != -1 || rng.adv_ints != -1 - if rng.adv_vals == -1 - @assert rng.idxF == MT_CACHE_F - push!(adv, 0, 0) # "(0, 0)" is nicer on the eyes than (-1, 1002) - else - push!(adv, rng.adv_vals, rng.idxF) - end + # "(0, 0)" is nicer on the eyes than (-1, 1002) + s = rng.adv_vals != -1 + print(io, sep) + show(io, s ? rng.adv_vals : zero(rng.adv_vals)) + print(io, sep) + show(io, s ? rng.idxF : zero(rng.idxF)) end if rng.adv_ints != -1 idxI = (length(rng.ints)*16 - rng.idxI) / 8 # 8 represents one Int64 idxI = Int(idxI) # idxI should always be an integer when using public APIs - push!(adv, rng.adv_ints, idxI) + print(io, sep) + show(io, rng.adv_ints) + print(io, sep) + show(io, idxI) end - join(io, adv, ", ") print(io, "))") end From dd26a76b17f1c9cb7440c5ddd3d36d83d8c72609 Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Sun, 30 Mar 2025 08:55:46 +0200 Subject: [PATCH 20/68] `Base`: shell escaping: inference improvement to prevent invalidation (#57915) (cherry picked from commit 4d2a350870b97059432ac8fa8de92b0b161c335b) --- base/shell.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/shell.jl b/base/shell.jl index 48214418bdee5..3d72ffc253011 100644 --- a/base/shell.jl +++ b/base/shell.jl @@ -315,7 +315,7 @@ function shell_escape_csh(io::IO, args::AbstractString...) end shell_escape_csh(args::AbstractString...) = sprint(shell_escape_csh, args...; - sizehint = sum(sizeof.(args)) + length(args) * 3) + sizehint = sum(sizeof, args) + length(args) * 3) """ shell_escape_wincmd(s::AbstractString) @@ -465,4 +465,4 @@ function escape_microsoft_c_args(io::IO, args::AbstractString...) end escape_microsoft_c_args(args::AbstractString...) = sprint(escape_microsoft_c_args, args...; - sizehint = (sum(sizeof.(args)) + 3*length(args))) + sizehint = (sum(sizeof, args) + 3*length(args))) From eec4655b60a52057caa0a902bda8e55e5b203459 Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Tue, 7 Jan 2025 21:37:32 -0800 Subject: [PATCH 21/68] Update OpenLibm to 0.8.5 (#56984) This fixes building on FreeBSD AArch64, as there was a regression in OpenLibm 0.8.4; see https://github.com/JuliaMath/openlibm/pull/314. (cherry picked from commit 3c7bc45f14c8a2b70ccf1d92d4418cfaaa112b56) --- deps/checksums/openlibm | 72 +++++++++++++++++--------------- deps/openlibm.version | 6 +-- stdlib/OpenLibm_jll/Project.toml | 2 +- 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/deps/checksums/openlibm b/deps/checksums/openlibm index 22d35d23ea7aa..cad61fd42cf94 100644 --- a/deps/checksums/openlibm +++ b/deps/checksums/openlibm @@ -1,34 +1,38 @@ -OpenLibm.v0.8.1+4.aarch64-apple-darwin.tar.gz/md5/f7f8947d276e50b58db7530e050062fd -OpenLibm.v0.8.1+4.aarch64-apple-darwin.tar.gz/sha512/9e28c02d1aa98fdff8e637c9d1a577325d76c098bc89837b5bd3a50ec05647ab8379893a7945d570907b82ef9446804a0065403b6ddec8ede4cb9b0acf5b27ea -OpenLibm.v0.8.1+4.aarch64-linux-gnu.tar.gz/md5/bf9e4bc08aa8488319724b9a3011be8b -OpenLibm.v0.8.1+4.aarch64-linux-gnu.tar.gz/sha512/773263ff709fbdbd07cde5049372175f7cf8fefa26a5fae0fdc48990886911e8c9bcfdb8b05158f38621c79a17e445ad73d41cc4186f6d9df84a3e8232a8a17d -OpenLibm.v0.8.1+4.aarch64-linux-musl.tar.gz/md5/eab078c285039562f65f8fc1d8d62fd9 -OpenLibm.v0.8.1+4.aarch64-linux-musl.tar.gz/sha512/c0b818d728f85ef1ed4231dc2a3bc78e95de9186829d12012b168505b6d6505e4732a379c67a5a3c37e40e048bbaadabc00d153383347e9f606fc9731ebb26d5 -OpenLibm.v0.8.1+4.armv6l-linux-gnueabihf.tar.gz/md5/c76f4ca7bbcd276e5769bd73bee00966 -OpenLibm.v0.8.1+4.armv6l-linux-gnueabihf.tar.gz/sha512/0a711684d1289c747ecadeee0fa1c8ca6067727a5ab5d531f7c30f998c8ee04803c6712067426c939f50698c15104724214eaa3953e522a6dc210c39954f2727 -OpenLibm.v0.8.1+4.armv6l-linux-musleabihf.tar.gz/md5/e072d09a671f98bede25cb0bd00a2fd4 -OpenLibm.v0.8.1+4.armv6l-linux-musleabihf.tar.gz/sha512/37a741e5f66ca4dc8c9358376afb562c7d2a1bb1fcbde2c9b555001a4e6faffc0446b5faff33aead192d4b2fc8cd45c8261b06cc40abec73e39dc63da932b8ab -OpenLibm.v0.8.1+4.armv7l-linux-gnueabihf.tar.gz/md5/c76f4ca7bbcd276e5769bd73bee00966 -OpenLibm.v0.8.1+4.armv7l-linux-gnueabihf.tar.gz/sha512/0a711684d1289c747ecadeee0fa1c8ca6067727a5ab5d531f7c30f998c8ee04803c6712067426c939f50698c15104724214eaa3953e522a6dc210c39954f2727 -OpenLibm.v0.8.1+4.armv7l-linux-musleabihf.tar.gz/md5/e072d09a671f98bede25cb0bd00a2fd4 -OpenLibm.v0.8.1+4.armv7l-linux-musleabihf.tar.gz/sha512/37a741e5f66ca4dc8c9358376afb562c7d2a1bb1fcbde2c9b555001a4e6faffc0446b5faff33aead192d4b2fc8cd45c8261b06cc40abec73e39dc63da932b8ab -OpenLibm.v0.8.1+4.i686-linux-gnu.tar.gz/md5/d8602652f0f347a16e39a869eb2ccb83 -OpenLibm.v0.8.1+4.i686-linux-gnu.tar.gz/sha512/b140032c96497c7e6626751b799598bd54f687af3976bc43ac7ddb6f490527dca64fd44a125da2f54d8ca3679ad53d2700d412ec4c2ddcfe7bfbfe719c0cfc05 -OpenLibm.v0.8.1+4.i686-linux-musl.tar.gz/md5/5d20637487e85b529e3901d129b586a7 -OpenLibm.v0.8.1+4.i686-linux-musl.tar.gz/sha512/b83742801cdfee62bf14bf66f4d20e42219899d431ab335c23f2943c00363c82ee2ab1b853ec421eb825dbe65899fc8f3a3c91c3d86c5e4891c60ddc981d0831 -OpenLibm.v0.8.1+4.i686-w64-mingw32.tar.gz/md5/6ba08a1d5b3aa21fa58618f9ea2a9b8d -OpenLibm.v0.8.1+4.i686-w64-mingw32.tar.gz/sha512/63bed0bc519fa87abae157b53f439f8b452720090780f306b62ff802b8f8ddc4b546899aad5b3e91dacdc3439b7a10a36080d34f09f072719f766c3cdb0baa82 -OpenLibm.v0.8.1+4.powerpc64le-linux-gnu.tar.gz/md5/c9c690ff59b202763ee901f22a798d69 -OpenLibm.v0.8.1+4.powerpc64le-linux-gnu.tar.gz/sha512/83a1e37a62dcff357836385f55f9410cf49b10445c8320b62381359557306c97f11773c508e816fb0bbd4de372b6666833e960921959b6dbb4c7c954e230004a -OpenLibm.v0.8.1+4.x86_64-apple-darwin.tar.gz/md5/bbe8f7a039ad3f07baca340112ea2e65 -OpenLibm.v0.8.1+4.x86_64-apple-darwin.tar.gz/sha512/89e578b7e8e56e7152c9f5881fbf51b33833f8e2143126212e60db262de5641573f33dea5b810575a39e21ec4fbaaa307a049d42e06970c7d4e052b97622938a -OpenLibm.v0.8.1+4.x86_64-linux-gnu.tar.gz/md5/94f42c769349c92987ed3bd9f35d96e0 -OpenLibm.v0.8.1+4.x86_64-linux-gnu.tar.gz/sha512/5e901618da26aa6f0f7d05ed1e5c538aea88cadb59b0efc7213203f1714e85aa0b6f0d463f2a4d367d2a8af36e37e7c353fdefbb98599fdc5bc182a6b93e4348 -OpenLibm.v0.8.1+4.x86_64-linux-musl.tar.gz/md5/462b9dfebd341f0c8bb33ba72f99f769 -OpenLibm.v0.8.1+4.x86_64-linux-musl.tar.gz/sha512/df84815a282a065e8fa33aa35ee29166a1119cd776f8812073e55521ca8c94f4bca679206b8a67ef69edbbb6ba6fca520d81626f687ca353a63146c4858ced80 -OpenLibm.v0.8.1+4.x86_64-unknown-freebsd.tar.gz/md5/f3642b29670e6216056a13f5da9e87b8 -OpenLibm.v0.8.1+4.x86_64-unknown-freebsd.tar.gz/sha512/613242d2f24b1dcc4b2bfd6f22ad66e9889f618ee685b50d9fdf33b9f3798fe503fc9e278f291118db70bacd54bd475f12554a4a56c60c369222fd23b3c5ba22 -OpenLibm.v0.8.1+4.x86_64-w64-mingw32.tar.gz/md5/59381a874c86a5ad5f3fb3b206587f64 -OpenLibm.v0.8.1+4.x86_64-w64-mingw32.tar.gz/sha512/3a8dd3ef46f15f78a34bdc37c5e26dcbf8522625457aa7c52dca21cbb967c5e39257216e5fc203ff57c9171b6c0f2e2ee9d6216c43b0254008f0e91318bb5f0e -openlibm-ae2d91698508701c83cab83714d42a1146dccf85.tar.gz/md5/19408d70bf042a109e1c267a53740089 -openlibm-ae2d91698508701c83cab83714d42a1146dccf85.tar.gz/sha512/9597fdcbc4af8369e6eecc3f8e86f251661cc64d236578f3ee8a6b39e77a47951446e1a0fe1151513da153e7ed17bf39aa5a36c32153d0d0400232bed2839e22 +OpenLibm.v0.8.5+0.aarch64-apple-darwin.tar.gz/md5/5fcbd746e90712e396e76dc4e76724d0 +OpenLibm.v0.8.5+0.aarch64-apple-darwin.tar.gz/sha512/f4ac2bc38bdc723384b67119daa2974fb43da34b2e45cea2029ea48f92c84c4cad6dfb43521b09a1e89ddf8c5b8cc22a38fa4b78ba39ac7524fd6bd1ba897aa9 +OpenLibm.v0.8.5+0.aarch64-linux-gnu.tar.gz/md5/4d1b4cd566805b5179c5ecdd060da473 +OpenLibm.v0.8.5+0.aarch64-linux-gnu.tar.gz/sha512/a9fe1a3d2e3898c017eb8615b2f3dbb514995ff041ac964c931c99c60d8cfe4eab7563a9cd65058f42f83c812f33d998573a7c5cc56a2e3960a4657e459ed321 +OpenLibm.v0.8.5+0.aarch64-linux-musl.tar.gz/md5/413be59af62b3ce0ebafeca093e3179e +OpenLibm.v0.8.5+0.aarch64-linux-musl.tar.gz/sha512/7bd76373e047ba854066af61f1c56b2e3a4d28c266228d7b30f596eadbaec52b070548ae60d41840c425ad5d0829c6c0cdaf326f2f160ed7508877ab5ec1a4b1 +OpenLibm.v0.8.5+0.aarch64-unknown-freebsd.tar.gz/md5/80736f9022c695eb1198e0b591a8fa63 +OpenLibm.v0.8.5+0.aarch64-unknown-freebsd.tar.gz/sha512/c633644578265e7ccc259ceb0442457b8c09290b4861b66c86dd6be7b30c4e394e70728142798097d6fe3afcfb4d9d1bd7ef58513fe8eed5684a4fba51bf185a +OpenLibm.v0.8.5+0.armv6l-linux-gnueabihf.tar.gz/md5/8fe0900a318393a290907f016bc654c3 +OpenLibm.v0.8.5+0.armv6l-linux-gnueabihf.tar.gz/sha512/167100a2d46e68462ef9a66915ced881d6358f05337bd38f2f77176f41cfd5be37e3c5226dd5d7d59147bd3e1aa7fb0893c1c81e9516134d3ab663b5752c4969 +OpenLibm.v0.8.5+0.armv6l-linux-musleabihf.tar.gz/md5/e8566719387984604f19dc5f9354a783 +OpenLibm.v0.8.5+0.armv6l-linux-musleabihf.tar.gz/sha512/532dd2b764fa15f7a838fb14cccafd2d4fe8fa4a132ea8394479a719c7aee11442f1b8a18e5d4a26ca820fa696d9d2afc7f5ec63dd96fa3b6763cea72b7026c3 +OpenLibm.v0.8.5+0.armv7l-linux-gnueabihf.tar.gz/md5/8fe0900a318393a290907f016bc654c3 +OpenLibm.v0.8.5+0.armv7l-linux-gnueabihf.tar.gz/sha512/167100a2d46e68462ef9a66915ced881d6358f05337bd38f2f77176f41cfd5be37e3c5226dd5d7d59147bd3e1aa7fb0893c1c81e9516134d3ab663b5752c4969 +OpenLibm.v0.8.5+0.armv7l-linux-musleabihf.tar.gz/md5/e8566719387984604f19dc5f9354a783 +OpenLibm.v0.8.5+0.armv7l-linux-musleabihf.tar.gz/sha512/532dd2b764fa15f7a838fb14cccafd2d4fe8fa4a132ea8394479a719c7aee11442f1b8a18e5d4a26ca820fa696d9d2afc7f5ec63dd96fa3b6763cea72b7026c3 +OpenLibm.v0.8.5+0.i686-linux-gnu.tar.gz/md5/9580d34e69d6067427b9c33db631cfd3 +OpenLibm.v0.8.5+0.i686-linux-gnu.tar.gz/sha512/46934f82791f69ac5f5da0dab7dcc6e3e9a4577c3bb529e9c0519c38f140c7b54517c55ff3579cd4ed4df68f0863e006aa98e51873f1dab452ce9f853996429a +OpenLibm.v0.8.5+0.i686-linux-musl.tar.gz/md5/66bfc9611d04c5d609e7824cb076d24b +OpenLibm.v0.8.5+0.i686-linux-musl.tar.gz/sha512/1bda2395d44c22aba3d1aab2b08ae06f763d3755037d454aa73f8e8134289a1ab5d65862bbc5a17a7a6b9f2918eb87e926b21527ddc4471e2ea20d605ba14e2d +OpenLibm.v0.8.5+0.i686-w64-mingw32.tar.gz/md5/0e97311b2f08b57d79085635f01ccced +OpenLibm.v0.8.5+0.i686-w64-mingw32.tar.gz/sha512/ae061ea406c06969332af58ed6fdfce2825326d771d30274d90775a1709b0361b7ca1dc7e6b0b76b93e4dd7a81d1842510a2c835251ee0a0978d6c839d96070e +OpenLibm.v0.8.5+0.powerpc64le-linux-gnu.tar.gz/md5/8ecfff7db76eee29591a654871e88855 +OpenLibm.v0.8.5+0.powerpc64le-linux-gnu.tar.gz/sha512/af03993b162316dd581f6ba5d1c23bca4c26cb22356ab229f326c42e111acbdf7ef45c9ad05894fe2d68794a63670cf89888653f788192a38b9255ce4bc72e28 +OpenLibm.v0.8.5+0.riscv64-linux-gnu.tar.gz/md5/69e06de135940666791c984941e9c4ad +OpenLibm.v0.8.5+0.riscv64-linux-gnu.tar.gz/sha512/2ac84deb7eb80a6a6237eff6fe861fd2907b3c95d1a76366dea062f3f35228dbc67aa40bd982e646508b4ff7cb6ef029111e2c0325039e60679800d6c6886be5 +OpenLibm.v0.8.5+0.x86_64-apple-darwin.tar.gz/md5/bd671ab9fe01835cab3e42e7cfa790fb +OpenLibm.v0.8.5+0.x86_64-apple-darwin.tar.gz/sha512/8bf2e66df17effc1e8778453904ffc20127f785bf096873289e8fdd8b17069ca844faffbd9f7621b87a7cb0a0051037eb9402360f2a03cf8794fbac8f7719777 +OpenLibm.v0.8.5+0.x86_64-linux-gnu.tar.gz/md5/df7fab134fbce3b625e9a82376f23e79 +OpenLibm.v0.8.5+0.x86_64-linux-gnu.tar.gz/sha512/64d07434e0db79833f84a2225838456eb9532617d377a776b3a534a908b1673bc4f890903f95350e4045e05c29539d993a18ecadeb879761e279ec3947f74390 +OpenLibm.v0.8.5+0.x86_64-linux-musl.tar.gz/md5/ebef6bb7651d116b397e035f39adfb1b +OpenLibm.v0.8.5+0.x86_64-linux-musl.tar.gz/sha512/de9036073e5dba2721b4119ecbbd21a0c9f75b65aff9392b7e88e464da35b97135d62404477441d0dadd3a2f8d49f1082291b35bf4b626fb1096d36d401980bf +OpenLibm.v0.8.5+0.x86_64-unknown-freebsd.tar.gz/md5/1115497539f00a37af18aa6516d52268 +OpenLibm.v0.8.5+0.x86_64-unknown-freebsd.tar.gz/sha512/71a2c06d141b3671fd220f2d88d72e845848b6d2b08a7b3a6c4bb1d5cc27cc450e1e681647bb583e7ed6375d5a70748401e95e61dc95d7808f33a9aa06755337 +OpenLibm.v0.8.5+0.x86_64-w64-mingw32.tar.gz/md5/b6b5335f4c83f7ebf0f74cf753358f00 +OpenLibm.v0.8.5+0.x86_64-w64-mingw32.tar.gz/sha512/e8351ddda305b757f337bb7ea26c441968843b23861676f0bdd7bcf83bb3969af790d4112307d3204eb87fac044dda9be305f349700ebe9ba2bfe3d6df24fde8 +openlibm-db24332879c320606c37f77fea165e6ecb49153c.tar.gz/md5/2375dd448e77e59152442a4b33abda01 +openlibm-db24332879c320606c37f77fea165e6ecb49153c.tar.gz/sha512/36054e7051990d04913f054a0542e2e104273f61308e9a442c2dab3dd392d40c03f264fbeca93c4296218eed85dad71028989a225088254013d752f4407d57ef diff --git a/deps/openlibm.version b/deps/openlibm.version index f35b291260380..16b17504430bc 100644 --- a/deps/openlibm.version +++ b/deps/openlibm.version @@ -2,6 +2,6 @@ OPENLIBM_JLL_NAME := OpenLibm ## source build -OPENLIBM_VER := 0.8.1 -OPENLIBM_BRANCH=v0.8.1 -OPENLIBM_SHA1=ae2d91698508701c83cab83714d42a1146dccf85 +OPENLIBM_VER := 0.8.5 +OPENLIBM_BRANCH=v0.8.5 +OPENLIBM_SHA1=db24332879c320606c37f77fea165e6ecb49153c diff --git a/stdlib/OpenLibm_jll/Project.toml b/stdlib/OpenLibm_jll/Project.toml index c5ba7fe77b651..431528ee3f400 100644 --- a/stdlib/OpenLibm_jll/Project.toml +++ b/stdlib/OpenLibm_jll/Project.toml @@ -1,6 +1,6 @@ name = "OpenLibm_jll" uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+4" +version = "0.8.5+0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" From 7d9b8899c2aedd8e0778571d1079b7d4fffb582c Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Mon, 31 Mar 2025 16:55:35 +0200 Subject: [PATCH 22/68] bump Pkg to latest v1.10 --- .../Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/md5 | 1 - .../Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/sha512 | 1 - .../Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/md5 | 1 + .../Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/md5 create mode 100644 deps/checksums/Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/sha512 diff --git a/deps/checksums/Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/md5 b/deps/checksums/Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/md5 deleted file mode 100644 index 4a0ebe571c2f4..0000000000000 --- a/deps/checksums/Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -7269046040e860f243cf379ad1b4291d diff --git a/deps/checksums/Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/sha512 b/deps/checksums/Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/sha512 deleted file mode 100644 index 46348fa21d5a3..0000000000000 --- a/deps/checksums/Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -31853a2d083a576ddacf2e0318fc5f3ca982b8b7ce2da88ce0fb20617ff2e4adcb3973bcf0d793d131f61104418da0471dd23f7912c65e406881d76f2e0b6914 diff --git a/deps/checksums/Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/md5 b/deps/checksums/Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/md5 new file mode 100644 index 0000000000000..f1a40d23aa48f --- /dev/null +++ b/deps/checksums/Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/md5 @@ -0,0 +1 @@ +3c84ff9961b0b0bba68446a6c1b9101b diff --git a/deps/checksums/Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/sha512 b/deps/checksums/Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/sha512 new file mode 100644 index 0000000000000..ec48ec9407c61 --- /dev/null +++ b/deps/checksums/Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/sha512 @@ -0,0 +1 @@ +5e82f3ae975f149e012eb0ba1ccf1032b51b1c939b2a71912914cbd65e9c2744a19443db04eaac6e7bfe39a24ee1b9865dbee05b62c7dbabcca5108b0ce192c6 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 1fa2db36f0cb9..9e1380e653cd2 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.10 -PKG_SHA1 = 0b3af4592d7158d4cba1132a9cc9df15e8e51706 +PKG_SHA1 = 2525c69000ea321c80eca26a3c4f2f0b1ba11e0e PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 13f02c7059861dbb567f7fad1c1aa4cf1dca7ade Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Mon, 31 Mar 2025 04:03:19 -0400 Subject: [PATCH 23/68] staticdata: Memoize `type_in_worklist` query (#57917) When pre-compiling `stdlib/` this cache has a 91% hit rate, so this seems fairly profitable. It also dramatically improves some pathological cases, a few of which have been hit in the wild (arguably due to inference bugs) Without this PR, this package takes exponentially long to pre-compile: ```julia function BigType(N) (N == 0) && return Nothing T = BigType(N-1) return Pair{T,T} end foo(::Type{T}) where T = T precompile(foo, (Type{BigType(40)},)) ``` For an in-the-wild test case hit by a customer, this reduces pre-compilation time from over an hour to just ~two and a half minutes. Resolves #53331. --- src/staticdata.c | 74 ++++++++++++++++++++++++++++-------------- src/staticdata_utils.c | 70 ++++++++++++++++++++++++--------------- 2 files changed, 93 insertions(+), 51 deletions(-) diff --git a/src/staticdata.c b/src/staticdata.c index a780add783d46..78d0d275a0ac3 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -89,6 +89,22 @@ External links: #include "valgrind.h" #include "julia_assert.h" +// This structure is used to store hash tables for the memoization +// of queries in staticdata.c (currently only `type_in_worklist`). +typedef struct { + htable_t type_in_worklist; +} jl_query_cache; + +static void init_query_cache(jl_query_cache *cache) +{ + htable_new(&cache->type_in_worklist, 0); +} + +static void destroy_query_cache(jl_query_cache *cache) +{ + htable_free(&cache->type_in_worklist); +} + #include "staticdata_utils.c" #include "precompile_utils.c" @@ -479,6 +495,7 @@ typedef struct { jl_array_t *link_ids_gctags; jl_array_t *link_ids_gvars; jl_array_t *link_ids_external_fnvars; + jl_query_cache *query_cache; jl_ptls_t ptls; htable_t callers_with_edges; jl_image_t *image; @@ -621,38 +638,37 @@ static int jl_needs_serialization(jl_serializer_state *s, jl_value_t *v) JL_NOTS return 1; } - -static int caching_tag(jl_value_t *v) JL_NOTSAFEPOINT +static int caching_tag(jl_value_t *v, jl_query_cache *query_cache) JL_NOTSAFEPOINT { if (jl_is_method_instance(v)) { jl_method_instance_t *mi = (jl_method_instance_t*)v; jl_value_t *m = mi->def.value; if (jl_is_method(m) && jl_object_in_image(m)) - return 1 + type_in_worklist(mi->specTypes); + return 1 + type_in_worklist(mi->specTypes, query_cache); } if (jl_is_datatype(v)) { jl_datatype_t *dt = (jl_datatype_t*)v; if (jl_is_tuple_type(dt) ? !dt->isconcretetype : dt->hasfreetypevars) return 0; // aka !is_cacheable from jltypes.c if (jl_object_in_image((jl_value_t*)dt->name)) - return 1 + type_in_worklist(v); + return 1 + type_in_worklist(v, query_cache); } jl_value_t *dtv = jl_typeof(v); if (jl_is_datatype_singleton((jl_datatype_t*)dtv)) { - return 1 - type_in_worklist(dtv); // these are already recached in the datatype in the image + return 1 - type_in_worklist(dtv, query_cache); // these are already recached in the datatype in the image } return 0; } -static int needs_recaching(jl_value_t *v) JL_NOTSAFEPOINT +static int needs_recaching(jl_value_t *v, jl_query_cache *query_cache) JL_NOTSAFEPOINT { - return caching_tag(v) == 2; + return caching_tag(v, query_cache) == 2; } -static int needs_uniquing(jl_value_t *v) JL_NOTSAFEPOINT +static int needs_uniquing(jl_value_t *v, jl_query_cache *query_cache) JL_NOTSAFEPOINT { assert(!jl_object_in_image(v)); - return caching_tag(v) == 1; + return caching_tag(v, query_cache) == 1; } static void record_field_change(jl_value_t **addr, jl_value_t *newval) JL_NOTSAFEPOINT @@ -738,7 +754,7 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ // ensure all type parameters are recached jl_queue_for_serialization_(s, (jl_value_t*)dt->parameters, 1, 1); jl_value_t *singleton = dt->instance; - if (singleton && needs_uniquing(singleton)) { + if (singleton && needs_uniquing(singleton, s->query_cache)) { assert(jl_needs_serialization(s, singleton)); // should be true, since we visited dt // do not visit dt->instance for our template object as it leads to unwanted cycles here // (it may get serialized from elsewhere though) @@ -749,7 +765,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)) { + if (needs_uniquing(v, s->query_cache)) { // we only need 3 specific fields of this (the rest are not used) jl_queue_for_serialization(s, mi->def.value); jl_queue_for_serialization(s, mi->specTypes); @@ -767,7 +783,7 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ record_field_change((jl_value_t**)&mi->cache, NULL); } else { - assert(!needs_recaching(v)); + assert(!needs_recaching(v, s->query_cache)); } // n.b. opaque closures cannot be inspected and relied upon like a // normal method since they can get improperly introduced by generated @@ -901,9 +917,9 @@ static void jl_queue_for_serialization_(jl_serializer_state *s, jl_value_t *v, i // Items that require postorder traversal must visit their children prior to insertion into // the worklist/serialization_order (and also before their first use) if (s->incremental && !immediate) { - if (jl_is_datatype(t) && needs_uniquing(v)) + if (jl_is_datatype(t) && needs_uniquing(v, s->query_cache)) immediate = 1; - if (jl_is_datatype_singleton((jl_datatype_t*)t) && needs_uniquing(v)) + if (jl_is_datatype_singleton((jl_datatype_t*)t) && needs_uniquing(v, s->query_cache)) immediate = 1; } @@ -1067,7 +1083,7 @@ static uintptr_t _backref_id(jl_serializer_state *s, jl_value_t *v, jl_array_t * static void record_uniquing(jl_serializer_state *s, jl_value_t *fld, uintptr_t offset) JL_NOTSAFEPOINT { - if (s->incremental && jl_needs_serialization(s, fld) && needs_uniquing(fld)) { + if (s->incremental && jl_needs_serialization(s, fld) && needs_uniquing(fld, s->query_cache)) { if (jl_is_datatype(fld) || jl_is_datatype_singleton((jl_datatype_t*)jl_typeof(fld))) arraylist_push(&s->uniquing_types, (void*)(uintptr_t)offset); else if (jl_is_method_instance(fld)) @@ -1211,7 +1227,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED 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)) + if (s->incremental && jl_needs_serialization(s, (jl_value_t*)t) && needs_uniquing((jl_value_t*)t, s->query_cache)) 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); @@ -1222,7 +1238,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED 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 (needs_uniquing(v, s->query_cache)) { if (jl_is_method_instance(v)) { assert(f == s->s); jl_method_instance_t *mi = (jl_method_instance_t*)v; @@ -1243,7 +1259,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED assert(jl_is_datatype_singleton(t) && "unreachable"); } } - else if (needs_recaching(v)) { + else if (needs_recaching(v, s->query_cache)) { arraylist_push(jl_is_datatype(v) ? &s->fixup_types : &s->fixup_objs, (void*)reloc_offset); } else if (jl_typetagis(v, jl_binding_type)) { @@ -1606,7 +1622,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED } } void *superidx = ptrhash_get(&serialization_order, dt->super); - if (s->incremental && superidx != HT_NOTFOUND && (char*)superidx - 1 - (char*)HT_NOTFOUND > item && needs_uniquing((jl_value_t*)dt->super)) + if (s->incremental && superidx != HT_NOTFOUND && (char*)superidx - 1 - (char*)HT_NOTFOUND > item && needs_uniquing((jl_value_t*)dt->super, s->query_cache)) arraylist_push(&s->uniquing_super, dt->super); } else if (jl_is_typename(v)) { @@ -2351,7 +2367,8 @@ JL_DLLEXPORT jl_value_t *jl_as_global_root(jl_value_t *val JL_MAYBE_UNROOTED) static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *newly_inferred, uint64_t worklist_key, /* outputs */ 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_array_t **method_roots_list, jl_array_t **ext_targets, jl_array_t **edges, + jl_query_cache *query_cache) { // extext_methods: [method1, ...], worklist-owned "extending external" methods added to functions owned by modules outside the worklist // ext_targets: [invokesig1, callee1, matches1, ...] non-worklist callees of worklist-owned methods @@ -2362,7 +2379,7 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new assert(edges_map == NULL); // Save the inferred code from newly inferred, external methods - *new_specializations = queue_external_cis(newly_inferred); + *new_specializations = queue_external_cis(newly_inferred, query_cache); // Collect method extensions and edges data JL_GC_PUSH1(&edges_map); @@ -2401,7 +2418,8 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new 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 + jl_array_t *ext_targets, jl_array_t *edges, + jl_query_cache *query_cache) JL_GC_DISABLED { htable_new(&field_replace, 0); // strip metadata and IR when requested @@ -2428,6 +2446,7 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, ios_mem(&gvar_record, 0); ios_mem(&fptr_record, 0); jl_serializer_state s = {0}; + s.query_cache = query_cache; s.incremental = !(worklist == NULL); s.s = &sysimg; s.const_data = &const_data; @@ -2743,12 +2762,15 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli int64_t datastartpos = 0; JL_GC_PUSH6(&mod_array, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges); + jl_query_cache query_cache; + init_query_cache(&query_cache); + if (worklist) { mod_array = jl_get_loaded_modules(); // __toplevel__ modules loaded in this session (from Base.loaded_modules_array) // Generate _native_data` if (_native_data != NULL) { jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist), - &extext_methods, &new_specializations, NULL, NULL, NULL); + &extext_methods, &new_specializations, NULL, NULL, NULL, &query_cache); jl_precompile_toplevel_module = (jl_module_t*)jl_array_ptr_ref(worklist, jl_array_len(worklist)-1); *_native_data = jl_precompile_worklist(worklist, extext_methods, new_specializations); jl_precompile_toplevel_module = NULL; @@ -2777,7 +2799,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli if (worklist) { htable_new(&relocatable_ext_cis, 0); jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist), - &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges); + &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges, &query_cache); if (!emit_split) { write_int32(f, 0); // No clone_targets write_padding(f, LLT_ALIGN(ios_pos(f), JL_CACHE_BYTE_ALIGNMENT) - ios_pos(f)); @@ -2789,7 +2811,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, mod_array, 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, &query_cache); if (_native_data != NULL) native_functions = NULL; if (worklist) @@ -2820,6 +2842,8 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli } } + destroy_query_cache(&query_cache); + JL_GC_POP(); *s = f; if (emit_split) diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index ce2c9fa70d67c..09cd328e694f8 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -103,62 +103,80 @@ JL_DLLEXPORT void jl_push_newly_inferred(jl_value_t* ci) JL_UNLOCK(&newly_inferred_mutex); } - // 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 -static int type_in_worklist(jl_value_t *v) JL_NOTSAFEPOINT +static int type_in_worklist(jl_value_t *v, jl_query_cache *cache) JL_NOTSAFEPOINT { if (jl_object_in_image(v)) return 0; // fast-path for rejection + + void *cached = HT_NOTFOUND; + if (cache != NULL) + cached = ptrhash_get(&cache->type_in_worklist, v); + + // fast-path for memoized results + if (cached != HT_NOTFOUND) + return cached == v; + + int result = 0; if (jl_is_uniontype(v)) { jl_uniontype_t *u = (jl_uniontype_t*)v; - return type_in_worklist(u->a) || - type_in_worklist(u->b); + result = type_in_worklist(u->a, cache) || + type_in_worklist(u->b, cache); } else if (jl_is_unionall(v)) { jl_unionall_t *ua = (jl_unionall_t*)v; - return type_in_worklist((jl_value_t*)ua->var) || - type_in_worklist(ua->body); + result = type_in_worklist((jl_value_t*)ua->var, cache) || + type_in_worklist(ua->body, cache); } else if (jl_is_typevar(v)) { jl_tvar_t *tv = (jl_tvar_t*)v; - return type_in_worklist(tv->lb) || - type_in_worklist(tv->ub); + result = type_in_worklist(tv->lb, cache) || + type_in_worklist(tv->ub, cache); } else if (jl_is_vararg(v)) { jl_vararg_t *tv = (jl_vararg_t*)v; - if (tv->T && type_in_worklist(tv->T)) - return 1; - if (tv->N && type_in_worklist(tv->N)) - return 1; + result = ((tv->T && type_in_worklist(tv->T, cache)) || + (tv->N && type_in_worklist(tv->N, cache))); } else if (jl_is_datatype(v)) { jl_datatype_t *dt = (jl_datatype_t*)v; - if (!jl_object_in_image((jl_value_t*)dt->name)) - return 1; - jl_svec_t *tt = dt->parameters; - size_t i, l = jl_svec_len(tt); - for (i = 0; i < l; i++) - if (type_in_worklist(jl_tparam(dt, i))) - return 1; + if (!jl_object_in_image((jl_value_t*)dt->name)) { + result = 1; + } + else { + jl_svec_t *tt = dt->parameters; + size_t i, l = jl_svec_len(tt); + for (i = 0; i < l; i++) { + if (type_in_worklist(jl_tparam(dt, i), cache)) { + result = 1; + break; + } + } + } } else { - return type_in_worklist(jl_typeof(v)); + return type_in_worklist(jl_typeof(v), cache); } - return 0; + + // Memoize result + if (cache != NULL) + ptrhash_put(&cache->type_in_worklist, (void*)v, result ? (void*)v : NULL); + + return result; } // When we infer external method instances, ensure they link back to the // package. Otherwise they might be, e.g., for external macros. // Implements Tarjan's SCC (strongly connected components) algorithm, simplified to remove the count variable -static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited, arraylist_t *stack) +static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited, arraylist_t *stack, jl_query_cache *query_cache) { jl_module_t *mod = mi->def.module; if (jl_is_method(mod)) mod = ((jl_method_t*)mod)->module; assert(jl_is_module(mod)); - if (mi->precompiled || !jl_object_in_image((jl_value_t*)mod) || type_in_worklist(mi->specTypes)) { + if (mi->precompiled || !jl_object_in_image((jl_value_t*)mod) || type_in_worklist(mi->specTypes, query_cache)) { return 1; } if (!mi->backedges) { @@ -181,7 +199,7 @@ static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited, 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); + int child_found = has_backedge_to_worklist(be, visited, stack, query_cache); if (child_found == 1 || child_found == 2) { // found what we were looking for, so terminate early found = 1; @@ -224,7 +242,7 @@ static int is_relocatable_ci(htable_t *relocatable_ext_cis, jl_code_instance_t * // 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) +static jl_array_t *queue_external_cis(jl_array_t *list, jl_query_cache *query_cache) { if (list == NULL) return NULL; @@ -245,7 +263,7 @@ static jl_array_t *queue_external_cis(jl_array_t *list) 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)) { - int found = has_backedge_to_worklist(mi, &visited, &stack); + int found = has_backedge_to_worklist(mi, &visited, &stack, query_cache); assert(found == 0 || found == 1 || found == 2); assert(stack.len == 0); if (found == 1 && ci->max_world == ~(size_t)0) { From 2d5d191e2e601531557565d45965b834bc21f07a Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Sun, 6 Apr 2025 12:36:26 +0800 Subject: [PATCH 24/68] typeintersect: fix triangular vars handling outside constructor (#58018) also add more fast path. --- src/subtype.c | 15 +++++++-------- test/subtype.jl | 15 +++++++++++++-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index db64d3b31d382..cb3408b54e925 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1354,7 +1354,7 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) x = pick_union_element(x, e, 0); } if (jl_is_uniontype(y)) { - if (x == ((jl_uniontype_t*)y)->a || x == ((jl_uniontype_t*)y)->b) + if (obviously_in_union(y, x)) return 1; if (jl_is_unionall(x)) return subtype_unionall(y, (jl_unionall_t*)x, e, 0, param); @@ -2484,9 +2484,6 @@ static jl_value_t *intersect_aside(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, static jl_value_t *intersect_union(jl_value_t *x, jl_uniontype_t *u, jl_stenv_t *e, int8_t R, int param) { - // band-aid for #56040 - if (!jl_is_uniontype(x) && obviously_in_union((jl_value_t *)u, x)) - return x; if (param == 2 || (!jl_has_free_typevars(x) && !jl_has_free_typevars((jl_value_t*)u))) { jl_value_t *a=NULL, *b=NULL; JL_GC_PUSH2(&a, &b); @@ -2616,7 +2613,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) { - if (x == jl_bottom_type || y == (jl_value_t*)jl_any_type) + if (x == jl_bottom_type || y == (jl_value_t*)jl_any_type || obviously_in_union(y, x)) return 1; int8_t *rs = (int8_t*)alloca(current_env_length(e)); jl_varbinding_t *v = e->vars; @@ -2739,7 +2736,7 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int 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) { + if (bb->constraintkind == 1 || (e->triangular && param == 1)) { if (e->triangular && check_unsat_bound(ub, b, e)) return jl_bottom_type; set_bound(&bb->ub, ub, b, e); @@ -3914,12 +3911,14 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa if (jl_subtype(y, x)) return y; } if (jl_is_uniontype(x)) { - if (y == ((jl_uniontype_t*)x)->a || y == ((jl_uniontype_t*)x)->b) + if (obviously_in_union(x, y)) return y; + if (jl_is_uniontype(y) && obviously_in_union(y, x)) + return x; return intersect_union(y, (jl_uniontype_t*)x, e, 0, param); } if (jl_is_uniontype(y)) { - if (x == ((jl_uniontype_t*)y)->a || x == ((jl_uniontype_t*)y)->b) + if (obviously_in_union(y, x)) return x; if (jl_is_unionall(x) && (jl_has_free_typevars(x) || jl_has_free_typevars(y))) return intersect_unionall(y, (jl_unionall_t*)x, e, 0, param); diff --git a/test/subtype.jl b/test/subtype.jl index d371d18732d9b..d59a93ba8016c 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2085,8 +2085,7 @@ let A = Tuple{Any, Type{Ref{_A}} where _A}, I = typeintersect(A, B) @test I != Union{} @test Tuple{Type{Ref{Integer}}, Type{Ref{Integer}}} <: I - # TODO: this intersection result seems too wide (I == B) ? - @test_broken !<:(Tuple{Type{Int}, Type{Int}}, I) + @test !<:(Tuple{Type{Int}, Type{Int}}, I) end @testintersect(Tuple{Type{T}, T} where T<:(Tuple{Vararg{_A, _B}} where _B where _A), @@ -2706,3 +2705,15 @@ end Pair{N, T} where {N,NTuple{N,Int}<:T<:Tuple{Int,Vararg{Int}}}, !Union{} ) + +#issue 57852 +@testintersect( + Tuple{Type{T}, Type{<:F}, Type{<:F}} where {T, F<:Union{String, T}}, + Tuple{Type{Complex{T}} where T, Type{Complex{T}} where T, Type{String}}, + Tuple{Type{Complex{T}}, Type{Complex{T}}, Type{String}} where T +) +@testintersect( + Tuple{Type{T}, Type{<:Union{F, Nothing}}, Type{<:Union{F, Nothing}}} where {T, F<:Union{String, T}}, + Tuple{Type{Complex{T}} where T, Type{Complex{T}} where T, Type{String}}, + Tuple{Type{Complex{T}}, Type{Complex{T}}, Type{String}} where T +) From 648c9bcb2f6a5ad96e6aae2ea8e2a6364cfe82c8 Mon Sep 17 00:00:00 2001 From: gbaraldi Date: Mon, 12 May 2025 16:32:55 -0300 Subject: [PATCH 25/68] Backport of 088bb90 (Fix removal of globals with addrspaces in removeAddrspaces (#58322)) --- src/llvm-remove-addrspaces.cpp | 4 ++-- test/llvmpasses/remove-addrspaces.ll | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/llvm-remove-addrspaces.cpp b/src/llvm-remove-addrspaces.cpp index a72013eaeb65f..97df6957b2708 100644 --- a/src/llvm-remove-addrspaces.cpp +++ b/src/llvm-remove-addrspaces.cpp @@ -271,7 +271,7 @@ bool removeAddrspaces(Module &M, AddrspaceRemapFunction ASRemapper) Name, (GlobalVariable *)nullptr, GV->getThreadLocalMode(), - GV->getType()->getAddressSpace()); + cast(TypeRemapper.remapType(GV->getType()))->getAddressSpace()); NGV->copyAttributesFrom(GV); VMap[GV] = NGV; } @@ -291,7 +291,7 @@ bool removeAddrspaces(Module &M, AddrspaceRemapFunction ASRemapper) auto *NGA = GlobalAlias::create( TypeRemapper.remapType(GA->getValueType()), - GA->getType()->getPointerAddressSpace(), + cast(TypeRemapper.remapType(GA->getType()))->getAddressSpace(), GA->getLinkage(), Name, &M); diff --git a/test/llvmpasses/remove-addrspaces.ll b/test/llvmpasses/remove-addrspaces.ll index 472f49d1ac03e..d25dd69748d86 100644 --- a/test/llvmpasses/remove-addrspaces.ll +++ b/test/llvmpasses/remove-addrspaces.ll @@ -13,6 +13,10 @@ ; TYPED-SAME: {}* ({}***, {}*, [1 x i64]*)* null ; OPAQUE-SAME: ptr null +; COM: check that the addrspace of the global itself is removed +; OPAQUE: @ejl_enz_runtime_exc = external global {} +@ejl_enz_runtime_exc = external addrspace(10) global {} + define i64 @getindex({} addrspace(10)* nonnull align 16 dereferenceable(40)) { ; CHECK-LABEL: @getindex top: @@ -123,6 +127,12 @@ define void @byval_type([1 x {} addrspace(10)*] addrspace(11)* byval([1 x {} add ret void } +define private fastcc void @diffejulia__mapreduce_97() { +L6: +; OPAQUE: store atomic ptr @ejl_enz_runtime_exc, ptr null unordered + store atomic {} addrspace(10)* @ejl_enz_runtime_exc, {} addrspace(10)* addrspace(10)* null unordered, align 8 + unreachable +} ; COM: check that function attributes are preserved on declarations too declare void @convergent_function() #0 From 9107c8bc3552682acea6748dc63cb383d9dd38d7 Mon Sep 17 00:00:00 2001 From: Martijn Visser Date: Tue, 25 Mar 2025 16:02:59 +0100 Subject: [PATCH 26/68] Don't error when initializing LibGit2 with CA roots path (#56924) When SSL_CERT_FILE or SSL_CERT_DIR is set, it is [impossible to set this location](https://github.com/libgit2/libgit2/blob/4dcdb64c6844d76776745cdc25071a72c1af84d6/src/libgit2/settings.c#L206-L222) in LibGit2_jll on Apple and Windows because [it isn't built with support for that](https://github.com/JuliaPackaging/Yggdrasil/blob/7123a60a68102ba6cd953e13a4e45845dc37fd82/L/LibGit2/build_tarballs.jl#L67). Until now we've errored out with a message telling users to set JULIA_SSL_CA_ROOTS_PATH to an empty string, which is a somewhat problematic workaround because the Windows environment variables UI doesn't allow empty values, and [setting it to an empty string from PowerShell unsets it](https://discourse.julialang.org/t/how-to-fix-ssl-cert-issues-in-pkg/115495/7?u=visr). This PR changes the behavior to allow this expected error. Variables like SSL_CERT_FILE are for instance [set by the Conda OpenSSL package on environment activation](https://github.com/conda-forge/openssl-feedstock/blob/83b5e2a793bc95d19e6cc2d9d28068f1a6ff6b79/recipe/activate-win.ps1) used by e.g. Python, ensuring many people cannot use Pkg operations that use LibGit2, like `dev Example`, `add Example#master`. See more user reports [on Discourse](https://discourse.julialang.org/search?q=JULIA_SSL_CA_ROOTS_PATH). Together with https://github.com/JuliaLang/NetworkOptions.jl/pull/37 this should improve the experience of users trying out Julia from a Conda environment. This should also be fine to backport. (cherry picked from commit 7fa969ab3ce9c1ab6b8b67969b1b8de04a671094) --- stdlib/LibGit2/src/LibGit2.jl | 16 ++++++---------- stdlib/LibGit2/test/bad_ca_roots.jl | 26 +++++++++++++++----------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/stdlib/LibGit2/src/LibGit2.jl b/stdlib/LibGit2/src/LibGit2.jl index 13b78c1046490..1d1874d55e992 100644 --- a/stdlib/LibGit2/src/LibGit2.jl +++ b/stdlib/LibGit2/src/LibGit2.jl @@ -1005,24 +1005,20 @@ function set_ssl_cert_locations(cert_loc) else # files, /dev/null, non-existent paths, etc. cert_file = cert_loc end - ret = @ccall libgit2.git_libgit2_opts( + ret = @ccall libgit2.git_libgit2_opts( Consts.SET_SSL_CERT_LOCATIONS::Cint; cert_file::Cstring, cert_dir::Cstring)::Cint ret >= 0 && return ret + # On macOS and Windows LibGit2_jll is built without a TLS backend that supports + # certificate locations; don't throw on this expected error so we allow certificate + # location environment variables to be set for other purposes. + # We still try doing so to support other LibGit2 builds. err = Error.GitError(ret) err.class == Error.SSL && err.msg == "TLS backend doesn't support certificate locations" || throw(err) - var = nothing - for v in NetworkOptions.CA_ROOTS_VARS - haskey(ENV, v) && (var = v) - end - @assert var !== nothing # otherwise we shouldn't be here - msg = """ - Your Julia is built with a SSL/TLS engine that libgit2 doesn't know how to configure to use a file or directory of certificate authority roots, but your environment specifies one via the $var variable. If you believe your system's root certificates are safe to use, you can `export JULIA_SSL_CA_ROOTS_PATH=""` in your environment to use those instead. - """ - throw(Error.GitError(err.class, err.code, chomp(msg))) + return ret end """ diff --git a/stdlib/LibGit2/test/bad_ca_roots.jl b/stdlib/LibGit2/test/bad_ca_roots.jl index 4882065167bdb..4caed4ed90beb 100644 --- a/stdlib/LibGit2/test/bad_ca_roots.jl +++ b/stdlib/LibGit2/test/bad_ca_roots.jl @@ -12,20 +12,24 @@ const CAN_SET_CA_ROOTS_PATH = !Sys.isapple() && !Sys.iswindows() # Given this is a sub-processed test file, not using @testsets avoids # leaking the report print into the Base test runner report begin # empty CA roots file - # these fail for different reasons on different platforms: - # - on Apple & Windows you cannot set the CA roots path location - # - on Linux & FreeBSD you you can but these are invalid files + # different behavior on different platforms: + # - on Apple & Windows you cannot set the CA roots path location; don't error + # - on Linux & FreeBSD you can but these are invalid files + ENV["JULIA_SSL_CA_ROOTS_PATH"] = "/dev/null" - @test_throws LibGit2.GitError LibGit2.ensure_initialized() + if CAN_SET_CA_ROOTS_PATH + @test_throws LibGit2.GitError LibGit2.ensure_initialized() + else + @test LibGit2.ensure_initialized() === nothing + end + ENV["JULIA_SSL_CA_ROOTS_PATH"] = tempname() - @test_throws LibGit2.GitError LibGit2.ensure_initialized() - # test that it still fails if called a second time - @test_throws LibGit2.GitError LibGit2.ensure_initialized() - if !CAN_SET_CA_ROOTS_PATH - # test that this doesn't work on macOS & Windows - ENV["JULIA_SSL_CA_ROOTS_PATH"] = NetworkOptions.bundled_ca_roots() + if CAN_SET_CA_ROOTS_PATH + @test_throws LibGit2.GitError LibGit2.ensure_initialized() + # test that it still fails if called a second time @test_throws LibGit2.GitError LibGit2.ensure_initialized() - delete!(ENV, "JULIA_SSL_CA_ROOTS_PATH") + else + @test LibGit2.ensure_initialized() === nothing @test LibGit2.ensure_initialized() === nothing end end From e2a20004ab2ccef15ac0f26fdffef69c8c1f5d57 Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Tue, 1 Apr 2025 09:05:56 +0200 Subject: [PATCH 27/68] `append_c_digits`: typeassert `Int` to improve inference (#57950) Makes the sysimage more resistant to method invalidation, when defining a new `Integer` subtype with a right bitshift method. (cherry picked from commit 69a22cf4bcaaf54e9bf08f2d4ed9e4e77f0ad1e4) --- base/intfuncs.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 8003dd6e0bfd2..87c8c0c491662 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -763,7 +763,7 @@ function append_c_digits(olength::Int, digits::Unsigned, buf, pos::Int) while i >= 2 d, c = divrem(digits, 0x64) digits = oftype(digits, d) - @inbounds d100 = _dec_d100[(c % Int) + 1] + @inbounds d100 = _dec_d100[(c % Int)::Int + 1] @inbounds buf[pos + i - 2] = d100 % UInt8 @inbounds buf[pos + i - 1] = (d100 >> 0x8) % UInt8 i -= 2 From 718283705b2783a8bf3b2c93a2b8791997df0f96 Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Fri, 4 Apr 2025 04:13:40 +0200 Subject: [PATCH 28/68] fix `nextpow`, `prevpow` for types without `typemax` (#49669) Without this change `prevpow` and `nextpow` fail for, e.g., `BigInt`; incorrectly throwing a `MethodError`. For example, calls like `prevpow(3, big"10")` or `nextpow(3, big"10")` fail. The issue is that the code incorrectly assumes the existence of a `typemax` method. Another issue was a missing promote for the arguments of a `mul_with_overflow` call. Fixes #57906 (cherry picked from commit 3627a85749e4aa8197edb56dd93d1b21fd54dbe8) --- base/intfuncs.jl | 8 +++++--- test/intfuncs.jl | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 87c8c0c491662..c81d3085642b0 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -488,7 +488,8 @@ function nextpow(a::Real, x::Real) n = ceil(Integer,log(a, x)) # round-off error of log can go either direction, so need some checks p = a^(n-1) - x > typemax(p) && throw(DomainError(x,"argument is beyond the range of type of the base")) + hastypemax(typeof(p)) && x > typemax(p) && + throw(DomainError(x,"argument is beyond the range of type of the base")) p >= x && return p wp = a^n wp > p || throw(OverflowError("result is beyond the range of type of the base")) @@ -529,9 +530,10 @@ function prevpow(a::T, x::Real) where T <: Real n = floor(Integer,log(a, x)) # round-off error of log can go either direction, so need some checks p = a^n - x > typemax(p) && throw(DomainError(x,"argument is beyond the range of type of the base")) + hastypemax(typeof(p)) && x > typemax(p) && + throw(DomainError(x,"argument is beyond the range of type of the base")) if a isa Integer - wp, overflow = mul_with_overflow(a, p) + wp, overflow = mul_with_overflow(promote(a, p)...) wp <= x && !overflow && return wp else wp = a^(n+1) diff --git a/test/intfuncs.jl b/test/intfuncs.jl index ceaac235a3da9..1cc6705812927 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -278,6 +278,14 @@ end end @testset "nextpow/prevpow" begin + fs = (prevpow, nextpow) + types = (Int8, BigInt, BigFloat) + for f ∈ fs, P ∈ types, R ∈ types, p ∈ 1:20, r ∈ 2:5 + q = P(p) + n = R(r) + @test f(r, p) == f(n, q) + end + @test nextpow(2, 3) == 4 @test nextpow(2, 4) == 4 @test nextpow(2, 7) == 8 @@ -291,7 +299,14 @@ end @test prevpow(10, 101.0) === 100 @test prevpow(10.0, 101) === 100.0 @test_throws DomainError prevpow(0, 3) - @test_throws DomainError prevpow(0, 3) + @test_throws DomainError prevpow(3, 0) + + # "argument is beyond the range of type of the base" + @test_throws DomainError prevpow(Int8(3), 243) + @test_throws DomainError nextpow(Int8(3), 243) + + # "result is beyond the range of type of the base" + @test_throws OverflowError nextpow(Int8(3), 82) end @testset "ndigits/ndigits0z" begin From dd78704cb16ae5cad1b9f73098105e64883b7688 Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Sun, 13 Apr 2025 08:08:55 +0200 Subject: [PATCH 29/68] fix zero-dimensional `reverse!` (#58086) Also changed the check to compare against the tuple argument, instead of against the method static parameter for the tuple length. Should be better when the length isn't known at compile time. Fixes #58085 (cherry picked from commit b265dbac3ef8d1f8f7d779044f458a3c5e2b4689) --- base/arraymath.jl | 2 +- test/arrayops.jl | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/base/arraymath.jl b/base/arraymath.jl index 62dc3772e4938..53a7d132a2c0c 100644 --- a/base/arraymath.jl +++ b/base/arraymath.jl @@ -72,12 +72,12 @@ _reverse!(A::AbstractArray{<:Any,N}, ::Colon) where {N} = _reverse!(A, ntuple(id _reverse!(A, dim::Integer) = _reverse!(A, (Int(dim),)) _reverse!(A, dims::NTuple{M,Integer}) where {M} = _reverse!(A, Int.(dims)) function _reverse!(A::AbstractArray{<:Any,N}, dims::NTuple{M,Int}) where {N,M} + dims === () && return A # nothing to reverse dimrev = ntuple(k -> k in dims, Val{N}()) # boolean tuple indicating reversed dims if N < M || M != sum(dimrev) throw(ArgumentError("invalid dimensions $dims in reverse!")) end - M == 0 && return A # nothing to reverse # swapping loop only needs to traverse ≈half of the array halfsz = ntuple(k -> k == dims[1] ? size(A,k) ÷ 2 : size(A,k), Val{N}()) diff --git a/test/arrayops.jl b/test/arrayops.jl index ba02847094b0e..27867080b8c21 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1685,6 +1685,12 @@ end end end +@testset "reverse zero dims" begin + a = fill(3) + @test a == reverse(a) + @test a === reverse!(a) +end + @testset "isdiag, istril, istriu" begin # Scalar @test isdiag(3) From 368516b9fd7500576ae01de6a7e78aea297a5cee Mon Sep 17 00:00:00 2001 From: adienes <51664769+adienes@users.noreply.github.com> Date: Sat, 19 Apr 2025 09:05:09 -0400 Subject: [PATCH 30/68] Switch from segfault to `zip` behavior for mismatched indices in `map!` (#56673) (cherry picked from commit 0947114d9d443e14c751ac40c9b2d8c2245d045e) --- base/abstractarray.jl | 19 +++++++++++++------ test/abstractarray.jl | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 52d3335a37426..bc98ebdac5db9 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3335,12 +3335,19 @@ function ith_all(i, as) end function map_n!(f::F, dest::AbstractArray, As) where F - idxs1 = LinearIndices(As[1]) - @boundscheck LinearIndices(dest) == idxs1 && all(x -> LinearIndices(x) == idxs1, As) - for i = idxs1 - @inbounds I = ith_all(i, As) - val = f(I...) - @inbounds dest[i] = val + idxs = LinearIndices(dest) + if all(x -> LinearIndices(x) == idxs, As) + for i in idxs + @inbounds as = ith_all(i, As) + val = f(as...) + @inbounds dest[i] = val + end + else + for (i, Is...) in zip(eachindex(dest), map(eachindex, As)...) + as = ntuple(j->getindex(As[j], Is[j]), length(As)) + val = f(as...) + dest[i] = val + end end return dest end diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 8d0c83897611b..e7469a7a331ed 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -835,6 +835,26 @@ include("generic_map_tests.jl") generic_map_tests(map, map!) @test_throws ArgumentError map!(-, [1]) +@testset "#30624" begin + ### unstructured + @test map!(+, ones(3), ones(3), ones(3), [1]) == [3, 1, 1] + @test map!(+, ones(3), [1], ones(3), ones(3)) == [3, 1, 1] + @test map!(+, [1], [1], [], []) == [1] + @test map!(+, [[1]], [1], [], []) == [[1]] + + # TODO: decide if input axes & lengths should be validated + # @test_throws BoundsError map!(+, ones(1), ones(2)) + # @test_throws BoundsError map!(+, ones(1), ones(2, 2)) + + @test map!(+, ones(3), view(ones(2, 3), 1:2, 2:3), ones(3)) == [2, 2, 2] + @test map!(+, ones(3), ones(2, 2), ones(3)) == [2, 2, 2] + + ### structured (all mapped arguments are <:AbstractArray equal ndims > 1) + @test map!(+, ones(4), ones(2, 2), ones(2, 2)) == [2, 2, 2, 2] + @test map!(+, ones(4), ones(2, 2), ones(1, 2)) == [2, 2, 1, 1] + # @test_throws BoundsError map!(+, ones(3), ones(2, 2), ones(2, 2)) +end + test_UInt_indexing(TestAbstractArray) test_13315(TestAbstractArray) test_checksquare() From 11fa9ccf11cfeffcc41d8e64406d399b24f35d40 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Sun, 20 Apr 2025 18:48:32 +0800 Subject: [PATCH 31/68] =?UTF-8?q?subtype:=20save=20some=20union=20stack=20?= =?UTF-8?q?space=20for=20=E2=88=83=20free=20cases.=20(#58159)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and avoid eager UnionAll unwrapping to hit more fast path. close #58129 (test passed locally) close #56350 (MWE returns `Tuple{Any, Any, Vararg}` now.) (cherry picked from commit 334c3167e853daeb11e5121d2041de657e175726) --- src/subtype.c | 34 +++++++++++++++++++++++++--------- test/subtype.jl | 14 ++++++++++++++ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index cb3408b54e925..b6ce6c94b6bee 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -128,7 +128,7 @@ static jl_varbinding_t *lookup(jl_stenv_t *e, jl_tvar_t *v) JL_GLOBALLY_ROOTED J static int statestack_get(jl_unionstate_t *st, int i) JL_NOTSAFEPOINT { - assert(i >= 0 && i <= 32767); // limited by the depth bit. + assert(i >= 0 && i < 32767); // limited by the depth bit. // get the `i`th bit in an array of 32-bit words jl_bits_stack_t *stack = &st->stack; while (i >= sizeof(stack->data) * 8) { @@ -142,7 +142,7 @@ static int statestack_get(jl_unionstate_t *st, int i) JL_NOTSAFEPOINT static void statestack_set(jl_unionstate_t *st, int i, int val) JL_NOTSAFEPOINT { - assert(i >= 0 && i <= 32767); // limited by the depth bit. + assert(i >= 0 && i < 32767); // limited by the depth bit. jl_bits_stack_t *stack = &st->stack; while (i >= sizeof(stack->data) * 8) { if (__unlikely(stack->next == NULL)) { @@ -1409,11 +1409,14 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) } if (jl_is_unionall(y)) { jl_varbinding_t *xb = lookup(e, (jl_tvar_t*)x); - if (xb == NULL ? !e->ignore_free : !xb->right) { + jl_value_t *xub = xb == NULL ? ((jl_tvar_t *)x)->ub : xb->ub; + if ((xb == NULL ? !e->ignore_free : !xb->right) && xub != y) { // 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}`: + // note: if xub == y we'd better try `subtype_var` as `subtype_left_var` + // hit `==` based fast path. return subtype_unionall(x, (jl_unionall_t*)y, e, 1, param); } } @@ -1551,6 +1554,8 @@ static int has_exists_typevar(jl_value_t *x, jl_stenv_t *e) JL_NOTSAFEPOINT return env != NULL && jl_has_bound_typevars(x, env); } +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) { int16_t oldRmore = e->Runions.more; @@ -1564,7 +1569,18 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t return jl_subtype(x, y); int has_exists = (!kindx && has_exists_typevar(x, e)) || (!kindy && has_exists_typevar(y, e)); - if (has_exists && (is_exists_typevar(x, e) != is_exists_typevar(y, e))) { + if (!has_exists) { + // We can use ∀_∃_subtype safely for ∃ free inputs. + // This helps to save some bits in union stack. + 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; + sub = forall_exists_subtype(x, y, e, param); + pop_unionstate(&e->Runions, &oldRunions); + return sub; + } + if (is_exists_typevar(x, e) != is_exists_typevar(y, e)) { e->Lunions.used = 0; while (1) { e->Lunions.more = 0; @@ -1578,7 +1594,7 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t if (limit_slow == -1) limit_slow = kindx || kindy; jl_savedenv_t se; - save_env(e, &se, has_exists); + save_env(e, &se, 1); int count, limited = 0, ini_count = 0; jl_saved_unionstate_t latestLunions = {0, 0, 0, NULL}; while (1) { @@ -1596,13 +1612,13 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t limited = 1; if (!sub || !next_union_state(e, 0)) break; - if (limited || !has_exists || e->Runions.more == oldRmore) { + if (limited || e->Runions.more == oldRmore) { // re-save env and freeze the ∃decision for previous ∀Union // Note: We could ignore the rest `∃Union` decisions if `x` and `y` // contain no ∃ typevar, as they have no effect on env. ini_count = count; push_unionstate(&latestLunions, &e->Lunions); - re_save_env(e, &se, has_exists); + re_save_env(e, &se, 1); e->Runions.more = oldRmore; } } @@ -1610,12 +1626,12 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t break; assert(e->Runions.more > oldRmore); next_union_state(e, 1); - restore_env(e, &se, has_exists); // also restore Rdepth here + restore_env(e, &se, 1); // also restore Rdepth here e->Runions.more = oldRmore; } if (!sub) assert(e->Runions.more == oldRmore); - else if (limited || !has_exists) + else if (limited) e->Runions.more = oldRmore; free_env(&se); return sub; diff --git a/test/subtype.jl b/test/subtype.jl index d59a93ba8016c..98f10ce03de37 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2717,3 +2717,17 @@ end Tuple{Type{Complex{T}} where T, Type{Complex{T}} where T, Type{String}}, Tuple{Type{Complex{T}}, Type{Complex{T}}, Type{String}} where T ) + +#issue 58129 +for k in 1:500 + @eval struct $(Symbol(:T58129, k)){T} end +end +let Tvar = TypeVar(:Tvar) + V = UnionAll(Tvar, Union{(@eval($(Symbol(:T58129, k)){$Tvar}) for k in 1:500)...}) + @test Set{<:V} <: AbstractSet{<:V} +end +let Tvar1 = TypeVar(:Tvar1), Tvar2 = TypeVar(:Tvar2) + V1 = UnionAll(Tvar1, Union{(@eval($(Symbol(:T58129, k)){$Tvar1}) for k in 1:100)...}) + V2 = UnionAll(Tvar2, Union{(@eval($(Symbol(:T58129, k)){$Tvar2}) for k in 1:100)...}) + @test Set{<:V2} <: AbstractSet{<:V1} +end From e3935c3b6cbea8ddb258c46ff744ab0f1400703d Mon Sep 17 00:00:00 2001 From: adienes <51664769+adienes@users.noreply.github.com> Date: Thu, 24 Apr 2025 07:39:38 -0700 Subject: [PATCH 32/68] Narrow `@inbounds` annotations in `accumulate.jl` to only indexing calls (#58200) `op` should not be `inbounds`-ed here the test case I added might technically be an illegal (or at least bad) use of `@propagate_inbounds` in case anyone has a suggestion for a more natural test case, but nonetheless it demonstrates getting a `BoundsError` instead of a segfault (worse) or incorrect / junk data (more worse), both of which happen on master (cherry picked from commit bdab032dbd80b6469353cd7fb8389c09512b2a9f) --- base/accumulate.jl | 55 +++++++++++++++++++++++----------------- test/boundscheck_exec.jl | 7 +++++ 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/base/accumulate.jl b/base/accumulate.jl index a2d8a1d368d86..4265f6ef67cc8 100644 --- a/base/accumulate.jl +++ b/base/accumulate.jl @@ -5,12 +5,14 @@ # it does double the number of operations compared to accumulate, # though for cheap operations like + this does not have much impact (20%) function _accumulate_pairwise!(op::Op, c::AbstractVector{T}, v::AbstractVector, s, i1, n)::T where {T,Op} - @inbounds if n < 128 - s_ = v[i1] - c[i1] = op(s, s_) + if n < 128 + @inbounds s_ = v[i1] + ci1 = op(s, s_) + @inbounds c[i1] = ci1 for i = i1+1:i1+n-1 - s_ = op(s_, v[i]) - c[i] = op(s, s_) + s_ = op(s_, @inbounds(v[i])) + ci = op(s, s_) + @inbounds c[i] = ci end else n2 = n >> 1 @@ -26,7 +28,8 @@ function accumulate_pairwise!(op::Op, result::AbstractVector, v::AbstractVector) n = length(li) n == 0 && return result i1 = first(li) - @inbounds result[i1] = v1 = reduce_first(op,v[i1]) + v1 = reduce_first(op, @inbounds(v[i1])) + @inbounds result[i1] = v1 n == 1 && return result _accumulate_pairwise!(op, result, v, v1, i1+1, n-1) return result @@ -379,16 +382,16 @@ function _accumulate!(op, B, A, dims::Integer, init::Union{Nothing, Some}) # We can accumulate to a temporary variable, which allows # register usage and will be slightly faster ind1 = inds_t[1] - @inbounds for I in CartesianIndices(tail(inds_t)) + for I in CartesianIndices(tail(inds_t)) if init === nothing - tmp = reduce_first(op, A[first(ind1), I]) + tmp = reduce_first(op, @inbounds(A[first(ind1), I])) else - tmp = op(something(init), A[first(ind1), I]) + tmp = op(something(init), @inbounds(A[first(ind1), I])) end - B[first(ind1), I] = tmp + @inbounds B[first(ind1), I] = tmp for i_1 = first(ind1)+1:last(ind1) - tmp = op(tmp, A[i_1, I]) - B[i_1, I] = tmp + tmp = op(tmp, @inbounds(A[i_1, I])) + @inbounds B[i_1, I] = tmp end end else @@ -402,12 +405,15 @@ end @noinline function _accumulaten!(op, B, A, R1, ind, R2, init::Nothing) # Copy the initial element in each 1d vector along dimension `dim` ii = first(ind) - @inbounds for J in R2, I in R1 - B[I, ii, J] = reduce_first(op, A[I, ii, J]) + for J in R2, I in R1 + tmp = reduce_first(op, @inbounds(A[I, ii, J])) + @inbounds B[I, ii, J] = tmp end # Accumulate - @inbounds for J in R2, i in first(ind)+1:last(ind), I in R1 - B[I, i, J] = op(B[I, i-1, J], A[I, i, J]) + for J in R2, i in first(ind)+1:last(ind), I in R1 + @inbounds Bv, Av = B[I, i-1, J], A[I, i, J] + tmp = op(Bv, Av) + @inbounds B[I, i, J] = tmp end B end @@ -415,12 +421,15 @@ end @noinline function _accumulaten!(op, B, A, R1, ind, R2, init::Some) # Copy the initial element in each 1d vector along dimension `dim` ii = first(ind) - @inbounds for J in R2, I in R1 - B[I, ii, J] = op(something(init), A[I, ii, J]) + for J in R2, I in R1 + tmp = op(something(init), @inbounds(A[I, ii, J])) + @inbounds B[I, ii, J] = tmp end # Accumulate - @inbounds for J in R2, i in first(ind)+1:last(ind), I in R1 - B[I, i, J] = op(B[I, i-1, J], A[I, i, J]) + for J in R2, i in first(ind)+1:last(ind), I in R1 + @inbounds Bv, Av = B[I, i-1, J], A[I, i, J] + tmp = op(Bv, Av) + @inbounds B[I, i, J] = tmp end B end @@ -434,10 +443,10 @@ function _accumulate1!(op, B, v1, A::AbstractVector, dim::Integer) cur_val = v1 B[i1] = cur_val next = iterate(inds, state) - @inbounds while next !== nothing + while next !== nothing (i, state) = next - cur_val = op(cur_val, A[i]) - B[i] = cur_val + cur_val = op(cur_val, @inbounds(A[i])) + @inbounds B[i] = cur_val next = iterate(inds, state) end return B diff --git a/test/boundscheck_exec.jl b/test/boundscheck_exec.jl index f2eb2ea630893..dddd6d30c6f12 100644 --- a/test/boundscheck_exec.jl +++ b/test/boundscheck_exec.jl @@ -239,6 +239,13 @@ if bc_opt != bc_off @test_throws BoundsError BadVector20469([1,2,3])[:] end +# Accumulate: do not set inbounds context for user-supplied functions +if bc_opt != bc_off + Base.@propagate_inbounds op58200(a, b) = (1, 2)[a] + (1, 2)[b] + @test_throws BoundsError accumulate(op58200, 1:10) + @test_throws BoundsError Base.accumulate_pairwise(op58200, 1:10) +end + # Ensure iteration over arrays is vectorizable function g27079(X) r = 0 From 16c5b198ce6e18caef2bfb75e3a2ca68cdde9b7d Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Sun, 27 Apr 2025 12:53:58 +0800 Subject: [PATCH 33/68] Subtype: enable more Tuple related fast path. (#58196) Fix the subtyping hang found in https://github.com/JuliaLang/julia/issues/58115#issuecomment-2821388658 (cherry picked from commit c9ad04dcc73256bc24eb079f9f6506299b64b8ec) --- src/subtype.c | 38 +++++++++++++++++++++++++++++--------- test/subtype.jl | 4 ++++ 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index b6ce6c94b6bee..bc66a7a9da14b 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1052,7 +1052,8 @@ static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e); static int subtype_tuple_varargs( jl_vararg_t *vtx, jl_vararg_t *vty, - size_t vx, size_t vy, + jl_value_t *lastx, jl_value_t *lasty, + size_t vx, size_t vy, size_t x_reps, jl_stenv_t *e, int param) { jl_value_t *xp0 = jl_unwrap_vararg(vtx); jl_value_t *xp1 = jl_unwrap_vararg_num(vtx); @@ -1103,12 +1104,30 @@ static int subtype_tuple_varargs( } } } - - // in Vararg{T1} <: Vararg{T2}, need to check subtype twice to - // simulate the possibility of multiple arguments, which is needed - // to implement the diagonal rule correctly. - if (!subtype(xp0, yp0, e, param)) return 0; - if (!subtype(xp0, yp0, e, 1)) return 0; + int x_same = vx > 1 || (lastx && obviously_egal(xp0, lastx)); + int y_same = vy > 1 || (lasty && obviously_egal(yp0, 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 the same + // element type on the right more than twice. + } + else if (x_same && e->Runions.depth == 0 && y_same && + !jl_has_free_typevars(xp0) && !jl_has_free_typevars(yp0)) { + // fast path for repeated elements + } + else if ((e->Runions.depth == 0 ? !jl_has_free_typevars(xp0) : jl_is_concrete_type(xp0)) && !jl_has_free_typevars(yp0)) { + // fast path for separable sub-formulas + if (!jl_subtype(xp0, yp0)) + return 0; + } + else { + // in Vararg{T1} <: Vararg{T2}, need to check subtype twice to + // simulate the possibility of multiple arguments, which is needed + // to implement the diagonal rule correctly. + if (!subtype(xp0, yp0, e, param)) return 0; + if (x_reps < 2 && !subtype(xp0, yp0, e, 1)) return 0; + } constrain_length: if (!yp1) { @@ -1232,7 +1251,8 @@ static int subtype_tuple_tail(jl_datatype_t *xd, jl_datatype_t *yd, int8_t R, jl return subtype_tuple_varargs( (jl_vararg_t*)xi, (jl_vararg_t*)yi, - vx, vy, e, param); + lastx, lasty, + vx, vy, x_reps, e, param); } if (j >= ly) @@ -1253,7 +1273,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 && !jl_has_free_typevars(xi) && !jl_has_free_typevars(yi)) { + else if ((e->Runions.depth == 0 ? !jl_has_free_typevars(xi) : jl_is_concrete_type(xi)) && !jl_has_free_typevars(yi)) { // fast path for separable sub-formulas if (!jl_subtype(xi, yi)) return 0; diff --git a/test/subtype.jl b/test/subtype.jl index 98f10ce03de37..4552e0c58c1ee 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2731,3 +2731,7 @@ let Tvar1 = TypeVar(:Tvar1), Tvar2 = TypeVar(:Tvar2) V2 = UnionAll(Tvar2, Union{(@eval($(Symbol(:T58129, k)){$Tvar2}) for k in 1:100)...}) @test Set{<:V2} <: AbstractSet{<:V1} end + +#issue 58115 +@test Tuple{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{ Union{Tuple{}, Tuple{Tuple{}}}}}}}}}}}}} , Tuple{}} <: + Tuple{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Union{Tuple{}, Tuple{Tuple{}}}}}}}}}}}}}}}, Tuple{}} From e1d01440cf5ba329bfad78e039bafbc922afc063 Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Tue, 29 Apr 2025 18:01:53 +0200 Subject: [PATCH 34/68] doc: cross-reference `bind` in `Channel` method doc string (#58113) (cherry picked from commit d4f2e8ab3758bc614c80535b13d487cac55a6c8b) --- base/channels.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/channels.jl b/base/channels.jl index 75207e9fac76b..fab01ec52758b 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -61,7 +61,7 @@ Channel(sz=0) = Channel{Any}(sz) """ Channel{T=Any}(func::Function, size=0; taskref=nothing, spawn=false, threadpool=nothing) -Create a new task from `func`, bind it to a new channel of type +Create a new task from `func`, [`bind`](@ref) it to a new channel of type `T` and size `size`, and schedule the task, all in a single call. The channel is automatically closed when the task terminates. From fb1c07590b56fb944a0e915c09910cb1d875f639 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 6 May 2025 06:08:40 -0300 Subject: [PATCH 35/68] Fix removal of globals with addrspaces in removeAddrspaces (#58322) (cherry picked from commit 088bb9002e95631738c8ec5ba58b7b8a7b33019d) --- test/llvmpasses/remove-addrspaces.ll | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/llvmpasses/remove-addrspaces.ll b/test/llvmpasses/remove-addrspaces.ll index d25dd69748d86..daf407d047d73 100644 --- a/test/llvmpasses/remove-addrspaces.ll +++ b/test/llvmpasses/remove-addrspaces.ll @@ -6,6 +6,9 @@ ; RUN: opt -enable-new-pm=0 --opaque-pointers=1 -load libjulia-codegen%shlibext -RemoveJuliaAddrspaces -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE ; RUN: opt -enable-new-pm=1 --opaque-pointers=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='RemoveJuliaAddrspaces' -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE +; COM: check that the addrspace of the global itself is removed +; OPAQUE: @ejl_enz_runtime_exc = external global {} +@ejl_enz_runtime_exc = external addrspace(10) global {} ; COM: check that package image fptrs work @pjlsys_BoundsError_32 = internal global {} addrspace(10)* ({}***, {} addrspace(10)*, [1 x i64] addrspace(11)*)* null @@ -134,6 +137,13 @@ L6: unreachable } +define private fastcc void @diffejulia__mapreduce_97() { +L6: +; OPAQUE: store atomic ptr @ejl_enz_runtime_exc, ptr null unordered + store atomic {} addrspace(10)* @ejl_enz_runtime_exc, {} addrspace(10)* addrspace(10)* null unordered, align 8 + unreachable +} + ; COM: check that function attributes are preserved on declarations too declare void @convergent_function() #0 attributes #0 = { convergent } From e3cfb88de95543b61eb07b81dae6bf7e57feae8d Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 2 Jun 2025 21:47:28 +0200 Subject: [PATCH 36/68] Update readlines(::Cmd) test to not rely on the filesystem (#58607) (cherry picked from commit 48c0e7f99c4e83d98761898e090ca3b5c6d3be90) --- test/spawn.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/spawn.jl b/test/spawn.jl index 1fab652199ee0..14b5fd28ca7dd 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -609,7 +609,9 @@ end @test reduce(&, [`$echocmd abc`, `$echocmd def`, `$echocmd hij`]) == `$echocmd abc` & `$echocmd def` & `$echocmd hij` # readlines(::Cmd), accidentally broken in #20203 -@test sort(readlines(`$lscmd -A`)) == sort(readdir()) +let str = "foo\nbar" + @test readlines(`$echocmd $str`) == split(str) +end # issue #19864 (PR #20497) let c19864 = readchomp(pipeline(ignorestatus( From 11fd0d8db7096ba950850929487877018687692f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 21 Aug 2023 11:30:12 -0400 Subject: [PATCH 37/68] recursive types: fix implementation of references_name (#50963) To do the old version correctly, we would need to carefully track whether we end up using that parameter as a parameter of any apply_type call in the fieldtype computation of any of the fields, recursively. Since any of those might cause recursion in the constructor graph, resulting in trying to layout some concrete type before we have finished computing the rest of the fieldtypes. That seems unnecessarily difficult to do well, so instead we go back to just doing the trivial cases. (cherry picked from commit 1182003f957157045de0f7f681d366885839c6ff) --- src/builtins.c | 64 +++++++++++++++++++++++++++----------------------- test/core.jl | 38 ++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 31 deletions(-) diff --git a/src/builtins.c b/src/builtins.c index a8285aab99953..7104cf2b9c47a 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1696,42 +1696,48 @@ static int equiv_field_types(jl_value_t *old, jl_value_t *ft) // inline it. The only way fields can reference this type (due to // syntax-enforced restrictions) is via being passed as a type parameter. Thus // we can conservatively check this by examining only the parameters of the -// dependent types. -// affects_layout is a hack introduced by #35275 to workaround a problem -// introduced by #34223: it checks whether we will potentially need to -// compute the layout of the object before we have fully computed the types of -// the fields during recursion over the allocation of the parameters for the -// field types (of the concrete subtypes) -static int references_name(jl_value_t *p, jl_typename_t *name, int affects_layout) JL_NOTSAFEPOINT -{ - if (jl_is_uniontype(p)) - return references_name(((jl_uniontype_t*)p)->a, name, affects_layout) || - references_name(((jl_uniontype_t*)p)->b, name, affects_layout); - if (jl_is_vararg(p)) { - jl_value_t *T = ((jl_vararg_t*)p)->T; - jl_value_t *N = ((jl_vararg_t*)p)->N; - return (T && references_name(T, name, affects_layout)) || - (N && references_name(N, name, affects_layout)); - } - if (jl_is_unionall(p)) - return references_name((jl_value_t*)((jl_unionall_t*)p)->var->lb, name, 0) || - references_name((jl_value_t*)((jl_unionall_t*)p)->var->ub, name, 0) || - references_name(((jl_unionall_t*)p)->body, name, affects_layout); +// dependent types. Additionally, a field might have already observed this +// object for layout purposes before we got around to deciding if inlining +// would be possible, so we cannot change the layout now if so. +// affects_layout is a (conservative) analysis of layout_uses_free_typevars +// freevars is a (conservative) analysis of what calling jl_has_bound_typevars from name->wrapper gives (TODO: just call this instead?) +static int references_name(jl_value_t *p, jl_typename_t *name, int affects_layout, int freevars) JL_NOTSAFEPOINT +{ + if (freevars && !jl_has_free_typevars(p)) + freevars = 0; + while (jl_is_unionall(p)) { + if (references_name((jl_value_t*)((jl_unionall_t*)p)->var->lb, name, 0, freevars) || + references_name((jl_value_t*)((jl_unionall_t*)p)->var->ub, name, 0, freevars)) + return 1; + p = ((jl_unionall_t*)p)->body; + } + if (jl_is_uniontype(p)) { + return references_name(((jl_uniontype_t*)p)->a, name, affects_layout, freevars) || + references_name(((jl_uniontype_t*)p)->b, name, affects_layout, freevars); + } if (jl_is_typevar(p)) return 0; // already checked by unionall, if applicable if (jl_is_datatype(p)) { jl_datatype_t *dp = (jl_datatype_t*)p; if (affects_layout && dp->name == name) return 1; - // affects_layout checks whether we will need to attempt to layout this - // type (based on whether all copies of it have the same layout) in - // that case, we still need to check the recursive parameters for - // layout recursion happening also, but we know it won't itself cause - // problems for the layout computation affects_layout = ((jl_datatype_t*)jl_unwrap_unionall(dp->name->wrapper))->layout == NULL; + // and even if it has a layout, the fields themselves might trigger layouts if they use tparam i + // rather than checking this for each field, we just assume it applies + if (!affects_layout && freevars && jl_field_names(dp) != jl_emptysvec) { + jl_svec_t *types = ((jl_datatype_t*)jl_unwrap_unionall(dp->name->wrapper))->types; + size_t i, l = jl_svec_len(types); + for (i = 0; i < l; i++) { + jl_value_t *ft = jl_svecref(types, i); + if (!jl_is_typevar(ft) && jl_has_free_typevars(ft)) { + affects_layout = 1; + break; + } + } + } size_t i, l = jl_nparams(p); for (i = 0; i < l; i++) { - if (references_name(jl_tparam(p, i), name, affects_layout)) + if (references_name(jl_tparam(p, i), name, affects_layout, freevars)) return 1; } } @@ -1767,12 +1773,12 @@ JL_CALLABLE(jl_f__typebody) // able to compute the layout of the object before needing to // publish it, so we must assume it cannot be inlined, if that // check passes, then we also still need to check the fields too. - if (!dt->name->mutabl && (nf == 0 || !references_name((jl_value_t*)dt->super, dt->name, 1))) { + if (!dt->name->mutabl && (nf == 0 || !references_name((jl_value_t*)dt->super, dt->name, 0, 1))) { int mayinlinealloc = 1; size_t i; for (i = 0; i < nf; i++) { jl_value_t *fld = jl_svecref(ft, i); - if (references_name(fld, dt->name, 1)) { + if (references_name(fld, dt->name, 1, 1)) { mayinlinealloc = 0; break; } diff --git a/test/core.jl b/test/core.jl index 4193267b1abed..4c6ba29330436 100644 --- a/test/core.jl +++ b/test/core.jl @@ -389,8 +389,8 @@ let ft = Base.datatype_fieldtypes @test ft(elT2.body)[1].parameters[1] === elT2 @test Base.isconcretetype(ft(elT2.body)[1]) end -#struct S22624{A,B,C} <: Ref{S22624{Int64,A}}; end -@test_broken @isdefined S22624 +struct S22624{A,B,C} <: Ref{S22624{Int,A}}; end +@test sizeof(S22624) == sizeof(S22624{Int,Int,Int}) == 0 # issue #42297 mutable struct Node42297{T, V} @@ -429,6 +429,18 @@ mutable struct FooFoo{A,B} y::FooFoo{A} end @test FooFoo{Int} <: FooFoo{Int,AbstractString}.types[1] +# make sure this self-referential struct doesn't crash type layout +struct SelfTyA{V} + a::Base.RefValue{V} +end +struct SelfTyB{T} + a::T + b::SelfTyA{SelfTyB{T}} +end +let T = Base.RefValue{SelfTyB{Int}} + @test sizeof(T) === sizeof(Int) + @test sizeof(T.types[1]) === 2 * sizeof(Int) +end let x = (2,3) @test +(x...) == 5 @@ -4125,7 +4137,29 @@ end let z1 = Z14477() @test isa(z1, Z14477) @test isa(z1.fld, Z14477) + @test isdefined(z1, :fld) + @test !isdefined(z1.fld, :fld) +end +struct Z14477B + fld::Union{Nothing,Z14477B} + Z14477B() = new(new(nothing)) end +let z1 = Z14477B() + @test isa(z1, Z14477B) + @test isa(z1.fld, Z14477B) + @test isa(z1.fld.fld, Nothing) +end +struct Z14477C{T} + fld::Z14477C{Int8} + Z14477C() = new{Int16}(new{Int8}()) +end +let z1 = Z14477C() + @test isa(z1, Z14477C) + @test isa(z1.fld, Z14477C) + @test isdefined(z1, :fld) + @test !isdefined(z1.fld, :fld) +end + # issue #8846, generic macros macro m8846(a, b=0) From c442d55c4e5b7e7efcd59591d873e38eec049db3 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 8 Aug 2024 15:18:11 -0400 Subject: [PATCH 38/68] handle unbound vars in NTuple fields (#55405) Comparing objects by `==` will happily answer nonsense for malformed type comparisons, such as `unwrap_unionall(A) == A`. Avoid forming that query. Additionally, need to recourse through Vararg when examining type structure to make decisions. Fix #55076 Fix #55189 (cherry picked from commit 32423a8039daeb57ecd7bc26db5476125c0bfb62) --- src/builtins.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/builtins.c b/src/builtins.c index 7104cf2b9c47a..6b5c22f0301a0 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1715,6 +1715,12 @@ static int references_name(jl_value_t *p, jl_typename_t *name, int affects_layou return references_name(((jl_uniontype_t*)p)->a, name, affects_layout, freevars) || references_name(((jl_uniontype_t*)p)->b, name, affects_layout, freevars); } + if (jl_is_vararg(p)) { + jl_value_t *T = ((jl_vararg_t*)p)->T; + jl_value_t *N = ((jl_vararg_t*)p)->N; + return (T && references_name(T, name, affects_layout, freevars)) || + (N && references_name(N, name, affects_layout, freevars)); + } if (jl_is_typevar(p)) return 0; // already checked by unionall, if applicable if (jl_is_datatype(p)) { From cfedfc079065be74233a6992f007954fe247ddb3 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 21 Jan 2025 10:04:45 -0500 Subject: [PATCH 39/68] inference: fix lattice for unusual InterConditional return and Const Bool representations (#57080) Fixes #54886 Rule introduced by 55cee67 (cherry picked from commit 4e13e0e449aee71a5ad7f335fbc8812d28c51cf2) --- base/compiler/abstractinterpretation.jl | 11 ++++++++++- base/compiler/typelattice.jl | 7 +++++++ test/compiler/inference.jl | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 49149fde59691..192d4b218cdde 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -3065,7 +3065,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) rt = abstract_eval_value(interp, stmt.val, currstate, frame) rt = widenreturn(rt, BestguessInfo(interp, bestguess, nargs, slottypes, currstate)) # narrow representation of bestguess slightly to prepare for tmerge with rt - if rt isa InterConditional && bestguess isa Const + if rt isa InterConditional && bestguess isa Const && bestguess.val isa Bool let slot_id = rt.slot old_id_type = slottypes[slot_id] if bestguess.val === true && rt.elsetype !== Bottom @@ -3074,6 +3074,15 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) bestguess = InterConditional(slot_id, Bottom, old_id_type) end end + # or narrow representation of rt slightly to prepare for tmerge with bestguess + elseif bestguess isa InterConditional && rt isa Const && rt.val isa Bool + slot_id = bestguess.slot + old_id_type = widenconditional(slottypes[slot_id]) + if rt.val === true && bestguess.elsetype !== Bottom + rt = InterConditional(slot_id, old_id_type, Bottom) + elseif rt.val === false && bestguess.thentype !== Bottom + rt = InterConditional(slot_id, Bottom, old_id_type) + end end # copy limitations to return value if !isempty(frame.pclimitations) diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index df6022609d612..12de472298326 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -427,6 +427,13 @@ end end a = Bool elseif isa(b, ConditionalT) + if isa(a, Const) && isa(a.val, Bool) + if (a.val === true && b.thentype === Any && b.elsetype === Bottom) || + (a.val === false && b.elsetype === Any && b.thentype === Bottom) + # this Conditional contains distinctly no lattice information, and is simply an alternative representation of the Const Bool used for internal tracking purposes + return true + end + end return false end return ⊑(widenlattice(lattice), a, b) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 916756eb6bfb7..777f1d5340e11 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2675,6 +2675,26 @@ vacond(cnd, va...) = cnd ? va : 0 vacond(isa(x, Tuple{Int,Int}), x, x) end |> only == Union{Int,Tuple{Any,Any}} +let A = Core.Const(true) + B = Core.InterConditional(2, Tuple, Union{}) + C = Core.InterConditional(2, Any, Union{}) + L = ipo_lattice(Core.Compiler.NativeInterpreter()) + @test !⊑(L, A, B) + @test ⊑(L, B, A) + @test tmerge(L, A, B) == C + @test ⊑(L, A, C) +end +function tail_is_ntuple((@nospecialize t::Tuple)) + if unknown + t isa Tuple + else + tail_is_ntuple(t) + end +end +tail_is_ntuple_val((@nospecialize t::Tuple)) = Val(tail_is_ntuple(t)) +@test Base.return_types(tail_is_ntuple, (Tuple,)) |> only === Bool +@test Base.return_types(tail_is_ntuple_val, (Tuple,)) |> only === Val{true} + # https://github.com/JuliaLang/julia/issues/47435 is_closed_ex(e::InvalidStateException) = true is_closed_ex(e) = false From 2e38e11bd3a506209f26922c3a4e0ff80c9469e2 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 14 Feb 2025 10:43:22 -0500 Subject: [PATCH 40/68] lowering: fix has_fcall computation (#57395) Previously didn't handle ccall or cfunction that were assigned to a variable. Required for inlining correctness. Fixes #57023 (cherry picked from commit 57b7d2e0a88d2e4006ad74ec4bd1db5586e2d0bf) --- src/method.c | 10 ++++++---- test/core.jl | 5 +++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/method.c b/src/method.c index e96d1008e3056..d99493208cc4d 100644 --- a/src/method.c +++ b/src/method.c @@ -380,11 +380,13 @@ static void jl_code_info_set_ir(jl_code_info_t *li, jl_expr_t *ir) } bd[j] = jl_nothing; } - else if (jl_is_expr(st) && ((jl_expr_t*)st)->head == jl_return_sym) { + else if (jl_is_expr(st) && ((jl_expr_t*)st)->head == jl_return_sym) jl_array_ptr_set(body, j, jl_new_struct(jl_returnnode_type, jl_exprarg(st, 0))); - } - else if (jl_is_expr(st) && (((jl_expr_t*)st)->head == jl_foreigncall_sym || ((jl_expr_t*)st)->head == jl_cfunction_sym)) { - li->has_fcall = 1; + else { + if (jl_is_expr(st) && ((jl_expr_t*)st)->head == jl_assign_sym) + st = jl_exprarg(st, 1); + if (jl_is_expr(st) && (((jl_expr_t*)st)->head == jl_foreigncall_sym || ((jl_expr_t*)st)->head == jl_cfunction_sym)) + li->has_fcall = 1; } if (is_flag_stmt) jl_array_uint8_set(li->ssaflags, j, 0); diff --git a/test/core.jl b/test/core.jl index 4c6ba29330436..26e16f20f0073 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8130,3 +8130,8 @@ let widen_diagonal(x::UnionAll) = Base.rewrap_unionall(Base.widen_diagonal(Base. @test Tuple === widen_diagonal(Union{Tuple{Vararg{S}}, Tuple{Vararg{T}}} where {S, T}) @test Tuple{Vararg{Val{<:Set}}} == widen_diagonal(Tuple{Vararg{T}} where T<:Val{<:Set}) end + +myfun57023a(::Type{T}) where {T} = (x = @ccall mycfun()::Ptr{T}; x) +@test only(code_lowered(myfun57023a)).has_fcall +myfun57023b(::Type{T}) where {T} = (x = @cfunction myfun57023a Ptr{T} (Ref{T},); x) +@test only(code_lowered(myfun57023b)).has_fcall From 2551d9dd9fe36212e9ececd90dd70e8d1e2c4b73 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 10 Apr 2025 11:48:16 -0400 Subject: [PATCH 41/68] Remove try-finally scope from `@time_imports` `@trace_compile` `@trace_dispatch` (#58011) Matches `@time` `@profile` etc. (cherry picked from commit 1117df66f2f199e7224697d110d023add4231579) --- base/timing.jl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/base/timing.jl b/base/timing.jl index eb56ba2a9521f..7dfd73a5b2640 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -511,11 +511,12 @@ end # here so it's possible to time/trace all imports, including InteractiveUtils and its deps macro time_imports(ex) quote - try - Base.Threads.atomic_add!(Base.TIMING_IMPORTS, 1) - $(esc(ex)) - finally + Base.Threads.atomic_add!(Base.TIMING_IMPORTS, 1) + @__tryfinally( + # try + $(esc(ex)), + # finally Base.Threads.atomic_sub!(Base.TIMING_IMPORTS, 1) - end + ) end end From 9379d813707dafab3e8c12d5e0057fbec3248fd3 Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Tue, 15 Apr 2025 16:00:59 -0400 Subject: [PATCH 42/68] [DOC] Update installation docs: /downloads/ => /install/ (#58127) I've created an [Install](https://julialang.org/install/) page separate from the [Downloads](https://julialang.org/downloads/) page on the website. This updates various references to point to the correct pages. (cherry picked from commit 48660a617318c0907ed0b2a260c4c46672d975ed) --- README.md | 29 +++++++++++++++++------------ doc/src/devdocs/build/windows.md | 2 +- doc/src/index.md | 2 +- doc/src/manual/faq.md | 3 +-- doc/src/manual/getting-started.md | 2 +- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index a4480ecf482cd..989d8900f86c8 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ and installing Julia, below. ## Resources - **Homepage:** -- **Binaries:** +- **Install:** - **Source code:** - **Documentation:** - **Packages:** @@ -64,17 +64,22 @@ helpful to start contributing to the Julia codebase. ## Binary Installation -If you would rather not compile the latest Julia from source, -platform-specific tarballs with pre-compiled binaries are also -[available for download](https://julialang.org/downloads/). The -downloads page also provides details on the -[different tiers of support](https://julialang.org/downloads/#supported_platforms) -for OS and platform combinations. - -If everything works correctly, you will see a Julia banner and an -interactive prompt into which you can enter expressions for -evaluation. You can read about [getting -started](https://docs.julialang.org/en/v1/manual/getting-started/) in the manual. +The recommended way of installing Julia is to use `juliaup` which will install +the latest stable `julia` for you and help keep it up to date. It can also let +you install and run different Julia versions simultaneously. Instructions for +this can be find [here](https://julialang.org/install/). If you want to manually +download specific Julia binaries, you can find those on the [downloads +page](https://julialang.org/downloads/). The downloads page also provides +details on the [different tiers of +support](https://julialang.org/downloads/#supported_platforms) for OS and +platform combinations. + +If everything works correctly, you will get a `julia` program and when you run +it in a terminal or command prompt, you will see a Julia banner and an +interactive prompt into which you can enter expressions for evaluation. You can +read about [getting +started](https://docs.julialang.org/en/v1/manual/getting-started/) in the +manual. **Note**: Although some system package managers provide Julia, such installations are neither maintained nor endorsed by the Julia diff --git a/doc/src/devdocs/build/windows.md b/doc/src/devdocs/build/windows.md index 7192bb8a7a544..1191169b565f5 100644 --- a/doc/src/devdocs/build/windows.md +++ b/doc/src/devdocs/build/windows.md @@ -32,7 +32,7 @@ or edit `%USERPROFILE%\.gitconfig` and add/edit the lines: ## Binary distribution For the binary distribution installation notes on Windows please see the instructions at -[https://julialang.org/downloads/platform/#windows](https://julialang.org/downloads/platform/#windows). +[https://julialang.org/downloads/platform/#windows](https://julialang.org/downloads/platform/#windows). Note, however, that on all platforms [using `juliaup`](https://julialang.org/install/) is recommended over manually installing binaries. ## Source distribution diff --git a/doc/src/index.md b/doc/src/index.md index bb758d14b4cf2..a8ebc7fa9beb3 100644 --- a/doc/src/index.md +++ b/doc/src/index.md @@ -37,7 +37,7 @@ Markdown.parse(""" Below is a non-exhasutive list of links that will be useful as you learn and use the Julia programming language. - [Julia Homepage](https://julialang.org) -- [Download Julia](https://julialang.org/downloads/) +- [Install Julia](https://julialang.org/install/) - [Discussion forum](https://discourse.julialang.org) - [Julia YouTube](https://www.youtube.com/user/JuliaLanguage) - [Find Julia Packages](https://julialang.org/packages/) diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index b04c9585e30f3..57742e2fdefe6 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -1072,8 +1072,7 @@ You may wish to test against the nightly version to ensure that such regressions Finally, you may also consider building Julia from source for yourself. This option is mainly for those individuals who are comfortable at the command line, or interested in learning. If this describes you, you may also be interested in reading our [guidelines for contributing](https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md). -Links to each of these download types can be found on the download page at [https://julialang.org/downloads/](https://julialang.org/downloads/). -Note that not all versions of Julia are available for all platforms. +The [`juliaup` install manager](https://julialang.org/install/) has pre-defined channels named `release` and `lts` for the latest stable release and the current LTS release, as well as version-specific channels. ### How can I transfer the list of installed packages after updating my version of Julia? diff --git a/doc/src/manual/getting-started.md b/doc/src/manual/getting-started.md index e972788022de6..9e0011ee7b714 100644 --- a/doc/src/manual/getting-started.md +++ b/doc/src/manual/getting-started.md @@ -1,7 +1,7 @@ # [Getting Started](@id man-getting-started) Julia installation is straightforward, whether using precompiled binaries or compiling from source. -Download and install Julia by following the instructions at [https://julialang.org/downloads/](https://julialang.org/downloads/). +Download and install Julia by following the instructions at [https://julialang.org/install/](https://julialang.org/install/). If you are coming to Julia from one of the following languages, then you should start by reading the section on noteworthy differences from [MATLAB](@ref Noteworthy-differences-from-MATLAB), [R](@ref Noteworthy-differences-from-R), [Python](@ref Noteworthy-differences-from-Python), [C/C++](@ref Noteworthy-differences-from-C/C) or [Common Lisp](@ref Noteworthy-differences-from-Common-Lisp). This will help you avoid some common pitfalls since Julia differs from those languages in many subtle ways. From 129de6d1cc1e075484c2c44a93ecc40599e1d288 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 22 Apr 2025 08:54:53 +0200 Subject: [PATCH 43/68] add showing a string to REPL precompile workload (#58157) (cherry picked from commit 4766133f6c1e7825f3fb35243e353a380eb425d5) --- contrib/generate_precompile.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 7ad976e1e0106..8785696138a7d 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -74,6 +74,7 @@ print("") printstyled("a", "b") display([1]) display([1 2; 3 4]) +display("a string") foo(x) = 1 @time @eval foo(1) ; pwd From 6e5bd800b965df95312f59ab6c69223aa2fa5698 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sat, 26 Apr 2025 21:21:09 +0530 Subject: [PATCH 44/68] Specialize `one` for the `SizedArray` test helper (#58209) Since the size of the array is encoded in the type, we may define `one` on the type. This is useful in certain linear algebra contexts. (cherry picked from commit d9fafab2e0c5e15eed0c20082755a1c685d4f5b3) --- test/abstractarray.jl | 1 + test/testhelpers/SizedArrays.jl | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/test/abstractarray.jl b/test/abstractarray.jl index e7469a7a331ed..10708c133deea 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -1907,3 +1907,4 @@ end @test r2[i] == z[j] end end + diff --git a/test/testhelpers/SizedArrays.jl b/test/testhelpers/SizedArrays.jl index dfcc5b79f1387..720309e9e3236 100644 --- a/test/testhelpers/SizedArrays.jl +++ b/test/testhelpers/SizedArrays.jl @@ -29,6 +29,12 @@ Base.size(a::SizedArray) = size(typeof(a)) Base.size(::Type{<:SizedArray{SZ}}) where {SZ} = SZ Base.getindex(A::SizedArray, i...) = getindex(A.data, i...) Base.zero(::Type{T}) where T <: SizedArray = SizedArray{size(T)}(zeros(eltype(T), size(T))) +function Base.one(::Type{SizedMatrix{SZ,T,A}}) where {SZ,T,A} + allequal(SZ) || throw(DimensionMismatch("multiplicative identity defined only for square matrices")) + D = diagm(fill(one(T), SZ[1])) + SizedArray{SZ}(convert(A, D)) +end +Base.parent(S::SizedArray) = S.data +(S1::SizedArray{SZ}, S2::SizedArray{SZ}) where {SZ} = SizedArray{SZ}(S1.data + S2.data) ==(S1::SizedArray{SZ}, S2::SizedArray{SZ}) where {SZ} = S1.data == S2.data function *(S1::SizedArray, S2::SizedArray) From 4ac30b7872a58e94d0f2aebefccc8d0357f3d0bf Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 9 May 2025 15:05:33 -0400 Subject: [PATCH 45/68] codegen: remove readonly from abstract type calling convention (#58356) Refs: #58070 (cherry picked from commit f07565fc65f3a0339d0fedbb2fbc2801d6cc853c) --- src/cgutils.cpp | 2 +- src/codegen.cpp | 42 ++++++++++++++++++++++------------------ src/julia.h | 2 +- test/compiler/codegen.jl | 4 ++++ 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index d560ad47ff7aa..8aa48cfe9942e 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1264,6 +1264,7 @@ static Value *emit_sizeof(jl_codectx_t &ctx, const jl_cgval_t &p) return dyn_size; } } +*/ static Value *emit_datatype_mutabl(jl_codectx_t &ctx, Value *dt) { @@ -1278,7 +1279,6 @@ static Value *emit_datatype_mutabl(jl_codectx_t &ctx, Value *dt) mutabl = ctx.builder.CreateLShr(mutabl, 1); return ctx.builder.CreateTrunc(mutabl, getInt1Ty(ctx.builder.getContext())); } -*/ static Value *emit_datatype_isprimitivetype(jl_codectx_t &ctx, Value *typ) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 20a6240a926d0..0a21d08a98f5e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1344,7 +1344,7 @@ static MDNode *best_tbaa(jl_tbaacache_t &tbaa_cache, jl_value_t *jt) { // note that this includes jl_isbits, although codegen should work regardless static bool jl_is_concrete_immutable(jl_value_t* t) { - return jl_is_immutable_datatype(t) && ((jl_datatype_t*)t)->isconcretetype; + return jl_may_be_immutable_datatype(t) && ((jl_datatype_t*)t)->isconcretetype; } static bool jl_is_pointerfree(jl_value_t* t) @@ -6248,9 +6248,9 @@ static Function* gen_cfun_wrapper( inputarg = mark_julia_type(ctx, val, false, jargty); } } - else if (static_at || (!jl_is_typevar(jargty) && !jl_is_immutable_datatype(jargty))) { - // must be a jl_value_t* (because it's mutable or contains gc roots) - inputarg = mark_julia_type(ctx, maybe_decay_untracked(ctx, emit_bitcast(ctx, val, ctx.types().T_prjlvalue)), true, jargty_proper); + else if (static_at || (!jl_is_typevar(jargty) && (!jl_is_datatype(jargty) || jl_is_abstracttype(jargty) || jl_is_mutable_datatype(jargty)))) { + // must be a jl_value_t* (because it is mutable or abstract) + inputarg = mark_julia_type(ctx, maybe_decay_untracked(ctx, val), true, jargty_proper); } else { // allocate val into a new box, if it might not be boxed @@ -6263,32 +6263,36 @@ static Function* gen_cfun_wrapper( ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_prjlvalue, nestPtr, jl_array_len(*closure_types)), Align(sizeof(void*))); BasicBlock *boxedBB = BasicBlock::Create(ctx.builder.getContext(), "isboxed", cw); - BasicBlock *loadBB = BasicBlock::Create(ctx.builder.getContext(), "need-load", cw); + BasicBlock *notanyBB = BasicBlock::Create(ctx.builder.getContext(), "not-any", cw); 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); // 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); - p1 = track_pjlvalue(ctx, p1); - ctx.builder.CreateBr(afterBB); - ctx.builder.SetInsertPoint(loadBB); Value *isrtany = ctx.builder.CreateICmpEQ( - literal_pointer_val(ctx, (jl_value_t*)jl_any_type), - ctx.builder.CreateBitCast(val, ctx.types().T_pjlvalue)); - ctx.builder.CreateCondBr(isrtany, isanyBB, unboxedBB); + track_pjlvalue(ctx,literal_pointer_val(ctx, (jl_value_t*)jl_any_type)), runtime_dt); + ctx.builder.CreateCondBr(isrtany, isanyBB, notanyBB); ctx.builder.SetInsertPoint(isanyBB); - Value *p2 = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, ctx.builder.CreateBitCast(val, ctx.types().T_pprjlvalue), Align(sizeof(void*))); + Value *p1 = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, val, Align(sizeof(void*))); + ctx.builder.CreateBr(afterBB); + isanyBB = ctx.builder.GetInsertBlock(); // could have changed + ctx.builder.SetInsertPoint(notanyBB); + jl_cgval_t runtime_dt_val = mark_julia_type(ctx, runtime_dt, true, jl_any_type); + Value *isrtboxed = // (!jl_is_datatype(runtime_dt) || !jl_is_concrete_datatype(runtime_dt) || jl_is_mutable_datatype(runtime_dt)) + emit_guarded_test(ctx, emit_exactly_isa(ctx, runtime_dt_val, jl_datatype_type), true, [&] { + return ctx.builder.CreateOr(ctx.builder.CreateNot(emit_isconcrete(ctx, runtime_dt)), emit_datatype_mutabl(ctx, runtime_dt)); + }); + ctx.builder.CreateCondBr(isrtboxed, boxedBB, unboxedBB); + ctx.builder.SetInsertPoint(boxedBB); + Value *p2 = track_pjlvalue(ctx, val); ctx.builder.CreateBr(afterBB); + boxedBB = ctx.builder.GetInsertBlock(); // could have changed ctx.builder.SetInsertPoint(unboxedBB); Value *p3 = emit_new_bits(ctx, runtime_dt, val); unboxedBB = ctx.builder.GetInsertBlock(); // could have changed ctx.builder.CreateBr(afterBB); ctx.builder.SetInsertPoint(afterBB); PHINode *p = ctx.builder.CreatePHI(ctx.types().T_prjlvalue, 3); - p->addIncoming(p1, boxedBB); - p->addIncoming(p2, isanyBB); + p->addIncoming(p1, isanyBB); + p->addIncoming(p2, boxedBB); p->addIncoming(p3, unboxedBB); inputarg = mark_julia_type(ctx, p, true, jargty_proper); } @@ -7069,7 +7073,7 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value param.addAttribute(Attribute::ReadOnly); ty = PointerType::get(ty, AddressSpace::Derived); } - else if (isboxed && jl_is_immutable_datatype(jt)) { + else if (isboxed && jl_may_be_immutable_datatype(jt) && !jl_is_abstracttype(jt)) { param.addAttribute(Attribute::ReadOnly); } else if (jl_is_primitivetype(jt) && ty->isIntegerTy()) { diff --git a/src/julia.h b/src/julia.h index 8166eb0286830..6a152e3cab9a9 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1316,7 +1316,7 @@ static inline int jl_is_layout_opaque(const jl_datatype_layout_t *l) JL_NOTSAFEP #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_may_be_immutable_datatype(t) (jl_is_datatype(t) && (!((jl_datatype_t*)t)->name->mutabl)) #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) diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 21cf2e8fca5db..60cf6adb07d85 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -879,3 +879,7 @@ struct Vec56937 x::NTuple{8, VecElement{Int}} end x56937 = Ref(Vec56937(ntuple(_->VecElement(1),8))) @test x56937[].x[1] == VecElement{Int}(1) # shouldn't crash + +@noinline f_mutateany(@nospecialize x) = x[] = 1 +g_mutateany() = (y = Ref(0); f_mutateany(y); y[]) +@test g_mutateany() === 1 From 2e097bd88ebfc0c493c440e4f108a3d79907cae0 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 14 May 2025 17:36:33 -0400 Subject: [PATCH 46/68] fix `hasmethod` with kwargs to exclude positional arg names (#58410) (cherry picked from commit 7df60f480df8c6aed874b35eb7c8a26fc769a4cc) --- base/reflection.jl | 2 +- test/reflection.jl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/base/reflection.jl b/base/reflection.jl index 7a1a9836b3681..6f32d8bad5aaa 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1851,11 +1851,11 @@ function hasmethod(f, t, kwnames::Tuple{Vararg{Symbol}}; world::UInt=get_world_c 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) + kws = kws[((match::Method).nargs + 1):end] # remove positional arguments 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/reflection.jl b/test/reflection.jl index c13e7d88d8cfd..efd52f8df3b03 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -825,6 +825,7 @@ f(x::Int; y=3) = x + y @test hasmethod(f, Tuple{Int}) @test hasmethod(f, Tuple{Int}, ()) @test hasmethod(f, Tuple{Int}, (:y,)) +@test !hasmethod(f, Tuple{Int}, (:x,)) @test !hasmethod(f, Tuple{Int}, (:jeff,)) @test !hasmethod(f, Tuple{Int}, (:y,), world=typemin(UInt)) g(; b, c, a) = a + b + c From 99ffacaf776836c3fdbbf296ea47a2d4cfb3f60b Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 27 May 2025 09:00:44 -0400 Subject: [PATCH 47/68] Don't filter `Core` methods from newly-inferred list (#58510) This allows constructors like `Tuple{Type{Vector{Foo}}, UndefInitializer, Tuple{Int}}` to precompile as usual Appears to have a minimal effect on the stdlib pkgimages: ```julia --- before.txt 2025-05-23 08:36:20.171870043 -0400 +++ after.txt 2025-05-22 14:48:49.003869097 -0400 @@ -47,7 +47,7 @@ 20K ../julia/usr/share/julia/compiled/v1.13/Logging/pkgimage.so 20K ../julia/usr/share/julia/compiled/v1.13/Logging/pkgimage.so 3.5M ../julia/usr/share/julia/compiled/v1.13/Markdown/pkgimage.so -3.5M ../julia/usr/share/julia/compiled/v1.13/Markdown/pkgimage.so +3.6M ../julia/usr/share/julia/compiled/v1.13/Markdown/pkgimage.so 184K ../julia/usr/share/julia/compiled/v1.13/Mmap/pkgimage.so 184K ../julia/usr/share/julia/compiled/v1.13/Mmap/pkgimage.so 28K ../julia/usr/share/julia/compiled/v1.13/MozillaCACerts_jll/pkgimage.so ``` Resolves #58497. (cherry picked from commit f8ece052b0ad98ed27316b63218b32c0877e69da) --- base/compiler/typeinfer.jl | 2 +- test/precompile.jl | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 92e6b0dd5f666..f00d8275493e9 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -406,7 +406,7 @@ function cache_result!(interp::AbstractInterpreter, result::InferenceResult) 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 + if isa(m, Method) ccall(:jl_push_newly_inferred, Cvoid, (Any,), ci) end end diff --git a/test/precompile.jl b/test/precompile.jl index cd5bdc5a02492..ba9c54fe77022 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -1854,6 +1854,29 @@ precompile_test_harness("Issue #50538") do load_path end +precompile_test_harness("Pre-compile Core methods") do load_path + # Core methods should support pre-compilation as external CI's like anything else + # https://github.com/JuliaLang/julia/issues/58497 + write(joinpath(load_path, "CorePrecompilation.jl"), + """ + module CorePrecompilation + struct Foo end + precompile(Tuple{Type{Vector{Foo}}, UndefInitializer, Tuple{Int}}) + end + """) + ji, ofile = Base.compilecache(Base.PkgId("CorePrecompilation")) + @eval using CorePrecompilation + invokelatest() do + let tt = Tuple{Type{Vector{CorePrecompilation.Foo}}, UndefInitializer, Tuple{Int}}, + match = first(Base._methods_by_ftype(tt, -1, Base.get_world_counter())), + mi = Base.specialize_method(match) + @test isdefined(mi, :cache) + @test mi.cache.max_world === typemax(UInt) + @test mi.cache.invoke != C_NULL + end + end +end + empty!(Base.DEPOT_PATH) append!(Base.DEPOT_PATH, original_depot_path) empty!(Base.LOAD_PATH) From 13be619dc76479f58fcc19fd0f86c2c5e673b0e3 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 8 Jan 2024 19:57:52 -0500 Subject: [PATCH 48/68] static-show: improve accuracy of some printings (#52799) - Show strings with escaping, rather than trying to output the text unmodified. - Show symbols with the same formatting as Strings - Avoid accidentally defining a broken Core.show method for NamedTuple (cherry picked from commit bd3eab649c3ec9d405256afc599f37197f8daec7) --- base/namedtuple.jl | 6 +- base/show.jl | 2 +- src/ast.c | 10 +- src/flisp/print.c | 2 +- src/julia.h | 10 +- src/rtutils.c | 137 +++++++++++++++-------- src/support/utf8.c | 15 +-- src/support/utf8.h | 4 +- stdlib/InteractiveUtils/test/runtests.jl | 2 +- test/show.jl | 16 ++- 10 files changed, 126 insertions(+), 78 deletions(-) diff --git a/base/namedtuple.jl b/base/namedtuple.jl index 687ada6417e11..463051d0af056 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -193,9 +193,8 @@ function convert(::Type{NT}, nt::NamedTuple{names}) where {names, NT<:NamedTuple end if nameof(@__MODULE__) === :Base - Tuple(nt::NamedTuple) = (nt...,) - (::Type{T})(nt::NamedTuple) where {T <: Tuple} = (t = Tuple(nt); t isa T ? t : convert(T, t)::T) -end +Tuple(nt::NamedTuple) = (nt...,) +(::Type{T})(nt::NamedTuple) where {T <: Tuple} = (t = Tuple(nt); t isa T ? t : convert(T, t)::T) function show(io::IO, t::NamedTuple) n = nfields(t) @@ -229,6 +228,7 @@ function show(io::IO, t::NamedTuple) print(io, ")") end end +end eltype(::Type{T}) where T<:NamedTuple = nteltype(T) nteltype(::Type) = Any diff --git a/base/show.jl b/base/show.jl index 6e9089f6a3b99..dd728a1eb31dd 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1744,7 +1744,7 @@ function show_sym(io::IO, sym::Symbol; allow_macroname=false) print(io, '@') show_sym(io, Symbol(sym_str[2:end])) else - print(io, "var", repr(string(sym))) + print(io, "var", repr(string(sym))) # TODO: this is not quite right, since repr uses String escaping rules, and Symbol uses raw string rules end end diff --git a/src/ast.c b/src/ast.c index 06727b453d6a3..c1e1c71733d69 100644 --- a/src/ast.c +++ b/src/ast.c @@ -942,7 +942,7 @@ JL_DLLEXPORT jl_value_t *jl_copy_ast(jl_value_t *expr) return expr; } -JL_DLLEXPORT int jl_is_operator(char *sym) +JL_DLLEXPORT int jl_is_operator(const char *sym) { jl_ast_context_t *ctx = jl_ast_ctx_enter(NULL); fl_context_t *fl_ctx = &ctx->fl; @@ -951,7 +951,7 @@ JL_DLLEXPORT int jl_is_operator(char *sym) return res; } -JL_DLLEXPORT int jl_is_unary_operator(char *sym) +JL_DLLEXPORT int jl_is_unary_operator(const char *sym) { jl_ast_context_t *ctx = jl_ast_ctx_enter(NULL); fl_context_t *fl_ctx = &ctx->fl; @@ -960,7 +960,7 @@ JL_DLLEXPORT int jl_is_unary_operator(char *sym) return res; } -JL_DLLEXPORT int jl_is_unary_and_binary_operator(char *sym) +JL_DLLEXPORT int jl_is_unary_and_binary_operator(const char *sym) { jl_ast_context_t *ctx = jl_ast_ctx_enter(NULL); fl_context_t *fl_ctx = &ctx->fl; @@ -969,7 +969,7 @@ JL_DLLEXPORT int jl_is_unary_and_binary_operator(char *sym) return res; } -JL_DLLEXPORT int jl_is_syntactic_operator(char *sym) +JL_DLLEXPORT int jl_is_syntactic_operator(const char *sym) { jl_ast_context_t *ctx = jl_ast_ctx_enter(NULL); fl_context_t *fl_ctx = &ctx->fl; @@ -978,7 +978,7 @@ JL_DLLEXPORT int jl_is_syntactic_operator(char *sym) return res; } -JL_DLLEXPORT int jl_operator_precedence(char *sym) +JL_DLLEXPORT int jl_operator_precedence(const char *sym) { jl_ast_context_t *ctx = jl_ast_ctx_enter(NULL); fl_context_t *fl_ctx = &ctx->fl; diff --git a/src/flisp/print.c b/src/flisp/print.c index 2b20d0d98b225..a6f633c2e6701 100644 --- a/src/flisp/print.c +++ b/src/flisp/print.c @@ -518,7 +518,7 @@ static void print_string(fl_context_t *fl_ctx, ios_t *f, char *str, size_t sz) } else { while (i < sz) { - size_t n = u8_escape(buf, sizeof(buf), str, &i, sz, 1, 0); + size_t n = u8_escape(buf, sizeof(buf), str, &i, sz, "\"", 0); outsn(fl_ctx, buf, f, n-1); } } diff --git a/src/julia.h b/src/julia.h index 6a152e3cab9a9..cfa5047b38585 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1955,11 +1955,11 @@ 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); -JL_DLLEXPORT int jl_is_operator(char *sym); -JL_DLLEXPORT int jl_is_unary_operator(char *sym); -JL_DLLEXPORT int jl_is_unary_and_binary_operator(char *sym); -JL_DLLEXPORT int jl_is_syntactic_operator(char *sym); -JL_DLLEXPORT int jl_operator_precedence(char *sym); +JL_DLLEXPORT int jl_is_operator(const char *sym); +JL_DLLEXPORT int jl_is_unary_operator(const char *sym); +JL_DLLEXPORT int jl_is_unary_and_binary_operator(const char *sym); +JL_DLLEXPORT int jl_is_syntactic_operator(const char *sym); +JL_DLLEXPORT int jl_operator_precedence(const char *sym); STATIC_INLINE int jl_vinfo_sa(uint8_t vi) { diff --git a/src/rtutils.c b/src/rtutils.c index eefd1b25f9bc4..26d0f57d32c87 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -572,7 +572,7 @@ static size_t jl_show_svec(JL_STREAM *out, jl_svec_t *t, const char *head, const JL_DLLEXPORT int jl_id_start_char(uint32_t wc) JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_id_char(uint32_t wc) JL_NOTSAFEPOINT; -JL_DLLEXPORT int jl_is_identifier(char *str) JL_NOTSAFEPOINT +JL_DLLEXPORT int jl_is_identifier(const char *str) JL_NOTSAFEPOINT { size_t i = 0; uint32_t wc = u8_nextchar(str, &i); @@ -655,22 +655,64 @@ static int is_globfunction(jl_value_t *v, jl_datatype_t *dv, jl_sym_t **globname return 0; } -static size_t jl_static_show_x_sym_escaped(JL_STREAM *out, jl_sym_t *name) JL_NOTSAFEPOINT +static size_t jl_static_show_string(JL_STREAM *out, const char *str, size_t len, int wrap) JL_NOTSAFEPOINT { size_t n = 0; - - char *sn = jl_symbol_name(name); - int hidden = 0; - if (!(jl_is_identifier(sn) || jl_is_operator(sn))) { - hidden = 1; + if (wrap) + n += jl_printf(out, "\""); + if (!u8_isvalid(str, len)) { + // alternate print algorithm that preserves data if it's not UTF-8 + static const char hexdig[] = "0123456789abcdef"; + for (size_t i = 0; i < len; i++) { + uint8_t c = str[i]; + if (c == '\\' || c == '"' || c == '$') + n += jl_printf(out, "\\%c", c); + else if (c >= 32 && c < 0x7f) + n += jl_printf(out, "%c", c); + else + n += jl_printf(out, "\\x%c%c", hexdig[c>>4], hexdig[c&0xf]); + } } - - if (hidden) { - n += jl_printf(out, "var\""); + else { + int special = 0; + for (size_t i = 0; i < len; i++) { + uint8_t c = str[i]; + if (c < 32 || c == 0x7f || c == '\\' || c == '"' || c == '$') { + special = 1; + break; + } + } + if (!special) { + jl_uv_puts(out, str, len); + n += len; + } + else { + char buf[512]; + size_t i = 0; + while (i < len) { + size_t r = u8_escape(buf, sizeof(buf), str, &i, len, "\"$", 0); + jl_uv_puts(out, buf, r - 1); + n += r - 1; + } + } } - n += jl_printf(out, "%s", sn); - if (hidden) { + if (wrap) n += jl_printf(out, "\""); + return n; +} + +static size_t jl_static_show_symbol(JL_STREAM *out, jl_sym_t *name) JL_NOTSAFEPOINT +{ + size_t n = 0; + const char *sn = jl_symbol_name(name); + int quoted = !jl_is_identifier(sn) && !jl_is_operator(sn); + if (quoted) { + n += jl_printf(out, "var"); + // TODO: this is not quite right, since repr uses String escaping rules, and Symbol uses raw string rules + n += jl_static_show_string(out, sn, strlen(sn), 1); + } + else { + n += jl_printf(out, "%s", sn); } return n; } @@ -788,11 +830,6 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt // Types are printed as a fully qualified name, with parameters, e.g. // `Base.Set{Int}`, and function types are printed as e.g. `typeof(Main.f)` jl_datatype_t *dv = (jl_datatype_t*)v; - jl_sym_t *globname; - int globfunc = is_globname_binding(v, dv) && is_globfunction(v, dv, &globname); - jl_sym_t *sym = globfunc ? globname : dv->name->name; - char *sn = jl_symbol_name(sym); - size_t quote = 0; if (dv->name == jl_tuple_typename) { if (dv == jl_tuple_type) return jl_printf(out, "Tuple"); @@ -825,8 +862,13 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt return n; } if (ctx.quiet) { - return jl_printf(out, "%s", jl_symbol_name(dv->name->name)); + return jl_static_show_symbol(out, dv->name->name); } + jl_sym_t *globname; + int globfunc = is_globname_binding(v, dv) && is_globfunction(v, dv, &globname); + jl_sym_t *sym = globfunc ? globname : dv->name->name; + char *sn = jl_symbol_name(sym); + size_t quote = 0; if (globfunc) { n += jl_printf(out, "typeof("); } @@ -839,7 +881,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt quote = 1; } } - n += jl_static_show_x_sym_escaped(out, sym); + n += jl_static_show_symbol(out, sym); if (globfunc) { n += jl_printf(out, ")"); if (quote) { @@ -908,9 +950,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, "nothing"); } else if (vt == jl_string_type) { - n += jl_printf(out, "\""); - jl_uv_puts(out, jl_string_data(v), jl_string_len(v)); n += jl_string_len(v); - n += jl_printf(out, "\""); + n += jl_static_show_string(out, jl_string_data(v), jl_string_len(v), 1); } else if (v == jl_bottom_type) { n += jl_printf(out, "Union{}"); @@ -959,7 +999,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, ")"); n += jl_printf(out, "<:"); } - n += jl_static_show_x_sym_escaped(out, var->name); + n += jl_static_show_symbol(out, var->name); if (showbounds && (ub != (jl_value_t*)jl_any_type || lb != jl_bottom_type)) { // show type-var upper bound if it is defined, or if we showed the lower bound int ua = jl_is_unionall(ub); @@ -977,18 +1017,11 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt 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)); + n += jl_static_show_symbol(out, m->name); } else if (vt == jl_symbol_type) { - char *sn = jl_symbol_name((jl_sym_t*)v); - int quoted = !jl_is_identifier(sn) && jl_operator_precedence(sn) == 0; - if (quoted) - n += jl_printf(out, "Symbol(\""); - else - n += jl_printf(out, ":"); - n += jl_printf(out, "%s", sn); - if (quoted) - n += jl_printf(out, "\")"); + n += jl_printf(out, ":"); + n += jl_static_show_symbol(out, (jl_sym_t*)v); } else if (vt == jl_ssavalue_type) { n += jl_printf(out, "SSAValue(%" PRIuPTR ")", @@ -996,8 +1029,12 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt } else if (vt == jl_globalref_type) { 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); + jl_sym_t *name = jl_globalref_name(v); + n += jl_printf(out, "."); + if (jl_is_operator(jl_symbol_name(name))) + n += jl_printf(out, ":(%s)", jl_symbol_name(name)); + else + n += jl_static_show_symbol(out, name); } else if (vt == jl_gotonode_type) { n += jl_printf(out, "goto %" PRIuPTR, jl_gotonode_label(v)); @@ -1031,17 +1068,17 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt 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, ctx); + 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, ctx); + n += jl_static_show_x(out, jl_exprarg(e, 1), depth, ctx); } else { - char sep = ' '; - n += jl_printf(out, "Expr(:%s", jl_symbol_name(e->head)); + n += jl_printf(out, "Expr("); + n += jl_static_show_x(out, (jl_value_t*)e->head, depth, ctx); 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, ctx); + n += jl_printf(out, ", "); + n += jl_static_show_x(out, jl_exprarg(e, i), depth, ctx); } n += jl_printf(out, ")"); } @@ -1128,7 +1165,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt } } - n += jl_static_show_x_sym_escaped(out, sym); + n += jl_static_show_symbol(out, sym); if (globfunc) { if (quote) { @@ -1164,8 +1201,14 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt jl_value_t *names = isnamedtuple ? jl_tparam0(vt) : (jl_value_t*)jl_field_names(vt); for (; i < tlen; i++) { if (!istuple) { - jl_value_t *fname = isnamedtuple ? jl_fieldref_noalloc(names, i) : jl_svecref(names, i); - n += jl_printf(out, "%s=", jl_symbol_name((jl_sym_t*)fname)); + jl_sym_t *fname = (jl_sym_t*)(isnamedtuple ? jl_fieldref_noalloc(names, i) : jl_svecref(names, i)); + if (fname == NULL || !jl_is_symbol(fname)) + n += jl_static_show_x(out, (jl_value_t*)fname, depth, ctx); + else if (jl_is_operator(jl_symbol_name(fname))) + n += jl_printf(out, "(%s)", jl_symbol_name(fname)); + else + n += jl_static_show_symbol(out, fname); + n += jl_printf(out, "="); } size_t offs = jl_field_offset(vt, i); char *fld_ptr = (char*)v + offs; @@ -1300,7 +1343,7 @@ size_t jl_static_show_func_sig_(JL_STREAM *s, jl_value_t *type, jl_static_show_c if ((jl_nparams(ftype) == 0 || ftype == ((jl_datatype_t*)ftype)->name->wrapper) && ((jl_datatype_t*)ftype)->name->mt != jl_type_type_mt && ((jl_datatype_t*)ftype)->name->mt != jl_nonfunction_mt) { - n += jl_printf(s, "%s", jl_symbol_name(((jl_datatype_t*)ftype)->name->mt->name)); + n += jl_static_show_symbol(s, ((jl_datatype_t*)ftype)->name->mt->name); } else { n += jl_printf(s, "(::"); @@ -1399,10 +1442,10 @@ void jl_log(int level, jl_value_t *module, jl_value_t *group, jl_value_t *id, } jl_printf(str, "\n@ "); if (jl_is_string(file)) { - jl_uv_puts(str, jl_string_data(file), jl_string_len(file)); + jl_static_show_string(str, jl_string_data(file), jl_string_len(file), 0); } else if (jl_is_symbol(file)) { - jl_printf(str, "%s", jl_symbol_name((jl_sym_t*)file)); + jl_static_show_string(str, jl_symbol_name((jl_sym_t*)file), strlen(jl_symbol_name((jl_sym_t*)file)), 0); } jl_printf(str, ":"); jl_static_show(str, line); diff --git a/src/support/utf8.c b/src/support/utf8.c index 42a420fb0c499..17dcf5f1efd51 100644 --- a/src/support/utf8.c +++ b/src/support/utf8.c @@ -410,7 +410,7 @@ int u8_escape_wchar(char *buf, size_t sz, uint32_t ch) } size_t u8_escape(char *buf, size_t sz, const char *src, size_t *pi, size_t end, - int escape_quotes, int ascii) + const char *escapes, int ascii) { size_t i = *pi, i0; uint32_t ch; @@ -420,12 +420,9 @@ size_t u8_escape(char *buf, size_t sz, const char *src, size_t *pi, size_t end, while (i 0x9f) return 0; - // Check for overlong encoding - if (byt == 0xe0 && *pnt < 0xa0) return 0; + // Check for overlong encoding + if (byt == 0xe0 && *pnt < 0xa0) return 0; pnt += 2; } else { // 4-byte sequence // Must have 3 valid continuation characters diff --git a/src/support/utf8.h b/src/support/utf8.h index a3d89f2268b41..eab86f602ee61 100644 --- a/src/support/utf8.h +++ b/src/support/utf8.h @@ -63,7 +63,7 @@ int u8_escape_wchar(char *buf, size_t sz, uint32_t ch); sz is buf size in bytes. must be at least 12. - if escape_quotes is nonzero, quote characters will be escaped. + if escapes is given, given characters will also be escaped (in addition to \\). if ascii is nonzero, the output is 7-bit ASCII, no UTF-8 survives. @@ -75,7 +75,7 @@ int u8_escape_wchar(char *buf, size_t sz, uint32_t ch); returns number of bytes placed in buf, including a NUL terminator. */ size_t u8_escape(char *buf, size_t sz, const char *src, size_t *pi, size_t end, - int escape_quotes, int ascii); + const char *escapes, int ascii); /* utility predicates used by the above */ int octal_digit(char c); diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index d93ee299ba834..73af49f8ef0c3 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -364,7 +364,7 @@ let err = tempname(), @test startswith(errstr, """start end Internal error: encountered unexpected error during compilation of f_broken_code: - ErrorException(\"unsupported or misplaced expression \"invalid\" in function f_broken_code\") + ErrorException(\"unsupported or misplaced expression \\\"invalid\\\" in function f_broken_code\") """) || errstr @test !endswith(errstr, "\nend\n") || errstr end diff --git a/test/show.jl b/test/show.jl index 9b6becbf95b1d..6c28d0c930932 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1446,12 +1446,20 @@ end @test static_shown(:+) == ":+" @test static_shown(://) == "://" @test static_shown(://=) == "://=" -@test static_shown(Symbol("")) == "Symbol(\"\")" -@test static_shown(Symbol("a/b")) == "Symbol(\"a/b\")" -@test static_shown(Symbol("a-b")) == "Symbol(\"a-b\")" +@test static_shown(Symbol("")) == ":var\"\"" +@test static_shown(Symbol("a/b")) == ":var\"a/b\"" +@test static_shown(Symbol("a-b")) == ":var\"a-b\"" @test static_shown(UnionAll) == "UnionAll" - @test static_shown(QuoteNode(:x)) == ":(:x)" +@test static_shown(:!) == ":!" +@test static_shown("\"") == "\"\\\"\"" +@test static_shown("\$") == "\"\\\$\"" +@test static_shown("\\") == "\"\\\\\"" +@test static_shown("a\x80b") == "\"a\\x80b\"" +@test static_shown("a\x80\$\\b") == "\"a\\x80\\\$\\\\b\"" +@test static_shown(GlobalRef(Main, :var"a#b")) == "Main.var\"a#b\"" +@test static_shown(GlobalRef(Main, :+)) == "Main.:(+)" +@test static_shown((a = 3, ! = 4, var"a b" = 5)) == "(a=3, (!)=4, var\"a b\"=5)" # PR #38049 @test static_shown(sum) == "Base.sum" From 94331522e1ddaa0eb47ff3fe97eb495aeca47652 Mon Sep 17 00:00:00 2001 From: Sam Schweigel <33556084+xal-0@users.noreply.github.com> Date: Thu, 29 May 2025 08:08:40 -0700 Subject: [PATCH 49/68] Make more types jl_static_show unambiguously (#58512) Makes more types survive `jl_static_show` unambiguously: - Symbols - Symbols printed in the `:var"foo"` form use raw string escaping, fixing `:var"a\b"`, `:var"a\\"`, `:var"$a"`, etc. - Symbols that require parens use parens (`:(=)`, ...) - Signed integers: Except for `Int`, signed integers print like `Int8(1)`. - Floats: floats are printed in a naive but reversible (TODO: double check) way. `Inf(16|32|)` and `NaN(16|32|)` are printed, and `Float16`/`Float32` print the type (`Float32(1.5)`). `Float64`s are printed with a trailing `.0` if it is necessary to disambiguate from `Int`. Fixes #52677, https://github.com/JuliaLang/julia/issues/58484#issuecomment-2902468354, https://github.com/JuliaLang/julia/issues/58484#issuecomment-2898733637, and the specific case mentioned in #58484. Improves the situation for round-trip (inexhaustive list): - Non-canonical NaNs - BFloat16 - User-defined primitive types. This one is tricky, because they can have a size different from any type we have literals for. (cherry picked from commit b03ef6b680703267e48d56b1e7b2dbbe81e52be2) --- src/rtutils.c | 123 ++++++++++++++++++++++++++++++++++++++++++-------- test/show.jl | 54 +++++++++++++++++++++- 2 files changed, 157 insertions(+), 20 deletions(-) diff --git a/src/rtutils.c b/src/rtutils.c index 26d0f57d32c87..7b6bbae1c3070 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -5,6 +5,8 @@ */ #include "platform.h" +#include +#include #include #include #include @@ -655,12 +657,12 @@ static int is_globfunction(jl_value_t *v, jl_datatype_t *dv, jl_sym_t **globname return 0; } -static size_t jl_static_show_string(JL_STREAM *out, const char *str, size_t len, int wrap) JL_NOTSAFEPOINT +static size_t jl_static_show_string(JL_STREAM *out, const char *str, size_t len, int wrap, int raw) JL_NOTSAFEPOINT { size_t n = 0; if (wrap) n += jl_printf(out, "\""); - if (!u8_isvalid(str, len)) { + if (!raw && !u8_isvalid(str, len)) { // alternate print algorithm that preserves data if it's not UTF-8 static const char hexdig[] = "0123456789abcdef"; for (size_t i = 0; i < len; i++) { @@ -677,7 +679,11 @@ static size_t jl_static_show_string(JL_STREAM *out, const char *str, size_t len, int special = 0; for (size_t i = 0; i < len; i++) { uint8_t c = str[i]; - if (c < 32 || c == 0x7f || c == '\\' || c == '"' || c == '$') { + if (raw && ((c == '\\' && i == len-1) || c == '"')) { + special = 1; + break; + } + else if (!raw && (c < 32 || c == 0x7f || c == '\\' || c == '"' || c == '$')) { special = 1; break; } @@ -686,6 +692,25 @@ static size_t jl_static_show_string(JL_STREAM *out, const char *str, size_t len, jl_uv_puts(out, str, len); n += len; } + else if (raw) { + // REF: Base.escape_raw_string + int escapes = 0; + for (size_t i = 0; i < len; i++) { + uint8_t c = str[i]; + if (c == '\\') { + escapes++; + } + else { + if (c == '"') + for (escapes++; escapes > 0; escapes--) + n += jl_printf(out, "\\"); + escapes = 0; + } + n += jl_printf(out, "%c", str[i]); + } + for (; escapes > 0; escapes--) + n += jl_printf(out, "\\"); + } else { char buf[512]; size_t i = 0; @@ -701,18 +726,28 @@ static size_t jl_static_show_string(JL_STREAM *out, const char *str, size_t len, return n; } +static int jl_is_quoted_sym(const char *sn) +{ + static const char *const quoted_syms[] = {":", "::", ":=", "=", "==", "===", "=>", "`"}; + for (int i = 0; i < sizeof quoted_syms / sizeof *quoted_syms; i++) + if (!strcmp(sn, quoted_syms[i])) + return 1; + return 0; +} + +// TODO: in theory, we need a separate function for showing symbols in an +// expression context (where `Symbol("foo\x01bar")` is ok) and a syntactic +// context (where var"" must be used). static size_t jl_static_show_symbol(JL_STREAM *out, jl_sym_t *name) JL_NOTSAFEPOINT { size_t n = 0; const char *sn = jl_symbol_name(name); - int quoted = !jl_is_identifier(sn) && !jl_is_operator(sn); - if (quoted) { - n += jl_printf(out, "var"); - // TODO: this is not quite right, since repr uses String escaping rules, and Symbol uses raw string rules - n += jl_static_show_string(out, sn, strlen(sn), 1); + if (jl_is_identifier(sn) || (jl_is_operator(sn) && !jl_is_quoted_sym(sn))) { + n += jl_printf(out, "%s", sn); } else { - n += jl_printf(out, "%s", sn); + n += jl_printf(out, "var"); + n += jl_static_show_string(out, sn, strlen(sn), 1, 1); } return n; } @@ -741,6 +776,51 @@ static int jl_static_is_function_(jl_datatype_t *vt) JL_NOTSAFEPOINT { return 0; } +static size_t jl_static_show_float(JL_STREAM *out, double v, + jl_datatype_t *vt) JL_NOTSAFEPOINT +{ + size_t n = 0; + // TODO: non-canonical NaNs do not round-trip + // TOOD: BFloat16 + const char *size_suffix = vt == jl_float16_type ? "16" : + vt == jl_float32_type ? "32" : + ""; + // Requires minimum 1 (sign) + 17 (sig) + 1 (dot) + 5 ("e-123") + 1 (null) + char buf[32]; + // Base B significand digits required to print n base-b significand bits + // (including leading 1): N = 2 + floor(n/log(b, B)) + // Float16 5 + // Float32 9 + // Float64 17 + // REF: https://dl.acm.org/doi/pdf/10.1145/93542.93559 + if (isnan(v)) { + n += jl_printf(out, "NaN%s", size_suffix); + } + else if (isinf(v)) { + n += jl_printf(out, "%sInf%s", v < 0 ? "-" : "", size_suffix); + } + else if (vt == jl_float64_type) { + n += jl_printf(out, "%#.17g", v); + } + else if (vt == jl_float32_type) { + size_t m = snprintf(buf, sizeof buf, "%.9g", v); + // If the exponent was printed, replace it with 'f' + char *p = (char *)memchr(buf, 'e', m); + if (p) + *p = 'f'; + jl_uv_puts(out, buf, m); + n += m; + // If no exponent was printed, we must add one + if (!p) + n += jl_printf(out, "f0"); + } + else { + assert(vt == jl_float16_type); + n += jl_printf(out, "Float16(%#.5g)", v); + } + return n; +} + // `v` might be pointing to a field inlined in a structure therefore // `jl_typeof(v)` may not be the same with `vt` and only `vt` should be // used to determine the type of the value. @@ -906,17 +986,21 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt int f = *(uint32_t*)jl_data_ptr(v); n += jl_printf(out, "#", f, jl_intrinsic_name(f)); } + else if (vt == jl_long_type) { + // Avoid unnecessary Int64(x)/Int32(x) + n += jl_printf(out, "%" PRIdPTR, *(intptr_t*)v); + } else if (vt == jl_int64_type) { - n += jl_printf(out, "%" PRId64, *(int64_t*)v); + n += jl_printf(out, "Int64(%" PRId64 ")", *(int64_t*)v); } else if (vt == jl_int32_type) { - n += jl_printf(out, "%" PRId32, *(int32_t*)v); + n += jl_printf(out, "Int32(%" PRId32 ")", *(int32_t*)v); } else if (vt == jl_int16_type) { - n += jl_printf(out, "%" PRId16, *(int16_t*)v); + n += jl_printf(out, "Int16(%" PRId16 ")", *(int16_t*)v); } else if (vt == jl_int8_type) { - n += jl_printf(out, "%" PRId8, *(int8_t*)v); + n += jl_printf(out, "Int8(%" PRId8 ")", *(int8_t*)v); } else if (vt == jl_uint64_type) { n += jl_printf(out, "0x%016" PRIx64, *(uint64_t*)v); @@ -937,11 +1021,14 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, "0x%08" PRIx32, *(uint32_t*)v); #endif } + else if (vt == jl_float16_type) { + n += jl_static_show_float(out, julia__gnu_h2f_ieee(*(uint16_t *)v), vt); + } else if (vt == jl_float32_type) { - n += jl_printf(out, "%gf", *(float*)v); + n += jl_static_show_float(out, *(float *)v, vt); } else if (vt == jl_float64_type) { - n += jl_printf(out, "%g", *(double*)v); + n += jl_static_show_float(out, *(double *)v, vt); } else if (vt == jl_bool_type) { n += jl_printf(out, "%s", *(uint8_t*)v ? "true" : "false"); @@ -950,7 +1037,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, "nothing"); } else if (vt == jl_string_type) { - n += jl_static_show_string(out, jl_string_data(v), jl_string_len(v), 1); + n += jl_static_show_string(out, jl_string_data(v), jl_string_len(v), 1, 0); } else if (v == jl_bottom_type) { n += jl_printf(out, "Union{}"); @@ -1442,10 +1529,10 @@ void jl_log(int level, jl_value_t *module, jl_value_t *group, jl_value_t *id, } jl_printf(str, "\n@ "); if (jl_is_string(file)) { - jl_static_show_string(str, jl_string_data(file), jl_string_len(file), 0); + jl_static_show_string(str, jl_string_data(file), jl_string_len(file), 0, 0); } else if (jl_is_symbol(file)) { - jl_static_show_string(str, jl_symbol_name((jl_sym_t*)file), strlen(jl_symbol_name((jl_sym_t*)file)), 0); + jl_static_show_string(str, jl_symbol_name((jl_sym_t*)file), strlen(jl_symbol_name((jl_sym_t*)file)), 0, 0); } jl_printf(str, ":"); jl_static_show(str, line); diff --git a/test/show.jl b/test/show.jl index 6c28d0c930932..5f92d1561015c 100644 --- a/test/show.jl +++ b/test/show.jl @@ -696,7 +696,7 @@ let oldout = stdout, olderr = stderr redirect_stderr(olderr) close(wrout) close(wrerr) - @test fetch(out) == "Int64 <: Signed\nTESTA\nTESTB\nΑ1Β2\"A\"\nA\n123\"C\"\n" + @test fetch(out) == "Int64 <: Signed\nTESTA\nTESTB\nΑ1Β2\"A\"\nA\n123.0000000000000000\"C\"\n" @test fetch(err) == "TESTA\nTESTB\nΑ1Β2\"A\"\n" finally redirect_stdout(oldout) @@ -1489,8 +1489,58 @@ struct var"%X%" end # Invalid name without '#' typeof(+), var"#f#", typeof(var"#f#"), + + # Integers should round-trip (#52677) + 1, UInt(1), + Int8(1), Int16(1), Int32(1), Int64(1), + UInt8(1), UInt16(1), UInt32(1), UInt64(1), + + # Float round-trip + Float16(1), Float32(1), Float64(1), + Float16(1.5), Float32(1.5), Float64(1.5), + Float16(0.4893243538921085), Float32(0.4893243538921085), Float64(0.4893243538921085), + # Examples that require the full 5, 9, and 17 digits of precision + Float16(0.00010014), Float32(1.00000075f-36), Float64(-1.561051336605761e-182), + floatmax(Float16), floatmax(Float32), floatmax(Float64), + floatmin(Float16), floatmin(Float32), floatmin(Float64), + Float16(0.0), 0.0f0, 0.0, + Float16(-0.0), -0.0f0, -0.0, + Inf16, Inf32, Inf, + -Inf16, -Inf32, -Inf, + nextfloat(Float16(0)), nextfloat(Float32(0)), nextfloat(Float64(0)), + NaN16, NaN32, NaN, + Float16(1e3), 1f7, 1e16, + Float16(-1e3), -1f7, -1e16, + Float16(1e4), 1f8, 1e17, + Float16(-1e4), -1f8, -1e17, + + # :var"" escaping rules differ from strings (#58484) + :foo, + :var"bar baz", + :var"a $b", # No escaping for $ in raw string + :var"a\b", # No escaping for backslashes in middle + :var"a\\", # Backslashes must be escaped at the end + :var"a\\\\", + :var"a\"b", + :var"a\"", + :var"\\\"", + :+, :var"+-", + :(=), :(:), :(::), # Requires quoting + Symbol("a\nb"), + + Val(Float16(1.0)), Val(1f0), Val(1.0), + Val(:abc), Val(:(=)), Val(:var"a\b"), + + Val(1), Val(Int8(1)), Val(Int16(1)), Val(Int32(1)), Val(Int64(1)), Val(Int128(1)), + Val(UInt(1)), Val(UInt8(1)), Val(UInt16(1)), Val(UInt32(1)), Val(UInt64(1)), Val(UInt128(1)), + + # BROKEN + # Symbol("a\xffb"), + # User-defined primitive types + # Non-canonical NaNs + # BFloat16 ) - @test v == eval(Meta.parse(static_shown(v))) + @test v === eval(Meta.parse(static_shown(v))) end end From c3bd899d2867f55862a8f1cdb17ba78eb672d4ad Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Fri, 30 May 2025 17:32:18 -0400 Subject: [PATCH 50/68] Make `Ptr` values static-show w/ type-information (#58584) Small follow-up to https://github.com/JuliaLang/julia/pull/58512/ (cherry picked from commit 36bd3ad86b2962e2ffe497e756d68e0aa432d867) --- src/rtutils.c | 7 ------- test/show.jl | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/rtutils.c b/src/rtutils.c index 7b6bbae1c3070..3710aba3429c2 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -1014,13 +1014,6 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt else if (vt == jl_uint8_type) { n += jl_printf(out, "0x%02" PRIx8, *(uint8_t*)v); } - else if (jl_pointer_type && jl_is_cpointer_type((jl_value_t*)vt)) { -#ifdef _P64 - n += jl_printf(out, "0x%016" PRIx64, *(uint64_t*)v); -#else - n += jl_printf(out, "0x%08" PRIx32, *(uint32_t*)v); -#endif - } else if (vt == jl_float16_type) { n += jl_static_show_float(out, julia__gnu_h2f_ieee(*(uint16_t *)v), vt); } diff --git a/test/show.jl b/test/show.jl index 5f92d1561015c..ae4d65967ba0b 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1514,6 +1514,9 @@ struct var"%X%" end # Invalid name without '#' Float16(1e4), 1f8, 1e17, Float16(-1e4), -1f8, -1e17, + # Pointers should round-trip + Ptr{Cvoid}(0), Ptr{Cvoid}(typemax(UInt)), Ptr{Any}(0), Ptr{Any}(typemax(UInt)), + # :var"" escaping rules differ from strings (#58484) :foo, :var"bar baz", From 549282d5a54aed37be169db7b2d1ec568f847f4e Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Sat, 31 May 2025 18:58:56 +0200 Subject: [PATCH 51/68] relax dispatch for the `IteratorSize` method for `Generator` (#58110) Fixes #58109 (cherry picked from commit 805f85f6e957af6d8ecdfd1ef0f5887ae4c44467) --- base/generator.jl | 2 +- test/iterators.jl | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/base/generator.jl b/base/generator.jl index aa4b7f67cba95..da735bf59bccf 100644 --- a/base/generator.jl +++ b/base/generator.jl @@ -97,7 +97,7 @@ 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{<:Generator{I}}) where {I} = (@isdefined I) ? IteratorSize(I) : SizeUnknown() haslength(iter) = IteratorSize(iter) isa Union{HasShape, HasLength} diff --git a/test/iterators.jl b/test/iterators.jl index a60ec32bb9ac0..786dcfd950624 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -959,6 +959,12 @@ end @test Iterators.tail_if_any((1,)) == () end +@testset "issue #58109" begin + i = Iterators.map(identity, 3) + j = Iterators.map(sqrt, 7) + @test (@inferred Base.IteratorSize(i)) === @inferred Base.IteratorSize(eltype([i, j])) +end + @testset "IteratorSize trait for zip" begin @test Base.IteratorSize(zip()) == Base.IsInfinite() # for zip of empty tuple @test Base.IteratorSize(zip((1,2,3), repeated(0))) == Base.HasLength() # for zip of ::HasLength and ::IsInfinite From ee8dd0fec15d2fb62a80762e644ce6194b9483e0 Mon Sep 17 00:00:00 2001 From: Klaus Crusius Date: Mon, 22 Apr 2024 05:47:57 +0200 Subject: [PATCH 52/68] power uses Float64 exponents for integers (#53967) Improve performance of `^(::Float64, n::Integer)` in the case of `abs(n) > 2^13`. While `pow_body` is unreliable for `abs(n) > 2^25` this implementation provides errors of a few ULPs, while runtime is capped to that of the `Float64` implementation. Fixes #53881 See also #53886. (cherry picked from commit fe49d5661833e512a557913591514fcd32f7b783) --- base/math.jl | 54 +++++++++++++++++++++++++++++++++++---------- base/special/exp.jl | 2 +- test/math.jl | 19 ++++++++++++++++ 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/base/math.jl b/base/math.jl index 0e2b7fb7c6397..8178099a4aaf8 100644 --- a/base/math.jl +++ b/base/math.jl @@ -1189,6 +1189,10 @@ function modf(x::T) where T<:IEEEFloat return (rx, ix) end +@inline function use_power_by_squaring(n::Integer) + -2^12 <= n <= 3 * 2^13 +end + # @constprop aggressive to help the compiler see the switch between the integer and float # variants for callers with constant `y` @constprop :aggressive function ^(x::Float64, y::Float64) @@ -1201,24 +1205,33 @@ end y = sign(y)*0x1.8p62 end yint = unsafe_trunc(Int64, y) # This is actually safe since julia freezes the result - y == yint && return @noinline x^yint - 2*xu==0 && return abs(y)*Inf*(!(y>0)) # if x==0 - x<0 && throw_exp_domainerror(x) # |y| is small enough that y isn't an integer - !isfinite(x) && return x*(y>0 || isnan(x)) # x is inf or NaN + yisint = y == yint + if yisint + yint == 0 && return 1.0 + use_power_by_squaring(yint) && return @noinline pow_body(x, yint) + end + 2*xu==0 && return abs(y)*Inf*(!(y>0)) # if x === +0.0 or -0.0 (Inf * false === 0.0) + s = 1 + if x < 0 + !yisint && throw_exp_domainerror(x) # y isn't an integer + s = ifelse(isodd(yint), -1, 1) + end + !isfinite(x) && return copysign(x,s)*(y>0 || isnan(x)) # x is inf or NaN + return copysign(pow_body(abs(x), y), s) +end + +@assume_effects :foldable @noinline function pow_body(x::Float64, y::Float64) + xu = reinterpret(UInt64, x) if xu < (UInt64(1)<<52) # x is subnormal xu = reinterpret(UInt64, x * 0x1p52) # normalize x xu &= ~sign_mask(Float64) xu -= UInt64(52) << 52 # mess with the exponent end - return pow_body(xu, y) -end - -@inline function pow_body(xu::UInt64, y::Float64) - logxhi,logxlo = Base.Math._log_ext(xu) + logxhi,logxlo = _log_ext(xu) xyhi, xylo = two_mul(logxhi,y) xylo = muladd(logxlo, y, xylo) hi = xyhi+xylo - return Base.Math.exp_impl(hi, xylo-(hi-xyhi), Val(:ℯ)) + return @inline Base.Math.exp_impl(hi, xylo-(hi-xyhi), Val(:ℯ)) end @constprop :aggressive function ^(x::T, y::T) where T <: Union{Float16, Float32} @@ -1242,12 +1255,29 @@ end return T(exp2(log2(abs(widen(x))) * y)) end -# compensated power by squaring @constprop :aggressive @inline function ^(x::Float64, n::Integer) + x^clamp(n, Int64) +end +@constprop :aggressive @inline function ^(x::Float64, n::Int64) n == 0 && return one(x) - return pow_body(x, n) + if use_power_by_squaring(n) + return pow_body(x, n) + else + s = ifelse(x < 0 && isodd(n), -1.0, 1.0) + x = abs(x) + y = float(n) + if y == n + return copysign(pow_body(x, y), s) + else + n2 = n % 1024 + y = float(n - n2) + return pow_body(x, y) * copysign(pow_body(x, n2), s) + end + end end +# compensated power by squaring +# this method is only reliable for -2^20 < n < 2^20 (cf. #53881 #53886) @assume_effects :terminates_locally @noinline function pow_body(x::Float64, n::Integer) y = 1.0 xnlo = ynlo = 0.0 diff --git a/base/special/exp.jl b/base/special/exp.jl index 32de6b9be296d..38d7509807aed 100644 --- a/base/special/exp.jl +++ b/base/special/exp.jl @@ -252,7 +252,7 @@ end twopk = (k + UInt64(53)) << 52 return reinterpret(T, twopk + reinterpret(UInt64, small_part))*0x1p-53 end - #k == 1024 && return (small_part * 2.0) * 2.0^1023 + k == 1024 && return (small_part * 2.0) * 2.0^1023 end twopk = Int64(k) << 52 return reinterpret(T, twopk + reinterpret(Int64, small_part)) diff --git a/test/math.jl b/test/math.jl index bd00f3417ecd3..3f0a8402c9575 100644 --- a/test/math.jl +++ b/test/math.jl @@ -1453,6 +1453,25 @@ end # two cases where we have observed > 1 ULP in the past @test 0.0013653274095082324^-97.60372292227069 == 4.088393948750035e279 @test 8.758520413376658e-5^70.55863059215994 == 5.052076767078296e-287 + + # issue #53881 + c53881 = 2.2844135865398217e222 # check correctness within 2 ULPs + @test prevfloat(1.0) ^ -Int64(2)^62 ≈ c53881 atol=2eps(c53881) + @test 2.0 ^ typemin(Int) == 0.0 + @test (-1.0) ^ typemin(Int) == 1.0 + Z = Int64(2) + E = prevfloat(1.0) + @test E ^ (-Z^54) ≈ 7.38905609893065 + @test E ^ (-Z^62) ≈ 2.2844135865231613e222 + @test E ^ (-Z^63) == Inf + @test abs(E ^ (Z^62-1) * E ^ (-Z^62+1) - 1) <= eps(1.0) + n, x = -1065564664, 0.9999997040311492 + @test abs(x^n - Float64(big(x)^n)) / eps(x^n) == 0 # ULPs + @test E ^ (big(2)^100 + 1) == 0 + @test E ^ 6705320061009595392 == nextfloat(0.0) + n = Int64(1024 / log2(E)) + @test E^n == Inf + @test E^float(n) == Inf end @testset "special function `::Real` fallback shouldn't recur without bound, issue #57789" begin From ec1115df76239ab277d4892c0c5f6ae429cfa2e7 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Mon, 14 Apr 2025 13:38:35 -0400 Subject: [PATCH 53/68] remove unnecessary edge from `exp_impl` to `pow` (#58062) This was just intended to be a constant and we have binary Float64 constants (cherry picked from commit f3474b5591b6dda800a88d1819d87b0c34edbfb1) --- base/special/exp.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/special/exp.jl b/base/special/exp.jl index 38d7509807aed..f0db1f70a354b 100644 --- a/base/special/exp.jl +++ b/base/special/exp.jl @@ -224,7 +224,7 @@ end twopk = (k + UInt64(53)) << 52 return reinterpret(T, twopk + reinterpret(UInt64, small_part))*0x1p-53 end - #k == 1024 && return (small_part * 2.0) * 2.0^1023 + #k == 1024 && return (small_part * 2.0) * 0x1p1023 end twopk = Int64(k) << 52 return reinterpret(T, twopk + reinterpret(Int64, small_part)) @@ -252,7 +252,7 @@ end twopk = (k + UInt64(53)) << 52 return reinterpret(T, twopk + reinterpret(UInt64, small_part))*0x1p-53 end - k == 1024 && return (small_part * 2.0) * 2.0^1023 + k == 1024 && return (small_part * 2.0) * 0x1p1023 end twopk = Int64(k) << 52 return reinterpret(T, twopk + reinterpret(Int64, small_part)) From d5b2b2a813bb22706c5e7f6bb3d187e32664f423 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 22 May 2024 20:50:33 +0200 Subject: [PATCH 54/68] Fixes for bitcast bugs with LLVM 17 / opaque pointers (#54548) Skip setName on folded inputs, and ensure the correct pointer address space is used. (cherry picked from commit baca8baea0e62f06530a9640153d793a69eba7f7) --- src/intrinsics.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 9c163458f22d2..fad6934706795 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -629,10 +629,24 @@ static jl_cgval_t generic_bitcast(jl_codectx_t &ctx, const jl_cgval_t *argv) setName(ctx.emission_context, vx, "bitcast_coercion"); } else if (!vxt->isPointerTy() && llvmt->isPointerTy()) { vx = emit_inttoptr(ctx, vx, llvmt); - setName(ctx.emission_context, vx, "bitcast_coercion"); + if (isa(vx) && !vx->hasName()) + // emit_inttoptr may undo an PtrToInt + setName(ctx.emission_context, vx, "bitcast_coercion"); + } else if (vxt->isPointerTy() && llvmt->isPointerTy()) { + // emit_bitcast preserves the origin address space, which we can't have here + #if JL_LLVM_VERSION >= 170000 + vx = ctx.builder.CreateAddrSpaceCast(vx, llvmt); + #else + vx = ctx.builder.CreatePointerBitCastOrAddrSpaceCast(vx, llvmt); + #endif + if (isa(vx) && !vx->hasName()) + // cast may have been folded + setName(ctx.emission_context, vx, "bitcast_coercion"); } else { vx = emit_bitcast(ctx, vx, llvmt); - setName(ctx.emission_context, vx, "bitcast_coercion"); + if (isa(vx) && !vx->hasName()) + // emit_bitcast may undo another bitcast + setName(ctx.emission_context, vx, "bitcast_coercion"); } } From 967a25639081eb56db880575d5cb6d159d1e2603 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 25 Mar 2025 08:59:40 -0400 Subject: [PATCH 55/68] Profile: remove scope from profile macros (#57858) Fixes this, which breaks expectations from the way `@time` doesn't introduce a new scope. ``` julia> using Profile julia> @profile x = 1 1 julia> x ERROR: UndefVarError: `x` not defined in `Main` Suggestion: check for spelling errors or missing imports. ``` (cherry picked from commit d6af199c5d489d5964cddc73425bfdc1a002ddfa) --- stdlib/Profile/src/Allocs.jl | 6 +++--- stdlib/Profile/src/Profile.jl | 8 ++++---- stdlib/Profile/test/runtests.jl | 10 ++++++++++ 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/stdlib/Profile/src/Allocs.jl b/stdlib/Profile/src/Allocs.jl index e45f4dca9607f..0b10781d39ba4 100644 --- a/stdlib/Profile/src/Allocs.jl +++ b/stdlib/Profile/src/Allocs.jl @@ -73,11 +73,11 @@ end function _prof_expr(expr, opts) quote $start(; $(esc(opts))) - try + Base.@__tryfinally( $(esc(expr)) - finally + , $stop() - end + ) end end diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index f88bc45e2dee4..2501d3b200dea 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -39,12 +39,12 @@ appended to an internal buffer of backtraces. """ macro profile(ex) return quote - try - start_timer() + start_timer() + Base.@__tryfinally( $(esc(ex)) - finally + , stop_timer() - end + ) end end diff --git a/stdlib/Profile/test/runtests.jl b/stdlib/Profile/test/runtests.jl index 2d6df81b1015d..183128205adb2 100644 --- a/stdlib/Profile/test/runtests.jl +++ b/stdlib/Profile/test/runtests.jl @@ -117,6 +117,16 @@ end @test z == 10 end +@testset "@profile no scope" begin + @profile no_scope_57858_1 = 1 + @test @isdefined no_scope_57858_1 + Profile.clear() + + Profile.Allocs.@profile no_scope_57858_2 = 1 + @test @isdefined no_scope_57858_2 + Profile.Allocs.clear() +end + @testset "setting sample count and delay in init" begin n_, delay_ = Profile.init() n_original = n_ From 5e6b56ac067c452ff7abef4cc848c09b8d82bc83 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 4 Apr 2025 09:30:32 -0400 Subject: [PATCH 56/68] Logging: Improve threadsafety (#57591) Closes https://github.com/JuliaLang/julia/issues/57376 Closes https://github.com/JuliaLang/julia/issues/34037 - Adds a lock in `SimpleLogger` and `ConsoleLogger` for use on maxlog tracking and stream writes to improve threadsafety. Closely similar to https://github.com/JuliaLang/julia/pull/54497 - Turns the internal `_min_enabled_level` into a `Threads.Atomic`. There are [some direct interactions](https://juliahub.com/ui/Search?type=code&q=_min_enabled_level&w=true) to this internal in the ecosystem, but they should still work ``` julia> Base.CoreLogging._min_enabled_level[] = Logging.Info+1 LogLevel(1) ``` - Brings tests over from https://github.com/JuliaLang/julia/pull/57448 Performance seems highly similar: ``` julia> @time for i in 1:10000 @info "foo" maxlog=10000000 end [ Info: foo ... 0.481446 seconds (1.33 M allocations: 89.226 MiB, 0.49% gc time) ``` ``` 0.477235 seconds (1.31 M allocations: 79.002 MiB, 1.77% gc time) ``` (cherry picked from commit 9af96508e9715e22154fc7b5a7283ad41d23765a) --- base/Base.jl | 8 +++--- base/logging.jl | 36 ++++++++++++++++-------- stdlib/Logging/src/ConsoleLogger.jl | 21 +++++++++----- stdlib/Logging/test/runtests.jl | 43 +++++++++++++++++++++++++++++ stdlib/Logging/test/threads_exec.jl | 13 +++++++++ stdlib/Test/src/logging.jl | 2 +- 6 files changed, 99 insertions(+), 24 deletions(-) create mode 100644 stdlib/Logging/test/threads_exec.jl diff --git a/base/Base.jl b/base/Base.jl index 6ed423a799e4e..4de06175a6a6f 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -330,10 +330,6 @@ using .Libc: getpid, gethostname, time, memcpy, memset, memmove, memcmp const libblas_name = "libblastrampoline" * (Sys.iswindows() ? "-5" : "") const liblapack_name = libblas_name -# Logging -include("logging.jl") -using .CoreLogging - # Concurrency include("linked_list.jl") include("condition.jl") @@ -345,6 +341,10 @@ include("task.jl") include("threads_overloads.jl") include("weakkeydict.jl") +# Logging +include("logging.jl") +using .CoreLogging + include("env.jl") # functions defined in Random diff --git a/base/logging.jl b/base/logging.jl index 96c133a239cc0..49f0a52600102 100644 --- a/base/logging.jl +++ b/base/logging.jl @@ -131,6 +131,7 @@ isless(a::LogLevel, b::LogLevel) = isless(a.level, b.level) +(level::LogLevel, inc::Integer) = LogLevel(level.level+inc) -(level::LogLevel, inc::Integer) = LogLevel(level.level-inc) convert(::Type{LogLevel}, level::Integer) = LogLevel(level) +convert(::Type{Int32}, level::LogLevel) = level.level const BelowMinLevel = LogLevel(-1000001) """ @@ -160,7 +161,8 @@ const Error = LogLevel( 2000) const AboveMaxLevel = LogLevel( 1000001) # Global log limiting mechanism for super fast but inflexible global log limiting. -const _min_enabled_level = Ref{LogLevel}(Debug) +# Atomic ensures that the value is always consistent across threads. +const _min_enabled_level = Threads.Atomic{Int32}(Debug) function show(io::IO, level::LogLevel) if level == BelowMinLevel print(io, "BelowMinLevel") @@ -383,7 +385,7 @@ function logmsg_code(_module, file, line, level, message, exs...) level = $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)[] + if std_level.level >= $(_min_enabled_level)[] group = $(log_data._group) _module = $(log_data._module) logger = $(current_logger_for_env)(std_level, group, _module) @@ -526,7 +528,8 @@ end Disable all log messages at log levels equal to or less than `level`. This is a *global* setting, intended to make debug logging extremely cheap when -disabled. +disabled. Note that this cannot be used to enable logging that is currently disabled +by other mechanisms. # Examples ```julia @@ -646,17 +649,21 @@ close(closed_stream) Simplistic logger for logging all messages with level greater than or equal to `min_level` to `stream`. If stream is closed then messages with log level greater or equal to `Warn` will be logged to `stderr` and below to `stdout`. + +This Logger is thread-safe, with a lock taken around orchestration of message +limits i.e. `maxlog`, and writes to the stream. """ struct SimpleLogger <: AbstractLogger stream::IO + lock::ReentrantLock min_level::LogLevel message_limits::Dict{Any,Int} end -SimpleLogger(stream::IO, level=Info) = SimpleLogger(stream, level, Dict{Any,Int}()) +SimpleLogger(stream::IO, level=Info) = SimpleLogger(stream, ReentrantLock(), level, Dict{Any,Int}()) SimpleLogger(level=Info) = SimpleLogger(closed_stream, level) shouldlog(logger::SimpleLogger, level, _module, group, id) = - get(logger.message_limits, id, 1) > 0 + @lock logger.lock get(logger.message_limits, id, 1) > 0 min_enabled_level(logger::SimpleLogger) = logger.min_level @@ -667,15 +674,14 @@ function handle_message(logger::SimpleLogger, level::LogLevel, message, _module, @nospecialize maxlog = get(kwargs, :maxlog, nothing) if maxlog isa Core.BuiltinInts - remaining = get!(logger.message_limits, id, Int(maxlog)::Int) - logger.message_limits[id] = remaining - 1 - remaining > 0 || return + @lock logger.lock begin + remaining = get!(logger.message_limits, id, Int(maxlog)::Int) + remaining == 0 && return + logger.message_limits[id] = remaining - 1 + end end buf = IOBuffer() stream::IO = logger.stream - if !(isopen(stream)::Bool) - stream = stderr - end iob = IOContext(buf, stream) levelstr = level == Warn ? "Warning" : string(level) msglines = eachsplit(chomp(convert(String, string(message))::String), '\n') @@ -689,7 +695,13 @@ function handle_message(logger::SimpleLogger, level::LogLevel, message, _module, println(iob, "│ ", key, " = ", val) end println(iob, "└ @ ", _module, " ", filepath, ":", line) - write(stream, take!(buf)) + b = take!(buf) + @lock logger.lock begin + if !(isopen(stream)::Bool) + stream = stderr + end + write(stream, b) + end nothing end diff --git a/stdlib/Logging/src/ConsoleLogger.jl b/stdlib/Logging/src/ConsoleLogger.jl index 747f8a2b22966..6cb0851675390 100644 --- a/stdlib/Logging/src/ConsoleLogger.jl +++ b/stdlib/Logging/src/ConsoleLogger.jl @@ -9,6 +9,9 @@ interactive work with the Julia REPL. Log levels less than `min_level` are filtered out. +This Logger is thread-safe, with locks for both orchestration of message +limits i.e. `maxlog`, and writes to the stream. + Message formatting can be controlled by setting keyword arguments: * `meta_formatter` is a function which takes the log event metadata @@ -24,6 +27,7 @@ Message formatting can be controlled by setting keyword arguments: """ struct ConsoleLogger <: AbstractLogger stream::IO + lock::ReentrantLock # do not log within this lock min_level::LogLevel meta_formatter show_limited::Bool @@ -33,19 +37,19 @@ end function ConsoleLogger(stream::IO, min_level=Info; meta_formatter=default_metafmt, show_limited=true, right_justify=0) - ConsoleLogger(stream, min_level, meta_formatter, + ConsoleLogger(stream, ReentrantLock(), min_level, meta_formatter, show_limited, right_justify, Dict{Any,Int}()) end function ConsoleLogger(min_level=Info; meta_formatter=default_metafmt, show_limited=true, right_justify=0) - ConsoleLogger(closed_stream, min_level, meta_formatter, + ConsoleLogger(closed_stream, ReentrantLock(), min_level, meta_formatter, show_limited, right_justify, Dict{Any,Int}()) end shouldlog(logger::ConsoleLogger, level, _module, group, id) = - get(logger.message_limits, id, 1) > 0 + @lock logger.lock get(logger.message_limits, id, 1) > 0 min_enabled_level(logger::ConsoleLogger) = logger.min_level @@ -109,9 +113,11 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module hasmaxlog = haskey(kwargs, :maxlog) ? 1 : 0 maxlog = get(kwargs, :maxlog, nothing) if maxlog isa Core.BuiltinInts - remaining = get!(logger.message_limits, id, Int(maxlog)::Int) - logger.message_limits[id] = remaining - 1 - remaining > 0 || return + @lock logger.lock begin + remaining = get!(logger.message_limits, id, Int(maxlog)::Int) + remaining == 0 && return + logger.message_limits[id] = remaining - 1 + end end # Generate a text representation of the message and all key value pairs, @@ -175,6 +181,7 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module println(iob) end - write(stream, take!(buf)) + b = take!(buf) + @lock logger.lock write(stream, b) nothing end diff --git a/stdlib/Logging/test/runtests.jl b/stdlib/Logging/test/runtests.jl index 3a793c4e0bc33..65a8c4051f4e7 100644 --- a/stdlib/Logging/test/runtests.jl +++ b/stdlib/Logging/test/runtests.jl @@ -292,4 +292,47 @@ end @test occursin("LogLevel(-500): a", String(take!(buf))) end +@testset "Logging when multithreaded" begin + n = 10000 + cmd = `$(Base.julia_cmd()) -t4 --color=no $(joinpath(@__DIR__, "threads_exec.jl")) $n` + fname = tempname() + @testset "Thread safety" begin + f = open(fname, "w") + @test success(run(pipeline(cmd, stderr=f))) + close(f) + end + + @testset "No tearing in log printing" begin + # Check for print tearing by verifying that each log entry starts and ends correctly + f = open(fname, "r") + entry_start = r"┌ (Info|Warning|Error): iteration" + entry_end = r"└ " + + open_entries = 0 + total_entries = 0 + for line in eachline(fname) + starts = count(entry_start, line) + starts > 1 && error("Interleaved logs: Multiple log entries started on one line") + if starts == 1 + startswith(line, entry_start) || error("Interleaved logs: Log entry started in the middle of a line") + open_entries += 1 + total_entries += 1 + end + + ends = count(entry_end, line) + starts == 1 && ends == 1 && error("Interleaved logs: Log entry started and and another ended on one line") + ends > 1 && error("Interleaved logs: Multiple log entries ended on one line") + if ends == 1 + startswith(line, entry_end) || error("Interleaved logs: Log entry ended in the middle of a line") + open_entries -= 1 + end + # Ensure no mismatched log entries + open_entries >= 0 || error("Interleaved logs") + end + + @test open_entries == 0 # Ensure all entries closed properly + @test total_entries == n * 3 # Ensure all logs were printed (3 because @debug is hidden) + end +end + end diff --git a/stdlib/Logging/test/threads_exec.jl b/stdlib/Logging/test/threads_exec.jl new file mode 100644 index 0000000000000..497a22b1c7b22 --- /dev/null +++ b/stdlib/Logging/test/threads_exec.jl @@ -0,0 +1,13 @@ +using Logging + +function test_threads_exec(n) + Threads.@threads for i in 1:n + @debug "iteration" maxlog=1 _id=Symbol("$(i)_debug") i Threads.threadid() + @info "iteration" maxlog=1 _id=Symbol("$(i)_info") i Threads.threadid() + @warn "iteration" maxlog=1 _id=Symbol("$(i)_warn") i Threads.threadid() + @error "iteration" maxlog=1 _id=Symbol("$(i)_error") i Threads.threadid() + end +end + +n = parse(Int, ARGS[1]) +test_threads_exec(n) diff --git a/stdlib/Test/src/logging.jl b/stdlib/Test/src/logging.jl index 8df7b016a8944..bfd2b5b826f91 100644 --- a/stdlib/Test/src/logging.jl +++ b/stdlib/Test/src/logging.jl @@ -107,8 +107,8 @@ function Logging.handle_message(logger::TestLogger, level, msg, _module, if maxlog isa Core.BuiltinInts @lock logger.lock begin remaining = get!(logger.message_limits, id, Int(maxlog)::Int) + remaining == 0 && return logger.message_limits[id] = remaining - 1 - remaining > 0 || return end end end From 37e27e464a1daaa8727e739de0cda7771f7281d0 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Sat, 3 May 2025 12:33:46 -0300 Subject: [PATCH 57/68] Make build_id.lo more random (#58258) This does not fix the underlying issue that can occur here which is a collision of build_ids.lo between modules in IR decompression. Fixing that requires a somewhat significant overhaul to the serialization of IR (probably using the module identity as a key). This does mean we use a lot more of the bits available here so it makes collisions a lot less likely( they were already extremely rare) but hrtime does tend to only use the lower bits of a 64 bit integer and this will hopefully add some more randomness and make this even less likely (cherry picked from commit 71574073759506b0a3171640ea5e1b468f2757c9) --- src/module.c | 3 ++- src/staticdata.c | 6 +++++- src/staticdata_utils.c | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/module.c b/src/module.c index d39b7e377b2df..173f4dacf2769 100644 --- a/src/module.c +++ b/src/module.c @@ -24,7 +24,8 @@ JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, ui m->istopmod = 0; m->uuid = uuid_zero; static unsigned int mcounter; // simple counter backup, in case hrtime is not incrementing - m->build_id.lo = jl_hrtime() + (++mcounter); + // TODO: this is used for ir decompression and is liable to hash collisions so use more of the bits + m->build_id.lo = bitmix(jl_hrtime() + (++mcounter), jl_rand()); if (!m->build_id.lo) m->build_id.lo++; // build id 0 is invalid m->build_id.hi = ~(uint64_t)0; diff --git a/src/staticdata.c b/src/staticdata.c index 78d0d275a0ac3..e87323e3383b1 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -3475,7 +3475,11 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i jl_insert_methods(extext_methods); // No special processing of `new_specializations` is required because recaching handled it // Add roots to methods - jl_copy_roots(method_roots_list, jl_worklist_key((jl_array_t*)restored)); + int failed = jl_copy_roots(method_roots_list, jl_worklist_key((jl_array_t*)restored)); + if (failed != 0) { + jl_printf(JL_STDERR, "Error copying roots to methods from Module: %s\n", pkgname); + abort(); + } // Handle edges 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) diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 09cd328e694f8..2972f33756c95 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -847,17 +847,31 @@ static void jl_insert_methods(jl_array_t *list) } } -static void jl_copy_roots(jl_array_t *method_roots_list, uint64_t key) +static int jl_copy_roots(jl_array_t *method_roots_list, uint64_t key) { size_t i, l = jl_array_len(method_roots_list); + int failed = 0; for (i = 0; i < l; i+=2) { jl_method_t *m = (jl_method_t*)jl_array_ptr_ref(method_roots_list, i); jl_array_t *roots = (jl_array_t*)jl_array_ptr_ref(method_roots_list, i+1); if (roots) { assert(jl_is_array(roots)); + if (m->root_blocks) { + // check for key collision + uint64_t *blocks = (uint64_t*)jl_array_data(m->root_blocks); + size_t nx2 = jl_array_nrows(m->root_blocks); + for (size_t i = 0; i < nx2; i+=2) { + if (blocks[i] == key) { + // found duplicate block + failed = -1; + } + } + } + jl_append_method_roots(m, key, roots); } } + return failed; } From cd38e84d2402a0f57a0f4eb22fca130108e1e9ba Mon Sep 17 00:00:00 2001 From: Matt Bauman Date: Mon, 2 Jun 2025 10:55:07 -0400 Subject: [PATCH 58/68] Re-enable tab completion of kwargs for large method tables (#58012) while testing to ensure that ~~absurdly large method tables~~ tab completing over an abstract function call doesn't tank the performance of the REPL Fixes #57836 (cherry picked from commit 6f129571004beac33a5cbc4aa7cbb6fcca4f5769) --- stdlib/REPL/src/REPLCompletions.jl | 8 +++++++- stdlib/REPL/test/replcompletions.jl | 29 +++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 8de1cefa06e4a..a17b381aa299c 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -949,7 +949,13 @@ function complete_keyword_argument(partial, last_idx, context_module) kwargs_flag == 2 && return fail # one of the previous kwargs is invalid methods = Completion[] - complete_methods!(methods, funct, Any[Vararg{Any}], kwargs_ex, MAX_METHOD_COMPLETIONS, kwargs_flag == 1) + # Limit kwarg completions to cases when function is concretely known; looking up + # matching methods for abstract functions — particularly `Any` or `Function` — can + # take many seconds to run over the thousands of possible methods. Note that + # isabstracttype would return naively return true for common constructor calls + # like Array, but the REPL's introspection here may know their Type{T}. + isconcretetype(funct) || return false + complete_methods!(methods, funct, Any[Vararg{Any}], kwargs_ex, -1, kwargs_flag == 1) # TODO: use args_ex instead of Any[Vararg{Any}] and only provide kwarg completion for # method calls compatible with the current arguments. diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 42c30b2cfdf9c..c00cb702988c3 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -2004,3 +2004,32 @@ let s = "using ...Issue52922.Inn" @test res @test "Inner2" in c end + +function g54131 end +for i in 1:498 + @eval g54131(::Val{$i}) = i +end +g54131(::Val{499}; kwarg=true) = 499*kwarg +struct F54131; end +Base.getproperty(::F54131, ::Symbol) = Any[cos, sin, g54131][rand(1:3)] +f54131 = F54131() +@testset "performance of kwarg completion with large method tables" begin + # The goal here is to simply ensure we aren't hitting catestrophically bad + # behaviors when shift isn't pressed. The difference between good and bad + # is on the order of tens of milliseconds vs tens of seconds; using 1 sec as + # a very rough canary that is hopefully robust even in the noisy CI coalmines + s = "g54131(kwa" + a, b, c = completions(s, lastindex(s), @__MODULE__, #= shift =# false) + @test REPLCompletions.KeywordArgumentCompletion("kwarg") in a + @test (@elapsed completions(s, lastindex(s), @__MODULE__, false)) < 1 + + s = "f54131.x(" + a, b, c = completions(s, lastindex(s), @__MODULE__, false) + @test only(a) isa REPLCompletions.TextCompletion + @test (@elapsed completions(s, lastindex(s), @__MODULE__, false)) < 1 + + s = "f54131.x(kwa" + a, b, c = completions(s, lastindex(s), @__MODULE__, false) + @test_broken REPLCompletions.KeywordArgumentCompletion("kwarg") in a + @test (@elapsed completions(s, lastindex(s), @__MODULE__, false)) < 1 +end From 70768569f788a5adc73b30dd26ef874e36300ae9 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 5 Jun 2025 14:13:48 +0200 Subject: [PATCH 59/68] backport busybox download fix --- test/spawn.jl | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/test/spawn.jl b/test/spawn.jl index 14b5fd28ca7dd..d592c8655c028 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -21,9 +21,11 @@ sleepcmd = `sleep` lscmd = `ls` havebb = false +busybox_hash_correct(file) = bytes2ex(open(SHA.sha256, file)) == "ed2f95da9555268e93c7af52feb48e148534ee518b9128f65dda9a2767b61b9e" + function _tryonce_download_from_cache(desired_url::AbstractString) cache_url = "https://cache.julialang.org/$(desired_url)" - cache_output_filename = joinpath(mktempdir(), "myfile") + cache_output_filename = joinpath(mktempdir(), "busybox" * (Sys.iswindows() ? ".exe" : "")) cache_response = Downloads.request( cache_url; output = cache_output_filename, @@ -32,9 +34,14 @@ function _tryonce_download_from_cache(desired_url::AbstractString) ) if cache_response isa Downloads.Response if Downloads.status_ok(cache_response.proto, cache_response.status) - return cache_output_filename + if busybox_hash_correct(cache_output_filename) + return cache_output_filename + else + @warn "The busybox executable downloaded from the cache has an incorrect hash" cache_output_filename bytes2hex(open(SHA.sha256, cache_output_filename)) + end end end + @warn "Could not download from cache at $cache_url, falling back to primary source at $desired_url" return Downloads.download(desired_url; timeout = 60) end @@ -46,7 +53,11 @@ function download_from_cache(desired_url::AbstractString) end if Sys.iswindows() - busybox = download_from_cache("https://frippery.org/files/busybox/busybox.exe") + # See https://frippery.org/files/busybox/ + # latest as of 2024-09-20 18:08 + busybox = download_from_cache("https://frippery.org/files/busybox/busybox-w32-FRP-5467-g9376eebd8.exe") + busybox_hash_correct(busybox) || error("The busybox executable downloaded has an incorrect hash") + havebb = try # use busybox-w32 on windows, if available success(`$busybox`) true From caea1d33780a8084f592b1839df0dd08d3d036fc Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 5 Jun 2025 15:09:38 +0200 Subject: [PATCH 60/68] Revert "codegen: remove readonly from abstract type calling convention (#58356)" This reverts commit 9a727c613085a62339cc562ab6bf8b36fbadcc38. --- src/cgutils.cpp | 2 +- src/codegen.cpp | 42 ++++++++++++++++++---------------------- src/julia.h | 2 +- test/compiler/codegen.jl | 4 ---- 4 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 8aa48cfe9942e..d560ad47ff7aa 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1264,7 +1264,6 @@ static Value *emit_sizeof(jl_codectx_t &ctx, const jl_cgval_t &p) return dyn_size; } } -*/ static Value *emit_datatype_mutabl(jl_codectx_t &ctx, Value *dt) { @@ -1279,6 +1278,7 @@ static Value *emit_datatype_mutabl(jl_codectx_t &ctx, Value *dt) mutabl = ctx.builder.CreateLShr(mutabl, 1); return ctx.builder.CreateTrunc(mutabl, getInt1Ty(ctx.builder.getContext())); } +*/ static Value *emit_datatype_isprimitivetype(jl_codectx_t &ctx, Value *typ) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 0a21d08a98f5e..20a6240a926d0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1344,7 +1344,7 @@ static MDNode *best_tbaa(jl_tbaacache_t &tbaa_cache, jl_value_t *jt) { // note that this includes jl_isbits, although codegen should work regardless static bool jl_is_concrete_immutable(jl_value_t* t) { - return jl_may_be_immutable_datatype(t) && ((jl_datatype_t*)t)->isconcretetype; + return jl_is_immutable_datatype(t) && ((jl_datatype_t*)t)->isconcretetype; } static bool jl_is_pointerfree(jl_value_t* t) @@ -6248,9 +6248,9 @@ static Function* gen_cfun_wrapper( inputarg = mark_julia_type(ctx, val, false, jargty); } } - else if (static_at || (!jl_is_typevar(jargty) && (!jl_is_datatype(jargty) || jl_is_abstracttype(jargty) || jl_is_mutable_datatype(jargty)))) { - // must be a jl_value_t* (because it is mutable or abstract) - inputarg = mark_julia_type(ctx, maybe_decay_untracked(ctx, val), true, jargty_proper); + else if (static_at || (!jl_is_typevar(jargty) && !jl_is_immutable_datatype(jargty))) { + // must be a jl_value_t* (because it's mutable or contains gc roots) + inputarg = mark_julia_type(ctx, maybe_decay_untracked(ctx, emit_bitcast(ctx, val, ctx.types().T_prjlvalue)), true, jargty_proper); } else { // allocate val into a new box, if it might not be boxed @@ -6263,36 +6263,32 @@ static Function* gen_cfun_wrapper( ctx.builder.CreateConstInBoundsGEP1_32(ctx.types().T_prjlvalue, nestPtr, jl_array_len(*closure_types)), Align(sizeof(void*))); BasicBlock *boxedBB = BasicBlock::Create(ctx.builder.getContext(), "isboxed", cw); - BasicBlock *notanyBB = BasicBlock::Create(ctx.builder.getContext(), "not-any", cw); + BasicBlock *loadBB = BasicBlock::Create(ctx.builder.getContext(), "need-load", cw); 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); // 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); + p1 = track_pjlvalue(ctx, p1); + ctx.builder.CreateBr(afterBB); + ctx.builder.SetInsertPoint(loadBB); Value *isrtany = ctx.builder.CreateICmpEQ( - track_pjlvalue(ctx,literal_pointer_val(ctx, (jl_value_t*)jl_any_type)), runtime_dt); - ctx.builder.CreateCondBr(isrtany, isanyBB, notanyBB); + literal_pointer_val(ctx, (jl_value_t*)jl_any_type), + ctx.builder.CreateBitCast(val, ctx.types().T_pjlvalue)); + ctx.builder.CreateCondBr(isrtany, isanyBB, unboxedBB); ctx.builder.SetInsertPoint(isanyBB); - Value *p1 = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, val, Align(sizeof(void*))); - ctx.builder.CreateBr(afterBB); - isanyBB = ctx.builder.GetInsertBlock(); // could have changed - ctx.builder.SetInsertPoint(notanyBB); - jl_cgval_t runtime_dt_val = mark_julia_type(ctx, runtime_dt, true, jl_any_type); - Value *isrtboxed = // (!jl_is_datatype(runtime_dt) || !jl_is_concrete_datatype(runtime_dt) || jl_is_mutable_datatype(runtime_dt)) - emit_guarded_test(ctx, emit_exactly_isa(ctx, runtime_dt_val, jl_datatype_type), true, [&] { - return ctx.builder.CreateOr(ctx.builder.CreateNot(emit_isconcrete(ctx, runtime_dt)), emit_datatype_mutabl(ctx, runtime_dt)); - }); - ctx.builder.CreateCondBr(isrtboxed, boxedBB, unboxedBB); - ctx.builder.SetInsertPoint(boxedBB); - Value *p2 = track_pjlvalue(ctx, val); + Value *p2 = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, ctx.builder.CreateBitCast(val, ctx.types().T_pprjlvalue), Align(sizeof(void*))); ctx.builder.CreateBr(afterBB); - boxedBB = ctx.builder.GetInsertBlock(); // could have changed ctx.builder.SetInsertPoint(unboxedBB); Value *p3 = emit_new_bits(ctx, runtime_dt, val); unboxedBB = ctx.builder.GetInsertBlock(); // could have changed ctx.builder.CreateBr(afterBB); ctx.builder.SetInsertPoint(afterBB); PHINode *p = ctx.builder.CreatePHI(ctx.types().T_prjlvalue, 3); - p->addIncoming(p1, isanyBB); - p->addIncoming(p2, boxedBB); + p->addIncoming(p1, boxedBB); + p->addIncoming(p2, isanyBB); p->addIncoming(p3, unboxedBB); inputarg = mark_julia_type(ctx, p, true, jargty_proper); } @@ -7073,7 +7069,7 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value param.addAttribute(Attribute::ReadOnly); ty = PointerType::get(ty, AddressSpace::Derived); } - else if (isboxed && jl_may_be_immutable_datatype(jt) && !jl_is_abstracttype(jt)) { + else if (isboxed && jl_is_immutable_datatype(jt)) { param.addAttribute(Attribute::ReadOnly); } else if (jl_is_primitivetype(jt) && ty->isIntegerTy()) { diff --git a/src/julia.h b/src/julia.h index cfa5047b38585..26783ceb1a45e 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1316,7 +1316,7 @@ static inline int jl_is_layout_opaque(const jl_datatype_layout_t *l) JL_NOTSAFEP #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_may_be_immutable_datatype(t) (jl_is_datatype(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_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) diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 60cf6adb07d85..21cf2e8fca5db 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -879,7 +879,3 @@ struct Vec56937 x::NTuple{8, VecElement{Int}} end x56937 = Ref(Vec56937(ntuple(_->VecElement(1),8))) @test x56937[].x[1] == VecElement{Int}(1) # shouldn't crash - -@noinline f_mutateany(@nospecialize x) = x[] = 1 -g_mutateany() = (y = Ref(0); f_mutateany(y); y[]) -@test g_mutateany() === 1 From 6b4695a9a7654e7fb94e2ffb80e32bcf3441c32b Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 5 Jun 2025 15:44:23 +0200 Subject: [PATCH 61/68] Revert "Specialize `one` for the `SizedArray` test helper (#58209)" This reverts commit 29bef403338aa1a43e972a168570d73cb117219d. --- test/abstractarray.jl | 1 - test/testhelpers/SizedArrays.jl | 6 ------ 2 files changed, 7 deletions(-) diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 10708c133deea..e7469a7a331ed 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -1907,4 +1907,3 @@ end @test r2[i] == z[j] end end - diff --git a/test/testhelpers/SizedArrays.jl b/test/testhelpers/SizedArrays.jl index 720309e9e3236..dfcc5b79f1387 100644 --- a/test/testhelpers/SizedArrays.jl +++ b/test/testhelpers/SizedArrays.jl @@ -29,12 +29,6 @@ Base.size(a::SizedArray) = size(typeof(a)) Base.size(::Type{<:SizedArray{SZ}}) where {SZ} = SZ Base.getindex(A::SizedArray, i...) = getindex(A.data, i...) Base.zero(::Type{T}) where T <: SizedArray = SizedArray{size(T)}(zeros(eltype(T), size(T))) -function Base.one(::Type{SizedMatrix{SZ,T,A}}) where {SZ,T,A} - allequal(SZ) || throw(DimensionMismatch("multiplicative identity defined only for square matrices")) - D = diagm(fill(one(T), SZ[1])) - SizedArray{SZ}(convert(A, D)) -end -Base.parent(S::SizedArray) = S.data +(S1::SizedArray{SZ}, S2::SizedArray{SZ}) where {SZ} = SizedArray{SZ}(S1.data + S2.data) ==(S1::SizedArray{SZ}, S2::SizedArray{SZ}) where {SZ} = S1.data == S2.data function *(S1::SizedArray, S2::SizedArray) From 41666c91179e505029403f8897faa29f9a9acd9e Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 5 Jun 2025 15:50:58 +0200 Subject: [PATCH 62/68] Revert "Re-enable tab completion of kwargs for large method tables (#58012)" This reverts commit f673211b6ccd19514c700f6015722115b6065c7b. --- stdlib/REPL/src/REPLCompletions.jl | 8 +------- stdlib/REPL/test/replcompletions.jl | 29 ----------------------------- 2 files changed, 1 insertion(+), 36 deletions(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index a17b381aa299c..8de1cefa06e4a 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -949,13 +949,7 @@ function complete_keyword_argument(partial, last_idx, context_module) kwargs_flag == 2 && return fail # one of the previous kwargs is invalid methods = Completion[] - # Limit kwarg completions to cases when function is concretely known; looking up - # matching methods for abstract functions — particularly `Any` or `Function` — can - # take many seconds to run over the thousands of possible methods. Note that - # isabstracttype would return naively return true for common constructor calls - # like Array, but the REPL's introspection here may know their Type{T}. - isconcretetype(funct) || return false - complete_methods!(methods, funct, Any[Vararg{Any}], kwargs_ex, -1, kwargs_flag == 1) + complete_methods!(methods, funct, Any[Vararg{Any}], kwargs_ex, MAX_METHOD_COMPLETIONS, kwargs_flag == 1) # TODO: use args_ex instead of Any[Vararg{Any}] and only provide kwarg completion for # method calls compatible with the current arguments. diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index c00cb702988c3..42c30b2cfdf9c 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -2004,32 +2004,3 @@ let s = "using ...Issue52922.Inn" @test res @test "Inner2" in c end - -function g54131 end -for i in 1:498 - @eval g54131(::Val{$i}) = i -end -g54131(::Val{499}; kwarg=true) = 499*kwarg -struct F54131; end -Base.getproperty(::F54131, ::Symbol) = Any[cos, sin, g54131][rand(1:3)] -f54131 = F54131() -@testset "performance of kwarg completion with large method tables" begin - # The goal here is to simply ensure we aren't hitting catestrophically bad - # behaviors when shift isn't pressed. The difference between good and bad - # is on the order of tens of milliseconds vs tens of seconds; using 1 sec as - # a very rough canary that is hopefully robust even in the noisy CI coalmines - s = "g54131(kwa" - a, b, c = completions(s, lastindex(s), @__MODULE__, #= shift =# false) - @test REPLCompletions.KeywordArgumentCompletion("kwarg") in a - @test (@elapsed completions(s, lastindex(s), @__MODULE__, false)) < 1 - - s = "f54131.x(" - a, b, c = completions(s, lastindex(s), @__MODULE__, false) - @test only(a) isa REPLCompletions.TextCompletion - @test (@elapsed completions(s, lastindex(s), @__MODULE__, false)) < 1 - - s = "f54131.x(kwa" - a, b, c = completions(s, lastindex(s), @__MODULE__, false) - @test_broken REPLCompletions.KeywordArgumentCompletion("kwarg") in a - @test (@elapsed completions(s, lastindex(s), @__MODULE__, false)) < 1 -end From 7f6ffc7886113037ba5c2ea4e971bad7e7d1a4db Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 5 Jun 2025 15:53:24 +0200 Subject: [PATCH 63/68] Revert "power uses Float64 exponents for integers (#53967)" This reverts commit fe073c46ef6c7da343abdaf36afa4cc58f661cb6. --- base/math.jl | 54 ++++++++++----------------------------------- base/special/exp.jl | 2 +- test/math.jl | 19 ---------------- 3 files changed, 13 insertions(+), 62 deletions(-) diff --git a/base/math.jl b/base/math.jl index 8178099a4aaf8..0e2b7fb7c6397 100644 --- a/base/math.jl +++ b/base/math.jl @@ -1189,10 +1189,6 @@ function modf(x::T) where T<:IEEEFloat return (rx, ix) end -@inline function use_power_by_squaring(n::Integer) - -2^12 <= n <= 3 * 2^13 -end - # @constprop aggressive to help the compiler see the switch between the integer and float # variants for callers with constant `y` @constprop :aggressive function ^(x::Float64, y::Float64) @@ -1205,33 +1201,24 @@ end y = sign(y)*0x1.8p62 end yint = unsafe_trunc(Int64, y) # This is actually safe since julia freezes the result - yisint = y == yint - if yisint - yint == 0 && return 1.0 - use_power_by_squaring(yint) && return @noinline pow_body(x, yint) - end - 2*xu==0 && return abs(y)*Inf*(!(y>0)) # if x === +0.0 or -0.0 (Inf * false === 0.0) - s = 1 - if x < 0 - !yisint && throw_exp_domainerror(x) # y isn't an integer - s = ifelse(isodd(yint), -1, 1) - end - !isfinite(x) && return copysign(x,s)*(y>0 || isnan(x)) # x is inf or NaN - return copysign(pow_body(abs(x), y), s) -end - -@assume_effects :foldable @noinline function pow_body(x::Float64, y::Float64) - xu = reinterpret(UInt64, x) + y == yint && return @noinline x^yint + 2*xu==0 && return abs(y)*Inf*(!(y>0)) # if x==0 + x<0 && throw_exp_domainerror(x) # |y| is small enough that y isn't an integer + !isfinite(x) && return x*(y>0 || isnan(x)) # x is inf or NaN if xu < (UInt64(1)<<52) # x is subnormal xu = reinterpret(UInt64, x * 0x1p52) # normalize x xu &= ~sign_mask(Float64) xu -= UInt64(52) << 52 # mess with the exponent end - logxhi,logxlo = _log_ext(xu) + return pow_body(xu, y) +end + +@inline function pow_body(xu::UInt64, y::Float64) + logxhi,logxlo = Base.Math._log_ext(xu) xyhi, xylo = two_mul(logxhi,y) xylo = muladd(logxlo, y, xylo) hi = xyhi+xylo - return @inline Base.Math.exp_impl(hi, xylo-(hi-xyhi), Val(:ℯ)) + return Base.Math.exp_impl(hi, xylo-(hi-xyhi), Val(:ℯ)) end @constprop :aggressive function ^(x::T, y::T) where T <: Union{Float16, Float32} @@ -1255,29 +1242,12 @@ end return T(exp2(log2(abs(widen(x))) * y)) end +# compensated power by squaring @constprop :aggressive @inline function ^(x::Float64, n::Integer) - x^clamp(n, Int64) -end -@constprop :aggressive @inline function ^(x::Float64, n::Int64) n == 0 && return one(x) - if use_power_by_squaring(n) - return pow_body(x, n) - else - s = ifelse(x < 0 && isodd(n), -1.0, 1.0) - x = abs(x) - y = float(n) - if y == n - return copysign(pow_body(x, y), s) - else - n2 = n % 1024 - y = float(n - n2) - return pow_body(x, y) * copysign(pow_body(x, n2), s) - end - end + return pow_body(x, n) end -# compensated power by squaring -# this method is only reliable for -2^20 < n < 2^20 (cf. #53881 #53886) @assume_effects :terminates_locally @noinline function pow_body(x::Float64, n::Integer) y = 1.0 xnlo = ynlo = 0.0 diff --git a/base/special/exp.jl b/base/special/exp.jl index f0db1f70a354b..44a022b9400ee 100644 --- a/base/special/exp.jl +++ b/base/special/exp.jl @@ -252,7 +252,7 @@ end twopk = (k + UInt64(53)) << 52 return reinterpret(T, twopk + reinterpret(UInt64, small_part))*0x1p-53 end - k == 1024 && return (small_part * 2.0) * 0x1p1023 + # k == 1024 && return (small_part * 2.0) * 0x1p1023 end twopk = Int64(k) << 52 return reinterpret(T, twopk + reinterpret(Int64, small_part)) diff --git a/test/math.jl b/test/math.jl index 3f0a8402c9575..bd00f3417ecd3 100644 --- a/test/math.jl +++ b/test/math.jl @@ -1453,25 +1453,6 @@ end # two cases where we have observed > 1 ULP in the past @test 0.0013653274095082324^-97.60372292227069 == 4.088393948750035e279 @test 8.758520413376658e-5^70.55863059215994 == 5.052076767078296e-287 - - # issue #53881 - c53881 = 2.2844135865398217e222 # check correctness within 2 ULPs - @test prevfloat(1.0) ^ -Int64(2)^62 ≈ c53881 atol=2eps(c53881) - @test 2.0 ^ typemin(Int) == 0.0 - @test (-1.0) ^ typemin(Int) == 1.0 - Z = Int64(2) - E = prevfloat(1.0) - @test E ^ (-Z^54) ≈ 7.38905609893065 - @test E ^ (-Z^62) ≈ 2.2844135865231613e222 - @test E ^ (-Z^63) == Inf - @test abs(E ^ (Z^62-1) * E ^ (-Z^62+1) - 1) <= eps(1.0) - n, x = -1065564664, 0.9999997040311492 - @test abs(x^n - Float64(big(x)^n)) / eps(x^n) == 0 # ULPs - @test E ^ (big(2)^100 + 1) == 0 - @test E ^ 6705320061009595392 == nextfloat(0.0) - n = Int64(1024 / log2(E)) - @test E^n == Inf - @test E^float(n) == Inf end @testset "special function `::Real` fallback shouldn't recur without bound, issue #57789" begin From fa48c49ea96568ca55379535a96dd1d688afcc85 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 5 Jun 2025 16:25:37 +0200 Subject: [PATCH 64/68] fixup! Don't filter `Core` methods from newly-inferred list (#58510) --- test/precompile.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/precompile.jl b/test/precompile.jl index ba9c54fe77022..ac155bb5d7af2 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -1869,7 +1869,7 @@ precompile_test_harness("Pre-compile Core methods") do load_path invokelatest() do let tt = Tuple{Type{Vector{CorePrecompilation.Foo}}, UndefInitializer, Tuple{Int}}, match = first(Base._methods_by_ftype(tt, -1, Base.get_world_counter())), - mi = Base.specialize_method(match) + mi = Core.Compiler.specialize_method(match) @test isdefined(mi, :cache) @test mi.cache.max_world === typemax(UInt) @test mi.cache.invoke != C_NULL From 6d01bb3b7e0edac3a916683955fc816761fb55a7 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 11 Jun 2025 16:35:57 +0200 Subject: [PATCH 65/68] fix test on 32-bit --- test/strings/basic.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 6298744855b9e..e7c65909fabc9 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -874,8 +874,8 @@ end end @testset "return type infers to `Int`" begin - @test all(==(Int64), Base.return_types(prevind, Tuple{AbstractString, Vararg})) - @test all(==(Int64), Base.return_types(nextind, Tuple{AbstractString, Vararg})) + @test all(==(Int), Base.return_types(prevind, Tuple{AbstractString, Vararg})) + @test all(==(Int), Base.return_types(nextind, Tuple{AbstractString, Vararg})) end end From 5f92e8d1f9ffa366a95ce72d2f24a1b0dd759942 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 11 Jun 2025 11:48:21 -0300 Subject: [PATCH 66/68] Fix llvmpasses --- test/llvmpasses/late-lower-gc.ll | 11 ++++++----- test/llvmpasses/remove-addrspaces.ll | 13 ------------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/test/llvmpasses/late-lower-gc.ll b/test/llvmpasses/late-lower-gc.ll index ecd09d968afff..4cb46ffaf15a9 100644 --- a/test/llvmpasses/late-lower-gc.ll +++ b/test/llvmpasses/late-lower-gc.ll @@ -134,10 +134,11 @@ top: %pgcstack = call {}*** @julia.get_pgcstack() %1 = bitcast {}*** %pgcstack to {}** %current_task = getelementptr inbounds {}*, {}** %1, i64 -12 - -; CHECK: %current_task = getelementptr inbounds ptr, ptr %1, i64 -12 - %2 = load float, ptr addrspace(1) %0, align 4, !invariant.load !1 -; CHECK-NEXT: %2 = load float, ptr addrspace(1) %0, align 4, !invariant.load +; TYPED: %current_task = %current_task = getelementptr inbounds {}*, {}** %1, i64 -12 +; OPAQUE: %current_task = getelementptr inbounds ptr, ptr %1, i64 -12 + %2 = load float, float addrspace(1)* %0, align 4, !invariant.load !1 +; TYPED-NEXT: %2 = load float, float addrspace(1)* %0, align 4, !invariant.load +; OPAQUE-NEXT: %2 = load float, ptr addrspace(1) %0, align 4, !invariant.load ret void } @@ -207,7 +208,7 @@ define void @decayar([2 x {} addrspace(10)* addrspace(11)*] %ar) { %l0 = load {} addrspace(10)*, {} addrspace(10)* addrspace(11)* %e0 %e1 = extractvalue [2 x {} addrspace(10)* addrspace(11)*] %ar, 1 %l1 = load {} addrspace(10)*, {} addrspace(10)* addrspace(11)* %e1 - %r = call i32 @callee_root({} addrspace(10)* %l0, {} addrspace(10)* %l1) + %r = call i32 @callee_root({} addrspace(10)* %l0, {} addrspace(10)* %l1) ret void } diff --git a/test/llvmpasses/remove-addrspaces.ll b/test/llvmpasses/remove-addrspaces.ll index daf407d047d73..d0e77e4a5d7ae 100644 --- a/test/llvmpasses/remove-addrspaces.ll +++ b/test/llvmpasses/remove-addrspaces.ll @@ -3,8 +3,6 @@ ; RUN: opt -enable-new-pm=0 --opaque-pointers=0 -load libjulia-codegen%shlibext -RemoveJuliaAddrspaces -S %s | FileCheck %s --check-prefixes=CHECK,TYPED ; RUN: opt -enable-new-pm=1 --opaque-pointers=0 --load-pass-plugin=libjulia-codegen%shlibext -passes='RemoveJuliaAddrspaces' -S %s | FileCheck %s --check-prefixes=CHECK,TYPED -; RUN: opt -enable-new-pm=0 --opaque-pointers=1 -load libjulia-codegen%shlibext -RemoveJuliaAddrspaces -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE -; RUN: opt -enable-new-pm=1 --opaque-pointers=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='RemoveJuliaAddrspaces' -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE ; COM: check that the addrspace of the global itself is removed ; OPAQUE: @ejl_enz_runtime_exc = external global {} @@ -16,10 +14,6 @@ ; TYPED-SAME: {}* ({}***, {}*, [1 x i64]*)* null ; OPAQUE-SAME: ptr null -; COM: check that the addrspace of the global itself is removed -; OPAQUE: @ejl_enz_runtime_exc = external global {} -@ejl_enz_runtime_exc = external addrspace(10) global {} - define i64 @getindex({} addrspace(10)* nonnull align 16 dereferenceable(40)) { ; CHECK-LABEL: @getindex top: @@ -137,13 +131,6 @@ L6: unreachable } -define private fastcc void @diffejulia__mapreduce_97() { -L6: -; OPAQUE: store atomic ptr @ejl_enz_runtime_exc, ptr null unordered - store atomic {} addrspace(10)* @ejl_enz_runtime_exc, {} addrspace(10)* addrspace(10)* null unordered, align 8 - unreachable -} - ; COM: check that function attributes are preserved on declarations too declare void @convergent_function() #0 attributes #0 = { convergent } From 5838394715e11af3b37e028137da64d581dcf963 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 11 Jun 2025 13:14:07 -0300 Subject: [PATCH 67/68] Fix typo and double tests --- test/llvmpasses/late-lower-gc.ll | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/llvmpasses/late-lower-gc.ll b/test/llvmpasses/late-lower-gc.ll index 4cb46ffaf15a9..b8733b8ab9a56 100644 --- a/test/llvmpasses/late-lower-gc.ll +++ b/test/llvmpasses/late-lower-gc.ll @@ -3,8 +3,6 @@ ; RUN: opt -enable-new-pm=0 --opaque-pointers=0 -load libjulia-codegen%shlibext -LateLowerGCFrame -S %s | FileCheck %s -check-prefixes=CHECK,TYPED ; RUN: opt -enable-new-pm=1 --opaque-pointers=0 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame)' -S %s | FileCheck %s -check-prefixes=CHECK,TYPED -; RUN: opt -enable-new-pm=0 --opaque-pointers=1 -load libjulia-codegen%shlibext -LateLowerGCFrame -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE -; RUN: opt -enable-new-pm=1 --opaque-pointers=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame)' -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE @tag = external addrspace(10) global {}, align 16 @@ -134,7 +132,7 @@ top: %pgcstack = call {}*** @julia.get_pgcstack() %1 = bitcast {}*** %pgcstack to {}** %current_task = getelementptr inbounds {}*, {}** %1, i64 -12 -; TYPED: %current_task = %current_task = getelementptr inbounds {}*, {}** %1, i64 -12 +; TYPED: %current_task = getelementptr inbounds {}*, {}** %1, i64 -12 ; OPAQUE: %current_task = getelementptr inbounds ptr, ptr %1, i64 -12 %2 = load float, float addrspace(1)* %0, align 4, !invariant.load !1 ; TYPED-NEXT: %2 = load float, float addrspace(1)* %0, align 4, !invariant.load From fab3a8202bf1a73cff6cf9efd09fab80ef2bac4c Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 13 Jun 2025 12:17:56 +0200 Subject: [PATCH 68/68] fix importing SHA in spawntests --- test/spawn.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spawn.jl b/test/spawn.jl index d592c8655c028..0458ea4466331 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -4,7 +4,7 @@ # Cross Platform tests for spawn. # ################################### -using Random, Sockets +using Random, Sockets, SHA using Downloads: Downloads, download valgrind_off = ccall(:jl_running_on_valgrind, Cint, ()) == 0