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:
@@ -19565,58 +19565,24 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
||||
let tag: i32 = taggedvariantindext(c, dt, src);
|
||||
if (tag < 0) { tag = 0; };
|
||||
if (src.kind == nkind.N_STRUCTLIT) {
|
||||
let fnode: *node = src.list;
|
||||
for (fnode != nil) {
|
||||
if (fnode.kind == nkind.N_FIELD) {
|
||||
let fname: str = fnode.str;
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (streq(fi.fname, fname)) {
|
||||
cgexpr(c, fnode.lhs);
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) {
|
||||
mov = "MOVSS";
|
||||
};
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((slot_off + 8 + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
} else { if (isstrtype(c, fi.tnode)
|
||||
|| isslicetype(c, fi.tnode)) {
|
||||
// str IS []u8 and a slice is the
|
||||
// same 3-word {ptr,len,cap} header
|
||||
// from cgexpr's AX/BX/CX (#1/Phase
|
||||
// 3). Slice arm rides #38b's regex-
|
||||
// shaped consumer — the prior str-
|
||||
// only gate dropped .len/.cap (#24
|
||||
// gap's widener twin).
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((slot_off + 8 + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((slot_off + 8 + fi.foff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((slot_off + 8 + fi.foff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((slot_off + 8 + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
}; };
|
||||
fi = nil;
|
||||
} else {
|
||||
fi = fi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
fnode = fnode.next;
|
||||
};
|
||||
// #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 EXCEPT fsz==2 scalars, where the
|
||||
// old loop's fieldstoreop emitted MOVW
|
||||
// against cstage's MOVQ — a latent cs≠ww the
|
||||
// fill's #13-pinned dispatch closes. Mirror
|
||||
// of cstage cg_widen_tagged_store's
|
||||
// N_STRUCTLIT arm.
|
||||
cgstructlitfill(c, si, src, 0, 0, "",
|
||||
slot_off + 8);
|
||||
} else {
|
||||
// Struct ident source: byte-copy struct words to slot+8+k.
|
||||
let lc: *local = localfindnode(c, src.str);
|
||||
|
||||
@@ -3573,58 +3573,24 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
||||
let tag: i32 = taggedvariantindext(c, dt, src);
|
||||
if (tag < 0) { tag = 0; };
|
||||
if (src.kind == nkind.N_STRUCTLIT) {
|
||||
let fnode: *node = src.list;
|
||||
for (fnode != nil) {
|
||||
if (fnode.kind == nkind.N_FIELD) {
|
||||
let fname: str = fnode.str;
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (streq(fi.fname, fname)) {
|
||||
cgexpr(c, fnode.lhs);
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) {
|
||||
mov = "MOVSS";
|
||||
};
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((slot_off + 8 + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
} else { if (isstrtype(c, fi.tnode)
|
||||
|| isslicetype(c, fi.tnode)) {
|
||||
// str IS []u8 and a slice is the
|
||||
// same 3-word {ptr,len,cap} header
|
||||
// from cgexpr's AX/BX/CX (#1/Phase
|
||||
// 3). Slice arm rides #38b's regex-
|
||||
// shaped consumer — the prior str-
|
||||
// only gate dropped .len/.cap (#24
|
||||
// gap's widener twin).
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((slot_off + 8 + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((slot_off + 8 + fi.foff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((slot_off + 8 + fi.foff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((slot_off + 8 + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
}; };
|
||||
fi = nil;
|
||||
} else {
|
||||
fi = fi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
fnode = fnode.next;
|
||||
};
|
||||
// #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 EXCEPT fsz==2 scalars, where the
|
||||
// old loop's fieldstoreop emitted MOVW
|
||||
// against cstage's MOVQ — a latent cs≠ww the
|
||||
// fill's #13-pinned dispatch closes. Mirror
|
||||
// of cstage cg_widen_tagged_store's
|
||||
// N_STRUCTLIT arm.
|
||||
cgstructlitfill(c, si, src, 0, 0, "",
|
||||
slot_off + 8);
|
||||
} else {
|
||||
// Struct ident source: byte-copy struct words to slot+8+k.
|
||||
let lc: *local = localfindnode(c, src.str);
|
||||
|
||||
@@ -19565,58 +19565,24 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
||||
let tag: i32 = taggedvariantindext(c, dt, src);
|
||||
if (tag < 0) { tag = 0; };
|
||||
if (src.kind == nkind.N_STRUCTLIT) {
|
||||
let fnode: *node = src.list;
|
||||
for (fnode != nil) {
|
||||
if (fnode.kind == nkind.N_FIELD) {
|
||||
let fname: str = fnode.str;
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (streq(fi.fname, fname)) {
|
||||
cgexpr(c, fnode.lhs);
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) {
|
||||
mov = "MOVSS";
|
||||
};
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((slot_off + 8 + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
} else { if (isstrtype(c, fi.tnode)
|
||||
|| isslicetype(c, fi.tnode)) {
|
||||
// str IS []u8 and a slice is the
|
||||
// same 3-word {ptr,len,cap} header
|
||||
// from cgexpr's AX/BX/CX (#1/Phase
|
||||
// 3). Slice arm rides #38b's regex-
|
||||
// shaped consumer — the prior str-
|
||||
// only gate dropped .len/.cap (#24
|
||||
// gap's widener twin).
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((slot_off + 8 + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((slot_off + 8 + fi.foff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((slot_off + 8 + fi.foff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((slot_off + 8 + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
}; };
|
||||
fi = nil;
|
||||
} else {
|
||||
fi = fi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
fnode = fnode.next;
|
||||
};
|
||||
// #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 EXCEPT fsz==2 scalars, where the
|
||||
// old loop's fieldstoreop emitted MOVW
|
||||
// against cstage's MOVQ — a latent cs≠ww the
|
||||
// fill's #13-pinned dispatch closes. Mirror
|
||||
// of cstage cg_widen_tagged_store's
|
||||
// N_STRUCTLIT arm.
|
||||
cgstructlitfill(c, si, src, 0, 0, "",
|
||||
slot_off + 8);
|
||||
} else {
|
||||
// Struct ident source: byte-copy struct words to slot+8+k.
|
||||
let lc: *local = localfindnode(c, src.str);
|
||||
|
||||
Reference in New Issue
Block a user