vxx / vlib / v3 / ssa / optimize / verifier.v
271 lines · 258 sloc · 8.29 KB · 6c4d26f1f98c5464b012a375667ab345e462f7e0
Raw
1module optimize
2
3import v3.ssa
4
5// verify_ssa validates verify ssa state for optimize.
6fn verify_ssa(m &ssa.Module, stage string) {
7 verify_module_indexes(m, stage)
8 verify_functions(m, stage)
9 verify_uses(m, stage)
10}
11
12// verify_module_indexes validates verify module indexes state for optimize.
13fn verify_module_indexes(m &ssa.Module, stage string) {
14 for vi in 0 .. m.values.len {
15 if m.values[vi].id != vi {
16 verify_fail(stage, 'value ${vi} has id ${m.values[vi].id}')
17 }
18 if m.values[vi].kind == .instruction {
19 instr_idx := m.values[vi].index
20 if instr_idx < 0 || instr_idx >= m.instrs.len {
21 verify_fail(stage,
22 'instruction value ${vi} has invalid instruction index ${instr_idx}')
23 }
24 }
25 }
26 for bi in 0 .. m.blocks.len {
27 if m.blocks[bi].id != bi {
28 verify_fail(stage, 'block ${bi} has id ${m.blocks[bi].id}')
29 }
30 }
31 for fi in 0 .. m.funcs.len {
32 if m.funcs[fi].id != fi {
33 verify_fail(stage, 'function ${fi} has id ${m.funcs[fi].id}')
34 }
35 }
36}
37
38// verify_functions validates verify functions state for optimize.
39fn verify_functions(m &ssa.Module, stage string) {
40 for fi in 0 .. m.funcs.len {
41 func := m.funcs[fi]
42 mut func_blocks := map[int]bool{}
43 for blk_id in func.blocks {
44 if !verify_block_id(m, stage, blk_id, fi, 'function block list') {
45 continue
46 }
47 if func_blocks[blk_id] {
48 verify_fail(stage, 'function ${fi} contains duplicate block ${blk_id}')
49 }
50 func_blocks[blk_id] = true
51 }
52 for blk_id in func.blocks {
53 if !verify_block_id(m, stage, blk_id, fi, 'function block list') {
54 continue
55 }
56 blk := m.blocks[blk_id]
57 if blk.parent != fi {
58 verify_fail(stage, 'block ${blk_id} parent is ${blk.parent}, expected ${fi}')
59 }
60 verify_block_instrs(m, stage, fi, blk_id, func_blocks)
61 verify_cfg_edges(m, stage, fi, blk_id, func_blocks)
62 }
63 }
64}
65
66// verify_block_instrs validates verify block instrs state for optimize.
67fn verify_block_instrs(m &ssa.Module, stage string, fi int, blk_id int, func_blocks map[int]bool) {
68 blk := m.blocks[blk_id]
69 if blk.instrs.len == 0 {
70 return
71 }
72 for ii, val_id in blk.instrs {
73 if val_id <= 0 || val_id >= m.values.len {
74 verify_fail(stage, 'block ${blk_id} contains invalid instruction value ${val_id}')
75 }
76 val := m.values[val_id]
77 if val.kind != .instruction {
78 verify_fail(stage, 'block ${blk_id} contains non-instruction value ${val_id}')
79 }
80 if val.index < 0 || val.index >= m.instrs.len {
81 verify_fail(stage,
82 'instruction value ${val_id} has invalid instruction index ${val.index}')
83 }
84 instr := m.instrs[val.index]
85 if instr.block != blk_id {
86 verify_fail(stage,
87 'instruction value ${val_id} is in block ${blk_id} but records block ${instr.block}')
88 }
89 verify_instruction_operands(m, stage, fi, blk_id, val_id, instr, func_blocks,
90 ii == blk.instrs.len - 1)
91 }
92}
93
94// verify_instruction_operands validates verify instruction operands state for optimize.
95fn verify_instruction_operands(m &ssa.Module, stage string, fi int, blk_id int, val_id int, instr ssa.Instruction, func_blocks map[int]bool, is_final bool) {
96 for op_id in instr.value_operands() {
97 if op_id <= 0 || op_id >= m.values.len {
98 verify_fail(stage,
99 'instruction value ${val_id} in block ${blk_id} uses invalid value ${op_id}')
100 }
101 if m.values[op_id].kind == .unknown {
102 verify_fail(stage,
103 'instruction value ${val_id} in block ${blk_id} uses unknown value ${op_id}')
104 }
105 }
106 match instr.op {
107 .ret {
108 if instr.operands.len > 1 {
109 verify_fail(stage,
110 'ret value ${val_id} in block ${blk_id} has ${instr.operands.len} operands')
111 }
112 }
113 .jmp {
114 if instr.operands.len != 1 {
115 verify_fail(stage,
116 'jmp value ${val_id} in block ${blk_id} has ${instr.operands.len} operands')
117 } else if is_final {
118 verify_target_block(m, stage, fi, blk_id, int(instr.operands[0]), func_blocks,
119 'jmp')
120 }
121 }
122 .unreachable {
123 if instr.operands.len != 0 {
124 verify_fail(stage,
125 'unreachable value ${val_id} in block ${blk_id} has ${instr.operands.len} operands')
126 }
127 }
128 .br {
129 if instr.operands.len != 3 {
130 verify_fail(stage,
131 'br value ${val_id} in block ${blk_id} has ${instr.operands.len} operands')
132 } else if is_final {
133 verify_target_block(m, stage, fi, blk_id, int(instr.operands[1]), func_blocks,
134 'br true')
135 verify_target_block(m, stage, fi, blk_id, int(instr.operands[2]), func_blocks,
136 'br false')
137 }
138 }
139 .phi {
140 if instr.operands.len == 0 || instr.operands.len % 2 != 0 {
141 verify_fail(stage,
142 'phi value ${val_id} in block ${blk_id} has ${instr.operands.len} operands')
143 }
144 for oi := 1; oi < instr.operands.len; oi += 2 {
145 verify_target_block(m, stage, fi, blk_id, int(instr.operands[oi]), func_blocks,
146 'phi predecessor')
147 }
148 }
149 else {}
150 }
151}
152
153// verify_target_block validates verify target block state for optimize.
154fn verify_target_block(m &ssa.Module, stage string, fi int, blk_id int, target_id int, func_blocks map[int]bool, label string) {
155 if !verify_block_id(m, stage, target_id, fi, '${label} target from block ${blk_id}') {
156 return
157 }
158 if !func_blocks[target_id] {
159 verify_fail(stage,
160 '${label} target ${target_id} from block ${blk_id} is outside function ${fi}')
161 }
162}
163
164// verify_cfg_edges validates verify cfg edges state for optimize.
165fn verify_cfg_edges(m &ssa.Module, stage string, fi int, blk_id int, func_blocks map[int]bool) {
166 blk := m.blocks[blk_id]
167 mut expected_succs := []int{}
168 if blk.instrs.len > 0 {
169 term_val_id := blk.instrs.last()
170 term := m.instrs[m.values[term_val_id].index]
171 match term.op {
172 .jmp {
173 if term.operands.len == 1 {
174 expected_succs << int(term.operands[0])
175 }
176 }
177 .br {
178 if term.operands.len == 3 {
179 expected_succs << int(term.operands[1])
180 if term.operands[2] != term.operands[1] {
181 expected_succs << int(term.operands[2])
182 }
183 }
184 }
185 else {}
186 }
187 }
188 verify_same_blocks(stage, 'successors for block ${blk_id}', expected_succs, blk.succs)
189
190 for succ in blk.succs {
191 if !verify_block_id(m, stage, succ, fi, 'successor of block ${blk_id}') {
192 continue
193 }
194 if !func_blocks[succ] {
195 verify_fail(stage, 'successor ${succ} from block ${blk_id} is outside function ${fi}')
196 }
197 if !arr_contains(m.blocks[succ].preds, blk_id) {
198 verify_fail(stage, 'successor ${succ} is missing predecessor ${blk_id}')
199 }
200 }
201 for pred in blk.preds {
202 if !verify_block_id(m, stage, pred, fi, 'predecessor of block ${blk_id}') {
203 continue
204 }
205 if !func_blocks[pred] {
206 verify_fail(stage, 'predecessor ${pred} of block ${blk_id} is outside function ${fi}')
207 }
208 if !arr_contains(m.blocks[pred].succs, blk_id) {
209 verify_fail(stage, 'predecessor ${pred} is missing successor ${blk_id}')
210 }
211 }
212}
213
214// verify_uses validates verify uses state for optimize.
215fn verify_uses(m &ssa.Module, stage string) {
216 mut expected_uses := map[int][]int{}
217 for func in m.funcs {
218 for blk_id in func.blocks {
219 if blk_id < 0 || blk_id >= m.blocks.len {
220 continue
221 }
222 for val_id in m.blocks[blk_id].instrs {
223 if val_id <= 0 || val_id >= m.values.len || m.values[val_id].kind != .instruction {
224 continue
225 }
226 instr := m.instrs[m.values[val_id].index]
227 for op_id in instr.value_operands() {
228 if op_id > 0 && op_id < m.values.len {
229 if !arr_contains(expected_uses[op_id], val_id) {
230 expected_uses[op_id] << val_id
231 }
232 }
233 }
234 }
235 }
236 }
237 for vi in 0 .. m.values.len {
238 verify_same_blocks(stage, 'uses for value ${vi}', expected_uses[vi], m.values[vi].uses)
239 }
240}
241
242// verify_block_id validates verify block id state for optimize.
243fn verify_block_id(m &ssa.Module, stage string, blk_id int, fi int, context string) bool {
244 if blk_id < 0 || blk_id >= m.blocks.len {
245 verify_fail(stage, '${context} references invalid block ${blk_id} in function ${fi}')
246 return false
247 }
248 return true
249}
250
251// verify_same_blocks validates verify same blocks state for optimize.
252fn verify_same_blocks(stage string, label string, expected []int, actual []int) {
253 if expected.len != actual.len {
254 verify_fail(stage, '${label}: expected ${expected}, got ${actual}')
255 }
256 for v in expected {
257 if !arr_contains(actual, v) {
258 verify_fail(stage, '${label}: expected ${expected}, got ${actual}')
259 }
260 }
261 for v in actual {
262 if !arr_contains(expected, v) {
263 verify_fail(stage, '${label}: expected ${expected}, got ${actual}')
264 }
265 }
266}
267
268// verify_fail validates verify fail state for optimize.
269fn verify_fail(stage string, msg string) {
270 panic('SSA verifier failed after ${stage}: ${msg}')
271}
272