Bug 925586 - IonMonkey: Document MUrsh's special case exemption from type() rules, and restrict it to only the cases that need it. r=nbp
authorDan Gohman <sunfish@google.com>
Tue, 15 Oct 2013 20:49:44 -0700
changeset 164687 8f0ebd310f1d0ef9d2d25b2a56b520b264ea13c3
parent 164686 29a1d64653e8af4d5adfdf9116605c017638caaa
child 164688 eba7271a9bee4b06a0c894b10c8ab48764cf68cd
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs925586
milestone27.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 925586 - IonMonkey: Document MUrsh's special case exemption from type() rules, and restrict it to only the cases that need it. r=nbp
js/src/jit/LIR-Common.h
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/RangeAnalysis.cpp
js/src/jit/RangeAnalysis.h
js/src/jit/arm/CodeGenerator-arm.cpp
js/src/jit/shared/CodeGenerator-x86-shared.cpp
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -2022,18 +2022,18 @@ class LBitOpI : public LInstructionHelpe
   public:
     LIR_HEADER(BitOpI)
 
     LBitOpI(JSOp op)
       : op_(op)
     { }
 
     const char *extraName() const {
-        if (bitop() == JSOP_URSH && mir_->toUrsh()->canOverflow())
-            return "UrshCanOverflow";
+        if (bitop() == JSOP_URSH && mir_->toUrsh()->bailoutsDisabled())
+            return "ursh:BailoutsDisabled";
         return js_CodeName[op_];
     }
 
     JSOp bitop() const {
         return op_;
     }
 };
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1461,19 +1461,19 @@ bool
 MMul::canOverflow()
 {
     if (isTruncated())
         return false;
     return !range() || !range()->hasInt32Bounds();
 }
 
 bool
-MUrsh::canOverflow()
+MUrsh::fallible()
 {
-    if (!canOverflow_)
+    if (bailoutsDisabled())
         return false;
     return !range() || !range()->hasInt32Bounds();
 }
 
 static inline bool
 KnownNonStringPrimitive(MDefinition *op)
 {
     return !op->mightBeType(MIRType_Object)
@@ -2008,17 +2008,24 @@ MUrsh::New(MDefinition *left, MDefinitio
     return new MUrsh(left, right);
 }
 
 MUrsh *
 MUrsh::NewAsmJS(MDefinition *left, MDefinition *right)
 {
     MUrsh *ins = new MUrsh(left, right);
     ins->specializeForAsmJS();
-    ins->canOverflow_ = false;
+
+    // Since Ion has no UInt32 type, we use Int32 and we have a special
+    // exception to the type rules: we can return values in
+    // (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type
+    // without bailing out. This is necessary because Ion has no UInt32
+    // type and we can't have bailouts in asm.js code.
+    ins->bailoutsDisabled_ = true;
+
     return ins;
 }
 
 MResumePoint *
 MResumePoint::New(MBasicBlock *block, jsbytecode *pc, MResumePoint *parent, Mode mode)
 {
     MResumePoint *resume = new MResumePoint(block, pc, parent, mode);
     if (!resume->init())
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -553,17 +553,18 @@ class MDefinition : public MNode
     }
 
   public:
     // Opcode testing and casts.
 #   define OPCODE_CASTS(opcode)                                             \
     bool is##opcode() const {                                               \
         return op() == Op_##opcode;                                         \
     }                                                                       \
-    inline M##opcode *to##opcode();
+    inline M##opcode *to##opcode();                                         \
+    inline const M##opcode *to##opcode() const;
     MIR_OPCODE_LIST(OPCODE_CASTS)
 #   undef OPCODE_CASTS
 
     inline MInstruction *toInstruction();
     bool isInstruction() const {
         return !isPhi();
     }
 
