v / vlib / v2 / ssa / optimize / verify_test.v
219 lines · 178 sloc · 6.29 KB · 5807d31b40f8a116454f70eec6cdf8c766512d1f
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4// vtest build: macos
5
6module optimize
7
8import v2.ssa
9
10fn test_verify_empty_function() {
11 mut m := ssa.Module.new('test')
12 func_id := m.new_function('test_fn', 0, [])
13
14 // Function with no blocks should fail
15 errors := verify(m)
16 assert errors.len > 0
17 assert errors[0].msg.contains('no blocks')
18}
19
20fn test_verify_missing_terminator() {
21 mut m := ssa.Module.new('test')
22 func_id := m.new_function('test_fn', 0, [])
23 blk_id := m.add_block(func_id, 'entry')
24
25 // Block with no instructions should fail (missing terminator)
26 errors := verify(m)
27 assert errors.len > 0
28 assert errors[0].msg.contains('no instructions') || errors[0].msg.contains('terminator')
29}
30
31fn test_verify_valid_function() {
32 mut m := ssa.Module.new('test')
33 i64_t := m.type_store.get_int(64)
34 func_id := m.new_function('test_fn', i64_t, [])
35 blk_id := m.add_block(func_id, 'entry')
36
37 // Create a constant
38 const_id := m.add_value_node(.constant, i64_t, 'const_42', 0)
39
40 // Add ret instruction with constant operand
41 m.add_instr(.ret, blk_id, 0, [const_id])
42
43 errors := verify(m)
44 assert errors.len == 0, 'expected no errors, got: ${errors}'
45}
46
47fn test_verify_binary_op_operand_count() {
48 mut m := ssa.Module.new('test')
49 i64_t := m.type_store.get_int(64)
50 func_id := m.new_function('test_fn', i64_t, [])
51 blk_id := m.add_block(func_id, 'entry')
52
53 // Create constants
54 const1 := m.add_value_node(.constant, i64_t, 'const_1', 0)
55 const2 := m.add_value_node(.constant, i64_t, 'const_2', 0)
56 const3 := m.add_value_node(.constant, i64_t, 'const_3', 0)
57
58 // Add binary op with wrong number of operands (3 instead of 2)
59 m.add_instr(.add, blk_id, i64_t, [const1, const2, const3])
60 m.add_instr(.ret, blk_id, 0, [])
61
62 errors := verify(m)
63 mut found_binary_error := false
64 for err in errors {
65 if err.msg.contains('binary op') && err.msg.contains('operands') {
66 found_binary_error = true
67 break
68 }
69 }
70 assert found_binary_error, 'expected binary op operand count error'
71}
72
73fn test_verify_load_non_pointer() {
74 mut m := ssa.Module.new('test')
75 i64_t := m.type_store.get_int(64)
76 func_id := m.new_function('test_fn', i64_t, [])
77 blk_id := m.add_block(func_id, 'entry')
78
79 // Create a non-pointer constant
80 const_id := m.add_value_node(.constant, i64_t, 'const_42', 0)
81
82 // Try to load from non-pointer (should error)
83 m.add_instr(.load, blk_id, i64_t, [const_id])
84 m.add_instr(.ret, blk_id, 0, [])
85
86 errors := verify(m)
87 mut found_load_error := false
88 for err in errors {
89 if err.msg.contains('load') && err.msg.contains('pointer') {
90 found_load_error = true
91 break
92 }
93 }
94 assert found_load_error, 'expected load non-pointer error'
95}
96
97fn test_verify_phi_odd_operands() {
98 mut m := ssa.Module.new('test')
99 i64_t := m.type_store.get_int(64)
100 func_id := m.new_function('test_fn', i64_t, [])
101 blk_id := m.add_block(func_id, 'entry')
102
103 // Create constant
104 const_id := m.add_value_node(.constant, i64_t, 'const_1', 0)
105
106 // Add phi with odd number of operands (should be pairs)
107 m.add_instr(.phi, blk_id, i64_t, [const_id]) // 1 operand instead of pairs
108 m.add_instr(.ret, blk_id, 0, [])
109
110 errors := verify(m)
111 mut found_phi_error := false
112 for err in errors {
113 if err.msg.contains('phi') && err.msg.contains('odd') {
114 found_phi_error = true
115 break
116 }
117 }
118 assert found_phi_error, 'expected phi odd operands error'
119}
120
121fn test_verify_branch_operand_count() {
122 mut m := ssa.Module.new('test')
123 i64_t := m.type_store.get_int(64)
124 func_id := m.new_function('test_fn', i64_t, [])
125 blk_id := m.add_block(func_id, 'entry')
126
127 // Create constant for condition
128 const_id := m.add_value_node(.constant, i64_t, 'cond', 0)
129
130 // Add br with wrong number of operands (1 instead of 3)
131 m.add_instr(.br, blk_id, 0, [const_id])
132
133 errors := verify(m)
134 mut found_br_error := false
135 for err in errors {
136 if err.msg.contains('br') && err.msg.contains('operands') {
137 found_br_error = true
138 break
139 }
140 }
141 assert found_br_error, 'expected br operand count error'
142}
143
144fn test_verify_jump_target_not_block() {
145 mut m := ssa.Module.new('test')
146 i64_t := m.type_store.get_int(64)
147 func_id := m.new_function('test_fn', i64_t, [])
148 blk_id := m.add_block(func_id, 'entry')
149
150 // Create constant (not a block)
151 const_id := m.add_value_node(.constant, i64_t, 'not_a_block', 0)
152
153 // Add jmp targeting non-block
154 m.add_instr(.jmp, blk_id, 0, [const_id])
155
156 errors := verify(m)
157 mut found_jmp_error := false
158 for err in errors {
159 if err.msg.contains('jmp') && err.msg.contains('basic_block') {
160 found_jmp_error = true
161 break
162 }
163 }
164 assert found_jmp_error, 'expected jmp target not block error'
165}
166
167fn test_verify_terminator_not_last() {
168 mut m := ssa.Module.new('test')
169 i64_t := m.type_store.get_int(64)
170 func_id := m.new_function('test_fn', i64_t, [])
171 blk_id := m.add_block(func_id, 'entry')
172
173 // Create block for jump target
174 target_blk := m.add_block(func_id, 'target')
175 target_val := m.blocks[target_blk].val_id
176
177 // Add terminator in middle, then another instruction
178 m.add_instr(.ret, blk_id, 0, [])
179
180 // Manually add another instruction after terminator
181 const_id := m.add_value_node(.constant, i64_t, 'const', 0)
182 m.add_instr(.add, blk_id, i64_t, [const_id, const_id])
183
184 // Add terminator to target block
185 m.add_instr(.ret, target_blk, 0, [])
186
187 errors := verify(m)
188 mut found_term_error := false
189 for err in errors {
190 if err.msg.contains('terminator') && err.msg.contains('end') {
191 found_term_error = true
192 break
193 }
194 }
195 assert found_term_error, 'expected terminator not at end error'
196}
197
198fn test_verify_use_def_chain() {
199 mut m := ssa.Module.new('test')
200 i64_t := m.type_store.get_int(64)
201 func_id := m.new_function('test_fn', i64_t, [])
202 blk_id := m.add_block(func_id, 'entry')
203
204 // Create constants and use them
205 const1 := m.add_value_node(.constant, i64_t, 'const_1', 0)
206 const2 := m.add_value_node(.constant, i64_t, 'const_2', 0)
207
208 // Add instruction using both constants
209 result := m.add_instr(.add, blk_id, i64_t, [const1, const2])
210 m.add_instr(.ret, blk_id, i64_t, [result])
211
212 // Verify use-def chains are correct
213 errors := verify(m)
214 assert errors.len == 0, 'expected no errors for valid use-def chain, got: ${errors}'
215
216 // Check that uses were registered
217 assert result in m.values[const1].uses, 'const1 should have result in uses'
218 assert result in m.values[const2].uses, 'const2 should have result in uses'
219}
220