vxx2 / vlib / v3 / ssa / optimize / dominators.v
401 lines · 371 sloc · 8.34 KB · 6c4d26f1f98c5464b012a375667ab345e462f7e0
Raw
1module optimize
2
3import v3.ssa
4
5// CfgData holds successor/predecessor lists indexed by block id. v3 branch and
6// phi operands store raw block ids, so this is built straight from the CFG that
7// build_cfg() already wrote into m.blocks.
8struct CfgData {
9mut:
10 succs [][]int
11 preds [][]int
12}
13
14// cfg_data_from_module snapshots the successor/predecessor sets from m.blocks.
15// build_cfg(mut m) must have been called first.
16fn cfg_data_from_module(m &ssa.Module) CfgData {
17 n_blocks := m.blocks.len
18 mut cfg := CfgData{
19 succs: [][]int{len: n_blocks}
20 preds: [][]int{len: n_blocks}
21 }
22 for bi in 0 .. n_blocks {
23 mut s := []int{cap: m.blocks[bi].succs.len}
24 for x in m.blocks[bi].succs {
25 s << int(x)
26 }
27 cfg.succs[bi] = s
28 mut p := []int{cap: m.blocks[bi].preds.len}
29 for x in m.blocks[bi].preds {
30 p << int(x)
31 }
32 cfg.preds[bi] = p
33 }
34 return cfg
35}
36
37// --- Dominators (Lengauer-Tarjan) ---
38
39// LTContext stores ltcontext state used by optimize.
40struct LTContext {
41mut:
42 parent []int
43 semi []int
44 vertex []int
45 bucket [][]int
46 dfnum []int
47 ancestor []int
48 label []int
49 idom []int
50 dom_tree [][]int
51 n int
52}
53
54// DfsFrame represents dfs frame data used by optimize.
55struct DfsFrame {
56mut:
57 node int
58 succ_idx int
59}
60
61// DomInfo holds dominator results in flat arrays indexed by block id.
62struct DomInfo {
63mut:
64 idom []int
65 dom_tree [][]int
66}
67
68// compute_dominators runs Lengauer-Tarjan for every function and also writes the
69// idom / dom_tree results back into m.blocks for the verifier's dominance checks.
70fn compute_dominators(mut m ssa.Module, cfg &CfgData) DomInfo {
71 max_id := m.blocks.len
72
73 mut ctx := LTContext{
74 parent: []int{len: max_id, init: -1}
75 semi: []int{len: max_id, init: -1}
76 vertex: []int{len: max_id + 1, init: -1}
77 bucket: [][]int{len: max_id}
78 dfnum: []int{len: max_id, init: 0}
79 ancestor: []int{len: max_id, init: -1}
80 label: []int{len: max_id, init: -1}
81 idom: []int{len: max_id, init: -1}
82 dom_tree: [][]int{len: max_id}
83 n: 0
84 }
85
86 for fi in 0 .. m.funcs.len {
87 func := m.funcs[fi]
88 if func.blocks.len == 0 {
89 continue
90 }
91
92 mut valid := true
93 n_func_blocks := func.blocks.len
94 for fbi in 0 .. n_func_blocks {
95 blk_id := func.blocks[fbi]
96 if blk_id < 0 || blk_id >= max_id {
97 valid = false
98 break
99 }
100 for s in cfg.succs[blk_id] {
101 if s < 0 || s >= max_id {
102 valid = false
103 break
104 }
105 }
106 if !valid {
107 break
108 }
109 for p in cfg.preds[blk_id] {
110 if p < 0 || p >= max_id {
111 valid = false
112 break
113 }
114 }
115 if !valid {
116 break
117 }
118 }
119 if !valid {
120 continue
121 }
122
123 ctx.n = 0
124 for fbi2 in 0 .. n_func_blocks {
125 blk_id := func.blocks[fbi2]
126 ctx.parent[blk_id] = -1
127 ctx.semi[blk_id] = blk_id
128 ctx.vertex[blk_id] = -1
129 ctx.bucket[blk_id] = []
130 ctx.dfnum[blk_id] = 0
131 ctx.ancestor[blk_id] = -1
132 ctx.label[blk_id] = blk_id
133 ctx.idom[blk_id] = -1
134 ctx.dom_tree[blk_id] = []
135 }
136
137 entry := func.blocks[0]
138 lt_dfs(entry, cfg, mut ctx)
139
140 for i := ctx.n; i >= 2; i-- {
141 if i >= ctx.vertex.len {
142 continue
143 }
144 w := ctx.vertex[i]
145 if w < 0 || w >= max_id {
146 continue
147 }
148
149 for p in cfg.preds[w] {
150 if p < 0 || p >= max_id || ctx.dfnum[p] == 0 {
151 continue
152 }
153 u := ctx.eval(p)
154 if u < 0 || u >= max_id {
155 continue
156 }
157 semi_u := ctx.semi[u]
158 semi_w := ctx.semi[w]
159 if semi_u < 0 || semi_u >= max_id || semi_w < 0 || semi_w >= max_id {
160 continue
161 }
162 if ctx.dfnum[semi_u] < ctx.dfnum[semi_w] {
163 ctx.semi[w] = ctx.semi[u]
164 }
165 }
166
167 semi_w2 := ctx.semi[w]
168 if semi_w2 >= 0 && semi_w2 < max_id {
169 mut b := ctx.bucket[semi_w2]
170 b << w
171 ctx.bucket[semi_w2] = b
172 }
173
174 ctx.link(ctx.parent[w], w)
175
176 parent_w := ctx.parent[w]
177 if parent_w < 0 || parent_w >= max_id {
178 continue
179 }
180 for v in ctx.bucket[parent_w] {
181 if v < 0 || v >= max_id {
182 continue
183 }
184 u := ctx.eval(v)
185 if u < 0 || u >= max_id {
186 continue
187 }
188 if ctx.semi[u] == ctx.semi[v] {
189 ctx.idom[v] = parent_w
190 } else {
191 ctx.idom[v] = u
192 }
193 }
194 ctx.bucket[parent_w] = []
195 }
196
197 for i := 2; i <= ctx.n; i++ {
198 if i >= ctx.vertex.len {
199 continue
200 }
201 w := ctx.vertex[i]
202 if w < 0 || w >= max_id {
203 continue
204 }
205 semi_w := ctx.semi[w]
206 if semi_w < 0 || semi_w >= max_id {
207 continue
208 }
209 dfnum_semi_w := ctx.dfnum[semi_w]
210 if dfnum_semi_w < 0 || dfnum_semi_w >= ctx.vertex.len {
211 continue
212 }
213 target := ctx.vertex[dfnum_semi_w]
214 if target < 0 {
215 continue
216 }
217 if ctx.idom[w] != target {
218 idom_w := ctx.idom[w]
219 if idom_w >= 0 && idom_w < max_id {
220 next_idom := ctx.idom[idom_w]
221 if next_idom >= 0 {
222 ctx.idom[w] = next_idom
223 }
224 }
225 }
226 }
227
228 ctx.idom[entry] = entry
229
230 for fbi4 in 0 .. n_func_blocks {
231 blk_id := func.blocks[fbi4]
232 idom := ctx.idom[blk_id]
233 if idom != -1 && idom != blk_id {
234 if idom >= 0 && idom < max_id {
235 mut t := ctx.dom_tree[idom]
236 t << blk_id
237 ctx.dom_tree[idom] = t
238 }
239 }
240 }
241 }
242
243 // Write idom / dom_tree back into the blocks for verifier dominance checks.
244 for bi in 0 .. max_id {
245 mut blk := m.blocks[bi]
246 blk.idom = ctx.idom[bi]
247 blk.dom_tree = []ssa.BlockID{cap: ctx.dom_tree[bi].len}
248 for c in ctx.dom_tree[bi] {
249 blk.dom_tree << ssa.BlockID(c)
250 }
251 m.blocks[bi] = blk
252 }
253
254 return DomInfo{
255 idom: ctx.idom
256 dom_tree: ctx.dom_tree
257 }
258}
259
260// lt_dfs supports lt dfs handling for optimize.
261fn lt_dfs(root int, cfg &CfgData, mut ctx LTContext) {
262 if root < 0 || root >= ctx.dfnum.len {
263 return
264 }
265 n_total_blocks := cfg.succs.len
266
267 mut stack := []DfsFrame{}
268 ctx.n++
269 if ctx.n >= ctx.vertex.len {
270 return
271 }
272 ctx.dfnum[root] = ctx.n
273 ctx.vertex[ctx.n] = root
274
275 stack << DfsFrame{
276 node: root
277 }
278
279 for stack.len > 0 {
280 top := stack.len - 1
281 node := stack[top].node
282 if node < 0 || node >= n_total_blocks {
283 stack.pop()
284 continue
285 }
286 n_succs := cfg.succs[node].len
287 si := stack[top].succ_idx
288 if si < n_succs {
289 mut frame := stack[top]
290 frame.succ_idx++
291 stack[top] = frame
292 w := cfg.succs[node][si]
293 if w >= 0 && w < ctx.dfnum.len && ctx.dfnum[w] == 0 {
294 ctx.parent[w] = node
295 ctx.n++
296 if ctx.n >= ctx.vertex.len {
297 return
298 }
299 ctx.dfnum[w] = ctx.n
300 ctx.vertex[ctx.n] = w
301 stack << DfsFrame{
302 node: w
303 }
304 }
305 } else {
306 stack.pop()
307 }
308 }
309}
310
311// compress supports compress handling for LTContext.
312fn (mut ctx LTContext) compress(v int) {
313 if v < 0 || v >= ctx.ancestor.len {
314 return
315 }
316 mut chain := []int{}
317 mut cur := v
318 for cur >= 0 && cur < ctx.ancestor.len {
319 av := ctx.ancestor[cur]
320 if av < 0 || av >= ctx.ancestor.len {
321 break
322 }
323 if ctx.ancestor[av] == -1 {
324 break
325 }
326 chain << cur
327 cur = av
328 }
329 for ci := chain.len - 1; ci >= 0; ci-- {
330 node := chain[ci]
331 if node < 0 || node >= ctx.ancestor.len {
332 continue
333 }
334 anc := ctx.ancestor[node]
335 if anc < 0 || anc >= ctx.label.len || node >= ctx.label.len {
336 continue
337 }
338 label_anc := ctx.label[anc]
339 label_node := ctx.label[node]
340 if label_anc < 0 || label_anc >= ctx.semi.len || label_node < 0
341 || label_node >= ctx.semi.len {
342 continue
343 }
344 semi_label_anc := ctx.semi[label_anc]
345 semi_label_node := ctx.semi[label_node]
346 if semi_label_anc < 0 || semi_label_anc >= ctx.dfnum.len || semi_label_node < 0
347 || semi_label_node >= ctx.dfnum.len {
348 continue
349 }
350 if ctx.dfnum[semi_label_anc] < ctx.dfnum[semi_label_node] {
351 ctx.label[node] = ctx.label[anc]
352 }
353 aav := ctx.ancestor[anc]
354 if aav >= 0 {
355 ctx.ancestor[node] = aav
356 }
357 }
358}
359
360// eval supports eval handling for LTContext.
361fn (mut ctx LTContext) eval(v int) int {
362 if v < 0 || v >= ctx.ancestor.len {
363 if v >= 0 && v < ctx.label.len {
364 return ctx.label[v]
365 }
366 return 0
367 }
368 if ctx.ancestor[v] == -1 {
369 return ctx.label[v]
370 }
371 ctx.compress(v)
372 av := ctx.ancestor[v]
373 if av < 0 || av >= ctx.label.len {
374 if v < ctx.label.len {
375 return ctx.label[v]
376 }
377 return 0
378 }
379 if v >= ctx.label.len {
380 return 0
381 }
382 label_av := ctx.label[av]
383 label_v := ctx.label[v]
384 if label_av < 0 || label_av >= ctx.semi.len || label_v < 0 || label_v >= ctx.semi.len {
385 return label_v
386 }
387 semi_lav := ctx.semi[label_av]
388 semi_lv := ctx.semi[label_v]
389 if semi_lav < 0 || semi_lav >= ctx.dfnum.len || semi_lv < 0 || semi_lv >= ctx.dfnum.len {
390 return label_v
391 }
392 if ctx.dfnum[semi_lav] >= ctx.dfnum[semi_lv] {
393 return label_v
394 }
395 return label_av
396}
397
398// link supports link handling for LTContext.
399fn (mut ctx LTContext) link(v int, w int) {
400 ctx.ancestor[w] = v
401}
402