@@ -3237,21 +3238,21 @@ class MRsh : public MShiftInstruction
         // x >> 0 => x
         return getOperand(0);
     }
     void computeRange();
 };
 
 class MUrsh : public MShiftInstruction
 {
-    bool canOverflow_;
+    bool bailoutsDisabled_;
 
     MUrsh(MDefinition *left, MDefinition *right)
       : MShiftInstruction(left, right),
-        canOverflow_(true)
+        bailoutsDisabled_(false)
     { }
 
   public:
     INSTRUCTION_HEADER(Ursh)
     static MUrsh *New(MDefinition *left, MDefinition *right);
     static MUrsh *NewAsmJS(MDefinition *left, MDefinition *right);
 
     MDefinition *foldIfZero(size_t operand) {
@@ -3259,21 +3260,21 @@ class MUrsh : public MShiftInstruction
         if (operand == 0)
             return getOperand(0);
 
         return this;
     }
 
     void infer(BaselineInspector *inspector, jsbytecode *pc);
 
-    bool canOverflow();
-
-    bool fallible() {
-        return canOverflow();
-    }
+    bool bailoutsDisabled() const {
+        return bailoutsDisabled_;
+    }
+
+    bool fallible();
 
     void computeRange();
 };
 
 class MBinaryArithInstruction
   : public MBinaryInstruction,
     public ArithPolicy
 {
@@ -8941,16 +8942,21 @@ class MAsmJSCheckOverRecursed : public M
 #undef INSTRUCTION_HEADER
 
 // Implement opcode casts now that the compiler can see the inheritance.
 #define OPCODE_CASTS(opcode)                                                \
     M##opcode *MDefinition::to##opcode()                                    \
     {                                                                       \
         JS_ASSERT(is##opcode());                                            \
         return static_cast<M##opcode *>(this);                              \
+    }                                                                       \
+    const M##opcode *MDefinition::to##opcode() const                        \
+    {                                                                       \
+        JS_ASSERT(is##opcode());                                            \
+        return static_cast<const M##opcode *>(this);                        \
     }
 MIR_OPCODE_LIST(OPCODE_CASTS)
 #undef OPCODE_CASTS
 
 MDefinition *MNode::toDefinition()
 {
     JS_ASSERT(isDefinition());
     return (MDefinition *)this;
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -431,16 +431,24 @@ Range::Range(const MDefinition *def)
           case MIRType_None:
             MOZ_ASSUME_UNREACHABLE("Asking for the range of an instruction with no value");
           default:
             setUnknown();
             break;
         }
     }
 
+    // As a special case, MUrsh is permitted to claim a result type of
+    // MIRType_Int32 while actually returning values in [0,UINT32_MAX] without
+    // bailouts. If range analysis hasn't ruled out values in
+    // (INT32_MAX,UINT32_MAX], set the range to be conservatively correct for
+    // use as either a uint32 or an int32.
+    if (!hasInt32UpperBound() && def->isUrsh() && def->toUrsh()->bailoutsDisabled())
+        lower_ = INT32_MIN;
+
     assertInvariants();
 }
 
 static inline bool
 MissingAnyInt32Bounds(const Range *lhs, const Range *rhs)
 {
     return !lhs->hasInt32LowerBound() || !lhs->hasInt32UpperBound() ||
            !rhs->hasInt32LowerBound() || !rhs->hasInt32UpperBound();
@@ -1067,18 +1075,16 @@ MUrsh::computeRange()
         right.wrapAroundToShiftCount();
         setRange(Range::ursh(&left, &right));
     } else {
         int32_t c = rhs->toConstant()->value().toInt32();
         setRange(Range::ursh(&left, c));
     }
 
     JS_ASSERT(range()->lower() >= 0);
-    if (type() == MIRType_Int32 && !range()->hasInt32UpperBound())
-        range()->extendUInt32ToInt32Min();
 }
 
 void
 MAbs::computeRange()
 {
     if (specialization_ != MIRType_Int32 && specialization_ != MIRType_Double)
         return;
 
--- a/js/src/jit/RangeAnalysis.h
+++ b/js/src/jit/RangeAnalysis.h
@@ -512,24 +512,16 @@ class Range : public TempObject {
     // If this range exceeds [0, 32) range, at either or both ends, change
     // it to the [0, 32) range.  Otherwise do nothing.
     void wrapAroundToShiftCount();
 
     // If this range exceeds [0, 1] range, at either or both ends, change
     // it to the [0, 1] range.  Otherwise do nothing.
     void wrapAroundToBoolean();
 
-    // As we lack support of MIRType_UInt32, we need to work around the int32
-    // representation by doing an overflow while keeping the upper infinity to
-    // repesent the fact that the value might reach bigger numbers.
-    void extendUInt32ToInt32Min() {
-        JS_ASSERT(!hasInt32UpperBound());
-        lower_ = JSVAL_INT_MIN;
-    }
-
     const SymbolicBound *symbolicLower() const {
         return symbolicLower_;
     }
     const SymbolicBound *symbolicUpper() const {
         return symbolicUpper_;
     }
 
     void setSymbolicLower(SymbolicBound *bound) {
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -902,17 +902,17 @@ CodeGeneratorARM::visitShiftI(LShiftI *i
                 masm.ma_mov(lhs, dest);
             break;
           case JSOP_URSH:
             if (shift) {
                 masm.ma_lsr(Imm32(shift), lhs, dest);
             } else {
                 // x >>> 0 can overflow.
                 masm.ma_mov(lhs, dest);
-                if (ins->mir()->toUrsh()->canOverflow()) {
+                if (ins->mir()->toUrsh()->fallible()) {
                     masm.ma_cmp(dest, Imm32(0));
                     if (!bailoutIf(Assembler::LessThan, ins->snapshot()))
                         return false;
                 }
             }
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("Unexpected shift op");
@@ -927,17 +927,17 @@ CodeGeneratorARM::visitShiftI(LShiftI *i
           case JSOP_LSH:
             masm.ma_lsl(dest, lhs, dest);
             break;
           case JSOP_RSH:
             masm.ma_asr(dest, lhs, dest);
             break;
           case JSOP_URSH:
             masm.ma_lsr(dest, lhs, dest);
-            if (ins->mir()->toUrsh()->canOverflow()) {
+            if (ins->mir()->toUrsh()->fallible()) {
                 // x >>> 0 can overflow.
                 masm.ma_cmp(dest, Imm32(0));
                 if (!bailoutIf(Assembler::LessThan, ins->snapshot()))
                     return false;
             }
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("Unexpected shift op");
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -1099,17 +1099,17 @@ CodeGeneratorX86Shared::visitShiftI(LShi
             break;
           case JSOP_RSH:
             if (shift)
                 masm.sarl(Imm32(shift), lhs);
             break;
           case JSOP_URSH:
             if (shift) {
                 masm.shrl(Imm32(shift), lhs);
-            } else if (ins->mir()->toUrsh()->canOverflow()) {
+            } else if (ins->mir()->toUrsh()->fallible()) {
                 // x >>> 0 can overflow.
                 masm.testl(lhs, lhs);
                 if (!bailoutIf(Assembler::Signed, ins->snapshot()))
                     return false;
             }
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("Unexpected shift op");
@@ -1120,17 +1120,17 @@ CodeGeneratorX86Shared::visitShiftI(LShi
           case JSOP_LSH:
             masm.shll_cl(lhs);
             break;
           case JSOP_RSH:
             masm.sarl_cl(lhs);
             break;
           case JSOP_URSH:
             masm.shrl_cl(lhs);
-            if (ins->mir()->toUrsh()->canOverflow()) {
+            if (ins->mir()->toUrsh()->fallible()) {
                 // x >>> 0 can overflow.
                 masm.testl(lhs, lhs);
                 if (!bailoutIf(Assembler::Signed, ins->snapshot()))
                     return false;
             }
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("Unexpected shift op");