vq / vlib / v3 / ssa / optimize / blocks.v
155 lines · 143 sloc · 3.77 KB · 6c4d26f1f98c5464b012a375667ab345e462f7e0
Raw
1module optimize
2
3import v3.ssa
4
5// remove_unreachable_blocks updates remove unreachable blocks state for optimize.
6fn remove_unreachable_blocks(mut m ssa.Module) {
7 build_cfg(mut m)
8 for fi in 0 .. m.funcs.len {
9 if m.funcs[fi].blocks.len == 0 {
10 continue
11 }
12 mut reachable := map[int]bool{}
13 mut q := []int{}
14 q << m.funcs[fi].blocks[0]
15 reachable[m.funcs[fi].blocks[0]] = true
16
17 for q.len > 0 {
18 curr := q.last()
19 q.delete_last()
20 for succ in m.blocks[curr].succs {
21 if !reachable[succ] {
22 reachable[succ] = true
23 q << succ
24 }
25 }
26 }
27
28 mut new_blocks := []int{}
29 for blk in m.funcs[fi].blocks {
30 if reachable[blk] {
31 new_blocks << blk
32 }
33 }
34 mut func := m.funcs[fi]
35 func.blocks = new_blocks
36 m.funcs[fi] = func
37 }
38}
39
40// merge_blocks supports merge blocks handling for optimize.
41fn merge_blocks(mut m ssa.Module) {
42 mut changed := true
43 mut first := true
44 for changed {
45 changed = false
46 if first {
47 build_cfg(mut m)
48 first = false
49 }
50
51 for fi in 0 .. m.funcs.len {
52 mut merged := map[int]bool{}
53
54 for blk_id in m.funcs[fi].blocks {
55 if merged[blk_id] {
56 continue
57 }
58 if m.blocks[blk_id].instrs.len == 0 {
59 continue
60 }
61 last_val := m.blocks[blk_id].instrs.last()
62 last_instr := m.instrs[m.values[last_val].index]
63 if last_instr.op != .jmp || last_instr.operands.len < 1 {
64 continue
65 }
66 target_id := int(last_instr.operands[0])
67 if target_id < 0 || target_id >= m.blocks.len || target_id == blk_id {
68 continue
69 }
70 if m.blocks[target_id].preds.len != 1 || m.blocks[target_id].preds[0] != blk_id {
71 continue
72 }
73
74 // Phi-safety: don't merge into a block that holds phi nodes. Its phis
75 // reference predecessors by block id; collapsing the edge would leave
76 // dangling predecessor operands.
77 mut target_has_phi := false
78 for vid in m.blocks[target_id].instrs {
79 if vid > 0 && vid < m.values.len && m.values[vid].kind == .instruction {
80 if m.instrs[m.values[vid].index].op == .phi {
81 target_has_phi = true
82 break
83 }
84 }
85 }
86 if target_has_phi {
87 continue
88 }
89
90 // Merge: remove jmp from A, append B's instrs (and reparent them).
91 mut blk := m.blocks[blk_id]
92 blk.instrs.delete_last()
93 for moved_val in m.blocks[target_id].instrs {
94 blk.instrs << moved_val
95 if moved_val > 0 && moved_val < m.values.len
96 && m.values[moved_val].kind == .instruction {
97 instr_idx := m.values[moved_val].index
98 if instr_idx >= 0 && instr_idx < m.instrs.len {
99 mut instr := m.instrs[instr_idx]
100 instr.block = blk_id
101 m.instrs[instr_idx] = instr
102 }
103 }
104 }
105 m.blocks[blk_id] = blk
106
107 // B's successors now have A as a predecessor instead of B. Rewrite any
108 // phi predecessor operands that named B (raw block id) to name A.
109 for succ_id in m.blocks[target_id].succs {
110 if succ_id < 0 || succ_id >= m.blocks.len {
111 continue
112 }
113 for iv in m.blocks[succ_id].instrs {
114 if iv <= 0 || iv >= m.values.len || m.values[iv].kind != .instruction {
115 continue
116 }
117 idx := m.values[iv].index
118 if m.instrs[idx].op != .phi {
119 continue
120 }
121 mut phi_ins := m.instrs[idx]
122 mut phi_modified := false
123 for i := 1; i < phi_ins.operands.len; i += 2 {
124 if int(phi_ins.operands[i]) == target_id {
125 phi_ins.operands[i] = ssa.ValueID(blk_id)
126 phi_modified = true
127 }
128 }
129 if phi_modified {
130 m.instrs[idx] = phi_ins
131 }
132 }
133 }
134
135 merged[target_id] = true
136 changed = true
137 }
138
139 if merged.len > 0 {
140 mut new_blks := []int{}
141 for b in m.funcs[fi].blocks {
142 if !merged[b] {
143 new_blks << b
144 }
145 }
146 mut func := m.funcs[fi]
147 func.blocks = new_blks
148 m.funcs[fi] = func
149 }
150 }
151 if changed {
152 build_cfg(mut m)
153 }
154 }
155}
156