Skip to content

Commit 4bcc424

Browse files
committed
v2: fix self host with paralell + regalloc; symlink without sudo
1 parent b2fa0c7 commit 4bcc424

22 files changed

Lines changed: 418 additions & 196 deletions

File tree

‎cmd/tools/vsymlink/vsymlink_nix.c.v‎

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,33 @@ fn setup_symlink() {
1111
}
1212
os.rm(link_path) or {}
1313
os.symlink(vexe, link_path) or {
14-
eprintln('Failed to create symlink "${link_path}". Try again with sudo.')
15-
exit(1)
14+
// Try ~/.local/bin as a fallback when /usr/local/bin is not writable.
15+
home := os.home_dir()
16+
if home == '' {
17+
eprintln('Failed to create symlink "${link_path}": ${err}')
18+
eprintln('Try again with sudo.')
19+
exit(1)
20+
}
21+
local_bin := os.join_path(home, '.local', 'bin')
22+
if !os.exists(local_bin) {
23+
os.mkdir_all(local_bin) or {
24+
eprintln('Failed to create symlink "${link_path}": ${err}')
25+
eprintln('Try again with sudo.')
26+
exit(1)
27+
}
28+
}
29+
link_path = os.join_path(local_bin, 'v')
30+
os.rm(link_path) or {}
31+
os.symlink(vexe, link_path) or {
32+
eprintln('Failed to create symlink "${link_path}": ${err}')
33+
eprintln('Try again with sudo.')
34+
exit(1)
35+
}
36+
eprintln('Note: Symlink created in "${local_bin}" instead of "/usr/local/bin".')
37+
if path := os.getenv_opt('PATH') {
38+
if !path.contains(local_bin) {
39+
eprintln('Make sure "${local_bin}" is in your PATH.')
40+
}
41+
}
1642
}
1743
}

‎cmd/v2/test_all.sh‎

Lines changed: 47 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -3,83 +3,73 @@ set -euo pipefail
33

44
cd "$(dirname "$0")"
55

6-
# Files that V1 may clobber during rebuild — backup and restore around v builds.
7-
# Use unique suffixes to avoid name collisions (e.g. two types.v files).
6+
# V1's formatter may clobber v2 source files during rebuild.
7+
# Back up the entire v2 tree and restore after each V1 build.
8+
V2_SRC="../../vlib/v2"
9+
V2_BAK="/tmp/v2_src_bak_test_all"
10+
811
backup_v2_src() {
9-
cp ../../vlib/v2/gen/cleanc/consts_and_globals.v /tmp/bak_ta_cleanc_consts.v
10-
cp ../../vlib/v2/gen/cleanc/assign.v /tmp/bak_ta_cleanc_assign.v
11-
cp ../../vlib/v2/ssa/module.v /tmp/bak_ta_ssa_module.v
12-
cp ../../vlib/v2/ssa/optimize/mem2reg.v /tmp/bak_ta_ssa_mem2reg.v
13-
cp ../../vlib/v2/transformer/struct.v /tmp/bak_ta_tr_struct.v
14-
cp ../../vlib/v2/transformer/transformer.v /tmp/bak_ta_tr_transformer.v
15-
cp ../../vlib/v2/transformer/types.v /tmp/bak_ta_tr_types.v
12+
rm -rf "$V2_BAK"
13+
cp -R "$V2_SRC" "$V2_BAK"
1614
}
1715

1816
restore_v2_src() {
19-
cp /tmp/bak_ta_cleanc_consts.v ../../vlib/v2/gen/cleanc/consts_and_globals.v
20-
cp /tmp/bak_ta_cleanc_assign.v ../../vlib/v2/gen/cleanc/assign.v
21-
cp /tmp/bak_ta_ssa_module.v ../../vlib/v2/ssa/module.v
22-
cp /tmp/bak_ta_ssa_mem2reg.v ../../vlib/v2/ssa/optimize/mem2reg.v
23-
cp /tmp/bak_ta_tr_struct.v ../../vlib/v2/transformer/struct.v
24-
cp /tmp/bak_ta_tr_transformer.v ../../vlib/v2/transformer/transformer.v
25-
cp /tmp/bak_ta_tr_types.v ../../vlib/v2/transformer/types.v
17+
rsync -a --delete "$V2_BAK/" "$V2_SRC/"
2618
}
2719

2820
KNOWN_FAILURES=0
2921

