Change float comparison operators to return false for NaN (bug 6107, r=ds).
1.1 --- a/core/logic/smn_float.cpp Sun Apr 20 00:37:20 2014 -0500
1.2 +++ b/core/logic/smn_float.cpp Tue Apr 22 19:40:45 2014 -0700
1.3 @@ -86,6 +86,44 @@
1.4 return sp_ftoc(val);
1.5 }
1.6
1.7 +static cell_t sm_FloatGt(IPluginContext *pCtx, const cell_t *params)
1.8 +{
1.9 + return !!(sp_ctof(params[1]) > sp_ctof(params[2]));
1.10 +}
1.11 +
1.12 +static cell_t sm_FloatGe(IPluginContext *pCtx, const cell_t *params)
1.13 +{
1.14 + return !!(sp_ctof(params[1]) >= sp_ctof(params[2]));
1.15 +}
1.16 +
1.17 +static cell_t sm_FloatLt(IPluginContext *pCtx, const cell_t *params)
1.18 +{
1.19 + return !!(sp_ctof(params[1]) < sp_ctof(params[2]));
1.20 +}
1.21 +
1.22 +static cell_t sm_FloatLe(IPluginContext *pCtx, const cell_t *params)
1.23 +{
1.24 + return !!(sp_ctof(params[1]) <= sp_ctof(params[2]));
1.25 +}
1.26 +
1.27 +static cell_t sm_FloatEq(IPluginContext *pCtx, const cell_t *params)
1.28 +{
1.29 + return !!(sp_ctof(params[1]) == sp_ctof(params[2]));
1.30 +}
1.31 +
1.32 +static cell_t sm_FloatNe(IPluginContext *pCtx, const cell_t *params)
1.33 +{
1.34 + return !!(sp_ctof(params[1]) != sp_ctof(params[2]));
1.35 +}
1.36 +
1.37 +static cell_t sm_FloatNot(IPluginContext *pCtx, const cell_t *params)
1.38 +{
1.39 + float val = sp_ctof(params[1]);
1.40 + if (isnan(val))
1.41 + return 1;
1.42 + return val ? 0 : 1;
1.43 +}
1.44 +
1.45 static cell_t sm_FloatCompare(IPluginContext *pCtx, const cell_t *params)
1.46 {
1.47 float val1 = sp_ctof(params[1]);
1.48 @@ -361,6 +399,13 @@
1.49 {"RoundToCeil", sm_RoundToCeil},
1.50 {"RoundToFloor", sm_RoundToFloor},
1.51 {"RoundToNearest", sm_RoundToNearest},
1.52 + {"__FLOAT_GT__", sm_FloatGt},
1.53 + {"__FLOAT_GE__", sm_FloatGe},
1.54 + {"__FLOAT_LT__", sm_FloatGt},
1.55 + {"__FLOAT_LE__", sm_FloatLe},
1.56 + {"__FLOAT_EQ__", sm_FloatEq},
1.57 + {"__FLOAT_NE__", sm_FloatNe},
1.58 + {"__FLOAT_NOT__", sm_FloatNot},
1.59 {"FloatCompare", sm_FloatCompare},
1.60 {"SquareRoot", sm_SquareRoot},
1.61 {"Pow", sm_Pow},
2.1 --- a/core/sm_stringutil.cpp Sun Apr 20 00:37:20 2014 -0500
2.2 +++ b/core/sm_stringutil.cpp Tue Apr 22 19:40:45 2014 -0700
2.3 @@ -213,6 +213,11 @@
2.4 int significant_digits = 0; // number of significant digits written
2.5 const int MAX_SIGNIFICANT_DIGITS = 16;
2.6
2.7 + if (isnan(fval)) {
2.8 + AddString(buf_p, maxlen, "NaN", width, prec);
2.9 + return;
2.10 + }
2.11 +
2.12 // default precision
2.13 if (prec < 0)
2.14 {
3.1 --- a/plugins/include/float.inc Sun Apr 20 00:37:20 2014 -0500
3.2 +++ b/plugins/include/float.inc Tue Apr 22 19:40:45 2014 -0700
3.3 @@ -246,10 +246,25 @@
3.4 */
3.5 #pragma rational Float
3.6
3.7 +native bool:__FLOAT_GT__(Float:a, Float:b);
3.8 +native bool:__FLOAT_GE__(Float:a, Float:b);
3.9 +native bool:__FLOAT_LT__(Float:a, Float:b);
3.10 +native bool:__FLOAT_LE__(Float:a, Float:b);
3.11 +native bool:__FLOAT_EQ__(Float:a, Float:b);
3.12 +native bool:__FLOAT_NE__(Float:a, Float:b);
3.13 +native bool:__FLOAT_NOT__(Float:a);
3.14 +
3.15 native Float:operator*(Float:oper1, Float:oper2) = FloatMul;
3.16 native Float:operator/(Float:oper1, Float:oper2) = FloatDiv;
3.17 native Float:operator+(Float:oper1, Float:oper2) = FloatAdd;
3.18 native Float:operator-(Float:oper1, Float:oper2) = FloatSub;
3.19 +native bool:operator!(Float:oper1) = __FLOAT_NOT__;
3.20 +native bool:operator>(Float:oper1, Float:oper2) = __FLOAT_GT__;
3.21 +native bool:operator>=(Float:oper1, Float:oper2) = __FLOAT_GE__;
3.22 +native bool:operator<(Float:oper1, Float:oper2) = __FLOAT_LT__;
3.23 +native bool:operator<=(Float:oper1, Float:oper2) = __FLOAT_LE__;
3.24 +native bool:operator!=(Float:oper1, Float:oper2) = __FLOAT_NE__;
3.25 +native bool:operator==(Float:oper1, Float:oper2) = __FLOAT_EQ__;
3.26
3.27 stock Float:operator++(Float:oper)
3.28 {
3.29 @@ -296,90 +311,54 @@
3.30 return FloatSub(float(oper1), oper2);
3.31 }
3.32
3.33 -stock bool:operator==(Float:oper1, Float:oper2)
3.34 -{
3.35 - return FloatCompare(oper1, oper2) == 0;
3.36 -}
3.37 -
3.38 stock bool:operator==(Float:oper1, oper2)
3.39 {
3.40 - return FloatCompare(oper1, float(oper2)) == 0; /* "==" is commutative */
3.41 -}
3.42 -
3.43 -stock bool:operator!=(Float:oper1, Float:oper2)
3.44 -{
3.45 - return FloatCompare(oper1, oper2) != 0;
3.46 + return __FLOAT_EQ__(oper1, float(oper2));
3.47 }
3.48
3.49 stock bool:operator!=(Float:oper1, oper2)
3.50 {
3.51 - return FloatCompare(oper1, float(oper2)) != 0; /* "==" is commutative */
3.52 -}
3.53 -
3.54 -stock bool:operator>(Float:oper1, Float:oper2)
3.55 -{
3.56 - return FloatCompare(oper1, oper2) > 0;
3.57 + return __FLOAT_NE__(oper1, float(oper2));
3.58 }
3.59
3.60 stock bool:operator>(Float:oper1, oper2)
3.61 {
3.62 - return FloatCompare(oper1, float(oper2)) > 0;
3.63 + return __FLOAT_GT__(oper1, float(oper2));
3.64 }
3.65
3.66 stock bool:operator>(oper1, Float:oper2)
3.67 {
3.68 - return FloatCompare(float(oper1), oper2) > 0;
3.69 -}
3.70 -
3.71 -stock bool:operator>=(Float:oper1, Float:oper2)
3.72 -{
3.73 - return FloatCompare(oper1, oper2) >= 0;
3.74 + return __FLOAT_GT__(float(oper1), oper2);
3.75 }
3.76
3.77 stock bool:operator>=(Float:oper1, oper2)
3.78 {
3.79 - return FloatCompare(oper1, float(oper2)) >= 0;
3.80 + return __FLOAT_GE__(oper1, float(oper2));
3.81 }
3.82
3.83 stock bool:operator>=(oper1, Float:oper2)
3.84 {
3.85 - return FloatCompare(float(oper1), oper2) >= 0;
3.86 -}
3.87 -
3.88 -stock bool:operator<(Float:oper1, Float:oper2)
3.89 -{
3.90 - return FloatCompare(oper1, oper2) < 0;
3.91 + return __FLOAT_GE__(float(oper1), oper2);
3.92 }
3.93
3.94 stock bool:operator<(Float:oper1, oper2)
3.95 {
3.96 - return FloatCompare(oper1, float(oper2)) < 0;
3.97 + return __FLOAT_LT__(oper1, float(oper2));
3.98 }
3.99
3.100 stock bool:operator<(oper1, Float:oper2)
3.101 {
3.102 - return FloatCompare(float(oper1), oper2) < 0;
3.103 -}
3.104 -
3.105 -stock bool:operator<=(Float:oper1, Float:oper2)
3.106 -{
3.107 - return FloatCompare(oper1, oper2) <= 0;
3.108 + return __FLOAT_LT__(float(oper1), oper2);
3.109 }
3.110
3.111 stock bool:operator<=(Float:oper1, oper2)
3.112 {
3.113 - return FloatCompare(oper1, float(oper2)) <= 0;
3.114 + return __FLOAT_LE__(oper1, float(oper2));
3.115 }
3.116
3.117 stock bool:operator<=(oper1, Float:oper2)
3.118 {
3.119 - return FloatCompare(float(oper1), oper2) <= 0;
3.120 -}
3.121 -
3.122 -stock bool:operator!(Float:oper)
3.123 -{
3.124 - return (_:oper & ((-1)/2)) == 0; /* -1 = all bits to 1; /2 = remove most significant bit (sign)
3.125 - works on both 32bit and 64bit systems; no constant required */
3.126 + return __FLOAT_LE__(float(oper1), oper2);
3.127 }
3.128
3.129 /**
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/plugins/testsuite/floats.sp Tue Apr 22 19:40:45 2014 -0700
4.3 @@ -0,0 +1,81 @@
4.4 +public OnPluginStart()
4.5 +{
4.6 + RegServerCmd("test_floats", TestFloats)
4.7 +}
4.8 +
4.9 +check(bool:got, bool:expect, const String:message[]="")
4.10 +{
4.11 + if (got != expect) {
4.12 + ThrowError("Check failed", message)
4.13 + }
4.14 +}
4.15 +
4.16 +public Action:TestFloats(args)
4.17 +{
4.18 + new Float:x = 5.3
4.19 + new Float:y = 10.2
4.20 +
4.21 + check(x < y, true)
4.22 + check(x <= y, true)
4.23 + check(x > y, false)
4.24 + check(x >= y, false)
4.25 + check(x == y, false)
4.26 + check(x != y, true)
4.27 +
4.28 + x = 10.5
4.29 + y = 2.3
4.30 +
4.31 + check(x < y, false)
4.32 + check(x <= y, false)
4.33 + check(x > y, true)
4.34 + check(x >= y, true)
4.35 + check(x == y, false)
4.36 + check(x != y, true)
4.37 +
4.38 + x = 10.5
4.39 + y = x
4.40 +
4.41 + check(x < y, false)
4.42 + check(x <= y, true)
4.43 + check(x > y, false)
4.44 + check(x >= y, true)
4.45 + check(x == y, true)
4.46 + check(x != y, false)
4.47 +
4.48 + x = 0.0
4.49 + y = 0.0
4.50 + new Float:nan = x / y
4.51 +
4.52 + check(x < nan, false)
4.53 + check(x <= nan, false)
4.54 + check(x > nan, false)
4.55 + check(x >= nan, false)
4.56 + check(x == nan, false)
4.57 + check(x != nan, true)
4.58 + check(nan < y, false)
4.59 + check(nan <= y, false)
4.60 + check(nan > y, false)
4.61 + check(nan >= y, false)
4.62 + check(nan == y, false)
4.63 + check(nan != y, true)
4.64 + check(nan == nan, false)
4.65 + check(nan != nan, true)
4.66 +
4.67 + x = 10.5
4.68 + y = 0.0
4.69 + check(!x, false)
4.70 + check(!y, true)
4.71 + check(!nan, true)
4.72 +
4.73 + y = -2.7
4.74 + check(-x == -10.5, true)
4.75 + check(-y == 2.7, true)
4.76 +
4.77 + new String:buffer[32]
4.78 + Format(buffer, sizeof(buffer), "%f", nan)
4.79 + check(StrEqual(buffer, "NaN"), true)
4.80 +
4.81 + PrintToServer("Tests finished.")
4.82 +
4.83 + return Plugin_Stop
4.84 +}
5.1 --- a/public/jit/x86/assembler-x86.h Sun Apr 20 00:37:20 2014 -0500
5.2 +++ b/public/jit/x86/assembler-x86.h Tue Apr 22 19:40:45 2014 -0700
5.3 @@ -178,7 +178,11 @@
5.4 zero = equal,
5.5 not_zero = not_equal,
5.6 less_equal = not_greater,
5.7 - greater_equal = not_less
5.8 + below_equal = not_above,
5.9 + greater_equal = not_less,
5.10 + above_equal = not_below,
5.11 + parity = even_parity,
5.12 + not_parity = odd_parity
5.13 };
5.14
5.15 enum Scale {
5.16 @@ -625,6 +629,9 @@
5.17 void fsubr32(const Operand &src) {
5.18 emit1(0xd8, 5, src);
5.19 }
5.20 + void fldz() {
5.21 + emit2(0xd9, 0xee);
5.22 + }
5.23
5.24 // Compare st0 with stN.
5.25 void fucomip(FpuRegister other) {
5.26 @@ -802,11 +809,16 @@
5.27 assert(Features().sse);
5.28 emit3(0xf3, 0x0f, 0x5e, dest.code, src);
5.29 }
5.30 - void ucomiss(FloatRegister left, Register right) {
5.31 - emit2(0x0f, 0x2e, left.code, right.code);
5.32 + void xorps(FloatRegister dest, FloatRegister src) {
5.33 + assert(Features().sse);
5.34 + emit2(0x0f, 0x57, src.code, dest.code);
5.35 }
5.36 - void ucomiss(FloatRegister left, const Operand &right) {
5.37 - emit2(0x0f, 0x2e, left.code, right);
5.38 +
5.39 + void ucomiss(FloatRegister left, FloatRegister right) {
5.40 + emit2(0x0f, 0x2e, right.code, left.code);
5.41 + }
5.42 + void ucomiss(const Operand &left, FloatRegister right) {
5.43 + emit2(0x0f, 0x2e, right.code, left);
5.44 }
5.45
5.46 // SSE2-only instructions.
6.1 --- a/sourcepawn/jit/BaseRuntime.cpp Sun Apr 20 00:37:20 2014 -0500
6.2 +++ b/sourcepawn/jit/BaseRuntime.cpp Tue Apr 22 19:40:45 2014 -0700
6.3 @@ -75,45 +75,47 @@
6.4 free(m_plugin.name);
6.5 }
6.6
6.7 +struct NativeMapping {
6.8 + const char *name;
6.9 + unsigned opcode;
6.10 +};
6.11 +
6.12 +static const NativeMapping sNativeMap[] = {
6.13 + { "FloatAbs", OP_FABS },
6.14 + { "FloatAdd", OP_FLOATADD },
6.15 + { "FloatSub", OP_FLOATSUB },
6.16 + { "FloatMul", OP_FLOATMUL },
6.17 + { "FloatDiv", OP_FLOATDIV },
6.18 + { "float", OP_FLOAT },
6.19 + { "FloatCompare", OP_FLOATCMP },
6.20 + { "RoundToCeil", OP_RND_TO_CEIL },
6.21 + { "RoundToZero", OP_RND_TO_ZERO },
6.22 + { "RoundToFloor", OP_RND_TO_FLOOR },
6.23 + { "RoundToNearest", OP_RND_TO_NEAREST },
6.24 + { "__FLOAT_GT__", OP_FLOAT_GT },
6.25 + { "__FLOAT_GE__", OP_FLOAT_GE },
6.26 + { "__FLOAT_LT__", OP_FLOAT_LT },
6.27 + { "__FLOAT_LE__", OP_FLOAT_LE },
6.28 + { "__FLOAT_EQ__", OP_FLOAT_EQ },
6.29 + { "__FLOAT_NE__", OP_FLOAT_NE },
6.30 + { "__FLOAT_NOT__", OP_FLOAT_NOT },
6.31 + { NULL, 0 },
6.32 +};
6.33 +
6.34 void
6.35 BaseRuntime::SetupFloatNativeRemapping()
6.36 {
6.37 float_table_ = new floattbl_t[m_plugin.num_natives];
6.38 for (size_t i = 0; i < m_plugin.num_natives; i++) {
6.39 const char *name = m_plugin.natives[i].name;
6.40 - if (!strcmp(name, "FloatAbs")) {
6.41 - float_table_[i].found = true;
6.42 - float_table_[i].index = OP_FABS;
6.43 - } else if (!strcmp(name, "FloatAdd")) {
6.44 - float_table_[i].found = true;
6.45 - float_table_[i].index = OP_FLOATADD;
6.46 - } else if (!strcmp(name, "FloatSub")) {
6.47 - float_table_[i].found = true;
6.48 - float_table_[i].index = OP_FLOATSUB;
6.49 - } else if (!strcmp(name, "FloatMul")) {
6.50 - float_table_[i].found = true;
6.51 - float_table_[i].index = OP_FLOATMUL;
6.52 - } else if (!strcmp(name, "FloatDiv")) {
6.53 - float_table_[i].found = true;
6.54 - float_table_[i].index = OP_FLOATDIV;
6.55 - } else if (!strcmp(name, "float")) {
6.56 - float_table_[i].found = true;
6.57 - float_table_[i].index = OP_FLOAT;
6.58 - } else if (!strcmp(name, "FloatCompare")) {
6.59 - float_table_[i].found = true;
6.60 - float_table_[i].index = OP_FLOATCMP;
6.61 - } else if (!strcmp(name, "RoundToZero")) {
6.62 - float_table_[i].found = true;
6.63 - float_table_[i].index = OP_RND_TO_ZERO;
6.64 - } else if (!strcmp(name, "RoundToCeil")) {
6.65 - float_table_[i].found = true;
6.66 - float_table_[i].index = OP_RND_TO_CEIL;
6.67 - } else if (!strcmp(name, "RoundToFloor")) {
6.68 - float_table_[i].found = true;
6.69 - float_table_[i].index = OP_RND_TO_FLOOR;
6.70 - } else if (!strcmp(name, "RoundToNearest")) {
6.71 - float_table_[i].found = true;
6.72 - float_table_[i].index = OP_RND_TO_NEAREST;
6.73 + const NativeMapping *iter = sNativeMap;
6.74 + while (iter->name) {
6.75 + if (strcmp(name, iter->name) == 0) {
6.76 + float_table_[i].found = true;
6.77 + float_table_[i].index = iter->opcode;
6.78 + break;
6.79 + }
6.80 + iter++;
6.81 }
6.82 }
6.83 }
7.1 --- a/sourcepawn/jit/opcodes.h Sun Apr 20 00:37:20 2014 -0500
7.2 +++ b/sourcepawn/jit/opcodes.h Tue Apr 22 19:40:45 2014 -0700
7.3 @@ -238,15 +238,22 @@
7.4 _(ENDPROC, "endproc") \
7.5 _(FABS, "fabs") \
7.6 _(FLOAT, "float") \
7.7 - _(FLOATADD, "floatadd") \
7.8 - _(FLOATSUB, "floatsub") \
7.9 - _(FLOATMUL, "floatmul") \
7.10 - _(FLOATDIV, "floatdiv") \
7.11 + _(FLOATADD, "float.add") \
7.12 + _(FLOATSUB, "float.sub") \
7.13 + _(FLOATMUL, "float.mul") \
7.14 + _(FLOATDIV, "float.div") \
7.15 _(RND_TO_NEAREST, "round") \
7.16 _(RND_TO_FLOOR, "floor") \
7.17 _(RND_TO_CEIL, "ceil") \
7.18 _(RND_TO_ZERO, "rndtozero") \
7.19 - _(FLOATCMP, "floatcmp")
7.20 + _(FLOATCMP, "float.cmp") \
7.21 + _(FLOAT_GT, "float.gt") \
7.22 + _(FLOAT_GE, "float.ge") \
7.23 + _(FLOAT_LT, "float.lt") \
7.24 + _(FLOAT_LE, "float.le") \
7.25 + _(FLOAT_NE, "float.ne") \
7.26 + _(FLOAT_EQ, "float.eq") \
7.27 + _(FLOAT_NOT, "float.not")
7.28
7.29 enum OPCODE {
7.30 #define _(op, text) OP_##op,
8.1 --- a/sourcepawn/jit/x86/jit_x86.cpp Sun Apr 20 00:37:20 2014 -0500
8.2 +++ b/sourcepawn/jit/x86/jit_x86.cpp Tue Apr 22 19:40:45 2014 -0700
8.3 @@ -1085,12 +1085,16 @@
8.4 __ addl(stk, 4);
8.5 break;
8.6
8.7 + // This is the old float cmp, which returns ordered results. In newly
8.8 + // compiled code it should not be used or generated.
8.9 + //
8.10 + // Note that the checks here are inverted: the test is |rhs OP lhs|.
8.11 case OP_FLOATCMP:
8.12 {
8.13 Label bl, ab, done;
8.14 if (MacroAssemblerX86::Features().sse) {
8.15 __ movss(xmm0, Operand(stk, 4));
8.16 - __ ucomiss(xmm0, Operand(stk, 0));
8.17 + __ ucomiss(Operand(stk, 0), xmm0);
8.18 } else {
8.19 __ fld32(Operand(stk, 0));
8.20 __ fld32(Operand(stk, 4));
8.21 @@ -1111,6 +1115,53 @@
8.22 break;
8.23 }
8.24
8.25 + case OP_FLOAT_GT:
8.26 + emitFloatCmp(above);
8.27 + break;
8.28 +
8.29 + case OP_FLOAT_GE:
8.30 + emitFloatCmp(above_equal);
8.31 + break;
8.32 +
8.33 + case OP_FLOAT_LE:
8.34 + emitFloatCmp(below_equal);
8.35 + break;
8.36 +
8.37 + case OP_FLOAT_LT:
8.38 + emitFloatCmp(below);
8.39 + break;
8.40 +
8.41 + case OP_FLOAT_EQ:
8.42 + emitFloatCmp(equal);
8.43 + break;
8.44 +
8.45 + case OP_FLOAT_NE:
8.46 + emitFloatCmp(not_equal);
8.47 + break;
8.48 +
8.49 + case OP_FLOAT_NOT:
8.50 + {
8.51 + if (MacroAssemblerX86::Features().sse) {
8.52 + __ xorps(xmm0, xmm0);
8.53 + __ ucomiss(Operand(stk, 0), xmm0);
8.54 + } else {
8.55 + __ fld32(Operand(stk, 0));
8.56 + __ fldz();
8.57 + __ fucomip(st1);
8.58 + __ fstp(st0);
8.59 + }
8.60 +
8.61 + // See emitFloatCmp() - this is a shorter version.
8.62 + Label done;
8.63 + __ movl(eax, 1);
8.64 + __ j(parity, &done);
8.65 + __ set(zero, r8_al);
8.66 + __ bind(&done);
8.67 +
8.68 + __ addl(stk, 4);
8.69 + break;
8.70 + }
8.71 +
8.72 case OP_STACK:
8.73 {
8.74 cell_t amount = readCell();
8.75 @@ -1672,6 +1723,56 @@
8.76 }
8.77
8.78 void
8.79 +Compiler::emitFloatCmp(ConditionCode cc)
8.80 +{
8.81 + unsigned lhs = 4;
8.82 + unsigned rhs = 0;
8.83 + if (cc == below || cc == below_equal) {
8.84 + // NaN results in ZF=1 PF=1 CF=1
8.85 + //
8.86 + // ja/jae check for ZF,CF=0 and CF=0. If we make all relational compares
8.87 + // look like ja/jae, we'll guarantee all NaN comparisons will fail (which
8.88 + // would not be true for jb/jbe, unless we checked with jp).
8.89 + if (cc == below)
8.90 + cc = above;
8.91 + else
8.92 + cc = above_equal;
8.93 + rhs = 4;
8.94 + lhs = 0;
8.95 + }
8.96 +
8.97 + if (MacroAssemblerX86::Features().sse) {
8.98 + __ movss(xmm0, Operand(stk, rhs));
8.99 + __ ucomiss(Operand(stk, lhs), xmm0);
8.100 + } else {
8.101 + __ fld32(Operand(stk, rhs));
8.102 + __ fld32(Operand(stk, lhs));
8.103 + __ fucomip(st1);
8.104 + __ fstp(st0);
8.105 + }
8.106 +
8.107 + // An equal or not-equal needs special handling for the parity bit.
8.108 + if (cc == equal || cc == not_equal) {
8.109 + // If NaN, PF=1, ZF=1, and E/Z tests ZF=1.
8.110 + //
8.111 + // If NaN, PF=1, ZF=1 and NE/NZ tests Z=0. But, we want any != with NaNs
8.112 + // to return true, including NaN != NaN.
8.113 + //
8.114 + // To make checks simpler, we set |eax| to the expected value of a NaN
8.115 + // beforehand. This also clears the top bits of |eax| for setcc.
8.116 + Label done;
8.117 + __ movl(eax, (cc == equal) ? 0 : 1);
8.118 + __ j(parity, &done);
8.119 + __ set(cc, r8_al);
8.120 + __ bind(&done);
8.121 + } else {
8.122 + __ movl(eax, 0);
8.123 + __ set(cc, r8_al);
8.124 + }
8.125 + __ addl(stk, 8);
8.126 +}
8.127 +
8.128 +void
8.129 Compiler::emitErrorPaths()
8.130 {
8.131 emitErrorPath(&error_divide_by_zero_, SP_ERROR_DIVIDE_BY_ZERO);
9.1 --- a/sourcepawn/jit/x86/jit_x86.h Sun Apr 20 00:37:20 2014 -0500
9.2 +++ b/sourcepawn/jit/x86/jit_x86.h Tue Apr 22 19:40:45 2014 -0700
9.3 @@ -105,6 +105,7 @@
9.4 void emitCheckAddress(Register reg);
9.5 void emitErrorPath(Label *dest, int code);
9.6 void emitErrorPaths();
9.7 + void emitFloatCmp(ConditionCode cc);
9.8
9.9 ExternalAddress cipAddr() {
9.10 sp_context_t *ctx = rt_->GetBaseContext()->GetCtx();
10.1 --- a/tools/checkout-deps.sh Sun Apr 20 00:37:20 2014 -0500
10.2 +++ b/tools/checkout-deps.sh Tue Apr 22 19:40:45 2014 -0700
10.3 @@ -57,6 +57,7 @@
10.4 hg clone https://hg.alliedmods.net/$path/$name
10.5 else
10.6 cd $name
10.7 + sed -i 's/http:/https:/g' .hg/hgrc
10.8 hg pull -u
10.9 cd ..
10.10 fi