wcc/ww: is/as on a module-global tagged ident loads from g(SB)

The global tagged ident operand read saved BP instead of the global:
'is' compared garbage as the tag; 'as' never had a payload. Route the
load through the g(SB) base — tag at +0, payload at +8, cap at +16 for
str (one mechanism, both consumers). The 'is' half aligns ww UP
(cs==ww pinned); the 'as' half is a both-wrong pair — cstage spills an
uninitialized payload register (its N_TYPEASSERT assumes cgexpr filled
AX/DX/CX; filed as task #46), so its rows assert ww-runtime-correct
with the cs divergence documented until #46 lands. Review item #18.
This commit is contained in:
2026-06-12 09:03:40 +09:00
parent 1eceb46ac3
commit 4961a91d14
5 changed files with 311 additions and 9 deletions

View File

@@ -23353,12 +23353,20 @@ fn cgtypetest(c: *cgen, n: *node) void = {
let scrutoff: i32 = 0;
let scrutt: *node = nil;
let nonident: bool = false;
let globalident: bool = false;
if (lhs != nil) {
if (lhs.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, lhs.str);
if (lc != nil) {
scrutoff = lc.off;
scrutt = resolvetagged(c, lc.tnode);
} else {
// #18 is-half (align-UP): global tagged ident — tag word at
// g(SB)+0, no BP slot. cstage N_TYPETEST is uniformly cgexpr
// (MOVQ g(SB),AX); pre-fix the !nonident emit read 0(BP)=saved BP.
let gt: *node = letvartnode(c, lhs.str);
scrutt = resolvetagged(c, gt);
globalident = true;
};
} else {
// #45: non-ident scrutinee (xs[i], p.field, call).
@@ -23414,9 +23422,15 @@ fn cgtypetest(c: *cgen, n: *node) void = {
want = cgtagvariantidx(c, scrutt, n.rhs);
};
if (!nonident) {
emitline("\tMOVQ\t");
emitoff(scrutoff: i64);
emitline("(BP), AX\n");
if (globalident) {
emitline("\tMOVQ\t");
emitsymname(c, lhs.str);
emitline("(SB), AX\n");
} else {
emitline("\tMOVQ\t");
emitoff(scrutoff: i64);
emitline("(BP), AX\n");
};
};
let nel: str = mklabel(c, "is_ne");
let dnl: str = mklabel(c, "is_done");
@@ -23496,6 +23510,29 @@ fn cgtypeassert(c: *cgen, n: *node) void = {
if (lc != nil) {
scrutoff = lc.off;
scrutt = resolvetagged(c, lc.tnode);
} else {
// #18 as-half (#263 ww-runtime-correct; cstage N_TYPEASSERT on a
// global tagged ident spills uninitialized DX as the payload —
// task #46). No BP slot: copy the box words from g(SB) into a
// fresh @asrt_spill so the tag-check + payload load below index
// off memory like a local. cs!=ww residual until #46 lands.
let gt: *node = letvartnode(c, lhs.str);
scrutt = resolvetagged(c, gt);
let gsz: i32 = matchspillsz(c, scrutt);
scrutoff = localalloc(c, "@asrt_spill", gsz, nil);
emitline("\tLEAQ\t");
emitsymname(c, lhs.str);
emitline("(SB), AX\n");
let gk: i32 = 0;
for (gk < gsz) {
emitline("\tMOVQ\t");
emitdispreg(gk: i64, "AX");
emitline(", DX\n");
emitline("\tMOVQ\tDX, ");
emitoff((scrutoff + gk): i64);
emitline("(BP)\n");
gk += 8;
};
};
} else {
// Non-ident scrutinee (call result, arr[i], p.field, ?,

View File

@@ -582,12 +582,20 @@ fn cgtypetest(c: *cgen, n: *node) void = {
let scrutoff: i32 = 0;
let scrutt: *node = nil;
let nonident: bool = false;
let globalident: bool = false;
if (lhs != nil) {
if (lhs.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, lhs.str);
if (lc != nil) {
scrutoff = lc.off;
scrutt = resolvetagged(c, lc.tnode);
} else {
// #18 is-half (align-UP): global tagged ident — tag word at
// g(SB)+0, no BP slot. cstage N_TYPETEST is uniformly cgexpr
// (MOVQ g(SB),AX); pre-fix the !nonident emit read 0(BP)=saved BP.
let gt: *node = letvartnode(c, lhs.str);
scrutt = resolvetagged(c, gt);
globalident = true;
};
} else {
// #45: non-ident scrutinee (xs[i], p.field, call).
@@ -643,9 +651,15 @@ fn cgtypetest(c: *cgen, n: *node) void = {
want = cgtagvariantidx(c, scrutt, n.rhs);
};
if (!nonident) {
emitline("\tMOVQ\t");
emitoff(scrutoff: i64);
emitline("(BP), AX\n");
if (globalident) {
emitline("\tMOVQ\t");
emitsymname(c, lhs.str);
emitline("(SB), AX\n");
} else {
emitline("\tMOVQ\t");
emitoff(scrutoff: i64);
emitline("(BP), AX\n");
};
};
let nel: str = mklabel(c, "is_ne");
let dnl: str = mklabel(c, "is_done");
@@ -725,6 +739,29 @@ fn cgtypeassert(c: *cgen, n: *node) void = {
if (lc != nil) {
scrutoff = lc.off;
scrutt = resolvetagged(c, lc.tnode);
} else {
// #18 as-half (#263 ww-runtime-correct; cstage N_TYPEASSERT on a
// global tagged ident spills uninitialized DX as the payload —
// task #46). No BP slot: copy the box words from g(SB) into a
// fresh @asrt_spill so the tag-check + payload load below index
// off memory like a local. cs!=ww residual until #46 lands.
let gt: *node = letvartnode(c, lhs.str);
scrutt = resolvetagged(c, gt);
let gsz: i32 = matchspillsz(c, scrutt);
scrutoff = localalloc(c, "@asrt_spill", gsz, nil);
emitline("\tLEAQ\t");
emitsymname(c, lhs.str);
emitline("(SB), AX\n");
let gk: i32 = 0;
for (gk < gsz) {
emitline("\tMOVQ\t");
emitdispreg(gk: i64, "AX");
emitline(", DX\n");
emitline("\tMOVQ\tDX, ");
emitoff((scrutoff + gk): i64);
emitline("(BP)\n");
gk += 8;
};
};
} else {
// Non-ident scrutinee (call result, arr[i], p.field, ?,

View File

@@ -23353,12 +23353,20 @@ fn cgtypetest(c: *cgen, n: *node) void = {
let scrutoff: i32 = 0;
let scrutt: *node = nil;
let nonident: bool = false;
let globalident: bool = false;
if (lhs != nil) {
if (lhs.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, lhs.str);
if (lc != nil) {
scrutoff = lc.off;
scrutt = resolvetagged(c, lc.tnode);
} else {
// #18 is-half (align-UP): global tagged ident — tag word at
// g(SB)+0, no BP slot. cstage N_TYPETEST is uniformly cgexpr
// (MOVQ g(SB),AX); pre-fix the !nonident emit read 0(BP)=saved BP.
let gt: *node = letvartnode(c, lhs.str);
scrutt = resolvetagged(c, gt);
globalident = true;
};
} else {
// #45: non-ident scrutinee (xs[i], p.field, call).
@@ -23414,9 +23422,15 @@ fn cgtypetest(c: *cgen, n: *node) void = {
want = cgtagvariantidx(c, scrutt, n.rhs);
};
if (!nonident) {
emitline("\tMOVQ\t");
emitoff(scrutoff: i64);
emitline("(BP), AX\n");
if (globalident) {
emitline("\tMOVQ\t");
emitsymname(c, lhs.str);
emitline("(SB), AX\n");
} else {
emitline("\tMOVQ\t");
emitoff(scrutoff: i64);
emitline("(BP), AX\n");
};
};
let nel: str = mklabel(c, "is_ne");
let dnl: str = mklabel(c, "is_done");
@@ -23496,6 +23510,29 @@ fn cgtypeassert(c: *cgen, n: *node) void = {
if (lc != nil) {
scrutoff = lc.off;
scrutt = resolvetagged(c, lc.tnode);
} else {
// #18 as-half (#263 ww-runtime-correct; cstage N_TYPEASSERT on a
// global tagged ident spills uninitialized DX as the payload —
// task #46). No BP slot: copy the box words from g(SB) into a
// fresh @asrt_spill so the tag-check + payload load below index
// off memory like a local. cs!=ww residual until #46 lands.
let gt: *node = letvartnode(c, lhs.str);
scrutt = resolvetagged(c, gt);
let gsz: i32 = matchspillsz(c, scrutt);
scrutoff = localalloc(c, "@asrt_spill", gsz, nil);
emitline("\tLEAQ\t");
emitsymname(c, lhs.str);
emitline("(SB), AX\n");
let gk: i32 = 0;
for (gk < gsz) {
emitline("\tMOVQ\t");
emitdispreg(gk: i64, "AX");
emitline(", DX\n");
emitline("\tMOVQ\tDX, ");
emitoff((scrutoff + gk): i64);
emitline("(BP)\n");
gk += 8;
};
};
} else {
// Non-ident scrutinee (call result, arr[i], p.field, ?,