wwstage: fold chained global-ptr field READ to cstage offset-fold (#16)
wwstage's chained-N_DOT resolver (dotchainresolve) didn't resolve a global
*struct root (only local *T and global value-struct), so gp.sf.len / gp.x.y
bailed to an inner-dot load + shuffle, byte-diverging from cstage's offset-fold.
Both stages already ran correct after #15 (475c003) -- a pure rule-10 asm
divergence. cstage is untouched (the oracle); wwstage aligns up.
Resolve a global N_TPTR root, and extract emitchainbase for the viacx base-load
(byte-identical across the 5 read + 2 store sites it replaces). The chained
STORE caller declines the global-ptr root (yok=false) so it falls to cstage's
address-spine mirror -- matching the #6/#15 decline-to-resolver discipline;
local *T chained stores still fold.
Test: +2 chained rows (gp.sf.len, gp.x.q), runtime + byte-id; proven to fail
byte-id with only the compiler files reverted, pass with the fix.
Sibling follow-ups filed: #17 (>32B tagged word-order), #18 (chained read into
an i64 sink MOVSXD check).
This commit is contained in:
@@ -3293,6 +3293,29 @@ fn cgptrfieldload(c: *cgen, fi: *fieldinfo) void = {
|
||||
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`
|
||||
// ROOT then derefs that address once (MOVQ (CX),CX) — covering the
|
||||
// global `*struct` root, where BOTH flags hold (#16) — while a LOCAL
|
||||
// `*T` root loads its frame slot (MOVQ off(BP),CX). The leaf load then
|
||||
// indexes at totaloff off CX.
|
||||
fn emitchainbase(c: *cgen, ptrroot: bool, isglobal: bool,
|
||||
rootoff: i32, rootname: str) void = {
|
||||
if (isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t(CX), CX\n");
|
||||
};
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), CX\n");
|
||||
};
|
||||
};
|
||||
|
||||
fn cgdot(c: *cgen, n: *syntax.node) void = {
|
||||
let lhs: *syntax.node = n.lhs;
|
||||
let fld: str = n.str;
|
||||
@@ -4460,15 +4483,8 @@ fn cgdot(c: *cgen, n: *syntax.node) void = {
|
||||
let viacx: bool = isglobal || ptrroot;
|
||||
if (slicedelta >= 0) {
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), CX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
};
|
||||
emitchainbase(c, ptrroot, isglobal,
|
||||
rootoff, rootname);
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((totaloff + slicedelta): i64, "CX");
|
||||
emitline(", AX\n");
|
||||
@@ -4493,15 +4509,8 @@ fn cgdot(c: *cgen, n: *syntax.node) void = {
|
||||
tlu = tichase(tlu);
|
||||
let ttsz: i32 = tlu.size: i32;
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), CX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
};
|
||||
emitchainbase(c, ptrroot, isglobal,
|
||||
rootoff, rootname);
|
||||
cgloadtaggedfield(c, "CX", totaloff, ttsz);
|
||||
} else {
|
||||
cgloadtaggedfield(c, "BP",
|
||||
@@ -4521,15 +4530,8 @@ fn cgdot(c: *cgen, n: *syntax.node) void = {
|
||||
// BP-rooted locals the registers do not alias so order is
|
||||
// free.
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), CX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
};
|
||||
emitchainbase(c, ptrroot, isglobal,
|
||||
rootoff, rootname);
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(totaloff: i64, "CX");
|
||||
emitline(", AX\n");
|
||||
@@ -4556,15 +4558,8 @@ fn cgdot(c: *cgen, n: *syntax.node) void = {
|
||||
let mov: str = "MOVSD";
|
||||
if (syntax.typeisf32(leaftype)) { mov = "MOVSS"; };
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), CX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
};
|
||||
emitchainbase(c, ptrroot, isglobal,
|
||||
rootoff, rootname);
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
@@ -4582,15 +4577,8 @@ fn cgdot(c: *cgen, n: *syntax.node) void = {
|
||||
let lop: str = loadopsz(syntax.typeissigned(leaftype),
|
||||
leaftype.slotsize: i32);
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), CX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
};
|
||||
emitchainbase(c, ptrroot, isglobal,
|
||||
rootoff, rootname);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
@@ -11216,6 +11204,15 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
let yok: bool = dotchainresolve(c, lhs,
|
||||
&rootname, &rootoff, &totaloff,
|
||||
&leaftype, &slicedelta, &isglobal, &ptrroot);
|
||||
// A global `*struct` root chained STORE: cstage stores
|
||||
// via the address-compute spine (LEAQ name(SB),BX; MOVQ
|
||||
// (BX),BX; ADDQ; MOVQ AX,(BX)), NOT the READ path's
|
||||
// offset-fold — so the fold spine below would diverge.
|
||||
// Decline and fall through to the address-spine mirror,
|
||||
// exactly as the addr-of path declines a ptr-rooted chain
|
||||
// (cgenexpr.ww:5114). A LOCAL `*T` root (ptrroot &&
|
||||
// !isglobal) DOES fold in both stages and stays. (#16)
|
||||
if (isglobal && ptrroot) { yok = false; };
|
||||
if (yok) {
|
||||
// `*T` root and global share the CX-based emit:
|
||||
// loader runs AFTER cgexpr(rhs) so AX/BX/X0 stay
|
||||
@@ -11224,15 +11221,8 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
if (slicedelta >= 0) {
|
||||
cgexpr(c, n.rhs);
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), CX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
};
|
||||
emitchainbase(c, ptrroot, isglobal,
|
||||
rootoff, rootname);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg((totaloff + slicedelta): i64, "CX");
|
||||
emitline("\n");
|
||||
@@ -11539,15 +11529,8 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
if (syntax.typeisf32(leaftype)) { mov = "MOVSS"; };
|
||||
cgexpr(c, n.rhs);
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), CX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
};
|
||||
emitchainbase(c, ptrroot, isglobal,
|
||||
rootoff, rootname);
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
|
||||
@@ -4942,6 +4942,28 @@ export fn dotchainresolve(c: *cgen, n: *syntax.node,
|
||||
};
|
||||
};
|
||||
};
|
||||
// Global `*struct` root (`let gp: *S`): localfindnode is nil and
|
||||
// letvarstructinfo below only matches a VALUE-struct global, so a
|
||||
// `*struct` global root needs its own arm. Gate on N_TPTR→N_TNAME
|
||||
// exactly as the local `*T` arm above; the post-resolution loop peels
|
||||
// the TY_PTR off cur.type_ and validates the pointee is a struct.
|
||||
// Mirrors cstage cgen.c's ptr_root + let_islet base resolution
|
||||
// (LEAQ name(SB),CX; MOVQ (CX),CX). (#16, chained twin of #15.)
|
||||
if (!resolved && lc == nil) {
|
||||
let gtn: *syntax.node = letvartnode(c, cur.str);
|
||||
if (gtn != nil) {
|
||||
if (gtn.kind == syntax.nkind.N_TPTR) {
|
||||
let pe: *syntax.node = gtn.lhs;
|
||||
if (pe != nil) {
|
||||
if (pe.kind == syntax.nkind.N_TNAME) {
|
||||
*outisglobal = true;
|
||||
*outptrroot = true;
|
||||
resolved = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (!resolved) {
|
||||
let gsi: *structinfo = letvarstructinfo(c, cur.str);
|
||||
if (gsi != nil) {
|
||||
|
||||
Reference in New Issue
Block a user