Skip to content

Commit 45d43f3

Browse files
authored
optimizer: fix #43254, avoid infinite CFG traversal in SROA's domination analysis (#43265)
Since CFG can be cyclic, the previous implementation of `has_safe_def` that simply walks predecessors recursively was just wrong. This commit fixes it by making `has_safe_def` maintain a single set that keeps the identities of basic blocks that have been examined already.
1 parent ec9bb1c commit 45d43f3

File tree

1 file changed

+32
-18
lines changed

1 file changed

+32
-18
lines changed

base/compiler/ssair/passes.jl

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -97,20 +97,34 @@ end
9797
# if this load at `idx` have any "safe" `setfield!` calls that define the field
9898
function has_safe_def(
9999
ir::IRCode, domtree::DomTree, allblocks::Vector{Int}, du::SSADefUse,
100-
newidx::Int, idx::Int, inclusive::Bool = false)
101-
def = first(find_def_for_use(ir, domtree, allblocks, du, idx, inclusive))
102-
103-
# this field is supposed to be defined at the `:new` site (but it's not and thus this load will throw)
100+
newidx::Int, idx::Int)
101+
def, _, _ = find_def_for_use(ir, domtree, allblocks, du, idx)
102+
# will throw since we already checked this `:new` site doesn't define this field
104103
def == newidx && return false
105-
106-
def 0 && return true # found a "safe" definition
107-
108-
# we may be able to replace this load with `PhiNode` if all the predecessors have "safe" definitions
109-
idxblock = block_for_inst(ir, idx)
110-
for pred in ir.cfg.blocks[idxblock].preds
111-
lastidx = last(ir.cfg.blocks[pred].stmts)
112-
# NOTE `lastidx` isn't a load, thus we can use inclusive coondition within the `find_def_for_use`
113-
has_safe_def(ir, domtree, allblocks, du, newidx, lastidx, true) || return false
104+
# found a "safe" definition
105+
def 0 && return true
106+
# we may still be able to replace this load with `PhiNode`
107+
# examine if all predecessors of `block` have any "safe" definition
108+
block = block_for_inst(ir, idx)
109+
seen = BitSet(block)
110+
worklist = BitSet(ir.cfg.blocks[block].preds)
111+
isempty(worklist) && return false
112+
while !isempty(worklist)
113+
pred = pop!(worklist)
114+
# if this block has already been examined, bail out to avoid infinite cycles
115+
pred in seen && return false
116+
idx = last(ir.cfg.blocks[pred].stmts)
117+
# NOTE `idx` isn't a load, thus we can use inclusive coondition within the `find_def_for_use`
118+
def, _, _ = find_def_for_use(ir, domtree, allblocks, du, idx, true)
119+
# will throw since we already checked this `:new` site doesn't define this field
120+
def == newidx && return false
121+
push!(seen, pred)
122+
# found a "safe" definition for this predecessor
123+
def 0 && continue
124+
# check for the predecessors of this predecessor
125+
for newpred in ir.cfg.blocks[pred].preds
126+
push!(worklist, newpred)
127+
end
114128
end
115129
return true
116130
end
@@ -599,8 +613,8 @@ function perform_lifting!(compact::IncrementalCompact,
599613
return stmt_val # N.B. should never happen
600614
end
601615

602-
# NOTE we use `IdSet{Int}` instead of `BitSet` for `sroa_pass!` since it works on IR after inlining,
603-
# which can be very large sometimes, and analyzed program counters are often very sparse
616+
# NOTE we use `IdSet{Int}` instead of `BitSet` for in these passes since they work on IR after inlining,
617+
# which can be very large sometimes, and program counters in question are often very sparse
604618
const SPCSet = IdSet{Int}
605619

606620
"""
@@ -897,8 +911,8 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
897911
end
898912
end
899913
for b in phiblocks
914+
n = ir[phinodes[b]]::PhiNode
900915
for p in ir.cfg.blocks[b].preds
901-
n = ir[phinodes[b]]::PhiNode
902916
push!(n.edges, p)
903917
push!(n.values, compute_value_for_block(ir, domtree,
904918
allblocks, du, phinodes, fidx, p))
@@ -967,7 +981,7 @@ function count_uses(@nospecialize(stmt), uses::Vector{Int})
967981
end
968982
end
969983

970-
function mark_phi_cycles!(compact::IncrementalCompact, safe_phis::BitSet, phi::Int)
984+
function mark_phi_cycles!(compact::IncrementalCompact, safe_phis::SPCSet, phi::Int)
971985
worklist = Int[]
972986
push!(worklist, phi)
973987
while !isempty(worklist)
@@ -1037,7 +1051,7 @@ function adce_pass!(ir::IRCode)
10371051
changed = true
10381052
while changed
10391053
changed = false
1040-
safe_phis = BitSet()
1054+
safe_phis = SPCSet()
10411055
for phi in all_phis
10421056
# Save any phi cycles that have non-phi uses
10431057
if compact.used_ssas[phi] - phi_uses[phi] != 0

0 commit comments

Comments
 (0)