v4 / vlib / v3 / tests / ssa_builder_parity_test.v
384 lines · 348 sloc · 8.79 KB · 6c4d26f1f98c5464b012a375667ab345e462f7e0
Raw
1import os
2import v3.flat
3import v3.parser
4import v3.pref
5import v3.ssa
6import v3.types
7
8// parse_checked_source reads parse checked source input for v3 tests.
9fn parse_checked_source(name string, source string) (&flat.FlatAst, &types.TypeChecker) {
10 src := os.join_path(os.temp_dir(), 'v3_ssa_builder_${name}.v')
11 os.write_file(src, source) or { panic(err) }
12 prefs := pref.new_preferences()
13 mut p := parser.Parser.new(prefs)
14 mut a := p.parse_file(src)
15 mut tc := types.TypeChecker.new(a)
16 tc.collect(a)
17 tc.annotate_types()
18 assert tc.errors.len == 0
19 return a, &tc
20}
21
22// build_source builds source data for v3 tests.
23fn build_source(name string, source string) &ssa.Module {
24 return build_source_with_used(name, source, map[string]bool{})
25}
26
27// build_source_with_used builds source with used data for v3 tests.
28fn build_source_with_used(name string, source string, used_fns map[string]bool) &ssa.Module {
29 a, tc := parse_checked_source(name, source)
30 return ssa.build_with_used(a, used_fns, tc)
31}
32
33// find_func resolves find func information for v3 tests.
34fn find_func(m &ssa.Module, name string) ssa.Function {
35 for f in m.funcs {
36 if f.name == name {
37 return f
38 }
39 }
40 assert false
41 return ssa.Function{}
42}
43
44// func_instrs supports func instrs handling for v3 tests.
45fn func_instrs(m &ssa.Module, name string) []ssa.Instruction {
46 f := find_func(m, name)
47 mut instrs := []ssa.Instruction{}
48 for block_id in f.blocks {
49 for val_id in m.blocks[block_id].instrs {
50 val := m.values[val_id]
51 if val.kind == .instruction {
52 instrs << m.instrs[val.index]
53 }
54 }
55 }
56 return instrs
57}
58
59// ret_operand supports ret operand handling for v3 tests.
60fn ret_operand(m &ssa.Module, name string) ssa.ValueID {
61 for instr in func_instrs(m, name) {
62 if instr.op == .ret && instr.operands.len == 1 {
63 return instr.operands[0]
64 }
65 }
66 assert false
67 return ssa.ValueID(0)
68}
69
70// has_call_to reports whether has call to applies in v3 tests.
71fn has_call_to(m &ssa.Module, fn_name string, callee string) bool {
72 for instr in func_instrs(m, fn_name) {
73 if instr.op != .call || instr.operands.len == 0 {
74 continue
75 }
76 fn_ref := m.values[instr.operands[0]]
77 if fn_ref.kind == .func_ref && fn_ref.name == callee {
78 return true
79 }
80 }
81 return false
82}
83
84// has_instr_op reports whether has instr op applies in v3 tests.
85fn has_instr_op(m &ssa.Module, fn_name string, op ssa.OpCode) bool {
86 for instr in func_instrs(m, fn_name) {
87 if instr.op == op {
88 return true
89 }
90 }
91 return false
92}
93
94// has_alloca_len_const reports whether has alloca len const applies in v3 tests.
95fn has_alloca_len_const(m &ssa.Module, fn_name string, len string) bool {
96 for instr in func_instrs(m, fn_name) {
97 if instr.op != .alloca || instr.operands.len == 0 {
98 continue
99 }
100 len_value := m.values[instr.operands[0]]
101 if len_value.kind == .constant && len_value.name == len {
102 return true
103 }
104 }
105 return false
106}
107
108// test_if_expression_builds_phi validates if expression builds phi behavior in v3 tests.
109fn test_if_expression_builds_phi() {
110 m := build_source('if_phi', '
111fn pick(flag bool) int {
112 return if flag {
113 1
114 } else {
115 2
116 }
117}
118')
119 mut found_phi := false
120 for instr in func_instrs(m, 'pick') {
121 if instr.op == .phi {
122 found_phi = true
123 assert instr.operands.len == 4
124 }
125 }
126 assert found_phi
127}
128
129// test_match_expression_builds_phi validates match expression builds phi behavior in v3 tests.
130fn test_match_expression_builds_phi() {
131 m := build_source('match_phi', '
132fn pick(x int) int {
133 return match x {
134 1 {
135 10
136 }
137 2, 3 {
138 20
139 }
140 else {
141 30
142 }
143 }
144}
145')
146 mut found_phi := false
147 for instr in func_instrs(m, 'pick') {
148 if instr.op == .phi {
149 found_phi = true
150 assert instr.operands.len == 6
151 }
152 }
153 assert found_phi
154}
155
156// test_string_infix_lowers_to_runtime_calls validates this v3 regression case.
157fn test_string_infix_lowers_to_runtime_calls() {
158 m := build_source('string_infix', '
159fn join(a string, b string) string {
160 return a + b
161}
162
163fn same(a string, b string) bool {
164 return a == b
165}
166')
167 assert has_call_to(m, 'join', 'string__plus')
168 assert has_call_to(m, 'same', 'string__eq')
169}
170
171// test_c_fn_decl_registers_extern_signature validates this v3 regression case.
172fn test_c_fn_decl_registers_extern_signature() {
173 m := build_source('c_fn_decl', '
174fn C.abs(x int) int
175
176fn main() {
177 _ := C.abs(-3)
178}
179')
180 f := find_func(m, 'C.abs')
181 assert f.is_c_extern
182 ret_type := m.type_store.types[f.typ]
183 assert ret_type.kind == .int_t
184 assert ret_type.width == 32
185}
186
187// test_function_parameter_call_lowers_to_call_indirect validates this v3 regression case.
188fn test_function_parameter_call_lowers_to_call_indirect() {
189 m := build_source('call_indirect', '
190fn add(a int, b int) int {
191 return a + b
192}
193
194fn apply(f fn (int, int) int, x int, y int) int {
195 return f(x, y)
196}
197')
198 mut found_indirect := false
199 for instr in func_instrs(m, 'apply') {
200 if instr.op == .call_indirect {
201 found_indirect = true
202 assert instr.operands.len == 3
203 }
204 }
205 assert found_indirect
206}
207
208// test_label_and_goto_lower_to_jump_blocks validates this v3 regression case.
209fn test_label_and_goto_lower_to_jump_blocks() {
210 m := build_source('label_goto', '
211fn jumpy() int {
212 mut x := 0
213 goto done
214 x = 1
215done:
216 return x
217}
218')
219 mut found_jump := false
220 for instr in func_instrs(m, 'jumpy') {
221 if instr.op == .jmp {
222 found_jump = true
223 }
224 }
225 assert found_jump
226}
227
228// test_const_identifier_lowers_to_const_value validates this v3 regression case.
229fn test_const_identifier_lowers_to_const_value() {
230 m := build_source('const_ident', '
231const answer = 42
232
233fn get_answer() int {
234 return answer
235}
236')
237 ret := m.values[ret_operand(m, 'get_answer')]
238 assert ret.kind == .constant
239 assert ret.name == '42'
240}
241
242// test_enum_selector_lowers_to_const_value validates this v3 regression case.
243fn test_enum_selector_lowers_to_const_value() {
244 m := build_source('enum_selector', '
245enum Color {
246 red
247 green = 5
248 blue
249}
250
251fn get_blue() int {
252 return int(Color.blue)
253}
254')
255 ret_id := ret_operand(m, 'get_blue')
256 ret := m.values[ret_id]
257 if ret.kind == .constant {
258 assert ret.name == '6'
259 } else {
260 assert ret.kind == .instruction
261 instr := m.instrs[ret.index]
262 assert instr.operands.len > 0
263 cast_input := m.values[instr.operands[0]]
264 assert cast_input.kind == .constant
265 assert cast_input.name == '6'
266 }
267}
268
269// test_float_literal_uses_float_type validates this v3 regression case.
270fn test_float_literal_uses_float_type() {
271 m := build_source('float_literal', '
272fn get_float() f64 {
273 return 1.25
274}
275')
276 ret := m.values[ret_operand(m, 'get_float')]
277 typ := m.type_store.types[ret.typ]
278 assert typ.kind == .float_t
279 assert typ.width == 64
280}
281
282// test_integer_casts_emit_conversion_ops validates this v3 regression case.
283fn test_integer_casts_emit_conversion_ops() {
284 m := build_source('int_casts', '
285fn narrow(x i64) u8 {
286 return u8(x)
287}
288
289fn widen(x u8) i64 {
290 return i64(x)
291}
292
293fn widen_signed(x int) i64 {
294 return i64(x)
295}
296')
297 mut found_trunc := false
298 for instr in func_instrs(m, 'narrow') {
299 if instr.op == .trunc {
300 found_trunc = true
301 assert m.type_store.types[instr.typ].width == 8
302 }
303 }
304 assert found_trunc
305
306 mut found_zext := false
307 for instr in func_instrs(m, 'widen') {
308 if instr.op == .zext {
309 found_zext = true
310 assert m.type_store.types[instr.typ].width == 64
311 }
312 }
313 assert found_zext
314 assert has_instr_op(m, 'widen_signed', .sext)
315}
316
317// test_unsigned_integer_infix_uses_unsigned_ops validates this v3 regression case.
318fn test_unsigned_integer_infix_uses_unsigned_ops() {
319 m := build_source('unsigned_ops', '
320fn div_u(x u32, y u32) u32 {
321 return x / y
322}
323
324fn rem_u(x u32, y u32) u32 {
325 return x % y
326}
327
328fn cmp_u(x u32, y u32) bool {
329 return x < y
330}
331
332fn cmp_s(x int, y int) bool {
333 return x < y
334}
335')
336 assert has_instr_op(m, 'div_u', .udiv)
337 assert has_instr_op(m, 'rem_u', .urem)
338 assert has_instr_op(m, 'cmp_u', .ult)
339 assert has_instr_op(m, 'cmp_s', .lt)
340 assert !has_instr_op(m, 'cmp_s', .ult)
341}
342
343// test_fixed_array_const_length_is_resolved validates this v3 regression case.
344fn test_fixed_array_const_length_is_resolved() {
345 mut used_fns := map[string]bool{}
346 used_fns['make_array'] = true
347 used_fns['main.make_array'] = true
348 m := build_source_with_used('fixed_array_const_len', '
349const fixed_len = 4
350
351fn make_array() int {
352 mut values := [fixed_len]int{}
353 values[0] = 7
354 return values[0]
355}
356',
357 used_fns)
358 assert has_alloca_len_const(m, 'make_array', '4')
359}
360
361// test_string_range_does_not_lower_to_array_slice validates this v3 regression case.
362fn test_string_range_does_not_lower_to_array_slice() {
363 m := build_source('string_range', '
364fn cut(s string) string {
365 return s[1..3]
366}
367')
368 assert !has_call_to(m, 'cut', 'array_slice')
369}
370
371// test_array_first_last_load_elements validates this v3 regression case.
372fn test_array_first_last_load_elements() {
373 m := build_source('array_first_last', '
374fn first_value(values []int) int {
375 return values.first()
376}
377
378fn last_value(values []int) int {
379 return values.last()
380}
381')
382 assert has_call_to(m, 'first_value', 'array_get')
383 assert has_call_to(m, 'last_value', 'array_get')
384}
385