wwstage compiled str ==/!= as a single CMPQ on the eager-eval'd ptr word (len ignored), so two distinct-pointer equal-content strings compared unequal. cstage was already correct (CALLs rt_streq, the #154 cbinop fix). The wwstage cgbin had no str-awareness -- every comparison fell to the generic CMPQ tail; the #154 fix was never mirrored. wwstage-only: a cgstreqpush helper + a str ==/!= branch at the top of cgbin (before the generic eval collapses the header), byte-matching cstage cbinop:4564-4623 -- push rhs/lhs (len,ptr), POPQ DI/SI/DX/CX, CALL rt_streq, XORQ $1 for !=. Gated on typeisstr (= cstage node_isstr, which also catches module-global str idents). cstage cgen unchanged (w6c md5 unchanged). The str== .s is byte-identical cs==ww for local, global, aliased, chained, and condition operands. Graduates 3 lib byte-id pins (test/wcc/989_lib_byteid #59.1 asciitest, #59.11 toktest, #59.12 asttest) M_DIVERGE->M_ID -- they used == on str and were pinned divergent because of this bug; now byte-identical. byte-id 990-997 8/8. test/wcc/827 table-driven.
This commit is contained in:
@@ -26856,6 +26856,43 @@ fn cgun(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// cgstreqpush — push one str operand's (len, then ptr) header words for
|
||||
// the rt_streq content-compare in cgbin. Mirrors cstage cgen.c:4568-4615:
|
||||
// an ident loads its 2-word header (ptr+len, NOT cap) from name(SB) for a
|
||||
// module-global str (#154 let_islet branch) or BP+off for a local; a
|
||||
// non-ident evals via cgexpr (AX=ptr, BX=len) and pushes BX then AX. Push
|
||||
// order is len then ptr so the matching POPQ pops ptr first.
|
||||
fn cgstreqpush(c: *cgen, op: *node) void = {
|
||||
if (op.kind == nkind.N_IDENT) {
|
||||
let nm: str = op.str;
|
||||
let lc: *local = localfindnode(c, nm);
|
||||
if (lc == nil && isletvar(c, nm)) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymnamehint(c, nm, c.curmod);
|
||||
emitline("(SB), BX\n");
|
||||
emitline("\tMOVQ\t8(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
return;
|
||||
};
|
||||
let off: i32 = 0;
|
||||
if (lc != nil) { off = lc.off; };
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
return;
|
||||
};
|
||||
cgexpr(c, op);
|
||||
emitline("\tPUSHQ\tBX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
};
|
||||
|
||||
fn cgbin(c: *cgen, n: *node) void = {
|
||||
// Short-circuit `&&` / `||`. Operands are bool (0/1); the type
|
||||
// checker enforces it. Eval LHS into AX, branch over RHS on the
|
||||
@@ -26877,6 +26914,26 @@ fn cgbin(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// #146 (#154 ww-twin): str ==/!= is a CONTENT compare via rt_streq,
|
||||
// not a ptr compare. Must run before the generic eager-eval tail
|
||||
// below collapses each str header to its ptr word (AX). Push rhs
|
||||
// then lhs (len, ptr each); POPQ DI/SI/DX/CX lands a.ptr,a.len,
|
||||
// b.ptr,b.len per rt/streq.s; CALL rt_streq -> AX in {0,1}; XOR 1
|
||||
// for !=. The gate reads the checker stamp (typeisstr) exactly like
|
||||
// cstage node_isstr. Byte-identical to cstage cbinop (cmd/w6c/
|
||||
// cgen.c:4564-4623). cstage was fixed by #154; this is its mirror.
|
||||
if ((n.op == tkind.TK_EQ || n.op == tkind.TK_NEQ)
|
||||
&& n.lhs != nil && n.rhs != nil
|
||||
&& typeisstr(n.lhs.type_: *tinfo)
|
||||
&& typeisstr(n.rhs.type_: *tinfo)) {
|
||||
cgstreqpush(c, n.rhs);
|
||||
cgstreqpush(c, n.lhs);
|
||||
emitline("\tPOPQ\tDI\n\tPOPQ\tSI\n\tPOPQ\tDX\n\tPOPQ\tCX\n");
|
||||
emitline("\tCALL\trt_streq(SB)\n");
|
||||
if (n.op == tkind.TK_NEQ) { emitline("\tXORQ\t$1, AX\n"); };
|
||||
return;
|
||||
};
|
||||
|
||||
let unsignd: bool = nodeisunsigned(c, n.lhs);
|
||||
if (!unsignd) { unsignd = nodeisunsigned(c, n.rhs); };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user