6c: float compare, fn-address @symbol, indirect call

Three N_BIN/N_IDENT/N_CALL sites missed cases that bit ncurses
demos and dyn-linker exercises:

- Float compare emitted CMPQ + signed Jcc on f64 bits. Now goes
  through UCOMISD/UCOMISS + the unsigned Jcc family, mirroring
  Plan 9 6c (txt.c around AUCOMISD).
- LEAQ-of-fn-ident (taking the address of a function as a value)
  used n->str without ffi_resolve, so binding a *fn from an
  @symbol("name") fn declaration produced a reference to the
  ww ident rather than the C symbol.
- N_CALL on a bare ident with no localfind hit was a direct
  CALL ident(SB). Now checks localfind first and indirects
  through AX when the callee names a local fn-pointer slot.

Hare/QBE handles both shapes the same way (sort/search.ha:15
calls cmp(...) where cmp is a *cmpfunc parameter).
This commit is contained in:
2026-05-11 09:42:02 +09:00
parent cb57cd795d
commit 635818eb13
2 changed files with 88 additions and 3 deletions

View File

@@ -507,7 +507,14 @@ cgexpr(Cg *c, Node *n, Local *locals)
Type *t = n->type;
Type *u = (t && t->kind == TY_NAMED) ? t->under : t;
if (u && u->kind == TY_FN) {
ins2(c, A_LEAQ, asym(n->str), areg(D_AX));
/* Take the address of a function. Apply
* @symbol resolution so taking the address
* of a body-less FFI binding yields the C
* symbol, not the ww-side ident. Hare emits
* the same `$symname` for both call and
* address-of via QBE; here we mirror that. */
ins2(c, A_LEAQ,
asym(ffi_resolve(n->str)), areg(D_AX));
break;
}
for (Sdef *s = sdefs; s; s = s->next) {
@@ -593,6 +600,48 @@ cgexpr(Cg *c, Node *n, Local *locals)
ins2(c, A_XORQ, aimm(1), areg(D_AX));
break;
}
/* Float comparison: operands are float but the BIN node's
* type is bool, so node_isfloat(n) is false — we have to
* inspect n->lhs. UCOMISD/UCOMISS sets ZF/CF as if an
* unsigned compare, so the JA family is the right Jcc set
* regardless of how the operand types are signed. Plan 9's
* own 6c picks the same pattern (txt.c around AUCOMISD).
* NaN handling: UCOMI sets PF on unordered; we ignore it,
* which means NaN compares behave like Hare's default. */
if (n->lhs && node_isfloat(n->lhs) &&
(n->op == TK_EQ || n->op == TK_NEQ
|| n->op == TK_LT || n->op == TK_LE
|| n->op == TK_GT || n->op == TK_GE)) {
int isf32 = node_isf32(n->lhs);
int mov = isf32 ? A_MOVSS : A_MOVSD;
int ucomi = isf32 ? A_UCOMISS : A_UCOMISD;
cgexpr(c, n->rhs, locals); /* rhs → X0 */
ins2(c, A_SUBQ, aimm(8), areg(D_SP));
ins2(c, mov, areg(D_X0), amem(D_SP, 0));
cgexpr(c, n->lhs, locals); /* lhs → X0 */
ins2(c, mov, amem(D_SP, 0), areg(D_X1));
ins2(c, A_ADDQ, aimm(8), areg(D_SP));
ins2(c, ucomi, areg(D_X1), areg(D_X0));
int op = A_JE;
switch (n->op) {
case TK_EQ: op = A_JE; break;
case TK_NEQ: op = A_JNE; break;
case TK_LT: op = A_JB; break;
case TK_LE: op = A_JBE; break;
case TK_GT: op = A_JA; break;
case TK_GE: op = A_JAE; break;
default: break;
}
char *t = mklabel(c, "ct");
char *e = mklabel(c, "ce");
ins1(c, op, abranch(t));
ins2(c, A_MOVQ, aimm(0), areg(D_AX));
ins1(c, A_JMP, abranch(e));
label(c, t);
ins2(c, A_MOVQ, aimm(1), areg(D_AX));
label(c, e);
break;
}
if (node_isfloat(n)) {
int isf32 = node_isf32(n);
int mov = isf32 ? A_MOVSS : A_MOVSD;
@@ -1389,7 +1438,22 @@ cgexpr(Cg *c, Node *n, Local *locals)
if (cu && cu->kind == TY_FN && cu->variadic)
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
if (n->lhs->kind == N_IDENT) {
ins1(c, A_CALL, asym(ffi_resolve(n->lhs->str)));
/* If the callee names a local variable holding a
* function pointer, load it and call indirect. Without
* this check `CALL fp(SB)` is emitted as if `fp` were
* a global symbol — the linker rightly fails. Hare /
* QBE handles this by treating any non-`$symbol` value
* as an indirect target; we get the same effect by
* reusing the cgexpr path. */
int loff = localfind(locals, n->lhs->str);
if (loff != 0) {
ins2(c, A_MOVQ,
amem(D_BP, loff), areg(D_AX));
ins1(c, A_CALL, areg(D_AX));
} else {
ins1(c, A_CALL,
asym(ffi_resolve(n->lhs->str)));
}
} else if (n->lhs->kind == N_DOT && n->lhs->lhs &&
n->lhs->lhs->kind == N_IDENT) {
/* `m.fn()` is module-qualified iff the ident has no
@@ -1604,7 +1668,8 @@ cgexpr(Cg *c, Node *n, Local *locals)
Type *t = n->type;
Type *tu = (t && t->kind == TY_NAMED) ? t->under : t;
if (tu && tu->kind == TY_FN) {
ins2(c, A_LEAQ, asym(n->str), areg(D_AX));
ins2(c, A_LEAQ,
asym(ffi_resolve(n->str)), areg(D_AX));
break;
}
for (Sdef *s = sdefs; s; s = s->next) {