Skip to content

Commit 550f869

Browse files
committed
v2: major improvements to cleanc, transformer, SSA builder, and ARM64 backend; hello world with builtin compiles and runs!
- Replace transform module with new transformer module for better AST transformations - Expand cleanc C code generator with significantly improved code generation - Enhance SSA builder with additional IR construction capabilities - Fix ARM64 native backend - now passes all tests - Add ARM64 test infrastructure (run_tests.v, string_concat.v) - Add comptime evaluation fixes - Add README with ARM64 testing instructions - Minor fixes to scanner, ast, types, and pref modules
1 parent ac46cc9 commit 550f869

17 files changed

Lines changed: 4854 additions & 762 deletions

File tree

‎cmd/v2/README.md‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Run all ARM64 tests
2+
```
3+
./v run vlib/v2/gen/arm64/tests/run_tests.v
4+
5+
```
6+
# Run specific test with test_ssa_backends
7+
```
8+
cd cmd/v2
9+
./test_ssa_backends arm64 ../../vlib/v2/gen/arm64/tests/string_concat.v
10+
```
11+
12+
# Or directly with v2
13+
```
14+
./v2 -backend arm64 ../../vlib/v2/gen/arm64/tests/string_concat.v
15+
```

‎cmd/v2/test.v‎

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ enum Status {
3333
done = 2
3434
}
3535