30-
echo "=== 1/13: ARM64 self-host hello world ==="
31-
if v -o v2 v2.v && ./v2 -backend arm64 -nocache -o v3 v2.v && ./v3 -o hello_arm hello.v && ./hello_arm; then
32-
echo "[PASS]"
33-
else
34-
echo "[KNOWN FAILURE] ARM64 self-host — skipping"
35-
KNOWN_FAILURES=$((KNOWN_FAILURES + 1))
36-
fi
22+
echo "=== 1/14: ARM64 self-host hello world ==="
23+
backup_v2_src
24+
v -skip-unused -cc cc -o v2 v2.v
25+
restore_v2_src
26+
./v2 -backend arm64 -nocache -o v3 v2.v && ./v3 -o hello_arm hello.v && ./hello_arm
3727

3828
echo ""
39-
echo "=== 2/13: Self-host test ==="
40-
if bash test_v2_self.sh; then
41-
echo "[PASS]"
29+
echo "=== 2/14: ARM64 self-host chain (v2->v3->v4->v5, parallel) ==="
30+
echo " Building v3 from v2..."
31+
./v2 -nocache -backend arm64 -o v3_chain v2.v
32+
echo " Building v4 from v3..."
33+
./v3_chain -nocache -gc none -backend arm64 -o v4_chain v2.v
34+
echo " Building v5 from v4..."
35+
./v4_chain -nocache -gc none -backend arm64 -o v5_chain v2.v
36+
V4_SIZE=$(wc -c < v4_chain)
37+
V5_SIZE=$(wc -c < v5_chain)
38+
if [ "$V4_SIZE" -eq "$V5_SIZE" ]; then
39+
echo " v4=v5 ($V4_SIZE bytes) — chain converged"
4240
else
43-
echo "[KNOWN FAILURE] Self-host test — skipping"
44-
KNOWN_FAILURES=$((KNOWN_FAILURES + 1))
41+
echo " FAIL: v4 ($V4_SIZE) != v5 ($V5_SIZE)"
42+
exit 1
4543
fi
44+
rm -f v3_chain v4_chain v5_chain
45+
46+
echo ""
47+
echo "=== 3/14: Self-host test ==="
48+
bash test_v2_self.sh
4649

4750
echo ""
48-
echo "=== 3/13: Rebuild v2 and run builtin test files ==="
51+
echo "=== 4/14: Builtin test files (cleanc) ==="
4952
rm -rf /tmp/v2_cleanc_obj_cache
50-
backup_v2_src
51-
v self && v -skip-unused -cc cc -o v2 v2.v
52-
restore_v2_src
5353
./v2 ../../vlib/builtin/array_test.v
5454
./v2 ../../vlib/builtin/string_test.v
5555
./v2 ../../vlib/builtin/map_test.v
5656

5757
echo ""
58-
echo "=== 4/13: Builtin test files (arm64) ==="
59-
if ./v2 -backend arm64 ../../vlib/builtin/array_test.v \
60-
&& ./v2 -backend arm64 ../../vlib/builtin/string_test.v \
61-
&& ./v2 -backend arm64 ../../vlib/builtin/map_test.v; then
62-
echo "[PASS]"
63-
else
64-
echo "[KNOWN FAILURE] ARM64 builtin tests — skipping"
65-
KNOWN_FAILURES=$((KNOWN_FAILURES + 1))
66-
fi
58+
echo "=== 5/14: Builtin test files (arm64) ==="
59+
./v2 -backend arm64 ../../vlib/builtin/array_test.v
60+
./v2 -backend arm64 ../../vlib/builtin/string_test.v
61+
./v2 -backend arm64 ../../vlib/builtin/map_test.v
6762

6863
echo ""
69-
echo "=== 5/13: Math test ==="
64+
echo "=== 6/14: Math test ==="
7065
./v2 ../../vlib/math/math_test.v
7166

7267
echo ""
73-
echo "=== 6/13: Math test (arm64) ==="
74-
if ./v2 -backend arm64 ../../vlib/math/math_test.v; then
75-
echo "[PASS]"
76-
else
77-
echo "[KNOWN FAILURE] ARM64 math test — skipping"
78-
KNOWN_FAILURES=$((KNOWN_FAILURES + 1))
79-
fi
68+
echo "=== 7/14: Math test (arm64) ==="
69+
./v2 -backend arm64 ../../vlib/math/math_test.v
8070

