w6c+selfhost: tagged-arr element ABI + full selfhost mirror
Closes the remaining tagged-union gaps after the prior two commits:
1. Tagged element in an array/slice (cstage). N_INDEX load now reads
slot words into AX/DX/CX, matching the tagged-return ABI so match
/ call-arg / let-init paths consume `arr[i]` uniformly. N_INDEX
store routes through a scratch slot + cg_widen_tagged_store +
byte-copy to &arr[i], so the full widening machinery (scalar /
str / struct payload / tagged subset / nullable fold) lights up
for element writes too.
2. Selfhost mirror — the cgen widen helpers (struct payload,
tagged-subset, spread-flatten) C cgen has had for two commits
finally land in selfhost:
cgwidentaggedstore — single writer for nullable / tagged ident /
tagged via AX:DX:CX / struct (lit + ident) /
str / scalar source shapes.
cgwidentagremap — CMPQ-chain tag remap for variant-subset.
rhsstructpayload — struct-name predicate; filters `!void` /
`!i32` aliases that share N_STRUCTLIT shape
but aren't structs.
rhstaggedident,
rhstaggedabicall — source-shape predicates.
flatvariantidx — spread-aware variant index lookup. Walks
`(...inner | T)` entries by resolving the
alias and inlining the inner's variants so
wwstage's tag order matches the check.c
flattening cstage does at type resolution.
cglet tagged init, cgassign tagged-ident reassign, cgreturn struct
/ subset payload, pushargsrev struct payload, cgindex tagged
element load, cgassign N_INDEX tagged element store all delegate
to these. cgmatch picks up scrutt from N_INDEX bases (element
type) and uses flatvariantidx for case dispatch.
3. Selfhost frame accounting: scanlocals reserves a 24B @tagscr slot
when the body contains a tagged-arr store, a struct-payload
tagged return, or a struct-payload call arg — dedup'd via
scanseenmark so multiple sites share one slot. N_LET stubs now
carry tnode so walk-time type checks see the array element type.
slotsize TARRAY learned to size tagged / struct / ptr / aliased
elements (was 8B-default for anything not N_TNAME-primitive,
undersizing tagged-element arrays).
Scalar / str call-arg widening keeps its direct-push fast path
(no scratch), so wwstage's asm on selfhost source remains
byte-identical to cstage's — 993/995 still pass.
700_e2e: 9 new rows — scalar/str/struct/subset/nullable variants in
arrays and slices, plus pass-arg / let-init / return / match shapes.
This commit is contained in:
@@ -162,6 +162,53 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// Struct payload or tagged-subset return — materialise
|
||||
// the widened value in scratch via cgwidentaggedstore
|
||||
// (handles tag remap and zero pad), then load AX/DX/CX
|
||||
// from the slot.
|
||||
let needswiden: bool = false;
|
||||
if (!isnullabletype(c.fnret)) {
|
||||
if (!forwardtagged) {
|
||||
let sname: str = rhsstructpayload(c, rhs);
|
||||
if (sname.len > 0) { needswiden = true; };
|
||||
if (rhstaggedident(c, rhs) != nil) {
|
||||
needswiden = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (needswiden) {
|
||||
let rsz: i32 = slotsize(c, c.fnret);
|
||||
let scroff: i32 = localadd(c, "@tagscr",
|
||||
24, nil);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let zz: i32 = 0;
|
||||
for (zz < rsz) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + zz): i64);
|
||||
emitline("(BP)\n");
|
||||
zz += 8;
|
||||
};
|
||||
cgwidentaggedstore(c, c.fnret, rhs, scroff,
|
||||
rsz);
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
if (rsz > 8) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
};
|
||||
if (rsz > 16) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
emitline("\tPOPQ\tBP\n");
|
||||
emitline("\tRET\n");
|
||||
c.lastwasreturn = 1;
|
||||
return;
|
||||
};
|
||||
cgexpr(c, rhs);
|
||||
if (isnullabletype(c.fnret)) {
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
@@ -247,75 +294,12 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
let off: i32 = localadd(c, nm, sz, tn);
|
||||
if (n.rhs != nil) {
|
||||
let rhs: *node = n.rhs;
|
||||
// Tagged-union init: `let r: (T | E) = expr;`.
|
||||
// - If rhs is a CALL to a fn returning tagged-union,
|
||||
// the result is already in (AX=tag, DX=v0, CX=v1);
|
||||
// just spill all three.
|
||||
// - Otherwise rhs is a bare variant value: pack tag +
|
||||
// value(s).
|
||||
// Tagged-union init: delegate to cgwidentaggedstore, which
|
||||
// handles nullable fold, tagged source (ident or AX/DX/CX
|
||||
// ABI call), struct payload (literal/ident), str payload,
|
||||
// scalar payload — with tag remap for tagged-subset widening.
|
||||
if (istaggedtype(c, tn)) {
|
||||
let nullable: bool = isnullabletype(tn);
|
||||
let rhsreturnstagged: bool = false;
|
||||
if (rhs.kind == nkind.N_CALL) {
|
||||
let callee: *node = rhs.lhs;
|
||||
if (callee != nil) {
|
||||
let calleename: str;
|
||||
calleename.ptr = nil; calleename.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) { calleename = callee.str; };
|
||||
if (callee.kind == nkind.N_DOT) { calleename = callee.str; };
|
||||
if (calleename.len > 0) {
|
||||
let rt: *node = fnretlookup(c, calleename);
|
||||
if (istaggedtype(c, rt)) { rhsreturnstagged = true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
cgexpr(c, rhs);
|
||||
if (nullable) {
|
||||
// Slot is one 8B word; AX is the pointer
|
||||
// (or 0 for null/void). Same path whether
|
||||
// the rhs is a call or a bare variant.
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
if (rhsreturnstagged) {
|
||||
// Spill size/8 registers (tag + value words).
|
||||
// Slots smaller than 24 don't carry a CX word.
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
if (sz > 16) {
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
let tagidx: i32 = taggedvariantindex(c, tn, rhs);
|
||||
if (tagidx < 0) { tagidx = 0; };
|
||||
if (nodeisstr(c, rhs)) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(tagidx: i64);
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
cgwidentaggedstore(c, tn, rhs, off, sz);
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user