36+
// Flag enum for bitfield operations
37+
@[flag]
38+
enum Permissions {
39+
read
40+
write
41+
execute
42+
}
43+
3644
// Interface declaration
3745
interface Drawable {
3846
draw() int
@@ -336,6 +344,131 @@ fn defer_fn_test() int {
336344
return x // returns 3, but defer(fn) adds 300 at function end
337345
}
338346

347+
// ===================== FLAG ENUM TEST =====================
348+
349+
fn flag_enum_test() int {
350+
// Test flag enum .has() method
351+
// Use fully qualified enum values (shorthand in | expr needs type inference)
352+
perms := Permissions.read | Permissions.write
353+
mut result := 0
354+
if perms.has(.read) {
355+
result += 1
356+
}
357+
if perms.has(.write) {
358+
result += 2
359+
}
360+
if perms.has(.execute) {
361+
result += 4 // Should NOT execute
362+
}
363+
// Test .all() method - use fully qualified for | in argument
364+
if perms.all(Permissions.read | Permissions.write) {
365+
result += 10
366+
}
367+
if perms.all(Permissions.read | Permissions.execute) {
368+
result += 20 // Should NOT execute
369+
}
370+
return result // Expected: 1 + 2 + 10 = 13
371+
}
372+
373+
// Debug test for flag enum - returns raw value of perms
374+
fn flag_enum_debug() int {
375+
// This should be: read (1) | write (2) = 3
376+
perms := Permissions.read | Permissions.write
377+
return int(perms) // Expected: 3
378+
}
379+
380+
// Debug test - return has(.read) as int
381+
fn flag_enum_has_read() int {
382+
perms := Permissions.read | Permissions.write
383+
if perms.has(.read) {
384+
return 1
385+
}
386+
return 0
387+
}
388+
389+
// Debug test - return has(.execute) as int
390+
fn flag_enum_has_execute() int {
391+
perms := Permissions.read | Permissions.write
392+
if perms.has(.execute) {
393+
return 1 // Should NOT return this
394+
}
395+
return 0 // Expected: 0
396+
}
397+
398+
// Debug test - return values of individual enum members
399+
fn flag_enum_values() int {
400+
r := int(Permissions.read) // Expected: 1
401+
w := int(Permissions.write) // Expected: 2
402+
e := int(Permissions.execute) // Expected: 4
403+
return r + w * 10 + e * 100 // Expected: 1 + 20 + 400 = 421
404+
}
405+
406+
// Debug test - raw AND operation
407+
fn flag_enum_and_test() int {
408+
perms := Permissions.read | Permissions.write // 3
409+
exec := Permissions.execute // 4
410+
result := int(perms) & int(exec) // 3 & 4 = 0
411+
return result // Expected: 0
412+
}
413+
414+
// Debug test - manual has check without calling has() method
415+
fn flag_enum_manual_has() int {
416+
perms := Permissions.read | Permissions.write // 3
417+
exec := Permissions.execute // 4
418+
anded := int(perms) & int(exec) // 3 & 4 = 0
419+
if anded != 0 {
420+
return 1 // Should NOT return this
421+
}
422+
return 0 // Expected: 0
423+
}
424+
425+
// Debug test - return has() result directly as int (no if)
426+
fn flag_enum_has_result() int {
427+
perms := Permissions.read | Permissions.write
428+
result := perms.has(.execute)
429+
return int(result) // Expected: 0 (false)
430+
}
431+
432+
// Debug test - manual implementation of has() logic
433+
fn flag_enum_manual_has_impl(self int, flag int) int {
434+
anded := self & flag
435+
if anded != 0 {
436+
return 1
437+
}
438+
return 0
439+
}
440+
441+
// Debug test - call manual has impl
442+
fn flag_enum_manual_call() int {
443+
perms := Permissions.read | Permissions.write // 3
444+
exec := Permissions.execute // 4
445+
return flag_enum_manual_has_impl(int(perms), int(exec)) // Expected: 0
446+
}
447+
448+
// Debug test - check what values the has() gets
449+
// This is exactly like has() but returns the args
450+
fn flag_enum_debug_args(self Permissions, flag Permissions) int {
451+
// Return self * 100 + flag so we can see both values
452+
return int(self) * 100 + int(flag)
453+
}
454+
455+
// Debug test - call debug_args (without shorthand)
456+
fn flag_enum_check_args() int {
457+
perms := Permissions.read | Permissions.write // 3
458+
exec := Permissions.execute // 4
459+
return flag_enum_debug_args(perms, exec) // Expected: 304 (3 * 100 + 4)
460+
}
461+
462+
// Debug test - simple 2-arg function with ints
463+
fn simple_two_arg(a int, b int) int {
464+
return a * 100 + b
465+
}
466+
467+
// Debug test - call simple 2-arg function
468+
fn flag_enum_check_int_args() int {
469+
return simple_two_arg(3, 4) // Expected: 304
470+
}
471+
339472
// ===================== IF-GUARD HELPERS =====================
340473

341474
// Returns the value if positive, none otherwise
@@ -2065,6 +2198,30 @@ fn main() {
20652198
// 38.6 Test defer(fn) - function-level defer
20662199
print_int(defer_fn_test()) // 303 (3 from loop + 300 from function-level defers)
20672200

2201+
// 38.7 Test flag enum .has() and .all() methods
2202+
print_str('enum vals (exp 421):')
2203+
print_int(flag_enum_values()) // Expected: 421 (1 + 20 + 400)
2204+
print_str('AND test (exp 0):')
2205+
print_int(flag_enum_and_test()) // Expected: 0 (3 & 4 = 0)
2206+
print_str('manual has (exp 0):')
2207+
print_int(flag_enum_manual_has()) // Expected: 0 (manual has check)
2208+
print_str('flag debug (exp 3):')
2209+
print_int(flag_enum_debug()) // Expected: 3 (read=1 | write=2)
2210+
print_str('has read (exp 1):')
2211+
print_int(flag_enum_has_read()) // Expected: 1
2212+
print_str('has exec (exp 0):')
2213+
print_int(flag_enum_has_execute()) // Expected: 0
2214+
print_str('has result (exp 0):')
2215+
print_int(flag_enum_has_result()) // Expected: 0 (direct bool->int)
2216+
print_str('manual call (exp 0):')
2217+
print_int(flag_enum_manual_call()) // Expected: 0 (manual has impl)
2218+
print_str('check args (exp 304):')
2219+
print_int(flag_enum_check_args()) // Expected: 304 (perms=3, exec=4)
2220+
print_str('int args (exp 304):')
2221+
print_int(flag_enum_check_int_args()) // Expected: 304
2222+
print_str('full test (exp 13):')
2223+
print_int(flag_enum_test()) // Expected: 13 (1 + 2 + 10)
2224+
20682225
// ==================== 39. ENUMS ====================
20692226
print_str('--- 39. Enums ---')
20702227

‎cmd/v2/test_ssa_backends.v‎

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,27 @@ fn main() {
3030
}
3131

3232
// Parse test file from args or default to test.v
33-
input_file := 'test.v'
33+
// Support: ./test_ssa_backends arm64 path/to/file.v
34+
mut input_file := 'test.v'
35+
for arg in os.args {
36+
if arg.ends_with('.v') && arg != @FILE {
37+
input_file = arg
38+
break
39+
}
40+
}
3441
if !os.exists(input_file) {
3542
eprintln('Error: ${input_file} not found')
3643
return
3744
}
3845

46+
// Derive output binary name from input file
47+
base_name := os.file_name(input_file).replace('.v', '')
48+
3949
// Run v2 with selected backend
4050
println('[*] Running v2 -backend ${backend} ${input_file}...')
41-
mut v2_cmd := '${v2_binary} -backend ${backend} ${input_file}'
51+
mut v2_cmd := '${v2_binary} -backend ${backend} ${input_file} -o ${base_name}'
4252
if os.args.contains('--skip-builtin') {
43-
v2_cmd = '${v2_binary} -backend ${backend} --skip-builtin ${input_file}'
53+
v2_cmd = '${v2_binary} -backend ${backend} --skip-builtin ${input_file} -o ${base_name}'
4454
}
4555
v2_res := os.execute(v2_cmd)
4656
if v2_res.exit_code != 0 {
@@ -51,6 +61,12 @@ fn main() {
5161
println(v2_res.output)
5262
println('compilation took ${time.since(t0)}')
5363

64+
// Save the v2-produced binary before running reference (which would overwrite it)
65+
os.cp('./${base_name}', './${base_name}_v2') or {
66+
eprintln('Error: Failed to save v2 binary')
67+
return
68+
}
69+
5470
// Run Reference (v run test.v)
5571
println('[*] Running reference: v -enable-globals run ${input_file}...')
5672
ref_res := os.execute('v -n -w -enable-globals run ${input_file}')
@@ -62,16 +78,11 @@ fn main() {
6278
// Normalize newlines
6379
expected_out := ref_res.output.trim_space().replace('\r\n', '\n')
6480

65-
// Run Generated Binary
81+
// Run Generated Binary (the v2-produced one we saved earlier)
6682
println('[*] Running generated binary...')
67-
// Use script command to create a pty (fixes output buffering issues with printf)
68-
// The -q flag suppresses "Script started" messages, /dev/null discards the typescript file
69-
mut cmd := 'script -q /dev/null ./out_bin'
83+
mut cmd := './${base_name}_v2'
7084
if os.user_os() == 'windows' {
71-
cmd = 'out_bin.exe'
72-
} else if os.user_os() != 'macos' {
73-
// Linux version of script has different syntax
74-
cmd = 'script -q -c ./out_bin /dev/null'
85+
cmd = '${base_name}_v2.exe'
7586
}
7687
gen_res := os.execute(cmd)
7788
if gen_res.exit_code != 0 {
@@ -98,6 +109,9 @@ fn main() {
98109
println('\n[FAILURE] Outputs differ')
99110
expected_lines := expected_out.split('\n')
100111
actual_lines := actual_out.split('\n')
112+
113+
// Find first differing line
114+
mut first_diff := -1
101115
max_lines := if expected_lines.len > actual_lines.len {
102116
expected_lines.len
103117
} else {
@@ -107,9 +121,30 @@ fn main() {
107121
exp := if i < expected_lines.len { expected_lines[i] } else { '<missing>' }
108122
act := if i < actual_lines.len { actual_lines[i] } else { '<missing>' }
109123
if exp != act {
110-
println('line ${i + 1}:')
111-
println(' expected: ${exp}')
112-
println(' got: ${act}')
124+
first_diff = i
125+
break
126+
}
127+
}
128+
129+
if first_diff >= 0 {
130+
context := 2
131+
start := if first_diff > context { first_diff - context } else { 0 }
132+
end := if first_diff + context + 1 < max_lines {
133+
first_diff + context + 1
134+
} else {
135+
max_lines
136+
}
137+
138+
println('\nExpected (reference compiler):')
139+
for i in start .. end {
140+
line := if i < expected_lines.len { expected_lines[i] } else { '<missing>' }
141+
println('${i + 1}: ${line}')
142+
}
143+
144+
println('\nGot (v2 ${backend}):')
145+
for i in start .. end {
146+
line := if i < actual_lines.len { actual_lines[i] } else { '<missing>' }
147+
println('${i + 1}: ${line}')
113148
}
114149
}
115150
}

‎vlib/v2/ast/ast.v‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,12 @@ pub fn (attributes []Attribute) has(name string) bool {
651651
if attribute.name == name {
652652
return true
653653
}
654+
// Also check value when it's a simple identifier (e.g., @[flag])
655+
if attribute.name == '' {
656+
if attribute.value is Ident && attribute.value.name == name {
657+
return true
658+
}
659+
}
654660
}
655661
return false
656662
}

0 commit comments

Comments
 (0)