diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index 8a7e8aee715a6..73949068ce8c6 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -3409,7 +3409,7 @@ function abstract_eval_foreigncall(interp::AbstractInterpreter, e::Expr, sstate: abstract_eval_value(interp, x, sstate, sv) end cconv = e.args[5] - if isa(cconv, QuoteNode) && (v = cconv.value; isa(v, Tuple{Symbol, UInt16})) + if isa(cconv, QuoteNode) && (v = cconv.value; isa(v, Tuple{Symbol, UInt16, Bool})) override = decode_effects_override(v[2]) effects = override_effects(effects, override) end diff --git a/Compiler/src/ssair/verify.jl b/Compiler/src/ssair/verify.jl index 974690885a2e4..6b9863a87ec05 100644 --- a/Compiler/src/ssair/verify.jl +++ b/Compiler/src/ssair/verify.jl @@ -1,7 +1,8 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +irshow_was_loaded() = invokelatest(isdefined, Compiler.IRShow, :debuginfo_firstline) function maybe_show_ir(ir::IRCode) - if isdefined(Core, :Main) && isdefined(Core.Main, :Base) + if irshow_was_loaded() # ensure we use I/O that does not yield, as this gets called during compilation invokelatest(Core.Main.Base.show, Core.stdout, "text/plain", ir) else @@ -104,15 +105,16 @@ function count_int(val::Int, arr::Vector{Int}) n end +_debuginfo_firstline(debuginfo::Union{DebugInfo,DebugInfoStream}) = IRShow.debuginfo_firstline(debuginfo) function verify_ir(ir::IRCode, print::Bool=true, allow_frontend_forms::Bool=false, 𝕃ₒ::AbstractLattice = SimpleInferenceLattice.instance, mi::Union{Nothing,MethodInstance}=nothing) function raise_error() error_args = Any["IR verification failed."] - if isdefined(Core, :Main) && isdefined(Core.Main, :Base) + if irshow_was_loaded() # ensure we use I/O that does not yield, as this gets called during compilation - firstline = invokelatest(IRShow.debuginfo_firstline, ir.debuginfo) + firstline = invokelatest(_debuginfo_firstline, ir.debuginfo) else firstline = nothing end diff --git a/Compiler/src/typeinfer.jl b/Compiler/src/typeinfer.jl index 427f485f9118b..ddcca9a6ffaa1 100644 --- a/Compiler/src/typeinfer.jl +++ b/Compiler/src/typeinfer.jl @@ -10,6 +10,7 @@ being used for this purpose alone. """ module Timings +using ..Core using ..Compiler: -, +, :, Vector, length, first, empty!, push!, pop!, @inline, @inbounds, copy, backtrace diff --git a/Compiler/src/typeutils.jl b/Compiler/src/typeutils.jl index d588a9aee1a6c..50b3dc6b0c6f5 100644 --- a/Compiler/src/typeutils.jl +++ b/Compiler/src/typeutils.jl @@ -36,14 +36,8 @@ function isTypeDataType(@nospecialize t) isType(t) && return false # Could be Union{} at runtime t === Core.TypeofBottom && return false - if t.name === Tuple.name - # If we have a Union parameter, could have been redistributed at runtime, - # e.g. `Tuple{Union{Int, Float64}, Int}` is a DataType, but - # `Union{Tuple{Int, Int}, Tuple{Float64, Int}}` is typeequal to it and - # is not. - return all(isTypeDataType, t.parameters) - end - return true + # Return true if `t` is not covariant + return t.name !== Tuple.name end has_extended_info(@nospecialize x) = (!isa(x, Type) && !isvarargtype(x)) || isType(x) diff --git a/Compiler/src/validation.jl b/Compiler/src/validation.jl index 9bde405a49956..4f9362e97b30d 100644 --- a/Compiler/src/validation.jl +++ b/Compiler/src/validation.jl @@ -23,7 +23,7 @@ const VALID_EXPR_HEADS = IdDict{Symbol,UnitRange{Int}}( :meta => 0:typemax(Int), :global => 1:1, :globaldecl => 1:2, - :foreigncall => 5:typemax(Int), # name, RT, AT, nreq, (cconv, effects), args..., roots... + :foreigncall => 5:typemax(Int), # name, RT, AT, nreq, (cconv, effects, gc_safe), args..., roots... :cfunction => 5:5, :isdefined => 1:2, :code_coverage_effect => 0:0, diff --git a/Compiler/test/inline.jl b/Compiler/test/inline.jl index b8ff14405391d..c5a7ab197e16e 100644 --- a/Compiler/test/inline.jl +++ b/Compiler/test/inline.jl @@ -1769,6 +1769,7 @@ let getfield_tfunc(@nospecialize xs...) = Compiler.getfield_tfunc(Compiler.fallback_lattice, xs...) @test getfield_tfunc(Type, Core.Const(:parameters)) !== Union{} @test !isa(getfield_tfunc(Type{Tuple{Union{Int, Float64}, Int}}, Core.Const(:name)), Core.Const) + @test !isa(getfield_tfunc(Type{Tuple{Any}}, Core.Const(:name)), Core.Const) end @test fully_eliminated(Base.ismutable, Tuple{Base.RefValue}) diff --git a/NEWS.md b/NEWS.md index 3a29ba1a10eac..30782942fd612 100644 --- a/NEWS.md +++ b/NEWS.md @@ -25,6 +25,7 @@ New language features * Support for Unicode 16 ([#56925]). * `Threads.@spawn` now takes a `:samepool` argument to specify the same threadpool as the caller. `Threads.@spawn :samepool foo()` which is shorthand for `Threads.@spawn Threads.threadpool() foo()` ([#57109]). +* The `@ccall` macro can now take a `gc_safe` argument, that if set to true allows the runtime to run garbage collection concurrently to the `ccall` Language changes ---------------- diff --git a/base/Base_compiler.jl b/base/Base_compiler.jl index 4ec6bae171d8f..5655d9bbedbab 100644 --- a/base/Base_compiler.jl +++ b/base/Base_compiler.jl @@ -2,6 +2,7 @@ baremodule Base +using Core using Core.Intrinsics, Core.IR # to start, we're going to use a very simple definition of `include` @@ -135,6 +136,9 @@ include("coreio.jl") import Core: @doc, @__doc__, WrappedException, @int128_str, @uint128_str, @big_str, @cmd +# Export list +include("exports.jl") + # core docsystem include("docs/core.jl") Core.atdoc!(CoreDocs.docm) @@ -142,7 +146,6 @@ Core.atdoc!(CoreDocs.docm) eval(x) = Core.eval(Base, x) eval(m::Module, x) = Core.eval(m, x) -include("exports.jl") include("public.jl") if false diff --git a/base/boot.jl b/base/boot.jl index 26a405f92f884..93d73679d65ed 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -238,7 +238,9 @@ export # method reflection applicable, invoke, # constants - nothing, Main + nothing, Main, + # backwards compatibility + arrayref, arrayset, arraysize, const_arrayref const getproperty = getfield # TODO: use `getglobal` for modules instead const setproperty! = setfield! @@ -1040,7 +1042,6 @@ const_arrayref(inbounds::Bool, A::Array, i::Int...) = Main.Base.getindex(A, i... arrayset(inbounds::Bool, A::Array{T}, x::Any, i::Int...) where {T} = Main.Base.setindex!(A, x::T, i...) arraysize(a::Array) = a.size arraysize(a::Array, i::Int) = sle_int(i, nfields(a.size)) ? getfield(a.size, i) : 1 -export arrayref, arrayset, arraysize, const_arrayref const check_top_bit = check_sign_bit # For convenience diff --git a/base/c.jl b/base/c.jl index c1b34579e0a0b..77ef631e295a3 100644 --- a/base/c.jl +++ b/base/c.jl @@ -268,7 +268,31 @@ The above input outputs this: (:printf, :Cvoid, [:Cstring, :Cuint], ["%d", :value]) """ -function ccall_macro_parse(expr::Expr) +function ccall_macro_parse(exprs) + gc_safe = false + expr = nothing + if exprs isa Expr + expr = exprs + elseif length(exprs) == 1 + expr = exprs[1] + elseif length(exprs) == 2 + gc_expr = exprs[1] + expr = exprs[2] + if gc_expr.head == :(=) && gc_expr.args[1] == :gc_safe + if gc_expr.args[2] == true + gc_safe = true + elseif gc_expr.args[2] == false + gc_safe = false + else + throw(ArgumentError("gc_safe must be true or false")) + end + else + throw(ArgumentError("@ccall option must be `gc_safe=true` or `gc_safe=false`")) + end + else + throw(ArgumentError("@ccall needs a function signature with a return type")) + end + # setup and check for errors if !isexpr(expr, :(::)) throw(ArgumentError("@ccall needs a function signature with a return type")) @@ -328,12 +352,11 @@ function ccall_macro_parse(expr::Expr) pusharg!(a) end end - - return func, rettype, types, args, nreq + return func, rettype, types, args, gc_safe, nreq end -function ccall_macro_lower(convention, func, rettype, types, args, nreq) +function ccall_macro_lower(convention, func, rettype, types, args, gc_safe, nreq) statements = [] # if interpolation was used, ensure the value is a function pointer at runtime. @@ -351,9 +374,15 @@ function ccall_macro_lower(convention, func, rettype, types, args, nreq) else func = esc(func) end + cconv = nothing + if convention isa Tuple + cconv = Expr(:cconv, (convention..., gc_safe), nreq) + else + cconv = Expr(:cconv, (convention, UInt16(0), gc_safe), nreq) + end return Expr(:block, statements..., - Expr(:call, :ccall, func, Expr(:cconv, convention, nreq), esc(rettype), + Expr(:call, :ccall, func, cconv, esc(rettype), Expr(:tuple, map(esc, types)...), map(esc, args)...)) end @@ -404,9 +433,16 @@ Example using an external library: The string literal could also be used directly before the function name, if desired `"libglib-2.0".g_uri_escape_string(...` + +It's possible to declare the ccall as `gc_safe` by using the `gc_safe = true` option: + @ccall gc_safe=true strlen(s::Cstring)::Csize_t +This allows the garbage collector to run concurrently with the ccall, which can be useful whenever +the `ccall` may block outside of julia. +WARNING: This option should be used with caution, as it can lead to undefined behavior if the ccall +calls back into the julia runtime. (`@cfunction`/`@ccallables` are safe however) """ -macro ccall(expr) - return ccall_macro_lower(:ccall, ccall_macro_parse(expr)...) +macro ccall(exprs...) + return ccall_macro_lower((:ccall), ccall_macro_parse(exprs)...) end macro ccall_effects(effects::UInt16, expr) diff --git a/base/exports.jl b/base/exports.jl index d81067478dd55..2e0bb3ccfe4cf 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1,5 +1,44 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +# Re-exports from `Core` +export Core, + # key types + Any, DataType, Vararg, NTuple, + Tuple, Type, UnionAll, TypeVar, Union, Nothing, Cvoid, + AbstractArray, DenseArray, NamedTuple, Pair, + # special objects + Function, Method, Module, Symbol, Task, UndefInitializer, undef, WeakRef, VecElement, + Array, Memory, MemoryRef, AtomicMemory, AtomicMemoryRef, GenericMemory, GenericMemoryRef, + # numeric types + Number, Real, Integer, Bool, Ref, Ptr, + AbstractFloat, Float16, Float32, Float64, + Signed, Int, Int8, Int16, Int32, Int64, Int128, + Unsigned, UInt, UInt8, UInt16, UInt32, UInt64, UInt128, + # string types + AbstractChar, Char, AbstractString, String, IO, + # errors + ErrorException, BoundsError, DivideError, DomainError, Exception, + InterruptException, InexactError, OutOfMemoryError, ReadOnlyMemoryError, + OverflowError, StackOverflowError, SegmentationFault, UndefRefError, UndefVarError, + TypeError, ArgumentError, MethodError, AssertionError, LoadError, InitError, + UndefKeywordError, ConcurrencyViolationError, FieldError, + # AST representation + Expr, QuoteNode, LineNumberNode, GlobalRef, + # object model functions + fieldtype, getfield, setfield!, swapfield!, modifyfield!, replacefield!, setfieldonce!, + nfields, throw, tuple, ===, isdefined, + # access to globals + getglobal, setglobal!, swapglobal!, modifyglobal!, replaceglobal!, setglobalonce!, isdefinedglobal, + # ifelse, sizeof # not exported, to avoid conflicting with Base + # type reflection + <:, typeof, isa, typeassert, + # method reflection + applicable, invoke, + # constants + nothing, Main, + # backwards compatibility + arrayref, arrayset, arraysize, const_arrayref + export # Modules Meta, diff --git a/base/intfuncs.jl b/base/intfuncs.jl index dc81f2bd3e489..bb0f454d9c574 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -8,6 +8,9 @@ Greatest common (positive) divisor (or zero if all arguments are zero). The arguments may be integer and rational numbers. +``a`` is a divisor of ``b`` if there exists an integer ``m`` such +that ``ma=b``. + !!! compat "Julia 1.4" Rational arguments require Julia 1.4 or later. diff --git a/base/invalidation.jl b/base/invalidation.jl index c0aed35aa90a0..462e348e09038 100644 --- a/base/invalidation.jl +++ b/base/invalidation.jl @@ -113,39 +113,36 @@ function invalidate_method_for_globalref!(gr::GlobalRef, method::Method, invalid end end -const BINDING_FLAG_EXPORTP = 0x2 - function invalidate_code_for_globalref!(b::Core.Binding, invalidated_bpart::Core.BindingPartition, new_bpart::Union{Core.BindingPartition, Nothing}, new_max_world::UInt) gr = b.globalref - if is_some_guard(binding_kind(invalidated_bpart)) + if !is_some_guard(binding_kind(invalidated_bpart)) # TODO: We may want to invalidate for these anyway, since they have performance implications - return - end - foreach_module_mtable(gr.mod, new_max_world) do mt::Core.MethodTable - for method in MethodList(mt) - invalidate_method_for_globalref!(gr, method, invalidated_bpart, new_max_world) + foreach_module_mtable(gr.mod, new_max_world) do mt::Core.MethodTable + for method in MethodList(mt) + invalidate_method_for_globalref!(gr, method, invalidated_bpart, new_max_world) + end + return true end - return true - end - if isdefined(b, :backedges) - for edge in b.backedges - if isa(edge, CodeInstance) - ccall(:jl_invalidate_code_instance, Cvoid, (Any, UInt), edge, new_max_world) - elseif isa(edge, Core.Binding) - isdefined(edge, :partitions) || continue - latest_bpart = edge.partitions - latest_bpart.max_world == typemax(UInt) || continue - is_some_imported(binding_kind(latest_bpart)) || continue - partition_restriction(latest_bpart) === b || continue - invalidate_code_for_globalref!(edge, latest_bpart, nothing, new_max_world) - else - invalidate_method_for_globalref!(gr, edge::Method, invalidated_bpart, new_max_world) + if isdefined(b, :backedges) + for edge in b.backedges + if isa(edge, CodeInstance) + ccall(:jl_invalidate_code_instance, Cvoid, (Any, UInt), edge, new_max_world) + elseif isa(edge, Core.Binding) + isdefined(edge, :partitions) || continue + latest_bpart = edge.partitions + latest_bpart.max_world == typemax(UInt) || continue + is_some_imported(binding_kind(latest_bpart)) || continue + partition_restriction(latest_bpart) === b || continue + invalidate_code_for_globalref!(edge, latest_bpart, nothing, new_max_world) + else + invalidate_method_for_globalref!(gr, edge::Method, invalidated_bpart, new_max_world) + end end end end - if (b.flags & BINDING_FLAG_EXPORTP) != 0 + if (invalidated_bpart.kind & BINDING_FLAG_EXPORTED != 0) || (new_bpart !== nothing && (new_bpart.kind & BINDING_FLAG_EXPORTED != 0)) # This binding was exported - we need to check all modules that `using` us to see if they - # have an implicit binding to us. + # have a binding that is affected by this change. usings_backedges = ccall(:jl_get_module_usings_backedges, Any, (Any,), gr.mod) if usings_backedges !== nothing for user in usings_backedges::Vector{Any} @@ -154,8 +151,8 @@ function invalidate_code_for_globalref!(b::Core.Binding, invalidated_bpart::Core isdefined(user_binding, :partitions) || continue latest_bpart = user_binding.partitions latest_bpart.max_world == typemax(UInt) || continue - is_some_imported(binding_kind(latest_bpart)) || continue - partition_restriction(latest_bpart) === b || continue + binding_kind(latest_bpart) in (BINDING_KIND_IMPLICIT, BINDING_KIND_FAILED, BINDING_KIND_GUARD) || continue + @atomic :release latest_bpart.max_world = new_max_world invalidate_code_for_globalref!(convert(Core.Binding, user_binding), latest_bpart, nothing, new_max_world) end end diff --git a/base/loading.jl b/base/loading.jl index 47741da26a1b2..818bd2366b2d0 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -3157,11 +3157,11 @@ This can be used to reduce package load times. Cache files are stored in `DEPOT_PATH[1]/compiled`. See [Module initialization and precompilation](@ref) for important notes. """ -function compilecache(pkg::PkgId, internal_stderr::IO = stderr, internal_stdout::IO = stdout; flags::Cmd=``, reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}(), loadable_exts::Union{Vector{PkgId},Nothing}=nothing) +function compilecache(pkg::PkgId, internal_stderr::IO = stderr, internal_stdout::IO = stdout; flags::Cmd=``, cacheflags::CacheFlags=CacheFlags(), reasons::Union{Dict{String,Int},Nothing}=Dict{String,Int}(), loadable_exts::Union{Vector{PkgId},Nothing}=nothing) @nospecialize internal_stderr internal_stdout path = locate_package(pkg) path === nothing && throw(ArgumentError("$(repr("text/plain", pkg)) not found during precompilation")) - return compilecache(pkg, path, internal_stderr, internal_stdout; flags, reasons, loadable_exts) + return compilecache(pkg, path, internal_stderr, internal_stdout; flags, cacheflags, reasons, loadable_exts) end const MAX_NUM_PRECOMPILE_FILES = Ref(10) diff --git a/base/math.jl b/base/math.jl index 650fc6bc0cef0..0e5f9dd41bdca 100644 --- a/base/math.jl +++ b/base/math.jl @@ -1217,7 +1217,8 @@ end # this method is only reliable for -2^20 < n < 2^20 (cf. #53881 #53886) @assume_effects :terminates_locally @noinline function pow_body(x::Float64, n::Integer) y = 1.0 - xnlo = ynlo = 0.0 + xnlo = -0.0 + ynlo = 0.0 n == 3 && return x*x*x # keep compatibility with literal_pow if n < 0 rx = inv(x) diff --git a/base/meta.jl b/base/meta.jl index 36875b8e2c625..4807b910c494a 100644 --- a/base/meta.jl +++ b/base/meta.jl @@ -427,7 +427,7 @@ function _partially_inline!(@nospecialize(x), slot_replacements::Vector{Any}, elseif i == 4 @assert isa(x.args[4], Int) elseif i == 5 - @assert isa((x.args[5]::QuoteNode).value, Union{Symbol, Tuple{Symbol, UInt8}}) + @assert isa((x.args[5]::QuoteNode).value, Union{Symbol, Tuple{Symbol, UInt16, Bool}}) else x.args[i] = _partially_inline!(x.args[i], slot_replacements, type_signature, static_param_values, diff --git a/base/public.jl b/base/public.jl index 8777a454c920a..8d2a65f9a150c 100644 --- a/base/public.jl +++ b/base/public.jl @@ -20,6 +20,7 @@ public Generator, ImmutableDict, OneTo, + Pairs, LogRange, UUID, diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 36961f58c5c3f..3a66dbda97477 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -209,6 +209,8 @@ const BINDING_KIND_GUARD = 0x8 const BINDING_KIND_UNDEF_CONST = 0x9 const BINDING_KIND_BACKDATED_CONST = 0xa +const BINDING_FLAG_EXPORTED = 0x10 + is_defined_const_binding(kind::UInt8) = (kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT || kind == BINDING_KIND_BACKDATED_CONST) is_some_const_binding(kind::UInt8) = (is_defined_const_binding(kind) || kind == BINDING_KIND_UNDEF_CONST) is_some_imported(kind::UInt8) = (kind == BINDING_KIND_IMPLICIT || kind == BINDING_KIND_EXPLICIT || kind == BINDING_KIND_IMPORTED) diff --git a/base/show.jl b/base/show.jl index 6b27fe838c89b..6316f2811a34c 100644 --- a/base/show.jl +++ b/base/show.jl @@ -618,7 +618,7 @@ function make_typealias(@nospecialize(x::Type)) Any === x && return nothing x <: Tuple && return nothing mods = modulesof!(Set{Module}(), x) - Core in mods && push!(mods, Base) + replace!(mods, Core=>Base) aliases = Tuple{GlobalRef,SimpleVector}[] xenv = UnionAll[] for p in uniontypes(unwrap_unionall(x)) @@ -3368,6 +3368,9 @@ function print_partition(io::IO, partition::Core.BindingPartition) else print(io, max_world) end + if (partition.kind & BINDING_FLAG_EXPORTED) != 0 + print(io, " [exported]") + end print(io, " - ") kind = binding_kind(partition) if kind == BINDING_KIND_BACKDATED_CONST diff --git a/base/staticdata.jl b/base/staticdata.jl index 7283a93dd8b3b..45504622fe0ec 100644 --- a/base/staticdata.jl +++ b/base/staticdata.jl @@ -70,6 +70,8 @@ function verify_method_graph(codeinst::CodeInstance, stack::Vector{CodeInstance} nothing end +get_require_world() = unsafe_load(cglobal(:jl_require_world, UInt)) + # Test all edges relevant to a method: # - Visit the entire call graph, starting from edges[idx] to determine if that method is valid # - Implements Tarjan's SCC (strongly connected components) algorithm, simplified to remove the count variable @@ -81,7 +83,14 @@ function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visi return 0, world, max_valid2 end end - local minworld::UInt, maxworld::UInt = 1, validation_world + # Implicitly referenced bindings in the current module do not get explicit edges. + # If they were invalidated, they'll be in `mwis`. If they weren't, they imply a minworld + # of `get_require_world`. In principle, this is only required for methods that do reference + # an implicit globalref. However, we already don't perform this validation for methods that + # don't have any (implicit or explicit) edges at all. The remaining corner case (some explicit, + # but no implicit edges) is rare and there would be little benefit to lower the minworld for it + # in any case, so we just always use `get_require_world` here. + local minworld::UInt, maxworld::UInt = get_require_world(), validation_world def = get_ci_mi(codeinst).def @assert def isa Method if haskey(visiting, codeinst) @@ -103,9 +112,8 @@ function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visi # verify current edges if isempty(callees) # quick return: no edges to verify (though we probably shouldn't have gotten here from WORLD_AGE_REVALIDATION_SENTINEL) - elseif maxworld == unsafe_load(cglobal(:jl_require_world, UInt)) + elseif maxworld == get_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 j = 1 while j ≤ length(callees) diff --git a/base/strings/string.jl b/base/strings/string.jl index 9f3c3d00e4b81..79ec12d11cb94 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -107,7 +107,7 @@ end # but the macro is not available at this time in bootstrap, so we write it manually. const _string_n_override = 0x04ee @eval _string_n(n::Integer) = $(Expr(:foreigncall, QuoteNode(:jl_alloc_string), Ref{String}, - :(Core.svec(Csize_t)), 1, QuoteNode((:ccall, _string_n_override)), :(convert(Csize_t, n)))) + :(Core.svec(Csize_t)), 1, QuoteNode((:ccall, _string_n_override, false)), :(convert(Csize_t, n)))) """ String(s::AbstractString) diff --git a/deps/JuliaSyntax.version b/deps/JuliaSyntax.version index 70dac3231f626..9487754d8a617 100644 --- a/deps/JuliaSyntax.version +++ b/deps/JuliaSyntax.version @@ -1,4 +1,4 @@ JULIASYNTAX_BRANCH = main -JULIASYNTAX_SHA1 = 86bc4331eaa08e08bf2af1ba7b50bbbf4af70cdb +JULIASYNTAX_SHA1 = 46723f071d5b2efcb21ca6757788028afb91cc13 JULIASYNTAX_GIT_URL := https://github.com/JuliaLang/JuliaSyntax.jl.git JULIASYNTAX_TAR_URL = https://api.github.com/repos/JuliaLang/JuliaSyntax.jl/tarball/$1 diff --git a/deps/checksums/JuliaSyntax-46723f071d5b2efcb21ca6757788028afb91cc13.tar.gz/md5 b/deps/checksums/JuliaSyntax-46723f071d5b2efcb21ca6757788028afb91cc13.tar.gz/md5 new file mode 100644 index 0000000000000..ff40f520dfe85 --- /dev/null +++ b/deps/checksums/JuliaSyntax-46723f071d5b2efcb21ca6757788028afb91cc13.tar.gz/md5 @@ -0,0 +1 @@ +2a0921e59edfab54554aa173f091c5b7 diff --git a/deps/checksums/JuliaSyntax-46723f071d5b2efcb21ca6757788028afb91cc13.tar.gz/sha512 b/deps/checksums/JuliaSyntax-46723f071d5b2efcb21ca6757788028afb91cc13.tar.gz/sha512 new file mode 100644 index 0000000000000..64e90d0edaba0 --- /dev/null +++ b/deps/checksums/JuliaSyntax-46723f071d5b2efcb21ca6757788028afb91cc13.tar.gz/sha512 @@ -0,0 +1 @@ +17050e23216335f6599f009f71e9614a11b6686e455554b1efd287cd8526a7ebece06dc473e34cd50f61bf52085ff72bb4279144a9fdb3a234d3d589a10fddaf diff --git a/deps/checksums/JuliaSyntax-86bc4331eaa08e08bf2af1ba7b50bbbf4af70cdb.tar.gz/md5 b/deps/checksums/JuliaSyntax-86bc4331eaa08e08bf2af1ba7b50bbbf4af70cdb.tar.gz/md5 deleted file mode 100644 index c5d3c249ca1e0..0000000000000 --- a/deps/checksums/JuliaSyntax-86bc4331eaa08e08bf2af1ba7b50bbbf4af70cdb.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -a514fe65096a489bd4d3c06b675573c7 diff --git a/deps/checksums/JuliaSyntax-86bc4331eaa08e08bf2af1ba7b50bbbf4af70cdb.tar.gz/sha512 b/deps/checksums/JuliaSyntax-86bc4331eaa08e08bf2af1ba7b50bbbf4af70cdb.tar.gz/sha512 deleted file mode 100644 index bdb48e7ef87eb..0000000000000 --- a/deps/checksums/JuliaSyntax-86bc4331eaa08e08bf2af1ba7b50bbbf4af70cdb.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -95d45a27e427f2553da4d4e821edaee6896121977ce6572212c4234013c6f85bc69fc78d237b4dae5d4ed3451f3ba9e1a7172668025ef7bf8aad024293aa2865 diff --git a/deps/checksums/Statistics-77bd5707f143eb624721a7df28ddef470e70ecef.tar.gz/md5 b/deps/checksums/Statistics-77bd5707f143eb624721a7df28ddef470e70ecef.tar.gz/md5 new file mode 100644 index 0000000000000..600c561d0cf14 --- /dev/null +++ b/deps/checksums/Statistics-77bd5707f143eb624721a7df28ddef470e70ecef.tar.gz/md5 @@ -0,0 +1 @@ +5235ac479da042d5dc3c572c473b7219 diff --git a/deps/checksums/Statistics-77bd5707f143eb624721a7df28ddef470e70ecef.tar.gz/sha512 b/deps/checksums/Statistics-77bd5707f143eb624721a7df28ddef470e70ecef.tar.gz/sha512 new file mode 100644 index 0000000000000..2f663a3d7c44d --- /dev/null +++ b/deps/checksums/Statistics-77bd5707f143eb624721a7df28ddef470e70ecef.tar.gz/sha512 @@ -0,0 +1 @@ +0c02ccf1b4988fc701209afb949f27e6f675f37a628385d3f28dc9ea333fed38ce1ca77b001e58fdbe15af833bbe98598cbf478cef21a98b37d54acfe52270b6 diff --git a/deps/checksums/Statistics-d49c2bf4f81e1efb4980a35fe39c815ef8396297.tar.gz/md5 b/deps/checksums/Statistics-d49c2bf4f81e1efb4980a35fe39c815ef8396297.tar.gz/md5 deleted file mode 100644 index 3956c67f7fd47..0000000000000 --- a/deps/checksums/Statistics-d49c2bf4f81e1efb4980a35fe39c815ef8396297.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -acf2bb0ea30132602e172e2f5f6274b4 diff --git a/deps/checksums/Statistics-d49c2bf4f81e1efb4980a35fe39c815ef8396297.tar.gz/sha512 b/deps/checksums/Statistics-d49c2bf4f81e1efb4980a35fe39c815ef8396297.tar.gz/sha512 deleted file mode 100644 index 051f2d0a862c3..0000000000000 --- a/deps/checksums/Statistics-d49c2bf4f81e1efb4980a35fe39c815ef8396297.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -5e879fe79bae19b62f81659a102602271c73a424faf4be069ab31fb50e30b536a8c7b3692127763000cc1dbab69c93ac3da7bace5f093d05dce2d652fb221d52 diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index fe63dfe35edac..706dfc34875fa 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -498,9 +498,9 @@ These symbols appear in the `head` field of [`Expr`](@ref)s in lowered form. The number of required arguments for a varargs function definition. - * `args[5]::QuoteNode{<:Union{Symbol,Tuple{Symbol,UInt16}}`: calling convention + * `args[5]::QuoteNode{<:Union{Symbol,Tuple{Symbol,UInt16}, Tuple{Symbol,UInt16,Bool}}`: calling convention - The calling convention for the call, optionally with effects. + The calling convention for the call, optionally with effects, and `gc_safe` (safe to execute concurrently to GC.). * `args[6:5+length(args[3])]` : arguments diff --git a/doc/src/manual/modules.md b/doc/src/manual/modules.md index cf24474916bef..b4d1cde9527be 100644 --- a/doc/src/manual/modules.md +++ b/doc/src/manual/modules.md @@ -192,8 +192,6 @@ julia> nice(::Cat) = "nice 😸" ERROR: invalid method definition in Main: function NiceStuff.nice must be explicitly imported to be extended Stacktrace: [1] top-level scope - @ none:0 - [2] top-level scope @ none:1 ``` diff --git a/src/ast.c b/src/ast.c index 0f24d96393f2f..eeff17162f3fc 100644 --- a/src/ast.c +++ b/src/ast.c @@ -178,7 +178,7 @@ static value_t fl_defined_julia_global(fl_context_t *fl_ctx, value_t *args, uint jl_sym_t *var = scmsym_to_julia(fl_ctx, args[0]); jl_binding_t *b = jl_get_module_binding(ctx->module, var, 0); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - return (bpart != NULL && decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) == BINDING_KIND_GLOBAL) ? fl_ctx->T : fl_ctx->F; + return (bpart != NULL && jl_binding_kind(bpart) == BINDING_KIND_GLOBAL) ? fl_ctx->T : fl_ctx->F; } // Used to generate a unique suffix for a given symbol (e.g. variable or type name) @@ -1369,6 +1369,34 @@ JL_DLLEXPORT jl_value_t *jl_expand_stmt(jl_value_t *expr, jl_module_t *inmodule) return jl_expand_stmt_with_loc(expr, inmodule, "none", 0); } +jl_code_info_t *jl_outer_ctor_body(jl_value_t *thistype, size_t nfields, size_t nsparams, jl_module_t *inmodule, const char *file, int line) +{ + JL_TIMING(LOWERING, LOWERING); + jl_timing_show_location(file, line, inmodule, JL_TIMING_DEFAULT_BLOCK); + jl_expr_t *expr = jl_exprn(jl_empty_sym, 3); + JL_GC_PUSH1(&expr); + jl_exprargset(expr, 0, thistype); + jl_exprargset(expr, 1, jl_box_long(nfields)); + jl_exprargset(expr, 2, jl_box_long(nsparams)); + jl_code_info_t *ci = (jl_code_info_t*)jl_call_scm_on_ast_and_loc("jl-default-outer-ctor-body", (jl_value_t*)expr, inmodule, file, line); + JL_GC_POP(); + assert(jl_is_code_info(ci)); + return ci; +} + +jl_code_info_t *jl_inner_ctor_body(jl_array_t *fieldkinds, jl_module_t *inmodule, const char *file, int line) +{ + JL_TIMING(LOWERING, LOWERING); + jl_timing_show_location(file, line, inmodule, JL_TIMING_DEFAULT_BLOCK); + jl_expr_t *expr = jl_exprn(jl_empty_sym, 0); + JL_GC_PUSH1(&expr); + expr->args = fieldkinds; + jl_code_info_t *ci = (jl_code_info_t*)jl_call_scm_on_ast_and_loc("jl-default-inner-ctor-body", (jl_value_t*)expr, inmodule, file, line); + JL_GC_POP(); + assert(jl_is_code_info(ci)); + return ci; +} + //------------------------------------------------------------------------------ // Parsing API and utils for calling parser from runtime diff --git a/src/builtin_proto.h b/src/builtin_proto.h index 77463ae4884cb..a543aa895fb97 100644 --- a/src/builtin_proto.h +++ b/src/builtin_proto.h @@ -80,6 +80,7 @@ JL_CALLABLE(jl_f__structtype); JL_CALLABLE(jl_f__abstracttype); JL_CALLABLE(jl_f__primitivetype); JL_CALLABLE(jl_f__setsuper); +JL_CALLABLE(jl_f__defaultctors); JL_CALLABLE(jl_f__equiv_typedef); JL_CALLABLE(jl_f_get_binding_type); JL_CALLABLE(jl_f__compute_sparams); diff --git a/src/builtins.c b/src/builtins.c index f3d2dfad42819..063b191510bfd 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -2345,6 +2345,13 @@ JL_CALLABLE(jl_f__equiv_typedef) return equiv_type(args[0], args[1]) ? jl_true : jl_false; } +JL_CALLABLE(jl_f__defaultctors) +{ + JL_NARGS(_defaultctors, 2, 2); + jl_ctor_def(args[0], args[1]); + return jl_nothing; +} + // IntrinsicFunctions --------------------------------------------------------- static void (*runtime_fp[num_intrinsics])(void); @@ -2541,6 +2548,7 @@ void jl_init_primitives(void) JL_GC_DISABLED add_builtin_func("_abstracttype", jl_f__abstracttype); add_builtin_func("_primitivetype", jl_f__primitivetype); add_builtin_func("_setsuper!", jl_f__setsuper); + add_builtin_func("_defaultctors", jl_f__defaultctors); jl_builtin__typebody = add_builtin_func("_typebody!", jl_f__typebody); add_builtin_func("_equiv_typedef", jl_f__equiv_typedef); jl_builtin_donotdelete = add_builtin_func("donotdelete", jl_f_donotdelete); diff --git a/src/ccall.cpp b/src/ccall.cpp index 6c03f9532d9a8..c35979eb85b1d 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1134,6 +1134,7 @@ class function_sig_t { AttributeList attributes; // vector of function call site attributes Type *lrt; // input parameter of the llvm return type (from julia_struct_to_llvm) bool retboxed; // input parameter indicating whether lrt is jl_value_t* + bool gc_safe; // input parameter indicating whether the call is safe to execute concurrently to GC Type *prt; // out parameter of the llvm return type for the function signature int sret; // out parameter for indicating whether return value has been moved to the first argument position std::string err_msg; @@ -1146,8 +1147,8 @@ class function_sig_t { size_t nreqargs; // number of required arguments in ccall function definition jl_codegen_params_t *ctx; - function_sig_t(const char *fname, Type *lrt, jl_value_t *rt, bool retboxed, jl_svec_t *at, jl_unionall_t *unionall_env, size_t nreqargs, CallingConv::ID cc, bool llvmcall, jl_codegen_params_t *ctx) - : lrt(lrt), retboxed(retboxed), + function_sig_t(const char *fname, Type *lrt, jl_value_t *rt, bool retboxed, bool gc_safe, jl_svec_t *at, jl_unionall_t *unionall_env, size_t nreqargs, CallingConv::ID cc, bool llvmcall, jl_codegen_params_t *ctx) + : lrt(lrt), retboxed(retboxed), gc_safe(gc_safe), prt(NULL), sret(0), cc(cc), llvmcall(llvmcall), at(at), rt(rt), unionall_env(unionall_env), nccallargs(jl_svec_len(at)), nreqargs(nreqargs), @@ -1295,6 +1296,7 @@ std::string generate_func_sig(const char *fname) RetAttrs = RetAttrs.addAttribute(LLVMCtx, Attribute::NonNull); if (rt == jl_bottom_type) FnAttrs = FnAttrs.addAttribute(LLVMCtx, Attribute::NoReturn); + assert(attributes.isEmpty()); attributes = AttributeList::get(LLVMCtx, FnAttrs, RetAttrs, paramattrs); return ""; @@ -1412,7 +1414,7 @@ static const std::string verify_ccall_sig(jl_value_t *&rt, jl_value_t *at, const int fc_args_start = 6; -// Expr(:foreigncall, pointer, rettype, (argtypes...), nreq, [cconv | (cconv, effects)], args..., roots...) +// Expr(:foreigncall, pointer, rettype, (argtypes...), nreq, gc_safe, [cconv | (cconv, effects)], args..., roots...) static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) { JL_NARGSV(ccall, 5); @@ -1424,11 +1426,13 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) assert(jl_is_quotenode(args[5])); jl_value_t *jlcc = jl_quotenode_value(args[5]); jl_sym_t *cc_sym = NULL; + bool gc_safe = false; if (jl_is_symbol(jlcc)) { cc_sym = (jl_sym_t*)jlcc; } else if (jl_is_tuple(jlcc)) { cc_sym = (jl_sym_t*)jl_get_nth_field_noalloc(jlcc, 0); + gc_safe = jl_unbox_bool(jl_get_nth_field_checked(jlcc, 2)); } assert(jl_is_symbol(cc_sym)); native_sym_arg_t symarg = {}; @@ -1547,7 +1551,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) } if (rt != args[2] && rt != (jl_value_t*)jl_any_type) jl_temporary_root(ctx, rt); - function_sig_t sig("ccall", lrt, rt, retboxed, + function_sig_t sig("ccall", lrt, rt, retboxed, gc_safe, (jl_svec_t*)at, unionall, nreqargs, cc, llvmcall, &ctx.emission_context); for (size_t i = 0; i < nccallargs; i++) { @@ -2158,11 +2162,16 @@ jl_cgval_t function_sig_t::emit_a_ccall( } } - OperandBundleDef OpBundle("jl_roots", gc_uses); + // Potentially we could drop `jl_roots(gc_uses)` in the presence of `gc-transition(gc_uses)` + SmallVector bundles; + if (!gc_uses.empty()) + bundles.push_back(OperandBundleDef("jl_roots", gc_uses)); + if (gc_safe) + bundles.push_back(OperandBundleDef("gc-transition", ArrayRef {})); // the actual call CallInst *ret = ctx.builder.CreateCall(functype, llvmf, argvals, - ArrayRef(&OpBundle, gc_uses.empty() ? 0 : 1)); + bundles); ((CallInst*)ret)->setAttributes(attributes); if (cc != CallingConv::C) diff --git a/src/clangsa/GCChecker.cpp b/src/clangsa/GCChecker.cpp index fdbe5ec9d9e29..af07ca2227839 100644 --- a/src/clangsa/GCChecker.cpp +++ b/src/clangsa/GCChecker.cpp @@ -856,7 +856,6 @@ bool GCChecker::isGCTrackedType(QualType QT) { Name.ends_with_insensitive("jl_stenv_t") || Name.ends_with_insensitive("jl_varbinding_t") || Name.ends_with_insensitive("set_world") || - Name.ends_with_insensitive("jl_ptr_kind_union_t") || Name.ends_with_insensitive("jl_codectx_t")) { return true; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 4a836b4f2a4c6..2f5a12aad326d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -940,7 +940,7 @@ static const auto jlcheckbpwritable_func = new JuliaFunction<>{ nullptr, }; static const auto jlgetbindingvalue_func = new JuliaFunction<>{ - XSTR(jl_reresolve_binding_value_seqcst), + XSTR(jl_get_binding_value_seqcst), [](LLVMContext &C) { auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); @@ -3138,9 +3138,9 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) jl_sym_t *sym = (jl_sym_t*)ex; jl_binding_t *bnd = jl_get_module_binding(ctx.module, sym, 0); jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); - jl_ptr_kind_union_t pku = jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world); - if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) - return decode_restriction_value(pku); + jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world); + if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart))) + return bpart->restriction; return NULL; } if (jl_is_slotnumber(ex) || jl_is_argument(ex)) @@ -3163,10 +3163,10 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) s = jl_globalref_name(ex); jl_binding_t *bnd = jl_get_module_binding(jl_globalref_mod(ex), s, 0); jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); - jl_ptr_kind_union_t pku = jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world); + jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world); jl_value_t *v = NULL; - if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) - v = decode_restriction_value(pku); + if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart))) + v = bpart->restriction; if (v) { if (bnd->deprecated) cg_bdw(ctx, s, bnd); @@ -3190,10 +3190,10 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) if (s && jl_is_symbol(s)) { jl_binding_t *bnd = jl_get_module_binding(m, s, 0); jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); - jl_ptr_kind_union_t pku = jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world); + jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world); jl_value_t *v = NULL; - if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) - v = decode_restriction_value(pku); + if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart))) + v = bpart->restriction; if (v) { if (bnd->deprecated) cg_bdw(ctx, s, bnd); @@ -3257,7 +3257,6 @@ static bool slot_eq(jl_value_t *e, int sl) // --- find volatile variables --- // assigned in a try block and used outside that try block - static bool local_var_occurs(jl_value_t *e, int sl) { if (slot_eq(e, sl)) { @@ -3297,13 +3296,13 @@ static bool have_try_block(jl_array_t *stmts) return 0; } -// conservative marking of all variables potentially used after a catch block that were assigned before it +// conservative marking of all variables potentially used after a catch block that were assigned after the try static void mark_volatile_vars(jl_array_t *stmts, SmallVectorImpl &slots, const std::set &bbstarts) { if (!have_try_block(stmts)) return; size_t slength = jl_array_dim0(stmts); - BitVector assigned_in_block(slots.size()); // conservatively only ignore slots assigned in the same basic block + BitVector assigned_in_block(slots.size()); // since we don't have domtree access, conservatively only ignore slots assigned in the same basic block for (int j = 0; j < (int)slength; j++) { if (bbstarts.count(j + 1)) assigned_in_block.reset(); @@ -3443,50 +3442,44 @@ static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t * if (!bpart) { return emit_globalref_runtime(ctx, bnd, mod, name); } - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { - // try to look this up now. - // TODO: This is bad and we'd like to delete it. - jl_get_binding(mod, name); - } // bpart was updated in place - this will change with full partition - pku = jl_atomic_load_acquire(&bpart->restriction); - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { + if (jl_bkind_is_some_guard(jl_binding_kind(bpart))) { // Redo the lookup at runtime return emit_globalref_runtime(ctx, bnd, mod, name); } else { while (true) { if (!bpart) break; - if (!jl_bkind_is_some_import(decode_restriction_kind(pku))) + if (!jl_bkind_is_some_import(jl_binding_kind(bpart))) break; if (bnd->deprecated) { cg_bdw(ctx, name, bnd); } - bnd = (jl_binding_t*)decode_restriction_value(pku); + bnd = (jl_binding_t*)bpart->restriction; bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); if (!bpart) break; - pku = jl_atomic_load_acquire(&bpart->restriction); } - enum jl_partition_kind kind = decode_restriction_kind(pku); - if (bpart && (jl_bkind_is_some_constant(kind) && kind != BINDING_KIND_BACKDATED_CONST)) { - jl_value_t *constval = decode_restriction_value(pku); - if (!constval) { - undef_var_error_ifnot(ctx, ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0), name, (jl_value_t*)mod); - return jl_cgval_t(); + if (bpart) { + enum jl_partition_kind kind = jl_binding_kind(bpart); + if (jl_bkind_is_some_constant(kind) && kind != BINDING_KIND_BACKDATED_CONST) { + jl_value_t *constval = bpart->restriction; + if (!constval) { + undef_var_error_ifnot(ctx, ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0), name, (jl_value_t*)mod); + return jl_cgval_t(); + } + return mark_julia_const(ctx, constval); } - return mark_julia_const(ctx, constval); } } - if (!bpart || decode_restriction_kind(pku) != BINDING_KIND_GLOBAL) { + if (!bpart || jl_binding_kind(bpart) != BINDING_KIND_GLOBAL) { return emit_globalref_runtime(ctx, bnd, mod, name); } Value *bp = julia_binding_gv(ctx, bnd); if (bnd->deprecated) { cg_bdw(ctx, name, bnd); } - jl_value_t *ty = decode_restriction_value(pku); + jl_value_t *ty = bpart->restriction; bp = julia_binding_pvalue(ctx, bp); if (ty == nullptr) ty = (jl_value_t*)jl_any_type; @@ -3502,9 +3495,8 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); Value *bp = julia_binding_gv(ctx, bnd); if (bpart) { - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - if (decode_restriction_kind(pku) == BINDING_KIND_GLOBAL) { - jl_value_t *ty = decode_restriction_value(pku); + if (jl_binding_kind(bpart) == BINDING_KIND_GLOBAL) { + jl_value_t *ty = bpart->restriction; if (ty != nullptr) { const std::string fname = issetglobal ? "setglobal!" : isreplaceglobal ? "replaceglobal!" : isswapglobal ? "swapglobal!" : ismodifyglobal ? "modifyglobal!" : "setglobalonce!"; if (!ismodifyglobal) { @@ -4189,8 +4181,8 @@ static jl_cgval_t emit_isdefinedglobal(jl_codectx_t &ctx, jl_module_t *modu, jl_ Value *isnull = NULL; jl_binding_t *bnd = allow_import ? jl_get_binding(modu, name) : jl_get_module_binding(modu, name, 0); jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); - jl_ptr_kind_union_t pku = bpart ? jl_atomic_load_relaxed(&bpart->restriction) : encode_restriction(NULL, BINDING_KIND_GUARD); - if (decode_restriction_kind(pku) == BINDING_KIND_GLOBAL || jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + enum jl_partition_kind kind = bpart ? jl_binding_kind(bpart) : BINDING_KIND_GUARD; + if (kind == BINDING_KIND_GLOBAL || jl_bkind_is_some_constant(kind)) { if (jl_get_binding_value_if_const(bnd)) return mark_julia_const(ctx, jl_true); Value *bp = julia_binding_gv(ctx, bnd); @@ -8049,7 +8041,7 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con if (rt != declrt && rt != (jl_value_t*)jl_any_type) jl_temporary_root(ctx, rt); - function_sig_t sig("cfunction", lrt, rt, retboxed, argt, unionall_env, false, CallingConv::C, false, &ctx.emission_context); + function_sig_t sig("cfunction", lrt, rt, retboxed, false, argt, unionall_env, false, CallingConv::C, false, &ctx.emission_context); assert(sig.fargt.size() + sig.sret == sig.fargt_sig.size()); if (!sig.err_msg.empty()) { emit_error(ctx, sig.err_msg); @@ -8189,7 +8181,7 @@ const char *jl_generate_ccallable(Module *llvmmod, void *sysimg_handle, jl_value } jl_value_t *err; { // scope block for sig - function_sig_t sig("cfunction", lcrt, crt, toboxed, + function_sig_t sig("cfunction", lcrt, crt, toboxed, false, argtypes, NULL, false, CallingConv::C, false, ¶ms); if (sig.err_msg.empty()) { if (sysimg_handle) { diff --git a/src/gc-stock.c b/src/gc-stock.c index 72479d14e67a5..c9cb57b19a604 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -2448,16 +2448,6 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ if (npointers == 0) return; uintptr_t nptr = (npointers << 2 | (bits & GC_OLD)); - if (vt == jl_binding_partition_type) { - // BindingPartition has a special union of jl_value_t and flag bits - // but is otherwise regular. - jl_binding_partition_t *bpart = (jl_binding_partition_t*)jl_valueof(o); - jl_value_t *val = decode_restriction_value( - jl_atomic_load_relaxed(&bpart->restriction)); - if (val) - gc_heap_snapshot_record_binding_partition_edge((jl_value_t*)bpart, val); - gc_try_claim_and_push(mq, val, &nptr); - } assert((layout->nfields > 0 || layout->flags.fielddesc_type == 3) && "opaque types should have been handled specially"); if (layout->flags.fielddesc_type == 0) { diff --git a/src/jlfrontend.scm b/src/jlfrontend.scm index 9c69da199c0cd..c313a1e9b0db5 100644 --- a/src/jlfrontend.scm +++ b/src/jlfrontend.scm @@ -198,6 +198,12 @@ (error-wrap (lambda () (julia-expand-macroscope expr)))) +(define (jl-default-inner-ctor-body field-kinds file line) + (expand-to-thunk- (default-inner-ctor-body (cdr field-kinds) file line) file line)) + +(define (jl-default-outer-ctor-body args file line) + (expand-to-thunk- (default-outer-ctor-body (cadr args) (caddr args) (cadddr args) file line) file line)) + ; run whole frontend on a string. useful for testing. (define (fe str) (expand-toplevel-expr (julia-parse str) 'none 0)) diff --git a/src/jltypes.c b/src/jltypes.c index b94922ce9cf54..7f89e15aa38f3 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3262,8 +3262,8 @@ void jl_init_types(void) JL_GC_DISABLED jl_binding_partition_type = jl_new_datatype(jl_symbol("BindingPartition"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(5, "restriction", "min_world", "max_world", "next", "reserved"), - jl_svec(5, jl_uint64_type /* Special GC-supported union of Any and flags*/, + jl_perm_symsvec(5, "restriction", "min_world", "max_world", "next", "kind"), + jl_svec(5, jl_any_type, jl_ulong_type, jl_ulong_type, jl_any_type/*jl_binding_partition_type*/, jl_ulong_type), jl_emptysvec, 0, 1, 0); const static uint32_t binding_partition_atomicfields[] = { 0b01101 }; // Set fields 1, 3, 4 as atomic @@ -3876,7 +3876,10 @@ void jl_init_types(void) JL_GC_DISABLED jl_string_type->ismutationfree = jl_string_type->isidentityfree = 1; jl_symbol_type->ismutationfree = jl_symbol_type->isidentityfree = 1; jl_simplevector_type->ismutationfree = jl_simplevector_type->isidentityfree = 1; + jl_typename_type->ismutationfree = 1; jl_datatype_type->ismutationfree = 1; + jl_uniontype_type->ismutationfree = 1; + jl_unionall_type->ismutationfree = 1; assert(((jl_datatype_t*)jl_array_any_type)->ismutationfree == 0); assert(((jl_datatype_t*)jl_array_uint8_type)->ismutationfree == 0); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 035d2a84e729d..b6dc7bb9c8a12 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -183,6 +183,7 @@ (meta ret-type ,R) ,@(list-tail body (+ 1 (length meta)))))))))) + ;; convert x<:T<:y etc. exprs into (name lower-bound upper-bound) ;; a bound is #f if not specified (define (analyze-typevar e) @@ -753,64 +754,34 @@ (params bounds) (sparam-name-bounds params) (struct-def-expr- name params bounds super (flatten-blocks fields) mut))) -;; replace field names with gensyms if they conflict with field-types -(define (safe-field-names field-names field-types) - (if (any (lambda (v) (contains (lambda (e) (eq? e v)) field-types)) - field-names) - (map (lambda (x) (gensy)) field-names) - ;; use a different name for a field called `_` - (map (lambda (x) (if (eq? x '_) (gensy) x)) field-names))) - -(define (with-wheres call wheres) - (if (pair? wheres) - `(where ,call ,@wheres) - call)) - -(define (default-inner-ctors name field-names field-types params bounds locs) - (let* ((field-names (safe-field-names field-names field-types)) - (all-ctor (if (null? params) - ;; definition with exact types for all arguments - `(function (call ,name - ,@(map make-decl field-names field-types)) - (block - ,@locs - (new (globalref (thismodule) ,name) ,@field-names))) - #f)) - (any-ctor (if (or (not all-ctor) (any (lambda (t) (not (equal? t '(core Any)))) - field-types)) - ;; definition with Any for all arguments - ;; only if any field type is not Any, checked at runtime - `(function (call (|::| |#ctor-self#| - ,(with-wheres - `(curly (core Type) ,(if (pair? params) - `(curly ,name ,@params) - name)) - (map (lambda (b) (cons 'var-bounds b)) bounds))) - ,@field-names) - (block - ,@locs - (call new ,@field-names))) ; this will add convert calls later - #f))) - (if all-ctor - (if any-ctor - (list all-ctor - `(if ,(foldl (lambda (t u) - `(&& ,u (call (core ===) (core Any) ,t))) - `(call (core ===) (core Any) ,(car field-types)) - (cdr field-types)) - '(block) - ,any-ctor)) - (list all-ctor)) - (list any-ctor)))) - -(define (default-outer-ctor name field-names field-types params bounds locs) - (let ((field-names (safe-field-names field-names field-types))) - `(function ,(with-wheres - `(call ,name ,@(map make-decl field-names field-types)) - (map (lambda (b) (cons 'var-bounds b)) bounds)) - (block - ,@locs - (new (curly ,name ,@params) ,@field-names))))) +;; definition with Any for all arguments (except type, which is exact) +;; field-kinds: +;; -1 no convert (e.g. because it is Any) +;; 0 normal convert to fieldtype +;; 1+ static_parameter N +(define (default-inner-ctor-body field-kinds file line) + (let* ((name '|#ctor-self#|) + (field-names (map (lambda (idx) (symbol (string "_" (+ idx 1)))) (iota (length field-kinds)))) + (field-convert (lambda (fld fty val) + (cond ((eq? fty -1) val) + ((> fty 0) (convert-for-type-decl val `(static_parameter ,fty) #f #f)) + (else (convert-for-type-decl val `(call (core fieldtype) ,name ,(+ fld 1)) #f #f))))) + (field-vals (map field-convert (iota (length field-names)) field-kinds field-names)) + (body `(block + (line ,line ,file) + (return (new ,name ,@field-vals))))) + `(lambda ,(cons name field-names) () (scope-block ,body)))) + +;; definition with exact types for all arguments (except type, which is not parameterized) +(define (default-outer-ctor-body thistype field-count sparam-count file line) + (let* ((name '|#ctor-self#|) + (field-names (map (lambda (idx) (symbol (string "_" (+ idx 1)))) (iota field-count))) + (sparams (map (lambda (idx) `(static_parameter ,(+ idx 1))) (iota sparam-count))) + (type (if (null? sparams) name `(curly ,thistype ,@sparams))) + (body `(block + (line ,line ,file) + (return (new ,type ,@field-names))))) + `(lambda ,(cons name field-names) () (scope-block ,body)))) (define (num-non-varargs args) (count (lambda (a) (not (vararg? a))) args)) @@ -993,14 +964,11 @@ fields))) (attrs (reverse attrs)) (defs (filter (lambda (x) (not (or (effect-free? x) (eq? (car x) 'string)))) defs)) - (locs (if (and (pair? fields0) (linenum? (car fields0))) - (list (car fields0)) - '())) + (loc (if (and (pair? fields0) (linenum? (car fields0))) + (car fields0) + '(line 0 ||))) (field-names (map decl-var fields)) (field-types (map decl-type fields)) - (defs2 (if (null? defs) - (default-inner-ctors name field-names field-types params bounds locs) - defs)) (min-initialized (min (ctors-min-initialized defs) (length fields))) (hasprev (make-ssavalue)) (prev (make-ssavalue)) @@ -1042,34 +1010,21 @@ (const (globalref (thismodule) ,name) ,newdef) (latestworld) (null))) - ;; "inner" constructors - (scope-block - (block - (hardscope) - (global ,name) - ,@(map (lambda (c) - (rewrite-ctor c name params field-names field-types)) - defs2))) - ;; "outer" constructors - ,@(if (and (null? defs) - (not (null? params)) - ;; To generate an outer constructor, each parameter must occur in a field - ;; type, or in the bounds of a subsequent parameter. - ;; Otherwise the constructor would not work, since the parameter values - ;; would never be specified. - (let loop ((root-types field-types) - (sp (reverse bounds))) - (or (null? sp) - (let ((p (car sp))) - (and (expr-contains-eq (car p) (cons 'list root-types)) - (loop (append (cdr p) root-types) - (cdr sp))))))) - `((scope-block - (block - (global ,name) - ,(default-outer-ctor name field-names field-types - params bounds locs)))) - '()) + ;; Always define ctors even if we didn't change the definition. + ;; If newdef===prev, then this is a bit suspect, since we don't know what might be + ;; changing about the old ctor definitions (we don't even track whether we're + ;; replacing defaultctors with identical ones). But it seems better to have the ctors + ;; added alongside (replacing) the old ones, than to not have them and need them. + ;; Commonly Revise.jl should be used to figure out actually which methods should + ;; actually be deleted or added anew. + ,(if (null? defs) + `(call (core _defaultctors) ,newdef (inert ,loc)) + `(scope-block + (block + (hardscope) + (global ,name) + ,@(map (lambda (c) (rewrite-ctor c name params field-names field-types)) defs)))) + (latestworld) (null))))) (define (abstract-type-def-expr name params super) @@ -4131,8 +4086,9 @@ f(x) = yt(x) `(toplevel-butfirst ;; wrap in toplevel-butfirst so it gets moved higher along with ;; closure type definitions + (unnecessary ,(cadr e)) ,e - (thunk (lambda () (() () 0 ()) (block (return ,e)))))))) + (latestworld))))) ((null? cvs) `(block ,@sp-inits @@ -4645,7 +4601,7 @@ f(x) = yt(x) ;; from the current function. (define (compile e break-labels value tail) (if (or (not (pair? e)) (memq (car e) '(null true false ssavalue quote inert top core copyast the_exception $ - globalref thismodule cdecl stdcall fastcall thiscall llvmcall))) + globalref thismodule cdecl stdcall fastcall thiscall llvmcall static_parameter))) (let ((e1 (if (and arg-map (symbol? e)) (get arg-map e e) e))) @@ -4656,7 +4612,7 @@ f(x) = yt(x) (cond (tail (emit-return tail e1)) (value e1) ((symbol? e1) (emit e1) #f) ;; keep symbols for undefined-var checking - ((and (pair? e1) (eq? (car e1) 'globalref)) (emit e1) #f) ;; keep globals for undefined-var checking + ((and (pair? e1) (memq (car e1) '(globalref static_parameter))) (emit e1) #f) ;; keep for undefined-var checking (else #f))) (case (car e) ((call new splatnew foreigncall cfunction new_opaque_closure) diff --git a/src/julia.h b/src/julia.h index 1fd709f42ee31..5edbf4a1c8955 100644 --- a/src/julia.h +++ b/src/julia.h @@ -706,12 +706,9 @@ enum jl_partition_kind { BINDING_KIND_IMPLICIT_RECOMPUTE = 0xb }; -#ifdef _P64 -// Union of a ptr and a 3 bit field. -typedef uintptr_t jl_ptr_kind_union_t; -#else -typedef struct __attribute__((aligned(8))) { jl_value_t *val; size_t kind; } jl_ptr_kind_union_t; -#endif +// These are flags that get anded into the above +static const uint8_t BINDING_FLAG_EXPORTED = 0x10; + typedef struct __attribute__((aligned(8))) _jl_binding_partition_t { JL_DATA_TYPE /* union { @@ -722,18 +719,19 @@ typedef struct __attribute__((aligned(8))) _jl_binding_partition_t { * // For ->kind in (BINDING_KIND_IMPLICIT, BINDING_KIND_EXPLICIT, BINDING_KIND_IMPORT) * jl_binding_t *imported; * } restriction; - * - * Currently: Low 3 bits hold ->kind on _P64 to avoid needing >8 byte atomics - * - * This field is updated atomically with both kind and restriction */ - _Atomic(jl_ptr_kind_union_t) restriction; + jl_value_t *restriction; size_t min_world; _Atomic(size_t) max_world; _Atomic(struct _jl_binding_partition_t *) next; - size_t reserved; // Reserved for ->kind. Currently this holds the low bits of ->restriction during serialization + size_t kind; } jl_binding_partition_t; +STATIC_INLINE enum jl_partition_kind jl_binding_kind(jl_binding_partition_t *bpart) JL_NOTSAFEPOINT +{ + return (enum jl_partition_kind)(bpart->kind & 0xf); +} + typedef struct _jl_binding_t { JL_DATA_TYPE jl_globalref_t *globalref; // cached GlobalRef for this binding @@ -741,8 +739,8 @@ typedef struct _jl_binding_t { _Atomic(jl_binding_partition_t*) partitions; jl_array_t *backedges; uint8_t did_print_backdate_admonition:1; - uint8_t exportp:1; // `public foo` sets `publicp`, `export foo` sets both `publicp` and `exportp` - uint8_t publicp:1; // exportp without publicp is not allowed. + uint8_t did_print_implicit_import_admonition:1; + uint8_t publicp:1; // `export` is tracked in partitions, but sets this as well uint8_t deprecated:2; // 0=not deprecated, 1=renamed, 2=moved to another package uint8_t padding:3; } jl_binding_t; @@ -1617,6 +1615,7 @@ static inline int jl_field_isconst(jl_datatype_t *st, int i) JL_NOTSAFEPOINT #define jl_is_quotenode(v) jl_typetagis(v,jl_quotenode_type) #define jl_is_newvarnode(v) jl_typetagis(v,jl_newvarnode_type) #define jl_is_linenode(v) jl_typetagis(v,jl_linenumbernode_type) +#define jl_is_linenumbernode(v) jl_typetagis(v,jl_linenumbernode_type) #define jl_is_method_instance(v) jl_typetagis(v,jl_method_instance_type) #define jl_is_code_instance(v) jl_typetagis(v,jl_code_instance_type) #define jl_is_code_info(v) jl_typetagis(v,jl_code_info_type) diff --git a/src/julia_internal.h b/src/julia_internal.h index 6d83000184880..983df1ccdc4c7 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -915,6 +915,8 @@ JL_DLLEXPORT jl_binding_t *jl_get_module_binding(jl_module_t *m JL_PROPAGATES_RO JL_DLLEXPORT void jl_binding_deprecation_warning(jl_module_t *m, jl_sym_t *sym, jl_binding_t *b); JL_DLLEXPORT jl_binding_partition_t *jl_replace_binding_locked(jl_binding_t *b JL_PROPAGATES_ROOT, jl_binding_partition_t *old_bpart, jl_value_t *restriction_val, enum jl_partition_kind kind, size_t new_world) JL_GLOBALLY_ROOTED; +JL_DLLEXPORT jl_binding_partition_t *jl_replace_binding_locked2(jl_binding_t *b JL_PROPAGATES_ROOT, + jl_binding_partition_t *old_bpart, jl_value_t *restriction_val, size_t kind, size_t new_world) JL_GLOBALLY_ROOTED; extern jl_array_t *jl_module_init_order JL_GLOBALLY_ROOTED; extern htable_t jl_current_modules JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_module_t *jl_precompile_toplevel_module JL_GLOBALLY_ROOTED; @@ -932,62 +934,6 @@ jl_method_t *jl_make_opaque_closure_method(jl_module_t *module, jl_value_t *name int nargs, jl_value_t *functionloc, jl_code_info_t *ci, int isva, int isinferred); JL_DLLEXPORT int jl_is_valid_oc_argtype(jl_tupletype_t *argt, jl_method_t *source); -EXTERN_INLINE_DECLARE enum jl_partition_kind decode_restriction_kind(jl_ptr_kind_union_t pku) JL_NOTSAFEPOINT -{ -#ifdef _P64 - uint8_t bits = (pku & 0x7); - jl_value_t *val = (jl_value_t*)(pku & ~0x7); - - if (val == NULL) { - if (bits == BINDING_KIND_IMPLICIT) { - return BINDING_KIND_GUARD; - } - if (bits == BINDING_KIND_CONST) { - return BINDING_KIND_UNDEF_CONST; - } - } else { - if (bits == BINDING_KIND_DECLARED) { - return BINDING_KIND_BACKDATED_CONST; - } - } - - return (enum jl_partition_kind)bits; -#else - return (enum jl_partition_kind)pku.kind; -#endif -} - -STATIC_INLINE jl_value_t *decode_restriction_value(jl_ptr_kind_union_t JL_PROPAGATES_ROOT pku) JL_NOTSAFEPOINT -{ -#ifdef _P64 - jl_value_t *val = (jl_value_t*)(pku & ~0x7); - return val; -#else - return pku.val; -#endif -} - -STATIC_INLINE jl_ptr_kind_union_t encode_restriction(jl_value_t *val, enum jl_partition_kind kind) JL_NOTSAFEPOINT -{ -#ifdef _P64 - if (kind == BINDING_KIND_GUARD || kind == BINDING_KIND_DECLARED || kind == BINDING_KIND_FAILED || kind == BINDING_KIND_UNDEF_CONST) - assert(val == NULL); - else if (kind == BINDING_KIND_IMPLICIT || kind == BINDING_KIND_CONST || kind == BINDING_KIND_BACKDATED_CONST) - assert(val != NULL); - if (kind == BINDING_KIND_GUARD) - kind = BINDING_KIND_IMPLICIT; - else if (kind == BINDING_KIND_UNDEF_CONST) - kind = BINDING_KIND_CONST; - else if (kind == BINDING_KIND_BACKDATED_CONST) - kind = BINDING_KIND_DECLARED; - assert((((uintptr_t)val) & 0x7) == 0); - return ((jl_ptr_kind_union_t)val) | kind; -#else - jl_ptr_kind_union_t ret = { val, kind }; - return ret; -#endif -} - STATIC_INLINE int jl_bkind_is_some_import(enum jl_partition_kind kind) JL_NOTSAFEPOINT { return kind == BINDING_KIND_IMPLICIT || kind == BINDING_KIND_EXPLICIT || kind == BINDING_KIND_IMPORTED; } @@ -1008,35 +954,31 @@ JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b JL JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition_all(jl_binding_t *b JL_PROPAGATES_ROOT, size_t min_world, size_t max_world) JL_GLOBALLY_ROOTED; EXTERN_INLINE_DECLARE uint8_t jl_bpart_get_kind(jl_binding_partition_t *bpart) JL_NOTSAFEPOINT { - return decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)); + return (uint8_t)(bpart->kind & 0xf); } -STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart JL_PROPAGATES_ROOT, size_t world) JL_NOTSAFEPOINT; -STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace_all(jl_binding_t **bnd, jl_binding_partition_t **bpart JL_PROPAGATES_ROOT, size_t min_world, size_t max_world) JL_NOTSAFEPOINT; +STATIC_INLINE void jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart JL_PROPAGATES_ROOT, size_t world) JL_NOTSAFEPOINT; +STATIC_INLINE void jl_walk_binding_inplace_all(jl_binding_t **bnd, jl_binding_partition_t **bpart JL_PROPAGATES_ROOT, size_t min_world, size_t max_world) JL_NOTSAFEPOINT; #ifndef __clang_analyzer__ -STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t world) JL_NOTSAFEPOINT +STATIC_INLINE void jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t world) JL_NOTSAFEPOINT { while (1) { - if (!*bpart) - return encode_restriction(NULL, BINDING_KIND_GUARD); - jl_ptr_kind_union_t pku = jl_atomic_load_acquire(&(*bpart)->restriction); - if (!jl_bkind_is_some_import(decode_restriction_kind(pku))) - return pku; - *bnd = (jl_binding_t*)decode_restriction_value(pku); + if (!jl_bkind_is_some_import(jl_binding_kind(*bpart))) + return; + *bnd = (jl_binding_t*)(*bpart)->restriction; *bpart = jl_get_binding_partition(*bnd, world); } } -STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace_all(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t min_world, size_t max_world) JL_NOTSAFEPOINT +STATIC_INLINE void jl_walk_binding_inplace_all(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t min_world, size_t max_world) JL_NOTSAFEPOINT { while (1) { - if (!*bpart) - return encode_restriction(NULL, BINDING_KIND_GUARD); - jl_ptr_kind_union_t pku = jl_atomic_load_acquire(&(*bpart)->restriction); - if (!jl_bkind_is_some_import(decode_restriction_kind(pku))) - return pku; - *bnd = (jl_binding_t*)decode_restriction_value(pku); + if (!(*bpart)) + return; + if (!jl_bkind_is_some_import(jl_binding_kind(*bpart))) + return; + *bnd = (jl_binding_t*)(*bpart)->restriction; *bpart = jl_get_binding_partition_all(*bnd, min_world, max_world); } } @@ -1238,6 +1180,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_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, uint8_t default_using_core, uint8_t self_name); 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 JL_PROPAGATES_ROOT, 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_PROPAGATES_ROOT; @@ -1273,6 +1216,9 @@ JL_DLLEXPORT int jl_has_meta(jl_array_t *body, jl_sym_t *sym) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_parse(const char *text, size_t text_len, jl_value_t *filename, size_t lineno, size_t offset, jl_value_t *options); +jl_code_info_t *jl_inner_ctor_body(jl_array_t *fieldkinds, jl_module_t *inmodule, const char *file, int line); +jl_code_info_t *jl_outer_ctor_body(jl_value_t *thistype, size_t nfields, size_t nsparams, jl_module_t *inmodule, const char *file, int line); +void jl_ctor_def(jl_value_t *ty, jl_value_t *functionloc); //-------------------------------------------------- // Backtraces diff --git a/src/llvm-codegen-shared.h b/src/llvm-codegen-shared.h index ff6f5a97299d7..d474fb4f61183 100644 --- a/src/llvm-codegen-shared.h +++ b/src/llvm-codegen-shared.h @@ -244,21 +244,17 @@ static inline llvm::Value *emit_gc_state_set(llvm::IRBuilder<> &builder, llvm::T unsigned offset = offsetof(jl_tls_states_t, gc_state); Value *gc_state = builder.CreateConstInBoundsGEP1_32(T_int8, ptls, offset, "gc_state"); if (old_state == nullptr) { - old_state = builder.CreateLoad(T_int8, gc_state); + old_state = builder.CreateLoad(T_int8, gc_state, "old_state"); cast(old_state)->setOrdering(AtomicOrdering::Monotonic); } builder.CreateAlignedStore(state, gc_state, Align(sizeof(void*)))->setOrdering(AtomicOrdering::Release); if (auto *C = dyn_cast(old_state)) - if (C->isZero()) - return old_state; - if (auto *C = dyn_cast(state)) - if (!C->isZero()) - return old_state; + if (auto *C2 = dyn_cast(state)) + if (C->getZExtValue() == C2->getZExtValue()) + return old_state; BasicBlock *passBB = BasicBlock::Create(builder.getContext(), "safepoint", builder.GetInsertBlock()->getParent()); BasicBlock *exitBB = BasicBlock::Create(builder.getContext(), "after_safepoint", builder.GetInsertBlock()->getParent()); - Constant *zero8 = ConstantInt::get(T_int8, 0); - builder.CreateCondBr(builder.CreateOr(builder.CreateICmpEQ(old_state, zero8), // if (!old_state || !state) - builder.CreateICmpEQ(state, zero8)), + builder.CreateCondBr(builder.CreateICmpEQ(old_state, state, "is_new_state"), // Safepoint whenever we change the GC state passBB, exitBB); builder.SetInsertPoint(passBB); MDNode *tbaa = get_tbaa_const(builder.getContext()); diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index 7d6fba65a79e7..1b7551f33ebcd 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -2181,16 +2181,47 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S, bool *CFGModified) { NewCall->copyMetadata(*CI); CI->replaceAllUsesWith(NewCall); UpdatePtrNumbering(CI, NewCall, S); - } else if (CI->arg_size() == CI->getNumOperands()) { - /* No operand bundle to lower */ - ++it; - continue; } else { - CallInst *NewCall = CallInst::Create(CI, None, CI); - NewCall->takeName(CI); - NewCall->copyMetadata(*CI); - CI->replaceAllUsesWith(NewCall); - UpdatePtrNumbering(CI, NewCall, S); + SmallVector bundles; + CI->getOperandBundlesAsDefs(bundles); + bool gc_transition = false; + for (auto &bundle: bundles) + if (bundle.getTag() == "gc-transition") + gc_transition = true; + + // In theory LLVM wants us to lower this using RewriteStatepointsForGC + if (gc_transition) { + // Insert the operations to switch to gc_safe if necessary. + IRBuilder<> builder(CI); + Value *pgcstack = getOrAddPGCstack(F); + assert(pgcstack); + // We dont use emit_state_set here because safepoints are unconditional for any code that reaches this + // We are basically guaranteed to go from gc_unsafe to gc_safe and back, and both transitions need a safepoint + // We also can't add any BBs here, so just avoiding the branches is good + Value *ptls = get_current_ptls_from_task(builder, get_current_task_from_pgcstack(builder, pgcstack), tbaa_gcframe); + unsigned offset = offsetof(jl_tls_states_t, gc_state); + Value *gc_state = builder.CreateConstInBoundsGEP1_32(Type::getInt8Ty(builder.getContext()), ptls, offset, "gc_state"); + LoadInst *last_gc_state = builder.CreateAlignedLoad(Type::getInt8Ty(builder.getContext()), gc_state, Align(sizeof(void*))); + last_gc_state->setOrdering(AtomicOrdering::Monotonic); + builder.CreateAlignedStore(builder.getInt8(JL_GC_STATE_SAFE), gc_state, Align(sizeof(void*)))->setOrdering(AtomicOrdering::Release); + MDNode *tbaa = get_tbaa_const(builder.getContext()); + emit_gc_safepoint(builder, T_size, ptls, tbaa, false); + builder.SetInsertPoint(CI->getNextNode()); + builder.CreateAlignedStore(last_gc_state, gc_state, Align(sizeof(void*)))->setOrdering(AtomicOrdering::Release); + emit_gc_safepoint(builder, T_size, ptls, tbaa, false); + } + if (CI->arg_size() == CI->getNumOperands()) { + /* No operand bundle to lower */ + ++it; + continue; + } else { + // remove operand bundle + CallInst *NewCall = CallInst::Create(CI, None, CI); + NewCall->takeName(CI); + NewCall->copyMetadata(*CI); + CI->replaceAllUsesWith(NewCall); + UpdatePtrNumbering(CI, NewCall, S); + } } if (!CI->use_empty()) { CI->replaceAllUsesWith(UndefValue::get(CI->getType())); @@ -2291,7 +2322,7 @@ void LateLowerGCFrame::PlaceGCFrameStores(State &S, unsigned MinColorRoot, const LargeSparseBitVector &NowLive = S.LiveSets[*rit]; // reset slots which are no longer alive for (int Idx : *LastLive) { - if (Idx >= PreAssignedColors && !HasBitSet(NowLive, Idx)) { + if (Colors[Idx] >= PreAssignedColors && !HasBitSet(NowLive, Idx)) { PlaceGCFrameReset(S, Idx, MinColorRoot, Colors, GCFrame, S.ReverseSafepointNumbering[*rit]); } diff --git a/src/llvm-pass-helpers.cpp b/src/llvm-pass-helpers.cpp index ca25251040fb2..9d415d923ecb6 100644 --- a/src/llvm-pass-helpers.cpp +++ b/src/llvm-pass-helpers.cpp @@ -88,6 +88,27 @@ llvm::CallInst *JuliaPassContext::getPGCstack(llvm::Function &F) const return nullptr; } +llvm::CallInst *JuliaPassContext::getOrAddPGCstack(llvm::Function &F) +{ + if (pgcstack_getter || adoptthread_func) + for (auto &I : F.getEntryBlock()) { + if (CallInst *callInst = dyn_cast(&I)) { + Value *callee = callInst->getCalledOperand(); + if ((pgcstack_getter && callee == pgcstack_getter) || + (adoptthread_func && callee == adoptthread_func)) { + return callInst; + } + } + } + IRBuilder<> builder(&F.getEntryBlock().front()); + if (pgcstack_getter) + return builder.CreateCall(pgcstack_getter); + auto FT = FunctionType::get(PointerType::get(F.getContext(), 0), false); + auto F2 = Function::Create(FT, Function::ExternalLinkage, "julia.get_pgcstack", F.getParent()); + pgcstack_getter = F2; + return builder.CreateCall( F2); +} + llvm::Function *JuliaPassContext::getOrNull( const jl_intrinsics::IntrinsicDescription &desc) const { diff --git a/src/llvm-pass-helpers.h b/src/llvm-pass-helpers.h index d46f1f46634e6..ac08cda2d61e0 100644 --- a/src/llvm-pass-helpers.h +++ b/src/llvm-pass-helpers.h @@ -87,7 +87,10 @@ struct JuliaPassContext { // point of the given function, if there exists such a call. // Otherwise, `nullptr` is returned. llvm::CallInst *getPGCstack(llvm::Function &F) const; - + // Gets a call to the `julia.get_pgcstack' intrinsic in the entry + // point of the given function, if there exists such a call. + // Otherwise, creates a new call to the intrinsic + llvm::CallInst *getOrAddPGCstack(llvm::Function &F); // Gets the intrinsic or well-known function that conforms to // the given description if it exists in the module. If not, // `nullptr` is returned. diff --git a/src/llvm-ptls.cpp b/src/llvm-ptls.cpp index e36136859517a..15f5a5574a6d3 100644 --- a/src/llvm-ptls.cpp +++ b/src/llvm-ptls.cpp @@ -196,8 +196,13 @@ void LowerPTLS::fix_pgcstack_use(CallInst *pgcstack, Function *pgcstack_getter, last_gc_state->addIncoming(prior, fastTerm->getParent()); for (auto &BB : *pgcstack->getParent()->getParent()) { if (isa(BB.getTerminator())) { + // Don't use emit_gc_safe_leave here, as that introduces a new BB while iterating BBs builder.SetInsertPoint(BB.getTerminator()); - emit_gc_unsafe_leave(builder, T_size, get_current_ptls_from_task(builder, get_current_task_from_pgcstack(builder, phi), tbaa), last_gc_state, true); + Value *ptls = get_current_ptls_from_task(builder, get_current_task_from_pgcstack(builder, phi), tbaa_gcframe); + unsigned offset = offsetof(jl_tls_states_t, gc_state); + Value *gc_state = builder.CreateConstInBoundsGEP1_32(Type::getInt8Ty(builder.getContext()), ptls, offset, "gc_state"); + builder.CreateAlignedStore(last_gc_state, gc_state, Align(sizeof(void*)))->setOrdering(AtomicOrdering::Release); + emit_gc_safepoint(builder, T_size, ptls, tbaa, true); } } } diff --git a/src/method.c b/src/method.c index 68542fdacabb6..6f53a6ff55c49 100644 --- a/src/method.c +++ b/src/method.c @@ -138,7 +138,7 @@ static jl_value_t *resolve_definition_effects(jl_value_t *expr, jl_module_t *mod return expr; } if (e->head == jl_foreigncall_sym) { - JL_NARGSV(ccall method definition, 5); // (fptr, rt, at, nreq, (cc, effects)) + JL_NARGSV(ccall method definition, 5); // (fptr, rt, at, nreq, (cc, effects, gc_safe)) jl_task_t *ct = jl_current_task; jl_value_t *rt = jl_exprarg(e, 1); jl_value_t *at = jl_exprarg(e, 2); @@ -172,11 +172,12 @@ static jl_value_t *resolve_definition_effects(jl_value_t *expr, jl_module_t *mod jl_value_t *cc = jl_quotenode_value(jl_exprarg(e, 4)); if (!jl_is_symbol(cc)) { JL_TYPECHK(ccall method definition, tuple, cc); - if (jl_nfields(cc) != 2) { + if (jl_nfields(cc) != 3) { jl_error("In ccall calling convention, expected two argument tuple or symbol."); } JL_TYPECHK(ccall method definition, symbol, jl_get_nth_field(cc, 0)); JL_TYPECHK(ccall method definition, uint16, jl_get_nth_field(cc, 1)); + JL_TYPECHK(ccall method definition, bool, jl_get_nth_field(cc, 2)); } } if (e->head == jl_call_sym && nargs > 0 && @@ -485,8 +486,12 @@ jl_code_info_t *jl_new_code_info_from_ir(jl_expr_t *ir) is_flag_stmt = 1; else if (jl_is_expr(st) && ((jl_expr_t*)st)->head == jl_return_sym) jl_array_ptr_set(body, j, jl_new_struct(jl_returnnode_type, jl_exprarg(st, 0))); - else if (jl_is_expr(st) && (((jl_expr_t*)st)->head == jl_foreigncall_sym || ((jl_expr_t*)st)->head == jl_cfunction_sym)) - li->has_fcall = 1; + else { + if (jl_is_expr(st) && ((jl_expr_t*)st)->head == jl_assign_sym) + st = jl_exprarg(st, 1); + if (jl_is_expr(st) && (((jl_expr_t*)st)->head == jl_foreigncall_sym || ((jl_expr_t*)st)->head == jl_cfunction_sym)) + li->has_fcall = 1; + } if (is_flag_stmt) jl_array_uint32_set(li->ssaflags, j, 0); else { @@ -1056,13 +1061,12 @@ JL_DLLEXPORT jl_value_t *jl_declare_const_gf(jl_module_t *mod, jl_sym_t *name) size_t new_world = jl_atomic_load_relaxed(&jl_world_counter) + 1; jl_binding_t *b = jl_get_binding_for_method_def(mod, name, new_world); jl_binding_partition_t *bpart = jl_get_binding_partition(b, new_world); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); jl_value_t *gf = NULL; - enum jl_partition_kind kind = decode_restriction_kind(pku); + enum jl_partition_kind kind = jl_binding_kind(bpart); if (!jl_bkind_is_some_guard(kind) && kind != BINDING_KIND_DECLARED && kind != BINDING_KIND_IMPLICIT) { - pku = jl_walk_binding_inplace(&b, &bpart, new_world); - if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { - gf = decode_restriction_value(pku); + jl_walk_binding_inplace(&b, &bpart, new_world); + if (jl_bkind_is_some_constant(jl_binding_kind(bpart))) { + gf = bpart->restriction; JL_GC_PROMISE_ROOTED(gf); jl_check_gf(gf, b->globalref->name); JL_UNLOCK(&world_counter_lock); @@ -1267,6 +1271,135 @@ JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata, return m; } +void jl_ctor_def(jl_value_t *ty, jl_value_t *functionloc) +{ + jl_datatype_t *dt = (jl_datatype_t*)jl_unwrap_unionall(ty); + JL_TYPECHK(ctor_def, datatype, (jl_value_t*)dt); + JL_TYPECHK(ctor_def, linenumbernode, functionloc); + jl_svec_t *fieldtypes = jl_get_fieldtypes(dt); + size_t nfields = jl_svec_len(fieldtypes); + size_t nparams = jl_subtype_env_size(ty); + jl_module_t *inmodule = dt->name->module; + jl_sym_t *file = (jl_sym_t*)jl_linenode_file(functionloc); + if (!jl_is_symbol(file)) + file = jl_empty_sym; + int32_t line = jl_linenode_line(functionloc); + + // argdata is svec(svec(types...), svec(typevars...), functionloc) + jl_svec_t *argdata = jl_alloc_svec(3); + jl_array_t *fieldkinds = NULL; + jl_code_info_t *body = NULL; + JL_GC_PUSH3(&argdata, &fieldkinds, &body); + jl_svecset(argdata, 2, functionloc); + jl_svec_t *tvars = jl_alloc_svec(nparams); + jl_svecset(argdata, 1, tvars); + jl_unionall_t *ua = (jl_unionall_t*)ty; + for (size_t i = 0; i < nparams; i++) { + assert(jl_is_unionall(ua)); + jl_svecset(tvars, i, ua->var); + ua = (jl_unionall_t*)ua->body; + } + jl_svec_t *names = dt->name->names; + + // define outer constructor (if all typevars are present (thus not definitely unconstrained) by the fields or other typevars which themselves are constrained) + int constrains_all_tvars = 1; + for (size_t i = nparams; i > 0; i--) { + jl_tvar_t *tv = (jl_tvar_t*)jl_svecref(tvars, i - 1); + int constrains_tvar = 0; + for (size_t i = 0; i < nfields; i++) { + jl_value_t *ft = jl_svecref(fieldtypes, i); + if (jl_has_typevar(ft, tv)) { + constrains_tvar = 1; + break; + } + } + for (size_t j = i; j < nparams; j++) { + jl_tvar_t *tv2 = (jl_tvar_t*)jl_svecref(tvars, j); + if (jl_has_typevar(tv2->ub, tv)) { // lb doesn't constrain, but jl_has_typevar doesn't have a way to specify that we care about may-constrain and not merely containment + constrains_tvar = 1; + break; + } + if (tv2 == tv) { + constrains_tvar = 0; + break; + } + } + if (!constrains_tvar) { + constrains_all_tvars = 0; + break; + } + } + if (constrains_all_tvars) { + jl_svec_t *atypes = jl_alloc_svec(nfields + 1); + jl_svecset(argdata, 0, atypes); + jl_svecset(atypes, 0, jl_wrap_Type(ty)); + for (size_t i = 0; i < nfields; i++) { + jl_value_t *ft = jl_svecref(fieldtypes, i); + jl_svecset(atypes, i + 1, ft); + } + body = jl_outer_ctor_body(ty, nfields, nparams, inmodule, jl_symbol_name(file), line); + if (names) { + jl_array_t *slotnames = body->slotnames; + for (size_t i = 0; i < nfields; i++) { + jl_array_ptr_set(slotnames, i + 1, jl_svecref(names, i)); + } + } + jl_method_def(argdata, NULL, body, inmodule); + if (nparams == 0) { + int all_Any = 1; // check if all fields are Any and the type is not parameterized, since inner constructor would be the same signature and code + for (size_t i = 0; i < nfields; i++) { + jl_value_t *ft = jl_svecref(fieldtypes, i); + if (ft != (jl_value_t*)jl_any_type) { + all_Any = 0; + break; + } + } + if (all_Any) { + JL_GC_POP(); + return; + } + } + } + + // define inner constructor + jl_svec_t *atypes = jl_svec_fill(nfields + 1, (jl_value_t*)jl_any_type); + jl_svecset(argdata, 0, atypes); + jl_value_t *typedt = (jl_value_t*)jl_wrap_Type((jl_value_t*)dt); + jl_svecset(atypes, 0, typedt); + fieldkinds = jl_alloc_vec_any(nfields); + for (size_t i = 0; i < nfields; i++) { + jl_value_t *ft = jl_svecref(fieldtypes, i); + int kind = ft == (jl_value_t*)jl_any_type ? -1 : 0; + // TODO: if more efficient to do so, we could reference the sparam instead of fieldtype + //if (jl_is_typevar(ft)) { + // for (size_t i = 0; i < nparams; i++) { + // if (jl_svecref(tvars, i) == ft) { + // kind = i + 1; + // break; // if repeated, must consider only the innermost + // } + // } + //} + jl_array_ptr_set(fieldkinds, i, jl_box_long(kind)); + } + // rewrap_unionall(Type{dt}, ty) + for (size_t i = nparams; i > 0; i--) { + jl_value_t *tv = jl_svecref(tvars, i - 1); + typedt = jl_new_struct(jl_unionall_type, tv, typedt); + jl_svecset(atypes, 0, typedt); + } + tvars = jl_emptysvec; + jl_svecset(argdata, 1, tvars); + body = jl_inner_ctor_body(fieldkinds, inmodule, jl_symbol_name(file), line); + if (names) { + jl_array_t *slotnames = body->slotnames; + for (size_t i = 0; i < nfields; i++) { + jl_array_ptr_set(slotnames, i + 1, jl_svecref(names, i)); + } + } + jl_method_def(argdata, NULL, body, inmodule); + JL_GC_POP(); +} + // root blocks // This section handles method roots. Roots are GC-preserved items needed to diff --git a/src/module.c b/src/module.c index 7fb2af14ef271..604e0b8e659fc 100644 --- a/src/module.c +++ b/src/module.c @@ -14,18 +14,15 @@ extern "C" { // In this translation unit and this translation unit only emit this symbol `extern` for use by julia EXTERN_INLINE_DEFINE uint8_t jl_bpart_get_kind(jl_binding_partition_t *bpart) JL_NOTSAFEPOINT; -extern inline enum jl_partition_kind decode_restriction_kind(jl_ptr_kind_union_t pku) JL_NOTSAFEPOINT; static jl_binding_partition_t *new_binding_partition(void) { jl_binding_partition_t *bpart = (jl_binding_partition_t*)jl_gc_alloc(jl_current_task->ptls, sizeof(jl_binding_partition_t), jl_binding_partition_type); - jl_atomic_store_relaxed(&bpart->restriction, encode_restriction(NULL, BINDING_KIND_GUARD)); + bpart->restriction = NULL; + bpart->kind = (size_t)BINDING_KIND_GUARD; bpart->min_world = 0; jl_atomic_store_relaxed(&bpart->max_world, (size_t)-1); jl_atomic_store_relaxed(&bpart->next, NULL); -#ifdef _P64 - bpart->reserved = 0; -#endif return bpart; } @@ -38,12 +35,12 @@ static int eq_bindings(jl_binding_partition_t *owner, jl_binding_t *alias, size_ jl_binding_partition_t *alias_bpart = jl_get_binding_partition(alias, world); if (owner == alias_bpart) return 1; - jl_ptr_kind_union_t owner_pku = jl_walk_binding_inplace(&ownerb, &owner, world); - jl_ptr_kind_union_t alias_pku = jl_walk_binding_inplace(&alias, &alias_bpart, world); - if (jl_bkind_is_some_constant(decode_restriction_kind(owner_pku)) && - jl_bkind_is_some_constant(decode_restriction_kind(alias_pku)) && - decode_restriction_value(owner_pku) && - decode_restriction_value(alias_pku) == decode_restriction_value(owner_pku)) + jl_walk_binding_inplace(&ownerb, &owner, world); + jl_walk_binding_inplace(&alias, &alias_bpart, world); + if (jl_bkind_is_some_constant(jl_binding_kind(owner)) && + jl_bkind_is_some_constant(jl_binding_kind(alias_bpart)) && + owner->restriction && + alias_bpart->restriction == owner->restriction) return 1; return owner == alias_bpart; } @@ -56,7 +53,8 @@ void jl_check_new_binding_implicit( modstack_t *tmp = st; for (; tmp != NULL; tmp = tmp->prev) { if (tmp->b == b) { - jl_atomic_store_relaxed(&new_bpart->restriction, encode_restriction(NULL, BINDING_KIND_FAILED /* BINDING_KIND_CYCLE */)); + new_bpart->restriction = NULL; + new_bpart->kind = BINDING_KIND_FAILED; /* BINDING_KIND_CYCLE */ return; } } @@ -67,6 +65,7 @@ void jl_check_new_binding_implicit( jl_binding_t *deprecated_impb = NULL; jl_binding_t *impb = NULL; + jl_binding_partition_t *impbpart = NULL; size_t min_world = new_bpart->min_world; size_t max_world = jl_atomic_load_relaxed(&new_bpart->max_world); @@ -92,7 +91,7 @@ void jl_check_new_binding_implicit( jl_module_t *imp = data.mod; JL_GC_PROMISE_ROOTED(imp); jl_binding_t *tempb = jl_get_module_binding(imp, var, 0); - if (tempb != NULL && tempb->exportp) { + if (tempb != NULL) { if (data.min_world > min_world) min_world = data.min_world; if (data.max_world < min_world) @@ -107,9 +106,20 @@ void jl_check_new_binding_implicit( if (tempbmax_world < max_world) max_world = tempbmax_world; + if ((tempbpart->kind & BINDING_FLAG_EXPORTED) == 0) + continue; + if (impb) { if (tempb->deprecated) continue; + if (jl_binding_kind(tempbpart) == BINDING_KIND_GUARD && + jl_binding_kind(impbpart) != BINDING_KIND_GUARD) + continue; + if (jl_binding_kind(impbpart) == BINDING_KIND_GUARD) { + impb = tempb; + impbpart = tempbpart; + continue; + } if (eq_bindings(tempbpart, impb, world)) continue; // Binding is ambiguous @@ -131,6 +141,7 @@ void jl_check_new_binding_implicit( } else { impb = tempb; + impbpart = tempbpart; } } } @@ -142,10 +153,13 @@ void jl_check_new_binding_implicit( new_bpart->min_world = min_world; jl_atomic_store_relaxed(&new_bpart->max_world, max_world); if (impb) { - jl_atomic_store_relaxed(&new_bpart->restriction, encode_restriction((jl_value_t*)impb, BINDING_KIND_IMPLICIT)); + new_bpart->kind = BINDING_KIND_IMPLICIT; + new_bpart->restriction = (jl_value_t*)impb; + jl_gc_wb(new_bpart, impb); // TODO: World age constraints? } else { - jl_atomic_store_relaxed(&new_bpart->restriction, encode_restriction(NULL, guard_kind)); + new_bpart->kind = guard_kind; + new_bpart->restriction = NULL; } JL_GC_POP(); return; @@ -178,6 +192,8 @@ STATIC_INLINE jl_binding_partition_t *jl_get_binding_partition_(jl_binding_t *b jl_atomic_store_relaxed(&new_bpart->max_world, max_world); JL_GC_PROMISE_ROOTED(new_bpart); // TODO: Analyzer doesn't understand MAYBE_UNROOTED properly jl_check_new_binding_implicit(new_bpart, b, st, world); + if (bpart && (bpart->kind & BINDING_FLAG_EXPORTED)) + new_bpart->kind |= BINDING_FLAG_EXPORTED; if (jl_atomic_cmpswap(insert, &bpart, new_bpart)) { jl_gc_wb(parent, new_bpart); return new_bpart; @@ -205,7 +221,7 @@ jl_binding_partition_t *jl_get_binding_partition_all(jl_binding_t *b, size_t min return bpart; } -JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, uint8_t default_names) +JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, uint8_t default_using_core, uint8_t self_name) { jl_task_t *ct = jl_current_task; const jl_uuid_t uuid_zero = {0, 0}; @@ -237,20 +253,114 @@ JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, ui jl_atomic_store_relaxed(&m->bindings, jl_emptysvec); jl_atomic_store_relaxed(&m->bindingkeyset, (jl_genericmemory_t*)jl_an_empty_memory_any); arraylist_new(&m->usings, 0); - if (jl_core_module && default_names) { - JL_GC_PUSH1(&m); - jl_module_using(m, jl_core_module); - // export own name, so "using Foo" makes "Foo" itself visible - jl_set_const(m, name, (jl_value_t*)m); - jl_module_public(m, name, 1); - JL_GC_POP(); + JL_GC_PUSH1(&m); + if (jl_core_module) { + // Bootstrap: Before jl_core_module is defined, we don't have enough infrastructure + // for bindings, so Core itself gets special handling in jltypes.c + if (default_using_core) { + jl_module_using(m, jl_core_module); + } + if (self_name) { + // export own name, so "using Foo" makes "Foo" itself visible + jl_set_const(m, name, (jl_value_t*)m); + jl_module_public(m, name, 1); + } } + JL_GC_POP(); return m; } +// Precondition: world_counter_lock is held +JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3( + jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val, + enum jl_partition_kind constant_kind, size_t new_world) +{ + jl_binding_partition_t *new_prev_bpart = NULL; + JL_GC_PUSH2(&val, &new_prev_bpart); + if (!b) { + b = jl_get_module_binding(mod, var, 1); + } + jl_binding_partition_t *new_bpart = NULL; + jl_binding_partition_t *bpart = jl_get_binding_partition(b, new_world); + while (!new_bpart) { + enum jl_partition_kind kind = jl_binding_kind(bpart); + if (jl_bkind_is_some_constant(kind)) { + if (!val) { + new_bpart = bpart; + break; + } + jl_value_t *old = bpart->restriction; + JL_GC_PROMISE_ROOTED(old); + if (jl_egal(val, old)) { + new_bpart = bpart; + break; + } + } else if (jl_bkind_is_some_import(kind) && kind != BINDING_KIND_IMPLICIT) { + jl_errorf("cannot declare %s.%s constant; it was already declared as an import", + jl_symbol_name(mod->name), jl_symbol_name(var)); + } else if (kind == BINDING_KIND_GLOBAL) { + jl_errorf("cannot declare %s.%s constant; it was already declared global", + jl_symbol_name(mod->name), jl_symbol_name(var)); + } + if (bpart->min_world == new_world) { + bpart->kind = constant_kind | (bpart->kind & 0xf0); + bpart->restriction = val; + if (val) + jl_gc_wb(bpart, val); + new_bpart = bpart; + } else { + new_bpart = jl_replace_binding_locked(b, bpart, val, constant_kind, new_world); + } + int need_backdate = new_world && val; + if (need_backdate) { + // We will backdate as long as this partition was never explicitly + // declared const, global, or imported. + jl_binding_partition_t *prev_bpart = bpart; + for (;;) { + enum jl_partition_kind prev_kind = jl_binding_kind(prev_bpart); + if (jl_bkind_is_some_constant(prev_kind) || prev_kind == BINDING_KIND_GLOBAL || + (jl_bkind_is_some_import(prev_kind))) { + need_backdate = 0; + break; + } + if (prev_bpart->min_world == 0) + break; + prev_bpart = jl_get_binding_partition(b, prev_bpart->min_world - 1); + } + } + // If backdate is required, replace each existing partition by a new one. + // We can't use one binding to cover the entire range, because we need to + // keep the flags partitioned. + if (need_backdate) { + jl_binding_partition_t *prev_bpart = bpart; + jl_binding_partition_t *backdate_bpart = new_binding_partition(); + new_prev_bpart = backdate_bpart; + while (1) { + backdate_bpart->kind = (size_t)BINDING_KIND_BACKDATED_CONST | (prev_bpart->kind & 0xf0); + backdate_bpart->restriction = val; + backdate_bpart->min_world = prev_bpart->min_world; + jl_gc_wb_fresh(backdate_bpart, val); + jl_atomic_store_relaxed(&backdate_bpart->max_world, + jl_atomic_load_relaxed(&prev_bpart->max_world)); + prev_bpart = jl_atomic_load_relaxed(&prev_bpart->next); + if (!prev_bpart) + break; + jl_binding_partition_t *next_prev_bpart = new_binding_partition(); + jl_atomic_store_relaxed(&backdate_bpart->next, next_prev_bpart); + jl_gc_wb(backdate_bpart, next_prev_bpart); + backdate_bpart = next_prev_bpart; + } + jl_atomic_store_release(&new_bpart->next, new_prev_bpart); + jl_gc_wb(new_bpart, new_prev_bpart); + } + } + JL_GC_POP(); + return new_bpart; +} + JL_DLLEXPORT jl_module_t *jl_new_module(jl_sym_t *name, jl_module_t *parent) { - return jl_new_module_(name, parent, 1); + return jl_new_module_(name, parent, 1, 1); } uint32_t jl_module_next_counter(jl_module_t *m) @@ -262,7 +372,7 @@ JL_DLLEXPORT jl_value_t *jl_f_new_module(jl_sym_t *name, uint8_t std_imports, ui { // TODO: should we prohibit this during incremental compilation? // TODO: the parent module is a lie - jl_module_t *m = jl_new_module_(name, jl_main_module, default_names); + jl_module_t *m = jl_new_module_(name, jl_main_module, default_names, default_names); JL_GC_PUSH1(&m); if (std_imports) jl_add_standard_imports(m); @@ -374,10 +484,10 @@ static jl_binding_t *new_binding(jl_module_t *mod, jl_sym_t *name) jl_atomic_store_relaxed(&b->partitions, NULL); b->globalref = NULL; b->backedges = NULL; - b->exportp = 0; b->publicp = 0; b->deprecated = 0; b->did_print_backdate_admonition = 0; + b->did_print_implicit_import_admonition = 0; JL_GC_PUSH1(&b); b->globalref = jl_new_globalref(mod, name, b); jl_gc_wb(b, b->globalref); @@ -419,8 +529,7 @@ static jl_module_t *jl_binding_dbgmodule(jl_binding_t *b, jl_module_t *m, jl_sym JL_DLLEXPORT void jl_check_binding_currently_writable(jl_binding_t *b, jl_module_t *m, jl_sym_t *s) { jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - enum jl_partition_kind kind = decode_restriction_kind(pku); + enum jl_partition_kind kind = jl_binding_kind(bpart); if (kind != BINDING_KIND_GLOBAL && kind != BINDING_KIND_DECLARED && !jl_bkind_is_some_constant(kind)) { if (jl_bkind_is_some_guard(kind)) { jl_errorf("Global %s.%s does not exist and cannot be assigned.\n" @@ -458,6 +567,7 @@ JL_DLLEXPORT jl_module_t *jl_get_module_of_binding(jl_module_t *m, jl_sym_t *var static NOINLINE void print_backdate_admonition(jl_binding_t *b) JL_NOTSAFEPOINT { + b->did_print_backdate_admonition = 1; jl_safe_printf( "WARNING: Detected access to binding `%s.%s` in a world prior to its definition world.\n" " Julia 1.12 has introduced more strict world age semantics for global bindings.\n" @@ -465,7 +575,6 @@ static NOINLINE void print_backdate_admonition(jl_binding_t *b) JL_NOTSAFEPOINT " !!! This code will error in future versions of Julia.\n" "Hint: Add an appropriate `invokelatest` around the access to this binding.\n", jl_symbol_name(b->globalref->mod->name), jl_symbol_name(b->globalref->name)); - b->did_print_backdate_admonition = 1; } static inline void check_backdated_binding(jl_binding_t *b, enum jl_partition_kind kind) JL_NOTSAFEPOINT @@ -479,13 +588,13 @@ static inline void check_backdated_binding(jl_binding_t *b, enum jl_partition_ki JL_DLLEXPORT jl_value_t *jl_get_binding_value(jl_binding_t *b) { 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); - enum jl_partition_kind kind = decode_restriction_kind(pku); + jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + enum jl_partition_kind kind = jl_binding_kind(bpart); if (jl_bkind_is_some_guard(kind)) return NULL; if (jl_bkind_is_some_constant(kind)) { check_backdated_binding(b, kind); - return decode_restriction_value(pku); + return bpart->restriction; } assert(!jl_bkind_is_some_import(kind)); return jl_atomic_load_relaxed(&b->value); @@ -494,13 +603,13 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value(jl_binding_t *b) JL_DLLEXPORT jl_value_t *jl_get_binding_value_seqcst(jl_binding_t *b) { 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); - enum jl_partition_kind kind = decode_restriction_kind(pku); + jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + enum jl_partition_kind kind = jl_binding_kind(bpart); if (jl_bkind_is_some_guard(kind)) return NULL; if (jl_bkind_is_some_constant(kind)) { check_backdated_binding(b, kind); - return decode_restriction_value(pku); + return bpart->restriction; } assert(!jl_bkind_is_some_import(kind)); return jl_atomic_load(&b->value); @@ -509,14 +618,14 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value_seqcst(jl_binding_t *b) JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_const(jl_binding_t *b) { 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); - enum jl_partition_kind kind = decode_restriction_kind(pku); + jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + enum jl_partition_kind kind = jl_binding_kind(bpart); if (jl_bkind_is_some_guard(kind)) return NULL; if (!jl_bkind_is_some_constant(kind)) return NULL; check_backdated_binding(b, kind); - return decode_restriction_value(pku); + return bpart->restriction; } JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_resolved_and_const(jl_binding_t *b) @@ -531,14 +640,13 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_resolved_and_const(jl_binding_t size_t max_world = jl_atomic_load_relaxed(&bpart->max_world); if (bpart->min_world > jl_current_task->world_age || jl_current_task->world_age > max_world) return NULL; - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - enum jl_partition_kind kind = decode_restriction_kind(pku); + enum jl_partition_kind kind = jl_binding_kind(bpart); if (jl_bkind_is_some_guard(kind)) return NULL; if (!jl_bkind_is_some_constant(kind)) return NULL; check_backdated_binding(b, kind); - return decode_restriction_value(pku); + return bpart->restriction; } JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_resolved(jl_binding_t *b) @@ -553,58 +661,44 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value_if_resolved(jl_binding_t *b) size_t max_world = jl_atomic_load_relaxed(&bpart->max_world); if (bpart->min_world > jl_current_task->world_age || jl_current_task->world_age > max_world) return NULL; - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - enum jl_partition_kind kind = decode_restriction_kind(pku); + enum jl_partition_kind kind = jl_binding_kind(bpart); if (jl_bkind_is_some_guard(kind)) return NULL; if (jl_bkind_is_some_import(kind)) return NULL; if (jl_bkind_is_some_constant(kind)) { check_backdated_binding(b, kind); - return decode_restriction_value(pku); + return bpart->restriction; } return jl_atomic_load_relaxed(&b->value); } JL_DLLEXPORT jl_value_t *jl_bpart_get_restriction_value(jl_binding_partition_t *bpart) { - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - jl_value_t *v = decode_restriction_value(pku); + jl_value_t *v = bpart->restriction; if (!v) jl_throw(jl_undefref_exception); return v; } -JL_DLLEXPORT jl_value_t *jl_reresolve_binding_value_seqcst(jl_binding_t *b) -{ - /* - jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - if (jl_bkind_is_some_guard(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)))) { - jl_resolve_owner(b, b->globalref->mod, b->globalref->name, NULL, jl_current_task->world_age); - } - */ - return jl_get_binding_value_seqcst(b); -} - // get binding for adding a method // like jl_get_binding_wr, but has different error paths and messages JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m, jl_sym_t *var, size_t new_world) { jl_binding_t *b = jl_get_module_binding(m, var, 1); jl_binding_partition_t *bpart = jl_get_binding_partition(b, new_world); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - enum jl_partition_kind kind = decode_restriction_kind(pku); - if (kind == BINDING_KIND_GLOBAL || kind == BINDING_KIND_DECLARED || jl_bkind_is_some_constant(decode_restriction_kind(pku))) + enum jl_partition_kind kind = jl_binding_kind(bpart); + if (kind == BINDING_KIND_GLOBAL || kind == BINDING_KIND_DECLARED || jl_bkind_is_some_constant(kind)) return b; if (jl_bkind_is_some_guard(kind)) { check_safe_newbinding(m, var); return b; } jl_binding_t *ownerb = b; - pku = jl_walk_binding_inplace(&ownerb, &bpart, new_world); + jl_walk_binding_inplace(&ownerb, &bpart, new_world); jl_value_t *f = NULL; - if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) - f = decode_restriction_value(pku); + if (jl_bkind_is_some_constant(jl_binding_kind(bpart))) + f = bpart->restriction; if (f == NULL) { if (kind == BINDING_KIND_IMPLICIT) { check_safe_newbinding(m, var); @@ -632,10 +726,12 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m, jl_sym_ else if (kind != BINDING_KIND_IMPORTED) { int should_error = strcmp(jl_symbol_name(var), "=>") == 0; jl_module_t *from = jl_binding_dbgmodule(b, m, var); - if (should_error) + if (should_error) { jl_errorf("invalid method definition in %s: function %s.%s must be explicitly imported to be extended", jl_module_debug_name(m), jl_module_debug_name(from), jl_symbol_name(var)); - else + } + else if (!b->did_print_implicit_import_admonition) { + b->did_print_implicit_import_admonition = 1; jl_printf(JL_STDERR, "WARNING: Constructor for type \"%s\" was extended in `%s` without explicit qualification or import.\n" " NOTE: Assumed \"%s\" refers to `%s.%s`. This behavior is deprecated and may differ in future versions.`\n" " NOTE: This behavior may have differed in Julia versions prior to 1.12.\n" @@ -645,6 +741,7 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m, jl_sym_ jl_symbol_name(var), jl_module_debug_name(from), jl_symbol_name(var), jl_symbol_name(var), jl_symbol_name(var), jl_module_debug_name(from), jl_symbol_name(var), jl_module_debug_name(from), jl_symbol_name(var)); + } } return ownerb; } @@ -654,9 +751,8 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m, jl_sym_ static jl_module_t *jl_binding_dbgmodule(jl_binding_t *b, jl_module_t *m, jl_sym_t *var) { jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - if (jl_bkind_is_some_import(decode_restriction_kind(pku))) { - return ((jl_binding_t*)decode_restriction_value(pku))->globalref->mod; + if (jl_bkind_is_some_import(jl_binding_kind(bpart))) { + return ((jl_binding_t*)bpart->restriction)->globalref->mod; } return m; } @@ -670,17 +766,17 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_type(jl_module_t *m, jl_sym_t *var) jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); if (b == NULL) return jl_nothing; - jl_ptr_kind_union_t pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) + jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + if (jl_bkind_is_some_guard(jl_binding_kind(bpart))) return jl_nothing; - if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + if (jl_bkind_is_some_constant(jl_binding_kind(bpart))) { // TODO: We would like to return the type of the constant, but // currently code relies on this returning any to bypass conversion // before an attempted assignment to a constant. - // return jl_typeof(jl_atomic_load_relaxed(&bpart->restriction)); + // return bpart->restriction; return (jl_value_t*)jl_any_type; } - return decode_restriction_value(pku); + return bpart->restriction; } JL_DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m, jl_sym_t *var) @@ -712,7 +808,7 @@ JL_DLLEXPORT int jl_is_imported(jl_module_t *m, jl_sym_t *var) { jl_binding_t *b = jl_get_module_binding(m, var, 0); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - return b && decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) == BINDING_KIND_IMPORTED; + return b && jl_binding_kind(bpart) == BINDING_KIND_IMPORTED; } extern const char *jl_filename; @@ -773,8 +869,6 @@ static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *asname, { jl_binding_t *b = jl_get_binding(from, s); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - (void)pku; if (b->deprecated) { if (jl_get_binding_value(b) == jl_nothing) { // silently skip importing deprecated values assigned to nothing (to allow later mutation) @@ -797,9 +891,9 @@ static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *asname, jl_binding_t *ownerb = b; jl_binding_partition_t *ownerbpart = bpart; - jl_ptr_kind_union_t owner_pku = jl_walk_binding_inplace(&ownerb, &ownerbpart, jl_current_task->world_age); + jl_walk_binding_inplace(&ownerb, &ownerbpart, jl_current_task->world_age); - if (jl_bkind_is_some_guard(decode_restriction_kind(owner_pku))) { + if (jl_bkind_is_some_guard(jl_binding_kind(ownerbpart))) { jl_printf(JL_STDERR, "WARNING: Imported binding %s.%s was undeclared at import time during import to %s.\n", jl_symbol_name(from->name), jl_symbol_name(s), @@ -814,10 +908,10 @@ static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *asname, JL_LOCK(&world_counter_lock); size_t new_world = jl_atomic_load_acquire(&jl_world_counter)+1; jl_binding_partition_t *btopart = jl_get_binding_partition(bto, new_world); - jl_ptr_kind_union_t bto_pku = jl_atomic_load_relaxed(&btopart->restriction); - if (decode_restriction_kind(bto_pku) == BINDING_KIND_GUARD || - decode_restriction_kind(bto_pku) == BINDING_KIND_IMPLICIT || - decode_restriction_kind(bto_pku) == BINDING_KIND_FAILED) { + enum jl_partition_kind btokind = jl_binding_kind(btopart); + if (btokind == BINDING_KIND_GUARD || + btokind == BINDING_KIND_IMPLICIT || + btokind == BINDING_KIND_FAILED) { jl_binding_partition_t *new_bpart = jl_replace_binding_locked(bto, btopart, (jl_value_t*)b, (explici != 0) ? BINDING_KIND_IMPORTED : BINDING_KIND_EXPLICIT, new_world); if (jl_atomic_load_relaxed(&new_bpart->max_world) == ~(size_t)0) @@ -827,12 +921,12 @@ static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *asname, else { if (eq_bindings(bpart, bto, new_world)) { // already imported - potentially upgrade _EXPLICIT to _IMPORTED - if (decode_restriction_kind(bto_pku) == BINDING_KIND_EXPLICIT && explici != 0) { + if (btokind == BINDING_KIND_EXPLICIT && explici != 0) { jl_replace_binding_locked(bto, btopart, (jl_value_t*)b, BINDING_KIND_IMPORTED, new_world); jl_atomic_store_release(&jl_world_counter, new_world); } } - else if (jl_bkind_is_some_import(decode_restriction_kind(bto_pku))) { + else if (jl_bkind_is_some_import(btokind)) { // already imported from somewhere else jl_printf(JL_STDERR, "WARNING: ignoring conflicting import of %s.%s into %s\n", @@ -915,13 +1009,13 @@ JL_DLLEXPORT void jl_module_using(jl_module_t *to, jl_module_t *from) jl_binding_t *b = (jl_binding_t*)jl_svecref(table, i); if ((void*)b == jl_nothing) break; - if (b->exportp) { + jl_binding_partition_t *frombpart = jl_get_binding_partition(b, new_world); + if (frombpart->kind & BINDING_FLAG_EXPORTED) { jl_sym_t *var = b->globalref->name; jl_binding_t *tob = jl_get_module_binding(to, var, 0); if (tob) { jl_binding_partition_t *tobpart = jl_get_binding_partition(tob, new_world); - jl_ptr_kind_union_t tobpku = jl_atomic_load_relaxed(&tobpart->restriction); - enum jl_partition_kind kind = decode_restriction_kind(tobpku); + enum jl_partition_kind kind = jl_binding_kind(tobpart); if (kind == BINDING_KIND_IMPLICIT || jl_bkind_is_some_guard(kind)) { jl_replace_binding_locked(tob, tobpart, NULL, BINDING_KIND_IMPLICIT_RECOMPUTE, new_world); } @@ -954,17 +1048,25 @@ JL_DLLEXPORT jl_value_t *jl_get_module_binding_or_nothing(jl_module_t *m, jl_sym JL_DLLEXPORT void jl_module_public(jl_module_t *from, jl_sym_t *s, int exported) { jl_binding_t *b = jl_get_module_binding(from, s, 1); + JL_LOCK(&world_counter_lock); + size_t new_world = jl_atomic_load_acquire(&jl_world_counter)+1; + jl_binding_partition_t *bpart = jl_get_binding_partition(b, new_world); + int was_exported = (bpart->kind & BINDING_FLAG_EXPORTED) != 0; if (b->publicp) { // check for conflicting declarations - if (b->exportp && !exported) + if (was_exported && !exported) jl_errorf("cannot declare %s.%s public; it is already declared exported", jl_symbol_name(from->name), jl_symbol_name(s)); - if (!b->exportp && exported) + if (!was_exported && exported) jl_errorf("cannot declare %s.%s exported; it is already declared public", jl_symbol_name(from->name), jl_symbol_name(s)); } b->publicp = 1; - b->exportp |= exported; + if (was_exported != exported) { + jl_replace_binding_locked2(b, bpart, bpart->restriction, bpart->kind | BINDING_FLAG_EXPORTED, new_world); + jl_atomic_store_release(&jl_world_counter, new_world); + } + JL_UNLOCK(&world_counter_lock); } JL_DLLEXPORT int jl_boundp(jl_module_t *m, jl_sym_t *var, int allow_import) // unlike most queries here, this is currently seq_cst @@ -975,16 +1077,15 @@ JL_DLLEXPORT int jl_boundp(jl_module_t *m, jl_sym_t *var, int allow_import) // u jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); if (!bpart) return 0; - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); if (!allow_import) { - if (!bpart || jl_bkind_is_some_import(decode_restriction_kind(pku))) + if (!bpart || jl_bkind_is_some_import(jl_binding_kind(bpart))) return 0; } else { - pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); } - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) + if (jl_bkind_is_some_guard(jl_binding_kind(bpart))) return 0; - if (jl_bkind_is_defined_constant(decode_restriction_kind(pku))) { + if (jl_bkind_is_defined_constant(jl_binding_kind(bpart))) { // N.B.: No backdated check for isdefined return 1; } @@ -995,13 +1096,14 @@ JL_DLLEXPORT int jl_defines_or_exports_p(jl_module_t *m, jl_sym_t *var) { jl_binding_t *b = jl_get_module_binding(m, var, 0); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - return b && (b->exportp || decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) == BINDING_KIND_GLOBAL); + return b && ((bpart->kind & BINDING_FLAG_EXPORTED) || jl_binding_kind(bpart) == BINDING_KIND_GLOBAL); } JL_DLLEXPORT int jl_module_exports_p(jl_module_t *m, jl_sym_t *var) { jl_binding_t *b = jl_get_module_binding(m, var, 0); - return b && b->exportp; + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + return b && (bpart->kind & BINDING_FLAG_EXPORTED); } JL_DLLEXPORT int jl_module_public_p(jl_module_t *m, jl_sym_t *var) @@ -1108,7 +1210,8 @@ JL_DLLEXPORT void jl_set_const(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var jl_binding_partition_t *bpart = jl_get_binding_partition(bp, jl_current_task->world_age); bpart->min_world = 0; jl_atomic_store_release(&bpart->max_world, ~(size_t)0); - jl_atomic_store_release(&bpart->restriction, encode_restriction(val, BINDING_KIND_CONST)); + bpart->kind = BINDING_KIND_CONST | (bpart->kind & 0xf0); + bpart->restriction = val; jl_gc_wb(bpart, val); } @@ -1164,23 +1267,36 @@ JL_DLLEXPORT void jl_maybe_add_binding_backedge(jl_globalref_t *gr, jl_module_t JL_DLLEXPORT jl_binding_partition_t *jl_replace_binding_locked(jl_binding_t *b, jl_binding_partition_t *old_bpart, jl_value_t *restriction_val, enum jl_partition_kind kind, size_t new_world) +{ + // Copy flags from old bpart + return jl_replace_binding_locked2(b, old_bpart, restriction_val, (size_t)kind | (size_t)(old_bpart->kind & 0xf0), + new_world); +} + +JL_DLLEXPORT jl_binding_partition_t *jl_replace_binding_locked2(jl_binding_t *b, + jl_binding_partition_t *old_bpart, jl_value_t *restriction_val, size_t kind, size_t new_world) { assert(jl_atomic_load_relaxed(&b->partitions) == old_bpart); jl_atomic_store_release(&old_bpart->max_world, new_world-1); jl_binding_partition_t *new_bpart = new_binding_partition(); + JL_GC_PUSH1(&new_bpart); new_bpart->min_world = new_world; - if (kind == BINDING_KIND_IMPLICIT_RECOMPUTE) { + if ((kind & 0x0f) == BINDING_KIND_IMPLICIT_RECOMPUTE) { assert(!restriction_val); - jl_check_new_binding_implicit(new_bpart, b, NULL, new_world); + jl_check_new_binding_implicit(new_bpart /* callee rooted */, b, NULL, new_world); + new_bpart->kind |= kind & 0xf0; + } + else { + new_bpart->kind = kind; + new_bpart->restriction = restriction_val; + jl_gc_wb_fresh(new_bpart, restriction_val); } - else - jl_atomic_store_relaxed(&new_bpart->restriction, encode_restriction(restriction_val, kind)); jl_atomic_store_relaxed(&new_bpart->next, old_bpart); - jl_gc_wb(new_bpart, old_bpart); + jl_gc_wb_fresh(new_bpart, old_bpart); jl_atomic_store_release(&b->partitions, new_bpart); jl_gc_wb(b, new_bpart); - + JL_GC_POP(); if (jl_typeinf_world != 1) { jl_task_t *ct = jl_current_task; @@ -1220,7 +1336,7 @@ JL_DLLEXPORT int jl_globalref_is_const(jl_globalref_t *gr) jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); if (!bpart) return 0; - return jl_bkind_is_some_constant(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction))); + return jl_bkind_is_some_constant(jl_binding_kind(bpart)); } JL_DLLEXPORT void jl_disable_binding(jl_globalref_t *gr) @@ -1230,7 +1346,7 @@ JL_DLLEXPORT void jl_disable_binding(jl_globalref_t *gr) b = jl_get_module_binding(gr->mod, gr->name, 1); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - if (decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) == BINDING_KIND_GUARD) { + if (jl_binding_kind(bpart) == BINDING_KIND_GUARD) { // Already guard return; } @@ -1244,8 +1360,8 @@ JL_DLLEXPORT int jl_is_const(jl_module_t *m, jl_sym_t *var) { jl_binding_t *b = jl_get_binding(m, var); 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); - return b && jl_bkind_is_some_constant(decode_restriction_kind(pku)); + jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + return b && jl_bkind_is_some_constant(jl_binding_kind(bpart)); } // set the deprecated flag for a binding: @@ -1296,10 +1412,9 @@ jl_value_t *jl_check_binding_assign_value(jl_binding_t *b JL_PROPAGATES_ROOT, jl { JL_GC_PUSH1(&rhs); // callee-rooted jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - enum jl_partition_kind kind = decode_restriction_kind(pku); + enum jl_partition_kind kind = jl_binding_kind(bpart); if (jl_bkind_is_some_constant(kind)) { - jl_value_t *old = decode_restriction_value(pku); + jl_value_t *old = bpart->restriction; JL_GC_PROMISE_ROOTED(old); if (jl_egal(rhs, old)) { JL_GC_POP(); @@ -1309,7 +1424,7 @@ jl_value_t *jl_check_binding_assign_value(jl_binding_t *b JL_PROPAGATES_ROOT, jl jl_symbol_name(mod->name), jl_symbol_name(var)); } assert(kind == BINDING_KIND_DECLARED || kind == BINDING_KIND_GLOBAL); - jl_value_t *old_ty = kind == BINDING_KIND_DECLARED ? (jl_value_t*)jl_any_type : decode_restriction_value(pku); + jl_value_t *old_ty = kind == BINDING_KIND_DECLARED ? (jl_value_t*)jl_any_type : bpart->restriction; JL_GC_PROMISE_ROOTED(old_ty); if (old_ty != (jl_value_t*)jl_any_type && jl_typeof(rhs) != old_ty) { if (!jl_isa(rhs, old_ty)) @@ -1347,12 +1462,12 @@ JL_DLLEXPORT jl_value_t *jl_checked_replace(jl_binding_t *b, jl_module_t *mod, j JL_DLLEXPORT jl_value_t *jl_checked_modify(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *op, jl_value_t *rhs) { jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - assert(!jl_bkind_is_some_guard(decode_restriction_kind(pku)) && !jl_bkind_is_some_import(decode_restriction_kind(pku))); - if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) + enum jl_partition_kind kind = jl_binding_kind(bpart); + assert(!jl_bkind_is_some_guard(kind) && !jl_bkind_is_some_import(kind)); + if (jl_bkind_is_some_constant(kind)) jl_errorf("invalid assignment to constant %s.%s", jl_symbol_name(mod->name), jl_symbol_name(var)); - jl_value_t *ty = decode_restriction_value(pku); + jl_value_t *ty = bpart->restriction; JL_GC_PROMISE_ROOTED(ty); return modify_value(ty, &b->value, (jl_value_t*)b, op, rhs, 1, mod, var); } @@ -1399,7 +1514,7 @@ void append_module_names(jl_array_t* a, jl_module_t *m, int all, int imported, i int hidden = jl_symbol_name(asname)[0]=='#'; int main_public = (m == jl_main_module && !(asname == jl_eval_sym || asname == jl_include_sym)); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - enum jl_partition_kind kind = decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)); + enum jl_partition_kind kind = jl_binding_kind(bpart); if (((b->publicp) || (imported && (kind == BINDING_KIND_CONST_IMPORT || kind == BINDING_KIND_IMPORTED)) || (usings && kind == BINDING_KIND_EXPLICIT) || @@ -1416,7 +1531,8 @@ void append_exported_names(jl_array_t* a, jl_module_t *m, int all) jl_binding_t *b = (jl_binding_t*)jl_svecref(table, i); if ((void*)b == jl_nothing) break; - if (b->exportp && (all || !b->deprecated)) + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + if ((bpart->kind & BINDING_FLAG_EXPORTED) && (all || !b->deprecated)) _append_symbol_to_bindings_array(a, b->globalref->name); } } @@ -1486,7 +1602,7 @@ JL_DLLEXPORT void jl_clear_implicit_imports(jl_module_t *m) if ((void*)b == jl_nothing) break; jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - if (decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) == BINDING_KIND_IMPLICIT) { + if (jl_binding_kind(bpart) == BINDING_KIND_IMPLICIT) { jl_atomic_store_relaxed(&b->partitions, NULL); } } diff --git a/src/staticdata.c b/src/staticdata.c index c0fa8931e27c8..3e916a93c6382 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -515,8 +515,8 @@ static const jl_fptr_args_t id_to_fptrs[] = { &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, &jl_f_ifelse, &jl_f__structtype, &jl_f__abstracttype, &jl_f__primitivetype, - &jl_f__typebody, &jl_f__setsuper, &jl_f__equiv_typedef, &jl_f_get_binding_type, - &jl_f_opaque_closure_call, &jl_f_donotdelete, &jl_f_compilerbarrier, + &jl_f__typebody, &jl_f__setsuper, &jl_f__equiv_typedef, &jl_f__defaultctors, + &jl_f_opaque_closure_call, &jl_f_donotdelete, &jl_f_compilerbarrier, &jl_f_get_binding_type, &jl_f_getglobal, &jl_f_setglobal, &jl_f_swapglobal, &jl_f_modifyglobal, &jl_f_replaceglobal, &jl_f_setglobalonce, &jl_f_finalizer, &jl_f__compute_sparams, &jl_f__svec_ref, &jl_f_current_scope, @@ -550,6 +550,9 @@ typedef struct { jl_array_t *link_ids_gctags; jl_array_t *link_ids_gvars; jl_array_t *link_ids_external_fnvars; + jl_array_t *method_roots_list; + htable_t method_roots_index; + uint64_t worklist_key; jl_ptls_t ptls; jl_image_t *image; int8_t incremental; @@ -931,22 +934,57 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ int is_relocatable = jl_is_code_info(inferred) || (jl_is_string(inferred) && jl_string_len(inferred) > 0 && jl_string_data(inferred)[jl_string_len(inferred) - 1]); if (!is_relocatable) { - record_field_change((jl_value_t**)&ci->inferred, jl_nothing); + inferred = jl_nothing; } else if (def->source == NULL) { // don't delete code from optimized opaque closures that can't be reconstructed (and builtins) } else 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); + inferred = jl_nothing; } else if (native_functions && // don't delete any code if making a ji file (ci->owner == jl_nothing) && // don't delete code for external interpreters !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 + inferred = jl_nothing; + } + if (inferred == jl_nothing) { record_field_change((jl_value_t**)&ci->inferred, jl_nothing); } + else if (jl_is_string(inferred)) { + // New roots for external methods + if (jl_object_in_image((jl_value_t*)def)) { + void **pfound = ptrhash_bp(&s->method_roots_index, def); + if (*pfound == HT_NOTFOUND) { + *pfound = def; + size_t nwithkey = nroots_with_key(def, s->worklist_key); + if (nwithkey) { + jl_array_ptr_1d_push(s->method_roots_list, (jl_value_t*)def); + jl_array_t *newroots = jl_alloc_vec_any(nwithkey); + jl_array_ptr_1d_push(s->method_roots_list, (jl_value_t*)newroots); + rle_iter_state rootiter = rle_iter_init(0); + uint64_t *rletable = NULL; + size_t nblocks2 = 0; + size_t nroots = jl_array_nrows(def->roots); + size_t k = 0; + if (def->root_blocks) { + rletable = jl_array_data(def->root_blocks, uint64_t); + nblocks2 = jl_array_nrows(def->root_blocks); + } + while (rle_iter_increment(&rootiter, nroots, rletable, nblocks2)) { + if (rootiter.key == s->worklist_key) { + jl_value_t *newroot = jl_array_ptr_ref(def->roots, rootiter.i); + jl_queue_for_serialization(s, newroot); + jl_array_ptr_set(newroots, k++, newroot); + } + } + assert(k == nwithkey); + } + } + } + } } } } @@ -1001,7 +1039,7 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ } else if (jl_is_binding_partition(v)) { jl_binding_partition_t *bpart = (jl_binding_partition_t*)v; - jl_queue_for_serialization_(s, decode_restriction_value(jl_atomic_load_relaxed(&bpart->restriction)), 1, immediate); + jl_queue_for_serialization_(s, bpart->restriction, 1, immediate); jl_queue_for_serialization_(s, get_replaceable_field((jl_value_t**)&bpart->next, 0), 1, immediate); } else if (layout->nfields > 0) { @@ -1680,13 +1718,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED } else if (jl_is_binding_partition(v)) { jl_binding_partition_t *bpart = (jl_binding_partition_t*)v; - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - jl_value_t *restriction_val = decode_restriction_value(pku); - static_assert(offsetof(jl_binding_partition_t, restriction) == 0, "BindingPartition layout mismatch"); - write_pointerfield(s, restriction_val); -#ifndef _P64 - write_uint(f, decode_restriction_kind(pku)); -#endif + write_pointerfield(s, bpart->restriction); size_t max_world = jl_atomic_load_relaxed(&bpart->max_world); if (s->incremental) { if (max_world == ~(size_t)0) { @@ -1708,13 +1740,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED write_uint(f, max_world); } write_pointerfield(s, (jl_value_t*)jl_atomic_load_relaxed(&bpart->next)); -#ifdef _P64 - write_uint(f, decode_restriction_kind(pku)); // This will be moved back into place during deserialization (if necessary) - static_assert(sizeof(jl_binding_partition_t) == 5*sizeof(void*), "BindingPartition layout mismatch"); -#else - write_uint(f, 0); - static_assert(sizeof(jl_binding_partition_t) == 6*sizeof(void*), "BindingPartition layout mismatch"); -#endif + write_uint(f, bpart->kind); } else { // Generic object::DataType serialization by field @@ -2873,10 +2899,9 @@ JL_DLLEXPORT jl_value_t *jl_as_global_root(jl_value_t *val, int insert) return val; } -static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *newly_inferred, uint64_t worklist_key, +static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *newly_inferred, /* outputs */ jl_array_t **extext_methods JL_REQUIRE_ROOTED_SLOT, jl_array_t **new_ext_cis JL_REQUIRE_ROOTED_SLOT, - jl_array_t **method_roots_list JL_REQUIRE_ROOTED_SLOT, jl_array_t **edges JL_REQUIRE_ROOTED_SLOT) { // extext_methods: [method1, ...], worklist-owned "extending external" methods added to functions owned by modules outside the worklist @@ -2900,15 +2925,12 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new } if (edges) { + // Extract `edges` now (from info prepared by jl_collect_methcache_from_mod) size_t world = jl_atomic_load_acquire(&jl_world_counter); - // Extract `new_ext_cis` and `edges` now (from info prepared by jl_collect_methcache_from_mod) - *method_roots_list = jl_alloc_vec_any(0); - // Collect the new method roots for external specializations - jl_collect_new_roots(*method_roots_list, *new_ext_cis, worklist_key); *edges = jl_alloc_vec_any(0); jl_collect_internal_cis(*edges, world); } - internal_methods = NULL; + internal_methods = NULL; // global JL_GC_POP(); } @@ -2916,8 +2938,7 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new // In addition to the system image (where `worklist = NULL`), this can also save incremental images with external linkage static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, jl_array_t *worklist, jl_array_t *extext_methods, - jl_array_t *new_ext_cis, jl_array_t *method_roots_list, - jl_array_t *edges) JL_GC_DISABLED + jl_array_t *new_ext_cis, jl_array_t *edges) { htable_new(&field_replace, 0); htable_new(&bits_replace, 0); @@ -3047,9 +3068,14 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, s.link_ids_gctags = jl_alloc_array_1d(jl_array_int32_type, 0); s.link_ids_gvars = jl_alloc_array_1d(jl_array_int32_type, 0); s.link_ids_external_fnvars = jl_alloc_array_1d(jl_array_int32_type, 0); + s.method_roots_list = NULL; + htable_new(&s.method_roots_index, 0); + if (worklist) { + s.method_roots_list = jl_alloc_vec_any(0); + s.worklist_key = jl_worklist_key(worklist); + } jl_value_t **const*const tags = get_tags(); // worklist == NULL ? get_tags() : NULL; - if (worklist == NULL) { // empty!(Core.ARGS) if (jl_core_module != NULL) { @@ -3094,8 +3120,6 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, jl_queue_for_serialization(&s, extext_methods); // Queue the new specializations jl_queue_for_serialization(&s, new_ext_cis); - // Queue the new roots - jl_queue_for_serialization(&s, method_roots_list); // Queue the edges jl_queue_for_serialization(&s, edges); } @@ -3106,7 +3130,15 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, if (jl_options.trim) record_gvars(&s, &MIs); jl_serialize_reachable(&s); - // step 1.3: prune (garbage collect) special weak references from the jl_global_roots_list + // Beyond this point, all content should already have been visited, so now we can prune + // the rest and add some internal root arrays. + // step 1.3: include some other special roots + if (s.incremental) { + // Queue the new roots array + jl_queue_for_serialization(&s, s.method_roots_list); + jl_serialize_reachable(&s); + } + // step 1.4: prune (garbage collect) special weak references from the jl_global_roots_list if (worklist == NULL) { global_roots_list = jl_alloc_memory_any(0); global_roots_keyset = jl_alloc_memory_any(0); @@ -3122,7 +3154,7 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, jl_queue_for_serialization(&s, global_roots_keyset); jl_serialize_reachable(&s); } - // step 1.4: prune (garbage collect) some special weak references from + // step 1.5: prune (garbage collect) some special weak references from // built-in type caches too for (i = 0; i < serialization_queue.len; i++) { jl_value_t *v = (jl_value_t*)serialization_queue.items[i]; @@ -3142,7 +3174,8 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, if (ptrhash_get(&serialization_order, mi) == HT_NOTFOUND) jl_svecset(specializations, i, jl_nothing); } - } else if (jl_is_module(v)) { + } + else if (jl_is_module(v)) { jl_prune_module_bindings((jl_module_t*)v); } } @@ -3274,7 +3307,7 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, jl_write_value(&s, jl_module_init_order); jl_write_value(&s, extext_methods); jl_write_value(&s, new_ext_cis); - jl_write_value(&s, method_roots_list); + jl_write_value(&s, s.method_roots_list); jl_write_value(&s, edges); } write_uint32(f, jl_array_len(s.link_ids_gctags)); @@ -3305,6 +3338,7 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, arraylist_free(&s.gctags_list); arraylist_free(&gvars); arraylist_free(&external_fns); + htable_free(&s.method_roots_index); htable_free(&field_replace); htable_free(&bits_replace); htable_free(&serialization_order); @@ -3365,18 +3399,17 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli } jl_array_t *mod_array = NULL, *extext_methods = NULL, *new_ext_cis = NULL; - jl_array_t *method_roots_list = NULL, *edges = NULL; + jl_array_t *edges = NULL; int64_t checksumpos = 0; int64_t checksumpos_ff = 0; int64_t datastartpos = 0; - JL_GC_PUSH5(&mod_array, &extext_methods, &new_ext_cis, &method_roots_list, &edges); + JL_GC_PUSH4(&mod_array, &extext_methods, &new_ext_cis, &edges); if (worklist) { mod_array = jl_get_loaded_modules(); // __toplevel__ modules loaded in this session (from Base.loaded_modules_array) // Generate _native_data` if (_native_data != NULL) { - jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist), - &extext_methods, &new_ext_cis, NULL, NULL); + jl_prepare_serialization_data(mod_array, newly_inferred, &extext_methods, &new_ext_cis, NULL); jl_precompile_toplevel_module = (jl_module_t*)jl_array_ptr_ref(worklist, jl_array_len(worklist)-1); *_native_data = jl_precompile_worklist(worklist, extext_methods, new_ext_cis); jl_precompile_toplevel_module = NULL; @@ -3407,8 +3440,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli assert((ct->reentrant_timing & 0b1110) == 0); ct->reentrant_timing |= 0b1000; if (worklist) { - jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist), - &extext_methods, &new_ext_cis, &method_roots_list, &edges); + jl_prepare_serialization_data(mod_array, newly_inferred, &extext_methods, &new_ext_cis, &edges); if (!emit_split) { write_int32(f, 0); // No clone_targets write_padding(f, LLT_ALIGN(ios_pos(f), JL_CACHE_BYTE_ALIGNMENT) - ios_pos(f)); @@ -3420,7 +3452,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli } if (_native_data != NULL) native_functions = *_native_data; - jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_ext_cis, method_roots_list, edges); + jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_ext_cis, edges); if (_native_data != NULL) native_functions = NULL; // make sure we don't run any Julia code concurrently before this point @@ -3510,16 +3542,17 @@ static void jl_validate_binding_partition(jl_binding_t *b, jl_binding_partition_ if (jl_atomic_load_relaxed(&bpart->max_world) != ~(size_t)0) return; - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - enum jl_partition_kind kind = decode_restriction_kind(pku); + size_t raw_kind = bpart->kind; + enum jl_partition_kind kind = (enum jl_partition_kind)(raw_kind & 0x0f); if (!jl_bkind_is_some_import(kind)) return; - jl_binding_t *imported_binding = (jl_binding_t*)decode_restriction_value(pku); + jl_binding_t *imported_binding = (jl_binding_t*)bpart->restriction; jl_binding_partition_t *latest_imported_bpart = jl_atomic_load_relaxed(&imported_binding->partitions); if (!latest_imported_bpart) return; if (kind == BINDING_KIND_IMPLICIT || kind == BINDING_KIND_FAILED) { jl_check_new_binding_implicit(bpart, b, NULL, jl_atomic_load_relaxed(&jl_world_counter)); + bpart->kind |= (raw_kind & 0xf0); if (bpart->min_world > jl_require_world) goto invalidated; } @@ -3550,7 +3583,7 @@ static void jl_validate_binding_partition(jl_binding_t *b, jl_binding_partition_ jl_validate_binding_partition(bedge, jl_atomic_load_relaxed(&bedge->partitions), mod_idx); } } - if (b->exportp) { + if (bpart->kind & BINDING_FLAG_EXPORTED) { jl_module_t *mod = b->globalref->mod; jl_sym_t *name = b->globalref->name; JL_LOCK(&mod->lock); @@ -4015,25 +4048,6 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl } } } - // Move the binding bits back to their correct place -#ifdef _P64 - jl_svec_t *table = jl_atomic_load_relaxed(&mod->bindings); - for (size_t i = 0; i < jl_svec_len(table); i++) { - jl_binding_t *b = (jl_binding_t*)jl_svecref(table, i); - if ((jl_value_t*)b == jl_nothing) - continue; - jl_binding_partition_t *bpart = jl_atomic_load_relaxed(&b->partitions); - while (bpart) { - jl_ptr_kind_union_t pku = encode_restriction( - (jl_value_t*)jl_atomic_load_relaxed(&bpart->restriction), - (enum jl_partition_kind)bpart->reserved); - jl_atomic_store_relaxed(&bpart->restriction, pku); - bpart->reserved = 0; - bpart = jl_atomic_load_relaxed(&bpart->next); - } - } - -#endif } else { abort(); @@ -4217,7 +4231,6 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i &edges, &base, &ccallable_list, &cachesizes); JL_SIGATOMIC_END(); - // No special processing of `new_ext_cis` is required because recaching handled it // Add roots to methods jl_copy_roots(method_roots_list, jl_worklist_key((jl_array_t*)restored)); // Insert method extensions and handle edges @@ -4390,7 +4403,7 @@ JL_DLLEXPORT void _jl_promote_ci_to_current(jl_code_instance_t *ci, size_t valid jl_value_t *edge = jl_svecref(edges, i); if (!jl_is_code_instance(edge)) continue; - _jl_promote_ci_to_current(ci, validated_world); + _jl_promote_ci_to_current((jl_code_instance_t *)edge, validated_world); } } diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 1985357321a3a..163eb1ea9cad5 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -261,51 +261,6 @@ static jl_array_t *queue_external_cis(jl_array_t *list) return new_ext_cis; } -// New roots for external methods -static void jl_collect_new_roots(jl_array_t *roots, jl_array_t *new_ext_cis, uint64_t key) -{ - htable_t mset; - htable_new(&mset, 0); - size_t l = new_ext_cis ? jl_array_nrows(new_ext_cis) : 0; - for (size_t i = 0; i < l; i++) { - jl_code_instance_t *ci = (jl_code_instance_t*)jl_array_ptr_ref(new_ext_cis, i); - assert(jl_is_code_instance(ci)); - jl_method_t *m = jl_get_ci_mi(ci)->def.method; - assert(jl_is_method(m)); - ptrhash_put(&mset, (void*)m, (void*)m); - } - int nwithkey; - void *const *table = mset.table; - jl_array_t *newroots = NULL; - JL_GC_PUSH1(&newroots); - for (size_t i = 0; i < mset.size; i += 2) { - if (table[i+1] != HT_NOTFOUND) { - jl_method_t *m = (jl_method_t*)table[i]; - assert(jl_is_method(m)); - nwithkey = nroots_with_key(m, key); - if (nwithkey) { - jl_array_ptr_1d_push(roots, (jl_value_t*)m); - newroots = jl_alloc_vec_any(nwithkey); - jl_array_ptr_1d_push(roots, (jl_value_t*)newroots); - rle_iter_state rootiter = rle_iter_init(0); - uint64_t *rletable = NULL; - size_t nblocks2 = 0, nroots = jl_array_nrows(m->roots), k = 0; - if (m->root_blocks) { - rletable = jl_array_data(m->root_blocks, uint64_t); - nblocks2 = jl_array_nrows(m->root_blocks); - } - while (rle_iter_increment(&rootiter, nroots, rletable, nblocks2)) - if (rootiter.key == key) - jl_array_ptr_set(newroots, k++, jl_array_ptr_ref(m->roots, rootiter.i)); - assert(k == nwithkey); - } - } - } - JL_GC_POP(); - htable_free(&mset); -} - - // For every method: // - if the method is owned by a worklist module, add it to the list of things to be // verified on reloading diff --git a/src/toplevel.c b/src/toplevel.c index 321ef8c79dac0..d931b59178ce6 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -135,7 +135,10 @@ static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex } int is_parent__toplevel__ = jl_is__toplevel__mod(parent_module); - jl_module_t *newm = jl_new_module(name, is_parent__toplevel__ ? NULL : parent_module); + // If we have `Base`, don't also try to import `Core` - the `Base` exports are a superset. + // While we allow multiple imports of the same binding from different modules, various error printing + // performs reflection on which module a binding came from and we'd prefer users see "Base" here. + jl_module_t *newm = jl_new_module_(name, is_parent__toplevel__ ? NULL : parent_module, std_imports && jl_base_module != NULL ? 0 : 1, 1); jl_value_t *form = (jl_value_t*)newm; JL_GC_PUSH1(&form); JL_LOCK(&jl_modules_mutex); @@ -154,32 +157,7 @@ static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex } } else { - jl_binding_t *b = jl_get_module_binding(parent_module, name, 1); - jl_binding_partition_t *bpart = jl_get_binding_partition(b, ct->world_age); - jl_ptr_kind_union_t pku = encode_restriction(NULL, BINDING_KIND_UNDEF_CONST); - jl_ptr_kind_union_t new_pku = encode_restriction((jl_value_t*)newm, BINDING_KIND_CONST); - if (!jl_atomic_cmpswap(&bpart->restriction, &pku, new_pku)) { - if (decode_restriction_kind(pku) != BINDING_KIND_CONST) { - jl_declare_constant_val(b, parent_module, name, (jl_value_t*)newm); - } else { - // As a special exception allow binding replacement of modules - if (!jl_is_module(decode_restriction_value(pku))) { - jl_errorf("invalid redefinition of constant %s", jl_symbol_name(name)); - } - if (jl_generating_output()) - jl_errorf("cannot replace module %s during compilation", jl_symbol_name(name)); - jl_printf(JL_STDERR, "WARNING: replacing module %s.\n", jl_symbol_name(name)); - pku = jl_atomic_exchange(&bpart->restriction, new_pku); - } - jl_gc_wb(bpart, newm); - if (decode_restriction_value(pku) != NULL && jl_is_module(decode_restriction_value(pku))) { - // create a hidden gc root for the old module - JL_LOCK(&jl_modules_mutex); - uintptr_t *refcnt = (uintptr_t*)ptrhash_bp(&jl_current_modules, decode_restriction_value(pku)); - *refcnt += 1; - JL_UNLOCK(&jl_modules_mutex); - } - } + jl_declare_constant_val(NULL, parent_module, name, (jl_value_t*)newm); } if (parent_module == jl_main_module && name == jl_symbol("Base")) { @@ -329,29 +307,27 @@ void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type, in global_type = (jl_value_t*)jl_any_type; while (1) { bpart = jl_get_binding_partition(b, new_world); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - enum jl_partition_kind kind = decode_restriction_kind(pku); + enum jl_partition_kind kind = jl_binding_kind(bpart); if (kind != BINDING_KIND_GLOBAL) { if (jl_bkind_is_some_guard(kind) || kind == BINDING_KIND_DECLARED || kind == BINDING_KIND_IMPLICIT) { - if (decode_restriction_kind(pku) == new_kind) { + if (kind == new_kind) { if (!set_type) goto done; goto check_type; } check_safe_newbinding(gm, gs); if (bpart->min_world == new_world) { - if (jl_atomic_cmpswap(&bpart->restriction, &pku, encode_restriction(global_type, new_kind))) { - break; - } - if (set_type) - jl_gc_wb(bpart, set_type); + bpart->kind = new_kind | (bpart->kind & 0xf0); + bpart->restriction = global_type; + if (global_type) + jl_gc_wb(bpart, global_type); continue; } else { jl_replace_binding_locked(b, bpart, global_type, new_kind, new_world); } break; } else if (set_type) { - if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + if (jl_bkind_is_some_constant(kind)) { jl_errorf("cannot set type for constant %s.%s.", jl_symbol_name(gm->name), jl_symbol_name(gs)); } else { @@ -363,7 +339,7 @@ void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type, in if (set_type) { check_type: ; - jl_value_t *old_ty = decode_restriction_value(pku); + jl_value_t *old_ty = bpart->restriction; JL_GC_PROMISE_ROOTED(old_ty); if (!jl_types_equal(set_type, old_ty)) { jl_errorf("cannot set type for global %s.%s. It already has a value or is already set to a different type.", @@ -457,7 +433,7 @@ static void expr_attributes(jl_value_t *v, jl_array_t *body, int *has_ccall, int if (jl_is_intrinsic(called) && jl_unbox_int32(called) == (int)llvmcall) { *has_ccall = 1; } - if (called == jl_builtin__typebody) { + if (called == jl_builtin__typebody) { // TODO: rely on latestworld instead of function callee detection here (or add it to jl_is_toplevel_only_expr) *has_defs = 1; } } @@ -679,14 +655,13 @@ static void import_module(jl_module_t *JL_NONNULL m, jl_module_t *import, jl_sym // TODO: this is a bit race-y with what error message we might print jl_binding_t *b = jl_get_module_binding(m, name, 1); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - enum jl_partition_kind kind = decode_restriction_kind(pku); + enum jl_partition_kind kind = jl_binding_kind(bpart); if (kind != BINDING_KIND_GUARD && kind != BINDING_KIND_FAILED && kind != BINDING_KIND_DECLARED && kind != BINDING_KIND_IMPLICIT) { // Unlike regular constant declaration, we allow this as long as we eventually end up at a constant. - pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); - if (decode_restriction_kind(pku) == BINDING_KIND_CONST || decode_restriction_kind(pku) == BINDING_KIND_BACKDATED_CONST || decode_restriction_kind(pku) == BINDING_KIND_CONST_IMPORT) { + jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + if (jl_binding_kind(bpart) == BINDING_KIND_CONST || jl_binding_kind(bpart) == BINDING_KIND_BACKDATED_CONST || jl_binding_kind(bpart) == BINDING_KIND_CONST_IMPORT) { // Already declared (e.g. on another thread) or imported. - if (decode_restriction_value(pku) == (jl_value_t*)import) + if (bpart->restriction == (jl_value_t*)import) return; } jl_errorf("importing %s into %s conflicts with an existing global", @@ -754,82 +729,6 @@ static void jl_eval_errorf(jl_module_t *m, const char *filename, int lineno, con JL_GC_POP(); } -JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3( - jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val, - enum jl_partition_kind constant_kind, size_t new_world) -{ - JL_GC_PUSH1(&val); - if (!b) { - b = jl_get_module_binding(mod, var, 1); - } - jl_binding_partition_t *new_bpart = NULL; - jl_binding_partition_t *bpart = jl_get_binding_partition(b, new_world); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - while (!new_bpart) { - enum jl_partition_kind kind = decode_restriction_kind(pku); - if (jl_bkind_is_some_constant(kind)) { - if (!val) { - new_bpart = bpart; - break; - } - jl_value_t *old = decode_restriction_value(pku); - JL_GC_PROMISE_ROOTED(old); - if (jl_egal(val, old)) { - new_bpart = bpart; - break; - } - } else if (jl_bkind_is_some_import(kind) && kind != BINDING_KIND_IMPLICIT) { - jl_errorf("cannot declare %s.%s constant; it was already declared as an import", - jl_symbol_name(mod->name), jl_symbol_name(var)); - } else if (kind == BINDING_KIND_GLOBAL) { - jl_errorf("cannot declare %s.%s constant; it was already declared global", - jl_symbol_name(mod->name), jl_symbol_name(var)); - } - if (bpart->min_world == new_world) { - if (!jl_atomic_cmpswap(&bpart->restriction, &pku, encode_restriction(val, constant_kind))) { - continue; - } - jl_gc_wb(bpart, val); - new_bpart = bpart; - } else { - new_bpart = jl_replace_binding_locked(b, bpart, val, constant_kind, new_world); - } - int need_backdate = new_world && val; - if (need_backdate) { - // We will backdate as long as this partition was never explicitly - // declared const, global, or imported. - jl_binding_partition_t *prev_bpart = bpart; - for (;;) { - jl_ptr_kind_union_t prev_pku = jl_atomic_load_relaxed(&prev_bpart->restriction); - enum jl_partition_kind prev_kind = decode_restriction_kind(prev_pku); - if (jl_bkind_is_some_constant(prev_kind) || prev_kind == BINDING_KIND_GLOBAL || - (jl_bkind_is_some_import(prev_kind))) { - need_backdate = 0; - break; - } - if (prev_bpart->min_world == 0) - break; - prev_bpart = jl_get_binding_partition(b, prev_bpart->min_world - 1); - } - } - // If backdate is required, rewrite all previous binding partitions to - // backdated const - if (need_backdate) { - // We will backdate as long as this partition was never explicitly - // declared const, global, or *explicitly* imported. - jl_binding_partition_t *prev_bpart = bpart; - for (;;) { - jl_atomic_store_relaxed(&prev_bpart->restriction, encode_restriction(val, BINDING_KIND_BACKDATED_CONST)); - if (prev_bpart->min_world == 0) - break; - prev_bpart = jl_get_binding_partition(b, prev_bpart->min_world - 1); - } - } - } - JL_GC_POP(); - return new_bpart; -} - JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2( jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val, enum jl_partition_kind constant_kind) diff --git a/stdlib/Statistics.version b/stdlib/Statistics.version index 3df70d30ba7e6..e6f0a62b3cec5 100644 --- a/stdlib/Statistics.version +++ b/stdlib/Statistics.version @@ -1,4 +1,4 @@ STATISTICS_BRANCH = master -STATISTICS_SHA1 = d49c2bf4f81e1efb4980a35fe39c815ef8396297 +STATISTICS_SHA1 = 77bd5707f143eb624721a7df28ddef470e70ecef 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/test/ccall.jl b/test/ccall.jl index b10504de21abc..f193d16fc09e2 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -1745,6 +1745,7 @@ using Base: ccall_macro_parse, ccall_macro_lower :Cvoid, # returntype Any[:Cstring, :Cstring, :Cint], # argument types Any["%s = %d\n", :name, :value], # argument symbols + false, # is gc_safe 1 # number of required arguments (for varargs) ) end @@ -1757,7 +1758,7 @@ end )::Cstring))...) @test call == Base.remove_linenums!( quote - ccall($(Expr(:escape, :((:func, libstring)))), $(Expr(:cconv, :ccall, 0)), $(Expr(:escape, :Cstring)), ($(Expr(:escape, :Cstring)), $(Expr(:escape, :Cint)), $(Expr(:escape, :Cint))), $(Expr(:escape, :str)), $(Expr(:escape, :num1)), $(Expr(:escape, :num2))) + ccall($(Expr(:escape, :((:func, libstring)))), $(Expr(:cconv, (:ccall, UInt16(0), false), 0)), $(Expr(:escape, :Cstring)), ($(Expr(:escape, :Cstring)), $(Expr(:escape, :Cint)), $(Expr(:escape, :Cint))), $(Expr(:escape, :str)), $(Expr(:escape, :num1)), $(Expr(:escape, :num2))) end) local fptr = :x @@ -1966,3 +1967,15 @@ let llvm = sprint(code_llvm, world_counter, ()) # the world age should be -1 in generated functions (or other pure contexts) @test (generated_world_counter() == reinterpret(UInt, -1)) end + +function gc_safe_ccall() + # jl_rand is marked as JL_NOTSAFEPOINT + @ccall gc_safe=true jl_rand()::UInt64 +end + +let llvm = sprint(code_llvm, gc_safe_ccall, ()) + # check that the call works + @test gc_safe_ccall() isa UInt64 + # check for the gc_safe store + @test occursin("store atomic i8 2", llvm) +end diff --git a/test/core.jl b/test/core.jl index ee47eba0d2c7d..ef1d6784cac94 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8443,3 +8443,8 @@ f_call_me() = invoke(f_invoke_me, f_invoke_me_ci) f_invalidate_me() = 2 @test_throws ErrorException invoke(f_invoke_me, f_invoke_me_ci) @test_throws ErrorException f_call_me() + +myfun57023a(::Type{T}) where {T} = (x = @ccall mycfun()::Ptr{T}; x) +@test only(code_lowered(myfun57023a)).has_fcall +myfun57023b(::Type{T}) where {T} = (x = @cfunction myfun57023a Ptr{T} (Ref{T},); x) +@test only(code_lowered(myfun57023b)).has_fcall diff --git a/test/math.jl b/test/math.jl index 7070fe63ba931..d9cfd411124ed 100644 --- a/test/math.jl +++ b/test/math.jl @@ -1505,7 +1505,19 @@ end @test E^n == Inf @test E^float(n) == Inf - # #55633 + # issue #55831 + @testset "literal pow zero sign" begin + @testset "T: $T" for T ∈ (Float16, Float32, Float64, BigFloat) + @testset "literal `-1`" begin + @test -0.0 === Float64(T(-Inf)^-1) + end + @testset "`Int(-1)`" begin + @test -0.0 === Float64(T(-Inf)^Int(-1)) + end + end + end + + # issue #55633 struct Issue55633_1 <: Number end struct Issue55633_3 <: Number end struct Issue55633_9 <: Number end diff --git a/test/misc.jl b/test/misc.jl index fef573e9fc747..bcc7ff69339a9 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -1574,7 +1574,12 @@ end @testset "Base docstrings" begin undoc = Docs.undocumented_names(Base) @test_broken isempty(undoc) - @test undoc == [:BufferStream, :CanonicalIndexError, :CapturedException, :Filesystem, :IOServer, :InvalidStateException, :Order, :PipeEndpoint, :ScopedValues, :Sort, :TTY] + @test isempty(setdiff(undoc, [:BufferStream, :CanonicalIndexError, :CapturedException, :Filesystem, :IOServer, :InvalidStateException, :Order, :PipeEndpoint, :ScopedValues, :Sort, :TTY, :AtomicMemoryRef, :Exception, :GenericMemoryRef, :GlobalRef, :IO, :LineNumberNode, :MemoryRef, :Method, :SegmentationFault, :TypeVar, :arrayref, :arrayset, :arraysize, :const_arrayref])) +end + +exported_names(m) = filter(s -> Base.isexported(m, s), names(m)) +@testset "Base re-exports Core" begin + @test issubset(exported_names(Core), exported_names(Base)) end @testset "Base.Libc docstrings" begin diff --git a/test/precompile.jl b/test/precompile.jl index e6c987326e44c..6d106acc185f5 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -2285,4 +2285,30 @@ precompile_test_harness("Constprop CodeInstance invalidation") do load_path end end +precompile_test_harness("llvmcall validation") do load_path + write(joinpath(load_path, "LLVMCall.jl"), + """ + module LLVMCall + using Base: llvmcall + @noinline do_llvmcall() = llvmcall("ret i32 0", UInt32, Tuple{}) + do_llvmcall2() = do_llvmcall() + do_llvmcall2() + end + """) + # Also test with --pkgimages=no + testcode = """ + insert!(LOAD_PATH, 1, $(repr(load_path))) + insert!(DEPOT_PATH, 1, $(repr(load_path))) + using LLVMCall + LLVMCall.do_llvmcall2() + """ + @test readchomp(`$(Base.julia_cmd()) --pkgimages=no -E $(testcode)`) == repr(UInt32(0)) + # Now the regular way + @eval using LLVMCall + invokelatest() do + @test LLVMCall.do_llvmcall2() == UInt32(0) + @test first(methods(LLVMCall.do_llvmcall)).specializations.cache.max_world === typemax(UInt) + end +end + finish_precompile_test!() diff --git a/test/rebinding.jl b/test/rebinding.jl index 23feb8ded0e56..2f343fd86eb9a 100644 --- a/test/rebinding.jl +++ b/test/rebinding.jl @@ -193,10 +193,31 @@ module RebindingPrecompile Core.eval(Export2, :(const import_me2 = 22)) end invokelatest() do - # Currently broken - # @test_throws UndefVarError ImportTest.f_use_binding2() + @test_throws UndefVarError ImportTest.f_use_binding2() end end finish_precompile_test!() end + +module Regression + using Test + + # Issue #57377 + module GeoParams57377 + module B + using ...GeoParams57377 + export S + struct S end + module C + using ..GeoParams57377 + h() = S() + x -> nothing + end + end + + using .B + export S + end + @test GeoParams57377.B.C.h() == GeoParams57377.B.C.S() +end diff --git a/test/syntax.jl b/test/syntax.jl index ebf6ed2e0b837..366b7f5e63679 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3977,7 +3977,7 @@ module ReplacementContainer const x = 1 end const Old = ReplaceMe - @test_warn r"WARNING: replacing module ReplaceMe" @eval module ReplaceMe + @eval module ReplaceMe const x = 2 end end @@ -4089,3 +4089,17 @@ abstract type A57267{S, T} end B57267{S} = A57267{S, 1} const C57267 = B57267 end + +# #57404 - Binding ambiguity resolution ignores guard bindings +module Ambig57404 + module A + export S + end + using .A + module B + const S = 1 + export S + end + using .B +end +@test Ambig57404.S == 1