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:
@@ -17043,6 +17043,39 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node, memphase: bool) i32 = {
|
||||
return rest + nw;
|
||||
};
|
||||
};
|
||||
// #150: a module-global by-value struct arg. localfindnode
|
||||
// above misses it (a global is not a frame slot), and the #271
|
||||
// arm below excludes a ≤16B struct ident (structident=true), so
|
||||
// pre-fix it fell through to the scalar single-PUSHQ default and
|
||||
// silently dropped word1. cstage's mirror-opposite bug read the
|
||||
// FRAME (localfind→0 off==0 footgun); both stages converge here
|
||||
// on the global-base load: LEAQ name(SB) into BX, then copy ALL
|
||||
// eightbytes high→low (the #256/#129-A.2 struct-global shape; the
|
||||
// isletvar||deflookup gate is the let_islet||def_isstructdef twin
|
||||
// from dotchainaddr). The slot-word count is the stamped tinfo
|
||||
// size (the type table, byte-id with cstage struct_arg_size).
|
||||
if (lc == nil) {
|
||||
let gst: *tinfo = arg.type_: *tinfo;
|
||||
gst = tichase(gst);
|
||||
if (gst != nil) { if (gst.kind == tykind.TY_STRUCT) {
|
||||
let gsz: i32 = gst.size: i32;
|
||||
if (gsz > 0 && gsz <= 16
|
||||
&& (isletvar(c, nm) || deflookup(c, nm))) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), BX\n");
|
||||
if (gsz > 8) {
|
||||
emitline("\tMOVQ\t8(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
let gnw: i32 = 1;
|
||||
if (gsz > 8) { gnw = 2; };
|
||||
return rest + gnw;
|
||||
};
|
||||
}; };
|
||||
};
|
||||
};
|
||||
// #271: aggregate (struct/array) arg from any source the ≤16B
|
||||
// struct-IDENT fast path above doesn't cover — a 16B struct from a
|
||||
|
||||
@@ -723,6 +723,39 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node, memphase: bool) i32 = {
|
||||
return rest + nw;
|
||||
};
|
||||
};
|
||||
// #150: a module-global by-value struct arg. localfindnode
|
||||
// above misses it (a global is not a frame slot), and the #271
|
||||
// arm below excludes a ≤16B struct ident (structident=true), so
|
||||
// pre-fix it fell through to the scalar single-PUSHQ default and
|
||||
// silently dropped word1. cstage's mirror-opposite bug read the
|
||||
// FRAME (localfind→0 off==0 footgun); both stages converge here
|
||||
// on the global-base load: LEAQ name(SB) into BX, then copy ALL
|
||||
// eightbytes high→low (the #256/#129-A.2 struct-global shape; the
|
||||
// isletvar||deflookup gate is the let_islet||def_isstructdef twin
|
||||
// from dotchainaddr). The slot-word count is the stamped tinfo
|
||||
// size (the type table, byte-id with cstage struct_arg_size).
|
||||
if (lc == nil) {
|
||||
let gst: *tinfo = arg.type_: *tinfo;
|
||||
gst = tichase(gst);
|
||||
if (gst != nil) { if (gst.kind == tykind.TY_STRUCT) {
|
||||
let gsz: i32 = gst.size: i32;
|
||||
if (gsz > 0 && gsz <= 16
|
||||
&& (isletvar(c, nm) || deflookup(c, nm))) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), BX\n");
|
||||
if (gsz > 8) {
|
||||
emitline("\tMOVQ\t8(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
let gnw: i32 = 1;
|
||||
if (gsz > 8) { gnw = 2; };
|
||||
return rest + gnw;
|
||||
};
|
||||
}; };
|
||||
};
|
||||
};
|
||||
// #271: aggregate (struct/array) arg from any source the ≤16B
|
||||
// struct-IDENT fast path above doesn't cover — a 16B struct from a
|
||||
|
||||
@@ -17043,6 +17043,39 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node, memphase: bool) i32 = {
|
||||
return rest + nw;
|
||||
};
|
||||
};
|
||||
// #150: a module-global by-value struct arg. localfindnode
|
||||
// above misses it (a global is not a frame slot), and the #271
|
||||
// arm below excludes a ≤16B struct ident (structident=true), so
|
||||
// pre-fix it fell through to the scalar single-PUSHQ default and
|
||||
// silently dropped word1. cstage's mirror-opposite bug read the
|
||||
// FRAME (localfind→0 off==0 footgun); both stages converge here
|
||||
// on the global-base load: LEAQ name(SB) into BX, then copy ALL
|
||||
// eightbytes high→low (the #256/#129-A.2 struct-global shape; the
|
||||
// isletvar||deflookup gate is the let_islet||def_isstructdef twin
|
||||
// from dotchainaddr). The slot-word count is the stamped tinfo
|
||||
// size (the type table, byte-id with cstage struct_arg_size).
|
||||
if (lc == nil) {
|
||||
let gst: *tinfo = arg.type_: *tinfo;
|
||||
gst = tichase(gst);
|
||||
if (gst != nil) { if (gst.kind == tykind.TY_STRUCT) {
|
||||
let gsz: i32 = gst.size: i32;
|
||||
if (gsz > 0 && gsz <= 16
|
||||
&& (isletvar(c, nm) || deflookup(c, nm))) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), BX\n");
|
||||
if (gsz > 8) {
|
||||
emitline("\tMOVQ\t8(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
let gnw: i32 = 1;
|
||||
if (gsz > 8) { gnw = 2; };
|
||||
return rest + gnw;
|
||||
};
|
||||
}; };
|
||||
};
|
||||
};
|
||||
// #271: aggregate (struct/array) arg from any source the ≤16B
|
||||
// struct-IDENT fast path above doesn't cover — a 16B struct from a
|
||||
|
||||
Reference in New Issue
Block a user