Skip to content

Commit ceb6b91

Browse files
committed
v3: fix CI reachability and arm64 lowering
1 parent 03ea0e8 commit ceb6b91

7 files changed

Lines changed: 181 additions & 29 deletions

File tree

vlib/v3/driver/driver.v

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8304,7 +8304,9 @@ pub fn run(args []string) {
83048304
pre_tc.reject_unsupported_generics = is_selfhost
83058305
mut ckpre_sw := time.new_stopwatch()
83068306
set_diagnostic_files(mut pre_tc, user_files)
8307-
trivial_literal_output = test_files.len == 0 && !is_checker_fixture
8307+
// The C generator has a dedicated literal-output path. The SSA/native backend
8308+
// still builds ordinary builtin bodies, so it needs their full dependency set.
8309+
trivial_literal_output = backend != 'arm64' && test_files.len == 0 && !is_checker_fixture
83088310
&& markused.is_trivial_literal_output_program(a, pre_tc.diagnostic_files)
83098311
if verbose {
83108312
eprintln(' [ttime] ck trivial gate ${f64(ckpre_sw.elapsed().microseconds()) / 1000.0:7.2f} ms')

vlib/v3/gen/arm64/gen.v

Lines changed: 73 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ fn arm64_max_int(a int, b int) int {
223223
fn (mut g Gen) gen_pre_pass() {
224224
mut data_offset := u64(0)
225225
for gi in 0 .. g.m.globals.len {
226+
if g.m.globals[gi].linkage == .external {
227+
continue
228+
}
226229
data_offset = (data_offset + 7) & ~u64(7)
227230
g.macho.add_symbol('_' + g.m.globals[gi].name, data_offset, true, 3)
228231
size := g.m.type_size(g.m.globals[gi].typ)
@@ -233,6 +236,9 @@ fn (mut g Gen) gen_pre_pass() {
233236
// gen_post_pass emits post pass output for arm64.
234237
fn (mut g Gen) gen_post_pass() {
235238
for gi in 0 .. g.m.globals.len {
239+
if g.m.globals[gi].linkage == .external {
240+
continue
241+
}
236242
for g.macho.data_data.len % 8 != 0 {
237243
g.macho.data_data << 0
238244
}
@@ -479,7 +485,7 @@ fn (mut g Gen) emit_zero_aggregate(ptr_reg int, typ_id ssa.TypeID, max_size int)
479485
}
480486
n_words := (size + 7) / 8
481487
for wi in 0 .. n_words {
482-
g.emit32(asm_str_imm(xzr, Reg(ptr_reg), u32(wi)))
488+
g.emit_store_reg_offset(xzr, Reg(ptr_reg), wi * 8)
483489
}
484490
}
485491

@@ -650,29 +656,27 @@ fn (mut g Gen) gen_instr(val_id int) {
650656
g.emit32(asm_str(Reg(8), Reg(ptr_reg)))
651657
g.emit32(asm_str_imm(Reg(10), Reg(ptr_reg), 1))
652658
} else {
659+
ptr_reg := g.load_val(ptr_id, 9)
660+
dest_type := g.ptr_elem_type(ptr_id)
661+
if g.is_zero_const(src_id) && g.is_aggregate_type(dest_type) {
662+
g.emit_zero_aggregate(ptr_reg, dest_type, g.aggregate_store_size(ptr_id,
663+
dest_type))
664+
return
665+
}
653666
src_size := g.m.type_size(src_val.typ)
654667
if src_size > 8 && g.is_value_aggregate_type(src_val.typ) {
655668
if src_off := g.stack_slot(src_id) {
656-
ptr_reg := g.load_val(ptr_id, 9)
657669
copy_size := g.aggregate_store_size(ptr_id, src_val.typ)
658670
n_words := (copy_size + 7) / 8
659671
for wi in 0 .. n_words {
660672
g.emit_load_fp(8, src_off + wi * 8)
661-
g.emit32(asm_str_imm(Reg(8), Reg(ptr_reg), u32(wi)))
673+
g.emit_store_reg_offset(Reg(8), Reg(ptr_reg), wi * 8)
662674
}
663675
} else {
664676
src_reg := g.load_val(src_id, 8)
665-
ptr_reg := g.load_val(ptr_id, 9)
666677
g.emit32(asm_str(Reg(src_reg), Reg(ptr_reg)))
667678
}
668679
} else {
669-
ptr_reg := g.load_val(ptr_id, 9)
670-
dest_type := g.ptr_elem_type(ptr_id)
671-
if g.is_zero_const(src_id) && g.is_aggregate_type(dest_type) {
672-
g.emit_zero_aggregate(ptr_reg, dest_type, g.aggregate_store_size(ptr_id,
673-
dest_type))
674-
return
675-
}
676680
src_reg := g.load_val(src_id, 8)
677681
store_typ := if int(dest_type) > 0 { dest_type } else { src_val.typ }
678682
g.emit_store_typed(src_reg, ptr_reg, store_typ)
@@ -723,7 +727,7 @@ fn (mut g Gen) gen_instr(val_id int) {
723727
copy_words := (copy_size + 7) / 8
724728
total_words := (result_size + 7) / 8
725729
for wi in 0 .. copy_words {
726-
g.emit32(asm_ldr_imm(Reg(8), Reg(ptr_reg), u32(wi)))
730+
g.emit_load_reg_offset(Reg(8), Reg(ptr_reg), wi * 8)
727731
g.emit_store_fp(8, off + wi * 8)
728732
}
729733
if copy_words < total_words {
@@ -992,18 +996,18 @@ fn (mut g Gen) gen_instr(val_id int) {
992996
if off := g.stack_slot(ret_id) {
993997
for wi in 0 .. n_words {
994998
g.emit_load_fp(8, off + wi * 8)
995-
g.emit32(asm_str_imm(Reg(8), Reg(9), u32(wi)))
999+
g.emit_store_reg_offset(Reg(8), Reg(9), wi * 8)
9961000
}
9971001
} else {
9981002
g.emit_mov_imm(8, 0)
9991003
for wi in 0 .. n_words {
1000-
g.emit32(asm_str_imm(Reg(8), Reg(9), u32(wi)))
1004+
g.emit_store_reg_offset(Reg(8), Reg(9), wi * 8)
10011005
}
10021006
}
10031007
} else {
10041008
g.emit_mov_imm(8, 0)
10051009
for wi in 0 .. n_words {
1006-
g.emit32(asm_str_imm(Reg(8), Reg(9), u32(wi)))
1010+
g.emit_store_reg_offset(Reg(8), Reg(9), wi * 8)
10071011
}
10081012
}
10091013
if g.stack_size > 0 {
@@ -1434,7 +1438,7 @@ fn (mut g Gen) emit_copy_ptr_to_fp(src_ptr_reg int, dst_off int, size int) {
14341438
n_words := (size + 7) / 8
14351439
tmp_reg := if src_ptr_reg == 8 { 10 } else { 8 }
14361440
for wi in 0 .. n_words {
1437-
g.emit32(asm_ldr_imm(Reg(tmp_reg), Reg(src_ptr_reg), u32(wi)))
1441+
g.emit_load_reg_offset(Reg(tmp_reg), Reg(src_ptr_reg), wi * 8)
14381442
g.emit_store_fp(tmp_reg, dst_off + wi * 8)
14391443
}
14401444
}
@@ -1564,12 +1568,28 @@ fn (mut g Gen) emit_global_addr(reg int, name string) {
15641568
} else {
15651569
sym_idx = g.macho.add_undefined(sym_name)
15661570
}
1571+
if g.is_external_global(name) {
1572+
g.macho.add_reloc(g.macho.text_data.len, sym_idx, arm64_reloc_got_load_page21, true)
1573+
g.emit32(asm_adrp(Reg(reg)))
1574+
g.macho.add_reloc(g.macho.text_data.len, sym_idx, arm64_reloc_got_load_pageoff12, false)
1575+
g.emit32(asm_ldr_pageoff(Reg(reg)))
1576+
return
1577+
}
15671578
g.macho.add_reloc(g.macho.text_data.len, sym_idx, arm64_reloc_page21, true)
15681579
g.emit32(asm_adrp(Reg(reg)))
15691580
g.macho.add_reloc(g.macho.text_data.len, sym_idx, arm64_reloc_pageoff12, false)
15701581
g.emit32(asm_add_pageoff(Reg(reg)))
15711582
}
15721583

1584+
fn (g &Gen) is_external_global(name string) bool {
1585+
for global in g.m.globals {
1586+
if global.name == name {
1587+
return global.linkage == .external
1588+
}
1589+
}
1590+
return false
1591+
}
1592+
15731593
// find_global_idx_by_name resolves find global idx by name information for arm64.
15741594
fn (g &Gen) find_global_idx_by_name(name string) int {
15751595
for i, global in g.m.globals {
@@ -1760,19 +1780,13 @@ fn (mut g Gen) emit_store_fp(reg int, offset int) {
17601780
g.emit32(asm_add_reg(Reg(11), fp, Reg(11)))
17611781
g.emit32(asm_str(Reg(reg), Reg(11)))
17621782
} else {
1763-
g.emit32(asm_str_imm(Reg(reg), fp, u32(offset / 8)))
1783+
g.emit_store_reg_offset(Reg(reg), fp, offset)
17641784
}
17651785
}
17661786

17671787
// emit_store_sp emits emit store sp output for arm64.
17681788
fn (mut g Gen) emit_store_sp(reg int, offset int) {
1769-
if offset >= 0 && offset < 32768 && offset % 8 == 0 {
1770-
g.emit32(asm_str_imm(Reg(reg), sp, u32(offset / 8)))
1771-
} else {
1772-
g.emit_mov_imm(11, i64(offset))
1773-
g.emit32(asm_add_reg(Reg(11), sp, Reg(11)))
1774-
g.emit32(asm_str(Reg(reg), Reg(11)))
1775-
}
1789+
g.emit_store_reg_offset(Reg(reg), sp, offset)
17761790
}
17771791

17781792
// emit_load_fp emits emit load fp output for arm64.
@@ -1784,8 +1798,42 @@ fn (mut g Gen) emit_load_fp(reg int, offset int) {
17841798
g.emit32(asm_add_reg(Reg(11), fp, Reg(11)))
17851799
g.emit32(asm_ldr(Reg(reg), Reg(11)))
17861800
} else {
1787-
g.emit32(asm_ldr_imm(Reg(reg), fp, u32(offset / 8)))
1801+
g.emit_load_reg_offset(Reg(reg), fp, offset)
1802+
}
1803+
}
1804+
1805+
fn (mut g Gen) emit_store_reg_offset(src Reg, base Reg, offset int) {
1806+
if offset >= 0 && offset < 32768 && offset % 8 == 0 {
1807+
g.emit32(asm_str_imm(src, base, u32(offset / 8)))
1808+
return
1809+
}
1810+
scratch := if int(src) != 11 && int(base) != 11 {
1811+
Reg(11)
1812+
} else if int(src) != 12 && int(base) != 12 {
1813+
Reg(12)
1814+
} else {
1815+
Reg(13)
1816+
}
1817+
g.emit_mov_imm(int(scratch), i64(offset))
1818+
g.emit32(asm_add_reg(scratch, base, scratch))
1819+
g.emit32(asm_str(src, scratch))
1820+
}
1821+
1822+
fn (mut g Gen) emit_load_reg_offset(dst Reg, base Reg, offset int) {
1823+
if offset >= 0 && offset < 32768 && offset % 8 == 0 {
1824+
g.emit32(asm_ldr_imm(dst, base, u32(offset / 8)))
1825+
return
1826+
}
1827+
scratch := if int(dst) != 11 && int(base) != 11 {
1828+
Reg(11)
1829+
} else if int(dst) != 12 && int(base) != 12 {
1830+
Reg(12)
1831+
} else {
1832+
Reg(13)
17881833
}
1834+
g.emit_mov_imm(int(scratch), i64(offset))
1835+
g.emit32(asm_add_reg(scratch, base, scratch))
1836+
g.emit32(asm_ldr(dst, scratch))
17891837
}
17901838

17911839
// emit_lea_fp emits emit lea fp output for arm64.

vlib/v3/gen/arm64/gen_test.v

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,26 @@ fn test_sparse_codegen_slots_only_store_current_function_ids() {
3535
assert g.alloca_byte_size(6) == none
3636
assert g.block_offset(0) == none
3737
}
38+
39+
fn test_zero_aggregate_uses_address_fallback_past_immediate_range() {
40+
mut m := ssa.Module.new()
41+
i64_type := m.type_store.get_int(64)
42+
large_array := m.type_store.get_array(i64_type, 4097)
43+
mut g := Gen.new(m)
44+
g.emit_zero_aggregate(9, large_array, 0)
45+
assert g.macho.text_data.len == (4096 + 3) * 4
46+
last := g.macho.text_data.len - 4
47+
assert read_u32_le(g.macho.text_data, last) == asm_str(xzr, Reg(11))
48+
}
49+
50+
fn test_external_global_address_uses_got_load_relocations() {
51+
mut m := ssa.Module.new()
52+
i8_type := m.type_store.get_int(8)
53+
m.add_external_global('environ', m.type_store.get_ptr(m.type_store.get_ptr(i8_type)))
54+
mut g := Gen.new(m)
55+
g.emit_global_addr(8, 'environ')
56+
assert g.macho.relocs.len == 2
57+
assert g.macho.relocs[0].type_ == arm64_reloc_got_load_page21
58+
assert g.macho.relocs[1].type_ == arm64_reloc_got_load_pageoff12
59+
assert read_u32_le(g.macho.text_data, 4) == asm_ldr_pageoff(Reg(8))
60+
}

vlib/v3/markused/markused.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ fn mark_used_with_test_files(a &flat.FlatAst, tc &types.TypeChecker, test_files
618618
}
619619
generic_type_bases: generic_type_bases
620620
detect_generics: detect_reachable_generics
621-
body_checker_edges_authoritative: !detect_reachable_generics
621+
body_checker_edges_authoritative: use_prepared && !detect_reachable_generics
622622
}
623623
// Precollect every body's call/initializer-ref lists up front (across
624624
// threads when available): the BFS below then only does the cheap

vlib/v3/ssa/builder.v

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6624,9 +6624,12 @@ fn (mut b Builder) build_assign(node flat.Node) {
66246624
b.emit2(.store, b.void_type, result, addr)
66256625
}
66266626
}
6627-
} else if lhs.kind == .selector || lhs.kind == .index {
6627+
} else if lhs.kind == .selector || lhs.kind == .index
6628+
|| (lhs.kind == .prefix && lhs.op == .mul) {
66286629
addr := if lhs.kind == .selector {
66296630
b.build_selector_addr(lhs)
6631+
} else if lhs.kind == .prefix && lhs.children_count > 0 {
6632+
b.build_expr(b.a.child(&lhs, 0))
66306633
} else {
66316634
b.build_lvalue_addr(lhs_id)
66326635
}
@@ -10926,6 +10929,12 @@ fn (mut b Builder) build_selector(node flat.Node) ValueID {
1092610929

1092710930
if base.kind == .ident && base.value == 'C' {
1092810931
match field_name {
10932+
'environ' {
10933+
ptr_i8 := b.m.type_store.get_ptr(b.i8_type)
10934+
environ_type := b.m.type_store.get_ptr(ptr_i8)
10935+
environ_addr := b.m.add_external_global('environ', environ_type)
10936+
return b.emit1(.load, environ_type, environ_addr)
10937+
}
1092910938
'SEEK_SET' {
1093010939
return b.m.get_or_add_const(b.i64_type, '0')
1093110940
}

vlib/v3/tests/ssa_builder_parity_test.v

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,59 @@ fn get_bytes(d &Digest) [32]u8 {
383383
assert typ.len == 32
384384
}
385385

386+
// test_c_environ_loads_external_global validates this v3 regression case.
387+
fn test_c_environ_loads_external_global() {
388+
m := build_source('c_environ_external_global', '
389+
fn read_environ() voidptr {
390+
return voidptr(C.environ)
391+
}
392+
')
393+
mut found := false
394+
for global in m.globals {
395+
if global.name == 'environ' {
396+
assert global.linkage == .external
397+
found = true
398+
}
399+
}
400+
assert found
401+
}
402+
403+
// test_dereference_struct_assignment_emits_store validates this v3 regression case.
404+
fn test_dereference_struct_assignment_emits_store() {
405+
m := build_source('dereference_struct_assignment', '
406+
struct Big {
407+
a int
408+
b int
409+
c int
410+
}
411+
412+
fn copy_into(dst &Big, value Big) {
413+
unsafe {
414+
*dst = value
415+
}
416+
}
417+
')
418+
f := find_func(m, 'copy_into')
419+
mut struct_stores := 0
420+
for block_id in f.blocks {
421+
for value_id in m.blocks[block_id].instrs {
422+
value := m.values[value_id]
423+
if value.kind != .instruction {
424+
continue
425+
}
426+
instr := m.instrs[value.index]
427+
if instr.op != .store || instr.operands.len < 2 {
428+
continue
429+
}
430+
src_type := m.values[instr.operands[0]].typ
431+
if src_type > 0 && m.type_store.types[src_type].kind == .struct_t {
432+
struct_stores++
433+
}
434+
}
435+
}
436+
assert struct_stores >= 2
437+
}
438+
386439
// test_label_and_goto_lower_to_jump_blocks validates this v3 regression case.
387440
fn test_label_and_goto_lower_to_jump_blocks() {
388441
m := build_source('label_goto', '

vlib/v3/tests/ssa_production_native_test.v

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,27 @@ fn loop_sum(n int) int {
3434
return total
3535
}
3636
37+
struct Index {
38+
mut:
39+
files map[string]int
40+
}
41+
42+
struct State {
43+
mut:
44+
index Index
45+
}
46+
47+
fn add_file(mut index Index) {
48+
index.files["main.v"] = 7
49+
}
50+
3751
fn main() {
3852
println(int_str(choose(true)))
3953
println(int_str(choose(false)))
4054
println(int_str(loop_sum(10)))
55+
mut state := State{}
56+
add_file(mut state.index)
57+
println(int_str(state.index.files["main.v"]))
4158
}
4259
') or {
4360
panic(err)
@@ -47,7 +64,7 @@ fn main() {
4764
assert compile.exit_code == 0, compile.output
4865
run := cmdexec.run(output, [])
4966
assert run.exit_code == 0, run.output
50-
assert run.output.trim_space() == '10\n20\n-5'
67+
assert run.output.trim_space() == '10\n20\n-5\n7'
5168
} $else {
5269
assert true
5370
}

0 commit comments

Comments
 (0)