Change float comparison operators to return false for NaN (bug 6107, r=ds).

--HG--
extra : rebase_source : a11c56fb23d6617545def3591ec6100dd143eb3e
This commit is contained in:
David Anderson
2014-04-22 19:40:45 -07:00
parent aa85a1866e
commit e69e9eddc7
10 changed files with 324 additions and 90 deletions
+35 -33
View File
@@ -75,45 +75,47 @@ BaseRuntime::~BaseRuntime()
free(m_plugin.name);
}
struct NativeMapping {
const char *name;
unsigned opcode;
};
static const NativeMapping sNativeMap[] = {
{ "FloatAbs", OP_FABS },
{ "FloatAdd", OP_FLOATADD },
{ "FloatSub", OP_FLOATSUB },
{ "FloatMul", OP_FLOATMUL },
{ "FloatDiv", OP_FLOATDIV },
{ "float", OP_FLOAT },
{ "FloatCompare", OP_FLOATCMP },
{ "RoundToCeil", OP_RND_TO_CEIL },
{ "RoundToZero", OP_RND_TO_ZERO },
{ "RoundToFloor", OP_RND_TO_FLOOR },
{ "RoundToNearest", OP_RND_TO_NEAREST },
{ "__FLOAT_GT__", OP_FLOAT_GT },
{ "__FLOAT_GE__", OP_FLOAT_GE },
{ "__FLOAT_LT__", OP_FLOAT_LT },
{ "__FLOAT_LE__", OP_FLOAT_LE },
{ "__FLOAT_EQ__", OP_FLOAT_EQ },
{ "__FLOAT_NE__", OP_FLOAT_NE },
{ "__FLOAT_NOT__", OP_FLOAT_NOT },
{ NULL, 0 },
};
void
BaseRuntime::SetupFloatNativeRemapping()
{
float_table_ = new floattbl_t[m_plugin.num_natives];
for (size_t i = 0; i < m_plugin.num_natives; i++) {
const char *name = m_plugin.natives[i].name;
if (!strcmp(name, "FloatAbs")) {
float_table_[i].found = true;
float_table_[i].index = OP_FABS;
} else if (!strcmp(name, "FloatAdd")) {
float_table_[i].found = true;
float_table_[i].index = OP_FLOATADD;
} else if (!strcmp(name, "FloatSub")) {
float_table_[i].found = true;
float_table_[i].index = OP_FLOATSUB;
} else if (!strcmp(name, "FloatMul")) {
float_table_[i].found = true;
float_table_[i].index = OP_FLOATMUL;
} else if (!strcmp(name, "FloatDiv")) {
float_table_[i].found = true;
float_table_[i].index = OP_FLOATDIV;
} else if (!strcmp(name, "float")) {
float_table_[i].found = true;
float_table_[i].index = OP_FLOAT;
} else if (!strcmp(name, "FloatCompare")) {
float_table_[i].found = true;
float_table_[i].index = OP_FLOATCMP;
} else if (!strcmp(name, "RoundToZero")) {
float_table_[i].found = true;
float_table_[i].index = OP_RND_TO_ZERO;
} else if (!strcmp(name, "RoundToCeil")) {
float_table_[i].found = true;
float_table_[i].index = OP_RND_TO_CEIL;
} else if (!strcmp(name, "RoundToFloor")) {
float_table_[i].found = true;
float_table_[i].index = OP_RND_TO_FLOOR;
} else if (!strcmp(name, "RoundToNearest")) {
float_table_[i].found = true;
float_table_[i].index = OP_RND_TO_NEAREST;
const NativeMapping *iter = sNativeMap;
while (iter->name) {
if (strcmp(name, iter->name) == 0) {
float_table_[i].found = true;
float_table_[i].index = iter->opcode;
break;
}
iter++;
}
}
}
+12 -5
View File
@@ -238,15 +238,22 @@
_(ENDPROC, "endproc") \
_(FABS, "fabs") \
_(FLOAT, "float") \
_(FLOATADD, "floatadd") \
_(FLOATSUB, "floatsub") \
_(FLOATMUL, "floatmul") \
_(FLOATDIV, "floatdiv") \
_(FLOATADD, "float.add") \
_(FLOATSUB, "float.sub") \
_(FLOATMUL, "float.mul") \
_(FLOATDIV, "float.div") \
_(RND_TO_NEAREST, "round") \
_(RND_TO_FLOOR, "floor") \
_(RND_TO_CEIL, "ceil") \
_(RND_TO_ZERO, "rndtozero") \
_(FLOATCMP, "floatcmp")
_(FLOATCMP, "float.cmp") \
_(FLOAT_GT, "float.gt") \
_(FLOAT_GE, "float.ge") \
_(FLOAT_LT, "float.lt") \
_(FLOAT_LE, "float.le") \
_(FLOAT_NE, "float.ne") \
_(FLOAT_EQ, "float.eq") \
_(FLOAT_NOT, "float.not")
enum OPCODE {
#define _(op, text) OP_##op,
+102 -1
View File
@@ -1085,12 +1085,16 @@ Compiler::emitOp(OPCODE op)
__ addl(stk, 4);
break;
// This is the old float cmp, which returns ordered results. In newly
// compiled code it should not be used or generated.
//
// Note that the checks here are inverted: the test is |rhs OP lhs|.
case OP_FLOATCMP:
{
Label bl, ab, done;
if (MacroAssemblerX86::Features().sse) {
__ movss(xmm0, Operand(stk, 4));
__ ucomiss(xmm0, Operand(stk, 0));
__ ucomiss(Operand(stk, 0), xmm0);
} else {
__ fld32(Operand(stk, 0));
__ fld32(Operand(stk, 4));
@@ -1111,6 +1115,53 @@ Compiler::emitOp(OPCODE op)
break;
}
case OP_FLOAT_GT:
emitFloatCmp(above);
break;
case OP_FLOAT_GE:
emitFloatCmp(above_equal);
break;
case OP_FLOAT_LE:
emitFloatCmp(below_equal);
break;
case OP_FLOAT_LT:
emitFloatCmp(below);
break;
case OP_FLOAT_EQ:
emitFloatCmp(equal);
break;
case OP_FLOAT_NE:
emitFloatCmp(not_equal);
break;
case OP_FLOAT_NOT:
{
if (MacroAssemblerX86::Features().sse) {
__ xorps(xmm0, xmm0);
__ ucomiss(Operand(stk, 0), xmm0);
} else {
__ fld32(Operand(stk, 0));
__ fldz();
__ fucomip(st1);
__ fstp(st0);
}
// See emitFloatCmp() - this is a shorter version.
Label done;
__ movl(eax, 1);
__ j(parity, &done);
__ set(zero, r8_al);
__ bind(&done);
__ addl(stk, 4);
break;
}
case OP_STACK:
{
cell_t amount = readCell();
@@ -1671,6 +1722,56 @@ Compiler::emitErrorPath(Label *dest, int code)
}
}
void
Compiler::emitFloatCmp(ConditionCode cc)
{
unsigned lhs = 4;
unsigned rhs = 0;
if (cc == below || cc == below_equal) {
// NaN results in ZF=1 PF=1 CF=1
//
// ja/jae check for ZF,CF=0 and CF=0. If we make all relational compares
// look like ja/jae, we'll guarantee all NaN comparisons will fail (which
// would not be true for jb/jbe, unless we checked with jp).
if (cc == below)
cc = above;
else
cc = above_equal;
rhs = 4;
lhs = 0;
}
if (MacroAssemblerX86::Features().sse) {
__ movss(xmm0, Operand(stk, rhs));
__ ucomiss(Operand(stk, lhs), xmm0);
} else {
__ fld32(Operand(stk, rhs));
__ fld32(Operand(stk, lhs));
__ fucomip(st1);
__ fstp(st0);
}
// An equal or not-equal needs special handling for the parity bit.
if (cc == equal || cc == not_equal) {
// If NaN, PF=1, ZF=1, and E/Z tests ZF=1.
//
// If NaN, PF=1, ZF=1 and NE/NZ tests Z=0. But, we want any != with NaNs
// to return true, including NaN != NaN.
//
// To make checks simpler, we set |eax| to the expected value of a NaN
// beforehand. This also clears the top bits of |eax| for setcc.
Label done;
__ movl(eax, (cc == equal) ? 0 : 1);
__ j(parity, &done);
__ set(cc, r8_al);
__ bind(&done);
} else {
__ movl(eax, 0);
__ set(cc, r8_al);
}
__ addl(stk, 8);
}
void
Compiler::emitErrorPaths()
{
+1
View File
@@ -105,6 +105,7 @@ class Compiler
void emitCheckAddress(Register reg);
void emitErrorPath(Label *dest, int code);
void emitErrorPaths();
void emitFloatCmp(ConditionCode cc);
ExternalAddress cipAddr() {
sp_context_t *ctx = rt_->GetBaseContext()->GetCtx();