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:
@@ -3828,7 +3828,8 @@ fn taggedidcastpeel(c: *cgen, e: *syntax.node) *syntax.node = {
|
||||
// Callers must guarantee basereg is one of "BP", "BX", "CX"; the
|
||||
// only register loaded into that is NOT a target is BX, so AX-
|
||||
// or DX-rooted callers must spill first.
|
||||
fn cgloadtaggedfield(c: *cgen, basereg: str, foff: i32, slot_sz: i32) void = {
|
||||
fn cgloadtaggedfield(c: *cgen, basereg: str, foff: i32, slot_sz: i32,
|
||||
cxlast: bool) void = {
|
||||
// #37: >32B box — leave its ADDRESS in AX (taggedmemread, the
|
||||
// sret-receive convention); the 4-reg cursor walk below would
|
||||
// truncate past payload word 2. Mirrors cstage's N_DOT
|
||||
@@ -3847,17 +3848,34 @@ fn cgloadtaggedfield(c: *cgen, basereg: str, foff: i32, slot_sz: i32) void = {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((foff + 8): i64, basereg);
|
||||
emitline(", DX\n");
|
||||
// word2 → R8 (slice variant: slot = 8 tag + 24 payload = 32).
|
||||
if (slot_sz > 24) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((foff + 24): i64, basereg);
|
||||
emitline(", R8\n");
|
||||
};
|
||||
// word1 → CX (load LAST; conflicts with CX-base globals).
|
||||
if (slot_sz > 16) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((foff + 16): i64, basereg);
|
||||
emitline(", CX\n");
|
||||
// word1 → CX, word2 → R8. cstage's direct *struct-ptr arm (cgen.c
|
||||
// N_DOT TY_PTR→TY_STRUCT, ~11926) loads them in strict offset order
|
||||
// (CX@+16 then R8@+24, BX is not a cursor target); every other arm —
|
||||
// local/global struct (CX may be the base addr) and the chained-
|
||||
// *struct twin (~12021) — loads R8 BEFORE CX. cxlast selects: true =
|
||||
// R8 then CX, false = offset order.
|
||||
if (cxlast) {
|
||||
if (slot_sz > 24) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((foff + 24): i64, basereg);
|
||||
emitline(", R8\n");
|
||||
};
|
||||
if (slot_sz > 16) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((foff + 16): i64, basereg);
|
||||
emitline(", CX\n");
|
||||
};
|
||||
} else {
|
||||
if (slot_sz > 16) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((foff + 16): i64, basereg);
|
||||
emitline(", CX\n");
|
||||
};
|
||||
if (slot_sz > 24) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((foff + 24): i64, basereg);
|
||||
emitline(", R8\n");
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user