wwstage: align direct-ptr tagged-field READ word-order to cstage (#17)
A >32B tagged-union field (slice payload) read through a direct *struct pointer byte-diverged: wwstage's cgloadtaggedfield always loaded R8@+24 before CX@+16, but cstage's direct-*struct-ptr arm (cgen.c ~11926) loads in offset order CX@+16 then R8@+24. Both ran correct -- a pre-existing rule-10 asm divergence, for a local *struct ptr as well as a global one. Thread a cxlast flag through cgloadtaggedfield: the direct-ptr site (cgptrfieldload, the shared local+global chokepoint) passes cxlast=false to match cstage's offset order; the other 5 callers keep cxlast=true (byte unchanged). A global flip was rejected -- it would clobber the CX-base callers (CX@+16 first destroys the base before the R8@+24 read), and the chained-BX caller must stay R8-first to mirror cstage's chained twin (cgen.c ~12021); the order is a genuine per-arm property of cstage, not derivable from the base register. Test: +2 rows (tagged_slice_field via global *struct ptr, _local via local *struct ptr), runtime + byte-id; both proven to fail byte-id with only the compiler files reverted.
This commit is contained in:
@@ -96,6 +96,30 @@ static const struct row ROWS[] = {
|
||||
"export fn main() i32 = { let s: S = S{a=0,t=5i32}; gp = &s;"
|
||||
" let v: i32 = 0; match (gp.t) { case let x: i32 => v = x;"
|
||||
" case let y: i64 => v = y: i32; }; return v; };\n", 5 },
|
||||
{ "tagged_slice_field",
|
||||
/* #17: a 32B slice-payload tagged field (8B tag + 24B slice =
|
||||
* full 4-reg cursor) read via global *struct ptr. cgloadtaggedfield
|
||||
* fills AX/DX/CX/R8; wwstage loaded R8@+24 before CX@+16 while
|
||||
* cstage's direct *struct-ptr arm loads them in offset order, so the
|
||||
* .s byte-diverged (both ran correct). The 16B tagged_field row above
|
||||
* is too small to fire the R8/CX split. */
|
||||
"package main;\n"
|
||||
"type S = struct { a: i64, t: (i64 | []u8) };\n"
|
||||
"let gp: *S = nil;\n"
|
||||
"export fn main() i32 = { let s: S = S{a=0,t=3i64}; gp = &s;"
|
||||
" let v: i32 = 0; match (gp.t) { case let x: i64 => v = x: i32;"
|
||||
" case let y: []u8 => v = len(y): i32; }; return v; };\n", 3 },
|
||||
{ "tagged_slice_field_local",
|
||||
/* #17 twin: same 32B slice-payload tagged read, but via a LOCAL
|
||||
* *struct ptr. cgptrfieldload is shared by the local (MOVQ off(BP),BX)
|
||||
* and global (MOVQ name(SB),BX) direct *struct-ptr arms, so the same
|
||||
* cxlast=false fix closes both — this row locks the local path against
|
||||
* a future regression of the R8/CX order. */
|
||||
"package main;\n"
|
||||
"type S = struct { a: i64, t: (i64 | []u8) };\n"
|
||||
"export fn main() i32 = { let s: S = S{a=0,t=3i64}; let p: *S = &s;"
|
||||
" let v: i32 = 0; match (p.t) { case let x: i64 => v = x: i32;"
|
||||
" case let y: []u8 => v = len(y): i32; }; return v; };\n", 3 },
|
||||
{ "pslice_len",
|
||||
"package main;\n"
|
||||
"let gp: *[]u8 = nil;\n"
|
||||
|
||||
Reference in New Issue
Block a user