From ba2a8a35ec9a4bd53a7c48b8aac1a05b184c3b12 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Thu, 5 Oct 2023 17:14:12 +0200 Subject: [PATCH 01/38] Use a simple error when reporting sysimg load failures. (#51598) `jl_errorexception_type` is undefined at the point we (fail to) load a sysimg. (cherry picked from commit 20a5fa751aa613b3244c9158581bcc85f049dc5c) --- src/processor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/processor.cpp b/src/processor.cpp index d2d91d3cd9966..587ff300c8d7e 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -634,7 +634,7 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) JL_GC_PUSH1(&rejection_reason); uint32_t target_idx = callback(ids, &rejection_reason); if (target_idx == (uint32_t)-1) { - jl_throw(jl_new_struct(jl_errorexception_type, rejection_reason)); + jl_error(jl_string_ptr(rejection_reason)); } JL_GC_POP(); From 1aa50550224454d854d174170a6b87671cde54e9 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 20 Oct 2023 07:26:06 +0200 Subject: [PATCH 02/38] fix parallel peakflop usage (#51757) This is required now once Distributed is not in the sysimage. Fixes https://github.com/JuliaLang/julia/issues/51756 (cherry picked from commit 795d8d7a033cb711f5914b0da7587ab55edb0f39) --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 85ba1d2770ba7..494572b1e8dd8 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -619,7 +619,9 @@ function peakflops(n::Integer=4096; eltype::DataType=Float64, ntrials::Integer=3 if parallel let Distributed = Base.require(Base.PkgId( Base.UUID((0x8ba89e20_285c_5b6f, 0x9357_94700520ee1b)), "Distributed")) - return sum(Distributed.pmap(peakflops, fill(n, Distributed.nworkers()))) + nworkers = @invokelatest Distributed.nworkers() + results = @invokelatest Distributed.pmap(peakflops, fill(n, nworkers)) + return sum(results) end else return 2*Float64(n)^3 / minimum(t) From feb75f830f23f98faa91d1634f3e70d2ce36d861 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 25 Oct 2023 10:16:47 -0500 Subject: [PATCH 03/38] allow finalizers to take any locks and yield during exit (#51848) This aligns their behavior with manual calls to `finalize(o)`, and prepares for a future time in which these functions are always run on a separate thread. This means that they can wait to acquire locks in this context, which otherwise would have been denied to them. (cherry picked from commit c54a3f2b83bfca5f79a1b482abaa9b8e7f0da9ce) --- src/gc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gc.c b/src/gc.c index a112d73de625a..6b86f4cab81a8 100644 --- a/src/gc.c +++ b/src/gc.c @@ -420,7 +420,7 @@ JL_DLLEXPORT void jl_gc_init_finalizer_rng_state(void) jl_rng_split(finalizer_rngState, jl_current_task->rngState); } -static void run_finalizers(jl_task_t *ct) +static void run_finalizers(jl_task_t *ct, int finalizers_thread) { // Racy fast path: // The race here should be OK since the race can only happen if @@ -448,7 +448,7 @@ static void run_finalizers(jl_task_t *ct) // This releases the finalizers lock. int8_t was_in_finalizer = ct->ptls->in_finalizer; - ct->ptls->in_finalizer = 1; + ct->ptls->in_finalizer = !finalizers_thread; jl_gc_run_finalizers_in_list(ct, &copied_list); ct->ptls->in_finalizer = was_in_finalizer; arraylist_free(&copied_list); @@ -462,7 +462,7 @@ JL_DLLEXPORT void jl_gc_run_pending_finalizers(jl_task_t *ct) ct = jl_current_task; jl_ptls_t ptls = ct->ptls; if (!ptls->in_finalizer && ptls->locks.len == 0 && ptls->finalizers_inhibited == 0) { - run_finalizers(ct); + run_finalizers(ct, 0); } } @@ -555,7 +555,7 @@ void jl_gc_run_all_finalizers(jl_task_t *ct) JL_UNLOCK_NOGC(&finalizers_lock); gc_n_threads = 0; gc_all_tls_states = NULL; - run_finalizers(ct); + run_finalizers(ct, 1); } void jl_gc_add_finalizer_(jl_ptls_t ptls, void *v, void *f) JL_NOTSAFEPOINT @@ -3466,7 +3466,7 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) // or wait for finalizers on other threads without dead lock). if (!ptls->finalizers_inhibited && ptls->locks.len == 0) { JL_TIMING(GC, GC_Finalizers); - run_finalizers(ct); + run_finalizers(ct, 0); } JL_PROBE_GC_FINALIZER(); From db5574228aaf6649b5774edba52cf799b0360d80 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 25 Oct 2023 12:34:51 -0500 Subject: [PATCH 04/38] add missing wait during Timer and AsyncCondition close (#51847) Can cause spurious warnings about not closing these properly and unexpected events to appear after `close` returns. (cherry picked from commit d0c42840f6ff509421a3f0d28ebe5bd80c625cc3) --- base/asyncevent.jl | 39 ++++++++++++++++++++++++++++++++------- test/channels.jl | 6 +++--- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/base/asyncevent.jl b/base/asyncevent.jl index a26945bbb1105..11551df5d0e52 100644 --- a/base/asyncevent.jl +++ b/base/asyncevent.jl @@ -118,14 +118,18 @@ end unsafe_convert(::Type{Ptr{Cvoid}}, t::Timer) = t.handle unsafe_convert(::Type{Ptr{Cvoid}}, async::AsyncCondition) = async.handle +# if this returns true, the object has been signaled +# if this returns false, the object is closed function _trywait(t::Union{Timer, AsyncCondition}) set = t.set if set # full barrier now for AsyncCondition t isa Timer || Core.Intrinsics.atomic_fence(:acquire_release) else - t.isopen || return false - t.handle == C_NULL && return false + if !isopen(t) + close(t) # wait for the close to complete + return false + end iolock_begin() set = t.set if !set @@ -133,7 +137,7 @@ function _trywait(t::Union{Timer, AsyncCondition}) lock(t.cond) try set = t.set - if !set && t.isopen && t.handle != C_NULL + if !set && t.handle != C_NULL # wait for set or handle, but not the isopen flag iolock_end() set = wait(t.cond) unlock(t.cond) @@ -160,10 +164,28 @@ end isopen(t::Union{Timer, AsyncCondition}) = t.isopen && t.handle != C_NULL function close(t::Union{Timer, AsyncCondition}) + t.handle == C_NULL && return # short-circuit path iolock_begin() - if isopen(t) - @atomic :monotonic t.isopen = false - ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t) + if t.handle != C_NULL + if t.isopen + @atomic :monotonic t.isopen = false + ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t) + end + # implement _trywait here without the auto-reset function, just waiting for the final close signal + preserve_handle(t) + lock(t.cond) + try + while t.handle != C_NULL + iolock_end() + wait(t.cond) + unlock(t.cond) + iolock_begin() + lock(t.cond) + end + finally + unlock(t.cond) + unpreserve_handle(t) + end end iolock_end() nothing @@ -220,7 +242,10 @@ function uv_timercb(handle::Ptr{Cvoid}) @atomic :monotonic t.set = true if ccall(:uv_timer_get_repeat, UInt64, (Ptr{Cvoid},), t) == 0 # timer is stopped now - close(t) + if t.isopen + @atomic :monotonic t.isopen = false + ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t) + end end notify(t.cond, true) finally diff --git a/test/channels.jl b/test/channels.jl index 36fec7b842de1..82689e8d08d28 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -457,8 +457,8 @@ end Sys.iswindows() && Base.process_events() # schedule event (windows?) close(async) # and close @test !isopen(async) - @test tc[] == 2 - @test tc[] == 2 + @test tc[] == 3 + @test tc[] == 3 yield() # consume event & then close @test tc[] == 3 sleep(0.1) # no further events @@ -479,7 +479,7 @@ end close(async) @test !isopen(async) Base.process_events() # and close - @test tc[] == 0 + @test tc[] == 1 yield() # consume event & then close @test tc[] == 1 sleep(0.1) # no further events From 35264b7f23e0f04c40026cda055787d23b9946c9 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Fri, 27 Oct 2023 16:34:09 +0200 Subject: [PATCH 05/38] Add aliasing warnings to docstrings for mutating functions in Base (#50824) (cherry picked from commit 58030da3bc4e6790d7bafe66d5f37b382dd6df3c) --- base/Base.jl | 6 ++++++ base/abstractarray.jl | 6 ++++++ base/abstractset.jl | 8 ++++++++ base/accumulate.jl | 8 ++++++++ base/array.jl | 6 ++++++ base/asyncmap.jl | 2 ++ base/combinatorics.jl | 4 ++++ base/multidimensional.jl | 5 +++-- base/reducedim.jl | 22 ++++++++++++++++++++-- base/sort.jl | 4 ++++ doc/src/manual/functions.md | 3 +++ 11 files changed, 70 insertions(+), 4 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 0ec70add2a2c4..6ed423a799e4e 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -115,6 +115,12 @@ time_ns() = ccall(:jl_hrtime, UInt64, ()) start_base_include = time_ns() +# A warning to be interpolated in the docstring of every dangerous mutating function in Base, see PR #50824 +const _DOCS_ALIASING_WARNING = """ +!!! warning + Behavior can be unexpected when any mutated argument shares memory with any other argument. +""" + ## Load essential files and libraries include("essentials.jl") include("ctypes.jl") diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 6f9823a5be5d7..c9a544425d8d0 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -905,6 +905,8 @@ If `dst` and `src` are of the same type, `dst == src` should hold after the call. If `dst` and `src` are multidimensional arrays, they must have equal [`axes`](@ref). +$(_DOCS_ALIASING_WARNING) + See also [`copyto!`](@ref). !!! compat "Julia 1.1" @@ -1369,6 +1371,8 @@ _unsafe_ind2sub(sz, i) = (@inline; _ind2sub(sz, i)) Store values from array `X` within some subset of `A` as specified by `inds`. The syntax `A[inds...] = X` is equivalent to `(setindex!(A, X, inds...); X)`. +$(_DOCS_ALIASING_WARNING) + # Examples ```jldoctest julia> A = zeros(2,2); @@ -3339,6 +3343,8 @@ end Like [`map`](@ref), but stores the result in `destination` rather than a new collection. `destination` must be at least as large as the smallest collection. +$(_DOCS_ALIASING_WARNING) + See also: [`map`](@ref), [`foreach`](@ref), [`zip`](@ref), [`copyto!`](@ref). # Examples diff --git a/base/abstractset.jl b/base/abstractset.jl index 5d0d65dad2de6..a6b123ceab007 100644 --- a/base/abstractset.jl +++ b/base/abstractset.jl @@ -65,6 +65,8 @@ const ∪ = union Construct the [`union`](@ref) of passed in sets and overwrite `s` with the result. Maintain order with arrays. +$(_DOCS_ALIASING_WARNING) + # Examples ```jldoctest julia> a = Set([3, 4, 5]); @@ -182,6 +184,8 @@ const ∩ = intersect Intersect all passed in sets and overwrite `s` with the result. Maintain order with arrays. + +$(_DOCS_ALIASING_WARNING) """ function intersect!(s::AbstractSet, itrs...) for x in itrs @@ -218,6 +222,8 @@ setdiff(s) = union(s) Remove from set `s` (in-place) each element of each iterable from `itrs`. Maintain order with arrays. +$(_DOCS_ALIASING_WARNING) + # Examples ```jldoctest julia> a = Set([1, 3, 4, 5]); @@ -272,6 +278,8 @@ symdiff(s) = symdiff!(copy(s)) Construct the symmetric difference of the passed in sets, and overwrite `s` with the result. When `s` is an array, the order is maintained. Note that in this case the multiplicity of elements matters. + +$(_DOCS_ALIASING_WARNING) """ function symdiff!(s::AbstractSet, itrs...) for x in itrs diff --git a/base/accumulate.jl b/base/accumulate.jl index eeb9759e125c7..a2d8a1d368d86 100644 --- a/base/accumulate.jl +++ b/base/accumulate.jl @@ -42,6 +42,8 @@ end cumsum!(B, A; dims::Integer) Cumulative sum of `A` along the dimension `dims`, storing the result in `B`. See also [`cumsum`](@ref). + +$(_DOCS_ALIASING_WARNING) """ cumsum!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(add_sum, B, A, dims=dims) @@ -150,6 +152,8 @@ cumsum(itr) = accumulate(add_sum, itr) Cumulative product of `A` along the dimension `dims`, storing the result in `B`. See also [`cumprod`](@ref). + +$(_DOCS_ALIASING_WARNING) """ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(mul_prod, B, A, dims=dims) @@ -159,6 +163,8 @@ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = Cumulative product of a vector `x`, storing the result in `y`. See also [`cumprod`](@ref). + +$(_DOCS_ALIASING_WARNING) """ cumprod!(y::AbstractVector, x::AbstractVector) = cumprod!(y, x, dims=1) @@ -301,6 +307,8 @@ Cumulative operation `op` on `A` along the dimension `dims`, storing the result Providing `dims` is optional for vectors. If the keyword argument `init` is given, its value is used to instantiate the accumulation. +$(_DOCS_ALIASING_WARNING) + See also [`accumulate`](@ref), [`cumsum!`](@ref), [`cumprod!`](@ref). # Examples diff --git a/base/array.jl b/base/array.jl index 7fb3d0501bb7b..535aad691c78d 100644 --- a/base/array.jl +++ b/base/array.jl @@ -322,6 +322,8 @@ source and `do` in the destination (1-indexed). The `unsafe` prefix on this function indicates that no validation is performed to ensure that N is inbounds on either array. Incorrect usage may corrupt or segfault your program, in the same manner as C. + +$(_DOCS_ALIASING_WARNING) """ function unsafe_copyto!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T t1 = @_gc_preserve_begin dest @@ -1781,6 +1783,8 @@ place of the removed items; in this case, `indices` must be a `AbstractUnitRange To insert `replacement` before an index `n` without removing any items, use `splice!(collection, n:n-1, replacement)`. +$(_DOCS_ALIASING_WARNING) + !!! compat "Julia 1.5" Prior to Julia 1.5, `indices` must always be a `UnitRange`. @@ -2760,6 +2764,8 @@ Remove the items at all the indices which are not given by `inds`, and return the modified `a`. Items which are kept are shifted to fill the resulting gaps. +$(_DOCS_ALIASING_WARNING) + `inds` must be an iterator of sorted and unique integer indices. See also [`deleteat!`](@ref). diff --git a/base/asyncmap.jl b/base/asyncmap.jl index be16ba1b27610..c81afbb7e9115 100644 --- a/base/asyncmap.jl +++ b/base/asyncmap.jl @@ -394,6 +394,8 @@ length(itr::AsyncGenerator) = length(itr.collector.enumerator) Like [`asyncmap`](@ref), but stores output in `results` rather than returning a collection. + +$(_DOCS_ALIASING_WARNING) """ function asyncmap!(f, r, c1, c...; ntasks=0, batch_size=nothing) foreach(identity, AsyncCollector(f, r, c1, c...; ntasks=ntasks, batch_size=batch_size)) diff --git a/base/combinatorics.jl b/base/combinatorics.jl index d09a5b6c0ce83..4a46e95113840 100644 --- a/base/combinatorics.jl +++ b/base/combinatorics.jl @@ -169,6 +169,8 @@ it is even faster to write into a pre-allocated output array with `u .= @view v[ (Even though `permute!` overwrites `v` in-place, it internally requires some allocation to keep track of which elements have been moved.) +$(_DOCS_ALIASING_WARNING) + See also [`invpermute!`](@ref). # Examples @@ -222,6 +224,8 @@ Note that if you have a pre-allocated output array (e.g. `u = similar(v)`), it is quicker to instead employ `u[p] = v`. (`invpermute!` internally allocates a copy of the data.) +$(_DOCS_ALIASING_WARNING) + # Examples ```jldoctest julia> A = [1, 1, 3, 4]; diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 2b4bf54d88826..02f2075919fbd 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -1179,8 +1179,7 @@ circshift!(dest::AbstractArray, src, ::Tuple{}) = copyto!(dest, src) Circularly shift, i.e. rotate, the data in `src`, storing the result in `dest`. `shifts` specifies the amount to shift in each dimension. -The `dest` array must be distinct from the `src` array (they cannot -alias each other). +$(_DOCS_ALIASING_WARNING) See also [`circshift`](@ref). """ @@ -1238,6 +1237,8 @@ their indices; any offset results in a (circular) wraparound. If the arrays have overlapping indices, then on the domain of the overlap `dest` agrees with `src`. +$(_DOCS_ALIASING_WARNING) + See also: [`circshift`](@ref). # Examples diff --git a/base/reducedim.jl b/base/reducedim.jl index c1c58ccdfefed..f5a22940310cf 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -448,6 +448,8 @@ _count(f, A::AbstractArrayOrBroadcasted, dims, init) = mapreduce(_bool(f), add_s Count the number of elements in `A` for which `f` returns `true` over the singleton dimensions of `r`, writing the result into `r` in-place. +$(_DOCS_ALIASING_WARNING) + !!! compat "Julia 1.5" inplace `count!` was added in Julia 1.5. @@ -525,8 +527,8 @@ sum(f, A::AbstractArray; dims) sum!(r, A) Sum elements of `A` over the singleton dimensions of `r`, and write results to `r`. -Note that since the sum! function is intended to operate without making any allocations, -the target should not alias with the source. + +$(_DOCS_ALIASING_WARNING) # Examples ```jldoctest @@ -601,6 +603,8 @@ prod(f, A::AbstractArray; dims) Multiply elements of `A` over the singleton dimensions of `r`, and write results to `r`. +$(_DOCS_ALIASING_WARNING) + # Examples ```jldoctest julia> A = [1 2; 3 4] @@ -678,6 +682,8 @@ maximum(f, A::AbstractArray; dims) Compute the maximum value of `A` over the singleton dimensions of `r`, and write results to `r`. +$(_DOCS_ALIASING_WARNING) + # Examples ```jldoctest julia> A = [1 2; 3 4] @@ -755,6 +761,8 @@ minimum(f, A::AbstractArray; dims) Compute the minimum value of `A` over the singleton dimensions of `r`, and write results to `r`. +$(_DOCS_ALIASING_WARNING) + # Examples ```jldoctest julia> A = [1 2; 3 4] @@ -820,6 +828,8 @@ extrema(f, A::AbstractArray; dims) Compute the minimum and maximum value of `A` over the singleton dimensions of `r`, and write results to `r`. +$(_DOCS_ALIASING_WARNING) + !!! compat "Julia 1.8" This method requires Julia 1.8 or later. @@ -895,6 +905,8 @@ all(::Function, ::AbstractArray; dims) Test whether all values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. +$(_DOCS_ALIASING_WARNING) + # Examples ```jldoctest julia> A = [true false; true false] @@ -968,6 +980,8 @@ any(::Function, ::AbstractArray; dims) Test whether any values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. +$(_DOCS_ALIASING_WARNING) + # Examples ```jldoctest julia> A = [true false; true false] @@ -1085,6 +1099,8 @@ end Find the minimum of `A` and the corresponding linear index along singleton dimensions of `rval` and `rind`, and store the results in `rval` and `rind`. `NaN` is treated as less than all other values except `missing`. + +$(_DOCS_ALIASING_WARNING) """ function findmin!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) @@ -1156,6 +1172,8 @@ end Find the maximum of `A` and the corresponding linear index along singleton dimensions of `rval` and `rind`, and store the results in `rval` and `rind`. `NaN` is treated as greater than all other values except `missing`. + +$(_DOCS_ALIASING_WARNING) """ function findmax!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) diff --git a/base/sort.jl b/base/sort.jl index abf0b9ed07682..b6a2d664e39f4 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1583,6 +1583,8 @@ v[ix[k]] == partialsort(v, k) The return value is the `k`th element of `ix` if `k` is an integer, or view into `ix` if `k` is a range. +$(Base._DOCS_ALIASING_WARNING) + # Examples ```jldoctest julia> v = [3, 1, 2, 1]; @@ -1707,6 +1709,8 @@ end Like [`sortperm`](@ref), but accepts a preallocated index vector or array `ix` with the same `axes` as `A`. `ix` is initialized to contain the values `LinearIndices(A)`. +$(Base._DOCS_ALIASING_WARNING) + !!! compat "Julia 1.9" The method accepting `dims` requires at least Julia 1.9. diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index a724f450dccfa..97f84f35f3eef 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -102,6 +102,9 @@ As a common convention in Julia (not a syntactic requirement), such a function w [typically be named `f!(x, y)`](@ref man-punctuation) rather than `f(x, y)`, as a visual reminder at the call site that at least one of the arguments (often the first one) is being mutated. +!!! warning "Shared memory between arguments" + The behavior of a mutating function can be unexpected when a mutated argument shares memory with another argument, a situation known as aliasing (e.g. when one is a view of the other). + Unless the function docstring explicitly indicates that aliasing produces the expected result, it is the responsibility of the caller to ensure proper behavior on such inputs. ## Argument-type declarations From 3a4b1401d359ff9c94641272d68680b1f411202b Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 1 Nov 2023 13:18:08 -0400 Subject: [PATCH 06/38] further fix to the new promoting method for AbstractDateTime subtraction (#51967) (cherry picked from commit 405ce111a86ea85a97734efed0ea995cfdc7f56c) --- stdlib/Dates/src/arithmetic.jl | 2 +- stdlib/Dates/test/arithmetic.jl | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/stdlib/Dates/src/arithmetic.jl b/stdlib/Dates/src/arithmetic.jl index b379747c5f9e9..83a2873b43409 100644 --- a/stdlib/Dates/src/arithmetic.jl +++ b/stdlib/Dates/src/arithmetic.jl @@ -7,7 +7,7 @@ # TimeType arithmetic (+)(x::TimeType) = x (-)(x::T, y::T) where {T<:TimeType} = x.instant - y.instant -(-)(x::DateTime, y::DateTime) = x.instant - y.instant +(-)(x::T, y::T) where {T<:AbstractDateTime} = x.instant - y.instant (-)(x::AbstractDateTime, y::AbstractDateTime) = -(promote(x, y)...) # Date-Time arithmetic diff --git a/stdlib/Dates/test/arithmetic.jl b/stdlib/Dates/test/arithmetic.jl index d266526496c45..e3ed6ace57c69 100644 --- a/stdlib/Dates/test/arithmetic.jl +++ b/stdlib/Dates/test/arithmetic.jl @@ -14,10 +14,15 @@ end struct MonthlyDate <: TimeType instant::Dates.UTInstant{Month} end +struct OtherTime <: Dates.AbstractDateTime + instant::Dates.UTInstant{Nanosecond} +end @testset "TimeType arithmetic" begin @test_throws MethodError DateTime(2023, 5, 2) - Date(2023, 5, 1) # check that - between two same-type TimeTypes works by default @test MonthlyDate(Dates.UTInstant(Month(10))) - MonthlyDate(Dates.UTInstant(Month(1))) == Month(9) + # ... and between two same-type AbstractDateTimes + @test OtherTime(Dates.UTInstant(Nanosecond(2))) - OtherTime(Dates.UTInstant(Nanosecond(1))) == Nanosecond(1) end @testset "Wrapping arithmetic for Months" begin From 2d6af7ebf494e1ff81b7b117327c6075d0dfe81c Mon Sep 17 00:00:00 2001 From: Paul Berg Date: Thu, 2 Nov 2023 14:53:26 +0100 Subject: [PATCH 07/38] macroexpand: handle const/atomic struct fields correctly (#51980) Fixes #51899 (cherry picked from commit 924aac92b977b7a492c8cba878ff14329a4ad4b0) --- src/macroexpand.scm | 28 +++++++++++++++++----------- test/syntax.jl | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/macroexpand.scm b/src/macroexpand.scm index e0e809eee08f1..6e390a6c24cf2 100644 --- a/src/macroexpand.scm +++ b/src/macroexpand.scm @@ -340,8 +340,22 @@ (define (reescape ux x) (if (and (pair? x) (eq? (car x) 'escape)) - (reescape '(escape ,ux) (cadr x))) - ux) + (reescape `(escape ,ux) (cadr x)) + ux)) + +;; type has special behavior: identifiers inside are +;; field names, not expressions. +(define (resolve-struct-field-expansion x env m parent-scope inarg) + (let ((ux (unescape x))) + (cond + ((atom? ux) ux) + ((and (pair? ux) (eq? (car ux) '|::|)) + `(|::| ,(unescape (cadr ux)) + ,(resolve-expansion-vars- (reescape (caddr ux) x) env m parent-scope inarg))) + ((and (pair? ux) (memq (car ux) '(const atomic))) + `(,(car ux) ,(resolve-struct-field-expansion (reescape (cadr ux) x) env m parent-scope inarg))) + (else + (resolve-expansion-vars-with-new-env x env m parent-scope inarg))))) (define (resolve-expansion-vars- e env m parent-scope inarg) (cond ((or (eq? e 'begin) (eq? e 'end) (eq? e 'ccall) (eq? e 'cglobal) (underscore-symbol? e)) @@ -377,16 +391,8 @@ ((symbolicgoto) e) ((struct) `(struct ,(cadr e) ,(resolve-expansion-vars- (caddr e) env m parent-scope inarg) - ;; type has special behavior: identifiers inside are - ;; field names, not expressions. ,(map (lambda (x) - (let ((ux (unescape x))) - (cond ((atom? ux) ux) - ((and (pair? ux) (eq? (car ux) '|::|)) - `(|::| ,(unescape (cadr ux)) - ,(resolve-expansion-vars- (reescape (caddr ux) x) env m parent-scope inarg))) - (else - (resolve-expansion-vars-with-new-env x env m parent-scope inarg))))) + (resolve-struct-field-expansion x env m parent-scope inarg)) (cadddr e)))) ((parameters) diff --git a/test/syntax.jl b/test/syntax.jl index c69843785136e..3516c306c42d4 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -1783,6 +1783,43 @@ end @test B28593.var.name === :S @test C28593.var.name === :S +# issue #51899 +macro struct_macro_51899() + quote + mutable struct Struct51899 + const const_field + const const_field_with_type::Int + $(esc(Expr(:const, :(escaped_const_field::MyType)))) + @atomic atomic_field + @atomic atomic_field_with_type::Int + end + end +end + +let ex = @macroexpand @struct_macro_51899() + const_field, const_field_with_type, escaped_const_field, + atomic_field, atomic_field_with_type = filter(x -> isa(x, Expr), ex.args[end].args[end].args) + @test Meta.isexpr(const_field, :const) + @test const_field.args[1] === :const_field + + @test Meta.isexpr(const_field_with_type, :const) + @test Meta.isexpr(const_field_with_type.args[1], :(::)) + @test const_field_with_type.args[1].args[1] === :const_field_with_type + @test const_field_with_type.args[1].args[2] == GlobalRef(@__MODULE__, :Int) + + @test Meta.isexpr(escaped_const_field, :const) + @test Meta.isexpr(const_field_with_type.args[1], :(::)) + @test escaped_const_field.args[1].args[1] === :escaped_const_field + @test escaped_const_field.args[1].args[2] === :MyType + + @test Meta.isexpr(atomic_field, :atomic) + @test atomic_field.args[1] === :atomic_field + + @test Meta.isexpr(atomic_field_with_type, :atomic) + @test atomic_field_with_type.args[1].args[1] === :atomic_field_with_type + @test atomic_field_with_type.args[1].args[2] == GlobalRef(@__MODULE__, :Int) +end + # issue #25955 macro noeffect25955(e) return e From 3b6e9dd1cf95e10964ca09dd91cd41a0a96fb98d Mon Sep 17 00:00:00 2001 From: Ashutosh Bharambe <43771652+ashutosh-b-b@users.noreply.github.com> Date: Thu, 2 Nov 2023 20:07:01 +0000 Subject: [PATCH 08/38] [Artifacts] Pass artifacts dictionary to `ensure_artifact_installed` dispatch (#51995) The artifacts dict is not lowered to ensure_artifact_installed which causes to load the ".toml" during runtime for lazy artifacts (cherry picked from commit 9bc6994fd6e61d98c12733f673aaa1a296465eb6) --- stdlib/Artifacts/src/Artifacts.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Artifacts/src/Artifacts.jl b/stdlib/Artifacts/src/Artifacts.jl index 70593bfadae05..5daf994bfbf10 100644 --- a/stdlib/Artifacts/src/Artifacts.jl +++ b/stdlib/Artifacts/src/Artifacts.jl @@ -548,7 +548,7 @@ function _artifact_str(__module__, artifacts_toml, name, path_tail, artifact_dic if nameof(lazyartifacts) in (:Pkg, :Artifacts) Base.depwarn("using Pkg instead of using LazyArtifacts is deprecated", :var"@artifact_str", force=true) end - return jointail(lazyartifacts.ensure_artifact_installed(string(name), artifacts_toml; platform), path_tail) + return jointail(lazyartifacts.ensure_artifact_installed(string(name), meta, artifacts_toml; platform), path_tail) end error("Artifact $(repr(name)) is a lazy artifact; package developers must call `using LazyArtifacts` in $(__module__) before using lazy artifacts.") end From 6f864a76665d1d7884a537c447a9100e330d6d85 Mon Sep 17 00:00:00 2001 From: Milan Bouchet-Valat Date: Thu, 9 Nov 2023 11:13:18 +0100 Subject: [PATCH 09/38] Bump Statistics (#52030) This bumps Statistics to the latest commit of the release-1.10 branch in order to backport JuliaStats/Statistics.jl#153. See https://github.com/JuliaData/DataFrames.jl/issues/3383. Cc: @bkamins @George9000 --- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + stdlib/Statistics.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Statistics-08562cb8abbe67829e437aa8533994121c350c05.tar.gz/md5 delete mode 100644 deps/checksums/Statistics-08562cb8abbe67829e437aa8533994121c350c05.tar.gz/sha512 create mode 100644 deps/checksums/Statistics-d147f9253aa0f2f71be9c3fed8d51c2215410408.tar.gz/md5 create mode 100644 deps/checksums/Statistics-d147f9253aa0f2f71be9c3fed8d51c2215410408.tar.gz/sha512 diff --git a/deps/checksums/Statistics-08562cb8abbe67829e437aa8533994121c350c05.tar.gz/md5 b/deps/checksums/Statistics-08562cb8abbe67829e437aa8533994121c350c05.tar.gz/md5 deleted file mode 100644 index 42034e9a42a7b..0000000000000 --- a/deps/checksums/Statistics-08562cb8abbe67829e437aa8533994121c350c05.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -735ae885122f81261ecf152853ce0b42 diff --git a/deps/checksums/Statistics-08562cb8abbe67829e437aa8533994121c350c05.tar.gz/sha512 b/deps/checksums/Statistics-08562cb8abbe67829e437aa8533994121c350c05.tar.gz/sha512 deleted file mode 100644 index dbec046cef091..0000000000000 --- a/deps/checksums/Statistics-08562cb8abbe67829e437aa8533994121c350c05.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -42dd950bd59fa91540806f9c1b26c391373ef4508106e59f875ca256805951784fe4469b834978a6d20ab340eb979da97b0f4b32c8f53626b03d7b3f84e4cb5a diff --git a/deps/checksums/Statistics-d147f9253aa0f2f71be9c3fed8d51c2215410408.tar.gz/md5 b/deps/checksums/Statistics-d147f9253aa0f2f71be9c3fed8d51c2215410408.tar.gz/md5 new file mode 100644 index 0000000000000..406ed9e1d1af7 --- /dev/null +++ b/deps/checksums/Statistics-d147f9253aa0f2f71be9c3fed8d51c2215410408.tar.gz/md5 @@ -0,0 +1 @@ +30cb9242a167ec2ed652d2923d1f7d23 diff --git a/deps/checksums/Statistics-d147f9253aa0f2f71be9c3fed8d51c2215410408.tar.gz/sha512 b/deps/checksums/Statistics-d147f9253aa0f2f71be9c3fed8d51c2215410408.tar.gz/sha512 new file mode 100644 index 0000000000000..16b0e72537968 --- /dev/null +++ b/deps/checksums/Statistics-d147f9253aa0f2f71be9c3fed8d51c2215410408.tar.gz/sha512 @@ -0,0 +1 @@ +c325f09ed306bef6d4fe186aa4ce9300f1e400752f776b23220d828468f8bae16a9058683d04176bd32a47fdc32aa71cb6a479a1bfa3102944838658581c94d7 diff --git a/stdlib/Statistics.version b/stdlib/Statistics.version index 05d7839223924..336d7c731bd47 100644 --- a/stdlib/Statistics.version +++ b/stdlib/Statistics.version @@ -1,4 +1,4 @@ STATISTICS_BRANCH = master -STATISTICS_SHA1 = 08562cb8abbe67829e437aa8533994121c350c05 +STATISTICS_SHA1 = d147f9253aa0f2f71be9c3fed8d51c2215410408 STATISTICS_GIT_URL := https://github.com/JuliaStats/Statistics.jl.git STATISTICS_TAR_URL = https://api.github.com/repos/JuliaStats/Statistics.jl/tarball/$1 From 405e1a4124cdc984724e6dc741744e950c3bc2a4 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 6 Sep 2023 07:35:01 -0300 Subject: [PATCH 10/38] Wait for other threads to finish compiling before exiting (#51213) This avoids a crashes where we run the destructors because C++ is fun and runs destructors before thread exit. (cherry picked from commit 3d88550124d30ac88fd68a851df2a67216a5854b) --- src/codegen.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index d28971b9a4caf..ea00c9488e303 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -9294,6 +9294,8 @@ extern "C" JL_DLLEXPORT_CODEGEN void jl_teardown_codegen_impl() JL_NOTSAFEPOINT if (jl_ExecutionEngine) jl_ExecutionEngine->printTimers(); PrintStatistics(); + JL_LOCK(&jl_codegen_lock); // TODO: If this lock gets removed reconsider + // LLVM global state/destructors (maybe a rwlock) } // the rest of this file are convenience functions From f5adeced00b9577a9584250f76b3256b5741e2c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Wed, 1 Nov 2023 14:28:55 +0000 Subject: [PATCH 11/38] [devdocs] Improve documentation about building external forks of LLVM (#50207) Suggested by @vchuravy. --------- Co-authored-by: Jameson Nash (cherry picked from commit 2adf54a951d70d6963af7f22950ca28c032000af) --- Make.inc | 4 +++ doc/src/devdocs/build/build.md | 52 ++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/Make.inc b/Make.inc index 1163f66c2fdbd..729870fbad860 100644 --- a/Make.inc +++ b/Make.inc @@ -359,6 +359,10 @@ USE_MLIR := 0 # Options to use RegionVectorizer USE_RV := 0 +# Use `ccache` for speeding up recompilation of the C/C++ part of Julia. +# Requires the `ccache` executable to be in the `PATH` environment variable. +USECCACHE := 0 + # Cross-compile #XC_HOST := i686-w64-mingw32 #XC_HOST := x86_64-w64-mingw32 diff --git a/doc/src/devdocs/build/build.md b/doc/src/devdocs/build/build.md index ad3871c2e70f0..c915c669f2c5e 100644 --- a/doc/src/devdocs/build/build.md +++ b/doc/src/devdocs/build/build.md @@ -60,6 +60,16 @@ To run julia from anywhere you can: - write `prefix=/path/to/install/folder` into `Make.user` and then run `make install`. If there is a version of Julia already installed in this folder, you should delete it before running `make install`. +Some of the options you can set to control the build of Julia are listed and documented at the beginning of the file `Make.inc`, but you should never edit it for this purpose, use `Make.user` instead. + +Julia's Makefiles define convenient automatic rules called `print-` for printing the value of variables, replacing `` with the name of the variable to print the value of. +For example +```console +$ make print-JULIA_PRECOMPILE +JULIA_PRECOMPILE=1 +``` +These rules are useful for debugging purposes. + Now you should be able to run Julia like this: julia @@ -239,10 +249,48 @@ For packaging Julia with LLVM, we recommend either: - bundling a Julia-only LLVM library inside the Julia package, or - adding the patches to the LLVM package of the distribution. * A complete list of patches is available in on [Github](https://github.com/JuliaLang/llvm-project) see the `julia-release/15.x` branch. - * The only Julia-specific patch is the lib renaming (`llvm-symver-jlprefix.patch`), which should _not_ be applied to a system LLVM. + * The only Julia-specific patch is the lib renaming (`llvm7-symver-jlprefix.patch`), which should _not_ be applied to a system LLVM. * The remaining patches are all upstream bug fixes, and have been contributed into upstream LLVM. -Using an unpatched or different version of LLVM will result in errors and/or poor performance. Though Julia can be built with newer LLVM versions, support for this should be regarded as experimental and not suitable for packaging. +Using an unpatched or different version of LLVM will result in errors and/or poor performance. +You can build a different version of LLVM from a remote Git repository with the following options in the `Make.user` file: + +```make +# Force source build of LLVM +USE_BINARYBUILDER_LLVM = 0 +# Use Git for fetching LLVM source code +# this is either `1` to get all of them +DEPS_GIT = 1 +# or a space-separated list of specific dependencies to download with git +DEPS_GIT = llvm + +# Other useful options: +#URL of the Git repository you want to obtain LLVM from: +# LLVM_GIT_URL = ... +#Name of the alternate branch to clone from git +# LLVM_BRANCH = julia-16.0.6-0 +#SHA hash of the alterate commit to check out automatically +# LLVM_SHA1 = $(LLVM_BRANCH) +#List of LLVM targets to build. It is strongly recommended to keep at least all the +#default targets listed in `deps/llvm.mk`, even if you don't necessarily need all of them. +# LLVM_TARGETS = ... +#Use ccache for faster recompilation in case you need to restart a build. +# USECCACHE = 1 +# CMAKE_GENERATOR=Ninja +# LLVM_ASSERTIONS=1 +# LLVM_DEBUG=Symbols +``` + +The various build phases are controlled by specific files: + * `deps/llvm.version` : touch or change to checkout a new version, `make get-llvm check-llvm` + * `deps/srccache/llvm/source-extracted` : result of `make extract-llvm` + * `deps/llvm/build_Release*/build-configured` : result of `make configure-llvm` + * `deps/llvm/build_Release*/build-configured` : result of `make compile-llvm` + * `usr-staging/llvm/build_Release*.tgz` : result of `make stage-llvm` (regenerate with `make reinstall-llvm`) + * `usr/manifest/llvm` : result of `make install-llvm` (regenerate with `make uninstall-llvm`) + * `make version-check-llvm` : runs every time to warn the user if there are local modifications + +Though Julia can be built with newer LLVM versions, support for this should be regarded as experimental and not suitable for packaging. ### libuv From 3ffa3db3b723c81696e76d668e567f30a65aefc3 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 8 Nov 2023 20:44:29 -0500 Subject: [PATCH 12/38] fix sorting for iterables that define copymutable (#52086) (cherry picked from commit f99e6bfe19b7b8d73975f5f96e54052e9725f1c8) --- base/sort.jl | 4 ++-- test/sorting.jl | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/base/sort.jl b/base/sort.jl index b6a2d664e39f4..253faa162d8f9 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1496,7 +1496,7 @@ function sort(v; kws...) size = IteratorSize(v) size == HasShape{0}() && throw(ArgumentError("$v cannot be sorted")) size == IsInfinite() && throw(ArgumentError("infinite iterator $v cannot be sorted")) - sort!(copymutable(v); kws...) + sort!(collect(v); kws...) end sort(v::AbstractVector; kws...) = sort!(copymutable(v); kws...) # for method disambiguation sort(::AbstractString; kws...) = @@ -1508,7 +1508,7 @@ function sort(x::NTuple{N}; lt::Function=isless, by::Function=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward) where N o = ord(lt,by,rev,order) if N > 9 - v = sort!(copymutable(x), DEFAULT_STABLE, o) + v = sort!(collect(x), DEFAULT_STABLE, o) tuple((v[i] for i in 1:N)...) else _sort(x, o) diff --git a/test/sorting.jl b/test/sorting.jl index 147a70a5db7d9..c12f07ae750d7 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -559,6 +559,11 @@ end @test_throws ArgumentError sort("string") @test_throws ArgumentError("1 cannot be sorted") sort(1) + + @test sort(Set((1, 3, 6))) == [1, 3, 6] + @test sort(Dict((1=>9, 3=>2, 6=>5))) == [1=>9, 3=>2, 6=>5] + @test sort(keys(Dict((1=>2, 3=>5, 6=>9)))) == [1, 3, 6] + @test sort(values(Dict((1=>9, 3=>2, 6=>5)))) == [2, 5, 9] end @testset "sort!(::AbstractVector{<:Integer}) with short int range" begin From 8b85bbf150a4137b943687829bf41a4f6f0a5f5a Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Fri, 10 Nov 2023 05:47:38 -0600 Subject: [PATCH 13/38] Fix errors in `sort` docstring (#52098) Two chagnes wrapped into one `Base.copymutable` => `Base.copymutable` & `collect` and `Base.copymutable` => `similar` & words. Followup for #52086 and #46104; also fixes #51932 (though we still may want to make `copymutable` public at some point) --------- Co-authored-by: Jameson Nash (cherry picked from commit 42c088b8b3e678edd19e5cb9ea54503e43624d06) --- base/sort.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/sort.jl b/base/sort.jl index 253faa162d8f9..a5d3927c450c4 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1470,7 +1470,8 @@ end Variant of [`sort!`](@ref) that returns a sorted copy of `v` leaving `v` itself unmodified. -Uses `Base.copymutable` to support immutable collections and iterables. +Returns something [`similar`](@ref) to `v` when `v` is an `AbstractArray` and uses +[`collect`](@ref) to support arbitrary non-`AbstractArray` iterables. !!! compat "Julia 1.10" `sort` of arbitrary iterables requires at least Julia 1.10. From 1ddd6da6480617ba50dff93f215c9035f6195587 Mon Sep 17 00:00:00 2001 From: Claire Foster Date: Mon, 13 Nov 2023 18:03:29 +1000 Subject: [PATCH 14/38] Bump JuliaSyntax to 0.4.7 (#52136) This fixes a whole bunch of small but annoying bugs, as described in the JuliaSyntax-0.4.7 release notes https://github.com/JuliaLang/JuliaSyntax.jl/releases/tag/v0.4.7 I've been careful about cutting the JuliaSyntax-0.4.7 release from nonbreaking changes, so we should be able to backport this to 1.10. --- Extended notes about compatibility * The public keyword in https://github.com/JuliaLang/JuliaSyntax.jl/pull/320 is released in JuliaSyntax-0.4.7 but JuliaSyntax is multi-version aware so this is disabled when used as the default parser in Julia 1.10, but is enabled in 1.11-DEV. So should be backportable. * We aim for parsing to `Expr` to always be stable in JuliaSyntax and independent of the host Julia `VERSION`, but we're not fully there yet for 1.11 / 1.10 due to https://github.com/JuliaLang/JuliaSyntax.jl/issues/377. Thus some careful management of the JuliaSyntax-0.4.x branch for now. (cherry picked from commit 85d7ccad2cc2154d9c3371283512eec33252cc40) --- deps/JuliaSyntax.version | 2 +- .../md5 | 1 + .../sha512 | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/md5 create mode 100644 deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/sha512 diff --git a/deps/JuliaSyntax.version b/deps/JuliaSyntax.version index e726eea3656fc..7f124715024ce 100644 --- a/deps/JuliaSyntax.version +++ b/deps/JuliaSyntax.version @@ -1,4 +1,4 @@ JULIASYNTAX_BRANCH = main -JULIASYNTAX_SHA1 = 045d156c44dbb87769c7416d049a7c08908539d4 +JULIASYNTAX_SHA1 = 4f1731d6ce7c2465fc21ea245110b7a39f34658a JULIASYNTAX_GIT_URL := https://github.com/JuliaLang/JuliaSyntax.jl.git JULIASYNTAX_TAR_URL = https://api.github.com/repos/JuliaLang/JuliaSyntax.jl/tarball/$1 diff --git a/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/md5 b/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/md5 new file mode 100644 index 0000000000000..c2663955ec773 --- /dev/null +++ b/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/md5 @@ -0,0 +1 @@ +8c9d9579eeab1ba40f978a32c9db9900 diff --git a/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/sha512 b/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/sha512 new file mode 100644 index 0000000000000..46647cb3e432b --- /dev/null +++ b/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/sha512 @@ -0,0 +1 @@ +1bdad624f61482b55deba8727fea1c087bfaea9e1f8afa3b44b984441fb7e663dac067baa4a96ae2d4cbd4a46ae8c87e9d20d2dfcd17046ad194711304184e57 From 99c4ae46a610a62699a6a59f66d21723734b911f Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Sat, 30 Sep 2023 12:27:10 -0300 Subject: [PATCH 15/38] Make allocopt respect the GC verifier rules with non usual address spaces (#51520) On AMDGPU, this was generating a `addrspace(10)` pointer to an `alloca` which is illegal and lead to other issues. (cherry picked from commit af9a7af3b27c0ff22179a7dcd30bf6753d3d575f) --- src/llvm-alloc-opt.cpp | 4 +--- test/llvmpasses/alloc-opt-gcframe-addrspaces.ll | 11 ++++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index b87a5a6799b0b..751cae6419dda 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -643,8 +643,6 @@ void Optimizer::moveToStack(CallInst *orig_inst, size_t sz, bool has_ref) } insertLifetime(ptr, ConstantInt::get(Type::getInt64Ty(prolog_builder.getContext()), sz), orig_inst); Instruction *new_inst = cast(prolog_builder.CreateBitCast(ptr, JuliaType::get_pjlvalue_ty(prolog_builder.getContext(), buff->getType()->getPointerAddressSpace()))); - if (orig_inst->getModule()->getDataLayout().getAllocaAddrSpace() != 0) - new_inst = cast(prolog_builder.CreateAddrSpaceCast(new_inst, JuliaType::get_pjlvalue_ty(prolog_builder.getContext(), orig_inst->getType()->getPointerAddressSpace()))); new_inst->takeName(orig_inst); auto simple_replace = [&] (Instruction *orig_i, Instruction *new_i) { @@ -692,7 +690,7 @@ void Optimizer::moveToStack(CallInst *orig_inst, size_t sz, bool has_ref) else if (auto call = dyn_cast(user)) { auto callee = call->getCalledOperand(); if (pass.pointer_from_objref_func == callee) { - call->replaceAllUsesWith(new_i); + call->replaceAllUsesWith(prolog_builder.CreateAddrSpaceCast(new_i, call->getCalledFunction()->getReturnType())); call->eraseFromParent(); return; } diff --git a/test/llvmpasses/alloc-opt-gcframe-addrspaces.ll b/test/llvmpasses/alloc-opt-gcframe-addrspaces.ll index b33f2cdac7dd4..1eefa7e56922f 100644 --- a/test/llvmpasses/alloc-opt-gcframe-addrspaces.ll +++ b/test/llvmpasses/alloc-opt-gcframe-addrspaces.ll @@ -19,15 +19,16 @@ declare {}* @julia.pointer_from_objref({} addrspace(11)*) ; Test that non-0 addrspace allocas are properly emitted and handled ; CHECK-LABEL: @non_zero_addrspace -; CHECK: %1 = alloca i32, align 8, addrspace(5) +; TYPED: %1 = alloca i32, align 8, addrspace(5) ; TYPED: %2 = bitcast i32 addrspace(5)* %1 to i8 addrspace(5)* -; TYPED: %3 = bitcast i8 addrspace(5)* %2 to {} addrspace(5)* -; TYPED: %var1 = addrspacecast {} addrspace(5)* %3 to {} addrspace(10)* +; TYPED: %var1 = bitcast i8 addrspace(5)* %2 to {} addrspace(5)* +; TYPED: %3 = addrspacecast {} addrspace(5)* %var1 to {}* ; TYPED: call void @llvm.lifetime.start.p5i8(i64 4, i8 addrspace(5)* %2) -; OPAQUE: %var1 = addrspacecast ptr addrspace(5) %1 to ptr addrspace(10) -; OPAQUE: call void @llvm.lifetime.start.p5(i64 4, ptr addrspace(5) %1) +; OPAQUE: %var1 = alloca i32, align 8, addrspace(5) +; OPAQUE: %1 = addrspacecast ptr addrspace(5) %var1 to ptr +; OPAQUE: call void @llvm.lifetime.start.p5(i64 4, ptr addrspace(5) %var1) ; CHECK: ret void define void @non_zero_addrspace() { From 0a1e83e27a87b26a9688c3a5c8213da1be0c8472 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 14 Nov 2023 21:42:19 -0500 Subject: [PATCH 16/38] Make c func `abspath` consistent on Windows. Fix tracking path conversion. (#52140) Explanation for the `GetFullPathName` behavior https://developercommunity.visualstudio.com/t/GetFullPath-fails-if-given-empty-string/483359#T-N486167 (cherry picked from commit eaef647957ca5e085eea299cfa9f699c6afe6d8f) --- src/init.c | 11 ++++++++++- test/cmdlineargs.jl | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/init.c b/src/init.c index 4a152ed04b13d..c49ec2ac80be2 100644 --- a/src/init.c +++ b/src/init.c @@ -574,6 +574,14 @@ static char *abspath(const char *in, int nprefix) } } #else + // GetFullPathName intentionally errors if given an empty string so manually insert `.` to invoke cwd + char *in2 = (char*)malloc_s(JL_PATH_MAX); + if (strlen(in) - nprefix == 0) { + memcpy(in2, in, nprefix); + in2[nprefix] = '.'; + in2[nprefix+1] = '\0'; + in = in2; + } DWORD n = GetFullPathName(in + nprefix, 0, NULL, NULL); if (n <= 0) { jl_error("fatal error: jl_options.image_file path too long or GetFullPathName failed"); @@ -584,6 +592,7 @@ static char *abspath(const char *in, int nprefix) jl_error("fatal error: jl_options.image_file path too long or GetFullPathName failed"); } memcpy(out, in, nprefix); + free(in2); #endif return out; } @@ -678,7 +687,7 @@ static void jl_resolve_sysimg_location(JL_IMAGE_SEARCH rel) if (jl_options.output_code_coverage) jl_options.output_code_coverage = absformat(jl_options.output_code_coverage); if (jl_options.tracked_path) - jl_options.tracked_path = absformat(jl_options.tracked_path); + jl_options.tracked_path = abspath(jl_options.tracked_path, 0); const char **cmdp = jl_options.cmds; if (cmdp) { diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 417ac82973558..72d1c6bb5663f 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -465,10 +465,47 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` @test occursin(expected, got) || (expected, got) @test_broken occursin(expected_good, got) + # Ask for coverage in current directory + tdir = dirname(realpath(inputfile)) + cd(tdir) do + # there may be atrailing separator here so use rstrip + @test readchomp(`$exename -E "(Base.JLOptions().code_coverage, rstrip(unsafe_string(Base.JLOptions().tracked_path), '/'))" -L $inputfile + --code-coverage=$covfile --code-coverage=@`) == "(3, $(repr(tdir)))" + end + @test isfile(covfile) + got = read(covfile, String) + rm(covfile) + @test occursin(expected, got) || (expected, got) + @test_broken occursin(expected_good, got) + + # Ask for coverage in relative directory + tdir = dirname(realpath(inputfile)) + cd(dirname(tdir)) do + @test readchomp(`$exename -E "(Base.JLOptions().code_coverage, unsafe_string(Base.JLOptions().tracked_path))" -L $inputfile + --code-coverage=$covfile --code-coverage=@testhelpers`) == "(3, $(repr(tdir)))" + end + @test isfile(covfile) + got = read(covfile, String) + rm(covfile) + @test occursin(expected, got) || (expected, got) + @test_broken occursin(expected_good, got) + + # Ask for coverage in relative directory with dot-dot notation + tdir = dirname(realpath(inputfile)) + cd(tdir) do + @test readchomp(`$exename -E "(Base.JLOptions().code_coverage, unsafe_string(Base.JLOptions().tracked_path))" -L $inputfile + --code-coverage=$covfile --code-coverage=@../testhelpers`) == "(3, $(repr(tdir)))" + end + @test isfile(covfile) + got = read(covfile, String) + rm(covfile) + @test occursin(expected, got) || (expected, got) + @test_broken occursin(expected_good, got) + # Ask for coverage in a different directory tdir = mktempdir() # a dir that contains no code @test readchomp(`$exename -E "(Base.JLOptions().code_coverage, unsafe_string(Base.JLOptions().tracked_path))" -L $inputfile - --code-coverage=$covfile --code-coverage=@$tdir`) == "(3, $(repr(tdir)))" + --code-coverage=$covfile --code-coverage=@$tdir`) == "(3, $(repr(realpath(tdir))))" @test isfile(covfile) got = read(covfile, String) @test isempty(got) From dc2c4f1254603b88a2ff69c2f8bdfc3a8acfca60 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 15 Nov 2023 14:15:16 -0500 Subject: [PATCH 17/38] [REPL] fix computation of startpos for path completions (#52009) Fixes https://github.com/JuliaLang/julia/issues/51985 Ensure that the REPL completions escape and unescape text correctly, using the correct functions, and accounting for exactly what the user has currently typed. The old broken method is left around for Pkg, since it has an over-reliance on it returning incorrect answers. Once Pkg is fixed, we can delete that code. Co-authored-by: Jameson Nash (cherry picked from commit 5edcdc5a234fa69a65f5e2b8d85ac51cfb37a653) --- base/shell.jl | 24 ++- stdlib/REPL/src/REPLCompletions.jl | 266 +++++++++++++++++++--------- stdlib/REPL/test/replcompletions.jl | 58 +++--- test/spawn.jl | 16 +- 4 files changed, 246 insertions(+), 118 deletions(-) diff --git a/base/shell.jl b/base/shell.jl index 5bfd11fb46d29..48214418bdee5 100644 --- a/base/shell.jl +++ b/base/shell.jl @@ -18,12 +18,11 @@ end function shell_parse(str::AbstractString, interpolate::Bool=true; special::AbstractString="", filename="none") - s = SubString(str, firstindex(str)) + last_arg = firstindex(str) # N.B.: This is used by REPLCompletions + s = SubString(str, last_arg) s = rstrip_shell(lstrip(s)) - # N.B.: This is used by REPLCompletions - last_parse = 0:-1 - isempty(s) && return interpolate ? (Expr(:tuple,:()),last_parse) : ([],last_parse) + isempty(s) && return interpolate ? (Expr(:tuple,:()), last_arg) : ([], last_arg) in_single_quotes = false in_double_quotes = false @@ -32,6 +31,7 @@ function shell_parse(str::AbstractString, interpolate::Bool=true; arg = [] i = firstindex(s) st = Iterators.Stateful(pairs(s)) + update_last_arg = false # true after spaces or interpolate function push_nonempty!(list, x) if !isa(x,AbstractString) || !isempty(x) @@ -54,6 +54,7 @@ function shell_parse(str::AbstractString, interpolate::Bool=true; for (j, c) in st j, c = j::Int, c::C if !in_single_quotes && !in_double_quotes && isspace(c) + update_last_arg = true i = consume_upto!(arg, s, i, j) append_2to1!(args, arg) while !isempty(st) @@ -77,12 +78,17 @@ function shell_parse(str::AbstractString, interpolate::Bool=true; # use parseatom instead of parse to respect filename (#28188) ex, j = Meta.parseatom(s, stpos, filename=filename) end - last_parse = (stpos:prevind(s, j)) .+ s.offset - push_nonempty!(arg, ex) + last_arg = stpos + s.offset + update_last_arg = true + push!(arg, ex) s = SubString(s, j) Iterators.reset!(st, pairs(s)) i = firstindex(s) else + if update_last_arg + last_arg = i + s.offset + update_last_arg = false + end if !in_double_quotes && c == '\'' in_single_quotes = !in_single_quotes i = consume_upto!(arg, s, i, j) @@ -124,14 +130,14 @@ function shell_parse(str::AbstractString, interpolate::Bool=true; push_nonempty!(arg, s[i:end]) append_2to1!(args, arg) - interpolate || return args, last_parse + interpolate || return args, last_arg # construct an expression ex = Expr(:tuple) for arg in args push!(ex.args, Expr(:tuple, arg...)) end - return ex, last_parse + return ex, last_arg end function shell_split(s::AbstractString) @@ -216,7 +222,7 @@ function print_shell_escaped_posixly(io::IO, args::AbstractString...) function isword(c::AbstractChar) if '0' <= c <= '9' || 'a' <= c <= 'z' || 'A' <= c <= 'Z' # word characters - elseif c == '_' || c == '/' || c == '+' || c == '-' + elseif c == '_' || c == '/' || c == '+' || c == '-' || c == '.' # other common characters elseif c == '\'' have_single = true diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index debe568b25f64..94ca678b8f387 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -243,8 +243,21 @@ const sorted_keyvals = ["false", "true"] complete_keyval(s::Union{String,SubString{String}}) = complete_from_list(KeyvalCompletion, sorted_keyvals, s) -function complete_path(path::AbstractString, pos::Int; - use_envpath=false, shell_escape=false, +function do_raw_escape(s) + # escape_raw_string with delim='`' and ignoring the rule for the ending \ + return replace(s, r"(\\+)`" => s"\1\\`") +end +function do_shell_escape(s) + return Base.shell_escape_posixly(s) +end +function do_string_escape(s) + return escape_string(s, ('\"','$')) +end + +function complete_path(path::AbstractString; + use_envpath=false, + shell_escape=false, + raw_escape=false, string_escape=false) @assert !(shell_escape && string_escape) if Base.Sys.isunix() && occursin(r"^~(?:/|$)", path) @@ -257,50 +270,49 @@ function complete_path(path::AbstractString, pos::Int; else dir, prefix = splitdir(path) end - local files - try + files = try if isempty(dir) - files = readdir() + readdir() elseif isdir(dir) - files = readdir(dir) + readdir(dir) else - return Completion[], 0:-1, false + return Completion[], dir, false end - catch - return Completion[], 0:-1, false + catch ex + ex isa Base.IOError || rethrow() + return Completion[], dir, false end matches = Set{String}() for file in files if startswith(file, prefix) p = joinpath(dir, file) - is_dir = try isdir(p) catch; false end - push!(matches, is_dir ? joinpath(file, "") : file) + is_dir = try isdir(p) catch ex; ex isa Base.IOError ? false : rethrow() end + push!(matches, is_dir ? file * "/" : file) end end - if use_envpath && length(dir) == 0 + if use_envpath && isempty(dir) # Look for files in PATH as well - local pathdirs = split(ENV["PATH"], @static Sys.iswindows() ? ";" : ":") + pathdirs = split(ENV["PATH"], @static Sys.iswindows() ? ";" : ":") for pathdir in pathdirs - local actualpath - try - actualpath = realpath(pathdir) - catch + actualpath = try + realpath(pathdir) + catch ex + ex isa Base.IOError || rethrow() # Bash doesn't expect every folder in PATH to exist, so neither shall we continue end - if actualpath != pathdir && in(actualpath,pathdirs) + if actualpath != pathdir && in(actualpath, pathdirs) # Remove paths which (after resolving links) are in the env path twice. # Many distros eg. point /bin to /usr/bin but have both in the env path. continue end - local filesinpath - try - filesinpath = readdir(pathdir) + filesinpath = try + readdir(pathdir) catch e # Bash allows dirs in PATH that can't be read, so we should as well. if isa(e, Base.IOError) || isa(e, Base.ArgumentError) @@ -321,18 +333,38 @@ function complete_path(path::AbstractString, pos::Int; end end - function do_escape(s) - return shell_escape ? replace(s, r"(\s|\\)" => s"\\\0") : - string_escape ? escape_string(s, ('\"','$')) : - s - end + matches = ((shell_escape ? do_shell_escape(s) : string_escape ? do_string_escape(s) : s) for s in matches) + matches = ((raw_escape ? do_raw_escape(s) : s) for s in matches) + matches = Completion[PathCompletion(s) for s in matches] + return matches, dir, !isempty(matches) +end - matchList = Completion[PathCompletion(do_escape(s)) for s in matches] - startpos = pos - lastindex(do_escape(prefix)) + 1 - # The pos - lastindex(prefix) + 1 is correct due to `lastindex(prefix)-lastindex(prefix)==0`, - # hence we need to add one to get the first index. This is also correct when considering - # pos, because pos is the `lastindex` a larger string which `endswith(path)==true`. - return matchList, startpos:pos, !isempty(matchList) +function complete_path(path::AbstractString, + pos::Int; + use_envpath=false, + shell_escape=false, + string_escape=false) + ## TODO: enable this depwarn once Pkg is fixed + #Base.depwarn("complete_path with pos argument is deprecated because the return value [2] is incorrect to use", :complete_path) + paths, dir, success = complete_path(path; use_envpath, shell_escape, string_escape) + if Base.Sys.isunix() && occursin(r"^~(?:/|$)", path) + # if the path is just "~", don't consider the expanded username as a prefix + if path == "~" + dir, prefix = homedir(), "" + else + dir, prefix = splitdir(homedir() * path[2:end]) + end + else + dir, prefix = splitdir(path) + end + startpos = pos - lastindex(prefix) + 1 + Sys.iswindows() && map!(paths, paths) do c::PathCompletion + # emulation for unnecessarily complicated return value, since / is a + # perfectly acceptable path character which does not require quoting + # but is required by Pkg's awkward parser handling + return endswith(c.path, "/") ? PathCompletion(chop(c.path) * "\\\\") : c + end + return paths, startpos:pos, success end function complete_expanduser(path::AbstractString, r) @@ -784,10 +816,11 @@ function afterusing(string::String, startpos::Int) return occursin(r"^\b(using|import)\s*((\w+[.])*\w+\s*,\s*)*$", str[fr:end]) end -function close_path_completion(str, startpos, r, paths, pos) +function close_path_completion(dir, paths, str, pos) length(paths) == 1 || return false # Only close if there's a single choice... - _path = str[startpos:prevind(str, first(r))] * (paths[1]::PathCompletion).path - path = expanduser(unescape_string(replace(_path, "\\\$"=>"\$", "\\\""=>"\""))) + path = (paths[1]::PathCompletion).path + path = unescape_string(replace(path, "\\\$"=>"\$")) + path = joinpath(dir, path) # ...except if it's a directory... try isdir(path) @@ -1075,42 +1108,80 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif return complete_identifiers!(Completion[], ffunc, context_module, string, string[startpos:pos], pos, dotpos, startpos) elseif inc_tag === :cmd - m = match(r"[\t\n\r\"`><=*?|]| (?!\\)", reverse(partial)) - startpos = nextind(partial, reverseind(partial, m.offset)) - r = startpos:pos - - # This expansion with "\\ "=>' ' replacement and shell_escape=true - # assumes the path isn't further quoted within the cmd backticks. - expanded = complete_expanduser(replace(string[r], r"\\ " => " "), r) - expanded[3] && return expanded # If user expansion available, return it - - paths, r, success = complete_path(replace(string[r], r"\\ " => " "), pos, - shell_escape=true) - - return sort!(paths, by=p->p.path), r, success + # TODO: should this call shell_completions instead of partially reimplementing it? + let m = match(r"[\t\n\r\"`><=*?|]| (?!\\)", reverse(partial)) # fuzzy shell_parse in reverse + startpos = nextind(partial, reverseind(partial, m.offset)) + r = startpos:pos + scs::String = string[r] + + expanded = complete_expanduser(scs, r) + expanded[3] && return expanded # If user expansion available, return it + + path::String = replace(scs, r"(\\+)\g1(\\?)`" => "\1\2`") # fuzzy unescape_raw_string: match an even number of \ before ` and replace with half as many + # This expansion with "\\ "=>' ' replacement and shell_escape=true + # assumes the path isn't further quoted within the cmd backticks. + path = replace(path, r"\\ " => " ", r"\$" => "\$") # fuzzy shell_parse (reversed by shell_escape_posixly) + paths, dir, success = complete_path(path, shell_escape=true, raw_escape=true) + + if success && !isempty(dir) + let dir = do_raw_escape(do_shell_escape(dir)) + # if escaping of dir matches scs prefix, remove that from the completions + # otherwise make it the whole completion + if endswith(dir, "/") && startswith(scs, dir) + r = (startpos + sizeof(dir)):pos + elseif startswith(scs, dir * "/") + r = nextind(string, startpos + sizeof(dir)):pos + else + map!(paths, paths) do c::PathCompletion + return PathCompletion(dir * "/" * c.path) + end + end + end + end + return sort!(paths, by=p->p.path), r, success + end elseif inc_tag === :string # Find first non-escaped quote - m = match(r"\"(?!\\)", reverse(partial)) - startpos = nextind(partial, reverseind(partial, m.offset)) - r = startpos:pos + let m = match(r"\"(?!\\)", reverse(partial)) + startpos = nextind(partial, reverseind(partial, m.offset)) + r = startpos:pos + scs::String = string[r] + + expanded = complete_expanduser(scs, r) + expanded[3] && return expanded # If user expansion available, return it + + path = try + unescape_string(replace(scs, "\\\$"=>"\$")) + catch ex + ex isa ArgumentError || rethrow() + nothing + end + if !isnothing(path) + paths, dir, success = complete_path(path::String, string_escape=true) - expanded = complete_expanduser(string[r], r) - expanded[3] && return expanded # If user expansion available, return it + if close_path_completion(dir, paths, path, pos) + paths[1] = PathCompletion((paths[1]::PathCompletion).path * "\"") + end - path_prefix = try - unescape_string(replace(string[r], "\\\$"=>"\$", "\\\""=>"\"")) - catch - nothing - end - if !isnothing(path_prefix) - paths, r, success = complete_path(path_prefix, pos, string_escape=true) + if success && !isempty(dir) + let dir = do_string_escape(dir) + # if escaping of dir matches scs prefix, remove that from the completions + # otherwise make it the whole completion + if endswith(dir, "/") && startswith(scs, dir) + r = (startpos + sizeof(dir)):pos + elseif startswith(scs, dir * "/") + r = nextind(string, startpos + sizeof(dir)):pos + else + map!(paths, paths) do c::PathCompletion + return PathCompletion(dir * "/" * c.path) + end + end + end + end - if close_path_completion(string, startpos, r, paths, pos) - paths[1] = PathCompletion((paths[1]::PathCompletion).path * "\"") + # Fallthrough allowed so that Latex symbols can be completed in strings + success && return sort!(paths, by=p->p.path), r, success end - - # Fallthrough allowed so that Latex symbols can be completed in strings - success && return sort!(paths, by=p->p.path), r, success end end @@ -1196,35 +1267,58 @@ end function shell_completions(string, pos) # First parse everything up to the current position scs = string[1:pos] - local args, last_parse - try - args, last_parse = Base.shell_parse(scs, true)::Tuple{Expr,UnitRange{Int}} - catch + args, last_arg_start = try + Base.shell_parse(scs, true)::Tuple{Expr,Int} + catch ex + ex isa ArgumentError || ex isa ErrorException || rethrow() return Completion[], 0:-1, false end ex = args.args[end]::Expr # Now look at the last thing we parsed isempty(ex.args) && return Completion[], 0:-1, false - arg = ex.args[end] - if all(s -> isa(s, AbstractString), ex.args) - arg = arg::AbstractString - # Treat this as a path - - # As Base.shell_parse throws away trailing spaces (unless they are escaped), - # we need to special case here. - # If the last char was a space, but shell_parse ignored it search on "". - ignore_last_word = arg != " " && scs[end] == ' ' - prefix = ignore_last_word ? "" : join(ex.args) + lastarg = ex.args[end] + # As Base.shell_parse throws away trailing spaces (unless they are escaped), + # we need to special case here. + # If the last char was a space, but shell_parse ignored it search on "". + if isexpr(lastarg, :incomplete) || isexpr(lastarg, :error) + partial = string[last_arg_start:pos] + ret, range = completions(partial, lastindex(partial)) + range = range .+ (last_arg_start - 1) + return ret, range, true + elseif endswith(scs, ' ') && !endswith(scs, "\\ ") + r = pos+1:pos + paths, dir, success = complete_path("", use_envpath=false, shell_escape=true) + return paths, r, success + elseif all(arg -> arg isa AbstractString, ex.args) + # Join these and treat this as a path + path::String = join(ex.args) + r = last_arg_start:pos # Also try looking into the env path if the user wants to complete the first argument - use_envpath = !ignore_last_word && length(args.args) < 2 + use_envpath = length(args.args) < 2 - return complete_path(prefix, pos, use_envpath=use_envpath, shell_escape=true) - elseif isexpr(arg, :incomplete) || isexpr(arg, :error) - partial = scs[last_parse] - ret, range = completions(partial, lastindex(partial)) - range = range .+ (first(last_parse) - 1) - return ret, range, true + # TODO: call complete_expanduser here? + + paths, dir, success = complete_path(path, use_envpath=use_envpath, shell_escape=true) + + if success && !isempty(dir) + let dir = do_shell_escape(dir) + # if escaping of dir matches scs prefix, remove that from the completions + # otherwise make it the whole completion + partial = string[last_arg_start:pos] + if endswith(dir, "/") && startswith(partial, dir) + r = (last_arg_start + sizeof(dir)):pos + elseif startswith(partial, dir * "/") + r = nextind(string, last_arg_start + sizeof(dir)):pos + else + map!(paths, paths) do c::PathCompletion + return PathCompletion(dir * "/" * c.path) + end + end + end + end + + return paths, r, success end return Completion[], 0:-1, false end diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index c5a2d1c8e006e..c515ec5927dd3 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -325,7 +325,7 @@ end let s = "\\alpha" c, r = test_bslashcomplete(s) @test c[1] == "α" - @test r == 1:length(s) + @test r == 1:lastindex(s) @test length(c) == 1 end @@ -1041,7 +1041,7 @@ let s, c, r s = "@show \"/dev/nul\"" c,r = completions(s, 15) c = map(completion_text, c) - @test "null" in c + @test "null\"" in c @test r == 13:15 @test s[r] == "nul" @@ -1065,8 +1065,8 @@ let s, c, r if !isdir(joinpath(s, "tmp")) c,r = test_scomplete(s) @test !("tmp/" in c) - @test r === length(s) + 1:0 - @test s[r] == "" + @test !("$s/tmp/" in c) + @test r === (sizeof(s) + 1):sizeof(s) end s = "cd \$(Iter" @@ -1091,7 +1091,7 @@ let s, c, r touch(file) s = string(tempdir(), "/repl\\ ") c,r = test_scomplete(s) - @test ["repl\\ completions"] == c + @test ["'repl completions'"] == c @test s[r] == "repl\\ " rm(file) end @@ -1185,7 +1185,7 @@ let current_dir, forbidden catch e e isa Base.IOError && occursin("ELOOP", e.msg) end - c, r = test_complete("\""*escape_string(joinpath(path, "selfsym"))) + c, r = test_complete("\"$(escape_string(path))/selfsym") @test c == ["selfsymlink"] end end @@ -1222,20 +1222,20 @@ mktempdir() do path dir_space = replace(space_folder, " " => "\\ ") s = Sys.iswindows() ? "cd $dir_space\\\\space" : "cd $dir_space/space" c, r = test_scomplete(s) - @test s[r] == "space" - @test "space\\ .file" in c + @test s[r] == (Sys.iswindows() ? "$dir_space\\\\space" : "$dir_space/space") + @test "'$space_folder'/'space .file'" in c # Also use shell escape rules within cmd backticks s = "`$s" c, r = test_scomplete(s) - @test s[r] == "space" - @test "space\\ .file" in c + @test s[r] == (Sys.iswindows() ? "$dir_space\\\\space" : "$dir_space/space") + @test "'$space_folder'/'space .file'" in c # escape string according to Julia escaping rules - julia_esc(str) = escape_string(str, ('\"','$')) + julia_esc(str) = REPL.REPLCompletions.do_string_escape(str) # For normal strings the string should be properly escaped according to # the usual rules for Julia strings. - s = "cd(\"" * julia_esc(joinpath(path, space_folder, "space")) + s = "cd(\"" * julia_esc(joinpath(path, space_folder) * "/space") c, r = test_complete(s) @test s[r] == "space" @test "space .file\"" in c @@ -1244,7 +1244,7 @@ mktempdir() do path # which needs to be escaped in Julia strings (on unix we could do this # test with all sorts of special chars) touch(joinpath(space_folder, "needs_escape\$.file")) - escpath = julia_esc(joinpath(path, space_folder, "needs_escape\$")) + escpath = julia_esc(joinpath(path, space_folder) * "/needs_escape\$") s = "cd(\"$escpath" c, r = test_complete(s) @test s[r] == "needs_escape\\\$" @@ -1281,12 +1281,12 @@ mktempdir() do path # in shell commands the shell path completion cannot complete # paths with these characters c, r, res = test_scomplete(test_dir) - @test c[1] == test_dir*(Sys.iswindows() ? "\\\\" : "/") + @test c[1] == "'$test_dir/'" @test res end escdir = julia_esc(test_dir) c, r, res = test_complete("\""*escdir) - @test c[1] == escdir*(Sys.iswindows() ? "\\\\" : "/") + @test c[1] == escdir * "/" @test res finally rm(joinpath(path, test_dir), recursive=true) @@ -1322,27 +1322,43 @@ if Sys.iswindows() cd(path) do s = "cd ..\\\\" c,r = test_scomplete(s) - @test r == length(s)+1:length(s) - @test temp_name * "\\\\" in c + @test r == lastindex(s)-3:lastindex(s) + @test "../$temp_name/" in c + + s = "cd ../" + c,r = test_scomplete(s) + @test r == lastindex(s)+1:lastindex(s) + @test "$temp_name/" in c s = "ls $(file[1:2])" c,r = test_scomplete(s) - @test r == length(s)-1:length(s) + @test r == lastindex(s)-1:lastindex(s) @test file in c s = "cd(\"..\\\\" c,r = test_complete(s) - @test r == length(s)+1:length(s) - @test temp_name * "\\\\" in c + @test r == lastindex(s)-3:lastindex(s) + @test "../$temp_name/" in c + + s = "cd(\"../" + c,r = test_complete(s) + @test r == lastindex(s)+1:lastindex(s) + @test "$temp_name/" in c s = "cd(\"$(file[1:2])" c,r = test_complete(s) - @test r == length(s) - 1:length(s) + @test r == lastindex(s) - 1:lastindex(s) @test (length(c) > 1 && file in c) || (["$file\""] == c) end rm(tmp) end +# issue 51985 +let s = "`\\" + c,r = test_scomplete(s) + @test r == lastindex(s)+1:lastindex(s) +end + # auto completions of true and false... issue #14101 let s = "tru" c, r, res = test_complete(s) diff --git a/test/spawn.jl b/test/spawn.jl index 3fdfa794ff39e..1fab652199ee0 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -660,9 +660,21 @@ let p = run(`$sleepcmd 100`, wait=false) kill(p) end -# Second argument of shell_parse +# Second return of shell_parse let s = " \$abc " - @test s[Base.shell_parse(s)[2]] == "abc" + @test Base.shell_parse(s)[2] === findfirst('a', s) + s = "abc def" + @test Base.shell_parse(s)[2] === findfirst('d', s) + s = "abc 'de'f\"\"g" + @test Base.shell_parse(s)[2] === findfirst('\'', s) + s = "abc \$x'de'f\"\"g" + @test Base.shell_parse(s)[2] === findfirst('\'', s) + s = "abc def\$x'g'" + @test Base.shell_parse(s)[2] === findfirst('\'', s) + s = "abc def\$x " + @test Base.shell_parse(s)[2] === findfirst('x', s) + s = "abc \$(d)ef\$(x " + @test Base.shell_parse(s)[2] === findfirst('x', s) - 1 end # Logging macros should not output to finalized streams (#26687) From 02185996555969879c41de3c2b1a307a46d0e17a Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Thu, 16 Nov 2023 23:09:09 -0300 Subject: [PATCH 18/38] cap the number of GC threads to number of cpu cores (#52192) (cherry picked from commit f26947b4f5aee73135581bb14290c1c00102b8e3) --- src/threading.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/threading.c b/src/threading.c index 4faa8a0a2dc46..436e6184a6c23 100644 --- a/src/threading.c +++ b/src/threading.c @@ -655,6 +655,7 @@ void jl_init_threading(void) } } + int cpu = jl_cpu_threads(); jl_n_markthreads = jl_options.nmarkthreads - 1; jl_n_sweepthreads = jl_options.nsweepthreads; if (jl_n_markthreads == -1) { // --gcthreads not specified @@ -683,8 +684,20 @@ void jl_init_threading(void) else { jl_n_markthreads = (nthreads / 2) - 1; } + // if `--gcthreads` or ENV[NUM_GCTHREADS_NAME] was not specified, + // cap the number of threads that may run the mark phase to + // the number of CPU cores + if (jl_n_markthreads + 1 >= cpu) { + jl_n_markthreads = cpu - 1; + } } } + // warn the user if they try to run with a number + // of GC threads which is larger than the number + // of physical cores + if (jl_n_markthreads + 1 > cpu) { + jl_safe_printf("WARNING: running Julia with %d GC threads on %d CPU cores\n", jl_n_markthreads + 1, cpu); + } int16_t ngcthreads = jl_n_markthreads + jl_n_sweepthreads; jl_all_tls_states_size = nthreads + nthreadsi + ngcthreads; From f67439cb43fca6f8d7345305fc9dff6fca1468c2 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 21 Nov 2023 18:06:36 -0300 Subject: [PATCH 19/38] Make have_fma consistent between interpreter and compiled (#52206) Currently the interpreter always returns false. Which isn't very good. Make it follow whatever the JIT will do. (cherry picked from commit a6c656e6c47ff2b1237c92e90ba73ac267fc1dc0) --- src/jl_exported_funcs.inc | 1 + src/llvm-cpufeatures.cpp | 4 ++-- src/processor.h | 2 ++ src/processor_arm.cpp | 16 ++++++++++++++++ src/processor_fallback.cpp | 5 +++++ src/processor_x86.cpp | 11 +++++++++++ src/runtime_intrinsics.c | 11 ++++++++--- test/llvmpasses/cpu-features.ll | 2 ++ test/sysinfo.jl | 2 ++ 9 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 745905b113622..ee255a0e1a876 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -214,6 +214,7 @@ XX(jl_get_binding_wr) \ XX(jl_get_cpu_name) \ XX(jl_get_cpu_features) \ + XX(jl_cpu_has_fma) \ XX(jl_get_current_task) \ XX(jl_get_default_sysimg_path) \ XX(jl_get_excstack) \ diff --git a/src/llvm-cpufeatures.cpp b/src/llvm-cpufeatures.cpp index 77f1baf6237c4..3d3fb6b0583da 100644 --- a/src/llvm-cpufeatures.cpp +++ b/src/llvm-cpufeatures.cpp @@ -60,7 +60,7 @@ static bool have_fma(Function &intr, Function &caller, const Triple &TT) JL_NOTS StringRef FS = FSAttr.isValid() ? FSAttr.getValueAsString() : jl_ExecutionEngine->getTargetFeatureString(); - SmallVector Features; + SmallVector Features; FS.split(Features, ','); for (StringRef Feature : Features) if (TT.isARM()) { @@ -68,7 +68,7 @@ static bool have_fma(Function &intr, Function &caller, const Triple &TT) JL_NOTS return typ == "f32" || typ == "f64"; else if (Feature == "+vfp4sp") return typ == "f32"; - } else { + } else if (TT.isX86()) { if (Feature == "+fma" || Feature == "+fma4") return typ == "f32" || typ == "f64"; } diff --git a/src/processor.h b/src/processor.h index a3ebdf4f8c605..09ee780f2b9c5 100644 --- a/src/processor.h +++ b/src/processor.h @@ -224,6 +224,8 @@ JL_DLLEXPORT jl_value_t *jl_get_cpu_name(void); // Return the features of the host CPU as a julia string. JL_DLLEXPORT jl_value_t *jl_get_cpu_features(void); // Dump the name and feature set of the host CPU +JL_DLLEXPORT jl_value_t *jl_cpu_has_fma(int bits); +// Check if the CPU has native FMA instructions; // For debugging only JL_DLLEXPORT void jl_dump_host_cpu(void); JL_DLLEXPORT jl_value_t* jl_check_pkgimage_clones(char* data); diff --git a/src/processor_arm.cpp b/src/processor_arm.cpp index f47d1509f4975..0018d2ec925d9 100644 --- a/src/processor_arm.cpp +++ b/src/processor_arm.cpp @@ -1808,6 +1808,22 @@ JL_DLLEXPORT jl_value_t *jl_get_cpu_features(void) return jl_cstr_to_string(jl_get_cpu_features_llvm().c_str()); } +JL_DLLEXPORT jl_value_t *jl_cpu_has_fma(int bits) +{ +#ifdef _CPU_AARCH64_ + return jl_true; +#else + TargetData target = jit_targets.front(); + FeatureList features = target.en.features; + if (bits == 32 && test_nbit(features, Feature::vfp4sp)) + return jl_true; + else if ((bits == 64 || bits == 32) && test_nbit(features, Feature::vfp4)) + return jl_true; + else + return jl_false; +#endif +} + jl_image_t jl_init_processor_sysimg(void *hdl) { if (!jit_targets.empty()) diff --git a/src/processor_fallback.cpp b/src/processor_fallback.cpp index 08a4274f44ac8..833cd02b5fdfc 100644 --- a/src/processor_fallback.cpp +++ b/src/processor_fallback.cpp @@ -172,6 +172,11 @@ JL_DLLEXPORT jl_value_t *jl_get_cpu_features(void) return jl_cstr_to_string(jl_get_cpu_features_llvm().c_str()); } +JL_DLLEXPORT jl_value_t *jl_cpu_has_fma(int bits) +{ + return jl_false; // Match behaviour of have_fma in src/llvm-cpufeatures.cpp (assume false) +} + JL_DLLEXPORT void jl_dump_host_cpu(void) { jl_safe_printf("CPU: %s\n", host_cpu_name().c_str()); diff --git a/src/processor_x86.cpp b/src/processor_x86.cpp index 73e0992bcf37c..d96e2061ee674 100644 --- a/src/processor_x86.cpp +++ b/src/processor_x86.cpp @@ -4,6 +4,7 @@ // CPUID +#include "julia.h" extern "C" JL_DLLEXPORT void jl_cpuid(int32_t CPUInfo[4], int32_t InfoType) { asm volatile ( @@ -1055,6 +1056,16 @@ JL_DLLEXPORT jl_value_t *jl_get_cpu_features(void) return jl_cstr_to_string(jl_get_cpu_features_llvm().c_str()); } +JL_DLLEXPORT jl_value_t *jl_cpu_has_fma(int bits) +{ + TargetData target = jit_targets.front(); + FeatureList features = target.en.features; + if ((bits == 32 || bits == 64) && (test_nbit(features, Feature::fma) || test_nbit(features, Feature::fma4))) + return jl_true; + else + return jl_false; +} + jl_image_t jl_init_processor_sysimg(void *hdl) { if (!jit_targets.empty()) diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index ed320aa9a6c35..844a5ca8338c7 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -1454,6 +1454,7 @@ un_fintrinsic(trunc_float,trunc_llvm) un_fintrinsic(rint_float,rint_llvm) un_fintrinsic(sqrt_float,sqrt_llvm) un_fintrinsic(sqrt_float,sqrt_llvm_fast) +jl_value_t *jl_cpu_has_fma(int bits); JL_DLLEXPORT jl_value_t *jl_arraylen(jl_value_t *a) { @@ -1463,7 +1464,11 @@ JL_DLLEXPORT jl_value_t *jl_arraylen(jl_value_t *a) JL_DLLEXPORT jl_value_t *jl_have_fma(jl_value_t *typ) { - JL_TYPECHK(have_fma, datatype, typ); - // TODO: run-time feature check? - return jl_false; + JL_TYPECHK(have_fma, datatype, typ); // TODO what about float16/bfloat16? + if (typ == (jl_value_t*)jl_float32_type) + return jl_cpu_has_fma(32); + else if (typ == (jl_value_t*)jl_float64_type) + return jl_cpu_has_fma(64); + else + return jl_false; } diff --git a/test/llvmpasses/cpu-features.ll b/test/llvmpasses/cpu-features.ll index eea3d1b288204..080b6e07d3086 100644 --- a/test/llvmpasses/cpu-features.ll +++ b/test/llvmpasses/cpu-features.ll @@ -5,6 +5,8 @@ ; RUN: opt -enable-new-pm=0 --opaque-pointers=1 -load libjulia-codegen%shlibext -CPUFeatures -simplifycfg -S %s | FileCheck %s ; RUN: opt -enable-new-pm=1 --opaque-pointers=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='CPUFeatures,simplifycfg' -S %s | FileCheck %s +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128-ni:10:11:12:13" +target triple = "x86_64-linux-gnu" declare i1 @julia.cpu.have_fma.f64() declare double @with_fma(double %0, double %1, double %2) diff --git a/test/sysinfo.jl b/test/sysinfo.jl index cb943cfd38843..f2e233d608338 100644 --- a/test/sysinfo.jl +++ b/test/sysinfo.jl @@ -12,6 +12,8 @@ Base.Sys.loadavg() @test length(ccall(:jl_get_cpu_name, String, ())) != 0 @test length(ccall(:jl_get_cpu_features, String, ())) >= 0 +foo_fma() = Core.Intrinsics.have_fma(Int64) +@test ccall(:jl_cpu_has_fma, Bool, (Cint,), 64) == foo_fma() if Sys.isunix() mktempdir() do tempdir From e9acaf53f6025c9c157d07cbbd3c0e5c839abcf0 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 22 Nov 2023 11:23:07 -0500 Subject: [PATCH 20/38] fix Unicode.julia_chartransform for Julia 1.10 (#52027) #49559 by @JeffBezanson updated `src/flisp/julia_charmap.h` but missed [the comment](https://github.com/JuliaLang/julia/blob/164969f3d06919b073f3aa9ee608e40974ca82d9/src/flisp/julia_charmap.h#L5-L6) noting that `base/strings/unicode.jl` has to be updated accordingly. (cherry picked from commit 05f4b05384af9af364dcdf181db1a35020e07270) --- base/strings/unicode.jl | 1 + stdlib/Unicode/test/runtests.jl | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/base/strings/unicode.jl b/base/strings/unicode.jl index 17c5d66c160b6..2e04633b87487 100644 --- a/base/strings/unicode.jl +++ b/base/strings/unicode.jl @@ -179,6 +179,7 @@ const _julia_charmap = Dict{UInt32,UInt32}( 0x00B7 => 0x22C5, 0x0387 => 0x22C5, 0x2212 => 0x002D, + 0x210F => 0x0127, ) utf8proc_map(s::AbstractString, flags::Integer, chartransform=identity) = utf8proc_map(String(s), flags, chartransform) diff --git a/stdlib/Unicode/test/runtests.jl b/stdlib/Unicode/test/runtests.jl index 5c5a75b33e363..573d5da6167ba 100644 --- a/stdlib/Unicode/test/runtests.jl +++ b/stdlib/Unicode/test/runtests.jl @@ -27,8 +27,8 @@ using Unicode: normalize, isassigned, julia_chartransform @test normalize("\u0072\u0307\u0323", :NFC) == "\u1E5B\u0307" #26917 # julia_chartransform identifier normalization - @test normalize("julia\u025B\u00B5\u00B7\u0387\u2212", chartransform=julia_chartransform) == - "julia\u03B5\u03BC\u22C5\u22C5\u002D" + @test normalize("julia\u025B\u00B5\u00B7\u0387\u2212\u210F", chartransform=julia_chartransform) == + "julia\u03B5\u03BC\u22C5\u22C5\u002D\u0127" @test julia_chartransform('\u00B5') === '\u03BC' end From 99d71c0b5921678130d9520c34d6ad8caa6c5a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Mon, 27 Nov 2023 05:27:30 +0000 Subject: [PATCH 21/38] More helpful error message for empty `cpu_target` in `Base.julia_cmd` (#52217) Fix #52209. (cherry picked from commit 4a18886a16308dee8d475c942bc641d718e59e9f) --- base/util.jl | 6 ++++-- test/cmdlineargs.jl | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/base/util.jl b/base/util.jl index 3ccdd0a37ae68..2617d5a602850 100644 --- a/base/util.jl +++ b/base/util.jl @@ -144,7 +144,7 @@ See also [`print`](@ref), [`println`](@ref), [`show`](@ref). printstyled(stdout, msg...; bold=bold, italic=italic, underline=underline, blink=blink, reverse=reverse, hidden=hidden, color=color) """ - Base.julia_cmd(juliapath=joinpath(Sys.BINDIR, julia_exename()); cpu_target) + Base.julia_cmd(juliapath=joinpath(Sys.BINDIR, julia_exename()); cpu_target::Union{Nothing,String}=nothing) Return a julia command similar to the one of the running process. Propagates any of the `--cpu-target`, `--sysimage`, `--compile`, `--sysimage-native-code`, @@ -154,6 +154,8 @@ command line arguments that are not at their default values. Among others, `--math-mode`, `--warn-overwrite`, and `--trace-compile` are notably not propagated currently. +Unless set to `nothing`, the `cpu_target` keyword argument can be used to override the CPU target set for the running process. + To get the julia command without propagated command line arguments, `julia_cmd()[1]` can be used. !!! compat "Julia 1.1" @@ -246,7 +248,7 @@ function julia_cmd(julia=joinpath(Sys.BINDIR, julia_exename()); cpu_target::Unio # If pkgimage is set, malloc_log and code_coverage should not @assert opts.malloc_log == 0 && opts.code_coverage == 0 end - return `$julia -C$cpu_target -J$image_file $addflags` + return `$julia -C $cpu_target -J$image_file $addflags` end function julia_exename() diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 72d1c6bb5663f..1730fe8179857 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -66,7 +66,9 @@ end get_julia_cmd(arg) = strip(read(`$julia_basic $arg -e 'print(repr(Base.julia_cmd()))'`, String), ['`']) for (arg, default) in ( - ("-C$(unsafe_string(opts.cpu_target))", false), + # Use a Cmd to handle space nicely when + # interpolating inside another Cmd. + (`-C $(unsafe_string(opts.cpu_target))`, false), ("-J$(unsafe_string(opts.image_file))", false), @@ -123,13 +125,21 @@ end ("--pkgimages=no", false), ) @testset "$arg" begin + str = arg isa Cmd ? join(arg.exec, ' ') : arg if default - @test !occursin(arg, get_julia_cmd(arg)) + @test !occursin(str, get_julia_cmd(arg)) else - @test occursin(arg, get_julia_cmd(arg)) + @test occursin(str, get_julia_cmd(arg)) end end end + + # Test empty `cpu_target` gives a helpful error message, issue #52209. + io = IOBuffer() + p = run(pipeline(`$(Base.julia_cmd(; cpu_target="")) --startup-file=no -e ''`; stderr=io); wait=false) + wait(p) + @test p.exitcode == 1 + @test occursin("empty CPU name", String(take!(io))) end let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` From 2638522b7576fce9a951ac54efbdbbacf7148a8d Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Mon, 27 Nov 2023 07:00:19 +0100 Subject: [PATCH 22/38] Memoize `cwstring` when used for env lookup / modification on Windows (#51371) ~This is just me proposing a suggestion from @KristofferC in https://discourse.julialang.org/t/debug-has-massive-performance-impact/103974/22, it's all his code / idea, not mine.~ This PR makes it so that on windows, we pre-allocate an `IdDict` and every time someone looks up environment variables (motivating example here is `@debug` statements), we store the result of `cwstring(::String)` in that `IdDict` so that it doesn't need to be re-computed for future uses. The idea behind this is that people have observed that [using `@debug` is significantly more costly on Windows than other platforms](https://discourse.julialang.org/t/debug-has-massive-performance-impact/103974), even though we have documented in that manual that it should be a really cheap operation. @KristofferC suggests this is due to the fact that [checking environment variables in Windows is more costly](https://discourse.julialang.org/t/debug-has-massive-performance-impact/103974/18). ~The idea here is that we preallocate a `Cwstring` on Windows that just holds the text `"JULIA_DEBUG"`, so that if `access_env(f, "JULIA_DEBUG")` gets called, we don't need to create a new `Cwstring` and then throw it away each time.~ --------- Co-authored-by: Ian Butterworth Co-authored-by: Jameson Nash (cherry picked from commit 9dcedaa8ec198b49387a8459e4daff1474b93ce2) --- base/env.jl | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/base/env.jl b/base/env.jl index 077b3a4ed28e5..27594acff6b7f 100644 --- a/base/env.jl +++ b/base/env.jl @@ -3,12 +3,29 @@ if Sys.iswindows() const ERROR_ENVVAR_NOT_FOUND = UInt32(203) + const env_dict = IdDict{String, Vector{Cwchar_t}}() + const env_lock = ReentrantLock() + + function memoized_env_lookup(str::AbstractString) + # Windows environment variables have a different format from Linux / MacOS, and previously + # incurred allocations because we had to convert a String to a Vector{Cwchar_t} each time + # an environment variable was looked up. This function memoizes that lookup process, storing + # the String => Vector{Cwchar_t} pairs in env_dict + var = get(env_dict, str, nothing) + if isnothing(var) + var = @lock env_lock begin + env_dict[str] = cwstring(str) + end + end + var + end + _getenvlen(var::Vector{UInt16}) = ccall(:GetEnvironmentVariableW,stdcall,UInt32,(Ptr{UInt16},Ptr{UInt16},UInt32),var,C_NULL,0) _hasenv(s::Vector{UInt16}) = _getenvlen(s) != 0 || Libc.GetLastError() != ERROR_ENVVAR_NOT_FOUND - _hasenv(s::AbstractString) = _hasenv(cwstring(s)) + _hasenv(s::AbstractString) = _hasenv(memoized_env_lookup(s)) function access_env(onError::Function, str::AbstractString) - var = cwstring(str) + var = memoized_env_lookup(str) len = _getenvlen(var) if len == 0 return Libc.GetLastError() != ERROR_ENVVAR_NOT_FOUND ? "" : onError(str) @@ -21,7 +38,7 @@ if Sys.iswindows() end function _setenv(svar::AbstractString, sval::AbstractString, overwrite::Bool=true) - var = cwstring(svar) + var = memoized_env_lookup(svar) val = cwstring(sval) if overwrite || !_hasenv(var) ret = ccall(:SetEnvironmentVariableW,stdcall,Int32,(Ptr{UInt16},Ptr{UInt16}),var,val) @@ -30,7 +47,7 @@ if Sys.iswindows() end function _unsetenv(svar::AbstractString) - var = cwstring(svar) + var = memoized_env_lookup(svar) ret = ccall(:SetEnvironmentVariableW,stdcall,Int32,(Ptr{UInt16},Ptr{UInt16}),var,C_NULL) windowserror(:setenv, ret == 0 && Libc.GetLastError() != ERROR_ENVVAR_NOT_FOUND) end From 9c097b673938875c8320e95c5531e7a8c4db939a Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 17 Nov 2023 03:41:28 +0100 Subject: [PATCH 23/38] Revert "Support sorting iterators (#46104)" (#52010) Co-authored-by: Lilith Orion Hafner (cherry picked from commit 1cb85ad3fb1ba32e18be9be67775d1b36dc27079) --- base/sort.jl | 43 ++----------------------------------------- test/sorting.jl | 36 ------------------------------------ 2 files changed, 2 insertions(+), 77 deletions(-) diff --git a/base/sort.jl b/base/sort.jl index a5d3927c450c4..971a68fa3d725 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -5,8 +5,7 @@ module Sort using Base.Order using Base: copymutable, midpoint, require_one_based_indexing, uinttype, - sub_with_overflow, add_with_overflow, OneTo, BitSigned, BitIntegerType, top_set_bit, - IteratorSize, HasShape, IsInfinite, tail + sub_with_overflow, add_with_overflow, OneTo, BitSigned, BitIntegerType, top_set_bit import Base: sort, @@ -1470,12 +1469,6 @@ end Variant of [`sort!`](@ref) that returns a sorted copy of `v` leaving `v` itself unmodified. -Returns something [`similar`](@ref) to `v` when `v` is an `AbstractArray` and uses -[`collect`](@ref) to support arbitrary non-`AbstractArray` iterables. - -!!! compat "Julia 1.10" - `sort` of arbitrary iterables requires at least Julia 1.10. - # Examples ```jldoctest julia> v = [3, 1, 2]; @@ -1493,39 +1486,7 @@ julia> v 2 ``` """ -function sort(v; kws...) - size = IteratorSize(v) - size == HasShape{0}() && throw(ArgumentError("$v cannot be sorted")) - size == IsInfinite() && throw(ArgumentError("infinite iterator $v cannot be sorted")) - sort!(collect(v); kws...) -end -sort(v::AbstractVector; kws...) = sort!(copymutable(v); kws...) # for method disambiguation -sort(::AbstractString; kws...) = - throw(ArgumentError("sort(::AbstractString) is not supported")) -sort(::Tuple; kws...) = - throw(ArgumentError("sort(::Tuple) is only supported for NTuples")) - -function sort(x::NTuple{N}; lt::Function=isless, by::Function=identity, - rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward) where N - o = ord(lt,by,rev,order) - if N > 9 - v = sort!(collect(x), DEFAULT_STABLE, o) - tuple((v[i] for i in 1:N)...) - else - _sort(x, o) - end -end -_sort(x::Union{NTuple{0}, NTuple{1}}, o::Ordering) = x -function _sort(x::NTuple, o::Ordering) - a, b = Base.IteratorsMD.split(x, Val(length(x)>>1)) - merge(_sort(a, o), _sort(b, o), o) -end -merge(x::NTuple, y::NTuple{0}, o::Ordering) = x -merge(x::NTuple{0}, y::NTuple, o::Ordering) = y -merge(x::NTuple{0}, y::NTuple{0}, o::Ordering) = x # Method ambiguity -merge(x::NTuple, y::NTuple, o::Ordering) = - (lt(o, y[1], x[1]) ? (y[1], merge(x, tail(y), o)...) : (x[1], merge(tail(x), y, o)...)) - +sort(v::AbstractVector; kws...) = sort!(copymutable(v); kws...) ## partialsortperm: the permutation to sort the first k elements of an array ## diff --git a/test/sorting.jl b/test/sorting.jl index c12f07ae750d7..81376ab5cd114 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -88,20 +88,6 @@ end vcat(2000, (x:x+99 for x in 1900:-100:100)..., 1:99) end -function tuple_sort_test(x) - @test issorted(sort(x)) - length(x) > 9 && return # length > 9 uses a vector fallback - @test 0 == @allocated sort(x) -end -@testset "sort(::NTuple)" begin - @test sort((9,8,3,3,6,2,0,8)) == (0,2,3,3,6,8,8,9) - @test sort((9,8,3,3,6,2,0,8), by=x->x÷3) == (2,0,3,3,8,6,8,9) - for i in 1:40 - tuple_sort_test(tuple(rand(i)...)) - end - @test_throws ArgumentError sort((1,2,3.0)) -end - @testset "partialsort" begin @test partialsort([3,6,30,1,9],3) == 6 @test partialsort([3,6,30,1,9],3:4) == [6,9] @@ -544,28 +530,6 @@ end @test isequal(a, [8,6,7,NaN,5,3,0,9]) end -@testset "sort!(iterable)" begin - gen = (x % 7 + 0.1x for x in 1:50) - @test sort(gen) == sort!(collect(gen)) - gen = (x % 7 + 0.1y for x in 1:10, y in 1:5) - @test sort(gen; dims=1) == sort!(collect(gen); dims=1) - @test sort(gen; dims=2) == sort!(collect(gen); dims=2) - - @test_throws ArgumentError("dimension out of range") sort(gen; dims=3) - - @test_throws UndefKeywordError(:dims) sort(gen) - @test_throws UndefKeywordError(:dims) sort(collect(gen)) - @test_throws UndefKeywordError(:dims) sort!(collect(gen)) - - @test_throws ArgumentError sort("string") - @test_throws ArgumentError("1 cannot be sorted") sort(1) - - @test sort(Set((1, 3, 6))) == [1, 3, 6] - @test sort(Dict((1=>9, 3=>2, 6=>5))) == [1=>9, 3=>2, 6=>5] - @test sort(keys(Dict((1=>2, 3=>5, 6=>9)))) == [1, 3, 6] - @test sort(values(Dict((1=>9, 3=>2, 6=>5)))) == [2, 5, 9] -end - @testset "sort!(::AbstractVector{<:Integer}) with short int range" begin a = view([9:-1:0;], :)::SubArray sort!(a) From 2030e7df55658c5523f006e7fbfd0e61323f3b41 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Fri, 24 Nov 2023 17:35:51 -0500 Subject: [PATCH 24/38] Turn Method Overwritten Error into a PrecompileError -- turning off caching (#52214) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #52213 Overwritting methods during cache creation is currently not something that the system can support and can lead to surprising, counter-intuitive and fatal errors. In 1.10 we turned it from a warning to a strong error, with this PR it remains a strong error, but the precompilation system recognizes it and essentially sets `__precompile__(false)` for this module and all modules that depend on it. Before: ``` julia> using OverwriteMethodError [ Info: Precompiling OverwriteMethodError [top-level] WARNING: Method definition +(Bool, Bool) in module Base at bool.jl:166 overwritten in module OverwriteMethodError at /home/vchuravy/src/julia2/OverwriteMethodError.jl:2. ERROR: LoadError: Method overwriting is not permitted during Module precompile. Stacktrace: [1] top-level scope @ ~/src/julia2/OverwriteMethodError.jl:2 [2] include @ Base ./Base.jl:489 [inlined] [3] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt128}}, source::Nothing) @ Base ./loading.jl:2216 [4] top-level scope @ stdin:3 in expression starting at /home/vchuravy/src/julia2/OverwriteMethodError.jl:1 in expression starting at stdin:3 ERROR: Failed to precompile OverwriteMethodError [top-level] to "/home/vchuravy/.julia/compiled/v1.10/jl_guiuCQ". Stacktrace: [1] error(s::String) @ Base ./error.jl:35 [2] compilecache(pkg::Base.PkgId, path::String, internal_stderr::IO, internal_stdout::IO, keep_loaded_modules::Bool) @ Base ./loading.jl:2462 [3] compilecache @ Base ./loading.jl:2334 [inlined] [4] (::Base.var"#968#969"{Base.PkgId})() @ Base ./loading.jl:1968 [5] mkpidlock(f::Base.var"#968#969"{Base.PkgId}, at::String, pid::Int32; kwopts::@Kwargs{stale_age::Int64, wait::Bool}) @ FileWatching.Pidfile ~/.julia/juliaup/julia-1.10.0-rc1+0.x64.linux.gnu/share/julia/stdlib/v1.10/FileWatching/src/pidfile.jl:93 [6] #mkpidlock#6 @ FileWatching.Pidfile ~/.julia/juliaup/julia-1.10.0-rc1+0.x64.linux.gnu/share/julia/stdlib/v1.10/FileWatching/src/pidfile.jl:88 [inlined] [7] trymkpidlock(::Function, ::Vararg{Any}; kwargs::@Kwargs{stale_age::Int64}) @ FileWatching.Pidfile ~/.julia/juliaup/julia-1.10.0-rc1+0.x64.linux.gnu/share/julia/stdlib/v1.10/FileWatching/src/pidfile.jl:111 [8] #invokelatest#2 @ Base ./essentials.jl:889 [inlined] [9] invokelatest @ Base ./essentials.jl:884 [inlined] [10] maybe_cachefile_lock(f::Base.var"#968#969"{Base.PkgId}, pkg::Base.PkgId, srcpath::String; stale_age::Int64) @ Base ./loading.jl:2977 [11] maybe_cachefile_lock @ Base ./loading.jl:2974 [inlined] [12] _require(pkg::Base.PkgId, env::String) @ Base ./loading.jl:1964 [13] __require_prelocked(uuidkey::Base.PkgId, env::String) @ Base ./loading.jl:1806 [14] #invoke_in_world#3 @ Base ./essentials.jl:921 [inlined] [15] invoke_in_world @ Base ./essentials.jl:918 [inlined] [16] _require_prelocked(uuidkey::Base.PkgId, env::String) @ Base ./loading.jl:1797 [17] macro expansion @ Base ./loading.jl:1784 [inlined] [18] macro expansion @ Base ./lock.jl:267 [inlined] [19] __require(into::Module, mod::Symbol) @ Base ./loading.jl:1747 [20] #invoke_in_world#3 @ Base ./essentials.jl:921 [inlined] [21] invoke_in_world @ Base ./essentials.jl:918 [inlined] [22] require(into::Module, mod::Symbol) @ Base ./loading.jl:1740 ``` After: ``` julia> using OverwriteMethodError ┌ Info: Precompiling OverwriteMethodError [top-level] └ @ Base loading.jl:2486 WARNING: Method definition +(Bool, Bool) in module Base at bool.jl:166 overwritten in module OverwriteMethodError at /home/vchuravy/src/julia2/OverwriteMethodError.jl:2. ERROR: Method overwriting is not permitted during Module precompile. ┌ Info: Skipping precompilation since __precompile__(false). Importing OverwriteMethodError [top-level]. └ @ Base loading.jl:2084 ``` --------- Co-authored-by: Kristoffer Carlsson (cherry picked from commit 9e8fe688c5e32bde3ab48bb71f9d4ab45ef272ee) --- base/boot.jl | 2 ++ base/loading.jl | 2 +- src/gf.c | 6 ++++-- src/jl_exported_data.inc | 1 + src/jltypes.c | 1 + src/julia.h | 1 + src/staticdata.c | 3 ++- test/precompile.jl | 11 +++++++++++ 8 files changed, 23 insertions(+), 4 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index 78b7daaf47d64..561be564d58f4 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -370,6 +370,8 @@ struct InitError <: WrappedException error end +struct PrecompilableError <: Exception end + String(s::String) = s # no constructor yet const Cvoid = Nothing diff --git a/base/loading.jl b/base/loading.jl index d748283c7c9d8..bbc14c3f1d591 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1686,7 +1686,7 @@ function include_dependency(path::AbstractString) end # we throw PrecompilableError when a module doesn't want to be precompiled -struct PrecompilableError <: Exception end +import Core: PrecompilableError function show(io::IO, ex::PrecompilableError) print(io, "Declaring __precompile__(false) is not allowed in files that are being precompiled.") end diff --git a/src/gf.c b/src/gf.c index 9ee77a8426432..1b6251078fa1f 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1568,8 +1568,10 @@ static void method_overwrite(jl_typemap_entry_t *newentry, jl_method_t *oldvalue jl_printf(s, ".\n"); jl_uv_flush(s); } - if (jl_generating_output()) - jl_error("Method overwriting is not permitted during Module precompile."); + if (jl_generating_output()) { + jl_printf(JL_STDERR, "ERROR: Method overwriting is not permitted during Module precompilation. Use `__precompile__(false)` to opt-out of precompilation.\n"); + jl_throw(jl_precompilable_error); + } } static void update_max_args(jl_methtable_t *mt, jl_value_t *type) diff --git a/src/jl_exported_data.inc b/src/jl_exported_data.inc index 092a48be81930..b9d4651ef24c0 100644 --- a/src/jl_exported_data.inc +++ b/src/jl_exported_data.inc @@ -126,6 +126,7 @@ XX(jl_voidpointer_type) \ XX(jl_void_type) \ XX(jl_weakref_type) \ + XX(jl_precompilable_error) \ // Data symbols that are defined inside the public libjulia #define JL_EXPORTED_DATA_SYMBOLS(XX) \ diff --git a/src/jltypes.c b/src/jltypes.c index a39bc935fb181..d61a06b06f159 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3410,6 +3410,7 @@ void post_boot_hooks(void) jl_methoderror_type = (jl_datatype_t*)core("MethodError"); jl_loaderror_type = (jl_datatype_t*)core("LoadError"); jl_initerror_type = (jl_datatype_t*)core("InitError"); + jl_precompilable_error = jl_new_struct_uninit((jl_datatype_t*)core("PrecompilableError")); jl_pair_type = core("Pair"); jl_kwcall_func = core("kwcall"); jl_kwcall_mt = ((jl_datatype_t*)jl_typeof(jl_kwcall_func))->name->mt; diff --git a/src/julia.h b/src/julia.h index f4229eae909ac..6da03fb3d526a 100644 --- a/src/julia.h +++ b/src/julia.h @@ -829,6 +829,7 @@ extern JL_DLLIMPORT jl_value_t *jl_readonlymemory_exception JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_diverror_exception JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_undefref_exception JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_interrupt_exception JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_value_t *jl_precompilable_error JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_boundserror_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_an_empty_vec_any JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_an_empty_string JL_GLOBALLY_ROOTED; diff --git a/src/staticdata.c b/src/staticdata.c index a974d98a39835..aae64ae2ffb51 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -99,7 +99,7 @@ extern "C" { // TODO: put WeakRefs on the weak_refs list during deserialization // TODO: handle finalizers -#define NUM_TAGS 158 +#define NUM_TAGS 159 // An array of references that need to be restored from the sysimg // This is a manually constructed dual of the gvars array, which would be produced by codegen for Julia code, for C. @@ -224,6 +224,7 @@ jl_value_t **const*const get_tags(void) { INSERT_TAG(jl_undefref_exception); INSERT_TAG(jl_readonlymemory_exception); INSERT_TAG(jl_atomicerror_type); + INSERT_TAG(jl_precompilable_error); // other special values INSERT_TAG(jl_emptysvec); diff --git a/test/precompile.jl b/test/precompile.jl index 82f445a115fee..e10d896da7d3f 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -504,6 +504,17 @@ precompile_test_harness(false) do dir """) @test Base.compilecache(Base.PkgId("Baz")) == Base.PrecompilableError() # due to __precompile__(false) + + OverwriteMethodError_file = joinpath(dir, "OverwriteMethodError.jl") + write(OverwriteMethodError_file, + """ + module OverwriteMethodError + Base.:(+)(x::Bool, y::Bool) = false + end + """) + + @test Base.compilecache(Base.PkgId("OverwriteMethodError")) == Base.PrecompilableError() # due to piracy + @eval using Baz @test Base.invokelatest(Baz.baz) == 1 From 9f55128062c2c1fc65217fb858c84b08fa66f325 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 13 Nov 2023 11:32:10 -0500 Subject: [PATCH 25/38] Devdocs on fixing precompile hangs, take 2 (#51895) This is #50914, with only the documentation changes, plus an improvement to the warning message. --------- Co-authored-by: Tim Holy Co-authored-by: Ian Butterworth (cherry picked from commit 6d5787a993ae7623df479217c9805767ceaa3556) --- doc/make.jl | 1 + doc/src/devdocs/img/precompilation_hang.png | Bin 0 -> 9175 bytes doc/src/devdocs/precompile_hang.md | 98 ++++++++++++++++++++ src/jl_uv.c | 9 +- 4 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 doc/src/devdocs/img/precompilation_hang.png create mode 100644 doc/src/devdocs/precompile_hang.md diff --git a/doc/make.jl b/doc/make.jl index a9343a3133a63..6bbc94b925b60 100644 --- a/doc/make.jl +++ b/doc/make.jl @@ -154,6 +154,7 @@ DevDocs = [ "devdocs/EscapeAnalysis.md", "devdocs/gc-sa.md", "devdocs/gc.md", + "devdocs/precompile_hang.md", ], "Developing/debugging Julia's C code" => [ "devdocs/backtraces.md", diff --git a/doc/src/devdocs/img/precompilation_hang.png b/doc/src/devdocs/img/precompilation_hang.png new file mode 100644 index 0000000000000000000000000000000000000000..d076b7697f2712a12aa8132197e95ea7e7014e2d GIT binary patch literal 9175 zcmb_iXH-+$wzhy&DIy?EihvLT9GdhX9SJogKxm2(nu0>8N2bX*+AJIoCJ8Z_c@Pl!=k*i>R})WnWKRjar+KW0M4vl%#hd&%|J{k#|J=D7 z0e7_2;r@23S+?bZV?N#LmFl?W(35dAe|odrz=8Zu=3-XUN;0CD`~@~W#K?B1 z3-lRyDb>+)yF}v95$}5T8>d{eR1A?MOTJ1)7VSnJJOf@VD{vq=&fX%NZ;CpZZB^@x?RWg)_bce6J@|O{bUl5=dL~Tm zn4h&Y;mCPvaZiz?ir%pyQYn1%KfUO~@a>%s*XiupVljDu8E&xjs-x+EwjX%;_~iFl zI(SjlrqIkuPsq`>Zm9?)8S@}_LB#gk{+=13srvS24rL3BgnJygy8WQ+5{RYW81l9p zL1zez=VZKew!C@G_)O%KZ@`@b(hEGD5@2F#m0f;W*M}P`asK(jL+CDuvJXCB=9ux* z`<>4}yS%nWSF6d6bc!e2SO0s*_$|pMeZlTnGkUKpA~DX?Ro;k_(mq1RB*oZyqD}`Hv-KHp zb{+NsuNxvOn~x=|BKoWKjteJ9&VAXfx*8xoh-d>=^eV^UgQ~lq#T{fvmDeWqJajwz zC*S#4t49(het>T_=)hf2=w+7Osp-g&aNr&yzl{*l>HMhxK!}$~pMflZhD)6U5u!A=J{dbLCaj zcX!W#);9!9EjKBZnrl$zp@Zk3cZdtc4b$Dhm*XAV=lTahhJ*RiD0OXwU9JR2$5sBl zX=t2)*WMyXILFkS`r|q>I+}#*)BI$opn%inY~jA6JY1Mqq3tq?n9qt=6OXmzGZzg*4BJ^IJnM26g)c774^=I(HDdYM zCMG`MNZ4xeKd16UcpN!dmOC3vSxJ^B$G`W`Jjm$QRB)$K$RSuq)UGAETkqwc)E3+rf-TR zeYSGFT_+aAGNAJYPojU14{Qq}7+9$zSUeeR>*{P9=@3qOJyQ>K&PKUG9z_u`4?u*o ziAJ3x{fclB-MoeA z47BW5GILKQRj4@=vd=@wG~)Dw90;R`bbBrJ<=9M9JOLUHgn<4|n~5?lmPvrMQSn_$ z*CtWEQ#bJ!&79x-N~7|w9ig(^fFfmhXaywm{e&i_?%q=ERaQKq;%!cF4Q~FW+aUnS zKlRpPA#PE%N-6z=MrWW48U%L!Ri$))cCbN133H%h4fazqcU#hMw!kxwfEn(p2*zgV zJLjj`<>O{srG4w~t6SZ=z|d`#Oh*;b6mr<*>^B$;jwovtP!*I5S7|w4nBo|vKrJ^H z`k_njJpYH^CEBR)@B(34)*o3M5U8nXI-e^wh`|NE->Wa~&OVsT98Ici^qK)Vc|-sD znL!Q%2pNmwdD3^Af)vLkudDI*Mx1Ce=kF9AJ*$|tfyj25dzvZ#o@SCQ3cUVgmi~(Q z%Nt_j6b^4LcU!UG653ccsY6(m7zL9O)UU*y|4Ka$x2vOLm7{Rp*Q;qSOE7l*csb|G zt!1-=x&jTcmov{2@VBoPkpRTrMs%Bi+YVLnC;VapXLNG+AG(x)=})Ay=>Bkvx)1d| zBR&=~2(5CZd7sSo%$28lcdb_ha~~BpK7PFS_2-zk>*GD6hO@w;AsZ80-t&kD614sO zbMjDXb)WTw6VLP|zpkt(3pX+iIT%F)I^k-AT7D_HUEdq?hpYf}I={8Kk4Zst=OlMZ98EY`?_vM0vWLiB!uej@-mQ`ij;2F?&DbUj*gz zv|Q*4W}}9Rnzd}a9;+g7mKvzcGz^N%0(PgV76FEq5qdTwy2%i=s+IlDXrmG&fYj$i z2oKLZOny_DzEW#ukuLxJ^x3xuYVWhb@{cANV#t2oi3I#gK%iQR*ctxFmxIn=jNX#I z@%Uy#W7>Dy-(x5{h%2#)v7Gf#PF}@CIG_!JNT#^(&3306=qoU%M@9SQsRd zs40DkZbl1J>N^~fZQwD;##bDK%AT7Y{iJ~pI9~OeIoSpn5CaHe*Hq6qdQVU5QUyqU zI{u1!IJhE>Y>00<86Y(Hs5go9L!kR2@T&q*#^yr!J?yK2$D91}M`p9#7Lr|k;mj2J zs#YC??+<7B=-jZ;HMg1AW;xpLj1V{-wK0})3c1DFhA?+%T>yDS1|t!dTO&M5`lU7r zCWA)+i8rNvc7JeNE<8@lECsURc$3cSx!?DfS?1?^aB%h|;hXc@w4=))p zD`2vIUIEDzI=bS%sZa2STVfUyWr&NLc^t@NeTFR-@= z*mEYa&*qBdf=jdAs7I3H;XGPMoWx~|l%`i>GBAf=wUKL9*mz7%4~af6NGtu~!rP z6z0e+&8oY7#{y<%ASW{`_dB*1k1z`?B%Ql1M~Q% zM7k~oM^K;3pr{jBtq7$COD(4QHP`$`rusT_h)N)#2}E&a+BDY75c!{>1y*-SVFzEF z3wJcg&H40xb6)B^pih$^8P=@(#Us%*iD4j?5q|()WYx;0lS(PWz0#+0fv59JMO1Nc z{M%J2{u1b`U0I@d!poU$1<(WdEam3-xbplG>oySVfJ#MrefiCUo^>FOMLk<=ACkZ$ zgZq+*b+3fTNp9^L+3uH!beQR>v|NhyJs*5HPKFC>VMKTaqfo!~S6hNgA6iSZ z0#dMS^++_dXcC%JR9b3)nmj&=DU#)#hMi9)1unejx(4J$ON#|53O{XqAfLm}tkoqG z2`fU_jnSPVNxACVAzSw5q2C4UraH45OJ?%T49Wd-p}Y0T<*|k0??k{sLoC^7EzDhnRkSKb+FNP1(_K zHRM+3wZ=_n{=l}d=bE9oJjniv#7i;x%KP`>9{;IMuS%t7^6%Y&LC^P5-_U~jGt7M* zpqe!QE_`X32i@Hujbw8hj=I2jvc`_~OY&f)90tuVr_F{9_pUfk2o)04?;b#ORq&Kwpb)pFD!$0M0%DpyjV7^5X(=Ve=RxpoFWtjPG)`y#8*zuFlC-mX1(vu(Jcm zj@?NNke)gjC)z+e8s9vAT)HQD7lUA%q^-K!o)GCcQSBmTq}nx2CBKFyBig;(U~f4_ zZFsI{$YpsLQGC)p%d79ZxhZlF7-a1)e}*mv_QjS$N*{Udnc+DD+j?$Nr}sE)K~w{; zgKj48P}Vb2;CjpFYwG0-Rm~-crM^cey)T2;Pnun~r&=BMsi$p@_6-$lS>0auP#!fO zkM_5OASf9lMo0p?4=hfIJz-QIo-pK4i&)x@mLxWj1XID>6x|!fWq{@$Yv^t|X416@ zeC&RZkSn7neW@uSU3QKstK^mix|W^n$D6s*gXn9CepcGcVGNo*cOvdgZE#qto-~~D z@p>g8LknRs+; zFm{!9Z&?uuY=&(4m0~6fZeGkj!d$Z~wDSHY#~N#xB|R_y3>?{%2NZ@Ny=D!qlTF^{ zXU47^L(`v)Vo3B~Mz(*H`T3J;0q@^N9gWm@g7v~KHB@cB6atyl6z9|m+o2B&4TZBffVOcedqvV1b z@G2Di_<8Kg9pU1qca>^~gL@VFAdu~AWN5EOkhD^SFy;Y=H^DK6=?0$$k*4ao-ilWw_n8xij%^w>8 z^x)kBGJ}CbgrOdx3V9>z9Cp~YP*rXXKb=Z*XfdgUBeCq|W5|Ca?^D#*su4O& zc#EB zU;u1uuM?~4lEO0O=EL^%$q_PnXT!~2869-kGg7&_+wYoDe!my80&Ky~ZIr-XcHjY# z8o|z{cT#)%OSEsHewj{nC~0{68E><<=y=E$2QqlO)W`?`X$R9R+F18Z%BcE`Sk}LPVZ(z4p;~^Ybsku z@_oF*Ilj|PPQ-p7gSlU#T?eIoGmTWI`Z@UgB*VdG{4wFv#5dEQi%)u@ek2VfxG%U6 zdx=KeRu5o5O^81~;NYu$*5tP0V zE*$ZYZMv!WWG?n|D|TgxIa*0m+Jm>e=W@;Fo`*B!kqcPxa5NwyJlq&z(3#+^rnPH4 z8t~PzSLu=SJKq#wUbNNW<4WJj1@uF<(UsPJGLopZ>uQ+aHvs^h5{hbnsN7E3Kyq>+ zOQo5KL$87Af2H~=#r??1XNWl+ptYyhMFQ2TOuKO45E2(Vy^-GKLRpG0inaMRIJJ&U z_4}W2n~1EyH7(>yN7mnkX~ns*nC)UMjXYvSjHFXca{N!Qp+ks%m#Zaq<^*w{(PbD6 zGJMJ1f&!w5E;-y2pP~FMm>qo)>=5-$L=2o8jyuo3Q)_N-Kk*Y&da%yH)=Qz1OqsLO z`0>K~lDS!PbxK3iCpzl*n%oP+ic0#pRoDcS?o+VaV`~$3{PPdsR(|Sg&5ZP6Us7}8 z$nrOc-SS9o<|7SX&X#8yJwl)qi7KLdkF(QLPJ)O_uR{wA-T_78F(vOA?T_ml_FS4p zI$gf}>B8Qb>|znnV^e0!YW|DY?d?}v;-Y}*&hyB6<5+^F{}J*_g!*{p)Em0|nr;`_ z5EM93XhwwDo}bJ!mk`2F8206!1alu#b$!vWMFjzv!^LdU@uY*8jD3lG@(R0-@@mr@ zCX>^W7`{;4y-u_tet#xa!qsc>cKRiJKol2FS!Xuxppc{AQ@zo%aG*>?^`|Fa8IPys zGp~g_1=h9_jVaLvpw>O8Z_>DkW2M9^r*X3S29$*ahEHfLUcP0@?Dq7xb7N}*Hnr06 zBBaO}ZjCCsJjbHDpAzKp@UR5}$B_0-jQ(<*GCOX)Fub5cZhw9vZE+-B)Oy;jfXcG& zxQ`sRz@@D~rMN?fj22|Sav>Z0#NsU!e^BV$>-($gf(O`z0=nP@d7c8LVii18>U>d@ z#wi&IhK#6zZ}+tBKr)L~#UWoMNuOFT8NH4{Db_WV_qiQEf0@$lV0&X(z@!0p%*Pf- zh*ZM)NMP7@@+=j8nW(Mx$g%zH#Nw+{rAHe3iFm9y96o;|UN!1ixnJe!%C1Pu z7jHhk>VSTlw)V0HjGz?~^L6>)rl0<~YBMjOHv65nZ zbX(Gag$Hn9x@-L@W0gmAaaCBf|i)tW3N05Q@@|amnHGS@v{^TrOby zIJ}>qMQG%7ufn=o4lUxeGWB`uvC2qMhQh5lOEj3AGXPyX+OFoXXwq<4k9UksN5ZqO z+<({ouLuVBLex=l(qHHsi{Xt0!o~A-r3>}_*#w2M>xi>f_Unr{7WsKUOSUU-M@0rH zMr4fs&|uZ;9S;Bf*fM1@6>iSS$x{fU)DKq`rCSDp^Hl~4iKUK86}O+Nbt{bc;t^}D z_F|t}U_0Sp>ol&9ZZ{MjQof%4L)9mD%zV4Xl2$H&ClN&UPK3VR4x1AJedvE#SHJWm z`p?j>X_xd)8wv*#rC4JrM-_#^?Za=L5tk3+O2;05uOGvNI~1DZF5`p@Uk*Ary|-N* zo+<4UUx@JyKjs_WIY=Xw1RW(Bzh|`Wu-$IE#&F;ASB3*wL)}eKki8<^#JgraL7g^k zk8rRH9kxEX3>9&-ZL6gQ^SlmS7oL;o2TF?Jil}rYPMtf@S6s z+4~^`a%X~6?`B$7f^|(bOTh1Po8%PMGb%ey`FpzPrSOAd+-cf3wDX3}Yjh03=INVv)S-S1)>u|Ui5VB_vF)(Zw$0i1oCQ4BeL*U25sr`SQyqv|*eALdk4C_rp zj}XdYbFmH`&_tOwAzRu*7gc;JYTZ$8i@KDgTObY+_nCoDSRa^di(<`hJ_)Is8 z!w;>HNYg#hb%a>&1DXk8W9fW!@|M!e@t+zS$Pieqxs=&m70)R(@bvSJ2yCa%kaH*Gj2-g*X&tMwQ7Lon79yy#X?f3KUk> z8K`qqcTYwCex9ND%~y}ZoetjdKo&Y8CT{X?vKW>MIsMggVMz^b`p80gTcHfy4e*4$ zTI7zD_@FscXHE*_A&1|H%!o5iuJi0XtAg}`s`>XK%MaBQfd3a7Z{{HPHhanEF5qd& zZJs-kP)Y+(vi^Oo%l&)V0|{wafy6RfIQzZ(9kukH5Qq6!XmPWTiWkLI)Q#F*$I)pA z6)8`NJ0WMz8R93!VqkB1sp8g|y<*~Q&u`zU%Ywk)UQ1jw#>0541`uYg6a=sdw>~}e zj5c;sAl&t~O2rjZI}17(h}c66IGp}z0Se5SC#_V3S9*%lQMPHL)=^h_S7GuS>tlDi zS0L;nT$@{8avs)TvLHx|zzu@_V2K=stt#O+IL{(w`0$K1U=3zD1cwV+m^AILn50P8 zGcFB(y8AbgeuWeZwQ|<~&I??g=`)X(sc^gBTSG1>ZdFQO^tOa~iJ6?8{J0pl@$#|E zSV!M}>43G_XRLZ95a+{sO9QizzM!c;#KfWQ!Hg!{atnIUR9zO8KcbK;ql-lCROezW z(N@_Rkp^I|uv-geB(n>)85Kxfd=_{}x)A;1s2p z?RYh}uv_*aV7Bm!FPc>f_@3~E)~&G%6!#Ka#jQG^GG%qoXvG1bX<9a~#va!KIJ0S| zi733sR$rbN=E-*brjJlGR`Ru^?mCELqWKy&nJ&n-UP$xzgeW^ODuyuRR z8Vi?C9IkPG*IErP@=|@|?}?`t`m(QBCB|3H@jLj07)GS}Q-~6J zJ(_sC4E){i7U2xIoU;%eKjjSk5s^YBoi76XN#$}qDG$4$~ z=)do(DNr+#O=(s_{tIgU|`W1xwKqdCZXMJ6I0uU!lIHm0sQQ&(7ybCybI)iD7Ass!2iq5 qCZ8Yq1ZQRb>u+AadB6Ace61eNQQn$&4Y)Js+#PKrtxAo1q5lO5s^B&N literal 0 HcmV?d00001 diff --git a/doc/src/devdocs/precompile_hang.md b/doc/src/devdocs/precompile_hang.md new file mode 100644 index 0000000000000..0de9c99792b64 --- /dev/null +++ b/doc/src/devdocs/precompile_hang.md @@ -0,0 +1,98 @@ +# Fixing precompilation hangs due to open tasks or IO + +On Julia 1.10 or higher, you might see the following message: + +![Screenshot of precompilation hang](./img/precompilation_hang.png) + +This may repeat. If it continues to repeat with no hints that it will +resolve itself, you may have a "precompilation hang" that requires +fixing. Even if it's transient, you might prefer to resolve it so that +users will not be bothered by this warning. This page walks you +through how to analyze and fix such issues. + +If you follow the advice and hit `Ctrl-C`, you might see + +``` +^C Interrupted: Exiting precompilation... + + 1 dependency had warnings during precompilation: +┌ Test1 [ac89d554-e2ba-40bc-bc5c-de68b658c982] +│ [pid 2745] waiting for IO to finish: +│ Handle type uv_handle_t->data +│ timer 0x55580decd1e0->0x7f94c3a4c340 +``` + +This message conveys two key pieces of information: + +- the hang is occurring during precompilation of `Test1`, a dependency of `Test2` (the package we were trying to load with `using Test2`) +- during precompilation of `Test1`, Julia created a `Timer` object (use `?Timer` if you're unfamiliar with Timers) which is still open; until that closes, the process is hung + +If this is enough of a hint for you to figure out how `timer = Timer(args...)` is being created, one good solution is to add `wait(timer)` if `timer` eventually finishes on its own, or `close(timer)` if you need to force-close it, before the final `end` of the module. + +However, there are cases that may not be that straightforward. Usually the best option is to start by determining whether the hang is due to code in Test1 or whether it is due to one of Test1's dependencies: + +- Option 1: `Pkg.add("Aqua")` and use [`Aqua.test_persistent_tasks`](https://juliatesting.github.io/Aqua.jl/dev/#Aqua.test_persistent_tasks-Tuple{Base.PkgId}). This should help you identify which package is causing the problem, after which the instructions [below](@ref pchang_fix) should be followed. If needed, you can create a `PkgId` as `Base.PkgId(UUID("..."), "Test1")`, where `...` comes from the `uuid` entry in `Test1/Project.toml`. +- Option 2: manually diagnose the source of the hang. + +To manually diagnose: + +1. `Pkg.develop("Test1")` +2. Comment out all the code `include`d or defined in `Test1`, *except* the `using/import` statements. +3. Try `using Test2` (or even `using Test1` assuming that hangs too) again + +Now we arrive at a fork in the road: either + +- the hang persists, indicating it is [due to one of your dependencies](@ref pchang_deps) +- the hang disappears, indicating that it is [due to something in your code](@ref pchang_fix). + +## [Diagnosing and fixing hangs due to a package dependency](@id pchang_deps) + +Use a binary search to identify the problematic dependency: start by commenting out half your dependencies, then when you isolate which half is responsible comment out half of that half, etc. (You don't have to remove them from the project, just comment out the `using`/`import` statements.) + +Once you've identified a suspect (here we'll call it `ThePackageYouThinkIsCausingTheProblem`), first try precompiling that package. If it also hangs during precompilation, continue chasing the problem backwards. + +However, most likely `ThePackageYouThinkIsCausingTheProblem` will precompile fine. This suggests it's in the function `ThePackageYouThinkIsCausingTheProblem.__init__`, which does not run during precompilation of `ThePackageYouThinkIsCausingTheProblem` but *does* in any package that loads `ThePackageYouThinkIsCausingTheProblem`. To test this theory, set up a minimal working example (MWE), something like + +```julia +(@v1.10) pkg> generate MWE + Generating project MWE: + MWE\Project.toml + MWE\src\MWE.jl +``` + +where the source code of `MWE.jl` is + +```julia +module MWE +using ThePackageYouThinkIsCausingTheProblem +end +``` + +and you've added `ThePackageYouThinkIsCausingTheProblem` to MWE's dependencies. + +If that MWE reproduces the hang, you've found your culprit: +`ThePackageYouThinkIsCausingTheProblem.__init__` must be creating the `Timer` object. If the timer object can be safely `close`d, that's a good option. Otherwise, the most common solution is to avoid creating the timer while *any* package is being precompiled: add + +```julia +ccall(:jl_generating_output, Cint, ()) == 1 && return nothing +``` + +as the first line of `ThePackageYouThinkIsCausingTheProblem.__init__`, and it will avoid doing any initialization in any Julia process whose purpose is to precompile packages. + +## [Fixing package code to avoid hangs](@id pchang_fix) + +Search your package for suggestive words (here like "Timer") and see if you can identify where the problem is being created. Note that a method *definition* like + +```julia +maketimer() = Timer(timer -> println("hi"), 0; interval=1) +``` + +is not problematic in and of itself: it can cause this problem only if `maketimer` gets called while the module is being defined. This might be happening from a top-level statement such as + +```julia +const GLOBAL_TIMER = maketimer() +``` + +or it might conceivably occur in a [precompile workload](https://github.com/JuliaLang/PrecompileTools.jl). + +If you struggle to identify the causative lines, then consider doing a binary search: comment out sections of your package (or `include` lines to omit entire files) until you've reduced the problem in scope. diff --git a/src/jl_uv.c b/src/jl_uv.c index 281dd798dbb36..62dc3a628d085 100644 --- a/src/jl_uv.c +++ b/src/jl_uv.c @@ -51,9 +51,9 @@ static void walk_print_cb(uv_handle_t *h, void *arg) npad += strlen(type); pad += npad < strlen(pad) ? npad : strlen(pad); if (fd == -1) - jl_safe_printf(" %s %s@%p->%p\n", type, pad, (void*)h, (void*)h->data); + jl_safe_printf(" %s %s%p->%p\n", type, pad, (void*)h, (void*)h->data); else - jl_safe_printf(" %s[%zd] %s@%p->%p\n", type, (size_t)fd, pad, (void*)h, (void*)h->data); + jl_safe_printf(" %s[%zd] %s%p->%p\n", type, (size_t)fd, pad, (void*)h, (void*)h->data); } static void wait_empty_func(uv_timer_t *t) @@ -63,9 +63,12 @@ static void wait_empty_func(uv_timer_t *t) if (!uv_loop_alive(t->loop)) return; jl_safe_printf("\n[pid %zd] waiting for IO to finish:\n" - " TYPE[FD/PID] @UV_HANDLE_T->DATA\n", + " Handle type uv_handle_t->data\n", (size_t)uv_os_getpid()); uv_walk(jl_io_loop, walk_print_cb, NULL); + if (jl_generating_output() && jl_options.incremental) { + jl_safe_printf("This means that a package has started a background task or event source that has not finished running. For precompilation to complete successfully, the event source needs to be closed explicitly. See the developer documentation on fixing precompilation hangs for more help.\n"); + } jl_gc_collect(JL_GC_FULL); } From 5c9602e137f5bd49daf184d74ed5a0a244fcd684 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Sat, 7 Oct 2023 18:53:10 -0400 Subject: [PATCH 26/38] Don't mark nonlocal symbols as hidden (#51596) Co-authored-by: Prem Chintalapudi (cherry picked from commit 341e9d067d0cbdce5fe85ff18d6ef5945cbadc28) --- src/aotcompile.cpp | 18 ++++++++++++++++-- src/codegen.cpp | 4 ++++ src/staticdata.c | 12 ++++++------ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 05088394500c1..5fc9a30fc35c3 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -835,8 +835,12 @@ static SmallVector partitionModule(Module &M, unsigned threads) { continue; if (!canPartition(G)) continue; - G.setLinkage(GlobalValue::ExternalLinkage); - G.setVisibility(GlobalValue::HiddenVisibility); + // Currently ccallable global aliases have extern linkage, we only want to make the + // internally linked functions/global variables extern+hidden + if (G.hasLocalLinkage()) { + G.setLinkage(GlobalValue::ExternalLinkage); + G.setVisibility(GlobalValue::HiddenVisibility); + } if (auto F = dyn_cast(&G)) { partitioner.make(&G, getFunctionWeight(*F).weight); } else { @@ -1569,6 +1573,16 @@ void jl_dump_native_impl(void *native_code, Type *T_psize = dataM.getDataLayout().getIntPtrType(Context)->getPointerTo(); + // This should really be in jl_create_native, but we haven't + // yet set the target triple binary format correctly at that + // point. This should be resolved when we start JITting for + // COFF when we switch over to JITLink. + for (auto &GA : dataM.aliases()) { + // Global aliases are only used for ccallable things, so we should + // mark them as dllexport + addComdat(&GA, TheTriple); + } + // add metadata information if (imaging_mode) { multiversioning_preannotate(dataM); diff --git a/src/codegen.cpp b/src/codegen.cpp index ea00c9488e303..0528180d1b50d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6797,6 +6797,10 @@ const char *jl_generate_ccallable(LLVMOrcThreadSafeModuleRef llvmmod, void *sysi int found = jl_dlsym(sysimg_handle, name, &addr, 0); if (found) add_named_global(name, addr); + else { + err = jl_get_exceptionf(jl_errorexception_type, "%s not found in sysimg", name); + jl_throw(err); + } } else { jl_method_instance_t *lam = jl_get_specialization1((jl_tupletype_t*)sigt, world, &min_valid, &max_valid, 0); diff --git a/src/staticdata.c b/src/staticdata.c index aae64ae2ffb51..36961b58f375a 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -3372,7 +3372,7 @@ static jl_value_t *jl_validate_cache_file(ios_t *f, jl_array_t *depmods, uint64_ } // TODO?: refactor to make it easier to create the "package inspector" -static jl_value_t *jl_restore_package_image_from_stream(ios_t *f, jl_image_t *image, jl_array_t *depmods, int completeinfo, const char *pkgname, bool needs_permalloc) +static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, ios_t *f, jl_image_t *image, jl_array_t *depmods, int completeinfo, const char *pkgname, bool needs_permalloc) { JL_TIMING(LOAD_IMAGE, LOAD_Pkgimg); jl_timing_printf(JL_TIMING_DEFAULT_BLOCK, pkgname); @@ -3427,7 +3427,7 @@ static jl_value_t *jl_restore_package_image_from_stream(ios_t *f, jl_image_t *im size_t world = jl_atomic_load_acquire(&jl_world_counter); jl_insert_backedges((jl_array_t*)edges, (jl_array_t*)ext_targets, (jl_array_t*)new_specializations, world); // restore external backedges (needs to be last) // reinit ccallables - jl_reinit_ccallable(&ccallable_list, base, NULL); + jl_reinit_ccallable(&ccallable_list, base, pkgimage_handle); arraylist_free(&ccallable_list); if (completeinfo) { @@ -3458,11 +3458,11 @@ static void jl_restore_system_image_from_stream(ios_t *f, jl_image_t *image, uin jl_restore_system_image_from_stream_(f, image, NULL, checksum | ((uint64_t)0xfdfcfbfa << 32), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } -JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(const char *buf, jl_image_t *image, size_t sz, jl_array_t *depmods, int completeinfo, const char *pkgname, bool needs_permalloc) +JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(void* pkgimage_handle, const char *buf, jl_image_t *image, size_t sz, jl_array_t *depmods, int completeinfo, const char *pkgname, bool needs_permalloc) { ios_t f; ios_static_buffer(&f, (char*)buf, sz); - jl_value_t *ret = jl_restore_package_image_from_stream(&f, image, depmods, completeinfo, pkgname, needs_permalloc); + jl_value_t *ret = jl_restore_package_image_from_stream(pkgimage_handle, &f, image, depmods, completeinfo, pkgname, needs_permalloc); ios_close(&f); return ret; } @@ -3475,7 +3475,7 @@ JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *d "Cache file \"%s\" not found.\n", fname); } jl_image_t pkgimage = {}; - jl_value_t *ret = jl_restore_package_image_from_stream(&f, &pkgimage, depmods, completeinfo, pkgname, true); + jl_value_t *ret = jl_restore_package_image_from_stream(NULL, &f, &pkgimage, depmods, completeinfo, pkgname, true); ios_close(&f); return ret; } @@ -3546,7 +3546,7 @@ JL_DLLEXPORT jl_value_t *jl_restore_package_image_from_file(const char *fname, j jl_image_t pkgimage = jl_init_processor_pkgimg(pkgimg_handle); - jl_value_t* mod = jl_restore_incremental_from_buf(pkgimg_data, &pkgimage, *plen, depmods, completeinfo, pkgname, false); + jl_value_t* mod = jl_restore_incremental_from_buf(pkgimg_handle, pkgimg_data, &pkgimage, *plen, depmods, completeinfo, pkgname, false); return mod; } From 4aeef2147943ed21cd91b66fa867af9c2a52753e Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 25 Oct 2023 15:45:45 +0900 Subject: [PATCH 27/38] [REPLCompletions] allow symbol completions within incomplete macrocall expression (#51834) fix #51827 --------- Co-authored-by: Kristoffer Carlsson (cherry picked from commit 3b1ba62bd27e0dabbd2ec0a6c000edb8138c5915) --- stdlib/REPL/src/REPLCompletions.jl | 5 +++++ stdlib/REPL/test/replcompletions.jl | 24 ++++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 94ca678b8f387..b94ea3da2890b 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -1041,6 +1041,11 @@ function complete_identifiers!(suggestions::Vector{Completion}, @nospecialize(ff if isinfix ex = ex.args[end] end + elseif isexpr(ex, :macrocall) && length(ex.args) > 1 + # allow symbol completions within potentially incomplete macrocalls + if s[end] ≠ '`' && s[end] ≠ ')' + ex = ex.args[end] + end end end append!(suggestions, complete_symbol(ex, name, ffunc, context_module)) diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index c515ec5927dd3..63b629c5aa784 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -157,7 +157,7 @@ end test_complete(s) = map_completion_text(@inferred(completions(s, lastindex(s)))) test_scomplete(s) = map_completion_text(@inferred(shell_completions(s, lastindex(s)))) test_bslashcomplete(s) = map_completion_text(@inferred(bslash_completions(s, lastindex(s)))[2]) -test_complete_context(s, m) = map_completion_text(@inferred(completions(s,lastindex(s), m))) +test_complete_context(s, m=@__MODULE__) = map_completion_text(@inferred(completions(s,lastindex(s), m))) test_complete_foo(s) = test_complete_context(s, Main.CompletionFoo) test_complete_noshift(s) = map_completion_text(@inferred(completions(s, lastindex(s), Main, false))) @@ -1841,7 +1841,7 @@ function Base.getproperty(v::Issue36437, s::Symbol) end let s = "Issue36437(42)." - c, r, res = test_complete_context(s, @__MODULE__) + c, r, res = test_complete_context(s) @test res for n in ("a", "b", "c") @test n in c @@ -1849,7 +1849,7 @@ let s = "Issue36437(42)." end let s = "Some(Issue36437(42)).value." - c, r, res = test_complete_context(s, @__MODULE__) + c, r, res = test_complete_context(s) @test res for n in ("a", "b", "c") @test n in c @@ -1858,7 +1858,7 @@ end # aggressive concrete evaluation on mutable allocation in `repl_frame` let s = "Ref(Issue36437(42))[]." - c, r, res = test_complete_context(s, @__MODULE__) + c, r, res = test_complete_context(s) @test res for n in ("a", "b", "c") @test n in c @@ -1868,7 +1868,7 @@ end const global_xs = [Some(42)] let s = "pop!(global_xs)." - c, r, res = test_complete_context(s, @__MODULE__) + c, r, res = test_complete_context(s) @test res @test "value" in c end @@ -1900,7 +1900,7 @@ end Issue49892(x) = x let s = "Issue49892(fal" - c, r, res = test_complete_context(s, @__MODULE__) + c, r, res = test_complete_context(s) @test res for n in ("false", "falses") @test n in c @@ -1920,3 +1920,15 @@ for (s, compl) in (("2*CompletionFoo.nam", "named"), c, r = test_complete(s) @test only(c) == compl end + +# allows symbol completion within incomplete :macrocall +# https://github.com/JuliaLang/julia/issues/51827 +macro issue51827(args...) + length(args) ≥ 2 || error("@issue51827: incomplete arguments") + return args +end +let s = "@issue51827 Base.ac" + c, r, res = test_complete_context(s) + @test res + @test "acquire" in c +end From 4f7feb19f70d8aeef3b54ec7d13f992b0c6200cd Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 25 Sep 2023 13:20:19 +0200 Subject: [PATCH 28/38] add support for async backtraces of Tasks on any thread (#51430) (cherry picked from commit e5c6340e252986c0bba240a0924ee4b6885aeb65) --- src/Makefile | 2 +- src/gc-stacks.c | 80 +++++--- src/gc.c | 8 +- src/interpreter.c | 3 +- src/julia.h | 5 + src/julia_internal.h | 5 + src/julia_threads.h | 8 +- src/mtarraylist.c | 81 ++++++++ src/signals-mach.c | 29 ++- src/signals-unix.c | 54 +++-- src/signals-win.c | 109 ++++++---- src/stackwalk.c | 468 +++++++++++++++++++++++-------------------- src/threading.c | 6 +- 13 files changed, 531 insertions(+), 327 deletions(-) create mode 100644 src/mtarraylist.c diff --git a/src/Makefile b/src/Makefile index 3ea28e7c40324..08fb816d302a0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -43,7 +43,7 @@ endif SRCS := \ jltypes gf typemap smallintset ast builtins module interpreter symbol \ dlload sys init task array staticdata toplevel jl_uv datatype \ - simplevector runtime_intrinsics precompile jloptions \ + simplevector runtime_intrinsics precompile jloptions mtarraylist \ threading partr stackwalk gc gc-debug gc-pages gc-stacks gc-alloc-profiler method \ jlapi signal-handling safepoint timing subtype rtutils gc-heap-snapshot \ crc32c APInt-C processor ircode opaque_closure codegen-stubs coverage runtime_ccall diff --git a/src/gc-stacks.c b/src/gc-stacks.c index b35c1722c82ff..b89ae8cb08e0d 100644 --- a/src/gc-stacks.c +++ b/src/gc-stacks.c @@ -112,7 +112,7 @@ static void _jl_free_stack(jl_ptls_t ptls, void *stkbuf, size_t bufsz) if (bufsz <= pool_sizes[JL_N_STACK_POOLS - 1]) { unsigned pool_id = select_pool(bufsz); if (pool_sizes[pool_id] == bufsz) { - arraylist_push(&ptls->heap.free_stacks[pool_id], stkbuf); + small_arraylist_push(&ptls->heap.free_stacks[pool_id], stkbuf); return; } } @@ -141,7 +141,7 @@ void jl_release_task_stack(jl_ptls_t ptls, jl_task_t *task) #ifdef _COMPILER_ASAN_ENABLED_ __asan_unpoison_stack_memory((uintptr_t)stkbuf, bufsz); #endif - arraylist_push(&ptls->heap.free_stacks[pool_id], stkbuf); + small_arraylist_push(&ptls->heap.free_stacks[pool_id], stkbuf); } } } @@ -156,9 +156,9 @@ JL_DLLEXPORT void *jl_malloc_stack(size_t *bufsz, jl_task_t *owner) JL_NOTSAFEPO if (ssize <= pool_sizes[JL_N_STACK_POOLS - 1]) { unsigned pool_id = select_pool(ssize); ssize = pool_sizes[pool_id]; - arraylist_t *pool = &ptls->heap.free_stacks[pool_id]; + small_arraylist_t *pool = &ptls->heap.free_stacks[pool_id]; if (pool->len > 0) { - stk = arraylist_pop(pool); + stk = small_arraylist_pop(pool); } } else { @@ -177,8 +177,8 @@ JL_DLLEXPORT void *jl_malloc_stack(size_t *bufsz, jl_task_t *owner) JL_NOTSAFEPO } *bufsz = ssize; if (owner) { - arraylist_t *live_tasks = &ptls->heap.live_tasks; - arraylist_push(live_tasks, owner); + small_arraylist_t *live_tasks = &ptls->heap.live_tasks; + mtarraylist_push(live_tasks, owner); } return stk; } @@ -202,7 +202,7 @@ void sweep_stack_pools(void) // free half of stacks that remain unused since last sweep for (int p = 0; p < JL_N_STACK_POOLS; p++) { - arraylist_t *al = &ptls2->heap.free_stacks[p]; + small_arraylist_t *al = &ptls2->heap.free_stacks[p]; size_t n_to_free; if (al->len > MIN_STACK_MAPPINGS_PER_POOL) { n_to_free = al->len / 2; @@ -213,12 +213,12 @@ void sweep_stack_pools(void) n_to_free = 0; } for (int n = 0; n < n_to_free; n++) { - void *stk = arraylist_pop(al); + void *stk = small_arraylist_pop(al); free_stack(stk, pool_sizes[p]); } } - arraylist_t *live_tasks = &ptls2->heap.live_tasks; + small_arraylist_t *live_tasks = &ptls2->heap.live_tasks; size_t n = 0; size_t ndel = 0; size_t l = live_tasks->len; @@ -261,24 +261,52 @@ void sweep_stack_pools(void) JL_DLLEXPORT jl_array_t *jl_live_tasks(void) { - jl_task_t *ct = jl_current_task; - jl_ptls_t ptls = ct->ptls; - arraylist_t *live_tasks = &ptls->heap.live_tasks; - size_t i, j, l; - jl_array_t *a; - do { - l = live_tasks->len; - a = jl_alloc_vec_any(l + 1); // may gc, changing the number of tasks - } while (l + 1 < live_tasks->len); - l = live_tasks->len; - void **lst = live_tasks->items; - j = 0; - ((void**)jl_array_data(a))[j++] = ptls->root_task; - for (i = 0; i < l; i++) { - if (((jl_task_t*)lst[i])->stkbuf != NULL) - ((void**)jl_array_data(a))[j++] = lst[i]; + size_t nthreads = jl_atomic_load_acquire(&jl_n_threads); + jl_ptls_t *allstates = jl_atomic_load_relaxed(&jl_all_tls_states); + size_t l = 0; // l is not reset on restart, so we keep getting more aggressive at making a big enough list everything it fails +restart: + for (size_t i = 0; i < nthreads; i++) { + // skip GC threads since they don't have tasks + if (gc_first_tid <= i && i < gc_first_tid + jl_n_gcthreads) { + continue; + } + jl_ptls_t ptls2 = allstates[i]; + if (ptls2 == NULL) + continue; + small_arraylist_t *live_tasks = &ptls2->heap.live_tasks; + size_t n = mtarraylist_length(live_tasks); + l += n + (ptls2->root_task->stkbuf != NULL); + } + l += l / 20; // add 5% for margin of estimation error + jl_array_t *a = jl_alloc_vec_any(l); // may gc, changing the number of tasks and forcing us to reload everything + nthreads = jl_atomic_load_acquire(&jl_n_threads); + allstates = jl_atomic_load_relaxed(&jl_all_tls_states); + size_t j = 0; + for (size_t i = 0; i < nthreads; i++) { + // skip GC threads since they don't have tasks + if (gc_first_tid <= i && i < gc_first_tid + jl_n_gcthreads) { + continue; + } + jl_ptls_t ptls2 = allstates[i]; + if (ptls2 == NULL) + continue; + jl_task_t *t = ptls2->root_task; + if (t->stkbuf != NULL) { + if (j == l) + goto restart; + ((void**)jl_array_data(a))[j++] = t; + } + small_arraylist_t *live_tasks = &ptls2->heap.live_tasks; + size_t n = mtarraylist_length(live_tasks); + for (size_t i = 0; i < n; i++) { + jl_task_t *t = (jl_task_t*)mtarraylist_get(live_tasks, i); + if (t->stkbuf != NULL) { + if (j == l) + goto restart; + ((void**)jl_array_data(a))[j++] = t; + } + } } - l = jl_array_len(a); if (j < l) { JL_GC_PUSH1(&a); jl_array_del_end(a, l - j); diff --git a/src/gc.c b/src/gc.c index 6b86f4cab81a8..72d878864352b 100644 --- a/src/gc.c +++ b/src/gc.c @@ -947,7 +947,7 @@ JL_DLLEXPORT jl_weakref_t *jl_gc_new_weakref_th(jl_ptls_t ptls, jl_weakref_t *wr = (jl_weakref_t*)jl_gc_alloc(ptls, sizeof(void*), jl_weakref_type); wr->value = value; // NOTE: wb not needed here - arraylist_push(&ptls->heap.weak_refs, wr); + small_arraylist_push(&ptls->heap.weak_refs, wr); return wr; } @@ -3510,8 +3510,10 @@ void jl_init_thread_heap(jl_ptls_t ptls) p[i].freelist = NULL; p[i].newpages = NULL; } - arraylist_new(&heap->weak_refs, 0); - arraylist_new(&heap->live_tasks, 0); + small_arraylist_new(&heap->weak_refs, 0); + small_arraylist_new(&heap->live_tasks, 0); + for (int i = 0; i < JL_N_STACK_POOLS; i++) + small_arraylist_new(&heap->free_stacks[i], 0); heap->mallocarrays = NULL; heap->mafreelist = NULL; heap->big_objects = NULL; diff --git a/src/interpreter.c b/src/interpreter.c index 2ad56e76b2549..d84a1381fccad 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -65,7 +65,8 @@ extern void JL_GC_ENABLEFRAME(interpreter_state*) JL_NOTSAFEPOINT; // we define this separately so that we can populate the frame before we add it to the backtrace // it's recommended to mark the containing function with NOINLINE, though not essential #define JL_GC_ENABLEFRAME(frame) \ - ((void**)&frame[1])[0] = __builtin_frame_address(0); + jl_signal_fence(); \ + ((void**)&frame[1])[0] = __builtin_frame_address(0); #endif diff --git a/src/julia.h b/src/julia.h index 6da03fb3d526a..1f85f06dd35dc 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1050,6 +1050,11 @@ JL_DLLEXPORT void *jl_gc_managed_realloc(void *d, size_t sz, size_t oldsz, int isaligned, jl_value_t *owner); JL_DLLEXPORT void jl_gc_safepoint(void); +void *mtarraylist_get(small_arraylist_t *_a, size_t idx) JL_NOTSAFEPOINT; +size_t mtarraylist_length(small_arraylist_t *_a) JL_NOTSAFEPOINT; +void mtarraylist_add(small_arraylist_t *_a, void *elt, size_t idx) JL_NOTSAFEPOINT; +void mtarraylist_push(small_arraylist_t *_a, void *elt) JL_NOTSAFEPOINT; + // object accessors ----------------------------------------------------------- #define jl_svec_len(t) (((jl_svec_t*)(t))->length) diff --git a/src/julia_internal.h b/src/julia_internal.h index 3e4c01365a2fc..51661fa1835f9 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -203,6 +203,8 @@ JL_DLLEXPORT void jl_lock_profile(void) JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER; JL_DLLEXPORT void jl_unlock_profile(void) JL_NOTSAFEPOINT JL_NOTSAFEPOINT_LEAVE; JL_DLLEXPORT void jl_lock_profile_wr(void) JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER; JL_DLLEXPORT void jl_unlock_profile_wr(void) JL_NOTSAFEPOINT JL_NOTSAFEPOINT_LEAVE; +int jl_lock_stackwalk(void) JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER; +void jl_unlock_stackwalk(int lockret) JL_NOTSAFEPOINT JL_NOTSAFEPOINT_LEAVE; // number of cycles since power-on static inline uint64_t cycleclock(void) JL_NOTSAFEPOINT @@ -1162,6 +1164,9 @@ void jl_print_bt_entry_codeloc(jl_bt_element_t *bt_data) JL_NOTSAFEPOINT; #ifdef _OS_WINDOWS_ JL_DLLEXPORT void jl_refresh_dbg_module_list(void); #endif +int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) JL_NOTSAFEPOINT; +void jl_thread_resume(int tid) JL_NOTSAFEPOINT; + // *to is NULL or malloc'd pointer, from is allowed to be NULL STATIC_INLINE char *jl_copy_str(char **to, const char *from) JL_NOTSAFEPOINT { diff --git a/src/julia_threads.h b/src/julia_threads.h index f4c235243e684..8acbf9b53d90c 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -109,7 +109,7 @@ typedef struct { // handle to reference an OS thread #ifdef _OS_WINDOWS_ -typedef DWORD jl_thread_t; +typedef HANDLE jl_thread_t; #else typedef pthread_t jl_thread_t; #endif @@ -140,10 +140,10 @@ typedef struct { typedef struct { // variable for tracking weak references - arraylist_t weak_refs; + small_arraylist_t weak_refs; // live tasks started on this thread // that are holding onto a stack from the pool - arraylist_t live_tasks; + small_arraylist_t live_tasks; // variables for tracking malloc'd arrays struct _mallocarray_t *mallocarrays; @@ -170,7 +170,7 @@ typedef struct { jl_gc_pool_t norm_pools[JL_GC_N_POOLS]; #define JL_N_STACK_POOLS 16 - arraylist_t free_stacks[JL_N_STACK_POOLS]; + small_arraylist_t free_stacks[JL_N_STACK_POOLS]; } jl_thread_heap_t; typedef struct { diff --git a/src/mtarraylist.c b/src/mtarraylist.c new file mode 100644 index 0000000000000..8bad44797dab4 --- /dev/null +++ b/src/mtarraylist.c @@ -0,0 +1,81 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#include "julia.h" +#include "julia_internal.h" +#include "julia_assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// this file provides some alternate API functions for small_arraylist (push and add) +// which can be safely observed from other threads concurrently +// there is only permitted to be a single writer thread (or a mutex) +// but there can be any number of observers + +typedef struct { + _Atomic(uint32_t) len; + uint32_t max; + _Atomic(_Atomic(void*)*) items; + _Atomic(void*) _space[SMALL_AL_N_INLINE]; +} small_mtarraylist_t; + +// change capacity to at least newlen +static void mtarraylist_resizeto(small_mtarraylist_t *a, size_t len, size_t newlen) JL_NOTSAFEPOINT +{ + size_t max = a->max; + if (newlen > max) { + size_t nm = max * 2; + if (nm == 0) + nm = 1; + while (newlen > nm) + nm *= 2; + void *olditems = (void*)jl_atomic_load_relaxed(&a->items); + void *p = calloc_s(nm * sizeof(void*)); + memcpy(p, olditems, len * sizeof(void*)); + jl_atomic_store_release(&a->items, (_Atomic(void*)*)p); + a->max = nm; + if (olditems != (void*)&a->_space[0]) { + jl_task_t *ct = jl_current_task; + jl_gc_add_quiescent(ct->ptls, (void**)olditems, free); + } + } +} + +// single-threaded +void mtarraylist_push(small_arraylist_t *_a, void *elt) +{ + small_mtarraylist_t *a = (small_mtarraylist_t*)_a; + size_t len = jl_atomic_load_relaxed(&a->len); + mtarraylist_resizeto(a, len, len + 1); + jl_atomic_store_release(&jl_atomic_load_relaxed(&a->items)[len], elt); + jl_atomic_store_release(&a->len, len + 1); +} + +// single-threaded +void mtarraylist_add(small_arraylist_t *_a, void *elt, size_t idx) +{ + small_mtarraylist_t *a = (small_mtarraylist_t*)_a; + size_t len = jl_atomic_load_relaxed(&a->len); + mtarraylist_resizeto(a, len, idx + 1); + jl_atomic_store_release(&jl_atomic_load_relaxed(&a->items)[idx], elt); + if (jl_atomic_load_relaxed(&a->len) < idx + 1) + jl_atomic_store_release(&a->len, idx + 1); +} + +// concurrent-safe +size_t mtarraylist_length(small_arraylist_t *_a) +{ + small_mtarraylist_t *a = (small_mtarraylist_t*)_a; + return jl_atomic_load_relaxed(&a->len); +} + +// concurrent-safe +void *mtarraylist_get(small_arraylist_t *_a, size_t idx) +{ + small_mtarraylist_t *a = (small_mtarraylist_t*)_a; + size_t len = jl_atomic_load_acquire(&a->len); + if (idx >= len) + return NULL; + return jl_atomic_load_relaxed(&jl_atomic_load_relaxed(&a->items)[idx]); +} diff --git a/src/signals-mach.c b/src/signals-mach.c index 02bb044609ade..6ec8f95570f17 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -384,12 +384,12 @@ static void attach_exception_port(thread_port_t thread, int segv_only) HANDLE_MACH_ERROR("thread_set_exception_ports", ret); } -static int jl_thread_suspend_and_get_state2(int tid, host_thread_state_t *ctx) +static int jl_thread_suspend_and_get_state2(int tid, host_thread_state_t *ctx) JL_NOTSAFEPOINT { jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; if (ptls2 == NULL) // this thread is not alive return 0; - jl_task_t *ct2 = ptls2 ? jl_atomic_load_relaxed(&ptls2->current_task) : NULL; + jl_task_t *ct2 = jl_atomic_load_relaxed(&ptls2->current_task); if (ct2 == NULL) // this thread is already dead return 0; @@ -407,18 +407,18 @@ static int jl_thread_suspend_and_get_state2(int tid, host_thread_state_t *ctx) return 1; } -static void jl_thread_suspend_and_get_state(int tid, int timeout, unw_context_t **ctx) +int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) { (void)timeout; - static host_thread_state_t state; + host_thread_state_t state; if (!jl_thread_suspend_and_get_state2(tid, &state)) { - *ctx = NULL; - return; + return 0; } - *ctx = (unw_context_t*)&state; + *ctx = *(unw_context_t*)&state; + return 1; } -static void jl_thread_resume(int tid, int sig) +void jl_thread_resume(int tid) { jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; mach_port_t thread = pthread_mach_thread_np(ptls2->system_id); @@ -593,8 +593,15 @@ static void jl_unlock_profile_mach(int dlsymlock, int keymgr_locked) jl_unlock_profile(); } -#define jl_lock_profile() int keymgr_locked = jl_lock_profile_mach(1) -#define jl_unlock_profile() jl_unlock_profile_mach(1, keymgr_locked) +int jl_lock_stackwalk(void) +{ + return jl_lock_profile_mach(1); +} + +void jl_unlock_stackwalk(int lockret) +{ + jl_unlock_profile_mach(1, lockret); +} void *mach_profile_listener(void *arg) { @@ -691,7 +698,7 @@ void *mach_profile_listener(void *arg) bt_data_prof[bt_size_cur++].uintptr = 0; } // We're done! Resume the thread. - jl_thread_resume(i, 0); + jl_thread_resume(i); } jl_unlock_profile_mach(0, keymgr_locked); if (running) { diff --git a/src/signals-unix.c b/src/signals-unix.c index b2056947e2b8a..0d5ad9b1be7c5 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -291,6 +291,18 @@ int exc_reg_is_write_fault(uintptr_t esr) { #include "signals-mach.c" #else +int jl_lock_stackwalk(void) +{ + jl_lock_profile(); + return 0; +} + +void jl_unlock_stackwalk(int lockret) +{ + (void)lockret; + jl_unlock_profile(); +} + #if defined(_OS_LINUX_) && (defined(_CPU_X86_64_) || defined(_CPU_X86_)) int is_write_fault(void *context) { @@ -384,12 +396,12 @@ JL_NO_ASAN static void segv_handler(int sig, siginfo_t *info, void *context) } #if !defined(JL_DISABLE_LIBUNWIND) -static unw_context_t *signal_context; +static bt_context_t *signal_context; pthread_mutex_t in_signal_lock; static pthread_cond_t exit_signal_cond; static pthread_cond_t signal_caught_cond; -static void jl_thread_suspend_and_get_state(int tid, int timeout, unw_context_t **ctx) +int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); @@ -399,9 +411,8 @@ static void jl_thread_suspend_and_get_state(int tid, int timeout, unw_context_t jl_task_t *ct2 = ptls2 ? jl_atomic_load_relaxed(&ptls2->current_task) : NULL; if (ct2 == NULL) { // this thread is not alive or already dead - *ctx = NULL; pthread_mutex_unlock(&in_signal_lock); - return; + return 0; } jl_atomic_store_release(&ptls2->signal_request, 1); pthread_kill(ptls2->system_id, SIGUSR2); @@ -410,9 +421,8 @@ static void jl_thread_suspend_and_get_state(int tid, int timeout, unw_context_t if (err == ETIMEDOUT) { sig_atomic_t request = 1; if (jl_atomic_cmpswap(&ptls2->signal_request, &request, 0)) { - *ctx = NULL; pthread_mutex_unlock(&in_signal_lock); - return; + return 0; } // Request is either now 0 (meaning the other thread is waiting for // exit_signal_cond already), @@ -429,15 +439,16 @@ static void jl_thread_suspend_and_get_state(int tid, int timeout, unw_context_t // checking it is 0, and add an acquire barrier for good measure) int request = jl_atomic_load_acquire(&ptls2->signal_request); assert(request == 0); (void) request; - *ctx = signal_context; + jl_atomic_store_release(&ptls2->signal_request, 1); // prepare to resume normally + *ctx = *signal_context; + return 1; } -static void jl_thread_resume(int tid, int sig) +void jl_thread_resume(int tid) { jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; - jl_atomic_store_release(&ptls2->signal_request, sig == -1 ? 3 : 1); pthread_cond_broadcast(&exit_signal_cond); - pthread_cond_wait(&signal_caught_cond, &in_signal_lock); // wait for thread to acknowledge + pthread_cond_wait(&signal_caught_cond, &in_signal_lock); // wait for thread to acknowledge (so that signal_request doesn't get mixed up) // The other thread is waiting to leave exit_signal_cond (verify that here by // checking it is 0, and add an acquire barrier for good measure) int request = jl_atomic_load_acquire(&ptls2->signal_request); @@ -472,14 +483,14 @@ CFI_NORETURN static void jl_exit_thread0(int signo, jl_bt_element_t *bt_data, size_t bt_size) { jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[0]; - unw_context_t *signal_context; + bt_context_t signal_context; // This also makes sure `sleep` is aborted. - jl_thread_suspend_and_get_state(0, 30, &signal_context); - if (signal_context != NULL) { + if (jl_thread_suspend_and_get_state(0, 30, &signal_context)) { thread0_exit_signo = signo; ptls2->bt_size = bt_size; // <= JL_MAX_BT_SIZE memcpy(ptls2->bt_data, bt_data, ptls2->bt_size * sizeof(bt_data[0])); - jl_thread_resume(0, -1); // resume with message 3 (call jl_exit_thread0_cb) + jl_atomic_store_release(&ptls2->signal_request, 3); + jl_thread_resume(0); // resume with message 3 (call jl_exit_thread0_cb) } else { // thread 0 is gone? just do the exit ourself @@ -840,11 +851,11 @@ static void *signal_listener(void *arg) int nthreads = jl_atomic_load_acquire(&jl_n_threads); bt_size = 0; #if !defined(JL_DISABLE_LIBUNWIND) - unw_context_t *signal_context; + bt_context_t signal_context; // sample each thread, round-robin style in reverse order // (so that thread zero gets notified last) if (critical || profile) { - jl_lock_profile(); + int lockret = jl_lock_stackwalk(); int *randperm; if (profile) randperm = profile_get_randperm(nthreads); @@ -852,8 +863,7 @@ static void *signal_listener(void *arg) // Stop the threads in the random or reverse round-robin order. int i = profile ? randperm[idx] : idx; // notify thread to stop - jl_thread_suspend_and_get_state(i, 1, &signal_context); - if (signal_context == NULL) + if (!jl_thread_suspend_and_get_state(i, 1, &signal_context)) continue; // do backtrace on thread contexts for critical signals @@ -861,7 +871,7 @@ static void *signal_listener(void *arg) if (critical) { bt_size += rec_backtrace_ctx(bt_data + bt_size, JL_MAX_BT_SIZE / nthreads - 1, - signal_context, NULL); + &signal_context, NULL); bt_data[bt_size++].uintptr = 0; } @@ -883,7 +893,7 @@ static void *signal_listener(void *arg) } else { // Get backtrace data bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, - bt_size_max - bt_size_cur - 1, signal_context, NULL); + bt_size_max - bt_size_cur - 1, &signal_context, NULL); } jl_set_safe_restore(old_buf); @@ -908,9 +918,9 @@ static void *signal_listener(void *arg) } // notify thread to resume - jl_thread_resume(i, sig); + jl_thread_resume(i); } - jl_unlock_profile(); + jl_unlock_stackwalk(lockret); } #ifndef HAVE_MACH if (profile && running) { diff --git a/src/signals-win.c b/src/signals-win.c index 7cd3b02462851..10bd0dec7f480 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -344,6 +344,54 @@ JL_DLLEXPORT void jl_install_sigint_handler(void) static volatile HANDLE hBtThread = 0; +int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) +{ + (void)timeout; + jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; + if (ptls2 == NULL) // this thread is not alive + return 0; + jl_task_t *ct2 = jl_atomic_load_relaxed(&ptls2->current_task); + if (ct2 == NULL) // this thread is already dead + return 0; + HANDLE hThread = ptls2->system_id; + if ((DWORD)-1 == SuspendThread(hThread)) + return 0; + assert(sizeof(*ctx) == sizeof(CONTEXT)); + memset(ctx, 0, sizeof(CONTEXT)); + ctx->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; + if (!GetThreadContext(hThread, ctx)) { + if ((DWORD)-1 == ResumeThread(hThread)) + abort(); + return 0; + } + return 1; +} + +void jl_thread_resume(int tid) +{ + jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; + HANDLE hThread = ptls2->system_id; + if ((DWORD)-1 == ResumeThread(hThread)) { + fputs("failed to resume main thread! aborting.", stderr); + abort(); + } +} + +int jl_lock_stackwalk(void) +{ + uv_mutex_lock(&jl_in_stackwalk); + jl_lock_profile(); + return 0; +} + +void jl_unlock_stackwalk(int lockret) +{ + (void)lockret; + jl_unlock_profile(); + uv_mutex_unlock(&jl_in_stackwalk); +} + + static DWORD WINAPI profile_bt( LPVOID lparam ) { // Note: illegal to use jl_* functions from this thread except for profiling-specific functions @@ -357,58 +405,45 @@ static DWORD WINAPI profile_bt( LPVOID lparam ) continue; } else { - uv_mutex_lock(&jl_in_stackwalk); - jl_lock_profile(); - if ((DWORD)-1 == SuspendThread(hMainThread)) { - fputs("failed to suspend main thread. aborting profiling.", stderr); - break; - } + // TODO: bring this up to parity with other OS by adding loop over tid here + int lockret = jl_lock_stackwalk(); CONTEXT ctxThread; - memset(&ctxThread, 0, sizeof(CONTEXT)); - ctxThread.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - if (!GetThreadContext(hMainThread, &ctxThread)) { - fputs("failed to get context from main thread. aborting profiling.", stderr); + if (!jl_thread_suspend_and_get_state(0, 0, &ctxThread)) { + jl_unlock_stackwalk(lockret); + fputs("failed to suspend main thread. aborting profiling.", stderr); jl_profile_stop_timer(); + break; } - else { - // Get backtrace data - bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, - bt_size_max - bt_size_cur - 1, &ctxThread, NULL); + // Get backtrace data + bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, + bt_size_max - bt_size_cur - 1, &ctxThread, NULL); - jl_ptls_t ptls = jl_atomic_load_relaxed(&jl_all_tls_states)[0]; // given only profiling hMainThread + jl_ptls_t ptls = jl_atomic_load_relaxed(&jl_all_tls_states)[0]; // given only profiling hMainThread - // store threadid but add 1 as 0 is preserved to indicate end of block - bt_data_prof[bt_size_cur++].uintptr = ptls->tid + 1; + // store threadid but add 1 as 0 is preserved to indicate end of block + bt_data_prof[bt_size_cur++].uintptr = ptls->tid + 1; - // store task id (never null) - bt_data_prof[bt_size_cur++].jlvalue = (jl_value_t*)jl_atomic_load_relaxed(&ptls->current_task); + // store task id (never null) + bt_data_prof[bt_size_cur++].jlvalue = (jl_value_t*)jl_atomic_load_relaxed(&ptls->current_task); - // store cpu cycle clock - bt_data_prof[bt_size_cur++].uintptr = cycleclock(); + // store cpu cycle clock + bt_data_prof[bt_size_cur++].uintptr = cycleclock(); - // store whether thread is sleeping but add 1 as 0 is preserved to indicate end of block - bt_data_prof[bt_size_cur++].uintptr = jl_atomic_load_relaxed(&ptls->sleep_check_state) + 1; + // store whether thread is sleeping but add 1 as 0 is preserved to indicate end of block + bt_data_prof[bt_size_cur++].uintptr = jl_atomic_load_relaxed(&ptls->sleep_check_state) + 1; - // Mark the end of this block with two 0's - bt_data_prof[bt_size_cur++].uintptr = 0; - bt_data_prof[bt_size_cur++].uintptr = 0; - } - jl_unlock_profile(); - uv_mutex_unlock(&jl_in_stackwalk); - if ((DWORD)-1 == ResumeThread(hMainThread)) { - jl_profile_stop_timer(); - fputs("failed to resume main thread! aborting.", stderr); - jl_gc_debug_critical_error(); - abort(); - } + // Mark the end of this block with two 0's + bt_data_prof[bt_size_cur++].uintptr = 0; + bt_data_prof[bt_size_cur++].uintptr = 0; + jl_unlock_stackwalk(lockret); + jl_thread_resume(0); jl_check_profile_autostop(); } } } - jl_unlock_profile(); uv_mutex_unlock(&jl_in_stackwalk); jl_profile_stop_timer(); - hBtThread = 0; + hBtThread = NULL; return 0; } diff --git a/src/stackwalk.c b/src/stackwalk.c index 18bf4b2126938..cd4e46c204281 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -856,7 +856,7 @@ _os_ptr_munge(uintptr_t ptr) extern bt_context_t *jl_to_bt_context(void *sigctx); -void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT +static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT { jl_task_t *ct = jl_current_task; jl_ptls_t ptls = ct->ptls; @@ -865,222 +865,242 @@ void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT ptls->bt_size = rec_backtrace(ptls->bt_data, JL_MAX_BT_SIZE, 0); return; } - if (t->copy_stack || !t->started || t->stkbuf == NULL) - return; - int16_t old = -1; - if (!jl_atomic_cmpswap(&t->tid, &old, ptls->tid) && old != ptls->tid) - return; bt_context_t *context = NULL; -#if defined(_OS_WINDOWS_) bt_context_t c; - memset(&c, 0, sizeof(c)); - _JUMP_BUFFER *mctx = (_JUMP_BUFFER*)&t->ctx.ctx.uc_mcontext; + int16_t old = -1; + while (!jl_atomic_cmpswap(&t->tid, &old, ptls->tid) && old != ptls->tid) { + int lockret = jl_lock_stackwalk(); + // if this task is already running somewhere, we need to stop the thread it is running on and query its state + if (!jl_thread_suspend_and_get_state(old, 0, &c)) { + jl_unlock_stackwalk(lockret); + return; + } + jl_unlock_stackwalk(lockret); + if (jl_atomic_load_relaxed(&t->tid) == old) { + jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[old]; + if (ptls2->previous_task == t || // we might print the wrong stack here, since we can't know whether we executed the swapcontext yet or not, but it at least avoids trying to access the state inside uc_mcontext which might not be set yet + (ptls2->previous_task == NULL && jl_atomic_load_relaxed(&ptls2->current_task) == t)) { // this case should be always accurate + // use the thread context for the unwind state + context = &c; + } + break; + } + // got the wrong thread stopped, try again + jl_thread_resume(old); + } + if (context == NULL && (!t->copy_stack && t->started && t->stkbuf != NULL)) { + // need to read the context from the task stored state +#if defined(_OS_WINDOWS_) + memset(&c, 0, sizeof(c)); + _JUMP_BUFFER *mctx = (_JUMP_BUFFER*)&t->ctx.ctx.uc_mcontext; #if defined(_CPU_X86_64_) - c.Rbx = mctx->Rbx; - c.Rsp = mctx->Rsp; - c.Rbp = mctx->Rbp; - c.Rsi = mctx->Rsi; - c.Rdi = mctx->Rdi; - c.R12 = mctx->R12; - c.R13 = mctx->R13; - c.R14 = mctx->R14; - c.R15 = mctx->R15; - c.Rip = mctx->Rip; - memcpy(&c.Xmm6, &mctx->Xmm6, 10 * sizeof(mctx->Xmm6)); // Xmm6-Xmm15 + c.Rbx = mctx->Rbx; + c.Rsp = mctx->Rsp; + c.Rbp = mctx->Rbp; + c.Rsi = mctx->Rsi; + c.Rdi = mctx->Rdi; + c.R12 = mctx->R12; + c.R13 = mctx->R13; + c.R14 = mctx->R14; + c.R15 = mctx->R15; + c.Rip = mctx->Rip; + memcpy(&c.Xmm6, &mctx->Xmm6, 10 * sizeof(mctx->Xmm6)); // Xmm6-Xmm15 #else - c.Eip = mctx->Eip; - c.Esp = mctx->Esp; - c.Ebp = mctx->Ebp; + c.Eip = mctx->Eip; + c.Esp = mctx->Esp; + c.Ebp = mctx->Ebp; #endif - context = &c; + context = &c; #elif defined(JL_HAVE_UNW_CONTEXT) - context = &t->ctx.ctx; + context = &t->ctx.ctx; #elif defined(JL_HAVE_UCONTEXT) - context = jl_to_bt_context(&t->ctx.ctx); + context = jl_to_bt_context(&t->ctx.ctx); #elif defined(JL_HAVE_ASM) - bt_context_t c; - memset(&c, 0, sizeof(c)); - #if defined(_OS_LINUX_) && defined(__GLIBC__) - __jmp_buf *mctx = &t->ctx.ctx.uc_mcontext->__jmpbuf; - mcontext_t *mc = &c.uc_mcontext; - #if defined(_CPU_X86_) - // https://github.com/bminor/glibc/blame/master/sysdeps/i386/__longjmp.S - // https://github.com/bminor/glibc/blame/master/sysdeps/i386/jmpbuf-offsets.h - // https://github.com/bminor/musl/blame/master/src/setjmp/i386/longjmp.s - mc->gregs[REG_EBX] = (*mctx)[0]; - mc->gregs[REG_ESI] = (*mctx)[1]; - mc->gregs[REG_EDI] = (*mctx)[2]; - mc->gregs[REG_EBP] = (*mctx)[3]; - mc->gregs[REG_ESP] = (*mctx)[4]; - mc->gregs[REG_EIP] = (*mctx)[5]; - // ifdef PTR_DEMANGLE ? - mc->gregs[REG_ESP] = ptr_demangle(mc->gregs[REG_ESP]); - mc->gregs[REG_EIP] = ptr_demangle(mc->gregs[REG_EIP]); - context = &c; - #elif defined(_CPU_X86_64_) - // https://github.com/bminor/glibc/blame/master/sysdeps/x86_64/__longjmp.S - // https://github.com/bminor/glibc/blame/master/sysdeps/x86_64/jmpbuf-offsets.h - // https://github.com/bminor/musl/blame/master/src/setjmp/x86_64/setjmp.s - mc->gregs[REG_RBX] = (*mctx)[0]; - mc->gregs[REG_RBP] = (*mctx)[1]; - mc->gregs[REG_R12] = (*mctx)[2]; - mc->gregs[REG_R13] = (*mctx)[3]; - mc->gregs[REG_R14] = (*mctx)[4]; - mc->gregs[REG_R15] = (*mctx)[5]; - mc->gregs[REG_RSP] = (*mctx)[6]; - mc->gregs[REG_RIP] = (*mctx)[7]; - // ifdef PTR_DEMANGLE ? - mc->gregs[REG_RBP] = ptr_demangle(mc->gregs[REG_RBP]); - mc->gregs[REG_RSP] = ptr_demangle(mc->gregs[REG_RSP]); - mc->gregs[REG_RIP] = ptr_demangle(mc->gregs[REG_RIP]); - context = &c; - #elif defined(_CPU_ARM_) - // https://github.com/bminor/glibc/blame/master/sysdeps/arm/__longjmp.S - // https://github.com/bminor/glibc/blame/master/sysdeps/arm/include/bits/setjmp.h - // https://github.com/bminor/musl/blame/master/src/setjmp/arm/longjmp.S - mc->arm_sp = (*mctx)[0]; - mc->arm_lr = (*mctx)[1]; - mc->arm_r4 = (*mctx)[2]; // aka v1 - mc->arm_r5 = (*mctx)[3]; // aka v2 - mc->arm_r6 = (*mctx)[4]; // aka v3 - mc->arm_r7 = (*mctx)[5]; // aka v4 - mc->arm_r8 = (*mctx)[6]; // aka v5 - mc->arm_r9 = (*mctx)[7]; // aka v6 aka sb - mc->arm_r10 = (*mctx)[8]; // aka v7 aka sl - mc->arm_fp = (*mctx)[10]; // aka v8 aka r11 - // ifdef PTR_DEMANGLE ? - mc->arm_sp = ptr_demangle(mc->arm_sp); - mc->arm_lr = ptr_demangle(mc->arm_lr); - mc->arm_pc = mc->arm_lr; - context = &c; - #elif defined(_CPU_AARCH64_) - // https://github.com/bminor/glibc/blame/master/sysdeps/aarch64/__longjmp.S - // https://github.com/bminor/glibc/blame/master/sysdeps/aarch64/jmpbuf-offsets.h - // https://github.com/bminor/musl/blame/master/src/setjmp/aarch64/longjmp.s - // https://github.com/libunwind/libunwind/blob/ec171c9ba7ea3abb2a1383cee2988a7abd483a1f/src/aarch64/unwind_i.h#L62 - unw_fpsimd_context_t *mcfp = (unw_fpsimd_context_t*)&mc->__reserved; - mc->regs[19] = (*mctx)[0]; - mc->regs[20] = (*mctx)[1]; - mc->regs[21] = (*mctx)[2]; - mc->regs[22] = (*mctx)[3]; - mc->regs[23] = (*mctx)[4]; - mc->regs[24] = (*mctx)[5]; - mc->regs[25] = (*mctx)[6]; - mc->regs[26] = (*mctx)[7]; - mc->regs[27] = (*mctx)[8]; - mc->regs[28] = (*mctx)[9]; - mc->regs[29] = (*mctx)[10]; // aka fp - mc->regs[30] = (*mctx)[11]; // aka lr - // Yes, they did skip 12 why writing the code originally; and, no, I do not know why. - mc->sp = (*mctx)[13]; - mcfp->vregs[7] = (*mctx)[14]; // aka d8 - mcfp->vregs[8] = (*mctx)[15]; // aka d9 - mcfp->vregs[9] = (*mctx)[16]; // aka d10 - mcfp->vregs[10] = (*mctx)[17]; // aka d11 - mcfp->vregs[11] = (*mctx)[18]; // aka d12 - mcfp->vregs[12] = (*mctx)[19]; // aka d13 - mcfp->vregs[13] = (*mctx)[20]; // aka d14 - mcfp->vregs[14] = (*mctx)[21]; // aka d15 - // ifdef PTR_DEMANGLE ? - mc->sp = ptr_demangle(mc->sp); - mc->regs[30] = ptr_demangle(mc->regs[30]); - mc->pc = mc->regs[30]; - context = &c; - #else - #pragma message("jl_rec_backtrace not defined for ASM/SETJMP on unknown linux") - (void)mc; - (void)c; - (void)mctx; - #endif - #elif defined(_OS_DARWIN_) - sigjmp_buf *mctx = &t->ctx.ctx.uc_mcontext; - #if defined(_CPU_X86_64_) - // from https://github.com/apple/darwin-libplatform/blob/main/src/setjmp/x86_64/_setjmp.s - x86_thread_state64_t *mc = (x86_thread_state64_t*)&c; - mc->__rbx = ((uint64_t*)mctx)[0]; - mc->__rbp = ((uint64_t*)mctx)[1]; - mc->__rsp = ((uint64_t*)mctx)[2]; - mc->__r12 = ((uint64_t*)mctx)[3]; - mc->__r13 = ((uint64_t*)mctx)[4]; - mc->__r14 = ((uint64_t*)mctx)[5]; - mc->__r15 = ((uint64_t*)mctx)[6]; - mc->__rip = ((uint64_t*)mctx)[7]; - // added in libsystem_plaform 177.200.16 (macOS Mojave 10.14.3) - // prior to that _os_ptr_munge_token was (hopefully) typically 0, - // so x ^ 0 == x and this is a no-op - mc->__rbp = _OS_PTR_UNMUNGE(mc->__rbp); - mc->__rsp = _OS_PTR_UNMUNGE(mc->__rsp); - mc->__rip = _OS_PTR_UNMUNGE(mc->__rip); - context = &c; - #elif defined(_CPU_AARCH64_) - // from https://github.com/apple/darwin-libplatform/blob/main/src/setjmp/arm64/setjmp.s - // https://github.com/apple/darwin-xnu/blob/main/osfmk/mach/arm/_structs.h - // https://github.com/llvm/llvm-project/blob/7714e0317520207572168388f22012dd9e152e9e/libunwind/src/Registers.hpp -> Registers_arm64 - arm_thread_state64_t *mc = (arm_thread_state64_t*)&c; - mc->__x[19] = ((uint64_t*)mctx)[0]; - mc->__x[20] = ((uint64_t*)mctx)[1]; - mc->__x[21] = ((uint64_t*)mctx)[2]; - mc->__x[22] = ((uint64_t*)mctx)[3]; - mc->__x[23] = ((uint64_t*)mctx)[4]; - mc->__x[24] = ((uint64_t*)mctx)[5]; - mc->__x[25] = ((uint64_t*)mctx)[6]; - mc->__x[26] = ((uint64_t*)mctx)[7]; - mc->__x[27] = ((uint64_t*)mctx)[8]; - mc->__x[28] = ((uint64_t*)mctx)[9]; - mc->__x[10] = ((uint64_t*)mctx)[10]; - mc->__x[11] = ((uint64_t*)mctx)[11]; - mc->__x[12] = ((uint64_t*)mctx)[12]; - // 13 is reserved/unused - double *mcfp = (double*)&mc[1]; - mcfp[7] = ((uint64_t*)mctx)[14]; // aka d8 - mcfp[8] = ((uint64_t*)mctx)[15]; // aka d9 - mcfp[9] = ((uint64_t*)mctx)[16]; // aka d10 - mcfp[10] = ((uint64_t*)mctx)[17]; // aka d11 - mcfp[11] = ((uint64_t*)mctx)[18]; // aka d12 - mcfp[12] = ((uint64_t*)mctx)[19]; // aka d13 - mcfp[13] = ((uint64_t*)mctx)[20]; // aka d14 - mcfp[14] = ((uint64_t*)mctx)[21]; // aka d15 - mc->__fp = _OS_PTR_UNMUNGE(mc->__x[10]); - mc->__lr = _OS_PTR_UNMUNGE(mc->__x[11]); - mc->__x[12] = _OS_PTR_UNMUNGE(mc->__x[12]); - mc->__sp = mc->__x[12]; - // libunwind is broken for signed-pointers, but perhaps best not to leave the signed pointer lying around either - mc->__pc = ptrauth_strip(mc->__lr, 0); - mc->__pad = 0; // aka __ra_sign_state = not signed - context = &c; - #else - #pragma message("jl_rec_backtrace not defined for ASM/SETJMP on unknown darwin") - (void)mctx; - (void)c; - #endif - #elif defined(_OS_FREEBSD_) && defined(_CPU_X86_64_) - sigjmp_buf *mctx = &t->ctx.ctx.uc_mcontext; - mcontext_t *mc = &c.uc_mcontext; - // https://github.com/freebsd/freebsd-src/blob/releng/13.1/lib/libc/amd64/gen/_setjmp.S - mc->mc_rip = ((long*)mctx)[0]; - mc->mc_rbx = ((long*)mctx)[1]; - mc->mc_rsp = ((long*)mctx)[2]; - mc->mc_rbp = ((long*)mctx)[3]; - mc->mc_r12 = ((long*)mctx)[4]; - mc->mc_r13 = ((long*)mctx)[5]; - mc->mc_r14 = ((long*)mctx)[6]; - mc->mc_r15 = ((long*)mctx)[7]; - context = &c; - #else - #pragma message("jl_rec_backtrace not defined for ASM/SETJMP on unknown system") - (void)c; - #endif + memset(&c, 0, sizeof(c)); + #if defined(_OS_LINUX_) && defined(__GLIBC__) + __jmp_buf *mctx = &t->ctx.ctx.uc_mcontext->__jmpbuf; + mcontext_t *mc = &c.uc_mcontext; + #if defined(_CPU_X86_) + // https://github.com/bminor/glibc/blame/master/sysdeps/i386/__longjmp.S + // https://github.com/bminor/glibc/blame/master/sysdeps/i386/jmpbuf-offsets.h + // https://github.com/bminor/musl/blame/master/src/setjmp/i386/longjmp.s + mc->gregs[REG_EBX] = (*mctx)[0]; + mc->gregs[REG_ESI] = (*mctx)[1]; + mc->gregs[REG_EDI] = (*mctx)[2]; + mc->gregs[REG_EBP] = (*mctx)[3]; + mc->gregs[REG_ESP] = (*mctx)[4]; + mc->gregs[REG_EIP] = (*mctx)[5]; + // ifdef PTR_DEMANGLE ? + mc->gregs[REG_ESP] = ptr_demangle(mc->gregs[REG_ESP]); + mc->gregs[REG_EIP] = ptr_demangle(mc->gregs[REG_EIP]); + context = &c; + #elif defined(_CPU_X86_64_) + // https://github.com/bminor/glibc/blame/master/sysdeps/x86_64/__longjmp.S + // https://github.com/bminor/glibc/blame/master/sysdeps/x86_64/jmpbuf-offsets.h + // https://github.com/bminor/musl/blame/master/src/setjmp/x86_64/setjmp.s + mc->gregs[REG_RBX] = (*mctx)[0]; + mc->gregs[REG_RBP] = (*mctx)[1]; + mc->gregs[REG_R12] = (*mctx)[2]; + mc->gregs[REG_R13] = (*mctx)[3]; + mc->gregs[REG_R14] = (*mctx)[4]; + mc->gregs[REG_R15] = (*mctx)[5]; + mc->gregs[REG_RSP] = (*mctx)[6]; + mc->gregs[REG_RIP] = (*mctx)[7]; + // ifdef PTR_DEMANGLE ? + mc->gregs[REG_RBP] = ptr_demangle(mc->gregs[REG_RBP]); + mc->gregs[REG_RSP] = ptr_demangle(mc->gregs[REG_RSP]); + mc->gregs[REG_RIP] = ptr_demangle(mc->gregs[REG_RIP]); + context = &c; + #elif defined(_CPU_ARM_) + // https://github.com/bminor/glibc/blame/master/sysdeps/arm/__longjmp.S + // https://github.com/bminor/glibc/blame/master/sysdeps/arm/include/bits/setjmp.h + // https://github.com/bminor/musl/blame/master/src/setjmp/arm/longjmp.S + mc->arm_sp = (*mctx)[0]; + mc->arm_lr = (*mctx)[1]; + mc->arm_r4 = (*mctx)[2]; // aka v1 + mc->arm_r5 = (*mctx)[3]; // aka v2 + mc->arm_r6 = (*mctx)[4]; // aka v3 + mc->arm_r7 = (*mctx)[5]; // aka v4 + mc->arm_r8 = (*mctx)[6]; // aka v5 + mc->arm_r9 = (*mctx)[7]; // aka v6 aka sb + mc->arm_r10 = (*mctx)[8]; // aka v7 aka sl + mc->arm_fp = (*mctx)[10]; // aka v8 aka r11 + // ifdef PTR_DEMANGLE ? + mc->arm_sp = ptr_demangle(mc->arm_sp); + mc->arm_lr = ptr_demangle(mc->arm_lr); + mc->arm_pc = mc->arm_lr; + context = &c; + #elif defined(_CPU_AARCH64_) + // https://github.com/bminor/glibc/blame/master/sysdeps/aarch64/__longjmp.S + // https://github.com/bminor/glibc/blame/master/sysdeps/aarch64/jmpbuf-offsets.h + // https://github.com/bminor/musl/blame/master/src/setjmp/aarch64/longjmp.s + // https://github.com/libunwind/libunwind/blob/ec171c9ba7ea3abb2a1383cee2988a7abd483a1f/src/aarch64/unwind_i.h#L62 + unw_fpsimd_context_t *mcfp = (unw_fpsimd_context_t*)&mc->__reserved; + mc->regs[19] = (*mctx)[0]; + mc->regs[20] = (*mctx)[1]; + mc->regs[21] = (*mctx)[2]; + mc->regs[22] = (*mctx)[3]; + mc->regs[23] = (*mctx)[4]; + mc->regs[24] = (*mctx)[5]; + mc->regs[25] = (*mctx)[6]; + mc->regs[26] = (*mctx)[7]; + mc->regs[27] = (*mctx)[8]; + mc->regs[28] = (*mctx)[9]; + mc->regs[29] = (*mctx)[10]; // aka fp + mc->regs[30] = (*mctx)[11]; // aka lr + // Yes, they did skip 12 why writing the code originally; and, no, I do not know why. + mc->sp = (*mctx)[13]; + mcfp->vregs[7] = (*mctx)[14]; // aka d8 + mcfp->vregs[8] = (*mctx)[15]; // aka d9 + mcfp->vregs[9] = (*mctx)[16]; // aka d10 + mcfp->vregs[10] = (*mctx)[17]; // aka d11 + mcfp->vregs[11] = (*mctx)[18]; // aka d12 + mcfp->vregs[12] = (*mctx)[19]; // aka d13 + mcfp->vregs[13] = (*mctx)[20]; // aka d14 + mcfp->vregs[14] = (*mctx)[21]; // aka d15 + // ifdef PTR_DEMANGLE ? + mc->sp = ptr_demangle(mc->sp); + mc->regs[30] = ptr_demangle(mc->regs[30]); + mc->pc = mc->regs[30]; + context = &c; + #else + #pragma message("jl_rec_backtrace not defined for ASM/SETJMP on unknown linux") + (void)mc; + (void)c; + (void)mctx; + #endif + #elif defined(_OS_DARWIN_) + sigjmp_buf *mctx = &t->ctx.ctx.uc_mcontext; + #if defined(_CPU_X86_64_) + // from https://github.com/apple/darwin-libplatform/blob/main/src/setjmp/x86_64/_setjmp.s + x86_thread_state64_t *mc = (x86_thread_state64_t*)&c; + mc->__rbx = ((uint64_t*)mctx)[0]; + mc->__rbp = ((uint64_t*)mctx)[1]; + mc->__rsp = ((uint64_t*)mctx)[2]; + mc->__r12 = ((uint64_t*)mctx)[3]; + mc->__r13 = ((uint64_t*)mctx)[4]; + mc->__r14 = ((uint64_t*)mctx)[5]; + mc->__r15 = ((uint64_t*)mctx)[6]; + mc->__rip = ((uint64_t*)mctx)[7]; + // added in libsystem_plaform 177.200.16 (macOS Mojave 10.14.3) + // prior to that _os_ptr_munge_token was (hopefully) typically 0, + // so x ^ 0 == x and this is a no-op + mc->__rbp = _OS_PTR_UNMUNGE(mc->__rbp); + mc->__rsp = _OS_PTR_UNMUNGE(mc->__rsp); + mc->__rip = _OS_PTR_UNMUNGE(mc->__rip); + context = &c; + #elif defined(_CPU_AARCH64_) + // from https://github.com/apple/darwin-libplatform/blob/main/src/setjmp/arm64/setjmp.s + // https://github.com/apple/darwin-xnu/blob/main/osfmk/mach/arm/_structs.h + // https://github.com/llvm/llvm-project/blob/7714e0317520207572168388f22012dd9e152e9e/libunwind/src/Registers.hpp -> Registers_arm64 + arm_thread_state64_t *mc = (arm_thread_state64_t*)&c; + mc->__x[19] = ((uint64_t*)mctx)[0]; + mc->__x[20] = ((uint64_t*)mctx)[1]; + mc->__x[21] = ((uint64_t*)mctx)[2]; + mc->__x[22] = ((uint64_t*)mctx)[3]; + mc->__x[23] = ((uint64_t*)mctx)[4]; + mc->__x[24] = ((uint64_t*)mctx)[5]; + mc->__x[25] = ((uint64_t*)mctx)[6]; + mc->__x[26] = ((uint64_t*)mctx)[7]; + mc->__x[27] = ((uint64_t*)mctx)[8]; + mc->__x[28] = ((uint64_t*)mctx)[9]; + mc->__x[10] = ((uint64_t*)mctx)[10]; + mc->__x[11] = ((uint64_t*)mctx)[11]; + mc->__x[12] = ((uint64_t*)mctx)[12]; + // 13 is reserved/unused + double *mcfp = (double*)&mc[1]; + mcfp[7] = ((uint64_t*)mctx)[14]; // aka d8 + mcfp[8] = ((uint64_t*)mctx)[15]; // aka d9 + mcfp[9] = ((uint64_t*)mctx)[16]; // aka d10 + mcfp[10] = ((uint64_t*)mctx)[17]; // aka d11 + mcfp[11] = ((uint64_t*)mctx)[18]; // aka d12 + mcfp[12] = ((uint64_t*)mctx)[19]; // aka d13 + mcfp[13] = ((uint64_t*)mctx)[20]; // aka d14 + mcfp[14] = ((uint64_t*)mctx)[21]; // aka d15 + mc->__fp = _OS_PTR_UNMUNGE(mc->__x[10]); + mc->__lr = _OS_PTR_UNMUNGE(mc->__x[11]); + mc->__x[12] = _OS_PTR_UNMUNGE(mc->__x[12]); + mc->__sp = mc->__x[12]; + // libunwind is broken for signed-pointers, but perhaps best not to leave the signed pointer lying around either + mc->__pc = ptrauth_strip(mc->__lr, 0); + mc->__pad = 0; // aka __ra_sign_state = not signed + context = &c; + #else + #pragma message("jl_rec_backtrace not defined for ASM/SETJMP on unknown darwin") + (void)mctx; + (void)c; + #endif + #elif defined(_OS_FREEBSD_) && defined(_CPU_X86_64_) + sigjmp_buf *mctx = &t->ctx.ctx.uc_mcontext; + mcontext_t *mc = &c.uc_mcontext; + // https://github.com/freebsd/freebsd-src/blob/releng/13.1/lib/libc/amd64/gen/_setjmp.S + mc->mc_rip = ((long*)mctx)[0]; + mc->mc_rbx = ((long*)mctx)[1]; + mc->mc_rsp = ((long*)mctx)[2]; + mc->mc_rbp = ((long*)mctx)[3]; + mc->mc_r12 = ((long*)mctx)[4]; + mc->mc_r13 = ((long*)mctx)[5]; + mc->mc_r14 = ((long*)mctx)[6]; + mc->mc_r15 = ((long*)mctx)[7]; + context = &c; + #else + #pragma message("jl_rec_backtrace not defined for ASM/SETJMP on unknown system") + (void)c; + #endif #elif defined(JL_HAVE_ASYNCIFY) - #pragma message("jl_rec_backtrace not defined for ASYNCIFY") + #pragma message("jl_rec_backtrace not defined for ASYNCIFY") #elif defined(JL_HAVE_SIGALTSTACK) - #pragma message("jl_rec_backtrace not defined for SIGALTSTACK") + #pragma message("jl_rec_backtrace not defined for SIGALTSTACK") #else - #pragma message("jl_rec_backtrace not defined for unknown task system") + #pragma message("jl_rec_backtrace not defined for unknown task system") #endif + } if (context) - ptls->bt_size = rec_backtrace_ctx(ptls->bt_data, JL_MAX_BT_SIZE, context, t->gcstack); + ptls->bt_size = rec_backtrace_ctx(ptls->bt_data, JL_MAX_BT_SIZE, context, t->gcstack); if (old == -1) jl_atomic_store_relaxed(&t->tid, old); + else if (old != ptls->tid) + jl_thread_resume(old); } //-------------------------------------------------- @@ -1107,7 +1127,7 @@ JL_DLLEXPORT void jlbacktrace(void) JL_NOTSAFEPOINT } } -// Print backtrace for specified task +// Print backtrace for specified task to jl_safe_printf stderr JL_DLLEXPORT void jlbacktracet(jl_task_t *t) JL_NOTSAFEPOINT { jl_task_t *ct = jl_current_task; @@ -1125,34 +1145,42 @@ JL_DLLEXPORT void jl_print_backtrace(void) JL_NOTSAFEPOINT jlbacktrace(); } -// Print backtraces for all live tasks, for all threads. -// WARNING: this is dangerous and can crash if used outside of gdb, if -// all of Julia's threads are not stopped! +// Print backtraces for all live tasks, for all threads, to jl_safe_printf stderr JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT { size_t nthreads = jl_atomic_load_acquire(&jl_n_threads); jl_ptls_t *allstates = jl_atomic_load_relaxed(&jl_all_tls_states); for (size_t i = 0; i < nthreads; i++) { jl_ptls_t ptls2 = allstates[i]; - arraylist_t *live_tasks = &ptls2->heap.live_tasks; - size_t n = live_tasks->len; + if (ptls2 == NULL) + continue; + small_arraylist_t *live_tasks = &ptls2->heap.live_tasks; + size_t n = mtarraylist_length(live_tasks); + jl_task_t *t = ptls2->root_task; + int t_state = jl_atomic_load_relaxed(&t->_state); jl_safe_printf("==== Thread %d created %zu live tasks\n", - ptls2->tid + 1, n + 1); - jl_safe_printf(" ---- Root task (%p)\n", ptls2->root_task); - jl_safe_printf(" (sticky: %d, started: %d, state: %d, tid: %d)\n", - ptls2->root_task->sticky, ptls2->root_task->started, - jl_atomic_load_relaxed(&ptls2->root_task->_state), - jl_atomic_load_relaxed(&ptls2->root_task->tid) + 1); - jlbacktracet(ptls2->root_task); + ptls2->tid + 1, n + (t_state != JL_TASK_STATE_DONE)); + if (show_done || t_state != JL_TASK_STATE_DONE) { + jl_safe_printf(" ---- Root task (%p)\n", ptls2->root_task); + jl_safe_printf(" (sticky: %d, started: %d, state: %d, tid: %d)\n", + t->sticky, t->started, t_state, + jl_atomic_load_relaxed(&t->tid) + 1); + if (t->stkbuf != NULL) + jlbacktracet(t); + else + jl_safe_printf(" no stack\n"); + jl_safe_printf(" ---- End root task\n"); + } - void **lst = live_tasks->items; - for (size_t j = 0; j < live_tasks->len; j++) { - jl_task_t *t = (jl_task_t *)lst[j]; + for (size_t j = 0; j < n; j++) { + jl_task_t *t = (jl_task_t*)mtarraylist_get(live_tasks, j); + if (t == NULL) + continue; int t_state = jl_atomic_load_relaxed(&t->_state); - if (!show_done && t_state == JL_TASK_STATE_DONE) { + if (!show_done && t_state == JL_TASK_STATE_DONE) continue; - } jl_safe_printf(" ---- Task %zu (%p)\n", j + 1, t); + // n.b. this information might not be consistent with the stack printing after it, since it could start running or change tid, etc. jl_safe_printf(" (sticky: %d, started: %d, state: %d, tid: %d)\n", t->sticky, t->started, t_state, jl_atomic_load_relaxed(&t->tid) + 1); diff --git a/src/threading.c b/src/threading.c index 436e6184a6c23..b82e36f66d994 100644 --- a/src/threading.c +++ b/src/threading.c @@ -314,6 +314,8 @@ static uv_mutex_t tls_lock; // controls write-access to these variables: _Atomic(jl_ptls_t*) jl_all_tls_states JL_GLOBALLY_ROOTED; int jl_all_tls_states_size; static uv_cond_t cond; +// concurrent reads are permitted, using the same pattern as mtsmall_arraylist +// it is implemented separately because the API of direct jl_all_tls_states use is already widely prevalent // return calling thread's ID JL_DLLEXPORT int16_t jl_threadid(void) @@ -347,7 +349,7 @@ jl_ptls_t jl_init_threadtls(int16_t tid) #ifndef _OS_WINDOWS_ pthread_setspecific(jl_task_exit_key, (void*)ptls); #endif - ptls->system_id = (jl_thread_t)(uintptr_t)uv_thread_self(); + ptls->system_id = uv_thread_self(); ptls->rngseed = jl_rand(); if (tid == 0) ptls->disable_gc = 1; @@ -382,10 +384,10 @@ jl_ptls_t jl_init_threadtls(int16_t tid) uv_cond_init(&ptls->wake_signal); uv_mutex_lock(&tls_lock); - jl_ptls_t *allstates = jl_atomic_load_relaxed(&jl_all_tls_states); if (tid == -1) tid = jl_atomic_load_relaxed(&jl_n_threads); ptls->tid = tid; + jl_ptls_t *allstates = jl_atomic_load_relaxed(&jl_all_tls_states); if (jl_all_tls_states_size <= tid) { int i, newsize = jl_all_tls_states_size + tid + 2; jl_ptls_t *newpptls = (jl_ptls_t*)calloc(newsize, sizeof(jl_ptls_t)); From a16e4e1847445129e315a1723be0d91a25b665ab Mon Sep 17 00:00:00 2001 From: Kiran Date: Wed, 27 Sep 2023 06:20:30 -0400 Subject: [PATCH 29/38] Fix segfault if root task is NULL (#51471) In `jl_print_task_backtraces()`. Follow-on to https://github.com/JuliaLang/julia/pull/51430. (cherry picked from commit cde964f392659b301c6019e4ec02c07d43da2c92) --- src/stackwalk.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/stackwalk.c b/src/stackwalk.c index cd4e46c204281..dcac2c4501682 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -1152,23 +1152,30 @@ JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT jl_ptls_t *allstates = jl_atomic_load_relaxed(&jl_all_tls_states); for (size_t i = 0; i < nthreads; i++) { jl_ptls_t ptls2 = allstates[i]; - if (ptls2 == NULL) + if (ptls2 == NULL) { continue; + } small_arraylist_t *live_tasks = &ptls2->heap.live_tasks; size_t n = mtarraylist_length(live_tasks); + int t_state = JL_TASK_STATE_DONE; jl_task_t *t = ptls2->root_task; - int t_state = jl_atomic_load_relaxed(&t->_state); + if (t != NULL) + t_state = jl_atomic_load_relaxed(&t->_state); jl_safe_printf("==== Thread %d created %zu live tasks\n", ptls2->tid + 1, n + (t_state != JL_TASK_STATE_DONE)); if (show_done || t_state != JL_TASK_STATE_DONE) { jl_safe_printf(" ---- Root task (%p)\n", ptls2->root_task); - jl_safe_printf(" (sticky: %d, started: %d, state: %d, tid: %d)\n", - t->sticky, t->started, t_state, - jl_atomic_load_relaxed(&t->tid) + 1); - if (t->stkbuf != NULL) - jlbacktracet(t); - else - jl_safe_printf(" no stack\n"); + if (t != NULL) { + jl_safe_printf(" (sticky: %d, started: %d, state: %d, tid: %d)\n", + t->sticky, t->started, t_state, + jl_atomic_load_relaxed(&t->tid) + 1); + if (t->stkbuf != NULL) { + jlbacktracet(t); + } + else { + jl_safe_printf(" no stack\n"); + } + } jl_safe_printf(" ---- End root task\n"); } From e8673bd7c2c11e12ce4c1a4ef8d82bb2c0b9f575 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 17 Nov 2023 13:58:01 -0500 Subject: [PATCH 30/38] codegen: ensure i1 bool is widened to i8 before storing (#52189) Teach value_to_pointer to convert primitive types to their stored representation first, to avoid exposing undef bits later (via memcpy). Take this opportunity to also generalizes the support for zext Bool to anywhere inside any struct for changing any bitwidth to a multiple of 8 bytes. This would change a vector like <2 x i4> from occupying i8 to i16 (c.f. LLVM's LangRef), if such an operation were expressible in Julia today. And take this opportunity to do a bit of code cleanup, now that codegen is better and using helpers from LLVM. Fixes #52127 (cherry picked from commit 9aa7980358349ee7017fa614525f571ffa92c55d) --- src/cgutils.cpp | 3 -- src/codegen.cpp | 27 +++------- src/intrinsics.cpp | 119 ++++++++++++++++++++++++++++++--------------- test/llvmcall2.jl | 9 ++++ 4 files changed, 98 insertions(+), 60 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 7b15ef366777c..bb96805bbb72b 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -760,9 +760,6 @@ static Type *_julia_struct_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt, lty = JuliaType::get_prjlvalue_ty(ctxt); isvector = false; } - else if (ty == (jl_value_t*)jl_bool_type) { - lty = getInt8Ty(ctxt); - } else if (jl_is_uniontype(ty)) { // pick an Integer type size such that alignment will generally be correct, // and always end with an Int8 (selector byte). diff --git a/src/codegen.cpp b/src/codegen.cpp index 0528180d1b50d..5c80a78cedd0c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1926,9 +1926,12 @@ static bool valid_as_globalinit(const Value *v) { return isa(v); } +static Value *zext_struct(jl_codectx_t &ctx, Value *V); + static inline jl_cgval_t value_to_pointer(jl_codectx_t &ctx, Value *v, jl_value_t *typ, Value *tindex) { Value *loc; + v = zext_struct(ctx, v); if (valid_as_globalinit(v)) { // llvm can't handle all the things that could be inside a ConstantExpr assert(jl_is_concrete_type(typ)); // not legal to have an unboxed abstract type loc = get_pointer_to_constant(ctx.emission_context, cast(v), Align(julia_alignment(typ)), "_j_const", *jl_Module); @@ -2054,17 +2057,6 @@ static void alloc_def_flag(jl_codectx_t &ctx, jl_varinfo_t& vi) // --- utilities --- -static Constant *undef_value_for_type(Type *T) { - auto tracked = CountTrackedPointers(T); - Constant *undef; - if (tracked.count) - // make sure gc pointers (including ptr_phi of union-split) are initialized to NULL - undef = Constant::getNullValue(T); - else - undef = UndefValue::get(T); - return undef; -} - static void CreateTrap(IRBuilder<> &irbuilder, bool create_new_block) { Function *f = irbuilder.GetInsertBlock()->getParent(); @@ -3346,7 +3338,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (f == jl_builtin_is && nargs == 2) { // emit comparison test Value *ans = emit_f_is(ctx, argv[1], argv[2]); - *ret = mark_julia_type(ctx, ctx.builder.CreateZExt(ans, getInt8Ty(ctx.builder.getContext())), false, jl_bool_type); + *ret = mark_julia_type(ctx, ans, false, jl_bool_type); return true; } @@ -3385,8 +3377,6 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (jl_is_type_type(ty.typ) && !jl_has_free_typevars(ty.typ)) { jl_value_t *tp0 = jl_tparam0(ty.typ); Value *isa_result = emit_isa(ctx, arg, tp0, NULL).first; - if (isa_result->getType() == getInt1Ty(ctx.builder.getContext())) - isa_result = ctx.builder.CreateZExt(isa_result, getInt8Ty(ctx.builder.getContext())); *ret = mark_julia_type(ctx, isa_result, false, jl_bool_type); return true; } @@ -5271,16 +5261,15 @@ static Value *emit_condition(jl_codectx_t &ctx, const jl_cgval_t &condV, const s emit_typecheck(ctx, condV, (jl_value_t*)jl_bool_type, msg); } if (isbool) { - Value *cond = emit_unbox(ctx, getInt8Ty(ctx.builder.getContext()), condV, (jl_value_t*)jl_bool_type); - assert(cond->getType() == getInt8Ty(ctx.builder.getContext())); - return ctx.builder.CreateXor(ctx.builder.CreateTrunc(cond, getInt1Ty(ctx.builder.getContext())), ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 1)); + Value *cond = emit_unbox(ctx, getInt1Ty(ctx.builder.getContext()), condV, (jl_value_t*)jl_bool_type); + return ctx.builder.CreateNot(cond); } if (condV.isboxed) { return ctx.builder.CreateICmpEQ(boxed(ctx, condV), track_pjlvalue(ctx, literal_pointer_val(ctx, jl_false))); } - // not a boolean - return ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0); // TODO: replace with Undef + // not a boolean (unreachable dead code) + return UndefValue::get(getInt1Ty(ctx.builder.getContext())); } static Value *emit_condition(jl_codectx_t &ctx, jl_value_t *cond, const std::string &msg) diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 810982370de19..9c163458f22d2 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -174,12 +174,7 @@ static Type *INTT(Type *t, const DataLayout &DL) static Value *uint_cnvt(jl_codectx_t &ctx, Type *to, Value *x) { - Type *t = x->getType(); - if (t == to) - return x; - if (to->getPrimitiveSizeInBits() < x->getType()->getPrimitiveSizeInBits()) - return ctx.builder.CreateTrunc(x, to); - return ctx.builder.CreateZExt(x, to); + return ctx.builder.CreateZExtOrTrunc(x, to); } static Constant *julia_const_to_llvm(jl_codectx_t &ctx, const void *ptr, jl_datatype_t *bt) @@ -318,25 +313,90 @@ static Constant *julia_const_to_llvm(jl_codectx_t &ctx, jl_value_t *e) return julia_const_to_llvm(ctx, e, (jl_datatype_t*)bt); } +static Constant *undef_value_for_type(Type *T) { + auto tracked = CountTrackedPointers(T); + Constant *undef; + if (tracked.count) + // make sure gc pointers (including ptr_phi of union-split) are initialized to NULL + undef = Constant::getNullValue(T); + else + undef = UndefValue::get(T); + return undef; +} + +// rebuild a struct type with any i1 Bool (e.g. the llvmcall type) widened to i8 (the native size for memcpy) +static Type *zext_struct_type(Type *T) +{ + if (auto *AT = dyn_cast(T)) { + return ArrayType::get(AT->getElementType(), AT->getNumElements()); + } + else if (auto *ST = dyn_cast(T)) { + SmallVector Elements(ST->element_begin(), ST->element_end()); + for (size_t i = 0; i < Elements.size(); i++) { + Elements[i] = zext_struct_type(Elements[i]); + } + return StructType::get(ST->getContext(), Elements, ST->isPacked()); + } + else if (auto *VT = dyn_cast(T)) { + return VectorType::get(zext_struct_type(VT->getElementType()), VT); + } + else if (auto *IT = dyn_cast(T)) { + unsigned BitWidth = IT->getBitWidth(); + if (alignTo(BitWidth, 8) != BitWidth) + return IntegerType::get(IT->getContext(), alignTo(BitWidth, 8)); + } + return T; +} + +// rebuild a struct with any i1 Bool (e.g. the llvmcall type) widened to i8 (the native size for memcpy) +static Value *zext_struct_helper(jl_codectx_t &ctx, Value *V, Type *T2) +{ + Type *T = V->getType(); + if (T == T2) + return V; + if (auto *AT = dyn_cast(T2)) { + Value *V2 = undef_value_for_type(AT); + for (size_t i = 0; i < AT->getNumElements(); i++) { + Value *E = zext_struct_helper(ctx, ctx.builder.CreateExtractValue(V, i), AT->getElementType()); + V2 = ctx.builder.CreateInsertValue(V2, E, i); + } + return V2; + } + else if (auto *ST = dyn_cast(T2)) { + Value *V2 = undef_value_for_type(ST); + for (size_t i = 0; i < ST->getNumElements(); i++) { + Value *E = zext_struct_helper(ctx, ctx.builder.CreateExtractValue(V, i), ST->getElementType(i)); + V2 = ctx.builder.CreateInsertValue(V2, E, i); + } + return V2; + } + else if (T2->isIntegerTy() || T2->isVectorTy()) { + return ctx.builder.CreateZExt(V, T2); + } + return V; +} + +static Value *zext_struct(jl_codectx_t &ctx, Value *V) +{ + return zext_struct_helper(ctx, V, zext_struct_type(V->getType())); +} + static Value *emit_unboxed_coercion(jl_codectx_t &ctx, Type *to, Value *unboxed) { + if (unboxed->getType() == to) + return unboxed; + if (CastInst::castIsValid(Instruction::Trunc, unboxed, to)) + return ctx.builder.CreateTrunc(unboxed, to); + unboxed = zext_struct(ctx, unboxed); Type *ty = unboxed->getType(); if (ty == to) return unboxed; bool frompointer = ty->isPointerTy(); bool topointer = to->isPointerTy(); const DataLayout &DL = jl_Module->getDataLayout(); - if (ty->isIntegerTy(1) && to->isIntegerTy(8)) { - // bools may be stored internally as int8 - unboxed = ctx.builder.CreateZExt(unboxed, to); - } - else if (ty->isIntegerTy(8) && to->isIntegerTy(1)) { - // bools may be stored internally as int8 - unboxed = ctx.builder.CreateTrunc(unboxed, to); - } - else if (ty->isVoidTy() || DL.getTypeSizeInBits(ty) != DL.getTypeSizeInBits(to)) { + if (ty->isVoidTy() || DL.getTypeSizeInBits(ty) != DL.getTypeSizeInBits(to)) { // this can happen in dead code - //emit_unreachable(ctx); + CreateTrap(ctx.builder); return UndefValue::get(to); } if (frompointer && topointer) { @@ -381,7 +441,7 @@ static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_va if (type_is_ghost(to)) { return NULL; } - //emit_unreachable(ctx); + CreateTrap(ctx.builder); return UndefValue::get(to); // type mismatch error } @@ -447,17 +507,9 @@ static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dest return; } - Value *unboxed = nullptr; - if (!x.ispointer()) { // already unboxed, but sometimes need conversion - unboxed = x.V; - assert(unboxed); - } - - // bools stored as int8, but can be narrowed to int1 often - if (x.typ == (jl_value_t*)jl_bool_type) - unboxed = emit_unbox(ctx, getInt8Ty(ctx.builder.getContext()), x, (jl_value_t*)jl_bool_type); - - if (unboxed) { + if (!x.ispointer()) { // already unboxed, but sometimes need conversion (e.g. f32 -> i32) + assert(x.V); + Value *unboxed = zext_struct(ctx, x.V); Type *dest_ty = unboxed->getType()->getPointerTo(); if (dest->getType() != dest_ty) dest = emit_bitcast(ctx, dest, dest_ty); @@ -1455,12 +1507,7 @@ static Value *emit_untyped_intrinsic(jl_codectx_t &ctx, intrinsic f, Value **arg Intrinsic::smul_with_overflow : Intrinsic::umul_with_overflow))))); FunctionCallee intr = Intrinsic::getDeclaration(jl_Module, intr_id, makeArrayRef(t)); - Value *res = ctx.builder.CreateCall(intr, {x, y}); - Value *val = ctx.builder.CreateExtractValue(res, ArrayRef(0)); - setName(ctx.emission_context, val, "checked"); - Value *obit = ctx.builder.CreateExtractValue(res, ArrayRef(1)); - setName(ctx.emission_context, obit, "overflow"); - Value *obyte = ctx.builder.CreateZExt(obit, getInt8Ty(ctx.builder.getContext())); + Value *tupval = ctx.builder.CreateCall(intr, {x, y}); jl_value_t *params[2]; params[0] = xtyp; @@ -1468,10 +1515,6 @@ static Value *emit_untyped_intrinsic(jl_codectx_t &ctx, intrinsic f, Value **arg jl_datatype_t *tuptyp = (jl_datatype_t*)jl_apply_tuple_type_v(params, 2); *newtyp = tuptyp; - Value *tupval; - tupval = UndefValue::get(julia_type_to_llvm(ctx, (jl_value_t*)tuptyp)); - tupval = ctx.builder.CreateInsertValue(tupval, val, ArrayRef(0)); - tupval = ctx.builder.CreateInsertValue(tupval, obyte, ArrayRef(1)); return tupval; } diff --git a/test/llvmcall2.jl b/test/llvmcall2.jl index 07b27fc407433..e3e89bb916f2d 100644 --- a/test/llvmcall2.jl +++ b/test/llvmcall2.jl @@ -73,3 +73,12 @@ end jl_str = unsafe_string(str) @test length(jl_str) > 4 end + + +# boolean structs +const NT4I = NTuple{4, VecElement{Int}} +const NT4B = NTuple{4, VecElement{Bool}} +f_nt4b(x, y) = ccall("llvm.sadd.with.overflow", llvmcall, Pair{NT4B, NT4B}, (NT4B, NT4B), x, y) +f_nt4i(x, y) = ccall("llvm.sadd.with.overflow", llvmcall, Pair{NT4I, NT4B}, (NT4I, NT4I), x, y) +@test f_nt4b((false, true, false, true), (false, false, true, true)) === (NT4B((false, true, true, false)) => NT4B((false, false, false, true))) +@test f_nt4i((typemin(Int), 0, typemax(Int), typemax(Int)), (-1, typemax(Int),-1, 1)) === (NT4I((typemax(Int), typemax(Int), typemax(Int)-1, typemin(Int))) => NT4B((true, false, false, true))) From db564883ef7b252dc4129d3bc46c07cc9bd9df58 Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Sat, 26 Aug 2023 14:03:21 -0300 Subject: [PATCH 31/38] refactor GC scanning code to reflect jl_binding_t are now first class (#51035) Removes some redundant code. Credits to Eduardo for pointing this out in the GC meeting. (cherry picked from commit 5bc558c9cde5db6fa89b7b4d1d56bf3f8dbe8454) --- src/gc.c | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/gc.c b/src/gc.c index 72d878864352b..98dcdeb631784 100644 --- a/src/gc.c +++ b/src/gc.c @@ -2278,19 +2278,10 @@ STATIC_INLINE void gc_mark_excstack(jl_ptls_t ptls, jl_excstack_t *excstack, siz } // Mark module binding -STATIC_INLINE void gc_mark_module_binding(jl_ptls_t ptls, jl_module_t *mb_parent, jl_binding_t **mb_begin, - jl_binding_t **mb_end, uintptr_t nptr, +STATIC_INLINE void gc_mark_module_binding(jl_ptls_t ptls, jl_module_t *mb_parent, uintptr_t nptr, uint8_t bits) JL_NOTSAFEPOINT { jl_gc_markqueue_t *mq = &ptls->mark_queue; - for (; mb_begin < mb_end; mb_begin++) { - jl_binding_t *b = *mb_begin; - if (b == (jl_binding_t *)jl_nothing) - continue; - verify_parent1("module", mb_parent, mb_begin, "binding_buff"); - gc_assert_parent_validity((jl_value_t *)mb_parent, (jl_value_t *)b); - gc_try_claim_and_push(mq, b, &nptr); - } jl_value_t *bindings = (jl_value_t *)jl_atomic_load_relaxed(&mb_parent->bindings); gc_assert_parent_validity((jl_value_t *)mb_parent, bindings); gc_try_claim_and_push(mq, bindings, &nptr); @@ -2422,13 +2413,8 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ else if (foreign_alloc) objprofile_count(jl_module_type, bits == GC_OLD_MARKED, sizeof(jl_module_t)); jl_module_t *mb_parent = (jl_module_t *)new_obj; - jl_svec_t *bindings = jl_atomic_load_relaxed(&mb_parent->bindings); - jl_binding_t **table = (jl_binding_t**)jl_svec_data(bindings); - size_t bsize = jl_svec_len(bindings); - uintptr_t nptr = ((bsize + mb_parent->usings.len + 1) << 2) | (bits & GC_OLD); - jl_binding_t **mb_begin = table + 1; - jl_binding_t **mb_end = table + bsize; - gc_mark_module_binding(ptls, mb_parent, mb_begin, mb_end, nptr, bits); + uintptr_t nptr = ((mb_parent->usings.len + 1) << 2) | (bits & GC_OLD); + gc_mark_module_binding(ptls, mb_parent, nptr, bits); } else if (vtag == jl_task_tag << 4) { if (update_meta) From e77afbfb2914092d5678b7859c1d19c7d8553b21 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Fri, 24 Nov 2023 10:51:32 -0300 Subject: [PATCH 32/38] Fix multiversioning issues caused by the parallel llvm work (#52194) So after struggling with this for a long while it seems there were two different issues. The first one we lacked coverage over, but the other was a very subtle issue when we sorted the fptrs. ~I still need to add test that does multiversioning where we call between multiversioned functions~ Fixes https://github.com/JuliaLang/julia/issues/52079 (cherry picked from commit a386cd101ca8ff1775f38e2ec2fbeb7076a39c4f) --- src/llvm-multiversioning.cpp | 4 +- src/processor.cpp | 5 +- test/compiler/codegen.jl | 13 +++ test/llvmpasses/multiversioning-x86.ll | 132 +++++++++++++++++++++++++ 4 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 test/llvmpasses/multiversioning-x86.ll diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index e4ebbe9d3838a..accdef0aaaa83 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -531,7 +531,7 @@ void CloneCtx::clone_decls() new_F->setVisibility(F->getVisibility()); new_F->setDSOLocal(true); auto base_func = F; - if (specs[i].flags & JL_TARGET_CLONE_ALL) + if (!(specs[i].flags & JL_TARGET_CLONE_ALL)) base_func = static_cast(linearized[specs[i].base])->base_func(F); (*linearized[i]->vmap)[base_func] = new_F; } @@ -586,7 +586,7 @@ void CloneCtx::clone_bodies() } for (auto &target : groups[i].clones) { prepare_vmap(*target.vmap); - auto target_F = cast_or_null(map_get(*target.vmap, F)); + auto target_F = cast_or_null(map_get(*target.vmap, group_F)); if (target_F) { if (!F->isDeclaration()) { clone_function(group_F, target_F, *target.vmap); diff --git a/src/processor.cpp b/src/processor.cpp index 587ff300c8d7e..c5b35f57799e8 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -779,7 +779,10 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) if (!clones.empty()) { assert(!fvars.empty()); - std::sort(clones.begin(), clones.end()); + std::sort(clones.begin(), clones.end(), + [](const std::pair &a, const std::pair &b) { + return (a.first & jl_sysimg_val_mask) < (b.first & jl_sysimg_val_mask); + }); auto clone_offsets = (int32_t *) malloc(sizeof(int32_t) * clones.size()); auto clone_idxs = (uint32_t *) malloc(sizeof(uint32_t) * clones.size()); for (size_t i = 0; i < clones.size(); i++) { diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 9b34ae7321ff6..1d28b818340a4 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -848,3 +848,16 @@ let res = @timed a50317[:b] @test res.bytes == 0 return res end + +# Very specific test for multiversioning +if Sys.ARCH === :x86_64 + foo52079() = Core.Intrinsics.have_fma(Float64) + if foo52079() == true + let io = IOBuffer() + code_native(io,^,(Float64,Float64), dump_module=false) + str = String(take!(io)) + @test !occursin("fma_emulated", str) + @test occursin("vfmadd", str) + end + end +end diff --git a/test/llvmpasses/multiversioning-x86.ll b/test/llvmpasses/multiversioning-x86.ll new file mode 100644 index 0000000000000..ca43462e1eda9 --- /dev/null +++ b/test/llvmpasses/multiversioning-x86.ll @@ -0,0 +1,132 @@ +; This file is a part of Julia. License is MIT: https://julialang.org/license + +; RUN: opt -enable-new-pm=1 --opaque-pointers=0 --load-pass-plugin=libjulia-codegen%shlibext -passes='JuliaMultiVersioning,CPUFeatures' -S %s | FileCheck %s + +; RUN: opt -enable-new-pm=1 --opaque-pointers=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='JuliaMultiVersioning,CPUFeatures' -S %s | FileCheck %s + +; COM: This test checks that multiversioning actually happens from start to finish +; COM: We need the fvars for a proper test + + + +; CHECK: @jl_fvar_idxs = hidden constant [5 x i32] [i32 0, i32 1, i32 2, i32 3, i32 4], align 16 +; CHECK: @jl_gvar_idxs = hidden constant [0 x i32] zeroinitializer, align 16 +; TYPED: @simd_test.reloc_slot = hidden global i32 (<4 x i32>)* null +; OPAQUE: @simd_test.reloc_slot = hidden global ptr null +; TYPED: @jl_fvar_offsets = hidden constant [6 x i32] [i32 5, i32 0, i32 trunc (i64 sub (i64 ptrtoint (float (float, float)* @fastmath_test to i64), i64 ptrtoint (i32 (i32)* @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (i32 (i32)* @loop_test to i64), i64 ptrtoint (i32 (i32)* @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (i32 (<4 x i32>)* @simd_test to i64), i64 ptrtoint (i32 (i32)* @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (i32 (<4 x i32>)* @simd_test_call to i64), i64 ptrtoint (i32 (i32)* @boring to i64)) to i32)] +; OPAQUE: @jl_fvar_offsets = hidden constant [6 x i32] [i32 5, i32 0, i32 trunc (i64 sub (i64 ptrtoint (ptr @fastmath_test to i64), i64 ptrtoint (ptr @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr @loop_test to i64), i64 ptrtoint (ptr @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr @simd_test to i64), i64 ptrtoint (ptr @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr @simd_test_call to i64), i64 ptrtoint (ptr @boring to i64)) to i32)] +; CHECK: @jl_gvar_base = hidden constant i64 0 +; CHECK: @jl_gvar_offsets = hidden constant [1 x i32] zeroinitializer +; TYPED: @jl_clone_slots = hidden constant [3 x i32] [i32 1, i32 3, i32 trunc (i64 sub (i64 ptrtoint (i32 (<4 x i32>)** @simd_test.reloc_slot to i64), i64 ptrtoint (i64* @jl_gvar_base to i64)) to i32)] +; OPAQUE: @jl_clone_slots = hidden constant [3 x i32] [i32 1, i32 3, i32 trunc (i64 sub (i64 ptrtoint (ptr @simd_test.reloc_slot to i64), i64 ptrtoint (ptr @jl_gvar_base to i64)) to i32)] +; CHECK: @jl_clone_idxs = hidden constant [10 x i32] [i32 -2147483647, i32 3, i32 -2147483647, i32 3, i32 4, i32 1, i32 1, i32 2, i32 -2147483645, i32 4] +; TYPED: @jl_clone_offsets = hidden constant [9 x i32] [i32 trunc (i64 sub (i64 ptrtoint (i32 (i32)* @boring.1 to i64), i64 ptrtoint (i32 (i32)* @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (float (float, float)* @fastmath_test.1 to i64), i64 ptrtoint (i32 (i32)* @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (i32 (i32)* @loop_test.1 to i64), i64 ptrtoint (i32 (i32)* @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (i32 (<4 x i32>)* @simd_test.1 to i64), i64 ptrtoint (i32 (i32)* @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (i32 (<4 x i32>)* @simd_test_call.1 to i64), i64 ptrtoint (i32 (i32)* @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (float (float, float)* @fastmath_test.2 to i64), i64 ptrtoint (i32 (i32)* @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (i32 (i32)* @loop_test.2 to i64), i64 ptrtoint (i32 (i32)* @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (i32 (<4 x i32>)* @simd_test.2 to i64), i64 ptrtoint (i32 (i32)* @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (i32 (<4 x i32>)* @simd_test_call.2 to i64), i64 ptrtoint (i32 (i32)* @boring to i64)) to i32)] +; OPAQUE: @jl_clone_offsets = hidden constant [9 x i32] [i32 trunc (i64 sub (i64 ptrtoint (ptr @boring.1 to i64), i64 ptrtoint (ptr @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr @fastmath_test.1 to i64), i64 ptrtoint (ptr @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr @loop_test.1 to i64), i64 ptrtoint (ptr @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr @simd_test.1 to i64), i64 ptrtoint (ptr @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr @simd_test_call.1 to i64), i64 ptrtoint (ptr @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr @fastmath_test.2 to i64), i64 ptrtoint (ptr @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr @loop_test.2 to i64), i64 ptrtoint (ptr @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr @simd_test.2 to i64), i64 ptrtoint (ptr @boring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr @simd_test_call.2 to i64), i64 ptrtoint (ptr @boring to i64)) to i32)] +; TYPED: @jl_fvar_base = hidden alias i64, bitcast (i32 (i32)* @boring to i64*) +; OPAQUE: @jl_fvar_base = hidden alias i64, ptr @boring + + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128-ni:10:11:12:13" +target triple = "x86_64-linux-gnu" + +@jl_fvars = global [5 x i64*] [i64* bitcast (i32 (i32)* @boring to i64*), + i64* bitcast (float (float, float)* @fastmath_test to i64*), + i64* bitcast (i32 (i32)* @loop_test to i64*), + i64* bitcast (i32 (<4 x i32>)* @simd_test to i64*), + i64* bitcast (i32 (<4 x i32>)* @simd_test_call to i64*) + ], align 16 +@jl_gvars = global [0 x i64*] zeroinitializer, align 16 +@jl_fvar_idxs = hidden constant [5 x i32] [i32 0, i32 1, i32 2, i32 3, i32 4], align 16 +@jl_gvar_idxs = hidden constant [0 x i32] zeroinitializer, align 16 + +declare i1 @julia.cpu.have_fma.f32() + +; CHECK: @boring{{.*}}#[[BORING_BASE:[0-9]+]] +define noundef i32 @boring(i32 noundef %0) { + ret i32 %0 +} + +; CHECK: @fastmath_test{{.*}}#[[NOT_BORING_BASE:[0-9]+]] +; CHECK: %3 = sitofp i1 false to float +define noundef float @fastmath_test(float noundef %0, float noundef %1) { + %3 = call i1 @julia.cpu.have_fma.f32() + %4 = sitofp i1 %3 to float + %5 = fadd fast float %0, %4 + ret float %5 +} + +; CHECK: @loop_test{{.*}}#[[NOT_BORING_BASE:[0-9]+]] +define noundef i32 @loop_test(i32 noundef %0) { + %2 = icmp sgt i32 %0, 0 + br i1 %2, label %5, label %3 + +3: ; preds = %5, %1 + %4 = phi i32 [ 0, %1 ], [ %9, %5 ] + ret i32 %4 + +5: ; preds = %1, %5 + %6 = phi i32 [ %10, %5 ], [ 0, %1 ] + %7 = phi i32 [ %9, %5 ], [ 0, %1 ] + %8 = lshr i32 %6, 1 + %9 = add nuw nsw i32 %8, %7 + %10 = add nuw nsw i32 %6, 1 + %11 = icmp eq i32 %10, %0 + br i1 %11, label %3, label %5;, !llvm.loop - +} + +; CHECK: @simd_test{{.*}}#[[SIMD_BASE_RELOC:[0-9]+]] +define noundef i32 @simd_test(<4 x i32> noundef %0) { + %2 = extractelement <4 x i32> %0, i64 0 + ret i32 %2 +} + +; CHECK: @simd_test_call{{.*}}#[[NOT_BORING_BASE:[0-9]+]] +define noundef i32 @simd_test_call(<4 x i32> noundef %0) { + %2 = call noundef i32 @simd_test(<4 x i32> noundef %0) + ret i32 %2 +} + +; CHECK: @boring{{.*}}#[[BORING_CLONE:[0-9]+]] + +; CHECK: @fastmath_test{{.*}}#[[NOT_BORING_CLONE1:[0-9]+]] +; CHECK: %3 = sitofp i1 false to float + +; CHECK: @fastmath_test{{.*}}#[[NOT_BORING_CLONE2:[0-9]+]] +; CHECK: %3 = sitofp i1 true to float + +; CHECK: @loop_test{{.*}}#[[NOT_BORING_CLONE1:[0-9]+]] + +; CHECK: @loop_test{{.*}}#[[NOT_BORING_CLONE2:[0-9]+]] + +; CHECK: @simd_test{{.*}}#[[SIMD_CLONE1:[0-9]+]] + +; CHECK: @simd_test{{.*}}#[[SIMD_CLONE2:[0-9]+]] + +; CHECK: @simd_test_call{{.*}}#[[NOT_BORING_CLONE1:[0-9]+]] +; TYPED: %2 = load i32 (<4 x i32>)*, i32 (<4 x i32>)** @simd_test.reloc_slot, align 8, !tbaa !8, !invariant.load !12 +; OPAQUE: %2 = load ptr, ptr @simd_test.reloc_slot, align 8, !tbaa !8, !invariant.load !12 +; CHECK: %3 = call noundef i32 %2(<4 x i32> noundef %0) + +; CHECK: @simd_test_call{{.*}}#[[NOT_BORING_CLONE2:[0-9]+]] +; CHECK: %2 = call noundef i32 @simd_test.2(<4 x i32> noundef %0) + +; CHECK-DAG: attributes #[[BORING_BASE]] = { "julia.mv.clone"="0" "julia.mv.clones"="2" "julia.mv.fvar" "target-cpu"="x86-64" "target-features"="+cx16,-sse3,-pclmul,-ssse3,-fma,-sse4.1,-sse4.2,-movbe,-popcnt,-aes,-xsave,-avx,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sahf,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } +; CHECK-DAG: attributes #[[NOT_BORING_BASE]] = { "julia.mv.clone"="0" "julia.mv.clones"="6" "julia.mv.fvar" "target-cpu"="x86-64" "target-features"="+cx16,-sse3,-pclmul,-ssse3,-fma,-sse4.1,-sse4.2,-movbe,-popcnt,-aes,-xsave,-avx,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sahf,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } +; CHECK-DAG: attributes #[[SIMD_BASE_RELOC]] = { "julia.mv.clone"="0" "julia.mv.clones"="6" "julia.mv.reloc" "target-cpu"="x86-64" "target-features"="+cx16,-sse3,-pclmul,-ssse3,-fma,-sse4.1,-sse4.2,-movbe,-popcnt,-aes,-xsave,-avx,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sahf,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } +; CHECK-DAG: attributes #[[BORING_CLONE]] = { "julia.mv.clone"="1" "julia.mv.clones"="2" "julia.mv.fvar" "target-cpu"="sandybridge" "target-features"="+sahf,+avx,+xsave,+popcnt,+sse4.2,+sse4.1,+cx16,+ssse3,+pclmul,+sse3,-fma,-movbe,-aes,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } +; CHECK-DAG: attributes #[[NOT_BORING_CLONE1]] = { "julia.mv.clone"="1" "julia.mv.clones"="6" "julia.mv.fvar" "target-cpu"="sandybridge" "target-features"="+sahf,+avx,+xsave,+popcnt,+sse4.2,+sse4.1,+cx16,+ssse3,+pclmul,+sse3,-fma,-movbe,-aes,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } +; CHECK-DAG: attributes #[[NOT_BORING_CLONE2]] = { "julia.mv.clone"="2" "julia.mv.clones"="6" "julia.mv.fvar" "target-cpu"="haswell" "target-features"="+lzcnt,+sahf,+bmi2,+avx2,+bmi,+fsgsbase,+f16c,+avx,+xsave,+popcnt,+movbe,+sse4.2,+sse4.1,+cx16,+fma,+ssse3,+pclmul,+sse3,-aes,-rdrnd,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } +; CHECK-DAG: attributes #[[SIMD_CLONE1]] = { "julia.mv.clone"="1" "julia.mv.clones"="6" "julia.mv.reloc" "target-cpu"="sandybridge" "target-features"="+sahf,+avx,+xsave,+popcnt,+sse4.2,+sse4.1,+cx16,+ssse3,+pclmul,+sse3,-fma,-movbe,-aes,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } +; CHECK-DAG: attributes #[[SIMD_CLONE2]] = { "julia.mv.clone"="2" "julia.mv.clones"="6" "julia.mv.reloc" "target-cpu"="haswell" "target-features"="+lzcnt,+sahf,+bmi2,+avx2,+bmi,+fsgsbase,+f16c,+avx,+xsave,+popcnt,+movbe,+sse4.2,+sse4.1,+cx16,+fma,+ssse3,+pclmul,+sse3,-aes,-rdrnd,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8" } + + +!llvm.module.flags = !{!0, !2} + + +!0 = !{i32 1, !"julia.mv.enable", i32 1} +!1 = !{!1} +!2 = !{i32 1, !"julia.mv.specs", !3} +!3 = !{!4, !5, !6} +!4 = !{!"x86-64", !"+cx16,-sse3,-pclmul,-ssse3,-fma,-sse4.1,-sse4.2,-movbe,-popcnt,-aes,-xsave,-avx,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sahf,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8", i32 0, i32 0} +!5 = !{!"sandybridge", !"+sahf,+avx,+xsave,+popcnt,+sse4.2,+sse4.1,+cx16,+ssse3,+pclmul,+sse3,-fma,-movbe,-aes,-f16c,-rdrnd,-fsgsbase,-bmi,-avx2,-bmi2,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-lzcnt,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8", i32 0, i32 2} +!6 = !{!"haswell", !"+lzcnt,+sahf,+bmi2,+avx2,+bmi,+fsgsbase,+f16c,+avx,+xsave,+popcnt,+movbe,+sse4.2,+sse4.1,+cx16,+fma,+ssse3,+pclmul,+sse3,-aes,-rdrnd,-rtm,-avx512f,-avx512dq,-rdseed,-adx,-avx512ifma,-clflushopt,-clwb,-avx512pf,-avx512er,-avx512cd,-sha,-avx512bw,-avx512vl,-prefetchwt1,-avx512vbmi,-pku,-waitpkg,-avx512vbmi2,-shstk,-gfni,-vaes,-vpclmulqdq,-avx512vnni,-avx512bitalg,-avx512vpopcntdq,-rdpid,-cldemote,-movdiri,-movdir64b,-enqcmd,-uintr,-avx512vp2intersect,-serialize,-tsxldtrk,-pconfig,-amx-bf16,-avx512fp16,-amx-tile,-amx-int8,-sse4a,-prfchw,-xop,-fma4,-tbm,-mwaitx,-xsaveopt,-xsavec,-xsaves,-clzero,-wbnoinvd,-avxvnni,-avx512bf16,-ptwrite,+sse2,+mmx,+fxsr,+64bit,+cx8", i32 1, i32 284} From 1e3842c6fc819c3d76d5b32dc628e414477f80c9 Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Mon, 27 Nov 2023 15:52:54 +0100 Subject: [PATCH 33/38] bump SparseArrays to latest v1.10 --- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - stdlib/SparseArrays.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/SparseArrays-279b363ca8d3129d4742903d37c8b11545fa08a2.tar.gz/md5 create mode 100644 deps/checksums/SparseArrays-279b363ca8d3129d4742903d37c8b11545fa08a2.tar.gz/sha512 delete mode 100644 deps/checksums/SparseArrays-fa6269b0dc5660cbaddb9365c7d89b206fc55992.tar.gz/md5 delete mode 100644 deps/checksums/SparseArrays-fa6269b0dc5660cbaddb9365c7d89b206fc55992.tar.gz/sha512 diff --git a/deps/checksums/SparseArrays-279b363ca8d3129d4742903d37c8b11545fa08a2.tar.gz/md5 b/deps/checksums/SparseArrays-279b363ca8d3129d4742903d37c8b11545fa08a2.tar.gz/md5 new file mode 100644 index 0000000000000..dbb5746626e3e --- /dev/null +++ b/deps/checksums/SparseArrays-279b363ca8d3129d4742903d37c8b11545fa08a2.tar.gz/md5 @@ -0,0 +1 @@ +ab41f62c0b16f5464c548e648f05cb94 diff --git a/deps/checksums/SparseArrays-279b363ca8d3129d4742903d37c8b11545fa08a2.tar.gz/sha512 b/deps/checksums/SparseArrays-279b363ca8d3129d4742903d37c8b11545fa08a2.tar.gz/sha512 new file mode 100644 index 0000000000000..1c67c8110b375 --- /dev/null +++ b/deps/checksums/SparseArrays-279b363ca8d3129d4742903d37c8b11545fa08a2.tar.gz/sha512 @@ -0,0 +1 @@ +9fa813d057415c3eba1941dd15634a5d4798015b6b72e7524112aeb57522ec54aee653fb1531574441fea08cd472c76f6964e1a0d75fa48a116000196a061d17 diff --git a/deps/checksums/SparseArrays-fa6269b0dc5660cbaddb9365c7d89b206fc55992.tar.gz/md5 b/deps/checksums/SparseArrays-fa6269b0dc5660cbaddb9365c7d89b206fc55992.tar.gz/md5 deleted file mode 100644 index afeeed49f2e21..0000000000000 --- a/deps/checksums/SparseArrays-fa6269b0dc5660cbaddb9365c7d89b206fc55992.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -85c5dcb49f11bce2d6bb75ef620ea892 diff --git a/deps/checksums/SparseArrays-fa6269b0dc5660cbaddb9365c7d89b206fc55992.tar.gz/sha512 b/deps/checksums/SparseArrays-fa6269b0dc5660cbaddb9365c7d89b206fc55992.tar.gz/sha512 deleted file mode 100644 index 7cab3aeec5ca9..0000000000000 --- a/deps/checksums/SparseArrays-fa6269b0dc5660cbaddb9365c7d89b206fc55992.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -a1810991895650263c33c3e9c2b3089c66948b86b4de16c4c06ed49841cf5500814641576a6b735d5cf3841991684e908c4828a56ee218be216424e6aa9d5f43 diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version index b30b1002d5285..8ff55582ca9df 100644 --- a/stdlib/SparseArrays.version +++ b/stdlib/SparseArrays.version @@ -1,4 +1,4 @@ SPARSEARRAYS_BRANCH = main -SPARSEARRAYS_SHA1 = fa6269b0dc5660cbaddb9365c7d89b206fc55992 +SPARSEARRAYS_SHA1 = 279b363ca8d3129d4742903d37c8b11545fa08a2 SPARSEARRAYS_GIT_URL := https://github.com/JuliaSparse/SparseArrays.jl.git SPARSEARRAYS_TAR_URL = https://api.github.com/repos/JuliaSparse/SparseArrays.jl/tarball/$1 From 46617e502bdc9c04d3adc2e1ccaae78aeaa1dbe3 Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Mon, 27 Nov 2023 15:54:11 +0100 Subject: [PATCH 34/38] bump Pkg to latest v1.10 --- .../Pkg-563a3387acddad6c427b2f675aa70531c4324cc2.tar.gz/md5 | 1 + .../Pkg-563a3387acddad6c427b2f675aa70531c4324cc2.tar.gz/sha512 | 1 + .../Pkg-e63c601bd3a00a8e6062d926673337c8a659542d.tar.gz/md5 | 1 - .../Pkg-e63c601bd3a00a8e6062d926673337c8a659542d.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-563a3387acddad6c427b2f675aa70531c4324cc2.tar.gz/md5 create mode 100644 deps/checksums/Pkg-563a3387acddad6c427b2f675aa70531c4324cc2.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-e63c601bd3a00a8e6062d926673337c8a659542d.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-e63c601bd3a00a8e6062d926673337c8a659542d.tar.gz/sha512 diff --git a/deps/checksums/Pkg-563a3387acddad6c427b2f675aa70531c4324cc2.tar.gz/md5 b/deps/checksums/Pkg-563a3387acddad6c427b2f675aa70531c4324cc2.tar.gz/md5 new file mode 100644 index 0000000000000..985e7ef06f2a5 --- /dev/null +++ b/deps/checksums/Pkg-563a3387acddad6c427b2f675aa70531c4324cc2.tar.gz/md5 @@ -0,0 +1 @@ +769c5379f1ded5529d82d83e092e954c diff --git a/deps/checksums/Pkg-563a3387acddad6c427b2f675aa70531c4324cc2.tar.gz/sha512 b/deps/checksums/Pkg-563a3387acddad6c427b2f675aa70531c4324cc2.tar.gz/sha512 new file mode 100644 index 0000000000000..d5f705de6d9d9 --- /dev/null +++ b/deps/checksums/Pkg-563a3387acddad6c427b2f675aa70531c4324cc2.tar.gz/sha512 @@ -0,0 +1 @@ +585760a1c104391bb08f920b6944b88762e942388fd6ca6bf3bfa6a0bf050f99d304c21165a3b82c96dea5a6711af3ddf0cf9b83bf5fd5f015b26f412b24015a diff --git a/deps/checksums/Pkg-e63c601bd3a00a8e6062d926673337c8a659542d.tar.gz/md5 b/deps/checksums/Pkg-e63c601bd3a00a8e6062d926673337c8a659542d.tar.gz/md5 deleted file mode 100644 index bddd4fc615336..0000000000000 --- a/deps/checksums/Pkg-e63c601bd3a00a8e6062d926673337c8a659542d.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -a68258944ba3c7b8a31864ad5dc6e320 diff --git a/deps/checksums/Pkg-e63c601bd3a00a8e6062d926673337c8a659542d.tar.gz/sha512 b/deps/checksums/Pkg-e63c601bd3a00a8e6062d926673337c8a659542d.tar.gz/sha512 deleted file mode 100644 index 0f89a4347a7d6..0000000000000 --- a/deps/checksums/Pkg-e63c601bd3a00a8e6062d926673337c8a659542d.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -e7ea74323f873e6ff0b92b19d368a16df768f21474222ef38918d36d827c92a5597e7fa243e9ef985b02f3bcf65d41904cfeeae9aca64612fdfbb9483c3cbd12 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 7c2d80c35fe19..b43eb592268ab 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.10 -PKG_SHA1 = e63c601bd3a00a8e6062d926673337c8a659542d +PKG_SHA1 = 563a3387acddad6c427b2f675aa70531c4324cc2 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 0e96c9cf193615d303a6968089025217d3351b60 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Thu, 7 Sep 2023 21:36:30 +0200 Subject: [PATCH 35/38] Fix getfield codegen for tuple inputs and unknown symbol fields. (#51234) (cherry picked from commit eab8d6b96b05f7e84103f66a902e4ee7ad395b48) --- src/codegen.cpp | 2 +- test/compiler/codegen.jl | 15 +++------------ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 5c80a78cedd0c..53a278d42d102 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3830,7 +3830,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } else if (fld.typ == (jl_value_t*)jl_symbol_type) { // Known type but unknown symbol if (jl_is_datatype(utt) && (utt != jl_module_type) && jl_struct_try_layout(utt)) { - if ((jl_datatype_nfields(utt) == 1 && !jl_is_namedtuple_type(utt))) { + if ((jl_datatype_nfields(utt) == 1 && !jl_is_namedtuple_type(utt) && !jl_is_tuple_type(utt))) { jl_svec_t *fn = jl_field_names(utt); assert(jl_svec_len(fn) == 1); Value *typ_sym = literal_pointer_val(ctx, jl_svecref(fn, 0)); diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 1d28b818340a4..88f9948bb30a5 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -849,15 +849,6 @@ let res = @timed a50317[:b] return res end -# Very specific test for multiversioning -if Sys.ARCH === :x86_64 - foo52079() = Core.Intrinsics.have_fma(Float64) - if foo52079() == true - let io = IOBuffer() - code_native(io,^,(Float64,Float64), dump_module=false) - str = String(take!(io)) - @test !occursin("fma_emulated", str) - @test occursin("vfmadd", str) - end - end -end +# https://github.com/JuliaLang/julia/issues/51233 +obj51233 = (1,) +@test_throws ErrorException obj51233.x From 332d9f3cc039cfa679ffbb798d0c25a99875c7b8 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 16 Nov 2023 21:55:10 -0500 Subject: [PATCH 36/38] jitlayers: replace sharedbytes intern pool with one that respects alignment (#52182) The llvm optimizations may increase alignment beyond the initial MAX_ALIGN. This pool's alignment was previously only `sizeof(struct { atomic RefCount; size_t Length; char Data[]; })` however, potentially resulting in segfaults at runtime. Fixes #52118. Should make CI much happier. (cherry picked from commit a65bc9a267837fcf9813bef2fc6eb79d02e25ea5) --- src/gc.c | 3 +- src/jitlayers.cpp | 92 ++++++++++++++++++++++++++++++-------------- src/jitlayers.h | 43 ++++++++++++++++++++- src/julia_internal.h | 2 +- 4 files changed, 108 insertions(+), 32 deletions(-) diff --git a/src/gc.c b/src/gc.c index 98dcdeb631784..197d1c6ad75c2 100644 --- a/src/gc.c +++ b/src/gc.c @@ -3854,8 +3854,7 @@ static void *gc_perm_alloc_large(size_t sz, int zero, unsigned align, unsigned o errno = last_errno; jl_may_leak(base); assert(align > 0); - unsigned diff = (offset - (uintptr_t)base) % align; - return (void*)((char*)base + diff); + return (void*)(LLT_ALIGN((uintptr_t)base + offset, (uintptr_t)align) - offset); } STATIC_INLINE void *gc_try_perm_alloc_pool(size_t sz, unsigned align, unsigned offset) JL_NOTSAFEPOINT diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 1a385403e9672..099d74ccb37c1 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -1301,6 +1301,69 @@ namespace { JuliaOJIT::ResourcePool> TMs; }; + + struct JITPointersT { + + JITPointersT(SharedBytesT &SharedBytes, std::mutex &Lock) JL_NOTSAFEPOINT + : SharedBytes(SharedBytes), Lock(Lock) {} + + void operator()(Module &M) JL_NOTSAFEPOINT { + std::lock_guard locked(Lock); + for (auto &GV : make_early_inc_range(M.globals())) { + if (auto *Shared = getSharedBytes(GV)) { + ++InternedGlobals; + GV.replaceAllUsesWith(Shared); + GV.eraseFromParent(); + } + } + + // Windows needs some inline asm to help + // build unwind tables + jl_decorate_module(M); + } + + private: + // optimize memory by turning long strings into memoized copies, instead of + // making a copy per object file of output. + // we memoize them using a StringSet with a custom-alignment allocator + // to ensure they are properly aligned + Constant *getSharedBytes(GlobalVariable &GV) JL_NOTSAFEPOINT { + // We could probably technically get away with + // interning even external linkage globals, + // as long as they have global unnamedaddr, + // but currently we shouldn't be emitting those + // except in imaging mode, and we don't want to + // do this optimization there. + if (GV.hasExternalLinkage() || !GV.hasGlobalUnnamedAddr()) { + return nullptr; + } + if (!GV.hasInitializer()) { + return nullptr; + } + if (!GV.isConstant()) { + return nullptr; + } + auto CDS = dyn_cast(GV.getInitializer()); + if (!CDS) { + return nullptr; + } + StringRef Data = CDS->getRawDataValues(); + if (Data.size() < 16) { + // Cutoff, since we don't want to intern small strings + return nullptr; + } + Align Required = GV.getAlign().valueOrOne(); + Align Preferred = MaxAlignedAlloc::alignment(Data.size()); + if (Required > Preferred) + return nullptr; + StringRef Interned = SharedBytes.insert(Data).first->getKey(); + assert(llvm::isAddrAligned(Preferred, Interned.data())); + return literal_static_pointer_val(Interned.data(), GV.getType()); + } + + SharedBytesT &SharedBytes; + std::mutex &Lock; + }; } llvm::DataLayout jl_create_datalayout(TargetMachine &TM) { @@ -1493,8 +1556,7 @@ void JuliaOJIT::addModule(orc::ThreadSafeModule TSM) ++ModulesAdded; orc::SymbolLookupSet NewExports; TSM.withModuleDo([&](Module &M) JL_NOTSAFEPOINT { - jl_decorate_module(M); - shareStrings(M); + JITPointersT(SharedBytes, RLST_mutex)(M); for (auto &F : M.global_values()) { if (!F.isDeclaration() && F.getLinkage() == GlobalValue::ExternalLinkage) { auto Name = ES.intern(getMangledName(F.getName())); @@ -1820,32 +1882,6 @@ void jl_merge_module(orc::ThreadSafeModule &destTSM, orc::ThreadSafeModule srcTS }); } -// optimize memory by turning long strings into memoized copies, instead of -// making a copy per object file of output. -void JuliaOJIT::shareStrings(Module &M) -{ - ++InternedGlobals; - std::vector erase; - for (auto &GV : M.globals()) { - if (!GV.hasInitializer() || !GV.isConstant()) - continue; - ConstantDataSequential *CDS = dyn_cast(GV.getInitializer()); - if (CDS == nullptr) - continue; - StringRef data = CDS->getRawDataValues(); - if (data.size() > 16) { // only for long strings: keep short ones as values - Type *T_size = Type::getIntNTy(GV.getContext(), sizeof(void*) * 8); - Constant *v = ConstantExpr::getIntToPtr( - ConstantInt::get(T_size, (uintptr_t)(*ES.intern(data)).data()), - GV.getType()); - GV.replaceAllUsesWith(v); - erase.push_back(&GV); - } - } - for (auto GV : erase) - GV->eraseFromParent(); -} - //TargetMachine pass-through methods std::unique_ptr JuliaOJIT::cloneTargetMachine() const diff --git a/src/jitlayers.h b/src/jitlayers.h index 380694af60742..b5160ba1863e8 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -1,6 +1,8 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license #include +#include +#include #include #include @@ -298,6 +300,44 @@ static const inline char *name_from_method_instance(jl_method_instance_t *li) JL return jl_is_method(li->def.method) ? jl_symbol_name(li->def.method->name) : "top-level scope"; } +template +class MaxAlignedAllocImpl + : public AllocatorBase> { + +public: + MaxAlignedAllocImpl() JL_NOTSAFEPOINT = default; + + static Align alignment(size_t Size) JL_NOTSAFEPOINT { + // Define the maximum alignment we expect to require, from offset bytes off + // the returned pointer, this is >= alignof(std::max_align_t), which is too + // small often to actually use. + const size_t MaxAlignment = JL_CACHE_BYTE_ALIGNMENT; + return Align(std::min((size_t)llvm::PowerOf2Ceil(Size), MaxAlignment)); + } + + LLVM_ATTRIBUTE_RETURNS_NONNULL void *Allocate(size_t Size, Align Alignment) { + Align MaxAlign = alignment(Size); + assert(Alignment < MaxAlign); (void)Alignment; + return jl_gc_perm_alloc(Size, 0, MaxAlign.value(), offset); + } + + inline LLVM_ATTRIBUTE_RETURNS_NONNULL + void * Allocate(size_t Size, size_t Alignment) { + return Allocate(Size, Align(Alignment)); + } + + // Pull in base class overloads. + using AllocatorBase::Allocate; + + void Deallocate(const void *Ptr, size_t Size, size_t /*Alignment*/) { abort(); } + + // Pull in base class overloads. + using AllocatorBase::Deallocate; + +private: +}; +using MaxAlignedAlloc = MaxAlignedAllocImpl<>; + typedef JITSymbol JL_JITSymbol; // The type that is similar to SymbolInfo on LLVM 4.0 is actually // `JITEvaluatedSymbol`. However, we only use this type when a JITSymbol @@ -306,6 +346,7 @@ typedef JITSymbol JL_SymbolInfo; using CompilerResultT = Expected>; using OptimizerResultT = Expected; +using SharedBytesT = StringSet::MapEntryTy)>>; class JuliaOJIT { public: @@ -538,7 +579,6 @@ class JuliaOJIT { private: std::string getMangledName(StringRef Name) JL_NOTSAFEPOINT; std::string getMangledName(const GlobalValue *GV) JL_NOTSAFEPOINT; - void shareStrings(Module &M) JL_NOTSAFEPOINT; const std::unique_ptr TM; const DataLayout DL; @@ -551,6 +591,7 @@ class JuliaOJIT { std::mutex RLST_mutex{}; int RLST_inc = 0; DenseMap ReverseLocalSymbolTable; + SharedBytesT SharedBytes; //Compilation streams jl_locked_stream dump_emitted_mi_name_stream; diff --git a/src/julia_internal.h b/src/julia_internal.h index 51661fa1835f9..4a845350424d4 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -351,7 +351,7 @@ JL_DLLEXPORT int jl_gc_classify_pools(size_t sz, int *osize) JL_NOTSAFEPOINT; extern uv_mutex_t gc_perm_lock; void *jl_gc_perm_alloc_nolock(size_t sz, int zero, unsigned align, unsigned offset) JL_NOTSAFEPOINT; -void *jl_gc_perm_alloc(size_t sz, int zero, +JL_DLLEXPORT void *jl_gc_perm_alloc(size_t sz, int zero, unsigned align, unsigned offset) JL_NOTSAFEPOINT; void gc_sweep_sysimg(void); From 465ff74aa3cfd8c991f7cd86c0850dae2cd80ca4 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sun, 19 Nov 2023 22:03:15 -0500 Subject: [PATCH 37/38] jitlayers: reduce excess alignment of #52182 (#52210) (cherry picked from commit 72cd63ce28c50c8c72e009df03dfec608802450e) --- src/jitlayers.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/jitlayers.h b/src/jitlayers.h index b5160ba1863e8..f57861fa56794 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -312,7 +312,9 @@ class MaxAlignedAllocImpl // the returned pointer, this is >= alignof(std::max_align_t), which is too // small often to actually use. const size_t MaxAlignment = JL_CACHE_BYTE_ALIGNMENT; - return Align(std::min((size_t)llvm::PowerOf2Ceil(Size), MaxAlignment)); + if (Size <= offset) + return Align(1); + return Align(std::min((size_t)llvm::PowerOf2Ceil(Size - offset), MaxAlignment)); } LLVM_ATTRIBUTE_RETURNS_NONNULL void *Allocate(size_t Size, Align Alignment) { From b497f44d8d7b98932f9fde1627686fef9a096675 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 1 Sep 2023 14:50:26 -0400 Subject: [PATCH 38/38] simplify call to promote_eltype with repeated elements (#51135) Helps to short-circuit calls to large splat calls, since those have all the same type elements. Fixes #51011 (cherry picked from commit 3527213ccb1bfe0c48feab5da64d30cadbd4c526) --- base/abstractarray.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index c9a544425d8d0..8b8e4a760eb9b 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1591,10 +1591,14 @@ eltypeof(x::AbstractArray) = eltype(x) promote_eltypeof() = error() promote_eltypeof(v1) = eltypeof(v1) promote_eltypeof(v1, vs...) = promote_type(eltypeof(v1), promote_eltypeof(vs...)) +promote_eltypeof(v1::T, vs::T...) where {T} = eltypeof(v1) +promote_eltypeof(v1::AbstractArray{T}, vs::AbstractArray{T}...) where {T} = T promote_eltype() = error() promote_eltype(v1) = eltype(v1) promote_eltype(v1, vs...) = promote_type(eltype(v1), promote_eltype(vs...)) +promote_eltype(v1::T, vs::T...) where {T} = eltype(T) +promote_eltype(v1::AbstractArray{T}, vs::AbstractArray{T}...) where {T} = T #TODO: ERROR CHECK _cat(catdim::Int) = Vector{Any}()