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 200218 43494708df76859ec649ea2aa0c53c175776eab3
parent 200217 a14312d5203e07b2f6fafe1d08ba9a17c296670c
child 200219 dd4680de882d04805521d5039676f45018b831b1
push id8272
push useremorley@mozilla.com
push dateTue, 19 Aug 2014 14:17:45 +0000
treeherderfx-team@a955e211bfde [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1054512
milestone34.0a1
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;