v / vlib / v2 / ssa / module_storage_test.v
595 lines · 517 sloc · 15.0 KB · ddb021b9866c3b4523b746fa2f4c16a594f8bd89
Raw
1module ssa
2
3import os
4import v2.ast
5import v2.parser
6import v2.pref
7import v2.token
8import v2.transformer
9import v2.types
10
11fn module_storage_ssa_tmp_dir(label string) string {
12 return os.join_path(os.temp_dir(), 'v2_module_storage_ssa_${label}_${os.getpid()}')
13}
14
15fn module_storage_ssa_builder_for_test_sources(label string, sources map[string]string) &Builder {
16 tmp_dir := module_storage_ssa_tmp_dir(label)
17 os.mkdir_all(tmp_dir) or { panic('cannot create ${tmp_dir}') }
18 defer {
19 os.rmdir_all(tmp_dir) or {}
20 }
21 mut paths := []string{}
22 for rel_path, code in sources {
23 path := os.join_path(tmp_dir, rel_path)
24 os.mkdir_all(os.dir(path)) or { panic('cannot create ${os.dir(path)}') }
25 os.write_file(path, code) or { panic('cannot write ${path}') }
26 paths << path
27 }
28 prefs := &pref.Preferences{
29 backend: .x64
30 arch: .x64
31 }
32 mut file_set := token.FileSet.new()
33 mut par := parser.Parser.new(prefs)
34 files := par.parse_files(paths, mut file_set)
35 env := types.Environment.new()
36 mut checker := types.Checker.new(prefs, file_set, env)
37 checker.check_files(files)
38 mut trans := transformer.Transformer.new_with_pref(env, prefs)
39 trans.set_file_set(file_set)
40 transformed_files := trans.transform_files(files)
41 mut mod := Module.new('module_storage_test')
42 mut builder := Builder.new_with_env(mod, env)
43 builder.build_all(transformed_files)
44 return builder
45}
46
47fn module_storage_ssa_for_test_sources(label string, sources map[string]string) &Module {
48 builder := module_storage_ssa_builder_for_test_sources(label, sources)
49 return builder.mod
50}
51
52fn module_storage_ssa_for_test_sources_flat(label string, sources map[string]string) &Module {
53 tmp_dir := module_storage_ssa_tmp_dir(label)
54 os.mkdir_all(tmp_dir) or { panic('cannot create ${tmp_dir}') }
55 defer {
56 os.rmdir_all(tmp_dir) or {}
57 }
58 mut paths := []string{}
59 for rel_path, code in sources {
60 path := os.join_path(tmp_dir, rel_path)
61 os.mkdir_all(os.dir(path)) or { panic('cannot create ${os.dir(path)}') }
62 os.write_file(path, code) or { panic('cannot write ${path}') }
63 paths << path
64 }
65 prefs := &pref.Preferences{
66 backend: .arm64
67 }
68 mut file_set := token.FileSet.new()
69 mut par := parser.Parser.new(prefs)
70 files := par.parse_files(paths, mut file_set)
71 env := types.Environment.new()
72 mut checker := types.Checker.new(prefs, file_set, env)
73 checker.check_files(files)
74 mut trans := transformer.Transformer.new_with_pref(env, prefs)
75 trans.set_file_set(file_set)
76 flat := ast.flatten_files(files)
77 transformed_flat, _ := trans.transform_files_to_flat(&flat, files)
78 mut mod := Module.new('module_storage_flat_test')
79 mut builder := Builder.new_with_env(mod, env)
80 builder.build_all_from_flat(&transformed_flat)
81 return mod
82}
83
84fn module_storage_ssa_global_names(m &Module) []string {
85 mut names := []string{}
86 for global in m.globals {
87 names << global.name
88 }
89 return names
90}
91
92fn module_storage_ssa_global_value_id(m &Module, name string) ?ValueID {
93 for value in m.values {
94 if value.kind == .global && value.name == name {
95 return value.id
96 }
97 }
98 return none
99}
100
101fn module_storage_ssa_has_store_to_global(m &Module, name string) bool {
102 global_id := module_storage_ssa_global_value_id(m, name) or { return false }
103 for instr in m.instrs {
104 if instr.op == .store && instr.operands.len >= 2 && instr.operands[1] == global_id {
105 return true
106 }
107 }
108 return false
109}
110
111fn module_storage_ssa_func(m &Module, name string) ?Function {
112 for func in m.funcs {
113 if func.name == name {
114 return func
115 }
116 }
117 return none
118}
119
120fn module_storage_ssa_return_value_id(m &Module, func Function) ?ValueID {
121 for blk_id in func.blocks {
122 for val_id in m.blocks[blk_id].instrs {
123 instr := m.instrs[m.values[val_id].index]
124 if instr.op == .ret && instr.operands.len > 0 {
125 return instr.operands[0]
126 }
127 }
128 }
129 return none
130}
131
132fn module_storage_ssa_return_value_ids(m &Module, func Function) []ValueID {
133 mut ret_vals := []ValueID{}
134 for blk_id in func.blocks {
135 for val_id in m.blocks[blk_id].instrs {
136 instr := m.instrs[m.values[val_id].index]
137 if instr.op == .ret && instr.operands.len > 0 {
138 ret_vals << instr.operands[0]
139 }
140 }
141 }
142 return ret_vals
143}
144
145fn module_storage_ssa_call_callees(m &Module, func Function) []string {
146 mut callees := []string{}
147 for blk_id in func.blocks {
148 for val_id in m.blocks[blk_id].instrs {
149 value := m.values[val_id]
150 if value.kind != .instruction {
151 continue
152 }
153 instr := m.instrs[value.index]
154 if instr.op != .call || instr.operands.len == 0 {
155 continue
156 }
157 callee_id := instr.operands[0]
158 if callee_id <= 0 || callee_id >= m.values.len {
159 continue
160 }
161 callee := m.values[callee_id]
162 if callee.kind == .func_ref {
163 callees << callee.name
164 }
165 }
166 }
167 return callees
168}
169
170fn test_module_storage_import_alias_resolves_to_declaring_module_in_ssa() {
171 m := module_storage_ssa_for_test_sources('alias', {
172 'report/report.v': 'module report
173
174pub __global mut errors = 0
175'
176 'main.v': 'module main
177
178import report as r
179
180fn main() {
181 r.errors += 1
182}
183'
184 })
185 names := module_storage_ssa_global_names(m)
186 assert 'report__errors' in names
187 assert 'r__errors' !in names
188 assert module_storage_ssa_has_store_to_global(m, 'report__errors')
189}
190
191fn module_storage_selective_import_nested_sources() map[string]string {
192 return {
193 'main.v': 'module main
194
195import mymodules.submodule { sub_xy }
196
197fn main() {
198 value := sub_xy(10, 7)
199 _ = value
200}
201'
202 'mymodules/submodule/sub_functions.v': 'module submodule
203
204pub fn sub_xy(x int, y int) int {
205 return x - y
206}
207'
208 }
209}
210
211fn module_storage_assert_nested_selective_import_callee(m &Module, label string) {
212 main_func := module_storage_ssa_func(m, 'main') or { panic('${label}: missing main') }
213 callees := module_storage_ssa_call_callees(m, main_func)
214 assert 'submodule__sub_xy' in callees, '${label}: missing submodule__sub_xy in ${callees}'
215 assert 'sub_xy' !in callees, '${label}: bare sub_xy callee leaked into ${callees}'
216 assert 'mymodules_submodule__sub_xy' !in callees, '${label}: import path callee leaked into ${callees}'
217}
218
219fn test_module_storage_legacy_selective_import_nested_module_uses_declared_leaf_module() {
220 m := module_storage_ssa_for_test_sources('selective_nested_legacy',
221 module_storage_selective_import_nested_sources())
222 module_storage_assert_nested_selective_import_callee(m, 'legacy')
223}
224
225fn test_module_storage_flat_selective_import_nested_module_uses_declared_leaf_module() {
226 m := module_storage_ssa_for_test_sources_flat('selective_nested_flat',
227 module_storage_selective_import_nested_sources())
228 module_storage_assert_nested_selective_import_callee(m, 'flat')
229}
230
231fn module_storage_direct_import_nested_sources() map[string]string {
232 return {
233 'main.v': 'module main
234
235import mymodules.submodule
236
237fn main() {
238 value := submodule.sub_xy(10, 7)
239 _ = value
240}
241'
242 'mymodules/submodule/sub_functions.v': 'module submodule
243
244pub fn sub_xy(x int, y int) int {
245 return x - y
246}
247'
248 }
249}
250
251fn test_module_storage_legacy_direct_import_nested_module_selector_uses_declared_leaf_module() {
252 m := module_storage_ssa_for_test_sources('direct_nested_legacy',
253 module_storage_direct_import_nested_sources())
254 module_storage_assert_nested_selective_import_callee(m, 'legacy direct')
255}
256
257fn test_module_storage_flat_direct_import_nested_module_selector_uses_declared_leaf_module() {
258 m := module_storage_ssa_for_test_sources_flat('direct_nested_flat',
259 module_storage_direct_import_nested_sources())
260 module_storage_assert_nested_selective_import_callee(m, 'flat direct')
261}
262
263fn test_module_storage_legacy_direct_import_module_selector_one_arg_stays_call() {
264 m := module_storage_ssa_for_test_sources('module_selector_one_arg_call', {
265 'main.v': 'module main
266
267import left as l
268import right
269import helper
270
271fn main() {
272 first := l.state == 10 && right.state == 200 && l.score() == 1001 && right.score() == 20005
273 direct := l.bump(3)
274 transitive := helper.bump_right(5)
275 second := direct == 132 && transitive == 2057 && l.state == 13 && right.state == 205
276 _ = first
277 _ = second
278}
279'
280 'left/left.v': 'module left
281
282pub __global mut state = 10
283__global mut private_hits = 1
284
285pub fn bump(delta int) int {
286 state += delta
287 private_hits += 1
288 return state * 10 + private_hits
289}
290
291pub fn score() int {
292 return state * 100 + private_hits
293}
294'
295 'right/right.v': 'module right
296
297pub __global mut state = 200
298__global mut private_hits = 5
299
300pub fn bump(delta int) int {
301 state += delta
302 private_hits += 2
303 return state * 10 + private_hits
304}
305
306pub fn score() int {
307 return state * 100 + private_hits
308}
309'
310 'helper/helper.v': 'module helper
311
312import right as r
313
314pub fn bump_right(delta int) int {
315 return r.bump(delta)
316}
317
318pub fn right_score() int {
319 return r.score()
320}
321'
322 })
323 main_func := module_storage_ssa_func(m, 'main') or { panic('missing main') }
324 callees := module_storage_ssa_call_callees(m, main_func)
325 assert 'helper__bump_right' in callees
326}
327
328fn test_module_storage_legacy_call_or_cast_module_selector_prefers_registered_call() {
329 mut builder := module_storage_ssa_builder_for_test_sources('module_selector_call_or_cast_decision', {
330 'helper/helper.v': 'module helper
331
332pub fn bump_right(delta int) int {
333 return delta + 1
334}
335'
336 'main.v': 'module main
337
338import helper
339
340fn main() {}
341'
342 })
343 sel := ast.SelectorExpr{
344 lhs: ast.Expr(ast.Ident{
345 name: 'helper'
346 })
347 rhs: ast.Ident{
348 name: 'bump_right'
349 }
350 }
351 assert !builder.call_or_cast_selector_should_remain_cast(sel, 'helper__bump_right')
352}
353
354fn test_module_qualified_alias_array_field_index_has_alias_element_type() {
355 m := module_storage_ssa_for_test_sources('selector_alias_array_elem', {
356 'v2/ssa/value.v': 'module ssa
357
358pub type ValueID = int
359'
360 'main.v': 'module main
361
362import v2.ssa
363
364struct Holder {
365 items []ssa.ValueID
366}
367
368fn get_item(h Holder, i int) ssa.ValueID {
369 return h.items[i]
370}
371'
372 })
373 get_item := module_storage_ssa_func(m, 'main__get_item') or {
374 module_storage_ssa_func(m, 'get_item') or {
375 module_storage_ssa_func(m, 'main.get_item') or { panic('missing get_item') }
376 }
377 }
378 mut ret_val := ValueID(0)
379 for blk_id in get_item.blocks {
380 for val_id in m.blocks[blk_id].instrs {
381 instr := m.instrs[m.values[val_id].index]
382 if instr.op == .ret && instr.operands.len > 0 {
383 ret_val = instr.operands[0]
384 }
385 }
386 }
387 assert ret_val != 0
388 ret_typ := m.type_store.types[m.values[ret_val].typ]
389 assert ret_typ.kind == .int_t
390 assert ret_typ.width == 32
391}
392
393fn test_map_array_alias_value_index_has_alias_element_type() {
394 m := module_storage_ssa_for_test_sources('map_array_alias_value_index', {
395 'main.v': 'module main
396
397type TypeID = int
398
399fn read_elem(values map[string][]TypeID, key string, idx int) TypeID {
400 param_elem_types := values[key] or { [TypeID(0)] }
401 return param_elem_types[idx]
402}
403'
404 })
405 read_elem := module_storage_ssa_func(m, 'main__read_elem') or {
406 module_storage_ssa_func(m, 'read_elem') or { panic('missing read_elem') }
407 }
408 ret_val := module_storage_ssa_return_value_id(m, read_elem) or {
409 panic('missing read_elem return value')
410 }
411 ret_typ := m.type_store.types[m.values[ret_val].typ]
412 assert ret_typ.kind == .int_t
413 assert ret_typ.width == 32
414}
415
416fn test_method_map_array_alias_value_index_has_alias_element_type() {
417 m := module_storage_ssa_for_test_sources('method_map_array_alias_value_index', {
418 'main.v': 'module main
419
420type TypeID = int
421
422struct Builder {
423mut:
424 fn_param_array_elem_types map[string][]TypeID
425}
426
427fn (mut b Builder) call_param_array_elem_type(fn_name string, param_idx int) TypeID {
428 param_elem_types := b.fn_param_array_elem_types[fn_name] or { return TypeID(-1) }
429 if param_idx >= param_elem_types.len {
430 return TypeID(-2)
431 }
432 elem_type := param_elem_types[param_idx]
433 if elem_type == 0 {
434 return TypeID(-3)
435 }
436 return elem_type
437}
438'
439 })
440 func := module_storage_ssa_func(m, 'Builder__call_param_array_elem_type') or {
441 panic('missing method function')
442 }
443 ret_vals := module_storage_ssa_return_value_ids(m, func)
444 assert ret_vals.len > 0
445 for ret_val in ret_vals {
446 ret_typ := m.type_store.types[m.values[ret_val].typ]
447 assert ret_typ.kind == .int_t
448 assert ret_typ.width == 32
449 }
450}
451
452fn test_flat_method_map_array_alias_value_index_has_alias_element_type() {
453 m := module_storage_ssa_for_test_sources_flat('flat_method_map_array_alias_value_index', {
454 'main.v': 'module main
455
456type TypeID = int
457
458struct Builder {
459mut:
460 fn_param_array_elem_types map[string][]TypeID
461}
462
463fn (mut b Builder) call_param_array_elem_type(fn_name string, param_idx int) TypeID {
464 param_elem_types := b.fn_param_array_elem_types[fn_name] or { return TypeID(-1) }
465 if param_idx >= param_elem_types.len {
466 return TypeID(-2)
467 }
468 elem_type := param_elem_types[param_idx]
469 if elem_type == 0 {
470 return TypeID(-3)
471 }
472 return elem_type
473}
474'
475 })
476 func := module_storage_ssa_func(m, 'Builder__call_param_array_elem_type') or {
477 panic('missing method function')
478 }
479 ret_vals := module_storage_ssa_return_value_ids(m, func)
480 assert ret_vals.len > 0
481 for ret_val in ret_vals {
482 ret_typ := m.type_store.types[m.values[ret_val].typ]
483 assert ret_typ.kind == .int_t
484 assert ret_typ.width == 32
485 }
486}
487
488fn test_module_storage_same_short_names_stay_distinct_in_ssa() {
489 m := module_storage_ssa_for_test_sources('collisions', {
490 'a/a.v': 'module a
491
492pub __global state = 1
493'
494 'b/b.v': 'module b
495
496pub __global state = 2
497'
498 'main.v': 'module main
499
500import a
501import b
502
503fn main() {
504 _ = a.state + b.state
505}
506'
507 })
508 names := module_storage_ssa_global_names(m)
509 assert 'a__state' in names
510 assert 'b__state' in names
511 assert 'state' !in names
512 a_id := module_storage_ssa_global_value_id(m, 'a__state') or {
513 panic('missing a__state global')
514 }
515 b_id := module_storage_ssa_global_value_id(m, 'b__state') or {
516 panic('missing b__state global')
517 }
518 assert a_id != b_id
519}
520
521fn test_module_storage_bare_dunder_name_resolves_to_current_module_in_ssa() {
522 m := module_storage_ssa_for_test_sources('bare_dunder', {
523 'a/a.v': 'module a
524
525pub __global mut state__x = 1
526
527pub fn bump() int {
528 state__x += 1
529 return state__x
530}
531'
532 'b/b.v': 'module b
533
534pub __global mut state__x = 2
535
536pub fn bump() int {
537 state__x += 1
538 return state__x
539}
540'
541 'main.v': 'module main
542
543import a
544import b
545
546fn main() {
547 _ = a.bump() + b.bump()
548 _ = a.state__x + b.state__x
549}
550'
551 })
552 names := module_storage_ssa_global_names(m)
553 assert 'a__state__x' in names
554 assert 'b__state__x' in names
555 assert 'state__x' !in names
556 assert module_storage_ssa_has_store_to_global(m, 'a__state__x')
557 assert module_storage_ssa_has_store_to_global(m, 'b__state__x')
558 a_id := module_storage_ssa_global_value_id(m, 'a__state__x') or {
559 panic('missing a__state__x global')
560 }
561 b_id := module_storage_ssa_global_value_id(m, 'b__state__x') or {
562 panic('missing b__state__x global')
563 }
564 assert a_id != b_id
565}
566
567fn test_module_storage_c_extern_raw_global_uses_c_symbol_in_ssa() {
568 m := module_storage_ssa_for_test_sources('c_extern_raw', {
569 'ext/ext.v': 'module ext
570
571@[c_extern]
572pub __global raw_status int
573
574pub fn read_status() int {
575 return raw_status
576}
577'
578 'main.v': 'module main
579
580import ext
581
582fn main() {
583 _ = ext.raw_status
584 _ = ext.read_status()
585}
586'
587 })
588 names := module_storage_ssa_global_names(m)
589 assert 'raw_status' in names
590 assert 'ext__raw_status' !in names
591 _ := module_storage_ssa_global_value_id(m, 'raw_status') or {
592 panic('missing raw_status C extern global')
593 }
594 assert module_storage_ssa_global_value_id(m, 'ext__raw_status') == none
595}
596