8171
echo ""
82-
echo "=== 7/13: Sumtype tests ==="
72+
echo "=== 8/14: Sumtype tests ==="
8373
./v2 test_sumtype.v
8474
./v2 test_sumtype2.v
8575
./v2 test_sumtype3.v
@@ -92,7 +82,7 @@ echo "=== 7/13: Sumtype tests ==="
9282
./v2 test_sumtype_global.v
9383

9484
echo ""
95-
echo "=== 8/13: Sumtype tests (arm64) ==="
85+
echo "=== 9/14: Sumtype tests (arm64) ==="
9686
./v2 -backend arm64 test_sumtype.v
9787
./v2 -backend arm64 test_sumtype2.v
9888
./v2 -backend arm64 test_sumtype3.v
@@ -105,28 +95,23 @@ echo "=== 8/13: Sumtype tests (arm64) ==="
10595
./v2 -backend arm64 test_sumtype_global.v
10696

10797
echo ""
108-
echo "=== 9/13: SSA backends test (arm64) ==="
98+
echo "=== 10/14: SSA backends test (arm64) ==="
10999
v -gc none run test_ssa_backends.v arm64
110100

111101
echo ""
112-
echo "=== 10/13: SSA backends test (cleanc) ==="
102+
echo "=== 11/14: SSA backends test (cleanc) ==="
113103
v -gc none run test_ssa_backends.v cleanc
114104

115105
echo ""
116-
echo "=== 11/13: Transformer unit tests ==="
117-
if v ../../vlib/v2/transformer/transformer_test.v; then
118-
echo "[PASS]"
119-
else
120-
echo "[KNOWN FAILURE] Transformer unit tests — skipping"
121-
KNOWN_FAILURES=$((KNOWN_FAILURES + 1))
122-
fi
106+
echo "=== 12/14: Transformer unit tests ==="
107+
v ../../vlib/v2/transformer/transformer_test.v
123108

124109
echo ""
125-
echo "=== 12/13: Transformer integration test ==="
110+
echo "=== 13/14: Transformer integration test ==="
126111
v ../../vlib/v2/transformer/transformer_v2_darwin_test.v
127112

128113
echo ""
129-
echo "=== 13/13: Cleanc runtime tests ==="
114+
echo "=== 14/14: Cleanc runtime tests ==="
130115
v -gc none run ../../vlib/v2/gen/cleanc/tests/run_tests.v
131116

132117
echo ""

‎cmd/v2/test_v2_self.sh‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,21 @@ if [[ ! -x "${v1_compiler}" ]]; then
2323
exit 1
2424
fi
2525

26+
# V1's formatter may clobber v2 source files — backup and restore.
27+
v2_src="${repo_root}/vlib/v2"
28+
v2_bak="/tmp/v2_src_bak_self_test"
29+
rm -rf "${v2_bak}"
30+
cp -R "${v2_src}" "${v2_bak}"
31+
2632
# Build v2 with v1.
2733
rm -f "${v2_bin}" "${v3_bin}" "${v3_bin}.c" "${v4_bin}" "${v4_bin}.c" "${v5_bin}" "${v5_bin}.c"
28-
"${v1_compiler}" -gc none -o "${v2_bin}" "${v2_source}"
34+
"${v1_compiler}" -skip-unused -cc cc -o "${v2_bin}" "${v2_source}"
35+
36+
# Restore v2 sources after V1 build.
37+
rsync -a --delete "${v2_bak}/" "${v2_src}/"
38+
39+
# Use clang instead of TCC for v2-compiled C code.
40+
export V2CC="${V2CC:-cc}"
2941

3042
# Use v2 to compile itself to v3 (using defined backend).
3143
"${v2_bin}" -gc none -o "${v3_bin}" -backend "${backend}" "${v2_source}"

‎vlib/v/help/installation/symlink.txt‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ This command adds a symlink for the V compiler executable.
33
Usage:
44
v symlink [OPTIONS]
55

6-
Note that on Unix systems this command requires write permissions to
7-
/usr/local/bin to work.
6+
On Unix systems this command creates a symlink in /usr/local/bin. If that
7+
is not writable, it falls back to ~/.local/bin without requiring sudo.
88

99
For GitHub Actions, the option -githubci needs to be specified.

‎vlib/v2/builder/gen_arm64_parallel.v‎

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,27 @@ module builder
55

66
import runtime
77
import v2.gen.arm64
8+
import v2.mir
89

