@@ -46,7 +46,10 @@ function CFGReachability(cfg::CFG, domtree::DomTree)
4646 Int[], # _worklist
4747 SCCStackItem[], # _stack
4848 )
49- tarjan! (reachability, cfg)
49+ tarjan! (reachability, cfg;
50+ # reducible back-edges don't need to be considered for reachability
51+ filter = (from:: Int ,to:: Int )-> ! dominates (domtree, to, from)
52+ )
5053 return reachability
5154end
5255
@@ -59,12 +62,12 @@ bb_in_irreducible_loop(reach::CFGReachability, bb::Int) = reach.irreducible[bb]
5962#
6063# `tarjan!` takes the transitive closure of this relation in order to detect
6164# which BasicBlocks are unreachable.
62- function _bb_externally_reachable (reach:: CFGReachability , cfg:: CFG , bb:: Int )
65+ function _bb_externally_reachable (reach:: CFGReachability , cfg:: CFG , bb:: Int ; filter )
6366 (; scc) = reach
6467 bb == 1 && return true
6568 for pred in cfg. blocks[bb]. preds
6669 scc[pred] <= 0 && continue
67- dominates (reach . domtree , bb, pred ) && continue
70+ ! filter (pred , bb) && continue
6871 @assert scc[pred] != scc[bb]
6972 return true
7073 end
@@ -85,10 +88,12 @@ Outputs:
8588 - `reach._worklist`: if performing an incremental update (`root != 1`), any traversed nodes that
8689 are unreachable from BasicBlock #1 are enqueued to this worklist
8790"""
88- function tarjan! (reach:: CFGReachability , cfg:: CFG ; root:: Int = 1 )
89- (; scc, irreducible, domtree) = reach
91+ function tarjan! (reach:: CFGReachability , cfg:: CFG ; root:: Int = 1 ,
92+ filter = (from:: Int ,to:: Int )-> true ,
93+ )
94+ (; scc, irreducible) = reach
9095 scc[root] != 0 && return scc
91- live = _bb_externally_reachable (reach, cfg, root)
96+ live = _bb_externally_reachable (reach, cfg, root; filter )
9297
9398 # the original algorithm has a separate stack and worklist (unrelated to `reach._worklist`)
9499 # here we use a single combined stack for improved memory/cache efficiency
@@ -117,12 +122,8 @@ function tarjan!(reach::CFGReachability, cfg::CFG; root::Int=1)
117122 stack[cursor] = item = SCCStackItem (item; child= child+ 1 )
118123 succ = bb. succs[child]
119124
120- # ignore any back-edges in a (natural) loop (see `kill_edge!`)
121- if dominates (domtree, succ, convert (Int, v))
122- # This check ensures that reducible CFG's will contain no SCC's. The vast majority
123- # of functions have reducible CFG's, so this optimization is very important.
124- continue
125- end
125+ # ignore any edges that don't pass the filter
126+ ! filter (convert (Int, v), succ) && continue
126127
127128 if scc[succ] < 0
128129 # next child is already in DFS tree
@@ -134,7 +135,7 @@ function tarjan!(reach::CFGReachability, cfg::CFG; root::Int=1)
134135 elseif scc[succ] == 0
135136 # next child is a new element in DFS tree
136137 preorder_id += 1
137- live = live || _bb_externally_reachable (reach, cfg, succ)
138+ live = live || _bb_externally_reachable (reach, cfg, succ; filter )
138139 push! (stack, SCCStackItem (
139140 succ, # v
140141 1 , # child
@@ -154,7 +155,7 @@ function tarjan!(reach::CFGReachability, cfg::CFG; root::Int=1)
154155 if live
155156 scc[item. v] = v
156157 scan_subgraph! (reach, cfg, convert (Int, item. v),
157- #= filter =# (pred,x)-> (! dominates (domtree , x, pred ) && scc[x] > typemax (Int)÷ 2 ),
158+ #= filter =# (pred,x)-> (filter (pred , x) && scc[x] > typemax (Int)÷ 2 ),
158159 #= action =# (x)-> (scc[x] -= typemax (Int)÷ 2 ;),
159160 )
160161 else # this offset marks a node as 'maybe-dead'
@@ -181,19 +182,19 @@ function tarjan!(reach::CFGReachability, cfg::CFG; root::Int=1)
181182 worklist = reach. _worklist
182183
183184 # filter the worklist, leaving any nodes not proven to be reachable from BB #1
184- n_filtered = 0
185+ n_popped = 0
185186 for i = (worklist_len + 1 ): length (worklist)
186187 @assert worklist[i] != 1
187188 @assert scc[worklist[i]] > 0
188189 if scc[worklist[i]] > typemax (Int)÷ 2
189190 # node is unreachable, enqueue it
190191 scc[worklist[i]] = 0
191- worklist[i - n_filtered ] = worklist[i]
192+ worklist[i - n_popped ] = worklist[i]
192193 else
193- n_filtered += 1
194+ n_popped += 1
194195 end
195196 end
196- resize! (worklist, length (worklist) - n_filtered )
197+ resize! (worklist, length (worklist) - n_popped )
197198
198199 return length (worklist) > worklist_len # if true, a (newly) unreachable node was enqueued
199200end
@@ -228,16 +229,20 @@ function enqueue_if_unreachable!(reach::CFGReachability, cfg::CFG, bb::Int)
228229 # irreducible CFG
229230 # this requires a full scan of the irreducible loop
230231
232+ # any reducible back-edges do not need to be considered as part of reachability
233+ # (very important optimization, since it means reducible CFGs will have no SCCs)
234+ filter = (from:: Int , to:: Int )-> ! dominates (domtree, to, from)
235+
231236 scc′ = scc[bb]
232237 scc[bb] = 0
233238 scan_subgraph! (reach, cfg, bb, # set this SCC to 0
234- #= filter =# (pred,x)-> (! dominates (domtree , x, pred ) && scc[x] == scc′),
239+ #= filter =# (pred,x)-> (filter (pred , x) && scc[x] == scc′),
235240 #= action =# (x)-> (scc[x] = 0 ;),
236241 )
237242
238243 # re-compute the SCC's for this portion of the CFG, adding any freshly
239244 # unreachable nodes to `reach._worklist`
240- return tarjan! (reach, cfg; root= bb)
245+ return tarjan! (reach, cfg; root= bb, filter )
241246 else
242247 # target is a reducible CFG node
243248 # this node lives iff it still has an incoming forward edge
@@ -250,6 +255,13 @@ function enqueue_if_unreachable!(reach::CFGReachability, cfg::CFG, bb::Int)
250255 end
251256end
252257
258+ function kill_cfg_edge! (cfg:: CFG , from:: Int , to:: Int )
259+ preds, succs = cfg. blocks[to]. preds, cfg. blocks[from]. succs
260+ deleteat! (preds, findfirst (x:: Int -> x== from, preds):: Int )
261+ deleteat! (succs, findfirst (x:: Int -> x== to, succs):: Int )
262+ return nothing
263+ end
264+
253265"""
254266Remove from `cfg` and `reach` the edge (from → to), as well as any blocks/edges
255267this causes to become unreachable.
@@ -265,9 +277,7 @@ function kill_edge!(reach::CFGReachability, cfg::CFG, from::Int, to::Int,
265277 @assert reach. scc[to] != 0
266278
267279 # delete (from → to) edge
268- preds, succs = cfg. blocks[to]. preds, cfg. blocks[from]. succs
269- deleteat! (preds, findfirst (x:: Int -> x== from, preds):: Int )
270- deleteat! (succs, findfirst (x:: Int -> x== to, succs):: Int )
280+ kill_cfg_edge! (cfg, from, to)
271281
272282 # check for unreachable target
273283 enqueued = enqueue_if_unreachable! (reach, cfg, to)
@@ -295,5 +305,6 @@ function kill_edge!(reach::CFGReachability, cfg::CFG, from::Int, to::Int,
295305 edge_callback (node, succ)
296306 end
297307 end
308+ empty! (cfg. blocks[node]. succs)
298309 end
299310end
0 commit comments