diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fd7f1c89420d6..9a3fe2cd441b3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -278,8 +278,8 @@ Be sure to change the UUID value back before making the pull request. The process of [creating a patch release](https://docs.julialang.org/en/v1/devdocs/build/distributing/#Point-releasing-101) is roughly as follows: -1. Create a new branch (e.g. `backports-release-1.6`) against the relevant minor release - branch (e.g. `release-1.6`). Usually a corresponding pull request is created as well. +1. Create a new branch (e.g. `backports-release-1.10`) against the relevant minor release + branch (e.g. `release-1.10`). Usually a corresponding pull request is created as well. 2. Add commits, nominally from `master` (hence "backports"), to that branch. See below for more information on this process. @@ -291,8 +291,8 @@ The process of [creating a patch release](https://docs.julialang.org/en/v1/devdo the pull request associated with the backports branch. Fix any issues. 4. Once all test and benchmark reports look good, merge the backports branch into - the corresponding release branch (e.g. merge `backports-release-1.6` into - `release-1.6`). + the corresponding release branch (e.g. merge `backports-release-1.10` into + `release-1.10`). 5. Open a pull request that bumps the version of the relevant minor release to the next patch version, e.g. as in [this pull request](https://github.com/JuliaLang/julia/pull/37718). diff --git a/Compiler/extras/CompilerDevTools/Manifest.toml b/Compiler/extras/CompilerDevTools/Manifest.toml new file mode 100644 index 0000000000000..bcc78f1ded34a --- /dev/null +++ b/Compiler/extras/CompilerDevTools/Manifest.toml @@ -0,0 +1,15 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.12.0-DEV" +manifest_format = "2.0" +project_hash = "84f495a1bf065c95f732a48af36dd0cd2cefb9d5" + +[[deps.Compiler]] +path = "../.." +uuid = "807dbc54-b67e-4c79-8afb-eafe4df6f2e1" +version = "0.0.2" + +[[deps.CompilerDevTools]] +path = "." +uuid = "92b2d91f-d2bd-4c05-9214-4609ac33433f" +version = "0.0.0" diff --git a/Compiler/extras/CompilerDevTools/Project.toml b/Compiler/extras/CompilerDevTools/Project.toml new file mode 100644 index 0000000000000..a2749a9a56a84 --- /dev/null +++ b/Compiler/extras/CompilerDevTools/Project.toml @@ -0,0 +1,5 @@ +name = "CompilerDevTools" +uuid = "92b2d91f-d2bd-4c05-9214-4609ac33433f" + +[deps] +Compiler = "807dbc54-b67e-4c79-8afb-eafe4df6f2e1" diff --git a/Compiler/extras/CompilerDevTools/src/CompilerDevTools.jl b/Compiler/extras/CompilerDevTools/src/CompilerDevTools.jl new file mode 100644 index 0000000000000..5d0df5ccaa4e4 --- /dev/null +++ b/Compiler/extras/CompilerDevTools/src/CompilerDevTools.jl @@ -0,0 +1,48 @@ +module CompilerDevTools + +using Compiler +using Core.IR + +struct SplitCacheOwner; end +struct SplitCacheInterp <: Compiler.AbstractInterpreter + world::UInt + inf_params::Compiler.InferenceParams + opt_params::Compiler.OptimizationParams + inf_cache::Vector{Compiler.InferenceResult} + function SplitCacheInterp(; + world::UInt = Base.get_world_counter(), + inf_params::Compiler.InferenceParams = Compiler.InferenceParams(), + opt_params::Compiler.OptimizationParams = Compiler.OptimizationParams(), + inf_cache::Vector{Compiler.InferenceResult} = Compiler.InferenceResult[]) + new(world, inf_params, opt_params, inf_cache) + end +end + +Compiler.InferenceParams(interp::SplitCacheInterp) = interp.inf_params +Compiler.OptimizationParams(interp::SplitCacheInterp) = interp.opt_params +Compiler.get_inference_world(interp::SplitCacheInterp) = interp.world +Compiler.get_inference_cache(interp::SplitCacheInterp) = interp.inf_cache +Compiler.cache_owner(::SplitCacheInterp) = SplitCacheOwner() + +import Core.OptimizedGenerics.CompilerPlugins: typeinf, typeinf_edge +@eval @noinline typeinf(::SplitCacheOwner, mi::MethodInstance, source_mode::UInt8) = + Base.invoke_in_world(which(typeinf, Tuple{SplitCacheOwner, MethodInstance, UInt8}).primary_world, Compiler.typeinf_ext, SplitCacheInterp(; world=Base.tls_world_age()), mi, source_mode) + +@eval @noinline function typeinf_edge(::SplitCacheOwner, mi::MethodInstance, parent_frame::Compiler.InferenceState, world::UInt, source_mode::UInt8) + # TODO: This isn't quite right, we're just sketching things for now + interp = SplitCacheInterp(; world) + Compiler.typeinf_edge(interp, mi.def, mi.specTypes, Core.svec(), parent_frame, false, false) +end + +function with_new_compiler(f, args...) + mi = @ccall jl_method_lookup(Any[f, args...]::Ptr{Any}, (1+length(args))::Csize_t, Base.tls_world_age()::Csize_t)::Ref{Core.MethodInstance} + world = Base.tls_world_age() + new_compiler_ci = Core.OptimizedGenerics.CompilerPlugins.typeinf( + SplitCacheOwner(), mi, Compiler.SOURCE_MODE_ABI + ) + invoke(f, new_compiler_ci, args...) +end + +export with_new_compiler + +end diff --git a/Compiler/src/Compiler.jl b/Compiler/src/Compiler.jl index 2cf7e5508196c..06cdbe09e06fc 100644 --- a/Compiler/src/Compiler.jl +++ b/Compiler/src/Compiler.jl @@ -41,36 +41,36 @@ ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), Compiler, using Core.Intrinsics, Core.IR -import Core: print, println, show, write, unsafe_write, - _apply_iterate, svec, apply_type, Builtin, IntrinsicFunction, - MethodInstance, CodeInstance, MethodTable, MethodMatch, PartialOpaque, - TypeofVararg, Core, SimpleVector, donotdelete, compilerbarrier, - memoryref_isassigned, memoryrefnew, memoryrefoffset, memoryrefget, - memoryrefset!, typename +using Core: Builtin, CodeInstance, IntrinsicFunction, MethodInstance, MethodMatch, + MethodTable, PartialOpaque, SimpleVector, TypeofVararg, + _apply_iterate, apply_type, compilerbarrier, donotdelete, memoryref_isassigned, + memoryrefget, memoryrefnew, memoryrefoffset, memoryrefset!, print, println, show, svec, + typename, unsafe_write, write using Base -using Base: Ordering, vect, EffectsOverride, BitVector, @_gc_preserve_begin, @_gc_preserve_end, RefValue, - @nospecializeinfer, @_foldable_meta, fieldindex, is_function_def, indexed_iterate, isexpr, methods, - get_world_counter, JLOptions, _methods_by_ftype, unwrap_unionall, cconvert, unsafe_convert, - issingletontype, isType, rewrap_unionall, has_free_typevars, isvarargtype, hasgenerator, - IteratorSize, SizeUnknown, _array_for, Bottom, generating_output, diff_names, - ismutationfree, NUM_EFFECTS_OVERRIDES, _NAMEDTUPLE_NAME, datatype_fieldtypes, - argument_datatype, isfieldatomic, unwrapva, iskindtype, _bits_findnext, copy_exprargs, - Generator, Filter, ismutabletypename, isvatuple, datatype_fieldcount, - isconcretedispatch, isdispatchelem, datatype_layoutsize, - datatype_arrayelem, unionlen, isidentityfree, _uniontypes, uniontypes, OneTo, Callable, - DataTypeFieldDesc, datatype_nfields, datatype_pointerfree, midpoint, is_valid_intrinsic_elptr, - allocatedinline, isbitsunion, widen_diagonal, unconstrain_vararg_length, - rename_unionall, may_invoke_generator, is_meta_expr_head, is_meta_expr, quoted, - specialize_method, hasintersect, is_nospecializeinfer, is_nospecialized, - get_nospecializeinfer_sig, tls_world_age, uniontype_layout, kwerr, - moduleroot, is_file_tracked, decode_effects_override, lookup_binding_partition, - is_some_imported, binding_kind, is_some_guard, is_some_const_binding, partition_restriction, - BINDING_KIND_GLOBAL, structdiff +using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospecializeinfer, + BINDING_KIND_GLOBAL, Base, BitVector, Bottom, Callable, DataTypeFieldDesc, + EffectsOverride, Filter, Generator, IteratorSize, JLOptions, NUM_EFFECTS_OVERRIDES, + OneTo, Ordering, RefValue, SizeUnknown, _NAMEDTUPLE_NAME, + _array_for, _bits_findnext, _methods_by_ftype, _uniontypes, all, allocatedinline, any, + argument_datatype, binding_kind, cconvert, copy_exprargs, datatype_arrayelem, + datatype_fieldcount, datatype_fieldtypes, datatype_layoutsize, datatype_nfields, + datatype_pointerfree, decode_effects_override, diff_names, fieldindex, + generating_output, get_nospecializeinfer_sig, get_world_counter, has_free_typevars, + hasgenerator, hasintersect, indexed_iterate, isType, is_file_tracked, is_function_def, + is_meta_expr, is_meta_expr_head, is_nospecialized, is_nospecializeinfer, + is_some_const_binding, is_some_guard, is_some_imported, is_valid_intrinsic_elptr, + isbitsunion, isconcretedispatch, isdispatchelem, isexpr, isfieldatomic, isidentityfree, + iskindtype, ismutabletypename, ismutationfree, issingletontype, isvarargtype, isvatuple, + kwerr, lookup_binding_partition, may_invoke_generator, methods, midpoint, moduleroot, + partition_restriction, quoted, rename_unionall, rewrap_unionall, specialize_method, + structdiff, tls_world_age, unconstrain_vararg_length, unionlen, uniontype_layout, + uniontypes, unsafe_convert, unwrap_unionall, unwrapva, vect, widen_diagonal using Base.Order -import Base: getindex, setindex!, length, iterate, push!, isempty, first, convert, ==, - copy, popfirst!, in, haskey, resize!, copy!, append!, last, get!, size, - get, iterate, findall, min_world, max_world, _topmod, isready + +import Base: ==, _topmod, append!, convert, copy, copy!, findall, first, get, get!, + getindex, haskey, in, isempty, isready, iterate, iterate, last, length, max_world, + min_world, popfirst!, push!, resize!, setindex!, size const getproperty = Core.getfield const setproperty! = Core.setfield! @@ -181,12 +181,26 @@ include("bootstrap.jl") include("reflection_interface.jl") include("opaque_closure.jl") +macro __SOURCE_FILE__() + __source__.file === nothing && return nothing + return QuoteNode(__source__.file::Symbol) +end + module IRShow end +function load_irshow!() + if isdefined(Base, :end_base_include) + # This code path is exclusively for Revise, which may want to re-run this + # after bootstrap. + include(IRShow, Base.joinpath(Base.dirname(Base.String(@__SOURCE_FILE__)), "ssair/show.jl")) + else + include(IRShow, "ssair/show.jl") + end +end if !isdefined(Base, :end_base_include) # During bootstrap, skip including this file and defer it to base/show.jl to include later else # When this module is loaded as the standard library, include this file as usual - include(IRShow, "ssair/show.jl") + load_irshow!() end end # baremodule Compiler diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index a3abbf814165a..59d382d8e4a34 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -856,8 +856,7 @@ end struct InvokeCall types # ::Type - lookupsig # ::Type - InvokeCall(@nospecialize(types), @nospecialize(lookupsig)) = new(types, lookupsig) + InvokeCall(@nospecialize(types)) = new(types) end struct ConstCallResult @@ -2218,26 +2217,69 @@ function abstract_invoke(interp::AbstractInterpreter, arginfo::ArgInfo, si::Stmt ft′ = argtype_by_index(argtypes, 2) ft = widenconst(ft′) ft === Bottom && return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) - (types, isexact, isconcrete, istype) = instanceof_tfunc(argtype_by_index(argtypes, 3), false) - isexact || return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) - unwrapped = unwrap_unionall(types) - types === Bottom && return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) - if !(unwrapped isa DataType && unwrapped.name === Tuple.name) - return Future(CallMeta(Bottom, TypeError, EFFECTS_THROWS, NoCallInfo())) - end - argtype = argtypes_to_type(argtype_tail(argtypes, 4)) - nargtype = typeintersect(types, argtype) - nargtype === Bottom && return Future(CallMeta(Bottom, TypeError, EFFECTS_THROWS, NoCallInfo())) - nargtype isa DataType || return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) # other cases are not implemented below - isdispatchelem(ft) || return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) # check that we might not have a subtype of `ft` at runtime, before doing supertype lookup below - ft = ft::DataType - lookupsig = rewrap_unionall(Tuple{ft, unwrapped.parameters...}, types)::Type - nargtype = Tuple{ft, nargtype.parameters...} - argtype = Tuple{ft, argtype.parameters...} - matched, valid_worlds = findsup(lookupsig, method_table(interp)) - matched === nothing && return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) - update_valid_age!(sv, valid_worlds) - method = matched.method + types = argtype_by_index(argtypes, 3) + if types isa Const && types.val isa Union{Method, CodeInstance} + method_or_ci = types.val + if isa(method_or_ci, CodeInstance) + our_world = sv.world.this + argtype = argtypes_to_type(pushfirst!(argtype_tail(argtypes, 4), ft)) + specsig = method_or_ci.def.specTypes + defdef = method_or_ci.def.def + exct = method_or_ci.exctype + if !hasintersect(argtype, specsig) + return Future(CallMeta(Bottom, TypeError, EFFECTS_THROWS, NoCallInfo())) + elseif !(argtype <: specsig) || (isa(defdef, Method) && !(argtype <: defdef.sig)) + exct = Union{exct, TypeError} + end + callee_valid_range = WorldRange(method_or_ci.min_world, method_or_ci.max_world) + if !(our_world in callee_valid_range) + if our_world < first(callee_valid_range) + update_valid_age!(sv, WorldRange(first(sv.world.valid_worlds), first(callee_valid_range)-1)) + else + update_valid_age!(sv, WorldRange(last(callee_valid_range)+1, last(sv.world.valid_worlds))) + end + return Future(CallMeta(Bottom, ErrorException, EFFECTS_THROWS, NoCallInfo())) + end + # TODO: When we add curing, we may want to assume this is nothrow + if (method_or_ci.owner === Nothing && method_ir_ci.def.def isa Method) + exct = Union{exct, ErrorException} + end + update_valid_age!(sv, callee_valid_range) + return Future(CallMeta(method_or_ci.rettype, exct, Effects(decode_effects(method_or_ci.ipo_purity_bits), nothrow=(exct===Bottom)), + InvokeCICallInfo(method_or_ci))) + else + method = method_or_ci::Method + types = method # argument value + lookupsig = method.sig # edge kind + argtype = argtypes_to_type(pushfirst!(argtype_tail(argtypes, 4), ft)) + nargtype = typeintersect(lookupsig, argtype) + nargtype === Bottom && return Future(CallMeta(Bottom, TypeError, EFFECTS_THROWS, NoCallInfo())) + nargtype isa DataType || return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) # other cases are not implemented below + # Fall through to generic invoke handling + end + else + hasintersect(widenconst(types), Union{Method, CodeInstance}) && return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) + (types, isexact, isconcrete, istype) = instanceof_tfunc(argtype_by_index(argtypes, 3), false) + isexact || return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) + unwrapped = unwrap_unionall(types) + types === Bottom && return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) + if !(unwrapped isa DataType && unwrapped.name === Tuple.name) + return Future(CallMeta(Bottom, TypeError, EFFECTS_THROWS, NoCallInfo())) + end + argtype = argtypes_to_type(argtype_tail(argtypes, 4)) + nargtype = typeintersect(types, argtype) + nargtype === Bottom && return Future(CallMeta(Bottom, TypeError, EFFECTS_THROWS, NoCallInfo())) + nargtype isa DataType || return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) # other cases are not implemented below + isdispatchelem(ft) || return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) # check that we might not have a subtype of `ft` at runtime, before doing supertype lookup below + ft = ft::DataType + lookupsig = rewrap_unionall(Tuple{ft, unwrapped.parameters...}, types)::Type + nargtype = Tuple{ft, nargtype.parameters...} + argtype = Tuple{ft, argtype.parameters...} + matched, valid_worlds = findsup(lookupsig, method_table(interp)) + matched === nothing && return Future(CallMeta(Any, Any, Effects(), NoCallInfo())) + update_valid_age!(sv, valid_worlds) + method = matched.method + end tienv = ccall(:jl_type_intersection_with_env, Any, (Any, Any), nargtype, method.sig)::SimpleVector ti = tienv[1] env = tienv[2]::SimpleVector @@ -2245,7 +2287,7 @@ function abstract_invoke(interp::AbstractInterpreter, arginfo::ArgInfo, si::Stmt match = MethodMatch(ti, env, method, argtype <: method.sig) ft′_box = Core.Box(ft′) lookupsig_box = Core.Box(lookupsig) - invokecall = InvokeCall(types, lookupsig) + invokecall = InvokeCall(types) return Future{CallMeta}(mresult, interp, sv) do result, interp, sv (; rt, exct, effects, edge, volatile_inf_result) = result local ft′ = ft′_box.contents @@ -2614,7 +2656,7 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), if sv isa InferenceState && f === typeassert # perform very limited back-propagation of invariants after this type assertion if rt !== Bottom && isa(fargs, Vector{Any}) - farg2 = fargs[2] + farg2 = ssa_def_slot(fargs[2], sv) if farg2 isa SlotNumber refinements = SlotRefinement(farg2, rt) end @@ -2852,20 +2894,21 @@ end function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, sstate::StatementState, sv::AbsIntState) f = abstract_eval_value(interp, e.args[2], sstate, sv) - # rt = sp_type_rewrap(e.args[3], sv.linfo, true) + # rt = sp_type_rewrap(e.args[3], sv.linfo, true) # verify that the result type make sense? + # rt === Bottom && return RTEffects(Union{}, Any, EFFECTS_UNKNOWN) atv = e.args[4]::SimpleVector at = Vector{Any}(undef, length(atv) + 1) at[1] = f for i = 1:length(atv) - at[i + 1] = sp_type_rewrap(at[i], frame_instance(sv), false) - at[i + 1] === Bottom && return + atᵢ = at[i + 1] = sp_type_rewrap(atv[i], frame_instance(sv), false) + atᵢ === Bottom && return RTEffects(Union{}, Any, EFFECTS_UNKNOWN) end # this may be the wrong world for the call, # but some of the result is likely to be valid anyways # and that may help generate better codegen abstract_call(interp, ArgInfo(nothing, at), StmtInfo(false, false), sv)::Future rt = e.args[1] - isa(rt, Type) || (rt = Any) + isconcretetype(rt) || (rt = Any) return RTEffects(rt, Any, EFFECTS_UNKNOWN) end @@ -3198,8 +3241,16 @@ function abstract_eval_throw_undef_if_not(interp::AbstractInterpreter, e::Expr, end function abstract_eval_the_exception(::AbstractInterpreter, sv::InferenceState) - (;handlers, handler_at) = sv.handler_info::HandlerInfo - return the_exception_info(handlers[handler_at[sv.currpc][2]].exct) + (;handler_info) = sv + if handler_info === nothing + return the_exception_info(Any) + end + (;handlers, handler_at) = handler_info + handler_id = handler_at[sv.currpc][2] + if handler_id === 0 + return the_exception_info(Any) + end + return the_exception_info(handlers[handler_id].exct) end abstract_eval_the_exception(::AbstractInterpreter, ::IRInterpretationState) = the_exception_info(Any) the_exception_info(@nospecialize t) = RTEffects(t, Union{}, Effects(EFFECTS_TOTAL; consistent=ALWAYS_FALSE)) @@ -4009,13 +4060,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState, nextr end effects === nothing || merge_override_effects!(interp, effects, frame) if lhs !== nothing && rt !== Bottom - if isa(lhs, SlotNumber) - changes = StateUpdate(lhs, VarState(rt, false)) - elseif isa(lhs, GlobalRef) - handle_global_assignment!(interp, frame, currsaw_latestworld, lhs, rt) - else - merge_effects!(interp, frame, EFFECTS_UNKNOWN) - end + changes = StateUpdate(lhs::SlotNumber, VarState(rt, false)) end end if !has_curr_ssaflag(frame, IR_FLAG_NOTHROW) diff --git a/Compiler/src/abstractlattice.jl b/Compiler/src/abstractlattice.jl index 645c865d085b3..7a9cff8918175 100644 --- a/Compiler/src/abstractlattice.jl +++ b/Compiler/src/abstractlattice.jl @@ -229,7 +229,7 @@ end if isa(t, Const) # don't consider mutable values useful constants val = t.val - return isa(val, Symbol) || isa(val, Type) || !ismutable(val) + return isa(val, Symbol) || isa(val, Type) || isa(val, Method) || isa(val, CodeInstance) || !ismutable(val) end isa(t, PartialTypeVar) && return false # this isn't forwardable return is_const_prop_profitable_arg(widenlattice(𝕃), t) diff --git a/Compiler/src/bootstrap.jl b/Compiler/src/bootstrap.jl index 7ee439cc7ac67..475c53e317152 100644 --- a/Compiler/src/bootstrap.jl +++ b/Compiler/src/bootstrap.jl @@ -5,7 +5,15 @@ # especially try to make sure any recursive and leaf functions have concrete signatures, # since we won't be able to specialize & infer them at runtime -activate_codegen!() = ccall(:jl_set_typeinf_func, Cvoid, (Any,), typeinf_ext_toplevel) +function activate_codegen!() + ccall(:jl_set_typeinf_func, Cvoid, (Any,), typeinf_ext_toplevel) + Core.eval(Compiler, quote + let typeinf_world_age = Base.tls_world_age() + @eval Core.OptimizedGenerics.CompilerPlugins.typeinf(::Nothing, mi::MethodInstance, source_mode::UInt8) = + Base.invoke_in_world($(Expr(:$, :typeinf_world_age)), typeinf_ext_toplevel, mi, Base.tls_world_age(), source_mode) + end + end) +end function bootstrap!() let time() = ccall(:jl_clock_now, Float64, ()) diff --git a/Compiler/src/effects.jl b/Compiler/src/effects.jl index e521166fd61fa..9aea4cb204ec6 100644 --- a/Compiler/src/effects.jl +++ b/Compiler/src/effects.jl @@ -335,6 +335,7 @@ is_inaccessiblemem_or_argmemonly(effects::Effects) = effects.inaccessiblememonly is_consistent_overlay(effects::Effects) = effects.nonoverlayed === CONSISTENT_OVERLAY +# (sync this with codegen.cpp and staticdata.c effects_foldable functions) function encode_effects(e::Effects) return ((e.consistent % UInt32) << 0) | ((e.effect_free % UInt32) << 3) | diff --git a/Compiler/src/inferencestate.jl b/Compiler/src/inferencestate.jl index 9eb929b725fbf..0ea0fc684b689 100644 --- a/Compiler/src/inferencestate.jl +++ b/Compiler/src/inferencestate.jl @@ -219,16 +219,29 @@ const CACHE_MODE_GLOBAL = 0x01 << 0 # cached globally, optimization required const CACHE_MODE_LOCAL = 0x01 << 1 # cached locally, optimization required const CACHE_MODE_VOLATILE = 0x01 << 2 # not cached, optimization required -mutable struct TryCatchFrame +abstract type Handler end +get_enter_idx(handler::Handler) = get_enter_idx_impl(handler)::Int + +mutable struct TryCatchFrame <: Handler exct scopet const enter_idx::Int scope_uses::Vector{Int} - TryCatchFrame(@nospecialize(exct), @nospecialize(scopet), enter_idx::Int) = new(exct, scopet, enter_idx) + TryCatchFrame(@nospecialize(exct), @nospecialize(scopet), enter_idx::Int) = + new(exct, scopet, enter_idx) +end +TryCatchFrame(stmt::EnterNode, pc::Int) = + TryCatchFrame(Bottom, isdefined(stmt, :scope) ? Bottom : nothing, pc) +get_enter_idx_impl((; enter_idx)::TryCatchFrame) = enter_idx + +struct SimpleHandler <: Handler + enter_idx::Int end +SimpleHandler(::EnterNode, pc::Int) = SimpleHandler(pc) +get_enter_idx_impl((; enter_idx)::SimpleHandler) = enter_idx -struct HandlerInfo - handlers::Vector{TryCatchFrame} +struct HandlerInfo{T<:Handler} + handlers::Vector{T} handler_at::Vector{Tuple{Int,Int}} # tuple of current (handler, exception stack) value at the pc end @@ -261,12 +274,13 @@ mutable struct InferenceState currbb::Int currpc::Int ip::BitSet#=TODO BoundedMinPrioritySet=# # current active instruction pointers - handler_info::Union{Nothing,HandlerInfo} + handler_info::Union{Nothing,HandlerInfo{TryCatchFrame}} ssavalue_uses::Vector{BitSet} # ssavalue sparsity and restart info # TODO: Could keep this sparsely by doing structural liveness analysis ahead of time. bb_vartables::Vector{Union{Nothing,VarTable}} # nothing if not analyzed yet bb_saw_latestworld::Vector{Bool} ssavaluetypes::Vector{Any} + ssaflags::Vector{UInt32} edges::Vector{Any} stmt_info::Vector{CallInfo} @@ -317,7 +331,7 @@ mutable struct InferenceState currbb = currpc = 1 ip = BitSet(1) # TODO BitSetBoundedMinPrioritySet(1) - handler_info = compute_trycatch(code) + handler_info = ComputeTryCatch{TryCatchFrame}()(code) nssavalues = src.ssavaluetypes::Int ssavalue_uses = find_ssavalue_uses(code, nssavalues) nstmts = length(code) @@ -343,6 +357,7 @@ mutable struct InferenceState bb_vartable1[i] = VarState(argtyp, i > nargtypes) end src.ssavaluetypes = ssavaluetypes = Any[ NOT_FOUND for i = 1:nssavalues ] + ssaflags = copy(src.ssaflags) unreachable = BitSet() pclimitations = IdSet{InferenceState}() @@ -374,7 +389,7 @@ mutable struct InferenceState this = new( mi, WorldWithRange(world, valid_worlds), mod, sptypes, slottypes, src, cfg, spec_info, - currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, bb_saw_latestworld, ssavaluetypes, edges, stmt_info, + currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, bb_saw_latestworld, ssavaluetypes, ssaflags, edges, stmt_info, tasks, pclimitations, limitations, cycle_backedges, callstack, parentid, frameid, cycleid, result, unreachable, bestguess, exc_bestguess, ipo_effects, restrict_abstract_call_sites, cache_mode, insert_coverage, @@ -419,10 +434,16 @@ is_inferred(result::InferenceResult) = result.result !== nothing was_reached(sv::InferenceState, pc::Int) = sv.ssavaluetypes[pc] !== NOT_FOUND -compute_trycatch(ir::IRCode) = compute_trycatch(ir.stmts.stmt, ir.cfg.blocks) +struct ComputeTryCatch{T<:Handler} end + +const compute_trycatch = ComputeTryCatch{SimpleHandler}() + +(compute_trycatch::ComputeTryCatch{SimpleHandler})(ir::IRCode) = + compute_trycatch(ir.stmts.stmt, ir.cfg.blocks) """ - compute_trycatch(code, [, bbs]) -> handler_info::Union{Nothing,HandlerInfo} + (::ComputeTryCatch{Handler})(code, [, bbs]) -> handler_info::Union{Nothing,HandlerInfo{Handler}} + const compute_trycatch = ComputeTryCatch{SimpleHandler}() Given the code of a function, compute, at every statement, the current try/catch handler, and the current exception stack top. This function returns @@ -431,9 +452,9 @@ a tuple of: 1. `handler_info.handler_at`: A statement length vector of tuples `(catch_handler, exception_stack)`, which are indices into `handlers` - 2. `handler_info.handlers`: A `TryCatchFrame` vector of handlers + 2. `handler_info.handlers`: A `Handler` vector of handlers """ -function compute_trycatch(code::Vector{Any}, bbs::Union{Vector{BasicBlock},Nothing}=nothing) +function (::ComputeTryCatch{Handler})(code::Vector{Any}, bbs::Union{Vector{BasicBlock},Nothing}=nothing) where Handler # The goal initially is to record the frame like this for the state at exit: # 1: (enter 3) # == 0 # 3: (expr) # == 1 @@ -452,10 +473,10 @@ function compute_trycatch(code::Vector{Any}, bbs::Union{Vector{BasicBlock},Nothi stmt = code[pc] if isa(stmt, EnterNode) (;handlers, handler_at) = handler_info = - (handler_info === nothing ? HandlerInfo(TryCatchFrame[], fill((0, 0), n)) : handler_info) + (handler_info === nothing ? HandlerInfo{Handler}(Handler[], fill((0, 0), n)) : handler_info) l = stmt.catch_dest - (bbs !== nothing) && (l = first(bbs[l].stmts)) - push!(handlers, TryCatchFrame(Bottom, isdefined(stmt, :scope) ? Bottom : nothing, pc)) + (bbs !== nothing) && (l != 0) && (l = first(bbs[l].stmts)) + push!(handlers, Handler(stmt, pc)) handler_id = length(handlers) handler_at[pc + 1] = (handler_id, 0) push!(ip, pc + 1) @@ -498,7 +519,7 @@ function compute_trycatch(code::Vector{Any}, bbs::Union{Vector{BasicBlock},Nothi break elseif isa(stmt, EnterNode) l = stmt.catch_dest - (bbs !== nothing) && (l = first(bbs[l].stmts)) + (bbs !== nothing) && (l != 0) && (l = first(bbs[l].stmts)) # We assigned a handler number above. Here we just merge that # with out current handler information. if l != 0 @@ -524,7 +545,7 @@ function compute_trycatch(code::Vector{Any}, bbs::Union{Vector{BasicBlock},Nothi end cur_hand = cur_stacks[1] for i = 1:l - cur_hand = handler_at[handlers[cur_hand].enter_idx][1] + cur_hand = handler_at[get_enter_idx(handlers[cur_hand])][1] end cur_stacks = (cur_hand, cur_stacks[2]) cur_stacks == (0, 0) && break @@ -1004,25 +1025,22 @@ function callers_in_cycle(sv::InferenceState) end callers_in_cycle(sv::IRInterpretationState) = AbsIntCycle(sv.callstack::Vector{AbsIntState}, 0, 0) -get_curr_ssaflag(sv::InferenceState) = sv.src.ssaflags[sv.currpc] +get_curr_ssaflag(sv::InferenceState) = sv.ssaflags[sv.currpc] get_curr_ssaflag(sv::IRInterpretationState) = sv.ir.stmts[sv.curridx][:flag] -has_curr_ssaflag(sv::InferenceState, flag::UInt32) = has_flag(sv.src.ssaflags[sv.currpc], flag) +has_curr_ssaflag(sv::InferenceState, flag::UInt32) = has_flag(sv.ssaflags[sv.currpc], flag) has_curr_ssaflag(sv::IRInterpretationState, flag::UInt32) = has_flag(sv.ir.stmts[sv.curridx][:flag], flag) function set_curr_ssaflag!(sv::InferenceState, flag::UInt32, mask::UInt32=typemax(UInt32)) - curr_flag = sv.src.ssaflags[sv.currpc] - sv.src.ssaflags[sv.currpc] = (curr_flag & ~mask) | flag -end -function set_curr_ssaflag!(sv::IRInterpretationState, flag::UInt32, mask::UInt32=typemax(UInt32)) - curr_flag = sv.ir.stmts[sv.curridx][:flag] - sv.ir.stmts[sv.curridx][:flag] = (curr_flag & ~mask) | flag + curr_flag = sv.ssaflags[sv.currpc] + sv.ssaflags[sv.currpc] = (curr_flag & ~mask) | flag + nothing end -add_curr_ssaflag!(sv::InferenceState, flag::UInt32) = sv.src.ssaflags[sv.currpc] |= flag +add_curr_ssaflag!(sv::InferenceState, flag::UInt32) = sv.ssaflags[sv.currpc] |= flag add_curr_ssaflag!(sv::IRInterpretationState, flag::UInt32) = add_flag!(sv.ir.stmts[sv.curridx], flag) -sub_curr_ssaflag!(sv::InferenceState, flag::UInt32) = sv.src.ssaflags[sv.currpc] &= ~flag +sub_curr_ssaflag!(sv::InferenceState, flag::UInt32) = sv.ssaflags[sv.currpc] &= ~flag sub_curr_ssaflag!(sv::IRInterpretationState, flag::UInt32) = sub_flag!(sv.ir.stmts[sv.curridx], flag) function merge_effects!(::AbstractInterpreter, caller::InferenceState, effects::Effects) @@ -1035,8 +1053,8 @@ function merge_effects!(::AbstractInterpreter, caller::InferenceState, effects:: end merge_effects!(::AbstractInterpreter, ::IRInterpretationState, ::Effects) = return -decode_statement_effects_override(sv::AbsIntState) = - decode_statement_effects_override(get_curr_ssaflag(sv)) +decode_statement_effects_override(sv::InferenceState) = decode_statement_effects_override(sv.src.ssaflags[sv.currpc]) +decode_statement_effects_override(sv::IRInterpretationState) = decode_statement_effects_override(UInt32(0)) struct InferenceLoopState rt diff --git a/Compiler/src/optimize.jl b/Compiler/src/optimize.jl index d2dfd26bfa00d..bc3fc4f9705c4 100644 --- a/Compiler/src/optimize.jl +++ b/Compiler/src/optimize.jl @@ -17,37 +17,41 @@ const SLOT_USEDUNDEF = 32 # slot has uses that might raise UndefVarError const IR_FLAG_NULL = zero(UInt32) # This statement is marked as @inbounds by user. -# Ff replaced by inlining, any contained boundschecks may be removed. +# If replaced by inlining, any contained boundschecks may be removed. const IR_FLAG_INBOUNDS = one(UInt32) << 0 # This statement is marked as @inline by user const IR_FLAG_INLINE = one(UInt32) << 1 # This statement is marked as @noinline by user const IR_FLAG_NOINLINE = one(UInt32) << 2 -# An optimization pass has updated this statement in a way that may -# have exposed information that inference did not see. Re-running -# inference on this statement may be profitable. -const IR_FLAG_REFINED = one(UInt32) << 3 # This statement is proven :consistent -const IR_FLAG_CONSISTENT = one(UInt32) << 4 +const IR_FLAG_CONSISTENT = one(UInt32) << 3 # This statement is proven :effect_free -const IR_FLAG_EFFECT_FREE = one(UInt32) << 5 +const IR_FLAG_EFFECT_FREE = one(UInt32) << 4 # This statement is proven :nothrow -const IR_FLAG_NOTHROW = one(UInt32) << 6 -# This statement is proven :terminates -const IR_FLAG_TERMINATES = one(UInt32) << 7 -# This statement is proven :noub -const IR_FLAG_NOUB = one(UInt32) << 8 -# TODO: Both of these should eventually go away once -# This statement is :effect_free == EFFECT_FREE_IF_INACCESSIBLEMEMONLY -const IR_FLAG_EFIIMO = one(UInt32) << 9 -# This statement is :inaccessiblememonly == INACCESSIBLEMEM_OR_ARGMEMONLY -const IR_FLAG_INACCESSIBLEMEM_OR_ARGMEM = one(UInt32) << 10 +const IR_FLAG_NOTHROW = one(UInt32) << 5 +# This statement is proven :terminates_globally +const IR_FLAG_TERMINATES = one(UInt32) << 6 +#const IR_FLAG_TERMINATES_LOCALLY = one(UInt32) << 7 +#const IR_FLAG_NOTASKSTATE = one(UInt32) << 8 +#const IR_FLAG_INACCESSIBLEMEM = one(UInt32) << 9 +const IR_FLAG_NOUB = one(UInt32) << 10 +#const IR_FLAG_NOUBINIB = one(UInt32) << 11 +#const IR_FLAG_CONSISTENTOVERLAY = one(UInt32) << 12 # This statement is :nortcall -const IR_FLAG_NORTCALL = one(UInt32) << 11 +const IR_FLAG_NORTCALL = one(UInt32) << 13 +# An optimization pass has updated this statement in a way that may +# have exposed information that inference did not see. Re-running +# inference on this statement may be profitable. +const IR_FLAG_REFINED = one(UInt32) << 16 # This statement has no users and may be deleted if flags get refined to IR_FLAGS_REMOVABLE -const IR_FLAG_UNUSED = one(UInt32) << 12 +const IR_FLAG_UNUSED = one(UInt32) << 17 +# TODO: Both of these next two should eventually go away once +# This statement is :effect_free == EFFECT_FREE_IF_INACCESSIBLEMEMONLY +const IR_FLAG_EFIIMO = one(UInt32) << 18 +# This statement is :inaccessiblememonly == INACCESSIBLEMEM_OR_ARGMEMONLY +const IR_FLAG_INACCESSIBLEMEM_OR_ARGMEM = one(UInt32) << 19 -const NUM_IR_FLAGS = 13 # sync with julia.h +const NUM_IR_FLAGS = 3 # sync with julia.h const IR_FLAGS_EFFECTS = IR_FLAG_CONSISTENT | IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW | @@ -643,9 +647,11 @@ struct GetNativeEscapeCache{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 +function ((; code_cache)::GetNativeEscapeCache)(codeinst::Union{CodeInstance,MethodInstance}) + if codeinst isa MethodInstance + codeinst = get(code_cache, codeinst, nothing) + codeinst isa CodeInstance || return false + end argescapes = traverse_analysis_results(codeinst) do @nospecialize result return result isa EscapeAnalysis.ArgEscapeCache ? result : nothing end @@ -815,6 +821,7 @@ function scan_non_dataflow_flags!(inst::Instruction, sv::PostOptAnalysisState) sv.nortcall = false end end + nothing end function scan_inconsistency!(inst::Instruction, sv::PostOptAnalysisState) @@ -1408,16 +1415,7 @@ function statement_cost(ex::Expr, line::Int, src::Union{CodeInfo, IRCode}, sptyp extyp = line == -1 ? Any : argextype(SSAValue(line), src, sptypes) return extyp === Union{} ? 0 : UNKNOWN_CALL_COST elseif head === :(=) - if ex.args[1] isa GlobalRef - cost = UNKNOWN_CALL_COST - else - cost = 0 - end - a = ex.args[2] - if a isa Expr - cost = plus_saturate(cost, statement_cost(a, -1, src, sptypes, params)) - end - return cost + return statement_cost(ex.args[2], -1, src, sptypes, params) elseif head === :copyast return 100 end diff --git a/Compiler/src/ssair/EscapeAnalysis.jl b/Compiler/src/ssair/EscapeAnalysis.jl index 47a7840628bb5..af8e9b1a4959e 100644 --- a/Compiler/src/ssair/EscapeAnalysis.jl +++ b/Compiler/src/ssair/EscapeAnalysis.jl @@ -13,22 +13,21 @@ export using Base: Base # imports -import Base: ==, getindex, setindex! +import Base: ==, copy, getindex, setindex! # usings -using Core: MethodMatch, SimpleVector, ifelse, sizeof +using Core: Builtin, IntrinsicFunction, SimpleVector, ifelse, sizeof using Core.IR using Base: # Base definitions @__MODULE__, @assert, @eval, @goto, @inbounds, @inline, @label, @noinline, - @nospecialize, @specialize, BitSet, Callable, Csize_t, IdDict, IdSet, UnitRange, Vector, - copy, delete!, empty!, enumerate, error, first, get, get!, haskey, in, isassigned, - isempty, ismutabletype, keys, last, length, max, min, missing, pop!, push!, pushfirst!, - unwrap_unionall, !, !=, !==, &, *, +, -, :, <, <<, =>, >, |, ∈, ∉, ∩, ∪, ≠, ≤, ≥, ⊆, - hasintersect + @nospecialize, @specialize, BitSet, IdDict, IdSet, UnitRange, Vector, + delete!, empty!, enumerate, first, get, get!, hasintersect, haskey, isassigned, + isempty, length, max, min, missing, println, push!, pushfirst!, + !, !==, &, *, +, -, :, <, <<, >, |, ∈, ∉, ∩, ∪, ≠, ≤, ≥, ⊆ using ..Compiler: # Compiler specific definitions - AbstractLattice, Bottom, IRCode, IR_FLAG_NOTHROW, InferenceResult, SimpleInferenceLattice, - argextype, fieldcount_noerror, hasintersect, has_flag, intrinsic_nothrow, - is_meta_expr_head, is_identity_free_argtype, isexpr, println, setfield!_nothrow, - singleton_type, try_compute_field, try_compute_fieldidx, widenconst, ⊑, Compiler + AbstractLattice, Compiler, IRCode, IR_FLAG_NOTHROW, + argextype, fieldcount_noerror, has_flag, intrinsic_nothrow, is_meta_expr_head, + is_identity_free_argtype, isexpr, setfield!_nothrow, singleton_type, try_compute_field, + try_compute_fieldidx, widenconst function include(x::String) if !isdefined(Base, :end_base_include) @@ -53,14 +52,13 @@ A lattice for escape information, which holds the following properties: * `pc ∈ x.ThrownEscape`: `x` may be thrown at the SSA statement at `pc` * `-1 ∈ x.ThrownEscape`: `x` may be thrown at arbitrary points of this call frame (the top) This information will be used by `escape_exception!` to propagate potential escapes via exception. -- `x.AliasInfo::Union{Bool,IndexableFields,IndexableElements,Unindexable}`: maintains all possible values +- `x.AliasInfo::Union{Bool,IndexableFields,Unindexable}`: maintains all possible values that can be aliased to fields or array elements of `x`: * `x.AliasInfo === false` indicates the fields/elements of `x` aren't analyzed yet * `x.AliasInfo === true` indicates the fields/elements of `x` can't be analyzed, e.g. the type of `x` is not known or is not concrete and thus its fields/elements can't be known precisely * `x.AliasInfo::IndexableFields` records all the possible values that can be aliased to fields of object `x` with precise index information - * `x.AliasInfo::IndexableElements` records all the possible values that can be aliased to elements of array `x` with precise index information * `x.AliasInfo::Unindexable` records all the possible values that can be aliased to fields/elements of `x` without precise index information - `x.Liveness::BitSet`: records SSA statement numbers where `x` should be live, e.g. to be used as a call argument, to be returned to a caller, or preserved for `:foreigncall`: @@ -87,14 +85,14 @@ struct EscapeInfo Analyzed::Bool ReturnEscape::Bool ThrownEscape::BitSet - AliasInfo #::Union{IndexableFields,IndexableElements,Unindexable,Bool} + AliasInfo #::Union{IndexableFields,Unindexable,Bool} Liveness::BitSet function EscapeInfo( Analyzed::Bool, ReturnEscape::Bool, ThrownEscape::BitSet, - AliasInfo#=::Union{IndexableFields,IndexableElements,Unindexable,Bool}=#, + AliasInfo#=::Union{IndexableFields,Unindexable,Bool}=#, Liveness::BitSet) @nospecialize AliasInfo return new( @@ -108,7 +106,7 @@ struct EscapeInfo x::EscapeInfo, # non-concrete fields should be passed as default arguments # in order to avoid allocating non-concrete `NamedTuple`s - AliasInfo#=::Union{IndexableFields,IndexableElements,Unindexable,Bool}=# = x.AliasInfo; + AliasInfo#=::Union{IndexableFields,Unindexable,Bool}=# = x.AliasInfo; Analyzed::Bool = x.Analyzed, ReturnEscape::Bool = x.ReturnEscape, ThrownEscape::BitSet = x.ThrownEscape, @@ -149,11 +147,11 @@ const ⊥, ⊤ = NotAnalyzed(), AllEscape() # Convenience names for some ⊑ₑ queries has_no_escape(x::EscapeInfo) = !x.ReturnEscape && isempty(x.ThrownEscape) && 0 ∉ x.Liveness -has_arg_escape(x::EscapeInfo) = 0 in x.Liveness +has_arg_escape(x::EscapeInfo) = 0 ∈ x.Liveness has_return_escape(x::EscapeInfo) = x.ReturnEscape -has_return_escape(x::EscapeInfo, pc::Int) = x.ReturnEscape && (-1 ∈ x.Liveness || pc in x.Liveness) +has_return_escape(x::EscapeInfo, pc::Int) = x.ReturnEscape && (-1 ∈ x.Liveness || pc ∈ x.Liveness) has_thrown_escape(x::EscapeInfo) = !isempty(x.ThrownEscape) -has_thrown_escape(x::EscapeInfo, pc::Int) = -1 ∈ x.ThrownEscape || pc in x.ThrownEscape +has_thrown_escape(x::EscapeInfo, pc::Int) = -1 ∈ x.ThrownEscape || pc ∈ x.ThrownEscape has_all_escape(x::EscapeInfo) = ⊤ ⊑ₑ x # utility lattice constructors @@ -166,14 +164,13 @@ ignore_liveness(x::EscapeInfo) = EscapeInfo(x; Liveness=BOT_LIVENESS) struct IndexableFields infos::Vector{AInfo} end -struct IndexableElements - infos::IdDict{Int,AInfo} -end struct Unindexable info::AInfo end IndexableFields(nflds::Int) = IndexableFields(AInfo[AInfo() for _ in 1:nflds]) Unindexable() = Unindexable(AInfo()) +copy(AliasInfo::IndexableFields) = IndexableFields(AInfo[copy(info) for info in AliasInfo.infos]) +copy(AliasInfo::Unindexable) = Unindexable(copy(AliasInfo.info)) merge_to_unindexable(AliasInfo::IndexableFields) = Unindexable(merge_to_unindexable(AliasInfo.infos)) merge_to_unindexable(AliasInfo::Unindexable, AliasInfos::IndexableFields) = Unindexable(merge_to_unindexable(AliasInfo.info, AliasInfos.infos)) @@ -184,15 +181,6 @@ function merge_to_unindexable(info::AInfo, infos::Vector{AInfo}) end return info end -merge_to_unindexable(AliasInfo::IndexableElements) = Unindexable(merge_to_unindexable(AliasInfo.infos)) -merge_to_unindexable(AliasInfo::Unindexable, AliasInfos::IndexableElements) = Unindexable(merge_to_unindexable(AliasInfo.info, AliasInfos.infos)) -merge_to_unindexable(infos::IdDict{Int,AInfo}) = merge_to_unindexable(AInfo(), infos) -function merge_to_unindexable(info::AInfo, infos::IdDict{Int,AInfo}) - for idx in keys(infos) - info = info ∪ infos[idx] - end - return info -end # we need to make sure this `==` operator corresponds to lattice equality rather than object equality, # otherwise `propagate_changes` can't detect the convergence @@ -215,9 +203,6 @@ x::EscapeInfo == y::EscapeInfo = begin elseif isa(xa, IndexableFields) isa(ya, IndexableFields) || return false xa.infos == ya.infos || return false - elseif isa(xa, IndexableElements) - isa(ya, IndexableElements) || return false - xa.infos == ya.infos || return false else xa = xa::Unindexable isa(ya, Unindexable) || return false @@ -269,8 +254,6 @@ x::EscapeInfo ⊑ₑ y::EscapeInfo = begin for i in 1:xn xinfos[i] ⊆ yinfos[i] || return false end - elseif isa(ya, IndexableElements) - return false elseif isa(ya, Unindexable) xinfos, yinfo = xa.infos, ya.info for i = length(xinfos) @@ -279,23 +262,6 @@ x::EscapeInfo ⊑ₑ y::EscapeInfo = begin else ya === true || return false end - elseif isa(xa, IndexableElements) - if isa(ya, IndexableElements) - xinfos, yinfos = xa.infos, ya.infos - keys(xinfos) ⊆ keys(yinfos) || return false - for idx in keys(xinfos) - xinfos[idx] ⊆ yinfos[idx] || return false - end - elseif isa(ya, IndexableFields) - return false - elseif isa(ya, Unindexable) - xinfos, yinfo = xa.infos, ya.info - for idx in keys(xinfos) - xinfos[idx] ⊆ yinfo || return false - end - else - ya === true || return false - end else xa = xa::Unindexable if isa(ya, Unindexable) @@ -401,33 +367,10 @@ function merge_alias_info(@nospecialize(xa), @nospecialize(ya)) else return true # handle conflicting case conservatively end - elseif isa(xa, IndexableElements) - if isa(ya, IndexableElements) - xinfos, yinfos = xa.infos, ya.infos - infos = IdDict{Int,AInfo}() - for idx in keys(xinfos) - if !haskey(yinfos, idx) - infos[idx] = xinfos[idx] - else - infos[idx] = xinfos[idx] ∪ yinfos[idx] - end - end - for idx in keys(yinfos) - haskey(xinfos, idx) && continue # unioned already - infos[idx] = yinfos[idx] - end - return IndexableElements(infos) - elseif isa(ya, Unindexable) - return merge_to_unindexable(ya, xa) - else - return true # handle conflicting case conservatively - end else xa = xa::Unindexable if isa(ya, IndexableFields) return merge_to_unindexable(xa, ya) - elseif isa(ya, IndexableElements) - return merge_to_unindexable(xa, ya) else ya = ya::Unindexable xinfo, yinfo = xa.info, ya.info @@ -439,8 +382,6 @@ end const AliasSet = IntDisjointSet{Int} -const ArrayInfo = IdDict{Int,Vector{Int}} - """ estate::EscapeState @@ -451,13 +392,12 @@ struct EscapeState escapes::Vector{EscapeInfo} aliasset::AliasSet nargs::Int - arrayinfo::Union{Nothing,ArrayInfo} end -function EscapeState(nargs::Int, nstmts::Int, arrayinfo::Union{Nothing,ArrayInfo}) +function EscapeState(nargs::Int, nstmts::Int) escapes = EscapeInfo[ 1 ≤ i ≤ nargs ? ArgEscape() : ⊥ for i in 1:(nargs+nstmts)] aliasset = AliasSet(nargs+nstmts) - return EscapeState(escapes, aliasset, nargs, arrayinfo) + return EscapeState(escapes, aliasset, nargs) end function getindex(estate::EscapeState, @nospecialize(x)) xidx = iridx(x, estate) @@ -503,8 +443,7 @@ that is analyzable in the context of `estate`. `iridx(irval(xidx, state), state) === xidx`. """ function irval(xidx::Int, estate::EscapeState) - x = xidx > estate.nargs ? SSAValue(xidx-estate.nargs) : Argument(xidx) - return x + return xidx > estate.nargs ? SSAValue(xidx-estate.nargs) : Argument(xidx) end function getaliases(x::Union{Argument,SSAValue}, estate::EscapeState) @@ -621,8 +560,8 @@ function analyze_escapes(ir::IRCode, nargs::Int, 𝕃ₒ::AbstractLattice, get_e stmts = ir.stmts nstmts = length(stmts) + length(ir.new_nodes.stmts) - tryregions, arrayinfo = compute_frameinfo(ir) - estate = EscapeState(nargs, nstmts, arrayinfo) + tryregions = compute_frameinfo(ir) + estate = EscapeState(nargs, nstmts) changes = Changes() # keeps changes that happen at current statement astate = AnalysisState(ir, estate, changes, 𝕃ₒ, get_escape_cache) @@ -642,13 +581,6 @@ function analyze_escapes(ir::IRCode, nargs::Int, 𝕃ₒ::AbstractLattice, get_e escape_invoke!(astate, pc, stmt.args) elseif head === :new || head === :splatnew escape_new!(astate, pc, stmt.args) - elseif head === :(=) - lhs, rhs = stmt.args - if isa(lhs, GlobalRef) # global store - add_escape_change!(astate, rhs, ⊤) - else - unexpected_assignment!(ir, pc) - end elseif head === :foreigncall escape_foreigncall!(astate, pc, stmt.args) elseif head === :throw_undef_if_not # XXX when is this expression inserted ? @@ -719,21 +651,15 @@ function analyze_escapes(ir::IRCode, nargs::Int, 𝕃ₒ::AbstractLattice, get_e end """ - compute_frameinfo(ir::IRCode) -> (tryregions, arrayinfo) + compute_frameinfo(ir::IRCode) -> tryregions -A preparatory linear scan before the escape analysis on `ir` to find: -- `tryregions::Union{Nothing,Vector{UnitRange{Int}}}`: regions in which potential `throw`s can be caught (used by `escape_exception!`) -- `arrayinfo::Union{Nothing,IdDict{Int,Vector{Int}}}`: array allocations whose dimensions are known precisely (with some very simple local analysis) - -!!! note - This array dimension analysis to compute `arrayinfo` is very local and doesn't account - for flow-sensitivity nor complex aliasing. - Ideally this dimension analysis should be done as a part of type inference that - propagates array dimensions in a flow sensitive way. +A preparatory linear scan before the escape analysis on `ir` to find +`tryregions::Union{Nothing,Vector{UnitRange{Int}}}`, that represent regions in which +potential `throw`s can be caught (used by `escape_exception!`) """ function compute_frameinfo(ir::IRCode) nstmts, nnewnodes = length(ir.stmts), length(ir.new_nodes.stmts) - tryregions, arrayinfo = nothing, nothing + tryregions = nothing for idx in 1:nstmts+nnewnodes inst = ir[SSAValue(idx)] stmt = inst[:stmt] @@ -745,41 +671,9 @@ function compute_frameinfo(ir::IRCode) leave_pc = first(ir.cfg.blocks[leave_block].stmts) push!(tryregions, idx:leave_pc) end - elseif arrayinfo !== nothing - # TODO this super limited alias analysis is able to handle only very simple cases - # this should be replaced with a proper forward dimension analysis - if isa(stmt, PhiNode) - values = stmt.values - local dims = nothing - for i = 1:length(values) - if isassigned(values, i) - val = values[i] - if isa(val, SSAValue) && haskey(arrayinfo, val.id) - if dims === nothing - dims = arrayinfo[val.id] - continue - elseif dims == arrayinfo[val.id] - continue - end - end - end - @goto next_stmt - end - if dims !== nothing - arrayinfo[idx] = dims - end - elseif isa(stmt, PiNode) - if isdefined(stmt, :val) - val = stmt.val - if isa(val, SSAValue) && haskey(arrayinfo, val.id) - arrayinfo[idx] = arrayinfo[val.id] - end - end - end end - @label next_stmt end - return tryregions, arrayinfo + return tryregions end # propagate changes, and check convergence @@ -833,7 +727,7 @@ end info = estate.escapes[xidx] Liveness = info.Liveness Liveness === TOP_LIVENESS && return false - livepc in Liveness && return false + livepc ∈ Liveness && return false if Liveness === BOT_LIVENESS || Liveness === ARG_LIVENESS # if this Liveness is a constant, we shouldn't modify it and propagate this change as a new EscapeInfo Liveness = copy(Liveness) @@ -981,26 +875,8 @@ function escape_unanalyzable_obj!(astate::AnalysisState, @nospecialize(obj), obj return objinfo end -@noinline function unexpected_assignment!(ir::IRCode, pc::Int) - @eval Main (ir = $ir; pc = $pc) - error("unexpected assignment found: inspect `Main.pc` and `Main.pc`") -end - is_nothrow(ir::IRCode, pc::Int) = has_flag(ir[SSAValue(pc)], IR_FLAG_NOTHROW) -# NOTE if we don't maintain the alias set that is separated from the lattice state, we can do -# something like below: it essentially incorporates forward escape propagation in our default -# backward propagation, and leads to inefficient convergence that requires more iterations -# # lhs = rhs: propagate escape information of `rhs` to `lhs` -# function escape_alias!(astate::AnalysisState, @nospecialize(lhs), @nospecialize(rhs)) -# if isa(rhs, SSAValue) || isa(rhs, Argument) -# vinfo = astate.estate[rhs] -# else -# return -# end -# add_escape_change!(astate, lhs, vinfo) -# end - """ escape_exception!(astate::AnalysisState, tryregions::Vector{UnitRange{Int}}) @@ -1056,7 +932,7 @@ function escape_exception!(astate::AnalysisState, tryregions::Vector{UnitRange{I xt === TOP_THROWN_ESCAPE && @goto propagate_exception_escape # fast pass for pc in xt for region in tryregions - pc in region && @goto propagate_exception_escape # early break because of AllEscape + pc ∈ region && @goto propagate_exception_escape # early break because of AllEscape end end continue @@ -1068,14 +944,16 @@ end # escape statically-resolved call, i.e. `Expr(:invoke, ::MethodInstance, ...)` function escape_invoke!(astate::AnalysisState, pc::Int, args::Vector{Any}) - mi = first(args) - if !(mi isa MethodInstance) - mi = (mi::CodeInstance).def # COMBAK get escape info directly from CI instead? + codeinst = first(args) + if codeinst isa MethodInstance + mi = codeinst + else + mi = (codeinst::CodeInstance).def end first_idx, last_idx = 2, length(args) add_liveness_changes!(astate, pc, args, first_idx, last_idx) # TODO inspect `astate.ir.stmts[pc][:info]` and use const-prop'ed `InferenceResult` if available - cache = astate.get_escape_cache(mi) + cache = astate.get_escape_cache(codeinst) ret = SSAValue(pc) if cache isa Bool if cache @@ -1155,12 +1033,6 @@ function escape_foreigncall!(astate::AnalysisState, pc::Int, args::Vector{Any}) argtypes = args[3]::SimpleVector nargs = length(argtypes) name = args[1] - nn = normalize(name) - if isa(nn, Symbol) - # if nn === :jl_gc_add_finalizer_th - # # TODO add `FinalizerEscape` ? - # end - end # NOTE array allocations might have been proven as nothrow (https://github.com/JuliaLang/julia/pull/43565) nothrow = is_nothrow(astate.ir, pc) name_info = nothrow ? ⊥ : ThrownEscape(pc) @@ -1195,37 +1067,31 @@ function escape_gc_preserve!(astate::AnalysisState, pc::Int, args::Vector{Any}) add_liveness_changes!(astate, pc, beginargs) end -normalize(@nospecialize x) = isa(x, QuoteNode) ? x.value : x - function escape_call!(astate::AnalysisState, pc::Int, args::Vector{Any}) - ir = astate.ir - ft = argextype(first(args), ir, ir.sptypes, ir.argtypes) + ft = argextype(first(args), astate.ir) f = singleton_type(ft) - if isa(f, Core.IntrinsicFunction) - # XXX somehow `:call` expression can creep in here, ideally we should be able to do: - # argtypes = Any[argextype(args[i], astate.ir) for i = 2:length(args)] - argtypes = Any[] - for i = 2:length(args) - arg = args[i] - push!(argtypes, isexpr(arg, :call) ? Any : argextype(arg, ir)) + if f isa IntrinsicFunction + if is_nothrow(astate.ir, pc) + add_liveness_changes!(astate, pc, args, 2) + else + add_fallback_changes!(astate, pc, args, 2) end - if intrinsic_nothrow(f, argtypes) + # TODO needs to account for pointer operations? + elseif f isa Builtin + result = escape_builtin!(f, astate, pc, args) + if result === missing + # if this call hasn't been handled by any of pre-defined handlers, escape it conservatively + add_conservative_changes!(astate, pc, args) + elseif result === true + add_liveness_changes!(astate, pc, args, 2) + elseif is_nothrow(astate.ir, pc) add_liveness_changes!(astate, pc, args, 2) else add_fallback_changes!(astate, pc, args, 2) end - return # TODO accounts for pointer operations? - end - result = escape_builtin!(f, astate, pc, args) - if result === missing - # if this call hasn't been handled by any of pre-defined handlers, escape it conservatively - add_conservative_changes!(astate, pc, args) - elseif result === true - add_liveness_changes!(astate, pc, args, 2) - elseif is_nothrow(astate.ir, pc) - add_liveness_changes!(astate, pc, args, 2) else - add_fallback_changes!(astate, pc, args, 2) + # escape this generic function or unknown function call conservatively + add_conservative_changes!(astate, pc, args) end end @@ -1286,6 +1152,7 @@ function escape_new!(astate::AnalysisState, pc::Int, args::Vector{Any}) @goto escape_indexable_def end elseif isa(AliasInfo, IndexableFields) + AliasInfo = copy(AliasInfo) @label escape_indexable_def # fields are known precisely: propagate escape information imposed on recorded possibilities to the exact field values infos = AliasInfo.infos @@ -1302,6 +1169,7 @@ function escape_new!(astate::AnalysisState, pc::Int, args::Vector{Any}) end add_escape_change!(astate, obj, EscapeInfo(objinfo, AliasInfo)) # update with new AliasInfo elseif isa(AliasInfo, Unindexable) + AliasInfo = copy(AliasInfo) @label escape_unindexable_def # fields are known partially: propagate escape information imposed on recorded possibilities to all fields values info = AliasInfo.info @@ -1355,7 +1223,7 @@ function analyze_fields(ir::IRCode, @nospecialize(typ), @nospecialize(fld)) return IndexableFields(nflds), fidx end -function reanalyze_fields(ir::IRCode, AliasInfo::IndexableFields, @nospecialize(typ), @nospecialize(fld)) +function reanalyze_fields(AliasInfo::IndexableFields, ir::IRCode, @nospecialize(typ), @nospecialize(fld)) nflds = fieldcount_noerror(typ) if nflds === nothing return merge_to_unindexable(AliasInfo), 0 @@ -1369,6 +1237,7 @@ function reanalyze_fields(ir::IRCode, AliasInfo::IndexableFields, @nospecialize( if fidx === nothing return merge_to_unindexable(AliasInfo), 0 end + AliasInfo = copy(AliasInfo) infos = AliasInfo.infos ninfos = length(infos) if nflds > ninfos @@ -1405,12 +1274,13 @@ function escape_builtin!(::typeof(getfield), astate::AnalysisState, pc::Int, arg @goto record_unindexable_use end elseif isa(AliasInfo, IndexableFields) - AliasInfo, fidx = reanalyze_fields(ir, AliasInfo, typ, args[3]) + AliasInfo, fidx = reanalyze_fields(AliasInfo, ir, typ, args[3]) isa(AliasInfo, Unindexable) && @goto record_unindexable_use @label record_indexable_use push!(AliasInfo.infos[fidx], LocalUse(pc)) add_escape_change!(astate, obj, EscapeInfo(objinfo, AliasInfo)) # update with new AliasInfo elseif isa(AliasInfo, Unindexable) + AliasInfo = copy(AliasInfo) @label record_unindexable_use push!(AliasInfo.info, LocalUse(pc)) add_escape_change!(astate, obj, EscapeInfo(objinfo, AliasInfo)) # update with new AliasInfo @@ -1451,7 +1321,7 @@ function escape_builtin!(::typeof(setfield!), astate::AnalysisState, pc::Int, ar end elseif isa(AliasInfo, IndexableFields) typ = widenconst(argextype(obj, ir)) - AliasInfo, fidx = reanalyze_fields(ir, AliasInfo, typ, args[3]) + AliasInfo, fidx = reanalyze_fields(AliasInfo, ir, typ, args[3]) isa(AliasInfo, Unindexable) && @goto escape_unindexable_def @label escape_indexable_def add_alias_escapes!(astate, val, AliasInfo.infos[fidx]) @@ -1461,7 +1331,7 @@ function escape_builtin!(::typeof(setfield!), astate::AnalysisState, pc::Int, ar # propagate the escape information of this object ignoring field information add_escape_change!(astate, val, ignore_aliasinfo(objinfo)) elseif isa(AliasInfo, Unindexable) - info = AliasInfo.info + AliasInfo = copy(AliasInfo) @label escape_unindexable_def add_alias_escapes!(astate, val, AliasInfo.info) push!(AliasInfo.info, LocalDef(pc)) @@ -1495,64 +1365,6 @@ function escape_builtin!(::typeof(setfield!), astate::AnalysisState, pc::Int, ar end end -# NOTE this function models and thus should be synced with the implementation of: -# size_t array_nd_index(jl_array_t *a, jl_value_t **args, size_t nidxs, ...) -function array_nd_index(astate::AnalysisState, @nospecialize(ary), args::Vector{Any}, nidxs::Int = length(args)) - isa(ary, SSAValue) || return nothing - aryid = ary.id - arrayinfo = astate.estate.arrayinfo - isa(arrayinfo, ArrayInfo) || return nothing - haskey(arrayinfo, aryid) || return nothing - dims = arrayinfo[aryid] - local i = 0 - local k, stride = 0, 1 - local nd = length(dims) - while k < nidxs - arg = args[k+1] - argval = argextype(arg, astate.ir) - isa(argval, Const) || return nothing - argval = argval.val - isa(argval, Int) || return nothing - ii = argval - 1 - i += ii * stride - d = k ≥ nd ? 1 : dims[k+1] - k < nidxs - 1 && ii ≥ d && return nothing # BoundsError - stride *= d - k += 1 - end - while k < nd - stride *= dims[k+1] - k += 1 - end - i ≥ stride && return nothing # BoundsError - return i -end - -function mark_unindexable!(astate::AnalysisState, @nospecialize(ary)) - isa(ary, SSAValue) || return - aryinfo = astate.estate[ary] - AliasInfo = aryinfo.AliasInfo - isa(AliasInfo, IndexableElements) || return - AliasInfo = merge_to_unindexable(AliasInfo) - add_escape_change!(astate, ary, EscapeInfo(aryinfo, AliasInfo)) -end - -# FIXME this implementation is very conservative, improve the accuracy and solve broken test cases -function escape_array_copy!(astate::AnalysisState, pc::Int, args::Vector{Any}) - length(args) ≥ 6 || return add_fallback_changes!(astate, pc, args) - ary = args[6] - aryt = argextype(ary, astate.ir) - aryt ⊑ Array || return add_fallback_changes!(astate, pc, args) - if isa(ary, SSAValue) || isa(ary, Argument) - newary = SSAValue(pc) - aryinfo = astate.estate[ary] - newaryinfo = astate.estate[newary] - add_escape_change!(astate, newary, aryinfo) - add_escape_change!(astate, ary, newaryinfo) - end - add_liveness_changes!(astate, pc, args, 6) -end - function escape_builtin!(::typeof(Core.finalizer), astate::AnalysisState, pc::Int, args::Vector{Any}) if length(args) ≥ 3 obj = args[3] diff --git a/Compiler/src/ssair/disjoint_set.jl b/Compiler/src/ssair/disjoint_set.jl index 3f64fe643bd17..e000d7e8a582f 100644 --- a/Compiler/src/ssair/disjoint_set.jl +++ b/Compiler/src/ssair/disjoint_set.jl @@ -5,7 +5,7 @@ # imports import Base: length, eltype, union!, push! # usings -import Base: OneTo, collect, zero, zeros, one, typemax +using Base: OneTo, collect, zero, zeros, one, typemax # Disjoint-Set @@ -55,7 +55,7 @@ eltype(::Type{IntDisjointSet{T}}) where {T<:Integer} = T # path compression is implemented here function find_root_impl!(parents::Vector{T}, x::Integer) where {T<:Integer} p = parents[x] - @inbounds if parents[p] != p + @inbounds if parents[p] ≠ p parents[x] = p = _find_root_impl!(parents, p) end return p @@ -64,7 +64,7 @@ end # unsafe version of the above function _find_root_impl!(parents::Vector{T}, x::Integer) where {T<:Integer} @inbounds p = parents[x] - @inbounds if parents[p] != p + @inbounds if parents[p] ≠ p parents[x] = p = _find_root_impl!(parents, p) end return p @@ -95,7 +95,7 @@ function union!(s::IntDisjointSet{T}, x::T, y::T) where {T<:Integer} parents = s.parents xroot = find_root_impl!(parents, x) yroot = find_root_impl!(parents, y) - return xroot != yroot ? root_union!(s, xroot, yroot) : xroot + return xroot ≠ yroot ? root_union!(s, xroot, yroot) : xroot end """ diff --git a/Compiler/src/ssair/irinterp.jl b/Compiler/src/ssair/irinterp.jl index e96d27a85bc37..a4969e81828cc 100644 --- a/Compiler/src/ssair/irinterp.jl +++ b/Compiler/src/ssair/irinterp.jl @@ -249,8 +249,10 @@ function process_terminator!(@nospecialize(stmt), bb::Int, bb_ip::BitSetBoundedM return backedge elseif isa(stmt, EnterNode) dest = stmt.catch_dest - @assert dest > bb - push!(bb_ip, dest) + if dest ≠ 0 + @assert dest > bb + push!(bb_ip, dest) + end push!(bb_ip, bb+1) return false else diff --git a/Compiler/src/ssair/passes.jl b/Compiler/src/ssair/passes.jl index e61f3207fc07a..ff333b9b0a129 100644 --- a/Compiler/src/ssair/passes.jl +++ b/Compiler/src/ssair/passes.jl @@ -2393,8 +2393,10 @@ function cfg_simplify!(ir::IRCode) end elseif isa(terminator, EnterNode) catchbb = terminator.catch_dest - if bb_rename_succ[catchbb] == 0 - push!(worklist, catchbb) + if catchbb ≠ 0 + if bb_rename_succ[catchbb] == 0 + push!(worklist, catchbb) + end end elseif isa(terminator, GotoNode) || isa(terminator, ReturnNode) # No implicit fall through. Schedule from work list. diff --git a/Compiler/src/ssair/slot2ssa.jl b/Compiler/src/ssair/slot2ssa.jl index 6fc87934d3bc5..80dffdab23243 100644 --- a/Compiler/src/ssair/slot2ssa.jl +++ b/Compiler/src/ssair/slot2ssa.jl @@ -801,9 +801,11 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, sv::OptimizationState, has_pinode[id] = false enter_idx = idx while (handler = gethandler(handler_info, enter_idx)) !== nothing - (; enter_idx) = handler - leave_block = block_for_inst(cfg, (code[enter_idx]::EnterNode).catch_dest) - cidx = findfirst((; slot)::NewPhiCNode2->slot_id(slot)==id, new_phic_nodes[leave_block]) + enter_idx = get_enter_idx(handler) + enter_node = code[enter_idx]::EnterNode + leave_block = block_for_inst(cfg, enter_node.catch_dest) + cidx = findfirst((; slot)::NewPhiCNode2->slot_id(slot)==id, + new_phic_nodes[leave_block]) if cidx !== nothing node = thisdef ? UpsilonNode(thisval) : UpsilonNode() if incoming_vals[id] === UNDEF_TOKEN diff --git a/Compiler/src/ssair/verify.jl b/Compiler/src/ssair/verify.jl index 59051058e1750..072a564a31f78 100644 --- a/Compiler/src/ssair/verify.jl +++ b/Compiler/src/ssair/verify.jl @@ -363,14 +363,8 @@ function verify_ir(ir::IRCode, print::Bool=true, isforeigncall = false if isa(stmt, Expr) if stmt.head === :(=) - if stmt.args[1] isa SSAValue - @verify_error "SSAValue as assignment LHS" - raise_error() - end - if stmt.args[2] isa GlobalRef - # undefined GlobalRef as assignment RHS is OK - continue - end + @verify_error "Assignment should have been removed during SSA conversion" + raise_error() elseif stmt.head === :isdefined if length(stmt.args) > 2 || (length(stmt.args) == 2 && !isa(stmt.args[2], Bool)) @verify_error "malformed isdefined" diff --git a/Compiler/src/stmtinfo.jl b/Compiler/src/stmtinfo.jl index 830bfa02d2d99..4f55f068e9456 100644 --- a/Compiler/src/stmtinfo.jl +++ b/Compiler/src/stmtinfo.jl @@ -268,6 +268,17 @@ end add_edges_impl(edges::Vector{Any}, info::UnionSplitApplyCallInfo) = for split in info.infos; add_edges!(edges, split); end +""" + info::InvokeCICallInfo + +Represents a resolved call to `Core.invoke` targeting a `Core.CodeInstance` +""" +struct InvokeCICallInfo <: CallInfo + edge::CodeInstance +end +add_edges_impl(edges::Vector{Any}, info::InvokeCICallInfo) = + add_inlining_edge!(edges, info.edge) + """ info::InvokeCallInfo diff --git a/Compiler/src/tfuncs.jl b/Compiler/src/tfuncs.jl index af8863c1b3a3a..906bc9521adf9 100644 --- a/Compiler/src/tfuncs.jl +++ b/Compiler/src/tfuncs.jl @@ -2017,6 +2017,12 @@ function tuple_tfunc(𝕃::AbstractLattice, argtypes::Vector{Any}) return anyinfo ? PartialStruct(𝕃, typ, argtypes) : typ end +@nospecs function memorynew_tfunc(𝕃::AbstractLattice, memtype, m) + hasintersect(widenconst(m), Int) || return Bottom + return tmeet(𝕃, instanceof_tfunc(memtype, true)[1], GenericMemory) +end +add_tfunc(Core.memorynew, 2, 2, memorynew_tfunc, 10) + @nospecs function memoryrefget_tfunc(𝕃::AbstractLattice, mem, order, boundscheck) memoryref_builtin_common_errorcheck(mem, order, boundscheck) || return Bottom return memoryref_elemtype(mem) @@ -2238,13 +2244,31 @@ end return boundscheck ⊑ Bool && memtype ⊑ GenericMemoryRef && order ⊑ Symbol end +@nospecs function memorynew_nothrow(argtypes::Vector{Any}) + if !(argtypes[1] isa Const && argtypes[2] isa Const) + return false + end + MemT = argtypes[1].val + if !(isconcretetype(MemT) && MemT <: GenericMemory) + return false + end + len = argtypes[2].val + if !(len isa Int && 0 <= len < typemax(Int)) + return false + end + elsz = datatype_layoutsize(MemT) + overflows = checked_smul_int(len, elsz)[2] + return !overflows +end # Query whether the given builtin is guaranteed not to throw given the `argtypes`. # `argtypes` can be assumed not to contain varargs. function _builtin_nothrow(𝕃::AbstractLattice, @nospecialize(f::Builtin), argtypes::Vector{Any}, @nospecialize(rt)) ⊑ = partialorder(𝕃) na = length(argtypes) - if f === memoryrefnew + if f === Core.memorynew + return memorynew_nothrow(argtypes) + elseif f === memoryrefnew return memoryref_builtin_common_nothrow(argtypes) elseif f === memoryrefoffset length(argtypes) == 1 || return false @@ -2347,6 +2371,7 @@ const _EFFECT_FREE_BUILTINS = [ isa, UnionAll, getfield, + Core.memorynew, memoryrefnew, memoryrefoffset, memoryrefget, @@ -2381,6 +2406,7 @@ const _INACCESSIBLEMEM_BUILTINS = Any[ compilerbarrier, Core._typevar, donotdelete, + Core.memorynew, ] const _ARGMEM_BUILTINS = Any[ @@ -2543,7 +2569,7 @@ function builtin_effects(𝕃::AbstractLattice, @nospecialize(f::Builtin), argty consistent = ALWAYS_TRUE elseif f === memoryrefget || f === memoryrefset! || f === memoryref_isassigned consistent = CONSISTENT_IF_INACCESSIBLEMEMONLY - elseif f === Core._typevar + elseif f === Core._typevar || f === Core.memorynew consistent = CONSISTENT_IF_NOTRETURNED else consistent = ALWAYS_FALSE diff --git a/Compiler/src/typeinfer.jl b/Compiler/src/typeinfer.jl index 83ec0271ea474..20c0a5000bd39 100644 --- a/Compiler/src/typeinfer.jl +++ b/Compiler/src/typeinfer.jl @@ -413,6 +413,7 @@ function finishinfer!(me::InferenceState, interp::AbstractInterpreter) ipo_effects = result.ipo_effects = me.ipo_effects = adjust_effects(me) result.exc_result = me.exc_bestguess = refine_exception_type(me.exc_bestguess, ipo_effects) me.src.rettype = widenconst(ignorelimited(bestguess)) + me.src.ssaflags = me.ssaflags me.src.min_world = first(me.world.valid_worlds) me.src.max_world = last(me.world.valid_worlds) istoplevel = !(me.linfo.def isa Method) @@ -936,7 +937,7 @@ function codeinfo_for_const(interp::AbstractInterpreter, mi::MethodInstance, @no tree.slotflags = fill(0x00, nargs) tree.ssavaluetypes = 1 tree.debuginfo = DebugInfo(mi) - tree.ssaflags = UInt32[0] + tree.ssaflags = [IR_FLAG_NULL] tree.rettype = Core.Typeof(val) tree.edges = Core.svec() set_inlineable!(tree, true) diff --git a/Compiler/src/utilities.jl b/Compiler/src/utilities.jl index 11d926f0c9d4e..196722f8bca33 100644 --- a/Compiler/src/utilities.jl +++ b/Compiler/src/utilities.jl @@ -12,25 +12,6 @@ if !@isdefined(var"@timeit") end end -# avoid cycle due to over-specializing `any` when used by inference -function _any(@nospecialize(f), a) - for x in a - f(x) && return true - end - return false -end -any(@nospecialize(f), itr) = _any(f, itr) -any(itr) = _any(identity, itr) - -function _all(@nospecialize(f), a) - for x in a - f(x) || return false - end - return true -end -all(@nospecialize(f), itr) = _all(f, itr) -all(itr) = _all(identity, itr) - function contains_is(itr, @nospecialize(x)) for y in itr if y === x @@ -54,8 +35,8 @@ function count_const_size(@nospecialize(x), count_self::Bool = true) # No definite size (isa(x, GenericMemory) || isa(x, String) || isa(x, SimpleVector)) && return MAX_INLINE_CONST_SIZE + 1 - if isa(x, Module) - # We allow modules, because we already assume they are externally + if isa(x, Module) || isa(x, Method) || isa(x, CodeInstance) + # We allow modules, methods and CodeInstance, because we already assume they are externally # rooted, so we count their contents as 0 size. return sizeof(Ptr{Cvoid}) end diff --git a/Compiler/test/EAUtils.jl b/Compiler/test/EAUtils.jl index f124aea2544fd..64d2a62db8622 100644 --- a/Compiler/test/EAUtils.jl +++ b/Compiler/test/EAUtils.jl @@ -14,27 +14,11 @@ import .Compiler: AbstractInterpreter, NativeInterpreter, WorldView, WorldRange, InferenceParams, OptimizationParams, get_world_counter, get_inference_cache, ipo_dataflow_analysis! # usings -using Core: - CodeInstance, MethodInstance, CodeInfo -using .Compiler: - InferenceResult, InferenceState, OptimizationState, IRCode +using Core.IR +using .Compiler: InferenceResult, InferenceState, OptimizationState, IRCode using .EA: analyze_escapes, ArgEscapeCache, ArgEscapeInfo, EscapeInfo, EscapeState -struct EAToken end - -# when working outside of CC, -# cache entire escape state for later inspection and debugging -struct EscapeCacheInfo - argescapes::ArgEscapeCache - state::EscapeState # preserved just for debugging purpose - ir::IRCode # preserved just for debugging purpose -end - -struct EscapeCache - cache::IdDict{MethodInstance,EscapeCacheInfo} # TODO(aviatesk) Should this be CodeInstance to EscapeCacheInfo? -end -EscapeCache() = EscapeCache(IdDict{MethodInstance,EscapeCacheInfo}()) -const GLOBAL_ESCAPE_CACHE = EscapeCache() +mutable struct EscapeAnalyzerCacheToken end struct EscapeResultForEntry ir::IRCode @@ -47,15 +31,15 @@ mutable struct EscapeAnalyzer <: AbstractInterpreter const inf_params::InferenceParams const opt_params::OptimizationParams const inf_cache::Vector{InferenceResult} - const escape_cache::EscapeCache + const token::EscapeAnalyzerCacheToken const entry_mi::Union{Nothing,MethodInstance} result::EscapeResultForEntry - function EscapeAnalyzer(world::UInt, escape_cache::EscapeCache; + function EscapeAnalyzer(world::UInt, cache_token::EscapeAnalyzerCacheToken; entry_mi::Union{Nothing,MethodInstance}=nothing) inf_params = InferenceParams() opt_params = OptimizationParams() inf_cache = InferenceResult[] - return new(world, inf_params, opt_params, inf_cache, escape_cache, entry_mi) + return new(world, inf_params, opt_params, inf_cache, cache_token, entry_mi) end end @@ -63,20 +47,19 @@ Compiler.InferenceParams(interp::EscapeAnalyzer) = interp.inf_params Compiler.OptimizationParams(interp::EscapeAnalyzer) = interp.opt_params Compiler.get_inference_world(interp::EscapeAnalyzer) = interp.world Compiler.get_inference_cache(interp::EscapeAnalyzer) = interp.inf_cache -Compiler.cache_owner(::EscapeAnalyzer) = EAToken() -Compiler.get_escape_cache(interp::EscapeAnalyzer) = GetEscapeCache(interp) +Compiler.cache_owner(interp::EscapeAnalyzer) = interp.token +Compiler.get_escape_cache(::EscapeAnalyzer) = GetEscapeCache() function Compiler.ipo_dataflow_analysis!(interp::EscapeAnalyzer, opt::OptimizationState, - ir::IRCode, caller::InferenceResult) + ir::IRCode, caller::InferenceResult) # run EA on all frames that have been optimized nargs = Int(opt.src.nargs) 𝕃ₒ = Compiler.optimizer_lattice(interp) - get_escape_cache = GetEscapeCache(interp) estate = try - analyze_escapes(ir, nargs, 𝕃ₒ, get_escape_cache) + analyze_escapes(ir, nargs, 𝕃ₒ, GetEscapeCache()) catch err @error "error happened within EA, inspect `Main.failedanalysis`" - failedanalysis = FailedAnalysis(caller, ir, nargs, get_escape_cache) + failedanalysis = FailedAnalysis(caller, ir, nargs) Core.eval(Main, :(failedanalysis = $failedanalysis)) rethrow(err) end @@ -84,25 +67,31 @@ function Compiler.ipo_dataflow_analysis!(interp::EscapeAnalyzer, opt::Optimizati # return back the result interp.result = EscapeResultForEntry(Compiler.copy(ir), estate, caller.linfo) end - record_escapes!(interp, caller, estate, ir) + record_escapes!(caller, estate, ir) @invoke Compiler.ipo_dataflow_analysis!(interp::AbstractInterpreter, opt::OptimizationState, - ir::IRCode, caller::InferenceResult) + ir::IRCode, caller::InferenceResult) end -function record_escapes!(interp::EscapeAnalyzer, - caller::InferenceResult, estate::EscapeState, ir::IRCode) +# cache entire escape state for inspection and debugging +struct EscapeCacheInfo + argescapes::ArgEscapeCache + state::EscapeState # preserved just for debugging purpose + ir::IRCode # preserved just for debugging purpose +end + +function record_escapes!(caller::InferenceResult, estate::EscapeState, ir::IRCode) argescapes = ArgEscapeCache(estate) ecacheinfo = EscapeCacheInfo(argescapes, estate, ir) return Compiler.stack_analysis_result!(caller, ecacheinfo) end -struct GetEscapeCache - escape_cache::EscapeCache - GetEscapeCache(interp::EscapeAnalyzer) = new(interp.escape_cache) -end -function ((; escape_cache)::GetEscapeCache)(mi::MethodInstance) - ecacheinfo = get(escape_cache.cache, mi, nothing) +struct GetEscapeCache end +function (::GetEscapeCache)(codeinst::Union{CodeInstance,MethodInstance}) + codeinst isa CodeInstance || return false + ecacheinfo = Compiler.traverse_analysis_results(codeinst) do @nospecialize result + return result isa EscapeCacheInfo ? result : nothing + end return ecacheinfo === nothing ? false : ecacheinfo.argescapes end @@ -110,15 +99,6 @@ struct FailedAnalysis caller::InferenceResult ir::IRCode nargs::Int - get_escape_cache::GetEscapeCache -end - -function Compiler.finish!(interp::EscapeAnalyzer, state::InferenceState; can_discard_trees::Bool=Compiler.may_discard_trees(interp)) - ecacheinfo = Compiler.traverse_analysis_results(state.result) do @nospecialize result - return result isa EscapeCacheInfo ? result : nothing - end - ecacheinfo isa EscapeCacheInfo && (interp.escape_cache.cache[state.linfo] = ecacheinfo) - return @invoke Compiler.finish!(interp::AbstractInterpreter, state::InferenceState; can_discard_trees) end # printing @@ -313,23 +293,29 @@ while caching the analysis results. - `world::UInt = Base.get_world_counter()`: controls the world age to use when looking up methods, use current world age if not specified. -- `interp::EscapeAnalyzer = EscapeAnalyzer(world)`: - specifies the escape analyzer to use, by default a new analyzer with the global cache is created. +- `cache_token::EscapeAnalyzerCacheToken = EscapeAnalyzerCacheToken()`: + specifies the cache token to use, by default a new token is generated to ensure + that `code_escapes` uses a fresh cache and performs a new analysis on each invocation. + If you with to perform analysis with the global cache enabled, specify a particular token instance. +- `interp::EscapeAnalyzer = EscapeAnalyzer(world, cache_token)`: + specifies the escape analyzer to use. - `debuginfo::Symbol = :none`: controls the amount of code metadata present in the output, possible options are `:none` or `:source`. """ function code_escapes(@nospecialize(f), @nospecialize(types=Base.default_tt(f)); world::UInt = get_world_counter(), + cache_token::EscapeAnalyzerCacheToken = EscapeAnalyzerCacheToken(), debuginfo::Symbol = :none) tt = Base.signature_type(f, types) match = Base._which(tt; world, raise=true) mi = Compiler.specialize_method(match) - return code_escapes(mi; world, debuginfo) + return code_escapes(mi; world, cache_token, debuginfo) end function code_escapes(mi::MethodInstance; world::UInt = get_world_counter(), - interp::EscapeAnalyzer=EscapeAnalyzer(world, GLOBAL_ESCAPE_CACHE; entry_mi=mi), + cache_token::EscapeAnalyzerCacheToken = EscapeAnalyzerCacheToken(), + interp::EscapeAnalyzer=EscapeAnalyzer(world, cache_token; entry_mi=mi), debuginfo::Symbol = :none) frame = Compiler.typeinf_frame(interp, mi, #=run_optimizer=#true) isdefined(interp, :result) || error("optimization didn't happen: maybe everything has been constant folded?") @@ -351,12 +337,17 @@ Note that this version does not cache the analysis results. - `world::UInt = Base.get_world_counter()`: controls the world age to use when looking up methods, use current world age if not specified. -- `interp::AbstractInterpreter = EscapeAnalyzer(world, EscapeCache())`: +- `cache_token::EscapeAnalyzerCacheToken = EscapeAnalyzerCacheToken()`: + specifies the cache token to use, by default a new token is generated to ensure + that `code_escapes` uses a fresh cache and performs a new analysis on each invocation. + If you with to perform analysis with the global cache enabled, specify a particular token instance. +- `interp::AbstractInterpreter = EscapeAnalyzer(world, cache_token)`: specifies the abstract interpreter to use, by default a new `EscapeAnalyzer` with an empty cache is created. """ function code_escapes(ir::IRCode, nargs::Int; world::UInt = get_world_counter(), - interp::AbstractInterpreter=EscapeAnalyzer(world, EscapeCache())) + cache_token::EscapeAnalyzerCacheToken = EscapeAnalyzerCacheToken(), + interp::AbstractInterpreter=EscapeAnalyzer(world, cache_token)) estate = analyze_escapes(ir, nargs, Compiler.optimizer_lattice(interp), Compiler.get_escape_cache(interp)) return EscapeResult(ir, estate) # return back the result end diff --git a/Compiler/test/EscapeAnalysis.jl b/Compiler/test/EscapeAnalysis.jl index 1831bd355cd48..60364769c95a8 100644 --- a/Compiler/test/EscapeAnalysis.jl +++ b/Compiler/test/EscapeAnalysis.jl @@ -34,24 +34,9 @@ let utils_ex = quote Core.eval(@__MODULE__, utils_ex) end -using .EscapeAnalysis: - EscapeInfo, IndexableElements, IndexableFields, normalize +using .EscapeAnalysis: EscapeInfo, IndexableFields isϕ(@nospecialize x) = isa(x, Core.PhiNode) -function with_normalized_name(@nospecialize(f), @nospecialize(x)) - if Meta.isexpr(x, :foreigncall) - name = x.args[1] - nn = normalize(name) - return isa(nn, Symbol) && f(nn) - end - return false -end -isarrayalloc(@nospecialize x) = - with_normalized_name(nn::Symbol->false, x) -isarrayresize(@nospecialize x) = - with_normalized_name(nn::Symbol->false, x) -isarraycopy(@nospecialize x) = - with_normalized_name(nn::Symbol->false, x) """ is_load_forwardable(x::EscapeInfo) -> Bool @@ -61,7 +46,7 @@ function is_load_forwardable(x::EscapeInfo) AliasInfo = x.AliasInfo # NOTE technically we also need to check `!has_thrown_escape(x)` here as well, # but we can also do equivalent check during forwarding - return isa(AliasInfo, IndexableFields) || isa(AliasInfo, IndexableElements) + return isa(AliasInfo, IndexableFields) end @testset "EAUtils" begin @@ -1501,585 +1486,6 @@ let result = @code_escapes compute!(MPoint(1+.5im, 2+.5im), MPoint(2+.25im, 4+.7 end end -@testset "array primitives" begin - # arrayref - @test_skip let result = code_escapes((Vector{String},Int)) do xs, i - s = Base.arrayref(true, xs, i) - return s - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - @test has_return_escape(result.state[Argument(2)], r) # xs - @test has_thrown_escape(result.state[Argument(2)]) # xs - @test !has_return_escape(result.state[Argument(3)], r) # i - end - @test_skip let result = code_escapes((Vector{String},Int)) do xs, i - s = Base.arrayref(false, xs, i) - return s - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - @test has_return_escape(result.state[Argument(2)], r) # xs - @test !has_thrown_escape(result.state[Argument(2)]) # xs - @test !has_return_escape(result.state[Argument(3)], r) # i - end - @test_skip let result = code_escapes((Vector{String},Bool)) do xs, i - c = Base.arrayref(true, xs, i) # TypeError will happen here - return c - end - t = only(findall(iscall((result.ir, Base.arrayref)), result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[Argument(2)], t) # xs - end - @test_skip let result = code_escapes((String,Int)) do xs, i - c = Base.arrayref(true, xs, i) # TypeError will happen here - return c - end - t = only(findall(iscall((result.ir, Base.arrayref)), result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[Argument(2)], t) # xs - end - @test_skip let result = code_escapes((AbstractVector{String},Int)) do xs, i - c = Base.arrayref(true, xs, i) # TypeError may happen here - return c - end - t = only(findall(iscall((result.ir, Base.arrayref)), result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[Argument(2)], t) # xs - end - @test_skip let result = code_escapes((Vector{String},Any)) do xs, i - c = Base.arrayref(true, xs, i) # TypeError may happen here - return c - end - t = only(findall(iscall((result.ir, Base.arrayref)), result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[Argument(2)], t) # xs - end - - # arrayset - @test_skip let result = code_escapes((Vector{String},String,Int,)) do xs, x, i - Base.arrayset(true, xs, x, i) - return xs - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - @test has_return_escape(result.state[Argument(2)], r) # xs - @test has_thrown_escape(result.state[Argument(2)]) # xs - @test has_return_escape(result.state[Argument(3)], r) # x - end - @test_skip let result = code_escapes((Vector{String},String,Int,)) do xs, x, i - Base.arrayset(false, xs, x, i) - return xs - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - @test has_return_escape(result.state[Argument(2)], r) # xs - @test !has_thrown_escape(result.state[Argument(2)]) # xs - @test has_return_escape(result.state[Argument(3)], r) # x - end - @test_skip let result = code_escapes((String,String,String,)) do s, t, u - xs = Vector{String}(undef, 3) - Base.arrayset(true, xs, s, 1) - Base.arrayset(true, xs, t, 2) - Base.arrayset(true, xs, u, 3) - return xs - end - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - r = only(findall(isreturn, result.ir.stmts.stmt)) - @test has_return_escape(result.state[SSAValue(i)], r) - for i in 2:result.state.nargs - @test has_return_escape(result.state[Argument(i)], r) - end - end - @test_skip let result = code_escapes((Vector{String},String,Bool,)) do xs, x, i - Base.arrayset(true, xs, x, i) # TypeError will happen here - return xs - end - t = only(findall(iscall((result.ir, Base.arrayset)), result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[Argument(2)], t) # xs - @test has_thrown_escape(result.state[Argument(3)], t) # x - end - @test_skip let result = code_escapes((String,String,Int,)) do xs, x, i - Base.arrayset(true, xs, x, i) # TypeError will happen here - return xs - end - t = only(findall(iscall((result.ir, Base.arrayset)), result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[Argument(2)], t) # xs::String - @test has_thrown_escape(result.state[Argument(3)], t) # x::String - end - @test_skip let result = code_escapes((AbstractVector{String},String,Int,)) do xs, x, i - Base.arrayset(true, xs, x, i) # TypeError may happen here - return xs - end - t = only(findall(iscall((result.ir, Base.arrayset)), result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[Argument(2)], t) # xs - @test has_thrown_escape(result.state[Argument(3)], t) # x - end - @test_skip let result = code_escapes((Vector{String},AbstractString,Int,)) do xs, x, i - Base.arrayset(true, xs, x, i) # TypeError may happen here - return xs - end - t = only(findall(iscall((result.ir, Base.arrayset)), result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[Argument(2)], t) # xs - @test has_thrown_escape(result.state[Argument(3)], t) # x - end - - # arrayref and arrayset - @test_skip let result = code_escapes() do - a = Vector{Vector{Any}}(undef, 1) - b = Any[] - a[1] = b - return a[1] - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - ai = only(findall(result.ir.stmts.stmt) do @nospecialize x - isarrayalloc(x) && x.args[2] === Vector{Vector{Any}} - end) - bi = only(findall(result.ir.stmts.stmt) do @nospecialize x - isarrayalloc(x) && x.args[2] === Vector{Any} - end) - @test !has_return_escape(result.state[SSAValue(ai)], r) - @test has_return_escape(result.state[SSAValue(bi)], r) - end - @test_skip let result = code_escapes() do - a = Vector{Vector{Any}}(undef, 1) - b = Any[] - a[1] = b - return a - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - ai = only(findall(result.ir.stmts.stmt) do @nospecialize x - isarrayalloc(x) && x.args[2] === Vector{Vector{Any}} - end) - bi = only(findall(result.ir.stmts.stmt) do @nospecialize x - isarrayalloc(x) && x.args[2] === Vector{Any} - end) - @test has_return_escape(result.state[SSAValue(ai)], r) - @test has_return_escape(result.state[SSAValue(bi)], r) - end - @test_skip let result = code_escapes((Vector{Any},String,Int,Int)) do xs, s, i, j - x = SafeRef(s) - xs[i] = x - xs[j] # potential error - end - i = only(findall(isnew, result.ir.stmts.stmt)) - t = only(findall(iscall((result.ir, Base.arrayref)), result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[Argument(3)], t) # s - @test has_thrown_escape(result.state[SSAValue(i)], t) # x - end - - # arraysize - @test_skip let result = code_escapes((Vector{Any},)) do xs - Core.arraysize(xs, 1) - end - t = only(findall(iscall((result.ir, Core.arraysize)), result.ir.stmts.stmt)) - @test !has_thrown_escape(result.state[Argument(2)], t) - end - @test_skip let result = code_escapes((Vector{Any},Int,)) do xs, dim - Core.arraysize(xs, dim) - end - t = only(findall(iscall((result.ir, Core.arraysize)), result.ir.stmts.stmt)) - @test !has_thrown_escape(result.state[Argument(2)], t) - end - @test_skip let result = code_escapes((Any,)) do xs - Core.arraysize(xs, 1) - end - t = only(findall(iscall((result.ir, Core.arraysize)), result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[Argument(2)], t) - end - - # arraylen - @test_skip let result = code_escapes((Vector{Any},)) do xs - Base.arraylen(xs) - end - t = only(findall(iscall((result.ir, Base.arraylen)), result.ir.stmts.stmt)) - @test !has_thrown_escape(result.state[Argument(2)], t) # xs - end - @test_skip let result = code_escapes((String,)) do xs - Base.arraylen(xs) - end - t = only(findall(iscall((result.ir, Base.arraylen)), result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[Argument(2)], t) # xs - end - @test_skip let result = code_escapes((Vector{Any},)) do xs - Base.arraylen(xs, 1) - end - t = only(findall(iscall((result.ir, Base.arraylen)), result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[Argument(2)], t) # xs - end - - # array resizing - # without BoundsErrors - @test_skip let result = code_escapes((Vector{Any},String)) do xs, x - @ccall jl_array_grow_beg(xs::Any, 2::UInt)::Cvoid - xs[1] = x - xs - end - t = only(findall(isarrayresize, result.ir.stmts.stmt)) - @test !has_thrown_escape(result.state[Argument(2)], t) # xs - @test !has_thrown_escape(result.state[Argument(3)], t) # x - end - @test_skip let result = code_escapes((Vector{Any},String)) do xs, x - @ccall jl_array_grow_end(xs::Any, 2::UInt)::Cvoid - xs[1] = x - xs - end - t = only(findall(isarrayresize, result.ir.stmts.stmt)) - @test !has_thrown_escape(result.state[Argument(2)], t) # xs - @test !has_thrown_escape(result.state[Argument(3)], t) # x - end - # with possible BoundsErrors - @test_skip let result = code_escapes((String,)) do x - xs = Any[1,2,3] - xs[3] = x - @ccall jl_array_del_beg(xs::Any, 2::UInt)::Cvoid # can potentially throw - xs - end - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - t = only(findall(isarrayresize, result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[SSAValue(i)], t) # xs - @test has_thrown_escape(result.state[Argument(2)], t) # x - end - @test_skip let result = code_escapes((String,)) do x - xs = Any[1,2,3] - xs[1] = x - @ccall jl_array_del_end(xs::Any, 2::UInt)::Cvoid # can potentially throw - xs - end - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - t = only(findall(isarrayresize, result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[SSAValue(i)], t) # xs - @test has_thrown_escape(result.state[Argument(2)], t) # x - end - @test_skip let result = code_escapes((String,)) do x - xs = Any[x] - @ccall jl_array_grow_at(xs::Any, 1::UInt, 2::UInt)::Cvoid # can potentially throw - end - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - t = only(findall(isarrayresize, result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[SSAValue(i)], t) # xs - @test has_thrown_escape(result.state[Argument(2)], t) # x - end - @test_skip let result = code_escapes((String,)) do x - xs = Any[x] - @ccall jl_array_del_at(xs::Any, 1::UInt, 2::UInt)::Cvoid # can potentially throw - end - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - t = only(findall(isarrayresize, result.ir.stmts.stmt)) - @test has_thrown_escape(result.state[SSAValue(i)], t) # xs - @test has_thrown_escape(result.state[Argument(2)], t) # x - end - - # array copy - @test_skip let result = code_escapes((Vector{Any},)) do xs - return copy(xs) - end - i = only(findall(isarraycopy, result.ir.stmts.stmt)) - r = only(findall(isreturn, result.ir.stmts.stmt)) - @test has_return_escape(result.state[SSAValue(i)], r) - @test !has_return_escape(result.state[Argument(2)], r) - end - @test_skip let result = code_escapes((String,)) do s - xs = String[s] - xs′ = copy(xs) - return xs′[1] - end - i1 = only(findall(isarrayalloc, result.ir.stmts.stmt)) - i2 = only(findall(isarraycopy, result.ir.stmts.stmt)) - r = only(findall(isreturn, result.ir.stmts.stmt)) - @test !has_return_escape(result.state[SSAValue(i1)]) - @test !has_return_escape(result.state[SSAValue(i2)]) - @test has_return_escape(result.state[Argument(2)], r) # s - end - @test_skip let result = code_escapes((Vector{Any},)) do xs - xs′ = copy(xs) - return xs′[1] # may potentially throw BoundsError, should escape `xs` conservatively (i.e. escape its elements) - end - i = only(findall(isarraycopy, result.ir.stmts.stmt)) - ref = only(findall(iscall((result.ir, Base.arrayref)), result.ir.stmts.stmt)) - ret = only(findall(isreturn, result.ir.stmts.stmt)) - @test_broken !has_thrown_escape(result.state[SSAValue(i)], ref) - @test_broken !has_return_escape(result.state[SSAValue(i)], ret) - @test has_thrown_escape(result.state[Argument(2)], ref) - @test has_return_escape(result.state[Argument(2)], ret) - end - @test_skip let result = code_escapes((String,)) do s - xs = Vector{String}(undef, 1) - xs[1] = s - xs′ = copy(xs) - length(xs′) > 2 && throw(xs′) - return xs′ - end - i1 = only(findall(isarrayalloc, result.ir.stmts.stmt)) - i2 = only(findall(isarraycopy, result.ir.stmts.stmt)) - t = only(findall(iscall((result.ir, throw)), result.ir.stmts.stmt)) - r = only(findall(isreturn, result.ir.stmts.stmt)) - @test_broken !has_thrown_escape(result.state[SSAValue(i1)], t) - @test_broken !has_return_escape(result.state[SSAValue(i1)], r) - @test has_thrown_escape(result.state[SSAValue(i2)], t) - @test has_return_escape(result.state[SSAValue(i2)], r) - @test has_thrown_escape(result.state[Argument(2)], t) - @test has_return_escape(result.state[Argument(2)], r) - end - - # isassigned - let result = code_escapes((Vector{Any},Int)) do xs, i - return isassigned(xs, i) - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - @test_broken !has_return_escape(result.state[Argument(2)], r) - @test_broken !has_thrown_escape(result.state[Argument(2)]) - end - - # indexing analysis - # ----------------- - - # safe case - @test_skip let result = code_escapes((String,String)) do s, t - a = Vector{Any}(undef, 2) - a[1] = s - a[2] = t - return a[1] - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - @test !has_return_escape(result.state[SSAValue(i)], r) - @test is_load_forwardable(result.state[SSAValue(i)]) - @test has_return_escape(result.state[Argument(2)], r) # s - @test !has_return_escape(result.state[Argument(3)], r) # t - end - @test_skip let result = code_escapes((String,String)) do s, t - a = Matrix{Any}(undef, 1, 2) - a[1, 1] = s - a[1, 2] = t - return a[1, 1] - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - @test !has_return_escape(result.state[SSAValue(i)], r) - @test is_load_forwardable(result.state[SSAValue(i)]) - @test has_return_escape(result.state[Argument(2)], r) # s - @test !has_return_escape(result.state[Argument(3)], r) # t - end - @test_skip let result = code_escapes((Bool,String,String,String)) do c, s, t, u - a = Vector{Any}(undef, 2) - if c - a[1] = s - a[2] = u - else - a[1] = t - a[2] = u - end - return a[1] - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - @test is_load_forwardable(result.state[SSAValue(i)]) - @test !has_return_escape(result.state[SSAValue(i)], r) - @test has_return_escape(result.state[Argument(3)], r) # s - @test has_return_escape(result.state[Argument(4)], r) # t - @test !has_return_escape(result.state[Argument(5)], r) # u - end - @test_skip let result = code_escapes((Bool,String,String,String)) do c, s, t, u - a = Any[nothing, nothing] # TODO how to deal with loop indexing? - if c - a[1] = s - a[2] = u - else - a[1] = t - a[2] = u - end - return a[1] - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - @test !has_return_escape(result.state[SSAValue(i)], r) - @test_broken is_load_forwardable(result.state[SSAValue(i)]) - @test has_return_escape(result.state[Argument(3)], r) # s - @test has_return_escape(result.state[Argument(4)], r) # t - @test_broken !has_return_escape(result.state[Argument(5)], r) # u - end - @test_skip let result = code_escapes((String,)) do s - a = Vector{Vector{Any}}(undef, 1) - b = Any[s] - a[1] = b - return a[1][1] - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - is = findall(isarrayalloc, result.ir.stmts.stmt) - @assert length(is) == 2 - ia, ib = is - @test !has_return_escape(result.state[SSAValue(ia)], r) - @test is_load_forwardable(result.state[SSAValue(ia)]) - @test !has_return_escape(result.state[SSAValue(ib)], r) - @test_broken is_load_forwardable(result.state[SSAValue(ib)]) - @test has_return_escape(result.state[Argument(2)], r) # s - end - @test_skip let result = code_escapes((Bool,String,String,Regex,Regex,)) do c, s1, s2, t1, t2 - if c - a = Vector{String}(undef, 2) - a[1] = s1 - a[2] = s2 - else - a = Vector{Regex}(undef, 2) - a[1] = t1 - a[2] = t2 - end - return a[1] - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - for i in findall(isarrayalloc, result.ir.stmts.stmt) - @test !has_return_escape(result.state[SSAValue(i)], r) - @test is_load_forwardable(result.state[SSAValue(i)]) - end - @test has_return_escape(result.state[Argument(3)], r) # s1 - @test !has_return_escape(result.state[Argument(4)], r) # s2 - @test has_return_escape(result.state[Argument(5)], r) # t1 - @test !has_return_escape(result.state[Argument(6)], r) # t2 - end - @test_skip let result = code_escapes((String,String,Int)) do s, t, i - a = Any[s] - push!(a, t) - return a[2] - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - @test !has_return_escape(result.state[SSAValue(i)], r) - @test_broken is_load_forwardable(result.state[SSAValue(i)]) - @test_broken !has_return_escape(result.state[Argument(2)], r) # s - @test has_return_escape(result.state[Argument(3)], r) # t - end - # unsafe cases - @test_skip let result = code_escapes((String,String,Int)) do s, t, i - a = Vector{Any}(undef, 2) - a[1] = s - a[2] = t - return a[i] - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - @test !has_return_escape(result.state[SSAValue(i)], r) - @test !is_load_forwardable(result.state[SSAValue(i)]) - @test has_return_escape(result.state[Argument(2)], r) # s - @test has_return_escape(result.state[Argument(3)], r) # t - end - @test_skip let result = code_escapes((String,String,Int)) do s, t, i - a = Vector{Any}(undef, 2) - a[1] = s - a[i] = t - return a[1] - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - @test !has_return_escape(result.state[SSAValue(i)], r) - @test !is_load_forwardable(result.state[SSAValue(i)]) - @test has_return_escape(result.state[Argument(2)], r) # s - @test has_return_escape(result.state[Argument(3)], r) # t - end - @test_skip let result = code_escapes((String,String,Int,Int,Int)) do s, t, i, j, k - a = Vector{Any}(undef, 2) - a[3] = s # BoundsError - a[1] = t - return a[1] - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - @test !has_return_escape(result.state[SSAValue(i)], r) - @test !is_load_forwardable(result.state[SSAValue(i)]) - end - @test_skip let result = @eval Module() begin - @noinline some_resize!(a) = pushfirst!(a, nothing) - $code_escapes((String,String,Int)) do s, t, i - a = Vector{Any}(undef, 2) - a[1] = s - some_resize!(a) - return a[2] - end - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - @test_broken !has_return_escape(result.state[SSAValue(i)], r) - @test !is_load_forwardable(result.state[SSAValue(i)]) - end - - # circular reference - @test_skip let result = code_escapes() do - xs = Vector{Any}(undef, 1) - xs[1] = xs - return xs[1] - end - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - r = only(findall(isreturn, result.ir.stmts.stmt)) - @test has_return_escape(result.state[SSAValue(i)], r) - end - @test_skip let result = @eval Module() begin - const Ax = Vector{Any}(undef, 1) - Ax[1] = Ax - $code_escapes() do - xs = Ax[1]::Vector{Any} - return xs[1] - end - end - r = only(findall(isreturn, result.ir.stmts.stmt)) - for i in findall(iscall((result.ir, Core.arrayref)), result.ir.stmts.stmt) - @test has_return_escape(result.state[SSAValue(i)], r) - end - end - let result = @eval Module() begin - @noinline function genxs() - xs = Vector{Any}(undef, 1) - xs[1] = xs - return xs - end - $code_escapes() do - xs = genxs() - return xs[1] - end - end - i = only(findall(isinvoke(:genxs), result.ir.stmts.stmt)) - r = only(findall(isreturn, result.ir.stmts.stmt)) - @test has_return_escape(result.state[SSAValue(i)], r) - end -end - -# demonstrate array primitive support with a realistic end to end example -@test_skip let result = code_escapes((Int,String,)) do n,s - xs = String[] - for i in 1:n - push!(xs, s) - end - xs - end - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - r = only(findall(isreturn, result.ir.stmts.stmt)) - @test has_return_escape(result.state[SSAValue(i)], r) - @test !has_thrown_escape(result.state[SSAValue(i)]) - @test has_return_escape(result.state[Argument(3)], r) # s - @test !has_thrown_escape(result.state[Argument(3)]) # s -end -@test_skip let result = code_escapes((Int,String,)) do n,s - xs = String[] - for i in 1:n - pushfirst!(xs, s) - end - xs - end - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - r = only(findall(isreturn, result.ir.stmts.stmt)) - @test has_return_escape(result.state[SSAValue(i)], r) # xs - @test !has_thrown_escape(result.state[SSAValue(i)]) # xs - @test has_return_escape(result.state[Argument(3)], r) # s - @test !has_thrown_escape(result.state[Argument(3)]) # s -end -@test_skip let result = code_escapes((String,String,String)) do s, t, u - xs = String[] - resize!(xs, 3) - xs[1] = s - xs[1] = t - xs[1] = u - xs - end - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - r = only(findall(isreturn, result.ir.stmts.stmt)) - @test has_return_escape(result.state[SSAValue(i)], r) - @test has_thrown_escape(result.state[SSAValue(i)]) # xs - @test has_return_escape(result.state[Argument(2)], r) # s - @test has_return_escape(result.state[Argument(3)], r) # t - @test has_return_escape(result.state[Argument(4)], r) # u -end - # demonstrate a simple type level analysis can sometimes improve the analysis accuracy # by compensating the lack of yet unimplemented analyses @testset "special-casing bitstype" begin @@ -2111,25 +1517,6 @@ end end end -# # TODO implement a finalizer elision pass -# mutable struct WithFinalizer -# v -# function WithFinalizer(v) -# x = new(v) -# f(t) = @async println("Finalizing $t.") -# return finalizer(x, x) -# end -# end -# make_m(v = 10) = MyMutable(v) -# function simple(cond) -# m = make_m() -# if cond -# # println(m.v) -# return nothing # <= insert `finalize` call here -# end -# return m -# end - # interprocedural analysis # ======================== @@ -2140,7 +1527,17 @@ let result = code_escapes() do end i = last(findall(isnew, result.ir.stmts.stmt)) @test_broken !has_return_escape(result.state[SSAValue(i)]) # TODO interprocedural alias analysis - @test !has_thrown_escape(result.state[SSAValue(i)]) + @test_broken !has_thrown_escape(result.state[SSAValue(i)]) # IDEA embed const-prop'ed `CodeInstance` for `:invoke`? +end +let result = code_escapes((Base.RefValue{Base.RefValue{String}},)) do x + out1 = broadcast_noescape2(Ref(Ref("Hi"))) + out2 = broadcast_noescape2(x) + return out1, out2 + end + i = last(findall(isnew, result.ir.stmts.stmt)) + @test_broken !has_return_escape(result.state[SSAValue(i)]) # TODO interprocedural alias analysis + @test_broken !has_thrown_escape(result.state[SSAValue(i)]) # IDEA embed const-prop'ed `CodeInstance` for `:invoke`? + @test has_thrown_escape(result.state[Argument(2)]) end @noinline allescape_argument(a) = (global GV = a) # obvious escape let result = code_escapes() do diff --git a/Compiler/test/codegen.jl b/Compiler/test/codegen.jl index b6805a77124ca..9ba268fe95be8 100644 --- a/Compiler/test/codegen.jl +++ b/Compiler/test/codegen.jl @@ -1027,3 +1027,12 @@ for a in ((@noinline Ref{Int}(2)), @test ex === a end end + +# Make sure that code that has unbound sparams works +#https://github.com/JuliaLang/julia/issues/56739 + +f56739(a) where {T} = a + +@test f56739(1) == 1 +g56739(x) = @noinline f56739(x) +@test g56739(1) == 1 diff --git a/Compiler/test/inference.jl b/Compiler/test/inference.jl index b3099897faf51..7cd6989bd2423 100644 --- a/Compiler/test/inference.jl +++ b/Compiler/test/inference.jl @@ -3031,14 +3031,13 @@ end # issue #28279 # ensure that lowering doesn't move these into statement position, which would require renumbering -using Base: +, - -function f28279(b::Bool) +@eval function f28279(b::Bool) let i = 1 - while i > b - i -= 1 + while $(>)(i, b) + i = $(-)(i, 1) end if b end - return i + 1 + return $(+)(i, 1) end end code28279 = code_lowered(f28279, (Bool,))[1].code @@ -4104,6 +4103,17 @@ end == [Union{Some{Float64}, Some{Int}, Some{UInt8}}] end return a end == Union{Int32,Int64} + + @test Base.infer_return_type((Vector{Any},)) do args + codeinst = first(args) + if codeinst isa Core.MethodInstance + mi = codeinst + else + codeinst::Core.CodeInstance + mi = codeinst.def + end + return mi + end == Core.MethodInstance end callsig_backprop_basic(::Int) = nothing @@ -4437,7 +4447,7 @@ let x = Tuple{Int,Any}[ #=20=# (0, Core.ReturnNode(Core.SlotNumber(3))) ] (;handler_at, handlers) = Compiler.compute_trycatch(last.(x)) - @test map(x->x[1] == 0 ? 0 : handlers[x[1]].enter_idx, handler_at) == first.(x) + @test map(x->x[1] == 0 ? 0 : Compiler.get_enter_idx(handlers[x[1]]), handler_at) == first.(x) end @test only(Base.return_types((Bool,)) do y @@ -6126,3 +6136,17 @@ function func_swapglobal!_must_throw(x) end @test Base.infer_return_type(func_swapglobal!_must_throw, (Int,); interp=SwapGlobalInterp()) === Union{} @test !Compiler.is_effect_free(Base.infer_effects(func_swapglobal!_must_throw, (Int,); interp=SwapGlobalInterp()) ) + +@eval get_exception() = $(Expr(:the_exception)) +@test Base.infer_return_type() do + get_exception() +end <: Any +@test @eval Base.infer_return_type((Float64,)) do x + out = $(Expr(:the_exception)) + try + out = sin(x) + catch + out = $(Expr(:the_exception)) + end + return out +end == Union{Float64,DomainError} diff --git a/Compiler/test/inline.jl b/Compiler/test/inline.jl index 46b78db3b781c..5f95fb761859e 100644 --- a/Compiler/test/inline.jl +++ b/Compiler/test/inline.jl @@ -2111,7 +2111,7 @@ for run_finalizer_escape_test in (run_finalizer_escape_test1, run_finalizer_esca global finalizer_escape::Int = 0 let src = code_typed1(run_finalizer_escape_test, Tuple{Bool, Bool}) - @test any(x->isexpr(x, :(=)), src.code) + @test any(iscall((src, Core.setglobal!)), src.code) end let diff --git a/Compiler/test/interpreter_exec.jl b/Compiler/test/interpreter_exec.jl index 4972df1a27202..b1d450f8f4286 100644 --- a/Compiler/test/interpreter_exec.jl +++ b/Compiler/test/interpreter_exec.jl @@ -23,7 +23,7 @@ let m = Meta.@lower 1 + 1 ] nstmts = length(src.code) src.ssavaluetypes = nstmts - src.ssaflags = fill(UInt8(0x00), nstmts) + src.ssaflags = fill(zero(UInt32), nstmts) src.debuginfo = Core.DebugInfo(:none) Compiler.verify_ir(Compiler.inflate_ir(src)) global test29262 = true @@ -63,7 +63,7 @@ let m = Meta.@lower 1 + 1 ] nstmts = length(src.code) src.ssavaluetypes = nstmts - src.ssaflags = fill(UInt8(0x00), nstmts) + src.ssaflags = fill(zero(UInt32), nstmts) src.debuginfo = Core.DebugInfo(:none) m.args[1] = copy(src) Compiler.verify_ir(Compiler.inflate_ir(src)) @@ -103,7 +103,7 @@ let m = Meta.@lower 1 + 1 ] nstmts = length(src.code) src.ssavaluetypes = nstmts - src.ssaflags = fill(UInt8(0x00), nstmts) + src.ssaflags = fill(zero(UInt32), nstmts) src.debuginfo = Core.DebugInfo(:none) Compiler.verify_ir(Compiler.inflate_ir(src)) global test29262 = true diff --git a/Compiler/test/irpasses.jl b/Compiler/test/irpasses.jl index 412ff3b98cb19..27b6d75f86c93 100644 --- a/Compiler/test/irpasses.jl +++ b/Compiler/test/irpasses.jl @@ -1083,12 +1083,13 @@ end # test `flags_for_effects` and DCE # ================================ -let # effect-freeness computation for array allocation +@testset "effect-freeness computation for array allocation" begin # should eliminate dead allocations good_dims = [1, 2, 3, 4, 10] Ns = [1, 2, 3, 4, 10] - for dim = good_dims, N = Ns + Ts = Any[Int, Union{Missing,Nothing}, Nothing, Any] + @testset "$dim, $N" for dim in good_dims, N in Ns Int64(dim)^N > typemax(Int) && continue dims = ntuple(i->dim, N) @test @eval fully_eliminated() do @@ -1099,7 +1100,7 @@ let # effect-freeness computation for array allocation # shouldn't eliminate erroneous dead allocations bad_dims = [-1, typemax(Int)] - for dim in bad_dims, N in [1, 2, 3, 4, 10], T in Any[Int, Union{Missing,Nothing}, Nothing, Any] + @testset "$dim, $N, $T" for dim in bad_dims, N in Ns, T in Ts dims = ntuple(i->dim, N) @test @eval !fully_eliminated() do Array{$T,$N}(undef, $(dims...)) @@ -1715,6 +1716,12 @@ end @test scope_folding_opt() == 1 @test_broken fully_eliminated(scope_folding) @test_broken fully_eliminated(scope_folding_opt) +let ir = first(only(Base.code_ircode(scope_folding, ()))) + @test Compiler.compute_trycatch(ir) isa Compiler.HandlerInfo +end +let ir = first(only(Base.code_ircode(scope_folding_opt, ()))) + @test Compiler.compute_trycatch(ir) isa Compiler.HandlerInfo +end # Function that happened to have lots of sroa that # happened to trigger a bad case in the renamer. We @@ -1816,7 +1823,34 @@ function f53521() end end end -@test code_typed(f53521)[1][2] === Nothing +let (ir,rt) = only(Base.code_ircode(f53521, ())) + @test rt == Nothing + Compiler.verify_ir(ir) + Compiler.cfg_simplify!(ir) + Compiler.verify_ir(ir) +end + +Base.@assume_effects :foldable Base.@constprop :aggressive function f53521(x::Int, ::Int) + VALUE = ScopedValue(x) + @with VALUE => 2 begin + for i = 1 + @with VALUE => 3 begin + local v + try + v = sin(VALUE[]) + catch + v = nothing + end + return v + end + end + end +end +let (ir,rt) = only(Base.code_ircode((Int,)) do y + f53521(1, y) + end) + @test rt == Union{Nothing,Float64} +end # Test that adce_pass! sets Refined on PhiNode values let code = Any[ diff --git a/Make.inc b/Make.inc index 29512bbbe7f45..a60a95d21c3db 100644 --- a/Make.inc +++ b/Make.inc @@ -1470,7 +1470,7 @@ ifeq (,$(findstring aarch64,$(ARCH))) OSLIBS += -lgcc_s endif -OSLIBS += -Wl,--export-dynamic -Wl,--undefined-version -Wl,--version-script=$(BUILDROOT)/src/julia.expmap \ +OSLIBS += -Wl,--export-dynamic -Wl,--version-script=$(BUILDROOT)/src/julia.expmap \ $(NO_WHOLE_ARCHIVE) endif diff --git a/NEWS.md b/NEWS.md index 535d14208f0b8..3c893566c7219 100644 --- a/NEWS.md +++ b/NEWS.md @@ -17,6 +17,11 @@ New language features - atomic set once (`@atomiconce v[3] = 2`), - atomic swap (`x = @atomicswap v[3] = 2`), and - atomic replace (`x = @atomicreplace v[3] 2=>5`). +- New option `--task-metrics=yes` to enable the collection of per-task timing information, + which can also be enabled/disabled at runtime with `Base.Experimental.task_metrics(::Bool)`. ([#56320]) + The available metrics are: + - actual running time for the task (`Base.Experimental.task_running_time_ns`), and + - wall-time for the task (`Base.Experimental.task_wall_time_ns`). Language changes ---------------- @@ -31,7 +36,7 @@ Language changes may pave the way for inference to be able to intelligently re-use the old results, once the new method is deleted. ([#53415]) - - Macro expansion will no longer eagerly recurse into into `Expr(:toplevel)` + - Macro expansion will no longer eagerly recurse into `Expr(:toplevel)` expressions returned from macros. Instead, macro expansion of `:toplevel` expressions will be delayed until evaluation time. This allows a later expression within a given `:toplevel` expression to make use of macros @@ -55,11 +60,11 @@ Command-line option changes --------------------------- * The `-m/--module` flag can be passed to run the `main` function inside a package with a set of arguments. - This `main` function should be declared using `@main` to indicate that it is an entry point. + This `main` function should be declared using `@main` to indicate that it is an entry point. ([#52103]) * Enabling or disabling color text in Julia can now be controlled with the [`NO_COLOR`](https://no-color.org/) or [`FORCE_COLOR`](https://force-color.org/) environment variables. These variables are also honored by Julia's build system ([#53742], [#56346]). -* `--project=@temp` starts Julia with a temporary environment. +* `--project=@temp` starts Julia with a temporary environment. ([#51149]) * New `--trace-compile-timing` option to report how long each method reported by `--trace-compile` took to compile, in ms. ([#54662]) * `--trace-compile` now prints recompiled methods in yellow or with a trailing comment if color is not supported ([#55763]) @@ -72,7 +77,7 @@ Multi-threading changes a `OncePerProcess{T}` type, which allows defining a function that should be run exactly once the first time it is called, and then always return the same result value of type `T` every subsequent time afterwards. There are also `OncePerThread{T}` and `OncePerTask{T}` types for - similar usage with threads or tasks. ([#TBD]) + similar usage with threads or tasks. ([#55793]) Build system changes -------------------- @@ -86,32 +91,15 @@ New library functions * The new `isfull(c::Channel)` function can be used to check if `put!(c, some_value)` will block. ([#53159]) * `waitany(tasks; throw=false)` and `waitall(tasks; failfast=false, throw=false)` which wait multiple tasks at once ([#53341]). * `uuid7()` creates an RFC 9652 compliant UUID with version 7 ([#54834]). -* `insertdims(array; dims)` allows to insert singleton dimensions into an array which is the inverse operation to `dropdims` +* `insertdims(array; dims)` allows to insert singleton dimensions into an array which is the inverse operation to `dropdims`. ([#45793]) * The new `Fix` type is a generalization of `Fix1/Fix2` for fixing a single argument ([#54653]). 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]). -* New function `Docs.hasdoc(module, symbol)` tells whether a name has a docstring ([#52139]). -* New function `Docs.undocumented_names(module)` returns a module's undocumented public names ([#52413]). -* Passing an `IOBuffer` as a stdout argument for `Process` spawn now works as - expected, synchronized with `wait` or `success`, so a `Base.BufferStream` is - no longer required there for correctness to avoid data races ([#52461]). -* After a process exits, `closewrite` will no longer be automatically called on - the stream passed to it. Call `wait` on the process instead to ensure the - content is fully written, then call `closewrite` manually to avoid - data-races. Or use the callback form of `open` to have all that handled - automatically. -* `@timed` now additionally returns the elapsed compilation and recompilation time ([#52889]) * `escape_string` takes additional keyword arguments `ascii=true` (to escape all non-ASCII characters) and `fullhex=true` (to require full 4/8-digit hex numbers - for u/U escapes, e.g. for C compatibility) [#55099]). -* `filter` can now act on a `NamedTuple` ([#50795]). + for u/U escapes, e.g. for C compatibility) ([#55099]). * `tempname` can now take a suffix string to allow the file name to include a suffix and include that suffix in the uniquing checking ([#53474]) * `RegexMatch` objects can now be used to construct `NamedTuple`s and `Dict`s ([#50988]) @@ -119,6 +107,10 @@ New library features * `Base.require_one_based_indexing` and `Base.has_offset_axes` are now public ([#56196]) * New `ltruncate`, `rtruncate` and `ctruncate` functions for truncating strings to text width, accounting for char widths ([#55351]) * `isless` (and thus `cmp`, sorting, etc.) is now supported for zero-dimensional `AbstractArray`s ([#55772]) +* `invoke` now supports passing a Method instead of a type signature making this interface somewhat more flexible for certain uncommon use cases ([#56692]). +* `invoke` now supports passing a CodeInstance instead of a type, which can enable +certain compiler plugin workflows ([#56660]). +* `sort` now supports `NTuple`s ([#54494]) Standard library changes ------------------------ @@ -132,7 +124,7 @@ Standard library changes * A new standard library for applying syntax highlighting to Julia code, this uses `JuliaSyntax` and `StyledStrings` to implement a `highlight` function - that creates an `AnnotatedString` with syntax highlighting applied. + that creates an `AnnotatedString` with syntax highlighting applied. ([#51810]) #### Package Manager @@ -180,6 +172,7 @@ Standard library changes in the REPL will now issue a warning the first time occurs. ([#54872]) - When an object is printed automatically (by being returned in the REPL), its display is now truncated after printing 20 KiB. This does not affect manual calls to `show`, `print`, and so forth. ([#53959]) +- Backslash completions now print the respective glyph or emoji next to each matching backslash shortcode. ([#54800]) #### SuiteSparse diff --git a/base/Base_compiler.jl b/base/Base_compiler.jl index 6014a6b7c9dd0..6582fe87e2045 100644 --- a/base/Base_compiler.jl +++ b/base/Base_compiler.jl @@ -249,6 +249,8 @@ using .Iterators: Flatten, Filter, product # for generators using .Iterators: Stateful # compat (was formerly used in reinterpretarray.jl) include("namedtuple.jl") +include("anyall.jl") + include("ordering.jl") using .Order diff --git a/base/abstractdict.jl b/base/abstractdict.jl index 85a726b4cdbf4..3be930151d4d4 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -88,8 +88,8 @@ Return an iterator over all keys in a dictionary. When the keys are stored internally in a hash table, as is the case for `Dict`, the order in which they are returned may vary. -But `keys(a)` and `values(a)` both iterate `a` and -return the elements in the same order. +But `keys(a)`, `values(a)` and `pairs(a)` all iterate `a` +and return the elements in the same order. # Examples ```jldoctest @@ -114,8 +114,8 @@ Return an iterator over all values in a collection. When the values are stored internally in a hash table, as is the case for `Dict`, the order in which they are returned may vary. -But `keys(a)` and `values(a)` both iterate `a` and -return the elements in the same order. +But `keys(a)`, `values(a)` and `pairs(a)` all iterate `a` +and return the elements in the same order. # Examples ```jldoctest @@ -138,6 +138,10 @@ values(a::AbstractDict) = ValueIterator(a) Return an iterator over `key => value` pairs for any collection that maps a set of keys to a set of values. This includes arrays, where the keys are the array indices. +When the entries are stored internally in a hash table, +as is the case for `Dict`, the order in which they are returned may vary. +But `keys(a)`, `values(a)` and `pairs(a)` all iterate `a` +and return the elements in the same order. # Examples ```jldoctest diff --git a/base/anyall.jl b/base/anyall.jl new file mode 100644 index 0000000000000..e51515bb3187d --- /dev/null +++ b/base/anyall.jl @@ -0,0 +1,231 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## all & any + +""" + any(itr) -> Bool + +Test whether any elements of a boolean collection are `true`, returning `true` as +soon as the first `true` value in `itr` is encountered (short-circuiting). To +short-circuit on `false`, use [`all`](@ref). + +If the input contains [`missing`](@ref) values, return `missing` if all non-missing +values are `false` (or equivalently, if the input contains no `true` value), following +[three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). + +See also: [`all`](@ref), [`count`](@ref), [`sum`](@ref), [`|`](@ref), [`||`](@ref). + +# Examples +```jldoctest +julia> a = [true,false,false,true] +4-element Vector{Bool}: + 1 + 0 + 0 + 1 + +julia> any(a) +true + +julia> any((println(i); v) for (i, v) in enumerate(a)) +1 +true + +julia> any([missing, true]) +true + +julia> any([false, missing]) +missing +``` +""" +any(itr) = any(identity, itr) + +""" + all(itr) -> Bool + +Test whether all elements of a boolean collection are `true`, returning `false` as +soon as the first `false` value in `itr` is encountered (short-circuiting). To +short-circuit on `true`, use [`any`](@ref). + +If the input contains [`missing`](@ref) values, return `missing` if all non-missing +values are `true` (or equivalently, if the input contains no `false` value), following +[three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). + +See also: [`all!`](@ref), [`any`](@ref), [`count`](@ref), [`&`](@ref), [`&&`](@ref), [`allunique`](@ref). + +# Examples +```jldoctest +julia> a = [true,false,false,true] +4-element Vector{Bool}: + 1 + 0 + 0 + 1 + +julia> all(a) +false + +julia> all((println(i); v) for (i, v) in enumerate(a)) +1 +2 +false + +julia> all([missing, false]) +false + +julia> all([true, missing]) +missing +``` +""" +all(itr) = all(identity, itr) + +""" + any(p, itr) -> Bool + +Determine whether predicate `p` returns `true` for any elements of `itr`, returning +`true` as soon as the first item in `itr` for which `p` returns `true` is encountered +(short-circuiting). To short-circuit on `false`, use [`all`](@ref). + +If the input contains [`missing`](@ref) values, return `missing` if all non-missing +values are `false` (or equivalently, if the input contains no `true` value), following +[three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). + +# Examples +```jldoctest +julia> any(i->(4<=i<=6), [3,5,7]) +true + +julia> any(i -> (println(i); i > 3), 1:10) +1 +2 +3 +4 +true + +julia> any(i -> i > 0, [1, missing]) +true + +julia> any(i -> i > 0, [-1, missing]) +missing + +julia> any(i -> i > 0, [-1, 0]) +false +``` +""" +any(f, itr) = _any(f, itr, :) + +for ItrT = (Tuple,Any) + # define a generic method and a specialized version for `Tuple`, + # whose method bodies are identical, while giving better effects to the later + @eval function _any(f, itr::$ItrT, ::Colon) + $(ItrT === Tuple ? :(@_terminates_locally_meta) : :nothing) + anymissing = false + for x in itr + v = f(x) + if ismissing(v) + anymissing = true + else + v && return true + end + end + return anymissing ? missing : false + end +end + +# Specialized versions of any(f, ::Tuple) +# We fall back to the for loop implementation all elements have the same type or +# if the tuple is too large. +function any(f, itr::Tuple) + if itr isa NTuple || length(itr) > 32 + return _any(f, itr, :) + end + _any_tuple(f, false, itr...) +end + +@inline function _any_tuple(f, anymissing, x, rest...) + v = f(x) + if ismissing(v) + anymissing = true + elseif v + return true + end + return _any_tuple(f, anymissing, rest...) +end +@inline _any_tuple(f, anymissing) = anymissing ? missing : false + +""" + all(p, itr) -> Bool + +Determine whether predicate `p` returns `true` for all elements of `itr`, returning +`false` as soon as the first item in `itr` for which `p` returns `false` is encountered +(short-circuiting). To short-circuit on `true`, use [`any`](@ref). + +If the input contains [`missing`](@ref) values, return `missing` if all non-missing +values are `true` (or equivalently, if the input contains no `false` value), following +[three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). + +# Examples +```jldoctest +julia> all(i->(4<=i<=6), [4,5,6]) +true + +julia> all(i -> (println(i); i < 3), 1:10) +1 +2 +3 +false + +julia> all(i -> i > 0, [1, missing]) +missing + +julia> all(i -> i > 0, [-1, missing]) +false + +julia> all(i -> i > 0, [1, 2]) +true +``` +""" +all(f, itr) = _all(f, itr, :) + +for ItrT = (Tuple,Any) + # define a generic method and a specialized version for `Tuple`, + # whose method bodies are identical, while giving better effects to the later + @eval function _all(f, itr::$ItrT, ::Colon) + $(ItrT === Tuple ? :(@_terminates_locally_meta) : :nothing) + anymissing = false + for x in itr + v = f(x) + if ismissing(v) + anymissing = true + else + v || return false + end + end + return anymissing ? missing : true + end +end + +# Specialized versions of all(f, ::Tuple), +# This is similar to any(f, ::Tuple) defined above. +function all(f, itr::Tuple) + if itr isa NTuple || length(itr) > 32 + return _all(f, itr, :) + end + _all_tuple(f, false, itr...) +end + +@inline function _all_tuple(f, anymissing, x, rest...) + v = f(x) + if ismissing(v) + anymissing = true + # this syntax allows throwing a TypeError for non-Bool, for consistency with any + elseif v + nothing + else + return false + end + return _all_tuple(f, anymissing, rest...) +end +@inline _all_tuple(f, anymissing) = anymissing ? missing : true + +all(::Tuple{Missing}) = missing diff --git a/base/boot.jl b/base/boot.jl index f66ee69780193..ed0a722c5e562 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -175,15 +175,33 @@ #end #mutable struct Task -# parent::Task +# next::Any +# queue::Any # storage::Any -# state::Symbol # donenotify::Any # result::Any -# exception::Any -# backtrace::Any # scope::Any # code::Any +# @atomic _state::UInt8 +# sticky::UInt8 +# priority::UInt16 +# @atomic _isexception::UInt8 +# pad00::UInt8 +# pad01::UInt8 +# pad02::UInt8 +# rngState0::UInt64 +# rngState1::UInt64 +# rngState2::UInt64 +# rngState3::UInt64 +# rngState4::UInt64 +# const metrics_enabled::Bool +# pad10::UInt8 +# pad11::UInt8 +# pad12::UInt8 +# @atomic first_enqueued_at::UInt64 +# @atomic last_started_running_at::UInt64 +# @atomic running_time_ns::UInt64 +# @atomic finished_at::UInt64 #end export @@ -557,12 +575,7 @@ struct UndefInitializer end const undef = UndefInitializer() # type and dimensionality specified -(self::Type{GenericMemory{kind,T,addrspace}})(::UndefInitializer, m::Int) where {T,addrspace,kind} = - if isdefined(self, :instance) && m === 0 - self.instance - else - ccall(:jl_alloc_genericmemory, Ref{GenericMemory{kind,T,addrspace}}, (Any, Int), self, m) - end +(self::Type{GenericMemory{kind,T,addrspace}})(::UndefInitializer, m::Int) where {T,addrspace,kind} = memorynew(self, m) (self::Type{GenericMemory{kind,T,addrspace}})(::UndefInitializer, d::NTuple{1,Int}) where {T,kind,addrspace} = self(undef, getfield(d,1)) # empty vector constructor (self::Type{GenericMemory{kind,T,addrspace}})() where {T,kind,addrspace} = self(undef, 0) diff --git a/base/cmd.jl b/base/cmd.jl index 84ec52f865e98..b46c8293cdf3c 100644 --- a/base/cmd.jl +++ b/base/cmd.jl @@ -491,7 +491,7 @@ end """ @cmd str -Similar to `cmd`, generate a `Cmd` from the `str` string which represents the shell command(s) to be executed. +Similar to ``` `str` ```, generate a `Cmd` from the `str` string which represents the shell command(s) to be executed. The [`Cmd`](@ref) object can be run as a process and can outlive the spawning julia process (see `Cmd` for more). # Examples diff --git a/base/deprecated.jl b/base/deprecated.jl index 84ef89e44b473..cffff05d954d1 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -4,7 +4,7 @@ # Instructions for Julia Core Developers: # 1. When making a breaking change that is known to be depnedet upon by an # important and closely coupled package, decide on a unique `change_name` -# for your PR and add it to the list below. In general, is is better to +# for your PR and add it to the list below. In general, it is better to # err on the side of caution and assign a `change_name` even if it is not # clear that it is required. `change_name`s may also be assigned after the # fact in a separate PR. (Note that this may cause packages to misbehave diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index c872244964160..cef5e12a9e0d8 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -1515,7 +1515,7 @@ kw"new" """ where -The `where` keyword creates a type that is an iterated union of other types, over all +The `where` keyword creates a [`UnionAll`](@ref) type, which may be thought of as an iterated union of other types, over all values of some variable. For example `Vector{T} where T<:Real` includes all [`Vector`](@ref)s where the element type is some kind of `Real` number. @@ -2030,21 +2030,49 @@ applicable """ invoke(f, argtypes::Type, args...; kwargs...) + invoke(f, argtypes::Method, args...; kwargs...) + invoke(f, argtypes::CodeInstance, args...; kwargs...) Invoke a method for the given generic function `f` matching the specified types `argtypes` on the specified arguments `args` and passing the keyword arguments `kwargs`. The arguments `args` must conform with the specified types in `argtypes`, i.e. conversion is not automatically performed. This method allows invoking a method other than the most specific matching method, which is useful when the behavior of a more general definition is explicitly needed (often as part of the -implementation of a more specific method of the same function). +implementation of a more specific method of the same function). However, because this means +the runtime must do more work, `invoke` is generally also slower--sometimes significantly +so--than doing normal dispatch with a regular call. -Be careful when using `invoke` for functions that you don't write. What definition is used +Be careful when using `invoke` for functions that you don't write. What definition is used for given `argtypes` is an implementation detail unless the function is explicitly states that calling with certain `argtypes` is a part of public API. For example, the change between `f1` and `f2` in the example below is usually considered compatible because the change is invisible by the caller with a normal (non-`invoke`) call. However, the change is visible if you use `invoke`. +# Passing a `Method` instead of a signature +The `argtypes` argument may be a `Method`, in which case the ordinary method table lookup is +bypassed entirely and the given method is invoked directly. Needing this feature is uncommon. +Note in particular that the specified `Method` may be entirely unreachable from ordinary dispatch +(or ordinary invoke), e.g. because it was replaced or fully covered by more specific methods. +If the method is part of the ordinary method table, this call behaves similar +to `invoke(f, method.sig, args...)`. + +!!! compat "Julia 1.12" + Passing a `Method` requires Julia 1.12. + +# Passing a `CodeInstance` instead of a signature +The `argtypes` argument may be a `CodeInstance`, bypassing both method lookup and specialization. +The semantics of this invocation are similar to a function pointer call of the `CodeInstance`'s +`invoke` pointer. It is an error to invoke a `CodeInstance` with arguments that do not match its +parent `MethodInstance` or from a world age not included in the `min_world`/`max_world` range. +It is undefined behavior to invoke a `CodeInstance` whose behavior does not match the constraints +specified in its fields. For some code instances with `owner !== nothing` (i.e. those generated +by external compilers), it may be an error to invoke them after passing through precompilation. +This is an advanced interface intended for use with external compiler plugins. + +!!! compat "Julia 1.12" + Passing a `CodeInstance` requires Julia 1.12. + # Examples ```jldoctest julia> f(x::Real) = x^2; diff --git a/base/experimental.jl b/base/experimental.jl index 31238d4015b3b..17871b4f346d6 100644 --- a/base/experimental.jl +++ b/base/experimental.jl @@ -494,4 +494,87 @@ function entrypoint(@nospecialize(argt::Type)) nothing end +""" + Base.Experimental.disable_new_worlds() + +Mark that no new worlds (methods additions, deletions, etc) are permitted to be created at +any future time, allowing for lower latencies for some operations and slightly lower memory +usage, by eliminating the tracking of those possible invalidation. +""" +disable_new_worlds() = ccall(:jl_disable_new_worlds, Cvoid, ()) + +### Task metrics + +""" + Base.Experimental.task_metrics(::Bool) + +Enable or disable the collection of per-task metrics. +A `Task` created when `Base.Experimental.task_metrics(true)` is in effect will have +[`Base.Experimental.task_running_time_ns`](@ref) and [`Base.Experimental.task_wall_time_ns`](@ref) +timing information available. + +!!! note + Task metrics can be enabled at start-up via the `--task-metrics=yes` command line option. +""" +function task_metrics(b::Bool) + if b + ccall(:jl_task_metrics_enable, Cvoid, ()) + else + ccall(:jl_task_metrics_disable, Cvoid, ()) + end + return nothing end + +""" + Base.Experimental.task_running_time_ns(t::Task) -> Union{UInt64, Nothing} + +Return the total nanoseconds that the task `t` has spent running. +This metric is only updated when `t` yields or completes unless `t` is the current task, in +which it will be updated continuously. +See also [`Base.Experimental.task_wall_time_ns`](@ref). + +Returns `nothing` if task timings are not enabled. +See [`Base.Experimental.task_metrics`](@ref). + +!!! note "This metric is from the Julia scheduler" + A task may be running on an OS thread that is descheduled by the OS + scheduler, this time still counts towards the metric. + +!!! compat "Julia 1.12" + This method was added in Julia 1.12. +""" +function task_running_time_ns(t::Task=current_task()) + t.metrics_enabled || return nothing + if t == current_task() + # These metrics fields can't update while we're running. + # But since we're running we need to include the time since we last started running! + return t.running_time_ns + (time_ns() - t.last_started_running_at) + else + return t.running_time_ns + end +end + +""" + Base.Experimental.task_wall_time_ns(t::Task) -> Union{UInt64, Nothing} + +Return the total nanoseconds that the task `t` was runnable. +This is the time since the task first entered the run queue until the time at which it +completed, or until the current time if the task has not yet completed. +See also [`Base.Experimental.task_running_time_ns`](@ref). + +Returns `nothing` if task timings are not enabled. +See [`Base.Experimental.task_metrics`](@ref). + +!!! compat "Julia 1.12" + This method was added in Julia 1.12. +""" +function task_wall_time_ns(t::Task=current_task()) + t.metrics_enabled || return nothing + start_at = t.first_enqueued_at + start_at == 0 && return UInt64(0) + end_at = t.finished_at + end_at == 0 && return time_ns() - start_at + return end_at - start_at +end + +end # module diff --git a/base/float.jl b/base/float.jl index c7230459d0822..faded5cd5978c 100644 --- a/base/float.jl +++ b/base/float.jl @@ -122,13 +122,16 @@ significand_mask(::Type{Float16}) = 0x03ff mantissa(x::T) where {T} = reinterpret(Unsigned, x) & significand_mask(T) for T in (Float16, Float32, Float64) - @eval significand_bits(::Type{$T}) = $(trailing_ones(significand_mask(T))) - @eval exponent_bits(::Type{$T}) = $(sizeof(T)*8 - significand_bits(T) - 1) - @eval exponent_bias(::Type{$T}) = $(Int(exponent_one(T) >> significand_bits(T))) + sb = trailing_ones(significand_mask(T)) + em = exponent_mask(T) + eb = Int(exponent_one(T) >> sb) + @eval significand_bits(::Type{$T}) = $(sb) + @eval exponent_bits(::Type{$T}) = $(sizeof(T)*8 - sb - 1) + @eval exponent_bias(::Type{$T}) = $(eb) # maximum float exponent - @eval exponent_max(::Type{$T}) = $(Int(exponent_mask(T) >> significand_bits(T)) - exponent_bias(T) - 1) + @eval exponent_max(::Type{$T}) = $(Int(em >> sb) - eb - 1) # maximum float exponent without bias - @eval exponent_raw_max(::Type{$T}) = $(Int(exponent_mask(T) >> significand_bits(T))) + @eval exponent_raw_max(::Type{$T}) = $(Int(em >> sb)) end """ diff --git a/base/intfuncs.jl b/base/intfuncs.jl index e8d4b65305be7..db3b1fdeeb521 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -1205,6 +1205,8 @@ julia> binomial(-5, 3) # External links * [Binomial coefficient](https://en.wikipedia.org/wiki/Binomial_coefficient) on Wikipedia. """ +binomial(n::Integer, k::Integer) = binomial(promote(n, k)...) + Base.@assume_effects :terminates_locally function binomial(n::T, k::T) where T<:Integer n0, k0 = n, k k < 0 && return zero(T) @@ -1233,7 +1235,6 @@ Base.@assume_effects :terminates_locally function binomial(n::T, k::T) where T<: end copysign(x, sgn) end -binomial(n::Integer, k::Integer) = binomial(promote(n, k)...) """ binomial(x::Number, k::Integer) diff --git a/base/iterators.jl b/base/iterators.jl index 6b8d9fe75e302..c6278e6284d70 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -15,7 +15,8 @@ using .Base: AbstractRange, AbstractUnitRange, UnitRange, LinearIndices, TupleOrBottom, (:), |, +, -, *, !==, !, ==, !=, <=, <, >, >=, =>, missing, any, _counttuple, eachindex, ntuple, zero, prod, reduce, in, firstindex, lastindex, - tail, fieldtypes, min, max, minimum, zero, oneunit, promote, promote_shape, LazyString + tail, fieldtypes, min, max, minimum, zero, oneunit, promote, promote_shape, LazyString, + afoldl using Core: @doc using .Base: @@ -1202,8 +1203,13 @@ julia> [(x,y) for x in 0:1 for y in 'a':'c'] # collects generators involving It flatten(itr) = Flatten(itr) eltype(::Type{Flatten{I}}) where {I} = eltype(eltype(I)) -eltype(::Type{Flatten{I}}) where {I<:Union{Tuple,NamedTuple}} = promote_typejoin(map(eltype, fieldtypes(I))...) -eltype(::Type{Flatten{Tuple{}}}) = eltype(Tuple{}) + +# For tuples, we statically know the element type of each index, so we can compute +# this at compile time. +function eltype(::Type{Flatten{I}}) where {I<:Union{Tuple,NamedTuple}} + afoldl((T, i) -> promote_typejoin(T, eltype(i)), Union{}, fieldtypes(I)...) +end + IteratorEltype(::Type{Flatten{I}}) where {I} = _flatteneltype(I, IteratorEltype(I)) IteratorEltype(::Type{Flatten{Tuple{}}}) = IteratorEltype(Tuple{}) _flatteneltype(I, ::HasEltype) = IteratorEltype(eltype(I)) diff --git a/base/loading.jl b/base/loading.jl index 0a70564077692..8ed43e4539c20 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1587,9 +1587,14 @@ function run_extension_callbacks(extid::ExtensionId) true catch # Try to continue loading if loading an extension errors - errs = current_exceptions() - @error "Error during loading of extension $(extid.id.name) of $(extid.parentid.name), \ + if JLOptions().incremental != 0 + # during incremental precompilation, this should be fail-fast + rethrow() + else + errs = current_exceptions() + @error "Error during loading of extension $(extid.id.name) of $(extid.parentid.name), \ use `Base.retry_load_extensions()` to retry." exception=errs + end false finally global loading_extension = false diff --git a/base/mathconstants.jl b/base/mathconstants.jl index de6b98cea634d..d26f5115b5ccb 100644 --- a/base/mathconstants.jl +++ b/base/mathconstants.jl @@ -29,7 +29,7 @@ end Base.@assume_effects :foldable function (::Type{T})(x::_KnownIrrational, r::RoundingMode) where {T<:Union{Float32,Float64}} Base._irrational_to_float(T, x, r) end -Base.@assume_effects :foldable function rationalize(::Type{T}, x::_KnownIrrational; tol::Real=0) where {T<:Integer} +Base.@assume_effects :foldable function Base.rationalize(::Type{T}, x::_KnownIrrational; tol::Real=0) where {T<:Integer} Base._rationalize_irrational(T, x, tol) end Base.@assume_effects :foldable function Base.lessrational(rx::Rational, x::_KnownIrrational) diff --git a/base/meta.jl b/base/meta.jl index bcf4fbf632ab2..0078e15bcd98b 100644 --- a/base/meta.jl +++ b/base/meta.jl @@ -363,10 +363,15 @@ function _partially_inline!(@nospecialize(x), slot_replacements::Vector{Any}, return x end if isa(x, Core.ReturnNode) + # Unreachable doesn't have val defined + if !isdefined(x, :val) + return x + else return Core.ReturnNode( _partially_inline!(x.val, slot_replacements, type_signature, static_param_values, slot_offset, statement_offset, boundscheck), ) + end end if isa(x, Core.GotoIfNot) return Core.GotoIfNot( diff --git a/base/optimized_generics.jl b/base/optimized_generics.jl index 86b54a294564d..6b1d146b6172b 100644 --- a/base/optimized_generics.jl +++ b/base/optimized_generics.jl @@ -54,4 +54,31 @@ module KeyValue function get end end +# Compiler-recognized intrinsics for compiler plugins +""" + module CompilerPlugins + +Implements a pair of functions `typeinf`/`typeinf_edge`. When the optimizer sees +a call to `typeinf`, it has license to instead call `typeinf_edge`, supplying the +current inference stack in `parent_frame` (but otherwise supplying the arguments +to `typeinf`). `typeinf_edge` will return the `CodeInstance` that `typeinf` would +have returned at runtime. The optimizer may perform a non-IPO replacement of +the call to `typeinf` by the result of `typeinf_edge`. In addition, the IPO-safe +fields of the `CodeInstance` may be propagated in IPO mode. +""" +module CompilerPlugins + """ + typeinf(owner, mi, source_mode)::CodeInstance + + Return a `CodeInstance` for the given `mi` whose valid results include at + the least current tls world and satisfies the requirements of `source_mode`. + """ + function typeinf end + + """ + typeinf_edge(owner, mi, parent_frame, world, abi_mode)::CodeInstance + """ + function typeinf_edge end +end + end diff --git a/base/options.jl b/base/options.jl index 07baa3b51f65b..7e7808bd5c047 100644 --- a/base/options.jl +++ b/base/options.jl @@ -61,6 +61,7 @@ struct JLOptions heap_size_hint::UInt64 trace_compile_timing::Int8 trim::Int8 + task_metrics::Int8 end # This runs early in the sysimage != is not defined yet diff --git a/base/pointer.jl b/base/pointer.jl index b1580ef17d562..de2f413d8f881 100644 --- a/base/pointer.jl +++ b/base/pointer.jl @@ -169,7 +169,7 @@ The `unsafe` prefix on this function indicates that no validation is performed o pointer `p` to ensure that it is valid. Like C, the programmer is responsible for ensuring that referenced memory is not freed or garbage collected while invoking this function. Incorrect usage may segfault your program. Unlike C, storing memory region allocated as -different type may be valid provided that that the types are compatible. +different type may be valid provided that the types are compatible. !!! compat "Julia 1.10" The `order` argument is available as of Julia 1.10. diff --git a/base/precompilation.jl b/base/precompilation.jl index 6eebded8b2f93..f61169c6ca16d 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -944,7 +944,7 @@ function _precompilepkgs(pkgs::Vector{String}, try # allows processes to wait if another process is precompiling a given package to # a functionally identical package cache (except for preferences, which may differ) - t = @elapsed ret = precompile_pkgs_maybe_cachefile_lock(io, print_lock, fancyprint, pkg_config, pkgspidlocked, hascolor) do + t = @elapsed ret = precompile_pkgs_maybe_cachefile_lock(io, print_lock, fancyprint, pkg_config, pkgspidlocked, hascolor, parallel_limiter) do Base.with_logger(Base.NullLogger()) do # The false here means we ignore loaded modules, so precompile for a fresh session keep_loaded_modules = false @@ -1130,7 +1130,7 @@ function _color_string(cstr::String, col::Union{Int64, Symbol}, hascolor) end # Can be merged with `maybe_cachefile_lock` in loading? -function precompile_pkgs_maybe_cachefile_lock(f, io::IO, print_lock::ReentrantLock, fancyprint::Bool, pkg_config, pkgspidlocked, hascolor) +function precompile_pkgs_maybe_cachefile_lock(f, io::IO, print_lock::ReentrantLock, fancyprint::Bool, pkg_config, pkgspidlocked, hascolor, parallel_limiter::Base.Semaphore) if !(isdefined(Base, :mkpidlock_hook) && isdefined(Base, :trymkpidlock_hook) && Base.isdefined(Base, :parse_pidfile_hook)) return f() end @@ -1153,17 +1153,22 @@ function precompile_pkgs_maybe_cachefile_lock(f, io::IO, print_lock::ReentrantLo !fancyprint && lock(print_lock) do println(io, " ", pkg.name, _color_string(" Being precompiled by $(pkgspidlocked[pkg_config])", Base.info_color(), hascolor)) end - # wait until the lock is available - @invokelatest Base.mkpidlock_hook(() -> begin - # double-check in case the other process crashed or the lock expired - if Base.isprecompiled(pkg; ignore_loaded=true, flags=cacheflags) # don't use caches for this as the env state will have changed - return nothing # returning nothing indicates a process waited for another - else - delete!(pkgspidlocked, pkg_config) - return f() # precompile - end - end, - pidfile; stale_age) + Base.release(parallel_limiter) # release so other work can be done while waiting + try + # wait until the lock is available + @invokelatest Base.mkpidlock_hook(() -> begin + # double-check in case the other process crashed or the lock expired + if Base.isprecompiled(pkg; ignore_loaded=true, flags=cacheflags) # don't use caches for this as the env state will have changed + return nothing # returning nothing indicates a process waited for another + else + delete!(pkgspidlocked, pkg_config) + Base.acquire(f, parallel_limiter) # precompile + end + end, + pidfile; stale_age) + finally + Base.acquire(parallel_limiter) # re-acquire so the outer release is balanced + end end return cachefile end diff --git a/base/range.jl b/base/range.jl index b05dddb025a7c..39428ab741955 100644 --- a/base/range.jl +++ b/base/range.jl @@ -954,7 +954,7 @@ function _getindex(v::UnitRange{T}, i::Integer) where {T<:OverflowSafe} end let BitInteger64 = Union{Int8,Int16,Int32,Int64,UInt8,UInt16,UInt32,UInt64} # for bootstrapping - function checkbounds(::Type{Bool}, v::StepRange{<:BitInteger64, <:BitInteger64}, i::BitInteger64) + global function checkbounds(::Type{Bool}, v::StepRange{<:BitInteger64, <:BitInteger64}, i::BitInteger64) res = widemul(step(v), i-oneunit(i)) + first(v) (0 < i) & ifelse(0 < step(v), res <= last(v), res >= last(v)) end diff --git a/base/reduce.jl b/base/reduce.jl index 6ceb76089d59c..25466eed4a105 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -1105,236 +1105,6 @@ julia> argmin([7, 1, 1, NaN]) """ argmin(itr) = findmin(itr)[2] -## all & any - -""" - any(itr) -> Bool - -Test whether any elements of a boolean collection are `true`, returning `true` as -soon as the first `true` value in `itr` is encountered (short-circuiting). To -short-circuit on `false`, use [`all`](@ref). - -If the input contains [`missing`](@ref) values, return `missing` if all non-missing -values are `false` (or equivalently, if the input contains no `true` value), following -[three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). - -See also: [`all`](@ref), [`count`](@ref), [`sum`](@ref), [`|`](@ref), [`||`](@ref). - -# Examples -```jldoctest -julia> a = [true,false,false,true] -4-element Vector{Bool}: - 1 - 0 - 0 - 1 - -julia> any(a) -true - -julia> any((println(i); v) for (i, v) in enumerate(a)) -1 -true - -julia> any([missing, true]) -true - -julia> any([false, missing]) -missing -``` -""" -any(itr) = any(identity, itr) - -""" - all(itr) -> Bool - -Test whether all elements of a boolean collection are `true`, returning `false` as -soon as the first `false` value in `itr` is encountered (short-circuiting). To -short-circuit on `true`, use [`any`](@ref). - -If the input contains [`missing`](@ref) values, return `missing` if all non-missing -values are `true` (or equivalently, if the input contains no `false` value), following -[three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). - -See also: [`all!`](@ref), [`any`](@ref), [`count`](@ref), [`&`](@ref), [`&&`](@ref), [`allunique`](@ref). - -# Examples -```jldoctest -julia> a = [true,false,false,true] -4-element Vector{Bool}: - 1 - 0 - 0 - 1 - -julia> all(a) -false - -julia> all((println(i); v) for (i, v) in enumerate(a)) -1 -2 -false - -julia> all([missing, false]) -false - -julia> all([true, missing]) -missing -``` -""" -all(itr) = all(identity, itr) - -""" - any(p, itr) -> Bool - -Determine whether predicate `p` returns `true` for any elements of `itr`, returning -`true` as soon as the first item in `itr` for which `p` returns `true` is encountered -(short-circuiting). To short-circuit on `false`, use [`all`](@ref). - -If the input contains [`missing`](@ref) values, return `missing` if all non-missing -values are `false` (or equivalently, if the input contains no `true` value), following -[three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). - -# Examples -```jldoctest -julia> any(i->(4<=i<=6), [3,5,7]) -true - -julia> any(i -> (println(i); i > 3), 1:10) -1 -2 -3 -4 -true - -julia> any(i -> i > 0, [1, missing]) -true - -julia> any(i -> i > 0, [-1, missing]) -missing - -julia> any(i -> i > 0, [-1, 0]) -false -``` -""" -any(f, itr) = _any(f, itr, :) - -for ItrT = (Tuple,Any) - # define a generic method and a specialized version for `Tuple`, - # whose method bodies are identical, while giving better effects to the later - @eval function _any(f, itr::$ItrT, ::Colon) - $(ItrT === Tuple ? :(@_terminates_locally_meta) : :nothing) - anymissing = false - for x in itr - v = f(x) - if ismissing(v) - anymissing = true - else - v && return true - end - end - return anymissing ? missing : false - end -end - -# Specialized versions of any(f, ::Tuple) -# We fall back to the for loop implementation all elements have the same type or -# if the tuple is too large. -function any(f, itr::Tuple) - if itr isa NTuple || length(itr) > 32 - return _any(f, itr, :) - end - _any_tuple(f, false, itr...) -end - -@inline function _any_tuple(f, anymissing, x, rest...) - v = f(x) - if ismissing(v) - anymissing = true - elseif v - return true - end - return _any_tuple(f, anymissing, rest...) -end -@inline _any_tuple(f, anymissing) = anymissing ? missing : false - -""" - all(p, itr) -> Bool - -Determine whether predicate `p` returns `true` for all elements of `itr`, returning -`false` as soon as the first item in `itr` for which `p` returns `false` is encountered -(short-circuiting). To short-circuit on `true`, use [`any`](@ref). - -If the input contains [`missing`](@ref) values, return `missing` if all non-missing -values are `true` (or equivalently, if the input contains no `false` value), following -[three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). - -# Examples -```jldoctest -julia> all(i->(4<=i<=6), [4,5,6]) -true - -julia> all(i -> (println(i); i < 3), 1:10) -1 -2 -3 -false - -julia> all(i -> i > 0, [1, missing]) -missing - -julia> all(i -> i > 0, [-1, missing]) -false - -julia> all(i -> i > 0, [1, 2]) -true -``` -""" -all(f, itr) = _all(f, itr, :) - -for ItrT = (Tuple,Any) - # define a generic method and a specialized version for `Tuple`, - # whose method bodies are identical, while giving better effects to the later - @eval function _all(f, itr::$ItrT, ::Colon) - $(ItrT === Tuple ? :(@_terminates_locally_meta) : :nothing) - anymissing = false - for x in itr - v = f(x) - if ismissing(v) - anymissing = true - else - v || return false - end - end - return anymissing ? missing : true - end -end - -# Specialized versions of all(f, ::Tuple), -# This is similar to any(f, ::Tuple) defined above. -function all(f, itr::Tuple) - if itr isa NTuple || length(itr) > 32 - return _all(f, itr, :) - end - _all_tuple(f, false, itr...) -end - -@inline function _all_tuple(f, anymissing, x, rest...) - v = f(x) - if ismissing(v) - anymissing = true - # this syntax allows throwing a TypeError for non-Bool, for consistency with any - elseif v - nothing - else - return false - end - return _all_tuple(f, anymissing, rest...) -end -@inline _all_tuple(f, anymissing) = anymissing ? missing : true - -all(::Tuple{Missing}) = missing - ## count _bool(f) = x->f(x)::Bool diff --git a/base/reshapedarray.jl b/base/reshapedarray.jl index 07f608588837b..f65a7d8c9561a 100644 --- a/base/reshapedarray.jl +++ b/base/reshapedarray.jl @@ -121,37 +121,56 @@ reshape reshape(parent::AbstractArray, dims::IntOrInd...) = reshape(parent, dims) reshape(parent::AbstractArray, shp::Tuple{Union{Integer,OneTo}, Vararg{Union{Integer,OneTo}}}) = reshape(parent, to_shape(shp)) +reshape(parent::AbstractArray, dims::Tuple{Integer, Vararg{Integer}}) = reshape(parent, map(Int, dims)) reshape(parent::AbstractArray, dims::Dims) = _reshape(parent, dims) # Allow missing dimensions with Colon(): reshape(parent::AbstractVector, ::Colon) = parent reshape(parent::AbstractVector, ::Tuple{Colon}) = parent reshape(parent::AbstractArray, dims::Int...) = reshape(parent, dims) -reshape(parent::AbstractArray, dims::Union{Int,Colon}...) = reshape(parent, dims) -reshape(parent::AbstractArray, dims::Tuple{Vararg{Union{Int,Colon}}}) = reshape(parent, _reshape_uncolon(parent, dims)) -@inline function _reshape_uncolon(A, dims) - @noinline throw1(dims) = throw(DimensionMismatch(string("new dimensions $(dims) ", - "may have at most one omitted dimension specified by `Colon()`"))) - @noinline throw2(A, dims) = throw(DimensionMismatch(string("array size $(length(A)) ", - "must be divisible by the product of the new dimensions $dims"))) - pre = _before_colon(dims...)::Tuple{Vararg{Int}} +reshape(parent::AbstractArray, dims::Integer...) = reshape(parent, dims) +reshape(parent::AbstractArray, dims::Union{Integer,Colon}...) = reshape(parent, dims) +reshape(parent::AbstractArray, dims::Tuple{Vararg{Union{Integer,Colon}}}) = reshape(parent, _reshape_uncolon(parent, dims)) + +@noinline throw1(dims) = throw(DimensionMismatch(LazyString("new dimensions ", dims, + " may have at most one omitted dimension specified by `Colon()`"))) +@noinline throw2(lenA, dims) = throw(DimensionMismatch(string("array size ", lenA, + " must be divisible by the product of the new dimensions ", dims))) + +@inline function _reshape_uncolon(A, _dims::Tuple{Vararg{Union{Integer, Colon}}}) + # promote the dims to `Int` at least + dims = map(x -> x isa Colon ? x : promote_type(typeof(x), Int)(x), _dims) + pre = _before_colon(dims...) post = _after_colon(dims...) _any_colon(post...) && throw1(dims) - post::Tuple{Vararg{Int}} len = length(A) - sz, is_exact = if iszero(len) - (0, true) + _reshape_uncolon_computesize(len, dims, pre, post) +end +@inline function _reshape_uncolon_computesize(len::Int, dims, pre::Tuple{Vararg{Int}}, post::Tuple{Vararg{Int}}) + sz = if iszero(len) + 0 else let pr = Core.checked_dims(pre..., post...) # safe product - if iszero(pr) - throw2(A, dims) - end - (quo, rem) = divrem(len, pr) - (Int(quo), iszero(rem)) + quo = _reshape_uncolon_computesize_nonempty(len, dims, pr) + convert(Int, quo) end - end::Tuple{Int,Bool} - is_exact || throw2(A, dims) - (pre..., sz, post...)::Tuple{Int,Vararg{Int}} + end + (pre..., sz, post...) +end +@inline function _reshape_uncolon_computesize(len, dims, pre, post) + pr = prod((pre..., post...)) + sz = if iszero(len) + promote(len, pr)[1] # zero of the correct type + else + _reshape_uncolon_computesize_nonempty(len, dims, pr) + end + (pre..., sz, post...) +end +@inline function _reshape_uncolon_computesize_nonempty(len, dims, pr) + iszero(pr) && throw2(len, dims) + (quo, rem) = divrem(len, pr) + iszero(rem) || throw2(len, dims) + quo end @inline _any_colon() = false @inline _any_colon(dim::Colon, tail...) = true diff --git a/base/show.jl b/base/show.jl index 23957d6e29b2d..cb36488b92bc1 100644 --- a/base/show.jl +++ b/base/show.jl @@ -2821,7 +2821,7 @@ function show(io::IO, vm::Core.TypeofVararg) end end -Compiler.include(Compiler.IRShow, "ssair/show.jl") # define `show` for the compiler types +Compiler.load_irshow!() const IRShow = Compiler.IRShow # an alias for compatibility function show(io::IO, src::CodeInfo; debuginfo::Symbol=:source) diff --git a/base/sort.jl b/base/sort.jl index 6991f12551ab4..e1d46a8ba20ae 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -4,7 +4,7 @@ module Sort using Base.Order -using Base: copymutable, midpoint, require_one_based_indexing, uinttype, +using Base: copymutable, midpoint, require_one_based_indexing, uinttype, tail, sub_with_overflow, add_with_overflow, OneTo, BitSigned, BitIntegerType, top_set_bit import Base: @@ -1475,21 +1475,16 @@ InitialOptimizations(next) = SubArrayOptimization( Small{10}( IEEEFloatOptimization( next))))) -""" - DEFAULT_STABLE - -The default sorting algorithm. - -This algorithm is guaranteed to be stable (i.e. it will not reorder elements that compare -equal). It makes an effort to be fast for most inputs. -The algorithms used by `DEFAULT_STABLE` are an implementation detail. See extended help -for the current dispatch system. +""" + struct DefaultStable <: Algorithm end -# Extended Help +`DefaultStable` is an algorithm which indicates that a fast, general purpose sorting +algorithm should be used, but does not specify exactly which algorithm. -`DEFAULT_STABLE` is composed of two parts: the [`InitialOptimizations`](@ref) and a hybrid -of Radix, Insertion, Counting, Quick sorts. +Currently, when sorting short NTuples, this is an unrolled mergesort, and otherwise it is +composed of two parts: the [`InitialOptimizations`](@ref) and a hybrid of Radix, Insertion, +Counting, Quick sorts. We begin with MissingOptimization because it has no runtime cost when it is not triggered and can enable other optimizations to be applied later. For example, @@ -1549,7 +1544,39 @@ stage. Finally, if the input has length less than 80, we dispatch to [`InsertionSort`](@ref) and otherwise we dispatch to [`ScratchQuickSort`](@ref). """ -const DEFAULT_STABLE = InitialOptimizations( +struct DefaultStable <: Algorithm end + +""" + DEFAULT_STABLE + +The default sorting algorithm. + +This algorithm is guaranteed to be stable (i.e. it will not reorder elements that compare +equal). It makes an effort to be fast for most inputs. + +The algorithms used by `DEFAULT_STABLE` are an implementation detail. See the docstring +of `Base.Sort.DefaultStable` for the current dispatch system. +""" +const DEFAULT_STABLE = DefaultStable() + +""" + DefaultUnstable <: Algorithm + +Like [`DefaultStable`](@ref), but does not guarantee stability. +""" +struct DefaultUnstable <: Algorithm end + +""" + DEFAULT_UNSTABLE + +An efficient sorting algorithm which may or may not be stable. + +The algorithms used by `DEFAULT_UNSTABLE` are an implementation detail. They are currently +the same as those used by [`DEFAULT_STABLE`](@ref), but this is subject to change in future. +""" +const DEFAULT_UNSTABLE = DefaultUnstable() + +const _DEFAULT_ALGORITHMS_FOR_VECTORS = InitialOptimizations( IsUIntMappable( Small{40}( CheckSorted( @@ -1560,15 +1587,10 @@ const DEFAULT_STABLE = InitialOptimizations( ScratchQuickSort())))))), StableCheckSorted( ScratchQuickSort()))) -""" - DEFAULT_UNSTABLE -An efficient sorting algorithm. +_sort!(v::AbstractVector, ::Union{DefaultStable, DefaultUnstable}, o::Ordering, kw) = + _sort!(v, _DEFAULT_ALGORITHMS_FOR_VECTORS, o, kw) -The algorithms used by `DEFAULT_UNSTABLE` are an implementation detail. They are currently -the same as those used by [`DEFAULT_STABLE`](@ref), but this is subject to change in future. -""" -const DEFAULT_UNSTABLE = DEFAULT_STABLE const SMALL_THRESHOLD = 20 function Base.show(io::IO, alg::Algorithm) @@ -1598,6 +1620,7 @@ defalg(v::AbstractArray) = DEFAULT_STABLE defalg(v::AbstractArray{<:Union{Number, Missing}}) = DEFAULT_UNSTABLE defalg(v::AbstractArray{Missing}) = DEFAULT_UNSTABLE # for method disambiguation defalg(v::AbstractArray{Union{}}) = DEFAULT_UNSTABLE # for method disambiguation +defalg(v::NTuple) = DEFAULT_STABLE """ sort!(v; alg::Base.Sort.Algorithm=Base.Sort.defalg(v), lt=isless, by=identity, rev::Bool=false, order::Base.Order.Ordering=Base.Order.Forward) @@ -1677,13 +1700,16 @@ julia> v = [(1, "c"), (3, "a"), (2, "b")]; sort!(v, by = x -> x[2]); v (2, "b") (1, "c") -julia> sort(0:3, by=x->x-2, order=Base.Order.By(abs)) # same as sort(0:3, by=abs(x->x-2)) +julia> sort(0:3, by=x->x-2, order=Base.Order.By(abs)) 4-element Vector{Int64}: 2 1 3 0 +julia> sort(0:3, by=x->x-2, order=Base.Order.By(abs)) == sort(0:3, by=x->abs(x-2)) +true + julia> sort([2, NaN, 1, NaN, 3]) # correct sort with default lt=isless 5-element Vector{Float64}: 1.0 @@ -1736,6 +1762,41 @@ julia> v """ sort(v::AbstractVector; kws...) = sort!(copymutable(v); kws...) +function sort(x::NTuple; + alg::Algorithm=defalg(x), + lt=isless, + by=identity, + rev::Union{Bool,Nothing}=nothing, + order::Ordering=Forward, + scratch::Union{Vector, Nothing}=nothing) + # Can't do this check with type parameters because of https://github.com/JuliaLang/julia/issues/56698 + scratch === nothing || eltype(x) == eltype(scratch) || throw(ArgumentError("scratch has the wrong eltype")) + _sort(x, alg, ord(lt,by,rev,order), (;scratch))::typeof(x) +end +# Folks who want to hack internals can define a new _sort(x::NTuple, ::TheirAlg, o::Ordering) +# or _sort(x::NTuple{N, TheirType}, ::DefaultStable, o::Ordering) where N +function _sort(x::NTuple, a::Union{DefaultStable, DefaultUnstable}, o::Ordering, kw) + # The unrolled tuple sort is prohibitively slow to compile for length > 9. + # See https://github.com/JuliaLang/julia/pull/46104#issuecomment-1435688502 for benchmarks + if length(x) > 9 + v = copymutable(x) + _sort!(v, a, o, kw) + typeof(x)(v) + else + _mergesort(x, o) + end +end +_mergesort(x::Union{NTuple{0}, NTuple{1}}, o::Ordering) = x +function _mergesort(x::NTuple, o::Ordering) + a, b = Base.IteratorsMD.split(x, Val(length(x)>>1)) + merge(_mergesort(a, o), _mergesort(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)...)) + ## partialsortperm: the permutation to sort the first k elements of an array ## """ diff --git a/base/special/exp.jl b/base/special/exp.jl index 312197339a086..38d7509807aed 100644 --- a/base/special/exp.jl +++ b/base/special/exp.jl @@ -216,6 +216,7 @@ end small_part = muladd(jU, expm1b_kernel(base, r), jL) + jU if !(abs(x) <= SUBNORM_EXP(base, T)) + isnan(x) && return x x >= MAX_EXP(base, T) && return Inf x <= MIN_EXP(base, T) && return 0.0 if k <= -53 @@ -243,6 +244,7 @@ end hi, lo = Base.canonicalize2(1.0, kern) small_part = fma(jU, hi, muladd(jU, (lo+xlo), very_small)) if !(abs(x) <= SUBNORM_EXP(base, T)) + isnan(x) && return x x >= MAX_EXP(base, T) && return Inf x <= MIN_EXP(base, T) && return 0.0 if k <= -53 diff --git a/base/strings/annotated.jl b/base/strings/annotated.jl index c5c330fe0dfcd..814ee2afa9d55 100644 --- a/base/strings/annotated.jl +++ b/base/strings/annotated.jl @@ -55,9 +55,9 @@ like [`string`](@ref) but preserves any annotations present in the arguments. # Examples -```julia-repl +```jldoctest; setup=:(using Base: AnnotatedString) julia> AnnotatedString("this is an example annotated string", - [(1:18, :A => 1), (12:28, :B => 2), (18:35, :C => 3)]) + [(1:18, :A, 1), (12:28, :B, 2), (18:35, :C, 3)]) "this is an example annotated string" ``` """ @@ -87,8 +87,8 @@ AnnotatedChar(s::S, annotations::Vector{$Annotation}) # Examples -```julia-repl -julia> AnnotatedChar('j', :label => 1) +```jldoctest; setup=:(using Base: AnnotatedChar) +julia> AnnotatedChar('j', [(:label, 1)]) 'j': ASCII/Unicode U+006A (category Ll: Letter, lowercase) ``` """ @@ -232,11 +232,11 @@ See also [`AnnotatedString`](@ref) and [`AnnotatedChar`](@ref). ## Examples -```julia-repl +```jldoctest; setup=:(using Base: AnnotatedString, annotatedstring) julia> annotatedstring("now a AnnotatedString") "now a AnnotatedString" -julia> annotatedstring(AnnotatedString("annotated", [(1:9, :label => 1)]), ", and unannotated") +julia> annotatedstring(AnnotatedString("annotated", [(1:9, :label, 1)]), ", and unannotated") "annotated, and unannotated" ``` """ @@ -344,7 +344,7 @@ end annotate!(str::AnnotatedString, [range::UnitRange{Int}], label::Symbol, value) annotate!(str::SubString{AnnotatedString}, [range::UnitRange{Int}], label::Symbol, value) -Annotate a `range` of `str` (or the entire string) with a labeled value (`label` => `value`). +Annotate a `range` of `str` (or the entire string) with a labeled value `(label, value)`. To remove existing `label` annotations, use a value of `nothing`. The order in which annotations are applied to `str` is semantically meaningful, @@ -365,7 +365,7 @@ annotate!(s::SubString{<:AnnotatedString}, label::Symbol, @nospecialize(val::Any """ annotate!(char::AnnotatedChar, label::Symbol, value::Any) -Annotate `char` with the pair `label => value`. +Annotate `char` with the labeled value `(label, value)`. """ annotate!(c::AnnotatedChar, label::Symbol, @nospecialize(val::Any)) = (push!(c.annotations, Annotation((; label, val))); c) diff --git a/base/strings/io.jl b/base/strings/io.jl index 116bcf71eeb7a..b4a3c7ad3e0c2 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -250,7 +250,9 @@ print(io::IO, s::Union{String,SubString{String}}) = (write(io, s); nothing) """ repr(x; context=nothing) -Create a string from any value using the 2-argument `show(io, x)` function. +Create a string representation of any value using the 2-argument `show(io, x)` function, +which aims to produce a string that is parseable Julia code, where possible. +i.e. `eval(Meta.parse(repr(x))) == x` should hold true. You should not add methods to `repr`; define a [`show`](@ref) method instead. The optional keyword argument `context` can be set to a `:key=>value` pair, a diff --git a/base/strings/unicode.jl b/base/strings/unicode.jl index ad047514c85a6..fcb4a371e9898 100644 --- a/base/strings/unicode.jl +++ b/base/strings/unicode.jl @@ -534,11 +534,17 @@ iscntrl(c::AbstractChar) = c <= '\x1f' || '\x7f' <= c <= '\u9f' Tests whether a character belongs to the Unicode general category Punctuation, i.e. a character whose category code begins with 'P'. +!!! note + This behavior is different from the `ispunct` function in C. + # Examples ```jldoctest julia> ispunct('α') false +julia> ispunct('=') +false + julia> ispunct('/') true diff --git a/base/sysimg.jl b/base/sysimg.jl index e57ec1c99bfe6..42f54a849f157 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -1,6 +1,6 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -# Can be be loaded on top of either an existing system image built from +# Can be loaded on top of either an existing system image built from # `Base_compiler.jl` or standalone, in which case we will build it now. let had_compiler = isdefined(Main, :Base) if had_compiler; else diff --git a/base/task.jl b/base/task.jl index 2a922c4b85f24..951e980ee903c 100644 --- a/base/task.jl +++ b/base/task.jl @@ -977,7 +977,11 @@ function enq_work(t::Task) return t end -schedule(t::Task) = enq_work(t) +function schedule(t::Task) + # [task] created -scheduled-> wait_time + maybe_record_enqueued!(t) + enq_work(t) +end """ schedule(t::Task, [val]; error=false) @@ -1031,6 +1035,8 @@ function schedule(t::Task, @nospecialize(arg); error=false) t.queue === nothing || Base.error("schedule: Task not runnable") setfield!(t, :result, arg) end + # [task] created -scheduled-> wait_time + maybe_record_enqueued!(t) enq_work(t) return t end @@ -1064,11 +1070,15 @@ immediately yields to `t` before calling the scheduler. Throws a `ConcurrencyViolationError` if `t` is the currently running task. """ function yield(t::Task, @nospecialize(x=nothing)) - current = current_task() - t === current && throw(ConcurrencyViolationError("Cannot yield to currently running task!")) + ct = current_task() + t === ct && throw(ConcurrencyViolationError("Cannot yield to currently running task!")) (t._state === task_state_runnable && t.queue === nothing) || throw(ConcurrencyViolationError("yield: Task not runnable")) + # [task] user_time -yield-> wait_time + record_running_time!(ct) + # [task] created -scheduled-> wait_time + maybe_record_enqueued!(t) t.result = x - enq_work(current) + enq_work(ct) set_next_task(t) return try_yieldto(ensure_rescheduled) end @@ -1082,6 +1092,7 @@ call to `yieldto`. This is a low-level call that only switches tasks, not consid or scheduling in any way. Its use is discouraged. """ function yieldto(t::Task, @nospecialize(x=nothing)) + ct = current_task() # TODO: these are legacy behaviors; these should perhaps be a scheduler # state error instead. if t._state === task_state_done @@ -1089,6 +1100,10 @@ function yieldto(t::Task, @nospecialize(x=nothing)) elseif t._state === task_state_failed throw(t.result) end + # [task] user_time -yield-> wait_time + record_running_time!(ct) + # [task] created -scheduled-unfairly-> wait_time + maybe_record_enqueued!(t) t.result = x set_next_task(t) return try_yieldto(identity) @@ -1102,6 +1117,10 @@ function try_yieldto(undo) rethrow() end ct = current_task() + # [task] wait_time -(re)started-> user_time + if ct.metrics_enabled + @atomic :monotonic ct.last_started_running_at = time_ns() + end if ct._isexception exc = ct.result ct.result = nothing @@ -1115,6 +1134,11 @@ end # yield to a task, throwing an exception in it function throwto(t::Task, @nospecialize exc) + ct = current_task() + # [task] user_time -yield-> wait_time + record_running_time!(ct) + # [task] created -scheduled-unfairly-> wait_time + maybe_record_enqueued!(t) t.result = exc t._isexception = true set_next_task(t) @@ -1167,6 +1191,9 @@ checktaskempty = Partr.multiq_check_empty end function wait() + ct = current_task() + # [task] user_time -yield-or-done-> wait_time + record_running_time!(ct) GC.safepoint() W = workqueue_for(Threads.threadid()) poptask(W) @@ -1181,3 +1208,21 @@ if Sys.iswindows() else pause() = ccall(:pause, Cvoid, ()) end + +# update the `running_time_ns` field of `t` to include the time since it last started running. +function record_running_time!(t::Task) + if t.metrics_enabled && !istaskdone(t) + @atomic :monotonic t.running_time_ns += time_ns() - t.last_started_running_at + end + return t +end + +# if this is the first time `t` has been added to the run queue +# (or the first time it has been unfairly yielded to without being added to the run queue) +# then set the `first_enqueued_at` field to the current time. +function maybe_record_enqueued!(t::Task) + if t.metrics_enabled && t.first_enqueued_at == 0 + @atomic :monotonic t.first_enqueued_at = time_ns() + end + return t +end diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl index a21d708b4a077..07ff814af1570 100644 --- a/base/threadingconstructs.jl +++ b/base/threadingconstructs.jl @@ -3,6 +3,8 @@ export threadid, nthreads, @threads, @spawn, threadpool, nthreadpools +public Condition + """ Threads.threadid([t::Task]) -> Int diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index ffbbfd620997f..b075223d9c7e4 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -136,60 +136,56 @@ for match = Base._methods(+, (Int, Int), -1, Base.get_world_counter()) m = match.method delete!(push!(Set{Method}(), m), m) copy(Core.Compiler.retrieve_code_info(Core.Compiler.specialize_method(match), typemax(UInt))) - - empty!(Set()) - push!(push!(Set{Union{GlobalRef,Symbol}}(), :two), GlobalRef(Base, :two)) - (setindex!(Dict{String,Base.PkgId}(), Base.PkgId(Base), "file.jl"))["file.jl"] - (setindex!(Dict{Symbol,Vector{Int}}(), [1], :two))[:two] - (setindex!(Dict{Base.PkgId,String}(), "file.jl", Base.PkgId(Base)))[Base.PkgId(Base)] - (setindex!(Dict{Union{GlobalRef,Symbol}, Vector{Int}}(), [1], :two))[:two] - (setindex!(IdDict{Type, Union{Missing, Vector{Tuple{LineNumberNode, Expr}}}}(), missing, Int))[Int] - Dict{Symbol, Union{Nothing, Bool, Symbol}}(:one => false)[:one] - Dict(Base => [:(1+1)])[Base] - Dict(:one => [1])[:one] - Dict("abc" => Set())["abc"] - pushfirst!([], sum) - get(Base.pkgorigins, Base.PkgId(Base), nothing) - sort!([1,2,3]) - unique!([1,2,3]) - cumsum([1,2,3]) - append!(Int[], BitSet()) - isempty(BitSet()) - delete!(BitSet([1,2]), 3) - deleteat!(Int32[1,2,3], [1,3]) - deleteat!(Any[1,2,3], [1,3]) - Core.svec(1, 2) == Core.svec(3, 4) - any(t->t[1].line > 1, [(LineNumberNode(2,:none), :(1+1))]) - - # Code loading uses this - sortperm(mtime.(readdir(".")), rev=true) - # JLLWrappers uses these - Dict{Base.UUID,Set{String}}()[Base.UUID("692b3bcd-3c85-4b1f-b108-f13ce0eb3210")] = Set{String}() - get!(Set{String}, Dict{Base.UUID,Set{String}}(), Base.UUID("692b3bcd-3c85-4b1f-b108-f13ce0eb3210")) - eachindex(IndexLinear(), Expr[]) - push!(Expr[], Expr(:return, false)) - vcat(String[], String[]) - k, v = (:hello => nothing) - Base.print_time_imports_report(Base) - Base.print_time_imports_report_init(Base) - - # Preferences uses these - get(Dict{String,Any}(), "missing", nothing) - delete!(Dict{String,Any}(), "missing") - for (k, v) in Dict{String,Any}() - println(k) - end - - # interactive startup uses this - write(IOBuffer(), "") - - # Not critical, but helps hide unrelated compilation from @time when using --trace-compile. - f55729() = Base.Experimental.@force_compile - @time @eval f55729() - @time @eval f55729() - break # only actually need to do this once end +empty!(Set()) +push!(push!(Set{Union{GlobalRef,Symbol}}(), :two), GlobalRef(Base, :two)) +(setindex!(Dict{String,Base.PkgId}(), Base.PkgId(Base), "file.jl"))["file.jl"] +(setindex!(Dict{Symbol,Vector{Int}}(), [1], :two))[:two] +(setindex!(Dict{Base.PkgId,String}(), "file.jl", Base.PkgId(Base)))[Base.PkgId(Base)] +(setindex!(Dict{Union{GlobalRef,Symbol}, Vector{Int}}(), [1], :two))[:two] +(setindex!(IdDict{Type, Union{Missing, Vector{Tuple{LineNumberNode, Expr}}}}(), missing, Int))[Int] +Dict{Symbol, Union{Nothing, Bool, Symbol}}(:one => false)[:one] +Dict(Base => [:(1+1)])[Base] +Dict(:one => [1])[:one] +Dict("abc" => Set())["abc"] +pushfirst!([], sum) +get(Base.pkgorigins, Base.PkgId(Base), nothing) +sort!([1,2,3]) +unique!([1,2,3]) +cumsum([1,2,3]) +append!(Int[], BitSet()) +isempty(BitSet()) +delete!(BitSet([1,2]), 3) +deleteat!(Int32[1,2,3], [1,3]) +deleteat!(Any[1,2,3], [1,3]) +Core.svec(1, 2) == Core.svec(3, 4) +any(t->t[1].line > 1, [(LineNumberNode(2,:none), :(1+1))]) + +# Code loading uses this +sortperm(mtime.(readdir(".")), rev=true) +# JLLWrappers uses these +Dict{Base.UUID,Set{String}}()[Base.UUID("692b3bcd-3c85-4b1f-b108-f13ce0eb3210")] = Set{String}() +get!(Set{String}, Dict{Base.UUID,Set{String}}(), Base.UUID("692b3bcd-3c85-4b1f-b108-f13ce0eb3210")) +eachindex(IndexLinear(), Expr[]) +push!(Expr[], Expr(:return, false)) +vcat(String[], String[]) +k, v = (:hello => nothing) +Base.print_time_imports_report(Base) +Base.print_time_imports_report_init(Base) + +# Preferences uses these +get(Dict{String,Any}(), "missing", nothing) +delete!(Dict{String,Any}(), "missing") +for (k, v) in Dict{String,Any}() + println(k) +end + +# interactive startup uses this +write(IOBuffer(), "") + +# precompile @time report generation and printing +@time @eval Base.Experimental.@force_compile """ julia_exepath() = joinpath(Sys.BINDIR, Base.julia_exename()) diff --git a/contrib/juliac.jl b/contrib/juliac.jl index 0f008976d2b4f..20d56615c6357 100644 --- a/contrib/juliac.jl +++ b/contrib/juliac.jl @@ -4,7 +4,6 @@ cmd = Base.julia_cmd() cmd = `$cmd --startup-file=no --history-file=no` output_type = nothing # exe, sharedlib, sysimage -trim = nothing outname = nothing file = nothing add_ccallables = false @@ -15,13 +14,16 @@ if help !== nothing println( """ Usage: julia juliac.jl [--output-exe | --output-lib | --output-sysimage] [options] - --trim= Only output code statically determined to be reachable + --experimental --trim= Only output code statically determined to be reachable --compile-ccallable Include all methods marked `@ccallable` in output --verbose Request verbose output """) exit(0) end +# arguments to forward to julia compilation process +julia_args = [] + let i = 1 while i <= length(ARGS) arg = ARGS[i] @@ -31,17 +33,13 @@ let i = 1 i == length(ARGS) && error("Output specifier requires an argument") global outname = ARGS[i+1] i += 1 - elseif startswith(arg, "--trim") - arg = split(arg, '=') - if length(arg) == 1 - global trim = "safe" - else - global trim = arg[2] - end elseif arg == "--compile-ccallable" global add_ccallables = true elseif arg == "--verbose" global verbose = true + elseif startswith(arg, "--trim") || arg == "--experimental" + # forwarded args + push!(julia_args, arg) else if arg[1] == '-' || !isnothing(file) println("Unexpected argument `$arg`") @@ -79,8 +77,7 @@ open(initsrc_path, "w") do io """) end -static_call_graph_arg() = isnothing(trim) ? `` : `--trim=$(trim)` -cmd = addenv(`$cmd --project=$(Base.active_project()) --output-o $img_path --output-incremental=no --strip-ir --strip-metadata $(static_call_graph_arg()) $(joinpath(@__DIR__,"juliac-buildscript.jl")) $absfile $output_type $add_ccallables`, "OPENBLAS_NUM_THREADS" => 1, "JULIA_NUM_THREADS" => 1) +cmd = addenv(`$cmd --project=$(Base.active_project()) --output-o $img_path --output-incremental=no --strip-ir --strip-metadata $julia_args $(joinpath(@__DIR__,"juliac-buildscript.jl")) $absfile $output_type $add_ccallables`, "OPENBLAS_NUM_THREADS" => 1, "JULIA_NUM_THREADS" => 1) verbose && println("Running: $cmd") if !success(pipeline(cmd; stdout, stderr)) println(stderr, "\nFailed to compile $file") diff --git a/deps/JuliaSyntax.version b/deps/JuliaSyntax.version index 7f124715024ce..86f94135884a0 100644 --- a/deps/JuliaSyntax.version +++ b/deps/JuliaSyntax.version @@ -1,4 +1,4 @@ JULIASYNTAX_BRANCH = main -JULIASYNTAX_SHA1 = 4f1731d6ce7c2465fc21ea245110b7a39f34658a +JULIASYNTAX_SHA1 = dfd1d69b153eb119873035e62993a109b27192f0 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/ArgTools-1314758ad02ff5e9e5ca718920c6c633b467a84a.tar.gz/md5 b/deps/checksums/ArgTools-1314758ad02ff5e9e5ca718920c6c633b467a84a.tar.gz/md5 new file mode 100644 index 0000000000000..e172379604478 --- /dev/null +++ b/deps/checksums/ArgTools-1314758ad02ff5e9e5ca718920c6c633b467a84a.tar.gz/md5 @@ -0,0 +1 @@ +d3209f45b8ea01a22ac7e9b265e3b84f diff --git a/deps/checksums/ArgTools-1314758ad02ff5e9e5ca718920c6c633b467a84a.tar.gz/sha512 b/deps/checksums/ArgTools-1314758ad02ff5e9e5ca718920c6c633b467a84a.tar.gz/sha512 new file mode 100644 index 0000000000000..991a457654113 --- /dev/null +++ b/deps/checksums/ArgTools-1314758ad02ff5e9e5ca718920c6c633b467a84a.tar.gz/sha512 @@ -0,0 +1 @@ +314981eee11356f14b6dc9e07389c51432e7862d6c767d87d6679385f5a36faef34902954a5dfa6b37d8f3f25eaa4f23ba9431cc78acd3513377955e7d73f210 diff --git a/deps/checksums/ArgTools-997089b9cd56404b40ff766759662e16dc1aab4b.tar.gz/md5 b/deps/checksums/ArgTools-997089b9cd56404b40ff766759662e16dc1aab4b.tar.gz/md5 deleted file mode 100644 index 3e85638390011..0000000000000 --- a/deps/checksums/ArgTools-997089b9cd56404b40ff766759662e16dc1aab4b.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -44175a2843f243a8f2e01cd1e781ecc9 diff --git a/deps/checksums/ArgTools-997089b9cd56404b40ff766759662e16dc1aab4b.tar.gz/sha512 b/deps/checksums/ArgTools-997089b9cd56404b40ff766759662e16dc1aab4b.tar.gz/sha512 deleted file mode 100644 index f5690055f63f4..0000000000000 --- a/deps/checksums/ArgTools-997089b9cd56404b40ff766759662e16dc1aab4b.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -8a9d0f20cd8fa0ed072966debc691597bb09b34836465f52595b9b2525e80b194c7176781495573dd2f9a02b142e832e59f0dccef15afa184543775d58dc7987 diff --git a/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/md5 b/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/md5 deleted file mode 100644 index 9904464c82b3b..0000000000000 --- a/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -390521058a478a131ca49d349c9b9383 diff --git a/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/sha512 b/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/sha512 deleted file mode 100644 index a7fbe055c2251..0000000000000 --- a/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -7f0f414d94739a25b7d713c46887e26cd349329828d42297f44928204b36d15ba9163ad6f670aba72ed9229557bb0f35ab4686429975d1f349fe12b1ba2b189f diff --git a/deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/md5 b/deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/md5 new file mode 100644 index 0000000000000..e1c0f9e87b7c7 --- /dev/null +++ b/deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/md5 @@ -0,0 +1 @@ +98b8b8bc0ea4bf24c4b2986a5b7ae3e9 diff --git a/deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/sha512 b/deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/sha512 new file mode 100644 index 0000000000000..ed816ebc21e97 --- /dev/null +++ b/deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/sha512 @@ -0,0 +1 @@ +4043933825bf716f2733f8e90632de34a95a437f3b31cda92edd510ffee208f8e374ec3c5922c8142342ae21b4ec4cbd1ecd4036b9057056a12c86169632ac7b diff --git a/deps/checksums/Downloads-89d3c7dded535a77551e763a437a6d31e4d9bf84.tar.gz/md5 b/deps/checksums/Downloads-89d3c7dded535a77551e763a437a6d31e4d9bf84.tar.gz/md5 deleted file mode 100644 index 611f3dd448d98..0000000000000 --- a/deps/checksums/Downloads-89d3c7dded535a77551e763a437a6d31e4d9bf84.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -2472bd6434d21c4b3e3199437e6fdcf7 diff --git a/deps/checksums/Downloads-89d3c7dded535a77551e763a437a6d31e4d9bf84.tar.gz/sha512 b/deps/checksums/Downloads-89d3c7dded535a77551e763a437a6d31e4d9bf84.tar.gz/sha512 deleted file mode 100644 index 6937982e838f3..0000000000000 --- a/deps/checksums/Downloads-89d3c7dded535a77551e763a437a6d31e4d9bf84.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -0a3fa9a09de81aa9676dbc7448408c7503f45e42519a2667540ad890316c7da089c95de5464a2032171f963c6f3cba73d6b3c246f1c7ac6ede283fc8132d5209 diff --git a/deps/checksums/Downloads-afd04be8aa94204c075c8aec83fca040ebb4ff98.tar.gz/md5 b/deps/checksums/Downloads-afd04be8aa94204c075c8aec83fca040ebb4ff98.tar.gz/md5 new file mode 100644 index 0000000000000..f0d72ab470aeb --- /dev/null +++ b/deps/checksums/Downloads-afd04be8aa94204c075c8aec83fca040ebb4ff98.tar.gz/md5 @@ -0,0 +1 @@ +9dbfa6c0a76d20b2ca8de844d08f3af4 diff --git a/deps/checksums/Downloads-afd04be8aa94204c075c8aec83fca040ebb4ff98.tar.gz/sha512 b/deps/checksums/Downloads-afd04be8aa94204c075c8aec83fca040ebb4ff98.tar.gz/sha512 new file mode 100644 index 0000000000000..7abbb06818d75 --- /dev/null +++ b/deps/checksums/Downloads-afd04be8aa94204c075c8aec83fca040ebb4ff98.tar.gz/sha512 @@ -0,0 +1 @@ +f5d2469419f4a083f84c1f23e6e528f4115804e45bdfdbd897110bb346aaa70afc57c24f9166ae20fb4305c1d40f5a77de4cfed7b69aef93f09d0d4eff183e3d diff --git a/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/md5 b/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/md5 deleted file mode 100644 index c2663955ec773..0000000000000 --- a/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -8c9d9579eeab1ba40f978a32c9db9900 diff --git a/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/sha512 b/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/sha512 deleted file mode 100644 index 46647cb3e432b..0000000000000 --- a/deps/checksums/JuliaSyntax-4f1731d6ce7c2465fc21ea245110b7a39f34658a.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -1bdad624f61482b55deba8727fea1c087bfaea9e1f8afa3b44b984441fb7e663dac067baa4a96ae2d4cbd4a46ae8c87e9d20d2dfcd17046ad194711304184e57 diff --git a/deps/checksums/JuliaSyntax-dfd1d69b153eb119873035e62993a109b27192f0.tar.gz/md5 b/deps/checksums/JuliaSyntax-dfd1d69b153eb119873035e62993a109b27192f0.tar.gz/md5 new file mode 100644 index 0000000000000..51b30461d3905 --- /dev/null +++ b/deps/checksums/JuliaSyntax-dfd1d69b153eb119873035e62993a109b27192f0.tar.gz/md5 @@ -0,0 +1 @@ +e58559668aabb0fa96d598970c4d648e diff --git a/deps/checksums/JuliaSyntax-dfd1d69b153eb119873035e62993a109b27192f0.tar.gz/sha512 b/deps/checksums/JuliaSyntax-dfd1d69b153eb119873035e62993a109b27192f0.tar.gz/sha512 new file mode 100644 index 0000000000000..63a513ec9ae63 --- /dev/null +++ b/deps/checksums/JuliaSyntax-dfd1d69b153eb119873035e62993a109b27192f0.tar.gz/sha512 @@ -0,0 +1 @@ +59e22f7db63a383beadf96a68d4db6ae173d61be6d766ea1792b3a3bd70125f73dd4df9e55bad4c66363aa0b6ff6ea5259d3c91abf42f5fe34446e3fa076cc87 diff --git a/deps/checksums/LazyArtifacts-a719c0e3d68a95c6f3dc9571459428ca8761fa2c.tar.gz/md5 b/deps/checksums/LazyArtifacts-a719c0e3d68a95c6f3dc9571459428ca8761fa2c.tar.gz/md5 new file mode 100644 index 0000000000000..363c289573765 --- /dev/null +++ b/deps/checksums/LazyArtifacts-a719c0e3d68a95c6f3dc9571459428ca8761fa2c.tar.gz/md5 @@ -0,0 +1 @@ +ce3754f1d195e1533fc1f3c51e2a2d4c diff --git a/deps/checksums/LazyArtifacts-a719c0e3d68a95c6f3dc9571459428ca8761fa2c.tar.gz/sha512 b/deps/checksums/LazyArtifacts-a719c0e3d68a95c6f3dc9571459428ca8761fa2c.tar.gz/sha512 new file mode 100644 index 0000000000000..62227971fe7f7 --- /dev/null +++ b/deps/checksums/LazyArtifacts-a719c0e3d68a95c6f3dc9571459428ca8761fa2c.tar.gz/sha512 @@ -0,0 +1 @@ +1e1beb6202f089b82f06c1f1fcb129a31d2b438df8a3a49c7367d3df4587d8b9b8fbcf74d3f5ff3c25c241314c71e5dfd72014338b822f01252890e2a14831d1 diff --git a/deps/checksums/LazyArtifacts-e9a36338d5d0dfa4b222f4e11b446cbb7ea5836c.tar.gz/md5 b/deps/checksums/LazyArtifacts-e9a36338d5d0dfa4b222f4e11b446cbb7ea5836c.tar.gz/md5 deleted file mode 100644 index 4d14c85460418..0000000000000 --- a/deps/checksums/LazyArtifacts-e9a36338d5d0dfa4b222f4e11b446cbb7ea5836c.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -8355c253fadfc3f9222e05cb67845dd6 diff --git a/deps/checksums/LazyArtifacts-e9a36338d5d0dfa4b222f4e11b446cbb7ea5836c.tar.gz/sha512 b/deps/checksums/LazyArtifacts-e9a36338d5d0dfa4b222f4e11b446cbb7ea5836c.tar.gz/sha512 deleted file mode 100644 index d44f215e67673..0000000000000 --- a/deps/checksums/LazyArtifacts-e9a36338d5d0dfa4b222f4e11b446cbb7ea5836c.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -6d965199ed02446e694789a38f05249ff60ac00f8295fe32bf91a79cca34649829e38eaf46cc0b0b72ff2df7e184c2eaeb610600ebb5158251b331c61e9dfc5d diff --git a/deps/checksums/Pkg-7b759d7f0af56c5ad01f2289bbad71284a556970.tar.gz/md5 b/deps/checksums/Pkg-7b759d7f0af56c5ad01f2289bbad71284a556970.tar.gz/md5 deleted file mode 100644 index e55e74562d717..0000000000000 --- a/deps/checksums/Pkg-7b759d7f0af56c5ad01f2289bbad71284a556970.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -20d63322fc5b547d4c2464c27e9a6a0e diff --git a/deps/checksums/Pkg-7b759d7f0af56c5ad01f2289bbad71284a556970.tar.gz/sha512 b/deps/checksums/Pkg-7b759d7f0af56c5ad01f2289bbad71284a556970.tar.gz/sha512 deleted file mode 100644 index 5094dddb8142a..0000000000000 --- a/deps/checksums/Pkg-7b759d7f0af56c5ad01f2289bbad71284a556970.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -93dd178af474c76cce9368416d34570b66cc44c7c311e4dc14569d3f9deed70afcae8a2b1976535ed0732ed305c6d8d1b0ef04cbeeaa3af2891e97650d51467d diff --git a/deps/checksums/Pkg-e7c37f34293ab12051258828884755ea116b77df.tar.gz/md5 b/deps/checksums/Pkg-e7c37f34293ab12051258828884755ea116b77df.tar.gz/md5 new file mode 100644 index 0000000000000..6627dc3683c65 --- /dev/null +++ b/deps/checksums/Pkg-e7c37f34293ab12051258828884755ea116b77df.tar.gz/md5 @@ -0,0 +1 @@ +7024326f0c033e3da419ed217754fbed diff --git a/deps/checksums/Pkg-e7c37f34293ab12051258828884755ea116b77df.tar.gz/sha512 b/deps/checksums/Pkg-e7c37f34293ab12051258828884755ea116b77df.tar.gz/sha512 new file mode 100644 index 0000000000000..cfc9d5f30f0e8 --- /dev/null +++ b/deps/checksums/Pkg-e7c37f34293ab12051258828884755ea116b77df.tar.gz/sha512 @@ -0,0 +1 @@ +bef19838be850f027fb3dedadce0efa762cd64797809030210ec95830f538ced1b2aede3bd2ca5f32d489e2dc517b7c8e38307adcba194db417ce792437cfb56 diff --git a/deps/checksums/SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/md5 b/deps/checksums/SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/md5 new file mode 100644 index 0000000000000..52e05f5e427ae --- /dev/null +++ b/deps/checksums/SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/md5 @@ -0,0 +1 @@ +e52615827242aae56422a4f73a8c6878 diff --git a/deps/checksums/SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/sha512 b/deps/checksums/SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/sha512 new file mode 100644 index 0000000000000..e6b8446587554 --- /dev/null +++ b/deps/checksums/SHA-8fa221ddc8f3b418d9929084f1644f4c32c9a27e.tar.gz/sha512 @@ -0,0 +1 @@ +7b1df257616aaa9067f822a88dddf52bc10f9f61e3a0728e33e595455bd7167e680c50371c41cb25f8c8a9fb9cf40225847df1523a6c6f3571a471f7163f563c diff --git a/deps/checksums/SHA-aaf2df61ff8c3898196587a375d3cf213bd40b41.tar.gz/md5 b/deps/checksums/SHA-aaf2df61ff8c3898196587a375d3cf213bd40b41.tar.gz/md5 deleted file mode 100644 index 3b51189e187a3..0000000000000 --- a/deps/checksums/SHA-aaf2df61ff8c3898196587a375d3cf213bd40b41.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -dec1d21e890c88e57a0d4eb085633d57 diff --git a/deps/checksums/SHA-aaf2df61ff8c3898196587a375d3cf213bd40b41.tar.gz/sha512 b/deps/checksums/SHA-aaf2df61ff8c3898196587a375d3cf213bd40b41.tar.gz/sha512 deleted file mode 100644 index cbe1ff2eea29e..0000000000000 --- a/deps/checksums/SHA-aaf2df61ff8c3898196587a375d3cf213bd40b41.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -fb611794a539c6725000ff6eda13e0af5dd3f82e22466bdff650ffa0e4edbba5ac4707195035531645a4161ecbb5f873f4f6b1040ce33e9b1adf9c1d34187718 diff --git a/deps/checksums/SparseArrays-1b4933ccc7b1f97427ff88bd7ba58950021f2c60.tar.gz/md5 b/deps/checksums/SparseArrays-1b4933ccc7b1f97427ff88bd7ba58950021f2c60.tar.gz/md5 deleted file mode 100644 index 41d78a15f2ddb..0000000000000 --- a/deps/checksums/SparseArrays-1b4933ccc7b1f97427ff88bd7ba58950021f2c60.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -a643f01ee101a274d86d6469dd6a9d48 diff --git a/deps/checksums/SparseArrays-1b4933ccc7b1f97427ff88bd7ba58950021f2c60.tar.gz/sha512 b/deps/checksums/SparseArrays-1b4933ccc7b1f97427ff88bd7ba58950021f2c60.tar.gz/sha512 deleted file mode 100644 index 1868f9a865af5..0000000000000 --- a/deps/checksums/SparseArrays-1b4933ccc7b1f97427ff88bd7ba58950021f2c60.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -09a86606b28b17f1066d608374f4f8b2fcdcd17d08a8fa37b08edea7b27a9e6becadc8e8e93b1dcc1477dc247255d6a8ded4f8e678f46d80c9fd0ad72a7f3973 diff --git a/deps/checksums/SparseArrays-4fd3aad5735e3b80eefe7b068f3407d7dd0c0924.tar.gz/md5 b/deps/checksums/SparseArrays-4fd3aad5735e3b80eefe7b068f3407d7dd0c0924.tar.gz/md5 new file mode 100644 index 0000000000000..4c75f5c1f619a --- /dev/null +++ b/deps/checksums/SparseArrays-4fd3aad5735e3b80eefe7b068f3407d7dd0c0924.tar.gz/md5 @@ -0,0 +1 @@ +308e26cc6171656caaa7f6ba07e83d1c diff --git a/deps/checksums/SparseArrays-4fd3aad5735e3b80eefe7b068f3407d7dd0c0924.tar.gz/sha512 b/deps/checksums/SparseArrays-4fd3aad5735e3b80eefe7b068f3407d7dd0c0924.tar.gz/sha512 new file mode 100644 index 0000000000000..ad389d6e42048 --- /dev/null +++ b/deps/checksums/SparseArrays-4fd3aad5735e3b80eefe7b068f3407d7dd0c0924.tar.gz/sha512 @@ -0,0 +1 @@ +62b94ad0dca0d62e5753f50aef806ebdb5c8b56b241a285957190845be21fc6b8c8f93089b6f627795f6d7f2b1b01118bcff87c21102a3f3bae6d4c408362681 diff --git a/deps/checksums/Statistics-68869af06e8cdeb7aba1d5259de602da7328057f.tar.gz/md5 b/deps/checksums/Statistics-68869af06e8cdeb7aba1d5259de602da7328057f.tar.gz/md5 deleted file mode 100644 index 9ba42f555d535..0000000000000 --- a/deps/checksums/Statistics-68869af06e8cdeb7aba1d5259de602da7328057f.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -01b84d67052d1558e51619d5159e7a8b diff --git a/deps/checksums/Statistics-68869af06e8cdeb7aba1d5259de602da7328057f.tar.gz/sha512 b/deps/checksums/Statistics-68869af06e8cdeb7aba1d5259de602da7328057f.tar.gz/sha512 deleted file mode 100644 index 31c9c6ca42cec..0000000000000 --- a/deps/checksums/Statistics-68869af06e8cdeb7aba1d5259de602da7328057f.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -6ab55ba6f93d2e8b34b19f53cb51a4bfc97b336d451b98f7b95ff81f04fee4fb90a2e4d04aa4bbf3ccffc99c36d9c82c9d00dbae283474308de4a27a91c2e0b7 diff --git a/deps/checksums/Statistics-d49c2bf4f81e1efb4980a35fe39c815ef8396297.tar.gz/md5 b/deps/checksums/Statistics-d49c2bf4f81e1efb4980a35fe39c815ef8396297.tar.gz/md5 new file mode 100644 index 0000000000000..3956c67f7fd47 --- /dev/null +++ b/deps/checksums/Statistics-d49c2bf4f81e1efb4980a35fe39c815ef8396297.tar.gz/md5 @@ -0,0 +1 @@ +acf2bb0ea30132602e172e2f5f6274b4 diff --git a/deps/checksums/Statistics-d49c2bf4f81e1efb4980a35fe39c815ef8396297.tar.gz/sha512 b/deps/checksums/Statistics-d49c2bf4f81e1efb4980a35fe39c815ef8396297.tar.gz/sha512 new file mode 100644 index 0000000000000..051f2d0a862c3 --- /dev/null +++ b/deps/checksums/Statistics-d49c2bf4f81e1efb4980a35fe39c815ef8396297.tar.gz/sha512 @@ -0,0 +1 @@ +5e879fe79bae19b62f81659a102602271c73a424faf4be069ab31fb50e30b536a8c7b3692127763000cc1dbab69c93ac3da7bace5f093d05dce2d652fb221d52 diff --git a/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/md5 b/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/md5 deleted file mode 100644 index 8d78dd7b0a11b..0000000000000 --- a/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -f053c84279a8920f355f202e605842af diff --git a/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/sha512 b/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/sha512 deleted file mode 100644 index 5a8ca888c38f8..0000000000000 --- a/deps/checksums/StyledStrings-056e843b2d428bb9735b03af0cff97e738ac7e14.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -b6f4c1d6c0dc73a520472746c96adff506e5405154e4b93d419e07b577b01804d2fc87d4a6cac48a136777579bebf8388c2c1e54f849b51e233138d482146b4f diff --git a/deps/checksums/StyledStrings-8985a37ac054c37d084a03ad2837208244824877.tar.gz/md5 b/deps/checksums/StyledStrings-8985a37ac054c37d084a03ad2837208244824877.tar.gz/md5 new file mode 100644 index 0000000000000..0fd8e8966e068 --- /dev/null +++ b/deps/checksums/StyledStrings-8985a37ac054c37d084a03ad2837208244824877.tar.gz/md5 @@ -0,0 +1 @@ +411277f3701cc3e286ec8a84ccdf6f11 diff --git a/deps/checksums/StyledStrings-8985a37ac054c37d084a03ad2837208244824877.tar.gz/sha512 b/deps/checksums/StyledStrings-8985a37ac054c37d084a03ad2837208244824877.tar.gz/sha512 new file mode 100644 index 0000000000000..0b495aefef55d --- /dev/null +++ b/deps/checksums/StyledStrings-8985a37ac054c37d084a03ad2837208244824877.tar.gz/sha512 @@ -0,0 +1 @@ +95a7e92389f6fd02d3bec17ec0201ba41316aa2d7c321b14af88ccce8246fd0000ed2c0cc818f87cb81f7134304233db897f656426a00caac1bc7635056260c2 diff --git a/deps/checksums/cacert-2024-03-11.pem/md5 b/deps/checksums/cacert-2024-03-11.pem/md5 deleted file mode 100644 index 618b6c74efdd4..0000000000000 --- a/deps/checksums/cacert-2024-03-11.pem/md5 +++ /dev/null @@ -1 +0,0 @@ -594084120d27f482b1dc48f558d12d48 diff --git a/deps/checksums/cacert-2024-03-11.pem/sha512 b/deps/checksums/cacert-2024-03-11.pem/sha512 deleted file mode 100644 index 441b8e84707b0..0000000000000 --- a/deps/checksums/cacert-2024-03-11.pem/sha512 +++ /dev/null @@ -1 +0,0 @@ -31f03cc19566d007c4cffdad2ada71d99b4734ad7b13bc4f30d73d321f40cbe13b87a801aa61d9788207a851cc1f95a8af8ac732a372d45edb932f204bce3744 diff --git a/deps/checksums/cacert-2024-11-26.pem/md5 b/deps/checksums/cacert-2024-11-26.pem/md5 new file mode 100644 index 0000000000000..865c6abf3e77a --- /dev/null +++ b/deps/checksums/cacert-2024-11-26.pem/md5 @@ -0,0 +1 @@ +92c13373d7dbe43bdc167479274a43e2 diff --git a/deps/checksums/cacert-2024-11-26.pem/sha512 b/deps/checksums/cacert-2024-11-26.pem/sha512 new file mode 100644 index 0000000000000..d51605348faf4 --- /dev/null +++ b/deps/checksums/cacert-2024-11-26.pem/sha512 @@ -0,0 +1 @@ +26c6fa1ac7bcfd523f9ab9e6c2d971103ccfc610ad0df504d4e9b064dad74576d77240c052b808f4c37c9240302a7e973a20f79ee39ac7bf3201a6fa9f0dfa96 diff --git a/deps/checksums/llvm b/deps/checksums/llvm index 1b375e6e72c5d..fbbb34480d893 100644 --- a/deps/checksums/llvm +++ b/deps/checksums/llvm @@ -110,38 +110,40 @@ LLVM.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/md5/0a4ce LLVM.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.asserts.tar.gz/sha512/7fd5c69bfde6264ae4e548ec9c399dd09b1a5fe4b9cced23d6bc4257f0f67874b838d53ee8d6eef7fc01ee9d086758e06f00bb0a0388b97de2eb85143a47192a LLVM.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/da2430483844823d31bcc5f302252ac2 LLVM.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/19e9168b44d40acdc0d924e16f93c315237207a4441ae78997c511135872e557f654236bc859453069671145e81e961ac93c9dfa601d1b6631b9ccfa09b929b3 -LLVMLibUnwind.v14.0.6+0.aarch64-apple-darwin.tar.gz/md5/d8584e0e3dc26ea7404d3719cea9e233 -LLVMLibUnwind.v14.0.6+0.aarch64-apple-darwin.tar.gz/sha512/7a0396eaace91b9b4d013c209605d468a7ff9b99ede9fdd57602539a6fa6f3ea84a440f32840056a1234df3ef1896739ea0820fee72b4f208096c553fc54adb9 -LLVMLibUnwind.v14.0.6+0.aarch64-linux-gnu.tar.gz/md5/d6edea561b61173d05aa79936e49f6b7 -LLVMLibUnwind.v14.0.6+0.aarch64-linux-gnu.tar.gz/sha512/9fbe29ec6a33c719bc9a4dd19911ceded9622269c042192d339a6cf45aa8209ad64c424167c094ca01293438af5930f091acba0538b3fe640a746297f5cc8cb3 -LLVMLibUnwind.v14.0.6+0.aarch64-linux-musl.tar.gz/md5/3ec68c87e4bddd024ee0ca6adc2b3b96 -LLVMLibUnwind.v14.0.6+0.aarch64-linux-musl.tar.gz/sha512/be3cd9d5510c2693dee1494c36c479d32311ff83f5b2d31c08508a3dd370788961ce46e9025afe148a0febd05942fd294370a357dd717bee353d8a108617f6de -LLVMLibUnwind.v14.0.6+0.armv6l-linux-gnueabihf.tar.gz/md5/8ca5a926d69124225d485d679232a54f -LLVMLibUnwind.v14.0.6+0.armv6l-linux-gnueabihf.tar.gz/sha512/353f540b342bc54877e7a41fe65c9eeac525fd91bf4cddbe1b3ec2ed93c3751beaf8316a4d31530502b067100b160301262e10cbe4407db3abf1ceb5d9a74eb2 -LLVMLibUnwind.v14.0.6+0.armv6l-linux-musleabihf.tar.gz/md5/4e5b576958f2a2e708eb5918ceef0de0 -LLVMLibUnwind.v14.0.6+0.armv6l-linux-musleabihf.tar.gz/sha512/2e98c472d3ee25c2e062efa4eb21ac9cfc49b26ea9d99ad4a8e7660c4c09f121d31193bd161f54ea332ce94785d601897311e9e6668adb1e25e2b666e0d5bb3f -LLVMLibUnwind.v14.0.6+0.armv7l-linux-gnueabihf.tar.gz/md5/1c81a886e799663ce8d04400c5b516a9 -LLVMLibUnwind.v14.0.6+0.armv7l-linux-gnueabihf.tar.gz/sha512/236b78b9a17eaae74ab07349ac8dde16c3abbd48e0d075abd1c195d60efff48e2fbf799554df114ea3d3dba937e0369430a2788bde2a1201126e026ef6cdac42 -LLVMLibUnwind.v14.0.6+0.armv7l-linux-musleabihf.tar.gz/md5/0371f43ebcb571d0a635739252b88986 -LLVMLibUnwind.v14.0.6+0.armv7l-linux-musleabihf.tar.gz/sha512/605318ae3737e26ff89d6291311a7db3bc3ec7c8d1f2e72ae40fd3d9df0754ee2ebfb77687122605f26d76d62effb85157bc39982814920d5af46c124e71a5ff -LLVMLibUnwind.v14.0.6+0.i686-linux-gnu.tar.gz/md5/cd3f1cdf404b6102754ced4bd3a890f6 -LLVMLibUnwind.v14.0.6+0.i686-linux-gnu.tar.gz/sha512/65fe2c5b1e04da1e1d8111a0b0083fa0fa9447eaea7af7a018c09fe6d5506566c491bbad296a7be8c488ca3495016ae16a6879d69f057f8866d94910147dee03 -LLVMLibUnwind.v14.0.6+0.i686-linux-musl.tar.gz/md5/abac9b416d2ba5abcf5ce849f43ffa96 -LLVMLibUnwind.v14.0.6+0.i686-linux-musl.tar.gz/sha512/fed677ed6f103c56eb9dd4578fa37a56ed2a4bc803aa1997c5af19762a623d2f82db1f72f429448d66fcef3b37af2104e6cb782f023aaabef086a921a862b042 -LLVMLibUnwind.v14.0.6+0.i686-w64-mingw32.tar.gz/md5/4c71ffd7c8cabb1c0ed6290b193883c5 -LLVMLibUnwind.v14.0.6+0.i686-w64-mingw32.tar.gz/sha512/6b1421a3268170467225112167cdb33fec962181993a2dad5594d4ee0623ac88ee0588cdc7d0656dc1cb9129ef96f621a97a224731cd161134d7d63c8fd32c16 -LLVMLibUnwind.v14.0.6+0.powerpc64le-linux-gnu.tar.gz/md5/06faf505f0dc354afcd01113cfc57af2 -LLVMLibUnwind.v14.0.6+0.powerpc64le-linux-gnu.tar.gz/sha512/1f9dfbd403e2ce121e126c217baede178cb1323012bb5e3cd1f778ff51e4216aed9dd69036e2baffbd60a6f5ae438ddaba6c13809459e94bb00be3f7bfc8c30e -LLVMLibUnwind.v14.0.6+0.x86_64-apple-darwin.tar.gz/md5/516a11d99306e3f214968a7951b07a06 -LLVMLibUnwind.v14.0.6+0.x86_64-apple-darwin.tar.gz/sha512/885738599bbd96f20083f9b9368ce3f243bd5868d3ac9a45189de6cb40b6664a6dcdaece159989e504670231db8c2addfa8d544003eb0cdabba960e4ab6a4470 -LLVMLibUnwind.v14.0.6+0.x86_64-linux-gnu.tar.gz/md5/d851b90ea3f9664774316169fc494e21 -LLVMLibUnwind.v14.0.6+0.x86_64-linux-gnu.tar.gz/sha512/a1f529454f0881baaa508481ba97ecffb040fa92141b4cbc72278adcf8b84f0766fa918aea7fb99ce690c4fd80c36fec365987625db42f4e7bb36ad24ce177d0 -LLVMLibUnwind.v14.0.6+0.x86_64-linux-musl.tar.gz/md5/dc4e86eb2effe1f6cb0d0ceda635f226 -LLVMLibUnwind.v14.0.6+0.x86_64-linux-musl.tar.gz/sha512/c52de384853890f9df81aa9e422c1ba3fde12b2ae9c7b60b9ecdc6d0c88eab495dd336af2b6cd2c31d6eddcd0a213954eadbc7884bc39ce2039cec672eac32fe -LLVMLibUnwind.v14.0.6+0.x86_64-unknown-freebsd.tar.gz/md5/8477e3624c73a820d8ab82a53e1e10fa -LLVMLibUnwind.v14.0.6+0.x86_64-unknown-freebsd.tar.gz/sha512/32ce031245a5b59a779cd77fa3c9bf05ee59e48c913b75d4964bea49f37da232c59a42ad993f7b5edc88322148c1d7394984349682bfce3b69d33a51756ac8e3 -LLVMLibUnwind.v14.0.6+0.x86_64-w64-mingw32.tar.gz/md5/7be93eccbdb0aff427c43af651073d66 -LLVMLibUnwind.v14.0.6+0.x86_64-w64-mingw32.tar.gz/sha512/89a61a81ec664c72107ac09e717200b00434350bf77064267180bc0c101a59e0ee8c8af4dd6fe75eacdeb14e82743c138b2fc558ca08550d8796b8db93f89da4 +LLVMLibUnwind.v19.1.4+0.aarch64-apple-darwin.tar.gz/md5/aace388fc1ece82ea524c582506ae931 +LLVMLibUnwind.v19.1.4+0.aarch64-apple-darwin.tar.gz/sha512/c0211340a05630bcfcf9e3bab97da3e9f07e596e8d391427fa919c99502ab0a09878eda379254f379511884347f7e742872e8589f9b6ccbc2d126a5dfe0a350f +LLVMLibUnwind.v19.1.4+0.aarch64-linux-gnu.tar.gz/md5/942d0b4ffb8bfd743cdafebf5bdfdbb3 +LLVMLibUnwind.v19.1.4+0.aarch64-linux-gnu.tar.gz/sha512/ec68df054c6694d17cb7f5c389adc4b8b855023f9ca03713d21f1f0c58de2b90166a9f3981b81da5f817f6b09f85fb11e85732d6c78f1d115d6aecf326dc20a1 +LLVMLibUnwind.v19.1.4+0.aarch64-linux-musl.tar.gz/md5/2c27d3c130f54e38e6639ebf7095f743 +LLVMLibUnwind.v19.1.4+0.aarch64-linux-musl.tar.gz/sha512/d348cc1f87927a3d36cd3f2587cf4161dbdc9f3555900ee338857d806384c0cff8fbe67bef97cad0d3098cc8c7f149aac699f3defe87db70fffcc94d681810b6 +LLVMLibUnwind.v19.1.4+0.aarch64-unknown-freebsd.tar.gz/md5/6bb1466d45159193407f27201a443ddc +LLVMLibUnwind.v19.1.4+0.aarch64-unknown-freebsd.tar.gz/sha512/da6da450e6fba5d501be13d83bc9133796b92e1b3a6cc7cb97470cc7476a369fcd8ddbc9267f03fa4cbe1f2484359eeb70fb629b26c9a1d7ea0065c5a671e1b9 +LLVMLibUnwind.v19.1.4+0.armv6l-linux-gnueabihf.tar.gz/md5/2cdf57d34b1db677498dfc5d89501599 +LLVMLibUnwind.v19.1.4+0.armv6l-linux-gnueabihf.tar.gz/sha512/217c15e1bfdc72014dd26321eb46ae9cfadb7839c693caf3c974989ee2036781cf7e62bb7175766f5171bf32de53a95598ef463c70a0ac64ec012ca9bc19e6df +LLVMLibUnwind.v19.1.4+0.armv6l-linux-musleabihf.tar.gz/md5/110c80b549d1f80faa36a3e0b39a11b4 +LLVMLibUnwind.v19.1.4+0.armv6l-linux-musleabihf.tar.gz/sha512/b9151aaaaae4adf5da5701ee5962d712def509f85101dae485b905f73391d8658b5a0a58ea1a4c68cc3bc68d7e17d557c05c98d33d907cdb512513ffff75765b +LLVMLibUnwind.v19.1.4+0.armv7l-linux-gnueabihf.tar.gz/md5/bf50011ce9e4c82d49e61e868b27ea23 +LLVMLibUnwind.v19.1.4+0.armv7l-linux-gnueabihf.tar.gz/sha512/d08faae71010e4a7d25a16374249ff1740ed7883e260e544e4fb0f0d3758d2eb76fea93433cb1987850f54f1ae6528b6336fc2e1db9b46f49defd870e97f8a94 +LLVMLibUnwind.v19.1.4+0.armv7l-linux-musleabihf.tar.gz/md5/142118a84c1b959b0b202d51072168f9 +LLVMLibUnwind.v19.1.4+0.armv7l-linux-musleabihf.tar.gz/sha512/71ac937417f5f2226b8952c925fff94b553de8a29fc45fee6c0fef53a9cf8c07979c60408c8efcf827b260bc3a287059aefa24e050393f2e09b65af45b60d07f +LLVMLibUnwind.v19.1.4+0.i686-linux-gnu.tar.gz/md5/1bcd011ba209cc840647c684dcad9631 +LLVMLibUnwind.v19.1.4+0.i686-linux-gnu.tar.gz/sha512/8309c3d82d0a94c4c7a8b72720702f5cb0c97f316492217f1eebfc0dc33b4e9c7c8af5c6ee3700ea0c1cc0fd66c90a52389c2aaaaeb67f6278e53e33a476abc1 +LLVMLibUnwind.v19.1.4+0.i686-linux-musl.tar.gz/md5/8db27a7ab4a23febfd6a8eb2f65cd611 +LLVMLibUnwind.v19.1.4+0.i686-linux-musl.tar.gz/sha512/dc7839d2c9a258b122985eb35096e0000561598c54fbd1c5f269921146e6e85589c6f60a0fb964ebfc78af703045373999163253ad2c8f09475bf6bdb923a59f +LLVMLibUnwind.v19.1.4+0.i686-w64-mingw32.tar.gz/md5/7de74ebac40c9425f619c7f8b309de00 +LLVMLibUnwind.v19.1.4+0.i686-w64-mingw32.tar.gz/sha512/f28f4e8c25cdc06c8d363735e1914c748c150a962c37dfa8a45a3ba514d3fa1b6c551809b8d7f668b258c3165674f012ee6a18f36421e624f38ece27db755a3f +LLVMLibUnwind.v19.1.4+0.powerpc64le-linux-gnu.tar.gz/md5/c5277c6c127ccc5fa66867ddeb6f93a2 +LLVMLibUnwind.v19.1.4+0.powerpc64le-linux-gnu.tar.gz/sha512/b3d61aee2187c185be1b1b26edaccea66da750931c1216db1f3e89393c1d2c101335d791f0124282320084e697386f395951035e5071da23ecd55133fad472fc +LLVMLibUnwind.v19.1.4+0.x86_64-apple-darwin.tar.gz/md5/64d459ec7cb7d70b89f5ed62a1261425 +LLVMLibUnwind.v19.1.4+0.x86_64-apple-darwin.tar.gz/sha512/861130348376c8a54b2aa8c86d9d338a4b5fb88d3d2745578dcf15e0f477f518c07a505ce86c898c87142a7c5bf2e1ce43daedecc386a7f3bde67af8e6a56e64 +LLVMLibUnwind.v19.1.4+0.x86_64-linux-gnu.tar.gz/md5/2702948c4171ad35f521e15ee4ebcc8e +LLVMLibUnwind.v19.1.4+0.x86_64-linux-gnu.tar.gz/sha512/306759ae9064a9746474c53b674eb0b9da7cef6271094009c3244542295ef7a86cb77096b4a18dc2e50628c6ab02e2f1c6e39a1401e86fe4743410ae8d782126 +LLVMLibUnwind.v19.1.4+0.x86_64-linux-musl.tar.gz/md5/a7f9ea5dfbd4760b5a33c97581ad4b95 +LLVMLibUnwind.v19.1.4+0.x86_64-linux-musl.tar.gz/sha512/08add6b1a4e90f50fbceea6d72a476fba3a2b271f44bf64f06b53f35dfecc756f71843d54d0895a2f62d56df24f3675619cf3220215acb2e0a574696c6fa630c +LLVMLibUnwind.v19.1.4+0.x86_64-unknown-freebsd.tar.gz/md5/05f5b916fa639a68096cc73fb82007f8 +LLVMLibUnwind.v19.1.4+0.x86_64-unknown-freebsd.tar.gz/sha512/0a137168c466861fdbdbef86dec96ece0d4c10f87fdc2dd729b445deb0fd59b214241b62644da77581a0100826e07dacf81fa060e67e35ff38df0d6807cb618b +LLVMLibUnwind.v19.1.4+0.x86_64-w64-mingw32.tar.gz/md5/bb073cb86c821a70b845bd5de0edc2d9 +LLVMLibUnwind.v19.1.4+0.x86_64-w64-mingw32.tar.gz/sha512/24d206c65c7be34485a1492250a9ca958e70be7057b981940bc24c4822e50e3963c9f88f42892ba2ea6df17fedb2783ace1693aeac74f200a5ca6033a14d6cb9 libLLVM.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/md5/f7ce9539d0802dd4b5e5e673d36d1a99 libLLVM.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.asserts.tar.gz/sha512/7a54be16ccc327731c802380d29f2c9ee5e635cd6af0b7eb6b69e9d3b0b4fecb74147359af182def3b016ec4445891bdb91eb0d541b783e451e8263968c25161 libLLVM.v18.1.7+3.aarch64-apple-darwin-llvm_version+18.tar.gz/md5/cd946ab46745ce71ad7438cf0f30cfd0 @@ -256,5 +258,5 @@ libLLVM.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/md5/0e21a6d22d libLLVM.v18.1.7+3.x86_64-w64-mingw32-cxx11-llvm_version+18.tar.gz/sha512/efbbad538c6f8b773d7ef1019a9b754e1ce7da59ea5f00f452fa7f7cc93c40f248762eb7f708e3d2fa7f9bdbc0b680d6e6502a07bbca0d4e701b51b0565d625e llvm-julia-18.1.7-2.tar.gz/md5/5c0ae4abc4ce31a86d5d6d4ecabc2683 llvm-julia-18.1.7-2.tar.gz/sha512/b4d1dde929a8670eec1a9b25abe23fbc926a922e61b60ed99b52b440cd07cb026e7f746878292db4cd0cb422d9b87ecc4ee4b2b141f8e9411855d18da51facb9 -llvm-project-14.0.6.tar.xz/md5/0b3373eded268dc27e2e874872fed4eb -llvm-project-14.0.6.tar.xz/sha512/6fc6eeb60fac698702d1aac495fc0161eb7216a1f8db2020af8fccec5837831f7cc20dc2a169bf4f0b5f520748280b4a86621f3697d622aa58faaa45dbfaad13 +llvm-project-19.1.4.tar.xz/md5/1e13043b18558e4346ea3769094c9737 +llvm-project-19.1.4.tar.xz/sha512/a586f8a41dde5e0d9ca6d8c58e9ef2a2e59b70a86d2e2c46106dc31b5c096bb80af0cdbdb486179e9cc676a540099f49a1c2db9e5e84c50362db1f72e9af6906 diff --git a/deps/checksums/pcre b/deps/checksums/pcre index 018ffd5201653..0c2732f8cc2b5 100644 --- a/deps/checksums/pcre +++ b/deps/checksums/pcre @@ -1,36 +1,36 @@ -PCRE2.v10.43.0+1.aarch64-apple-darwin.tar.gz/md5/f1bee27b8d9465c14eaf9362701fb795 -PCRE2.v10.43.0+1.aarch64-apple-darwin.tar.gz/sha512/33b8f6e3703f0a52cd2d57897c28e35fb3c63af459296a2fef4e414dc99239617833b2ab176068d6aab690122a34a9ab9b6042dfff54b5a30ad60429a809818d -PCRE2.v10.43.0+1.aarch64-linux-gnu.tar.gz/md5/c55a569260e302f315f4a1bd185346ab -PCRE2.v10.43.0+1.aarch64-linux-gnu.tar.gz/sha512/be4d2883e69d562898a157424b2baa146fe79545a8c10935cf25b54e498ca2c14fae026fa0d958d175895fe2cb695d0f96ef7f09fecbf54e1cee4a55b81a382b -PCRE2.v10.43.0+1.aarch64-linux-musl.tar.gz/md5/fb041ccace415ccc26263968c6435a47 -PCRE2.v10.43.0+1.aarch64-linux-musl.tar.gz/sha512/06672ebe18e0f6bfa1dd2d5c02e10d9fd67236a73fd38ee2e8f4496d98f297f7866760f0be3b9cebeca348a5d748a3719e416b84cec96a90c71eac55afbbd905 -PCRE2.v10.43.0+1.aarch64-unknown-freebsd.tar.gz/md5/8c73fe6faa94102616cfafcc6cc1bf9d -PCRE2.v10.43.0+1.aarch64-unknown-freebsd.tar.gz/sha512/464a892e646fb5aa028d2e96e6f8beaa0c15f0ef56a6ba3388cba4ce85151448b0dfd51357a3e8dea4505957394ffbab14ceb29b9fc73a67e2b2f54dd28a7aed -PCRE2.v10.43.0+1.armv6l-linux-gnueabihf.tar.gz/md5/4f303a4cbf26abb7bf4ffb8bfe3d636d -PCRE2.v10.43.0+1.armv6l-linux-gnueabihf.tar.gz/sha512/dddb3b227ee48d8329f6c65c5d0fce9f460eccaec98594a05bf28d1d9af01397cf7ef86c96e88b0e96030a7f6d8406461f78dd5fa558db8fc8f7bfb3b522ed54 -PCRE2.v10.43.0+1.armv6l-linux-musleabihf.tar.gz/md5/eade1fff90404bf3584fd15b62be0cfa -PCRE2.v10.43.0+1.armv6l-linux-musleabihf.tar.gz/sha512/351f6fa11c39b90fcc4086bd00b1b1126ed92272595f0b745757ca4e7e360c84d244446a871029245c3bcf838b23f42d908f858e44fae7deb9002a36cb76753c -PCRE2.v10.43.0+1.armv7l-linux-gnueabihf.tar.gz/md5/daa0a34b2cf0b71a6f8e1f9456cd4b06 -PCRE2.v10.43.0+1.armv7l-linux-gnueabihf.tar.gz/sha512/ae72956ae7a9a5f315bfc816fdbb500937a170dfea306a28289ec9eac57d883cf2fa5a467ce9406eea80546b632a272c63bbb48b89ebe6d9f69d30366fd84180 -PCRE2.v10.43.0+1.armv7l-linux-musleabihf.tar.gz/md5/90bfb9e4efd7c92a2bb6a1a48fd88ecb -PCRE2.v10.43.0+1.armv7l-linux-musleabihf.tar.gz/sha512/147ac98d82fec4695de0c43c87d3d9242b9c024bc6df7ad7504d17ef6a12a029ed703c4deade0e2b24faf5283d66309f880d62f8c4834f27b2cc8889587d7abe -PCRE2.v10.43.0+1.i686-linux-gnu.tar.gz/md5/6fde649bf449c4122438fff32c0706ab -PCRE2.v10.43.0+1.i686-linux-gnu.tar.gz/sha512/edfaa15490497723c095eaa5df26194637b0606e9dce7b89b400024ef8ac42e21f010bb31c2cee5c735ce82fc8de0c42bf2b35b095a1e70a9a111d3bfba6da64 -PCRE2.v10.43.0+1.i686-linux-musl.tar.gz/md5/73aa8d13cc48338a5071e30b3a899109 -PCRE2.v10.43.0+1.i686-linux-musl.tar.gz/sha512/200e2d3ffd68f49b76c70a5be80cb0ae9703049214674485a2ab24abaaea7aefd6dec2042a14bd48cc52b04379f57322ec1e1788dc8c00896e1074921725d9cc -PCRE2.v10.43.0+1.i686-w64-mingw32.tar.gz/md5/4ddf0f31c97463e5216ed71afc4fb014 -PCRE2.v10.43.0+1.i686-w64-mingw32.tar.gz/sha512/75903d81668a66a5c4d830e31657391d507883943d86245998f224655406dcc6a95ba4f5fad20dcf608a98d6ccf49abe50107993448669b03c42a878d8466611 -PCRE2.v10.43.0+1.powerpc64le-linux-gnu.tar.gz/md5/64cb71080da1c97eba3a440ff53d298c -PCRE2.v10.43.0+1.powerpc64le-linux-gnu.tar.gz/sha512/16348b96a45c7a7d86775cb1d082b4d1c060e5a8acfb37554885d8da0db87430d8a40f834f008a90f4a7b1c07b8329df96836ba0430ecec506a143b7347bb101 -PCRE2.v10.43.0+1.x86_64-apple-darwin.tar.gz/md5/31bbb2485f5e06c3616fb061ffb2f022 -PCRE2.v10.43.0+1.x86_64-apple-darwin.tar.gz/sha512/3284ee63ed1e5631267efacb354a1d90bd1b7db0bc81d7233c9580eee4a9af06093c1c4f240786c34299df89a36a17ed92598fc302074f5a200c56cc96081bf1 -PCRE2.v10.43.0+1.x86_64-linux-gnu.tar.gz/md5/2fb7e0e9bbc32dddf543f4d395b50d3f -PCRE2.v10.43.0+1.x86_64-linux-gnu.tar.gz/sha512/5a533a3a01f817689077377835dc88edf914459ed0df7323f8f4dba602a47fd6af700075feb1f448221366b1cf7e2d717c615a5c506eb4ca2db9c600fd290fb0 -PCRE2.v10.43.0+1.x86_64-linux-musl.tar.gz/md5/b432063c93aa477dd0883428191041f8 -PCRE2.v10.43.0+1.x86_64-linux-musl.tar.gz/sha512/36475e90e29d7324046fe1da669fb37f667245a680df23f3978394964e14eb9bda3fd56703ad62cd56e27a5af77d8b6b9612516457ae803cef0627bd919e4628 -PCRE2.v10.43.0+1.x86_64-unknown-freebsd.tar.gz/md5/6124870a991e70c2ed8a64d8f3258760 -PCRE2.v10.43.0+1.x86_64-unknown-freebsd.tar.gz/sha512/4645a2d05af149467f2e4ce5e48853b57c585d6a5950c70726d04bc71a5d82f50809af141ad98e99671e764ac74965651ecad1c49a849caa8fd077c7f4911c7c -PCRE2.v10.43.0+1.x86_64-w64-mingw32.tar.gz/md5/cc4e9f45471f538c1fefa657ab99b878 -PCRE2.v10.43.0+1.x86_64-w64-mingw32.tar.gz/sha512/eed45e621263cb307b6e8ab42e2c12cf9e1d61ad523760fd721a85765c359b74d580752ca7c3d222e0cba26a74e872a6d43dbf2dbf08e4733a3e709417e48651 -pcre2-10.43.tar.bz2/md5/c8e2043cbc4abb80e76dba323f7c409f -pcre2-10.43.tar.bz2/sha512/8ac1520c32e9e5672404aaf6104e23c9ee5c3c28ad28ff101435599d813cbb20e0491a3fd34e012b4411b3e0366a4c6dfa3f02d093acaa6ff0ab25478bb7ade9 +PCRE2.v10.44.0+0.aarch64-apple-darwin.tar.gz/md5/14de26cfc0f6ff7635fac39e81e81a27 +PCRE2.v10.44.0+0.aarch64-apple-darwin.tar.gz/sha512/45079ecca5f4966a32895fcc63585f1dd60f306dc1cb5c098d42452fcff67f7f6b405c200a15747af4680151bb6a6374832a0119b8ddd743d2ed13d0beaef7c9 +PCRE2.v10.44.0+0.aarch64-linux-gnu.tar.gz/md5/3cf179ed36d37bff698ab81cf3d5797b +PCRE2.v10.44.0+0.aarch64-linux-gnu.tar.gz/sha512/db93e5a5c0c46b5536ed49515682d9bfe1d23f6ba8ae2468289ec8f2160140f39f5606a3c7095f45251f3663d8ccf2d6d7e5e8b1efb21c39bbf9a13b6ec60ef9 +PCRE2.v10.44.0+0.aarch64-linux-musl.tar.gz/md5/02baa415218f581a5ceeb7bf7fc0a090 +PCRE2.v10.44.0+0.aarch64-linux-musl.tar.gz/sha512/1685f37ed8f465ecc2f738fdf65d20bb1806934ff2c50194882282fb6c3900121c61c39210e4c0b89847493bfc3e15bb7b9136b0d968103b47c8662a78b412fe +PCRE2.v10.44.0+0.aarch64-unknown-freebsd.tar.gz/md5/4de065ea59ab4f622b46079df1d9d941 +PCRE2.v10.44.0+0.aarch64-unknown-freebsd.tar.gz/sha512/aa6df9edfb690d155a8b5a9390db7ca11622ac0020174cf070a33a075801bfe43bd4c80b8e28017989a8b7374d39897cdcf72ab0e1962e3e234239975f7ac0b4 +PCRE2.v10.44.0+0.armv6l-linux-gnueabihf.tar.gz/md5/f8a0907fbb20a06507fce849db098c4f +PCRE2.v10.44.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/3f5bcc1742380a31683a81740d55e198d7ec8d8ea5a13d6d0556d6603e4fadbf0dc648093c44e36dd6d3793c52a5e3dae6f2f459c73e3d3b5a005f3395d26772 +PCRE2.v10.44.0+0.armv6l-linux-musleabihf.tar.gz/md5/8854c24183441aa6fd21989c00888904 +PCRE2.v10.44.0+0.armv6l-linux-musleabihf.tar.gz/sha512/a74d9378f071dc4cb021e5171d66cd4ac5de3b348e993fc90d824ce5d2f554f7c8af7af55ec31d874d302aaba7d542b6505cc5963e53656c28026a06a53ed48b +PCRE2.v10.44.0+0.armv7l-linux-gnueabihf.tar.gz/md5/04960309ee7cf69a53e280878d5880ef +PCRE2.v10.44.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/a1644daf036daa3799368598427c87c23bcfdddac55a0d06adca08a2e9d617c893285855af562101b05129d0ed0d84d22f5a8a1703316ecd09aa1752b8330eef +PCRE2.v10.44.0+0.armv7l-linux-musleabihf.tar.gz/md5/1335defc6090be76c509840633f7cdfb +PCRE2.v10.44.0+0.armv7l-linux-musleabihf.tar.gz/sha512/9595052eeae4da413b930b14d7e89359a29220cd9e908325e0b7788c8f4a2feb2134e78a0d8f56007787f0fefadc9de31750db6104bbdd048fa50e1d785c2a8c +PCRE2.v10.44.0+0.i686-linux-gnu.tar.gz/md5/e2d6be1d19566c965c2afeb995aba52f +PCRE2.v10.44.0+0.i686-linux-gnu.tar.gz/sha512/4a9d981bb6aa9150b670db7c5d4d188c8391fcb2a16bc710ede7a84bf7ec546fc5fd9096a339720579d25b6dcb5674b2b5b28e9664e5ef589b1a5044ce38b6a7 +PCRE2.v10.44.0+0.i686-linux-musl.tar.gz/md5/23cf857bd3daea4f094fcec48a7712dc +PCRE2.v10.44.0+0.i686-linux-musl.tar.gz/sha512/534f0cfab0cd60db9498eff387f7280a8baaf893a98dd2e7a737e68ba6473ed8236e9da85116eefb9812ec5323c705a00fcaff010b1900f752de8bdff65ef3ad +PCRE2.v10.44.0+0.i686-w64-mingw32.tar.gz/md5/3d05764df2305f16e4ffab60031ad40c +PCRE2.v10.44.0+0.i686-w64-mingw32.tar.gz/sha512/3e21cc6b71849c1a361373de30567990dba13dfd8812e7a7b5e2734b572bf1d45aeb730289d329975e76932c4c40e476824be2ab8e80a40fb7a7e2f46159235a +PCRE2.v10.44.0+0.powerpc64le-linux-gnu.tar.gz/md5/596d7c29d1417ed8959ea3ae3b4df453 +PCRE2.v10.44.0+0.powerpc64le-linux-gnu.tar.gz/sha512/89e03bfd6890150e2c8dddc4e7d024f2e09421c25a3d0fef3b5cd7f6bab7d6402ec1e82b02ecb5d26d01dfa2fb6068d050513894c374b7f2244c8fcbf00d69e2 +PCRE2.v10.44.0+0.x86_64-apple-darwin.tar.gz/md5/18f13c78ff6388c601bd36788e526b31 +PCRE2.v10.44.0+0.x86_64-apple-darwin.tar.gz/sha512/7b43a289f54064fc3c292de98173ec91cde2e49402c99c7848cbdc0e6d90a23a86d41f521e3986fcc8d941ee070d09e29ddc89a4e23009b8e9333e577ae4a09c +PCRE2.v10.44.0+0.x86_64-linux-gnu.tar.gz/md5/9f45feca0955f81ceb898208b9c74e15 +PCRE2.v10.44.0+0.x86_64-linux-gnu.tar.gz/sha512/eac215838306f7b5adb2166c3f620a69ed52fbd752ef3673a887507963a826c305d9b078dbb5236dc9a45eaca0d34f77325aab41703745701a077c84822ec0d0 +PCRE2.v10.44.0+0.x86_64-linux-musl.tar.gz/md5/79f092c6e8e971027ac6c1f0987376fb +PCRE2.v10.44.0+0.x86_64-linux-musl.tar.gz/sha512/2c5655b0f719a7d442c89f1040f2973b03f8becd855a0cfd6c0a985a07b25de351a84e3b9daaebd952b62628db0d937de08a8d05ee4bcace7e72d6b5ce6b8435 +PCRE2.v10.44.0+0.x86_64-unknown-freebsd.tar.gz/md5/a0bc32a099a584d453458a76c892fe47 +PCRE2.v10.44.0+0.x86_64-unknown-freebsd.tar.gz/sha512/6649c1b9e9569a9decccf6ebaa61d44acdb9069208ec796777d8e70a908210f775be2142053f6a5762ebaa321e297f6d8b51db99629766bc702c498b5f772492 +PCRE2.v10.44.0+0.x86_64-w64-mingw32.tar.gz/md5/eeffb6164fba08b0d5c7f50afa081475 +PCRE2.v10.44.0+0.x86_64-w64-mingw32.tar.gz/sha512/f06db992a2070a88559c15224972aeb098d4291a4325970fc0fbbb7cdd539f4a2fd4f90c0de90a34fe454da6c38290f9e0c7fdf2fe8c441f687fe4491d652adc +pcre2-10.44.tar.bz2/md5/9d1fe11e2e919c7b395e3e8f0a5c3eec +pcre2-10.44.tar.bz2/sha512/ee91cc10a2962bc7818b03d368df3dd31f42ea9a7260ae51483ea8cd331b7431e36e63256b0adc213cc6d6741e7c90414fd420622308c0ae3fcb5dd878591be2 diff --git a/deps/libgit2.version b/deps/libgit2.version index d51beb34c27f5..ae475f0b3644f 100644 --- a/deps/libgit2.version +++ b/deps/libgit2.version @@ -11,4 +11,4 @@ LIBGIT2_SHA1=d74d491481831ddcd23575d376e56d2197e95910 # The versions of cacert.pem are identified by the date (YYYY-MM-DD) of their changes. # See https://curl.haxx.se/docs/caextract.html for more details. # Keep in sync with `stdlib/MozillaCACerts_jll/Project.toml`. -MOZILLA_CACERT_VERSION := 2024-03-11 +MOZILLA_CACERT_VERSION := 2024-11-26 diff --git a/deps/llvmunwind.version b/deps/llvmunwind.version index 9c2a91c566ba2..666cae54025b4 100644 --- a/deps/llvmunwind.version +++ b/deps/llvmunwind.version @@ -2,4 +2,4 @@ LLVMUNWIND_JLL_NAME := LLVMLibUnwind ## source build -LLVMUNWIND_VER := 14.0.6 +LLVMUNWIND_VER := 19.1.4 diff --git a/deps/patches/llvm-libunwind-force-dwarf.patch b/deps/patches/llvm-libunwind-force-dwarf.patch index 2f4d31acb8a4a..494c5e77e187b 100644 --- a/deps/patches/llvm-libunwind-force-dwarf.patch +++ b/deps/patches/llvm-libunwind-force-dwarf.patch @@ -6,22 +6,23 @@ Date: Tue Aug 27 15:01:22 2013 -0400 Add option to step with DWARF --- -diff -pur a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h ---- a/libunwind/include/libunwind.h 2021-06-28 18:23:38.000000000 +0200 -+++ b/libunwind/include/libunwind.h 2022-05-04 18:44:24.000000000 +0200 +diff --git a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h +index b2dae8f..fc37afb 100644 +--- a/libunwind/include/libunwind.h ++++ b/libunwind/include/libunwind.h @@ -108,6 +108,7 @@ extern "C" { - + extern int unw_getcontext(unw_context_t *) LIBUNWIND_AVAIL; extern int unw_init_local(unw_cursor_t *, unw_context_t *) LIBUNWIND_AVAIL; +extern int unw_init_local_dwarf(unw_cursor_t *, unw_context_t *) LIBUNWIND_AVAIL; extern int unw_step(unw_cursor_t *) LIBUNWIND_AVAIL; extern int unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *) LIBUNWIND_AVAIL; extern int unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *) LIBUNWIND_AVAIL; -Only in b/libunwind/include: libunwind.h.orig -diff -pur a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp ---- a/libunwind/src/UnwindCursor.hpp 2021-06-28 18:23:38.000000000 +0200 -+++ b/libunwind/src/UnwindCursor.hpp 2022-05-04 18:45:11.000000000 +0200 -@@ -437,6 +437,9 @@ public: +diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp +index 7753936..26ca486 100644 +--- a/libunwind/src/UnwindCursor.hpp ++++ b/libunwind/src/UnwindCursor.hpp +@@ -453,6 +453,9 @@ public: virtual bool isSignalFrame() { _LIBUNWIND_ABORT("isSignalFrame not implemented"); } @@ -31,7 +32,7 @@ diff -pur a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp virtual bool getFunctionName(char *, size_t, unw_word_t *) { _LIBUNWIND_ABORT("getFunctionName not implemented"); } -@@ -894,6 +897,7 @@ public: +@@ -944,6 +947,7 @@ public: virtual void getInfo(unw_proc_info_t *); virtual void jumpto(); virtual bool isSignalFrame(); @@ -39,24 +40,23 @@ diff -pur a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off); virtual void setInfoBasedOnIPRegister(bool isReturnAddress = false); virtual const char *getRegisterName(int num); -@@ -963,7 +967,7 @@ private: +@@ -1031,7 +1035,7 @@ private: const UnwindInfoSections §s); - int stepWithCompactEncoding() { - #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + int stepWithCompactEncoding(bool stage2 = false) { + #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) - if ( compactSaysUseDwarf() ) + if ( _forceDwarf || compactSaysUseDwarf() ) - return stepWithDwarfFDE(); - #endif + return stepWithDwarfFDE(stage2); + #endif R dummy; -@@ -1198,6 +1202,7 @@ private: - unw_proc_info_t _info; - bool _unwindInfoMissing; - bool _isSignalFrame; -+ bool _forceDwarf; - #if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) +@@ -1317,13 +1321,14 @@ private: + #if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) bool _isSigReturn = false; #endif -@@ -1207,7 +1212,7 @@ private: ++ bool _forceDwarf; + }; + + template UnwindCursor::UnwindCursor(unw_context_t *context, A &as) : _addressSpace(as), _registers(context), _unwindInfoMissing(false), @@ -65,8 +65,8 @@ diff -pur a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp static_assert((check_fit, unw_cursor_t>::does_fit), "UnwindCursor<> does not fit in unw_cursor_t"); static_assert((alignof(UnwindCursor) <= alignof(unw_cursor_t)), -@@ -1217,7 +1222,8 @@ UnwindCursor::UnwindCursor(unw_con - +@@ -1333,7 +1338,8 @@ UnwindCursor::UnwindCursor(unw_context_t *context, A &as) + template UnwindCursor::UnwindCursor(A &as, void *) - : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false) { @@ -75,18 +75,18 @@ diff -pur a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp memset(&_info, 0, sizeof(_info)); // FIXME // fill in _registers from thread arg -@@ -1273,6 +1279,10 @@ template bool U +@@ -1396,6 +1402,10 @@ template bool UnwindCursor::isSignalFrame() { return _isSignalFrame; } - + +template void UnwindCursor::setForceDWARF(bool force) { + _forceDwarf = force; +} + #endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) - + #if defined(_LIBUNWIND_ARM_EHABI) -@@ -1941,7 +1951,13 @@ void UnwindCursor::setInfoBasedOnI +@@ -2611,7 +2621,12 @@ void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { // record that we have no unwind info. if (_info.format == 0) _unwindInfoMissing = true; @@ -96,14 +96,14 @@ diff -pur a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp + #else return; + #endif -+ } } #endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) -diff -pur a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp ---- a/libunwind/src/libunwind.cpp 2021-06-28 18:23:38.000000000 +0200 -+++ b/libunwind/src/libunwind.cpp 2022-05-04 18:44:24.000000000 +0200 -@@ -71,6 +71,7 @@ _LIBUNWIND_HIDDEN int __unw_init_local(u +diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp +index 217dde9..8e9a77a 100644 +--- a/libunwind/src/libunwind.cpp ++++ b/libunwind/src/libunwind.cpp +@@ -86,6 +86,7 @@ _LIBUNWIND_HIDDEN int __unw_init_local(unw_cursor_t *cursor, new (reinterpret_cast *>(cursor)) UnwindCursor( context, LocalAddressSpace::sThisAddressSpace); @@ -111,10 +111,10 @@ diff -pur a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp #undef REGISTER_KIND AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; co->setInfoBasedOnIPRegister(); -@@ -79,6 +80,54 @@ _LIBUNWIND_HIDDEN int __unw_init_local(u +@@ -109,6 +110,54 @@ _LIBUNWIND_HIDDEN int __unw_get_reg(unw_cursor_t *cursor, unw_regnum_t regNum, } - _LIBUNWIND_WEAK_ALIAS(__unw_init_local, unw_init_local) - + _LIBUNWIND_WEAK_ALIAS(__unw_get_reg, unw_get_reg) + +_LIBUNWIND_HIDDEN int __unw_init_local_dwarf(unw_cursor_t *cursor, + unw_context_t *context) { + _LIBUNWIND_TRACE_API("__unw_init_local_dwarf(cursor=%p, context=%p)", @@ -163,14 +163,15 @@ diff -pur a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp +} +_LIBUNWIND_WEAK_ALIAS(__unw_init_local_dwarf, unw_init_local_dwarf) + - /// Get value of specified register at cursor position in stack frame. - _LIBUNWIND_HIDDEN int __unw_get_reg(unw_cursor_t *cursor, unw_regnum_t regNum, - unw_word_t *value) { -diff -pur a/libunwind/src/libunwind_ext.h b/libunwind/src/libunwind_ext.h ---- a/libunwind/src/libunwind_ext.h 2021-06-28 18:23:38.000000000 +0200 -+++ b/libunwind/src/libunwind_ext.h 2022-05-04 18:44:24.000000000 +0200 + /// Set value of specified register at cursor position in stack frame. + _LIBUNWIND_HIDDEN int __unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum, + unw_word_t value) { +diff --git a/libunwind/src/libunwind_ext.h b/libunwind/src/libunwind_ext.h +index 28db43a..c4f9767 100644 +--- a/libunwind/src/libunwind_ext.h ++++ b/libunwind/src/libunwind_ext.h @@ -25,6 +25,7 @@ extern "C" { - + extern int __unw_getcontext(unw_context_t *); extern int __unw_init_local(unw_cursor_t *, unw_context_t *); +extern int __unw_init_local_dwarf(unw_cursor_t *, unw_context_t *); diff --git a/deps/patches/llvm-libunwind-prologue-epilogue.patch b/deps/patches/llvm-libunwind-prologue-epilogue.patch index 7dadca728f9cf..b2618998905e4 100644 --- a/deps/patches/llvm-libunwind-prologue-epilogue.patch +++ b/deps/patches/llvm-libunwind-prologue-epilogue.patch @@ -14,7 +14,7 @@ index 1c3175dff50a..78a658ccbc27 100644 @@ -310,6 +310,50 @@ int CompactUnwinder_x86_64::stepWithCompactEncodingRBPFrame( uint32_t savedRegistersLocations = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); - + + // If we have not stored EBP yet + if (functionStart == registers.getIP()) { + uint64_t rsp = registers.getSP(); diff --git a/deps/pcre.version b/deps/pcre.version index e3ea507376105..681e97e197f51 100644 --- a/deps/pcre.version +++ b/deps/pcre.version @@ -2,4 +2,4 @@ PCRE_JLL_NAME := PCRE2 ## source build -PCRE_VER := 10.43 +PCRE_VER := 10.44 diff --git a/deps/tools/jlchecksum b/deps/tools/jlchecksum index 329d3a2a845d4..9945ec89e6bda 100755 --- a/deps/tools/jlchecksum +++ b/deps/tools/jlchecksum @@ -63,7 +63,7 @@ find_checksum() fi done if [ ! -f "$DEPSDIR/checksums/$BASENAME/$CHECKSUM_TYPE" ]; then - if [ ${TAGGED_RELEASE_BANNER:-} ]; then + if [ "${TAGGED_RELEASE_BANNER:-}" ]; then echo "WARNING: $CHECKSUM_TYPE checksum for $BASENAME not found in deps/checksums/, failing release build." >&2 exit 3 fi diff --git a/deps/unwind.mk b/deps/unwind.mk index 3951bbf36e22f..c934c382a23e7 100644 --- a/deps/unwind.mk +++ b/deps/unwind.mk @@ -85,7 +85,7 @@ check-unwind: $(BUILDDIR)/libunwind-$(UNWIND_VER)/build-checked ## LLVM libunwind ## -LLVMUNWIND_OPTS := $(CMAKE_COMMON) \ +LLVMUNWIND_OPTS := $(CMAKE_GENERATOR_COMMAND) $(CMAKE_COMMON) \ -DCMAKE_BUILD_TYPE=MinSizeRel \ -DLIBUNWIND_ENABLE_PEDANTIC=OFF \ -DLIBUNWIND_INCLUDE_DOCS=OFF \ @@ -93,6 +93,7 @@ LLVMUNWIND_OPTS := $(CMAKE_COMMON) \ -DLIBUNWIND_INSTALL_HEADERS=ON \ -DLIBUNWIND_ENABLE_ASSERTIONS=OFF \ -DLLVM_CONFIG_PATH=$(build_depsbindir)/llvm-config \ + -DLLVM_ENABLE_RUNTIMES="libunwind" \ -DLLVM_PATH=$(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/llvm $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER).tar.xz: | $(SRCCACHE) @@ -122,16 +123,23 @@ checksum-llvmunwind: $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER).tar.xz $(BUILDDIR)/llvmunwind-$(LLVMUNWIND_VER)/build-configured: $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/source-extracted $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/libunwind/llvm-libunwind-freebsd-libgcc-api-compat.patch-applied mkdir -p $(dir $@) cd $(dir $@) && \ - $(CMAKE) $(dir $<)/libunwind $(LLVMUNWIND_OPTS) + $(CMAKE) $(dir $<) -S $(dir $<)/runtimes $(LLVMUNWIND_OPTS) echo 1 > $@ $(BUILDDIR)/llvmunwind-$(LLVMUNWIND_VER)/build-compiled: $(BUILDDIR)/llvmunwind-$(LLVMUNWIND_VER)/build-configured - $(MAKE) -C $(dir $<) + cd $(dir $<) && \ + $(if $(filter $(CMAKE_GENERATOR),make), \ + $(MAKE), \ + $(CMAKE) --build . --target unwind) echo 1 > $@ +LIBUNWIND_INSTALL = \ + cd $1 && mkdir -p $2$$(build_depsbindir) && \ + $$(CMAKE) -DCMAKE_INSTALL_PREFIX="$2$$(build_prefix)" -P libunwind/cmake_install.cmake + $(eval $(call staged-install, \ llvmunwind,llvmunwind-$(LLVMUNWIND_VER), \ - MAKE_INSTALL,,, \ + LIBUNWIND_INSTALL,,, \ cp -fR $(SRCCACHE)/llvm-project-$(LLVMUNWIND_VER)/libunwind/* $(build_includedir))) clean-llvmunwind: diff --git a/doc/man/julia.1 b/doc/man/julia.1 index 56cb690d66eeb..2da11ae1b3f18 100644 --- a/doc/man/julia.1 +++ b/doc/man/julia.1 @@ -294,6 +294,10 @@ If --trace-compile is enabled show how long each took to compile in ms --trace-dispatch={stderr|name} Print precompile statements for methods dispatched during execution or save to stderr or a path. +.TP +--task-metrics={yes|no*} +Enable the collection of per-task metrics. + .TP -image-codegen Force generate code in imaging mode diff --git a/doc/src/base/multi-threading.md b/doc/src/base/multi-threading.md index 9e3bc49acf6dc..81d1d83d765ac 100644 --- a/doc/src/base/multi-threading.md +++ b/doc/src/base/multi-threading.md @@ -65,3 +65,11 @@ These building blocks are used to create the regular synchronization objects. ```@docs Base.Threads.SpinLock ``` + +## Task metrics (Experimental) + +```@docs +Base.Experimental.task_metrics +Base.Experimental.task_running_time_ns +Base.Experimental.task_wall_time_ns +``` diff --git a/doc/src/base/reflection.md b/doc/src/base/reflection.md index 9228fb38322df..d88c3c8b0d0cf 100644 --- a/doc/src/base/reflection.md +++ b/doc/src/base/reflection.md @@ -100,10 +100,12 @@ as assignments, branches, and calls: ```jldoctest; setup = (using Base: +, sin) julia> Meta.lower(@__MODULE__, :( [1+2, sin(0.5)] )) :($(Expr(:thunk, CodeInfo( -1 ─ %1 = dynamic 1 + 2 -│ %2 = dynamic sin(0.5) -│ %3 = dynamic Base.vect(%1, %2) -└── return %3 +1 ─ %1 = :+ +│ %2 = dynamic (%1)(1, 2) +│ %3 = sin +│ %4 = dynamic (%3)(0.5) +│ %5 = dynamic Base.vect(%2, %4) +└── return %5 )))) ``` diff --git a/doc/src/devdocs/build/build.md b/doc/src/devdocs/build/build.md index 553f7c2e815cf..966ef3d102d1c 100644 --- a/doc/src/devdocs/build/build.md +++ b/doc/src/devdocs/build/build.md @@ -249,7 +249,7 @@ The most complicated dependency is LLVM, for which we require additional patches For packaging Julia with LLVM, we recommend either: - bundling a Julia-only LLVM library inside the Julia package, or - adding the patches to the LLVM package of the distribution. - * A complete list of patches is available in on [Github](https://github.com/JuliaLang/llvm-project) see the `julia-release/15.x` branch. + * A complete list of patches is available in on [Github](https://github.com/JuliaLang/llvm-project) see the `julia-release/18.x` branch. * The only Julia-specific patch is the lib renaming (`llvm7-symver-jlprefix.patch`), which should _not_ be applied to a system LLVM. * The remaining patches are all upstream bug fixes, and have been contributed into upstream LLVM. diff --git a/doc/src/manual/command-line-interface.md b/doc/src/manual/command-line-interface.md index 734d7031db5e8..3477f61164c50 100644 --- a/doc/src/manual/command-line-interface.md +++ b/doc/src/manual/command-line-interface.md @@ -203,6 +203,7 @@ The following is a complete list of command-line switches available when launchi |`--code-coverage=tracefile.info` |Append coverage information to the LCOV tracefile (filename supports format tokens).| |`--track-allocation[={none*\|user\|all}]` |Count bytes allocated by each source line (omitting setting is equivalent to "user")| |`--track-allocation=@` |Count bytes but only in files that fall under the given file path/directory. The `@` prefix is required to select this option. A `@` with no path will track the current directory.| +|`--task-metrics={yes\|no*}` |Enable the collection of per-task metrics| |`--bug-report=KIND` |Launch a bug report session. It can be used to start a REPL, run a script, or evaluate expressions. It first tries to use BugReporting.jl installed in current environment and falls back to the latest compatible BugReporting.jl if not. For more information, see `--bug-report=help`.| |`--heap-size-hint=` |Forces garbage collection if memory usage is higher than the given value. The value may be specified as a number of bytes, optionally in units of KB, MB, GB, or TB, or as a percentage of physical memory with %.| |`--compile={yes*\|no\|all\|min}` |Enable or disable JIT compiler, or request exhaustive or minimal compilation| @@ -221,6 +222,10 @@ The following is a complete list of command-line switches available when launchi |`--permalloc-pkgimg={yes\|no*}` |Copy the data section of package images into memory| |`--trim={no*\|safe\|unsafe\|unsafe-warn}` |Build a sysimage including only code provably reachable from methods marked by calling `entrypoint`. The three non-default options differ in how they handle dynamic call sites. In safe mode, such sites result in compile-time errors. In unsafe mode, such sites are allowed but the resulting binary might be missing needed code and can throw runtime errors. With unsafe-warn, such sites will trigger warnings at compile-time and might error at runtime.| +Options that have the form `--option={...}` can be specified either as `--option=value` or as `--option value`. For example, `julia --banner=no` is equivalent to `julia --banner no`. This is especially relevant for options that take a filename for output, because forgetting to specifying the argument for (say) `--trace-compile` will cause the option following it to be interpreted as the filename, possibly unintentionally overwriting it. + +Note that options of the form `--option[=...]` can **not** be specified as `--option value`, but only as `--option=value` (or simply `--option`, when no argument is provided). + !!! compat "Julia 1.1" In Julia 1.0, the default `--project=@.` option did not search up from the root directory of a Git repository for the `Project.toml` file. From Julia 1.1 forward, it diff --git a/doc/src/manual/complex-and-rational-numbers.md b/doc/src/manual/complex-and-rational-numbers.md index 9cab2ed1e4f24..d1d6ffeca245f 100644 --- a/doc/src/manual/complex-and-rational-numbers.md +++ b/doc/src/manual/complex-and-rational-numbers.md @@ -254,13 +254,30 @@ julia> float(3//4) ``` Conversion from rational to floating-point respects the following identity for any integral values -of `a` and `b`, with the exception of the two cases `b == 0` and `a == 0 && b < 0`: +of `a` and `b`, except when `a==0 && b <= 0`: ```jldoctest julia> a = 1; b = 2; julia> isequal(float(a//b), a/b) true + +julia> a, b = 0, 0 +(0, 0) + +julia> float(a//b) +ERROR: ArgumentError: invalid rational: zero(Int64)//zero(Int64) +Stacktrace: +[...] + +julia> a/b +NaN + +julia> a, b = 0, -1 +(0, -1) + +julia> float(a//b), a/b +(0.0, -0.0) ``` Constructing infinite rational values is acceptable: diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index 038000f55e761..1166164241c60 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -1058,12 +1058,12 @@ the output. As a trivial example, compare ```jldoctest prealloc julia> function xinc(x) - return [x, x+1, x+2] + return [x + i for i in 1:3000] end; julia> function loopinc() y = 0 - for i = 1:10^7 + for i = 1:10^5 ret = xinc(i) y += ret[2] end @@ -1075,16 +1075,16 @@ with ```jldoctest prealloc julia> function xinc!(ret::AbstractVector{T}, x::T) where T - ret[1] = x - ret[2] = x+1 - ret[3] = x+2 + for i in 1:3000 + ret[i] = x+i + end nothing end; julia> function loopinc_prealloc() - ret = Vector{Int}(undef, 3) + ret = Vector{Int}(undef, 3000) y = 0 - for i = 1:10^7 + for i = 1:10^5 xinc!(ret, i) y += ret[2] end @@ -1096,12 +1096,12 @@ Timing results: ```jldoctest prealloc; filter = r"[0-9\.]+ seconds \(.*?\)" julia> @time loopinc() - 0.529894 seconds (40.00 M allocations: 1.490 GiB, 12.14% gc time) -50000015000000 + 0.297454 seconds (200.00 k allocations: 2.239 GiB, 39.80% gc time) +5000250000 julia> @time loopinc_prealloc() - 0.030850 seconds (6 allocations: 288 bytes) -50000015000000 + 0.009410 seconds (2 allocations: 23.477 KiB) +5000250000 ``` Preallocation has other advantages, for example by allowing the caller to control the "output" diff --git a/doc/src/manual/variables-and-scoping.md b/doc/src/manual/variables-and-scoping.md index 64a12ea88c7dd..99f7ba088311d 100644 --- a/doc/src/manual/variables-and-scoping.md +++ b/doc/src/manual/variables-and-scoping.md @@ -64,7 +64,7 @@ The constructs introducing scope blocks are: | Construct | Scope Type Introduced | Scope Types Able to Contain Construct | |:----------|:----------------------|:--------------------------------------| | [`module`](@ref), [`baremodule`](@ref) | global | global | -| [`struct`](@ref) | local (soft) | global | +| [`struct`](@ref) | local (hard) | global | | [`macro`](@ref) | local (hard) | global | | [`for`](@ref), [`while`](@ref), [`try`](@ref try) | local (soft) | global, local | | [`function`](@ref), [`do`](@ref), [`let`](@ref), [comprehensions](@ref man-comprehensions), [generators](@ref man-generators) | local (hard) | global, local | @@ -169,10 +169,10 @@ that location: 1. **Existing local:** If `x` is *already a local variable*, then the existing local `x` is assigned; 2. **Hard scope:** If `x` is *not already a local variable* and assignment occurs inside of any - hard scope construct (i.e. within a `let` block, function or macro body, comprehension, or + hard scope construct (i.e. within a `let` block, function, struct or macro body, comprehension, or generator), a new local named `x` is created in the scope of the assignment; 3. **Soft scope:** If `x` is *not already a local variable* and all of the scope constructs - containing the assignment are soft scopes (loops, `try`/`catch` blocks, or `struct` blocks), the + containing the assignment are soft scopes (loops, `try`/`catch` blocks), the behavior depends on whether the global variable `x` is defined: * if global `x` is *undefined*, a new local named `x` is created in the scope of the assignment; diff --git a/src/APInt-C.cpp b/src/APInt-C.cpp index e73399c2ecde4..86b0bdb27638b 100644 --- a/src/APInt-C.cpp +++ b/src/APInt-C.cpp @@ -475,7 +475,6 @@ void LLVMTrunc(jl_datatype_t *ty, integerPart *pa, jl_datatype_t *otys, integerP memcpy(pr, pa, onumbytes); } -#if JL_LLVM_VERSION >= 170000 extern "C" JL_DLLEXPORT unsigned countr_zero_8(uint8_t Val) { return countr_zero(Val); @@ -495,27 +494,6 @@ extern "C" JL_DLLEXPORT unsigned countr_zero_64(uint64_t Val) { return countr_zero(Val); } -#else -extern "C" JL_DLLEXPORT -unsigned countTrailingZeros_8(uint8_t Val) { - return countTrailingZeros(Val); -} - -extern "C" JL_DLLEXPORT -unsigned countTrailingZeros_16(uint16_t Val) { - return countTrailingZeros(Val); -} - -extern "C" JL_DLLEXPORT -unsigned countTrailingZeros_32(uint32_t Val) { - return countTrailingZeros(Val); -} - -extern "C" JL_DLLEXPORT -unsigned countTrailingZeros_64(uint64_t Val) { - return countTrailingZeros(Val); -} -#endif extern "C" JL_DLLEXPORT void jl_LLVMSMod(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr) { @@ -545,7 +523,6 @@ void jl_LLVMFlipSign(unsigned numbits, integerPart *pa, integerPart *pb, integer memcpy(pr, pa, numbytes); } -#if JL_LLVM_VERSION >= 170000 extern "C" JL_DLLEXPORT unsigned LLVMPopcount(unsigned numbits, integerPart *pa) { CREATE(a) @@ -575,34 +552,3 @@ unsigned LLVMCountl_zero(unsigned numbits, integerPart *pa) { CREATE(a) return a.countl_zero(); } -#else -extern "C" JL_DLLEXPORT -unsigned LLVMCountPopulation(unsigned numbits, integerPart *pa) { - CREATE(a) - return a.countPopulation(); -} - -extern "C" JL_DLLEXPORT -unsigned LLVMCountTrailingOnes(unsigned numbits, integerPart *pa) { - CREATE(a) - return a.countTrailingOnes(); -} - -extern "C" JL_DLLEXPORT -unsigned LLVMCountTrailingZeros(unsigned numbits, integerPart *pa) { - CREATE(a) - return a.countTrailingZeros(); -} - -extern "C" JL_DLLEXPORT -unsigned LLVMCountLeadingOnes(unsigned numbits, integerPart *pa) { - CREATE(a) - return a.countLeadingOnes(); -} - -extern "C" JL_DLLEXPORT -unsigned LLVMCountLeadingZeros(unsigned numbits, integerPart *pa) { - CREATE(a) - return a.countLeadingZeros(); -} -#endif diff --git a/src/APInt-C.h b/src/APInt-C.h index a44d922a40d24..59ce3c765eeec 100644 --- a/src/APInt-C.h +++ b/src/APInt-C.h @@ -54,19 +54,11 @@ JL_DLLEXPORT int LLVMDiv_uov(unsigned numbits, integerPart *pa, integerPart *pb, JL_DLLEXPORT int LLVMRem_sov(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr); JL_DLLEXPORT int LLVMRem_uov(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr); -#if JL_LLVM_VERSION >= 170000 JL_DLLEXPORT unsigned LLVMPopcount(unsigned numbits, integerPart *pa); JL_DLLEXPORT unsigned LLVMCountr_one(unsigned numbits, integerPart *pa); JL_DLLEXPORT unsigned LLVMCountr_zero(unsigned numbits, integerPart *pa); JL_DLLEXPORT unsigned LLVMCountl_one(unsigned numbits, integerPart *pa); JL_DLLEXPORT unsigned LLVMCountl_zero(unsigned numbits, integerPart *pa); -#else -JL_DLLEXPORT unsigned LLVMCountPopulation(unsigned numbits, integerPart *pa); -JL_DLLEXPORT unsigned LLVMCountTrailingOnes(unsigned numbits, integerPart *pa); -JL_DLLEXPORT unsigned LLVMCountTrailingZeros(unsigned numbits, integerPart *pa); -JL_DLLEXPORT unsigned LLVMCountLeadingOnes(unsigned numbits, integerPart *pa); -JL_DLLEXPORT unsigned LLVMCountLeadingZeros(unsigned numbits, integerPart *pa); -#endif JL_DLLEXPORT void LLVMFPtoSI(jl_datatype_t *ty, integerPart *pa, jl_datatype_t *oty, integerPart *pr); JL_DLLEXPORT void LLVMFPtoUI(jl_datatype_t *ty, integerPart *pa, jl_datatype_t *oty, integerPart *pr); @@ -82,17 +74,10 @@ JL_DLLEXPORT int LLVMFPtoUI_exact(jl_datatype_t *ty, integerPart *pa, jl_datatyp JL_DLLEXPORT void jl_LLVMSMod(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr); JL_DLLEXPORT void jl_LLVMFlipSign(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr); -#if JL_LLVM_VERSION >= 170000 JL_DLLEXPORT unsigned countr_zero_8(uint8_t Val); JL_DLLEXPORT unsigned countr_zero_16(uint16_t Val); JL_DLLEXPORT unsigned countr_zero_32(uint32_t Val); JL_DLLEXPORT unsigned countr_zero_64(uint64_t Val); -#else -JL_DLLEXPORT unsigned countTrailingZeros_8(uint8_t Val); -JL_DLLEXPORT unsigned countTrailingZeros_16(uint16_t Val); -JL_DLLEXPORT unsigned countTrailingZeros_32(uint32_t Val); -JL_DLLEXPORT unsigned countTrailingZeros_64(uint64_t Val); -#endif //uint8_t getSwappedBytes_8(uint8_t Value); // no-op //uint16_t getSwappedBytes_16(uint16_t Value); diff --git a/src/Makefile b/src/Makefile index 3458f51fa5548..9355ca2c4c675 100644 --- a/src/Makefile +++ b/src/Makefile @@ -77,11 +77,7 @@ else # JULIACODEGEN != LLVM endif -RT_LLVM_LIBS := support - -ifeq ($(shell test $(LLVM_VER_MAJ) -ge 16 && echo true),true) -RT_LLVM_LIBS += targetparser -endif +RT_LLVM_LIBS := support targetparser ifeq ($(OS),WINNT) SRCS += win32_ucontext diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 6af5227aafd92..198d7490cb092 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -4,11 +4,7 @@ #include "platform.h" // target support -#if JL_LLVM_VERSION >= 170000 #include -#else -#include -#endif #include "llvm/Support/CodeGen.h" #include #include @@ -95,33 +91,55 @@ void jl_get_function_id_impl(void *native_code, jl_code_instance_t *codeinst, } } -extern "C" JL_DLLEXPORT_CODEGEN -void jl_get_llvm_mis_impl(void *native_code, arraylist_t* MIs) +extern "C" JL_DLLEXPORT_CODEGEN void +jl_get_llvm_mis_impl(void *native_code, size_t *num_elements, jl_method_instance_t **data) { - jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code; - auto map = data->jl_fvar_map; + jl_native_code_desc_t *desc = (jl_native_code_desc_t *)native_code; + auto &map = desc->jl_fvar_map; + + if (data == NULL) { + *num_elements = map.size(); + return; + } + + assert(*num_elements == map.size()); + size_t i = 0; for (auto &ci : map) { - jl_method_instance_t *mi = ci.first->def; - arraylist_push(MIs, mi); + data[i++] = ci.first->def; } } -extern "C" JL_DLLEXPORT_CODEGEN -void jl_get_llvm_gvs_impl(void *native_code, arraylist_t *gvs) +extern "C" JL_DLLEXPORT_CODEGEN void jl_get_llvm_gvs_impl(void *native_code, + size_t *num_elements, void **data) { // map a memory location (jl_value_t or jl_binding_t) to a GlobalVariable - jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code; - arraylist_grow(gvs, data->jl_value_to_llvm.size()); - memcpy(gvs->items, data->jl_value_to_llvm.data(), gvs->len * sizeof(void*)); + jl_native_code_desc_t *desc = (jl_native_code_desc_t *)native_code; + auto &value_map = desc->jl_value_to_llvm; + + if (data == NULL) { + *num_elements = value_map.size(); + return; + } + + assert(*num_elements == value_map.size()); + memcpy(data, value_map.data(), *num_elements * sizeof(void *)); } -extern "C" JL_DLLEXPORT_CODEGEN -void jl_get_llvm_external_fns_impl(void *native_code, arraylist_t *external_fns) +extern "C" JL_DLLEXPORT_CODEGEN void jl_get_llvm_external_fns_impl(void *native_code, + size_t *num_elements, + jl_code_instance_t *data) { - jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code; - arraylist_grow(external_fns, data->jl_external_to_llvm.size()); - memcpy(external_fns->items, data->jl_external_to_llvm.data(), - external_fns->len * sizeof(jl_code_instance_t*)); + jl_native_code_desc_t *desc = (jl_native_code_desc_t *)native_code; + auto &external_map = desc->jl_external_to_llvm; + + if (data == NULL) { + *num_elements = external_map.size(); + return; + } + + assert(*num_elements == external_map.size()); + memcpy((void *)data, (const void *)external_map.data(), + *num_elements * sizeof(jl_code_instance_t *)); } extern "C" JL_DLLEXPORT_CODEGEN @@ -487,7 +505,7 @@ static void compile_workqueue(jl_codegen_params_t ¶ms, egal_set &method_root size_t nrealargs = jl_nparams(mi->specTypes); // number of actual arguments being passed bool is_opaque_closure = jl_is_method(mi->def.value) && mi->def.method->is_for_opaque_closure; // TODO: maybe this can be cached in codeinst->specfptr? - emit_specsig_to_fptr1(proto.decl, proto.cc, proto.return_roots, mi->specTypes, codeinst->rettype, is_opaque_closure, nrealargs, params, pinvoke, 0, 0); + emit_specsig_to_fptr1(proto.decl, proto.cc, proto.return_roots, mi->specTypes, codeinst->rettype, is_opaque_closure, nrealargs, params, pinvoke); preal_decl = ""; // no need to fixup the name } if (!preal_decl.empty()) { @@ -571,9 +589,10 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm }); egal_set method_roots; jl_codegen_params_t params(ctxt, std::move(target_info.first), std::move(target_info.second)); + if (!llvmmod) + params.getContext().setDiscardValueNames(true); params.params = cgparams; params.imaging_mode = imaging; - params.debug_level = cgparams->debug_info_level; params.external_linkage = _external_linkage; params.temporary_roots = jl_alloc_array_1d(jl_array_any_type, 0); JL_GC_PUSH3(¶ms.temporary_roots, &method_roots.list, &method_roots.keyset); @@ -940,11 +959,7 @@ static FunctionInfo getFunctionWeight(const Function &F) auto val = F.getFnAttribute("julia.mv.clones").getValueAsString(); // base16, so must be at most 4 * length bits long // popcount gives number of clones - #if JL_LLVM_VERSION >= 170000 info.clones = APInt(val.size() * 4, val, 16).popcount() + 1; - #else - info.clones = APInt(val.size() * 4, val, 16).countPopulation() + 1; - #endif } info.weight += info.insts; // more basic blocks = more complex than just sum of insts, @@ -1349,7 +1364,7 @@ static AOTOutputs add_output_impl(Module &M, TargetMachine &SourceTM, ShardTimer // So for now we inject a definition of these functions that calls our runtime // functions. We do so after optimization to avoid cloning these functions. // Float16 conversion routines -#if defined(_CPU_X86_64_) && defined(_OS_DARWIN_) && JL_LLVM_VERSION >= 160000 +#if defined(_CPU_X86_64_) && defined(_OS_DARWIN_) // LLVM 16 reverted to soft-float ABI for passing half on x86_64 Darwin // https://github.com/llvm/llvm-project/commit/2bcf51c7f82ca7752d1bba390a2e0cb5fdd05ca9 injectCRTAlias(M, "__gnu_h2f_ieee", "julia_half_to_float", @@ -1697,9 +1712,7 @@ static SmallVector add_output(Module &M, TargetMachine &TM, Stri for (unsigned i = 0; i < threads; i++) { std::function func = [&, i]() { LLVMContext ctx; - #if JL_LLVM_VERSION < 170000 - SetOpaquePointer(ctx); - #endif + ctx.setDiscardValueNames(true); // Lazily deserialize the entire module timers[i].deserialize.startTimer(); auto EM = getLazyBitcodeModule(MemoryBufferRef(StringRef(serialized.data(), serialized.size()), "Optimized"), ctx); @@ -1864,7 +1877,7 @@ void jl_dump_native_impl(void *native_code, Str += "10.14.0"; TheTriple.setOSName(Str); } - Optional RelocModel; + std::optional RelocModel; if (TheTriple.isOSLinux() || TheTriple.isOSFreeBSD() || TheTriple.isOSOpenBSD()) { RelocModel = Reloc::PIC_; } @@ -1908,9 +1921,7 @@ void jl_dump_native_impl(void *native_code, if (z) { JL_TIMING(NATIVE_AOT, NATIVE_Sysimg); LLVMContext Context; - #if JL_LLVM_VERSION < 170000 - SetOpaquePointer(Context); - #endif + Context.setDiscardValueNames(true); Module sysimgM("sysimg", Context); sysimgM.setTargetTriple(TheTriple.str()); sysimgM.setDataLayout(DL); @@ -2055,9 +2066,7 @@ void jl_dump_native_impl(void *native_code, { JL_TIMING(NATIVE_AOT, NATIVE_Metadata); LLVMContext Context; - #if JL_LLVM_VERSION < 170000 - SetOpaquePointer(Context); - #endif + Context.setDiscardValueNames(true); Module metadataM("metadata", Context); metadataM.setTargetTriple(TheTriple.str()); metadataM.setDataLayout(DL); @@ -2256,7 +2265,6 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, jl_ // output.imaging = true; // This would also be nice, but it seems to cause OOMs on the windows32 builder // To get correct names in the IR this needs to be at least 2 - output.debug_level = params.debug_info_level; output.temporary_roots = jl_alloc_array_1d(jl_array_any_type, 0); JL_GC_PUSH1(&output.temporary_roots); auto decls = jl_emit_code(m, mi, src, output); @@ -2274,16 +2282,7 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, jl_ } else { auto p = literal_static_pointer_val(global.first, global.second->getValueType()); - #if JL_LLVM_VERSION >= 170000 Type *elty = PointerType::get(output.getContext(), 0); - #else - Type *elty; - if (p->getType()->isOpaquePointerTy()) { - elty = PointerType::get(output.getContext(), 0); - } else { - elty = p->getType()->getNonOpaquePointerElementType(); - } - #endif // For pretty printing, when LLVM inlines the global initializer into its loads auto alias = GlobalAlias::create(elty, 0, GlobalValue::PrivateLinkage, global.second->getName() + ".jit", p, global.second->getParent()); global.second->setInitializer(ConstantExpr::getBitCast(alias, global.second->getValueType())); diff --git a/src/array.c b/src/array.c index f0051ec17565a..da9cb24b4d0e9 100644 --- a/src/array.c +++ b/src/array.c @@ -16,12 +16,6 @@ extern "C" { #endif -#if defined(_P64) && defined(UINT128MAX) -typedef __uint128_t wideint_t; -#else -typedef uint64_t wideint_t; -#endif - #define MAXINTVAL (((size_t)-1)>>1) JL_DLLEXPORT int jl_array_validate_dims(size_t *nel, uint32_t ndims, size_t *dims) @@ -30,10 +24,9 @@ JL_DLLEXPORT int jl_array_validate_dims(size_t *nel, uint32_t ndims, size_t *dim size_t _nel = 1; for (i = 0; i < ndims; i++) { size_t di = dims[i]; - wideint_t prod = (wideint_t)_nel * (wideint_t)di; - if (prod >= (wideint_t) MAXINTVAL || di >= MAXINTVAL) + int overflow = __builtin_mul_overflow(_nel, di, &_nel); + if (overflow || di >= MAXINTVAL) return 1; - _nel = prod; } *nel = _nel; return 0; @@ -204,7 +197,7 @@ JL_DLLEXPORT void jl_array_grow_end(jl_array_t *a, size_t inc) int isbitsunion = jl_genericmemory_isbitsunion(a->ref.mem); size_t newnrows = n + inc; if (!isbitsunion && elsz == 0) { - jl_genericmemory_t *newmem = jl_alloc_genericmemory(mtype, MAXINTVAL - 1); + jl_genericmemory_t *newmem = jl_alloc_genericmemory(mtype, MAXINTVAL - 2); a->ref.mem = newmem; jl_gc_wb(a, newmem); a->dimsize[0] = newnrows; diff --git a/src/ast.c b/src/ast.c index 474c0661f5230..0f24d96393f2f 100644 --- a/src/ast.c +++ b/src/ast.c @@ -181,42 +181,6 @@ static value_t fl_defined_julia_global(fl_context_t *fl_ctx, value_t *args, uint return (bpart != NULL && decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) == BINDING_KIND_GLOBAL) ? 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 { - if (!iscons(argmod) || !issymbol(car_(argmod)) || scmsym_to_julia(fl_ctx, car_(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); - jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - jl_ptr_kind_union_t pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); - if (!bpart) - return fl_ctx->F; - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) - return fl_ctx->F; - return (jl_bkind_is_some_constant(decode_restriction_kind(pku)) ? - decode_restriction_value(pku) : jl_atomic_load_relaxed(&b->value)) != NULL ? fl_ctx->T : fl_ctx->F; -} - // Used to generate a unique suffix for a given symbol (e.g. variable or type name) // first argument contains a stack of method definitions seen so far by `closure-convert` in flisp. // if the top of the stack is non-NIL, we use it to augment the suffix so that it becomes @@ -288,7 +252,6 @@ 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_module_unique_name }, { "julia-scalar?", fl_julia_scalar }, { NULL, NULL } @@ -706,6 +669,8 @@ static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, jl_module_t *m if (iscvalue(e) && cv_class((cvalue_t*)ptr(e)) == jl_ast_ctx(fl_ctx)->jvtype) { return *(jl_value_t**)cv_data((cvalue_t*)ptr(e)); } + fl_print(fl_ctx, ios_stderr, e); + ios_putc('\n', ios_stderr); jl_error("malformed tree"); } diff --git a/src/builtin_proto.h b/src/builtin_proto.h index 7fbd555758675..70ad67040991d 100644 --- a/src/builtin_proto.h +++ b/src/builtin_proto.h @@ -46,6 +46,7 @@ DECLARE_BUILTIN(is); DECLARE_BUILTIN(isa); DECLARE_BUILTIN(isdefined); DECLARE_BUILTIN(issubtype); +DECLARE_BUILTIN(memorynew); DECLARE_BUILTIN(memoryref); DECLARE_BUILTIN(memoryref_isassigned); DECLARE_BUILTIN(memoryrefget); diff --git a/src/builtins.c b/src/builtins.c index b129cca0ee71d..06dbd84aff6c0 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -931,22 +931,27 @@ JL_CALLABLE(jl_f__call_in_world_total) // tuples --------------------------------------------------------------------- -JL_CALLABLE(jl_f_tuple) +static jl_value_t *arg_tuple(jl_value_t *a1, jl_value_t **args, size_t nargs) { size_t i; - if (nargs == 0) - return (jl_value_t*)jl_emptytuple; - jl_datatype_t *tt = jl_inst_arg_tuple_type(args[0], &args[1], nargs, 0); + jl_datatype_t *tt = jl_inst_arg_tuple_type(a1, args, nargs, 0); JL_GC_PROMISE_ROOTED(tt); // it is a concrete type if (tt->instance != NULL) return tt->instance; jl_task_t *ct = jl_current_task; jl_value_t *jv = jl_gc_alloc(ct->ptls, jl_datatype_size(tt), tt); for (i = 0; i < nargs; i++) - set_nth_field(tt, jv, i, args[i], 0); + set_nth_field(tt, jv, i, i == 0 ? a1 : args[i - 1], 0); return jv; } +JL_CALLABLE(jl_f_tuple) +{ + if (nargs == 0) + return (jl_value_t*)jl_emptytuple; + return arg_tuple(args[0], &args[1], nargs); +} + JL_CALLABLE(jl_f_svec) { size_t i; @@ -1577,14 +1582,41 @@ JL_CALLABLE(jl_f_invoke) { JL_NARGSV(invoke, 2); jl_value_t *argtypes = args[1]; - JL_GC_PUSH1(&argtypes); - if (!jl_is_tuple_type(jl_unwrap_unionall(args[1]))) - jl_type_error("invoke", (jl_value_t*)jl_anytuple_type_type, args[1]); + if (jl_is_method(argtypes)) { + jl_method_t *m = (jl_method_t*)argtypes; + if (!jl_tuple1_isa(args[0], &args[2], nargs - 1, (jl_datatype_t*)m->sig)) + jl_type_error("invoke: argument type error", argtypes, arg_tuple(args[0], &args[2], nargs - 1)); + return jl_gf_invoke_by_method(m, args[0], &args[2], nargs - 1); + } else if (jl_is_code_instance(argtypes)) { + jl_code_instance_t *codeinst = (jl_code_instance_t*)args[1]; + jl_callptr_t invoke = jl_atomic_load_acquire(&codeinst->invoke); + // N.B.: specTypes need not be a subtype of the method signature. We need to check both. + if (!jl_tuple1_isa(args[0], &args[2], nargs - 1, (jl_datatype_t*)codeinst->def->specTypes) || + (jl_is_method(codeinst->def->def.value) && !jl_tuple1_isa(args[0], &args[2], nargs - 1, (jl_datatype_t*)codeinst->def->def.method->sig))) { + jl_type_error("invoke: argument type error", codeinst->def->specTypes, arg_tuple(args[0], &args[2], nargs - 1)); + } + if (jl_atomic_load_relaxed(&codeinst->min_world) > jl_current_task->world_age || + jl_current_task->world_age > jl_atomic_load_relaxed(&codeinst->max_world)) { + jl_error("invoke: CodeInstance not valid for this world"); + } + if (!invoke) { + jl_compile_codeinst(codeinst); + invoke = jl_atomic_load_acquire(&codeinst->invoke); + } + if (invoke) { + return invoke(args[0], &args[2], nargs - 2, codeinst); + } else { + if (codeinst->owner != jl_nothing) { + jl_error("Failed to invoke or compile external codeinst"); + } + return jl_invoke(args[0], &args[2], nargs - 1, codeinst->def); + } + } + if (!jl_is_tuple_type(jl_unwrap_unionall(argtypes))) + jl_type_error("invoke", (jl_value_t*)jl_anytuple_type_type, argtypes); if (!jl_tuple_isa(&args[2], nargs - 2, (jl_datatype_t*)argtypes)) jl_type_error("invoke: argument type error", argtypes, jl_f_tuple(NULL, &args[2], nargs - 2)); - jl_value_t *res = jl_gf_invoke(argtypes, args[0], &args[2], nargs - 1); - JL_GC_POP(); - return res; + return jl_gf_invoke(argtypes, args[0], &args[2], nargs - 1); } // Expr constructor for internal use ------------------------------------------ @@ -1643,6 +1675,15 @@ JL_CALLABLE(jl_f__typevar) } // genericmemory --------------------------------------------------------------------- +JL_CALLABLE(jl_f_memorynew) +{ + JL_NARGS(memorynew, 2, 2); + jl_datatype_t *jl_genericmemory_type_type = jl_datatype_type; + JL_TYPECHK(memorynew, genericmemory_type, args[0]); + JL_TYPECHK(memorynew, long, args[1]); + size_t nel = jl_unbox_long(args[1]); + return (jl_value_t*)jl_alloc_genericmemory(args[0], nel); +} JL_CALLABLE(jl_f_memoryref) { @@ -2409,6 +2450,7 @@ void jl_init_primitives(void) JL_GC_DISABLED jl_builtin_setglobalonce = add_builtin_func("setglobalonce!", jl_f_setglobalonce); // memory primitives + jl_builtin_memorynew = add_builtin_func("memorynew", jl_f_memorynew); jl_builtin_memoryref = add_builtin_func("memoryrefnew", jl_f_memoryref); jl_builtin_memoryrefoffset = add_builtin_func("memoryrefoffset", jl_f_memoryrefoffset); jl_builtin_memoryrefget = add_builtin_func("memoryrefget", jl_f_memoryrefget); diff --git a/src/ccall.cpp b/src/ccall.cpp index 952625a71287b..3937570896f82 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -264,9 +264,6 @@ static GlobalVariable *emit_plt_thunk( SmallVector args; for (auto &arg : plt->args()) args.push_back(&arg); - #if JL_LLVM_VERSION < 170000 - assert(cast(ptr->getType())->isOpaqueOrPointeeTypeMatches(functype)); - #endif CallInst *ret = irbuilder.CreateCall( functype, ptr, ArrayRef(args)); @@ -851,6 +848,7 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar // generate a temporary module that contains our IR std::unique_ptr Mod; + bool shouldDiscardValueNames = ctx.builder.getContext().shouldDiscardValueNames(); Function *f; if (entry == NULL) { // we only have function IR, which we should put in a function @@ -878,7 +876,9 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar << jl_string_data(ir) << "\n}"; SMDiagnostic Err = SMDiagnostic(); + ctx.builder.getContext().setDiscardValueNames(false); Mod = parseAssemblyString(ir_stream.str(), Err, ctx.builder.getContext()); + ctx.builder.getContext().setDiscardValueNames(shouldDiscardValueNames); // backwards compatibility: support for IR with integer pointers if (!Mod) { @@ -911,8 +911,9 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar << jl_string_data(ir) << "\n}"; SMDiagnostic Err = SMDiagnostic(); - Mod = - parseAssemblyString(compat_ir_stream.str(), Err, ctx.builder.getContext()); + ctx.builder.getContext().setDiscardValueNames(false); + Mod = parseAssemblyString(compat_ir_stream.str(), Err, ctx.builder.getContext()); + ctx.builder.getContext().setDiscardValueNames(shouldDiscardValueNames); } if (!Mod) { @@ -932,7 +933,9 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar if (jl_is_string(ir)) { SMDiagnostic Err = SMDiagnostic(); + ctx.builder.getContext().setDiscardValueNames(false); Mod = parseAssemblyString(jl_string_data(ir), Err, ctx.builder.getContext()); + ctx.builder.getContext().setDiscardValueNames(shouldDiscardValueNames); if (!Mod) { std::string message = "Failed to parse LLVM assembly: \n"; raw_string_ostream stream(message); @@ -1712,18 +1715,12 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) return ghostValue(ctx, jl_nothing_type); } else if (is_libjulia_func(jl_get_tls_world_age)) { - bool toplevel = !(ctx.linfo && jl_is_method(ctx.linfo->def.method)); - if (!toplevel) { // top level code does not see a stable world age during execution - ++CCALL_STAT(jl_get_tls_world_age); - assert(lrt == ctx.types().T_size); - assert(!isVa && !llvmcall && nccallargs == 0); - JL_GC_POP(); - Instruction *world_age = cast(ctx.world_age_at_entry); - setName(ctx.emission_context, world_age, "task_world_age"); - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); - ai.decorateInst(world_age); - return mark_or_box_ccall_result(ctx, world_age, retboxed, rt, unionall, static_rt); - } + ++CCALL_STAT(jl_get_tls_world_age); + assert(lrt == ctx.types().T_size); + assert(!isVa && !llvmcall && nccallargs == 0); + JL_GC_POP(); + Value *world_age = get_tls_world_age(ctx); + return mark_or_box_ccall_result(ctx, world_age, retboxed, rt, unionall, static_rt); } else if (is_libjulia_func(jl_get_world_counter)) { ++CCALL_STAT(jl_get_world_counter); @@ -1880,33 +1877,6 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) JL_GC_POP(); return mark_julia_type(ctx, obj, true, jl_any_type); } - else if (is_libjulia_func(jl_alloc_genericmemory)) { - ++CCALL_STAT(jl_alloc_genericmemory); - assert(lrt == ctx.types().T_prjlvalue); - assert(!isVa && !llvmcall && nccallargs == 2); - const jl_cgval_t &typ = argv[0]; - const jl_cgval_t &nel = argv[1]; - auto arg_typename = [&] JL_NOTSAFEPOINT { - auto istyp = argv[0].constant; - std::string type_str; - if (istyp && jl_is_datatype(istyp) && jl_is_genericmemory_type(istyp)){ - auto eltype = jl_tparam1(istyp); - if (jl_is_datatype(eltype)) - type_str = jl_symbol_name(((jl_datatype_t*)eltype)->name->name); - else if (jl_is_uniontype(eltype)) - type_str = "Union"; - else - type_str = ""; - } - else - type_str = ""; - return "Memory{" + type_str + "}[]"; - }; - auto alloc = ctx.builder.CreateCall(prepare_call(jl_allocgenericmemory), { boxed(ctx,typ), emit_unbox(ctx, ctx.types().T_size, nel, (jl_value_t*)jl_ulong_type)}); - setName(ctx.emission_context, alloc, arg_typename); - JL_GC_POP(); - return mark_julia_type(ctx, alloc, true, jl_any_type); - } else if (is_libjulia_func(memcpy) && (rt == (jl_value_t*)jl_nothing_type || jl_is_cpointer_type(rt))) { ++CCALL_STAT(memcpy); const jl_cgval_t &dst = argv[0]; diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 7d4bd917eff30..6aabc459d2c91 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -60,11 +60,7 @@ static Value *decay_derived(jl_codectx_t &ctx, Value *V) if (T->getPointerAddressSpace() == AddressSpace::Derived) return V; // Once llvm deletes pointer element types, we won't need it here any more either. - #if JL_LLVM_VERSION >= 170000 Type *NewT = PointerType::get(T, AddressSpace::Derived); - #else - Type *NewT = PointerType::getWithSamePointeeType(cast(T), AddressSpace::Derived); - #endif return ctx.builder.CreateAddrSpaceCast(V, NewT); } @@ -74,11 +70,7 @@ static Value *maybe_decay_tracked(jl_codectx_t &ctx, Value *V) Type *T = V->getType(); if (T->getPointerAddressSpace() != AddressSpace::Tracked) return V; - #if JL_LLVM_VERSION >= 170000 Type *NewT = PointerType::get(T, AddressSpace::Derived); - #else - Type *NewT = PointerType::getWithSamePointeeType(cast(T), AddressSpace::Derived); - #endif return ctx.builder.CreateAddrSpaceCast(V, NewT); } @@ -1010,54 +1002,6 @@ static void emit_memcpy_llvm(jl_codectx_t &ctx, Value *dst, jl_aliasinfo_t const { if (sz == 0) return; - #if JL_LLVM_VERSION < 170000 - // If the types are small and simple, use load and store directly. - // Going through memcpy can cause LLVM (e.g. SROA) to create bitcasts between float and int - // that interferes with other optimizations. - // TODO: Restore this for opaque pointers? Needs extra type information from the caller. - if (ctx.builder.getContext().supportsTypedPointers() && sz <= 64) { - // The size limit is arbitrary but since we mainly care about floating points and - // machine size vectors this should be enough. - const DataLayout &DL = jl_Module->getDataLayout(); - auto srcty = cast(src->getType()); - //TODO unsafe nonopaque pointer - auto srcel = srcty->getNonOpaquePointerElementType(); - auto dstty = cast(dst->getType()); - //TODO unsafe nonopaque pointer - auto dstel = dstty->getNonOpaquePointerElementType(); - while (srcel->isArrayTy() && srcel->getArrayNumElements() == 1) { - src = ctx.builder.CreateConstInBoundsGEP2_32(srcel, src, 0, 0); - srcel = srcel->getArrayElementType(); - srcty = srcel->getPointerTo(); - } - while (dstel->isArrayTy() && dstel->getArrayNumElements() == 1) { - dst = ctx.builder.CreateConstInBoundsGEP2_32(dstel, dst, 0, 0); - dstel = dstel->getArrayElementType(); - dstty = dstel->getPointerTo(); - } - - llvm::Type *directel = nullptr; - if (srcel->isSized() && srcel->isSingleValueType() && DL.getTypeStoreSize(srcel) == sz) { - directel = srcel; - dst = emit_bitcast(ctx, dst, srcty); - } - else if (dstel->isSized() && dstel->isSingleValueType() && - DL.getTypeStoreSize(dstel) == sz) { - directel = dstel; - src = emit_bitcast(ctx, src, dstty); - } - if (directel) { - if (isa(src) && !src->hasName()) - setName(ctx.emission_context, src, "memcpy_refined_src"); - if (isa(dst) && !dst->hasName()) - setName(ctx.emission_context, dst, "memcpy_refined_dst"); - auto val = src_ai.decorateInst(ctx.builder.CreateAlignedLoad(directel, src, MaybeAlign(align_src), is_volatile)); - dst_ai.decorateInst(ctx.builder.CreateAlignedStore(val, dst, align_dst, is_volatile)); - ++SkippedMemcpys; - return; - } - } - #endif ++EmittedMemcpys; // the memcpy intrinsic does not allow to specify different alias tags @@ -1604,19 +1548,24 @@ static void emit_error(jl_codectx_t &ctx, const Twine &txt) } // DO NOT PASS IN A CONST CONDITION! -static void error_unless(jl_codectx_t &ctx, Value *cond, const Twine &msg) +static void error_unless(jl_codectx_t &ctx, Function *F, Value *cond, const Twine &msg) { ++EmittedConditionalErrors; BasicBlock *failBB = BasicBlock::Create(ctx.builder.getContext(), "fail", ctx.f); BasicBlock *passBB = BasicBlock::Create(ctx.builder.getContext(), "pass"); ctx.builder.CreateCondBr(cond, passBB, failBB); ctx.builder.SetInsertPoint(failBB); - just_emit_error(ctx, prepare_call(jlerror_func), msg); + just_emit_error(ctx, F, msg); ctx.builder.CreateUnreachable(); passBB->insertInto(ctx.f); ctx.builder.SetInsertPoint(passBB); } +static void error_unless(jl_codectx_t &ctx, Value *cond, const Twine &msg) +{ + error_unless(ctx, prepare_call(jlerror_func), cond, msg); +} + static void raise_exception(jl_codectx_t &ctx, Value *exc, BasicBlock *contBB=nullptr) { @@ -1940,7 +1889,7 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, // actual `isa` calls, this optimization should already have been performed upstream // anyway, but having this optimization in codegen might still be beneficial for // `typeassert`s if we can make it correct. - Optional known_isa; + std::optional known_isa; jl_value_t *intersected_type = type; if (x.constant) known_isa = jl_isa(x.constant, type); @@ -2335,7 +2284,7 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx, const jl_cgval_t argv[3] = { cmp, lhs, rhs }; jl_cgval_t ret; if (modifyop) { - ret = emit_invoke(ctx, *modifyop, argv, 3, (jl_value_t*)jl_any_type); + ret = emit_invoke(ctx, *modifyop, argv, 3, (jl_value_t*)jl_any_type, nullptr); } else { if (trim_may_error(ctx.params->trim)) { @@ -3786,11 +3735,7 @@ static void recursively_adjust_ptr_type(llvm::Value *Val, unsigned FromAS, unsig for (auto *User : Val->users()) { if (isa(User)) { GetElementPtrInst *Inst = cast(User); - #if JL_LLVM_VERSION >= 170000 Inst->mutateType(PointerType::get(Inst->getType(), ToAS)); - #else - Inst->mutateType(PointerType::getWithSamePointeeType(cast(Inst->getType()), ToAS)); - #endif recursively_adjust_ptr_type(Inst, FromAS, ToAS); } else if (isa(User)) { @@ -3799,11 +3744,7 @@ static void recursively_adjust_ptr_type(llvm::Value *Val, unsigned FromAS, unsig } else if (isa(User)) { BitCastInst *Inst = cast(User); - #if JL_LLVM_VERSION >= 170000 Inst->mutateType(PointerType::get(Inst->getType(), ToAS)); - #else - Inst->mutateType(PointerType::getWithSamePointeeType(cast(Inst->getType()), ToAS)); - #endif recursively_adjust_ptr_type(Inst, FromAS, ToAS); } } @@ -4082,7 +4023,7 @@ static jl_cgval_t union_store(jl_codectx_t &ctx, emit_lockstate_value(ctx, needlock, false); const jl_cgval_t argv[3] = { cmp, oldval, rhs }; if (modifyop) { - rhs = emit_invoke(ctx, *modifyop, argv, 3, (jl_value_t*)jl_any_type); + rhs = emit_invoke(ctx, *modifyop, argv, 3, (jl_value_t*)jl_any_type, nullptr); } else { if (trim_may_error(ctx.params->trim)) { @@ -4491,6 +4432,107 @@ static int compare_cgparams(const jl_cgparams_t *a, const jl_cgparams_t *b) } #endif +static jl_cgval_t emit_memorynew(jl_codectx_t &ctx, jl_datatype_t *typ, jl_cgval_t nel, jl_genericmemory_t *inst) +{ + emit_typecheck(ctx, nel, (jl_value_t*)jl_long_type, "memorynew"); + nel = update_julia_type(ctx, nel, (jl_value_t*)jl_long_type); + if (nel.typ == jl_bottom_type) + return jl_cgval_t(); + + const jl_datatype_layout_t *layout = ((jl_datatype_t*)typ)->layout; + assert(((jl_datatype_t*)typ)->has_concrete_subtype && layout != NULL); + size_t elsz = layout->size; + int isboxed = layout->flags.arrayelem_isboxed; + int isunion = layout->flags.arrayelem_isunion; + int zi = ((jl_datatype_t*)typ)->zeroinit; + if (isboxed) + elsz = sizeof(void*); + + auto ptls = get_current_ptls(ctx); + auto T_size = ctx.types().T_size; + auto int8t = getInt8Ty(ctx.builder.getContext()); + BasicBlock *emptymemBB, *nonemptymemBB, *retvalBB; + emptymemBB = BasicBlock::Create(ctx.builder.getContext(), "emptymem"); + nonemptymemBB = BasicBlock::Create(ctx.builder.getContext(), "nonemptymem"); + retvalBB = BasicBlock::Create(ctx.builder.getContext(), "retval"); + auto nel_unboxed = emit_unbox(ctx, ctx.types().T_size, nel, (jl_value_t*)jl_long_type); + Value *memorynew_empty = ctx.builder.CreateICmpEQ(nel_unboxed, ConstantInt::get(T_size, 0)); + setName(ctx.emission_context, memorynew_empty, "memorynew_empty"); + ctx.builder.CreateCondBr(memorynew_empty, emptymemBB, nonemptymemBB); + // if nel == 0 + emptymemBB->insertInto(ctx.f); + ctx.builder.SetInsertPoint(emptymemBB); + auto emptyalloc = track_pjlvalue(ctx, literal_pointer_val(ctx, (jl_value_t*)inst)); + ctx.builder.CreateBr(retvalBB); + nonemptymemBB->insertInto(ctx.f); + ctx.builder.SetInsertPoint(nonemptymemBB); + // else actually allocate mem + auto arg_typename = [&] JL_NOTSAFEPOINT { + std::string type_str; + auto eltype = jl_tparam1(typ); + if (jl_is_datatype(eltype)) + type_str = jl_symbol_name(((jl_datatype_t*)eltype)->name->name); + else if (jl_is_uniontype(eltype)) + type_str = "Union"; + else + type_str = ""; + return "Memory{" + type_str + "}[]"; + }; + auto cg_typ = literal_pointer_val(ctx, (jl_value_t*) typ); + auto cg_elsz = ConstantInt::get(T_size, elsz); + + FunctionCallee intr = Intrinsic::getDeclaration(jl_Module, Intrinsic::smul_with_overflow, ArrayRef(T_size)); + // compute nbytes with possible overflow + Value *prod_with_overflow = ctx.builder.CreateCall(intr, {nel_unboxed, cg_elsz}); + Value *nbytes = ctx.builder.CreateExtractValue(prod_with_overflow, 0); + Value *overflow = ctx.builder.CreateExtractValue(prod_with_overflow, 1); + if (isunion) { + // if isunion, we need to allocate the union selector bytes as well + intr = Intrinsic::getDeclaration(jl_Module, Intrinsic::sadd_with_overflow, ArrayRef(T_size)); + Value *add_with_overflow = ctx.builder.CreateCall(intr, {nel_unboxed, nbytes}); + nbytes = ctx.builder.CreateExtractValue(add_with_overflow, 0); + Value *overflow1 = ctx.builder.CreateExtractValue(add_with_overflow, 1); + overflow = ctx.builder.CreateOr(overflow, overflow1); + } + Value *negnel = ctx.builder.CreateICmpSLT(nel_unboxed, ConstantInt::get(T_size, 0)); + overflow = ctx.builder.CreateOr(overflow, negnel); + auto cg_typemax_int = ConstantInt::get(T_size, (((size_t)-1)>>1)-1); + Value *tobignel = ctx.builder.CreateICmpSLT(cg_typemax_int, elsz == 0 ? nel_unboxed: nbytes); + overflow = ctx.builder.CreateOr(overflow, tobignel); + Value *notoverflow = ctx.builder.CreateNot(overflow); + error_unless(ctx, prepare_call(jlargumenterror_func), notoverflow, "invalid GenericMemory size: the number of elements is either negative or too large for system address width"); + // actually allocate + auto call = prepare_call(jl_alloc_genericmemory_unchecked_func); + Value *alloc = ctx.builder.CreateCall(call, { ptls, nbytes, cg_typ}); + // set length (jl_alloc_genericmemory_unchecked_func doesn't have it) + Value *decay_alloc = decay_derived(ctx, alloc); + Value *len_field = ctx.builder.CreateStructGEP(ctx.types().T_jlgenericmemory, decay_alloc, 0); + auto len_store = ctx.builder.CreateAlignedStore(nel_unboxed, len_field, Align(sizeof(void*))); + auto aliasinfo = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_memorylen); + aliasinfo.decorateInst(len_store); + //This avoids the length store from being deleted which is illegal + ctx.builder.CreateFence(AtomicOrdering::Release, SyncScope::SingleThread); + // zeroinit pointers and unions + if (zi) { + Value *memory_ptr = ctx.builder.CreateStructGEP(ctx.types().T_jlgenericmemory, decay_alloc, 1); + auto *load = ctx.builder.CreateAlignedLoad(ctx.types().T_ptr, memory_ptr, Align(sizeof(void*))); + aliasinfo = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_memoryptr); + aliasinfo.decorateInst(load); + ctx.builder.CreateMemSet(load, ConstantInt::get(int8t, 0), nbytes, Align(sizeof(void*))); + } + + setName(ctx.emission_context, alloc, arg_typename); + ctx.builder.CreateBr(retvalBB); + nonemptymemBB = ctx.builder.GetInsertBlock(); + // phi node to choose which side of branch + retvalBB->insertInto(ctx.f); + ctx.builder.SetInsertPoint(retvalBB); + auto phi = ctx.builder.CreatePHI(ctx.types().T_prjlvalue, 2); + phi->addIncoming(emptyalloc, emptymemBB); + phi->addIncoming(alloc, nonemptymemBB); + return mark_julia_type(ctx, phi, true, typ); +} + static jl_cgval_t _emit_memoryref(jl_codectx_t &ctx, Value *mem, Value *data, const jl_datatype_layout_t *layout, jl_value_t *typ) { //jl_cgval_t argv[] = { diff --git a/src/clangsa/GCChecker.cpp b/src/clangsa/GCChecker.cpp index cac89a6761d01..40093ca15859b 100644 --- a/src/clangsa/GCChecker.cpp +++ b/src/clangsa/GCChecker.cpp @@ -847,7 +847,8 @@ bool GCChecker::isGCTrackedType(QualType QT) { Name.ends_with_insensitive("jl_vararg_t") || Name.ends_with_insensitive("jl_opaque_closure_t") || Name.ends_with_insensitive("jl_globalref_t") || - // Probably not technically true for these, but let's allow it + // Probably not technically true for these, but let's allow it as a root + Name.ends_with_insensitive("jl_ircode_state") || Name.ends_with_insensitive("typemap_intersection_env") || Name.ends_with_insensitive("interpreter_state") || Name.ends_with_insensitive("jl_typeenv_t") || diff --git a/src/codegen.cpp b/src/codegen.cpp index 3645a0b25827e..86c9afd134d9a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -23,11 +23,7 @@ #include #include #include -#if JL_LLVM_VERSION >= 170000 #include -#else -#include -#endif #include #include @@ -81,6 +77,10 @@ #include #include +#ifdef USE_ITTAPI +#include "ittapi/ittnotify.h" +#endif + using namespace llvm; static bool jl_fpo_disabled(const Triple &TT) { @@ -167,7 +167,7 @@ void setName(jl_codegen_params_t ¶ms, Value *V, const Twine &Name) // is not checking that setName is only called for non-folded instructions (e.g. folded bitcasts // and 0-byte geps), which can result in information loss on the renamed instruction. assert((isa(V) || isa(V)) && "Should only set names on instructions!"); - if (params.debug_level >= 2 && !isa(V)) { + if (!isa(V)) { V->setName(Name); } } @@ -175,23 +175,21 @@ void setName(jl_codegen_params_t ¶ms, Value *V, const Twine &Name) void maybeSetName(jl_codegen_params_t ¶ms, Value *V, const Twine &Name) { // To be used when we may get an Instruction or something that is not an instruction i.e Constants/Arguments - if (params.debug_level >= 2 && isa(V)) { + if (isa(V)) V->setName(Name); - } } void setName(jl_codegen_params_t ¶ms, Value *V, std::function GetName) { assert((isa(V) || isa(V)) && "Should only set names on instructions!"); - if (params.debug_level >= 2 && !isa(V)) { + if (!params.getContext().shouldDiscardValueNames() && !isa(V)) V->setName(Twine(GetName())); - } } void setNameWithField(jl_codegen_params_t ¶ms, Value *V, std::function GetObjName, jl_datatype_t *jt, unsigned idx, const Twine &suffix) { assert((isa(V) || isa(V)) && "Should only set names on instructions!"); - if (params.debug_level >= 2 && !isa(V)) { + if (!params.getContext().shouldDiscardValueNames() && !isa(V)) { if (jl_is_tuple_type(jt)){ V->setName(Twine(GetObjName()) + "[" + Twine(idx + 1) + "]"+ suffix); return; @@ -635,11 +633,7 @@ static AttributeList get_func_attrs(LLVMContext &C) static AttributeList get_donotdelete_func_attrs(LLVMContext &C) { AttrBuilder FnAttrs(C); -#if JL_LLVM_VERSION >= 160000 FnAttrs.addMemoryAttr(MemoryEffects::inaccessibleMemOnly()); -#else - FnAttrs.addAttribute(Attribute::InaccessibleMemOnly); -#endif FnAttrs.addAttribute(Attribute::WillReturn); FnAttrs.addAttribute(Attribute::NoUnwind); return AttributeList::get(C, @@ -669,11 +663,7 @@ static AttributeList get_attrs_box_float(LLVMContext &C, unsigned nbytes) auto FnAttrs = AttrBuilder(C); FnAttrs.addAttribute(Attribute::WillReturn); FnAttrs.addAttribute(Attribute::NoUnwind); -#if JL_LLVM_VERSION >= 160000 FnAttrs.addMemoryAttr(MemoryEffects::inaccessibleMemOnly()); -#else - FnAttrs.addAttribute(Attribute::InaccessibleMemOnly); -#endif auto RetAttrs = AttrBuilder(C); RetAttrs.addAttribute(Attribute::NonNull); RetAttrs.addDereferenceableAttr(nbytes); @@ -689,11 +679,7 @@ static AttributeList get_attrs_box_sext(LLVMContext &C, unsigned nbytes) auto FnAttrs = AttrBuilder(C); FnAttrs.addAttribute(Attribute::WillReturn); FnAttrs.addAttribute(Attribute::NoUnwind); -#if JL_LLVM_VERSION >= 160000 FnAttrs.addMemoryAttr(MemoryEffects::inaccessibleMemOnly()); -#else - FnAttrs.addAttribute(Attribute::InaccessibleMemOnly); -#endif auto RetAttrs = AttrBuilder(C); RetAttrs.addAttribute(Attribute::NonNull); RetAttrs.addAttribute(Attribute::getWithDereferenceableBytes(C, nbytes)); @@ -710,11 +696,7 @@ static AttributeList get_attrs_box_zext(LLVMContext &C, unsigned nbytes) auto FnAttrs = AttrBuilder(C); FnAttrs.addAttribute(Attribute::WillReturn); FnAttrs.addAttribute(Attribute::NoUnwind); -#if JL_LLVM_VERSION >= 160000 FnAttrs.addMemoryAttr(MemoryEffects::inaccessibleMemOnly()); -#else - FnAttrs.addAttribute(Attribute::InaccessibleMemOnly); -#endif auto RetAttrs = AttrBuilder(C); RetAttrs.addAttribute(Attribute::NonNull); RetAttrs.addDereferenceableAttr(nbytes); @@ -819,6 +801,12 @@ static const auto jlerror_func = new JuliaFunction<>{ {getPointerTy(C)}, false); }, get_attrs_noreturn, }; +static const auto jlargumenterror_func = new JuliaFunction<>{ + XSTR(jl_argument_error), + [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), + {getPointerTy(C)}, false); }, + get_attrs_noreturn, +}; static const auto jlatomicerror_func = new JuliaFunction<>{ XSTR(jl_atomic_error), [](LLVMContext &C) { return FunctionType::get(getVoidTy(C), @@ -1139,12 +1127,7 @@ static const auto jlegalx_func = new JuliaFunction{ return FunctionType::get(getInt32Ty(C), {T, T, T_size}, false); }, [](LLVMContext &C) { AttrBuilder FnAttrs(C); -#if JL_LLVM_VERSION >= 160000 FnAttrs.addMemoryAttr(MemoryEffects::inaccessibleOrArgMemOnly()); -#else - FnAttrs.addAttribute(Attribute::ReadOnly); - FnAttrs.addAttribute(Attribute::ArgMemOnly); -#endif FnAttrs.addAttribute(Attribute::NoUnwind); return AttributeList::get(C, AttributeSet::get(C, FnAttrs), @@ -1164,9 +1147,30 @@ static const auto jl_alloc_obj_func = new JuliaFunction{ auto FnAttrs = AttrBuilder(C); FnAttrs.addAllocSizeAttr(1, None); // returns %1 bytes FnAttrs.addAllocKindAttr(AllocFnKind::Alloc); -#if JL_LLVM_VERSION >= 160000 FnAttrs.addMemoryAttr(MemoryEffects::argMemOnly(ModRefInfo::Ref) | MemoryEffects::inaccessibleMemOnly(ModRefInfo::ModRef)); -#endif + FnAttrs.addAttribute(Attribute::WillReturn); + FnAttrs.addAttribute(Attribute::NoUnwind); + auto RetAttrs = AttrBuilder(C); + RetAttrs.addAttribute(Attribute::NoAlias); + RetAttrs.addAttribute(Attribute::NonNull); + return AttributeList::get(C, + AttributeSet::get(C, FnAttrs), + AttributeSet::get(C, RetAttrs), + None); + }, +}; +static const auto jl_alloc_genericmemory_unchecked_func = new JuliaFunction{ + XSTR(jl_alloc_genericmemory_unchecked), + [](LLVMContext &C, Type *T_size) { + auto T_jlvalue = JuliaType::get_jlvalue_ty(C); + auto T_prjlvalue = PointerType::get(T_jlvalue, AddressSpace::Tracked); + auto T_pjlvalue = PointerType::get(T_jlvalue, 0); + return FunctionType::get(T_prjlvalue, + {T_pjlvalue, T_size, T_pjlvalue}, false); + }, + [](LLVMContext &C) { + auto FnAttrs = AttrBuilder(C); + FnAttrs.addMemoryAttr(MemoryEffects::argMemOnly(ModRefInfo::Ref) | MemoryEffects::inaccessibleMemOnly(ModRefInfo::ModRef)); FnAttrs.addAttribute(Attribute::WillReturn); FnAttrs.addAttribute(Attribute::NoUnwind); auto RetAttrs = AttrBuilder(C); @@ -1202,11 +1206,7 @@ static const auto jl_typeof_func = new JuliaFunction<>{ }, [](LLVMContext &C) { AttrBuilder FnAttrs(C); -#if JL_LLVM_VERSION >= 160000 FnAttrs.addMemoryAttr(MemoryEffects::none()); -#else - FnAttrs.addAttribute(Attribute::ReadNone); -#endif FnAttrs.addAttribute(Attribute::NoUnwind); FnAttrs.addAttribute(Attribute::NoRecurse); return AttributeList::get(C, @@ -1221,11 +1221,7 @@ static const auto jl_write_barrier_func = new JuliaFunction<>{ {JuliaType::get_prjlvalue_ty(C)}, true); }, [](LLVMContext &C) { AttrBuilder FnAttrs(C); -#if JL_LLVM_VERSION >= 160000 FnAttrs.addMemoryAttr(MemoryEffects::inaccessibleMemOnly()); -#else - FnAttrs.addAttribute(Attribute::InaccessibleMemOnly); -#endif FnAttrs.addAttribute(Attribute::NoUnwind); FnAttrs.addAttribute(Attribute::NoRecurse); return AttributeList::get(C, @@ -1298,12 +1294,7 @@ static const auto memcmp_func = new JuliaFunction{ {getPointerTy(C), getPointerTy(C), T_size}, false); }, [](LLVMContext &C) { AttrBuilder FnAttrs(C); -#if JL_LLVM_VERSION >= 160000 FnAttrs.addMemoryAttr(MemoryEffects::argMemOnly(ModRefInfo::Ref)); -#else - FnAttrs.addAttribute(Attribute::ArgMemOnly); - FnAttrs.addAttribute(Attribute::ReadOnly); -#endif FnAttrs.addAttribute(Attribute::NoUnwind); return AttributeList::get(C, AttributeSet::get(C, FnAttrs), @@ -1353,11 +1344,7 @@ static const auto jlfieldindex_func = new JuliaFunction<>{ }, [](LLVMContext &C) { AttrBuilder FnAttrs(C); -#if JL_LLVM_VERSION >= 160000 FnAttrs.addMemoryAttr(MemoryEffects::readOnly()); -#else - FnAttrs.addAttribute(Attribute::ReadOnly); -#endif FnAttrs.addAttribute(Attribute::NoUnwind); FnAttrs.addAttribute(Attribute::WillReturn); return AttributeList::get(C, @@ -1423,9 +1410,7 @@ static const auto jl_allocgenericmemory = new JuliaFunction= 160000 FnAttrs.addMemoryAttr(MemoryEffects::inaccessibleMemOnly(ModRefInfo::ModRef) | MemoryEffects::argMemOnly(ModRefInfo::Ref)); -#endif FnAttrs.addAttribute(Attribute::WillReturn); RetAttrs.addAlignmentAttr(Align(16)); RetAttrs.addAttribute(Attribute::NonNull); @@ -1503,11 +1488,7 @@ static const auto pointer_from_objref_func = new JuliaFunction<>{ {PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::Derived)}, false); }, [](LLVMContext &C) { AttrBuilder FnAttrs(C); -#if JL_LLVM_VERSION >= 160000 FnAttrs.addMemoryAttr(MemoryEffects::none()); -#else - FnAttrs.addAttribute(Attribute::ReadNone); -#endif FnAttrs.addAttribute(Attribute::NoUnwind); return AttributeList::get(C, AttributeSet::get(C, FnAttrs), @@ -1530,11 +1511,7 @@ static const auto gc_loaded_func = new JuliaFunction<>{ FnAttrs.addAttribute(Attribute::Speculatable); FnAttrs.addAttribute(Attribute::WillReturn); FnAttrs.addAttribute(Attribute::NoRecurse); -#if JL_LLVM_VERSION >= 160000 FnAttrs.addMemoryAttr(MemoryEffects::none()); -#else - FnAttrs.addAttribute(Attribute::ReadNone); -#endif AttrBuilder RetAttrs(C); RetAttrs.addAttribute(Attribute::NonNull); RetAttrs.addAttribute(Attribute::NoUndef); @@ -1626,6 +1603,7 @@ static const auto &builtin_func_map() { { jl_f_nfields_addr, new JuliaFunction<>{XSTR(jl_f_nfields), get_func_sig, get_func_attrs} }, { jl_f__expr_addr, new JuliaFunction<>{XSTR(jl_f__expr), get_func_sig, get_func_attrs} }, { jl_f__typevar_addr, new JuliaFunction<>{XSTR(jl_f__typevar), get_func_sig, get_func_attrs} }, + { jl_f_memorynew_addr, new JuliaFunction<>{XSTR(jl_f_memorynew), get_func_sig, get_func_attrs} }, { jl_f_memoryref_addr, new JuliaFunction<>{XSTR(jl_f_memoryref), get_func_sig, get_func_attrs} }, { jl_f_memoryrefoffset_addr, new JuliaFunction<>{XSTR(jl_f_memoryrefoffset), get_func_sig, get_func_attrs} }, { jl_f_memoryrefset_addr, new JuliaFunction<>{XSTR(jl_f_memoryrefset), get_func_sig, get_func_attrs} }, @@ -1985,7 +1963,7 @@ class jl_codectx_t { Value *pgcstack = NULL; Instruction *topalloca = NULL; - Value *world_age_at_entry = NULL; // Not valid to use in toplevel code + Value *world_age_at_entry = NULL; bool use_cache = false; bool external_linkage = false; @@ -2115,6 +2093,7 @@ static jl_cgval_t emit_sparam(jl_codectx_t &ctx, size_t i); static Value *emit_condition(jl_codectx_t &ctx, const jl_cgval_t &condV, const Twine &msg); static Value *get_current_task(jl_codectx_t &ctx); static Value *get_current_ptls(jl_codectx_t &ctx); +static Value *get_tls_world_age(jl_codectx_t &ctx); static Value *get_scope_field(jl_codectx_t &ctx); static Value *get_tls_world_age_field(jl_codectx_t &ctx); static void CreateTrap(IRBuilder<> &irbuilder, bool create_new_block = true); @@ -2125,7 +2104,7 @@ static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction<> *theFptr, Value static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2, Value *nullcheck1 = nullptr, Value *nullcheck2 = nullptr); static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t nargs, ArrayRef argv, bool is_promotable=false); -static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayRef argv, size_t nargs, jl_value_t *rt); +static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayRef argv, size_t nargs, jl_value_t *rt, Value *age_ok); static Value *literal_pointer_val(jl_codectx_t &ctx, jl_value_t *p); static unsigned julia_alignment(jl_value_t *jt); @@ -2192,18 +2171,18 @@ static jl_value_t *StackFrame( return frame; } -static void push_frames(jl_codectx_t &ctx, jl_method_instance_t *caller, jl_method_instance_t *callee, int no_debug=false) +static void push_frames(jl_codectx_t &ctx, jl_method_instance_t *caller, jl_method_instance_t *callee) { CallFrames frames; auto it = ctx.emission_context.enqueuers.find(callee); if (it != ctx.emission_context.enqueuers.end()) return; - if (no_debug) { // Used in tojlinvoke + auto DL = ctx.builder.getCurrentDebugLocation(); + if (caller == nullptr || !DL) { // Used in various places frames.push_back({ctx.funcName, "", 0}); ctx.emission_context.enqueuers.insert({callee, {caller, std::move(frames)}}); return; } - auto DL = ctx.builder.getCurrentDebugLocation(); auto filename = std::string(DL->getFilename()); auto line = DL->getLine(); auto fname = std::string(DL->getScope()->getSubprogram()->getName()); @@ -2437,11 +2416,7 @@ static Value *emit_inttoptr(jl_codectx_t &ctx, Value *v, Type *ty) auto ptr = I->getOperand(0); if (ty->getPointerAddressSpace() == ptr->getType()->getPointerAddressSpace()) return ptr; - #if JL_LLVM_VERSION >= 170000 else - #else - else if (cast(ty)->hasSameElementTypeAs(cast(ptr->getType()))) - #endif return ctx.builder.CreateAddrSpaceCast(ptr, ty); } ++EmittedIntToPtrs; @@ -4476,6 +4451,20 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, return true; } + else if (f == jl_builtin_memorynew && (nargs == 2)) { + const jl_cgval_t &memty = argv[1]; + if (!memty.constant) + return false; + jl_datatype_t *typ = (jl_datatype_t*) memty.constant; + if (!jl_is_concrete_type((jl_value_t*)typ) || !jl_is_genericmemory_type(typ)) + return false; + jl_genericmemory_t *inst = (jl_genericmemory_t*)((jl_datatype_t*)typ)->instance; + if (inst == NULL) + return false; + *ret = emit_memorynew(ctx, typ, argv[2], inst); + return true; + } + else if (f == jl_builtin_memoryref && nargs == 1) { const jl_cgval_t &mem = argv[1]; jl_datatype_t *mty_dt = (jl_datatype_t*)jl_unwrap_unionall(mem.typ); @@ -5260,14 +5249,12 @@ static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction<> *theFptr, Value return emit_jlcall(ctx, prepare_call(theFptr), theF, argv, nargs, trampoline); } -static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_closure, jl_value_t *specTypes, jl_value_t *jlretty, jl_returninfo_t &returninfo, jl_code_instance_t *fromexternal, - ArrayRef argv, size_t nargs) +static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_closure, jl_value_t *specTypes, jl_value_t *jlretty, jl_returninfo_t &returninfo, ArrayRef argv, size_t nargs) { ++EmittedSpecfunCalls; // emit specialized call site bool gcstack_arg = JL_FEAT_TEST(ctx, gcstack_arg); - FunctionType *cft = returninfo.decl.getFunctionType(); - size_t nfargs = cft->getNumParams(); + size_t nfargs = returninfo.decl.getFunctionType()->getNumParams(); SmallVector argvals(nfargs); unsigned idx = 0; AllocaInst *result = nullptr; @@ -5359,23 +5346,7 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos idx++; } assert(idx == nfargs); - Value *TheCallee = returninfo.decl.getCallee(); - if (fromexternal) { - std::string namep("p"); - namep += cast(returninfo.decl.getCallee())->getName(); - GlobalVariable *GV = cast_or_null(jl_Module->getNamedValue(namep)); - if (GV == nullptr) { - GV = new GlobalVariable(*jl_Module, TheCallee->getType(), false, - GlobalVariable::ExternalLinkage, - Constant::getNullValue(TheCallee->getType()), - namep); - ctx.emission_context.external_fns[std::make_tuple(fromexternal, true)] = GV; - } - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); - TheCallee = ai.decorateInst(ctx.builder.CreateAlignedLoad(TheCallee->getType(), GV, Align(sizeof(void*)))); - setName(ctx.emission_context, TheCallee, namep); - } - CallInst *call = ctx.builder.CreateCall(cft, TheCallee, argvals); + CallInst *call = ctx.builder.CreateCall(returninfo.decl, argvals); call->setAttributes(returninfo.attrs); if (gcstack_arg && ctx.emission_context.use_swiftcc) call->setCallingConv(CallingConv::Swift); @@ -5417,7 +5388,7 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos } static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_closure, jl_value_t *specTypes, jl_value_t *jlretty, llvm::Value *callee, StringRef specFunctionObject, jl_code_instance_t *fromexternal, - ArrayRef argv, size_t nargs, jl_returninfo_t::CallingConv *cc, unsigned *nreturn_roots, jl_value_t *inferred_retty) + ArrayRef argv, size_t nargs, jl_returninfo_t::CallingConv *cc, unsigned *nreturn_roots, jl_value_t *inferred_retty, Value *age_ok) { ++EmittedSpecfunCalls; // emit specialized call site @@ -5425,21 +5396,52 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, bool is_opaque_clos jl_returninfo_t returninfo = get_specsig_function(ctx, jl_Module, callee, specFunctionObject, specTypes, jlretty, is_opaque_closure, gcstack_arg); *cc = returninfo.cc; *nreturn_roots = returninfo.return_roots; - jl_cgval_t retval = emit_call_specfun_other(ctx, is_opaque_closure, specTypes, jlretty, returninfo, fromexternal, argv, nargs); + if (fromexternal) { + std::string namep("p"); + Value *TheCallee = returninfo.decl.getCallee(); + namep += cast(TheCallee)->getName(); + GlobalVariable *GV = cast_or_null(jl_Module->getNamedValue(namep)); + if (GV == nullptr) { + GV = new GlobalVariable(*jl_Module, TheCallee->getType(), false, + GlobalVariable::ExternalLinkage, + Constant::getNullValue(TheCallee->getType()), + namep); + ctx.emission_context.external_fns[std::make_tuple(fromexternal, true)] = GV; + } + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); + TheCallee = ai.decorateInst(ctx.builder.CreateAlignedLoad(TheCallee->getType(), GV, Align(sizeof(void*)))); + setName(ctx.emission_context, TheCallee, namep); + returninfo.decl = FunctionCallee(returninfo.decl.getFunctionType(), TheCallee); + } + if (age_ok) { + std::string funcName(specFunctionObject); + funcName += "_gfthunk"; + Function *gf_thunk = Function::Create(returninfo.decl.getFunctionType(), + GlobalVariable::InternalLinkage, funcName, jl_Module); + jl_init_function(gf_thunk, ctx.emission_context.TargetTriple); + gf_thunk->setAttributes(AttributeList::get(gf_thunk->getContext(), {returninfo.attrs, gf_thunk->getAttributes()})); + // build a specsig -> jl_apply_generic converter thunk + // this builds a method that calls jl_apply_generic (as a closure over a singleton function pointer), + // but which has the signature of a specsig + emit_specsig_to_fptr1(gf_thunk, returninfo.cc, returninfo.return_roots, specTypes, jlretty, is_opaque_closure, nargs, ctx.emission_context, + prepare_call(jlapplygeneric_func)); + returninfo.decl = FunctionCallee(returninfo.decl.getFunctionType(), ctx.builder.CreateSelect(age_ok, returninfo.decl.getCallee(), gf_thunk)); + } + jl_cgval_t retval = emit_call_specfun_other(ctx, is_opaque_closure, specTypes, jlretty, returninfo, argv, nargs); // see if inference has a different / better type for the call than the lambda return update_julia_type(ctx, retval, inferred_retty); } static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_t *mi, jl_value_t *jlretty, StringRef specFunctionObject, jl_code_instance_t *fromexternal, - ArrayRef argv, size_t nargs, jl_returninfo_t::CallingConv *cc, unsigned *return_roots, jl_value_t *inferred_retty) + ArrayRef argv, size_t nargs, jl_returninfo_t::CallingConv *cc, unsigned *return_roots, jl_value_t *inferred_retty, Value *age_ok) { bool is_opaque_closure = jl_is_method(mi->def.value) && mi->def.method->is_for_opaque_closure; return emit_call_specfun_other(ctx, is_opaque_closure, mi->specTypes, jlretty, NULL, - specFunctionObject, fromexternal, argv, nargs, cc, return_roots, inferred_retty); + specFunctionObject, fromexternal, argv, nargs, cc, return_roots, inferred_retty, age_ok); } static jl_cgval_t emit_call_specfun_boxed(jl_codectx_t &ctx, jl_value_t *jlretty, StringRef specFunctionObject, jl_code_instance_t *fromexternal, - ArrayRef argv, size_t nargs, jl_value_t *inferred_retty) + ArrayRef argv, size_t nargs, jl_value_t *inferred_retty, Value *age_ok) { Value *theFptr; if (fromexternal) { @@ -5462,6 +5464,8 @@ static jl_cgval_t emit_call_specfun_boxed(jl_codectx_t &ctx, jl_value_t *jlretty theFptr = jl_Module->getOrInsertFunction(specFunctionObject, ctx.types().T_jlfunc).getCallee(); addRetAttr(cast(theFptr), Attribute::NonNull); } + if (age_ok) + theFptr = ctx.builder.CreateSelect(age_ok, theFptr, prepare_call(jlapplygeneric_func)); Value *ret = emit_jlcall(ctx, FunctionCallee(ctx.types().T_jlfunc, theFptr), nullptr, argv, nargs, julia_call); return update_julia_type(ctx, mark_julia_type(ctx, ret, true, jlretty), inferred_retty); } @@ -5480,10 +5484,10 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt) if (argv[i].typ == jl_bottom_type) return jl_cgval_t(); } - return emit_invoke(ctx, lival, argv, nargs, rt); + return emit_invoke(ctx, lival, argv, nargs, rt, nullptr); } -static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayRef argv, size_t nargs, jl_value_t *rt) +static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayRef argv, size_t nargs, jl_value_t *rt, Value *age_ok) { ++EmittedInvokes; bool handled = false; @@ -5506,12 +5510,12 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayR FunctionType *ft = ctx.f->getFunctionType(); StringRef protoname = ctx.f->getName(); if (ft == ctx.types().T_jlfunc) { - result = emit_call_specfun_boxed(ctx, ctx.rettype, protoname, nullptr, argv, nargs, rt); + result = emit_call_specfun_boxed(ctx, ctx.rettype, protoname, nullptr, argv, nargs, rt, age_ok); handled = true; } else if (ft != ctx.types().T_jlfuncparams) { unsigned return_roots = 0; - result = emit_call_specfun_other(ctx, mi, ctx.rettype, protoname, nullptr, argv, nargs, &cc, &return_roots, rt); + result = emit_call_specfun_other(ctx, mi, ctx.rettype, protoname, nullptr, argv, nargs, &cc, &return_roots, rt, age_ok); handled = true; } } @@ -5524,62 +5528,70 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayR result = mark_julia_const(ctx, codeinst->rettype_const); handled = true; } - else if (invoke != jl_fptr_sparam_addr) { + else { bool specsig, needsparams; std::tie(specsig, needsparams) = uses_specsig(mi, codeinst->rettype, ctx.params->prefer_specsig); - std::string name; - StringRef protoname; - bool need_to_emit = true; - bool cache_valid = ctx.use_cache || ctx.external_linkage; - bool external = false; - - // Check if we already queued this up - auto it = ctx.call_targets.find(codeinst); - if (need_to_emit && it != ctx.call_targets.end()) { - assert(it->second.specsig == specsig); - protoname = it->second.decl->getName(); - need_to_emit = cache_valid = false; - } + if (needsparams) { + if (trim_may_error(ctx.params->trim)) + push_frames(ctx, ctx.linfo, mi); + Value *r = emit_jlcall(ctx, jlinvoke_func, track_pjlvalue(ctx, literal_pointer_val(ctx, (jl_value_t*)mi)), argv, nargs, julia_call2); + result = mark_julia_type(ctx, r, true, rt); + handled = true; + } else { + std::string name; + StringRef protoname; + bool need_to_emit = true; + bool cache_valid = ctx.use_cache || ctx.external_linkage; + bool external = false; + + // Check if we already queued this up + auto it = ctx.call_targets.find(codeinst); + if (need_to_emit && it != ctx.call_targets.end()) { + assert(it->second.specsig == specsig); + protoname = it->second.decl->getName(); + need_to_emit = cache_valid = false; + } - // Check if it is already compiled (either JIT or externally) - if (need_to_emit && cache_valid) { - // optimization: emit the correct name immediately, if we know it - // TODO: use `emitted` map here too to try to consolidate names? - uint8_t specsigflags; - jl_callptr_t invoke; - void *fptr; - jl_read_codeinst_invoke(codeinst, &specsigflags, &invoke, &fptr, 0); - if (specsig ? specsigflags & 0b1 : invoke == jl_fptr_args_addr) { - protoname = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)fptr, invoke, codeinst); - if (ctx.external_linkage) { - // TODO: Add !specsig support to aotcompile.cpp - // Check that the codeinst is containing native code - if (specsig && (specsigflags & 0b100)) { - external = true; + // Check if it is already compiled (either JIT or externally) + if (need_to_emit && cache_valid) { + // optimization: emit the correct name immediately, if we know it + // TODO: use `emitted` map here too to try to consolidate names? + uint8_t specsigflags; + jl_callptr_t invoke; + void *fptr; + jl_read_codeinst_invoke(codeinst, &specsigflags, &invoke, &fptr, 0); + if (specsig ? specsigflags & 0b1 : invoke == jl_fptr_args_addr) { + protoname = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)fptr, invoke, codeinst); + if (ctx.external_linkage) { + // TODO: Add !specsig support to aotcompile.cpp + // Check that the codeinst is containing native code + if (specsig && (specsigflags & 0b100)) { + external = true; + need_to_emit = false; + } + } + else { // ctx.use_cache need_to_emit = false; } } - else { // ctx.use_cache - need_to_emit = false; - } } - } - if (need_to_emit) { - raw_string_ostream(name) << (specsig ? "j_" : "j1_") << name_from_method_instance(mi) << "_" << jl_atomic_fetch_add_relaxed(&globalUniqueGeneratedNames, 1); - protoname = StringRef(name); - } - jl_returninfo_t::CallingConv cc = jl_returninfo_t::CallingConv::Boxed; - unsigned return_roots = 0; - if (specsig) - result = emit_call_specfun_other(ctx, mi, codeinst->rettype, protoname, external ? codeinst : nullptr, argv, nargs, &cc, &return_roots, rt); - else - result = emit_call_specfun_boxed(ctx, codeinst->rettype, protoname, external ? codeinst : nullptr, argv, nargs, rt); - handled = true; - if (need_to_emit) { - Function *trampoline_decl = cast(jl_Module->getNamedValue(protoname)); - ctx.call_targets[codeinst] = {cc, return_roots, trampoline_decl, nullptr, specsig}; - if (trim_may_error(ctx.params->trim)) - push_frames(ctx, ctx.linfo, mi); + if (need_to_emit) { + raw_string_ostream(name) << (specsig ? "j_" : "j1_") << name_from_method_instance(mi) << "_" << jl_atomic_fetch_add_relaxed(&globalUniqueGeneratedNames, 1); + protoname = StringRef(name); + } + jl_returninfo_t::CallingConv cc = jl_returninfo_t::CallingConv::Boxed; + unsigned return_roots = 0; + if (specsig) + result = emit_call_specfun_other(ctx, mi, codeinst->rettype, protoname, external ? codeinst : nullptr, argv, nargs, &cc, &return_roots, rt, age_ok); + else + result = emit_call_specfun_boxed(ctx, codeinst->rettype, protoname, external ? codeinst : nullptr, argv, nargs, rt, age_ok); + handled = true; + if (need_to_emit) { + Function *trampoline_decl = cast(jl_Module->getNamedValue(protoname)); + ctx.call_targets[codeinst] = {cc, return_roots, trampoline_decl, nullptr, specsig}; + if (trim_may_error(ctx.params->trim)) + push_frames(ctx, ctx.linfo, mi); + } } } } @@ -5597,8 +5609,8 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayR print_stacktrace(ctx, ctx.params->trim); } } - Value *r = emit_jlcall(ctx, jlinvoke_func, boxed(ctx, lival), argv, nargs, julia_call2); - result = mark_julia_type(ctx, r, true, rt); + Value *r = age_ok ? emit_jlcall(ctx, jlapplygeneric_func, nullptr, argv, nargs, julia_call) : emit_jlcall(ctx, jlinvoke_func, boxed(ctx, lival), argv, nargs, julia_call2); + result = mark_julia_type(ctx, r, true, age_ok ? (jl_value_t*)jl_any_type : rt); } if (result.typ == jl_bottom_type) { #ifndef JL_NDEBUG @@ -5691,7 +5703,7 @@ static jl_cgval_t emit_specsig_oc_call(jl_codectx_t &ctx, jl_value_t *oc_type, j Value *specptr = emit_unbox(ctx, ctx.types().T_size, closure_specptr, (jl_value_t*)jl_long_type); JL_GC_PUSH1(&sigtype); jl_cgval_t r = emit_call_specfun_other(ctx, true, sigtype, oc_rett, specptr, "", NULL, argv, nargs, - &cc, &return_roots, oc_rett); + &cc, &return_roots, oc_rett, nullptr); JL_GC_POP(); return r; } @@ -7044,11 +7056,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ std::tie(F, specF) = get_oc_function(ctx, (jl_method_t*)source.constant, (jl_tupletype_t*)env_t, argt_typ, ub.constant); if (F) { jl_cgval_t jlcall_ptr = mark_julia_type(ctx, F, false, jl_voidpointer_type); - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); - bool not_toplevel = (ctx.linfo && jl_is_method(ctx.linfo->def.method)); - Instruction *I = not_toplevel ? cast(ctx.world_age_at_entry) : - ctx.builder.CreateAlignedLoad(ctx.types().T_size, get_tls_world_age_field(ctx), ctx.types().alignof_ptr); - jl_cgval_t world_age = mark_julia_type(ctx, ai.decorateInst(I), false, jl_long_type); + jl_cgval_t world_age = mark_julia_type(ctx, get_tls_world_age(ctx), false, jl_long_type); jl_cgval_t fptr; if (specF) fptr = mark_julia_type(ctx, specF, false, jl_voidpointer_type); @@ -7207,6 +7215,25 @@ static Value *get_tls_world_age_field(jl_codectx_t &ctx) return emit_ptrgep(ctx, ct, offsetof(jl_task_t, world_age), "world_age"); } +// Get the value of the world age of the current task +static Value *get_tls_world_age(jl_codectx_t &ctx) +{ + if (ctx.world_age_at_entry) + return ctx.world_age_at_entry; + IRBuilderBase::InsertPointGuard IP(ctx.builder); + bool toplevel = !jl_is_method(ctx.linfo->def.method); + if (!toplevel) { + ctx.builder.SetInsertPoint(ctx.topalloca->getParent(), ++ctx.topalloca->getIterator()); + ctx.builder.SetCurrentDebugLocation(ctx.topalloca->getStableDebugLoc()); + } + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); + auto *world = ctx.builder.CreateAlignedLoad(ctx.types().T_size, get_tls_world_age_field(ctx), ctx.types().alignof_ptr); + ai.decorateInst(world); + if (!toplevel) + ctx.world_age_at_entry = world; + return world; +} + static Value *get_scope_field(jl_codectx_t &ctx) { Value *ct = get_current_task(ctx); @@ -7229,8 +7256,7 @@ Function *emit_tojlinvoke(jl_code_instance_t *codeinst, StringRef theFptrName, M name, M); jl_init_function(f, params.TargetTriple); if (trim_may_error(params.params->trim)) { - // TODO: Debuginfo! - push_frames(ctx, ctx.linfo, codeinst->def, 1); + push_frames(ctx, ctx.linfo, codeinst->def); } jl_name_jlfunc_args(params, f); //f->setAlwaysInline(); @@ -7272,11 +7298,10 @@ void emit_specsig_to_fptr1( jl_value_t *calltype, jl_value_t *rettype, bool is_for_opaque_closure, size_t nargs, jl_codegen_params_t ¶ms, - Function *target, - size_t min_world, size_t max_world) + Function *target) { ++EmittedCFuncInvalidates; - jl_codectx_t ctx(gf_thunk->getParent()->getContext(), params, min_world, max_world); + jl_codectx_t ctx(gf_thunk->getParent()->getContext(), params, 0, 0); ctx.f = gf_thunk; BasicBlock *b0 = BasicBlock::Create(ctx.builder.getContext(), "top", gf_thunk); @@ -7393,12 +7418,11 @@ void emit_specsig_to_fptr1( } } -static Function* gen_cfun_wrapper( +static Function *gen_cfun_wrapper( Module *into, jl_codegen_params_t ¶ms, const function_sig_t &sig, jl_value_t *ff, const char *aliasname, jl_value_t *declrt, jl_method_instance_t *lam, - jl_unionall_t *unionall_env, jl_svec_t *sparam_vals, jl_array_t **closure_types, - size_t min_world, size_t max_world) + jl_unionall_t *unionall_env, jl_svec_t *sparam_vals, jl_array_t **closure_types) { ++GeneratedCFuncWrappers; // Generate a c-callable wrapper @@ -7406,36 +7430,17 @@ static Function* gen_cfun_wrapper( size_t nargs = sig.nccallargs; const char *name = "cfunction"; size_t world = jl_atomic_load_acquire(&jl_world_counter); - jl_code_instance_t *codeinst = NULL; bool nest = (!ff || unionall_env); jl_value_t *astrt = (jl_value_t*)jl_any_type; - void *callptr = NULL; - jl_callptr_t invoke = NULL; - int calltype = 0; if (aliasname) name = aliasname; else if (lam) name = jl_symbol_name(lam->def.method->name); - if (lam && params.cache) { + + jl_code_instance_t *codeinst = NULL; + if (lam) { // TODO: this isn't ideal to be unconditionally calling type inference (and compile) from here - codeinst = jl_compile_method_internal(lam, world); - uint8_t specsigflags; - void *fptr; - jl_read_codeinst_invoke(codeinst, &specsigflags, &invoke, &fptr, 0); - assert(invoke); - if (invoke == jl_fptr_args_addr) { - callptr = fptr; - calltype = 1; - } - else if (invoke == jl_fptr_const_return_addr) { - // don't need the fptr - callptr = (void*)codeinst->rettype_const; - calltype = 2; - } - else if (specsigflags & 0b1) { - callptr = fptr; - calltype = 3; - } + codeinst = jl_type_infer(lam, world, SOURCE_MODE_NOT_REQUIRED); astrt = codeinst->rettype; if (astrt != (jl_value_t*)jl_bottom_type && jl_type_intersection(astrt, declrt) == jl_bottom_type) { @@ -7511,7 +7516,7 @@ static Function* gen_cfun_wrapper( jl_init_function(cw, params.TargetTriple); cw->setAttributes(AttributeList::get(M->getContext(), {attributes, cw->getAttributes()})); - jl_codectx_t ctx(M->getContext(), params, min_world, max_world); + jl_codectx_t ctx(M->getContext(), params, 0, 0); ctx.f = cw; ctx.name = name; ctx.funcName = name; @@ -7524,15 +7529,14 @@ static Function* gen_cfun_wrapper( auto world_age_field = get_tls_world_age_field(ctx); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); - Value *last_age = ai.decorateInst( + ctx.world_age_at_entry = ai.decorateInst( ctx.builder.CreateAlignedLoad(ctx.types().T_size, world_age_field, ctx.types().alignof_ptr)); - ctx.world_age_at_entry = last_age; Value *world_v = ctx.builder.CreateAlignedLoad(ctx.types().T_size, prepare_global_in(jl_Module, jlgetworld_global), ctx.types().alignof_ptr); cast(world_v)->setOrdering(AtomicOrdering::Acquire); - Value *age_ok = NULL; - if (calltype) { + Value *age_ok = nullptr; + if (codeinst) { LoadInst *lam_max = ctx.builder.CreateAlignedLoad( ctx.types().T_size, emit_ptrgep(ctx, literal_pointer_val(ctx, (jl_value_t*)codeinst), offsetof(jl_code_instance_t, max_world)), @@ -7696,79 +7700,9 @@ static Function* gen_cfun_wrapper( // Create the call bool jlfunc_sret; jl_cgval_t retval; - if (calltype == 2) { - nargs = 0; // arguments not needed -- TODO: not really true, should emit an age_ok test and jlcall - (void)nargs; // silence unused variable warning - jlfunc_sret = false; - retval = mark_julia_const(ctx, (jl_value_t*)callptr); - } - else if (calltype == 0 || calltype == 1) { - // emit a jlcall - jlfunc_sret = false; - Function *theFptr = NULL; - if (calltype == 1) { - StringRef fname = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)callptr, invoke, codeinst); - theFptr = cast_or_null(jl_Module->getNamedValue(fname)); - if (!theFptr) { - theFptr = Function::Create(ctx.types().T_jlfunc, GlobalVariable::ExternalLinkage, - fname, jl_Module); - jl_init_function(theFptr, ctx.emission_context.TargetTriple); - jl_name_jlfunc_args(ctx.emission_context, theFptr); - addRetAttr(theFptr, Attribute::NonNull); - } - else { - assert(theFptr->getFunctionType() == ctx.types().T_jlfunc); - } - } - BasicBlock *b_generic, *b_jlcall, *b_after; - Value *ret_jlcall; - if (age_ok) { - assert(theFptr); - b_generic = BasicBlock::Create(ctx.builder.getContext(), "generic", cw); - b_jlcall = BasicBlock::Create(ctx.builder.getContext(), "apply", cw); - b_after = BasicBlock::Create(ctx.builder.getContext(), "after", cw); - ctx.builder.CreateCondBr(age_ok, b_jlcall, b_generic); - ctx.builder.SetInsertPoint(b_jlcall); - // for jlcall, we need to pass the function object even if it is a ghost. - Value *theF = boxed(ctx, inputargs[0]); - assert(theF); - ret_jlcall = emit_jlcall(ctx, theFptr, theF, ArrayRef(inputargs).drop_front(), nargs, julia_call); - ctx.builder.CreateBr(b_after); - ctx.builder.SetInsertPoint(b_generic); - } - Value *ret = emit_jlcall(ctx, jlapplygeneric_func, NULL, inputargs, nargs + 1, julia_call); - if (age_ok) { - ctx.builder.CreateBr(b_after); - ctx.builder.SetInsertPoint(b_after); - PHINode *retphi = ctx.builder.CreatePHI(ctx.types().T_prjlvalue, 2); - retphi->addIncoming(ret_jlcall, b_jlcall); - retphi->addIncoming(ret, b_generic); - ret = retphi; - } - retval = mark_julia_type(ctx, ret, true, astrt); - } - else { - bool is_opaque_closure = jl_is_method(lam->def.value) && lam->def.method->is_for_opaque_closure; - assert(calltype == 3); - // emit a specsig call - StringRef protoname = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)callptr, invoke, codeinst); - bool gcstack_arg = JL_FEAT_TEST(ctx, gcstack_arg); - jl_returninfo_t returninfo = get_specsig_function(ctx, M, NULL, protoname, lam->specTypes, astrt, is_opaque_closure, gcstack_arg); - if (age_ok) { - funcName += "_gfthunk"; - Function *gf_thunk = Function::Create(returninfo.decl.getFunctionType(), - GlobalVariable::InternalLinkage, funcName, M); - jl_init_function(gf_thunk, ctx.emission_context.TargetTriple); - gf_thunk->setAttributes(AttributeList::get(M->getContext(), {returninfo.attrs, gf_thunk->getAttributes()})); - // build a specsig -> jl_apply_generic converter thunk - // this builds a method that calls jl_apply_generic (as a closure over a singleton function pointer), - // but which has the signature of a specsig - emit_specsig_to_fptr1(gf_thunk, returninfo.cc, returninfo.return_roots, lam->specTypes, codeinst->rettype, is_opaque_closure, nargs + 1, ctx.emission_context, - prepare_call_in(gf_thunk->getParent(), jlapplygeneric_func), min_world, max_world); - returninfo.decl = FunctionCallee(returninfo.decl.getFunctionType(), ctx.builder.CreateSelect(age_ok, returninfo.decl.getCallee(), gf_thunk)); - } - retval = emit_call_specfun_other(ctx, is_opaque_closure, lam->specTypes, codeinst->rettype, returninfo, nullptr, inputargs, nargs + 1); - jlfunc_sret = (returninfo.cc == jl_returninfo_t::SRet); + if (codeinst) { + retval = emit_invoke(ctx, mark_julia_const(ctx, (jl_value_t*)codeinst), inputargs, nargs + 1, astrt, age_ok); + jlfunc_sret = retval.V && isa(retval.V) && !retval.TIndex && retval.inline_roots.empty(); if (jlfunc_sret && sig.sret) { // fuse the two sret together assert(retval.ispointer()); @@ -7778,6 +7712,12 @@ static Function* gen_cfun_wrapper( result->eraseFromParent(); } } + else { + // emit a dispatch + jlfunc_sret = false; + Value *ret = emit_jlcall(ctx, jlapplygeneric_func, NULL, inputargs, nargs + 1, julia_call); + retval = mark_julia_type(ctx, ret, true, astrt); + } // inline a call to typeassert here, if required emit_typecheck(ctx, retval, declrt, "cfunction"); @@ -7808,7 +7748,7 @@ static Function* gen_cfun_wrapper( r = NULL; } - ctx.builder.CreateStore(last_age, world_age_field); + ctx.builder.CreateStore(ctx.world_age_at_entry, world_age_field); ctx.builder.CreateRet(r); ctx.builder.SetCurrentDebugLocation(noDbg); @@ -7950,15 +7890,13 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con } } size_t world = jl_atomic_load_acquire(&jl_world_counter); - size_t min_valid = 0; - size_t max_valid = ~(size_t)0; // try to look up this function for direct invoking - jl_method_instance_t *lam = sigt ? jl_get_specialization1((jl_tupletype_t*)sigt, world, &min_valid, &max_valid, 0) : NULL; + jl_method_instance_t *lam = sigt ? jl_get_specialization1((jl_tupletype_t*)sigt, world, 0) : NULL; Value *F = gen_cfun_wrapper( jl_Module, ctx.emission_context, sig, fexpr_rt.constant, NULL, declrt, lam, - unionall_env, sparam_vals, &closure_types, min_valid, max_valid); + unionall_env, sparam_vals, &closure_types); bool outboxed; if (nest) { // F is actually an init_trampoline function that returns the real address @@ -8018,7 +7956,7 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con // do codegen to create a C-callable alias/wrapper, or if sysimg_handle is set, // restore one from a loaded system image. -const char *jl_generate_ccallable(LLVMOrcThreadSafeModuleRef llvmmod, void *sysimg_handle, jl_value_t *declrt, jl_value_t *sigt, jl_codegen_params_t ¶ms) +const char *jl_generate_ccallable(Module *llvmmod, void *sysimg_handle, jl_value_t *declrt, jl_value_t *sigt, jl_codegen_params_t ¶ms) { ++GeneratedCCallables; jl_datatype_t *ft = (jl_datatype_t*)jl_tparam0(sigt); @@ -8047,8 +7985,6 @@ const char *jl_generate_ccallable(LLVMOrcThreadSafeModuleRef llvmmod, void *sysi argtypes, NULL, false, CallingConv::C, false, ¶ms); if (sig.err_msg.empty()) { size_t world = jl_atomic_load_acquire(&jl_world_counter); - size_t min_valid = 0; - size_t max_valid = ~(size_t)0; if (sysimg_handle) { // restore a ccallable from the system image void *addr; @@ -8061,9 +7997,9 @@ const char *jl_generate_ccallable(LLVMOrcThreadSafeModuleRef llvmmod, void *sysi } } else { - jl_method_instance_t *lam = jl_get_specialization1((jl_tupletype_t*)sigt, world, &min_valid, &max_valid, 0); + jl_method_instance_t *lam = jl_get_specialization1((jl_tupletype_t*)sigt, world, 0); //Safe b/c params holds context lock - gen_cfun_wrapper(unwrap(llvmmod)->getModuleUnlocked(), params, sig, ff, name, declrt, lam, NULL, NULL, NULL, min_valid, max_valid); + gen_cfun_wrapper(llvmmod, params, sig, ff, name, declrt, lam, NULL, NULL, NULL); } JL_GC_POP(); return name; @@ -8125,7 +8061,7 @@ static void gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlretty, j } argv[i] = mark_julia_type(ctx, theArg, true, ty); } - jl_cgval_t retval = emit_call_specfun_other(ctx, is_opaque_closure, lam->specTypes, jlretty, f, nullptr, argv, nargs); + jl_cgval_t retval = emit_call_specfun_other(ctx, is_opaque_closure, lam->specTypes, jlretty, f, argv, nargs); if (retarg != -1) { Value *theArg; if (retarg == 0) @@ -8312,7 +8248,7 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value if (f == NULL) { f = Function::Create(ftype, GlobalVariable::ExternalLinkage, name, M); jl_init_function(f, ctx.emission_context.TargetTriple); - if (ctx.emission_context.debug_level >= 2) { + if (ctx.emission_context.params->debug_info_level >= 2) { ios_t sigbuf; ios_mem(&sigbuf, 0); jl_static_show_func_sig((JL_STREAM*) &sigbuf, sig); @@ -8418,10 +8354,9 @@ static jl_llvm_functions_t ctx.source = src; std::map labels; - bool toplevel = false; ctx.module = jl_is_method(lam->def.method) ? lam->def.method->module : lam->def.module; ctx.linfo = lam; - ctx.name = TSM.getModuleUnlocked()->getModuleIdentifier().data(); + ctx.name = name_from_method_instance(lam); size_t nreq = src->nargs; int va = src->isva; ctx.nargs = nreq; @@ -8438,7 +8373,6 @@ static jl_llvm_functions_t if (vn != jl_unused_sym) ctx.vaSlot = ctx.nargs - 1; } - toplevel = !jl_is_method(lam->def.method); ctx.rettype = jlrettype; ctx.funcName = ctx.name; ctx.spvals_ptr = NULL; @@ -8475,7 +8409,7 @@ static jl_llvm_functions_t // jl_printf(JL_STDERR, "\n*** compiling %s at %s:%d\n\n", // jl_symbol_name(ctx.name), ctx.file.str().c_str(), toplineno); - bool debug_enabled = ctx.emission_context.debug_level != 0; + bool debug_enabled = ctx.emission_context.params->debug_info_level != 0; if (dbgFuncName.empty()) // Should never happen anymore? debug_enabled = false; @@ -8551,7 +8485,6 @@ static jl_llvm_functions_t // allocate Function declarations and wrapper objects //Safe because params holds ctx lock Module *M = TSM.getModuleUnlocked(); - M->addModuleFlag(Module::Warning, "julia.debug_level", ctx.emission_context.debug_level); jl_debugcache_t debugcache; debugcache.initialize(M); jl_returninfo_t returninfo = {}; @@ -8559,7 +8492,7 @@ static jl_llvm_functions_t bool has_sret = false; if (specsig) { // assumes !va and !needsparams SmallVector ArgNames(0); - if (ctx.emission_context.debug_level >= 2) { + if (!M->getContext().shouldDiscardValueNames()) { ArgNames.resize(ctx.nargs, ""); for (int i = 0; i < ctx.nargs; i++) { jl_sym_t *argname = slot_symbol(ctx, i); @@ -8626,7 +8559,7 @@ static jl_llvm_functions_t declarations.functionObject = needsparams ? "jl_fptr_sparam" : "jl_fptr_args"; } - if (ctx.emission_context.debug_level >= 2 && lam->def.method && jl_is_method(lam->def.method) && lam->specTypes != (jl_value_t*)jl_emptytuple_type) { + if (!params.getContext().shouldDiscardValueNames() && ctx.emission_context.params->debug_info_level >= 2 && lam->def.method && jl_is_method(lam->def.method) && lam->specTypes != (jl_value_t*)jl_emptytuple_type) { ios_t sigbuf; ios_mem(&sigbuf, 0); jl_static_show_func_sig((JL_STREAM*) &sigbuf, (jl_value_t*)lam->specTypes); @@ -8681,7 +8614,7 @@ static jl_llvm_functions_t if (debug_enabled) { topfile = dbuilder.createFile(ctx.file, "."); DISubroutineType *subrty; - if (ctx.emission_context.debug_level <= 1) + if (ctx.emission_context.params->debug_info_level <= 1) subrty = debugcache.jl_di_func_null_sig; else if (!specsig) subrty = debugcache.jl_di_func_sig; @@ -8702,7 +8635,7 @@ static jl_llvm_functions_t ); topdebugloc = DILocation::get(ctx.builder.getContext(), toplineno, 0, SP, NULL); f->setSubprogram(SP); - if (ctx.emission_context.debug_level >= 2) { + if (ctx.emission_context.params->debug_info_level >= 2) { const bool AlwaysPreserve = true; // Go over all arguments and local variables and initialize their debug information for (i = 0; i < nreq; i++) { @@ -8776,12 +8709,12 @@ static jl_llvm_functions_t // step 6. set up GC frame allocate_gc_frame(ctx, b0); Value *last_age = NULL; - auto world_age_field = get_tls_world_age_field(ctx); - { // scope + Value *world_age_field = NULL; + if (ctx.is_opaque_closure) { + world_age_field = get_tls_world_age_field(ctx); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); last_age = ai.decorateInst(ctx.builder.CreateAlignedLoad( ctx.types().T_size, world_age_field, ctx.types().alignof_ptr)); - ctx.world_age_at_entry = last_age; // Load world age for use in get_tls_world_age } // step 7. allocate local variables slots @@ -9005,6 +8938,7 @@ static jl_llvm_functions_t Value *worldaddr = emit_ptrgep(ctx, oc_this, offsetof(jl_opaque_closure_t, world)); jl_cgval_t closure_world = typed_load(ctx, worldaddr, NULL, (jl_value_t*)jl_long_type, nullptr, nullptr, false, AtomicOrdering::NotAtomic, false, ctx.types().alignof_ptr.value()); + assert(ctx.world_age_at_entry == nullptr); ctx.world_age_at_entry = closure_world.V; // The tls world in a OC is the world of the closure emit_unbox_store(ctx, closure_world, world_age_field, ctx.tbaa().tbaa_gcframe, ctx.types().alignof_ptr); @@ -9282,19 +9216,11 @@ static jl_llvm_functions_t Instruction &prologue_end = ctx.builder.GetInsertBlock()->back(); - // step 11a. For top-level code, load the world age - if (toplevel && !ctx.is_opaque_closure) { - LoadInst *world = ctx.builder.CreateAlignedLoad(ctx.types().T_size, - prepare_global_in(jl_Module, jlgetworld_global), ctx.types().alignof_ptr); - world->setOrdering(AtomicOrdering::Acquire); - ctx.builder.CreateAlignedStore(world, world_age_field, ctx.types().alignof_ptr); - } - - // step 11b. Emit the entry safepoint + // step 11a. Emit the entry safepoint if (JL_FEAT_TEST(ctx, safepoint_on_entry)) emit_gc_safepoint(ctx.builder, ctx.types().T_size, get_current_ptls(ctx), ctx.tbaa().tbaa_const); - // step 11c. Do codegen in control flow order + // step 11b. Do codegen in control flow order SmallVector workstack; std::map BB; std::map come_from_bb; @@ -9966,8 +9892,7 @@ static jl_llvm_functions_t Instruction *root = cast_or_null(ctx.slots[ctx.vaSlot].boxroot); if (root) { bool have_real_use = false; - for (Use &U : root->uses()) { - User *RU = U.getUser(); + for (User *RU : root->users()) { if (StoreInst *SRU = dyn_cast(RU)) { assert(isa(SRU->getValueOperand()) || SRU->getValueOperand() == restTuple); (void)SRU; @@ -9986,21 +9911,21 @@ static jl_llvm_functions_t } } if (!have_real_use) { - Instruction *use = NULL; - for (Use &U : root->uses()) { - if (use) // erase after the iterator moves on - use->eraseFromParent(); - User *RU = U.getUser(); - use = cast(RU); + for (User *RU : make_early_inc_range(root->users())) { + // This is safe because it checked above that each User is known and has at most one Use of root + cast(RU)->eraseFromParent(); } - if (use) - use->eraseFromParent(); root->eraseFromParent(); restTuple->eraseFromParent(); } } } + if (ctx.topalloca->use_empty()) { + ctx.topalloca->eraseFromParent(); + ctx.topalloca = nullptr; + } + // link the dependent llvmcall modules, but switch their function's linkage to internal // so that they don't conflict when they show up in the execution engine. Linker L(*jl_Module); @@ -10092,8 +10017,7 @@ static jl_llvm_functions_t jl_emit_oc_wrapper(orc::ThreadSafeModule &m, jl_codeg size_t nrealargs = jl_nparams(mi->specTypes); emit_specsig_to_fptr1(gf_thunk, returninfo.cc, returninfo.return_roots, mi->specTypes, rettype, true, nrealargs, ctx.emission_context, - prepare_call_in(gf_thunk->getParent(), jlopaque_closure_call_func), // TODO: this could call emit_oc_call directly - ctx.min_world, ctx.max_world); + prepare_call_in(gf_thunk->getParent(), jlopaque_closure_call_func)); // TODO: this could call emit_oc_call directly declarations.specFunctionObject = funcName; } return declarations; @@ -10156,7 +10080,7 @@ jl_llvm_functions_t jl_emit_codeinst( if (// keep code when keeping everything !(JL_DELETE_NON_INLINEABLE) || // aggressively keep code when debugging level >= 2 - // note that this uses the global jl_options.debug_level, not the local emission_ctx.debug_level + // note that this uses the global jl_options.debug_level, not the local emission_ctx.debug_info_level jl_options.debug_level > 1) { // update the stored code if (inferred != (jl_value_t*)src) { @@ -10176,10 +10100,10 @@ jl_llvm_functions_t jl_emit_codeinst( else if (jl_is_method(def) && // don't delete toplevel code def->source != NULL && // don't delete code from optimized opaque closures that can't be reconstructed inferred != jl_nothing && // and there is something to delete (test this before calling jl_ir_inlining_cost) - !effects_foldable(jl_atomic_load_relaxed(&codeinst->ipo_purity_bits)) && // don't delete code we may want for irinterp - ((jl_ir_inlining_cost(inferred) == UINT16_MAX) || // don't delete inlineable code - jl_atomic_load_relaxed(&codeinst->invoke) == jl_fptr_const_return_addr) && // unless it is constant - !(params.imaging_mode || jl_options.incremental)) { // don't delete code when generating a precompile file + ((!effects_foldable(jl_atomic_load_relaxed(&codeinst->ipo_purity_bits)) && // don't delete code we may want for irinterp + (jl_ir_inlining_cost(inferred) == UINT16_MAX) && // don't delete inlineable code + !jl_generating_output()) || // don't delete code when generating a precompile file, trading memory in the short term for avoiding likely duplicating inference work for aotcompile + jl_atomic_load_relaxed(&codeinst->invoke) == jl_fptr_const_return_addr)) { // unless it is constant (although this shouldn't have had code in the first place) // Never end up in a situation where the codeinst has no invoke, but also no source, so we never fall // through the cracks of SOURCE_MODE_ABI. jl_callptr_t expected = NULL; @@ -10272,6 +10196,7 @@ static void init_jit_functions(void) add_named_global(jltypeassert_func, &jl_typeassert); add_named_global(jlapplytype_func, &jl_instantiate_type_in_env); add_named_global(jl_object_id__func, &jl_object_id_); + add_named_global(jl_alloc_genericmemory_unchecked_func, &jl_alloc_genericmemory_unchecked); add_named_global(jl_alloc_obj_func, (void*)NULL); add_named_global(jl_newbits_func, (void*)jl_new_bits); add_named_global(jl_typeof_func, (void*)NULL); @@ -10337,10 +10262,6 @@ char jl_using_perf_jitevents = 0; int jl_is_timing_passes = 0; -#if JL_LLVM_VERSION < 170000 -int jl_opaque_ptrs_set = 0; -#endif - extern "C" void jl_init_llvm(void) { jl_page_size = jl_getpagesize(); @@ -10359,12 +10280,8 @@ extern "C" void jl_init_llvm(void) initializeAnalysis(Registry); initializeTransformUtils(Registry); initializeInstCombine(Registry); -#if JL_LLVM_VERSION >= 160000 - // TODO -#else - initializeAggressiveInstCombine(Registry); - initializeInstrumentation(Registry); -#endif + // TODO: initializeAggressiveInstCombine(Registry); + // TODO: initializeInstrumentation(Registry); initializeTarget(Registry); #ifdef USE_POLLY polly::initializePollyPasses(Registry); @@ -10390,17 +10307,6 @@ extern "C" void jl_init_llvm(void) if (clopt && clopt->getNumOccurrences() == 0) cl::ProvidePositionalOption(clopt, "4", 1); - #if JL_LLVM_VERSION < 170000 - // we want the opaque-pointers to be opt-in, per LLVMContext, for this release - // so change the default value back to pre-14.x, without changing the NumOccurrences flag for it - clopt = llvmopts.lookup("opaque-pointers"); - if (clopt && clopt->getNumOccurrences() == 0) { - clopt->addOccurrence(1, clopt->ArgStr, "false", true); - } else { - jl_opaque_ptrs_set = 1; - } - #endif - clopt = llvmopts.lookup("time-passes"); if (clopt && clopt->getNumOccurrences() > 0) jl_is_timing_passes = 1; @@ -10425,8 +10331,16 @@ extern "C" void jl_init_llvm(void) const char *jit_profiling = getenv("ENABLE_JITPROFILING"); #if defined(JL_USE_INTEL_JITEVENTS) - if (jit_profiling && atoi(jit_profiling)) { - jl_using_intel_jitevents = 1; + if (jit_profiling) { + if (atoi(jit_profiling)) { + jl_using_intel_jitevents = 1; + } + } else { +#ifdef USE_ITTAPI + __itt_collection_state state = __itt_get_collection_state(); + jl_using_intel_jitevents = state == __itt_collection_init_successful || + state == __itt_collection_collector_exists; +#endif } #endif diff --git a/src/disasm.cpp b/src/disasm.cpp index b944e48430c29..6a7985bd7ec1b 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -58,11 +58,7 @@ #include "llvm-version.h" // for outputting disassembly -#if JL_LLVM_VERSION >= 170000 #include -#else -#include -#endif #include #include #include @@ -505,7 +501,7 @@ jl_value_t *jl_dump_function_ir_impl(jl_llvmf_dump_t *dump, char strip_ir_metada auto TSM = std::unique_ptr(unwrap(dump->TSM)); //If TSM is not passed in, then the context MUST be locked externally. //RAII will release the lock - Optional lock; + std::optional lock; if (TSM) { lock.emplace(TSM->getContext().getLock()); } @@ -1107,11 +1103,7 @@ static void jl_dump_asm_internal( const MCOperand &OpI = Inst.getOperand(Op); if (OpI.isImm()) { int64_t imm = OpI.getImm(); - #if JL_LLVM_VERSION >= 170000 if (opinfo.operands()[Op].OperandType == MCOI::OPERAND_PCREL) - #else - if (opinfo.OpInfo[Op].OperandType == MCOI::OPERAND_PCREL) - #endif imm += Fptr + Index; const char *name = DisInfo.lookupSymbolName(imm); if (name) diff --git a/src/gc-common.c b/src/gc-common.c index c751b54f059f5..3d578b81578b1 100644 --- a/src/gc-common.c +++ b/src/gc-common.c @@ -557,17 +557,8 @@ size_t jl_genericmemory_nbytes(jl_genericmemory_t *m) JL_NOTSAFEPOINT // tracking Memorys with malloc'd storage void jl_gc_track_malloced_genericmemory(jl_ptls_t ptls, jl_genericmemory_t *m, int isaligned){ // This is **NOT** a GC safe point. - mallocmemory_t *ma; - if (ptls->gc_tls_common.heap.mafreelist == NULL) { - ma = (mallocmemory_t*)malloc_s(sizeof(mallocmemory_t)); - } - else { - ma = ptls->gc_tls_common.heap.mafreelist; - ptls->gc_tls_common.heap.mafreelist = ma->next; - } - ma->a = (jl_genericmemory_t*)((uintptr_t)m | !!isaligned); - ma->next = ptls->gc_tls_common.heap.mallocarrays; - ptls->gc_tls_common.heap.mallocarrays = ma; + void *a = (void*)((uintptr_t)m | !!isaligned); + small_arraylist_push(&ptls->gc_tls_common.heap.mallocarrays, a); } // =========================================================================== // diff --git a/src/gc-common.h b/src/gc-common.h index 3007151009f7d..1745df17950f9 100644 --- a/src/gc-common.h +++ b/src/gc-common.h @@ -61,12 +61,6 @@ extern jl_gc_callback_list_t *gc_cblist_notify_gc_pressure; // malloc wrappers, aligned allocation // =========================================================================== // -// data structure for tracking malloc'd genericmemory. -typedef struct _mallocmemory_t { - jl_genericmemory_t *a; // lowest bit is tagged if this is aligned memory - struct _mallocmemory_t *next; -} mallocmemory_t; - #if defined(_OS_WINDOWS_) STATIC_INLINE void *jl_malloc_aligned(size_t sz, size_t align) { diff --git a/src/gc-debug.c b/src/gc-debug.c index 7c479484cde45..6e51064035b7b 100644 --- a/src/gc-debug.c +++ b/src/gc-debug.c @@ -1025,12 +1025,11 @@ void gc_stats_big_obj(void) v = v->next; } - mallocmemory_t *ma = ptls2->gc_tls.heap.mallocarrays; - while (ma != NULL) { - uint8_t bits =jl_astaggedvalue(ma->a)->bits.gc; + void **lst = ptls2->gc_tls.heap.mallocarrays.items; + for (size_t i = 0, l = ptls2->gc_tls.heap.mallocarrays.len; i < l; i++) { + jl_genericmemory_t *m = (jl_genericmemory_t*)((uintptr_t)lst[i] & ~(uintptr_t)1); + uint8_t bits = jl_astaggedvalue(m)->bits.gc; if (gc_marked(bits)) { - jl_genericmemory_t *m = (jl_genericmemory_t*)ma->a; - m = (jl_genericmemory_t*)((uintptr_t)m & ~(uintptr_t)1); size_t sz = jl_genericmemory_nbytes(m); if (gc_old(bits)) { assert(bits == GC_OLD_MARKED); @@ -1042,7 +1041,6 @@ void gc_stats_big_obj(void) stat.nbytes_used += sz; } } - ma = ma->next; } } jl_safe_printf("%lld kB (%lld%% old) in %lld large objects (%lld%% old)\n", diff --git a/src/gc-heap-snapshot.cpp b/src/gc-heap-snapshot.cpp index 72eb17115f4c7..f3793939610b5 100644 --- a/src/gc-heap-snapshot.cpp +++ b/src/gc-heap-snapshot.cpp @@ -380,7 +380,7 @@ size_t record_node_to_gc_snapshot(jl_value_t *a) JL_NOTSAFEPOINT ios_mem(&str_, 0); JL_STREAM* str = (JL_STREAM*)&str_; jl_static_show(str, (jl_value_t*)type); - + node_type = StringRef((const char*)str_.buf, str_.size); name = StringRef((const char*)str_.buf, str_.size); } diff --git a/src/gc-stock.c b/src/gc-stock.c index 61a013f347975..c1bc1d64ae199 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -388,24 +388,18 @@ static void sweep_weak_refs(void) jl_ptls_t ptls2 = gc_all_tls_states[i]; if (ptls2 != NULL) { size_t n = 0; - size_t ndel = 0; + size_t i = 0; size_t l = ptls2->gc_tls_common.heap.weak_refs.len; void **lst = ptls2->gc_tls_common.heap.weak_refs.items; - if (l == 0) - continue; - while (1) { - jl_weakref_t *wr = (jl_weakref_t*)lst[n]; - if (gc_marked(jl_astaggedvalue(wr)->bits.gc)) + // filter with preserving order + for (i = 0; i < l; i++) { + jl_weakref_t *wr = (jl_weakref_t*)lst[i]; + if (gc_marked(jl_astaggedvalue(wr)->bits.gc)) { + lst[n] = wr; n++; - else - ndel++; - if (n >= l - ndel) - break; - void *tmp = lst[n]; - lst[n] = lst[n + ndel]; - lst[n + ndel] = tmp; + } } - ptls2->gc_tls_common.heap.weak_refs.len -= ndel; + ptls2->gc_tls_common.heap.weak_refs.len = n; } } } @@ -629,10 +623,9 @@ void jl_gc_reset_alloc_count(void) JL_NOTSAFEPOINT reset_thread_gc_counts(); } -static void jl_gc_free_memory(jl_value_t *v, int isaligned) JL_NOTSAFEPOINT +static void jl_gc_free_memory(jl_genericmemory_t *m, int isaligned) JL_NOTSAFEPOINT { - assert(jl_is_genericmemory(v)); - jl_genericmemory_t *m = (jl_genericmemory_t*)v; + assert(jl_is_genericmemory(m)); assert(jl_genericmemory_how(m) == 1 || jl_genericmemory_how(m) == 2); char *d = (char*)m->ptr; size_t freed_bytes = memory_block_usable_size(d, isaligned); @@ -654,25 +647,23 @@ static void sweep_malloced_memory(void) JL_NOTSAFEPOINT for (int t_i = 0; t_i < gc_n_threads; t_i++) { jl_ptls_t ptls2 = gc_all_tls_states[t_i]; if (ptls2 != NULL) { - mallocmemory_t *ma = ptls2->gc_tls_common.heap.mallocarrays; - mallocmemory_t **pma = &ptls2->gc_tls_common.heap.mallocarrays; - while (ma != NULL) { - mallocmemory_t *nxt = ma->next; - jl_value_t *a = (jl_value_t*)((uintptr_t)ma->a & ~1); - int bits = jl_astaggedvalue(a)->bits.gc; - if (gc_marked(bits)) { - pma = &ma->next; + size_t n = 0; + size_t l = ptls2->gc_tls_common.heap.mallocarrays.len; + void **lst = ptls2->gc_tls_common.heap.mallocarrays.items; + // filter without preserving order + while (n < l) { + jl_genericmemory_t *m = (jl_genericmemory_t*)((uintptr_t)lst[n] & ~1); + if (gc_marked(jl_astaggedvalue(m)->bits.gc)) { + n++; } else { - *pma = nxt; - int isaligned = (uintptr_t)ma->a & 1; - jl_gc_free_memory(a, isaligned); - ma->next = ptls2->gc_tls_common.heap.mafreelist; - ptls2->gc_tls_common.heap.mafreelist = ma; + int isaligned = (uintptr_t)lst[n] & 1; + jl_gc_free_memory(m, isaligned); + l--; + lst[n] = lst[l]; } - gc_time_count_mallocd_memory(bits); - ma = nxt; } + ptls2->gc_tls_common.heap.mallocarrays.len = l; } } gc_time_mallocd_memory_end(); @@ -3439,8 +3430,7 @@ void jl_init_thread_heap(jl_ptls_t ptls) small_arraylist_new(&common_heap->live_tasks, 0); for (int i = 0; i < JL_N_STACK_POOLS; i++) small_arraylist_new(&common_heap->free_stacks[i], 0); - common_heap->mallocarrays = NULL; - common_heap->mafreelist = NULL; + small_arraylist_new(&common_heap->mallocarrays, 0); heap->young_generation_of_bigvals = (bigval_t*)calloc_s(sizeof(bigval_t)); // sentinel assert(gc_bigval_sentinel_tag != 0); // make sure the sentinel is initialized heap->young_generation_of_bigvals->header = gc_bigval_sentinel_tag; diff --git a/src/gc-tls-common.h b/src/gc-tls-common.h index ba36f5c1c238e..473668d648294 100644 --- a/src/gc-tls-common.h +++ b/src/gc-tls-common.h @@ -21,9 +21,8 @@ typedef struct { // that are holding onto a stack from the pool small_arraylist_t live_tasks; - // variables for tracking malloc'd arrays - struct _mallocmemory_t *mallocarrays; - struct _mallocmemory_t *mafreelist; + // variable for tracking malloc'd arrays + small_arraylist_t mallocarrays; #define JL_N_STACK_POOLS 16 small_arraylist_t free_stacks[JL_N_STACK_POOLS]; diff --git a/src/genericmemory.c b/src/genericmemory.c index 2b02f021ccdd0..e435ec3b63c9f 100644 --- a/src/genericmemory.c +++ b/src/genericmemory.c @@ -24,49 +24,51 @@ JL_DLLEXPORT char *jl_genericmemory_typetagdata(jl_genericmemory_t *m) JL_NOTSAF return (char*)m->ptr + m->length * layout->size; } -#if defined(_P64) && defined(UINT128MAX) -typedef __uint128_t wideint_t; -#else -typedef uint64_t wideint_t; -#endif - #define MAXINTVAL (((size_t)-1)>>1) -jl_genericmemory_t *_new_genericmemory_(jl_value_t *mtype, size_t nel, int8_t isunion, int8_t zeroinit, size_t elsz) +// ONLY USE FROM CODEGEN. It only partially initializes the mem +JL_DLLEXPORT jl_genericmemory_t *jl_alloc_genericmemory_unchecked(jl_ptls_t ptls, size_t nbytes, jl_datatype_t *mtype) { - jl_task_t *ct = jl_current_task; - char *data; - jl_genericmemory_t *m; - if (nel == 0) // zero-sized allocation optimization - return (jl_genericmemory_t*)((jl_datatype_t*)mtype)->instance; - wideint_t prod = (wideint_t)nel * elsz; - if (isunion) { - // an extra byte for each isbits union memory element, stored at m->ptr + m->length - prod += nel; - } - if (nel >= MAXINTVAL || prod >= (wideint_t) MAXINTVAL) - jl_exceptionf(jl_argumenterror_type, "invalid GenericMemory size: the number of elements is either negative or too large for system address width"); - size_t tot = (size_t)prod + LLT_ALIGN(sizeof(jl_genericmemory_t),JL_SMALL_BYTE_ALIGNMENT); + size_t tot = nbytes + LLT_ALIGN(sizeof(jl_genericmemory_t),JL_SMALL_BYTE_ALIGNMENT); int pooled = tot <= GC_MAX_SZCLASS; + char *data; + jl_genericmemory_t *m; if (!pooled) { - data = (char*)jl_gc_managed_malloc(prod); + data = (char*)jl_gc_managed_malloc(nbytes); tot = sizeof(jl_genericmemory_t) + sizeof(void*); } - m = (jl_genericmemory_t*)jl_gc_alloc(ct->ptls, tot, mtype); + m = (jl_genericmemory_t*)jl_gc_alloc(ptls, tot, mtype); if (pooled) { data = (char*)m + JL_SMALL_BYTE_ALIGNMENT; } else { int isaligned = 1; // jl_gc_managed_malloc is always aligned - jl_gc_track_malloced_genericmemory(ct->ptls, m, isaligned); + jl_gc_track_malloced_genericmemory(ptls, m, isaligned); jl_genericmemory_data_owner_field(m) = (jl_value_t*)m; } - m->length = nel; + // length set by codegen m->ptr = data; + return m; +} +jl_genericmemory_t *_new_genericmemory_(jl_value_t *mtype, size_t nel, int8_t isunion, int8_t zeroinit, size_t elsz) +{ + if (nel == 0) // zero-sized allocation optimization + return (jl_genericmemory_t*)((jl_datatype_t*)mtype)->instance; + size_t nbytes; + int overflow = __builtin_mul_overflow(nel, elsz, &nbytes); + if (isunion) { + // an extra byte for each isbits union memory element, stored at m->ptr + m->length + overflow |= __builtin_add_overflow(nel, nbytes, &nbytes); + } + if ((nel >= MAXINTVAL-1) || (nbytes >= MAXINTVAL-1) || overflow) + jl_exceptionf(jl_argumenterror_type, "invalid GenericMemory size: the number of elements is either negative or too large for system address width"); + jl_task_t *ct = jl_current_task; + jl_genericmemory_t *m = jl_alloc_genericmemory_unchecked((jl_ptls_t) ct->ptls, nbytes, (jl_datatype_t*)mtype); + m->length = nel; if (zeroinit) - memset(data, 0, (size_t)prod); + memset((char*)m->ptr, 0, nbytes); return m; } @@ -150,13 +152,14 @@ JL_DLLEXPORT jl_genericmemory_t *jl_ptr_to_genericmemory(jl_value_t *mtype, void if (((uintptr_t)data) & ((align > JL_HEAP_ALIGNMENT ? JL_HEAP_ALIGNMENT : align) - 1)) jl_exceptionf(jl_argumenterror_type, "unsafe_wrap: pointer %p is not properly aligned to %u bytes", data, align); - wideint_t prod = (wideint_t)nel * elsz; + size_t nbytes; + int overflow = __builtin_mul_overflow(nel, elsz, &nbytes); if (isunion) { // an extra byte for each isbits union memory element, stored at m->ptr + m->length - prod += nel; + overflow |= __builtin_add_overflow(nel, nbytes, &nbytes); } - if (nel >= MAXINTVAL || prod >= (wideint_t) MAXINTVAL) - jl_exceptionf(jl_argumenterror_type, "invalid GenericMemory size: too large for system address width"); + if ((nel >= MAXINTVAL) || (nbytes >= MAXINTVAL) || overflow) + jl_exceptionf(jl_argumenterror_type, "invalid GenericMemory size: the number of elements is either negative or too large for system address width"); int tsz = sizeof(jl_genericmemory_t) + sizeof(void*); m = (jl_genericmemory_t*)jl_gc_alloc(ct->ptls, tsz, mtype); m->ptr = data; diff --git a/src/gf.c b/src/gf.c index 90b874d614b0c..a5ba309784557 100644 --- a/src/gf.c +++ b/src/gf.c @@ -24,6 +24,7 @@ extern "C" { #endif +_Atomic(int) allow_new_worlds = 1; JL_DLLEXPORT _Atomic(size_t) jl_world_counter = 1; // uses atomic acquire/release jl_mutex_t world_counter_lock; JL_DLLEXPORT size_t jl_get_world_counter(void) JL_NOTSAFEPOINT @@ -1819,38 +1820,42 @@ static void invalidate_backedges(jl_method_instance_t *replaced_mi, size_t max_w // add a backedge from callee to caller JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, jl_value_t *invokesig, jl_code_instance_t *caller) { + if (!jl_atomic_load_relaxed(&allow_new_worlds)) + return; if (invokesig == jl_nothing) invokesig = NULL; // julia uses `nothing` but C uses NULL (#undef) assert(jl_is_method_instance(callee)); assert(jl_is_code_instance(caller)); assert(invokesig == NULL || jl_is_type(invokesig)); JL_LOCK(&callee->def.method->writelock); - int found = 0; - // TODO: use jl_cache_type_(invokesig) like cache_method does to save memory - if (!callee->backedges) { - // lazy-init the backedges array - callee->backedges = jl_alloc_vec_any(0); - jl_gc_wb(callee, callee->backedges); - } - else { - size_t i = 0, l = jl_array_nrows(callee->backedges); - for (i = 0; i < l; i++) { - // optimized version of while (i < l) i = get_next_edge(callee->backedges, i, &invokeTypes, &mi); - jl_value_t *mi = jl_array_ptr_ref(callee->backedges, i); - if (mi != (jl_value_t*)caller) - continue; - jl_value_t *invokeTypes = i > 0 ? jl_array_ptr_ref(callee->backedges, i - 1) : NULL; - if (invokeTypes && jl_is_method_instance(invokeTypes)) - invokeTypes = NULL; - if ((invokesig == NULL && invokeTypes == NULL) || - (invokesig && invokeTypes && jl_types_equal(invokesig, invokeTypes))) { - found = 1; - break; + if (jl_atomic_load_relaxed(&allow_new_worlds)) { + int found = 0; + // TODO: use jl_cache_type_(invokesig) like cache_method does to save memory + if (!callee->backedges) { + // lazy-init the backedges array + callee->backedges = jl_alloc_vec_any(0); + jl_gc_wb(callee, callee->backedges); + } + else { + size_t i = 0, l = jl_array_nrows(callee->backedges); + for (i = 0; i < l; i++) { + // optimized version of while (i < l) i = get_next_edge(callee->backedges, i, &invokeTypes, &mi); + jl_value_t *mi = jl_array_ptr_ref(callee->backedges, i); + if (mi != (jl_value_t*)caller) + continue; + jl_value_t *invokeTypes = i > 0 ? jl_array_ptr_ref(callee->backedges, i - 1) : NULL; + if (invokeTypes && jl_is_method_instance(invokeTypes)) + invokeTypes = NULL; + if ((invokesig == NULL && invokeTypes == NULL) || + (invokesig && invokeTypes && jl_types_equal(invokesig, invokeTypes))) { + found = 1; + break; + } } } + if (!found) + push_edge(callee->backedges, invokesig, caller); } - if (!found) - push_edge(callee->backedges, invokesig, caller); JL_UNLOCK(&callee->def.method->writelock); } @@ -1858,37 +1863,41 @@ JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, JL_DLLEXPORT void jl_method_table_add_backedge(jl_methtable_t *mt, jl_value_t *typ, jl_code_instance_t *caller) { assert(jl_is_code_instance(caller)); + if (!jl_atomic_load_relaxed(&allow_new_worlds)) + return; JL_LOCK(&mt->writelock); - if (!mt->backedges) { - // lazy-init the backedges array - mt->backedges = jl_alloc_vec_any(2); - jl_gc_wb(mt, mt->backedges); - jl_array_ptr_set(mt->backedges, 0, typ); - jl_array_ptr_set(mt->backedges, 1, caller); - } - else { - // check if the edge is already present and avoid adding a duplicate - size_t i, l = jl_array_nrows(mt->backedges); - for (i = 1; i < l; i += 2) { - if (jl_array_ptr_ref(mt->backedges, i) == (jl_value_t*)caller) { - if (jl_types_equal(jl_array_ptr_ref(mt->backedges, i - 1), typ)) { - JL_UNLOCK(&mt->writelock); - return; + if (jl_atomic_load_relaxed(&allow_new_worlds)) { + if (!mt->backedges) { + // lazy-init the backedges array + mt->backedges = jl_alloc_vec_any(2); + jl_gc_wb(mt, mt->backedges); + jl_array_ptr_set(mt->backedges, 0, typ); + jl_array_ptr_set(mt->backedges, 1, caller); + } + else { + // check if the edge is already present and avoid adding a duplicate + size_t i, l = jl_array_nrows(mt->backedges); + for (i = 1; i < l; i += 2) { + if (jl_array_ptr_ref(mt->backedges, i) == (jl_value_t*)caller) { + if (jl_types_equal(jl_array_ptr_ref(mt->backedges, i - 1), typ)) { + JL_UNLOCK(&mt->writelock); + return; + } } } - } - // reuse an already cached instance of this type, if possible - // TODO: use jl_cache_type_(tt) like cache_method does, instead of this linear scan? - for (i = 1; i < l; i += 2) { - if (jl_array_ptr_ref(mt->backedges, i) != (jl_value_t*)caller) { - if (jl_types_equal(jl_array_ptr_ref(mt->backedges, i - 1), typ)) { - typ = jl_array_ptr_ref(mt->backedges, i - 1); - break; + // reuse an already cached instance of this type, if possible + // TODO: use jl_cache_type_(tt) like cache_method does, instead of this linear scan? + for (i = 1; i < l; i += 2) { + if (jl_array_ptr_ref(mt->backedges, i) != (jl_value_t*)caller) { + if (jl_types_equal(jl_array_ptr_ref(mt->backedges, i - 1), typ)) { + typ = jl_array_ptr_ref(mt->backedges, i - 1); + break; + } } } + jl_array_ptr_1d_push(mt->backedges, typ); + jl_array_ptr_1d_push(mt->backedges, (jl_value_t*)caller); } - jl_array_ptr_1d_push(mt->backedges, typ); - jl_array_ptr_1d_push(mt->backedges, (jl_value_t*)caller); } JL_UNLOCK(&mt->writelock); } @@ -2024,10 +2033,55 @@ static void jl_method_table_invalidate(jl_methtable_t *mt, jl_method_t *replaced } } +static int erase_method_backedges(jl_typemap_entry_t *def, void *closure) +{ + jl_method_t *method = def->func.method; + JL_LOCK(&method->writelock); + jl_value_t *specializations = jl_atomic_load_relaxed(&method->specializations); + if (jl_is_svec(specializations)) { + size_t i, l = jl_svec_len(specializations); + for (i = 0; i < l; i++) { + jl_method_instance_t *mi = (jl_method_instance_t*)jl_svecref(specializations, i); + if ((jl_value_t*)mi != jl_nothing) { + mi->backedges = NULL; + } + } + } + else { + jl_method_instance_t *mi = (jl_method_instance_t*)specializations; + mi->backedges = NULL; + } + JL_UNLOCK(&method->writelock); + return 1; +} + +static int erase_all_backedges(jl_methtable_t *mt, void *env) +{ + // removes all method caches + // this might not be entirely safe (GC or MT), thus we only do it very early in bootstrapping + JL_LOCK(&mt->writelock); + mt->backedges = NULL; + JL_UNLOCK(&mt->writelock); + jl_typemap_visitor(jl_atomic_load_relaxed(&mt->defs), erase_method_backedges, env); + return 1; +} + +JL_DLLEXPORT void jl_disable_new_worlds(void) +{ + if (jl_generating_output()) + jl_error("Disabling Method changes is not possible when generating output."); + JL_LOCK(&world_counter_lock); + jl_atomic_store_relaxed(&allow_new_worlds, 0); + JL_UNLOCK(&world_counter_lock); + jl_foreach_reachable_mtable(erase_all_backedges, (void*)NULL); +} + JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *method) { jl_typemap_entry_t *methodentry = do_typemap_search(mt, method); JL_LOCK(&world_counter_lock); + if (!jl_atomic_load_relaxed(&allow_new_worlds)) + jl_error("Method changes have been disabled via a call to disable_new_worlds."); JL_LOCK(&mt->writelock); // Narrow the world age on the method to make it uncallable size_t world = jl_atomic_load_relaxed(&jl_world_counter); @@ -2341,6 +2395,8 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method jl_typemap_entry_t *newentry = jl_method_table_add(mt, method, simpletype); JL_GC_PUSH1(&newentry); JL_LOCK(&world_counter_lock); + if (!jl_atomic_load_relaxed(&allow_new_worlds)) + jl_error("Method changes have been disabled via a call to disable_new_worlds."); size_t world = jl_atomic_load_relaxed(&jl_world_counter) + 1; jl_atomic_store_relaxed(&method->primary_world, world); jl_atomic_store_relaxed(&method->deleted_world, ~(size_t)0); @@ -3028,7 +3084,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_method_match_to_mi(jl_method_match_t *matc } // compile-time method lookup -jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types JL_PROPAGATES_ROOT, size_t world, size_t *min_valid, size_t *max_valid, int mt_cache) +jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types JL_PROPAGATES_ROOT, size_t world, int mt_cache) { if (jl_has_free_typevars((jl_value_t*)types)) return NULL; // don't poison the cache due to a malformed query @@ -3040,10 +3096,6 @@ jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types JL_PROPAGATES size_t max_valid2 = ~(size_t)0; int ambig = 0; jl_value_t *matches = jl_matching_methods(types, jl_nothing, 1, 1, world, &min_valid2, &max_valid2, &ambig); - if (*min_valid < min_valid2) - *min_valid = min_valid2; - if (*max_valid > max_valid2) - *max_valid = max_valid2; if (matches == jl_nothing || jl_array_nrows(matches) != 1 || ambig) return NULL; JL_GC_PUSH1(&matches); diff --git a/src/init.c b/src/init.c index 1cd14e8556cc6..7b41e63e98455 100644 --- a/src/init.c +++ b/src/init.c @@ -849,6 +849,10 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) #if defined(_COMPILER_GCC_) && __GNUC__ >= 12 #pragma GCC diagnostic ignored "-Wdangling-pointer" #endif + if (jl_options.task_metrics == JL_OPTIONS_TASK_METRICS_ON) { + // enable before creating the root task so it gets timings too. + jl_atomic_fetch_add(&jl_task_metrics_enabled, 1); + } // warning: this changes `jl_current_task`, so be careful not to call that from this function jl_task_t *ct = jl_init_root_task(ptls, stack_lo, stack_hi); #pragma GCC diagnostic pop diff --git a/src/interpreter.c b/src/interpreter.c index 49a3afed14f0c..a465fe27f5084 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -137,8 +137,28 @@ static jl_value_t *do_invoke(jl_value_t **args, size_t nargs, interpreter_state argv[i-1] = eval_value(args[i], s); jl_value_t *c = args[0]; assert(jl_is_code_instance(c) || jl_is_method_instance(c)); - jl_method_instance_t *meth = jl_is_method_instance(c) ? (jl_method_instance_t*)c : ((jl_code_instance_t*)c)->def; - jl_value_t *result = jl_invoke(argv[0], nargs == 2 ? NULL : &argv[1], nargs - 2, meth); + jl_value_t *result = NULL; + if (jl_is_code_instance(c)) { + jl_code_instance_t *codeinst = (jl_code_instance_t*)c; + assert(jl_atomic_load_relaxed(&codeinst->min_world) <= jl_current_task->world_age && + jl_current_task->world_age <= jl_atomic_load_relaxed(&codeinst->max_world)); + jl_callptr_t invoke = jl_atomic_load_acquire(&codeinst->invoke); + if (!invoke) { + jl_compile_codeinst(codeinst); + invoke = jl_atomic_load_acquire(&codeinst->invoke); + } + if (invoke) { + result = invoke(argv[0], nargs == 2 ? NULL : &argv[1], nargs - 2, codeinst); + + } else { + if (codeinst->owner != jl_nothing) { + jl_error("Failed to invoke or compile external codeinst"); + } + result = jl_invoke(argv[0], nargs == 2 ? NULL : &argv[1], nargs - 2, codeinst->def); + } + } else { + result = jl_invoke(argv[0], nargs == 2 ? NULL : &argv[1], nargs - 2, (jl_method_instance_t*)c); + } JL_GC_POP(); return result; } @@ -569,25 +589,11 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, s->locals[n - 1] = rhs; } else { - jl_module_t *modu; - jl_sym_t *sym; - // Plain assignment is allowed to create bindings at - // toplevel and only for the current module - int alloc = toplevel; - if (jl_is_globalref(lhs)) { - modu = jl_globalref_mod(lhs); - sym = jl_globalref_name(lhs); - alloc &= modu == s->module; - } - else { - assert(jl_is_symbol(lhs)); - modu = s->module; - sym = (jl_sym_t*)lhs; - } - JL_GC_PUSH1(&rhs); - jl_binding_t *b = jl_get_binding_wr(modu, sym, alloc); - jl_checked_assignment(b, modu, sym, rhs); - JL_GC_POP(); + // This is an unmodeled error. Our frontend only generates + // legal `=` expressions, but since GlobalRef used to be legal + // here, give a loud error in case any package is modifying + // internals. + jl_error("Invalid IR: Assignment LHS not a Slot"); } } else if (head == jl_leave_sym) { @@ -887,10 +893,7 @@ jl_value_t *NOINLINE jl_interpret_toplevel_thunk(jl_module_t *m, jl_code_info_t s->mi = NULL; s->ci = NULL; JL_GC_ENABLEFRAME(s); - jl_task_t *ct = jl_current_task; - size_t last_age = ct->world_age; jl_value_t *r = eval_body(stmts, s, 0, 1); - ct->world_age = last_age; JL_GC_POP(); return r; } diff --git a/src/ircode.c b/src/ircode.c index bec8d46513eef..9e64e3fe2b574 100644 --- a/src/ircode.c +++ b/src/ircode.c @@ -10,19 +10,79 @@ #include "julia_internal.h" #include "serialize.h" -#ifndef _OS_WINDOWS_ -#include -#endif - -#include "valgrind.h" #include "julia_assert.h" #ifdef __cplusplus extern "C" { #endif +#define TAG_SYMBOL 2 +#define TAG_SSAVALUE 3 +#define TAG_DATATYPE 4 +#define TAG_SLOTNUMBER 5 +#define TAG_SVEC 6 +#define TAG_NEARBYSSAVALUE 7 +#define TAG_NULL 8 +#define TAG_EXPR 9 +#define TAG_PHINODE 10 +#define TAG_PHICNODE 11 +#define TAG_LONG_SYMBOL 12 +#define TAG_LONG_SVEC 13 +#define TAG_LONG_EXPR 14 +#define TAG_LONG_PHINODE 15 +#define TAG_LONG_PHICNODE 16 +#define TAG_METHODROOT 17 +#define TAG_EDGE 18 +#define TAG_STRING 19 +#define TAG_SHORT_INT64 20 +//#define TAG_UNUSED 21 +#define TAG_CNULL 22 +#define TAG_ARRAY1D 23 +#define TAG_SINGLETON 24 +#define TAG_MODULE 25 +#define TAG_TVAR 26 +#define TAG_METHOD_INSTANCE 27 +#define TAG_METHOD 28 +#define TAG_CODE_INSTANCE 29 +#define TAG_COMMONSYM 30 +#define TAG_NEARBYGLOBAL 31 +#define TAG_GLOBALREF 32 +#define TAG_CORE 33 +#define TAG_BASE 34 +#define TAG_BITYPENAME 35 +#define TAG_NEARBYMODULE 36 +#define TAG_INT32 37 +#define TAG_INT64 38 +#define TAG_UINT8 39 +#define TAG_VECTORTY 40 +#define TAG_PTRTY 41 +#define TAG_LONG_SSAVALUE 42 +#define TAG_LONG_METHODROOT 43 +#define TAG_LONG_EDGE 44 +#define TAG_SHORTER_INT64 45 +#define TAG_SHORT_INT32 46 +#define TAG_CALL1 47 +#define TAG_CALL2 48 +#define TAG_SHORT_BACKREF 49 +#define TAG_BACKREF 50 +#define TAG_UNIONALL 51 +#define TAG_GOTONODE 52 +#define TAG_QUOTENODE 53 +#define TAG_GENERAL 54 +#define TAG_GOTOIFNOT 55 +#define TAG_RETURNNODE 56 +#define TAG_ARGUMENT 57 +#define TAG_RELOC_METHODROOT 58 +#define TAG_BINDING 59 +#define TAG_MEMORYT 60 +#define TAG_ENTERNODE 61 + +#define LAST_TAG 61 + + typedef struct { ios_t *s; + size_t ssaid; // method we're compressing for jl_method_t *method; jl_svec_t *edges; @@ -38,29 +98,29 @@ static jl_value_t *deser_tag[256]; static htable_t common_symbol_tag; static jl_value_t *deser_symbols[256]; -void *jl_lookup_ser_tag(jl_value_t *v) +static void *jl_lookup_ser_tag(jl_value_t *v) { return ptrhash_get(&ser_tag, v); } -void *jl_lookup_common_symbol(jl_value_t *v) +static void *jl_lookup_common_symbol(jl_value_t *v) { return ptrhash_get(&common_symbol_tag, v); } -jl_value_t *jl_deser_tag(uint8_t tag) +static jl_value_t *jl_deser_tag(uint8_t tag) { return deser_tag[tag]; } -jl_value_t *jl_deser_symbol(uint8_t tag) +static jl_value_t *jl_deser_symbol(uint8_t tag) { return deser_symbols[tag]; } // --- encoding --- -static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) JL_GC_DISABLED; +static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal); #define jl_encode_value(s, v) jl_encode_value_((s), (jl_value_t*)(v), 0) static void tagged_root(rle_reference *rr, jl_ircode_state *s, int i) @@ -69,7 +129,7 @@ static void tagged_root(rle_reference *rr, jl_ircode_state *s, int i) s->relocatability = 0; } -static void literal_val_id(rle_reference *rr, jl_ircode_state *s, jl_value_t *v) JL_GC_DISABLED +static void literal_val_id(rle_reference *rr, jl_ircode_state *s, jl_value_t *v) { jl_array_t *rs = s->method->roots; int i, l = jl_array_nrows(rs); @@ -142,7 +202,7 @@ static void jl_encode_as_indexed_root(jl_ircode_state *s, jl_value_t *v) } } -static void jl_encode_memory_slice(jl_ircode_state *s, jl_genericmemory_t *mem, size_t offset, size_t len) JL_GC_DISABLED +static void jl_encode_memory_slice(jl_ircode_state *s, jl_genericmemory_t *mem, size_t offset, size_t len) { jl_datatype_t *t = (jl_datatype_t*)jl_typetagof(mem); size_t i; @@ -180,7 +240,7 @@ static void jl_encode_memory_slice(jl_ircode_state *s, jl_genericmemory_t *mem, } } -static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) JL_GC_DISABLED +static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) { size_t i; @@ -248,6 +308,10 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) jl_encode_value(s, jl_globalref_name(v)); } } + else if (jl_is_ssavalue(v) && s->ssaid - ((jl_ssavalue_t*)v)->id < 256) { + write_uint8(s->s, TAG_NEARBYSSAVALUE); + write_uint8(s->s, s->ssaid - ((jl_ssavalue_t*)v)->id); + } else if (jl_is_ssavalue(v) && ((jl_ssavalue_t*)v)->id < 256 && ((jl_ssavalue_t*)v)->id >= 0) { write_uint8(s->s, TAG_SSAVALUE); write_uint8(s->s, ((jl_ssavalue_t*)v)->id); @@ -306,8 +370,11 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) } for (i = 0; i < l; i++) { int32_t e = jl_array_data(edges, int32_t)[i]; - if (e <= 20) - jl_encode_value(s, jl_box_int32(e)); + if (e <= 0 && e <= 20) { // 1-byte encodings + jl_value_t *ebox = jl_box_int32(e); + JL_GC_PROMISE_ROOTED(ebox); + jl_encode_value(s, ebox); + } else jl_encode_int32(s, e); } @@ -333,25 +400,39 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) } else if (jl_is_gotonode(v)) { write_uint8(s->s, TAG_GOTONODE); - jl_encode_value(s, jl_get_nth_field(v, 0)); + jl_value_t *f = jl_get_nth_field(v, 0); + JL_GC_PUSH1(&f); + jl_encode_value(s, f); + JL_GC_POP(); } else if (jl_is_gotoifnot(v)) { write_uint8(s->s, TAG_GOTOIFNOT); - jl_encode_value(s, jl_get_nth_field(v, 0)); - jl_encode_value(s, jl_get_nth_field(v, 1)); + jl_value_t *f = jl_get_nth_field_noalloc(v, 0); + JL_GC_PUSH1(&f); + jl_encode_value(s, f); + f = jl_get_nth_field(v, 1); + jl_encode_value(s, f); + JL_GC_POP(); } else if (jl_is_enternode(v)) { write_uint8(s->s, TAG_ENTERNODE); - jl_encode_value(s, jl_get_nth_field(v, 0)); - jl_encode_value(s, jl_get_nth_field(v, 1)); + jl_value_t *f = jl_get_nth_field(v, 0); + JL_GC_PUSH1(&f); + jl_encode_value(s, f); + f = jl_get_nth_field_noalloc(v, 1); + jl_encode_value(s, f); + JL_GC_POP(); } else if (jl_is_argument(v)) { write_uint8(s->s, TAG_ARGUMENT); - jl_encode_value(s, jl_get_nth_field(v, 0)); + jl_value_t *f = jl_get_nth_field(v, 0); + JL_GC_PUSH1(&f); + jl_encode_value(s, f); + JL_GC_POP(); } else if (jl_is_returnnode(v)) { write_uint8(s->s, TAG_RETURNNODE); - jl_encode_value(s, jl_get_nth_field(v, 0)); + jl_encode_value(s, jl_returnnode_value(v)); } else if (jl_is_quotenode(v)) { write_uint8(s->s, TAG_QUOTENODE); @@ -394,19 +475,15 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) write_int32(s->s, jl_string_len(v)); ios_write(s->s, jl_string_data(v), jl_string_len(v)); } - else if (as_literal && jl_is_array(v)) { + else if (as_literal && jl_is_array(v) && jl_array_ndims(v)) { jl_array_t *ar = (jl_array_t*)v; - if (jl_array_ndims(ar) == 1) { - write_uint8(s->s, TAG_ARRAY1D); - } - else { - write_uint8(s->s, TAG_ARRAY); - write_uint16(s->s, jl_array_ndims(ar)); - } - for (i = 0; i < jl_array_ndims(ar); i++) - jl_encode_value(s, jl_box_long(jl_array_dim(ar, i))); + write_uint8(s->s, TAG_ARRAY1D); + size_t l = jl_array_dim0(ar); + jl_value_t *lbox = jl_box_long(l); + JL_GC_PUSH1(&lbox); + jl_encode_value(s, lbox); + JL_GC_POP(); jl_encode_value(s, jl_typeof(ar)); - size_t l = jl_array_len(ar); const jl_datatype_layout_t *layout = ((jl_datatype_t*)jl_typetagof(ar->ref.mem))->layout; size_t offset; if (layout->flags.arrayelem_isunion || layout->size == 0) @@ -419,7 +496,10 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) jl_genericmemory_t* m = (jl_genericmemory_t*)v; write_uint8(s->s, TAG_MEMORYT); jl_encode_value(s, (jl_datatype_t*)jl_typetagof(v)); - jl_encode_value(s, jl_box_long(m->length)); + jl_value_t *lbox = jl_box_long(m->length); + JL_GC_PUSH1(&lbox); + jl_encode_value(s, lbox); + JL_GC_POP(); jl_encode_memory_slice(s, m, 0, m->length); } else if (as_literal && jl_is_layout_opaque(((jl_datatype_t*)jl_typeof(v))->layout)) { @@ -428,16 +508,8 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) else if (as_literal || jl_is_uniontype(v) || jl_is_newvarnode(v) || jl_is_linenode(v) || jl_is_upsilonnode(v) || jl_is_pinode(v) || jl_is_slotnumber(v) || jl_is_ssavalue(v) || (jl_isbits(jl_typeof(v)) && jl_datatype_size(jl_typeof(v)) <= 64)) { + write_uint8(s->s, TAG_GENERAL); jl_datatype_t *t = (jl_datatype_t*)jl_typeof(v); - size_t tsz = jl_datatype_size(t); - if (tsz <= 255) { - write_uint8(s->s, TAG_SHORT_GENERAL); - write_uint8(s->s, tsz); - } - else { - write_uint8(s->s, TAG_GENERAL); - write_int32(s->s, tsz); - } jl_encode_value(s, t); char *data = (char*)jl_data_ptr(v); @@ -477,7 +549,8 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) static jl_code_info_flags_t code_info_flags(uint8_t propagate_inbounds, uint8_t has_fcall, uint8_t nospecializeinfer, uint8_t isva, - uint8_t inlining, uint8_t constprop, uint8_t nargsmatchesmethod) + uint8_t inlining, uint8_t constprop, uint8_t nargsmatchesmethod, + jl_array_t *ssaflags) { jl_code_info_flags_t flags; flags.bits.propagate_inbounds = propagate_inbounds; @@ -487,39 +560,45 @@ static jl_code_info_flags_t code_info_flags(uint8_t propagate_inbounds, uint8_t flags.bits.inlining = inlining; flags.bits.constprop = constprop; flags.bits.nargsmatchesmethod = nargsmatchesmethod; + flags.bits.has_ssaflags = 0; + const uint32_t *ssaflag_data = jl_array_data(ssaflags, uint32_t); + for (size_t i = 0, l = jl_array_dim0(ssaflags); i < l; i++) + if (ssaflag_data[i]) + flags.bits.has_ssaflags = 1; return flags; } // --- decoding --- -static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED; +static jl_value_t *jl_decode_value(jl_ircode_state *s); -static jl_value_t *jl_decode_value_svec(jl_ircode_state *s, uint8_t tag) JL_GC_DISABLED +static jl_value_t *jl_decode_value_svec(jl_ircode_state *s, uint8_t tag) { size_t i, len; if (tag == TAG_SVEC) len = read_uint8(s->s); else len = read_int32(s->s); - jl_svec_t *sv = jl_alloc_svec_uninit(len); - jl_value_t **data = jl_svec_data(sv); - for (i = 0; i < len; i++) { - data[i] = jl_decode_value(s); - } + jl_svec_t *sv = jl_alloc_svec(len); + JL_GC_PUSH1(&sv); + for (i = 0; i < len; i++) + jl_svecset(sv, i, jl_decode_value(s)); + JL_GC_POP(); return (jl_value_t*)sv; } -static jl_value_t *jl_decode_value_memory(jl_ircode_state *s, jl_value_t *mty, size_t nel) JL_GC_DISABLED +static jl_genericmemory_t *jl_decode_value_memory(jl_ircode_state *s, jl_value_t *mty, size_t nel) { jl_genericmemory_t *m = jl_alloc_genericmemory(mty, nel); + JL_GC_PUSH1(&m); const jl_datatype_layout_t *layout = ((jl_datatype_t*)mty)->layout; if (layout->flags.arrayelem_isboxed) { jl_value_t **data = (jl_value_t**)m->ptr; size_t i, numel = m->length; for (i = 0; i < numel; i++) { data[i] = jl_decode_value(s); + jl_gc_wb(m, data[i]); } - assert(jl_astaggedvalue(m)->bits.gc == GC_CLEAN); // gc is disabled } else if (layout->first_ptr >= 0) { size_t i, numel = m->length; @@ -534,49 +613,48 @@ static jl_value_t *jl_decode_value_memory(jl_ircode_state *s, jl_value_t *mty, s if ((char*)fld != start) ios_readall(s->s, start, (const char*)fld - start); *fld = jl_decode_value(s); + jl_gc_wb(m, fld); start = (char*)&fld[1]; } data += elsz; if (data != start) ios_readall(s->s, start, data - start); } - assert(jl_astaggedvalue(m)->bits.gc == GC_CLEAN); // gc is disabled } else { size_t extra = jl_genericmemory_isbitsunion(m) ? m->length : 0; size_t tot = m->length * layout->size + extra; ios_readall(s->s, (char*)m->ptr, tot); } - return (jl_value_t*)m; + JL_GC_POP(); + return m; } JL_DLLEXPORT jl_array_t *jl_alloc_array_nd(jl_value_t *atype, size_t *dims, size_t ndims); -static jl_value_t *jl_decode_value_array(jl_ircode_state *s, uint8_t tag) JL_GC_DISABLED +static jl_value_t *jl_decode_value_array1d(jl_ircode_state *s, uint8_t tag) { - int16_t i, ndims; - if (tag == TAG_ARRAY1D) - ndims = 1; - else - ndims = read_uint16(s->s); - size_t *dims = (size_t*)alloca(ndims * sizeof(size_t)); - size_t len = 1; - for (i = 0; i < ndims; i++) { - dims[i] = jl_unbox_long(jl_decode_value(s)); - len *= dims[i]; - } + int16_t ndims = 1; + size_t dim0 = jl_unbox_long(jl_decode_value(s)); + size_t len = dim0; jl_value_t *aty = jl_decode_value(s); - jl_array_t *a = jl_alloc_array_nd(aty, dims, ndims); - a->ref.mem = (jl_genericmemory_t*)jl_decode_value_memory(s, jl_field_type_concrete((jl_datatype_t*)jl_field_type_concrete((jl_datatype_t*)aty, 0), 1), len); + JL_GC_PROMISE_ROOTED(aty); // (JL_ALWAYS_LEAFTYPE) + jl_genericmemory_t *mem = jl_decode_value_memory(s, jl_field_type_concrete((jl_datatype_t*)jl_field_type_concrete((jl_datatype_t*)aty, 0), 1), len); + JL_GC_PUSH1(&mem); + int tsz = sizeof(jl_array_t) + ndims*sizeof(size_t); + jl_array_t *a = (jl_array_t*)jl_gc_alloc(s->ptls, tsz, aty); + a->ref.mem = mem; const jl_datatype_layout_t *layout = ((jl_datatype_t*)jl_typetagof(a->ref.mem))->layout; if (layout->flags.arrayelem_isunion || layout->size == 0) a->ref.ptr_or_offset = (void*)0; else a->ref.ptr_or_offset = a->ref.mem->ptr; + a->dimsize[0] = dim0; + JL_GC_POP(); return (jl_value_t*)a; } -static jl_value_t *jl_decode_value_expr(jl_ircode_state *s, uint8_t tag) JL_GC_DISABLED +static jl_value_t *jl_decode_value_expr(jl_ircode_state *s, uint8_t tag) { size_t i, len; jl_sym_t *head = NULL; @@ -597,14 +675,18 @@ static jl_value_t *jl_decode_value_expr(jl_ircode_state *s, uint8_t tag) JL_GC_D if (head == NULL) head = (jl_sym_t*)jl_decode_value(s); jl_expr_t *e = jl_exprn(head, len); + JL_GC_PUSH1(&e); jl_value_t **data = jl_array_ptr_data(e->args); + jl_value_t *owner = jl_array_owner(e->args); for (i = 0; i < len; i++) { data[i] = jl_decode_value(s); + jl_gc_wb(owner, data[i]); } + JL_GC_POP(); return (jl_value_t*)e; } -static jl_value_t *jl_decode_value_phi(jl_ircode_state *s, uint8_t tag) JL_GC_DISABLED +static jl_value_t *jl_decode_value_phi(jl_ircode_state *s, uint8_t tag) { size_t i, len_e, len_v; if (tag == TAG_PHINODE) { @@ -614,9 +696,13 @@ static jl_value_t *jl_decode_value_phi(jl_ircode_state *s, uint8_t tag) JL_GC_DI len_e = read_int32(s->s); len_v = read_int32(s->s); } - jl_array_t *e = jl_alloc_array_1d(jl_array_int32_type, len_e); - jl_array_t *v = jl_alloc_vec_any(len_v); - jl_value_t *phi = jl_new_struct(jl_phinode_type, e, v); + jl_array_t *e = NULL; + jl_array_t *v = NULL; + jl_value_t *phi = NULL; + JL_GC_PUSH3(&e, &v, &phi); + e = jl_alloc_array_1d(jl_array_int32_type, len_e); + v = jl_alloc_vec_any(len_v); + phi = jl_new_struct(jl_phinode_type, e, v); int32_t *data_e = jl_array_data(e, int32_t); for (i = 0; i < len_e; i++) { data_e[i] = jl_unbox_int32(jl_decode_value(s)); @@ -624,11 +710,13 @@ static jl_value_t *jl_decode_value_phi(jl_ircode_state *s, uint8_t tag) JL_GC_DI jl_value_t **data_v = jl_array_ptr_data(v); for (i = 0; i < len_v; i++) { data_v[i] = jl_decode_value(s); + jl_gc_wb(jl_array_owner(v), data_v[i]); } + JL_GC_POP(); return phi; } -static jl_value_t *jl_decode_value_phic(jl_ircode_state *s, uint8_t tag) JL_GC_DISABLED +static jl_value_t *jl_decode_value_phic(jl_ircode_state *s, uint8_t tag) { size_t i, len; if (tag == TAG_PHICNODE) @@ -636,41 +724,53 @@ static jl_value_t *jl_decode_value_phic(jl_ircode_state *s, uint8_t tag) JL_GC_D else len = read_int32(s->s); jl_array_t *v = jl_alloc_vec_any(len); - jl_value_t *phic = jl_new_struct(jl_phicnode_type, v); + jl_value_t *phic = (jl_value_t*)v; + JL_GC_PUSH1(&phic); + phic = jl_new_struct(jl_phicnode_type, v); jl_value_t **data = jl_array_ptr_data(v); for (i = 0; i < len; i++) { data[i] = jl_decode_value(s); + jl_gc_wb(jl_array_owner(v), data[i]); } + JL_GC_POP(); return phic; } -static jl_value_t *jl_decode_value_globalref(jl_ircode_state *s) JL_GC_DISABLED +static jl_value_t *jl_decode_value_globalref(jl_ircode_state *s) { - jl_value_t *mod = jl_decode_value(s); - jl_value_t *var = jl_decode_value(s); - return jl_module_globalref((jl_module_t*)mod, (jl_sym_t*)var); + jl_module_t *mod = (jl_module_t*)jl_decode_value(s); + JL_GC_PROMISE_ROOTED(mod); + jl_sym_t *var = (jl_sym_t*)jl_decode_value(s); + JL_GC_PROMISE_ROOTED(var); + return jl_module_globalref(mod, var); } -static jl_value_t *jl_decode_value_any(jl_ircode_state *s, uint8_t tag) JL_GC_DISABLED +static jl_value_t *jl_decode_value_any(jl_ircode_state *s) { - int32_t sz = (tag == TAG_SHORT_GENERAL ? read_uint8(s->s) : read_int32(s->s)); - jl_value_t *v = jl_gc_alloc(s->ptls, sz, NULL); - jl_set_typeof(v, (void*)(intptr_t)0xf50); jl_datatype_t *dt = (jl_datatype_t*)jl_decode_value(s); - if (dt->smalltag) + JL_GC_PROMISE_ROOTED(dt); // (JL_ALWAYS_LEAFTYPE) + // jl_new_struct_uninit + size_t sz = jl_datatype_size(dt); + jl_value_t *v = jl_gc_alloc(s->ptls, sz, dt); + if (dt->smalltag) // TODO: do we need this? jl_set_typetagof(v, dt->smalltag, 0); - else - jl_set_typeof(v, dt); char *data = (char*)jl_data_ptr(v); size_t i, np = dt->layout->npointers; char *start = data; - for (i = 0; i < np; i++) { - uint32_t ptr = jl_ptr_offset(dt, i); - jl_value_t **fld = &((jl_value_t**)data)[ptr]; - if ((char*)fld != start) - ios_readall(s->s, start, (const char*)fld - start); - *fld = jl_decode_value(s); - start = (char*)&fld[1]; + if (np) { + if (sz > 0) + memset(v, 0, sz); + JL_GC_PUSH1(&v); + for (i = 0; i < np; i++) { + uint32_t ptr = jl_ptr_offset(dt, i); + jl_value_t **fld = &((jl_value_t**)data)[ptr]; + if ((char*)fld != start) + ios_readall(s->s, start, (const char*)fld - start); + *fld = jl_decode_value(s); + jl_gc_wb(v, *fld); + start = (char*)&fld[1]; + } + JL_GC_POP(); } data += jl_datatype_size(dt); if (data != start) @@ -678,7 +778,7 @@ static jl_value_t *jl_decode_value_any(jl_ircode_state *s, uint8_t tag) JL_GC_DI return v; } -static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED +static jl_value_t *jl_decode_value(jl_ircode_state *s) { assert(!ios_eof(s->s)); jl_value_t *v; @@ -718,16 +818,21 @@ static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED case TAG_SSAVALUE: v = jl_box_ssavalue(read_uint8(s->s)); return v; + case TAG_NEARBYSSAVALUE: + v = jl_box_ssavalue(s->ssaid - read_uint8(s->s)); + return v; case TAG_LONG_SSAVALUE: v = jl_box_ssavalue(read_uint16(s->s)); return v; case TAG_SLOTNUMBER: v = jl_box_slotnumber(read_uint16(s->s)); return v; - case TAG_ARRAY: JL_FALLTHROUGH; case TAG_ARRAY1D: - return jl_decode_value_array(s, tag); + case TAG_ARRAY1D: + return jl_decode_value_array1d(s, tag); case TAG_MEMORYT: - return jl_decode_value_memory(s, jl_decode_value(s), jl_unbox_long(jl_decode_value(s))); + v = jl_decode_value(s); + JL_GC_PROMISE_ROOTED(v); // (JL_ALWAYS_LEAFTYPE) + return (jl_value_t*)jl_decode_value_memory(s, v, jl_unbox_long(jl_decode_value(s))); case TAG_EXPR: JL_FALLTHROUGH; case TAG_LONG_EXPR: JL_FALLTHROUGH; case TAG_CALL1: JL_FALLTHROUGH; @@ -738,27 +843,47 @@ static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED case TAG_PHICNODE: JL_FALLTHROUGH; case TAG_LONG_PHICNODE: return jl_decode_value_phic(s, tag); case TAG_GOTONODE: JL_FALLTHROUGH; case TAG_QUOTENODE: + { v = jl_new_struct_uninit(tag == TAG_GOTONODE ? jl_gotonode_type : jl_quotenode_type); + JL_GC_PUSH1(&v); set_nth_field(tag == TAG_GOTONODE ? jl_gotonode_type : jl_quotenode_type, v, 0, jl_decode_value(s), 0); + JL_GC_POP(); return v; + } case TAG_GOTOIFNOT: + { v = jl_new_struct_uninit(jl_gotoifnot_type); + JL_GC_PUSH1(&v); set_nth_field(jl_gotoifnot_type, v, 0, jl_decode_value(s), 0); set_nth_field(jl_gotoifnot_type, v, 1, jl_decode_value(s), 0); + JL_GC_POP(); return v; + } case TAG_ENTERNODE: + { v = jl_new_struct_uninit(jl_enternode_type); + JL_GC_PUSH1(&v); set_nth_field(jl_enternode_type, v, 0, jl_decode_value(s), 0); set_nth_field(jl_enternode_type, v, 1, jl_decode_value(s), 0); + JL_GC_POP(); return v; + } case TAG_ARGUMENT: + { v = jl_new_struct_uninit(jl_argument_type); + JL_GC_PUSH1(&v); set_nth_field(jl_argument_type, v, 0, jl_decode_value(s), 0); + JL_GC_POP(); return v; + } case TAG_RETURNNODE: + { v = jl_new_struct_uninit(jl_returnnode_type); + JL_GC_PUSH1(&v); set_nth_field(jl_returnnode_type, v, 0, jl_decode_value(s), 0); + JL_GC_POP(); return v; + } case TAG_SHORTER_INT64: v = jl_box_int64((int16_t)read_uint16(s->s)); return v; @@ -777,9 +902,14 @@ static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED case TAG_UINT8: return jl_box_uint8(read_uint8(s->s)); case TAG_NEARBYGLOBAL: - assert(s->method != NULL); + { + jl_method_t *m = s->method; + assert(m != NULL); + JL_GC_PROMISE_ROOTED(m); v = jl_decode_value(s); - return jl_module_globalref(s->method->module, (jl_sym_t*)v); + JL_GC_PROMISE_ROOTED(v); // symbol + return jl_module_globalref(m->module, (jl_sym_t*)v); + } case TAG_NEARBYMODULE: assert(s->method != NULL); return (jl_value_t*)s->method->module; @@ -792,19 +922,29 @@ static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED case TAG_BASE: return (jl_value_t*)jl_base_module; case TAG_VECTORTY: + { v = jl_decode_value(s); - return jl_apply_type2((jl_value_t*)jl_array_type, v, jl_box_long(1)); + JL_GC_PUSH1(&v); + v = jl_apply_type2((jl_value_t*)jl_array_type, v, jl_box_long(1)); + JL_GC_POP(); + return v; + } case TAG_PTRTY: + { v = jl_decode_value(s); - return jl_apply_type1((jl_value_t*)jl_pointer_type, v); + JL_GC_PUSH1(&v); + v = jl_apply_type1((jl_value_t*)jl_pointer_type, v); + JL_GC_POP(); + return v; + } case TAG_STRING: n = read_int32(s->s); v = jl_alloc_string(n); ios_readall(s->s, jl_string_data(v), n); return v; default: - assert(tag == TAG_GENERAL || tag == TAG_SHORT_GENERAL); - return jl_decode_value_any(s, tag); + assert(tag == TAG_GENERAL); + return jl_decode_value_any(s); } } @@ -880,8 +1020,6 @@ JL_DLLEXPORT jl_string_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) assert(jl_array_nrows(code->code) == codelocs_nstmts(code->debuginfo->codelocs) || jl_string_len(code->debuginfo->codelocs) == 0); ios_t dest; ios_mem(&dest, 0); - int en = jl_gc_enable(0); // Might GC - size_t i; if (m->roots == NULL) { m->roots = jl_alloc_vec_any(0); @@ -890,6 +1028,7 @@ JL_DLLEXPORT jl_string_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) jl_value_t *edges = code->edges; jl_ircode_state s = { &dest, + 0, m, (!isdef && jl_is_svec(edges)) ? (jl_svec_t*)edges : jl_emptysvec, jl_current_task->ptls, @@ -900,7 +1039,8 @@ 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->propagate_inbounds, code->has_fcall, code->nospecializeinfer, code->isva, code->inlining, code->constprop, - nargsmatchesmethod); + nargsmatchesmethod, + code->ssaflags); write_uint16(s.s, checked_size(flags.packed, IR_DATASIZE_FLAGS)); write_uint16(s.s, checked_size(code->purity.bits, IR_DATASIZE_PURITY)); write_uint16(s.s, checked_size(code->inlining_cost, IR_DATASIZE_INLINING_COST)); @@ -919,38 +1059,45 @@ JL_DLLEXPORT jl_string_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) write_int32(s.s, (int32_t)nargs); } - for (i = 0; i < 5; i++) { - int copy = 1; - if (i == 1) { // skip debuginfo - assert(jl_field_offset(jl_code_info_type, i) == offsetof(jl_code_info_t, debuginfo)); - continue; - } - jl_encode_value_(&s, jl_get_nth_field((jl_value_t*)code, i), copy); + size_t i, l = jl_array_dim0(code->code); + write_uint64(s.s, l); + for (i = 0; i < l; i++) { + s.ssaid = i; + jl_encode_value(&s, jl_array_ptr_ref(code->code, i)); } + s.ssaid = 0; + jl_encode_value_(&s, (jl_value_t*)code->ssavaluetypes, 1); + assert(jl_typetagis(code->ssaflags, jl_array_uint32_type)); + assert(jl_array_dim0(code->ssaflags) == l); + const uint32_t *ssaflags_data = jl_array_data(code->ssaflags, uint32_t); + if (flags.bits.has_ssaflags) + ios_write(s.s, (const char*)ssaflags_data, l * sizeof(*ssaflags_data)); // For opaque closure, also save the slottypes. We technically only need the first slot type, // but this is simpler for now. We may want to refactor where this gets stored in the future. if (m->is_for_opaque_closure) jl_encode_value_(&s, code->slottypes, 1); + jl_string_t *v = NULL; + JL_GC_PUSH1(&v); // Slotnames. For regular methods, we require that m->slot_syms matches the // CodeInfo's slotnames, so we do not need to save it here. - if (m->generator) + if (m->generator) { // can't optimize generated functions - jl_encode_value_(&s, (jl_value_t*)jl_compress_argnames(code->slotnames), 1); - else + v = jl_compress_argnames(code->slotnames); + jl_encode_value_(&s, (jl_value_t*)v, 1); + } + else { jl_encode_value(&s, jl_nothing); + } write_uint8(s.s, s.relocatability); ios_flush(s.s); - jl_string_t *v = jl_pchar_to_string(s.s->buf, s.s->size); + v = jl_pchar_to_string(s.s->buf, s.s->size); ios_close(s.s); - if (jl_array_nrows(m->roots) == 0) { + if (jl_array_nrows(m->roots) == 0) m->roots = NULL; - } - JL_GC_PUSH1(&v); - jl_gc_enable(en); JL_UNLOCK(&m->writelock); // Might GC JL_GC_POP(); @@ -965,21 +1112,22 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t JL_LOCK(&m->writelock); // protect the roots array (Might GC) assert(jl_is_method(m)); assert(jl_is_string(data)); - size_t i; ios_t src; ios_mem(&src, 0); ios_setbuf(&src, (char*)jl_string_data(data), jl_string_len(data), 0); src.size = jl_string_len(data); - int en = jl_gc_enable(0); // Might GC jl_ircode_state s = { &src, + 0, m, metadata == NULL ? NULL : jl_atomic_load_relaxed(&metadata->edges), jl_current_task->ptls, 1 }; - jl_code_info_t *code = jl_new_code_info_uninit(); + jl_value_t *slotnames = NULL; + JL_GC_PUSH2(&code, &slotnames); + jl_code_info_flags_t flags; flags.packed = read_uint16(s.s); code->inlining = flags.bits.inlining; @@ -991,9 +1139,9 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t code->purity.bits = read_uint16(s.s); code->inlining_cost = read_uint16(s.s); - size_t nslots = read_int32(s.s); code->slotflags = jl_alloc_array_1d(jl_array_uint8_type, nslots); + jl_gc_wb(code, code->slotflags); ios_readall(s.s, jl_array_data(code->slotflags, char), nslots); if (flags.bits.nargsmatchesmethod) { @@ -1002,25 +1150,40 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t code->nargs = read_int32(s.s); } - for (i = 0; i < 5; i++) { - if (i == 1) // skip debuginfo - continue; - assert(jl_field_isptr(jl_code_info_type, i)); - jl_value_t **fld = (jl_value_t**)((char*)jl_data_ptr(code) + jl_field_offset(jl_code_info_type, i)); - *fld = jl_decode_value(&s); - } - if (m->is_for_opaque_closure) + size_t i, l = read_uint64(s.s); + code->code = jl_alloc_array_1d(jl_array_any_type, l); + jl_gc_wb(code, code->code); + for (i = 0; i < l; i++) { + s.ssaid = i; + jl_array_ptr_set(code->code, i, jl_decode_value(&s)); + } + s.ssaid = 0; + code->ssavaluetypes = jl_decode_value(&s); + jl_gc_wb(code, code->ssavaluetypes); + code->ssaflags = jl_alloc_array_1d(jl_array_uint32_type, l); + jl_gc_wb(code, code->ssaflags); + uint32_t *ssaflags_data = jl_array_data(code->ssaflags, uint32_t); + if (flags.bits.has_ssaflags) + ios_readall(s.s, (char*)ssaflags_data, l * sizeof(*ssaflags_data)); + else + memset(ssaflags_data, 0, l * sizeof(*ssaflags_data)); + + if (m->is_for_opaque_closure) { code->slottypes = jl_decode_value(&s); + jl_gc_wb(code, code->slottypes); + } - jl_value_t *slotnames = jl_decode_value(&s); + slotnames = jl_decode_value(&s); if (!jl_is_string(slotnames)) slotnames = m->slot_syms; code->slotnames = jl_uncompress_argnames(slotnames); + jl_gc_wb(code, code->slotnames); if (metadata) code->debuginfo = jl_atomic_load_relaxed(&metadata->debuginfo); else code->debuginfo = m->debuginfo; + jl_gc_wb(code, code->debuginfo); assert(code->debuginfo); assert(jl_array_nrows(code->code) == codelocs_nstmts(code->debuginfo->codelocs) || jl_string_len(code->debuginfo->codelocs) == 0); @@ -1029,10 +1192,7 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t assert(ios_getc(s.s) == -1); ios_close(s.s); - JL_GC_PUSH1(&code); - jl_gc_enable(en); JL_UNLOCK(&m->writelock); // Might GC - JL_GC_POP(); if (metadata) { code->parent = metadata->def; jl_gc_wb(code, code->parent); @@ -1043,6 +1203,7 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t code->edges = (jl_value_t*)s.edges; jl_gc_wb(code, s.edges); } + JL_GC_POP(); return code; } @@ -1470,7 +1631,7 @@ void jl_init_serializer(void) deser_tag[TAG_DATATYPE] = (jl_value_t*)jl_datatype_type; deser_tag[TAG_SLOTNUMBER] = (jl_value_t*)jl_slotnumber_type; deser_tag[TAG_SVEC] = (jl_value_t*)jl_simplevector_type; - deser_tag[TAG_ARRAY] = (jl_value_t*)jl_array_type; + deser_tag[TAG_ARRAY1D] = (jl_value_t*)jl_array_type; deser_tag[TAG_MEMORYT] = (jl_value_t*)jl_genericmemory_type; deser_tag[TAG_EXPR] = (jl_value_t*)jl_expr_type; deser_tag[TAG_PHINODE] = (jl_value_t*)jl_phinode_type; diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index d7e8ca4a4850a..a1b01af716bb4 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -37,11 +37,7 @@ #include #include #include -#if JL_LLVM_VERSION >= 170000 #include -#else -#include -#endif #include #include @@ -386,7 +382,7 @@ static int jl_analyze_workqueue(jl_code_instance_t *callee, jl_codegen_params_t jl_method_instance_t *mi = codeinst->def; size_t nrealargs = jl_nparams(mi->specTypes); // number of actual arguments being passed bool is_opaque_closure = jl_is_method(mi->def.value) && mi->def.method->is_for_opaque_closure; - emit_specsig_to_fptr1(proto.decl, proto.cc, proto.return_roots, mi->specTypes, codeinst->rettype, is_opaque_closure, nrealargs, params, pinvoke, 0, 0); + emit_specsig_to_fptr1(proto.decl, proto.cc, proto.return_roots, mi->specTypes, codeinst->rettype, is_opaque_closure, nrealargs, params, pinvoke); jl_gc_unsafe_leave(ct->ptls, gc_state); preal_decl = ""; // no need to fixup the name } @@ -690,9 +686,9 @@ static void jl_emit_codeinst_to_jit( JL_TIMING(CODEINST_COMPILE, CODEINST_COMPILE); // emit the code in LLVM IR form to the new context jl_codegen_params_t params(std::make_unique(), jl_ExecutionEngine->getDataLayout(), jl_ExecutionEngine->getTargetTriple()); // Locks the context + params.getContext().setDiscardValueNames(true); params.cache = true; params.imaging_mode = imaging_default(); - params.debug_level = jl_options.debug_level; orc::ThreadSafeModule result_m = jl_create_ts_module(name_from_method_instance(codeinst->def), params.tsctx, params.DL, params.TargetTriple); params.temporary_roots = jl_alloc_array_1d(jl_array_any_type, 0); @@ -717,7 +713,7 @@ static void jl_emit_codeinst_to_jit( int waiting = jl_analyze_workqueue(codeinst, params); if (waiting) { auto release = std::move(params.tsctx_lock); // unlock again before moving from it - incompletemodules.insert(std::pair(codeinst, std::make_tuple(std::move(params), waiting))); + incompletemodules.try_emplace(codeinst, std::move(params), waiting); } else { finish_params(result_m.getModuleUnlocked(), params); @@ -764,7 +760,7 @@ static void _jl_compile_codeinst( } -const char *jl_generate_ccallable(LLVMOrcThreadSafeModuleRef llvmmod, void *sysimg_handle, jl_value_t *declrt, jl_value_t *sigt, jl_codegen_params_t ¶ms); +const char *jl_generate_ccallable(Module *llvmmod, void *sysimg_handle, jl_value_t *declrt, jl_value_t *sigt, jl_codegen_params_t ¶ms); // compile a C-callable alias extern "C" JL_DLLEXPORT_CODEGEN @@ -778,44 +774,68 @@ int jl_compile_extern_c_impl(LLVMOrcThreadSafeModuleRef llvmmod, void *p, void * uint8_t measure_compile_time_enabled = jl_atomic_load_relaxed(&jl_measure_compile_time_enabled); if (measure_compile_time_enabled) compiler_start_time = jl_hrtime(); + jl_codegen_params_t *pparams = (jl_codegen_params_t*)p; + DataLayout DL = pparams ? pparams->DL : jl_ExecutionEngine->getDataLayout(); + Triple TargetTriple = pparams ? pparams->TargetTriple : jl_ExecutionEngine->getTargetTriple(); orc::ThreadSafeContext ctx; auto into = unwrap(llvmmod); - jl_codegen_params_t *pparams = (jl_codegen_params_t*)p; orc::ThreadSafeModule backing; + bool success = true; + const char *name = ""; + SmallVector dependencies; if (into == NULL) { - if (!pparams) { - ctx = jl_ExecutionEngine->makeContext(); - } - backing = jl_create_ts_module("cextern", pparams ? pparams->tsctx : ctx, pparams ? pparams->DL : jl_ExecutionEngine->getDataLayout(), pparams ? pparams->TargetTriple : jl_ExecutionEngine->getTargetTriple()); + ctx = pparams ? pparams->tsctx : jl_ExecutionEngine->makeContext(); + backing = jl_create_ts_module("cextern", ctx, DL, TargetTriple); into = &backing; } - bool success = true; - { - auto Lock = into->getContext().getLock(); - Module *M = into->getModuleUnlocked(); - jl_codegen_params_t params(into->getContext(), M->getDataLayout(), Triple(M->getTargetTriple())); - params.imaging_mode = imaging_default(); - params.debug_level = jl_options.debug_level; - if (pparams == NULL) + { // params scope + jl_codegen_params_t params(into->getContext(), DL, TargetTriple); + if (pparams == NULL) { + params.cache = p == NULL; + params.imaging_mode = imaging_default(); + params.tsctx.getContext()->setDiscardValueNames(true); pparams = ¶ms; - assert(pparams->tsctx.getContext() == into->getContext().getContext()); - const char *name = jl_generate_ccallable(wrap(into), sysimg, declrt, sigt, *pparams); - if (!sysimg) { - jl_unique_gcsafe_lock lock(extern_c_lock); - if (jl_ExecutionEngine->getGlobalValueAddress(name)) { - success = false; + } + Module &M = *into->getModuleUnlocked(); + assert(pparams->tsctx.getContext() == &M.getContext()); + name = jl_generate_ccallable(&M, sysimg, declrt, sigt, *pparams); + if (!sysimg && !p) { + { // drop lock to keep analyzer happy (since it doesn't know we have the only reference to it) + auto release = std::move(params.tsctx_lock); } - if (success && p == NULL) { - jl_jit_globals(params.global_targets); - assert(params.workqueue.empty()); - if (params._shared_module) { - jl_ExecutionEngine->optimizeDLSyms(*params._shared_module); // safepoint - jl_ExecutionEngine->addModule(orc::ThreadSafeModule(std::move(params._shared_module), params.tsctx)); + { // lock scope + jl_unique_gcsafe_lock lock(extern_c_lock); + if (jl_ExecutionEngine->getGlobalValueAddress(name)) + success = false; + } + params.tsctx_lock = params.tsctx.getLock(); // re-acquire lock + if (success && params.cache) { + for (auto &it : params.workqueue) { + jl_code_instance_t *codeinst = it.first; + JL_GC_PROMISE_ROOTED(codeinst); + dependencies.push_back(codeinst); + recursive_compile_graph(codeinst, nullptr); } + jl_analyze_workqueue(nullptr, params, true); + assert(params.workqueue.empty()); + finish_params(&M, params); } - if (success && llvmmod == NULL) { - jl_ExecutionEngine->optimizeDLSyms(*M); // safepoint - jl_ExecutionEngine->addModule(std::move(*into)); + } + pparams = nullptr; + } + if (!sysimg && success && llvmmod == NULL) { + { // lock scope + jl_unique_gcsafe_lock lock(extern_c_lock); + if (!jl_ExecutionEngine->getGlobalValueAddress(name)) { + for (auto dep : dependencies) + jl_compile_codeinst_now(dep); + { + auto Lock = backing.getContext().getLock(); + jl_ExecutionEngine->optimizeDLSyms(*backing.getModuleUnlocked()); // safepoint + } + jl_ExecutionEngine->addModule(std::move(backing)); + success = jl_ExecutionEngine->getGlobalValueAddress(name); + assert(success); } } } @@ -1102,22 +1122,13 @@ class JLDebuginfoPlugin : public ObjectLinkingLayer::Plugin { return Error::success(); } -#if JL_LLVM_VERSION >= 160000 Error notifyRemovingResources(JITDylib &JD, orc::ResourceKey K) override -#else - Error notifyRemovingResources(orc::ResourceKey K) override -#endif { return Error::success(); } -#if JL_LLVM_VERSION >= 160000 void notifyTransferringResources(JITDylib &JD, orc::ResourceKey DstKey, orc::ResourceKey SrcKey) override {} -#else - void notifyTransferringResources(orc::ResourceKey DstKey, - orc::ResourceKey SrcKey) override {} -#endif void modifyPassConfig(MaterializationResponsibility &MR, jitlink::LinkGraph &, jitlink::PassConfiguration &PassConfig) override @@ -1167,21 +1178,12 @@ class JLMemoryUsagePlugin : public ObjectLinkingLayer::Plugin { Error notifyFailed(orc::MaterializationResponsibility &MR) override { return Error::success(); } -#if JL_LLVM_VERSION >= 160000 Error notifyRemovingResources(JITDylib &JD, orc::ResourceKey K) override -#else - Error notifyRemovingResources(orc::ResourceKey K) override -#endif { return Error::success(); } -#if JL_LLVM_VERSION >= 160000 void notifyTransferringResources(JITDylib &JD, orc::ResourceKey DstKey, orc::ResourceKey SrcKey) override {} -#else - void notifyTransferringResources(orc::ResourceKey DstKey, - orc::ResourceKey SrcKey) override {} -#endif void modifyPassConfig(orc::MaterializationResponsibility &, jitlink::LinkGraph &, @@ -1198,11 +1200,7 @@ class JLMemoryUsagePlugin : public ObjectLinkingLayer::Plugin { for (auto block : section.blocks()) { secsize += block->getSize(); } -#if JL_LLVM_VERSION >= 160000 if ((section.getMemProt() & orc::MemProt::Exec) == orc::MemProt::None) { -#else - if ((section.getMemProt() & jitlink::MemProt::Exec) == jitlink::MemProt::None) { -#endif data_size += secsize; } else { code_size += secsize; @@ -1234,11 +1232,7 @@ class JLMemoryUsagePlugin : public ObjectLinkingLayer::Plugin { // TODO: Port our memory management optimisations to JITLink instead of using the // default InProcessMemoryManager. std::unique_ptr createJITLinkMemoryManager() JL_NOTSAFEPOINT { -#if JL_LLVM_VERSION < 160000 - return cantFail(orc::MapperJITLinkMemoryManager::CreateWithMapper()); -#else return cantFail(orc::MapperJITLinkMemoryManager::CreateWithMapper(/*Reservation Granularity*/ 16 * 1024 * 1024)); -#endif } #ifdef _COMPILER_CLANG_ @@ -1287,21 +1281,11 @@ class ForwardingMemoryManager : public RuntimeDyld::MemoryManager { bool IsReadOnly) override { return MemMgr->allocateDataSection(Size, Alignment, SectionID, SectionName, IsReadOnly); } -#if JL_LLVM_VERSION >= 160000 virtual void reserveAllocationSpace(uintptr_t CodeSize, Align CodeAlign, uintptr_t RODataSize, Align RODataAlign, uintptr_t RWDataSize, Align RWDataAlign) override { return MemMgr->reserveAllocationSpace(CodeSize, CodeAlign, RODataSize, RODataAlign, RWDataSize, RWDataAlign); } -#else - virtual void reserveAllocationSpace(uintptr_t CodeSize, uint32_t CodeAlign, - uintptr_t RODataSize, - uint32_t RODataAlign, - uintptr_t RWDataSize, - uint32_t RWDataAlign) override { - return MemMgr->reserveAllocationSpace(CodeSize, CodeAlign, RODataSize, RODataAlign, RWDataSize, RWDataAlign); - } -#endif virtual bool needsToReserveAllocationSpace() override { return MemMgr->needsToReserveAllocationSpace(); } @@ -1309,9 +1293,7 @@ class ForwardingMemoryManager : public RuntimeDyld::MemoryManager { size_t Size) override { return MemMgr->registerEHFrames(Addr, LoadAddr, Size); } - virtual void deregisterEHFrames() override { - return MemMgr->deregisterEHFrames(); - } + virtual void deregisterEHFrames() override { /* not actually supported or allowed with this */ } virtual bool finalizeMemory(std::string *ErrMsg = nullptr) override { bool b = false; if (MemMgr.use_count() == 2) @@ -1414,7 +1396,7 @@ namespace { FeaturesStr = Features.getString(); } // Allocate a target... - Optional codemodel = + std::optional codemodel = #ifdef _P64 // Make sure we are using the large code model on 64bit // Let LLVM pick a default suitable for jitting on 32bit @@ -1829,11 +1811,6 @@ struct JuliaOJIT::DLSymOptimizer { if (named) { auto T = GV.getValueType(); assert(T->isPointerTy()); - #if JL_LLVM_VERSION < 170000 - if (!T->isOpaquePointerTy()) { - T = T->getNonOpaquePointerElementType(); - } - #endif init = GlobalAlias::create(T, 0, GlobalValue::PrivateLinkage, GV.getName() + ".jit", init, &M); } GV.setInitializer(init); @@ -1884,11 +1861,6 @@ struct JuliaOJIT::DLSymOptimizer { if (named) { auto T = CI->getType(); assert(T->isPointerTy()); - #if JL_LLVM_VERSION < 170000 - if (!T->isOpaquePointerTy()) { - T = T->getNonOpaquePointerElementType(); - } - #endif init = GlobalAlias::create(T, 0, GlobalValue::PrivateLinkage, CI->getName() + ".jit", init, &M); } // DCE and SimplifyCFG will kill the branching structure around @@ -1932,19 +1904,6 @@ void fixupTM(TargetMachine &TM) { } } -#if JL_LLVM_VERSION < 170000 -extern int jl_opaque_ptrs_set; -void SetOpaquePointer(LLVMContext &ctx) { - if (jl_opaque_ptrs_set) - return; -#ifndef JL_LLVM_OPAQUE_POINTERS - ctx.setOpaquePointers(false); -#else - ctx.setOpaquePointers(true); -#endif -} -#endif - llvm::DataLayout jl_create_datalayout(TargetMachine &TM) { // Mark our address spaces as non-integral auto jl_data_layout = TM.createDataLayout(); @@ -2052,7 +2011,7 @@ JuliaOJIT::JuliaOJIT() orc::SymbolAliasMap jl_crt = { // Float16 conversion routines -#if defined(_CPU_X86_64_) && defined(_OS_DARWIN_) && JL_LLVM_VERSION >= 160000 +#if defined(_CPU_X86_64_) && defined(_OS_DARWIN_) // LLVM 16 reverted to soft-float ABI for passing half on x86_64 Darwin // https://github.com/llvm/llvm-project/commit/2bcf51c7f82ca7752d1bba390a2e0cb5fdd05ca9 { mangle("__gnu_h2f_ieee"), { mangle("julia_half_to_float"), JITSymbolFlags::Exported } }, @@ -2086,7 +2045,6 @@ JuliaOJIT::JuliaOJIT() #ifdef MSAN_EMUTLS_WORKAROUND orc::SymbolMap msan_crt; - #if JL_LLVM_VERSION >= 170000 msan_crt[mangle("__emutls_get_address")] = {ExecutorAddr::fromPtr(msan_workaround::getTLSAddress), JITSymbolFlags::Exported}; msan_crt[mangle("__emutls_v.__msan_param_tls")] = {ExecutorAddr::fromPtr( reinterpret_cast(static_cast(msan_workaround::MSanTLS::param))), JITSymbolFlags::Exported}; @@ -2104,25 +2062,6 @@ JuliaOJIT::JuliaOJIT() reinterpret_cast(static_cast(msan_workaround::MSanTLS::va_arg_overflow_size))), JITSymbolFlags::Exported}; msan_crt[mangle("__emutls_v.__msan_origin_tls")] = {ExecutorAddr::fromPtr( reinterpret_cast(static_cast(msan_workaround::MSanTLS::origin))), JITSymbolFlags::Exported}; - #else - msan_crt[mangle("__emutls_get_address")] = JITEvaluatedSymbol::fromPointer(msan_workaround::getTLSAddress, JITSymbolFlags::Exported); - msan_crt[mangle("__emutls_v.__msan_param_tls")] = JITEvaluatedSymbol::fromPointer( - reinterpret_cast(static_cast(msan_workaround::MSanTLS::param)), JITSymbolFlags::Exported); - msan_crt[mangle("__emutls_v.__msan_param_origin_tls")] = JITEvaluatedSymbol::fromPointer( - reinterpret_cast(static_cast(msan_workaround::MSanTLS::param_origin)), JITSymbolFlags::Exported); - msan_crt[mangle("__emutls_v.__msan_retval_tls")] = JITEvaluatedSymbol::fromPointer( - reinterpret_cast(static_cast(msan_workaround::MSanTLS::retval)), JITSymbolFlags::Exported); - msan_crt[mangle("__emutls_v.__msan_retval_origin_tls")] = JITEvaluatedSymbol::fromPointer( - reinterpret_cast(static_cast(msan_workaround::MSanTLS::retval_origin)), JITSymbolFlags::Exported); - msan_crt[mangle("__emutls_v.__msan_va_arg_tls")] = JITEvaluatedSymbol::fromPointer( - reinterpret_cast(static_cast(msan_workaround::MSanTLS::va_arg)), JITSymbolFlags::Exported); - msan_crt[mangle("__emutls_v.__msan_va_arg_origin_tls")] = JITEvaluatedSymbol::fromPointer( - reinterpret_cast(static_cast(msan_workaround::MSanTLS::va_arg_origin)), JITSymbolFlags::Exported); - msan_crt[mangle("__emutls_v.__msan_va_arg_overflow_size_tls")] = JITEvaluatedSymbol::fromPointer( - reinterpret_cast(static_cast(msan_workaround::MSanTLS::va_arg_overflow_size)), JITSymbolFlags::Exported); - msan_crt[mangle("__emutls_v.__msan_origin_tls")] = JITEvaluatedSymbol::fromPointer( - reinterpret_cast(static_cast(msan_workaround::MSanTLS::origin)), JITSymbolFlags::Exported); - #endif cantFail(GlobalJD.define(orc::absoluteSymbols(msan_crt))); #endif #if JL_LLVM_VERSION < 190000 @@ -2132,11 +2071,7 @@ JuliaOJIT::JuliaOJIT() // hopefully fixed upstream by e7698a13e319a9919af04d3d693a6f6ea7168a44 static int64_t jl___asan_globals_registered; orc::SymbolMap asan_crt; - #if JL_LLVM_VERSION >= 170000 asan_crt[mangle("___asan_globals_registered")] = {ExecutorAddr::fromPtr(&jl___asan_globals_registered), JITSymbolFlags::Common | JITSymbolFlags::Exported}; - #else - asan_crt[mangle("___asan_globals_registered")] = JITEvaluatedSymbol::fromPointer(&jl___asan_globals_registered, JITSymbolFlags::Common | JITSymbolFlags::Exported); - #endif cantFail(JD.define(orc::absoluteSymbols(asan_crt))); #endif #endif @@ -2147,9 +2082,6 @@ JuliaOJIT::~JuliaOJIT() = default; ThreadSafeContext JuliaOJIT::makeContext() { auto ctx = std::make_unique(); - #if JL_LLVM_VERSION < 170000 - SetOpaquePointer(*ctx); - #endif return orc::ThreadSafeContext(std::move(ctx)); } @@ -2161,11 +2093,7 @@ orc::SymbolStringPtr JuliaOJIT::mangle(StringRef Name) void JuliaOJIT::addGlobalMapping(StringRef Name, uint64_t Addr) { - #if JL_LLVM_VERSION >= 170000 cantFail(JD.define(orc::absoluteSymbols({{mangle(Name), {ExecutorAddr::fromPtr((void*)Addr), JITSymbolFlags::Exported}}}))); - #else - cantFail(JD.define(orc::absoluteSymbols({{mangle(Name), JITEvaluatedSymbol::fromPointer((void*)Addr)}}))); - #endif } void JuliaOJIT::addModule(orc::ThreadSafeModule TSM) @@ -2244,7 +2172,6 @@ SmallVector JuliaOJIT::findSymbols(ArrayRef Names) return Addrs; } -#if JL_LLVM_VERSION >= 170000 Expected JuliaOJIT::findSymbol(StringRef Name, bool ExportedSymbolsOnly) { orc::JITDylib* SearchOrders[3] = {&JD, &GlobalJD, &ExternalJD}; @@ -2285,50 +2212,6 @@ uint64_t JuliaOJIT::getFunctionAddress(StringRef Name) } return addr->getAddress().getValue(); } -#else -JL_JITSymbol JuliaOJIT::findSymbol(StringRef Name, bool ExportedSymbolsOnly) -{ - orc::JITDylib* SearchOrders[3] = {&JD, &GlobalJD, &ExternalJD}; - ArrayRef SearchOrder = ArrayRef(&SearchOrders[0], ExportedSymbolsOnly ? 3 : 1); - auto Sym = ES.lookup(SearchOrder, Name); - if (Sym) - return *Sym; - return Sym.takeError(); -} - -JL_JITSymbol JuliaOJIT::findUnmangledSymbol(StringRef Name) -{ - return findSymbol(getMangledName(Name), true); -} - -Expected JuliaOJIT::findExternalJDSymbol(StringRef Name, bool ExternalJDOnly) -{ - orc::JITDylib* SearchOrders[3] = {&ExternalJD, &GlobalJD, &JD}; - ArrayRef SearchOrder = ArrayRef(&SearchOrders[0], ExternalJDOnly ? 1 : 3); - auto Sym = ES.lookup(SearchOrder, getMangledName(Name)); - return Sym; -} - -uint64_t JuliaOJIT::getGlobalValueAddress(StringRef Name) -{ - auto addr = findSymbol(getMangledName(Name), false); - if (!addr) { - consumeError(addr.takeError()); - return 0; - } - return cantFail(addr).getAddress(); -} - -uint64_t JuliaOJIT::getFunctionAddress(StringRef Name) -{ - auto addr = findSymbol(getMangledName(Name), false); - if (!addr) { - consumeError(addr.takeError()); - return 0; - } - return cantFail(addr).getAddress(); -} -#endif StringRef JuliaOJIT::getFunctionAtAddress(uint64_t Addr, jl_callptr_t invoke, jl_code_instance_t *codeinst) { @@ -2360,13 +2243,8 @@ StringRef JuliaOJIT::getFunctionAtAddress(uint64_t Addr, jl_callptr_t invoke, jl } #ifdef JL_USE_JITLINK -#if JL_LLVM_VERSION >= 170000 #define addAbsoluteToMap(map,name) \ (map[mangle(#name)] = {ExecutorAddr::fromPtr(&name), JITSymbolFlags::Exported | JITSymbolFlags::Callable}, orc::ExecutorAddr::fromPtr(&name)) -#else -#define addAbsoluteToMap(map,name) \ - (map[mangle(#name)] = JITEvaluatedSymbol::fromPointer(&name, JITSymbolFlags::Exported | JITSymbolFlags::Callable), orc::ExecutorAddr::fromPtr(&name)) -#endif void JuliaOJIT::enableJITDebuggingSupport() { @@ -2551,11 +2429,7 @@ void jl_merge_module(orc::ThreadSafeModule &destTSM, orc::ThreadSafeModule srcTS } // Reparent the global variable: SG.removeFromParent(); - #if JL_LLVM_VERSION >= 170000 dest.insertGlobalVariable(&SG); - #else - dest.getGlobalList().push_back(&SG); - #endif // Comdat is owned by the Module SG.setComdat(nullptr); } @@ -2602,11 +2476,7 @@ void jl_merge_module(orc::ThreadSafeModule &destTSM, orc::ThreadSafeModule srcTS } } SG.removeFromParent(); - #if JL_LLVM_VERSION >= 170000 dest.insertAlias(&SG); - #else - dest.getAliasList().push_back(&SG); - #endif } // metadata nodes need to be explicitly merged not just copied diff --git a/src/jitlayers.h b/src/jitlayers.h index d5fa878211200..b6f1e9ec20c6b 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -259,9 +259,8 @@ struct jl_codegen_params_t { bool cache = false; bool external_linkage = false; bool imaging_mode; - int debug_level; bool use_swiftcc = true; - jl_codegen_params_t(orc::ThreadSafeContext ctx, DataLayout DL, Triple triple) + jl_codegen_params_t(orc::ThreadSafeContext ctx, DataLayout DL, Triple triple) JL_NOTSAFEPOINT JL_NOTSAFEPOINT_ENTER : tsctx(std::move(ctx)), tsctx_lock(tsctx.getLock()), DL(std::move(DL)), @@ -272,6 +271,8 @@ struct jl_codegen_params_t { if (TargetTriple.isRISCV()) use_swiftcc = false; } + jl_codegen_params_t(jl_codegen_params_t &&) JL_NOTSAFEPOINT = default; + ~jl_codegen_params_t() JL_NOTSAFEPOINT JL_NOTSAFEPOINT_LEAVE = default; }; jl_llvm_functions_t jl_emit_code( @@ -300,8 +301,7 @@ void emit_specsig_to_fptr1( jl_value_t *calltype, jl_value_t *rettype, bool is_for_opaque_closure, size_t nargs, jl_codegen_params_t ¶ms, - Function *target, - size_t min_world, size_t max_world) JL_NOTSAFEPOINT; + Function *target) JL_NOTSAFEPOINT; Function *get_or_emit_fptr1(StringRef Name, Module *M) JL_NOTSAFEPOINT; void jl_init_function(Function *F, const Triple &TT) JL_NOTSAFEPOINT; @@ -364,14 +364,6 @@ class MaxAlignedAllocImpl }; using MaxAlignedAlloc = MaxAlignedAllocImpl<>; -#if JL_LLVM_VERSION < 170000 -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 -// is expected. -typedef JITSymbol JL_SymbolInfo; -#endif - using CompilerResultT = Expected>; using OptimizerResultT = Expected; using SharedBytesT = StringSet::MapEntryTy)>>; @@ -473,7 +465,7 @@ class JuliaOJIT { } private: ResourcePool &pool; - Optional resource; + std::optional resource; }; OwningResource operator*() JL_NOTSAFEPOINT { @@ -559,16 +551,10 @@ class JuliaOJIT { orc::ExecutionSession &getExecutionSession() JL_NOTSAFEPOINT { return ES; } orc::JITDylib &getExternalJITDylib() JL_NOTSAFEPOINT { return ExternalJD; } - #if JL_LLVM_VERSION >= 170000 Expected findSymbol(StringRef Name, bool ExportedSymbolsOnly) JL_NOTSAFEPOINT; Expected findUnmangledSymbol(StringRef Name) JL_NOTSAFEPOINT; Expected findExternalJDSymbol(StringRef Name, bool ExternalJDOnly) JL_NOTSAFEPOINT; SmallVector findSymbols(ArrayRef Names) JL_NOTSAFEPOINT; - #else - JITEvaluatedSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) JL_NOTSAFEPOINT; - JITEvaluatedSymbol findUnmangledSymbol(StringRef Name) JL_NOTSAFEPOINT; - Expected findExternalJDSymbol(StringRef Name, bool ExternalJDOnly) JL_NOTSAFEPOINT; - #endif uint64_t getGlobalValueAddress(StringRef Name) JL_NOTSAFEPOINT; uint64_t getFunctionAddress(StringRef Name) JL_NOTSAFEPOINT; StringRef getFunctionAtAddress(uint64_t Addr, jl_callptr_t invoke, jl_code_instance_t *codeinst) JL_NOTSAFEPOINT; @@ -661,10 +647,6 @@ Module &jl_codegen_params_t::shared_module() JL_NOTSAFEPOINT { } void fixupTM(TargetMachine &TM) JL_NOTSAFEPOINT; -#if JL_LLVM_VERSION < 170000 -void SetOpaquePointer(LLVMContext &ctx) JL_NOTSAFEPOINT; -#endif - void optimizeDLSyms(Module &M); // NewPM diff --git a/src/jlapi.c b/src/jlapi.c index a3621385a437e..defb2db6ac911 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -809,6 +809,28 @@ JL_DLLEXPORT uint64_t jl_cumulative_recompile_time_ns(void) return jl_atomic_load_relaxed(&jl_cumulative_recompile_time); } +/** + * @brief Enable per-task timing. + */ +JL_DLLEXPORT void jl_task_metrics_enable(void) +{ + // Increment the flag to allow reentrant callers. + jl_atomic_fetch_add(&jl_task_metrics_enabled, 1); +} + +/** + * @brief Disable per-task timing. + */ +JL_DLLEXPORT void jl_task_metrics_disable(void) +{ + // Prevent decrementing the counter below zero + uint8_t enabled = jl_atomic_load_relaxed(&jl_task_metrics_enabled); + while (enabled > 0) { + if (jl_atomic_cmpswap(&jl_task_metrics_enabled, &enabled, enabled-1)) + break; + } +} + /** * @brief Retrieve floating-point environment constants. * diff --git a/src/jlfrontend.scm b/src/jlfrontend.scm index 3d46940d6fcbb..9c69da199c0cd 100644 --- a/src/jlfrontend.scm +++ b/src/jlfrontend.scm @@ -31,7 +31,6 @@ ;; this is overwritten when we run in actual julia (define (defined-julia-global v) #f) -(define (nothrow-julia-global v) #f) ;; parser entry points diff --git a/src/jloptions.c b/src/jloptions.c index f81cf0453db21..c68b5ce193d98 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -152,6 +152,7 @@ JL_DLLEXPORT void jl_init_options(void) 0, // heap-size-hint 0, // trace_compile_timing JL_TRIM_NO, // trim + 0, // task_metrics }; jl_options_initialized = 1; } @@ -316,6 +317,7 @@ static const char opts_hidden[] = " comment if color is not supported\n" " --trace-compile-timing If --trace-compile is enabled show how long each took to\n" " compile in ms\n" + " --task-metrics={yes|no*} Enable collection of per-task timing data.\n" " --image-codegen Force generate code in imaging mode\n" " --permalloc-pkgimg={yes|no*} Copy the data section of package images into memory\n" " --trim={no*|safe|unsafe|unsafe-warn}\n" @@ -347,6 +349,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) opt_trace_compile, opt_trace_compile_timing, opt_trace_dispatch, + opt_task_metrics, opt_math_mode, opt_worker, opt_bind_to, @@ -427,6 +430,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) { "trace-compile", required_argument, 0, opt_trace_compile }, { "trace-compile-timing", no_argument, 0, opt_trace_compile_timing }, { "trace-dispatch", required_argument, 0, opt_trace_dispatch }, + { "task-metrics", required_argument, 0, opt_task_metrics }, { "math-mode", required_argument, 0, opt_math_mode }, { "handle-signals", required_argument, 0, opt_handle_signals }, // hidden command line options @@ -978,6 +982,14 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) else jl_errorf("julia: invalid argument to --trim={safe|no|unsafe|unsafe-warn} (%s)", optarg); break; + case opt_task_metrics: + if (!strcmp(optarg, "no")) + jl_options.task_metrics = JL_OPTIONS_TASK_METRICS_OFF; + else if (!strcmp(optarg, "yes")) + jl_options.task_metrics = JL_OPTIONS_TASK_METRICS_ON; + else + jl_errorf("julia: invalid argument to --task-metrics={yes|no} (%s)", optarg); + break; default: jl_errorf("julia: unhandled option -- %c\n" "This is a bug, please report it.", c); diff --git a/src/jloptions.h b/src/jloptions.h index b9910702f3f9b..211122242cbbd 100644 --- a/src/jloptions.h +++ b/src/jloptions.h @@ -65,6 +65,7 @@ typedef struct { uint64_t heap_size_hint; int8_t trace_compile_timing; int8_t trim; + int8_t task_metrics; } jl_options_t; #endif diff --git a/src/jltypes.c b/src/jltypes.c index 6c6325d84a5ff..52b993cd5aa4d 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -910,7 +910,7 @@ JL_DLLEXPORT jl_value_t *jl_type_unionall(jl_tvar_t *v, jl_value_t *body) if (jl_options.depwarn) { if (jl_options.depwarn == JL_OPTIONS_DEPWARN_ERROR) jl_error("Wrapping `Vararg` directly in UnionAll is deprecated (wrap the tuple instead).\nYou may need to write `f(x::Vararg{T})` rather than `f(x::Vararg{<:T})` or `f(x::Vararg{T}) where T` instead of `f(x::Vararg{T} where T)`."); - jl_printf(JL_STDERR, "WARNING: Wrapping `Vararg` directly in UnionAll is deprecated (wrap the tuple instead).\nYou may need to write `f(x::Vararg{T})` rather than `f(x::Vararg{<:T})` or `f(x::Vararg{T}) where T` instead of `f(x::Vararg{T} where T)`.\n"); + jl_printf(JL_STDERR, "WARNING: Wrapping `Vararg` directly in UnionAll is deprecated (wrap the tuple instead).\nYou may need to write `f(x::Vararg{T})` rather than `f(x::Vararg{<:T})` or `f(x::Vararg{T}) where T` instead of `f(x::Vararg{T} where T)`.\nTo make this warning an error, and hence obtain a stack trace, use `julia --depwarn=error`.\n"); } jl_vararg_t *vm = (jl_vararg_t*)body; int T_has_tv = vm->T && jl_has_typevar(vm->T, v); @@ -3746,7 +3746,7 @@ void jl_init_types(void) JL_GC_DISABLED NULL, jl_any_type, jl_emptysvec, - jl_perm_symsvec(16, + jl_perm_symsvec(27, "next", "queue", "storage", @@ -3754,16 +3754,27 @@ void jl_init_types(void) JL_GC_DISABLED "result", "scope", "code", + "_state", + "sticky", + "priority", + "_isexception", + "pad00", + "pad01", + "pad02", "rngState0", "rngState1", "rngState2", "rngState3", "rngState4", - "_state", - "sticky", - "_isexception", - "priority"), - jl_svec(16, + "metrics_enabled", + "pad10", + "pad11", + "pad12", + "first_enqueued_at", + "last_started_running_at", + "running_time_ns", + "finished_at"), + jl_svec(27, jl_any_type, jl_any_type, jl_any_type, @@ -3771,21 +3782,36 @@ void jl_init_types(void) JL_GC_DISABLED jl_any_type, jl_any_type, jl_any_type, + jl_uint8_type, + jl_bool_type, + jl_uint16_type, + jl_bool_type, + jl_uint8_type, + jl_uint8_type, + jl_uint8_type, jl_uint64_type, jl_uint64_type, jl_uint64_type, jl_uint64_type, jl_uint64_type, - jl_uint8_type, jl_bool_type, - jl_bool_type, - jl_uint16_type), + jl_uint8_type, + jl_uint8_type, + jl_uint8_type, + jl_uint64_type, + jl_uint64_type, + jl_uint64_type, + jl_uint64_type), jl_emptysvec, 0, 1, 6); XX(task); jl_value_t *listt = jl_new_struct(jl_uniontype_type, jl_task_type, jl_nothing_type); jl_svecset(jl_task_type->types, 0, listt); - const static uint32_t task_atomicfields[1] = {0x00001000}; // Set fields 13 as atomic + // Set field 20 (metrics_enabled) as const + // Set fields 8 (_state) and 24-27 (metric counters) as atomic + const static uint32_t task_constfields[1] = { 0b00000000000010000000000000000000 }; + const static uint32_t task_atomicfields[1] = { 0b00000111100000000000000010000000 }; + jl_task_type->name->constfields = task_constfields; jl_task_type->name->atomicfields = task_atomicfields; tv = jl_svec2(tvar("A"), tvar("R")); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 72e97da3c2daa..c522a565f462a 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -4279,6 +4279,7 @@ f(x) = yt(x) (if (or exists (and short (pair? alldefs))) `(toplevel-butfirst (null) + ,@(map (lambda (v) `(moved-local ,v)) moved-vars) ,@sp-inits ,@mk-method (latestworld)) @@ -4355,7 +4356,6 @@ f(x) = yt(x) (define (valid-ir-argument? e) (or (simple-atom? e) - (and (globalref? e) (nothrow-julia-global (cadr e) (caddr e))) (and (pair? e) (memq (car e) '(quote inert top core slot static_parameter))))) @@ -4606,14 +4606,20 @@ f(x) = yt(x) (cdr cnd) (list cnd)))))) tests)) + (define (emit-assignment-or-setglobal lhs rhs) + (if (globalref? lhs) + (begin + (emit `(global ,lhs)) + (emit `(call (top setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))) + (emit `(= ,lhs ,rhs)))) (define (emit-assignment lhs rhs) (if rhs (if (valid-ir-rvalue? lhs rhs) - (emit `(= ,lhs ,rhs)) + (emit-assignment-or-setglobal lhs rhs) (let ((rr (make-ssavalue))) (emit `(= ,rr ,rhs)) - (emit `(= ,lhs ,rr)))) - (emit `(= ,lhs (null)))) ; in unreachable code (such as after return), still emit the assignment so that the structure of those uses is preserved + (emit-assignment-or-setglobal lhs rr))) + (emit-assignment-or-setglobal lhs `(null))) ; in unreachable code (such as after return), still emit the assignment so that the structure of those uses is preserved #f) ;; the interpreter loop. `break-labels` keeps track of the labels to jump to ;; for all currently closing break-blocks. @@ -4692,7 +4698,7 @@ f(x) = yt(x) rhs (make-ssavalue)))) (if (not (eq? rr rhs)) (emit `(= ,rr ,rhs))) - (emit `(= ,lhs ,rr)) + (emit-assignment-or-setglobal lhs rr) (if tail (emit-return tail rr)) rr) (emit-assignment lhs rhs)))))) diff --git a/src/julia.expmap.in b/src/julia.expmap.in index 29366f6296a85..b28a714e75f69 100644 --- a/src/julia.expmap.in +++ b/src/julia.expmap.in @@ -2,7 +2,7 @@ global: pthread*; __stack_chk_*; - asprintf; + asprintf*; bitvector_*; ios_*; arraylist_*; @@ -10,33 +10,33 @@ jl_*; ijl_*; _jl_mutex_*; - rec_backtrace; + rec_backtrace*; julia_*; - libsupport_init; - localtime_r; - memhash; - memhash32; - memhash32_seed; - memhash_seed; - restore_signals; + libsupport_init*; + localtime_r*; + memhash*; + memhash32*; + memhash32_seed*; + memhash_seed*; + restore_signals*; u8_*; uv_*; - add_library_mapping; + add_library_mapping*; utf8proc_*; - jlbacktrace; - jlbacktracet; - _IO_stdin_used; - _Z24jl_coverage_data_pointerN4llvm9StringRefEi; - _Z22jl_coverage_alloc_lineN4llvm9StringRefEi; - _Z22jl_malloc_data_pointerN4llvm9StringRefEi; + jlbacktrace*; + jlbacktracet*; + _IO_stdin_used*; /* glibc expects this to be exported to detect which version of glibc is being used, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=634261#109 for further details */ + _Z24jl_coverage_data_pointerN4llvm9StringRefEi*; + _Z22jl_coverage_alloc_lineN4llvm9StringRefEi*; + _Z22jl_malloc_data_pointerN4llvm9StringRefEi*; _jl_timing_*; LLVMExtra*; JLJIT*; - llvmGetPassPluginInfo; + llvmGetPassPluginInfo*; /* freebsd */ - environ; - __progname; + environ*; + __progname*; local: *; diff --git a/src/julia.h b/src/julia.h index 944fd3c43a297..049416a33336f 100644 --- a/src/julia.h +++ b/src/julia.h @@ -279,7 +279,7 @@ typedef union __jl_purity_overrides_t { } _jl_purity_overrides_t; #define NUM_EFFECTS_OVERRIDES 11 -#define NUM_IR_FLAGS 13 +#define NUM_IR_FLAGS 3 // This type describes a single function body typedef struct _jl_code_info_t { @@ -292,15 +292,8 @@ typedef struct _jl_code_info_t { // 1 << 0 = inbounds region // 1 << 1 = callsite inline region // 1 << 2 = callsite noinline region - // 1 << 3 = refined statement - // 1 << 4 = :consistent - // 1 << 5 = :effect_free - // 1 << 6 = :nothrow - // 1 << 7 = :terminates - // 1 << 8 = :noub - // 1 << 9 = :effect_free_if_inaccessiblememonly - // 1 << 10 = :inaccessiblemem_or_argmemonly - // 1 << 11-19 = callsite effects overrides + // 1 << 3-14 = purity + // 1 << 16+ = reserved for inference // miscellaneous data: jl_array_t *slotnames; // names of local variables jl_array_t *slotflags; // local var bit flags @@ -1956,6 +1949,7 @@ JL_DLLEXPORT jl_genericmemory_t *jl_ptr_to_genericmemory(jl_value_t *mtype, void size_t nel, int own_buffer); JL_DLLEXPORT jl_genericmemory_t *jl_alloc_genericmemory(jl_value_t *mtype, size_t nel); JL_DLLEXPORT jl_genericmemory_t *jl_pchar_to_memory(const char *str, size_t len); +JL_DLLEXPORT jl_genericmemory_t *jl_alloc_genericmemory_unchecked(jl_ptls_t ptls, size_t nbytes, jl_datatype_t *mtype); JL_DLLEXPORT jl_value_t *jl_genericmemory_to_string(jl_genericmemory_t *m, size_t len); JL_DLLEXPORT jl_genericmemory_t *jl_alloc_memory_any(size_t n); JL_DLLEXPORT jl_value_t *jl_genericmemoryref(jl_genericmemory_t *m, size_t i); // 0-indexed @@ -2075,6 +2069,7 @@ JL_DLLEXPORT void JL_NORETURN jl_type_error_rt(const char *fname, jl_value_t *got JL_MAYBE_UNROOTED); JL_DLLEXPORT void JL_NORETURN jl_undefined_var_error(jl_sym_t *var, jl_value_t *scope JL_MAYBE_UNROOTED); JL_DLLEXPORT void JL_NORETURN jl_has_no_field_error(jl_datatype_t *t, jl_sym_t *var); +JL_DLLEXPORT void JL_NORETURN jl_argument_error(char *str); JL_DLLEXPORT void JL_NORETURN jl_atomic_error(char *str); JL_DLLEXPORT void JL_NORETURN jl_bounds_error(jl_value_t *v JL_MAYBE_UNROOTED, jl_value_t *t JL_MAYBE_UNROOTED); @@ -2283,16 +2278,25 @@ typedef struct _jl_task_t { jl_value_t *result; jl_value_t *scope; jl_function_t *start; - // 4 byte padding on 32-bit systems - // uint32_t padding0; - uint64_t rngState[JL_RNG_SIZE]; _Atomic(uint8_t) _state; uint8_t sticky; // record whether this Task can be migrated to a new thread - _Atomic(uint8_t) _isexception; // set if `result` is an exception to throw or that we exited with - // 1 byte padding - // uint8_t padding1; - // multiqueue priority uint16_t priority; + _Atomic(uint8_t) _isexception; // set if `result` is an exception to throw or that we exited with + uint8_t pad0[3]; + // === 64 bytes (cache line) + uint64_t rngState[JL_RNG_SIZE]; + // flag indicating whether or not to record timing metrics for this task + uint8_t metrics_enabled; + uint8_t pad1[3]; + // timestamp this task first entered the run queue + _Atomic(uint64_t) first_enqueued_at; + // timestamp this task was most recently scheduled to run + _Atomic(uint64_t) last_started_running_at; + // time this task has spent running; updated when it yields or finishes. + _Atomic(uint64_t) running_time_ns; + // === 64 bytes (cache line) + // timestamp this task finished (i.e. entered state DONE or FAILED). + _Atomic(uint64_t) finished_at; // hidden state: // cached floating point environment @@ -2619,6 +2623,9 @@ JL_DLLEXPORT int jl_generating_output(void) JL_NOTSAFEPOINT; #define JL_TRIM_UNSAFE 2 #define JL_TRIM_UNSAFE_WARN 3 +#define JL_OPTIONS_TASK_METRICS_OFF 0 +#define JL_OPTIONS_TASK_METRICS_ON 1 + // Version information #include // Generated file diff --git a/src/julia_internal.h b/src/julia_internal.h index ca3f63b274968..e0e8158f8e0d9 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -316,6 +316,9 @@ extern JL_DLLEXPORT _Atomic(uint8_t) jl_measure_compile_time_enabled; extern JL_DLLEXPORT _Atomic(uint64_t) jl_cumulative_compile_time; extern JL_DLLEXPORT _Atomic(uint64_t) jl_cumulative_recompile_time; +// Global *atomic* integer controlling *process-wide* task timing. +extern JL_DLLEXPORT _Atomic(uint8_t) jl_task_metrics_enabled; + #define jl_return_address() ((uintptr_t)__builtin_return_address(0)) STATIC_INLINE uint32_t jl_int32hash_fast(uint32_t a) @@ -651,6 +654,7 @@ typedef struct { uint16_t nargsmatchesmethod:1; uint16_t inlining:2; // 0 = use heuristic; 1 = aggressive; 2 = none uint16_t constprop:2; // 0 = use heuristic; 1 = aggressive; 2 = none + uint16_t has_ssaflags:1; } jl_code_info_flags_bitfield_t; typedef union { @@ -1191,7 +1195,7 @@ _Atomic(jl_value_t*) *jl_table_peek_bp(jl_genericmemory_t *a, jl_value_t *key) J JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t*); JL_DLLEXPORT jl_methtable_t *jl_new_method_table(jl_sym_t *name, jl_module_t *module); -JL_DLLEXPORT jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types, size_t world, size_t *min_valid, size_t *max_valid, int mt_cache); +JL_DLLEXPORT jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types, size_t world, int mt_cache); jl_method_instance_t *jl_get_specialized(jl_method_t *m, jl_value_t *types, jl_svec_t *sp); JL_DLLEXPORT jl_value_t *jl_rettype_inferred(jl_value_t *owner, jl_method_instance_t *li JL_PROPAGATES_ROOT, size_t min_world, size_t max_world); JL_DLLEXPORT jl_value_t *jl_rettype_inferred_native(jl_method_instance_t *mi, size_t min_world, size_t max_world) JL_NOTSAFEPOINT; @@ -1951,13 +1955,15 @@ JL_DLLIMPORT void *jl_create_native(jl_array_t *methods, LLVMOrcThreadSafeModule JL_DLLIMPORT void jl_dump_native(void *native_code, const char *bc_fname, const char *unopt_bc_fname, const char *obj_fname, const char *asm_fname, ios_t *z, ios_t *s, jl_emission_params_t *params); -JL_DLLIMPORT void jl_get_llvm_gvs(void *native_code, arraylist_t *gvs); -JL_DLLIMPORT void jl_get_llvm_external_fns(void *native_code, arraylist_t *gvs); +JL_DLLIMPORT void jl_get_llvm_gvs(void *native_code, size_t *num_els, void **gvs); +JL_DLLIMPORT void jl_get_llvm_external_fns(void *native_code, size_t *num_els, + jl_code_instance_t *gvs); JL_DLLIMPORT void jl_get_function_id(void *native_code, jl_code_instance_t *ncode, int32_t *func_idx, int32_t *specfunc_idx); JL_DLLIMPORT void jl_register_fptrs(uint64_t image_base, const struct _jl_image_fptrs_t *fptrs, jl_method_instance_t **linfos, size_t n); -JL_DLLIMPORT void jl_get_llvm_mis(void *native_code, arraylist_t* MIs); +JL_DLLIMPORT void jl_get_llvm_mis(void *native_code, size_t *num_els, + jl_method_instance_t *MIs); JL_DLLIMPORT void jl_init_codegen(void); JL_DLLIMPORT void jl_teardown_codegen(void) JL_NOTSAFEPOINT; JL_DLLIMPORT int jl_getFunctionInfo(jl_frame_t **frames, uintptr_t pointer, int skipC, int noInline) JL_NOTSAFEPOINT; diff --git a/src/llvm-codegen-shared.h b/src/llvm-codegen-shared.h index a99e18f3e3762..d9551e0552f9c 100644 --- a/src/llvm-codegen-shared.h +++ b/src/llvm-codegen-shared.h @@ -1,5 +1,6 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license +#include #include #include #include @@ -8,30 +9,15 @@ #include #include #include - -#if JL_LLVM_VERSION >= 160000 #include -#endif #include "julia.h" #define STR(csym) #csym #define XSTR(csym) STR(csym) -#if JL_LLVM_VERSION >= 160000 - -#include - -template -using Optional = std::optional; static constexpr std::nullopt_t None = std::nullopt; -#else - -#include - -#endif - enum AddressSpace { Generic = 0, Tracked = 10, @@ -180,7 +166,7 @@ static inline llvm::Instruction *tbaa_decorate(llvm::MDNode *md, llvm::Instructi using namespace llvm; inst->setMetadata(llvm::LLVMContext::MD_tbaa, md); if (llvm::isa(inst) && md && md == get_tbaa_const(md->getContext())) { - inst->setMetadata(llvm::LLVMContext::MD_invariant_load, llvm::MDNode::get(md->getContext(), None)); + inst->setMetadata(llvm::LLVMContext::MD_invariant_load, llvm::MDNode::get(md->getContext(), std::nullopt)); } return inst; } @@ -245,11 +231,7 @@ static inline void emit_gc_safepoint(llvm::IRBuilder<> &builder, llvm::Type *T_s if (!F) { FunctionType *FT = FunctionType::get(Type::getVoidTy(C), {T_size->getPointerTo()}, false); F = Function::Create(FT, Function::ExternalLinkage, "julia.safepoint", M); -#if JL_LLVM_VERSION >= 160000 F->setMemoryEffects(MemoryEffects::inaccessibleOrArgMemOnly()); -#else - F->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly); -#endif } builder.CreateCall(F, {signal_page}); } diff --git a/src/llvm-cpufeatures.cpp b/src/llvm-cpufeatures.cpp index 05d62adc57926..a6e963664b0f3 100644 --- a/src/llvm-cpufeatures.cpp +++ b/src/llvm-cpufeatures.cpp @@ -37,7 +37,7 @@ STATISTIC(LoweredWithoutFMA, "Number of have_fma's that were lowered to false"); extern JuliaOJIT *jl_ExecutionEngine; // whether this platform unconditionally (i.e. without needing multiversioning) supports FMA -Optional always_have_fma(Function &intr, const Triple &TT) JL_NOTSAFEPOINT { +std::optional always_have_fma(Function &intr, const Triple &TT) JL_NOTSAFEPOINT { if (TT.isAArch64()) { auto intr_name = intr.getName(); auto typ = intr_name.substr(strlen("julia.cpu.have_fma.")); diff --git a/src/llvm-julia-licm.cpp b/src/llvm-julia-licm.cpp index 8d80f7fd54877..baf844dffa89c 100644 --- a/src/llvm-julia-licm.cpp +++ b/src/llvm-julia-licm.cpp @@ -342,11 +342,7 @@ struct JuliaLICM : public JuliaPassContext { } } if (changed && SE) { -#if JL_LLVM_VERSION >= 160000 SE->forgetLoopDispositions(); -#else - SE->forgetLoopDispositions(L); -#endif } #ifdef JL_VERIFY_PASSES assert(!verifyLLVMIR(*L)); diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index 3e372ec9884e7..ff2cac6e49406 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -1163,9 +1163,6 @@ State LateLowerGCFrame::LocalScan(Function &F) { } if (CI->hasStructRetAttr()) { Type *ElT = getAttributeAtIndex(CI->getAttributes(), 1, Attribute::StructRet).getValueAsType(); - #if JL_LLVM_VERSION < 170000 - assert(cast(CI->getArgOperand(0)->getType())->isOpaqueOrPointeeTypeMatches(getAttributeAtIndex(CI->getAttributes(), 1, Attribute::StructRet).getValueAsType())); - #endif auto tracked = CountTrackedPointers(ElT, true); if (tracked.count) { AllocaInst *SRet = dyn_cast((CI->arg_begin()[0])->stripInBoundsOffsets()); @@ -1252,38 +1249,20 @@ State LateLowerGCFrame::LocalScan(Function &F) { callee->getName() == "memcmp") { continue; } -#if JL_LLVM_VERSION >= 160000 if (callee->getMemoryEffects().onlyReadsMemory() || callee->getMemoryEffects().onlyAccessesArgPointees()) { continue; } -#else - if (callee->hasFnAttribute(Attribute::ReadNone) || - callee->hasFnAttribute(Attribute::ReadOnly) || - callee->hasFnAttribute(Attribute::ArgMemOnly)) { - continue; - } -#endif if (MemTransferInst *MI = dyn_cast(CI)) { MaybeTrackDst(S, MI); } } -#if JL_LLVM_VERSION >= 160000 if (isa(CI) || CI->getMemoryEffects().onlyAccessesArgPointees() || CI->getMemoryEffects().onlyReadsMemory()) { // Intrinsics are never safepoints. continue; } -#else - if (isa(CI) || - CI->hasFnAttr(Attribute::ArgMemOnly) || - CI->hasFnAttr(Attribute::ReadNone) || - CI->hasFnAttr(Attribute::ReadOnly)) { - // Intrinsics are never safepoints. - continue; - } -#endif SmallVector CalleeRoots; for (Use &U : CI->args()) { // Find all callee rooted arguments. @@ -1944,28 +1923,19 @@ void LateLowerGCFrame::CleanupWriteBarriers(Function &F, State *S, const SmallVe if (CFGModified) { *CFGModified = true; } - auto DebugInfoMeta = F.getParent()->getModuleFlag("julia.debug_level"); - int debug_info = 1; - if (DebugInfoMeta != nullptr) { - debug_info = cast(cast(DebugInfoMeta)->getValue())->getZExtValue(); - } IRBuilder<> builder(CI); builder.SetCurrentDebugLocation(CI->getDebugLoc()); - auto parBits = builder.CreateAnd(EmitLoadTag(builder, T_size, parent), GC_OLD_MARKED); - setName(parBits, "parent_bits", debug_info); - auto parOldMarked = builder.CreateICmpEQ(parBits, ConstantInt::get(T_size, GC_OLD_MARKED)); - setName(parOldMarked, "parent_old_marked", debug_info); + auto parBits = builder.CreateAnd(EmitLoadTag(builder, T_size, parent), GC_OLD_MARKED, "parent_bits"); + auto parOldMarked = builder.CreateICmpEQ(parBits, ConstantInt::get(T_size, GC_OLD_MARKED), "parent_old_marked"); auto mayTrigTerm = SplitBlockAndInsertIfThen(parOldMarked, CI, false); builder.SetInsertPoint(mayTrigTerm); - setName(mayTrigTerm->getParent(), "may_trigger_wb", debug_info); + mayTrigTerm->getParent()->setName("may_trigger_wb"); Value *anyChldNotMarked = NULL; for (unsigned i = 1; i < CI->arg_size(); i++) { Value *child = CI->getArgOperand(i); - Value *chldBit = builder.CreateAnd(EmitLoadTag(builder, T_size, child), GC_MARKED); - setName(chldBit, "child_bit", debug_info); - Value *chldNotMarked = builder.CreateICmpEQ(chldBit, ConstantInt::get(T_size, 0),"child_not_marked"); - setName(chldNotMarked, "child_not_marked", debug_info); + Value *chldBit = builder.CreateAnd(EmitLoadTag(builder, T_size, child), GC_MARKED, "child_bit"); + Value *chldNotMarked = builder.CreateICmpEQ(chldBit, ConstantInt::get(T_size, 0), "child_not_marked"); anyChldNotMarked = anyChldNotMarked ? builder.CreateOr(anyChldNotMarked, chldNotMarked) : chldNotMarked; } assert(anyChldNotMarked); // handled by all_of test above @@ -1973,7 +1943,7 @@ void LateLowerGCFrame::CleanupWriteBarriers(Function &F, State *S, const SmallVe SmallVector Weights{1, 9}; auto trigTerm = SplitBlockAndInsertIfThen(anyChldNotMarked, mayTrigTerm, false, MDB.createBranchWeights(Weights)); - setName(trigTerm->getParent(), "trigger_wb", debug_info); + trigTerm->getParent()->setName("trigger_wb"); builder.SetInsertPoint(trigTerm); if (CI->getCalledOperand() == write_barrier_func) { builder.CreateCall(getOrDeclare(jl_intrinsics::queueGCRoot), parent); diff --git a/src/llvm-lower-handlers.cpp b/src/llvm-lower-handlers.cpp index e09ea892ee488..c359bf6c117ce 100644 --- a/src/llvm-lower-handlers.cpp +++ b/src/llvm-lower-handlers.cpp @@ -8,11 +8,7 @@ #include #include -#if JL_LLVM_VERSION >= 170000 #include -#else -#include -#endif #include #include #include diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 22ef973decfe9..a76d076ebd6f3 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -15,11 +15,7 @@ #include #include #include -#if JL_LLVM_VERSION >= 170000 #include -#else -#include -#endif #include #include #include @@ -51,7 +47,7 @@ using namespace llvm; -extern Optional always_have_fma(Function&, const Triple &TT); +extern std::optional always_have_fma(Function&, const Triple &TT); namespace { constexpr uint32_t clone_mask = @@ -185,7 +181,7 @@ struct TargetSpec { } }; -static Optional> get_target_specs(Module &M) { +static std::optional> get_target_specs(Module &M) { auto md = M.getModuleFlag("julia.mv.specs"); if (!md) return None; diff --git a/src/llvm-pass-helpers.cpp b/src/llvm-pass-helpers.cpp index cc6c73161968d..ca25251040fb2 100644 --- a/src/llvm-pass-helpers.cpp +++ b/src/llvm-pass-helpers.cpp @@ -129,9 +129,7 @@ namespace jl_intrinsics { static Function *addGCAllocAttributes(Function *target) { auto FnAttrs = AttrBuilder(target->getContext()); -#if JL_LLVM_VERSION >= 160000 FnAttrs.addMemoryAttr(MemoryEffects::argMemOnly(ModRefInfo::Ref) | MemoryEffects::inaccessibleMemOnly(ModRefInfo::ModRef)); -#endif FnAttrs.addAllocKindAttr(AllocFnKind::Alloc); FnAttrs.addAttribute(Attribute::WillReturn); FnAttrs.addAttribute(Attribute::NoUnwind); @@ -228,11 +226,7 @@ namespace jl_intrinsics { false), Function::ExternalLinkage, QUEUE_GC_ROOT_NAME); -#if JL_LLVM_VERSION >= 160000 intrinsic->setMemoryEffects(MemoryEffects::inaccessibleOrArgMemOnly()); -#else - intrinsic->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly); -#endif return intrinsic; }); @@ -248,11 +242,7 @@ namespace jl_intrinsics { false), Function::ExternalLinkage, SAFEPOINT_NAME); -#if JL_LLVM_VERSION >= 160000 intrinsic->setMemoryEffects(MemoryEffects::inaccessibleOrArgMemOnly()); -#else - intrinsic->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly); -#endif return intrinsic; }); } @@ -309,11 +299,7 @@ namespace jl_well_known { false), Function::ExternalLinkage, GC_QUEUE_ROOT_NAME); -#if JL_LLVM_VERSION >= 160000 func->setMemoryEffects(MemoryEffects::inaccessibleOrArgMemOnly()); -#else - func->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly); -#endif return func; }); @@ -335,10 +321,3 @@ namespace jl_well_known { return addGCAllocAttributes(allocTypedFunc); }); } - -void setName(llvm::Value *V, const llvm::Twine &Name, int debug_info) -{ - if (debug_info >= 2 && !llvm::isa(V)) { - V->setName(Name); - } -} diff --git a/src/llvm-propagate-addrspaces.cpp b/src/llvm-propagate-addrspaces.cpp index 4e5a2ee5e0d54..06a52ad3dcb43 100644 --- a/src/llvm-propagate-addrspaces.cpp +++ b/src/llvm-propagate-addrspaces.cpp @@ -163,22 +163,14 @@ Value *PropagateJuliaAddrspacesVisitor::LiftPointer(Module *M, Value *V, Instruc Instruction *InstV = cast(V); Instruction *NewV = InstV->clone(); ToInsert.push_back(std::make_pair(NewV, InstV)); - #if JL_LLVM_VERSION >= 170000 Type *NewRetTy = PointerType::get(InstV->getType(), allocaAddressSpace); - #else - Type *NewRetTy = PointerType::getWithSamePointeeType(cast(InstV->getType()), allocaAddressSpace); - #endif NewV->mutateType(NewRetTy); LiftingMap[InstV] = NewV; ToRevisit.push_back(NewV); } } auto CollapseCastsAndLift = [&](Value *CurrentV, Instruction *InsertPt) -> Value * { - #if JL_LLVM_VERSION >= 170000 PointerType *TargetType = PointerType::get(CurrentV->getType(), allocaAddressSpace); - #else - PointerType *TargetType = PointerType::getWithSamePointeeType(cast(CurrentV->getType()), allocaAddressSpace); - #endif while (!LiftingMap.count(CurrentV)) { if (isa(CurrentV)) CurrentV = cast(CurrentV)->getOperand(0); @@ -192,17 +184,7 @@ Value *PropagateJuliaAddrspacesVisitor::LiftPointer(Module *M, Value *V, Instruc } if (LiftingMap.count(CurrentV)) CurrentV = LiftingMap[CurrentV]; - #if JL_LLVM_VERSION >= 170000 assert(CurrentV->getType() == TargetType); - #else - if (CurrentV->getType() != TargetType) { - // Shouldn't get here when using opaque pointers, so the new BitCastInst is fine - assert(CurrentV->getContext().supportsTypedPointers()); - auto *BCI = new BitCastInst(CurrentV, TargetType); - ToInsert.push_back(std::make_pair(BCI, InsertPt)); - CurrentV = BCI; - } - #endif return CurrentV; }; diff --git a/src/llvm-ptls.cpp b/src/llvm-ptls.cpp index 614ed15f840e6..e36136859517a 100644 --- a/src/llvm-ptls.cpp +++ b/src/llvm-ptls.cpp @@ -9,11 +9,7 @@ #include #include -#if JL_LLVM_VERSION >= 170000 #include -#else -#include -#endif #include #include #include @@ -67,11 +63,7 @@ struct LowerPTLS { void LowerPTLS::set_pgcstack_attrs(CallInst *pgcstack) const { -#if JL_LLVM_VERSION >= 160000 pgcstack->addFnAttr(Attribute::getWithMemoryEffects(pgcstack->getContext(), MemoryEffects::none())); -#else - addFnAttr(pgcstack, Attribute::ReadNone); -#endif addFnAttr(pgcstack, Attribute::NoUnwind); } diff --git a/src/llvm-remove-addrspaces.cpp b/src/llvm-remove-addrspaces.cpp index 89ae1d292d108..bb492f467e74c 100644 --- a/src/llvm-remove-addrspaces.cpp +++ b/src/llvm-remove-addrspaces.cpp @@ -44,19 +44,7 @@ class AddrspaceRemoveTypeRemapper : public ValueMapTypeRemapper { DstTy = SrcTy; if (auto Ty = dyn_cast(SrcTy)) { - #if JL_LLVM_VERSION >= 170000 DstTy = PointerType::get(Ty->getContext(), ASRemapper(Ty->getAddressSpace())); - #else - if (Ty->isOpaque()) { - DstTy = PointerType::get(Ty->getContext(), ASRemapper(Ty->getAddressSpace())); - } - else { - //Remove once opaque pointer transition is complete - DstTy = PointerType::get( - remapType(Ty->getNonOpaquePointerElementType()), - ASRemapper(Ty->getAddressSpace())); - } - #endif } else if (auto Ty = dyn_cast(SrcTy)) { SmallVector Params; @@ -157,24 +145,8 @@ class AddrspaceRemoveValueMaterializer : public ValueMaterializer { Ops.push_back(NewOp ? cast(NewOp) : Op); } - #if JL_LLVM_VERSION >= 170000 if (CE->getOpcode() != Instruction::GetElementPtr) DstV = CE->getWithOperands(Ops, Ty); - #else - if (CE->getOpcode() == Instruction::GetElementPtr) { - // GEP const exprs need to know the type of the source. - // asserts remapType(typeof arg0) == typeof mapValue(arg0). - Constant *Src = CE->getOperand(0); - auto ptrty = cast(Src->getType()->getScalarType()); - //Remove once opaque pointer transition is complete - if (!ptrty->isOpaque()) { - Type *SrcTy = remapType(ptrty->getNonOpaquePointerElementType()); - DstV = CE->getWithOperands(Ops, Ty, false, SrcTy); - } - } - else - DstV = CE->getWithOperands(Ops, Ty); - #endif } } diff --git a/src/llvm-simdloop.cpp b/src/llvm-simdloop.cpp index e12b30e3db466..3faa9d9728e67 100644 --- a/src/llvm-simdloop.cpp +++ b/src/llvm-simdloop.cpp @@ -297,11 +297,7 @@ static bool processLoop(Loop &L, OptimizationRemarkEmitter &ORE, ScalarEvolution } if (SE) -#if JL_LLVM_VERSION >= 160000 SE->forgetLoopDispositions(); -#else - SE->forgetLoopDispositions(&L); -#endif } #ifdef JL_VERIFY_PASSES diff --git a/src/llvm-version.h b/src/llvm-version.h index 984e918d480cc..061d80deb02f9 100644 --- a/src/llvm-version.h +++ b/src/llvm-version.h @@ -10,12 +10,8 @@ #define JL_LLVM_VERSION (LLVM_VERSION_MAJOR * 10000 + LLVM_VERSION_MINOR * 100 \ + LLVM_VERSION_PATCH) -#if JL_LLVM_VERSION < 150000 - #error Only LLVM versions >= 15.0.0 are supported by Julia -#endif - -#if JL_LLVM_VERSION >= 160000 - #define JL_LLVM_OPAQUE_POINTERS 1 +#if JL_LLVM_VERSION < 170000 + #error Only LLVM versions >= 17.0.0 are supported by Julia #endif #if JL_LLVM_VERSION < 19000 && defined(_CPU_RISCV64_) diff --git a/src/mtarraylist.c b/src/mtarraylist.c index 7af265a86ab63..0a0f3fe867e39 100644 --- a/src/mtarraylist.c +++ b/src/mtarraylist.c @@ -14,8 +14,8 @@ extern "C" { // but there can be any number of observers typedef struct { - _Atomic(uint32_t) len; - uint32_t max; + _Atomic(size_t) len; + size_t max; _Atomic(_Atomic(void*)*) items; _Atomic(void*) _space[SMALL_AL_N_INLINE]; } small_mtarraylist_t; diff --git a/src/pipeline.cpp b/src/pipeline.cpp index f8976099ee53c..8c9054c0d65ff 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -150,15 +150,9 @@ namespace { // Opts.UseAfterReturn = CodeGenOpts.getSanitizeAddressUseAfterReturn(); // MPM.addPass(RequireAnalysisPass()); //Let's assume the defaults are actually fine for our purposes - #if JL_LLVM_VERSION < 160000 - // MPM.addPass(ModuleAddressSanitizerPass( - // Opts, UseGlobalGC, UseOdrIndicator, DestructorKind)); - MPM.addPass(ModuleAddressSanitizerPass(AddressSanitizerOptions())); - #else // LLVM 16+ // MPM.addPass(AddressSanitizerPass( // Opts, UseGlobalGC, UseOdrIndicator, DestructorKind)); MPM.addPass(AddressSanitizerPass(AddressSanitizerOptions(), true, false)); - #endif // } }; ASanPass(/*SanitizerKind::Address, */false); @@ -347,12 +341,8 @@ static void buildEarlySimplificationPipeline(ModulePassManager &MPM, PassBuilder FPM.addPass(DCEPass()); FPM.addPass(SimplifyCFGPass(basicSimplifyCFGOptions())); if (O.getSpeedupLevel() >= 1) { -#if JL_LLVM_VERSION >= 160000 // TODO check the LLVM 15 default. FPM.addPass(SROAPass(SROAOptions::PreserveCFG)); -#else - FPM.addPass(SROAPass()); -#endif } MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); } @@ -389,12 +379,8 @@ static void buildEarlyOptimizerPipeline(ModulePassManager &MPM, PassBuilder *PB, if (O.getSpeedupLevel() >= 1) { FunctionPassManager FPM; if (O.getSpeedupLevel() >= 2) { -#if JL_LLVM_VERSION >= 160000 // TODO check the LLVM 15 default. FPM.addPass(SROAPass(SROAOptions::PreserveCFG)); -#else - FPM.addPass(SROAPass()); -#endif // SROA can duplicate PHI nodes which can block LowerSIMD FPM.addPass(InstCombinePass()); FPM.addPass(JumpThreadingPass()); @@ -468,12 +454,8 @@ static void buildScalarOptimizerPipeline(FunctionPassManager &FPM, PassBuilder * if (options.enable_scalar_optimizations) { if (O.getSpeedupLevel() >= 2) { JULIA_PASS(FPM.addPass(AllocOptPass())); - #if JL_LLVM_VERSION >= 160000 // TODO check the LLVM 15 default. FPM.addPass(SROAPass(SROAOptions::PreserveCFG)); - #else - FPM.addPass(SROAPass()); - #endif FPM.addPass(InstSimplifyPass()); FPM.addPass(GVNPass()); FPM.addPass(MemCpyOptPass()); @@ -737,12 +719,7 @@ void NewPM::run(Module &M) { //We must recreate the analysis managers every time //so that analyses from previous runs of the pass manager //do not hang around for the next run -#if JL_LLVM_VERSION >= 160000 StandardInstrumentations SI(M.getContext(),false); -#else - StandardInstrumentations SI(false); -#endif -#if JL_LLVM_VERSION >= 170000 PassInstrumentationCallbacks PIC; adjustPIC(PIC); TimePasses.registerCallbacks(PIC); @@ -752,17 +729,6 @@ void NewPM::run(Module &M) { ModuleAnalysisManager MAM; SI.registerCallbacks(PIC, &MAM); SI.getTimePasses().setOutStream(nulls()); //TODO: figure out a better way of doing this -#else - FunctionAnalysisManager FAM(createFAM(O, *TM.get())); - PassInstrumentationCallbacks PIC; - adjustPIC(PIC); - TimePasses.registerCallbacks(PIC); - SI.registerCallbacks(PIC, &FAM); - SI.getTimePasses().setOutStream(nulls()); //TODO: figure out a better way of doing this - LoopAnalysisManager LAM; - CGSCCAnalysisManager CGAM; - ModuleAnalysisManager MAM; -#endif PassBuilder PB(TM.get(), PipelineTuningOptions(), None, &PIC); PB.registerLoopAnalyses(LAM); PB.registerFunctionAnalyses(FAM); @@ -794,7 +760,7 @@ OptimizationLevel getOptLevel(int optlevel) { } //This part is also basically stolen from LLVM's PassBuilder.cpp file -static Optional> parseJuliaPipelineOptions(StringRef name) { +static std::optional> parseJuliaPipelineOptions(StringRef name) { if (name.consume_front("julia")) { auto O = OptimizationLevel::O2; auto options = OptimizationOptions::defaults(); diff --git a/src/precompile_utils.c b/src/precompile_utils.c index 81c60ba70d29f..4717351d499b0 100644 --- a/src/precompile_utils.c +++ b/src/precompile_utils.c @@ -264,10 +264,8 @@ static void *jl_precompile_(jl_array_t *m, int external_linkage) jl_value_t *item = jl_array_ptr_ref(m, i); if (jl_is_method_instance(item)) { mi = (jl_method_instance_t*)item; - size_t min_world = 0; - size_t max_world = ~(size_t)0; if (mi != jl_atomic_load_relaxed(&mi->def.method->unspecialized) && !jl_isa_compileable_sig((jl_tupletype_t*)mi->specTypes, mi->sparam_vals, mi->def.method)) - mi = jl_get_specialization1((jl_tupletype_t*)mi->specTypes, jl_atomic_load_acquire(&jl_world_counter), &min_world, &max_world, 0); + mi = jl_get_specialization1((jl_tupletype_t*)mi->specTypes, jl_atomic_load_acquire(&jl_world_counter), 0); if (mi) jl_array_ptr_1d_push(m2, (jl_value_t*)mi); } diff --git a/src/processor.cpp b/src/processor.cpp index bc12f5b54be19..3edebcc2f3ae6 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -7,11 +7,7 @@ #include #include #include -#if JL_LLVM_VERSION >= 170000 #include -#else -#include -#endif #include #include @@ -162,11 +158,7 @@ struct FeatureList { { int cnt = 0; for (size_t i = 0; i < n; i++) - #if JL_LLVM_VERSION >= 170000 cnt += llvm::popcount(eles[i]); - #else - cnt += llvm::countPopulation(eles[i]); - #endif return cnt; } inline bool empty() const diff --git a/src/rtutils.c b/src/rtutils.c index fcc4a489d3f38..00a5b639d8683 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -157,6 +157,13 @@ JL_DLLEXPORT void JL_NORETURN jl_has_no_field_error(jl_datatype_t *t, jl_sym_t * jl_throw(jl_new_struct(jl_fielderror_type, t, var)); } +JL_DLLEXPORT void JL_NORETURN jl_argument_error(char *str) // == jl_exceptionf(jl_argumenterror_type, "%s", str) +{ + jl_value_t *msg = jl_pchar_to_string((char*)str, strlen(str)); + JL_GC_PUSH1(&msg); + jl_throw(jl_new_struct(jl_argumenterror_type, msg)); +} + JL_DLLEXPORT void JL_NORETURN jl_atomic_error(char *str) // == jl_exceptionf(jl_atomicerror_type, "%s", str) { jl_value_t *msg = jl_pchar_to_string((char*)str, strlen(str)); diff --git a/src/runtime_ccall.cpp b/src/runtime_ccall.cpp index 56f8487d8fb73..2a6cb00961594 100644 --- a/src/runtime_ccall.cpp +++ b/src/runtime_ccall.cpp @@ -4,11 +4,7 @@ #include #include #include -#if JL_LLVM_VERSION >= 170000 #include -#else -#include -#endif #include #include "julia.h" diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index 450096eef5b01..80281b733bf44 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -1574,7 +1574,6 @@ bi_iintrinsic_cnvtb_fast(LLVMAShr, ashr_op, ashr_int, , 1) //un_iintrinsic_fast(LLVMByteSwap, bswap_op, bswap_int, u) un_iintrinsic_slow(LLVMByteSwap, bswap_int, u) //#define ctpop_op(a) __builtin_ctpop(a) -#if JL_LLVM_VERSION >= 170000 //uu_iintrinsic_fast(LLVMPopcount, ctpop_op, ctpop_int, u) uu_iintrinsic_slow(LLVMPopcount, ctpop_int, u) //#define ctlz_op(a) __builtin_ctlz(a) @@ -1583,16 +1582,6 @@ uu_iintrinsic_slow(LLVMCountl_zero, ctlz_int, u) //#define cttz_op(a) __builtin_cttz(a) //uu_iintrinsic_fast(LLVMCountr_zero, cttz_op, cttz_int, u) uu_iintrinsic_slow(LLVMCountr_zero, cttz_int, u) -#else -//uu_iintrinsic_fast(LLVMCountPopulation, ctpop_op, ctpop_int, u) -uu_iintrinsic_slow(LLVMCountPopulation, ctpop_int, u) -//#define ctlz_op(a) __builtin_ctlz(a) -//uu_iintrinsic_fast(LLVMCountLeadingZeros, ctlz_op, ctlz_int, u) -uu_iintrinsic_slow(LLVMCountLeadingZeros, ctlz_int, u) -//#define cttz_op(a) __builtin_cttz(a) -//uu_iintrinsic_fast(LLVMCountTrailingZeros, cttz_op, cttz_int, u) -uu_iintrinsic_slow(LLVMCountTrailingZeros, cttz_int, u) -#endif #define not_op(a) ~a un_iintrinsic_fast(LLVMFlipAllBits, not_op, not_int, u) diff --git a/src/serialize.h b/src/serialize.h index 3aa82a1d09a9b..549c1588073ff 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -7,69 +7,6 @@ extern "C" { #endif -#define TAG_SYMBOL 2 -#define TAG_SSAVALUE 3 -#define TAG_DATATYPE 4 -#define TAG_SLOTNUMBER 5 -#define TAG_SVEC 6 -#define TAG_ARRAY 7 -#define TAG_NULL 8 -#define TAG_EXPR 9 -#define TAG_PHINODE 10 -#define TAG_PHICNODE 11 -#define TAG_LONG_SYMBOL 12 -#define TAG_LONG_SVEC 13 -#define TAG_LONG_EXPR 14 -#define TAG_LONG_PHINODE 15 -#define TAG_LONG_PHICNODE 16 -#define TAG_METHODROOT 17 -#define TAG_EDGE 18 -#define TAG_STRING 19 -#define TAG_SHORT_INT64 20 -#define TAG_SHORT_GENERAL 21 -#define TAG_CNULL 22 -#define TAG_ARRAY1D 23 -#define TAG_SINGLETON 24 -#define TAG_MODULE 25 -#define TAG_TVAR 26 -#define TAG_METHOD_INSTANCE 27 -#define TAG_METHOD 28 -#define TAG_CODE_INSTANCE 29 -#define TAG_COMMONSYM 30 -#define TAG_NEARBYGLOBAL 31 -#define TAG_GLOBALREF 32 -#define TAG_CORE 33 -#define TAG_BASE 34 -#define TAG_BITYPENAME 35 -#define TAG_NEARBYMODULE 36 -#define TAG_INT32 37 -#define TAG_INT64 38 -#define TAG_UINT8 39 -#define TAG_VECTORTY 40 -#define TAG_PTRTY 41 -#define TAG_LONG_SSAVALUE 42 -#define TAG_LONG_METHODROOT 43 -#define TAG_LONG_EDGE 44 -#define TAG_SHORTER_INT64 45 -#define TAG_SHORT_INT32 46 -#define TAG_CALL1 47 -#define TAG_CALL2 48 -#define TAG_SHORT_BACKREF 49 -#define TAG_BACKREF 50 -#define TAG_UNIONALL 51 -#define TAG_GOTONODE 52 -#define TAG_QUOTENODE 53 -#define TAG_GENERAL 54 -#define TAG_GOTOIFNOT 55 -#define TAG_RETURNNODE 56 -#define TAG_ARGUMENT 57 -#define TAG_RELOC_METHODROOT 58 -#define TAG_BINDING 59 -#define TAG_MEMORYT 60 -#define TAG_ENTERNODE 61 - -#define LAST_TAG 61 - #define write_uint8(s, n) ios_putc((n), (s)) #define read_uint8(s) ((uint8_t)ios_getc((s))) #define write_int8(s, n) write_uint8((s), (n)) @@ -137,12 +74,6 @@ static inline uint32_t read_uint32(ios_t *s) JL_NOTSAFEPOINT #define read_uint(s) read_uint32(s) #endif - -void *jl_lookup_ser_tag(jl_value_t *v); -void *jl_lookup_common_symbol(jl_value_t *v); -jl_value_t *jl_deser_tag(uint8_t tag); -jl_value_t *jl_deser_symbol(uint8_t tag); - #ifdef __cplusplus } #endif diff --git a/src/staticdata.c b/src/staticdata.c index c2e8d7e3956ea..a0d56f3e38f54 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -89,6 +89,7 @@ External links: #include "julia_assert.h" static const size_t WORLD_AGE_REVALIDATION_SENTINEL = 0x1; +size_t jl_require_world = ~(size_t)0; #include "staticdata_utils.c" #include "precompile_utils.c" @@ -100,7 +101,7 @@ extern "C" { // TODO: put WeakRefs on the weak_refs list during deserialization // TODO: handle finalizers -#define NUM_TAGS 193 +#define NUM_TAGS 194 // 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. @@ -289,6 +290,7 @@ jl_value_t **const*const get_tags(void) { INSERT_TAG(jl_builtin_replacefield); INSERT_TAG(jl_builtin_setfieldonce); INSERT_TAG(jl_builtin_fieldtype); + INSERT_TAG(jl_builtin_memorynew); INSERT_TAG(jl_builtin_memoryref); INSERT_TAG(jl_builtin_memoryrefoffset); INSERT_TAG(jl_builtin_memoryrefget); @@ -507,7 +509,7 @@ static const jl_fptr_args_t id_to_fptrs[] = { &jl_f__call_latest, &jl_f__call_in_world, &jl_f__call_in_world_total, &jl_f_isdefined, &jl_f_tuple, &jl_f_svec, &jl_f_intrinsic_call, &jl_f_getfield, &jl_f_setfield, &jl_f_swapfield, &jl_f_modifyfield, &jl_f_setfieldonce, - &jl_f_replacefield, &jl_f_fieldtype, &jl_f_nfields, &jl_f_apply_type, + &jl_f_replacefield, &jl_f_fieldtype, &jl_f_nfields, &jl_f_apply_type, &jl_f_memorynew, &jl_f_memoryref, &jl_f_memoryrefoffset, &jl_f_memoryrefget, &jl_f_memoryref_isassigned, &jl_f_memoryrefset, &jl_f_memoryrefswap, &jl_f_memoryrefmodify, &jl_f_memoryrefreplace, &jl_f_memoryrefsetonce, &jl_f_applicable, &jl_f_invoke, &jl_f_sizeof, &jl_f__expr, &jl_f__typevar, @@ -768,6 +770,16 @@ static uintptr_t jl_fptr_id(void *fptr) return *(uintptr_t*)pbp; } +static int effects_foldable(uint32_t effects) +{ + // N.B.: This needs to be kept in sync with Core.Compiler.is_foldable(effects, true) + return ((effects & 0x7) == 0) && // is_consistent(effects) + (((effects >> 10) & 0x03) == 0) && // is_noub(effects) + (((effects >> 3) & 0x03) == 0) && // is_effect_free(effects) + ((effects >> 6) & 0x01); // is_terminates(effects) +} + + // `jl_queue_for_serialization` adds items to `serialization_order` #define jl_queue_for_serialization(s, v) jl_queue_for_serialization_((s), (jl_value_t*)(v), 1, 0) static void jl_queue_for_serialization_(jl_serializer_state *s, jl_value_t *v, int recursive, int immediate) JL_GC_DISABLED; @@ -907,8 +919,24 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ // TODO: if (ci in ci->defs->cache) record_field_change((jl_value_t**)&ci->next, NULL); } - if (jl_atomic_load_relaxed(&ci->inferred) && !is_relocatable_ci(&relocatable_ext_cis, ci)) - record_field_change((jl_value_t**)&ci->inferred, jl_nothing); + jl_value_t *inferred = jl_atomic_load_relaxed(&ci->inferred); + if (inferred && inferred != jl_nothing) { // disregard if there is nothing here to delete (e.g. builtins, unspecialized) + if (!is_relocatable_ci(&relocatable_ext_cis, ci)) + record_field_change((jl_value_t**)&ci->inferred, jl_nothing); + else if (jl_is_method(ci->def->def.method) && // don't delete toplevel code + ci->def->def.method->source) { // don't delete code from optimized opaque closures that can't be reconstructed (and builtins) + if (jl_atomic_load_relaxed(&ci->max_world) != ~(size_t)0 || // delete all code that cannot run + jl_atomic_load_relaxed(&ci->invoke) == jl_fptr_const_return) { // delete all code that just returns a constant + record_field_change((jl_value_t**)&ci->inferred, jl_nothing); + } + else if (native_functions && // don't delete any code if making a ji file + !effects_foldable(jl_atomic_load_relaxed(&ci->ipo_purity_bits)) && // don't delete code we may want for irinterp + jl_ir_inlining_cost(inferred) == UINT16_MAX) { // don't delete inlineable code + // delete the code now: if we thought it was worth keeping, it would have been converted to object code + record_field_change((jl_value_t**)&ci->inferred, jl_nothing); + } + } + } } if (immediate) // must be things that can be recursively handled, and valid as type parameters @@ -2577,6 +2605,7 @@ static void strip_specializations_(jl_method_instance_t *mi) if (inferred && inferred != jl_nothing) { if (jl_options.strip_ir) { record_field_change((jl_value_t**)&codeinst->inferred, jl_nothing); + record_field_change((jl_value_t**)&codeinst->edges, (jl_value_t*)jl_emptysvec); } else if (jl_options.strip_metadata) { jl_value_t *stripped = strip_codeinfo_meta(mi->def.method, inferred, codeinst); @@ -2678,7 +2707,6 @@ jl_genericmemory_t *jl_global_roots_list; jl_genericmemory_t *jl_global_roots_keyset; jl_mutex_t global_roots_lock; extern jl_mutex_t world_counter_lock; -extern size_t jl_require_world; jl_mutex_t precompile_field_replace_lock; jl_svec_t *precompile_field_replace JL_GLOBALLY_ROOTED; @@ -2896,10 +2924,20 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, int en = jl_gc_enable(0); if (native_functions) { - jl_get_llvm_gvs(native_functions, &gvars); - jl_get_llvm_external_fns(native_functions, &external_fns); - if (jl_options.trim) - jl_get_llvm_mis(native_functions, &MIs); + size_t num_gvars, num_external_fns; + jl_get_llvm_gvs(native_functions, &num_gvars, NULL); + arraylist_grow(&gvars, num_gvars); + jl_get_llvm_gvs(native_functions, &num_gvars, gvars.items); + jl_get_llvm_external_fns(native_functions, &num_external_fns, NULL); + arraylist_grow(&external_fns, num_external_fns); + jl_get_llvm_external_fns(native_functions, &num_external_fns, + (jl_code_instance_t *)external_fns.items); + if (jl_options.trim) { + size_t num_mis; + jl_get_llvm_mis(native_functions, &num_mis, NULL); + arraylist_grow(&MIs, num_mis); + jl_get_llvm_mis(native_functions, &num_mis, (jl_method_instance_t *)MIs.items); + } } if (jl_options.trim) { jl_rebuild_methtables(&MIs, &new_methtables); @@ -4044,16 +4082,30 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i // Add roots to methods jl_copy_roots(method_roots_list, jl_worklist_key((jl_array_t*)restored)); // Insert method extensions and handle edges + int new_methods = jl_array_nrows(extext_methods) > 0; + if (!new_methods) { + size_t i, l = jl_array_nrows(internal_methods); + for (i = 0; i < l; i++) { + jl_value_t *obj = jl_array_ptr_ref(internal_methods, i); + if (jl_is_method(obj)) { + new_methods = 1; + break; + } + } + } JL_LOCK(&world_counter_lock); - // allocate a world for the new methods, and insert them there, invalidating content as needed - size_t world = jl_atomic_load_relaxed(&jl_world_counter) + 1; - jl_activate_methods(extext_methods, internal_methods, world); - // TODO: inject new_ext_cis into caches here, so the system can see them immediately as potential candidates (before validation) - // allow users to start running in this updated world - jl_atomic_store_release(&jl_world_counter, world); - // now permit more methods to be added again + // allocate a world for the new methods, and insert them there, invalidating content as needed + size_t world = jl_atomic_load_relaxed(&jl_world_counter); + if (new_methods) + world += 1; + jl_activate_methods(extext_methods, internal_methods, world, pkgname); + // TODO: inject new_ext_cis into caches here, so the system can see them immediately as potential candidates (before validation) + // allow users to start running in this updated world + if (new_methods) + jl_atomic_store_release(&jl_world_counter, world); + // now permit more methods to be added again JL_UNLOCK(&world_counter_lock); - // but one of those immediate users is going to be our cache insertions + // but one of those immediate users is going to be our cache insertions jl_insert_backedges((jl_array_t*)edges, (jl_array_t*)new_ext_cis); // restore existing caches (needs to be last) // reinit ccallables jl_reinit_ccallable(&ccallable_list, base, pkgimage_handle); diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 32e59d7d7c641..ea0b7216155bd 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -704,10 +704,12 @@ static void jl_add_methods(jl_array_t *external) } } -static void jl_activate_methods(jl_array_t *external, jl_array_t *internal, size_t world) +extern _Atomic(int) allow_new_worlds; +static void jl_activate_methods(jl_array_t *external, jl_array_t *internal, size_t world, const char *pkgname) { size_t i, l = jl_array_nrows(internal); for (i = 0; i < l; i++) { + // allow_new_worlds doesn't matter here, since we aren't actually changing anything external jl_value_t *obj = jl_array_ptr_ref(internal, i); if (jl_typetagis(obj, jl_typemap_entry_type)) { jl_typemap_entry_t *entry = (jl_typemap_entry_t*)obj; @@ -735,11 +737,17 @@ static void jl_activate_methods(jl_array_t *external, jl_array_t *internal, size } } l = jl_array_nrows(external); - for (i = 0; i < l; i++) { - jl_typemap_entry_t *entry = (jl_typemap_entry_t*)jl_array_ptr_ref(external, i); - jl_methtable_t *mt = jl_method_get_table(entry->func.method); - assert((jl_value_t*)mt != jl_nothing); - jl_method_table_activate(mt, entry); + if (l) { + if (!jl_atomic_load_relaxed(&allow_new_worlds)) { + jl_printf(JL_STDERR, "WARNING: Method changes for %s have been disabled via a call to disable_new_worlds.\n", pkgname); + return; + } + for (i = 0; i < l; i++) { + jl_typemap_entry_t *entry = (jl_typemap_entry_t*)jl_array_ptr_ref(external, i); + jl_methtable_t *mt = jl_method_get_table(entry->func.method); + assert((jl_value_t*)mt != jl_nothing); + jl_method_table_activate(mt, entry); + } } } @@ -864,71 +872,80 @@ static int jl_verify_method(jl_code_instance_t *codeinst, size_t *minworld, size size_t depth = stack->len; *bp = (char*)HT_NOTFOUND + depth; JL_TIMING(VERIFY_IMAGE, VERIFY_Methods); - jl_value_t *loctag = NULL; - jl_value_t *sig = NULL; - jl_value_t *matches = NULL; - JL_GC_PUSH3(&loctag, &matches, &sig); jl_svec_t *callees = jl_atomic_load_relaxed(&codeinst->edges); assert(jl_is_svec((jl_value_t*)callees)); // verify current edges - for (size_t j = 0; j < jl_svec_len(callees); ) { - jl_value_t *edge = jl_svecref(callees, j); - size_t min_valid2; - size_t max_valid2; - assert(!jl_is_method(edge)); // `Method`-edge isn't allowed for the optimized one-edge format - if (jl_is_code_instance(edge)) - edge = (jl_value_t*)((jl_code_instance_t*)edge)->def; - if (jl_is_method_instance(edge)) { - jl_method_instance_t *mi = (jl_method_instance_t*)edge; - sig = jl_type_intersection(mi->def.method->sig, (jl_value_t*)mi->specTypes); // TODO: ?? - verify_call(sig, callees, j, 1, world, &min_valid2, &max_valid2, &matches); - sig = NULL; - j += 1; - } - else if (jl_is_long(edge)) { - jl_value_t *sig = jl_svecref(callees, j + 1); - size_t nedges = jl_unbox_long(edge); - verify_call(sig, callees, j + 2, nedges, world, &min_valid2, &max_valid2, &matches); - j += 2 + nedges; - edge = sig; - } - else { - jl_method_instance_t *callee = (jl_method_instance_t*)jl_svecref(callees, j + 1); - jl_method_t *meth; - if (jl_is_mtable(callee)) { - // skip the legacy edge (missing backedge) - j += 2; - continue; + if (callees == jl_emptysvec) { + // quick return: no edges to verify (though we probably shouldn't have gotten here from WORLD_AGE_REVALIDATION_SENTINEL) + } + else if (*maxworld == jl_require_world) { + // if no new worlds were allocated since serializing the base module, then no new validation is worth doing right now either + *minworld = *maxworld; + } + else { + jl_value_t *loctag = NULL; + jl_value_t *sig = NULL; + jl_value_t *matches = NULL; + JL_GC_PUSH3(&loctag, &matches, &sig); + for (size_t j = 0; j < jl_svec_len(callees); ) { + jl_value_t *edge = jl_svecref(callees, j); + size_t min_valid2; + size_t max_valid2; + assert(!jl_is_method(edge)); // `Method`-edge isn't allowed for the optimized one-edge format + if (jl_is_code_instance(edge)) + edge = (jl_value_t*)((jl_code_instance_t*)edge)->def; + if (jl_is_method_instance(edge)) { + jl_method_instance_t *mi = (jl_method_instance_t*)edge; + sig = jl_type_intersection(mi->def.method->sig, (jl_value_t*)mi->specTypes); // TODO: ?? + verify_call(sig, callees, j, 1, world, &min_valid2, &max_valid2, &matches); + sig = NULL; + j += 1; } - if (jl_is_code_instance(callee)) - callee = ((jl_code_instance_t*)callee)->def; - if (jl_is_method_instance(callee)) { - meth = callee->def.method; + else if (jl_is_long(edge)) { + jl_value_t *sig = jl_svecref(callees, j + 1); + size_t nedges = jl_unbox_long(edge); + verify_call(sig, callees, j + 2, nedges, world, &min_valid2, &max_valid2, &matches); + j += 2 + nedges; + edge = sig; } else { - assert(jl_is_method(callee)); - meth = (jl_method_t*)callee; + jl_method_instance_t *callee = (jl_method_instance_t*)jl_svecref(callees, j + 1); + jl_method_t *meth; + if (jl_is_mtable(callee)) { + // skip the legacy edge (missing backedge) + j += 2; + continue; + } + if (jl_is_code_instance(callee)) + callee = ((jl_code_instance_t*)callee)->def; + if (jl_is_method_instance(callee)) { + meth = callee->def.method; + } + else { + assert(jl_is_method(callee)); + meth = (jl_method_t*)callee; + } + verify_invokesig(edge, meth, world, &min_valid2, &max_valid2); + j += 2; } - verify_invokesig(edge, meth, world, &min_valid2, &max_valid2); - j += 2; - } - if (*minworld < min_valid2) - *minworld = min_valid2; - if (*maxworld > max_valid2) - *maxworld = max_valid2; - if (max_valid2 != ~(size_t)0 && _jl_debug_method_invalidation) { - jl_array_ptr_1d_push(_jl_debug_method_invalidation, edge); - loctag = jl_cstr_to_string("insert_backedges_callee"); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)codeinst); - jl_array_ptr_1d_push(_jl_debug_method_invalidation, matches); + if (*minworld < min_valid2) + *minworld = min_valid2; + if (*maxworld > max_valid2) + *maxworld = max_valid2; + if (max_valid2 != ~(size_t)0 && _jl_debug_method_invalidation) { + jl_array_ptr_1d_push(_jl_debug_method_invalidation, edge); + loctag = jl_cstr_to_string("insert_backedges_callee"); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)codeinst); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, matches); + } + //jl_static_show((JL_STREAM*)ios_stderr, (jl_value_t*)edge); + //ios_puts(max_valid2 == ~(size_t)0 ? "valid\n" : "INVALID\n", ios_stderr); + if (max_valid2 == 0 && !_jl_debug_method_invalidation) + break; } - //jl_static_show((JL_STREAM*)ios_stderr, (jl_value_t*)edge); - //ios_puts(max_valid2 == ~(size_t)0 ? "valid\n" : "INVALID\n", ios_stderr); - if (max_valid2 == 0 && !_jl_debug_method_invalidation) - break; + JL_GC_POP(); } - JL_GC_POP(); // verify recursive edges (if valid, or debugging) size_t cycle = depth; jl_code_instance_t *cause = codeinst; @@ -979,6 +996,7 @@ static int jl_verify_method(jl_code_instance_t *codeinst, size_t *minworld, size assert(*bp == (char*)HT_NOTFOUND + stack->len + 1); *bp = HT_NOTFOUND; if (_jl_debug_method_invalidation && *maxworld < current_world) { + jl_value_t *loctag; jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)child); loctag = jl_cstr_to_string("verify_methods"); JL_GC_PUSH1(&loctag); diff --git a/src/subtype.c b/src/subtype.c index 8de5b3514ef2f..a0b7bff4006ce 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -1660,6 +1660,42 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t return sub; } +static int equal_var(jl_tvar_t *v, jl_value_t *x, jl_stenv_t *e) +{ + assert(e->Loffset == 0); + // Theoretically bounds change would be merged for union inputs. + // But intersection is not happy as splitting helps to avoid circular env. + assert(!e->intersection || !jl_is_uniontype(x)); + jl_varbinding_t *vb = lookup(e, v); + if (e->intersection && vb != NULL && vb->lb == vb->ub && jl_is_typevar(vb->lb)) + return equal_var((jl_tvar_t *)vb->lb, x, e); + record_var_occurrence(vb, e, 2); + if (vb == NULL) + return e->ignore_free || ( + local_forall_exists_subtype(x, v->lb, e, 2, !jl_has_free_typevars(x)) && + local_forall_exists_subtype(v->ub, x, e, 0, 0)); + if (!vb->right) + return local_forall_exists_subtype(x, vb->lb, e, 2, !jl_has_free_typevars(x)) && + local_forall_exists_subtype(vb->ub, x, e, 0, 0); + if (vb->lb == x) + return var_lt(v, x, e, 0); + if (!subtype_ccheck(x, vb->ub, e)) + return 0; + jl_value_t *lb = simple_join(vb->lb, x); + JL_GC_PUSH1(&lb); + if (!e->intersection || !jl_is_typevar(lb) || !reachable_var(lb, v, e)) + vb->lb = lb; + JL_GC_POP(); + if (vb->ub == x) + return 1; + if (!subtype_ccheck(vb->lb, x, e)) + return 0; + // skip `simple_meet` here as we have proven `x <: vb->ub` + if (!e->intersection || !reachable_var(x, v, e)) + vb->ub = x; + return 1; +} + static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) { if (obviously_egal(x, y)) return 1; @@ -1690,6 +1726,12 @@ static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) } } + if (e->Loffset == 0 && jl_is_typevar(y) && jl_is_type(x) && (!e->intersection || !jl_is_uniontype(x))) { + // Fastpath for Type == TypeVar. + // Avoid duplicated `<:` check between adjacent `var_gt` and `var_lt` + return equal_var((jl_tvar_t *)y, x, e); + } + jl_saved_unionstate_t oldLunions; push_unionstate(&oldLunions, &e->Lunions); int sub = local_forall_exists_subtype(x, y, e, 2, -1); diff --git a/src/support/arraylist.h b/src/support/arraylist.h index a83bd2808756c..8d4ef61ba251c 100644 --- a/src/support/arraylist.h +++ b/src/support/arraylist.h @@ -5,7 +5,7 @@ #define AL_N_INLINE 29 -#define SMALL_AL_N_INLINE 6 +#define SMALL_AL_N_INLINE 5 #ifdef __cplusplus extern "C" { @@ -13,7 +13,7 @@ extern "C" { #include "analyzer_annotations.h" -typedef struct { +typedef struct { // 32 words size_t len; size_t max; void **items; @@ -27,9 +27,9 @@ JL_DLLEXPORT void arraylist_push(arraylist_t *a, void *elt) JL_NOTSAFEPOINT; JL_DLLEXPORT void *arraylist_pop(arraylist_t *a) JL_NOTSAFEPOINT; JL_DLLEXPORT void arraylist_grow(arraylist_t *a, size_t n) JL_NOTSAFEPOINT; -typedef struct { - uint32_t len; - uint32_t max; +typedef struct { // 8 words + size_t len; + size_t max; void **items; void *_space[SMALL_AL_N_INLINE]; } small_arraylist_t; diff --git a/src/support/win32-clang-ABI-bug/optional b/src/support/win32-clang-ABI-bug/optional index a2ecdad1e33ce..fd2f7646e1766 100644 --- a/src/support/win32-clang-ABI-bug/optional +++ b/src/support/win32-clang-ABI-bug/optional @@ -57,7 +57,6 @@ namespace optional_detail { // // The move constructible / assignable conditions emulate the remaining behavior // of std::is_trivially_copyable. -#if JL_LLVM_VERSION >= 170000 template ::value && std::is_trivially_copy_assignable::value && @@ -65,15 +64,6 @@ template ::value) && (std::is_trivially_move_assignable::value || !std::is_move_assignable::value))> -#else -template ::value && - std::is_trivially_copy_assignable::value && - (llvm::is_trivially_move_constructible::value || - !std::is_move_constructible::value) && - (std::is_trivially_move_assignable::value || - !std::is_move_assignable::value))> -#endif class OptionalStorage { union { char empty; diff --git a/src/task.c b/src/task.c index 5e1172a96a409..1a50d6fcbcf65 100644 --- a/src/task.c +++ b/src/task.c @@ -313,6 +313,13 @@ void JL_NORETURN jl_finish_task(jl_task_t *ct) { JL_PROBE_RT_FINISH_TASK(ct); JL_SIGATOMIC_BEGIN(); + if (ct->metrics_enabled) { + // [task] user_time -finished-> wait_time + assert(jl_atomic_load_relaxed(&ct->first_enqueued_at) != 0); + uint64_t now = jl_hrtime(); + jl_atomic_store_relaxed(&ct->finished_at, now); + jl_atomic_fetch_add_relaxed(&ct->running_time_ns, now - jl_atomic_load_relaxed(&ct->last_started_running_at)); + } if (jl_atomic_load_relaxed(&ct->_isexception)) jl_atomic_store_release(&ct->_state, JL_TASK_STATE_FAILED); else @@ -1146,6 +1153,11 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion t->ptls = NULL; t->world_age = ct->world_age; t->reentrant_timing = 0; + t->metrics_enabled = jl_atomic_load_relaxed(&jl_task_metrics_enabled) != 0; + jl_atomic_store_relaxed(&t->first_enqueued_at, 0); + jl_atomic_store_relaxed(&t->last_started_running_at, 0); + jl_atomic_store_relaxed(&t->running_time_ns, 0); + jl_atomic_store_relaxed(&t->finished_at, 0); jl_timing_task_init(t); if (t->ctx.copy_stack) @@ -1245,6 +1257,12 @@ CFI_NORETURN fesetenv(&ct->fenv); ct->ctx.started = 1; + if (ct->metrics_enabled) { + // [task] wait_time -started-> user_time + assert(jl_atomic_load_relaxed(&ct->first_enqueued_at) != 0); + assert(jl_atomic_load_relaxed(&ct->last_started_running_at) == 0); + jl_atomic_store_relaxed(&ct->last_started_running_at, jl_hrtime()); + } JL_PROBE_RT_START_TASK(ct); jl_timing_block_task_enter(ct, ptls, NULL); if (jl_atomic_load_relaxed(&ct->_isexception)) { @@ -1596,6 +1614,19 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) ct->ptls = ptls; ct->world_age = 1; // OK to run Julia code on this task ct->reentrant_timing = 0; + jl_atomic_store_relaxed(&ct->running_time_ns, 0); + jl_atomic_store_relaxed(&ct->finished_at, 0); + ct->metrics_enabled = jl_atomic_load_relaxed(&jl_task_metrics_enabled) != 0; + if (ct->metrics_enabled) { + // [task] created -started-> user_time + uint64_t now = jl_hrtime(); + jl_atomic_store_relaxed(&ct->first_enqueued_at, now); + jl_atomic_store_relaxed(&ct->last_started_running_at, now); + } + else { + jl_atomic_store_relaxed(&ct->first_enqueued_at, 0); + jl_atomic_store_relaxed(&ct->last_started_running_at, 0); + } ptls->root_task = ct; jl_atomic_store_relaxed(&ptls->current_task, ct); JL_GC_PROMISE_ROOTED(ct); diff --git a/src/threading.c b/src/threading.c index 8f0dfb3330885..ac9cc276d613a 100644 --- a/src/threading.c +++ b/src/threading.c @@ -49,6 +49,8 @@ JL_DLLEXPORT _Atomic(uint8_t) jl_measure_compile_time_enabled = 0; JL_DLLEXPORT _Atomic(uint64_t) jl_cumulative_compile_time = 0; JL_DLLEXPORT _Atomic(uint64_t) jl_cumulative_recompile_time = 0; +JL_DLLEXPORT _Atomic(uint8_t) jl_task_metrics_enabled = 0; + JL_DLLEXPORT void *jl_get_ptls_states(void) { // mostly deprecated: use current_task instead diff --git a/src/toplevel.c b/src/toplevel.c index 56a5f21f43661..44b503d4e1463 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -496,7 +496,7 @@ static void body_attributes(jl_array_t *body, int *has_ccall, int *has_defs, int *forced_compile = jl_has_meta(body, jl_force_compile_sym); } -size_t jl_require_world = ~(size_t)0; +extern size_t jl_require_world; static jl_module_t *call_require(jl_module_t *mod, jl_sym_t *var) JL_GLOBALLY_ROOTED { JL_TIMING(LOAD_IMAGE, LOAD_Require); @@ -1050,10 +1050,7 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val // use codegen mfunc = jl_method_instance_for_thunk(thk, m); jl_resolve_globals_in_ir((jl_array_t*)thk->code, m, NULL, 0); - // Don't infer blocks containing e.g. method definitions, since it's probably not - // worthwhile and also unsound (see #24316). - // TODO: This is still not correct since an `eval` can happen elsewhere, but it - // helps in common cases. + // Don't infer blocks containing e.g. method definitions, since it's probably not worthwhile. size_t world = jl_atomic_load_acquire(&jl_world_counter); ct->world_age = world; if (!has_defs && jl_get_module_infer(m) != 0) { @@ -1068,7 +1065,10 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val if (has_opaque) { jl_resolve_globals_in_ir((jl_array_t*)thk->code, m, NULL, 0); } + size_t world = jl_atomic_load_acquire(&jl_world_counter); + ct->world_age = world; result = jl_interpret_toplevel_thunk(m, thk); + ct->world_age = last_age; } JL_GC_POP(); @@ -1078,8 +1078,8 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val JL_DLLEXPORT jl_value_t *jl_toplevel_eval(jl_module_t *m, jl_value_t *v) { const char *filename = jl_filename; - int lieno = jl_lineno; - return jl_toplevel_eval_flex(m, v, 1, 0, &filename, &lieno); + int lineno = jl_lineno; + return jl_toplevel_eval_flex(m, v, 1, 0, &filename, &lineno); } // Check module `m` is open for `eval/include`, or throw an error. @@ -1180,14 +1180,13 @@ static jl_value_t *jl_parse_eval_all(jl_module_t *module, jl_value_t *text, jl_task_t *ct = jl_current_task; int last_lineno = jl_lineno; const char *last_filename = jl_filename; - size_t last_age = ct->world_age; int lineno = 0; jl_lineno = 0; const char *filename_str = jl_string_data(filename); jl_filename = filename_str; - int err = 0; JL_TRY { + size_t last_age = ct->world_age; ct->world_age = jl_atomic_load_acquire(&jl_world_counter); for (size_t i = 0; i < jl_expr_nargs(ast); i++) { expression = jl_exprarg(ast, i); @@ -1203,23 +1202,20 @@ static jl_value_t *jl_parse_eval_all(jl_module_t *module, jl_value_t *text, ct->world_age = jl_atomic_load_relaxed(&jl_world_counter); result = jl_toplevel_eval_flex(module, expression, 1, 1, &filename_str, &lineno); } + ct->world_age = last_age; } JL_CATCH { - result = jl_box_long(jl_lineno); // (ab)use result to root error line - err = 1; - goto finally; // skip jl_restore_excstack - } -finally: - ct->world_age = last_age; - jl_lineno = last_lineno; - jl_filename = last_filename; - if (err) { + result = jl_box_long(lineno); // (ab)use result to root error line + jl_lineno = last_lineno; + jl_filename = last_filename; if (jl_loaderror_type == NULL) jl_rethrow(); else jl_rethrow_other(jl_new_struct(jl_loaderror_type, filename, result, jl_current_exception(ct))); } + jl_lineno = last_lineno; + jl_filename = last_filename; JL_GC_POP(); return result; } diff --git a/stdlib/ArgTools.version b/stdlib/ArgTools.version index 09090a62ce0bf..914746c1a6900 100644 --- a/stdlib/ArgTools.version +++ b/stdlib/ArgTools.version @@ -1,4 +1,4 @@ ARGTOOLS_BRANCH = master -ARGTOOLS_SHA1 = 997089b9cd56404b40ff766759662e16dc1aab4b +ARGTOOLS_SHA1 = 1314758ad02ff5e9e5ca718920c6c633b467a84a ARGTOOLS_GIT_URL := https://github.com/JuliaIO/ArgTools.jl.git ARGTOOLS_TAR_URL = https://api.github.com/repos/JuliaIO/ArgTools.jl/tarball/$1 diff --git a/stdlib/Dates/src/io.jl b/stdlib/Dates/src/io.jl index 388edb693d76f..aa7019566093c 100644 --- a/stdlib/Dates/src/io.jl +++ b/stdlib/Dates/src/io.jl @@ -713,7 +713,7 @@ except that it does not truncate values longer than the width. When creating a `format` you can use any non-code characters as a separator. For example to generate the string "1996-01-15T00:00:00" you could use `format`: "yyyy-mm-ddTHH:MM:SS". Note that if you need to use a code character as a literal you can use the escape character -backslash. The string "1996y01m" can be produced with the format "yyyy\\ymm\\m". +backslash. The string "1996y01m" can be produced with the format raw"yyyy\\ymm\\m". """ function format(dt::TimeType, f::AbstractString; locale::Locale=ENGLISH) format(dt, DateFormat(f, locale)) diff --git a/stdlib/Distributed.version b/stdlib/Distributed.version index 02eac7eadf0ad..4a7ab49defed2 100644 --- a/stdlib/Distributed.version +++ b/stdlib/Distributed.version @@ -1,4 +1,4 @@ DISTRIBUTED_BRANCH = master -DISTRIBUTED_SHA1 = 6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781 +DISTRIBUTED_SHA1 = c6136853451677f1957bec20ecce13419cde3a12 DISTRIBUTED_GIT_URL := https://github.com/JuliaLang/Distributed.jl DISTRIBUTED_TAR_URL = https://api.github.com/repos/JuliaLang/Distributed.jl/tarball/$1 diff --git a/stdlib/Downloads.version b/stdlib/Downloads.version index b539771fbdb47..3d1da64bdfe11 100644 --- a/stdlib/Downloads.version +++ b/stdlib/Downloads.version @@ -1,4 +1,4 @@ DOWNLOADS_BRANCH = master -DOWNLOADS_SHA1 = 89d3c7dded535a77551e763a437a6d31e4d9bf84 +DOWNLOADS_SHA1 = afd04be8aa94204c075c8aec83fca040ebb4ff98 DOWNLOADS_GIT_URL := https://github.com/JuliaLang/Downloads.jl.git DOWNLOADS_TAR_URL = https://api.github.com/repos/JuliaLang/Downloads.jl/tarball/$1 diff --git a/stdlib/LLVMLibUnwind_jll/Project.toml b/stdlib/LLVMLibUnwind_jll/Project.toml index 0cb0fe5440066..e102af311abec 100644 --- a/stdlib/LLVMLibUnwind_jll/Project.toml +++ b/stdlib/LLVMLibUnwind_jll/Project.toml @@ -1,6 +1,6 @@ name = "LLVMLibUnwind_jll" uuid = "47c5dbc3-30ba-59ef-96a6-123e260183d9" -version = "14.0.6+0" +version = "19.1.4+0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/LazyArtifacts.version b/stdlib/LazyArtifacts.version index 4246ec3ad5d1a..f740516eb9953 100644 --- a/stdlib/LazyArtifacts.version +++ b/stdlib/LazyArtifacts.version @@ -1,4 +1,4 @@ LAZYARTIFACTS_BRANCH = main -LAZYARTIFACTS_SHA1 = e9a36338d5d0dfa4b222f4e11b446cbb7ea5836c +LAZYARTIFACTS_SHA1 = a719c0e3d68a95c6f3dc9571459428ca8761fa2c LAZYARTIFACTS_GIT_URL := https://github.com/JuliaPackaging/LazyArtifacts.jl.git LAZYARTIFACTS_TAR_URL = https://api.github.com/repos/JuliaPackaging/LazyArtifacts.jl/tarball/$1 diff --git a/stdlib/LibGit2/src/callbacks.jl b/stdlib/LibGit2/src/callbacks.jl index 043e04e0dfad6..c4156d4a44c71 100644 --- a/stdlib/LibGit2/src/callbacks.jl +++ b/stdlib/LibGit2/src/callbacks.jl @@ -43,7 +43,7 @@ end function user_abort() ensure_initialized() # Note: Potentially it could be better to just throw a Julia error. - ccall((:giterr_set_str, libgit2), Cvoid, + ccall((:git_error_set_str, libgit2), Cvoid, (Cint, Cstring), Cint(Error.Callback), "Aborting, user cancelled credential request.") return Cint(Error.EUSER) @@ -51,7 +51,7 @@ end function prompt_limit() ensure_initialized() - ccall((:giterr_set_str, libgit2), Cvoid, + ccall((:git_error_set_str, libgit2), Cvoid, (Cint, Cstring), Cint(Error.Callback), "Aborting, maximum number of prompts reached.") return Cint(Error.EAUTH) @@ -59,7 +59,7 @@ end function exhausted_abort() ensure_initialized() - ccall((:giterr_set_str, libgit2), Cvoid, + ccall((:git_error_set_str, libgit2), Cvoid, (Cint, Cstring), Cint(Error.Callback), "All authentication methods have failed.") return Cint(Error.EAUTH) @@ -339,7 +339,7 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Cvoid}}, url_ptr::Cstring, if err == 0 if p.explicit !== nothing ensure_initialized() - ccall((:giterr_set_str, libgit2), Cvoid, (Cint, Cstring), Cint(Error.Callback), + ccall((:git_error_set_str, libgit2), Cvoid, (Cint, Cstring), Cint(Error.Callback), "The explicitly provided credential is incompatible with the requested " * "authentication methods.") end diff --git a/stdlib/LibGit2/src/error.jl b/stdlib/LibGit2/src/error.jl index 1a493006ea1b5..6647d803d3193 100644 --- a/stdlib/LibGit2/src/error.jl +++ b/stdlib/LibGit2/src/error.jl @@ -19,7 +19,7 @@ export GitError EUNMERGED = Cint(-10), # merge in progress prevented op ENONFASTFORWARD = Cint(-11), # ref not fast-forwardable EINVALIDSPEC = Cint(-12), # name / ref not in valid format - EMERGECONFLICT = Cint(-13), # merge conflict prevented op + ECONFLICT = Cint(-13), # Checkout conflicts prevented operation ELOCKED = Cint(-14), # lock file prevented op EMODIFIED = Cint(-15), # ref value does not match expected EAUTH = Cint(-16), # authentication error @@ -27,6 +27,11 @@ export GitError EAPPLIED = Cint(-18), # patch/merge has already been applied EPEEL = Cint(-19), # the requested peel operation is not possible EEOF = Cint(-20), # unexpected EOF + EINVALID = Cint(-21), # Invalid operation or input + EUNCOMMITTED = Cint(-22), # Uncommitted changes in index prevented operation + EDIRECTORY = Cint(-23), # The operation is not valid for a directory + EMERGECONFLICT = Cint(-24), # A merge conflict exists and cannot continue + PASSTHROUGH = Cint(-30), # internal only ITEROVER = Cint(-31), # signals end of iteration RETRY = Cint(-32), # internal only @@ -34,7 +39,11 @@ export GitError EINDEXDIRTY = Cint(-34), # unsaved changes in the index would be overwritten EAPPLYFAIL = Cint(-35), # patch application failed EOWNER = Cint(-36), # the object is not owned by the current user - TIMEOUT = Cint(-37)) # The operation timed out + TIMEOUT = Cint(-37), # The operation timed out + EUNCHANGED = Cint(-38), # There were no changes + ENOTSUPPORTED = Cint(-39), # An option is not supported + EREADONLY = Cint(-40), # The subject is read-only +) @enum(Class, None, NoMemory, @@ -88,7 +97,7 @@ Base.show(io::IO, err::GitError) = print(io, "GitError(Code:$(err.code), Class:$ function last_error() ensure_initialized() - err = ccall((:giterr_last, libgit2), Ptr{ErrorStruct}, ()) + err = ccall((:git_error_last, libgit2), Ptr{ErrorStruct}, ()) if err != C_NULL err_obj = unsafe_load(err) err_class = Class(err_obj.class) diff --git a/stdlib/LibGit2/test/libgit2-tests.jl b/stdlib/LibGit2/test/libgit2-tests.jl index 9ab75ed1dc39b..1dfa5429368b6 100644 --- a/stdlib/LibGit2/test/libgit2-tests.jl +++ b/stdlib/LibGit2/test/libgit2-tests.jl @@ -1070,7 +1070,7 @@ mktempdir() do dir # test workaround for git_tree_walk issue # https://github.com/libgit2/libgit2/issues/4693 - ccall((:giterr_set_str, libgit2), Cvoid, (Cint, Cstring), + ccall((:git_error_set_str, libgit2), Cvoid, (Cint, Cstring), Cint(LibGit2.Error.Invalid), "previous error") try # file needs to exist in tree in order to trigger the stop walk condition diff --git a/stdlib/MozillaCACerts_jll/Project.toml b/stdlib/MozillaCACerts_jll/Project.toml index 181171a4c04c1..5df9bd5949972 100644 --- a/stdlib/MozillaCACerts_jll/Project.toml +++ b/stdlib/MozillaCACerts_jll/Project.toml @@ -1,7 +1,7 @@ name = "MozillaCACerts_jll" uuid = "14a3606d-f60d-562e-9121-12d972cd8159" # Keep in sync with `deps/libgit2.version`. -version = "2024.03.11" +version = "2024.11.26" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/stdlib/PCRE2_jll/Project.toml b/stdlib/PCRE2_jll/Project.toml index ae1fb74922d79..fee83c7ce552c 100644 --- a/stdlib/PCRE2_jll/Project.toml +++ b/stdlib/PCRE2_jll/Project.toml @@ -1,6 +1,6 @@ name = "PCRE2_jll" uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" -version = "10.43.0+1" +version = "10.44.0+0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/PCRE2_jll/test/runtests.jl b/stdlib/PCRE2_jll/test/runtests.jl index af0ed9434d2b6..21df2ec430e0e 100644 --- a/stdlib/PCRE2_jll/test/runtests.jl +++ b/stdlib/PCRE2_jll/test/runtests.jl @@ -6,5 +6,5 @@ using Test, Libdl, PCRE2_jll vstr = zeros(UInt8, 32) @test ccall((:pcre2_config_8, libpcre2_8), Cint, (UInt32, Ref{UInt8}), 11, vstr) > 0 vn = VersionNumber(split(unsafe_string(pointer(vstr)), " ")[1]) - @test vn == v"10.43.0" + @test vn == v"10.44.0" end diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 8b40c45c4366f..50105c42c642c 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 7b759d7f0af56c5ad01f2289bbad71284a556970 +PKG_SHA1 = e7c37f34293ab12051258828884755ea116b77df PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index e881a65ca6b1c..e15807f645119 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -181,7 +181,18 @@ struct EmptyHistoryProvider <: HistoryProvider end reset_state(::EmptyHistoryProvider) = nothing -complete_line(c::EmptyCompletionProvider, s; hint::Bool=false) = String[], "", true +# Before, completions were always given as strings. But at least for backslash +# completions, it's nice to see what glyphs are available in the completion preview. +# To separate between what's shown in the preview list of possible matches, and what's +# actually completed, we introduce this struct. +struct NamedCompletion + completion::String # what is actually completed, for example "\trianglecdot" + name::String # what is displayed in lists of possible completions, for example "◬ \trianglecdot" +end + +NamedCompletion(completion::String) = NamedCompletion(completion, completion) + +complete_line(c::EmptyCompletionProvider, s; hint::Bool=false) = NamedCompletion[], "", true # complete_line can be specialized for only two arguments, when the active module # doesn't matter (e.g. Pkg does this) @@ -308,6 +319,7 @@ end set_action!(s, command::Symbol) = nothing +common_prefix(completions::Vector{NamedCompletion}) = common_prefix(map(x -> x.completion, completions)) function common_prefix(completions::Vector{String}) ret = "" c1 = completions[1] @@ -330,6 +342,8 @@ end # does not restrict column length when multiple columns are used. const MULTICOLUMN_THRESHOLD = 5 +show_completions(s::PromptState, completions::Vector{NamedCompletion}) = show_completions(s, map(x -> x.name, completions)) + # Show available completions function show_completions(s::PromptState, completions::Vector{String}) # skip any lines of input after the cursor @@ -374,6 +388,18 @@ function complete_line(s::MIState) end end +# due to close coupling of the Pkg ReplExt `complete_line` can still return a vector of strings, +# so we convert those in this helper +function complete_line_named(args...; kwargs...)::Tuple{Vector{NamedCompletion},String,Bool} + result = complete_line(args...; kwargs...)::Union{Tuple{Vector{NamedCompletion},String,Bool},Tuple{Vector{String},String,Bool}} + if result isa Tuple{Vector{NamedCompletion},String,Bool} + return result + else + completions, partial, should_complete = result + return map(NamedCompletion, completions), partial, should_complete + end +end + function check_for_hint(s::MIState) st = state(s) if !options(st).hint_tab_completes || !eof(buffer(st)) @@ -383,12 +409,14 @@ function check_for_hint(s::MIState) return clear_hint(st) end - completions, partial, should_complete = try - complete_line(st.p.complete, st, s.active_module; hint = true)::Tuple{Vector{String},String,Bool} + named_completions, partial, should_complete = try + complete_line_named(st.p.complete, st, s.active_module; hint = true) catch @debug "error completing line for hint" exception=current_exceptions() return clear_hint(st) end + completions = map(x -> x.completion, named_completions) + isempty(completions) && return clear_hint(st) # Don't complete for single chars, given e.g. `x` completes to `xor` if length(partial) > 1 && should_complete @@ -425,7 +453,7 @@ function clear_hint(s::ModeState) end function complete_line(s::PromptState, repeats::Int, mod::Module; hint::Bool=false) - completions, partial, should_complete = complete_line(s.p.complete, s, mod; hint)::Tuple{Vector{String},String,Bool} + completions, partial, should_complete = complete_line_named(s.p.complete, s, mod; hint) isempty(completions) && return false if !should_complete # should_complete is false for cases where we only want to show @@ -435,7 +463,7 @@ function complete_line(s::PromptState, repeats::Int, mod::Module; hint::Bool=fal # Replace word by completion prev_pos = position(s) push_undo(s) - edit_splice!(s, (prev_pos - sizeof(partial)) => prev_pos, completions[1]) + edit_splice!(s, (prev_pos - sizeof(partial)) => prev_pos, completions[1].completion) else p = common_prefix(completions) if !isempty(p) && p != partial diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 50f610ff3b3e8..6c3f4bd4ba73a 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -843,7 +843,7 @@ function complete_line(c::REPLCompletionProvider, s::PromptState, mod::Module; h full = LineEdit.input_string(s) ret, range, should_complete = completions(full, lastindex(partial), mod, c.modifiers.shift, hint) c.modifiers = LineEdit.Modifiers() - return unique!(String[completion_text(x) for x in ret]), partial[range], should_complete + return unique!(LineEdit.NamedCompletion[named_completion(x) for x in ret]), partial[range], should_complete end function complete_line(c::ShellCompletionProvider, s::PromptState; hint::Bool=false) @@ -851,14 +851,14 @@ function complete_line(c::ShellCompletionProvider, s::PromptState; hint::Bool=fa partial = beforecursor(s.input_buffer) full = LineEdit.input_string(s) ret, range, should_complete = shell_completions(full, lastindex(partial), hint) - return unique!(String[completion_text(x) for x in ret]), partial[range], should_complete + return unique!(LineEdit.NamedCompletion[named_completion(x) for x in ret]), partial[range], should_complete end function complete_line(c::LatexCompletions, s; hint::Bool=false) partial = beforecursor(LineEdit.buffer(s)) full = LineEdit.input_string(s)::String ret, range, should_complete = bslash_completions(full, lastindex(partial), hint)[2] - return unique!(String[completion_text(x) for x in ret]), partial[range], should_complete + return unique!(LineEdit.NamedCompletion[named_completion(x) for x in ret]), partial[range], should_complete end with_repl_linfo(f, repl) = f(outstream(repl)) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 1f2c0cabbdb38..0bffb1a1015cd 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -2,7 +2,7 @@ module REPLCompletions -export completions, shell_completions, bslash_completions, completion_text +export completions, shell_completions, bslash_completions, completion_text, named_completion using Core: Const # We want to insulate the REPLCompletion module from any changes the user may @@ -13,6 +13,8 @@ using Base.Meta using Base: propertynames, something, IdSet using Base.Filesystem: _readdirx +using ..REPL.LineEdit: NamedCompletion + abstract type Completion end struct TextCompletion <: Completion @@ -57,8 +59,10 @@ struct MethodCompletion <: Completion end struct BslashCompletion <: Completion - bslash::String + completion::String # what is actually completed, for example "\trianglecdot" + name::String # what is displayed, for example "◬ \trianglecdot" end +BslashCompletion(completion::String) = BslashCompletion(completion, completion) struct ShellCompletion <: Completion text::String @@ -114,13 +118,21 @@ _completion_text(c::PackageCompletion) = c.package _completion_text(c::PropertyCompletion) = sprint(Base.show_sym, c.property) _completion_text(c::FieldCompletion) = sprint(Base.show_sym, c.field) _completion_text(c::MethodCompletion) = repr(c.method) -_completion_text(c::BslashCompletion) = c.bslash _completion_text(c::ShellCompletion) = c.text _completion_text(c::DictCompletion) = c.key _completion_text(c::KeywordArgumentCompletion) = c.kwarg*'=' completion_text(c) = _completion_text(c)::String +named_completion(c::BslashCompletion) = NamedCompletion(c.completion, c.name) + +function named_completion(c) + text = completion_text(c)::String + return NamedCompletion(text, text) +end + +named_completion_completion(c) = named_completion(c).completion::String + const Completions = Tuple{Vector{Completion}, UnitRange{Int}, Bool} function completes_global(x, name) @@ -984,12 +996,10 @@ function bslash_completions(string::String, pos::Int, hint::Bool=false) end # return possible matches; these cannot be mixed with regular # Julian completions as only latex / emoji symbols contain the leading \ - if startswith(s, "\\:") # emoji - namelist = Iterators.filter(k -> startswith(k, s), keys(emoji_symbols)) - else # latex - namelist = Iterators.filter(k -> startswith(k, s), keys(latex_symbols)) - end - return (true, (Completion[BslashCompletion(name) for name in sort!(collect(namelist))], slashpos:pos, true)) + symbol_dict = startswith(s, "\\:") ? emoji_symbols : latex_symbols + namelist = Iterators.filter(k -> startswith(k, s), keys(symbol_dict)) + completions = Completion[BslashCompletion(name, "$(symbol_dict[name]) $name") for name in sort!(collect(namelist))] + return (true, (completions, slashpos:pos, true)) end return (false, (Completion[], 0:-1, false)) end @@ -1099,7 +1109,7 @@ function complete_keyword_argument(partial::String, last_idx::Int, context_modul complete_keyval!(suggestions, last_word) end - return sort!(suggestions, by=completion_text), wordrange + return sort!(suggestions, by=named_completion_completion), wordrange end function get_loading_candidates(pkgstarts::String, project_file::String) @@ -1298,7 +1308,7 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif complete_identifiers!(suggestions, context_module, string, name, pos, separatorpos, startpos; shift) - return sort!(unique!(completion_text, suggestions), by=completion_text), (separatorpos+1):pos, true + return sort!(unique!(named_completion, suggestions), by=named_completion_completion), (separatorpos+1):pos, true elseif inc_tag === :cmd # 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 @@ -1496,7 +1506,7 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif complete_identifiers!(suggestions, context_module, string, name, pos, separatorpos, startpos; comp_keywords, complete_modules_only, shift) - return sort!(unique!(completion_text, suggestions), by=completion_text), namepos:pos, true + return sort!(unique!(named_completion, suggestions), by=named_completion_completion), namepos:pos, true end function shell_completions(string, pos, hint::Bool=false) diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index b259567884486..2c8d48cc232cf 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -170,17 +170,23 @@ end function map_completion_text(completions) c, r, res = completions - return map(completion_text, c), r, res + return map(x -> named_completion(x).completion, c), r, res +end + +function map_named_completion(completions) + c, r, res = completions + return map(named_completion, c), r, res end test_complete(s) = map_completion_text(@inferred(completions(s, lastindex(s)))) test_scomplete(s) = map_completion_text(@inferred(shell_completions(s, lastindex(s)))) -test_bslashcomplete(s) = map_completion_text(@inferred(bslash_completions(s, lastindex(s)))[2]) test_complete_context(s, m=@__MODULE__; shift::Bool=true) = map_completion_text(@inferred(completions(s,lastindex(s), m, shift))) test_complete_foo(s) = test_complete_context(s, Main.CompletionFoo) test_complete_noshift(s) = map_completion_text(@inferred(completions(s, lastindex(s), Main, false))) +test_bslashcomplete(s) = map_named_completion(@inferred(bslash_completions(s, lastindex(s)))[2]) + test_methods_list(@nospecialize(f), tt) = map(x -> string(x.method), Base._methods_by_ftype(Base.signature_type(f, tt), 10, Base.get_world_counter())) @@ -350,7 +356,8 @@ end # test latex symbol completions let s = "\\alpha" c, r = test_bslashcomplete(s) - @test c[1] == "α" + @test c[1].completion == "α" + @test c[1].name == "α" @test r == 1:lastindex(s) @test length(c) == 1 end @@ -358,7 +365,8 @@ end # test latex symbol completions after unicode #9209 let s = "α\\alpha" c, r = test_bslashcomplete(s) - @test c[1] == "α" + @test c[1].completion == "α" + @test c[1].name == "α" @test r == 3:sizeof(s) @test length(c) == 1 end @@ -366,20 +374,25 @@ end # test emoji symbol completions let s = "\\:koala:" c, r = test_bslashcomplete(s) - @test c[1] == "🐨" + @test c[1].completion == "🐨" + @test c[1].name == "🐨" @test r == 1:sizeof(s) @test length(c) == 1 end let s = "\\:ko" c, r = test_bslashcomplete(s) - @test "\\:koala:" in c + ko = only(filter(c) do namedcompletion + namedcompletion.completion == "\\:koala:" + end) + @test ko.name == "🐨 \\:koala:" end # test emoji symbol completions after unicode #9209 let s = "α\\:koala:" c, r = test_bslashcomplete(s) - @test c[1] == "🐨" + @test c[1].name == "🐨" + @test c[1].completion == "🐨" @test r == 3:sizeof(s) @test length(c) == 1 end @@ -1069,8 +1082,8 @@ let s, c, r # Issue #8047 s = "@show \"/dev/nul\"" c,r = completions(s, 15) - c = map(completion_text, c) - @test "null\"" in c + c = map(named_completion, c) + @test "null\"" in [_c.completion for _c in c] @test r == 13:15 @test s[r] == "nul" @@ -1476,7 +1489,7 @@ function test_dict_completion(dict_name) @test c == Any["\"abcd\"]"] s = "$dict_name[\"abcd]" # trailing close bracket c, r = completions(s, lastindex(s) - 1) - c = map(completion_text, c) + c = map(x -> named_completion(x).completion, c) @test c == Any["\"abcd\""] s = "$dict_name[:b" c, r = test_complete(s) diff --git a/stdlib/SHA.version b/stdlib/SHA.version index f22bb33dc7ea2..4b33964a6dcdb 100644 --- a/stdlib/SHA.version +++ b/stdlib/SHA.version @@ -1,4 +1,4 @@ SHA_BRANCH = master -SHA_SHA1 = aaf2df61ff8c3898196587a375d3cf213bd40b41 +SHA_SHA1 = 8fa221ddc8f3b418d9929084f1644f4c32c9a27e SHA_GIT_URL := https://github.com/JuliaCrypto/SHA.jl.git SHA_TAR_URL = https://api.github.com/repos/JuliaCrypto/SHA.jl/tarball/$1 diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version index af6fac41ddf84..0ff7a761bffa3 100644 --- a/stdlib/SparseArrays.version +++ b/stdlib/SparseArrays.version @@ -1,4 +1,4 @@ SPARSEARRAYS_BRANCH = main -SPARSEARRAYS_SHA1 = 1b4933ccc7b1f97427ff88bd7ba58950021f2c60 +SPARSEARRAYS_SHA1 = 4fd3aad5735e3b80eefe7b068f3407d7dd0c0924 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/Statistics.version b/stdlib/Statistics.version index 1449dcee29b79..3df70d30ba7e6 100644 --- a/stdlib/Statistics.version +++ b/stdlib/Statistics.version @@ -1,4 +1,4 @@ STATISTICS_BRANCH = master -STATISTICS_SHA1 = 68869af06e8cdeb7aba1d5259de602da7328057f +STATISTICS_SHA1 = d49c2bf4f81e1efb4980a35fe39c815ef8396297 STATISTICS_GIT_URL := https://github.com/JuliaStats/Statistics.jl.git STATISTICS_TAR_URL = https://api.github.com/repos/JuliaStats/Statistics.jl/tarball/$1 diff --git a/stdlib/StyledStrings.version b/stdlib/StyledStrings.version index 5e58a5456148a..c72f7a8399725 100644 --- a/stdlib/StyledStrings.version +++ b/stdlib/StyledStrings.version @@ -1,4 +1,4 @@ STYLEDSTRINGS_BRANCH = main -STYLEDSTRINGS_SHA1 = 056e843b2d428bb9735b03af0cff97e738ac7e14 +STYLEDSTRINGS_SHA1 = 8985a37ac054c37d084a03ad2837208244824877 STYLEDSTRINGS_GIT_URL := https://github.com/JuliaLang/StyledStrings.jl.git STYLEDSTRINGS_TAR_URL = https://api.github.com/repos/JuliaLang/StyledStrings.jl/tarball/$1 diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 7c985828d47f2..3a60527a26e11 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -1194,7 +1194,7 @@ function print_test_results(ts::AbstractTestSet, depth_pad=0) duration_width = max(textwidth("Time"), textwidth(tc.duration)) # Calculate the alignment of the test result counts by # recursively walking the tree of test sets - align = max(get_alignment(ts, 0), textwidth("Test Summary:")) + align = max(get_alignment(ts, depth_pad), textwidth("Test Summary:")) # Print the outer test set header once printstyled(rpad("Test Summary:", align, " "), " |", " "; bold=true) if pass_width > 0 @@ -1965,8 +1965,9 @@ Arguments #self#::Core.Const(f) a::Int64 Body::UNION{FLOAT64, INT64} -1 ─ %1 = (a > 1)::Bool -└── goto #3 if not %1 +1 ─ %1 = :>::Core.Const(>) +│ %2 = (%1)(a, 1)::Bool +└── goto #3 if not %2 2 ─ return 1 3 ─ return 1.0 diff --git a/sysimage.mk b/sysimage.mk index 5371fbd975025..a74aace4dd11c 100644 --- a/sysimage.mk +++ b/sysimage.mk @@ -65,14 +65,14 @@ RELDATADIR := $(call rel_path,$(JULIAHOME)/base,$(build_datarootdir))/ # <-- mak $(build_private_libdir)/basecompiler.ji: $(COMPILER_SRCS) @$(call PRINT_JULIA, cd $(JULIAHOME)/base && \ - $(call spawn,$(JULIA_EXECUTABLE)) -C "$(JULIA_CPU_TARGET)" $(HEAPLIM) --output-ji $(call cygpath_w,$@).tmp \ + JULIA_NUM_THREADS=1 $(call spawn,$(JULIA_EXECUTABLE)) -C "$(JULIA_CPU_TARGET)" $(HEAPLIM) --output-ji $(call cygpath_w,$@).tmp \ --startup-file=no --warn-overwrite=yes -g$(BOOTSTRAP_DEBUG_LEVEL) -O1 Base_compiler.jl --buildroot $(RELBUILDROOT) --dataroot $(RELDATADIR)) @mv $@.tmp $@ $(build_private_libdir)/sys.ji: $(build_private_libdir)/basecompiler.ji $(JULIAHOME)/VERSION $(BASE_SRCS) $(STDLIB_SRCS) @$(call PRINT_JULIA, cd $(JULIAHOME)/base && \ if ! JULIA_BINDIR=$(call cygpath_w,$(build_bindir)) WINEPATH="$(call cygpath_w,$(build_bindir));$$WINEPATH" \ - $(call spawn, $(JULIA_EXECUTABLE)) -g1 -O1 -C "$(JULIA_CPU_TARGET)" $(HEAPLIM) --output-ji $(call cygpath_w,$@).tmp $(JULIA_SYSIMG_BUILD_FLAGS) \ + JULIA_NUM_THREADS=1 $(call spawn, $(JULIA_EXECUTABLE)) -g1 -O1 -C "$(JULIA_CPU_TARGET)" $(HEAPLIM) --output-ji $(call cygpath_w,$@).tmp $(JULIA_SYSIMG_BUILD_FLAGS) \ --startup-file=no --warn-overwrite=yes --sysimage $(call cygpath_w,$<) sysimg.jl --buildroot $(RELBUILDROOT) --dataroot $(RELDATADIR); then \ echo '*** This error might be fixed by running `make clean`. If the error persists$(COMMA) try `make cleanall`. ***'; \ false; \ diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 2a2ec8e8e432c..c7ec61704c1bc 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -2186,3 +2186,44 @@ end copyto!(A, 1, x, 1) @test A == axes(A,1) end + +@testset "reshape with Integer sizes" begin + @test reshape(1:4, big(2), big(2)) == reshape(1:4, 2, 2) + a = [1 2 3; 4 5 6] + reshaped_arrays = ( + reshape(a, 3, 2), + reshape(a, (3, 2)), + reshape(a, big(3), big(2)), + reshape(a, (big(3), big(2))), + reshape(a, :, big(2)), + reshape(a, (:, big(2))), + reshape(a, big(3), :), + reshape(a, (big(3), :)), + ) + @test allequal(reshaped_arrays) + for b ∈ reshaped_arrays + @test b isa Matrix{Int} + @test b.ref === a.ref + end +end +@testset "AbstractArrayMath" begin + @testset "IsReal" begin + A = [1, 2, 3, 4] + @test isreal(A) == true + B = [1.1, 2.2, 3.3, 4.4] + @test isreal(B) == true + C = [1, 2.2, 3] + @test isreal(C) == true + D = Real[] + @test isreal(D) == true + E = [1 + 1im, 2 - 2im] + @test isreal(E) == false + struct MyReal <: Real + value::Float64 + end + F = [MyReal(1.0), MyReal(2.0)] + @test isreal(F) == true + G = ["a", "b", "c"] + @test_throws MethodError isreal(G) + end +end diff --git a/test/arrayops.jl b/test/arrayops.jl index ca378c3f3036b..655e14675bfb4 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -307,34 +307,40 @@ end @test_throws ArgumentError dropdims(a, dims=3) @test_throws ArgumentError dropdims(a, dims=4) @test_throws ArgumentError dropdims(a, dims=6) - - - a = rand(8, 7) - @test @inferred(insertdims(a, dims=1)) == @inferred(insertdims(a, dims=(1,))) == reshape(a, (1, 8, 7)) - @test @inferred(insertdims(a, dims=3)) == @inferred(insertdims(a, dims=(3,))) == reshape(a, (8, 7, 1)) - @test @inferred(insertdims(a, dims=(1, 3))) == reshape(a, (1, 8, 1, 7)) - @test @inferred(insertdims(a, dims=(1, 2, 3))) == reshape(a, (1, 1, 1, 8, 7)) - @test @inferred(insertdims(a, dims=(1, 4))) == reshape(a, (1, 8, 7, 1)) - @test @inferred(insertdims(a, dims=(1, 3, 5))) == reshape(a, (1, 8, 1, 7, 1)) - @test @inferred(insertdims(a, dims=(1, 2, 4, 6))) == reshape(a, (1, 1, 8, 1, 7, 1)) - @test @inferred(insertdims(a, dims=(1, 3, 4, 6))) == reshape(a, (1, 8, 1, 1, 7, 1)) - @test @inferred(insertdims(a, dims=(1, 4, 6, 3))) == reshape(a, (1, 8, 1, 1, 7, 1)) - @test @inferred(insertdims(a, dims=(1, 3, 5, 6))) == reshape(a, (1, 8, 1, 7, 1, 1)) - - @test_throws ArgumentError insertdims(a, dims=(1, 1, 2, 3)) - @test_throws ArgumentError insertdims(a, dims=(1, 2, 2, 3)) - @test_throws ArgumentError insertdims(a, dims=(1, 2, 3, 3)) - @test_throws UndefKeywordError insertdims(a) - @test_throws ArgumentError insertdims(a, dims=0) - @test_throws ArgumentError insertdims(a, dims=(1, 2, 1)) - @test_throws ArgumentError insertdims(a, dims=4) - @test_throws ArgumentError insertdims(a, dims=6) - - # insertdims and dropdims are inverses - b = rand(1,1,1,5,1,1,7) - for dims in [1, (1,), 2, (2,), 3, (3,), (1,3), (1,2,3), (1,2), (1,3,5), (1,2,5,6), (1,3,5,6), (1,3,5,6), (1,6,5,3)] - @test dropdims(insertdims(a; dims); dims) == a - @test insertdims(dropdims(b; dims); dims) == b + @testset "insertdims" begin + a = rand(8, 7) + @test @inferred(insertdims(a, dims=1)) == @inferred(insertdims(a, dims=(1,))) == reshape(a, (1, 8, 7)) + @test @inferred(insertdims(a, dims=3)) == @inferred(insertdims(a, dims=(3,))) == reshape(a, (8, 7, 1)) + @test @inferred(insertdims(a, dims=(1, 3))) == reshape(a, (1, 8, 1, 7)) + @test @inferred(insertdims(a, dims=(1, 2, 3))) == reshape(a, (1, 1, 1, 8, 7)) + @test @inferred(insertdims(a, dims=(1, 4))) == reshape(a, (1, 8, 7, 1)) + @test @inferred(insertdims(a, dims=(1, 3, 5))) == reshape(a, (1, 8, 1, 7, 1)) + @test @inferred(insertdims(a, dims=(1, 2, 4, 6))) == reshape(a, (1, 1, 8, 1, 7, 1)) + @test @inferred(insertdims(a, dims=(1, 3, 4, 6))) == reshape(a, (1, 8, 1, 1, 7, 1)) + @test @inferred(insertdims(a, dims=(1, 4, 6, 3))) == reshape(a, (1, 8, 1, 1, 7, 1)) + @test @inferred(insertdims(a, dims=(1, 3, 5, 6))) == reshape(a, (1, 8, 1, 7, 1, 1)) + @test_throws ArgumentError insertdims(a, dims=(1, 1, 2, 3)) + @test_throws ArgumentError insertdims(a, dims=(1, 2, 2, 3)) + @test_throws ArgumentError insertdims(a, dims=(1, 2, 3, 3)) + @test_throws UndefKeywordError insertdims(a) + @test_throws ArgumentError insertdims(a, dims=0) + @test_throws ArgumentError insertdims(a, dims=(1, 2, 1)) + @test_throws ArgumentError insertdims(a, dims=4) + @test_throws ArgumentError insertdims(a, dims=6) + A = reshape(1:6, 2, 3) + @test_throws ArgumentError insertdims(A, dims=(2, 2)) + D = insertdims(A, dims=()) + @test size(D) == size(A) + @test D == A + E = ones(2, 3, 4) + F = insertdims(E, dims=(2, 4, 6)) + @test size(F) == (2, 1, 3, 1, 4, 1) + # insertdims and dropdims are inverses + b = rand(1,1,1,5,1,1,7) + for dims in [1, (1,), 2, (2,), 3, (3,), (1,3), (1,2,3), (1,2), (1,3,5), (1,2,5,6), (1,3,5,6), (1,3,5,6), (1,6,5,3)] + @test dropdims(insertdims(a; dims); dims) == a + @test insertdims(dropdims(b; dims); dims) == b + end end sz = (5,8,7) diff --git a/test/bitarray.jl b/test/bitarray.jl index 67d8fae0eda6d..fd5c1421a256f 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -1,7 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license using Base: findprevnot, findnextnot -using Random, LinearAlgebra, Test +using Random, Test, LinearAlgebra # Ideally, these tests should not depend on LinearAlgebra isdefined(Main, :SizedArrays) || @eval Main include("testhelpers/SizedArrays.jl") using .Main.SizedArrays @@ -15,7 +15,6 @@ tc(r1,r2) = false bitcheck(b::BitArray) = Test._check_bitarray_consistency(b) bitcheck(x) = true -bcast_setindex!(b, x, I...) = (b[I...] .= x; b) function check_bitop_call(ret_type, func, args...; kwargs...) r2 = func(map(x->(isa(x, BitArray) ? Array(x) : x), args)...; kwargs...) @@ -34,6 +33,9 @@ macro check_bit_operation(ex) Expr(:call, :check_bitop_call, nothing, map(esc, ex.args)...) end +bcast_setindex!(b, x, I...) = (b[I...] .= x; b) + + let t0 = time_ns() global timesofar function timesofar(str) @@ -1641,69 +1643,6 @@ end timesofar("cat") -@testset "Linear algebra" begin - b1 = bitrand(v1) - b2 = bitrand(v1) - @check_bit_operation dot(b1, b2) Int - - b1 = bitrand(n1, n2) - @test_throws ArgumentError tril(b1, -n1 - 2) - @test_throws ArgumentError tril(b1, n2) - @test_throws ArgumentError triu(b1, -n1) - @test_throws ArgumentError triu(b1, n2 + 2) - for k in (-n1 - 1):(n2 - 1) - @check_bit_operation tril(b1, k) BitMatrix - end - for k in (-n1 + 1):(n2 + 1) - @check_bit_operation triu(b1, k) BitMatrix - end - - for sz = [(n1,n1), (n1,n2), (n2,n1)], (f,isf) = [(tril,istril), (triu,istriu)] - b1 = bitrand(sz...) - @check_bit_operation isf(b1) Bool - b1 = f(bitrand(sz...)) - @check_bit_operation isf(b1) Bool - end - - b1 = bitrand(n1,n1) - b1 .|= copy(b1') - @check_bit_operation issymmetric(b1) Bool - @check_bit_operation ishermitian(b1) Bool - - b1 = bitrand(n1) - b2 = bitrand(n2) - @check_bit_operation kron(b1, b2) BitVector - - b1 = bitrand(s1, s2) - b2 = bitrand(s3, s4) - @check_bit_operation kron(b1, b2) BitMatrix - - b1 = bitrand(v1) - @check_bit_operation diff(b1) Vector{Int} - - b1 = bitrand(n1, n2) - @check_bit_operation diff(b1, dims=1) Matrix{Int} - @check_bit_operation diff(b1, dims=2) Matrix{Int} - - b1 = bitrand(n1, n1) - @test ((svdb1, svdb1A) = (svd(b1), svd(Array(b1))); - svdb1.U == svdb1A.U && svdb1.S == svdb1A.S && svdb1.V == svdb1A.V) - @test ((qrb1, qrb1A) = (qr(b1), qr(Array(b1))); - Matrix(qrb1.Q) == Matrix(qrb1A.Q) && qrb1.R == qrb1A.R) - - b1 = bitrand(v1) - @check_bit_operation diagm(0 => b1) BitMatrix - - b1 = bitrand(v1) - b2 = bitrand(v1) - @check_bit_operation diagm(-1 => b1, 1 => b2) BitMatrix - - b1 = bitrand(n1, n1) - @check_bit_operation diag(b1) -end - -timesofar("linalg") - @testset "findmax, findmin" begin b1 = trues(0) @test_throws ArgumentError findmax(b1) diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index cc3f8950f0dc0..5df174694049d 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -783,6 +783,15 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` "Int(Base.JLOptions().fast_math)"`)) == JL_OPTIONS_FAST_MATH_DEFAULT end + let JL_OPTIONS_TASK_METRICS_OFF = 0, JL_OPTIONS_TASK_METRICS_ON = 1 + @test parse(Int,readchomp(`$exename -E + "Int(Base.JLOptions().task_metrics)"`)) == JL_OPTIONS_TASK_METRICS_OFF + @test parse(Int, readchomp(`$exename --task-metrics=yes -E + "Int(Base.JLOptions().task_metrics)"`)) == JL_OPTIONS_TASK_METRICS_ON + @test !parse(Bool, readchomp(`$exename -E "current_task().metrics_enabled"`)) + @test parse(Bool, readchomp(`$exename --task-metrics=yes -E "current_task().metrics_enabled"`)) + end + # --worker takes default / custom as argument (default/custom arguments # tested in test/parallel.jl) @test errors_not_signals(`$exename --worker=true`) diff --git a/test/core.jl b/test/core.jl index 836532d661638..4bbb2ca368019 100644 --- a/test/core.jl +++ b/test/core.jl @@ -25,6 +25,7 @@ for (T, c) in ( (TypeVar, [:name, :ub, :lb]), (Core.Memory, [:length, :ptr]), (Core.GenericMemoryRef, [:mem, :ptr_or_offset]), + (Task, [:metrics_enabled]), ) @test Set((fieldname(T, i) for i in 1:fieldcount(T) if isconst(T, i))) == Set(c) end @@ -42,7 +43,7 @@ for (T, c) in ( (DataType, [:types, :layout]), (Core.Memory, []), (Core.GenericMemoryRef, []), - (Task, [:_state]) + (Task, [:_state, :running_time_ns, :finished_at, :first_enqueued_at, :last_started_running_at]), ) @test Set((fieldname(T, i) for i in 1:fieldcount(T) if Base.isfieldatomic(T, i))) == Set(c) end @@ -4504,6 +4505,15 @@ for T in (Any, ValueWrapper) end end +#test grow_end ccall directly since it's used in the C source +for ET in [Nothing, Int, Union{Int, Nothing}, Any] + for n in [0, 1, 10] + arr = Vector{ET}(undef, n) + ccall(:jl_array_grow_end, Cvoid, (Any, UInt), arr, 1) + @test length(arr) == n+1 + end +end + # check if we can run multiple finalizers at the same time # Use a `@noinline` function to make sure the inefficient gc root generation # doesn't keep the object alive. @@ -7434,6 +7444,7 @@ end @test isa(Core.eval(@__MODULE__, :(Bar31062(()))), Bar31062) @test precompile(identity, (Foo31062,)) +using Core: SSAValue ftype_eval = Ref(0) FieldTypeA = String FieldTypeE = UInt32 @@ -7457,27 +7468,41 @@ let fc = FieldConvert(1.0, [2.0], 0x3, 0x4, 0x5) end @test ftype_eval[] == 1 let code = code_lowered(FieldConvert)[1].code - 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)) + calls = Vector{Pair{SSAValue, Expr}}(undef, 0) + for i = 1:length(code) + expr = code[i] + if Meta.isexpr(expr, :call) || (Meta.isexpr(expr, :(=)) && Meta.isexpr(expr.args[2], :call)) + push!(calls, SSAValue(i)=>expr) + end + end + + function is_globalref(arg, gr) + while isa(arg, SSAValue) + arg = code[arg.id] + end + arg == gr + end + + # calls[1] + @test all(is_globalref.(calls[1][2].args[1:3], (GlobalRef(Core, :apply_type), GlobalRef(@__MODULE__, :FieldConvert), GlobalRef(@__MODULE__, :FieldTypeA)))) + + # calls[2] + @test all(is_globalref.(calls[2][2].args[1:1], (GlobalRef(Core, :fieldtype),))) + @test all(calls[2][2].args[2:3] .== (calls[1][1], 1)) + + # calls[3] - isa + + # calls[4] + let calle = calls[4][2] + @test Meta.isexpr(calle, :(=)) + call = calle.args[2] + @test is_globalref(call.args[1], GlobalRef(Base, :convert)) + @test call.args[2] == calls[2][1] + end + + # calls[5] + @test all(is_globalref.(calls[5][2].args[1:1], (GlobalRef(Core, :fieldtype),))) + @test all(calls[5][2].args[2:3] .== (calls[1][1], 2)) end # Issue #32820 @@ -8155,7 +8180,7 @@ end @test Core.Compiler.is_foldable(Base.infer_effects(length, (Core.SimpleVector,))) @test Core.Compiler.is_foldable(Base.infer_effects(getindex, (Core.SimpleVector,Int))) -# Test that a nothrow-globalref doesn't get outlined during lowering +# Test that a the lowering of nothrow globalref module WellKnownGlobal global well_known = 1 end @@ -8164,7 +8189,7 @@ macro insert_global() end check_globalref_lowering() = @insert_global let src = code_lowered(check_globalref_lowering)[1] - @test length(src.code) == 2 + @test length(src.code) == 4 end # Test correctness of widen_diagonal @@ -8352,3 +8377,24 @@ macro define_call(sym) end @test eval(Expr(:toplevel, :(@define_call(f_macro_defined1)))) == 1 @test @define_call(f_macro_defined2) == 1 + +# `invoke` of `Method` +let m = which(+, (Int, Int)) + @eval f56692(i) = invoke(+, $m, i, 4) + global g56692() = f56692(5) == 9 ? "true" : false +end +@test @inferred(f56692(3)) == 7 +@test @inferred(g56692()) == "true" + +# `invoke` of `CodeInstance` +f_invalidate_me() = return 1 +f_invoke_me() = return f_invalidate_me() +@test f_invoke_me() == 1 +const f_invoke_me_ci = Base.specialize_method(Base._which(Tuple{typeof(f_invoke_me)})).cache +f_call_me() = invoke(f_invoke_me, f_invoke_me_ci) +@test invoke(f_invoke_me, f_invoke_me_ci) == 1 +@test f_call_me() == 1 +@test_throws TypeError invoke(f_invoke_me, f_invoke_me_ci, 1) +f_invalidate_me() = 2 +@test_throws ErrorException invoke(f_invoke_me, f_invoke_me_ci) +@test_throws ErrorException f_call_me() diff --git a/test/dict.jl b/test/dict.jl index 909afb3607907..83d35ae18bb85 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -787,6 +787,13 @@ end [v for (k, v) in d] == [d[x[1]] for (i, x) in enumerate(d)] end +@testset "consistency of dict iteration order (issue #56841)" begin + dict = Dict(randn() => randn() for _ = 1:100) + @test all(zip(dict, keys(dict), values(dict), pairs(dict))) do (d, k, v, p) + d == p && first(d) == first(p) == k && last(d) == last(p) == v + end +end + @testset "generators, similar" begin d = Dict(:a=>"a") # TODO: restore when 0.7 deprecation is removed diff --git a/test/errorshow.jl b/test/errorshow.jl index 7a3d50d599f2e..f83bbe31b7cc4 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -791,8 +791,22 @@ backtrace() @test occursin("g28442", output[3]) @test lstrip(output[5])[1:3] == "[2]" @test occursin("f28442", output[5]) - @test occursin("the above 2 lines are repeated 5000 more times", output[7]) - @test lstrip(output[8])[1:7] == "[10003]" + is_windows_32_bit = Sys.iswindows() && (Sys.WORD_SIZE == 32) + if is_windows_32_bit + # These tests are currently broken (intermittently/non-determistically) on 32-bit Windows. + # https://github.com/JuliaLang/julia/issues/55900 + # Instead of skipping them entirely, we skip one, and we loosen the other. + + # Broken test: @test occursin("the above 2 lines are repeated 5000 more times", output[7]) + @test occursin("the above 2 lines are repeated ", output[7]) + @test occursin(" more times", output[7]) + + # Broken test: @test lstrip(output[8])[1:7] == "[10003]" + @test_broken false + else + @test occursin("the above 2 lines are repeated 5000 more times", output[7]) + @test lstrip(output[8])[1:7] == "[10003]" + end end @testset "Line number correction" begin diff --git a/test/iterators.jl b/test/iterators.jl index d1e7525c43465..1feccf5fb1d3e 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -515,6 +515,14 @@ end @test eltype(flatten(UnitRange{Int8}[1:2, 3:4])) == Int8 @test eltype(flatten(([1, 2], [3.0, 4.0]))) == Real @test eltype(flatten((a = [1, 2], b = Int8[3, 4]))) == Signed +@test eltype(flatten((Int[], Nothing[], Int[]))) == Union{Int, Nothing} +@test eltype(flatten((String[],))) == String +@test eltype(flatten((Int[], UInt[], Int8[],))) == Integer +@test eltype(flatten((; a = Int[], b = Nothing[], c = Int[]))) == Union{Int, Nothing} +@test eltype(flatten((; a = String[],))) == String +@test eltype(flatten((; a = Int[], b = UInt[], c = Int8[],))) == Integer +@test eltype(flatten(())) == Union{} +@test eltype(flatten((;))) == Union{} @test length(flatten(zip(1:3, 4:6))) == 6 @test length(flatten(1:6)) == 6 @test collect(flatten(Any[])) == Any[] diff --git a/test/llvmcall.jl b/test/llvmcall.jl index c83ac05b1ec48..ddf66ca680d45 100644 --- a/test/llvmcall.jl +++ b/test/llvmcall.jl @@ -157,7 +157,7 @@ module ObjLoadTest nothing end @test_throws(ErrorException("@ccallable was already defined for this method name"), - @eval @ccallable Cvoid jl_the_callback(not_the_method::Int) = "other") + @eval @ccallable String jl_the_callback(not_the_method::Int) = "other") # Make sure everything up until here gets compiled @test jl_the_callback() === nothing @test jl_the_callback(1) == "other" diff --git a/test/math.jl b/test/math.jl index d794facb02d25..7070fe63ba931 100644 --- a/test/math.jl +++ b/test/math.jl @@ -382,6 +382,10 @@ end end end +@testset "https://github.com/JuliaLang/julia/issues/56782" begin + @test isnan(exp(reinterpret(Float64, 0x7ffbb14880000000))) +end + @testset "test abstractarray trig functions" begin TAA = rand(2,2) TAA = (TAA + TAA')/2. diff --git a/test/meta.jl b/test/meta.jl index 5fb1ebc0d3647..2b235d3cc1b0d 100644 --- a/test/meta.jl +++ b/test/meta.jl @@ -276,6 +276,11 @@ ci = code_lowered(g, Tuple{Val{true}})[1] @test Meta.partially_inline!(copy(ci.code), Any[isdefined_globalref, 1], Tuple{typeof(isdefined_globalref), Int}, [], 0, 0, :propagate)[1] == Expr(:isdefined, GlobalRef(Base, :foo)) + withunreachable(s::String) = sin(s) + ci = code_lowered(withunreachable, Tuple{String})[1] + ci.code[end] = Core.ReturnNode() + @test Meta.partially_inline!(copy(ci.code), Any[withunreachable, "foo"], Tuple{typeof(withunreachable), String}, + [], 0, 0, :propagate)[end] == Core.ReturnNode() end @testset "Base.Meta docstrings" begin diff --git a/test/misc.jl b/test/misc.jl index 7f9992e22a3d7..5bbc2c3c65fa2 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -1211,10 +1211,7 @@ include("testenv.jl") let flags = Cmd(filter(a->!occursin("depwarn", a), collect(test_exeflags))) local cmd = `$test_exename $flags --depwarn=yes deprecation_exec.jl` - - if !success(pipeline(cmd; stdout=stdout, stderr=stderr)) - error("Deprecation test failed, cmd : $cmd") - end + run(cmd, devnull) end # PR #23664, make sure names don't get added to the default `Main` workspace @@ -1489,7 +1486,7 @@ end # Test that read fault on a prot-none region does not incorrectly give # ReadOnlyMemoryError, but rather crashes the program const MAP_ANONYMOUS_PRIVATE = Sys.isbsd() ? 0x1002 : 0x22 -let script = :( +let script = """ let ptr = Ptr{Cint}(ccall(:jl_mmap, Ptr{Cvoid}, (Ptr{Cvoid}, Csize_t, Cint, Cint, Cint, Int), C_NULL, 16*1024, 0, $MAP_ANONYMOUS_PRIVATE, -1, 0)) @@ -1499,19 +1496,24 @@ let script = :( println(e) end end - ) + """ cmd = if Sys.isunix() # Set the maximum core dump size to 0 to keep this expected crash from # producing a (and potentially overwriting an existing) core dump file - `sh -c "ulimit -c 0; $(Base.shell_escape(Base.julia_cmd())) -e '$script'"` + `sh -c "ulimit -c 0; $(Base.shell_escape(Base.julia_cmd())) -e $(Base.shell_escape(script))"` else - `$(Base.julia_cmd()) -e '$script'` + `$(Base.julia_cmd()) -e $script` + end + p = run(ignorestatus(cmd), devnull, stdout, devnull) + if p.termsignal == 0 + Sys.isunix() ? @test(p.exitcode ∈ (128+7, 128+10, 128+11)) : @test(p.exitcode != 0) # expect SIGBUS (7 on BSDs or 10 on Linux) or SIGSEGV (11) + else + @test(p.termsignal ∈ (7, 10, 11)) end - @test !success(cmd) end # issue #41656 -@test success(`$(Base.julia_cmd()) -e 'isempty(x) = true'`) +run(`$(Base.julia_cmd()) -e 'isempty(x) = true'`) @testset "Base/timing.jl" begin @test Base.jit_total_bytes() >= 0 @@ -1596,3 +1598,17 @@ end end @test !occursin("loop not unrolled", out_err) end + +let errs = IOBuffer() + run(`$(Base.julia_cmd()) -e ' + using Test + @test isdefined(DataType.name.mt, :backedges) + Base.Experimental.disable_new_worlds() + @test_throws "disable_new_worlds" @eval f() = 1 + @test !isdefined(DataType.name.mt, :backedges) + @test_throws "disable_new_worlds" Base.delete_method(which(+, (Int, Int))) + @test 1+1 == 2 + using Dates + '`, devnull, stdout, errs) + @test occursin("disable_new_worlds", String(take!(errs))) +end diff --git a/test/offsetarray.jl b/test/offsetarray.jl index fb5855dfbaa0d..8e2ee33c49ed6 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -914,3 +914,14 @@ end b = sum(a, dims=1) @test b[begin] == sum(r) end + +@testset "reshape" begin + A0 = [1 3; 2 4] + A = reshape(A0, 2:3, 4:5) + @test axes(A) == Base.IdentityUnitRange.((2:3, 4:5)) + + B = reshape(A0, -10:-9, 9:10) + @test isa(B, OffsetArray{Int,2}) + @test parent(B) == A0 + @test axes(B) == Base.IdentityUnitRange.((-10:-9, 9:10)) +end diff --git a/test/sets.jl b/test/sets.jl index b78d2f15dd989..4d52cb243620c 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -1037,6 +1037,8 @@ end @test !isempty(A) A = empty!(A) @test isempty(A) + @test isnothing(sizehint!(A, 10)) + @test Base.copymutable(A) == copy(A) end @testset "⊊, ⊋" begin diff --git a/test/sorting.jl b/test/sorting.jl index f12486b9c9b40..e16b30de5bfc8 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -94,6 +94,22 @@ 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(()) == () + @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(rand(NTuple{i, Float64})) + end + @test_throws MethodError sort((1,2,3.0)) + @test Base.infer_return_type(sort, Tuple{Tuple{Vararg{Int}}}) == Tuple{Vararg{Int}} +end + @testset "partialsort" begin @test partialsort([3,6,30,1,9],3) == 6 @test partialsort([3,6,30,1,9],3:4) == [6,9] @@ -819,9 +835,9 @@ end let requires_uint_mappable = Union{Base.Sort.RadixSort, Base.Sort.ConsiderRadixSort, Base.Sort.CountingSort, Base.Sort.ConsiderCountingSort, - typeof(Base.Sort.DEFAULT_STABLE.next.next.next.big.next.yes), - typeof(Base.Sort.DEFAULT_STABLE.next.next.next.big.next.yes.big), - typeof(Base.Sort.DEFAULT_STABLE.next.next.next.big.next.yes.big.next)} + typeof(Base.Sort._DEFAULT_ALGORITHMS_FOR_VECTORS.next.next.next.big.next.yes), + typeof(Base.Sort._DEFAULT_ALGORITHMS_FOR_VECTORS.next.next.next.big.next.yes.big), + typeof(Base.Sort._DEFAULT_ALGORITHMS_FOR_VECTORS.next.next.next.big.next.yes.big.next)} function test_alg(kw, alg, float=true) for order in [Base.Forward, Base.Reverse, Base.By(x -> x^2)] @@ -861,15 +877,18 @@ end end end - test_alg_rec(Base.DEFAULT_STABLE) + test_alg_rec(Base.Sort._DEFAULT_ALGORITHMS_FOR_VECTORS) end end @testset "show(::Algorithm)" begin - @test eval(Meta.parse(string(Base.DEFAULT_STABLE))) === Base.DEFAULT_STABLE - lines = split(string(Base.DEFAULT_STABLE), '\n') + @test eval(Meta.parse(string(Base.Sort._DEFAULT_ALGORITHMS_FOR_VECTORS))) === Base.Sort._DEFAULT_ALGORITHMS_FOR_VECTORS + lines = split(string(Base.Sort._DEFAULT_ALGORITHMS_FOR_VECTORS), '\n') @test 10 < maximum(length, lines) < 100 @test 1 < length(lines) < 30 + + @test eval(Meta.parse(string(Base.DEFAULT_STABLE))) === Base.DEFAULT_STABLE + @test string(Base.DEFAULT_STABLE) == "Base.Sort.DefaultStable()" end @testset "Extensibility" begin @@ -910,6 +929,20 @@ end end @test sort([1,2,3], alg=MySecondAlg()) == [9,9,9] @test all(sort(v, alg=Base.Sort.InitialOptimizations(MySecondAlg())) .=== vcat(fill(9, 100), fill(missing, 10))) + + # Tuple extensions (custom alg) + @test_throws MethodError sort((1,2,3), alg=MyFirstAlg()) + Base.Sort._sort(v::NTuple, ::MyFirstAlg, o::Base.Order.Ordering, kw) = (17,2,9) + @test sort((1,2,3), alg=MyFirstAlg()) == (17,2,9) + + struct TupleFoo + x::Int + end + + # Tuple extensions (custom type) + @test_throws MethodError sort(TupleFoo.((3,1,2))) + Base.Sort._sort(v::NTuple{N, TupleFoo}, ::Base.Sort.DefaultStable, o::Base.Order.Ordering, kw) where N = v + @test sort(TupleFoo.((3,1,2))) === TupleFoo.((3,1,2)) end @testset "sort!(v, lo, hi, alg, order)" begin diff --git a/test/staged.jl b/test/staged.jl index 1b28144639f97..6cb99950a7bb2 100644 --- a/test/staged.jl +++ b/test/staged.jl @@ -270,12 +270,12 @@ end # PR #23168 -function f23168(a, x) +@eval function f23168(a, x) push!(a, 1) if @generated - :(y = x + x) + :(y = $(+)(x, x)) else - y = 2x + y = $(*)(2, x) end push!(a, y) if @generated @@ -290,9 +290,9 @@ end let a = Any[] @test f23168(a, 3) == (6, Int) @test a == [1, 6, 3] - @test occursin(" + ", string(code_lowered(f23168, (Vector{Any},Int)))) - @test occursin("2 * ", string(Base.uncompressed_ir(first(methods(f23168))))) - @test occursin("2 * ", string(code_lowered(f23168, (Vector{Any},Int), generated=false))) + @test occursin("(+)(", string(code_lowered(f23168, (Vector{Any},Int)))) + @test occursin("(*)(2", string(Base.uncompressed_ir(first(methods(f23168))))) + @test occursin("(*)(2", string(code_lowered(f23168, (Vector{Any},Int), generated=false))) @test occursin("Base.add_int", string(code_typed(f23168, (Vector{Any},Int)))) end diff --git a/test/strings/annotated.jl b/test/strings/annotated.jl index 3379452d3e871..dbb81cb48acbc 100644 --- a/test/strings/annotated.jl +++ b/test/strings/annotated.jl @@ -71,6 +71,9 @@ "AnnotatedString{String}(\"some string\", [(1:4, :thing, 0x01), (6:11, :other, 0x02), (1:11, :all, 0x03)])" @test eval(Meta.parse(repr(str))) == str @test sprint(show, MIME("text/plain"), str) == "\"some string\"" + + a = Base.AnnotatedString("hello", [(1:5, :label, 1)]) + @test first(a) == Base.AnnotatedChar('h', [(:label, 1)]) end @testset "AnnotatedChar" begin @@ -247,4 +250,8 @@ end @test write(wrapio, Base.AnnotatedChar('a', [(:y, 2)])) == 1 @test read(seekstart(aio), Base.AnnotatedString) == Base.AnnotatedString("heya", [(1:3, :x, 1), (4:4, :y, 2)]) + # show-ing an AnnotatedIOBuffer + aio = Base.AnnotatedIOBuffer() + write(aio, Base.AnnotatedString("hello", [(1:5, :tag, 1)])) + @test sprint(show, aio) == "Base.AnnotatedIOBuffer(5 bytes, 1 annotation)" end diff --git a/test/strings/basic.jl b/test/strings/basic.jl index bc4e5ae66419a..3fc611a660975 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -430,6 +430,8 @@ end @test Symbol(gstr) === Symbol("12") + @test eltype(gstr) == Char + @test firstindex(gstr) == 1 @test sizeof(gstr) == 2 @test ncodeunits(gstr) == 2 @test length(gstr) == 2 diff --git a/test/subtype.jl b/test/subtype.jl index dfa1487eaa55d..ba7f86bb86a14 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2730,3 +2730,19 @@ let S = Dict{V,V} where {V}, @test A <: typeintersect(S, T) @test A <: typeintersect(T, S) end + +#issue 56606 +let + A = Tuple{Val{1}} + B = Tuple{Val} + for _ in 1:30 + A = Tuple{Val{A}} + B = Tuple{Val{<:B}} + end + @test A <: B +end +@testintersect( + Val{Tuple{Int,S,T}} where {S<:Any,T<:Vector{Vector{Int}}}, + Val{Tuple{T,R,S}} where {T,R<:Vector{T},S<:Vector{R}}, + Val{Tuple{Int, Vector{Int}, T}} where T<:Vector{Vector{Int}}, +) diff --git a/test/syntax.jl b/test/syntax.jl index d9d311ac6615d..fca8b9e8ccdf5 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -476,7 +476,7 @@ let err = try catch e e end - @test err.line == 7 + @test err.line in (5, 7) end # PR #17393 @@ -3713,7 +3713,7 @@ end module Foreign54607 # Syntactic, not dynamic try_to_create_binding1() = (Foreign54607.foo = 2) - # GlobalRef is allowed for same-module assignment + # GlobalRef is allowed for same-module assignment and declares the binding @eval try_to_create_binding2() = ($(GlobalRef(Foreign54607, :foo2)) = 2) function global_create_binding() global bar @@ -3728,7 +3728,7 @@ module Foreign54607 end @test_throws ErrorException (Foreign54607.foo = 1) @test_throws ErrorException Foreign54607.try_to_create_binding1() -@test_throws ErrorException Foreign54607.try_to_create_binding2() +Foreign54607.try_to_create_binding2() function assign_in_foreign_module() (Foreign54607.foo = 1) nothing @@ -3744,6 +3744,7 @@ Foreign54607.global_create_binding() @test isdefined(Foreign54607, :baz) @test isdefined(Foreign54607, :compiled_assign) @test isdefined(Foreign54607, :gr_assign) +@test isdefined(Foreign54607, :foo2) Foreign54607.bar = 8 @test Foreign54607.bar == 8 begin @@ -4033,3 +4034,11 @@ end @test isa(create_inner_f_no_methods(), Function) @test length(methods(create_inner_f_no_methods())) == 0 @test Base.invoke_in_world(first(methods(create_inner_f_one_method)).primary_world, create_inner_f_one_method()) == 1 + +# Issue 56711 - Scope of signature hoisting +function fs56711() + f(lhs::Integer) = 1 + f(lhs::Integer, rhs::(local x_should_not_be_defined=Integer; x_should_not_be_defined)) = 2 + return f +end +@test !@isdefined(x_should_not_be_defined) diff --git a/test/testhelpers/OffsetArrays.jl b/test/testhelpers/OffsetArrays.jl index 17b2d8c28680a..e895372a34974 100644 --- a/test/testhelpers/OffsetArrays.jl +++ b/test/testhelpers/OffsetArrays.jl @@ -529,7 +529,7 @@ _similar_axes_or_length(AT, ax::I, ::I) where {I} = similar(AT, map(_indexlength # reshape accepts a single colon Base.reshape(A::AbstractArray, inds::OffsetAxis...) = reshape(A, inds) -function Base.reshape(A::AbstractArray, inds::Tuple{OffsetAxis,Vararg{OffsetAxis}}) +function Base.reshape(A::AbstractArray, inds::Tuple{Vararg{OffsetAxis}}) AR = reshape(no_offset_view(A), map(_indexlength, inds)) O = OffsetArray(AR, map(_offset, axes(AR), inds)) return _popreshape(O, axes(AR), _filterreshapeinds(inds)) @@ -557,6 +557,8 @@ Base.reshape(A::OffsetArray, inds::Tuple{OffsetAxis,Vararg{OffsetAxis}}) = OffsetArray(_reshape(parent(A), inds), map(_toaxis, inds)) # And for non-offset axes, we can just return a reshape of the parent directly Base.reshape(A::OffsetArray, inds::Tuple{Union{Integer,Base.OneTo},Vararg{Union{Integer,Base.OneTo}}}) = _reshape_nov(A, inds) +Base.reshape(A::OffsetArray, inds::Tuple{Integer,Vararg{Integer}}) = _reshape_nov(A, inds) +Base.reshape(A::OffsetArray, inds::Tuple{Union{Colon, Integer}, Vararg{Union{Colon, Integer}}}) = _reshape_nov(A, inds) Base.reshape(A::OffsetArray, inds::Dims) = _reshape_nov(A, inds) Base.reshape(A::OffsetVector, ::Colon) = A Base.reshape(A::OffsetVector, ::Tuple{Colon}) = A diff --git a/test/threads_exec.jl b/test/threads_exec.jl index ac54dd009390c..d77cf06905f44 100644 --- a/test/threads_exec.jl +++ b/test/threads_exec.jl @@ -3,6 +3,7 @@ using Test using Base.Threads using Base.Threads: SpinLock, threadpoolsize +using LinearAlgebra: peakflops # for cfunction_closure include("testenv.jl") @@ -1312,4 +1313,227 @@ end end end end + +@testset "Base.Experimental.task_metrics" begin + t = Task(() -> nothing) + @test_throws "const field" t.metrics_enabled = true + is_task_metrics_enabled() = fetch(Threads.@spawn current_task().metrics_enabled) + @test !is_task_metrics_enabled() + try + @testset "once" begin + Base.Experimental.task_metrics(true) + @test is_task_metrics_enabled() + Base.Experimental.task_metrics(false) + @test !is_task_metrics_enabled() + end + @testset "multiple" begin + Base.Experimental.task_metrics(true) # 1 + Base.Experimental.task_metrics(true) # 2 + Base.Experimental.task_metrics(true) # 3 + @test is_task_metrics_enabled() + Base.Experimental.task_metrics(false) # 2 + @test is_task_metrics_enabled() + Base.Experimental.task_metrics(false) # 1 + @test is_task_metrics_enabled() + @sync for i in 1:5 # 0 (not negative) + Threads.@spawn Base.Experimental.task_metrics(false) + end + @test !is_task_metrics_enabled() + Base.Experimental.task_metrics(true) # 1 + @test is_task_metrics_enabled() + end + finally + while is_task_metrics_enabled() + Base.Experimental.task_metrics(false) + end + end +end + +@testset "task time counters" begin + @testset "enabled" begin + try + Base.Experimental.task_metrics(true) + start_time = time_ns() + t = Threads.@spawn peakflops() + wait(t) + end_time = time_ns() + wall_time_delta = end_time - start_time + @test t.metrics_enabled + @test Base.Experimental.task_running_time_ns(t) > 0 + @test Base.Experimental.task_wall_time_ns(t) > 0 + @test Base.Experimental.task_wall_time_ns(t) >= Base.Experimental.task_running_time_ns(t) + @test wall_time_delta > Base.Experimental.task_wall_time_ns(t) + finally + Base.Experimental.task_metrics(false) + end + end + @testset "disabled" begin + t = Threads.@spawn peakflops() + wait(t) + @test !t.metrics_enabled + @test isnothing(Base.Experimental.task_running_time_ns(t)) + @test isnothing(Base.Experimental.task_wall_time_ns(t)) + end + @testset "task not run" begin + t1 = Task(() -> nothing) + @test !t1.metrics_enabled + @test isnothing(Base.Experimental.task_running_time_ns(t1)) + @test isnothing(Base.Experimental.task_wall_time_ns(t1)) + try + Base.Experimental.task_metrics(true) + t2 = Task(() -> nothing) + @test t2.metrics_enabled + @test Base.Experimental.task_running_time_ns(t2) == 0 + @test Base.Experimental.task_wall_time_ns(t2) == 0 + finally + Base.Experimental.task_metrics(false) + end + end + @testset "task failure" begin + try + Base.Experimental.task_metrics(true) + t = Threads.@spawn error("this task failed") + @test_throws "this task failed" wait(t) + @test Base.Experimental.task_running_time_ns(t) > 0 + @test Base.Experimental.task_wall_time_ns(t) > 0 + @test Base.Experimental.task_wall_time_ns(t) >= Base.Experimental.task_running_time_ns(t) + finally + Base.Experimental.task_metrics(false) + end + end + @testset "direct yield(t)" begin + try + Base.Experimental.task_metrics(true) + start = time_ns() + t_outer = Threads.@spawn begin + t_inner = Task(() -> peakflops()) + t_inner.sticky = false + # directly yield to `t_inner` rather calling `schedule(t_inner)` + yield(t_inner) + wait(t_inner) + @test Base.Experimental.task_running_time_ns(t_inner) > 0 + @test Base.Experimental.task_wall_time_ns(t_inner) > 0 + @test Base.Experimental.task_wall_time_ns(t_inner) >= Base.Experimental.task_running_time_ns(t_inner) + end + wait(t_outer) + delta = time_ns() - start + @test Base.Experimental.task_running_time_ns(t_outer) > 0 + @test Base.Experimental.task_wall_time_ns(t_outer) > 0 + @test Base.Experimental.task_wall_time_ns(t_outer) >= Base.Experimental.task_running_time_ns(t_outer) + @test Base.Experimental.task_wall_time_ns(t_outer) < delta + finally + Base.Experimental.task_metrics(false) + end + end + @testset "bad schedule" begin + try + Base.Experimental.task_metrics(true) + t1 = Task((x) -> 1) + schedule(t1) # MethodError + yield() + @assert istaskfailed(t1) + @test Base.Experimental.task_running_time_ns(t1) > 0 + @test Base.Experimental.task_wall_time_ns(t1) > 0 + foo(a, b) = a + b + t2 = Task(() -> (peakflops(); foo(wait()))) + schedule(t2) + yield() + @assert istaskstarted(t1) && !istaskdone(t2) + schedule(t2, 1) + yield() + @assert istaskfailed(t2) + @test Base.Experimental.task_running_time_ns(t2) > 0 + @test Base.Experimental.task_wall_time_ns(t2) > 0 + finally + Base.Experimental.task_metrics(false) + end + end + @testset "continuously update until task done" begin + try + Base.Experimental.task_metrics(true) + last_running_time = Ref(typemax(Int)) + last_wall_time = Ref(typemax(Int)) + t = Threads.@spawn begin + running_time = Base.Experimental.task_running_time_ns() + wall_time = Base.Experimental.task_wall_time_ns() + for _ in 1:5 + x = time_ns() + while time_ns() < x + 100 + end + new_running_time = Base.Experimental.task_running_time_ns() + new_wall_time = Base.Experimental.task_wall_time_ns() + @test new_running_time > running_time + @test new_wall_time > wall_time + running_time = new_running_time + wall_time = new_wall_time + end + last_running_time[] = running_time + last_wall_time[] = wall_time + end + wait(t) + final_running_time = Base.Experimental.task_running_time_ns(t) + final_wall_time = Base.Experimental.task_wall_time_ns(t) + @test last_running_time[] < final_running_time + @test last_wall_time[] < final_wall_time + # ensure many more tasks are run to make sure the counters are + # not being updated after a task is done e.g. only when a new task is found + @sync for _ in 1:Threads.nthreads() + Threads.@spawn rand() + end + @test final_running_time == Base.Experimental.task_running_time_ns(t) + @test final_wall_time == Base.Experimental.task_wall_time_ns(t) + finally + Base.Experimental.task_metrics(false) + end + end +end + +@testset "task time counters: lots of spawns" begin + using Dates + try + Base.Experimental.task_metrics(true) + # create more tasks than we have threads. + # - all tasks must have: cpu time <= wall time + # - some tasks must have: cpu time < wall time + # - summing across all tasks we must have: total cpu time <= available cpu time + n_tasks = 2 * Threads.nthreads(:default) + cpu_times = Vector{UInt64}(undef, n_tasks) + wall_times = Vector{UInt64}(undef, n_tasks) + start_time = time_ns() + @sync begin + for i in 1:n_tasks + start_time_i = time_ns() + task_i = Threads.@spawn peakflops() + Threads.@spawn begin + wait(task_i) + end_time_i = time_ns() + wall_time_delta_i = end_time_i - start_time_i + cpu_times[$i] = cpu_time_i = Base.Experimental.task_running_time_ns(task_i) + wall_times[$i] = wall_time_i = Base.Experimental.task_wall_time_ns(task_i) + # task should have recorded some cpu-time and some wall-time + @test cpu_time_i > 0 + @test wall_time_i > 0 + # task cpu-time cannot be greater than its wall-time + @test wall_time_i >= cpu_time_i + # task wall-time must be less than our manually measured wall-time + # between calling `@spawn` and returning from `wait`. + @test wall_time_delta_i > wall_time_i + end + end + end + end_time = time_ns() + wall_time_delta = (end_time - start_time) + available_cpu_time = wall_time_delta * Threads.nthreads(:default) + summed_cpu_time = sum(cpu_times) + # total CPU time from all tasks can't exceed what was actually available. + @test available_cpu_time > summed_cpu_time + # some tasks must have cpu-time less than their wall-time, because we had more tasks + # than threads. + summed_wall_time = sum(wall_times) + @test summed_wall_time > summed_cpu_time + finally + Base.Experimental.task_metrics(false) + end +end + end # main testset diff --git a/test/trimming/Makefile b/test/trimming/Makefile index c6e105d637013..d2da21eb71a88 100644 --- a/test/trimming/Makefile +++ b/test/trimming/Makefile @@ -33,7 +33,7 @@ LDFLAGS_ADD = -lm $(shell $(JULIA_CONFIG) --ldflags --ldlibs) -ljulia-internal release: hello$(EXE) hello.o: $(SRCDIR)/hello.jl $(BUILDSCRIPT) - $(JULIA) -t 1 -J $(BIN)/../lib/julia/sys.so --startup-file=no --history-file=no --output-o $@ --output-incremental=no --strip-ir --strip-metadata --trim $(BUILDSCRIPT) $(SRCDIR)/hello.jl --output-exe true + $(JULIA) -t 1 -J $(BIN)/../lib/julia/sys.so --startup-file=no --history-file=no --output-o $@ --output-incremental=no --strip-ir --strip-metadata --experimental --trim $(BUILDSCRIPT) $(SRCDIR)/hello.jl --output-exe true init.o: $(SRCDIR)/init.c $(CC) -c -o $@ $< $(CPPFLAGS_ADD) $(CPPFLAGS) $(CFLAGS_ADD) $(CFLAGS)