w6c+wwstage: store over-cap tuple sret into local field/index (#234)
The STORE-twin of the Fold-B over-cap-tuple sret RECEIVE (a937d67). Fold B
wired single-var-let / destructure / reassign / return-forward to receive a
> 4-eightbyte (sret) tuple-returning call, but a FIELD or INDEXED-lvalue
dest stayed unwired: the store dropped the callee's sret body (a truncated
MOVQ through a stale RDI) — a silent miscompile, gate-blind because the
bootstrap never field-stores a wide tuple.
Per Rob's ruling A (one class, one commit): convert the silent miscompile
into either a CORRECT store or a LOUD stop, never a fall-through.
- cstage cmd/w6c/cgen.c: the struct-field N_DOT store and the N_INDEX
lvalue store each gain an arm keyed on cg_sret_retsize(dest) > 0 &&
rhs == N_CALL. A LOCAL dest (BP-relative, not via_ptr / global) sets
cg_sret_dest_off so the callee's hidden RDI writes the WHOLE tuple
straight into the slot — field: boff + foff; indexed: boff + cidx*esz
(a CONSTANT index into a local value array, the only indexed form whose
dest is a static BP offset). Every other dest fatals "#234-tail".
- wwstage selfhost/cmd/wcc/cgenexpr.ww: symmetric (rule 10). The direct
struct-local field branch sets c.sretdestoff = lc.off + fi.foff; the
via_ptr branch, the global branch, and the N_INDEX arm hard-stop loud
with the same #234-tail diagnostic. The field branches key on
sretretsize(fi.tnode) > 0 (fi.tnode is a real type-AST node). The
N_INDEX arm keys its ENTRY on callsretsize(c, n.rhs) > 0 — the
callee-return-type SSoT (cgenutil.ww) the receive sites use — NOT on
sretretsize(elemtn): elemtn is only a type node for an N_IDENT base, a
VALUE node for an N_DOT base (`s.arr[i]`) / chained (`a[i][k]`), which
fell to sretretsize=0 and let those forms drop SILENTLY through to the
truncating store. The callee return type equals the dest-element type
(checker-guaranteed), so the verdict is byte-identical to cstage's
cg_sret_retsize, and the base-shape split then loud-stops every
non-local-array form, base-kind-independent.
Deferred (#234-tail): a via_ptr field (`p.f`), a global field (`g.f`), an
N_DOT-base index (`s.arr[i]`), a chained index (`a[i][k]`), and a runtime /
slice / pointer index all need a runtime RDI-pointer dest, which
cg_sret_dest_off (BP-relative only) can't express — they hard-error loud
(rule 7), never a truncating store.
Depends on #237 (committed first): the wwstage struct-field slot for a
tuple field is only correctly sized with that fix, so the struct-field arm
is byte-id-symmetric here.
Test 940: indexed-on-local and local-struct-field rows RUN on both stages
(exit 0) AND assert cs==ww byte-id; readback via a raw pointer
(`(&dest):*int; p[i]`) since a tuple-element read `dest.N` is a separate gap
(#238). Builderr rows assert the via_ptr / global / runtime-index /
N_DOT-base / chained-index forms loud-stop with #234-tail on BOTH drivers
(the N_DOT-base + chained rows are the regression witnesses for the wwstage
silent-store gap closed by the callsretsize re-key). The bootstrap exercises
no such store, so the w6c/wwdump combined amalgams regen with no asm change
(byte-id-neutral bootstrap; the new hard-error never fires self-compiling).
This commit is contained in:
@@ -23569,6 +23569,54 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
};
|
||||
// #234: over-cap sret STORE into an indexed lvalue —
|
||||
// `arr[i] = wide();` STORE-twin of the Fold-B sret RECEIVE
|
||||
// (a937d67). c.sretdestoff is a STATIC BP-relative offset, so
|
||||
// only a CONSTANT index into a LOCAL value array yields a
|
||||
// static dest slot (off + idx*esz) the callee can sret
|
||||
// straight into. Every other indexed form — runtime index,
|
||||
// slice/ptr base, N_DOT-base (`s.arr[i]`), chained (`a[i][k]`),
|
||||
// global base — needs the runtime RDI-pointer dest variant
|
||||
// deferred to #234-tail and HARD-STOPS loud (rule 7). Mirror of
|
||||
// cstage cgen.c (#234) N_INDEX arm.
|
||||
//
|
||||
// Gate ENTRY on callsretsize(n.rhs) — the callee-return-type
|
||||
// SSoT (cgenutil.ww) the receive sites use — NOT on
|
||||
// sretretsize(elemtn): elemtn is only a type node for an
|
||||
// N_IDENT base, but a value node for N_DOT (4695) / chained
|
||||
// (4710), which fell to sretretsize=0 and let those forms
|
||||
// drop SILENTLY through to the truncating store. The callee
|
||||
// return type equals the dest-element type (checker-guaranteed),
|
||||
// so the verdict is byte-identical to cstage's cg_sret_retsize.
|
||||
// The base-shape split below then loud-stops every non-local-
|
||||
// array form, base-kind-independent.
|
||||
if (n.rhs != nil && n.rhs.kind == nkind.N_CALL
|
||||
&& callsretsize(c, n.rhs) > 0) {
|
||||
let islocalarr: bool = false;
|
||||
if (baselocal != nil) {
|
||||
let btn: *node = baselocal.tnode;
|
||||
if (btn != nil) {
|
||||
if (btn.kind == nkind.N_TARRAY) { islocalarr = true; };
|
||||
};
|
||||
};
|
||||
let constidx: bool = false;
|
||||
let cidx: i32 = 0;
|
||||
if (idx != nil) {
|
||||
if (idx.kind == nkind.N_INTLIT) {
|
||||
constidx = true;
|
||||
cidx = idx.uval: i32;
|
||||
};
|
||||
};
|
||||
if (!islocalarr || !constidx) {
|
||||
let m234: str = "#234-tail: over-cap tuple sret store to non-local/dynamic-index dest unsupported\n";
|
||||
os.write(2, m234.ptr, m234.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
c.sretdestoff = baselocal.off + cidx * esz;
|
||||
cgexpr(c, n.rhs);
|
||||
c.sretdestoff = 0;
|
||||
return;
|
||||
};
|
||||
cgexpr(c, n.rhs); // value → AX
|
||||
// str/slice: spill cap (CX) + len (BX) before
|
||||
// computing the index so the post-index store can
|
||||
@@ -24138,6 +24186,18 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
n.rhs, "BX", fi.foff, fsz);
|
||||
return;
|
||||
};
|
||||
// #234-tail: via-ptr (`p.f`) sret field STORE. The dest must
|
||||
// be a runtime RDI pointer (the BP-relative c.sretdestoff
|
||||
// can't name `p.f`); deferred. HARD-STOP loud, never fall
|
||||
// through to the truncating generic store (rule 7).
|
||||
if (n.op == tkind.TK_ASSIGN
|
||||
&& n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_CALL
|
||||
&& sretretsize(c, fi.tnode) > 0) {
|
||||
let m234: str = "#234-tail: over-cap tuple sret store to via-ptr field dest unsupported\n";
|
||||
os.write(2, m234.ptr, m234.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
// struct-typed field via *struct base — three
|
||||
// rhs shapes (call/structlit added with #5;
|
||||
// closes #27 marker here):
|
||||
@@ -24359,6 +24419,23 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
n.rhs, "BP", lc.off + fi.foff, fsz);
|
||||
return;
|
||||
};
|
||||
// #234: over-cap sret STORE into a LOCAL struct field —
|
||||
// `s.f = wide();` where f's type returns via sret
|
||||
// (sretretsize > 0: a >24B struct OR an over-cap tuple).
|
||||
// STORE-twin of the Fold-B sret RECEIVE (a937d67): point
|
||||
// the callee's hidden RDI dest at the field slot
|
||||
// (c.sretdestoff = lc.off + fi.foff) so it writes the
|
||||
// WHOLE value there, never the truncating generic store
|
||||
// below. Mirror of cstage cgen.c (#234) field local arm.
|
||||
if (n.op == tkind.TK_ASSIGN
|
||||
&& n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_CALL
|
||||
&& sretretsize(c, fi.tnode) > 0) {
|
||||
c.sretdestoff = lc.off + fi.foff;
|
||||
cgexpr(c, n.rhs);
|
||||
c.sretdestoff = 0;
|
||||
return;
|
||||
};
|
||||
// struct-typed field on a direct struct
|
||||
// local — three rhs shapes (call/structlit
|
||||
// added with #5; closes #27 marker here):
|
||||
@@ -24638,6 +24715,18 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (streq(fi.fname, fld)) {
|
||||
// #234-tail: GLOBAL (`g.f`) sret field STORE. c.sretdestoff is
|
||||
// BP-relative only and can't name a global slot; the runtime
|
||||
// RDI-pointer dest variant is deferred. HARD-STOP loud, never
|
||||
// the truncating generic store (rule 7).
|
||||
if (n.op == tkind.TK_ASSIGN
|
||||
&& n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_CALL
|
||||
&& sretretsize(c, fi.tnode) > 0) {
|
||||
let m234: str = "#234-tail: over-cap tuple sret store to global field dest unsupported\n";
|
||||
os.write(2, m234.ptr, m234.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
// struct-typed field on a global struct base —
|
||||
// three rhs shapes (call/structlit added with
|
||||
// #5; closes #27 marker here):
|
||||
|
||||
Reference in New Issue
Block a user