@@ -46,7 +46,10 @@ function CFGReachability(cfg::CFG, domtree::DomTree)
46
46
Int[], # _worklist
47
47
SCCStackItem[], # _stack
48
48
)
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
+ )
50
53
return reachability
51
54
end
52
55
@@ -59,12 +62,12 @@ bb_in_irreducible_loop(reach::CFGReachability, bb::Int) = reach.irreducible[bb]
59
62
#
60
63
# `tarjan!` takes the transitive closure of this relation in order to detect
61
64
# 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 )
63
66
(; scc) = reach
64
67
bb == 1 && return true
65
68
for pred in cfg. blocks[bb]. preds
66
69
scc[pred] <= 0 && continue
67
- dominates (reach . domtree , bb, pred ) && continue
70
+ ! filter (pred , bb) && continue
68
71
@assert scc[pred] != scc[bb]
69
72
return true
70
73
end
@@ -85,10 +88,12 @@ Outputs:
85
88
- `reach._worklist`: if performing an incremental update (`root != 1`), any traversed nodes that
86
89
are unreachable from BasicBlock #1 are enqueued to this worklist
87
90
"""
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
90
95
scc[root] != 0 && return scc
91
- live = _bb_externally_reachable (reach, cfg, root)
96
+ live = _bb_externally_reachable (reach, cfg, root; filter )
92
97
93
98
# the original algorithm has a separate stack and worklist (unrelated to `reach._worklist`)
94
99
# 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)
117
122
stack[cursor] = item = SCCStackItem (item; child= child+ 1 )
118
123
succ = bb. succs[child]
119
124
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
126
127
127
128
if scc[succ] < 0
128
129
# next child is already in DFS tree
@@ -134,7 +135,7 @@ function tarjan!(reach::CFGReachability, cfg::CFG; root::Int=1)
134
135
elseif scc[succ] == 0
135
136
# next child is a new element in DFS tree
136
137
preorder_id += 1
137
- live = live || _bb_externally_reachable (reach, cfg, succ)
138
+ live = live || _bb_externally_reachable (reach, cfg, succ; filter )
138
139
push! (stack, SCCStackItem (
139
140
succ, # v
140
141
1 , # child
@@ -154,7 +155,7 @@ function tarjan!(reach::CFGReachability, cfg::CFG; root::Int=1)
154
155
if live
155
156
scc[item. v] = v
156
157
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 ),
158
159
#= action =# (x)-> (scc[x] -= typemax (Int)÷ 2 ;),
159
160
)
160
161
else # this offset marks a node as 'maybe-dead'
@@ -181,19 +182,19 @@ function tarjan!(reach::CFGReachability, cfg::CFG; root::Int=1)
181
182
worklist = reach. _worklist
182
183
183
184
# filter the worklist, leaving any nodes not proven to be reachable from BB #1
184
- n_filtered = 0
185
+ n_popped = 0
185
186
for i = (worklist_len + 1 ): length (worklist)
186
187
@assert worklist[i] != 1
187
188
@assert scc[worklist[i]] > 0
188
189
if scc[worklist[i]] > typemax (Int)÷ 2
189
190
# node is unreachable, enqueue it
190
191
scc[worklist[i]] = 0
191
- worklist[i - n_filtered ] = worklist[i]
192
+ worklist[i - n_popped ] = worklist[i]
192
193
else
193
- n_filtered += 1
194
+ n_popped += 1
194
195
end
195
196
end
196
- resize! (worklist, length (worklist) - n_filtered )
197
+ resize! (worklist, length (worklist) - n_popped )
197
198
198
199
return length (worklist) > worklist_len # if true, a (newly) unreachable node was enqueued
199
200
end
@@ -228,16 +229,20 @@ function enqueue_if_unreachable!(reach::CFGReachability, cfg::CFG, bb::Int)
228
229
# irreducible CFG
229
230
# this requires a full scan of the irreducible loop
230
231
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
+
231
236
scc′ = scc[bb]
232
237
scc[bb] = 0
233
238
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′),
235
240
#= action =# (x)-> (scc[x] = 0 ;),
236
241
)
237
242
238
243
# re-compute the SCC's for this portion of the CFG, adding any freshly
239
244
# unreachable nodes to `reach._worklist`
240
- return tarjan! (reach, cfg; root= bb)
245
+ return tarjan! (reach, cfg; root= bb, filter )
241
246
else
242
247
# target is a reducible CFG node
243
248
# 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)
250
255
end
251
256
end
252
257
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
+
253
265
"""
254
266
Remove from `cfg` and `reach` the edge (from → to), as well as any blocks/edges
255
267
this causes to become unreachable.
@@ -265,9 +277,7 @@ function kill_edge!(reach::CFGReachability, cfg::CFG, from::Int, to::Int,
265
277
@assert reach. scc[to] != 0
266
278
267
279
# 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)
271
281
272
282
# check for unreachable target
273
283
enqueued = enqueue_if_unreachable! (reach, cfg, to)
@@ -295,5 +305,6 @@ function kill_edge!(reach::CFGReachability, cfg::CFG, from::Int, to::Int,
295
305
edge_callback (node, succ)
296
306
end
297
307
end
308
+ empty! (cfg. blocks[node]. succs)
298
309
end
299
310
end
0 commit comments