From 595577f6066cc04918e56dc150c50d79a3d7f3bf Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Thu, 28 May 2026 20:13:07 +0900 Subject: [PATCH] cgen: route non-named callee to indirect CALL AX (wwstage #181-cgen) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Discovered while verifying #181's checker fix end-to-end: wwstage's cgcall pre-computes `isfnptrcall` only for N_IDENT (local) and N_DOT (struct fn-ptr field) callees. For a non-named callee — the deref-call `(*f)(...)` shape (N_UN TK_STAR) most prominently — the flag stayed false, the IDENT/DOT name-emit branches both missed, and the emit produced `CALL (SB)` with an empty symbol. Cstage handles this naturally via its default-fallthrough at cmd/w6c/cgen.c:5918-5921 — `else { cgexpr(c, n->lhs, locals); ins1(c, A_CALL, areg(D_AX)); }` catches every callee shape that isn't bare-IDENT module-fn or N_DOT module-qualified call. The fix here mirrors that fallthrough: any callee whose kind is neither N_IDENT nor N_DOT sets isfnptrcall = true, routing through the existing cgexpr-into-AX + CALL AX path. Combined.ww regenerated for selfhost/cmd/{w6c,wwdump}/main .combined.ww per #110 freshness gate. Lands as a follow-up to the #181 checker bail-lift: the checker now stamps the deref-call N_CALL (so cgen runs), and with this fix the wwstage cgen lowers it correctly. test/wcc/766's wwstage +byte-id rows go green; runtime symmetry with cstage holds. --- selfhost/cmd/w6c/main.combined.ww | 13 +++++++++++++ selfhost/cmd/wcc/cgenexpr.ww | 13 +++++++++++++ selfhost/cmd/wwdump/main.combined.ww | 13 +++++++++++++ 3 files changed, 39 insertions(+) diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index ac20e233..7441f933 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -21659,6 +21659,19 @@ fn cgcall(c: *cgen, n: *node) void = { isfnptrcall = true; }; }; + // #181-cgen: a non-named callee (`(*f)(...)` → N_UN TK_STAR, + // or any other expression-valued fn) is an indirect call. + // cgexpr the callee into AX; CALL AX. Mirrors cstage's + // default-fallthrough at cmd/w6c/cgen.c:5918-5921 which + // catches every callee shape that isn't a bare-IDENT module + // fn or N_DOT module-qualified call. Pre-fix wwstage emitted + // `CALL (SB)` (empty symbol) for the N_UN-callee shape — the + // IDENT/DOT name-emit branches missed and isfnptrcall stayed + // false. + if (callee.kind != nkind.N_IDENT + && callee.kind != nkind.N_DOT) { + isfnptrcall = true; + }; if (callee.kind == nkind.N_DOT) { let base: *node = callee.lhs; let fld: str = callee.str; diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index 956dae18..9fdc650d 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -4099,6 +4099,19 @@ fn cgcall(c: *cgen, n: *node) void = { isfnptrcall = true; }; }; + // #181-cgen: a non-named callee (`(*f)(...)` → N_UN TK_STAR, + // or any other expression-valued fn) is an indirect call. + // cgexpr the callee into AX; CALL AX. Mirrors cstage's + // default-fallthrough at cmd/w6c/cgen.c:5918-5921 which + // catches every callee shape that isn't a bare-IDENT module + // fn or N_DOT module-qualified call. Pre-fix wwstage emitted + // `CALL (SB)` (empty symbol) for the N_UN-callee shape — the + // IDENT/DOT name-emit branches missed and isfnptrcall stayed + // false. + if (callee.kind != nkind.N_IDENT + && callee.kind != nkind.N_DOT) { + isfnptrcall = true; + }; if (callee.kind == nkind.N_DOT) { let base: *node = callee.lhs; let fld: str = callee.str; diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index b60f8818..6dc73499 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -21659,6 +21659,19 @@ fn cgcall(c: *cgen, n: *node) void = { isfnptrcall = true; }; }; + // #181-cgen: a non-named callee (`(*f)(...)` → N_UN TK_STAR, + // or any other expression-valued fn) is an indirect call. + // cgexpr the callee into AX; CALL AX. Mirrors cstage's + // default-fallthrough at cmd/w6c/cgen.c:5918-5921 which + // catches every callee shape that isn't a bare-IDENT module + // fn or N_DOT module-qualified call. Pre-fix wwstage emitted + // `CALL (SB)` (empty symbol) for the N_UN-callee shape — the + // IDENT/DOT name-emit branches missed and isfnptrcall stayed + // false. + if (callee.kind != nkind.N_IDENT + && callee.kind != nkind.N_DOT) { + isfnptrcall = true; + }; if (callee.kind == nkind.N_DOT) { let base: *node = callee.lhs; let fld: str = callee.str;