diff --git a/Makefile b/Makefile index 3d8bf5436b476..735d342a79eb5 100644 --- a/Makefile +++ b/Makefile @@ -382,6 +382,11 @@ endif cp -R -L $(JULIAHOME)/base/* $(DESTDIR)$(datarootdir)/julia/base cp -R -L $(JULIAHOME)/test/* $(DESTDIR)$(datarootdir)/julia/test cp -R -L $(build_datarootdir)/julia/* $(DESTDIR)$(datarootdir)/julia + + # Set .jl sources as read-only to match package directories + find $(DESTDIR)$(datarootdir)/julia/base -type f -name \*.jl -exec chmod 0444 '{}' \; + find $(DESTDIR)$(datarootdir)/julia/test -type f -name \*.jl -exec chmod 0444 '{}' \; + # Copy documentation cp -R -L $(BUILDROOT)/doc/_build/html $(DESTDIR)$(docdir)/ # Remove various files which should not be installed diff --git a/NEWS.md b/NEWS.md index 4bbe7645165dd..b5caaf5376fb5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -135,6 +135,10 @@ Standard library changes #### Profile +* `Profile.take_heap_snapshot` takes a new keyword argument, `redact_data::Bool`, + that is `true` by default. When set, the contents of Julia objects are not emitted + in the heap snapshot. This currently only applies to strings. ([#55326]) + #### Random #### REPL diff --git a/base/Base.jl b/base/Base.jl index 221ab90d8d2a9..10a8dd1532f92 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -424,7 +424,6 @@ include("weakkeydict.jl") # ScopedValues include("scopedvalues.jl") -using .ScopedValues # metaprogramming include("meta.jl") @@ -627,7 +626,7 @@ function start_profile_listener() # this will prompt any ongoing or pending event to flush also close(cond) # error-propagation is not needed, since the errormonitor will handle printing that better - _wait(t) + t === current_task() || _wait(t) end finalizer(cond) do c # if something goes south, still make sure we aren't keeping a reference in C to this @@ -648,7 +647,7 @@ function __init__() init_active_project() append!(empty!(_sysimage_modules), keys(loaded_modules)) empty!(explicit_loaded_modules) - @assert isempty(loaded_precompiles) + empty!(loaded_precompiles) # If we load a packageimage when building the image this might not be empty for (mod, key) in module_keys loaded_precompiles[key => module_build_id(mod)] = mod end diff --git a/base/Enums.jl b/base/Enums.jl index 6e9efd8ccde38..d4094945853ec 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -44,7 +44,7 @@ Base.print(io::IO, x::Enum) = print(io, _symbol(x)) function Base.show(io::IO, x::Enum) sym = _symbol(x) if !(get(io, :compact, false)::Bool) - from = get(io, :module, Base.active_module()) + from = get(io, :module, Main) def = parentmodule(typeof(x)) if from === nothing || !Base.isvisible(sym, def, from) show(io, def) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 3f8886e14940c..754ab20660ab8 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3532,7 +3532,7 @@ function push!(a::AbstractVector{T}, item) where T itemT = item isa T ? item : convert(T, item)::T new_length = length(a) + 1 resize!(a, new_length) - a[new_length] = itemT + a[end] = itemT return a end @@ -3540,7 +3540,7 @@ end function push!(a::AbstractVector{Any}, @nospecialize x) new_length = length(a) + 1 resize!(a, new_length) - a[new_length] = x + a[end] = x return a end function push!(a::AbstractVector{Any}, @nospecialize x...) @@ -3548,8 +3548,9 @@ function push!(a::AbstractVector{Any}, @nospecialize x...) na = length(a) nx = length(x) resize!(a, na + nx) + e = lastindex(a) - nx for i = 1:nx - a[na+i] = x[i] + a[e+i] = x[i] end return a end diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 90d395600bbde..f3fc4e0423173 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -793,10 +793,10 @@ function edge_matches_sv(interp::AbstractInterpreter, frame::AbsIntState, # otherwise, we don't # check in the cycle list first - # all items in here are mutual parents of all others + # all items in here are considered mutual parents of all others if !any(p::AbsIntState->matches_sv(p, sv), callers_in_cycle(frame)) let parent = frame_parent(frame) - parent !== nothing || return false + parent === nothing && return false (is_cached(parent) || frame_parent(parent) !== nothing) || return false matches_sv(parent, sv) || return false end @@ -1027,7 +1027,7 @@ collect_const_args(arginfo::ArgInfo, start::Int) = collect_const_args(arginfo.ar function collect_const_args(argtypes::Vector{Any}, start::Int) return Any[ let a = widenslotwrapper(argtypes[i]) isa(a, Const) ? a.val : - isconstType(a) ? (a::DataType).parameters[1] : + isconstType(a) ? a.parameters[1] : (a::DataType).instance end for i = start:length(argtypes) ] end @@ -1298,7 +1298,7 @@ function semi_concrete_eval_call(interp::AbstractInterpreter, if code !== nothing irsv = IRInterpretationState(interp, code, mi, arginfo.argtypes, world) if irsv !== nothing - irsv.parent = sv + assign_parentchild!(irsv, sv) rt, (nothrow, noub) = ir_abstract_constant_propagation(interp, irsv) @assert !(rt isa Conditional || rt isa MustAlias) "invalid lattice element returned from irinterp" if !(isa(rt, Type) && hasintersect(rt, Bool)) @@ -1376,11 +1376,17 @@ function const_prop_call(interp::AbstractInterpreter, add_remark!(interp, sv, "[constprop] Could not retrieve the source") return nothing # this is probably a bad generated function (unsound), but just ignore it end - frame.parent = sv + assign_parentchild!(frame, sv) if !typeinf(interp, frame) add_remark!(interp, sv, "[constprop] Fresh constant inference hit a cycle") + @assert frame.frameid != 0 && frame.cycleid == frame.frameid + callstack = frame.callstack::Vector{AbsIntState} + @assert callstack[end] === frame && length(callstack) == frame.frameid + pop!(callstack) return nothing end + @assert frame.frameid != 0 && frame.cycleid == frame.frameid + @assert frame.parentid == sv.frameid @assert inf_result.result !== nothing # ConditionalSimpleArgtypes is allowed, because the only case in which it modifies # the argtypes is when one of the argtypes is a `Conditional`, which case @@ -2006,26 +2012,39 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs return Conditional(aty.slot, thentype, elsetype) end elseif f === isdefined - uty = argtypes[2] a = ssa_def_slot(fargs[2], sv) - if isa(uty, Union) && isa(a, SlotNumber) - fld = argtypes[3] - thentype = Bottom - elsetype = Bottom - for ty in uniontypes(uty) - cnd = isdefined_tfunc(𝕃ᡒ, ty, fld) - if isa(cnd, Const) - if cnd.val::Bool - thentype = thentype βŠ” ty + if isa(a, SlotNumber) + argtype2 = argtypes[2] + if isa(argtype2, Union) + fld = argtypes[3] + thentype = Bottom + elsetype = Bottom + for ty in uniontypes(argtype2) + cnd = isdefined_tfunc(𝕃ᡒ, ty, fld) + if isa(cnd, Const) + if cnd.val::Bool + thentype = thentype βŠ” ty + else + elsetype = elsetype βŠ” ty + end else + thentype = thentype βŠ” ty elsetype = elsetype βŠ” ty end - else - thentype = thentype βŠ” ty - elsetype = elsetype βŠ” ty + end + return Conditional(a, thentype, elsetype) + else + thentype = form_partially_defined_struct(argtype2, argtypes[3]) + if thentype !== nothing + elsetype = argtype2 + if rt === Const(false) + thentype = Bottom + elseif rt === Const(true) + elsetype = Bottom + end + return Conditional(a, thentype, elsetype) end end - return Conditional(a, thentype, elsetype) end end end @@ -2033,6 +2052,34 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs return rt end +function form_partially_defined_struct(@nospecialize(obj), @nospecialize(name)) + obj isa Const && return nothing # nothing to refine + name isa Const || return nothing + objt0 = widenconst(obj) + objt = unwrap_unionall(objt0) + objt isa DataType || return nothing + isabstracttype(objt) && return nothing + fldidx = try_compute_fieldidx(objt, name.val) + fldidx === nothing && return nothing + nminfld = datatype_min_ninitialized(objt) + if ismutabletype(objt) + # A mutable struct can have non-contiguous undefined fields, but `PartialStruct` cannot + # model such a state. So here `PartialStruct` can be used to represent only the + # objects where the field following the minimum initialized fields is also defined. + if fldidx β‰  nminfld+1 + # if it is already represented as a `PartialStruct`, we can add one more + # `isdefined`-field information on top of those implied by its `fields` + if !(obj isa PartialStruct && fldidx == length(obj.fields)+1) + return nothing + end + end + else + fldidx > nminfld || return nothing + end + return PartialStruct(objt0, Any[obj isa PartialStruct && i≀length(obj.fields) ? + obj.fields[i] : fieldtype(objt0,i) for i = 1:fldidx]) +end + function abstract_call_unionall(interp::AbstractInterpreter, argtypes::Vector{Any}, call::CallMeta) na = length(argtypes) if isvarargtype(argtypes[end]) @@ -2573,20 +2620,18 @@ function abstract_eval_new(interp::AbstractInterpreter, e::Expr, vtypes::Union{V end ats[i] = at end - # For now, don't allow: - # - Const/PartialStruct of mutables (but still allow PartialStruct of mutables - # with `const` fields if anything refined) - # - partially initialized Const/PartialStruct - if fcount == nargs - if consistent === ALWAYS_TRUE && allconst - argvals = Vector{Any}(undef, nargs) - for j in 1:nargs - argvals[j] = (ats[j]::Const).val - end - rt = Const(ccall(:jl_new_structv, Any, (Any, Ptr{Cvoid}, UInt32), rt, argvals, nargs)) - elseif anyrefine - rt = PartialStruct(rt, ats) + if fcount == nargs && consistent === ALWAYS_TRUE && allconst + argvals = Vector{Any}(undef, nargs) + for j in 1:nargs + argvals[j] = (ats[j]::Const).val end + rt = Const(ccall(:jl_new_structv, Any, (Any, Ptr{Cvoid}, UInt32), rt, argvals, nargs)) + elseif anyrefine || nargs > datatype_min_ninitialized(rt) + # propagate partially initialized struct as `PartialStruct` when: + # - any refinement information is available (`anyrefine`), or when + # - `nargs` is greater than `n_initialized` derived from the struct type + # information alone + rt = PartialStruct(rt, ats) end else rt = refine_partial_type(rt) @@ -2688,6 +2733,8 @@ function abstract_eval_isdefined(interp::AbstractInterpreter, e::Expr, vtypes::U rt = Const(false) # never assigned previously elseif !vtyp.undef rt = Const(true) # definitely assigned previously + else # form `Conditional` to refine `vtyp.undef` in the then branch + rt = Conditional(sym, vtyp.typ, vtyp.typ; isdefined=true) end elseif isa(sym, GlobalRef) if InferenceParams(interp).assume_bindings_static @@ -2794,7 +2841,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp elseif ehead === :globaldecl return RTEffects(Nothing, Any, EFFECTS_UNKNOWN) elseif ehead === :thunk - return RTEffects(Any, Any, EFFECTS_UNKNOWN) + return RTEffects(Any, Any, Effects()) end # N.B.: abstract_eval_value_expr can modify the global effects, but # we move out any arguments with effects during SSA construction later @@ -3094,7 +3141,8 @@ end @nospecializeinfer function widenreturn_partials(𝕃ᡒ::PartialsLattice, @nospecialize(rt), info::BestguessInfo) if isa(rt, PartialStruct) fields = copy(rt.fields) - local anyrefine = false + anyrefine = !isvarargtype(rt.fields[end]) && + length(rt.fields) > datatype_min_ninitialized(unwrap_unionall(rt.typ)) 𝕃 = typeinf_lattice(info.interp) ⊏ = strictpartialorder(𝕃) for i in 1:length(fields) @@ -3159,7 +3207,7 @@ end @inline function abstract_eval_basic_statement(interp::AbstractInterpreter, @nospecialize(stmt), pc_vartable::VarTable, frame::InferenceState) if isa(stmt, NewvarNode) - changes = StateUpdate(stmt.slot, VarState(Bottom, true), false) + changes = StateUpdate(stmt.slot, VarState(Bottom, true)) return BasicStmtChange(changes, nothing, Union{}) elseif !isa(stmt, Expr) (; rt, exct) = abstract_eval_statement(interp, stmt, pc_vartable, frame) @@ -3174,7 +3222,7 @@ end end lhs = stmt.args[1] if isa(lhs, SlotNumber) - changes = StateUpdate(lhs, VarState(rt, false), false) + changes = StateUpdate(lhs, VarState(rt, false)) elseif isa(lhs, GlobalRef) handle_global_assignment!(interp, frame, lhs, rt) elseif !isa(lhs, SSAValue) @@ -3184,7 +3232,7 @@ end elseif hd === :method fname = stmt.args[1] if isa(fname, SlotNumber) - changes = StateUpdate(fname, VarState(Any, false), false) + changes = StateUpdate(fname, VarState(Any, false)) end return BasicStmtChange(changes, nothing, Union{}) elseif (hd === :code_coverage_effect || ( @@ -3298,7 +3346,6 @@ end # make as much progress on `frame` as possible (without handling cycles) function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) @assert !is_inferred(frame) - frame.dont_work_on_me = true # mark that this function is currently on the stack W = frame.ip ssavaluetypes = frame.ssavaluetypes bbs = frame.cfg.blocks @@ -3519,7 +3566,6 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) end end # while currbb <= nbbs - frame.dont_work_on_me = false nothing end @@ -3532,7 +3578,7 @@ function apply_refinement!(𝕃ᡒ::AbstractLattice, slot::SlotNumber, @nospecia oldtyp = vtype.typ ⊏ = strictpartialorder(𝕃ᡒ) if newtyp ⊏ oldtyp - stmtupdate = StateUpdate(slot, VarState(newtyp, vtype.undef), false) + stmtupdate = StateUpdate(slot, VarState(newtyp, vtype.undef)) stoverwrite1!(currstate, stmtupdate) end end @@ -3556,7 +3602,9 @@ function conditional_change(𝕃ᡒ::AbstractLattice, currstate::VarTable, condt # "causes" since we ignored those in the comparison newtyp = tmerge(𝕃ᡒ, newtyp, LimitedAccuracy(Bottom, oldtyp.causes)) end - return StateUpdate(SlotNumber(condt.slot), VarState(newtyp, vtype.undef), true) + # if this `Conditional` is from from `@isdefined condt.slot`, refine its `undef` information + newundef = condt.isdefined ? !then_or_else : vtype.undef + return StateUpdate(SlotNumber(condt.slot), VarState(newtyp, newundef), #=conditional=#true) end function condition_object_change(currstate::VarTable, condt::Conditional, @@ -3565,22 +3613,29 @@ function condition_object_change(currstate::VarTable, condt::Conditional, newcondt = Conditional(condt.slot, then_or_else ? condt.thentype : Union{}, then_or_else ? Union{} : condt.elsetype) - return StateUpdate(condslot, VarState(newcondt, vtype.undef), false) + return StateUpdate(condslot, VarState(newcondt, vtype.undef)) end # make as much progress on `frame` as possible (by handling cycles) function typeinf_nocycle(interp::AbstractInterpreter, frame::InferenceState) typeinf_local(interp, frame) + @assert isempty(frame.ip) + callstack = frame.callstack::Vector{AbsIntState} + frame.cycleid == length(callstack) && return true - # If the current frame is part of a cycle, solve the cycle before finishing no_active_ips_in_callers = false - while !no_active_ips_in_callers + while true + # If the current frame is not the top part of a cycle, continue to the top of the cycle before resuming work + frame.cycleid == frame.frameid || return false + # If done, return and finalize this cycle + no_active_ips_in_callers && return true + # Otherwise, do at least one iteration over the entire current cycle no_active_ips_in_callers = true - for caller in frame.callers_in_cycle - caller.dont_work_on_me && return false # cycle is above us on the stack + for i = reverse(frame.cycleid:length(callstack)) + caller = callstack[i]::InferenceState if !isempty(caller.ip) # Note that `typeinf_local(interp, caller)` can potentially modify the other frames - # `frame.callers_in_cycle`, which is why making incremental progress requires the + # `frame.cycleid`, which is why making incremental progress requires the # outer while loop. typeinf_local(interp, caller) no_active_ips_in_callers = false diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index 166df78f3130c..7778c96e019e5 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -169,12 +169,12 @@ const NOUB_IF_NOINBOUNDS = 0x01 << 1 # :nonoverlayed bits const CONSISTENT_OVERLAY = 0x01 << 1 -const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true) -const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true) -const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_TRUE, false) # unknown mostly, but it's not overlayed at least (e.g. it's not a call) -const _EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_FALSE, false) # unknown really +const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true) +const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE, true) +const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_TRUE, false) # unknown mostly, but it's not overlayed at least (e.g. it's not a call) -function Effects(effects::Effects = _EFFECTS_UNKNOWN; +function Effects(effects::Effects=Effects( + ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_FALSE, false); consistent::UInt8 = effects.consistent, effect_free::UInt8 = effects.effect_free, nothrow::Bool = effects.nothrow, diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 87647628f772e..6953dea5b9bd7 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -209,10 +209,10 @@ to enable flow-sensitive analysis. """ const VarTable = Vector{VarState} -const CACHE_MODE_NULL = 0x00 # not cached, without optimization -const CACHE_MODE_GLOBAL = 0x01 << 0 # cached globally, optimization allowed -const CACHE_MODE_LOCAL = 0x01 << 1 # cached locally, optimization allowed -const CACHE_MODE_VOLATILE = 0x01 << 2 # not cached, optimization allowed +const CACHE_MODE_NULL = 0x00 # not cached, optimization optional +const CACHE_MODE_GLOBAL = 0x01 << 0 # cached globally, optimization required +const CACHE_MODE_LOCAL = 0x01 << 1 # cached locally, optimization required +const CACHE_MODE_VOLATILE = 0x01 << 2 # not cached, optimization required mutable struct TryCatchFrame exct @@ -254,9 +254,12 @@ mutable struct InferenceState pclimitations::IdSet{InferenceState} # causes of precision restrictions (LimitedAccuracy) on currpc ssavalue limitations::IdSet{InferenceState} # causes of precision restrictions (LimitedAccuracy) on return cycle_backedges::Vector{Tuple{InferenceState, Int}} # call-graph backedges connecting from callee to caller - callers_in_cycle::Vector{InferenceState} - dont_work_on_me::Bool - parent # ::Union{Nothing,AbsIntState} + + # IPO tracking of in-process work, shared with all frames given AbstractInterpreter + callstack #::Vector{AbsIntState} + parentid::Int # index into callstack of the parent frame that originally added this frame (call frame_parent to extract the current parent of the SCC) + frameid::Int # index into callstack at which this object is found (or zero, if this is not a cached frame and has no parent) + cycleid::Int # index into the callstack of the topmost frame in the cycle (all frames in the same cycle share the same cycleid) #= results =# result::InferenceResult # remember where to put the result @@ -324,9 +327,7 @@ mutable struct InferenceState pclimitations = IdSet{InferenceState}() limitations = IdSet{InferenceState}() cycle_backedges = Vector{Tuple{InferenceState,Int}}() - callers_in_cycle = Vector{InferenceState}() - dont_work_on_me = false - parent = nothing + callstack = AbsIntState[] valid_worlds = WorldRange(1, get_world_counter()) bestguess = Bottom @@ -347,17 +348,23 @@ mutable struct InferenceState restrict_abstract_call_sites = isa(def, Module) - # some more setups - !iszero(cache_mode & CACHE_MODE_LOCAL) && push!(get_inference_cache(interp), result) - this = new( mi, world, mod, sptypes, slottypes, src, cfg, method_info, currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, ssavaluetypes, stmt_edges, stmt_info, - pclimitations, limitations, cycle_backedges, callers_in_cycle, dont_work_on_me, parent, + pclimitations, limitations, cycle_backedges, callstack, 0, 0, 0, result, unreachable, valid_worlds, bestguess, exc_bestguess, ipo_effects, restrict_abstract_call_sites, cache_mode, insert_coverage, interp) + # some more setups + if !iszero(cache_mode & CACHE_MODE_LOCAL) + push!(get_inference_cache(interp), result) + end + if !iszero(cache_mode & CACHE_MODE_GLOBAL) + push!(callstack, this) + this.cycleid = this.frameid = length(callstack) + end + # Apply generated function restrictions if src.min_world != 1 || src.max_world != typemax(UInt) # From generated functions @@ -769,30 +776,6 @@ function empty_backedges!(frame::InferenceState, currpc::Int=frame.currpc) return nothing end -function print_callstack(sv::InferenceState) - print("=================== Callstack: ==================\n") - idx = 0 - while sv !== nothing - print("[") - print(idx) - if !isa(sv.interp, NativeInterpreter) - print(", ") - print(typeof(sv.interp)) - end - print("] ") - print(sv.linfo) - is_cached(sv) || print(" [uncached]") - println() - for cycle in sv.callers_in_cycle - print(' ', cycle.linfo) - println() - end - sv = sv.parent - idx += 1 - end - print("================= End callstack ==================\n") -end - function narguments(sv::InferenceState, include_va::Bool=true) nargs = Int(sv.src.nargs) if !include_va @@ -818,7 +801,9 @@ mutable struct IRInterpretationState const lazyreachability::LazyCFGReachability valid_worlds::WorldRange const edges::Vector{Any} - parent # ::Union{Nothing,AbsIntState} + callstack #::Vector{AbsIntState} + frameid::Int + parentid::Int function IRInterpretationState(interp::AbstractInterpreter, method_info::MethodInfo, ir::IRCode, mi::MethodInstance, argtypes::Vector{Any}, @@ -841,9 +826,9 @@ mutable struct IRInterpretationState lazyreachability = LazyCFGReachability(ir) valid_worlds = WorldRange(min_world, max_world == typemax(UInt) ? get_world_counter() : max_world) edges = Any[] - parent = nothing + callstack = AbsIntState[] return new(method_info, ir, mi, world, curridx, argtypes_refined, ir.sptypes, tpdum, - ssa_refined, lazyreachability, valid_worlds, edges, parent) + ssa_refined, lazyreachability, valid_worlds, edges, callstack, 0, 0) end end @@ -863,11 +848,34 @@ function IRInterpretationState(interp::AbstractInterpreter, codeinst.min_world, codeinst.max_world) end + # AbsIntState # =========== const AbsIntState = Union{InferenceState,IRInterpretationState} +function print_callstack(frame::AbsIntState) + print("=================== Callstack: ==================\n") + frames = frame.callstack::Vector{AbsIntState} + for idx = (frame.frameid == 0 ? 0 : 1):length(frames) + sv = (idx == 0 ? frame : frames[idx]) + idx == frame.frameid && print("*") + print("[") + print(idx) + if sv isa InferenceState && !isa(sv.interp, NativeInterpreter) + print(", ") + print(typeof(sv.interp)) + end + print("] ") + print(frame_instance(sv)) + is_cached(sv) || print(" [uncached]") + sv.parentid == idx - 1 || print(" [parent=", sv.parentid, "]") + println() + @assert sv.frameid == idx + end + print("================= End callstack ==================\n") +end + frame_instance(sv::InferenceState) = sv.linfo frame_instance(sv::IRInterpretationState) = sv.mi @@ -878,8 +886,32 @@ function frame_module(sv::AbsIntState) return def.module end -frame_parent(sv::InferenceState) = sv.parent::Union{Nothing,AbsIntState} -frame_parent(sv::IRInterpretationState) = sv.parent::Union{Nothing,AbsIntState} +function frame_parent(sv::InferenceState) + sv.parentid == 0 && return nothing + callstack = sv.callstack::Vector{AbsIntState} + sv = callstack[sv.cycleid]::InferenceState + sv.parentid == 0 && return nothing + return callstack[sv.parentid] +end +frame_parent(sv::IRInterpretationState) = sv.parentid == 0 ? nothing : (sv.callstack::Vector{AbsIntState})[sv.parentid] + +# add the orphan child to the parent and the parent to the child +function assign_parentchild!(child::InferenceState, parent::AbsIntState) + @assert child.frameid in (0, 1) + child.callstack = callstack = parent.callstack::Vector{AbsIntState} + child.parentid = parent.frameid + push!(callstack, child) + child.cycleid = child.frameid = length(callstack) + nothing +end +function assign_parentchild!(child::IRInterpretationState, parent::AbsIntState) + @assert child.frameid in (0, 1) + child.callstack = callstack = parent.callstack::Vector{AbsIntState} + child.parentid = parent.frameid + push!(callstack, child) + child.frameid = length(callstack) + nothing +end function is_constproped(sv::InferenceState) (;overridden_by_const) = sv.result @@ -899,9 +931,6 @@ method_for_inference_limit_heuristics(sv::AbsIntState) = method_info(sv).method_ frame_world(sv::InferenceState) = sv.world frame_world(sv::IRInterpretationState) = sv.world -callers_in_cycle(sv::InferenceState) = sv.callers_in_cycle -callers_in_cycle(sv::IRInterpretationState) = () - function is_effect_overridden(sv::AbsIntState, effect::Symbol) if is_effect_overridden(frame_instance(sv), effect) return true @@ -938,20 +967,39 @@ Note that cycles may be visited in any order. struct AbsIntStackUnwind sv::AbsIntState end -iterate(unw::AbsIntStackUnwind) = (unw.sv, (unw.sv, 0)) -function iterate(unw::AbsIntStackUnwind, (sv, cyclei)::Tuple{AbsIntState, Int}) - # iterate through the cycle before walking to the parent - callers = callers_in_cycle(sv) - if callers !== () && cyclei < length(callers) - cyclei += 1 - parent = callers[cyclei] - else - cyclei = 0 - parent = frame_parent(sv) +iterate(unw::AbsIntStackUnwind) = (unw.sv, length(unw.sv.callstack::Vector{AbsIntState})) +function iterate(unw::AbsIntStackUnwind, frame::Int) + frame == 0 && return nothing + return ((unw.sv.callstack::Vector{AbsIntState})[frame], frame - 1) +end + +struct AbsIntCycle + frames::Vector{AbsIntState} + cycleid::Int + cycletop::Int +end +iterate(unw::AbsIntCycle) = unw.cycleid == 0 ? nothing : (unw.frames[unw.cycletop], unw.cycletop) +function iterate(unw::AbsIntCycle, frame::Int) + frame == unw.cycleid && return nothing + return (unw.frames[frame - 1], frame - 1) +end + +""" + callers_in_cycle(sv::AbsIntState) + +Iterate through all callers of the given `AbsIntState` in the abstract +interpretation stack (including the given `AbsIntState` itself) that are part +of the same cycle, only if it is part of a cycle with multiple frames. +""" +function callers_in_cycle(sv::InferenceState) + callstack = sv.callstack::Vector{AbsIntState} + cycletop = cycleid = sv.cycleid + while cycletop < length(callstack) && (callstack[cycletop + 1]::InferenceState).cycleid == cycleid + cycletop += 1 end - parent === nothing && return nothing - return (parent, (parent, cyclei)) + return AbsIntCycle(callstack, cycletop == cycleid ? 0 : cycleid, cycletop) end +callers_in_cycle(sv::IRInterpretationState) = AbsIntCycle(sv.callstack::Vector{AbsIntState}, 0, 0) # temporarily accumulate our edges to later add as backedges in the callee function add_backedge!(caller::InferenceState, mi::MethodInstance) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 936b604d373a0..fb712b1c71b12 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -59,6 +59,12 @@ const IR_FLAGS_NEEDS_EA = IR_FLAG_EFIIMO | IR_FLAG_INACCESSIBLEMEM_OR_ARGMEM has_flag(curr::UInt32, flag::UInt32) = (curr & flag) == flag +function iscallstmt(@nospecialize stmt) + stmt isa Expr || return false + head = stmt.head + return head === :call || head === :invoke || head === :foreigncall +end + function flags_for_effects(effects::Effects) flags = zero(UInt32) if is_consistent(effects) @@ -380,7 +386,7 @@ function recompute_effects_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), elseif nothrow flag |= IR_FLAG_NOTHROW end - if !(isexpr(stmt, :call) || isexpr(stmt, :invoke)) + if !iscallstmt(stmt) # There is a bit of a subtle point here, which is that some non-call # statements (e.g. PiNode) can be UB:, however, we consider it # illegal to introduce such statements that actually cause UB (for any @@ -784,7 +790,7 @@ function scan_non_dataflow_flags!(inst::Instruction, sv::PostOptAnalysisState) if !has_flag(flag, IR_FLAG_NORTCALL) # if a function call that might invoke `Core.Compiler.return_type` has been deleted, # there's no need to taint with `:nortcall`, allowing concrete evaluation - if isexpr(stmt, :call) || isexpr(stmt, :invoke) + if iscallstmt(stmt) sv.nortcall = false end end diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index c665c5bef299e..960da88ddffc8 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -2,7 +2,8 @@ Core.PhiNode() = Core.PhiNode(Int32[], Any[]) -isterminator(@nospecialize(stmt)) = isa(stmt, GotoNode) || isa(stmt, GotoIfNot) || isa(stmt, ReturnNode) || isa(stmt, EnterNode) || isexpr(stmt, :leave) +isterminator(@nospecialize(stmt)) = isa(stmt, GotoNode) || isa(stmt, GotoIfNot) || + isa(stmt, ReturnNode) || isa(stmt, EnterNode) || isexpr(stmt, :leave) struct CFG blocks::Vector{BasicBlock} diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index 83881354e494e..1aeb87accbcd7 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -24,7 +24,7 @@ function concrete_eval_invoke(interp::AbstractInterpreter, ci::CodeInstance, arg end newirsv = IRInterpretationState(interp, ci, mi, argtypes, world) if newirsv !== nothing - newirsv.parent = parent + assign_parentchild!(newirsv, parent) return ir_abstract_constant_propagation(interp, newirsv) end return Pair{Any,Tuple{Bool,Bool}}(nothing, (is_nothrow(effects), is_noub(effects))) @@ -141,7 +141,8 @@ function reprocess_instruction!(interp::AbstractInterpreter, inst::Instruction, rt = nothing if isa(stmt, Expr) head = stmt.head - if head === :call || head === :foreigncall || head === :new || head === :splatnew || head === :static_parameter || head === :isdefined || head === :boundscheck + if (head === :call || head === :foreigncall || head === :new || head === :splatnew || + head === :static_parameter || head === :isdefined || head === :boundscheck) (; rt, effects) = abstract_eval_statement_expr(interp, stmt, nothing, irsv) add_flag!(inst, flags_for_effects(effects)) elseif head === :invoke @@ -440,6 +441,12 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR store_backedges(frame_instance(irsv), irsv.edges) end + if irsv.frameid != 0 + callstack = irsv.callstack::Vector{AbsIntState} + @assert callstack[end] === irsv && length(callstack) == irsv.frameid + pop!(callstack) + end + return Pair{Any,Tuple{Bool,Bool}}(maybe_singleton_const(ultimate_rt), (nothrow, noub)) end diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 33cda9bf27d20..37d79e2bd7b0c 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1166,7 +1166,12 @@ struct IntermediaryCollector <: WalkerCallback intermediaries::SPCSet end function (walker_callback::IntermediaryCollector)(@nospecialize(def), @nospecialize(defssa::AnySSAValue)) - isa(def, Expr) || push!(walker_callback.intermediaries, defssa.id) + if !(def isa Expr) + push!(walker_callback.intermediaries, defssa.id) + if def isa PiNode + return LiftedValue(def.val) + end + end return nothing end diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index 756dc98863af5..e70633ffecf6a 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -88,6 +88,9 @@ function fixup_slot!(ir::IRCode, ci::CodeInfo, idx::Int, slot::Int, @nospecializ insert_node!(ir, idx, NewInstruction( Expr(:throw_undef_if_not, ci.slotnames[slot], false), Any)) return UNDEF_TOKEN + elseif has_flag(ir.stmts[idx], IR_FLAG_NOTHROW) + # if the `isdefined`-ness of this slot is guaranteed by abstract interpretation, + # there is no need to form a `:throw_undef_if_not` elseif def_ssa !== true insert_node!(ir, idx, NewInstruction( Expr(:throw_undef_if_not, ci.slotnames[slot], def_ssa), Any)) @@ -153,12 +156,12 @@ end function fixup_uses!(ir::IRCode, ci::CodeInfo, code::Vector{Any}, uses::Vector{Int}, slot::Int, @nospecialize(ssa)) for use in uses - code[use] = fixemup!(x::SlotNumber->slot_id(x)==slot, stmt::SlotNumber->(ssa, true), ir, ci, use, code[use]) + code[use] = fixemup!(x::SlotNumber->slot_id(x)==slot, ::SlotNumber->Pair{Any,Any}(ssa, true), ir, ci, use, code[use]) end end function rename_uses!(ir::IRCode, ci::CodeInfo, idx::Int, @nospecialize(stmt), renames::Vector{Pair{Any, Any}}) - return fixemup!(stmt::SlotNumber->true, stmt::SlotNumber->renames[slot_id(stmt)], ir, ci, idx, stmt) + return fixemup!(::SlotNumber->true, x::SlotNumber->renames[slot_id(x)], ir, ci, idx, stmt) end # maybe use expr_type? @@ -656,7 +659,7 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, sv::OptimizationState, visited = BitSet() new_nodes = ir.new_nodes @timeit "SSA Rename" while !isempty(worklist) - (item::Int, pred, incoming_vals) = pop!(worklist) + (item, pred, incoming_vals) = pop!(worklist) if sv.bb_vartables[item] === nothing continue end diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 9a4c761b4209b..0c57c04a6ddea 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -419,7 +419,7 @@ end else return Bottom end - if 1 <= idx <= datatype_min_ninitialized(a1) + if 1 ≀ idx ≀ datatype_min_ninitialized(a1) return Const(true) elseif a1.name === _NAMEDTUPLE_NAME if isconcretetype(a1) @@ -427,15 +427,21 @@ end else ns = a1.parameters[1] if isa(ns, Tuple) - return Const(1 <= idx <= length(ns)) + return Const(1 ≀ idx ≀ length(ns)) end end - elseif idx <= 0 || (!isvatuple(a1) && idx > fieldcount(a1)) + elseif idx ≀ 0 || (!isvatuple(a1) && idx > fieldcount(a1)) return Const(false) elseif isa(arg1, Const) if !ismutabletype(a1) || isconst(a1, idx) return Const(isdefined(arg1.val, idx)) end + elseif isa(arg1, PartialStruct) + if !isvarargtype(arg1.fields[end]) + if 1 ≀ idx ≀ length(arg1.fields) + return Const(true) + end + end elseif !isvatuple(a1) fieldT = fieldtype(a1, idx) if isa(fieldT, DataType) && isbitstype(fieldT) @@ -989,27 +995,39 @@ end βŠ‘ = partialorder(𝕃) # If we have s00 being a const, we can potentially refine our type-based analysis above - if isa(s00, Const) || isconstType(s00) - if !isa(s00, Const) - sv = (s00::DataType).parameters[1] - else + if isa(s00, Const) || isconstType(s00) || isa(s00, PartialStruct) + if isa(s00, Const) sv = s00.val + sty = typeof(sv) + nflds = nfields(sv) + ismod = sv isa Module + elseif isa(s00, PartialStruct) + sty = unwrap_unionall(s00.typ) + nflds = fieldcount_noerror(sty) + ismod = false + else + sv = (s00::DataType).parameters[1] + sty = typeof(sv) + nflds = nfields(sv) + ismod = sv isa Module end if isa(name, Const) nval = name.val if !isa(nval, Symbol) - isa(sv, Module) && return false + ismod && return false isa(nval, Int) || return false end return isdefined_tfunc(𝕃, s00, name) === Const(true) end - boundscheck && return false + # If bounds checking is disabled and all fields are assigned, # we may assume that we don't throw - isa(sv, Module) && return false + @assert !boundscheck + ismod && return false name βŠ‘ Int || name βŠ‘ Symbol || return false - typeof(sv).name.n_uninitialized == 0 && return true - for i = (datatype_min_ninitialized(typeof(sv)) + 1):nfields(sv) + sty.name.n_uninitialized == 0 && return true + nflds === nothing && return false + for i = (datatype_min_ninitialized(sty)+1):nflds isdefined_tfunc(𝕃, s00, Const(i)) === Const(true) || return false end return true @@ -2948,7 +2966,7 @@ end function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::AbsIntState, max_methods::Int) length(argtypes) < 2 && return CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo()) - isvarargtype(argtypes[2]) && return CallMeta(Bool, Any, EFFECTS_UNKNOWN, NoCallInfo()) + isvarargtype(argtypes[2]) && return CallMeta(Bool, Any, EFFECTS_THROWS, NoCallInfo()) argtypes = argtypes[2:end] atype = argtypes_to_type(argtypes) matches = find_method_matches(interp, argtypes, atype; max_methods) @@ -3173,6 +3191,11 @@ function foreigncall_effects(@specialize(abstract_eval), e::Expr) elseif name === :jl_genericmemory_copy_slice return Effects(EFFECTS_TOTAL; consistent=CONSISTENT_IF_NOTRETURNED, nothrow=false) end + # `:foreigncall` can potentially perform all sorts of operations, including calling + # overlay methods, but the `:foreigncall` itself is not dispatched, and there is no + # concern that the method calls that potentially occur within the `:foreigncall` will + # be executed using the wrong method table due to concrete evaluation, so using + # `EFFECTS_UNKNOWN` here and not tainting with `:nonoverlayed` is fine return EFFECTS_UNKNOWN end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 41fb774266f25..e2f2a1f2cc975 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -261,36 +261,37 @@ end function _typeinf(interp::AbstractInterpreter, frame::InferenceState) typeinf_nocycle(interp, frame) || return false # frame is now part of a higher cycle # with no active ip's, frame is done - frames = frame.callers_in_cycle - if isempty(frames) - finish_nocycle(interp, frame) - elseif length(frames) == 1 - @assert frames[1] === frame "invalid callers_in_cycle" + frames = frame.callstack::Vector{AbsIntState} + if length(frames) == frame.cycleid finish_nocycle(interp, frame) else - finish_cycle(interp, frames) + @assert frame.cycleid != 0 + finish_cycle(interp, frames, frame.cycleid) end - empty!(frames) return true end function finish_nocycle(::AbstractInterpreter, frame::InferenceState) - frame.dont_work_on_me = true finishinfer!(frame, frame.interp) opt = frame.result.src if opt isa OptimizationState # implies `may_optimize(caller.interp) === true` optimize(frame.interp, opt, frame.result) end finish!(frame.interp, frame) + if frame.cycleid != 0 + frames = frame.callstack::Vector{AbsIntState} + @assert frames[end] === frame + pop!(frames) + end return nothing end -function finish_cycle(::AbstractInterpreter, frames::Vector{InferenceState}) +function finish_cycle(::AbstractInterpreter, frames::Vector{AbsIntState}, cycleid::Int) cycle_valid_worlds = WorldRange() cycle_valid_effects = EFFECTS_TOTAL - for caller in frames - @assert !(caller.dont_work_on_me) - caller.dont_work_on_me = true + for caller in cycleid:length(frames) + caller = frames[caller]::InferenceState + @assert caller.cycleid == cycleid # converge the world age range and effects for this cycle here: # all frames in the cycle should have the same bits of `valid_worlds` and `effects` # that are simply the intersection of each partial computation, without having @@ -298,19 +299,23 @@ function finish_cycle(::AbstractInterpreter, frames::Vector{InferenceState}) cycle_valid_worlds = intersect(cycle_valid_worlds, caller.valid_worlds) cycle_valid_effects = merge_effects(cycle_valid_effects, caller.ipo_effects) end - for caller in frames + for caller in cycleid:length(frames) + caller = frames[caller]::InferenceState adjust_cycle_frame!(caller, cycle_valid_worlds, cycle_valid_effects) finishinfer!(caller, caller.interp) end - for caller in frames + for caller in cycleid:length(frames) + caller = frames[caller]::InferenceState opt = caller.result.src if opt isa OptimizationState # implies `may_optimize(caller.interp) === true` optimize(caller.interp, opt, caller.result) end end - for caller in frames + for caller in cycleid:length(frames) + caller = frames[caller]::InferenceState finish!(caller.interp, caller) end + resize!(frames, cycleid - 1) return nothing end @@ -396,9 +401,9 @@ end function cycle_fix_limited(@nospecialize(typ), sv::InferenceState) if typ isa LimitedAccuracy - if sv.parent === nothing + if sv.parentid === 0 # we might have introduced a limit marker, but we should know it must be sv and other callers_in_cycle - #@assert !isempty(sv.callers_in_cycle) + #@assert !isempty(callers_in_cycle(sv)) # FIXME: this assert fails, appearing to indicate there is a bug in filtering this list earlier. # In particular (during doctests for example), during inference of # show(Base.IOContext{Base.GenericIOBuffer{Memory{UInt8}}}, Base.Multimedia.MIME{:var"text/plain"}, LinearAlgebra.BunchKaufman{Float64, Array{Float64, 2}, Array{Int64, 1}}) @@ -407,7 +412,7 @@ function cycle_fix_limited(@nospecialize(typ), sv::InferenceState) end causes = copy(typ.causes) delete!(causes, sv) - for caller in sv.callers_in_cycle + for caller in callers_in_cycle(sv) delete!(causes, caller) end if isempty(causes) @@ -521,6 +526,7 @@ end # update the MethodInstance function finishinfer!(me::InferenceState, interp::AbstractInterpreter) # prepare to run optimization passes on fulltree + @assert isempty(me.ip) s_edges = get_stmt_edges!(me, 1) for i = 2:length(me.stmt_edges) isassigned(me.stmt_edges, i) || continue @@ -541,7 +547,7 @@ function finishinfer!(me::InferenceState, interp::AbstractInterpreter) gt = me.ssavaluetypes for j = 1:length(gt) gt[j] = gtj = cycle_fix_limited(gt[j], me) - if gtj isa LimitedAccuracy && me.parent !== nothing + if gtj isa LimitedAccuracy && me.parentid != 0 limited_src = true break end @@ -573,10 +579,10 @@ function finishinfer!(me::InferenceState, interp::AbstractInterpreter) type_annotate!(interp, me) mayopt = may_optimize(interp) doopt = mayopt && - # disable optimization if we don't use this later - (me.cache_mode != CACHE_MODE_NULL || me.parent !== nothing) && + # disable optimization if we don't use this later (because it is not cached) + me.cache_mode != CACHE_MODE_NULL && # disable optimization if we've already obtained very accurate result - !result_is_constabi(interp, result, mayopt) + !result_is_constabi(interp, result) if doopt result.src = OptimizationState(me, interp) else @@ -746,41 +752,29 @@ function type_annotate!(interp::AbstractInterpreter, sv::InferenceState) return nothing end -# at the end, all items in b's cycle -# will now be added to a's cycle -function union_caller_cycle!(a::InferenceState, b::InferenceState) - callers_in_cycle = b.callers_in_cycle - b.parent = a.parent - b.callers_in_cycle = a.callers_in_cycle - contains_is(a.callers_in_cycle, b) || push!(a.callers_in_cycle, b) - if callers_in_cycle !== a.callers_in_cycle - for caller in callers_in_cycle - if caller !== b - caller.parent = a.parent - caller.callers_in_cycle = a.callers_in_cycle - push!(a.callers_in_cycle, caller) - end - end - end - return -end - -function merge_call_chain!(interp::AbstractInterpreter, parent::InferenceState, ancestor::InferenceState, child::InferenceState) +function merge_call_chain!(interp::AbstractInterpreter, parent::InferenceState, child::InferenceState) # add backedge of parent <- child # then add all backedges of parent <- parent.parent - # and merge all of the callers into ancestor.callers_in_cycle - # and ensure that walking the parent list will get the same result (DAG) from everywhere + frames = parent.callstack::Vector{AbsIntState} + @assert child.callstack === frames + ancestorid = child.cycleid while true add_cycle_backedge!(parent, child) - union_caller_cycle!(ancestor, child) + parent.cycleid === ancestorid && break child = parent - child === ancestor && break parent = frame_parent(child) while !isa(parent, InferenceState) # XXX we may miss some edges here? parent = frame_parent(parent::IRInterpretationState) end - parent = parent::InferenceState + end + # ensure that walking the callstack has the same cycleid (DAG) + for frame = reverse(ancestorid:length(frames)) + frame = frames[frame] + frame isa InferenceState || continue + frame.cycleid == ancestorid && break + @assert frame.cycleid > ancestorid + frame.cycleid = ancestorid end end @@ -796,8 +790,8 @@ end # Walk through `mi`'s upstream call chain, starting at `parent`. If a parent # frame matching `mi` is encountered, then there is a cycle in the call graph # (i.e. `mi` is a descendant callee of itself). Upon encountering this cycle, -# we "resolve" it by merging the call chain, which entails unioning each intermediary -# frame's `callers_in_cycle` field and adding the appropriate backedges. Finally, +# we "resolve" it by merging the call chain, which entails updating each intermediary +# frame's `cycleid` field and adding the appropriate backedges. Finally, # we return `mi`'s pre-existing frame. If no cycles are found, `nothing` is # returned instead. function resolve_call_cycle!(interp::AbstractInterpreter, mi::MethodInstance, parent::AbsIntState) @@ -805,10 +799,11 @@ function resolve_call_cycle!(interp::AbstractInterpreter, mi::MethodInstance, pa # This works just because currently the `:terminate` condition guarantees that # irinterp doesn't fail into unresolved cycles, but it's not a good solution. # We should revisit this once we have a better story for handling cycles in irinterp. - isa(parent, InferenceState) || return false - frame = parent + frames = parent.callstack::Vector{AbsIntState} uncached = false - while isa(frame, InferenceState) + for frame = reverse(1:length(frames)) + frame = frames[frame] + isa(frame, InferenceState) || break uncached |= !is_cached(frame) # ensure we never add an uncached frame to a cycle if is_same_frame(interp, mi, frame) if uncached @@ -818,20 +813,9 @@ function resolve_call_cycle!(interp::AbstractInterpreter, mi::MethodInstance, pa poison_callstack!(parent, frame) return true end - merge_call_chain!(interp, parent, frame, frame) + merge_call_chain!(interp, parent, frame) return frame end - for caller in callers_in_cycle(frame) - if is_same_frame(interp, mi, caller) - if uncached - poison_callstack!(parent, frame) - return true - end - merge_call_chain!(interp, parent, frame, caller) - return caller - end - end - frame = frame_parent(frame) end return false end @@ -920,9 +904,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize end return EdgeCallResult(Any, Any, nothing, Effects()) end - if is_cached(caller) || frame_parent(caller) !== nothing # don't involve uncached functions in cycle resolution - frame.parent = caller - end + assign_parentchild!(frame, caller) typeinf(interp, frame) update_valid_age!(caller, frame.valid_worlds) isinferred = is_inferred(frame) @@ -1011,9 +993,8 @@ function codeinstance_for_const_with_code(interp::AbstractInterpreter, code::Cod code.relocatability, src.debuginfo) end -result_is_constabi(interp::AbstractInterpreter, result::InferenceResult, - run_optimizer::Bool=may_optimize(interp)) = - run_optimizer && may_discard_trees(interp) && is_result_constabi_eligible(result) +result_is_constabi(interp::AbstractInterpreter, result::InferenceResult) = + may_discard_trees(interp) && is_result_constabi_eligible(result) # compute an inferred AST and return type typeinf_code(interp::AbstractInterpreter, match::MethodMatch, run_optimizer::Bool) = @@ -1024,11 +1005,6 @@ typeinf_code(interp::AbstractInterpreter, method::Method, @nospecialize(atype), function typeinf_code(interp::AbstractInterpreter, mi::MethodInstance, run_optimizer::Bool) frame = typeinf_frame(interp, mi, run_optimizer) frame === nothing && return nothing - is_inferred(frame) || return nothing - if result_is_constabi(interp, frame.result, run_optimizer) - rt = frame.result.result::Const - return codeinfo_for_const(interp, frame.linfo, rt.val) - end return frame.src end @@ -1051,17 +1027,14 @@ typeinf_ircode(interp::AbstractInterpreter, method::Method, @nospecialize(atype) 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, mi, false) if frame === nothing - ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) return nothing, Any end (; result) = frame opt = OptimizationState(frame, interp) ir = run_passes_ipo_safe(opt.src, opt, result, optimize_until) rt = widenconst(ignorelimited(result.result)) - ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) return ir, rt end @@ -1072,13 +1045,22 @@ typeinf_frame(interp::AbstractInterpreter, method::Method, @nospecialize(atype), 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)) - cache_mode = run_optimizer ? :global : :no - frame = InferenceState(result, cache_mode, interp) + frame = InferenceState(result, #=cache_mode=#:no, interp) frame === nothing && return nothing typeinf(interp, frame) - ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) + is_inferred(frame) || return nothing + if run_optimizer + if result_is_constabi(interp, frame.result) + rt = frame.result.result::Const + opt = codeinfo_for_const(interp, frame.linfo, rt.val) + else + opt = OptimizationState(frame, interp) + optimize(interp, opt, frame.result) + opt = ir_to_codeinf!(opt) + end + result.src = frame.src = opt + end return frame end diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index 1be76f7d8bea3..86fa8af21615f 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -6,17 +6,42 @@ # N.B.: Const/PartialStruct/InterConditional are defined in Core, to allow them to be used # inside the global code cache. -# -# # The type of a value might be constant -# struct Const -# val -# end -# -# struct PartialStruct -# typ -# fields::Vector{Any} # elements are other type lattice members -# end + import Core: Const, PartialStruct + +""" + struct Const + val + end + +The type representing a constant value. +""" +:(Const) + +""" + struct PartialStruct + typ + fields::Vector{Any} # elements are other type lattice members + end + +This extended lattice element is introduced when we have information about an object's +fields beyond what can be obtained from the object type. E.g. it represents a tuple where +some elements are known to be constants or a struct whose `Any`-typed field is initialized +with `Int` values. + +- `typ` indicates the type of the object +- `fields` holds the lattice elements corresponding to each field of the object + +If `typ` is a struct, `fields` represents the fields of the struct that are guaranteed to be +initialized. For instance, if the length of `fields` of `PartialStruct` representing a +struct with 4 fields is 3, the 4th field may not be initialized. If the length is 4, all +fields are guaranteed to be initialized. + +If `typ` is a tuple, the last element of `fields` may be `Vararg`. In this case, it is +guaranteed that the number of elements in the tuple is at least `length(fields)-1`, but the +exact number of elements is unknown. +""" +:(PartialStruct) function PartialStruct(@nospecialize(typ), fields::Vector{Any}) for i = 1:length(fields) assert_nested_slotwrapper(fields[i]) @@ -48,17 +73,27 @@ struct Conditional slot::Int thentype elsetype - function Conditional(slot::Int, @nospecialize(thentype), @nospecialize(elsetype)) + # `isdefined` indicates this `Conditional` is from `@isdefined slot`, implying that + # the `undef` information of `slot` can be improved in the then branch. + # Since this is only beneficial for local inference, it is not translated into `InterConditional`. + isdefined::Bool + function Conditional(slot::Int, @nospecialize(thentype), @nospecialize(elsetype); + isdefined::Bool=false) assert_nested_slotwrapper(thentype) assert_nested_slotwrapper(elsetype) - return new(slot, thentype, elsetype) + return new(slot, thentype, elsetype, isdefined) end end -Conditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetype)) = - Conditional(slot_id(var), thentype, elsetype) +Conditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetype); isdefined::Bool=false) = + Conditional(slot_id(var), thentype, elsetype; isdefined) +import Core: InterConditional """ - cnd::InterConditional + struct InterConditional + slot::Int + thentype + elsetype + end Similar to `Conditional`, but conveys inter-procedural constraints imposed on call arguments. This is separate from `Conditional` to catch logic errors: the lattice element name is `InterConditional` @@ -66,14 +101,6 @@ while processing a call, then `Conditional` everywhere else. Thus `InterConditio `CompilerTypes`β€”these type's usages are disjointβ€”though we define the lattice for `InterConditional`. """ :(InterConditional) -import Core: InterConditional -# struct InterConditional -# slot::Int -# thentype -# elsetype -# InterConditional(slot::Int, @nospecialize(thentype), @nospecialize(elsetype)) = -# new(slot, thentype, elsetype) -# end InterConditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetype)) = InterConditional(slot_id(var), thentype, elsetype) @@ -120,8 +147,6 @@ end MustAlias(var::SlotNumber, @nospecialize(vartyp), fldidx::Int, @nospecialize(fldtyp)) = MustAlias(slot_id(var), vartyp, fldidx, fldtyp) -_uniontypes(x::MustAlias, ts) = _uniontypes(widenconst(x), ts) - """ alias::InterMustAlias @@ -160,6 +185,7 @@ struct StateUpdate var::SlotNumber vtype::VarState conditional::Bool + StateUpdate(var::SlotNumber, vtype::VarState, conditional::Bool=false) = new(var, vtype, conditional) end """ @@ -285,11 +311,17 @@ end # `Conditional` and `InterConditional` are valid in opposite contexts # (i.e. local inference and inter-procedural call), as such they will never be compared -@nospecializeinfer function issubconditional(lattice::AbstractLattice, a::C, b::C) where {C<:AnyConditional} +@nospecializeinfer issubconditional(𝕃::AbstractLattice, a::Conditional, b::Conditional) = + _issubconditional(𝕃, a, b, #=check_isdefined=#true) +@nospecializeinfer issubconditional(𝕃::AbstractLattice, a::InterConditional, b::InterConditional) = + _issubconditional(𝕃, a, b, #=check_isdefined=#false) +@nospecializeinfer function _issubconditional(𝕃::AbstractLattice, a::C, b::C, check_isdefined::Bool) where C<:AnyConditional if is_same_conditionals(a, b) - if βŠ‘(lattice, a.thentype, b.thentype) - if βŠ‘(lattice, a.elsetype, b.elsetype) - return true + if βŠ‘(𝕃, a.thentype, b.thentype) + if βŠ‘(𝕃, a.elsetype, b.elsetype) + if !check_isdefined || a.isdefined β‰₯ b.isdefined + return true + end end end end @@ -447,8 +479,13 @@ end @nospecializeinfer function βŠ‘(lattice::PartialsLattice, @nospecialize(a), @nospecialize(b)) if isa(a, PartialStruct) if isa(b, PartialStruct) - if !(length(a.fields) == length(b.fields) && a.typ <: b.typ) - return false + a.typ <: b.typ || return false + if length(a.fields) β‰  length(b.fields) + if !(isvarargtype(a.fields[end]) || isvarargtype(b.fields[end])) + length(a.fields) β‰₯ length(b.fields) || return false + else + return false + end end for i in 1:length(b.fields) af = a.fields[i] @@ -471,19 +508,25 @@ end return isa(b, Type) && a.typ <: b elseif isa(b, PartialStruct) if isa(a, Const) - nf = nfields(a.val) - nf == length(b.fields) || return false widea = widenconst(a)::DataType wideb = widenconst(b) widebβ€² = unwrap_unionall(wideb)::DataType widea.name === widebβ€².name || return false - # We can skip the subtype check if b is a Tuple, since in that - # case, the βŠ‘ of the elements is sufficient. - if widebβ€².name !== Tuple.name && !(widea <: wideb) - return false + if widebβ€².name === Tuple.name + # We can skip the subtype check if b is a Tuple, since in that + # case, the βŠ‘ of the elements is sufficient. + # But for tuple comparisons, we need their lengths to be the same for now. + # TODO improve accuracy for cases when `b` contains vararg element + nfields(a.val) == length(b.fields) || return false + else + widea <: wideb || return false + # for structs we need to check that `a` has more information than `b` that may be partially initialized + n_initialized(a) β‰₯ length(b.fields) || return false end + nf = nfields(a.val) for i in 1:nf isdefined(a.val, i) || continue # since βˆ€ T Union{} βŠ‘ T + i > length(b.fields) && break # `a` has more information than `b` that is partially initialized struct bfα΅’ = b.fields[i] if i == nf bfα΅’ = unwrapva(bfα΅’) diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 318ac0b5c27e5..91a44d3b117ab 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -321,6 +321,11 @@ end # even after complicated recursion and other operations on it elsewhere const issimpleenoughtupleelem = issimpleenoughtype +function n_initialized(t::Const) + nf = nfields(t.val) + return something(findfirst(i::Int->!isdefined(t.val,i), 1:nf), nf+1)-1 +end + # A simplified type_more_complex query over the extended lattice # (assumes typeb βŠ‘ typea) @nospecializeinfer function issimplertype(𝕃::AbstractLattice, @nospecialize(typea), @nospecialize(typeb)) @@ -328,6 +333,13 @@ const issimpleenoughtupleelem = issimpleenoughtype typea === typeb && return true if typea isa PartialStruct aty = widenconst(typea) + if typeb isa Const + @assert length(typea.fields) ≀ n_initialized(typeb) "typeb βŠ‘ typea is assumed" + elseif typeb isa PartialStruct + @assert length(typea.fields) ≀ length(typeb.fields) "typeb βŠ‘ typea is assumed" + else + return false + end for i = 1:length(typea.fields) ai = unwrapva(typea.fields[i]) bi = fieldtype(aty, i) @@ -572,34 +584,38 @@ end # N.B. This can also be called with both typea::Const and typeb::Const to # to recover PartialStruct from `Const`s with overlapping fields. -@nospecializeinfer function tmerge_partial_struct(lattice::PartialsLattice, @nospecialize(typea), @nospecialize(typeb)) +@nospecializeinfer function tmerge_partial_struct(𝕃::PartialsLattice, @nospecialize(typea), @nospecialize(typeb)) aty = widenconst(typea) bty = widenconst(typeb) if aty === bty - # must have egal here, since we do not create PartialStruct for non-concrete types - typea_nfields = nfields_tfunc(lattice, typea) - typeb_nfields = nfields_tfunc(lattice, typeb) - isa(typea_nfields, Const) || return nothing - isa(typeb_nfields, Const) || return nothing - type_nfields = typea_nfields.val::Int - type_nfields === typeb_nfields.val::Int || return nothing - type_nfields == 0 && return nothing - fields = Vector{Any}(undef, type_nfields) - anyrefine = false - for i = 1:type_nfields - ai = getfield_tfunc(lattice, typea, Const(i)) - bi = getfield_tfunc(lattice, typeb, Const(i)) + if typea isa PartialStruct + if typeb isa PartialStruct + nflds = min(length(typea.fields), length(typeb.fields)) + else + nflds = min(length(typea.fields), n_initialized(typeb::Const)) + end + elseif typeb isa PartialStruct + nflds = min(n_initialized(typea::Const), length(typeb.fields)) + else + nflds = min(n_initialized(typea::Const), n_initialized(typeb::Const)) + end + nflds == 0 && return nothing + fields = Vector{Any}(undef, nflds) + anyrefine = nflds > datatype_min_ninitialized(unwrap_unionall(aty)) + for i = 1:nflds + ai = getfield_tfunc(𝕃, typea, Const(i)) + bi = getfield_tfunc(𝕃, typeb, Const(i)) # N.B.: We're assuming here that !isType(aty), because that case # only arises when typea === typeb, which should have been caught # before calling this. ft = fieldtype(aty, i) - if is_lattice_equal(lattice, ai, bi) || is_lattice_equal(lattice, ai, ft) + if is_lattice_equal(𝕃, ai, bi) || is_lattice_equal(𝕃, ai, ft) # Since ai===bi, the given type has no restrictions on complexity. # and can be used to refine ft tyi = ai - elseif is_lattice_equal(lattice, bi, ft) + elseif is_lattice_equal(𝕃, bi, ft) tyi = bi - elseif (tyiβ€² = tmerge_field(lattice, ai, bi); tyiβ€² !== nothing) + elseif (tyiβ€² = tmerge_field(𝕃, ai, bi); tyiβ€² !== nothing) # allow external lattice implementation to provide a custom field-merge strategy tyi = tyiβ€² else @@ -621,8 +637,8 @@ end end fields[i] = tyi if !anyrefine - anyrefine = has_nontrivial_extended_info(lattice, tyi) || # extended information - β‹€(lattice, tyi, ft) # just a type-level information, but more precise than the declared type + anyrefine = has_nontrivial_extended_info(𝕃, tyi) || # extended information + β‹€(𝕃, tyi, ft) # just a type-level information, but more precise than the declared type end end anyrefine && return PartialStruct(aty, fields) diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index a4499e003cf2c..577452a873b5e 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -18,7 +18,7 @@ function hasuniquerep(@nospecialize t) iskindtype(typeof(t)) || return true # non-types are always compared by egal in the type system isconcretetype(t) && return true # these are also interned and pointer comparable if isa(t, DataType) && t.name !== Tuple.name && !isvarargtype(t) # invariant DataTypes - return _all(hasuniquerep, t.parameters) + return all(hasuniquerep, t.parameters) end return false end diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl index a9f2f1eebe1b5..78db5ef5e4ed8 100644 --- a/base/compiler/validation.jl +++ b/base/compiler/validation.jl @@ -257,7 +257,9 @@ end function is_valid_rvalue(@nospecialize(x)) is_valid_argument(x) && return true - if isa(x, Expr) && x.head in (:new, :splatnew, :the_exception, :isdefined, :call, :invoke, :invoke_modify, :foreigncall, :cfunction, :gc_preserve_begin, :copyast, :new_opaque_closure) + if isa(x, Expr) && x.head in (:new, :splatnew, :the_exception, :isdefined, :call, + :invoke, :invoke_modify, :foreigncall, :cfunction, :gc_preserve_begin, :copyast, + :new_opaque_closure) return true end return false diff --git a/base/condition.jl b/base/condition.jl index 52781f348eb0d..bc14b17b3ac6b 100644 --- a/base/condition.jl +++ b/base/condition.jl @@ -138,7 +138,7 @@ function wait(c::GenericCondition; first::Bool=false) try return wait() catch - ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) + q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) rethrow() finally relockall(c.lock, token) diff --git a/base/file.jl b/base/file.jl index 3987029d5f74f..81bca9dd65577 100644 --- a/base/file.jl +++ b/base/file.jl @@ -1093,24 +1093,30 @@ end walkdir(dir; topdown=true, follow_symlinks=false, onerror=throw) Return an iterator that walks the directory tree of a directory. -The iterator returns a tuple containing `(rootpath, dirs, files)`. + +The iterator returns a tuple containing `(path, dirs, files)`. +Each iteration `path` will change to the next directory in the tree; +then `dirs` and `files` will be vectors containing the directories and files +in the current `path` directory. The directory tree can be traversed top-down or bottom-up. If `walkdir` or `stat` encounters a `IOError` it will rethrow the error by default. A custom error handling function can be provided through `onerror` keyword argument. `onerror` is called with a `IOError` as argument. +The returned iterator is stateful so when accessed repeatedly each access will +resume where the last left off, like [`Iterators.Stateful`](@ref). See also: [`readdir`](@ref). # Examples ```julia -for (root, dirs, files) in walkdir(".") - println("Directories in \$root") +for (path, dirs, files) in walkdir(".") + println("Directories in \$path") for dir in dirs - println(joinpath(root, dir)) # path to directories + println(joinpath(path, dir)) # path to directories end - println("Files in \$root") + println("Files in \$path") for file in files - println(joinpath(root, file)) # path to files + println(joinpath(path, file)) # path to files end end ``` @@ -1120,18 +1126,18 @@ julia> mkpath("my/test/dir"); julia> itr = walkdir("my"); -julia> (root, dirs, files) = first(itr) +julia> (path, dirs, files) = first(itr) ("my", ["test"], String[]) -julia> (root, dirs, files) = first(itr) +julia> (path, dirs, files) = first(itr) ("my/test", ["dir"], String[]) -julia> (root, dirs, files) = first(itr) +julia> (path, dirs, files) = first(itr) ("my/test/dir", String[], String[]) ``` """ -function walkdir(root; topdown=true, follow_symlinks=false, onerror=throw) - function _walkdir(chnl, root) +function walkdir(path; topdown=true, follow_symlinks=false, onerror=throw) + function _walkdir(chnl, path) tryf(f, p) = try f(p) catch err @@ -1143,7 +1149,7 @@ function walkdir(root; topdown=true, follow_symlinks=false, onerror=throw) end return end - entries = tryf(_readdirx, root) + entries = tryf(_readdirx, path) entries === nothing && return dirs = Vector{String}() files = Vector{String}() @@ -1157,17 +1163,17 @@ function walkdir(root; topdown=true, follow_symlinks=false, onerror=throw) end if topdown - push!(chnl, (root, dirs, files)) + push!(chnl, (path, dirs, files)) end for dir in dirs - _walkdir(chnl, joinpath(root, dir)) + _walkdir(chnl, joinpath(path, dir)) end if !topdown - push!(chnl, (root, dirs, files)) + push!(chnl, (path, dirs, files)) end nothing end - return Channel{Tuple{String,Vector{String},Vector{String}}}(chnl -> _walkdir(chnl, root)) + return Channel{Tuple{String,Vector{String},Vector{String}}}(chnl -> _walkdir(chnl, path)) end function unlink(p::AbstractString) diff --git a/base/initdefs.jl b/base/initdefs.jl index aa2ea67528da9..707c96a2444d6 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -438,6 +438,11 @@ function atexit(f::Function) end function _atexit(exitcode::Cint) + # this current task shouldn't be scheduled anywhere, but if it was (because + # this exit came from a signal for example), then try to clear that state + # to minimize scheduler issues later + ct = current_task() + q = ct.queue; q === nothing || list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) # Don't hold the lock around the iteration, just in case any other thread executing in # parallel tries to register a new atexit hook while this is running. We don't want to # block that thread from proceeding, and we can allow it to register its hook which we diff --git a/base/intfuncs.jl b/base/intfuncs.jl index f72ac6ee08d4d..8d46fcffa3ad5 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -263,14 +263,16 @@ end invmod(n::T) where {T <: Base.BitInteger} Compute the modular inverse of `n` in the integer ring of type `T`, i.e. modulo -`2^N` where `N = 8*sizeof(T)` (e.g. `N = 32` for `Int32`). In other words these +`2^N` where `N = 8*sizeof(T)` (e.g. `N = 32` for `Int32`). In other words, these methods satisfy the following identities: ``` n * invmod(n) == 1 (n * invmod(n, T)) % T == 1 (n % T) * invmod(n, T) == 1 ``` -Note that `*` here is modular multiplication in the integer ring, `T`. +Note that `*` here is modular multiplication in the integer ring, `T`. This will +throw an error if `n` is even, because then it is not relatively prime with `2^N` +and thus has no such inverse. Specifying the modulus implied by an integer type as an explicit value is often inconvenient since the modulus is by definition too big to be represented by the diff --git a/base/logging/logging.jl b/base/logging/logging.jl index c50f581db89ba..5cf3882a300ec 100644 --- a/base/logging/logging.jl +++ b/base/logging/logging.jl @@ -3,7 +3,7 @@ module CoreLogging import Base: isless, +, -, convert, show -import Base: ScopedValue, with, @with +import Base.ScopedValues: ScopedValue, with, @with export AbstractLogger, diff --git a/base/mpfr.jl b/base/mpfr.jl index ed3ea5937ce87..d393469aa26a1 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -109,9 +109,9 @@ end tie_breaker_is_to_even(::MPFRRoundingMode) = true const ROUNDING_MODE = Ref{MPFRRoundingMode}(MPFRRoundNearest) -const CURRENT_ROUNDING_MODE = Base.ScopedValue{MPFRRoundingMode}() +const CURRENT_ROUNDING_MODE = Base.ScopedValues.ScopedValue{MPFRRoundingMode}() const DEFAULT_PRECISION = Ref{Clong}(256) -const CURRENT_PRECISION = Base.ScopedValue{Clong}() +const CURRENT_PRECISION = Base.ScopedValues.ScopedValue{Clong}() # Basic type and initialization definitions # Warning: the constants are MPFR implementation details from @@ -162,7 +162,7 @@ significand_limb_count(x::BigFloat) = div(sizeof(x._d), sizeof(Limb), RoundToZer rounding_raw(::Type{BigFloat}) = something(Base.ScopedValues.get(CURRENT_ROUNDING_MODE), ROUNDING_MODE[]) setrounding_raw(::Type{BigFloat}, r::MPFRRoundingMode) = ROUNDING_MODE[]=r function setrounding_raw(f::Function, ::Type{BigFloat}, r::MPFRRoundingMode) - Base.@with(CURRENT_ROUNDING_MODE => r, f()) + Base.ScopedValues.@with(CURRENT_ROUNDING_MODE => r, f()) end @@ -1109,7 +1109,7 @@ Note: `nextfloat()`, `prevfloat()` do not use the precision mentioned by The `base` keyword requires at least Julia 1.8. """ function setprecision(f::Function, ::Type{BigFloat}, prec::Integer; base::Integer=2) - Base.@with(CURRENT_PRECISION => _convert_precision_from_base(prec, base), f()) + Base.ScopedValues.@with(CURRENT_PRECISION => _convert_precision_from_base(prec, base), f()) end setprecision(f::Function, prec::Integer; base::Integer=2) = setprecision(f, BigFloat, prec; base) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 5e32a19c2cafb..99f41f2404e47 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -730,6 +730,8 @@ end end @inline checkindex(::Type{Bool}, inds::Tuple, I::CartesianIndex) = checkbounds_indices(Bool, inds, I.I) +@inline checkindex(::Type{Bool}, inds::Tuple, i::AbstractRange{<:CartesianIndex}) = + isempty(i) | (checkindex(Bool, inds, first(i)) & checkindex(Bool, inds, last(i))) # Indexing into Array with mixtures of Integers and CartesianIndices is # extremely performance-sensitive. While the abstract fallbacks support this, diff --git a/base/operators.jl b/base/operators.jl index 2c8070b44d704..d01902e302359 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -1339,8 +1339,7 @@ struct Splat{F} <: Function Splat(f) = new{Core.Typeof(f)}(f) end (s::Splat)(args) = s.f(args...) -print(io::IO, s::Splat) = print(io, "splat(", s.f, ')') -show(io::IO, s::Splat) = print(io, s) +show(io::IO, s::Splat) = (print(io, "splat("); show(io, s.f); print(io, ")")) ## in and related operators diff --git a/base/precompilation.jl b/base/precompilation.jl index aa70718eab9bc..6997ce12c8d01 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -304,9 +304,10 @@ function show_progress(io::IO, p::MiniProgressBar; termwidth=nothing, carriagere to_print = sprint(; context=io) do io print(io, " "^p.indent) printstyled(io, p.header, color=p.color, bold=true) - print(io, " [") - print(io, "="^n_filled, ">") - print(io, " "^n_left, "] ", ) + print(io, " ") + printstyled(io, "━"^n_filled; color=p.color) + printstyled(io, perc >= 95 ? "━" : "β•Έ"; color=p.color) + printstyled(io, "━"^n_left, " "; color=:light_black) print(io, progress_text) carriagereturn && print(io, "\r") end diff --git a/base/reducedim.jl b/base/reducedim.jl index e74fe2b765277..0478afe1a46b6 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -258,8 +258,9 @@ function _mapreducedim!(f, op, R::AbstractArray, A::AbstractArrayOrBroadcasted) # use mapreduce_impl, which is probably better tuned to achieve higher performance nslices = div(length(A), lsiz) ibase = first(LinearIndices(A))-1 - for i = 1:nslices - @inbounds R[i] = op(R[i], mapreduce_impl(f, op, A, ibase+1, ibase+lsiz)) + for i in eachindex(R) + r = op(@inbounds(R[i]), mapreduce_impl(f, op, A, ibase+1, ibase+lsiz)) + @inbounds R[i] = r ibase += lsiz end return R diff --git a/base/reflection.jl b/base/reflection.jl index 6dfaf34bc0047..4b491ca9f6bd4 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1199,11 +1199,17 @@ hasgenerator(m::Core.MethodInstance) = hasgenerator(m.def::Method) # low-level method lookup functions used by the compiler -unionlen(x::Union) = unionlen(x.a) + unionlen(x.b) -unionlen(@nospecialize(x)) = 1 +unionlen(@nospecialize(x)) = x isa Union ? unionlen(x.a) + unionlen(x.b) : 1 -_uniontypes(x::Union, ts) = (_uniontypes(x.a,ts); _uniontypes(x.b,ts); ts) -_uniontypes(@nospecialize(x), ts) = (push!(ts, x); ts) +function _uniontypes(@nospecialize(x), ts::Array{Any,1}) + if x isa Union + _uniontypes(x.a, ts) + _uniontypes(x.b, ts) + else + push!(ts, x) + end + return ts +end uniontypes(@nospecialize(x)) = _uniontypes(x, Any[]) function _methods(@nospecialize(f), @nospecialize(t), lim::Int, world::UInt) diff --git a/base/show.jl b/base/show.jl index fa66a198aef4d..0a2976e7ebe42 100644 --- a/base/show.jl +++ b/base/show.jl @@ -514,24 +514,16 @@ function _show_default(io::IO, @nospecialize(x)) end function active_module() - REPL = REPL_MODULE_REF[] - REPL === Base && return Main - return invokelatest(REPL.active_module)::Module + if ccall(:jl_is_in_pure_context, Bool, ()) + error("active_module() should not be called from a pure context") + end + if !@isdefined(active_repl) || active_repl === nothing + return Main + end + return invokelatest(active_module, active_repl)::Module end -# Check if a particular symbol is exported from a standard library module -function is_exported_from_stdlib(name::Symbol, mod::Module) - !isdefined(mod, name) && return false - orig = getfield(mod, name) - while !(mod === Base || mod === Core) - activemod = active_module() - parent = parentmodule(mod) - if mod === activemod || mod === parent || parent === activemod - return false - end - mod = parent - end - return isexported(mod, name) && isdefined(mod, name) && !isdeprecated(mod, name) && getfield(mod, name) === orig +module UsesCoreAndBaseOnly end function show_function(io::IO, f::Function, compact::Bool, fallback::Function) @@ -544,13 +536,13 @@ function show_function(io::IO, f::Function, compact::Bool, fallback::Function) print(io, mt.name) elseif isdefined(mt, :module) && isdefined(mt.module, mt.name) && getfield(mt.module, mt.name) === f - mod = active_module() - if is_exported_from_stdlib(mt.name, mt.module) || mt.module === mod - show_sym(io, mt.name) - else + # this used to call the removed internal function `is_exported_from_stdlib`, which effectively + # just checked for exports from Core and Base. + mod = get(io, :module, UsesCoreAndBaseOnly) + if !(isvisible(mt.name, mt.module, mod) || mt.module === mod) print(io, mt.module, ".") - show_sym(io, mt.name) end + show_sym(io, mt.name) else fallback(io, f) end @@ -737,9 +729,9 @@ end function show_typealias(io::IO, name::GlobalRef, x::Type, env::SimpleVector, wheres::Vector) if !(get(io, :compact, false)::Bool) # Print module prefix unless alias is visible from module passed to - # IOContext. If :module is not set, default to Main (or current active module). + # IOContext. If :module is not set, default to Main. # nothing can be used to force printing prefix. - from = get(io, :module, active_module()) + from = get(io, :module, Main) if (from === nothing || !isvisible(name.name, name.mod, from)) show(io, name.mod) print(io, ".") @@ -1053,9 +1045,9 @@ function show_type_name(io::IO, tn::Core.TypeName) quo = false if !(get(io, :compact, false)::Bool) # Print module prefix unless type is visible from module passed to - # IOContext If :module is not set, default to Main (or current active module). + # IOContext If :module is not set, default to Main. # nothing can be used to force printing prefix - from = get(io, :module, active_module()) + from = get(io, :module, Main) if isdefined(tn, :module) && (from === nothing || !isvisible(sym, tn.module, from::Module)) show(io, tn.module) print(io, ".") @@ -2535,7 +2527,7 @@ function show_signature_function(io::IO, @nospecialize(ft), demangle=false, farg uw = unwrap_unionall(ft) if ft <: Function && isa(uw, DataType) && isempty(uw.parameters) && _isself(uw) uwmod = parentmodule(uw) - if qualified && !is_exported_from_stdlib(uw.name.mt.name, uwmod) && uwmod !== Main + if qualified && !isexported(uwmod, uw.name.mt.name) && uwmod !== Main print_within_stacktrace(io, uwmod, '.', bold=true) end s = sprint(show_sym, (demangle ? demangle_function_name : identity)(uw.name.mt.name), context=io) diff --git a/base/stat.jl b/base/stat.jl index 3330ff0c35bc8..506b5644dccbc 100644 --- a/base/stat.jl +++ b/base/stat.jl @@ -198,6 +198,7 @@ end """ stat(file) + stat(joinpath...) Return a structure whose fields contain information about the file. The fields of the structure are: @@ -218,16 +219,19 @@ The fields of the structure are: | mtime | `Float64` | Unix timestamp of when the file was last modified | | ctime | `Float64` | Unix timestamp of when the file's metadata was changed | """ +stat(path) = (path2 = joinpath(path); path2 isa typeof(path) ? error("stat not implemented for $(typeof(path))") : stat(path2)) stat(path...) = stat(joinpath(path...)) """ lstat(file) + lstat(joinpath...) Like [`stat`](@ref), but for symbolic links gets the info for the link itself rather than the file it refers to. This function must be called on a file path rather than a file object or a file descriptor. """ +lstat(path) = (path2 = joinpath(path); path2 isa typeof(path) ? error("lstat not implemented for $(typeof(path))") : lstat(path2)) lstat(path...) = lstat(joinpath(path...)) # some convenience functions diff --git a/base/stream.jl b/base/stream.jl index a45307b883da8..93aeead79eb9c 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -462,7 +462,7 @@ function closewrite(s::LibuvStream) # try-finally unwinds the sigatomic level, so need to repeat sigatomic_end sigatomic_end() iolock_begin() - ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) + q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) if uv_req_data(req) != C_NULL # req is still alive, # so make sure we won't get spurious notifications later @@ -1076,7 +1076,7 @@ function uv_write(s::LibuvStream, p::Ptr{UInt8}, n::UInt) # try-finally unwinds the sigatomic level, so need to repeat sigatomic_end sigatomic_end() iolock_begin() - ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) + q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) if uv_req_data(uvw) != C_NULL # uvw is still alive, # so make sure we won't get spurious notifications later diff --git a/base/strings/annotated.jl b/base/strings/annotated.jl index f077c577237d0..be4c6887d4a6d 100644 --- a/base/strings/annotated.jl +++ b/base/strings/annotated.jl @@ -384,7 +384,7 @@ a vector of region–annotation tuples. In accordance with the semantics documented in [`AnnotatedString`](@ref), the order of annotations returned matches the order in which they were applied. -See also: `annotate!`. +See also: [`annotate!`](@ref). """ annotations(s::AnnotatedString) = s.annotations diff --git a/base/strings/string.jl b/base/strings/string.jl index 89e2ff288c3d7..f5abbead34bd1 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -102,9 +102,11 @@ function unsafe_string(p::Union{Ptr{UInt8},Ptr{Int8}}) ccall(:jl_cstr_to_string, Ref{String}, (Ptr{UInt8},), p) end -# This is @assume_effects :effect_free :nothrow :terminates_globally @ccall jl_alloc_string(n::Csize_t)::Ref{String}, +# This is `@assume_effects :total !:consistent @ccall jl_alloc_string(n::Csize_t)::Ref{String}`, # but the macro is not available at this time in bootstrap, so we write it manually. -@eval _string_n(n::Integer) = $(Expr(:foreigncall, QuoteNode(:jl_alloc_string), Ref{String}, Expr(:call, Expr(:core, :svec), :Csize_t), 1, QuoteNode((:ccall,0x000e)), :(convert(Csize_t, n)))) +const _string_n_override = 0x04ee +@eval _string_n(n::Integer) = $(Expr(:foreigncall, QuoteNode(:jl_alloc_string), Ref{String}, + :(Core.svec(Csize_t)), 1, QuoteNode((:ccall, _string_n_override)), :(convert(Csize_t, n)))) """ String(s::AbstractString) diff --git a/base/strings/unicode.jl b/base/strings/unicode.jl index 3c6710025077c..ad047514c85a6 100644 --- a/base/strings/unicode.jl +++ b/base/strings/unicode.jl @@ -6,7 +6,7 @@ module Unicode import Base: show, ==, hash, string, Symbol, isless, length, eltype, convert, isvalid, ismalformed, isoverlong, iterate, AnnotatedString, AnnotatedChar, annotated_chartransform, - @assume_effects + @assume_effects, annotations # whether codepoints are valid Unicode scalar values, i.e. 0-0xd7ff, 0xe000-0x10ffff diff --git a/base/task.jl b/base/task.jl index ae99a71585c85..6cb1ff785eeee 100644 --- a/base/task.jl +++ b/base/task.jl @@ -156,20 +156,10 @@ const task_state_runnable = UInt8(0) const task_state_done = UInt8(1) const task_state_failed = UInt8(2) -const _state_index = findfirst(==(:_state), fieldnames(Task)) -@eval function load_state_acquire(t) - # TODO: Replace this by proper atomic operations when available - @GC.preserve t llvmcall($(""" - %rv = load atomic i8, i8* %0 acquire, align 8 - ret i8 %rv - """), UInt8, Tuple{Ptr{UInt8}}, - Ptr{UInt8}(pointer_from_objref(t) + fieldoffset(Task, _state_index))) -end - @inline function getproperty(t::Task, field::Symbol) if field === :state # TODO: this field name should be deprecated in 2.0 - st = load_state_acquire(t) + st = @atomic :acquire t._state if st === task_state_runnable return :runnable elseif st === task_state_done @@ -223,7 +213,7 @@ julia> istaskdone(b) true ``` """ -istaskdone(t::Task) = load_state_acquire(t) !== task_state_runnable +istaskdone(t::Task) = (@atomic :acquire t._state) !== task_state_runnable """ istaskstarted(t::Task) -> Bool @@ -267,7 +257,7 @@ true !!! compat "Julia 1.3" This function requires at least Julia 1.3. """ -istaskfailed(t::Task) = (load_state_acquire(t) === task_state_failed) +istaskfailed(t::Task) = ((@atomic :acquire t._state) === task_state_failed) Threads.threadid(t::Task) = Int(ccall(:jl_get_task_tid, Int16, (Any,), t)+1) function Threads.threadpool(t::Task) @@ -320,6 +310,7 @@ end # just wait for a task to be done, no error propagation function _wait(t::Task) + t === current_task() && Core.throw(ConcurrencyViolationError("deadlock detected: cannot wait on current task")) if !istaskdone(t) donenotify = t.donenotify::ThreadSynchronizer lock(donenotify) @@ -374,7 +365,6 @@ in an error, thrown as a [`TaskFailedException`](@ref) which wraps the failed ta Throws a `ConcurrencyViolationError` if `t` is the currently running task, to prevent deadlocks. """ function wait(t::Task; throw=true) - t === current_task() && Core.throw(ConcurrencyViolationError("deadlock detected: cannot wait on current task")) _wait(t) if throw && istaskfailed(t) Core.throw(TaskFailedException(t)) @@ -813,12 +803,15 @@ macro sync_add(expr) end end -throwto_repl_task(@nospecialize val) = throwto(getfield(active_repl_backend, :backend_task)::Task, val) - -function is_repl_running() - return isdefined(Base, :active_repl_backend) && - (getfield(active_repl_backend, :backend_task)::Task)._state === task_state_runnable && - getfield(active_repl_backend, :in_eval) +function repl_backend_task() + @isdefined(active_repl_backend) || return + backend = active_repl_backend + isdefined(backend, :backend_task) || return + backend_task = getfield(active_repl_backend, :backend_task)::Task + if backend_task._state === task_state_runnable && getfield(backend, :in_eval) + return backend_task + end + return end # runtime system hook called when a task finishes @@ -842,8 +835,9 @@ function task_done_hook(t::Task) end if err && !handled && Threads.threadid() == 1 - if isa(result, InterruptException) && isempty(Workqueue) && is_repl_running() - throwto_repl_task(result) + if isa(result, InterruptException) && isempty(Workqueue) + backend = repl_backend_task() + backend isa Task && throwto(backend, result) end end # Clear sigatomic before waiting @@ -854,11 +848,11 @@ function task_done_hook(t::Task) # If an InterruptException happens while blocked in the event loop, try handing # the exception to the REPL task since the current task is done. # issue #19467 - if Threads.threadid() == 1 && isa(e, InterruptException) && isempty(Workqueue) && is_repl_running() - throwto_repl_task(e) - else - rethrow() + if Threads.threadid() == 1 && isa(e, InterruptException) && isempty(Workqueue) + backend = repl_backend_task() + backend isa Task && throwto(backend, e) end + rethrow() # this will terminate the program end end @@ -1032,7 +1026,7 @@ function schedule(t::Task, @nospecialize(arg); error=false) # schedule a task to be (re)started with the given value or exception t._state === task_state_runnable || Base.error("schedule: Task not runnable") if error - t.queue === nothing || Base.list_deletefirst!(t.queue::IntrusiveLinkedList{Task}, t) + q = t.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, t) setfield!(t, :result, arg) setfield!(t, :_isexception, true) else @@ -1056,7 +1050,7 @@ function yield() try wait() catch - ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) + q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) rethrow() end end diff --git a/cli/Makefile b/cli/Makefile index bbe722f6f4816..7b8d3587f5386 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -152,7 +152,7 @@ $(build_bindir)/julia$(EXE): $(EXE_OBJS) $(build_shlibdir)/libjulia.$(SHLIB_EXT) $(build_bindir)/julia-debug$(EXE): $(EXE_DOBJS) $(build_shlibdir)/libjulia-debug.$(SHLIB_EXT) | $(build_bindir) @$(call PRINT_LINK, $(CC) $(LOADER_CFLAGS) $(DEBUGFLAGS) $(EXE_DOBJS) -o $@ $(LOADER_LDFLAGS) $(RPATH) -ljulia-debug) -$(BUILDDIR)/julia.expmap: $(SRCDIR)/julia.expmap.in +$(BUILDDIR)/julia.expmap: $(SRCDIR)/julia.expmap.in $(JULIAHOME)/VERSION sed <'$<' >'$@' -e 's/@JULIA_SHLIB_SYMBOL_VERSION@/JL_LIBJULIA_$(SOMAJOR)/' clean: | $(CLEAN_TARGETS) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 65a5e7621a714..af2a36cfce8ab 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -546,7 +546,7 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { (*jl_codegen_exported_func_addrs[symbol_idx]) = addr; } // Next, if we're on Linux/FreeBSD, set up fast TLS. -#if !defined(_OS_WINDOWS_) && !defined(_OS_DARWIN_) && !defined(_OS_OPENBSD_) +#if !defined(_OS_WINDOWS_) && !defined(_OS_OPENBSD_) void (*jl_pgcstack_setkey)(void*, void*(*)(void)) = lookup_symbol(libjulia_internal, "jl_pgcstack_setkey"); if (jl_pgcstack_setkey == NULL) { jl_loader_print_stderr("ERROR: Cannot find jl_pgcstack_setkey() function within libjulia-internal!\n"); diff --git a/contrib/pgo-lto-bolt/Makefile b/contrib/pgo-lto-bolt/Makefile index fa88cdcd3d6a7..2114b14991184 100644 --- a/contrib/pgo-lto-bolt/Makefile +++ b/contrib/pgo-lto-bolt/Makefile @@ -83,7 +83,7 @@ TOOLCHAIN_FLAGS = $\ "RANLIB=$(STAGE0_TOOLS)llvm-ranlib" $\ "CFLAGS+=$(PGO_CFLAGS)" $\ "CXXFLAGS+=$(PGO_CXXFLAGS)" $\ - "LDFLAGS+=$(PGO_LDFLAGS)" + "LDFLAGS+=-fuse-ld=lld $(PGO_LDFLAGS)" $(STAGE0_BUILD) $(STAGE1_BUILD) $(STAGE2_BUILD): $(MAKE) -C $(JULIA_ROOT) O=$@ configure @@ -99,7 +99,7 @@ stage0: | $(STAGE0_BUILD) $(STAGE1_BUILD): stage0 stage1: PGO_CFLAGS:=-fprofile-generate=$(PGO_PROFILE_DIR) -Xclang -mllvm -Xclang -vp-counters-per-site=$(COUNTERS_PER_SITE) stage1: PGO_CXXFLAGS:=-fprofile-generate=$(PGO_PROFILE_DIR) -Xclang -mllvm -Xclang -vp-counters-per-site=$(COUNTERS_PER_SITE) -stage1: PGO_LDFLAGS:=-fuse-ld=lld -flto=thin -fprofile-generate=$(PGO_PROFILE_DIR) +stage1: PGO_LDFLAGS:=-flto=thin -fprofile-generate=$(PGO_PROFILE_DIR) stage1: export USE_BINARYBUILDER_LLVM=0 stage1: | $(STAGE1_BUILD) $(MAKE) -C $(STAGE1_BUILD) $(TOOLCHAIN_FLAGS) && touch $@ @@ -107,7 +107,7 @@ stage1: | $(STAGE1_BUILD) stage2: PGO_CFLAGS:=-fprofile-use=$(PGO_PROFILE_FILE) stage2: PGO_CXXFLAGS:=-fprofile-use=$(PGO_PROFILE_FILE) -stage2: PGO_LDFLAGS:=-fuse-ld=lld -flto=thin -fprofile-use=$(PGO_PROFILE_FILE) -Wl,--icf=safe +stage2: PGO_LDFLAGS:=-flto=thin -fprofile-use=$(PGO_PROFILE_FILE) -Wl,--icf=safe stage2: export USE_BINARYBUILDER_LLVM=0 stage2: $(PGO_PROFILE_FILE) | $(STAGE2_BUILD) $(MAKE) -C $(STAGE2_BUILD) $(TOOLCHAIN_FLAGS) $(BOLT_FLAGS) julia-src-release julia-symlink julia-libccalltest \ @@ -135,10 +135,13 @@ bolt_instrument: copy_originals # We don't want to rebuild julia-src as then we lose the bolt instrumentation # So we have to manually build the sysimage and package image +finish_stage2: PGO_CFLAGS:=-fprofile-use=$(PGO_PROFILE_FILE) +finish_stage2: PGO_CXXFLAGS:=-fprofile-use=$(PGO_PROFILE_FILE) +finish_stage2: PGO_LDFLAGS:=-flto=thin -fprofile-use=$(PGO_PROFILE_FILE) -Wl,--icf=safe finish_stage2: stage2 - $(MAKE) -C $(STAGE2_BUILD) julia-base-cache && \ - $(MAKE) -C $(STAGE2_BUILD) -f sysimage.mk sysimg-release && \ - $(MAKE) -C $(STAGE2_BUILD) -f pkgimage.mk release + $(MAKE) -C $(STAGE2_BUILD) $(TOOLCHAIN_FLAGS) julia-base-cache && \ + $(MAKE) -C $(STAGE2_BUILD) $(TOOLCHAIN_FLAGS) -f sysimage.mk sysimg-release && \ + $(MAKE) -C $(STAGE2_BUILD) $(TOOLCHAIN_FLAGS) -f pkgimage.mk release merge_data: bolt_instrument for file in $(FILES_TO_OPTIMIZE); do \ diff --git a/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/md5 b/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/md5 new file mode 100644 index 0000000000000..f42bbedb6d415 --- /dev/null +++ b/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/md5 @@ -0,0 +1 @@ +70878dd96911d6960537dfee2a820d98 diff --git a/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/sha512 b/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/sha512 new file mode 100644 index 0000000000000..83164cad9a89d --- /dev/null +++ b/deps/checksums/Downloads-1061ecc377a053fce0df94e1a19e5260f7c030f5.tar.gz/sha512 @@ -0,0 +1 @@ +87d2bdc6c85cbbce5302aab8ffe92fc542c9c71a396844fcc04c0416be059b00298b4816ab5e5491dbf865660a3a6152f1c245875a1ec75fb49b4c7ba0d303d8 diff --git a/deps/checksums/Downloads-a9d274ff6588cc5dbfa90e908ee34c2408bab84a.tar.gz/md5 b/deps/checksums/Downloads-a9d274ff6588cc5dbfa90e908ee34c2408bab84a.tar.gz/md5 deleted file mode 100644 index fc3bce951cafb..0000000000000 --- a/deps/checksums/Downloads-a9d274ff6588cc5dbfa90e908ee34c2408bab84a.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -97bb33510fadec7f4cc4c718c739e9a0 diff --git a/deps/checksums/Downloads-a9d274ff6588cc5dbfa90e908ee34c2408bab84a.tar.gz/sha512 b/deps/checksums/Downloads-a9d274ff6588cc5dbfa90e908ee34c2408bab84a.tar.gz/sha512 deleted file mode 100644 index bf2821e8252b0..0000000000000 --- a/deps/checksums/Downloads-a9d274ff6588cc5dbfa90e908ee34c2408bab84a.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -a362aaf762f42deebb8632a7a7980cd22b2777e8c4dc629e418580269e24a64217ad846d61acad70438cfdc190e47ba2ff7716edd4e04d8d10c1d765efce604d diff --git a/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/md5 b/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/md5 new file mode 100644 index 0000000000000..2d5f5888e777f --- /dev/null +++ b/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/md5 @@ -0,0 +1 @@ +d992a5c629199747d68baa1593a7c37d diff --git a/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/sha512 b/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/sha512 new file mode 100644 index 0000000000000..4201ee05347a7 --- /dev/null +++ b/deps/checksums/Pkg-43e7849ce37545493d0da3226cd7449f5f88563e.tar.gz/sha512 @@ -0,0 +1 @@ +27ea738dbc4db8e4a02b00fbbdc4e2047906fe0561dd4c7f9e5ce5ea9b0b7b8ef9e29234f8e435deaa6cb3e29861130b06cb0da11118c40d78f4c475ac48db3f diff --git a/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/md5 b/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/md5 deleted file mode 100644 index 218260c77ea07..0000000000000 --- a/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -832f88c404516179ece213581cd227f8 diff --git a/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/sha512 b/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/sha512 deleted file mode 100644 index fc763c8d86f40..0000000000000 --- a/deps/checksums/Pkg-7aef1f044f3483e8b07d33fb4cfe918be554de69.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -4355bb51a7f83bde489e587527e1e3a9c70799a5c0d27cd7f42b4227a5fbca2a1200a83db0317a75c582ee997bec72e9e8afafb059c395bd46e2aa015f481dca diff --git a/deps/checksums/openblas b/deps/checksums/openblas index 51317261c82a0..08bd98646c24b 100644 --- a/deps/checksums/openblas +++ b/deps/checksums/openblas @@ -1,94 +1,94 @@ -OpenBLAS.v0.3.28+0.aarch64-apple-darwin-libgfortran5.tar.gz/md5/e3edc449afa805b3744eb153460b681f -OpenBLAS.v0.3.28+0.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/4f619ae720bc2a55c6d7d53b78bf0a15f66c5b3474c968b367f41d71c759b39028817e3e7ba3cebc4ee06f2176578a5a1bd2be7cf2f1461a396c418292fcf782 -OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran3.tar.gz/md5/e01dcbdbfd2c8f15d78efb0aa5673944 -OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/33dee7c48f981b218792e150aea506989b7bbacfd540ebd1fefb150af3c33eae62cd523c329ef8f37c0b56643d480e105ed82e46ec5b3f683e006d05dda717ee -OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran4.tar.gz/md5/43662cb933b2aab820bd210edd4e994a -OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/520abb2521a4b9ae71c86dafc7de4155d51df09ff119a3b1d25a9bac3fb73aceaf38b7805888d4858b96e73c0d1cf80d8953b9db954df4d0e6c164894d07d715 -OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran5.tar.gz/md5/5c693f190d26194353c1e096b40568bc -OpenBLAS.v0.3.28+0.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/3c3e11ba038e59002d23e885e22129dda13453469dad79f39f9cddceadbf1d39e61487894f5112b2fcb5265cd98075103d99eff2a83f79407aafa545b03e9f9c -OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran3.tar.gz/md5/2892710a8545b4573014024222bb8dff -OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran3.tar.gz/sha512/6a628c9f7eab2a34198028846a6aec7bb13af4425e1b73ba5b58d326c1eb0741b5dc08fff3db565c92cbc0e2718b62fa6dedac5fa0bdb2f35561663f36f4dfbe -OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran4.tar.gz/md5/fbec5f47685d4bb36956cd4aee34f1e5 -OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran4.tar.gz/sha512/ac69a9ed17900de79c6da0ff08a97f3397860de92ce1888f77c8c8fada08fab15fff1b19868c95865ad4a387701c2ffe74e695d6949d8ba02534f91aca2a5ca3 -OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran5.tar.gz/md5/6a0a1a1cad6452ac687e24147128f027 -OpenBLAS.v0.3.28+0.aarch64-linux-musl-libgfortran5.tar.gz/sha512/0ea2b7f829b4e406276911db743706b17d7902899d4492e18b9676fed9b27d976d586e38505c52932e27f194c9806d6cb53182cb128baab41898605af7c346b5 -OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/677baf1c9142f1db12c89ef98a082d03 -OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/0a182dba6512dd50193d7625091487bb45f61ec5edbb8adffdeb68fa43744d8c9aa1233ac709249b09fed59e63b6532bf40386dfe884c26605551a6974ed0cc8 -OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/d6b08be3200bef4a045be99246a3f294 -OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/467d6d12cd56237e6128de4041dbffa3428f208e313f20975c0665abf42a3c39d6b527676573897d6b6801306a9a241da17f4231ce79f0081fb433733d3cb6b4 -OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/8645788a731c86f26f40eaf6f65bf74c -OpenBLAS.v0.3.28+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/19ea4ffdef48ef1af6bdd68ce39986814b1732d65bcaee673cd3c0dcb5572faef53962c4ac18e0d1800eb9745324b3145f98c136606ff71d96778e85d4d6bf72 -OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/a40dc134a8a5e31bea637bc0a6ee45b6 -OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/3d4a1a67753f41bde880ae0b1d19ad7998ae7646530d3e469829e7ea859a394dde73e20239b80e8c61b58974c266d0960cbe256dea4103b04dd4ec52318f02c0 -OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/0ff472d7bf455b8b3b50daa91241f288 -OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/c0b306bf1ba71baebbe191d7f105287aa19fccd61ae2bc48c9b9ffd92140d4f02d3a78e0632e83924fb02c93826455493c8f5767d71b7e505a1066bd67b95dff -OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/26a05928260315bc2088842d2fa75347 -OpenBLAS.v0.3.28+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/dd5ceb6b7fd028df3c4eac732857c537e81c6c8bb7662c6075e432acd51eb6421556b3453e37483481499b2557d34fcec22fda9192cd54b6c7c7205dd40ed387 -OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/677baf1c9142f1db12c89ef98a082d03 -OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/0a182dba6512dd50193d7625091487bb45f61ec5edbb8adffdeb68fa43744d8c9aa1233ac709249b09fed59e63b6532bf40386dfe884c26605551a6974ed0cc8 -OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/d6b08be3200bef4a045be99246a3f294 -OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/467d6d12cd56237e6128de4041dbffa3428f208e313f20975c0665abf42a3c39d6b527676573897d6b6801306a9a241da17f4231ce79f0081fb433733d3cb6b4 -OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/8645788a731c86f26f40eaf6f65bf74c -OpenBLAS.v0.3.28+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/19ea4ffdef48ef1af6bdd68ce39986814b1732d65bcaee673cd3c0dcb5572faef53962c4ac18e0d1800eb9745324b3145f98c136606ff71d96778e85d4d6bf72 -OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/a40dc134a8a5e31bea637bc0a6ee45b6 -OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/3d4a1a67753f41bde880ae0b1d19ad7998ae7646530d3e469829e7ea859a394dde73e20239b80e8c61b58974c266d0960cbe256dea4103b04dd4ec52318f02c0 -OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/0ff472d7bf455b8b3b50daa91241f288 -OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/c0b306bf1ba71baebbe191d7f105287aa19fccd61ae2bc48c9b9ffd92140d4f02d3a78e0632e83924fb02c93826455493c8f5767d71b7e505a1066bd67b95dff -OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/26a05928260315bc2088842d2fa75347 -OpenBLAS.v0.3.28+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/dd5ceb6b7fd028df3c4eac732857c537e81c6c8bb7662c6075e432acd51eb6421556b3453e37483481499b2557d34fcec22fda9192cd54b6c7c7205dd40ed387 -OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran3.tar.gz/md5/36f76f7588ad5bc48c2f68daee49da57 -OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran3.tar.gz/sha512/2184cac67657fb58afc42fff46069084ffbcbc67938e7e74e9e5a926cc83733c702cacf16ca320381f5bb1f219cbea764ae8cdb9c445f7224ac0cd0beab822ff -OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran4.tar.gz/md5/ef8501cc6babf8be3b8b649da2a7c692 -OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran4.tar.gz/sha512/98543cfd34a185644cebc33cd82ebfb663c92f1fa8349121e6d34f86b1d10f4f37688b84b22182f9e29daa74664a469ddc67408827e8bc7fddb1a7311d918532 -OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran5.tar.gz/md5/598c07efb122e75e6e99ba7fc0c4fb4b -OpenBLAS.v0.3.28+0.i686-linux-gnu-libgfortran5.tar.gz/sha512/b7caa20a36d6806895f3efb02830017c3ca8037c5af3a29df00f9fe34945324c34181a945b1dbe8a8ca43c7f792667d7640c23b5c2fa4fd93564f1da78561190 -OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran3.tar.gz/md5/e7667d215442ac0db83969d41a678774 -OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran3.tar.gz/sha512/3b22dd658b5948d6867b3e57fe53976eef59339d2714709746098b96f13766d86e918a139929aa60672be91c50c7f739c5c0db372f07a71ae2447588db3685e4 -OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran4.tar.gz/md5/91d95572ce67a21d107b9fbcd3aba11d -OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran4.tar.gz/sha512/7727d24fec0a333a943de3f9d6dd5c698e4f3b9099fd838b8b5652f6216f7b9fe4a2d8f014a4f0b3b7ad7fe05b81a9079e570454d456f0462e7d04f66e264ecb -OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran5.tar.gz/md5/2d56a5cfeae0a6afa2d2b8efa1ab22c5 -OpenBLAS.v0.3.28+0.i686-linux-musl-libgfortran5.tar.gz/sha512/e81207bee11f89669837db08b57b63813056730f68412345421539399c12a675ed01942558ebd42045959c29a2b774a75f28c4a6b14549b7373b54a5e93e8933 -OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran3.tar.gz/md5/5b741b5fec8b564ba8b24435b5d885ae -OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran3.tar.gz/sha512/97e72a4b9b39d4889c4e36eff85186fcbabfff2930185e69b3c259b43cdbaa5fab51bf0ed4024d1ddd3c780edaf501c4f5f7534e0a2edb802d580987fbd026ab -OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran4.tar.gz/md5/d5f059fc960b7dc84ee4b92c431d87b4 -OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran4.tar.gz/sha512/f1e8f31f89274ff5b466931f8941de55fb27d2ee773d87e7e0b992deeec7d921358b10520cc0f47975022536b5e9d7b1cc9acc481b95f83cc2096d7cb7494616 -OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran5.tar.gz/md5/cb99d7d4944c5283a1a0142683e1d377 -OpenBLAS.v0.3.28+0.i686-w64-mingw32-libgfortran5.tar.gz/sha512/b77d3225e60f49506917bfff78c187df7157dbc834eccda2fa03d03eef8214b225682888a411a8b6e4b29a8d7e2b0ca625ea8c56b84ecc39e1f4f1012523c096 -OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/c6e5d4867a068e08b3f56f474e498b81 -OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/de6249439758a501bfd27d3ef04ec04cc06edf64de73f0709a6a40a2eaf40bd3d5d77dfd54b7b19e2f6bf6c104b4416d3e225faa0cff4cb631785c08d90b8614 -OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/32e70466cfa3cfec65ab4cad3abc5f03 -OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/2642385a5e9fc8e9c3839a5a44f9753b21b5078725f7d0c3e1ebe96b76129a3b8e2627d92629dee4f6fd7e8e51e86a7fbedc80cbe4d1a6812cea363559950da0 -OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/e2332831bd88d57132241697952819e7 -OpenBLAS.v0.3.28+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/ad03edf9ac56bf6311f0ca70a1bc359242accfe82cba9e42f39f6cb1c3006226179ff9be8218847889cae10fac13bc33f60837e1e3249e309172da7fbc25400f -OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran3.tar.gz/md5/24c915a3156983745662ff99e5d1b040 -OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/47fb327281c903eebb0a261458fc10f09bac317d7e135dff72a112c868a2525fa542f93f22da083c13454fc241352d39a8e8463085685aa77e055ffcadf451c8 -OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran4.tar.gz/md5/93041d21ad3f95e6d9cbac6cd6730363 -OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/17cd2302860eeee884f97d87eaf0ad12cdc7361734cfaa77b543119c58103a5da107b478e7ecfcb135d2e5beffd6a3907108b2911a095a3cbc1d16f32371ac1b -OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran5.tar.gz/md5/98a8c6c8c80c11e8b6d127959c9b3414 -OpenBLAS.v0.3.28+0.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/d26a51785391d81efcaefcf647fcf0348ad68ff01845ab3547778903d2ab5c5c1cdb2a562ae5cf7f12878f3345c46321719ea82fb87ef655d303a4c0c9803377 -OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran3.tar.gz/md5/41f7fdc10d8cab0965da95e00e2269ba -OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/0a47ef77f9b2b70f424e00957f676c53d19c5dffbbcd5a743ab24bbc8608c5e8ad3cb3afefd8cab60a3c51970a63dd47c97868ecc0ef3532b83278c41a8daf96 -OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran4.tar.gz/md5/8453e7a5e5285e770fde7592582bc0e2 -OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/a37edfe68c85a6312d7918f1b58d6ac4bafc13081dbd327c830631913350a54bbf8bea57115b4f242d5f93c6b0a8f4995b5ef544a0de06e76c66287ff092e74c -OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran5.tar.gz/md5/6df24890be7a4899f35a2949f9f21d65 -OpenBLAS.v0.3.28+0.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/66601632f91689fe72afecd2e4d4ef3ad3b7653874228d5509c7732f2e6d63640f35d176ce2219b732632e0daeb76dc3ba11d3e776639985359b21b313056883 -OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran3.tar.gz/md5/d35df8d213c55bc1f9f765e0ba8c7b4e -OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran3.tar.gz/sha512/5d5de73150a2e796dc29425dbf415ff7aa443264d767d4e688de07335961ee39835c94b7d68900d49b70bf3ac08d356f3ae00c6d651eed64e504b02c9351edcb -OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran4.tar.gz/md5/384f9173b3804e6f9c73bcde9dacb545 -OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran4.tar.gz/sha512/64d3abeca401cee06575915161458408e9fb51e26f759129e1c7a9c27f68729d66e75f0654b977e842844650698c4b1627a18e495d91202a8c0483ef1b35bafc -OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran5.tar.gz/md5/0bd296248e1337fac054b9e0993fea82 -OpenBLAS.v0.3.28+0.x86_64-linux-musl-libgfortran5.tar.gz/sha512/fdb9ce296228f92c112bbeb346a2900a8d5a73e21a313a217cf7135fd77484cdeed53c86382ee5550f1b624eb6ed99d06b739229add7364217ca68fefedd04c4 -OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/524a2481331fdd3933f06b40e63433f1 -OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/06831cc855e9801dbf2248a0da123c345b6731c830f39d3166b8d8e7de8d649b6d9900e534ec6c1113a227203f6a9aa8171fcf548cfd56a4a67b6037c105ecf5 -OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/82f2b8f31f7b718f6ea743c996acbe4d -OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/fd1ccab964ae9410238d29b38cfe8c2ccca3fda3d78b4294bb4a54ab8abfd6bdaa80cadc0aeadf054bf99138c5dc3cac9370920b0b16cb8793630ab21d5bf667 -OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/b91add21ba0e2a0f28a9e4d347111cc3 -OpenBLAS.v0.3.28+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/8ed1d9e327529ee067d975c5c96dac3eabab5a88ed7b1b6e1b030f96bbd2418e3173cacd70e9976d619245757f2a34cc9527aafef1626fd288f14918c9b13eaa -OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/937847e2ad00539f3422d1ecb9d26d55 -OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/751d889661ddd46cd5718b49e34f826a4fb34b1b992251a5a975bc0af15b74a75d8a56f403e8fae570223477b2b8927d9cb36764e4b9e466045d5f317b8e7196 -OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/180c54c50362d05696589b270693ee8f -OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/2e3b76be5b7c4a7dc45f07e17493abd7ef9185e92429d8fa4d38766e0da96dd0777b619a9e420d2e1142bdab2ae1f755f9bc9ad97ee9a7927741778f89b9135f -OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/2f0fac7c96af66ea63fce26e409f4db6 -OpenBLAS.v0.3.28+0.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/141522971447c38b4908342f3ad09ffb18142d2e79b44f66fd80047b44c09216c9b94c39f776e3093f9ceb6bc4d6270cbbfb4209b2fc0debfe93e7145cb4dbff +OpenBLAS.v0.3.28+2.aarch64-apple-darwin-libgfortran5.tar.gz/md5/312aa603d089d680205dad7d5da58195 +OpenBLAS.v0.3.28+2.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/ffb0069561f52f8ac2f8affe937a00592e0c5d75c6d64bb0d5c93d1c925c93a46b763638031c88818b9dcef4a7b149ee3f15792a812e87f57a8ad086604164c4 +OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran3.tar.gz/md5/7c43d9e9ac07820130a3d5faefdef882 +OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/3ade0f098796148c37b118f9c052bad4e40431b4792f001043f040f8b1e4b7c3bae512f56ea21e6c0111246b2200e7720fe720a56a19dd11d1fba789344f29e3 +OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran4.tar.gz/md5/cd2fe87dac703c8bfa25406aa732b88a +OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/2aea68bd8f1db2ac920951c8d9a47ce8c071f3736ee8aad8d185a09be25234a0ffd11b9f9640015b82770ba3b3fad9aa511cc43501c1bb5a3a44f1fb7ccd5692 +OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran5.tar.gz/md5/e3db2bf2f1f38aeee8530c78f3ec049a +OpenBLAS.v0.3.28+2.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/a0ccb92e818650ac3cbc292d5af1a000ee9b123953cc3eb16e2479e926af3f2be0ed9858e3c0c1075b1b9dd70ec1e51b9dce2c9d45b999d296aa050d257a3cb1 +OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran3.tar.gz/md5/5bb605738930037259e773ebdb4a7041 +OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran3.tar.gz/sha512/967e0f33be7b743d9617627a947a802286962a46c7c3b2418aaa1504cffc5f311b01e1702b35ded18ae3686b1914c6085213b03fa8a51e0a7ca16dc4cfee8504 +OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran4.tar.gz/md5/ce175e82b9c6597c546552e79a43f934 +OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran4.tar.gz/sha512/8ff5dff293d9786fc4f541b209b35afcbe325c13ddd0f9c8f9bfca8ba5c318c7890152260a5441b9e9088751ce03b1ff8f0f5d6fd4f142fae34bdb7390d1952c +OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran5.tar.gz/md5/cae6aabbdccf31fb78b234785b52d48a +OpenBLAS.v0.3.28+2.aarch64-linux-musl-libgfortran5.tar.gz/sha512/ac842023e5db243fcfada22adca051bd2ffa04fca496454539931eede159e5d0490d444c338684c2d178c3367b23b8f3d76c544e30f1897bbed181f56237619f +OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/5d1f45f53dd1730051095fb8e027b14f +OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/0b1f91e86b5078b7cd6b64bc429a0e63bb5adf28df1baa336e67819fbd2c09f59b643c39e580f63e3bbccdc631c5d5e14c7d8afa6af94250453ce5286958f90f +OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/8b3e3ea928975c575798d47466aafb82 +OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/ebac0f7047dd8b97d85e4251953a23824701af02754afd6808f13eb276326b30eb292c85fa717fbd2f21b929e6a9816a012b8ea378a0fa27e671f81435f5d3b9 +OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/5aacfce96d5673b4d8341cb097d22c4a +OpenBLAS.v0.3.28+2.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/b84dc2b8cbe5453555182c3fcd8624d7a2b28fe3826d54fde3b77ad2c33e60309317d150f07554dd85e168b0ac1f91537a5c2c17fff9c02dd9216f01161e4965 +OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/dfeac22ee204868cf254dab5ae79382b +OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/710117eb7400a0aacf69d6053730eb3b3ff4767f8d38defb2aaad94aebf1646a794489e78a8f46b469901159cdca73dd2b9460fff11e95daa4a2642cab721a25 +OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/13ff2a40bc55839bdef76b796db1eb76 +OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/eb61fe6c0221e8f9d7a626b8d088ae1497155341dafb69835e7d53af76689ae212e1e4621e0729df5d896888c0b2d7354a24f7b57fe1d68f0b35c26bcf096699 +OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/aa7349724ba1d47256705777e755289a +OpenBLAS.v0.3.28+2.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/25ab56c44b7d0d5de17344f39071e6894e878e89b5e35412a3c9fe345abd2eef76d7816cabb6407c7c521c3bf67a4741b37ad7e580962ead9275273e431f1fb3 +OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/5d1f45f53dd1730051095fb8e027b14f +OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/0b1f91e86b5078b7cd6b64bc429a0e63bb5adf28df1baa336e67819fbd2c09f59b643c39e580f63e3bbccdc631c5d5e14c7d8afa6af94250453ce5286958f90f +OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/8b3e3ea928975c575798d47466aafb82 +OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/ebac0f7047dd8b97d85e4251953a23824701af02754afd6808f13eb276326b30eb292c85fa717fbd2f21b929e6a9816a012b8ea378a0fa27e671f81435f5d3b9 +OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/5aacfce96d5673b4d8341cb097d22c4a +OpenBLAS.v0.3.28+2.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/b84dc2b8cbe5453555182c3fcd8624d7a2b28fe3826d54fde3b77ad2c33e60309317d150f07554dd85e168b0ac1f91537a5c2c17fff9c02dd9216f01161e4965 +OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/dfeac22ee204868cf254dab5ae79382b +OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/710117eb7400a0aacf69d6053730eb3b3ff4767f8d38defb2aaad94aebf1646a794489e78a8f46b469901159cdca73dd2b9460fff11e95daa4a2642cab721a25 +OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/13ff2a40bc55839bdef76b796db1eb76 +OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/eb61fe6c0221e8f9d7a626b8d088ae1497155341dafb69835e7d53af76689ae212e1e4621e0729df5d896888c0b2d7354a24f7b57fe1d68f0b35c26bcf096699 +OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/aa7349724ba1d47256705777e755289a +OpenBLAS.v0.3.28+2.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/25ab56c44b7d0d5de17344f39071e6894e878e89b5e35412a3c9fe345abd2eef76d7816cabb6407c7c521c3bf67a4741b37ad7e580962ead9275273e431f1fb3 +OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran3.tar.gz/md5/53087cc770708c57d2654fd0095b64df +OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran3.tar.gz/sha512/90961448ae40b0445bf881d0815aec54d2096ad235dc8e3db8d698a72961ef9a97e7fcd08f79c83cd1f7c5a341464f52a90351d927d5f1c3e9c8ee32b17970db +OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran4.tar.gz/md5/ee910e19faa961bde11fdf90c211df9d +OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran4.tar.gz/sha512/f5cfecfe965991cfd7843eff71efa71d6842058565bb63657e909b2942e58a8c7506aa66335308961e59f392da16e1177d79542ae509795566a14122f67a1782 +OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran5.tar.gz/md5/fe52ba7ca8e16f37aa04b79248e0471d +OpenBLAS.v0.3.28+2.i686-linux-gnu-libgfortran5.tar.gz/sha512/79b5108886d60f12424709a841e359dc1cf23cef21bb0ee6d1a48043ac48a35dac1637e43c8ebf3f2e10dd34721993a7a12c5776f2975dd5bd7b6e29e1a9adc3 +OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran3.tar.gz/md5/88d8ff421d29456f1d7670ceaf8867ca +OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran3.tar.gz/sha512/91c1bd8142845d11fecba87a719315a14218e3863955ddd2ed82cecd4a2c177a48c660b6aac374ee9a11008245c0ced1bae70eaf5a1a6e3114db02e09a96396f +OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran4.tar.gz/md5/3035066a53032b551e49f56b323e941d +OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran4.tar.gz/sha512/f218e152a1c92bd374599814612add8010aedc78113cbe06465e8a1ee7f66892bb654cad687aa55555e74f3a65d74608692d41c9f0ce6c0bc63475ef62ab55b7 +OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran5.tar.gz/md5/f7cf36ac9a0cbb535952ec73f2e6c9ea +OpenBLAS.v0.3.28+2.i686-linux-musl-libgfortran5.tar.gz/sha512/00ab052d9fa4a72a640545782019f24ed6017b36aa89c5e659ce73b1e821817f560c09f71b26c027c0a05bd13567c71a6d7f5995d1c39ab233bec56cd3a7fd9e +OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran3.tar.gz/md5/b65414bb15539e5aa2f5f1c7984edb94 +OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran3.tar.gz/sha512/847ada020bb92fe6ea81dfffaf855707a529c9c0f7e246e802b9521e5c7d4aa36104d04279c09a905a797184cdf05a6fabf84711b7661ecb14e9ac2fba251f61 +OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran4.tar.gz/md5/0b626ebb8b3fc49b946723a9a2a21a91 +OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran4.tar.gz/sha512/b5bba23878399fc1ff20abc2e2eb4acb9691ce982f290e33384732452774a0b447bd0fb01ee696d10ad8b03d99eec905662af92bd3b499d9fe6db419e05d2573 +OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran5.tar.gz/md5/cb99d7d4944c5283a1a0142683e1d377 +OpenBLAS.v0.3.28+2.i686-w64-mingw32-libgfortran5.tar.gz/sha512/b77d3225e60f49506917bfff78c187df7157dbc834eccda2fa03d03eef8214b225682888a411a8b6e4b29a8d7e2b0ca625ea8c56b84ecc39e1f4f1012523c096 +OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/c6e5d4867a068e08b3f56f474e498b81 +OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/de6249439758a501bfd27d3ef04ec04cc06edf64de73f0709a6a40a2eaf40bd3d5d77dfd54b7b19e2f6bf6c104b4416d3e225faa0cff4cb631785c08d90b8614 +OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/32e70466cfa3cfec65ab4cad3abc5f03 +OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/2642385a5e9fc8e9c3839a5a44f9753b21b5078725f7d0c3e1ebe96b76129a3b8e2627d92629dee4f6fd7e8e51e86a7fbedc80cbe4d1a6812cea363559950da0 +OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/e2332831bd88d57132241697952819e7 +OpenBLAS.v0.3.28+2.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/ad03edf9ac56bf6311f0ca70a1bc359242accfe82cba9e42f39f6cb1c3006226179ff9be8218847889cae10fac13bc33f60837e1e3249e309172da7fbc25400f +OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran3.tar.gz/md5/27c24775af446a44a72a28ffd197696d +OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/2af8caa33bee88efff84653f3932b04e8fd4aabb1bf16d49fa73657b0ec13c9457fde7ab3f953fc9b01da5c2841c3c9b588e3b0f559b89df0e6268468d1f7cc8 +OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran4.tar.gz/md5/414e701d918d5fba08a12de6979db4b5 +OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/949886d388b80e19b944d102852f2bb58ffa03c42e624986dd9dc076797c996634d4a8fc0f04544451d6848c2079969816979e1f68a999b2747e9dd5472be7a6 +OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran5.tar.gz/md5/29fcf62c0280cc10f91d22189a2e8de8 +OpenBLAS.v0.3.28+2.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/02e75d4ecf9cd922157a72c0ca2e713cf336b125df3982cd5f7cc4f2a04367ad4c2b1190ca2a0a9df8b639c7ebcfc9783066e99dd0b13acde7b02038391e8567 +OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran3.tar.gz/md5/147d5e8eb2ec78fc8a31bdb091fab001 +OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/2319eda568800c0b1f2d96a8a36c59b1bbd792c06de1d740aea3f1e49798242426ea8d10c100c42c3c281702e2b4f5b673b6ab5252b276d48542e875bcaa3094 +OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran4.tar.gz/md5/448857d9c4b2e95afc12a14c75b24055 +OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/3e7c8cd55e0b15a30992b1e0b48a6e2ae36fd9babf689fa5595c7de94aec401de1d7821d45a22bf14cd5c45c708bc8fa3511d34d732dadd4daaca3f49e200bdb +OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran5.tar.gz/md5/3aaf417685b44e0e505208f7b31b981a +OpenBLAS.v0.3.28+2.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/f7b1d123e48ede93fe624a79d9535a8915bfa3441d7a6f9c6643467027414c9f2538e299858ea98bbb49d4e6d385a6a491063cb1878ac3b0b3d6a8f7ff0a48df +OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran3.tar.gz/md5/5723136deaaf4b2e5960fb0774943288 +OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran3.tar.gz/sha512/127ea8b2b0d8d4586a23a2b8ecbf148d512efe68626e89b0688c3c9e29ed9420b45ae86755c1467313c565f9f3835762051d7086a815b813dbe6e9eb05fb4be1 +OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran4.tar.gz/md5/80b1b9cf5346916edda653174a987aa2 +OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran4.tar.gz/sha512/77e1387ec969bbed4945d2a598a1cd04d258265c4b2d5c43af92118eb32e0c69e40619a20ea1835f277febcfea068b241343d44932afef832bdcfd2e9f618f0a +OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran5.tar.gz/md5/44dcedf01c938d1a1c67dd3bc90ab61d +OpenBLAS.v0.3.28+2.x86_64-linux-musl-libgfortran5.tar.gz/sha512/e490d49b8d41d73ab3e71aca8c691ca58704f0fc6930cbfcc203f97b8db8d83144bad597a2c53ff0c0c4f7c40316d975a1b589a3603873d508f6beeb75970c5b +OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/0e8a7e88b54cb836292c289d1c456fa9 +OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/0e9b3af6839b9c41c950bb4d8b739f0243a890af7092ef9f3a00e4931f2acc3820afb78e40c7bfef716dcd3230c1d0acc7b0b37f30eb47441b476bd7540745e6 +OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/5fc47ad55780c99ef9cab7ef1b26d9c0 +OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/c531201e4abddd652efeb5801658f5c1e4891578f181e99d6e41fc0d3bc6347b82e5e928ff8a717ee1e75bb0a6a765260bf7c99fce44aa24c21f1c5a5e3c1e3b +OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/dc127f3ab984b5d47b325d5701ab73cd +OpenBLAS.v0.3.28+2.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/50850911703320894a2e1e996c5de4613b5f9e3012f5cbf591f3677799599c45d9cc4c42cf310bdc6ba91ef550e52f6424bbbabdf47f96748d4669d94e6b46a4 +OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/937847e2ad00539f3422d1ecb9d26d55 +OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/751d889661ddd46cd5718b49e34f826a4fb34b1b992251a5a975bc0af15b74a75d8a56f403e8fae570223477b2b8927d9cb36764e4b9e466045d5f317b8e7196 +OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/180c54c50362d05696589b270693ee8f +OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/2e3b76be5b7c4a7dc45f07e17493abd7ef9185e92429d8fa4d38766e0da96dd0777b619a9e420d2e1142bdab2ae1f755f9bc9ad97ee9a7927741778f89b9135f +OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/2f0fac7c96af66ea63fce26e409f4db6 +OpenBLAS.v0.3.28+2.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/141522971447c38b4908342f3ad09ffb18142d2e79b44f66fd80047b44c09216c9b94c39f776e3093f9ceb6bc4d6270cbbfb4209b2fc0debfe93e7145cb4dbff openblas-5ef8b1964658f9cb6a6324a06f6a1a022609b0c5.tar.gz/md5/f7a1fe86cefbf7d4f2608843c7833ca7 openblas-5ef8b1964658f9cb6a6324a06f6a1a022609b0c5.tar.gz/sha512/5f6020e958967a12a3c5b18bde13331f9c0602bd073563f35cd7cec848c92b45f30ca362819b12cd16989c0e4641ee3e63db8322d1092f61b31ba2e4068dd7a7 diff --git a/deps/checksums/patchelf b/deps/checksums/patchelf index e2029b83f14fc..6392e44d8f2e8 100644 --- a/deps/checksums/patchelf +++ b/deps/checksums/patchelf @@ -1,2 +1,2 @@ -patchelf-0.18.0.tar.bz2/md5/9b091a689583fdc7c3206679586322d5 -patchelf-0.18.0.tar.bz2/sha512/bf26194ca3435b141dd330890fcc0c9d805d0ad6a537901dabe6707a13cd28e7e6217462f3ebb3cb4861302dd8632342ec988fc18246c35332a94f2b349d4f4f +patchelf-0.17.2.tar.bz2/md5/d76db4f1a27b0934d0b0d0585b081c0f +patchelf-0.17.2.tar.bz2/sha512/8277adf95513f88fb190536a38bdfdf438a4cc7685d8a130bdffbe064441f0f25095b6c83bbb190133e1a138963776d15b46c247dd2f1a073a1bfe1d1dbdd503 diff --git a/deps/openblas.mk b/deps/openblas.mk index 1bc068d2859d9..affd1c7a7aa55 100644 --- a/deps/openblas.mk +++ b/deps/openblas.mk @@ -90,7 +90,12 @@ $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-winexit.patch-applied: $(BUILDDIR)/$(OP patch -p1 -f < $(SRCDIR)/patches/openblas-winexit.patch echo 1 > $@ -$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-ofast-power.patch-applied: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-winexit.patch-applied +$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-memory-buffer-multi-threading.patch-applied: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-winexit.patch-applied + cd $(BUILDDIR)/$(OPENBLAS_SRC_DIR) && \ + patch -p1 -f < $(SRCDIR)/patches/openblas-memory-buffer-multi-threading.patch + echo 1 > $@ + +$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-ofast-power.patch-applied: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-memory-buffer-multi-threading.patch-applied cd $(BUILDDIR)/$(OPENBLAS_SRC_DIR) && \ patch -p1 -f < $(SRCDIR)/patches/openblas-ofast-power.patch echo 1 > $@ diff --git a/deps/patchelf.version b/deps/patchelf.version index 9038338d45faf..6e4f32a0c2fe4 100644 --- a/deps/patchelf.version +++ b/deps/patchelf.version @@ -1,3 +1,4 @@ ## source build # Patchelf (we don't ship this or even use a JLL, we just always build it) -PATCHELF_VER := 0.18.0 +# NOTE: Do not upgrade this to 0.18+ until https://github.com/NixOS/patchelf/issues/492 is fixed +PATCHELF_VER := 0.17.2 diff --git a/deps/patches/openblas-memory-buffer-multi-threading.patch b/deps/patches/openblas-memory-buffer-multi-threading.patch new file mode 100644 index 0000000000000..9693b5cf61597 --- /dev/null +++ b/deps/patches/openblas-memory-buffer-multi-threading.patch @@ -0,0 +1,49 @@ +From 23b5d66a86417a071bba9a96a0573192237981b6 Mon Sep 17 00:00:00 2001 +From: Martin Kroeker +Date: Wed, 14 Aug 2024 10:35:44 +0200 +Subject: [PATCH 1/2] Ensure a memory buffer has been allocated for each thread + before invoking it + +--- + driver/others/blas_server.c | 2 ++ + 1 file changed, 2 insertions(+) + +From d24b3cf39392a99e81ed47a5f093fbd074d4b39b Mon Sep 17 00:00:00 2001 +From: Martin Kroeker +Date: Thu, 15 Aug 2024 15:32:58 +0200 +Subject: [PATCH 2/2] properly fix buffer allocation and assignment + +--- + driver/others/blas_server.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) +diff --git a/driver/others/blas_server.c b/driver/others/blas_server.c +index 765511d8c7..b9a7674c17 100644 +--- a/driver/others/blas_server.c ++++ b/driver/others/blas_server.c +@@ -1076,6 +1076,8 @@ fprintf(STDERR, "Server[%2ld] Calculation started. Mode = 0x%03x M = %3ld N=%3l + main_status[cpu] = MAIN_RUNNING1; + #endif + ++if (buffer == NULL) blas_thread_buffer[cpu] = blas_memory_alloc(2); ++ + //For target LOONGSON3R5, applying an offset to the buffer is essential + //for minimizing cache conflicts and optimizing performance. + #if defined(ARCH_LOONGARCH64) && !defined(NO_AFFINITY) + +diff --git a/driver/others/blas_server.c b/driver/others/blas_server.c +index b9a7674c17..29f8a5e646 100644 +--- a/driver/others/blas_server.c ++++ b/driver/others/blas_server.c +@@ -1076,7 +1076,11 @@ fprintf(STDERR, "Server[%2ld] Calculation started. Mode = 0x%03x M = %3ld N=%3l + main_status[cpu] = MAIN_RUNNING1; + #endif + +-if (buffer == NULL) blas_thread_buffer[cpu] = blas_memory_alloc(2); ++if (buffer == NULL) { ++ blas_thread_buffer[cpu] = blas_memory_alloc(2); ++ buffer = blas_thread_buffer[cpu]; ++} ++ + + //For target LOONGSON3R5, applying an offset to the buffer is essential + //for minimizing cache conflicts and optimizing performance. diff --git a/doc/man/julia.1 b/doc/man/julia.1 index 049543d795acd..ebac4362b39a6 100644 --- a/doc/man/julia.1 +++ b/doc/man/julia.1 @@ -28,7 +28,7 @@ julia - a high-level, high-performance dynamic programming language for technical computing .SH SYNOPSIS -\fBjulia\fR [OPTIONS...] \fB--\fR [PROGRAMMFILE] [ARGS...] +\fBjulia\fR [OPTIONS...] \fB--\fR [PROGRAMFILE] [ARGS...] If a Julia source file is given as a \fIPROGRAMFILE\fP (optionally followed by arguments in \fIARGS\fP) Julia will execute the program and exit. diff --git a/doc/src/base/base.md b/doc/src/base/base.md index 1a8cd29f91066..b5d50a846ce89 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -106,6 +106,7 @@ where . -> :: +[] ``` ## Standard Modules diff --git a/doc/src/base/math.md b/doc/src/base/math.md index 7091aa6f1aa87..4f816ce2a6c1d 100644 --- a/doc/src/base/math.md +++ b/doc/src/base/math.md @@ -166,6 +166,7 @@ Base.flipsign Base.sqrt(::Number) Base.isqrt Base.Math.cbrt(::AbstractFloat) +Base.fourthroot(::Number) Base.real Base.imag Base.reim diff --git a/doc/src/manual/environment-variables.md b/doc/src/manual/environment-variables.md index 84f36144304aa..30f2263904f40 100644 --- a/doc/src/manual/environment-variables.md +++ b/doc/src/manual/environment-variables.md @@ -267,6 +267,14 @@ versions of packages already installed as possible. !!! compat "Julia 1.9" This only affects Julia 1.9 and above. +### [`JULIA_PKG_GC_AUTO`](@id JULIA_PKG_GC_AUTO) + +If set to `false`, automatic garbage collection of packages and artifacts will be disabled; +see [`Pkg.gc`](https://pkgdocs.julialang.org/v1/api/#Pkg.gc) for more details. + +!!! compat "Julia 1.12" + This environment variable is only supported on Julia 1.12 and above. + ## Network transport ### [`JULIA_NO_VERIFY_HOSTS`](@id JULIA_NO_VERIFY_HOSTS) diff --git a/doc/src/manual/mathematical-operations.md b/doc/src/manual/mathematical-operations.md index 1d613931669fc..d2cef68bd6fff 100644 --- a/doc/src/manual/mathematical-operations.md +++ b/doc/src/manual/mathematical-operations.md @@ -551,21 +551,22 @@ See [Conversion and Promotion](@ref conversion-and-promotion) for how to define ### Powers, logs and roots -| Function | Description | -|:------------------------ |:-------------------------------------------------------------------------- | -| [`sqrt(x)`](@ref), `√x` | square root of `x` | -| [`cbrt(x)`](@ref), `βˆ›x` | cube root of `x` | -| [`hypot(x, y)`](@ref) | hypotenuse of right-angled triangle with other sides of length `x` and `y` | -| [`exp(x)`](@ref) | natural exponential function at `x` | -| [`expm1(x)`](@ref) | accurate `exp(x) - 1` for `x` near zero | -| [`ldexp(x, n)`](@ref) | `x * 2^n` computed efficiently for integer values of `n` | -| [`log(x)`](@ref) | natural logarithm of `x` | -| [`log(b, x)`](@ref) | base `b` logarithm of `x` | -| [`log2(x)`](@ref) | base 2 logarithm of `x` | -| [`log10(x)`](@ref) | base 10 logarithm of `x` | -| [`log1p(x)`](@ref) | accurate `log(1 + x)` for `x` near zero | -| [`exponent(x)`](@ref) | binary exponent of `x` | -| [`significand(x)`](@ref) | binary significand (a.k.a. mantissa) of a floating-point number `x` | +| Function | Description | +|:----------------------------- |:-------------------------------------------------------------------------- | +| [`sqrt(x)`](@ref), `√x` | square root of `x` | +| [`cbrt(x)`](@ref), `βˆ›x` | cube root of `x` | +| [`fourthroot(x)`](@ref), `∜x` | fourth root of `x` | +| [`hypot(x, y)`](@ref) | hypotenuse of right-angled triangle with other sides of length `x` and `y` | +| [`exp(x)`](@ref) | natural exponential function at `x` | +| [`expm1(x)`](@ref) | accurate `exp(x) - 1` for `x` near zero | +| [`ldexp(x, n)`](@ref) | `x * 2^n` computed efficiently for integer values of `n` | +| [`log(x)`](@ref) | natural logarithm of `x` | +| [`log(b, x)`](@ref) | base `b` logarithm of `x` | +| [`log2(x)`](@ref) | base 2 logarithm of `x` | +| [`log10(x)`](@ref) | base 10 logarithm of `x` | +| [`log1p(x)`](@ref) | accurate `log(1 + x)` for `x` near zero | +| [`exponent(x)`](@ref) | binary exponent of `x` | +| [`significand(x)`](@ref) | binary significand (a.k.a. mantissa) of a floating-point number `x` | For an overview of why functions like [`hypot`](@ref), [`expm1`](@ref), and [`log1p`](@ref) are necessary and useful, see John D. Cook's excellent pair of blog posts on the subject: [expm1, log1p, erfc](https://www.johndcook.com/blog/2010/06/07/math-library-functions-that-seem-unnecessary/), diff --git a/src/Makefile b/src/Makefile index 6f78f4a8b6aa1..52e673aa6cc1a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -385,7 +385,7 @@ $(BUILDDIR)/julia_version.h: $(JULIAHOME)/VERSION CXXLD = $(CXX) -shared -$(BUILDDIR)/julia.expmap: $(SRCDIR)/julia.expmap.in +$(BUILDDIR)/julia.expmap: $(SRCDIR)/julia.expmap.in $(JULIAHOME)/VERSION $(LLVM_CONFIG_ABSOLUTE) sed <'$<' >'$@' -e "s/@JULIA_SHLIB_SYMBOL_VERSION@/JL_LIBJULIA_$(SOMAJOR)/" \ -e "s/@LLVM_SHLIB_SYMBOL_VERSION@/$(LLVM_SHLIB_SYMBOL_VERSION)/" diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 48afa360c0037..b4c8ef6095a55 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -284,7 +284,7 @@ jl_code_instance_t *jl_ci_cache_lookup(const jl_cgparams_t &cgparams, jl_method_ jl_value_t *ci = cgparams.lookup(mi, world, world); JL_GC_PROMISE_ROOTED(ci); jl_code_instance_t *codeinst = NULL; - if (ci != jl_nothing) { + if (ci != jl_nothing && jl_atomic_load_relaxed(&((jl_code_instance_t *)ci)->inferred) != jl_nothing) { codeinst = (jl_code_instance_t*)ci; } else { diff --git a/src/ccall.cpp b/src/ccall.cpp index 97315d46b6ead..36808e13fdbf9 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1671,9 +1671,8 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) emit_gc_safepoint(ctx.builder, ctx.types().T_size, get_current_ptls(ctx), ctx.tbaa().tbaa_const); return ghostValue(ctx, jl_nothing_type); } - else if (is_libjulia_func("jl_get_ptls_states")) { + else if (is_libjulia_func(jl_get_ptls_states)) { ++CCALL_STAT(jl_get_ptls_states); - assert(lrt == ctx.types().T_size); assert(!isVa && !llvmcall && nccallargs == 0); JL_GC_POP(); return mark_or_box_ccall_result(ctx, get_current_ptls(ctx), retboxed, rt, unionall, static_rt); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 0969f78f10bb4..f911ef17eea38 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -650,7 +650,7 @@ static Type *_julia_type_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt, jl { // this function converts a Julia Type into the equivalent LLVM type if (isboxed) *isboxed = false; - if (jt == (jl_value_t*)jl_bottom_type) + if (jt == (jl_value_t*)jl_bottom_type || jt == (jl_value_t*)jl_typeofbottom_type || jt == (jl_value_t*)jl_typeofbottom_type->super) return getVoidTy(ctxt); if (jl_is_concrete_immutable(jt)) { if (jl_datatype_nbits(jt) == 0) @@ -760,7 +760,7 @@ static Type *_julia_struct_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt, // use this where C-compatible (unboxed) structs are desired // use julia_type_to_llvm directly when you want to preserve Julia's type semantics if (isboxed) *isboxed = false; - if (jt == (jl_value_t*)jl_bottom_type) + if (jt == (jl_value_t*)jl_bottom_type || jt == (jl_value_t*)jl_typeofbottom_type || jt == (jl_value_t*)jl_typeofbottom_type->super) return getVoidTy(ctxt); if (jl_is_primitivetype(jt)) return bitstype_to_llvm(jt, ctxt, llvmcall); @@ -948,6 +948,9 @@ static bool for_each_uniontype_small( allunbox &= for_each_uniontype_small(f, ((jl_uniontype_t*)ty)->b, counter); return allunbox; } + else if (ty == (jl_value_t*)jl_typeofbottom_type->super) { + f(++counter, jl_typeofbottom_type); // treat Tuple{union{}} as identical to typeof(Union{}) + } else if (jl_is_pointerfree(ty)) { f(++counter, (jl_datatype_t*)ty); return true; @@ -1691,6 +1694,8 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, if (intersected_type == (jl_value_t*)jl_bottom_type) known_isa = false; } + if (intersected_type == (jl_value_t*)jl_typeofbottom_type->super) + intersected_type = (jl_value_t*)jl_typeofbottom_type; // swap abstract Type{Union{}} for concrete typeof(Union{}) if (known_isa) { if (!*known_isa && !msg.isTriviallyEmpty()) { emit_type_error(ctx, x, literal_pointer_val(ctx, type), msg); diff --git a/src/codegen.cpp b/src/codegen.cpp index 6551f13ea566e..e499d1193dee6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2240,6 +2240,8 @@ static inline jl_cgval_t ghostValue(jl_codectx_t &ctx, jl_value_t *typ) // replace T::Type{T} with T, by assuming that T must be a leaftype of some sort jl_cgval_t constant(NULL, true, typ, NULL, best_tbaa(ctx.tbaa(), typ)); constant.constant = jl_tparam0(typ); + if (typ == (jl_value_t*)jl_typeofbottom_type->super) + constant.isghost = true; return constant; } return jl_cgval_t(typ); @@ -2252,7 +2254,7 @@ static inline jl_cgval_t ghostValue(jl_codectx_t &ctx, jl_datatype_t *typ) static inline jl_cgval_t mark_julia_const(jl_codectx_t &ctx, jl_value_t *jv) { jl_value_t *typ; - if (jl_is_type(jv)) { + if (jl_is_type(jv) && jv != jl_bottom_type) { typ = (jl_value_t*)jl_wrap_Type(jv); // TODO: gc-root this? } else { @@ -3619,8 +3621,8 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva if (arg1.constant && arg2.constant) return ConstantInt::get(getInt1Ty(ctx.builder.getContext()), jl_egal(arg1.constant, arg2.constant)); - jl_value_t *rt1 = arg1.typ; - jl_value_t *rt2 = arg2.typ; + jl_value_t *rt1 = (arg1.constant ? jl_typeof(arg1.constant) : arg1.typ); + jl_value_t *rt2 = (arg2.constant ? jl_typeof(arg2.constant) : arg2.typ); if (jl_is_concrete_type(rt1) && jl_is_concrete_type(rt2) && !jl_is_kind(rt1) && !jl_is_kind(rt2) && rt1 != rt2) { // disjoint concrete leaf types are never equal (quick test) return ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0); @@ -6410,13 +6412,6 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ bp = julia_binding_gv(ctx, bnd); bp = julia_binding_pvalue(ctx, bp); } - else if (jl_is_slotnumber(mn) || jl_is_argument(mn)) { - // XXX: eval_methoddef does not have this code branch - int sl = jl_slot_number(mn)-1; - jl_varinfo_t &vi = ctx.slots[sl]; - bp = vi.boxroot; - name = literal_pointer_val(ctx, (jl_value_t*)slot_symbol(ctx, sl)); - } if (bp) { Value *mdargs[] = { name, literal_pointer_val(ctx, (jl_value_t*)mod), bp, literal_pointer_val(ctx, bnd) }; jl_cgval_t gf = mark_julia_type( @@ -9534,7 +9529,7 @@ static jl_llvm_functions_t RTindex = UndefValue::get(getInt8Ty(ctx.builder.getContext())); } else if (jl_is_concrete_type(val.typ) || val.constant) { - size_t tindex = get_box_tindex((jl_datatype_t*)val.typ, phiType); + size_t tindex = get_box_tindex((jl_datatype_t*)(val.constant ? jl_typeof(val.constant) : val.typ), phiType); if (tindex == 0) { if (VN) V = boxed(ctx, val); diff --git a/src/datatype.c b/src/datatype.c index 5d35a7b55057d..1157c1d425cb2 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -357,6 +357,8 @@ int jl_struct_try_layout(jl_datatype_t *dt) int jl_datatype_isinlinealloc(jl_datatype_t *ty, int pointerfree) { + if (jl_typeofbottom_type && ty == jl_typeofbottom_type->super) + ty = jl_typeofbottom_type; if (ty->name->mayinlinealloc && jl_struct_try_layout(ty)) { if (ty->layout->npointers > 0) { if (pointerfree) @@ -1656,6 +1658,8 @@ JL_DLLEXPORT jl_value_t *jl_new_struct_uninit(jl_datatype_t *type) { jl_task_t *ct = jl_current_task; if (!jl_is_datatype(type) || !type->isconcretetype || type->layout == NULL || jl_is_layout_opaque(type->layout)) { + if (type == jl_typeofbottom_type->super) + return jl_bottom_type; // ::Type{Union{}} is an abstract type, but is also a singleton when used as a field type jl_type_error("new", (jl_value_t*)jl_datatype_type, (jl_value_t*)type); } if (type->instance != NULL) @@ -2188,7 +2192,7 @@ inline int setonce_bits(jl_datatype_t *rty, char *p, jl_value_t *parent, jl_valu } else { char *px = lock(p, parent, needlock, isatomic); - success = undefref_check(rty, (jl_value_t*)px) != NULL; + success = undefref_check(rty, (jl_value_t*)px) == NULL; if (success) memassign_safe(hasptr, px, rhs, fsz); unlock(p, parent, needlock, isatomic); diff --git a/src/gc-debug.c b/src/gc-debug.c index ec3c8d731edd8..19dd93af5f236 100644 --- a/src/gc-debug.c +++ b/src/gc-debug.c @@ -537,13 +537,13 @@ static void gc_scrub_task(jl_task_t *ta) char *low; char *high; - if (ta->copy_stack && ptls2 && ta == jl_atomic_load_relaxed(&ptls2->current_task)) { + if (ta->ctx.copy_stack && ptls2 && ta == jl_atomic_load_relaxed(&ptls2->current_task)) { low = (char*)ptls2->stackbase - ptls2->stacksize; high = (char*)ptls2->stackbase; } - else if (ta->stkbuf) { - low = (char*)ta->stkbuf; - high = (char*)ta->stkbuf + ta->bufsz; + else if (ta->ctx.stkbuf) { + low = (char*)ta->ctx.stkbuf; + high = (char*)ta->ctx.stkbuf + ta->ctx.bufsz; } else return; diff --git a/src/gc-heap-snapshot.cpp b/src/gc-heap-snapshot.cpp index b84d1f96f273c..4fcc66495fc45 100644 --- a/src/gc-heap-snapshot.cpp +++ b/src/gc-heap-snapshot.cpp @@ -182,6 +182,7 @@ struct HeapSnapshot { // global heap snapshot, mutated by garbage collector // when snapshotting is on. int gc_heap_snapshot_enabled = 0; +int gc_heap_snapshot_redact_data = 0; HeapSnapshot *g_snapshot = nullptr; // mutex for gc-heap-snapshot. jl_mutex_t heapsnapshot_lock; @@ -195,7 +196,7 @@ void _add_synthetic_root_entries(HeapSnapshot *snapshot) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_gc_take_heap_snapshot(ios_t *nodes, ios_t *edges, - ios_t *strings, ios_t *json, char all_one) + ios_t *strings, ios_t *json, char all_one, char redact_data) { HeapSnapshot snapshot; snapshot.nodes = nodes; @@ -207,6 +208,7 @@ JL_DLLEXPORT void jl_gc_take_heap_snapshot(ios_t *nodes, ios_t *edges, // Enable snapshotting g_snapshot = &snapshot; + gc_heap_snapshot_redact_data = redact_data; gc_heap_snapshot_enabled = true; _add_synthetic_root_entries(&snapshot); @@ -216,6 +218,7 @@ JL_DLLEXPORT void jl_gc_take_heap_snapshot(ios_t *nodes, ios_t *edges, // Disable snapshotting gc_heap_snapshot_enabled = false; + gc_heap_snapshot_redact_data = 0; g_snapshot = nullptr; jl_mutex_unlock(&heapsnapshot_lock); @@ -328,7 +331,7 @@ size_t record_node_to_gc_snapshot(jl_value_t *a) JL_NOTSAFEPOINT if (jl_is_string(a)) { node_type = "String"; - name = jl_string_data(a); + name = gc_heap_snapshot_redact_data ? "" : jl_string_data(a); self_size = jl_string_len(a); } else if (jl_is_symbol(a)) { diff --git a/src/gc-heap-snapshot.h b/src/gc-heap-snapshot.h index a58f58aba5458..e7fbb36249ec1 100644 --- a/src/gc-heap-snapshot.h +++ b/src/gc-heap-snapshot.h @@ -122,7 +122,7 @@ static inline void gc_heap_snapshot_record_finlist(jl_value_t *finlist, size_t i // Functions to call from Julia to take heap snapshot // --------------------------------------------------------------------- JL_DLLEXPORT void jl_gc_take_heap_snapshot(ios_t *nodes, ios_t *edges, - ios_t *strings, ios_t *json, char all_one); + ios_t *strings, ios_t *json, char all_one, char redact_data); #ifdef __cplusplus diff --git a/src/gc-stacks.c b/src/gc-stacks.c index 5706f4ce67c1d..08425019a4daf 100644 --- a/src/gc-stacks.c +++ b/src/gc-stacks.c @@ -160,7 +160,7 @@ static unsigned select_pool(size_t nb) JL_NOTSAFEPOINT } -static void _jl_free_stack(jl_ptls_t ptls, void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT +void _jl_free_stack(jl_ptls_t ptls, void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT { #ifdef _COMPILER_ASAN_ENABLED_ __asan_unpoison_stack_memory((uintptr_t)stkbuf, bufsz); @@ -186,14 +186,14 @@ JL_DLLEXPORT void jl_free_stack(void *stkbuf, size_t bufsz) void jl_release_task_stack(jl_ptls_t ptls, jl_task_t *task) { // avoid adding an original thread stack to the free list - if (task == ptls->root_task && !task->copy_stack) + if (task == ptls->root_task && !task->ctx.copy_stack) return; - void *stkbuf = task->stkbuf; - size_t bufsz = task->bufsz; + void *stkbuf = task->ctx.stkbuf; + size_t bufsz = task->ctx.bufsz; if (bufsz <= pool_sizes[JL_N_STACK_POOLS - 1]) { unsigned pool_id = select_pool(bufsz); if (pool_sizes[pool_id] == bufsz) { - task->stkbuf = NULL; + task->ctx.stkbuf = NULL; #ifdef _COMPILER_ASAN_ENABLED_ __asan_unpoison_stack_memory((uintptr_t)stkbuf, bufsz); #endif @@ -296,17 +296,17 @@ void sweep_stack_pools(void) JL_NOTSAFEPOINT jl_task_t *t = (jl_task_t*)lst[n]; assert(jl_is_task(t)); if (gc_marked(jl_astaggedvalue(t)->bits.gc)) { - if (t->stkbuf == NULL) + if (t->ctx.stkbuf == NULL) ndel++; // jl_release_task_stack called else n++; } else { ndel++; - void *stkbuf = t->stkbuf; - size_t bufsz = t->bufsz; + void *stkbuf = t->ctx.stkbuf; + size_t bufsz = t->ctx.bufsz; if (stkbuf) { - t->stkbuf = NULL; + t->ctx.stkbuf = NULL; _jl_free_stack(ptls2, stkbuf, bufsz); } #ifdef _COMPILER_TSAN_ENABLED_ @@ -338,7 +338,7 @@ JL_DLLEXPORT jl_array_t *jl_live_tasks(void) continue; small_arraylist_t *live_tasks = &ptls2->gc_tls.heap.live_tasks; size_t n = mtarraylist_length(live_tasks); - l += n + (ptls2->root_task->stkbuf != NULL); + l += n + (ptls2->root_task->ctx.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 @@ -350,7 +350,7 @@ JL_DLLEXPORT jl_array_t *jl_live_tasks(void) if (ptls2 == NULL) continue; jl_task_t *t = ptls2->root_task; - if (t->stkbuf != NULL) { + if (t->ctx.stkbuf != NULL) { if (j == l) goto restart; jl_array_data(a,void*)[j++] = t; @@ -359,7 +359,7 @@ JL_DLLEXPORT jl_array_t *jl_live_tasks(void) 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 (t->ctx.stkbuf != NULL) { if (j == l) goto restart; jl_array_data(a,void*)[j++] = t; diff --git a/src/gc-stock.c b/src/gc-stock.c index 1fc2087da6503..3ae14f378a2e7 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -2144,9 +2144,9 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ (ta, tid != -1 && ta == gc_all_tls_states[tid]->root_task)); } #ifdef COPY_STACKS - void *stkbuf = ta->stkbuf; - if (stkbuf && ta->copy_stack) { - gc_setmark_buf_(ptls, stkbuf, bits, ta->bufsz); + void *stkbuf = ta->ctx.stkbuf; + if (stkbuf && ta->ctx.copy_stack) { + gc_setmark_buf_(ptls, stkbuf, bits, ta->ctx.bufsz); // For gc_heap_snapshot_record: // TODO: attribute size of stack // TODO: edge to stack data @@ -2159,12 +2159,12 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ uintptr_t lb = 0; uintptr_t ub = (uintptr_t)-1; #ifdef COPY_STACKS - if (stkbuf && ta->copy_stack && !ta->ptls) { + if (stkbuf && ta->ctx.copy_stack && !ta->ptls) { int16_t tid = jl_atomic_load_relaxed(&ta->tid); assert(tid >= 0); jl_ptls_t ptls2 = gc_all_tls_states[tid]; ub = (uintptr_t)ptls2->stackbase; - lb = ub - ta->copy_stack; + lb = ub - ta->ctx.copy_stack; offset = (uintptr_t)stkbuf - lb; } #endif diff --git a/src/gc-stock.h b/src/gc-stock.h index 7176ad8b504f4..45c93bf4289ae 100644 --- a/src/gc-stock.h +++ b/src/gc-stock.h @@ -446,7 +446,7 @@ STATIC_INLINE int gc_is_concurrent_collector_thread(int tid) JL_NOTSAFEPOINT STATIC_INLINE int gc_random_parallel_collector_thread_id(jl_ptls_t ptls) JL_NOTSAFEPOINT { assert(jl_n_markthreads > 0); - int v = gc_first_tid + (int)cong(jl_n_markthreads - 1, &ptls->rngseed); + int v = gc_first_tid + (int)cong(jl_n_markthreads, &ptls->rngseed); // cong is [0, n) assert(v >= gc_first_tid && v <= gc_last_parallel_collector_thread_id()); return v; } diff --git a/src/init.c b/src/init.c index eff786b564e54..1d466a0a736f9 100644 --- a/src/init.c +++ b/src/init.c @@ -64,15 +64,19 @@ void jl_init_stack_limits(int ismaster, void **stack_lo, void **stack_hi) // threads since it seems to return bogus values for master thread on Linux // and possibly OSX. if (!ismaster) { -# if defined(_OS_LINUX_) +# if defined(_OS_LINUX_) || defined(_OS_FREEBSD_) pthread_attr_t attr; +#if defined(_OS_FREEBSD_) + pthread_attr_get_np(pthread_self(), &attr); +#else pthread_getattr_np(pthread_self(), &attr); +#endif void *stackaddr; size_t stacksize; pthread_attr_getstack(&attr, &stackaddr, &stacksize); pthread_attr_destroy(&attr); - *stack_hi = stackaddr; - *stack_lo = (char*)stackaddr - stacksize; + *stack_lo = stackaddr; + *stack_hi = (char*)stackaddr + stacksize; return; # elif defined(_OS_DARWIN_) extern void *pthread_get_stackaddr_np(pthread_t thread); @@ -80,19 +84,8 @@ void jl_init_stack_limits(int ismaster, void **stack_lo, void **stack_hi) pthread_t thread = pthread_self(); void *stackaddr = pthread_get_stackaddr_np(thread); size_t stacksize = pthread_get_stacksize_np(thread); - *stack_hi = stackaddr; *stack_lo = (char*)stackaddr - stacksize; - return; -# elif defined(_OS_FREEBSD_) - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_get_np(pthread_self(), &attr); - void *stackaddr; - size_t stacksize; - pthread_attr_getstack(&attr, &stackaddr, &stacksize); - pthread_attr_destroy(&attr); *stack_hi = stackaddr; - *stack_lo = (char*)stackaddr - stacksize; return; # else # warning "Getting precise stack size for thread is not supported." @@ -882,8 +875,8 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_ jl_n_markthreads = 0; jl_n_sweepthreads = 0; jl_n_gcthreads = 0; - jl_n_threads_per_pool[0] = 1; - jl_n_threads_per_pool[1] = 0; + jl_n_threads_per_pool[0] = 0; // Interactive threadpool + jl_n_threads_per_pool[1] = 1; // Default threadpool } else { post_image_load_hooks(); } diff --git a/src/jltypes.c b/src/jltypes.c index 5dc50ff0ca4e6..a587552aaa011 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3024,6 +3024,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_typeofbottom_type->super->layout = jl_typeofbottom_type->layout; // the only abstract type with a layout 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; @@ -3661,6 +3662,8 @@ void jl_init_types(void) JL_GC_DISABLED XX(task); jl_value_t *listt = jl_new_struct(jl_uniontype_type, jl_task_type, jl_nothing_type); jl_svecset(jl_task_type->types, 0, listt); + const static uint32_t task_atomicfields[1] = {0x00001000}; // Set fields 13 as atomic + jl_task_type->name->atomicfields = task_atomicfields; tv = jl_svec2(tvar("A"), tvar("R")); jl_opaque_closure_type = (jl_unionall_t*)jl_new_datatype(jl_symbol("OpaqueClosure"), core, jl_function_type, tv, diff --git a/src/julia.h b/src/julia.h index e211f31c6512c..074c50fd0aa21 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2231,11 +2231,7 @@ typedef struct _jl_task_t { // current exception handler jl_handler_t *eh; // saved thread state - jl_ucontext_t ctx; - void *stkbuf; // malloc'd memory (either copybuf or stack) - size_t bufsz; // actual sizeof stkbuf - unsigned int copy_stack:31; // sizeof stack for copybuf - unsigned int started:1; + jl_ucontext_t ctx; // pointer into stkbuf, if suspended } jl_task_t; #define JL_TASK_STATE_RUNNABLE 0 diff --git a/src/julia_fasttls.h b/src/julia_fasttls.h index 1c0929717b293..1f35d3693fefd 100644 --- a/src/julia_fasttls.h +++ b/src/julia_fasttls.h @@ -22,14 +22,9 @@ extern "C" { typedef struct _jl_gcframe_t jl_gcframe_t; -#if defined(_OS_DARWIN_) -#include -typedef void *(jl_get_pgcstack_func)(pthread_key_t); // aka typeof(pthread_getspecific) -#else typedef jl_gcframe_t **(jl_get_pgcstack_func)(void); -#endif -#if !defined(_OS_DARWIN_) && !defined(_OS_WINDOWS_) +#if !defined(_OS_WINDOWS_) #define JULIA_DEFINE_FAST_TLS \ static __attribute__((tls_model("local-exec"))) __thread jl_gcframe_t **jl_pgcstack_localexec; \ JL_DLLEXPORT _Atomic(char) jl_pgcstack_static_semaphore; \ diff --git a/src/julia_internal.h b/src/julia_internal.h index d4d1a3239785c..8ea1940224e66 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -65,7 +65,8 @@ static inline void asan_unpoison_task_stack(jl_task_t *ct, jl_jmp_buf *buf) that we're resetting to. The idea is to remove the poison from the frames that we're skipping over, since they won't be unwound. */ uintptr_t top = jmpbuf_sp(buf); - uintptr_t bottom = (uintptr_t)ct->stkbuf; + uintptr_t bottom = (uintptr_t)(ct->ctx.copy_stack ? (char*)ct->ptls->stackbase - ct->ptls->stacksize : (char*)ct->ctx.stkbuf); + //uintptr_t bottom = (uintptr_t)⊤ __asan_unpoison_stack_memory(bottom, top - bottom); } static inline void asan_unpoison_stack_memory(uintptr_t addr, size_t size) { @@ -1005,9 +1006,7 @@ int jl_safepoint_consume_sigint(void); void jl_wake_libuv(void) JL_NOTSAFEPOINT; void jl_set_pgcstack(jl_gcframe_t **) JL_NOTSAFEPOINT; -#if defined(_OS_DARWIN_) -typedef pthread_key_t jl_pgcstack_key_t; -#elif defined(_OS_WINDOWS_) +#if defined(_OS_WINDOWS_) typedef DWORD jl_pgcstack_key_t; #else typedef jl_gcframe_t ***(*jl_pgcstack_key_t)(void) JL_NOTSAFEPOINT; @@ -1306,20 +1305,36 @@ JL_DLLEXPORT size_t jl_maxrss(void); // congruential random number generator // for a small amount of thread-local randomness -STATIC_INLINE uint64_t cong(uint64_t max, uint64_t *seed) JL_NOTSAFEPOINT +//TODO: utilize https://github.com/openssl/openssl/blob/master/crypto/rand/rand_uniform.c#L13-L99 +// for better performance, it does however require making users expect a 32bit random number. + +STATIC_INLINE uint64_t cong(uint64_t max, uint64_t *seed) JL_NOTSAFEPOINT // Open interval [0, max) { - if (max == 0) + if (max < 2) return 0; uint64_t mask = ~(uint64_t)0; - --max; - mask >>= __builtin_clzll(max|1); - uint64_t x; + int zeros = __builtin_clzll(max); + int bits = CHAR_BIT * sizeof(uint64_t) - zeros; + mask = mask >> zeros; do { - *seed = 69069 * (*seed) + 362437; - x = *seed & mask; - } while (x > max); - return x; + uint64_t value = 69069 * (*seed) + 362437; + *seed = value; + uint64_t x = value & mask; + if (x < max) { + return x; + } + int bits_left = zeros; + while (bits_left >= bits) { + value >>= bits; + x = value & mask; + if (x < max) { + return x; + } + bits_left -= bits; + } + } while (1); } + JL_DLLEXPORT uint64_t jl_rand(void) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_srand(uint64_t) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_init_rand(void); diff --git a/src/julia_threads.h b/src/julia_threads.h index 3486c5b969383..e56ff5edd6176 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -86,9 +86,13 @@ typedef ucontext_t _jl_ucontext_t; typedef struct { union { - _jl_ucontext_t ctx; - jl_stack_context_t copy_ctx; + _jl_ucontext_t *ctx; + jl_stack_context_t *copy_ctx; }; + void *stkbuf; // malloc'd memory (either copybuf or stack) + size_t bufsz; // actual sizeof stkbuf + unsigned int copy_stack:31; // sizeof stack for copybuf + unsigned int started:1; #if defined(_COMPILER_TSAN_ENABLED_) void *tsan_state; #endif @@ -155,13 +159,9 @@ typedef struct _jl_tls_states_t { struct _jl_task_t *previous_task; struct _jl_task_t *root_task; struct _jl_timing_block_t *timing_stack; + // This is the location of our copy_stack void *stackbase; size_t stacksize; - union { - _jl_ucontext_t base_ctx; // base context of stack - // This hack is needed to support always_copy_stacks: - jl_stack_context_t copy_stack_ctx; - }; // Temp storage for exception thrown in signal handler. Not rooted. struct _jl_value_t *sig_exception; // Temporary backtrace buffer. Scanned for gc roots when bt_size > 0. @@ -206,10 +206,7 @@ typedef struct _jl_tls_states_t { #endif } jl_tls_states_t; -#ifndef JL_LIBRARY_EXPORTS -// deprecated (only for external consumers) JL_DLLEXPORT void *jl_get_ptls_states(void); -#endif // Update codegen version in `ccall.cpp` after changing either `pause` or `wake` #ifdef __MIC__ diff --git a/src/llvm-demote-float16.cpp b/src/llvm-demote-float16.cpp index 5d0d9f5d37c40..7f1b076897fc8 100644 --- a/src/llvm-demote-float16.cpp +++ b/src/llvm-demote-float16.cpp @@ -49,37 +49,28 @@ extern JuliaOJIT *jl_ExecutionEngine; namespace { -static bool have_fp16(Function &caller, const Triple &TT) { - Attribute FSAttr = caller.getFnAttribute("target-features"); - StringRef FS = ""; - if (FSAttr.isValid()) - FS = FSAttr.getValueAsString(); - else if (jl_ExecutionEngine) - FS = jl_ExecutionEngine->getTargetFeatureString(); - // else probably called from opt, just do nothing - if (TT.isAArch64()) { - if (FS.find("+fp16fml") != llvm::StringRef::npos || FS.find("+fullfp16") != llvm::StringRef::npos){ - return true; - } - } else if (TT.getArch() == Triple::x86_64) { - if (FS.find("+avx512fp16") != llvm::StringRef::npos){ - return true; - } - } - if (caller.hasFnAttribute("julia.hasfp16")) { - return true; - } - return false; +static bool have_fp16(Function &F, const Triple &TT) { + // for testing purposes + Attribute Attr = F.getFnAttribute("julia.hasfp16"); + if (Attr.isValid()) + return Attr.getValueAsBool(); + + // llvm/llvm-project#97975: on some platforms, `half` uses excessive precision + if (TT.isPPC()) + return false; + + return true; } -static bool have_bf16(Function &caller, const Triple &TT) { - if (caller.hasFnAttribute("julia.hasbf16")) { - return true; - } +static bool have_bf16(Function &F, const Triple &TT) { + // for testing purposes + Attribute Attr = F.getFnAttribute("julia.hasbf16"); + if (Attr.isValid()) + return Attr.getValueAsBool(); - // there's no targets that fully support bfloat yet;, - // AVX512BF16 only provides conversion and dot product instructions. - return false; + // https://github.com/llvm/llvm-project/issues/97975#issuecomment-2218770199: + // on current versions of LLVM, bf16 always uses TypeSoftPromoteHalf + return true; } static bool demoteFloat16(Function &F) diff --git a/src/rtutils.c b/src/rtutils.c index 4a2e5c230883e..a60597827b92c 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -1403,6 +1403,7 @@ size_t jl_static_show_func_sig_(JL_STREAM *s, jl_value_t *type, jl_static_show_c return n; } if ((jl_nparams(ftype) == 0 || ftype == ((jl_datatype_t*)ftype)->name->wrapper) && + ((jl_datatype_t*)ftype)->name->mt && ((jl_datatype_t*)ftype)->name->mt != jl_type_type_mt && ((jl_datatype_t*)ftype)->name->mt != jl_nonfunction_mt) { n += jl_static_show_symbol(s, ((jl_datatype_t*)ftype)->name->mt->name); diff --git a/src/scheduler.c b/src/scheduler.c index 3505e935afcf6..bd7da13aa42e3 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -87,7 +87,7 @@ extern int jl_gc_mark_queue_obj_explicit(jl_gc_mark_cache_t *gc_cache, // parallel task runtime // --- -JL_DLLEXPORT uint32_t jl_rand_ptls(uint32_t max) +JL_DLLEXPORT uint32_t jl_rand_ptls(uint32_t max) // [0, n) { jl_ptls_t ptls = jl_current_task->ptls; return cong(max, &ptls->rngseed); @@ -199,6 +199,21 @@ static int sleep_check_after_threshold(uint64_t *start_cycles) JL_NOTSAFEPOINT return 0; } +void surprise_wakeup(jl_ptls_t ptls) JL_NOTSAFEPOINT +{ + // equivalent to wake_thread, without the assert on wasrunning + int8_t state = jl_atomic_load_relaxed(&ptls->sleep_check_state); + if (state == sleeping) { + if (jl_atomic_cmpswap_relaxed(&ptls->sleep_check_state, &state, not_sleeping)) { + // this notification will never be consumed, so we may have now + // introduced some inaccuracy into the count, but that is + // unavoidable with any asynchronous interruption + jl_atomic_fetch_add_relaxed(&n_threads_running, 1); + } + } +} + + static int set_not_sleeping(jl_ptls_t ptls) JL_NOTSAFEPOINT { if (jl_atomic_load_relaxed(&ptls->sleep_check_state) != not_sleeping) { @@ -238,10 +253,7 @@ static void wake_libuv(void) JL_NOTSAFEPOINT JULIA_DEBUG_SLEEPWAKE( io_wakeup_leave = cycleclock() ); } -/* ensure thread tid is awake if necessary */ -JL_DLLEXPORT void jl_wakeup_thread(int16_t tid) JL_NOTSAFEPOINT -{ - jl_task_t *ct = jl_current_task; +void wakeup_thread(jl_task_t *ct, int16_t tid) JL_NOTSAFEPOINT { // Pass in ptls when we have it already available to save a lookup int16_t self = jl_atomic_load_relaxed(&ct->tid); if (tid != self) jl_fence(); // [^store_buffering_1] @@ -296,6 +308,12 @@ JL_DLLEXPORT void jl_wakeup_thread(int16_t tid) JL_NOTSAFEPOINT JULIA_DEBUG_SLEEPWAKE( wakeup_leave = cycleclock() ); } +/* ensure thread tid is awake if necessary */ +JL_DLLEXPORT void jl_wakeup_thread(int16_t tid) JL_NOTSAFEPOINT +{ + jl_task_t *ct = jl_current_task; + wakeup_thread(ct, tid); +} // get the next runnable task static jl_task_t *get_next_task(jl_value_t *trypoptask, jl_value_t *q) @@ -432,7 +450,7 @@ JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q, // responsibility, so need to make sure thread 0 will take care // of us. if (jl_atomic_load_relaxed(&jl_uv_mutex.owner) == NULL) // aka trylock - jl_wakeup_thread(0); + wakeup_thread(ct, 0); } if (uvlock) { int enter_eventloop = may_sleep(ptls); @@ -560,7 +578,7 @@ void scheduler_delete_thread(jl_ptls_t ptls) JL_NOTSAFEPOINT else { jl_atomic_fetch_add_relaxed(&n_threads_running, 1); } - jl_wakeup_thread(0); // force thread 0 to see that we do not have the IO lock (and am dead) + wakeup_thread(jl_atomic_load_relaxed(&ptls->current_task), 0); // force thread 0 to see that we do not have the IO lock (and am dead) jl_atomic_fetch_add_relaxed(&n_threads_running, -1); } diff --git a/src/signal-handling.c b/src/signal-handling.c index febf05b653662..d7f4697a3c4f0 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -155,7 +155,7 @@ static void jl_shuffle_int_array_inplace(int *carray, int size, uint64_t *seed) // The "modern Fisher–Yates shuffle" - O(n) algorithm // https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm for (int i = size; i-- > 1; ) { - size_t j = cong(i, seed); + size_t j = cong(i + 1, seed); // cong is an open interval so we add 1 uint64_t tmp = carray[j]; carray[j] = carray[i]; carray[i] = tmp; @@ -427,6 +427,8 @@ void jl_show_sigill(void *_ctx) #endif } +void surprise_wakeup(jl_ptls_t ptls) JL_NOTSAFEPOINT; + // make it invalid for a task to return from this point to its stack // this is generally quite an foolish operation, but does free you up to do // arbitrary things on this stack now without worrying about corrupt state that @@ -439,15 +441,17 @@ void jl_task_frame_noreturn(jl_task_t *ct) JL_NOTSAFEPOINT ct->eh = NULL; ct->world_age = 1; // Force all locks to drop. Is this a good idea? Of course not. But the alternative would probably deadlock instead of crashing. - small_arraylist_t *locks = &ct->ptls->locks; + jl_ptls_t ptls = ct->ptls; + small_arraylist_t *locks = &ptls->locks; for (size_t i = locks->len; i > 0; i--) jl_mutex_unlock_nogc((jl_mutex_t*)locks->items[i - 1]); locks->len = 0; - ct->ptls->in_pure_callback = 0; - ct->ptls->in_finalizer = 0; - ct->ptls->defer_signal = 0; + ptls->in_pure_callback = 0; + ptls->in_finalizer = 0; + ptls->defer_signal = 0; // forcibly exit GC (if we were in it) or safe into unsafe, without the mandatory safepoint - jl_atomic_store_release(&ct->ptls->gc_state, JL_GC_STATE_UNSAFE); + jl_atomic_store_release(&ptls->gc_state, JL_GC_STATE_UNSAFE); + surprise_wakeup(ptls); // allow continuing to use a Task that should have already died--unsafe necromancy! jl_atomic_store_relaxed(&ct->_state, JL_TASK_STATE_RUNNABLE); } @@ -461,6 +465,7 @@ void jl_critical_error(int sig, int si_code, bt_context_t *context, jl_task_t *c size_t i, n = ct ? *bt_size : 0; if (sig) { // kill this task, so that we cannot get back to it accidentally (via an untimely ^C or jlbacktrace in jl_exit) + // and also resets the state of ct and ptls so that some code can run on this task again jl_task_frame_noreturn(ct); #ifndef _OS_WINDOWS_ sigset_t sset; diff --git a/src/signals-unix.c b/src/signals-unix.c index 5e79fd5a2e29e..005422bea03d3 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -230,13 +230,13 @@ static pthread_t signals_thread; static int is_addr_on_stack(jl_task_t *ct, void *addr) { - if (ct->copy_stack) { + if (ct->ctx.copy_stack) { jl_ptls_t ptls = ct->ptls; return ((char*)addr > (char*)ptls->stackbase - ptls->stacksize && (char*)addr < (char*)ptls->stackbase); } - return ((char*)addr > (char*)ct->stkbuf && - (char*)addr < (char*)ct->stkbuf + ct->bufsz); + return ((char*)addr > (char*)ct->ctx.stkbuf && + (char*)addr < (char*)ct->ctx.stkbuf + ct->ctx.bufsz); } static void sigdie_handler(int sig, siginfo_t *info, void *context) @@ -314,6 +314,8 @@ int exc_reg_is_write_fault(uintptr_t esr) { #if defined(HAVE_MACH) #include "signals-mach.c" #else +#include +#include int jl_lock_stackwalk(void) { @@ -439,17 +441,13 @@ JL_NO_ASAN static void segv_handler(int sig, siginfo_t *info, void *context) } } -#if !defined(JL_DISABLE_LIBUNWIND) -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; +pthread_mutex_t in_signal_lock; // shared with jl_delete_thread +static bt_context_t *signal_context; // protected by in_signal_lock +static int exit_signal_cond = -1; +static int signal_caught_cond = -1; int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) { - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += timeout; pthread_mutex_lock(&in_signal_lock); jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; jl_task_t *ct2 = ptls2 ? jl_atomic_load_relaxed(&ptls2->current_task) : NULL; @@ -458,48 +456,51 @@ int jl_thread_suspend_and_get_state(int tid, int timeout, bt_context_t *ctx) pthread_mutex_unlock(&in_signal_lock); return 0; } - jl_atomic_store_release(&ptls2->signal_request, 1); - pthread_kill(ptls2->system_id, SIGUSR2); - // wait for thread to acknowledge - int err = pthread_cond_timedwait(&signal_caught_cond, &in_signal_lock, &ts); - if (err == ETIMEDOUT) { - sig_atomic_t request = 1; + sig_atomic_t request = 0; + if (!jl_atomic_cmpswap(&ptls2->signal_request, &request, 1)) { + // something is wrong, or there is already a usr2 in flight elsewhere + pthread_mutex_unlock(&in_signal_lock); + return 0; + } + request = 1; + int err = pthread_kill(ptls2->system_id, SIGUSR2); + // wait for thread to acknowledge or timeout + struct pollfd event = {signal_caught_cond, POLLIN, 0}; + if (err == 0) { + do { + err = poll(&event, 1, timeout * 1000); + } while (err == -1 && errno == EINTR); + } + if ((event.revents & POLLIN) == 0) { + // not ready after timeout: try to cancel this request if (jl_atomic_cmpswap(&ptls2->signal_request, &request, 0)) { pthread_mutex_unlock(&in_signal_lock); return 0; } - // Request is either now 0 (meaning the other thread is waiting for - // exit_signal_cond already), - // Or it is now -1 (meaning the other thread - // is waiting for in_signal_lock, and we need to release that lock - // here for a bit, until the other thread has a chance to get to the - // exit_signal_cond) - if (request == -1) { - err = pthread_cond_wait(&signal_caught_cond, &in_signal_lock); - assert(!err); - } } + eventfd_t got; + do { + err = read(signal_caught_cond, &got, sizeof(eventfd_t)); + } while (err == -1 && errno == EINTR); + if (err != sizeof(eventfd_t)) abort(); + assert(got == 1); (void) got; // Now the other thread is waiting on 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); + request = jl_atomic_load_acquire(&ptls2->signal_request); assert(request == 0); (void) request; - jl_atomic_store_release(&ptls2->signal_request, 1); // prepare to resume normally + jl_atomic_store_release(&ptls2->signal_request, 4); // prepare to resume normally, but later code may change this *ctx = *signal_context; return 1; } void jl_thread_resume(int tid) { - jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; - pthread_cond_broadcast(&exit_signal_cond); - 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); - assert(request == 0); (void) request; + int err; + eventfd_t got = 1; + err = write(exit_signal_cond, &got, sizeof(eventfd_t)); + if (err != sizeof(eventfd_t)) abort(); pthread_mutex_unlock(&in_signal_lock); } -#endif // Throw jl_interrupt_exception if the master thread is in a signal async region // or if SIGINT happens too often. @@ -508,9 +509,11 @@ static void jl_try_deliver_sigint(void) jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[0]; jl_safepoint_enable_sigint(); jl_wake_libuv(); + pthread_mutex_lock(&in_signal_lock); jl_atomic_store_release(&ptls2->signal_request, 2); // This also makes sure `sleep` is aborted. pthread_kill(ptls2->system_id, SIGUSR2); + pthread_mutex_unlock(&in_signal_lock); } // Write only by signal handling thread, read only by main thread @@ -543,12 +546,12 @@ static void jl_exit_thread0(int signo, jl_bt_element_t *bt_data, size_t bt_size) } // request: -// -1: beginning processing [invalid outside here] // 0: nothing [not from here] -// 1: get state +// 1: get state & wait for request // 2: throw sigint if `!defer_signal && io_wait` or if force throw threshold // is reached // 3: raise `thread0_exit_signo` and try to exit +// 4: no-op void usr2_handler(int sig, siginfo_t *info, void *ctx) { jl_task_t *ct = jl_get_current_task(); @@ -559,25 +562,21 @@ void usr2_handler(int sig, siginfo_t *info, void *ctx) return; int errno_save = errno; // acknowledge that we saw the signal_request - sig_atomic_t request = jl_atomic_exchange(&ptls->signal_request, -1); -#if !defined(JL_DISABLE_LIBUNWIND) + sig_atomic_t request = jl_atomic_exchange(&ptls->signal_request, 0); if (request == 1) { - pthread_mutex_lock(&in_signal_lock); signal_context = jl_to_bt_context(ctx); - // acknowledge that we set the signal_caught_cond broadcast + int err; + eventfd_t got = 1; + err = write(signal_caught_cond, &got, sizeof(eventfd_t)); + if (err != sizeof(eventfd_t)) abort(); + do { + err = read(exit_signal_cond, &got, sizeof(eventfd_t)); + } while (err == -1 && errno == EINTR); + if (err != sizeof(eventfd_t)) abort(); + assert(got == 1); request = jl_atomic_exchange(&ptls->signal_request, 0); - assert(request == -1); (void) request; - pthread_cond_broadcast(&signal_caught_cond); - pthread_cond_wait(&exit_signal_cond, &in_signal_lock); - request = jl_atomic_exchange(&ptls->signal_request, 0); - assert(request == 1 || request == 3); - // acknowledge that we got the resume signal - pthread_cond_broadcast(&signal_caught_cond); - pthread_mutex_unlock(&in_signal_lock); + assert(request == 2 || request == 3 || request == 4); } - else -#endif - jl_atomic_exchange(&ptls->signal_request, 0); // returns -1 if (request == 2) { int force = jl_check_force_sigint(); if (force || (!ptls->defer_signal && ptls->io_wait)) { @@ -1038,10 +1037,12 @@ void restore_signals(void) jl_sigsetset(&sset); pthread_sigmask(SIG_SETMASK, &sset, 0); -#if !defined(HAVE_MACH) && !defined(JL_DISABLE_LIBUNWIND) +#if !defined(HAVE_MACH) + exit_signal_cond = eventfd(0, EFD_CLOEXEC); + signal_caught_cond = eventfd(0, EFD_CLOEXEC); if (pthread_mutex_init(&in_signal_lock, NULL) != 0 || - pthread_cond_init(&exit_signal_cond, NULL) != 0 || - pthread_cond_init(&signal_caught_cond, NULL) != 0) { + exit_signal_cond == -1 || + signal_caught_cond == -1) { jl_error("SIGUSR pthread init failed"); } #endif diff --git a/src/stackwalk.c b/src/stackwalk.c index a63694e7c3b0c..15a9fddeac9a4 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -83,7 +83,7 @@ static int jl_unw_stepn(bt_cursor_t *cursor, jl_bt_element_t *bt_data, size_t *b skip--; } #endif -#if !defined(_OS_WINDOWS_) +#if !defined(_OS_WINDOWS_) // no point on windows, since RtlVirtualUnwind won't give us a second chance if the segfault happens in ntdll jl_jmp_buf *old_buf = jl_get_safe_restore(); jl_jmp_buf buf; jl_set_safe_restore(&buf); @@ -921,14 +921,12 @@ _os_ptr_munge(uintptr_t ptr) JL_NOTSAFEPOINT extern bt_context_t *jl_to_bt_context(void *sigctx); -static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT +JL_DLLEXPORT size_t jl_record_backtrace(jl_task_t *t, jl_bt_element_t *bt_data, size_t max_bt_size) JL_NOTSAFEPOINT { jl_task_t *ct = jl_current_task; jl_ptls_t ptls = ct->ptls; - ptls->bt_size = 0; if (t == ct) { - ptls->bt_size = rec_backtrace(ptls->bt_data, JL_MAX_BT_SIZE, 0); - return; + return rec_backtrace(bt_data, max_bt_size, 0); } bt_context_t *context = NULL; bt_context_t c; @@ -936,9 +934,11 @@ static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT 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)) { + if (!jl_thread_suspend_and_get_state(old, 1, &c)) { jl_unlock_stackwalk(lockret); - return; + if (jl_atomic_load_relaxed(&t->tid) != old) + continue; + return 0; } jl_unlock_stackwalk(lockret); if (jl_atomic_load_relaxed(&t->tid) == old) { @@ -953,11 +953,11 @@ static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT // got the wrong thread stopped, try again jl_thread_resume(old); } - if (context == NULL && (!t->copy_stack && t->started && t->stkbuf != NULL)) { + if (context == NULL && (!t->ctx.copy_stack && t->ctx.started && t->ctx.ctx != 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; + _JUMP_BUFFER *mctx = (_JUMP_BUFFER*)&t->ctx.ctx->uc_mcontext; #if defined(_CPU_X86_64_) c.Rbx = mctx->Rbx; c.Rsp = mctx->Rsp; @@ -979,13 +979,13 @@ static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT #endif 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) memset(&c, 0, sizeof(c)); #if defined(_OS_LINUX_) && defined(__GLIBC__) - __jmp_buf *mctx = &t->ctx.ctx.uc_mcontext->__jmpbuf; + __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 @@ -1071,13 +1071,13 @@ static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT mc->pc = mc->regs[30]; context = &c; #else - #pragma message("jl_rec_backtrace not defined for ASM/SETJMP on unknown linux") + #pragma message("jl_record_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; + 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; @@ -1133,12 +1133,12 @@ static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT 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") + #pragma message("jl_record_backtrace not defined for ASM/SETJMP on unknown darwin") (void)mctx; (void)c; #endif #elif defined(_OS_FREEBSD_) - sigjmp_buf *mctx = &t->ctx.ctx.uc_mcontext; + sigjmp_buf *mctx = &t->ctx.ctx->uc_mcontext; mcontext_t *mc = &c.uc_mcontext; #if defined(_CPU_X86_64_) // https://github.com/freebsd/freebsd-src/blob/releng/13.1/lib/libc/amd64/gen/_setjmp.S @@ -1175,24 +1175,26 @@ static void jl_rec_backtrace(jl_task_t *t) JL_NOTSAFEPOINT mc->mc_fpregs.fp_q[14] = ((long*)mctx)[20]; context = &c; #else - #pragma message("jl_rec_backtrace not defined for ASM/SETJMP on unknown freebsd") + #pragma message("jl_record_backtrace not defined for ASM/SETJMP on unknown freebsd") (void)mctx; (void)c; #endif #else - #pragma message("jl_rec_backtrace not defined for ASM/SETJMP on unknown system") + #pragma message("jl_record_backtrace not defined for ASM/SETJMP on unknown system") (void)c; #endif #else - #pragma message("jl_rec_backtrace not defined for unknown task system") + #pragma message("jl_record_backtrace not defined for unknown task system") #endif } + size_t bt_size = 0; if (context) - ptls->bt_size = rec_backtrace_ctx(ptls->bt_data, JL_MAX_BT_SIZE, context, t->gcstack); + bt_size = rec_backtrace_ctx(bt_data, 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); + return bt_size; } //-------------------------------------------------- @@ -1224,12 +1226,15 @@ JL_DLLEXPORT void jlbacktracet(jl_task_t *t) JL_NOTSAFEPOINT { jl_task_t *ct = jl_current_task; jl_ptls_t ptls = ct->ptls; - jl_rec_backtrace(t); - size_t i, bt_size = ptls->bt_size; + ptls->bt_size = 0; jl_bt_element_t *bt_data = ptls->bt_data; + size_t bt_size = jl_record_backtrace(t, bt_data, JL_MAX_BT_SIZE); + size_t i; for (i = 0; i < bt_size; i += jl_bt_entry_size(bt_data + i)) { jl_print_bt_entry_codeloc(bt_data + i); } + if (bt_size == 0) + jl_safe_printf(" no backtrace recorded\n"); } JL_DLLEXPORT void jl_print_backtrace(void) JL_NOTSAFEPOINT @@ -1269,14 +1274,9 @@ JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT 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, + t->sticky, t->ctx.started, t_state, jl_atomic_load_relaxed(&t->tid) + 1); - if (t->stkbuf != NULL) { - jlbacktracet(t); - } - else { - jl_safe_printf(" no stack\n"); - } + jlbacktracet(t); } jl_safe_printf(" ---- End root task\n"); } @@ -1291,12 +1291,9 @@ JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT 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, + t->sticky, t->ctx.started, t_state, jl_atomic_load_relaxed(&t->tid) + 1); - if (t->stkbuf != NULL) - jlbacktracet(t); - else - jl_safe_printf(" no stack\n"); + jlbacktracet(t); jl_safe_printf(" ---- End task %zu\n", j + 1); } jl_safe_printf("==== End thread %d\n", ptls2->tid + 1); diff --git a/src/task.c b/src/task.c index 1f41ebd8cd2f8..86acac23a186a 100644 --- a/src/task.c +++ b/src/task.c @@ -49,27 +49,27 @@ extern "C" { // c.f. interceptor in jl_dlopen as well void (*real_siglongjmp)(jmp_buf _Buf, int _Value) = NULL; #endif -static inline void sanitizer_start_switch_fiber(jl_ptls_t ptls, jl_task_t *from, jl_task_t *to) { +static inline void sanitizer_start_switch_fiber(jl_ptls_t ptls, jl_ucontext_t *from, jl_ucontext_t *to) { if (to->copy_stack) - __sanitizer_start_switch_fiber(&from->ctx.asan_fake_stack, (char*)ptls->stackbase-ptls->stacksize, ptls->stacksize); + __sanitizer_start_switch_fiber(&from->asan_fake_stack, (char*)ptls->stackbase - ptls->stacksize, ptls->stacksize); else - __sanitizer_start_switch_fiber(&from->ctx.asan_fake_stack, to->stkbuf, to->bufsz); + __sanitizer_start_switch_fiber(&from->asan_fake_stack, to->stkbuf, to->bufsz); } -static inline void sanitizer_start_switch_fiber_killed(jl_ptls_t ptls, jl_task_t *to) { +static inline void sanitizer_start_switch_fiber_killed(jl_ptls_t ptls, jl_ucontext_t *to) { if (to->copy_stack) - __sanitizer_start_switch_fiber(NULL, (char*)ptls->stackbase-ptls->stacksize, ptls->stacksize); + __sanitizer_start_switch_fiber(NULL, (char*)ptls->stackbase - ptls->stacksize, ptls->stacksize); else __sanitizer_start_switch_fiber(NULL, to->stkbuf, to->bufsz); } -static inline void sanitizer_finish_switch_fiber(jl_task_t *last, jl_task_t *current) { - __sanitizer_finish_switch_fiber(current->ctx.asan_fake_stack, NULL, NULL); +static inline void sanitizer_finish_switch_fiber(jl_ucontext_t *last, jl_ucontext_t *current) { + __sanitizer_finish_switch_fiber(current->asan_fake_stack, NULL, NULL); //(const void**)&last->stkbuf, //&last->bufsz); } #else -static inline void sanitizer_start_switch_fiber(jl_ptls_t ptls, jl_task_t *from, jl_task_t *to) JL_NOTSAFEPOINT {} -static inline void sanitizer_start_switch_fiber_killed(jl_ptls_t ptls, jl_task_t *to) JL_NOTSAFEPOINT {} -static inline void sanitizer_finish_switch_fiber(jl_task_t *last, jl_task_t *current) JL_NOTSAFEPOINT {} +static inline void sanitizer_start_switch_fiber(jl_ptls_t ptls, jl_ucontext_t *from, jl_ucontext_t *to) JL_NOTSAFEPOINT {} +static inline void sanitizer_start_switch_fiber_killed(jl_ptls_t ptls, jl_ucontext_t *to) JL_NOTSAFEPOINT {} +static inline void sanitizer_finish_switch_fiber(jl_ucontext_t *last, jl_ucontext_t *current) JL_NOTSAFEPOINT {} #endif #if defined(_COMPILER_TSAN_ENABLED_) @@ -85,19 +85,6 @@ static inline void sanitizer_finish_switch_fiber(jl_task_t *last, jl_task_t *cur jl_ucontext_t *_tsan_macro_ctx = (_ctx); \ __tsan_switch_to_fiber(_tsan_macro_ctx->tsan_state, 0); \ } while (0) -#ifdef COPY_STACKS -#define tsan_destroy_copyctx(_ptls, _ctx) do { \ - jl_ucontext_t *_tsan_macro_ctx = (_ctx); \ - if (_tsan_macro_ctx != &(_ptls)->root_task->ctx) { \ - __tsan_destroy_fiber(_tsan_macro_ctx->tsan_state); \ - } \ - _tsan_macro_ctx->tsan_state = NULL; \ - } while (0) -#define tsan_switch_to_copyctx(_ctx) do { \ - struct jl_stack_context_t *_tsan_macro_ctx = (_ctx); \ - __tsan_switch_to_fiber(_tsan_macro_ctx->tsan_state, 0); \ - } while (0) -#endif #else // just do minimal type-checking on the arguments #define tsan_destroy_ctx(_ptls, _ctx) do { \ @@ -108,16 +95,6 @@ static inline void sanitizer_finish_switch_fiber(jl_task_t *last, jl_task_t *cur jl_ucontext_t *_tsan_macro_ctx = (_ctx); \ (void)_tsan_macro_ctx; \ } while (0) -#ifdef COPY_STACKS -#define tsan_destroy_copyctx(_ptls, _ctx) do { \ - jl_ucontext_t *_tsan_macro_ctx = (_ctx); \ - (void)_tsan_macro_ctx; \ - } while (0) -#define tsan_switch_to_copyctx(_ctx) do { \ - jl_ucontext_t *_tsan_macro_ctx = (_ctx); \ - (void)_tsan_macro_ctx; \ - } while (0) -#endif #endif // empirically, jl_finish_task needs about 64k stack space to infer/run @@ -134,7 +111,6 @@ static inline void sanitizer_finish_switch_fiber(jl_task_t *last, jl_task_t *cur #define ROOT_TASK_STACK_ADJUSTMENT 3000000 #endif -static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner) JL_NOTSAFEPOINT; static void jl_set_fiber(jl_ucontext_t *t); static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t); static void jl_start_fiber_swap(jl_ucontext_t *savet, jl_ucontext_t *t); @@ -214,17 +190,17 @@ static void NOINLINE save_stack(jl_ptls_t ptls, jl_task_t *lastt, jl_task_t **pt assert(stackbase > frame_addr); size_t nb = stackbase - frame_addr; void *buf; - if (lastt->bufsz < nb) { - asan_free_copy_stack(lastt->stkbuf, lastt->bufsz); + if (lastt->ctx.bufsz < nb) { + asan_free_copy_stack(lastt->ctx.stkbuf, lastt->ctx.bufsz); buf = (void*)jl_gc_alloc_buf(ptls, nb); - lastt->stkbuf = buf; - lastt->bufsz = nb; + lastt->ctx.stkbuf = buf; + lastt->ctx.bufsz = nb; } else { - buf = lastt->stkbuf; + buf = lastt->ctx.stkbuf; } *pt = NULL; // clear the gc-root for the target task before copying the stack for saving - lastt->copy_stack = nb; + lastt->ctx.copy_stack = nb; lastt->sticky = 1; memcpy_stack_a16((uint64_t*)buf, (uint64_t*)frame_addr, nb); // this task's stack could have been modified after @@ -233,58 +209,101 @@ static void NOINLINE save_stack(jl_ptls_t ptls, jl_task_t *lastt, jl_task_t **pt jl_gc_wb_back(lastt); } -JL_NO_ASAN static void NOINLINE JL_NORETURN restore_stack(jl_task_t *t, jl_ptls_t ptls, char *p) +JL_NO_ASAN static void NOINLINE JL_NORETURN restore_stack(jl_ucontext_t *t, jl_ptls_t ptls, char *p) { size_t nb = t->copy_stack; char *_x = (char*)ptls->stackbase - nb; if (!p) { // switch to a stackframe that's beyond the bounds of the last switch - p = _x; - if ((char*)&_x > _x) { - p = (char*)alloca((char*)&_x - _x); + p = _x - 4096; + if ((char*)&_x > p) { + p = (char*)alloca((char*)&_x - p); } restore_stack(t, ptls, p); // pass p to ensure the compiler can't tailcall this or avoid the alloca } void *_y = t->stkbuf; assert(_x != NULL && _y != NULL); +#if defined(_OS_WINDOWS_) // this platform does not implement CFI_NORETURN correctly or at all in libunwind (or equivalent) which requires a workaround +#if defined(_CPU_X86_) || defined(_CPU_X86_64_) + void *volatile *return_address = (void *volatile *)__builtin_frame_address(0) + 1; + assert(*return_address == __builtin_return_address(0)); + *return_address = NULL; +#else +#pragma message("warning: CFI_NORETURN not implemented for this platform, so profiling of copy_stacks may segfault in this build") +#endif +#else +CFI_NORETURN +#endif memcpy_stack_a16((uint64_t*)_x, (uint64_t*)_y, nb); // destroys all but the current stackframe #if defined(_OS_WINDOWS_) - jl_setcontext(&t->ctx.copy_ctx); + jl_setcontext(t->copy_ctx); #else - jl_longjmp(t->ctx.copy_ctx.uc_mcontext, 1); + jl_longjmp(t->copy_ctx->uc_mcontext, 1); #endif abort(); // unreachable } -JL_NO_ASAN static void restore_stack2(jl_task_t *t, jl_ptls_t ptls, jl_task_t *lastt) +JL_NO_ASAN static void restore_stack2(jl_ucontext_t *t, jl_ptls_t ptls, jl_ucontext_t *lastt) { assert(t->copy_stack && !lastt->copy_stack); size_t nb = t->copy_stack; - char *_x = (char*)ptls->stackbase - nb; - void *_y = t->stkbuf; - assert(_x != NULL && _y != NULL); - memcpy_stack_a16((uint64_t*)_x, (uint64_t*)_y, nb); // destroys all but the current stackframe + if (nb > 1) { + char *_x = (char*)ptls->stackbase - nb; + void *_y = t->stkbuf; + assert(_x != NULL && _y != NULL); + memcpy_stack_a16((uint64_t*)_x, (uint64_t*)_y, nb); + } +#if defined(_OS_WINDOWS_) + // jl_swapcontext and setjmp are the same on Windows, so we can just use swapcontext directly + tsan_switch_to_ctx(t); + jl_swapcontext(lastt->ctx, t->copy_ctx); +#else #if defined(JL_HAVE_UNW_CONTEXT) volatile int returns = 0; - int r = unw_getcontext(&lastt->ctx.ctx); + int r = unw_getcontext(lastt->ctx); if (++returns == 2) // r is garbage after the first return return; if (r != 0 || returns != 1) abort(); -#elif defined(JL_HAVE_ASM) || defined(_OS_WINDOWS_) - if (jl_setjmp(lastt->ctx.copy_ctx.uc_mcontext, 0)) +#elif defined(JL_HAVE_ASM) + if (jl_setjmp(lastt->ctx->uc_mcontext, 0)) return; #else #error COPY_STACKS is incompatible with this platform #endif - tsan_switch_to_copyctx(&t->ctx); -#if defined(_OS_WINDOWS_) - jl_setcontext(&t->ctx.copy_ctx); + tsan_switch_to_ctx(t); + jl_longjmp(t->copy_ctx->uc_mcontext, 1); +#endif +} + +JL_NO_ASAN static void NOINLINE restore_stack3(jl_ucontext_t *t, jl_ptls_t ptls, char *p) +{ +#if !defined(JL_HAVE_ASM) + char *_x = (char*)ptls->stackbase; + if (!p) { + // switch to a stackframe that's well beyond the bounds of the next switch + p = _x - 4096; + if ((char*)&_x > p) { + p = (char*)alloca((char*)&_x - p); + } + restore_stack3(t, ptls, p); // pass p to ensure the compiler can't tailcall this or avoid the alloca + } +#endif +#if defined(_OS_WINDOWS_) // this platform does not implement CFI_NORETURN correctly or at all in libunwind (or equivalent) which requires a workaround +#if defined(_CPU_X86_) || defined(_CPU_X86_64_) + void *volatile *return_address = (void *volatile *)__builtin_frame_address(0) + 1; + assert(*return_address == __builtin_return_address(0)); + *return_address = NULL; +#endif #else - jl_longjmp(t->ctx.copy_ctx.uc_mcontext, 1); +CFI_NORETURN #endif + tsan_switch_to_ctx(t); + jl_start_fiber_set(t); // (doesn't return) + abort(); } + #endif /* Rooted by the base module */ @@ -298,9 +317,9 @@ void JL_NORETURN jl_finish_task(jl_task_t *ct) jl_atomic_store_release(&ct->_state, JL_TASK_STATE_FAILED); else jl_atomic_store_release(&ct->_state, JL_TASK_STATE_DONE); - if (ct->copy_stack) { // early free of stkbuf - asan_free_copy_stack(ct->stkbuf, ct->bufsz); - ct->stkbuf = NULL; + if (ct->ctx.copy_stack) { // early free of stkbuf + asan_free_copy_stack(ct->ctx.stkbuf, ct->ctx.bufsz); + ct->ctx.stkbuf = NULL; } // ensure that state is cleared ct->ptls->in_finalizer = 0; @@ -344,33 +363,33 @@ JL_DLLEXPORT void *jl_task_stack_buffer(jl_task_t *task, size_t *size, int *ptid if (ptls2) { *ptid = jl_atomic_load_relaxed(&task->tid); #ifdef COPY_STACKS - if (task->copy_stack) { + if (task->ctx.copy_stack) { *size = ptls2->stacksize; return (char *)ptls2->stackbase - *size; } #endif } - *size = task->bufsz - off; - return (void *)((char *)task->stkbuf + off); + *size = task->ctx.bufsz - off; + return (void *)((char *)task->ctx.stkbuf + off); } JL_DLLEXPORT void jl_active_task_stack(jl_task_t *task, char **active_start, char **active_end, char **total_start, char **total_end) { - if (!task->started) { + if (!task->ctx.started) { *total_start = *active_start = 0; *total_end = *active_end = 0; return; } jl_ptls_t ptls2 = task->ptls; - if (task->copy_stack && ptls2) { + if (task->ctx.copy_stack && ptls2) { *total_start = *active_start = (char*)ptls2->stackbase - ptls2->stacksize; *total_end = *active_end = (char*)ptls2->stackbase; } - else if (task->stkbuf) { - *total_start = *active_start = (char*)task->stkbuf; + else if (task->ctx.stkbuf) { + *total_start = *active_start = (char*)task->ctx.stkbuf; #ifndef _OS_WINDOWS_ jl_ptls_t ptls0 = jl_atomic_load_relaxed(&jl_all_tls_states)[0]; if (ptls0->root_task == task) { @@ -383,12 +402,12 @@ JL_DLLEXPORT void jl_active_task_stack(jl_task_t *task, } #endif - *total_end = *active_end = (char*)task->stkbuf + task->bufsz; + *total_end = *active_end = (char*)task->ctx.stkbuf + task->ctx.bufsz; #ifdef COPY_STACKS // save_stack stores the stack of an inactive task in stkbuf, and the // actual number of used bytes in copy_stack. - if (task->copy_stack > 1) - *active_end = (char*)task->stkbuf + task->copy_stack; + if (task->ctx.copy_stack > 1) + *active_end = (char*)task->ctx.stkbuf + task->ctx.copy_stack; #endif } else { @@ -449,20 +468,16 @@ JL_NO_ASAN static void ctx_switch(jl_task_t *lastt) #endif int killed = jl_atomic_load_relaxed(&lastt->_state) != JL_TASK_STATE_RUNNABLE; - if (!t->started && !t->copy_stack) { + if (!t->ctx.started && !t->ctx.copy_stack) { // may need to allocate the stack - if (t->stkbuf == NULL) { - t->stkbuf = jl_alloc_fiber(&t->ctx.ctx, &t->bufsz, t); - if (t->stkbuf == NULL) { + if (t->ctx.stkbuf == NULL) { + t->ctx.stkbuf = jl_malloc_stack(&t->ctx.bufsz, t); + if (t->ctx.stkbuf == NULL) { #ifdef COPY_STACKS // fall back to stack copying if mmap fails - t->copy_stack = 1; + t->ctx.copy_stack = 1; + t->ctx.bufsz = 0; t->sticky = 1; - t->bufsz = 0; - if (always_copy_stacks) - memcpy(&t->ctx.copy_ctx, &ptls->copy_stack_ctx, sizeof(t->ctx.copy_ctx)); - else - memcpy(&t->ctx.ctx, &ptls->base_ctx, sizeof(t->ctx.ctx)); #else jl_throw(jl_memory_exception); #endif @@ -470,28 +485,45 @@ JL_NO_ASAN static void ctx_switch(jl_task_t *lastt) } } + union { + _jl_ucontext_t ctx; + jl_stack_context_t copy_ctx; + } lasttstate; + if (killed) { *pt = NULL; // can't fail after here: clear the gc-root for the target task now lastt->gcstack = NULL; lastt->eh = NULL; - if (!lastt->copy_stack && lastt->stkbuf) { + if (!lastt->ctx.copy_stack && lastt->ctx.stkbuf) { // early free of stkbuf back to the pool jl_release_task_stack(ptls, lastt); } } else { + if (lastt->ctx.copy_stack) { // save the old copy-stack +#ifdef _OS_WINDOWS_ + lasttstate.copy_ctx.uc_stack.ss_sp = (char*)ptls->stackbase - ptls->stacksize; + lasttstate.copy_ctx.uc_stack.ss_size = ptls->stacksize; +#endif #ifdef COPY_STACKS - if (lastt->copy_stack) { // save the old copy-stack - save_stack(ptls, lastt, pt); // allocates (gc-safepoint, and can also fail) - if (jl_setjmp(lastt->ctx.copy_ctx.uc_mcontext, 0)) { - sanitizer_finish_switch_fiber(ptls->previous_task, jl_atomic_load_relaxed(&ptls->current_task)); - // TODO: mutex unlock the thread we just switched from + if (jl_setjmp(lasttstate.copy_ctx.uc_mcontext, 0)) { +#ifdef MIGRATE_TASKS + ptls = lastt->ptls; +#endif + lastt->ctx.copy_ctx = NULL; + sanitizer_finish_switch_fiber(&ptls->previous_task->ctx, &lastt->ctx); return; } - } - else + save_stack(ptls, lastt, pt); // allocates (gc-safepoint, and can also fail) + lastt->ctx.copy_ctx = &lasttstate.copy_ctx; +#else + abort(); #endif - *pt = NULL; // can't fail after here: clear the gc-root for the target task now + } + else { + *pt = NULL; // can't fail after here: clear the gc-root for the target task now + lastt->ctx.ctx = &lasttstate.ctx; + } } // set up global state for new task and clear global state for old task @@ -506,41 +538,44 @@ JL_NO_ASAN static void ctx_switch(jl_task_t *lastt) ptls->previous_task = lastt; #endif - if (t->started) { + if (t->ctx.started) { + if (t->ctx.copy_stack) { #ifdef COPY_STACKS - if (t->copy_stack) { - if (lastt->copy_stack) { + if (lastt->ctx.copy_stack) { // Switching from copystack to copystack. Clear any shadow stack // memory above the saved shadow stack. - uintptr_t stacktop = (uintptr_t)ptls->stackbase - t->copy_stack; + uintptr_t stacktop = (uintptr_t)ptls->stackbase - t->ctx.copy_stack; uintptr_t stackbottom = ((uintptr_t)jl_get_frame_addr() & ~15); if (stackbottom < stacktop) - asan_unpoison_stack_memory(stackbottom, stacktop-stackbottom); + asan_unpoison_stack_memory(stackbottom, stacktop - stackbottom); } - if (!killed && !lastt->copy_stack) { - sanitizer_start_switch_fiber(ptls, lastt, t); - restore_stack2(t, ptls, lastt); - } else { - tsan_switch_to_copyctx(&t->ctx); + if (!killed && !lastt->ctx.copy_stack) { + sanitizer_start_switch_fiber(ptls, &lastt->ctx, &t->ctx); + restore_stack2(&t->ctx, ptls, &lastt->ctx); // half jl_swap_fiber and half restore_stack + } + else { + tsan_switch_to_ctx(&t->ctx); if (killed) { - sanitizer_start_switch_fiber_killed(ptls, t); - tsan_destroy_copyctx(ptls, &lastt->ctx); - } else { - sanitizer_start_switch_fiber(ptls, lastt, t); + sanitizer_start_switch_fiber_killed(ptls, &t->ctx); + tsan_destroy_ctx(ptls, &lastt->ctx); + } + else { + sanitizer_start_switch_fiber(ptls, &lastt->ctx, &t->ctx); } - if (lastt->copy_stack) { - restore_stack(t, ptls, NULL); // (doesn't return) + if (lastt->ctx.copy_stack) { + restore_stack(&t->ctx, ptls, NULL); // (doesn't return) + abort(); } else { - restore_stack(t, ptls, (char*)1); // (doesn't return) + restore_stack(&t->ctx, ptls, (char*)1); // (doesn't return) + abort(); } } - } - else #endif - { - if (lastt->copy_stack) { + } + else { + if (lastt->ctx.copy_stack) { // Switching away from a copystack to a non-copystack. Clear // the whole shadow stack now, because otherwise we won't know // how much stack memory to clear the next time we switch to @@ -549,22 +584,23 @@ JL_NO_ASAN static void ctx_switch(jl_task_t *lastt) uintptr_t stackbottom = ((uintptr_t)jl_get_frame_addr() & ~15); // We're not restoring the stack, but we still need to unpoison the // stack, so it starts with a pristine stack. - asan_unpoison_stack_memory(stackbottom, stacktop-stackbottom); + asan_unpoison_stack_memory(stackbottom, stacktop - stackbottom); } if (killed) { - sanitizer_start_switch_fiber_killed(ptls, t); + sanitizer_start_switch_fiber_killed(ptls, &t->ctx); tsan_switch_to_ctx(&t->ctx); tsan_destroy_ctx(ptls, &lastt->ctx); jl_set_fiber(&t->ctx); // (doesn't return) abort(); // unreachable } else { - sanitizer_start_switch_fiber(ptls, lastt, t); - if (lastt->copy_stack) { + sanitizer_start_switch_fiber(ptls, &lastt->ctx, &t->ctx); + if (lastt->ctx.copy_stack) { // Resume at the jl_setjmp earlier in this function, // don't do a full task swap tsan_switch_to_ctx(&t->ctx); jl_set_fiber(&t->ctx); // (doesn't return) + abort(); } else { jl_swap_fiber(&lastt->ctx, &t->ctx); @@ -573,41 +609,58 @@ JL_NO_ASAN static void ctx_switch(jl_task_t *lastt) } } else { - if (lastt->copy_stack) { +#ifdef _COMPILER_TSAN_ENABLED_ + t->ctx.tsan_state = __tsan_create_fiber(0); +#endif + if (lastt->ctx.copy_stack) { uintptr_t stacktop = (uintptr_t)ptls->stackbase; uintptr_t stackbottom = ((uintptr_t)jl_get_frame_addr() & ~15); // We're not restoring the stack, but we still need to unpoison the // stack, so it starts with a pristine stack. - asan_unpoison_stack_memory(stackbottom, stacktop-stackbottom); + asan_unpoison_stack_memory(stackbottom, stacktop - stackbottom); } - if (t->copy_stack && always_copy_stacks) { + if (t->ctx.copy_stack) { +#ifdef COPY_STACKS tsan_switch_to_ctx(&t->ctx); + // create a temporary non-copy_stack context for starting this fiber + jl_ucontext_t ctx = t->ctx; + ctx.ctx = NULL; + ctx.stkbuf = (char*)ptls->stackbase - ptls->stacksize; + ctx.bufsz = ptls->stacksize; + ctx.copy_stack = 0; + ctx.started = 0; if (killed) { - sanitizer_start_switch_fiber_killed(ptls, t); + sanitizer_start_switch_fiber_killed(ptls, &t->ctx); tsan_destroy_ctx(ptls, &lastt->ctx); - } else { - sanitizer_start_switch_fiber(ptls, lastt, t); + if (lastt->ctx.copy_stack) + restore_stack3(&ctx, ptls, NULL); // (doesn't return) + else + jl_start_fiber_set(&ctx); + abort(); + } + sanitizer_start_switch_fiber(ptls, &lastt->ctx, &t->ctx); + if (lastt->ctx.copy_stack) { + restore_stack3(&ctx, ptls, NULL); // (doesn't return) + abort(); + } + else { + jl_start_fiber_swap(&lastt->ctx, &ctx); } -#ifdef COPY_STACKS -#if defined(_OS_WINDOWS_) - jl_setcontext(&t->ctx.copy_ctx); #else - jl_longjmp(t->ctx.copy_ctx.uc_mcontext, 1); -#endif + abort(); #endif - abort(); // unreachable } else { if (killed) { - sanitizer_start_switch_fiber_killed(ptls, t); + sanitizer_start_switch_fiber_killed(ptls, &t->ctx); tsan_switch_to_ctx(&t->ctx); tsan_destroy_ctx(ptls, &lastt->ctx); jl_start_fiber_set(&t->ctx); // (doesn't return) abort(); } - sanitizer_start_switch_fiber(ptls, lastt, t); - if (lastt->copy_stack) { - // Resume at the jl_setjmp earlier in this function + sanitizer_start_switch_fiber(ptls, &lastt->ctx, &t->ctx); + if (lastt->ctx.copy_stack) { + // copy_stack resumes at the jl_setjmp earlier in this function, so don't swap here tsan_switch_to_ctx(&t->ctx); jl_start_fiber_set(&t->ctx); // (doesn't return) abort(); @@ -617,7 +670,14 @@ JL_NO_ASAN static void ctx_switch(jl_task_t *lastt) } } } - sanitizer_finish_switch_fiber(ptls->previous_task, jl_atomic_load_relaxed(&ptls->current_task)); + +#ifdef MIGRATE_TASKS + ptls = lastt->ptls; +#endif + assert(ptls); + assert(lastt == jl_atomic_load_relaxed(&ptls->current_task)); + lastt->ctx.ctx = NULL; + sanitizer_finish_switch_fiber(&ptls->previous_task->ctx, &lastt->ctx); } JL_DLLEXPORT void jl_switch(void) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER @@ -629,7 +689,7 @@ JL_DLLEXPORT void jl_switch(void) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER return; } int8_t gc_state = jl_gc_unsafe_enter(ptls); - if (t->started && t->stkbuf == NULL) + if (t->ctx.started && t->ctx.stkbuf == NULL) jl_error("attempt to switch to exited task"); if (ptls->in_finalizer) jl_error("task switch not allowed from inside gc finalizer"); @@ -654,7 +714,7 @@ JL_DLLEXPORT void jl_switch(void) JL_NOTSAFEPOINT_LEAVE JL_NOTSAFEPOINT_ENTER ptls->previous_task = NULL; assert(t != ct); assert(jl_atomic_load_relaxed(&t->tid) == ptls->tid); - if (!t->sticky && !t->copy_stack) + if (!t->sticky && !t->ctx.copy_stack) jl_atomic_store_release(&t->tid, -1); #else assert(ptls == ct->ptls); @@ -1071,26 +1131,28 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion jl_task_t *t = (jl_task_t*)jl_gc_alloc(ct->ptls, sizeof(jl_task_t), jl_task_type); jl_set_typetagof(t, jl_task_tag, 0); JL_PROBE_RT_NEW_TASK(ct, t); - t->copy_stack = 0; + t->ctx.copy_stack = 0; if (ssize == 0) { // stack size unspecified; use default if (always_copy_stacks) { - t->copy_stack = 1; - t->bufsz = 0; + t->ctx.copy_stack = 1; + t->ctx.bufsz = 0; } else { - t->bufsz = JL_STACK_SIZE; + t->ctx.bufsz = JL_STACK_SIZE; } - t->stkbuf = NULL; + t->ctx.stkbuf = NULL; } else { // user requested dedicated stack of a certain size if (ssize < MINSTKSZ) ssize = MINSTKSZ; - t->bufsz = ssize; - t->stkbuf = jl_alloc_fiber(&t->ctx.ctx, &t->bufsz, t); - if (t->stkbuf == NULL) + t->ctx.bufsz = ssize; + t->ctx.stkbuf = jl_malloc_stack(&t->ctx.bufsz, t); + if (t->ctx.stkbuf == NULL) { + t->ctx.bufsz = 0; jl_throw(jl_memory_exception); + } } t->next = jl_nothing; t->queue = jl_nothing; @@ -1109,30 +1171,21 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion t->sticky = 1; t->gcstack = NULL; t->excstack = NULL; - t->started = 0; + t->ctx.started = 0; t->priority = 0; - jl_atomic_store_relaxed(&t->tid, t->copy_stack ? jl_atomic_load_relaxed(&ct->tid) : -1); // copy_stacks are always pinned since they can't be moved + jl_atomic_store_relaxed(&t->tid, -1); t->threadpoolid = ct->threadpoolid; t->ptls = NULL; t->world_age = ct->world_age; t->reentrant_timing = 0; jl_timing_task_init(t); -#ifdef COPY_STACKS - if (!t->copy_stack) { -#if defined(JL_DEBUG_BUILD) - memset(&t->ctx, 0, sizeof(t->ctx)); -#endif - } - else { - if (always_copy_stacks) - memcpy(&t->ctx.copy_ctx, &ct->ptls->copy_stack_ctx, sizeof(t->ctx.copy_ctx)); - else - memcpy(&t->ctx.ctx, &ct->ptls->base_ctx, sizeof(t->ctx.ctx)); - } -#endif + if (t->ctx.copy_stack) + t->ctx.copy_ctx = NULL; + else + t->ctx.ctx = NULL; #ifdef _COMPILER_TSAN_ENABLED_ - t->ctx.tsan_state = __tsan_create_fiber(0); + t->ctx.tsan_state = NULL; #endif #ifdef _COMPILER_ASAN_ENABLED_ t->ctx.asan_fake_stack = NULL; @@ -1196,7 +1249,7 @@ CFI_NORETURN jl_task_t *ct = jl_current_task; #endif jl_ptls_t ptls = ct->ptls; - sanitizer_finish_switch_fiber(ptls->previous_task, ct); + sanitizer_finish_switch_fiber(&ptls->previous_task->ctx, &ct->ctx); _start_task(); } @@ -1210,6 +1263,7 @@ CFI_NORETURN #else jl_task_t *ct = jl_current_task; #endif + ct->ctx.ctx = NULL; jl_ptls_t ptls = ct->ptls; jl_value_t *res; assert(ptls->finalizers_inhibited == 0); @@ -1217,11 +1271,11 @@ CFI_NORETURN #ifdef MIGRATE_TASKS jl_task_t *pt = ptls->previous_task; ptls->previous_task = NULL; - if (!pt->sticky && !pt->copy_stack) + if (!pt->sticky && !pt->ctx.copy_stack) jl_atomic_store_release(&pt->tid, -1); #endif - ct->started = 1; + ct->ctx.started = 1; JL_PROBE_RT_START_TASK(ct); jl_timing_block_task_enter(ct, ptls, NULL); if (jl_atomic_load_relaxed(&ct->_isexception)) { @@ -1258,64 +1312,52 @@ skip_pop_exception:; #ifdef _OS_WINDOWS_ #define setcontext jl_setcontext #define swapcontext jl_swapcontext -#define makecontext jl_makecontext #endif -static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner) JL_NOTSAFEPOINT +static int make_fiber(jl_ucontext_t *t, _jl_ucontext_t *ctx) { #ifndef _OS_WINDOWS_ - int r = getcontext(t); - if (r != 0) - jl_error("getcontext failed"); + int r = getcontext(ctx); + if (r != 0) abort(); #endif - void *stk = jl_malloc_stack(ssize, owner); - if (stk == NULL) - return NULL; - t->uc_stack.ss_sp = stk; - t->uc_stack.ss_size = *ssize; + ctx->uc_stack.ss_sp = (char*)t->stkbuf; + ctx->uc_stack.ss_size = t->bufsz; #ifdef _OS_WINDOWS_ - makecontext(t, &start_task); + jl_makecontext(ctx, &start_task); #else - t->uc_link = NULL; - makecontext(t, &start_task, 0); + ctx->uc_link = NULL; + makecontext(ctx, &start_task, 0); #endif - return (char*)stk; + return 1; } static void jl_start_fiber_set(jl_ucontext_t *t) { - setcontext(&t->ctx); + _jl_ucontext_t ctx; + make_fiber(t, &ctx); + setcontext(&ctx); } static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t *t) { + _jl_ucontext_t ctx; + make_fiber(t, &ctx); assert(lastt); tsan_switch_to_ctx(t); - swapcontext(&lastt->ctx, &t->ctx); + swapcontext(lastt->ctx, &ctx); } static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t) { tsan_switch_to_ctx(t); - swapcontext(&lastt->ctx, &t->ctx); + swapcontext(lastt->ctx, t->ctx); } static void jl_set_fiber(jl_ucontext_t *t) { - setcontext(&t->ctx); -} -#endif - -#if defined(JL_HAVE_UNW_CONTEXT) || defined(JL_HAVE_ASM) -static char *jl_alloc_fiber(_jl_ucontext_t *t, size_t *ssize, jl_task_t *owner) -{ - char *stkbuf = (char*)jl_malloc_stack(ssize, owner); - if (stkbuf == NULL) - return NULL; -#ifndef __clang_gcanalyzer__ - ((char**)t)[0] = stkbuf; // stash the stack pointer somewhere for start_fiber - ((size_t*)t)[1] = *ssize; // stash the stack size somewhere for start_fiber -#endif - return stkbuf; + setcontext(t->ctx); } #endif #if defined(JL_HAVE_UNW_CONTEXT) +#ifdef _OS_WINDOWS_ +#error unw_context_t not defined in Windows +#endif static inline void jl_unw_swapcontext(unw_context_t *old, unw_cursor_t *c) { volatile int returns = 0; @@ -1329,15 +1371,15 @@ static inline void jl_unw_swapcontext(unw_context_t *old, unw_cursor_t *c) static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t) { unw_cursor_t c; - int r = unw_init_local(&c, &t->ctx); + int r = unw_init_local(&c, t->ctx); if (r < 0) abort(); - jl_unw_swapcontext(&lastt->ctx, &c); + jl_unw_swapcontext(lastt->ctx, &c); } static void jl_set_fiber(jl_ucontext_t *t) { unw_cursor_t c; - int r = unw_init_local(&c, &t->ctx); + int r = unw_init_local(&c, t->ctx); if (r < 0) abort(); unw_resume(&c); @@ -1345,14 +1387,14 @@ static void jl_set_fiber(jl_ucontext_t *t) #elif defined(JL_HAVE_ASM) static void jl_swap_fiber(jl_ucontext_t *lastt, jl_ucontext_t *t) { - if (jl_setjmp(lastt->ctx.uc_mcontext, 0)) + if (jl_setjmp(lastt->ctx->uc_mcontext, 0)) return; tsan_switch_to_ctx(t); jl_set_fiber(t); // doesn't return } static void jl_set_fiber(jl_ucontext_t *t) { - jl_longjmp(t->ctx.uc_mcontext, 1); + jl_longjmp(t->ctx->uc_mcontext, 1); } #endif @@ -1373,14 +1415,14 @@ static void jl_set_fiber(jl_ucontext_t *t) static void jl_start_fiber_set(jl_ucontext_t *t) { unw_cursor_t c; - char *stk = ((char**)&t->ctx)[0]; - size_t ssize = ((size_t*)&t->ctx)[1]; + char *stk = (char*)t->stkbuf; + size_t ssize = t->bufsz; uintptr_t fn = (uintptr_t)&start_task; stk += ssize; - int r = unw_getcontext(&t->ctx); + int r = unw_getcontext(t->ctx); if (r) abort(); - if (unw_init_local(&c, &t->ctx)) + if (unw_init_local(&c, t->ctx)) abort(); PUSH_RET(&c, stk); #if defined __linux__ @@ -1396,43 +1438,46 @@ static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t *t) { assert(lastt); unw_cursor_t c; - char *stk = ((char**)&t->ctx)[0]; - size_t ssize = ((size_t*)&t->ctx)[1]; + char *stk = (char*)t->stkbuf; + size_t ssize = t->bufsz; uintptr_t fn = (uintptr_t)&start_task; stk += ssize; volatile int returns = 0; - int r = unw_getcontext(&lastt->ctx); + int r = unw_getcontext(lastt->ctx); if (++returns == 2) // r is garbage after the first return return; if (r != 0 || returns != 1) abort(); - r = unw_getcontext(&t->ctx); + r = unw_getcontext(t->ctx); if (r != 0) abort(); - if (unw_init_local(&c, &t->ctx)) + if (unw_init_local(&c, t->ctx)) abort(); PUSH_RET(&c, stk); if (unw_set_reg(&c, UNW_REG_SP, (uintptr_t)stk)) abort(); if (unw_set_reg(&c, UNW_REG_IP, fn)) abort(); - jl_unw_swapcontext(&lastt->ctx, &c); + jl_unw_swapcontext(lastt->ctx, &c); } #endif #if defined(JL_HAVE_ASM) +#ifdef _OS_WINDOWS_ +#error JL_HAVE_ASM not defined in Windows +#endif JL_NO_ASAN static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t *t) { assert(lastt); #ifdef JL_HAVE_UNW_CONTEXT volatile int returns = 0; - int r = unw_getcontext(&lastt->ctx); + int r = unw_getcontext(lastt->ctx); if (++returns == 2) // r is garbage after the first return return; if (r != 0 || returns != 1) abort(); #else - if (jl_setjmp(lastt->ctx.uc_mcontext, 0)) + if (jl_setjmp(lastt->ctx->uc_mcontext, 0)) return; #endif tsan_switch_to_ctx(t); @@ -1440,8 +1485,9 @@ JL_NO_ASAN static void jl_start_fiber_swap(jl_ucontext_t *lastt, jl_ucontext_t * } JL_NO_ASAN static void jl_start_fiber_set(jl_ucontext_t *t) { - char *stk = ((char**)&t->ctx)[0]; - size_t ssize = ((size_t*)&t->ctx)[1]; +CFI_NORETURN + char *stk = (char*)t->stkbuf; + size_t ssize = t->bufsz; uintptr_t fn = (uintptr_t)&start_task; stk += ssize; #ifdef _CPU_X86_64_ @@ -1539,14 +1585,14 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) } #endif if (always_copy_stacks) { - ct->copy_stack = 1; - ct->stkbuf = NULL; - ct->bufsz = 0; + ct->ctx.copy_stack = 1; + ct->ctx.stkbuf = NULL; + ct->ctx.bufsz = 0; } else { - ct->copy_stack = 0; - ct->stkbuf = stack; - ct->bufsz = ssize; + ct->ctx.copy_stack = 0; + ct->ctx.stkbuf = stack; + ct->ctx.bufsz = ssize; } #ifdef USE_TRACY @@ -1554,7 +1600,7 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) strcpy(unique_string, "Root"); ct->name = unique_string; #endif - ct->started = 1; + ct->ctx.started = 1; ct->next = jl_nothing; ct->queue = jl_nothing; ct->tls = jl_nothing; @@ -1594,21 +1640,18 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) if (always_copy_stacks) { // when this is set, we will attempt to corrupt the process stack to switch tasks, // although this is unreliable, and thus not recommended - ptls->stackbase = stack_hi; - ptls->stacksize = ssize; -#ifdef _OS_WINDOWS_ - ptls->copy_stack_ctx.uc_stack.ss_sp = stack_hi; - ptls->copy_stack_ctx.uc_stack.ss_size = ssize; -#endif - if (jl_setjmp(ptls->copy_stack_ctx.uc_mcontext, 0)) - start_task(); // sanitizer_finish_switch_fiber is part of start_task + ptls->stackbase = jl_get_frame_addr(); + ptls->stacksize = (char*)ptls->stackbase - (char*)stack_lo; } else { - ssize = JL_STACK_SIZE; - char *stkbuf = jl_alloc_fiber(&ptls->base_ctx, &ssize, NULL); + size_t bufsz = JL_STACK_SIZE; + void *stkbuf = jl_malloc_stack(&bufsz, NULL); if (stkbuf != NULL) { - ptls->stackbase = stkbuf + ssize; - ptls->stacksize = ssize; + ptls->stackbase = (char*)stkbuf + bufsz; + ptls->stacksize = bufsz; + } + else { + ptls->stacksize = 0; } } #endif @@ -1621,7 +1664,7 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) JL_DLLEXPORT int jl_is_task_started(jl_task_t *t) JL_NOTSAFEPOINT { - return t->started; + return t->ctx.started; } JL_DLLEXPORT int16_t jl_get_task_tid(jl_task_t *t) JL_NOTSAFEPOINT diff --git a/src/threading.c b/src/threading.c index 8f37ee814056c..a6050ace01833 100644 --- a/src/threading.c +++ b/src/threading.c @@ -82,51 +82,17 @@ JL_DLLEXPORT void jl_set_safe_restore(jl_jmp_buf *sr) // The tls_states buffer: // // On platforms that do not use ELF (i.e. where `__thread` is emulated with -// lower level API) (Mac, Windows), we use the platform runtime API to create +// lower level API) (Windows), we use the platform runtime API to create // TLS variable directly. // This is functionally equivalent to using `__thread` but can be // more efficient since we can have better control over the creation and // initialization of the TLS buffer. // -// On platforms that use ELF (Linux, FreeBSD), we use a `__thread` variable +// On platforms that support native TLS (ELF platforms + Macos) we use a `__thread` variable // as the fallback in the shared object. For better efficiency, we also // create a `__thread` variable in the main executable using a static TLS // model. -#if defined(_OS_DARWIN_) -// Mac doesn't seem to have static TLS model so the runtime TLS getter -// registration will only add overhead to TLS access. The `__thread` variables -// are emulated with `pthread_key_t` so it is actually faster to use it directly. -static pthread_key_t jl_pgcstack_key; - -__attribute__((constructor)) void jl_init_tls(void) -{ - pthread_key_create(&jl_pgcstack_key, NULL); -} - -JL_CONST_FUNC jl_gcframe_t **jl_get_pgcstack(void) JL_NOTSAFEPOINT -{ - return (jl_gcframe_t**)pthread_getspecific(jl_pgcstack_key); -} - -void jl_set_pgcstack(jl_gcframe_t **pgcstack) JL_NOTSAFEPOINT -{ - pthread_setspecific(jl_pgcstack_key, (void*)pgcstack); -} - -void jl_pgcstack_getkey(jl_get_pgcstack_func **f, pthread_key_t *k) -{ - // for codegen - *f = pthread_getspecific; - *k = jl_pgcstack_key; -} - - -JL_DLLEXPORT void jl_pgcstack_setkey(jl_get_pgcstack_func *f, pthread_key_t k) -{ - jl_safe_printf("ERROR: Attempt to change TLS address.\n"); -} - -#elif defined(_OS_WINDOWS_) +#if defined(_OS_WINDOWS_) // Apparently windows doesn't have a static TLS model (or one that can be // reliably used from a shared library) either..... Use `TLSAlloc` instead. @@ -464,6 +430,7 @@ void jl_safepoint_resume_all_threads(jl_task_t *ct) void jl_task_frame_noreturn(jl_task_t *ct) JL_NOTSAFEPOINT; void scheduler_delete_thread(jl_ptls_t ptls) JL_NOTSAFEPOINT; +void _jl_free_stack(jl_ptls_t ptls, void *stkbuf, size_t bufsz) JL_NOTSAFEPOINT; static void jl_delete_thread(void *value) JL_NOTSAFEPOINT_ENTER { @@ -492,7 +459,7 @@ static void jl_delete_thread(void *value) JL_NOTSAFEPOINT_ENTER } if (signal_stack != NULL) { if (signal_stack_size) - jl_free_stack(signal_stack, signal_stack_size); + _jl_free_stack(ptls ,signal_stack, signal_stack_size); else free(signal_stack); } diff --git a/stdlib/Downloads.version b/stdlib/Downloads.version index 7805348a4b2f5..cb041d86d7f66 100644 --- a/stdlib/Downloads.version +++ b/stdlib/Downloads.version @@ -1,4 +1,4 @@ DOWNLOADS_BRANCH = master -DOWNLOADS_SHA1 = a9d274ff6588cc5dbfa90e908ee34c2408bab84a +DOWNLOADS_SHA1 = 1061ecc377a053fce0df94e1a19e5260f7c030f5 DOWNLOADS_GIT_URL := https://github.com/JuliaLang/Downloads.jl.git DOWNLOADS_TAR_URL = https://api.github.com/repos/JuliaLang/Downloads.jl/tarball/$1 diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index b000f353443c4..424564b70384c 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -394,13 +394,21 @@ let errf = tempname(), try redirect_stderr(new_stderr) @test occursin("f_broken_code", sprint(code_native, h_broken_code, ())) + Libc.flush_cstdio() println(new_stderr, "start") flush(new_stderr) - @test_throws "could not compile the specified method" sprint(code_native, f_broken_code, ()) + @test_throws "could not compile the specified method" sprint(io -> code_native(io, f_broken_code, (), dump_module=true)) Libc.flush_cstdio() - println(new_stderr, "end") + println(new_stderr, "middle") + flush(new_stderr) + @test !isempty(sprint(io -> code_native(io, f_broken_code, (), dump_module=false))) + Libc.flush_cstdio() + println(new_stderr, "later") flush(new_stderr) @test invokelatest(g_broken_code) == 0 + Libc.flush_cstdio() + println(new_stderr, "end") + flush(new_stderr) finally Libc.flush_cstdio() redirect_stderr(old_stderr) @@ -410,6 +418,14 @@ let errf = tempname(), Internal error: encountered unexpected error during compilation of f_broken_code: ErrorException(\"unsupported or misplaced expression \\\"invalid\\\" in function f_broken_code\") """) || errstr + @test occursin("""\nmiddle + Internal error: encountered unexpected error during compilation of f_broken_code: + ErrorException(\"unsupported or misplaced expression \\\"invalid\\\" in function f_broken_code\") + """, errstr) || errstr + @test occursin("""\nlater + Internal error: encountered unexpected error during compilation of f_broken_code: + ErrorException(\"unsupported or misplaced expression \\\"invalid\\\" in function f_broken_code\") + """, errstr) || errstr @test endswith(errstr, "\nend\n") || errstr end rm(errf) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 5aa4314c9ae51..a997347eabd58 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -186,7 +186,7 @@ end throw(ArgumentError(LazyString(lazy"cannot set entry ($i, $j) off the ", A.uplo == 'U' ? "upper" : "lower", " bidiagonal band to a nonzero value ", x))) end - return x + return A end Base._reverse(A::Bidiagonal, dims) = reverse!(Matrix(A); dims) @@ -535,12 +535,18 @@ function rmul!(B::Bidiagonal, D::Diagonal) end @noinline function check_A_mul_B!_sizes((mC, nC)::NTuple{2,Integer}, (mA, nA)::NTuple{2,Integer}, (mB, nB)::NTuple{2,Integer}) + # check for matching sizes in one column of B and C + check_A_mul_B!_sizes((mC,), (mA, nA), (mB,)) + # ensure that the number of columns in B and C match + if nB != nC + throw(DimensionMismatch(lazy"second dimension of output C, $nC, and second dimension of B, $nB, must match")) + end +end +@noinline function check_A_mul_B!_sizes((mC,)::Tuple{Integer}, (mA, nA)::NTuple{2,Integer}, (mB,)::Tuple{Integer}) if mA != mC throw(DimensionMismatch(lazy"first dimension of A, $mA, and first dimension of output C, $mC, must match")) elseif nA != mB throw(DimensionMismatch(lazy"second dimension of A, $nA, and first dimension of B, $mB, must match")) - elseif nB != nC - throw(DimensionMismatch(lazy"second dimension of output C, $nC, and second dimension of B, $nB, must match")) end end @@ -563,8 +569,10 @@ _mul!(C::AbstractMatrix, A::BiTriSym, B::TriSym, _add::MulAddMul) = _mul!(C::AbstractMatrix, A::BiTriSym, B::Bidiagonal, _add::MulAddMul) = _bibimul!(C, A, B, _add) function _bibimul!(C, A, B, _add) + require_one_based_indexing(C) check_A_mul_B!_sizes(size(C), size(A), size(B)) n = size(A,1) + iszero(n) && return C n <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) # We use `_rmul_or_fill!` instead of `_modify!` here since using # `_modify!` in the following loop will not update the @@ -727,15 +735,10 @@ end function _mul!(C::AbstractVecOrMat, A::BiTriSym, B::AbstractVecOrMat, _add::MulAddMul) require_one_based_indexing(C, B) + check_A_mul_B!_sizes(size(C), size(A), size(B)) nA = size(A,1) nB = size(B,2) - if !(size(C,1) == size(B,1) == nA) - throw(DimensionMismatch(lazy"A has first dimension $nA, B has $(size(B,1)), C has $(size(C,1)) but all must match")) - end - if size(C,2) != nB - throw(DimensionMismatch(lazy"A has second dimension $nA, B has $(size(B,2)), C has $(size(C,2)) but all must match")) - end - iszero(nA) && return C + (iszero(nA) || iszero(nB)) && return C iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) nA <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) l = _diag(A, -1) @@ -758,9 +761,10 @@ end function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::TriSym, _add::MulAddMul) require_one_based_indexing(C, A) check_A_mul_B!_sizes(size(C), size(A), size(B)) - iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) n = size(A,1) m = size(B,2) + (iszero(m) || iszero(n)) && return C + iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) if n <= 3 || m <= 1 return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) end @@ -793,11 +797,12 @@ end function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::Bidiagonal, _add::MulAddMul) require_one_based_indexing(C, A) check_A_mul_B!_sizes(size(C), size(A), size(B)) + m, n = size(A) + (iszero(m) || iszero(n)) && return C iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) if size(A, 1) <= 3 || size(B, 2) <= 1 return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) end - m, n = size(A) @inbounds if B.uplo == 'U' for i in 1:m for j in n:-1:2 @@ -824,6 +829,7 @@ function _dibimul!(C, A, B, _add) require_one_based_indexing(C) check_A_mul_B!_sizes(size(C), size(A), size(B)) n = size(A,1) + iszero(n) && return C n <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) _rmul_or_fill!(C, _add.beta) # see the same use above iszero(_add.alpha) && return C diff --git a/stdlib/LinearAlgebra/src/cholesky.jl b/stdlib/LinearAlgebra/src/cholesky.jl index cb7c6b94d4ca6..545d92ec1704d 100644 --- a/stdlib/LinearAlgebra/src/cholesky.jl +++ b/stdlib/LinearAlgebra/src/cholesky.jl @@ -551,6 +551,9 @@ end # allow packages like SparseArrays.jl to hook into here and redirect to out-of-place `cholesky` _cholesky(A::AbstractMatrix, args...; kwargs...) = cholesky!(A, args...; kwargs...) +# allow cholesky of cholesky +cholesky(A::Cholesky) = A + ## With pivoting """ cholesky(A, RowMaximum(); tol = 0.0, check = true) -> CholeskyPivoted diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index 94a075ffaf24d..62096cbb172f2 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -370,13 +370,10 @@ julia> diagm([1,2,3]) diagm(v::AbstractVector) = diagm(0 => v) diagm(m::Integer, n::Integer, v::AbstractVector) = diagm(m, n, 0 => v) -function tr(A::Matrix{T}) where T - n = checksquare(A) - t = zero(T) - @inbounds @simd for i in 1:n - t += A[i,i] - end - t +function tr(A::StridedMatrix{T}) where T + checksquare(A) + isempty(A) && return zero(T) + reduce(+, (A[i] for i in diagind(A, IndexStyle(A)))) end _kronsize(A::AbstractMatrix, B::AbstractMatrix) = map(*, size(A), size(B)) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 77459f7cca520..92f399bb774ff 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -205,7 +205,7 @@ function setindex!(D::Diagonal, v, i::Int, j::Int) elseif !iszero(v) throw(ArgumentError(lazy"cannot set off-diagonal entry ($i, $j) to a nonzero value ($v)")) end - return v + return D end diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 412d375d3e842..9a232d3ad1e51 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -61,9 +61,10 @@ function (*)(A::AbstractMatrix{T}, x::AbstractVector{S}) where {T,S} end # these will throw a DimensionMismatch unless B has 1 row (or 1 col for transposed case): -(*)(a::AbstractVector, tB::TransposeAbsMat) = reshape(a, length(a), 1) * tB -(*)(a::AbstractVector, adjB::AdjointAbsMat) = reshape(a, length(a), 1) * adjB -(*)(a::AbstractVector, B::AbstractMatrix) = reshape(a, length(a), 1) * B +function (*)(a::AbstractVector, B::AbstractMatrix) + require_one_based_indexing(a) + reshape(a, length(a), 1) * B +end # Add a level of indirection and specialize _mul! to avoid ambiguities in mul! @inline mul!(y::AbstractVector, A::AbstractVecOrMat, x::AbstractVector, diff --git a/stdlib/LinearAlgebra/src/symmetric.jl b/stdlib/LinearAlgebra/src/symmetric.jl index d801158232673..ab7b5ee031260 100644 --- a/stdlib/LinearAlgebra/src/symmetric.jl +++ b/stdlib/LinearAlgebra/src/symmetric.jl @@ -12,7 +12,7 @@ struct Symmetric{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} end end """ - Symmetric(A, uplo=:U) + Symmetric(A::AbstractMatrix, uplo::Symbol=:U) Construct a `Symmetric` view of the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of the matrix `A`. @@ -63,7 +63,7 @@ function Symmetric(A::AbstractMatrix, uplo::Symbol=:U) end """ - symmetric(A, uplo=:U) + symmetric(A, uplo::Symbol=:U) Construct a symmetric view of `A`. If `A` is a matrix, `uplo` controls whether the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of `A` is used to implicitly fill the @@ -105,7 +105,7 @@ struct Hermitian{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} end end """ - Hermitian(A, uplo=:U) + Hermitian(A::AbstractMatrix, uplo::Symbol=:U) Construct a `Hermitian` view of the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of the matrix `A`. @@ -153,7 +153,7 @@ function Hermitian(A::AbstractMatrix, uplo::Symbol=:U) end """ - hermitian(A, uplo=:U) + hermitian(A, uplo::Symbol=:U) Construct a hermitian view of `A`. If `A` is a matrix, `uplo` controls whether the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of `A` is used to implicitly fill the @@ -261,6 +261,7 @@ Base._reverse(A::Symmetric, ::Colon) = Symmetric(reverse(A.data), A.uplo == 'U' @propagate_inbounds function setindex!(A::Symmetric, v, i::Integer, j::Integer) i == j || throw(ArgumentError("Cannot set a non-diagonal index in a symmetric matrix")) setindex!(A.data, v, i, j) + return A end Base._reverse(A::Hermitian, dims) = reverse!(Matrix(A); dims) @@ -274,6 +275,7 @@ Base._reverse(A::Hermitian, ::Colon) = Hermitian(reverse(A.data), A.uplo == 'U' else setindex!(A.data, v, i, j) end + return A end Base.dataids(A::HermOrSym) = Base.dataids(parent(A)) @@ -449,8 +451,8 @@ Base.copy(A::Adjoint{<:Any,<:Symmetric}) = Base.copy(A::Transpose{<:Any,<:Hermitian}) = Hermitian(copy(transpose(A.parent.data)), ifelse(A.parent.uplo == 'U', :L, :U)) -tr(A::Symmetric) = tr(A.data) # to avoid AbstractMatrix fallback (incl. allocations) -tr(A::Hermitian) = real(tr(A.data)) +tr(A::Symmetric{<:Number}) = tr(A.data) # to avoid AbstractMatrix fallback (incl. allocations) +tr(A::Hermitian{<:Number}) = real(tr(A.data)) Base.conj(A::Symmetric) = Symmetric(parentof_applytri(conj, A), sym_uplo(A.uplo)) Base.conj(A::Hermitian) = Hermitian(parentof_applytri(conj, A), sym_uplo(A.uplo)) @@ -998,7 +1000,7 @@ function cbrt(A::HermOrSym{<:Real}) end """ - hermitianpart(A, uplo=:U) -> Hermitian + hermitianpart(A::AbstractMatrix, uplo::Symbol=:U) -> Hermitian Return the Hermitian part of the square matrix `A`, defined as `(A + A') / 2`, as a [`Hermitian`](@ref) matrix. For real matrices `A`, this is also known as the symmetric part @@ -1014,7 +1016,7 @@ See also [`hermitianpart!`](@ref) for the corresponding in-place operation. hermitianpart(A::AbstractMatrix, uplo::Symbol=:U) = Hermitian(_hermitianpart(A), uplo) """ - hermitianpart!(A, uplo=:U) -> Hermitian + hermitianpart!(A::AbstractMatrix, uplo::Symbol=:U) -> Hermitian Overwrite the square matrix `A` in-place with its Hermitian part `(A + A') / 2`, and return [`Hermitian(A, uplo)`](@ref). For real matrices `A`, this is also known as the symmetric diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 365ce8ee4bae2..923e13e488c85 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -979,9 +979,20 @@ _trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::AbstractTriangular) = _trimul!(C::AbstractMatrix, A::AbstractTriangular, B::UpperOrLowerTriangular) = generic_mattrimul!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B))) -lmul!(A::AbstractTriangular, B::AbstractVecOrMat) = @inline _trimul!(B, A, B) -rmul!(A::AbstractMatrix, B::AbstractTriangular) = @inline _trimul!(A, A, B) - +function lmul!(A::AbstractTriangular, B::AbstractVecOrMat) + if istriu(A) + _trimul!(B, UpperTriangular(A), B) + else + _trimul!(B, LowerTriangular(A), B) + end +end +function rmul!(A::AbstractMatrix, B::AbstractTriangular) + if istriu(B) + _trimul!(A, A, UpperTriangular(B)) + else + _trimul!(A, A, LowerTriangular(B)) + end +end for TC in (:AbstractVector, :AbstractMatrix) @eval @inline function _mul!(C::$TC, A::AbstractTriangular, B::AbstractVector, alpha::Number, beta::Number) @@ -1017,8 +1028,20 @@ _ldiv!(C::AbstractVecOrMat, A::UpperOrLowerTriangular, B::AbstractVecOrMat) = _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::UpperOrLowerTriangular) = generic_mattridiv!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B))) -ldiv!(A::AbstractTriangular, B::AbstractVecOrMat) = @inline _ldiv!(B, A, B) -rdiv!(A::AbstractMatrix, B::AbstractTriangular) = @inline _rdiv!(A, A, B) +function ldiv!(A::AbstractTriangular, B::AbstractVecOrMat) + if istriu(A) + _ldiv!(B, UpperTriangular(A), B) + else + _ldiv!(B, LowerTriangular(A), B) + end +end +function rdiv!(A::AbstractMatrix, B::AbstractTriangular) + if istriu(B) + _rdiv!(A, A, UpperTriangular(B)) + else + _rdiv!(A, A, LowerTriangular(B)) + end +end # preserve triangular structure in in-place multiplication/division for (cty, aty, bty) in ((:UpperTriangular, :UpperTriangular, :UpperTriangular), diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index 0ba03634d82ad..c2806c21c00ff 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -476,7 +476,7 @@ Base._reverse!(A::SymTridiagonal, dims::Colon) = (reverse!(A.dv); reverse!(A.ev) else throw(ArgumentError(lazy"cannot set off-diagonal entry ($i, $j)")) end - return x + return A end ## Tridiagonal matrices ## @@ -731,7 +731,7 @@ end throw(ArgumentError(LazyString(lazy"cannot set entry ($i, $j) off ", lazy"the tridiagonal band to a nonzero value ($x)"))) end - return x + return A end ## structured matrix methods ## diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index e19d890237a26..387657ba12d04 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -124,6 +124,9 @@ Random.seed!(1) Bl = Bidiagonal(rand(elty, 10), zeros(elty, 9), 'L') @test_throws ArgumentError Bu[5, 4] = 1 @test_throws ArgumentError Bl[4, 5] = 1 + + # setindex should return the destination + @test setindex!(ubd, 1, 1, 1) === ubd end @testset "isstored" begin @@ -1020,4 +1023,26 @@ end @test_throws "cannot set entry" B[1,2] = 4 end +@testset "mul with empty arrays" begin + A = zeros(5,0) + B = Bidiagonal(zeros(0), zeros(0), :U) + BL = Bidiagonal(zeros(5), zeros(4), :U) + @test size(A * B) == size(A) + @test size(BL * A) == size(A) + @test size(B * B) == size(B) + C = similar(A) + @test mul!(C, A, B) == A * B + @test mul!(C, BL, A) == BL * A + @test mul!(similar(B), B, B) == B * B + @test mul!(similar(B, size(B)), B, B) == B * B + + v = zeros(size(B,2)) + @test size(B * v) == size(v) + @test mul!(similar(v), B, v) == B * v + + D = Diagonal(zeros(size(B,2))) + @test size(B * D) == size(D * B) == size(D) + @test mul!(similar(D), B, D) == mul!(similar(D), D, B) == B * D +end + end # module TestBidiagonal diff --git a/stdlib/LinearAlgebra/test/cholesky.jl b/stdlib/LinearAlgebra/test/cholesky.jl index 2bcc6208c12df..00bfc18a21638 100644 --- a/stdlib/LinearAlgebra/test/cholesky.jl +++ b/stdlib/LinearAlgebra/test/cholesky.jl @@ -630,4 +630,14 @@ end end end +@testset "cholesky_of_cholesky" begin + for T in (Float64, ComplexF64), uplo in (:U, :L) + A = randn(T, 100, 100) + P = Hermitian(A' * A, uplo) + C = cholesky(P) + CC = cholesky(C) + @test C == CC + end +end + end # module TestCholesky diff --git a/stdlib/LinearAlgebra/test/dense.jl b/stdlib/LinearAlgebra/test/dense.jl index afc1df817a544..1d43d76899392 100644 --- a/stdlib/LinearAlgebra/test/dense.jl +++ b/stdlib/LinearAlgebra/test/dense.jl @@ -1285,4 +1285,20 @@ end @test eltype(A) == eltype(T) end +@testset "tr" begin + @testset "block matrices" begin + S = [1 2; 3 4] + M = fill(S, 3, 3) + @test tr(M) == 3S + @test tr(view(M, :, :)) == 3S + @test tr(view(M, axes(M)...)) == 3S + end + @testset "avoid promotion" begin + A = Int8[1 3; 2 4] + @test tr(A) === Int8(5) + @test tr(view(A, :, :)) === Int8(5) + @test tr(view(A, axes(A)...)) === Int8(5) + end +end + end # module TestDense diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 29f3a38473d4a..866c11b9931cd 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -617,6 +617,8 @@ end @test_throws ArgumentError D[i, j] = 1 end end + # setindex should return the destination + @test setindex!(D, 1, 1, 1) === D end @testset "Test reverse" begin @@ -1352,4 +1354,9 @@ end end end +@testset "bounds-check with CartesianIndex ranges" begin + D = Diagonal(1:typemax(Int)) + @test checkbounds(Bool, D, diagind(D, IndexCartesian())) +end + end # module TestDiagonal diff --git a/stdlib/LinearAlgebra/test/matmul.jl b/stdlib/LinearAlgebra/test/matmul.jl index 56834a39a3ceb..4c79451ebfc8b 100644 --- a/stdlib/LinearAlgebra/test/matmul.jl +++ b/stdlib/LinearAlgebra/test/matmul.jl @@ -1120,4 +1120,14 @@ end end end +@testset "vector-matrix multiplication" begin + a = [1,2] + A = reshape([1,2], 2, 1) + B = [1 2] + @test a * B β‰ˆ A * B + B = reshape([1,2], 2, 1) + @test a * B' β‰ˆ A * B' + @test a * transpose(B) β‰ˆ A * transpose(B) +end + end # module TestMatmul diff --git a/stdlib/LinearAlgebra/test/symmetric.jl b/stdlib/LinearAlgebra/test/symmetric.jl index 89e9ca0d6a51d..939e677039dc7 100644 --- a/stdlib/LinearAlgebra/test/symmetric.jl +++ b/stdlib/LinearAlgebra/test/symmetric.jl @@ -1116,4 +1116,23 @@ end end end +@testset "tr for block matrices" begin + m = [1 2; 3 4] + for b in (m, m * (1 + im)) + M = fill(b, 3, 3) + for ST in (Symmetric, Hermitian) + S = ST(M) + @test tr(S) == sum(diag(S)) + end + end +end + +@testset "setindex! returns the destination" begin + M = rand(2,2) + for T in (Symmetric, Hermitian) + S = T(M) + @test setindex!(S, 0, 2, 2) === S + end +end + end # module TestSymmetric diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index 3f7cea91ec6d4..5f0a829f9cdda 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -25,6 +25,12 @@ debug && println("Test basic type functionality") @test_throws DimensionMismatch LowerTriangular(randn(5, 4)) @test LowerTriangular(randn(3, 3)) |> t -> [size(t, i) for i = 1:3] == [size(Matrix(t), i) for i = 1:3] +struct MyTriangular{T, A<:LinearAlgebra.AbstractTriangular{T}} <: LinearAlgebra.AbstractTriangular{T} + data :: A +end +Base.size(A::MyTriangular) = size(A.data) +Base.getindex(A::MyTriangular, i::Int, j::Int) = A.data[i,j] + # The following test block tries to call all methods in base/linalg/triangular.jl in order for a combination of input element types. Keep the ordering when adding code. @testset for elty1 in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}, Int) # Begin loop for first Triangular matrix @@ -897,7 +903,7 @@ end function test_one_oneunit_triangular(a) b = Matrix(a) @test (@inferred a^1) == b^1 - @test (@inferred a^-1) == b^-1 + @test (@inferred a^-1) β‰ˆ b^-1 @test one(a) == one(b) @test one(a)*a == a @test a*one(a) == a @@ -1194,4 +1200,32 @@ end end end +@testset "(l/r)mul! and (l/r)div! for generic triangular" begin + @testset for T in (UpperTriangular, LowerTriangular, UnitUpperTriangular, UnitLowerTriangular) + M = MyTriangular(T(rand(4,4))) + A = rand(4,4) + Ac = similar(A) + @testset "lmul!" begin + Ac .= A + lmul!(M, Ac) + @test Ac β‰ˆ M * A + end + @testset "rmul!" begin + Ac .= A + rmul!(Ac, M) + @test Ac β‰ˆ A * M + end + @testset "ldiv!" begin + Ac .= A + ldiv!(M, Ac) + @test Ac β‰ˆ M \ A + end + @testset "rdiv!" begin + Ac .= A + rdiv!(Ac, M) + @test Ac β‰ˆ A / M + end + end +end + end # module TestTriangular diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index e0a8e32d77852..759d692f8bc68 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -262,6 +262,8 @@ end @test_throws ArgumentError A[3, 2] = 1 # test assignment on the subdiagonal @test_throws ArgumentError A[2, 3] = 1 # test assignment on the superdiagonal end + # setindex! should return the destination + @test setindex!(A, A[2,2], 2, 2) === A end @testset "diag" begin @test (@inferred diag(A))::typeof(d) == d @@ -917,6 +919,28 @@ end end end +@testset "mul with empty arrays" begin + A = zeros(5,0) + T = Tridiagonal(zeros(0), zeros(0), zeros(0)) + TL = Tridiagonal(zeros(4), zeros(5), zeros(4)) + @test size(A * T) == size(A) + @test size(TL * A) == size(A) + @test size(T * T) == size(T) + C = similar(A) + @test mul!(C, A, T) == A * T + @test mul!(C, TL, A) == TL * A + @test mul!(similar(T), T, T) == T * T + @test mul!(similar(T, size(T)), T, T) == T * T + + v = zeros(size(T,2)) + @test size(T * v) == size(v) + @test mul!(similar(v), T, v) == T * v + + D = Diagonal(zeros(size(T,2))) + @test size(T * D) == size(D * T) == size(D) + @test mul!(similar(D), T, D) == mul!(similar(D), D, T) == T * D +end + @testset "show" begin T = Tridiagonal(1:3, 1:4, 1:3) @test sprint(show, T) == "Tridiagonal(1:3, 1:4, 1:3)" diff --git a/stdlib/OpenBLAS_jll/Project.toml b/stdlib/OpenBLAS_jll/Project.toml index dfca282c74704..a9a1a04facff5 100644 --- a/stdlib/OpenBLAS_jll/Project.toml +++ b/stdlib/OpenBLAS_jll/Project.toml @@ -1,6 +1,6 @@ name = "OpenBLAS_jll" uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.28+0" +version = "0.3.28+2" [deps] # See note in `src/OpenBLAS_jll.jl` about this dependency. diff --git a/stdlib/OpenBLAS_jll/test/runtests.jl b/stdlib/OpenBLAS_jll/test/runtests.jl index 1d944bab8cd67..76242b2e4080e 100644 --- a/stdlib/OpenBLAS_jll/test/runtests.jl +++ b/stdlib/OpenBLAS_jll/test/runtests.jl @@ -13,5 +13,5 @@ else end @testset "OpenBLAS_jll" begin - @test dlsym(OpenBLAS_jll.libopenblas_handle, @blasfunc(openblas_set_num_threads); throw_error=false) != nothing + @test dlsym(OpenBLAS_jll.libopenblas_handle, @blasfunc(openblas_set_num_threads); throw_error=false) !== nothing end diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 964c43dfcc786..60d2914b7f853 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 7aef1f044f3483e8b07d33fb4cfe918be554de69 +PKG_SHA1 = 43e7849ce37545493d0da3226cd7449f5f88563e 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/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index 55352cc4b3a9c..799f23034b9ac 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -1250,8 +1250,10 @@ end """ - Profile.take_heap_snapshot(filepath::String, all_one::Bool=false, streaming=false) - Profile.take_heap_snapshot(all_one::Bool=false; dir::String, streaming=false) + Profile.take_heap_snapshot(filepath::String, all_one::Bool=false; + redact_data::Bool=true, streaming::Bool=false) + Profile.take_heap_snapshot(all_one::Bool=false; redact_data:Bool=true, + dir::String=nothing, streaming::Bool=false) Write a snapshot of the heap, in the JSON format expected by the Chrome Devtools Heap Snapshot viewer (.heapsnapshot extension) to a file @@ -1262,6 +1264,8 @@ full file path, or IO stream. If `all_one` is true, then report the size of every object as one so they can be easily counted. Otherwise, report the actual size. +If `redact_data` is true (default), then do not emit the contents of any object. + If `streaming` is true, we will stream the snapshot data out into four files, using filepath as the prefix, to avoid having to hold the entire snapshot in memory. This option should be used for any setting where your memory is constrained. These files can then be reassembled @@ -1277,28 +1281,28 @@ backwards-compatibility) and your process is killed, note that this will always parts in the same directory as your provided filepath, so you can still reconstruct the snapshot after the fact, via `assemble_snapshot()`. """ -function take_heap_snapshot(filepath::AbstractString, all_one::Bool=false; streaming::Bool=false) +function take_heap_snapshot(filepath::AbstractString, all_one::Bool=false; redact_data::Bool=true, streaming::Bool=false) if streaming - _stream_heap_snapshot(filepath, all_one) + _stream_heap_snapshot(filepath, all_one, redact_data) else # Support the legacy, non-streaming mode, by first streaming the parts, then # reassembling it after we're done. prefix = filepath - _stream_heap_snapshot(prefix, all_one) + _stream_heap_snapshot(prefix, all_one, redact_data) Profile.HeapSnapshot.assemble_snapshot(prefix, filepath) Profile.HeapSnapshot.cleanup_streamed_files(prefix) end return filepath end -function take_heap_snapshot(io::IO, all_one::Bool=false) +function take_heap_snapshot(io::IO, all_one::Bool=false; redact_data::Bool=true) # Support the legacy, non-streaming mode, by first streaming the parts to a tempdir, # then reassembling it after we're done. dir = tempdir() prefix = joinpath(dir, "snapshot") - _stream_heap_snapshot(prefix, all_one) + _stream_heap_snapshot(prefix, all_one, redact_data) Profile.HeapSnapshot.assemble_snapshot(prefix, io) end -function _stream_heap_snapshot(prefix::AbstractString, all_one::Bool) +function _stream_heap_snapshot(prefix::AbstractString, all_one::Bool, redact_data::Bool) # Nodes and edges are binary files open("$prefix.nodes", "w") do nodes open("$prefix.edges", "w") do edges @@ -1311,9 +1315,9 @@ function _stream_heap_snapshot(prefix::AbstractString, all_one::Bool) Base.@_lock_ios(json, ccall(:jl_gc_take_heap_snapshot, Cvoid, - (Ptr{Cvoid},Ptr{Cvoid},Ptr{Cvoid},Ptr{Cvoid}, Cchar), + (Ptr{Cvoid},Ptr{Cvoid},Ptr{Cvoid},Ptr{Cvoid}, Cchar, Cchar), nodes.handle, edges.handle, strings.handle, json.handle, - Cchar(all_one)) + Cchar(all_one), Cchar(redact_data)) ) ) ) @@ -1323,7 +1327,7 @@ function _stream_heap_snapshot(prefix::AbstractString, all_one::Bool) end end end -function take_heap_snapshot(all_one::Bool=false; dir::Union{Nothing,S}=nothing) where {S <: AbstractString} +function take_heap_snapshot(all_one::Bool=false; dir::Union{Nothing,S}=nothing, kwargs...) where {S <: AbstractString} fname = "$(getpid())_$(time_ns()).heapsnapshot" if isnothing(dir) wd = pwd() @@ -1338,7 +1342,7 @@ function take_heap_snapshot(all_one::Bool=false; dir::Union{Nothing,S}=nothing) else fpath = joinpath(expanduser(dir), fname) end - return take_heap_snapshot(fpath, all_one) + return take_heap_snapshot(fpath, all_one; kwargs...) end """ diff --git a/stdlib/Profile/test/runtests.jl b/stdlib/Profile/test/runtests.jl index cbfdde61d7054..32d628130c4ac 100644 --- a/stdlib/Profile/test/runtests.jl +++ b/stdlib/Profile/test/runtests.jl @@ -172,8 +172,10 @@ let cmd = Base.julia_cmd() t = Timer(120) do t # should be under 10 seconds, so give it 2 minutes then report failure println("KILLING debuginfo registration test BY PROFILE TEST WATCHDOG\n") - kill(p, Base.SIGTERM) - sleep(10) + kill(p, Base.SIGQUIT) + sleep(30) + kill(p, Base.SIGQUIT) + sleep(30) kill(p, Base.SIGKILL) end s = read(p, String) @@ -202,8 +204,10 @@ if Sys.isbsd() || Sys.islinux() t = Timer(120) do t # should be under 10 seconds, so give it 2 minutes then report failure println("KILLING siginfo/sigusr1 test BY PROFILE TEST WATCHDOG\n") - kill(p, Base.SIGTERM) - sleep(10) + kill(p, Base.SIGQUIT) + sleep(30) + kill(p, Base.SIGQUIT) + sleep(30) kill(p, Base.SIGKILL) close(notify_exit) end @@ -275,16 +279,31 @@ end @testset "HeapSnapshot" begin tmpdir = mktempdir() + + # ensure that we can prevent redacting data fname = cd(tmpdir) do - read(`$(Base.julia_cmd()) --startup-file=no -e "using Profile; print(Profile.take_heap_snapshot())"`, String) + read(`$(Base.julia_cmd()) --startup-file=no -e "using Profile; const x = \"redact_this\"; print(Profile.take_heap_snapshot(; redact_data=false))"`, String) end @test isfile(fname) - open(fname) do fs - @test readline(fs) != "" + sshot = read(fname, String) + @test sshot != "" + @test contains(sshot, "redact_this") + + rm(fname) + + # ensure that string data is redacted by default + fname = cd(tmpdir) do + read(`$(Base.julia_cmd()) --startup-file=no -e "using Profile; const x = \"redact_this\"; print(Profile.take_heap_snapshot())"`, String) end + @test isfile(fname) + + sshot = read(fname, String) + @test sshot != "" + @test !contains(sshot, "redact_this") + rm(fname) rm(tmpdir, force = true, recursive = true) end diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 67f5860082c8a..585ff1aa775b7 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -329,7 +329,7 @@ function warn_on_non_owning_accesses(current_mod, ast) end return ast end -warn_on_non_owning_accesses(ast) = warn_on_non_owning_accesses(REPL.active_module(), ast) +warn_on_non_owning_accesses(ast) = warn_on_non_owning_accesses(Base.active_module(), ast) const repl_ast_transforms = Any[softscope, warn_on_non_owning_accesses] # defaults for new REPL backends @@ -497,7 +497,7 @@ end function display(d::REPLDisplay, mime::MIME"text/plain", x) x = Ref{Any}(x) with_repl_linfo(d.repl) do io - io = IOContext(io, :limit => true, :module => active_module(d)::Module) + io = IOContext(io, :limit => true, :module => Base.active_module(d)::Module) if d.repl isa LineEditREPL mistate = d.repl.mistate mode = LineEdit.mode(mistate) @@ -527,7 +527,7 @@ show_repl(io::IO, ::MIME"text/plain", ex::Expr) = function print_response(repl::AbstractREPL, response, show_value::Bool, have_color::Bool) repl.waserror = response[2] with_repl_linfo(repl) do io - io = IOContext(io, :module => active_module(repl)::Module) + io = IOContext(io, :module => Base.active_module(repl)::Module) print_response(io, response, show_value, have_color, specialdisplay(repl)) end return nothing @@ -628,7 +628,7 @@ function run_repl(repl::AbstractREPL, @nospecialize(consumer = x -> nothing); ba Core.println(Core.stderr, e) Core.println(Core.stderr, catch_backtrace()) end - get_module = () -> active_module(repl) + get_module = () -> Base.active_module(repl) if backend_on_current_task t = @async run_frontend(repl, backend_ref) errormonitor(t) @@ -760,14 +760,9 @@ REPLCompletionProvider() = REPLCompletionProvider(LineEdit.Modifiers()) mutable struct ShellCompletionProvider <: CompletionProvider end struct LatexCompletions <: CompletionProvider end -function active_module() # this method is also called from Base - isdefined(Base, :active_repl) || return Main - Base.active_repl === nothing && return Main - return active_module(Base.active_repl::AbstractREPL) -end -active_module((; mistate)::LineEditREPL) = mistate === nothing ? Main : mistate.active_module -active_module(::AbstractREPL) = Main -active_module(d::REPLDisplay) = active_module(d.repl) +Base.active_module((; mistate)::LineEditREPL) = mistate === nothing ? Main : mistate.active_module +Base.active_module(::AbstractREPL) = Main +Base.active_module(d::REPLDisplay) = Base.active_module(d.repl) setmodifiers!(c::CompletionProvider, m::LineEdit.Modifiers) = nothing @@ -1206,7 +1201,7 @@ enable_promptpaste(v::Bool) = JL_PROMPT_PASTE[] = v function contextual_prompt(repl::LineEditREPL, prompt::Union{String,Function}) function () - mod = active_module(repl) + mod = Base.active_module(repl) prefix = mod == Main ? "" : string('(', mod, ") ") pr = prompt isa String ? prompt : prompt() prefix * pr diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index dc21cfe529e46..77f7fdf15cc9c 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -603,7 +603,7 @@ is_repl_frame(sv::CC.InferenceState) = sv.linfo.def isa Module && sv.cache_mode function is_call_graph_uncached(sv::CC.InferenceState) CC.is_cached(sv) && return false - parent = sv.parent + parent = CC.frame_parent(sv) parent === nothing && return true return is_call_graph_uncached(parent::CC.InferenceState) end @@ -626,7 +626,7 @@ function is_repl_frame_getproperty(sv::CC.InferenceState) def isa Method || return false def.name === :getproperty || return false CC.is_cached(sv) && return false - return is_repl_frame(sv.parent) + return is_repl_frame(CC.frame_parent(sv)) end # aggressive global binding resolution for `getproperty(::Module, ::Symbol)` calls within `repl_frame` diff --git a/stdlib/Serialization/src/Serialization.jl b/stdlib/Serialization/src/Serialization.jl index 7600457812f66..bc476181e5b0d 100644 --- a/stdlib/Serialization/src/Serialization.jl +++ b/stdlib/Serialization/src/Serialization.jl @@ -1570,11 +1570,11 @@ function deserialize(s::AbstractSerializer, ::Type{Task}) t.storage = deserialize(s) state = deserialize(s) if state === :runnable - t._state = Base.task_state_runnable + @atomic :release t._state = Base.task_state_runnable elseif state === :done - t._state = Base.task_state_done + @atomic :release t._state = Base.task_state_done elseif state === :failed - t._state = Base.task_state_failed + @atomic :release t._state = Base.task_state_failed else @assert false end diff --git a/stdlib/Sockets/src/Sockets.jl b/stdlib/Sockets/src/Sockets.jl index 5baf8826cc883..3c30b214305fb 100644 --- a/stdlib/Sockets/src/Sockets.jl +++ b/stdlib/Sockets/src/Sockets.jl @@ -450,7 +450,7 @@ function send(sock::UDPSocket, ipaddr::IPAddr, port::Integer, msg) finally Base.sigatomic_end() iolock_begin() - ct.queue === nothing || Base.list_deletefirst!(ct.queue, ct) + q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) if uv_req_data(uvw) != C_NULL # uvw is still alive, # so make sure we won't get spurious notifications later diff --git a/stdlib/Sockets/src/addrinfo.jl b/stdlib/Sockets/src/addrinfo.jl index 4ee9e07a58430..866a1684c85a1 100644 --- a/stdlib/Sockets/src/addrinfo.jl +++ b/stdlib/Sockets/src/addrinfo.jl @@ -90,7 +90,7 @@ function getalladdrinfo(host::String) finally Base.sigatomic_end() iolock_begin() - ct.queue === nothing || Base.list_deletefirst!(ct.queue, ct) + q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) if uv_req_data(req) != C_NULL # req is still alive, # so make sure we don't get spurious notifications later @@ -223,7 +223,7 @@ function getnameinfo(address::Union{IPv4, IPv6}) finally Base.sigatomic_end() iolock_begin() - ct.queue === nothing || Base.list_deletefirst!(ct.queue, ct) + q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct) if uv_req_data(req) != C_NULL # req is still alive, # so make sure we don't get spurious notifications later diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 8b4a1d9113940..f655d9abe423f 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -1437,14 +1437,30 @@ using .Main.OffsetArrays end @testset "Check push!($a, $args...)" for - a in (["foo", "Bar"], SimpleArray(["foo", "Bar"]), OffsetVector(["foo", "Bar"], 0:1)), + a in (["foo", "Bar"], SimpleArray(["foo", "Bar"]), SimpleArray{Any}(["foo", "Bar"]), OffsetVector(["foo", "Bar"], 0:1)), args in (("eenie",), ("eenie", "minie"), ("eenie", "minie", "mo")) orig = copy(a) push!(a, args...) @test length(a) == length(orig) + length(args) + @test a[axes(orig,1)] == orig @test all(a[end-length(args)+1:end] .== args) end +@testset "Check append!($a, $args)" for + a in (["foo", "Bar"], SimpleArray(["foo", "Bar"]), SimpleArray{Any}(["foo", "Bar"]), OffsetVector(["foo", "Bar"], 0:1)), + args in (("eenie",), ("eenie", "minie"), ("eenie", "minie", "mo")) + orig = copy(a) + append!(a, args) + @test length(a) == length(orig) + length(args) + @test a[axes(orig,1)] == orig + @test all(a[end-length(args)+1:end] .== args) +end + +@testset "Check sizehint!($a)" for + a in (["foo", "Bar"], SimpleArray(["foo", "Bar"]), SimpleArray{Any}(["foo", "Bar"]), OffsetVector(["foo", "Bar"], 0:1)) + @test sizehint!(a, 10) === a +end + @testset "splatting into hvcat" begin t = (1, 2) @test [t...; 3 4] == [1 2; 3 4] diff --git a/test/arrayops.jl b/test/arrayops.jl index f58fdb36942a2..333b68e287c4c 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -591,32 +591,32 @@ end @test findall(!, m) == [k for (k,v) in pairs(m) if !v] @test findfirst(!iszero, a) == 2 @test findfirst(a.==0) == 1 - @test findfirst(a.==5) == nothing + @test findfirst(a.==5) === nothing @test findfirst(Dict(1=>false, 2=>true)) == 2 - @test findfirst(Dict(1=>false)) == nothing + @test findfirst(Dict(1=>false)) === nothing @test findfirst(isequal(3), [1,2,4,1,2,3,4]) == 6 @test findfirst(!isequal(1), [1,2,4,1,2,3,4]) == 2 @test findfirst(isodd, [2,4,6,3,9,2,0]) == 4 - @test findfirst(isodd, [2,4,6,2,0]) == nothing + @test findfirst(isodd, [2,4,6,2,0]) === nothing @test findnext(!iszero,a,4) == 4 @test findnext(!iszero,a,5) == 6 @test findnext(!iszero,a,1) == 2 @test findnext(isequal(1),a,4) == 6 - @test findnext(isequal(5),a,4) == nothing + @test findnext(isequal(5),a,4) === nothing @test findlast(!iszero, a) == 8 @test findlast(a.==0) == 5 - @test findlast(a.==5) == nothing - @test findlast(false) == nothing # test non-AbstractArray findlast + @test findlast(a.==5) === nothing + @test findlast(false) === nothing # test non-AbstractArray findlast @test findlast(isequal(3), [1,2,4,1,2,3,4]) == 6 @test findlast(isodd, [2,4,6,3,9,2,0]) == 5 - @test findlast(isodd, [2,4,6,2,0]) == nothing + @test findlast(isodd, [2,4,6,2,0]) === nothing @test findprev(!iszero,a,4) == 4 @test findprev(!iszero,a,5) == 4 - @test findprev(!iszero,a,1) == nothing + @test findprev(!iszero,a,1) === nothing @test findprev(isequal(1),a,4) == 2 @test findprev(isequal(1),a,8) == 6 @test findprev(isodd, [2,4,5,3,9,2,0], 7) == 5 - @test findprev(isodd, [2,4,5,3,9,2,0], 2) == nothing + @test findprev(isodd, [2,4,5,3,9,2,0], 2) === nothing @test findfirst(isequal(0x00), [0x01, 0x00]) == 2 @test findlast(isequal(0x00), [0x01, 0x00]) == 2 @test findnext(isequal(0x00), [0x00, 0x01, 0x00], 2) == 3 diff --git a/test/atomics.jl b/test/atomics.jl index 165c8dc4e2dfc..adfe4c87138cd 100644 --- a/test/atomics.jl +++ b/test/atomics.jl @@ -319,6 +319,7 @@ test_field_orderings(ARefxy{Union{Nothing,Missing}}(nothing, missing), nothing, test_field_orderings(ARefxy{Union{Nothing,Int}}(nothing, 123_1), nothing, 123_1) test_field_orderings(Complex{Int128}(10, 30), Complex{Int128}(20, 40)) test_field_orderings(Complex{Real}(10, 30), Complex{Real}(20, 40)) +test_field_orderings(Complex{Rational{Integer}}(10, 30), Complex{Rational{Integer}}(20, 40)) test_field_orderings(10.0, 20.0) test_field_orderings(NaN, Inf) diff --git a/test/bitarray.jl b/test/bitarray.jl index 2cf285370441e..67d8fae0eda6d 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -1357,11 +1357,11 @@ timesofar("find") @test findprev(b1, 777) == findprevnot(b2, 777) == findprev(!, b2, 777) == 777 @test findprev(b1, 776) == findprevnot(b2, 776) == findprev(!, b2, 776) == 77 @test findprev(b1, 77) == findprevnot(b2, 77) == findprev(!, b2, 77) == 77 - @test findprev(b1, 76) == findprevnot(b2, 76) == findprev(!, b2, 76) == nothing - @test findprev(b1, -1) == findprevnot(b2, -1) == findprev(!, b2, -1) == nothing - @test findprev(identity, b1, -1) == nothing - @test findprev(Returns(false), b1, -1) == nothing - @test findprev(Returns(true), b1, -1) == nothing + @test findprev(b1, 76) == findprevnot(b2, 76) == findprev(!, b2, 76) === nothing + @test findprev(b1, -1) == findprevnot(b2, -1) == findprev(!, b2, -1) === nothing + @test findprev(identity, b1, -1) === nothing + @test findprev(Returns(false), b1, -1) === nothing + @test findprev(Returns(true), b1, -1) === nothing @test_throws BoundsError findnext(b1, -1) @test_throws BoundsError findnextnot(b2, -1) @test_throws BoundsError findnext(!, b2, -1) @@ -1372,28 +1372,28 @@ timesofar("find") @test findnext(b1, 77) == findnextnot(b2, 77) == findnext(!, b2, 77) == 77 @test findnext(b1, 78) == findnextnot(b2, 78) == findnext(!, b2, 78) == 777 @test findnext(b1, 777) == findnextnot(b2, 777) == findnext(!, b2, 777) == 777 - @test findnext(b1, 778) == findnextnot(b2, 778) == findnext(!, b2, 778) == nothing - @test findnext(b1, 1001) == findnextnot(b2, 1001) == findnext(!, b2, 1001) == nothing - @test findnext(identity, b1, 1001) == findnext(Returns(false), b1, 1001) == findnext(Returns(true), b1, 1001) == nothing + @test findnext(b1, 778) == findnextnot(b2, 778) == findnext(!, b2, 778) === nothing + @test findnext(b1, 1001) == findnextnot(b2, 1001) == findnext(!, b2, 1001) === nothing + @test findnext(identity, b1, 1001) == findnext(Returns(false), b1, 1001) == findnext(Returns(true), b1, 1001) === nothing @test findlast(b1) == Base.findlastnot(b2) == 777 @test findfirst(b1) == Base.findfirstnot(b2) == 77 b0 = BitVector() - @test findprev(Returns(true), b0, -1) == nothing + @test findprev(Returns(true), b0, -1) === nothing @test_throws BoundsError findprev(Returns(true), b0, 1) @test_throws BoundsError findnext(Returns(true), b0, -1) - @test findnext(Returns(true), b0, 1) == nothing + @test findnext(Returns(true), b0, 1) === nothing b1 = falses(10) @test findprev(Returns(true), b1, 5) == 5 @test findnext(Returns(true), b1, 5) == 5 - @test findprev(Returns(true), b1, -1) == nothing - @test findnext(Returns(true), b1, 11) == nothing - @test findprev(Returns(false), b1, 5) == nothing - @test findnext(Returns(false), b1, 5) == nothing - @test findprev(Returns(false), b1, -1) == nothing - @test findnext(Returns(false), b1, 11) == nothing + @test findprev(Returns(true), b1, -1) === nothing + @test findnext(Returns(true), b1, 11) === nothing + @test findprev(Returns(false), b1, 5) === nothing + @test findnext(Returns(false), b1, 5) === nothing + @test findprev(Returns(false), b1, -1) === nothing + @test findnext(Returns(false), b1, 11) === nothing @test_throws BoundsError findprev(Returns(true), b1, 11) @test_throws BoundsError findnext(Returns(true), b1, -1) @@ -1415,7 +1415,7 @@ timesofar("find") for l = [1, 63, 64, 65, 127, 128, 129] f = falses(l) t = trues(l) - @test findprev(f, l) == findprevnot(t, l) == nothing + @test findprev(f, l) == findprevnot(t, l) === nothing @test findprev(t, l) == findprevnot(f, l) == l b1 = falses(l) b1[end] = true diff --git a/test/channels.jl b/test/channels.jl index f1642de1b7bec..d62c0b581775c 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -382,7 +382,7 @@ end """error in running finalizer: ErrorException("task switch not allowed from inside gc finalizer")""", output)) # test for invalid state in Workqueue during yield t = @async nothing - t._state = 66 + @atomic t._state = 66 newstderr = redirect_stderr() try errstream = @async read(newstderr[1], String) @@ -500,7 +500,7 @@ end c = Channel(1) close(c) @test !isopen(c) - c.excp == nothing # to trigger the branch + c.excp === nothing # to trigger the branch @test_throws InvalidStateException Base.check_channel_state(c) end diff --git a/test/char.jl b/test/char.jl index 5da92121b1630..3100add0e81c5 100644 --- a/test/char.jl +++ b/test/char.jl @@ -121,7 +121,7 @@ end #iterate(c::Char) for x in testarrays @test iterate(x)[1] == x - @test iterate(x, iterate(x)[2]) == nothing + @test iterate(x, iterate(x)[2]) === nothing end #isless(x::Char, y::Integer) = isless(UInt32(x), y) diff --git a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl index d8ea8be21fe07..31c21f7228014 100644 --- a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl +++ b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl @@ -2139,21 +2139,13 @@ end # ======================== # propagate escapes imposed on call arguments -@noinline broadcast_noescape1(a) = (broadcast(identity, a); nothing) -let result = code_escapes() do - broadcast_noescape1(Ref("Hi")) - end - i = only(findall(isnew, result.ir.stmts.stmt)) - @test !has_return_escape(result.state[SSAValue(i)]) - @test_broken !has_thrown_escape(result.state[SSAValue(i)]) # TODO `getfield(RefValue{String}, :x)` isn't safe -end @noinline broadcast_noescape2(b) = broadcast(identity, b) let result = code_escapes() do broadcast_noescape2(Ref("Hi")) end i = only(findall(isnew, result.ir.stmts.stmt)) @test_broken !has_return_escape(result.state[SSAValue(i)]) # TODO interprocedural alias analysis - @test_broken !has_thrown_escape(result.state[SSAValue(i)]) # TODO `getfield(RefValue{String}, :x)` isn't safe + @test !has_thrown_escape(result.state[SSAValue(i)]) end @noinline allescape_argument(a) = (global GV = a) # obvious escape let result = code_escapes() do diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index c74dfbb29d3dd..0260113044a3b 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -440,7 +440,7 @@ function f1_30093(r) end end -@test f1_30093(Ref(0)) == nothing +@test f1_30093(Ref(0)) === nothing # issue 33590 function f33590(b, x) @@ -966,3 +966,36 @@ end let x = Incomplete55396(55396) @test x.x === (55396,) end + +# Core.getptls() special handling +@test !occursin("call ptr @jlplt", get_llvm(Core.getptls, Tuple{})) #It should lower to a direct load of the ptls and not a ccall + +# issue 55208 +@noinline function f55208(x, i) + z = (i == 0 ? x[1] : x[i]) + return z isa Core.TypeofBottom +end +@test f55208((Union{}, 5, 6, 7), 0) + +@noinline function g55208(x, i) + z = (i == 0 ? x[1] : x[i]) + typeof(z) +end +@test g55208((Union{}, true, true), 0) === typeof(Union{}) + +@test string((Core.Union{}, true, true, true)) == "(Union{}, true, true, true)" + +# Issue #55558 +for (T, StructName) in ((Int128, :Issue55558), (UInt128, :UIssue55558)) + @eval begin + struct $(StructName) + a::$(T) + b::Int64 + c::$(T) + end + local broken_i128 = Base.BinaryPlatforms.arch(Base.BinaryPlatforms.HostPlatform()) == "powerpc64le" + @test fieldoffset($(StructName), 2) == 16 + @test fieldoffset($(StructName), 3) == 32 broken=broken_i128 + @test sizeof($(StructName)) == 48 broken=broken_i128 + end +end diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index a27d52d68b9a9..11c30aad0b9a4 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -1361,3 +1361,8 @@ end |> Core.Compiler.is_nothrow @test Base.infer_effects((Vector{Any},)) do xs Core.svec(xs...) end |> Core.Compiler.is_nothrow + +# effects for unknown `:foreigncall`s +@test Base.infer_effects() do + @ccall unsafecall()::Cvoid +end == Core.Compiler.EFFECTS_UNKNOWN diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 9ae98b884bef4..485ee579abd52 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1065,7 +1065,7 @@ gl_17003 = [1, 2, 3] f2_17003(item::AVector_17003) = nothing f2_17003(::Any) = f2_17003(NArray_17003(gl_17003)) -@test f2_17003(1) == nothing +@test f2_17003(1) === nothing # issue #20847 function segfaultfunction_20847(A::Vector{NTuple{N, T}}) where {N, T} @@ -1076,7 +1076,7 @@ end tuplevec_20847 = Tuple{Float64, Float64}[(0.0,0.0), (1.0,0.0)] for A in (1,) - @test segfaultfunction_20847(tuplevec_20847) == nothing + @test segfaultfunction_20847(tuplevec_20847) === nothing end # Issue #20902, check that this doesn't error. @@ -1538,7 +1538,7 @@ let nfields_tfunc(@nospecialize xs...) = @test sizeof_nothrow(String) @test !sizeof_nothrow(Type{String}) @test sizeof_tfunc(Type{Union{Int64, Int32}}) == Const(Core.sizeof(Union{Int64, Int32})) - let PT = Core.Compiler.PartialStruct(Tuple{Int64,UInt64}, Any[Const(10), UInt64]) + let PT = Core.PartialStruct(Tuple{Int64,UInt64}, Any[Const(10), UInt64]) @test sizeof_tfunc(PT) === Const(16) @test nfields_tfunc(PT) === Const(2) @test sizeof_nothrow(PT) @@ -4743,32 +4743,80 @@ end # issue #43784 @testset "issue #43784" begin - init = Base.ImmutableDict{Any,Any}() - a = Const(init) - b = Core.PartialStruct(typeof(init), Any[Const(init), Any, Any]) - c = Core.Compiler.tmerge(a, b) - @test βŠ‘(a, c) - @test βŠ‘(b, c) - - init = Base.ImmutableDict{Number,Number}() - a = Const(init) - b = Core.Compiler.PartialStruct(typeof(init), Any[Const(init), Any, ComplexF64]) - c = Core.Compiler.tmerge(a, b) - @test βŠ‘(a, c) && βŠ‘(b, c) - @test c === typeof(init) - - a = Core.Compiler.PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64]) - c = Core.Compiler.tmerge(a, b) - @test βŠ‘(a, c) && βŠ‘(b, c) - @test c.fields[2] === Any # or Number - @test c.fields[3] === ComplexF64 - - b = Core.Compiler.PartialStruct(typeof(init), Any[Const(init), ComplexF32, Union{ComplexF32,ComplexF64}]) - c = Core.Compiler.tmerge(a, b) - @test βŠ‘(a, c) - @test βŠ‘(b, c) - @test c.fields[2] === Complex - @test c.fields[3] === Complex + βŠ‘ = Core.Compiler.partialorder(Core.Compiler.fallback_lattice) + βŠ” = Core.Compiler.join(Core.Compiler.fallback_lattice) + Const, PartialStruct = Core.Const, Core.PartialStruct + + let init = Base.ImmutableDict{Any,Any}() + a = Const(init) + b = PartialStruct(typeof(init), Any[Const(init), Any, Any]) + c = a βŠ” b + @test a βŠ‘ c && b βŠ‘ c + @test c === typeof(init) + end + let init = Base.ImmutableDict{Any,Any}(1,2) + a = Const(init) + b = PartialStruct(typeof(init), Any[Const(getfield(init,1)), Any, Any]) + c = a βŠ” b + @test a βŠ‘ c && b βŠ‘ c + @test c isa PartialStruct + @test length(c.fields) == 3 + end + let init = Base.ImmutableDict{Number,Number}() + a = Const(init) + b = PartialStruct(typeof(init), Any[Const(init), Number, ComplexF64]) + c = a βŠ” b + @test a βŠ‘ c && b βŠ‘ c + @test c === typeof(init) + end + let init = Base.ImmutableDict{Number,Number}() + a = PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64]) + b = PartialStruct(typeof(init), Any[Const(init), Number, ComplexF64]) + c = a βŠ” b + @test a βŠ‘ c && b βŠ‘ c + @test c isa PartialStruct + @test c.fields[2] === Number + @test c.fields[3] === ComplexF64 + end + let init = Base.ImmutableDict{Number,Number}() + a = PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64]) + b = PartialStruct(typeof(init), Any[Const(init), ComplexF32, Union{ComplexF32,ComplexF64}]) + c = a βŠ” b + @test a βŠ‘ c && b βŠ‘ c + @test c isa PartialStruct + @test c.fields[2] === Complex + @test c.fields[3] === Complex + end + let T = Base.ImmutableDict{Number,Number} + a = PartialStruct(T, Any[T]) + b = PartialStruct(T, Any[T, Number, Number]) + @test b βŠ‘ a + c = a βŠ” b + @test a βŠ‘ c && b βŠ‘ c + @test c isa PartialStruct + @test length(c.fields) == 1 + end + let T = Base.ImmutableDict{Number,Number} + a = PartialStruct(T, Any[T]) + b = Const(T()) + c = a βŠ” b + @test a βŠ‘ c && b βŠ‘ c + @test c === T + end + let T = Base.ImmutableDict{Number,Number} + a = Const(T()) + b = PartialStruct(T, Any[T]) + c = a βŠ” b + @test a βŠ‘ c && b βŠ‘ c + @test c === T + end + let T = Base.ImmutableDict{Number,Number} + a = Const(T()) + b = Const(T(1,2)) + c = a βŠ” b + @test a βŠ‘ c && b βŠ‘ c + @test c === T + end global const ginit43784 = Base.ImmutableDict{Any,Any}() @test Base.return_types() do @@ -4802,6 +4850,31 @@ end @test a == Tuple end +let βŠ‘ = Core.Compiler.partialorder(Core.Compiler.fallback_lattice) + βŠ” = Core.Compiler.join(Core.Compiler.fallback_lattice) + Const, PartialStruct = Core.Const, Core.PartialStruct + + @test (Const((1,2)) βŠ‘ PartialStruct(Tuple{Int,Int}, Any[Const(1),Int])) + @test !(Const((1,2)) βŠ‘ PartialStruct(Tuple{Int,Int,Int}, Any[Const(1),Int,Int])) + @test !(Const((1,2,3)) βŠ‘ PartialStruct(Tuple{Int,Int}, Any[Const(1),Int])) + @test (Const((1,2,3)) βŠ‘ PartialStruct(Tuple{Int,Int,Int}, Any[Const(1),Int,Int])) + @test (Const((1,2)) βŠ‘ PartialStruct(Tuple{Int,Vararg{Int}}, Any[Const(1),Vararg{Int}])) + @test (Const((1,2)) βŠ‘ PartialStruct(Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}])) broken=true + @test (Const((1,2,3)) βŠ‘ PartialStruct(Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}])) + @test !(PartialStruct(Tuple{Int,Int}, Any[Const(1),Int]) βŠ‘ Const((1,2))) + @test !(PartialStruct(Tuple{Int,Int,Int}, Any[Const(1),Int,Int]) βŠ‘ Const((1,2))) + @test !(PartialStruct(Tuple{Int,Int}, Any[Const(1),Int]) βŠ‘ Const((1,2,3))) + @test !(PartialStruct(Tuple{Int,Int,Int}, Any[Const(1),Int,Int]) βŠ‘ Const((1,2,3))) + @test !(PartialStruct(Tuple{Int,Vararg{Int}}, Any[Const(1),Vararg{Int}]) βŠ‘ Const((1,2))) + @test !(PartialStruct(Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}]) βŠ‘ Const((1,2))) + @test !(PartialStruct(Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}]) βŠ‘ Const((1,2,3))) + + t = Const((false, false)) βŠ” Const((false, true)) + @test t isa PartialStruct && length(t.fields) == 2 && t.fields[1] === Const(false) + t = t βŠ” Const((false, false, 0)) + @test t βŠ‘ Union{Tuple{Bool,Bool},Tuple{Bool,Bool,Int}} +end + # Test that a function-wise `@max_methods` works as expected Base.Experimental.@max_methods 1 function f_max_methods end f_max_methods(x::Int) = 1 @@ -5867,6 +5940,138 @@ bar54341(args...) = foo54341(4, args...) @test Core.Compiler.return_type(bar54341, Tuple{Vararg{Int}}) === Int +# `PartialStruct` for partially initialized structs: +struct PartiallyInitialized1 + a; b; c + PartiallyInitialized1(a) = (@nospecialize; new(a)) + PartiallyInitialized1(a, b) = (@nospecialize; new(a, b)) + PartiallyInitialized1(a, b, c) = (@nospecialize; new(a, b, c)) +end +mutable struct PartiallyInitialized2 + a; b; c + PartiallyInitialized2(a) = (@nospecialize; new(a)) + PartiallyInitialized2(a, b) = (@nospecialize; new(a, b)) + PartiallyInitialized2(a, b, c) = (@nospecialize; new(a, b, c)) +end + +# 1. isdefined modeling for partial struct +@test Base.infer_return_type((Any,Any)) do a, b + Val(isdefined(PartiallyInitialized1(a, b), :b)) +end == Val{true} +@test Base.infer_return_type((Any,Any,)) do a, b + Val(isdefined(PartiallyInitialized1(a, b), :c)) +end >: Val{false} +@test Base.infer_return_type((PartiallyInitialized1,)) do x + @assert isdefined(x, :a) + return Val(isdefined(x, :c)) +end == Val +@test Base.infer_return_type((Any,Any,Any)) do a, b, c + Val(isdefined(PartiallyInitialized1(a, b, c), :c)) +end == Val{true} +@test Base.infer_return_type((Any,Any)) do a, b + Val(isdefined(PartiallyInitialized2(a, b), :b)) +end == Val{true} +@test Base.infer_return_type((Any,Any,)) do a, b + Val(isdefined(PartiallyInitialized2(a, b), :c)) +end >: Val{false} +@test Base.infer_return_type((Any,Any,Any)) do a, b, c + s = PartiallyInitialized2(a, b) + s.c = c + Val(isdefined(s, :c)) +end >: Val{true} +@test Base.infer_return_type((Any,Any,Any)) do a, b, c + Val(isdefined(PartiallyInitialized2(a, b, c), :c)) +end == Val{true} +@test Base.infer_return_type((Vector{Int},)) do xs + Val(isdefined(tuple(1, xs...), 1)) +end == Val{true} +@test Base.infer_return_type((Vector{Int},)) do xs + Val(isdefined(tuple(1, xs...), 2)) +end == Val + +# 2. getfield modeling for partial struct +@test Base.infer_effects((Any,Any); optimize=false) do a, b + getfield(PartiallyInitialized1(a, b), :b) +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any,Symbol,); optimize=false) do a, b, f + getfield(PartiallyInitialized1(a, b), f, #=boundscheck=#false) +end |> !Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any,Any); optimize=false) do a, b, c + getfield(PartiallyInitialized1(a, b, c), :c) +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any,Any,Symbol); optimize=false) do a, b, c, f + getfield(PartiallyInitialized1(a, b, c), f, #=boundscheck=#false) +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any); optimize=false) do a, b + getfield(PartiallyInitialized2(a, b), :b) +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any,Symbol,); optimize=false) do a, b, f + getfield(PartiallyInitialized2(a, b), f, #=boundscheck=#false) +end |> !Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any,Any); optimize=false) do a, b, c + getfield(PartiallyInitialized2(a, b, c), :c) +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any,Any,Symbol); optimize=false) do a, b, c, f + getfield(PartiallyInitialized2(a, b, c), f, #=boundscheck=#false) +end |> Core.Compiler.is_nothrow + +# isdefined-Conditionals +@test Base.infer_effects((Base.RefValue{Any},)) do x + if isdefined(x, :x) + return getfield(x, :x) + end +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Base.RefValue{Any},)) do x + if isassigned(x) + return x[] + end +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any); optimize=false) do a, c + x = PartiallyInitialized2(a) + x.c = c + if isdefined(x, :c) + return x.b + end +end |> !Core.Compiler.is_nothrow +@test Base.infer_effects((PartiallyInitialized2,); optimize=false) do x + if isdefined(x, :b) + if isdefined(x, :c) + return x.c + end + return x.b + end + return nothing +end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Bool,Int,); optimize=false) do c, b + x = c ? PartiallyInitialized1(true) : PartiallyInitialized1(true, b) + if isdefined(x, :b) + return Val(x.a), x.b + end + return nothing +end |> Core.Compiler.is_nothrow + +# refine `undef` information from `@isdefined` check +function isdefined_nothrow(c, x) + local val + if c + val = x + end + if @isdefined val + return val + end + return zero(Int) +end +@test Core.Compiler.is_nothrow(Base.infer_effects(isdefined_nothrow, (Bool,Int))) +@test !any(first(only(code_typed(isdefined_nothrow, (Bool,Int)))).code) do @nospecialize x + Meta.isexpr(x, :throw_undef_if_not) +end + +# End to end test case for the partially initialized struct with `PartialStruct` +@noinline broadcast_noescape1(a) = (broadcast(identity, a); nothing) +@test fully_eliminated() do + broadcast_noescape1(Ref("x")) +end + # InterConditional rt with Vararg argtypes fcondvarargs(a, b, c, d) = isa(d, Int64) gcondvarargs(a, x...) = return fcondvarargs(a, x...) ? isa(a, Int64) : !isa(a, Int64) diff --git a/test/compiler/ssair.jl b/test/compiler/ssair.jl index 06258f52cb69c..b7d75d0be5567 100644 --- a/test/compiler/ssair.jl +++ b/test/compiler/ssair.jl @@ -219,7 +219,7 @@ let code = Any[ ] ir = make_ircode(code; verify=false) ir = Core.Compiler.compact!(ir, true) - @test Core.Compiler.verify_ir(ir) == nothing + @test Core.Compiler.verify_ir(ir) === nothing end # issue #37919 diff --git a/test/copy.jl b/test/copy.jl index b6ad53600027a..d2f555604c4d8 100644 --- a/test/copy.jl +++ b/test/copy.jl @@ -198,7 +198,7 @@ end bar = Bar19921(foo, Dict(foo => 3)) bar2 = deepcopy(bar) @test bar2.foo ∈ keys(bar2.fooDict) - @test bar2.fooDict[bar2.foo] != nothing + @test bar2.fooDict[bar2.foo] !== nothing end let d = IdDict(rand(2) => rand(2) for i = 1:100) diff --git a/test/core.jl b/test/core.jl index 4cbb872ce4e50..648dd68602fa5 100644 --- a/test/core.jl +++ b/test/core.jl @@ -42,6 +42,7 @@ for (T, c) in ( (DataType, [:types, :layout]), (Core.Memory, []), (Core.GenericMemoryRef, []), + (Task, [:_state]) ) @test Set((fieldname(T, i) for i in 1:fieldcount(T) if Base.isfieldatomic(T, i))) == Set(c) end @@ -7030,7 +7031,7 @@ translate27368(::Type{Val{name}}) where {name} = # issue #27456 @inline foo27456() = try baz_nonexistent27456(); catch; nothing; end bar27456() = foo27456() -@test bar27456() == nothing +@test bar27456() === nothing # issue #27365 mutable struct foo27365 @@ -8265,3 +8266,5 @@ end @test Tuple{Vararg{Int}} === Union{Tuple{Int}, Tuple{}, Tuple{Int, Int, Vararg{Int}}} @test (Tuple{Vararg{T}} where T) === (Union{Tuple{T, T, Vararg{T}}, Tuple{}, Tuple{T}} where T) @test_broken (Tuple{Vararg{T}} where T) === Union{Tuple{T, T, Vararg{T}} where T, Tuple{}, Tuple{T} where T} + +@test sizeof(Pair{Union{typeof(Union{}),Nothing}, Union{Type{Union{}},Nothing}}(Union{}, Union{})) == 2 diff --git a/test/corelogging.jl b/test/corelogging.jl index 778e70aecd406..b8cd3716cad2e 100644 --- a/test/corelogging.jl +++ b/test/corelogging.jl @@ -140,9 +140,9 @@ end end @test length(logger.logs) == 1 record = logger.logs[1] - @test record._module == nothing - @test record.file == nothing - @test record.line == nothing + @test record._module === nothing + @test record.file === nothing + @test record.line === nothing end # PR #28209 diff --git a/test/dict.jl b/test/dict.jl index e327c86521c88..13c60d5a6a053 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -8,7 +8,7 @@ using Random @test isequal(p,10=>20) @test iterate(p)[1] == 10 @test iterate(p, iterate(p)[2])[1] == 20 - @test iterate(p, iterate(p, iterate(p)[2])[2]) == nothing + @test iterate(p, iterate(p, iterate(p)[2])[2]) === nothing @test firstindex(p) == 1 @test lastindex(p) == length(p) == 2 @test Base.indexed_iterate(p, 1, nothing) == (10,2) @@ -683,9 +683,9 @@ end @inferred setindex!(d, -1, 10) @test d[10] == -1 @test 1 == @inferred d[1] - @test get(d, -111, nothing) == nothing + @test get(d, -111, nothing) === nothing @test 1 == @inferred get(d, 1, 1) - @test pop!(d, -111, nothing) == nothing + @test pop!(d, -111, nothing) === nothing @test 1 == @inferred pop!(d, 1) # get! and delete! diff --git a/test/file.jl b/test/file.jl index 1ab990a70af77..5512f492ca5f9 100644 --- a/test/file.jl +++ b/test/file.jl @@ -1758,8 +1758,18 @@ end @test s.blocks isa Int64 @test s.mtime isa Float64 @test s.ctime isa Float64 + + @test s === stat((f,)) + @test s === lstat((f,)) + @test s === stat(".", f) + @test s === lstat(".", f) end +mutable struct URI50890; f::String; end +Base.joinpath(x::URI50890) = URI50890(x.f) +@test_throws "stat not implemented" stat(URI50890(".")) +@test_throws "lstat not implemented" lstat(URI50890(".")) + @testset "StatStruct show's extended details" begin f, io = mktemp() s = stat(f) diff --git a/test/generic_map_tests.jl b/test/generic_map_tests.jl index b155370dd6465..7f19d60fe31fb 100644 --- a/test/generic_map_tests.jl +++ b/test/generic_map_tests.jl @@ -43,7 +43,7 @@ function generic_map_tests(mapf, inplace_mapf=nothing) @test mapf(f, Int[], Int[], Complex{Int}[]) == Union{}[] # In-place map - if inplace_mapf != nothing + if inplace_mapf !== nothing A = Float64[1:10...] inplace_mapf(x -> x*x, A, A) @test A == map(x -> x*x, Float64[1:10...]) diff --git a/test/intrinsics.jl b/test/intrinsics.jl index e61354fe4f7f3..7a63cd1c0a62e 100644 --- a/test/intrinsics.jl +++ b/test/intrinsics.jl @@ -197,8 +197,8 @@ for order in (:not_atomic, :monotonic, :acquire, :release, :acquire_release, :se @test (order -> Core.Intrinsics.atomic_fence(order))(order) === nothing @test Base.invokelatest(@eval () -> Core.Intrinsics.atomic_fence($(QuoteNode(order)))) === nothing end -@test Core.Intrinsics.atomic_pointerref(C_NULL, :sequentially_consistent) == nothing -@test (@force_compile; Core.Intrinsics.atomic_pointerref(C_NULL, :sequentially_consistent)) == nothing +@test Core.Intrinsics.atomic_pointerref(C_NULL, :sequentially_consistent) === nothing +@test (@force_compile; Core.Intrinsics.atomic_pointerref(C_NULL, :sequentially_consistent)) === nothing primitive type Int256 <: Signed 256 end Int256(i::Int) = Core.Intrinsics.sext_int(Int256, i) diff --git a/test/iobuffer.jl b/test/iobuffer.jl index 0e74595d29d20..b5b34a2dbed8c 100644 --- a/test/iobuffer.jl +++ b/test/iobuffer.jl @@ -351,7 +351,7 @@ end a = Base.GenericIOBuffer(UInt8[], true, true, false, true, typemax(Int)) mark(a) # mark at position 0 write(a, "Hello!") - @test Base.compact(a) == nothing # because pointer > mark + @test Base.compact(a) === nothing # because pointer > mark close(a) b = Base.GenericIOBuffer(UInt8[], true, true, false, true, typemax(Int)) write(b, "Hello!") diff --git a/test/iterators.jl b/test/iterators.jl index 95275195cd7c0..0df4d9afd371a 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -499,7 +499,7 @@ end @test Base.IteratorSize(product(1:2, countfrom(1))) == Base.IsInfinite() @test Base.iterate(product()) == ((), true) -@test Base.iterate(product(), 1) == nothing +@test Base.iterate(product(), 1) === nothing # intersection @test intersect(product(1:3, 4:6), product(2:4, 3:5)) == Iterators.ProductIterator((2:3, 4:5)) @@ -993,7 +993,7 @@ end end @testset "Iterators.peel" begin - @test Iterators.peel([]) == nothing + @test Iterators.peel([]) === nothing @test Iterators.peel(1:10)[1] == 1 @test Iterators.peel(1:10)[2] |> collect == 2:10 @test Iterators.peel(x^2 for x in 2:4)[1] == 4 diff --git a/test/llvmpasses/fastmath.jl b/test/llvmpasses/fastmath.jl index dd0892be56a0b..3c4c1d491ec28 100644 --- a/test/llvmpasses/fastmath.jl +++ b/test/llvmpasses/fastmath.jl @@ -16,29 +16,3 @@ import Base.FastMath # CHECK: call fast float @llvm.sqrt.f32(float %"x::Float32") emit(FastMath.sqrt_fast, Float32) - - -# Float16 operations should be performed as Float32, unless @fastmath is specified -# TODO: this is not true for platforms that natively support Float16 - -foo(x::T,y::T) where T = x-y == zero(T) -# CHECK: define {{(swiftcc )?}}i8 @julia_foo_{{[0-9]+}}({{.*}}half %[[X:"x::Float16"]], half %[[Y:"y::Float16"]]) {{.*}}{ -# CHECK-DAG: %[[XEXT:[0-9]+]] = fpext half %[[X]] to float -# CHECK-DAG: %[[YEXT:[0-9]+]] = fpext half %[[Y]] to float -# CHECK: %[[DIFF:[0-9]+]] = fsub float %[[XEXT]], %[[YEXT]] -# CHECK: %[[TRUNC:[0-9]+]] = fptrunc float %[[DIFF]] to half -# CHECK: %[[DIFFEXT:[0-9]+]] = fpext half %[[TRUNC]] to float -# CHECK: %[[CMP:[0-9]+]] = fcmp oeq float %[[DIFFEXT]], 0.000000e+00 -# CHECK: %[[ZEXT:[0-9]+]] = zext i1 %[[CMP]] to i8 -# CHECK: ret i8 %[[ZEXT]] -# CHECK: } -emit(foo, Float16, Float16) - -@fastmath foo(x::T,y::T) where T = x-y == zero(T) -# CHECK: define {{(swiftcc )?}}i8 @julia_foo_{{[0-9]+}}({{.*}}half %[[X:"x::Float16"]], half %[[Y:"y::Float16"]]) {{.*}}{ -# CHECK: %[[DIFF:[0-9]+]] = fsub fast half %[[X]], %[[Y]] -# CHECK: %[[CMP:[0-9]+]] = fcmp fast oeq half %[[DIFF]], 0xH0000 -# CHECK: %[[ZEXT:[0-9]+]] = zext i1 %[[CMP]] to i8 -# CHECK: ret i8 %[[ZEXT]] -# CHECK: } -emit(foo, Float16, Float16) diff --git a/test/llvmpasses/float16.ll b/test/llvmpasses/float16.ll index 33069c71179ed..d1dfb6aca11dd 100644 --- a/test/llvmpasses/float16.ll +++ b/test/llvmpasses/float16.ll @@ -99,7 +99,7 @@ top: ret half %13 } -define bfloat @demote_bfloat_test(bfloat %a, bfloat %b) { +define bfloat @demote_bfloat_test(bfloat %a, bfloat %b) #2 { top: ; CHECK-LABEL: @demote_bfloat_test( ; CHECK-NEXT: top: @@ -160,5 +160,70 @@ top: ret bfloat %13 } -attributes #0 = { "target-features"="-avx512fp16" } -attributes #1 = { "target-features"="+avx512fp16" } +define bfloat @native_bfloat_test(bfloat %a, bfloat %b) #3 { +top: +; CHECK-LABEL: @native_bfloat_test( +; CHECK-NEXT: top: +; CHECK-NEXT: %0 = fadd bfloat %a, %b +; CHECK-NEXT: %1 = fadd bfloat %0, %b +; CHECK-NEXT: %2 = fadd bfloat %1, %b +; CHECK-NEXT: %3 = fmul bfloat %2, %b +; CHECK-NEXT: %4 = fdiv bfloat %3, %b +; CHECK-NEXT: %5 = insertelement <2 x bfloat> undef, bfloat %a, i32 0 +; CHECK-NEXT: %6 = insertelement <2 x bfloat> %5, bfloat %b, i32 1 +; CHECK-NEXT: %7 = insertelement <2 x bfloat> undef, bfloat %b, i32 0 +; CHECK-NEXT: %8 = insertelement <2 x bfloat> %7, bfloat %b, i32 1 +; CHECK-NEXT: %9 = fadd <2 x bfloat> %6, %8 +; CHECK-NEXT: %10 = extractelement <2 x bfloat> %9, i32 0 +; CHECK-NEXT: %11 = extractelement <2 x bfloat> %9, i32 1 +; CHECK-NEXT: %12 = fadd bfloat %10, %11 +; CHECK-NEXT: %13 = fadd bfloat %12, %4 +; CHECK-NEXT: ret bfloat %13 +; + %0 = fadd bfloat %a, %b + %1 = fadd bfloat %0, %b + %2 = fadd bfloat %1, %b + %3 = fmul bfloat %2, %b + %4 = fdiv bfloat %3, %b + %5 = insertelement <2 x bfloat> undef, bfloat %a, i32 0 + %6 = insertelement <2 x bfloat> %5, bfloat %b, i32 1 + %7 = insertelement <2 x bfloat> undef, bfloat %b, i32 0 + %8 = insertelement <2 x bfloat> %7, bfloat %b, i32 1 + %9 = fadd <2 x bfloat> %6, %8 + %10 = extractelement <2 x bfloat> %9, i32 0 + %11 = extractelement <2 x bfloat> %9, i32 1 + %12 = fadd bfloat %10, %11 + %13 = fadd bfloat %12, %4 + ret bfloat %13 +} + +define i1 @fast_half_test(half %0, half %1) #0 { +top: +; CHECK-LABEL: @fast_half_test( +; CHECK-NEXT: top: +; CHECK-NEXT: %2 = fsub fast half %0, %1 +; CHECK-NEXT: %3 = fcmp fast oeq half %2, 0xH0000 +; CHECK-NEXT: ret i1 %3 +; + %2 = fsub fast half %0, %1 + %3 = fcmp fast oeq half %2, 0xH0000 + ret i1 %3 +} + +define i1 @fast_bfloat_test(bfloat %0, bfloat %1) #2 { +top: +; CHECK-LABEL: @fast_bfloat_test( +; CHECK-NEXT: top: +; CHECK-NEXT: %2 = fsub fast bfloat %0, %1 +; CHECK-NEXT: %3 = fcmp fast oeq bfloat %2, 0xR0000 +; CHECK-NEXT: ret i1 %3 +; + %2 = fsub fast bfloat %0, %1 + %3 = fcmp fast oeq bfloat %2, 0xR0000 + ret i1 %3 +} + +attributes #0 = { "julia.hasfp16"="false" } +attributes #1 = { "julia.hasfp16"="true" } +attributes #2 = { "julia.hasbf16"="false" } +attributes #3 = { "julia.hasbf16"="true" } diff --git a/test/loading.jl b/test/loading.jl index 8310cb03c410b..51e0c45d2faf1 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -167,7 +167,7 @@ end @test root.uuid == root_uuid @test this.uuid == this_uuid - @test that == nothing + @test that === nothing write(project_file, """ name = "Root" @@ -180,8 +180,8 @@ end that = Base.identify_package("That") @test root.uuid == proj_uuid - @test this == nothing - @test that == nothing + @test this === nothing + @test that === nothing finally copy!(LOAD_PATH, old_load_path) end @@ -213,8 +213,8 @@ end that = Base.identify_package("That") @test root.uuid == root_uuid - @test this == nothing - @test that == nothing + @test this === nothing + @test that === nothing @test Base.get_uuid_name(project_file, this_uuid) == "This" finally @@ -273,8 +273,8 @@ end @test joinpath(@__DIR__, normpath(path)) == locate_package(pkg) @test Base.compilecache_path(pkg, UInt64(0)) == Base.compilecache_path(pkg, UInt64(0)) end - @test identify_package("Baz") == nothing - @test identify_package("Qux") == nothing + @test identify_package("Baz") === nothing + @test identify_package("Qux") === nothing @testset "equivalent package names" begin classes = [ ["Foo"], @@ -848,7 +848,7 @@ end proj = joinpath(tmp, "Project.toml") touch(proj) touch(joinpath(tmp, "Manifest-v1.5.toml")) - @test Base.project_file_manifest_path(proj) == nothing + @test Base.project_file_manifest_path(proj) === nothing touch(joinpath(tmp, "Manifest.toml")) man = basename(Base.project_file_manifest_path(proj)) @test man == "Manifest.toml" diff --git a/test/offsetarray.jl b/test/offsetarray.jl index c50f38c382385..fb5855dfbaa0d 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -383,6 +383,18 @@ v2 = copy(v) @test v2[end-1] == 2 @test v2[end] == 1 +# push!(v::AbstractVector, x...) +v2 = copy(v) +@test @invoke(push!(v2::AbstractVector, 3)) === v2 +@test v2[axes(v,1)] == v +@test v2[end] == 3 +@test v2[begin] == v[begin] == v[-2] +v2 = copy(v) +@test @invoke(push!(v2::AbstractVector, 5, 6)) == v2 +@test v2[axes(v,1)] == v +@test v2[end-1] == 5 +@test v2[end] == 6 + # append! from array v2 = copy(v) @test append!(v2, [2, 1]) === v2 @@ -399,6 +411,23 @@ v2 = copy(v) @test v2[axes(v, 1)] == v @test v2[lastindex(v)+1:end] == [2, 1] +# append!(::AbstractVector, ...) +# append! from array +v2 = copy(v) +@test @invoke(append!(v2::AbstractVector, [2, 1]::Any)) === v2 +@test v2[axes(v, 1)] == v +@test v2[lastindex(v)+1:end] == [2, 1] +# append! from HasLength iterator +v2 = copy(v) +@test @invoke(append!(v2::AbstractVector, (v for v in [2, 1])::Any)) === v2 +@test v2[axes(v, 1)] == v +@test v2[lastindex(v)+1:end] == [2, 1] +# append! from SizeUnknown iterator +v2 = copy(v) +@test @invoke(append!(v2::AbstractVector, (v for v in [2, 1] if true)::Any)) === v2 +@test v2[axes(v, 1)] == v +@test v2[lastindex(v)+1:end] == [2, 1] + # other functions v = OffsetArray(v0, (-3,)) @test lastindex(v) == 1 @@ -878,3 +907,10 @@ end v = view([1,2,3,4], :) @test v[Base.IdentityUnitRange(2:3)] == OffsetArray(2:3, 2:3) end + +@testset "mapreduce with OffsetRanges" begin + r = 5:100 + a = OffsetArray(r, 2) + b = sum(a, dims=1) + @test b[begin] == sum(r) +end diff --git a/test/ranges.jl b/test/ranges.jl index d789871c6d049..16b2c6bf7b77b 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -437,17 +437,17 @@ end @testset "findfirst" begin @test findfirst(==(1), Base.IdentityUnitRange(-1:1)) == 1 @test findfirst(isequal(3), Base.OneTo(10)) == 3 - @test findfirst(==(0), Base.OneTo(10)) == nothing - @test findfirst(==(11), Base.OneTo(10)) == nothing + @test findfirst(==(0), Base.OneTo(10)) === nothing + @test findfirst(==(11), Base.OneTo(10)) === nothing @test findfirst(==(4), Int16(3):Int16(7)) === Int(2) - @test findfirst(==(2), Int16(3):Int16(7)) == nothing - @test findfirst(isequal(8), 3:7) == nothing + @test findfirst(==(2), Int16(3):Int16(7)) === nothing + @test findfirst(isequal(8), 3:7) === nothing @test findfirst(isequal(7), 1:2:10) == 4 @test findfirst(==(7), 1:2:10) == 4 - @test findfirst(==(10), 1:2:10) == nothing - @test findfirst(==(11), 1:2:10) == nothing + @test findfirst(==(10), 1:2:10) === nothing + @test findfirst(==(11), 1:2:10) === nothing @test findfirst(==(-7), 1:-1:-10) == 9 - @test findfirst(==(2),1:-1:2) == nothing + @test findfirst(==(2),1:-1:2) === nothing end @testset "reverse" begin @test reverse(reverse(1:10)) == 1:10 diff --git a/test/regex.jl b/test/regex.jl index a1d0b1b0ed69a..51802125a3467 100644 --- a/test/regex.jl +++ b/test/regex.jl @@ -213,7 +213,7 @@ r = r"" * raw"a\Eb|c" @test match(r, raw"a\Eb|c").match == raw"a\Eb|c" - @test match(r, raw"c") == nothing + @test match(r, raw"c") === nothing # error for really incompatible options @test_throws ArgumentError r"a" * Regex("b", Base.DEFAULT_COMPILER_OPTS & ~Base.PCRE.UCP, Base.DEFAULT_MATCH_OPTS) diff --git a/test/some.jl b/test/some.jl index 59ccd05be96bf..89f699d8306c3 100644 --- a/test/some.jl +++ b/test/some.jl @@ -44,8 +44,8 @@ ## == and isequal nothing -@test Some(1) != nothing -@test Some(nothing) != nothing +@test Some(1) !== nothing +@test Some(nothing) !== nothing @test !isequal(Some(1), nothing) @test !isequal(Some(nothing), nothing) diff --git a/test/spawn.jl b/test/spawn.jl index 831eac493d4aa..c1802ba1f74da 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -573,7 +573,7 @@ end @test Cmd(`foo`, env=["A=true"]).env == ["A=true"] @test Cmd(`foo`, env=("A"=>true,)).env == ["A=true"] @test Cmd(`foo`, env=["A"=>true]).env == ["A=true"] -@test Cmd(`foo`, env=nothing).env == nothing +@test Cmd(`foo`, env=nothing).env === nothing # test for interpolation of Cmd let c = setenv(`x`, "A"=>true) diff --git a/test/strings/annotated.jl b/test/strings/annotated.jl index e985c0b421a51..90aaadd6ede24 100644 --- a/test/strings/annotated.jl +++ b/test/strings/annotated.jl @@ -5,6 +5,7 @@ @test str == Base.AnnotatedString(str.string, Tuple{UnitRange{Int}, Pair{Symbol, Any}}[]) @test length(str) == 11 @test ncodeunits(str) == 11 + @test convert(Base.AnnotatedString, str) === str @test eltype(str) == Base.AnnotatedChar{eltype(str.string)} @test first(str) == Base.AnnotatedChar(first(str.string), Pair{Symbol, Any}[]) @test str[1:4] isa SubString{typeof(str)} @@ -63,7 +64,12 @@ end @testset "AnnotatedChar" begin chr = Base.AnnotatedChar('c') + @test Base.AnnotatedChar(UInt32('c')) == chr + @test convert(Base.AnnotatedChar, chr) === chr @test chr == Base.AnnotatedChar(chr.char, Pair{Symbol, Any}[]) + @test uppercase(chr) == Base.AnnotatedChar('C') + @test titlecase(chr) == Base.AnnotatedChar('C') + @test lowercase(Base.AnnotatedChar('C')) == chr str = Base.AnnotatedString("hmm", [(1:1, :attr => "h0h0"), (1:2, :attr => "h0m1"), (2:3, :attr => "m1m2")]) diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 87d812c5bf201..a7266f52f16fc 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -1235,6 +1235,8 @@ end @test !Core.Compiler.is_removable_if_unused(e) || (f, Ts) end @test_throws ArgumentError Symbol("a\0a") + + @test Base._string_n_override == Core.Compiler.encode_effects_override(Base.compute_assumed_settings((:total, :(!:consistent)))) end @testset "Ensure UTF-8 DFA can never leave invalid state" begin @@ -1388,3 +1390,17 @@ end end end end + +@testset "transcode" begin + # string starting with an ASCII character + str_1 = "zΞ²Ξ³" + # string starting with a 2 byte UTF-8 character + str_2 = "Ξ±Ξ²Ξ³" + # string starting with a 3 byte UTF-8 character + str_3 = "ΰ€†ΰ€–" + @testset for str in (str_1, str_2, str_3) + @test transcode(String, transcode(UInt16, str)) == str + @test transcode(String, transcode(UInt16, transcode(UInt8, str))) == str + @test transcode(String, transcode(UInt8, transcode(UInt16, str))) == str + end +end diff --git a/test/strings/search.jl b/test/strings/search.jl index e737096b3371d..692286359868d 100644 --- a/test/strings/search.jl +++ b/test/strings/search.jl @@ -26,19 +26,19 @@ end for str in [astr, GenericString(astr)] @test_throws BoundsError findnext(isequal('z'), str, 0) @test_throws BoundsError findnext(isequal('βˆ€'), str, 0) - @test findfirst(isequal('x'), str) == nothing - @test findfirst(isequal('\0'), str) == nothing - @test findfirst(isequal('\u80'), str) == nothing - @test findfirst(isequal('βˆ€'), str) == nothing + @test findfirst(isequal('x'), str) === nothing + @test findfirst(isequal('\0'), str) === nothing + @test findfirst(isequal('\u80'), str) === nothing + @test findfirst(isequal('βˆ€'), str) === nothing @test findfirst(isequal('H'), str) == 1 @test findfirst(isequal('l'), str) == 3 @test findnext(isequal('l'), str, 4) == 4 @test findnext(isequal('l'), str, 5) == 11 - @test findnext(isequal('l'), str, 12) == nothing + @test findnext(isequal('l'), str, 12) === nothing @test findfirst(isequal(','), str) == 6 - @test findnext(isequal(','), str, 7) == nothing + @test findnext(isequal(','), str, 7) === nothing @test findfirst(isequal('\n'), str) == 14 - @test findnext(isequal('\n'), str, 15) == nothing + @test findnext(isequal('\n'), str, 15) === nothing @test_throws BoundsError findnext(isequal('Ξ΅'), str, nextind(str,lastindex(str))+1) @test_throws BoundsError findnext(isequal('a'), str, nextind(str,lastindex(str))+1) end @@ -46,59 +46,59 @@ end for str in [astr, GenericString(astr)] @test_throws BoundsError findnext('z', str, 0) @test_throws BoundsError findnext('βˆ€', str, 0) - @test findfirst('x', str) == nothing - @test findfirst('\0', str) == nothing - @test findfirst('\u80', str) == nothing - @test findfirst('βˆ€', str) == nothing + @test findfirst('x', str) === nothing + @test findfirst('\0', str) === nothing + @test findfirst('\u80', str) === nothing + @test findfirst('βˆ€', str) === nothing @test findfirst('H', str) == 1 @test findfirst('l', str) == 3 @test findfirst('e', str) == 2 - @test findfirst('u', str) == nothing + @test findfirst('u', str) === nothing @test findnext('l', str, 4) == 4 @test findnext('l', str, 5) == 11 - @test findnext('l', str, 12) == nothing + @test findnext('l', str, 12) === nothing @test findfirst(',', str) == 6 - @test findnext(',', str, 7) == nothing + @test findnext(',', str, 7) === nothing @test findfirst('\n', str) == 14 - @test findnext('\n', str, 15) == nothing + @test findnext('\n', str, 15) === nothing @test_throws BoundsError findnext('Ξ΅', str, nextind(str,lastindex(str))+1) @test_throws BoundsError findnext('a', str, nextind(str,lastindex(str))+1) end # ascii backward search for str in [astr] - @test findlast(isequal('x'), str) == nothing - @test findlast(isequal('\0'), str) == nothing - @test findlast(isequal('\u80'), str) == nothing - @test findlast(isequal('βˆ€'), str) == nothing + @test findlast(isequal('x'), str) === nothing + @test findlast(isequal('\0'), str) === nothing + @test findlast(isequal('\u80'), str) === nothing + @test findlast(isequal('βˆ€'), str) === nothing @test findlast(isequal('H'), str) == 1 - @test findprev(isequal('H'), str, 0) == nothing + @test findprev(isequal('H'), str, 0) === nothing @test findlast(isequal('l'), str) == 11 @test findprev(isequal('l'), str, 5) == 4 @test findprev(isequal('l'), str, 4) == 4 @test findprev(isequal('l'), str, 3) == 3 - @test findprev(isequal('l'), str, 2) == nothing + @test findprev(isequal('l'), str, 2) === nothing @test findlast(isequal(','), str) == 6 - @test findprev(isequal(','), str, 5) == nothing + @test findprev(isequal(','), str, 5) === nothing @test findlast(isequal('\n'), str) == 14 end for str in [astr] - @test findlast('x', str) == nothing - @test findlast('\0', str) == nothing - @test findlast('\u80', str) == nothing - @test findlast('βˆ€', str) == nothing + @test findlast('x', str) === nothing + @test findlast('\0', str) === nothing + @test findlast('\u80', str) === nothing + @test findlast('βˆ€', str) === nothing @test findlast('H', str) == 1 - @test findprev('H', str, 0) == nothing + @test findprev('H', str, 0) === nothing @test findlast('l', str) == 11 @test findprev('l', str, 5) == 4 @test findprev('l', str, 4) == 4 @test findprev('l', str, 3) == 3 - @test findprev('l', str, 2) == nothing + @test findprev('l', str, 2) === nothing @test findlast(',', str) == 6 - @test findprev(',', str, 5) == nothing - @test findlast(str, "") == nothing - @test findlast(str^2, str) == nothing + @test findprev(',', str, 5) === nothing + @test findlast(str, "") === nothing + @test findlast(str^2, str) === nothing @test findlast('\n', str) == 14 end @@ -106,133 +106,133 @@ end for str in (u8str, GenericString(u8str)) @test_throws BoundsError findnext(isequal('z'), str, 0) @test_throws BoundsError findnext(isequal('βˆ€'), str, 0) - @test findfirst(isequal('z'), str) == nothing - @test findfirst(isequal('\0'), str) == nothing - @test findfirst(isequal('\u80'), str) == nothing - @test findfirst(isequal('βˆ„'), str) == nothing + @test findfirst(isequal('z'), str) === nothing + @test findfirst(isequal('\0'), str) === nothing + @test findfirst(isequal('\u80'), str) === nothing + @test findfirst(isequal('βˆ„'), str) === nothing @test findfirst(isequal('βˆ€'), str) == 1 @test_throws StringIndexError findnext(isequal('βˆ€'), str, 2) - @test findnext(isequal('βˆ€'), str, 4) == nothing + @test findnext(isequal('βˆ€'), str, 4) === nothing @test findfirst(isequal('βˆƒ'), str) == 13 @test_throws StringIndexError findnext(isequal('βˆƒ'), str, 15) - @test findnext(isequal('βˆƒ'), str, 16) == nothing + @test findnext(isequal('βˆƒ'), str, 16) === nothing @test findfirst(isequal('x'), str) == 26 @test findnext(isequal('x'), str, 27) == 43 - @test findnext(isequal('x'), str, 44) == nothing + @test findnext(isequal('x'), str, 44) === nothing @test findfirst(isequal('Ξ΄'), str) == 17 @test_throws StringIndexError findnext(isequal('Ξ΄'), str, 18) @test findnext(isequal('Ξ΄'), str, nextind(str,17)) == 33 - @test findnext(isequal('Ξ΄'), str, nextind(str,33)) == nothing + @test findnext(isequal('Ξ΄'), str, nextind(str,33)) === nothing @test findfirst(isequal('Ξ΅'), str) == 5 @test findnext(isequal('Ξ΅'), str, nextind(str,5)) == 54 - @test findnext(isequal('Ξ΅'), str, nextind(str,54)) == nothing - @test findnext(isequal('Ξ΅'), str, nextind(str,lastindex(str))) == nothing - @test findnext(isequal('a'), str, nextind(str,lastindex(str))) == nothing + @test findnext(isequal('Ξ΅'), str, nextind(str,54)) === nothing + @test findnext(isequal('Ξ΅'), str, nextind(str,lastindex(str))) === nothing + @test findnext(isequal('a'), str, nextind(str,lastindex(str))) === nothing @test_throws BoundsError findnext(isequal('Ξ΅'), str, nextind(str,lastindex(str))+1) @test_throws BoundsError findnext(isequal('a'), str, nextind(str,lastindex(str))+1) end # utf-8 backward search for str in [u8str] - @test findlast(isequal('z'), str) == nothing - @test findlast(isequal('\0'), str) == nothing - @test findlast(isequal('\u80'), str) == nothing - @test findlast(isequal('βˆ„'), str) == nothing + @test findlast(isequal('z'), str) === nothing + @test findlast(isequal('\0'), str) === nothing + @test findlast(isequal('\u80'), str) === nothing + @test findlast(isequal('βˆ„'), str) === nothing @test findlast(isequal('βˆ€'), str) == 1 - @test findprev(isequal('βˆ€'), str, 0) == nothing + @test findprev(isequal('βˆ€'), str, 0) === nothing @test findlast(isequal('βˆƒ'), str) == 13 @test findprev(isequal('βˆƒ'), str, 14) == 13 @test findprev(isequal('βˆƒ'), str, 13) == 13 - @test findprev(isequal('βˆƒ'), str, 12) == nothing + @test findprev(isequal('βˆƒ'), str, 12) === nothing @test findlast(isequal('x'), str) == 43 @test findprev(isequal('x'), str, 42) == 26 - @test findprev(isequal('x'), str, 25) == nothing + @test findprev(isequal('x'), str, 25) === nothing @test findlast(isequal('Ξ΄'), str) == 33 @test findprev(isequal('Ξ΄'), str, 32) == 17 - @test findprev(isequal('Ξ΄'), str, 16) == nothing + @test findprev(isequal('Ξ΄'), str, 16) === nothing @test findlast(isequal('Ξ΅'), str) == 54 @test findprev(isequal('Ξ΅'), str, 53) == 5 - @test findprev(isequal('Ξ΅'), str, 4) == nothing + @test findprev(isequal('Ξ΅'), str, 4) === nothing end # string forward search with a single-char string -@test findfirst("x", astr) == nothing +@test findfirst("x", astr) === nothing @test findfirst("H", astr) == 1:1 -@test findnext("H", astr, 2) == nothing +@test findnext("H", astr, 2) === nothing @test findfirst("l", astr) == 3:3 @test findnext("l", astr, 4) == 4:4 @test findnext("l", astr, 5) == 11:11 -@test findnext("l", astr, 12) == nothing +@test findnext("l", astr, 12) === nothing @test findfirst("\n", astr) == 14:14 -@test findnext("\n", astr, 15) == nothing +@test findnext("\n", astr, 15) === nothing -@test findfirst("z", u8str) == nothing -@test findfirst("βˆ„", u8str) == nothing +@test findfirst("z", u8str) === nothing +@test findfirst("βˆ„", u8str) === nothing @test findfirst("βˆ€", u8str) == 1:1 -@test findnext("βˆ€", u8str, 4) == nothing +@test findnext("βˆ€", u8str, 4) === nothing @test findfirst("βˆƒ", u8str) == 13:13 -@test findnext("βˆƒ", u8str, 16) == nothing +@test findnext("βˆƒ", u8str, 16) === nothing @test findfirst("x", u8str) == 26:26 @test findnext("x", u8str, 27) == 43:43 -@test findnext("x", u8str, 44) == nothing +@test findnext("x", u8str, 44) === nothing @test findfirst("Ξ΅", u8str) == 5:5 @test findnext("Ξ΅", u8str, 7) == 54:54 -@test findnext("Ξ΅", u8str, 56) == nothing +@test findnext("Ξ΅", u8str, 56) === nothing # strifindprev backward search with a single-char string -@test findlast("x", astr) == nothing +@test findlast("x", astr) === nothing @test findlast("H", astr) == 1:1 @test findprev("H", astr, 2) == 1:1 -@test findprev("H", astr, 0) == nothing +@test findprev("H", astr, 0) === nothing @test findlast("l", astr) == 11:11 @test findprev("l", astr, 10) == 4:4 @test findprev("l", astr, 4) == 4:4 @test findprev("l", astr, 3) == 3:3 -@test findprev("l", astr, 2) == nothing +@test findprev("l", astr, 2) === nothing @test findlast("\n", astr) == 14:14 -@test findprev("\n", astr, 13) == nothing +@test findprev("\n", astr, 13) === nothing -@test findlast("z", u8str) == nothing -@test findlast("βˆ„", u8str) == nothing +@test findlast("z", u8str) === nothing +@test findlast("βˆ„", u8str) === nothing @test findlast("βˆ€", u8str) == 1:1 -@test findprev("βˆ€", u8str, 0) == nothing +@test findprev("βˆ€", u8str, 0) === nothing #TODO: setting the limit in the middle of a wide char # makes findnext fail but findprev succeed. # Should findprev fail as well? -#@test findprev("βˆ€", u8str, 2) == nothing # gives 1:3 +#@test findprev("βˆ€", u8str, 2) === nothing # gives 1:3 @test findlast("βˆƒ", u8str) == 13:13 -@test findprev("βˆƒ", u8str, 12) == nothing +@test findprev("βˆƒ", u8str, 12) === nothing @test findlast("x", u8str) == 43:43 @test findprev("x", u8str, 42) == 26:26 -@test findprev("x", u8str, 25) == nothing +@test findprev("x", u8str, 25) === nothing @test findlast("Ξ΅", u8str) == 54:54 @test findprev("Ξ΅", u8str, 53) == 5:5 -@test findprev("Ξ΅", u8str, 4) == nothing +@test findprev("Ξ΅", u8str, 4) === nothing # string forward search with a single-char regex -@test findfirst(r"x", astr) == nothing +@test findfirst(r"x", astr) === nothing @test findfirst(r"H", astr) == 1:1 -@test findnext(r"H", astr, 2) == nothing +@test findnext(r"H", astr, 2) === nothing @test findfirst(r"l", astr) == 3:3 @test findnext(r"l", astr, 4) == 4:4 @test findnext(r"l", astr, 5) == 11:11 -@test findnext(r"l", astr, 12) == nothing +@test findnext(r"l", astr, 12) === nothing @test findfirst(r"\n", astr) == 14:14 -@test findnext(r"\n", astr, 15) == nothing -@test findfirst(r"z", u8str) == nothing -@test findfirst(r"βˆ„", u8str) == nothing +@test findnext(r"\n", astr, 15) === nothing +@test findfirst(r"z", u8str) === nothing +@test findfirst(r"βˆ„", u8str) === nothing @test findfirst(r"βˆ€", u8str) == 1:1 -@test findnext(r"βˆ€", u8str, 4) == nothing +@test findnext(r"βˆ€", u8str, 4) === nothing @test findfirst(r"βˆ€", u8str) == findfirst(r"\u2200", u8str) @test findnext(r"βˆ€", u8str, 4) == findnext(r"\u2200", u8str, 4) @test findfirst(r"βˆƒ", u8str) == 13:13 -@test findnext(r"βˆƒ", u8str, 16) == nothing +@test findnext(r"βˆƒ", u8str, 16) === nothing @test findfirst(r"x", u8str) == 26:26 @test findnext(r"x", u8str, 27) == 43:43 -@test findnext(r"x", u8str, 44) == nothing +@test findnext(r"x", u8str, 44) === nothing @test findfirst(r"Ξ΅", u8str) == 5:5 @test findnext(r"Ξ΅", u8str, 7) == 54:54 -@test findnext(r"Ξ΅", u8str, 56) == nothing +@test findnext(r"Ξ΅", u8str, 56) === nothing for i = 1:lastindex(astr) @test findnext(r"."s, astr, i) == i:i end @@ -272,18 +272,18 @@ for i = 1:lastindex(u8str) end # string forward search with a two-char string literal -@test findfirst("xx", "foo,bar,baz") == nothing +@test findfirst("xx", "foo,bar,baz") === nothing @test findfirst("fo", "foo,bar,baz") == 1:2 -@test findnext("fo", "foo,bar,baz", 3) == nothing +@test findnext("fo", "foo,bar,baz", 3) === nothing @test findfirst("oo", "foo,bar,baz") == 2:3 -@test findnext("oo", "foo,bar,baz", 4) == nothing +@test findnext("oo", "foo,bar,baz", 4) === nothing @test findfirst("o,", "foo,bar,baz") == 3:4 -@test findnext("o,", "foo,bar,baz", 5) == nothing +@test findnext("o,", "foo,bar,baz", 5) === nothing @test findfirst(",b", "foo,bar,baz") == 4:5 @test findnext(",b", "foo,bar,baz", 6) == 8:9 -@test findnext(",b", "foo,bar,baz", 10) == nothing +@test findnext(",b", "foo,bar,baz", 10) === nothing @test findfirst("az", "foo,bar,baz") == 10:11 -@test findnext("az", "foo,bar,baz", 12) == nothing +@test findnext("az", "foo,bar,baz", 12) === nothing # issue #9365 # string forward search with a two-char UTF-8 (2 byte) string literal @@ -327,32 +327,32 @@ end @test findprev("\U1f596\U1f596", "\U1f596\U1f596", lastindex("\U1f596\U1f596\U1f596")) == 1:5 # string backward search with a two-char string literal -@test findlast("xx", "foo,bar,baz") == nothing +@test findlast("xx", "foo,bar,baz") === nothing @test findlast("fo", "foo,bar,baz") == 1:2 -@test findprev("fo", "foo,bar,baz", 1) == nothing +@test findprev("fo", "foo,bar,baz", 1) === nothing @test findlast("oo", "foo,bar,baz") == 2:3 -@test findprev("oo", "foo,bar,baz", 2) == nothing +@test findprev("oo", "foo,bar,baz", 2) === nothing @test findlast("o,", "foo,bar,baz") == 3:4 -@test findprev("o,", "foo,bar,baz", 1) == nothing +@test findprev("o,", "foo,bar,baz", 1) === nothing @test findlast(",b", "foo,bar,baz") == 8:9 @test findprev(",b", "foo,bar,baz", 6) == 4:5 -@test findprev(",b", "foo,bar,baz", 3) == nothing +@test findprev(",b", "foo,bar,baz", 3) === nothing @test findlast("az", "foo,bar,baz") == 10:11 -@test findprev("az", "foo,bar,baz", 10) == nothing +@test findprev("az", "foo,bar,baz", 10) === nothing # string search with a two-char regex -@test findfirst(r"xx", "foo,bar,baz") == nothing +@test findfirst(r"xx", "foo,bar,baz") === nothing @test findfirst(r"fo", "foo,bar,baz") == 1:2 -@test findnext(r"fo", "foo,bar,baz", 3) == nothing +@test findnext(r"fo", "foo,bar,baz", 3) === nothing @test findfirst(r"oo", "foo,bar,baz") == 2:3 -@test findnext(r"oo", "foo,bar,baz", 4) == nothing +@test findnext(r"oo", "foo,bar,baz", 4) === nothing @test findfirst(r"o,", "foo,bar,baz") == 3:4 -@test findnext(r"o,", "foo,bar,baz", 5) == nothing +@test findnext(r"o,", "foo,bar,baz", 5) === nothing @test findfirst(r",b", "foo,bar,baz") == 4:5 @test findnext(r",b", "foo,bar,baz", 6) == 8:9 -@test findnext(r",b", "foo,bar,baz", 10) == nothing +@test findnext(r",b", "foo,bar,baz", 10) === nothing @test findfirst(r"az", "foo,bar,baz") == 10:11 -@test findnext(r"az", "foo,bar,baz", 12) == nothing +@test findnext(r"az", "foo,bar,baz", 12) === nothing # occursin with a String and Char needle @test occursin("o", "foo") @@ -417,7 +417,7 @@ end A = T[0x40, 0x52, 0x00, 0x52, 0x00] for A in (A, @view(A[1:end]), codeunits(String(copyto!(Vector{UInt8}(undef,5), A)))) - @test findfirst(VT[0x30], A) === findfirst(==(VT(0x30)), A) == nothing + @test findfirst(VT[0x30], A) === findfirst(==(VT(0x30)), A) === nothing @test findfirst(VT[0x52], A) === 2:2 @test findfirst(==(VT(0x52)), A) === 2 @test findlast(VT[0x30], A) === findlast(==(VT(0x30)), A) === nothing diff --git a/test/strings/types.jl b/test/strings/types.jl index 771be253b1ec9..dbcf65b1d843b 100644 --- a/test/strings/types.jl +++ b/test/strings/types.jl @@ -118,8 +118,8 @@ end # search and SubString (issue #5679) let str = "Hello, world!" u = SubString(str, 1, 5) - @test findlast("World", u) == nothing - @test findlast(isequal('z'), u) == nothing + @test findlast("World", u) === nothing + @test findlast(isequal('z'), u) === nothing @test findlast("ll", u) == 3:4 end diff --git a/test/testdefs.jl b/test/testdefs.jl index b96c95045f2bd..eb0bf570b11fd 100644 --- a/test/testdefs.jl +++ b/test/testdefs.jl @@ -23,7 +23,7 @@ function runtests(name, path, isolate=true; seed=nothing) end res_and_time_data = @timed @testset "$name" begin # Random.seed!(nothing) will fail - seed != nothing && Random.seed!(seed) + seed !== nothing && Random.seed!(seed) original_depot_path = copy(Base.DEPOT_PATH) original_load_path = copy(Base.LOAD_PATH) diff --git a/test/threads.jl b/test/threads.jl index 7b4558091022b..2832f2a0e972c 100644 --- a/test/threads.jl +++ b/test/threads.jl @@ -288,18 +288,16 @@ close(proc.in) proc = run(cmd; wait = false) done = Threads.Atomic{Bool}(false) timeout = false - timer = Timer(100) do _ + timer = Timer(200) do _ timeout = true - for sig in [Base.SIGTERM, Base.SIGHUP, Base.SIGKILL] - for _ in 1:1000 + for sig in (Base.SIGQUIT, Base.SIGKILL) + for _ in 1:3 kill(proc, sig) + sleep(1) if done[] - if sig != Base.SIGTERM - @warn "Terminating `$script` required signal $sig" - end + @warn "Terminating `$script` required signal $sig" return end - sleep(0.001) end end end @@ -309,16 +307,11 @@ close(proc.in) done[] = true close(timer) end - if ( !success(proc) ) || ( timeout ) + if !success(proc) || timeout @error "A \"spawn and wait lots of tasks\" test failed" n proc.exitcode proc.termsignal success(proc) timeout end - if Sys.iswindows() || Sys.isapple() - # Known failure: https://github.com/JuliaLang/julia/issues/43124 - @test_skip success(proc) - else - @test success(proc) - @test !timeout - end + @test success(proc) + @test !timeout end end diff --git a/test/tuple.jl b/test/tuple.jl index b1894bd2bb6ce..355ad965f9584 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -533,7 +533,7 @@ end @test ntuple(identity, Val(n)) == ntuple(identity, n) end - @test Core.Compiler.return_type(ntuple, Tuple{typeof(identity), Val}) == Tuple{Vararg{Int}} + @test Base.infer_return_type(ntuple, Tuple{typeof(identity), Val}) == Tuple{Vararg{Int}} end struct A_15703{N} @@ -835,8 +835,8 @@ end @test @inferred(Base.circshift(t3, 7)) == ('b', 'c', 'd', 'a') @test @inferred(Base.circshift(t3, -1)) == ('b', 'c', 'd', 'a') @test_throws MethodError circshift(t1, 'a') - @test Core.Compiler.return_type(circshift, Tuple{Tuple,Integer}) <: Tuple - @test Core.Compiler.return_type(circshift, Tuple{Tuple{Vararg{Any,10}},Integer}) <: Tuple{Vararg{Any,10}} + @test Base.infer_return_type(circshift, Tuple{Tuple,Integer}) <: Tuple + @test Base.infer_return_type(circshift, Tuple{Tuple{Vararg{Any,10}},Integer}) <: Tuple{Vararg{Any,10}} for len ∈ 0:5 v = 1:len t = Tuple(v)