Compare commits

..

4 Commits

Author SHA1 Message Date
Quentin Carbonneaux
ac48f83f17 extern DYNCONST
New DYNCONST flag to access symbols from
dynamically-linked libraries. It can also
be used by frontends to implement PIC.

Review by mcf
2026-05-03 11:34:53 +02:00
Quentin Carbonneaux
577b4667db negated conditions rework
Floating point comparisons are subtle due to
unordered operands (i.e., when one or two of
the operands are nans).

In amd64 we arrange post isel to make sure we
only have comparisons that will return false
on unordered operands and for which we have a
negated version (returning true on unordered
operands).  I made this situation a bit more
explicit by marking unexpected comparisons
with "?" in amd64/emit.c.

In arm64, the instruction set is rich enough
to have a negated version for all operators
that returns true on unordered inputs. So we
build a backend specific table.

I made sure to remove cmpneg() from utils.c;
it is a footgun as it does not work well for
floating point ops because of the 'unordered'
edge case.

I found the ARM docs about condition codes
pretty bad; the following C program is a
good substitute:

    #include <stdio.h>
    int
    flags(float a, float b)
    {
    	int z, c, n, v, le;
    	__asm__(
    		"fcmpe %s5, %s6\n"
    		"\tcset %0, eq\n"
    		"\tcset %1, mi\n"
    		"\tcset %2, cs\n"
    		"\tcset %3, vs\n"
    		"\tcset %4, le\n"
    	: "=r"(z),"=r"(n),"=r"(c),"=r"(v),"=r"(le)
    	: "x"(a), "x"(b)
    	: "cc");
    	printf(
    		"cmp(%g,%g): z=%d n=%d c=%d v=%d le=%d\n",
    		a, b, z, n, c, v, le);
    }
    int
    main()
    {
    	flags(1.0, 1.0);
    	flags(0.0, 1.0);
    	flags(1.0, 0.0);
    	flags(0.0, 0.0/0.0);
    	flags(0.0/0.0, 0.0);
    }

Compile & run with:

    aarch64-linux-gnu-gcc -static -no-pie flags.c
    qemu-aarch64 ./a.out
2026-04-29 17:45:20 +02:00
Quentin Carbonneaux
bef981e9a1 no cmpneg() for float comparisons 2026-04-29 15:04:27 +02:00
Quentin Carbonneaux
0454fa259b arm64: fix unordered fp comparisons 2026-04-29 13:37:22 +02:00
4 changed files with 59 additions and 64 deletions

1
all.h
View File

@ -502,7 +502,6 @@ void emiti(Ins);
void idup(Blk *, Ins *, ulong); void idup(Blk *, Ins *, ulong);
Ins *icpy(Ins *, Ins *, ulong); Ins *icpy(Ins *, Ins *, ulong);
int cmpop(int); int cmpop(int);
int cmpneg(int);
int cmpwlneg(int); int cmpwlneg(int);
int clsmerge(short *, short); int clsmerge(short *, short);
int phicls(int, Tmp *); int phicls(int, Tmp *);

View File

