From 804921a3ab463848aa0ffbe495ca542b3789c841 Mon Sep 17 00:00:00 2001 From: Michael Forney Date: Tue, 17 Aug 2021 13:14:54 -0700 Subject: amd64/isel: fix floating < and <= result with NaN When the two operands are Unordered (for instance if one of them is NaN), ucomisd sets ZF=1, PF=1, and CF=1. When the result is LessThan, it sets ZF=0, PF=0, and CF=1. However, jb[e]/setb[e] only checks that CF=1 [or ZF=1] which causes the result to be true for unordered operands. To fix this, change the operand swap condition for these two floating point comparison types: always rewrite x < y as y > x, and never rewrite x > y as y < x. Add a test to check the result of cltd, cled, cgtd, cged, ceqd, and cned with arguments that are LessThan, Equal, GreaterThan, and Unordered. Additionally, check three different implementations for equality testing: one that uses the result of ceqd directly, one that uses the result to control a conditional jump, and one that uses the result both as a value and for a conditional jump. For now, unordered equality tests are still broken so they are disabled. --- amd64/isel.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) (limited to 'amd64') diff --git a/amd64/isel.c b/amd64/isel.c index 0b0a2df..07e6142 100644 --- a/amd64/isel.c +++ b/amd64/isel.c @@ -165,13 +165,25 @@ seladdr(Ref *r, ANum *an, Fn *fn) } static int -selcmp(Ref arg[2], int k, Fn *fn) +cmpswap(Ref arg[2], int op) +{ + switch (op) { + case NCmpI+Cflt: + case NCmpI+Cfle: + return 1; + case NCmpI+Cfgt: + case NCmpI+Cfge: + return 0; + } + return rtype(arg[0]) == RCon; +} + +static void +selcmp(Ref arg[2], int k, int swap, Fn *fn) { - int swap; Ref r; Ins *icmp; - swap = rtype(arg[0]) == RCon; if (swap) { r = arg[1]; arg[1] = arg[0]; @@ -180,20 +192,20 @@ selcmp(Ref arg[2], int k, Fn *fn) emit(Oxcmp, k, R, arg[1], arg[0]); icmp = curi; if (rtype(arg[0]) == RCon) { - assert(k == Kl); + assert(k != Kw); icmp->arg[1] = newtmp("isel", k, fn); emit(Ocopy, k, icmp->arg[1], arg[0], R); + fixarg(&curi->arg[0], k, curi, fn); } fixarg(&icmp->arg[0], k, icmp, fn); fixarg(&icmp->arg[1], k, icmp, fn); - return swap; } static void sel(Ins i, ANum *an, Fn *fn) { Ref r0, r1; - int x, k, kc; + int x, k, kc, swap; int64_t sz; Ins *i0, *i1; @@ -332,10 +344,11 @@ Emit: if (isload(i.op)) goto case_Oload; if (iscmp(i.op, &kc, &x)) { + swap = cmpswap(i.arg, x); + if (swap) + x = cmpop(x); emit(Oflag+x, k, i.to, R, R); - i1 = curi; - if (selcmp(i.arg, kc, fn)) - i1->op = Oflag + cmpop(x); + selcmp(i.arg, kc, swap, fn); break; } die("unknown instruction %s", optab[i.op].name); @@ -365,7 +378,7 @@ static void seljmp(Blk *b, Fn *fn) { Ref r; - int c, k; + int c, k, swap; Ins *fi; Tmp *t; @@ -384,14 +397,15 @@ seljmp(Blk *b, Fn *fn) } fi = flagi(b->ins, &b->ins[b->nins]); if (!fi || !req(fi->to, r)) { - selcmp((Ref[2]){r, CON_Z}, Kw, fn); /* todo, long jnz */ + selcmp((Ref[2]){r, CON_Z}, Kw, 0, fn); /* todo, long jnz */ b->jmp.type = Jjf + Cine; } else if (iscmp(fi->op, &k, &c)) { - if (rtype(fi->arg[0]) == RCon) + swap = cmpswap(fi->arg, c); + if (swap) c = cmpop(c); if (t->nuse == 1) { - selcmp(fi->arg, k, fn); + selcmp(fi->arg, k, swap, fn); *fi = (Ins){.op = Onop}; } b->jmp.type = Jjf + c; -- cgit 1.4.1