v / vlib / v2 / ssa / dynamic_array_literal_storage_test.v
345 lines · 318 sloc · 8.98 KB · e78b7311ad580c800e5a3a0bd0afe3876e609685
Raw
1module ssa
2
3import v2.ast
4import v2.token
5import v2.types
6
7fn dal_ident(name string) ast.Expr {
8 return ast.Expr(ast.Ident{
9 name: name
10 })
11}
12
13fn dal_num(value string) ast.Expr {
14 return ast.Expr(ast.BasicLiteral{
15 kind: .number
16 value: value
17 })
18}
19
20fn dal_array_type(elem ast.Expr) ast.Expr {
21 return ast.Expr(ast.Type(ast.ArrayType{
22 elem_type: elem
23 }))
24}
25
26fn dal_sizeof(expr ast.Expr) ast.Expr {
27 return ast.Expr(ast.KeywordOperator{
28 op: token.Token.key_sizeof
29 exprs: [expr]
30 })
31}
32
33fn dal_field(name string, typ string) ast.FieldDecl {
34 return ast.FieldDecl{
35 name: name
36 typ: dal_ident(typ)
37 }
38}
39
40fn dal_param(name string, typ ast.Expr) ast.Parameter {
41 return ast.Parameter{
42 name: name
43 typ: typ
44 }
45}
46
47fn dal_builtin_file() ast.File {
48 return ast.File{
49 name: 'builtin.v'
50 mod: 'builtin'
51 stmts: [
52 ast.Stmt(ast.ModuleStmt{
53 name: 'builtin'
54 }),
55 ast.Stmt(ast.StructDecl{
56 name: 'array'
57 fields: [
58 dal_field('data', 'voidptr'),
59 dal_field('offset', 'int'),
60 dal_field('len', 'int'),
61 dal_field('cap', 'int'),
62 dal_field('flags', 'int'),
63 dal_field('element_size', 'int'),
64 ]
65 }),
66 ast.Stmt(ast.FnDecl{
67 name: 'new_array_from_c_array_noscan'
68 typ: ast.FnType{
69 params: [
70 dal_param('len', dal_ident('int')),
71 dal_param('cap', dal_ident('int')),
72 dal_param('elem_size', dal_ident('int')),
73 dal_param('data', dal_ident('voidptr')),
74 ]
75 return_type: dal_ident('array')
76 }
77 }),
78 ]
79 }
80}
81
82fn dal_dynamic_literal_files() []ast.File {
83 return [
84 dal_builtin_file(),
85 ast.File{
86 name: 'main.v'
87 mod: 'main'
88 stmts: [
89 ast.Stmt(ast.ModuleStmt{
90 name: 'main'
91 }),
92 ast.Stmt(ast.FnDecl{
93 name: 'make_arr'
94 typ: ast.FnType{
95 return_type: dal_array_type(dal_ident('int'))
96 }
97 stmts: [
98 ast.Stmt(ast.AssignStmt{
99 op: .decl_assign
100 lhs: [dal_ident('a')]
101 rhs: [
102 ast.Expr(ast.ArrayInitExpr{
103 exprs: [dal_num('1'), dal_num('2'),
104 dal_num('3')]
105 }),
106 ]
107 }),
108 ast.Stmt(ast.ReturnStmt{
109 exprs: [
110 dal_ident('a'),
111 ]
112 }),
113 ]
114 }),
115 ast.Stmt(ast.FnDecl{
116 name: 'make_arr_of_arrs'
117 typ: ast.FnType{
118 params: [
119 dal_param('a', dal_array_type(dal_ident('int'))),
120 dal_param('b', dal_array_type(dal_ident('int'))),
121 dal_param('c', dal_array_type(dal_ident('int'))),
122 ]
123 return_type: dal_array_type(dal_array_type(dal_ident('int')))
124 }
125 stmts: [
126 ast.Stmt(ast.ReturnStmt{
127 exprs: [
128 ast.Expr(ast.CallExpr{
129 lhs: dal_ident('new_array_from_c_array_noscan')
130 args: [dal_num('3'), dal_num('3'),
131 dal_sizeof(dal_array_type(dal_ident('int'))),
132 ast.Expr(ast.ArrayInitExpr{
133 exprs: [dal_ident('a'),
134 dal_ident('b'),
135 dal_ident('c')]
136 })]
137 }),
138 ]
139 }),
140 ]
141 }),
142 ]
143 },
144 ]
145}
146
147fn dal_build_legacy() &Builder {
148 files := dal_dynamic_literal_files()
149 env := types.Environment.new()
150 mut mod := Module.new('dynamic_array_literal_storage_legacy')
151 mut b := Builder.new_with_env(mod, env)
152 b.minimal_runtime_roots = true
153 b.build_all(files)
154 return b
155}
156
157fn dal_build_flat() &Builder {
158 files := dal_dynamic_literal_files()
159 flat := ast.flatten_files(files)
160 env := types.Environment.new()
161 mut mod := Module.new('dynamic_array_literal_storage_flat')
162 mut b := Builder.new_with_env(mod, env)
163 b.minimal_runtime_roots = true
164 b.build_all_from_flat(&flat)
165 return b
166}
167
168fn dal_func_value_ids(b &Builder, name string) []ValueID {
169 idx := b.fn_index[name] or {
170 assert false, 'missing SSA function ${name}'
171 return []ValueID{}
172 }
173 mut ids := []ValueID{}
174 for block_id in b.mod.funcs[idx].blocks {
175 for value_id in b.mod.blocks[block_id].instrs {
176 ids << value_id
177 }
178 }
179 return ids
180}
181
182fn dal_new_array_call_ids(b &Builder, fn_name string) []ValueID {
183 mut call_ids := []ValueID{}
184 for value_id in dal_func_value_ids(b, fn_name) {
185 value := b.mod.values[value_id]
186 if value.kind != .instruction {
187 continue
188 }
189 instr := b.mod.instrs[value.index]
190 if instr.op != .call || instr.operands.len == 0 {
191 continue
192 }
193 callee_id := instr.operands[0]
194 if callee_id > 0 && callee_id < b.mod.values.len
195 && b.mod.values[callee_id].name == 'builtin__new_array_from_c_array_noscan' {
196 call_ids << value_id
197 }
198 }
199 return call_ids
200}
201
202fn dal_new_array_call_in_fn(b &Builder, fn_name string) (ValueID, []ValueID) {
203 call_ids := dal_new_array_call_ids(b, fn_name)
204 assert call_ids.len == 1
205 call_id := call_ids[0]
206 instr := b.mod.instrs[b.mod.values[call_id].index]
207 return call_id, instr.operands
208}
209
210fn dal_new_array_call(b &Builder) (ValueID, []ValueID) {
211 return dal_new_array_call_in_fn(b, 'make_arr')
212}
213
214fn dal_fixed_array_alloca_type_id(b &Builder, value_id ValueID) TypeID {
215 if value_id <= 0 || value_id >= b.mod.values.len {
216 return 0
217 }
218 value := b.mod.values[value_id]
219 if value.kind != .instruction {
220 return 0
221 }
222 instr := b.mod.instrs[value.index]
223 if instr.op != .alloca {
224 return 0
225 }
226 typ := b.mod.type_store.types[value.typ]
227 if typ.kind != .ptr_t || typ.elem_type <= 0 || typ.elem_type >= b.mod.type_store.types.len {
228 return 0
229 }
230 fixed_type := typ.elem_type
231 if b.mod.type_store.types[fixed_type].kind != .array_t {
232 return 0
233 }
234 return fixed_type
235}
236
237fn dal_new_array_data_buffer_alloca(b &Builder, fn_name string) ValueID {
238 _, operands := dal_new_array_call_in_fn(b, fn_name)
239 assert operands.len == 5
240 data_arg := operands[4]
241 assert data_arg > 0 && data_arg < b.mod.values.len
242 data_value := b.mod.values[data_arg]
243 assert data_value.kind == .instruction
244 data_instr := b.mod.instrs[data_value.index]
245 if data_instr.op == .bitcast {
246 assert data_instr.operands.len == 1
247 return data_instr.operands[0]
248 }
249 return data_arg
250}
251
252fn dal_assert_single_new_array_call_in_fn(b &Builder, fn_name string) {
253 call_ids := dal_new_array_call_ids(b, fn_name)
254 assert call_ids.len == 1
255}
256
257fn dal_assert_array_of_arrays_literal_is_raw_element_buffer(b &Builder) {
258 array_type := b.struct_types['array'] or { TypeID(0) }
259 assert array_type != 0
260 dal_assert_single_new_array_call_in_fn(b, 'make_arr_of_arrs')
261 buffer_alloca := dal_new_array_data_buffer_alloca(b, 'make_arr_of_arrs')
262 fixed_type := dal_fixed_array_alloca_type_id(b, buffer_alloca)
263 assert fixed_type != 0
264 fixed_info := b.mod.type_store.types[fixed_type]
265 assert fixed_info.len == 3
266 assert fixed_info.elem_type == array_type
267 _, operands := dal_new_array_call_in_fn(b, 'make_arr_of_arrs')
268 assert b.mod.values[operands[1]].name == '3'
269 assert b.mod.values[operands[2]].name == '3'
270 assert b.mod.values[operands[3]].name == '32'
271}
272
273fn dal_is_fixed_array_alloca_ptr(b &Builder, value_id ValueID) bool {
274 return dal_fixed_array_alloca_type_id(b, value_id) != 0
275}
276
277fn dal_assert_no_nested_dynamic_array_for_arg(b &Builder) {
278 dal_assert_single_new_array_call_in_fn(b, 'make_arr_of_arrs')
279}
280
281fn dal_assert_dijkstra_array_literal_regression(b &Builder) {
282 dal_assert_array_of_arrays_literal_is_raw_element_buffer(b)
283 dal_assert_no_nested_dynamic_array_for_arg(b)
284}
285
286fn dal_assert_no_raw_fixed_array_local_store(b &Builder) {
287 for value_id in dal_func_value_ids(b, 'make_arr') {
288 value := b.mod.values[value_id]
289 if value.kind != .instruction {
290 continue
291 }
292 instr := b.mod.instrs[value.index]
293 if instr.op != .store || instr.operands.len < 2 {
294 continue
295 }
296 assert !dal_is_fixed_array_alloca_ptr(b, instr.operands[0])
297 }
298}
299
300fn dal_assert_dynamic_literal_storage_invariant(b &Builder) {
301 array_type := b.struct_types['array'] or { TypeID(0) }
302 assert array_type != 0
303 call_id, operands := dal_new_array_call(b)
304 assert b.mod.values[call_id].typ == array_type
305 assert operands.len == 5
306 assert b.mod.values[operands[1]].name == '3'
307 assert b.mod.values[operands[2]].name == '3'
308 assert b.mod.values[operands[3]].name == '4'
309
310 data_arg := operands[4]
311 assert data_arg > 0 && data_arg < b.mod.values.len
312 data_value := b.mod.values[data_arg]
313 assert data_value.kind == .instruction
314 data_instr := b.mod.instrs[data_value.index]
315 assert data_instr.op == .bitcast
316 assert data_instr.operands.len == 1
317 assert dal_is_fixed_array_alloca_ptr(b, data_instr.operands[0])
318
319 mut stored_call_to_array_local := false
320 for value_id in dal_func_value_ids(b, 'make_arr') {
321 value := b.mod.values[value_id]
322 if value.kind != .instruction {
323 continue
324 }
325 instr := b.mod.instrs[value.index]
326 if instr.op != .store || instr.operands.len < 2 || instr.operands[0] != call_id {
327 continue
328 }
329 dst_type := b.mod.type_store.types[b.mod.values[instr.operands[1]].typ]
330 assert dst_type.kind == .ptr_t
331 assert dst_type.elem_type == array_type
332 stored_call_to_array_local = true
333 }
334 assert stored_call_to_array_local
335 dal_assert_no_raw_fixed_array_local_store(b)
336}
337
338fn test_dynamic_array_literal_storage_invariant_matches_legacy_and_flat() {
339 legacy := dal_build_legacy()
340 dal_assert_dynamic_literal_storage_invariant(legacy)
341 dal_assert_dijkstra_array_literal_regression(legacy)
342 flat := dal_build_flat()
343 dal_assert_dynamic_literal_storage_invariant(flat)
344 dal_assert_dijkstra_array_literal_regression(flat)
345}
346