wcc/cgen: #116 non-literal tuple source into a tagged box (both-stage)
cg_widen_tagged_store only handled a tuple LITERAL (N_TUPLE / cast-of-
N_TUPLE) widened into a tagged box; any addressable non-literal tuple
source -- IDENT var, INDEX tbl[i], DEREF *p -- hit the `else fatal`
("tuple-typed source shape unwired"). Both stages loud-identical
(honest, no silent miscompile). This blocked indexing a const tuple
table into a union (regex charclass_map[i] -> charset union).
Add an addressable-tuple-source arm, both stages (cgen.c +
cgenutil.ww twin). It resolves the source address via the cgplaceaddr
place-spine (covering ident/index/deref -- one mechanism, so the trio
is family-closed) and block-copies the tuple's type-table ->size bytes
into the box payload (after the 8B tag), then stamps the variant tag.
No re-slotting: a tuple's in-memory layout uses the same eslot strides
(str=24B header, *fn=8B, ...) as the box payload the literal loop
fills, so source-layout == dest-layout. The existing narrow-pack and
tag-unresolved guards stay as the honest boundary; CALL/sret tuple
sources (different receive, #68-kin) stay loud.
align-BOTH: both stages were loud (no runtime reference), and byte-id
is structurally blind to an identical-wrong emission -- so correctness
is proven by a RUNTIME read-back pin (944_nonlit_tuple_widen_run, per
shape: match-extract + assert str header + call the fn-ptr elem with
distinct fns so a stale pointer is caught). 936's old reject row
graduates to a run row. Both stages byte-identical (990-997 green).
This commit is contained in:
@@ -2737,14 +2737,9 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
else if (src->kind == N_CAST && src->lhs
|
||||
&& src->lhs->kind == N_TUPLE)
|
||||
tupsrc = src->lhs;
|
||||
/* #72: any OTHER tuple-typed source (ident, call result,
|
||||
* match binding) would fall to the scalar arm below and
|
||||
* silently drop payload slot 1+ — loud-stop (rule 7) until
|
||||
* the word-copy / cursor-receive arms are wired. */
|
||||
if (tupsrc == NULL)
|
||||
fatal("cg_widen_tagged_store: tuple-typed source "
|
||||
"shape unwired (only the bare/cast tuple literal "
|
||||
"carries a full payload; see #72)");
|
||||
/* #116: a NON-LITERAL tuple-typed source (ident, index,
|
||||
* deref) is no longer loud here — it routes to the
|
||||
* addressable block-copy arm just below the literal arm. */
|
||||
}
|
||||
if (tupsrc != NULL) {
|
||||
int tag = cg_tag_for_variant(du, st);
|
||||
@@ -2833,6 +2828,62 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
if (via_outer) goto copy_out;
|
||||
return;
|
||||
}
|
||||
/* #116: a NON-LITERAL but ADDRESSABLE tuple source — a tuple IDENT
|
||||
* var, a slice/array INDEX (tbl[i]), or a DEREF (*p). The tuple in
|
||||
* memory uses the SAME tuple_eslot strides as the box payload the
|
||||
* literal loop above fills (a scalar 8B, a slice/str its 24B header,
|
||||
* a tagged element its boxed tag+payload), so the source-in-memory
|
||||
* layout already equals the box payload layout — the fill is a flat
|
||||
* block-copy of sum(tuple_eslot) bytes from the source address into
|
||||
* write_off+8, no re-slotting (a tagged element rides over as its
|
||||
* already-built box, so no recursion is needed). Kept loud (out of
|
||||
* scope): a CALL/sret result (the tuple sits at the sret address,
|
||||
* #40-kin) and a struct-field / array-literal-element source (loud
|
||||
* EARLIER at construction, #49 / #270-1c). A cast wrapping a
|
||||
* concrete-variant tuple (`(tbl[i]: ci)`) survived the widen-cast
|
||||
* peel above; its operand is the addressable expr. */
|
||||
if (su && su->kind == TY_TUPLE) {
|
||||
Node *addrsrc = src;
|
||||
if (addrsrc->kind == N_CAST && addrsrc->lhs)
|
||||
addrsrc = addrsrc->lhs;
|
||||
if (addrsrc->kind != N_IDENT && addrsrc->kind != N_INDEX
|
||||
&& !(addrsrc->kind == N_UN && addrsrc->op == TK_STAR))
|
||||
fatal("cg_widen_tagged_store: tuple-typed source shape "
|
||||
"unwired (only the bare/cast tuple literal and the "
|
||||
"addressable ident/index/deref trio carry a full "
|
||||
"payload; see #116)");
|
||||
int tag = cg_tag_for_variant(du, st);
|
||||
if (tag < 0)
|
||||
fatal("cg_widen_tagged_store: tuple-in-union variant "
|
||||
"tag unresolved (untyped/literal tuple element; "
|
||||
"see #242 / #241)");
|
||||
/* The tuple's type-table size IS sum(tuple_eslot) under the
|
||||
* 8B-slot tuple layout (every walk takes its stride from
|
||||
* tuple_eslot; the type's size is their sum), so the payload
|
||||
* byte-count routes through the type table (rule 13) without
|
||||
* re-walking the elements — and matches the wwstage twin, whose
|
||||
* tuple tinfo carries no per-element params list. */
|
||||
int total = (int)su->size;
|
||||
if (8 + total > sz)
|
||||
fatal("cg_widen_tagged_store: tuple-in-union payload "
|
||||
"needs SysV eightbyte packing (narrow elements "
|
||||
"share an eightbyte; see #242 follow-up)");
|
||||
if (!cgplaceaddr(c, addrsrc, D_SI, *locals_p))
|
||||
fatal("cg_widen_tagged_store: addressable tuple source "
|
||||
"address unresolved (see #116)");
|
||||
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
||||
for (int k = 0; k < sz; k += 8)
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, write_off + k));
|
||||
for (int k = 0; k < total; k += 8) {
|
||||
ins2(c, A_MOVQ, amem(D_SI, k), areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, write_off + 8 + k));
|
||||
}
|
||||
ins2(c, A_MOVQ, aimm(tag), amem(D_BP, write_off + 0));
|
||||
if (via_outer) goto copy_out;
|
||||
return;
|
||||
}
|
||||
/* Struct payload: zero the whole slot, then write fields/words
|
||||
* at slot+8+ — keeping the tag word at slot+0 from the zero-fill,
|
||||
* then patch it with the variant tag. */
|
||||
|
||||
Reference in New Issue
Block a user