selfhost+test: route chained N_INDEX outer element size through indexvaluetnode (#24)
Wwstage cgindex's base-inspection (cgenexpr.ww) only computed esz/
signed_elem when base.kind == N_IDENT or N_DOT. For a chained
`names[i][k]` (names: **u8) the outer N_INDEX has base.kind ==
N_INDEX; esz fell through to the default 8 so the outer load
emitted `MOVQ (AX), AX` over a 1-byte u8 plus a stray
`MOVQ $8, CX; IMULQ CX, AX` scaling on the outer index that cstage
doesn't emit. Wrong-width-narrow-load: the byte was read as 8 bytes
(reaching into adjacent memory) and the outer offset multiplied by
sizeof *u8 instead of sizeof u8.
Cstage walks n->lhs->type directly via the typed AST
(cmd/w6c/cgen.c idx_eff → eff->sub->size at N_INDEX). Wwstage
needed the parallel via indexvaluetnode — return the value-type
of an N_INDEX expression by stripping one element layer off base's
type, recursing for chained inner. cgindex's else-if chain now
adds the N_INDEX arm: call indexvaluetnode + elemsizeofc/
elemissignedc.
Class A wwstage cgen UNDER. Surfaced first time the codebase
exercised the **T[i][k] shape — through expanddir in
selfhost/cmd/ww/main.ww (post-#22 dir-enum, commit 9e0816e). The
workaround there split names[i][k] into `let nm: *u8 = names[i];
nm[k]` to route through the bare-pointer index path. Retired in
this commit: expanddir uses the natural chained form since the
read path is now byte-identical across stages.
Bundling justification (rule 11): the workaround retirement is
the in-tree verification this fix works — without retiring,
neither bootstrap byte-id nor 995_self_rebuild exercises the
chained read shape. Test 739_chained_index pins cstage-byte-
identical asm for **u8 (MOVZBQ load, 1 inner-stride-8 IMULQ pair,
no outer scale) + **i32 (MOVSXD load, inner $8 + outer $4 IMULQ
pairs).
Sister latents filed (no in-tree consumer, no probe):
Task #27 — cgassign chained-write N_INDEX: write path
`names[i][k] = v` for **u8 has the same dispatch gap. Selfhost +
lib grep is empty.
New latent (filed during review) — cgindex N_DOT base on chained
index: `obj.mat[i][k]` over a struct-field base falls back to
esz=8. indexvaluetnode currently handles N_IDENT + N_INDEX bases
only.
113/113 ok. ww2 == ww3 == ww4 byte-id.
This commit is contained in:
@@ -8202,6 +8202,32 @@ fn elemsizeofc(c: *cgen, t: *node) i32 = {
|
||||
return slotsize(c, elem);
|
||||
};
|
||||
|
||||
// indexvaluetnode — type node of the value produced by an N_INDEX
|
||||
// expression. Walks base's type and returns its element. Recurses
|
||||
// through chained N_INDEX so `names[i][k]` (names: **u8) resolves
|
||||
// the outer base type to *u8 (the post-inner-index value type), so
|
||||
// cgindex can compute the outer element size honestly. Mirrors
|
||||
// cstage's `n->lhs->type` via typed-AST (cmd/w6c/cgen.c idx_eff).
|
||||
fn indexvaluetnode(c: *cgen, n: *node) *node = {
|
||||
if (n == nil) { return nil; };
|
||||
if (n.kind != nkind.N_INDEX) { return nil; };
|
||||
let base: *node = n.lhs;
|
||||
if (base == nil) { return nil; };
|
||||
let bt: *node = nil;
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, base.str);
|
||||
if (lc != nil) { bt = lc.tnode; }
|
||||
else { bt = letvartnode(c, base.str); };
|
||||
};
|
||||
if (base.kind == nkind.N_INDEX) { bt = indexvaluetnode(c, base); };
|
||||
if (bt == nil) { return nil; };
|
||||
let k: nkind = bt.kind;
|
||||
if (k == nkind.N_TPTR) { return bt.lhs; };
|
||||
if (k == nkind.N_TSLICE) { return bt.lhs; };
|
||||
if (k == nkind.N_TARRAY) { return bt.lhs; };
|
||||
return nil;
|
||||
};
|
||||
|
||||
// nodeisunsigned — best-effort cgen-time inference from the AST. We
|
||||
// don't have a typed AST yet, so we walk surface nodes:
|
||||
// nkind.N_INTLIT — never marked unsigned (no tsuffix plumbing yet)
|
||||
@@ -11378,7 +11404,13 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
};
|
||||
} else { if (base.kind == nkind.N_DOT) {
|
||||
esz = indexbaseesz(c, base);
|
||||
};};
|
||||
} else { if (base.kind == nkind.N_INDEX) {
|
||||
let bt: *node = indexvaluetnode(c, base);
|
||||
if (bt != nil) {
|
||||
esz = elemsizeofc(c, bt);
|
||||
signed_elem = elemissignedc(c, bt);
|
||||
};
|
||||
};};};
|
||||
};
|
||||
// Tagged-union element: load slot words into (AX=tag, DX=val0,
|
||||
// CX=val1) matching the tagged-return ABI so call-arg / let /
|
||||
|
||||
@@ -680,7 +680,13 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
};
|
||||
} else { if (base.kind == nkind.N_DOT) {
|
||||
esz = indexbaseesz(c, base);
|
||||
};};
|
||||
} else { if (base.kind == nkind.N_INDEX) {
|
||||
let bt: *node = indexvaluetnode(c, base);
|
||||
if (bt != nil) {
|
||||
esz = elemsizeofc(c, bt);
|
||||
signed_elem = elemissignedc(c, bt);
|
||||
};
|
||||
};};};
|
||||
};
|
||||
// Tagged-union element: load slot words into (AX=tag, DX=val0,
|
||||
// CX=val1) matching the tagged-return ABI so call-arg / let /
|
||||
|
||||
@@ -1241,6 +1241,32 @@ fn elemsizeofc(c: *cgen, t: *node) i32 = {
|
||||
return slotsize(c, elem);
|
||||
};
|
||||
|
||||
// indexvaluetnode — type node of the value produced by an N_INDEX
|
||||
// expression. Walks base's type and returns its element. Recurses
|
||||
// through chained N_INDEX so `names[i][k]` (names: **u8) resolves
|
||||
// the outer base type to *u8 (the post-inner-index value type), so
|
||||
// cgindex can compute the outer element size honestly. Mirrors
|
||||
// cstage's `n->lhs->type` via typed-AST (cmd/w6c/cgen.c idx_eff).
|
||||
fn indexvaluetnode(c: *cgen, n: *node) *node = {
|
||||
if (n == nil) { return nil; };
|
||||
if (n.kind != nkind.N_INDEX) { return nil; };
|
||||
let base: *node = n.lhs;
|
||||
if (base == nil) { return nil; };
|
||||
let bt: *node = nil;
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, base.str);
|
||||
if (lc != nil) { bt = lc.tnode; }
|
||||
else { bt = letvartnode(c, base.str); };
|
||||
};
|
||||
if (base.kind == nkind.N_INDEX) { bt = indexvaluetnode(c, base); };
|
||||
if (bt == nil) { return nil; };
|
||||
let k: nkind = bt.kind;
|
||||
if (k == nkind.N_TPTR) { return bt.lhs; };
|
||||
if (k == nkind.N_TSLICE) { return bt.lhs; };
|
||||
if (k == nkind.N_TARRAY) { return bt.lhs; };
|
||||
return nil;
|
||||
};
|
||||
|
||||
// nodeisunsigned — best-effort cgen-time inference from the AST. We
|
||||
// don't have a typed AST yet, so we walk surface nodes:
|
||||
// nkind.N_INTLIT — never marked unsigned (no tsuffix plumbing yet)
|
||||
|
||||
@@ -1523,19 +1523,13 @@ fn expanddir(c: *expctx, dirpath: *u8) void = {
|
||||
let dirpkg: *u8 = nil;
|
||||
let i: i32 = 0;
|
||||
for (i < n) {
|
||||
// Two-step deref+index to avoid wwstage chained `names[i][k]`
|
||||
// cgen UNDER (task #24 — wwstage cgen chained-index inner
|
||||
// element size on **T). Wwstage treats inner element as 8B
|
||||
// (sizeof *u8) instead of 1B (sizeof u8); cstage handles
|
||||
// via typed-AST natively. Retire once the wwstage fix lands.
|
||||
let nm: *u8 = names[i];
|
||||
let nlen: u64 = cstrlen(nm);
|
||||
let nlen: u64 = cstrlen(names[i]);
|
||||
let fp: *u8 = amalloc(c.a, dlen + 1u64 + nlen + 1u64): *u8;
|
||||
let k: u64 = 0u64;
|
||||
for (k < dlen) { fp[k] = dirpath[k]; k += 1u64; };
|
||||
fp[dlen] = 47u8; // '/'
|
||||
k = 0u64;
|
||||
for (k < nlen) { fp[dlen + 1u64 + k] = nm[k]; k += 1u64; };
|
||||
for (k < nlen) { fp[dlen + 1u64 + k] = names[i][k]; k += 1u64; };
|
||||
fp[dlen + 1u64 + nlen] = 0u8;
|
||||
let pkg: *u8 = peekpackage(c.a, fp);
|
||||
if (pkg != nil) {
|
||||
|
||||
@@ -663,19 +663,13 @@ fn expanddir(c: *expctx, dirpath: *u8) void = {
|
||||
let dirpkg: *u8 = nil;
|
||||
let i: i32 = 0;
|
||||
for (i < n) {
|
||||
// Two-step deref+index to avoid wwstage chained `names[i][k]`
|
||||
// cgen UNDER (task #24 — wwstage cgen chained-index inner
|
||||
// element size on **T). Wwstage treats inner element as 8B
|
||||
// (sizeof *u8) instead of 1B (sizeof u8); cstage handles
|
||||
// via typed-AST natively. Retire once the wwstage fix lands.
|
||||
let nm: *u8 = names[i];
|
||||
let nlen: u64 = cstrlen(nm);
|
||||
let nlen: u64 = cstrlen(names[i]);
|
||||
let fp: *u8 = amalloc(c.a, dlen + 1u64 + nlen + 1u64): *u8;
|
||||
let k: u64 = 0u64;
|
||||
for (k < dlen) { fp[k] = dirpath[k]; k += 1u64; };
|
||||
fp[dlen] = 47u8; // '/'
|
||||
k = 0u64;
|
||||
for (k < nlen) { fp[dlen + 1u64 + k] = nm[k]; k += 1u64; };
|
||||
for (k < nlen) { fp[dlen + 1u64 + k] = names[i][k]; k += 1u64; };
|
||||
fp[dlen + 1u64 + nlen] = 0u8;
|
||||
let pkg: *u8 = peekpackage(c.a, fp);
|
||||
if (pkg != nil) {
|
||||
|
||||
@@ -8202,6 +8202,32 @@ fn elemsizeofc(c: *cgen, t: *node) i32 = {
|
||||
return slotsize(c, elem);
|
||||
};
|
||||
|
||||
// indexvaluetnode — type node of the value produced by an N_INDEX
|
||||
// expression. Walks base's type and returns its element. Recurses
|
||||
// through chained N_INDEX so `names[i][k]` (names: **u8) resolves
|
||||
// the outer base type to *u8 (the post-inner-index value type), so
|
||||
// cgindex can compute the outer element size honestly. Mirrors
|
||||
// cstage's `n->lhs->type` via typed-AST (cmd/w6c/cgen.c idx_eff).
|
||||
fn indexvaluetnode(c: *cgen, n: *node) *node = {
|
||||
if (n == nil) { return nil; };
|
||||
if (n.kind != nkind.N_INDEX) { return nil; };
|
||||
let base: *node = n.lhs;
|
||||
if (base == nil) { return nil; };
|
||||
let bt: *node = nil;
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, base.str);
|
||||
if (lc != nil) { bt = lc.tnode; }
|
||||
else { bt = letvartnode(c, base.str); };
|
||||
};
|
||||
if (base.kind == nkind.N_INDEX) { bt = indexvaluetnode(c, base); };
|
||||
if (bt == nil) { return nil; };
|
||||
let k: nkind = bt.kind;
|
||||
if (k == nkind.N_TPTR) { return bt.lhs; };
|
||||
if (k == nkind.N_TSLICE) { return bt.lhs; };
|
||||
if (k == nkind.N_TARRAY) { return bt.lhs; };
|
||||
return nil;
|
||||
};
|
||||
|
||||
// nodeisunsigned — best-effort cgen-time inference from the AST. We
|
||||
// don't have a typed AST yet, so we walk surface nodes:
|
||||
// nkind.N_INTLIT — never marked unsigned (no tsuffix plumbing yet)
|
||||
@@ -11378,7 +11404,13 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
};
|
||||
} else { if (base.kind == nkind.N_DOT) {
|
||||
esz = indexbaseesz(c, base);
|
||||
};};
|
||||
} else { if (base.kind == nkind.N_INDEX) {
|
||||
let bt: *node = indexvaluetnode(c, base);
|
||||
if (bt != nil) {
|
||||
esz = elemsizeofc(c, bt);
|
||||
signed_elem = elemissignedc(c, bt);
|
||||
};
|
||||
};};};
|
||||
};
|
||||
// Tagged-union element: load slot words into (AX=tag, DX=val0,
|
||||
// CX=val1) matching the tagged-return ABI so call-arg / let /
|
||||
|
||||
Reference in New Issue
Block a user