wcc/cgen: #58 indexed tagged-field read+assign cursor arm (both-stage)
Reading or writing a tagged field of an indexed array element (xs[i].field) was broken on BOTH stages, byte-identically and silently (#263 gate-blind): the arr[i].field branches had arms for array/str/slice/float but no TY_TAGGED arm, so the tagged field fell to the single-word scalar path. READ loaded only the tag word (stale payload -> `xs[i].min as T` read garbage); ASSIGN stored the raw unboxed scalar into the tag slot, corrupting the box. Insert a TY_TAGGED cursor arm before each scalar fallback, both sites both stages (cgen.c read + assign; cgenexpr.ww cgdot N_INDEX-lhs read + cgassign indexed-field). READ mirrors cg_tagged_memread (payload -> DX/CX/R8, tag -> AX last). ASSIGN synthesizes the tag for the concrete variant (taggedvariantindext) and stores tag+payload via the str/slice 3-word store spine -- not the source-remap widener (concrete rhs has no source tag to remap). >32B / multi-word / float payloads are loud-stopped at all four arms (emission not yet wired; see #114). That shape is reachable today via a narrow-variant ctor, so it louds rather than silently miscompiling. Both stages get the same arm -> byte-id preserved (990-997 green; the runtime is the net for this #263 class). Pin 944_idx_tagged_field_run (read/assign runtime rows + >32B expect-loud rows).
This commit is contained in:
134
cmd/w6c/cgen.c
134
cmd/w6c/cgen.c
@@ -5234,6 +5234,95 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
amem(D_DX, foff + 16));
|
||||
break;
|
||||
}
|
||||
/* #58: a TAGGED field of an indexed array
|
||||
* element (`xs[i].f = v`). The scalar store
|
||||
* below would write the raw unboxed rhs into
|
||||
* the TAG slot — never boxing, never writing
|
||||
* the payload (box-corruption, the #38a
|
||||
* write-twin). BOX (mirror the #24 tagged-
|
||||
* field-assign tag lookup, cg_tag_for_variant)
|
||||
* + STORE spine (mirror the co-located str/
|
||||
* slice 3-word arm above): cgexpr the payload,
|
||||
* spill it across the index/address
|
||||
* computation, compute &xs[i]->BX, then store
|
||||
* the variant tag (constant) at foff+0 and the
|
||||
* scalar payload at foff+8. Only a SCALAR-
|
||||
* payload variant (box <=16B) store is wired
|
||||
* here. A >16B / multi-word / float-payload
|
||||
* union field IS constructible (a wide box,
|
||||
* built via a NARROW variant — not unbuildable
|
||||
* as earlier triage assumed; the #54/#23
|
||||
* construction hole fires only on STRUCT-
|
||||
* LITERAL payloads), but its box+memcpy store
|
||||
* arm is not yet wired, so it LOUD-STOPS rather
|
||||
* than silently corrupting the box (rule 7, the
|
||||
* #41 untested-arm trap), byte-id-neutral.
|
||||
* Reachable + pinned expect-loud (test/wcc/944
|
||||
* cfail rows). When #114 wires them, that commit
|
||||
* replaces these stops with the real str/slice/
|
||||
* struct/float/>32B box+memcpy emission + value
|
||||
* pin rows. */
|
||||
if (n->op == TK_ASSIGN && fu
|
||||
&& fu->kind == TY_TAGGED) {
|
||||
int bsz = (int)fu->size;
|
||||
Type *st = n->rhs
|
||||
? n->rhs->type : NULL;
|
||||
int h2_isf32 = 0;
|
||||
if (bsz > TUPLE_GPCAP * 8)
|
||||
fatal("#58: >32B tagged-"
|
||||
"field indexed store "
|
||||
"unreachable until #114");
|
||||
if (bsz > 16)
|
||||
fatal("#58: multi-word "
|
||||
"tagged-field indexed "
|
||||
"store unreachable "
|
||||
"until #114");
|
||||
if (fld_isfloat(st,
|
||||
&h2_isf32))
|
||||
fatal("#58: float-payload "
|
||||
"tagged-field indexed "
|
||||
"store unreachable "
|
||||
"until #114");
|
||||
cgexpr(c, n->rhs, locals);
|
||||
ins1(c, A_PUSHQ,
|
||||
areg(D_AX));
|
||||
cgexpr(c, idx, locals);
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ,
|
||||
aimm(esz),
|
||||
areg(D_CX));
|
||||
ins2(c, A_IMULQ,
|
||||
areg(D_CX),
|
||||
areg(D_AX));
|
||||
}
|
||||
if (is_arr)
|
||||
ins2(c, A_LEAQ,
|
||||
amem(D_BP, off),
|
||||
areg(D_BX));
|
||||
else
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BP, off),
|
||||
areg(D_BX));
|
||||
ins2(c, A_ADDQ,
|
||||
areg(D_AX),
|
||||
areg(D_BX));
|
||||
if (viaptr)
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BX, 0),
|
||||
areg(D_BX));
|
||||
ins1(c, A_POPQ,
|
||||
areg(D_AX));
|
||||
int v58tag =
|
||||
cg_tag_for_variant(fu, st);
|
||||
ins2(c, A_MOVQ,
|
||||
aimm(v58tag < 0
|
||||
? 0 : v58tag),
|
||||
amem(D_BX, foff + 0));
|
||||
ins2(c, A_MOVQ,
|
||||
areg(D_AX),
|
||||
amem(D_BX, foff + 8));
|
||||
break;
|
||||
}
|
||||
if (n->op == TK_ASSIGN) {
|
||||
cgexpr(c, n->rhs, locals);
|
||||
ins1(c, A_PUSHQ,
|
||||
@@ -11216,6 +11305,51 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
areg(D_AX));
|
||||
goto dot_done;
|
||||
}
|
||||
/* #58: a TAGGED field of an indexed array
|
||||
* element (`xs[i].f`). AX holds &xs[i]; load
|
||||
* the box cursor (AX=tag, DX/CX/R8=payload)
|
||||
* mirroring cg_tagged_memread's ≤32B
|
||||
* convention, tag LAST (it clobbers the base
|
||||
* AX). Without this arm the field fell to the
|
||||
* scalar load below, reading only the tag word
|
||||
* and leaving the payload cursor (DX) stale
|
||||
* (`xs[i].f as T` read garbage; #38a INDEX-
|
||||
* spine residual). >32B box: a wide-box union
|
||||
* (largest variant >32B) IS constructible via a
|
||||
* NARROW variant (not unbuildable as earlier
|
||||
* triage assumed; #54/#23 fires only on STRUCT-
|
||||
* LITERAL payloads), but the mem-based read (LEAQ
|
||||
* foff(AX),AX, cg_tagged_memread:639) is not yet
|
||||
* wired here — so this arm LOUD-STOPS rather than
|
||||
* silently reading a truncated box (rule 7, the
|
||||
* #41 untested-arm trap), byte-id-neutral.
|
||||
* Reachable + pinned expect-loud (test/wcc/944
|
||||
* cfail rows). When #114 wires it, that commit
|
||||
* replaces this with the LEAQ box-address
|
||||
* emission + a >32B value pin row. */
|
||||
if (fu && fu->kind == TY_TAGGED) {
|
||||
int bsz = (int)fu->size;
|
||||
if (bsz > TUPLE_GPCAP * 8)
|
||||
fatal("#58: >32B tagged-field "
|
||||
"indexed read unreachable "
|
||||
"until #114");
|
||||
if (bsz > 24)
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_AX, foff + 24),
|
||||
areg(D_R8));
|
||||
if (bsz > 16)
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_AX, foff + 16),
|
||||
areg(D_CX));
|
||||
if (bsz > 8)
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_AX, foff + 8),
|
||||
areg(D_DX));
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_AX, foff + 0),
|
||||
areg(D_AX));
|
||||
goto dot_done;
|
||||
}
|
||||
int g_isf32 = 0;
|
||||
if (fld_isfloat(ft, &g_isf32)) {
|
||||
int mov = g_isf32
|
||||
|
||||
Reference in New Issue
Block a user