From 4acab6e0bbff299b3ddc3246a2b1b3f8fdf9729d Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Tue, 26 May 2026 20:39:03 +0900 Subject: [PATCH] wcc: cgen N_CALL "len" TY_ARRAY/TY_SLICE/TY_STR intercept (#131) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wwstage cgenexpr cgcall now intercepts the N_IDENT-callee `len` like cstage cgen.c:4283-4297 — TY_ARRAY folds to MOVQ $alen,AX at compile time, TY_SLICE/TY_STR + N_IDENT loads the .len slot from local header, fallback to cgexpr. Rule-9 Hare-fidelity (Hare/Rust/Go compile-time-fold len(fixedarray)) + rule-10 align wwstage UP to cstage. Byte-id-neutral at master (bootstrap has no current len(fixedarray) call-form uses); prereq for fold-3 decimal.ha port (`len(d.digits)` at decimal.ha:66/ 77/86/124). --- selfhost/cmd/w6c/main.combined.ww | 46 ++++++++++++++++++++++++++++ selfhost/cmd/wcc/cgenexpr.ww | 46 ++++++++++++++++++++++++++++ selfhost/cmd/wwdump/main.combined.ww | 46 ++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+) diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 988414ac..c98ab225 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -17714,6 +17714,52 @@ fn cgcall(c: *cgen, n: *node) void = { }; }; }; + // `len(x)` Hare builtin — mirror cmd/w6c/cgen.c:4283-4297. + // Required for byte-id when compiler-imported lib code uses + // len(fixedarray) (e.g. lib/strconv/decimal.ha's `len(d.digits)` + // over the [800]u8 field). Without this intercept wwstage falls + // through to a regular CALL len(SB) while cstage folds to + // `MOVQ $alen, AX` — rule-10 byte-id break (#131). + // + // Argument-type-driven branches: + // TY_SLICE / TY_STR (+ N_IDENT operand) → load .len at BP+off+8. + // TY_ARRAY → fold `MOVQ $alen, AX`. + // else → evaluate operand (cstage's pseudo-.len fallback — + // unlikely to fire on Hare-shaped sources). + if (streq(callee.str, "len")) { + if (n.list != nil) { + let a: *node = n.list; + let at: *tinfo = a.type_: *tinfo; + let u: *tinfo = at; + for (u != nil && u.kind == tykind.TY_NAMED) { + u = u.under; + }; + if (u != nil) { + if ((u.kind == tykind.TY_SLICE + || u.kind == tykind.TY_STR) + && a.kind == nkind.N_IDENT) { + let lc: *local = localfindnode(c, a.str); + if (lc != nil) { + emitline("\tMOVQ\t"); + emitoff((lc.off + 8): i64); + emitline("(BP), AX\n"); + return; + }; + }; + if (u.kind == tykind.TY_ARRAY) { + emitline("\tMOVQ\t$"); + emitint(u.alen: i64); + emitline(", AX\n"); + return; + }; + }; + // Fallback: evaluate the argument and let AX carry + // whatever the value-load shape yields. Mirrors + // cstage's `cgexpr(c, a, locals)` fallthrough. + cgexpr(c, a); + return; + }; + }; }; }; diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index 0d77fb5e..c4f55d40 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -3404,6 +3404,52 @@ fn cgcall(c: *cgen, n: *node) void = { }; }; }; + // `len(x)` Hare builtin — mirror cmd/w6c/cgen.c:4283-4297. + // Required for byte-id when compiler-imported lib code uses + // len(fixedarray) (e.g. lib/strconv/decimal.ha's `len(d.digits)` + // over the [800]u8 field). Without this intercept wwstage falls + // through to a regular CALL len(SB) while cstage folds to + // `MOVQ $alen, AX` — rule-10 byte-id break (#131). + // + // Argument-type-driven branches: + // TY_SLICE / TY_STR (+ N_IDENT operand) → load .len at BP+off+8. + // TY_ARRAY → fold `MOVQ $alen, AX`. + // else → evaluate operand (cstage's pseudo-.len fallback — + // unlikely to fire on Hare-shaped sources). + if (streq(callee.str, "len")) { + if (n.list != nil) { + let a: *node = n.list; + let at: *tinfo = a.type_: *tinfo; + let u: *tinfo = at; + for (u != nil && u.kind == tykind.TY_NAMED) { + u = u.under; + }; + if (u != nil) { + if ((u.kind == tykind.TY_SLICE + || u.kind == tykind.TY_STR) + && a.kind == nkind.N_IDENT) { + let lc: *local = localfindnode(c, a.str); + if (lc != nil) { + emitline("\tMOVQ\t"); + emitoff((lc.off + 8): i64); + emitline("(BP), AX\n"); + return; + }; + }; + if (u.kind == tykind.TY_ARRAY) { + emitline("\tMOVQ\t$"); + emitint(u.alen: i64); + emitline(", AX\n"); + return; + }; + }; + // Fallback: evaluate the argument and let AX carry + // whatever the value-load shape yields. Mirrors + // cstage's `cgexpr(c, a, locals)` fallthrough. + cgexpr(c, a); + return; + }; + }; }; }; diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 0ccd36a2..42f9e4ac 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -17714,6 +17714,52 @@ fn cgcall(c: *cgen, n: *node) void = { }; }; }; + // `len(x)` Hare builtin — mirror cmd/w6c/cgen.c:4283-4297. + // Required for byte-id when compiler-imported lib code uses + // len(fixedarray) (e.g. lib/strconv/decimal.ha's `len(d.digits)` + // over the [800]u8 field). Without this intercept wwstage falls + // through to a regular CALL len(SB) while cstage folds to + // `MOVQ $alen, AX` — rule-10 byte-id break (#131). + // + // Argument-type-driven branches: + // TY_SLICE / TY_STR (+ N_IDENT operand) → load .len at BP+off+8. + // TY_ARRAY → fold `MOVQ $alen, AX`. + // else → evaluate operand (cstage's pseudo-.len fallback — + // unlikely to fire on Hare-shaped sources). + if (streq(callee.str, "len")) { + if (n.list != nil) { + let a: *node = n.list; + let at: *tinfo = a.type_: *tinfo; + let u: *tinfo = at; + for (u != nil && u.kind == tykind.TY_NAMED) { + u = u.under; + }; + if (u != nil) { + if ((u.kind == tykind.TY_SLICE + || u.kind == tykind.TY_STR) + && a.kind == nkind.N_IDENT) { + let lc: *local = localfindnode(c, a.str); + if (lc != nil) { + emitline("\tMOVQ\t"); + emitoff((lc.off + 8): i64); + emitline("(BP), AX\n"); + return; + }; + }; + if (u.kind == tykind.TY_ARRAY) { + emitline("\tMOVQ\t$"); + emitint(u.alen: i64); + emitline(", AX\n"); + return; + }; + }; + // Fallback: evaluate the argument and let AX carry + // whatever the value-load shape yields. Mirrors + // cstage's `cgexpr(c, a, locals)` fallthrough. + cgexpr(c, a); + return; + }; + }; }; };