w6c+wwstage: aggregate arg from any non-ident source via the closed addr machinery (#271) — close aggregate-arg family
Passing an aggregate BY VALUE as a call argument worked ONLY for a ≤16B struct from an IDENT source; every non-ident source — CALL mk(), N_DOT o.f, N_INDEX a[i], DEREF *p — and every array / >24B-struct (even as an ident) fell to the scalar default: one PUSHQ for a multi-word aggregate, stack-imbalancing against the type-based multi-word drain. cs!=ww, both garbage (f(mk()) cs4/ww236, f(o.f) cs8/ww108, f(a[i]) cs4/ww28, f(*p) cs4/ww140; arrays + 32B sret struct same). The arg-pass twin of the #265/#268 let-init copy. A new aggregate-arg push arm materialises the source into the arg convention: the source ADDRESS in SI (ident LEAQ / deref operand / dotchainaddr #253 / &base[i] spine #252-270) then its ceil(sz/8) words pushed high→low; a CALL receives first — ≤24B in AX/DX/CX pushed straight, >24B sret'd into a per-fn @aggargscr then pushed from there. The pop-forward drain gained a matching array / >16B-struct arm and the callee prologue an is_bigagg receive (ceil(sz/8) GP eightbytes), so caller and callee agree on the multi-word layout. The ≤16B-struct-IDENT fast path is untouched (byte-id preserved). The new-arm exclusion is TYPE-keyed (the stamped tinfo, mirroring cstage node_isstructarg over args[i]->type), not the name-keyed structparamsize — a name-keyed gate re-opened the #211/#13 cross-module same-leaf collision (784 symmetric: an 8B `sa.s` struct whose name-resolution collides with `sb.s = *vtable` would miss the struct fast path and wrongly enter the new arm, diverging from cstage's 1-word push). A float-bearing ≤16B struct from a non-ident source loud-stops in both stages (the #165 SSE eightbyte transport the GP push/drain can't model; out of scope). A const array/struct `def` global as an aggregate arg is aligned DOWN to the leaner wwstage (both loud-stop) per rule-10. #110: cgen is compiler-imported by w6c + wwdump — main.combined.ww regen'd for both. 949 rows: arg_{struct16,arr16,struct32}_{call,dot,idx,deref,ident}, full member readback (struct 16B reg-class + 32B sret-class + array [4]u32, each non-ident source + ident control); byteid=1 throughout (master both-broken-and-divergent → converge on the correct full push, #263). All 111 dotbaseaddr + 3/3 784 pass; test-unit 241 green; sizelint + smoke OK; the full w6c compiler source (214705 asm lines) self-compiles cs==ww byte-id.
This commit is contained in:
@@ -1128,6 +1128,92 @@ fn dotbaseaddr(c: *cgen, base: *node, dstreg: str) bool = {
|
||||
return true;
|
||||
};
|
||||
|
||||
// aggargsrcaddr — land the ADDRESS of an addressable aggregate (struct/
|
||||
// array) call-arg source in dstreg (#271, mirror of cstage
|
||||
// aggarg_srcaddr). Reuses the closed #265/#268 let-init-copy dispatch:
|
||||
// local ident slot (LEAQ off(BP)), module-let global (LEAQ name(SB)),
|
||||
// deref operand (cgexpr of the pointer), N_DOT field (dotchainaddr,
|
||||
// #253), N_INDEX element of an N_IDENT array base (the &base[i] spine,
|
||||
// #252/#270). Returns false for an uncovered source kind (caller loud-
|
||||
// stops, rule 7). The CALL source is handled at the push site.
|
||||
fn aggargsrcaddr(c: *cgen, src: *node, dst: str) bool = {
|
||||
if (src.kind == nkind.N_UN) {
|
||||
if (src.op == tkind.TK_STAR) {
|
||||
cgexpr(c, src.lhs);
|
||||
if (!streq(dst, "AX")) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
};
|
||||
return true;
|
||||
};
|
||||
};
|
||||
if (src.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, src.str);
|
||||
if (lc != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
// global value source. Gated to a module-`let` (letvartnode,
|
||||
// the cstage let_islet twin); a const array/struct `def`
|
||||
// aggregate ARG is untested + out of scope (#274; both stages
|
||||
// loud-stop, rule-10 aligned).
|
||||
if (letvartnode(c, src.str) != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, src.str);
|
||||
emitline("(SB), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
if (src.kind == nkind.N_DOT) {
|
||||
return dotchainaddr(c, src, dst);
|
||||
};
|
||||
if (src.kind == nkind.N_INDEX) {
|
||||
let base: *node = src.lhs;
|
||||
let idx: *node = src.rhs;
|
||||
if (base == nil) { return false; };
|
||||
if (base.kind != nkind.N_IDENT) { return false; };
|
||||
let bu: *tinfo = base.type_: *tinfo;
|
||||
for (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
|
||||
if (bu == nil) { return false; };
|
||||
if (bu.kind != tykind.TY_ARRAY) { return false; };
|
||||
let esz: i32 = 1;
|
||||
if (bu.sub != nil) { esz = bu.sub.size: i32; };
|
||||
cgexpr(c, idx);
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
let boff: *local = localfindnode(c, base.str);
|
||||
if (boff != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(boff.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, base.str);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
emitline("\tADDQ\tBX, AX\n");
|
||||
if (!streq(dst, "AX")) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
};
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn cgindex(c: *cgen, n: *node) void = {
|
||||
// Element-size-aware load: u8 → MOVZBQ, i32 → MOVSXD, u32 → MOVL,
|
||||
// str → (ptr, len) into (AX, BX), everything else → MOVQ. Fast
|
||||
@@ -4506,6 +4592,13 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
// words and shift intidx out of sync.
|
||||
let tcs: i32 = taggedcallslot(c, a);
|
||||
if (tcs > 0) { extra = tcs / 8 - 1; };
|
||||
// #271: array / >16B-struct / non-ident 16B-struct
|
||||
// aggregate arg — pushargsrev staged ceil(sz/8) words;
|
||||
// drain exactly that many so intidx tracks per-arg
|
||||
// (the ≤16B struct IDENT case is the stfc branch
|
||||
// above). Mirror of cstage node_isaggarg drain arm.
|
||||
let aggsz: i32 = aggargsizetn(a.type_: *tinfo);
|
||||
if (aggsz > 0) { extra = (aggsz + 7) / 8 - 1; };
|
||||
let words: i32 = 1 + extra;
|
||||
let w: i32 = 0;
|
||||
for (w < words) {
|
||||
|
||||
Reference in New Issue
Block a user