wcc/cgen: #150 by-value module-global struct-arg base — load main.g(SB) all words (both stages)

Passing a module-global struct by value -- let g: pt = pt{...}; take(g)
-- was silently miscompiled, mirror-opposite on the two stages. cstage's
by-value struct-arg arm hit localfind(g)->0 and read 2 words from the
frame (MOVQ (BP)), never main.g(SB) -> returned garbage. wwstage used the
correct main.g(SB) base but fell through to the scalar single-PUSHQ
default, pushing one word for a 2-word struct -> dropped a field.

Both stages now take the off==0 global branch: LEAQ main.NAME(SB) and copy
all struct-size/8 eightbytes (reusing the GAP-A.ptr/#231 global-base
predicate), converging to one byte-identical sequence. The local path
(off!=0) is unchanged; >16B aggregates (#271) already resolved globals.

Commit A of the cluster; the cstage-only inferred-global-type Sym-repoint
(every let g = ... module-global yields <nil> downstream) is Commit B
(#18). Slice/str global-by-value args have the same wwstage field-drop --
filed (#10 G-valglobal-arg; struct closed here). byte-id 990-997 8/8.
test/wcc/822 table-driven, byte-id per stage.
This commit is contained in:
2026-06-08 20:51:01 +09:00
parent 03fc7c7abe
commit 5c3764828f
6 changed files with 372 additions and 0 deletions

View File

@@ -9243,6 +9243,25 @@ cgexpr(Cg *c, Node *n, Local *locals)
/* load qword(s) directly from the struct's slot */
int off = localfind(locals, args[i]->str);
int sz = struct_arg_size(args[i]->type);
/* #150: a module-global struct source — off==0
* is the localfind footgun (GAP-A.ptr/#231
* family); the BP loads below read the stack
* frame, not main.g(SB). Resolve the global base
* into BX (the #129-A.2 struct-global LEAQ shape)
* and copy ALL eightbytes from it. */
if (off == 0 && (let_islet(args[i]->str)
|| def_isstructdef(args[i]->str))) {
ins2(c, A_LEAQ, masym(c, args[i]->str),
areg(D_BX));
if (sz > 8) {
ins2(c, A_MOVQ, amem(D_BX, 8),
areg(D_AX));
ins1(c, A_PUSHQ, areg(D_AX));
}
ins2(c, A_MOVQ, amem(D_BX, 0), areg(D_AX));
ins1(c, A_PUSHQ, areg(D_AX));
continue;
}
if (sz > 8) {
ins2(c, A_MOVQ, amem(D_BP, off + 8), areg(D_AX));
ins1(c, A_PUSHQ, areg(D_AX));