Skip to content

Commit f988dd4

Browse files
committed
stpyv8: unnest the V8 riscv64 stack helpers GCC rejects
The first run to get past gclient sync spent 9h17m in ninja and reached 1146 of 2117 targets -- as far as baseline.o, the first translation unit that pulls in src/codegen/macro-assembler.h -- before GCC refused src/codegen/riscv/macro-assembler-riscv.h:389 and :537, "explicit specialization in non-namespace scope 'class v8::internal::MacroAssembler'". Not a wall of any kind: no OOM, no ENOSPC, no ICE, no signal in the whole log, exactly one FAILED edge, and the 48h timeout was under a fifth spent. MacroAssembler::push_helper and ::pop_helper end a variadic recursion over a register pack with a one-register explicit specialization written inside the class body, which only clang accepts; V8 builds its RISC-V port with nothing else, while settings.py has to choose GCC because no prebuilt Chromium clang runs on a riscv64 host. V8 itself replaced both with an empty zero-register overload in 13.8 -- the variadic overload has already stored the last register at sp+0 by the time the pack empties -- so backport that rather than invent a fix. Rewriting the pinned 13.1.201.22 header with this hunk reproduces 13.8's text byte for byte, and the two forms were compiled both ways to confirm GCC rejects only the old one and clang accepts both. It is the only blocker of its kind left in this V8: the riscv64 sources carry no other in-class specialization, and the two further GCC stoppers the mini-racer V8 build hit postdate 13.1.201.22 (unicode.h has no WriteLeadingAscii here, and highway is not in this BUILD.gn at all). The warning class is already disarmed -- settings.py declares treat_warnings_as_errors=false and no -Werror appears anywhere in the log. Still, pass ninja -k 1000 so the next run reports every remaining GCC-only failure at once instead of one per ten hours, and drop it once the build is green.
1 parent eb460d8 commit f988dd4

1 file changed

Lines changed: 123 additions & 0 deletions

