Bug 1293313 - IonMonkey: Handle non-canonical NaNs in constant folding. r=luke

This commit is contained in:
Dan Gohman 2016-08-09 15:35:55 -07:00
Родитель cbb507cc3e
Коммит 95d7524476
4 изменённых файлов: 51 добавлений и 30 удалений

Просмотреть файл

@ -681,7 +681,7 @@ BUILD_JSVAL(JSValueTag tag, uint64_t payload)
static inline bool
JSVAL_IS_DOUBLE_IMPL(jsval_layout l)
{
return l.asBits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE;
return (l.asBits | mozilla::DoubleTypeTraits::kSignBit) <= JSVAL_SHIFTED_TAG_MAX_DOUBLE;
}
static inline jsval_layout
@ -689,7 +689,7 @@ DOUBLE_TO_JSVAL_IMPL(double d)
{
jsval_layout l;
l.asDouble = d;
MOZ_ASSERT(l.asBits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE);
MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
return l;
}
@ -941,9 +941,8 @@ MAGIC_UINT32_TO_JSVAL_IMPL(uint32_t payload)
static inline bool
JSVAL_SAME_TYPE_IMPL(jsval_layout lhs, jsval_layout rhs)
{
uint64_t lbits = lhs.asBits, rbits = rhs.asBits;
return (lbits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE && rbits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE) ||
(((lbits ^ rbits) & 0xFFFF800000000000LL) == 0);
return (JSVAL_IS_DOUBLE_IMPL(lhs) && JSVAL_IS_DOUBLE_IMPL(rhs)) ||
(((lhs.asBits ^ rhs.asBits) & 0xFFFF800000000000LL) == 0);
}
static inline JSValueType

Просмотреть файл

