wcc/cgen: GAP-A.ptr global-array base — LEAQ name(SB) not (BP) (#11, both stages)

A global fixed array's .ptr (= &A[0]) must take the SB base, but cstage
emitted frame-relative LEAQ off(BP) for BOTH let- and def-global arrays
-> *A.ptr read frame garbage (0 instead of the element). cstage-SILENT;
wwstage def-global was a loud link-error. The .ptr read arm now gates
off==0 && (let_islet || def_isarraydef) -> LEAQ name(SB), reusing the
def-array index base predicate (cgen.c:4367, the #94/#231/#48 class).
Locals (off != 0) stay BP-relative -- the 14 toolchain backing-ptr sites
unaffected.

wwstage let-global was already correct; this adds the missing def-global
arm (cgenexpr.ww), converging cstage/wwstage byte-identical across all
three flavors (local / let-global / def-global) and closing a latent
cstage-only let-global cs!=ww divergence.

Byte-id 990-997 8/8 (corpus has no global .ptr); w6c/w6c_ww binaries move
(cgen changed). test/wcc/818 table-driven, build+run+byte-id per flavor.
This commit is contained in:
2026-06-08 17:39:45 +09:00
parent 1c87881bda
commit 267e81b89e
6 changed files with 300 additions and 10 deletions

View File

@@ -24981,11 +24981,24 @@ fn cgdot(c: *cgen, n: *node) void = {
// SB fallback → MOVQ len(SB) (w6l: undefined 'len'). cstage cgen.c
// emits MOVQ $alen here. defvartnode is the def-side mirror of
// letvartnode (returns dtnode = the N_TARRAY whose .rhs length child
// is #11-stamped). `.ptr`/`.cap` are NOT mirrored: cstage's def .ptr
// is itself buggy (LEAQ (BP), task GAP-A.ptr) and arrays have no
// .cap (cstage rejects, task GAP-A.cap).
// is #11-stamped). `.ptr` mirrors the let-global arm above: the
// array's backing pointer ≡ &A[0] = LEAQ name(SB) (drew #13,
// .ai/drew-gapa-ptr-ruling.md; GAP-A.ptr now fixed cstage-side too,
// so both stages byte-id). `.cap` stays unmirrored — arrays have no
// .cap (both checkers reject, task GAP-A.cap).
if (lhs != nil) {
if (lhs.kind == nkind.N_IDENT) {
if (streq(fld, "ptr")) {
let dtn: *node = defvartnode(c, lhs.str);
if (dtn != nil) {
if (dtn.kind == nkind.N_TARRAY) {
emitline("\tLEAQ\t");
emitsymname(c, lhs.str);
emitline("(SB), AX\n");
return;
};
};
};
if (streq(fld, "len")) {
let dtn: *node = defvartnode(c, lhs.str);
if (dtn != nil) {

View File

@@ -3461,11 +3461,24 @@ fn cgdot(c: *cgen, n: *node) void = {
// SB fallback → MOVQ len(SB) (w6l: undefined 'len'). cstage cgen.c
// emits MOVQ $alen here. defvartnode is the def-side mirror of
// letvartnode (returns dtnode = the N_TARRAY whose .rhs length child
// is #11-stamped). `.ptr`/`.cap` are NOT mirrored: cstage's def .ptr
// is itself buggy (LEAQ (BP), task GAP-A.ptr) and arrays have no
// .cap (cstage rejects, task GAP-A.cap).
// is #11-stamped). `.ptr` mirrors the let-global arm above: the
// array's backing pointer ≡ &A[0] = LEAQ name(SB) (drew #13,
// .ai/drew-gapa-ptr-ruling.md; GAP-A.ptr now fixed cstage-side too,
// so both stages byte-id). `.cap` stays unmirrored — arrays have no
// .cap (both checkers reject, task GAP-A.cap).
if (lhs != nil) {
if (lhs.kind == nkind.N_IDENT) {
if (streq(fld, "ptr")) {
let dtn: *node = defvartnode(c, lhs.str);
if (dtn != nil) {
if (dtn.kind == nkind.N_TARRAY) {
emitline("\tLEAQ\t");
emitsymname(c, lhs.str);
emitline("(SB), AX\n");
return;
};
};
};
if (streq(fld, "len")) {
let dtn: *node = defvartnode(c, lhs.str);
if (dtn != nil) {

View File

@@ -24981,11 +24981,24 @@ fn cgdot(c: *cgen, n: *node) void = {
// SB fallback → MOVQ len(SB) (w6l: undefined 'len'). cstage cgen.c
// emits MOVQ $alen here. defvartnode is the def-side mirror of
// letvartnode (returns dtnode = the N_TARRAY whose .rhs length child
// is #11-stamped). `.ptr`/`.cap` are NOT mirrored: cstage's def .ptr
// is itself buggy (LEAQ (BP), task GAP-A.ptr) and arrays have no
// .cap (cstage rejects, task GAP-A.cap).
// is #11-stamped). `.ptr` mirrors the let-global arm above: the
// array's backing pointer ≡ &A[0] = LEAQ name(SB) (drew #13,
// .ai/drew-gapa-ptr-ruling.md; GAP-A.ptr now fixed cstage-side too,
// so both stages byte-id). `.cap` stays unmirrored — arrays have no
// .cap (both checkers reject, task GAP-A.cap).
if (lhs != nil) {
if (lhs.kind == nkind.N_IDENT) {
if (streq(fld, "ptr")) {
let dtn: *node = defvartnode(c, lhs.str);
if (dtn != nil) {
if (dtn.kind == nkind.N_TARRAY) {
emitline("\tLEAQ\t");
emitsymname(c, lhs.str);
emitline("(SB), AX\n");
return;
};
};
};
if (streq(fld, "len")) {
let dtn: *node = defvartnode(c, lhs.str);
if (dtn != nil) {