wcc+w6c+w6c_ww: tagged tuple elements take their full slot — tuple_eslot accessor (#22a)
slot = roundup8(size(elem)) — 8B is a FLOOR, not a ceiling (user- ratified 2026-06-04; the #237 fieldslotsize precedent; (str,str)=48B predates this — tagged was the one truncated >8B kind). Pre-fix the checkers truncated a tagged element to one 8B slot and every cgen transport walk strode wide=(STR||SLICE)-else-8: cstage read the NEIGHBOR slot, wwstage read ZEROS — both-wrong-differently, so the byte-id gates were blind (prober-9 PG1, /tmp/p9). One stride accessor per stage — tuple_eslot (cgen.c) / tupeslot (cgenstmt.ww) — now feeds every tuple walk; the per-site predicates are deleted as absorbed. Sizer: check.c N_TTUPLE + check.ww tupleelemslot gain the TY_TAGGED arm (astsize already rides the type table since commit 0). Routes flipped to the accessor, both stages symmetric: cursor producers (lit/slot-to-cursor; tagged ident elements load their box from the slot — cgexpr's tagged ident load is word0-only), let-receive (tuple_store/tupstore generalized to eslot/8 words), N_RETURN send, by-value param receive, arg restage/drain (tagged stays loud per C-t2), destructure (MLET/MASSIGN, ident + sret + in-cap), t.N read + len(t.N) + global-g.N offset walks (t.N gains the tagged box load: AX=tag, DX/CX/R8=payload — the is/as spill cursor), sret classifier, DATA emit. wwstage cgtagvariantidx gains the #67 stamped-carrier arm (flatvariantidxt on .type_) — its AST-only key silently clamped 't.0 is size' to tag 0; fused here because the tuple-element read this commit wires is its only exercisable consumer. Exit invariant: zero silent tagged-tuple paths — in-cap shapes (<=4 GP eightbytes) are correct end-to-end; everything else is LOUD: over-cap sret return (#22b, task #28), call-arg (C-t2 #32), non-local literal element sources (#22b/#23), tuple-in-union payload (#242/#22b), global static-init, element write (pre-existing). Closure proof-grep at HEAD: 'tuple_ebytes|tupebytes' -> 0 hits; 'wide.*=.*(TY_SLICE|TY_STR)' tuple-walk survivors are all behind tagged loud-guards (cgen.c:2535/2568/12013 widen-store + over-cap send; cgenutil.ww:3527 twin). Latent cross-checks closed by the accessor: wwstage MLET-sret strode esz (4 for i32) vs cstage 8; wwstage param-receive strode slotsize (composite slotsize) vs cstage 8; both now the accessor's floor-8. Tagged inits in pins use the CAST form (5: size) — the bare untyped-int widen-store mis-tag is pre-existing at master and filed (task #33). 941 gains 13 rows: t22_* size/align folds (+ void-elem 0-slot pin), the full runtime round-trip (read, is/as, both element orders, void variant, destructure, literal-let), two-tagged-elem in-cap and float+tagged in BOTH orders (ken k1/k2 regression-pinned as rows), t.N-as-call-arg, 5 loud pins (arg, over-cap return, call-source element, global init, element write), and the sret_narrow_mix_* triple below. Runtime rows exit-checked under both drivers + byte-id. 129/129; unit tests green; sizelint clean. ken R1 (re-validation fix): an OVER-CAP tuple init whose rhs is not a CALL fell past every cstage N_LET store arm to NOTHING — silent uninitialized-frame reads — while wwstage loud-rejected the same shape. Pre-existing for (str,str) literals; the #22a tagged slots routed >16B-box tagged shapes into it (k5b/k5c/k5d, base-correct because base truncated them in-cap). cstage now routes the rhs through cgexpr (the cursor materialisers carry the exact wwstage loud texts) with a trailing fatal as the net; 941 pins both the tagged and the (str,str) spellings of the hole. reviewer-22 (review fixes, folded): the MLET-receive slot flip above landed ALONE on the wwstage over-cap sret family — the ww RETURN-send and MASSIGN-receive still strode packed esz (4 for u32), so `let (a,b,c) = f()` over (str,u32,str) read c at slot offset 32 while ww's send wrote it packed at 28: a ww runtime REGRESSION vs base (base was packed-consistent on both sides — ran right, byte-diff). Both walks now stride the slot (send mirrors cstage's `wide ? esz : 8`; MASSIGN strides tupeslotn) — closing, in the same stroke, the pre-existing base skews: ww `let t = f()` over-cap narrow-mix read (reader slot-laid vs send packed, runtime-wrong at base) and the cs≠ww asm on all three routings. Second find: t.N tagged element as a CALL ARG — cstage rides its generic node_istaggedarg cursor push, but wwstage's kind-gated aistagged missed N_DOT and mis-routed the box into the widening branch (taggedvariantindex -1 clamped to 0: callee read variant 0, silent, ww-only). cgenutil.ww gains the N_DOT arm (aistagged + pushargsrev), the #67 stamped-carrier twin of the N_INDEX (#12) arm. The N_DOT arm was a LIVE wwstage stdlib miscompile: hextest's import graph carried two t.N tagged-arg sites (base .s diff = exactly the clamped-tag PUSHQ pair) — the 989 lib-byteid ratchet caught the convergence and #59.4 graduates to M_ID (runtime-correct per 979_hex_run). Probes: /tmp/r22. Residual filed: tagged arg from deref/cast/unwrap sources is a word0-only read on BOTH stages (pre-existing, base-confirmed) — generalizing wwstage's kind gate to cstage's type-keyed check rides that task.
This commit is contained in:
@@ -132,9 +132,36 @@ fn tupsse(i: i32) str = {
|
||||
return "X1";
|
||||
};
|
||||
|
||||
fn tupebytes(wide: bool) i32 = {
|
||||
if (wide) { return (tyslicesize() / 8i64): i32; };
|
||||
return 1;
|
||||
// tupeslot — THE tuple element-stride accessor (#22): the slot a tuple
|
||||
// element occupies, in bytes. slot = roundup8(size(elem)), 8B a FLOOR
|
||||
// not a ceiling (user-ratified 2026-06-04): str/slice carry their 24B
|
||||
// header, a tagged element its full tag+payload box ((str,str)=48B
|
||||
// predates this; tagged was the one truncated >8B kind — the #237
|
||||
// fieldslotsize precedent), narrow scalars pad UP to one 8B eightbyte.
|
||||
// Every tuple walk (cursor send/receive, t.N read, destructure, sret
|
||||
// classify, DATA emit) takes its stride and its eightbyte count
|
||||
// (eslot/8) from here — the per-site wide=(STR||SLICE)-else-8
|
||||
// predicates this absorbs were the #22 neighbor-slot/zeros miscompile.
|
||||
// Checker twin: check.ww tupleelemslot / check.c N_TTUPLE; cstage twin:
|
||||
// tuple_eslot (cmd/w6c/cgen.c).
|
||||
export fn tupeslot(ti: *tinfo) i32 = {
|
||||
let t: *tinfo = ti;
|
||||
for (t != nil && t.kind == tykind.TY_NAMED) { t = t.under; };
|
||||
if (t == nil) { return 8; };
|
||||
if (t.kind == tykind.TY_VOID) { return 0; };
|
||||
// a literal tuple's stamped element can be untyped_str (size 0) —
|
||||
// it occupies the str header slot (the C-t2 type_isstr lesson).
|
||||
if (t.kind == tykind.TY_UNTYPED_STR) { return tyslicesize(): i32; };
|
||||
if (t.kind == tykind.TY_STR || t.kind == tykind.TY_SLICE ||
|
||||
t.kind == tykind.TY_TAGGED) {
|
||||
return ((t.size + 7u64) & ~7u64): i32;
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
|
||||
export fn tupeslotn(n: *node) i32 = {
|
||||
if (n == nil) { return 8; };
|
||||
return tupeslot(n.type_: *tinfo);
|
||||
};
|
||||
|
||||
// rettupleof — the N_TTUPLE return-type node of an N_CALL rhs (else nil).
|
||||
@@ -201,29 +228,24 @@ fn nodetuplearg(c: *cgen, a: *node) *node = {
|
||||
};
|
||||
|
||||
// tupstore — store the tuple element at register-cursor `cur` into the
|
||||
// BP-relative slot at `off`. A slice/str stores its 3-word {ptr,len,cap}
|
||||
// header (ref/hare/rt/ensure.ha:4-8) at off/+8/+16 from consecutive
|
||||
// INTEGER cursor registers; a float rides the SSE cursor (X0,X1); a
|
||||
// scalar stores 1 INTEGER word. The caller owns the dual cursor
|
||||
// (validated + advanced). Byte-identical to the cstage tuple_store
|
||||
// (cmd/w6c/cgen.c).
|
||||
fn tupstore(c: *cgen, gpcur: i32, ssecur: i32, off: i32, wide: bool, tn: *node) void = {
|
||||
if (wide) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(tupreg(gpcur + 0));
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(tupreg(gpcur + 1));
|
||||
emitline(", ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(tupreg(gpcur + 2));
|
||||
emitline(", ");
|
||||
emitoff((off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
// BP-relative slot at `off`. A >8B element (slice/str 3-word
|
||||
// {ptr,len,cap} header, ref/hare/rt/ensure.ha:4-8; tagged tag+payload
|
||||
// box, #22) stores its eslot/8 words from consecutive INTEGER cursor
|
||||
// registers; a float rides the SSE cursor (X0,X1); a scalar stores 1
|
||||
// INTEGER word. The caller owns the dual cursor (validated +
|
||||
// advanced). Byte-identical to the cstage tuple_store (cmd/w6c/cgen.c).
|
||||
fn tupstore(c: *cgen, gpcur: i32, ssecur: i32, off: i32, eslot: i32, tn: *node) void = {
|
||||
if (eslot == 0) { return; }; // void element: the checker's 0-slot
|
||||
if (eslot > 8) {
|
||||
let k: i32 = 0;
|
||||
for (k < eslot / 8) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(tupreg(gpcur + k));
|
||||
emitline(", ");
|
||||
emitoff((off + k * 8): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 1;
|
||||
};
|
||||
return;
|
||||
};
|
||||
// #105 / #164 (#107): an f64/f32 element rides the SSE cursor reg
|
||||
@@ -277,6 +299,67 @@ fn tupstore(c: *cgen, gpcur: i32, ssecur: i32, off: i32, wide: bool, tn: *node)
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
|
||||
// tuplitgpwords — INTEGER cursor words an N_TUPLE literal element
|
||||
// occupies. MUST mirror the literal push arms (tuplitpushelem) exactly
|
||||
// — the count drives the POP fill, so a count/push skew silently
|
||||
// shifts every later element (#22 class). A float rides the SSE row
|
||||
// (0 GP words); str/slice push their 3-word header; a tagged element
|
||||
// its tupeslot/8 box words; a void element pushes nothing (the
|
||||
// checker's 0-slot); a scalar 1. Mirror of cstage tuple_lit_gpwords.
|
||||
fn tuplitgpwords(c: *cgen, e: *node) i32 = {
|
||||
if (isfloattype(c, e)) { return 0; };
|
||||
if (nodeisstr(c, e) || nodeisslice(c, e)) {
|
||||
return (tyslicesize() / 8i64): i32;
|
||||
};
|
||||
let t: *tinfo = e.type_: *tinfo;
|
||||
for (t != nil && t.kind == tykind.TY_NAMED) { t = t.under; };
|
||||
if (t != nil && (t.kind == tykind.TY_TAGGED ||
|
||||
t.kind == tykind.TY_VOID)) {
|
||||
return tupeslotn(e) / 8;
|
||||
};
|
||||
return 1;
|
||||
};
|
||||
|
||||
// tuplitpushelem — evaluate one N_TUPLE literal element and push its
|
||||
// INTEGER cursor words L->R (the pop side fills tupreg in reverse). A
|
||||
// tagged element loads its box words straight from its local slot —
|
||||
// cgexpr's ident load is word0-only for tagged (every tagged consumer
|
||||
// reads memory), so the cursor fill must too; any other tagged source
|
||||
// shape is loud (rule 7; the cursor-receive arm for call results is
|
||||
// with #22b's tupstore work, widening literals with #23). Mirror of
|
||||
// cstage tuple_lit_push_elem — count (tuplitgpwords) and push live or
|
||||
// die together.
|
||||
fn tuplitpushelem(c: *cgen, e: *node) void = {
|
||||
let t: *tinfo = e.type_: *tinfo;
|
||||
for (t != nil && t.kind == tykind.TY_NAMED) { t = t.under; };
|
||||
if (t != nil && t.kind == tykind.TY_TAGGED) {
|
||||
let eslot: i32 = tupeslotn(e);
|
||||
let eoff: i32 = 0;
|
||||
if (e.kind == nkind.N_IDENT) { eoff = localfind(c, e.str); };
|
||||
if (eoff == 0) {
|
||||
let m22: str = "#22a: tagged tuple element from a non-local source shape unwired (ident locals only; rule 7, #22b/#23 follow-ups)\n";
|
||||
os.write(2, m22.ptr, m22.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let k: i32 = 0;
|
||||
for (k < eslot / 8) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((eoff + k * 8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
k += 1;
|
||||
};
|
||||
return;
|
||||
};
|
||||
cgexpr(c, e);
|
||||
if (t != nil && t.kind == tykind.TY_VOID) { return; };
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
if (nodeisstr(c, e) || nodeisslice(c, e)) {
|
||||
emitline("\tPUSHQ\tBX\n");
|
||||
emitline("\tPUSHQ\tCX\n");
|
||||
};
|
||||
};
|
||||
|
||||
// cgtuplelittocursor — #241: materialise an N_TUPLE literal's elements into
|
||||
// the SysV register-return cursor (integer words L->R over tupreg AX/DX/CX/
|
||||
// R8, floats over tupsse X0/X1, a slice/str's {ptr,len,cap} over three
|
||||
@@ -296,8 +379,7 @@ fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
if (isfloattype(c, e)) {
|
||||
ssecount = ssecount + 1;
|
||||
} else {
|
||||
let wide: bool = nodeisstr(c, e) || nodeisslice(c, e);
|
||||
gptotal = gptotal + tupebytes(wide);
|
||||
gptotal = gptotal + tuplitgpwords(c, e);
|
||||
};
|
||||
e = e.next;
|
||||
};
|
||||
@@ -314,8 +396,8 @@ fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
e = tuple.list;
|
||||
for (e != nil) {
|
||||
let isflt: bool = isfloattype(c, e);
|
||||
cgexpr(c, e);
|
||||
if (isflt) {
|
||||
cgexpr(c, e);
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, e)) { mov = "MOVSS"; };
|
||||
emitline("\t"); emitline(mov); emitline("\tX0, ");
|
||||
@@ -323,11 +405,7 @@ fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
sseidx = sseidx + 1;
|
||||
} else {
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
if (nodeisstr(c, e) || nodeisslice(c, e)) {
|
||||
emitline("\tPUSHQ\tBX\n");
|
||||
emitline("\tPUSHQ\tCX\n");
|
||||
};
|
||||
tuplitpushelem(c, e);
|
||||
};
|
||||
e = e.next;
|
||||
};
|
||||
@@ -374,8 +452,7 @@ fn cgtupleslottocursor(c: *cgen, srcoff: i32, tu: *tinfo) void = {
|
||||
if (et != nil && (et.kind == tykind.TY_F32 || et.kind == tykind.TY_F64)) {
|
||||
ssecount = ssecount + 1;
|
||||
} else {
|
||||
let wide: bool = et != nil && (et.kind == tykind.TY_SLICE || et.kind == tykind.TY_STR);
|
||||
gptotal = gptotal + tupebytes(wide);
|
||||
gptotal = gptotal + tupeslot(el.type_) / 8;
|
||||
};
|
||||
el = el.tnext;
|
||||
};
|
||||
@@ -392,7 +469,7 @@ fn cgtupleslottocursor(c: *cgen, srcoff: i32, tu: *tinfo) void = {
|
||||
let et: *tinfo = el.type_;
|
||||
for (et != nil && et.kind == tykind.TY_NAMED) { et = et.under; };
|
||||
let isflt: bool = et != nil && (et.kind == tykind.TY_F32 || et.kind == tykind.TY_F64);
|
||||
let wide: bool = et != nil && (et.kind == tykind.TY_SLICE || et.kind == tykind.TY_STR);
|
||||
let eslot: i32 = tupeslot(el.type_);
|
||||
if (isflt) {
|
||||
let mov: str = "MOVSD";
|
||||
if (et.kind == tykind.TY_F32) { mov = "MOVSS"; };
|
||||
@@ -403,9 +480,9 @@ fn cgtupleslottocursor(c: *cgen, srcoff: i32, tu: *tinfo) void = {
|
||||
emitline("\n");
|
||||
sse = sse + 1;
|
||||
foff += 8;
|
||||
} else { if (wide) {
|
||||
} else {
|
||||
let k: i32 = 0;
|
||||
for (k < 3) {
|
||||
for (k < eslot / 8) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((srcoff + foff + k * 8): i64);
|
||||
emitline("(BP), ");
|
||||
@@ -413,17 +490,9 @@ fn cgtupleslottocursor(c: *cgen, srcoff: i32, tu: *tinfo) void = {
|
||||
emitline("\n");
|
||||
k += 1;
|
||||
};
|
||||
gp += 3;
|
||||
foff += et.size: i32;
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((srcoff + foff): i64);
|
||||
emitline("(BP), ");
|
||||
emitline(tupreg(gp));
|
||||
emitline("\n");
|
||||
gp += 1;
|
||||
foff += 8;
|
||||
}; };
|
||||
gp += eslot / 8;
|
||||
foff += eslot;
|
||||
};
|
||||
el = el.tnext;
|
||||
};
|
||||
};
|
||||
@@ -443,9 +512,8 @@ fn cgtaggedtuplepayloadshift(c: *cgen, tup: *tinfo) void = {
|
||||
let et: *tinfo = el.type_;
|
||||
for (et != nil && et.kind == tykind.TY_NAMED) { et = et.under; };
|
||||
let isflt: bool = et != nil && (et.kind == tykind.TY_F32 || et.kind == tykind.TY_F64);
|
||||
let wide: bool = et != nil && (et.kind == tykind.TY_SLICE || et.kind == tykind.TY_STR);
|
||||
if (isflt || wide) {
|
||||
let msg: str = "tuple-in-union ? unwrap: float/slice/str payload element needs SysV per-eightbyte classification (see #243); only integer tuple payloads supported\n";
|
||||
if (isflt || tupeslot(el.type_) != 8) {
|
||||
let msg: str = "tuple-in-union ? unwrap: float/slice/str/tagged payload element needs SysV per-eightbyte classification (see #243); only integer tuple payloads supported\n";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
@@ -502,12 +570,28 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
if (isfloattype(c, e)) {
|
||||
ssecount = ssecount + 1;
|
||||
} else {
|
||||
let wide: bool = nodeisstr(c, e) || nodeisslice(c, e);
|
||||
gptotal = gptotal + tupebytes(wide);
|
||||
gptotal = gptotal + tuplitgpwords(c, e);
|
||||
};
|
||||
e = e.next;
|
||||
};
|
||||
if (gptotal > TUPLE_GPCAP || ssecount > ssecap) {
|
||||
// #22a (rule 7): a tagged element's box can't
|
||||
// ride the scalar/wide store arms below — the
|
||||
// MEMORY-class tagged element store is #22b
|
||||
// (task #28); pre-guard it silently stored
|
||||
// word0 of the box.
|
||||
let gq: *node = nil;
|
||||
if (c.fnret != nil) { gq = c.fnret.list; };
|
||||
for (gq != nil) {
|
||||
let gqt: *tinfo = gq.lhs.type_: *tinfo;
|
||||
for (gqt != nil && gqt.kind == tykind.TY_NAMED) { gqt = gqt.under; };
|
||||
if (gqt != nil && gqt.kind == tykind.TY_TAGGED) {
|
||||
let m22b: str = "#22b: tagged element in an over-cap (sret) tuple return unwired (rule 7)\n";
|
||||
os.write(2, m22b.ptr, m22b.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
gq = gq.next;
|
||||
};
|
||||
// #10 Fold A: over-cap tuple returns via sret. The
|
||||
// prologue wired @sretarg (sretretsize agrees on the
|
||||
// caps — TUPLE_GPCAP/TUPLE_SSECAP, the shared SSoT),
|
||||
@@ -567,7 +651,12 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
emitline("\n");
|
||||
};
|
||||
};
|
||||
foff += esz;
|
||||
// C-t0: the sret buffer is slot-laid like
|
||||
// every tuple home (checker size, t.N
|
||||
// reader, mlet receive agree); esz keeps
|
||||
// the store WIDTH natural. Mirrors cstage
|
||||
// cgen.c N_RETURN over-cap arm.
|
||||
if (wide) { foff += esz; } else { foff += 8; };
|
||||
we = we.next;
|
||||
if (pt != nil) { pt = pt.next; };
|
||||
};
|
||||
@@ -588,8 +677,8 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
e = rhs.list;
|
||||
for (e != nil) {
|
||||
let isflt: bool = isfloattype(c, e);
|
||||
cgexpr(c, e);
|
||||
if (isflt) {
|
||||
cgexpr(c, e);
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, e)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
@@ -599,11 +688,9 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
sseidx = sseidx + 1;
|
||||
} else {
|
||||
emitline("\tPUSHQ\tAX\n"); // scalar / .ptr
|
||||
if (nodeisstr(c, e) || nodeisslice(c, e)) {
|
||||
emitline("\tPUSHQ\tBX\n"); // .len
|
||||
emitline("\tPUSHQ\tCX\n"); // .cap
|
||||
};
|
||||
// scalar=AX; slice/str=AX,BX,CX; tagged
|
||||
// box from its slot (tuplitpushelem)
|
||||
tuplitpushelem(c, e);
|
||||
};
|
||||
e = e.next;
|
||||
};
|
||||
@@ -2044,20 +2131,15 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
for (q != nil) {
|
||||
let qt: *node = q.lhs;
|
||||
let isflt: bool = isfloattype(c, qt);
|
||||
let wide: bool = isstrtype(c, qt)
|
||||
|| isslicetype(c, qt);
|
||||
let eslot: i32 = tupeslotn(qt);
|
||||
tupstore(c, gpcur, ssecur,
|
||||
off + eoff, wide, qt);
|
||||
off + eoff, eslot, qt);
|
||||
if (isflt) {
|
||||
ssecur = ssecur + 1;
|
||||
} else {
|
||||
gpcur = gpcur + tupebytes(wide);
|
||||
};
|
||||
if (wide) {
|
||||
eoff = eoff + (tyslicesize(): i32);
|
||||
} else {
|
||||
eoff = eoff + 8;
|
||||
gpcur = gpcur + eslot / 8;
|
||||
};
|
||||
eoff = eoff + eslot;
|
||||
q = q.next;
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
@@ -2898,7 +2980,10 @@ fn cgmassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
foff += esz;
|
||||
// C-t0: slot stride — must mirror the N_RETURN
|
||||
// over-cap SEND's buffer layout (cstage N_MASSIGN
|
||||
// twin strides tuple_eslot).
|
||||
foff += tupeslotn(tn);
|
||||
lb = lb.next;
|
||||
if (pt2 != nil) { pt2 = pt2.next; };
|
||||
};
|
||||
@@ -2918,8 +3003,7 @@ fn cgmassign(c: *cgen, n: *node) void = {
|
||||
if (isfloattype(c, tn)) {
|
||||
ssetotal = ssetotal + 1;
|
||||
} else {
|
||||
let wide: bool = isstrtype(c, tn) || isslicetype(c, tn);
|
||||
gptotal = gptotal + tupebytes(wide);
|
||||
gptotal = gptotal + tupeslotn(tn) / 8;
|
||||
};
|
||||
l = l.next;
|
||||
if (pt != nil) { pt = pt.next; };
|
||||
@@ -2946,18 +3030,18 @@ fn cgmassign(c: *cgen, n: *node) void = {
|
||||
let tn: *node = nil;
|
||||
if (pt != nil) { tn = pt.lhs; };
|
||||
let isflt: bool = isfloattype(c, tn);
|
||||
let wide: bool = isstrtype(c, tn) || isslicetype(c, tn);
|
||||
let eslot: i32 = tupeslotn(tn);
|
||||
let off: i32 = 0;
|
||||
if (l.kind == nkind.N_IDENT) { off = localfind(c, l.str); };
|
||||
// harec `_` (off==0): skip the store but CONSUME the cursor
|
||||
// slot so the next element stays aligned.
|
||||
if (off != 0) {
|
||||
tupstore(c, gpcur, ssecur, off, wide, tn);
|
||||
tupstore(c, gpcur, ssecur, off, eslot, tn);
|
||||
};
|
||||
if (isflt) {
|
||||
ssecur = ssecur + 1;
|
||||
} else {
|
||||
gpcur = gpcur + tupebytes(wide);
|
||||
gpcur = gpcur + eslot / 8;
|
||||
};
|
||||
l = l.next;
|
||||
if (pt != nil) { pt = pt.next; };
|
||||
@@ -3020,13 +3104,13 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
for (lb != nil) {
|
||||
let tn: *node = lb.lhs;
|
||||
let isflt: bool = isfloattype(c, tn);
|
||||
let wide: bool = isstrtype(c, tn) || isslicetype(c, tn);
|
||||
let eslot: i32 = tupeslotn(tn);
|
||||
let esz: i32 = 8;
|
||||
let eti: *tinfo = nil;
|
||||
if (tn != nil) { eti = tn.type_: *tinfo; };
|
||||
if (eti != nil) { esz = eti.size: i32; };
|
||||
let bsz: i32 = 8;
|
||||
if (wide) { bsz = tyslicesize(): i32; };
|
||||
if (eslot > 8) { bsz = eslot; };
|
||||
let off: i32 = localadd(c, lb.str, bsz, tn);
|
||||
if (isflt) {
|
||||
let mov: str = "MOVSD";
|
||||
@@ -3036,9 +3120,9 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
emitline("(BP), X0\n");
|
||||
emitline("\t"); emitline(mov); emitline("\tX0, ");
|
||||
emitoff(off: i64); emitline("(BP)\n");
|
||||
} else { if (wide) {
|
||||
} else { if (eslot > 8) {
|
||||
let k: i32 = 0;
|
||||
for (k < esz) {
|
||||
for (k < eslot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((srcoff + foff + k): i64);
|
||||
emitline("(BP), AX\n");
|
||||
@@ -3056,7 +3140,7 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
emitline("\t"); emitline(sop); emitline("\tAX, ");
|
||||
emitoff(off: i64); emitline("(BP)\n");
|
||||
}; };
|
||||
if (wide) { foff += 24; } else { foff += 8; };
|
||||
foff += eslot;
|
||||
lb = lb.next;
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
@@ -3077,14 +3161,14 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
let tn: *node = nil;
|
||||
if (pt2 != nil) { tn = pt2.lhs; };
|
||||
let isflt: bool = isfloattype(c, tn);
|
||||
let wide: bool = isstrtype(c, tn) || isslicetype(c, tn);
|
||||
let eslot: i32 = tupeslotn(tn);
|
||||
let esz: i32 = 8;
|
||||
if (pt2 != nil) {
|
||||
let eti: *tinfo = pt2.lhs.type_: *tinfo;
|
||||
if (eti != nil) { esz = eti.size: i32; };
|
||||
};
|
||||
let bsz: i32 = 8;
|
||||
if (wide) { bsz = tyslicesize(): i32; };
|
||||
if (eslot > 8) { bsz = eslot; };
|
||||
let off: i32 = localadd(c, lb.str, bsz, tn);
|
||||
if (isflt) {
|
||||
let mov: str = "MOVSD";
|
||||
@@ -3095,9 +3179,9 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
emitline("\t"); emitline(mov); emitline("\tX0, ");
|
||||
emitoff(off: i64); emitline("(BP)\n");
|
||||
} else {
|
||||
if (wide) {
|
||||
if (eslot > 8) {
|
||||
let k: i32 = 0;
|
||||
for (k < esz) {
|
||||
for (k < eslot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scr + foff + k): i64);
|
||||
emitline("(BP), AX\n");
|
||||
@@ -3117,7 +3201,7 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
emitoff(off: i64); emitline("(BP)\n");
|
||||
};
|
||||
};
|
||||
foff += esz;
|
||||
foff += eslot;
|
||||
lb = lb.next;
|
||||
if (pt2 != nil) { pt2 = pt2.next; };
|
||||
};
|
||||
@@ -3139,8 +3223,7 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
if (isfloattype(c, tn)) {
|
||||
ssetotal = ssetotal + 1;
|
||||
} else {
|
||||
let wide: bool = isstrtype(c, tn) || isslicetype(c, tn);
|
||||
gptotal = gptotal + tupebytes(wide);
|
||||
gptotal = gptotal + tupeslotn(tn) / 8;
|
||||
};
|
||||
l = l.next;
|
||||
if (pt != nil) { pt = pt.next; };
|
||||
@@ -3169,15 +3252,15 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
if (pt != nil) { tn = pt.lhs; };
|
||||
};
|
||||
let isflt: bool = isfloattype(c, tn);
|
||||
let wide: bool = isstrtype(c, tn) || isslicetype(c, tn);
|
||||
let eslot: i32 = tupeslotn(tn);
|
||||
let sz: i32 = 8;
|
||||
if (wide) { sz = tyslicesize(): i32; };
|
||||
if (eslot > 8) { sz = eslot; };
|
||||
let off: i32 = localadd(c, l.str, sz, tn);
|
||||
tupstore(c, gpcur, ssecur, off, wide, tn);
|
||||
tupstore(c, gpcur, ssecur, off, eslot, tn);
|
||||
if (isflt) {
|
||||
ssecur = ssecur + 1;
|
||||
} else {
|
||||
gpcur = gpcur + tupebytes(wide);
|
||||
gpcur = gpcur + eslot / 8;
|
||||
};
|
||||
l = l.next;
|
||||
if (pt != nil) { pt = pt.next; };
|
||||
|
||||
Reference in New Issue
Block a user