diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 3d3ccf87..f83e4bea 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -13426,7 +13426,9 @@ fn cgindex(c: *cgen, n: *node) void = { emitline("\tMOVQ\t(BX), AX\n"); return; }; - if (esz == 16) { + // #43: str element-stride routes through primtypesize so the + // 16-vs-24 dispatch tracks ty_str.size for #1. + if (esz == primtypesize("str"): i32) { emitline("\tMOVQ\t8(BX), CX\n"); emitline("\tMOVQ\t(BX), AX\n"); emitline("\tMOVQ\tCX, BX\n"); @@ -13465,9 +13467,10 @@ fn cgindex(c: *cgen, n: *node) void = { emitline("\tMOVQ\t(BX), AX\n"); return; }; - // str element (16B): load (ptr, len) into (AX, BX) so + // str element (16B today): load (ptr, len) into (AX, BX) so // the value flows through the str-rhs convention. - if (esz == 16) { + // #43: route via primtypesize so the stride tracks #1. + if (esz == primtypesize("str"): i32) { emitline("\tMOVQ\t8(BX), CX\n"); emitline("\tMOVQ\t(BX), AX\n"); emitline("\tMOVQ\tCX, BX\n"); @@ -13497,7 +13500,9 @@ fn cgindex(c: *cgen, n: *node) void = { emitline("\tMOVQ\t(BX), AX\n"); return; }; - if (esz == 16) { + // #43: str element-stride routes through primtypesize so the + // 16-vs-24 dispatch tracks ty_str.size for #1. + if (esz == primtypesize("str"): i32) { emitline("\tMOVQ\t8(AX), BX\n"); emitline("\tMOVQ\t(AX), AX\n"); return; @@ -16391,7 +16396,10 @@ fn cgassign(c: *cgen, n: *node) void = { }; }; cgexpr(c, n.rhs); // value → AX - if (esz == 16) { emitline("\tPUSHQ\tBX\n"); }; + // #43: spill BX (str.len) before computing + // the index so the post-index store can pop + // it; the stride gate tracks ty_str.size. + if (esz == primtypesize("str"): i32) { emitline("\tPUSHQ\tBX\n"); }; emitline("\tPUSHQ\tAX\n"); cgexpr(c, idx); // idx → AX if (esz > 1) { @@ -16429,7 +16437,10 @@ fn cgassign(c: *cgen, n: *node) void = { emitline("\tPOPQ\tAX\n"); // scaled idx emitline("\tADDQ\tAX, BX\n"); emitline("\tPOPQ\tAX\n"); // value - if (esz == 16) { + // #43: str-element write — pop the saved + // .len and store both halves. Stride gate + // routes through primtypesize for #1. + if (esz == primtypesize("str"): i32) { emitline("\tMOVQ\tAX, (BX)\n"); emitline("\tPOPQ\tCX\n"); emitline("\tMOVQ\tCX, 8(BX)\n"); @@ -19001,8 +19012,13 @@ fn cglet(c: *cgen, n: *node) void = { }; }; }; let shapeok: bool = false; + // #43: route the slice-shape size guard through SSoT. + // The N_TSLICE kind gate already discriminates here, so + // this is belt-and-suspenders, but the literal would + // silently miss after #1 if check.ww's astsize ever + // drifted from this dispatch. if (scall != nil && tn != nil - && tn.kind == nkind.N_TSLICE && sz == 24) { + && tn.kind == nkind.N_TSLICE && sz == tyslicesize(): i32) { let callee: *node = scall.lhs; let a0: *node = scall.list; let a1: *node = nil; @@ -19417,13 +19433,20 @@ fn cglet(c: *cgen, n: *node) void = { emitoff(off: i64); emitline("(BP)\n"); // str init: cgexpr also leaves len in BX; store both. - if (sz == 16) { + // #43: route through primtypesize so #1's str-size bump + // surfaces this site (today sz==16 picks str; once str=24, + // the in-flight str ABI (#34) must propagate cap and the + // dispatch needs reshaping by kind, not raw size). + if (sz == primtypesize("str"): i32) { emitline("\tMOVQ\tBX, "); emitoff((off + 8): i64); emitline("(BP)\n"); }; // slice init: ptr/len/cap in AX/BX/CX. - if (sz == 24) { + // #43: same routing — bare `sz == 24` would collide with a + // 24B str slot under #1; the kind disambiguation lives at + // the caller (cglet's tn.kind == N_TSLICE shape check). + if (sz == tyslicesize(): i32) { emitline("\tMOVQ\tBX, "); emitoff((off + 8): i64); emitline("(BP)\n"); @@ -21678,7 +21701,10 @@ export fn letpreintern(c: *cgen, file: *node) void = { for (d != nil) { if (d.kind == nkind.N_LET) { let sz: i32 = letemitsize(c, d); - if (sz == 16) { + // #43: route the str-let gate through primtypesize so + // #1 doesn't desync this with emitletdataw's matching + // `sz == primtypesize("str"): i32` strlit-init branch. + if (sz == primtypesize("str"): i32) { let r: *node = d.rhs; for (r != nil) { if (r.kind != nkind.N_CAST) { break; }; diff --git a/selfhost/cmd/wcc/cgen.ww b/selfhost/cmd/wcc/cgen.ww index 8838493a..79f97a1d 100644 --- a/selfhost/cmd/wcc/cgen.ww +++ b/selfhost/cmd/wcc/cgen.ww @@ -1199,7 +1199,10 @@ export fn letpreintern(c: *cgen, file: *node) void = { for (d != nil) { if (d.kind == nkind.N_LET) { let sz: i32 = letemitsize(c, d); - if (sz == 16) { + // #43: route the str-let gate through primtypesize so + // #1 doesn't desync this with emitletdataw's matching + // `sz == primtypesize("str"): i32` strlit-init branch. + if (sz == primtypesize("str"): i32) { let r: *node = d.rhs; for (r != nil) { if (r.kind != nkind.N_CAST) { break; }; diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index 0db26d0a..b1ba8aaa 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -799,7 +799,9 @@ fn cgindex(c: *cgen, n: *node) void = { emitline("\tMOVQ\t(BX), AX\n"); return; }; - if (esz == 16) { + // #43: str element-stride routes through primtypesize so the + // 16-vs-24 dispatch tracks ty_str.size for #1. + if (esz == primtypesize("str"): i32) { emitline("\tMOVQ\t8(BX), CX\n"); emitline("\tMOVQ\t(BX), AX\n"); emitline("\tMOVQ\tCX, BX\n"); @@ -838,9 +840,10 @@ fn cgindex(c: *cgen, n: *node) void = { emitline("\tMOVQ\t(BX), AX\n"); return; }; - // str element (16B): load (ptr, len) into (AX, BX) so + // str element (16B today): load (ptr, len) into (AX, BX) so // the value flows through the str-rhs convention. - if (esz == 16) { + // #43: route via primtypesize so the stride tracks #1. + if (esz == primtypesize("str"): i32) { emitline("\tMOVQ\t8(BX), CX\n"); emitline("\tMOVQ\t(BX), AX\n"); emitline("\tMOVQ\tCX, BX\n"); @@ -870,7 +873,9 @@ fn cgindex(c: *cgen, n: *node) void = { emitline("\tMOVQ\t(BX), AX\n"); return; }; - if (esz == 16) { + // #43: str element-stride routes through primtypesize so the + // 16-vs-24 dispatch tracks ty_str.size for #1. + if (esz == primtypesize("str"): i32) { emitline("\tMOVQ\t8(AX), BX\n"); emitline("\tMOVQ\t(AX), AX\n"); return; @@ -3764,7 +3769,10 @@ fn cgassign(c: *cgen, n: *node) void = { }; }; cgexpr(c, n.rhs); // value → AX - if (esz == 16) { emitline("\tPUSHQ\tBX\n"); }; + // #43: spill BX (str.len) before computing + // the index so the post-index store can pop + // it; the stride gate tracks ty_str.size. + if (esz == primtypesize("str"): i32) { emitline("\tPUSHQ\tBX\n"); }; emitline("\tPUSHQ\tAX\n"); cgexpr(c, idx); // idx → AX if (esz > 1) { @@ -3802,7 +3810,10 @@ fn cgassign(c: *cgen, n: *node) void = { emitline("\tPOPQ\tAX\n"); // scaled idx emitline("\tADDQ\tAX, BX\n"); emitline("\tPOPQ\tAX\n"); // value - if (esz == 16) { + // #43: str-element write — pop the saved + // .len and store both halves. Stride gate + // routes through primtypesize for #1. + if (esz == primtypesize("str"): i32) { emitline("\tMOVQ\tAX, (BX)\n"); emitline("\tPOPQ\tCX\n"); emitline("\tMOVQ\tCX, 8(BX)\n"); diff --git a/selfhost/cmd/wcc/cgenstmt.ww b/selfhost/cmd/wcc/cgenstmt.ww index 7f53443b..3c207542 100644 --- a/selfhost/cmd/wcc/cgenstmt.ww +++ b/selfhost/cmd/wcc/cgenstmt.ww @@ -603,8 +603,13 @@ fn cglet(c: *cgen, n: *node) void = { }; }; }; let shapeok: bool = false; + // #43: route the slice-shape size guard through SSoT. + // The N_TSLICE kind gate already discriminates here, so + // this is belt-and-suspenders, but the literal would + // silently miss after #1 if check.ww's astsize ever + // drifted from this dispatch. if (scall != nil && tn != nil - && tn.kind == nkind.N_TSLICE && sz == 24) { + && tn.kind == nkind.N_TSLICE && sz == tyslicesize(): i32) { let callee: *node = scall.lhs; let a0: *node = scall.list; let a1: *node = nil; @@ -1019,13 +1024,20 @@ fn cglet(c: *cgen, n: *node) void = { emitoff(off: i64); emitline("(BP)\n"); // str init: cgexpr also leaves len in BX; store both. - if (sz == 16) { + // #43: route through primtypesize so #1's str-size bump + // surfaces this site (today sz==16 picks str; once str=24, + // the in-flight str ABI (#34) must propagate cap and the + // dispatch needs reshaping by kind, not raw size). + if (sz == primtypesize("str"): i32) { emitline("\tMOVQ\tBX, "); emitoff((off + 8): i64); emitline("(BP)\n"); }; // slice init: ptr/len/cap in AX/BX/CX. - if (sz == 24) { + // #43: same routing — bare `sz == 24` would collide with a + // 24B str slot under #1; the kind disambiguation lives at + // the caller (cglet's tn.kind == N_TSLICE shape check). + if (sz == tyslicesize(): i32) { emitline("\tMOVQ\tBX, "); emitoff((off + 8): i64); emitline("(BP)\n"); diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index b7bd0c72..88f6afcc 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -13426,7 +13426,9 @@ fn cgindex(c: *cgen, n: *node) void = { emitline("\tMOVQ\t(BX), AX\n"); return; }; - if (esz == 16) { + // #43: str element-stride routes through primtypesize so the + // 16-vs-24 dispatch tracks ty_str.size for #1. + if (esz == primtypesize("str"): i32) { emitline("\tMOVQ\t8(BX), CX\n"); emitline("\tMOVQ\t(BX), AX\n"); emitline("\tMOVQ\tCX, BX\n"); @@ -13465,9 +13467,10 @@ fn cgindex(c: *cgen, n: *node) void = { emitline("\tMOVQ\t(BX), AX\n"); return; }; - // str element (16B): load (ptr, len) into (AX, BX) so + // str element (16B today): load (ptr, len) into (AX, BX) so // the value flows through the str-rhs convention. - if (esz == 16) { + // #43: route via primtypesize so the stride tracks #1. + if (esz == primtypesize("str"): i32) { emitline("\tMOVQ\t8(BX), CX\n"); emitline("\tMOVQ\t(BX), AX\n"); emitline("\tMOVQ\tCX, BX\n"); @@ -13497,7 +13500,9 @@ fn cgindex(c: *cgen, n: *node) void = { emitline("\tMOVQ\t(BX), AX\n"); return; }; - if (esz == 16) { + // #43: str element-stride routes through primtypesize so the + // 16-vs-24 dispatch tracks ty_str.size for #1. + if (esz == primtypesize("str"): i32) { emitline("\tMOVQ\t8(AX), BX\n"); emitline("\tMOVQ\t(AX), AX\n"); return; @@ -16391,7 +16396,10 @@ fn cgassign(c: *cgen, n: *node) void = { }; }; cgexpr(c, n.rhs); // value → AX - if (esz == 16) { emitline("\tPUSHQ\tBX\n"); }; + // #43: spill BX (str.len) before computing + // the index so the post-index store can pop + // it; the stride gate tracks ty_str.size. + if (esz == primtypesize("str"): i32) { emitline("\tPUSHQ\tBX\n"); }; emitline("\tPUSHQ\tAX\n"); cgexpr(c, idx); // idx → AX if (esz > 1) { @@ -16429,7 +16437,10 @@ fn cgassign(c: *cgen, n: *node) void = { emitline("\tPOPQ\tAX\n"); // scaled idx emitline("\tADDQ\tAX, BX\n"); emitline("\tPOPQ\tAX\n"); // value - if (esz == 16) { + // #43: str-element write — pop the saved + // .len and store both halves. Stride gate + // routes through primtypesize for #1. + if (esz == primtypesize("str"): i32) { emitline("\tMOVQ\tAX, (BX)\n"); emitline("\tPOPQ\tCX\n"); emitline("\tMOVQ\tCX, 8(BX)\n"); @@ -19001,8 +19012,13 @@ fn cglet(c: *cgen, n: *node) void = { }; }; }; let shapeok: bool = false; + // #43: route the slice-shape size guard through SSoT. + // The N_TSLICE kind gate already discriminates here, so + // this is belt-and-suspenders, but the literal would + // silently miss after #1 if check.ww's astsize ever + // drifted from this dispatch. if (scall != nil && tn != nil - && tn.kind == nkind.N_TSLICE && sz == 24) { + && tn.kind == nkind.N_TSLICE && sz == tyslicesize(): i32) { let callee: *node = scall.lhs; let a0: *node = scall.list; let a1: *node = nil; @@ -19417,13 +19433,20 @@ fn cglet(c: *cgen, n: *node) void = { emitoff(off: i64); emitline("(BP)\n"); // str init: cgexpr also leaves len in BX; store both. - if (sz == 16) { + // #43: route through primtypesize so #1's str-size bump + // surfaces this site (today sz==16 picks str; once str=24, + // the in-flight str ABI (#34) must propagate cap and the + // dispatch needs reshaping by kind, not raw size). + if (sz == primtypesize("str"): i32) { emitline("\tMOVQ\tBX, "); emitoff((off + 8): i64); emitline("(BP)\n"); }; // slice init: ptr/len/cap in AX/BX/CX. - if (sz == 24) { + // #43: same routing — bare `sz == 24` would collide with a + // 24B str slot under #1; the kind disambiguation lives at + // the caller (cglet's tn.kind == N_TSLICE shape check). + if (sz == tyslicesize(): i32) { emitline("\tMOVQ\tBX, "); emitoff((off + 8): i64); emitline("(BP)\n"); @@ -21678,7 +21701,10 @@ export fn letpreintern(c: *cgen, file: *node) void = { for (d != nil) { if (d.kind == nkind.N_LET) { let sz: i32 = letemitsize(c, d); - if (sz == 16) { + // #43: route the str-let gate through primtypesize so + // #1 doesn't desync this with emitletdataw's matching + // `sz == primtypesize("str"): i32` strlit-init branch. + if (sz == primtypesize("str"): i32) { let r: *node = d.rhs; for (r != nil) { if (r.kind != nkind.N_CAST) { break; };