wcc_ww/cgen: #55 tagged-source arg-widen into wider tagged slot (align-up)

wwstage pushargsrev treated a narrower tagged-union argument widened into
a wider tagged param slot as a concrete variant: taggedvariantindex<0
clamped the tag to 0 and pushed word0 only (deref/index/dot silent-wrong;
ident ran correct only by prefix-union tag-index luck). cstage is correct
(cg_widen_tagged_push routes src_is_tagged unconditionally); align ww UP.

Three arms in cgenutil.ww, all mirroring cstage cgen.c:
 - slot-gate the ident aistagged short-circuit so a slot-differ tagged
   ident falls to the widen path instead of the raw 2-word push;
 - route a tagged source in the widensz>0 arm through @tagscr +
   cgwidentaggedstore + push high->low (cgen.c cg_widen_tagged_push);
 - cgwidentaggedstore cursor arm (<=32B INDEX/DOT) spills by source
   width, zero-pads, and tag-remaps (cgen.c 2698-2714) — was dst-slot
   spill of stale high regs with no pad and no remap.

Same-slot tagged->tagged is byte-id-neutral by construction (empty pad +
identity remap). cstage untouched; 4 legs x {aligned, misaligned-tag}
converge ww->cs byte-identical. Pin 944_tagged_widen_arg_run.
This commit is contained in:
2026-06-06 15:15:13 +09:00
parent 00d9580c9f
commit cc896bd078
5 changed files with 465 additions and 15 deletions

View File

