v / vlib / v2 / ssa / mut_array_param_append_test.v
177 lines · 159 sloc · 5.19 KB · ddb021b9866c3b4523b746fa2f4c16a594f8bd89
Raw
1module ssa
2
3import os
4import v2.ast
5import v2.parser
6import v2.pref as vpref
7import v2.token
8import v2.transformer
9import v2.types
10
11fn mut_array_param_append_source() string {
12 return r'
13module main
14
15fn add_seen(mut seen []string, name string) {
16 seen << name
17}
18
19fn push_path(mut patterns [][]string, path []string) {
20 patterns << path
21}
22
23fn push_many_paths(mut patterns [][]string, paths [][]string) {
24 patterns << paths
25}
26
27fn push_literal_path(mut patterns [][]string, path []string) {
28 patterns << [path]
29}
30
31fn main() {
32 mut seen := []string{}
33 add_seen(mut seen, "A")
34 path := ["A", "B"]
35 mut patterns := [][]string{}
36 push_path(mut patterns, path)
37 push_many_paths(mut patterns, [path])
38 push_literal_path(mut patterns, path)
39}
40'
41}
42
43fn mut_array_param_append_parse(source string) ([]ast.File, &types.Environment, &token.FileSet) {
44 tmp_file := os.join_path(os.vtmp_dir(), 'v2_ssa_mut_array_param_append_${os.getpid()}.v')
45 os.write_file(tmp_file, source) or { panic('failed to write temp file') }
46 defer {
47 os.rm(tmp_file) or {}
48 }
49 prefs := &vpref.Preferences{
50 backend: .x64
51 arch: .x64
52 no_parallel: true
53 }
54 mut file_set := token.FileSet.new()
55 mut par := parser.Parser.new(prefs)
56 files := par.parse_files([tmp_file], mut file_set)
57 env := types.Environment.new()
58 mut checker := types.Checker.new(prefs, file_set, env)
59 checker.check_files(files)
60 return files, env, file_set
61}
62
63fn mut_array_param_append_build_legacy(source string) &Module {
64 files, env, file_set := mut_array_param_append_parse(source)
65 prefs := &vpref.Preferences{
66 backend: .x64
67 arch: .x64
68 no_parallel: true
69 }
70 mut trans := transformer.Transformer.new_with_pref(env, prefs)
71 trans.set_file_set(file_set)
72 transformed := trans.transform_files(files)
73 mut mod := Module.new('mut_array_param_append_legacy')
74 mut b := Builder.new_with_env(mod, env)
75 b.build_all(transformed)
76 return mod
77}
78
79fn mut_array_param_append_build_flat(source string) &Module {
80 files, env, file_set := mut_array_param_append_parse(source)
81 prefs := &vpref.Preferences{
82 backend: .x64
83 arch: .x64
84 no_parallel: true
85 }
86 mut trans := transformer.Transformer.new_with_pref(env, prefs)
87 trans.set_file_set(file_set)
88 transformed := trans.transform_files(files)
89 flat := ast.flatten_files(transformed)
90 mut mod := Module.new('mut_array_param_append_flat')
91 mut b := Builder.new_with_env(mod, env)
92 b.build_all_from_flat(&flat)
93 return mod
94}
95
96fn mut_array_param_append_func_index(m &Module, fn_name string) int {
97 for i, func in m.funcs {
98 if func.name == fn_name {
99 return i
100 }
101 }
102 assert false, 'missing SSA function ${fn_name}'
103 return 0
104}
105
106fn mut_array_param_append_calls(m &Module, fn_name string, callee_name string) [][]ValueID {
107 func_idx := mut_array_param_append_func_index(m, fn_name)
108 mut calls := [][]ValueID{}
109 for block_id in m.funcs[func_idx].blocks {
110 for value_id in m.blocks[block_id].instrs {
111 value := m.values[value_id]
112 if value.kind != .instruction {
113 continue
114 }
115 instr := m.instrs[value.index]
116 if instr.op != .call || instr.operands.len == 0 {
117 continue
118 }
119 callee_id := instr.operands[0]
120 if callee_id > 0 && callee_id < m.values.len && m.values[callee_id].name == callee_name {
121 calls << instr.operands
122 }
123 }
124 }
125 return calls
126}
127
128fn mut_array_param_append_assert_arg_uses_local_storage(m &Module, fn_name string, arg_id ValueID) {
129 assert arg_id > 0 && arg_id < m.values.len, '${fn_name} append argument is out of range'
130 mut cur_id := arg_id
131 for _ in 0 .. 6 {
132 assert cur_id > 0 && cur_id < m.values.len
133 value := m.values[cur_id]
134 assert value.kind == .instruction
135 instr := m.instrs[value.index]
136 if instr.op == .alloca {
137 return
138 }
139 assert instr.op in [.bitcast, .load]
140 assert instr.operands.len == 1
141 cur_id = instr.operands[0]
142 }
143 assert false, '${fn_name} append argument does not resolve to local storage'
144}
145
146fn mut_array_param_append_assert_module(m &Module) {
147 add_push_calls := mut_array_param_append_calls(m, 'add_seen', 'builtin__array_push_noscan')
148 assert add_push_calls.len == 1
149 assert add_push_calls[0].len >= 2
150 mut_array_param_append_assert_arg_uses_local_storage(m, 'add_seen', add_push_calls[0][1])
151
152 path_push_calls := mut_array_param_append_calls(m, 'push_path', 'builtin__array_push_noscan')
153 assert path_push_calls.len == 1
154 assert path_push_calls[0].len >= 2
155 mut_array_param_append_assert_arg_uses_local_storage(m, 'push_path', path_push_calls[0][1])
156
157 many_push_calls := mut_array_param_append_calls(m, 'push_many_paths', 'array__push_many')
158 assert many_push_calls.len == 1
159 assert many_push_calls[0].len >= 2
160 mut_array_param_append_assert_arg_uses_local_storage(m, 'push_many_paths',
161 many_push_calls[0][1])
162
163 literal_push_calls := mut_array_param_append_calls(m, 'push_literal_path',
164 'builtin__array_push_noscan')
165 assert literal_push_calls.len == 1
166 assert literal_push_calls[0].len >= 2
167 mut_array_param_append_assert_arg_uses_local_storage(m, 'push_literal_path',
168 literal_push_calls[0][1])
169}
170
171fn test_mut_array_param_append_uses_local_storage_legacy_and_flat() {
172 source := mut_array_param_append_source()
173 legacy := mut_array_param_append_build_legacy(source)
174 mut_array_param_append_assert_module(legacy)
175 flat := mut_array_param_append_build_flat(source)
176 mut_array_param_append_assert_module(flat)
177}
178