Skip to content

Commit 1bd610f

Browse files
authored
optimizer: simplify the finalizer inlining pass a bit (#55934)
Minor adjustments have been made to the algorithm of the finalizer inlining pass. Previously, it required that the finalizer registration dominate all uses, but this is not always necessary as far as the finalizer inlining point dominates all the uses. So the check has been relaxed. Other minor fixes have been made as well, but their importance is low.
1 parent 32ad9e6 commit 1bd610f

File tree

4 files changed

+42
-66
lines changed

4 files changed

+42
-66
lines changed

base/compiler/optimize.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@ end
647647
function refine_effects!(interp::AbstractInterpreter, sv::PostOptAnalysisState)
648648
if !is_effect_free(sv.result.ipo_effects) && sv.all_effect_free && !isempty(sv.ea_analysis_pending)
649649
ir = sv.ir
650-
nargs = length(ir.argtypes)
650+
nargs = let def = sv.result.linfo.def; isa(def, Method) ? Int(def.nargs) : 0; end
651651
estate = EscapeAnalysis.analyze_escapes(ir, nargs, optimizer_lattice(interp), GetNativeEscapeCache(interp))
652652
argescapes = EscapeAnalysis.ArgEscapeCache(estate)
653653
stack_analysis_result!(sv.result, argescapes)

base/compiler/ssair/inlining.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1597,7 +1597,6 @@ function handle_finalizer_call!(ir::IRCode, idx::Int, stmt::Expr, info::Finalize
15971597
push!(stmt.args, item1.invoke)
15981598
elseif isa(item1, ConstantCase)
15991599
push!(stmt.args, nothing)
1600-
push!(stmt.args, item1.val)
16011600
end
16021601
end
16031602
return nothing

base/compiler/ssair/passes.jl

Lines changed: 41 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,10 +1564,12 @@ end
15641564

15651565
is_nothrow(ir::IRCode, ssa::SSAValue) = has_flag(ir[ssa], IR_FLAG_NOTHROW)
15661566

1567-
function reachable_blocks(cfg::CFG, from_bb::Int, to_bb::Union{Nothing,Int} = nothing)
1567+
function reachable_blocks(cfg::CFG, from_bb::Int, to_bb::Int)
15681568
worklist = Int[from_bb]
15691569
visited = BitSet(from_bb)
1570-
if to_bb !== nothing
1570+
if to_bb == from_bb
1571+
return visited
1572+
else
15711573
push!(visited, to_bb)
15721574
end
15731575
function visit!(bb::Int)
@@ -1582,100 +1584,78 @@ function reachable_blocks(cfg::CFG, from_bb::Int, to_bb::Union{Nothing,Int} = no
15821584
return visited
15831585
end
15841586

1585-
function try_resolve_finalizer!(ir::IRCode, idx::Int, finalizer_idx::Int, defuse::SSADefUse,
1587+
function try_resolve_finalizer!(ir::IRCode, alloc_idx::Int, finalizer_idx::Int, defuse::SSADefUse,
15861588
inlining::InliningState, lazydomtree::LazyDomtree,
15871589
lazypostdomtree::LazyPostDomtree, @nospecialize(info::CallInfo))
15881590
# For now, require that:
15891591
# 1. The allocation dominates the finalizer registration
1590-
# 2. The finalizer registration dominates all uses reachable from the
1591-
# finalizer registration.
1592-
# 3. The insertion block for the finalizer is the post-dominator of all
1593-
# uses and the finalizer registration block. The insertion block must
1594-
# be dominated by the finalizer registration block.
1595-
# 4. The path from the finalizer registration to the finalizer inlining
1592+
# 2. The insertion block for the finalizer is the post-dominator of all
1593+
# uses (including the finalizer registration).
1594+
# 3. The path from the finalizer registration to the finalizer inlining
15961595
# location is nothrow
15971596
#
1598-
# TODO: We could relax item 3, by inlining the finalizer multiple times.
1597+
# TODO: We could relax the check 2, by inlining the finalizer multiple times.
15991598

16001599
# Check #1: The allocation dominates the finalizer registration
16011600
domtree = get!(lazydomtree)
16021601
finalizer_bb = block_for_inst(ir, finalizer_idx)
1603-
alloc_bb = block_for_inst(ir, idx)
1602+
alloc_bb = block_for_inst(ir, alloc_idx)
16041603
dominates(domtree, alloc_bb, finalizer_bb) || return nothing
16051604

1606-
bb_insert_block::Int = finalizer_bb
1607-
bb_insert_idx::Union{Int,Nothing} = finalizer_idx
1608-
function note_block_use!(usebb::Int, useidx::Int)
1609-
new_bb_insert_block = nearest_common_dominator(get!(lazypostdomtree),
1610-
bb_insert_block, usebb)
1611-
if new_bb_insert_block == bb_insert_block && bb_insert_idx !== nothing
1612-
bb_insert_idx = max(bb_insert_idx::Int, useidx)
1613-
elseif new_bb_insert_block == usebb
1614-
bb_insert_idx = useidx
1605+
# Check #2: The insertion block for the finalizer is the post-dominator of all uses
1606+
insert_bb::Int = finalizer_bb
1607+
insert_idx::Union{Int,Nothing} = finalizer_idx
1608+
function note_defuse!(x::Union{Int,SSAUse})
1609+
defuse_idx = x isa SSAUse ? x.idx : x
1610+
defuse_idx == finalizer_idx && return nothing
1611+
defuse_bb = block_for_inst(ir, defuse_idx)
1612+
new_insert_bb = nearest_common_dominator(get!(lazypostdomtree),
1613+
insert_bb, defuse_bb)
1614+
if new_insert_bb == insert_bb && insert_idx !== nothing
1615+
insert_idx = max(insert_idx::Int, defuse_idx)
1616+
elseif new_insert_bb == defuse_bb
1617+
insert_idx = defuse_idx
16151618
else
1616-
bb_insert_idx = nothing
1619+
insert_idx = nothing
16171620
end
1618-
bb_insert_block = new_bb_insert_block
1621+
insert_bb = new_insert_bb
16191622
nothing
16201623
end
1621-
1622-
# Collect all reachable blocks between the finalizer registration and the
1623-
# insertion point
1624-
blocks = reachable_blocks(ir.cfg, finalizer_bb, alloc_bb)
1625-
1626-
# Check #2
1627-
function check_defuse(x::Union{Int,SSAUse})
1628-
duidx = x isa SSAUse ? x.idx : x
1629-
duidx == finalizer_idx && return true
1630-
bb = block_for_inst(ir, duidx)
1631-
# Not reachable from finalizer registration - we're ok
1632-
bb blocks && return true
1633-
note_block_use!(bb, duidx)
1634-
if dominates(domtree, finalizer_bb, bb)
1635-
return true
1636-
else
1637-
return false
1638-
end
1639-
end
1640-
all(check_defuse, defuse.uses) || return nothing
1641-
all(check_defuse, defuse.defs) || return nothing
1642-
bb_insert_block != 0 || return nothing # verify post-dominator of all uses exists
1643-
1644-
# Check #3
1645-
dominates(domtree, finalizer_bb, bb_insert_block) || return nothing
1624+
foreach(note_defuse!, defuse.uses)
1625+
foreach(note_defuse!, defuse.defs)
1626+
insert_bb != 0 || return nothing # verify post-dominator of all uses exists
16461627

16471628
if !OptimizationParams(inlining.interp).assume_fatal_throw
16481629
# Collect all reachable blocks between the finalizer registration and the
16491630
# insertion point
1650-
blocks = finalizer_bb == bb_insert_block ? Int[finalizer_bb] :
1651-
reachable_blocks(ir.cfg, finalizer_bb, bb_insert_block)
1631+
blocks = reachable_blocks(ir.cfg, finalizer_bb, insert_bb)
16521632

1653-
# Check #4
1654-
function check_range_nothrow(ir::IRCode, s::Int, e::Int)
1633+
# Check #3
1634+
function check_range_nothrow(s::Int, e::Int)
16551635
return all(s:e) do sidx::Int
16561636
sidx == finalizer_idx && return true
1657-
sidx == idx && return true
1637+
sidx == alloc_idx && return true
16581638
return is_nothrow(ir, SSAValue(sidx))
16591639
end
16601640
end
16611641
for bb in blocks
16621642
range = ir.cfg.blocks[bb].stmts
16631643
s, e = first(range), last(range)
1664-
if bb == bb_insert_block
1665-
bb_insert_idx === nothing && continue
1666-
e = bb_insert_idx
1644+
if bb == insert_bb
1645+
insert_idx === nothing && continue
1646+
e = insert_idx
16671647
end
16681648
if bb == finalizer_bb
16691649
s = finalizer_idx
16701650
end
1671-
check_range_nothrow(ir, s, e) || return nothing
1651+
check_range_nothrow(s, e) || return nothing
16721652
end
16731653
end
16741654

16751655
# Ok, legality check complete. Figure out the exact statement where we're
16761656
# going to inline the finalizer.
1677-
loc = bb_insert_idx === nothing ? first(ir.cfg.blocks[bb_insert_block].stmts) : bb_insert_idx::Int
1678-
attach_after = bb_insert_idx !== nothing
1657+
loc = insert_idx === nothing ? first(ir.cfg.blocks[insert_bb].stmts) : insert_idx::Int
1658+
attach_after = insert_idx !== nothing
16791659

16801660
finalizer_stmt = ir[SSAValue(finalizer_idx)][:stmt]
16811661
argexprs = Any[finalizer_stmt.args[2], finalizer_stmt.args[3]]
@@ -1702,11 +1682,10 @@ function try_resolve_finalizer!(ir::IRCode, idx::Int, finalizer_idx::Int, defuse
17021682
return nothing
17031683
end
17041684

1705-
function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse}}, used_ssas::Vector{Int}, lazydomtree::LazyDomtree, inlining::Union{Nothing, InliningState})
1685+
function sroa_mutables!(ir::IRCode, defuses::IdDict{Int,Tuple{SPCSet,SSADefUse}}, used_ssas::Vector{Int}, lazydomtree::LazyDomtree, inlining::Union{Nothing,InliningState})
17061686
𝕃ₒ = inlining === nothing ? SimpleInferenceLattice.instance : optimizer_lattice(inlining.interp)
17071687
lazypostdomtree = LazyPostDomtree(ir)
17081688
for (defidx, (intermediaries, defuse)) in defuses
1709-
intermediaries = collect(intermediaries)
17101689
# Check if there are any uses we did not account for. If so, the variable
17111690
# escapes and we cannot eliminate the allocation. This works, because we're guaranteed
17121691
# not to include any intermediaries that have dead uses. As a result, missing uses will only ever
@@ -1906,7 +1885,7 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
19061885
end
19071886
end
19081887

1909-
function form_new_preserves(origex::Expr, intermediates::Vector{Int}, new_preserves::Vector{Any})
1888+
function form_new_preserves(origex::Expr, intermediaries::Union{Vector{Int},SPCSet}, new_preserves::Vector{Any})
19101889
newex = Expr(:foreigncall)
19111890
nccallargs = length(origex.args[3]::SimpleVector)
19121891
for i in 1:(6+nccallargs-1)
@@ -1915,7 +1894,7 @@ function form_new_preserves(origex::Expr, intermediates::Vector{Int}, new_preser
19151894
for i in (6+nccallargs):length(origex.args)
19161895
x = origex.args[i]
19171896
# don't need to preserve intermediaries
1918-
if isa(x, SSAValue) && x.id in intermediates
1897+
if isa(x, SSAValue) && x.id in intermediaries
19191898
continue
19201899
end
19211900
push!(newex.args, x)

test/compiler/inline.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,7 +1570,6 @@ let
15701570
@test get_finalization_count() == 1000
15711571
end
15721572

1573-
15741573
function cfg_finalization7(io)
15751574
for i = -999:1000
15761575
o = DoAllocWithField(0)
@@ -1597,7 +1596,6 @@ let
15971596
@test get_finalization_count() == 1000
15981597
end
15991598

1600-
16011599
# optimize `[push!|pushfirst!](::Vector{Any}, x...)`
16021600
@testset "optimize `$f(::Vector{Any}, x...)`" for f = Any[push!, pushfirst!]
16031601
@eval begin

0 commit comments

Comments
 (0)