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:
@@ -955,6 +955,173 @@ static const struct row rows[] = {
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};", 42 },
|
||||
/* Tagged-union element in a fixed array — scalar+str variants.
|
||||
* Store via N_INDEX widening, read+match through cgexpr N_INDEX
|
||||
* tagged-slot load. 10 + len("hi")=2 + 5 = 17. */
|
||||
{ "fn main() i32 = {\n"
|
||||
" let arr: [3](i32 | str);\n"
|
||||
" arr[0] = 10;\n"
|
||||
" arr[1] = \"hi\";\n"
|
||||
" arr[2] = 5;\n"
|
||||
" let s: i32 = 0;\n"
|
||||
" let i: i32 = 0;\n"
|
||||
" for (i < 3) {\n"
|
||||
" match (arr[i]) {\n"
|
||||
" case let v: i32 => s += v;\n"
|
||||
" case let t: str => s += t.len: i32;\n"
|
||||
" };\n"
|
||||
" i += 1;\n"
|
||||
" };\n"
|
||||
" return s;\n"
|
||||
"};", 17 },
|
||||
/* Tagged-union array with struct payload variant. Struct fields
|
||||
* are written at slot+8+field_off via cg_widen_tagged_store; the
|
||||
* read side just copies slot bytes into AX/DX/CX for match.
|
||||
* 5 + (7+11) + 2 + 9 = 34. */
|
||||
{ "type pair = struct { a: i32, b: i32 };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let arr: [4](i32 | pair | str);\n"
|
||||
" arr[0] = 5;\n"
|
||||
" arr[1] = pair { a = 7, b = 11 };\n"
|
||||
" arr[2] = \"yo\";\n"
|
||||
" arr[3] = 9;\n"
|
||||
" let s: i32 = 0;\n"
|
||||
" let i: i32 = 0;\n"
|
||||
" for (i < 4) {\n"
|
||||
" match (arr[i]) {\n"
|
||||
" case let v: i32 => s += v;\n"
|
||||
" case let p: pair => s += p.a + p.b;\n"
|
||||
" case let t: str => s += t.len: i32;\n"
|
||||
" };\n"
|
||||
" i += 1;\n"
|
||||
" };\n"
|
||||
" return s;\n"
|
||||
"};", 34 },
|
||||
/* Slicing an array of tagged elements — the slice load path
|
||||
* uses the fallback (non-ident base) N_INDEX which loads slot
|
||||
* bytes from a computed address. 1 + 2 + 3 + 4 = 10. */
|
||||
{ "fn main() i32 = {\n"
|
||||
" let buf: [4](i32 | str);\n"
|
||||
" buf[0] = 1;\n"
|
||||
" buf[1] = \"ww\";\n"
|
||||
" buf[2] = 3;\n"
|
||||
" buf[3] = 4;\n"
|
||||
" let xs: [](i32 | str) = buf[0:4];\n"
|
||||
" let s: i32 = 0;\n"
|
||||
" let i: i32 = 0;\n"
|
||||
" for (i < 4) {\n"
|
||||
" match (xs[i]) {\n"
|
||||
" case let v: i32 => s += v;\n"
|
||||
" case let t: str => s += t.len: i32;\n"
|
||||
" };\n"
|
||||
" i += 1;\n"
|
||||
" };\n"
|
||||
" return s;\n"
|
||||
"};", 10 },
|
||||
/* Passing arr[i] (tagged element) as a tagged arg — cgexpr leaves
|
||||
* the slot in AX/DX/CX which the call-site shuffle pushes onto
|
||||
* the arg stack. 5 + 30 (len 3 * 10) + 7 = 42. */
|
||||
{ "fn weight(v: (i32 | str)) i32 = {\n"
|
||||
" match (v) {\n"
|
||||
" case let n: i32 => return n;\n"
|
||||
" case let s: str => return s.len: i32 * 10;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let arr: [3](i32 | str);\n"
|
||||
" arr[0] = 5;\n"
|
||||
" arr[1] = \"abc\";\n"
|
||||
" arr[2] = 7;\n"
|
||||
" let s: i32 = 0;\n"
|
||||
" let i: i32 = 0;\n"
|
||||
" for (i < 3) {\n"
|
||||
" s += weight(arr[i]);\n"
|
||||
" i += 1;\n"
|
||||
" };\n"
|
||||
" return s;\n"
|
||||
"};", 42 },
|
||||
/* let-init of a tagged local from arr[i] — the let path routes
|
||||
* through cg_widen_tagged_store which, for a tagged source via
|
||||
* cgexpr, spills AX/DX/CX into the slot. "ww!".len == 3. */
|
||||
{ "fn main() i32 = {\n"
|
||||
" let arr: [3](i32 | str);\n"
|
||||
" arr[0] = 11;\n"
|
||||
" arr[1] = \"ww!\";\n"
|
||||
" arr[2] = 7;\n"
|
||||
" let r: (i32 | str) = arr[1];\n"
|
||||
" match (r) {\n"
|
||||
" case let n: i32 => return n;\n"
|
||||
" case let s: str => return s.len: i32;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};", 3 },
|
||||
/* Tagged-subset store into a wider tagged-union array element:
|
||||
* source slot is (i32|str), dest element is (i32|str|u64). The
|
||||
* store path materialises the subset in scratch then copies slot
|
||||
* bytes — tag remap is a no-op here (variant order matches).
|
||||
* arr[0]=9:i32, arr[1]="hi":str → 9 + 2 = 11. */
|
||||
{ "type inner = (i32 | str);\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let arr: [2](i32 | str | u64);\n"
|
||||
" let v: inner = 9;\n"
|
||||
" arr[0] = v;\n"
|
||||
" let w: inner = \"hi\";\n"
|
||||
" arr[1] = w;\n"
|
||||
" let s: i32 = 0;\n"
|
||||
" let i: i32 = 0;\n"
|
||||
" for (i < 2) {\n"
|
||||
" match (arr[i]) {\n"
|
||||
" case let n: i32 => s += n;\n"
|
||||
" case let t: str => s += t.len: i32;\n"
|
||||
" case let u: u64 => s += 100;\n"
|
||||
" };\n"
|
||||
" i += 1;\n"
|
||||
" };\n"
|
||||
" return s;\n"
|
||||
"};", 11 },
|
||||
/* Returning arr[i] from a fn whose return type matches the
|
||||
* element. cgexpr leaves slot in AX/DX/CX; the return path
|
||||
* forwards as-is. arr[1] = "abc" → str variant → .len == 3. */
|
||||
{ "fn pick(i: i32) (i32 | str) = {\n"
|
||||
" let arr: [2](i32 | str);\n"
|
||||
" arr[0] = 21;\n"
|
||||
" arr[1] = \"abc\";\n"
|
||||
" return arr[i];\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let r: (i32 | str) = pick(1);\n"
|
||||
" match (r) {\n"
|
||||
" case let n: i32 => return n;\n"
|
||||
" case let s: str => return s.len: i32;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};", 3 },
|
||||
/* Nullable folded element `(*T | void)` — the slot is one 8B
|
||||
* pointer word; null is the void variant. Stores route through
|
||||
* the nullable branch of cg_widen_tagged_store (single MOVQ at
|
||||
* +0). 100 (nil) + 1 (non-nil) + 100 (nil) = 201. */
|
||||
{ "fn pickptr(b: bool) *i32 = {\n"
|
||||
" let x: i32 = 42;\n"
|
||||
" if (b) { return &x; };\n"
|
||||
" return nil;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let arr: [3](*i32 | void);\n"
|
||||
" arr[0] = nil;\n"
|
||||
" arr[1] = pickptr(true);\n"
|
||||
" arr[2] = pickptr(false);\n"
|
||||
" let s: i32 = 0;\n"
|
||||
" let i: i32 = 0;\n"
|
||||
" for (i < 3) {\n"
|
||||
" match (arr[i]) {\n"
|
||||
" case let p: *i32 => s += 1;\n"
|
||||
" case => s += 100;\n"
|
||||
" };\n"
|
||||
" i += 1;\n"
|
||||
" };\n"
|
||||
" return s;\n"
|
||||
"};", 201 },
|
||||
/* Plan 9-style sentinel error idiom: `def NAME: error = "lit"`
|
||||
* inlines as the (ptr, len) pair at use sites. */
|
||||
{ "type error = str;\n"
|
||||
|
||||
Reference in New Issue
Block a user