Skip to content

Commit

Permalink
WIP: Better current_scope folding
Browse files Browse the repository at this point in the history
  • Loading branch information
Keno committed Dec 21, 2023
1 parent 5423276 commit 95cfbc3
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 54 deletions.
12 changes: 8 additions & 4 deletions base/compiler/ssair/ir.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Core.PhiNode() = Core.PhiNode(Int32[], Any[])

isterminator(@nospecialize(stmt)) = isa(stmt, GotoNode) || isa(stmt, GotoIfNot) || isa(stmt, ReturnNode) || isa(stmt, EnterNode)
isterminator(@nospecialize(stmt)) = isa(stmt, GotoNode) || isa(stmt, GotoIfNot) || isa(stmt, ReturnNode) || isa(stmt, EnterNode) || isexpr(stmt, :leave)

struct CFG
blocks::Vector{BasicBlock}
Expand Down Expand Up @@ -953,11 +953,15 @@ function insert_node!(compact::IncrementalCompact, @nospecialize(before), newins
end
end

function maybe_reopen_bb!(compact)
function did_just_finish_bb(compact)
result_idx = compact.result_idx
result_bbs = compact.cfg_transform.result_bbs
if (compact.active_result_bb == length(result_bbs) + 1) ||
result_idx == first(result_bbs[compact.active_result_bb].stmts)
(compact.active_result_bb == length(result_bbs) + 1) ||
result_idx == first(result_bbs[compact.active_result_bb].stmts)
end

function maybe_reopen_bb!(compact)
if did_just_finish_bb(compact)
compact.active_result_bb -= 1
return true
end
Expand Down
105 changes: 56 additions & 49 deletions base/compiler/ssair/passes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1126,42 +1126,6 @@ function fold_ifelse!(compact::IncrementalCompact, idx::Int, stmt::Expr)
return false
end

function fold_current_scope!(compact::IncrementalCompact, idx::Int, stmt::Expr, lazydomtree::LazyDomtree)
domtree = get!(lazydomtree)

# The frontend enforces the invariant that any :enter dominates its active
# region, so all we have to do here is walk the domtree to find it.
dombb = block_for_inst(compact, SSAValue(idx))

local bbterminator
prevdombb = dombb
while true
dombb = domtree.idoms_bb[dombb]

# Did not find any dominating :enter - scope is inherited from the outside
dombb == 0 && return nothing

bbterminator = compact[SSAValue(last(compact.cfg_transform.result_bbs[dombb].stmts))][:stmt]
if !isa(bbterminator, EnterNode) || !isdefined(bbterminator, :scope)
prevdombb = dombb
continue
end
if bbterminator.catch_dest == 0
# TODO: dominance alone is not enough here, we need to actually find the :leaves
return nothing
end
# Check that we are inside the :enter region, i.e. are dominated by the first block in the
# enter region - otherwise we've already left this :enter and should keep going
if prevdombb != dombb + 1
prevdombb = dombb
continue
end
compact[idx] = bbterminator.scope
return nothing
end
end


# NOTE we use `IdSet{Int}` instead of `BitSet` for in these passes since they work on IR after inlining,
# which can be very large sometimes, and program counters in question are often very sparse
const SPCSet = IdSet{Int}
Expand All @@ -1176,6 +1140,11 @@ function (this::IntermediaryCollector)(@nospecialize(pi), @nospecialize(ssa))
return nothing
end

function update_scope_mapping!(scope_mapping, bb, val)
@assert (scope_mapping[bb] in (val, SSAValue(0)))
scope_mapping[bb] = val
end

"""
sroa_pass!(ir::IRCode) -> newir::IRCode
Expand All @@ -1200,9 +1169,47 @@ function sroa_pass!(ir::IRCode, inlining::Union{Nothing,InliningState}=nothing)
defuses = nothing # will be initialized once we encounter mutability in order to reduce dynamic allocations
# initialization of domtree is delayed to avoid the expensive computation in many cases
lazydomtree = LazyDomtree(ir)
scope_mapping::Union{Vector{SSAValue}, Nothing} = nothing
for ((old_idx, idx), stmt) in compact
# If we encounter any EnterNode with set :scope, propagate the current scope for all basic blocks, so
# we have easy access for current_scope folding below.
if !isa(stmt, Expr)
bb = compact.active_result_bb - 1
if scope_mapping !== nothing && did_just_finish_bb(compact)
this_scope = scope_mapping[bb]
if isa(stmt, GotoIfNot)
update_scope_mapping!(scope_mapping, stmt.dest, this_scope)
update_scope_mapping!(scope_mapping, bb+1, this_scope)
elseif isa(stmt, GotoNode)
update_scope_mapping!(scope_mapping, stmt.label, this_scope)
elseif isa(stmt, EnterNode)
if stmt.catch_dest != 0
update_scope_mapping!(scope_mapping, stmt.catch_dest, this_scope)
end
isdefined(stmt, :scope) || update_scope_mapping!(scope_mapping, bb+1, this_scope)
elseif !isa(stmt, ReturnNode)
update_scope_mapping!(scope_mapping, bb+1, this_scope)
end
end
if isa(stmt, EnterNode)
if isdefined(stmt, :scope)
if scope_mapping === nothing
scope_mapping = SSAValue[SSAValue(0) for i = 1:length(compact.cfg_transform.result_bbs)]
end
update_scope_mapping!(scope_mapping, bb+1, SSAValue(idx))
end
end
continue
end
if scope_mapping !== nothing && did_just_finish_bb(compact)
bb = compact.active_result_bb - 1
if isexpr(stmt, :leave)
update_scope_mapping!(scope_mapping, bb+1, scope_mapping[block_for_inst(compact, scope_mapping[bb])])
else
update_scope_mapping!(scope_mapping, bb+1, scope_mapping[bb])
end
end
# check whether this statement is `getfield` / `setfield!` (or other "interesting" statement)
isa(stmt, Expr) || continue
is_setfield = is_isdefined = is_finalizer = is_keyvalue_get = false
field_ordering = :unspecified
if is_known_call(stmt, setfield!, compact)
Expand Down Expand Up @@ -1293,7 +1300,13 @@ function sroa_pass!(ir::IRCode, inlining::Union{Nothing,InliningState}=nothing)
2 == (length(stmt.args) - (isexpr(stmt, :invoke) ? 2 : 1)) || continue
lift_keyvalue_get!(compact, idx, stmt, 𝕃ₒ)
elseif is_known_call(stmt, Core.current_scope, compact)
fold_current_scope!(compact, idx, stmt, lazydomtree)
length(stmt.args) == 1 || continue
scope_mapping !== nothing || continue
bb = compact.active_result_bb
did_just_finish_bb(compact) && (bb -= 1)
enter_ssa = scope_mapping[bb]
enter_ssa == SSAValue(0) && continue
compact[SSAValue(idx)] = (compact[enter_ssa][:stmt]::EnterNode).scope
elseif isexpr(stmt, :new)
refine_new_effects!(𝕃ₒ, compact, idx, stmt)
end
Expand Down Expand Up @@ -2095,14 +2108,6 @@ end
function is_legal_bb_drop(ir::IRCode, bbidx::Int, bb::BasicBlock)
# For the time being, don't drop the first bb, because it has special predecessor semantics.
bbidx == 1 && return false
# If the block we're going to is the same as the fallthrow, it's always legal to drop
# the block.
length(bb.stmts) == 0 && return true
if length(bb.stmts) == 1
stmt = ir[SSAValue(first(bb.stmts))][:stmt]
stmt === nothing && return true
((stmt::GotoNode).label == bbidx + 1) && return true
end
return true
end

Expand Down Expand Up @@ -2210,9 +2215,11 @@ function cfg_simplify!(ir::IRCode)
if length(bb.succs) == 1
succ = bb.succs[1]
if length(bbs[succ].preds) == 1 && succ != 1
# Can't merge blocks with :enter terminator even if they
# only have one successor.
if isa(ir[SSAValue(last(bb.stmts))][:stmt], EnterNode)
# Can't merge blocks with a non-GotoNode terminator, even if they
# only have one successor, because it would not be legal to have that
# terminator in the middle of a basic block.
terminator = ir[SSAValue(last(bb.stmts))][:stmt]
if !isa(terminator, GotoNode) && isterminator(terminator)
continue
end
# Prevent cycles by making sure we don't end up back at `idx`
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/ssair/verify.jl
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ function verify_ir(ir::IRCode, print::Bool=true,
error("")
end
end
elseif (isa(stmt, GotoNode) || isa(stmt, GotoIfNot) || isa(stmt, EnterNode)) && idx != last(ir.cfg.blocks[bb].stmts)
elseif isterminator(stmt) && idx != last(ir.cfg.blocks[bb].stmts)
@verify_error "Terminator $idx in bb $bb is not the last statement in the block"
error("")
else
Expand Down

0 comments on commit 95cfbc3

Please sign in to comment.