@@ -16402,7 +16402,23 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node, memphase: bool) i32 = {
if (arg.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, arg.str);
if (lc != nil) {
aistagged = istaggedtype(c, lc.tnode);
// #55: only treat a tagged ident as
// "already tagged" (natural push, no remap)
// when its slot MATCHES the param. On slot-
// DIFFER the source is a NARROWER union widened
// into a wider one — fall to the widen scratch
// + tag-remap below (the slot-gated INDEX/DOT/
// STAR arms' twin). Pre-#55 the ungated TRUE
// natural-pushed the narrower box's words with
// no remap (aligned-tag LUCK, misaligned-tag
// wrong). cstage routes every tagged source
// through cg_widen_tagged_store (cmd/w6c/
// cgen.c:2982 src_is_tagged).
if (istaggedtype(c, lc.tnode)) {
if (slotsize(c, lc.tnode) == slotsize(c, ptype)) {
aistagged = true;
};
};
};
};
// #21: a CALL returning a tagged-union must
@@ -16496,13 +16512,26 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node, memphase: bool) i32 = {
let argtup: *tinfo = arg.type_: *tinfo;
argtup = tichase(argtup);
let argistuple: bool = false;
// #55: a tagged SOURCE widened into a wider/reordered tagged param
// (slot-DIFFER — the ident/deref/index/dot legs that fell past the
// slot-gated aistagged arms above) has no direct-push shape: the
// scalar fast arm below would box word0 with a tag clamped to 0,
// dropping the real tag + high words and skipping the variant
// remap. Route through the @tagscr store, whose ident/memread/
// cursor arms copy the box + tag-remap. Mirrors cstage
// cg_widen_tagged_push's unconditional src_is_tagged scratch
// routing (cmd/w6c/cgen.c:2982).
let argistagged: bool = false;
if (argtup != nil) {
if (argtup.kind == tykind.TY_TUPLE) {
argistuple = true;
};
if (argtup.kind == tykind.TY_TAGGED) {
argistagged = true;
};
};
let pname: str = rhsstructpayload(c, arg);
if (pname.len > 0 || argistuple) {
if (pname.len > 0 || argistuple || argistagged) {
let ptype: *node = param.lhs;
let scroff: i32 = tagscradd(c, widensz);
emitline("\tXORQ\tAX, AX\n");
@@ -20017,25 +20046,48 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
cgwidentagremap(c, dt, src.type_: *tinfo, slot_off);
return;
};
// #55: spill the AX/DX/CX/R8 cursor by the SOURCE box width
// (su.size), zero-pad to the dst slot, then tag-remap — the
// cursor twin of the ident / memread arms above. Pre-#55 it
// spilled by slot_sz (reading STALE high regs when the source is
// narrower) and skipped the pad + remap, so an INDEX/DOT tagged
// element widened into a wider/reordered union pushed a stale word
// and the un-remapped source tag (silent cs!=ww). Mirrors cstage
// cg_widen_tagged_store's cursor arm + shared pad/remap tail
// (cmd/w6c/cgen.c:2695-2714). Same-slot source (ssz==slot_sz)
// leaves the pad empty + an identity remap, so the emission is
// byte-identical to the prior arm for the exact-slot callers.
cgexpr(c, src);
let ssz: i32 = su.size: i32;
emitline("\tMOVQ\tAX, ");
emitoff(slot_off: i64);
emitline("(BP)\n");
if (slot_sz > 8) {
if (ssz > 8) {
emitline("\tMOVQ\tDX, ");
emitoff((slot_off + 8): i64);
emitline("(BP)\n");
};
if (slot_sz > 16) {
if (ssz > 16) {
emitline("\tMOVQ\tCX, ");
emitoff((slot_off + 16): i64);
emitline("(BP)\n");
};
if (slot_sz > 24) {
if (ssz > 24) {
emitline("\tMOVQ\tR8, ");
emitoff((slot_off + 24): i64);
emitline("(BP)\n");
};
if (ssz < slot_sz) {
emitline("\tXORQ\tAX, AX\n");
let pp: i32 = ssz;
for (pp < slot_sz) {
emitline("\tMOVQ\tAX, ");
emitoff((slot_off + pp): i64);
emitline("(BP)\n");
pp += 8;
};
};
cgwidentagremap(c, dt, src.type_: *tinfo, slot_off);
return;
};
// #37 (rule 7): a >32B TAGGED source of a kind the resolver arms

View File

@@ -261,7 +261,23 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node, memphase: bool) i32 = {
if (arg.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, arg.str);
if (lc != nil) {
aistagged = istaggedtype(c, lc.tnode);
// #55: only treat a tagged ident as
// "already tagged" (natural push, no remap)
// when its slot MATCHES the param. On slot-
// DIFFER the source is a NARROWER union widened
// into a wider one — fall to the widen scratch
// + tag-remap below (the slot-gated INDEX/DOT/
// STAR arms' twin). Pre-#55 the ungated TRUE
// natural-pushed the narrower box's words with
// no remap (aligned-tag LUCK, misaligned-tag
// wrong). cstage routes every tagged source
// through cg_widen_tagged_store (cmd/w6c/
// cgen.c:2982 src_is_tagged).
if (istaggedtype(c, lc.tnode)) {
if (slotsize(c, lc.tnode) == slotsize(c, ptype)) {
aistagged = true;
};
};
};
};
// #21: a CALL returning a tagged-union must
@@ -355,13 +371,26 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node, memphase: bool) i32 = {
let argtup: *tinfo = arg.type_: *tinfo;
argtup = tichase(argtup);
let argistuple: bool = false;
// #55: a tagged SOURCE widened into a wider/reordered tagged param
// (slot-DIFFER — the ident/deref/index/dot legs that fell past the
// slot-gated aistagged arms above) has no direct-push shape: the
// scalar fast arm below would box word0 with a tag clamped to 0,
// dropping the real tag + high words and skipping the variant
// remap. Route through the @tagscr store, whose ident/memread/
// cursor arms copy the box + tag-remap. Mirrors cstage
// cg_widen_tagged_push's unconditional src_is_tagged scratch
// routing (cmd/w6c/cgen.c:2982).
let argistagged: bool = false;
if (argtup != nil) {
if (argtup.kind == tykind.TY_TUPLE) {
argistuple = true;
};
if (argtup.kind == tykind.TY_TAGGED) {
argistagged = true;
};
};
let pname: str = rhsstructpayload(c, arg);
if (pname.len > 0 || argistuple) {
if (pname.len > 0 || argistuple || argistagged) {
let ptype: *node = param.lhs;
let scroff: i32 = tagscradd(c, widensz);
emitline("\tXORQ\tAX, AX\n");
@@ -3876,25 +3905,48 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
cgwidentagremap(c, dt, src.type_: *tinfo, slot_off);
return;
};
// #55: spill the AX/DX/CX/R8 cursor by the SOURCE box width
// (su.size), zero-pad to the dst slot, then tag-remap — the
// cursor twin of the ident / memread arms above. Pre-#55 it
// spilled by slot_sz (reading STALE high regs when the source is
// narrower) and skipped the pad + remap, so an INDEX/DOT tagged
// element widened into a wider/reordered union pushed a stale word
// and the un-remapped source tag (silent cs!=ww). Mirrors cstage
// cg_widen_tagged_store's cursor arm + shared pad/remap tail
// (cmd/w6c/cgen.c:2695-2714). Same-slot source (ssz==slot_sz)
// leaves the pad empty + an identity remap, so the emission is
// byte-identical to the prior arm for the exact-slot callers.
cgexpr(c, src);
let ssz: i32 = su.size: i32;
emitline("\tMOVQ\tAX, ");
emitoff(slot_off: i64);
emitline("(BP)\n");
if (slot_sz > 8) {
if (ssz > 8) {
emitline("\tMOVQ\tDX, ");
emitoff((slot_off + 8): i64);
emitline("(BP)\n");
};
if (slot_sz > 16) {
if (ssz > 16) {
emitline("\tMOVQ\tCX, ");
emitoff((slot_off + 16): i64);
emitline("(BP)\n");
};
if (slot_sz > 24) {
if (ssz > 24) {
emitline("\tMOVQ\tR8, ");
emitoff((slot_off + 24): i64);
emitline("(BP)\n");
};
if (ssz < slot_sz) {
emitline("\tXORQ\tAX, AX\n");
let pp: i32 = ssz;
for (pp < slot_sz) {
emitline("\tMOVQ\tAX, ");
emitoff((slot_off + pp): i64);
emitline("(BP)\n");
pp += 8;
};
};
cgwidentagremap(c, dt, src.type_: *tinfo, slot_off);
return;
};
// #37 (rule 7): a >32B TAGGED source of a kind the resolver arms

View File

@@ -16402,7 +16402,23 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node, memphase: bool) i32 = {
if (arg.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, arg.str);
if (lc != nil) {
aistagged = istaggedtype(c, lc.tnode);
// #55: only treat a tagged ident as
// "already tagged" (natural push, no remap)
// when its slot MATCHES the param. On slot-
// DIFFER the source is a NARROWER union widened
// into a wider one — fall to the widen scratch
// + tag-remap below (the slot-gated INDEX/DOT/
// STAR arms' twin). Pre-#55 the ungated TRUE
// natural-pushed the narrower box's words with
// no remap (aligned-tag LUCK, misaligned-tag
// wrong). cstage routes every tagged source
// through cg_widen_tagged_store (cmd/w6c/
// cgen.c:2982 src_is_tagged).
if (istaggedtype(c, lc.tnode)) {
if (slotsize(c, lc.tnode) == slotsize(c, ptype)) {
aistagged = true;
};
};
};
};
// #21: a CALL returning a tagged-union must
@@ -16496,13 +16512,26 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node, memphase: bool) i32 = {
let argtup: *tinfo = arg.type_: *tinfo;
argtup = tichase(argtup);
let argistuple: bool = false;
// #55: a tagged SOURCE widened into a wider/reordered tagged param
// (slot-DIFFER — the ident/deref/index/dot legs that fell past the
// slot-gated aistagged arms above) has no direct-push shape: the
// scalar fast arm below would box word0 with a tag clamped to 0,
// dropping the real tag + high words and skipping the variant
// remap. Route through the @tagscr store, whose ident/memread/
// cursor arms copy the box + tag-remap. Mirrors cstage
// cg_widen_tagged_push's unconditional src_is_tagged scratch
// routing (cmd/w6c/cgen.c:2982).
let argistagged: bool = false;
if (argtup != nil) {
if (argtup.kind == tykind.TY_TUPLE) {
argistuple = true;
};
if (argtup.kind == tykind.TY_TAGGED) {
argistagged = true;
};
};
let pname: str = rhsstructpayload(c, arg);
if (pname.len > 0 || argistuple) {
if (pname.len > 0 || argistuple || argistagged) {
let ptype: *node = param.lhs;
let scroff: i32 = tagscradd(c, widensz);
emitline("\tXORQ\tAX, AX\n");
@@ -20017,25 +20046,48 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
cgwidentagremap(c, dt, src.type_: *tinfo, slot_off);
return;
};
// #55: spill the AX/DX/CX/R8 cursor by the SOURCE box width
// (su.size), zero-pad to the dst slot, then tag-remap — the
// cursor twin of the ident / memread arms above. Pre-#55 it
// spilled by slot_sz (reading STALE high regs when the source is
// narrower) and skipped the pad + remap, so an INDEX/DOT tagged
// element widened into a wider/reordered union pushed a stale word
// and the un-remapped source tag (silent cs!=ww). Mirrors cstage
// cg_widen_tagged_store's cursor arm + shared pad/remap tail
// (cmd/w6c/cgen.c:2695-2714). Same-slot source (ssz==slot_sz)
// leaves the pad empty + an identity remap, so the emission is
// byte-identical to the prior arm for the exact-slot callers.
cgexpr(c, src);
let ssz: i32 = su.size: i32;
emitline("\tMOVQ\tAX, ");
emitoff(slot_off: i64);
emitline("(BP)\n");
if (slot_sz > 8) {
if (ssz > 8) {
emitline("\tMOVQ\tDX, ");
emitoff((slot_off + 8): i64);
emitline("(BP)\n");
};
if (slot_sz > 16) {
if (ssz > 16) {
emitline("\tMOVQ\tCX, ");
emitoff((slot_off + 16): i64);
emitline("(BP)\n");
};
if (slot_sz > 24) {
if (ssz > 24) {
emitline("\tMOVQ\tR8, ");
emitoff((slot_off + 24): i64);
emitline("(BP)\n");
};
if (ssz < slot_sz) {
emitline("\tXORQ\tAX, AX\n");
let pp: i32 = ssz;
for (pp < slot_sz) {
emitline("\tMOVQ\tAX, ");
emitoff((slot_off + pp): i64);
emitline("(BP)\n");
pp += 8;
};
};
cgwidentagremap(c, dt, src.type_: *tinfo, slot_off);
return;
};
// #37 (rule 7): a >32B TAGGED source of a kind the resolver arms