Skip to content

Commit 6c710c0

Browse files
committed
v2: self compilation with arm64 backend works!!! 5 levels deep
1 parent 413f509 commit 6c710c0

38 files changed

Lines changed: 3220 additions & 1011 deletions

‎cmd/v2/README.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@
3838
# Execute a small V program directly from the v2 AST:
3939
./v2 -backend eval file.v
4040
41+
# Pass runtime args to the interpreted program:
42+
./v2 -backend eval file.v -- arg1 arg2
43+
44+
# Self-host under eval without recursively reusing the outer compiler flags:
45+
./v2 -backend eval cmd/v2/v2.v -- -o /tmp/v2self cmd/v2/v2.v
46+
4147
# Current scope: literals, locals/consts, plain fn calls, if/for/range,
4248
# arrays, indexing, len, println/print, and basic string interpolation.
4349
```

‎cmd/v2/v2.v‎

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,24 @@ import v2.pref
88
import v2.builder
99

1010
fn main() {
11-
args := os.args[1..]
11+
compile_args, runtime_args := split_eval_runtime_args(os.args[1..])
1212

1313
// Check for 'ast' subcommand
14-
if args.len > 0 && args[0] == 'ast' {
15-
run_ast_command(args[1..])
14+
if compile_args.len > 0 && compile_args[0] == 'ast' {
15+
run_ast_command(compile_args[1..])
1616
return
1717
}
1818

19-
prefs := pref.new_preferences_from_args(args)
19+
mut prefs := pref.new_preferences_from_args(compile_args)
2020

21-
files := get_files(args)
21+
files := get_files(compile_args)
2222
if files.len == 0 {
2323
eprintln('At least 1 .v file expected')
2424
exit(1)
2525
}
26+
mut eval_runtime_args := [files[0]]
27+
eval_runtime_args << runtime_args
28+
prefs.eval_runtime_args = eval_runtime_args
2629

2730
mut b := builder.new_builder(prefs)
2831
b.build(files)
@@ -42,6 +45,15 @@ fn main() {
4245
}
4346
}
4447

48+
fn split_eval_runtime_args(args []string) ([]string, []string) {
49+
for i, arg in args {
50+
if arg == '--' {
51+
return args[..i], args[i + 1..]
52+
}
53+
}
54+
return args, []string{}
55+
}
56+
4557
fn run_ast_command(args []string) {
4658
if args.len == 0 {
4759
eprintln('Usage: v2 ast <file.v>')
@@ -90,6 +102,9 @@ fn get_files(args []string) []string {
90102
mut files := []string{}
91103
mut skip_next := false
92104
for arg in args {
105+
if arg == '--' {
106+
break
107+
}
93108
if skip_next {
94109
skip_next = false
95110
continue

‎examples/hot_reload/bounce.v‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ fn main() {
5151
fn frame(mut game Game) {
5252
game.gg.begin()
5353
game.gg.draw_text_def(10, 5, 'Modify examples/hot_reload/bounce.v to get instant updates')
54-
game.gg.draw_rect_filled(game.x, game.y, width, width, gg.blue)
54+
game.gg.draw_rect_filled(game.x, game.y, width, width, gg.black)
5555
game.gg.draw_rect_filled(window_width - width - game.x + 10, 200 - game.y + width,
5656
width, width, gg.rgb(228, 10, 55))
5757
game.gg.draw_rect_filled(game.x - 25, 250 - game.y, width, width, gg.rgb(28, 240,

‎vlib/builtin/array.v‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,10 @@ fn __new_array_with_map_default(mylen int, cap int, elm_size int, val map) array
161161
fn new_array_from_c_array(len int, cap int, elm_size int, c_array voidptr) array {
162162
panic_on_negative_len(len)
163163
panic_on_negative_cap(cap)
164-
cap_ := if cap < len { len } else { cap }
164+
mut cap_ := cap
165+
if cap < len {
166+
cap_ = len
167+
}
165168
arr := array{
166169
element_size: elm_size
167170
data: vcalloc(u64(cap_) * u64(elm_size))

‎vlib/sokol/gfx/gfx_structs.c.v‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,9 @@ pub fn (b &Bindings) append_index_buffer(data voidptr, element_size int, element
171171

172172
pub struct C.sg_stage_bindings {
173173
pub mut:
174-
images [12]Image
175-
samplers [8]Sampler
174+
images [12]Image
175+
samplers [8]Sampler
176+
storage_buffers [8]Buffer
176177
}
177178

178179
pub type StageBindings = C.sg_stage_bindings

‎vlib/v2/abi/abi.v‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ fn call_signature(m mir.Module, instr &mir.Instruction, fn_by_name map[string]in
168168
ret_typ = fn_typ.ret_type
169169
param_types = fn_typ.params.clone()
170170
}
171+
} else if fn_ptr_typ.kind == .func_t {
172+
// Function pointer extracted from struct field via extractvalue
173+
// has func_t type directly (not wrapped in ptr_t).
174+
ret_typ = fn_ptr_typ.ret_type
175+
param_types = fn_ptr_typ.params.clone()
171176
}
172177
}
173178
}

‎vlib/v2/builder/builder.v‎

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -349,12 +349,30 @@ fn sanitized_utf8_str_visible_length_fn() string {
349349

350350
fn (mut b Builder) compile_cleanc_executable(output_name string, cc string, cc_flags string, cc_link_flags string, error_limit_flag string, mut sw time.StopWatch) {
351351
cc_start := sw.elapsed()
352-
// Non-cached path: compile and link in one step, so include both compile and link flags.
353-
mut all_flags := cc_flags
352+
if b.pref.is_shared_lib {
353+
// Shared library: compile with -shared -fPIC -undefined dynamic_lookup
354+
// Use -fvisibility=hidden so only explicitly exported (impl_live_*) symbols
355+
// are visible. All other functions become hidden, causing the dylib to
356+
// resolve them from the host executable at load time.
357+
mut cc_cmd := '${cc} ${cc_flags} -shared -fPIC -fvisibility=hidden -undefined dynamic_lookup -w -Wno-incompatible-function-pointer-types "${staged_c_file}"'
358+
if cc_link_flags.len > 0 {
359+
cc_cmd += ' -x none ${cc_link_flags}'
360+
}
361+
cc_cmd += ' -o "${output_name}"${error_limit_flag}'
362+
run_cc_cmd_or_exit(cc_cmd, 'shared lib compilation', b.pref.show_cc)
363+
print_time('CC (shared)', time.Duration(sw.elapsed() - cc_start))
364+
println('[*] Compiled shared library ${output_name}')
365+
return
366+
}
367+
// Non-cached path: compile and link in one step.
368+
// Place link flags (which may include .o files) AFTER the source file.
369+
// Use `-x none` to reset the language before .o files, since -x objective-c
370+
// would cause cc to treat .o files as source code.
371+
mut cc_cmd := '${cc} ${cc_flags} -w -Wno-incompatible-function-pointer-types "${staged_c_file}"'
354372
if cc_link_flags.len > 0 {
355-
all_flags += ' ${cc_link_flags}'
373+
cc_cmd += ' -x none ${cc_link_flags}'
356374
}
357-
cc_cmd := '${cc} ${all_flags} -w "${staged_c_file}" -o "${output_name}"${error_limit_flag}'
375+
cc_cmd += ' -o "${output_name}"${error_limit_flag}'
358376
run_cc_cmd_or_exit(cc_cmd, 'C compilation', b.pref.show_cc)
359377
print_time('CC', time.Duration(sw.elapsed() - cc_start))
360378

@@ -924,7 +942,7 @@ fn (mut b Builder) gen_cleanc_with_cached_core(output_name string, cc string, cc
924942

925943
cc_start := sw.elapsed()
926944
main_obj := staged_main_obj_file
927-
compile_main_cmd := '${main_cc} ${main_cc_flags} -w -c "${main_c_file}" -o "${main_obj}"${error_limit_flag}'
945+
compile_main_cmd := '${main_cc} ${main_cc_flags} -w -Wno-incompatible-function-pointer-types -c "${main_c_file}" -o "${main_obj}"${error_limit_flag}'
928946
main_fell_back := run_cc_cmd_or_exit(compile_main_cmd, 'C compilation', b.pref.show_cc)
929947
if main_fell_back && main_cc.contains('tcc') {
930948
// TCC failed on main.c but cached .o files are ELF (from TCC).
@@ -936,7 +954,11 @@ fn (mut b Builder) gen_cleanc_with_cached_core(output_name string, cc string, cc
936954
}
937955
return false
938956
}
939-
mut link_cmd := '${main_cc} ${main_cc_flags} -w "${main_obj}" "${builtin_obj}"'
957+
// Strip -c and -x flags from link command since we're linking, not compiling.
958+
// -x objective-c would cause cc to treat .o files as source code.
959+
mut link_flags := main_cc_flags.replace('-x objective-c', '').replace('-x c', '').replace(' -c ',
960+
' ')
961+
mut link_cmd := '${main_cc} ${link_flags} -w "${main_obj}" "${builtin_obj}"'
940962
if vlib_obj.len > 0 {
941963
link_cmd += ' "${vlib_obj}"'
942964
}
@@ -1070,7 +1092,7 @@ fn (mut b Builder) ensure_cached_module_object(cache_dir string, cache_name stri
10701092
}
10711093
os.write_file(c_path, module_source)!
10721094

1073-
compile_cmd := '${cc} ${cc_flags} -w -c "${c_path}" -o "${obj_path}"${error_limit_flag}'
1095+
compile_cmd := '${cc} ${cc_flags} -w -Wno-incompatible-function-pointer-types -c "${c_path}" -o "${obj_path}"${error_limit_flag}'
10741096
run_cc_cmd_or_exit(compile_cmd, 'C compilation', b.pref.show_cc)
10751097
os.write_file(stamp_path, expected_stamp)!
10761098
return obj_path
@@ -1235,6 +1257,17 @@ fn flag_references_missing_file(flag string) bool {
12351257
|| clean.ends_with('.dylib') || clean.ends_with('.m') || clean.ends_with('.c') {
12361258
if os.is_abs_path(clean) || clean.starts_with('./') || clean.starts_with('../') {
12371259
if !os.exists(clean) {
1260+
// For .o files, try to build from corresponding .c file
1261+
if clean.ends_with('.o') {
1262+
c_file := clean[..clean.len - 2] + '.c'
1263+
if os.exists(c_file) {
1264+
compile_cmd := 'cc -c -w -O2 "${c_file}" -o "${clean}"'
1265+
res := os.execute(compile_cmd)
1266+
if res.exit_code == 0 {
1267+
continue // successfully compiled, not missing
1268+
}
1269+
}
1270+
}
12381271
return true
12391272
}
12401273
}
@@ -1479,10 +1512,52 @@ fn (mut b Builder) gen_native(backend_arch pref.Arch) {
14791512
print_time('SSA Build', time.Duration(native_sw.elapsed() - stage_start))
14801513

14811514
stage_start = native_sw.elapsed()
1482-
ssa_optimize.optimize(mut mod)
1515+
if b.pref.no_optimize {
1516+
eprintln(' opt: skipped (-O0)')
1517+
} else {
1518+
ssa_optimize.optimize(mut mod)
1519+
}
14831520
print_time('SSA Optimize', time.Duration(native_sw.elapsed() - stage_start))
14841521
$if debug {
1485-
ssa_optimize.verify_and_panic(mod, 'full optimization')
1522+
if !b.pref.no_optimize {
1523+
ssa_optimize.verify_and_panic(mod, 'full optimization')
1524+
}
1525+
}
1526+
1527+
// Post-optimization SSA dump for debugging
1528+
dump_fn_name := os.getenv('V2_DUMP_OPT_SSA')
1529+
if dump_fn_name.len > 0 {
1530+
for func in mod.funcs {
1531+
if func.name == dump_fn_name {
1532+
eprintln('=== POST-OPT SSA DUMP: ${func.name} ===')
1533+
eprintln(' params: ${func.params}')
1534+
for pi, pid in func.params {
1535+
pval := mod.values[pid]
1536+
eprintln(' param[${pi}]: v${pid} kind=${pval.kind} name=`${pval.name}` typ=${pval.typ}')
1537+
}
1538+
for blk_id in func.blocks {
1539+
blk := mod.blocks[blk_id]
1540+
eprintln(' block ${blk_id} (${blk.name}):')
1541+
for dval_id in blk.instrs {
1542+
dval := mod.values[dval_id]
1543+
if dval.kind != .instruction {
1544+
continue
1545+
}
1546+
dinstr := mod.instrs[dval.index]
1547+
mut ops_str := ''
1548+
for oi, op_id in dinstr.operands {
1549+
op_v := mod.values[op_id]
1550+
ops_str += 'v${op_id}(${op_v.kind}:${op_v.name})'
1551+
if oi < dinstr.operands.len - 1 {
1552+
ops_str += ', '
1553+
}
1554+
}
1555+
eprintln(' v${dval_id}: ${dinstr.op} [${ops_str}] typ=${dval.typ}')
1556+
}
1557+
}
1558+
eprintln('=== END POST-OPT SSA DUMP ===')
1559+
}
1560+
}
14861561
}
14871562

14881563
stage_start = native_sw.elapsed()

0 commit comments

Comments
 (0)