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:
@@ -3403,6 +3403,59 @@ fn cgptrfieldload(c: *cgen, fi: *fieldinfo) void = {
|
||||
emitline(", AX\n");
|
||||
};
|
||||
|
||||
// cgptrfieldloadtf — tfield twin of cgptrfieldload (#31): load struct
|
||||
// field `tf` from a *struct base already in BX, resolving offset + field
|
||||
// type off the checker-STAMPED tfield (tf.offset/tf.type_) instead of the
|
||||
// name-keyed fieldinfo. The read-side choke-point shared by the R1 (local)
|
||||
// and R2 (global) *struct field-read arms; a cross-module same-leaf
|
||||
// collision that mis-resolved structlookupchain(inner) cannot reach a
|
||||
// stamped tinfo. Emission is byte-identical to cgptrfieldload (the #21 R0
|
||||
// substitution table): syntax.typeis*(tf.type_) replaces is*type(fi.tnode),
|
||||
// tichase(tf.type_).size replaces slotsize/fsz. BX is never a load target,
|
||||
// so the str/slice triple writes BX (the base) LAST.
|
||||
fn cgptrfieldloadtf(c: *cgen, tf: *syntax.tfield) void = {
|
||||
let foff: i32 = tf.offset: i32;
|
||||
let ftraw: *syntax.tinfo = tf.type_;
|
||||
if (syntax.typeistagged(ftraw)) {
|
||||
let ftc: *syntax.tinfo = tichase(ftraw);
|
||||
let tsz: i32 = 0;
|
||||
if (ftc != nil) { tsz = ftc.size: i32; };
|
||||
cgloadtaggedfield(c, "BX", foff, tsz, false);
|
||||
return;
|
||||
};
|
||||
if (syntax.typeisstr(ftraw) || syntax.typeisslice(ftraw)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(foff: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((foff + 16): i64, "BX");
|
||||
emitline(", CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((foff + 8): i64, "BX");
|
||||
emitline(", BX\n");
|
||||
return;
|
||||
};
|
||||
if (syntax.typeisfloat(ftraw)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (syntax.typeisf32(ftraw)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitdispreg(foff: i64, "BX");
|
||||
emitline(", X0\n");
|
||||
return;
|
||||
};
|
||||
let ftc: *syntax.tinfo = tichase(ftraw);
|
||||
let fsz: i32 = 0;
|
||||
if (ftc != nil) { fsz = ftc.size: i32; };
|
||||
let op: str = loadopsz(syntax.typeissigned(ftraw), fsz);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
emitdispreg(foff: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
};
|
||||
|
||||
// emitchainbase — load the chained-DOT root BASE into CX for the viacx
|
||||
// (global or `*T`-root) spine. Mirrors cstage cgen.c's uniform base
|
||||
// resolution: a global is reached via LEAQ name(SB),CX; a `*struct`
|
||||
@@ -3533,41 +3586,37 @@ fn cgdot(c: *cgen, n: *syntax.node) void = {
|
||||
let lkind: syntax.nkind = tn.kind;
|
||||
// Pointer-to-struct: deref then field load.
|
||||
if (lkind == syntax.nkind.N_TPTR) {
|
||||
let inner: *syntax.node = tn.lhs;
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (inner != nil) {
|
||||
if (inner.kind == syntax.nkind.N_TNAME) {
|
||||
sname = inner.str;
|
||||
};
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
// structlookupchain walks the alias chain on
|
||||
// a miss so `*tokenizer` where tokenizer is
|
||||
// a transitively-aliased struct still
|
||||
// resolves to the underlying fieldinfo (#22).
|
||||
let si: *structinfo = structlookupchain(c, inner);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
let fn_: str = fi.fname;
|
||||
if (syntax.streq(fn_, fld)) {
|
||||
// Stage the *struct base in BX, then field
|
||||
// load via cgptrfieldload (shared with the
|
||||
// #15 global-ptr arm below). BX isn't a load
|
||||
// target (AX/DX/CX/R8/X0), so order is
|
||||
// harmless. Mirrors cstage cgen.c N_DOT
|
||||
// *struct field arm.
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
cgptrfieldload(c, fi);
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
// #31: resolve the *struct field OFFSET + type off the
|
||||
// checker-STAMPED receiver tinfo (tichase(dotlhs.type_)
|
||||
// → .sub pointee), NOT the name-keyed
|
||||
// structlookupchain(inner). Under a cross-module same-
|
||||
// leaf collision inner is a bare leaf that mis-resolves
|
||||
// to a FOREIGN same-leaf struct → wrong offset/load-op
|
||||
// (the inferred-local READ row, ww=28). The stamped tinfo
|
||||
// carries the right layout; mirror cstage
|
||||
// type_chase_named(bu->sub)->fields (cgen.c:506-512) +
|
||||
// the #21 R0 template. Non-struct pointees (*str/*slice/
|
||||
// *[N]T) are not TY_STRUCT → fall through to the pseudo-
|
||||
// field arms below (guard, never a bare-leaf fallback).
|
||||
// Read choke-point cgptrfieldloadtf shared with the R2
|
||||
// global arm. Stage the *struct base in BX (not a load
|
||||
// target), then field-load.
|
||||
let sti: *syntax.tinfo = nil;
|
||||
if (dotlhs != nil) { sti = tichase(dotlhs.type_: *syntax.tinfo); };
|
||||
if (sti != nil && sti.kind == syntax.tykind.TY_PTR) { sti = tichase(sti.sub); };
|
||||
if (sti != nil) { if (sti.kind == syntax.tykind.TY_STRUCT) {
|
||||
let tf: *syntax.tfield = sti.fields;
|
||||
for (tf != nil) {
|
||||
if (syntax.streq(tf.name, fld)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
cgptrfieldloadtf(c, tf);
|
||||
return;
|
||||
};
|
||||
tf = tf.tnext;
|
||||
};
|
||||
};
|
||||
}; };
|
||||
};
|
||||
// Direct struct local: field load at off+foff.
|
||||
if (lkind == syntax.nkind.N_TNAME) {
|
||||
@@ -3855,29 +3904,29 @@ fn cgdot(c: *cgen, n: *syntax.node) void = {
|
||||
if (gtn != nil) {
|
||||
if (gtn.kind == syntax.nkind.N_TPTR) {
|
||||
let inner: *syntax.node = gtn.lhs;
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (inner != nil) {
|
||||
if (inner.kind == syntax.nkind.N_TNAME) {
|
||||
sname = inner.str;
|
||||
};
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookupchain(c, inner);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (syntax.streq(fi.fname, fld)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, gnm);
|
||||
emitline("(SB), BX\n");
|
||||
cgptrfieldload(c, fi);
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
// #31: stamped-tinfo *struct field resolution
|
||||
// (tichase(dotlhs.type_)->.sub), the read choke-point
|
||||
// twin of the R1 local arm -- see cgptrfieldloadtf. A
|
||||
// global *struct decl is qualified (let gp: *m1.pair)
|
||||
// -> non-reddenable; converted for close-by-construction
|
||||
// (byte-id with the name-keyed path). Non-struct
|
||||
// pointees fall through to the str/slice arm below.
|
||||
let sti: *syntax.tinfo = nil;
|
||||
if (dotlhs != nil) { sti = tichase(dotlhs.type_: *syntax.tinfo); };
|
||||
if (sti != nil && sti.kind == syntax.tykind.TY_PTR) { sti = tichase(sti.sub); };
|
||||
if (sti != nil) { if (sti.kind == syntax.tykind.TY_STRUCT) {
|
||||
let tf: *syntax.tfield = sti.fields;
|
||||
for (tf != nil) {
|
||||
if (syntax.streq(tf.name, fld)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, gnm);
|
||||
emitline("(SB), BX\n");
|
||||
cgptrfieldloadtf(c, tf);
|
||||
return;
|
||||
};
|
||||
tf = tf.tnext;
|
||||
};
|
||||
};
|
||||
}; };
|
||||
// Pointer to str/slice (`*[]u8`, `*str`): deref
|
||||
// name(SB), then load at delta within the header.
|
||||
let delta: i32 = -1;
|
||||
@@ -4141,65 +4190,64 @@ fn cgdot(c: *cgen, n: *syntax.node) void = {
|
||||
// from the wrong offset).
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == syntax.nkind.N_IDENT) {
|
||||
let si: *structinfo = letvarstructinfo(c, lhs.str);
|
||||
if (si == nil) { si = defvarstructinfo(c, lhs.str); };
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (syntax.streq(fi.fname, fld)) {
|
||||
// #31: resolve the global value-struct field OFFSET + type
|
||||
// off the checker-STAMPED ident tinfo (tichase(lhs.type_)),
|
||||
// NOT name-keyed letvarstructinfo/defvarstructinfo. A global
|
||||
// decl is always qualified (let g: m1.pair) -> non-reddenable;
|
||||
// converted for close-by-construction (byte-id). Covers both
|
||||
// let- and def-struct globals (both stamp lhs.type_). Mirrors
|
||||
// cstage type-keyed N_DOT global arm + the #21 R0 template.
|
||||
let sti: *syntax.tinfo = tichase(lhs.type_: *syntax.tinfo);
|
||||
if (sti != nil) { if (sti.kind == syntax.tykind.TY_STRUCT) {
|
||||
let tf: *syntax.tfield = sti.fields;
|
||||
for (tf != nil) {
|
||||
if (syntax.streq(tf.name, fld)) {
|
||||
let foff: i32 = tf.offset: i32;
|
||||
let ftraw: *syntax.tinfo = tf.type_;
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, lhs.str);
|
||||
emitline("(SB), CX\n");
|
||||
// tagged-union field: load via the tagged-
|
||||
// return ABI off CX. cgloadtaggedfield orders
|
||||
// the loads so CX (word1 target) is written
|
||||
// LAST — otherwise the base address would be
|
||||
// trashed before the +24/R8 (slice variant)
|
||||
// read could index off it. Pre-#28 fell
|
||||
// through to fieldloadop and dropped payload.
|
||||
if (istaggedtype(c, fi.tnode)) {
|
||||
let tsz: i32 = slotsize(c, fi.tnode);
|
||||
cgloadtaggedfield(c, "CX", fi.foff, tsz, true);
|
||||
if (syntax.typeistagged(ftraw)) {
|
||||
let ftc: *syntax.tinfo = tichase(ftraw);
|
||||
let tsz: i32 = 0;
|
||||
if (ftc != nil) { tsz = ftc.size: i32; };
|
||||
cgloadtaggedfield(c, "CX", foff, tsz, true);
|
||||
return;
|
||||
};
|
||||
// str IS []u8 — 3-word {ptr,len,cap}, the local
|
||||
// slice-field arm (BP) retargeted to the CX global
|
||||
// base. cap→CX LAST: CX is the base, so .ptr/.len
|
||||
// must read first. cstage folds local+global in one
|
||||
// base_reg arm; ww splits them, so this global arm
|
||||
// carries its own lift (filed divergence task).
|
||||
if (isstrtype(c, fi.tnode)) {
|
||||
if (syntax.typeisstr(ftraw)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(fi.foff: i64, "CX");
|
||||
emitdispreg(foff: i64, "CX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 8): i64, "CX");
|
||||
emitdispreg((foff + 8): i64, "CX");
|
||||
emitline(", BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 16): i64, "CX");
|
||||
emitdispreg((foff + 16): i64, "CX");
|
||||
emitline(", CX\n");
|
||||
} else { if (isfloattype(c, fi.tnode)) {
|
||||
// f64/f32 global field: route through X0.
|
||||
} else { if (syntax.typeisfloat(ftraw)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
if (syntax.typeisf32(ftraw)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "CX");
|
||||
emitdispreg(foff: i64, "CX");
|
||||
emitline(", X0\n");
|
||||
} else {
|
||||
let op: str = fieldloadop(c, fi);
|
||||
let ftc: *syntax.tinfo = tichase(ftraw);
|
||||
let fsz: i32 = 0;
|
||||
if (ftc != nil) { fsz = ftc.size: i32; };
|
||||
let op: str = loadopsz(syntax.typeissigned(ftraw), fsz);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "CX");
|
||||
emitdispreg(foff: i64, "CX");
|
||||
emitline(", AX\n");
|
||||
}; };
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
tf = tf.tnext;
|
||||
};
|
||||
};
|
||||
}; };
|
||||
};
|
||||
};
|
||||
// `arr[i].field` — element-then-field through a `[N]*S` / `[N]S`
|
||||
@@ -5277,30 +5325,29 @@ fn cgun(c: *cgen, n: *syntax.node) void = {
|
||||
// structlookup head, byte-id by
|
||||
// construction.
|
||||
if (lkind == syntax.nkind.N_TPTR) {
|
||||
let inner: *syntax.node = tn.lhs;
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (inner != nil) {
|
||||
if (inner.kind == syntax.nkind.N_TNAME) { sname = inner.str; };
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookupchain(c, inner);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (syntax.streq(fi.fname, fld)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitdispreg(fi.foff: i64, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
// #31: &p.f offset off the stamped *struct tinfo
|
||||
// (tichase(opnd.lhs.type_)->.sub), NOT structlookupchain(inner).
|
||||
// Twin of the R1 read arm (inferred-local ADDR row). Non-struct
|
||||
// pointees fall through to the slice/str pseudo arm below.
|
||||
let sti: *syntax.tinfo = nil;
|
||||
if (opnd.lhs != nil) { sti = tichase(opnd.lhs.type_: *syntax.tinfo); };
|
||||
if (sti != nil && sti.kind == syntax.tykind.TY_PTR) { sti = tichase(sti.sub); };
|
||||
if (sti != nil) { if (sti.kind == syntax.tykind.TY_STRUCT) {
|
||||
let tf: *syntax.tfield = sti.fields;
|
||||
for (tf != nil) {
|
||||
if (syntax.streq(tf.name, fld)) {
|
||||
let foff: i32 = tf.offset: i32;
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitdispreg(foff: i64, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
tf = tf.tnext;
|
||||
};
|
||||
};
|
||||
}; };
|
||||
};
|
||||
// Value-struct local: &o.f.
|
||||
// #102 review-found sibling: same
|
||||
@@ -5309,19 +5356,24 @@ fn cgun(c: *cgen, n: *syntax.node) void = {
|
||||
// struct fell to the generic route.
|
||||
// Same chase, same construction.
|
||||
if (lkind == syntax.nkind.N_TNAME) {
|
||||
let si: *structinfo = structlookupchain(c, tn);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (syntax.streq(fi.fname, fld)) {
|
||||
// #31: &o.f offset off the stamped value-struct tinfo
|
||||
// (tichase(opnd.lhs.type_)), NOT structlookupchain(tn). Non-
|
||||
// struct (str alias) falls through to the slice/str arm below.
|
||||
let sti: *syntax.tinfo = nil;
|
||||
if (opnd.lhs != nil) { sti = tichase(opnd.lhs.type_: *syntax.tinfo); };
|
||||
if (sti != nil) { if (sti.kind == syntax.tykind.TY_STRUCT) {
|
||||
let tf: *syntax.tfield = sti.fields;
|
||||
for (tf != nil) {
|
||||
if (syntax.streq(tf.name, fld)) {
|
||||
let foff: i32 = tf.offset: i32;
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitoff((lc.off + foff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
tf = tf.tnext;
|
||||
};
|
||||
};
|
||||
}; };
|
||||
};
|
||||
// Slice/str pseudo-field on a local:
|
||||
// &s.ptr / &s.len / &s.cap. Delta is
|
||||
@@ -5347,22 +5399,28 @@ fn cgun(c: *cgen, n: *syntax.node) void = {
|
||||
// Global root: top-level let, either a
|
||||
// struct or a slice/str.
|
||||
if (isletvar(c, basenm)) {
|
||||
let gsi: *structinfo = letvarstructinfo(c, basenm);
|
||||
if (gsi != nil) {
|
||||
let fi: *fieldinfo = gsi.fields;
|
||||
for (fi != nil) {
|
||||
if (syntax.streq(fi.fname, fld)) {
|
||||
// #31: global &g.f offset off the stamped value-struct tinfo
|
||||
// (tichase(opnd.lhs.type_)), NOT name-keyed letvarstructinfo.
|
||||
// Global decls are qualified -> non-reddenable; converted for
|
||||
// close-by-construction (byte-id).
|
||||
let gsti: *syntax.tinfo = nil;
|
||||
if (opnd.lhs != nil) { gsti = tichase(opnd.lhs.type_: *syntax.tinfo); };
|
||||
if (gsti != nil) { if (gsti.kind == syntax.tykind.TY_STRUCT) {
|
||||
let tf: *syntax.tfield = gsti.fields;
|
||||
for (tf != nil) {
|
||||
if (syntax.streq(tf.name, fld)) {
|
||||
let foff: i32 = tf.offset: i32;
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, basenm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitdispreg(fi.foff: i64, "CX");
|
||||
emitdispreg(foff: i64, "CX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
tf = tf.tnext;
|
||||
};
|
||||
};
|
||||
}; };
|
||||
let isstr: bool = letvarisstr(c, basenm);
|
||||
let issl: bool = letvarisslice(c, basenm);
|
||||
if (isstr || issl) {
|
||||
@@ -11310,25 +11368,29 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
let tn: *syntax.node = letvartnode(c, bn);
|
||||
if (tn != nil) {
|
||||
if (tn.kind == syntax.nkind.N_TPTR) {
|
||||
let inner: *syntax.node = tn.lhs;
|
||||
let si: *structinfo = structlookupchain(c, inner);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (syntax.streq(fi.fname, fld)) {
|
||||
// scalar only: str/slice/tagged/float/
|
||||
// struct fields keep their F6 route.
|
||||
// #31: scalar global *struct field store -- resolve offset +
|
||||
// scalar-ness off the stamped *struct tinfo
|
||||
// (tichase(base.type_)->.sub), NOT structlookupchain(inner).
|
||||
// Non-scalar fields (str/slice/tagged/float/nested-struct)
|
||||
// keep their F6 cgplaceaddr route (scalar=false -> fall
|
||||
// through). Global *struct decls are qualified -> non-
|
||||
// reddenable; convert for close-by-construction (byte-id).
|
||||
let sti: *syntax.tinfo = tichase(base.type_: *syntax.tinfo);
|
||||
if (sti != nil && sti.kind == syntax.tykind.TY_PTR) { sti = tichase(sti.sub); };
|
||||
if (sti != nil) { if (sti.kind == syntax.tykind.TY_STRUCT) {
|
||||
let tf: *syntax.tfield = sti.fields;
|
||||
for (tf != nil) {
|
||||
if (syntax.streq(tf.name, fld)) {
|
||||
let foff: i32 = tf.offset: i32;
|
||||
let ftraw: *syntax.tinfo = tf.type_;
|
||||
// scalar only: str/slice/tagged/float/struct fields
|
||||
// keep their F6 route (cstage type-keys the same).
|
||||
let scalar: bool = true;
|
||||
if (istaggedtype(c, fi.tnode)) { scalar = false; };
|
||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) { scalar = false; };
|
||||
if (isfloattype(c, fi.tnode)) { scalar = false; };
|
||||
if (fi.tnode != nil) {
|
||||
if (fi.tnode.kind == syntax.nkind.N_TNAME) {
|
||||
if (aliasprimsize(c, fi.tnode.str) == 0) {
|
||||
if (structlookup(c, fi.tnode.str) != nil) { scalar = false; };
|
||||
};
|
||||
};
|
||||
};
|
||||
if (syntax.typeistagged(ftraw)) { scalar = false; };
|
||||
if (syntax.typeisstr(ftraw) || syntax.typeisslice(ftraw)) { scalar = false; };
|
||||
if (syntax.typeisfloat(ftraw)) { scalar = false; };
|
||||
let ftc: *syntax.tinfo = tichase(ftraw);
|
||||
if (ftc != nil) { if (ftc.kind == syntax.tykind.TY_STRUCT) { scalar = false; }; };
|
||||
if (scalar) {
|
||||
cgexpr(c, n.rhs);
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
@@ -11339,18 +11401,23 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
emitdispreg(0i64, "BX");
|
||||
emitline(", BX\n");
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
let fsz: i32 = 0;
|
||||
if (ftc != nil) { fsz = ftc.size: i32; };
|
||||
let sop: str = "MOVQ";
|
||||
if (fsz == 1) { sop = "MOVB"; }
|
||||
else { if (fsz == 2) { sop = "MOVW"; }
|
||||
else { if (fsz == 4) { sop = "MOVL"; }; }; };
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitdispreg(foff: i64, "BX");
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
};
|
||||
fi = fi.finext;
|
||||
tf = tf.tnext;
|
||||
};
|
||||
};
|
||||
}; };
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -12171,8 +12238,10 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
&& n.rhs.kind == syntax.nkind.N_CALL && lvftn != nil) {
|
||||
let gsz: i32 = 0;
|
||||
if (lvftn.kind == syntax.nkind.N_TNAME) {
|
||||
let gsi: *structinfo = structlookup(c, lvftn.str);
|
||||
if (gsi != nil) { gsz = structabisize(gsi); };
|
||||
// #31: size off the stamped tinfo, not structlookup(lvftn.str).
|
||||
// Non-reddenable (global decl qualified); convert for
|
||||
// close-by-construction.
|
||||
gsz = structabisizetn(lvftn.type_: *syntax.tinfo);
|
||||
};
|
||||
if (lvftn.kind == syntax.nkind.N_TARRAY) {
|
||||
let gat: *syntax.tinfo = lvftn.type_: *syntax.tinfo;
|
||||
|
||||
@@ -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