@ -22,8 +22,8 @@ struct E {
X(Ciuge, "ae", "b") \ X(Ciuge, "ae", "b") \
X(Cieq, "z", "nz") \ X(Cieq, "z", "nz") \
X(Cine, "nz", "z") \ X(Cine, "nz", "z") \
X(NCmpI+Cfle, "be", "a") \ X(NCmpI+Cfle, "?" , "?") \
X(NCmpI+Cflt, "b", "ae") \ X(NCmpI+Cflt, "?", "?") \
X(NCmpI+Cfgt, "a", "be") \ X(NCmpI+Cfgt, "a", "be") \
X(NCmpI+Cfge, "ae", "b") \ X(NCmpI+Cfge, "ae", "b") \
X(NCmpI+Cfo, "np", "p") \ X(NCmpI+Cfo, "np", "p") \
@ -660,8 +660,8 @@ sysv_framesz(E *e)
void void
amd64_sysv_emitfn(Fn *fn, FILE *f) amd64_sysv_emitfn(Fn *fn, FILE *f)
{ {
static char *ctoa[] = { static char *ctoa[][2] = {
#define X(c, s, _) [c] = s, #define X(c, s, n) [c] = {s, n},
CMP(X) CMP(X)
#undef X #undef X
}; };
@ -747,9 +747,10 @@ amd64_sysv_emitfn(Fn *fn, FILE *f)
s = b->s1; s = b->s1;
b->s1 = b->s2; b->s1 = b->s2;
b->s2 = s; b->s2 = s;
n = 0;
} else } else
c = cmpneg(c); n = 1;
fprintf(f, "\tj%s %sbb%d\n", ctoa[c], fprintf(f, "\tj%s %sbb%d\n", ctoa[c][n],
T.asloc, id0+b->s2->id); T.asloc, id0+b->s2->id);
goto Jmp; goto Jmp;
} }
@ -785,15 +786,15 @@ winabi_framesz(E *e)
void void
amd64_winabi_emitfn(Fn *fn, FILE *f) amd64_winabi_emitfn(Fn *fn, FILE *f)
{ {
static char *ctoa[] = { static char *ctoa[][2] = {
#define X(c, s, _) [c] = s, #define X(c, s, n) [c] = {s, n},
CMP(X) CMP(X)
#undef X #undef X
}; };
static int id0; static int id0;
Blk *b, *s; Blk *b, *s;
Ins *i, itmp; Ins *i, itmp;
int *r, c, lbl; int *r, c, n, lbl;
E *e; E *e;
e = &(E){.f = f, .fn = fn}; e = &(E){.f = f, .fn = fn};
@ -860,13 +861,14 @@ amd64_winabi_emitfn(Fn *fn, FILE *f)
default: default:
c = b->jmp.type - Jjf; c = b->jmp.type - Jjf;
if (0 <= c && c <= NCmp) { if (0 <= c && c <= NCmp) {
if (b->link == b->s2) { if (b->link == b->s2 || c >= NCmpI) {
s = b->s1; s = b->s1;
b->s1 = b->s2; b->s1 = b->s2;
b->s2 = s; b->s2 = s;
n = 0;
} else } else
c = cmpneg(c); n = 1;
fprintf(f, "\tj%s %sbb%d\n", ctoa[c], fprintf(f, "\tj%s %sbb%d\n", ctoa[c][n],
T.asloc, id0+b->s2->id); T.asloc, id0+b->s2->id);
goto Jmp; goto Jmp;
} }

View File

@ -10,24 +10,24 @@ struct E {
}; };
#define CMP(X) \ #define CMP(X) \
X(Cieq, "eq") \ X(Cieq, "eq", "ne") \
X(Cine, "ne") \ X(Cine, "ne", "eq") \
X(Cisge, "ge") \ X(Cisge, "ge", "lt") \
X(Cisgt, "gt") \ X(Cisgt, "gt", "le") \
X(Cisle, "le") \ X(Cisle, "le", "gt") \
X(Cislt, "lt") \ X(Cislt, "lt", "ge") \
X(Ciuge, "cs") \ X(Ciuge, "cs", "cc") \
X(Ciugt, "hi") \ X(Ciugt, "hi", "ls") \
X(Ciule, "ls") \ X(Ciule, "ls", "hi") \
X(Ciult, "cc") \ X(Ciult, "cc", "cs") \
X(NCmpI+Cfeq, "eq") \ X(NCmpI+Cfeq, "eq", "ne") \
X(NCmpI+Cfge, "ge") \ X(NCmpI+Cfge, "ge", "lt") \
X(NCmpI+Cfgt, "gt") \ X(NCmpI+Cfgt, "gt", "le") \
X(NCmpI+Cfle, "ls") \ X(NCmpI+Cfle, "ls", "hi") \
X(NCmpI+Cflt, "mi") \ X(NCmpI+Cflt, "mi", "pl") \
X(NCmpI+Cfne, "ne") \ X(NCmpI+Cfne, "ne", "eq") \
X(NCmpI+Cfo, "vc") \ X(NCmpI+Cfo, "vc", "vs") \
X(NCmpI+Cfuo, "vs") X(NCmpI+Cfuo, "vs", "vc")
enum { enum {
Ki = -1, /* matches Kw and Kl */ Ki = -1, /* matches Kw and Kl */
@ -102,7 +102,7 @@ static struct {
{ Oacmn, Ki, "cmn %0, %1" }, { Oacmn, Ki, "cmn %0, %1" },
{ Oafcmp, Ka, "fcmpe %0, %1" }, { Oafcmp, Ka, "fcmpe %0, %1" },
#define X(c, str) \ #define X(c, str, _) \
{ Oflag+c, Ki, "cset %=, " str }, { Oflag+c, Ki, "cset %=, " str },
CMP(X) CMP(X)
#undef X #undef X
@ -544,8 +544,8 @@ framelayout(E *e)
void void
arm64_emitfn(Fn *fn, FILE *out) arm64_emitfn(Fn *fn, FILE *out)
{ {
static char *ctoa[] = { static char *ctoa[][2] = {
#define X(c, s) [c] = s, #define X(c, s, n) [c] = {s, n},
CMP(X) CMP(X)
#undef X #undef X
}; };
@ -676,11 +676,12 @@ arm64_emitfn(Fn *fn, FILE *out)
t = b->s1; t = b->s1;
b->s1 = b->s2; b->s1 = b->s2;
b->s2 = t; b->s2 = t;
n = 0;
} else } else
c = cmpneg(c); n = 1;
fprintf(e->f, fprintf(e->f,
"\tb%s\t%s%d\n", "\tb%s\t%s%d\n",
ctoa[c], T.asloc, id0+b->s2->id ctoa[c][n], T.asloc, id0+b->s2->id
); );
goto Jmp; goto Jmp;
} }

49
util.c
View File

@ -343,34 +343,27 @@ icpy(Ins *d, Ins *s, ulong n)
} }
static int cmptab[][2] ={ static int cmptab[][2] ={
/* negation swap */ /* negation swap */
[Ciule] = {Ciugt, Ciuge}, [Ciule] = {Ciugt, Ciuge},
[Ciult] = {Ciuge, Ciugt}, [Ciult] = {Ciuge, Ciugt},
[Ciugt] = {Ciule, Ciult}, [Ciugt] = {Ciule, Ciult},
[Ciuge] = {Ciult, Ciule}, [Ciuge] = {Ciult, Ciule},
[Cisle] = {Cisgt, Cisge}, [Cisle] = {Cisgt, Cisge},
[Cislt] = {Cisge, Cisgt}, [Cislt] = {Cisge, Cisgt},
[Cisgt] = {Cisle, Cislt}, [Cisgt] = {Cisle, Cislt},
[Cisge] = {Cislt, Cisle}, [Cisge] = {Cislt, Cisle},
[Cieq] = {Cine, Cieq}, [Cieq] = {Cine, Cieq},
[Cine] = {Cieq, Cine}, [Cine] = {Cieq, Cine},
[NCmpI+Cfle] = {NCmpI+Cfgt, NCmpI+Cfge}, [NCmpI+Cfle] = {-1, NCmpI+Cfge},
[NCmpI+Cflt] = {NCmpI+Cfge, NCmpI+Cfgt}, [NCmpI+Cflt] = {-1, NCmpI+Cfgt},
[NCmpI+Cfgt] = {NCmpI+Cfle, NCmpI+Cflt}, [NCmpI+Cfgt] = {-1, NCmpI+Cflt},
[NCmpI+Cfge] = {NCmpI+Cflt, NCmpI+Cfle}, [NCmpI+Cfge] = {-1, NCmpI+Cfle},
[NCmpI+Cfeq] = {NCmpI+Cfne, NCmpI+Cfeq}, [NCmpI+Cfeq] = {-1, NCmpI+Cfeq},
[NCmpI+Cfne] = {NCmpI+Cfeq, NCmpI+Cfne}, [NCmpI+Cfne] = {-1, NCmpI+Cfne},
[NCmpI+Cfo] = {NCmpI+Cfuo, NCmpI+Cfo}, [NCmpI+Cfo] = {-1, NCmpI+Cfo},
[NCmpI+Cfuo] = {NCmpI+Cfo, NCmpI+Cfuo}, [NCmpI+Cfuo] = {-1, NCmpI+Cfuo},
}; };
int
cmpneg(int c)
{
assert(0 <= c && c < NCmp);
return cmptab[c][0];
}
int int
cmpop(int c) cmpop(int c)
{ {
@ -382,9 +375,9 @@ int
cmpwlneg(int op) cmpwlneg(int op)
{ {
if (INRANGE(op, Ocmpw, Ocmpw1)) if (INRANGE(op, Ocmpw, Ocmpw1))
return cmpneg(op - Ocmpw) + Ocmpw; return cmptab[op - Ocmpw][0] + Ocmpw;
if (INRANGE(op, Ocmpl, Ocmpl1)) if (INRANGE(op, Ocmpl, Ocmpl1))
return cmpneg(op - Ocmpl) + Ocmpl; return cmptab[op - Ocmpl][0] + Ocmpl;
die("not a wl comparison"); die("not a wl comparison");
} }