w6c+selfhost: cgen && and || short-circuit
Both stages were eagerly evaluating RHS regardless of LHS (eager ANDQ/ORQ on the two results). Now: eval LHS into AX, CMPQ $0 + JE/JNE to a per-call-site label, eval RHS into AX, fall through. AX holds the LHS sentinel on the skipped path — typechecker already enforces bool operands. Surfaced by lib/getopt's nil-argv guard segfault. Six new rows in test/wcc/700_e2e.c, three of which segfault pre-fix. lib/getopt test comment relaxed; nested-if kept as regression marker.
This commit is contained in:
@@ -1380,6 +1380,22 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
break;
|
||||
case N_BIN: {
|
||||
/* Short-circuit `&&` / `||`. Operands are bool (0/1); the
|
||||
* type checker enforces it. Eval LHS into AX, branch over
|
||||
* RHS on the short-circuit polarity, otherwise eval RHS
|
||||
* into AX. The surviving AX is the result. Must precede
|
||||
* any eager-eval path below — `if (p != nil && p.x > 0)`
|
||||
* would segfault on a nil deref otherwise. */
|
||||
if (n->op == TK_AND || n->op == TK_OR) {
|
||||
char *end = mklabel(c, n->op == TK_AND ? "andend" : "orend");
|
||||
int jshrt = (n->op == TK_AND) ? A_JE : A_JNE;
|
||||
cgexpr(c, n->lhs, locals);
|
||||
ins2(c, A_CMPQ, aimm(0), areg(D_AX));
|
||||
ins1(c, jshrt, abranch(end));
|
||||
cgexpr(c, n->rhs, locals);
|
||||
label(c, end);
|
||||
break;
|
||||
}
|
||||
/* str == str / str != str — delegate to rt_streq, which
|
||||
* does the byte-by-byte compare. */
|
||||
if ((n->op == TK_EQ || n->op == TK_NEQ) &&
|
||||
@@ -1550,14 +1566,8 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
label(c, e);
|
||||
break;
|
||||
}
|
||||
case TK_AND: case TK_OR: {
|
||||
/* short-circuit not yet — eager evaluation. */
|
||||
if (n->op == TK_AND)
|
||||
ins2(c, A_ANDQ, areg(D_BX), areg(D_AX));
|
||||
else
|
||||
ins2(c, A_ORQ, areg(D_BX), areg(D_AX));
|
||||
break;
|
||||
}
|
||||
/* TK_AND / TK_OR handled with short-circuit codegen at the
|
||||
* top of N_BIN — they never reach this eager-eval switch. */
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user