w6c+w6c_ww: global-base arm for indexed struct-element field read (fix #21)
The `arr[i].field` N_DOT read branch in both stages was gated on a LOCAL
base lookup (cstage `localfind != 0`, wwstage `localfindnode != nil`). A
module-GLOBAL base (`let g: [2]pt = [...]`) missed it:
- cstage fell to a generic index-load that drops f->offset — it read
element[i] at offset 0, so `g[i].b` returned a's value (g[0].b -> 1,
g[1].b -> 3 instead of 2, 4).
- wwstage fell to the module-qualified SB fallback — garbage, no main.g
load at all.
Silent, byte-id-divergent. This is the READ twin of #11 (the global
`g[i] = v` write fix) and the #15 sibling. Local `[N]struct` bases read
correctly (tests 680/681 cover only those), which is why it was never
caught.
Fix (both stages, converged byte-identical): resolve the global the same
way the N_INDEX arm does — cstage `let_islet || def_isarraydef`, wwstage
`letvartnode || defvartnode` — and dispatch the base load by shape: array
-> LEAQ name(SB) (the symbol IS the storage), slice/ptr -> MOVQ name(SB)
(the symbol's first word IS the .ptr). The field then loads at f->offset
exactly as the local arm does. esz (element stride) and f->offset both
come from the type table (rule 13). Mirrors #11's write-side global-base
resolution. combined.ww embeds (w6c + wwdump) regenerate.
688_global_arr_elem_field: global `[2]pt` reads of .a/.b on both elements
(the .b reads are the bug), a non-8-aligned `[2]rec {tag:u8,x:i32,y:i64}`
to stress f->offset + a u8 sub-word leaf, and a slice-base read
(`let g: []rec = arr;`) that exercises the MOVQ-deref .ptr arm. Runtime
(cstage build+run) + cstage==wwstage byte-id per row. The slice row is
byte-id ONLY: its read asm is correct and identical on both stages, but a
slice-of-struct module global does not data-emit a symbol yet (a separate,
pre-existing data-emission gap, sibling of #10/#20), so it cannot link/run.
This commit is contained in:
@@ -22306,8 +22306,28 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
let idxbase: *node = lhs.lhs;
|
||||
if (idxbase != nil) { if (idxbase.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, idxbase.str);
|
||||
if (lc != nil) { if (lc.tnode != nil) {
|
||||
let tn: *node = lc.tnode;
|
||||
// #21 (READ twin of #11): a module-GLOBAL base makes
|
||||
// localfindnode return nil, so this field-offset-aware
|
||||
// branch was skipped and `g[i].field` fell through to
|
||||
// the module-qualified fallback below (garbage — no
|
||||
// main.g load at all). Resolve the global's tnode via
|
||||
// letvartnode/defvartnode (the same source cgindex's
|
||||
// global arm uses) and dispatch the base load by shape:
|
||||
// array -> LEAQ name(SB) (the symbol IS the storage),
|
||||
// slice/ptr -> MOVQ name(SB) (the symbol's first word
|
||||
// IS the .ptr). Mirrors cstage cgen.c's #21 arm.
|
||||
let tn: *node = nil;
|
||||
let isglobal: bool = false;
|
||||
let gname: str;
|
||||
gname.ptr = nil; gname.len = 0;
|
||||
if (lc != nil) {
|
||||
tn = lc.tnode;
|
||||
} else {
|
||||
tn = letvartnode(c, idxbase.str);
|
||||
if (tn == nil) { tn = defvartnode(c, idxbase.str); };
|
||||
if (tn != nil) { isglobal = true; gname = idxbase.str; };
|
||||
};
|
||||
if (tn != nil) {
|
||||
let elemt: *node = nil;
|
||||
let baseisarray: bool = false;
|
||||
let tk: nkind = tn.kind;
|
||||
@@ -22342,14 +22362,26 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
if (isglobal) {
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, gname);
|
||||
emitline("(SB), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, gname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
};
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
if (viaptr) {
|
||||
@@ -22415,7 +22447,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
};};
|
||||
};
|
||||
};};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2562,8 +2562,28 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
let idxbase: *node = lhs.lhs;
|
||||
if (idxbase != nil) { if (idxbase.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, idxbase.str);
|
||||
if (lc != nil) { if (lc.tnode != nil) {
|
||||
let tn: *node = lc.tnode;
|
||||
// #21 (READ twin of #11): a module-GLOBAL base makes
|
||||
// localfindnode return nil, so this field-offset-aware
|
||||
// branch was skipped and `g[i].field` fell through to
|
||||
// the module-qualified fallback below (garbage — no
|
||||
// main.g load at all). Resolve the global's tnode via
|
||||
// letvartnode/defvartnode (the same source cgindex's
|
||||
// global arm uses) and dispatch the base load by shape:
|
||||
// array -> LEAQ name(SB) (the symbol IS the storage),
|
||||
// slice/ptr -> MOVQ name(SB) (the symbol's first word
|
||||
// IS the .ptr). Mirrors cstage cgen.c's #21 arm.
|
||||
let tn: *node = nil;
|
||||
let isglobal: bool = false;
|
||||
let gname: str;
|
||||
gname.ptr = nil; gname.len = 0;
|
||||
if (lc != nil) {
|
||||
tn = lc.tnode;
|
||||
} else {
|
||||
tn = letvartnode(c, idxbase.str);
|
||||
if (tn == nil) { tn = defvartnode(c, idxbase.str); };
|
||||
if (tn != nil) { isglobal = true; gname = idxbase.str; };
|
||||
};
|
||||
if (tn != nil) {
|
||||
let elemt: *node = nil;
|
||||
let baseisarray: bool = false;
|
||||
let tk: nkind = tn.kind;
|
||||
@@ -2598,14 +2618,26 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
if (isglobal) {
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, gname);
|
||||
emitline("(SB), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, gname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
};
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
if (viaptr) {
|
||||
@@ -2671,7 +2703,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
};};
|
||||
};
|
||||
};};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -22306,8 +22306,28 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
let idxbase: *node = lhs.lhs;
|
||||
if (idxbase != nil) { if (idxbase.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, idxbase.str);
|
||||
if (lc != nil) { if (lc.tnode != nil) {
|
||||
let tn: *node = lc.tnode;
|
||||
// #21 (READ twin of #11): a module-GLOBAL base makes
|
||||
// localfindnode return nil, so this field-offset-aware
|
||||
// branch was skipped and `g[i].field` fell through to
|
||||
// the module-qualified fallback below (garbage — no
|
||||
// main.g load at all). Resolve the global's tnode via
|
||||
// letvartnode/defvartnode (the same source cgindex's
|
||||
// global arm uses) and dispatch the base load by shape:
|
||||
// array -> LEAQ name(SB) (the symbol IS the storage),
|
||||
// slice/ptr -> MOVQ name(SB) (the symbol's first word
|
||||
// IS the .ptr). Mirrors cstage cgen.c's #21 arm.
|
||||
let tn: *node = nil;
|
||||
let isglobal: bool = false;
|
||||
let gname: str;
|
||||
gname.ptr = nil; gname.len = 0;
|
||||
if (lc != nil) {
|
||||
tn = lc.tnode;
|
||||
} else {
|
||||
tn = letvartnode(c, idxbase.str);
|
||||
if (tn == nil) { tn = defvartnode(c, idxbase.str); };
|
||||
if (tn != nil) { isglobal = true; gname = idxbase.str; };
|
||||
};
|
||||
if (tn != nil) {
|
||||
let elemt: *node = nil;
|
||||
let baseisarray: bool = false;
|
||||
let tk: nkind = tn.kind;
|
||||
@@ -22342,14 +22362,26 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
if (isglobal) {
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, gname);
|
||||
emitline("(SB), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, gname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
};
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
if (viaptr) {
|
||||
@@ -22415,7 +22447,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
};};
|
||||
};
|
||||
};};
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user