Bug 1054512: IonMonkey: Run the type policy of added instructions during type analysis, r=jandem
authorHannes Verschore <hv1989@gmail.com>
Mon, 18 Aug 2014 21:37:10 +0200
changeset 200196 43494708df76859ec649ea2aa0c53c175776eab3
parent 200195 a14312d5203e07b2f6fafe1d08ba9a17c296670c
child 200197 dd4680de882d04805521d5039676f45018b831b1
push id27337
push useremorley@mozilla.com
push dateTue, 19 Aug 2014 12:40:34 +0000
treeherdermozilla-central@a38daccaa557 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1054512
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1054512: IonMonkey: Run the type policy of added instructions during type analysis, r=jandem
js/src/jit-test/tests/ion/bug1054512.js
js/src/jit/MIR.h
js/src/jit/TypePolicy.cpp
js/src/jit/arm/Lowering-arm.cpp
js/src/jit/mips/Lowering-mips.cpp
js/src/jit/x64/Lowering-x64.cpp
js/src/jit/x86/Lowering-x86.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1054512.js
@@ -0,0 +1,11 @@
+function f(x) {
+    x((x | 0) + x);
+};
+try {
+    f(1);
+} catch (e) {}
+for (var k = 0; k < 1; ++k) {
+    try {
+        f(Symbol());
+    } catch (e) {}
+}
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -3107,17 +3107,21 @@ class MUnbox : public MUnaryInstruction,
   private:
     Mode mode_;
     BailoutKind bailoutKind_;
 
     MUnbox(MDefinition *ins, MIRType type, Mode mode, BailoutKind kind)
       : MUnaryInstruction(ins),
         mode_(mode)
     {
-        JS_ASSERT(ins->type() == MIRType_Value);
+        // Only allow unboxing a non MIRType_Value when input and output types
+        // don't match. This is often used to force a bailout. Boxing happens
+        // during type analysis.
+        JS_ASSERT_IF(ins->type() != MIRType_Value, type != ins->type());
+
         JS_ASSERT(type == MIRType_Boolean ||
                   type == MIRType_Int32   ||
                   type == MIRType_Double  ||
                   type == MIRType_String  ||
                   type == MIRType_Symbol  ||
                   type == MIRType_Object);
 
         setResultType(type);
@@ -3834,28 +3838,29 @@ class MToInt32
     bool isConsistentFloat32Use(MUse *use) const { return true; }
 #endif
 
     ALLOW_CLONE(MToInt32)
 };
 
 // Converts a value or typed input to a truncated int32, for use with bitwise
 // operations. This is an infallible ValueToECMAInt32.
