w6c+w6c_ww: tagged-union struct-lit payload fills via the canonical fill (#23)
The widen choke-point's struct-payload arm carried its own inline N_STRUCTLIT field loop -- a parallel fill that drifted from cg_structlit_fill/cgstructlitfill: no tagged-field widen arm, so a (void|T)-typed field's raw scalar landed in the field's TAG word (silent truncation past the first tagged field, both stages, byte-id, gate-blind; prober-9 PG5). Delete both loops and delegate to the canonical fill at the payload base: one fill path, one widen path, mutually recursive. Inherits the nested-struct/call/arrlit field arms and closes a latent fsz==2 cs!=ww (old ww loop's fieldstoreop MOVW vs cstage MOVQ). Test 938: 15-row table-driven runtime readback (incl. ellipsis autofill, offset-0 tagged field, (void|str) payload, 3-level widen-fill recursion torture), all 13 bug rows silent-fail at master 6699158; 2 rows skip the byte-id check loudly (pre-existing match-on-tagged-FIELD readback cs!=ww, master-confirmed, separate family).
This commit is contained in:
@@ -2606,52 +2606,17 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
amem(D_BP, write_off + 8 + k));
|
||||
}
|
||||
} else if (src->kind == N_STRUCTLIT) {
|
||||
for (Node *f = src->list; f; f = f->next) {
|
||||
u64 foff = 0;
|
||||
int fsz = 8;
|
||||
Type *ftype = NULL;
|
||||
for (Tfield *fl = su->fields; fl; fl = fl->next) {
|
||||
if (strcmp(fl->name, f->str) == 0) {
|
||||
foff = fl->offset;
|
||||
fsz = (int)(fl->type ? fl->type->size : 8);
|
||||
ftype = fl->type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cgexpr(c, f->lhs, *locals_p);
|
||||
int sl_isf32 = 0;
|
||||
if (fld_isfloat(ftype, &sl_isf32)) {
|
||||
int mov = sl_isf32 ? A_MOVSS : A_MOVSD;
|
||||
ins2(c, mov, areg(D_X0),
|
||||
amem(D_BP, write_off + 8 + (int)foff));
|
||||
continue;
|
||||
}
|
||||
Type *fu = (ftype && ftype->kind == TY_NAMED)
|
||||
? ftype->under : ftype;
|
||||
/* str IS []u8 and a slice is the same 3-word
|
||||
* {ptr,len,cap} header from cgexpr's AX/BX/CX
|
||||
* (#1/Phase 3). The slice arm rides #38b's
|
||||
* regex-shaped consumer (slice fields inside a
|
||||
* union-payload struct literal); the prior
|
||||
* str-only gate dropped .len/.cap via the
|
||||
* scalar store below — the #24 gap's widener
|
||||
* twin. */
|
||||
if (fu && (fu->kind == TY_STR
|
||||
|| fu->kind == TY_SLICE)) {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, write_off + 8 + (int)foff + 0));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_BP, write_off + 8 + (int)foff + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX),
|
||||
amem(D_BP, write_off + 8 + (int)foff + 16));
|
||||
continue;
|
||||
}
|
||||
int op = A_MOVQ;
|
||||
if (fsz == 1) op = A_MOVB;
|
||||
else if (fsz == 4) op = A_MOVL;
|
||||
ins2(c, op, areg(D_AX),
|
||||
amem(D_BP, write_off + 8 + (int)foff));
|
||||
}
|
||||
/* #23: delegate to the single fill path. The inline
|
||||
* field loop this replaces was a parallel fill that
|
||||
* drifted: it lacked the tagged-field widen arm, so
|
||||
* a (void|T)-typed field's raw scalar landed in the
|
||||
* field's TAG word (silent truncation past the first
|
||||
* tagged field, both stages). Delegation also
|
||||
* inherits the nested-struct / call / array-lit
|
||||
* field arms; float / str / slice / scalar fields
|
||||
* emit byte-identically to the old loop. */
|
||||
cg_structlit_fill(c, locals_p, su, src,
|
||||
DST_BP, 0, NULL, write_off + 8);
|
||||
}
|
||||
ins2(c, A_MOVQ, aimm(tag < 0 ? 0 : tag),
|
||||
amem(D_BP, write_off + 0));
|
||||
|
||||
Reference in New Issue
Block a user