wcc/cgen: #154 str==-global ident operand — name(SB) base in cbinop, not (BP) garbage (cstage)

The str==/!= arm of cbinop had an N_IDENT fast-path that assumed the operand
was a local: localfind returns 0 for a module-global str, so it loaded
(BP)/8(BP) — saved-BP/retaddr garbage — into rt_streq. `p == sepstr` silently
compared garbage (returned wrong). Mirror #148's global branch at both sub-sites
(rhs/lhs): off==0 && let_islet -> LEAQ name(SB) base, load ptr/len. Distinct
per-site fast-path, not a shared choke (the by-value-global-arg family
#148/#150/#151 closes separately). cstage-only; the wwstage str== twin is #146
(-> #125 batch).

Pin test/wcc/989_strglobeq (table-driven: const+let globals, rhs+lhs ident,
==/!=, unequal + len>1 rows; teeth-proven). Surfaced by the lib/path c3
buffer-ops gate-1 oracle.
This commit is contained in:
2026-06-08 13:18:59 +09:00
parent feae910a9b
commit 3f6b68cbf2
4 changed files with 155 additions and 8 deletions

View File

@@ -4568,10 +4568,23 @@ cgexpr(Cg *c, Node *n, Local *locals)
/* Push rhs (len, then ptr top) */
if (n->rhs->kind == N_IDENT) {
int off = localfind(locals, n->rhs->str);
ins2(c, A_MOVQ, amem(D_BP, off + 8), areg(D_AX));
ins1(c, A_PUSHQ, areg(D_AX));
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_AX));
ins1(c, A_PUSHQ, areg(D_AX));
/* #154: localfind→0 for a module global, but the
* str header lives at name(SB), not BP+0. Mirror
* #148's slice global branch (cgen.c:9082): LEAQ
* the symbol into a base reg, push len then ptr. */
if (off == 0 && let_islet(n->rhs->str)) {
ins2(c, A_LEAQ, masym(c, n->rhs->str),
areg(D_BX));
ins2(c, A_MOVQ, amem(D_BX, 8), areg(D_AX));
ins1(c, A_PUSHQ, areg(D_AX));
ins2(c, A_MOVQ, amem(D_BX, 0), areg(D_AX));
ins1(c, A_PUSHQ, areg(D_AX));
} else {
ins2(c, A_MOVQ, amem(D_BP, off + 8), areg(D_AX));
ins1(c, A_PUSHQ, areg(D_AX));
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_AX));
ins1(c, A_PUSHQ, areg(D_AX));
}
} else {
cgexpr(c, n->rhs, locals); /* AX=ptr, BX=len */
ins1(c, A_PUSHQ, areg(D_BX));
@@ -4580,10 +4593,21 @@ cgexpr(Cg *c, Node *n, Local *locals)
/* Push lhs */
if (n->lhs->kind == N_IDENT) {
int off = localfind(locals, n->lhs->str);
ins2(c, A_MOVQ, amem(D_BP, off + 8), areg(D_AX));
ins1(c, A_PUSHQ, areg(D_AX));
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_AX));
ins1(c, A_PUSHQ, areg(D_AX));
/* #154: see the rhs branch above — a module-global
* str ident lives at name(SB), not BP+0. */
if (off == 0 && let_islet(n->lhs->str)) {
ins2(c, A_LEAQ, masym(c, n->lhs->str),
areg(D_BX));
ins2(c, A_MOVQ, amem(D_BX, 8), areg(D_AX));
ins1(c, A_PUSHQ, areg(D_AX));
ins2(c, A_MOVQ, amem(D_BX, 0), areg(D_AX));
ins1(c, A_PUSHQ, areg(D_AX));
} else {
ins2(c, A_MOVQ, amem(D_BP, off + 8), areg(D_AX));
ins1(c, A_PUSHQ, areg(D_AX));
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_AX));
ins1(c, A_PUSHQ, areg(D_AX));
}
} else {
cgexpr(c, n->lhs, locals);
ins1(c, A_PUSHQ, areg(D_BX));