w6c+selfhost: cg_widen_tagged_store basereg + N_ASSIGN tagged field (closes #26)
Extended cg_widen_tagged_store (cstage) / cgwidentaggedstore (wwstage) to take a base_reg/basereg parameter so the primitive supports non-BP destinations. Cstage extends body in-place via via_outer gate + spill+scratch+copy-out; wwstage splits into wrapper (non-BP) + cgwidentaggedstorebp (BP-only) to dodge the no-goto constraint. New N_ASSIGN field TY_TAGGED branch routes through the primitive for all rhs shapes. Scope-adjacent: fieldsize recurses through N_TTAGGED via slotsize and TNAME-aliased-to-tagged via aliaslookup. Needed for the test fixtures. Wwstage read-side N_DOT-of-tagged-field source is filed as task #28; test rows use mark-canary verification until that lands.
This commit is contained in:
@@ -423,6 +423,72 @@ static const struct row rows[] = {
|
||||
" return 42;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
/* Whole-tagged rhs to struct tagged-union field (task #26). cgen
|
||||
* N_ASSIGN had a TY_TAGGED branch that called cg_tag_for_variant
|
||||
* with the rhs type — when the rhs was the tagged union itself
|
||||
* (not a concrete variant), the function returned -1 and the
|
||||
* branch wrote literal 0 as the tag plus only AX as the payload,
|
||||
* dropping the original tag and trailing payload words. Fixed
|
||||
* by routing through cg_widen_tagged_store, which copies all slot
|
||||
* words from the source's tagged slot and runs cg_widen_tag_remap.
|
||||
*
|
||||
* Verification: the write must not bleed into the trailing `mark`
|
||||
* field. Direct struct local. Reading x.e back via match is a
|
||||
* separate code path (gated on a wwstage read-side bug for
|
||||
* tagged N_DOT — filed below as a follow-up); these rows pin the
|
||||
* write-side bug and let mark act as a canary for stray stores
|
||||
* past the field boundary. */
|
||||
{ "tagged_field_value_write_local_mark_canary",
|
||||
"type ev = (i64 | i32);\n"
|
||||
"type holder = struct { e: ev, mark: i32 };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let v: ev = 33: i32;\n"
|
||||
" let x: holder;\n"
|
||||
" x.mark = 0x3a3a3a3a;\n"
|
||||
" x.e = v;\n"
|
||||
" if (x.mark != 0x3a3a3a3a) { return 1; };\n"
|
||||
" return 42;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
/* Same shape, concrete-variant str widening. The str payload
|
||||
* occupies +8/+16 (ptr/len); a missing store to +16 (the pre-#26
|
||||
* shape that wrote only AX) would leave .len at whatever the
|
||||
* neighbouring memory held. mark canary at +24 catches a stray
|
||||
* AX-only path that wrote past 16B into the mark slot. */
|
||||
{ "tagged_field_value_write_str_variant_mark_canary",
|
||||
"type ev = (i32 | str);\n"
|
||||
"type holder = struct { e: ev, mark: i32 };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let x: holder;\n"
|
||||
" x.mark = 0x5e5e5e5e;\n"
|
||||
" x.e = (\"hello\": ev);\n"
|
||||
" if (x.mark != 0x5e5e5e5e) { return 1; };\n"
|
||||
" return 42;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
/* Pointer-rooted dst — `(*p).e = v` exercises the base-reg
|
||||
* parameterisation added to cg_widen_tagged_store in #26. The
|
||||
* wrapper spills BX (the *struct pointer), routes the body
|
||||
* through a BP-rooted scratch, reloads BX, and word-copies the
|
||||
* scratch to (BX, foff). Without that path the via_ptr arm of
|
||||
* the new TY_TAGGED branch would either trample BX during cgexpr
|
||||
* or write to a stale slot address. mark canary at +24 catches
|
||||
* a stray spill that wrote past the e field. */
|
||||
{ "tagged_field_value_write_via_ptr_mark_canary",
|
||||
"type ev = (i64 | i32);\n"
|
||||
"type holder = struct { e: ev, mark: i32 };\n"
|
||||
"fn fill(h: *holder) void = {\n"
|
||||
" let v: ev = 33: i32;\n"
|
||||
" h.mark = 0x77777777;\n"
|
||||
" h.e = v;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let x: holder;\n"
|
||||
" fill(&x);\n"
|
||||
" if (x.mark != 0x77777777) { return 1; };\n"
|
||||
" return 42;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
};
|
||||
|
||||
static int
|
||||
|
||||
Reference in New Issue
Block a user