cgen: fix global-ptr field READ, load ptr value via SB before offset (#15)

Reading gp.f through a module-global pointer miscompiled in BOTH stages,
differently: cstage classified gp as a local at boff 0 and derefed BP
(MOVQ (BP),BX), wwstage collapsed gp.f to an undefined global symbol f
(MOVQ f(SB)). Both now load the pointer value from the global's data slot
before the field offset, converging on MOVQ gp(SB),BX; MOVQ off(BX),AX.
cstage mirrors the #6 store decline; wwstage gains a global-ptr arm and
shares a cgptrfieldload helper with the local arm.

Fused, not split: the two stages must emit byte-identical asm, so a
one-stage commit would fail the byte-id gate. Sibling byte-divergences
filed: #16 (chained-spine gp.x.y), #17 (>32B tagged word-order).

Test: table-driven 689_globptr_field_read_run (24 rows, runtime + byte-id).
This commit is contained in:
2026-06-23 06:42:38 +09:00
parent 02967e04ce
commit 475c003b0d
4 changed files with 399 additions and 59 deletions

View File

@@ -11856,7 +11856,17 @@ cgexpr(Cg *c, Node *n, Local *locals)
&& (lenfld || capfld || ptrfld)
&& dot_lhs && dot_lhs->kind == N_IDENT) {
int off = localfind(locals, dot_lhs->str);
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_BX));
/* #15 — a module-GLOBAL ptr has no local
* slot (off==0); load its pointer VALUE from
* the data slot via SB, else amem(BP,0) derefs
* the saved BP (read twin of the #6 store fix). */
if (off == 0 && let_islet(dot_lhs->str))
ins2(c, A_MOVQ,
mafn(c, dot_lhs->str, c->cur_mod),
areg(D_BX));
else
ins2(c, A_MOVQ, amem(D_BP, off),
areg(D_BX));
int delta = ptrfld ? 0 : (lenfld ? 8 : 16);
ins2(c, A_MOVQ, amem(D_BX, delta), areg(D_AX));
break;
@@ -11878,7 +11888,17 @@ cgexpr(Cg *c, Node *n, Local *locals)
if (inner && inner->kind == TY_STRUCT
&& dot_lhs && dot_lhs->kind == N_IDENT) {
int off = localfind(locals, dot_lhs->str);
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_BX));
/* #15 — module-GLOBAL ptr base: load its pointer
* VALUE from the data slot via SB (off==0 = no
* local slot), else amem(BP,0) derefs the saved
* BP (read twin of the #6 store fix). */
if (off == 0 && let_islet(dot_lhs->str))
ins2(c, A_MOVQ,
mafn(c, dot_lhs->str, c->cur_mod),
areg(D_BX));
else
ins2(c, A_MOVQ, amem(D_BP, off),
areg(D_BX));
for (Tfield *f = inner->fields; f; f = f->next) {
if (strcmp(f->name, n->str) != 0) continue;
/* tagged-union field through *struct: BX