diff --git a/Make.inc b/Make.inc index 8dc96eeff5317b..0f170734127ed5 100644 --- a/Make.inc +++ b/Make.inc @@ -1190,7 +1190,7 @@ endif # We need python for things like BB triplet recognition. We don't really care # about version, generally, so just find something that works: -PYTHON := "$(shell which python 2>/dev/null || which python3 2>/dev/null || which python2 2>/dev/null || echo not found)" +PYTHON := $(shell which python 2>/dev/null || which python3 2>/dev/null || which python2 2>/dev/null || echo not found) PYTHON_SYSTEM := $(shell $(PYTHON) -c 'from __future__ import print_function; import platform; print(platform.system())') # If we're running on Cygwin, but using a native-windows Python, we need to use cygpath -w diff --git a/Makefile b/Makefile index 5e9ea8a44c66ff..d205a4cf8deb8c 100644 --- a/Makefile +++ b/Makefile @@ -116,7 +116,7 @@ stdlibs-cache-release stdlibs-cache-debug : stdlibs-cache-% : julia-% debug release : % : julia-% stdlibs-cache-% -docs: julia-sysimg-$(JULIA_BUILD_MODE) +docs: julia-sysimg-$(JULIA_BUILD_MODE) stdlibs-cache-$(JULIA_BUILD_MODE) @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/doc JULIA_EXECUTABLE='$(call spawn,$(JULIA_EXECUTABLE_$(JULIA_BUILD_MODE))) --startup-file=no' docs-revise: @@ -184,7 +184,7 @@ $(build_depsbindir)/stringreplace: $(JULIAHOME)/contrib/stringreplace.c | $(buil @$(call PRINT_CC, $(HOSTCC) -o $(build_depsbindir)/stringreplace $(JULIAHOME)/contrib/stringreplace.c) julia-base-cache: julia-sysimg-$(JULIA_BUILD_MODE) | $(DIRS) $(build_datarootdir)/julia - @JULIA_BINDIR=$(call cygpath_w,$(build_bindir)) WINEPATH="$(call cygpath_w,$(build_bindir));$$WINEPATH" \ + @JULIA_BINDIR=$(call cygpath_w,$(build_bindir)) JULIA_FALLBACK_REPL=1 WINEPATH="$(call cygpath_w,$(build_bindir));$$WINEPATH" \ $(call spawn, $(JULIA_EXECUTABLE) --startup-file=no $(call cygpath_w,$(JULIAHOME)/etc/write_base_cache.jl) \ $(call cygpath_w,$(build_datarootdir)/julia/base.cache)) diff --git a/NEWS.md b/NEWS.md index 18a8f89f6d382c..39ddd762bd0548 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,10 @@ Julia v1.11 Release Notes New language features --------------------- +* `public` is a new keyword. Symbols marked with `public` are considered public + API. Symbols marked with `export` are now also treated as public API. The + difference between `public` and `export` is that `public` names do not become + available when `using` a package/module. ([#50105]) * `ScopedValue` implement dynamic scope with inheritance across tasks ([#50958]). Language changes @@ -47,6 +51,8 @@ Standard library changes #### Random +* When seeding RNGs provided by `Random`, negative integer seeds can now be used ([#51416]). + #### REPL * Tab complete hints now show in lighter text while typing in the repl. To disable diff --git a/base/abstractarray.jl b/base/abstractarray.jl index e74f031b77451d..0171c38bc9c0bb 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1816,17 +1816,16 @@ function __cat_offset1!(A, shape, catdims, offsets, x) inds = ntuple(length(offsets)) do i (i <= length(catdims) && catdims[i]) ? offsets[i] .+ cat_indices(x, i) : 1:shape[i] end - if x isa AbstractArray - A[inds...] = x - else - fill!(view(A, inds...), x) - end + _copy_or_fill!(A, inds, x) newoffsets = ntuple(length(offsets)) do i (i <= length(catdims) && catdims[i]) ? offsets[i] + cat_size(x, i) : offsets[i] end return newoffsets end +_copy_or_fill!(A, inds, x) = fill!(view(A, inds...), x) +_copy_or_fill!(A, inds, x::AbstractArray) = (A[inds...] = x) + """ vcat(A...) diff --git a/base/binaryplatforms.jl b/base/binaryplatforms.jl index 913dc51426eefc..b374d57ce97311 100644 --- a/base/binaryplatforms.jl +++ b/base/binaryplatforms.jl @@ -170,20 +170,18 @@ end # Allow us to easily serialize Platform objects -function Base.repr(p::Platform; context=nothing) - str = string( - "Platform(", - repr(arch(p)), - ", ", - repr(os(p)), - "; ", - join(("$(k) = $(repr(v))" for (k, v) in tags(p) if k ∉ ("arch", "os")), ", "), - ")", - ) +function Base.show(io::IO, p::Platform) + print(io, "Platform(") + show(io, arch(p)) + print(io, ", ") + show(io, os(p)) + print(io, "; ") + join(io, ("$(k) = $(repr(v))" for (k, v) in tags(p) if k ∉ ("arch", "os")), ", ") + print(io, ")") end # Make showing the platform a bit more palatable -function Base.show(io::IO, p::Platform) +function Base.show(io::IO, ::MIME"text/plain", p::Platform) str = string(platform_name(p), " ", arch(p)) # Add on all the other tags not covered by os/arch: other_tags = sort!(filter!(kv -> kv[1] ∉ ("os", "arch"), collect(tags(p)))) diff --git a/base/client.jl b/base/client.jl index 88d8321d092781..35abb26c7ff43d 100644 --- a/base/client.jl +++ b/base/client.jl @@ -405,13 +405,28 @@ function load_InteractiveUtils(mod::Module=Main) return getfield(mod, :InteractiveUtils) end +function load_REPL() + # load interactive-only libraries + try + return Base.require(PkgId(UUID(0x3fa0cd96_eef1_5676_8a61_b3b8758bbffb), "REPL")) + catch ex + @warn "Failed to import REPL" exception=(ex, catch_backtrace()) + end + return nothing +end + global active_repl # run the requested sort of evaluation loop on stdio function run_main_repl(interactive::Bool, quiet::Bool, banner::Symbol, history_file::Bool, color_set::Bool) - load_InteractiveUtils() - - fallback_repl = get_bool_env("JULIA_FALLBACK_REPL", false) + fallback_repl = parse(Bool, get(ENV, "JULIA_FALLBACK_REPL", "false")) + if !fallback_repl && interactive + load_InteractiveUtils() + if !isassigned(REPL_MODULE_REF) + load_REPL() + end + end + # TODO cleanup REPL_MODULE_REF if !fallback_repl && interactive && isassigned(REPL_MODULE_REF) invokelatest(REPL_MODULE_REF[]) do REPL @@ -435,8 +450,8 @@ function run_main_repl(interactive::Bool, quiet::Bool, banner::Symbol, history_f end else # otherwise provide a simple fallback - if interactive && !quiet - @warn "REPL provider not available: using basic fallback" + if !fallback_repl && interactive && !quiet + @warn "REPL provider not available: using basic fallback" LOAD_PATH=join(Base.LOAD_PATH, Sys.iswindows() ? ';' : ':') end banner == :no || Base.banner(short=banner==:short) let input = stdin diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index e82e1e974df489..bfddc2d6927b34 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -485,7 +485,10 @@ function abstract_call_method(interp::AbstractInterpreter, return MethodCallResult(Any, false, false, nothing, Effects()) end sigtuple = unwrap_unionall(sig) - sigtuple isa DataType || return MethodCallResult(Any, false, false, nothing, Effects()) + sigtuple isa DataType || + return MethodCallResult(Any, false, false, nothing, Effects()) + all(@nospecialize(x) -> valid_as_lattice(unwrapva(x), true), sigtuple.parameters) || + return MethodCallResult(Union{}, false, false, nothing, EFFECTS_THROWS) # catch bad type intersections early if is_nospecializeinfer(method) sig = get_nospecializeinfer_sig(method, sig, sparams) @@ -1365,25 +1368,35 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) end if isa(tti, Union) utis = uniontypes(tti) - if any(@nospecialize(t) -> !isa(t, DataType) || !(t <: Tuple) || !isknownlength(t), utis) - return AbstractIterationResult(Any[Vararg{Any}], nothing, Effects()) - end - ltp = length((utis[1]::DataType).parameters) - for t in utis - if length((t::DataType).parameters) != ltp - return AbstractIterationResult(Any[Vararg{Any}], nothing) + # refine the Union to remove elements that are not valid tags for objects + filter!(@nospecialize(x) -> valid_as_lattice(x, true), utis) + if length(utis) == 0 + return AbstractIterationResult(Any[], nothing) # oops, this statement was actually unreachable + elseif length(utis) == 1 + tti = utis[1] + tti0 = rewrap_unionall(tti, tti0) + else + if any(@nospecialize(t) -> !isa(t, DataType) || !(t <: Tuple) || !isknownlength(t), utis) + return AbstractIterationResult(Any[Vararg{Any}], nothing, Effects()) end - end - result = Any[ Union{} for _ in 1:ltp ] - for t in utis - tps = (t::DataType).parameters - _all(valid_as_lattice, tps) || continue - for j in 1:ltp - result[j] = tmerge(result[j], rewrap_unionall(tps[j], tti0)) + ltp = length((utis[1]::DataType).parameters) + for t in utis + if length((t::DataType).parameters) != ltp + return AbstractIterationResult(Any[Vararg{Any}], nothing) + end + end + result = Any[ Union{} for _ in 1:ltp ] + for t in utis + tps = (t::DataType).parameters + for j in 1:ltp + @assert valid_as_lattice(tps[j], true) + result[j] = tmerge(result[j], rewrap_unionall(tps[j], tti0)) + end end + return AbstractIterationResult(result, nothing) end - return AbstractIterationResult(result, nothing) - elseif tti0 <: Tuple + end + if tti0 <: Tuple if isa(tti0, DataType) return AbstractIterationResult(Any[ p for p in tti0.parameters ], nothing) elseif !isa(tti, DataType) @@ -1647,7 +1660,7 @@ end return isa_condition(xt, ty, max_union_splitting) end @inline function isa_condition(@nospecialize(xt), @nospecialize(ty), max_union_splitting::Int) - tty_ub, isexact_tty = instanceof_tfunc(ty) + tty_ub, isexact_tty = instanceof_tfunc(ty, true) tty = widenconst(xt) if isexact_tty && !isa(tty_ub, TypeVar) tty_lb = tty_ub # TODO: this would be wrong if !isexact_tty, but instanceof_tfunc doesn't preserve this info @@ -1657,7 +1670,7 @@ end # `typeintersect` may be unable narrow down `Type`-type thentype = tty_ub end - valid_as_lattice(thentype) || (thentype = Bottom) + valid_as_lattice(thentype, true) || (thentype = Bottom) elsetype = typesubtract(tty, tty_lb, max_union_splitting) return ConditionalTypes(thentype, elsetype) end @@ -1853,51 +1866,57 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs end function abstract_call_unionall(interp::AbstractInterpreter, argtypes::Vector{Any}, call::CallMeta) - if length(argtypes) == 3 - canconst = true + na = length(argtypes) + if isvarargtype(argtypes[end]) + if na ≤ 2 + return CallMeta(Any, EFFECTS_THROWS, call.info) + elseif na > 4 + return CallMeta(Bottom, EFFECTS_THROWS, NoCallInfo()) + end + a2 = argtypes[2] + a3 = unwrapva(argtypes[3]) + nothrow = false + elseif na == 3 a2 = argtypes[2] a3 = argtypes[3] ⊑ᵢ = ⊑(typeinf_lattice(interp)) - if isvarargtype(a3) - a3 = unwrapva(a3) - nothrow = false - else - nothrow = a2 ⊑ᵢ TypeVar && (a3 ⊑ᵢ Type || a3 ⊑ᵢ TypeVar) - end - if isa(a3, Const) - body = a3.val - elseif isType(a3) - body = a3.parameters[1] + nothrow = a2 ⊑ᵢ TypeVar && (a3 ⊑ᵢ Type || a3 ⊑ᵢ TypeVar) + else + return CallMeta(Bottom, EFFECTS_THROWS, NoCallInfo()) + end + canconst = true + if isa(a3, Const) + body = a3.val + elseif isType(a3) + body = a3.parameters[1] + canconst = false + else + return CallMeta(Any, Effects(EFFECTS_TOTAL; nothrow), call.info) + end + if !(isa(body, Type) || isa(body, TypeVar)) + return CallMeta(Any, EFFECTS_THROWS, call.info) + end + if has_free_typevars(body) + if isa(a2, Const) + tv = a2.val + elseif isa(a2, PartialTypeVar) + tv = a2.tv canconst = false else - return CallMeta(Any, Effects(EFFECTS_TOTAL; nothrow), call.info) - end - if !(isa(body, Type) || isa(body, TypeVar)) return CallMeta(Any, EFFECTS_THROWS, call.info) end - if has_free_typevars(body) - if isa(a2, Const) - tv = a2.val - elseif isa(a2, PartialTypeVar) - tv = a2.tv - canconst = false - else - return CallMeta(Any, EFFECTS_THROWS, call.info) - end - isa(tv, TypeVar) || return CallMeta(Any, EFFECTS_THROWS, call.info) - body = UnionAll(tv, body) - end - ret = canconst ? Const(body) : Type{body} - return CallMeta(ret, Effects(EFFECTS_TOTAL; nothrow), call.info) + isa(tv, TypeVar) || return CallMeta(Any, EFFECTS_THROWS, call.info) + body = UnionAll(tv, body) end - return CallMeta(Bottom, EFFECTS_THROWS, NoCallInfo()) + ret = canconst ? Const(body) : Type{body} + return CallMeta(ret, Effects(EFFECTS_TOTAL; nothrow), call.info) end function abstract_invoke(interp::AbstractInterpreter, (; fargs, argtypes)::ArgInfo, si::StmtInfo, sv::AbsIntState) ft′ = argtype_by_index(argtypes, 2) ft = widenconst(ft′) ft === Bottom && return CallMeta(Bottom, EFFECTS_THROWS, NoCallInfo()) - (types, isexact, isconcrete, istype) = instanceof_tfunc(argtype_by_index(argtypes, 3)) + (types, isexact, isconcrete, istype) = instanceof_tfunc(argtype_by_index(argtypes, 3), false) isexact || return CallMeta(Any, Effects(), NoCallInfo()) unwrapped = unwrap_unionall(types) if types === Bottom || !(unwrapped isa DataType) || unwrapped.name !== Tuple.name @@ -2316,7 +2335,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp (; rt, effects) = abstract_eval_call(interp, e, vtypes, sv) t = rt elseif ehead === :new - t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv)) + t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv), true) ut = unwrap_unionall(t) consistent = noub = ALWAYS_FALSE nothrow = false @@ -2381,7 +2400,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp end effects = Effects(EFFECTS_TOTAL; consistent, nothrow, noub) elseif ehead === :splatnew - t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv)) + t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv), true) nothrow = false # TODO: More precision if length(e.args) == 2 && isconcretedispatch(t) && !ismutabletype(t) at = abstract_eval_value(interp, e.args[2], vtypes, sv) diff --git a/base/compiler/abstractlattice.jl b/base/compiler/abstractlattice.jl index 98a53e7283cbc4..c1229124d1cecc 100644 --- a/base/compiler/abstractlattice.jl +++ b/base/compiler/abstractlattice.jl @@ -98,8 +98,10 @@ is_valid_lattice_norec(::InferenceLattice, @nospecialize(elem)) = isa(elem, Limi """ tmeet(𝕃::AbstractLattice, a, b::Type) -Compute the lattice meet of lattice elements `a` and `b` over the lattice `𝕃`. -If `𝕃` is `JLTypeLattice`, this is equivalent to type intersection. +Compute the lattice meet of lattice elements `a` and `b` over the lattice `𝕃`, +dropping any results that will not be inhabited at runtime. +If `𝕃` is `JLTypeLattice`, this is equivalent to type intersection plus the +elimination of results that have no concrete subtypes. Note that currently `b` is restricted to being a type (interpreted as a lattice element in the `JLTypeLattice` sub-lattice of `𝕃`). """ @@ -107,7 +109,7 @@ function tmeet end function tmeet(::JLTypeLattice, @nospecialize(a::Type), @nospecialize(b::Type)) ti = typeintersect(a, b) - valid_as_lattice(ti) || return Bottom + valid_as_lattice(ti, true) || return Bottom return ti end diff --git a/base/compiler/bootstrap.jl b/base/compiler/bootstrap.jl index 4b5887a82d0460..12c83df74fe501 100644 --- a/base/compiler/bootstrap.jl +++ b/base/compiler/bootstrap.jl @@ -5,15 +5,16 @@ # especially try to make sure any recursive and leaf functions have concrete signatures, # since we won't be able to specialize & infer them at runtime -time() = ccall(:jl_clock_now, Float64, ()) +let time() = ccall(:jl_clock_now, Float64, ()) -let interp = NativeInterpreter() + interp = NativeInterpreter() - # analyze_escapes_tt = Tuple{typeof(analyze_escapes), IRCode, Int, Bool, TODO} + # analyze_escapes_tt = Tuple{typeof(analyze_escapes), IRCode, Int, TODO} + optimize_tt = Tuple{typeof(optimize), NativeInterpreter, OptimizationState{NativeInterpreter}, InferenceResult} fs = Any[ # we first create caches for the optimizer, because they contain many loop constructions # and they're better to not run in interpreter even during bootstrapping - #=analyze_escapes_tt,=# run_passes_ipo_safe, + #=analyze_escapes_tt,=# optimize_tt, # then we create caches for inference entries typeinf_ext, typeinf, typeinf_edge, ] diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index c3535eb5882ecd..550b5a2541b0f5 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -539,6 +539,13 @@ function sptypes_from_meth_instance(linfo::MethodInstance) # then `arg` is more precise than `Type{T} where lb<:T<:ub` ty = fieldtype(linfo.specTypes, j) @goto ty_computed + elseif (va = va_from_vatuple(sⱼ)) !== nothing + # if this parameter came from `::Tuple{.., Vararg{T,vᵢ}}`, + # then `vᵢ` is known to be `Int` + if isdefined(va, :N) && va.N === vᵢ + ty = Int + @goto ty_computed + end end end ub = unwraptv_ub(v) @@ -568,6 +575,8 @@ function sptypes_from_meth_instance(linfo::MethodInstance) constrains_param(v, sig, #=covariant=#true) end) elseif isvarargtype(v) + # if this parameter came from `func(..., ::Vararg{T,v})`, + # so the type is known to be `Int` ty = Int undef = false else @@ -579,6 +588,21 @@ function sptypes_from_meth_instance(linfo::MethodInstance) return sptypes end +function va_from_vatuple(@nospecialize(t)) + @_foldable_meta + t = unwrap_unionall(t) + if isa(t, DataType) + n = length(t.parameters) + if n > 0 + va = t.parameters[n] + if isvarargtype(va) + return va + end + end + end + return nothing +end + _topmod(sv::InferenceState) = _topmod(frame_module(sv)) function record_ssa_assign!(𝕃ᵢ::AbstractLattice, ssa_id::Int, @nospecialize(new), frame::InferenceState) @@ -874,8 +898,14 @@ function should_infer_this_call(interp::AbstractInterpreter, sv::InferenceState) return true end function should_infer_for_effects(sv::InferenceState) + def = sv.linfo.def + def isa Method || return false # toplevel frame will not be [semi-]concrete-evaluated effects = sv.ipo_effects - return is_terminates(effects) && is_effect_free(effects) + override = decode_effects_override(def.purity) + effects.consistent === ALWAYS_FALSE && !is_effect_overridden(override, :consistent) && return false + effects.effect_free === ALWAYS_FALSE && !is_effect_overridden(override, :effect_free) && return false + !effects.terminates && !is_effect_overridden(override, :terminates_globally) && return false + return true end should_infer_this_call(::AbstractInterpreter, ::IRInterpretationState) = true diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 790e1b4e022d50..fb57b06d75dbce 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -243,7 +243,7 @@ function new_expr_effect_flags(𝕃ₒ::AbstractLattice, args::Vector{Any}, src: Targ = args[1] atyp = argextype(Targ, src) # `Expr(:new)` of unknown type could raise arbitrary TypeError. - typ, isexact = instanceof_tfunc(atyp) + typ, isexact = instanceof_tfunc(atyp, true) if !isexact atyp = unwrap_unionall(widenconst(atyp)) if isType(atyp) && isTypeDataType(atyp.parameters[1]) @@ -335,7 +335,7 @@ function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospe elseif head === :new_opaque_closure length(args) < 4 && return (false, false, false) typ = argextype(args[1], src) - typ, isexact = instanceof_tfunc(typ) + typ, isexact = instanceof_tfunc(typ, true) isexact || return (false, false, false) ⊑(𝕃ₒ, typ, Tuple) || return (false, false, false) rt_lb = argextype(args[2], src) diff --git a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl index b8244f734170ec..6a6994d497d8ea 100644 --- a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl +++ b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl @@ -564,23 +564,22 @@ end struct ArgEscapeCache argescapes::Vector{ArgEscapeInfo} argaliases::Vector{ArgAliasing} -end - -function ArgEscapeCache(estate::EscapeState) - nargs = estate.nargs - argescapes = Vector{ArgEscapeInfo}(undef, nargs) - argaliases = ArgAliasing[] - for i = 1:nargs - info = estate.escapes[i] - @assert info.AliasInfo === true - argescapes[i] = ArgEscapeInfo(info) - for j = (i+1):nargs - if isaliased(i, j, estate) - push!(argaliases, ArgAliasing(i, j)) + function ArgEscapeCache(estate::EscapeState) + nargs = estate.nargs + argescapes = Vector{ArgEscapeInfo}(undef, nargs) + argaliases = ArgAliasing[] + for i = 1:nargs + info = estate.escapes[i] + @assert info.AliasInfo === true + argescapes[i] = ArgEscapeInfo(info) + for j = (i+1):nargs + if isaliased(i, j, estate) + push!(argaliases, ArgAliasing(i, j)) + end end end + return new(argescapes, argaliases) end - return ArgEscapeCache(argescapes, argaliases) end abstract type Change end @@ -1093,11 +1092,9 @@ function escape_exception!(astate::AnalysisState, tryregions::Vector{UnitRange{I end # escape statically-resolved call, i.e. `Expr(:invoke, ::MethodInstance, ...)` -escape_invoke!(astate::AnalysisState, pc::Int, args::Vector{Any}) = - escape_invoke!(astate, pc, args, first(args)::MethodInstance, 2) - -function escape_invoke!(astate::AnalysisState, pc::Int, args::Vector{Any}, - mi::MethodInstance, first_idx::Int, last_idx::Int = length(args)) +function escape_invoke!(astate::AnalysisState, pc::Int, args::Vector{Any}) + mi = first(args)::MethodInstance + first_idx, last_idx = 2, length(args) # TODO inspect `astate.ir.stmts[pc][:info]` and use const-prop'ed `InferenceResult` if available cache = astate.get_escape_cache(mi) if cache === nothing @@ -1127,7 +1124,7 @@ function escape_invoke!(astate::AnalysisState, pc::Int, args::Vector{Any}, end end for (; aidx, bidx) in cache.argaliases - add_alias_change!(astate, args[aidx-(first_idx-1)], args[bidx-(first_idx-1)]) + add_alias_change!(astate, args[aidx+(first_idx-1)], args[bidx+(first_idx-1)]) end # we should disable the alias analysis on this newly introduced object add_escape_change!(astate, ret, EscapeInfo(retinfo, true)) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 279a30aa3084ce..0a5b5c65805956 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -504,50 +504,45 @@ end """ ir_inline_unionsplit! -The core idea of this function is to simulate the dispatch semantics by generating -(flat) `isa`-checks corresponding to the signatures of union-split dispatch candidates, -and then inline their bodies into each `isa`-conditional block. -This `isa`-based virtual dispatch requires few pre-conditions to hold in order to simulate -the actual semantics correctly. +The primary purpose of this function is to emulate the dispatch behavior by generating flat +`isa`-checks that correspond to the signatures of union-split dispatch candidates. +These checks allow us to inline the method bodies into respective `isa`-conditional blocks. -The first one is that these dispatch candidates need to be processed in order of their specificity, -and the corresponding `isa`-checks should reflect the method specificities, since now their -signatures are not necessarily concrete. -For example, given the following definitions: +Note that two pre-conditions are required for this emulation to work correctly: + +1. Ordered Dispatch Candidates + +The dispatch candidates must be processed in order of their specificity. +The generated `isa`-checks should reflect this order, +especially since the method signatures may not be concrete. +For instance, with the methods: f(x::Int) = ... f(x::Number) = ... f(x::Any) = ... -and a callsite: - - f(x::Any) - -then a correct `isa`-based virtual dispatch would be: +A correct `isa`-based dispatch emulation for the call site `f(x::Any)` would look like: if isa(x, Int) [inlined/resolved f(x::Int)] elseif isa(x, Number) [inlined/resolved f(x::Number)] - else # implies `isa(x, Any)`, which fully covers this call signature, - # otherwise we need to insert a fallback dynamic dispatch case also + else [inlined/resolved f(x::Any)] end -Fortunately, `ml_matches` should already sorted them in that way, except cases when there is -any ambiguity, from which we already bail out at this point. +`ml_matches` should already sort the matched method candidates correctly, +except in ambiguous cases, which we've already excluded at this state. -Another consideration is type equality constraint from type variables: the `isa`-checks are -not enough to simulate the dispatch semantics in cases like: -Given a definition: +2. Type Equality Constraints - g(x::T, y::T) where T<:Integer = ... - -transform a callsite: +Another factor is the type equality constraint imposed by type variables. +Simple `isa`-checks are insufficient to capture the semantics in some cases. +For example, given the following method definition: - g(x::Any, y::Any) + g(x::T, y::T) where T<:Integer = ... -into the optimized form: +it is _invalid_ to optimize a cal site like `g(x::Any, y::Any)` into: if isa(x, Integer) && isa(y, Integer) [inlined/resolved g(x::Integer, y::Integer)] @@ -555,11 +550,14 @@ into the optimized form: g(x, y) # fallback dynamic dispatch end -But again, we should already bail out from such cases at this point, essentially by -excluding cases where `case.sig::UnionAll`. +since we also need to check that `x` and `y` are equal types. + +But, we've already excluded such cases at this point, +mainly by filtering out `case.sig::UnionAll`, +so there is no need to worry about type equality at this point. -In short, here we can process the dispatch candidates in order, assuming we haven't changed -their order somehow somewhere up to this point. +In essence, we can process the dispatch candidates sequentially, +assuming their order stays the same post-discovery in `ml_matches`. """ function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, argexprs::Vector{Any}, union_split::UnionSplit, boundscheck::Symbol, @@ -1202,7 +1200,7 @@ function handle_invoke_call!(todo::Vector{Pair{Int,Any}}, end function invoke_signature(argtypes::Vector{Any}) - ft, argtyps = widenconst(argtypes[2]), instanceof_tfunc(widenconst(argtypes[3]))[1] + ft, argtyps = widenconst(argtypes[2]), instanceof_tfunc(widenconst(argtypes[3]), false)[1] return rewrap_unionall(Tuple{ft, unwrap_unionall(argtyps).parameters...}, argtyps) end diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 428909e2792eaf..af3d91d8b69f48 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1756,7 +1756,7 @@ function adce_pass!(ir::IRCode, inlining::Union{Nothing,InliningState}=nothing) else if is_known_call(stmt, typeassert, compact) && length(stmt.args) == 3 # nullify safe `typeassert` calls - ty, isexact = instanceof_tfunc(argextype(stmt.args[3], compact)) + ty, isexact = instanceof_tfunc(argextype(stmt.args[3], compact), true) if isexact && ⊑(𝕃ₒ, argextype(stmt.args[2], compact), ty) compact[idx] = nothing continue diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index b4135fd9dc568f..39e092332aae61 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -587,6 +587,7 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, sv::OptimizationState, handler_at = compute_trycatch(code, BitSet()) phi_slots = Vector{Int}[Int[] for _ = 1:length(ir.cfg.blocks)] + live_slots = Vector{Int}[Int[] for _ = 1:length(ir.cfg.blocks)] new_phi_nodes = Vector{NewPhiNode2}[NewPhiNode2[] for _ = 1:length(cfg.blocks)] new_phic_nodes = IdDict{Int, Vector{NewPhiCNode2}}() for (; leave_block) in catch_entry_blocks @@ -617,8 +618,10 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, sv::OptimizationState, end continue end + @timeit "liveness" (live = compute_live_ins(cfg, slot)) for li in live.live_in_bbs + push!(live_slots[li], idx) cidx = findfirst(x::TryCatchRegion->x.leave_block==li, catch_entry_blocks) if cidx !== nothing # The slot is live-in into this block. We need to @@ -735,7 +738,7 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, sv::OptimizationState, end # Record Pi nodes if necessary has_pinode = fill(false, length(sv.slottypes)) - for slot in 1:length(sv.slottypes) + for slot in live_slots[item] (ival, idef) = incoming_vals[slot] (ival === SSAValue(-1)) && continue (ival === SSAValue(-2)) && continue diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 2f0b6711cf9958..2104686dca5666 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -95,9 +95,9 @@ add_tfunc(throw, 1, 1, @nospecs((𝕃::AbstractLattice, x)->Bottom), 0) # if isexact is false, the actual runtime type may (will) be a subtype of t # if isconcrete is true, the actual runtime type is definitely concrete (unreachable if not valid as a typeof) # if istype is true, the actual runtime value will definitely be a type (e.g. this is false for Union{Type{Int}, Int}) -function instanceof_tfunc(@nospecialize(t)) +function instanceof_tfunc(@nospecialize(t), astag::Bool=false) if isa(t, Const) - if isa(t.val, Type) && valid_as_lattice(t.val) + if isa(t.val, Type) && valid_as_lattice(t.val, astag) return t.val, true, isconcretetype(t.val), true end return Bottom, true, false, false # runtime throws on non-Type @@ -109,11 +109,11 @@ function instanceof_tfunc(@nospecialize(t)) return Bottom, true, false, false # literal Bottom or non-Type elseif isType(t) tp = t.parameters[1] - valid_as_lattice(tp) || return Bottom, true, false, false # runtime unreachable / throws on non-Type + valid_as_lattice(tp, astag) || return Bottom, true, false, false # runtime unreachable / throws on non-Type return tp, !has_free_typevars(tp), isconcretetype(tp), true elseif isa(t, UnionAll) t′ = unwrap_unionall(t) - t′′, isexact, isconcrete, istype = instanceof_tfunc(t′) + t′′, isexact, isconcrete, istype = instanceof_tfunc(t′, astag) tr = rewrap_unionall(t′′, t) if t′′ isa DataType && t′′.name !== Tuple.name && !has_free_typevars(tr) # a real instance must be within the declared bounds of the type, @@ -128,8 +128,8 @@ function instanceof_tfunc(@nospecialize(t)) end return tr, isexact, isconcrete, istype elseif isa(t, Union) - ta, isexact_a, isconcrete_a, istype_a = instanceof_tfunc(t.a) - tb, isexact_b, isconcrete_b, istype_b = instanceof_tfunc(t.b) + ta, isexact_a, isconcrete_a, istype_a = instanceof_tfunc(t.a, astag) + tb, isexact_b, isconcrete_b, istype_b = instanceof_tfunc(t.b, astag) isconcrete = isconcrete_a && isconcrete_b istype = istype_a && istype_b # most users already handle the Union case, so here we assume that @@ -149,9 +149,9 @@ end # ---------- @nospecs bitcast_tfunc(𝕃::AbstractLattice, t, x) = bitcast_tfunc(widenlattice(𝕃), t, x) -@nospecs bitcast_tfunc(::JLTypeLattice, t, x) = instanceof_tfunc(t)[1] +@nospecs bitcast_tfunc(::JLTypeLattice, t, x) = instanceof_tfunc(t, true)[1] @nospecs conversion_tfunc(𝕃::AbstractLattice, t, x) = conversion_tfunc(widenlattice(𝕃), t, x) -@nospecs conversion_tfunc(::JLTypeLattice, t, x) = instanceof_tfunc(t)[1] +@nospecs conversion_tfunc(::JLTypeLattice, t, x) = instanceof_tfunc(t, true)[1] add_tfunc(bitcast, 2, 2, bitcast_tfunc, 1) add_tfunc(sext_int, 2, 2, conversion_tfunc, 1) @@ -291,7 +291,7 @@ add_tfunc(checked_umul_int, 2, 2, chk_tfunc, 10) # ----------- @nospecs function llvmcall_tfunc(𝕃::AbstractLattice, fptr, rt, at, a...) - return instanceof_tfunc(rt)[1] + return instanceof_tfunc(rt, true)[1] end add_tfunc(Core.Intrinsics.llvmcall, 3, INT_INF, llvmcall_tfunc, 10) @@ -461,7 +461,7 @@ function sizeof_nothrow(@nospecialize(x)) return sizeof_nothrow(rewrap_unionall(xu.a, x)) && sizeof_nothrow(rewrap_unionall(xu.b, x)) end - t, exact, isconcrete = instanceof_tfunc(x) + t, exact, isconcrete = instanceof_tfunc(x, false) if t === Bottom # x must be an instance (not a Type) or is the Bottom type object x = widenconst(x) @@ -513,7 +513,7 @@ end end # Core.sizeof operates on either a type or a value. First check which # case we're in. - t, exact = instanceof_tfunc(x) + t, exact = instanceof_tfunc(x, false) if t !== Bottom # The value corresponding to `x` at runtime could be a type. # Normalize the query to ask about that type. @@ -665,7 +665,7 @@ function pointer_eltype(@nospecialize(ptr)) unw = unwrap_unionall(a) if isa(unw, DataType) && unw.name === Ptr.body.name T = unw.parameters[1] - valid_as_lattice(T) || return Bottom + valid_as_lattice(T, true) || return Bottom return rewrap_unionall(T, a) end end @@ -697,7 +697,7 @@ end if isa(unw, DataType) && unw.name === Ptr.body.name T = unw.parameters[1] # note: we could sometimes refine this to a PartialStruct if we analyzed `op(T, T)::T` - valid_as_lattice(T) || return Bottom + valid_as_lattice(T, true) || return Bottom return rewrap_unionall(Pair{T, T}, a) end end @@ -709,7 +709,7 @@ end unw = unwrap_unionall(a) if isa(unw, DataType) && unw.name === Ptr.body.name T = unw.parameters[1] - valid_as_lattice(T) || return Bottom + valid_as_lattice(T, true) || return Bottom return rewrap_unionall(ccall(:jl_apply_cmpswap_type, Any, (Any,), T), a) end end @@ -817,7 +817,7 @@ end add_tfunc(typeof, 1, 1, typeof_tfunc, 1) @nospecs function typeassert_tfunc(𝕃::AbstractLattice, v, t) - t = instanceof_tfunc(t)[1] + t = instanceof_tfunc(t, true)[1] t === Any && return v return tmeet(𝕃, v, t) end @@ -825,7 +825,7 @@ add_tfunc(typeassert, 2, 2, typeassert_tfunc, 4) @nospecs function typeassert_nothrow(𝕃::AbstractLattice, v, t) ⊑ = Core.Compiler.:⊑(𝕃) - # ty, exact = instanceof_tfunc(t) + # ty, exact = instanceof_tfunc(t, true) # return exact && v ⊑ ty if (isType(t) && !has_free_typevars(t) && v ⊑ t.parameters[1]) || (isa(t, Const) && isa(t.val, Type) && v ⊑ t.val) @@ -835,7 +835,7 @@ add_tfunc(typeassert, 2, 2, typeassert_tfunc, 4) end @nospecs function isa_tfunc(𝕃::AbstractLattice, v, tt) - t, isexact = instanceof_tfunc(tt) + t, isexact = instanceof_tfunc(tt, true) if t === Bottom # check if t could be equivalent to typeof(Bottom), since that's valid in `isa`, but the set of `v` is empty # if `t` cannot have instances, it's also invalid on the RHS of isa @@ -875,8 +875,8 @@ add_tfunc(isa, 2, 2, isa_tfunc, 1) end @nospecs function subtype_tfunc(𝕃::AbstractLattice, a, b) - a, isexact_a = instanceof_tfunc(a) - b, isexact_b = instanceof_tfunc(b) + a, isexact_a = instanceof_tfunc(a, false) + b, isexact_b = instanceof_tfunc(b, false) if !has_free_typevars(a) && !has_free_typevars(b) if a <: b if isexact_b || a === Bottom @@ -1223,31 +1223,36 @@ end return Bottom end if nf == 1 - return rewrap_unionall(unwrapva(ftypes[1]), s00) - end - # union together types of all fields - t = Bottom - for i in 1:nf - _ft = ftypes[i] - setfield && isconst(s, i) && continue - t = tmerge(t, rewrap_unionall(unwrapva(_ft), s00)) - t === Any && break + fld = 1 + else + # union together types of all fields + t = Bottom + for i in 1:nf + _ft = unwrapva(ftypes[i]) + valid_as_lattice(_ft, true) || continue + setfield && isconst(s, i) && continue + t = tmerge(t, rewrap_unionall(_ft, s00)) + t === Any && break + end + return t end - return t + else + fld = _getfield_fieldindex(s, name) + fld === nothing && return Bottom end - fld = _getfield_fieldindex(s, name) - fld === nothing && return Bottom if s <: Tuple && fld >= nf && isvarargtype(ftypes[nf]) - return rewrap_unionall(unwrapva(ftypes[nf]), s00) - end - if fld < 1 || fld > nf - return Bottom - elseif setfield && isconst(s, fld) - return Bottom - end - R = ftypes[fld] - if isempty(s.parameters) - return R + R = unwrapva(ftypes[nf]) + else + if fld < 1 || fld > nf + return Bottom + elseif setfield && isconst(s, fld) + return Bottom + end + R = ftypes[fld] + valid_as_lattice(R, true) || return Bottom + if isempty(s.parameters) + return R + end end return rewrap_unionall(R, s00) end @@ -1382,7 +1387,7 @@ end T = _fieldtype_tfunc(𝕃, o, f, isconcretetype(o)) T === Bottom && return Bottom PT = Const(Pair) - return instanceof_tfunc(apply_type_tfunc(𝕃, PT, T, T))[1] + return instanceof_tfunc(apply_type_tfunc(𝕃, PT, T, T), true)[1] end function abstract_modifyfield!(interp::AbstractInterpreter, argtypes::Vector{Any}, si::StmtInfo, sv::AbsIntState) nargs = length(argtypes) @@ -1424,7 +1429,7 @@ end T = _fieldtype_tfunc(𝕃, o, f, isconcretetype(o)) T === Bottom && return Bottom PT = Const(ccall(:jl_apply_cmpswap_type, Any, (Any,), T) where T) - return instanceof_tfunc(apply_type_tfunc(𝕃, PT, T))[1] + return instanceof_tfunc(apply_type_tfunc(𝕃, PT, T), true)[1] end # we could use tuple_tfunc instead of widenconst, but `o` is mutable, so that is unlikely to be beneficial @@ -1456,7 +1461,7 @@ add_tfunc(replacefield!, 4, 6, replacefield!_tfunc, 3) fieldtype_nothrow(𝕃, rewrap_unionall(su.b, s0), name) end - s, exact = instanceof_tfunc(s0) + s, exact = instanceof_tfunc(s0, false) s === Bottom && return false # always return _fieldtype_nothrow(s, exact, name) end @@ -1521,7 +1526,7 @@ end fieldtype_tfunc(𝕃, rewrap_unionall(su.b, s0), name)) end - s, exact = instanceof_tfunc(s0) + s, exact = instanceof_tfunc(s0, false) s === Bottom && return Bottom return _fieldtype_tfunc(𝕃, s, name, exact) end @@ -1534,8 +1539,8 @@ end tb0 = _fieldtype_tfunc(𝕃, rewrap_unionall(u.b, s), name, exact) ta0 ⊑ tb0 && return tb0 tb0 ⊑ ta0 && return ta0 - ta, exacta, _, istypea = instanceof_tfunc(ta0) - tb, exactb, _, istypeb = instanceof_tfunc(tb0) + ta, exacta, _, istypea = instanceof_tfunc(ta0, false) + tb, exactb, _, istypeb = instanceof_tfunc(tb0, false) if exact && exacta && exactb return Const(Union{ta, tb}) end @@ -1669,7 +1674,7 @@ function apply_type_nothrow(𝕃::AbstractLattice, argtypes::Vector{Any}, @nospe return false end else - T, exact, _, istype = instanceof_tfunc(ai) + T, exact, _, istype = instanceof_tfunc(ai, false) if T === Bottom if !(u.var.lb === Union{} && u.var.ub === Any) return false @@ -1733,9 +1738,7 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K, end end if largs == 1 # Union{T} --> T - u1 = typeintersect(widenconst(args[1]), Union{Type,TypeVar}) - valid_as_lattice(u1) || return Bottom - return u1 + return tmeet(widenconst(args[1]), Union{Type,TypeVar}) end hasnonType && return Type ty = Union{} @@ -1820,7 +1823,7 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K, elseif !isT # if we didn't have isType to compute ub directly, try to use instanceof_tfunc to refine this guess ai_w = widenconst(ai) - ub = ai_w isa Type && ai_w <: Type ? instanceof_tfunc(ai)[1] : Any + ub = ai_w isa Type && ai_w <: Type ? instanceof_tfunc(ai, false)[1] : Any end if istuple # in the last parameter of a Tuple type, if the upper bound is Any @@ -2019,7 +2022,7 @@ function array_elmtype(@nospecialize ary) end if isa(a, DataType) T = a.parameters[1] - valid_as_lattice(T) || return Bottom + valid_as_lattice(T, true) || return Bottom return rewrap_unionall(T, a0) end end @@ -2571,7 +2574,7 @@ function intrinsic_nothrow(f::IntrinsicFunction, argtypes::Vector{Any}) return argtypes[1] ⊑ Array end if f === Intrinsics.bitcast - ty, isexact, isconcrete = instanceof_tfunc(argtypes[1]) + ty, isexact, isconcrete = instanceof_tfunc(argtypes[1], true) xty = widenconst(argtypes[2]) return isconcrete && isprimitivetype(ty) && isprimitivetype(xty) && Core.sizeof(ty) === Core.sizeof(xty) end @@ -2580,12 +2583,12 @@ function intrinsic_nothrow(f::IntrinsicFunction, argtypes::Vector{Any}) Intrinsics.sitofp, Intrinsics.fptrunc, Intrinsics.fpext) # If !isconcrete, `ty` may be Union{} at runtime even if we have # isprimitivetype(ty). - ty, isexact, isconcrete = instanceof_tfunc(argtypes[1]) + ty, isexact, isconcrete = instanceof_tfunc(argtypes[1], true) xty = widenconst(argtypes[2]) return isconcrete && isprimitivetype(ty) && isprimitivetype(xty) end if f === Intrinsics.have_fma - ty, isexact, isconcrete = instanceof_tfunc(argtypes[1]) + ty, isexact, isconcrete = instanceof_tfunc(argtypes[1], true) return isconcrete && isprimitivetype(ty) end # The remaining intrinsics are math/bits/comparison intrinsics. They work on all @@ -2775,7 +2778,7 @@ function _hasmethod_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, sv else return CallMeta(Any, Effects(), NoCallInfo()) end - (types, isexact, isconcrete, istype) = instanceof_tfunc(argtype_by_index(argtypes, typeidx)) + (types, isexact, isconcrete, istype) = instanceof_tfunc(argtype_by_index(argtypes, typeidx), false) isexact || return CallMeta(Bool, Effects(), NoCallInfo()) unwrapped = unwrap_unionall(types) if types === Bottom || !(unwrapped isa DataType) || unwrapped.name !== Tuple.name diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 880ce479a48c8d..f246289b5bea0d 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -921,8 +921,13 @@ result_is_constabi(interp::AbstractInterpreter, run_optimizer::Bool, result::Inf run_optimizer && may_discard_trees(interp) && is_result_constabi_eligible(result) # compute an inferred AST and return type -function typeinf_code(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, run_optimizer::Bool) - frame = typeinf_frame(interp, method, atype, sparams, run_optimizer) +typeinf_code(interp::AbstractInterpreter, match::MethodMatch, run_optimizer::Bool) = + typeinf_code(interp, specialize_method(match), run_optimizer) +typeinf_code(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, + run_optimizer::Bool) = + typeinf_code(interp, specialize_method(method, atype, sparams), run_optimizer) +function typeinf_code(interp::AbstractInterpreter, mi::MethodInstance, run_optimizer::Bool) + frame = typeinf_frame(interp, mi, run_optimizer) frame === nothing && return nothing, Any is_inferred(frame) || return nothing, Any if result_is_constabi(interp, run_optimizer, frame.result) @@ -935,25 +940,26 @@ function typeinf_code(interp::AbstractInterpreter, method::Method, @nospecialize end """ - typeinf_ircode( - interp::AbstractInterpreter, - method::Method, - atype, - sparams::SimpleVector, - optimize_until::Union{Integer,AbstractString,Nothing}, - ) -> (ir::Union{IRCode,Nothing}, returntype::Type) + typeinf_ircode(interp::AbstractInterpreter, match::MethodMatch, + optimize_until::Union{Integer,AbstractString,Nothing}) -> (ir::Union{IRCode,Nothing}, returntype::Type) + typeinf_ircode(interp::AbstractInterpreter, + method::Method, atype, sparams::SimpleVector, + optimize_until::Union{Integer,AbstractString,Nothing}) -> (ir::Union{IRCode,Nothing}, returntype::Type) + typeinf_ircode(interp::AbstractInterpreter, mi::MethodInstance, + optimize_until::Union{Integer,AbstractString,Nothing}) -> (ir::Union{IRCode,Nothing}, returntype::Type) Infer a `method` and return an `IRCode` with inferred `returntype` on success. """ -function typeinf_ircode( - interp::AbstractInterpreter, - method::Method, - @nospecialize(atype), - sparams::SimpleVector, - optimize_until::Union{Integer,AbstractString,Nothing}, -) +typeinf_ircode(interp::AbstractInterpreter, match::MethodMatch, + optimize_until::Union{Integer,AbstractString,Nothing}) = + typeinf_ircode(interp, specialize_method(match), optimize_until) +typeinf_ircode(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, + optimize_until::Union{Integer,AbstractString,Nothing}) = + typeinf_ircode(interp, specialize_method(method, atype, sparams), optimize_until) +function typeinf_ircode(interp::AbstractInterpreter, mi::MethodInstance, + optimize_until::Union{Integer,AbstractString,Nothing}) start_time = ccall(:jl_typeinf_timing_begin, UInt64, ()) - frame = typeinf_frame(interp, method, atype, sparams, false) + frame = typeinf_frame(interp, mi, false) if frame === nothing ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) return nothing, Any @@ -967,10 +973,11 @@ function typeinf_ircode( end # compute an inferred frame -function typeinf_frame(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, run_optimizer::Bool) - mi = specialize_method(method, atype, sparams)::MethodInstance - return typeinf_frame(interp, mi, run_optimizer) -end +typeinf_frame(interp::AbstractInterpreter, match::MethodMatch, run_optimizer::Bool) = + typeinf_frame(interp, specialize_method(match), run_optimizer) +typeinf_frame(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, + run_optimizer::Bool) = + typeinf_frame(interp, specialize_method(method, atype, sparams), run_optimizer) function typeinf_frame(interp::AbstractInterpreter, mi::MethodInstance, run_optimizer::Bool) start_time = ccall(:jl_typeinf_timing_begin, UInt64, ()) result = InferenceResult(mi, typeinf_lattice(interp)) diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index 324f2b600cc445..df6022609d6124 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -607,7 +607,7 @@ end if ti === widev return v end - valid_as_lattice(ti) || return Bottom + valid_as_lattice(ti, true) || return Bottom if widev <: Tuple new_fields = Vector{Any}(undef, length(v.fields)) for i = 1:length(new_fields) @@ -631,7 +631,7 @@ end return v end ti = typeintersect(widev, t) - valid_as_lattice(ti) || return Bottom + valid_as_lattice(ti, true) || return Bottom return PartialOpaque(ti, v.env, v.parent, v.source) end return tmeet(widenlattice(lattice), v, t) diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index 22952961b2484b..a4499e003cf2c5 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -95,12 +95,13 @@ end has_concrete_subtype(d::DataType) = d.flags & 0x0020 == 0x0020 # n.b. often computed only after setting the type and layout fields -# determine whether x is a valid lattice element tag +# determine whether x is a valid lattice element # For example, Type{v} is not valid if v is a value -# Accepts TypeVars also, since it assumes the user will rewrap it correctly -function valid_as_lattice(@nospecialize(x)) +# Accepts TypeVars and has_free_typevar also, since it assumes the user will rewrap it correctly +# If astag is true, then also requires that it be a possible type tag for a valid object +function valid_as_lattice(@nospecialize(x), astag::Bool=false) x === Bottom && false - x isa TypeVar && return valid_as_lattice(x.ub) + x isa TypeVar && return valid_as_lattice(x.ub, astag) x isa UnionAll && (x = unwrap_unionall(x)) if x isa Union # the Union constructor ensures this (and we'll recheck after @@ -111,6 +112,9 @@ function valid_as_lattice(@nospecialize(x)) if isType(x) p = x.parameters[1] p isa Type || p isa TypeVar || return false + elseif astag && isstructtype(x) + datatype_fieldtypes(x) # force computation of has_concrete_subtype to be updated now + return has_concrete_subtype(x) end return true end @@ -149,6 +153,7 @@ function compatible_vatuple(a::DataType, b::DataType) end # return an upper-bound on type `a` with type `b` removed +# and also any contents that are not valid type tags on any objects # such that `return <: a` && `Union{return, b} == Union{a, b}` function typesubtract(@nospecialize(a), @nospecialize(b), max_union_splitting::Int) if a <: b && isnotbrokensubtype(a, b) @@ -158,8 +163,8 @@ function typesubtract(@nospecialize(a), @nospecialize(b), max_union_splitting::I if isa(ua, Union) uua = typesubtract(rewrap_unionall(ua.a, a), b, max_union_splitting) uub = typesubtract(rewrap_unionall(ua.b, a), b, max_union_splitting) - return Union{valid_as_lattice(uua) ? uua : Union{}, - valid_as_lattice(uub) ? uub : Union{}} + return Union{valid_as_lattice(uua, true) ? uua : Union{}, + valid_as_lattice(uub, true) ? uub : Union{}} elseif a isa DataType ub = unwrap_unionall(b) if ub isa DataType diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 28ef5df6d619c3..4ca384361c4e34 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -535,7 +535,7 @@ function docm(source::LineNumberNode, mod::Module, ex) elseif isassigned(Base.REPL_MODULE_REF) # TODO: this is a shim to continue to allow `@doc` for looking up docstrings REPL = Base.REPL_MODULE_REF[] - return REPL.lookup_doc(ex) + return invokelatest(REPL.lookup_doc, ex) end return nothing end diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 5ed4c70da6e061..19acd296c4082a 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -66,6 +66,9 @@ kw"export" public API of the module . For example: `public foo` indicates that the name `foo` is public, without making it available when [`using`](@ref) the module. See the [manual section about modules](@ref modules) for details. + +!!! compat "Julia 1.11" + The public keyword and notion of publicity were added in Julia 1.11. """ kw"public" @@ -111,7 +114,7 @@ kw"abstract type", kw"abstract" `module` declares a [`Module`](@ref), which is a separate global variable workspace. Within a module, you can control which names from other modules are visible (via importing), and -specify which of your names are intended to be public (via exporting). +specify which of your names are intended to be public (via `export` and `public`). Modules allow you to create top-level definitions without worrying about name conflicts when your code is used together with somebody else’s. See the [manual section about modules](@ref modules) for more details. diff --git a/base/experimental.jl b/base/experimental.jl index af67d9e0193784..133171b78271da 100644 --- a/base/experimental.jl +++ b/base/experimental.jl @@ -92,6 +92,10 @@ are complete, or at least one of them has errored. The first exception is immedi rethrown. It is the responsibility of the user to cancel any still-running operations during error handling. +!!! Note + This is different to [`@sync`](@ref) in that errors from wrapped tasks are thrown immediately, + potentially before all tasks have returned. + !!! Note This interface is experimental and subject to change or removal without notice. """ diff --git a/base/loading.jl b/base/loading.jl index 5ebb5dc1202823..df0252f3d695b6 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1818,9 +1818,6 @@ function __require_prelocked(uuidkey::PkgId, env=nothing) insert_extension_triggers(uuidkey) # After successfully loading, notify downstream consumers run_package_callbacks(uuidkey) - if uuidkey == REPL_PKGID - REPL_MODULE_REF[] = newm - end else newm = root_module(uuidkey) end @@ -3130,7 +3127,6 @@ end _f = fixup_stdlib_path(f) if isfile(_f) && startswith(_f, Sys.STDLIB) # mtime is changed by extraction - @debug "Skipping mtime check for file $f used by $cachefile, since it is a stdlib" continue end @debug "Rejecting stale cache file $cachefile because file $f does not exist" diff --git a/base/namedtuple.jl b/base/namedtuple.jl index 37514ec4500136..36ec56a4e0a2a2 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -285,8 +285,9 @@ end return Tuple{Any[ fieldtype(sym_in(names[n], bn) ? b : a, names[n]) for n in 1:length(names) ]...} end -@assume_effects :foldable function merge_fallback(@nospecialize(a::NamedTuple), @nospecialize(b::NamedTuple), - @nospecialize(an::Tuple{Vararg{Symbol}}), @nospecialize(bn::Tuple{Vararg{Symbol}})) +@assume_effects :foldable function merge_fallback(a::NamedTuple, b::NamedTuple, + an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}}) + @nospecialize names = merge_names(an, bn) types = merge_types(names, typeof(a), typeof(b)) n = length(names) @@ -298,6 +299,10 @@ end _new_NamedTuple(NamedTuple{names, types}, (A...,)) end +# This is `Experimental.@max_methods 4 function merge end`, which is not +# defined at this point in bootstrap. +typeof(function merge end).name.max_methods = UInt8(4) + """ merge(a::NamedTuple, bs::NamedTuple...) diff --git a/base/reflection.jl b/base/reflection.jl index 6f4ce493ff7ae8..ac3dfeb4af6472 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -124,6 +124,9 @@ Returns whether a symbol is marked as public in a module. Exported symbols are considered public. +!!! compat "Julia 1.11" + This function and the notion of publicity were added in Julia 1.11. + See also: [`isexported`](@ref), [`names`](@ref) ```jldoctest @@ -1474,15 +1477,6 @@ function may_invoke_generator(method::Method, @nospecialize(atype), sparams::Sim return true end -# give a decent error message if we try to instantiate a staged function on non-leaf types -function func_for_method_checked(m::Method, @nospecialize(types), sparams::SimpleVector) - if isdefined(m, :generator) && !may_invoke_generator(m, types, sparams) - error("cannot call @generated function `", m, "` ", - "with abstract argument types: ", types) - end - return m -end - """ code_typed(f, types; kw...) @@ -1565,10 +1559,9 @@ function code_typed_by_type(@nospecialize(tt::Type); asts = [] for match in matches match = match::Core.MethodMatch - meth = func_for_method_checked(match.method, tt, match.sparams) - (code, ty) = Core.Compiler.typeinf_code(interp, meth, match.spec_types, match.sparams, optimize) + (code, ty) = Core.Compiler.typeinf_code(interp, match, optimize) if code === nothing - push!(asts, meth => Any) + push!(asts, match.method => Any) else debuginfo === :none && remove_linenums!(code) push!(asts, code => ty) @@ -1662,16 +1655,9 @@ function code_ircode_by_type( asts = [] for match in matches match = match::Core.MethodMatch - meth = func_for_method_checked(match.method, tt, match.sparams) - (code, ty) = Core.Compiler.typeinf_ircode( - interp, - meth, - match.spec_types, - match.sparams, - optimize_until, - ) + (code, ty) = Core.Compiler.typeinf_ircode(interp, match, optimize_until) if code === nothing - push!(asts, meth => Any) + push!(asts, match.method => Any) else push!(asts, code => ty) end @@ -1732,8 +1718,7 @@ function return_types(@nospecialize(f), @nospecialize(types=default_tt(f)); matches = _methods_by_ftype(tt, #=lim=#-1, world)::Vector for match in matches match = match::Core.MethodMatch - meth = func_for_method_checked(match.method, types, match.sparams) - ty = Core.Compiler.typeinf_type(interp, meth, match.spec_types, match.sparams) + ty = Core.Compiler.typeinf_type(interp, match.method, match.spec_types, match.sparams) push!(rts, something(ty, Any)) end return rts @@ -1801,8 +1786,7 @@ function infer_effects(@nospecialize(f), @nospecialize(types=default_tt(f)); end for match in matches.matches match = match::Core.MethodMatch - frame = Core.Compiler.typeinf_frame(interp, - match.method, match.spec_types, match.sparams, #=run_optimizer=#true) + frame = Core.Compiler.typeinf_frame(interp, match, #=run_optimizer=#true) frame === nothing && return Core.Compiler.Effects() effects = Core.Compiler.merge_effects(effects, frame.result.ipo_effects) end @@ -1830,9 +1814,8 @@ function print_statement_costs(io::IO, @nospecialize(tt::Type); cst = Int[] for match in matches match = match::Core.MethodMatch - meth = func_for_method_checked(match.method, tt, match.sparams) - println(io, meth) - (code, ty) = Core.Compiler.typeinf_code(interp, meth, match.spec_types, match.sparams, true) + println(io, match.method) + (code, ty) = Core.Compiler.typeinf_code(interp, match, true) if code === nothing println(io, " inference not successful") else diff --git a/base/show.jl b/base/show.jl index fcf5e87900dbd2..079bf5a423cc0d 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1084,29 +1084,68 @@ function show_datatype(io::IO, x::DataType, wheres::Vector{TypeVar}=TypeVar[]) # Print tuple types with homogeneous tails longer than max_n compactly using `NTuple` or `Vararg` if istuple + if n == 0 + print(io, "Tuple{}") + return + end + + # find the length of the homogeneous tail max_n = 3 taillen = 1 - for i in (n-1):-1:1 - if parameters[i] === parameters[n] - taillen += 1 + pn = parameters[n] + fulln = n + vakind = :none + vaN = 0 + if pn isa Core.TypeofVararg + if isdefined(pn, :N) + vaN = pn.N + if vaN isa Int + taillen = vaN + fulln += taillen - 1 + vakind = :fixed + else + vakind = :bound + end else - break + vakind = :unbound + end + pn = unwrapva(pn) + end + if !(pn isa TypeVar || pn isa Type) + # prefer Tuple over NTuple if it contains something other than types + # (e.g. if the user has switched the N and T accidentally) + taillen = 0 + elseif vakind === :none || vakind === :fixed + for i in (n-1):-1:1 + if parameters[i] === pn + taillen += 1 + else + break + end end end - if n == taillen > max_n - print(io, "NTuple{", n, ", ") - show(io, parameters[1]) + + # prefer NTuple over Tuple if it is a Vararg without a fixed length + # and prefer Tuple for short lists of elements + if (vakind == :bound && n == 1 == taillen) || (vakind === :fixed && taillen == fulln > max_n) || + (vakind === :none && taillen == fulln > max_n) + print(io, "NTuple{") + vakind === :bound ? show(io, vaN) : print(io, fulln) + print(io, ", ") + show(io, pn) print(io, "}") else print(io, "Tuple{") - for i = 1:(taillen > max_n ? n-taillen : n) + headlen = (taillen > max_n ? fulln - taillen : fulln) + for i = 1:headlen i > 1 && print(io, ", ") - show(io, parameters[i]) + show(io, vakind === :fixed && i >= n ? pn : parameters[i]) end - if taillen > max_n - print(io, ", Vararg{") - show(io, parameters[n]) - print(io, ", ", taillen, "}") + if headlen < fulln + headlen > 0 && print(io, ", ") + print(io, "Vararg{") + show(io, pn) + print(io, ", ", fulln - headlen, "}") end print(io, "}") end @@ -1778,9 +1817,8 @@ end function show_unquoted(io::IO, ex::SlotNumber, ::Int, ::Int) slotid = ex.id slotnames = get(io, :SOURCE_SLOTNAMES, false) - if (isa(slotnames, Vector{String}) && - slotid <= length(slotnames::Vector{String})) - print(io, (slotnames::Vector{String})[slotid]) + if isa(slotnames, Vector{String}) && slotid ≤ length(slotnames) + print(io, slotnames[slotid]) else print(io, "_", slotid) end diff --git a/base/stat.jl b/base/stat.jl index 81f9dcfd201915..43c51b0b911dff 100644 --- a/base/stat.jl +++ b/base/stat.jl @@ -185,7 +185,7 @@ The fields of the structure are: | gid | The group id of the file owner | | rdev | If this file refers to a device, the ID of the device it refers to | | blksize | The file-system preferred block size for the file | -| blocks | The number of such blocks allocated | +| blocks | The number of 512-byte blocks allocated | | mtime | Unix timestamp of when the file was last modified | | ctime | Unix timestamp of when the file's metadata was changed | diff --git a/base/sysimg.jl b/base/sysimg.jl index 8c289bf501618e..1bdbe60479e914 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -31,43 +31,19 @@ let # Run with the `--exclude-jlls` option to filter out all JLL packages stdlibs = [ # No dependencies - :ArgTools, - :Artifacts, - :Base64, - :CRC32c, - :FileWatching, - :Libdl, - :Logging, - :Mmap, - :NetworkOptions, - :SHA, - :Serialization, - :Sockets, - :Unicode, + :FileWatching, # used by loading.jl -- implicit assumption that init runs + :Libdl, # Transitive through LinAlg + :Artifacts, # Transitive through LinAlg + :SHA, # transitive through Random + :Sockets, # used by stream.jl + + # Transitive through LingAlg + # OpenBLAS_jll + # libblastrampoline_jll # 1-depth packages - :LinearAlgebra, - :Markdown, - :Printf, - :Random, - :Tar, - - # 2-depth packages - :Dates, - :Future, - :InteractiveUtils, - :LibGit2, - :UUIDs, - - # 3-depth packages - :REPL, - :TOML, - - # 4-depth packages - :LibCURL, - - # 5-depth packages - :Downloads, + :LinearAlgebra, # Commits type-piracy and GEMM + :Random, # Can't be removed due to rand being exported by Base ] # PackageCompiler can filter out stdlibs so it can be empty maxlen = maximum(textwidth.(string.(stdlibs)); init=0) diff --git a/base/terminfo.jl b/base/terminfo.jl index 7bd3151cdb42f1..ff7e6fab7f1f78 100644 --- a/base/terminfo.jl +++ b/base/terminfo.jl @@ -95,8 +95,8 @@ function read(data::IO, ::Type{TermInfoRaw}) throw(ArgumentError("Terminfo did not contain a null byte after the flag section, expected to position the start of the numbers section on an even byte")) end # Numbers, Strings, Table - numbers = reinterpret(NumInt, read(data, numbers_count * sizeof(NumInt))) .|> ltoh - string_indices = reinterpret(UInt16, read(data, string_count * sizeof(UInt16))) .|> ltoh + numbers = map(ltoh, reinterpret(NumInt, read(data, numbers_count * sizeof(NumInt)))) + string_indices = map(ltoh, reinterpret(UInt16, read(data, string_count * sizeof(UInt16)))) strings_table = read(data, table_bytes) strings = map(string_indices) do idx if idx ∉ (0xffff, 0xfffe) @@ -107,7 +107,7 @@ function read(data::IO, ::Type{TermInfoRaw}) end end TermInfoRaw(term_names, flags, numbers, strings, - if !eof(data) extendedterminfo(data; NumInt) end) + if !eof(data) extendedterminfo(data, NumInt) end) end """ @@ -119,7 +119,7 @@ This will accept any terminfo content that conforms with `term(5)`. See also: `read(::IO, ::Type{TermInfoRaw})` """ -function extendedterminfo(data::IO; NumInt::Union{Type{UInt16}, Type{UInt32}}) +function extendedterminfo(data::IO, NumInt::Union{Type{UInt16}, Type{UInt32}}) # Extended info if position(data) % 2 != 0 0x00 == read(data, UInt8) || @@ -138,12 +138,15 @@ function extendedterminfo(data::IO; NumInt::Union{Type{UInt16}, Type{UInt32}}) throw(ArgumentError("Terminfo did not contain a null byte after the extended flag section, expected to position the start of the numbers section on an even byte")) end numbers = map(n -> Int(ltoh(n)), reinterpret(NumInt, read(data, numbers_count * sizeof(NumInt)))) - table_indices = reinterpret(UInt16, read(data, table_count * sizeof(UInt16))) .|> ltoh + table_indices = map(ltoh, reinterpret(UInt16, read(data, table_count * sizeof(UInt16)))) table_strings = [String(readuntil(data, 0x00)) for _ in 1:length(table_indices)] + info = Dict{Symbol, Union{Bool, Int, String}}() strings = table_strings[1:string_count] - labels = Symbol.(table_strings[string_count+1:end]) - Dict{Symbol, Union{Bool, Int, String}}( - labels .=> vcat(flags, numbers, strings)) + labels = table_strings[string_count+1:end] + for (label, val) in zip(labels, vcat(flags, numbers, strings)) + info[Symbol(label)] = val + end + return info end """ @@ -178,7 +181,7 @@ function TermInfo(raw::TermInfoRaw) Symbol[] end TermInfo(raw.names, length(raw.flags), - raw.numbers .!= typemax(eltype(raw.numbers)), + map(n-> n != typemax(typeof(n)), raw.numbers), map(!isnothing, raw.strings), extensions, capabilities) end diff --git a/base/tuple.jl b/base/tuple.jl index f01d2655da7c0c..9c03274f190bf9 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -42,9 +42,9 @@ get(f::Callable, t::Tuple, i::Integer) = i in 1:length(t) ? getindex(t, i) : f() # returns new tuple; N.B.: becomes no-op if `i` is out-of-bounds """ - setindex(c::Tuple, v, i::Integer) + setindex(t::Tuple, v, i::Integer) -Creates a new tuple similar to `x` with the value at index `i` set to `v`. +Creates a new tuple similar to `t` with the value at index `i` set to `v`. Throws a `BoundsError` when out of bounds. # Examples diff --git a/base/util.jl b/base/util.jl index 0a983d454b7953..fe04eaf04bd47f 100644 --- a/base/util.jl +++ b/base/util.jl @@ -700,7 +700,9 @@ function runtests(tests = ["all"]; ncores::Int = ceil(Int, Sys.CPU_THREADS / 2), catch buf = PipeBuffer() original_load_path = copy(Base.LOAD_PATH); empty!(Base.LOAD_PATH); pushfirst!(Base.LOAD_PATH, "@stdlib") - Base.require(Base, :InteractiveUtils).versioninfo(buf) + let InteractiveUtils = Base.require(Base, :InteractiveUtils) + @invokelatest InteractiveUtils.versioninfo(buf) + end empty!(Base.LOAD_PATH); append!(Base.LOAD_PATH, original_load_path) error("A test has failed. Please submit a bug report (https://github.com/JuliaLang/julia/issues)\n" * "including error messages above and the output of versioninfo():\n$(read(buf, String))") diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 7df9992c41f169..23320df96e7302 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -33,6 +33,19 @@ UP_ARROW = "\e[A" DOWN_ARROW = "\e[B" hardcoded_precompile_statements = """ +precompile(Base.unsafe_string, (Ptr{UInt8},)) +precompile(Base.unsafe_string, (Ptr{Int8},)) + +# loading.jl +precompile(Base.__require_prelocked, (Base.PkgId, Nothing)) +precompile(Base._require, (Base.PkgId, Nothing)) + +# REPL +precompile(isequal, (String, String)) +precompile(Base.check_open, (Base.TTY,)) +precompile(Base.getproperty, (Base.TTY, Symbol)) +precompile(write, (Base.TTY, String)) + # used by Revise.jl precompile(Tuple{typeof(Base.parse_cache_header), String}) precompile(Base.read_dependency_src, (String, String)) @@ -66,30 +79,6 @@ for T in (Float16, Float32, Float64), IO in (IOBuffer, IOContext{IOBuffer}, Base hardcoded_precompile_statements *= "precompile(Tuple{typeof(show), $IO, $T})\n" end -repl_script = """ -2+2 -print("") -printstyled("a", "b") -display([1]) -display([1 2; 3 4]) -foo(x) = 1 -@time @eval foo(1) -; pwd -$CTRL_C -$CTRL_R$CTRL_C -? reinterpret -using Ra\t$CTRL_C -\\alpha\t$CTRL_C -\e[200~paste here ;)\e[201~"$CTRL_C -$UP_ARROW$DOWN_ARROW$CTRL_C -123\b\b\b$CTRL_C -\b\b$CTRL_C -f(x) = x03 -f(1,2) -[][1] -cd("complet_path\t\t$CTRL_C -""" - precompile_script = """ # NOTE: these were moved to the end of Base.jl. TODO: move back here. # # Used by Revise & its dependencies @@ -127,14 +116,6 @@ precompile_script = """ julia_exepath() = joinpath(Sys.BINDIR, Base.julia_exename()) -have_repl = haskey(Base.loaded_modules, - Base.PkgId(Base.UUID("3fa0cd96-eef1-5676-8a61-b3b8758bbffb"), "REPL")) -if have_repl - hardcoded_precompile_statements *= """ - precompile(Tuple{typeof(getproperty), REPL.REPLBackend, Symbol}) - """ -end - Artifacts = get(Base.loaded_modules, Base.PkgId(Base.UUID("56f22d72-fd6d-98f1-02f0-08ddc0907c33"), "Artifacts"), nothing) @@ -173,27 +154,12 @@ if Libdl !== nothing """ end -InteractiveUtils = get(Base.loaded_modules, - Base.PkgId(Base.UUID("b77e0a4c-d291-57a0-90e8-8db25a27a240"), "InteractiveUtils"), - nothing) -if InteractiveUtils !== nothing - repl_script *= """ - @time_imports using Random - """ -end - -const JULIA_PROMPT = "julia> " -const SHELL_PROMPT = "shell> " -const HELP_PROMPT = "help?> " - # Printing the current state let global print_state print_lk = ReentrantLock() status = Dict{String, String}( "step1" => "W", - "step2" => "W", - "repl" => "0/0", "step3" => "W", "clock" => "◐", ) @@ -214,8 +180,6 @@ let isempty(args) || push!(status, args...) print("\r└ Collect (Basic: ") print_status("step1") - print(", REPL ", status["repl"], ": ") - print_status("step2") print(") => Execute ") print_status("step3") end @@ -230,7 +194,8 @@ procenv = Dict{String,Any}( "JULIA_PROJECT" => nothing, # remove from environment "JULIA_LOAD_PATH" => "@stdlib", "JULIA_DEPOT_PATH" => Sys.iswindows() ? ";" : ":", - "TERM" => "") + "TERM" => "", + "JULIA_FALLBACK_REPL" => "true") generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printed start_time = time_ns() @@ -238,7 +203,6 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe # Extract the precompile statements from the precompile file statements_step1 = Channel{String}(Inf) - statements_step2 = Channel{String}(Inf) # From hardcoded statements for statement in split(hardcoded_precompile_statements::String, '\n') @@ -253,7 +217,7 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe anim_chars = ["◐","◓","◑","◒"] current = 1 if fancyprint - while isopen(statements_step2) || !isempty(statements_step2) + while isopen(statements_step1) || !isempty(statements_step1) print_state("clock" => anim_chars[current]) wait(t) current = current == 4 ? 1 : current + 1 @@ -297,105 +261,9 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe print_state("step1" => "F$n_step1") return :ok end + Base.errormonitor(step1) !PARALLEL_PRECOMPILATION && wait(step1) - step2 = @async mktemp() do precompile_file, precompile_file_h - print_state("step2" => "R") - # Collect statements from running a REPL process and replaying our REPL script - touch(precompile_file) - pts, ptm = open_fake_pty() - if have_repl - cmdargs = `-e 'import REPL; REPL.Terminals.is_precompiling[] = true'` - else - cmdargs = `-e nothing` - end - p = run(addenv(addenv(```$(julia_exepath()) -O0 --trace-compile=$precompile_file --sysimage $sysimg - --cpu-target=native --startup-file=no --color=yes -i $cmdargs```, procenv), - "JULIA_PKG_PRECOMPILE_AUTO" => "0"), - pts, pts, pts; wait=false) - Base.close_stdio(pts) - # Prepare a background process to copy output from process until `pts` is closed - output_copy = Base.BufferStream() - tee = @async try - while !eof(ptm) - l = readavailable(ptm) - write(debug_output, l) - Sys.iswindows() && (sleep(0.1); yield(); yield()) # workaround hang - probably a libuv issue? - write(output_copy, l) - end - catch ex - if !(ex isa Base.IOError && ex.code == Base.UV_EIO) - rethrow() # ignore EIO on ptm after pts dies - end - finally - close(output_copy) - close(ptm) - end - repl_inputter = @async begin - # wait for the definitive prompt before start writing to the TTY - readuntil(output_copy, JULIA_PROMPT) - sleep(0.1) - readavailable(output_copy) - # Input our script - if have_repl - precompile_lines = split(repl_script::String, '\n'; keepempty=false) - curr = 0 - for l in precompile_lines - sleep(0.1) - curr += 1 - print_state("repl" => "$curr/$(length(precompile_lines))") - # consume any other output - bytesavailable(output_copy) > 0 && readavailable(output_copy) - # push our input - write(debug_output, "\n#### inputting statement: ####\n$(repr(l))\n####\n") - write(ptm, l, "\n") - readuntil(output_copy, "\n") - # wait for the next prompt-like to appear - readuntil(output_copy, "\n") - strbuf = "" - while !eof(output_copy) - strbuf *= String(readavailable(output_copy)) - occursin(JULIA_PROMPT, strbuf) && break - occursin(SHELL_PROMPT, strbuf) && break - occursin(HELP_PROMPT, strbuf) && break - sleep(0.1) - end - end - end - write(ptm, "exit()\n") - wait(tee) - success(p) || Base.pipeline_error(p) - close(ptm) - write(debug_output, "\n#### FINISHED ####\n") - end - - n_step2 = 0 - precompile_copy = Base.BufferStream() - buffer_reader = @async for statement in eachline(precompile_copy) - print_state("step2" => "R$n_step2") - push!(statements_step2, statement) - n_step2 += 1 - end - - open(precompile_file, "r") do io - while true - # We need to allways call eof(io) for bytesavailable(io) to work - eof(io) && istaskdone(repl_inputter) && eof(io) && break - if bytesavailable(io) == 0 - sleep(0.1) - continue - end - write(precompile_copy, readavailable(io)) - end - end - close(precompile_copy) - wait(buffer_reader) - close(statements_step2) - print_state("step2" => "F$n_step2") - return :ok - end - !PARALLEL_PRECOMPILATION && wait(step2) - # Create a staging area where all the loaded packages are available PrecompileStagingArea = Module() for (_pkgid, _mod) in Base.loaded_modules @@ -408,7 +276,7 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe # Make statements unique statements = Set{String}() # Execute the precompile statements - for sts in [statements_step1, statements_step2], statement in sts + for sts in [statements_step1,], statement in sts # Main should be completely clean occursin("Main.", statement) && continue Base.in!(statement, statements) && continue @@ -447,7 +315,6 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe n_succeeded > (have_repl ? 650 : 90) || @warn "Only $n_succeeded precompile statements" fetch(step1) == :ok || throw("Step 1 of collecting precompiles failed.") - fetch(step2) == :ok || throw("Step 2 of collecting precompiles failed.") tot_time = time_ns() - start_time println("Precompilation complete. Summary:") diff --git a/deps/checksums/Pkg-3960c692bd4dd2b2eafc7699e8d0744f90756812.tar.gz/md5 b/deps/checksums/Pkg-3960c692bd4dd2b2eafc7699e8d0744f90756812.tar.gz/md5 new file mode 100644 index 00000000000000..7348a59594c66a --- /dev/null +++ b/deps/checksums/Pkg-3960c692bd4dd2b2eafc7699e8d0744f90756812.tar.gz/md5 @@ -0,0 +1 @@ +42eb123b3f0484f253e8344dadff0ed3 diff --git a/deps/checksums/Pkg-3960c692bd4dd2b2eafc7699e8d0744f90756812.tar.gz/sha512 b/deps/checksums/Pkg-3960c692bd4dd2b2eafc7699e8d0744f90756812.tar.gz/sha512 new file mode 100644 index 00000000000000..5f55fc9a7eb892 --- /dev/null +++ b/deps/checksums/Pkg-3960c692bd4dd2b2eafc7699e8d0744f90756812.tar.gz/sha512 @@ -0,0 +1 @@ +21712978ebc63842ae8c92e3621d5de802a7f90a021cbbf994570ebc2b492852c4b00616118b2b8dc1f7f488e66f527baa56de2154280711f1562606fa7ab718 diff --git a/deps/checksums/Pkg-cf0019fbbaf3ab1e606d4e973c1908bbd899a9d5.tar.gz/md5 b/deps/checksums/Pkg-cf0019fbbaf3ab1e606d4e973c1908bbd899a9d5.tar.gz/md5 deleted file mode 100644 index 4a3dd69e57a6bc..00000000000000 --- a/deps/checksums/Pkg-cf0019fbbaf3ab1e606d4e973c1908bbd899a9d5.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -e640498e569782d09fbb26b9f2d861f3 diff --git a/deps/checksums/Pkg-cf0019fbbaf3ab1e606d4e973c1908bbd899a9d5.tar.gz/sha512 b/deps/checksums/Pkg-cf0019fbbaf3ab1e606d4e973c1908bbd899a9d5.tar.gz/sha512 deleted file mode 100644 index 320af8b56c72d6..00000000000000 --- a/deps/checksums/Pkg-cf0019fbbaf3ab1e606d4e973c1908bbd899a9d5.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -6cb6ac1baaa34f2dcd752d2cf0c8d3ca644d336497f16fcd982d95519757de348bb4ae50013680d894810092769b803c66eb11e23291f336d766d77eba5cd823 diff --git a/deps/checksums/unwind b/deps/checksums/unwind index a3f80c50b3be13..fcb0ba474e1bac 100644 --- a/deps/checksums/unwind +++ b/deps/checksums/unwind @@ -1,26 +1,26 @@ -LibUnwind.v1.5.0+5.aarch64-linux-gnu.tar.gz/md5/10395299ac6a2b102e5ff83f92216c24 -LibUnwind.v1.5.0+5.aarch64-linux-gnu.tar.gz/sha512/9a42751de3766c91173c3a35adc48920ddce3fb80e79d76a8b6e51be368b3fa307a5911a5a577bcb44bf4c8d84a2a61af2bb4717918be50173de64f86e8eab04 -LibUnwind.v1.5.0+5.aarch64-linux-musl.tar.gz/md5/5d188ea86a5e52c4e14e54b801bc27e8 -LibUnwind.v1.5.0+5.aarch64-linux-musl.tar.gz/sha512/d89eb00f8d2f307235d0e794ff0b8e8a2cb016742210ba66938ecf3f6e158a0feada2e4ea167d9159ae65a298721e9678374062e24822c66d2c20d5c22b14d90 -LibUnwind.v1.5.0+5.armv6l-linux-gnueabihf.tar.gz/md5/924948e2f8562f167b3ccd10eae4bed3 -LibUnwind.v1.5.0+5.armv6l-linux-gnueabihf.tar.gz/sha512/24b8307e02e4168f3683d18f343680053ea7645e4135e6db9ed52a8991d60ccaa6b78dd66e1aa9e3505c3825a19d7c4fd007362ff1191cf86302c9e2c6171d8d -LibUnwind.v1.5.0+5.armv6l-linux-musleabihf.tar.gz/md5/64769f744a1d645026becf67f504350e -LibUnwind.v1.5.0+5.armv6l-linux-musleabihf.tar.gz/sha512/242c76f349743c6be387c13faae15f588c8a0f656a505df8801c51c5d8744983a324ea5ddb32de146ad6289a42356fabff945074e4bc2c79e0665f641d4db745 -LibUnwind.v1.5.0+5.armv7l-linux-gnueabihf.tar.gz/md5/aeeafd36bcc42624218a6975a2ae2317 -LibUnwind.v1.5.0+5.armv7l-linux-gnueabihf.tar.gz/sha512/41b7cf03467ac36d8987f41123399255bf26cb44187c2eea7deeb3a7235f29eacd97077b279fb546c97b17708d9d60bc26489ec299b4d9b14d0f78b49b5853b5 -LibUnwind.v1.5.0+5.armv7l-linux-musleabihf.tar.gz/md5/cfb1ad3a96b0d8c3769affc2a0e57569 -LibUnwind.v1.5.0+5.armv7l-linux-musleabihf.tar.gz/sha512/1686d4240d18493a5c9beeb2947702e6f347888d2c9767a25e5e02fac025801503d45b99476d3d3c9e0d79b078402e64c1497d7447a74a4c58513adfe383ec55 -LibUnwind.v1.5.0+5.i686-linux-gnu.tar.gz/md5/99fc0e135bdb78da987727f05a838bde -LibUnwind.v1.5.0+5.i686-linux-gnu.tar.gz/sha512/793aa4f7a6ecd070a28ba0893de41b8ddf83751c9346b767053ec81da76320c56ea170954727cbdfedbb031dfa208edfbbee695798429b23fb142039957a58c2 -LibUnwind.v1.5.0+5.i686-linux-musl.tar.gz/md5/1bbe1b8467a0c818e764a0944fe90cda -LibUnwind.v1.5.0+5.i686-linux-musl.tar.gz/sha512/3e3053ad7f482e9426480090d0b5ee2f341060dfddb923dbafc346635ba1e8568154ca35afc5f6dc8cf7a71ba18246b2a28b6ef0a7e50f3c2ddc197843de8c4b -LibUnwind.v1.5.0+5.powerpc64le-linux-gnu.tar.gz/md5/b321964dcd3d709dac481160f7511eeb -LibUnwind.v1.5.0+5.powerpc64le-linux-gnu.tar.gz/sha512/63705322de56256bb89d2d6d12525d139cd3a58b4124ce9744c5ebac1b98fe88c690c7e17c6c7df6b86c7f8b9c76e21e398a6b42ee75796bce80a32604933033 -LibUnwind.v1.5.0+5.x86_64-linux-gnu.tar.gz/md5/2fae0ae259306bbfe38a8022311aa12c -LibUnwind.v1.5.0+5.x86_64-linux-gnu.tar.gz/sha512/fe8b713dbf7d6a48793023b44ad6d79b49f5dbb538b4e585aa20bf1100634e02319b7396624ac67bd4f95f8252540fee5249698da5e8ca4a71502edef7784024 -LibUnwind.v1.5.0+5.x86_64-linux-musl.tar.gz/md5/b33f43f6626aa29d2d84180dc1512c00 -LibUnwind.v1.5.0+5.x86_64-linux-musl.tar.gz/sha512/e1d8f2c5ad4e66f230833b09ab275a091274794d633172be4bffd464dc5c5d65c37371bf5095b0b0454c7abe940e78f1f64f2598f4bf74e2607e5bbdd9a014c4 -LibUnwind.v1.5.0+5.x86_64-unknown-freebsd.tar.gz/md5/071a13b18fea587f70d19d2af3760e67 -LibUnwind.v1.5.0+5.x86_64-unknown-freebsd.tar.gz/sha512/dc38292ae835aac91b44c3fce66b08c6732285a98d8e675ad3fa53a5d86a0b780f005d2a1bd9ee67672350c2e3c5eedcc61b97fd6188839a36787507022420d1 -libunwind-1.5.0.tar.gz/md5/c6923dda0675f6a4ef21426164dc8b6a -libunwind-1.5.0.tar.gz/sha512/1df20ca7a8cee2f2e61294fa9b677e88fec52e9d5a329f88d05c2671c69fa462f6c18808c97ca9ff664ef57292537a844f00b18d142b1938c9da701ca95a4bab +LibUnwind.v1.7.2+1.aarch64-linux-gnu.tar.gz/md5/5c73031895f590a08b52200259b7bdf3 +LibUnwind.v1.7.2+1.aarch64-linux-gnu.tar.gz/sha512/f0b4cb946bab283c3a2dbd1278c556e7c41226ee3672a83dfd66a75f38ca6487a17a43d7e2a90720abd5468c4fb7fdee5f5ffa6d18fc15c85fa76a61a9e7135a +LibUnwind.v1.7.2+1.aarch64-linux-musl.tar.gz/md5/beadb9e35c0713759482952273444135 +LibUnwind.v1.7.2+1.aarch64-linux-musl.tar.gz/sha512/cd37803ea219eddfc0b792bb54b2d5002479485efc30052cd1e5397dbbc2a3d666d57daab983dd2bb236db6eecfc3ccb2fe9726dfa464dcc2b5c0549c9a3b520 +LibUnwind.v1.7.2+1.armv6l-linux-gnueabihf.tar.gz/md5/615030f6c33a37308f72c2c9a976eda8 +LibUnwind.v1.7.2+1.armv6l-linux-gnueabihf.tar.gz/sha512/8d417fef3dfbac4c380ed6585e408c6c2958a1dff211812754c5198f82c699b0b40672b0acb1cff3e8bdbb7d2ff4a51ebac209b4d6d5922359e0d3221daf4521 +LibUnwind.v1.7.2+1.armv6l-linux-musleabihf.tar.gz/md5/20889d57eb365a7a318710d424a57163 +LibUnwind.v1.7.2+1.armv6l-linux-musleabihf.tar.gz/sha512/9e335a6eaeac08a7476dc1ee87952b7202c0bee073cbe96e71690b7e179c3d739c628bb8b511747330a519b3e99b7590e5b235106e8d788be93e156a6040e845 +LibUnwind.v1.7.2+1.armv7l-linux-gnueabihf.tar.gz/md5/3383684c3d47f3707786df48b7ac9668 +LibUnwind.v1.7.2+1.armv7l-linux-gnueabihf.tar.gz/sha512/be4828b347a7324c06bef3c3f457e9a1f28c1f37ed9a2de9c2888d73ee55f207b56be485131cc26b393311697c22dae66084d0496dc77ce61ad2bf24e2a0b9cb +LibUnwind.v1.7.2+1.armv7l-linux-musleabihf.tar.gz/md5/be6fe35176f002c636f3b009fc8e3e53 +LibUnwind.v1.7.2+1.armv7l-linux-musleabihf.tar.gz/sha512/13746270bf6a4c34a6e15964c5035b2f2b4d361a40f75294c5be2c37b5f39a94375ac832e4ebacd33a1d52d0ef4220e76c4e666420bd2ebef238e950e0a00258 +LibUnwind.v1.7.2+1.i686-linux-gnu.tar.gz/md5/33f3514cdb3d039137f543599a98f3ca +LibUnwind.v1.7.2+1.i686-linux-gnu.tar.gz/sha512/2aea691a1d0dfabefbe12b15ee1203ca037284c18733fb56a8feb0e79e352fd4f8ce55fb585748d936b513226cd21b9545cf1092f0b4e87e67397acf544c8926 +LibUnwind.v1.7.2+1.i686-linux-musl.tar.gz/md5/4aed5e07fbf7e4860ff184e106cf27c4 +LibUnwind.v1.7.2+1.i686-linux-musl.tar.gz/sha512/cf0ecace4b888e77153bc9697bf1cbd7834fe592d7012f7b5d9d700a0f39cb95fd0870437e3ba3c3fe0816d29f119bf3282c230dfaa2af10fcb9006d69141dac +LibUnwind.v1.7.2+1.powerpc64le-linux-gnu.tar.gz/md5/60f8567a63b5e9562a18591be16dcf8a +LibUnwind.v1.7.2+1.powerpc64le-linux-gnu.tar.gz/sha512/82cf61e3775f575f19a607618d345a0bb393b48099f835227c000f632bfb9bd851fc00b9fa80b79898d1309d7dcc4e4c2c3cd3239d66381693bda95db9673907 +LibUnwind.v1.7.2+1.x86_64-linux-gnu.tar.gz/md5/5b0cdaab2a0dc470d3926227f2cc67f9 +LibUnwind.v1.7.2+1.x86_64-linux-gnu.tar.gz/sha512/ae8f9b85a83208601c067b2c8d6c69b87d78b940dd7d8bce08da61df0d440191cc0490c0958a1c1cf027b333965659bccfd122fed91ef5f04163f4c0abf6ab35 +LibUnwind.v1.7.2+1.x86_64-linux-musl.tar.gz/md5/606580e0a666939a5cd6e5454f5f0062 +LibUnwind.v1.7.2+1.x86_64-linux-musl.tar.gz/sha512/95ecae2208ea957f79d21f2e3229c7b4e14f012c961502dc67892629555fb867ccbe0e1169bbfd6cd2c8386e2dd939e76a4c0853dc602441332f2d7bcd98f637 +LibUnwind.v1.7.2+1.x86_64-unknown-freebsd.tar.gz/md5/d043be8787b39d5e9a467bd8ff90be1d +LibUnwind.v1.7.2+1.x86_64-unknown-freebsd.tar.gz/sha512/44fda1ffe4a3f4d442dcccb414efe6c0d6ab895f619d74f4aa8cdcef26d9388d7b6975cb43fc18b207df1f2e61faf3555d65db8597f85e3ab4b053be5ce72e66 +libunwind-1.7.2.tar.gz/md5/35799cd8e475d3e157230ad2590c10f1 +libunwind-1.7.2.tar.gz/sha512/903f7e26c7d4c22e6ef4fe8954ca0f153fdf346cec40e1e8f7ab966d251110f4deb0a84d1fd150aee194ed966b5c1e01ee27c821cd043859852da33a94faae1f diff --git a/deps/gmp.mk b/deps/gmp.mk index 0ebabe53acf8d9..491d649e9202f9 100644 --- a/deps/gmp.mk +++ b/deps/gmp.mk @@ -39,27 +39,27 @@ checksum-gmp: $(SRCCACHE)/gmp-$(GMP_VER).tar.bz2 # Necessary for version 6.2.1, remove after next gmp release $(SRCCACHE)/gmp-$(GMP_VER)/gmp-HG-changeset.patch-applied: $(SRCCACHE)/gmp-$(GMP_VER)/source-extracted cd $(dir $@) && \ - patch -p1 < $(SRCDIR)/patches/gmp-HG-changeset.patch + patch -p1 -f < $(SRCDIR)/patches/gmp-HG-changeset.patch echo 1 > $@ $(SRCCACHE)/gmp-$(GMP_VER)/gmp-exception.patch-applied: $(SRCCACHE)/gmp-$(GMP_VER)/gmp-HG-changeset.patch-applied cd $(dir $@) && \ - patch -p1 < $(SRCDIR)/patches/gmp-exception.patch + patch -p1 -f < $(SRCDIR)/patches/gmp-exception.patch echo 1 > $@ $(SRCCACHE)/gmp-$(GMP_VER)/gmp_alloc_overflow_func.patch-applied: $(SRCCACHE)/gmp-$(GMP_VER)/gmp-exception.patch-applied cd $(dir $@) && \ - patch -p1 < $(SRCDIR)/patches/gmp_alloc_overflow_func.patch + patch -p1 -f < $(SRCDIR)/patches/gmp_alloc_overflow_func.patch echo 1 > $@ $(SRCCACHE)/gmp-$(GMP_VER)/gmp-CVE-2021-43618.patch-applied: $(SRCCACHE)/gmp-$(GMP_VER)/gmp_alloc_overflow_func.patch-applied cd $(dir $@) && \ - patch -p1 < $(SRCDIR)/patches/gmp-CVE-2021-43618.patch + patch -p1 -f < $(SRCDIR)/patches/gmp-CVE-2021-43618.patch echo 1 > $@ $(SRCCACHE)/gmp-$(GMP_VER)/gmp-more_alloc_overflow.patch-applied: $(SRCCACHE)/gmp-$(GMP_VER)/gmp-CVE-2021-43618.patch-applied cd $(dir $@) && \ - patch -p1 < $(SRCDIR)/patches/gmp-more_alloc_overflow.patch + patch -p1 -f < $(SRCDIR)/patches/gmp-more_alloc_overflow.patch echo 1 > $@ $(SRCCACHE)/gmp-$(GMP_VER)/source-patched: $(SRCCACHE)/gmp-$(GMP_VER)/gmp-more_alloc_overflow.patch-applied diff --git a/deps/llvm.mk b/deps/llvm.mk index 2a8365dd73e75e..a06db1fb0781b1 100644 --- a/deps/llvm.mk +++ b/deps/llvm.mk @@ -214,7 +214,7 @@ LLVM_CMAKE += -DLLVM_SHLIB_SYMBOL_VERSION:STRING="JL_LLVM_$(LLVM_VER_SHORT)" LLVM_PATCH_PREV := define LLVM_PATCH $$(SRCCACHE)/$$(LLVM_SRC_DIR)/$1.patch-applied: $$(SRCCACHE)/$$(LLVM_SRC_DIR)/source-extracted | $$(SRCDIR)/patches/$1.patch $$(LLVM_PATCH_PREV) - cd $$(SRCCACHE)/$$(LLVM_SRC_DIR)/llvm && patch -p1 < $$(SRCDIR)/patches/$1.patch + cd $$(SRCCACHE)/$$(LLVM_SRC_DIR)/llvm && patch -p1 -f < $$(SRCDIR)/patches/$1.patch echo 1 > $$@ # declare that applying any patch must re-run the compile step $$(LLVM_BUILDDIR_withtype)/build-compiled: $$(SRCCACHE)/$$(LLVM_SRC_DIR)/$1.patch-applied @@ -223,7 +223,7 @@ endef define LLVM_PROJ_PATCH $$(SRCCACHE)/$$(LLVM_SRC_DIR)/$1.patch-applied: $$(SRCCACHE)/$$(LLVM_SRC_DIR)/source-extracted | $$(SRCDIR)/patches/$1.patch $$(LLVM_PATCH_PREV) - cd $$(SRCCACHE)/$$(LLVM_SRC_DIR) && patch -p1 < $$(SRCDIR)/patches/$1.patch + cd $$(SRCCACHE)/$$(LLVM_SRC_DIR) && patch -p1 -f < $$(SRCDIR)/patches/$1.patch echo 1 > $$@ # declare that applying any patch must re-run the compile step $$(LLVM_BUILDDIR_withtype)/build-compiled: $$(SRCCACHE)/$$(LLVM_SRC_DIR)/$1.patch-applied @@ -249,7 +249,7 @@ $(BUILDDIR)/julia-patches.patch: # Apply the patch. $(SRCCACHE)/$(LLVM_SRC_DIR)/julia-patches.patch-applied: $(BUILDDIR)/julia-patches.patch $(SRCCACHE)/$(LLVM_SRC_DIR)/source-extracted - cd $(SRCCACHE)/$(LLVM_SRC_DIR) && patch -p1 < $(realpath $<) + cd $(SRCCACHE)/$(LLVM_SRC_DIR) && patch -p1 -f < $(realpath $<) echo 1 > $@ # Require application of Julia's patchset before configuring LLVM. diff --git a/deps/patches/gmp-more_alloc_overflow.patch b/deps/patches/gmp-more_alloc_overflow.patch index 09d07d7dbd8d5e..597f0d52d73e79 100644 --- a/deps/patches/gmp-more_alloc_overflow.patch +++ b/deps/patches/gmp-more_alloc_overflow.patch @@ -1,6 +1,6 @@ -diff -ur gmp-6.2.1.orig/mpz/n_pow_ui.c gmp-6.2.1/mpz/n_pow_ui.c ---- gmp-6.2.1.orig/mpz/n_pow_ui.c 2023-09-08 11:41:16.620551175 +0200 -+++ gmp-6.2.1/mpz/n_pow_ui.c 2023-09-08 12:49:29.650492180 +0200 +diff -ur a/mpz/n_pow_ui.c b/mpz/n_pow_ui.c +--- a/mpz/n_pow_ui.c ++++ b/mpz/n_pow_ui.c @@ -220,8 +220,7 @@ umul_ppmm (ovfl, rtwos_bits, e, btwos); if (ovfl) @@ -21,10 +21,10 @@ diff -ur gmp-6.2.1.orig/mpz/n_pow_ui.c gmp-6.2.1/mpz/n_pow_ui.c } ralloc = ralloc / GMP_NUMB_BITS + 5; -diff -ur gmp-6.2.1.orig/tal-reent.c gmp-6.2.1/tal-reent.c ---- gmp-6.2.1.orig/tal-reent.c 2020-11-14 19:45:09.000000000 +0100 -+++ gmp-6.2.1/tal-reent.c 2023-09-08 12:10:34.061357613 +0200 -@@ -61,6 +61,11 @@ +diff -ur a/tal-reent.c b/tal-reent.c +--- a/tal-reent.c ++++ b/tal-reent.c +@@ -61,6 +61,10 @@ total_size = size + HSIZ; p = __GMP_ALLOCATE_FUNC_TYPE (total_size, char); diff --git a/deps/patches/libunwind-cfa-rsp.patch b/deps/patches/libunwind-cfa-rsp.patch deleted file mode 100644 index 6b2080c10c2cf1..00000000000000 --- a/deps/patches/libunwind-cfa-rsp.patch +++ /dev/null @@ -1,368 +0,0 @@ -From 8c8c78e2db09c5dc66ad0188a088b1664483a13f Mon Sep 17 00:00:00 2001 -From: Keno Fischer -Date: Sun, 29 Aug 2021 11:07:54 -0700 -Subject: [PATCH] x86_64: Stop aliasing RSP and CFA - -RSP and CFA are different concepts. RSP refers to the physical -register, CFA is a virtual register that serves as the base -address for various other saved registers. It is true that -in many frames these are set to alias, however this is not -a requirement. For example, a function that performs a stack -switch would likely change the rsp in the middle of the function, -but would keep the CFA at the original RSP such that saved registers -may be appropriately recovered. - -We are seeing incorrect unwinds in the Julia runtime when running -julia under rr. This is because injects code (with correct CFI) -that performs just such a stack switch [1]. GDB manages to unwind -this correctly, but libunwind incorrectly sets the rsp to the CFA -address, causing a misunwind. - -Tested on x86_64, patches for other architectures are ported, but -not tested. - -[1] https://github.com/rr-debugger/rr/blob/469c22059a4a1798d33a8a224457faf22b2c178c/src/preload/syscall_hook.S#L454 ---- - include/dwarf.h | 3 +- - include/libunwind_i.h | 4 ++ - include/tdep-x86/dwarf-config.h | 2 - - include/tdep-x86/libunwind_i.h | 73 ++++++++++++--------------------- - src/dwarf/Gparser.c | 15 +++++-- - src/x86/Gos-freebsd.c | 1 + - src/x86/Gregs.c | 2 +- - src/x86/Gstep.c | 4 +- - src/x86_64/Gos-freebsd.c | 1 + - src/x86_64/Gregs.c | 2 +- - src/x86_64/Gstep.c | 2 +- - 11 files changed, 52 insertions(+), 57 deletions(-) - -diff --git a/include/dwarf.h b/include/dwarf.h -index 175c419bb..23ff4c4f6 100644 ---- a/include/dwarf.h -+++ b/include/dwarf.h -@@ -231,6 +231,7 @@ typedef enum - DWARF_WHERE_REG, /* register saved in another register */ - DWARF_WHERE_EXPR, /* register saved */ - DWARF_WHERE_VAL_EXPR, /* register has computed value */ -+ DWARF_WHERE_CFA, /* register is set to the computed cfa value */ - } - dwarf_where_t; - -@@ -313,7 +314,7 @@ typedef struct dwarf_cursor - void *as_arg; /* argument to address-space callbacks */ - unw_addr_space_t as; /* reference to per-address-space info */ - -- unw_word_t cfa; /* canonical frame address; aka frame-/stack-pointer */ -+ unw_word_t cfa; /* canonical frame address; aka frame-pointer */ - unw_word_t ip; /* instruction pointer */ - unw_word_t args_size; /* size of arguments */ - unw_word_t eh_args[UNW_TDEP_NUM_EH_REGS]; -diff --git a/include/libunwind_i.h b/include/libunwind_i.h -index fea5c2607..6c7dda9a8 100644 ---- a/include/libunwind_i.h -+++ b/include/libunwind_i.h -@@ -346,6 +346,10 @@ static inline void invalidate_edi (struct elf_dyn_info *edi) - - #include "tdep/libunwind_i.h" - -+#ifndef TDEP_DWARF_SP -+#define TDEP_DWARF_SP UNW_TDEP_SP -+#endif -+ - #ifndef tdep_get_func_addr - # define tdep_get_func_addr(as,addr,v) (*(v) = addr, 0) - #endif -diff --git a/include/tdep-x86/dwarf-config.h b/include/tdep-x86/dwarf-config.h -index f76f9c1c4..11398e4e6 100644 ---- a/include/tdep-x86/dwarf-config.h -+++ b/include/tdep-x86/dwarf-config.h -@@ -43,9 +43,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - typedef struct dwarf_loc - { - unw_word_t val; --#ifndef UNW_LOCAL_ONLY - unw_word_t type; /* see X86_LOC_TYPE_* macros. */ --#endif - } - dwarf_loc_t; - -diff --git a/include/tdep-x86/libunwind_i.h b/include/tdep-x86/libunwind_i.h -index d4c5ccdb1..ad4edc2f5 100644 ---- a/include/tdep-x86/libunwind_i.h -+++ b/include/tdep-x86/libunwind_i.h -@@ -84,15 +84,26 @@ dwarf_get_uc(const struct dwarf_cursor *cursor) - } - - #define DWARF_GET_LOC(l) ((l).val) -+# define DWARF_LOC_TYPE_MEM (0 << 0) -+# define DWARF_LOC_TYPE_FP (1 << 0) -+# define DWARF_LOC_TYPE_REG (1 << 1) -+# define DWARF_LOC_TYPE_VAL (1 << 2) - --#ifdef UNW_LOCAL_ONLY -+# define DWARF_IS_REG_LOC(l) (((l).type & DWARF_LOC_TYPE_REG) != 0) -+# define DWARF_IS_FP_LOC(l) (((l).type & DWARF_LOC_TYPE_FP) != 0) -+# define DWARF_IS_MEM_LOC(l) ((l).type == DWARF_LOC_TYPE_MEM) -+# define DWARF_IS_VAL_LOC(l) (((l).type & DWARF_LOC_TYPE_VAL) != 0) -+ -+# define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r), .type = (t) }) - # define DWARF_NULL_LOC DWARF_LOC (0, 0) --# define DWARF_IS_NULL_LOC(l) (DWARF_GET_LOC (l) == 0) --# define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r) }) --# define DWARF_IS_REG_LOC(l) 0 -+# define DWARF_IS_NULL_LOC(l) \ -+ ({ dwarf_loc_t _l = (l); _l.val == 0 && _l.type == 0; }) -+# define DWARF_VAL_LOC(c,v) DWARF_LOC ((v), DWARF_LOC_TYPE_VAL) -+# define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), DWARF_LOC_TYPE_MEM) -+ -+#ifdef UNW_LOCAL_ONLY - # define DWARF_REG_LOC(c,r) (DWARF_LOC((unw_word_t) \ - tdep_uc_addr(dwarf_get_uc(c), (r)), 0)) --# define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), 0) - # define DWARF_FPREG_LOC(c,r) (DWARF_LOC((unw_word_t) \ - tdep_uc_addr(dwarf_get_uc(c), (r)), 0)) - -@@ -114,35 +125,8 @@ dwarf_putfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t val) - return 0; - } - --static inline int --dwarf_get (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t *val) --{ -- if (!DWARF_GET_LOC (loc)) -- return -1; -- return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), val, -- 0, c->as_arg); --} -- --static inline int --dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) --{ -- if (!DWARF_GET_LOC (loc)) -- return -1; -- return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), &val, -- 1, c->as_arg); --} -- - #else /* !UNW_LOCAL_ONLY */ --# define DWARF_LOC_TYPE_FP (1 << 0) --# define DWARF_LOC_TYPE_REG (1 << 1) --# define DWARF_NULL_LOC DWARF_LOC (0, 0) --# define DWARF_IS_NULL_LOC(l) \ -- ({ dwarf_loc_t _l = (l); _l.val == 0 && _l.type == 0; }) --# define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r), .type = (t) }) --# define DWARF_IS_REG_LOC(l) (((l).type & DWARF_LOC_TYPE_REG) != 0) --# define DWARF_IS_FP_LOC(l) (((l).type & DWARF_LOC_TYPE_FP) != 0) - # define DWARF_REG_LOC(c,r) DWARF_LOC((r), DWARF_LOC_TYPE_REG) --# define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), 0) - # define DWARF_FPREG_LOC(c,r) DWARF_LOC((r), (DWARF_LOC_TYPE_REG \ - | DWARF_LOC_TYPE_FP)) - -@@ -192,38 +176,33 @@ dwarf_putfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t val) - 1, c->as_arg); - } - -+#endif /* !UNW_LOCAL_ONLY */ -+ - static inline int - dwarf_get (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t *val) - { - if (DWARF_IS_NULL_LOC (loc)) - return -UNW_EBADREG; - -- /* If a code-generator were to save a value of type unw_word_t in a -- floating-point register, we would have to support this case. I -- suppose it could happen with MMX registers, but does it really -- happen? */ -- assert (!DWARF_IS_FP_LOC (loc)); -- - if (DWARF_IS_REG_LOC (loc)) - return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), val, - 0, c->as_arg); -- else -+ if (DWARF_IS_MEM_LOC (loc)) - return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), val, - 0, c->as_arg); -+ assert(DWARF_IS_VAL_LOC (loc)); -+ *val = DWARF_GET_LOC (loc); -+ return 0; - } - - static inline int - dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) - { -+ assert(!DWARF_IS_VAL_LOC (loc)); -+ - if (DWARF_IS_NULL_LOC (loc)) - return -UNW_EBADREG; - -- /* If a code-generator were to save a value of type unw_word_t in a -- floating-point register, we would have to support this case. I -- suppose it could happen with MMX registers, but does it really -- happen? */ -- assert (!DWARF_IS_FP_LOC (loc)); -- - if (DWARF_IS_REG_LOC (loc)) - return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), &val, - 1, c->as_arg); -@@ -232,7 +211,9 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) - 1, c->as_arg); - } - --#endif /* !UNW_LOCAL_ONLY */ -+// For historical reasons, the DWARF numbering does not match the libunwind -+// numbering, necessitating this override -+#define TDEP_DWARF_SP 4 - - #define tdep_getcontext_trace unw_getcontext - #define tdep_init_done UNW_OBJ(init_done) -diff --git a/src/dwarf/Gparser.c b/src/dwarf/Gparser.c -index da170d4b3..70a62c505 100644 ---- a/src/dwarf/Gparser.c -+++ b/src/dwarf/Gparser.c -@@ -508,6 +508,9 @@ setup_fde (struct dwarf_cursor *c, dwarf_state_record_t *sr) - for (i = 0; i < DWARF_NUM_PRESERVED_REGS + 2; ++i) - set_reg (sr, i, DWARF_WHERE_SAME, 0); - -+ // SP defaults to CFA (but is overridable) -+ set_reg (sr, TDEP_DWARF_SP, DWARF_WHERE_CFA, 0); -+ - struct dwarf_cie_info *dci = c->pi.unwind_info; - sr->rs_current.ret_addr_column = dci->ret_addr_column; - unw_word_t addr = dci->cie_instr_start; -@@ -792,14 +795,14 @@ apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs) - /* As a special-case, if the stack-pointer is the CFA and the - stack-pointer wasn't saved, popping the CFA implicitly pops - the stack-pointer as well. */ -- if ((rs->reg.val[DWARF_CFA_REG_COLUMN] == UNW_TDEP_SP) -- && (UNW_TDEP_SP < ARRAY_SIZE(rs->reg.val)) -- && (rs->reg.where[UNW_TDEP_SP] == DWARF_WHERE_SAME)) -+ if ((rs->reg.val[DWARF_CFA_REG_COLUMN] == TDEP_DWARF_SP) -+ && (TDEP_DWARF_SP < ARRAY_SIZE(rs->reg.val)) -+ && (DWARF_IS_NULL_LOC(c->loc[TDEP_DWARF_SP]))) - cfa = c->cfa; - else - { - regnum = dwarf_to_unw_regnum (rs->reg.val[DWARF_CFA_REG_COLUMN]); -- if ((ret = unw_get_reg ((unw_cursor_t *) c, regnum, &cfa)) < 0) -+ if ((ret = unw_get_reg (dwarf_to_cursor(c), regnum, &cfa)) < 0) - return ret; - } - cfa += rs->reg.val[DWARF_CFA_OFF_COLUMN]; -@@ -836,6 +839,10 @@ apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs) - case DWARF_WHERE_SAME: - break; - -+ case DWARF_WHERE_CFA: -+ new_loc[i] = DWARF_VAL_LOC (c, cfa); -+ break; -+ - case DWARF_WHERE_CFAREL: - new_loc[i] = DWARF_MEM_LOC (c, cfa + rs->reg.val[i]); - break; -diff --git a/src/x86/Gos-freebsd.c b/src/x86/Gos-freebsd.c -index 7dd014046..1b251d027 100644 ---- a/src/x86/Gos-freebsd.c -+++ b/src/x86/Gos-freebsd.c -@@ -138,6 +138,7 @@ x86_handle_signal_frame (unw_cursor_t *cursor) - c->dwarf.loc[ST0] = DWARF_NULL_LOC; - } else if (c->sigcontext_format == X86_SCF_FREEBSD_SYSCALL) { - c->dwarf.loc[EIP] = DWARF_LOC (c->dwarf.cfa, 0); -+ c->dwarf.loc[ESP] = DWARF_VAL_LOC (c, c->dwarf.cfa + 4); - c->dwarf.loc[EAX] = DWARF_NULL_LOC; - c->dwarf.cfa += 4; - c->dwarf.use_prev_instr = 1; -diff --git a/src/x86/Gregs.c b/src/x86/Gregs.c -index 4a9592617..9446d6c62 100644 ---- a/src/x86/Gregs.c -+++ b/src/x86/Gregs.c -@@ -53,7 +53,6 @@ tdep_access_reg (struct cursor *c, unw_regnum_t reg, unw_word_t *valp, - break; - - case UNW_X86_CFA: -- case UNW_X86_ESP: - if (write) - return -UNW_EREADONLYREG; - *valp = c->dwarf.cfa; -@@ -81,6 +80,7 @@ tdep_access_reg (struct cursor *c, unw_regnum_t reg, unw_word_t *valp, - case UNW_X86_ECX: loc = c->dwarf.loc[ECX]; break; - case UNW_X86_EBX: loc = c->dwarf.loc[EBX]; break; - -+ case UNW_X86_ESP: loc = c->dwarf.loc[ESP]; break; - case UNW_X86_EBP: loc = c->dwarf.loc[EBP]; break; - case UNW_X86_ESI: loc = c->dwarf.loc[ESI]; break; - case UNW_X86_EDI: loc = c->dwarf.loc[EDI]; break; -diff --git a/src/x86/Gstep.c b/src/x86/Gstep.c -index 129b739a3..061dcbaaa 100644 ---- a/src/x86/Gstep.c -+++ b/src/x86/Gstep.c -@@ -47,7 +47,7 @@ unw_step (unw_cursor_t *cursor) - { - /* DWARF failed, let's see if we can follow the frame-chain - or skip over the signal trampoline. */ -- struct dwarf_loc ebp_loc, eip_loc; -+ struct dwarf_loc ebp_loc, eip_loc, esp_loc; - - /* We could get here because of missing/bad unwind information. - Validate all addresses before dereferencing. */ -@@ -77,6 +77,7 @@ unw_step (unw_cursor_t *cursor) - c->dwarf.cfa); - - ebp_loc = DWARF_LOC (c->dwarf.cfa, 0); -+ esp_loc = DWARF_VAL_LOC (c, c->dwarf.cfa + 8); - eip_loc = DWARF_LOC (c->dwarf.cfa + 4, 0); - c->dwarf.cfa += 8; - -@@ -87,6 +88,7 @@ unw_step (unw_cursor_t *cursor) - c->dwarf.loc[i] = DWARF_NULL_LOC; - - c->dwarf.loc[EBP] = ebp_loc; -+ c->dwarf.loc[ESP] = esp_loc; - c->dwarf.loc[EIP] = eip_loc; - c->dwarf.use_prev_instr = 1; - } -diff --git a/src/x86_64/Gos-freebsd.c b/src/x86_64/Gos-freebsd.c -index 8f28d1d8c..0c5a17940 100644 ---- a/src/x86_64/Gos-freebsd.c -+++ b/src/x86_64/Gos-freebsd.c -@@ -133,6 +133,7 @@ x86_64_handle_signal_frame (unw_cursor_t *cursor) - c->dwarf.loc[RCX] = c->dwarf.loc[R10]; - /* rsp_loc = DWARF_LOC(c->dwarf.cfa - 8, 0); */ - /* rbp_loc = c->dwarf.loc[RBP]; */ -+ c->dwarf.loc[RSP] = DWARF_VAL_LOC (c, c->dwarf.cfa + 8); - c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0); - ret = dwarf_get (&c->dwarf, c->dwarf.loc[RIP], &c->dwarf.ip); - Debug (1, "Frame Chain [RIP=0x%Lx] = 0x%Lx\n", -diff --git a/src/x86_64/Gregs.c b/src/x86_64/Gregs.c -index baf8a24f0..dff5bcbe7 100644 ---- a/src/x86_64/Gregs.c -+++ b/src/x86_64/Gregs.c -@@ -79,7 +79,6 @@ tdep_access_reg (struct cursor *c, unw_regnum_t reg, unw_word_t *valp, - break; - - case UNW_X86_64_CFA: -- case UNW_X86_64_RSP: - if (write) - return -UNW_EREADONLYREG; - *valp = c->dwarf.cfa; -@@ -107,6 +106,7 @@ tdep_access_reg (struct cursor *c, unw_regnum_t reg, unw_word_t *valp, - case UNW_X86_64_RCX: loc = c->dwarf.loc[RCX]; break; - case UNW_X86_64_RBX: loc = c->dwarf.loc[RBX]; break; - -+ case UNW_X86_64_RSP: loc = c->dwarf.loc[RSP]; break; - case UNW_X86_64_RBP: loc = c->dwarf.loc[RBP]; break; - case UNW_X86_64_RSI: loc = c->dwarf.loc[RSI]; break; - case UNW_X86_64_RDI: loc = c->dwarf.loc[RDI]; break; -diff --git a/src/x86_64/Gstep.c b/src/x86_64/Gstep.c -index 3c5c3830f..fdad298c7 100644 ---- a/src/x86_64/Gstep.c -+++ b/src/x86_64/Gstep.c -@@ -223,7 +223,7 @@ unw_step (unw_cursor_t *cursor) - Debug (2, "RIP fixup didn't work, falling back\n"); - unw_word_t rbp1 = 0; - rbp_loc = DWARF_LOC(rbp, 0); -- rsp_loc = DWARF_NULL_LOC; -+ rsp_loc = DWARF_VAL_LOC(c, rbp + 16); - rip_loc = DWARF_LOC (rbp + 8, 0); - ret = dwarf_get (&c->dwarf, rbp_loc, &rbp1); - Debug (1, "[RBP=0x%lx] = 0x%lx (cfa = 0x%lx) -> 0x%lx\n", diff --git a/deps/patches/libunwind-dwarf-table.patch b/deps/patches/libunwind-dwarf-table.patch deleted file mode 100644 index 5905982f9a3497..00000000000000 --- a/deps/patches/libunwind-dwarf-table.patch +++ /dev/null @@ -1,36 +0,0 @@ -From a5b5fd28ed03cb1ab524d24dc534c1fa167bf5a1 Mon Sep 17 00:00:00 2001 -From: Alex Arslan -Date: Fri, 5 Nov 2021 16:58:41 -0700 -Subject: [PATCH] Fix table indexing in `dwarf_search_unwind_table` - -`table_len` is used as an index into `table`, assuming it represents the -number of entries. However, it is defined as the number of entries -multiplied by `sizeof(unw_word_t)`. This is accounted for in other -places that use `table_len`, e.g. in `lookup`, which divides out the -size of `unw_word_t`, but the indexing expression uses `table_len` -directly. So when `table` has say 2 entries, we're actually looking at -index 15 rather than 1 in the comparison. This can cause the conditional -to erroneously evaluate to true, allowing the following line to -segfault. - -This was observed with JIT compiled code from Julia with LLVM on -FreeBSD. - -Co-Authored-By: Jameson Nash ---- - src/dwarf/Gfind_proc_info-lsb.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/dwarf/Gfind_proc_info-lsb.c b/src/dwarf/Gfind_proc_info-lsb.c -index 5e27a501..af4cbce8 100644 ---- a/src/dwarf/Gfind_proc_info-lsb.c -+++ b/src/dwarf/Gfind_proc_info-lsb.c -@@ -866,7 +866,7 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, - if (as == unw_local_addr_space) - { - e = lookup (table, table_len, ip - ip_base); -- if (e && &e[1] < &table[table_len]) -+ if (e && &e[1] < &table[table_len / sizeof (unw_word_t)]) - last_ip = e[1].start_ip_offset + ip_base; - else - last_ip = di->end_ip; diff --git a/deps/patches/libunwind-non-empty-structs.patch b/deps/patches/libunwind-non-empty-structs.patch deleted file mode 100644 index 0c04709a131843..00000000000000 --- a/deps/patches/libunwind-non-empty-structs.patch +++ /dev/null @@ -1,108 +0,0 @@ -From 1f35cd8f2bdcc1876af7352cc3e87bb7277e8162 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Mos=C3=A8=20Giordano?= -Date: Sat, 18 Jun 2022 10:35:36 +0100 -Subject: [PATCH 1/1] Make some structs non-empty - -Backport of . ---- - include/libunwind-aarch64.h | 6 ++++++ - include/libunwind-arm.h | 6 ++++++ - include/libunwind-x86.h | 6 ++++++ - 3 files changed, 18 insertions(+) - -diff --git a/include/libunwind-aarch64.h b/include/libunwind-aarch64.h -index aeaef630..b7066c51 100644 ---- a/include/libunwind-aarch64.h -+++ b/include/libunwind-aarch64.h -@@ -35,6 +35,10 @@ extern "C" { - #include - #include - -+#ifndef UNW_EMPTY_STRUCT -+# define UNW_EMPTY_STRUCT uint8_t unused; -+#endif -+ - #define UNW_TARGET aarch64 - #define UNW_TARGET_AARCH64 1 - -@@ -60,6 +64,7 @@ typedef long double unw_tdep_fpreg_t; - typedef struct - { - /* no aarch64-specific auxiliary proc-info */ -+ UNW_EMPTY_STRUCT - } - unw_tdep_proc_info_t; - -@@ -169,6 +174,7 @@ aarch64_regnum_t; - typedef struct unw_tdep_save_loc - { - /* Additional target-dependent info on a save location. */ -+ UNW_EMPTY_STRUCT - } - unw_tdep_save_loc_t; - -diff --git a/include/libunwind-arm.h b/include/libunwind-arm.h -index 6709b7ab..7c7005d1 100644 ---- a/include/libunwind-arm.h -+++ b/include/libunwind-arm.h -@@ -32,6 +32,10 @@ extern "C" { - #include - #include - -+#ifndef UNW_EMPTY_STRUCT -+# define UNW_EMPTY_STRUCT uint8_t unused; -+#endif -+ - #define UNW_TARGET arm - #define UNW_TARGET_ARM 1 - -@@ -247,6 +251,7 @@ arm_regnum_t; - typedef struct unw_tdep_save_loc - { - /* Additional target-dependent info on a save location. */ -+ UNW_EMPTY_STRUCT - } - unw_tdep_save_loc_t; - -@@ -288,6 +293,7 @@ unw_tdep_context_t; - typedef struct - { - /* no arm-specific auxiliary proc-info */ -+ UNW_EMPTY_STRUCT - } - unw_tdep_proc_info_t; - -diff --git a/include/libunwind-x86.h b/include/libunwind-x86.h -index 40fe0464..d3b741d3 100644 ---- a/include/libunwind-x86.h -+++ b/include/libunwind-x86.h -@@ -34,6 +34,10 @@ extern "C" { - #include - #include - -+#ifndef UNW_EMPTY_STRUCT -+# define UNW_EMPTY_STRUCT uint8_t unused; -+#endif -+ - #define UNW_TARGET x86 - #define UNW_TARGET_X86 1 - -@@ -158,6 +162,7 @@ x86_regnum_t; - typedef struct unw_tdep_save_loc - { - /* Additional target-dependent info on a save location. */ -+ UNW_EMPTY_STRUCT - } - unw_tdep_save_loc_t; - -@@ -169,6 +174,7 @@ typedef ucontext_t unw_tdep_context_t; - typedef struct - { - /* no x86-specific auxiliary proc-info */ -+ UNW_EMPTY_STRUCT - } - unw_tdep_proc_info_t; - --- -2.36.1 - diff --git a/deps/patches/libunwind-prefer-extbl.patch b/deps/patches/libunwind-prefer-extbl.patch deleted file mode 100644 index 07b172604d6236..00000000000000 --- a/deps/patches/libunwind-prefer-extbl.patch +++ /dev/null @@ -1,194 +0,0 @@ -From 2d6a50435bb743be1e4d88eee002372344348349 Mon Sep 17 00:00:00 2001 -From: Yichao Yu -Date: Sun, 29 Aug 2021 13:43:01 -0700 -Subject: [PATCH] Prefer EXTBL unwinding on ARM - -It is part of the C++ ABI so a EXTBL unwind info that's not `CANT_UNWIND` -should always be reliable/correct. -Ignore `ESTOPUNWIND` so that a `CANT_UNWIND` info can fallback to unwinding -using the debug info instead. ---- - include/tdep-arm/libunwind_i.h | 4 +++ - src/arm/Gex_tables.c | 18 ++++++++--- - src/arm/Gstep.c | 55 ++++++++++++++++++++-------------- - 3 files changed, 51 insertions(+), 26 deletions(-) - -diff --git a/include/tdep-arm/libunwind_i.h b/include/tdep-arm/libunwind_i.h -index 88ebfb069..5bd28c953 100644 ---- a/include/tdep-arm/libunwind_i.h -+++ b/include/tdep-arm/libunwind_i.h -@@ -256,6 +256,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) - #define tdep_init_done UNW_OBJ(init_done) - #define tdep_init UNW_OBJ(init) - #define arm_find_proc_info UNW_OBJ(find_proc_info) -+#define arm_find_proc_info2 UNW_OBJ(find_proc_info2) - #define arm_put_unwind_info UNW_OBJ(put_unwind_info) - /* Platforms that support UNW_INFO_FORMAT_TABLE need to define - tdep_search_unwind_table. */ -@@ -297,6 +298,9 @@ extern void tdep_init (void); - extern int arm_find_proc_info (unw_addr_space_t as, unw_word_t ip, - unw_proc_info_t *pi, int need_unwind_info, - void *arg); -+extern int arm_find_proc_info2 (unw_addr_space_t as, unw_word_t ip, -+ unw_proc_info_t *pi, int need_unwind_info, -+ void *arg, int methods); - extern void arm_put_unwind_info (unw_addr_space_t as, - unw_proc_info_t *pi, void *arg); - extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, -diff --git a/src/arm/Gex_tables.c b/src/arm/Gex_tables.c -index efdcf2978..083d2b2f7 100644 ---- a/src/arm/Gex_tables.c -+++ b/src/arm/Gex_tables.c -@@ -506,18 +506,20 @@ arm_phdr_cb (struct dl_phdr_info *info, size_t size, void *data) - } - - HIDDEN int --arm_find_proc_info (unw_addr_space_t as, unw_word_t ip, -- unw_proc_info_t *pi, int need_unwind_info, void *arg) -+arm_find_proc_info2 (unw_addr_space_t as, unw_word_t ip, -+ unw_proc_info_t *pi, int need_unwind_info, void *arg, -+ int methods) - { - int ret = -1; - intrmask_t saved_mask; - - Debug (14, "looking for IP=0x%lx\n", (long) ip); - -- if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)) -+ if (UNW_TRY_METHOD (UNW_ARM_METHOD_DWARF) && (methods & UNW_ARM_METHOD_DWARF)) - ret = dwarf_find_proc_info (as, ip, pi, need_unwind_info, arg); - -- if (ret < 0 && UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX)) -+ if (ret < 0 && UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX) && -+ (methods & UNW_ARM_METHOD_EXIDX)) - { - struct arm_cb_data cb_data; - -@@ -540,6 +542,14 @@ arm_find_proc_info (unw_addr_space_t as, unw_word_t ip, - return ret; - } - -+HIDDEN int -+arm_find_proc_info (unw_addr_space_t as, unw_word_t ip, -+ unw_proc_info_t *pi, int need_unwind_info, void *arg) -+{ -+ return arm_find_proc_info2 (as, ip, pi, need_unwind_info, arg, -+ UNW_ARM_METHOD_ALL); -+} -+ - HIDDEN void - arm_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg) - { -diff --git a/src/arm/Gstep.c b/src/arm/Gstep.c -index 895e8a892..e4ada651b 100644 ---- a/src/arm/Gstep.c -+++ b/src/arm/Gstep.c -@@ -54,17 +54,22 @@ arm_exidx_step (struct cursor *c) - c->dwarf.as_arg); - if (ret == -UNW_ENOINFO) - { -+#ifdef UNW_LOCAL_ONLY -+ if ((ret = arm_find_proc_info2 (c->dwarf.as, ip, &c->dwarf.pi, -+ 1, c->dwarf.as_arg, -+ UNW_ARM_METHOD_EXIDX)) < 0) -+ return ret; -+#else - if ((ret = tdep_find_proc_info (&c->dwarf, ip, 1)) < 0) - return ret; -+#endif - } - - if (c->dwarf.pi.format != UNW_INFO_FORMAT_ARM_EXIDX) - return -UNW_ENOINFO; - - ret = arm_exidx_extract (&c->dwarf, buf); -- if (ret == -UNW_ESTOPUNWIND) -- return 0; -- else if (ret < 0) -+ if (ret < 0) - return ret; - - ret = arm_exidx_decode (buf, ret, &c->dwarf); -@@ -88,6 +93,7 @@ unw_step (unw_cursor_t *cursor) - { - struct cursor *c = (struct cursor *) cursor; - int ret = -UNW_EUNSPEC; -+ int has_stopunwind = 0; - - Debug (1, "(cursor=%p)\n", c); - -@@ -95,17 +101,31 @@ unw_step (unw_cursor_t *cursor) - if (unw_is_signal_frame (cursor) > 0) - return arm_handle_signal_frame (cursor); - -+ /* First, try extbl-based unwinding. */ -+ if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX)) -+ { -+ ret = arm_exidx_step (c); -+ Debug(1, "arm_exidx_step()=%d\n", ret); -+ if (ret > 0) -+ return 1; -+ if (ret == 0) -+ return ret; -+ if (ret == -UNW_ESTOPUNWIND) -+ has_stopunwind = 1; -+ } -+ - #ifdef CONFIG_DEBUG_FRAME -- /* First, try DWARF-based unwinding. */ -+ /* Second, try DWARF-based unwinding. */ - if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)) - { -+ Debug (13, "%s(ret=%d), trying extbl\n", -+ UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX) ? "arm_exidx_step() failed " : "", -+ ret); - ret = dwarf_step (&c->dwarf); - Debug(1, "dwarf_step()=%d\n", ret); - - if (likely (ret > 0)) - return 1; -- else if (unlikely (ret == -UNW_ESTOPUNWIND)) -- return ret; - - if (ret < 0 && ret != -UNW_ENOINFO) - { -@@ -115,18 +135,9 @@ unw_step (unw_cursor_t *cursor) - } - #endif /* CONFIG_DEBUG_FRAME */ - -- /* Next, try extbl-based unwinding. */ -- if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX)) -- { -- Debug (13, "%s(ret=%d), trying extbl\n", -- UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() failed " : "", -- ret); -- ret = arm_exidx_step (c); -- if (ret > 0) -- return 1; -- if (ret == -UNW_ESTOPUNWIND || ret == 0) -- return ret; -- } -+ // Before trying the fallback, if any unwind info tell us to stop, do that. -+ if (has_stopunwind) -+ return -UNW_ESTOPUNWIND; - - /* Fall back on APCS frame parsing. - Note: This won't work in case the ARM EABI is used. */ -@@ -139,13 +150,13 @@ unw_step (unw_cursor_t *cursor) - if (UNW_TRY_METHOD(UNW_ARM_METHOD_FRAME)) - { - Debug (13, "%s%s%s%s(ret=%d), trying frame-chain\n", -- UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() " : "", -- (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) && UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX)) ? "and " : "", - UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX) ? "arm_exidx_step() " : "", -- (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) || UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX)) ? "failed " : "", -+ (UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX) && UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)) ? "and " : "", -+ UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() " : "", -+ (UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX) || UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)) ? "failed " : "", - ret); - ret = UNW_ESUCCESS; -- /* DWARF unwinding failed, try to follow APCS/optimized APCS frame chain */ -+ /* EXIDX and/or DWARF unwinding failed, try to follow APCS/optimized APCS frame chain */ - unw_word_t instr, i; - dwarf_loc_t ip_loc, fp_loc; - unw_word_t frame; diff --git a/deps/patches/libunwind-static-arm.patch b/deps/patches/libunwind-static-arm.patch deleted file mode 100644 index 92544a003b8b96..00000000000000 --- a/deps/patches/libunwind-static-arm.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/arm/Gex_tables.c b/src/arm/Gex_tables.c -index d6573a65..1d64803e 100644 ---- a/src/arm/Gex_tables.c -+++ b/src/arm/Gex_tables.c -@@ -381,7 +381,7 @@ arm_exidx_extract (struct dwarf_cursor *c, uint8_t *buf) - return nbuf; - } - --int -+static int - arm_search_unwind_table (unw_addr_space_t as, unw_word_t ip, - unw_dyn_info_t *di, unw_proc_info_t *pi, - int need_unwind_info, void *arg) diff --git a/deps/unwind.mk b/deps/unwind.mk index 76593df1e5ef00..e44b5e46009e89 100644 --- a/deps/unwind.mk +++ b/deps/unwind.mk @@ -26,30 +26,10 @@ $(SRCCACHE)/libunwind-$(UNWIND_VER)/source-extracted: $(SRCCACHE)/libunwind-$(UN checksum-unwind: $(SRCCACHE)/libunwind-$(UNWIND_VER).tar.gz $(JLCHECKSUM) $< -$(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-prefer-extbl.patch-applied: $(SRCCACHE)/libunwind-$(UNWIND_VER)/source-extracted - cd $(SRCCACHE)/libunwind-$(UNWIND_VER) && patch -p1 -f < $(SRCDIR)/patches/libunwind-prefer-extbl.patch - echo 1 > $@ - -$(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-static-arm.patch-applied: $(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-prefer-extbl.patch-applied - cd $(SRCCACHE)/libunwind-$(UNWIND_VER) && patch -p1 -f < $(SRCDIR)/patches/libunwind-static-arm.patch - echo 1 > $@ - -$(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-cfa-rsp.patch-applied: $(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-static-arm.patch-applied - cd $(SRCCACHE)/libunwind-$(UNWIND_VER) && patch -p1 -f -u < $(SRCDIR)/patches/libunwind-cfa-rsp.patch - echo 1 > $@ - -$(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-dwarf-table.patch-applied: $(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-cfa-rsp.patch-applied - cd $(SRCCACHE)/libunwind-$(UNWIND_VER) && patch -p1 -f -u -l < $(SRCDIR)/patches/libunwind-dwarf-table.patch - echo 1 > $@ - -$(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-non-empty-structs.patch-applied: $(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-dwarf-table.patch-applied - cd $(SRCCACHE)/libunwind-$(UNWIND_VER) && patch -p1 -f -u -l < $(SRCDIR)/patches/libunwind-non-empty-structs.patch - echo 1 > $@ - # note minidebuginfo requires liblzma, which we do not have a source build for # (it will be enabled in BinaryBuilder-based downloads however) # since https://github.com/JuliaPackaging/Yggdrasil/commit/0149e021be9badcb331007c62442a4f554f3003c -$(BUILDDIR)/libunwind-$(UNWIND_VER)/build-configured: $(SRCCACHE)/libunwind-$(UNWIND_VER)/source-extracted $(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-non-empty-structs.patch-applied +$(BUILDDIR)/libunwind-$(UNWIND_VER)/build-configured: $(SRCCACHE)/libunwind-$(UNWIND_VER)/source-extracted mkdir -p $(dir $@) cd $(dir $@) && \ $(dir $<)/configure $(CONFIGURE_COMMON) CPPFLAGS="$(CPPFLAGS) $(LIBUNWIND_CPPFLAGS)" CFLAGS="$(CFLAGS) $(LIBUNWIND_CFLAGS)" --enable-shared --disable-minidebuginfo --disable-tests --enable-zlibdebuginfo --disable-conservative-checks diff --git a/deps/unwind.version b/deps/unwind.version index e17b2e91c2e516..1349f2d657e87f 100644 --- a/deps/unwind.version +++ b/deps/unwind.version @@ -2,5 +2,5 @@ UNWIND_JLL_NAME := LibUnwind ## source build -UNWIND_VER_TAG := 1.5 -UNWIND_VER := 1.5.0 +UNWIND_VER_TAG := 1.7.2 +UNWIND_VER := 1.7.2 diff --git a/doc/Manifest.toml b/doc/Manifest.toml index cf50a1d41ddbd2..31eb3634fa7093 100644 --- a/doc/Manifest.toml +++ b/doc/Manifest.toml @@ -1,6 +1,6 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.9.0-DEV" +julia_version = "1.11.0-DEV" manifest_format = "2.0" project_hash = "e0c77beb18dc1f6cce661ebd60658c0c1a77390f" @@ -9,6 +9,9 @@ git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" version = "0.0.1" +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + [[deps.Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" @@ -45,9 +48,22 @@ uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" version = "0.21.3" [[deps.LibGit2]] -deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +[[deps.LibGit2_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] +uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" +version = "1.7.1+0" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.11.0+1" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + [[deps.Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" @@ -55,6 +71,11 @@ uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.2+1" + [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" @@ -77,7 +98,7 @@ deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.Random]] -deps = ["SHA", "Serialization"] +deps = ["SHA"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [[deps.SHA]] diff --git a/doc/src/devdocs/inference.md b/doc/src/devdocs/inference.md index 0ec40378e792c5..598191f02c5b82 100644 --- a/doc/src/devdocs/inference.md +++ b/doc/src/devdocs/inference.md @@ -36,9 +36,9 @@ m = first(mths) # Create variables needed to call `typeinf_code` interp = Core.Compiler.NativeInterpreter() sparams = Core.svec() # this particular method doesn't have type-parameters -optimize = true # run all inference optimizations +run_optimizer = true # run all inference optimizations types = Tuple{typeof(convert), atypes.parameters...} # Tuple{typeof(convert), Type{Int}, UInt} -Core.Compiler.typeinf_code(interp, m, types, sparams, optimize) +Core.Compiler.typeinf_code(interp, m, types, sparams, run_optimizer) ``` If your debugging adventures require a `MethodInstance`, you can look it up by diff --git a/doc/src/manual/modules.md b/doc/src/manual/modules.md index 4bcf75e2c69fcc..6ed8e3c8bc8e04 100644 --- a/doc/src/manual/modules.md +++ b/doc/src/manual/modules.md @@ -106,7 +106,9 @@ modules. We will see how to manage name clashes below. To mark a name as public without exporting it into the namespace of folks who call `using NiceStuff`, one can use `public` instead of `export`. This marks the public name(s) as part of the public API, -but does not have any namespace implications. +but does not have any namespace implications. The `public` keyword is only availiable in Julia 1.11 +and above. To maintain compatibility with Julia 1.10 and below, use the `@compat` macro from the +[Compat](https://github.com/JuliaLang/Compat.jl) package. ### Standalone `using` and `import` diff --git a/pkgimage.mk b/pkgimage.mk index 5d359080353770..9a91488955420a 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -2,8 +2,8 @@ SRCDIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) BUILDDIR := . JULIAHOME := $(SRCDIR) include $(JULIAHOME)/Make.inc +include $(JULIAHOME)/stdlib/stdlib.mk -VERSDIR := v$(shell cut -d. -f1-2 < $(JULIAHOME)/VERSION) # set some influential environment variables export JULIA_DEPOT_PATH := $(build_prefix)/share/julia @@ -11,6 +11,8 @@ export JULIA_LOAD_PATH := @stdlib unexport JULIA_PROJECT := unexport JULIA_BINDIR := +export JULIA_FALLBACK_REPL := true + default: release release: all-release debug: all-debug @@ -22,114 +24,108 @@ $(JULIA_DEPOT_PATH): print-depot-path: @$(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e '@show Base.DEPOT_PATH') -STDLIBS := ArgTools Artifacts Base64 CRC32c FileWatching Libdl NetworkOptions SHA Serialization \ - GMP_jll LLVMLibUnwind_jll LibUV_jll LibUnwind_jll MbedTLS_jll OpenLibm_jll PCRE2_jll \ - Zlib_jll dSFMT_jll libLLVM_jll libblastrampoline_jll OpenBLAS_jll Printf Random Tar \ - LibSSH2_jll MPFR_jll LinearAlgebra Dates Distributed Future LibGit2 Profile SparseArrays UUIDs \ - SharedArrays TOML Test LibCURL Downloads Pkg Dates LazyArtifacts Sockets Unicode Markdown \ - InteractiveUtils REPL DelimitedFiles Statistics - all-release: $(addprefix cache-release-, $(STDLIBS)) all-debug: $(addprefix cache-debug-, $(STDLIBS)) -define pkgimg_builder -$1_SRCS := $$(shell find $$(build_datarootdir)/julia/stdlib/$$(VERSDIR)/$1/src -name \*.jl) \ - $$(wildcard $$(build_prefix)/manifest/$$(VERSDIR)/$1) +define stdlib_builder +ifneq ($(filter $(1),$(INDEPENDENT_STDLIBS)),) $$(BUILDDIR)/stdlib/$1.release.image: $$($1_SRCS) $$(addsuffix .release.image,$$(addprefix $$(BUILDDIR)/stdlib/,$2)) $(build_private_libdir)/sys.$(SHLIB_EXT) @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes -e 'Base.compilecache(Base.identify_package("$1"))') @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --pkgimage=no -e 'Base.compilecache(Base.identify_package("$1"))') @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.compilecache(Base.identify_package("$1"))') touch $$@ -cache-release-$1: $$(BUILDDIR)/stdlib/$1.release.image $$(BUILDDIR)/stdlib/$1.debug.image: $$($1_SRCS) $$(addsuffix .debug.image,$$(addprefix $$(BUILDDIR)/stdlib/,$2)) $(build_private_libdir)/sys-debug.$(SHLIB_EXT) @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes -e 'Base.compilecache(Base.identify_package("$1"))') @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --pkgimage=no -e 'Base.compilecache(Base.identify_package("$1"))') @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.compilecache(Base.identify_package("$1"))') -cache-debug-$1: $$(BUILDDIR)/stdlib/$1.debug.image -.SECONDARY: $$(BUILDDIR)/stdlib/$1.release.image $$(BUILDDIR)/stdlib/$1.debug.image -endef - -# Used to just define them in the dependency graph -# reside in the system image -define sysimg_builder + touch $$@ +else +ifneq ($(filter $(1),$(STDLIBS_WITHIN_SYSIMG)),) $$(BUILDDIR)/stdlib/$1.release.image: touch $$@ -cache-release-$1: $$(BUILDDIR)/stdlib/$1.release.image $$(BUILDDIR)/stdlib/$1.debug.image: touch $$@ +else +$$(error $(1) neither in STDLIBS_WITHIN_SYSIMG nor INDEPENDENT_STDLIBS) +endif +endif +cache-release-$1: $$(BUILDDIR)/stdlib/$1.release.image cache-debug-$1: $$(BUILDDIR)/stdlib/$1.debug.image .SECONDARY: $$(BUILDDIR)/stdlib/$1.release.image $$(BUILDDIR)/stdlib/$1.debug.image endef # no dependencies -$(eval $(call pkgimg_builder,MozillaCACerts_jll,)) -$(eval $(call sysimg_builder,ArgTools,)) -$(eval $(call sysimg_builder,Artifacts,)) -$(eval $(call sysimg_builder,Base64,)) -$(eval $(call sysimg_builder,CRC32c,)) -$(eval $(call sysimg_builder,FileWatching,)) -$(eval $(call sysimg_builder,Libdl,)) -$(eval $(call sysimg_builder,Logging,)) -$(eval $(call sysimg_builder,Mmap,)) -$(eval $(call sysimg_builder,NetworkOptions,)) -$(eval $(call sysimg_builder,SHA,)) -$(eval $(call sysimg_builder,Serialization,)) -$(eval $(call sysimg_builder,Sockets,)) -$(eval $(call sysimg_builder,Unicode,)) -$(eval $(call pkgimg_builder,Profile,)) +$(eval $(call stdlib_builder,MozillaCACerts_jll,)) +$(eval $(call stdlib_builder,ArgTools,)) +$(eval $(call stdlib_builder,Artifacts,)) +$(eval $(call stdlib_builder,Base64,)) +$(eval $(call stdlib_builder,CRC32c,)) +$(eval $(call stdlib_builder,FileWatching,)) +$(eval $(call stdlib_builder,Libdl,)) +$(eval $(call stdlib_builder,Logging,)) +$(eval $(call stdlib_builder,Mmap,)) +$(eval $(call stdlib_builder,NetworkOptions,)) +$(eval $(call stdlib_builder,SHA,)) +$(eval $(call stdlib_builder,Serialization,)) +$(eval $(call stdlib_builder,Sockets,)) +$(eval $(call stdlib_builder,Unicode,)) +$(eval $(call stdlib_builder,Profile,)) # 1-depth packages -$(eval $(call pkgimg_builder,GMP_jll,Artifacts Libdl)) -$(eval $(call pkgimg_builder,LLVMLibUnwind_jll,Artifacts Libdl)) -$(eval $(call pkgimg_builder,LibUV_jll,Artifacts Libdl)) -$(eval $(call pkgimg_builder,LibUnwind_jll,Artifacts Libdl)) -$(eval $(call sysimg_builder,MbedTLS_jll,Artifacts Libdl)) -$(eval $(call pkgimg_builder,nghttp2_jll,Artifacts Libdl)) -$(eval $(call pkgimg_builder,OpenLibm_jll,Artifacts Libdl)) -$(eval $(call pkgimg_builder,PCRE2_jll,Artifacts Libdl)) -$(eval $(call pkgimg_builder,Zlib_jll,Artifacts Libdl)) -$(eval $(call pkgimg_builder,dSFMT_jll,Artifacts Libdl)) -$(eval $(call pkgimg_builder,libLLVM_jll,Artifacts Libdl)) -$(eval $(call sysimg_builder,libblastrampoline_jll,Artifacts Libdl)) -$(eval $(call sysimg_builder,OpenBLAS_jll,Artifacts Libdl)) -$(eval $(call sysimg_builder,Markdown,Base64)) -$(eval $(call sysimg_builder,Printf,Unicode)) -$(eval $(call sysimg_builder,Random,SHA)) -$(eval $(call sysimg_builder,Tar,ArgTools,SHA)) -$(eval $(call pkgimg_builder,DelimitedFiles,Mmap)) +$(eval $(call stdlib_builder,GMP_jll,Artifacts Libdl)) +$(eval $(call stdlib_builder,LLVMLibUnwind_jll,Artifacts Libdl)) +$(eval $(call stdlib_builder,LibUV_jll,Artifacts Libdl)) +$(eval $(call stdlib_builder,LibUnwind_jll,Artifacts Libdl)) +$(eval $(call stdlib_builder,MbedTLS_jll,Artifacts Libdl)) +$(eval $(call stdlib_builder,nghttp2_jll,Artifacts Libdl)) +$(eval $(call stdlib_builder,OpenLibm_jll,Artifacts Libdl)) +$(eval $(call stdlib_builder,PCRE2_jll,Artifacts Libdl)) +$(eval $(call stdlib_builder,Zlib_jll,Artifacts Libdl)) +$(eval $(call stdlib_builder,dSFMT_jll,Artifacts Libdl)) +$(eval $(call stdlib_builder,libLLVM_jll,Artifacts Libdl)) +$(eval $(call stdlib_builder,libblastrampoline_jll,Artifacts Libdl)) +$(eval $(call stdlib_builder,p7zip_jll,Artifacts Libdl)) +$(eval $(call stdlib_builder,OpenBLAS_jll,Artifacts Libdl)) +$(eval $(call stdlib_builder,Markdown,Base64)) +$(eval $(call stdlib_builder,Printf,Unicode)) +$(eval $(call stdlib_builder,Random,SHA)) +$(eval $(call stdlib_builder,Tar,ArgTools,SHA)) +$(eval $(call stdlib_builder,DelimitedFiles,Mmap)) # 2-depth packages -$(eval $(call pkgimg_builder,LLD_jll,Zlib_jll libLLVM_jll Artifacts Libdl)) -$(eval $(call sysimg_builder,LibSSH2_jll,Artifacts Libdl MbedTLS_jll)) -$(eval $(call pkgimg_builder,MPFR_jll,Artifacts Libdl GMP_jll)) -$(eval $(call sysimg_builder,LinearAlgebra,Libdl libblastrampoline_jll OpenBLAS_jll)) -$(eval $(call sysimg_builder,Dates,Printf)) -$(eval $(call pkgimg_builder,Distributed,Random Serialization Sockets)) -$(eval $(call sysimg_builder,Future,Random)) -$(eval $(call sysimg_builder,InteractiveUtils,Markdown)) -$(eval $(call sysimg_builder,UUIDs,Random SHA)) +$(eval $(call stdlib_builder,LLD_jll,Zlib_jll libLLVM_jll Artifacts Libdl)) +$(eval $(call stdlib_builder,LibSSH2_jll,Artifacts Libdl MbedTLS_jll)) +$(eval $(call stdlib_builder,MPFR_jll,Artifacts Libdl GMP_jll)) +$(eval $(call stdlib_builder,LinearAlgebra,Libdl libblastrampoline_jll OpenBLAS_jll)) +$(eval $(call stdlib_builder,Dates,Printf)) +$(eval $(call stdlib_builder,Distributed,Random Serialization Sockets)) +$(eval $(call stdlib_builder,Future,Random)) +$(eval $(call stdlib_builder,UUIDs,Random SHA)) +$(eval $(call stdlib_builder,InteractiveUtils,Markdown)) # 3-depth packages -$(eval $(call sysimg_builder,LibGit2_jll,MbedTLS_jll LibSSH2_jll Artifacts Libdl)) -$(eval $(call pkgimg_builder,LibCURL_jll,LibSSH2_jll nghttp2_jll MbedTLS_jll Zlib_jll Artifacts Libdl)) -$(eval $(call sysimg_builder,REPL,InteractiveUtils Markdown Sockets Unicode)) -$(eval $(call pkgimg_builder,SharedArrays,Distributed Mmap Random Serialization)) -$(eval $(call sysimg_builder,TOML,Dates)) -$(eval $(call pkgimg_builder,Test,Logging Random Serialization InteractiveUtils)) +$(eval $(call stdlib_builder,LibGit2_jll,MbedTLS_jll LibSSH2_jll Artifacts Libdl)) +$(eval $(call stdlib_builder,LibCURL_jll,LibSSH2_jll nghttp2_jll MbedTLS_jll Zlib_jll Artifacts Libdl)) +$(eval $(call stdlib_builder,REPL,InteractiveUtils Markdown Sockets Unicode)) +$(eval $(call stdlib_builder,SharedArrays,Distributed Mmap Random Serialization)) +$(eval $(call stdlib_builder,TOML,Dates)) +$(eval $(call stdlib_builder,Test,Logging Random Serialization InteractiveUtils)) # 4-depth packages -$(eval $(call sysimg_builder,LibGit2,LibGit2_jll NetworkOptions Printf SHA Base64)) -$(eval $(call sysimg_builder,LibCURL,LibCURL_jll MozillaCACerts_jll)) +$(eval $(call stdlib_builder,LibGit2,LibGit2_jll NetworkOptions Printf SHA Base64)) +$(eval $(call stdlib_builder,LibCURL,LibCURL_jll MozillaCACerts_jll)) # 5-depth packages -$(eval $(call sysimg_builder,Downloads,ArgTools FileWatching LibCURL NetworkOptions)) +$(eval $(call stdlib_builder,Downloads,ArgTools FileWatching LibCURL NetworkOptions)) # 6-depth packages -$(eval $(call pkgimg_builder,Pkg,Dates LibGit2 Libdl Logging Printf Random SHA UUIDs)) # Markdown REPL +$(eval $(call stdlib_builder,Pkg, Artifacts Dates Downloads FileWatching LibGit2 Libdl\ + Logging Markdown Printf REPL Random SHA Serialization\ + TOML Tar UUIDs p7zip_jll)) # 7-depth packages -$(eval $(call pkgimg_builder,LazyArtifacts,Artifacts Pkg)) +$(eval $(call stdlib_builder,LazyArtifacts,Artifacts Pkg)) -$(eval $(call pkgimg_builder,SparseArrays,Libdl LinearAlgebra Random Serialization)) -$(eval $(call pkgimg_builder,Statistics,LinearAlgebra SparseArrays)) +$(eval $(call stdlib_builder,SparseArrays,Libdl LinearAlgebra Random Serialization)) +$(eval $(call stdlib_builder,Statistics,LinearAlgebra SparseArrays)) # SuiteSparse_jll diff --git a/src/Makefile b/src/Makefile index 34315e33fe6960..906c4357a7d6e0 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/ccall.cpp b/src/ccall.cpp index 1add621edde28a..118803cef1b10f 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -550,6 +550,8 @@ static Value *julia_to_native( // pass the address of an alloca'd thing, not a box // since those are immutable. Value *slot = emit_static_alloca(ctx, to); + unsigned align = julia_alignment(jlto); + cast(slot)->setAlignment(Align(align)); setName(ctx.emission_context, slot, "native_convert_buffer"); if (!jvinfo.ispointer()) { jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, jvinfo.tbaa); @@ -557,7 +559,7 @@ static Value *julia_to_native( } else { jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, jvinfo.tbaa); - emit_memcpy(ctx, slot, ai, jvinfo, jl_datatype_size(jlto), julia_alignment(jlto)); + emit_memcpy(ctx, slot, ai, jvinfo, jl_datatype_size(jlto), align, align); } return slot; } @@ -1826,7 +1828,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) emit_inttoptr(ctx, emit_unbox(ctx, ctx.types().T_size, src, (jl_value_t*)jl_voidpointer_type), getInt8PtrTy(ctx.builder.getContext())), - MaybeAlign(0), + MaybeAlign(1), emit_unbox(ctx, ctx.types().T_size, n, (jl_value_t*)jl_ulong_type), false); JL_GC_POP(); @@ -2171,7 +2173,7 @@ jl_cgval_t function_sig_t::emit_a_ccall( slot->setAlignment(Align(boxalign)); ctx.builder.CreateAlignedStore(result, slot, Align(boxalign)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); - emit_memcpy(ctx, strct, ai, slot, ai, rtsz, boxalign); + emit_memcpy(ctx, strct, ai, slot, ai, rtsz, boxalign, boxalign); } else { init_bits_value(ctx, strct, result, tbaa, boxalign); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index c0916ef8a70764..2fa4cea55efbd2 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -460,6 +460,7 @@ static unsigned julia_alignment(jl_value_t *jt) // and this is the guarantee we have for the GC bits return 16; } + assert(jl_is_datatype(jt) && jl_struct_try_layout((jl_datatype_t*)jt)); unsigned alignment = jl_datatype_align(jt); if (alignment > JL_HEAP_ALIGNMENT) @@ -934,11 +935,11 @@ static Value *data_pointer(jl_codectx_t &ctx, const jl_cgval_t &x) } static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const &dst_ai, Value *src, - jl_aliasinfo_t const &src_ai, uint64_t sz, unsigned align, bool is_volatile) + jl_aliasinfo_t const &src_ai, uint64_t sz, unsigned align_dst, unsigned align_src, bool is_volatile) { if (sz == 0) return; - assert(align && "align must be specified"); + assert(align_dst && "align must be specified"); // If the types are small and simple, use load and store directly. // Going through memcpy can cause LLVM (e.g. SROA) to create bitcasts between float and int // that interferes with other optimizations. @@ -979,8 +980,8 @@ static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const setName(ctx.emission_context, src, "memcpy_refined_src"); if (isa(dst) && !dst->hasName()) setName(ctx.emission_context, dst, "memcpy_refined_dst"); - auto val = src_ai.decorateInst(ctx.builder.CreateAlignedLoad(directel, src, Align(align), is_volatile)); - dst_ai.decorateInst(ctx.builder.CreateAlignedStore(val, dst, Align(align), is_volatile)); + auto val = src_ai.decorateInst(ctx.builder.CreateAlignedLoad(directel, src, MaybeAlign(align_src), is_volatile)); + dst_ai.decorateInst(ctx.builder.CreateAlignedStore(val, dst, Align(align_dst), is_volatile)); ++SkippedMemcpys; return; } @@ -998,37 +999,37 @@ static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const // above problem won't be as serious. auto merged_ai = dst_ai.merge(src_ai); - ctx.builder.CreateMemCpy(dst, MaybeAlign(align), src, MaybeAlign(0), sz, is_volatile, + ctx.builder.CreateMemCpy(dst, Align(align_dst), src, Align(align_src), sz, is_volatile, merged_ai.tbaa, merged_ai.tbaa_struct, merged_ai.scope, merged_ai.noalias); } static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const &dst_ai, Value *src, - jl_aliasinfo_t const &src_ai, Value *sz, unsigned align, bool is_volatile) + jl_aliasinfo_t const &src_ai, Value *sz, unsigned align_dst, unsigned align_src, bool is_volatile) { if (auto const_sz = dyn_cast(sz)) { - emit_memcpy_llvm(ctx, dst, dst_ai, src, src_ai, const_sz->getZExtValue(), align, is_volatile); + emit_memcpy_llvm(ctx, dst, dst_ai, src, src_ai, const_sz->getZExtValue(), align_dst, align_src, is_volatile); return; } ++EmittedMemcpys; auto merged_ai = dst_ai.merge(src_ai); - ctx.builder.CreateMemCpy(dst, MaybeAlign(align), src, MaybeAlign(0), sz, is_volatile, + ctx.builder.CreateMemCpy(dst, MaybeAlign(align_dst), src, MaybeAlign(align_src), sz, is_volatile, merged_ai.tbaa, merged_ai.tbaa_struct, merged_ai.scope, merged_ai.noalias); } template static void emit_memcpy(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const &dst_ai, Value *src, - jl_aliasinfo_t const &src_ai, T1 &&sz, unsigned align, bool is_volatile=false) + jl_aliasinfo_t const &src_ai, T1 &&sz, unsigned align_dst, unsigned align_src, bool is_volatile=false) { - emit_memcpy_llvm(ctx, dst, dst_ai, src, src_ai, sz, align, is_volatile); + emit_memcpy_llvm(ctx, dst, dst_ai, src, src_ai, sz, align_dst, align_src, is_volatile); } template static void emit_memcpy(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const &dst_ai, const jl_cgval_t &src, - T1 &&sz, unsigned align, bool is_volatile=false) + T1 &&sz, unsigned align_dst, unsigned align_src, bool is_volatile=false) { auto src_ai = jl_aliasinfo_t::fromTBAA(ctx, src.tbaa); - emit_memcpy_llvm(ctx, dst, dst_ai, data_pointer(ctx, src), src_ai, sz, align, is_volatile); + emit_memcpy_llvm(ctx, dst, dst_ai, data_pointer(ctx, src), src_ai, sz, align_dst, align_src, is_volatile); } static LoadInst *emit_nthptr_recast(jl_codectx_t &ctx, Value *v, Value *idx, MDNode *tbaa, Type *type) @@ -1884,7 +1885,7 @@ static jl_cgval_t typed_load(jl_codectx_t &ctx, Value *ptr, Value *idx_0based, j else if (!alignment) alignment = julia_alignment(jltype); if (intcast && Order == AtomicOrdering::NotAtomic) { - emit_memcpy(ctx, intcast, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), data, jl_aliasinfo_t::fromTBAA(ctx, tbaa), nb, alignment); + emit_memcpy(ctx, intcast, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), data, jl_aliasinfo_t::fromTBAA(ctx, tbaa), nb, alignment, intcast->getAlign().value()); } else { LoadInst *load = ctx.builder.CreateAlignedLoad(elty, data, Align(alignment), false); @@ -2481,7 +2482,7 @@ static jl_cgval_t emit_unionload(jl_codectx_t &ctx, Value *addr, Value *ptindex, if (al > 1) lv->setAlignment(Align(al)); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); - emit_memcpy(ctx, lv, ai, addr, ai, fsz, al); + emit_memcpy(ctx, lv, ai, addr, ai, fsz, al, al); addr = lv; } return mark_julia_slot(fsz > 0 ? addr : nullptr, jfty, tindex, tbaa); @@ -3110,7 +3111,7 @@ static void init_bits_cgval(jl_codectx_t &ctx, Value *newv, const jl_cgval_t& v, { // newv should already be tagged if (v.ispointer()) { - emit_memcpy(ctx, newv, jl_aliasinfo_t::fromTBAA(ctx, tbaa), v, jl_datatype_size(v.typ), sizeof(void*)); + emit_memcpy(ctx, newv, jl_aliasinfo_t::fromTBAA(ctx, tbaa), v, jl_datatype_size(v.typ), sizeof(void*), julia_alignment(v.typ)); } else { init_bits_value(ctx, newv, v.V, tbaa); @@ -3315,6 +3316,7 @@ static Value *compute_tindex_unboxed(jl_codectx_t &ctx, const jl_cgval_t &val, j return compute_box_tindex(ctx, typof, val.typ, typ); } + static void union_alloca_type(jl_uniontype_t *ut, bool &allunbox, size_t &nbytes, size_t &align, size_t &min_align) { @@ -3579,7 +3581,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con // if (skip) src_ptr = ctx.builder.CreateSelect(skip, dest, src_ptr); auto f = [&] { (void)emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dst), src_ptr, - jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), nb, alignment, isVolatile); + jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), nb, alignment, alignment, isVolatile); return nullptr; }; if (skip) @@ -3616,7 +3618,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con return; } else { emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dst), src_ptr, - jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), nb, alignment, isVolatile); + jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), nb, alignment, alignment, isVolatile); } } ctx.builder.CreateBr(postBB); @@ -3641,7 +3643,8 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, MDNode *tbaa_dst, con auto f = [&] { Value *datatype = emit_typeof(ctx, src, false, false); Value *copy_bytes = emit_datatype_size(ctx, datatype); - emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dst), src, copy_bytes, /*TODO: min-align*/1, isVolatile); + (void)emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dst), data_pointer(ctx, src), + jl_aliasinfo_t::fromTBAA(ctx, src.tbaa), copy_bytes, 1, 1, isVolatile); return nullptr; }; if (skip) diff --git a/src/codegen.cpp b/src/codegen.cpp index 324157814b19be..b6d18b23c930e3 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4930,7 +4930,7 @@ static jl_cgval_t emit_varinfo(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_sym_t *va else { const DataLayout &DL = jl_Module->getDataLayout(); uint64_t sz = DL.getTypeStoreSize(T); - emit_memcpy(ctx, ssaslot, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), vi.value, sz, ssaslot->getAlign().value()); + emit_memcpy(ctx, ssaslot, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), vi.value, sz, ssaslot->getAlign().value(), varslot->getAlign().value()); } Value *tindex = NULL; if (vi.pTIndex) @@ -5039,7 +5039,7 @@ static void emit_vi_assignment_unboxed(jl_codectx_t &ctx, jl_varinfo_t &vi, Valu if (vi.value.V != rval_info.V) { Value *copy_bytes = ConstantInt::get(getInt32Ty(ctx.builder.getContext()), jl_datatype_size(vi.value.typ)); emit_memcpy(ctx, vi.value.V, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), rval_info, copy_bytes, - julia_alignment(rval_info.typ), vi.isVolatile); + julia_alignment(rval_info.typ), julia_alignment(rval_info.typ), vi.isVolatile); } } else { @@ -5087,7 +5087,7 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) Value *isboxed = ctx.builder.CreateICmpNE( ctx.builder.CreateAnd(Tindex_phi, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0x80)), ConstantInt::get(getInt8Ty(ctx.builder.getContext()), 0)); - ctx.builder.CreateMemCpy(phi, MaybeAlign(min_align), dest, MaybeAlign(0), nbytes, false); + ctx.builder.CreateMemCpy(phi, MaybeAlign(min_align), dest, dest->getAlign(), nbytes, false); ctx.builder.CreateLifetimeEnd(dest); Value *ptr = ctx.builder.CreateSelect(isboxed, maybe_bitcast(ctx, decay_derived(ctx, ptr_phi), getInt8PtrTy(ctx.builder.getContext())), @@ -5127,8 +5127,8 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) // here it's moved into phi in the successor (from dest) dest = emit_static_alloca(ctx, vtype); Value *phi = emit_static_alloca(ctx, vtype); - ctx.builder.CreateMemCpy(phi, MaybeAlign(julia_alignment(phiType)), - dest, MaybeAlign(0), + ctx.builder.CreateMemCpy(phi, Align(julia_alignment(phiType)), + dest, dest->getAlign(), jl_datatype_size(phiType), false); ctx.builder.CreateLifetimeEnd(dest); slot = mark_julia_slot(phi, phiType, NULL, ctx.tbaa().tbaa_stack); @@ -6064,7 +6064,7 @@ static void emit_cfunc_invalidate( ctx.builder.CreateStore(gf_ret, root1); } emit_memcpy(ctx, &*gf_thunk->arg_begin(), jl_aliasinfo_t::fromTBAA(ctx, nullptr), gf_ret, - jl_aliasinfo_t::fromTBAA(ctx, nullptr), jl_datatype_size(rettype), julia_alignment(rettype)); + jl_aliasinfo_t::fromTBAA(ctx, nullptr), jl_datatype_size(rettype), julia_alignment(rettype), julia_alignment(rettype)); ctx.builder.CreateRetVoid(); break; } @@ -6738,7 +6738,7 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con sigt = NULL; } else { - sigt = jl_apply_tuple_type((jl_svec_t*)sigt); + sigt = jl_apply_tuple_type((jl_svec_t*)sigt, 1); } if (sigt && !(unionall_env && jl_has_typevar_from_unionall(rt, unionall_env))) { unionall_env = NULL; @@ -7242,7 +7242,7 @@ static jl_datatype_t *compute_va_type(jl_method_instance_t *lam, size_t nreq) } jl_svecset(tupargs, i-nreq, argType); } - jl_value_t *typ = jl_apply_tuple_type(tupargs); + jl_value_t *typ = jl_apply_tuple_type(tupargs, 1); JL_GC_POP(); return (jl_datatype_t*)typ; } @@ -8420,7 +8420,7 @@ static jl_llvm_functions_t if (returninfo.cc == jl_returninfo_t::SRet) { assert(jl_is_concrete_type(jlrettype)); emit_memcpy(ctx, sret, jl_aliasinfo_t::fromTBAA(ctx, nullptr), retvalinfo, - jl_datatype_size(jlrettype), julia_alignment(jlrettype)); + jl_datatype_size(jlrettype), julia_alignment(jlrettype), julia_alignment(jlrettype)); } else { // must be jl_returninfo_t::Union emit_unionmove(ctx, sret, nullptr, retvalinfo, /*skip*/isboxed_union); diff --git a/src/gc-debug.c b/src/gc-debug.c index cb5ba66b9cce6c..7145531828853b 100644 --- a/src/gc-debug.c +++ b/src/gc-debug.c @@ -115,7 +115,7 @@ static void gc_clear_mark_outer(int bits) { for (int i = 0; i < gc_n_threads; i++) { jl_ptls_t ptls2 = gc_all_tls_states[i]; - jl_gc_pagemeta_t *pg = ptls2->page_metadata_allocd; + jl_gc_pagemeta_t *pg = jl_atomic_load_relaxed(&ptls2->page_metadata_allocd.bottom); while (pg != NULL) { gc_clear_mark_page(pg, bits); pg = pg->next; @@ -1153,7 +1153,7 @@ static void gc_count_pool_pagetable(void) { for (int i = 0; i < gc_n_threads; i++) { jl_ptls_t ptls2 = gc_all_tls_states[i]; - jl_gc_pagemeta_t *pg = ptls2->page_metadata_allocd; + jl_gc_pagemeta_t *pg = jl_atomic_load_relaxed(&ptls2->page_metadata_allocd.bottom); while (pg != NULL) { if (gc_alloc_map_is_set(pg->data)) { gc_count_pool_page(pg); diff --git a/src/gc-pages.c b/src/gc-pages.c index 44434c44cfa754..696f0831762bea 100644 --- a/src/gc-pages.c +++ b/src/gc-pages.c @@ -100,7 +100,7 @@ NOINLINE jl_gc_pagemeta_t *jl_gc_alloc_page(void) JL_NOTSAFEPOINT jl_gc_pagemeta_t *meta = NULL; // try to get page from `pool_lazily_freed` - meta = pop_lf_page_metadata_back(&global_page_pool_lazily_freed); + meta = pop_lf_back(&global_page_pool_lazily_freed); if (meta != NULL) { gc_alloc_map_set(meta->data, GC_PAGE_ALLOCATED); // page is already mapped @@ -108,14 +108,14 @@ NOINLINE jl_gc_pagemeta_t *jl_gc_alloc_page(void) JL_NOTSAFEPOINT } // try to get page from `pool_clean` - meta = pop_lf_page_metadata_back(&global_page_pool_clean); + meta = pop_lf_back(&global_page_pool_clean); if (meta != NULL) { gc_alloc_map_set(meta->data, GC_PAGE_ALLOCATED); goto exit; } // try to get page from `pool_freed` - meta = pop_lf_page_metadata_back(&global_page_pool_freed); + meta = pop_lf_back(&global_page_pool_freed); if (meta != NULL) { jl_atomic_fetch_add_relaxed(&gc_heap_stats.bytes_resident, GC_PAGE_SZ); gc_alloc_map_set(meta->data, GC_PAGE_ALLOCATED); @@ -124,7 +124,7 @@ NOINLINE jl_gc_pagemeta_t *jl_gc_alloc_page(void) JL_NOTSAFEPOINT uv_mutex_lock(&gc_perm_lock); // another thread may have allocated a large block while we were waiting... - meta = pop_lf_page_metadata_back(&global_page_pool_clean); + meta = pop_lf_back(&global_page_pool_clean); if (meta != NULL) { uv_mutex_unlock(&gc_perm_lock); gc_alloc_map_set(meta->data, GC_PAGE_ALLOCATED); @@ -138,10 +138,10 @@ NOINLINE jl_gc_pagemeta_t *jl_gc_alloc_page(void) JL_NOTSAFEPOINT pg->data = data + GC_PAGE_SZ * i; gc_alloc_map_maybe_create(pg->data); if (i == 0) { - gc_alloc_map_set(pg->data, 1); + gc_alloc_map_set(pg->data, GC_PAGE_ALLOCATED); } else { - push_lf_page_metadata_back(&global_page_pool_clean, pg); + push_lf_back(&global_page_pool_clean, pg); } } uv_mutex_unlock(&gc_perm_lock); diff --git a/src/gc-stacks.c b/src/gc-stacks.c index 6f59c0fedc2cb4..6f97323f22528d 100644 --- a/src/gc-stacks.c +++ b/src/gc-stacks.c @@ -116,7 +116,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; } } @@ -145,7 +145,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); } } } @@ -160,9 +160,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 { @@ -181,8 +181,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; } @@ -206,7 +206,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; @@ -217,12 +217,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; @@ -265,24 +265,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 cf04641d1fb690..190b9810010e99 100644 --- a/src/gc.c +++ b/src/gc.c @@ -18,6 +18,10 @@ int jl_n_markthreads; int jl_n_sweepthreads; // Number of threads currently running the GC mark-loop _Atomic(int) gc_n_threads_marking; +// Number of threads sweeping +_Atomic(int) gc_n_threads_sweeping; +// Temporary for the `ptls->page_metadata_allocd` used during parallel sweeping +_Atomic(jl_gc_page_stack_t *) gc_allocd_scratch; // `tid` of mutator thread that triggered GC _Atomic(int) gc_master_tid; // `tid` of first GC thread @@ -750,6 +754,7 @@ static int mark_reset_age = 0; static int64_t scanned_bytes; // young bytes scanned while marking static int64_t perm_scanned_bytes; // old bytes scanned while marking int prev_sweep_full = 1; +int current_sweep_full = 0; int under_pressure = 0; // Full collection heuristics @@ -954,7 +959,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; } @@ -1285,9 +1290,9 @@ STATIC_INLINE jl_taggedvalue_t *gc_reset_page(jl_ptls_t ptls2, const jl_gc_pool_ return beg; } -jl_gc_global_page_pool_t global_page_pool_lazily_freed; -jl_gc_global_page_pool_t global_page_pool_clean; -jl_gc_global_page_pool_t global_page_pool_freed; +jl_gc_page_stack_t global_page_pool_lazily_freed; +jl_gc_page_stack_t global_page_pool_clean; +jl_gc_page_stack_t global_page_pool_freed; pagetable_t alloc_map; // Add a new page to the pool. Discards any pages in `p->newpages` before. @@ -1296,7 +1301,7 @@ static NOINLINE jl_taggedvalue_t *gc_add_page(jl_gc_pool_t *p) JL_NOTSAFEPOINT // Do not pass in `ptls` as argument. This slows down the fast path // in pool_alloc significantly jl_ptls_t ptls = jl_current_task->ptls; - jl_gc_pagemeta_t *pg = pop_page_metadata_back(&ptls->page_metadata_lazily_freed); + jl_gc_pagemeta_t *pg = pop_lf_back(&ptls->page_metadata_buffered); if (pg != NULL) { gc_alloc_map_set(pg->data, GC_PAGE_ALLOCATED); } @@ -1306,7 +1311,7 @@ static NOINLINE jl_taggedvalue_t *gc_add_page(jl_gc_pool_t *p) JL_NOTSAFEPOINT pg->osize = p->osize; pg->thread_n = ptls->tid; set_page_metadata(pg); - push_page_metadata_back(&ptls->page_metadata_allocd, pg); + push_lf_back(&ptls->page_metadata_allocd, pg); jl_taggedvalue_t *fl = gc_reset_page(ptls, p, pg); jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, GC_PAGE_SZ); p->newpages = fl; @@ -1405,11 +1410,11 @@ int jl_gc_classify_pools(size_t sz, int *osize) // sweep phase -int64_t lazy_freed_pages = 0; +int64_t buffered_pages = 0; // Returns pointer to terminal pointer of list rooted at *pfl. -static jl_taggedvalue_t **gc_sweep_page(jl_gc_pool_t *p, jl_gc_pagemeta_t **allocd, - jl_gc_pagemeta_t **lazily_freed, jl_gc_pagemeta_t *pg, jl_taggedvalue_t **pfl, int sweep_full, int osize) JL_NOTSAFEPOINT +static void gc_sweep_page(jl_gc_pool_t *p, jl_gc_page_stack_t *allocd, jl_gc_page_stack_t *buffered, + jl_gc_pagemeta_t *pg, int osize) JL_NOTSAFEPOINT { char *data = pg->data; jl_taggedvalue_t *v = (jl_taggedvalue_t*)(data + GC_PAGE_OFFSET); @@ -1422,7 +1427,7 @@ static jl_taggedvalue_t **gc_sweep_page(jl_gc_pool_t *p, jl_gc_pagemeta_t **allo size_t nfree; int re_use_page = 1; - int freed_lazily = 0; + int keep_as_local_buffer = 0; int freedall = 1; int pg_skpd = 1; if (!pg->has_marked) { @@ -1433,9 +1438,9 @@ static jl_taggedvalue_t **gc_sweep_page(jl_gc_pool_t *p, jl_gc_pagemeta_t **allo // the eager one uses less memory. // FIXME - need to do accounting on a per-thread basis // on quick sweeps, keep a few pages empty but allocated for performance - if (!sweep_full && lazy_freed_pages <= default_collect_interval / GC_PAGE_SZ) { - lazy_freed_pages++; - freed_lazily = 1; + if (!current_sweep_full && buffered_pages <= default_collect_interval / GC_PAGE_SZ) { + buffered_pages++; + keep_as_local_buffer = 1; } #endif nfree = (GC_PAGE_SZ - GC_PAGE_OFFSET) / osize; @@ -1443,15 +1448,9 @@ static jl_taggedvalue_t **gc_sweep_page(jl_gc_pool_t *p, jl_gc_pagemeta_t **allo } // For quick sweep, we might be able to skip the page if the page doesn't // have any young live cell before marking. - if (!sweep_full && !pg->has_young) { + if (!current_sweep_full && !pg->has_young) { assert(!prev_sweep_full || pg->prev_nold >= pg->nold); if (!prev_sweep_full || pg->prev_nold == pg->nold) { - // the position of the freelist begin/end in this page - // is stored in its metadata - if (pg->fl_begin_offset != (uint16_t)-1) { - *pfl = page_pfl_beg(pg); - pfl = (jl_taggedvalue_t**)page_pfl_end(pg); - } freedall = 0; nfree = pg->nfree; goto done; @@ -1464,6 +1463,8 @@ static jl_taggedvalue_t **gc_sweep_page(jl_gc_pool_t *p, jl_gc_pagemeta_t **allo int has_young = 0; int16_t prev_nold = 0; int pg_nfree = 0; + jl_taggedvalue_t *fl = NULL; + jl_taggedvalue_t **pfl = &fl; jl_taggedvalue_t **pfl_begin = NULL; while ((char*)v <= lim) { int bits = v->bits.gc; @@ -1475,7 +1476,7 @@ static jl_taggedvalue_t **gc_sweep_page(jl_gc_pool_t *p, jl_gc_pagemeta_t **allo pg_nfree++; } else { // marked young or old - if (sweep_full || bits == GC_MARKED) { // old enough + if (current_sweep_full || bits == GC_MARKED) { // old enough bits = v->bits.gc = GC_OLD; // promote } prev_nold++; @@ -1497,7 +1498,7 @@ static jl_taggedvalue_t **gc_sweep_page(jl_gc_pool_t *p, jl_gc_pagemeta_t **allo } pg->nfree = pg_nfree; - if (sweep_full) { + if (current_sweep_full) { pg->nold = 0; pg->prev_nold = prev_nold; } @@ -1506,45 +1507,33 @@ static jl_taggedvalue_t **gc_sweep_page(jl_gc_pool_t *p, jl_gc_pagemeta_t **allo done: if (re_use_page) { - push_page_metadata_back(allocd, pg); - } - else if (freed_lazily) { - gc_alloc_map_set(pg->data, GC_PAGE_LAZILY_FREED); - push_page_metadata_back(lazily_freed, pg); - jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, -GC_PAGE_SZ); + push_lf_back(allocd, pg); } else { + gc_alloc_map_set(pg->data, GC_PAGE_LAZILY_FREED); jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, -GC_PAGE_SZ); - #ifdef _P64 // only enable concurrent sweeping on 64bit - if (jl_n_sweepthreads == 0) { - jl_gc_free_page(pg); - push_lf_page_metadata_back(&global_page_pool_freed, pg); + if (keep_as_local_buffer) { + push_lf_back(buffered, pg); } else { - gc_alloc_map_set(pg->data, GC_PAGE_LAZILY_FREED); - push_lf_page_metadata_back(&global_page_pool_lazily_freed, pg); + push_lf_back(&global_page_pool_lazily_freed, pg); } - #else - jl_gc_free_page(pg); - push_lf_page_metadata_back(&global_page_pool_freed, pg); - #endif } gc_time_count_page(freedall, pg_skpd); - gc_num.freed += (nfree - old_nfree) * osize; - pool_live_bytes += GC_PAGE_SZ - GC_PAGE_OFFSET - nfree * osize; - return pfl; + jl_atomic_fetch_add((_Atomic(int64_t) *)&pool_live_bytes, GC_PAGE_SZ - GC_PAGE_OFFSET - nfree * osize); + jl_atomic_fetch_add((_Atomic(int64_t) *)&gc_num.freed, (nfree - old_nfree) * osize); } // the actual sweeping over all allocated pages in a memory pool -STATIC_INLINE void gc_sweep_pool_page(jl_taggedvalue_t ***pfl, jl_gc_pagemeta_t **allocd, - jl_gc_pagemeta_t **lazily_freed, jl_gc_pagemeta_t *pg, int sweep_full) JL_NOTSAFEPOINT +STATIC_INLINE void gc_sweep_pool_page(jl_gc_page_stack_t *allocd, jl_gc_page_stack_t *lazily_freed, + jl_gc_pagemeta_t *pg) JL_NOTSAFEPOINT { int p_n = pg->pool_n; int t_n = pg->thread_n; jl_ptls_t ptls2 = gc_all_tls_states[t_n]; jl_gc_pool_t *p = &ptls2->heap.norm_pools[p_n]; int osize = pg->osize; - pfl[t_n * JL_GC_N_POOLS + p_n] = gc_sweep_page(p, allocd, lazily_freed, pg, pfl[t_n * JL_GC_N_POOLS + p_n], sweep_full, osize); + gc_sweep_page(p, allocd, lazily_freed, pg, osize); } // sweep over all memory that is being used and not in a pool @@ -1570,11 +1559,70 @@ static void gc_pool_sync_nfree(jl_gc_pagemeta_t *pg, jl_taggedvalue_t *last) JL_ pg->nfree = nfree; } +void gc_sweep_wake_all(void) +{ + uv_mutex_lock(&gc_threads_lock); + for (int i = gc_first_tid; i < gc_first_tid + jl_n_markthreads; i++) { + jl_ptls_t ptls2 = gc_all_tls_states[i]; + jl_atomic_fetch_add(&ptls2->gc_sweeps_requested, 1); + } + uv_cond_broadcast(&gc_threads_cond); + uv_mutex_unlock(&gc_threads_lock); +} + +void gc_sweep_pool_parallel(void) +{ + jl_atomic_fetch_add(&gc_n_threads_sweeping, 1); + jl_gc_page_stack_t *allocd_scratch = jl_atomic_load(&gc_allocd_scratch); + if (allocd_scratch != NULL) { + while (1) { + int found_pg = 0; + for (int t_i = 0; t_i < gc_n_threads; t_i++) { + jl_ptls_t ptls2 = gc_all_tls_states[t_i]; + if (ptls2 == NULL) { + continue; + } + jl_gc_page_stack_t *allocd = &allocd_scratch[t_i]; + jl_gc_pagemeta_t *pg = pop_lf_back(&ptls2->page_metadata_allocd); + if (pg == NULL) { + continue; + } + gc_sweep_pool_page(allocd, &ptls2->page_metadata_buffered, pg); + found_pg = 1; + } + if (!found_pg) { + break; + } + } + } + jl_atomic_fetch_add(&gc_n_threads_sweeping, -1); +} + +void gc_sweep_wait_for_all(void) +{ + jl_atomic_store(&gc_allocd_scratch, NULL); + while (jl_atomic_load_relaxed(&gc_n_threads_sweeping) != 0) { + jl_cpu_pause(); + } +} + +void gc_free_pages(void) +{ + while (1) { + jl_gc_pagemeta_t *pg = pop_lf_back(&global_page_pool_lazily_freed); + if (pg == NULL) { + break; + } + jl_gc_free_page(pg); + push_lf_back(&global_page_pool_freed, pg); + } +} + // setup the data-structures for a sweep over all memory pools -static void gc_sweep_pool(int sweep_full) +static void gc_sweep_pool(void) { gc_time_pool_start(); - lazy_freed_pages = 0; + buffered_pages = 0; // For the benefit of the analyzer, which doesn't know that gc_n_threads // doesn't change over the course of this function @@ -1614,26 +1662,26 @@ static void gc_sweep_pool(int sweep_full) pg->has_young = 1; } } - jl_gc_pagemeta_t *pg = ptls2->page_metadata_lazily_freed; + jl_gc_pagemeta_t *pg = jl_atomic_load_relaxed(&ptls2->page_metadata_buffered.bottom); while (pg != NULL) { jl_gc_pagemeta_t *pg2 = pg->next; - lazy_freed_pages++; + buffered_pages++; pg = pg2; } } // the actual sweeping + jl_gc_page_stack_t *tmp = (jl_gc_page_stack_t *)alloca(n_threads * sizeof(jl_gc_page_stack_t)); + memset(tmp, 0, n_threads * sizeof(jl_gc_page_stack_t)); + jl_atomic_store(&gc_allocd_scratch, tmp); + gc_sweep_wake_all(); + gc_sweep_pool_parallel(); + gc_sweep_wait_for_all(); + for (int t_i = 0; t_i < n_threads; t_i++) { jl_ptls_t ptls2 = gc_all_tls_states[t_i]; if (ptls2 != NULL) { - jl_gc_pagemeta_t *allocd = NULL; - jl_gc_pagemeta_t *pg = ptls2->page_metadata_allocd; - while (pg != NULL) { - jl_gc_pagemeta_t *pg2 = pg->next; - gc_sweep_pool_page(pfl, &allocd, &ptls2->page_metadata_lazily_freed, pg, sweep_full); - pg = pg2; - } - ptls2->page_metadata_allocd = allocd; + ptls2->page_metadata_allocd = tmp[t_i]; for (int i = 0; i < JL_GC_N_POOLS; i++) { jl_gc_pool_t *p = &ptls2->heap.norm_pools[i]; p->newpages = NULL; @@ -1641,6 +1689,26 @@ static void gc_sweep_pool(int sweep_full) } } + // merge free lists + for (int t_i = 0; t_i < n_threads; t_i++) { + jl_ptls_t ptls2 = gc_all_tls_states[t_i]; + if (ptls2 == NULL) { + continue; + } + jl_gc_pagemeta_t *pg = jl_atomic_load_relaxed(&ptls2->page_metadata_allocd.bottom); + while (pg != NULL) { + jl_gc_pagemeta_t *pg2 = pg->next; + if (pg->fl_begin_offset != UINT16_MAX) { + char *cur_pg = pg->data; + jl_taggedvalue_t *fl_beg = (jl_taggedvalue_t*)(cur_pg + pg->fl_begin_offset); + jl_taggedvalue_t *fl_end = (jl_taggedvalue_t*)(cur_pg + pg->fl_end_offset); + *pfl[t_i * JL_GC_N_POOLS + pg->pool_n] = fl_beg; + pfl[t_i * JL_GC_N_POOLS + pg->pool_n] = &fl_end->next; + } + pg = pg2; + } + } + // null out terminal pointers of free lists for (int t_i = 0; t_i < n_threads; t_i++) { jl_ptls_t ptls2 = gc_all_tls_states[t_i]; @@ -1656,9 +1724,13 @@ static void gc_sweep_pool(int sweep_full) if (jl_n_sweepthreads > 0) { uv_sem_post(&gc_sweep_assists_needed); } + else { + gc_free_pages(); + } +#else + gc_free_pages(); #endif - - gc_time_pool_end(sweep_full); + gc_time_pool_end(current_sweep_full); } static void gc_sweep_perm_alloc(void) @@ -3289,13 +3361,14 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) #ifdef USE_TRACY TracyCZoneColor(full_timing_block.tracy_ctx, 0xFFA500); #endif + current_sweep_full = sweep_full; sweep_weak_refs(); sweep_stack_pools(); gc_sweep_foreign_objs(); gc_sweep_other(ptls, sweep_full); gc_scrub(); gc_verify_tags(); - gc_sweep_pool(sweep_full); + gc_sweep_pool(); if (sweep_full) gc_sweep_perm_alloc(); } @@ -3551,8 +3624,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/gc.h b/src/gc.h index f713ebd9e9737d..7985a3ddd43ef3 100644 --- a/src/gc.h +++ b/src/gc.h @@ -182,13 +182,9 @@ typedef struct _jl_gc_pagemeta_t { char *data; } jl_gc_pagemeta_t; -typedef struct { - _Atomic(jl_gc_pagemeta_t *) page_metadata_back; -} jl_gc_global_page_pool_t; - -extern jl_gc_global_page_pool_t global_page_pool_lazily_freed; -extern jl_gc_global_page_pool_t global_page_pool_clean; -extern jl_gc_global_page_pool_t global_page_pool_freed; +extern jl_gc_page_stack_t global_page_pool_lazily_freed; +extern jl_gc_page_stack_t global_page_pool_clean; +extern jl_gc_page_stack_t global_page_pool_freed; #define GC_BACKOFF_MIN 4 #define GC_BACKOFF_MAX 12 @@ -209,28 +205,29 @@ STATIC_INLINE void gc_backoff(int *i) JL_NOTSAFEPOINT // get away with just using a CAS and not implementing some ABA // prevention mechanism since once a node is popped from the // `jl_gc_global_page_pool_t`, it may only be pushed back to them -// in the sweeping phase, which is serial +// in the sweeping phase, which also doesn't push a node into the +// same stack after it's popped -STATIC_INLINE void push_lf_page_metadata_back(jl_gc_global_page_pool_t *pool, jl_gc_pagemeta_t *elt) JL_NOTSAFEPOINT +STATIC_INLINE void push_lf_back(jl_gc_page_stack_t *pool, jl_gc_pagemeta_t *elt) JL_NOTSAFEPOINT { while (1) { - jl_gc_pagemeta_t *old_back = jl_atomic_load_relaxed(&pool->page_metadata_back); + jl_gc_pagemeta_t *old_back = jl_atomic_load_relaxed(&pool->bottom); elt->next = old_back; - if (jl_atomic_cmpswap(&pool->page_metadata_back, &old_back, elt)) { + if (jl_atomic_cmpswap(&pool->bottom, &old_back, elt)) { break; } jl_cpu_pause(); } } -STATIC_INLINE jl_gc_pagemeta_t *pop_lf_page_metadata_back(jl_gc_global_page_pool_t *pool) JL_NOTSAFEPOINT +STATIC_INLINE jl_gc_pagemeta_t *pop_lf_back(jl_gc_page_stack_t *pool) JL_NOTSAFEPOINT { while (1) { - jl_gc_pagemeta_t *old_back = jl_atomic_load_relaxed(&pool->page_metadata_back); + jl_gc_pagemeta_t *old_back = jl_atomic_load_relaxed(&pool->bottom); if (old_back == NULL) { return NULL; } - if (jl_atomic_cmpswap(&pool->page_metadata_back, &old_back, old_back->next)) { + if (jl_atomic_cmpswap(&pool->bottom, &old_back, old_back->next)) { return old_back; } jl_cpu_pause(); @@ -386,7 +383,7 @@ extern jl_gc_num_t gc_num; extern bigval_t *big_objects_marked; extern arraylist_t finalizer_list_marked; extern arraylist_t to_finalize; -extern int64_t lazy_freed_pages; +extern int64_t buffered_pages; extern int gc_first_tid; extern int gc_n_threads; extern jl_ptls_t* gc_all_tls_states; @@ -455,12 +452,15 @@ extern uv_mutex_t gc_threads_lock; extern uv_cond_t gc_threads_cond; extern uv_sem_t gc_sweep_assists_needed; extern _Atomic(int) gc_n_threads_marking; +extern _Atomic(int) gc_n_threads_sweeping; void gc_mark_queue_all_roots(jl_ptls_t ptls, jl_gc_markqueue_t *mq); void gc_mark_finlist_(jl_gc_markqueue_t *mq, jl_value_t **fl_begin, jl_value_t **fl_end) JL_NOTSAFEPOINT; void gc_mark_finlist(jl_gc_markqueue_t *mq, arraylist_t *list, size_t start) JL_NOTSAFEPOINT; void gc_mark_loop_serial_(jl_ptls_t ptls, jl_gc_markqueue_t *mq); void gc_mark_loop_serial(jl_ptls_t ptls); void gc_mark_loop_parallel(jl_ptls_t ptls, int master); +void gc_sweep_pool_parallel(void); +void gc_free_pages(void); void sweep_stack_pools(void); void jl_gc_debug_init(void); diff --git a/src/gf.c b/src/gf.c index e8cdb493026b03..b403a6e1af087c 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1266,7 +1266,7 @@ static jl_method_instance_t *cache_method( intptr_t max_varargs = get_max_varargs(definition, kwmt, mt, NULL); jl_compilation_sig(tt, sparams, definition, max_varargs, &newparams); if (newparams) { - temp2 = jl_apply_tuple_type(newparams); + temp2 = jl_apply_tuple_type(newparams, 1); // Now there may be a problem: the widened signature is more general // than just the given arguments, so it might conflict with another // definition that does not have cache instances yet. To fix this, we @@ -1389,7 +1389,7 @@ static jl_method_instance_t *cache_method( } } if (newparams) { - simplett = (jl_datatype_t*)jl_apply_tuple_type(newparams); + simplett = (jl_datatype_t*)jl_apply_tuple_type(newparams, 1); temp2 = (jl_value_t*)simplett; } @@ -2579,7 +2579,7 @@ JL_DLLEXPORT jl_value_t *jl_normalize_to_compilable_sig(jl_methtable_t *mt, jl_t jl_compilation_sig(ti, env, m, max_varargs, &newparams); int is_compileable = ((jl_datatype_t*)ti)->isdispatchtuple; if (newparams) { - tt = (jl_datatype_t*)jl_apply_tuple_type(newparams); + tt = (jl_datatype_t*)jl_apply_tuple_type(newparams, 1); if (!is_compileable) { // compute new env, if used below jl_value_t *ti = jl_type_intersection_env((jl_value_t*)tt, (jl_value_t*)m->sig, &newparams); @@ -2834,7 +2834,7 @@ jl_value_t *jl_argtype_with_function_type(jl_value_t *ft JL_MAYBE_UNROOTED, jl_v jl_svecset(tt, 0, ft); for (size_t i = 0; i < l; i++) jl_svecset(tt, i+1, jl_tparam(types,i)); - tt = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)tt); + tt = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)tt, 1); tt = jl_rewrap_unionall_(tt, types0); JL_GC_POP(); return tt; diff --git a/src/interpreter.c b/src/interpreter.c index 2ad56e76b2549b..d84a1381fccad9 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/intrinsics.cpp b/src/intrinsics.cpp index c7f1263af030a0..3e7ace18a17490 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -469,7 +469,7 @@ static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dest } Value *src = data_pointer(ctx, x); - emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dest), src, jl_aliasinfo_t::fromTBAA(ctx, x.tbaa), jl_datatype_size(x.typ), alignment, isVolatile); + emit_memcpy(ctx, dest, jl_aliasinfo_t::fromTBAA(ctx, tbaa_dest), src, jl_aliasinfo_t::fromTBAA(ctx, x.tbaa), jl_datatype_size(x.typ), alignment, alignment, isVolatile); } static jl_datatype_t *staticeval_bitstype(const jl_cgval_t &targ) @@ -707,7 +707,7 @@ static jl_cgval_t emit_pointerref(jl_codectx_t &ctx, jl_cgval_t *argv) thePtr = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), emit_bitcast(ctx, thePtr, getInt8PtrTy(ctx.builder.getContext())), im1); setName(ctx.emission_context, thePtr, "pointerref_src"); MDNode *tbaa = best_tbaa(ctx.tbaa(), ety); - emit_memcpy(ctx, strct, jl_aliasinfo_t::fromTBAA(ctx, tbaa), thePtr, jl_aliasinfo_t::fromTBAA(ctx, nullptr), size, 1); + emit_memcpy(ctx, strct, jl_aliasinfo_t::fromTBAA(ctx, tbaa), thePtr, jl_aliasinfo_t::fromTBAA(ctx, nullptr), size, sizeof(jl_value_t*), align_nb); return mark_julia_type(ctx, strct, true, ety); } else { @@ -783,7 +783,7 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, jl_cgval_t *argv) setName(ctx.emission_context, im1, "pointerset_offset"); auto gep = ctx.builder.CreateInBoundsGEP(getInt8Ty(ctx.builder.getContext()), thePtr, im1); setName(ctx.emission_context, gep, "pointerset_ptr"); - emit_memcpy(ctx, gep, jl_aliasinfo_t::fromTBAA(ctx, nullptr), x, size, align_nb); + emit_memcpy(ctx, gep, jl_aliasinfo_t::fromTBAA(ctx, nullptr), x, size, align_nb, julia_alignment(ety)); } else { bool isboxed; diff --git a/src/jltypes.c b/src/jltypes.c index f38197e49353db..998f3fe47f1571 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -333,7 +333,7 @@ JL_DLLEXPORT int jl_get_size(jl_value_t *val, size_t *pnt) if (jl_is_long(val)) { ssize_t slen = jl_unbox_long(val); if (slen < 0) - jl_errorf("size or dimension is negative: %d", slen); + jl_errorf("size or dimension is negative: %zd", slen); *pnt = slen; return 1; } @@ -1435,17 +1435,6 @@ jl_datatype_t *jl_apply_cmpswap_type(jl_value_t *ty) return rettyp; } -// used to expand an NTuple to a flat representation -static jl_value_t *jl_tupletype_fill(size_t n, jl_value_t *v) -{ - jl_value_t *p = NULL; - JL_GC_PUSH1(&p); - p = (jl_value_t*)jl_svec_fill(n, v); - p = jl_apply_tuple_type((jl_svec_t*)p); - JL_GC_POP(); - return p; -} - JL_EXTENSION struct _jl_typestack_t { jl_datatype_t *tt; struct _jl_typestack_t *prev; @@ -1724,7 +1713,7 @@ static void check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, si JL_GC_POP(); } -jl_value_t *extract_wrapper(jl_value_t *t JL_PROPAGATES_ROOT) JL_GLOBALLY_ROOTED +jl_value_t *extract_wrapper(jl_value_t *t JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT JL_GLOBALLY_ROOTED { t = jl_unwrap_unionall(t); if (jl_is_datatype(t)) @@ -1796,13 +1785,13 @@ int _may_substitute_ub(jl_value_t *v, jl_tvar_t *var, int inside_inv, int *cov_c // * `var` does not appear in invariant position // * `var` appears at most once (in covariant position) and not in a `Vararg` // unless the upper bound is concrete (diagonal rule) -int may_substitute_ub(jl_value_t *v, jl_tvar_t *var) JL_NOTSAFEPOINT +static int may_substitute_ub(jl_value_t *v, jl_tvar_t *var) JL_NOTSAFEPOINT { int cov_count = 0; return _may_substitute_ub(v, var, 0, &cov_count); } -jl_value_t *normalize_unionalls(jl_value_t *t) +static jl_value_t *normalize_unionalls(jl_value_t *t) { if (jl_is_uniontype(t)) { jl_uniontype_t *u = (jl_uniontype_t*)t; @@ -1840,6 +1829,31 @@ jl_value_t *normalize_unionalls(jl_value_t *t) return t; } +// used to expand an NTuple to a flat representation +static jl_value_t *jl_tupletype_fill(size_t n, jl_value_t *t, int check) +{ + jl_value_t *p = NULL; + JL_GC_PUSH1(&p); + if (check) { + // Since we are skipping making the Vararg and skipping checks later, + // we inline the checks from jl_wrap_vararg here now + if (!jl_valid_type_param(t)) + jl_type_error_rt("Vararg", "type", (jl_value_t*)jl_type_type, t); + // jl_wrap_vararg sometimes simplifies the type, so we only do this 1 time, instead of for each n later + t = normalize_unionalls(t); + p = t; + jl_value_t *tw = extract_wrapper(t); + if (tw && t != tw && jl_types_equal(t, tw)) + t = tw; + p = t; + check = 0; // remember that checks are already done now + } + p = (jl_value_t*)jl_svec_fill(n, t); + p = jl_apply_tuple_type((jl_svec_t*)p, check); + JL_GC_POP(); + return p; +} + static jl_value_t *_jl_instantiate_type_in_env(jl_value_t *ty, jl_unionall_t *env, jl_value_t **vals, jl_typeenv_t *prev, jl_typestack_t *stack); static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **iparams, size_t ntp, @@ -1962,7 +1976,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value if (nt == 0 || !jl_has_free_typevars(va0)) { if (ntp == 1) { JL_GC_POP(); - return jl_tupletype_fill(nt, va0); + return jl_tupletype_fill(nt, va0, 0); } size_t i, l; p = jl_alloc_svec(ntp - 1 + nt); @@ -1971,7 +1985,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value l = ntp - 1 + nt; for (; i < l; i++) jl_svecset(p, i, va0); - jl_value_t *ndt = jl_apply_tuple_type(p); + jl_value_t *ndt = jl_apply_tuple_type(p, check); JL_GC_POP(); return ndt; } @@ -2136,19 +2150,19 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value return (jl_value_t*)ndt; } -static jl_value_t *jl_apply_tuple_type_v_(jl_value_t **p, size_t np, jl_svec_t *params) +static jl_value_t *jl_apply_tuple_type_v_(jl_value_t **p, size_t np, jl_svec_t *params, int check) { - return inst_datatype_inner(jl_anytuple_type, params, p, np, NULL, NULL, 1); + return inst_datatype_inner(jl_anytuple_type, params, p, np, NULL, NULL, check); } -JL_DLLEXPORT jl_value_t *jl_apply_tuple_type(jl_svec_t *params) +JL_DLLEXPORT jl_value_t *jl_apply_tuple_type(jl_svec_t *params, int check) { - return jl_apply_tuple_type_v_(jl_svec_data(params), jl_svec_len(params), params); + return jl_apply_tuple_type_v_(jl_svec_data(params), jl_svec_len(params), params, check); } JL_DLLEXPORT jl_value_t *jl_apply_tuple_type_v(jl_value_t **p, size_t np) { - return jl_apply_tuple_type_v_(p, np, NULL); + return jl_apply_tuple_type_v_(p, np, NULL, 1); } jl_tupletype_t *jl_lookup_arg_tuple_type(jl_value_t *arg1, jl_value_t **args, size_t nargs, int leaf) @@ -2211,13 +2225,15 @@ static jl_value_t *inst_tuple_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_ jl_datatype_t *tt = (jl_datatype_t*)t; jl_svec_t *tp = tt->parameters; size_t ntp = jl_svec_len(tp); - // Instantiate NTuple{3,Int} + // Instantiate Tuple{Vararg{T,N}} where T is fixed and N is known, such as Dims{3} + // And avoiding allocating the intermediate steps // Note this does not instantiate Tuple{Vararg{Int,3}}; that's done in inst_datatype_inner + // Note this does not instantiate NTuple{N,T}, since it is unnecessary and inefficient to expand that now if (jl_is_va_tuple(tt) && ntp == 1) { - // If this is a Tuple{Vararg{T,N}} with known N, expand it to + // If this is a Tuple{Vararg{T,N}} with known N and T, expand it to // a fixed-length tuple jl_value_t *T=NULL, *N=NULL; - jl_value_t *va = jl_unwrap_unionall(jl_tparam0(tt)); + jl_value_t *va = jl_tparam0(tt); jl_value_t *ttT = jl_unwrap_vararg(va); jl_value_t *ttN = jl_unwrap_vararg_num(va); jl_typeenv_t *e = env; @@ -2228,11 +2244,12 @@ static jl_value_t *inst_tuple_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_ N = e->val; e = e->prev; } - if (T != NULL && N != NULL && jl_is_long(N)) { + if (T != NULL && N != NULL && jl_is_long(N)) { // TODO: && !jl_has_free_typevars(T) to match inst_datatype_inner, or even && jl_is_concrete_type(T) + // Since this is skipping jl_wrap_vararg, we inline the checks from it here ssize_t nt = jl_unbox_long(N); if (nt < 0) - jl_errorf("size or dimension is negative: %zd", nt); - return jl_tupletype_fill(nt, T); + jl_errorf("Vararg length is negative: %zd", nt); + return jl_tupletype_fill(nt, T, check); } } jl_value_t **iparams; @@ -2428,9 +2445,8 @@ jl_vararg_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n, int check) } } if (t) { - if (!jl_valid_type_param(t)) { + if (!jl_valid_type_param(t)) jl_type_error_rt("Vararg", "type", (jl_value_t*)jl_type_type, t); - } t = normalize_unionalls(t); jl_value_t *tw = extract_wrapper(t); if (tw && t != tw && jl_types_equal(t, tw)) @@ -2735,7 +2751,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_anytuple_type->layout = NULL; jl_typeofbottom_type->super = jl_wrap_Type(jl_bottom_type); - jl_emptytuple_type = (jl_datatype_t*)jl_apply_tuple_type(jl_emptysvec); + jl_emptytuple_type = (jl_datatype_t*)jl_apply_tuple_type(jl_emptysvec, 0); jl_emptytuple = jl_gc_permobj(0, jl_emptytuple_type); jl_emptytuple_type->instance = jl_emptytuple; diff --git a/src/julia.h b/src/julia.h index 50c4f8994de150..07f8459d37238c 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1054,6 +1054,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) @@ -1564,7 +1569,7 @@ JL_DLLEXPORT jl_value_t *jl_apply_type1(jl_value_t *tc, jl_value_t *p1); JL_DLLEXPORT jl_value_t *jl_apply_type2(jl_value_t *tc, jl_value_t *p1, jl_value_t *p2); JL_DLLEXPORT jl_datatype_t *jl_apply_modify_type(jl_value_t *dt); JL_DLLEXPORT jl_datatype_t *jl_apply_cmpswap_type(jl_value_t *dt); -JL_DLLEXPORT jl_value_t *jl_apply_tuple_type(jl_svec_t *params); +JL_DLLEXPORT jl_value_t *jl_apply_tuple_type(jl_svec_t *params, int check); // if uncertain, set check=1 JL_DLLEXPORT jl_value_t *jl_apply_tuple_type_v(jl_value_t **p, size_t np); JL_DLLEXPORT jl_datatype_t *jl_new_datatype(jl_sym_t *name, jl_module_t *module, diff --git a/src/julia_internal.h b/src/julia_internal.h index 61e141c755a5ea..41f976b8585f31 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -204,6 +204,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 @@ -1163,6 +1165,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 d4cbb88e619ba7..7510eae308d27e 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 { @@ -200,6 +200,10 @@ typedef struct { struct _jl_bt_element_t; struct _jl_gc_pagemeta_t; +typedef struct { + _Atomic(struct _jl_gc_pagemeta_t *) bottom; +} jl_gc_page_stack_t; + // This includes all the thread local states we care about for a thread. // Changes to TLS field types must be reflected in codegen. #define JL_MAX_BT_SIZE 80000 @@ -261,11 +265,12 @@ typedef struct _jl_tls_states_t { #endif jl_thread_t system_id; arraylist_t finalizers; - struct _jl_gc_pagemeta_t *page_metadata_allocd; - struct _jl_gc_pagemeta_t *page_metadata_lazily_freed; + jl_gc_page_stack_t page_metadata_allocd; + jl_gc_page_stack_t page_metadata_buffered; jl_gc_markqueue_t mark_queue; jl_gc_mark_cache_t gc_cache; arraylist_t sweep_objs; + _Atomic(int64_t) gc_sweeps_requested; // Saved exception for previous *external* API call or NULL if cleared. // Access via jl_exception_occurred(). struct _jl_value_t *previous_exception; diff --git a/src/method.c b/src/method.c index 68110d6995bbf8..7d8d0e9ec4a788 100644 --- a/src/method.c +++ b/src/method.c @@ -998,7 +998,7 @@ JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata, JL_GC_PUSH3(&f, &m, &argtype); size_t i, na = jl_svec_len(atypes); - argtype = jl_apply_tuple_type(atypes); + argtype = jl_apply_tuple_type(atypes, 1); if (!jl_is_datatype(argtype)) jl_error("invalid type in method definition (Union{})"); diff --git a/src/mtarraylist.c b/src/mtarraylist.c new file mode 100644 index 00000000000000..8bad44797dab43 --- /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/partr.c b/src/partr.c index 75d6d832fe78fc..a660f6be63de3e 100644 --- a/src/partr.c +++ b/src/partr.c @@ -107,14 +107,18 @@ void jl_init_threadinginfra(void) void JL_NORETURN jl_finish_task(jl_task_t *t); - static inline int may_mark(void) JL_NOTSAFEPOINT { return (jl_atomic_load(&gc_n_threads_marking) > 0); } -// gc thread mark function -void jl_gc_mark_threadfun(void *arg) +static inline int may_sweep(jl_ptls_t ptls) JL_NOTSAFEPOINT +{ + return (jl_atomic_load(&ptls->gc_sweeps_requested) > 0); +} + +// parallel gc thread function +void jl_parallel_gc_threadfun(void *arg) { jl_threadarg_t *targ = (jl_threadarg_t*)arg; @@ -130,16 +134,22 @@ void jl_gc_mark_threadfun(void *arg) while (1) { uv_mutex_lock(&gc_threads_lock); - while (!may_mark()) { + while (!may_mark() && !may_sweep(ptls)) { uv_cond_wait(&gc_threads_cond, &gc_threads_lock); } uv_mutex_unlock(&gc_threads_lock); - gc_mark_loop_parallel(ptls, 0); + if (may_mark()) { + gc_mark_loop_parallel(ptls, 0); + } + if (may_sweep(ptls)) { // not an else! + gc_sweep_pool_parallel(); + jl_atomic_fetch_add(&ptls->gc_sweeps_requested, -1); + } } } -// gc thread sweep function -void jl_gc_sweep_threadfun(void *arg) +// concurrent gc thread function +void jl_concurrent_gc_threadfun(void *arg) { jl_threadarg_t *targ = (jl_threadarg_t*)arg; @@ -155,14 +165,7 @@ void jl_gc_sweep_threadfun(void *arg) while (1) { uv_sem_wait(&gc_sweep_assists_needed); - while (1) { - jl_gc_pagemeta_t *pg = pop_lf_page_metadata_back(&global_page_pool_lazily_freed); - if (pg == NULL) { - break; - } - jl_gc_free_page(pg); - push_lf_page_metadata_back(&global_page_pool_freed, pg); - } + gc_free_pages(); } } diff --git a/src/precompile_utils.c b/src/precompile_utils.c index 055ec4b3330f15..9a577b900a1b72 100644 --- a/src/precompile_utils.c +++ b/src/precompile_utils.c @@ -120,7 +120,7 @@ static void _compile_all_union(jl_value_t *sig) jl_svecset(p, i, ty); } } - methsig = jl_apply_tuple_type(p); + methsig = jl_apply_tuple_type(p, 1); methsig = jl_rewrap_unionall(methsig, sig); _compile_all_tvar_union(methsig); } diff --git a/src/signals-mach.c b/src/signals-mach.c index 02bb044609ade8..6ec8f95570f177 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 b2056947e2b8aa..0d5ad9b1be7c5e 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 7cd3b024628516..10bd0dec7f480c 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 18bf4b21269380..1289e7d08657e4 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,55 @@ 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! +extern int gc_first_tid; + +// 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++) { + // 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]; - 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); + int t_state = JL_TASK_STATE_DONE; + jl_task_t *t = ptls2->root_task; + 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 + 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); + 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"); + } - 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/subtype.c b/src/subtype.c index c67beecae9dbd7..d8177f0fd21ff4 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -3393,7 +3393,7 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten else if (isy) res = (jl_value_t*)yd; else if (p) - res = jl_apply_tuple_type(p); + res = jl_apply_tuple_type(p, 1); else res = jl_apply_tuple_type_v(params, np); } @@ -4130,7 +4130,7 @@ static jl_value_t *switch_union_tuple(jl_value_t *a, jl_value_t *b) ts[1] = jl_tparam(b, i); jl_svecset(vec, i, jl_type_union(ts, 2)); } - jl_value_t *ans = jl_apply_tuple_type(vec); + jl_value_t *ans = jl_apply_tuple_type(vec, 1); JL_GC_POP(); return ans; } diff --git a/src/threading.c b/src/threading.c index 4faa8a0a2dc469..2ec9c220fbafb3 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)); @@ -752,10 +754,10 @@ void jl_start_threads(void) } } else if (i == nthreads - 1 && jl_n_sweepthreads == 1) { - uv_thread_create(&uvtid, jl_gc_sweep_threadfun, t); + uv_thread_create(&uvtid, jl_concurrent_gc_threadfun, t); } else { - uv_thread_create(&uvtid, jl_gc_mark_threadfun, t); + uv_thread_create(&uvtid, jl_parallel_gc_threadfun, t); } uv_thread_detach(&uvtid); } diff --git a/src/threading.h b/src/threading.h index 73d2cd73fb70d2..260ecffa30dd55 100644 --- a/src/threading.h +++ b/src/threading.h @@ -25,8 +25,8 @@ jl_ptls_t jl_init_threadtls(int16_t tid) JL_NOTSAFEPOINT; // provided by a threading infrastructure void jl_init_threadinginfra(void); -void jl_gc_mark_threadfun(void *arg); -void jl_gc_sweep_threadfun(void *arg); +void jl_parallel_gc_threadfun(void *arg); +void jl_concurrent_gc_threadfun(void *arg); void jl_threadfun(void *arg); #ifdef __cplusplus diff --git a/stdlib/LibUnwind_jll/Project.toml b/stdlib/LibUnwind_jll/Project.toml index 3b0bb28817e132..bbc587c1718c29 100644 --- a/stdlib/LibUnwind_jll/Project.toml +++ b/stdlib/LibUnwind_jll/Project.toml @@ -1,6 +1,6 @@ name = "LibUnwind_jll" uuid = "745a5e78-f969-53e9-954f-d19f2f74f4e3" -version = "1.5.0+5" +version = "1.7.2+1" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 386de771d666ff..5e1d0e1ba2aee7 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -678,7 +678,8 @@ end function __init__() try - BLAS.lbt_forward(OpenBLAS_jll.libopenblas_path; clear=true) + verbose = parse(Bool, get(ENV, "LBT_VERBOSE", "false")) + BLAS.lbt_forward(OpenBLAS_jll.libopenblas_path; clear=true, verbose) BLAS.check() catch ex Base.showerror_nostdio(ex, "WARNING: Error during initialization of module LinearAlgebra") diff --git a/stdlib/LinearAlgebra/src/abstractq.jl b/stdlib/LinearAlgebra/src/abstractq.jl index 93358d052d50b1..2aa333beef2b28 100644 --- a/stdlib/LinearAlgebra/src/abstractq.jl +++ b/stdlib/LinearAlgebra/src/abstractq.jl @@ -8,6 +8,7 @@ end parent(adjQ::AdjointQ) = adjQ.Q eltype(::Type{<:AbstractQ{T}}) where {T} = T +Base.eltypeof(Q::AbstractQ) = eltype(Q) ndims(::AbstractQ) = 2 # inversion/adjoint/transpose @@ -129,6 +130,16 @@ function copyto!(dest::PermutedDimsArray{T,2,perm}, src::AbstractQ) where {T,per end return dest end +# used in concatenations: Base.__cat_offset1! +Base._copy_or_fill!(A, inds, Q::AbstractQ) = (A[inds...] = collect(Q)) +# overloads of helper functions +Base.cat_size(A::AbstractQ) = size(A) +Base.cat_size(A::AbstractQ, d) = size(A, d) +Base.cat_length(a::AbstractQ) = prod(size(a)) +Base.cat_ndims(a::AbstractQ) = ndims(a) +Base.cat_indices(A::AbstractQ, d) = axes(A, d) +Base.cat_similar(A::AbstractQ, T::Type, shape::Tuple) = Array{T}(undef, shape) +Base.cat_similar(A::AbstractQ, T::Type, shape::Vector) = Array{T}(undef, shape...) function show(io::IO, ::MIME{Symbol("text/plain")}, Q::AbstractQ) print(io, Base.dims2string(size(Q)), ' ', summary(Q)) diff --git a/stdlib/LinearAlgebra/src/special.jl b/stdlib/LinearAlgebra/src/special.jl index 885f29fa1417be..d028fe43e6338c 100644 --- a/stdlib/LinearAlgebra/src/special.jl +++ b/stdlib/LinearAlgebra/src/special.jl @@ -336,6 +336,116 @@ const _SpecialArrays = Union{} promote_to_array_type(::Tuple) = Matrix +# promote_to_arrays(n,k, T, A...) promotes any UniformScaling matrices +# in A to matrices of type T and sizes given by n[k:end]. n is an array +# so that the same promotion code can be used for hvcat. We pass the type T +# so that we can re-use this code for sparse-matrix hcat etcetera. +promote_to_arrays_(n::Int, ::Type, a::Number) = a +promote_to_arrays_(n::Int, ::Type{Matrix}, J::UniformScaling{T}) where {T} = Matrix(J, n, n) +promote_to_arrays_(n::Int, ::Type, A::AbstractArray) = A +promote_to_arrays_(n::Int, ::Type, A::AbstractQ) = collect(A) +promote_to_arrays(n,k, ::Type) = () +promote_to_arrays(n,k, ::Type{T}, A) where {T} = (promote_to_arrays_(n[k], T, A),) +promote_to_arrays(n,k, ::Type{T}, A, B) where {T} = + (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B)) +promote_to_arrays(n,k, ::Type{T}, A, B, C) where {T} = + (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays_(n[k+2], T, C)) +promote_to_arrays(n,k, ::Type{T}, A, B, Cs...) where {T} = + (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays(n,k+2, T, Cs...)...) + +_us2number(A) = A +_us2number(J::UniformScaling) = J.λ + +for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols")) + @eval begin + @inline $f(A::Union{AbstractArray,AbstractQ,UniformScaling}...) = $_f(A...) + # if there's a Number present, J::UniformScaling must be 1x1-dimensional + @inline $f(A::Union{AbstractArray,AbstractQ,UniformScaling,Number}...) = $f(map(_us2number, A)...) + function $_f(A::Union{AbstractArray,AbstractQ,UniformScaling,Number}...; array_type = promote_to_array_type(A)) + n = -1 + for a in A + if !isa(a, UniformScaling) + require_one_based_indexing(a) + na = size(a,$dim) + n >= 0 && n != na && + throw(DimensionMismatch(string("number of ", $name, + " of each array must match (got ", n, " and ", na, ")"))) + n = na + end + end + n == -1 && throw(ArgumentError($("$f of only UniformScaling objects cannot determine the matrix size"))) + return cat(promote_to_arrays(fill(n, length(A)), 1, array_type, A...)..., dims=Val(3-$dim)) + end + end +end + +hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,AbstractQ,UniformScaling}...) = _hvcat(rows, A...) +hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,AbstractQ,UniformScaling,Number}...) = _hvcat(rows, A...) +function _hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,AbstractQ,UniformScaling,Number}...; array_type = promote_to_array_type(A)) + require_one_based_indexing(A...) + nr = length(rows) + sum(rows) == length(A) || throw(ArgumentError("mismatch between row sizes and number of arguments")) + n = fill(-1, length(A)) + needcols = false # whether we also need to infer some sizes from the column count + j = 0 + for i = 1:nr # infer UniformScaling sizes from row counts, if possible: + ni = -1 # number of rows in this block-row, -1 indicates unknown + for k = 1:rows[i] + if !isa(A[j+k], UniformScaling) + na = size(A[j+k], 1) + ni >= 0 && ni != na && + throw(DimensionMismatch("mismatch in number of rows")) + ni = na + end + end + if ni >= 0 + for k = 1:rows[i] + n[j+k] = ni + end + else # row consisted only of UniformScaling objects + needcols = true + end + j += rows[i] + end + if needcols # some sizes still unknown, try to infer from column count + nc = -1 + j = 0 + for i = 1:nr + nci = 0 + rows[i] > 0 && n[j+1] == -1 && (j += rows[i]; continue) + for k = 1:rows[i] + nci += isa(A[j+k], UniformScaling) ? n[j+k] : size(A[j+k], 2) + end + nc >= 0 && nc != nci && throw(DimensionMismatch("mismatch in number of columns")) + nc = nci + j += rows[i] + end + nc == -1 && throw(ArgumentError("sizes of UniformScalings could not be inferred")) + j = 0 + for i = 1:nr + if rows[i] > 0 && n[j+1] == -1 # this row consists entirely of UniformScalings + nci, r = divrem(nc, rows[i]) + r != 0 && throw(DimensionMismatch("indivisible UniformScaling sizes")) + for k = 1:rows[i] + n[j+k] = nci + end + end + j += rows[i] + end + end + Amat = promote_to_arrays(n, 1, array_type, A...) + # We have two methods for promote_to_array_type, one returning Matrix and + # another one returning SparseMatrixCSC (in SparseArrays.jl). In the dense + # case, we cannot call hvcat for the promoted UniformScalings because this + # causes a stack overflow. In the sparse case, however, we cannot call + # typed_hvcat because we need a sparse output. + if array_type == Matrix + return typed_hvcat(promote_eltype(Amat...), rows, Amat...) + else + return hvcat(rows, Amat...) + end +end + # factorizations function cholesky(S::RealHermSymComplexHerm{<:Real,<:SymTridiagonal}, ::NoPivot = NoPivot(); check::Bool = true) T = choltype(eltype(S)) diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index 0b3168113acf75..b014472a9cec25 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -402,114 +402,6 @@ function cond(J::UniformScaling{T}) where T return J.λ ≠ zero(T) ? onereal : oftype(onereal, Inf) end -# promote_to_arrays(n,k, T, A...) promotes any UniformScaling matrices -# in A to matrices of type T and sizes given by n[k:end]. n is an array -# so that the same promotion code can be used for hvcat. We pass the type T -# so that we can re-use this code for sparse-matrix hcat etcetera. -promote_to_arrays_(n::Int, ::Type, a::Number) = a -promote_to_arrays_(n::Int, ::Type{Matrix}, J::UniformScaling{T}) where {T} = Matrix(J, n, n) -promote_to_arrays_(n::Int, ::Type, A::AbstractArray) = A -promote_to_arrays(n,k, ::Type) = () -promote_to_arrays(n,k, ::Type{T}, A) where {T} = (promote_to_arrays_(n[k], T, A),) -promote_to_arrays(n,k, ::Type{T}, A, B) where {T} = - (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B)) -promote_to_arrays(n,k, ::Type{T}, A, B, C) where {T} = - (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays_(n[k+2], T, C)) -promote_to_arrays(n,k, ::Type{T}, A, B, Cs...) where {T} = - (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays(n,k+2, T, Cs...)...) - -_us2number(A) = A -_us2number(J::UniformScaling) = J.λ - -for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols")) - @eval begin - @inline $f(A::Union{AbstractArray,UniformScaling}...) = $_f(A...) - # if there's a Number present, J::UniformScaling must be 1x1-dimensional - @inline $f(A::Union{AbstractArray,UniformScaling,Number}...) = $f(map(_us2number, A)...) - function $_f(A::Union{AbstractArray,UniformScaling,Number}...; array_type = promote_to_array_type(A)) - n = -1 - for a in A - if !isa(a, UniformScaling) - require_one_based_indexing(a) - na = size(a,$dim) - n >= 0 && n != na && - throw(DimensionMismatch(string("number of ", $name, - " of each array must match (got ", n, " and ", na, ")"))) - n = na - end - end - n == -1 && throw(ArgumentError($("$f of only UniformScaling objects cannot determine the matrix size"))) - return cat(promote_to_arrays(fill(n, length(A)), 1, array_type, A...)..., dims=Val(3-$dim)) - end - end -end - -hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,UniformScaling,Number}...) = _hvcat(rows, A...) -function _hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,UniformScaling,Number}...; array_type = promote_to_array_type(A)) - require_one_based_indexing(A...) - nr = length(rows) - sum(rows) == length(A) || throw(ArgumentError("mismatch between row sizes and number of arguments")) - n = fill(-1, length(A)) - needcols = false # whether we also need to infer some sizes from the column count - j = 0 - for i = 1:nr # infer UniformScaling sizes from row counts, if possible: - ni = -1 # number of rows in this block-row, -1 indicates unknown - for k = 1:rows[i] - if !isa(A[j+k], UniformScaling) - na = size(A[j+k], 1) - ni >= 0 && ni != na && - throw(DimensionMismatch("mismatch in number of rows")) - ni = na - end - end - if ni >= 0 - for k = 1:rows[i] - n[j+k] = ni - end - else # row consisted only of UniformScaling objects - needcols = true - end - j += rows[i] - end - if needcols # some sizes still unknown, try to infer from column count - nc = -1 - j = 0 - for i = 1:nr - nci = 0 - rows[i] > 0 && n[j+1] == -1 && (j += rows[i]; continue) - for k = 1:rows[i] - nci += isa(A[j+k], UniformScaling) ? n[j+k] : size(A[j+k], 2) - end - nc >= 0 && nc != nci && throw(DimensionMismatch("mismatch in number of columns")) - nc = nci - j += rows[i] - end - nc == -1 && throw(ArgumentError("sizes of UniformScalings could not be inferred")) - j = 0 - for i = 1:nr - if rows[i] > 0 && n[j+1] == -1 # this row consists entirely of UniformScalings - nci, r = divrem(nc, rows[i]) - r != 0 && throw(DimensionMismatch("indivisible UniformScaling sizes")) - for k = 1:rows[i] - n[j+k] = nci - end - end - j += rows[i] - end - end - Amat = promote_to_arrays(n, 1, array_type, A...) - # We have two methods for promote_to_array_type, one returning Matrix and - # another one returning SparseMatrixCSC (in SparseArrays.jl). In the dense - # case, we cannot call hvcat for the promoted UniformScalings because this - # causes a stack overflow. In the sparse case, however, we cannot call - # typed_hvcat because we need a sparse output. - if array_type == Matrix - return typed_hvcat(promote_eltype(Amat...), rows, Amat...) - else - return hvcat(rows, Amat...) - end -end - ## Matrix construction from UniformScaling function Matrix{T}(s::UniformScaling, dims::Dims{2}) where {T} A = zeros(T, dims) diff --git a/stdlib/LinearAlgebra/test/special.jl b/stdlib/LinearAlgebra/test/special.jl index eaa297e05d9576..7e96af369e3107 100644 --- a/stdlib/LinearAlgebra/test/special.jl +++ b/stdlib/LinearAlgebra/test/special.jl @@ -259,9 +259,10 @@ end bidiagmat = Bidiagonal(1:N, 1:(N-1), :U) tridiagmat = Tridiagonal(1:(N-1), 1:N, 1:(N-1)) symtridiagmat = SymTridiagonal(1:N, 1:(N-1)) - specialmats = (diagmat, bidiagmat, tridiagmat, symtridiagmat) + abstractq = qr(tridiagmat).Q + specialmats = (diagmat, bidiagmat, tridiagmat, symtridiagmat, abstractq, zeros(Int,N,N)) for specialmata in specialmats, specialmatb in specialmats - MA = Matrix(specialmata); MB = Matrix(specialmatb) + MA = collect(specialmata); MB = collect(specialmatb) @test hcat(specialmata, specialmatb) == hcat(MA, MB) @test vcat(specialmata, specialmatb) == vcat(MA, MB) @test hvcat((1,1), specialmata, specialmatb) == hvcat((1,1), MA, MB) diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 2f5c5ead0f454f..a61011827f53dc 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = cf0019fbbaf3ab1e606d4e973c1908bbd899a9d5 +PKG_SHA1 = 3960c692bd4dd2b2eafc7699e8d0744f90756812 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 diff --git a/stdlib/REPL/Project.toml b/stdlib/REPL/Project.toml index 4f77157da01461..2c3ab32fdc3272 100644 --- a/stdlib/REPL/Project.toml +++ b/stdlib/REPL/Project.toml @@ -8,8 +8,8 @@ Sockets = "6462fe0b-24de-5631-8697-dd941f90decc" Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" [extras] -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] test = ["Test", "Random"] diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 29f4a9780ff992..81aa56631eaaf6 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -14,6 +14,10 @@ REPL.run_repl(repl) """ module REPL +function __init__() + Base.REPL_MODULE_REF[] = REPL +end + Base.Experimental.@optlevel 1 Base.Experimental.@max_methods 1 @@ -1531,4 +1535,13 @@ end import .Numbered.numbered_prompt! +# this assignment won't survive precompilation, +# but will stick if REPL is baked into a sysimg. +# Needs to occur after this module is finished. +Base.REPL_MODULE_REF[] = REPL + +if Base.generating_output() + include("precompile.jl") +end + end # module diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 57ee508cd302be..117e6f55d37325 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -356,13 +356,10 @@ end # Returns a range that includes the method name in front of the first non # closed start brace from the end of the string. function find_start_brace(s::AbstractString; c_start='(', c_end=')') - braces = 0 r = reverse(s) i = firstindex(r) - in_single_quotes = false - in_double_quotes = false - in_back_ticks = false - in_comment = 0 + braces = in_comment = 0 + in_single_quotes = in_double_quotes = in_back_ticks = false while i <= ncodeunits(r) c, i = iterate(r, i) if c == '#' && i <= ncodeunits(r) && iterate(r, i)[1] == '=' @@ -457,7 +454,8 @@ struct REPLInterpreter <: CC.AbstractInterpreter code_cache::REPLInterpreterCache function REPLInterpreter(repl_frame::CC.InferenceResult; world::UInt = Base.get_world_counter(), - inf_params::CC.InferenceParams = CC.InferenceParams(), + inf_params::CC.InferenceParams = CC.InferenceParams(; + unoptimize_throw_blocks=false), opt_params::CC.OptimizationParams = CC.OptimizationParams(), inf_cache::Vector{CC.InferenceResult} = CC.InferenceResult[], code_cache::REPLInterpreterCache = get_code_cache()) @@ -569,6 +567,10 @@ end # lower `ex` and run type inference on the resulting top-level expression function repl_eval_ex(@nospecialize(ex), context_module::Module) + if isexpr(ex, :toplevel) || isexpr(ex, :tuple) + # get the inference result for the last expression + ex = ex.args[end] + end lwr = try Meta.lower(context_module, ex) catch # macro expansion failed, etc. @@ -846,20 +848,18 @@ function dict_identifier_key(str::String, tag::Symbol, context_module::Module=Ma else str_close = str end - frange, end_of_identifier = find_start_brace(str_close, c_start='[', c_end=']') isempty(frange) && return (nothing, nothing, nothing) - obj = context_module - for name in split(str[frange[1]:end_of_identifier], '.') - Base.isidentifier(name) || return (nothing, nothing, nothing) - sym = Symbol(name) - isdefined(obj, sym) || return (nothing, nothing, nothing) - obj = getfield(obj, sym) - end - (isa(obj, AbstractDict) && length(obj)::Int < 1_000_000) || return (nothing, nothing, nothing) + objstr = str[1:end_of_identifier] + objex = Meta.parse(objstr, raise=false, depwarn=false) + objt = repl_eval_ex(objex, context_module) + isa(objt, Core.Const) || return (nothing, nothing, nothing) + obj = objt.val + isa(obj, AbstractDict) || return (nothing, nothing, nothing) + length(obj)::Int < 1_000_000 || return (nothing, nothing, nothing) begin_of_key = something(findnext(!isspace, str, nextind(str, end_of_identifier) + 1), # +1 for [ lastindex(str)+1) - return (obj::AbstractDict, str[begin_of_key:end], begin_of_key) + return (obj, str[begin_of_key:end], begin_of_key) end # This needs to be a separate non-inlined function, see #19441 diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index 377f63b5148d26..cc3a7fe980190a 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -76,7 +76,7 @@ function _helpmode(io::IO, line::AbstractString, mod::Module=Main, internal_acce end _helpmode(line::AbstractString, mod::Module=Main) = _helpmode(stdout, line, mod) -# Print vertical lines along each docstring if there are multiple docs +# Print horizontal lines between each docstring if there are multiple docs function insert_hlines(docs) if !isa(docs, Markdown.MD) || !haskey(docs.meta, :results) || isempty(docs.meta[:results]) return docs diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl new file mode 100644 index 00000000000000..7985cf426a250f --- /dev/null +++ b/stdlib/REPL/src/precompile.jl @@ -0,0 +1,207 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module Precompile +# Can't use this during incremental: `@eval Module() begin`` + +import ..REPL + +# Ugly hack for our cache file to not have a dependency edge on FakePTYs. +Base._track_dependencies[] = false +try + Base.include(@__MODULE__, joinpath(Sys.BINDIR, "..", "share", "julia", "test", "testhelpers", "FakePTYs.jl")) + import .FakePTYs: open_fake_pty +finally + Base._track_dependencies[] = true +end +using Base.Meta + +import Markdown + +## Debugging options +# Disable parallel precompiles generation by setting `false` +const PARALLEL_PRECOMPILATION = true + +# View the code sent to the repl by setting this to `stdout` +const debug_output = devnull # or stdout + +CTRL_C = '\x03' +CTRL_R = '\x12' +UP_ARROW = "\e[A" +DOWN_ARROW = "\e[B" + +repl_script = """ +2+2 +print("") +printstyled("a", "b") +display([1]) +display([1 2; 3 4]) +foo(x) = 1 +@time @eval foo(1) +; pwd +$CTRL_C +$CTRL_R$CTRL_C +? reinterpret +using Ra\t$CTRL_C +\\alpha\t$CTRL_C +\e[200~paste here ;)\e[201~"$CTRL_C +$UP_ARROW$DOWN_ARROW$CTRL_C +123\b\b\b$CTRL_C +\b\b$CTRL_C +f(x) = x03 +f(1,2) +[][1] +cd("complet_path\t\t$CTRL_C +""" + +julia_exepath() = joinpath(Sys.BINDIR, Base.julia_exename()) + +const JULIA_PROMPT = "julia> " +const PKG_PROMPT = "pkg> " +const SHELL_PROMPT = "shell> " +const HELP_PROMPT = "help?> " + +blackhole = Sys.isunix() ? "/dev/null" : "nul" +procenv = Dict{String,Any}( + "JULIA_HISTORY" => blackhole, + "JULIA_PROJECT" => nothing, # remove from environment + "JULIA_LOAD_PATH" => "@stdlib", + "JULIA_DEPOT_PATH" => Sys.iswindows() ? ";" : ":", + "TERM" => "", + "JULIA_FALLBACK_REPL" => "0") # Turn REPL.jl on in subprocess + +generate_precompile_statements() = try + # Extract the precompile statements from the precompile file + statements_step = Channel{String}(Inf) + + step = @async mktemp() do precompile_file, precompile_file_h + # Collect statements from running a REPL process and replaying our REPL script + touch(precompile_file) + pts, ptm = open_fake_pty() + cmdargs = `-e 'import REPL; REPL.Terminals.is_precompiling[] = true'` + p = run(addenv(addenv(```$(julia_exepath()) -O0 --trace-compile=$precompile_file + --cpu-target=native --startup-file=no --compiled-modules=existing --color=yes -i $cmdargs```, procenv), + "JULIA_PKG_PRECOMPILE_AUTO" => "0"), + pts, pts, pts; wait=false) + Base.close_stdio(pts) + # Prepare a background process to copy output from process until `pts` is closed + output_copy = Base.BufferStream() + tee = @async try + while !eof(ptm) + l = readavailable(ptm) + write(debug_output, l) + Sys.iswindows() && (sleep(0.1); yield(); yield()) # workaround hang - probably a libuv issue? + write(output_copy, l) + end + catch ex + if !(ex isa Base.IOError && ex.code == Base.UV_EIO) + rethrow() # ignore EIO on ptm after pts dies + end + finally + close(output_copy) + close(ptm) + end + Base.errormonitor(tee) + repl_inputter = @async begin + # wait for the definitive prompt before start writing to the TTY + readuntil(output_copy, JULIA_PROMPT) + sleep(0.1) + readavailable(output_copy) + # Input our script + precompile_lines = split(repl_script::String, '\n'; keepempty=false) + curr = 0 + for l in precompile_lines + sleep(0.1) + curr += 1 + # consume any other output + bytesavailable(output_copy) > 0 && readavailable(output_copy) + # push our input + write(debug_output, "\n#### inputting statement: ####\n$(repr(l))\n####\n") + write(ptm, l, "\n") + readuntil(output_copy, "\n") + # wait for the next prompt-like to appear + readuntil(output_copy, "\n") + strbuf = "" + while !eof(output_copy) + strbuf *= String(readavailable(output_copy)) + occursin(JULIA_PROMPT, strbuf) && break + occursin(PKG_PROMPT, strbuf) && break + occursin(SHELL_PROMPT, strbuf) && break + occursin(HELP_PROMPT, strbuf) && break + sleep(0.1) + end + end + write(ptm, "exit()\n") + wait(tee) + success(p) || Base.pipeline_error(p) + close(ptm) + write(debug_output, "\n#### FINISHED ####\n") + end + Base.errormonitor(repl_inputter) + + n_step = 0 + precompile_copy = Base.BufferStream() + buffer_reader = @async for statement in eachline(precompile_copy) + push!(statements_step, statement) + n_step += 1 + end + + open(precompile_file, "r") do io + while true + # We need to allways call eof(io) for bytesavailable(io) to work + eof(io) && istaskdone(repl_inputter) && eof(io) && break + if bytesavailable(io) == 0 + sleep(0.1) + continue + end + write(precompile_copy, readavailable(io)) + end + end + close(precompile_copy) + wait(buffer_reader) + close(statements_step) + return :ok + end + !PARALLEL_PRECOMPILATION && wait(step) + + # Make statements unique + statements = Set{String}() + # Execute the precompile statements + for statement in statements_step + # Main should be completely clean + occursin("Main.", statement) && continue + Base.in!(statement, statements) && continue + try + ps = Meta.parse(statement) + if !isexpr(ps, :call) + # these are typically comments + @debug "skipping statement because it does not parse as an expression" statement + delete!(statements, statement) + continue + end + popfirst!(ps.args) # precompile(...) + ps.head = :tuple + # println(ps) + ps = eval(ps) + if !precompile(ps...) + @warn "Failed to precompile expression" form=statement _module=nothing _file=nothing _line=0 + end + catch ex + # See #28808 + @warn "Failed to precompile expression" form=statement exception=ex _module=nothing _file=nothing _line=0 + end + end + + fetch(step) == :ok || throw("Collecting precompiles failed.") + return nothing +finally + GC.gc(true); GC.gc(false); # reduce memory footprint +end + +generate_precompile_statements() + +# As a last step in system image generation, +# remove some references to build time environment for a more reproducible build. +Base.Filesystem.temp_cleanup_purge(force=true) + +precompile(Tuple{typeof(getproperty), REPL.REPLBackend, Symbol}) +end # Precompile diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index b5365ca4fb98c1..0fb41ddacefc7f 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -6,6 +6,8 @@ using Random import REPL.LineEdit using Markdown +@test isassigned(Base.REPL_MODULE_REF) + const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") isdefined(Main, :FakePTYs) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "FakePTYs.jl")) import .Main.FakePTYs: with_fake_pty diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index c62febe941619e..325194afcfa9c3 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -1832,6 +1832,32 @@ let s = "Some(Issue36437(42)).value." end end +some_issue36437 = Some(Issue36437(42)) + +let s = "some_issue36437.value." + c, r, res = test_complete_context(s, @__MODULE__) + @test res + for n in ("a", "b", "c") + @test n in c + end +end + +# get completions for :toplevel/:tuple expressions +let s = "some_issue36437.value.a, some_issue36437.value." + c, r, res = test_complete_context(s, @__MODULE__) + @test res + for n in ("a", "b", "c") + @test n in c + end +end +let s = "@show some_issue36437.value.a; some_issue36437.value." + c, r, res = test_complete_context(s, @__MODULE__) + @test res + for n in ("a", "b", "c") + @test n in c + end +end + # aggressive concrete evaluation on mutable allocation in `repl_frame` let s = "Ref(Issue36437(42))[]." c, r, res = test_complete_context(s, @__MODULE__) @@ -1842,6 +1868,36 @@ let s = "Ref(Issue36437(42))[]." @test "v" ∉ c end +# concrete evaluation through `getindex`ing dictionary +global_dict = Dict{Symbol, Any}(:r => r"foo") +let s = "global_dict[:r]." + c, r, res = test_complete_context(s, @__MODULE__) + @test res + for fname in fieldnames(Regex) + @test String(fname) in c + end +end +global_dict_nested = Dict{Symbol, Any}(:g => global_dict) +let s = "global_dict_nested[:g][:r]." + c, r, res = test_complete_context(s, @__MODULE__) + @test res + for fname in fieldnames(Regex) + @test String(fname) in c + end +end + +# dict completions through nested `getindex`ing +let s = "global_dict_nested[" + c, r, res = test_complete_context(s, @__MODULE__) + @test res + @test ":g]" in c +end +let s = "global_dict_nested[:g][" + c, r, res = test_complete_context(s, @__MODULE__) + @test res + @test ":r]" in c +end + const global_xs = [Some(42)] let s = "pop!(global_xs)." c, r, res = test_complete_context(s, @__MODULE__) diff --git a/stdlib/Random/src/RNGs.jl b/stdlib/Random/src/RNGs.jl index a67ad65f6348be..39eba996996543 100644 --- a/stdlib/Random/src/RNGs.jl +++ b/stdlib/Random/src/RNGs.jl @@ -83,12 +83,12 @@ MersenneTwister(seed::Vector{UInt32}, state::DSFMT_state) = Create a `MersenneTwister` RNG object. Different RNG objects can have their own seeds, which may be useful for generating different streams of random numbers. -The `seed` may be a non-negative integer or a vector of -`UInt32` integers. If no seed is provided, a randomly generated one -is created (using entropy from the system). -See the [`seed!`](@ref) function for reseeding an already existing -`MersenneTwister` object. +The `seed` may be an integer or a vector of `UInt32` integers. +If no seed is provided, a randomly generated one is created (using entropy from the system). +See the [`seed!`](@ref) function for reseeding an already existing `MersenneTwister` object. +!!! compat "Julia 1.11" + Passing a negative integer seed requires at least Julia 1.11. # Examples ```jldoctest @@ -290,20 +290,48 @@ function make_seed() end end +""" + make_seed(n::Integer) -> Vector{UInt32} + +Transform `n` into a bit pattern encoded as a `Vector{UInt32}`, suitable for +RNG seeding routines. + +`make_seed` is "injective" : if `n != m`, then `make_seed(n) != `make_seed(m)`. +Moreover, if `n == m`, then `make_seed(n) == make_seed(m)`. + +This is an internal function, subject to change. +""" function make_seed(n::Integer) - n < 0 && throw(DomainError(n, "`n` must be non-negative.")) + neg = signbit(n) + if neg + n = ~n + end + @assert n >= 0 seed = UInt32[] - while true + # we directly encode the bit pattern of `n` into the resulting vector `seed`; + # to greatly limit breaking the streams of random numbers, we encode the sign bit + # as the upper bit of `seed[end]` (i.e. for most positive seeds, `make_seed` returns + # the same vector as when we didn't encode the sign bit) + while !iszero(n) push!(seed, n & 0xffffffff) - n >>= 32 - if n == 0 - return seed - end + n >>>= 32 + end + if isempty(seed) || !iszero(seed[end] & 0x80000000) + push!(seed, zero(UInt32)) end + if neg + seed[end] |= 0x80000000 + end + seed end # inverse of make_seed(::Integer) -from_seed(a::Vector{UInt32})::BigInt = sum(a[i] * big(2)^(32*(i-1)) for i in 1:length(a)) +function from_seed(a::Vector{UInt32})::BigInt + neg = !iszero(a[end] & 0x80000000) + seed = sum((i == length(a) ? a[i] & 0x7fffffff : a[i]) * big(2)^(32*(i-1)) + for i in 1:length(a)) + neg ? ~seed : seed +end #### seed!() diff --git a/stdlib/Random/src/Random.jl b/stdlib/Random/src/Random.jl index 78d4f15e2beac4..15165f53809457 100644 --- a/stdlib/Random/src/Random.jl +++ b/stdlib/Random/src/Random.jl @@ -394,6 +394,8 @@ sequence of numbers if and only if a `seed` is provided. Some RNGs don't accept a seed, like `RandomDevice`. After the call to `seed!`, `rng` is equivalent to a newly created object initialized with the same seed. +The types of accepted seeds depend on the type of `rng`, but in general, +integer seeds should work. If `rng` is not specified, it defaults to seeding the state of the shared task-local generator. diff --git a/stdlib/Random/src/Xoshiro.jl b/stdlib/Random/src/Xoshiro.jl index ceb9b1b2553396..133679c2019cac 100644 --- a/stdlib/Random/src/Xoshiro.jl +++ b/stdlib/Random/src/Xoshiro.jl @@ -4,7 +4,7 @@ # Lots of implementation is shared with TaskLocalRNG """ - Xoshiro(seed) + Xoshiro(seed::Integer) Xoshiro() Xoshiro256++ is a fast pseudorandom number generator described by David Blackman and @@ -21,6 +21,12 @@ multiple interleaved xoshiro instances). The virtual PRNGs are discarded once the bulk request has been serviced (and should cause no heap allocations). +If no seed is provided, a randomly generated one is created (using entropy from the system). +See the [`seed!`](@ref) function for reseeding an already existing `Xoshiro` object. + +!!! compat "Julia 1.11" + Passing a negative integer seed requires at least Julia 1.11. + # Examples ```jldoctest julia> using Random @@ -89,6 +95,12 @@ endianness and possibly word size. Using or seeding the RNG of any other task than the one returned by `current_task()` is undefined behavior: it will work most of the time, and may sometimes fail silently. + +When seeding `TaskLocalRNG()` with [`seed!`](@ref), the passed seed, if any, +may be any integer. + +!!! compat "Julia 1.11" + Seeding `TaskLocalRNG()` with a negative integer seed requires at least Julia 1.11. """ struct TaskLocalRNG <: AbstractRNG end TaskLocalRNG(::Nothing) = TaskLocalRNG() @@ -122,6 +134,8 @@ rng_native_52(::TaskLocalRNG) = UInt64 copy(rng::Union{TaskLocalRNG, Xoshiro}) = Xoshiro(getstate(rng)...) copy!(dst::Union{TaskLocalRNG, Xoshiro}, src::Union{TaskLocalRNG, Xoshiro}) = setstate!(dst, getstate(src)) ==(x::Union{TaskLocalRNG, Xoshiro}, y::Union{TaskLocalRNG, Xoshiro}) = getstate(x) == getstate(y) +# use a magic (random) number to scramble `h` so that `hash(x)` is distinct from `hash(getstate(x))` +hash(x::Union{TaskLocalRNG, Xoshiro}, h::UInt) = hash(getstate(x), h + 0x49a62c2dda6fa9be % UInt) function seed!(rng::Union{TaskLocalRNG, Xoshiro}) # as we get good randomness from RandomDevice, we can skip hashing diff --git a/stdlib/Random/test/runtests.jl b/stdlib/Random/test/runtests.jl index 94749b12123c34..0dba6ed8d94989 100644 --- a/stdlib/Random/test/runtests.jl +++ b/stdlib/Random/test/runtests.jl @@ -594,24 +594,41 @@ guardseed() do Random.seed!(typemax(UInt128)) end -# copy, == and hash -let seed = rand(UInt32, 10) - r = MersenneTwister(seed) - @test r == MersenneTwister(seed) # r.vals should be all zeros - @test hash(r) == hash(MersenneTwister(seed)) - s = copy(r) - @test s == r && s !== r - @test hash(s) == hash(r) - skip, len = rand(0:2000, 2) - for j=1:skip - rand(r) - rand(s) +@testset "copy, == and hash" begin + for RNG = (MersenneTwister, Xoshiro) + seed = rand(UInt32, 10) + r = RNG(seed) + t = RNG(seed) + @test r == t + @test hash(r) == hash(t) + s = copy(r) + @test s == r == t && s !== r + @test hash(s) == hash(r) + skip, len = rand(0:2000, 2) + for j=1:skip + rand(r) + @test r != s + @test hash(r) != hash(s) + rand(s) + end + @test rand(r, len) == rand(s, len) + @test s == r + @test hash(s) == hash(r) + h = rand(UInt) + @test hash(s, h) == hash(r, h) + if RNG == Xoshiro + t = copy(TaskLocalRNG()) + @test hash(t) == hash(TaskLocalRNG()) + @test hash(t, h) == hash(TaskLocalRNG(), h) + x = rand() + @test hash(t) != hash(TaskLocalRNG()) + @test rand(t) == x + @test hash(t) == hash(TaskLocalRNG()) + copy!(TaskLocalRNG(), r) + @test hash(TaskLocalRNG()) == hash(r) + @test TaskLocalRNG() == r + end end - @test rand(r, len) == rand(s, len) - @test s == r - @test hash(s) == hash(r) - h = rand(UInt) - @test hash(s, h) == hash(r, h) end # MersenneTwister initialization with invalid values @@ -930,6 +947,12 @@ end m = MersenneTwister(0); rand(m, Int64); rand(m) @test string(m) == "MersenneTwister(0, (0, 2256, 1254, 1, 0, 1))" @test m == MersenneTwister(0, (0, 2256, 1254, 1, 0, 1)) + + # negative seeds + Random.seed!(m, -3) + @test string(m) == "MersenneTwister(-3)" + Random.seed!(m, typemin(Int8)) + @test string(m) == "MersenneTwister(-128)" end @testset "RandomDevice" begin @@ -1088,3 +1111,33 @@ end @test TaskLocalRNG() == rng3 end end + +@testset "seed! and make_seed" begin + # Test that: + # 1) if n == m, then make_seed(n) == make_seed(m) + # 2) if n != m, then make_seed(n) != make_seed(m) + rngs = (Xoshiro(0), TaskLocalRNG(), MersenneTwister(0)) + seeds = Any[] + for T = Base.BitInteger_types + append!(seeds, rand(T, 8)) + push!(seeds, typemin(T), typemin(T) + T(1), typemin(T) + T(2), + typemax(T), typemax(T) - T(1), typemax(T) - T(2)) + T <: Signed && push!(seeds, T(0), T(1), T(2), T(-1), T(-2)) + end + + vseeds = Dict{Vector{UInt32}, BigInt}() + for seed = seeds + bigseed = big(seed) + vseed = Random.make_seed(bigseed) + # test property 1) above + @test Random.make_seed(seed) == vseed + # test property 2) above + @test bigseed == get!(vseeds, vseed, bigseed) + # test that the property 1) is actually inherited by `seed!` + for rng = rngs + rng2 = copy(Random.seed!(rng, seed)) + Random.seed!(rng, bigseed) + @test rng == rng2 + end + end +end diff --git a/stdlib/stdlib.mk b/stdlib/stdlib.mk new file mode 100644 index 00000000000000..99bdefc66fa90e --- /dev/null +++ b/stdlib/stdlib.mk @@ -0,0 +1,27 @@ +STDLIBS_WITHIN_SYSIMG := \ + Artifacts FileWatching Libdl SHA libblastrampoline_jll OpenBLAS_jll Random \ + LinearAlgebra Sockets + +INDEPENDENT_STDLIBS := \ + ArgTools Base64 CRC32c Dates DelimitedFiles Distributed Downloads Future \ + InteractiveUtils LazyArtifacts LibGit2 LibCURL Logging Markdown Mmap \ + NetworkOptions Profile Printf Pkg REPL Serialization SharedArrays SparseArrays \ + Statistics Tar Test TOML Unicode UUIDs \ + dSFMT_jll GMP_jll libLLVM_jll LLD_jll LLVMLibUnwind_jll LibUnwind_jll LibUV_jll \ + LibCURL_jll LibSSH2_jll LibGit2_jll nghttp2_jll MozillaCACerts_jll MbedTLS_jll \ + MPFR_jll OpenLibm_jll PCRE2_jll p7zip_jll Zlib_jll + + +STDLIBS := $(STDLIBS_WITHIN_SYSIMG) $(INDEPENDENT_STDLIBS) +VERSDIR := v$(shell cut -d. -f1-2 < $(JULIAHOME)/VERSION) + +SYSIMG_STDLIB_SRCS = +define STDLIB_srcs +$1_SRCS := $$(shell find $$(build_datarootdir)/julia/stdlib/$$(VERSDIR)/$1/src -name \*.jl) \ + $$(wildcard $$(build_prefix)/manifest/$$(VERSDIR)/$1) $$(build_datarootdir)/julia/stdlib/$$(VERSDIR)/$1/Project.toml +ifneq ($(filter $(1),$(STDLIBS_WITHIN_SYSIMG)),) + SYSIMG_STDLIB_SRCS += $$($1_SRCS) +endif +endef + +$(foreach stdlib,$(STDLIBS),$(eval $(call STDLIB_srcs,$(stdlib)))) diff --git a/sysimage.mk b/sysimage.mk index 993ee9a990058f..44c5762ec8b812 100644 --- a/sysimage.mk +++ b/sysimage.mk @@ -2,6 +2,7 @@ SRCDIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) BUILDDIR := . JULIAHOME := $(SRCDIR) include $(JULIAHOME)/Make.inc +include $(JULIAHOME)/stdlib/stdlib.mk default: sysimg-$(JULIA_BUILD_MODE) # contains either "debug" or "release" all: sysimg-release sysimg-debug @@ -53,8 +54,7 @@ COMPILER_SRCS += $(shell find $(JULIAHOME)/base/compiler -name \*.jl) # sort these to remove duplicates BASE_SRCS := $(sort $(shell find $(JULIAHOME)/base -name \*.jl -and -not -name sysimg.jl) \ $(shell find $(BUILDROOT)/base -name \*.jl -and -not -name sysimg.jl)) -STDLIB_SRCS := $(JULIAHOME)/base/sysimg.jl $(shell find $(build_datarootdir)/julia/stdlib/$(VERSDIR)/*/src -name \*.jl) \ - $(wildcard $(build_prefix)/manifest/$(VERSDIR)/*) +STDLIB_SRCS := $(JULIAHOME)/base/sysimg.jl $(SYSIMG_STDLIB_SRCS) RELBUILDROOT := $(call rel_path,$(JULIAHOME)/base,$(BUILDROOT)/base)/ # <-- make sure this always has a trailing slash $(build_private_libdir)/corecompiler.ji: $(COMPILER_SRCS) diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index b29904fb5eb6ca..b51d95669975c1 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -140,7 +140,8 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` "JULIA_LOAD_PATH" => "", "JULIA_DEPOT_PATH" => ";:", "HOME" => homedir())) - @test v == ("false\nREPL: InteractiveUtilstrue\n", true) + # @which is undefined + @test_broken v == ("false\nREPL: InteractiveUtilstrue\n", true) end let v = writereadpipeline("println(\"REPL: \", InteractiveUtils)", setenv(`$exename -i -e 'const InteractiveUtils = 3'`, @@ -159,7 +160,11 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` # make sure this is a non-fatal error and the REPL still loads @test v[1] @test isempty(v[2]) - @test startswith(v[3], "┌ Warning: Failed to import InteractiveUtils into module Main\n") + # Can't load REPL if it's outside the sysimg if we break the load path. + # Need to rewrite this test nicer + # ┌ Warning: REPL provider not available: using basic fallback + # └ @ Base client.jl:459 + @test_broken startswith(v[3], "┌ Warning: Failed to import InteractiveUtils into module Main\n") end real_threads = string(ccall(:jl_cpu_threads, Int32, ())) for nc in ("0", "-2", "x", "2x", " ", "") diff --git a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl index e96a6527002f96..b598388a3d1386 100644 --- a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl +++ b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl @@ -2259,7 +2259,7 @@ end # end # interprocedural analysis -# ------------------------ +# ======================== # propagate escapes imposed on call arguments @noinline broadcast_noescape1(a) = (broadcast(identity, a); nothing) @@ -2352,6 +2352,22 @@ let result = code_escapes() do @test has_no_escape(result.state[SSAValue(i)]) end +function with_self_aliased(from_bb::Int, succs::Vector{Int}) + worklist = Int[from_bb] + visited = BitSet(from_bb) + function visit!(bb::Int) + if bb ∉ visited + push!(visited, bb) + push!(worklist, bb) + end + end + while !isempty(worklist) + foreach(visit!, succs) + end + return visited +end +@test code_escapes(with_self_aliased) isa EAUtils.EscapeResult + # accounts for ThrownEscape via potential MethodError # no method error diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index 94eb46ee7810ac..d978c6db88805a 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -892,7 +892,7 @@ end |> Core.Compiler.is_foldable getfield(w, s) end |> Core.Compiler.is_foldable -# Flow-sensitive consistenct for _typevar +# Flow-sensitive consistent for _typevar @test Base.infer_effects() do return WrapperOneField == (WrapperOneField{T} where T) end |> Core.Compiler.is_foldable_nothrow @@ -1001,6 +1001,22 @@ isassigned_effects(s) = isassigned(Ref(s)) isassigned_effects(:foo) end +# inference on throw block should be disabled only when the effects are already known to be +# concrete-eval ineligible: +function optimize_throw_block_for_effects(x) + a = [x] + if x < 0 + throw(ArgumentError(lazy"negative number given: $x")) + end + return a +end +let effects = Base.infer_effects(optimize_throw_block_for_effects, (Int,)) + @test Core.Compiler.is_consistent_if_notreturned(effects) + @test Core.Compiler.is_effect_free(effects) + @test !Core.Compiler.is_nothrow(effects) + @test Core.Compiler.is_terminates(effects) +end + # :isdefined effects @test @eval Base.infer_effects() do @isdefined($(gensym("some_undef_symbol"))) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index e69b7ed864e9a4..52309744b4a054 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -3290,7 +3290,10 @@ end call_ntuple(a, b) = my_ntuple(i->(a+b; i), Val(4)) @test Base.return_types(call_ntuple, Tuple{Any,Any}) == [NTuple{4, Int}] @test length(code_typed(my_ntuple, Tuple{Any, Val{4}})) == 1 -@test_throws ErrorException code_typed(my_ntuple, Tuple{Any, Val}) +let (src, rt) = only(code_typed(my_ntuple, Tuple{Any, Val})) + @test src isa CodeInfo + @test rt == Tuple +end @generated unionall_sig_generated(::Vector{T}, b::Vector{S}) where {T, S} = :($b) @test length(code_typed(unionall_sig_generated, Tuple{Any, Vector{Int}})) == 1 @@ -5235,3 +5238,42 @@ end |> only == Val{true} @test code_typed() do b{c} = d... end |> only |> first isa Core.CodeInfo + +abstract_call_unionall_vararg(some::Some{Any}) = UnionAll(some.value...) +@test only(Base.return_types(abstract_call_unionall_vararg)) !== Union{} +let TV = TypeVar(:T) + t = Vector{TV} + some = Some{Any}((TV, t)) + @test abstract_call_unionall_vararg(some) isa UnionAll +end + +# use `Vararg` type constraints +use_vararg_constrant1(args::Vararg{T,N}) where {T,N} = Val(T), Val(N) +@test only(Base.return_types(use_vararg_constrant1, Tuple{Int,Int})) == Tuple{Val{Int},Val{2}} +use_vararg_constrant2(args::Vararg{T,N}) where {T,N} = Val(T), N +@test only(Base.return_types(use_vararg_constrant2, Tuple{Vararg{Int}})) == Tuple{Val{Int},Int} +use_vararg_constrant3(args::NTuple{N,T}) where {T,N} = Val(T), Val(N) +@test only(Base.return_types(use_vararg_constrant3, Tuple{Tuple{Int,Int}})) == Tuple{Val{Int},Val{2}} +use_vararg_constrant4(args::NTuple{N,T}) where {T,N} = Val(T), N +@test only(Base.return_types(use_vararg_constrant4, Tuple{NTuple{N,Int}} where N)) == Tuple{Val{Int},Int} + +# issue 51228 +global whatever_unknown_value51228 +f51228() = f51228(whatever_unknown_value51228) +f51228(x) = 1 +f51228(::Vararg{T,T}) where {T} = "2" +@test only(Base.return_types(f51228, ())) == Int + +struct A51317 + b::Tuple{1} + A1() = new() +end +struct An51317 + a::Int + b::Tuple{1} + An51317() = new() +end +@test only(Base.return_types((x,f) -> getfield(x, f), (A51317, Symbol))) === Union{} +@test only(Base.return_types((x,f) -> getfield(x, f), (An51317, Symbol))) === Int +@test only(Base.return_types(x -> getfield(x, :b), (A51317,))) === Union{} +@test only(Base.return_types(x -> getfield(x, :b), (An51317,))) === Union{} diff --git a/test/compiler/irpasses.jl b/test/compiler/irpasses.jl index 66d040fe8b893b..225839d8e0cf38 100644 --- a/test/compiler/irpasses.jl +++ b/test/compiler/irpasses.jl @@ -1519,3 +1519,51 @@ let ir = first(only(Base.code_ircode(f_with_early_try_catch_exit, (); optimize_u end @test isnothing(f_with_early_try_catch_exit()) + +# Issue #51144 - UndefRefError during compaction +let m = Meta.@lower 1 + 1 + @assert Meta.isexpr(m, :thunk) + src = m.args[1]::CodeInfo + src.code = Any[ + # block 1 → 2, 3 + #= %1: =# Expr(:(=), Core.SlotNumber(4), Core.Argument(2)), + #= %2: =# Expr(:call, :(===), Core.SlotNumber(4), nothing), + #= %3: =# GotoIfNot(Core.SSAValue(1), 5), + # block 2 + #= %4: =# ReturnNode(nothing), + # block 3 → 4, 5 + #= %5: =# Expr(:(=), Core.SlotNumber(4), false), + #= %6: =# GotoIfNot(Core.Argument(2), 8), + # block 4 → 5 + #= %7: =# Expr(:(=), Core.SlotNumber(4), true), + # block 5 + #= %8: =# ReturnNode(nothing), # Must not insert a π-node here + ] + nstmts = length(src.code) + nslots = 4 + src.ssavaluetypes = nstmts + src.codelocs = fill(Int32(1), nstmts) + src.ssaflags = fill(Int32(0), nstmts) + src.slotflags = fill(0, nslots) + src.slottypes = Any[Any, Union{Bool, Nothing}, Bool, Union{Bool, Nothing}] + ir = Core.Compiler.inflate_ir(src) + + mi = ccall(:jl_new_method_instance_uninit, Ref{Core.MethodInstance}, ()); + mi.specTypes = Tuple{} + mi.def = Module() + + # Simulate the important results from inference + interp = Core.Compiler.NativeInterpreter() + sv = Core.Compiler.OptimizationState(mi, src, interp) + slot_id = 4 + for block_id = 3:5 + # (_4 !== nothing) conditional narrows the type, triggering PiNodes + sv.bb_vartables[block_id][slot_id] = VarState(Bool, #= maybe_undef =# false) + end + + ir = Core.Compiler.convert_to_ircode(src, sv) + ir = Core.Compiler.slot2reg(ir, src, sv) + ir = Core.Compiler.compact!(ir) + + Core.Compiler.verify_ir(ir) +end diff --git a/test/dict.jl b/test/dict.jl index aceedabd6d91db..37067c304bb62f 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -1495,8 +1495,10 @@ end # getindex is :effect_free and :terminates but not :consistent for T in (Int, Float64, String, Symbol) - @test !Core.Compiler.is_consistent(Base.infer_effects(getindex, (Dict{T,Any}, T))) - @test Core.Compiler.is_effect_free(Base.infer_effects(getindex, (Dict{T,Any}, T))) - @test !Core.Compiler.is_nothrow(Base.infer_effects(getindex, (Dict{T,Any}, T))) - @test Core.Compiler.is_terminates(Base.infer_effects(getindex, (Dict{T,Any}, T))) + @testset let T=T + @test !Core.Compiler.is_consistent(Base.infer_effects(getindex, (Dict{T,Any}, T))) + @test_broken Core.Compiler.is_effect_free(Base.infer_effects(getindex, (Dict{T,Any}, T))) + @test !Core.Compiler.is_nothrow(Base.infer_effects(getindex, (Dict{T,Any}, T))) + @test_broken Core.Compiler.is_terminates(Base.infer_effects(getindex, (Dict{T,Any}, T))) + end end diff --git a/test/docs.jl b/test/docs.jl index a2f556b7ee8486..760aa76671c0ca 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -1028,7 +1028,7 @@ struct $(curmod_prefix)Undocumented.st3{T<:Integer, N} # Fields ``` -a :: Tuple{Vararg{T<:Integer, N}} +a :: NTuple{N, T<:Integer} b :: Array{Int64, N} c :: Int64 ``` @@ -1052,7 +1052,7 @@ struct $(curmod_prefix)Undocumented.st4{T, N} # Fields ``` a :: T -b :: Tuple{Vararg{T, N}} +b :: NTuple{N, T} ``` # Supertype Hierarchy diff --git a/test/loading.jl b/test/loading.jl index d002d10d0dab36..72a07835dd3390 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -60,7 +60,7 @@ let exename = `$(Base.julia_cmd()) --compiled-modules=yes --startup-file=no --co @test !endswith(s_dir, Base.Filesystem.path_separator) end -@test Base.in_sysimage(Base.PkgId(Base.UUID("cf7118a7-6976-5b1a-9a39-7adc72f591a4"), "UUIDs")) +@test Base.in_sysimage(Base.PkgId(Base.UUID("8f399da3-3557-5675-b5ff-fb832c97cbdb"), "Libdl")) @test Base.in_sysimage(Base.PkgId(Base.UUID("3a7fdc7e-7467-41b4-9f64-ea033d046d5b"), "NotAPackage")) == false ## Unit tests for safe file operations ## diff --git a/test/namedtuple.jl b/test/namedtuple.jl index eb3846c8cbffd2..babc61f267cc97 100644 --- a/test/namedtuple.jl +++ b/test/namedtuple.jl @@ -382,10 +382,22 @@ end # Test effect/inference for merge/diff of unknown NamedTuples for f in (Base.merge, Base.structdiff) - let eff = Base.infer_effects(f, Tuple{NamedTuple, NamedTuple}) - @test Core.Compiler.is_foldable(eff) && eff.nonoverlayed + @testset let f = f + # test the effects of the fallback path + fallback_func(a::NamedTuple, b::NamedTuple) = @invoke f(a::NamedTuple, b::NamedTuple) + @testset let eff = Base.infer_effects(fallback_func) + @test Core.Compiler.is_foldable(eff) + @test eff.nonoverlayed + end + @test only(Base.return_types(fallback_func)) == NamedTuple + # test if `max_methods = 4` setting works as expected + general_func(a::NamedTuple, b::NamedTuple) = f(a, b) + @testset let eff = Base.infer_effects(general_func) + @test Core.Compiler.is_foldable(eff) + @test eff.nonoverlayed + end + @test only(Base.return_types(general_func)) == NamedTuple end - @test Core.Compiler.return_type(f, Tuple{NamedTuple, NamedTuple}) == NamedTuple end @test Core.Compiler.is_foldable(Base.infer_effects(pairs, Tuple{NamedTuple})) diff --git a/test/precompile.jl b/test/precompile.jl index 4841dfb07ddcdd..ccc37a32e41c58 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -407,7 +407,17 @@ precompile_test_harness(false) do dir Base.PkgId(m) => Base.module_build_id(m) end for s in [Symbol(x.name) for x in Base._sysimage_modules if !(x.name in ["Base", "Core", "Main"])]), # plus test module, - Dict(Base.PkgId(Base.root_module(Base, :Test)) => Base.module_build_id(Base.root_module(Base, :Test))) + Dict(Base.PkgId(Base.root_module(Base, :Test)) => Base.module_build_id(Base.root_module(Base, :Test))), + # plus dependencies of test module + Dict(Base.PkgId(Base.root_module(Base, :InteractiveUtils)) => Base.module_build_id(Base.root_module(Base, :InteractiveUtils))), + Dict(Base.PkgId(Base.root_module(Base, :Logging)) => Base.module_build_id(Base.root_module(Base, :Logging))), + Dict(Base.PkgId(Base.root_module(Base, :Random)) => Base.module_build_id(Base.root_module(Base, :Random))), + Dict(Base.PkgId(Base.root_module(Base, :Serialization)) => Base.module_build_id(Base.root_module(Base, :Serialization))), + # and their dependencies + Dict(Base.PkgId(Base.root_module(Base, :SHA)) => Base.module_build_id(Base.root_module(Base, :SHA))), + Dict(Base.PkgId(Base.root_module(Base, :Markdown)) => Base.module_build_id(Base.root_module(Base, :Markdown))), + # and their dependencies + Dict(Base.PkgId(Base.root_module(Base, :Base64)) => Base.module_build_id(Base.root_module(Base, :Base64))), ) @test Dict(modules) == modules_ok diff --git a/test/reflection.jl b/test/reflection.jl index a67407c2d0f489..c0f32e39805e5a 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -1076,3 +1076,50 @@ private() = 1 end @test names(TestNames) == [:TestNames, :exported, :publicized] + +# reflections for generated function with abstract input types + +# :generated_only function should return failed results if given abstract input types +@generated function generated_only_simple(x) + if x <: Integer + return :(x ^ 2) + else + return :(x) + end +end +@test only(Base.return_types(generated_only_simple, (Real,))) == Core.Compiler.return_type(generated_only_simple, Tuple{Real}) == Any +let (src, rt) = only(code_typed(generated_only_simple, (Real,))) + @test src isa Method + @test rt == Any +end + +# optionally generated function should return fallback results if given abstract input types +function sub2ind_gen_impl(dims::Type{NTuple{N,Int}}, I...) where N + ex = :(I[$N] - 1) + for i = (N - 1):-1:1 + ex = :(I[$i] - 1 + dims[$i] * $ex) + end + return :($ex + 1) +end; +function sub2ind_gen_fallback(dims::NTuple{N,Int}, I) where N + ind = I[N] - 1 + for i = (N - 1):-1:1 + ind = I[i] - 1 + dims[i]*ind + end + return ind + 1 +end; +function sub2ind_gen(dims::NTuple{N,Int}, I::Integer...) where N + length(I) == N || error("partial indexing is unsupported") + if @generated + return sub2ind_gen_impl(dims, I...) + else + return sub2ind_gen_fallback(dims, I) + end +end; +@test only(Base.return_types(sub2ind_gen, (NTuple,Int,Int,))) == Int +let (src, rt) = only(code_typed(sub2ind_gen, (NTuple,Int,Int,); optimize=false)) + @test src isa CodeInfo + @test rt == Int + @test any(iscall((src,sub2ind_gen_fallback)), src.code) + @test any(iscall((src,error)), src.code) +end diff --git a/test/show.jl b/test/show.jl index 0aa4d805491b1f..f95f943c3c1a47 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1368,6 +1368,9 @@ test_repr("(:).a") @test repr(Tuple{Float32, Float32, Float32}) == "Tuple{Float32, Float32, Float32}" @test repr(Tuple{String, Int64, Int64, Int64}) == "Tuple{String, Int64, Int64, Int64}" @test repr(Tuple{String, Int64, Int64, Int64, Int64}) == "Tuple{String, Vararg{Int64, 4}}" +@test repr(NTuple) == "NTuple{N, T} where {N, T}" +@test repr(Tuple{NTuple{N}, Vararg{NTuple{N}, 4}} where N) == "NTuple{5, NTuple{N, T} where T} where N" +@test repr(Tuple{Float64, NTuple{N}, Vararg{NTuple{N}, 4}} where N) == "Tuple{Float64, Vararg{NTuple{N, T} where T, 5}} where N" # Test printing of NamedTuples using the macro syntax @test repr(@NamedTuple{kw::Int64}) == "@NamedTuple{kw::Int64}" @@ -1380,17 +1383,20 @@ test_repr("(:).a") @test repr(@Kwargs{init::Int}) == "Base.Pairs{Symbol, $Int, Tuple{Symbol}, @NamedTuple{init::$Int}}" @testset "issue #42931" begin - @test repr(NTuple{4, :A}) == "NTuple{4, :A}" + @test repr(NTuple{4, :A}) == "Tuple{:A, :A, :A, :A}" @test repr(NTuple{3, :A}) == "Tuple{:A, :A, :A}" @test repr(NTuple{2, :A}) == "Tuple{:A, :A}" @test repr(NTuple{1, :A}) == "Tuple{:A}" @test repr(NTuple{0, :A}) == "Tuple{}" @test repr(Tuple{:A, :A, :A, :B}) == "Tuple{:A, :A, :A, :B}" - @test repr(Tuple{:A, :A, :A, :A}) == "NTuple{4, :A}" + @test repr(Tuple{:A, :A, :A, :A}) == "Tuple{:A, :A, :A, :A}" @test repr(Tuple{:A, :A, :A}) == "Tuple{:A, :A, :A}" @test repr(Tuple{:A}) == "Tuple{:A}" @test repr(Tuple{}) == "Tuple{}" + + @test repr(Tuple{Vararg{N, 10}} where N) == "NTuple{10, N} where N" + @test repr(Tuple{Vararg{10, N}} where N) == "Tuple{Vararg{10, N}} where N" end # Test that REPL/mime display of invalid UTF-8 data doesn't throw an exception: