From 096c1d2bcbae1b7388ee4285b89da50dbb9f7094 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Sat, 5 Oct 2024 14:38:53 +0900 Subject: [PATCH] optimizer: enable load forwarding with the `finalizer` elision (#55991) When the finalizer elision pass is used, load forwarding is not performed currently, regardless of whether the pass succeeds or not. But this is not necessary, and by keeping the `setfield!` call, we can safely forward `getfield` even if finalizer elision is tried. --- base/compiler/ssair/passes.jl | 16 +++++++++------- test/compiler/inline.jl | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index e227249b48598..0e2272524a0ed 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1707,22 +1707,24 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int,Tuple{SPCSet,SSADefUse}} ismutabletype(typ) || continue typ = typ::DataType # First check for any finalizer calls - finalizer_idx = nothing - for use in defuse.uses + finalizer_useidx = nothing + for (useidx, use) in enumerate(defuse.uses) if use.kind === :finalizer # For now: Only allow one finalizer per allocation - finalizer_idx !== nothing && @goto skip - finalizer_idx = use.idx + finalizer_useidx !== nothing && @goto skip + finalizer_useidx = useidx end end - if finalizer_idx !== nothing && inlining !== nothing + all_eliminated = all_forwarded = true + if finalizer_useidx !== nothing && inlining !== nothing + finalizer_idx = defuse.uses[finalizer_useidx].idx try_resolve_finalizer!(ir, defidx, finalizer_idx, defuse, inlining, lazydomtree, lazypostdomtree, ir[SSAValue(finalizer_idx)][:info]) - continue + deleteat!(defuse.uses, finalizer_useidx) + all_eliminated = all_forwarded = false # can't eliminate `setfield!` calls safely end # Partition defuses by field fielddefuse = SSADefUse[SSADefUse() for _ = 1:fieldcount(typ)] - all_eliminated = all_forwarded = true for use in defuse.uses if use.kind === :preserve for du in fielddefuse diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index fceb920352482..2de6d9950d4e4 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -1596,6 +1596,32 @@ let @test get_finalization_count() == 1000 end +# Load forwarding with `finalizer` elision +let src = code_typed1((Int,)) do x + xs = finalizer(Ref(x)) do obj + @noinline + Base.@assume_effects :nothrow :notaskstate + Core.println("finalizing: ", obj[]) + end + Base.@assume_effects :nothrow @noinline println("xs[] = ", @inline xs[]) + return xs[] + end + @test count(iscall((src, getfield)), src.code) == 0 +end +let src = code_typed1((Int,)) do x + xs = finalizer(Ref(x)) do obj + @noinline + Base.@assume_effects :nothrow :notaskstate + Core.println("finalizing: ", obj[]) + end + Base.@assume_effects :nothrow @noinline println("xs[] = ", @inline xs[]) + xs[] += 1 + return xs[] + end + @test count(iscall((src, getfield)), src.code) == 0 + @test count(iscall((src, setfield!)), src.code) == 1 +end + # optimize `[push!|pushfirst!](::Vector{Any}, x...)` @testset "optimize `$f(::Vector{Any}, x...)`" for f = Any[push!, pushfirst!] @eval begin