v4 / vlib / v3 / tests / ssa_worker_test.v
79 lines · 68 sloc · 2.76 KB · 1704098da5411db759596f32d61e0eacc8975e39
Raw
1import v3.ssa
2import v3.ssa.optimize
3
4// Build a function body inside a worker module and merge it back, exercising the
5// raw-block-id operand remapping (branch/phi block operands must shift by the
6// block offset, value operands by the value offset).
7fn test_worker_clone_merge_remaps_block_and_value_operands() {
8 mut m := ssa.Module.new()
9 i64t := m.type_store.get_int(64)
10 // A pre-existing (seed) constant and function registered serially.
11 _ := m.get_or_add_const(i64t, '111')
12 fidx := m.new_function('compute', i64t)
13
14 // Clone a worker and snapshot the seed sizes.
15 mut w := m.new_worker_module()
16 seed_values := m.values.len
17 seed_instrs := m.instrs.len
18 seed_blocks := m.blocks.len
19 seed_types := m.type_store.types.len
20 seed_funcs := m.funcs.len
21
22 // Build a diamond + merge phi entirely inside the worker.
23 entry := w.add_block(fidx, 'entry')
24 then_b := w.add_block(fidx, 'then')
25 else_b := w.add_block(fidx, 'else')
26 merge := w.add_block(fidx, 'merge')
27
28 cond := w.get_or_add_const(w.type_store.get_int(1), '1')
29 w.add_instr(.br, entry, ssa.TypeID(0), [cond, ssa.ValueID(then_b), ssa.ValueID(else_b)])
30
31 ten := w.get_or_add_const(i64t, '10')
32 w.add_instr(.jmp, then_b, ssa.TypeID(0), [ssa.ValueID(merge)])
33 twenty := w.get_or_add_const(i64t, '20')
34 w.add_instr(.jmp, else_b, ssa.TypeID(0), [ssa.ValueID(merge)])
35
36 // phi: [ten, then_b, twenty, else_b] (odd operands are raw block ids)
37 phi := w.add_instr(.phi, merge, i64t, [ten, ssa.ValueID(then_b), twenty, ssa.ValueID(else_b)])
38 w.add_instr(.ret, merge, ssa.TypeID(0), [phi])
39
40 fd := ssa.FuncSSAData{
41 func_idx: fidx
42 blocks: w.funcs[fidx].blocks
43 params: w.funcs[fidx].params
44 }
45
46 m.merge_worker_module(w, [fd], seed_values, seed_instrs, seed_blocks, seed_types, seed_funcs)
47
48 // The function now has its 4 blocks, all parented correctly.
49 assert m.funcs[fidx].blocks.len == 4
50 for blk_id in m.funcs[fidx].blocks {
51 assert blk_id >= 0 && blk_id < m.blocks.len
52 assert m.blocks[blk_id].parent == fidx
53 }
54
55 // The merged branch must target real blocks inside the function (this fails if
56 // block operands were remapped as values).
57 entry_id := m.funcs[fidx].blocks[0]
58 term_id := m.blocks[entry_id].instrs.last()
59 term := m.instrs[m.values[term_id].index]
60 assert term.op == .br
61 for oi in [1, 2] {
62 tgt := int(term.operands[oi])
63 assert tgt >= 0 && tgt < m.blocks.len
64 assert tgt in m.funcs[fidx].blocks
65 }
66
67 // The whole thing must verify and optimize cleanly (optimize() panics on a
68 // broken CFG / dangling phi predecessor, so reaching the asserts proves the
69 // remapping is consistent).
70 optimize.optimize_with_options(mut m, optimize.OptimizeOptions{
71 mem2reg: false
72 eliminate_phis: true
73 })
74 errors := optimize.verify(m)
75 for e in errors {
76 assert !e.msg.contains('invalid')
77 assert !e.msg.contains('outside function')
78 }
79}
80