w6c+wwstage: agree on mixed-scalar tuple sret layout (#240)
An over-cap tuple mixing a scalar with slices/str (e.g. (int,[]u8,str),
56B) laid out differently in the two stages — gate-blind, since no
bootstrap path returns such a tuple. Two silent cs!=ww bugs, one per
ABI side:
- callee SEND (cstage cgen.c N_RETURN over-cap-tuple arm): foff
advanced by the LITERAL expression's type size. A bare int literal
element is stamped TY_UNTYPED_INT (size 0), so `e->type->size`
added 0 for a leading scalar — the next element clobbered it at
offset 0 and every trailing element packed 8 bytes low. wwstage
already sized from the return-type tuple (c.fnret.list), so the
callee frames diverged. Fix: size foff from cg_ret_type's tuple
params (rule-13 type table), aligning cstage to wwstage and to the
t.N reader's f->offset.
- caller RECEIVE (wwstage cgenstmt.ww cglet N_TTUPLE arm): the
in-cap register tuple-receive branch had no capacity gate, so a
56B over-cap tuple was received via AX/DX/CX/R8 (+ R8 fill)
instead of from the sret dest the callee wrote. cstage gates the
twin branch on `sz == 16 || sz == 32` and falls over-cap tuples
through to the sret receive. Fix: add the same size gate to
wwstage, aligning it to cstage.
Both stages now emit byte-identical asm and the value round-trips.
Regen w6c + wwdump combined.ww (cgenstmt embeds in both).
New 940_mixed_scalar_tuple_sret_run: leading/trailing/middle scalar
shapes, annotated + inferred let, each self-asserting every element
(scalar direct, slice/str via len) — both drivers exit 0 + cs==ww
byte-id (12/12).
This commit is contained in:
@@ -27296,6 +27296,14 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
// positional slot (eoff steps by the element's slot size: a
|
||||
// slice/str takes its 24B header). str IS []u8 (24B) → 32B tuple
|
||||
// (#1/Phase 3, task #5). Mirror of the cstage unified branch.
|
||||
//
|
||||
// #240: cap-gate on sz (16/32 = in-cap, AX/DX/CX/R8). Without it
|
||||
// an OVER-cap mixed-scalar tuple (e.g. (int,[]u8,str), 56B) was
|
||||
// received here via the register cursor (4 GP regs + R8 fill)
|
||||
// instead of from the sret dest the callee actually wrote — a
|
||||
// silent cs!=ww divergence (cstage gates the twin branch on
|
||||
// `sz == 16 || sz == 32`, cgen.c N_LET, and falls an over-cap
|
||||
// tuple through to the sret receive below).
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == nkind.N_TTUPLE) {
|
||||
let p0: *node = n.lhs.list;
|
||||
@@ -27311,7 +27319,7 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
|| isslicetype(c, p1t);
|
||||
if (p0 != nil) {
|
||||
if (p1 != nil) {
|
||||
if (s0_is_str != s1_is_str) {
|
||||
if ((s0_is_str != s1_is_str) && (sz == 16 || sz == 32)) {
|
||||
cgexpr(c, rhs);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
|
||||
@@ -1084,6 +1084,14 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
// positional slot (eoff steps by the element's slot size: a
|
||||
// slice/str takes its 24B header). str IS []u8 (24B) → 32B tuple
|
||||
// (#1/Phase 3, task #5). Mirror of the cstage unified branch.
|
||||
//
|
||||
// #240: cap-gate on sz (16/32 = in-cap, AX/DX/CX/R8). Without it
|
||||
// an OVER-cap mixed-scalar tuple (e.g. (int,[]u8,str), 56B) was
|
||||
// received here via the register cursor (4 GP regs + R8 fill)
|
||||
// instead of from the sret dest the callee actually wrote — a
|
||||
// silent cs!=ww divergence (cstage gates the twin branch on
|
||||
// `sz == 16 || sz == 32`, cgen.c N_LET, and falls an over-cap
|
||||
// tuple through to the sret receive below).
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == nkind.N_TTUPLE) {
|
||||
let p0: *node = n.lhs.list;
|
||||
@@ -1099,7 +1107,7 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
|| isslicetype(c, p1t);
|
||||
if (p0 != nil) {
|
||||
if (p1 != nil) {
|
||||
if (s0_is_str != s1_is_str) {
|
||||
if ((s0_is_str != s1_is_str) && (sz == 16 || sz == 32)) {
|
||||
cgexpr(c, rhs);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
|
||||
@@ -27296,6 +27296,14 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
// positional slot (eoff steps by the element's slot size: a
|
||||
// slice/str takes its 24B header). str IS []u8 (24B) → 32B tuple
|
||||
// (#1/Phase 3, task #5). Mirror of the cstage unified branch.
|
||||
//
|
||||
// #240: cap-gate on sz (16/32 = in-cap, AX/DX/CX/R8). Without it
|
||||
// an OVER-cap mixed-scalar tuple (e.g. (int,[]u8,str), 56B) was
|
||||
// received here via the register cursor (4 GP regs + R8 fill)
|
||||
// instead of from the sret dest the callee actually wrote — a
|
||||
// silent cs!=ww divergence (cstage gates the twin branch on
|
||||
// `sz == 16 || sz == 32`, cgen.c N_LET, and falls an over-cap
|
||||
// tuple through to the sret receive below).
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == nkind.N_TTUPLE) {
|
||||
let p0: *node = n.lhs.list;
|
||||
@@ -27311,7 +27319,7 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
|| isslicetype(c, p1t);
|
||||
if (p0 != nil) {
|
||||
if (p1 != nil) {
|
||||
if (s0_is_str != s1_is_str) {
|
||||
if ((s0_is_str != s1_is_str) && (sz == 16 || sz == 32)) {
|
||||
cgexpr(c, rhs);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
|
||||
Reference in New Issue
Block a user