910
struct GenARM64ChunkArgs {
10-
gen voidptr // &arm64.Gen (main gen, used as template for cloning)
11+
worker voidptr // &arm64.Gen — pre-cloned worker (created on main thread)
12+
mod_ptr voidptr // &mir.Module — shared MIR module
1113
start_idx int
1214
end_idx int
13-
worker voidptr // &voidptr — output slot for worker Gen pointer
1415
}
1516

1617
fn C.pthread_create(thread voidptr, attr voidptr, start_routine fn (voidptr) voidptr, arg voidptr) int
17-
1818
fn C.pthread_join(thread voidptr, retval voidptr) int
19+
fn C.pthread_attr_init(attr voidptr) int
20+
fn C.pthread_attr_setstacksize(attr voidptr, stacksize usize) int
21+
fn C.pthread_attr_destroy(attr voidptr) int
1922

2023
fn gen_arm64_chunk_thread(arg voidptr) voidptr {
2124
a := unsafe { &GenARM64ChunkArgs(arg) }
22-
g := unsafe { &arm64.Gen(a.gen) }
23-
mut w := g.new_worker_clone()
25+
mut w := unsafe { &arm64.Gen(a.worker) }
26+
m := unsafe { &mir.Module(a.mod_ptr) }
2427
for fi := a.start_idx; fi < a.end_idx; fi++ {
25-
w.gen_func(g.mod.funcs[fi])
26-
}
27-
unsafe {
28-
*(&voidptr(a.worker)) = voidptr(w)
28+
w.gen_func(m.funcs[fi])
2929
}
3030
return unsafe { nil }
3131
}
@@ -45,35 +45,50 @@ fn (mut b Builder) gen_arm64_parallel(mut gen arm64.Gen) {
4545
return
4646
}
4747

48-
// Split functions into chunks and spawn workers via pthreads
48+
// Split functions into chunks
4949
chunk_size := (n_funcs + n_jobs - 1) / n_jobs
50-
mut worker_ptrs := []voidptr{len: n_jobs, init: unsafe { nil }}
5150
mut thread_ids := []voidptr{len: n_jobs, init: unsafe { nil }}
5251
mut args := []GenARM64ChunkArgs{cap: n_jobs}
52+
53+
// Pre-create all workers on the main thread to avoid concurrent .clone() races.
54+
// Each worker gets its own deep copy of maps/arrays.
55+
mut workers := []voidptr{cap: n_jobs}
56+
5357
mut chunk_idx := 0
5458
mut i := 0
5559
for i < n_funcs {
5660
end := if i + chunk_size < n_funcs { i + chunk_size } else { n_funcs }
61+
w := gen.new_worker_clone()
62+
workers << voidptr(w)
5763
args << GenARM64ChunkArgs{
58-
gen: unsafe { voidptr(&gen) }
64+
worker: voidptr(w)
65+
mod_ptr: unsafe { voidptr(gen.mod) }
5966
start_idx: i
6067
end_idx: end
61-
worker: unsafe { voidptr(&worker_ptrs[chunk_idx]) }
6268
}
63-
C.pthread_create(unsafe { voidptr(&thread_ids[chunk_idx]) }, unsafe { nil },
64-
gen_arm64_chunk_thread, unsafe { voidptr(&args[chunk_idx]) })
6569
i = end
6670
chunk_idx++
6771
}
6872

73+
attr_buf := [64]u8{}
74+
attr := unsafe { voidptr(&attr_buf[0]) }
75+
C.pthread_attr_init(attr)
76+
C.pthread_attr_setstacksize(attr, 64 * 1024 * 1024)
77+
78+
for ci := 0; ci < chunk_idx; ci++ {
79+
C.pthread_create(unsafe { voidptr(&thread_ids[ci]) }, attr, gen_arm64_chunk_thread,
80+
unsafe { voidptr(&args[ci]) })
81+
}
82+
C.pthread_attr_destroy(attr)
83+
6984
// Wait for all workers
7085
for ci := 0; ci < chunk_idx; ci++ {
7186
C.pthread_join(thread_ids[ci], unsafe { nil })
7287
}
7388

7489
// Merge worker results in order
7590
for ci := 0; ci < chunk_idx; ci++ {
76-
w := unsafe { &arm64.Gen(worker_ptrs[ci]) }
91+
w := unsafe { &arm64.Gen(workers[ci]) }
7792
gen.merge_worker(w)
7893
}
7994

0 commit comments

Comments
 (0)