cgen: type-key the struct field-layout receiver arms via stamped tinfo (#31 c1)
wwstage cgen resolved struct field LAYOUT (offset + field type) by bare-leaf
name (structlookupchain / structlookup / letvarstructinfo) at nine field
READ / addr-of / let-copy receiver arms whose base is a *struct pointer, a
value struct, or a module global. Under a cross-module same-leaf collision
(two modules each exporting a `pair`, 16B vs 24B) the bare-leaf lookup
first-matches the wrong-size struct -> the field is read / addressed / copied
at the wrong offset with the wrong width. cstage type-keys off the stamped
node.type_ (type_chase_named(base->type)->fields) and is correct; this aligns
wwstage UP to it (ww-only change).
Route the nine arms off the stamped receiver tinfo: R1/R2 *struct reads via a
new shared choke-point cgptrfieldloadtf (the tinfo twin of cgptrfieldload);
A1/A2/A3 addr-of and W3 scalar global-store via tichase(recv.type_)->fields;
C1/W4a copy/size via structabisizetn(tichase(.type_)). Mirrors #21 (5ae6e34);
the *struct arms peel the pointee with tichase(.type_).sub, the faithful twin
of cstage type_chase_named(bu->sub).
The global value-struct arms (R2/R3/A3/W3/W4a) are converted-for-construction:
a global struct's type is always explicitly qualified, so the bare leaf already
resolved correctly and they cannot be reddened -- byte-id (cs.s==ww.s) is their
net, not a value pin. Labelled so the absent reddening pin is explained, not
silent.
Commit 1 of a 2-commit arc (RULING R2 / Opt-2, .ai/ken-31-spec.md): closes the
field-LAYOUT read/copy/addr surface. Commit 2 converts the W1/W2/W5/W4b store
loops to a tinfo-native fill (cgstructlitfilltn + sretretsizetn) and closes the
in-loop nested sub-arms by construction. Part of the #224 name-keyed-cgen
cluster retirement.
Pin: test/wcc/797 value-asserts R1 ptr-read / C1 let-copy / A1/A2 addr, each
reddening under independent per-arm revert (a ratchet proves nothing for this
silent-capable class).
This commit is contained in:
@@ -2654,55 +2654,49 @@ fn cgletbody(c: *cgen, n: *syntax.node, off: i32) void = {
|
||||
// via the str-init tail) — silent partial copy. Mirrors
|
||||
// cstage cgen.c N_LET struct-ident branch (Task #32).
|
||||
if (rhs.kind == syntax.nkind.N_IDENT) {
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (tn != nil) {
|
||||
if (tn.kind == syntax.nkind.N_TNAME) { sname = tn.str; };
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let lsi: *structinfo = structlookup(c, sname);
|
||||
if (lsi != nil) {
|
||||
// memcpy run sizes on the maxalign-rounded ABI
|
||||
// size — cstage N_LET sets sz = lu->size
|
||||
// (cgen.c:7533, struct-IDENT branch :7869),
|
||||
// check.c:760 SSoT. structnaturalsize would
|
||||
// short struct{i64,i32} (natural 12, ABI 16)
|
||||
// to MOVQ+MOVL where cstage writes MOVQ+MOVQ.
|
||||
let lsz: i32 = structabisize(lsi);
|
||||
if (lsz > 8) {
|
||||
let lc: *local = localfindnode(c, rhs.str);
|
||||
if (lc != nil) {
|
||||
let soff: i32 = lc.off;
|
||||
let ki: i32 = 0;
|
||||
for (ki + 8 <= lsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((soff + ki): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + ki): i64);
|
||||
emitline("(BP)\n");
|
||||
ki += 8;
|
||||
};
|
||||
if (ki < lsz) {
|
||||
let tail: i32 = lsz - ki;
|
||||
let lop: str = "MOVQ";
|
||||
if (tail == 4) { lop = "MOVL"; }
|
||||
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitoff((soff + ki): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((off + ki): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
// #31: size the local-to-local struct COPY off the COPY
|
||||
// SOURCE's stamped tinfo (structabisizetn(rhs.type_)), NOT
|
||||
// structlookup(tn.str). On a cross-module same-leaf collision
|
||||
// the inferred-let's declared-type leaf (tn.str) mis-resolves
|
||||
// to a FOREIGN same-leaf struct -> the memcpy run was sized off
|
||||
// it (a 24B foreign over-reads a 16B source + over-writes the
|
||||
// dst slot -- silent OOB, cs!=ww). rhs.type_ is the correct
|
||||
// source struct; byte-id with structabisize on a resolving
|
||||
// lookup. memcpy run = maxalign-rounded ABI size = cstage N_LET
|
||||
// sz=lu->size (cgen.c:7533, struct-IDENT :7869, check.c:760 SSoT).
|
||||
let lsz: i32 = structabisizetn(rhs.type_: *syntax.tinfo);
|
||||
if (lsz > 8) {
|
||||
let lc: *local = localfindnode(c, rhs.str);
|
||||
if (lc != nil) {
|
||||
let soff: i32 = lc.off;
|
||||
let ki: i32 = 0;
|
||||
for (ki + 8 <= lsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((soff + ki): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + ki): i64);
|
||||
emitline("(BP)\n");
|
||||
ki += 8;
|
||||
};
|
||||
if (ki < lsz) {
|
||||
let tail: i32 = lsz - ki;
|
||||
let lop: str = "MOVQ";
|
||||
if (tail == 4) { lop = "MOVL"; }
|
||||
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitoff((soff + ki): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((off + ki): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user