-class MTruncateToInt32 : public MUnaryInstruction
+class MTruncateToInt32
+  : public MUnaryInstruction,
+    public ToInt32Policy
 {
     explicit MTruncateToInt32(MDefinition *def)
       : MUnaryInstruction(def)
     {
         setResultType(MIRType_Int32);
         setMovable();
 
         // An object might have "valueOf", which means it is effectful.
         // ToInt32(symbol) throws.
         MOZ_ASSERT(def->type() != MIRType_Object);
-        MOZ_ASSERT(def->type() != MIRType_Symbol);
         if (def->mightBeType(MIRType_Object) || def->mightBeType(MIRType_Symbol))
             setGuard();
     }
 
   public:
     INSTRUCTION_HEADER(TruncateToInt32)
     static MTruncateToInt32 *New(TempAllocator &alloc, MDefinition *def) {
         return new(alloc) MTruncateToInt32(def);
@@ -3876,16 +3881,20 @@ class MTruncateToInt32 : public MUnaryIn
     void computeRange(TempAllocator &alloc);
     TruncateKind operandTruncateKind(size_t index) const;
 # ifdef DEBUG
     bool isConsistentFloat32Use(MUse *use) const {
         return true;
     }
 #endif
 
+    TypePolicy *typePolicy() {
+        return this;
+    }
+
     ALLOW_CLONE(MTruncateToInt32)
 };
 
 // Converts any type to a string
 class MToString :
   public MUnaryInstruction,
   public ToStringPolicy
 {
@@ -6095,16 +6104,17 @@ class MStringReplace
 
     static MStringReplace *New(TempAllocator &alloc, MDefinition *string, MDefinition *pattern, MDefinition *replacement) {
         return new(alloc) MStringReplace(string, pattern, replacement);
     }
 
     bool congruentTo(const MDefinition *ins) const {
         return congruentIfOperandsEqual(ins);
     }
+
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 struct LambdaFunctionInfo
 {
     // The functions used in lambdas are the canonical original function in
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -71,34 +71,28 @@ ArithPolicy::adjustInputs(TempAllocator 
 
     for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
         MDefinition *in = ins->getOperand(i);
         if (in->type() == ins->type())
             continue;
 
         MInstruction *replace;
 
-        // If the input is a string or object, the conversion is not
-        // possible--at least, we can't specialize. So box the input.
-        if (in->type() == MIRType_Object ||
-            in->type() == MIRType_String ||
-            (in->type() == MIRType_Undefined && specialization_ == MIRType_Int32))
-        {
-            in = boxAt(alloc, ins, in);
-        }
-
         if (ins->type() == MIRType_Double)
             replace = MToDouble::New(alloc, in);
         else if (ins->type() == MIRType_Float32)
             replace = MToFloat32::New(alloc, in);
         else
             replace = MToInt32::New(alloc, in);
 
         ins->block()->insertBefore(ins, replace);
         ins->replaceOperand(i, replace);
+
+        if (!replace->typePolicy()->adjustInputs(alloc, replace))
+            return false;
     }
 
     return true;
 }
 
 bool
 ComparePolicy::adjustInputs(TempAllocator &alloc, MInstruction *def)
 {
@@ -189,23 +183,16 @@ ComparePolicy::adjustInputs(TempAllocato
               type == MIRType_Object || type == MIRType_String || type == MIRType_Float32);
     for (size_t i = 0; i < 2; i++) {
         MDefinition *in = def->getOperand(i);
         if (in->type() == type)
             continue;
 
         MInstruction *replace;
 
-        // See ArithPolicy::adjustInputs for an explanation of the following.
-        if (in->type() == MIRType_Object || in->type() == MIRType_String ||
-            in->type() == MIRType_Undefined)
-        {
-            in = boxAt(alloc, def, in);
-        }
-
         switch (type) {
           case MIRType_Double: {
             MToDouble::ConversionKind convert = MToDouble::NumbersOnly;
             if (compare->compareType() == MCompare::Compare_DoubleMaybeCoerceLHS && i == 0)
                 convert = MToDouble::NonNullNonStringPrimitives;
             else if (compare->compareType() == MCompare::Compare_DoubleMaybeCoerceRHS && i == 1)
                 convert = MToDouble::NonNullNonStringPrimitives;
             if (in->type() == MIRType_Null ||
@@ -260,16 +247,19 @@ ComparePolicy::adjustInputs(TempAllocato
             replace = MUnbox::New(alloc, in, MIRType_String, MUnbox::Infallible);
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("Unknown compare specialization");
         }
 
         def->block()->insertBefore(def, replace);
         def->replaceOperand(i, replace);
+
+        if (!replace->typePolicy()->adjustInputs(alloc, replace))
+            return false;
     }
 
     return true;
 }
 
 bool
 TypeBarrierPolicy::adjustInputs(TempAllocator &alloc, MInstruction *def)
 {
@@ -357,27 +347,22 @@ BitwisePolicy::adjustInputs(TempAllocato
     JS_ASSERT(specialization_ == MIRType_Int32 || specialization_ == MIRType_Double);
 
     // This policy works for both unary and binary bitwise operations.
     for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
         MDefinition *in = ins->getOperand(i);
         if (in->type() == MIRType_Int32)
             continue;
 
-        // See ArithPolicy::adjustInputs for an explanation of the following.
-        // MTruncateToInt32 in particular does not support MIRType_Symbol input.
-        if (in->type() == MIRType_Object || in->type() == MIRType_String ||
-            in->type() == MIRType_Symbol)
-        {
-            in = boxAt(alloc, ins, in);
-        }
-
         MInstruction *replace = MTruncateToInt32::New(alloc, in);
         ins->block()->insertBefore(ins, replace);
         ins->replaceOperand(i, replace);
+
+        if (!replace->typePolicy()->adjustInputs(alloc, replace))
+            return false;
     }
 
     return true;
 }
 
 bool
 PowPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
@@ -462,87 +447,53 @@ ConvertToInt32Policy<Op>::staticAdjustIn
 {
     MDefinition *in = def->getOperand(Op);
     if (in->type() == MIRType_Int32)
         return true;
 
     MToInt32 *replace = MToInt32::New(alloc, in);
     def->block()->insertBefore(def, replace);
     def->replaceOperand(Op, replace);
-    return true;
+
+    return replace->typePolicy()->adjustInputs(alloc, replace);
 }
 
 template bool ConvertToInt32Policy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 
 template <unsigned Op>
 bool
 DoublePolicy<Op>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def)
 {
     MDefinition *in = def->getOperand(Op);
     if (in->type() == MIRType_Double)
         return true;
 
-    // Force a bailout. Objects may be effectful; strings and symbols are
-    // currently unhandled.
-    if (in->type() == MIRType_Object ||
-        in->type() == MIRType_String ||
-        in->type() == MIRType_Symbol)
-    {
-        MBox *box = MBox::New(alloc, in);
-        def->block()->insertBefore(def, box);
-
-        MUnbox *unbox = MUnbox::New(alloc, box, MIRType_Double, MUnbox::Fallible);
-        def->block()->insertBefore(def, unbox);
-        def->replaceOperand(Op, unbox);
-        return true;
-    }
-
     MToDouble *replace = MToDouble::New(alloc, in);
     def->block()->insertBefore(def, replace);
     def->replaceOperand(Op, replace);
-    return true;
+
+    return replace->typePolicy()->adjustInputs(alloc, replace);
 }
 
 template bool DoublePolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 template bool DoublePolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 
 template <unsigned Op>
 bool
 Float32Policy<Op>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def)
 {
     MDefinition *in = def->getOperand(Op);
     if (in->type() == MIRType_Float32)
         return true;
 
-    // Force a bailout. Objects may be effectful; strings and symbols are
-    // currently unhandled.
-    if (in->type() == MIRType_Object ||
-        in->type() == MIRType_String ||
-        in->type() == MIRType_Symbol)
-    {
-        MToDouble *toDouble = MToDouble::New(alloc, in);
-        def->block()->insertBefore(def, toDouble);
-
-        MBox *box = MBox::New(alloc, toDouble);
-        def->block()->insertBefore(def, box);
-
-        MUnbox *unbox = MUnbox::New(alloc, box, MIRType_Double, MUnbox::Fallible);
-        def->block()->insertBefore(def, unbox);
-
-        MToFloat32 *toFloat32 = MToFloat32::New(alloc, unbox);
-        def->block()->insertBefore(def, toFloat32);
-
-        def->replaceOperand(Op, unbox);
-        return true;
-    }
-
     MToFloat32 *replace = MToFloat32::New(alloc, in);
     def->block()->insertBefore(def, replace);
     def->replaceOperand(Op, replace);
-    return true;
+
+    return replace->typePolicy()->adjustInputs(alloc, replace);
 }
 
 template bool Float32Policy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 template bool Float32Policy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 template bool Float32Policy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 
 template <unsigned Op>
 bool
@@ -604,28 +555,34 @@ ToDoublePolicy::staticAdjustInputs(TempA
     in = boxAt(alloc, ins, in);
     ins->replaceOperand(0, in);
     return true;
 }
 
 bool
 ToInt32Policy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
-    JS_ASSERT(ins->isToInt32());
+    JS_ASSERT(ins->isToInt32() || ins->isTruncateToInt32());
 
     MDefinition *in = ins->getOperand(0);
     switch (in->type()) {
       case MIRType_Object:
       case MIRType_String:
       case MIRType_Symbol:
-      case MIRType_Undefined:
-        // Objects might be effectful. Undefined and symbols coerce to NaN, not int32.
+        // Objects might be effectful. Symbols give TypeError.
         in = boxAt(alloc, ins, in);
         ins->replaceOperand(0, in);
         break;
+      case MIRType_Undefined:
+        // Undefined coerce to NaN, not int32, when not truncated.
+        if (ins->isToInt32()) {
+            in = boxAt(alloc, ins, in);
+            ins->replaceOperand(0, in);
+        }
+        break;
       default:
         break;
     }
 
     return true;
 }
 
 bool
@@ -651,45 +608,41 @@ ObjectPolicy<Op>::staticAdjustInputs(Tem
 {
     MDefinition *in = ins->getOperand(Op);
     if (in->type() == MIRType_Object || in->type() == MIRType_Slots ||
         in->type() == MIRType_Elements)
     {
         return true;
     }
 
-    if (in->type() != MIRType_Value)
-        in = boxAt(alloc, ins, in);
-
     MUnbox *replace = MUnbox::New(alloc, in, MIRType_Object, MUnbox::Fallible);
     ins->block()->insertBefore(ins, replace);
     ins->replaceOperand(Op, replace);
-    return true;
+
+    return replace->typePolicy()->adjustInputs(alloc, replace);
 }
 
 template bool ObjectPolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
 template bool ObjectPolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
 template bool ObjectPolicy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
 template bool ObjectPolicy<3>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
 
 bool
 CallPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
     MCall *call = ins->toCall();
 
     MDefinition *func = call->getFunction();
     if (func->type() != MIRType_Object) {
-        // If the function is impossible to call,
-        // bail out by causing a subsequent unbox to fail.
-        if (func->type() != MIRType_Value)
-            func = boxAt(alloc, call, func);
-
         MInstruction *unbox = MUnbox::New(alloc, func, MIRType_Object, MUnbox::Fallible);
         call->block()->insertBefore(call, unbox);
         call->replaceFunction(unbox);
+
+        if (!unbox->typePolicy()->adjustInputs(alloc, unbox))
+            return false;
     }
 
     for (uint32_t i = 0; i < call->numStackArgs(); i++)
         EnsureOperandNotFloat32(alloc, call, MCall::IndexOfStackArg(i));
 
     return true;
 }
 
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -121,16 +121,17 @@ LIRGeneratorARM::visitBox(MBox *box)
 
 bool
 LIRGeneratorARM::visitUnbox(MUnbox *unbox)
 {
     // An unbox on arm reads in a type tag (either in memory or a register) and
     // a payload. Unlike most instructions conusming a box, we ask for the type
     // second, so that the result can re-use the first input.
     MDefinition *inner = unbox->getOperand(0);
+    JS_ASSERT(inner->type() == MIRType_Value);
 
     if (!ensureDefined(inner))
         return false;
 
     if (IsFloatingPointType(unbox->type())) {
         LUnboxFloatingPoint *lir = new(alloc()) LUnboxFloatingPoint(unbox->type());
         if (unbox->fallible() && !assignSnapshot(lir, unbox->bailoutKind()))
             return false;
--- a/js/src/jit/mips/Lowering-mips.cpp
+++ b/js/src/jit/mips/Lowering-mips.cpp
@@ -123,16 +123,17 @@ LIRGeneratorMIPS::visitBox(MBox *box)
 
 bool
 LIRGeneratorMIPS::visitUnbox(MUnbox *unbox)
 {
     // An unbox on mips reads in a type tag (either in memory or a register) and
     // a payload. Unlike most instructions consuming a box, we ask for the type
     // second, so that the result can re-use the first input.
     MDefinition *inner = unbox->getOperand(0);
+    JS_ASSERT(inner->type() == MIRType_Value);
 
     if (!ensureDefined(inner))
         return false;
 
     if (IsFloatingPointType(unbox->type())) {
         LUnboxFloatingPoint *lir = new(alloc()) LUnboxFloatingPoint(unbox->type());
         if (unbox->fallible() && !assignSnapshot(lir, unbox->bailoutKind()))
             return false;
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -70,16 +70,18 @@ LIRGeneratorX64::visitBox(MBox *box)
     LBox *ins = new(alloc()) LBox(opd->type(), useRegister(opd));
     return define(ins, box, LDefinition(LDefinition::BOX));
 }
 
 bool
 LIRGeneratorX64::visitUnbox(MUnbox *unbox)
 {
     MDefinition *box = unbox->getOperand(0);
+    JS_ASSERT(box->type() == MIRType_Value);
+
     LUnboxBase *lir;
     if (IsFloatingPointType(unbox->type()))
         lir = new(alloc()) LUnboxFloatingPoint(useRegisterAtStart(box), unbox->type());
     else
         lir = new(alloc()) LUnbox(useRegisterAtStart(box));
 
     if (unbox->fallible() && !assignSnapshot(lir, unbox->bailoutKind()))
         return false;
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -114,16 +114,17 @@ LIRGeneratorX86::visitBox(MBox *box)
 
 bool
 LIRGeneratorX86::visitUnbox(MUnbox *unbox)
 {
     // An unbox on x86 reads in a type tag (either in memory or a register) and
     // a payload. Unlike most instructions conusming a box, we ask for the type
     // second, so that the result can re-use the first input.
     MDefinition *inner = unbox->getOperand(0);
+    JS_ASSERT(inner->type() == MIRType_Value);
 
     if (!ensureDefined(inner))
         return false;
 
     if (IsFloatingPointType(unbox->type())) {
         LUnboxFloatingPoint *lir = new(alloc()) LUnboxFloatingPoint(unbox->type());
         if (unbox->fallible() && !assignSnapshot(lir, unbox->bailoutKind()))
             return false;