v / vlib / v2 / ssa / build_assign_from_flat_test.v
201 lines · 186 sloc · 5.27 KB · d358576851079d4716834cc86627307d28acd1ae
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//
6// Bit-equality pin for `build_assign_from_flat`. AssignStmt flat encoding
7// stores lhs/rhs expression edges split at `c.extra_int()` and the assignment
8// operator in `c.aux()`. The cursor port must match legacy assign lowering
9// without rehydrating lhs/rhs expression arrays.
10module ssa
11
12import v2.ast
13import v2.token
14import v2.types
15
16fn assign_pin_ident(name string) ast.Expr {
17 return ast.Expr(ast.Ident{
18 name: name
19 })
20}
21
22fn assign_pin_num(value string) ast.Expr {
23 return ast.Expr(ast.BasicLiteral{
24 kind: .number
25 value: value
26 })
27}
28
29fn assign_pin_stmt(lhs []ast.Expr, rhs []ast.Expr, op token.Token) ast.Stmt {
30 return ast.Stmt(ast.AssignStmt{
31 lhs: lhs
32 rhs: rhs
33 op: op
34 })
35}
36
37fn assign_pin_decl(name string, rhs ast.Expr) ast.Stmt {
38 return assign_pin_stmt([assign_pin_ident(name)], [rhs], .decl_assign)
39}
40
41fn assign_pin_selector(lhs ast.Expr, rhs string) ast.Expr {
42 return ast.Expr(ast.SelectorExpr{
43 lhs: lhs
44 rhs: ast.Ident{
45 name: rhs
46 }
47 })
48}
49
50fn assign_pin_index(lhs ast.Expr, idx ast.Expr) ast.Expr {
51 return ast.Expr(ast.IndexExpr{
52 lhs: lhs
53 expr: idx
54 })
55}
56
57fn assign_pin_prefix(op token.Token, expr ast.Expr) ast.Expr {
58 return ast.Expr(ast.PrefixExpr{
59 op: op
60 expr: expr
61 })
62}
63
64fn assign_pin_fixed_array() ast.Expr {
65 return ast.Expr(ast.ArrayInitExpr{
66 exprs: [assign_pin_num('1'), assign_pin_num('2')]
67 len: ast.Expr(ast.PostfixExpr{
68 op: .not
69 expr: ast.empty_expr
70 })
71 })
72}
73
74fn assign_pin_point_init() ast.Expr {
75 return ast.Expr(ast.InitExpr{
76 typ: assign_pin_ident('Point')
77 fields: [
78 ast.FieldInit{
79 name: 'x'
80 value: assign_pin_num('1')
81 },
82 ]
83 })
84}
85
86// Fixture: simple declarations, rebinding, compound ops, multi-assign, fixed
87// array index stores, struct field stores, and pointer dereference stores.
88fn make_assign_fixture() []ast.File {
89 return [
90 ast.File{
91 name: 'main.v'
92 mod: 'main'
93 stmts: [
94 ast.Stmt(ast.ModuleStmt{
95 name: 'main'
96 }),
97 ast.Stmt(ast.StructDecl{
98 name: 'Point'
99 fields: [
100 ast.FieldDecl{
101 name: 'x'
102 typ: assign_pin_ident('int')
103 },
104 ]
105 }),
106 ast.Stmt(ast.FnDecl{
107 name: 'do_assign'
108 typ: ast.FnType{}
109 stmts: [
110 assign_pin_decl('x', assign_pin_num('1')),
111 assign_pin_decl('y', assign_pin_ident('x')),
112 assign_pin_stmt([assign_pin_ident('x')], [
113 assign_pin_num('7')], .assign),
114 assign_pin_stmt([assign_pin_ident('x')], [
115 assign_pin_num('2')], .plus_assign),
116 assign_pin_stmt([assign_pin_ident('x'),
117 assign_pin_ident('y')], [assign_pin_ident('y'),
118 assign_pin_ident('x')], .assign),
119 assign_pin_decl('arr', assign_pin_fixed_array()),
120 assign_pin_stmt([
121 assign_pin_index(assign_pin_ident('arr'), assign_pin_num('0')),
122 ], [
123 assign_pin_ident('x'),
124 ], .assign),
125 assign_pin_decl('p', assign_pin_point_init()),
126 assign_pin_stmt([
127 assign_pin_selector(assign_pin_ident('p'), 'x'),
128 ], [
129 assign_pin_index(assign_pin_ident('arr'), assign_pin_num('0')),
130 ], .assign),
131 assign_pin_stmt([
132 assign_pin_selector(assign_pin_ident('p'), 'x'),
133 ], [
134 assign_pin_num('1'),
135 ], .plus_assign),
136 assign_pin_decl('ptr', assign_pin_prefix(.amp, assign_pin_ident('x'))),
137 assign_pin_stmt([
138 assign_pin_prefix(.mul, assign_pin_ident('ptr')),
139 ], [
140 assign_pin_selector(assign_pin_ident('p'), 'x'),
141 ], .assign),
142 ]
143 }),
144 ]
145 },
146 ]
147}
148
149fn build_via_legacy_assign(files []ast.File, env &types.Environment, name string) &Module {
150 mut mod := Module.new(name)
151 mut b := Builder.new_with_env(mod, env)
152 b.register_types_pass1(files[0])
153 b.register_types_pass2(files[0])
154 b.register_fn_signatures(files[0])
155 b.build_fn_bodies(files[0])
156 return mod
157}
158
159fn build_via_flat_assign(files []ast.File, env &types.Environment, name string) &Module {
160 flat := ast.flatten_files(files)
161 fc := flat.file_cursor(0)
162 mut mod := Module.new(name)
163 mut b := Builder.new_with_env(mod, env)
164 b.register_types_pass1_from_flat(fc)
165 b.register_types_pass2_from_flat(fc)
166 b.register_fn_signatures_from_flat(fc)
167 stmts := fc.stmts()
168 for si in 0 .. stmts.len() {
169 c := stmts.at(si)
170 if c.kind() != .stmt_fn_decl {
171 continue
172 }
173 decl := c.fn_decl_signature()
174 fn_name := b.mangle_fn_name(decl)
175 func_idx := b.fn_index[fn_name] or { continue }
176 b.cur_func = func_idx
177 b.label_blocks = map[string]BlockID{}
178 b.vars = map[string]ValueID{}
179 entry := mod.add_block(func_idx, 'entry')
180 b.cur_block = entry
181 body := c.list_at(3)
182 b.build_stmts_from_flat(body)
183 if !b.block_has_terminator(b.cur_block) {
184 mod.add_instr(.ret, b.cur_block, 0, []ValueID{})
185 }
186 }
187 return mod
188}
189
190fn test_build_assign_from_flat_matches_legacy() {
191 files := make_assign_fixture()
192 env := types.Environment.new()
193 mod_legacy := build_via_legacy_assign(files, env, 'assign_legacy')
194 mod_flat := build_via_flat_assign(files, env, 'assign_flat')
195
196 assert mod_legacy.funcs.len == mod_flat.funcs.len
197 assert mod_legacy.funcs.len == 1
198 assert mod_legacy.blocks.len == mod_flat.blocks.len
199 assert mod_legacy.instrs.len == mod_flat.instrs.len
200 assert mod_legacy.values.len == mod_flat.values.len
201}
202