@ -95,3 +95,11 @@ assertErrorMessage(() => wasmEvalText('(module (func (param f32) (param f32) (re
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param f64) (result f64) (f64.eq (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f64"));
assertErrorMessage(() => wasmEvalText('(module (func (param f64) (param i32) (result f64) (f64.eq (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f64"));
assertErrorMessage(() => wasmEvalText('(module (func (param f64) (param f64) (result f64) (f64.eq (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f64"));
// Non-canonical NaNs.
assertEq(wasmEvalText('(module (func (result i32) (i32.reinterpret/f32 (f32.mul (f32.const 0.0) (f32.const -nan:0x222222)))) (export "" 0))')(), -0x1dddde);
assertEq(wasmEvalText('(module (func (result i32) (i32.reinterpret/f32 (f32.min (f32.const 0.0) (f32.const -nan:0x222222)))) (export "" 0))')(), -0x1dddde);
assertEq(wasmEvalText('(module (func (result i32) (i32.reinterpret/f32 (f32.max (f32.const 0.0) (f32.const -nan:0x222222)))) (export "" 0))')(), -0x1dddde);
assertEq(wasmEvalText('(module (func (result i32) (local i64) (set_local 0 (i64.reinterpret/f64 (f64.mul (f64.const 0.0) (f64.const -nan:0x4444444444444)))) (i32.xor (i32.wrap/i64 (get_local 0)) (i32.wrap/i64 (i64.shr_u (get_local 0) (i64.const 32))))) (export "" 0))')(), -0x44480000);
assertEq(wasmEvalText('(module (func (result i32) (local i64) (set_local 0 (i64.reinterpret/f64 (f64.min (f64.const 0.0) (f64.const -nan:0x4444444444444)))) (i32.xor (i32.wrap/i64 (get_local 0)) (i32.wrap/i64 (i64.shr_u (get_local 0) (i64.const 32))))) (export "" 0))')(), -0x44480000);
assertEq(wasmEvalText('(module (func (result i32) (local i64) (set_local 0 (i64.reinterpret/f64 (f64.max (f64.const 0.0) (f64.const -nan:0x4444444444444)))) (i32.xor (i32.wrap/i64 (get_local 0)) (i32.wrap/i64 (i64.shr_u (get_local 0) (i64.const 32))))) (export "" 0))')(), -0x44480000);

Просмотреть файл

@ -283,3 +283,7 @@ testConversion('f64', 'convert_u', 'i32', 40, 40);
testConversion('f32', 'demote', 'f64', 40.1, 40.099998474121094);
testConversion('f64', 'promote', 'f32', 40.1, 40.099998474121094);
// Non-canonical NaNs.
assertEq(wasmEvalText('(module (func (result i32) (i32.reinterpret/f32 (f32.demote/f64 (f64.const -nan:0x4444444444444)))) (export "" 0))')(), -0x1dddde);
assertEq(wasmEvalText('(module (func (result i32) (local i64) (set_local 0 (i64.reinterpret/f64 (f64.promote/f32 (f32.const -nan:0x222222)))) (i32.xor (i32.wrap/i64 (get_local 0)) (i32.wrap/i64 (i64.shr_u (get_local 0) (i64.const 32))))) (export "" 0))')(), -0x4003bbbc);

Просмотреть файл

@ -93,47 +93,47 @@ EvaluateConstantOperands(TempAllocator& alloc, MBinaryInstruction* ins, bool* pt
MConstant* lhs = left->toConstant();
MConstant* rhs = right->toConstant();
Value ret = UndefinedValue();
double ret = JS::GenericNaN();
switch (ins->op()) {
case MDefinition::Op_BitAnd:
ret = Int32Value(lhs->toInt32() & rhs->toInt32());
ret = double(lhs->toInt32() & rhs->toInt32());
break;
case MDefinition::Op_BitOr:
ret = Int32Value(lhs->toInt32() | rhs->toInt32());
ret = double(lhs->toInt32() | rhs->toInt32());
break;
case MDefinition::Op_BitXor:
ret = Int32Value(lhs->toInt32() ^ rhs->toInt32());
ret = double(lhs->toInt32() ^ rhs->toInt32());
break;
case MDefinition::Op_Lsh:
ret = Int32Value(uint32_t(lhs->toInt32()) << (rhs->toInt32() & 0x1F));
ret = double(uint32_t(lhs->toInt32()) << (rhs->toInt32() & 0x1F));
break;
case MDefinition::Op_Rsh:
ret = Int32Value(lhs->toInt32() >> (rhs->toInt32() & 0x1F));
ret = double(lhs->toInt32() >> (rhs->toInt32() & 0x1F));
break;
case MDefinition::Op_Ursh:
ret.setNumber(uint32_t(lhs->toInt32()) >> (rhs->toInt32() & 0x1F));
ret = double(uint32_t(lhs->toInt32()) >> (rhs->toInt32() & 0x1F));
break;
case MDefinition::Op_Add:
ret.setNumber(lhs->numberToDouble() + rhs->numberToDouble());
ret = lhs->numberToDouble() + rhs->numberToDouble();
break;
case MDefinition::Op_Sub:
ret.setNumber(lhs->numberToDouble() - rhs->numberToDouble());
ret = lhs->numberToDouble() - rhs->numberToDouble();
break;
case MDefinition::Op_Mul:
ret.setNumber(lhs->numberToDouble() * rhs->numberToDouble());
ret = lhs->numberToDouble() * rhs->numberToDouble();
break;
case MDefinition::Op_Div:
if (ins->toDiv()->isUnsigned()) {
if (rhs->isInt32(0)) {
if (ins->toDiv()->trapOnError())
return nullptr;
ret.setInt32(0);
ret = 0.0;
} else {
ret.setInt32(uint32_t(lhs->toInt32()) / uint32_t(rhs->toInt32()));
ret = double(uint32_t(lhs->toInt32()) / uint32_t(rhs->toInt32()));
}
} else {
ret.setNumber(NumberDiv(lhs->numberToDouble(), rhs->numberToDouble()));
ret = NumberDiv(lhs->numberToDouble(), rhs->numberToDouble());
}
break;
case MDefinition::Op_Mod:
@ -141,30 +141,40 @@ EvaluateConstantOperands(TempAllocator& alloc, MBinaryInstruction* ins, bool* pt
if (rhs->isInt32(0)) {
if (ins->toMod()->trapOnError())
return nullptr;
ret.setInt32(0);
ret = 0.0;
} else {
ret.setInt32(uint32_t(lhs->toInt32()) % uint32_t(rhs->toInt32()));
ret = double(uint32_t(lhs->toInt32()) % uint32_t(rhs->toInt32()));
}
} else {
ret.setNumber(NumberMod(lhs->numberToDouble(), rhs->numberToDouble()));
ret = NumberMod(lhs->numberToDouble(), rhs->numberToDouble());
}
break;
default:
MOZ_CRASH("NYI");
}
// setNumber eagerly transforms a number to int32.
// Transform back to double, if the output type is double.
if (ins->type() == MIRType::Double && ret.isInt32())
ret.setDouble(ret.toNumber());
// For a float32 or double value, use NewRawDouble so that we preserve NaN
// bits. This isn't strictly required for either ES or wasm, but it does
// avoid making constant-folding observable.
if (ins->type() == MIRType::Double)
return MConstant::NewRawDouble(alloc, ret);
if (ins->type() == MIRType::Float32)
return MConstant::NewRawFloat32(alloc, float(ret));
if (ins->type() != MIRTypeFromValue(ret)) {
Value retVal;
retVal.setNumber(JS::CanonicalizeNaN(ret));
// If this was an int32 operation but the result isn't an int32 (for
// example, a division where the numerator isn't evenly divisible by the
// denominator), decline folding.
MOZ_ASSERT(ins->type() == MIRType::Int32);
if (!retVal.isInt32()) {
if (ptypeChange)
*ptypeChange = true;
return nullptr;
}
return MConstant::New(alloc, ret);
return MConstant::New(alloc, retVal);
}
static MMul*
@ -3150,7 +3160,7 @@ MMinMax::foldsTo(TempAllocator& alloc)
return MConstant::NewFloat32(alloc, result);
} else {
MOZ_ASSERT(type() == MIRType::Double);
return MConstant::New(alloc, DoubleValue(result));
return MConstant::NewRawDouble(alloc, result);
}
}
@ -4134,7 +4144,7 @@ MToDouble::foldsTo(TempAllocator& alloc)
if (input->isConstant() && input->toConstant()->isTypeRepresentableAsDouble()) {
double out = input->toConstant()->numberToDouble();
return MConstant::New(alloc, DoubleValue(out));
return MConstant::NewRawDouble(alloc, out);
}
return this;
@ -4159,7 +4169,7 @@ MToFloat32::foldsTo(TempAllocator& alloc)
}
if (input->isConstant() && input->toConstant()->isTypeRepresentableAsDouble())
return MConstant::NewFloat32(alloc, float(input->toConstant()->numberToDouble()));
return MConstant::NewRawFloat32(alloc, float(input->toConstant()->numberToDouble()));
return this;
}