wcc: module-imported array indexed-load via cg_dotbase_addr (#128b)

Fix segfault-class memory corruption on `module.array[i]` indexed-read
where both stages emitted MOVQ-not-LEAQ on the module-qualified base
plus wrong stride. Extends the #135 cg_dotbase_addr/dotbaseaddr helper
to handle the SK_USE module-ident-base case: when bt is NULL/ty_err
and let_islet(base.str) resolves to TY_ARRAY, emit LEAQ base(SB),dst
instead of MOVQ. Wwstage parallel via letvartnode/N_TARRAY check.
Stride fix via let_var_type fallback in cgindex when n.lhs.kind==N_DOT.

Use-site fix per #135 precedent (Option B); preserves cgdot's MOVQ
semantics for the whole-array-assign defensive case (zero current
consumers). Test 915 carries 3 module-u16 indexed-read rows
(strconv.left_shift_table[0/2/4]) + 2 local-array controls; the
strconv.left_shift_table[2]:u32 probe segfaulted (exit 139) pre-fix
and exits cleanly post-fix. Broader width-variation rows (u8/u32/i32
module-imported) deferred as informational enhancement. Test 915
skips its inline cs==ww .s cmp on needs_import rows (line 217-222)
since `ww build` only drives cstage; reviewer externally verified
byte-id on /tmp/k128probe.combined.ww (driver-expanded form, no
imports). Future enhancement: 915 could read the driver-emitted
combined.ww and add a cmp leg there.

Bootstrap NEUTRAL (zero current module.array[i] consumers; strconv
decimal.ww uses IDENT-base from within package). 178/178 incl.
990-997 + combined_ww_fresh green. Sibling bugs #137 (chained N_DOT)
/ #141 (variadic-gather esz==2) / #142 (wwstage primsize-on-alias)
properly deferred to backlog.
This commit is contained in:
2026-05-27 03:07:49 +09:00
parent 7230f3aa61
commit 1de1b9a8a9
6 changed files with 372 additions and 3 deletions

View File

@@ -15378,8 +15378,26 @@ fn dotbaseaddr(c: *cgen, base: *node, dstreg: str) bool = {
let inner: *node = base.lhs;
if (inner == nil) { return false; };
if (inner.kind != nkind.N_IDENT) { return false; };
// #128b: module-qualified `mod.arr` where arr is an imported
// top-level `let X: [N]T`. The checker leaves SK_USE module-
// idents without a localfindnode entry; detect via letvartnode
// resolving to N_TARRAY and emit LEAQ X(SB). Without this, the
// cgindex fallback's cgexpr(base) auto-MOVQs the symbol's first
// 8 bytes as if it were a pointer-var — wrong shape (cstage
// sister fix in cg_dotbase_addr).
let lc: *local = localfindnode(c, inner.str);
if (lc == nil) { return false; };
if (lc == nil) {
let gt: *node = letvartnode(c, base.str);
if (gt != nil && gt.kind == nkind.N_TARRAY) {
emitline("\tLEAQ\t");
emitsymname(c, base.str);
emitline("(SB), ");
emitline(dstreg);
emitline("\n");
return true;
};
return false;
};
let bu: *tinfo = inner.type_: *tinfo;
for (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
if (bu == nil) { return false; };

View File

@@ -759,8 +759,26 @@ fn dotbaseaddr(c: *cgen, base: *node, dstreg: str) bool = {
let inner: *node = base.lhs;
if (inner == nil) { return false; };
if (inner.kind != nkind.N_IDENT) { return false; };
// #128b: module-qualified `mod.arr` where arr is an imported
// top-level `let X: [N]T`. The checker leaves SK_USE module-
// idents without a localfindnode entry; detect via letvartnode
// resolving to N_TARRAY and emit LEAQ X(SB). Without this, the
// cgindex fallback's cgexpr(base) auto-MOVQs the symbol's first
// 8 bytes as if it were a pointer-var — wrong shape (cstage
// sister fix in cg_dotbase_addr).
let lc: *local = localfindnode(c, inner.str);
if (lc == nil) { return false; };
if (lc == nil) {
let gt: *node = letvartnode(c, base.str);
if (gt != nil && gt.kind == nkind.N_TARRAY) {
emitline("\tLEAQ\t");
emitsymname(c, base.str);
emitline("(SB), ");
emitline(dstreg);
emitline("\n");
return true;
};
return false;
};
let bu: *tinfo = inner.type_: *tinfo;
for (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
if (bu == nil) { return false; };

View File

@@ -15378,8 +15378,26 @@ fn dotbaseaddr(c: *cgen, base: *node, dstreg: str) bool = {
let inner: *node = base.lhs;
if (inner == nil) { return false; };
if (inner.kind != nkind.N_IDENT) { return false; };
// #128b: module-qualified `mod.arr` where arr is an imported
// top-level `let X: [N]T`. The checker leaves SK_USE module-
// idents without a localfindnode entry; detect via letvartnode
// resolving to N_TARRAY and emit LEAQ X(SB). Without this, the
// cgindex fallback's cgexpr(base) auto-MOVQs the symbol's first
// 8 bytes as if it were a pointer-var — wrong shape (cstage
// sister fix in cg_dotbase_addr).
let lc: *local = localfindnode(c, inner.str);
if (lc == nil) { return false; };
if (lc == nil) {
let gt: *node = letvartnode(c, base.str);
if (gt != nil && gt.kind == nkind.N_TARRAY) {
emitline("\tLEAQ\t");
emitsymname(c, base.str);
emitline("(SB), ");
emitline(dstreg);
emitline("\n");
return true;
};
return false;
};
let bu: *tinfo = inner.type_: *tinfo;
for (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
if (bu == nil) { return false; };