selfhost: fix several wwstage cgen miscompilations
Surfaced via examples/lisp, which had to work around the following in
source. Each lowering now matches cstage on the same shape.
- cgassign / cgdot: two-level field through a non-pointer sub-struct.
`(*L).cur.kind = k` (cur a struct-by-value field of L) silently
dropped the store; the corresponding read fell into the SB-symbol
fallback and the linker reported `undefined reference to kind`. The
two new branches resolve outer-field offset + inner-field offset
and emit a single direct store/load at the combined slot, both for
T-by-value and *T-base shapes.
- cgdot: `xs[i].field` chains the trailing field load through the
N_INDEX result for [N]T / []T / *T element-of-struct-ptr. The
cgforrange loop variable now carries the elem tnode so the same
fast path covers `for (let x .. xs) { x.field }`.
- cgindex / cgassign: top-level `[N]T` array and `*T` pointer used
as an index base. cgindex now emits LEAQ name(SB) (array) or
MOVQ name(SB) (pointer) with the correct element scaling; without
this the fallback emitted neither base and walked off the saved
BP slot. Adds letvartnode() helper, an N_TARRAY branch to
letemitsize so the array shows up in c.lets, and an N_TARRAY
initialiser path in emitletdataw that lays the literal bytes into
DATAW.
- cglet / scanlocals: infer the local's tnode for an unannotated
`let x = f()` / `let x = f()?`. inferletcalltype() reads the
callee's declared return; `?` and `!` strip to the success variant
so a tagged-union let allocates the full 24B slot and the
struct-field dispatch in cgdot/cgassign sees the right type.
letslotsize now defers to slotsize on the inferred type.
- slotsize: follow type aliases for tagged-union variants. With
`type parserr = !str;`, the variant slot was 8B instead of the
required 16B; the tagged let stomped on the next slot at the
AX/DX/CX spill.
- cgreturn: tagged-union return forwarding. `return f();` where f
also returns a tagged union now passes the (tag, payload1,
payload2) triple through unchanged instead of re-wrapping it.
- cgreturn / cglet / taggedvariantindex: dispatch by variant name
with module-qualified-vs-bare matching, and recognise N_STRUCTLIT
as the variant tag for `return eof{};`. cgexpr default emits
`MOVQ $0, AX` so the surrounding return shuffle isn't left with
a stale AX.
- isstrtype / nodeisstr: resolve through `!T` aliases. `parserr =
!str` was not propagating the str-shape to the rhs check and the
MOVQ BX,CX shuffle was being dropped from str-typed local
returns.
- exprfloatkind: recognise `p.field` as f64/f32 when the struct
field is so declared, so `v.fval: i64` lowers to CVTTSD2SI on X0.
- cgassign: str field on a direct struct local writes both halves.
`L.src = s;` previously dropped s.len.
- cgcall: pop into the int reg window only up to 6 (DI..R9); rest
stays on the stack and the caller emits ADDQ to clean up.
cgfnparams accepts >6-arg signatures by registering the overflow
params at positive BP offsets (16+8*k(BP)), no spill instruction
emitted.
All 26 harness tests pass; bootstrap reaches a byte-stable fixed
point at ww3 == ww4.
This commit is contained in:
@@ -167,6 +167,11 @@ fn cgfnparams(c: *cgen, params: *node) void = {
|
||||
let p: *node = params;
|
||||
let idx: i32 = 0;
|
||||
let fidx: i32 = 0;
|
||||
// Cursor for args that overflow the SysV reg windows. Each
|
||||
// stack-passed arg lives at 16+8*k(BP) — no spill, the local
|
||||
// is registered with a *positive* offset pointing into the
|
||||
// caller's frame. Mirrors C cgen's cg_stack_arg_cursor.
|
||||
let stkcursor: i32 = 0;
|
||||
for (p != nil) {
|
||||
if (p.kind == nkind.N_PARAM) {
|
||||
let nm: str = p.str;
|
||||
@@ -175,81 +180,101 @@ fn cgfnparams(c: *cgen, params: *node) void = {
|
||||
// (X0..X7). 8B (f64) or 4B (f32) slot.
|
||||
let fsz: i32 = 8;
|
||||
if (isf32type(c, p.lhs)) { fsz = 4; };
|
||||
let off: i32 = localadd(c, nm, fsz, p.lhs);
|
||||
let mov: str = "MOVSD";
|
||||
if (fsz == 4) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitline(fargregname(fidx));
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
fidx += 1;
|
||||
if (fidx < 8) {
|
||||
let off: i32 = localadd(c, nm, fsz, p.lhs);
|
||||
let mov: str = "MOVSD";
|
||||
if (fsz == 4) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitline(fargregname(fidx));
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
fidx += 1;
|
||||
} else {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += 1;
|
||||
};
|
||||
p = p.next;
|
||||
continue;
|
||||
};
|
||||
if (istaggedtype(p.lhs)) {
|
||||
// tagged-union param: spill size/8 registers
|
||||
// (tag + value words). Slot sized to match.
|
||||
let slot: i32 = slotsize(c, p.lhs);
|
||||
let off: i32 = localadd(c, nm, slot, p.lhs);
|
||||
let nw: i32 = slot / 8;
|
||||
let w: i32 = 0;
|
||||
for (w < nw) {
|
||||
if (idx + nw <= 6) {
|
||||
let off: i32 = localadd(c, nm, slot, p.lhs);
|
||||
let w: i32 = 0;
|
||||
for (w < nw) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + w*8): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
w += 1;
|
||||
};
|
||||
} else {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += nw;
|
||||
};
|
||||
} else { if (isslicetype(c, p.lhs)) {
|
||||
if (idx + 3 <= 6) {
|
||||
let off: i32 = localadd(c, nm, 24, p.lhs);
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + w*8): i64);
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
w += 1;
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
} else {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += 3;
|
||||
};
|
||||
} else { if (isslicetype(c, p.lhs)) {
|
||||
// slice param: 3 regs (ptr, len, cap), 24-byte slot.
|
||||
let off: i32 = localadd(c, nm, 24, p.lhs);
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
} else { if (isstrtype(c, p.lhs)) {
|
||||
// str param: passed in two regs (ptr, len).
|
||||
// Slot is 16 bytes; ptr at off+0, len at off+8.
|
||||
let off: i32 = localadd(c, nm, 16, p.lhs);
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
if (idx + 2 <= 6) {
|
||||
let off: i32 = localadd(c, nm, 16, p.lhs);
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
} else {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += 2;
|
||||
};
|
||||
} else {
|
||||
let off: i32 = localadd(c, nm, 8, p.lhs);
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
if (idx < 6) {
|
||||
let off: i32 = localadd(c, nm, 8, p.lhs);
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
} else {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += 1;
|
||||
};
|
||||
};};};
|
||||
};
|
||||
p = p.next;
|
||||
|
||||
Reference in New Issue
Block a user