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:
@@ -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) {
|
||||
|
||||
@@ -62,6 +62,26 @@ static const struct row rows[] = {
|
||||
/* float: arg, arith, literal, cast back to int */
|
||||
{ "fn area(r: f64) f64 = { return 3.14 * r * r; };\n"
|
||||
"fn main() i32 = { let a: f64 = area(5.0); return a: i32; };", 78 },
|
||||
/* float comparison: must emit UCOMISD + JA (not CMPQ + JG) */
|
||||
{ "fn main() i32 = {\n"
|
||||
" let a: f64 = 1.5;\n"
|
||||
" let b: f64 = 2.5;\n"
|
||||
" if (a < b) { if (b > a) { return 7; }; };\n"
|
||||
" return 0;\n"
|
||||
"};", 7 },
|
||||
/* float ==/!= via UCOMISD */
|
||||
{ "fn main() i32 = {\n"
|
||||
" let a: f64 = 3.14;\n"
|
||||
" let b: f64 = 3.14;\n"
|
||||
" if (a == b) { return 11; };\n"
|
||||
" return 0;\n"
|
||||
"};", 11 },
|
||||
/* function pointer: take address of a named fn, call indirectly */
|
||||
{ "fn add(a: i32, b: i32) i32 = { return a + b; };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let fp: fn(a: i32, b: i32) i32 = add;\n"
|
||||
" return fp(20, 22);\n"
|
||||
"};", 42 },
|
||||
/* string literal via syscall — exit code = bytes written */
|
||||
{ "@symbol(\"rt_syscall\") fn rt_syscall(num: i64, a: i64, b: i64, c: i64) i64;\n"
|
||||
"fn print(s: str) i64 = { return rt_syscall(1, 1, s.ptr: i64, s.len: i64); };\n"
|
||||
|
||||
Reference in New Issue
Block a user