File tree

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2+
From: Ludovic Henry <git@ludovic.dev>
3+
Date: Sun, 20 Sep 2026 12:30:00 +0200
4+
Subject: [PATCH] setup: fix the V8 riscv64 stack helpers GCC rejects
5+
6+
The first run to get past gclient sync and into the build spent 9h17m in
7+
ninja, reached 1146 of 2117 targets -- as far as the first translation
8+
unit that pulls in src/codegen/macro-assembler.h -- and stopped there:
9+
10+
FAILED: obj/v8_base_without_compiler/baseline.o
11+
../../src/codegen/riscv/macro-assembler-riscv.h:389:13: error: explicit
12+
specialization in non-namespace scope 'class v8::internal::MacroAssembler'
13+
../../src/codegen/riscv/macro-assembler-riscv.h:537:13: error: explicit
14+
specialization in non-namespace scope 'class v8::internal::MacroAssembler'
15+
16+
MacroAssembler::push_helper and ::pop_helper walk a register pack by
17+
recursion and end it with a one-register explicit specialization declared
18+
inside the class body. An explicit specialization has to be declared at
19+
namespace scope; clang -- the only compiler V8 builds this port with --
20+
takes the in-class one as an extension, GCC rejects it, and settings.py
21+
has to choose GCC here because no prebuilt Chromium clang runs on a
22+
riscv64 host. Nothing riscv64-specific is wrong with the code: it is
23+
simply the first time this header meets a compiler other than clang.
24+
25+
V8 fixed it in 13.8 by replacing both specializations with an empty,
26+
zero-register overload: once the pack is empty the variadic overload has
27+
already stored the last register at sp+0, so the base case has nothing
28+
left to do and the generated code is identical. Backport exactly that --
29+
rewriting the pinned 13.1.201.22 header with the hunk below reproduces
30+
13.8's own text byte for byte, and both forms were compiled either way to
31+
confirm which compiler accepts which.
32+
33+
Refuse to build rather than rewrite nothing if a future V8 no longer
34+
matches: silently skipping costs another ten hours to find out.
35+
36+
Also pass ninja -k 1000 on riscv64. V8 is warning-clean under clang only,
37+
which is why settings.py already declares treat_warnings_as_errors=false,
38+
but a hard error like this one is still found one translation unit per
39+
run, and this run costs ten hours before it even reaches this file. Let
40+
one build collect as many of them as it can instead; drop it once the
41+
build is green.
42+
43+
https://github.com/riseproject-dev/python-wheels/actions/runs/35479941332/job/105995838025
44+
45+
Upstream-Status: To upstream [not yet submitted to cloudflare/stpyv8]
46+
47+
Signed-off-by: Ludovic Henry <git@ludovic.dev>
48+
---
49+
setup.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++-
50+
1 file changed, 50 insertions(+), 1 deletion(-)
51+
52+
diff --git a/setup.py b/setup.py
53+
index e887710..9586b8d 100644
54+
--- a/setup.py
55+
+++ b/setup.py
56+
@@ -236,7 +236,50 @@ def checkout_v8():
57+
exit(1)
58+
59+
60+
+def unnest_riscv_stack_helper_specializations():
61+
+ # V8 compiles its RISC-V port with clang only, and clang takes an explicit
62+
+ # specialization declared inside a class body as an extension. GCC, which
63+
+ # settings.py routes this build at (is_clang=false, no prebuilt clang runs
64+
+ # on riscv64), rejects it the way the standard asks -- an explicit
65+
+ # specialization has to be declared at namespace scope:
66+
+ #
67+
+ # src/codegen/riscv/macro-assembler-riscv.h:389:13: error: explicit
68+
+ # specialization in non-namespace scope 'class v8::internal::MacroAssembler'
69+
+ #
70+
+ # Both offenders are the one-register base case of a variadic recursion
71+
+ # that pushes or pops a register list. V8 replaced them with an empty,
72+
+ # zero-register overload in 13.8; the variadic overload already writes the
73+
+ # last register at sp+0 once the pack is empty, so the emitted code does
74+
+ # not change. Backport that shape.
75+
+ header = os.path.join(
76+
+ V8_HOME, "src", "codegen", "riscv", "macro-assembler-riscv.h"
77+
+ )
78+
+
79+
+ with open(header, encoding="utf-8", mode="r") as fd:
80+
+ text = fd.read()
81+
+
82+
+ for helper, access in (("push", "StoreWord"), ("pop", "LoadWord")):
83+
+ specialization = (
84+
+ " template <>\n"
85+
+ f" void {helper}_helper(Register r) {{\n"
86+
+ f" {access}(r, MemOperand(sp, 0));\n"
87+
+ " }\n"
88+
+ )
89+
+
90+
+ if specialization not in text:
91+
+ log.error("No %s_helper specialization to unnest in %s", helper, header)
92+
+ exit(1)
93+
+
94+
+ text = text.replace(specialization, f" void {helper}_helper() {{}}\n", 1)
95+
+
96+
+ with open(header, encoding="utf-8", mode="w") as fd:
97+
+ fd.write(text)
98+
+
99+
+
100+
def build_v8():
101+
+ if RISCV64:
102+
+ unnest_riscv_stack_helper_specializations()
103+
+
104+
args = f"gen {os.path.join('out.gn', 'x64.release.sample')} --args=\"{GN_ARGS}\""
105+
success, _, __ = exec_cmd(
106+
os.path.join(DEPOT_HOME, "gn"),
107+
@@ -248,9 +291,15 @@ def build_v8():
108+
if not success:
109+
exit(1)
110+
111+
+ # V8's own CI never compiles this tree with GCC, so a first riscv64 build
112+
+ # meets GCC-only diagnostics one translation unit at a time -- and a single
113+
+ # one of them costs the ten hours it takes to reach it. Collect them all in
114+
+ # one run instead; drop this once the build is green.
115+
+ keep_going = "-k 1000 " if RISCV64 else ""
116+
+
117+
success, _, __ = exec_cmd(
118+
os.path.join(DEPOT_HOME, "ninja"),
119+
- f"-C {os.path.join('out.gn', 'x64.release.sample')} v8_monolith",
120+
+ f"-C {os.path.join('out.gn', 'x64.release.sample')} {keep_going}v8_monolith",
121+
cwd=V8_HOME,
122+
msg="Build V8 with ninja",
123+
)

0 commit comments

Comments
 (0)