diff --git a/base/Base.jl b/base/Base.jl index 84e10ca788ba2e..57dcebe01e7282 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -271,8 +271,7 @@ end BUILDROOT::String = "" -baremodule BuildSettings -end +baremodule BuildSettings end let i = 1 global BUILDROOT diff --git a/base/boot.jl b/base/boot.jl index 608e273d4b514f..b291d249e004d7 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -214,7 +214,7 @@ export nfields, throw, tuple, ===, isdefined, eval, # access to globals getglobal, setglobal!, swapglobal!, modifyglobal!, replaceglobal!, setglobalonce!, - # ifelse, sizeof # not exported, to avoid conflicting with Base + # ifelse, sizeof, finalize # not exported, to avoid conflicting with Base # type reflection <:, typeof, isa, typeassert, # method reflection diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 0e2272524a0ed2..80c7cf60e6814f 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1625,19 +1625,18 @@ function try_resolve_finalizer!(ir::IRCode, alloc_idx::Int, finalizer_idx::Int, foreach(note_defuse!, defuse.defs) insert_bb != 0 || return nothing # verify post-dominator of all uses exists + # Figure out the exact statement where we're going to inline the finalizer. + loc = insert_idx === nothing ? first(ir.cfg.blocks[insert_bb].stmts) : insert_idx::Int + attach_after = insert_idx !== nothing + flag = info isa FinalizerInfo ? flags_for_effects(info.effects) : IR_FLAG_NULL + finalizer_stmt = ir[SSAValue(finalizer_idx)][:stmt] + if !OptimizationParams(inlining.interp).assume_fatal_throw # Collect all reachable blocks between the finalizer registration and the # insertion point blocks = reachable_blocks(ir.cfg, finalizer_bb, insert_bb) # Check #3 - function check_range_nothrow(s::Int, e::Int) - return all(s:e) do sidx::Int - sidx == finalizer_idx && return true - sidx == alloc_idx && return true - return is_nothrow(ir, SSAValue(sidx)) - end - end for bb in blocks range = ir.cfg.blocks[bb].stmts s, e = first(range), last(range) @@ -1648,18 +1647,23 @@ function try_resolve_finalizer!(ir::IRCode, alloc_idx::Int, finalizer_idx::Int, if bb == finalizer_bb s = finalizer_idx end - check_range_nothrow(s, e) || return nothing + all(s:e) do sidx::Int + sidx == finalizer_idx && return true + sidx == alloc_idx && return true + return is_nothrow(ir, SSAValue(sidx)) + end && continue + + # An exception may be thrown between the finalizer registration and the point + # where the objectโ€™s lifetime ends (`insert_idx`): In such cases, we canโ€™t + # remove the finalizer registration, but we can still insert a `Core.finalize` + # call at `insert_idx` while leaving the registration intact. + newinst = add_flag(NewInstruction(Expr(:call, GlobalRef(Core, :finalize), finalizer_stmt.args[3]), Nothing), flag) + insert_node!(ir, loc, newinst, attach_after) + return nothing end end - # Ok, legality check complete. Figure out the exact statement where we're - # going to inline the finalizer. - loc = insert_idx === nothing ? first(ir.cfg.blocks[insert_bb].stmts) : insert_idx::Int - attach_after = insert_idx !== nothing - - finalizer_stmt = ir[SSAValue(finalizer_idx)][:stmt] argexprs = Any[finalizer_stmt.args[2], finalizer_stmt.args[3]] - flag = info isa FinalizerInfo ? flags_for_effects(info.effects) : IR_FLAG_NULL if length(finalizer_stmt.args) >= 4 inline = finalizer_stmt.args[4] if inline === nothing diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index a6b7e53c6f3201..403184aa1057ff 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -743,6 +743,7 @@ add_tfunc(donotdelete, 0, INT_INF, @nospecs((๐•ƒ::AbstractLattice, args...)->No end add_tfunc(compilerbarrier, 2, 2, compilerbarrier_tfunc, 5) add_tfunc(Core.finalizer, 2, 4, @nospecs((๐•ƒ::AbstractLattice, args...)->Nothing), 5) +add_tfunc(Core.finalize, 1, 1, @nospecs((๐•ƒ::AbstractLattice, o)->Nothing), 100) @nospecs function compilerbarrier_nothrow(setting, val) return isa(setting, Const) && contains_is((:type, :const, :conditional), setting.val) @@ -2288,8 +2289,11 @@ function _builtin_nothrow(๐•ƒ::AbstractLattice, @nospecialize(f::Builtin), argt return true elseif f === Core.finalizer 2 <= na <= 4 || return false - # Core.finalizer does no error checking - that's done in Base.finalizer + # `Core.finalizer` does no error checking - that's done in Base.finalizer return true + elseif f === Core.finalize + na == 2 || return false + return true # `Core.finalize` does no error checking elseif f === Core.compilerbarrier na == 2 || return false return compilerbarrier_nothrow(argtypes[1], nothing) diff --git a/base/gcutils.jl b/base/gcutils.jl index 84a184537ffc05..8a905dd8ad205b 100644 --- a/base/gcutils.jl +++ b/base/gcutils.jl @@ -1,6 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license - """ WeakRef(x) @@ -99,8 +98,7 @@ end Immediately run finalizers registered for object `x`. """ -finalize(@nospecialize(o)) = ccall(:jl_finalize_th, Cvoid, (Any, Any,), - current_task(), o) +finalize(@nospecialize(o)) = Core.finalize(o) """ Base.GC diff --git a/src/builtin_proto.h b/src/builtin_proto.h index 7fbd5557586757..6c263d218a7868 100644 --- a/src/builtin_proto.h +++ b/src/builtin_proto.h @@ -37,6 +37,7 @@ DECLARE_BUILTIN(compilerbarrier); DECLARE_BUILTIN(current_scope); DECLARE_BUILTIN(donotdelete); DECLARE_BUILTIN(fieldtype); +DECLARE_BUILTIN(finalize); DECLARE_BUILTIN(finalizer); DECLARE_BUILTIN(getfield); DECLARE_BUILTIN(getglobal); diff --git a/src/builtins.c b/src/builtins.c index 96c4cec0f50876..5f37b563ba901c 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -2010,6 +2010,13 @@ JL_CALLABLE(jl_f_finalizer) return jl_nothing; } +JL_CALLABLE(jl_f_finalize) +{ + JL_NARGS(finalize, 1, 1); + jl_finalize(args[0]); + return jl_nothing; +} + JL_CALLABLE(jl_f__compute_sparams) { JL_NARGSV(_compute_sparams, 1); @@ -2442,6 +2449,7 @@ void jl_init_primitives(void) JL_GC_DISABLED jl_builtin_donotdelete = add_builtin_func("donotdelete", jl_f_donotdelete); jl_builtin_compilerbarrier = add_builtin_func("compilerbarrier", jl_f_compilerbarrier); add_builtin_func("finalizer", jl_f_finalizer); + add_builtin_func("finalize", jl_f_finalize); add_builtin_func("_compute_sparams", jl_f__compute_sparams); add_builtin_func("_svec_ref", jl_f__svec_ref); jl_builtin_current_scope = add_builtin_func("current_scope", jl_f_current_scope); diff --git a/src/codegen.cpp b/src/codegen.cpp index bcda527416676f..8db2efad05b67d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1607,47 +1607,48 @@ static const auto jlintrinsic_func = new JuliaFunction<>{XSTR(jl_f_intrinsic_cal static const auto &builtin_func_map() { static std::map*> builtins = { - { jl_f_is_addr, new JuliaFunction<>{XSTR(jl_f_is), get_func_sig, get_func_attrs} }, - { jl_f_typeof_addr, new JuliaFunction<>{XSTR(jl_f_typeof), get_func_sig, get_func_attrs} }, - { jl_f_sizeof_addr, new JuliaFunction<>{XSTR(jl_f_sizeof), get_func_sig, get_func_attrs} }, - { jl_f_issubtype_addr, new JuliaFunction<>{XSTR(jl_f_issubtype), get_func_sig, get_func_attrs} }, - { jl_f_isa_addr, new JuliaFunction<>{XSTR(jl_f_isa), get_func_sig, get_func_attrs} }, - { jl_f_typeassert_addr, new JuliaFunction<>{XSTR(jl_f_typeassert), get_func_sig, get_func_attrs} }, - { jl_f_ifelse_addr, new JuliaFunction<>{XSTR(jl_f_ifelse), get_func_sig, get_func_attrs} }, - { jl_f__apply_iterate_addr, new JuliaFunction<>{XSTR(jl_f__apply_iterate), get_func_sig, get_func_attrs} }, - { jl_f__apply_pure_addr, new JuliaFunction<>{XSTR(jl_f__apply_pure), get_func_sig, get_func_attrs} }, - { jl_f__call_latest_addr, new JuliaFunction<>{XSTR(jl_f__call_latest), get_func_sig, get_func_attrs} }, - { jl_f__call_in_world_addr, new JuliaFunction<>{XSTR(jl_f__call_in_world), get_func_sig, get_func_attrs} }, + { jl_f_is_addr, new JuliaFunction<>{XSTR(jl_f_is), get_func_sig, get_func_attrs} }, + { jl_f_typeof_addr, new JuliaFunction<>{XSTR(jl_f_typeof), get_func_sig, get_func_attrs} }, + { jl_f_sizeof_addr, new JuliaFunction<>{XSTR(jl_f_sizeof), get_func_sig, get_func_attrs} }, + { jl_f_issubtype_addr, new JuliaFunction<>{XSTR(jl_f_issubtype), get_func_sig, get_func_attrs} }, + { jl_f_isa_addr, new JuliaFunction<>{XSTR(jl_f_isa), get_func_sig, get_func_attrs} }, + { jl_f_typeassert_addr, new JuliaFunction<>{XSTR(jl_f_typeassert), get_func_sig, get_func_attrs} }, + { jl_f_ifelse_addr, new JuliaFunction<>{XSTR(jl_f_ifelse), get_func_sig, get_func_attrs} }, + { jl_f__apply_iterate_addr, new JuliaFunction<>{XSTR(jl_f__apply_iterate), get_func_sig, get_func_attrs} }, + { jl_f__apply_pure_addr, new JuliaFunction<>{XSTR(jl_f__apply_pure), get_func_sig, get_func_attrs} }, + { jl_f__call_latest_addr, new JuliaFunction<>{XSTR(jl_f__call_latest), get_func_sig, get_func_attrs} }, + { jl_f__call_in_world_addr, new JuliaFunction<>{XSTR(jl_f__call_in_world), get_func_sig, get_func_attrs} }, { jl_f__call_in_world_total_addr, new JuliaFunction<>{XSTR(jl_f__call_in_world_total), get_func_sig, get_func_attrs} }, - { jl_f_throw_addr, new JuliaFunction<>{XSTR(jl_f_throw), get_func_sig, get_func_attrs} }, - { jl_f_throw_methoderror_addr, new JuliaFunction<>{XSTR(jl_f_throw_methoderror), get_func_sig, get_func_attrs} }, - { jl_f_tuple_addr, jltuple_func }, - { jl_f_svec_addr, new JuliaFunction<>{XSTR(jl_f_svec), get_func_sig, get_func_attrs} }, - { jl_f_applicable_addr, new JuliaFunction<>{XSTR(jl_f_applicable), get_func_sig, get_func_attrs} }, - { jl_f_invoke_addr, new JuliaFunction<>{XSTR(jl_f_invoke), get_func_sig, get_func_attrs} }, - { jl_f_isdefined_addr, new JuliaFunction<>{XSTR(jl_f_isdefined), get_func_sig, get_func_attrs} }, - { jl_f_getfield_addr, new JuliaFunction<>{XSTR(jl_f_getfield), get_func_sig, get_func_attrs} }, - { jl_f_setfield_addr, new JuliaFunction<>{XSTR(jl_f_setfield), get_func_sig, get_func_attrs} }, - { jl_f_swapfield_addr, new JuliaFunction<>{XSTR(jl_f_swapfield), get_func_sig, get_func_attrs} }, - { jl_f_modifyfield_addr, new JuliaFunction<>{XSTR(jl_f_modifyfield), get_func_sig, get_func_attrs} }, - { jl_f_fieldtype_addr, new JuliaFunction<>{XSTR(jl_f_fieldtype), get_func_sig, get_func_attrs} }, - { jl_f_nfields_addr, new JuliaFunction<>{XSTR(jl_f_nfields), get_func_sig, get_func_attrs} }, - { jl_f__expr_addr, new JuliaFunction<>{XSTR(jl_f__expr), get_func_sig, get_func_attrs} }, - { jl_f__typevar_addr, new JuliaFunction<>{XSTR(jl_f__typevar), get_func_sig, get_func_attrs} }, - { jl_f_memoryref_addr, new JuliaFunction<>{XSTR(jl_f_memoryref), get_func_sig, get_func_attrs} }, - { jl_f_memoryrefoffset_addr, new JuliaFunction<>{XSTR(jl_f_memoryrefoffset), get_func_sig, get_func_attrs} }, - { jl_f_memoryrefset_addr, new JuliaFunction<>{XSTR(jl_f_memoryrefset), get_func_sig, get_func_attrs} }, - { jl_f_memoryrefswap_addr, new JuliaFunction<>{XSTR(jl_f_memoryrefswap), get_func_sig, get_func_attrs} }, - { jl_f_memoryrefreplace_addr, new JuliaFunction<>{XSTR(jl_f_memoryrefreplace), get_func_sig, get_func_attrs} }, - { jl_f_memoryrefmodify_addr, new JuliaFunction<>{XSTR(jl_f_memoryrefmodify), get_func_sig, get_func_attrs} }, - { jl_f_memoryrefsetonce_addr, new JuliaFunction<>{XSTR(jl_f_memoryrefsetonce), get_func_sig, get_func_attrs} }, - { jl_f_memoryref_isassigned_addr,new JuliaFunction<>{XSTR(jl_f_memoryref_isassigned), get_func_sig, get_func_attrs} }, - { jl_f_apply_type_addr, new JuliaFunction<>{XSTR(jl_f_apply_type), get_func_sig, get_func_attrs} }, - { jl_f_donotdelete_addr, new JuliaFunction<>{XSTR(jl_f_donotdelete), get_donotdelete_sig, get_donotdelete_func_attrs} }, - { jl_f_compilerbarrier_addr, new JuliaFunction<>{XSTR(jl_f_compilerbarrier), get_func_sig, get_func_attrs} }, - { jl_f_finalizer_addr, new JuliaFunction<>{XSTR(jl_f_finalizer), get_func_sig, get_func_attrs} }, - { jl_f__svec_ref_addr, new JuliaFunction<>{XSTR(jl_f__svec_ref), get_func_sig, get_func_attrs} }, - { jl_f_current_scope_addr, new JuliaFunction<>{XSTR(jl_f_current_scope), get_func_sig, get_func_attrs} }, + { jl_f_throw_addr, new JuliaFunction<>{XSTR(jl_f_throw), get_func_sig, get_func_attrs} }, + { jl_f_throw_methoderror_addr, new JuliaFunction<>{XSTR(jl_f_throw_methoderror), get_func_sig, get_func_attrs} }, + { jl_f_tuple_addr, jltuple_func }, + { jl_f_svec_addr, new JuliaFunction<>{XSTR(jl_f_svec), get_func_sig, get_func_attrs} }, + { jl_f_applicable_addr, new JuliaFunction<>{XSTR(jl_f_applicable), get_func_sig, get_func_attrs} }, + { jl_f_invoke_addr, new JuliaFunction<>{XSTR(jl_f_invoke), get_func_sig, get_func_attrs} }, + { jl_f_isdefined_addr, new JuliaFunction<>{XSTR(jl_f_isdefined), get_func_sig, get_func_attrs} }, + { jl_f_getfield_addr, new JuliaFunction<>{XSTR(jl_f_getfield), get_func_sig, get_func_attrs} }, + { jl_f_setfield_addr, new JuliaFunction<>{XSTR(jl_f_setfield), get_func_sig, get_func_attrs} }, + { jl_f_swapfield_addr, new JuliaFunction<>{XSTR(jl_f_swapfield), get_func_sig, get_func_attrs} }, + { jl_f_modifyfield_addr, new JuliaFunction<>{XSTR(jl_f_modifyfield), get_func_sig, get_func_attrs} }, + { jl_f_fieldtype_addr, new JuliaFunction<>{XSTR(jl_f_fieldtype), get_func_sig, get_func_attrs} }, + { jl_f_nfields_addr, new JuliaFunction<>{XSTR(jl_f_nfields), get_func_sig, get_func_attrs} }, + { jl_f__expr_addr, new JuliaFunction<>{XSTR(jl_f__expr), get_func_sig, get_func_attrs} }, + { jl_f__typevar_addr, new JuliaFunction<>{XSTR(jl_f__typevar), get_func_sig, get_func_attrs} }, + { jl_f_memoryref_addr, new JuliaFunction<>{XSTR(jl_f_memoryref), get_func_sig, get_func_attrs} }, + { jl_f_memoryrefoffset_addr, new JuliaFunction<>{XSTR(jl_f_memoryrefoffset), get_func_sig, get_func_attrs} }, + { jl_f_memoryrefset_addr, new JuliaFunction<>{XSTR(jl_f_memoryrefset), get_func_sig, get_func_attrs} }, + { jl_f_memoryrefswap_addr, new JuliaFunction<>{XSTR(jl_f_memoryrefswap), get_func_sig, get_func_attrs} }, + { jl_f_memoryrefreplace_addr, new JuliaFunction<>{XSTR(jl_f_memoryrefreplace), get_func_sig, get_func_attrs} }, + { jl_f_memoryrefmodify_addr, new JuliaFunction<>{XSTR(jl_f_memoryrefmodify), get_func_sig, get_func_attrs} }, + { jl_f_memoryrefsetonce_addr, new JuliaFunction<>{XSTR(jl_f_memoryrefsetonce), get_func_sig, get_func_attrs} }, + { jl_f_memoryref_isassigned_addr, new JuliaFunction<>{XSTR(jl_f_memoryref_isassigned), get_func_sig, get_func_attrs} }, + { jl_f_apply_type_addr, new JuliaFunction<>{XSTR(jl_f_apply_type), get_func_sig, get_func_attrs} }, + { jl_f_donotdelete_addr, new JuliaFunction<>{XSTR(jl_f_donotdelete), get_donotdelete_sig, get_donotdelete_func_attrs} }, + { jl_f_compilerbarrier_addr, new JuliaFunction<>{XSTR(jl_f_compilerbarrier), get_func_sig, get_func_attrs} }, + { jl_f_finalizer_addr, new JuliaFunction<>{XSTR(jl_f_finalizer), get_func_sig, get_func_attrs} }, + { jl_f_finalize_addr, new JuliaFunction<>{XSTR(jl_f_finalize), get_func_sig, get_func_attrs} }, + { jl_f__svec_ref_addr, new JuliaFunction<>{XSTR(jl_f__svec_ref), get_func_sig, get_func_attrs} }, + { jl_f_current_scope_addr, new JuliaFunction<>{XSTR(jl_f_current_scope), get_func_sig, get_func_attrs} }, }; return builtins; } diff --git a/src/staticdata.c b/src/staticdata.c index 0a8cbe6db7c67b..47df1dae295f27 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -514,7 +514,7 @@ static const jl_fptr_args_t id_to_fptrs[] = { &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_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_finalizer, &jl_f_finalize, &jl_f__compute_sparams, &jl_f__svec_ref, &jl_f_current_scope, NULL }; diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 2de6d9950d4e45..f5500fb0e72d11 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -1622,6 +1622,19 @@ let src = code_typed1((Int,)) do x @test count(iscall((src, setfield!)), src.code) == 1 end +# early `finalize` insertion +let src = code_typed1((Int,)) do x + xs = finalizer(Ref(x)) do obj + Base.@assume_effects :nothrow :notaskstate + Core.println("finalizing: ", objectid(obj)) + end + @show xs[] + return xs[] + end + @test count(iscall((src, Core.finalizer)), src.code) == 1 + @test count(iscall((src, Core.finalize)), src.code) == 1 +end + # optimize `[push!|pushfirst!](::Vector{Any}, x...)` @testset "optimize `$f(::Vector{Any}, x...)`" for f = Any[push!, pushfirst!] @eval begin