diff --git a/Make.inc b/Make.inc index d62c485b03dbc8..910fcdc276e3b0 100644 --- a/Make.inc +++ b/Make.inc @@ -599,10 +599,13 @@ CPP_STDOUT := $(CPP) -P # file extensions ifeq ($(OS), WINNT) SHLIB_EXT := dll + PATHSEP := ; else ifeq ($(OS), Darwin) SHLIB_EXT := dylib + PATHSEP := : else SHLIB_EXT := so + PATHSEP := : endif ifeq ($(OS),WINNT) diff --git a/NEWS.md b/NEWS.md index d53911040e4d79..c9b968da3c1e09 100644 --- a/NEWS.md +++ b/NEWS.md @@ -19,6 +19,15 @@ New language features Language changes ---------------- +* During precompilation, the `atexit` hooks now run before saving the output file. This + allows users to safely tear down background state (such as closing Timers and sending + disconnect notifications to heartbeat tasks) and cleanup other resources when the program + wants to begin exiting. +* Code coverage and malloc tracking is no longer generated during the package precompilation stage. + Further, during these modes pkgimage caches are now used for packages that are not being tracked. + Meaning that coverage testing (the default for `julia-actions/julia-runtest`) will by default use + pkgimage caches for all other packages than the package being tested, likely meaning faster test + execution. ([#52123]) Compiler/Runtime improvements ----------------------------- @@ -54,6 +63,9 @@ New library functions New library features -------------------- + +* `invmod(n, T)` where `T` is a native integer type now computes the modular inverse of `n` in the modular integer ring that `T` defines ([#52180]). +* `invmod(n)` is an abbreviation for `invmod(n, typeof(n))` for native integer types ([#52180]). * `replace(string, pattern...)` now supports an optional `IO` argument to write the output to a stream rather than returning a string ([#48625]). * `sizehint!(s, n)` now supports an optional `shrink` argument to disable shrinking ([#51929]). diff --git a/README.md b/README.md index e904b3b8d6770d..add11e8cef44b4 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ and then use the command prompt to change into the resulting julia directory. By Julia. However, most users should use the [most recent stable version](https://github.com/JuliaLang/julia/releases) of Julia. You can get this version by running: - git checkout v1.9.3 + git checkout v1.9.4 To build the `julia` executable, run `make` from within the julia directory. diff --git a/base/Base.jl b/base/Base.jl index 9d0a44b7a1f5dd..0ca13265adc4f1 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -481,6 +481,7 @@ include("summarysize.jl") include("errorshow.jl") include("initdefs.jl") +Filesystem.__postinit__() # worker threads include("threadcall.jl") @@ -594,14 +595,12 @@ if is_primary_base_module # Profiling helper # triggers printing the report and (optionally) saving a heap snapshot after a SIGINFO/SIGUSR1 profile request # Needs to be in Base because Profile is no longer loaded on boot -const PROFILE_PRINT_COND = Ref{Base.AsyncCondition}() -function profile_printing_listener() +function profile_printing_listener(cond::Base.AsyncCondition) profile = nothing try - while true - wait(PROFILE_PRINT_COND[]) - profile = @something(profile, require(PkgId(UUID("9abbd945-dff8-562f-b5e8-e1ebf5ef1b79"), "Profile"))) - + while _trywait(cond) + # this call to require is mostly legal, only because Profile has no dependencies and is usually in LOAD_PATH + profile = @something(profile, require(PkgId(UUID("9abbd945-dff8-562f-b5e8-e1ebf5ef1b79"), "Profile")))::Module invokelatest(profile.peek_report[]) if Base.get_bool_env("JULIA_PROFILE_PEEK_HEAP_SNAPSHOT", false) === true println(stderr, "Saving heap snapshot...") @@ -614,10 +613,13 @@ function profile_printing_listener() @error "Profile printing listener crashed" exception=ex,catch_backtrace() end end + nothing end function __init__() # Base library init + global _atexit_hooks_finished = false + Filesystem.__postinit__() reinit_stdio() Multimedia.reinit_displays() # since Multimedia.displays uses stdout as fallback # initialize loading @@ -633,9 +635,20 @@ function __init__() # triggering a profile via signals is not implemented on windows cond = Base.AsyncCondition() Base.uv_unref(cond.handle) - PROFILE_PRINT_COND[] = cond - ccall(:jl_set_peek_cond, Cvoid, (Ptr{Cvoid},), PROFILE_PRINT_COND[].handle) - errormonitor(Threads.@spawn(profile_printing_listener())) + t = errormonitor(Threads.@spawn(profile_printing_listener(cond))) + atexit() do + # destroy this callback when exiting + ccall(:jl_set_peek_cond, Cvoid, (Ptr{Cvoid},), C_NULL) + # 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) + end + finalizer(cond) do c + # if something goes south, still make sure we aren't keeping a reference in C to this + ccall(:jl_set_peek_cond, Cvoid, (Ptr{Cvoid},), C_NULL) + end + ccall(:jl_set_peek_cond, Cvoid, (Ptr{Cvoid},), cond.handle) end _require_world_age[] = get_world_counter() # Prevent spawned Julia process from getting stuck waiting on Tracy to connect. diff --git a/base/array.jl b/base/array.jl index d883c620428e4a..e90723957afdbc 100644 --- a/base/array.jl +++ b/base/array.jl @@ -124,8 +124,8 @@ const DenseVecOrMat{T} = Union{DenseVector{T}, DenseMatrix{T}} @_safeindex This internal macro converts: -- `getindex(xs::Tuple, )` -> `__inbounds_getindex(args...)` -- `setindex!(xs::Vector, args...)` -> `__inbounds_setindex!(xs, args...)` +- `getindex(xs::Tuple, i::Int)` -> `__safe_getindex(xs, i)` +- `setindex!(xs::Vector{T}, x, i::Int)` -> `__safe_setindex!(xs, x, i)` to tell the compiler that indexing operations within the applied expression are always inbounds and do not need to taint `:consistent` and `:nothrow`. """ @@ -143,10 +143,10 @@ function _safeindex(__module__, ex) for i = 2:length(lhs.args) args[i-1] = _safeindex(__module__, lhs.args[i]) end - return Expr(:call, GlobalRef(__module__, :__inbounds_setindex!), xs, _safeindex(__module__, rhs), args...) + return Expr(:call, GlobalRef(__module__, :__safe_setindex!), xs, _safeindex(__module__, rhs), args...) end elseif ex.head === :ref # xs[i] - return Expr(:call, GlobalRef(__module__, :__inbounds_getindex), ex.args...) + return Expr(:call, GlobalRef(__module__, :__safe_getindex), ex.args...) end args = Vector{Any}(undef, length(ex.args)) for i = 1:length(ex.args) @@ -236,6 +236,7 @@ sizeof(a::Array) = length(a) * elsize(typeof(a)) # n.b. this ignores bitsunion b function isassigned(a::Array, i::Int...) @inline + @_noub_if_noinbounds_meta @boundscheck checkbounds(Bool, a, i...) || return false ii = _sub2ind(size(a), i...) return @inbounds isassigned(memoryref(a.ref, ii, false)) @@ -243,6 +244,7 @@ end function isassigned(a::Vector, i::Int) # slight compiler simplification for the most common case @inline + @_noub_if_noinbounds_meta @boundscheck checkbounds(Bool, a, i) || return false return @inbounds isassigned(memoryref(a.ref, i, false)) end @@ -962,29 +964,23 @@ Dict{String, Int64} with 2 entries: function setindex! end function setindex!(A::Array{T}, x, i::Int) where {T} + @_noub_if_noinbounds_meta @boundscheck (i - 1)%UInt < length(A)%UInt || throw_boundserror(A, (i,)) memoryrefset!(memoryref(A.ref, i, false), x isa T ? x : convert(T,x)::T, :not_atomic, false) return A end function setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} @inline + @_noub_if_noinbounds_meta @boundscheck checkbounds(A, i1, i2, I...) # generally _to_linear_index requires bounds checking memoryrefset!(memoryref(A.ref, _to_linear_index(A, i1, i2, I...), false), x isa T ? x : convert(T,x)::T, :not_atomic, false) return A end -function __inbounds_setindex!(A::Array{T}, x, i::Int) where {T} - @inline - val = x isa T ? x : convert(T,x)::T - memoryrefset!(memoryref(A.ref, i, false), val, :not_atomic, false) - return A -end -function __inbounds_setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} - @inline - val = x isa T ? x : convert(T,x)::T - memoryrefset!(memoryref(A.ref, _to_linear_index(A, i1, i2, I...), false), val, :not_atomic, false) - return A -end +__safe_setindex!(A::Vector{T}, x::T, i::Int) where {T} = (@inline; @_nothrow_noub_meta; + memoryrefset!(memoryref(A.ref, i, false), x, :not_atomic, false); return A) +__safe_setindex!(A::Vector{T}, x, i::Int) where {T} = (@inline; + __safe_setindex!(A, convert(T, x)::T, i)) # This is redundant with the abstract fallbacks but needed and helpful for bootstrap function setindex!(A::Array, X::AbstractArray, I::AbstractVector{Int}) diff --git a/base/boot.jl b/base/boot.jl index 604817cbf2117d..ff3502c2f418ea 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -281,7 +281,8 @@ macro _foldable_meta() #=:terminates_locally=#false, #=:notaskstate=#true, #=:inaccessiblememonly=#true, - #=:noub=#true)) + #=:noub=#true, + #=:noub_if_noinbounds=#false)) end macro inline() Expr(:meta, :inline) end @@ -342,6 +343,9 @@ end struct ConcurrencyViolationError <: Exception msg::AbstractString end +struct MissingCodeError <: Exception + mi::MethodInstance +end struct InterruptException <: Exception end struct DomainError <: Exception val @@ -477,12 +481,12 @@ end) function CodeInstance( mi::MethodInstance, @nospecialize(rettype), @nospecialize(inferred_const), @nospecialize(inferred), const_flags::Int32, min_world::UInt, max_world::UInt, - ipo_effects::UInt32, effects::UInt32, @nospecialize(argescapes#=::Union{Nothing,Vector{ArgEscapeInfo}}=#), + ipo_effects::UInt32, effects::UInt32, @nospecialize(analysis_results), relocatability::UInt8) return ccall(:jl_new_codeinst, Ref{CodeInstance}, (Any, Any, Any, Any, Int32, UInt, UInt, UInt32, UInt32, Any, UInt8), mi, rettype, inferred_const, inferred, const_flags, min_world, max_world, - ipo_effects, effects, argescapes, + ipo_effects, effects, analysis_results, relocatability) end GlobalRef(m::Module, s::Symbol) = ccall(:jl_module_globalref, Ref{GlobalRef}, (Any, Any), m, s) diff --git a/base/c.jl b/base/c.jl index 1182033802aad5..eb552d3507662b 100644 --- a/base/c.jl +++ b/base/c.jl @@ -409,6 +409,6 @@ macro ccall(expr) return ccall_macro_lower(:ccall, ccall_macro_parse(expr)...) end -macro ccall_effects(effects::UInt8, expr) +macro ccall_effects(effects::UInt16, expr) return ccall_macro_lower((:ccall, effects), ccall_macro_parse(expr)...) end diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index a092b0312eaf11..f173f19fe00809 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -10,7 +10,8 @@ call_result_unused(si::StmtInfo) = !si.used function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), arginfo::ArgInfo, si::StmtInfo, @nospecialize(atype), sv::AbsIntState, max_methods::Int) - ⊑ₚ = ⊑(ipo_lattice(interp)) + 𝕃ₚ, 𝕃ᵢ = ipo_lattice(interp), typeinf_lattice(interp) + ⊑ₚ = ⊑(𝕃ₚ) if !should_infer_this_call(interp, sv) add_remark!(interp, sv, "Skipped call in throw block") # At this point we are guaranteed to end up throwing on this path, @@ -21,7 +22,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), end argtypes = arginfo.argtypes - matches = find_matching_methods(typeinf_lattice(interp), argtypes, atype, method_table(interp), + matches = find_matching_methods(𝕃ᵢ, argtypes, atype, method_table(interp), InferenceParams(interp).max_union_splitting, max_methods) if isa(matches, FailedMethodMatch) add_remark!(interp, sv, matches.reason) @@ -40,7 +41,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), fargs = arginfo.fargs all_effects = EFFECTS_TOTAL - 𝕃ₚ = ipo_lattice(interp) for i in 1:napplicable match = applicable[i]::MethodMatch method = match.method @@ -131,9 +131,9 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), Any[Bottom for _ in 1:length(argtypes)] end for i = 1:length(argtypes) - cnd = conditional_argtype(this_conditional, sig, argtypes, i) - conditionals[1][i] = tmerge(conditionals[1][i], cnd.thentype) - conditionals[2][i] = tmerge(conditionals[2][i], cnd.elsetype) + cnd = conditional_argtype(𝕃ᵢ, this_conditional, sig, argtypes, i) + conditionals[1][i] = tmerge(𝕃ᵢ, conditionals[1][i], cnd.thentype) + conditionals[2][i] = tmerge(𝕃ᵢ, conditionals[2][i], cnd.elsetype) end end if bail_out_call(interp, InferenceLoopState(sig, rettype, all_effects), sv) @@ -320,7 +320,7 @@ function from_interprocedural!(interp::AbstractInterpreter, @nospecialize(rt), s arginfo::ArgInfo, @nospecialize(maybecondinfo)) rt = collect_limitations!(rt, sv) if isa(rt, InterMustAlias) - rt = from_intermustalias(rt, arginfo) + rt = from_intermustalias(rt, arginfo, sv) elseif is_lattice_bool(ipo_lattice(interp), rt) if maybecondinfo === nothing rt = widenconditional(rt) @@ -340,10 +340,10 @@ function collect_limitations!(@nospecialize(typ), sv::InferenceState) return typ end -function from_intermustalias(rt::InterMustAlias, arginfo::ArgInfo) +function from_intermustalias(rt::InterMustAlias, arginfo::ArgInfo, sv::AbsIntState) fargs = arginfo.fargs if fargs !== nothing && 1 ≤ rt.slot ≤ length(fargs) - arg = fargs[rt.slot] + arg = ssa_def_slot(fargs[rt.slot], sv) if isa(arg, SlotNumber) argtyp = widenslotwrapper(arginfo.argtypes[rt.slot]) if rt.vartyp ⊑ argtyp @@ -394,7 +394,7 @@ function from_interconditional(𝕃ᵢ::AbstractLattice, @nospecialize(rt), sv:: new_elsetype = maybecondinfo[2][i] else # otherwise compute it on the fly - cnd = conditional_argtype(rt, maybecondinfo, argtypes, i) + cnd = conditional_argtype(𝕃ᵢ, rt, maybecondinfo, argtypes, i) new_thentype = cnd.thentype new_elsetype = cnd.elsetype end @@ -440,11 +440,12 @@ function from_interconditional(𝕃ᵢ::AbstractLattice, @nospecialize(rt), sv:: return widenconditional(rt) end -function conditional_argtype(@nospecialize(rt), @nospecialize(sig), argtypes::Vector{Any}, i::Int) +function conditional_argtype(𝕃ᵢ::AbstractLattice, @nospecialize(rt), @nospecialize(sig), + argtypes::Vector{Any}, i::Int) if isa(rt, InterConditional) && rt.slot == i return rt else - thentype = elsetype = tmeet(widenslotwrapper(argtypes[i]), fieldtype(sig, i)) + thentype = elsetype = tmeet(𝕃ᵢ, widenslotwrapper(argtypes[i]), fieldtype(sig, i)) condval = maybe_extract_const_bool(rt) condval === true && (elsetype = Bottom) condval === false && (thentype = Bottom) @@ -1334,6 +1335,9 @@ function ssa_def_slot(@nospecialize(arg), sv::InferenceState) return arg end +# No slots in irinterp +ssa_def_slot(@nospecialize(arg), sv::IRInterpretationState) = nothing + struct AbstractIterationResult cti::Vector{Any} info::MaybeAbstractIterationInfo @@ -1743,7 +1747,7 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs a3 = argtypes[3] if isa(a3, Const) if rt !== Bottom && !isalreadyconst(rt) - var = fargs[2] + var = ssa_def_slot(fargs[2], sv) if isa(var, SlotNumber) vartyp = widenslotwrapper(argtypes[2]) fldidx = maybe_const_fldidx(vartyp, a3.val) @@ -2013,7 +2017,9 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), return abstract_applicable(interp, argtypes, sv, max_methods) end rt = abstract_call_builtin(interp, f, arginfo, sv) - effects = builtin_effects(𝕃ᵢ, f, arginfo, rt) + ft = popfirst!(argtypes) + effects = builtin_effects(𝕃ᵢ, f, argtypes, rt) + pushfirst!(argtypes, ft) return CallMeta(rt, effects, NoCallInfo()) elseif isa(f, Core.OpaqueClosure) # calling an OpaqueClosure about which we have no information returns no information @@ -2039,8 +2045,7 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), abstract_call_gf_by_type(interp, f, ArgInfo(nothing, T), si, atype, sv, max_methods) end pT = typevar_tfunc(𝕃ᵢ, n, lb_var, ub_var) - effects = builtin_effects(𝕃ᵢ, Core._typevar, ArgInfo(nothing, - Any[Const(Core._typevar), n, lb_var, ub_var]), pT) + effects = builtin_effects(𝕃ᵢ, Core._typevar, Any[n, lb_var, ub_var], pT) return CallMeta(pT, effects, call.info) elseif f === UnionAll call = abstract_call_gf_by_type(interp, f, ArgInfo(nothing, Any[Const(UnionAll), Any, Any]), si, Tuple{Type{UnionAll}, Any, Any}, sv, max_methods) @@ -2347,24 +2352,22 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp elseif ehead === :new t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv), true) ut = unwrap_unionall(t) - consistent = noub = ALWAYS_FALSE - nothrow = false if isa(ut, DataType) && !isabstracttype(ut) ismutable = ismutabletype(ut) fcount = datatype_fieldcount(ut) nargs = length(e.args) - 1 - if (fcount === nothing || (fcount > nargs && (let t = t + has_any_uninitialized = (fcount === nothing || (fcount > nargs && (let t = t any(i::Int -> !is_undefref_fieldtype(fieldtype(t, i)), (nargs+1):fcount) end))) - # allocation with undefined field leads to undefined behavior and should taint `:noub` + if has_any_uninitialized + # allocation with undefined field is inconsistent always + consistent = ALWAYS_FALSE elseif ismutable - # mutable object isn't `:consistent`, but we still have a chance that + # mutable allocation isn't `:consistent`, but we still have a chance that # return type information later refines the `:consistent`-cy of the method consistent = CONSISTENT_IF_NOTRETURNED - noub = ALWAYS_TRUE else - consistent = ALWAYS_TRUE - noub = ALWAYS_TRUE + consistent = ALWAYS_TRUE # immutable allocation is consistent end if isconcretedispatch(t) nothrow = true @@ -2406,9 +2409,13 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp end else t = refine_partial_type(t) + nothrow = false end + else + consistent = ALWAYS_FALSE + nothrow = false end - effects = Effects(EFFECTS_TOTAL; consistent, nothrow, noub) + effects = Effects(EFFECTS_TOTAL; consistent, nothrow) elseif ehead === :splatnew t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv), true) nothrow = false # TODO: More precision @@ -2599,7 +2606,7 @@ function abstract_eval_foreigncall(interp::AbstractInterpreter, e::Expr, vtypes: abstract_eval_value(interp, x, vtypes, sv) end cconv = e.args[5] - if isa(cconv, QuoteNode) && (v = cconv.value; isa(v, Tuple{Symbol, UInt8})) + if isa(cconv, QuoteNode) && (v = cconv.value; isa(v, Tuple{Symbol, UInt16})) override = decode_effects_override(v[2]) effects = Effects(effects; consistent = override.consistent ? ALWAYS_TRUE : effects.consistent, @@ -2608,7 +2615,7 @@ function abstract_eval_foreigncall(interp::AbstractInterpreter, e::Expr, vtypes: terminates = override.terminates_globally ? true : effects.terminates, notaskstate = override.notaskstate ? true : effects.notaskstate, inaccessiblememonly = override.inaccessiblememonly ? ALWAYS_TRUE : effects.inaccessiblememonly, - noub = override.noub ? ALWAYS_TRUE : effects.noub) + noub = override.noub ? ALWAYS_TRUE : override.noub_if_noinbounds ? NOUB_IF_NOINBOUNDS : effects.noub) end return RTEffects(t, effects) end @@ -3047,6 +3054,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) @goto branch elseif isa(stmt, GotoIfNot) condx = stmt.cond + condxslot = ssa_def_slot(condx, frame) condt = abstract_eval_value(interp, condx, currstate, frame) if condt === Bottom ssavaluetypes[currpc] = Bottom @@ -3054,10 +3062,10 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) @goto find_next_bb end orig_condt = condt - if !(isa(condt, Const) || isa(condt, Conditional)) && isa(condx, SlotNumber) + if !(isa(condt, Const) || isa(condt, Conditional)) && isa(condxslot, SlotNumber) # if this non-`Conditional` object is a slot, we form and propagate # the conditional constraint on it - condt = Conditional(condx, Const(true), Const(false)) + condt = Conditional(condxslot, Const(true), Const(false)) end condval = maybe_extract_const_bool(condt) nothrow = (condval !== nothing) || ⊑(𝕃ᵢ, orig_condt, Bool) diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index 485ba5e4166657..fc774efbbee85d 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -197,15 +197,14 @@ is_nothrow(effects::Effects) = effects.nothrow is_terminates(effects::Effects) = effects.terminates is_notaskstate(effects::Effects) = effects.notaskstate is_inaccessiblememonly(effects::Effects) = effects.inaccessiblememonly === ALWAYS_TRUE +is_noub(effects::Effects) = effects.noub === ALWAYS_TRUE +is_noub_if_noinbounds(effects::Effects) = effects.noub === NOUB_IF_NOINBOUNDS is_nonoverlayed(effects::Effects) = effects.nonoverlayed -is_noub(effects::Effects, noinbounds::Bool=true) = - effects.noub === ALWAYS_TRUE || (noinbounds && effects.noub === NOUB_IF_NOINBOUNDS) - # implies `is_notaskstate` & `is_inaccessiblememonly`, but not explicitly checked here is_foldable(effects::Effects) = is_consistent(effects) && - is_noub(effects) && + (is_noub(effects) || is_noub_if_noinbounds(effects)) && is_effect_free(effects) && is_terminates(effects) @@ -262,29 +261,32 @@ struct EffectsOverride notaskstate::Bool inaccessiblememonly::Bool noub::Bool + noub_if_noinbounds::Bool end function encode_effects_override(eo::EffectsOverride) - e = 0x00 - eo.consistent && (e |= (0x01 << 0)) - eo.effect_free && (e |= (0x01 << 1)) - eo.nothrow && (e |= (0x01 << 2)) - eo.terminates_globally && (e |= (0x01 << 3)) - eo.terminates_locally && (e |= (0x01 << 4)) - eo.notaskstate && (e |= (0x01 << 5)) - eo.inaccessiblememonly && (e |= (0x01 << 6)) - eo.noub && (e |= (0x01 << 7)) + e = 0x0000 + eo.consistent && (e |= (0x0001 << 0)) + eo.effect_free && (e |= (0x0001 << 1)) + eo.nothrow && (e |= (0x0001 << 2)) + eo.terminates_globally && (e |= (0x0001 << 3)) + eo.terminates_locally && (e |= (0x0001 << 4)) + eo.notaskstate && (e |= (0x0001 << 5)) + eo.inaccessiblememonly && (e |= (0x0001 << 6)) + eo.noub && (e |= (0x0001 << 7)) + eo.noub_if_noinbounds && (e |= (0x0001 << 8)) return e end -function decode_effects_override(e::UInt8) +function decode_effects_override(e::UInt16) return EffectsOverride( - (e & (0x01 << 0)) != 0x00, - (e & (0x01 << 1)) != 0x00, - (e & (0x01 << 2)) != 0x00, - (e & (0x01 << 3)) != 0x00, - (e & (0x01 << 4)) != 0x00, - (e & (0x01 << 5)) != 0x00, - (e & (0x01 << 6)) != 0x00, - (e & (0x01 << 7)) != 0x00) + !iszero(e & (0x0001 << 0)), + !iszero(e & (0x0001 << 1)), + !iszero(e & (0x0001 << 2)), + !iszero(e & (0x0001 << 3)), + !iszero(e & (0x0001 << 4)), + !iszero(e & (0x0001 << 5)), + !iszero(e & (0x0001 << 6)), + !iszero(e & (0x0001 << 7)), + !iszero(e & (0x0001 << 8))) end diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 62a20896334b34..68118f82fb5f22 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -24,9 +24,7 @@ const IR_FLAG_INLINE = UInt32(1) << 1 # This statement is marked as @noinline by user const IR_FLAG_NOINLINE = UInt32(1) << 2 const IR_FLAG_THROW_BLOCK = UInt32(1) << 3 -# This statement may be removed if its result is unused. In particular, -# it must be both :effect_free and :nothrow. -# TODO: Separate these out. +# This statement was proven :effect_free const IR_FLAG_EFFECT_FREE = UInt32(1) << 4 # This statement was proven not to throw const IR_FLAG_NOTHROW = UInt32(1) << 5 @@ -39,6 +37,12 @@ const IR_FLAG_REFINED = UInt32(1) << 7 # This is :noub == ALWAYS_TRUE const IR_FLAG_NOUB = UInt32(1) << 8 +# TODO: Both of these should eventually go away once +# This is :effect_free == EFFECT_FREE_IF_INACCESSIBLEMEMONLY +const IR_FLAG_EFIIMO = UInt32(1) << 9 +# This is :inaccessiblememonly == INACCESSIBLEMEM_OR_ARGMEMONLY +const IR_FLAG_INACCESSIBLE_OR_ARGMEM = UInt32(1) << 10 + const IR_FLAGS_EFFECTS = IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW | IR_FLAG_CONSISTENT | IR_FLAG_NOUB const TOP_TUPLE = GlobalRef(Core, :tuple) @@ -295,8 +299,8 @@ function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospe isa(f, Builtin) || return (false, false, false) # Needs to be handled in inlining to look at the callee effects f === Core._apply_iterate && return (false, false, false) - argtypes = Any[argextype(args[arg], src) for arg in 1:length(args)] - effects = builtin_effects(𝕃ₒ, f, ArgInfo(args, argtypes), rt) + argtypes = Any[argextype(args[arg], src) for arg in 2:length(args)] + effects = builtin_effects(𝕃ₒ, f, argtypes, rt) consistent = is_consistent(effects) effect_free = is_effect_free(effects) nothrow = is_nothrow(effects) @@ -514,7 +518,6 @@ function get!(lazyagdomtree::LazyAugmentedDomtree) return lazyagdomtree.agdomtree = AugmentedDomtree(cfg, domtree) end -# TODO refine `:effect_free` using EscapeAnalysis mutable struct PostOptAnalysisState const result::InferenceResult const ir::IRCode @@ -522,8 +525,10 @@ mutable struct PostOptAnalysisState const tpdum::TwoPhaseDefUseMap const lazypostdomtree::LazyPostDomtree const lazyagdomtree::LazyAugmentedDomtree + const ea_analysis_pending::Vector{Int} all_retpaths_consistent::Bool all_effect_free::Bool + effect_free_if_argmem_only::Union{Nothing,Bool} all_nothrow::Bool all_noub::Bool any_conditional_ub::Bool @@ -532,12 +537,14 @@ mutable struct PostOptAnalysisState tpdum = TwoPhaseDefUseMap(length(ir.stmts)) lazypostdomtree = LazyPostDomtree(ir) lazyagdomtree = LazyAugmentedDomtree(ir) - return new(result, ir, inconsistent, tpdum, lazypostdomtree, lazyagdomtree, true, true, true, true, false) + return new(result, ir, inconsistent, tpdum, lazypostdomtree, lazyagdomtree, Int[], + true, true, nothing, true, true, false) end end give_up_refinements!(sv::PostOptAnalysisState) = - sv.all_retpaths_consistent = sv.all_effect_free = sv.all_nothrow = sv.all_noub = false + sv.all_retpaths_consistent = sv.all_effect_free = sv.effect_free_if_argmem_only = + sv.all_nothrow = sv.all_noub = false function any_refinable(sv::PostOptAnalysisState) effects = sv.result.ipo_effects @@ -547,12 +554,47 @@ function any_refinable(sv::PostOptAnalysisState) (!is_noub(effects) & sv.all_noub)) end -function refine_effects!(sv::PostOptAnalysisState) +struct GetNativeEscapeCache{CodeCache} + code_cache::CodeCache + GetNativeEscapeCache(code_cache::CodeCache) where CodeCache = new{CodeCache}(code_cache) +end +GetNativeEscapeCache(interp::AbstractInterpreter) = GetNativeEscapeCache(code_cache(interp)) +function ((; code_cache)::GetNativeEscapeCache)(mi::MethodInstance) + codeinst = get(code_cache, mi, nothing) + codeinst isa CodeInstance || return false + argescapes = traverse_analysis_results(codeinst) do @nospecialize result + return result isa EscapeAnalysis.ArgEscapeCache ? result : nothing + end + if argescapes !== nothing + return argescapes + end + effects = decode_effects(codeinst.ipo_purity_bits) + if is_effect_free(effects) && is_inaccessiblememonly(effects) + # We might not have run EA on simple frames without any escapes (e.g. when optimization + # is skipped when result is constant-folded by abstract interpretation). If those + # frames aren't inlined, the accuracy of EA for caller context takes a big hit. + # This is a HACK to avoid that, but obviously, a more comprehensive fix would be ideal. + return true + end + return false +end + +function refine_effects!(interp::AbstractInterpreter, sv::PostOptAnalysisState) + if !is_effect_free(sv.result.ipo_effects) && sv.all_effect_free && !isempty(sv.ea_analysis_pending) + ir = sv.ir + nargs = length(ir.argtypes) + estate = EscapeAnalysis.analyze_escapes(ir, nargs, optimizer_lattice(interp), GetNativeEscapeCache(interp)) + argescapes = EscapeAnalysis.ArgEscapeCache(estate) + stack_analysis_result!(sv.result, argescapes) + validate_mutable_arg_escapes!(estate, sv) + end + any_refinable(sv) || return false effects = sv.result.ipo_effects sv.result.ipo_effects = Effects(effects; consistent = sv.all_retpaths_consistent ? ALWAYS_TRUE : effects.consistent, - effect_free = sv.all_effect_free ? ALWAYS_TRUE : effects.effect_free, + effect_free = sv.all_effect_free ? ALWAYS_TRUE : + sv.effect_free_if_argmem_only === true ? EFFECT_FREE_IF_INACCESSIBLEMEMONLY : effects.effect_free, nothrow = sv.all_nothrow ? true : effects.nothrow, noub = sv.all_noub ? (sv.any_conditional_ub ? NOUB_IF_NOINBOUNDS : ALWAYS_TRUE) : effects.noub) return true @@ -563,52 +605,133 @@ function is_ipo_dataflow_analysis_profitable(effects::Effects) is_nothrow(effects) && is_noub(effects)) end -function is_getfield_with_boundscheck_arg(@nospecialize(stmt), sv::PostOptAnalysisState) - is_known_call(stmt, getfield, sv.ir) || return false - length(stmt.args) < 4 && return false +function iscall_with_boundscheck(@nospecialize(stmt), sv::PostOptAnalysisState) + isexpr(stmt, :call) || return false + ft = argextype(stmt.args[1], sv.ir) + f = singleton_type(ft) + f === nothing && return false + if f === getfield + nargs = 4 + elseif f === memoryref || f === memoryrefget || f === memoryref_isassigned + nargs = 4 + elseif f === memoryrefset! + nargs = 5 + else + return false + end + length(stmt.args) < nargs && return false boundscheck = stmt.args[end] argextype(boundscheck, sv.ir) === Bool || return false isa(boundscheck, SSAValue) || return false return true end +function check_all_args_noescape!(sv::PostOptAnalysisState, ir::IRCode, @nospecialize(stmt), + estate::EscapeAnalysis.EscapeState) + stmt isa Expr || return false + if isexpr(stmt, :invoke) + startidx = 2 + elseif isexpr(stmt, :new) + startidx = 1 + else + return false + end + for i = startidx:length(stmt.args) + arg = stmt.args[i] + argt = argextype(arg, ir) + if is_mutation_free_argtype(argt) + continue + end + # See if we can find the allocation + if isa(arg, Argument) + if EscapeAnalysis.has_no_escape(EscapeAnalysis.ignore_argescape(estate[arg])) + # Even if we prove everything else effect_free, the best we can + # say is :effect_free_if_argmem_only + if sv.effect_free_if_argmem_only === nothing + sv.effect_free_if_argmem_only = true + end + else + sv.effect_free_if_argmem_only = false + end + return false + elseif isa(arg, SSAValue) + EscapeAnalysis.has_no_escape(estate[arg]) || return false + check_all_args_noescape!(sv, ir, ir[arg][:stmt], estate) || return false + else + return false + end + end + return true +end + +function validate_mutable_arg_escapes!(estate::EscapeAnalysis.EscapeState, sv::PostOptAnalysisState) + ir = sv.ir + for idx in sv.ea_analysis_pending + # See if any mutable memory was allocated in this function and determined + # not to escape. + inst = ir[SSAValue(idx)] + stmt = inst[:stmt] + if !check_all_args_noescape!(sv, ir, stmt, estate) + return sv.all_effect_free = false + end + end + return true +end + function is_conditional_noub(inst::Instruction, sv::PostOptAnalysisState) - # Special case: `:boundscheck` into `getfield` stmt = inst[:stmt] - is_getfield_with_boundscheck_arg(stmt, sv) || return false - barg = stmt.args[end] + iscall_with_boundscheck(stmt, sv) || return false + barg = stmt.args[end]::SSAValue bstmt = sv.ir[barg][:stmt] isexpr(bstmt, :boundscheck) || return false # If IR_FLAG_INBOUNDS is already set, no more conditional ub (!isempty(bstmt.args) && bstmt.args[1] === false) && return false - sv.any_conditional_ub = true return true end +const IR_FLAGS_NEEDS_EA = IR_FLAG_EFIIMO | IR_FLAG_INACCESSIBLE_OR_ARGMEM + function scan_non_dataflow_flags!(inst::Instruction, sv::PostOptAnalysisState) flag = inst[:flag] + # If we can prove that the argmem does not escape the current function, we can + # refine this to :effect_free. + needs_ea_validation = (flag & IR_FLAGS_NEEDS_EA) == IR_FLAGS_NEEDS_EA stmt = inst[:stmt] - if !isterminator(stmt) && stmt !== nothing - # ignore control flow node – they are not removable on their own and thus not - # have `IR_FLAG_EFFECT_FREE` but still do not taint `:effect_free`-ness of - # the whole method invocation - sv.all_effect_free &= !iszero(flag & IR_FLAG_EFFECT_FREE) + if !needs_ea_validation + if !isterminator(stmt) && stmt !== nothing + # ignore control flow node – they are not removable on their own and thus not + # have `IR_FLAG_EFFECT_FREE` but still do not taint `:effect_free`-ness of + # the whole method invocation + sv.all_effect_free &= !iszero(flag & IR_FLAG_EFFECT_FREE) + end + elseif sv.all_effect_free + if (isexpr(stmt, :invoke) || isexpr(stmt, :new) || + # HACK for performance: limit the scope of EA to code with object field access only, + # since its abilities to reason about e.g. arrays are currently very limited anyways. + is_known_call(stmt, setfield!, sv.ir)) + push!(sv.ea_analysis_pending, inst.idx) + else + sv.all_effect_free = false + end end sv.all_nothrow &= !iszero(flag & IR_FLAG_NOTHROW) if iszero(flag & IR_FLAG_NOUB) - if !is_conditional_noub(inst, sv) + # Special case: `:boundscheck` into `getfield` or memory operations is `:noub_if_noinbounds` + if is_conditional_noub(inst, sv) + sv.any_conditional_ub = true + else sv.all_noub = false end end end -function scan_inconsistency!(inst::Instruction, idx::Int, sv::PostOptAnalysisState) +function scan_inconsistency!(inst::Instruction, sv::PostOptAnalysisState) flag = inst[:flag] stmt_inconsistent = iszero(flag & IR_FLAG_CONSISTENT) stmt = inst[:stmt] - # Special case: For getfield, we allow inconsistency of the :boundscheck argument + # Special case: For `getfield` and memory operations, we allow inconsistency of the :boundscheck argument (; inconsistent, tpdum) = sv - if is_getfield_with_boundscheck_arg(stmt, sv) + if iscall_with_boundscheck(stmt, sv) for i = 1:(length(stmt.args)-1) val = stmt.args[i] if isa(val, SSAValue) @@ -625,7 +748,7 @@ function scan_inconsistency!(inst::Instruction, idx::Int, sv::PostOptAnalysisSta end end end - stmt_inconsistent && push!(inconsistent, idx) + stmt_inconsistent && push!(inconsistent, inst.idx) return stmt_inconsistent end @@ -633,7 +756,7 @@ struct ScanStmt sv::PostOptAnalysisState end -function ((; sv)::ScanStmt)(inst::Instruction, idx::Int, lstmt::Int, bb::Int) +function ((; sv)::ScanStmt)(inst::Instruction, lstmt::Int, bb::Int) stmt = inst[:stmt] if isexpr(stmt, :enter) @@ -644,9 +767,9 @@ function ((; sv)::ScanStmt)(inst::Instruction, idx::Int, lstmt::Int, bb::Int) scan_non_dataflow_flags!(inst, sv) - stmt_inconsistent = scan_inconsistency!(inst, idx, sv) + stmt_inconsistent = scan_inconsistency!(inst, sv) - if stmt_inconsistent && idx == lstmt + if stmt_inconsistent && inst.idx == lstmt if isa(stmt, ReturnNode) && isdefined(stmt, :val) sv.all_retpaths_consistent = false elseif isa(stmt, GotoIfNot) @@ -703,7 +826,7 @@ function check_inconsistentcy!(sv::PostOptAnalysisState, scanner::BBScanner) idx = popfirst!(stmt_ip) inst = ir[SSAValue(idx)] stmt = inst[:stmt] - if is_getfield_with_boundscheck_arg(stmt, sv) + if iscall_with_boundscheck(stmt, sv) any_non_boundscheck_inconsistent = false for i = 1:(length(stmt.args)-1) val = stmt.args[i] @@ -734,7 +857,11 @@ function check_inconsistentcy!(sv::PostOptAnalysisState, scanner::BBScanner) end function ipo_dataflow_analysis!(interp::AbstractInterpreter, ir::IRCode, result::InferenceResult) - is_ipo_dataflow_analysis_profitable(result.ipo_effects) || return false + if !is_ipo_dataflow_analysis_profitable(result.ipo_effects) + return false + end + + @assert isempty(ir.new_nodes) "IRCode should be compacted before post-opt analysis" sv = PostOptAnalysisState(result, ir) scanner = BBScanner(ir) @@ -746,7 +873,7 @@ function ipo_dataflow_analysis!(interp::AbstractInterpreter, ir::IRCode, result: check_inconsistentcy!(sv, scanner) else # No longer any dataflow concerns, just scan the flags - scan!(scanner, false) do inst::Instruction, idx::Int, lstmt::Int, bb::Int + scan!(scanner, false) do inst::Instruction, lstmt::Int, bb::Int scan_non_dataflow_flags!(inst, sv) # bail out early if there are no possibilities to refine the effects if !any_refinable(sv) @@ -757,7 +884,7 @@ function ipo_dataflow_analysis!(interp::AbstractInterpreter, ir::IRCode, result: end end - return refine_effects!(sv) + return refine_effects!(interp, sv) end # run the optimization work diff --git a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl index a770a4a92146c3..820975d4055250 100644 --- a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl +++ b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl @@ -27,7 +27,7 @@ using Core.Compiler: # Core.Compiler specific definitions Bottom, IRCode, IR_FLAG_NOTHROW, InferenceResult, SimpleInferenceLattice, argextype, check_effect_free!, fieldcount_noerror, hasintersect, intrinsic_nothrow, is_meta_expr_head, isbitstype, isexpr, println, setfield!_nothrow, singleton_type, - try_compute_field, try_compute_fieldidx, widenconst, ⊑ + try_compute_field, try_compute_fieldidx, widenconst, ⊑, AbstractLattice include(x) = _TOP_MOD.include(@__MODULE__, x) if _TOP_MOD === Core.Compiler @@ -37,7 +37,6 @@ else end const AInfo = IdSet{Any} -const 𝕃ₒ = SimpleInferenceLattice.instance """ x::EscapeInfo @@ -598,10 +597,11 @@ struct LivenessChange <: Change end const Changes = Vector{Change} -struct AnalysisState{T} +struct AnalysisState{T, L <: AbstractLattice} ir::IRCode estate::EscapeState changes::Changes + 𝕃ₒ::L get_escape_cache::T end @@ -610,17 +610,17 @@ end Analyzes escape information in `ir`: - `nargs`: the number of actual arguments of the analyzed call -- `get_escape_cache(::MethodInstance) -> Union{Nothing,ArgEscapeCache}`: +- `get_escape_cache(::MethodInstance) -> Union{Bool,ArgEscapeCache}`: retrieves cached argument escape information """ -function analyze_escapes(ir::IRCode, nargs::Int, get_escape_cache) +function analyze_escapes(ir::IRCode, nargs::Int, 𝕃ₒ::AbstractLattice, get_escape_cache) stmts = ir.stmts nstmts = length(stmts) + length(ir.new_nodes.stmts) tryregions, arrayinfo = compute_frameinfo(ir) estate = EscapeState(nargs, nstmts, arrayinfo) changes = Changes() # keeps changes that happen at current statement - astate = AnalysisState(ir, estate, changes, get_escape_cache) + astate = AnalysisState(ir, estate, changes, 𝕃ₒ, get_escape_cache) local debug_itr_counter = 0 while true @@ -1061,11 +1061,14 @@ function escape_invoke!(astate::AnalysisState, pc::Int, args::Vector{Any}) first_idx, last_idx = 2, length(args) # TODO inspect `astate.ir.stmts[pc][:info]` and use const-prop'ed `InferenceResult` if available cache = astate.get_escape_cache(mi) - if cache === nothing - return add_conservative_changes!(astate, pc, args, 2) - else - cache = cache::ArgEscapeCache + if cache isa Bool + if cache + return nothing # guaranteed to have no escape + else + return add_conservative_changes!(astate, pc, args, 2) + end end + cache = cache::ArgEscapeCache ret = SSAValue(pc) retinfo = astate.estate[ret] # escape information imposed on the call statement method = mi.def::Method @@ -1449,10 +1452,10 @@ function escape_builtin!(::typeof(setfield!), astate::AnalysisState, pc::Int, ar add_escape_change!(astate, val, ssainfo) # compute the throwness of this setfield! call here since builtin_nothrow doesn't account for that @label add_thrown_escapes - if length(args) == 4 && setfield!_nothrow(𝕃ₒ, + if length(args) == 4 && setfield!_nothrow(astate.𝕃ₒ, argextype(args[2], ir), argextype(args[3], ir), argextype(args[4], ir)) return true - elseif length(args) == 3 && setfield!_nothrow(𝕃ₒ, + elseif length(args) == 3 && setfield!_nothrow(astate.𝕃ₒ, argextype(args[2], ir), argextype(args[3], ir)) return true else diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 7f6514b16d6e76..f62e773567ac3e 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -383,12 +383,13 @@ function ir_prepare_inlining!(insert_node!::Inserter, inline_target::Union{IRCod return SSASubstitute(mi, argexprs, spvals_ssa, linetable_offset) end -function adjust_boundscheck!(inline_compact, idx′, stmt, boundscheck) +function adjust_boundscheck!(inline_compact::IncrementalCompact, idx′::Int, stmt::Expr, boundscheck::Symbol) if boundscheck === :off - length(stmt.args) == 0 && push!(stmt.args, false) + isempty(stmt.args) && push!(stmt.args, false) elseif boundscheck !== :propagate - length(stmt.args) == 0 && push!(stmt.args, true) + isempty(stmt.args) && push!(stmt.args, true) end + return nothing end function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector{Any}, @@ -398,11 +399,8 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector ssa_substitute = ir_prepare_inlining!(InsertHere(compact), compact, item.ir, item.mi, inlined_at, argexprs) - if boundscheck === :default || boundscheck === :propagate - if (compact.result[idx][:flag] & IR_FLAG_INBOUNDS) != 0 - boundscheck = :off - end - end + boundscheck = iszero(compact.result[idx][:flag] & IR_FLAG_INBOUNDS) ? boundscheck : :off + # If the iterator already moved on to the next basic block, # temporarily re-open in again. local return_value @@ -419,7 +417,7 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector inline_compact[idx′] = nothing insert_node! = InsertBefore(inline_compact, SSAValue(idx′)) stmt′ = ssa_substitute!(insert_node!, inline_compact[SSAValue(idx′)], stmt′, - ssa_substitute, boundscheck) + ssa_substitute) if isa(stmt′, ReturnNode) val = stmt′.val return_value = SSAValue(idx′) @@ -450,7 +448,7 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector inline_compact[idx′] = nothing insert_node! = InsertBefore(inline_compact, SSAValue(idx′)) stmt′ = ssa_substitute!(insert_node!, inline_compact[SSAValue(idx′)], stmt′, - ssa_substitute, boundscheck) + ssa_substitute) if isa(stmt′, ReturnNode) if isdefined(stmt′, :val) val = stmt′.val @@ -659,10 +657,7 @@ function batch_inline!(ir::IRCode, todo::Vector{Pair{Int,Any}}, propagate_inboun end finish_cfg_inline!(state) - boundscheck = :default - if boundscheck === :default && propagate_inbounds - boundscheck = :propagate - end + boundscheck = propagate_inbounds ? :propagate : :default let compact = IncrementalCompact(ir, CFGTransformState!(state.new_cfg_blocks, false)) # This needs to be a minimum and is more of a size hint @@ -1008,11 +1003,16 @@ function flags_for_effects(effects::Effects) end if is_effect_free(effects) flags |= IR_FLAG_EFFECT_FREE + elseif is_effect_free_if_inaccessiblememonly(effects) + flags |= IR_FLAG_EFIIMO + end + if is_inaccessiblemem_or_argmemonly(effects) + flags |= IR_FLAG_INACCESSIBLE_OR_ARGMEM end if is_nothrow(effects) flags |= IR_FLAG_NOTHROW end - if is_noub(effects, false) + if is_noub(effects) flags |= IR_FLAG_NOUB end return flags @@ -1265,7 +1265,7 @@ function check_effect_free!(ir::IRCode, idx::Int, @nospecialize(stmt), @nospecia elseif nothrow ir.stmts[idx][:flag] |= IR_FLAG_NOTHROW end - if !isexpr(stmt, :call) && !isexpr(stmt, :invoke) + if !(isexpr(stmt, :call) || isexpr(stmt, :invoke)) # 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 @@ -1845,9 +1845,9 @@ struct SSASubstitute end function ssa_substitute!(insert_node!::Inserter, subst_inst::Instruction, @nospecialize(val), - ssa_substitute::SSASubstitute, boundscheck::Symbol) + ssa_substitute::SSASubstitute) subst_inst[:line] += ssa_substitute.linetable_offset - return ssa_substitute_op!(insert_node!, subst_inst, val, ssa_substitute, boundscheck) + return ssa_substitute_op!(insert_node!, subst_inst, val, ssa_substitute) end function insert_spval!(insert_node!::Inserter, spvals_ssa::SSAValue, spidx::Int, do_isdefined::Bool) @@ -1864,7 +1864,7 @@ function insert_spval!(insert_node!::Inserter, spvals_ssa::SSAValue, spidx::Int, end function ssa_substitute_op!(insert_node!::Inserter, subst_inst::Instruction, @nospecialize(val), - ssa_substitute::SSASubstitute, boundscheck::Symbol) + ssa_substitute::SSASubstitute) if isa(val, Argument) return ssa_substitute.arg_replacements[val.n] end @@ -1920,7 +1920,7 @@ function ssa_substitute_op!(insert_node!::Inserter, subst_inst::Instruction, @no isa(val, Union{SSAValue, NewSSAValue}) && return val # avoid infinite loop urs = userefs(val) for op in urs - op[] = ssa_substitute_op!(insert_node!, subst_inst, op[], ssa_substitute, boundscheck) + op[] = ssa_substitute_op!(insert_node!, subst_inst, op[], ssa_substitute) end return urs[] end diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index 3f8160a088e8d3..c2c3d2ac0ed54f 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -1112,7 +1112,7 @@ end (this::Refiner)() = (this.result_flags[this.result_idx] |= IR_FLAG_REFINED; nothing) function process_phinode_values(old_values::Vector{Any}, late_fixup::Vector{Int}, - processed_idx::Int, result_idx::Int, + already_inserted, result_idx::Int, ssa_rename::Vector{Any}, used_ssas::Vector{Int}, new_new_used_ssas::Vector{Int}, do_rename_ssa::Bool, @@ -1123,7 +1123,7 @@ function process_phinode_values(old_values::Vector{Any}, late_fixup::Vector{Int} val = old_values[i] if isa(val, SSAValue) if do_rename_ssa - if val.id > processed_idx + if !already_inserted(i, OldSSAValue(val.id)) push!(late_fixup, result_idx) val = OldSSAValue(val.id) else @@ -1133,7 +1133,7 @@ function process_phinode_values(old_values::Vector{Any}, late_fixup::Vector{Int} used_ssas[val.id] += 1 end elseif isa(val, OldSSAValue) - if val.id > processed_idx + if !already_inserted(i, val) push!(late_fixup, result_idx) else # Always renumber these. do_rename_ssa applies only to actual SSAValues @@ -1293,6 +1293,7 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr (; result, ssa_rename, late_fixup, used_ssas, new_new_used_ssas) = compact (; cfg_transforms_enabled, fold_constant_branches, bb_rename_succ, bb_rename_pred, result_bbs) = compact.cfg_transform mark_refined! = Refiner(result.flag, result_idx) + already_inserted = (::Int, ssa::OldSSAValue)->ssa.id <= processed_idx if stmt === nothing ssa_rename[idx] = stmt elseif isa(stmt, OldSSAValue) @@ -1461,7 +1462,7 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr values = stmt.values end - values = process_phinode_values(values, late_fixup, processed_idx, result_idx, ssa_rename, used_ssas, new_new_used_ssas, do_rename_ssa, mark_refined!) + values = process_phinode_values(values, late_fixup, already_inserted, result_idx, ssa_rename, used_ssas, new_new_used_ssas, do_rename_ssa, mark_refined!) # Don't remove the phi node if it is before the definition of its value # because doing so can create forward references. This should only # happen with dead loops, but can cause problems when optimization @@ -1500,7 +1501,7 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr push!(values, value) end end - result[result_idx][:stmt] = PhiCNode(process_phinode_values(values, late_fixup, processed_idx, result_idx, ssa_rename, used_ssas, new_new_used_ssas, do_rename_ssa, mark_refined!)) + result[result_idx][:stmt] = PhiCNode(process_phinode_values(values, late_fixup, already_inserted, result_idx, ssa_rename, used_ssas, new_new_used_ssas, do_rename_ssa, mark_refined!)) result_idx += 1 else if isa(stmt, SSAValue) diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index 0e34cf9ce70bc5..fbd2112a1c0fef 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -21,20 +21,20 @@ function concrete_eval_invoke(interp::AbstractInterpreter, try Core._call_in_world_total(world, args...) catch - return Pair{Any,Tuple{Bool,Bool}}(Bottom, (false, is_noub(effects, false))) + return Pair{Any,Tuple{Bool,Bool}}(Bottom, (false, is_noub(effects))) end end return Pair{Any,Tuple{Bool,Bool}}(Const(value), (true, true)) else if is_constprop_edge_recursed(mi, irsv) - return Pair{Any,Tuple{Bool,Bool}}(nothing, (is_nothrow(effects), is_noub(effects, false))) + return Pair{Any,Tuple{Bool,Bool}}(nothing, (is_nothrow(effects), is_noub(effects))) end newirsv = IRInterpretationState(interp, code, mi, argtypes, world) if newirsv !== nothing newirsv.parent = irsv return ir_abstract_constant_propagation(interp, newirsv) end - return Pair{Any,Tuple{Bool,Bool}}(nothing, (is_nothrow(effects), is_noub(effects, false))) + return Pair{Any,Tuple{Bool,Bool}}(nothing, (is_nothrow(effects), is_noub(effects))) end end @@ -95,10 +95,10 @@ function kill_terminator_edges!(irsv::IRInterpretationState, term_idx::Int, bb:: end end -function reprocess_instruction!(interp::AbstractInterpreter, idx::Int, bb::Union{Int,Nothing}, - @nospecialize(stmt), @nospecialize(typ), irsv::IRInterpretationState) +function reprocess_instruction!(interp::AbstractInterpreter, inst::Instruction, idx::Int, + bb::Union{Int,Nothing}, irsv::IRInterpretationState) ir = irsv.ir - inst = ir[SSAValue(idx)] + stmt = inst[:stmt] if isa(stmt, GotoIfNot) cond = stmt.cond condval = maybe_extract_const_bool(argextype(cond, ir)) @@ -172,7 +172,7 @@ function reprocess_instruction!(interp::AbstractInterpreter, idx::Int, bb::Union inst[:stmt] = quoted(rt.val) end return true - elseif !⊑(typeinf_lattice(interp), typ, rt) + elseif !⊑(typeinf_lattice(interp), inst[:type], rt) inst[:type] = rt return true end @@ -226,7 +226,7 @@ function scan!(callback, scanner::BBScanner, forwards_only::Bool) lstmt = last(stmts) for idx = stmts inst = ir[SSAValue(idx)] - ret = callback(inst, idx, lstmt, bb) + ret = callback(inst, lstmt, bb) ret === nothing && return true ret::Bool || break idx == lstmt && process_terminator!(inst[:stmt], bb, bb_ip) && forwards_only && return false @@ -236,11 +236,11 @@ function scan!(callback, scanner::BBScanner, forwards_only::Bool) end function populate_def_use_map!(tpdum::TwoPhaseDefUseMap, scanner::BBScanner) - scan!(scanner, false) do inst::Instruction, idx::Int, lstmt::Int, bb::Int + scan!(scanner, false) do inst::Instruction, lstmt::Int, bb::Int for ur in userefs(inst) val = ur[] if isa(val, SSAValue) - push!(tpdum[val.id], idx) + push!(tpdum[val.id], inst.idx) end end return true @@ -251,10 +251,10 @@ populate_def_use_map!(tpdum::TwoPhaseDefUseMap, ir::IRCode) = function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IRInterpretationState; externally_refined::Union{Nothing,BitSet} = nothing) - interp = switch_to_irinterp(interp) - (; ir, tpdum, ssa_refined) = irsv + @assert isempty(ir.new_nodes) "IRCode should be compacted before irinterp" + all_rets = Int[] scanner = BBScanner(ir) @@ -263,15 +263,16 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR # Fast path: Scan both use counts and refinement in one single pass of # of the instructions. In the absence of backedges, this will # converge. - completed_scan = scan!(scanner, true) do inst::Instruction, idx::Int, lstmt::Int, bb::Int + completed_scan = scan!(scanner, true) do inst::Instruction, lstmt::Int, bb::Int + idx = inst.idx irsv.curridx = idx - stmt = ir.stmts[idx][:stmt] - typ = ir.stmts[idx][:type] - flag = ir.stmts[idx][:flag] + stmt = inst[:stmt] + typ = inst[:type] + flag = inst[:flag] any_refined = false if (flag & IR_FLAG_REFINED) != 0 any_refined = true - ir.stmts[idx][:flag] &= ~IR_FLAG_REFINED + inst[:flag] &= ~IR_FLAG_REFINED end for ur in userefs(stmt) val = ur[] @@ -292,11 +293,11 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR if typ === Bottom && !(idx == lstmt && is_terminator_or_phi) return true end - if (any_refined && reprocess_instruction!(interp, idx, bb, stmt, typ, irsv)) || + if (any_refined && reprocess_instruction!(interp, inst, idx, bb, irsv)) || (externally_refined !== nothing && idx in externally_refined) push!(ssa_refined, idx) - stmt = ir.stmts[idx][:stmt] - typ = ir.stmts[idx][:type] + stmt = inst[:stmt] + typ = inst[:type] end if typ === Bottom && !is_terminator_or_phi kill_terminator_edges!(irsv, lstmt, bb) @@ -316,7 +317,8 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR stmt_ip = BitSetBoundedMinPrioritySet(length(ir.stmts)) # Slow Path Phase 1.A: Complete use scanning - scan!(scanner, false) do inst::Instruction, idx::Int, lstmt::Int, bb::Int + scan!(scanner, false) do inst::Instruction, lstmt::Int, bb::Int + idx = inst.idx irsv.curridx = idx stmt = inst[:stmt] flag = inst[:flag] @@ -356,9 +358,7 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR idx = popfirst!(stmt_ip) irsv.curridx = idx inst = ir[SSAValue(idx)] - stmt = inst[:stmt] - typ = inst[:type] - if reprocess_instruction!(interp, idx, nothing, stmt, typ, irsv) + if reprocess_instruction!(interp, inst, idx, nothing, irsv) append!(stmt_ip, tpdum[idx]) end end diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index ce3d5f96337a62..21e0e9b213f1c2 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1259,8 +1259,7 @@ function try_inline_finalizer!(ir::IRCode, argexprs::Vector{Any}, idx::Int, stmt′ = ssamap(stmt′) do ssa::SSAValue ssa_rename[ssa.id] end - stmt′ = ssa_substitute_op!(InsertBefore(ir, SSAValue(idx)), inst, stmt′, - ssa_substitute, :default) + stmt′ = ssa_substitute_op!(InsertBefore(ir, SSAValue(idx)), inst, stmt′, ssa_substitute) ssa_rename[idx′] = insert_node!(ir, idx, NewInstruction(inst; stmt=stmt′, line=inst[:line]+ssa_substitute.linetable_offset), attach_after) @@ -2029,7 +2028,7 @@ function cfg_simplify!(ir::IRCode) i = popfirst!(worklist) # Drop blocks that will be merged away if merge_into[i] != 0 - bb_rename_succ[i] = -1 + bb_rename_succ[i] = typemin(Int) end # Mark dropped blocks for fixup if !isempty(searchsorted(dropped_bbs, i)) @@ -2049,7 +2048,7 @@ function cfg_simplify!(ir::IRCode) # we have to schedule that block next while merged_succ[curr] != 0 if bb_rename_succ[curr] == 0 - bb_rename_succ[curr] = -1 + bb_rename_succ[curr] = typemin(Int) end curr = merged_succ[curr] end @@ -2089,9 +2088,9 @@ function cfg_simplify!(ir::IRCode) resolved_all = true for bb in dropped_bbs obb = bb_rename_succ[bb] - if obb < -1 + if obb < 0 && obb != typemin(Int) nsucc = bb_rename_succ[-obb] - if nsucc == -1 + if nsucc == typemin(Int) nsucc = -merge_into[-obb] end bb_rename_succ[bb] = nsucc @@ -2106,6 +2105,8 @@ function cfg_simplify!(ir::IRCode) if bb_rename_succ[i] == 0 bb_rename_succ[i] = -1 bb_rename_pred[i] = -2 + elseif bb_rename_succ[i] == typemin(Int) + bb_rename_succ[i] = -1 end end @@ -2208,7 +2209,8 @@ function cfg_simplify!(ir::IRCode) ms = orig_bb bb_start = true while ms != 0 - for i in bbs[ms].stmts + old_bb_stmts = bbs[ms].stmts + for i in old_bb_stmts node = ir.stmts[i] compact.result[compact.result_idx] = node stmt = node[:stmt] @@ -2220,8 +2222,13 @@ function cfg_simplify!(ir::IRCode) values = phi.values (; ssa_rename, late_fixup, used_ssas, new_new_used_ssas) = compact ssa_rename[i] = SSAValue(compact.result_idx) - processed_idx = i - renamed_values = process_phinode_values(values, late_fixup, processed_idx, compact.result_idx, ssa_rename, used_ssas, new_new_used_ssas, true, nothing) + already_inserted = function (i::Int, val::OldSSAValue) + if val.id in old_bb_stmts + return val.id <= i + end + return bb_rename_pred[phi.edges[i]] < idx + end + renamed_values = process_phinode_values(values, late_fixup, already_inserted, compact.result_idx, ssa_rename, used_ssas, new_new_used_ssas, true, nothing) edges = Int32[] values = Any[] sizehint!(edges, length(phi.edges)); sizehint!(values, length(renamed_values)) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 588ecabe429ec2..eb43e77885d64a 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -922,48 +922,43 @@ function try_compute_fieldidx(@nospecialize(typ), @nospecialize(field)) return field end -function getfield_boundscheck((; fargs, argtypes)::ArgInfo) # Symbol - farg = nothing - if length(argtypes) == 3 +function getfield_boundscheck(argtypes::Vector{Any}) + if length(argtypes) == 2 + isvarargtype(argtypes[2]) && return :unsafe return :on - elseif length(argtypes) == 4 - fargs !== nothing && (farg = fargs[4]) - boundscheck = argtypes[4] + elseif length(argtypes) == 3 + boundscheck = argtypes[3] isvarargtype(boundscheck) && return :unsafe if widenconst(boundscheck) === Symbol return :on end - elseif length(argtypes) == 5 - fargs !== nothing && (farg = fargs[5]) - boundscheck = argtypes[5] + elseif length(argtypes) == 4 + boundscheck = argtypes[4] + isvarargtype(boundscheck) && return :unsafe else return :unsafe end - isvarargtype(boundscheck) && return :unsafe boundscheck = widenconditional(boundscheck) if widenconst(boundscheck) === Bool if isa(boundscheck, Const) return boundscheck.val::Bool ? :on : :off - elseif farg !== nothing && isexpr(farg, :boundscheck) - return :boundscheck end - return :unknown + return :unknown # including a case when specified as `:boundscheck` end return :unsafe end -function getfield_nothrow(𝕃::AbstractLattice, arginfo::ArgInfo, boundscheck::Symbol=getfield_boundscheck(arginfo)) +function getfield_nothrow(𝕃::AbstractLattice, argtypes::Vector{Any}, boundscheck::Symbol=getfield_boundscheck(argtypes)) boundscheck === :unsafe && return false - (;argtypes) = arginfo ordering = Const(:not_atomic) - if length(argtypes) == 4 - isvarargtype(argtypes[4]) && return false - if widenconst(argtypes[4]) !== Bool - ordering = argtypes[4] - end - elseif length(argtypes) == 5 - ordering = argtypes[4] - elseif length(argtypes) != 3 + if length(argtypes) == 3 + isvarargtype(argtypes[3]) && return false + if widenconst(argtypes[3]) !== Bool + ordering = argtypes[3] + end + elseif length(argtypes) == 4 + ordering = argtypes[3] + elseif length(argtypes) ≠ 2 return false end isa(ordering, Const) || return false @@ -972,7 +967,7 @@ function getfield_nothrow(𝕃::AbstractLattice, arginfo::ArgInfo, boundscheck:: if ordering !== :not_atomic # TODO: this is assuming not atomic return false end - return getfield_nothrow(𝕃, argtypes[2], argtypes[3], !(boundscheck === :off)) + return getfield_nothrow(𝕃, argtypes[1], argtypes[2], !(boundscheck === :off)) end @nospecs function getfield_nothrow(𝕃::AbstractLattice, s00, name, boundscheck::Bool) # If we don't have boundscheck off and don't know the field, don't even bother @@ -1998,7 +1993,7 @@ end add_tfunc(memoryref_isassigned, 3, 3, memoryref_isassigned_tfunc, 20) @nospecs function memoryref_tfunc(𝕃::AbstractLattice, mem) - a = widenconst(mem) + a = widenconst(unwrapva(mem)) if !has_free_typevars(a) unw = unwrap_unionall(a) if isa(unw, DataType) && unw.name === GenericMemory.body.body.body.name @@ -2012,7 +2007,10 @@ add_tfunc(memoryref_isassigned, 3, 3, memoryref_isassigned_tfunc, 20) return GenericMemoryRef end @nospecs function memoryref_tfunc(𝕃::AbstractLattice, ref, idx) - memoryref_tfunc(𝕃, ref, idx, Const(true)) + if isvarargtype(idx) + idx = unwrapva(idx) + end + return memoryref_tfunc(𝕃, ref, idx, Const(true)) end @nospecs function memoryref_tfunc(𝕃::AbstractLattice, ref, idx, boundscheck) memoryref_builtin_common_errorcheck(ref, Const(:not_atomic), boundscheck) || return Bottom @@ -2027,12 +2025,10 @@ add_tfunc(memoryref, 1, 3, memoryref_tfunc, 1) end add_tfunc(memoryrefoffset, 1, 1, memoryrefoffset_tfunc, 5) - - @nospecs function memoryref_builtin_common_errorcheck(mem, order, boundscheck) hasintersect(widenconst(mem), GenericMemoryRef) || return false hasintersect(widenconst(order), Symbol) || return false - hasintersect(widenconst(boundscheck), Bool) || return false + hasintersect(widenconst(unwrapva(boundscheck)), Bool) || return false return true end @@ -2149,7 +2145,6 @@ end return boundscheck ⊑ Bool && memtype ⊑ GenericMemoryRef && order ⊑ Symbol end - # Query whether the given builtin is guaranteed not to throw given the argtypes @nospecs function _builtin_nothrow(𝕃::AbstractLattice, f, argtypes::Vector{Any}, rt) ⊑ = Core.Compiler.:⊑(𝕃) @@ -2180,7 +2175,7 @@ end elseif f === invoke return false elseif f === getfield - return getfield_nothrow(𝕃, ArgInfo(nothing, Any[Const(f), argtypes...])) + return getfield_nothrow(𝕃, argtypes) elseif f === setfield! if na == 3 return setfield!_nothrow(𝕃, argtypes[1], argtypes[2], argtypes[3]) @@ -2384,10 +2379,9 @@ function isdefined_effects(𝕃::AbstractLattice, argtypes::Vector{Any}) return Effects(EFFECTS_TOTAL; consistent, nothrow, inaccessiblememonly) end -function getfield_effects(𝕃::AbstractLattice, arginfo::ArgInfo, @nospecialize(rt)) - (;argtypes) = arginfo - length(argtypes) < 3 && return EFFECTS_THROWS - obj = argtypes[2] +function getfield_effects(𝕃::AbstractLattice, argtypes::Vector{Any}, @nospecialize(rt)) + length(argtypes) < 2 && return EFFECTS_THROWS + obj = argtypes[1] if isvarargtype(obj) return Effects(EFFECTS_TOTAL; consistent=CONSISTENT_IF_INACCESSIBLEMEMONLY, @@ -2405,12 +2399,12 @@ function getfield_effects(𝕃::AbstractLattice, arginfo::ArgInfo, @nospecialize # with undefined value to avoid tainting `:consistent` too aggressively # TODO this should probably taint `:noub`, however, it would hinder concrete eval for # `REPLInterpreter` that can ignore `:consistent-cy`, causing worse completions - if !(length(argtypes) ≥ 3 && getfield_notundefined(obj, argtypes[3])) + if !(length(argtypes) ≥ 2 && getfield_notundefined(obj, argtypes[2])) consistent = ALWAYS_FALSE end noub = ALWAYS_TRUE - bcheck = getfield_boundscheck(arginfo) - nothrow = getfield_nothrow(𝕃, arginfo, bcheck) + bcheck = getfield_boundscheck(argtypes) + nothrow = getfield_nothrow(𝕃, argtypes, bcheck) if !nothrow if bcheck !== :on # If we cannot independently prove inboundsness, taint `:noub`. @@ -2425,7 +2419,7 @@ function getfield_effects(𝕃::AbstractLattice, arginfo::ArgInfo, @nospecialize end end if hasintersect(widenconst(obj), Module) - inaccessiblememonly = getglobal_effects(argtypes[2:end], rt).inaccessiblememonly + inaccessiblememonly = getglobal_effects(argtypes, rt).inaccessiblememonly elseif is_mutation_free_argtype(obj) inaccessiblememonly = ALWAYS_TRUE else @@ -2453,15 +2447,20 @@ function getglobal_effects(argtypes::Vector{Any}, @nospecialize(rt)) return Effects(EFFECTS_TOTAL; consistent, nothrow, inaccessiblememonly) end -function builtin_effects(𝕃::AbstractLattice, @nospecialize(f::Builtin), arginfo::ArgInfo, @nospecialize(rt)) +""" + builtin_effects(𝕃::AbstractLattice, f::Builtin, argtypes::Vector{Any}, rt) -> Effects + +Compute the effects of a builtin function call. `argtypes` should not include `f` itself. +""" +function builtin_effects(𝕃::AbstractLattice, @nospecialize(f::Builtin), argtypes::Vector{Any}, @nospecialize(rt)) if isa(f, IntrinsicFunction) - return intrinsic_effects(f, arginfo.argtypes[2:end]) + return intrinsic_effects(f, argtypes) end @assert !contains_is(_SPECIAL_BUILTINS, f) if f === getfield - return getfield_effects(𝕃, arginfo, rt) + return getfield_effects(𝕃, argtypes, rt) end # if this builtin call deterministically throws, @@ -2469,7 +2468,6 @@ function builtin_effects(𝕃::AbstractLattice, @nospecialize(f::Builtin), argin # note this is safe only if we accounted for :noub already rt === Bottom && return EFFECTS_THROWS - argtypes = arginfo.argtypes[2:end] if f === isdefined return isdefined_effects(𝕃, argtypes) elseif f === getglobal @@ -2511,10 +2509,50 @@ function builtin_effects(𝕃::AbstractLattice, @nospecialize(f::Builtin), argin else inaccessiblememonly = ALWAYS_FALSE end - return Effects(EFFECTS_TOTAL; consistent, effect_free, nothrow, inaccessiblememonly) + if f === memoryref || f === memoryrefget || f === memoryrefset! || f === memoryref_isassigned + noub = memoryop_noub(f, argtypes) ? ALWAYS_TRUE : ALWAYS_FALSE + else + noub = ALWAYS_TRUE + end + return Effects(EFFECTS_TOTAL; consistent, effect_free, nothrow, inaccessiblememonly, noub) end end +function memoryop_noub(@nospecialize(f), argtypes::Vector{Any}) + nargs = length(argtypes) + nargs == 0 && return true # must throw and noub + lastargtype = argtypes[end] + isva = isvarargtype(lastargtype) + if f === memoryref + if nargs == 1 && !isva + return true + elseif nargs == 2 && !isva + return true + end + expected_nargs = 3 + elseif f === memoryrefget || f === memoryref_isassigned + expected_nargs = 3 + else + @assert f === memoryrefset! "unexpected memoryop is given" + expected_nargs = 4 + end + if nargs == expected_nargs && !isva + boundscheck = widenconditional(lastargtype) + hasintersect(widenconst(boundscheck), Bool) || return true # must throw and noub + boundscheck isa Const && boundscheck.val === true && return true + elseif nargs > expected_nargs + 1 + return true # must throw and noub + elseif !isva + return true # must throw and noub + end + return false +end + +""" + builtin_nothrow(𝕃::AbstractLattice, f::Builtin, argtypes::Vector{Any}, rt) -> Bool + +Compute throw-ness of a builtin function call. `argtypes` should not include `f` itself. +""" function builtin_nothrow(𝕃::AbstractLattice, @nospecialize(f), argtypes::Vector{Any}, @nospecialize(rt)) rt === Bottom && return false contains_is(_PURE_BUILTINS, f) && return true diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 29d3c01d151be9..c1230980c42a65 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -243,7 +243,6 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState) end function _typeinf(interp::AbstractInterpreter, frame::InferenceState) - interp = switch_from_irinterp(interp) 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 @@ -331,7 +330,7 @@ function CodeInstance(interp::AbstractInterpreter, result::InferenceResult, widenconst(result_type), rettype_const, inferred_result, const_flags, first(valid_worlds), last(valid_worlds), # TODO: Actually do something with non-IPO effects - encode_effects(result.ipo_effects), encode_effects(result.ipo_effects), result.argescapes, + encode_effects(result.ipo_effects), encode_effects(result.ipo_effects), result.analysis_results, relocatability) end @@ -446,6 +445,8 @@ function adjust_effects(ipo_effects::Effects, def::Method) end if is_effect_overridden(override, :noub) ipo_effects = Effects(ipo_effects; noub=ALWAYS_TRUE) + elseif is_effect_overridden(override, :noub_if_noinbounds) && ipo_effects.noub !== ALWAYS_TRUE + ipo_effects = Effects(ipo_effects; noub=NOUB_IF_NOINBOUNDS) end return ipo_effects end diff --git a/base/compiler/types.jl b/base/compiler/types.jl index db1d85cdddc40f..41fde10dfedf45 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -59,6 +59,16 @@ end abstract type ForwardableArgtypes end +struct AnalysisResults + result + next::AnalysisResults + AnalysisResults(@nospecialize(result), next::AnalysisResults) = new(result, next) + AnalysisResults(@nospecialize(result)) = new(result) + # NullAnalysisResults() = new(nothing) + # global const NULL_ANALYSIS_RESULTS = NullAnalysisResults() +end +const NULL_ANALYSIS_RESULTS = AnalysisResults(nothing) + """ InferenceResult(linfo::MethodInstance, [argtypes::ForwardableArgtypes, 𝕃::AbstractLattice]) @@ -75,14 +85,14 @@ mutable struct InferenceResult valid_worlds::WorldRange # if inference and optimization is finished ipo_effects::Effects # if inference is finished effects::Effects # if optimization is finished - argescapes # ::ArgEscapeCache if optimized, nothing otherwise + analysis_results::AnalysisResults # AnalysisResults with e.g. result::ArgEscapeCache if optimized, otherwise NULL_ANALYSIS_RESULTS is_src_volatile::Bool # `src` has been cached globally as the compressed format already, allowing `src` to be used destructively function InferenceResult(linfo::MethodInstance, cache_argtypes::Vector{Any}, overridden_by_const::BitVector) # def = linfo.def # nargs = def isa Method ? Int(def.nargs) : 0 # @assert length(cache_argtypes) == nargs return new(linfo, cache_argtypes, overridden_by_const, nothing, nothing, - WorldRange(), Effects(), Effects(), nothing, false) + WorldRange(), Effects(), Effects(), NULL_ANALYSIS_RESULTS, false) end end InferenceResult(linfo::MethodInstance, 𝕃::AbstractLattice=fallback_lattice) = @@ -90,6 +100,21 @@ InferenceResult(linfo::MethodInstance, 𝕃::AbstractLattice=fallback_lattice) = InferenceResult(linfo::MethodInstance, argtypes::ForwardableArgtypes, 𝕃::AbstractLattice=fallback_lattice) = InferenceResult(linfo, matching_cache_argtypes(𝕃, linfo, argtypes)...) +function stack_analysis_result!(inf_result::InferenceResult, @nospecialize(result)) + return inf_result.analysis_results = AnalysisResults(result, inf_result.analysis_results) +end + +function traverse_analysis_results(callback, (;analysis_results)::Union{InferenceResult,CodeInstance}) + analysis_results isa AnalysisResults || return nothing + while isdefined(analysis_results, :next) + if (result = callback(analysis_results.result)) !== nothing + return result + end + analysis_results = analysis_results.next + end + return nothing +end + """ inf_params::InferenceParams @@ -342,9 +367,6 @@ struct NativeInterpreter <: AbstractInterpreter # Parameters for inference and optimization inf_params::InferenceParams opt_params::OptimizationParams - - # a boolean flag to indicate if this interpreter is performing semi concrete interpretation - irinterp::Bool end function NativeInterpreter(world::UInt = get_world_counter(); @@ -364,7 +386,7 @@ function NativeInterpreter(world::UInt = get_world_counter(); inf_cache = Vector{InferenceResult}() # Initially empty cache - return NativeInterpreter(world, method_table, inf_cache, inf_params, opt_params, #=irinterp=#false) + return NativeInterpreter(world, method_table, inf_cache, inf_params, opt_params) end function NativeInterpreter(interp::NativeInterpreter; @@ -372,9 +394,8 @@ function NativeInterpreter(interp::NativeInterpreter; method_table::CachedMethodTable{InternalMethodTable} = interp.method_table, inf_cache::Vector{InferenceResult} = interp.inf_cache, inf_params::InferenceParams = interp.inf_params, - opt_params::OptimizationParams = interp.opt_params, - irinterp::Bool = interp.irinterp) - return NativeInterpreter(world, method_table, inf_cache, inf_params, opt_params, irinterp) + opt_params::OptimizationParams = interp.opt_params) + return NativeInterpreter(world, method_table, inf_cache, inf_params, opt_params) end # Quickly and easily satisfy the AbstractInterpreter API contract @@ -468,34 +489,6 @@ typeinf_lattice(::AbstractInterpreter) = InferenceLattice(BaseInferenceLattice.i ipo_lattice(::AbstractInterpreter) = InferenceLattice(IPOResultLattice.instance) optimizer_lattice(::AbstractInterpreter) = SimpleInferenceLattice.instance -typeinf_lattice(interp::NativeInterpreter) = interp.irinterp ? - InferenceLattice(SimpleInferenceLattice.instance) : - InferenceLattice(BaseInferenceLattice.instance) -ipo_lattice(interp::NativeInterpreter) = interp.irinterp ? - InferenceLattice(SimpleInferenceLattice.instance) : - InferenceLattice(IPOResultLattice.instance) -optimizer_lattice(interp::NativeInterpreter) = SimpleInferenceLattice.instance - -""" - switch_to_irinterp(interp::AbstractInterpreter) -> irinterp::AbstractInterpreter - -This interface allows `ir_abstract_constant_propagation` to convert `interp` to a new -`irinterp::AbstractInterpreter` to perform semi-concrete interpretation. -`NativeInterpreter` uses this interface to switch its lattice to `optimizer_lattice` during -semi-concrete interpretation on `IRCode`. -""" -switch_to_irinterp(interp::AbstractInterpreter) = interp -switch_to_irinterp(interp::NativeInterpreter) = NativeInterpreter(interp; irinterp=true) - -""" - switch_from_irinterp(irinterp::AbstractInterpreter) -> interp::AbstractInterpreter - -The inverse operation of `switch_to_irinterp`, allowing `typeinf` to convert `irinterp` back -to a new `interp::AbstractInterpreter` to perform ordinary abstract interpretation. -""" -switch_from_irinterp(irinterp::AbstractInterpreter) = irinterp -switch_from_irinterp(irinterp::NativeInterpreter) = NativeInterpreter(irinterp; irinterp=false) - abstract type CallInfo end @nospecialize diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index f46f9f19ee629d..3d1fe354530605 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -446,9 +446,12 @@ function find_ssavalue_uses(e::PhiNode, uses::Vector{BitSet}, line::Int) end end -function is_throw_call(e::Expr) +function is_throw_call(e::Expr, code::Vector{Any}) if e.head === :call f = e.args[1] + if isa(f, SSAValue) + f = code[f.id] + end if isa(f, GlobalRef) ff = abstract_eval_globalref_type(f) if isa(ff, Const) && ff.val === Core.throw @@ -478,7 +481,7 @@ function find_throw_blocks(code::Vector{Any}, handler_at::Vector{Int}) end elseif s.head === :return # see `ReturnNode` handling - elseif is_throw_call(s) + elseif is_throw_call(s, code) if handler_at[i] == 0 push!(stmts, i) end diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl index 5505f5a2b423ba..ef6602b0827970 100644 --- a/base/compiler/validation.jl +++ b/base/compiler/validation.jl @@ -253,5 +253,3 @@ function is_valid_rvalue(@nospecialize(x)) end is_valid_return(@nospecialize(x)) = is_valid_argument(x) || (isa(x, Expr) && x.head === :lambda) - -is_flag_set(byte::UInt8, flag::UInt8) = (byte & flag) == flag diff --git a/base/docs/intrinsicsdocs.jl b/base/docs/intrinsicsdocs.jl index effc9e859becf0..9f6ec773ff9a87 100644 --- a/base/docs/intrinsicsdocs.jl +++ b/base/docs/intrinsicsdocs.jl @@ -3,14 +3,14 @@ """ Core.IR -The Core.IR module exports the IR object model. +The `Core.IR` module exports the IR object model. """ Core.IR """ Core.IntrinsicFunction <: Core.Builtin <: Function -The Core.IntrinsicFunction function define some basic primitives for what defines the +The `Core.IntrinsicFunction` function define some basic primitives for what defines the abilities and behaviors of a Julia program """ Core.IntrinsicFunction @@ -18,7 +18,7 @@ Core.IntrinsicFunction """ Core.Intrinsics -The Core.Intrinsics module holds the Core.IntrinsicFunction objects. +The `Core.Intrinsics` module holds the `Core.IntrinsicFunction` objects. """ Core.Intrinsics @@ -26,21 +26,21 @@ Core.Intrinsics Core.memoryref(::GenericMemory) Core.memoryref(::GenericMemoryRef, index::Int, [boundscheck::Bool]) -Return a GenericMemoryRef for a GenericMemory. See [`MemoryRef`](@ref). +Return a `GenericMemoryRef` for a `GenericMemory`. See [`MemoryRef`](@ref). """ Core.memoryref """ Core..memoryrefoffset(::GenericMemoryRef) -Return the offset index that was used to construct the MemoryRef. See [`Core.memoryref`](@ref). +Return the offset index that was used to construct the `MemoryRef`. See [`Core.memoryref`](@ref). """ Core.memoryrefoffset """ Core.memoryrefget(::GenericMemoryRef, ordering::Symbol, boundscheck::Bool) -Return the value stored at the MemoryRef, throwing a BoundsError if the Memory is empty. See `ref[]`. +Return the value stored at the `MemoryRef`, throwing a `BoundsError` if the `Memory` is empty. See `ref[]`. The memory ordering specified must be compatible with the `isatomic` parameter. """ Core.memoryrefget @@ -48,7 +48,7 @@ Core.memoryrefget """ Core.memoryrefset!(::GenericMemoryRef, value, ordering::Symbol, boundscheck::Bool) -Store the value to the MemoryRef, throwing a BoundsError if the Memory is empty. See `ref[] = value`. +Store the value to the `MemoryRef`, throwing a `BoundsError` if the `Memory` is empty. See `ref[] = value`. The memory ordering specified must be compatible with the `isatomic` parameter. """ Core.memoryrefset! @@ -56,7 +56,8 @@ Core.memoryrefset! """ Core.memoryref_isassigned(::GenericMemoryRef, ordering::Symbol, boundscheck::Bool) -Return whether there is a value stored at the MemoryRef, returning false if the Memory is empty. See [`isassigned(::Base.RefValue)`](@ref), [`Core.memoryrefget`](@ref). +Return whether there is a value stored at the `MemoryRef`, returning false if the `Memory` +is empty. See [`isassigned(::Base.RefValue)`](@ref), [`Core.memoryrefget`](@ref). The memory ordering specified must be compatible with the `isatomic` parameter. """ Core.memoryref_isassigned diff --git a/base/essentials.jl b/base/essentials.jl index a8a3ec3c153824..106826d140c574 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -13,9 +13,6 @@ length(a::Array) = getfield(getfield(getfield(a, :ref), :mem), :length) length(a::GenericMemory) = getfield(a, :length) throw_boundserror(A, I) = (@noinline; throw(BoundsError(A, I))) -getindex(A::GenericMemory{:not_atomic}, i::Int) = memoryrefget(memoryref(memoryref(A), i, @_boundscheck), :not_atomic, false) -getindex(A::GenericMemoryRef{:not_atomic}) = memoryrefget(A, :not_atomic, @_boundscheck) - # multidimensional getindex will be defined later on ==(a::GlobalRef, b::GlobalRef) = a.mod === b.mod && a.name === b.name @@ -214,7 +211,8 @@ macro _total_meta() #=:terminates_locally=#false, #=:notaskstate=#true, #=:inaccessiblememonly=#true, - #=:noub=#true)) + #=:noub=#true, + #=:noub_if_noinbounds=#false)) end # can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping) macro _foldable_meta() @@ -226,19 +224,8 @@ macro _foldable_meta() #=:terminates_locally=#false, #=:notaskstate=#true, #=:inaccessiblememonly=#true, - #=:noub=#true)) -end -# can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping) -macro _nothrow_meta() - return _is_internal(__module__) && Expr(:meta, Expr(:purity, - #=:consistent=#false, - #=:effect_free=#false, - #=:nothrow=#true, - #=:terminates_globally=#false, - #=:terminates_locally=#false, - #=:notaskstate=#false, - #=:inaccessiblememonly=#false, - #=:noub=#false)) + #=:noub=#true, + #=:noub_if_noinbounds=#false)) end # can be used in place of `@assume_effects :terminates_locally` (supposed to be used for bootstrapping) macro _terminates_locally_meta() @@ -250,7 +237,8 @@ macro _terminates_locally_meta() #=:terminates_locally=#true, #=:notaskstate=#false, #=:inaccessiblememonly=#false, - #=:noub=#false)) + #=:noub=#false, + #=:noub_if_noinbounds=#false)) end # can be used in place of `@assume_effects :terminates_globally` (supposed to be used for bootstrapping) macro _terminates_globally_meta() @@ -262,7 +250,8 @@ macro _terminates_globally_meta() #=:terminates_locally=#true, #=:notaskstate=#false, #=:inaccessiblememonly=#false, - #=:noub=#false)) + #=:noub=#false, + #=:noub_if_noinbounds=#false)) end # can be used in place of `@assume_effects :effect_free :terminates_locally` (supposed to be used for bootstrapping) macro _effect_free_terminates_locally_meta() @@ -274,7 +263,34 @@ macro _effect_free_terminates_locally_meta() #=:terminates_locally=#true, #=:notaskstate=#false, #=:inaccessiblememonly=#false, - #=:noub=#false)) + #=:noub=#false, + #=:noub_if_noinbounds=#false)) +end +# can be used in place of `@assume_effects :nothrow :noub` (supposed to be used for bootstrapping) +macro _nothrow_noub_meta() + return _is_internal(__module__) && Expr(:meta, Expr(:purity, + #=:consistent=#false, + #=:effect_free=#false, + #=:nothrow=#true, + #=:terminates_globally=#false, + #=:terminates_locally=#false, + #=:notaskstate=#false, + #=:inaccessiblememonly=#false, + #=:noub=#true, + #=:noub_if_noinbounds=#false)) +end +# can be used in place of `@assume_effects :noub_if_noinbounds` (supposed to be used for bootstrapping) +macro _noub_if_noinbounds_meta() + return _is_internal(__module__) && Expr(:meta, Expr(:purity, + #=:consistent=#false, + #=:effect_free=#false, + #=:nothrow=#false, + #=:terminates_globally=#false, + #=:terminates_locally=#false, + #=:notaskstate=#false, + #=:inaccessiblememonly=#false, + #=:noub=#false, + #=:noub_if_noinbounds=#true)) end # another version of inlining that propagates an inbounds context @@ -285,6 +301,10 @@ macro _nospecializeinfer_meta() return Expr(:meta, :nospecializeinfer) end +getindex(A::GenericMemory{:not_atomic}, i::Int) = (@_noub_if_noinbounds_meta; + memoryrefget(memoryref(memoryref(A), i, @_boundscheck), :not_atomic, false)) +getindex(A::GenericMemoryRef{:not_atomic}) = memoryrefget(A, :not_atomic, @_boundscheck) + function iterate end """ @@ -787,11 +807,13 @@ end # linear indexing function getindex(A::Array, i::Int) + @_noub_if_noinbounds_meta @boundscheck ult_int(bitcast(UInt, sub_int(i, 1)), bitcast(UInt, length(A))) || throw_boundserror(A, (i,)) memoryrefget(memoryref(getfield(A, :ref), i, false), :not_atomic, false) end # simple Array{Any} operations needed for bootstrap function setindex!(A::Array{Any}, @nospecialize(x), i::Int) + @_noub_if_noinbounds_meta @boundscheck ult_int(bitcast(UInt, sub_int(i, 1)), bitcast(UInt, length(A))) || throw_boundserror(A, (i,)) memoryrefset!(memoryref(getfield(A, :ref), i, false), x, :not_atomic, false) return A diff --git a/base/expr.jl b/base/expr.jl index 4e418d140e5a65..3e68c68b2ac4f7 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -477,6 +477,7 @@ The following `setting`s are supported. - `:notaskstate` - `:inaccessiblememonly` - `:noub` +- `:noub_if_noinbounds` - `:foldable` - `:removable` - `:total` @@ -714,8 +715,8 @@ macro assume_effects(args...) ex = nothing idx = length(args) end - (consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly, noub) = - (false, false, false, false, false, false, false, false, false) + consistent = effect_free = nothrow = terminates_globally = terminates_locally = + notaskstate = inaccessiblememonly = noub = noub_if_noinbounds = false for i in 1:idx org_setting = args[i] (setting, val) = compute_assumed_setting(org_setting) @@ -735,6 +736,8 @@ macro assume_effects(args...) inaccessiblememonly = val elseif setting === :noub noub = val + elseif setting === :noub_if_noinbounds + noub_if_noinbounds = val elseif setting === :foldable consistent = effect_free = terminates_globally = noub = val elseif setting === :removable @@ -747,15 +750,18 @@ macro assume_effects(args...) end if is_function_def(inner) return esc(pushmeta!(ex, :purity, - consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly, noub)) + consistent, effect_free, nothrow, terminates_globally, terminates_locally, + notaskstate, inaccessiblememonly, noub, noub_if_noinbounds)) elseif isexpr(ex, :macrocall) && ex.args[1] === Symbol("@ccall") ex.args[1] = GlobalRef(Base, Symbol("@ccall_effects")) insert!(ex.args, 3, Core.Compiler.encode_effects_override(Core.Compiler.EffectsOverride( - consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly, noub))) + consistent, effect_free, nothrow, terminates_globally, terminates_locally, + notaskstate, inaccessiblememonly, noub, noub_if_noinbounds))) return esc(ex) else # anonymous function case return Expr(:meta, Expr(:purity, - consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly, noub)) + consistent, effect_free, nothrow, terminates_globally, terminates_locally, + notaskstate, inaccessiblememonly, noub, noub_if_noinbounds)) end end diff --git a/base/file.jl b/base/file.jl index 9060b086b44bb8..e63ed67ae249bc 100644 --- a/base/file.jl +++ b/base/file.jl @@ -521,37 +521,70 @@ const TEMP_CLEANUP = Dict{String,Bool}() const TEMP_CLEANUP_LOCK = ReentrantLock() function temp_cleanup_later(path::AbstractString; asap::Bool=false) - lock(TEMP_CLEANUP_LOCK) + @lock TEMP_CLEANUP_LOCK begin # each path should only be inserted here once, but if there # is a collision, let !asap win over asap: if any user might # still be using the path, don't delete it until process exit TEMP_CLEANUP[path] = get(TEMP_CLEANUP, path, true) & asap if length(TEMP_CLEANUP) > TEMP_CLEANUP_MAX[] - temp_cleanup_purge() + temp_cleanup_purge_prelocked(false) TEMP_CLEANUP_MAX[] = max(TEMP_CLEANUP_MIN[], 2*length(TEMP_CLEANUP)) end - unlock(TEMP_CLEANUP_LOCK) - return nothing + end + nothing end -function temp_cleanup_purge(; force::Bool=false) - need_gc = Sys.iswindows() - for (path, asap) in TEMP_CLEANUP +function temp_cleanup_forget(path::AbstractString) + @lock TEMP_CLEANUP_LOCK delete!(TEMP_CLEANUP, path) + nothing +end + +function temp_cleanup_purge_prelocked(force::Bool) + filter!(TEMP_CLEANUP) do (path, asap) try - if (force || asap) && ispath(path) - need_gc && GC.gc(true) - need_gc = false + ispath(path) || return false + if force || asap prepare_for_deletion(path) rm(path, recursive=true, force=true) end - !ispath(path) && delete!(TEMP_CLEANUP, path) + return ispath(path) catch ex @warn """ Failed to clean up temporary path $(repr(path)) $ex """ _group=:file + ex isa InterruptException && rethrow() + return true + end + end + nothing +end + +function temp_cleanup_purge_all() + may_need_gc = false + @lock TEMP_CLEANUP_LOCK filter!(TEMP_CLEANUP) do (path, asap) + try + ispath(path) || return false + may_need_gc = true + return true + catch ex + ex isa InterruptException && rethrow() + return true end end + if may_need_gc + # this is only usually required on Sys.iswindows(), but may as well do it everywhere + GC.gc(true) + end + @lock TEMP_CLEANUP_LOCK temp_cleanup_purge_prelocked(true) + nothing +end + +# deprecated internal function used by some packages +temp_cleanup_purge(; force=false) = force ? temp_cleanup_purge_all() : @lock TEMP_CLEANUP_LOCK temp_cleanup_purge_prelocked(false) + +function __postinit__() + Base.atexit(temp_cleanup_purge_all) end const temp_prefix = "jl_" @@ -733,10 +766,11 @@ temporary file upon completion. See also: [`mktempdir`](@ref). """ function mktemp(fn::Function, parent::AbstractString=tempdir()) - (tmp_path, tmp_io) = mktemp(parent, cleanup=false) + (tmp_path, tmp_io) = mktemp(parent) try fn(tmp_path, tmp_io) finally + temp_cleanup_forget(tmp_path) try close(tmp_io) ispath(tmp_path) && rm(tmp_path) @@ -761,10 +795,11 @@ See also: [`mktemp`](@ref), [`mkdir`](@ref). """ function mktempdir(fn::Function, parent::AbstractString=tempdir(); prefix::AbstractString=temp_prefix) - tmpdir = mktempdir(parent; prefix=prefix, cleanup=false) + tmpdir = mktempdir(parent; prefix=prefix) try fn(tmpdir) finally + temp_cleanup_forget(tmpdir) try if ispath(tmpdir) prepare_for_deletion(tmpdir) diff --git a/base/genericmemory.jl b/base/genericmemory.jl index 34c452af97ad20..51b25d453e5c45 100644 --- a/base/genericmemory.jl +++ b/base/genericmemory.jl @@ -196,17 +196,6 @@ function setindex!(A::Memory{T}, x, i1::Int, i2::Int, I::Int...) where {T} setindex!(A, x, i1) end -function __inbounds_setindex!(A::Memory{T}, x, i1::Int) where {T} - val = x isa T ? x : convert(T,x)::T - ref = memoryref(memoryref(A), i1, false) - memoryrefset!(ref, val, :not_atomic, false) - return A -end -function __inbounds_setindex!(A::Memory{T}, x, i1::Int, i2::Int, I::Int...) where {T} - @boundscheck (i2 == 1 && all(==(1), I)) || throw_boundserror(A, (i1, i2, I...)) - __inbounds_setindex(A, x, i1) -end - # Faster contiguous setindex! with copyto! function setindex!(A::Memory{T}, X::Memory{T}, I::AbstractUnitRange{Int}) where T @inline diff --git a/base/initdefs.jl b/base/initdefs.jl index debbb45428e993..59b2f59d7b263f 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -367,9 +367,7 @@ end ## atexit: register exit hooks ## -const atexit_hooks = Callable[ - () -> Filesystem.temp_cleanup_purge(force=true) -] +const atexit_hooks = Callable[] const _atexit_hooks_lock = ReentrantLock() global _atexit_hooks_finished::Bool = false @@ -413,7 +411,7 @@ function _atexit(exitcode::Cint) # will immediately run here. while true local f - Base.@lock _atexit_hooks_lock begin + @lock _atexit_hooks_lock begin # If this is the last iteration, atomically disable atexit hooks to prevent # someone from registering a hook that will never be run. # (We do this inside the loop, so that it is atomic: no one can have registered @@ -434,7 +432,7 @@ function _atexit(exitcode::Cint) end catch ex showerror(stderr, ex) - Base.show_backtrace(stderr, catch_backtrace()) + show_backtrace(stderr, catch_backtrace()) println(stderr) end end @@ -454,7 +452,7 @@ function _postoutput() f() catch ex showerror(stderr, ex) - Base.show_backtrace(stderr, catch_backtrace()) + show_backtrace(stderr, catch_backtrace()) println(stderr) end end diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 558d55c118b349..ea35da0910736e 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -218,7 +218,7 @@ gcdx(a::T, b::T) where T<:Real = throw(MethodError(gcdx, (a,b))) # multiplicative inverse of n mod m, error if none """ - invmod(n, m) + invmod(n::Integer, m::Integer) Take the inverse of `n` modulo `m`: `y` such that ``n y = 1 \\pmod m``, and ``div(y,m) = 0``. This will throw an error if ``m = 0``, or if @@ -257,6 +257,43 @@ function invmod(n::Integer, m::Integer) return mod(x, m) end +""" + invmod(n::Integer, T) where {T <: Base.BitInteger} + 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 +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`. + +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 +type. + +The modular inverse is computed much more efficiently than the general case +using the algorithm described in https://arxiv.org/pdf/2204.04342.pdf. + +!!! compat "Julia 1.11" + The `invmod(n)` and `invmod(n, T)` methods require Julia 1.11 or later. +""" +invmod(n::Integer, ::Type{T}) where {T<:BitInteger} = invmod(n % T) + +function invmod(n::T) where {T<:BitInteger} + isodd(n) || throw(DomainError(n, "Argument must be odd.")) + x = (3*n ⊻ 2) % T + y = (1 - n*x) % T + for _ = 1:trailing_zeros(2*sizeof(T)) + x *= y + true + y *= y + end + return x +end + # ^ for any x supporting * to_power_type(x) = convert(Base._return_type(*, Tuple{typeof(x), typeof(x)}), x) @noinline throw_domerr_powbysq(::Any, p) = throw(DomainError(p, LazyString( diff --git a/base/loading.jl b/base/loading.jl index 125e9b1302fb58..ff1a2207a2022e 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1044,7 +1044,21 @@ const TIMING_IMPORTS = Threads.Atomic{Int}(0) # these return either the array of modules loaded from the path / content given # or an Exception that describes why it couldn't be loaded # and it reconnects the Base.Docs.META -function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{Nothing, String}, depmods::Vector{Any}) +function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{Nothing, String}, depmods::Vector{Any}, ignore_native::Union{Nothing,Bool}=nothing) + if isnothing(ignore_native) + if JLOptions().code_coverage == 0 && JLOptions().malloc_log == 0 + ignore_native = false + else + io = open(path, "r") + try + iszero(isvalid_cache_header(io)) && return ArgumentError("Invalid header in cache file $path.") + _, (includes, _, _), _, _, _, _, _, _ = parse_cache_header(io, path) + ignore_native = pkg_tracked(includes) + finally + close(io) + end + end + end assert_havelock(require_lock) timing_imports = TIMING_IMPORTS[] > 0 try @@ -1056,7 +1070,7 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No if ocachepath !== nothing @debug "Loading object cache file $ocachepath for $pkg" - sv = ccall(:jl_restore_package_image_from_file, Any, (Cstring, Any, Cint, Cstring), ocachepath, depmods, false, pkg.name) + sv = ccall(:jl_restore_package_image_from_file, Any, (Cstring, Any, Cint, Cstring, Cint), ocachepath, depmods, false, pkg.name, ignore_native) else @debug "Loading cache file $path for $pkg" sv = ccall(:jl_restore_incremental, Any, (Cstring, Any, Cint, Cstring), path, depmods, false, pkg.name) @@ -1264,7 +1278,7 @@ function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any} for (ext, triggers) in extensions triggers = triggers::Union{String, Vector{String}} triggers isa String && (triggers = [triggers]) - id = PkgId(uuid5(parent.uuid, ext), ext) + id = PkgId(uuid5(parent.uuid::UUID, ext), ext) if id in keys(EXT_PRIMED) || haskey(Base.loaded_modules, id) continue # extension is already primed or loaded, don't add it again end @@ -1495,15 +1509,45 @@ function _tryrequire_from_serialized(modkey::PkgId, path::String, ocachepath::Un return loaded end +# returns whether the package is tracked in coverage or malloc tracking based on +# JLOptions and includes +function pkg_tracked(includes) + if JLOptions().code_coverage == 0 && JLOptions().malloc_log == 0 + return false + elseif JLOptions().code_coverage == 1 || JLOptions().malloc_log == 1 # user + # Just say true. Pkgimages aren't in Base + return true + elseif JLOptions().code_coverage == 2 || JLOptions().malloc_log == 2 # all + return true + elseif JLOptions().code_coverage == 3 || JLOptions().malloc_log == 3 # tracked path + if JLOptions().tracked_path == C_NULL + return false + else + tracked_path = unsafe_string(JLOptions().tracked_path) + if isempty(tracked_path) + return false + else + return any(includes) do inc + startswith(inc.filename, tracked_path) + end + end + end + end +end + # loads a precompile cache file, ignoring stale_cachefile tests # load the best available (non-stale) version of all dependent modules first function _tryrequire_from_serialized(pkg::PkgId, path::String, ocachepath::Union{Nothing, String}) assert_havelock(require_lock) local depmodnames io = open(path, "r") + ignore_native = false try iszero(isvalid_cache_header(io)) && return ArgumentError("Invalid header in cache file $path.") - _, _, depmodnames, _, _, _, clone_targets, _ = parse_cache_header(io, path) + _, (includes, _, _), depmodnames, _, _, _, clone_targets, _ = parse_cache_header(io, path) + + ignore_native = pkg_tracked(includes) + pkgimage = !isempty(clone_targets) if pkgimage ocachepath !== nothing || return ArgumentError("Expected ocachepath to be provided") @@ -1529,7 +1573,7 @@ function _tryrequire_from_serialized(pkg::PkgId, path::String, ocachepath::Union depmods[i] = dep end # then load the file - return _include_from_serialized(pkg, path, ocachepath, depmods) + return _include_from_serialized(pkg, path, ocachepath, depmods, ignore_native) end # returns `nothing` if require found a precompile cache for this sourcepath, but couldn't load it diff --git a/base/multidimensional.jl b/base/multidimensional.jl index d8646e4c261ed8..0ca2fbc36e6dfc 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -1897,39 +1897,25 @@ julia> sortslices(reshape([5; 4; 3; 2; 1], (1,1,5)), dims=3, by=x->x[1,1]) ``` """ function sortslices(A::AbstractArray; dims::Union{Integer, Tuple{Vararg{Integer}}}, kws...) - _sortslices(A, Val{dims}(); kws...) -end + if A isa Matrix && dims isa Integer && dims == 1 + # TODO: remove once the generic version becomes as fast or faster + perm = sortperm(eachslice(A; dims); kws...) + return A[perm, :] + end -# Works around inference's lack of ability to recognize partial constness -struct DimSelector{dims, T} - A::T + B = similar(A) + _sortslices!(B, A, Val{dims}(); kws...) + B end -DimSelector{dims}(x::T) where {dims, T} = DimSelector{dims, T}(x) -(ds::DimSelector{dims, T})(i) where {dims, T} = i in dims ? axes(ds.A, i) : (:,) - -_negdims(n, dims) = filter(i->!(i in dims), 1:n) -function compute_itspace(A, ::Val{dims}) where {dims} - negdims = _negdims(ndims(A), dims) - axs = Iterators.product(ntuple(DimSelector{dims}(A), ndims(A))...) - vec(permutedims(collect(axs), (dims..., negdims...))) -end +function _sortslices!(B, A, ::Val{dims}; kws...) where dims + ves = vec(eachslice(A; dims)) + perm = sortperm(ves; kws...) + bes = eachslice(B; dims) -function _sortslices(A::AbstractArray, d::Val{dims}; kws...) where dims - itspace = compute_itspace(A, d) - vecs = map(its->view(A, its...), itspace) - p = sortperm(vecs; kws...) - if ndims(A) == 2 && isa(dims, Integer) && isa(A, Array) - # At the moment, the performance of the generic version is subpar - # (about 5x slower). Hardcode a fast-path until we're able to - # optimize this. - return dims == 1 ? A[p, :] : A[:, p] - else - B = similar(A) - for (x, its) in zip(p, itspace) - B[its...] = vecs[x] - end - B + # TODO for further optimization: traverse in memory order + for (slice, i) in zip(eachslice(B; dims), perm) + slice .= ves[i] end end diff --git a/base/reflection.jl b/base/reflection.jl index e0764ee761a3e0..304f7a4344fb2f 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -720,18 +720,10 @@ If `x === y` then `objectid(x) == objectid(y)`, and usually when `x !== y`, `obj See also [`hash`](@ref), [`IdDict`](@ref). """ -function objectid(x) - # objectid is foldable iff it isn't a pointer. - if isidentityfree(typeof(x)) - return _foldable_objectid(x) - end - return _objectid(x) -end -function _foldable_objectid(@nospecialize(x)) - @_foldable_meta - _objectid(x) +function objectid(@nospecialize(x)) + @_total_meta + return ccall(:jl_object_id, UInt, (Any,), x) end -_objectid(@nospecialize(x)) = ccall(:jl_object_id, UInt, (Any,), x) """ isdispatchtuple(T) @@ -1802,10 +1794,9 @@ function infer_effects(@nospecialize(f), @nospecialize(types=default_tt(f)); error("code reflection cannot be used from generated functions") if isa(f, Core.Builtin) types = to_tuple_type(types) - argtypes = Any[Core.Const(f), types.parameters...] - rt = Core.Compiler.builtin_tfunction(interp, f, argtypes[2:end], nothing) - return Core.Compiler.builtin_effects(Core.Compiler.typeinf_lattice(interp), f, - Core.Compiler.ArgInfo(nothing, argtypes), rt) + argtypes = Any[types.parameters...] + rt = Core.Compiler.builtin_tfunction(interp, f, argtypes, nothing) + return Core.Compiler.builtin_effects(Core.Compiler.typeinf_lattice(interp), f, argtypes, rt) end tt = signature_type(f, types) matches = Core.Compiler.findall(tt, Core.Compiler.method_table(interp)) @@ -2081,6 +2072,8 @@ function bodyfunction(basemethod::Method) else return nothing end + elseif isa(fsym, Core.SSAValue) + fsym = ast.code[fsym.id] else return nothing end diff --git a/base/shell.jl b/base/shell.jl index 5bfd11fb46d29c..48214418bdee52 100644 --- a/base/shell.jl +++ b/base/shell.jl @@ -18,12 +18,11 @@ end function shell_parse(str::AbstractString, interpolate::Bool=true; special::AbstractString="", filename="none") - s = SubString(str, firstindex(str)) + last_arg = firstindex(str) # N.B.: This is used by REPLCompletions + s = SubString(str, last_arg) s = rstrip_shell(lstrip(s)) - # N.B.: This is used by REPLCompletions - last_parse = 0:-1 - isempty(s) && return interpolate ? (Expr(:tuple,:()),last_parse) : ([],last_parse) + isempty(s) && return interpolate ? (Expr(:tuple,:()), last_arg) : ([], last_arg) in_single_quotes = false in_double_quotes = false @@ -32,6 +31,7 @@ function shell_parse(str::AbstractString, interpolate::Bool=true; arg = [] i = firstindex(s) st = Iterators.Stateful(pairs(s)) + update_last_arg = false # true after spaces or interpolate function push_nonempty!(list, x) if !isa(x,AbstractString) || !isempty(x) @@ -54,6 +54,7 @@ function shell_parse(str::AbstractString, interpolate::Bool=true; for (j, c) in st j, c = j::Int, c::C if !in_single_quotes && !in_double_quotes && isspace(c) + update_last_arg = true i = consume_upto!(arg, s, i, j) append_2to1!(args, arg) while !isempty(st) @@ -77,12 +78,17 @@ function shell_parse(str::AbstractString, interpolate::Bool=true; # use parseatom instead of parse to respect filename (#28188) ex, j = Meta.parseatom(s, stpos, filename=filename) end - last_parse = (stpos:prevind(s, j)) .+ s.offset - push_nonempty!(arg, ex) + last_arg = stpos + s.offset + update_last_arg = true + push!(arg, ex) s = SubString(s, j) Iterators.reset!(st, pairs(s)) i = firstindex(s) else + if update_last_arg + last_arg = i + s.offset + update_last_arg = false + end if !in_double_quotes && c == '\'' in_single_quotes = !in_single_quotes i = consume_upto!(arg, s, i, j) @@ -124,14 +130,14 @@ function shell_parse(str::AbstractString, interpolate::Bool=true; push_nonempty!(arg, s[i:end]) append_2to1!(args, arg) - interpolate || return args, last_parse + interpolate || return args, last_arg # construct an expression ex = Expr(:tuple) for arg in args push!(ex.args, Expr(:tuple, arg...)) end - return ex, last_parse + return ex, last_arg end function shell_split(s::AbstractString) @@ -216,7 +222,7 @@ function print_shell_escaped_posixly(io::IO, args::AbstractString...) function isword(c::AbstractChar) if '0' <= c <= '9' || 'a' <= c <= 'z' || 'A' <= c <= 'Z' # word characters - elseif c == '_' || c == '/' || c == '+' || c == '-' + elseif c == '_' || c == '/' || c == '+' || c == '-' || c == '.' # other common characters elseif c == '\'' have_single = true diff --git a/base/sort.jl b/base/sort.jl index f35add7a55b35d..90b43c040d9690 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -5,8 +5,7 @@ module Sort using Base.Order using Base: copymutable, midpoint, require_one_based_indexing, uinttype, - sub_with_overflow, add_with_overflow, OneTo, BitSigned, BitIntegerType, top_set_bit, - IteratorSize, HasShape, IsInfinite, tail + sub_with_overflow, add_with_overflow, OneTo, BitSigned, BitIntegerType, top_set_bit import Base: sort, @@ -1474,12 +1473,6 @@ end Variant of [`sort!`](@ref) that returns a sorted copy of `v` leaving `v` itself unmodified. -Returns something [`similar`](@ref) to `v` when `v` is an `AbstractArray` and uses -[`collect`](@ref) to support arbitrary non-`AbstractArray` iterables. - -!!! compat "Julia 1.10" - `sort` of arbitrary iterables requires at least Julia 1.10. - # Examples ```jldoctest julia> v = [3, 1, 2]; @@ -1497,39 +1490,7 @@ julia> v 2 ``` """ -function sort(v; kws...) - size = IteratorSize(v) - size == HasShape{0}() && throw(ArgumentError("$v cannot be sorted")) - size == IsInfinite() && throw(ArgumentError("infinite iterator $v cannot be sorted")) - sort!(collect(v); kws...) -end -sort(v::AbstractVector; kws...) = sort!(copymutable(v); kws...) # for method disambiguation -sort(::AbstractString; kws...) = - throw(ArgumentError("sort(::AbstractString) is not supported")) -sort(::Tuple; kws...) = - throw(ArgumentError("sort(::Tuple) is only supported for NTuples")) - -function sort(x::NTuple{N}; lt::Function=isless, by::Function=identity, - rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward) where N - o = ord(lt,by,rev,order) - if N > 9 - v = sort!(collect(x), DEFAULT_STABLE, o) - tuple((v[i] for i in 1:N)...) - else - _sort(x, o) - end -end -_sort(x::Union{NTuple{0}, NTuple{1}}, o::Ordering) = x -function _sort(x::NTuple, o::Ordering) - a, b = Base.IteratorsMD.split(x, Val(length(x)>>1)) - merge(_sort(a, o), _sort(b, o), o) -end -merge(x::NTuple, y::NTuple{0}, o::Ordering) = x -merge(x::NTuple{0}, y::NTuple, o::Ordering) = y -merge(x::NTuple{0}, y::NTuple{0}, o::Ordering) = x # Method ambiguity -merge(x::NTuple, y::NTuple, o::Ordering) = - (lt(o, y[1], x[1]) ? (y[1], merge(x, tail(y), o)...) : (x[1], merge(tail(x), y, o)...)) - +sort(v::AbstractVector; kws...) = sort!(copymutable(v); kws...) ## partialsortperm: the permutation to sort the first k elements of an array ## diff --git a/base/strings/string.jl b/base/strings/string.jl index 9f5995c25c558f..29216ae97aa37b 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -87,7 +87,7 @@ end # This is @assume_effects :effect_free :nothrow :terminates_globally @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,0xe)), :(convert(Csize_t, n)))) +@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)))) """ String(s::AbstractString) diff --git a/base/subarray.jl b/base/subarray.jl index 61d59bddf547d8..464bd660b08560 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -52,8 +52,10 @@ viewindexing(I::Tuple{Slice, Slice, Vararg{Any}}) = (@inline; viewindexing(tail( # A UnitRange can follow Slices, but only if all other indices are scalar viewindexing(I::Tuple{Slice, AbstractUnitRange, Vararg{ScalarIndex}}) = IndexLinear() viewindexing(I::Tuple{Slice, Slice, Vararg{ScalarIndex}}) = IndexLinear() # disambiguate -# In general, ranges are only fast if all other indices are scalar -viewindexing(I::Tuple{AbstractRange, Vararg{ScalarIndex}}) = IndexLinear() +# In general, scalar ranges are only fast if all other indices are scalar +# Other ranges, such as those of `CartesianIndex`es, are not fast even if these +# are followed by `ScalarIndex`es +viewindexing(I::Tuple{AbstractRange{<:ScalarIndex}, Vararg{ScalarIndex}}) = IndexLinear() # All other index combinations are slow viewindexing(I::Tuple{Vararg{Any}}) = IndexCartesian() # Of course, all other array types are slow @@ -483,8 +485,8 @@ end has_offset_axes(S::SubArray) = has_offset_axes(S.indices...) function replace_in_print_matrix(S::SubArray{<:Any,2,<:AbstractMatrix}, i::Integer, j::Integer, s::AbstractString) - replace_in_print_matrix(S.parent, reindex(S.indices, (i,j))..., s) + replace_in_print_matrix(S.parent, to_indices(S.parent, reindex(S.indices, (i,j)))..., s) end function replace_in_print_matrix(S::SubArray{<:Any,1,<:AbstractVector}, i::Integer, j::Integer, s::AbstractString) - replace_in_print_matrix(S.parent, reindex(S.indices, (i,))..., j, s) + replace_in_print_matrix(S.parent, to_indices(S.parent, reindex(S.indices, (i,)))..., j, s) end diff --git a/base/tuple.jl b/base/tuple.jl index c2e0a478cca48f..2e1c5972c407df 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -30,8 +30,7 @@ size(@nospecialize(t::Tuple), d::Integer) = (d == 1) ? length(t) : throw(Argumen axes(@nospecialize t::Tuple) = (OneTo(length(t)),) getindex(@nospecialize(t::Tuple), i::Int) = getfield(t, i, @_boundscheck) getindex(@nospecialize(t::Tuple), i::Integer) = getfield(t, convert(Int, i), @_boundscheck) -__inbounds_getindex(@nospecialize(t::Tuple), i::Int) = getfield(t, i, false) -__inbounds_getindex(@nospecialize(t::Tuple), i::Integer) = getfield(t, convert(Int, i), false) +__safe_getindex(@nospecialize(t::Tuple), i::Int) = (@_nothrow_noub_meta; getfield(t, i, false)) getindex(t::Tuple, r::AbstractArray{<:Any,1}) = (eltype(t)[t[ri] for ri in r]...,) getindex(t::Tuple, b::AbstractArray{Bool,1}) = length(b) == length(t) ? getindex(t, findall(b)) : throw(BoundsError(t, b)) getindex(t::Tuple, c::Colon) = t diff --git a/base/util.jl b/base/util.jl index fe04eaf04bd47f..a3771f4ae9dc49 100644 --- a/base/util.jl +++ b/base/util.jl @@ -163,8 +163,7 @@ To get the julia command without propagated command line arguments, `julia_cmd() The flags `--color` and `--startup-file` were added in Julia 1.5. !!! compat "Julia 1.9" - The keyword argument `cpu_target` was added. - + The keyword argument `cpu_target` was added in 1.9. The flag `--pkgimages` was added in Julia 1.9. """ function julia_cmd(julia=joinpath(Sys.BINDIR, julia_exename()); cpu_target::Union{Nothing,String} = nothing) @@ -243,9 +242,6 @@ function julia_cmd(julia=joinpath(Sys.BINDIR, julia_exename()); cpu_target::Unio end if opts.use_pkgimages == 0 push!(addflags, "--pkgimages=no") - else - # If pkgimage is set, malloc_log and code_coverage should not - @assert opts.malloc_log == 0 && opts.code_coverage == 0 end return `$julia -C$cpu_target -J$image_file $addflags` end @@ -691,7 +687,7 @@ function runtests(tests = ["all"]; ncores::Int = ceil(Int, Sys.CPU_THREADS / 2), ENV2["JULIA_CPU_THREADS"] = "$ncores" pathsep = Sys.iswindows() ? ";" : ":" ENV2["JULIA_DEPOT_PATH"] = string(mktempdir(; cleanup = true), pathsep) # make sure the default depots can be loaded - delete!(ENV2, "JULIA_LOAD_PATH") + ENV2["JULIA_LOAD_PATH"] = string("@", pathsep, "@stdlib") delete!(ENV2, "JULIA_PROJECT") try run(setenv(`$(julia_cmd()) $(joinpath(Sys.BINDIR, diff --git a/cli/Makefile b/cli/Makefile index b6a2b48ebf0441..c72ebff2b9bfd0 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -20,7 +20,8 @@ ifeq ($(OS),WINNT) LOADER_LDFLAGS += -municode -mconsole -nostdlib --disable-auto-import \ --disable-runtime-pseudo-reloc -lntdll -lkernel32 -lpsapi else ifeq ($(OS),Linux) -LOADER_LDFLAGS += -Wl,--no-as-needed -ldl -lpthread -rdynamic -lc -Wl,--as-needed +# textoff and notext are aliases to the same option which suppress the TEXTREL warning for i686 +LOADER_LDFLAGS += -Wl,--no-as-needed -ldl -lpthread -rdynamic -lc -Wl,--as-needed -Wl,-z,notext else ifeq ($(OS),FreeBSD) LOADER_LDFLAGS += -Wl,--no-as-needed -ldl -lpthread -rdynamic -lc -Wl,--as-needed else ifeq ($(OS),Darwin) diff --git a/cli/trampolines/trampolines_i686.S b/cli/trampolines/trampolines_i686.S index 3d9cacf0ce652c..f6c46fd6ee49b3 100644 --- a/cli/trampolines/trampolines_i686.S +++ b/cli/trampolines/trampolines_i686.S @@ -3,13 +3,41 @@ #include "common.h" #include "../../src/jl_exported_funcs.inc" +// set this option to 1 to get very slightly slower trampolines which however do not trigger +// this linker warning: +// ld: ./loader_trampolines.o: warning: relocation against `jl_***_addr' in read-only section `.text' +// ld: warning: creating DT_TEXTREL in a shared object +// If you have a large libjulia.so file or other restrictions on using TEXTREL for some +// reason, this may be worthwhile. +// This is not relevant on Windows (though it is valid there), since it always uses +// DT_TEXTREL anyways, and does not support this notion of PIC. +#define USE_PC32 0 + +#if USE_PC32 +.cfi_startproc +julia__x86.get_pc_thunk.ax: + mov (%esp),%eax + ret +.cfi_endproc + +#define CALL(name) \ + call julia__x86.get_pc_thunk.ax; \ + jmpl *(CNAMEADDR(name) - .)(%eax); \ + +#else + +#define CALL(name) \ + jmpl *(CNAMEADDR(name)); \ + +#endif + #define XX(name) \ DEBUGINFO(CNAME(name)); \ .global CNAME(name); \ .cfi_startproc; \ CNAME(name)##:; \ CET_START(); \ - jmpl *(CNAMEADDR(name)); \ + CALL(name); \ ud2; \ .cfi_endproc; \ EXPORT(name); \ diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 82c9ea2aba037f..a685f28a2c735d 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -334,10 +334,6 @@ end generate_precompile_statements() -# As a last step in system image generation, -# remove some references to build time environment for a more reproducible build. -Base.Filesystem.temp_cleanup_purge(force=true) - let stdout = Ref{IO}(stdout) Base.PROGRAM_FILE = "" Sys.BINDIR = "" diff --git a/deps/JuliaSyntax.version b/deps/JuliaSyntax.version index 1d7dfd8efd0968..7f124715024ce1 100644 --- a/deps/JuliaSyntax.version +++ b/deps/JuliaSyntax.version @@ -1,4 +1,4 @@ JULIASYNTAX_BRANCH = main -JULIASYNTAX_SHA1 = a9110fa8ecbe79943bb9525b4ccd99a3976cfcb7 +JULIASYNTAX_SHA1 = 4f1731d6ce7c2465fc21ea245110b7a39f34658a JULIASYNTAX_GIT_URL := https://github.com/JuliaLang/JuliaSyntax.jl.git JULIASYNTAX_TAR_URL = https://api.github.com/repos/JuliaLang/JuliaSyntax.jl/tarball/$1 diff --git a/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/md5 b/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/md5 new file mode 100644 index 00000000000000..c2663955ec7736 --- /dev/null +++ b/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/md5 @@ -0,0 +1 @@ +8c9d9579eeab1ba40f978a32c9db9900 diff --git a/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/sha512 b/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/sha512 new file mode 100644 index 00000000000000..46647cb3e432b6 --- /dev/null +++ b/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/sha512 @@ -0,0 +1 @@ +1bdad624f61482b55deba8727fea1c087bfaea9e1f8afa3b44b984441fb7e663dac067baa4a96ae2d4cbd4a46ae8c87e9d20d2dfcd17046ad194711304184e57 diff --git a/deps/checksums/JuliaSyntax-a9110fa8ecbe79943bb9525b4ccd99a3976cfcb7.tar.gz/md5 b/deps/checksums/JuliaSyntax-a9110fa8ecbe79943bb9525b4ccd99a3976cfcb7.tar.gz/md5 deleted file mode 100644 index 25f5adf71897d8..00000000000000 --- a/deps/checksums/JuliaSyntax-a9110fa8ecbe79943bb9525b4ccd99a3976cfcb7.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -a42bbb42babbbd727556f6bc01455826 diff --git a/deps/checksums/JuliaSyntax-a9110fa8ecbe79943bb9525b4ccd99a3976cfcb7.tar.gz/sha512 b/deps/checksums/JuliaSyntax-a9110fa8ecbe79943bb9525b4ccd99a3976cfcb7.tar.gz/sha512 deleted file mode 100644 index cd3f3f3043d450..00000000000000 --- a/deps/checksums/JuliaSyntax-a9110fa8ecbe79943bb9525b4ccd99a3976cfcb7.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -d542766e72b57418b9b4e17743f89d8535c1f36497346b57538bd0cb451e64af9493015692179f80ec4ee8cf18349c1e0888f5710db895e19f9bb0322f0f7464 diff --git a/deps/checksums/Pkg-2d59169bffdf29a4ed7bafd8b35c391ede612dde.tar.gz/md5 b/deps/checksums/Pkg-2d59169bffdf29a4ed7bafd8b35c391ede612dde.tar.gz/md5 deleted file mode 100644 index 97f6d9582d52c2..00000000000000 --- a/deps/checksums/Pkg-2d59169bffdf29a4ed7bafd8b35c391ede612dde.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -d077b794fabe0879f4bb8b548ad75ead diff --git a/deps/checksums/Pkg-2d59169bffdf29a4ed7bafd8b35c391ede612dde.tar.gz/sha512 b/deps/checksums/Pkg-2d59169bffdf29a4ed7bafd8b35c391ede612dde.tar.gz/sha512 deleted file mode 100644 index a59f670469bf0c..00000000000000 --- a/deps/checksums/Pkg-2d59169bffdf29a4ed7bafd8b35c391ede612dde.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -70502a1fc7732b690cfd2145079d44761800a6fe3ff090b7f27a7b96680178c6afc47b9f3df00d4f254dbb355203f038d69fb44bceb7c8fab278b47fb8a3cf79 diff --git a/deps/checksums/Pkg-debc38b9a2d800f035a17f42a01c14a3775fa859.tar.gz/md5 b/deps/checksums/Pkg-debc38b9a2d800f035a17f42a01c14a3775fa859.tar.gz/md5 new file mode 100644 index 00000000000000..d222465b498dde --- /dev/null +++ b/deps/checksums/Pkg-debc38b9a2d800f035a17f42a01c14a3775fa859.tar.gz/md5 @@ -0,0 +1 @@ +e6fee75f07782a63581ec844a25e97f5 diff --git a/deps/checksums/Pkg-debc38b9a2d800f035a17f42a01c14a3775fa859.tar.gz/sha512 b/deps/checksums/Pkg-debc38b9a2d800f035a17f42a01c14a3775fa859.tar.gz/sha512 new file mode 100644 index 00000000000000..9ee7f65eaaabf1 --- /dev/null +++ b/deps/checksums/Pkg-debc38b9a2d800f035a17f42a01c14a3775fa859.tar.gz/sha512 @@ -0,0 +1 @@ +f3353f4493c25a45db7125be2448fb3799e2c6f02b9fde14a444b4d4bf7c144d56abde6f8b678407f918b3c2ba00f6ab19047b908004f5034d422d5cfd6c1f41 diff --git a/deps/checksums/SparseArrays-37fc321e28a32f79a928c24e5739a83d92de5205.tar.gz/md5 b/deps/checksums/SparseArrays-37fc321e28a32f79a928c24e5739a83d92de5205.tar.gz/md5 deleted file mode 100644 index 2f1c9774022162..00000000000000 --- a/deps/checksums/SparseArrays-37fc321e28a32f79a928c24e5739a83d92de5205.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -c69549ac1ff7fd9122b242c2bc50ecb2 diff --git a/deps/checksums/SparseArrays-37fc321e28a32f79a928c24e5739a83d92de5205.tar.gz/sha512 b/deps/checksums/SparseArrays-37fc321e28a32f79a928c24e5739a83d92de5205.tar.gz/sha512 deleted file mode 100644 index 9f54e3f375b16b..00000000000000 --- a/deps/checksums/SparseArrays-37fc321e28a32f79a928c24e5739a83d92de5205.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -381ac83206f2791115f9ccb5e7f794760fdc927a1e22601b26a555d0a744133dceaf8cf0b5ae42f1df9c9158f95f3c51adc8246a4d7a7c543ed71497dd19e0d3 diff --git a/deps/checksums/SparseArrays-f154de2b6801ec8d5afaf58b73b830c8e71013c3.tar.gz/md5 b/deps/checksums/SparseArrays-f154de2b6801ec8d5afaf58b73b830c8e71013c3.tar.gz/md5 new file mode 100644 index 00000000000000..9a38667523ea0d --- /dev/null +++ b/deps/checksums/SparseArrays-f154de2b6801ec8d5afaf58b73b830c8e71013c3.tar.gz/md5 @@ -0,0 +1 @@ +e4c17513749ebdd898daf62f932fc9b3 diff --git a/deps/checksums/SparseArrays-f154de2b6801ec8d5afaf58b73b830c8e71013c3.tar.gz/sha512 b/deps/checksums/SparseArrays-f154de2b6801ec8d5afaf58b73b830c8e71013c3.tar.gz/sha512 new file mode 100644 index 00000000000000..65478645745404 --- /dev/null +++ b/deps/checksums/SparseArrays-f154de2b6801ec8d5afaf58b73b830c8e71013c3.tar.gz/sha512 @@ -0,0 +1 @@ +15a6dfcf64ebc2ff3893373107f317a1abf8518d852219092ead1bdf1db602dc4ac36d836e0c8412b6b1e0b42147deab95d355741fc72234b52f3399a56ba8f9 diff --git a/deps/checksums/nghttp2 b/deps/checksums/nghttp2 index 66ae3cbf34c0ef..9c419f5234ffcb 100644 --- a/deps/checksums/nghttp2 +++ b/deps/checksums/nghttp2 @@ -1,34 +1,34 @@ -nghttp2-1.52.0.tar.bz2/md5/bde5874bd8e7e8be3512a621de27b9d5 -nghttp2-1.52.0.tar.bz2/sha512/019ec7a904d1baf8755ffcea0b38acf45ea9c6829d989a530ab35807338ba78d3328b86eebb3106b8372b7a8c51b466974d423e0cd786b6d6d020f0840c160bf -nghttp2.v1.52.0+1.aarch64-apple-darwin.tar.gz/md5/e3d9e07029e184cc55b7e0c4d2e27c7f -nghttp2.v1.52.0+1.aarch64-apple-darwin.tar.gz/sha512/cd098db984f751b00d2cc99d7f7eba0fa830ba178dd85a9dfa679a591e62d57364dcfd74e6a55ef513a0436a8e520b1a5474d4bfa9a8bdcd70e398482b7c9985 -nghttp2.v1.52.0+1.aarch64-linux-gnu.tar.gz/md5/73fe75f3cfa2bd3e804ea39a4eb884a9 -nghttp2.v1.52.0+1.aarch64-linux-gnu.tar.gz/sha512/71f4b2a23ba148b66432797b0db954dbd98fc900045d4572f488b43779aae125f71929e5bba6bbadd30c7998a133c5e5beb70888968bf3b01bb5fe9c9ea0e451 -nghttp2.v1.52.0+1.aarch64-linux-musl.tar.gz/md5/736a24a7eee567851a965558e31489fb -nghttp2.v1.52.0+1.aarch64-linux-musl.tar.gz/sha512/ab36182b04a590b092fae9e3a912a87467e8b01ad40a628a1d2e52910ee513ab327d5d2836df598d5aa8203f60a605d19d0b9636eb35d12a84a1c9d87124604b -nghttp2.v1.52.0+1.armv6l-linux-gnueabihf.tar.gz/md5/56fd32e8d77d4c9d9e2355565f4db19b -nghttp2.v1.52.0+1.armv6l-linux-gnueabihf.tar.gz/sha512/85718e0e5cee35d91a8684ea33d8f965bb30d62dbd6b74a574a2fbc4c1027b1ef23ef68f1dec3f037fa6c5739287329567df9591a69f8f23b23fab2516a0b644 -nghttp2.v1.52.0+1.armv6l-linux-musleabihf.tar.gz/md5/283273d3bf4d53b56d12ef6af2e72f20 -nghttp2.v1.52.0+1.armv6l-linux-musleabihf.tar.gz/sha512/5c1d92cbf5f2f4e1ceb4ee13634c0bceb6ca28abaf9d87cc673f264d274bb96aa095648295e9aa76f86eb0890a426f47c0b942e72610daf722ed8e86b5f0df69 -nghttp2.v1.52.0+1.armv7l-linux-gnueabihf.tar.gz/md5/d7ae84e5365759a42d0fe0360f679b61 -nghttp2.v1.52.0+1.armv7l-linux-gnueabihf.tar.gz/sha512/63212e3ad94d2bc54ca9ebd452d8de8e67aa53c03a3b3033d36da765303e714d8d5c24156ea4fb985acc72fe52e2977e8e8a658cdd9409bd41ecf401c08c1aee -nghttp2.v1.52.0+1.armv7l-linux-musleabihf.tar.gz/md5/a6ad0f25f43b7f1832faeaaadf683ed4 -nghttp2.v1.52.0+1.armv7l-linux-musleabihf.tar.gz/sha512/64b9075c0d819288345d53c5ce88b360d2ca4d24c3d2e81fb53c55f86054b1a3e95d7831b363a4100965cdbf479268a5993d66ef59089a219a97b4151d8fef60 -nghttp2.v1.52.0+1.i686-linux-gnu.tar.gz/md5/9781f6eeb4d24a291d6737e59e74edc1 -nghttp2.v1.52.0+1.i686-linux-gnu.tar.gz/sha512/2b542cb67e78993ef881694dc50c980b57db3761c5f4e11c381afb1b31d1fb8ab0a8b20e1279303a602c07912f21e8ef9d732366b76ab3f356a74b444a5dc78c -nghttp2.v1.52.0+1.i686-linux-musl.tar.gz/md5/08603b9364179ab4cbe0637b9b1b63b5 -nghttp2.v1.52.0+1.i686-linux-musl.tar.gz/sha512/0a5b79709482548c6a713843b670695b4b13d2b219b592d029719da0b4187fe884798fb44e2c511c300f02bab03f2b0b289d49d6256e3ce0b9602a66ea2382bd -nghttp2.v1.52.0+1.i686-w64-mingw32.tar.gz/md5/1abdf0cad466ed0ca0da137809999d8e -nghttp2.v1.52.0+1.i686-w64-mingw32.tar.gz/sha512/04680895ead989fda56b284d8963e7ca31680492c8f77f4c6bd7ca03b9a66ee7529b78cf35e07b2e106f43c9aa543dffd4081b034339803ba95021293d3df997 -nghttp2.v1.52.0+1.powerpc64le-linux-gnu.tar.gz/md5/ae411e40e24cb3f3b07fe8de211b58c6 -nghttp2.v1.52.0+1.powerpc64le-linux-gnu.tar.gz/sha512/7433502d76646e5761ea2707fa65ea5a412c513c70908a4d9ceb504f08121b1f39bcff984543370c221814785b7064f85dedc777a22df5e30a64a64e510e0978 -nghttp2.v1.52.0+1.x86_64-apple-darwin.tar.gz/md5/59f0de0affaa17898e837b5074de68fc -nghttp2.v1.52.0+1.x86_64-apple-darwin.tar.gz/sha512/e639c813373b17d95220640ec2a568e9731cfc32df826610357ec9ff8e9d7e7abe10291140eaeb9342ae69215798bf3f999db7647c23efb4f815b54f4da9cfe4 -nghttp2.v1.52.0+1.x86_64-linux-gnu.tar.gz/md5/6bc8501392d47b349c7463e984dc5909 -nghttp2.v1.52.0+1.x86_64-linux-gnu.tar.gz/sha512/522cc2a8464ee5770c01b83a6b4ecbbcce322efffbd738f7c907643fe85342e785bbc805028d41c2b7404d6241168d1ab37a9db15018623c265b53905bcf060f -nghttp2.v1.52.0+1.x86_64-linux-musl.tar.gz/md5/725a6adc23880b28303017597b974535 -nghttp2.v1.52.0+1.x86_64-linux-musl.tar.gz/sha512/ede5a34b7f71310e4c3cd99b9b61b2453db5dc8117675de12adb1e68c9283cdf821614f49f4d04bdd3b0f17d51a52972ec1e226d0dbdc5462b1a4a1fcc9f39e7 -nghttp2.v1.52.0+1.x86_64-unknown-freebsd.tar.gz/md5/02e68f367dd5f2ceac3a619da402cbb4 -nghttp2.v1.52.0+1.x86_64-unknown-freebsd.tar.gz/sha512/d0522c4f40471cdfc0768863f9b0e97b453b2e0c850417811d4f264fd167622493141beea66a8668b15dc6b9b4ae42a38017b9f31ed59c9205701188df3d84b9 -nghttp2.v1.52.0+1.x86_64-w64-mingw32.tar.gz/md5/e1c8ec6ec2d69b2ac64b114ebf09f8b4 -nghttp2.v1.52.0+1.x86_64-w64-mingw32.tar.gz/sha512/cb43cb138f14717501e852ed388a44d41012e2bb70b6887584b37b4e0f42827d74f17ea85ba4aa0bc09d623dedeef73eee80815c1db2b6858b31251feb0b5580 +nghttp2-1.58.0.tar.bz2/md5/f60a0340da2638eba448df73b6783bc3 +nghttp2-1.58.0.tar.bz2/sha512/7a40afe80587a13bdd7fd85fc7eb666c0dcc18b951a03c65ab4b52429bdf5d2eae27a9bfaa7303e7517e275239397f60dd466170364dfbd1077e25188ba54aca +nghttp2.v1.58.0+0.aarch64-apple-darwin.tar.gz/md5/528dec6d4a54854532d527067dccade7 +nghttp2.v1.58.0+0.aarch64-apple-darwin.tar.gz/sha512/cfc993f23f95ef3b2841d40e704c17816dec22a173a16c5d0bc3c85a092f1d63b69e47112a8a1aeb4e85603a7087746b97fbf9ea91a0ec42b45d38deb78f3e0b +nghttp2.v1.58.0+0.aarch64-linux-gnu.tar.gz/md5/c358e3407e956bddab6fde752c2bd2b5 +nghttp2.v1.58.0+0.aarch64-linux-gnu.tar.gz/sha512/207ef70f4ba231e8e4fd570a5da7931a488a297d2d525983499d874abef268440d3e8408cc953c3deab5219bc8c93034b89bf2f3e4c1179b52cede354ae6d27f +nghttp2.v1.58.0+0.aarch64-linux-musl.tar.gz/md5/eef1a1209fdbf30a71af878e307c4fb2 +nghttp2.v1.58.0+0.aarch64-linux-musl.tar.gz/sha512/9ad61002303add92b3d9842a408092b8b35ac8ac5ab17fabb77f144261c4829a2695111177b4794901efba87f97ed9274aac9935fc702c60189395abbb4d9a3f +nghttp2.v1.58.0+0.armv6l-linux-gnueabihf.tar.gz/md5/2864d534833745ff12ebb86378a90b0b +nghttp2.v1.58.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/091c367466cd547859d06b6d0bd37d424b0ccefc0f41dd4819156a61f98e0cc1eebf6da4171810e2a876fec9b919675efd9240089681ca8d3cd8620f5f40f8f3 +nghttp2.v1.58.0+0.armv6l-linux-musleabihf.tar.gz/md5/30a7b272ed36c45a3a5f58e7406150e5 +nghttp2.v1.58.0+0.armv6l-linux-musleabihf.tar.gz/sha512/c1c96c2e987a700929c0158232ef745a0fe8d93c722174a91b32c767cdc0441ee73059da9f64daca4010367ee005a9037919cdb73079a12f9612f45852549b0d +nghttp2.v1.58.0+0.armv7l-linux-gnueabihf.tar.gz/md5/3940e07955102a7a71030fe4c8aed59f +nghttp2.v1.58.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/5b628901371a36937a16802211707334b02632c8651a5fd01d2e1508926e51c960b693abbea6f629cfb7ca750b6039b5e4385324f2d431f1fe6e47b121a57f30 +nghttp2.v1.58.0+0.armv7l-linux-musleabihf.tar.gz/md5/2d6d0b8adba7bfea20dabde3c87333df +nghttp2.v1.58.0+0.armv7l-linux-musleabihf.tar.gz/sha512/10c72fdc2e635add9f0364dd95fd8404f187068bce05587531d4865f75126c8eaf4f7c154762175d3ed0e85914b21df90c193766f610da91bb6dd75fe93df5fa +nghttp2.v1.58.0+0.i686-linux-gnu.tar.gz/md5/8753bf62f8863b5c3041ef73ed3fe36a +nghttp2.v1.58.0+0.i686-linux-gnu.tar.gz/sha512/741a8f2bd5902df9f06de67ecdcb041951a21e0d32f5acffac780cba7a41be5d67507ca49f65bf636849baf1b07e53871d9ee5624d4e33e1d4ff4137bd785bda +nghttp2.v1.58.0+0.i686-linux-musl.tar.gz/md5/a1b0f85e40bef6f2647a9e1dfa415196 +nghttp2.v1.58.0+0.i686-linux-musl.tar.gz/sha512/0e438073cc6fe39e2b47d7b89467c1cab1abe953e357f782b2cb16de5f9238f063b925ab602558a1d562b3c3f608167dd339a4ec54ec93b27c4f43bd2e54aaed +nghttp2.v1.58.0+0.i686-w64-mingw32.tar.gz/md5/7cf81bd21105f4d9fd5dd4dbf61fcb70 +nghttp2.v1.58.0+0.i686-w64-mingw32.tar.gz/sha512/47624f56df68983d3bd881cab1e88be249b678cb184cc426443c4a8c85481af03cddc65f642f1d88afd300cad88950662a99c5dbd75a459bd99dc56d3f38231c +nghttp2.v1.58.0+0.powerpc64le-linux-gnu.tar.gz/md5/a6e9e975026f658cb2b68418c5f2cee9 +nghttp2.v1.58.0+0.powerpc64le-linux-gnu.tar.gz/sha512/2007cf1420ac0866b4b6b5b463aa9c491abd85b2559bea259efa075e308301b10f1d8e72f86c36c21cf42e891f672ac289b5c81640a83f002c89aefebeefb355 +nghttp2.v1.58.0+0.x86_64-apple-darwin.tar.gz/md5/06c250253f1969f1c8460f3c65ec02a9 +nghttp2.v1.58.0+0.x86_64-apple-darwin.tar.gz/sha512/397bd1ea32617ff005ad4217cdd5652a6a58d5c98837cdb8366aa4d0fd86824673864850aeb3771c3304755d8d5466b881b4007603a9cded924cd6b8b18384f4 +nghttp2.v1.58.0+0.x86_64-linux-gnu.tar.gz/md5/2be0cc258a81106eaea132fbbdfce04f +nghttp2.v1.58.0+0.x86_64-linux-gnu.tar.gz/sha512/1be2b3906b9a5e2cb9af24ea358b92b50e3c7be85e9533784e3721b28219bce555700ebe3edf789123ff471a062dfc22f10eef1d21a6f060a43151f8904495d0 +nghttp2.v1.58.0+0.x86_64-linux-musl.tar.gz/md5/55898f6770dd85835d76e52003e8a1f6 +nghttp2.v1.58.0+0.x86_64-linux-musl.tar.gz/sha512/ad2266563dca33543c5fae9d0cb4daf9f0c6f4ccab8efc30e2eb273be9df33ae43da3007bcf83d19f39b830c11c17f8577014c81fcf5e76dce159f28937166f2 +nghttp2.v1.58.0+0.x86_64-unknown-freebsd.tar.gz/md5/5f9886a4e321bc393dd086106a685466 +nghttp2.v1.58.0+0.x86_64-unknown-freebsd.tar.gz/sha512/d86ba3730bb63143f990bdbe453fa4a0692b434beab372aa9a8e7673c2fddc8c97ebd2e47ac109ffe50172121f2d56ddd40d8d500b73e61a58635edcede8d071 +nghttp2.v1.58.0+0.x86_64-w64-mingw32.tar.gz/md5/98ecb527506234cc69cb8a958a74a002 +nghttp2.v1.58.0+0.x86_64-w64-mingw32.tar.gz/sha512/3941233fdfa82af9a5590f5975728d51e42791eb1b966a4ae7913a24f52500c24dea05e66911b3c5768535b145c99b31e2dc2ab50354648492df4b4b63434578 diff --git a/deps/checksums/openblas b/deps/checksums/openblas index 9e11a478e74273..b49af255be0e7a 100644 --- a/deps/checksums/openblas +++ b/deps/checksums/openblas @@ -1,94 +1,94 @@ -OpenBLAS.v0.3.24+0.aarch64-apple-darwin-libgfortran5.tar.gz/md5/9b52215f0729a2d96bc86a820c280433 -OpenBLAS.v0.3.24+0.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/b772022a498075f1712e707ed394583af49a25db60bce9d59981827f3d82968e5ddbbd05394c791ce89595b4c2442330870c3a36739e5511a99f166c2805b3ae -OpenBLAS.v0.3.24+0.aarch64-linux-gnu-libgfortran3.tar.gz/md5/d89ee1bb7ebefe186c02354209d60e94 -OpenBLAS.v0.3.24+0.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/ed8178b62bacf8aefe094adb822cfda4cb696161ca712b7b70c635b1f7868d84c2912880010465cb6ba00632e25b3bdcdb9f1bab9307ccb7bf1d7eb7a2a760c7 -OpenBLAS.v0.3.24+0.aarch64-linux-gnu-libgfortran4.tar.gz/md5/271a15b5d77c1501b13934702e0bea3d -OpenBLAS.v0.3.24+0.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/f2dfb1478471870526c3b8a2b8173eb4705b2dcb7e2c8b4fd4acebe24f4bdc999262e998f832a6769a5def4577256b07f4aa76c058abf7e93ea5e814a74d014d -OpenBLAS.v0.3.24+0.aarch64-linux-gnu-libgfortran5.tar.gz/md5/7c47ee25fade4bf4d4fcbd9b8e5feea7 -OpenBLAS.v0.3.24+0.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/b92e08f527c30d74470e1fa8275dfa6f8ce6bdae028394fc1440a55e4b1ddd15d8ab14bf36b87cf400ee22a69c13ce9b78c22889dccb5ffc4945c5293deea445 -OpenBLAS.v0.3.24+0.aarch64-linux-musl-libgfortran3.tar.gz/md5/2c00dd0f2d0e8e3938e02e21ad4fcbed -OpenBLAS.v0.3.24+0.aarch64-linux-musl-libgfortran3.tar.gz/sha512/e61635827d9495bbbe8726a3d7803c786b9304dbb32ba3d99ac5a359ae827b6e48e0db7ffa3fc2279d65e2cda3ca96abccdbcdd86b54e962cc8b7f86937e9594 -OpenBLAS.v0.3.24+0.aarch64-linux-musl-libgfortran4.tar.gz/md5/35297e1b32247edeb41907fca6f18a0a -OpenBLAS.v0.3.24+0.aarch64-linux-musl-libgfortran4.tar.gz/sha512/b8277b5f07b29940f5893f44af4fec3278c8cb7e52db7fb7f416ffe644d47f781d98f8d9fef89dea623762473e78abe61977eff1f4c3f1c9ff44dd833430f897 -OpenBLAS.v0.3.24+0.aarch64-linux-musl-libgfortran5.tar.gz/md5/ce187d3e01cfa633421567c2c5d18509 -OpenBLAS.v0.3.24+0.aarch64-linux-musl-libgfortran5.tar.gz/sha512/408106528aa80ea68808d99544ff4e6485d5912bf7e2586936c0e7758d94a53705388077140b8335714cd75772ef9d5465fa629fdac6939268ba11617076c42d -OpenBLAS.v0.3.24+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/52a7f29bfe47e89f32b5c95f6ce65c29 -OpenBLAS.v0.3.24+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/4816b3e022613b6a7a2073dd292311b29d8e848887f4d0ed7bcf32140bce2e9f15e05d754c48c1575cbaf70fcae8cfec501725df15d6bb1c7ac1ac1c74b59489 -OpenBLAS.v0.3.24+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/6f4aa76cc544ce52da6aa094ee5a1aeb -OpenBLAS.v0.3.24+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/c3428c36e19e9bd89dd0eb809f2617b7f90cbfaadffe9c58bf16c319e5e36d07102f48bafeb7a787bb6a18655ba3688a29ccd4f3724bc9354915d7e55fb64d44 -OpenBLAS.v0.3.24+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/298a83c0550694b3b6c22f6dd460cc9d -OpenBLAS.v0.3.24+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/b8e5ac5304364c950a427384db13ed5c573e7b5e9d16bb7c354b7b5e46ca8e7471bc911a2cf9defbfc933fbfbff315949011b5596532b8863c551de6d543f434 -OpenBLAS.v0.3.24+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/2b584ae3aa496ff871cc735242e63cf4 -OpenBLAS.v0.3.24+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/ba0911f7f6f9f9a5823fa0e98778e4a7d2a9bbd5d674d552fefd20458743b60baf10a49e1ee65a3b934636229296d64b1eece55826f136824f9cae91b6a208c4 -OpenBLAS.v0.3.24+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/2ff249795f3d8796b99536a4bf11670c -OpenBLAS.v0.3.24+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/517c4a99933e439de7ede07376b6f40785406216489f1bd6f995e5d6e444c860df401807abba6dc04ff83dd701f32047fcd4d3fd01660601f27edbc545ea783e -OpenBLAS.v0.3.24+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/f3c3fab4d30655b8cd831511a307d4d3 -OpenBLAS.v0.3.24+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/06d47db95ec374b5238857c1ad595cee882441fd462ae6a030f1a1c4465b617228feb34fd80a97d8ca965bfc87f8d4a3a94a3654b2c0615ef4506fa827e8c155 -OpenBLAS.v0.3.24+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/52a7f29bfe47e89f32b5c95f6ce65c29 -OpenBLAS.v0.3.24+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/4816b3e022613b6a7a2073dd292311b29d8e848887f4d0ed7bcf32140bce2e9f15e05d754c48c1575cbaf70fcae8cfec501725df15d6bb1c7ac1ac1c74b59489 -OpenBLAS.v0.3.24+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/6f4aa76cc544ce52da6aa094ee5a1aeb -OpenBLAS.v0.3.24+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/c3428c36e19e9bd89dd0eb809f2617b7f90cbfaadffe9c58bf16c319e5e36d07102f48bafeb7a787bb6a18655ba3688a29ccd4f3724bc9354915d7e55fb64d44 -OpenBLAS.v0.3.24+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/298a83c0550694b3b6c22f6dd460cc9d -OpenBLAS.v0.3.24+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/b8e5ac5304364c950a427384db13ed5c573e7b5e9d16bb7c354b7b5e46ca8e7471bc911a2cf9defbfc933fbfbff315949011b5596532b8863c551de6d543f434 -OpenBLAS.v0.3.24+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/2b584ae3aa496ff871cc735242e63cf4 -OpenBLAS.v0.3.24+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/ba0911f7f6f9f9a5823fa0e98778e4a7d2a9bbd5d674d552fefd20458743b60baf10a49e1ee65a3b934636229296d64b1eece55826f136824f9cae91b6a208c4 -OpenBLAS.v0.3.24+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/2ff249795f3d8796b99536a4bf11670c -OpenBLAS.v0.3.24+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/517c4a99933e439de7ede07376b6f40785406216489f1bd6f995e5d6e444c860df401807abba6dc04ff83dd701f32047fcd4d3fd01660601f27edbc545ea783e -OpenBLAS.v0.3.24+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/f3c3fab4d30655b8cd831511a307d4d3 -OpenBLAS.v0.3.24+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/06d47db95ec374b5238857c1ad595cee882441fd462ae6a030f1a1c4465b617228feb34fd80a97d8ca965bfc87f8d4a3a94a3654b2c0615ef4506fa827e8c155 -OpenBLAS.v0.3.24+0.i686-linux-gnu-libgfortran3.tar.gz/md5/316162aaa72c7b0938ea3f1e77b904a9 -OpenBLAS.v0.3.24+0.i686-linux-gnu-libgfortran3.tar.gz/sha512/b6ed223ab2ac32938d4283d7fd45a88565ef6d5b361ff28adfd7def4da8bc51895af26d3dff1104e472ae0681dac3af7ba4e75760e1c57e25b2a51599d8152c2 -OpenBLAS.v0.3.24+0.i686-linux-gnu-libgfortran4.tar.gz/md5/6dc4ceeb488810cd8e6e90b05743764f -OpenBLAS.v0.3.24+0.i686-linux-gnu-libgfortran4.tar.gz/sha512/c0a13c43667218fe1ffb4e19fc74ca5b25dbccb74fb57e193fc9343e8659731664e60c1c7a50c243a0825ba8747c97729f2a68bb36421ec6ba646c4f0c507219 -OpenBLAS.v0.3.24+0.i686-linux-gnu-libgfortran5.tar.gz/md5/124d38befd30a1fba95048612eb20911 -OpenBLAS.v0.3.24+0.i686-linux-gnu-libgfortran5.tar.gz/sha512/009d3217b990ec40729db4313526a4dcd977760fde4878c5fd420cce9649f367f348cc1d9f480057ff87b59545421935c7494f9fa7646206f8d114fede13051c -OpenBLAS.v0.3.24+0.i686-linux-musl-libgfortran3.tar.gz/md5/908d7ebace63754a7842e7d1dd338a43 -OpenBLAS.v0.3.24+0.i686-linux-musl-libgfortran3.tar.gz/sha512/8ab5364d7b3a445390b07dad5046d6e428fddf0eeb0b3b36f630b8a3e428fb8544d1a8fb493ba429cd3683dbe70e0a476333d322fe233cec356895b7a94c5670 -OpenBLAS.v0.3.24+0.i686-linux-musl-libgfortran4.tar.gz/md5/cf70732029bae0fc4f87713d75faad9c -OpenBLAS.v0.3.24+0.i686-linux-musl-libgfortran4.tar.gz/sha512/fea55b3aa2d6a8282345d710957401ad9d6350c57bf39d695173f3996c886a05d7f2672f3767397f6e0878c405117bd650923b7de5313fcd855b4846325b22e8 -OpenBLAS.v0.3.24+0.i686-linux-musl-libgfortran5.tar.gz/md5/cb4f6f8c7175c9d2159c213c37f71d86 -OpenBLAS.v0.3.24+0.i686-linux-musl-libgfortran5.tar.gz/sha512/0e92168e196d271105d8d267ed2329ab8b213375ca67539dd91a293576842ed13755f9e21536a48332d0c55e9323349f7c96817e5e80ced06986c8ad1e47a8fc -OpenBLAS.v0.3.24+0.i686-w64-mingw32-libgfortran3.tar.gz/md5/54b061bb2191fdbb39cb8103615d70bd -OpenBLAS.v0.3.24+0.i686-w64-mingw32-libgfortran3.tar.gz/sha512/b45fa1d1f5a8556f0a56f81e602457d98528d2b7b6dccbbad93cedcc3cecbe1a31d3f9543161ae801d0bdbb1413a9a70589ba6f221dc5d81fd3fc17e6e536f7f -OpenBLAS.v0.3.24+0.i686-w64-mingw32-libgfortran4.tar.gz/md5/8590055b4fc585317a2f36fbce1c2a50 -OpenBLAS.v0.3.24+0.i686-w64-mingw32-libgfortran4.tar.gz/sha512/47bc23c59e314606d45e252dedd7ec228c36bf25cf1cc19d64ca2b862407eede5410a6e7594881d8dc263aab0d140bfdde3082d57fa41ea038c286467d8354a3 -OpenBLAS.v0.3.24+0.i686-w64-mingw32-libgfortran5.tar.gz/md5/32120e6d84ff84bd859e080e4f124986 -OpenBLAS.v0.3.24+0.i686-w64-mingw32-libgfortran5.tar.gz/sha512/7ddada5781415099d6b1f198d27277d91a36f6850d957446ccfa7b8b2b38594b18de3ce5b1c7861c97e66897a7df1500531ac2c3ad12c597c7548def1db05fef -OpenBLAS.v0.3.24+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/d7dbb78769e588175612a939ce69c32b -OpenBLAS.v0.3.24+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/a1894b94dcf26d5da9da8ad119b3ecde8e6bf6d3a9d97286c1e405a059ec999a15e69ac6f497b170b485d64d6b324b4f38eb59984ec6b20d5aa6b881be819c4d -OpenBLAS.v0.3.24+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/acda753318cb352a2581c3ebc49d6d42 -OpenBLAS.v0.3.24+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/82bda1158b223fa899b7c9350b2abf0a7b693de75f928e48698ce9555998fc98d357b1b63ac8ca18d935636812141f0ceb58f51c2a1f48faec60cc8c0ca85126 -OpenBLAS.v0.3.24+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/abbd21e61746ec41b03c1efb5dfb884c -OpenBLAS.v0.3.24+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/7b284db00a9ab39b222caa9d4d6f3bcff3cc86560e275b7bac68c8c06c0cbe1c39226f381fba9fa90f52b8273e0c5436897c9ae0507482eb62bdeacc39ef3d35 -OpenBLAS.v0.3.24+0.x86_64-apple-darwin-libgfortran3.tar.gz/md5/36258636e985f73ab926fd4ee0e4cccb -OpenBLAS.v0.3.24+0.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/a53aa38afdb9ca13e14b3420f63c728ad83451a4c7bcda53a9944ff5488c6455367db6f8253983fbe174fd408513f2a48e7fec5d184900113165f2e82a812e7e -OpenBLAS.v0.3.24+0.x86_64-apple-darwin-libgfortran4.tar.gz/md5/780f66fc5707839a98c7bdad0432975b -OpenBLAS.v0.3.24+0.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/52957cdab20b7042e844468eb6fab822937636346807469decfdb6cfb43cd6d8cc479e3e58f0259d593f120ad5ad330006a470c390bfdafbf4865ee04d3fc401 -OpenBLAS.v0.3.24+0.x86_64-apple-darwin-libgfortran5.tar.gz/md5/83e26fedfa1f84a0a13c92e10b9c12f7 -OpenBLAS.v0.3.24+0.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/7a5eddc7486d46f2ef7b285d8d25ed17e0cf6a00c9c74784044b086b0063f0494bbdd28197a6f933f32345167e337fe62400f5d6b76d69f578aeb3a37ab56069 -OpenBLAS.v0.3.24+0.x86_64-linux-gnu-libgfortran3.tar.gz/md5/d4b83cbbd5a06eb84edd40e10cea9c78 -OpenBLAS.v0.3.24+0.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/28b86fbaad0adae2efe39f086f573e19b1f4a1db16f48144cb1ed4212013b82a5a22dabb75c7bb8c2b5f465ea0bfbf413af7e2fac75884baef1a2cfd7dfb0ceb -OpenBLAS.v0.3.24+0.x86_64-linux-gnu-libgfortran4.tar.gz/md5/2be815ebba026781801af79447f85383 -OpenBLAS.v0.3.24+0.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/2d86efa35a0b29e1b96d4063031e64c823440aca01739d2020856e825da22cf9044561f11ebf447674ced34c697c8b43207fe324f64c42c1c62f2edf869698e3 -OpenBLAS.v0.3.24+0.x86_64-linux-gnu-libgfortran5.tar.gz/md5/235767c8f001d56bb8c9e2b4694e94fe -OpenBLAS.v0.3.24+0.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/f326fed57de6017cf1de0e1d2806af9bc0b2897377611472f051634dd862fb3d964131e02f04d7b741a64c8e72241399716510e70132f802d046f54b1d170a35 -OpenBLAS.v0.3.24+0.x86_64-linux-musl-libgfortran3.tar.gz/md5/b202b8750e1f69099875a83be96683a1 -OpenBLAS.v0.3.24+0.x86_64-linux-musl-libgfortran3.tar.gz/sha512/c83a2c0a977b3afbc6fbe408b07888750a89e8dd66e65e1563d609296214b5114cf0b97009e4dea65a2579377d14ff1ef281c20dc327154d8635c23d50261263 -OpenBLAS.v0.3.24+0.x86_64-linux-musl-libgfortran4.tar.gz/md5/63cf99543c8c7d741808775d0e2b133c -OpenBLAS.v0.3.24+0.x86_64-linux-musl-libgfortran4.tar.gz/sha512/5a850adffb7cc1026b81c1780e3ff97875b29e5f794a33e9d1b3feb41d45eb25c9867c4c9e1699d0d78884ed93b32fb90d9ae8b48000939af7ecbe0d53793ac5 -OpenBLAS.v0.3.24+0.x86_64-linux-musl-libgfortran5.tar.gz/md5/2c34974e3d5ae02dcdbb8c091dd39e2f -OpenBLAS.v0.3.24+0.x86_64-linux-musl-libgfortran5.tar.gz/sha512/ec27f0891b9d56fe0ab86811c7a3043a6bee6518952e6ec58d00313cb6de858c8eaa7918692876e3bc8a0e3aac5f5ec563f612af9eacca47ebbdd4bea1153a0e -OpenBLAS.v0.3.24+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/85cbc32584528f6e5f1573eee0815f41 -OpenBLAS.v0.3.24+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/e7d48088e65b74a0e5c4a0714c97ce379ba4edc1f0f9cdf733a41da1a51fcdb2231589ac92d71a28605526c1408fb3eec7bb1f1d914e67cacd37425ce22c450e -OpenBLAS.v0.3.24+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/ad6c269fa00c998ea49311656afbf8cd -OpenBLAS.v0.3.24+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/604067b4b827395cdb7802f88a850e023acfee7e642669567eda0d1ed9612bdf968af4f5e43a25e6b3a213fd118bf3a49cd7a02dc2ef019ee853e31bf4316664 -OpenBLAS.v0.3.24+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/e5f233a4f87cec0c18b7a47073d9ea4d -OpenBLAS.v0.3.24+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/04fda9400f11948ec4f4c088096dd2971b4fbe2c513c755515bee757903f50c3246a5076f1fa095aae26092b0a76847ee1db4c972e235cd154af188600c387d8 -OpenBLAS.v0.3.24+0.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/0134d022448a5cc11f0b92b37600f7ce -OpenBLAS.v0.3.24+0.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/8da81604e96bb917d6300242709056ddd9c07232a0e7a1862cb1972e8ec631af093e3ddad8538e352295de78c19387dcb9ad7eb163c40fe715a8129855fe1dfb -OpenBLAS.v0.3.24+0.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/7f79751f5fb9713152410e665061872d -OpenBLAS.v0.3.24+0.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/50d836e6aac10e3bd181b87946917d77aacfc74bba3d973c12e94dcdac946bcc8d58fb079838784812c27c581c46fb5485bc6e0a192287fc11df46fb7674ec17 -OpenBLAS.v0.3.24+0.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/8b13dbb97a45754c418994d522eee197 -OpenBLAS.v0.3.24+0.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/1883a4b88cede205def3c29d82773379035edc3b04b8738197137a00bcf95726ca689866f5fc1a25914ea09e3fa2775435f8b4fff95738f7fda3857bcd44653d +OpenBLAS.v0.3.25+0.aarch64-apple-darwin-libgfortran5.tar.gz/md5/1acfa1d2dbebaf274812e36b59cdd0f7 +OpenBLAS.v0.3.25+0.aarch64-apple-darwin-libgfortran5.tar.gz/sha512/4cf10b1e5a791dcaea814f4f34b328499405b39c68ed652569e3a070f14c51b935e18d04bc10440b19967a40d21d7ef4c061817a5e6abf1403613e25cd45ef2f +OpenBLAS.v0.3.25+0.aarch64-linux-gnu-libgfortran3.tar.gz/md5/bf8cb41b65e7b60015b67727adca5c50 +OpenBLAS.v0.3.25+0.aarch64-linux-gnu-libgfortran3.tar.gz/sha512/710646a8e7bcf1cb0320777006d328b20f90463dac29f37b0c0472d22a8e00e49bd8aad1a0e71772279bf452e5daef9a6e9b51a5ea2af806fcdcae2999039108 +OpenBLAS.v0.3.25+0.aarch64-linux-gnu-libgfortran4.tar.gz/md5/60ff3026347c9a53f88cc90fd0f29950 +OpenBLAS.v0.3.25+0.aarch64-linux-gnu-libgfortran4.tar.gz/sha512/1dd600ac2b315a051c84529e4653aad77d43c228538ebe94c36b6f68ed14a15c5d7cfad17b49eeba4ce0ec857d7199b767c241a5e9863f7c60d5f516020a8d20 +OpenBLAS.v0.3.25+0.aarch64-linux-gnu-libgfortran5.tar.gz/md5/5b57d42de0fa82c123f792adb6835195 +OpenBLAS.v0.3.25+0.aarch64-linux-gnu-libgfortran5.tar.gz/sha512/e534b6c26100a8c841a1a81dee75cd2ae8c34f5bc3ddcd6b5cc9afdc21d46419459046371fa448c5b9c6d02eeccfb64d10c887c53b52c4744e809c50d3b7d41d +OpenBLAS.v0.3.25+0.aarch64-linux-musl-libgfortran3.tar.gz/md5/d2417bfa94754ca621c875b926dadd66 +OpenBLAS.v0.3.25+0.aarch64-linux-musl-libgfortran3.tar.gz/sha512/249b95d0502f6f82ac7151a38380c8a6cf9314e856a9f545cc086b14eaa3f7804ff24eb8b3430144c3add912aeec8fbfaee40fe30e4245fcafec3e9df3a73484 +OpenBLAS.v0.3.25+0.aarch64-linux-musl-libgfortran4.tar.gz/md5/d68bee54d083f93fa9a96ccaa0b10d6c +OpenBLAS.v0.3.25+0.aarch64-linux-musl-libgfortran4.tar.gz/sha512/909d9d4a9581899c564f6a42f325e50ba7b82f1bf7d2912a9d47cc88e8e13ddda6dff8a40680720f8d0a4a1973f6b6c48ffd0ec51d0323cb08cc73268386a61c +OpenBLAS.v0.3.25+0.aarch64-linux-musl-libgfortran5.tar.gz/md5/f103f1d84b9bbac3d73d70efa79f8f8a +OpenBLAS.v0.3.25+0.aarch64-linux-musl-libgfortran5.tar.gz/sha512/ad1bbfbfce09e7a7eb6d33e87f432530a3347d68b033d877029f8da9e290b129b2ab887eac510b27c9a1d9506679fb3e7e554dc0accfa05c5c950ae40c1501e6 +OpenBLAS.v0.3.25+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/md5/8d92bf601f216b0512e1eb9bb0f4689e +OpenBLAS.v0.3.25+0.armv6l-linux-gnueabihf-libgfortran3.tar.gz/sha512/8abd7f22db9d739c90df3c512ec4958989135e894d45ee91bf364e4c4a07b3dd8640351e596ef4b62f314bcf0ed22a35036499756611f5db1f98bdf1a3130033 +OpenBLAS.v0.3.25+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/md5/c56a5861637867ca4d0a88e2d71e0301 +OpenBLAS.v0.3.25+0.armv6l-linux-gnueabihf-libgfortran4.tar.gz/sha512/301a30c2d5c1a6355516a0520caadf83f1e121e95480bbaf3c39baaedfb10902adea826b289f0e5411ec9bb12eeb0a63fb3630b1ef3780304689a2d2bb97add8 +OpenBLAS.v0.3.25+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/md5/6c8f6582db03396ed30b41deeb706877 +OpenBLAS.v0.3.25+0.armv6l-linux-gnueabihf-libgfortran5.tar.gz/sha512/22e2c5ba0a9d6fe1dea222d24fe5da9b208525656185945a8ef90e201592e8c2692571bde7824651dde50850b0a81f25b5dfd8d4283a097d2f6193507128b587 +OpenBLAS.v0.3.25+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/md5/546e7ec9501a6687784c9285b70cf2d4 +OpenBLAS.v0.3.25+0.armv6l-linux-musleabihf-libgfortran3.tar.gz/sha512/8d306c18e810a22b637c5fa2f35b791b44aec67198b1a2632ff245cf5519cf138d2074eea84e85a348075b77b90eb5ff6342a920e66e508501eefd6c8cabfb6b +OpenBLAS.v0.3.25+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/md5/494c5b1a2df492723aedc05301958606 +OpenBLAS.v0.3.25+0.armv6l-linux-musleabihf-libgfortran4.tar.gz/sha512/01d413b45df28b6c262d2e1c1859a499d10e24cbf32c8954b5fe5f66c6cbc13e113e7834db304beee02a31228f2c5c1bb83e83455551b63cc972cf0d604db843 +OpenBLAS.v0.3.25+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/md5/0e14407a456d8354d4759140b8c2dcab +OpenBLAS.v0.3.25+0.armv6l-linux-musleabihf-libgfortran5.tar.gz/sha512/38b045a701d0dc1cc161877944a6db5154b36437d4122460727f489b05b902a7766e760ace5331a4056f804a8008fb3e123c905adae45839d1eeeadf1f209116 +OpenBLAS.v0.3.25+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5/8d92bf601f216b0512e1eb9bb0f4689e +OpenBLAS.v0.3.25+0.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512/8abd7f22db9d739c90df3c512ec4958989135e894d45ee91bf364e4c4a07b3dd8640351e596ef4b62f314bcf0ed22a35036499756611f5db1f98bdf1a3130033 +OpenBLAS.v0.3.25+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5/c56a5861637867ca4d0a88e2d71e0301 +OpenBLAS.v0.3.25+0.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512/301a30c2d5c1a6355516a0520caadf83f1e121e95480bbaf3c39baaedfb10902adea826b289f0e5411ec9bb12eeb0a63fb3630b1ef3780304689a2d2bb97add8 +OpenBLAS.v0.3.25+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5/6c8f6582db03396ed30b41deeb706877 +OpenBLAS.v0.3.25+0.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512/22e2c5ba0a9d6fe1dea222d24fe5da9b208525656185945a8ef90e201592e8c2692571bde7824651dde50850b0a81f25b5dfd8d4283a097d2f6193507128b587 +OpenBLAS.v0.3.25+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5/546e7ec9501a6687784c9285b70cf2d4 +OpenBLAS.v0.3.25+0.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512/8d306c18e810a22b637c5fa2f35b791b44aec67198b1a2632ff245cf5519cf138d2074eea84e85a348075b77b90eb5ff6342a920e66e508501eefd6c8cabfb6b +OpenBLAS.v0.3.25+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5/494c5b1a2df492723aedc05301958606 +OpenBLAS.v0.3.25+0.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512/01d413b45df28b6c262d2e1c1859a499d10e24cbf32c8954b5fe5f66c6cbc13e113e7834db304beee02a31228f2c5c1bb83e83455551b63cc972cf0d604db843 +OpenBLAS.v0.3.25+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5/0e14407a456d8354d4759140b8c2dcab +OpenBLAS.v0.3.25+0.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512/38b045a701d0dc1cc161877944a6db5154b36437d4122460727f489b05b902a7766e760ace5331a4056f804a8008fb3e123c905adae45839d1eeeadf1f209116 +OpenBLAS.v0.3.25+0.i686-linux-gnu-libgfortran3.tar.gz/md5/fb62eeaa26adad79f27a4871abe4c8f2 +OpenBLAS.v0.3.25+0.i686-linux-gnu-libgfortran3.tar.gz/sha512/1712755c80f7d48052fdc2ffd254b745ddd5d9a90bcf2f25532448d258317664895efdd8b92054a17271349e6371449b3dbf1cbe44d2bab2b2a11e77a0f8207e +OpenBLAS.v0.3.25+0.i686-linux-gnu-libgfortran4.tar.gz/md5/bbb096eb2294179d4bbac42fa08dfa04 +OpenBLAS.v0.3.25+0.i686-linux-gnu-libgfortran4.tar.gz/sha512/6de33c7a98077c82d2d730b2a3046f9d84cf779c46527e70b4e862c2b51b7487d3b5d66eb1693235ab19bb9b254f85677707bfe496ca96e8b2f9840413d52d86 +OpenBLAS.v0.3.25+0.i686-linux-gnu-libgfortran5.tar.gz/md5/25e6e7f428ca9d817ce06dfd1ce440d7 +OpenBLAS.v0.3.25+0.i686-linux-gnu-libgfortran5.tar.gz/sha512/3d7d12c9de5d435fd7d9b8919c6b511d3a825e02d9d303a2222f16d13f7c93b620e6099fb290cd36648f51952c187b9100753e107530be2d40c1e006f44ac453 +OpenBLAS.v0.3.25+0.i686-linux-musl-libgfortran3.tar.gz/md5/0f09043138753743b33bdc1598ea2f98 +OpenBLAS.v0.3.25+0.i686-linux-musl-libgfortran3.tar.gz/sha512/a43510a70e63b534812fb0213af75743c0c78f8d153e61cfa0b3ec0b5a51172db3d3ca5b40429ccfd2fe99eb730db661566d61f825f0c97d079f75933ab17385 +OpenBLAS.v0.3.25+0.i686-linux-musl-libgfortran4.tar.gz/md5/b8c78d226bf548306cb0de09c296fcfd +OpenBLAS.v0.3.25+0.i686-linux-musl-libgfortran4.tar.gz/sha512/165b85271b686da7daad21e2d3be683efcf8beb3208d2ba73e753cdc8405236c673ceb5713f84131a5b44045721c98de70242f600f7823c1597d38156f692ca6 +OpenBLAS.v0.3.25+0.i686-linux-musl-libgfortran5.tar.gz/md5/1f508ddbb3ef7aff7880b34a8caeb032 +OpenBLAS.v0.3.25+0.i686-linux-musl-libgfortran5.tar.gz/sha512/9f603451d957483b5be45dd8d705f0a45f0fa3fa4e32d9431a15b7dc986fbce200d7d8966717564c588ee3a367c949342b63f03b81de387bb90c691cb3b0d2a5 +OpenBLAS.v0.3.25+0.i686-w64-mingw32-libgfortran3.tar.gz/md5/e85605ee1a4d33d43a2fb2e516eaa99f +OpenBLAS.v0.3.25+0.i686-w64-mingw32-libgfortran3.tar.gz/sha512/98512575e60b34df6fe0a2d3f4a0a57d8f9ef1ef7a65b014427776104633acffbbd48bdb8aecdeb54dca1bdee39e1efd6f677dd68028911f2dfbe55f1180f054 +OpenBLAS.v0.3.25+0.i686-w64-mingw32-libgfortran4.tar.gz/md5/673c70c7f29dab76a154b5171f6112a2 +OpenBLAS.v0.3.25+0.i686-w64-mingw32-libgfortran4.tar.gz/sha512/4ec6313ddbdcf3a98d48bc8510d5c120fb3232433738a28e9ccd9beec1e2a0ef401c625b0b97702cd7be7fd6061e2f8ed48f35ae36b46495d952053e0accaaae +OpenBLAS.v0.3.25+0.i686-w64-mingw32-libgfortran5.tar.gz/md5/7242fefbdb29d0ef5f08f39590fb204d +OpenBLAS.v0.3.25+0.i686-w64-mingw32-libgfortran5.tar.gz/sha512/be253db33b2345388550d3e82b8cea4e9416874df5e01b24e96796faace87440178a10e93a47d228b4e31e21754de8b42a067e69aba8ab6267a19e7197f3d54c +OpenBLAS.v0.3.25+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5/1fd83d8f353195c7ae1a1fa9ea266171 +OpenBLAS.v0.3.25+0.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512/0cb87fb1dbe6af1f1aef1bb3e346078a9de39d2ef33a51c99c0fafe67cdb494b1aae567429a9bec8fdfd41c651afe63815b7ecec20ea95fb3d0bedf06eb78188 +OpenBLAS.v0.3.25+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5/f2a4141dea91feef792036dd149e552d +OpenBLAS.v0.3.25+0.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512/58dc721598f36380c65f34cb5d3efeb77bb03605afb82bb08689674893cb399af34dde47002105f072422d031df8f629fbfb4f463a9900c3b09fd6e98b3d007b +OpenBLAS.v0.3.25+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5/f472322bd6f463f335d7b9c5b2ae69be +OpenBLAS.v0.3.25+0.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512/b9530287d7972b599535b4d78a2540e00a49664660c077cf7c50ebcc86e79fb7fb2e6ddd2d1f3e80c1f4f6d6094c4f4a0641e3112994f7b770ac14868c704ec0 +OpenBLAS.v0.3.25+0.x86_64-apple-darwin-libgfortran3.tar.gz/md5/28c498d970d4c659d47981f915448a2e +OpenBLAS.v0.3.25+0.x86_64-apple-darwin-libgfortran3.tar.gz/sha512/f5a5d9d98013b39b26134910ba9e2cbd23caf1bfca1cb9867e972753081cafd90edfa5089747d94b87368aa4cecffcb40314326b82a47ddb4eafa3a63437482e +OpenBLAS.v0.3.25+0.x86_64-apple-darwin-libgfortran4.tar.gz/md5/2888ac4b4ad4f693b652c0054187360b +OpenBLAS.v0.3.25+0.x86_64-apple-darwin-libgfortran4.tar.gz/sha512/0292f8d1ecbc5b357c1b9891944831cf652d1937d62a4d7486dc5c4dea3a9fa54bd1f0d76ba5553fe009d09e68edb79ee1310ac2b296ba61e33ea1ed794f4713 +OpenBLAS.v0.3.25+0.x86_64-apple-darwin-libgfortran5.tar.gz/md5/f3868395464c24b87fe543a630799cb8 +OpenBLAS.v0.3.25+0.x86_64-apple-darwin-libgfortran5.tar.gz/sha512/4790f559458a301cb4ca9ce11bb2004b2bc0a72f7cf88c7dc45572f7c56adb154f7ab76a72e463ac0871019248e4b6ed4129356ab036bc71bee3700235f9f805 +OpenBLAS.v0.3.25+0.x86_64-linux-gnu-libgfortran3.tar.gz/md5/83fde9ea432e7c87413ebedea4062070 +OpenBLAS.v0.3.25+0.x86_64-linux-gnu-libgfortran3.tar.gz/sha512/00428ed95ef6dd1191911e768d929d9a511f34ad1f098c6311df7f2176fb82d67e763326da95fc390ec05854a7a1482c3e2e4efd4b1975653c45a825b4b3ef68 +OpenBLAS.v0.3.25+0.x86_64-linux-gnu-libgfortran4.tar.gz/md5/415f468e44c3762451b39596eb6dd45d +OpenBLAS.v0.3.25+0.x86_64-linux-gnu-libgfortran4.tar.gz/sha512/bda9c30547cc219fdc00d3854ad02708d225a46850c7a95b786b527722e75185787ab2353185007cb9f692a4cd5a3c38d64018a46ee4d400df9d4612371a2a9b +OpenBLAS.v0.3.25+0.x86_64-linux-gnu-libgfortran5.tar.gz/md5/7858a2a1c13b6707f6bee3b021811728 +OpenBLAS.v0.3.25+0.x86_64-linux-gnu-libgfortran5.tar.gz/sha512/0a0e4995652e9459dd54325ee92262261244ace5f065f293459ad91433345efbbdf1e51996ae2629968498a8fc34b0fb64fadde7bc36498a48eeefafde987d5d +OpenBLAS.v0.3.25+0.x86_64-linux-musl-libgfortran3.tar.gz/md5/30f5deef5223ee198acb8c213a7c89b1 +OpenBLAS.v0.3.25+0.x86_64-linux-musl-libgfortran3.tar.gz/sha512/a933e33e6070475450312d0608626811bae98e129adb30d84cf5a3c7d525217e805b0ffb88490931a927b1fda1c9479af953282e47af1f469f993d4d2150da95 +OpenBLAS.v0.3.25+0.x86_64-linux-musl-libgfortran4.tar.gz/md5/86fd8946da2bd4fb97f54c8df5883d5e +OpenBLAS.v0.3.25+0.x86_64-linux-musl-libgfortran4.tar.gz/sha512/d61187e632a0d49c89854d441b9b42fcd9dc7517f4f38514cd17f599c2f9ad3600054bf8457d0e9fb766fe9c7d0c248cd3e5383be2600ac2c538f2d524a8e1de +OpenBLAS.v0.3.25+0.x86_64-linux-musl-libgfortran5.tar.gz/md5/d95539fa91c80db375310ed09d191379 +OpenBLAS.v0.3.25+0.x86_64-linux-musl-libgfortran5.tar.gz/sha512/ab786856a1c4ab47808e9ea2167c6ae42e69d6a11e539b7771464bbc0c7bb1d5482ed2c3c0154fae37bfb1579103e862637675e4d81726d4e3ed633cd7548cd9 +OpenBLAS.v0.3.25+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/md5/09ea7329e39dac562e45f00e6b135ad6 +OpenBLAS.v0.3.25+0.x86_64-unknown-freebsd-libgfortran3.tar.gz/sha512/c0b8b3b6f7c9a326b857e218014bc232165122865c18811bc5c9af28b54c494a4a0ed139101cdf30ff5e8b03f9e8bb4ce32865ab5ba4e5251f841a2bf612eb86 +OpenBLAS.v0.3.25+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/md5/f874721e0ee45094c62cf4a006c5309c +OpenBLAS.v0.3.25+0.x86_64-unknown-freebsd-libgfortran4.tar.gz/sha512/03d57202ef4209f23f83a5faef01c772c2c6b04085d8448b835eec793b50b9f2f13dec2ba1d0a7ac55f6be7df8231a04163ac6d1fccb91976e965fd10bc0e3f1 +OpenBLAS.v0.3.25+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/md5/69de0e673292ccaf3294e785a96cc22b +OpenBLAS.v0.3.25+0.x86_64-unknown-freebsd-libgfortran5.tar.gz/sha512/9ff1dd63a62d5f0d771833a7872d0abdf6e2307aabf57d7b5f1469f75aeda99525beb5a4c2f311cef0b8af3905daab321435b613c9e046a7d7c5c30e2548ba0b +OpenBLAS.v0.3.25+0.x86_64-w64-mingw32-libgfortran3.tar.gz/md5/2cd93681be2f2f781285ca7dbef7eba1 +OpenBLAS.v0.3.25+0.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512/3163b20884e9be0a6d99735c400ca58f52f097fbbcb396be0ce9c29b3d4adebe5cee4ddb91f095a692e1f812c5558690a603bd455c5576c374e7c49e6cfaac7b +OpenBLAS.v0.3.25+0.x86_64-w64-mingw32-libgfortran4.tar.gz/md5/751aff51cf7674bcb68fe3bb94137603 +OpenBLAS.v0.3.25+0.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512/b5fa7477f9033f5e55214fb4e0bd3bf923e2c580c6582b7241739237dc50d269448459efebf75da82415ceb2f547033210f118d9b8c9ab2585fc792253331309 +OpenBLAS.v0.3.25+0.x86_64-w64-mingw32-libgfortran5.tar.gz/md5/de458200baed33ba568f8222a9b72865 +OpenBLAS.v0.3.25+0.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512/d6acccb3a10e3eb01be2aacb4d77f084f022fef3ca3bbf05530cae5c13224b4ab12b62e8e8a3ce1ef6ab0b2cca9e88c687794a8620fe0af1c478323976268d5a openblas-9f815cf1bf16b4e64d4aee681b33558fc090b62a.tar.gz/md5/d890367a6452aa209f396359fb770bc7 openblas-9f815cf1bf16b4e64d4aee681b33558fc090b62a.tar.gz/sha512/0627960fc2582e08fd039c77aab1f4105f18edb1beba52176a791813bb3f1ebb04f52d174814e156826c45c6adf5f3c1d2242e81fdb520a7fff2170cb1277228 diff --git a/deps/nghttp2.version b/deps/nghttp2.version index 200e08bf4bfd9e..680be055e44432 100644 --- a/deps/nghttp2.version +++ b/deps/nghttp2.version @@ -3,4 +3,4 @@ NGHTTP2_JLL_NAME := nghttp2 ## source build -NGHTTP2_VER := 1.52.0 +NGHTTP2_VER := 1.58.0 diff --git a/deps/openblas.version b/deps/openblas.version index 9790ad1718e78a..7b16df95c9c7f8 100644 --- a/deps/openblas.version +++ b/deps/openblas.version @@ -3,9 +3,9 @@ OPENBLAS_JLL_NAME := OpenBLAS ## source build -OPENBLAS_VER := 0.3.24 -OPENBLAS_BRANCH=v0.3.24 -OPENBLAS_SHA1=9f815cf1bf16b4e64d4aee681b33558fc090b62a +OPENBLAS_VER := 0.3.25 +OPENBLAS_BRANCH=v0.3.25 +OPENBLAS_SHA1=5e1a429eab44731b6668b8f6043c1ea951b0a80b # LAPACK, source-only LAPACK_VER := 3.9.0 diff --git a/doc/make.jl b/doc/make.jl index 78604a5fd3c754..da0fb8242913bf 100644 --- a/doc/make.jl +++ b/doc/make.jl @@ -128,6 +128,7 @@ BaseDocs = [ StdlibDocs = [stdlib.targetfile for stdlib in STDLIB_DOCS] Tutorials = [ + "tutorials/creating-packages.md", "tutorials/profile.md", "tutorials/external.md", ] @@ -163,6 +164,7 @@ DevDocs = [ "devdocs/gc.md", "devdocs/jit.md", "devdocs/builtins.md", + "devdocs/precompile_hang.md", ], "Developing/debugging Julia's C code" => [ "devdocs/backtraces.md", diff --git a/doc/src/base/reflection.md b/doc/src/base/reflection.md index e5c70175e5f9a1..d44bc474abbd29 100644 --- a/doc/src/base/reflection.md +++ b/doc/src/base/reflection.md @@ -97,7 +97,7 @@ Finally, the [`Meta.lower`](@ref) function gives the `lowered` form of any expre particular interest for understanding how language constructs map to primitive operations such as assignments, branches, and calls: -```jldoctest +```jldoctest; setup = (using Base: +, sin) julia> Meta.lower(@__MODULE__, :( [1+2, sin(0.5)] )) :($(Expr(:thunk, CodeInfo( @ none within `top-level scope` diff --git a/doc/src/devdocs/img/precompilation_hang.png b/doc/src/devdocs/img/precompilation_hang.png new file mode 100644 index 00000000000000..d076b7697f2712 Binary files /dev/null and b/doc/src/devdocs/img/precompilation_hang.png differ diff --git a/doc/src/devdocs/precompile_hang.md b/doc/src/devdocs/precompile_hang.md new file mode 100644 index 00000000000000..0de9c99792b649 --- /dev/null +++ b/doc/src/devdocs/precompile_hang.md @@ -0,0 +1,98 @@ +# Fixing precompilation hangs due to open tasks or IO + +On Julia 1.10 or higher, you might see the following message: + +![Screenshot of precompilation hang](./img/precompilation_hang.png) + +This may repeat. If it continues to repeat with no hints that it will +resolve itself, you may have a "precompilation hang" that requires +fixing. Even if it's transient, you might prefer to resolve it so that +users will not be bothered by this warning. This page walks you +through how to analyze and fix such issues. + +If you follow the advice and hit `Ctrl-C`, you might see + +``` +^C Interrupted: Exiting precompilation... + + 1 dependency had warnings during precompilation: +┌ Test1 [ac89d554-e2ba-40bc-bc5c-de68b658c982] +│ [pid 2745] waiting for IO to finish: +│ Handle type uv_handle_t->data +│ timer 0x55580decd1e0->0x7f94c3a4c340 +``` + +This message conveys two key pieces of information: + +- the hang is occurring during precompilation of `Test1`, a dependency of `Test2` (the package we were trying to load with `using Test2`) +- during precompilation of `Test1`, Julia created a `Timer` object (use `?Timer` if you're unfamiliar with Timers) which is still open; until that closes, the process is hung + +If this is enough of a hint for you to figure out how `timer = Timer(args...)` is being created, one good solution is to add `wait(timer)` if `timer` eventually finishes on its own, or `close(timer)` if you need to force-close it, before the final `end` of the module. + +However, there are cases that may not be that straightforward. Usually the best option is to start by determining whether the hang is due to code in Test1 or whether it is due to one of Test1's dependencies: + +- Option 1: `Pkg.add("Aqua")` and use [`Aqua.test_persistent_tasks`](https://juliatesting.github.io/Aqua.jl/dev/#Aqua.test_persistent_tasks-Tuple{Base.PkgId}). This should help you identify which package is causing the problem, after which the instructions [below](@ref pchang_fix) should be followed. If needed, you can create a `PkgId` as `Base.PkgId(UUID("..."), "Test1")`, where `...` comes from the `uuid` entry in `Test1/Project.toml`. +- Option 2: manually diagnose the source of the hang. + +To manually diagnose: + +1. `Pkg.develop("Test1")` +2. Comment out all the code `include`d or defined in `Test1`, *except* the `using/import` statements. +3. Try `using Test2` (or even `using Test1` assuming that hangs too) again + +Now we arrive at a fork in the road: either + +- the hang persists, indicating it is [due to one of your dependencies](@ref pchang_deps) +- the hang disappears, indicating that it is [due to something in your code](@ref pchang_fix). + +## [Diagnosing and fixing hangs due to a package dependency](@id pchang_deps) + +Use a binary search to identify the problematic dependency: start by commenting out half your dependencies, then when you isolate which half is responsible comment out half of that half, etc. (You don't have to remove them from the project, just comment out the `using`/`import` statements.) + +Once you've identified a suspect (here we'll call it `ThePackageYouThinkIsCausingTheProblem`), first try precompiling that package. If it also hangs during precompilation, continue chasing the problem backwards. + +However, most likely `ThePackageYouThinkIsCausingTheProblem` will precompile fine. This suggests it's in the function `ThePackageYouThinkIsCausingTheProblem.__init__`, which does not run during precompilation of `ThePackageYouThinkIsCausingTheProblem` but *does* in any package that loads `ThePackageYouThinkIsCausingTheProblem`. To test this theory, set up a minimal working example (MWE), something like + +```julia +(@v1.10) pkg> generate MWE + Generating project MWE: + MWE\Project.toml + MWE\src\MWE.jl +``` + +where the source code of `MWE.jl` is + +```julia +module MWE +using ThePackageYouThinkIsCausingTheProblem +end +``` + +and you've added `ThePackageYouThinkIsCausingTheProblem` to MWE's dependencies. + +If that MWE reproduces the hang, you've found your culprit: +`ThePackageYouThinkIsCausingTheProblem.__init__` must be creating the `Timer` object. If the timer object can be safely `close`d, that's a good option. Otherwise, the most common solution is to avoid creating the timer while *any* package is being precompiled: add + +```julia +ccall(:jl_generating_output, Cint, ()) == 1 && return nothing +``` + +as the first line of `ThePackageYouThinkIsCausingTheProblem.__init__`, and it will avoid doing any initialization in any Julia process whose purpose is to precompile packages. + +## [Fixing package code to avoid hangs](@id pchang_fix) + +Search your package for suggestive words (here like "Timer") and see if you can identify where the problem is being created. Note that a method *definition* like + +```julia +maketimer() = Timer(timer -> println("hi"), 0; interval=1) +``` + +is not problematic in and of itself: it can cause this problem only if `maketimer` gets called while the module is being defined. This might be happening from a top-level statement such as + +```julia +const GLOBAL_TIMER = maketimer() +``` + +or it might conceivably occur in a [precompile workload](https://github.com/JuliaLang/PrecompileTools.jl). + +If you struggle to identify the causative lines, then consider doing a binary search: comment out sections of your package (or `include` lines to omit entire files) until you've reduced the problem in scope. diff --git a/doc/src/devdocs/ssair.md b/doc/src/devdocs/ssair.md index d00f1ebb809c35..4375012c9f5800 100644 --- a/doc/src/devdocs/ssair.md +++ b/doc/src/devdocs/ssair.md @@ -225,7 +225,7 @@ Instead, we do the following: - RAUW style operations are performed by setting the corresponding statement index to the replacement value. - Statements are erased by setting the corresponding statement to `nothing` (this is essentially just a special-case - convention of the above. + convention of the above). - If there are any uses of the statement being erased, they will be set to `nothing`. There is a `compact!` function that compacts the above data structure by performing the insertion of nodes in the appropriate place, trivial copy propagation, and renaming of uses to any changed SSA values. However, the clever part diff --git a/doc/src/manual/style-guide.md b/doc/src/manual/style-guide.md index f892d6284fb108..19b4908927187c 100644 --- a/doc/src/manual/style-guide.md +++ b/doc/src/manual/style-guide.md @@ -346,7 +346,7 @@ This would provide custom showing of vectors with a specific new element type. W this should be avoided. The trouble is that users will expect a well-known type like `Vector()` to behave in a certain way, and overly customizing its behavior can make it harder to work with. -## Avoid type piracy +## [Avoid type piracy](@id avoid-type-piracy) "Type piracy" refers to the practice of extending or redefining methods in Base or other packages on types that you have not defined. In extreme cases, you can crash Julia diff --git a/doc/src/tutorials/creating-packages.md b/doc/src/tutorials/creating-packages.md new file mode 100644 index 00000000000000..a01141a2f6e487 --- /dev/null +++ b/doc/src/tutorials/creating-packages.md @@ -0,0 +1,634 @@ +# [Creating Packages](@id creating-packages-tutorial) + +## Generating files for a package + +!!! note + The [PkgTemplates](https://github.com/invenia/PkgTemplates.jl) package offers an easy, repeatable, and + customizable way to generate the files for a new package. It can also generate files needed for Documentation, CI, etc. + We recommend that you use PkgTemplates for creating + new packages instead of using the minimal `pkg> generate` functionality described below. + +To generate the bare minimum files for a new package, use `pkg> generate`. + +```julia-repl +(@v1.8) pkg> generate HelloWorld +``` + +This creates a new project `HelloWorld` in a subdirectory by the same name, with the following files (visualized with the external [`tree` command](https://linux.die.net/man/1/tree)): + +```julia-repl +shell> tree HelloWorld/ +HelloWorld/ +├── Project.toml +└── src + └── HelloWorld.jl + +2 directories, 2 files +``` + +The `Project.toml` file contains the name of the package, its unique UUID, its version, the authors and potential dependencies: + +```toml +name = "HelloWorld" +uuid = "b4cd1eb8-1e24-11e8-3319-93036a3eb9f3" +version = "0.1.0" +authors = ["Some One "] + +[deps] +``` + +The content of `src/HelloWorld.jl` is: + +```julia +module HelloWorld + +greet() = print("Hello World!") + +end # module +``` + +We can now activate the project by using the path to the directory where it is installed, and load the package: + +```julia-repl +pkg> activate ./HelloWorld + +julia> import HelloWorld + +julia> HelloWorld.greet() +Hello World! +``` + +For the rest of the tutorial we enter inside the directory of the project, for convenience: + +```julia-repl +julia> cd("HelloWorld") +``` + +## Adding dependencies to the project + +Let’s say we want to use the standard library package `Random` and the registered package `JSON` in our project. +We simply `add` these packages (note how the prompt now shows the name of the newly generated project, +since we `activate`d it): + +```julia-repl +(HelloWorld) pkg> add Random JSON + Resolving package versions... + Updating `~/HelloWorld/Project.toml` + [682c06a0] + JSON v0.21.3 + [9a3f8284] + Random + Updating `~/HelloWorld/Manifest.toml` + [682c06a0] + JSON v0.21.3 + [69de0a69] + Parsers v2.4.0 + [ade2ca70] + Dates + ... +``` + +Both `Random` and `JSON` got added to the project’s `Project.toml` file, and the resulting dependencies got added to the `Manifest.toml` file. +The resolver has installed each package with the highest possible version, while still respecting the compatibility that each package enforces on its dependencies. + +We can now use both `Random` and `JSON` in our project. Changing `src/HelloWorld.jl` to + +```julia +module HelloWorld + +import Random +import JSON + +greet() = print("Hello World!") +greet_alien() = print("Hello ", Random.randstring(8)) + +end # module +``` + +and reloading the package, the new `greet_alien` function that uses `Random` can be called: + +```julia-repl +julia> HelloWorld.greet_alien() +Hello aT157rHV +``` + +## Defining a public API + +If you want your package to be useful to other packages and you want folks to be able to +easily update to newer version of your package when they come out, it is important to +document what behavior will stay consistent across updates. + +Unless you note otherwise, the public API of your package is defined as all the behavior you +describe about public symbols. A public symbol is a symbol that is exported from your +package with the `export` keyword or marked as public with the `public` keyword. When you +change the behavior of something that was previously public so that the new +version no longer conforms to the specifications provided in the old version, you should +adjust your package version number according to [Julia's variant on SemVer](#Version-specifier-format). +If you would like to include a symbol in your public API without exporting it into the +global namespace of folks who call `using YourPackage`, you should mark that symbol as +public with `public that_symbol`. Symbols marked as public with the `public` keyword are +just as public as those marked as public with the `export` keyword, but when folks call +`using YourPackage`, they will still have to qualify access to those symbols with +`YourPackage.that_symbol`. + +Let's say we would like our `greet` function to be part of the public API, but not the +`greet_alien` function. We could the write the following and release it as version `1.0.0`. + +```julia +module HelloWorld + +export greet + +import Random +import JSON + +"Writes a friendly message." +greet() = print("Hello World!") + +"Greet an alien by a randomly generated name." +greet_alien() = print("Hello ", Random.randstring(8)) + +end # module +``` + +Then, if we change `greet` to + +```julia +"Writes a friendly message that is exactly three words long." +greet() = print("Hello Lovely World!") +``` + +We would release the new version as `1.1.0`. This is not breaking +because the new implementation conforms to the old documentation, but +it does add a new feature, that the message must be three words long. + +Later, we may wish to change `greet_alien` to + +```julia +"Greet an alien by a the name of \"Zork\"." +greet_alien() = print("Hello Zork") +``` + +And also export it by changing + +```julia +export greet +``` + +to + +```julia +export greet, greet_alien +``` + +We should release this new version as `1.2.0` because it adds a new feature +`greet_alien` to the public API. Even though `greet_alien` was documented before +and the new version does not conform to the old documentation, this is not breaking +because the old documentation was not attached to a symbol that was exported +at the time so that documentation does not apply across released versions. + +However, if we now wish to change `greet` to + +```julia +"Writes a friendly message that is exactly four words long." +greet() = print("Hello very lovely world") +``` + +we would need to release the new version as `2.0.0`. In version `1.1.0`, we specified that +the greeting would be three words long, and because `greet` was exported, that description +also applies to all future versions until the next breaking release. Because this new +version does not conform to the old specification, it must be tagged as a breaking change. + +Please note that version numbers are free and unlimited. It is okay to use lots of them +(e.g. version `6.62.8`). + +## Adding a build step to the package + +The build step is executed the first time a package is installed or when explicitly invoked with `build`. +A package is built by executing the file `deps/build.jl`. + +```julia-repl +julia> mkpath("deps"); + +julia> write("deps/build.jl", + """ + println("I am being built...") + """); + +(HelloWorld) pkg> build + Building HelloWorld → `deps/build.log` + Resolving package versions... + +julia> print(readchomp("deps/build.log")) +I am being built... +``` + +If the build step fails, the output of the build step is printed to the console + +```julia-repl +julia> write("deps/build.jl", + """ + error("Ooops") + """); + +(HelloWorld) pkg> build + Building HelloWorld → `~/HelloWorld/deps/build.log` +ERROR: Error building `HelloWorld`: +ERROR: LoadError: Ooops +Stacktrace: + [1] error(s::String) + @ Base ./error.jl:35 + [2] top-level scope + @ ~/HelloWorld/deps/build.jl:1 + [3] include(fname::String) + @ Base.MainInclude ./client.jl:476 + [4] top-level scope + @ none:5 +in expression starting at /home/kc/HelloWorld/deps/build.jl:1 +``` + +!!! warning + A build step should generally not create or modify any files in the package directory. If you need to store some files + from the build step, use the [Scratch.jl](https://github.com/JuliaPackaging/Scratch.jl) package. + +## Adding tests to the package + +When a package is tested the file `test/runtests.jl` is executed: + +```julia-repl +julia> mkpath("test"); + +julia> write("test/runtests.jl", + """ + println("Testing...") + """); + +(HelloWorld) pkg> test + Testing HelloWorld + Resolving package versions... +Testing... + Testing HelloWorld tests passed +``` + +Tests are run in a new Julia process, where the package itself, and any +test-specific dependencies, are available, see below. + + +!!! warning + Tests should generally not create or modify any files in the package directory. If you need to store some files + from the build step, use the [Scratch.jl](https://github.com/JuliaPackaging/Scratch.jl) package. + +### Test-specific dependencies + +There are two ways of adding test-specific dependencies (dependencies that are not dependencies of the package but will still be available to +load when the package is tested). + +#### `target` based test specific dependencies + +Using this method of adding test-specific dependencies, the packages are added under an `[extras]` section and to a test target, +e.g. to add `Markdown` and `Test` as test dependencies, add the following to the `Project.toml` file: + +```toml +[extras] +Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Markdown", "Test"] +``` + +Note that the only supported targets are `test` and `build`, the latter of which (not recommended) can be used +for any `deps/build.jl` scripts. + +#### Alternative approach: `test/Project.toml` file test specific dependencies + +!!! note + The exact interaction between `Project.toml`, `test/Project.toml` and their corresponding + `Manifest.toml`s are not fully worked out and may be subject to change in future versions. + The older method of adding test-specific dependencies, described in the previous section, + will therefore be supported throughout all Julia 1.X releases. + +In Julia 1.2 and later test dependencies can be declared in `test/Project.toml`. When running +tests, Pkg will automatically merge this and the package Projects to create the test environment. + +!!! note + If no `test/Project.toml` exists Pkg will use the `target` based test specific dependencies. + +To add a test-specific dependency, i.e. a dependency that is available only when testing, +it is thus enough to add this dependency to the `test/Project.toml` project. This can be +done from the Pkg REPL by activating this environment, and then use `add` as one normally +does. Let's add the `Test` standard library as a test dependency: + +```julia-repl +(HelloWorld) pkg> activate ./test +[ Info: activating environment at `~/HelloWorld/test/Project.toml`. + +(test) pkg> add Test + Resolving package versions... + Updating `~/HelloWorld/test/Project.toml` + [8dfed614] + Test + Updating `~/HelloWorld/test/Manifest.toml` + [...] +``` + +We can now use `Test` in the test script and we can see that it gets installed when testing: + +```julia-repl +julia> write("test/runtests.jl", + """ + using Test + @test 1 == 1 + """); + +(test) pkg> activate . + +(HelloWorld) pkg> test + Testing HelloWorld + Resolving package versions... + Updating `/var/folders/64/76tk_g152sg6c6t0b4nkn1vw0000gn/T/tmpPzUPPw/Project.toml` + [d8327f2a] + HelloWorld v0.1.0 [`~/.julia/dev/Pkg/HelloWorld`] + [8dfed614] + Test + Updating `/var/folders/64/76tk_g152sg6c6t0b4nkn1vw0000gn/T/tmpPzUPPw/Manifest.toml` + [d8327f2a] + HelloWorld v0.1.0 [`~/.julia/dev/Pkg/HelloWorld`] + Testing HelloWorld tests passed``` +``` + +## Compatibility on dependencies + +Every dependency should in general have a compatibility constraint on it. +This is an important topic so there is a chapter in the package docs about it: +[Compatibility](https://pkgdocs.julialang.org/v1/compatibility). + +## Weak dependencies + +!!! note + This is a somewhat advanced usage of Pkg which can be skipped for people new to Julia and Julia packages. + +!!! compat + The described feature requires Julia 1.9+. + +A weak dependency is a dependency that will not automatically install when the package is installed but +you can still control what versions of that package are allowed to be installed by setting compatibility on it. +These are listed in the project file under the `[weakdeps]` section: + +```toml +[weakdeps] +SomePackage = "b3785f31-9d33-4cdf-bc73-f646780f1739" + +[compat] +SomePackage = "1.2" +``` + +The current usage of this is almost solely limited to "extensions" which is described in the next section. + +## Conditional loading of code in packages (Extensions) + +!!! note + This is a somewhat advanced usage of Pkg which can be skipped for people new to Julia and Julia packages. + +!!! compat + The described feature requires Julia 1.9+. + +Sometimes one wants to make two or more packages work well together, but may be reluctant (perhaps due to increased load times) to make one an unconditional dependency of the other. +A package *extension* is a module in a file (similar to a package) that is automatically loaded when *some other set of packages* are +loaded into the Julia session. This is very similar to functionality that the external package +[Requires.jl](https://github.com/JuliaPackaging/Requires.jl) provides, but which is now available directly through Julia, +and provides added benefits such as being able to precompile the extension. + +### Code structure + +A useful application of extensions could be for a plotting package that should be able to plot +objects from a wide variety of different Julia packages. +Adding all those different Julia packages as dependencies of the plotting package +could be expensive since they would end up getting loaded even if they were never used. +Instead, the code required to plot objects for specific packages can be put into separate files +(extensions) and these are loaded only when the packages that define the type(s) we want to plot +are loaded. + +Below is an example of how the code can be structured for a use case in which a +`Plotting` package wants to be able to display objects defined in the external package `Contour`. +The file and folder structure shown below is found in the `Plotting` package. + + `Project.toml`: + ```toml +name = "Plotting" +version = "0.1.0" +uuid = "..." + +[weakdeps] +Contour = "d38c429a-6771-53c6-b99e-75d170b6e991" + +[extensions] +# name of extension to the left +# extension dependencies required to load the extension to the right +# use a list for multiple extension dependencies +PlottingContourExt = "Contour" + +[compat] +Contour = "0.6.2" +``` + +`src/Plotting.jl`: +```julia +module Plotting + +function plot(x::Vector) + # Some functionality for plotting a vector here +end + +end # module +``` + +`ext/PlottingContourExt.jl` (can also be in `ext/PlottingContourExt/PlottingContourExt.jl`): +```julia +module PlottingContourExt # Should be same name as the file (just like a normal package) + +using Plotting, Contour + +function Plotting.plot(c::Contour.ContourCollection) + # Some functionality for plotting a contour here +end + +end # module +``` + +Extensions can have any arbitrary name (here `PlottingContourExt`), but using something similar to the format of +this example that makes the extended functionality and dependency of the extension clear is likely a good idea. + +!!! compat + Often you will put the extension dependencies into the `test` target so they are loaded when running e.g. `Pkg.test()`. On earlier Julia versions + this requires you to also put the package in the `[extras]` section. This is unfortunate but the project verifier on older Julia versions will + complain if this is not done. + +!!! note + If you use a manifest generated by a Julia version that does not know about extensions with a Julia version that does + know about them, the extensions will not load. This is because the manifest lacks some information that tells Julia + when it should load these packages. So make sure you use a manifest generated at least the Julia version you are using. + +### Behavior of extensions + +A user that depends only on `Plotting` will not pay the cost of the "extension" inside the `PlottingContourExt` module. +It is only when the `Contour` package actually gets loaded that the `PlottingContourExt` extension is loaded too +and provides the new functionality. + +In our example, the new functionality is an additional _method_, which we add to an existing _function_ from the parent package `Plotting`. +Implementing such methods is among the most standard use cases of package extensions. +Within the parent package, the function to extend can even be defined with zero methods, as follows: + +```julia +function plot end +``` + +!!! note + If one considers `PlottingContourExt` as a completely separate package, it could be argued that defining `Plotting.plot(c::Contour.ContourCollection)` is + [type piracy](@ref avoid-type-piracy) since `PlottingContourExt` _owns_ neither the function `Plotting.plot` nor the type `Contour.ContourCollection`. + However, for extensions, it is ok to assume that the extension owns the functions in its parent package. + +In other situations, one may need to define new symbols in the extension (types, structs, functions, etc.) instead of reusing those from the parent package. +Such symbols are created in a separate module corresponding to the extension, namely `PlottingContourExt`, and thus not in `Plotting` itself. +If extension symbols are needed in the parent package, one must call `Base.get_extension` to retrieve them. +Here is an example showing how a custom type defined in `PlottingContourExt` can be accessed in `Plotting`: + +```julia +ext = Base.get_extension(@__MODULE__, :PlottingContourExt) +if !isnothing(ext) + ContourPlotType = ext.ContourPlotType +end +``` + +On the other hand, accessing extension symbols from a third-party package (i.e. not the parent) is not a recommended practice at the moment. + +### Backwards compatibility + +This section discusses various methods for using extensions on Julia versions that support them, +while simultaneously providing similar functionality on older Julia versions. + +#### Requires.jl + +This section is relevant if you are currently using Requires.jl but want to transition to using extensions (while still having Requires be used on Julia versions that do not support extensions). +This is done by making the following changes (using the example above): + +- Add the following to the package file. This makes it so that Requires.jl loads and inserts the + callback only when extensions are not supported + ```julia + # This symbol is only defined on Julia versions that support extensions + if !isdefined(Base, :get_extension) + using Requires + end + + @static if !isdefined(Base, :get_extension) + function __init__() + @require Contour = "d38c429a-6771-53c6-b99e-75d170b6e991" include("../ext/PlottingContourExt.jl") + end + end + ``` + or if you have other things in your `__init__()` function: + ```julia + if !isdefined(Base, :get_extension) + using Requires + end + + function __init__() + # Other init functionality here + + @static if !isdefined(Base, :get_extension) + @require Contour = "d38c429a-6771-53c6-b99e-75d170b6e991" include("../ext/PlottingContourExt.jl") + end + end + ``` +- Make the following change in the conditionally-loaded code: + ```julia + isdefined(Base, :get_extension) ? (using Contour) : (using ..Contour) + ``` +- Add `Requires` to `[weakdeps]` in your `Project.toml` file, so that it is listed in both `[deps]` and `[weakdeps]`. + Julia 1.9+ knows to not install it as a regular dependency, whereas earlier versions will consider it a dependency. + +The package should now work with Requires.jl on Julia versions before extensions were introduced +and with extensions on more recent Julia versions. + +#### Transition from normal dependency to extension + +This section is relevant if you have a normal dependency that you want to transition be an extension (while still having the dependency be a normal dependency on Julia versions that do not support extensions). +This is done by making the following changes (using the example above): + +- Make sure that the package is **both** in the `[deps]` and `[weakdeps]` section. Newer Julia versions will ignore dependencies in `[deps]` that are also in `[weakdeps]`. +- Add the following to your main package file (typically at the bottom): + ```julia + if !isdefined(Base, :get_extension) + include("../ext/PlottingContourExt.jl") + end + ``` + +#### Using an extension while supporting older Julia versions + +In the case where one wants to use an extension (without worrying about the +feature of the extension begin available on older Julia versions) while still +supporting older Julia versions the packages under `[weakdeps]` should be +duplicated into `[extras]`. This is an unfortunate duplication, but without +doing this the project verifier under older Julia versions will throw an error +if it finds packages under `[compat]` that is not listed in `[extras]`. + +## Package naming guidelines + +Package names should be sensible to most Julia users, *even to those who are not domain experts*. +The following guidelines apply to the `General` registry but may be useful for other package +registries as well. + +Since the `General` registry belongs to the entire community, people may have opinions about +your package name when you publish it, especially if it's ambiguous or can be confused with +something other than what it is. Usually, you will then get suggestions for a new name that +may fit your package better. + +1. Avoid jargon. In particular, avoid acronyms unless there is minimal possibility of confusion. + + * It's ok to say `USA` if you're talking about the USA. + * It's not ok to say `PMA`, even if you're talking about positive mental attitude. +2. Avoid using `Julia` in your package name or prefixing it with `Ju`. + + * It is usually clear from context and to your users that the package is a Julia package. + * Package names already have a `.jl` extension, which communicates to users that `Package.jl` is a Julia package. + * Having Julia in the name can imply that the package is connected to, or endorsed by, contributors + to the Julia language itself. +3. Packages that provide most of their functionality in association with a new type should have pluralized + names. + + * `DataFrames` provides the `DataFrame` type. + * `BloomFilters` provides the `BloomFilter` type. + * In contrast, `JuliaParser` provides no new type, but instead new functionality in the `JuliaParser.parse()` + function. +4. Err on the side of clarity, even if clarity seems long-winded to you. + + * `RandomMatrices` is a less ambiguous name than `RndMat` or `RMT`, even though the latter are shorter. +5. A less systematic name may suit a package that implements one of several possible approaches to + its domain. + + * Julia does not have a single comprehensive plotting package. Instead, `Gadfly`, `PyPlot`, `Winston` + and other packages each implement a unique approach based on a particular design philosophy. + * In contrast, `SortingAlgorithms` provides a consistent interface to use many well-established + sorting algorithms. +6. Packages that wrap external libraries or programs should be named after those libraries or programs. + + * `CPLEX.jl` wraps the `CPLEX` library, which can be identified easily in a web search. + * `MATLAB.jl` provides an interface to call the MATLAB engine from within Julia. +7. Avoid naming a package closely to an existing package + * `Websocket` is too close to `WebSockets` and can be confusing to users. Rather use a new name such as `SimpleWebsockets`. + +## Registering packages + +Once a package is ready it can be registered with the [General Registry](https://github.com/JuliaRegistries/General#registering-a-package-in-general) (see also the [FAQ](https://github.com/JuliaRegistries/General#faq)). +Currently, packages are submitted via [`Registrator`](https://juliaregistrator.github.io/). +In addition to `Registrator`, [`TagBot`](https://github.com/marketplace/actions/julia-tagbot) helps manage the process of tagging releases. + +## Best Practices + +Packages should avoid mutating their own state (writing to files within their package directory). +Packages should, in general, not assume that they are located in a writable location (e.g. if installed as part of a system-wide depot) or even a stable one (e.g. if they are bundled into a system image by [PackageCompiler.jl](https://github.com/JuliaLang/PackageCompiler.jl)). +To support the various use cases in the Julia package ecosystem, the Pkg developers have created a number of auxiliary packages and techniques to help package authors create self-contained, immutable, and relocatable packages: + +* [`Artifacts`](https://pkgdocs.julialang.org/v1/artifacts/) can be used to bundle chunks of data alongside your package, or even allow them to be downloaded on-demand. + Prefer artifacts over attempting to open a file via a path such as `joinpath(@__DIR__, "data", "my_dataset.csv")` as this is non-relocatable. + Once your package has been precompiled, the result of `@__DIR__` will have been baked into your precompiled package data, and if you attempt to distribute this package, it will attempt to load files at the wrong location. + Artifacts can be bundled and accessed easily using the `artifact"name"` string macro. + +* [`Scratch.jl`](https://github.com/JuliaPackaging/Scratch.jl) provides the notion of "scratch spaces", mutable containers of data for packages. + Scratch spaces are designed for data caches that are completely managed by a package and should be removed when the package itself is uninstalled. + For important user-generated data, packages should continue to write out to a user-specified path that is not managed by Julia or Pkg. + +* [`Preferences.jl`](https://github.com/JuliaPackaging/Preferences.jl) allows packages to read and write preferences to the top-level `Project.toml`. + These preferences can be read at runtime or compile-time, to enable or disable different aspects of package behavior. + Packages previously would write out files to their own package directories to record options set by the user or environment, but this is highly discouraged now that `Preferences` is available. diff --git a/pkgimage.mk b/pkgimage.mk index 58049e9a48f2b5..83c66bd94c7020 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -35,12 +35,10 @@ $$(BUILDDIR)/stdlib/$1.debug.image: export JULIA_CPU_TARGET=$(JULIA_CPU_TARGET) $$(BUILDDIR)/stdlib/$1.release.image: $$($1_SRCS) $$(addsuffix .release.image,$$(addprefix $$(BUILDDIR)/stdlib/,$2)) $(build_private_libdir)/sys.$(SHLIB_EXT) @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes -e 'Base.compilecache(Base.identify_package("$1"))') - @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --pkgimage=no -e 'Base.compilecache(Base.identify_package("$1"))') @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.compilecache(Base.identify_package("$1"))') touch $$@ $$(BUILDDIR)/stdlib/$1.debug.image: $$($1_SRCS) $$(addsuffix .debug.image,$$(addprefix $$(BUILDDIR)/stdlib/,$2)) $(build_private_libdir)/sys-debug.$(SHLIB_EXT) @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes -e 'Base.compilecache(Base.identify_package("$1"))') - @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --pkgimage=no -e 'Base.compilecache(Base.identify_package("$1"))') @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.compilecache(Base.identify_package("$1"))') touch $$@ else diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index af7e05b338d2ff..b65d412b6bcda6 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1263,6 +1263,12 @@ static void construct_vars(Module &M, Partition &partition) { gidxs_var->setDSOLocal(true); } +extern "C" void lambda_trampoline(void* arg) { + std::function* func = static_cast*>(arg); + (*func)(); + delete func; +} + // Entrypoint to optionally-multithreaded image compilation. This handles global coordination of the threading, // as well as partitioning, serialization, and deserialization. template @@ -1351,9 +1357,9 @@ static SmallVector add_output(Module &M, TargetMachine &TM, Stri // Start all of the worker threads { JL_TIMING(NATIVE_AOT, NATIVE_Opt); - std::vector workers(threads); + std::vector workers(threads); for (unsigned i = 0; i < threads; i++) { - workers[i] = std::thread([&, i]() { + std::function func = [&, i]() { LLVMContext ctx; // Lazily deserialize the entire module timers[i].deserialize.startTimer(); @@ -1375,12 +1381,14 @@ static SmallVector add_output(Module &M, TargetMachine &TM, Stri timers[i].construct.stopTimer(); outputs[i] = add_output_impl(*M, TM, timers[i], unopt_out, opt_out, obj_out, asm_out); - }); + }; + auto arg = new std::function(func); + uv_thread_create(&workers[i], lambda_trampoline, arg); // Use libuv thread to avoid issues with stack sizes } // Wait for all of the worker threads to finish - for (auto &w : workers) - w.join(); + for (unsigned i = 0; i < threads; i++) + uv_thread_join(&workers[i]); } output_timer.stopTimer(); @@ -1418,7 +1426,6 @@ static unsigned compute_image_thread_count(const ModuleInfo &info) { LLVM_DEBUG(dbgs() << "COFF is restricted to a single thread for large images\n"); return 1; } - // This is not overridable because empty modules do occasionally appear, but they'll be very small and thus exit early to // known easy behavior. Plus they really don't warrant multiple threads if (info.weight < 1000) { diff --git a/src/ast.c b/src/ast.c index 9e22369d836b97..e6adbdc4d5aac6 100644 --- a/src/ast.c +++ b/src/ast.c @@ -153,17 +153,61 @@ static jl_value_t *scm_to_julia(fl_context_t *fl_ctx, value_t e, jl_module_t *mo static value_t julia_to_scm(fl_context_t *fl_ctx, jl_value_t *v); static jl_value_t *jl_expand_macros(jl_value_t *expr, jl_module_t *inmodule, struct macroctx_stack *macroctx, int onelevel, size_t world, int throw_load_error); +static jl_sym_t *scmsym_to_julia(fl_context_t *fl_ctx, value_t s) +{ + assert(issymbol(s)); + if (fl_isgensym(fl_ctx, s)) { + char gsname[16]; + char *n = uint2str(&gsname[1], sizeof(gsname)-1, + ((gensym_t*)ptr(s))->id, 10); + *(--n) = '#'; + return jl_symbol(n); + } + return jl_symbol(symbol_name(fl_ctx, s)); +} + static value_t fl_defined_julia_global(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) { // tells whether a var is defined in and *by* the current module argcount(fl_ctx, "defined-julia-global", nargs, 1); (void)tosymbol(fl_ctx, args[0], "defined-julia-global"); jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx); - jl_sym_t *var = jl_symbol(symbol_name(fl_ctx, args[0])); + jl_sym_t *var = scmsym_to_julia(fl_ctx, args[0]); jl_binding_t *b = jl_get_module_binding(ctx->module, var, 0); return (b != NULL && jl_atomic_load_relaxed(&b->owner) == b) ? fl_ctx->T : fl_ctx->F; } +static value_t fl_nothrow_julia_global(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) +{ + // tells whether a var is defined, in the sense that accessing it is nothrow + // can take either a symbol or a module and a symbol + jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx); + jl_module_t *mod = ctx->module; + jl_sym_t *var = NULL; + if (nargs == 1) { + (void)tosymbol(fl_ctx, args[0], "nothrow-julia-global"); + var = scmsym_to_julia(fl_ctx, args[0]); + } + else { + argcount(fl_ctx, "nothrow-julia-global", nargs, 2); + value_t argmod = args[0]; + if (iscvalue(argmod) && cv_class((cvalue_t*)ptr(argmod)) == jl_ast_ctx(fl_ctx)->jvtype) { + mod = *(jl_module_t**)cv_data((cvalue_t*)ptr(argmod)); + JL_GC_PROMISE_ROOTED(mod); + } else { + (void)tosymbol(fl_ctx, argmod, "nothrow-julia-global"); + if (scmsym_to_julia(fl_ctx, argmod) != jl_thismodule_sym) { + lerrorf(fl_ctx, fl_ctx->ArgError, "nothrow-julia-global: Unknown globalref module kind"); + } + } + (void)tosymbol(fl_ctx, args[1], "nothrow-julia-global"); + var = scmsym_to_julia(fl_ctx, args[1]); + } + jl_binding_t *b = jl_get_module_binding(mod, var, 0); + b = b ? jl_atomic_load_relaxed(&b->owner) : NULL; + return b != NULL && jl_atomic_load_relaxed(&b->value) != NULL ? fl_ctx->T : fl_ctx->F; +} + static value_t fl_current_module_counter(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) JL_NOTSAFEPOINT { jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx); @@ -210,6 +254,7 @@ static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, jl_module_t *m static const builtinspec_t julia_flisp_ast_ext[] = { { "defined-julia-global", fl_defined_julia_global }, // TODO: can we kill this safepoint + { "nothrow-julia-global", fl_nothrow_julia_global }, { "current-julia-module-counter", fl_current_module_counter }, { "julia-scalar?", fl_julia_scalar }, { "julia-current-file", fl_julia_current_file }, @@ -421,20 +466,6 @@ JL_DLLEXPORT void fl_profile(const char *fname) jl_ast_ctx_leave(ctx); } - -static jl_sym_t *scmsym_to_julia(fl_context_t *fl_ctx, value_t s) -{ - assert(issymbol(s)); - if (fl_isgensym(fl_ctx, s)) { - char gsname[16]; - char *n = uint2str(&gsname[1], sizeof(gsname)-1, - ((gensym_t*)ptr(s))->id, 10); - *(--n) = '#'; - return jl_symbol(n); - } - return jl_symbol(symbol_name(fl_ctx, s)); -} - static jl_value_t *scm_to_julia(fl_context_t *fl_ctx, value_t e, jl_module_t *mod) { jl_value_t *v = NULL; diff --git a/src/builtins.c b/src/builtins.c index b2733818abcb0c..04b45500fddaa9 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -344,6 +344,9 @@ static uintptr_t type_object_id_(jl_value_t *v, jl_varidx_t *env) JL_NOTSAFEPOIN i++; pe = pe->prev; } + uintptr_t bits = jl_astaggedvalue(v)->header; + if (bits & GC_IN_IMAGE) + return ((uintptr_t*)v)[-2]; return inthash((uintptr_t)v); } if (tv == jl_uniontype_type) { @@ -432,50 +435,56 @@ static uintptr_t immut_id_(jl_datatype_t *dt, jl_value_t *v, uintptr_t h) JL_NOT return h; } -static uintptr_t NOINLINE jl_object_id__cold(jl_datatype_t *dt, jl_value_t *v) JL_NOTSAFEPOINT +static uintptr_t NOINLINE jl_object_id__cold(uintptr_t tv, jl_value_t *v) JL_NOTSAFEPOINT { - if (dt == jl_simplevector_type) - return hash_svec((jl_svec_t*)v); - if (dt == jl_datatype_type) { - jl_datatype_t *dtv = (jl_datatype_t*)v; - uintptr_t h = ~dtv->name->hash; - return bitmix(h, hash_svec(dtv->parameters)); - } - if (dt == jl_string_type) { + jl_datatype_t *dt = (jl_datatype_t*)jl_to_typeof(tv); + if (dt->name->mutabl) { + if (dt == jl_string_type) { #ifdef _P64 - return memhash_seed(jl_string_data(v), jl_string_len(v), 0xedc3b677); + return memhash_seed(jl_string_data(v), jl_string_len(v), 0xedc3b677); #else - return memhash32_seed(jl_string_data(v), jl_string_len(v), 0xedc3b677); + return memhash32_seed(jl_string_data(v), jl_string_len(v), 0xedc3b677); #endif - } - if (dt == jl_module_type) { - jl_module_t *m = (jl_module_t*)v; - return m->hash; - } - if (dt->name->mutabl) + } + if (dt == jl_simplevector_type) + return hash_svec((jl_svec_t*)v); + if (dt == jl_datatype_type) { + jl_datatype_t *dtv = (jl_datatype_t*)v; + uintptr_t h = ~dtv->name->hash; + return bitmix(h, hash_svec(dtv->parameters)); + } + if (dt == jl_module_type) { + jl_module_t *m = (jl_module_t*)v; + return m->hash; + } + uintptr_t bits = jl_astaggedvalue(v)->header; + if (bits & GC_IN_IMAGE) + return ((uintptr_t*)v)[-2]; return inthash((uintptr_t)v); + } return immut_id_(dt, v, dt->hash); } -JL_DLLEXPORT inline uintptr_t jl_object_id_(jl_value_t *tv, jl_value_t *v) JL_NOTSAFEPOINT +JL_DLLEXPORT inline uintptr_t jl_object_id_(uintptr_t tv, jl_value_t *v) JL_NOTSAFEPOINT { - jl_datatype_t *dt = (jl_datatype_t*)tv; - if (dt == jl_symbol_type) + if (tv == jl_symbol_tag << 4) { return ((jl_sym_t*)v)->hash; - if (dt == jl_typename_type) - return ((jl_typename_t*)v)->hash; - if (dt == jl_datatype_type) { + } + else if (tv == jl_datatype_tag << 4) { jl_datatype_t *dtv = (jl_datatype_t*)v; if (dtv->isconcretetype) return dtv->hash; } - return jl_object_id__cold(dt, v); + else if (tv == (uintptr_t)jl_typename_type) { + return ((jl_typename_t*)v)->hash; + } + return jl_object_id__cold(tv, v); } JL_DLLEXPORT uintptr_t jl_object_id(jl_value_t *v) JL_NOTSAFEPOINT { - return jl_object_id_(jl_typeof(v), v); + return jl_object_id_(jl_typetagof(v), v); } // eq hash table -------------------------------------------------------------- @@ -1968,7 +1977,7 @@ JL_CALLABLE(jl_f_intrinsic_call) f = cglobal_auto; unsigned fargs = intrinsic_nargs[f]; if (!fargs) - jl_errorf("`%s` must be compiled to be called", jl_intrinsic_name(f)); + jl_errorf("`%s` requires the compiler", jl_intrinsic_name(f)); JL_NARGS(intrinsic_call, fargs, fargs); union { diff --git a/src/ccall.cpp b/src/ccall.cpp index db2d73bbfcd3fc..ece0ee24908e85 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1003,8 +1003,9 @@ static Value *box_ccall_result(jl_codectx_t &ctx, Value *result, Value *runtime_ // XXX: need to handle parameterized zero-byte types (singleton) const DataLayout &DL = ctx.builder.GetInsertBlock()->getModule()->getDataLayout(); unsigned nb = DL.getTypeStoreSize(result->getType()); + unsigned align = sizeof(void*); // Allocations are at least pointer aligned MDNode *tbaa = jl_is_mutable(rt) ? ctx.tbaa().tbaa_mutab : ctx.tbaa().tbaa_immut; - Value *strct = emit_allocobj(ctx, nb, runtime_dt); + Value *strct = emit_allocobj(ctx, nb, runtime_dt, true, align); setName(ctx.emission_context, strct, "ccall_result_box"); init_bits_value(ctx, strct, result, tbaa); return strct; @@ -1383,6 +1384,10 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) assert(i < nccallargs && i + fc_args_start <= nargs); jl_value_t *argi = args[fc_args_start + i]; argv[i] = emit_expr(ctx, argi); + if (argv[i].typ == jl_bottom_type) { + JL_GC_POP(); + return jl_cgval_t(); + } } // emit roots @@ -1880,7 +1885,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) if (!val.isghost && !val.ispointer()) val = value_to_pointer(ctx, val); Value *args[] = { - emit_typeof(ctx, val), + emit_typeof(ctx, val, false, true), val.isghost ? ConstantPointerNull::get(T_pint8_derived) : ctx.builder.CreateBitCast( decay_derived(ctx, data_pointer(ctx, val)), @@ -1984,7 +1989,7 @@ jl_cgval_t function_sig_t::emit_a_ccall( // XXX: result needs to be zero'd and given a GC root here // and has incorrect write barriers. // instead this code path should behave like `unsafe_load` - result = emit_allocobj(ctx, (jl_datatype_t*)rt); + result = emit_allocobj(ctx, (jl_datatype_t*)rt, true); setName(ctx.emission_context, result, "ccall_sret_box"); sretty = ctx.types().T_jlvalue; sretboxed = true; @@ -2141,7 +2146,7 @@ jl_cgval_t function_sig_t::emit_a_ccall( else if (jlretboxed && !retboxed) { assert(jl_is_datatype(rt)); if (static_rt) { - Value *strct = emit_allocobj(ctx, (jl_datatype_t*)rt); + Value *strct = emit_allocobj(ctx, (jl_datatype_t*)rt, true); setName(ctx.emission_context, strct, "ccall_ret_box"); MDNode *tbaa = jl_is_mutable(rt) ? ctx.tbaa().tbaa_mutab : ctx.tbaa().tbaa_immut; int boxalign = julia_alignment(rt); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index ea59d4e298f1ca..15a1522ee0d571 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -783,9 +783,6 @@ static Type *_julia_struct_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt, lty = JuliaType::get_prjlvalue_ty(ctxt); isvector = false; } - else if (ty == (jl_value_t*)jl_bool_type) { - lty = getInt8Ty(ctxt); - } else if (jl_is_uniontype(ty)) { // pick an Integer type size such that alignment will generally be correct, // and always end with an Int8 (selector byte). @@ -2899,7 +2896,7 @@ static Value *emit_genericmemoryowner(jl_codectx_t &ctx, Value *t) // --- boxing --- -static Value *emit_allocobj(jl_codectx_t &ctx, jl_datatype_t *jt); +static Value *emit_allocobj(jl_codectx_t &ctx, jl_datatype_t *jt, bool fully_initialized); static void init_bits_value(jl_codectx_t &ctx, Value *newv, Value *v, MDNode *tbaa, unsigned alignment = sizeof(void*)) // min alignment in julia's gc is pointer-aligned @@ -3209,7 +3206,7 @@ static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallB jl_cgval_t vinfo_r = jl_cgval_t(vinfo, (jl_value_t*)jt, NULL); box = _boxed_special(ctx, vinfo_r, t); if (!box) { - box = emit_allocobj(ctx, jt); + box = emit_allocobj(ctx, jt, true); setName(ctx.emission_context, box, "unionbox"); init_bits_cgval(ctx, box, vinfo_r, jl_is_mutable(jt) ? ctx.tbaa().tbaa_mutab : ctx.tbaa().tbaa_immut); } @@ -3336,7 +3333,7 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo, bool is_promotab if (do_promote && is_promotable) { auto IP = ctx.builder.saveIP(); ctx.builder.SetInsertPoint(vinfo.promotion_point); - box = emit_allocobj(ctx, (jl_datatype_t*)jt); + box = emit_allocobj(ctx, (jl_datatype_t*)jt, true); Value *decayed = decay_derived(ctx, box); AllocaInst *originalAlloca = cast(vinfo.V); box->takeName(originalAlloca); @@ -3352,7 +3349,7 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo, bool is_promotab auto arg_typename = [&] JL_NOTSAFEPOINT { return "box::" + std::string(jl_symbol_name(((jl_datatype_t*)(jt))->name->name)); }; - box = emit_allocobj(ctx, (jl_datatype_t*)jt); + box = emit_allocobj(ctx, (jl_datatype_t*)jt, true); setName(ctx.emission_context, box, arg_typename); init_bits_cgval(ctx, box, vinfo, jl_is_mutable(jt) ? ctx.tbaa().tbaa_mutab : ctx.tbaa().tbaa_immut); } @@ -3481,7 +3478,7 @@ static void emit_cpointercheck(jl_codectx_t &ctx, const jl_cgval_t &x, const Twi // allocation for known size object // returns a prjlvalue static Value *emit_allocobj(jl_codectx_t &ctx, size_t static_size, Value *jt, - unsigned align=sizeof(void*)) // Allocations are at least pointer alingned + bool fully_initialized, unsigned align) { ++EmittedAllocObjs; Value *current_task = get_current_task(ctx); @@ -3489,15 +3486,19 @@ static Value *emit_allocobj(jl_codectx_t &ctx, size_t static_size, Value *jt, auto call = ctx.builder.CreateCall(F, {current_task, ConstantInt::get(ctx.types().T_size, static_size), maybe_decay_untracked(ctx, jt)}); call->setAttributes(F->getAttributes()); if (static_size > 0) - call->addRetAttr(Attribute::getWithDereferenceableBytes(ctx.builder.getContext(), static_size)); - call->addRetAttr(Attribute::getWithAlignment(ctx.builder.getContext(), Align(align))); + call->addRetAttr(Attribute::getWithDereferenceableBytes(call->getContext(), static_size)); + call->addRetAttr(Attribute::getWithAlignment(call->getContext(), Align(align))); +#if JL_LLVM_VERSION >= 150000 + if (fully_initialized) + call->addFnAttr(Attribute::get(call->getContext(), Attribute::AllocKind, uint64_t(AllocFnKind::Alloc | AllocFnKind::Uninitialized))); +#endif return call; } -static Value *emit_allocobj(jl_codectx_t &ctx, jl_datatype_t *jt) +static Value *emit_allocobj(jl_codectx_t &ctx, jl_datatype_t *jt, bool fully_initialized) { return emit_allocobj(ctx, jl_datatype_size(jt), ctx.builder.CreateIntToPtr(emit_tagfrom(ctx, jt), ctx.types().T_pjlvalue), - julia_alignment((jl_value_t*)jt)); + fully_initialized, julia_alignment((jl_value_t*)jt)); } // allocation for unknown object from an untracked pointer @@ -3719,14 +3720,20 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg strct = NULL; } else if (init_as_value) { - if (tracked.count) + if (tracked.count) { strct = Constant::getNullValue(lt); - else + } + else { strct = UndefValue::get(lt); + if (nargs < nf) + strct = ctx.builder.CreateFreeze(strct); + } } else { strct = emit_static_alloca(ctx, lt); setName(ctx.emission_context, strct, arg_typename); + if (nargs < nf) + ctx.builder.CreateStore(ctx.builder.CreateFreeze(UndefValue::get(lt)), strct); if (tracked.count) undef_derived_strct(ctx, strct, sty, ctx.tbaa().tbaa_stack); } @@ -3896,7 +3903,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg return ret; } } - Value *strct = emit_allocobj(ctx, sty); + Value *strct = emit_allocobj(ctx, sty, nargs >= nf); setName(ctx.emission_context, strct, arg_typename); jl_cgval_t strctinfo = mark_julia_type(ctx, strct, true, ty); strct = decay_derived(ctx, strct); @@ -3929,13 +3936,14 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg return strctinfo; } else { - // 0 fields, ghost or bitstype + // 0 fields, ghost or primitive type if (jl_datatype_nbits(sty) == 0) return ghostValue(ctx, sty); + // n.b. this is not valid IR form to construct a primitive type (use bitcast for example) bool isboxed; Type *lt = julia_type_to_llvm(ctx, ty, &isboxed); assert(!isboxed); - return mark_julia_type(ctx, UndefValue::get(lt), false, ty); + return mark_julia_type(ctx, ctx.builder.CreateFreeze(UndefValue::get(lt)), false, ty); } } diff --git a/src/codegen.cpp b/src/codegen.cpp index 079038c71e4816..632c76caa1c08c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1052,7 +1052,7 @@ static const auto jl_alloc_obj_func = new JuliaFunction{ auto FnAttrs = AttrBuilder(C); FnAttrs.addAllocSizeAttr(1, None); // returns %1 bytes #if JL_LLVM_VERSION >= 150000 - FnAttrs.addAllocKindAttr(AllocFnKind::Alloc | AllocFnKind::Uninitialized); + FnAttrs.addAllocKindAttr(AllocFnKind::Alloc); #endif #if JL_LLVM_VERSION >= 160000 FnAttrs.addMemoryAttr(MemoryEffects::argMemOnly(ModRefInfo::Ref) | inaccessibleMemOnly(ModRefInfo::ModRef)); @@ -1165,7 +1165,7 @@ static const auto jlapplytype_func = new JuliaFunction<>{ static const auto jl_object_id__func = new JuliaFunction{ XSTR(jl_object_id_), [](LLVMContext &C, Type *T_size) { return FunctionType::get(T_size, - {JuliaType::get_prjlvalue_ty(C), PointerType::get(getInt8Ty(C), AddressSpace::Derived)}, false); }, + {T_size, PointerType::get(getInt8Ty(C), AddressSpace::Derived)}, false); }, nullptr, }; static const auto setjmp_func = new JuliaFunction{ @@ -2163,9 +2163,12 @@ static bool valid_as_globalinit(const Value *v) { return isa(v); } +static Value *zext_struct(jl_codectx_t &ctx, Value *V); + static inline jl_cgval_t value_to_pointer(jl_codectx_t &ctx, Value *v, jl_value_t *typ, Value *tindex) { Value *loc; + v = zext_struct(ctx, v); if (valid_as_globalinit(v)) { // llvm can't handle all the things that could be inside a ConstantExpr assert(jl_is_concrete_type(typ)); // not legal to have an unboxed abstract type loc = get_pointer_to_constant(ctx.emission_context, cast(v), Align(julia_alignment(typ)), "_j_const", *jl_Module); @@ -2291,17 +2294,6 @@ static void alloc_def_flag(jl_codectx_t &ctx, jl_varinfo_t& vi) // --- utilities --- -static Constant *undef_value_for_type(Type *T) { - auto tracked = CountTrackedPointers(T); - Constant *undef; - if (tracked.count) - // make sure gc pointers (including ptr_phi of union-split) are initialized to NULL - undef = Constant::getNullValue(T); - else - undef = UndefValue::get(T); - return undef; -} - static void CreateTrap(IRBuilder<> &irbuilder, bool create_new_block) { Function *f = irbuilder.GetInsertBlock()->getParent(); @@ -3574,7 +3566,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (f == jl_builtin_is && nargs == 2) { // emit comparison test Value *ans = emit_f_is(ctx, argv[1], argv[2]); - *ret = mark_julia_type(ctx, ctx.builder.CreateZExt(ans, getInt8Ty(ctx.builder.getContext())), false, jl_bool_type); + *ret = mark_julia_type(ctx, ans, false, jl_bool_type); return true; } @@ -3613,8 +3605,6 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (jl_is_type_type(ty.typ) && !jl_has_free_typevars(ty.typ)) { jl_value_t *tp0 = jl_tparam0(ty.typ); Value *isa_result = emit_isa(ctx, arg, tp0, Twine()).first; - if (isa_result->getType() == getInt1Ty(ctx.builder.getContext())) - isa_result = ctx.builder.CreateZExt(isa_result, getInt8Ty(ctx.builder.getContext())); *ret = mark_julia_type(ctx, isa_result, false, jl_bool_type); return true; } @@ -5227,7 +5217,7 @@ static void emit_vi_assignment_unboxed(jl_codectx_t &ctx, jl_varinfo_t &vi, Valu } else { Value *dest = vi.value.V; - if (vi.pTIndex) + if (vi.pTIndex) // TODO: use lifetime-end here instead ctx.builder.CreateStore(UndefValue::get(cast(vi.value.V)->getAllocatedType()), vi.value.V); Type *store_ty = julia_type_to_llvm(ctx, rval_info.constant ? jl_typeof(rval_info.constant) : rval_info.typ); Type *dest_ty = store_ty->getPointerTo(); @@ -5551,16 +5541,15 @@ static Value *emit_condition(jl_codectx_t &ctx, const jl_cgval_t &condV, const T emit_typecheck(ctx, condV, (jl_value_t*)jl_bool_type, msg); } if (isbool) { - Value *cond = emit_unbox(ctx, getInt8Ty(ctx.builder.getContext()), condV, (jl_value_t*)jl_bool_type); - assert(cond->getType() == getInt8Ty(ctx.builder.getContext())); - return ctx.builder.CreateXor(ctx.builder.CreateTrunc(cond, getInt1Ty(ctx.builder.getContext())), ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 1)); + Value *cond = emit_unbox(ctx, getInt1Ty(ctx.builder.getContext()), condV, (jl_value_t*)jl_bool_type); + return ctx.builder.CreateNot(cond); } if (condV.isboxed) { return ctx.builder.CreateICmpEQ(boxed(ctx, condV), track_pjlvalue(ctx, literal_pointer_val(ctx, jl_false))); } - // not a boolean - return ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0); // TODO: replace with Undef + // not a boolean (unreachable dead code) + return UndefValue::get(getInt1Ty(ctx.builder.getContext())); } static Value *emit_condition(jl_codectx_t &ctx, jl_value_t *cond, const Twine &msg) @@ -7029,7 +7018,7 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con outboxed = (output_type != (jl_value_t*)jl_voidpointer_type); if (outboxed) { assert(jl_datatype_size(output_type) == sizeof(void*) * 4); - Value *strct = emit_allocobj(ctx, (jl_datatype_t*)output_type); + Value *strct = emit_allocobj(ctx, (jl_datatype_t*)output_type, true); setName(ctx.emission_context, strct, "cfun_result"); Value *derived_strct = emit_bitcast(ctx, decay_derived(ctx, strct), ctx.types().T_size->getPointerTo()); MDNode *tbaa = best_tbaa(ctx.tbaa(), output_type); @@ -7205,7 +7194,7 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret if (!lty->isAggregateType()) // keep "aggregate" type values in place as pointers theArg = ctx.builder.CreateAlignedLoad(lty, theArg, Align(julia_alignment(ty))); } - assert(dyn_cast(theArg) == NULL); + assert(!isa(theArg)); args[idx] = theArg; idx++; } @@ -8450,15 +8439,19 @@ static jl_llvm_functions_t cursor = -1; }; + // If a pkgimage or sysimage is being generated, disable tracking. + // This means sysimage build or pkgimage precompilation workloads aren't tracked. auto do_coverage = [&] (bool in_user_code, bool is_tracked) { - return (coverage_mode == JL_LOG_ALL || + return (jl_generating_output() == 0 && + (coverage_mode == JL_LOG_ALL || (in_user_code && coverage_mode == JL_LOG_USER) || - (is_tracked && coverage_mode == JL_LOG_PATH)); + (is_tracked && coverage_mode == JL_LOG_PATH))); }; auto do_malloc_log = [&] (bool in_user_code, bool is_tracked) { - return (malloc_log_mode == JL_LOG_ALL || + return (jl_generating_output() == 0 && + (malloc_log_mode == JL_LOG_ALL || (in_user_code && malloc_log_mode == JL_LOG_USER) || - (is_tracked && malloc_log_mode == JL_LOG_PATH)); + (is_tracked && malloc_log_mode == JL_LOG_PATH))); }; SmallVector current_lineinfo, new_lineinfo; auto coverageVisitStmt = [&] (size_t dbg) { @@ -8969,6 +8962,7 @@ static jl_llvm_functions_t } for (PHINode *PN : ToDelete) { + // This basic block is statically unreachable, thus so is this PHINode PN->replaceAllUsesWith(UndefValue::get(PN->getType())); PN->eraseFromParent(); } diff --git a/src/common_symbols2.inc b/src/common_symbols2.inc index 6850fcd3033d1a..a4583fe39f186c 100644 --- a/src/common_symbols2.inc +++ b/src/common_symbols2.inc @@ -50,7 +50,6 @@ jl_symbol("C_NULL"), jl_symbol("oftype"), jl_symbol("_growend!"), jl_symbol("Any"), -jl_symbol("__inbounds_setindex!"), jl_symbol("Tuple"), jl_symbol("float.jl"), jl_symbol("ncodeunits"), diff --git a/src/gc.c b/src/gc.c index 38a4a5f60bcd75..bc0361c8ac7a89 100644 --- a/src/gc.c +++ b/src/gc.c @@ -700,6 +700,7 @@ static uint64_t gc_end_time = 0; static int thrash_counter = 0; static int thrashing = 0; // global variables for GC stats +static uint64_t freed_in_runtime = 0; // Resetting the object to a young object, this is used when marking the // finalizer list to collect them the next time because the object is very @@ -1005,6 +1006,22 @@ static void sweep_weak_refs(void) } +STATIC_INLINE void jl_batch_accum_heap_size(jl_ptls_t ptls, uint64_t sz) JL_NOTSAFEPOINT +{ + uint64_t alloc_acc = jl_atomic_load_relaxed(&ptls->gc_num.alloc_acc) + sz; + if (alloc_acc < 16*1024) + jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, alloc_acc); + else { + jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, alloc_acc); + jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, 0); + } +} + +STATIC_INLINE void jl_batch_accum_free_size(jl_ptls_t ptls, uint64_t sz) JL_NOTSAFEPOINT +{ + jl_atomic_store_relaxed(&ptls->gc_num.free_acc, jl_atomic_load_relaxed(&ptls->gc_num.free_acc) + sz); +} + // big value list // Size includes the tag and the tag is not cleared!! @@ -1027,13 +1044,7 @@ STATIC_INLINE jl_value_t *jl_gc_big_alloc_inner(jl_ptls_t ptls, size_t sz) jl_atomic_load_relaxed(&ptls->gc_num.allocd) + allocsz); jl_atomic_store_relaxed(&ptls->gc_num.bigalloc, jl_atomic_load_relaxed(&ptls->gc_num.bigalloc) + 1); - uint64_t alloc_acc = jl_atomic_load_relaxed(&ptls->gc_num.alloc_acc); - if (alloc_acc + allocsz < 16*1024) - jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, alloc_acc + allocsz); - else { - jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, alloc_acc + allocsz); - jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, 0); - } + jl_batch_accum_heap_size(ptls, allocsz); #ifdef MEMDEBUG memset(v, 0xee, allocsz); #endif @@ -1147,16 +1158,10 @@ void jl_gc_count_allocd(size_t sz) JL_NOTSAFEPOINT jl_ptls_t ptls = jl_current_task->ptls; jl_atomic_store_relaxed(&ptls->gc_num.allocd, jl_atomic_load_relaxed(&ptls->gc_num.allocd) + sz); - uint64_t alloc_acc = jl_atomic_load_relaxed(&ptls->gc_num.alloc_acc); - if (alloc_acc + sz < 16*1024) - jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, alloc_acc + sz); - else { - jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, alloc_acc + sz); - jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, 0); - } + jl_batch_accum_heap_size(ptls, sz); } - -static void combine_thread_gc_counts(jl_gc_num_t *dest) JL_NOTSAFEPOINT +// Only safe to update the heap inside the GC +static void combine_thread_gc_counts(jl_gc_num_t *dest, int update_heap) JL_NOTSAFEPOINT { int gc_n_threads; jl_ptls_t* gc_all_tls_states; @@ -1170,12 +1175,14 @@ static void combine_thread_gc_counts(jl_gc_num_t *dest) JL_NOTSAFEPOINT dest->realloc += jl_atomic_load_relaxed(&ptls->gc_num.realloc); dest->poolalloc += jl_atomic_load_relaxed(&ptls->gc_num.poolalloc); dest->bigalloc += jl_atomic_load_relaxed(&ptls->gc_num.bigalloc); - uint64_t alloc_acc = jl_atomic_load_relaxed(&ptls->gc_num.alloc_acc); - uint64_t free_acc = jl_atomic_load_relaxed(&ptls->gc_num.free_acc); dest->freed += jl_atomic_load_relaxed(&ptls->gc_num.free_acc); - jl_atomic_store_relaxed(&gc_heap_stats.heap_size, alloc_acc - free_acc + jl_atomic_load_relaxed(&gc_heap_stats.heap_size)); - jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, 0); - jl_atomic_store_relaxed(&ptls->gc_num.free_acc, 0); + if (update_heap) { + uint64_t alloc_acc = jl_atomic_load_relaxed(&ptls->gc_num.alloc_acc); + freed_in_runtime += jl_atomic_load_relaxed(&ptls->gc_num.free_acc); + jl_atomic_store_relaxed(&gc_heap_stats.heap_size, alloc_acc + jl_atomic_load_relaxed(&gc_heap_stats.heap_size)); + jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, 0); + jl_atomic_store_relaxed(&ptls->gc_num.free_acc, 0); + } } } } @@ -1209,7 +1216,7 @@ static int64_t inc_live_bytes(int64_t inc) JL_NOTSAFEPOINT void jl_gc_reset_alloc_count(void) JL_NOTSAFEPOINT { - combine_thread_gc_counts(&gc_num); + combine_thread_gc_counts(&gc_num, 0); inc_live_bytes(gc_num.deferred_alloc + gc_num.allocd); gc_num.allocd = 0; gc_num.deferred_alloc = 0; @@ -1413,6 +1420,38 @@ int jl_gc_classify_pools(size_t sz, int *osize) // sweep phase +gc_fragmentation_stat_t gc_page_fragmentation_stats[JL_GC_N_POOLS]; + +extern gc_fragmentation_stat_t gc_page_fragmentation_stats[JL_GC_N_POOLS]; + +STATIC_INLINE void gc_update_page_fragmentation_data(jl_gc_pagemeta_t *pg) JL_NOTSAFEPOINT +{ +#ifdef GC_MEASURE_PAGE_FRAGMENTATION + gc_fragmentation_stat_t *stats = &gc_page_fragmentation_stats[pg->pool_n]; + jl_atomic_fetch_add(&stats->n_freed_objs, pg->nfree); + jl_atomic_fetch_add(&stats->n_pages_allocd, 1); +#endif +} + +STATIC_INLINE void gc_dump_page_utilization_data(void) JL_NOTSAFEPOINT +{ +#ifdef GC_MEASURE_PAGE_FRAGMENTATION + for (int i = 0; i < JL_GC_N_POOLS; i++) { + gc_fragmentation_stat_t *stats = &gc_page_fragmentation_stats[i]; + double utilization = 1.0; + size_t n_freed_objs = jl_atomic_load_relaxed(&stats->n_freed_objs); + size_t n_pages_allocd = jl_atomic_load_relaxed(&stats->n_pages_allocd); + if (n_pages_allocd != 0) { + utilization -= ((double)n_freed_objs * (double)jl_gc_sizeclasses[i]) / (double)n_pages_allocd / (double)GC_PAGE_SZ; + } + jl_safe_printf("Size class %d: %.2f%% utilization\n", jl_gc_sizeclasses[i], utilization * 100.0); + jl_atomic_store_relaxed(&stats->n_freed_objs, 0); + jl_atomic_store_relaxed(&stats->n_pages_allocd, 0); + } + jl_safe_printf("-----------------------------------------\n"); +#endif +} + int64_t buffered_pages = 0; // Returns pointer to terminal pointer of list rooted at *pfl. @@ -1522,6 +1561,7 @@ static void gc_sweep_page(jl_gc_pool_t *p, jl_gc_page_stack_t *allocd, jl_gc_pag push_lf_back(&global_page_pool_lazily_freed, pg); } } + gc_update_page_fragmentation_data(pg); gc_time_count_page(freedall, pg_skpd); jl_ptls_t ptls = gc_all_tls_states[pg->thread_n]; jl_atomic_fetch_add(&ptls->gc_num.pool_live_bytes, GC_PAGE_SZ - GC_PAGE_OFFSET - nfree * osize); @@ -1735,6 +1775,7 @@ static void gc_sweep_pool(void) #else gc_free_pages(); #endif + gc_dump_page_utilization_data(); gc_time_pool_end(current_sweep_full); } @@ -3142,7 +3183,7 @@ JL_DLLEXPORT int jl_gc_is_enabled(void) JL_DLLEXPORT void jl_gc_get_total_bytes(int64_t *bytes) JL_NOTSAFEPOINT { jl_gc_num_t num = gc_num; - combine_thread_gc_counts(&num); + combine_thread_gc_counts(&num, 0); // Sync this logic with `base/util.jl:GC_Diff` *bytes = (num.total_allocd + num.deferred_alloc + num.allocd); } @@ -3155,7 +3196,7 @@ JL_DLLEXPORT uint64_t jl_gc_total_hrtime(void) JL_DLLEXPORT jl_gc_num_t jl_gc_num(void) { jl_gc_num_t num = gc_num; - combine_thread_gc_counts(&num); + combine_thread_gc_counts(&num, 0); return num; } @@ -3214,7 +3255,7 @@ size_t jl_maxrss(void); // Only one thread should be running in this function static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) { - combine_thread_gc_counts(&gc_num); + combine_thread_gc_counts(&gc_num, 1); // We separate the update of the graph from the update of live_bytes here // so that the sweep shows a downward trend in memory usage. @@ -3398,6 +3439,8 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) gc_num.last_incremental_sweep = gc_end_time; } + jl_atomic_store_relaxed(&gc_heap_stats.heap_size, jl_atomic_load_relaxed(&gc_heap_stats.heap_size) - freed_in_runtime); + freed_in_runtime = 0; size_t heap_size = jl_atomic_load_relaxed(&gc_heap_stats.heap_size); double target_allocs = 0.0; double min_interval = default_collect_interval; @@ -3746,13 +3789,7 @@ JL_DLLEXPORT void *jl_gc_counted_malloc(size_t sz) jl_atomic_load_relaxed(&ptls->gc_num.allocd) + sz); jl_atomic_store_relaxed(&ptls->gc_num.malloc, jl_atomic_load_relaxed(&ptls->gc_num.malloc) + 1); - uint64_t alloc_acc = jl_atomic_load_relaxed(&ptls->gc_num.alloc_acc); - if (alloc_acc + sz < 16*1024) - jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, alloc_acc + sz); - else { - jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, alloc_acc + sz); - jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, 0); - } + jl_batch_accum_heap_size(ptls, sz); } return data; } @@ -3769,13 +3806,7 @@ JL_DLLEXPORT void *jl_gc_counted_calloc(size_t nm, size_t sz) jl_atomic_load_relaxed(&ptls->gc_num.allocd) + nm*sz); jl_atomic_store_relaxed(&ptls->gc_num.malloc, jl_atomic_load_relaxed(&ptls->gc_num.malloc) + 1); - uint64_t alloc_acc = jl_atomic_load_relaxed(&ptls->gc_num.alloc_acc); - if (alloc_acc + sz < 16*1024) - jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, alloc_acc + sz * nm); - else { - jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, alloc_acc + sz * nm); - jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, 0); - } + jl_batch_accum_heap_size(ptls, sz * nm); } return data; } @@ -3786,14 +3817,7 @@ JL_DLLEXPORT void jl_gc_counted_free_with_size(void *p, size_t sz) jl_task_t *ct = jl_current_task; free(p); if (pgcstack != NULL && ct->world_age) { - jl_ptls_t ptls = ct->ptls; - uint64_t free_acc = jl_atomic_load_relaxed(&ptls->gc_num.free_acc); - if (free_acc + sz < 16*1024) - jl_atomic_store_relaxed(&ptls->gc_num.free_acc, free_acc + sz); - else { - jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, -(free_acc + sz)); - jl_atomic_store_relaxed(&ptls->gc_num.free_acc, 0); - } + jl_batch_accum_free_size(ct->ptls, sz); } } @@ -3813,23 +3837,10 @@ JL_DLLEXPORT void *jl_gc_counted_realloc_with_old_size(void *p, size_t old, size int64_t diff = sz - old; if (diff < 0) { - diff = -diff; - uint64_t free_acc = jl_atomic_load_relaxed(&ptls->gc_num.free_acc); - if (free_acc + diff < 16*1024) - jl_atomic_store_relaxed(&ptls->gc_num.free_acc, free_acc + diff); - else { - jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, -(free_acc + diff)); - jl_atomic_store_relaxed(&ptls->gc_num.free_acc, 0); - } + jl_batch_accum_free_size(ptls, -diff); } else { - uint64_t alloc_acc = jl_atomic_load_relaxed(&ptls->gc_num.alloc_acc); - if (alloc_acc + diff < 16*1024) - jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, alloc_acc + diff); - else { - jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, alloc_acc + diff); - jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, 0); - } + jl_batch_accum_heap_size(ptls, diff); } } return data; @@ -3914,13 +3925,7 @@ JL_DLLEXPORT void *jl_gc_managed_malloc(size_t sz) jl_atomic_load_relaxed(&ptls->gc_num.allocd) + allocsz); jl_atomic_store_relaxed(&ptls->gc_num.malloc, jl_atomic_load_relaxed(&ptls->gc_num.malloc) + 1); - uint64_t alloc_acc = jl_atomic_load_relaxed(&ptls->gc_num.alloc_acc); - if (alloc_acc + allocsz < 16*1024) - jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, alloc_acc + allocsz); - else { - jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, alloc_acc + allocsz); - jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, 0); - } + jl_batch_accum_heap_size(ptls, allocsz); #ifdef _OS_WINDOWS_ SetLastError(last_error); #endif @@ -3968,23 +3973,10 @@ static void *gc_managed_realloc_(jl_ptls_t ptls, void *d, size_t sz, size_t olds int64_t diff = allocsz - oldsz; if (diff < 0) { - diff = -diff; - uint64_t free_acc = jl_atomic_load_relaxed(&ptls->gc_num.free_acc); - if (free_acc + diff < 16*1024) - jl_atomic_store_relaxed(&ptls->gc_num.free_acc, free_acc + diff); - else { - jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, -(free_acc + diff)); - jl_atomic_store_relaxed(&ptls->gc_num.free_acc, 0); - } + jl_batch_accum_free_size(ptls, -diff); } else { - uint64_t alloc_acc = jl_atomic_load_relaxed(&ptls->gc_num.alloc_acc); - if (alloc_acc + diff < 16*1024) - jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, alloc_acc + diff); - else { - jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, alloc_acc + diff); - jl_atomic_store_relaxed(&ptls->gc_num.alloc_acc, 0); - } + jl_batch_accum_heap_size(ptls, diff); } if (allocsz > oldsz) { maybe_record_alloc_to_profile((jl_value_t*)b, allocsz - oldsz, (jl_datatype_t*)jl_buff_tag); @@ -4065,8 +4057,7 @@ static void *gc_perm_alloc_large(size_t sz, int zero, unsigned align, unsigned o errno = last_errno; jl_may_leak(base); assert(align > 0); - unsigned diff = (offset - (uintptr_t)base) % align; - return (void*)((char*)base + diff); + return (void*)(LLT_ALIGN((uintptr_t)base + offset, (uintptr_t)align) - offset); } STATIC_INLINE void *gc_try_perm_alloc_pool(size_t sz, unsigned align, unsigned offset) JL_NOTSAFEPOINT diff --git a/src/gc.h b/src/gc.h index 4830d2d123c5d9..227dc5b6b7a53a 100644 --- a/src/gc.h +++ b/src/gc.h @@ -234,6 +234,14 @@ STATIC_INLINE jl_gc_pagemeta_t *pop_lf_back(jl_gc_page_stack_t *pool) JL_NOTSAFE } } +// data structures for tracking fragmentation in the pool allocator +// #define GC_MEASURE_PAGE_FRAGMENTATION + +typedef struct { + _Atomic(size_t) n_freed_objs; + _Atomic(size_t) n_pages_allocd; +} gc_fragmentation_stat_t; + #ifdef _P64 #define REGION0_PG_COUNT (1 << 16) #define REGION1_PG_COUNT (1 << 16) diff --git a/src/gf.c b/src/gf.c index 0ad875b1675066..f964927aa33681 100644 --- a/src/gf.c +++ b/src/gf.c @@ -288,7 +288,7 @@ JL_DLLEXPORT jl_code_instance_t* jl_new_codeinst( jl_method_instance_t *mi, jl_value_t *rettype, jl_value_t *inferred_const, jl_value_t *inferred, int32_t const_flags, size_t min_world, size_t max_world, - uint32_t ipo_effects, uint32_t effects, jl_value_t *argescapes, + uint32_t ipo_effects, uint32_t effects, jl_value_t *analysis_results, uint8_t relocatability); jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_args_t fptr) JL_GC_DISABLED @@ -486,7 +486,7 @@ JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst( jl_method_instance_t *mi, jl_value_t *rettype, jl_value_t *inferred_const, jl_value_t *inferred, int32_t const_flags, size_t min_world, size_t max_world, - uint32_t ipo_effects, uint32_t effects, jl_value_t *argescapes, + uint32_t ipo_effects, uint32_t effects, jl_value_t *analysis_results, uint8_t relocatability /*, jl_array_t *edges, int absolute_max*/) { @@ -514,7 +514,7 @@ JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst( jl_atomic_store_relaxed(&codeinst->next, NULL); codeinst->ipo_purity_bits = ipo_effects; jl_atomic_store_relaxed(&codeinst->purity_bits, effects); - codeinst->argescapes = argescapes; + codeinst->analysis_results = analysis_results; codeinst->relocatability = relocatability; return codeinst; } @@ -2490,7 +2490,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t return codeinst; } if (compile_option == JL_OPTIONS_COMPILE_OFF) { - jl_printf(JL_STDERR, "code missing for "); + jl_printf(JL_STDERR, "No compiled code available for "); jl_static_show(JL_STDERR, (jl_value_t*)mi); jl_printf(JL_STDERR, " : sysimg may not have been built with --compile=all\n"); } @@ -2505,10 +2505,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t if (ucache_invoke == NULL) { if (def->source == jl_nothing && (jl_atomic_load_relaxed(&ucache->def->uninferred) == jl_nothing || jl_atomic_load_relaxed(&ucache->def->uninferred) == NULL)) { - jl_printf(JL_STDERR, "source not available for "); - jl_static_show(JL_STDERR, (jl_value_t*)mi); - jl_printf(JL_STDERR, "\n"); - jl_error("source missing for method that needs to be compiled"); + jl_throw(jl_new_struct(jl_missingcodeerror_type, (jl_value_t*)mi)); } jl_generate_fptr_for_unspecialized(ucache); ucache_invoke = jl_atomic_load_acquire(&ucache->invoke); diff --git a/src/iddict.c b/src/iddict.c index 43ce6c7aa73774..eaa55657e2517f 100644 --- a/src/iddict.c +++ b/src/iddict.c @@ -5,7 +5,7 @@ // compute empirical max-probe for a given size #define max_probe(size) ((size) <= 1024 ? 16 : (size) >> 6) -#define keyhash(k) jl_object_id_(jl_typeof(k), k) +#define keyhash(k) jl_object_id_(jl_typetagof(k), k) #define h2index(hv, sz) (size_t)(((hv) & ((sz)-1)) * 2) static inline int jl_table_assign_bp(jl_genericmemory_t **pa, jl_value_t *key, jl_value_t *val); diff --git a/src/init.c b/src/init.c index fef3e73bd2d06a..925ef0018048fd 100644 --- a/src/init.c +++ b/src/init.c @@ -246,25 +246,16 @@ JL_DLLEXPORT void jl_atexit_hook(int exitcode) JL_NOTSAFEPOINT_ENTER jl_task_t *ct = jl_get_current_task(); - if (ct) { - if (exitcode == 0) - jl_write_compiler_output(); + if (ct == NULL && jl_base_module) { + ct = container_of(jl_adopt_thread(), jl_task_t, gcstack); + } + else if (ct != NULL) { // we are about to start tearing everything down, so lets try not to get // upset by the local mess of things when we run the user's _atexit hooks // this also forces us into a GC-unsafe region without a safepoint jl_task_frame_noreturn(ct); - } - - if (ct == NULL && jl_base_module) - ct = container_of(jl_adopt_thread(), jl_task_t, gcstack); - else if (ct != NULL) jl_gc_safepoint_(ct->ptls); - - jl_print_gc_stats(JL_STDERR); - if (jl_options.code_coverage) - jl_write_coverage_data(jl_options.output_code_coverage); - if (jl_options.malloc_log) - jl_write_malloc_log(); + } if (jl_base_module) { jl_value_t *f = jl_get_global(jl_base_module, jl_symbol("_atexit")); @@ -290,6 +281,15 @@ JL_DLLEXPORT void jl_atexit_hook(int exitcode) JL_NOTSAFEPOINT_ENTER } } + if (ct && exitcode == 0) + jl_write_compiler_output(); + + jl_print_gc_stats(JL_STDERR); + if (jl_options.code_coverage) + jl_write_coverage_data(jl_options.output_code_coverage); + if (jl_options.malloc_log) + jl_write_malloc_log(); + // replace standard output streams with something that we can still print to // after the finalizers from base/stream.jl close the TTY JL_STDOUT = (uv_stream_t*) STDOUT_FILENO; @@ -575,6 +575,14 @@ static char *abspath(const char *in, int nprefix) } } #else + // GetFullPathName intentionally errors if given an empty string so manually insert `.` to invoke cwd + char *in2 = (char*)malloc_s(JL_PATH_MAX); + if (strlen(in) - nprefix == 0) { + memcpy(in2, in, nprefix); + in2[nprefix] = '.'; + in2[nprefix+1] = '\0'; + in = in2; + } DWORD n = GetFullPathName(in + nprefix, 0, NULL, NULL); if (n <= 0) { jl_error("fatal error: jl_options.image_file path too long or GetFullPathName failed"); @@ -585,6 +593,7 @@ static char *abspath(const char *in, int nprefix) jl_error("fatal error: jl_options.image_file path too long or GetFullPathName failed"); } memcpy(out, in, nprefix); + free(in2); #endif return out; } @@ -680,7 +689,7 @@ static void jl_resolve_sysimg_location(JL_IMAGE_SEARCH rel) if (jl_options.output_code_coverage) jl_options.output_code_coverage = absformat(jl_options.output_code_coverage); if (jl_options.tracked_path) - jl_options.tracked_path = absformat(jl_options.tracked_path); + jl_options.tracked_path = abspath(jl_options.tracked_path, 0); const char **cmdp = jl_options.cmds; if (cmdp) { @@ -710,6 +719,7 @@ extern jl_mutex_t jl_modules_mutex; extern jl_mutex_t precomp_statement_out_lock; extern jl_mutex_t newly_inferred_mutex; extern jl_mutex_t global_roots_lock; +extern jl_mutex_t profile_show_peek_cond_lock; static void restore_fp_env(void) { @@ -729,6 +739,7 @@ static void init_global_mutexes(void) { JL_MUTEX_INIT(&global_roots_lock, "global_roots_lock"); JL_MUTEX_INIT(&jl_codegen_lock, "jl_codegen_lock"); JL_MUTEX_INIT(&typecache_lock, "typecache_lock"); + JL_MUTEX_INIT(&profile_show_peek_cond_lock, "profile_show_peek_cond_lock"); } JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) @@ -803,11 +814,6 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) #endif #endif - if ((jl_options.outputo || jl_options.outputbc || jl_options.outputasm) && - (jl_options.code_coverage || jl_options.malloc_log)) { - jl_error("cannot generate code-coverage or track allocation information while generating a .o, .bc, or .s output file"); - } - jl_init_rand(); jl_init_runtime_ccall(); jl_init_tasks(); diff --git a/src/interpreter.c b/src/interpreter.c index f6000b6aeca925..d9536ca512889d 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -683,7 +683,7 @@ jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *mi, size_t world) } } if (!src || !jl_is_code_info(src)) { - jl_error("source missing for method called in interpreter"); + jl_throw(jl_new_struct(jl_missingcodeerror_type, (jl_value_t*)mi)); } return src; } diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index b4d493ee808008..81fc9ec1af831c 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -173,12 +173,7 @@ static Type *INTT(Type *t, const DataLayout &DL) static Value *uint_cnvt(jl_codectx_t &ctx, Type *to, Value *x) { - Type *t = x->getType(); - if (t == to) - return x; - if (to->getPrimitiveSizeInBits() < x->getType()->getPrimitiveSizeInBits()) - return ctx.builder.CreateTrunc(x, to); - return ctx.builder.CreateZExt(x, to); + return ctx.builder.CreateZExtOrTrunc(x, to); } static Constant *julia_const_to_llvm(jl_codectx_t &ctx, const void *ptr, jl_datatype_t *bt) @@ -317,25 +312,90 @@ static Constant *julia_const_to_llvm(jl_codectx_t &ctx, jl_value_t *e) return julia_const_to_llvm(ctx, e, (jl_datatype_t*)bt); } +static Constant *undef_value_for_type(Type *T) { + auto tracked = CountTrackedPointers(T); + Constant *undef; + if (tracked.count) + // make sure gc pointers (including ptr_phi of union-split) are initialized to NULL + undef = Constant::getNullValue(T); + else + undef = UndefValue::get(T); + return undef; +} + +// rebuild a struct type with any i1 Bool (e.g. the llvmcall type) widened to i8 (the native size for memcpy) +static Type *zext_struct_type(Type *T) +{ + if (auto *AT = dyn_cast(T)) { + return ArrayType::get(AT->getElementType(), AT->getNumElements()); + } + else if (auto *ST = dyn_cast(T)) { + SmallVector Elements(ST->element_begin(), ST->element_end()); + for (size_t i = 0; i < Elements.size(); i++) { + Elements[i] = zext_struct_type(Elements[i]); + } + return StructType::get(ST->getContext(), Elements, ST->isPacked()); + } + else if (auto *VT = dyn_cast(T)) { + return VectorType::get(zext_struct_type(VT->getElementType()), VT); + } + else if (auto *IT = dyn_cast(T)) { + unsigned BitWidth = IT->getBitWidth(); + if (alignTo(BitWidth, 8) != BitWidth) + return IntegerType::get(IT->getContext(), alignTo(BitWidth, 8)); + } + return T; +} + +// rebuild a struct with any i1 Bool (e.g. the llvmcall type) widened to i8 (the native size for memcpy) +static Value *zext_struct_helper(jl_codectx_t &ctx, Value *V, Type *T2) +{ + Type *T = V->getType(); + if (T == T2) + return V; + if (auto *AT = dyn_cast(T2)) { + Value *V2 = undef_value_for_type(AT); + for (size_t i = 0; i < AT->getNumElements(); i++) { + Value *E = zext_struct_helper(ctx, ctx.builder.CreateExtractValue(V, i), AT->getElementType()); + V2 = ctx.builder.CreateInsertValue(V2, E, i); + } + return V2; + } + else if (auto *ST = dyn_cast(T2)) { + Value *V2 = undef_value_for_type(ST); + for (size_t i = 0; i < ST->getNumElements(); i++) { + Value *E = zext_struct_helper(ctx, ctx.builder.CreateExtractValue(V, i), ST->getElementType(i)); + V2 = ctx.builder.CreateInsertValue(V2, E, i); + } + return V2; + } + else if (T2->isIntegerTy() || T2->isVectorTy()) { + return ctx.builder.CreateZExt(V, T2); + } + return V; +} + +static Value *zext_struct(jl_codectx_t &ctx, Value *V) +{ + return zext_struct_helper(ctx, V, zext_struct_type(V->getType())); +} + static Value *emit_unboxed_coercion(jl_codectx_t &ctx, Type *to, Value *unboxed) { + if (unboxed->getType() == to) + return unboxed; + if (CastInst::castIsValid(Instruction::Trunc, unboxed, to)) + return ctx.builder.CreateTrunc(unboxed, to); + unboxed = zext_struct(ctx, unboxed); Type *ty = unboxed->getType(); if (ty == to) return unboxed; bool frompointer = ty->isPointerTy(); bool topointer = to->isPointerTy(); const DataLayout &DL = jl_Module->getDataLayout(); - if (ty->isIntegerTy(1) && to->isIntegerTy(8)) { - // bools may be stored internally as int8 - unboxed = ctx.builder.CreateZExt(unboxed, to); - } - else if (ty->isIntegerTy(8) && to->isIntegerTy(1)) { - // bools may be stored internally as int8 - unboxed = ctx.builder.CreateTrunc(unboxed, to); - } - else if (ty->isVoidTy() || DL.getTypeSizeInBits(ty) != DL.getTypeSizeInBits(to)) { + if (ty->isVoidTy() || DL.getTypeSizeInBits(ty) != DL.getTypeSizeInBits(to)) { // this can happen in dead code - //emit_unreachable(ctx); + CreateTrap(ctx.builder); return UndefValue::get(to); } if (frompointer && topointer) { @@ -380,7 +440,7 @@ static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_va if (type_is_ghost(to)) { return NULL; } - //emit_unreachable(ctx); + CreateTrap(ctx.builder); return UndefValue::get(to); // type mismatch error } @@ -446,17 +506,9 @@ static void emit_unbox_store(jl_codectx_t &ctx, const jl_cgval_t &x, Value *dest return; } - Value *unboxed = nullptr; - if (!x.ispointer()) { // already unboxed, but sometimes need conversion - unboxed = x.V; - assert(unboxed); - } - - // bools stored as int8, but can be narrowed to int1 often - if (x.typ == (jl_value_t*)jl_bool_type) - unboxed = emit_unbox(ctx, getInt8Ty(ctx.builder.getContext()), x, (jl_value_t*)jl_bool_type); - - if (unboxed) { + if (!x.ispointer()) { // already unboxed, but sometimes need conversion (e.g. f32 -> i32) + assert(x.V); + Value *unboxed = zext_struct(ctx, x.V); Type *dest_ty = unboxed->getType()->getPointerTo(); if (dest->getType() != dest_ty) dest = emit_bitcast(ctx, dest, dest_ty); @@ -587,7 +639,8 @@ static jl_cgval_t generic_bitcast(jl_codectx_t &ctx, const jl_cgval_t *argv) return mark_julia_type(ctx, vx, false, bt); } else { - Value *box = emit_allocobj(ctx, nb, bt_value_rt); + unsigned align = sizeof(void*); // Allocations are at least pointer aligned + Value *box = emit_allocobj(ctx, nb, bt_value_rt, true, align); setName(ctx.emission_context, box, "bitcast_box"); init_bits_value(ctx, box, vx, ctx.tbaa().tbaa_immut); return mark_julia_type(ctx, box, true, bt->name->wrapper); @@ -646,7 +699,8 @@ static jl_cgval_t generic_cast( else { Value *targ_rt = boxed(ctx, targ); emit_concretecheck(ctx, targ_rt, std::string(jl_intrinsic_name(f)) + ": target type not a leaf primitive type"); - Value *box = emit_allocobj(ctx, nb, targ_rt); + unsigned align = sizeof(void*); // Allocations are at least pointer aligned + Value *box = emit_allocobj(ctx, nb, targ_rt, true, align); setName(ctx.emission_context, box, "cast_box"); init_bits_value(ctx, box, ans, ctx.tbaa().tbaa_immut); return mark_julia_type(ctx, box, true, jlto->name->wrapper); @@ -697,7 +751,7 @@ static jl_cgval_t emit_pointerref(jl_codectx_t &ctx, jl_cgval_t *argv) else if (!deserves_stack(ety)) { assert(jl_is_datatype(ety)); uint64_t size = jl_datatype_size(ety); - Value *strct = emit_allocobj(ctx, (jl_datatype_t*)ety); + Value *strct = emit_allocobj(ctx, (jl_datatype_t*)ety, true); setName(ctx.emission_context, strct, "pointerref_box"); im1 = ctx.builder.CreateMul(im1, ConstantInt::get(ctx.types().T_size, LLT_ALIGN(size, jl_datatype_align(ety)))); @@ -853,7 +907,7 @@ static jl_cgval_t emit_atomic_pointerref(jl_codectx_t &ctx, jl_cgval_t *argv) if (!deserves_stack(ety)) { assert(jl_is_datatype(ety)); - Value *strct = emit_allocobj(ctx, (jl_datatype_t*)ety); + Value *strct = emit_allocobj(ctx, (jl_datatype_t*)ety, true); setName(ctx.emission_context, strct, "atomic_pointerref_box"); Value *thePtr = emit_unbox(ctx, getInt8PtrTy(ctx.builder.getContext()), e, e.typ); Type *loadT = Type::getIntNTy(ctx.builder.getContext(), nb * 8); @@ -1445,12 +1499,7 @@ static Value *emit_untyped_intrinsic(jl_codectx_t &ctx, intrinsic f, Value **arg Intrinsic::smul_with_overflow : Intrinsic::umul_with_overflow))))); FunctionCallee intr = Intrinsic::getDeclaration(jl_Module, intr_id, ArrayRef(t)); - Value *res = ctx.builder.CreateCall(intr, {x, y}); - Value *val = ctx.builder.CreateExtractValue(res, ArrayRef(0)); - setName(ctx.emission_context, val, "checked"); - Value *obit = ctx.builder.CreateExtractValue(res, ArrayRef(1)); - setName(ctx.emission_context, obit, "overflow"); - Value *obyte = ctx.builder.CreateZExt(obit, getInt8Ty(ctx.builder.getContext())); + Value *tupval = ctx.builder.CreateCall(intr, {x, y}); jl_value_t *params[2]; params[0] = xtyp; @@ -1458,10 +1507,6 @@ static Value *emit_untyped_intrinsic(jl_codectx_t &ctx, intrinsic f, Value **arg jl_datatype_t *tuptyp = (jl_datatype_t*)jl_apply_tuple_type_v(params, 2); *newtyp = tuptyp; - Value *tupval; - tupval = UndefValue::get(julia_type_to_llvm(ctx, (jl_value_t*)tuptyp)); - tupval = ctx.builder.CreateInsertValue(tupval, val, ArrayRef(0)); - tupval = ctx.builder.CreateInsertValue(tupval, obyte, ArrayRef(1)); return tupval; } diff --git a/src/ircode.c b/src/ircode.c index e660b8a463aa00..6ed1740b3667ac 100644 --- a/src/ircode.c +++ b/src/ircode.c @@ -788,6 +788,18 @@ static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED typedef jl_value_t jl_string_t; // for local expressibility +#define IR_DATASIZE_FLAGS sizeof(uint8_t) +#define IR_DATASIZE_PURITY sizeof(uint16_t) +#define IR_DATASIZE_INLINING_COST sizeof(uint16_t) +#define IR_DATASIZE_NSLOTS sizeof(int32_t) +typedef enum { + ir_offset_flags = 0, + ir_offset_purity = 0 + IR_DATASIZE_FLAGS, + ir_offset_inlining_cost = 0 + IR_DATASIZE_FLAGS + IR_DATASIZE_PURITY, + ir_offset_nslots = 0 + IR_DATASIZE_FLAGS + IR_DATASIZE_PURITY + IR_DATASIZE_INLINING_COST, + ir_offset_slotflags = 0 + IR_DATASIZE_FLAGS + IR_DATASIZE_PURITY + IR_DATASIZE_INLINING_COST + IR_DATASIZE_NSLOTS +} ir_offset; + JL_DLLEXPORT jl_string_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) { JL_TIMING(AST_COMPRESS, AST_COMPRESS); @@ -813,12 +825,16 @@ JL_DLLEXPORT jl_string_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) jl_code_info_flags_t flags = code_info_flags(code->inferred, code->propagate_inbounds, code->has_fcall, code->nospecializeinfer, code->inlining, code->constprop); write_uint8(s.s, flags.packed); - write_uint8(s.s, code->purity.bits); + static_assert(sizeof(flags.packed) == IR_DATASIZE_FLAGS, "ir_datasize_flags is mismatched with the actual size"); + write_uint16(s.s, code->purity.bits); + static_assert(sizeof(code->purity.bits) == IR_DATASIZE_PURITY, "ir_datasize_purity is mismatched with the actual size"); write_uint16(s.s, code->inlining_cost); + static_assert(sizeof(code->inlining_cost) == IR_DATASIZE_INLINING_COST, "ir_datasize_inlining_cost is mismatched with the actual size"); - size_t nslots = jl_array_nrows(code->slotflags); + int32_t nslots = jl_array_nrows(code->slotflags); assert(nslots >= m->nargs && nslots < INT32_MAX); // required by generated functions write_int32(s.s, nslots); + static_assert(sizeof(nslots) == IR_DATASIZE_NSLOTS, "ir_datasize_nslots is mismatched with the actual size"); ios_write(s.s, jl_array_data(code->slotflags, const char), nslots); // N.B.: The layout of everything before this point is explicitly referenced @@ -911,7 +927,7 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t code->propagate_inbounds = flags.bits.propagate_inbounds; code->has_fcall = flags.bits.has_fcall; code->nospecializeinfer = flags.bits.nospecializeinfer; - code->purity.bits = read_uint8(s.s); + code->purity.bits = read_uint16(s.s); code->inlining_cost = read_uint16(s.s); size_t nslots = read_int32(&src); @@ -973,7 +989,7 @@ JL_DLLEXPORT uint8_t jl_ir_flag_inferred(jl_string_t *data) return ((jl_code_info_t*)data)->inferred; assert(jl_is_string(data)); jl_code_info_flags_t flags; - flags.packed = jl_string_data(data)[0]; + flags.packed = jl_string_data(data)[ir_offset_flags]; return flags.bits.inferred; } @@ -983,7 +999,7 @@ JL_DLLEXPORT uint8_t jl_ir_flag_inlining(jl_string_t *data) return ((jl_code_info_t*)data)->inlining; assert(jl_is_string(data)); jl_code_info_flags_t flags; - flags.packed = jl_string_data(data)[0]; + flags.packed = jl_string_data(data)[ir_offset_flags]; return flags.bits.inlining; } @@ -993,7 +1009,7 @@ JL_DLLEXPORT uint8_t jl_ir_flag_has_fcall(jl_string_t *data) return ((jl_code_info_t*)data)->has_fcall; assert(jl_is_string(data)); jl_code_info_flags_t flags; - flags.packed = jl_string_data(data)[0]; + flags.packed = jl_string_data(data)[ir_offset_flags]; return flags.bits.has_fcall; } @@ -1002,7 +1018,7 @@ JL_DLLEXPORT uint16_t jl_ir_inlining_cost(jl_string_t *data) if (jl_is_code_info(data)) return ((jl_code_info_t*)data)->inlining_cost; assert(jl_is_string(data)); - uint16_t res = jl_load_unaligned_i16(jl_string_data(data) + 2); + uint16_t res = jl_load_unaligned_i16(jl_string_data(data) + ir_offset_inlining_cost); return res; } @@ -1040,7 +1056,7 @@ JL_DLLEXPORT ssize_t jl_ir_nslots(jl_value_t *data) } else { assert(jl_is_string(data)); - int nslots = jl_load_unaligned_i32(jl_string_data(data) + 2 + sizeof(uint16_t)); + int nslots = jl_load_unaligned_i32(jl_string_data(data) + ir_offset_nslots); return nslots; } } @@ -1053,7 +1069,7 @@ JL_DLLEXPORT uint8_t jl_ir_slotflag(jl_string_t *data, size_t i) return jl_array_data(slotflags, uint8_t)[i]; } assert(jl_is_string(data)); - return jl_string_data(data)[2 + sizeof(uint16_t) + sizeof(int32_t) + i]; + return jl_string_data(data)[ir_offset_slotflags + i]; } JL_DLLEXPORT jl_array_t *jl_uncompress_argnames(jl_value_t *syms) diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index a22a5d8a6a2647..2de54559033756 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -1410,10 +1410,12 @@ namespace { struct JITPointersT { - JITPointersT(orc::ExecutionSession &ES) JL_NOTSAFEPOINT : ES(ES) {} + JITPointersT(SharedBytesT &SharedBytes, std::mutex &Lock) JL_NOTSAFEPOINT + : SharedBytes(SharedBytes), Lock(Lock) {} Expected operator()(orc::ThreadSafeModule TSM, orc::MaterializationResponsibility &R) JL_NOTSAFEPOINT { TSM.withModuleDo([&](Module &M) JL_NOTSAFEPOINT { + std::lock_guard locked(Lock); for (auto &GV : make_early_inc_range(M.globals())) { if (auto *Shared = getSharedBytes(GV)) { ++InternedGlobals; @@ -1429,10 +1431,11 @@ namespace { return std::move(TSM); } + private: // optimize memory by turning long strings into memoized copies, instead of // making a copy per object file of output. - // we memoize them using the ExecutionSession's string pool; - // this makes it unsafe to call clearDeadEntries() on the pool. + // we memoize them using a StringSet with a custom-alignment allocator + // to ensure they are properly aligned Constant *getSharedBytes(GlobalVariable &GV) JL_NOTSAFEPOINT { // We could probably technically get away with // interning even external linkage globals, @@ -1458,11 +1461,17 @@ namespace { // Cutoff, since we don't want to intern small strings return nullptr; } - auto Interned = *ES.intern(Data); + Align Required = GV.getAlign().valueOrOne(); + Align Preferred = MaxAlignedAlloc::alignment(Data.size()); + if (Required > Preferred) + return nullptr; + StringRef Interned = SharedBytes.insert(Data).first->getKey(); + assert(llvm::isAddrAligned(Preferred, Interned.data())); return literal_static_pointer_val(Interned.data(), GV.getType()); } - orc::ExecutionSession &ES; + SharedBytesT &SharedBytes; + std::mutex &Lock; }; } @@ -1696,7 +1705,7 @@ JuliaOJIT::JuliaOJIT() #endif LockLayer(ObjectLayer), CompileLayer(ES, LockLayer, std::make_unique>(orc::irManglingOptionsFromTargetOptions(TM->Options), *TM)), - JITPointersLayer(ES, CompileLayer, orc::IRTransformLayer::TransformFunction(JITPointersT(ES))), + JITPointersLayer(ES, CompileLayer, orc::IRTransformLayer::TransformFunction(JITPointersT(SharedBytes, RLST_mutex))), OptimizeLayer(ES, JITPointersLayer, orc::IRTransformLayer::TransformFunction(OptimizerT(*TM, PrintLLVMTimers))), OptSelLayer(ES, OptimizeLayer, orc::IRTransformLayer::TransformFunction(selectOptLevel)), DepsVerifyLayer(ES, OptSelLayer, orc::IRTransformLayer::TransformFunction(validateExternRelocations)), diff --git a/src/jitlayers.h b/src/jitlayers.h index 016f97b92140b6..85cbb2cf8ec286 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -1,6 +1,8 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license #include +#include +#include #include #include @@ -292,6 +294,44 @@ static const inline char *name_from_method_instance(jl_method_instance_t *li) JL return jl_is_method(li->def.method) ? jl_symbol_name(li->def.method->name) : "top-level scope"; } +template +class MaxAlignedAllocImpl + : public AllocatorBase> { + +public: + MaxAlignedAllocImpl() JL_NOTSAFEPOINT = default; + + static Align alignment(size_t Size) JL_NOTSAFEPOINT { + // Define the maximum alignment we expect to require, from offset bytes off + // the returned pointer, this is >= alignof(std::max_align_t), which is too + // small often to actually use. + const size_t MaxAlignment = JL_CACHE_BYTE_ALIGNMENT; + return Align(std::min((size_t)llvm::PowerOf2Ceil(Size), MaxAlignment)); + } + + LLVM_ATTRIBUTE_RETURNS_NONNULL void *Allocate(size_t Size, Align Alignment) { + Align MaxAlign = alignment(Size); + assert(Alignment < MaxAlign); (void)Alignment; + return jl_gc_perm_alloc(Size, 0, MaxAlign.value(), offset); + } + + inline LLVM_ATTRIBUTE_RETURNS_NONNULL + void * Allocate(size_t Size, size_t Alignment) { + return Allocate(Size, Align(Alignment)); + } + + // Pull in base class overloads. + using AllocatorBase::Allocate; + + void Deallocate(const void *Ptr, size_t Size, size_t /*Alignment*/) { abort(); } + + // Pull in base class overloads. + using AllocatorBase::Deallocate; + +private: +}; +using MaxAlignedAlloc = MaxAlignedAllocImpl<>; + typedef JITSymbol JL_JITSymbol; // The type that is similar to SymbolInfo on LLVM 4.0 is actually // `JITEvaluatedSymbol`. However, we only use this type when a JITSymbol @@ -300,6 +340,7 @@ typedef JITSymbol JL_SymbolInfo; using CompilerResultT = Expected>; using OptimizerResultT = Expected; +using SharedBytesT = StringSet::MapEntryTy)>>; class JuliaOJIT { public: @@ -516,6 +557,7 @@ class JuliaOJIT { // Note that this is a safepoint due to jl_get_library_ and jl_dlsym calls void optimizeDLSyms(Module &M); + private: const std::unique_ptr TM; @@ -529,6 +571,7 @@ class JuliaOJIT { std::mutex RLST_mutex{}; int RLST_inc = 0; DenseMap ReverseLocalSymbolTable; + SharedBytesT SharedBytes; std::unique_ptr DLSymOpt; diff --git a/src/jl_exported_data.inc b/src/jl_exported_data.inc index 11617bea9ec651..b6b9774c1538e5 100644 --- a/src/jl_exported_data.inc +++ b/src/jl_exported_data.inc @@ -84,6 +84,7 @@ XX(jl_method_match_type) \ XX(jl_method_type) \ XX(jl_methtable_type) \ + XX(jl_missingcodeerror_type) \ XX(jl_module_type) \ XX(jl_n_threads_per_pool) \ XX(jl_namedtuple_type) \ diff --git a/src/jl_uv.c b/src/jl_uv.c index 281dd798dbb36d..8cdd8f95ba2e62 100644 --- a/src/jl_uv.c +++ b/src/jl_uv.c @@ -51,9 +51,9 @@ static void walk_print_cb(uv_handle_t *h, void *arg) npad += strlen(type); pad += npad < strlen(pad) ? npad : strlen(pad); if (fd == -1) - jl_safe_printf(" %s %s@%p->%p\n", type, pad, (void*)h, (void*)h->data); + jl_safe_printf(" %s %s%p->%p\n", type, pad, (void*)h, (void*)h->data); else - jl_safe_printf(" %s[%zd] %s@%p->%p\n", type, (size_t)fd, pad, (void*)h, (void*)h->data); + jl_safe_printf(" %s[%zd] %s%p->%p\n", type, (size_t)fd, pad, (void*)h, (void*)h->data); } static void wait_empty_func(uv_timer_t *t) @@ -63,9 +63,12 @@ static void wait_empty_func(uv_timer_t *t) if (!uv_loop_alive(t->loop)) return; jl_safe_printf("\n[pid %zd] waiting for IO to finish:\n" - " TYPE[FD/PID] @UV_HANDLE_T->DATA\n", + " Handle type uv_handle_t->data\n", (size_t)uv_os_getpid()); uv_walk(jl_io_loop, walk_print_cb, NULL); + if (jl_generating_output() && jl_options.incremental) { + jl_safe_printf("This means that a package has started a background task or event source that has not finished running. For precompilation to complete successfully, the event source needs to be closed explicitly. See the developer documentation on fixing precompilation hangs for more help.\n"); + } jl_gc_collect(JL_GC_FULL); } @@ -83,12 +86,10 @@ void jl_wait_empty_begin(void) } JL_UV_UNLOCK(); } - void jl_wait_empty_end(void) { - JL_UV_LOCK(); + // n.b. caller must be holding jl_uv_mutex uv_close((uv_handle_t*)&wait_empty_worker, NULL); - JL_UV_UNLOCK(); } diff --git a/src/jlfrontend.scm b/src/jlfrontend.scm index 34a8f5405676ae..b46663c5603468 100644 --- a/src/jlfrontend.scm +++ b/src/jlfrontend.scm @@ -31,6 +31,7 @@ ;; this is overwritten when we run in actual julia (define (defined-julia-global v) #f) +(define (nothrow-julia-global v) #f) (define (julia-current-file) 'none) (define (julia-current-line) 0) diff --git a/src/jloptions.c b/src/jloptions.c index f9443ed8f57046..cc00e102c16b14 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -333,7 +333,6 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) const char **cmds = NULL; int codecov = JL_LOG_NONE; int malloclog = JL_LOG_NONE; - int pkgimage_explicit = 0; int argc = *argcp; char **argv = *argvp; char *endptr; @@ -469,13 +468,12 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) jl_errorf("julia: invalid argument to --compiled-modules={yes|no|existing} (%s)", optarg); break; case opt_pkgimages: - pkgimage_explicit = 1; if (!strcmp(optarg,"yes")) jl_options.use_pkgimages = JL_OPTIONS_USE_PKGIMAGES_YES; else if (!strcmp(optarg,"no")) jl_options.use_pkgimages = JL_OPTIONS_USE_PKGIMAGES_NO; else - jl_errorf("julia: invalid argument to --pkgimage={yes|no} (%s)", optarg); + jl_errorf("julia: invalid argument to --pkgimages={yes|no} (%s)", optarg); break; case 'C': // cpu-target jl_options.cpu_target = strdup(optarg); @@ -860,13 +858,6 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) "This is a bug, please report it.", c); } } - if (codecov || malloclog) { - if (pkgimage_explicit && jl_options.use_pkgimages) { - jl_errorf("julia: Can't use --pkgimages=yes together " - "with --track-allocation or --code-coverage."); - } - jl_options.use_pkgimages = 0; - } jl_options.code_coverage = codecov; jl_options.malloc_log = malloclog; int proc_args = *argcp < optind ? *argcp : optind; diff --git a/src/jltypes.c b/src/jltypes.c index c55c6f47f064c1..b1830ec4e765e6 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3132,7 +3132,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_bool_type, jl_uint8_type, jl_uint8_type, - jl_uint8_type, + jl_uint16_type, jl_uint16_type), jl_emptysvec, 0, 1, 22); @@ -3201,7 +3201,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_bool_type, jl_uint8_type, jl_uint8_type, - jl_uint8_type), + jl_uint16_type), jl_emptysvec, 0, 1, 10); //const static uint32_t method_constfields[1] = { 0x03fc065f }; // (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<6)|(1<<9)|(1<<10)|(1<<18)|(1<<19)|(1<<20)|(1<<21)|(1<<22)|(1<<23)|(1<<24)|(1<<25); @@ -3255,7 +3255,7 @@ void jl_init_types(void) JL_GC_DISABLED //"edges", //"absolute_max", "ipo_purity_bits", "purity_bits", - "argescapes", + "analysis_results", "isspecsig", "precompile", "relocatability", "invoke", "specptr"), // function object decls jl_svec(15, @@ -3507,6 +3507,7 @@ void post_boot_hooks(void) jl_methoderror_type = (jl_datatype_t*)core("MethodError"); jl_loaderror_type = (jl_datatype_t*)core("LoadError"); jl_initerror_type = (jl_datatype_t*)core("InitError"); + jl_missingcodeerror_type = (jl_datatype_t*)core("MissingCodeError"); jl_pair_type = core("Pair"); jl_kwcall_func = core("kwcall"); jl_kwcall_mt = ((jl_datatype_t*)jl_typeof(jl_kwcall_func))->name->mt; diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index c1e9fcf429c667..2937f31ecc0f2f 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -4254,17 +4254,21 @@ f(x) = yt(x) (else (for-each linearize (cdr e)))) e) +;; N.B.: This assumes that resolve-scopes has run, so outerref is equivalent to +;; a global in the current scope. (define (valid-ir-argument? e) - (or (simple-atom? e) (symbol? e) + (or (simple-atom? e) + (and (outerref? e) (nothrow-julia-global (cadr e))) + (and (globalref? e) (nothrow-julia-global (cadr e) (caddr e))) (and (pair? e) - (memq (car e) '(quote inert top core globalref outerref + (memq (car e) '(quote inert top core slot static_parameter))))) (define (valid-ir-rvalue? lhs e) (or (ssavalue? lhs) (valid-ir-argument? e) (and (symbol? lhs) (pair? e) - (memq (car e) '(new splatnew the_exception isdefined call invoke foreigncall cfunction gc_preserve_begin copyast new_opaque_closure))))) + (memq (car e) '(new splatnew the_exception isdefined call invoke foreigncall cfunction gc_preserve_begin copyast new_opaque_closure globalref outerref))))) (define (valid-ir-return? e) ;; returning lambda directly is needed for @generated @@ -4410,48 +4414,59 @@ f(x) = yt(x) (else (string "\"" h "\" expression")))) (if (not (null? (cadr lam))) (error (string (head-to-text (car e)) " not at top level")))) + (define (valid-body-ir-argument? aval) + (or (valid-ir-argument? aval) + (and (symbol? aval) ; Arguments are always defined slots + (or (memq aval (lam:args lam)) + (let ((vi (get vinfo-table aval #f))) + (and vi (vinfo:never-undef vi))))))) + (define (single-assign-var? aval) + (and (symbol? aval) ; Arguments are always sa + (or (memq aval (lam:args lam)) + (let ((vi (get vinfo-table aval #f))) + (and vi (vinfo:sa vi)))))) + ;; TODO: We could also allow const globals here + (define (const-read-arg? x) + ;; Even if we have side effects, we know that singly-assigned + ;; locals cannot be affected them, so we can inline them anyway. + (or (simple-atom? x) (single-assign-var? x) + (and (pair? x) + (memq (car x) '(quote inert top core))))) ;; evaluate the arguments of a call, creating temporary locations as needed (define (compile-args lst break-labels) (if (null? lst) '() - (let ((simple? (every (lambda (x) (or (simple-atom? x) (symbol? x) - (and (pair? x) - (memq (car x) '(quote inert top core globalref outerref))))) - lst))) - (let loop ((lst lst) - (vals '())) - (if (null? lst) - (reverse! vals) - (let* ((arg (car lst)) - (aval (or (compile arg break-labels #t #f) - ;; TODO: argument exprs that don't yield a value? - '(null)))) - (loop (cdr lst) - (cons (if (and (not simple?) - (not (simple-atom? arg)) - (not (simple-atom? aval)) - (not (and (pair? arg) - (memq (car arg) '(quote inert top core)))) - (not (and (symbol? aval) ;; function args are immutable and always assigned - (memq aval (lam:args lam)))) - (not (and (or (symbol? arg) - (and (pair? arg) - (memq (car arg) '(globalref outerref)))) - (or (null? (cdr lst)) - (null? vals))))) - (let ((tmp (make-ssavalue))) - (emit `(= ,tmp ,aval)) - tmp) - aval) - vals)))))))) + ;; First check if all the arguments as simple (and therefore side-effect free). + ;; Otherwise, we need to use ssa values for all arguments to ensure proper + ;; left-to-right evaluation semantics. + (let ((simple? (every (lambda (x) (or (simple-atom? x) (symbol? x) + (and (pair? x) + (memq (car x) '(quote inert top core globalref outerref))))) + lst))) + (let loop ((lst lst) + (vals '())) + (if (null? lst) + (reverse! vals) + (let* ((arg (car lst)) + (aval (or (compile arg break-labels #t #f) + ;; TODO: argument exprs that don't yield a value? + '(null)))) + (loop (cdr lst) + (cons (if (and + (or simple? (const-read-arg? aval)) + (valid-body-ir-argument? aval)) + aval + (let ((tmp (make-ssavalue))) + (emit `(= ,tmp ,aval)) + tmp)) + vals)))))))) (define (compile-cond ex break-labels) (let ((cnd (or (compile ex break-labels #t #f) ;; TODO: condition exprs that don't yield a value? '(null)))) - (if (not (valid-ir-argument? cnd)) + (if (valid-body-ir-argument? cnd) cnd (let ((tmp (make-ssavalue))) (emit `(= ,tmp ,cnd)) - tmp) - cnd))) + tmp)))) (define (emit-cond cnd break-labels endl) (let* ((cnd (if (and (pair? cnd) (eq? (car cnd) 'block)) (flatten-ex 'block cnd) diff --git a/src/julia.h b/src/julia.h index b2f6ab8b7a62f9..acb700c0bd936e 100644 --- a/src/julia.h +++ b/src/julia.h @@ -248,20 +248,21 @@ typedef struct _jl_line_info_node_t { // the following mirrors `struct EffectsOverride` in `base/compiler/effects.jl` typedef union __jl_purity_overrides_t { struct { - uint8_t ipo_consistent : 1; - uint8_t ipo_effect_free : 1; - uint8_t ipo_nothrow : 1; - uint8_t ipo_terminates_globally : 1; + uint16_t ipo_consistent : 1; + uint16_t ipo_effect_free : 1; + uint16_t ipo_nothrow : 1; + uint16_t ipo_terminates_globally : 1; // Weaker form of `terminates` that asserts // that any control flow syntactically in the method // is guaranteed to terminate, but does not make // assertions about any called functions. - uint8_t ipo_terminates_locally : 1; - uint8_t ipo_notaskstate : 1; - uint8_t ipo_inaccessiblememonly : 1; - uint8_t ipo_noub : 1; + uint16_t ipo_terminates_locally : 1; + uint16_t ipo_notaskstate : 1; + uint16_t ipo_inaccessiblememonly : 1; + uint16_t ipo_noub : 1; + uint16_t ipo_noub_if_noinbounds : 1; } overrides; - uint8_t bits; + uint16_t bits; } _jl_purity_overrides_t; // This type describes a single function body @@ -435,7 +436,7 @@ typedef struct _jl_code_instance_t { // uint8_t nonoverlayed : 1; // uint8_t notaskstate : 2; // uint8_t inaccessiblememonly : 2; - jl_value_t *argescapes; // escape information of call arguments + jl_value_t *analysis_results; // Analysis results about this code (IPO-safe) // compilation state cache _Atomic(uint8_t) specsigflags; // & 0b001 == specptr is a specialized function signature for specTypes->rettype @@ -827,6 +828,7 @@ extern JL_DLLIMPORT jl_datatype_t *jl_typeerror_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_methoderror_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_undefvarerror_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_atomicerror_type JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_datatype_t *jl_missingcodeerror_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_lineinfonode_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_stackovf_exception JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_memory_exception JL_GLOBALLY_ROOTED; diff --git a/src/julia_internal.h b/src/julia_internal.h index 2c0c13d0bb3438..c09b73f8a1052b 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -194,7 +194,6 @@ int jl_running_under_rr(int recheck) JL_NOTSAFEPOINT; // Returns time in nanosec JL_DLLEXPORT uint64_t jl_hrtime(void) JL_NOTSAFEPOINT; -JL_DLLEXPORT void jl_set_peek_cond(uintptr_t); JL_DLLEXPORT double jl_get_profile_peek_duration(void); JL_DLLEXPORT void jl_set_profile_peek_duration(double); @@ -350,7 +349,7 @@ JL_DLLEXPORT int jl_gc_classify_pools(size_t sz, int *osize) JL_NOTSAFEPOINT; extern uv_mutex_t gc_perm_lock; void *jl_gc_perm_alloc_nolock(size_t sz, int zero, unsigned align, unsigned offset) JL_NOTSAFEPOINT; -void *jl_gc_perm_alloc(size_t sz, int zero, +JL_DLLEXPORT void *jl_gc_perm_alloc(size_t sz, int zero, unsigned align, unsigned offset) JL_NOTSAFEPOINT; void gc_sweep_sysimg(void); @@ -1370,7 +1369,7 @@ JL_DLLEXPORT int jl_stored_inline(jl_value_t *el_type); JL_DLLEXPORT jl_value_t *(jl_array_data_owner)(jl_array_t *a); JL_DLLEXPORT jl_array_t *jl_array_copy(jl_array_t *ary); -JL_DLLEXPORT uintptr_t jl_object_id_(jl_value_t *tv, jl_value_t *v) JL_NOTSAFEPOINT; +JL_DLLEXPORT uintptr_t jl_object_id_(uintptr_t tv, jl_value_t *v) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_set_next_task(jl_task_t *task) JL_NOTSAFEPOINT; // -- synchronization utilities -- // diff --git a/src/llvm-alloc-helpers.cpp b/src/llvm-alloc-helpers.cpp index 665c0b1d2e00e7..953ecc1830142a 100644 --- a/src/llvm-alloc-helpers.cpp +++ b/src/llvm-alloc-helpers.cpp @@ -125,6 +125,12 @@ JL_USED_FUNC void AllocUseInfo::dump(llvm::raw_ostream &OS) OS << "hastypeof: " << hastypeof << '\n'; OS << "refload: " << refload << '\n'; OS << "refstore: " << refstore << '\n'; + OS << "allockind:"; + if ((allockind & AllocFnKind::Uninitialized) != AllocFnKind::Unknown) + OS << " uninitialized"; + if ((allockind & AllocFnKind::Zeroed) != AllocFnKind::Unknown) + OS << " zeroed"; + OS << '\n'; OS << "Uses: " << uses.size() << '\n'; for (auto inst: uses) inst->print(OS); @@ -164,8 +170,11 @@ JL_USED_FUNC void AllocUseInfo::dump() #define REMARK(remark) #endif -void jl_alloc::runEscapeAnalysis(llvm::Instruction *I, EscapeAnalysisRequiredArgs required, EscapeAnalysisOptionalArgs options) { +void jl_alloc::runEscapeAnalysis(llvm::CallInst *I, EscapeAnalysisRequiredArgs required, EscapeAnalysisOptionalArgs options) { required.use_info.reset(); + Attribute allockind = I->getFnAttr(Attribute::AllocKind); + if (allockind.isValid()) + required.use_info.allockind = allockind.getAllocKind(); if (I->use_empty()) return; CheckInst::Frame cur{I, 0, I->use_begin(), I->use_end()}; diff --git a/src/llvm-alloc-helpers.h b/src/llvm-alloc-helpers.h index 3bd80704a0888c..49c3b15332a56f 100644 --- a/src/llvm-alloc-helpers.h +++ b/src/llvm-alloc-helpers.h @@ -87,6 +87,8 @@ namespace jl_alloc { bool returned:1; // The object is used in an error function bool haserror:1; + // For checking attributes of "uninitialized" or "zeroed" or unknown + llvm::AllocFnKind allockind; // The alloc has a Julia object reference not in an explicit field. bool has_unknown_objref:1; @@ -105,6 +107,7 @@ namespace jl_alloc { hasunknownmem = false; returned = false; haserror = false; + allockind = llvm::AllocFnKind::Unknown; has_unknown_objref = false; has_unknown_objrefaggr = false; uses.clear(); @@ -153,7 +156,7 @@ namespace jl_alloc { } }; - void runEscapeAnalysis(llvm::Instruction *I, EscapeAnalysisRequiredArgs required, EscapeAnalysisOptionalArgs options=EscapeAnalysisOptionalArgs()); + void runEscapeAnalysis(llvm::CallInst *I, EscapeAnalysisRequiredArgs required, EscapeAnalysisOptionalArgs options=EscapeAnalysisOptionalArgs()); } diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index 619fa03ef58dd6..5df4f52aca425c 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -135,12 +135,13 @@ struct Optimizer { // insert llvm.lifetime.* calls for `ptr` with size `sz` based on the use of `orig`. void insertLifetime(Value *ptr, Constant *sz, Instruction *orig); - void checkInst(Instruction *I); + void checkInst(CallInst *I); void replaceIntrinsicUseWith(IntrinsicInst *call, Intrinsic::ID ID, Instruction *orig_i, Instruction *new_i); void removeAlloc(CallInst *orig_inst); - void moveToStack(CallInst *orig_inst, size_t sz, bool has_ref); + void moveToStack(CallInst *orig_inst, size_t sz, bool has_ref, AllocFnKind allockind); + void initializeAlloca(IRBuilder<> &prolog_builder, AllocaInst *buff, AllocFnKind allockind); void splitOnStack(CallInst *orig_inst); void optimizeTag(CallInst *orig_inst); @@ -288,7 +289,7 @@ void Optimizer::optimizeAll() << "GC allocation moved to stack " << ore::NV("GC Allocation", orig); }); // The object has no fields with mix reference access - moveToStack(orig, sz, has_ref); + moveToStack(orig, sz, has_ref, use_info.allockind); } } @@ -355,7 +356,7 @@ ssize_t Optimizer::getGCAllocSize(Instruction *I) return -1; } -void Optimizer::checkInst(Instruction *I) +void Optimizer::checkInst(CallInst *I) { LLVM_DEBUG(dbgs() << "Running escape analysis on " << *I << "\n"); jl_alloc::EscapeAnalysisRequiredArgs required{use_info, check_stack, pass, *pass.DL}; @@ -598,9 +599,25 @@ void Optimizer::replaceIntrinsicUseWith(IntrinsicInst *call, Intrinsic::ID ID, call->eraseFromParent(); } +void Optimizer::initializeAlloca(IRBuilder<> &prolog_builder, AllocaInst *buff, AllocFnKind allockind) +{ + if ((allockind & AllocFnKind::Uninitialized) != AllocFnKind::Unknown) + return; + assert(!buff->isArrayAllocation()); + Type *T = buff->getAllocatedType(); + Value *Init = UndefValue::get(T); + if ((allockind & AllocFnKind::Zeroed) != AllocFnKind::Unknown) + Init = Constant::getNullValue(T); // zero, as described + else if (allockind == AllocFnKind::Unknown) + Init = Constant::getNullValue(T); // assume zeroed since we didn't find the attribute + else + Init = prolog_builder.CreateFreeze(UndefValue::get(T)); // assume freeze, since LLVM does not natively support this case + prolog_builder.CreateStore(Init, buff); +} + // This function should not erase any safepoint so that the lifetime marker can find and cache // all the original safepoints. -void Optimizer::moveToStack(CallInst *orig_inst, size_t sz, bool has_ref) +void Optimizer::moveToStack(CallInst *orig_inst, size_t sz, bool has_ref, AllocFnKind allockind) { ++RemovedAllocs; ++StackAllocs; @@ -643,6 +660,10 @@ void Optimizer::moveToStack(CallInst *orig_inst, size_t sz, bool has_ref) ptr = cast(prolog_builder.CreateBitCast(buff, Type::getInt8PtrTy(prolog_builder.getContext(), buff->getType()->getPointerAddressSpace()))); } insertLifetime(ptr, ConstantInt::get(Type::getInt64Ty(prolog_builder.getContext()), sz), orig_inst); + if (sz != 0 && !has_ref) { // TODO: fix has_ref case too + IRBuilder<> builder(orig_inst); + initializeAlloca(builder, buff, allockind); + } Instruction *new_inst = cast(prolog_builder.CreateBitCast(ptr, JuliaType::get_pjlvalue_ty(prolog_builder.getContext(), buff->getType()->getPointerAddressSpace()))); new_inst->takeName(orig_inst); @@ -927,8 +948,10 @@ void Optimizer::splitOnStack(CallInst *orig_inst) allocty = ArrayType::get(Type::getInt8Ty(pass.getLLVMContext()), field.size); } slot.slot = prolog_builder.CreateAlloca(allocty); + IRBuilder<> builder(orig_inst); insertLifetime(prolog_builder.CreateBitCast(slot.slot, Type::getInt8PtrTy(prolog_builder.getContext())), ConstantInt::get(Type::getInt64Ty(prolog_builder.getContext()), field.size), orig_inst); + initializeAlloca(builder, slot.slot, use_info.allockind); slots.push_back(std::move(slot)); } const auto nslots = slots.size(); diff --git a/src/llvm-pass-helpers.cpp b/src/llvm-pass-helpers.cpp index ed8cc9ed645aeb..d17ce3105135cf 100644 --- a/src/llvm-pass-helpers.cpp +++ b/src/llvm-pass-helpers.cpp @@ -130,7 +130,7 @@ namespace jl_intrinsics { #if JL_LLVM_VERSION >= 160000 FnAttrs.addMemoryAttr(MemoryEffects::argMemOnly(ModRefInfo::Ref) | inaccessibleMemOnly(ModRefInfo::ModRef)); #endif - FnAttrs.addAllocKindAttr(AllocFnKind::Alloc | AllocFnKind::Uninitialized); + FnAttrs.addAllocKindAttr(AllocFnKind::Alloc); FnAttrs.addAttribute(Attribute::WillReturn); FnAttrs.addAttribute(Attribute::NoUnwind); target->addFnAttrs(FnAttrs); diff --git a/src/method.c b/src/method.c index 4e3062fef52465..8b89e66c62eb0f 100644 --- a/src/method.c +++ b/src/method.c @@ -192,7 +192,7 @@ static jl_value_t *resolve_globals(jl_value_t *expr, jl_module_t *module, jl_sve jl_error("In ccall calling convention, expected two argument tuple or symbol."); } JL_TYPECHK(ccall method definition, symbol, jl_get_nth_field(cc, 0)); - JL_TYPECHK(ccall method definition, uint8, jl_get_nth_field(cc, 1)); + JL_TYPECHK(ccall method definition, uint16, jl_get_nth_field(cc, 1)); } jl_exprargset(e, 0, resolve_globals(jl_exprarg(e, 0), module, sparam_vals, binding_effects, 1)); i++; @@ -327,7 +327,7 @@ static void jl_code_info_set_ir(jl_code_info_t *li, jl_expr_t *ir) else if (ma == (jl_value_t*)jl_no_constprop_sym) li->constprop = 2; else if (jl_is_expr(ma) && ((jl_expr_t*)ma)->head == jl_purity_sym) { - if (jl_expr_nargs(ma) == 8) { + if (jl_expr_nargs(ma) == 9) { li->purity.overrides.ipo_consistent = jl_unbox_bool(jl_exprarg(ma, 0)); li->purity.overrides.ipo_effect_free = jl_unbox_bool(jl_exprarg(ma, 1)); li->purity.overrides.ipo_nothrow = jl_unbox_bool(jl_exprarg(ma, 2)); @@ -336,6 +336,7 @@ static void jl_code_info_set_ir(jl_code_info_t *li, jl_expr_t *ir) li->purity.overrides.ipo_notaskstate = jl_unbox_bool(jl_exprarg(ma, 5)); li->purity.overrides.ipo_inaccessiblememonly = jl_unbox_bool(jl_exprarg(ma, 6)); li->purity.overrides.ipo_noub = jl_unbox_bool(jl_exprarg(ma, 7)); + li->purity.overrides.ipo_noub_if_noinbounds = jl_unbox_bool(jl_exprarg(ma, 8)); } } else diff --git a/src/opaque_closure.c b/src/opaque_closure.c index d73beff0f85870..8b7cc7292be229 100644 --- a/src/opaque_closure.c +++ b/src/opaque_closure.c @@ -138,7 +138,7 @@ JL_DLLEXPORT jl_code_instance_t* jl_new_codeinst( jl_method_instance_t *mi, jl_value_t *rettype, jl_value_t *inferred_const, jl_value_t *inferred, int32_t const_flags, size_t min_world, size_t max_world, - uint32_t ipo_effects, uint32_t effects, jl_value_t *argescapes, + uint32_t ipo_effects, uint32_t effects, jl_value_t *analysis_results, uint8_t relocatability); JL_DLLEXPORT jl_opaque_closure_t *jl_new_opaque_closure_from_code_info(jl_tupletype_t *argt, jl_value_t *rt_lb, jl_value_t *rt_ub, diff --git a/src/partr.c b/src/partr.c index a660f6be63de3e..bc31b187f83e75 100644 --- a/src/partr.c +++ b/src/partr.c @@ -357,9 +357,14 @@ void jl_task_wait_empty(void) ct->world_age = jl_atomic_load_acquire(&jl_world_counter); if (f) jl_apply_generic(f, NULL, 0); + // we are back from jl_task_get_next now ct->world_age = lastage; wait_empty = NULL; + // TODO: move this lock acquire-release pair to the caller, so that we ensure new work + // (from uv_unref objects) didn't unexpectedly get scheduled and start running behind our back + JL_UV_LOCK(); jl_wait_empty_end(); + JL_UV_UNLOCK(); } } diff --git a/src/signal-handling.c b/src/signal-handling.c index 00bc9d9d8aabb3..abe63ba6d2d7f2 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -285,21 +285,27 @@ void jl_set_profile_peek_duration(double t) profile_peek_duration = t; } -uintptr_t profile_show_peek_cond_loc; -JL_DLLEXPORT void jl_set_peek_cond(uintptr_t cond) +jl_mutex_t profile_show_peek_cond_lock; +static uv_async_t *profile_show_peek_cond_loc; +JL_DLLEXPORT void jl_set_peek_cond(uv_async_t *cond) { + JL_LOCK_NOGC(&profile_show_peek_cond_lock); profile_show_peek_cond_loc = cond; + JL_UNLOCK_NOGC(&profile_show_peek_cond_lock); } static void jl_check_profile_autostop(void) { - if ((profile_autostop_time != -1.0) && (jl_hrtime() > profile_autostop_time)) { + if (profile_show_peek_cond_loc != NULL && profile_autostop_time != -1.0 && jl_hrtime() > profile_autostop_time) { profile_autostop_time = -1.0; jl_profile_stop_timer(); jl_safe_printf("\n==============================================================\n"); jl_safe_printf("Profile collected. A report will print at the next yield point\n"); jl_safe_printf("==============================================================\n\n"); - uv_async_send((uv_async_t*)profile_show_peek_cond_loc); + JL_LOCK_NOGC(&profile_show_peek_cond_lock); + if (profile_show_peek_cond_loc != NULL) + uv_async_send(profile_show_peek_cond_loc); + JL_UNLOCK_NOGC(&profile_show_peek_cond_lock); } } @@ -425,11 +431,18 @@ void jl_task_frame_noreturn(jl_task_t *ct) JL_NOTSAFEPOINT ct->gcstack = NULL; ct->eh = NULL; ct->world_age = 1; - ct->ptls->locks.len = 0; + // 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; + 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; - jl_atomic_store_release(&ct->ptls->gc_state, 0); // forcibly exit GC (if we were in it) or safe into unsafe, without the mandatory safepoint + // forcibly exit GC (if we were in it) or safe into unsafe, without the mandatory safepoint + jl_atomic_store_release(&ct->ptls->gc_state, 0); + // allow continuing to use a Task that should have already died--unsafe necromancy! + jl_atomic_store_relaxed(&ct->_state, JL_TASK_STATE_RUNNABLE); } } diff --git a/src/staticdata.c b/src/staticdata.c index 74ac268508ede9..42a5fdb63b63e0 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -71,7 +71,6 @@ External links: */ #include #include -#include #include // printf #include // PRIxPTR @@ -99,7 +98,7 @@ extern "C" { // TODO: put WeakRefs on the weak_refs list during deserialization // TODO: handle finalizers -#define NUM_TAGS 173 +#define NUM_TAGS 174 // An array of references that need to be restored from the sysimg // This is a manually constructed dual of the gvars array, which would be produced by codegen for Julia code, for C. @@ -237,6 +236,7 @@ jl_value_t **const*const get_tags(void) { INSERT_TAG(jl_undefref_exception); INSERT_TAG(jl_readonlymemory_exception); INSERT_TAG(jl_atomicerror_type); + INSERT_TAG(jl_missingcodeerror_type); // other special values INSERT_TAG(jl_emptysvec); @@ -500,8 +500,6 @@ typedef struct { int8_t incremental; } jl_serializer_state; -static jl_value_t *jl_idtable_type = NULL; -static jl_typename_t *jl_idtable_typename = NULL; static jl_value_t *jl_bigint_type = NULL; static int gmp_limb_size = 0; static jl_sym_t *jl_docmeta_sym = NULL; @@ -1252,24 +1250,35 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED assert(!(s->incremental && jl_object_in_image(v))); jl_datatype_t *t = (jl_datatype_t*)jl_typeof(v); assert((!jl_is_datatype_singleton(t) || t->instance == v) && "detected singleton construction corruption"); + int mutabl = t->name->mutabl; ios_t *f = s->s; if (t->smalltag) { if (t->layout->npointers == 0 || t == jl_string_type) { - if (jl_datatype_nfields(t) == 0 || t->name->mutabl == 0 || t == jl_string_type) { + if (jl_datatype_nfields(t) == 0 || mutabl == 0 || t == jl_string_type) { f = s->const_data; } } } - // realign stream to expected gc alignment (16 bytes) + // realign stream to expected gc alignment (16 bytes) after tag uintptr_t skip_header_pos = ios_pos(f) + sizeof(jl_taggedvalue_t); + uintptr_t object_id_expected = mutabl && + t != jl_datatype_type && + t != jl_typename_type && + t != jl_string_type && + t != jl_simplevector_type && + t != jl_module_type; + if (object_id_expected) + skip_header_pos += sizeof(size_t); write_padding(f, LLT_ALIGN(skip_header_pos, 16) - skip_header_pos); // write header + if (object_id_expected) + write_uint(f, jl_object_id(v)); if (s->incremental && jl_needs_serialization(s, (jl_value_t*)t) && needs_uniquing((jl_value_t*)t)) arraylist_push(&s->uniquing_types, (void*)(uintptr_t)(ios_pos(f)|1)); if (f == s->const_data) - write_uint(s->const_data, ((uintptr_t)t->smalltag << 4) | GC_OLD_MARKED); + write_uint(s->const_data, ((uintptr_t)t->smalltag << 4) | GC_OLD_MARKED | GC_IN_IMAGE); else write_gctaggedfield(s, t); size_t reloc_offset = ios_pos(f); @@ -1717,11 +1726,6 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED arraylist_push(&s->fixup_objs, (void*)reloc_offset); } } - else if (((jl_datatype_t*)(jl_typeof(v)))->name == jl_idtable_typename) { - assert(f == s->s); - // will need to rehash this, later (after types are fully constructed) - arraylist_push(&s->fixup_objs, (void*)reloc_offset); - } else if (jl_is_genericmemoryref(v)) { assert(f == s->s); record_memoryref(s, reloc_offset, *(jl_genericmemoryref_t*)v); @@ -2581,8 +2585,6 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, } } } - jl_idtable_type = jl_base_module ? jl_get_global(jl_base_module, jl_symbol("IdDict")) : NULL; - jl_idtable_typename = jl_base_module ? ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_idtable_type))->name : NULL; jl_bigint_type = jl_base_module ? jl_get_global(jl_base_module, jl_symbol("BigInt")) : NULL; if (jl_bigint_type) { gmp_limb_size = jl_unbox_long(jl_get_global((jl_module_t*)jl_get_global(jl_base_module, jl_symbol("GMP")), @@ -3408,12 +3410,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl } } else { - // rehash IdDict - //assert(((jl_datatype_t*)(jl_typeof(obj)))->name == jl_idtable_typename); - jl_genericmemory_t **a = (jl_genericmemory_t**)obj; - assert(jl_typetagis(*a, jl_memory_any_type)); - *a = jl_idtable_rehash(*a, (*a)->length); - jl_gc_wb(obj, *a); + abort(); } } // Now pick up the globalref binding pointer field @@ -3524,7 +3521,7 @@ static jl_value_t *jl_validate_cache_file(ios_t *f, jl_array_t *depmods, uint64_ } // TODO?: refactor to make it easier to create the "package inspector" -static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, ios_t *f, jl_image_t *image, jl_array_t *depmods, int completeinfo, const char *pkgname, bool needs_permalloc) +static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, ios_t *f, jl_image_t *image, jl_array_t *depmods, int completeinfo, const char *pkgname, int needs_permalloc) { JL_TIMING(LOAD_IMAGE, LOAD_Pkgimg); jl_timing_printf(JL_TIMING_DEFAULT_BLOCK, pkgname); @@ -3550,7 +3547,7 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i JL_SIGATOMIC_BEGIN(); size_t len = dataendpos - datastartpos; char *sysimg; - bool success = !needs_permalloc; + int success = !needs_permalloc; ios_seek(f, datastartpos); if (needs_permalloc) sysimg = (char*)jl_gc_perm_alloc(len, 0, 64, 0); @@ -3610,7 +3607,7 @@ static void jl_restore_system_image_from_stream(ios_t *f, jl_image_t *image, uin jl_restore_system_image_from_stream_(f, image, NULL, checksum | ((uint64_t)0xfdfcfbfa << 32), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } -JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(void* pkgimage_handle, const char *buf, jl_image_t *image, size_t sz, jl_array_t *depmods, int completeinfo, const char *pkgname, bool needs_permalloc) +JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(void* pkgimage_handle, const char *buf, jl_image_t *image, size_t sz, jl_array_t *depmods, int completeinfo, const char *pkgname, int needs_permalloc) { ios_t f; ios_static_buffer(&f, (char*)buf, sz); @@ -3627,7 +3624,7 @@ JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *d "Cache file \"%s\" not found.\n", fname); } jl_image_t pkgimage = {}; - jl_value_t *ret = jl_restore_package_image_from_stream(NULL, &f, &pkgimage, depmods, completeinfo, pkgname, true); + jl_value_t *ret = jl_restore_package_image_from_stream(NULL, &f, &pkgimage, depmods, completeinfo, pkgname, 1); ios_close(&f); return ret; } @@ -3677,7 +3674,7 @@ JL_DLLEXPORT void jl_restore_system_image_data(const char *buf, size_t len) JL_SIGATOMIC_END(); } -JL_DLLEXPORT jl_value_t *jl_restore_package_image_from_file(const char *fname, jl_array_t *depmods, int completeinfo, const char *pkgname) +JL_DLLEXPORT jl_value_t *jl_restore_package_image_from_file(const char *fname, jl_array_t *depmods, int completeinfo, const char *pkgname, int ignore_native) { void *pkgimg_handle = jl_dlopen(fname, JL_RTLD_LAZY); if (!pkgimg_handle) { @@ -3698,7 +3695,11 @@ JL_DLLEXPORT jl_value_t *jl_restore_package_image_from_file(const char *fname, j jl_image_t pkgimage = jl_init_processor_pkgimg(pkgimg_handle); - jl_value_t* mod = jl_restore_incremental_from_buf(pkgimg_handle, pkgimg_data, &pkgimage, *plen, depmods, completeinfo, pkgname, false); + if (ignore_native){ + memset(&pkgimage.fptrs, 0, sizeof(pkgimage.fptrs)); + } + + jl_value_t* mod = jl_restore_incremental_from_buf(pkgimg_handle, pkgimg_data, &pkgimage, *plen, depmods, completeinfo, pkgname, 0); return mod; } diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 384c479b10f58d..a6d883c3870c31 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -717,22 +717,24 @@ static int64_t write_dependency_list(ios_t *s, jl_array_t* worklist, jl_array_t size_t i, l = udeps ? jl_array_nrows(udeps) : 0; for (i = 0; i < l; i++) { jl_value_t *deptuple = jl_array_ptr_ref(udeps, i); - jl_value_t *abspath = jl_fieldref(deptuple, 1); - - jl_value_t **replace_depot_args; - JL_GC_PUSHARGS(replace_depot_args, 2); - replace_depot_args[0] = replace_depot_func; - replace_depot_args[1] = abspath; - ct = jl_current_task; - size_t last_age = ct->world_age; - ct->world_age = jl_atomic_load_acquire(&jl_world_counter); - jl_value_t *depalias = (jl_value_t*)jl_apply(replace_depot_args, 2); - ct->world_age = last_age; - JL_GC_POP(); - - size_t slen = jl_string_len(depalias); + jl_value_t *deppath = jl_fieldref(deptuple, 1); + + if (replace_depot_func) { + jl_value_t **replace_depot_args; + JL_GC_PUSHARGS(replace_depot_args, 2); + replace_depot_args[0] = replace_depot_func; + replace_depot_args[1] = deppath; + ct = jl_current_task; + size_t last_age = ct->world_age; + ct->world_age = jl_atomic_load_acquire(&jl_world_counter); + deppath = (jl_value_t*)jl_apply(replace_depot_args, 2); + ct->world_age = last_age; + JL_GC_POP(); + } + + size_t slen = jl_string_len(deppath); write_int32(s, slen); - ios_write(s, jl_string_data(depalias), slen); + ios_write(s, jl_string_data(deppath), slen); write_uint64(s, jl_unbox_uint64(jl_fieldref(deptuple, 2))); // fsize write_uint32(s, jl_unbox_uint32(jl_fieldref(deptuple, 3))); // hash write_float64(s, jl_unbox_float64(jl_fieldref(deptuple, 4))); // mtime diff --git a/src/threading.c b/src/threading.c index 18c502cf94387a..e20e7cbf80f265 100644 --- a/src/threading.c +++ b/src/threading.c @@ -681,6 +681,7 @@ void jl_init_threading(void) } } + int cpu = jl_cpu_threads(); jl_n_markthreads = jl_options.nmarkthreads - 1; jl_n_sweepthreads = jl_options.nsweepthreads; if (jl_n_markthreads == -1) { // --gcthreads not specified @@ -709,8 +710,20 @@ void jl_init_threading(void) else { jl_n_markthreads = (nthreads / 2) - 1; } + // if `--gcthreads` or ENV[NUM_GCTHREADS_NAME] was not specified, + // cap the number of threads that may run the mark phase to + // the number of CPU cores + if (jl_n_markthreads + 1 >= cpu) { + jl_n_markthreads = cpu - 1; + } } } + // warn the user if they try to run with a number + // of GC threads which is larger than the number + // of physical cores + if (jl_n_markthreads + 1 > cpu) { + jl_safe_printf("WARNING: running Julia with %d GC threads on %d CPU cores\n", jl_n_markthreads + 1, cpu); + } int16_t ngcthreads = jl_n_markthreads + jl_n_sweepthreads; jl_all_tls_states_size = nthreads + nthreadsi + ngcthreads; diff --git a/src/toplevel.c b/src/toplevel.c index 5b67ab6061c518..750f34aaa9fed6 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -352,7 +352,7 @@ JL_DLLEXPORT jl_module_t *jl_base_relative_to(jl_module_t *m) return jl_top_module; } -static void expr_attributes(jl_value_t *v, int *has_ccall, int *has_defs, int *has_opaque) +static void expr_attributes(jl_value_t *v, jl_array_t *body, int *has_ccall, int *has_defs, int *has_opaque) { if (!jl_is_expr(v)) return; @@ -390,6 +390,9 @@ static void expr_attributes(jl_value_t *v, int *has_ccall, int *has_defs, int *h else if (head == jl_call_sym && jl_expr_nargs(e) > 0) { jl_value_t *called = NULL; jl_value_t *f = jl_exprarg(e, 0); + if (jl_is_ssavalue(f)) { + f = jl_array_ptr_ref(body, ((jl_ssavalue_t*)f)->id - 1); + } if (jl_is_globalref(f)) { jl_module_t *mod = jl_globalref_mod(f); jl_sym_t *name = jl_globalref_name(f); @@ -417,7 +420,7 @@ static void expr_attributes(jl_value_t *v, int *has_ccall, int *has_defs, int *h for (i = 0; i < jl_array_nrows(e->args); i++) { jl_value_t *a = jl_exprarg(e, i); if (jl_is_expr(a)) - expr_attributes(a, has_ccall, has_defs, has_opaque); + expr_attributes(a, body, has_ccall, has_defs, has_opaque); } } @@ -431,7 +434,7 @@ int jl_code_requires_compiler(jl_code_info_t *src, int include_force_compile) return 1; for(i=0; i < jl_array_nrows(body); i++) { jl_value_t *stmt = jl_array_ptr_ref(body,i); - expr_attributes(stmt, &has_ccall, &has_defs, &has_opaque); + expr_attributes(stmt, body, &has_ccall, &has_defs, &has_opaque); if (has_ccall) return 1; } @@ -454,7 +457,7 @@ static void body_attributes(jl_array_t *body, int *has_ccall, int *has_defs, int *has_loops = 1; } } - expr_attributes(stmt, has_ccall, has_defs, has_opaque); + expr_attributes(stmt, body, has_ccall, has_defs, has_opaque); } *forced_compile = jl_has_meta(body, jl_force_compile_sym); } diff --git a/stdlib/LinearAlgebra/src/adjtrans.jl b/stdlib/LinearAlgebra/src/adjtrans.jl index c8b02f7a10a761..3df9e3e151f25d 100644 --- a/stdlib/LinearAlgebra/src/adjtrans.jl +++ b/stdlib/LinearAlgebra/src/adjtrans.jl @@ -281,6 +281,11 @@ adjoint(A::Adjoint) = A.parent transpose(A::Transpose) = A.parent adjoint(A::Transpose{<:Real}) = A.parent transpose(A::Adjoint{<:Real}) = A.parent +adjoint(A::Transpose{<:Any,<:Adjoint}) = transpose(A.parent.parent) +transpose(A::Adjoint{<:Any,<:Transpose}) = adjoint(A.parent.parent) +# disambiguation +adjoint(A::Transpose{<:Real,<:Adjoint}) = transpose(A.parent.parent) +transpose(A::Adjoint{<:Real,<:Transpose}) = A.parent # printing function Base.showarg(io::IO, v::Adjoint, toplevel) @@ -395,11 +400,16 @@ map(f, avs::AdjointAbsVec...) = adjoint(map((xs...) -> adjoint(f(adjoint.(xs)... map(f, tvs::TransposeAbsVec...) = transpose(map((xs...) -> transpose(f(transpose.(xs)...)), parent.(tvs)...)) quasiparentt(x) = parent(x); quasiparentt(x::Number) = x # to handle numbers in the defs below quasiparenta(x) = parent(x); quasiparenta(x::Number) = conj(x) # to handle numbers in the defs below +quasiparentc(x) = parent(parent(x)); quasiparentc(x::Number) = conj(x) # to handle numbers in the defs below broadcast(f, avs::Union{Number,AdjointAbsVec}...) = adjoint(broadcast((xs...) -> adjoint(f(adjoint.(xs)...)), quasiparenta.(avs)...)) broadcast(f, tvs::Union{Number,TransposeAbsVec}...) = transpose(broadcast((xs...) -> transpose(f(transpose.(xs)...)), quasiparentt.(tvs)...)) # Hack to preserve behavior after #32122; this needs to be done with a broadcast style instead to support dotted fusion Broadcast.broadcast_preserving_zero_d(f, avs::Union{Number,AdjointAbsVec}...) = adjoint(broadcast((xs...) -> adjoint(f(adjoint.(xs)...)), quasiparenta.(avs)...)) Broadcast.broadcast_preserving_zero_d(f, tvs::Union{Number,TransposeAbsVec}...) = transpose(broadcast((xs...) -> transpose(f(transpose.(xs)...)), quasiparentt.(tvs)...)) +Broadcast.broadcast_preserving_zero_d(f, tvs::Union{Number,Transpose{<:Any,<:AdjointAbsVec}}...) = + transpose(adjoint(broadcast((xs...) -> adjoint(transpose(f(conj.(xs)...))), quasiparentc.(tvs)...))) +Broadcast.broadcast_preserving_zero_d(f, tvs::Union{Number,Adjoint{<:Any,<:TransposeAbsVec}}...) = + adjoint(transpose(broadcast((xs...) -> transpose(adjoint(f(conj.(xs)...))), quasiparentc.(tvs)...))) # TODO unify and allow mixed combinations with a broadcast style diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index f8cc3ceadcfadb..78c79b6fcefac7 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -426,6 +426,7 @@ end const BandedMatrix = Union{Bidiagonal,Diagonal,Tridiagonal,SymTridiagonal} # or BiDiTriSym const BiTriSym = Union{Bidiagonal,Tridiagonal,SymTridiagonal} +const TriSym = Union{Tridiagonal,SymTridiagonal} const BiTri = Union{Bidiagonal,Tridiagonal} @inline mul!(C::AbstractVector, A::BandedMatrix, B::AbstractVector, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) @inline mul!(C::AbstractMatrix, A::BandedMatrix, B::AbstractVector, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) @@ -433,6 +434,9 @@ const BiTri = Union{Bidiagonal,Tridiagonal} @inline mul!(C::AbstractMatrix, A::AbstractMatrix, B::BandedMatrix, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) @inline mul!(C::AbstractMatrix, A::BandedMatrix, B::BandedMatrix, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) +lmul!(A::Bidiagonal, B::AbstractVecOrMat) = @inline _mul!(B, A, B, MulAddMul()) +rmul!(B::AbstractMatrix, A::Bidiagonal) = @inline _mul!(B, B, A, MulAddMul()) + function check_A_mul_B!_sizes(C, A, B) mA, nA = size(A) mB, nB = size(B) @@ -460,7 +464,11 @@ function _diag(A::Bidiagonal, k) end end -function _mul!(C::AbstractMatrix, A::BiTriSym, B::BiTriSym, _add::MulAddMul = MulAddMul()) +_mul!(C::AbstractMatrix, A::BiTriSym, B::TriSym, _add::MulAddMul = MulAddMul()) = + _bibimul!(C, A, B, _add) +_mul!(C::AbstractMatrix, A::BiTriSym, B::Bidiagonal, _add::MulAddMul = MulAddMul()) = + _bibimul!(C, A, B, _add) +function _bibimul!(C, A, B, _add) check_A_mul_B!_sizes(C, A, B) n = size(A,1) n <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) @@ -583,7 +591,7 @@ function _mul!(C::AbstractVecOrMat, A::BiTriSym, B::AbstractVecOrMat, _add::MulA C end -function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::BiTriSym, _add::MulAddMul = MulAddMul()) +function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::TriSym, _add::MulAddMul = MulAddMul()) require_one_based_indexing(C, A) check_A_mul_B!_sizes(C, A, B) iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) @@ -618,7 +626,37 @@ function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::BiTriSym, _add::MulAddMu C end -function _mul!(C::AbstractMatrix, A::Diagonal, B::BiTriSym, _add::MulAddMul = MulAddMul()) +function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::Bidiagonal, _add::MulAddMul = MulAddMul()) + require_one_based_indexing(C, A) + check_A_mul_B!_sizes(C, A, B) + 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 + _modify!(_add, A[i,j] * B.dv[j] + A[i,j-1] * B.ev[j-1], C, (i, j)) + end + _modify!(_add, A[i,1] * B.dv[1], C, (i, 1)) + end + else # uplo == 'L' + for i in 1:m + for j in 1:n-1 + _modify!(_add, A[i,j] * B.dv[j] + A[i,j+1] * B.ev[j], C, (i, j)) + end + _modify!(_add, A[i,n] * B.dv[n], C, (i, n)) + end + end + C +end + +_mul!(C::AbstractMatrix, A::Diagonal, B::Bidiagonal, _add::MulAddMul = MulAddMul()) = + _dibimul!(C, A, B, _add) +_mul!(C::AbstractMatrix, A::Diagonal, B::TriSym, _add::MulAddMul = MulAddMul()) = + _dibimul!(C, A, B, _add) +function _dibimul!(C, A, B, _add) require_one_based_indexing(C) check_A_mul_B!_sizes(C, A, B) n = size(A,1) diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 018ad20e538c83..0ca0527785d610 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -337,7 +337,7 @@ julia> lmul!(F.Q, B) lmul!(A, B) # THE one big BLAS dispatch -@inline function generic_matmatmul!(C::StridedMatrix{T}, tA, tB, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, +Base.@constprop :aggressive function generic_matmatmul!(C::StridedMatrix{T}, tA, tB, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, _add::MulAddMul=MulAddMul()) where {T<:BlasFloat} if all(in(('N', 'T', 'C')), (tA, tB)) if tA == 'T' && tB == 'N' && A === B @@ -364,16 +364,16 @@ lmul!(A, B) return BLAS.hemm!('R', tB == 'H' ? 'U' : 'L', alpha, B, A, beta, C) end end - return _generic_matmatmul!(C, 'N', 'N', wrap(A, tA), wrap(B, tB), _add) + return _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), _add) end # Complex matrix times (transposed) real matrix. Reinterpret the first matrix to real for efficiency. -@inline function generic_matmatmul!(C::StridedVecOrMat{Complex{T}}, tA, tB, A::StridedVecOrMat{Complex{T}}, B::StridedVecOrMat{T}, +Base.@constprop :aggressive function generic_matmatmul!(C::StridedVecOrMat{Complex{T}}, tA, tB, A::StridedVecOrMat{Complex{T}}, B::StridedVecOrMat{T}, _add::MulAddMul=MulAddMul()) where {T<:BlasReal} if all(in(('N', 'T', 'C')), (tA, tB)) gemm_wrapper!(C, tA, tB, A, B, _add) else - _generic_matmatmul!(C, 'N', 'N', wrap(A, tA), wrap(B, tB), _add) + _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), _add) end end @@ -563,11 +563,11 @@ function gemm_wrapper(tA::AbstractChar, tB::AbstractChar, if all(in(('N', 'T', 'C')), (tA, tB)) gemm_wrapper!(C, tA, tB, A, B) else - _generic_matmatmul!(C, 'N', 'N', wrap(A, tA), wrap(B, tB), _add) + _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), _add) end end -function gemm_wrapper!(C::StridedVecOrMat{T}, tA::AbstractChar, tB::AbstractChar, +Base.@constprop :aggressive function gemm_wrapper!(C::StridedVecOrMat{T}, tA::AbstractChar, tB::AbstractChar, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, _add = MulAddMul()) where {T<:BlasFloat} mA, nA = lapack_size(tA, A) @@ -604,10 +604,10 @@ function gemm_wrapper!(C::StridedVecOrMat{T}, tA::AbstractChar, tB::AbstractChar stride(C, 2) >= size(C, 1)) return BLAS.gemm!(tA, tB, alpha, A, B, beta, C) end - _generic_matmatmul!(C, tA, tB, A, B, _add) + _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), _add) end -function gemm_wrapper!(C::StridedVecOrMat{Complex{T}}, tA::AbstractChar, tB::AbstractChar, +Base.@constprop :aggressive function gemm_wrapper!(C::StridedVecOrMat{Complex{T}}, tA::AbstractChar, tB::AbstractChar, A::StridedVecOrMat{Complex{T}}, B::StridedVecOrMat{T}, _add = MulAddMul()) where {T<:BlasReal} mA, nA = lapack_size(tA, A) @@ -647,7 +647,7 @@ function gemm_wrapper!(C::StridedVecOrMat{Complex{T}}, tA::AbstractChar, tB::Abs BLAS.gemm!(tA, tB, alpha, reinterpret(T, A), B, beta, reinterpret(T, C)) return C end - _generic_matmatmul!(C, tA, tB, A, B, _add) + _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), _add) end # blas.jl defines matmul for floats; other integer and mixed precision @@ -764,197 +764,65 @@ end const tilebufsize = 10800 # Approximately 32k/3 -function generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::AbstractVecOrMat, B::AbstractVecOrMat, _add::MulAddMul) - mA, nA = lapack_size(tA, A) - mB, nB = lapack_size(tB, B) - mC, nC = size(C) - - if iszero(_add.alpha) - return _rmul_or_fill!(C, _add.beta) - end - if mA == nA == mB == nB == mC == nC == 2 - return matmul2x2!(C, tA, tB, A, B, _add) - end - if mA == nA == mB == nB == mC == nC == 3 - return matmul3x3!(C, tA, tB, A, B, _add) - end - A, tA = tA in ('H', 'h', 'S', 's') ? (wrap(A, tA), 'N') : (A, tA) - B, tB = tB in ('H', 'h', 'S', 's') ? (wrap(B, tB), 'N') : (B, tB) - _generic_matmatmul!(C, tA, tB, A, B, _add) -end +Base.@constprop :aggressive generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::AbstractVecOrMat, B::AbstractVecOrMat, _add::MulAddMul) = + _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), _add) -function _generic_matmatmul!(C::AbstractVecOrMat{R}, tA, tB, A::AbstractVecOrMat{T}, B::AbstractVecOrMat{S}, +@noinline function _generic_matmatmul!(C::AbstractVecOrMat{R}, A::AbstractVecOrMat{T}, B::AbstractVecOrMat{S}, _add::MulAddMul) where {T,S,R} - @assert tA in ('N', 'T', 'C') && tB in ('N', 'T', 'C') - require_one_based_indexing(C, A, B) - - mA, nA = lapack_size(tA, A) - mB, nB = lapack_size(tB, B) - if mB != nA - throw(DimensionMismatch(lazy"matrix A has dimensions ($mA,$nA), matrix B has dimensions ($mB,$nB)")) - end - if size(C,1) != mA || size(C,2) != nB - throw(DimensionMismatch(lazy"result C has dimensions $(size(C)), needs ($mA,$nB)")) - end - - if iszero(_add.alpha) || isempty(A) || isempty(B) - return _rmul_or_fill!(C, _add.beta) - end - - tile_size = 0 - if isbitstype(R) && isbitstype(T) && isbitstype(S) && (tA == 'N' || tB != 'N') - tile_size = floor(Int, sqrt(tilebufsize / max(sizeof(R), sizeof(S), sizeof(T), 1))) - end - @inbounds begin - if tile_size > 0 - sz = (tile_size, tile_size) - Atile = Array{T}(undef, sz) - Btile = Array{S}(undef, sz) - - z1 = zero(A[1, 1]*B[1, 1] + A[1, 1]*B[1, 1]) - z = convert(promote_type(typeof(z1), R), z1) - - if mA < tile_size && nA < tile_size && nB < tile_size - copy_transpose!(Atile, 1:nA, 1:mA, tA, A, 1:mA, 1:nA) - copyto!(Btile, 1:mB, 1:nB, tB, B, 1:mB, 1:nB) - for j = 1:nB - boff = (j-1)*tile_size - for i = 1:mA - aoff = (i-1)*tile_size - s = z - for k = 1:nA - s += Atile[aoff+k] * Btile[boff+k] - end - _modify!(_add, s, C, (i,j)) - end - end - else - Ctile = Array{R}(undef, sz) - for jb = 1:tile_size:nB - jlim = min(jb+tile_size-1,nB) - jlen = jlim-jb+1 - for ib = 1:tile_size:mA - ilim = min(ib+tile_size-1,mA) - ilen = ilim-ib+1 - fill!(Ctile, z) - for kb = 1:tile_size:nA - klim = min(kb+tile_size-1,mB) - klen = klim-kb+1 - copy_transpose!(Atile, 1:klen, 1:ilen, tA, A, ib:ilim, kb:klim) - copyto!(Btile, 1:klen, 1:jlen, tB, B, kb:klim, jb:jlim) - for j=1:jlen - bcoff = (j-1)*tile_size - for i = 1:ilen - aoff = (i-1)*tile_size - s = z - for k = 1:klen - s += Atile[aoff+k] * Btile[bcoff+k] - end - Ctile[bcoff+i] += s - end - end - end - if isone(_add.alpha) && iszero(_add.beta) - copyto!(C, ib:ilim, jb:jlim, Ctile, 1:ilen, 1:jlen) - else - C[ib:ilim, jb:jlim] .= @views _add.(Ctile[1:ilen, 1:jlen], C[ib:ilim, jb:jlim]) - end - end + AxM = axes(A, 1) + AxK = axes(A, 2) # we use two `axes` calls in case of `AbstractVector` + BxK = axes(B, 1) + BxN = axes(B, 2) + CxM = axes(C, 1) + CxN = axes(C, 2) + if AxM != CxM + throw(DimensionMismatch(lazy"matrix A has axes ($AxM,$AxK), matrix C has axes ($CxM,$CxN)")) + end + if AxK != BxK + throw(DimensionMismatch(lazy"matrix A has axes ($AxM,$AxK), matrix B has axes ($BxK,$CxN)")) + end + if BxN != CxN + throw(DimensionMismatch(lazy"matrix B has axes ($BxK,$BxN), matrix C has axes ($CxM,$CxN)")) + end + if isbitstype(R) && sizeof(R) ≤ 16 && !(A isa Adjoint || A isa Transpose) + _rmul_or_fill!(C, _add.beta) + (iszero(_add.alpha) || isempty(A) || isempty(B)) && return C + @inbounds for n in BxN, k in BxK + Balpha = B[k,n]*_add.alpha + @simd for m in AxM + C[m,n] = muladd(A[m,k], Balpha, C[m,n]) end end + elseif isbitstype(R) && sizeof(R) ≤ 16 && ((A isa Adjoint && B isa Adjoint) || (A isa Transpose && B isa Transpose)) + _rmul_or_fill!(C, _add.beta) + (iszero(_add.alpha) || isempty(A) || isempty(B)) && return C + t = wrapperop(A) + pB = parent(B) + pA = parent(A) + tmp = similar(C, CxN) + ci = first(CxM) + ta = t(_add.alpha) + for i in AxM + mul!(tmp, pB, view(pA, :, i)) + C[ci,:] .+= t.(ta .* tmp) + ci += 1 + end else - # Multiplication for non-plain-data uses the naive algorithm - if tA == 'N' - if tB == 'N' - for i = 1:mA, j = 1:nB - z2 = zero(A[i, 1]*B[1, j] + A[i, 1]*B[1, j]) - Ctmp = convert(promote_type(R, typeof(z2)), z2) - for k = 1:nA - Ctmp += A[i, k]*B[k, j] - end - _modify!(_add, Ctmp, C, (i,j)) - end - elseif tB == 'T' - for i = 1:mA, j = 1:nB - z2 = zero(A[i, 1]*transpose(B[j, 1]) + A[i, 1]*transpose(B[j, 1])) - Ctmp = convert(promote_type(R, typeof(z2)), z2) - for k = 1:nA - Ctmp += A[i, k] * transpose(B[j, k]) - end - _modify!(_add, Ctmp, C, (i,j)) - end - else - for i = 1:mA, j = 1:nB - z2 = zero(A[i, 1]*B[j, 1]' + A[i, 1]*B[j, 1]') - Ctmp = convert(promote_type(R, typeof(z2)), z2) - for k = 1:nA - Ctmp += A[i, k]*B[j, k]' - end - _modify!(_add, Ctmp, C, (i,j)) - end - end - elseif tA == 'T' - if tB == 'N' - for i = 1:mA, j = 1:nB - z2 = zero(transpose(A[1, i])*B[1, j] + transpose(A[1, i])*B[1, j]) - Ctmp = convert(promote_type(R, typeof(z2)), z2) - for k = 1:nA - Ctmp += transpose(A[k, i]) * B[k, j] - end - _modify!(_add, Ctmp, C, (i,j)) - end - elseif tB == 'T' - for i = 1:mA, j = 1:nB - z2 = zero(transpose(A[1, i])*transpose(B[j, 1]) + transpose(A[1, i])*transpose(B[j, 1])) - Ctmp = convert(promote_type(R, typeof(z2)), z2) - for k = 1:nA - Ctmp += transpose(A[k, i]) * transpose(B[j, k]) - end - _modify!(_add, Ctmp, C, (i,j)) - end - else - for i = 1:mA, j = 1:nB - z2 = zero(transpose(A[1, i])*B[j, 1]' + transpose(A[1, i])*B[j, 1]') - Ctmp = convert(promote_type(R, typeof(z2)), z2) - for k = 1:nA - Ctmp += transpose(A[k, i]) * adjoint(B[j, k]) - end - _modify!(_add, Ctmp, C, (i,j)) - end - end - else - if tB == 'N' - for i = 1:mA, j = 1:nB - z2 = zero(A[1, i]'*B[1, j] + A[1, i]'*B[1, j]) - Ctmp = convert(promote_type(R, typeof(z2)), z2) - for k = 1:nA - Ctmp += A[k, i]'B[k, j] - end - _modify!(_add, Ctmp, C, (i,j)) - end - elseif tB == 'T' - for i = 1:mA, j = 1:nB - z2 = zero(A[1, i]'*transpose(B[j, 1]) + A[1, i]'*transpose(B[j, 1])) - Ctmp = convert(promote_type(R, typeof(z2)), z2) - for k = 1:nA - Ctmp += adjoint(A[k, i]) * transpose(B[j, k]) - end - _modify!(_add, Ctmp, C, (i,j)) - end - else - for i = 1:mA, j = 1:nB - z2 = zero(A[1, i]'*B[j, 1]' + A[1, i]'*B[j, 1]') - Ctmp = convert(promote_type(R, typeof(z2)), z2) - for k = 1:nA - Ctmp += A[k, i]'B[j, k]' - end - _modify!(_add, Ctmp, C, (i,j)) - end + if iszero(_add.alpha) || isempty(A) || isempty(B) + return _rmul_or_fill!(C, _add.beta) + end + a1 = first(AxK) + b1 = first(BxK) + @inbounds for i in AxM, j in BxN + z2 = zero(A[i, a1]*B[b1, j] + A[i, a1]*B[b1, j]) + Ctmp = convert(promote_type(R, typeof(z2)), z2) + @simd for k in AxK + Ctmp = muladd(A[i, k], B[k, j], Ctmp) end + _modify!(_add, Ctmp, C, (i,j)) end end - end # @inbounds - C + return C end @@ -963,7 +831,7 @@ function matmul2x2(tA, tB, A::AbstractMatrix{T}, B::AbstractMatrix{S}) where {T, matmul2x2!(similar(B, promote_op(matprod, T, S), 2, 2), tA, tB, A, B) end -function matmul2x2!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix, +Base.@constprop :aggressive function matmul2x2!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix, _add::MulAddMul = MulAddMul()) require_one_based_indexing(C, A, B) if !(size(A) == size(B) == size(C) == (2,2)) @@ -1030,7 +898,7 @@ function matmul3x3(tA, tB, A::AbstractMatrix{T}, B::AbstractMatrix{S}) where {T, matmul3x3!(similar(B, promote_op(matprod, T, S), 3, 3), tA, tB, A, B) end -function matmul3x3!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix, +Base.@constprop :aggressive function matmul3x3!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix, _add::MulAddMul = MulAddMul()) require_one_based_indexing(C, A, B) if !(size(A) == size(B) == size(C) == (3,3)) diff --git a/stdlib/LinearAlgebra/src/transpose.jl b/stdlib/LinearAlgebra/src/transpose.jl index cd9123615d4bbb..afc3494fc97260 100644 --- a/stdlib/LinearAlgebra/src/transpose.jl +++ b/stdlib/LinearAlgebra/src/transpose.jl @@ -194,7 +194,7 @@ function copy_transpose!(B::AbstractVecOrMat, ir_dest::AbstractRange{Int}, jr_de for jsrc in jr_src jdest = first(jr_dest) for isrc in ir_src - B[idest,jdest] = A[isrc,jsrc] + B[idest,jdest] = transpose(A[isrc,jsrc]) jdest += step(jr_dest) end idest += step(ir_dest) diff --git a/stdlib/LinearAlgebra/test/adjtrans.jl b/stdlib/LinearAlgebra/test/adjtrans.jl index 555010913660ab..ccd93c97d440eb 100644 --- a/stdlib/LinearAlgebra/test/adjtrans.jl +++ b/stdlib/LinearAlgebra/test/adjtrans.jl @@ -681,4 +681,19 @@ end @test sprint(Base.print_matrix, Adjoint(o)) == sprint(Base.print_matrix, OneHotVecOrMat((1,2), (1,4))) end +@testset "copy_transpose!" begin + # scalar case + A = [randn() for _ in 1:2, _ in 1:3] + At = copy(transpose(A)) + B = zero.(At) + LinearAlgebra.copy_transpose!(B, axes(B, 1), axes(B, 2), A, axes(A, 1), axes(A, 2)) + @test B == At + # matrix of matrices + A = [randn(2,3) for _ in 1:2, _ in 1:3] + At = copy(transpose(A)) + B = zero.(At) + LinearAlgebra.copy_transpose!(B, axes(B, 1), axes(B, 2), A, axes(A, 1), axes(A, 2)) + @test B == At +end + end # module TestAdjointTranspose diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index a3e5a2f437e932..2fce781e30ab13 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -439,6 +439,9 @@ Random.seed!(1) for op in (+, -, *) @test Array(op(T, T2)) ≈ op(Tfull, Tfull2) end + A = kron(T.dv, T.dv') + @test T * A ≈ lmul!(T, copy(A)) + @test A * T ≈ rmul!(copy(A), T) end # test pass-through of mul! for SymTridiagonal*Bidiagonal TriSym = SymTridiagonal(T.dv, T.ev) @@ -446,7 +449,8 @@ Random.seed!(1) # test pass-through of mul! for AbstractTriangular*Bidiagonal Tri = UpperTriangular(diagm(1 => T.ev)) Dia = Diagonal(T.dv) - @test Array(Tri*T) ≈ Array(Tri)*Array(T) + @test Array(Tri*T) ≈ Array(Tri)*Array(T) ≈ rmul!(copy(Tri), T) + @test Array(T*Tri) ≈ Array(T)*Array(Tri) ≈ lmul!(T, copy(Tri)) # test mul! itself for these types for AA in (Tri, Dia) for f in (identity, transpose, adjoint) @@ -459,8 +463,10 @@ Random.seed!(1) for f in (identity, transpose, adjoint) C = relty == Int ? rand(float(elty), n, n) : rand(elty, n, n) B = rand(elty, n, n) - D = copy(C) + 2.0 * Array(T*f(B)) - mul!(C, T, f(B), 2.0, 1.0) ≈ D + D = C + 2.0 * Array(T*f(B)) + @test mul!(C, T, f(B), 2.0, 1.0) ≈ D + @test lmul!(T, copy(f(B))) ≈ T * f(B) + @test rmul!(copy(f(B)), T) ≈ f(B) * T end # Issue #31870 diff --git a/stdlib/LinearAlgebra/test/matmul.jl b/stdlib/LinearAlgebra/test/matmul.jl index 86606654e911a2..30cc74694b3f40 100644 --- a/stdlib/LinearAlgebra/test/matmul.jl +++ b/stdlib/LinearAlgebra/test/matmul.jl @@ -227,7 +227,7 @@ end @test C == AB mul!(C, A, B, 2, -1) @test C == AB - LinearAlgebra._generic_matmatmul!(C, 'N', 'N', A, B, LinearAlgebra.MulAddMul(2, -1)) + LinearAlgebra.generic_matmatmul!(C, 'N', 'N', A, B, LinearAlgebra.MulAddMul(2, -1)) @test C == AB end @@ -255,7 +255,7 @@ end @testset "mixed Blas-non-Blas matmul" begin AA = rand(-10:10, 6, 6) - BB = rand(Float64, 6, 6) + BB = ones(Float64, 6, 6) CC = zeros(Float64, 6, 6) for A in (copy(AA), view(AA, 1:6, 1:6)), B in (copy(BB), view(BB, 1:6, 1:6)), C in (copy(CC), view(CC, 1:6, 1:6)) @test LinearAlgebra.mul!(C, A, B) == A * B @@ -667,12 +667,9 @@ end import Base: *, adjoint, transpose import LinearAlgebra: Adjoint, Transpose (*)(x::RootInt, y::RootInt) = x.i * y.i +(*)(x::RootInt, y::Integer) = x.i * y adjoint(x::RootInt) = x transpose(x::RootInt) = x -Adjoint(x::RootInt) = x -Transpose(x::RootInt) = x -# TODO once Adjoint/Transpose constructors call adjoint/transpose recursively -# rather than Adjoint/Transpose, the additional definitions should become unnecessary @test Base.promote_op(*, RootInt, RootInt) === Int @@ -690,7 +687,7 @@ Transpose(x::RootInt) = x @test A * a == [56] end -function test_mul(C, A, B) +function test_mul(C, A, B, S) mul!(C, A, B) @test Array(A) * Array(B) ≈ C @test A * B ≈ C @@ -699,10 +696,10 @@ function test_mul(C, A, B) # but consider all number types involved: rtol = max(rtoldefault.(real.(eltype.((C, A, B))))...) - rand!(C) + rand!(C, S) T = promote_type(eltype.((A, B))...) - α = rand(T) - β = rand(T) + α = T <: AbstractFloat ? rand(T) : rand(T(-10):T(10)) + β = T <: AbstractFloat ? rand(T) : rand(T(-10):T(10)) βArrayC = β * Array(C) βC = β * C mul!(C, A, B, α, β) @@ -711,7 +708,7 @@ function test_mul(C, A, B) end @testset "mul! vs * for special types" begin - eltypes = [Float32, Float64, Int64] + eltypes = [Float32, Float64, Int64(-100):Int64(100)] for k in [3, 4, 10] T = rand(eltypes) bi1 = Bidiagonal(rand(T, k), rand(T, k - 1), rand([:U, :L])) @@ -724,26 +721,26 @@ end specialmatrices = (bi1, bi2, tri1, tri2, stri1, stri2) for A in specialmatrices B = specialmatrices[rand(1:length(specialmatrices))] - test_mul(C, A, B) + test_mul(C, A, B, T) end for S in specialmatrices l = rand(1:6) B = randn(k, l) C = randn(k, l) - test_mul(C, S, B) + test_mul(C, S, B, T) A = randn(l, k) C = randn(l, k) - test_mul(C, A, S) + test_mul(C, A, S, T) end end for T in eltypes A = Bidiagonal(rand(T, 2), rand(T, 1), rand([:U, :L])) B = Bidiagonal(rand(T, 2), rand(T, 1), rand([:U, :L])) C = randn(2, 2) - test_mul(C, A, B) + test_mul(C, A, B, T) B = randn(2, 9) C = randn(2, 9) - test_mul(C, A, B) + test_mul(C, A, B, T) end let tri44 = Tridiagonal(randn(3), randn(4), randn(3)) @@ -871,7 +868,7 @@ end # Just in case dispatching on the surface API `mul!` is changed in the future, # let's test the function where the tiled multiplication is defined. fill!(C, 0) - LinearAlgebra._generic_matmatmul!(C, 'N', 'N', A, B, LinearAlgebra.MulAddMul(-1, 0)) + LinearAlgebra.generic_matmatmul!(C, 'N', 'N', A, B, LinearAlgebra.MulAddMul(-1, 0)) @test D ≈ C end diff --git a/stdlib/OpenBLAS_jll/Project.toml b/stdlib/OpenBLAS_jll/Project.toml index ad5041cf4ac817..99a75366e24a75 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.24+0" +version = "0.3.25+0" [deps] # See note in `src/OpenBLAS_jll.jl` about this dependency. diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 23a4475e5ca01a..273ffd825b9f1f 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 2d59169bffdf29a4ed7bafd8b35c391ede612dde +PKG_SHA1 = debc38b9a2d800f035a17f42a01c14a3775fa859 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/Project.toml b/stdlib/Profile/Project.toml index 8e50b474f1cd36..ad0107ecf94043 100644 --- a/stdlib/Profile/Project.toml +++ b/stdlib/Profile/Project.toml @@ -2,9 +2,6 @@ name = "Profile" uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" version = "1.11.0" -[deps] -Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" - [extras] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index f7d248464f3c9d..c89cff0db38b30 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -258,8 +258,21 @@ const sorted_keyvals = ["false", "true"] complete_keyval(s::Union{String,SubString{String}}) = complete_from_list(KeyvalCompletion, sorted_keyvals, s) -function complete_path(path::AbstractString, pos::Int; - use_envpath=false, shell_escape=false, +function do_raw_escape(s) + # escape_raw_string with delim='`' and ignoring the rule for the ending \ + return replace(s, r"(\\+)`" => s"\1\\`") +end +function do_shell_escape(s) + return Base.shell_escape_posixly(s) +end +function do_string_escape(s) + return escape_string(s, ('\"','$')) +end + +function complete_path(path::AbstractString; + use_envpath=false, + shell_escape=false, + raw_escape=false, string_escape=false) @assert !(shell_escape && string_escape) if Base.Sys.isunix() && occursin(r"^~(?:/|$)", path) @@ -272,50 +285,49 @@ function complete_path(path::AbstractString, pos::Int; else dir, prefix = splitdir(path) end - local files - try + files = try if isempty(dir) - files = readdir() + readdir() elseif isdir(dir) - files = readdir(dir) + readdir(dir) else - return Completion[], 0:-1, false + return Completion[], dir, false end - catch - return Completion[], 0:-1, false + catch ex + ex isa Base.IOError || rethrow() + return Completion[], dir, false end matches = Set{String}() for file in files if startswith(file, prefix) p = joinpath(dir, file) - is_dir = try isdir(p) catch; false end - push!(matches, is_dir ? joinpath(file, "") : file) + is_dir = try isdir(p) catch ex; ex isa Base.IOError ? false : rethrow() end + push!(matches, is_dir ? file * "/" : file) end end - if use_envpath && length(dir) == 0 + if use_envpath && isempty(dir) # Look for files in PATH as well - local pathdirs = split(ENV["PATH"], @static Sys.iswindows() ? ";" : ":") + pathdirs = split(ENV["PATH"], @static Sys.iswindows() ? ";" : ":") for pathdir in pathdirs - local actualpath - try - actualpath = realpath(pathdir) - catch + actualpath = try + realpath(pathdir) + catch ex + ex isa Base.IOError || rethrow() # Bash doesn't expect every folder in PATH to exist, so neither shall we continue end - if actualpath != pathdir && in(actualpath,pathdirs) + if actualpath != pathdir && in(actualpath, pathdirs) # Remove paths which (after resolving links) are in the env path twice. # Many distros eg. point /bin to /usr/bin but have both in the env path. continue end - local filesinpath - try - filesinpath = readdir(pathdir) + filesinpath = try + readdir(pathdir) catch e # Bash allows dirs in PATH that can't be read, so we should as well. if isa(e, Base.IOError) || isa(e, Base.ArgumentError) @@ -347,18 +359,38 @@ function complete_path(path::AbstractString, pos::Int; end end - function do_escape(s) - return shell_escape ? replace(s, r"(\s|\\)" => s"\\\0") : - string_escape ? escape_string(s, ('\"','$')) : - s - end + matches = ((shell_escape ? do_shell_escape(s) : string_escape ? do_string_escape(s) : s) for s in matches) + matches = ((raw_escape ? do_raw_escape(s) : s) for s in matches) + matches = Completion[PathCompletion(s) for s in matches] + return matches, dir, !isempty(matches) +end - matchList = Completion[PathCompletion(do_escape(s)) for s in matches] - startpos = pos - lastindex(do_escape(prefix)) + 1 - # The pos - lastindex(prefix) + 1 is correct due to `lastindex(prefix)-lastindex(prefix)==0`, - # hence we need to add one to get the first index. This is also correct when considering - # pos, because pos is the `lastindex` a larger string which `endswith(path)==true`. - return matchList, startpos:pos, !isempty(matchList) +function complete_path(path::AbstractString, + pos::Int; + use_envpath=false, + shell_escape=false, + string_escape=false) + ## TODO: enable this depwarn once Pkg is fixed + #Base.depwarn("complete_path with pos argument is deprecated because the return value [2] is incorrect to use", :complete_path) + paths, dir, success = complete_path(path; use_envpath, shell_escape, string_escape) + if Base.Sys.isunix() && occursin(r"^~(?:/|$)", path) + # if the path is just "~", don't consider the expanded username as a prefix + if path == "~" + dir, prefix = homedir(), "" + else + dir, prefix = splitdir(homedir() * path[2:end]) + end + else + dir, prefix = splitdir(path) + end + startpos = pos - lastindex(prefix) + 1 + Sys.iswindows() && map!(paths, paths) do c::PathCompletion + # emulation for unnecessarily complicated return value, since / is a + # perfectly acceptable path character which does not require quoting + # but is required by Pkg's awkward parser handling + return endswith(c.path, "/") ? PathCompletion(chop(c.path) * "\\\\") : c + end + return paths, startpos:pos, success end function complete_expanduser(path::AbstractString, r) @@ -838,10 +870,11 @@ function afterusing(string::String, startpos::Int) return occursin(r"^\b(using|import)\s*((\w+[.])*\w+\s*,\s*)*$", str[fr:end]) end -function close_path_completion(str, startpos, r, paths, pos) +function close_path_completion(dir, paths, str, pos) length(paths) == 1 || return false # Only close if there's a single choice... - _path = str[startpos:prevind(str, first(r))] * (paths[1]::PathCompletion).path - path = expanduser(unescape_string(replace(_path, "\\\$"=>"\$", "\\\""=>"\""))) + path = (paths[1]::PathCompletion).path + path = unescape_string(replace(path, "\\\$"=>"\$")) + path = joinpath(dir, path) # ...except if it's a directory... try isdir(path) @@ -1136,42 +1169,80 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif return complete_identifiers!(Completion[], ffunc, context_module, string, string[startpos:pos], pos, dotpos, startpos) elseif inc_tag === :cmd - m = match(r"[\t\n\r\"`><=*?|]| (?!\\)", reverse(partial)) - startpos = nextind(partial, reverseind(partial, m.offset)) - r = startpos:pos - - # This expansion with "\\ "=>' ' replacement and shell_escape=true - # assumes the path isn't further quoted within the cmd backticks. - expanded = complete_expanduser(replace(string[r], r"\\ " => " "), r) - expanded[3] && return expanded # If user expansion available, return it - - paths, r, success = complete_path(replace(string[r], r"\\ " => " "), pos, - shell_escape=true) - - return sort!(paths, by=p->p.path), r, success + # TODO: should this call shell_completions instead of partially reimplementing it? + let m = match(r"[\t\n\r\"`><=*?|]| (?!\\)", reverse(partial)) # fuzzy shell_parse in reverse + startpos = nextind(partial, reverseind(partial, m.offset)) + r = startpos:pos + scs::String = string[r] + + expanded = complete_expanduser(scs, r) + expanded[3] && return expanded # If user expansion available, return it + + path::String = replace(scs, r"(\\+)\g1(\\?)`" => "\1\2`") # fuzzy unescape_raw_string: match an even number of \ before ` and replace with half as many + # This expansion with "\\ "=>' ' replacement and shell_escape=true + # assumes the path isn't further quoted within the cmd backticks. + path = replace(path, r"\\ " => " ", r"\$" => "\$") # fuzzy shell_parse (reversed by shell_escape_posixly) + paths, dir, success = complete_path(path, shell_escape=true, raw_escape=true) + + if success && !isempty(dir) + let dir = do_raw_escape(do_shell_escape(dir)) + # if escaping of dir matches scs prefix, remove that from the completions + # otherwise make it the whole completion + if endswith(dir, "/") && startswith(scs, dir) + r = (startpos + sizeof(dir)):pos + elseif startswith(scs, dir * "/") + r = nextind(string, startpos + sizeof(dir)):pos + else + map!(paths, paths) do c::PathCompletion + return PathCompletion(dir * "/" * c.path) + end + end + end + end + return sort!(paths, by=p->p.path), r, success + end elseif inc_tag === :string # Find first non-escaped quote - m = match(r"\"(?!\\)", reverse(partial)) - startpos = nextind(partial, reverseind(partial, m.offset)) - r = startpos:pos + let m = match(r"\"(?!\\)", reverse(partial)) + startpos = nextind(partial, reverseind(partial, m.offset)) + r = startpos:pos + scs::String = string[r] + + expanded = complete_expanduser(scs, r) + expanded[3] && return expanded # If user expansion available, return it + + path = try + unescape_string(replace(scs, "\\\$"=>"\$")) + catch ex + ex isa ArgumentError || rethrow() + nothing + end + if !isnothing(path) + paths, dir, success = complete_path(path::String, string_escape=true) - expanded = complete_expanduser(string[r], r) - expanded[3] && return expanded # If user expansion available, return it + if close_path_completion(dir, paths, path, pos) + paths[1] = PathCompletion((paths[1]::PathCompletion).path * "\"") + end - path_prefix = try - unescape_string(replace(string[r], "\\\$"=>"\$", "\\\""=>"\"")) - catch - nothing - end - if !isnothing(path_prefix) - paths, r, success = complete_path(path_prefix, pos, string_escape=true) + if success && !isempty(dir) + let dir = do_string_escape(dir) + # if escaping of dir matches scs prefix, remove that from the completions + # otherwise make it the whole completion + if endswith(dir, "/") && startswith(scs, dir) + r = (startpos + sizeof(dir)):pos + elseif startswith(scs, dir * "/") + r = nextind(string, startpos + sizeof(dir)):pos + else + map!(paths, paths) do c::PathCompletion + return PathCompletion(dir * "/" * c.path) + end + end + end + end - if close_path_completion(string, startpos, r, paths, pos) - paths[1] = PathCompletion((paths[1]::PathCompletion).path * "\"") + # Fallthrough allowed so that Latex symbols can be completed in strings + success && return sort!(paths, by=p->p.path), r, success end - - # Fallthrough allowed so that Latex symbols can be completed in strings - success && return sort!(paths, by=p->p.path), r, success end end @@ -1257,35 +1328,58 @@ end function shell_completions(string, pos) # First parse everything up to the current position scs = string[1:pos] - local args, last_parse - try - args, last_parse = Base.shell_parse(scs, true)::Tuple{Expr,UnitRange{Int}} - catch + args, last_arg_start = try + Base.shell_parse(scs, true)::Tuple{Expr,Int} + catch ex + ex isa ArgumentError || ex isa ErrorException || rethrow() return Completion[], 0:-1, false end ex = args.args[end]::Expr # Now look at the last thing we parsed isempty(ex.args) && return Completion[], 0:-1, false - arg = ex.args[end] - if all(s -> isa(s, AbstractString), ex.args) - arg = arg::AbstractString - # Treat this as a path - - # As Base.shell_parse throws away trailing spaces (unless they are escaped), - # we need to special case here. - # If the last char was a space, but shell_parse ignored it search on "". - ignore_last_word = arg != " " && scs[end] == ' ' - prefix = ignore_last_word ? "" : join(ex.args) + lastarg = ex.args[end] + # As Base.shell_parse throws away trailing spaces (unless they are escaped), + # we need to special case here. + # If the last char was a space, but shell_parse ignored it search on "". + if isexpr(lastarg, :incomplete) || isexpr(lastarg, :error) + partial = string[last_arg_start:pos] + ret, range = completions(partial, lastindex(partial)) + range = range .+ (last_arg_start - 1) + return ret, range, true + elseif endswith(scs, ' ') && !endswith(scs, "\\ ") + r = pos+1:pos + paths, dir, success = complete_path("", use_envpath=false, shell_escape=true) + return paths, r, success + elseif all(arg -> arg isa AbstractString, ex.args) + # Join these and treat this as a path + path::String = join(ex.args) + r = last_arg_start:pos # Also try looking into the env path if the user wants to complete the first argument - use_envpath = !ignore_last_word && length(args.args) < 2 + use_envpath = length(args.args) < 2 - return complete_path(prefix, pos, use_envpath=use_envpath, shell_escape=true) - elseif isexpr(arg, :incomplete) || isexpr(arg, :error) - partial = scs[last_parse] - ret, range = completions(partial, lastindex(partial)) - range = range .+ (first(last_parse) - 1) - return ret, range, true + # TODO: call complete_expanduser here? + + paths, dir, success = complete_path(path, use_envpath=use_envpath, shell_escape=true) + + if success && !isempty(dir) + let dir = do_shell_escape(dir) + # if escaping of dir matches scs prefix, remove that from the completions + # otherwise make it the whole completion + partial = string[last_arg_start:pos] + if endswith(dir, "/") && startswith(partial, dir) + r = (last_arg_start + sizeof(dir)):pos + elseif startswith(partial, dir * "/") + r = nextind(string, last_arg_start + sizeof(dir)):pos + else + map!(paths, paths) do c::PathCompletion + return PathCompletion(dir * "/" * c.path) + end + end + end + end + + return paths, r, success end return Completion[], 0:-1, false end diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index 8551c5f38fcd3f..41f30204470909 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -184,7 +184,7 @@ log_nonpublic_access(expr, ::Module, _) = expr function insert_internal_warning(md::Markdown.MD, internal_access::Set{Pair{Module,Symbol}}) if !isempty(internal_access) - items = Any[Any[Markdown.Paragraph(Any[Markdown.Code("", s)])] for s in sort("$mod.$sym" for (mod, sym) in internal_access)] + items = Any[Any[Markdown.Paragraph(Any[Markdown.Code("", s)])] for s in sort!(["$mod.$sym" for (mod, sym) in internal_access])] admonition = Markdown.Admonition("warning", "Warning", Any[ Markdown.Paragraph(Any["The following bindings may be internal; they may change or be removed in future versions:"]), Markdown.List(items, -1, false)]) diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index a1cd746fbeb887..97c494cb331d3d 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -205,9 +205,5 @@ end generate_precompile_statements() -# As a last step in system image generation, -# remove some references to build time environment for a more reproducible build. -Base.Filesystem.temp_cleanup_purge(force=true) - precompile(Tuple{typeof(getproperty), REPL.REPLBackend, Symbol}) end # Precompile diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 4e7f8b2becc3c9..868457d6514cdb 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -327,7 +327,7 @@ end let s = "\\alpha" c, r = test_bslashcomplete(s) @test c[1] == "α" - @test r == 1:length(s) + @test r == 1:lastindex(s) @test length(c) == 1 end @@ -1043,7 +1043,7 @@ let s, c, r s = "@show \"/dev/nul\"" c,r = completions(s, 15) c = map(completion_text, c) - @test "null" in c + @test "null\"" in c @test r == 13:15 @test s[r] == "nul" @@ -1067,8 +1067,8 @@ let s, c, r if !isdir(joinpath(s, "tmp")) c,r = test_scomplete(s) @test !("tmp/" in c) - @test r === length(s) + 1:0 - @test s[r] == "" + @test !("$s/tmp/" in c) + @test r === (sizeof(s) + 1):sizeof(s) end s = "cd \$(Iter" @@ -1093,7 +1093,7 @@ let s, c, r touch(file) s = string(tempdir(), "/repl\\ ") c,r = test_scomplete(s) - @test ["repl\\ completions"] == c + @test ["'repl completions'"] == c @test s[r] == "repl\\ " rm(file) end @@ -1194,7 +1194,7 @@ let current_dir, forbidden catch e e isa Base.IOError && occursin("ELOOP", e.msg) end - c, r = test_complete("\""*escape_string(joinpath(path, "selfsym"))) + c, r = test_complete("\"$(escape_string(path))/selfsym") @test c == ["selfsymlink"] end end @@ -1231,20 +1231,20 @@ mktempdir() do path dir_space = replace(space_folder, " " => "\\ ") s = Sys.iswindows() ? "cd $dir_space\\\\space" : "cd $dir_space/space" c, r = test_scomplete(s) - @test s[r] == "space" - @test "space\\ .file" in c + @test s[r] == (Sys.iswindows() ? "$dir_space\\\\space" : "$dir_space/space") + @test "'$space_folder'/'space .file'" in c # Also use shell escape rules within cmd backticks s = "`$s" c, r = test_scomplete(s) - @test s[r] == "space" - @test "space\\ .file" in c + @test s[r] == (Sys.iswindows() ? "$dir_space\\\\space" : "$dir_space/space") + @test "'$space_folder'/'space .file'" in c # escape string according to Julia escaping rules - julia_esc(str) = escape_string(str, ('\"','$')) + julia_esc(str) = REPL.REPLCompletions.do_string_escape(str) # For normal strings the string should be properly escaped according to # the usual rules for Julia strings. - s = "cd(\"" * julia_esc(joinpath(path, space_folder, "space")) + s = "cd(\"" * julia_esc(joinpath(path, space_folder) * "/space") c, r = test_complete(s) @test s[r] == "space" @test "space .file\"" in c @@ -1253,7 +1253,7 @@ mktempdir() do path # which needs to be escaped in Julia strings (on unix we could do this # test with all sorts of special chars) touch(joinpath(space_folder, "needs_escape\$.file")) - escpath = julia_esc(joinpath(path, space_folder, "needs_escape\$")) + escpath = julia_esc(joinpath(path, space_folder) * "/needs_escape\$") s = "cd(\"$escpath" c, r = test_complete(s) @test s[r] == "needs_escape\\\$" @@ -1290,12 +1290,12 @@ mktempdir() do path # in shell commands the shell path completion cannot complete # paths with these characters c, r, res = test_scomplete(test_dir) - @test c[1] == test_dir*(Sys.iswindows() ? "\\\\" : "/") + @test c[1] == "'$test_dir/'" @test res end escdir = julia_esc(test_dir) c, r, res = test_complete("\""*escdir) - @test c[1] == escdir*(Sys.iswindows() ? "\\\\" : "/") + @test c[1] == escdir * "/" @test res finally rm(joinpath(path, test_dir), recursive=true) @@ -1331,27 +1331,43 @@ if Sys.iswindows() cd(path) do s = "cd ..\\\\" c,r = test_scomplete(s) - @test r == length(s)+1:length(s) - @test temp_name * "\\\\" in c + @test r == lastindex(s)-3:lastindex(s) + @test "../$temp_name/" in c + + s = "cd ../" + c,r = test_scomplete(s) + @test r == lastindex(s)+1:lastindex(s) + @test "$temp_name/" in c s = "ls $(file[1:2])" c,r = test_scomplete(s) - @test r == length(s)-1:length(s) + @test r == lastindex(s)-1:lastindex(s) @test file in c s = "cd(\"..\\\\" c,r = test_complete(s) - @test r == length(s)+1:length(s) - @test temp_name * "\\\\" in c + @test r == lastindex(s)-3:lastindex(s) + @test "../$temp_name/" in c + + s = "cd(\"../" + c,r = test_complete(s) + @test r == lastindex(s)+1:lastindex(s) + @test "$temp_name/" in c s = "cd(\"$(file[1:2])" c,r = test_complete(s) - @test r == length(s) - 1:length(s) + @test r == lastindex(s) - 1:lastindex(s) @test (length(c) > 1 && file in c) || (["$file\""] == c) end rm(tmp) end +# issue 51985 +let s = "`\\" + c,r = test_scomplete(s) + @test r == lastindex(s)+1:lastindex(s) +end + # auto completions of true and false... issue #14101 let s = "tru" c, r, res = test_complete(s) @@ -2123,3 +2139,12 @@ end # issue #51823 @test "include" in test_complete_context("inc", Main)[1] + +# REPL completions should not try to concrete-evaluate !:noub methods +function very_unsafe_method(i::Int) + xs = Any[] + @inbounds xs[i] +end +let t = REPLCompletions.repl_eval_ex(:(unsafe_method(42)), @__MODULE__) + @test isnothing(t) +end diff --git a/stdlib/Serialization/src/Serialization.jl b/stdlib/Serialization/src/Serialization.jl index 8e8290569cdb72..46081f38cf9aa1 100644 --- a/stdlib/Serialization/src/Serialization.jl +++ b/stdlib/Serialization/src/Serialization.jl @@ -80,7 +80,7 @@ const TAGS = Any[ const NTAGS = length(TAGS) @assert NTAGS == 255 -const ser_version = 25 # do not make changes without bumping the version #! +const ser_version = 26 # do not make changes without bumping the version #! format_version(::AbstractSerializer) = ser_version format_version(s::Serializer) = s.version @@ -1058,7 +1058,8 @@ function deserialize(s::AbstractSerializer, ::Type{Method}) isva = deserialize(s)::Bool is_for_opaque_closure = false nospecializeinfer = false - constprop = purity = 0x00 + constprop = 0x00 + purity = 0x0000 template_or_is_opaque = deserialize(s) if isa(template_or_is_opaque, Bool) is_for_opaque_closure = template_or_is_opaque @@ -1068,8 +1069,10 @@ function deserialize(s::AbstractSerializer, ::Type{Method}) if format_version(s) >= 14 constprop = deserialize(s)::UInt8 end - if format_version(s) >= 17 - purity = deserialize(s)::UInt8 + if format_version(s) >= 26 + purity = deserialize(s)::UInt16 + elseif format_version(s) >= 17 + purity = UInt16(deserialize(s)::UInt8) end template = deserialize(s) else @@ -1242,7 +1245,9 @@ function deserialize(s::AbstractSerializer, ::Type{CodeInfo}) if format_version(s) >= 14 ci.constprop = deserialize(s)::UInt8 end - if format_version(s) >= 17 + if format_version(s) >= 26 + ci.purity = deserialize(s)::UInt16 + elseif format_version(s) >= 17 ci.purity = deserialize(s)::UInt8 end if format_version(s) >= 22 diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version index 761eb10fd20625..68cdbbf3fca7c0 100644 --- a/stdlib/SparseArrays.version +++ b/stdlib/SparseArrays.version @@ -1,4 +1,4 @@ SPARSEARRAYS_BRANCH = main -SPARSEARRAYS_SHA1 = 37fc321e28a32f79a928c24e5739a83d92de5205 +SPARSEARRAYS_SHA1 = f154de2b6801ec8d5afaf58b73b830c8e71013c3 SPARSEARRAYS_GIT_URL := https://github.com/JuliaSparse/SparseArrays.jl.git SPARSEARRAYS_TAR_URL = https://api.github.com/repos/JuliaSparse/SparseArrays.jl/tarball/$1 diff --git a/stdlib/Test/docs/src/index.md b/stdlib/Test/docs/src/index.md index 3d133419a17922..ddb5d580ef8322 100644 --- a/stdlib/Test/docs/src/index.md +++ b/stdlib/Test/docs/src/index.md @@ -491,3 +491,15 @@ Using `Test.jl`, more complicated tests can be added for packages but this shoul ```@meta DocTestSetup = nothing ``` + +### Code Coverage + +Code coverage tracking during tests can be enabled using the `pkg> test --coverage` flag (or at a lower level using the +[`--code-coverage`](@ref command-line-interface) julia arg). This is on by default in the +[julia-runtest](https://github.com/julia-actions/julia-runtest) GitHub action. + +To evaluate coverage either manually inspect the `.cov` files that are generated beside the source files locally, +or in CI use the [julia-processcoverage](https://github.com/julia-actions/julia-processcoverage) GitHub action. + +!!! compat "Julia 1.11" + Since Julia 1.11, coverage is not collected during the package precompilation phase. diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 7e416df91864e0..9742975d7c464a 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -1814,7 +1814,7 @@ matches the inferred type modulo `AllowedType`, or when the return type is a sub `AllowedType`. This is useful when testing type stability of functions returning a small union such as `Union{Nothing, T}` or `Union{Missing, T}`. -```jldoctest; setup = :(using InteractiveUtils), filter = r"begin\\n(.|\\n)*end" +```jldoctest; setup = :(using InteractiveUtils; using Base: >), filter = r"begin\\n(.|\\n)*end" julia> f(a) = a > 1 ? 1 : 1.0 f (generic function with 1 method) diff --git a/stdlib/nghttp2_jll/Project.toml b/stdlib/nghttp2_jll/Project.toml index d75e0780bd01a0..560a9537a3b216 100644 --- a/stdlib/nghttp2_jll/Project.toml +++ b/stdlib/nghttp2_jll/Project.toml @@ -1,6 +1,6 @@ name = "nghttp2_jll" uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.52.0+1" +version = "1.58.0+0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/nghttp2_jll/test/runtests.jl b/stdlib/nghttp2_jll/test/runtests.jl index 2f9af6d6a3338a..8155e4e787fccf 100644 --- a/stdlib/nghttp2_jll/test/runtests.jl +++ b/stdlib/nghttp2_jll/test/runtests.jl @@ -11,5 +11,5 @@ end @testset "nghttp2_jll" begin info = unsafe_load(ccall((:nghttp2_version,libnghttp2), Ptr{nghttp2_info}, (Cint,), 0)) - @test VersionNumber(unsafe_string(info.version_str)) == v"1.52.0" + @test VersionNumber(unsafe_string(info.version_str)) == v"1.58.0" end diff --git a/test/Makefile b/test/Makefile index d8605341ff9b9a..c3b877793f4b5e 100644 --- a/test/Makefile +++ b/test/Makefile @@ -7,7 +7,7 @@ STDLIBDIR := $(build_datarootdir)/julia/stdlib/$(VERSDIR) # TODO: this Makefile ignores BUILDDIR, except for computing JULIA_EXECUTABLE export JULIA_DEPOT_PATH := $(build_prefix)/share/julia -export JULIA_LOAD_PATH := @stdlib +export JULIA_LOAD_PATH := @$(PATHSEP)@stdlib unexport JULIA_PROJECT := unexport JULIA_BINDIR := diff --git a/test/arrayops.jl b/test/arrayops.jl index 30f00ca5d5a461..9c9aac987929d5 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1446,6 +1446,15 @@ end @test sortslices(B, dims=(1,3)) == B end +@testset "sortslices inference (#52019)" begin + x = rand(3, 2) + @inferred sortslices(x, dims=1) + @inferred sortslices(x, dims=(2,)) + x = rand(1, 2, 3) + @inferred sortslices(x, dims=(1,2)) + @inferred sortslices(x, dims=3, by=sum) +end + @testset "fill" begin @test fill!(Float64[1.0], -0.0)[1] === -0.0 A = fill(1.,3,3) diff --git a/test/atexit.jl b/test/atexit.jl index 64b56e32466df3..4a37d465f250bd 100644 --- a/test/atexit.jl +++ b/test/atexit.jl @@ -214,12 +214,13 @@ using Test # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 3. attempting to register a hook after all hooks have finished (disallowed) """ - const atexit_has_finished = Threads.Atomic{Bool}(false) + const atexit_has_finished = Threads.Atomic{Int}(0) atexit() do Threads.@spawn begin # Block until the atexit hooks have all finished. We use a manual "spin # lock" because task switch is disallowed inside the finalizer, below. - while !atexit_has_finished[] end + atexit_has_finished[] = 1 + while atexit_has_finished[] == 1 end try # By the time this runs, all the atexit hooks will be done. # So this will throw. @@ -231,15 +232,16 @@ using Test exit(22) end end + while atexit_has_finished[] == 0 end end # Finalizers run after the atexit hooks, so this blocks exit until the spawned # task above gets a chance to run. x = [] finalizer(x) do x # Allow the spawned task to finish - atexit_has_finished[] = true + atexit_has_finished[] = 2 # Then spin forever to prevent exit. - while atexit_has_finished[] end + while atexit_has_finished[] == 2 end end exit(0) """ => 22, diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index c1681532d26ea1..1c90352af29c91 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -62,8 +62,19 @@ end @testset "julia_cmd" begin julia_basic = Base.julia_cmd() + function get_julia_cmd(arg) + io = Base.BufferStream() + cmd = `$julia_basic $arg -e 'print(repr(Base.julia_cmd()))'` + try + run(pipeline(cmd, stdout=io, stderr=io)) + catch + @error "cmd failed" cmd read(io, String) + rethrow() + end + return read(io, String) + end + opts = Base.JLOptions() - get_julia_cmd(arg) = strip(read(`$julia_basic $arg -e 'print(repr(Base.julia_cmd()))'`, String), ['`']) for (arg, default) in ( ("-C$(unsafe_string(opts.cpu_target))", false), @@ -473,10 +484,47 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` @test occursin(expected, got) || (expected, got) @test_broken occursin(expected_good, got) + # Ask for coverage in current directory + tdir = dirname(realpath(inputfile)) + cd(tdir) do + # there may be atrailing separator here so use rstrip + @test readchomp(`$exename -E "(Base.JLOptions().code_coverage, rstrip(unsafe_string(Base.JLOptions().tracked_path), '/'))" -L $inputfile + --code-coverage=$covfile --code-coverage=@`) == "(3, $(repr(tdir)))" + end + @test isfile(covfile) + got = read(covfile, String) + rm(covfile) + @test occursin(expected, got) || (expected, got) + @test_broken occursin(expected_good, got) + + # Ask for coverage in relative directory + tdir = dirname(realpath(inputfile)) + cd(dirname(tdir)) do + @test readchomp(`$exename -E "(Base.JLOptions().code_coverage, unsafe_string(Base.JLOptions().tracked_path))" -L $inputfile + --code-coverage=$covfile --code-coverage=@testhelpers`) == "(3, $(repr(tdir)))" + end + @test isfile(covfile) + got = read(covfile, String) + rm(covfile) + @test occursin(expected, got) || (expected, got) + @test_broken occursin(expected_good, got) + + # Ask for coverage in relative directory with dot-dot notation + tdir = dirname(realpath(inputfile)) + cd(tdir) do + @test readchomp(`$exename -E "(Base.JLOptions().code_coverage, unsafe_string(Base.JLOptions().tracked_path))" -L $inputfile + --code-coverage=$covfile --code-coverage=@../testhelpers`) == "(3, $(repr(tdir)))" + end + @test isfile(covfile) + got = read(covfile, String) + rm(covfile) + @test occursin(expected, got) || (expected, got) + @test_broken occursin(expected_good, got) + # Ask for coverage in a different directory tdir = mktempdir() # a dir that contains no code @test readchomp(`$exename -E "(Base.JLOptions().code_coverage, unsafe_string(Base.JLOptions().tracked_path))" -L $inputfile - --code-coverage=$covfile --code-coverage=@$tdir`) == "(3, $(repr(tdir)))" + --code-coverage=$covfile --code-coverage=@$tdir`) == "(3, $(repr(realpath(tdir))))" @test isfile(covfile) got = read(covfile, String) @test isempty(got) @@ -996,11 +1044,10 @@ end @test readchomp(`$(Base.julia_cmd()) -e 'module Hello; export main; (@main)(ARGS) = println("hello"); end; import .Hello'`) == "" # test --bug-report=rr -if Sys.islinux() && Sys.ARCH in (:i686, :x86_64, :aarch64) # rr is only available on these platforms +if Sys.islinux() && Sys.ARCH in (:i686, :x86_64) # rr is only available on these platforms mktempdir() do temp_trace_dir @test success(pipeline(setenv(`$(Base.julia_cmd()) --bug-report=rr-local -e 'exit()'`, "JULIA_RR_RECORD_ARGS" => "-n --nested=ignore", - "_RR_TRACE_DIR" => temp_trace_dir); - #=stdout, stderr=#)) + "_RR_TRACE_DIR" => temp_trace_dir); #=stderr, stdout=#)) end end diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index 2980e456e9f9c9..9580fc55508ac5 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -464,6 +464,7 @@ let # generate cache lookup) io = IOBuffer() code_llvm(io, custom_lookup_target, (Bool,Int,); params) - @test occursin("j_sin_", String(take!(io))) - @test !occursin("j_cos_", String(take!(io))) + s = String(take!(io)) + @test occursin("j_sin_", s) + @test !occursin("j_cos_", s) end diff --git a/test/compiler/EscapeAnalysis/EAUtils.jl b/test/compiler/EscapeAnalysis/EAUtils.jl index 937113c34c872d..27a971173ac700 100644 --- a/test/compiler/EscapeAnalysis/EAUtils.jl +++ b/test/compiler/EscapeAnalysis/EAUtils.jl @@ -62,14 +62,13 @@ __clear_cache!() = empty!(GLOBAL_EA_CODE_CACHE) # imports import .CC: AbstractInterpreter, NativeInterpreter, WorldView, WorldRange, - InferenceParams, OptimizationParams, get_world_counter, get_inference_cache, code_cache + InferenceParams, OptimizationParams, get_world_counter, get_inference_cache, code_cache, + ipo_dataflow_analysis!, cache_result! # usings using Core: CodeInstance, MethodInstance, CodeInfo using .CC: - InferenceResult, OptimizationState, IRCode, copy as cccopy, - @timeit, convert_to_ircode, slot2reg, compact!, ssa_inlining_pass!, sroa_pass!, - adce_pass!, JLOptions, verify_ir, verify_linetable + InferenceResult, OptimizationState, IRCode using .EA: analyze_escapes, ArgEscapeCache, EscapeInfo, EscapeState struct CodeCache @@ -144,17 +143,31 @@ function CC.setindex!(wvc::WorldView{EscapeAnalyzerCacheView}, ci::CodeInstance, return wvc end -function CC.optimize(interp::EscapeAnalyzer, opt::OptimizationState, caller::InferenceResult) - ir = run_passes_ipo_safe_with_ea(interp, opt.src, opt, caller) - CC.ipo_dataflow_analysis!(interp, ir, caller) - return CC.finish(interp, opt, ir, caller) +function CC.ipo_dataflow_analysis!(interp::EscapeAnalyzer, ir::IRCode, caller::InferenceResult) + # run EA on all frames that have been optimized + nargs = let def = caller.linfo.def; isa(def, Method) ? Int(def.nargs) : 0; end + get_escape_cache = GetEscapeCache(interp) + estate = try + analyze_escapes(ir, nargs, CC.optimizer_lattice(interp), get_escape_cache) + catch err + @error "error happened within EA, inspect `Main.failed_escapeanalysis`" + Main.failed_escapeanalysis = FailedAnalysis(ir, nargs, get_escape_cache) + rethrow(err) + end + if caller.linfo === interp.entry_mi + # return back the result + interp.result = EscapeResultForEntry(CC.copy(ir), estate, caller.linfo) + end + record_escapes!(interp, caller, estate, ir) + + @invoke CC.ipo_dataflow_analysis!(interp::AbstractInterpreter, ir::IRCode, caller::InferenceResult) end function record_escapes!(interp::EscapeAnalyzer, - caller::InferenceResult, estate::EscapeState, cacheir::IRCode) - cache = ArgEscapeCache(estate) - ecache = EscapeCacheInfo(cache, estate, cacheir) - return caller.argescapes = ecache + caller::InferenceResult, estate::EscapeState, ir::IRCode) + argescapes = ArgEscapeCache(estate) + ecacheinfo = EscapeCacheInfo(argescapes, estate, ir) + return CC.stack_analysis_result!(caller, ecacheinfo) end struct GetEscapeCache @@ -162,8 +175,8 @@ struct GetEscapeCache GetEscapeCache(interp::EscapeAnalyzer) = new(interp.escape_cache) end function ((; escape_cache)::GetEscapeCache)(mi::MethodInstance) - cached = get(escape_cache.cache, mi, nothing) - return cached === nothing ? nothing : cached.argescapes + ecacheinfo = get(escape_cache.cache, mi, nothing) + return ecacheinfo === nothing ? false : ecacheinfo.argescapes end struct FailedAnalysis @@ -172,45 +185,12 @@ struct FailedAnalysis get_escape_cache::GetEscapeCache end -function run_passes_ipo_safe_with_ea(interp::EscapeAnalyzer, - ci::CodeInfo, sv::OptimizationState, caller::InferenceResult) - @timeit "convert" ir = convert_to_ircode(ci, sv) - @timeit "slot2reg" ir = slot2reg(ir, ci, sv) - # TODO: Domsorting can produce an updated domtree - no need to recompute here - @timeit "compact 1" ir = compact!(ir) - @timeit "Inlining" ir = ssa_inlining_pass!(ir, sv.inlining, ci.propagate_inbounds) - # @timeit "verify 2" verify_ir(ir) - @timeit "compact 2" ir = compact!(ir) - @timeit "SROA" ir = sroa_pass!(ir, sv.inlining) - @timeit "ADCE" ir = adce_pass!(ir, sv.inlining) - @timeit "compact 3" ir = compact!(ir, true) - if JLOptions().debug_level == 2 - @timeit "verify 3" (verify_ir(ir); verify_linetable(ir.linetable)) - end - nargs = let def = sv.linfo.def; isa(def, Method) ? Int(def.nargs) : 0; end - get_escape_cache = GetEscapeCache(interp) - local estate::EscapeState - try - @timeit "EA" estate = analyze_escapes(ir, nargs, get_escape_cache) - catch err - @error "error happened within EA, inspect `Main.failed_escapeanalysis`" - Main.failed_escapeanalysis = FailedAnalysis(ir, nargs, get_escape_cache) - rethrow(err) - end - if caller.linfo === interp.entry_mi - # return back the result - interp.result = EscapeResultForEntry(cccopy(ir), estate, sv.linfo) - end - record_escapes!(interp, caller, estate, ir) - return ir -end - -function CC.cache_result!(interp::EscapeAnalyzer, result::InferenceResult) - argescapes = result.argescapes - if argescapes isa EscapeCacheInfo - interp.escape_cache.cache[result.linfo] = argescapes +function CC.cache_result!(interp::EscapeAnalyzer, inf_result::InferenceResult) + ecacheinfo = CC.traverse_analysis_results(inf_result) do @nospecialize result + return result isa EscapeCacheInfo ? result : nothing end - return @invoke CC.cache_result!(interp::AbstractInterpreter, result::InferenceResult) + ecacheinfo isa EscapeCacheInfo && (interp.escape_cache.cache[inf_result.linfo] = ecacheinfo) + return @invoke CC.cache_result!(interp::AbstractInterpreter, inf_result::InferenceResult) end # printing @@ -278,7 +258,7 @@ Base.show(io::IO, result::EscapeResult) = print_with_info(io, result) @eval Base.iterate(res::EscapeResult, state=1) = return state > $(fieldcount(EscapeResult)) ? nothing : (getfield(res, state), state+1) -Base.show(io::IO, cached::EscapeCacheInfo) = show(io, EscapeResult(cached.ir, cached.state)) +Base.show(io::IO, ecacheinfo::EscapeCacheInfo) = show(io, EscapeResult(ecacheinfo.ir, ecacheinfo.state)) # adapted from https://github.com/JuliaDebug/LoweredCodeUtils.jl/blob/4612349432447e868cf9285f647108f43bd0a11c/src/codeedges.jl#L881-L897 function print_with_info(io::IO, result::EscapeResult) diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index ad2c60723adf65..5405066263d79f 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -477,9 +477,10 @@ end |> Core.Compiler.is_effect_free # `getfield_effects` handles access to union object nicely let 𝕃 = Core.Compiler.fallback_lattice - @test Core.Compiler.is_consistent(Core.Compiler.getfield_effects(𝕃, Core.Compiler.ArgInfo(nothing, Any[Core.Const(getfield), Some{String}, Core.Const(:value)]), String)) - @test Core.Compiler.is_consistent(Core.Compiler.getfield_effects(𝕃, Core.Compiler.ArgInfo(nothing, Any[Core.Const(getfield), Some{Symbol}, Core.Const(:value)]), Symbol)) - @test Core.Compiler.is_consistent(Core.Compiler.getfield_effects(𝕃, Core.Compiler.ArgInfo(nothing, Any[Core.Const(getfield), Union{Some{Symbol},Some{String}}, Core.Const(:value)]), Union{Symbol,String})) + getfield_effects = Core.Compiler.getfield_effects + @test Core.Compiler.is_consistent(getfield_effects(𝕃, Any[Some{String}, Core.Const(:value)], String)) + @test Core.Compiler.is_consistent(getfield_effects(𝕃, Any[Some{Symbol}, Core.Const(:value)], Symbol)) + @test Core.Compiler.is_consistent(getfield_effects(𝕃, Any[Union{Some{Symbol},Some{String}}, Core.Const(:value)], Union{Symbol,String})) end @test Base.infer_effects((Bool,)) do c obj = c ? Some{String}("foo") : Some{Symbol}(:bar) @@ -880,7 +881,7 @@ end # Test that builtin_effects handles vararg correctly @test !Core.Compiler.is_nothrow(Core.Compiler.builtin_effects(Core.Compiler.fallback_lattice, Core.isdefined, - Core.Compiler.ArgInfo(nothing, Any[Core.Compiler.Const(Core.isdefined), String, Vararg{Any}]), Bool)) + Any[String, Vararg{Any}], Bool)) # Test that :new can be eliminated even if an sparam is unknown struct SparamUnused{T} @@ -1132,21 +1133,61 @@ end post_opt_refine_effect_free(y, true) end |> Core.Compiler.is_effect_free -# effects for Cmd construction -for f in (() -> `a b c`, () -> `a a$("bb")a $("c")`) - effects = Base.infer_effects(f) - @test Core.Compiler.is_effect_free(effects) - @test Core.Compiler.is_terminates(effects) - @test Core.Compiler.is_noub(effects) - @test !Core.Compiler.is_consistent(effects) +# Check EA-based refinement of :effect_free +Base.@assume_effects :nothrow @noinline _noinline_set!(x) = (x[] = 1; nothing) + +function set_ref_with_unused_arg_1(_) + x = Ref(0) + _noinline_set!(x) + return nothing end -let effects = Base.infer_effects(x -> `a $x`, (Any,)) - @test !Core.Compiler.is_effect_free(effects) - @test !Core.Compiler.is_terminates(effects) - @test !Core.Compiler.is_noub(effects) - @test !Core.Compiler.is_consistent(effects) +function set_ref_with_unused_arg_2(_) + x = @noinline Ref(0) + _noinline_set!(x) + return nothing +end +function set_arg_ref!(x) + _noinline_set!(x) + y = Ref(false) + y[] && (Main.x = x) + return nothing end +function set_arr_with_unused_arg_1(_) + x = Int[0] + _noinline_set!(x) + return nothing +end +function set_arr_with_unused_arg_2(_) + x = @noinline Int[0] + _noinline_set!(x) + return nothing +end +function set_arg_arr!(x) + _noinline_set!(x) + y = Bool[false] + y[] && (Main.x = x) + return nothing +end + +# This is inferable by type analysis only since the arguments have no mutable memory +@test Core.Compiler.is_effect_free_if_inaccessiblememonly(Base.infer_effects(_noinline_set!, (Base.RefValue{Int},))) +@test Core.Compiler.is_effect_free_if_inaccessiblememonly(Base.infer_effects(_noinline_set!, (Vector{Int},))) +for func in (set_ref_with_unused_arg_1, set_ref_with_unused_arg_2, + set_arr_with_unused_arg_1, set_arr_with_unused_arg_2) + effects = Base.infer_effects(func, (Nothing,)) + @test Core.Compiler.is_inaccessiblememonly(effects) + @test Core.Compiler.is_effect_free(effects) +end + +# These need EA +@test Core.Compiler.is_effect_free(Base.infer_effects(set_ref_with_unused_arg_1, (Base.RefValue{Int},))) +@test Core.Compiler.is_effect_free(Base.infer_effects(set_ref_with_unused_arg_2, (Base.RefValue{Int},))) +@test Core.Compiler.is_effect_free_if_inaccessiblememonly(Base.infer_effects(set_arg_ref!, (Base.RefValue{Int},))) +@test_broken Core.Compiler.is_effect_free(Base.infer_effects(set_arr_with_unused_arg_1, (Vector{Int},))) +@test_broken Core.Compiler.is_effect_free(Base.infer_effects(set_arr_with_unused_arg_2, (Vector{Int},))) +@test_broken Core.Compiler.is_effect_free_if_inaccessiblememonly(Base.infer_effects(set_arg_arr!, (Vector{Int},))) + function issue51837(; openquotechar::Char, newlinechar::Char) ncodeunits(openquotechar) == 1 || throw(ArgumentError("`openquotechar` must be a single-byte character")) if !isnothing(newlinechar) @@ -1172,3 +1213,70 @@ callgetfield_inbounds(x, f) = @inbounds callgetfield2(x, f) @test Base.infer_effects(callgetfield_inbounds, (Some{Any},Symbol)).noub === Base.infer_effects(callgetfield_inbounds, (Some{Any},Symbol)).noub === Core.Compiler.ALWAYS_FALSE + +# noub modeling for memory ops +let (memoryref, memoryrefget, memoryref_isassigned, memoryrefset!) = + (Core.memoryref, Core.memoryrefget, Core.memoryref_isassigned, Core.memoryrefset!) + function builtin_effects(@nospecialize xs...) + interp = Core.Compiler.NativeInterpreter() + 𝕃 = Core.Compiler.typeinf_lattice(interp) + rt = Core.Compiler.builtin_tfunction(interp, xs..., nothing) + return Core.Compiler.builtin_effects(𝕃, xs..., rt) + end + @test Core.Compiler.is_noub(builtin_effects(memoryref, Any[Memory,])) + @test Core.Compiler.is_noub(builtin_effects(memoryref, Any[MemoryRef,Int])) + @test Core.Compiler.is_noub(builtin_effects(memoryref, Any[MemoryRef,Int,Core.Const(true)])) + @test !Core.Compiler.is_noub(builtin_effects(memoryref, Any[MemoryRef,Int,Core.Const(false)])) + @test !Core.Compiler.is_noub(builtin_effects(memoryref, Any[MemoryRef,Int,Bool])) + @test Core.Compiler.is_noub(builtin_effects(memoryref, Any[MemoryRef,Int,Int])) + @test !Core.Compiler.is_noub(builtin_effects(memoryref, Any[MemoryRef,Int,Vararg{Bool}])) + @test !Core.Compiler.is_noub(builtin_effects(memoryref, Any[MemoryRef,Vararg{Any}])) + @test Core.Compiler.is_noub(builtin_effects(memoryrefget, Any[MemoryRef,Symbol,Core.Const(true)])) + @test !Core.Compiler.is_noub(builtin_effects(memoryrefget, Any[MemoryRef,Symbol,Core.Const(false)])) + @test !Core.Compiler.is_noub(builtin_effects(memoryrefget, Any[MemoryRef,Symbol,Bool])) + @test Core.Compiler.is_noub(builtin_effects(memoryrefget, Any[MemoryRef,Symbol,Int])) + @test !Core.Compiler.is_noub(builtin_effects(memoryrefget, Any[MemoryRef,Symbol,Vararg{Bool}])) + @test !Core.Compiler.is_noub(builtin_effects(memoryrefget, Any[MemoryRef,Vararg{Any}])) + @test Core.Compiler.is_noub(builtin_effects(memoryref_isassigned, Any[MemoryRef,Symbol,Core.Const(true)])) + @test !Core.Compiler.is_noub(builtin_effects(memoryref_isassigned, Any[MemoryRef,Symbol,Core.Const(false)])) + @test !Core.Compiler.is_noub(builtin_effects(memoryref_isassigned, Any[MemoryRef,Symbol,Bool])) + @test Core.Compiler.is_noub(builtin_effects(memoryref_isassigned, Any[MemoryRef,Symbol,Int])) + @test !Core.Compiler.is_noub(builtin_effects(memoryref_isassigned, Any[MemoryRef,Symbol,Vararg{Bool}])) + @test !Core.Compiler.is_noub(builtin_effects(memoryref_isassigned, Any[MemoryRef,Vararg{Any}])) + @test Core.Compiler.is_noub(builtin_effects(memoryrefset!, Any[MemoryRef,Any,Symbol,Core.Const(true)])) + @test !Core.Compiler.is_noub(builtin_effects(memoryrefset!, Any[MemoryRef,Any,Symbol,Core.Const(false)])) + @test !Core.Compiler.is_noub(builtin_effects(memoryrefset!, Any[MemoryRef,Any,Symbol,Bool])) + @test Core.Compiler.is_noub(builtin_effects(memoryrefset!, Any[MemoryRef,Any,Symbol,Int])) + @test !Core.Compiler.is_noub(builtin_effects(memoryrefset!, Any[MemoryRef,Any,Symbol,Vararg{Bool}])) + @test !Core.Compiler.is_noub(builtin_effects(memoryrefset!, Any[MemoryRef,Vararg{Any}])) + # `:boundscheck` taint should be refined by post-opt analysis + @test Base.infer_effects() do xs::Vector{Any}, i::Int + memoryrefget(memoryref(getfield(xs, :ref), i, Base.@_boundscheck), :not_atomic, Base.@_boundscheck) + end |> Core.Compiler.is_noub_if_noinbounds +end + +# high level tests +@test Core.Compiler.is_noub_if_noinbounds(Base.infer_effects(getindex, (Vector{Int},Int))) +@test Core.Compiler.is_noub_if_noinbounds(Base.infer_effects(getindex, (Vector{Any},Int))) +@test Core.Compiler.is_noub_if_noinbounds(Base.infer_effects(setindex!, (Vector{Int},Int,Int))) +@test Core.Compiler.is_noub_if_noinbounds(Base.infer_effects(setindex!, (Vector{Any},Any,Int))) +@test Core.Compiler.is_noub_if_noinbounds(Base.infer_effects(isassigned, (Vector{Int},Int))) +@test Core.Compiler.is_noub_if_noinbounds(Base.infer_effects(isassigned, (Vector{Any},Int))) +@test Base.infer_effects((Vector{Int},Int)) do xs, i + xs[i] +end |> Core.Compiler.is_noub +@test Base.infer_effects((Vector{Any},Int)) do xs, i + xs[i] +end |> Core.Compiler.is_noub +@test Base.infer_effects((Vector{Int},Int,Int)) do xs, x, i + xs[i] = x +end |> Core.Compiler.is_noub +@test Base.infer_effects((Vector{Any},Any,Int)) do xs, x, i + xs[i] = x +end |> Core.Compiler.is_noub +@test Base.infer_effects((Vector{Int},Int)) do xs, i + @inbounds xs[i] +end |> !Core.Compiler.is_noub +@test Base.infer_effects((Vector{Any},Int)) do xs, i + @inbounds xs[i] +end |> !Core.Compiler.is_noub diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 174218f65b988a..ace17baeb5859b 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1580,15 +1580,21 @@ end f_typeof_tfunc(x) = typeof(x) @test Base.return_types(f_typeof_tfunc, (Union{<:T, Int} where T<:Complex,)) == Any[Union{Type{Int}, Type{Complex{T}} where T<:Real}] -# memoryref_tfunc, memoryrefget_tfunc, memoryrefset!_tfunc +# memoryref_tfunc, memoryrefget_tfunc, memoryrefset!_tfunc, memoryref_isassigned, memoryrefoffset_tfunc let memoryref_tfunc(@nospecialize xs...) = Core.Compiler.memoryref_tfunc(Core.Compiler.fallback_lattice, xs...) memoryrefget_tfunc(@nospecialize xs...) = Core.Compiler.memoryrefget_tfunc(Core.Compiler.fallback_lattice, xs...) + memoryref_isassigned_tfunc(@nospecialize xs...) = Core.Compiler.memoryref_isassigned_tfunc(Core.Compiler.fallback_lattice, xs...) memoryrefset!_tfunc(@nospecialize xs...) = Core.Compiler.memoryrefset!_tfunc(Core.Compiler.fallback_lattice, xs...) + memoryrefoffset_tfunc(@nospecialize xs...) = Core.Compiler.memoryrefoffset_tfunc(Core.Compiler.fallback_lattice, xs...) + interp = Core.Compiler.NativeInterpreter() + builtin_tfunction(@nospecialize xs...) = Core.Compiler.builtin_tfunction(interp, xs..., nothing) @test memoryref_tfunc(Memory{Int}) == MemoryRef{Int} @test memoryref_tfunc(Memory{Integer}) == MemoryRef{Integer} @test memoryref_tfunc(MemoryRef{Int}, Int) == MemoryRef{Int} + @test memoryref_tfunc(MemoryRef{Int}, Vararg{Int}) == MemoryRef{Int} @test memoryref_tfunc(MemoryRef{Int}, Int, Symbol) == Union{} @test memoryref_tfunc(MemoryRef{Int}, Int, Bool) == MemoryRef{Int} + @test memoryref_tfunc(MemoryRef{Int}, Int, Vararg{Bool}) == MemoryRef{Int} @test memoryref_tfunc(Memory{Int}, Int) == Union{} @test memoryref_tfunc(Any, Any, Any) == Any # also probably could be GenericMemoryRef @test memoryref_tfunc(Any, Any) == Any # also probably could be GenericMemoryRef @@ -1603,6 +1609,20 @@ let memoryref_tfunc(@nospecialize xs...) = Core.Compiler.memoryref_tfunc(Core.Co @test memoryrefget_tfunc(MemoryRef{Int}, String, Bool) === Union{} @test memoryrefget_tfunc(MemoryRef{Int}, Symbol, String) === Union{} @test memoryrefget_tfunc(Any, Any, Any) === Any + @test builtin_tfunction(Core.memoryrefget, Any[MemoryRef{Int}, Vararg{Any}]) == Int + @test builtin_tfunction(Core.memoryrefget, Any[MemoryRef{Int}, Symbol, Bool, Vararg{Bool}]) == Int + @test memoryref_isassigned_tfunc(MemoryRef{Any}, Symbol, Bool) === Bool + @test memoryref_isassigned_tfunc(MemoryRef{Any}, Any, Any) === Bool + @test memoryref_isassigned_tfunc(MemoryRef{<:Integer}, Symbol, Bool) === Bool + @test memoryref_isassigned_tfunc(GenericMemoryRef, Symbol, Bool) === Bool + @test memoryref_isassigned_tfunc(GenericMemoryRef{:not_atomic}, Symbol, Bool) === Bool + @test memoryref_isassigned_tfunc(Vector{Int}, Symbol, Bool) === Union{} + @test memoryref_isassigned_tfunc(String, Symbol, Bool) === Union{} + @test memoryref_isassigned_tfunc(MemoryRef{Int}, String, Bool) === Union{} + @test memoryref_isassigned_tfunc(MemoryRef{Int}, Symbol, String) === Union{} + @test memoryref_isassigned_tfunc(Any, Any, Any) === Bool + @test builtin_tfunction(Core.memoryref_isassigned, Any[MemoryRef{Int}, Vararg{Any}]) == Bool + @test builtin_tfunction(Core.memoryref_isassigned, Any[MemoryRef{Int}, Symbol, Bool, Vararg{Bool}]) == Bool @test memoryrefset!_tfunc(MemoryRef{Int}, Int, Symbol, Bool) === MemoryRef{Int} let ua = MemoryRef{<:Integer} @test memoryrefset!_tfunc(ua, Int, Symbol, Bool) === ua @@ -1617,6 +1637,15 @@ let memoryref_tfunc(@nospecialize xs...) = Core.Compiler.memoryref_tfunc(Core.Co @test memoryrefset!_tfunc(GenericMemoryRef{:not_atomic}, Any, Any, Any) === GenericMemoryRef{:not_atomic} @test memoryrefset!_tfunc(GenericMemoryRef, Any, Any, Any) === GenericMemoryRef @test memoryrefset!_tfunc(Any, Any, Any, Any) === Any # also probably could be GenericMemoryRef + @test builtin_tfunction(Core.memoryrefset!, Any[MemoryRef{Int}, Vararg{Any}]) == MemoryRef{Int} + @test builtin_tfunction(Core.memoryrefset!, Any[MemoryRef{Int}, Vararg{Symbol}]) == Union{} + @test builtin_tfunction(Core.memoryrefset!, Any[MemoryRef{Int}, Any, Symbol, Vararg{Bool}]) == MemoryRef{Int} + @test builtin_tfunction(Core.memoryrefset!, Any[MemoryRef{Int}, Any, Symbol, Bool, Vararg{Any}]) == MemoryRef{Int} + @test memoryrefoffset_tfunc(MemoryRef) == memoryrefoffset_tfunc(GenericMemoryRef) == Int + @test memoryrefoffset_tfunc(Memory) == memoryrefoffset_tfunc(GenericMemory) == Union{} + @test builtin_tfunction(Core.memoryrefoffset, Any[Vararg{MemoryRef}]) == Int + @test builtin_tfunction(Core.memoryrefoffset, Any[Vararg{Any}]) == Int + @test builtin_tfunction(Core.memoryrefoffset, Any[Vararg{Memory}]) == Union{} end let tuple_tfunc(@nospecialize xs...) = @@ -2999,13 +3028,16 @@ end @test ig27907(Int, Int, 1, 0) == 0 # issue #28279 +# ensure that lowering doesn't move these into statement position, which would require renumbering +using Base: +, - function f28279(b::Bool) - i = 1 - while i > b - i -= 1 + let i = 1 + while i > b + i -= 1 + end + if b end + return i + 1 end - if b end - return i + 1 end code28279 = code_lowered(f28279, (Bool,))[1].code oldcode28279 = deepcopy(code28279) @@ -5451,7 +5483,6 @@ end @test Base.return_types(phic_type8) |> only === Int @test phic_type8() === 2 - function phic_type9() local a try @@ -5490,6 +5521,11 @@ end @test Base.return_types(phic_type10) |> only === Int @test phic_type10() === 2 +undef_trycatch() = try (a_undef_trycatch = a_undef_trycatch, b = 2); return 1 catch end +# `global a_undef_trycatch` could be defined dynamically, so both paths must be allowed +@test Base.return_types(undef_trycatch) |> only === Union{Nothing, Int} +@test undef_trycatch() === nothing + # Test that `exit` returns `Union{}` (issue #51856) function test_exit_bottom(s) n = tryparse(Int, s) diff --git a/test/compiler/irpasses.jl b/test/compiler/irpasses.jl index 61fb0f6935f1bd..204d0400ea701e 100644 --- a/test/compiler/irpasses.jl +++ b/test/compiler/irpasses.jl @@ -1569,4 +1569,50 @@ let m = Meta.@lower 1 + 1 Core.Compiler.verify_ir(ir) end +function f_with_merge_to_entry_block() + while true + i = @noinline rand(Int) + if @noinline isodd(i) + return i + end + end +end + +let (ir, _) = only(Base.code_ircode(f_with_merge_to_entry_block)) + Core.Compiler.verify_ir(ir) + ir = Core.Compiler.cfg_simplify!(ir) + Core.Compiler.verify_ir(ir) +end + +# Test that CFG simplify doesn't leave an un-renamed SSA Value +let m = Meta.@lower 1 + 1 + # Test that CFG simplify doesn't try to merge every block in a loop into + # its predecessor + @assert Meta.isexpr(m, :thunk) + src = m.args[1]::CodeInfo + src.code = Any[ + # Block 1 + GotoIfNot(Argument(1), 3), + # Block 2 + GotoNode(5), + # Block 3 + Expr(:call, Base.inferencebarrier, 1), + GotoNode(6), + # Block 4 + Expr(:call, Base.inferencebarrier, 2), # fallthrough + # Block 5 + PhiNode(Int32[4, 5], Any[SSAValue(3), SSAValue(5)]), + ReturnNode(1) + ] + nstmts = length(src.code) + src.ssavaluetypes = nstmts + src.codelocs = fill(Int32(1), nstmts) + src.ssaflags = fill(Int32(0), nstmts) + ir = Core.Compiler.inflate_ir(src) + Core.Compiler.verify_ir(ir) + ir = Core.Compiler.cfg_simplify!(ir) + Core.Compiler.verify_ir(ir) + @test length(ir.cfg.blocks) == 4 +end + # JET.test_opt(Core.Compiler.cfg_simplify!, (Core.Compiler.IRCode,)) diff --git a/test/core.jl b/test/core.jl index 72b9f435d21a1e..00ab41e4ecd487 100644 --- a/test/core.jl +++ b/test/core.jl @@ -14,7 +14,7 @@ include("testenv.jl") # sanity tests that our built-in types are marked correctly for const fields for (T, c) in ( (Core.CodeInfo, []), - (Core.CodeInstance, [:def, :rettype, :rettype_const, :ipo_purity_bits, :argescapes]), + (Core.CodeInstance, [:def, :rettype, :rettype_const, :ipo_purity_bits, :analysis_results]), (Core.Method, [#=:name, :module, :file, :line, :primary_world, :sig, :slot_syms, :external_mt, :nargs, :called, :nospecialize, :nkw, :isva, :is_for_opaque_closure, :constprop=#]), (Core.MethodInstance, [#=:def, :specTypes, :sparam_vals=#]), (Core.MethodTable, [:module]), @@ -7357,21 +7357,27 @@ let fc = FieldConvert(1.0, [2.0], 0x3, 0x4, 0x5) end @test ftype_eval[] == 1 let code = code_lowered(FieldConvert)[1].code - @test code[1] == Expr(:call, GlobalRef(Core, :apply_type), GlobalRef(@__MODULE__, :FieldConvert), GlobalRef(@__MODULE__, :FieldTypeA), Expr(:static_parameter, 1)) - @test code[2] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(1), 1) - @test code[7] == Expr(:(=), Core.SlotNumber(10), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(2), Core.SlotNumber(10))) - @test code[8] == Core.SlotNumber(10) - @test code[9] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(1), 2) - @test code[14] == Expr(:(=), Core.SlotNumber(9), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(9), Core.SlotNumber(9))) - @test code[15] == Core.SlotNumber(9) - @test code[16] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(1), 4) - @test code[21] == Expr(:(=), Core.SlotNumber(8), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(16), Core.SlotNumber(8))) - @test code[22] == Core.SlotNumber(8) - @test code[23] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(1), 5) - @test code[28] == Expr(:(=), Core.SlotNumber(7), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(23), Core.SlotNumber(7))) - @test code[29] == Core.SlotNumber(7) - @test code[30] == Expr(:new, Core.SSAValue(1), Core.SSAValue(8), Core.SSAValue(15), Core.SlotNumber(4), Core.SSAValue(22), Core.SSAValue(29)) - @test code[31] == Core.ReturnNode(Core.SSAValue(30)) + local fc_global_ssa, sp1_ssa, apply_type_ssa, field_type_ssa, + field_type2_ssa, field_type4_ssa, field_type5_ssa, + slot_read_1, slot_read_2, slot_read_3, slot_read_4, + new_ssa + @test code[(fc_global_ssa = 1;)] == GlobalRef(@__MODULE__, :FieldConvert) + @test code[(sp1_ssa = 2;)] == Expr(:static_parameter, 1) + @test code[(apply_type_ssa = 3;)] == Expr(:call, GlobalRef(Core, :apply_type), Core.SSAValue(fc_global_ssa), GlobalRef(@__MODULE__, :FieldTypeA), Core.SSAValue(sp1_ssa)) + @test code[(field_type_ssa = 4;)] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(apply_type_ssa), 1) + @test code[10] == Expr(:(=), Core.SlotNumber(10), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(field_type_ssa), Core.SlotNumber(10))) + @test code[(slot_read_1 = 11;)] == Core.SlotNumber(10) + @test code[(field_type2_ssa = 12;)] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(apply_type_ssa), 2) + @test code[18] == Expr(:(=), Core.SlotNumber(9), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(field_type2_ssa), Core.SlotNumber(9))) + @test code[(slot_read_2 = 19;)] == Core.SlotNumber(9) + @test code[(field_type4_ssa = 20;)] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(apply_type_ssa), 4) + @test code[26] == Expr(:(=), Core.SlotNumber(8), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(field_type4_ssa), Core.SlotNumber(8))) + @test code[(slot_read_3 = 27;)] == Core.SlotNumber(8) + @test code[(field_type5_ssa = 28;)] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(apply_type_ssa), 5) + @test code[34] == Expr(:(=), Core.SlotNumber(7), Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(field_type5_ssa), Core.SlotNumber(7))) + @test code[(slot_read_4 = 35;)] == Core.SlotNumber(7) + @test code[(new_ssa = 36;)] == Expr(:new, Core.SSAValue(apply_type_ssa), Core.SSAValue(slot_read_1), Core.SSAValue(slot_read_2), Core.SlotNumber(4), Core.SSAValue(slot_read_3), Core.SSAValue(slot_read_4)) + @test code[37] == Core.ReturnNode(Core.SSAValue(new_ssa)) end # Issue #32820 @@ -7994,12 +8000,11 @@ for T in (Int, String, Symbol, Module) @test Core.Compiler.is_foldable(Base.infer_effects(hash, (Tuple{T},))) @test Core.Compiler.is_foldable(Base.infer_effects(objectid, (Tuple{T,T},))) @test Core.Compiler.is_foldable(Base.infer_effects(hash, (Tuple{T,T},))) + @test Core.Compiler.is_foldable(Base.infer_effects(objectid, (Ref{T},))) + @test Core.Compiler.is_foldable(Base.infer_effects(objectid, (Tuple{Ref{T}},))) + @test Core.Compiler.is_foldable(Base.infer_effects(objectid, (Tuple{Vector{T}},))) end -@test !Core.Compiler.is_consistent(Base.infer_effects(objectid, (Ref{Int},))) -@test !Core.Compiler.is_consistent(Base.infer_effects(objectid, (Tuple{Ref{Int}},))) -# objectid for datatypes is inconsistent for types that have unbound type parameters. -@test !Core.Compiler.is_consistent(Base.infer_effects(objectid, (DataType,))) -@test !Core.Compiler.is_consistent(Base.infer_effects(objectid, (Tuple{Vector{Int}},))) +@test Core.Compiler.is_foldable(Base.infer_effects(objectid, (DataType,))) # donotdelete should not taint consistency of the containing function f_donotdete(x) = (Core.Compiler.donotdelete(x); 1) @@ -8042,3 +8047,15 @@ end let lin = Core.LineInfoNode(Base, first(methods(convert)), :foo, Int32(5), Int32(0)) @test convert(LineNumberNode, lin) == LineNumberNode(5, :foo) end + +# Test that a nothrow-globalref doesn't get outlined during lowering +module WellKnownGlobal + global well_known = 1 +end +macro insert_global() + Expr(:call, GlobalRef(Base, :println), GlobalRef(WellKnownGlobal, :well_known)) +end +check_globalref_lowering() = @insert_global +let src = code_lowered(check_globalref_lowering)[1] + @test length(src.code) == 2 +end diff --git a/test/intfuncs.jl b/test/intfuncs.jl index ceaac235a3da9a..ed661b2806fb5b 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -221,7 +221,7 @@ end @test_throws MethodError gcdx(MyOtherRational(2//3), MyOtherRational(3//4)) end -@testset "invmod" begin +@testset "invmod(n, m)" begin @test invmod(6, 31) === 26 @test invmod(-1, 3) === 2 @test invmod(1, -3) === -2 @@ -256,6 +256,37 @@ end end end +@testset "invmod(n)" begin + for T in (Int8,UInt8,Int16,UInt16,Int32,UInt32,Int64,UInt64,Int128,UInt128) + if sizeof(T) ≤ 2 + # test full domain for small types + for a = typemin(T)+true:T(2):typemax(T) + b = invmod(a) + @test a * b == 1 + end + else + # test random sample for large types + for _ = 1:2^12 + a = rand(T) | true + b = invmod(a) + @test a * b == 1 + end + end + end +end + +@testset "invmod(n, T)" begin + for S in (Int8,UInt8,Int16,UInt16,Int32,UInt32,Int64,UInt64,Int128,UInt128), + T in (Int8,UInt8,Int16,UInt16,Int32,UInt32,Int64,UInt64,Int128,UInt128) + for _ = 1:2^8 + a = rand(S) | true + b = invmod(a, T) + @test (a * b) % T == 1 + @test (a % T) * b == 1 + end + end +end + @testset "powermod" begin @test powermod(2, 3, 5) == 3 @test powermod(2, 3, -5) == -2 diff --git a/test/llvmcall2.jl b/test/llvmcall2.jl index 07b27fc4074331..e3e89bb916f2d7 100644 --- a/test/llvmcall2.jl +++ b/test/llvmcall2.jl @@ -73,3 +73,12 @@ end jl_str = unsafe_string(str) @test length(jl_str) > 4 end + + +# boolean structs +const NT4I = NTuple{4, VecElement{Int}} +const NT4B = NTuple{4, VecElement{Bool}} +f_nt4b(x, y) = ccall("llvm.sadd.with.overflow", llvmcall, Pair{NT4B, NT4B}, (NT4B, NT4B), x, y) +f_nt4i(x, y) = ccall("llvm.sadd.with.overflow", llvmcall, Pair{NT4I, NT4B}, (NT4I, NT4I), x, y) +@test f_nt4b((false, true, false, true), (false, false, true, true)) === (NT4B((false, true, true, false)) => NT4B((false, false, false, true))) +@test f_nt4i((typemin(Int), 0, typemax(Int), typemax(Int)), (-1, typemax(Int),-1, 1)) === (NT4I((typemax(Int), typemax(Int), typemax(Int)-1, typemin(Int))) => NT4B((true, false, false, true))) diff --git a/test/llvmpasses/alloc-opt-pass.ll b/test/llvmpasses/alloc-opt-pass.ll index 521fd88d582eee..6bee0fd325105e 100644 --- a/test/llvmpasses/alloc-opt-pass.ll +++ b/test/llvmpasses/alloc-opt-pass.ll @@ -81,6 +81,7 @@ L3: ; CHECK-LABEL: @legal_int_types ; CHECK: alloca [12 x i8] ; CHECK-NOT: alloca i96 +; CHECK: store [12 x i8] zeroinitializer, ; CHECK: ret void define void @legal_int_types() { %pgcstack = call {}*** @julia.get_pgcstack() @@ -143,6 +144,7 @@ L2: ; CHECK: alloca ; CHECK-NOT: call token(...) @llvm.julia.gc_preserve_begin ; CHECK: call void @llvm.lifetime.start +; CHECK: store [8 x i8] zeroinitializer, ; CHECK-NOT: call void @llvm.lifetime.end define void @lifetime_no_preserve_end({}* noalias nocapture noundef nonnull sret({}) %0) { %pgcstack = call {}*** @julia.get_pgcstack() @@ -160,3 +162,39 @@ define void @lifetime_no_preserve_end({}* noalias nocapture noundef nonnull sret ret void } ; CHECK-LABEL: }{{$}} + + +; CHECK-LABEL: @initializers +; CHECK: alloca [1 x i8] +; CHECK-DAG: alloca [2 x i8] +; CHECK-DAG: alloca [3 x i8] +; CHECK-DAG: freeze [1 x i8] undef +; CHECK-DAG: store [1 x i8] % +; CHECK-DAG: store [3 x i8] zeroinitializer, +; CHECK-NOT: store +; CHECK-NOT: zeroinitializer +; CHECK: ret void +define void @initializers() { + %pgcstack = call {}*** @julia.get_pgcstack() + %ptls = call {}*** @julia.ptls_states() + %ptls_i8 = bitcast {}*** %ptls to i8* + + %var1 = call {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 1, {} addrspace(10)* @tag) #0 + %var2 = addrspacecast {} addrspace(10)* %var1 to {} addrspace(11)* + %var3 = call {}* @julia.pointer_from_objref({} addrspace(11)* %var2) + + %var4 = call {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 2, {} addrspace(10)* @tag) #1 + %var5 = addrspacecast {} addrspace(10)* %var4 to {} addrspace(11)* + %var6 = call {}* @julia.pointer_from_objref({} addrspace(11)* %var5) + + %var7 = call {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 3, {} addrspace(10)* @tag) #2 + %var8 = addrspacecast {} addrspace(10)* %var7 to {} addrspace(11)* + %var9 = call {}* @julia.pointer_from_objref({} addrspace(11)* %var8) + + ret void +} +; CHECK-LABEL: }{{$}} + +attributes #0 = { allockind("alloc") } +attributes #1 = { allockind("alloc,uninitialized") } +attributes #2 = { allockind("alloc,zeroed") } diff --git a/test/loading.jl b/test/loading.jl index 9f81a7637cb622..d39bdceb53341e 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1092,7 +1092,7 @@ end end end -pkgimage(val) = val == 1 ? `--pkgimage=yes` : `--pkgimage=no` +pkgimage(val) = val == 1 ? `--pkgimages=yes` : `--pkgimages=no` opt_level(val) = `-O$val` debug_level(val) = `-g$val` inline(val) = val == 1 ? `--inline=yes` : `--inline=no` @@ -1277,6 +1277,9 @@ end @testset "relocatable upgrades #51989" begin mktempdir() do depot + project_path = joinpath(depot, "project") + mkpath(project_path) + # Create fake `Foo.jl` package with two files: foo_path = joinpath(depot, "dev", "Foo") mkpath(joinpath(foo_path, "src")) @@ -1300,9 +1303,8 @@ end # In our depot, `dev` and then `precompile` this `Foo` package. @test success(addenv( - `$(Base.julia_cmd()) --startup-file=no -e 'import Pkg; Pkg.develop("Foo"); Pkg.precompile(); exit(0)'`, - "JULIA_DEPOT_PATH" => depot, - )) + `$(Base.julia_cmd()) --project=$project_path --startup-file=no -e 'import Pkg; Pkg.develop("Foo"); Pkg.precompile(); exit(0)'`, + "JULIA_DEPOT_PATH" => depot)) # Get the size of the generated `.ji` file so that we can ensure that it gets altered foo_compiled_path = joinpath(depot, "compiled", "v$(VERSION.major).$(VERSION.minor)", "Foo") @@ -1321,7 +1323,7 @@ end # Try to load `Foo`; this should trigger recompilation, not an error! @test success(addenv( - `$(Base.julia_cmd()) --startup-file=no -e 'using Foo; exit(0)'`, + `$(Base.julia_cmd()) --project=$project_path --startup-file=no -e 'using Foo; exit(0)'`, "JULIA_DEPOT_PATH" => depot, )) @@ -1331,3 +1333,47 @@ end @test filesize(cache_path) != cache_size end end + +@testset "code coverage disabled during precompilation" begin + mktempdir() do depot + cov_test_dir = joinpath(@__DIR__, "project", "deps", "CovTest.jl") + cov_cache_dir = joinpath(depot, "compiled", "v$(VERSION.major).$(VERSION.minor)", "CovTest") + function rm_cov_files() + for cov_file in filter(endswith(".cov"), readdir(joinpath(cov_test_dir, "src"), join=true)) + rm(cov_file) + end + @test !cov_exists() + end + cov_exists() = !isempty(filter(endswith(".cov"), readdir(joinpath(cov_test_dir, "src")))) + + rm_cov_files() # clear out any coverage files first + @test !cov_exists() + + cd(cov_test_dir) do + # In our depot, precompile CovTest.jl with coverage on + @test success(addenv( + `$(Base.julia_cmd()) --startup-file=no --pkgimage=yes --code-coverage=@ --project -e 'using CovTest; exit(0)'`, + "JULIA_DEPOT_PATH" => depot, + )) + @test !isempty(filter(!endswith(".ji"), readdir(cov_cache_dir))) # check that object cache file(s) exists + @test !cov_exists() + rm_cov_files() + + # same again but call foo(), which is in the pkgimage, and should generate coverage + @test success(addenv( + `$(Base.julia_cmd()) --startup-file=no --pkgimage=yes --code-coverage=@ --project -e 'using CovTest; foo(); exit(0)'`, + "JULIA_DEPOT_PATH" => depot, + )) + @test cov_exists() + rm_cov_files() + + # same again but call bar(), which is NOT in the pkgimage, and should generate coverage + @test success(addenv( + `$(Base.julia_cmd()) --startup-file=no --pkgimage=yes --code-coverage=@ --project -e 'using CovTest; bar(); exit(0)'`, + "JULIA_DEPOT_PATH" => depot, + )) + @test cov_exists() + rm_cov_files() + end + end +end diff --git a/test/meta.jl b/test/meta.jl index 36a8acbfe08dd4..856462fedd9fa1 100644 --- a/test/meta.jl +++ b/test/meta.jl @@ -254,14 +254,14 @@ end f(::T) where {T} = T ci = code_lowered(f, Tuple{Int})[1] @test Meta.partially_inline!(ci.code, [], Tuple{typeof(f),Int}, Any[Int], 0, 0, :propagate) == - Any[Core.ReturnNode(QuoteNode(Int))] + Any[QuoteNode(Int), Core.ReturnNode(Core.SSAValue(1))] g(::Val{x}) where {x} = x ? 1 : 0 ci = code_lowered(g, Tuple{Val{true}})[1] -@test Meta.partially_inline!(ci.code, [], Tuple{typeof(g),Val{true}}, Any[true], 0, 0, :propagate)[1] == - Core.GotoIfNot(QuoteNode(true), 3) -@test Meta.partially_inline!(ci.code, [], Tuple{typeof(g),Val{true}}, Any[true], 0, 2, :propagate)[1] == - Core.GotoIfNot(QuoteNode(true), 5) +@test Meta.partially_inline!(ci.code, [], Tuple{typeof(g),Val{true}}, Any[true], 0, 0, :propagate)[2] == + Core.GotoIfNot(Core.SSAValue(1), 4) +@test Meta.partially_inline!(ci.code, [], Tuple{typeof(g),Val{true}}, Any[true], 0, 2, :propagate)[2] == + Core.GotoIfNot(Core.SSAValue(3), 6) @testset "inlining with isdefined" begin isdefined_slot(x) = @isdefined(x) diff --git a/test/precompile.jl b/test/precompile.jl index 8fd8e0a110cbb3..bb87e1f6b1dc79 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -288,6 +288,9 @@ precompile_test_harness(false) do dir a_vec_inline = Pair{Int,Any}[] push!(a_vec_inline, 1=>2, 3=>4) a_mat_inline = reshape(a_vec_inline, (1, 2)) + + oid_vec_int = objectid(a_vec_int) + oid_mat_int = objectid(a_mat_int) end """) # Issue #12623 @@ -371,6 +374,10 @@ precompile_test_harness(false) do dir @test Foo.a_mat_inline == Pair{Int,Any}[1=>2 3=>4] Foo.a_mat_inline[1, 2] = 5=>6 @test Foo.a_vec_inline[2] === Pair{Int,Any}(5, 6) + + @test objectid(Foo.a_vec_int) === Foo.oid_vec_int + @test objectid(Foo.a_mat_int) === Foo.oid_mat_int + @test Foo.oid_vec_int !== Foo.oid_mat_int end @eval begin function ccallable_test() @@ -1771,7 +1778,7 @@ precompile_test_harness("PkgCacheInspector") do load_path end if ocachefile !== nothing - sv = ccall(:jl_restore_package_image_from_file, Any, (Cstring, Any, Cint, Cstring), ocachefile, depmods, true, "PCI") + sv = ccall(:jl_restore_package_image_from_file, Any, (Cstring, Any, Cint, Cstring, Cint), ocachefile, depmods, true, "PCI", false) else sv = ccall(:jl_restore_incremental, Any, (Cstring, Any, Cint, Cstring), cachefile, depmods, true, "PCI") end diff --git a/test/project/deps/CovTest.jl/Project.toml b/test/project/deps/CovTest.jl/Project.toml new file mode 100644 index 00000000000000..97fb2c7d9cfce9 --- /dev/null +++ b/test/project/deps/CovTest.jl/Project.toml @@ -0,0 +1,3 @@ +name = "CovTest" +uuid = "f1f4390d-b815-473a-b5dd-5af6e1d717cb" +version = "0.1.0" diff --git a/test/project/deps/CovTest.jl/src/CovTest.jl b/test/project/deps/CovTest.jl/src/CovTest.jl new file mode 100644 index 00000000000000..bd172fc3a00f48 --- /dev/null +++ b/test/project/deps/CovTest.jl/src/CovTest.jl @@ -0,0 +1,26 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module CovTest + +function foo() + x = 1 + y = 2 + z = x * y + return z +end + +function bar() + x = 1 + y = 2 + z = x * y + return z +end + +if Base.generating_output() + # precompile foo but not bar + foo() +end + +export foo, bar + +end #module diff --git a/test/sorting.jl b/test/sorting.jl index 9cb5d717635c9d..bb0fd3cc6392e8 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -88,20 +88,6 @@ end vcat(2000, (x:x+99 for x in 1900:-100:100)..., 1:99) end -function tuple_sort_test(x) - @test issorted(sort(x)) - length(x) > 9 && return # length > 9 uses a vector fallback - @test 0 == @allocated sort(x) -end -@testset "sort(::NTuple)" begin - @test sort((9,8,3,3,6,2,0,8)) == (0,2,3,3,6,8,8,9) - @test sort((9,8,3,3,6,2,0,8), by=x->x÷3) == (2,0,3,3,8,6,8,9) - for i in 1:40 - tuple_sort_test(tuple(rand(i)...)) - end - @test_throws ArgumentError sort((1,2,3.0)) -end - @testset "partialsort" begin @test partialsort([3,6,30,1,9],3) == 6 @test partialsort([3,6,30,1,9],3:4) == [6,9] @@ -544,28 +530,6 @@ end @test isequal(a, [8,6,7,NaN,5,3,0,9]) end -@testset "sort!(iterable)" begin - gen = (x % 7 + 0.1x for x in 1:50) - @test sort(gen) == sort!(collect(gen)) - gen = (x % 7 + 0.1y for x in 1:10, y in 1:5) - @test sort(gen; dims=1) == sort!(collect(gen); dims=1) - @test sort(gen; dims=2) == sort!(collect(gen); dims=2) - - @test_throws ArgumentError("dimension out of range") sort(gen; dims=3) - - @test_throws UndefKeywordError(:dims) sort(gen) - @test_throws UndefKeywordError(:dims) sort(collect(gen)) - @test_throws UndefKeywordError(:dims) sort!(collect(gen)) - - @test_throws ArgumentError sort("string") - @test_throws ArgumentError("1 cannot be sorted") sort(1) - - @test sort(Set((1, 3, 6))) == [1, 3, 6] - @test sort(Dict((1=>9, 3=>2, 6=>5))) == [1=>9, 3=>2, 6=>5] - @test sort(keys(Dict((1=>2, 3=>5, 6=>9)))) == [1, 3, 6] - @test sort(values(Dict((1=>9, 3=>2, 6=>5)))) == [2, 5, 9] -end - @testset "sort!(::AbstractVector{<:Integer}) with short int range" begin a = view([9:-1:0;], :)::SubArray sort!(a) diff --git a/test/spawn.jl b/test/spawn.jl index 3fdfa794ff39e2..dab18573993752 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -660,9 +660,21 @@ let p = run(`$sleepcmd 100`, wait=false) kill(p) end -# Second argument of shell_parse +# Second return of shell_parse let s = " \$abc " - @test s[Base.shell_parse(s)[2]] == "abc" + @test Base.shell_parse(s)[2] === findfirst('a', s) + s = "abc def" + @test Base.shell_parse(s)[2] === findfirst('d', s) + s = "abc 'de'f\"\"g" + @test Base.shell_parse(s)[2] === findfirst('\'', s) + s = "abc \$x'de'f\"\"g" + @test Base.shell_parse(s)[2] === findfirst('\'', s) + s = "abc def\$x'g'" + @test Base.shell_parse(s)[2] === findfirst('\'', s) + s = "abc def\$x " + @test Base.shell_parse(s)[2] === findfirst('x', s) + s = "abc \$(d)ef\$(x " + @test Base.shell_parse(s)[2] === findfirst('x', s) - 1 end # Logging macros should not output to finalized streams (#26687) @@ -1003,5 +1015,19 @@ end args = ["ab ^` c", " \" ", "\"", ascii95, ascii95, "\"\\\"\\", "", "|", "&&", ";"]; @test Base.shell_escape_wincmd(Base.escape_microsoft_c_args(args...)) == "\"ab ^` c\" \" \\\" \" \"\\\"\" \" !\\\"#\$%^&'^(^)*+,-./0123456789:;^<=^>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^^_`abcdefghijklmnopqrstuvwxyz{^|}~\" \" ^!\\\"#\$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\" \"\\\"\\\\\\\"\\\\\" \"\" ^| ^&^& ;" +end +# effects for Cmd construction +for f in (() -> `a b c`, () -> `a a$("bb")a $("c")`) + effects = Base.infer_effects(f) + @test Core.Compiler.is_effect_free(effects) + @test Core.Compiler.is_terminates(effects) + @test Core.Compiler.is_noub(effects) + @test !Core.Compiler.is_consistent(effects) +end +let effects = Base.infer_effects(x -> `a $x`, (Any,)) + @test !Core.Compiler.is_effect_free(effects) + @test !Core.Compiler.is_terminates(effects) + @test !Core.Compiler.is_noub(effects) + @test !Core.Compiler.is_consistent(effects) end diff --git a/test/subarray.jl b/test/subarray.jl index f7350d50f793db..35e376f1a40b43 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -799,6 +799,9 @@ end end V = view(OneElVec(6, 2), 1:5) @test sprint(show, "text/plain", V) == "$(summary(V)):\n ⋅\n 1\n ⋅\n ⋅\n ⋅" + + V = view(1:2, [CartesianIndex(2)]) + @test sprint(show, "text/plain", V) == "$(summary(V)):\n 2" end @testset "Base.first_index for offset indices" begin @@ -806,3 +809,11 @@ end b = view(a, Base.IdentityUnitRange(4:7)) @test first(b) == a[Base.first_index(b)] end + +@testset "StepRangeLen of CartesianIndex-es" begin + v = view(1:2, StepRangeLen(CartesianIndex(1,1), CartesianIndex(1,1), 0)) + @test isempty(v) + r = StepRangeLen(CartesianIndex(1), CartesianIndex(1), 1) + v = view(1:2, r) + @test v == view(1:2, collect(r)) +end diff --git a/test/syntax.jl b/test/syntax.jl index 106a8f296ced29..4d204f3e29364c 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -553,7 +553,7 @@ for (str, tag) in Dict("" => :none, "\"" => :string, "#=" => :comment, "'" => :c end # meta nodes for optional positional arguments -let src = Meta.lower(Main, :(@inline f(p::Int=2) = 3)).args[1].code[end-1].args[3] +let src = Meta.lower(Main, :(@inline f(p::Int=2) = 3)).args[1].code[end-2].args[3] @test Core.Compiler.is_declared_inline(src) end @@ -713,7 +713,7 @@ m1_exprs = get_expr_list(Meta.lower(@__MODULE__, quote @m1 end)) let low3 = Meta.lower(@__MODULE__, quote @m3 end) m3_exprs = get_expr_list(low3) ci = low3.args[1]::Core.CodeInfo - @test ci.codelocs == [4, 2] + @test ci.codelocs in ([4, 4, 2], [4, 2]) @test is_return_ssavalue(m3_exprs[end]) end @@ -2511,7 +2511,14 @@ end function ncalls_in_lowered(ex, fname) lowered_exprs = Meta.lower(Main, ex).args[1].code return count(lowered_exprs) do ex - Meta.isexpr(ex, :call) && ex.args[1] == fname + if Meta.isexpr(ex, :call) + arg = ex.args[1] + if isa(arg, Core.SSAValue) + arg = lowered_exprs[arg.id] + end + return arg == fname + end + return false end end diff --git a/test/testhelpers/Furlongs.jl b/test/testhelpers/Furlongs.jl index f63b5460c7c164..6d52260bb20fd7 100644 --- a/test/testhelpers/Furlongs.jl +++ b/test/testhelpers/Furlongs.jl @@ -99,5 +99,6 @@ for op in (:rem, :mod) end end Base.sqrt(x::Furlong) = _div(sqrt(x.val), x, Val(2)) +Base.muladd(x::Furlong, y::Furlong, z::Furlong) = x*y + z end